source: tailor/vcpx/session.py @ 318

Revision 318, 10.4 KB checked in by lele@…, 8 years ago (diff)

Really make exit and EOF aliases

Line 
1#! /usr/bin/python
2# -*- mode: python; coding: utf-8 -*-
3# :Progetto: vcpx -- Interactive session
4# :Creato:   ven 13 mag 2005 02:00:57 CEST
5# :Autore:   Lele Gaifax <lele@nautilus.homeip.net>
6# :Licenza:  GNU General Public License
7#
8
9"""
10Tailor interactive session.
11
12This module implements an alternative approach at driving the various
13steps tipically performed by tailor, using an interaction with the user
14instead of pushing options madness on him.
15"""
16
17__docformat__ = 'reStructuredText'
18
19from cmd import Cmd
20from os import chdir, getcwd       
21
22
23INTRO = """\
24Welcome to the Tailor interactive session: you can issue several commands
25with the usual `readline` facilities. With "help" you'll get a list of
26available commands.
27"""
28
29
30class Session(Cmd):
31    """Tailor interactive session."""
32   
33    prompt = "tailor $ "
34   
35    def __init__(self, options, args):
36        """
37        Initialize a new interactive session.
38
39        Set the default values, and override them with option settings,
40        then slurp in each command line argument that should contain
41        a list of commands to be executed.
42        """
43       
44        Cmd.__init__(self)
45        self.options = options       
46        self.args = args
47       
48        self.source_repository = options.repository
49        self.source_kind = options.source_kind
50        self.source_module = options.module
51        self.target_repository = None
52        self.target_kind = options.target_kind
53        self.target_module = None
54        self.current_directory = getcwd()
55        self.sub_directory = None
56       
57        self.state_file = 'tailor.state'
58       
59        self.changesets = None
60        self.logfile = None
61        self.logger = None
62       
63        self.__processArgs()
64
65    def __processArgs(self):
66        """
67        Process optional command line arguments.
68
69        Each argument is assumed to contain a list of tailor commands
70        to execute in order.
71        """
72
73        for arg in self.args:
74            self.cmdqueue.extend(file(arg).readlines())
75
76    def __log(self, what):
77        if self.logger:
78            self.logger.info(what)
79           
80        if self.options.verbose:
81            self.stdout.write(what)
82
83    def __err(self, what):
84        if self.logger:
85            self.logger.error(what)
86           
87        self.stdout.write('Error: ')
88        self.stdout.write(what)
89       
90
91    ## Interactive commands
92
93    def emptyline(self):
94        """Override the default impl of reexecuting last command."""
95        pass
96       
97    def do_exit(self, arg):
98        """Exit the interactive session."""
99
100        self.__log('Exiting...\n')
101        return True
102
103    do_EOF = do_exit
104
105    def do_save(self, arg):
106        """Save the commands history on the specified file."""
107
108        import readline
109
110        if not arg:
111            return
112           
113        readline.write_history_file(arg)
114        self.__log('History saved in: %s\n' % arg)
115       
116    def do_cd(self, arg):
117        """Print or set current active directory."""
118
119        if arg and self.current_directory <> arg:
120            try:
121                chdir(arg)
122                self.current_directory = getcwd()
123            except:
124                self.__log('Cannot change current directory to %s\n' %
125                           arg)
126        self.__log('Current directory: %s\n' % self.current_directory)
127
128    do_current_directory = do_cd
129
130    def do_sub_directory(self, arg):
131        """
132        Print or set the subdirectory that actually contains the working copy.
133
134        This is desumed automatically to be the last component of
135        the upstream module or repository.
136        """
137       
138        if arg and self.sub_directory <> arg:
139            self.sub_directory = arg
140
141        self.__log('Sub directory: %s\n' % self.sub_directory)
142
143    def do_logfile(self, arg):
144        """Print or set the logfile of operations."""
145
146        import logging
147       
148        if arg:
149            self.logfile = arg
150            self.logger = logging.getLogger('tailor')
151            hdlr = logging.FileHandler(self.logfile)
152            formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s')
153            hdlr.setFormatter(formatter)
154            self.logger.addHandler(hdlr) 
155            self.logger.setLevel(logging.INFO)
156           
157        self.__log('Logging to: %s\n' % self.logfile)
158
159    def do_source_kind(self, arg):
160        """Print or set the source repository kind."""
161
162        if arg and self.source_kind <> arg:
163            self.source_kind = arg
164
165        self.__log('Current source kind: %s\n' % self.source_kind)
166       
167    def do_target_kind(self, arg):
168        """Print or set the target repository kind."""
169
170        if arg and self.target_kind <> arg:
171            self.target_kind = arg
172
173        self.__log('Current target kind: %s\n' % self.target_kind)
174
175    def do_source_repository(self, arg):
176        """Print or set the source repository."""
177
178        from os.path import sep
179       
180        if arg and self.source_repository <> arg:
181            if arg.endswith(sep):
182                arg = arg[:-1]
183            self.source_repository = arg
184
185        self.__log('Current source repository: %s\n' % self.source_repository)
186
187    def do_target_repository(self, arg):
188        """Print or set the target repository."""
189
190        from os.path import sep
191       
192        if arg and self.target_repository <> arg:
193            if arg.endswith(sep):
194                arg = arg[:-1]
195            self.target_repository = arg
196
197        self.__log('Current target repository: %s\n' % self.target_repository)
198
199    def do_source_module(self, arg):
200        """Print or set the source module."""
201
202        from os.path import sep
203       
204        if arg and self.source_module <> arg:
205            if arg.endswith(sep):
206                arg = arg[:-1]
207            self.source_module = arg
208
209        self.__log('Current target kind: %s\n' % self.source_module)
210
211    def do_target_module(self, arg):
212        """Print or set the target module."""
213
214        from os.path import sep
215       
216        if arg and self.target_module <> arg:
217            if arg.endswith(sep):
218                arg = arg[:-1]
219            self.target_module = arg
220
221        self.__log('Current target kind: %s\n' % self.target_module)
222
223    def readSourceRevision(self):
224        """Read the source revision from the state file."""
225
226        try:
227            sf = open(self.state_file)
228            revision = sf.read()
229            sf.close()
230        except IOError:
231            revision = None
232
233        return revision
234           
235    def saveSourceRevision(self, revision):
236        """Write current source revision in the state file."""
237
238        sf = open(self.state_file, 'w')
239        sf.write(revision)
240        sf.close()
241       
242    def do_state_file(self, arg):
243        """
244        Print or set the current state_file.
245
246        The argument must be a file name, possibly with the usual
247        "~user/file" convention.       
248        """
249
250        from os.path import isabs, abspath, expanduser
251
252        if arg:
253            arg = expanduser(arg)
254            if not isabs(arg):
255                arg = abspath(arg)
256       
257        if arg and self.state_file <> arg:
258            self.state_file = arg
259               
260        self.__log('Current state file: %s\n' % self.state_file)
261
262    def do_get_changes(self, arg):
263        """Fetch information on upstream changes."""
264
265        source_revision = self.readSourceRevision()
266        if self.source_kind and \
267           self.source_repository and \
268           self.source_module and \
269           source_revision:
270
271            dwd = DualWorkingDir(self.source_kind, self.target_kind)
272            self.changesets = dwd.getUpstreamChangesets(self.current_directory,
273                                                        self.source_repository,
274                                                        self.source_module,
275                                                        source_revision)
276            self.__log('Collected %d upstream changesets\n' %
277                       len(self.changesets))
278        else:
279            self.__err("needs 'source_kind', 'source_repository' and "
280                       "'source_module' to proceed.\n")
281
282    def do_show_changes(self, arg):
283        """Show the upstream changes not yet applied."""
284
285        if self.changesets:
286            self.__log(`self.changesets`)
287            self.__log('\n')
288        else:
289            self.__err("needs `get_changes` to proceed.\n")
290
291    def do_bootstrap(self, arg):
292        """
293        Checkout the initial upstream revision, by default HEAD (or
294        specified by argument), then import the subtree into the
295        target repository.
296        """
297       
298        from os.path import join, split, sep
299        from dualwd import DualWorkingDir
300
301        if self.sub_directory:
302            subdir = self.sub_directory
303        else:
304            subdir = split(self.source_module or self.source_repository)[1] or ''
305            self.do_sub_directory(subdir)
306
307        revision = arg or self.options.revision or 'HEAD'
308       
309        dwd = DualWorkingDir(self.source_kind, self.target_kind)
310        self.__log("Getting %s revision '%s' of '%s' from '%s'\n" % (
311            self.source_kind, revision,
312            self.source_module, self.source_repository))
313
314        try:
315            actual = dwd.checkoutUpstreamRevision(self.current_directory,
316                                                  self.source_repository,
317                                                  self.source_module,
318                                                  revision,
319                                                  subdir=subdir,
320                                                  logger=self.logger)
321            self.saveSourceRevision(actual)
322        except Exception, exc:
323            self.__err('Checkout failed: %s, %s' % (exc.__doc__, exc))
324            if self.logger:
325                self.logger.exception('Checkout failed')
326
327        try:
328            dwd.initializeNewWorkingDir(self.current_directory,
329                                        self.target_repository,
330                                        self.target_module,
331                                        self.sub_directory,
332                                        actual)
333        except:
334            self.__err('Working copy initialization failed: %s, %s' % (exc.__doc__, exc))
335            if self.logger:
336                self.logger.exception('Working copy initialization failed')
337
338       
339def interactive(options, args):
340    session = Session(options, args)
341    session.cmdloop(options.verbose and INTRO or "")
Note: See TracBrowser for help on using the repository browser.