Пример #1
0
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)
Пример #2
0
    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
Пример #3
0
    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
Пример #4
0
    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)
Пример #5
0
    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)
Пример #6
0
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
Пример #7
0
    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
Пример #8
0
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