Show More
@@ -7,6 +7,8 b'' | |||||
7 |
|
7 | |||
8 | from __future__ import absolute_import |
|
8 | from __future__ import absolute_import | |
9 |
|
9 | |||
|
10 | import functools | |||
|
11 | ||||
10 | from .i18n import _ |
|
12 | from .i18n import _ | |
11 | from . import ( |
|
13 | from . import ( | |
12 | error, |
|
14 | error, | |
@@ -24,6 +26,179 b' nevernegate = {' | |||||
24 | 'version', |
|
26 | 'version', | |
25 | } |
|
27 | } | |
26 |
|
28 | |||
|
29 | def _earlyoptarg(arg, shortlist, namelist): | |||
|
30 | """Check if the given arg is a valid unabbreviated option | |||
|
31 | ||||
|
32 | Returns (flag_str, has_embedded_value?, embedded_value, takes_value?) | |||
|
33 | ||||
|
34 | >>> def opt(arg): | |||
|
35 | ... return _earlyoptarg(arg, b'R:q', [b'cwd=', b'debugger']) | |||
|
36 | ||||
|
37 | long form: | |||
|
38 | ||||
|
39 | >>> opt(b'--cwd') | |||
|
40 | ('--cwd', False, '', True) | |||
|
41 | >>> opt(b'--cwd=') | |||
|
42 | ('--cwd', True, '', True) | |||
|
43 | >>> opt(b'--cwd=foo') | |||
|
44 | ('--cwd', True, 'foo', True) | |||
|
45 | >>> opt(b'--debugger') | |||
|
46 | ('--debugger', False, '', False) | |||
|
47 | >>> opt(b'--debugger=') # invalid but parsable | |||
|
48 | ('--debugger', True, '', False) | |||
|
49 | ||||
|
50 | short form: | |||
|
51 | ||||
|
52 | >>> opt(b'-R') | |||
|
53 | ('-R', False, '', True) | |||
|
54 | >>> opt(b'-Rfoo') | |||
|
55 | ('-R', True, 'foo', True) | |||
|
56 | >>> opt(b'-q') | |||
|
57 | ('-q', False, '', False) | |||
|
58 | >>> opt(b'-qfoo') # invalid but parsable | |||
|
59 | ('-q', True, 'foo', False) | |||
|
60 | ||||
|
61 | unknown or invalid: | |||
|
62 | ||||
|
63 | >>> opt(b'--unknown') | |||
|
64 | ('', False, '', False) | |||
|
65 | >>> opt(b'-u') | |||
|
66 | ('', False, '', False) | |||
|
67 | >>> opt(b'-ufoo') | |||
|
68 | ('', False, '', False) | |||
|
69 | >>> opt(b'--') | |||
|
70 | ('', False, '', False) | |||
|
71 | >>> opt(b'-') | |||
|
72 | ('', False, '', False) | |||
|
73 | >>> opt(b'-:') | |||
|
74 | ('', False, '', False) | |||
|
75 | >>> opt(b'-:foo') | |||
|
76 | ('', False, '', False) | |||
|
77 | """ | |||
|
78 | if arg.startswith('--'): | |||
|
79 | flag, eq, val = arg.partition('=') | |||
|
80 | if flag[2:] in namelist: | |||
|
81 | return flag, bool(eq), val, False | |||
|
82 | if flag[2:] + '=' in namelist: | |||
|
83 | return flag, bool(eq), val, True | |||
|
84 | elif arg.startswith('-') and arg != '-' and not arg.startswith('-:'): | |||
|
85 | flag, val = arg[:2], arg[2:] | |||
|
86 | i = shortlist.find(flag[1:]) | |||
|
87 | if i >= 0: | |||
|
88 | return flag, bool(val), val, shortlist.startswith(':', i + 1) | |||
|
89 | return '', False, '', False | |||
|
90 | ||||
|
91 | def earlygetopt(args, shortlist, namelist, gnu=False, keepsep=False): | |||
|
92 | """Parse options like getopt, but ignores unknown options and abbreviated | |||
|
93 | forms | |||
|
94 | ||||
|
95 | If gnu=False, this stops processing options as soon as a non/unknown-option | |||
|
96 | argument is encountered. Otherwise, option and non-option arguments may be | |||
|
97 | intermixed, and unknown-option arguments are taken as non-option. | |||
|
98 | ||||
|
99 | If keepsep=True, '--' won't be removed from the list of arguments left. | |||
|
100 | This is useful for stripping early options from a full command arguments. | |||
|
101 | ||||
|
102 | >>> def get(args, gnu=False, keepsep=False): | |||
|
103 | ... return earlygetopt(args, b'R:q', [b'cwd=', b'debugger'], | |||
|
104 | ... gnu=gnu, keepsep=keepsep) | |||
|
105 | ||||
|
106 | default parsing rules for early options: | |||
|
107 | ||||
|
108 | >>> get([b'x', b'--cwd', b'foo', b'-Rbar', b'-q', b'y'], gnu=True) | |||
|
109 | ([('--cwd', 'foo'), ('-R', 'bar'), ('-q', '')], ['x', 'y']) | |||
|
110 | >>> get([b'x', b'--cwd=foo', b'y', b'-R', b'bar', b'--debugger'], gnu=True) | |||
|
111 | ([('--cwd', 'foo'), ('-R', 'bar'), ('--debugger', '')], ['x', 'y']) | |||
|
112 | >>> get([b'--unknown', b'--cwd=foo', b'--', '--debugger'], gnu=True) | |||
|
113 | ([('--cwd', 'foo')], ['--unknown', '--debugger']) | |||
|
114 | ||||
|
115 | restricted parsing rules (early options must come first): | |||
|
116 | ||||
|
117 | >>> get([b'--cwd', b'foo', b'-Rbar', b'x', b'-q', b'y'], gnu=False) | |||
|
118 | ([('--cwd', 'foo'), ('-R', 'bar')], ['x', '-q', 'y']) | |||
|
119 | >>> get([b'--cwd=foo', b'x', b'y', b'-R', b'bar', b'--debugger'], gnu=False) | |||
|
120 | ([('--cwd', 'foo')], ['x', 'y', '-R', 'bar', '--debugger']) | |||
|
121 | >>> get([b'--unknown', b'--cwd=foo', b'--', '--debugger'], gnu=False) | |||
|
122 | ([], ['--unknown', '--cwd=foo', '--debugger']) | |||
|
123 | ||||
|
124 | stripping early options (without loosing '--'): | |||
|
125 | ||||
|
126 | >>> get([b'x', b'-Rbar', b'--', '--debugger'], gnu=True, keepsep=True)[1] | |||
|
127 | ['x', '--', '--debugger'] | |||
|
128 | ||||
|
129 | last argument: | |||
|
130 | ||||
|
131 | >>> get([b'--cwd']) | |||
|
132 | ([], ['--cwd']) | |||
|
133 | >>> get([b'--cwd=foo']) | |||
|
134 | ([('--cwd', 'foo')], []) | |||
|
135 | >>> get([b'-R']) | |||
|
136 | ([], ['-R']) | |||
|
137 | >>> get([b'-Rbar']) | |||
|
138 | ([('-R', 'bar')], []) | |||
|
139 | >>> get([b'-q']) | |||
|
140 | ([('-q', '')], []) | |||
|
141 | >>> get([b'-q', b'--']) | |||
|
142 | ([('-q', '')], []) | |||
|
143 | ||||
|
144 | value passed to bool options: | |||
|
145 | ||||
|
146 | >>> get([b'--debugger=foo', b'x']) | |||
|
147 | ([], ['--debugger=foo', 'x']) | |||
|
148 | >>> get([b'-qfoo', b'x']) | |||
|
149 | ([], ['-qfoo', 'x']) | |||
|
150 | ||||
|
151 | short option isn't separated with '=': | |||
|
152 | ||||
|
153 | >>> get([b'-R=bar']) | |||
|
154 | ([('-R', '=bar')], []) | |||
|
155 | ||||
|
156 | ':' may be in shortlist, but shouldn't be taken as an option letter: | |||
|
157 | ||||
|
158 | >>> get([b'-:', b'y']) | |||
|
159 | ([], ['-:', 'y']) | |||
|
160 | ||||
|
161 | '-' is a valid non-option argument: | |||
|
162 | ||||
|
163 | >>> get([b'-', b'y']) | |||
|
164 | ([], ['-', 'y']) | |||
|
165 | """ | |||
|
166 | # ignoring everything just after '--' isn't correct as '--' may be an | |||
|
167 | # option value (e.g. ['-R', '--']), but we do that consistently. | |||
|
168 | try: | |||
|
169 | argcount = args.index('--') | |||
|
170 | except ValueError: | |||
|
171 | argcount = len(args) | |||
|
172 | ||||
|
173 | parsedopts = [] | |||
|
174 | parsedargs = [] | |||
|
175 | pos = 0 | |||
|
176 | while pos < argcount: | |||
|
177 | arg = args[pos] | |||
|
178 | flag, hasval, val, takeval = _earlyoptarg(arg, shortlist, namelist) | |||
|
179 | if not hasval and takeval and pos + 1 >= argcount: | |||
|
180 | # missing last argument | |||
|
181 | break | |||
|
182 | if not flag or hasval and not takeval: | |||
|
183 | # non-option argument or -b/--bool=INVALID_VALUE | |||
|
184 | if gnu: | |||
|
185 | parsedargs.append(arg) | |||
|
186 | pos += 1 | |||
|
187 | else: | |||
|
188 | break | |||
|
189 | elif hasval == takeval: | |||
|
190 | # -b/--bool or -s/--str=VALUE | |||
|
191 | parsedopts.append((flag, val)) | |||
|
192 | pos += 1 | |||
|
193 | else: | |||
|
194 | # -s/--str VALUE | |||
|
195 | parsedopts.append((flag, args[pos + 1])) | |||
|
196 | pos += 2 | |||
|
197 | ||||
|
198 | parsedargs.extend(args[pos:argcount]) | |||
|
199 | parsedargs.extend(args[argcount + (not keepsep):]) | |||
|
200 | return parsedopts, parsedargs | |||
|
201 | ||||
27 | def gnugetopt(args, options, longoptions): |
|
202 | def gnugetopt(args, options, longoptions): | |
28 | """Parse options mostly like getopt.gnu_getopt. |
|
203 | """Parse options mostly like getopt.gnu_getopt. | |
29 |
|
204 | |||
@@ -51,7 +226,7 b' def gnugetopt(args, options, longoptions' | |||||
51 | return opts, args |
|
226 | return opts, args | |
52 |
|
227 | |||
53 |
|
228 | |||
54 | def fancyopts(args, options, state, gnu=False): |
|
229 | def fancyopts(args, options, state, gnu=False, early=False): | |
55 | """ |
|
230 | """ | |
56 | read args, parse options, and store options in state |
|
231 | read args, parse options, and store options in state | |
57 |
|
232 | |||
@@ -124,7 +299,9 b' def fancyopts(args, options, state, gnu=' | |||||
124 | namelist.append(oname) |
|
299 | namelist.append(oname) | |
125 |
|
300 | |||
126 | # parse arguments |
|
301 | # parse arguments | |
127 |
if |
|
302 | if early: | |
|
303 | parse = functools.partial(earlygetopt, gnu=gnu) | |||
|
304 | elif gnu: | |||
128 | parse = gnugetopt |
|
305 | parse = gnugetopt | |
129 | else: |
|
306 | else: | |
130 | parse = pycompat.getoptb |
|
307 | parse = pycompat.getoptb |
@@ -48,6 +48,7 b" testmod('mercurial.context')" | |||||
48 | testmod('mercurial.dagparser', optionflags=doctest.NORMALIZE_WHITESPACE) |
|
48 | testmod('mercurial.dagparser', optionflags=doctest.NORMALIZE_WHITESPACE) | |
49 | testmod('mercurial.dispatch') |
|
49 | testmod('mercurial.dispatch') | |
50 | testmod('mercurial.encoding') |
|
50 | testmod('mercurial.encoding') | |
|
51 | testmod('mercurial.fancyopts') | |||
51 | testmod('mercurial.formatter') |
|
52 | testmod('mercurial.formatter') | |
52 | testmod('mercurial.hg') |
|
53 | testmod('mercurial.hg') | |
53 | testmod('mercurial.hgweb.hgwebdir_mod') |
|
54 | testmod('mercurial.hgweb.hgwebdir_mod') |
General Comments 0
You need to be logged in to leave comments.
Login now