28 from .misc
import plural_or_not
29 from .encoding
import console_decode, system_decode
30 from .platform
import PY2
31 from .utf8reader
import Utf8Reader
32 from .robottypes
import is_falsy, is_integer, is_list_like, is_string, is_unicode
36 space =
' ', apos =
"'", quot =
'"', lt =
'<', gt =
'>',
37 pipe =
'|', star =
'*', comma =
',', slash =
'/', semic =
';',
38 colon =
':', quest =
'?', hash =
'#', amp =
'&', dollar =
'$',
39 percent =
'%', at =
'@', exclam =
'!', paren1 =
'(', paren2 =
')',
40 square1 =
'[', square2 =
']', curly1 =
'{', curly2 =
'}', bslash =
'\\'
46 args = args.encode(
'UTF-8')
47 decode =
lambda item: item.decode(
'UTF-8')
49 decode =
lambda item: item
50 lexer = shlex.shlex(args, posix=
True)
53 lexer.escapedquotes =
'"\''
55 lexer.whitespace_split =
True
57 return [decode(token)
for token
in lexer]
58 except ValueError
as err:
59 raise ValueError(
"Parsing '%s' failed: %s" % (args, err))
66 _opt_line_re = re.compile(
'''
67 ^\s{1,4} # 1-4 spaces in the beginning of the line
68 ((-\S\s)*) # all possible short options incl. spaces (group 1)
69 --(\S{2,}) # required long option (group 3)
70 (\s\S+)? # optional value (group 4)
71 (\s\*)? # optional '*' telling option allowed multiple times (group 5)
77 _quotes_re = re.compile(
'(.*)(\".*\")(.*)?')
84 def __init__(self, usage, name=None, version=None, arg_limits=None,
85 validator=None, env_options=None, auto_help=True,
86 auto_version=True, auto_escape=True, auto_pythonpath=True,
87 auto_argumentfile=True):
90 self.
namename = name
or usage.splitlines()[0].split(
' -- ')[0].strip()
164 raise DataError(
"Using '--argumentfile' option in shortened format "
165 "like '--argumentf' is not supported.")
169 opts, args = self.
_validator_validator(opts, args)
181 if self.
_auto_escape_auto_escape
and opts.get(
'escape'):
183 if self.
_auto_help_auto_help
and opts.get(
'help'):
188 sys.path = self.
_get_pythonpath_get_pythonpath(opts[
'pythonpath']) + sys.path
189 for auto, opt
in [(self.
_auto_help_auto_help,
'help'),
194 if auto
and opt
in opts:
202 return args.strip().strip().split()
212 for gr
in res.groups():
214 if gr
is not None and gr !=
'':
215 second_m = re.split(
'"', gr)
220 for idx
in range(0, m):
223 line.extend(second_m[idx].strip().strip().split())
225 line.append(f
"{second_m[idx]}")
227 for idx
in range(0, m):
229 line.extend(second_m[idx].strip().strip().split())
233 for idx, value
in enumerate(clean):
234 if value[-1] ==
':' and idx + 1 < len(clean):
235 clean[idx] =
''.join([value, clean[idx+1]])
244 except getopt.GetoptError
as err:
249 if not opt.startswith(
'--'):
253 opt, value = opt.split(
'=', 1)
254 return '%s=%s' % (opt.lower(), value)
258 with LOGGER.cache_only:
259 LOGGER.warn(
"Option '--escape' is deprecated. Use console escape "
260 "mechanism instead.")
262 escape_strings = opts[
'escape']
266 for name, value
in opts.items():
268 opts[name] = self.
_unescape_unescape(value, escapes)
269 return opts, [self.
_unescape_unescape(arg, escapes)
for arg
in args]
272 options = [
'--argumentfile']
273 for short_opt, long_opt
in self.
_short_to_long_short_to_long.items():
274 if long_opt ==
'argumentfile':
275 options.append(
'-'+short_opt)
280 for estr
in escape_strings:
282 name, value = estr.split(
':', 1)
284 raise DataError(
"Invalid escape string syntax '%s'. "
285 "Expected: what:with" % estr)
287 escapes[value] = ESCAPES[name.lower()]
289 raise DataError(
"Invalid escape '%s'. Available: %s"
294 if value
in [
None,
True,
False]:
297 return [self.
_unescape_unescape(item, escapes)
for item
in value]
298 for esc_name, esc_value
in escapes.items():
299 if esc_name
in value:
300 value = value.replace(esc_name, esc_value)
305 for name, value
in opt_tuple:
308 opts[name].append(value)
311 elif name.startswith(
'no')
and name[2:]
in self.
_flag_opts_flag_opts:
312 opts[name[2:]] =
False
320 opt = opt.rstrip(
'=')
321 if opt.startswith(
'no')
and opt[2:]
in self.
_flag_opts_flag_opts:
323 defaults[opt] = []
if opt
in self.
_multi_opts_multi_opts
else None
329 paths = sorted(glob.glob(path))
337 name = name.lstrip(
'-')
344 for line
in usage.splitlines():
347 self.
_create_option_create_option(short_opts=[o[1]
for o
in res.group(1).split()],
348 long_opt=res.group(3).
lower(),
349 takes_arg=bool(res.group(4)),
350 is_multi=bool(res.group(5)))
354 for sopt
in short_opts:
362 short_opts = [sopt+
':' for sopt
in short_opts]
364 if long_opt.startswith(
'no'):
365 long_opt = long_opt[2:]
366 self.
_long_opts_long_opts.append(
'no' + long_opt)
369 self.
_short_opts_short_opts += (
''.join(short_opts))
373 if opt.startswith(
'no'):
377 elif opt
in [o.rstrip(
'=')
for o
in self.
_long_opts_long_opts]:
385 temp.extend(glob.glob(path))
386 return [os.path.abspath(path)
for path
in temp
if path]
390 tokens =
':'.join(paths).split(
':')
397 item = item.replace(
'/',
'\\')
398 if drive
and item.startswith(
'\\'):
399 ret.append(
'%s:%s' % (drive, item))
405 if len(item) == 1
and item
in string.ascii_letters:
414 names = sorted(ESCAPES.keys(), key=str.lower)
415 return ', '.join(
'%s (%s)' % (n, ESCAPES[n])
for n
in names)
420 msg = msg.replace(
'<VERSION>', self.
versionversion)
421 def replace_escapes(res):
423 lines = textwrap.wrap(escapes, width=len(res.group(2)))
424 indent =
' ' * len(res.group(1))
425 return '\n'.join(indent + line
for line
in lines)
426 msg = re.sub(
'( *)(<-+ESCAPES-+>)', replace_escapes, msg)
442 if arg_limits
is None:
443 return 0, sys.maxsize
445 return arg_limits, arg_limits
446 if len(arg_limits) == 1:
447 return arg_limits[0], sys.maxsize
448 return arg_limits[0], arg_limits[1]
451 if not (self._min_args <= len(args) <= self.
_max_args_max_args):
456 if min_args == max_args:
457 expectation =
"%d argument%s" % (min_args, min_end)
458 elif max_args != sys.maxsize:
459 expectation =
"%d to %d arguments" % (min_args, max_args)
461 expectation =
"at least %d argument%s" % (min_args, min_end)
462 raise DataError(
"Expected %s, got %d." % (expectation, arg_count))
472 path, replace = self.
_get_index_get_index(args)
475 args[replace] = self.
_get_args_get_args(path)
480 start = opt +
'=' if opt.startswith(
'--')
else opt
481 for index, arg
in enumerate(args):
482 normalized_arg = arg.lower()
if opt.startswith(
'--')
else arg
484 if normalized_arg == opt
and index + 1 < len(args):
485 return args[index+1], slice(index, index+2)
487 if normalized_arg.startswith(start):
488 return arg[len(start):], slice(index, index+1)
492 if path.upper() !=
'STDIN':
502 except (IOError, UnicodeError)
as err:
503 raise DataError(
"Opening argument file '%s' failed: %s"
511 for line
in content.splitlines():
513 if line.startswith(
'-'):
515 elif line
and not line.startswith(
'#'):
523 option, value = line.split(separator, 1)
525 value = value.strip()
526 return [option, value]
529 if ' ' not in line
and '=' not in line:
535 return ' ' if line.index(
' ') < line.index(
'=')
else '='
Used when variable does not exist.
Can be used when the core framework goes to unexpected state.
def _read_from_file(self, path)
def __init__(self, options)
def _split_option(self, line)
def _get_index(self, args)
def _get_option_separator(self, line)
def _read_from_stdin(self)
def _get_args(self, path)
def _process_file(self, content)
def _raise_invalid_args(self, min_args, max_args, arg_count)
def __init__(self, arg_limits)
def _parse_arg_limits(self, arg_limits)
def _get_pythonpath(self, paths)
def _parse_args(self, args)
def _create_option(self, short_opts, long_opt, takes_arg, is_multi)
def _get_available_escapes(self)
def _get_env_options(self)
def _process_possible_argfile(self, args)
def _glob_args(self, args)
def _create_options(self, usage)
def _save_filenames(self, args)
def _raise_option_multiple_times_in_usage(self, opt)
def __init__(self, usage, name=None, version=None, arg_limits=None, validator=None, env_options=None, auto_help=True, auto_version=True, auto_escape=True, auto_pythonpath=True, auto_argumentfile=True)
Available options and tool name are read from the usage.
def parse_args(self, args)
Parse given arguments and return options and positional arguments.
def _get_default_opts(self)
def _verify_long_not_already_used(self, opt, flag=False)
def _unescape_opts_and_args(self, opts, args)
def _process_opts(self, opt_tuple)
def _get_escapes(self, escape_strings)
def _handle_special_options(self, opts, args)
def _unescape(self, value, escapes)
def _get_name(self, name)
def _lowercase_long_option(self, opt)
def _split_pythonpath(self, paths)
def cmdline2list(args, escaping=False)
def system_decode(string)
Decodes bytes from system (e.g.
def console_decode(string, encoding=CONSOLE_ENCODING, force=False)
Decodes bytes from console encoding to Unicode.
def is_falsy(item)
Opposite of :func:is_truthy.
def get_full_version(program=None, naked=False)