def _AddFlagsFileFlags(inject, flags_file, parent_locations=None): """Recursively append the flags file flags to inject.""" flag = calliope_base.FLAGS_FILE_FLAG.name if parent_locations and parent_locations.FileInStack(flags_file): raise parser_errors.ArgumentError( '{} recursive reference ({}).'.format(flag, parent_locations)) # Load the YAML flag:value dict or list of dicts. List of dicts allows # flags to be specified more than once. if flags_file == '-': contents = sys.stdin.read() elif not os.path.exists(flags_file): raise parser_errors.ArgumentError( '{} [{}] not found.'.format(flag, flags_file)) else: contents = files.ReadFileContents(flags_file) data = yaml.load(contents, location_value=True) group = data if isinstance(data, list) else [data] # Generate the list of args to inject. for member in group: if not isinstance(member.value, dict): raise parser_errors.ArgumentError( '{}:{}: {} file must contain a dictionary or list of dictionaries ' 'of flags.'.format(flags_file, member.lc.line + 1, flag)) for arg, obj in six.iteritems(member.value): line_col = obj.lc value = obj.value if arg == flag: # The flags-file YAML arg value can be a path or list of paths. file_list = obj.value if isinstance(obj.value, list) else [obj.value] for path in file_list: locations = _ArgLocations(arg, flags_file, line_col, parent_locations) _AddFlagsFileFlags(inject, path, locations) continue if isinstance(value, (type(None), bool)): separate_value_arg = False elif isinstance(value, (list, dict)): separate_value_arg = True else: separate_value_arg = False arg = '{}={}'.format(arg, value) inject.append(_FLAG_FILE_LINE_NAME) inject.append(_ArgLocations(arg, flags_file, line_col, parent_locations)) inject.append(arg) if separate_value_arg: # Add the already lexed arg and with one swoop we sidestep all flag # value and command line interpreter quoting issues. The ArgList and # ArgDict arg parsers have been adjusted to handle this. inject.append(value)
def __call__(self, parser, namespace, values, option_string=None): """Render a help document according to the style in values. Args: parser: The ArgParse object. namespace: The ArgParse namespace. values: The --document flag ArgDict() value: style=STYLE The output style. Must be specified. title=DOCUMENT TITLE The document title. notes=SENTENCES Inserts SENTENCES into the document NOTES section. option_string: The ArgParse flag string. Raises: parser_errors.ArgumentError: For unknown flag value attribute name. """ base.LogCommand(parser.prog, namespace) if default_style: # --help metrics.Loaded() style = default_style notes = None title = None for attributes in values: for name, value in six.iteritems(attributes): if name == 'notes': notes = value elif name == 'style': style = value elif name == 'title': title = value else: raise parser_errors.ArgumentError( 'Unknown document attribute [{0}]'.format(name)) if title is None: title = command.dotted_name metrics.Help(command.dotted_name, style) # '--help' is set by the --help flag, the others by gcloud <style> ... . if style in ('--help', 'help', 'topic'): style = 'text' md = io.StringIO(markdown.Markdown(command)) out = (io.StringIO() if console_io.IsInteractive(output=True) else None) if style == 'linter': meta_data = GetCommandMetaData(command) else: meta_data = None render_document.RenderDocument(style, md, out=out or log.out, notes=notes, title=title, command_metadata=meta_data) metrics.Ran() if out: console_io.More(out.getvalue()) sys.exit(0)
def Run(self, args): catalog_ref = args.CONCEPTS.catalog.Parse() api_client = product_util.ProductsClient() # TODO(b/69863480) Remove this once optional modal groups are fixed if (args.bounds or args.category) and not (args.bounds and args.category): missing = 'bounds' if not args.bounds else 'category' raise parser_errors.ArgumentError( 'Missing [{}]. Both category and bounds must be specified if ' 'either is provided'.format(missing)) ref_image = api_client.BuildRefImage( args.product_id, args.image_path, bounds=api_client.BuildBoundingPoly(args.bounds), product_category=args.category) created_image = api_client.CreateRefImage(ref_image, catalog_ref.RelativeName()) log.CreatedResource(created_image.name, kind='ReferenceImage') return created_image
def Run(self, args): config = _LoadConfig(args.config) root_dir = config['root_dir'] changed_config = False if args.api_discovery_doc: if not os.path.isfile(args.api_discovery_doc): raise regen_utils.DiscoveryDocError('File not found {}'.format( args.api_discovery_doc)) if len(args.api) != 1: raise parser_errors.ArgumentError( 'Can only specify one api when discovery doc is provided.') match = re.match(_API_REGEX, args.api[0]) if not match: raise regen_utils.DiscoveryDocError( 'Api name must match {} pattern when discovery doc ' 'is specified'.format(_API_REGEX)) api_name, api_version = match.group(1), match.group(2) if api_name not in config['apis']: log.warning('No such api %s in config, adding...', api_name) config['apis'][api_name] = {api_version: {'discovery_doc': ''}} changed_config = True elif api_version not in config['apis'][api_name]: log.warning('No such api version %s in config, adding...', api_version) config['apis'][api_name][api_version] = {'discovery_doc': ''} changed_config = True api_version_config = config['apis'].get(api_name).get( api_version, {}) discovery_doc = api_name + '_' + api_version + '.json' new_discovery_doc = os.path.realpath(args.api_discovery_doc) old_discovery_doc = os.path.realpath( os.path.join(args.base_dir, root_dir, discovery_doc)) if new_discovery_doc != old_discovery_doc: log.status.Print('Copying in {}'.format(new_discovery_doc)) shutil.copyfile(new_discovery_doc, old_discovery_doc) if api_version_config['discovery_doc'] != discovery_doc: changed_config = True api_version_config['discovery_doc'] = discovery_doc regenerate_list = [(match.group(1), match.group(2), api_version_config)] else: regex_patern = '|'.join(map(fnmatch.translate, args.api)) regenerate_list = [ (api_name, api_version, api_config) for api_name, api_version_config in six.iteritems( config['apis']) for api_version, api_config in six.iteritems( api_version_config) if re.match(regex_patern, api_name + '/' + api_version) ] if not regenerate_list: raise regen_utils.UnknownApi( 'api [{api_name}] not found in "apis" section of ' '{config_file}. Use [gcloud meta apis list] to see available apis.' .format(api_name=','.join(args.api), config_file=args.config)) base_dir = args.base_dir or os.path.dirname( os.path.dirname(googlecloudsdk.__file__)) for api_name, api_version, api_config in sorted(regenerate_list): log.status.Print('Generating {} {} from {}'.format( api_name, api_version, os.path.join(root_dir, api_config['discovery_doc']))) generate.GenerateApi(base_dir, root_dir, api_name, api_version, api_config) generate.GenerateResourceModule(base_dir, root_dir, api_name, api_version, api_config['discovery_doc'], api_config.get('resources', {})) generate.GenerateApiMap(base_dir, root_dir, config['apis']) # Now that everything passed, config can be updated if needed. if changed_config: log.warning('Updated %s', args.config) with files.FileWriter(args.config) as stream: ruamel.yaml.round_trip_dump(config, stream)
def DeprecationFunc(value): if show_message(value): if removed: raise parser_errors.ArgumentError(add_help.message) else: log.warn(add_help.message)
def testArgumentErrorUnbalanced(self): e = parser_errors.ArgumentError('abc [{info] xyz', info='TEST INFO') self.assertEqual('abc [{info] xyz', str(e))
def testArgumentErrorBadSpec(self): e = parser_errors.ArgumentError('abc [{:3}] xyz') self.assertEqual('abc [{:3}] xyz', str(e))
def testArgumentErrorUnknownKey(self): e = parser_errors.ArgumentError('abc [{info}] xyz', data='TEST INFO') self.assertEqual('abc [{info}] xyz', str(e))
def testArgumentErrorOK(self): e = parser_errors.ArgumentError('abc [{info}] xyz', info='TEST INFO') self.assertEqual('abc [TEST INFO] xyz', str(e))
def CheckRangeLegacyModeOrRaise(args): """Checks for range being used with incompatible mode and raises an error.""" if args.range is not None: if ((args.subnet_mode and args.subnet_mode != 'legacy') or (args.mode and args.mode != 'legacy')): raise parser_errors.ArgumentError(_RANGE_NON_LEGACY_MODE_ERROR)
def error(self, message='', context=None, reproduce=False): """Overrides argparse.ArgumentParser's .error(message) method. Specifically, it avoids reprinting the program name and the string "error:". Args: message: str, The error message to print. context: _ErrorContext, An error context with affected parser. reproduce: bool, Reproduce a previous call to this method from context. """ if reproduce and context: # Reproduce a previous call to this method from the info in context. message = context.message parser = context.parser error = context.error if not error: error = parser_errors.ArgumentError(message, parser=self) else: if context: message = context.message parser = context.parser error = context.error else: if 'Invalid choice:' in message: exc = parser_errors.UnrecognizedArgumentsError else: exc = parser_errors.ArgumentError if message: message = re.sub(r"\bu'", "'", message) error = exc(message, parser=self) parser = self if ('_ARGCOMPLETE' not in os.environ and not isinstance(error, parser_errors.DetailedArgumentError) and ( self._probe_error or 'Invalid choice' in message or 'unknown parser' in message ) ): if 'unknown parser' in message: return if self._probe_error and 'expected one argument' in message: return # Save this context for later. We may be able to deduce a better error # message. For instance, argparse might complain about an invalid # command choice 'flag-value' for '--unknown-flag flag-value', but # with a little finagling in parse_known_args() we can verify that # '--unknown-flag' is in fact an unknown flag and error out on that. self._SetErrorContext(context or _ErrorContext(message, parser, error)) return # Ignore errors better handled by validate_specified_args(). if '_ARGCOMPLETE' not in os.environ: if re.search('too few arguments', message): return if (re.search('arguments? .* required', message) and not re.search('in dict arg but not provided', message)): return parser.ReportErrorMetrics(error, message) # No need to output help/usage text if we are in completion mode. However, # we do need to populate group/command level choices. These choices are not # loaded when there is a parser error since we do lazy loading. if '_ARGCOMPLETE' in os.environ: # pylint:disable=protected-access if self._calliope_command._sub_parser: self._calliope_command.LoadAllSubElements() else: message = console_attr.SafeText(message) log.error('({prog}) {message}'.format(prog=self.prog, message=message)) # multi-line message means hints already added, no need for usage. # pylint:disable=protected-access if '\n' not in message: argparse._sys.stderr.write(self._calliope_command.GetUsage()) self.exit(2, exception=error)
def CheckRangeLegacyModeOrRaise(args): """Checks for range being used with incompatible mode and raises an error.""" if args.IsSpecified('range') and args.IsSpecified( 'subnet_mode') and args.subnet_mode != 'legacy': raise parser_errors.ArgumentError(_RANGE_NON_LEGACY_MODE_ERROR)
def CheckIncompatibleFlagsOrRaise(args): """Checks for incompatible flags in arguments and raises an error if found.""" if (HasReplaceAdvertisementFlags(args) and HasIncrementalAdvertisementFlags(args)): raise parser_errors.ArgumentError( _INCOMPATIBLE_INCREMENTAL_FLAGS_ERROR_MESSAGE)