Ejemplo n.º 1
0
def test_toml_serializer() -> None:
    original_values: Dict = {
        "GLOBAL": {
            "truthy": True,
            "falsy": False,
            "int": 0,
            "float": 0.0,
            "word": "hello there",
            "listy": ["a", "b", "c"],
            "map": {
                "a": 0,
                "b": 1
            },
        },
        "some-subsystem": {
            "o": ""
        },
    }
    assert TomlSerializer(original_values).normalize() == {
        "GLOBAL": {
            **original_values["GLOBAL"], "map": "{'a': 0, 'b': 1}"
        },
        "some-subsystem": {
            "o": ""
        },
    }
Ejemplo n.º 2
0
def test_toml_serializer_list_add_remove() -> None:
    original_values = {"GLOBAL": {"backend_packages.add": ["added"]}}
    assert TomlSerializer(
        original_values).normalize() == {  # type: ignore[arg-type]
            "GLOBAL": {
                "backend_packages": "+['added']"
            }
        }
Ejemplo n.º 3
0
def test_toml_serializer_add_remove() -> None:
    original_values: Dict = {
        "GLOBAL": {
            "backend_packages.add": ["added"],
        },
    }
    assert TomlSerializer(original_values).normalize() == {
        "GLOBAL": {
            "backend_packages": "+['added']",
        },
    }
Ejemplo n.º 4
0
def test_toml_serializer() -> None:
    original_values: Dict = {
        "GLOBAL": {
            "truthy": True,
            "falsy": False,
            "int": 0,
            "float": 0.0,
            "word": "hello there",
            "listy": ["a", "b", "c"],
            "map": {"a": 0, "b": 1},
        },
        "cache.java": {"o": ""},
        "inception.nested.nested-again.one-more": {"o": ""},
    }
    assert TomlSerializer(original_values).normalize() == {
        "GLOBAL": {**original_values["GLOBAL"], "map": "{'a': 0, 'b': 1}"},
        "cache": {"java": {"o": ""}},
        "inception": {"nested": {"nested-again": {"one-more": {"o": ""}}}},
    }
Ejemplo n.º 5
0
def run_pants_with_workdir_without_waiting(
    command: Command,
    *,
    workdir: str,
    hermetic: bool = True,
    use_pantsd: bool = True,
    config: Mapping | None = None,
    extra_env: Mapping[str, str] | None = None,
    print_stacktrace: bool = True,
    **kwargs: Any,
) -> PantsJoinHandle:
    args = [
        "--no-pantsrc",
        f"--pants-workdir={workdir}",
        f"--print-stacktrace={print_stacktrace}",
    ]

    pantsd_in_command = "--no-pantsd" in command or "--pantsd" in command
    pantsd_in_config = config and "GLOBAL" in config and "pantsd" in config[
        "GLOBAL"]
    if not pantsd_in_command and not pantsd_in_config:
        args.append("--pantsd" if use_pantsd else "--no-pantsd")

    if hermetic:
        args.append("--pants-config-files=[]")

    if config:
        toml_file_name = os.path.join(workdir, "pants.toml")
        with safe_open(toml_file_name, mode="w") as fp:
            fp.write(TomlSerializer(config).serialize())
        args.append(f"--pants-config-files={toml_file_name}")

    pants_script = [sys.executable, "-m", "pants"]

    # Permit usage of shell=True and string-based commands to allow e.g. `./pants | head`.
    pants_command: Command
    if kwargs.get("shell") is True:
        assert not isinstance(
            command,
            list), "must pass command as a string when using shell=True"
        pants_command = " ".join([*pants_script, " ".join(args), command])
    else:
        pants_command = [*pants_script, *args, *command]

    # Only allow-listed entries will be included in the environment if hermetic=True. Note that
    # the env will already be fairly hermetic thanks to the v2 engine; this provides an
    # additional layer of hermiticity.
    if hermetic:
        # With an empty environment, we would generally get the true underlying system default
        # encoding, which is unlikely to be what we want (it's generally ASCII, still). So we
        # explicitly set an encoding here.
        env = {"LC_ALL": "en_US.UTF-8"}
        # Apply our allowlist.
        for h in (
                "HOME",
                "PATH",  # Needed to find Python interpreters and other binaries.
                "PANTS_PROFILE",
                "RUN_PANTS_FROM_PEX",
        ):
            value = os.getenv(h)
            if value is not None:
                env[h] = value
        hermetic_env = os.getenv("HERMETIC_ENV")
        if hermetic_env:
            for h in hermetic_env.strip(",").split(","):
                value = os.getenv(h)
                if value is not None:
                    env[h] = value
    else:
        env = os.environ.copy()
    if extra_env:
        env.update(extra_env)
    env.update(PYTHONPATH=os.pathsep.join(sys.path))

    # Pants command that was called from the test shouldn't have a parent.
    if "PANTS_PARENT_BUILD_ID" in env:
        del env["PANTS_PARENT_BUILD_ID"]

    return PantsJoinHandle(
        command=pants_command,
        process=subprocess.Popen(
            pants_command,
            env=env,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            **kwargs,
        ),
        workdir=workdir,
    )
