def default_key(func, *nargs, **kwargs): parts = [func.__module__] argnames = py23.get_function_arg_names(func) if argnames: if argnames[0] == "cls": cls_ = nargs[0] parts.append(cls_.__name__) nargs = nargs[1:] elif argnames[0] == "self": cls_ = nargs[0].__class__ parts.append(cls_.__name__) nargs = nargs[1:] parts.append(func.__name__) value = ('.'.join(parts), nargs, tuple(sorted(kwargs.items()))) # make sure key is hashable. We don't strictly need it to be, but this # is a way of hopefully avoiding object types that are not ordered (these # would give an unreliable key). If you need to key on unhashable args, # you should provide your own `key` functor. # _ = hash(value) # noqa return repr(value)
def command(opts, parser, extra_arg_groups=None): from rez.config import config from rez.utils.platform_ import platform_ from rez.exceptions import RezSystemError from rez.vendor import yaml from rez.vendor.yaml.error import YAMLError from rez.utils import py23 import os.path # we don't usually want warnings printed in a wrapped tool. But in cases # where we do (for debugging) we leave a backdoor - setting $REZ_QUIET=0 # will stop this warning suppression. if "REZ_QUIET" not in os.environ: config.override("quiet", True) yaml_file = os.path.abspath(opts.YAML) cli_args = opts.ARG for arg_group in (extra_arg_groups or []): cli_args.extend(arg_group) if platform_.name == "windows" and yaml_file.lower().endswith(".cmd"): with open(yaml_file) as f: content = "\n".join(f.readlines()[4:]) # strip batch script else: with open(yaml_file) as f: content = f.read() try: doc = yaml.load(content, Loader=yaml.FullLoader) except YAMLError as e: raise RezSystemError("Invalid executable file %s: %s" % (yaml_file, str(e))) func_name = doc["func_name"] nargs = doc.get("nargs", []) kwargs = doc.get("kwargs", {}) if isinstance(doc["module"], basestring): # refers to a rez module from rez.backport.importlib import import_module namespace = "rez.%s" % doc["module"] module = import_module(namespace) else: # refers to a rez plugin module from rez.plugin_managers import plugin_manager plugin_type, plugin_name = doc["module"] module = plugin_manager.get_plugin_module(plugin_type, plugin_name) target_func = getattr(module, func_name) func_args = py23.get_function_arg_names(target_func) if "_script" in func_args: kwargs["_script"] = yaml_file if "_cli_args" in func_args: kwargs["_cli_args"] = cli_args target_func(*nargs, **kwargs)
def _process(value): if isinstance(value, dict): for k, v in value.items(): value[k] = _process(v) return value elif isfunction(value): func = value if hasattr(func, "_early"): # run the function now, and replace with return value # # make a copy of the func with its own globals, and add 'this' import types fn = types.FunctionType(func.__code__, func.__globals__.copy(), name=func.__name__, argdefs=func.__defaults__, closure=func.__closure__) # apply globals fn.__globals__["this"] = EarlyThis(data) fn.__globals__.update(get_objects()) # execute the function args = py23.get_function_arg_names(func) if len(args) not in (0, 1): raise ResourceError("@early decorated function must " "take zero or one args only") if args: # this 'data' arg support isn't needed anymore, but I'm # supporting it til I know nobody is using it... # value_ = fn(data) else: value_ = fn() # process again in case this is a function returning a function return _process(value_) elif hasattr(func, "_late"): return SourceCode(func=func, filepath=filepath, eval_as_function=True) elif func.__name__ in package_rex_keys: # if a rex function, the code has to be eval'd NOT as a function, # otherwise the globals dict doesn't get updated with any vars # defined in the code, and that means rex code like this: # # rr = 'test' # env.RR = '{rr}' # # ..won't work. It was never intentional that the above work, but # it does, so now we have to keep it so. # return SourceCode(func=func, filepath=filepath, eval_as_function=False) else: # a normal function. Leave unchanged, it will be stripped after return func else: return value