def Print(self, resources, single=False, intermediate=False): """Prints resources using printer.AddRecord() and printer.Finish(). Args: resources: A singleton or list of JSON-serializable Python objects. single: If True then resources is a single item and not a list. For example, use this to print a single object as JSON. intermediate: This is an intermediate call, do not call Finish(). Raises: ProjectionRequiredError: If the projection is empty and the format requires a non-empty projection. """ if 'disable' in self.attributes: # Disable formatted output and do not consume the resources. self._empty = False return if self._non_empty_projection_required and ( not self.column_attributes or not self.column_attributes.Columns()): raise ProjectionRequiredError( 'Format [{0}] requires a non-empty projection.'.format(self._name)) # Resources may be a generator and since generators can raise exceptions, we # have to call Finish() in the finally block to make sure that the resources # we've been able to pull out of the generator are printed before control is # given to the exception-handling code. try: if resources: if single or not resource_property.IsListLike(resources): self.AddRecord(resources, delimit=intermediate) else: for resource in resources: self.AddRecord(resource) finally: if not intermediate: self.Finish()
def TransformFilter(r, expression): """Selects elements of r that match the filter expression. Args: r: A JSON-serializable object. expression: The filter expression to apply to r. Returns: The elements of r that match the filter expression. Example: x.filter("key:val") selects elements of r that have 'key' fields containing 'val'. """ # import loop from googlecloudsdk.core.resource import resource_filter # pylint: disable=g-import-not-at-top if not r: return r select = resource_filter.Compile(expression).Evaluate if not resource_property.IsListLike(r): return r if select(r) else '' transformed = [] for item in r: if select(item): transformed.append(item) return transformed
def Run(self, args): if args.json_file: with open(args.json_file, 'r') as f: resources = json.load(f) else: resources = json.load(sys.stdin) # TODO(gsfowler): Drop this if when the --aggregate global flag lands. if args.aggregate: key = resource_lex.Lexer(args.aggregate).Key() resources = Aggregator(resources, key) # TODO(gsfowler): Return resources here when the --filter global flag lands. if not args.format: args.format = 'json' if not args.filter: return resources select = resource_filter.Compile(args.filter).Evaluate filtered_resources = [] if resource_property.IsListLike(resources): for resource in resources: if select(resource): filtered_resources.append(resource) elif select(resources): # treat non-iterable resources as a list of length 1 filtered_resources.append(resources) return filtered_resources
def _AddSortByTap(self): """Sorts the resources using the --sort-by keys.""" if not resource_property.IsListLike(self._resources): return sort_keys = self._GetSortKeys() if not sort_keys: return self._args.sort_by = None # This loop partitions the keys into groups having the same reverse value. # The groups are then applied in reverse order to maintain the original # precedence. # # This is not a pure tap since, by necessity, it consumes self._resources # to sort and converts it to a list. groups = [ ] # [(group_keys, group_reverse)] LIFO to preserve precedence group_keys = [] # keys for current group group_reverse = False for key, reverse in sort_keys: if not group_keys: group_reverse = reverse elif group_reverse != reverse: groups.insert(0, (group_keys, group_reverse)) group_keys = [] group_reverse = reverse group_keys.append(key) if group_keys: groups.insert(0, (group_keys, group_reverse)) # Finally sort the resources by groups having the same reverse value. for keys, reverse in groups: self._SortResources(keys, reverse)
def Evaluate(self, obj): """Apply the list of transforms to obj and return the transformed value.""" for transform in self._transforms: if transform.map_transform and resource_property.IsListLike(obj): # A transform mapped on a list - transform each list item. # map_transform > 1 for nested lists. For example: # abc[].def[].ghi[].map(3) # iterates over the items in ghi[] for all abc[] and def[]. items = obj for _ in range(transform.map_transform - 1): nested = [] try: # Stop if items is not a list. for item in items: nested.extend(item) except TypeError: break items = nested obj = [] for item in items: obj.append( transform.func(item, *transform.args, **transform.kwargs)) elif obj or not transform.map_transform: obj = transform.func(obj, *transform.args, **transform.kwargs) return obj
def _ProjectTransform(self, obj, transforms): """Applies transforms to obj. Args: obj: The object to transform. transforms: The list of resource_projection_parser._Transform objects. Returns: The transformed object. """ if not self._transforms_enabled: if self._transforms_enabled is not None: return obj if transforms[0].active not in (None, self._projection.active): return obj for transform in transforms: if transform.map_transform and resource_property.IsListLike(obj): # A transform mapped on a list - transform each list item. items = obj obj = [] for item in items: obj.append( transform.func(item, *transform.args, **transform.kwargs)) elif obj or not transform.map_transform: obj = transform.func(obj, *transform.args, **transform.kwargs) return obj
def Print(resources, print_format, out=None, defaults=None, single=False): """Prints the given resources. Args: resources: A singleton or list of JSON-serializable Python objects. print_format: The _FORMATTER name with optional projection expression. out: Output stream, log.out if None. defaults: Optional resource_projection_spec.ProjectionSpec defaults. single: If True then resources is a single item and not a list. For example, use this to print a single object as JSON. Raises: ProjectionRequiredError: If a format requires a projection and one is not provided. """ printer = Printer(print_format, out=out, defaults=defaults) if printer.ByColumns() and not printer.column_attributes.Columns(): raise ProjectionRequiredError( 'Format [{0}] requires a non-empty projection.'.format( printer.column_attributes.Name())) # Resources may be a generator and since generators can raise exceptions, we # have to call Finish() in the finally block to make sure that the resources # we've been able to pull out of the generator are printed before control is # given to the exception-handling code. try: if resources: if single or not resource_property.IsListLike(resources): printer.AddRecord(resources, delimit=False) else: for resource in resources: printer.AddRecord(resource) finally: printer.Finish()
def Display(self): """The default display method.""" if not log.IsUserOutputEnabled(): log.info('Display disabled.') # NOTICE: Do not consume resources here. Some commands use this case to # access the results of Run() via the return value of Execute(). However, # to satisfy callers who are only interetsted in silent side effects, # generators/iterators must be converted to a list here. if resource_property.IsListLike(self._resources): return list(self._resources) return self._resources # Initialize the printer. self._InitPrinter() # Add a URI cache update tap if needed. self._AddUriCacheTap() # Add a resource page tap if needed. self._AddPageTap() # Add a resource flatten tap if needed. self._AddFlattenTap() # Add a sort tap if needed. self._AddSortByTap() # Add a resource filter tap if needed. self._AddFilterTap() # Add a resource limit tap if needed. self._AddLimitTap() # Add the URI replace tap if needed. self._AddUriReplaceTap() resources_were_displayed = True if self._printer: # Most command output will end up here. log.info('Display format: "%s"', self._format) self._printer.Print(self._resources) resources_were_displayed = self._printer.ResourcesWerePrinted() elif hasattr(self._command, 'Display'): # This will eventually be rare. log.info('Explicit Display.') self._command.Display(self._args, self._resources) # Resource display is done. log.out.flush() # If the default format was used then display the epilog. if not self._args.IsSpecified('format'): self._command.Epilog(resources_were_displayed) return self._resources
def Evaluate(self, obj): """Apply the list of transforms to obj and return the transformed value.""" for transform in self._transforms: if transform.map_transform and resource_property.IsListLike(obj): # A transform mapped on a list - transform each list item. items = obj obj = [] for item in items: obj.append(transform.func(item, *transform.args, **transform.kwargs)) elif obj or not transform.map_transform: if transform.restriction: obj = transform.func(*transform.args, **transform.kwargs) else: obj = transform.func(obj, *transform.args, **transform.kwargs) return obj
def _PrintResources(resources, printer, single=False): """Prints resources using printer.AddRecord() and printer.Finish(). Args: resources: A singleton or list of JSON-serializable Python objects. printer: An instantiated printer. single: If True then resources is a single item and not a list. For example, use this to print a single object as JSON. """ # Resources may be a generator and since generators can raise exceptions, we # have to call Finish() in the finally block to make sure that the resources # we've been able to pull out of the generator are printed before control is # given to the exception-handling code. try: if resources: if single or not resource_property.IsListLike(resources): printer.AddRecord(resources, delimit=False) else: for resource in resources: printer.AddRecord(resource) finally: printer.Finish()