source: tailor/vcpx/session.py @ 312

Revision 312, 10.7 KB checked in by lele@…, 8 years ago (diff)

Basic functionalities exposed in a interactive session
This started by exploring different approaches to tailor configuration,
and has become funny enough to play with to be pushed beyond my
personal satisfation.

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
16Each session's data may persist in *state file*, that may be specified
17either thru the ``state_file`` command::
18
19  bash $ tailor --verbose --interactive
20  Welcome to the Tailor interactive session: you can issue several commands
21  with the usual `readline` facilities. With "help" you'll get a list of
22  available commands.
23
24  tailor $ state_file ~/tailored/someproj.state
25 
26or directly as the only non-option argument on the tailor's command line::
27
28  bash $ tailor -vi ~/tailored/someproj.state
29
30with the same meaning. The content of the file overrides the options.
31"""
32
33__docformat__ = 'reStructuredText'
34
35from cmd import Cmd
36from os import chdir, getcwd       
37
38
39INTRO = """\
40Welcome to the Tailor interactive session: you can issue several commands
41with the usual `readline` facilities. With "help" you'll get a list of
42available commands.
43"""
44
45
46class Session(Cmd):
47    """Tailor interactive session."""
48   
49    prompt = "tailor $ "
50   
51    PERSIST_ATTRS = ('source_repository', 'source_kind', 'source_module',
52                     'source_revision', 'target_repository',
53                     'target_kind', 'target_module', 'current_directory',
54                     'sub_directory')
55   
56    def __init__(self, options, args):
57        Cmd.__init__(self)
58        self.options = options       
59        self.args = args
60       
61        self.source_repository = options.repository
62        self.source_kind = options.source_kind
63        self.source_module = options.module
64        self.source_revision = None
65        self.target_repository = None
66        self.target_kind = options.target_kind
67        self.target_module = None
68        self.state_file = args and args[0] or None
69        self.current_directory = getcwd()
70        self.sub_directory = None
71       
72        self.changesets = None
73        self.changed = False
74
75        if self.state_file:
76            self.__loadState()
77           
78    def __del__(self):
79        if self.state_file and self.changed:
80            self.__saveState()
81
82    def __saveState(self):
83        self.__log('Saving state file...\n')
84        state = file(self.state_file, 'w')
85        for attr in self.PERSIST_ATTRS:
86            value = getattr(self, attr)
87            if not value is None:
88                state.write('%s=%s\n' % (attr, value))
89        state.close()
90
91    def __loadState(self):
92        state = file(self.state_file, 'r')
93        for line in state:
94            attr, value = line[:-1].split('=',1)
95            meth = getattr(self, 'do_' + attr)
96            meth(value)
97        state.close()
98       
99    def __log(self, what):
100        if self.options.verbose:
101            self.stdout.write(what)
102
103    def __err(self, what):
104        self.stdout.write('Error: ')
105        self.stdout.write(what)
106       
107
108    ## Interactive commands
109
110    def emptyline(self):
111        for attr in self.PERSIST_ATTRS:
112            print '%s=%s' % (attr, getattr(self, attr))
113       
114    def do_exit(self, arg):
115        """Exit the interactive session."""
116
117        self.__log('Exiting...\n')
118        return True
119
120    def do_EOF(self, arg):
121        """Exit the interactive session."""
122       
123        return self.do_exit(arg)
124
125    def do_save(self, arg):
126        """Save the commands history."""
127
128        import readline
129
130        if not arg:
131            arg = '/tmp/tailor.cmds'
132           
133        readline.write_history_file(arg)
134        self.__log('History saved in: %s\n' % arg)
135       
136    def do_cd(self, arg):
137        """Print or set current active directory."""
138
139        if arg and self.current_directory <> arg:
140            try:
141                chdir(arg)
142                self.current_directory = getcwd()
143                self.changed = True
144            except:
145                self.__log('Cannot change current directory to %s\n' %
146                           arg)
147        self.__log('Current directory: %s\n' % self.current_directory)
148
149    do_current_directory = do_cd
150
151    def do_sub_directory(self, arg):
152        """
153        Print or set the subdirectory that actually contains the working copy.
154
155        This is desumed automatically to be the last component of
156        the upstream module or repository.
157        """
158       
159        if arg and self.sub_directory <> arg:
160            self.sub_directory = getcwd()
161            self.changed = True
162
163        self.__log('Sub directory: %s\n' % self.sub_directory)
164       
165    def do_source_kind(self, arg):
166        """Print or set the source repository kind."""
167
168        if arg and self.source_kind <> arg:
169            self.source_kind = arg
170            self.changed = True
171
172        self.__log('Current source kind: %s\n' % self.source_kind)
173       
174    def do_target_kind(self, arg):
175        """Print or set the target repository kind."""
176
177        if arg and self.target_kind <> arg:
178            self.target_kind = arg
179            self.changed = True
180
181        self.__log('Current target kind: %s\n' % self.target_kind)
182
183    def do_source_repository(self, arg):
184        """Print or set the source repository."""
185
186        from os.path import sep
187       
188        if arg and self.source_repository <> arg:
189            if arg.endswith(sep):
190                arg = arg[:-1]
191            self.source_repository = arg
192            self.changed = True
193
194        self.__log('Current source repository: %s\n' % self.source_repository)
195
196    def do_target_repository(self, arg):
197        """Print or set the target repository."""
198
199        from os.path import sep
200       
201        if arg and self.target_repository <> arg:
202            if arg.endswith(sep):
203                arg = arg[:-1]
204            self.target_repository = arg
205            self.changed = True
206
207        self.__log('Current target repository: %s\n' % self.target_repository)
208
209    def do_source_module(self, arg):
210        """Print or set the source module."""
211
212        from os.path import sep
213       
214        if arg and self.source_module <> arg:
215            if arg.endswith(sep):
216                arg = arg[:-1]
217            self.source_module = arg
218            self.changed = True
219
220        self.__log('Current target kind: %s\n' % self.source_module)
221
222    def do_target_module(self, arg):
223        """Print or set the target module."""
224
225        from os.path import sep
226       
227        if arg and self.target_module <> arg:
228            if arg.endswith(sep):
229                arg = arg[:-1]
230            self.target_module = arg
231            self.changed = True
232
233        self.__log('Current target kind: %s\n' % self.target_module)
234
235    def do_source_revision(self, arg):
236        """Print or set the current source revision."""
237       
238        if arg and self.source_revision <> arg:
239            self.source_revision = arg
240            self.changed = True
241
242        self.__log('Current source revision: %s\n' % self.source_revision)
243       
244    def do_state_file(self, arg):
245        """
246        Print or set the current state_file.
247
248        When specified, this is used as the persistent storage for this
249        session, automatically saved upon clean exit.
250
251        The argument must be a file name, possibly with the usual
252        "~user/file" convention.       
253        """
254
255        from os.path import exists, isabs, abspath, expanduser
256
257        if arg:
258            arg = expanduser(arg)
259            if not isabs(arg):
260                arg = abspath(arg)
261       
262        if arg and self.state_file <> arg:
263            self.state_file = arg
264            self.changed = True
265
266        self.__log('Current state file: %s\n' % self.state_file)
267
268        if exists(self.state_file):
269            self.__loadState()
270           
271    def do_get_changes(self, arg):
272        """Fetch information on upstream changes."""
273       
274        if self.source_kind and \
275           self.source_repository and \
276           self.source_module and \
277           self.source_revision:
278
279            dwd = DualWorkingDir(self.source_kind, self.target_kind)
280            self.changesets = dwd.getUpstreamChangesets(self.current_directory,
281                                                        self.source_repository,
282                                                        self.source_module,
283                                                        self.source_revision)
284            self.__log('Collected %d upstream changesets\n' %
285                       len(self.changesets))
286        else:
287            self.__err("needs 'source_kind', 'source_repository', "
288                       "'source_module' and 'source_revision' to proceed.\n")
289
290    def do_show_changes(self, arg):
291        """Show the upstream changes not yet applied."""
292
293        if self.changesets:
294            self.__log(`self.changesets`)
295            self.__log('\n')
296        else:
297            self.__err("needs `get_changes` to proceed.\n")
298           
299    def do_bootstrap(self, arg):
300        """Bootstrap a new tailorized module."""
301
302        from os.path import join, split, sep
303        from dualwd import DualWorkingDir
304
305        if self.sub_directory:
306            subdir = self.sub_directory
307        else:
308            subdir = split(self.source_module or self.source_repository)[1] or ''
309            self.do_sub_directory(subdir)
310           
311        self.__log("Bootstrapping '%s'\n" % join(self.current_directory, subdir))
312
313        dwd = DualWorkingDir(self.source_kind, self.target_kind)
314        self.__log("Getting %s revision '%s' of '%s' from '%s'\n" % (
315            self.source_kind, self.source_revision,
316            self.source_module, self.source_repository))
317
318        try:
319            actual = dwd.checkoutUpstreamRevision(self.current_directory,
320                                                  self.source_repository,
321                                                  self.source_module,
322                                                  self.source_revision,
323                                                  subdir=subdir)
324        except:
325            self.__err('Checkout failed!\n')
326            raise
327       
328        try:
329            dwd.initializeNewWorkingDir(self.current_directory,
330                                        self.target_repository,
331                                        self.target_module,
332                                        subdir, actual)
333        except:
334            self.__err('Working copy initialization failed!\n')
335            raise
336
337        self.do_source_revision(actual)
338        self.__log("Bootstrap completed")
339
340       
341def interactive(options, args):
342    session = Session(options, args)
343    session.cmdloop(options.verbose and INTRO or "")
Note: See TracBrowser for help on using the repository browser.