3
3
# Licensed under the Apache License, Version 2.0
4
4
# See LICENSE file for details.
5
5
6
+ from .lib import lazyregex
7
+
6
8
# NOTE: argcomplete must be first!
7
9
# argcomplete, pip install argcomplete, tab-completion for argparse, Apache-2
8
10
import argcomplete
11
13
import argparse
12
14
import logging
13
15
import sys
14
- import pkg_resources
15
16
from functools import reduce
16
17
17
- # subcommand modules, , add subcommands, internal
18
- from . import version
19
- from . import link
20
- from . import link_target
21
- from . import install
22
- from . import update
23
- from . import target
24
- from . import build
25
- from . import init
26
- from . import publish
27
- from . import unpublish
28
- from . import debug
29
- from . import test_subcommand as test
30
- from . import login
31
- from . import logout
32
- from . import list as list_command
33
- from . import uninstall
34
- from . import owners
35
- from . import licenses
36
- from . import clean
37
- from . import search
38
- from . import config
39
-
40
18
# logging setup, , setup the logging system, internal
41
19
from .lib import logging_setup
42
20
# detect, , detect things about the system, internal
@@ -54,6 +32,49 @@ def splitList(l, at_value):
54
32
r [- 1 ].append (x )
55
33
return r
56
34
35
+ # (instead of subclassing argparse._SubParsersAction, we monkey-patch it,
36
+ # because argcomplete has special-cases for the _SubParsersAction class that
37
+ # it's impossible to extend to another class)
38
+ #
39
+ # add a add_parser_async function, which allows a subparser to be added whose
40
+ # options are not evaluated until the subparser has been selected. This allows
41
+ # us to defer loading the modules for all the subcommands until they are
42
+ # actually:
43
+ def _SubParsersAction_addParserAsync (self , name , * args , ** kwargs ):
44
+ if not 'callback' in kwargs :
45
+ raise ValueError ('callback=fn(parser) argument must be specified' )
46
+ callback = kwargs ['callback' ]
47
+ del kwargs ['callback' ]
48
+ parser = self .add_parser (name , * args , ** kwargs )
49
+ parser ._lazy_load_callback = callback
50
+ return None
51
+ argparse ._SubParsersAction .add_parser_async = _SubParsersAction_addParserAsync
52
+
53
+ def _wrapSubParserActionCall (orig_call ):
54
+ def wrapped_call (self , parser , namespace , values , option_string = None ):
55
+ parser_name = values [0 ]
56
+ if parser_name in self ._name_parser_map :
57
+ subparser = self ._name_parser_map [parser_name ]
58
+ if hasattr (subparser , '_lazy_load_callback' ):
59
+ # the callback is responsible for adding the subparser's own
60
+ # arguments:
61
+ subparser ._lazy_load_callback (subparser )
62
+ # now we can go ahead and call the subparser action: its arguments are
63
+ # now all set up
64
+ return orig_call (self , parser , namespace , values , option_string )
65
+ return wrapped_call
66
+ argparse ._SubParsersAction .__call__ = _wrapSubParserActionCall (argparse ._SubParsersAction .__call__ )
67
+
68
+
69
+ # Override the argparse default version action so that we can avoid importing
70
+ # pkg_resources (which is slowww) unless someone has actually asked for the
71
+ # version
72
+ class FastVersionAction (argparse .Action ):
73
+ def __call__ (self , parser , args , values , option_string = None ):
74
+ import pkg_resources
75
+ sys .stdout .write (pkg_resources .require ("yotta" )[0 ].version + '\n ' )
76
+ sys .exit (0 )
77
+
57
78
58
79
def main ():
59
80
parser = argparse .ArgumentParser (
@@ -62,9 +83,8 @@ def main():
62
83
'For more detailed help on each subcommand, run: yotta <subcommand> --help'
63
84
)
64
85
subparser = parser .add_subparsers (metavar = '<subcommand>' )
65
-
66
- parser .add_argument ('--version' , dest = 'show_version' , action = 'version' ,
67
- version = pkg_resources .require ("yotta" )[0 ].version ,
86
+
87
+ parser .add_argument ('--version' , nargs = 0 , action = FastVersionAction ,
68
88
help = 'display the version'
69
89
)
70
90
parser .add_argument ('-v' , '--verbose' , dest = 'verbosity' , action = 'count' ,
@@ -92,25 +112,29 @@ def main():
92
112
'--registry' , default = None , dest = 'registry' , help = argparse .SUPPRESS
93
113
)
94
114
95
- def addParser (name , module , description , help = None ):
115
+ def addParser (name , module_name , description , help = None ):
96
116
if help is None :
97
117
help = description
98
- parser = subparser .add_parser (
118
+ def onParserAdded (parser ):
119
+ import importlib
120
+ module = importlib .import_module ('.' + module_name , 'yotta' )
121
+ module .addOptions (parser )
122
+ parser .set_defaults (command = module .execCommand )
123
+ subparser .add_parser_async (
99
124
name , description = description , help = help ,
100
- formatter_class = argparse .RawTextHelpFormatter
125
+ formatter_class = argparse .RawTextHelpFormatter ,
126
+ callback = onParserAdded
101
127
)
102
- module .addOptions (parser )
103
- parser .set_defaults (command = module .execCommand )
104
128
105
- addParser ('search' , search ,
129
+ addParser ('search' , ' search' ,
106
130
'Search for open-source modules and targets that have been published ' +
107
131
'to the yotta registry (with yotta publish). See help for `yotta ' +
108
132
'install --save` for installing modules, and for `yotta target` for ' +
109
133
'switching targets.'
110
134
)
111
- addParser ('init' , init , 'Create a new module.' )
112
- addParser ('install' , install , 'Install dependencies for the current module, or a specific module.' )
113
- addParser ('build' , build ,
135
+ addParser ('init' , ' init' , 'Create a new module.' )
136
+ addParser ('install' , ' install' , 'Install dependencies for the current module, or a specific module.' )
137
+ addParser ('build' , ' build' ,
114
138
'Build the current module. Options can be passed to the underlying ' + \
115
139
'build tool by passing them after --, e.g. to do a parallel build ' + \
116
140
'when make is the build tool, run:\n yotta build -- -j\n \n ' +
@@ -120,13 +144,13 @@ def addParser(name, module, description, help=None):
120
144
'all dependencies, run:\n yotta build all_tests\n \n ' ,
121
145
'Build the current module.'
122
146
)
123
- addParser ('version' , version , 'Bump the module version, or (with no arguments) display the current version.' )
124
- addParser ('link' , link , 'Symlink a module.' )
125
- addParser ('link-target' , link_target , 'Symlink a target.' )
126
- addParser ('update' , update , 'Update dependencies for the current module, or a specific module.' )
127
- addParser ('target' , target , 'Set or display the target device.' )
128
- addParser ('debug' , debug , 'Attach a debugger to the current target. Requires target support.' )
129
- addParser ('test' , test ,
147
+ addParser ('version' , ' version' , 'Bump the module version, or (with no arguments) display the current version.' )
148
+ addParser ('link' , ' link' , 'Symlink a module.' )
149
+ addParser ('link-target' , ' link_target' , 'Symlink a target.' )
150
+ addParser ('update' , ' update' , 'Update dependencies for the current module, or a specific module.' )
151
+ addParser ('target' , ' target' , 'Set or display the target device.' )
152
+ addParser ('debug' , ' debug' , 'Attach a debugger to the current target. Requires target support.' )
153
+ addParser ('test' , 'test_subcommand' ,
130
154
'Run the tests for the current module on the current target. A build ' +
131
155
'will be run first, and options to the build subcommand are also ' +
132
156
'accepted by test.\n This subcommand requires the target to provide a ' +
@@ -135,16 +159,16 @@ def addParser(name, module, description, help=None):
135
159
'each test, and may produce a summary.' ,
136
160
'Run the tests for the current module on the current target. Requires target support.'
137
161
)
138
- addParser ('publish' , publish , 'Publish a module or target to the public registry.' )
139
- addParser ('unpublish' , unpublish , 'Un-publish a recently published module or target.' )
140
- addParser ('login' , login , 'Authorize for access to private github repositories and publishing to the yotta registry.' )
141
- addParser ('logout' , logout , 'Remove saved authorization token for the current user.' )
142
- addParser ('list' , list_command , 'List the dependencies of the current module, or the inherited targets of the current target.' )
143
- addParser ('uninstall' , uninstall , 'Remove a specific dependency of the current module.' )
144
- addParser ('owners' , owners , 'Add/remove/display the owners of a module or target.' )
145
- addParser ('licenses' , licenses , 'List the licenses of the current module and its dependencies.' )
146
- addParser ('clean' , clean , 'Remove files created by yotta and the build.' )
147
- addParser ('config' , config , 'Display the target configuration info.' )
162
+ addParser ('publish' , ' publish' , 'Publish a module or target to the public registry.' )
163
+ addParser ('unpublish' , ' unpublish' , 'Un-publish a recently published module or target.' )
164
+ addParser ('login' , ' login' , 'Authorize for access to private github repositories and publishing to the yotta registry.' )
165
+ addParser ('logout' , ' logout' , 'Remove saved authorization token for the current user.' )
166
+ addParser ('list' , 'list' , 'List the dependencies of the current module, or the inherited targets of the current target.' )
167
+ addParser ('uninstall' , ' uninstall' , 'Remove a specific dependency of the current module.' )
168
+ addParser ('owners' , ' owners' , 'Add/remove/display the owners of a module or target.' )
169
+ addParser ('licenses' , ' licenses' , 'List the licenses of the current module and its dependencies.' )
170
+ addParser ('clean' , ' clean' , 'Remove files created by yotta and the build.' )
171
+ addParser ('config' , ' config' , 'Display the target configuration info.' )
148
172
149
173
# short synonyms, subparser.choices is a dictionary, so use update() to
150
174
# merge in the keys from another dictionary
0 commit comments