~ubuntu-branches/ubuntu/natty/quickly/natty

« back to all changes in this revision

Viewing changes to quickly/commands.py

  • Committer: Bazaar Package Importer
  • Author(s): Didier Roche
  • Date: 2010-03-29 19:00:48 UTC
  • mfrom: (8.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20100329190048-olhobq492x3xnztz
Tags: 0.3.90-0ubuntu1
* New release:
  Quickly core:
  - check now that --template has one argument
  - -- enables to give options to templates (unknown options and arguments
    are still given to templates but -- specify explicity what to give to
    templates commands like -h, --version...)
  - check and enable recreation of the credential if user deletes it on
    Launchpad
  - Use realpath so that we can symlink to the binary in trunk.
    (Jonathan Lange)
  - project_path_not_found is raised when data_path_not_found should be
    (originated from Philip Peitsch's patch). (LP: #497688)
  - importing command in one template from another template is now possible
    support as well "all" keyword to import all commands from a template
    into another one (take care of overridden commands too)
    (LP: #452306, #487301)
  - add an apport hook to get installed templates (LP: #411127)
  - enable option completion for templates
  - handle now version upgrade, launching script for templates and handling
    versionning on them
  - change getstarted wording (LP: #486180) - Grant
  - Quickly ship now "version" tag instead of "format" tag
  - add some templatetools to check X display
  - if a commands exit with 4, do not show "ERROR:" (case of wrong command
    usage, for instance)
  - Rewrote importing module in both bin/quickly and ubuntu-project binary
  - Add get_camel_case_name() and get_sentence_name() to templatetools
  - Refactor some code in pre_create hook()
  - Now dashes and spaces support in project name! Dealing with python and
    packaging issues for you. (LP: #493130)
  - Provide Quickly API (LP: #426480)
  - Fix some issues in the man page
  ubuntu-application Template:
  - Enabling upload to team or user ppa, as well as any ppa.
    Use --ppa ppaname or --ppa team/ppaname
    .quickly file can be used as well with ppa = ppaname or
    ppa = team/ppaname (LP: #470192)
    ppa completion is also available
  - Change BSD header by full license
  - Add MIT license
  - Fixes an error when launchpad display name contains unicode character
    (Petar Vasić)
  - Fix typo in quickly help save (Petar Vasić)
  - Adds logo.png as logo property in about dialog (LP: #478389)
    (Petar Vasić)
  - Remove lp-project-change command and add configure command.
    configure lp-project [project_name] to set/reset lp project binded to
    your Quickly project
    ppa <ppa name> to set a default ppa with your project
  - Fix ~/.selected-editor being ignored (LP: #484730)
  - Fix wrong shell completion when in a template (it wrongly proposed
    all commands followed by templates)
  - Check if we have a X display available before running "run" or launching
    GUI in "create" command.
  - add -- support in quickly run to pass options like --help, --version so
    that Quickly core don't take them.
  - enable preferences save call being called multiple times (Philip Peitsch)
  - Use realpath so that we can symlink to the binary in trunk.
  - Fixed some errors in the tutorial (Brian) (LP: #499356)
  - Fix missing import sys in setup.py (Philip Peitsch) (LP: #499712)
  - rename ubuntu-project to ubuntu-application
  - enhance quickly license (no more Copyright file, only one AUTHORS file)
    personal license needs COPYING file now (and the header will be copied in
    every files). No more # needed in those files too. Some other little
    refactoring too (LP: #469330)
  - autolicence by default on first share/release (with LP info and on GPL-3)
  - change versionning support in ubuntu application: (LP: #476814)
   + quickly share just adds -publicX to current version where X is bumped
     at each "quicky share" execution
   + quickly release remove -publicX (if any) and release with current YY.MM.
     If there is already a release with that version, it will be YY.MM.1, 
     then YY.MM.2
  - add accelerators to menu items (jens persson)
  - set correctly title dialog (Philip Peitsch) (LP: #501999)
  - about dialog box is now fully automated: (LP: #478414)
   + update copyright, authors, license (when licensing)
   + version (when sharing and releasing)
   + homepage (when changing launchpad project: release and configure)
  - add an 'add' command. $ quickly dialog … is now $ quickly add dialog …
    (LP: #438320)
  - enable automatic release and milestone built in LP, releasing and pushing
    upstream tarball (LP: #470344)
  - automatic collect and publish changelog message from commits and save
    messages used during devlopment. (LP: #476572)
  - add i18n to boiler plate (LP: #423529)
  - enable adding manual depdencies to quickly project (LP: #474639)
  - enable configure its own bzr branch and resetting parent branch even once
    already configured (quickly configure bzr <branch-path>)
  - now recommends seahorse-plugins to have a graphical prompt for signing
    package
  - rename quickly glade to quickly design (LP: #528664)
  - create command is now more flexible and enable people to inherit from
    other template more easily
  - add translation domain to gtkbuilder and fix some items that shouldn't
    been translatable (Łukasz Jernaś)
  - add apport and launchpadintegration to new and existing project once
    bounded to LP (Philip Peitsch)
  - fix spelling mistake "connexion" (Łukasz Jernaś) (LP: #532667)
  - the ubuntu-application tutorial is now internationalized and use docbook
    format. Translators, it's yours! (Shane Fagan) (LP: #525168)
  - package/share/release are now more quiet and only print something on error
    warning as a summary as with unfound packages, and so on (LP: #482915)
  - new algorithm to select good email automatically. Try to get the preferred
    email and update the AUTHOR file with it.
  - test GPG key and create one automatically if none is available. The
    corresponding public key availabity in launchpad is also checked.
    (LP: #427730, #408993)
  - add devscripts as a dependency (bug #465833), will fix all the depends
    mess later
  - bump python-distutils-extra (>= 2.18bzr1)
  ubuntu-cli Template:
  - first release using import command feature (no command written into the
    template itself). This is a basic ubuntu CLI application
  ubuntu-pygame Template:
  - first release using import command feature. python-pygame added as a
    depends
* debian/control:
  - renamed and separate in multiple packages to get a -common used with
    Quickly widgets (LP: #519633)
  - suggests python-quickly-widgets

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# -*- coding: utf-8 -*-
2
 
# Copyright 2009 Canonical Ltd.
3
 
# Author 2009 Didier Roche
 
2
# Copyright 2009 Didier Roche
4
3
#
5
4
# This file is part of Quickly
6
5
#
7
 
#This program is free software: you can redistribute it and/or modify it 
8
 
#under the terms of the GNU General Public License version 3, as published 
 
6
#This program is free software: you can redistribute it and/or modify it
 
7
#under the terms of the GNU General Public License version 3, as published
9
8
#by the Free Software Foundation.
10
9
 
11
 
#This program is distributed in the hope that it will be useful, but 
12
 
#WITHOUT ANY WARRANTY; without even the implied warranties of 
13
 
#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 
 
10
#This program is distributed in the hope that it will be useful, but
 
11
#WITHOUT ANY WARRANTY; without even the implied warranties of
 
12
#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
14
13
#PURPOSE.  See the GNU General Public License for more details.
15
14
 
16
 
#You should have received a copy of the GNU General Public License along 
 
15
#You should have received a copy of the GNU General Public License along
17
16
#with this program.  If not, see <http://www.gnu.org/licenses/>.
18
17
 
19
18
import os
 
19
import re
20
20
import subprocess
21
21
import sys
22
22
 
23
23
import builtincommands
24
 
import tools
 
24
import configurationhandler
 
25
import templatetools, tools
25
26
 
26
27
import gettext
27
28
from gettext import gettext as _
29
30
 
30
31
gettext.textdomain('quickly')
31
32
 
32
 
# double depths tabular : template (or "builtin"), name
 
33
# A dictionary with keys either 'builtins' or the names of templates. Values
 
34
# are another dictionary mapping command names to their command objects. This
 
35
# is used as a cache of all commands that we've found in templates.
33
36
__commands = {}
34
37
 
 
38
def try_to_import_command(commands, importing_template, imported_template,
 
39
                          command_name):
 
40
    """Import command in one template from another template
 
41
 
 
42
    Ignore unknown command or template"""
 
43
 
 
44
    # let's override by locally defined command
 
45
    if command_name not in commands[importing_template]:
 
46
         try:
 
47
             commands[importing_template][command_name] = \
 
48
             commands[imported_template][command_name]
 
49
         except KeyError:
 
50
             # command/template doesn't exist: ignore
 
51
             pass
 
52
    return commands
35
53
 
36
54
def get_all_commands():
37
55
    """Load all commands
38
 
    
39
 
    First, load template command and then builtins one. Push right parameters depending
40
 
    if hooks are available, or if the command execution is special
41
 
    You can note that create command is automatically overloaded atm"""
42
 
 
 
56
 
 
57
    First, load template command and then builtins one. Push right parameters
 
58
    depending if hooks are available, or if the command execution is special
 
59
    You can note that create command is automatically overloaded atm.
 
60
    """
 
61
 
 
62
    global __commands
43
63
    if len(__commands) > 0:
44
64
        return __commands
45
65
 
46
66
    try:
47
67
        template_directories = tools.get_template_directories()
48
 
    except tools.no_template_path_not_found:
 
68
    except tools.template_path_not_found:
49
69
        template_directories = []
 
70
    import_commands = {}
 
71
 
50
72
    for template_dir in template_directories:
51
73
        for template in os.listdir(template_dir):
52
74
            __commands[template] = {}
 
75
            import_commands[template] = {}
53
76
            template_path = os.path.join(template_dir, template)
54
77
 
55
78
            # load special attributes declared for every command
56
79
            launch_inside_project_command_list = []
57
80
            launch_outside_project_command_list = []
58
81
            command_followed_by_command_list = []
 
82
            current_template_import = None
 
83
 
59
84
            try:
60
 
                files_command_parameters = file(os.path.join(template_path, "commandsconfig"), 'rb')
61
 
                for line in files_command_parameters: 
62
 
                    fields = line.split('#')[0] # Suppress commentary after the value in configuration file and in full line
 
85
                files_command_parameters = file(
 
86
                    os.path.join(template_path, "commandsconfig"), 'rb')
 
87
                for line in files_command_parameters:
 
88
                    # Suppress commentary after the value in configuration
 
89
                    # file and in full line.
 
90
                    fields = line.split('#')[0]
63
91
                    fields = fields.split('=') # Separate variable from value
64
 
                    # normally, we have two fields in "fields"
 
92
                    # command definitions or import have two fields
65
93
                    if len(fields) == 2:
66
94
                        targeted_property = fields[0].strip()
67
 
                        command_list = [command.strip() for command in fields[1].split(';')]
68
 
                        if targeted_property == 'COMMANDS_LAUNCHED_IN_OR_OUTSIDE_PROJECT':
69
 
                            launch_inside_project_command_list.extend(command_list)
70
 
                            launch_outside_project_command_list.extend(command_list)
71
 
                        if targeted_property == 'COMMANDS_LAUNCHED_OUTSIDE_PROJECT_ONLY':
72
 
                            launch_outside_project_command_list.extend(command_list)
73
 
                        if targeted_property == 'COMMANDS_FOLLOWED_BY_COMMAND':
74
 
                            command_followed_by_command_list.extend(command_list)
 
95
                        command_list = [
 
96
                            command.strip()
 
97
                            for command in fields[1].split(';')]
 
98
                        if (targeted_property
 
99
                            == 'COMMANDS_LAUNCHED_IN_OR_OUTSIDE_PROJECT'):
 
100
                            launch_inside_project_command_list.extend(
 
101
                                command_list)
 
102
                            launch_outside_project_command_list.extend(
 
103
                                command_list)
 
104
                        if (targeted_property
 
105
                            == 'COMMANDS_LAUNCHED_OUTSIDE_PROJECT_ONLY'):
 
106
                            launch_outside_project_command_list.extend(
 
107
                                command_list)
 
108
                        if (targeted_property
 
109
                            == 'COMMANDS_FOLLOWED_BY_COMMAND'):
 
110
                            command_followed_by_command_list.extend(
 
111
                                command_list)
 
112
                        if (targeted_property
 
113
                            == 'IMPORT') and current_template_import:
 
114
                            import_commands[template][current_template_import] \
 
115
                                           = command_list
 
116
                    else:
 
117
                        # try to fetch import command results
 
118
                        reg_result = re.search('\[(.*)\]', fields[0].strip())
 
119
                        if reg_result:
 
120
                            current_template_import = reg_result.group(1)
 
121
                        
75
122
            except (OSError, IOError):
76
123
                pass
77
124
 
81
128
                if "." in command_name:
82
129
                    command_name = ".".join(command_name.split('.')[0:-1])
83
130
 
84
 
                if os.path.isfile(file_path) and os.access(file_path, os.X_OK): # add the command to the list if is executable
85
 
                    hooks = {'pre': None, 'post':None}
 
131
                # add the command to the list if is executable (commands are
 
132
                # only formed of executable files)
 
133
                if os.path.isfile(file_path) and os.access(file_path, os.X_OK):
 
134
                    hooks = {'pre': None, 'post': None}
86
135
                    for event in ('pre', 'post'):
87
 
                        if hasattr(builtincommands, event + '_' + command_name):
88
 
                            hooks[event] = getattr(builtincommands, event + '_' + command_name)
89
 
                    
90
 
                    # define special options for command    
 
136
                        event_hook = getattr(
 
137
                            builtincommands, event + '_' + command_name, None)
 
138
                        if event_hook:
 
139
                            hooks[event] = event_hook
 
140
 
 
141
                    # define special options for command
91
142
                    launch_inside_project = False
92
143
                    launch_outside_project = False
93
144
                    followed_by_template = False
99
150
                        followed_by_template = True
100
151
                    if command_name in builtincommands.followed_by_command:
101
152
                        followed_by_command = True
102
 
                    # default for commands: if not inside nor outside, and it's a template command, make it launch inside a project only
103
 
                    if not launch_inside_project and not launch_outside_project:
 
153
                    # default for commands: if not inside nor outside, and
 
154
                    # it's a template command, make it launch inside a project
 
155
                    # only
 
156
                    if not (launch_inside_project or launch_outside_project):
104
157
                        launch_inside_project = True
105
 
                    
106
 
                    __commands[template][command_name] = Command(command_name, file_path, template, launch_inside_project, launch_outside_project, followed_by_template, followed_by_command, hooks['pre'], hooks['post'])
107
 
 
108
 
     
 
158
 
 
159
                    __commands[template][command_name] = Command(
 
160
                        command_name, file_path, template,
 
161
                        launch_inside_project, launch_outside_project,
 
162
                        followed_by_template, followed_by_command,
 
163
                        hooks['pre'], hooks['post'])
 
164
 
 
165
    # now try to import command for existing templates
 
166
    for importing_template in import_commands:
 
167
        for imported_template in import_commands[importing_template]:
 
168
            for command_name in import_commands[importing_template][imported_template]:
 
169
                # if instruction to import all commands, get them first
 
170
                if command_name == 'all':
 
171
                    try:
 
172
                        for command_name in __commands[imported_template]:
 
173
                            __commands = try_to_import_command(__commands,
 
174
                                         importing_template, imported_template,
 
175
                                         command_name)
 
176
                    except KeyError:
 
177
                        # template doesn't exist: ignore
 
178
                        pass
 
179
                    break # no need to cycle anymore (all commands imported)
 
180
                else:
 
181
                    __commands = try_to_import_command(__commands,
 
182
                                 importing_template, imported_template,
 
183
                                 command_name)
 
184
 
109
185
    # add builtin commands (avoiding gettext and hooks)
110
186
    __commands['builtins'] = {}
111
187
    for elem in dir(builtincommands):
112
188
        command = getattr(builtincommands, elem)
113
 
        if callable(command) and not command.__name__.startswith(('pre_', 'post_', 'gettext')):
 
189
        if (callable(command)
 
190
            and not command.__name__.startswith(('pre_', 'post_', 'gettext'))):
114
191
            command_name = command.__name__
115
192
            # here, special case for some commands
116
193
            launch_inside_project = False
127
204
            if command_name in builtincommands.followed_by_command:
128
205
                followed_by_command = True
129
206
 
130
 
            # default for commands: if not inside nor outside only, and it's a builtin command, make it launch wherever
 
207
            # default for commands: if not inside nor outside only, and it's a
 
208
            # builtin command, make it launch wherever
131
209
            if not launch_inside_project and not launch_outside_project:
132
210
                launch_inside_project = True
133
211
                launch_outside_project = True
134
 
        
135
 
            hooks = {'pre': None, 'post':None}
 
212
 
 
213
            hooks = {'pre': None, 'post': None}
136
214
            for event in ('pre', 'post'):
137
 
                if hasattr(builtincommands, event + '_' + command_name):
138
 
                    hooks[event] = getattr(builtincommands, event + '_' + command_name)               
139
 
 
140
 
            __commands['builtins'][command_name] = Command(command_name, command, None, launch_inside_project, launch_outside_project, followed_by_template, followed_by_command, hooks['pre'], hooks['post'])
141
 
                
 
215
                event_hook = getattr(builtincommands,
 
216
                                     event + '_' + command_name, None)
 
217
                if event_hook:
 
218
                    hooks[event] = event_hook
 
219
 
 
220
            __commands['builtins'][command_name] = Command(
 
221
                command_name, command, None, launch_inside_project,
 
222
                launch_outside_project, followed_by_template,
 
223
                followed_by_command, hooks['pre'], hooks['post'])
 
224
 
142
225
    return __commands
143
 
    
144
 
 
145
 
def get_command_by_criteria(**criterias):
 
226
 
 
227
 
 
228
def get_commands_by_criteria(**criterias):
146
229
    """Get a list of all commands corresponding to criterias
147
 
    
148
 
    Criterias correponds to Command object properties"""
149
 
 
150
 
    # all criterias are None by default, which means, don't care about the value
 
230
 
 
231
    Criterias correponds to Command object properties.
 
232
    """
 
233
 
 
234
    # all criterias are None by default, which means, don't care about the
 
235
    # value.
151
236
    matched_commands = []
152
237
    all_commands = get_all_commands()
153
238
 
154
239
    for template_available in all_commands:
155
 
        if criterias.has_key('template') and criterias['template'] != template_available:
156
 
            continue # to speed up the search
 
240
        if ('template' in criterias
 
241
            and criterias['template'] != template_available):
 
242
            continue # go to next round if no template match
157
243
        for candidate_command_name in all_commands[template_available]:
158
 
            candidate_command = all_commands[template_available][candidate_command_name]
 
244
            candidate_command = all_commands[
 
245
                template_available][candidate_command_name]
159
246
            command_ok = True
160
247
            # check all criterias (template has already been checked)
161
248
            for elem in criterias:
162
 
                if elem is not 'template' and getattr(candidate_command, elem) != criterias[elem]:
 
249
                if (elem is not 'template'
 
250
                    and getattr(candidate_command, elem) != criterias[elem]):
163
251
                    command_ok = False
164
252
                    continue # no need to check other criterias
165
253
            if command_ok:
166
 
                matched_commands.append(candidate_command)    
167
 
    
 
254
                matched_commands.append(candidate_command)
 
255
 
168
256
    return matched_commands
169
257
 
170
258
 
 
259
def get_command_names_by_criteria(**criteria):
 
260
    """Get a tuple of all command names corresponding to criteria.
 
261
 
 
262
    'criteria' correponds to Command object properties.
 
263
    """
 
264
    return (command.name for command in get_commands_by_criteria(**criteria))
 
265
 
 
266
 
171
267
def get_all_templates():
172
 
    """Get a list of all templates"""
173
 
    
174
 
    return [template for template in get_all_commands().keys() if template != "builtins"]
175
 
 
 
268
    """Get a tuple of all templates"""
 
269
    return (
 
270
        template for template in get_all_commands().keys()
 
271
        if template != "builtins")
176
272
 
177
273
class Command:
178
274
 
179
275
    def _die(self, function_name, return_code):
 
276
       '''Quit immediately and print error if return_code != 4'''
 
277
       if return_code != 4:
180
278
            print _("ERROR: %s command failed") % function_name
181
279
            print _("Aborting")
182
 
            sys.exit(return_code)
 
280
       sys.exit(return_code)
183
281
 
184
 
    def __init__(self, command_name, command, template=None, inside_project=True, outside_project=False, followed_by_template=False, followed_by_command=False, prehook=None, posthook=None):
 
282
    def __init__(self, command_name, command, template=None,
 
283
                 inside_project=True, outside_project=False,
 
284
                 followed_by_template=False, followed_by_command=False,
 
285
                 prehook=None, posthook=None):
185
286
        self.command = command
 
287
        # self.template is the native template where the command is from
 
288
        # if this command is imported into another template, the object
 
289
        # is still the same, only the access byx
 
290
        # get_all_commands()[importing_template]["command_name"]
186
291
        self.template = template
187
292
        self.prehook = prehook
188
293
        self.posthook = posthook
194
299
 
195
300
    def shell_completion(self, template_in_cli, args):
196
301
        """Smart completion of a command
197
 
  
198
 
        This command try to see if the command is followed by a template and present template
199
 
        if it's the case. Otherwise, it calls the corresponding command argument"""
200
 
        
 
302
 
 
303
        This command try to see if the command is followed by a template and
 
304
        present template if it's the case. Otherwise, it calls the
 
305
        corresponding command argument.
 
306
        """
 
307
 
201
308
        completion = []
202
309
 
203
310
        if len(args) == 1:
205
312
                if self.followed_by_template: # template completion
206
313
                    if not self.template: # builtins command case
207
314
                        completion.extend(get_all_templates())
208
 
                    else: # complete with current template (which != from template_in_cli: ex create command (multiple templates))
209
 
                        completion.extend([self.template])
 
315
                    else:
 
316
                        # complete with current template (which != from
 
317
                        # template_in_cli: ex create command (multiple
 
318
                        # templates)). Fetch as well imported command in
 
319
                        # other template
 
320
                        for template in get_all_templates():
 
321
                            try:
 
322
                                if get_all_commands()[template][self.name] == self:
 
323
                                    completion.extend([template])
 
324
                            except KeyError:
 
325
                                pass
210
326
            else: # there is a template, add template commands
211
327
                if self.followed_by_command: # template command completion
212
 
                    completion.extend([command.name for command in get_command_by_criteria(template=template_in_cli)])
 
328
                    completion.extend(
 
329
                        get_command_names_by_criteria(
 
330
                            template=template_in_cli))
213
331
            if self.followed_by_command: # builtin command completion
214
 
                completion.extend([command.name for command in get_command_by_criteria(template="builtins")])
 
332
                completion.extend(
 
333
                    get_command_names_by_criteria(template="builtins"))
215
334
 
216
335
        elif len(args) == 2:
217
336
            if not template_in_cli and self.followed_by_template:
218
337
                template_in_cli = args[0]
219
 
                if self.followed_by_command: # template command completion and builtins command
220
 
                    completion.extend([command.name for command in get_command_by_criteria(template=template_in_cli)])
221
 
                    completion.extend([command.name for command in get_command_by_criteria(template="builtins")])
 
338
                # template command completion and builtins command.
 
339
                if self.followed_by_command:
 
340
                    completion.extend(
 
341
                        get_command_names_by_criteria(
 
342
                            template=template_in_cli))
 
343
                    completion.extend(
 
344
                        get_command_names_by_criteria(template="builtins"))
222
345
 
223
 
        # give to the command the opportunity of giving some shell-completion features        
 
346
        # give to the command the opportunity of giving some shell-completion
 
347
        # features
224
348
        if template_in_cli == self.template and len(completion) == 0:
225
349
            if callable(self.command): # Internal function
226
 
                completion.extend(self.command(template_in_cli, "", args, True))
 
350
                completion.extend(
 
351
                    self.command(template_in_cli, "", args, True))
227
352
            else: # External command
228
 
                instance = subprocess.Popen([self.command, "shell-completion"] + args, stdout=subprocess.PIPE)
 
353
                instance = subprocess.Popen(
 
354
                    [self.command, "shell-completion"] + args,
 
355
                    stdout=subprocess.PIPE)
229
356
                command_return_completion, err = instance.communicate()
230
357
                if instance.returncode != 0:
231
358
                    print err
232
 
                    sys.exit(1)                    
 
359
                    sys.exit(1)
233
360
                completion.extend(command_return_completion.split(','))
234
361
 
235
 
        return(" ".join(completion))
 
362
        return " ".join(completion)
236
363
 
237
 
    def help(self, dest_path,command_args):
 
364
    def help(self, dest_path, command_args):
238
365
        """Print help of the current command"""
239
366
 
240
 
        return_code = 0        
 
367
        return_code = 0
241
368
        if callable(self.command): # intern function, return __doc__
242
369
            print (self.command.__doc__)
243
370
        else: # launch command with "help" parameter
244
 
            return_code = subprocess.call([self.command, "help"] + command_args, cwd=dest_path)
 
371
            return_code = subprocess.call(
 
372
                [self.command, "help"] + command_args, cwd=dest_path)
245
373
 
246
 
        return(return_code)
 
374
        return return_code
247
375
 
248
376
    def is_right_context(self, dest_path, verbose=True):
249
 
        """Check if we are in the right context for launching the command"""
250
 
        
251
 
        # verbose à false pour l'introspection des commandes dispos
252
 
        
 
377
        """Check if we are in the right context for launching the command.
 
378
 
 
379
        If you are using this to introspect available commands, then set
 
380
        verbose to False.
 
381
        """
253
382
        # check if dest_path check outside or inside only project :)
254
383
        if self.inside_project and not self.outside_project:
255
384
            try:
256
385
                project_path = tools.get_root_project_path(dest_path)
257
386
            except tools.project_path_not_found:
258
387
                if verbose:
259
 
                    print _("ERROR: Can't find project in %s.\nEnsure you launch this command from a quickly project directory.") % dest_path
 
388
                    print (_(
 
389
                        "ERROR: Can't find project in %s.\nEnsure you launch "
 
390
                        "this command from a quickly project directory.")
 
391
                           % dest_path)
260
392
                    print _("Aborting")
261
393
                return False
262
394
        if self.outside_project and not self.inside_project:
263
395
            try:
264
396
                project_path = tools.get_root_project_path(dest_path)
265
397
                if verbose:
266
 
                    print _("ERROR: %s is a project. You can't launch %s command within a project. " \
267
 
                            "Please choose another path." % (project_path, self.command))
 
398
                    print _(
 
399
                        "ERROR: %s is a project. You can't launch %s command "
 
400
                        "within a project. Please choose another path."
 
401
                        % (project_path, self.command))
268
402
                    print _("Aborting")
269
403
                return False
270
404
            except tools.project_path_not_found:
271
405
                pass
272
 
            
 
406
 
273
407
        return True
274
408
 
275
 
 
276
 
    def launch(self, current_dir, command_args, template=None):
 
409
    def launch(self, current_dir, command_args, project_template=None):
277
410
        """Launch command and hooks for it
278
 
        
279
 
        This command will perform the right action (insider function or script execution) after having
280
 
        checked the context"""
281
 
    
282
 
        # template is current template (it will be useful for builtin commands)
283
411
 
284
 
        # if template not specified, take the one for the command
285
 
        # the template argument is useful when builtin commands which behavior take into account the template name
286
 
        if template is None:
287
 
            template = self.template # (which can be None if it's a builtin command launched outside a project)
 
412
        This command will perform the right action (insider function or script
 
413
        execution) after having checked the context.
 
414
        """
288
415
 
289
416
        if not self.is_right_context(current_dir): # check in verbose mode
290
 
            return(1)
 
417
            return 1
 
418
 
 
419
 
291
420
 
292
421
        # get root project dir
293
422
        try:
294
423
            project_path = tools.get_root_project_path(current_dir)
295
 
        except tools.project_path_not_found:       
 
424
            inside_project = True
 
425
        except tools.project_path_not_found:
296
426
            # launch in current project
297
427
            project_path = current_dir
 
428
            inside_project = False
 
429
 
 
430
        # transition if we are inside a project and template is not None (builtins)
 
431
        # (call upgrade from native template)
 
432
        if inside_project and self.name != "upgrade" and self.template:
 
433
            (project_version, template_version) = templatetools.get_project_and_template_versions(self.template)
 
434
            if project_version < template_version:
 
435
                try:
 
436
                    return_code = get_all_commands()[self.template]['upgrade'].launch( current_dir, [project_version, template_version], project_template)
 
437
                    if return_code == 0:
 
438
                        templatetools.update_version_in_project_file(template_version, self.template)
 
439
                    else:
 
440
                        sys.exit(return_code)
 
441
                except KeyError: # if KeyError, no upgrade command for this template
 
442
                    pass
298
443
 
299
444
        if self.prehook:
300
 
            return_code = self.prehook(template, project_path, command_args)
 
445
            return_code = self.prehook(self.template, project_template, project_path, command_args)
301
446
            if return_code != 0:
302
447
                self._die(self.prehook.__name__, return_code)
303
 
        
 
448
 
304
449
        if callable(self.command): # Internal function
305
 
            return_code = self.command(template, project_path, command_args)
 
450
            return_code = self.command(project_template, project_path, command_args)
306
451
        else: # External command
307
 
            return_code = subprocess.call([self.command] + command_args, cwd=project_path)
 
452
            return_code = subprocess.call(
 
453
                [self.command] + command_args, cwd=project_path)
308
454
        if return_code != 0:
309
 
            self._die(self.name,return_code)
 
455
            self._die(self.name, return_code)
310
456
 
311
457
        if self.posthook:
312
 
            return_code = self.posthook(template, project_path, command_args)
 
458
            return_code = self.posthook(project_template, project_path, command_args)
313
459
            if return_code != 0:
314
460
                self._die(self.posthook.__name__, return_code)
315
 
        
316
 
        return(0)
317
 
 
318
 
 
319
 
 
 
461
 
 
462
        return 0