Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pact-python-cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

All notable changes to this project will be documented in this file.

Note that this _only_ includes changes to the Python re-packaging of the Pact CLI. For changes to the Pact CLI itself, see the [Pact CLI changelog](https://github.com/pact-foundation/pact-ruby-standalone/blob/master/CHANGELOG.md).
Note that this _only_ includes changes to the Python re-packaging of the Pact CLI. For changes to the Pact CLI itself, see the [Pact CLI changelog](https://github.com/pact-foundation/pact-standalone/blob/master/CHANGELOG.md).

<!-- markdownlint-disable no-duplicate-heading -->
<!-- markdownlint-disable emph-style -->
Expand Down
4 changes: 2 additions & 2 deletions pact-python-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@

---

This sub-package is part of the [Pact Python](https://github.com/pact-foundation/pact-python) project and exists solely to distribute the [Pact CLI](https://github.com/pact-foundation/pact-ruby-standalone) as a Python package. If you are looking for the main Pact Python library for contract testing, please see the [root package](https://github.com/pact-foundation/pact-python#pact-python).
This sub-package is part of the [Pact Python](https://github.com/pact-foundation/pact-python) project and exists solely to distribute the [Pact CLI](https://github.com/pact-foundation/pact-standalone) as a Python package. If you are looking for the main Pact Python library for contract testing, please see the [root package](https://github.com/pact-foundation/pact-python#pact-python).

It is used by version 2 of Pact Python, and can be used to install the Pact CLI in Python environments.

Expand All @@ -108,7 +108,7 @@ pip install pact-python-cli

Contributions to this package are generally not required as it contains minimal Python functionality and generally only requires updating the version number. This is done by pushing a tag of the form `pact-python-cli/<version>` which will automatically trigger a release build in the CI pipeline.

To contribute to the Pact CLI itself, please refer to the [Pact Ruby Standalone repository](https://github.com/pact-foundation/pact-ruby-standalone).
To contribute to the Pact CLI itself, please refer to the [Pact Ruby Standalone repository](https://github.com/pact-foundation/pact-standalone).

For contributing to Pact Python, see the [main contributing guide](https://github.com/pact-foundation/pact-python/blob/main/CONTRIBUTING.md).

Expand Down
2 changes: 1 addition & 1 deletion pact-python-cli/cliff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ header = """

All notable changes to this project will be documented in this file.

Note that this _only_ includes changes to the Python re-packaging of the Pact CLI. For changes to the Pact CLI itself, see the [Pact CLI changelog](https://github.com/pact-foundation/pact-ruby-standalone/blob/master/CHANGELOG.md).
Note that this _only_ includes changes to the Python re-packaging of the Pact CLI. For changes to the Pact CLI itself, see the [Pact CLI changelog](https://github.com/pact-foundation/pact-standalone/blob/master/CHANGELOG.md).

<!-- markdownlint-disable no-duplicate-heading -->
<!-- markdownlint-disable emph-style -->
Expand Down
3 changes: 3 additions & 0 deletions pact-python-cli/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ requires-python = ">=3.10"
pact-mock-service = "pact_cli:_exec"
pact-plugin-cli = "pact_cli:_exec"
pact-provider-verifier = "pact_cli:_exec"
pact-stub-server = "pact_cli:_exec"
pact-stub-service = "pact_cli:_exec"
pact_mock_server_cli = "pact_cli:_exec"
pact_verifier_cli = "pact_cli:_exec"
pactflow = "pact_cli:_exec"

[dependency-groups]
Expand Down
41 changes: 38 additions & 3 deletions pact-python-cli/src/pact_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,16 @@
)

if TYPE_CHECKING:
from collections.abc import Mapping
from collections.abc import Container, Mapping

_USE_SYSTEM_BINS = os.getenv("PACT_USE_SYSTEM_BINS", "").upper() in ("TRUE", "YES")
_BIN_DIR = Path(__file__).parent.resolve() / "bin"
_LEGACY_BINS: Container[str] = frozenset((
"pact-message",
"pact-mock-service",
"pact-provider-verifier",
"pact-stub-service",
))


def _telemetry_env() -> Mapping[str, str]:
Expand Down Expand Up @@ -91,15 +97,26 @@ def _exec() -> None:
"pact-broker",
"pact-message",
"pact-mock-service",
"pact-provider-verifier",
"pact-plugin-cli",
"pact-publish",
"pact-provider-verifier",
"pact-stub-server",
"pact-stub-service",
"pact_mock_server_cli",
"pact_verifier_cli",
"pactflow",
):
print("Unknown command:", command, file=sys.stderr) # noqa: T201
sys.exit(1)

if command in _LEGACY_BINS:
warnings.warn(
f"The '{command}' executable is deprecated and will be removed in "
"a future release. Please migrate to the new Pact CLI tools. "
"See: <https://github.com/pact-foundation/pact-standalone>",
DeprecationWarning,
stacklevel=2,
)

if not _USE_SYSTEM_BINS:
executable = _find_executable(command)
else:
Expand Down Expand Up @@ -173,6 +190,9 @@ def _find_executable(executable: str) -> str | None:
BROKER_CLIENT_PATH = _find_executable("pact-broker")
"""
Path to the Pact Broker executable

This value is identical to `BROKER_PATH` and is provided for backward
compatibility.
"""
MESSAGE_PATH = _find_executable("pact-message")
"""
Expand All @@ -190,10 +210,25 @@ def _find_executable(executable: str) -> str | None:
"""
Path to the Pact Provider Verifier executable
"""
STUB_SERVER_PATH = _find_executable("pact-stub-server")
"""
Path to the Pact Stub Server executable
"""
STUB_SERVICE_PATH = _find_executable("pact-stub-service")
"""
Path to the Pact Stub Service executable
"""
MOCK_SERVER_PATH = _find_executable("pact_mock_server_cli")
"""
Path to the Pact Mock Server CLI executable
"""
VERIFIER_CLI_PATH = _find_executable("pact_verifier_cli")
"""
Path to the Pact Verifier CLI executable

This is distinct to the `VERIFIER_PATH` which points to the older Ruby-based
CLI.
"""
PACTFLOW_PATH = _find_executable("pactflow")
"""
Path to the PactFlow CLI executable
Expand Down
35 changes: 24 additions & 11 deletions pact-python-cli/tests/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,22 @@ def assert_in_sys_path(p: str | Path) -> None:
@pytest.mark.parametrize(
("constant", "expected"),
[
pytest.param("PACT_PATH", "pact", id="pact"),
pytest.param("BROKER_PATH", "pact-broker", id="pact-broker"),
pytest.param("BROKER_CLIENT_PATH", "pact-broker", id="pact-broker"),
pytest.param("BROKER_PATH", "pact-broker", id="pact-broker"),
pytest.param("MESSAGE_PATH", "pact-message", id="pactmessage"),
pytest.param("MOCK_SERVICE_PATH", "pact-mock-service", id="pact-message"),
pytest.param(
"MOCK_SERVER_PATH", "pact_mock_server_cli", id="pact_mock_server_cli"
),
pytest.param("MOCK_SERVICE_PATH", "pact-mock-service", id="pact-mock-service"),
pytest.param("PACTFLOW_PATH", "pactflow", id="pactflow"),
pytest.param("PACT_PATH", "pact", id="pact"),
pytest.param("PLUGIN_CLI_PATH", "pact-plugin-cli", id="pact-plugin-cli"),
pytest.param("STUB_SERVER_PATH", "pact-stub-server", id="pact-stub-server"),
pytest.param("STUB_SERVICE_PATH", "pact-stub-service", id="pact-stub-service"),
pytest.param("VERIFIER_CLI_PATH", "pact_verifier_cli", id="pact_verifier_cli"),
pytest.param(
"VERIFIER_PATH", "pact-provider-verifier", id="pact-provider-verifier"
),
pytest.param("STUB_SERVICE_PATH", "pact-stub-service", id="pact-stub-service"),
pytest.param("PACTFLOW_PATH", "pactflow", id="pactflow"),
],
)
def test_constants_are_valid_executable_paths(constant: str, expected: str) -> None:
Expand All @@ -86,7 +91,10 @@ def test_constants_are_valid_executable_paths(constant: str, expected: str) -> N
pytest.param("pact-message", id="pact-message"),
pytest.param("pact-plugin-cli", id="pact-plugin-cli"),
pytest.param("pact-provider-verifier", id="pact-provider-verifier"),
pytest.param("pact-stub-server", id="pact-stub-server"),
pytest.param("pact-stub-service", id="pact-stub-service"),
pytest.param("pact_mock_server_cli", id="pact_mock_server_cli"),
pytest.param("pact_verifier_cli", id="pact_verifier_cli"),
pytest.param("pactflow", id="pactflow"),
],
)
Expand Down Expand Up @@ -154,7 +162,10 @@ def test_cli_exec_wrapper_for_mock_service() -> None:
pytest.param("pact-mock-service", id="pact-mock-service"),
pytest.param("pact-plugin-cli", id="pact-plugin-cli"),
pytest.param("pact-provider-verifier", id="pact-provider-verifier"),
pytest.param("pact-stub-server", id="pact-stub-server"),
pytest.param("pact-stub-service", id="pact-stub-service"),
pytest.param("pact_mock_server_cli", id="pact_mock_server_cli"),
pytest.param("pact_verifier_cli", id="pact_verifier_cli"),
pytest.param("pactflow", id="pactflow"),
],
)
Expand All @@ -167,21 +178,23 @@ def test_exec_directly(executable: str) -> None:

with (
patch.object(sys, "argv", new=[executable, "--help"]),
patch("os.execv") as mock_execv,
patch("os.execve") as mock_execve,
):
pact_cli._exec() # noqa: SLF001
mock_execv.assert_called_once()
cmd, args = mock_execv.call_args[0]
mock_execve.assert_called_once()
cmd, args, env = mock_execve.call_args[0]
assert (os.sep + executable) in cmd
assert args == [cmd, "--help"]
assert env

patch.object(sys, "argv", new=[executable])
with (
patch.object(sys, "argv", new=[executable]),
patch("os.execv") as mock_execv,
patch("os.execve") as mock_execve,
):
pact_cli._exec() # noqa: SLF001
mock_execv.assert_called_once()
cmd, args = mock_execv.call_args[0]
mock_execve.assert_called_once()
cmd, args, env = mock_execve.call_args[0]
assert (os.sep + executable) in cmd
assert args == [cmd]
assert env
Loading