Merge lp:~rogpeppe/gnuflag/support-equals into lp:~gophers/gnuflag/trunk
- support-equals
- Merge into trunk
Proposed by
Roger Peppe
Status: | Merged |
---|---|
Merged at revision: | 12 |
Proposed branch: | lp:~rogpeppe/gnuflag/support-equals |
Merge into: | lp:~gophers/gnuflag/trunk |
Diff against target: |
547 lines (+258/-172) 2 files modified
flag.go (+36/-12) flag_test.go (+222/-160) |
To merge this branch: | bzr merge lp:~rogpeppe/gnuflag/support-equals |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
The Go Language Gophers | Pending | ||
Review via email: mp+127469@code.launchpad.net |
Commit message
Description of the change
gnuflag: implement = in flags.
We should be pretty close to compatible with
all GNU flag usage now.
To post a comment you must log in.
Revision history for this message
Roger Peppe (rogpeppe) wrote : | # |
Revision history for this message
Roger Peppe (rogpeppe) wrote : | # |
Please take a look.
- 14. By Roger Peppe
-
refine behaviour of -x=foo
Revision history for this message
Roger Peppe (rogpeppe) wrote : | # |
i've changed it now, based on your remarks from your original CL.
-x=foo is different from how i imagined it, but
i think it's better to go with gnu-faithfullness in this case.
PTAL.
On 2 October 2012 14:16, <email address hidden> wrote:
> On 2012/10/02 12:27:23, rog wrote:
>>
>> Please take a look.
>
>
> LGTM, nice tests. Thanks.
>
> https:/
Revision history for this message
Roger Peppe (rogpeppe) wrote : | # |
*** Submitted:
gnuflag: implement = in flags.
We should be pretty close to compatible with
all GNU flag usage now.
R=dfc
CC=
https:/
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'flag.go' |
2 | --- flag.go 2012-02-02 11:25:56 +0000 |
3 | +++ flag.go 2012-10-02 14:10:24 +0000 |
4 | @@ -35,6 +35,7 @@ |
5 | "os" |
6 | "sort" |
7 | "strconv" |
8 | + "strings" |
9 | "time" |
10 | "unicode/utf8" |
11 | ) |
12 | @@ -688,16 +689,18 @@ |
13 | } |
14 | } |
15 | |
16 | -func (f *FlagSet) parseOneGnu() (flagName string, finished bool, err error) { |
17 | +func (f *FlagSet) parseOneGnu() (flagName string, long, finished bool, err error) { |
18 | if len(f.procArgs) == 0 { |
19 | - return "", true, nil |
20 | + finished = true |
21 | + return |
22 | } |
23 | |
24 | // processing previously encountered single-rune flag |
25 | if flag := f.procFlag; len(flag) > 0 { |
26 | _, n := utf8.DecodeRuneInString(flag) |
27 | f.procFlag = flag[n:] |
28 | - return flag[0:n], false, nil |
29 | + flagName = flag[0:n] |
30 | + return |
31 | } |
32 | |
33 | a := f.procArgs[0] |
34 | @@ -707,25 +710,38 @@ |
35 | if f.allowIntersperse { |
36 | f.args = append(f.args, a) |
37 | f.procArgs = f.procArgs[1:] |
38 | - return "", false, nil |
39 | + return |
40 | } |
41 | f.args = append(f.args, f.procArgs...) |
42 | f.procArgs = nil |
43 | - return "", true, nil |
44 | + finished = true |
45 | + return |
46 | } |
47 | |
48 | // end of flags |
49 | if f.procArgs[0] == "--" { |
50 | f.args = append(f.args, f.procArgs[1:]...) |
51 | f.procArgs = nil |
52 | - return "", true, nil |
53 | + finished = true |
54 | + return |
55 | } |
56 | |
57 | // long flag signified with "--" prefix |
58 | if a[1] == '-' { |
59 | - // TODO allow '=' so we can set bools to false? |
60 | - flagName = a[2:] |
61 | + long = true |
62 | + i := strings.Index(a, "=") |
63 | + if i < 0 { |
64 | + f.procArgs = f.procArgs[1:] |
65 | + flagName = a[2:] |
66 | + return |
67 | + } |
68 | + flagName = a[2:i] |
69 | + if flagName == "" { |
70 | + err = fmt.Errorf("empty flag in argument %q", a) |
71 | + return |
72 | + } |
73 | f.procArgs = f.procArgs[1:] |
74 | + f.procFlag = a[i:] |
75 | return |
76 | } |
77 | |
78 | @@ -745,7 +761,7 @@ |
79 | return "-" + name |
80 | } |
81 | |
82 | -func (f *FlagSet) parseGnuFlagArg(name string) (finished bool, err error) { |
83 | +func (f *FlagSet) parseGnuFlagArg(name string, long bool) (finished bool, err error) { |
84 | m := f.formal |
85 | flag, alreadythere := m[name] // BUG |
86 | if !alreadythere { |
87 | @@ -756,7 +772,9 @@ |
88 | // TODO print --xxx when flag is more than one rune. |
89 | return false, f.failf("flag provided but not defined: %s", flagWithMinus(name)) |
90 | } |
91 | - if fv, ok := flag.Value.(*boolValue); ok { // special case: doesn't need an arg |
92 | + if fv, ok := flag.Value.(*boolValue); ok && !strings.HasPrefix(f.procFlag, "=") { |
93 | + // special case: doesn't need an arg, and an arg hasn't |
94 | + // been provided explicitly. |
95 | fv.Set("true") |
96 | } else { |
97 | // It must have a value, which might be the next argument. |
98 | @@ -765,6 +783,12 @@ |
99 | if f.procFlag != "" { |
100 | // value directly follows flag |
101 | value = f.procFlag |
102 | + if long { |
103 | + if value[0] != '=' { |
104 | + panic("no leading '=' in long flag") |
105 | + } |
106 | + value = value[1:] |
107 | + } |
108 | hasValue = true |
109 | f.procFlag = "" |
110 | } |
111 | @@ -800,10 +824,10 @@ |
112 | f.args = nil |
113 | f.allowIntersperse = allowIntersperse |
114 | for { |
115 | - name, finished, err := f.parseOneGnu() |
116 | + name, long, finished, err := f.parseOneGnu() |
117 | if !finished { |
118 | if name != "" { |
119 | - finished, err = f.parseGnuFlagArg(name) |
120 | + finished, err = f.parseGnuFlagArg(name, long) |
121 | } |
122 | } |
123 | if err != nil { |
124 | |
125 | === modified file 'flag_test.go' |
126 | --- flag_test.go 2012-02-02 11:13:43 +0000 |
127 | +++ flag_test.go 2012-10-02 14:10:24 +0000 |
128 | @@ -108,168 +108,228 @@ |
129 | } |
130 | |
131 | var parseTests = []struct { |
132 | + about string |
133 | intersperse bool |
134 | args []string |
135 | vals map[string]interface{} |
136 | remaining []string |
137 | error string |
138 | -}{ |
139 | - { |
140 | - true, |
141 | - []string{ |
142 | - "--bool2", |
143 | - "--int", "22", |
144 | - "--int64", "0x23", |
145 | - "--uint", "24", |
146 | - "--uint64", "25", |
147 | - "--string", "hello", |
148 | - "--float64", "2718e28", |
149 | - "--duration", "2m", |
150 | - "one - extra - argument", |
151 | - }, |
152 | - map[string]interface{}{ |
153 | - "bool": false, |
154 | - "bool2": true, |
155 | - "int": 22, |
156 | - "int64": int64(0x23), |
157 | - "uint": uint(24), |
158 | - "uint64": uint64(25), |
159 | - "string": "hello", |
160 | - "float64": 2718e28, |
161 | - "duration": 2 * 60 * time.Second, |
162 | - }, |
163 | - []string{ |
164 | - "one - extra - argument", |
165 | - }, |
166 | - "", |
167 | - }, |
168 | - { |
169 | - true, |
170 | - []string{ |
171 | - "-a", |
172 | - "-", |
173 | - "-bc", |
174 | - "2", |
175 | - "-de1s", |
176 | - "-f2s", |
177 | - "-g", "3s", |
178 | - "--h", |
179 | - "--long", |
180 | - "--long2", "-4s", |
181 | - "3", |
182 | - "4", |
183 | - "--", "-5", |
184 | - }, |
185 | - map[string]interface{}{ |
186 | - "a": true, |
187 | - "b": true, |
188 | - "c": true, |
189 | - "d": true, |
190 | - "e": "1s", |
191 | - "f": "2s", |
192 | - "g": "3s", |
193 | - "h": true, |
194 | - "long": true, |
195 | - "long2": "-4s", |
196 | - "z": "default", |
197 | - "www": 99, |
198 | - }, |
199 | - []string{ |
200 | - "-", |
201 | - "2", |
202 | - "3", |
203 | - "4", |
204 | - "-5", |
205 | - }, |
206 | - "", |
207 | - }, { |
208 | - true, |
209 | - []string{ |
210 | - "-a", |
211 | - "--", |
212 | - "-b", |
213 | - }, |
214 | - map[string]interface{}{ |
215 | - "a": true, |
216 | - "b": false, |
217 | - }, |
218 | - []string{ |
219 | - "-b", |
220 | - }, |
221 | - "", |
222 | - }, { |
223 | - false, |
224 | - []string{ |
225 | - "-a", |
226 | - "foo", |
227 | - "-b", |
228 | - }, |
229 | - map[string]interface{}{ |
230 | - "a": true, |
231 | - "b": false, |
232 | - }, |
233 | - []string{ |
234 | - "foo", |
235 | - "-b", |
236 | - }, |
237 | - "", |
238 | - }, |
239 | - { |
240 | - false, |
241 | - []string{ |
242 | - "-a", |
243 | - "--", |
244 | - "foo", |
245 | - "-b", |
246 | - }, |
247 | - map[string]interface{}{ |
248 | - "a": true, |
249 | - "b": false, |
250 | - }, |
251 | - []string{ |
252 | - "foo", |
253 | - "-b", |
254 | - }, |
255 | - "", |
256 | - }, |
257 | - { |
258 | - true, |
259 | - []string{ |
260 | - "-a", |
261 | - "-b", |
262 | - }, |
263 | - map[string]interface{}{ |
264 | - "a": true, |
265 | - }, |
266 | - nil, |
267 | - "flag provided but not defined: -b", |
268 | - }, |
269 | - { |
270 | - true, |
271 | - []string{ |
272 | - "-a", |
273 | - }, |
274 | - map[string]interface{}{ |
275 | - "a": "default", |
276 | - }, |
277 | - nil, |
278 | - "flag needs an argument: -a", |
279 | - }, |
280 | - { |
281 | - true, |
282 | - []string{ |
283 | - "-a", "b", |
284 | - }, |
285 | - map[string]interface{}{ |
286 | - "a": 0, |
287 | - }, |
288 | - nil, |
289 | - `invalid value "b" for flag -a: strconv.ParseInt: parsing "b": invalid syntax`, |
290 | - }, |
291 | +}{{ |
292 | + about: "regular args", |
293 | + intersperse: true, |
294 | + args: []string{ |
295 | + "--bool2", |
296 | + "--int", "22", |
297 | + "--int64", "0x23", |
298 | + "--uint", "24", |
299 | + "--uint64", "25", |
300 | + "--string", "hello", |
301 | + "--float64", "2718e28", |
302 | + "--duration", "2m", |
303 | + "one - extra - argument", |
304 | + }, |
305 | + vals: map[string]interface{}{ |
306 | + "bool": false, |
307 | + "bool2": true, |
308 | + "int": 22, |
309 | + "int64": int64(0x23), |
310 | + "uint": uint(24), |
311 | + "uint64": uint64(25), |
312 | + "string": "hello", |
313 | + "float64": 2718e28, |
314 | + "duration": 2 * 60 * time.Second, |
315 | + }, |
316 | + remaining: []string{ |
317 | + "one - extra - argument", |
318 | + }, |
319 | +}, { |
320 | + about: "playing with -", |
321 | + intersperse: true, |
322 | + args: []string{ |
323 | + "-a", |
324 | + "-", |
325 | + "-bc", |
326 | + "2", |
327 | + "-de1s", |
328 | + "-f2s", |
329 | + "-g", "3s", |
330 | + "--h", |
331 | + "--long", |
332 | + "--long2", "-4s", |
333 | + "3", |
334 | + "4", |
335 | + "--", "-5", |
336 | + }, |
337 | + vals: map[string]interface{}{ |
338 | + "a": true, |
339 | + "b": true, |
340 | + "c": true, |
341 | + "d": true, |
342 | + "e": "1s", |
343 | + "f": "2s", |
344 | + "g": "3s", |
345 | + "h": true, |
346 | + "long": true, |
347 | + "long2": "-4s", |
348 | + "z": "default", |
349 | + "www": 99, |
350 | + }, |
351 | + remaining: []string{ |
352 | + "-", |
353 | + "2", |
354 | + "3", |
355 | + "4", |
356 | + "-5", |
357 | + }, |
358 | +}, { |
359 | + about: "flag after explicit --", |
360 | + intersperse: true, |
361 | + args: []string{ |
362 | + "-a", |
363 | + "--", |
364 | + "-b", |
365 | + }, |
366 | + vals: map[string]interface{}{ |
367 | + "a": true, |
368 | + "b": false, |
369 | + }, |
370 | + remaining: []string{ |
371 | + "-b", |
372 | + }, |
373 | +}, { |
374 | + about: "flag after end", |
375 | + args: []string{ |
376 | + "-a", |
377 | + "foo", |
378 | + "-b", |
379 | + }, |
380 | + vals: map[string]interface{}{ |
381 | + "a": true, |
382 | + "b": false, |
383 | + }, |
384 | + remaining: []string{ |
385 | + "foo", |
386 | + "-b", |
387 | + }, |
388 | +}, { |
389 | + about: "arg and flag after explicit end", |
390 | + args: []string{ |
391 | + "-a", |
392 | + "--", |
393 | + "foo", |
394 | + "-b", |
395 | + }, |
396 | + vals: map[string]interface{}{ |
397 | + "a": true, |
398 | + "b": false, |
399 | + }, |
400 | + remaining: []string{ |
401 | + "foo", |
402 | + "-b", |
403 | + }, |
404 | +}, { |
405 | + about: "boolean args, explicitly and non-explicitly given", |
406 | + args: []string{ |
407 | + "--a=false", |
408 | + "--b=true", |
409 | + "--c", |
410 | + }, |
411 | + vals: map[string]interface{}{ |
412 | + "a": false, |
413 | + "b": true, |
414 | + "c": true, |
415 | + }, |
416 | +}, { |
417 | + about: "using =", |
418 | + args: []string{ |
419 | + "--arble=bar", |
420 | + "--bletch=", |
421 | + "--a=something", |
422 | + "-b=other", |
423 | + "-cdand more", |
424 | + "--curdle=--milk", |
425 | + "--sandwich", "=", |
426 | + "--darn=", |
427 | + "=arg", |
428 | + }, |
429 | + vals: map[string]interface{}{ |
430 | + "arble": "bar", |
431 | + "bletch": "", |
432 | + "a": "something", |
433 | + "b": "=other", |
434 | + "c": true, |
435 | + "d": "and more", |
436 | + "curdle": "--milk", |
437 | + "sandwich": "=", |
438 | + "darn": "", |
439 | + }, |
440 | + remaining: []string{"=arg"}, |
441 | +}, { |
442 | + about: "empty flag #1", |
443 | + args: []string{ |
444 | + "--=bar", |
445 | + }, |
446 | + error: `empty flag in argument "--=bar"`, |
447 | +}, { |
448 | + about: "single-letter equals", |
449 | + args: []string{ |
450 | + "-=bar", |
451 | + }, |
452 | + error: `flag provided but not defined: -=`, |
453 | +}, { |
454 | + about: "empty flag #2", |
455 | + args: []string{ |
456 | + "--=", |
457 | + }, |
458 | + error: `empty flag in argument "--="`, |
459 | +}, { |
460 | + about: "no equals", |
461 | + args: []string{ |
462 | + "-=", |
463 | + }, |
464 | + error: `flag provided but not defined: -=`, |
465 | +}, { |
466 | + args: []string{ |
467 | + "-a=true", |
468 | + }, |
469 | + vals: map[string]interface{}{ |
470 | + "a": true, |
471 | + }, |
472 | + error: `invalid value "=true" for flag -a: strconv.ParseBool: parsing "=true": invalid syntax`, |
473 | +}, { |
474 | + intersperse: true, |
475 | + args: []string{ |
476 | + "-a", |
477 | + "-b", |
478 | + }, |
479 | + vals: map[string]interface{}{ |
480 | + "a": true, |
481 | + }, |
482 | + error: "flag provided but not defined: -b", |
483 | +}, { |
484 | + intersperse: true, |
485 | + args: []string{ |
486 | + "-a", |
487 | + }, |
488 | + vals: map[string]interface{}{ |
489 | + "a": "default", |
490 | + }, |
491 | + error: "flag needs an argument: -a", |
492 | +}, { |
493 | + intersperse: true, |
494 | + args: []string{ |
495 | + "-a", "b", |
496 | + }, |
497 | + vals: map[string]interface{}{ |
498 | + "a": 0, |
499 | + }, |
500 | + error: `invalid value "b" for flag -a: strconv.ParseInt: parsing "b": invalid syntax`, |
501 | +}, |
502 | } |
503 | |
504 | func testParse(newFlagSet func() *FlagSet, t *testing.T) { |
505 | for i, g := range parseTests { |
506 | + t.Logf("test %d. %s", i, g.about) |
507 | f := newFlagSet() |
508 | flags := make(map[string]interface{}) |
509 | for name, val := range g.vals { |
510 | @@ -291,28 +351,30 @@ |
511 | case time.Duration: |
512 | flags[name] = f.Duration(name, 5*time.Second, "duration value") |
513 | default: |
514 | - panic(fmt.Errorf("unhandled type %T", val)) |
515 | + t.Fatalf("unhandled type %T", val) |
516 | } |
517 | } |
518 | err := f.Parse(g.intersperse, g.args) |
519 | - if err != nil { |
520 | - if err.Error() != g.error { |
521 | - t.Errorf("test %d; expected error %q got %q", i, g.error, err.Error()) |
522 | + if g.error != "" { |
523 | + if err == nil { |
524 | + t.Errorf("expected error %q got nil", g.error) |
525 | + } else if err.Error() != g.error { |
526 | + t.Errorf("expected error %q got %q", g.error, err.Error()) |
527 | } |
528 | continue |
529 | } |
530 | for name, val := range g.vals { |
531 | actual := reflect.ValueOf(flags[name]).Elem().Interface() |
532 | if val != actual { |
533 | - t.Errorf("test %d: flag %q, expected %v got %v", i, name, val, actual) |
534 | + t.Errorf("flag %q, expected %v got %v", name, val, actual) |
535 | } |
536 | } |
537 | if len(f.Args()) != len(g.remaining) { |
538 | - t.Fatalf("test %d: remaining args, expected %q got %q", i, g.remaining, f.Args()) |
539 | + t.Fatalf("remaining args, expected %q got %q", g.remaining, f.Args()) |
540 | } |
541 | for j, a := range f.Args() { |
542 | if a != g.remaining[j] { |
543 | - t.Errorf("test %d: arg %d, expected %q got %q", i, j, g.remaining[i], a) |
544 | + t.Errorf("arg %d, expected %q got %q", j, g.remaining[i], a) |
545 | } |
546 | } |
547 | } |
Reviewers: mp+127469_ code.launchpad. net,
Message:
Please take a look.
Description:
gnuflag: implement = in flags.
https:/ /code.launchpad .net/~rogpeppe/ gnuflag/ support- equals/ +merge/ 127469
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/6598052/
Affected files:
A [revision details]
M flag.go
M flag_test.go