def get_command(self, ctx, name): """Retrieve the appropriate method from the Resource, decorate it as a click command, and return that method. """ # Sanity check: Does a method exist corresponding to this # command? If not, this is an error. if not hasattr(self.resource, name): raise exc.UsageError( 'The %s resource has no such command: "%s"' % (self.resource_name, name), ) # Get the method. method = getattr(self.resource, name) # Get any attributes that were given at command-declaration # time. attrs = getattr(method, '_cli_command_attrs', {}) # If the help message comes from the docstring, then # convert it into a message specifically for this resource. help_text = inspect.getdoc(method) attrs['help'] = self._auto_help_text(help_text or '') # On some methods, we ignore the defaults, which are intended # for writing and not reading; process this. ignore_defaults = attrs.pop('ignore_defaults', False) # Wrap the method, such that it outputs its final return # value rather than returning it. new_method = self._echo_method(method) # Soft copy the "__click_params__", if any exist. # This is the internal holding method that the click library # uses to store @click.option and @click.argument directives # before the method is converted into a command. # # Because self._echo_method uses @functools.wraps, this is # actually preserved; the purpose of copying it over is # so we can get our resource fields at the top of the help; # the easiest way to do this is to load them in before the # conversion takes place. (This is a happy result of Armin's # work to get around Python's processing decorators # bottom-to-top.) click_params = getattr(method, '__click_params__', []) new_method.__click_params__ = copy(click_params) # Write options based on the fields available on this resource. if attrs.pop('use_fields_as_options', True): for field in reversed(self.resource.fields): if not field.is_option: continue # Create the initial arguments based on the # option value. If we have a different key to use # (which is what gets routed to the Tower API), # ensure that is the first argument. args = [field.option] if field.key: args.insert(0, field.key) # Apply the option to the method. click.option( *args, default=field.default if not ignore_defaults else None, help=field.help, type=field.type, show_default=field.show_default)(new_method) # Make a click Command instance using this method # as the callback, and return it. cmd = command(name=name, cls=Command, **attrs)(new_method) # If this method has a `pk` positional argument, # then add a click argument for it. code = six.get_function_code(method) if 'pk' in code.co_varnames: click.argument('pk', nargs=1, required=False, type=int, metavar='[ID]')(cmd) # Done; return the command. return cmd
def get_command(self, ctx, name): """Retrieve the appropriate method from the Resource, decorate it as a click command, and return that method. """ # Sanity check: Does a method exist corresponding to this # command? If not, this is an error. if not hasattr(self.resource, name): raise exc.UsageError( 'The %s resource has no such command: "%s"' % (self.resource_name, name), ) # Get the method. method = getattr(self.resource, name) # Get any attributes that were given at command-declaration # time. attrs = getattr(method, '_cli_command_attrs', {}) # If the help message comes from the docstring, then # convert it into a message specifically for this resource. help_text = inspect.getdoc(method) attrs['help'] = self._auto_help_text(help_text or '') # On some methods, we ignore the defaults, which are intended # for writing and not reading; process this. ignore_defaults = attrs.pop('ignore_defaults', False) # Wrap the method, such that it outputs its final return # value rather than returning it. new_method = self._echo_method(method) # Soft copy the "__click_params__", if any exist. # This is the internal holding method that the click library # uses to store @click.option and @click.argument directives # before the method is converted into a command. # # Because self._echo_method uses @functools.wraps, this is # actually preserved; the purpose of copying it over is # so we can get our resource fields at the top of the help; # the easiest way to do this is to load them in before the # conversion takes place. (This is a happy result of Armin's # work to get around Python's processing decorators # bottom-to-top.) click_params = getattr(method, '__click_params__', []) new_method.__click_params__ = copy(click_params) # Write options based on the fields available on this resource. fao = attrs.pop('use_fields_as_options', True) if fao: for field in reversed(self.resource.fields): if not field.is_option: continue # If we got an iterable rather than a boolean, # then it is a list of fields to use; check for # presence in that list. if not isinstance(fao, bool) and field.name not in fao: continue # Create the initial arguments based on the # option value. If we have a different key to use # (which is what gets routed to the Tower API), # ensure that is the first argument. args = [field.option] if field.key: args.insert(0, field.key) # Apply the option to the method. click.option( *args, default=field.default if not ignore_defaults else None, help=field.help, type=field.type, show_default=field.show_default )(new_method) # Make a click Command instance using this method # as the callback, and return it. cmd = command(name=name, cls=Command, **attrs)(new_method) # If this method has a `pk` positional argument, # then add a click argument for it. code = six.get_function_code(method) if 'pk' in code.co_varnames: click.argument('pk', nargs=1, required=False, type=int, metavar='[ID]')(cmd) # Done; return the command. return cmd