Ejemplo n.º 6
0
    def run_pants_with_workdir_without_waiting(
        self,
        command,
        workdir,
        config=None,
        extra_env=None,
        build_root=None,
        print_exception_stacktrace=True,
        **kwargs,
    ) -> PantsJoinHandle:
        args = [
            "--no-pantsrc",
            f"--pants-workdir={workdir}",
            f"--print-exception-stacktrace={print_exception_stacktrace}",
        ]

        if self.hermetic():
            args.extend(
                [
                    "--pants-config-files=[]",
                    # Turn off cache globally.  A hermetic integration test shouldn't rely on cache,
                    # or we have no idea if it's actually testing anything.
                    "--no-cache-read",
                    "--no-cache-write",
                    # Turn cache on just for tool bootstrapping, for performance.
                    "--cache-bootstrap-read",
                    "--cache-bootstrap-write",
                ]
            )

        if self.use_pantsd_env_var():
            args.append("--pantsd")

        if config:
            toml_file_name = os.path.join(workdir, "pants.toml")
            with safe_open(toml_file_name, mode="w") as fp:
                fp.write(TomlSerializer(config).serialize())
            args.append("--pants-config-files=" + toml_file_name)

        pants_script = [sys.executable, "-m", "pants"]

        # Permit usage of shell=True and string-based commands to allow e.g. `./pants | head`.
        if kwargs.get("shell") is True:
            assert not isinstance(
                command, list
            ), "must pass command as a string when using shell=True"
            pants_command = " ".join([*pants_script, " ".join(args), command])
        else:
            pants_command = pants_script + args + command

        # Only whitelisted entries will be included in the environment if hermetic=True.
        if self.hermetic():
            env = dict()
            # With an empty environment, we would generally get the true underlying system default
            # encoding, which is unlikely to be what we want (it's generally ASCII, still). So we
            # explicitly set an encoding here.
            env["LC_ALL"] = "en_US.UTF-8"
            for h in self.hermetic_env_whitelist():
                value = os.getenv(h)
                if value is not None:
                    env[h] = value
            hermetic_env = os.getenv("HERMETIC_ENV")
            if hermetic_env:
                for h in hermetic_env.strip(",").split(","):
                    value = os.getenv(h)
                    if value is not None:
                        env[h] = value
        else:
            env = os.environ.copy()
        if extra_env:
            env.update(extra_env)
        env.update(PYTHONPATH=os.pathsep.join(sys.path))

        # Pants command that was called from the test shouldn't have a parent.
        if "PANTS_PARENT_BUILD_ID" in env:
            del env["PANTS_PARENT_BUILD_ID"]

        # Don't overwrite the profile of this process in the called process.
        # Instead, write the profile into a sibling file.
        if env.get("PANTS_PROFILE"):
            prof = f"{env['PANTS_PROFILE']}.{self._get_profile_disambiguator()}"
            env["PANTS_PROFILE"] = prof
            # Make a note the subprocess command, so the user can correctly interpret the profile files.
            with open(f"{prof}.cmd", "w") as fp:
                fp.write(" ".join(pants_command))

        return PantsJoinHandle(
            command=pants_command,
            process=subprocess.Popen(
                pants_command,
                env=env,
                stdin=subprocess.PIPE,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                **kwargs,
            ),
            workdir=workdir,
        )