bzr branch
http://bzr.ed.am/stdhome
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
1 |
# program.py |
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
2 |
# |
3 |
# Copyright (C) 2013 Tim Marston <tim@edm.am> |
|
4 |
# |
|
5 |
# This file is part of stdhome (hereafter referred to as "this program"). |
|
6 |
# See http://ed.am/dev/stdhome for more information. |
|
7 |
# |
|
8 |
# This program is free software: you can redistribute it and/or modify |
|
9 |
# it under the terms of the GNU General Public License as published by |
|
10 |
# the Free Software Foundation, either version 3 of the License, or |
|
11 |
# (at your option) any later version. |
|
12 |
# |
|
13 |
# This program is distributed in the hope that it will be useful, |
|
14 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
15 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
16 |
# GNU General Public License for more details. |
|
17 |
# |
|
18 |
# You should have received a copy of the GNU General Public License |
|
19 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
20 |
||
21 |
||
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
22 |
import os, sys, getopt |
23 |
import the |
|
24 |
||
25 |
||
26 |
class Program: |
|
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
27 |
|
28 |
||
29 |
def __init__( self, version ): |
|
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
30 |
self.name = os.path.basename( sys.argv[ 0 ] ) |
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
31 |
self.version = version |
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
32 |
|
33 |
||
34 |
def die( self, error_message ): |
|
35 |
prefix = self.name + ( ' ' + the.command if the.command else '' ) |
|
36 |
print >> sys.stderr, '%s: %s' % ( prefix, error_message ) |
|
37 |
exit( 1 ) |
|
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
38 |
|
39 |
||
40 |
def print_usage( self, error_message ): |
|
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
41 |
suffix = ' ' + the.command if the.command else '' |
42 |
self.die( error_message + \ |
|
43 |
"\nTry '%s%s --help' for more information." % \ |
|
44 |
( self.name, suffix ) ) |
|
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
45 |
|
46 |
||
47 |
def print_help( self ): |
|
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
48 |
print "Usage: " + self.name + " COMMAND [OPTION]..." |
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
49 |
|
50 |
# 01234567890123456789012345678901234567890123456789012345678901234567890123456789 |
|
51 |
print "Manage your home directories, across multiple computers, similar to how you" |
|
52 |
print "would software under version control." |
|
53 |
||
54 |
print "Global options (for all commands):" |
|
55 |
print " --help display help and exit" |
|
56 |
print " --version output version information and exit" |
|
57 |
||
58 |
print "Commands:" |
|
59 |
print " init initialise a local copy of your repositories" |
|
60 |
print " update update files in your home directory" |
|
61 |
print " resolve try to finish an update (that had conflicts)" |
|
62 |
print " add add local files/changes to the repository" |
|
63 |
print " remove remove a local file from the repository" |
|
64 |
print " revert undo changes made to a local file" |
|
65 |
print " stage-add stage local files/changes" |
|
66 |
print " stage-remove stage the removal of files" |
|
67 |
print " stage-revert revert staged changes" |
|
68 |
print " stage-status show status of staging area" |
|
69 |
print " stage-commit commit staged changes to repository" |
|
70 |
||
71 |
print "For help about a particular command (including the additional options that the" |
|
72 |
print "command accepts) try typing:" |
|
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
73 |
print " $ " + self.name + " COMMAND --help" |
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
74 |
exit( 0 ) |
75 |
||
76 |
||
77 |
def print_version( self ): |
|
78 |
print "stdhome " + self.version |
|
79 |
||
80 |
print "Copyright (C) 2013 Tim Marston" |
|
81 |
||
82 |
# 01234567890123456789012345678901234567890123456789012345678901234567890123456789 |
|
83 |
print "This program is free software, and you may use, modify and redistribute it" |
|
84 |
print "under the terms of the GNU General Public License version 3 or later. This" |
|
85 |
print "program comes with ABSOLUTELY NO WARRANTY, to the extent permitted by law." |
|
86 |
||
87 |
print "For more information, including documentation, please see the project website" |
|
88 |
print "at http://ed.am/dev/stdhome." |
|
89 |
||
90 |
print "Please report bugs to <tim@ed.am>." |
|
91 |
exit( 0 ) |
|
92 |
||
93 |
||
94 |
def check_command( self, command ): |
|
95 |
""" |
|
96 |
Check that the given command is valid and return the full name of the command. |
|
97 |
||
98 |
Arguments: |
|
99 |
- `command`: the given command |
|
100 |
""" |
|
101 |
# commands |
|
102 |
if [ 'init', 'update', 'resolve', 'add', 'remove', 'revert', |
|
103 |
'stage-add', 'stage-remove', 'stage-revert', 'stage-status', |
|
104 |
'stage-commit' ].count( command ) == 1: |
|
105 |
return command |
|
106 |
||
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
107 |
# resolve aliases |
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
108 |
elif command == 'up': |
109 |
return 'update' |
|
110 |
elif command == 'rm': |
|
111 |
return 'remove' |
|
112 |
||
113 |
# invalid |
|
114 |
else: |
|
115 |
return None |
|
116 |
||
117 |
||
118 |
def get_command_argument( self, args ): |
|
119 |
""" |
|
120 |
Find the first program argument what isn't an option argument. |
|
121 |
||
122 |
Arguments: |
|
123 |
- `args`: the program arguments |
|
124 |
""" |
|
125 |
while args: |
|
126 |
if args[ 0 ] == '--': |
|
127 |
return args[ 1 ] if len( args ) > 1 else None |
|
128 |
if args[ 0 ][ 0 : 1 ] != '-': |
|
129 |
return args[ 0 ] |
|
130 |
args = args[ 1 : ] |
|
131 |
return None |
|
132 |
||
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
133 |
|
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
134 |
def run( self ): |
135 |
# make an initial attempt to parse the command line, looking only for |
|
136 |
# --help and --version, so that they have the chance to run without a |
|
137 |
# command being specified |
|
138 |
try: |
|
139 |
opts, args = getopt.gnu_getopt( |
|
140 |
sys.argv[ 1: ], "", |
|
141 |
[ "help", "version" ] ) |
|
142 |
||
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
143 |
for opt, optarg in opts: |
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
144 |
# we only show help if there are no non-option arguments (e.g., |
145 |
# a command) specified. If a command has been specified it will |
|
146 |
# have to be parsed and --help will be handled by it, instead) |
|
147 |
if opt == "--help" and len( args ) == 0: |
|
148 |
self.print_help() |
|
149 |
elif opt == "--version": |
|
150 |
self.print_version() |
|
151 |
||
152 |
except getopt.GetoptError as e: |
|
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
153 |
# ignore errors -- we aren't parsing the command line properly yet |
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
154 |
pass |
155 |
||
156 |
# find the first non-option argument (the command) |
|
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
157 |
the.command = self.get_command_argument( sys.argv[ 1: ] ) |
158 |
if the.command == None: |
|
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
159 |
self.print_usage( "missing command" ) |
160 |
||
161 |
# check command is valid |
|
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
162 |
the.command = self.check_command( the.command ) |
163 |
if the.command == None: |
|
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
164 |
self.print_usage( "bad command" ) |
165 |
||
166 |
# calculate class name |
|
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
167 |
bits = the.command.split( '-' ) |
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
168 |
class_name = 'Command' |
169 |
for bit in bits: |
|
170 |
class_name += bit[ 0 ].upper() + bit[ 1 : ] |
|
171 |
||
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
172 |
# instantiate the command class |
173 |
module = __import__( 'stdhome.command_' + the.command, |
|
174 |
fromlist = [ class_name ] ) |
|
175 |
instance = getattr( module, class_name )() |
|
176 |
||
177 |
# run it |
|
178 |
try: |
|
179 |
instance.parse_command_line() |
|
180 |
instance.run() |
|
181 |
except ( getopt.GetoptError, self.UsageError ) as e: |
|
182 |
self.print_usage( e.msg ) |
|
183 |
except self.FatalError as e: |
|
184 |
message = e.msg |
|
185 |
if the.verbose and e.output: |
|
186 |
message += '\n\nOUTPUT:\n' + e.output |
|
187 |
self.die( message ) |
|
188 |
||
189 |
||
190 |
class UsageError( Exception ): |
|
191 |
||
192 |
def __init__( self, error_message ): |
|
193 |
self.msg = error_message |
|
194 |
||
195 |
||
196 |
class FatalError( Exception ): |
|
197 |
||
198 |
def __init__( self, message, output = None ): |
|
199 |
self.msg = message |
|
200 |
self.output = output |