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 |
||
36
by Tim Marston
moved config to its own file and read-in ignore lists |
22 |
import os, sys, getopt |
88
by Tim Marston
python3ification |
23 |
from . import the |
24 |
from .config import Config |
|
25 |
from .vcs.vcs import Vcs |
|
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
26 |
|
27 |
||
28 |
class Program: |
|
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
29 |
|
30 |
||
31 |
def __init__( self, version ): |
|
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
32 |
self.name = os.path.basename( sys.argv[ 0 ] ) |
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
33 |
self.version = version |
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
34 |
|
35 |
||
36 |
def die( self, error_message ): |
|
37 |
prefix = self.name + ( ' ' + the.command if the.command else '' ) |
|
88
by Tim Marston
python3ification |
38 |
print('%s: %s' % ( prefix, error_message ), file=sys.stderr) |
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
39 |
exit( 1 ) |
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
40 |
|
41 |
||
42 |
def print_usage( self, error_message ): |
|
3
by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment |
43 |
command = ' ' + the.command if the.command else '' |
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
44 |
self.die( error_message + \ |
45 |
"\nTry '%s%s --help' for more information." % \ |
|
3
by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment |
46 |
( self.name, command ) ) |
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
47 |
|
48 |
||
49 |
def print_help( self ): |
|
88
by Tim Marston
python3ification |
50 |
print("Usage: " + self.name + " COMMAND [OPTION]...") |
51 |
print() |
|
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
52 |
# 01234567890123456789012345678901234567890123456789012345678901234567890123456789 |
88
by Tim Marston
python3ification |
53 |
print("Tool to manage a set of files in your home directory and distribute them across") |
54 |
print("multiple computers, merging local changes (in the same way as you would manage") |
|
55 |
print("source code under version control).") |
|
56 |
print() |
|
57 |
print("Global options (for all commands):") |
|
58 |
print(" --help display help and exit") |
|
59 |
print(" --version output version information and exit") |
|
60 |
print() |
|
61 |
print("Commands:") |
|
89
by Tim Marston
added staging commands; updated command helps |
62 |
print(" init initialise a local copy of your repository") |
63 |
print(" update merge remote changes in to your home directory") |
|
88
by Tim Marston
python3ification |
64 |
print(" resolve try to finish an update (that had conflicts)") |
65 |
print(" add add local files/changes to the repository") |
|
89
by Tim Marston
added staging commands; updated command helps |
66 |
# print(" remove remove a local file from the repository") |
88
by Tim Marston
python3ification |
67 |
print(" status list files that have changed locally") |
68 |
print(" diff shows changes made to local files") |
|
69 |
print(" revert undo changes made to local files") |
|
89
by Tim Marston
added staging commands; updated command helps |
70 |
print() |
71 |
print("Manual staging commands:") |
|
72 |
print(" stage-add add (but don't commit) files/changes to local repository") |
|
73 |
# print(" stage-remove delete *but don't comit) files from the local repository") |
|
88
by Tim Marston
python3ification |
74 |
print(" stage-revert revert changes in the local repository") |
91
by Tim Marston
added stage-status to main help |
75 |
print(" stage-status show status of local repository") |
89
by Tim Marston
added staging commands; updated command helps |
76 |
# print(" stage-diff shows changes in local repository") |
77 |
print(" stage-commit commit changes in the local repository") |
|
88
by Tim Marston
python3ification |
78 |
print() |
79 |
print("For help about a particular command (including the additional options that the") |
|
80 |
print("command accepts) try typing:") |
|
81 |
print(" $ " + self.name + " COMMAND --help") |
|
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
82 |
exit( 0 ) |
83 |
||
84 |
||
85 |
def print_version( self ): |
|
88
by Tim Marston
python3ification |
86 |
print("stdhome " + self.version) |
87 |
print() |
|
88 |
print("Copyright (C) 2013 to 2014 Tim Marston") |
|
89 |
print() |
|
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
90 |
# 01234567890123456789012345678901234567890123456789012345678901234567890123456789 |
88
by Tim Marston
python3ification |
91 |
print("This program is free software, and you may use, modify and redistribute it") |
92 |
print("under the terms of the GNU General Public License version 3 or later. This") |
|
93 |
print("program comes with ABSOLUTELY NO WARRANTY, to the extent permitted by law.") |
|
94 |
print() |
|
95 |
print("For more information, including documentation, please see the project website") |
|
96 |
print("at http://ed.am/dev/stdhome.") |
|
97 |
print() |
|
98 |
print("Please report bugs to <tim@ed.am>.") |
|
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
99 |
exit( 0 ) |
100 |
||
101 |
||
102 |
def check_command( self, command ): |
|
52
by Tim Marston
attempt to allow arguments before main command by treating the first thing that |
103 |
"""Check that the given command is valid and return the full name of the |
104 |
command. |
|
105 |
||
106 |
@param command the given command |
|
107 |
""" |
|
108 |
||
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
109 |
# commands |
3
by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment |
110 |
if [ 'init', 'update', 'resolve', 'add', 'remove', 'revert', 'status', |
111 |
'diff', 'stage-add', 'stage-remove', 'stage-revert', |
|
112 |
'stage-status', 'stage-diff', 'stage-commit' |
|
113 |
].count( command ) == 1: |
|
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
114 |
return command |
115 |
||
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
116 |
# resolve aliases |
8
by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert |
117 |
alias = { |
118 |
'up': 'update', |
|
119 |
'rm': 'remove', |
|
120 |
'st': 'status', |
|
121 |
'co': 'init', |
|
71
by Tim Marston
added 'ci' as an alias of add; fixed issue with lack of output from vcs when |
122 |
'ci': 'add', |
90
by Tim Marston
add stage-status command; add common stage notice to helps |
123 |
'stst': 'stage-status', |
124 |
'stadd': 'stage-add', |
|
125 |
'strm': 'stage-remove', |
|
126 |
'stci': 'stage-commit', |
|
8
by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert |
127 |
}.get( command, False ) |
128 |
if alias: return alias |
|
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
129 |
|
130 |
# invalid |
|
8
by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert |
131 |
return None |
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
132 |
|
133 |
||
52
by Tim Marston
attempt to allow arguments before main command by treating the first thing that |
134 |
def get_command_argument( self, args ): |
135 |
"""Find the first program argument what isn't an option argument. |
|
136 |
||
137 |
@param args the program arguments |
|
138 |
""" |
|
139 |
||
140 |
while args: |
|
141 |
if args[ 0 ] == '--': |
|
142 |
return args[ 1 ] if len( args ) > 1 else None |
|
143 |
if args[ 0 ][ 0 : 1 ] != '-': |
|
144 |
return args[ 0 ] |
|
145 |
args = args[ 1 : ] |
|
146 |
return None |
|
147 |
||
148 |
||
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
149 |
def run( self ): |
150 |
# make an initial attempt to parse the command line, looking only for |
|
63
by Tim Marston
determine and instantiate repo vcs dynamically; for new repos, added default vcs |
151 |
# --help and --version so that they have the chance to run without a |
152 |
# command being specified |
|
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
153 |
try: |
154 |
opts, args = getopt.gnu_getopt( |
|
63
by Tim Marston
determine and instantiate repo vcs dynamically; for new repos, added default vcs |
155 |
sys.argv[ 1: ], "", |
156 |
[ "help", "version" ] ) |
|
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
157 |
|
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
158 |
for opt, optarg in opts: |
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
159 |
# we only show help if there are no non-option arguments (e.g., |
160 |
# a command) specified. If a command has been specified it will |
|
161 |
# have to be parsed and --help will be handled by it, instead) |
|
63
by Tim Marston
determine and instantiate repo vcs dynamically; for new repos, added default vcs |
162 |
if opt == "--help" and len( args ) == 0: |
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
163 |
self.print_help() |
164 |
elif opt == "--version": |
|
165 |
self.print_version() |
|
166 |
||
167 |
except getopt.GetoptError as e: |
|
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
168 |
# 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 |
169 |
pass |
170 |
||
17
by Tim Marston
read ~/.stdhomerc; commands set repo before run(); program performs late |
171 |
# read program configuration |
36
by Tim Marston
moved config to its own file and read-in ignore lists |
172 |
the.config = Config() |
17
by Tim Marston
read ~/.stdhomerc; commands set repo before run(); program performs late |
173 |
|
52
by Tim Marston
attempt to allow arguments before main command by treating the first thing that |
174 |
# find the command by grabbing the first program argument that doesn't |
175 |
# look like an option (note: the first argument that doesn't look like |
|
176 |
# an option may actually be an argument to an option, so this is far |
|
177 |
# from perfect, but, provided the user is mindful, this is preferable to |
|
178 |
# forcing the user to always specify options after the command) |
|
179 |
the.command = self.get_command_argument( sys.argv[ 1: ] ) |
|
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
180 |
if the.command == None: |
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
181 |
self.print_usage( "missing command" ) |
182 |
||
183 |
# check command is valid |
|
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
184 |
the.command = self.check_command( the.command ) |
185 |
if the.command == None: |
|
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
186 |
self.print_usage( "bad command" ) |
187 |
||
3
by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment |
188 |
# calculate module and class name |
8
by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert |
189 |
class_name = module_name = '' |
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
190 |
bits = the.command.split( '-' ) |
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
191 |
for bit in bits: |
192 |
class_name += bit[ 0 ].upper() + bit[ 1 : ] |
|
8
by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert |
193 |
if module_name: module_name += '_' |
194 |
module_name += bit |
|
195 |
class_name += 'Command' |
|
1
by Tim Marston
initial commit; basic app startup and initial command-line processing |
196 |
|
3
by Tim Marston
added bzr as a vcs backend; finished init command; implemented deployment |
197 |
# import module and instantiate the class |
8
by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert |
198 |
module = __import__( 'stdhome.command.' + module_name, |
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
199 |
fromlist = [ class_name ] ) |
200 |
instance = getattr( module, class_name )() |
|
201 |
||
17
by Tim Marston
read ~/.stdhomerc; commands set repo before run(); program performs late |
202 |
# fully parse the command line, as per the command |
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
203 |
try: |
204 |
instance.parse_command_line() |
|
5
by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers"; |
205 |
except( getopt.GetoptError, self.UsageError ) as e: |
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
206 |
self.print_usage( e.msg ) |
17
by Tim Marston
read ~/.stdhomerc; commands set repo before run(); program performs late |
207 |
except self.FatalError as e: |
208 |
self.die( e.msg ) |
|
209 |
||
210 |
# do late initialisation |
|
211 |
the.late_init() |
|
212 |
||
213 |
# run the command |
|
214 |
try: |
|
215 |
instance.run() |
|
5
by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers"; |
216 |
except Vcs.VcsError as e: |
71
by Tim Marston
added 'ci' as an alias of add; fixed issue with lack of output from vcs when |
217 |
message = e.msg.rstrip() + '\n\nVCS OUTPUT:\n' + e.output.rstrip() |
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
218 |
self.die( message ) |
5
by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers"; |
219 |
except self.FatalError as e: |
220 |
self.die( e.msg ) |
|
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
221 |
|
222 |
||
223 |
class UsageError( Exception ): |
|
224 |
||
225 |
def __init__( self, error_message ): |
|
226 |
self.msg = error_message |
|
227 |
||
228 |
||
229 |
class FatalError( Exception ): |
|
230 |
||
5
by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers"; |
231 |
def __init__( self, message ): |
2
by Tim Marston
added global objects (the.repo, the.program), deployment object and implemented |
232 |
self.msg = message |