def create_executable_script(filepath, body, program=None): """Create an executable script. Args: filepath (str): File to create. body (str or callable): Contents of the script. If a callable, its code is used as the script body. program (str): Name of program to launch the script, 'python' if None """ program = program or "python" if callable(body): from rez.utils.sourcecode import SourceCode code = SourceCode(func=body) body = code.source if not body.endswith('\n'): body += '\n' with open(filepath, 'w') as f: # TODO: make cross platform f.write("#!/usr/bin/env %s\n" % program) f.write(body) # TODO: Although Windows supports os.chmod you can only set the readonly # flag. Setting the file readonly breaks the unit tests that expect to # clean up the files once the test has run. Temporarily we don't bother # setting the permissions, but this will need to change. if os.name == "posix": os.chmod( filepath, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
def _convert_to_rex(self, commands): if isinstance(commands, list): from rez.utils.backcompat import convert_old_commands msg = "package %r is using old-style commands." % self.uri if config.disable_rez_1_compatibility or config.error_old_commands: raise SchemaError(None, msg) elif config.warn("old_commands"): print_warning(msg) commands = convert_old_commands(commands) if isinstance(commands, basestring): return SourceCode(source=commands) elif callable(commands): return SourceCode(func=commands) else: return commands
def _process(value): if isinstance(value, dict): for k, v in value.items(): new_value = _process(v) if new_value is _remove: del value[k] else: value[k] = new_value return value elif isfunction(value): if hasattr(value, "_early"): # run the function now, and replace with return value with add_sys_paths( config.package_definition_build_python_paths): func = value spec = getargspec(func) args = spec.args or [] if len(args) not in (0, 1): raise ResourceError("@early decorated function must " "take zero or one args only") if args: value_ = func(data) else: value_ = func() # process again in case this is a function returning a function return _process(value_) else: # 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. # as_function = (value.__name__ not in package_rex_keys) return SourceCode(func=value, filepath=filepath, eval_as_function=as_function) elif ismodule(value): # modules cannot be installed as package attributes. They are present # in developer packages sometimes though - it's fine for a package # attribute to use an imported module at build time. # return _remove else: return value
def test_3(self): """check package contents.""" # a py-based package package = get_package("versioned", "3.0") expected_data = dict( name="versioned", version=Version("3.0"), base=os.path.join(self.py_packages_path, "versioned", "3.0"), commands=SourceCode('env.PATH.append("{root}/bin")')) data = package.validated_data() # Account for case-insensitive paths for d in (expected_data, data): d["base"] = d["base"].lower() self.assertDictEqual(data, expected_data) # a yaml-based package package = get_package("versioned", "2.0") expected_uri = os.path.join(self.yaml_packages_path, "versioned", "2.0", "package.yaml") self.assertEqual(package.uri, expected_uri) # a py-based package with late binding attribute functions package = get_package("late_binding", "1.0") self.assertEqual(package.tools, ["util"]) # a 'combined' type package package = get_package("multi", "1.0") expected_uri = os.path.join(self.yaml_packages_path, "multi.yaml<1.0>") self.assertEqual(package.uri, expected_uri) expected_data = dict( name="multi", version=Version("1.0"), tools=["tweak"]) data = package.validated_data() self.assertDictEqual(data, expected_data) # a 'combined' type package, with version overrides package = get_package("multi", "1.1") expected_uri = os.path.join(self.yaml_packages_path, "multi.yaml<1.1>") self.assertEqual(package.uri, expected_uri) expected_data = dict( name="multi", version=Version("1.1"), tools=["twerk"]) data = package.validated_data() self.assertDictEqual(data, expected_data) # check that visibility of 'combined' packages is correct package = get_package("multi", "2.0") expected_uri = os.path.join(self.py_packages_path, "multi.py<2.0>") self.assertEqual(package.uri, expected_uri)
def test_6(self): """test variant iteration.""" expected_data = dict( name="variants_py", version=Version("2.0"), description="package with variants", base=os.path.join(self.py_packages_path, "variants_py", "2.0"), requires=[PackageRequest("python-2.7")], commands=SourceCode('env.PATH.append("{root}/bin")')) requires_ = ["platform-linux", "platform-osx"] package = get_package("variants_py", "2.0") for i, variant in enumerate(package.iter_variants()): data = variant.validated_data() self.assertDictEqual(data, expected_data) self.assertEqual(variant.index, i) self.assertEqual(variant.parent, package)
def _decode(obj): if not isinstance(obj, dict): return obj if "::rez:sourcecode::" in obj: value = obj["::rez:sourcecode::"] source_code = SourceCode() # Reference: SourceCode.__setstate__ source_code.source = value["source"] source_code.filepath = value["filepath"] source_code.funcname = value["funcname"] source_code.eval_as_function = value["eval_as_function"] source_code.decorators = value["decorators"] return source_code if "::rez:version::" in obj: value = obj["::rez:version::"] return Version(value["version"]) return obj
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
def create_executable_script(filepath, body, program=None, py_script_mode=None): """ Create an executable script. In case a py_script_mode has been set to create a .py script the shell is expected to have the PATHEXT environment variable to include ".PY" in order to properly launch the command without the .py extension. Args: filepath (str): File to create. body (str or callable): Contents of the script. If a callable, its code is used as the script body. program (str): Name of program to launch the script. Default is 'python' py_script_mode(ExecutableScriptMode): What kind of script to create. Defaults to rezconfig.create_executable_script_mode. Returns: List of filepaths of created scripts. This may differ from the supplied filepath depending on the py_script_mode """ from rez.config import config from rez.utils.platform_ import platform_ program = program or "python" py_script_mode = py_script_mode or config.create_executable_script_mode # https://github.com/nerdvegas/rez/pull/968 is_forwarding_script_on_windows = (program == "_rez_fwd" and platform_.name == "windows" and filepath.lower().endswith(".cmd")) if callable(body): from rez.utils.sourcecode import SourceCode code = SourceCode(func=body) body = code.source if not body.endswith('\n'): body += '\n' # Windows does not support shebang, but it will run with # default python, or in case of later python versions 'py' that should # try to use sensible python interpreters depending on the shebang line. # Compare PEP-397. # In order for execution to work in windows we need to create a .py # file and set the PATHEXT to include .py (as done by the shell plugins) # So depending on the py_script_mode we might need to create more then # one script script_filepaths = [filepath] if program == "python": script_filepaths = _get_python_script_files(filepath, py_script_mode, platform_.name) for current_filepath in script_filepaths: with open(current_filepath, 'w') as f: # TODO: make cross platform if is_forwarding_script_on_windows: # following lines of batch script will be stripped # before yaml.load f.write("@echo off\n") f.write("%s.exe %%~dpnx0\n" % program) f.write("goto :eof\n") # skip YAML body f.write(":: YAML\n") # comment for human else: f.write("#!/usr/bin/env %s\n" % program) f.write(body) # TODO: Although Windows supports os.chmod you can only set the readonly # flag. Setting the file readonly breaks the unit tests that expect to # clean up the files once the test has run. Temporarily we don't bother # setting the permissions, but this will need to change. if os.name == "posix": os.chmod( current_filepath, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) return script_filepaths