1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
#
# Copyright (c) 2010 Testrepository Contributors
#
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
# license at the users choice. A copy of both licenses are available in the
# project source as Apache-2.0 and BSD. You may not use this file except in
# compliance with one of these two licences.
#
# Unless required by applicable law or agreed to in writing, software
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# license you chose for the specific language governing permissions and
# limitations under that license.
"""'Arguments' for testr.
This is a small typed arguments concept - which is perhaps obsoleted by
argparse in Python 2.7, but for testrepository is an extension used with
optparse.
The code in this module contains the AbstractArgument base class. Individual
argument types are present in e.g. testrepository.arguments.string.
See testrepository.commands.Command for usage of Arguments.
Plugins and extensions wanting to add argument types should either define them
internally or install into testrepository.arguments as somename (perhaps by
extending the testrepository.arguments __path__ to include a directory
containing their argument types - no __init__ is needed in that directory.)
"""
import sys
from testtools.compat import reraise
class AbstractArgument(object):
"""A argument that a command may need.
Arguments can be converted into a summary for describing the UI to users,
and provide validator/parsers for the arguments.
:ivar: The name of the argument. This is used for retrieving the argument
from UI objects, and for generating the summary.
"""
def __init__(self, name, min=1, max=1):
"""Create an AbstractArgument.
While conceptually a separate SequenceArgument could be used, all
arguments support sequencing to avoid unnecessary boilerplate in user
code.
:param name: The name for the argument.
:param min: The minimum number of occurences permitted.
:param max: The maximum number of occurences permitted. None for
unlimited.
"""
self.name = name
self.minimum_count = min
self.maximum_count = max
def summary(self):
"""Get a regex-like summary of this argument."""
result = self.name
if (self.minimum_count == self.maximum_count and
self.minimum_count == 1):
return result
minmax = (self.minimum_count, self.maximum_count)
if minmax == (0, 1):
return result + '?'
if minmax == (1, None):
return result + '+'
if minmax == (0, None):
return result + '*'
if minmax[1] == None:
minmax = (minmax[0], '')
return result + '{%s,%s}' % minmax
def parse(self, argv):
"""Evaluate arguments in argv.
Used arguments are removed from argv.
:param argv: The arguments to parse.
:return: The parsed results as a list.
"""
count = 0
result = []
error = None
while len(argv) > count and (
self.maximum_count is None or count < self.maximum_count):
arg = argv[count]
count += 1
try:
result.append(self._parse_one(arg))
except ValueError:
# argument rejected this element
error = sys.exc_info()
count -= 1
break
if count < self.minimum_count:
if error is not None:
reraise(error[0], error[1], error[2])
raise ValueError('not enough arguments present/matched in %s' % argv)
del argv[:count]
return result
def _parse_one(self, arg):
"""Parse a single argument.
:param arg: An arg from an argv.
:result: The parsed argument.
:raises ValueError: If the arg cannot be parsed/validated.
"""
raise NotImplementedError(self._parse_one)
|