From f5f85cb20b9fd7d54ce570b878817ffe9ab4d630 Mon Sep 17 00:00:00 2001 From: Yaroslav Petrov Date: Tue, 10 Jun 2025 21:06:29 +0000 Subject: [PATCH 01/14] Add basic implementation of amqp_params --- .devcontainer/devcontainer.json | 1 + src/asyncapi_python/amqp/__init__.py | 2 ++ src/asyncapi_python/amqp/base_application.py | 3 +++ src/asyncapi_python/amqp/connection.py | 1 + src/asyncapi_python/amqp/endpoint/base.py | 2 ++ src/asyncapi_python/amqp/endpoint/receiver.py | 2 ++ src/asyncapi_python/amqp/params.py | 4 ++++ .../generators/amqp/templates/application.py.j2 | 6 +++--- 8 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 src/asyncapi_python/amqp/params.py diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 4cac009..12deb9e 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -25,6 +25,7 @@ "github.vscode-github-actions" ], "settings": { + "python.defaultInterpreterPath": "/workspaces/${localWorkspaceFolderBasename}/.venv/bin/python", "yaml.schemas": { "https://asyncapi.com/schema-store/3.0.0-without-$id.json": [ "file:///workspaces/asyncapi-python/examples/*.yaml" diff --git a/src/asyncapi_python/amqp/__init__.py b/src/asyncapi_python/amqp/__init__.py index 290ac2d..d570ec6 100644 --- a/src/asyncapi_python/amqp/__init__.py +++ b/src/asyncapi_python/amqp/__init__.py @@ -19,9 +19,11 @@ from .operation import Operation from .utils import union_model from .error import Rejection, RejectedError +from .params import AmqpParams __all__ = [ "channel_pool", + "AmqpParams", "AmqpPool", "BaseApplication", "Router", diff --git a/src/asyncapi_python/amqp/base_application.py b/src/asyncapi_python/amqp/base_application.py index 02af354..00583b9 100644 --- a/src/asyncapi_python/amqp/base_application.py +++ b/src/asyncapi_python/amqp/base_application.py @@ -23,6 +23,7 @@ from .endpoint import EndpointParams from .connection import channel_pool from .utils import encode_message, decode_message +from .params import AmqpParams from typing import Generic, Optional, TypeVar @@ -61,6 +62,7 @@ def __init__( amqp_uri: str, producer_factory: type[P], consumer_factory: type[C], + amqp_params: AmqpParams, ): self.__params = EndpointParams( pool=channel_pool(amqp_uri), @@ -69,6 +71,7 @@ def __init__( register_correlation_id=self.__register_correlation_id, stop_application=self.stop, app_id=str(uuid4()), + amqp_params=amqp_params, ) self.__reply_futures: dict[ str, diff --git a/src/asyncapi_python/amqp/connection.py b/src/asyncapi_python/amqp/connection.py index e24a86a..cc97c15 100644 --- a/src/asyncapi_python/amqp/connection.py +++ b/src/asyncapi_python/amqp/connection.py @@ -14,6 +14,7 @@ from functools import cache +from typing import TypeAlias from aio_pika.robust_connection import ( AbstractRobustConnection, AbstractRobustChannel, diff --git a/src/asyncapi_python/amqp/endpoint/base.py b/src/asyncapi_python/amqp/endpoint/base.py index ed4443b..fd3069a 100644 --- a/src/asyncapi_python/amqp/endpoint/base.py +++ b/src/asyncapi_python/amqp/endpoint/base.py @@ -31,6 +31,7 @@ from ..error import Rejection, RejectedError from ..connection import AmqpPool from ..operation import Operation +from ..params import AmqpParams from aio_pika.abc import ( AbstractRobustChannel, AbstractRobustQueue, @@ -64,6 +65,7 @@ class EndpointParams: ] app_id: str stop_application: Callable[[], Awaitable[None]] + amqp_params: AmqpParams @property def reply_queue_name(self) -> str: diff --git a/src/asyncapi_python/amqp/endpoint/receiver.py b/src/asyncapi_python/amqp/endpoint/receiver.py index 896efb6..4e09005 100644 --- a/src/asyncapi_python/amqp/endpoint/receiver.py +++ b/src/asyncapi_python/amqp/endpoint/receiver.py @@ -45,6 +45,8 @@ async def start(self) -> None: print("start", self._op) if self._fn: async with self._params.pool.acquire() as ch: + if self._params.amqp_params.prefetch_count: + await ch.set_qos(prefetch_count=self._params.amqp_params.prefetch_count) q = self._queue = await self._declare(ch) self._consumer_tag = await q.consume(self._consumer) return diff --git a/src/asyncapi_python/amqp/params.py b/src/asyncapi_python/amqp/params.py new file mode 100644 index 0000000..c772199 --- /dev/null +++ b/src/asyncapi_python/amqp/params.py @@ -0,0 +1,4 @@ +from typing import TypedDict + +class AmqpParams(TypedDict, total=False): + prefetch_count: str diff --git a/src/asyncapi_python_codegen/generators/amqp/templates/application.py.j2 b/src/asyncapi_python_codegen/generators/amqp/templates/application.py.j2 index 6647723..c8f69f2 100644 --- a/src/asyncapi_python_codegen/generators/amqp/templates/application.py.j2 +++ b/src/asyncapi_python_codegen/generators/amqp/templates/application.py.j2 @@ -13,9 +13,9 @@ limitations under the License. #} from .consumer import _Router_0 as Consumer from .producer import _Router_0 as Producer -from asyncapi_python.amqp.base_application import BaseApplication +from asyncapi_python.amqp import BaseApplication, AmqpParams class Application(BaseApplication[Producer, Consumer]): - def __init__(self, amqp_uri: str): - super().__init__(amqp_uri, Producer, Consumer) + def __init__(self, amqp_uri: str, amqp_params: AmqpParams = {}): + super().__init__(amqp_uri, Producer, Consumer, amqp_params) From afe3e162d577e4d83653688d4343784e80ed1c0f Mon Sep 17 00:00:00 2001 From: Yaroslav Petrov Date: Tue, 10 Jun 2025 21:50:50 +0000 Subject: [PATCH 02/14] Fix typing --- src/asyncapi_python/amqp/connection.py | 1 - src/asyncapi_python/amqp/endpoint/receiver.py | 4 ++-- src/asyncapi_python/amqp/params.py | 3 ++- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/asyncapi_python/amqp/connection.py b/src/asyncapi_python/amqp/connection.py index cc97c15..e24a86a 100644 --- a/src/asyncapi_python/amqp/connection.py +++ b/src/asyncapi_python/amqp/connection.py @@ -14,7 +14,6 @@ from functools import cache -from typing import TypeAlias from aio_pika.robust_connection import ( AbstractRobustConnection, AbstractRobustChannel, diff --git a/src/asyncapi_python/amqp/endpoint/receiver.py b/src/asyncapi_python/amqp/endpoint/receiver.py index 4e09005..e75fbcf 100644 --- a/src/asyncapi_python/amqp/endpoint/receiver.py +++ b/src/asyncapi_python/amqp/endpoint/receiver.py @@ -45,8 +45,8 @@ async def start(self) -> None: print("start", self._op) if self._fn: async with self._params.pool.acquire() as ch: - if self._params.amqp_params.prefetch_count: - await ch.set_qos(prefetch_count=self._params.amqp_params.prefetch_count) + if prefetch_count := self._params.amqp_params.get("prefetch_count"): + await ch.set_qos(prefetch_count=prefetch_count) q = self._queue = await self._declare(ch) self._consumer_tag = await q.consume(self._consumer) return diff --git a/src/asyncapi_python/amqp/params.py b/src/asyncapi_python/amqp/params.py index c772199..b66b4ce 100644 --- a/src/asyncapi_python/amqp/params.py +++ b/src/asyncapi_python/amqp/params.py @@ -1,4 +1,5 @@ from typing import TypedDict + class AmqpParams(TypedDict, total=False): - prefetch_count: str + prefetch_count: int From fcf7e7e53add94b55135e7e5ab58aee5f114b808 Mon Sep 17 00:00:00 2001 From: Yaroslav Petrov Date: Wed, 11 Jun 2025 21:34:40 +0000 Subject: [PATCH 03/14] Update docs and tests --- README.md | 49 +++++--------------------------- pyproject.toml | 2 +- tests/core/amqp/test_endpoint.py | 1 + 3 files changed, 9 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 9c02875..fda745e 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ asyncapi_python_service( ) ``` -This will be generating python module named `asyncapi_app` on `codegen-export` and `export` goals. +This will be generating python module named `asyncapi_app` on `codegen-export`, and `export` goals. This target can later be used as a dependency of `python_sources`. ```python @@ -100,51 +100,16 @@ Note that this plugin does not do dependency injection, so asyncapi-python must asyncapi-python[amqp] ``` -### Deploying this plugin into pants monorepo - -In order to deploy this plugin into your pants monorepo, create the following structure inside your plugins folder: - -```bash -pants-plugins/ -└── asyncapi_python_plugin - ├── BUILD - ├── __init__.py - ├── register.py - └── requirements.txt -``` - -`requirements.txt` must contain: - -```text -asyncapi-python -``` - -`register.py` should have: - -```python -from asyncapi_python_pants.register import * -``` - -`BUILD` must include: - -```python -python_sources( - dependencies=[":reqs"], -) - -python_requirements( - name="reqs", -) -``` - -`__init__.py` can be empty, but it has to exist. - -Finally, add `pants-plugins` to your `PYTHONPATH`, and add the created folder as a backend package: +### Deploying this plugin into your pants monorepo ```toml # pants.toml +plugins = [ + "asyncapi_python[codegen]==0.2.5", # Plugin version MUST match the version of your python clients + ... +] backend_packages = [ - "asyncapi_python_plugin", + "asyncapi_python_pants", ... ] pythonpath = ["%(buildroot)s/pants-plugins"] diff --git a/pyproject.toml b/pyproject.toml index 7cc49e0..21cfbc0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "asyncapi-python" -version = "0.2.4" +version = "0.2.5" license = "Apache-2.0" description = "Easily generate type-safe and async Python applications from AsyncAPI 3 specifications." authors = ["Yaroslav Petrov "] diff --git a/tests/core/amqp/test_endpoint.py b/tests/core/amqp/test_endpoint.py index 23de56e..fad5c1a 100644 --- a/tests/core/amqp/test_endpoint.py +++ b/tests/core/amqp/test_endpoint.py @@ -117,6 +117,7 @@ def params( decode=decode_message, app_id=app_id, stop_application=lambda: exit(-1), + amqp_params={}, ) From fb23f5ba87498836735804bf4bd10c8b8687aa41 Mon Sep 17 00:00:00 2001 From: Yaroslav Petrov Date: Wed, 11 Jun 2025 22:02:07 +0000 Subject: [PATCH 04/14] Fix tests --- pyproject.toml | 9 +++++---- tests/core/amqp/conftest.py | 1 + tests/core/amqp/test_endpoint.py | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 21cfbc0..8af6481 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,9 +52,10 @@ pex = "*" requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" -[tool.pytest.ini_options] -asyncio_mode = "auto" -asyncio_default_fixture_loop_scope = "function" - [tool.poetry.requires-plugins] poetry-plugin-export = ">=1.8" + +[tool.pytest.ini_options] +asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "session" +asyncio_default_test_loop_scope = "session" diff --git a/tests/core/amqp/conftest.py b/tests/core/amqp/conftest.py index 65acef1..a6a068a 100644 --- a/tests/core/amqp/conftest.py +++ b/tests/core/amqp/conftest.py @@ -20,5 +20,6 @@ @pytest_asyncio.fixture(scope="function") async def amqp_pool(amqp_uri: str) -> AsyncGenerator[AmqpPool, None]: + channel_pool.cache_clear() pool = channel_pool(amqp_uri) yield pool diff --git a/tests/core/amqp/test_endpoint.py b/tests/core/amqp/test_endpoint.py index fad5c1a..07ca1d7 100644 --- a/tests/core/amqp/test_endpoint.py +++ b/tests/core/amqp/test_endpoint.py @@ -136,6 +136,7 @@ def params_2(amqp_pool, correlation_ids): return params("app-2", amqp_pool, correlation_ids) +@pytest.mark.asyncio async def test_queue(params_1: EndpointParams, operation: Operation): producer: Sender[Log] = Sender(operation, params_1) consumer: Receiver[Log] = Receiver(operation, params_1) From 1ba4a4e4ecf626614a5db203e6cfb7a0e364485b Mon Sep 17 00:00:00 2001 From: Yaroslav Petrov Date: Tue, 19 Aug 2025 18:22:11 +0200 Subject: [PATCH 05/14] Update pex in rules --- src/asyncapi_python_pants/rules.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/asyncapi_python_pants/rules.py b/src/asyncapi_python_pants/rules.py index 9493766..50905b6 100644 --- a/src/asyncapi_python_pants/rules.py +++ b/src/asyncapi_python_pants/rules.py @@ -20,6 +20,7 @@ from pants.backend.python.util_rules.interpreter_constraints import ( InterpreterConstraints, ) +from pants.backend.python.util_rules.pex import EntryPoint from pants.backend.python.util_rules.pex import ( Pex, PexProcess, @@ -38,11 +39,9 @@ async def generate_python_from_asyncapi( PexRequest( output_filename="asyncapi-python-codegen.pex", internal_only=True, - requirements=PexRequirements( - [f"asyncapi-python[codegen]=={version('asyncapi-python')}"] - ), + requirements=PexRequirements([]), interpreter_constraints=InterpreterConstraints([">=3.9"]), - main=ConsoleScript("asyncapi-python-codegen"), + main=EntryPoint("asyncapi_python_codegen"), ), ) transitive_targets = await Get( From fb5700c183d745a20b6c222b3037a8f9e182fd69 Mon Sep 17 00:00:00 2001 From: Yaroslav Petrov Date: Tue, 19 Aug 2025 18:25:13 +0200 Subject: [PATCH 06/14] Update rules --- src/asyncapi_python_pants/rules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asyncapi_python_pants/rules.py b/src/asyncapi_python_pants/rules.py index 50905b6..4fea9b3 100644 --- a/src/asyncapi_python_pants/rules.py +++ b/src/asyncapi_python_pants/rules.py @@ -41,7 +41,7 @@ async def generate_python_from_asyncapi( internal_only=True, requirements=PexRequirements([]), interpreter_constraints=InterpreterConstraints([">=3.9"]), - main=EntryPoint("asyncapi_python_codegen"), + main=EntryPoint("asyncapi_python_codegen:app"), ), ) transitive_targets = await Get( From 57561518fa42d777e4ad925c3eea221fe5c9d475 Mon Sep 17 00:00:00 2001 From: Yaroslav Petrov Date: Tue, 19 Aug 2025 18:29:42 +0200 Subject: [PATCH 07/14] Update rules --- src/asyncapi_python_pants/rules.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/asyncapi_python_pants/rules.py b/src/asyncapi_python_pants/rules.py index 4fea9b3..19a2bbc 100644 --- a/src/asyncapi_python_pants/rules.py +++ b/src/asyncapi_python_pants/rules.py @@ -1,4 +1,3 @@ -from importlib.metadata import version from pants.engine.internals.native_engine import ( Digest, MergeDigests, @@ -16,14 +15,12 @@ from pants.engine.rules import rule, Get, MultiGet from pants.engine.process import ProcessResult from pants.source.source_root import SourceRoot, SourceRootRequest -from pants.backend.python.target_types import ConsoleScript from pants.backend.python.util_rules.interpreter_constraints import ( InterpreterConstraints, ) -from pants.backend.python.util_rules.pex import EntryPoint from pants.backend.python.util_rules.pex import ( Pex, - PexProcess, + Process, PexRequest, PexRequirements, ) @@ -41,7 +38,7 @@ async def generate_python_from_asyncapi( internal_only=True, requirements=PexRequirements([]), interpreter_constraints=InterpreterConstraints([">=3.9"]), - main=EntryPoint("asyncapi_python_codegen:app"), + # No main parameter - creates a REPL-style PEX ), ) transitive_targets = await Get( @@ -66,11 +63,16 @@ async def generate_python_from_asyncapi( ) output_dir = "_generated_files" module_name = request.protocol_target.address.target_name + + # Use Process instead of PexProcess to execute with -m flag result = await Get( ProcessResult, - PexProcess( - pex, + Process( argv=[ + pex.pex.path, + "-m", + "asyncapi_python_codegen", # Execute as module + "generate", # Your CLI command request.protocol_target[AsyncapiServiceField].value or "", f"{output_dir}/{module_name}", ], From ec0a2e0471cc6d528ecf14888956712e146f54a8 Mon Sep 17 00:00:00 2001 From: Yaroslav Petrov Date: Tue, 19 Aug 2025 18:32:21 +0200 Subject: [PATCH 08/14] Update rules --- src/asyncapi_python_pants/rules.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/asyncapi_python_pants/rules.py b/src/asyncapi_python_pants/rules.py index 19a2bbc..e2fd118 100644 --- a/src/asyncapi_python_pants/rules.py +++ b/src/asyncapi_python_pants/rules.py @@ -13,14 +13,13 @@ TransitiveTargetsRequest, ) from pants.engine.rules import rule, Get, MultiGet -from pants.engine.process import ProcessResult +from pants.engine.process import ProcessResult, Process from pants.source.source_root import SourceRoot, SourceRootRequest from pants.backend.python.util_rules.interpreter_constraints import ( InterpreterConstraints, ) from pants.backend.python.util_rules.pex import ( Pex, - Process, PexRequest, PexRequirements, ) @@ -69,7 +68,7 @@ async def generate_python_from_asyncapi( ProcessResult, Process( argv=[ - pex.pex.path, + "./asyncapi-python-codegen.pex", # Use relative path to PEX file "-m", "asyncapi_python_codegen", # Execute as module "generate", # Your CLI command From de9e18f310f73ecf33d6126f6d29f922747440f1 Mon Sep 17 00:00:00 2001 From: Yaroslav Petrov Date: Tue, 19 Aug 2025 18:34:42 +0200 Subject: [PATCH 09/14] Update rules --- src/asyncapi_python_pants/rules.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/asyncapi_python_pants/rules.py b/src/asyncapi_python_pants/rules.py index e2fd118..923acd2 100644 --- a/src/asyncapi_python_pants/rules.py +++ b/src/asyncapi_python_pants/rules.py @@ -63,12 +63,13 @@ async def generate_python_from_asyncapi( output_dir = "_generated_files" module_name = request.protocol_target.address.target_name - # Use Process instead of PexProcess to execute with -m flag + # Use python to execute the PEX file with -m flag result = await Get( ProcessResult, Process( argv=[ - "./asyncapi-python-codegen.pex", # Use relative path to PEX file + "python3", # Use python interpreter + "asyncapi-python-codegen.pex", # PEX file (no ./ prefix) "-m", "asyncapi_python_codegen", # Execute as module "generate", # Your CLI command From c1dcd8cad3647a6f6e5cc3d098b07d2f812072f1 Mon Sep 17 00:00:00 2001 From: Yaroslav Petrov Date: Tue, 19 Aug 2025 18:37:02 +0200 Subject: [PATCH 10/14] Update rules --- src/asyncapi_python_pants/rules.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/asyncapi_python_pants/rules.py b/src/asyncapi_python_pants/rules.py index 923acd2..3584a8e 100644 --- a/src/asyncapi_python_pants/rules.py +++ b/src/asyncapi_python_pants/rules.py @@ -13,7 +13,7 @@ TransitiveTargetsRequest, ) from pants.engine.rules import rule, Get, MultiGet -from pants.engine.process import ProcessResult, Process +from pants.engine.process import ProcessResult from pants.source.source_root import SourceRoot, SourceRootRequest from pants.backend.python.util_rules.interpreter_constraints import ( InterpreterConstraints, @@ -22,6 +22,7 @@ Pex, PexRequest, PexRequirements, + PexProcess, ) from .targets import * @@ -63,13 +64,12 @@ async def generate_python_from_asyncapi( output_dir = "_generated_files" module_name = request.protocol_target.address.target_name - # Use python to execute the PEX file with -m flag + # Use PexProcess to properly execute the PEX result = await Get( ProcessResult, - Process( + PexProcess( + pex, argv=[ - "python3", # Use python interpreter - "asyncapi-python-codegen.pex", # PEX file (no ./ prefix) "-m", "asyncapi_python_codegen", # Execute as module "generate", # Your CLI command From 0e3718126eded48ee6b3cb377f07bab1a3fa25ed Mon Sep 17 00:00:00 2001 From: Yaroslav Petrov Date: Tue, 19 Aug 2025 18:39:47 +0200 Subject: [PATCH 11/14] Update rules --- src/asyncapi_python_pants/rules.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/asyncapi_python_pants/rules.py b/src/asyncapi_python_pants/rules.py index 3584a8e..bc16789 100644 --- a/src/asyncapi_python_pants/rules.py +++ b/src/asyncapi_python_pants/rules.py @@ -13,7 +13,7 @@ TransitiveTargetsRequest, ) from pants.engine.rules import rule, Get, MultiGet -from pants.engine.process import ProcessResult +from pants.engine.process import ProcessResult, Process from pants.source.source_root import SourceRoot, SourceRootRequest from pants.backend.python.util_rules.interpreter_constraints import ( InterpreterConstraints, @@ -22,7 +22,6 @@ Pex, PexRequest, PexRequirements, - PexProcess, ) from .targets import * @@ -36,9 +35,15 @@ async def generate_python_from_asyncapi( PexRequest( output_filename="asyncapi-python-codegen.pex", internal_only=True, - requirements=PexRequirements([]), + requirements=PexRequirements( + [ + # Include your plugin as a requirement so it's available in the PEX + "asyncapi_python_codegen", # or whatever your package is called + ] + ), interpreter_constraints=InterpreterConstraints([">=3.9"]), - # No main parameter - creates a REPL-style PEX + # Make it executable by specifying the main module + main="-m asyncapi_python_codegen", # This makes it executable ), ) transitive_targets = await Get( @@ -64,14 +69,12 @@ async def generate_python_from_asyncapi( output_dir = "_generated_files" module_name = request.protocol_target.address.target_name - # Use PexProcess to properly execute the PEX + # Now use Process with the executable PEX result = await Get( ProcessResult, - PexProcess( - pex, + Process( argv=[ - "-m", - "asyncapi_python_codegen", # Execute as module + "./asyncapi-python-codegen.pex", # Executable PEX "generate", # Your CLI command request.protocol_target[AsyncapiServiceField].value or "", f"{output_dir}/{module_name}", From ca1d8ae302bc773341fa1f31aec5cfefbc202622 Mon Sep 17 00:00:00 2001 From: Yaroslav Petrov Date: Tue, 19 Aug 2025 18:54:40 +0200 Subject: [PATCH 12/14] Update rules --- src/asyncapi_python_pants/rules.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/asyncapi_python_pants/rules.py b/src/asyncapi_python_pants/rules.py index bc16789..d35fbe2 100644 --- a/src/asyncapi_python_pants/rules.py +++ b/src/asyncapi_python_pants/rules.py @@ -30,20 +30,19 @@ async def generate_python_from_asyncapi( request: GeneratePythonFromAsyncapiRequest, ) -> GeneratedSources: + pex_filename = "asyncapi-python-codegen.pex" pex = await Get( Pex, PexRequest( - output_filename="asyncapi-python-codegen.pex", + output_filename=pex_filename, internal_only=True, requirements=PexRequirements( [ - # Include your plugin as a requirement so it's available in the PEX - "asyncapi_python_codegen", # or whatever your package is called + "asyncapi_python_codegen", ] ), interpreter_constraints=InterpreterConstraints([">=3.9"]), - # Make it executable by specifying the main module - main="-m asyncapi_python_codegen", # This makes it executable + # Don't specify main - we'll handle execution manually ), ) transitive_targets = await Get( @@ -69,13 +68,16 @@ async def generate_python_from_asyncapi( output_dir = "_generated_files" module_name = request.protocol_target.address.target_name - # Now use Process with the executable PEX + # Execute the PEX with python and -m flag result = await Get( ProcessResult, Process( argv=[ - "./asyncapi-python-codegen.pex", # Executable PEX - "generate", # Your CLI command + "python", # Use python interpreter + pex_filename, # PEX file + "-m", + "asyncapi_python_codegen", # Module execution + "generate", # CLI command request.protocol_target[AsyncapiServiceField].value or "", f"{output_dir}/{module_name}", ], From a96f63bf9434aa4a78e13261c3c3605304ac3cc4 Mon Sep 17 00:00:00 2001 From: Yaroslav Petrov Date: Tue, 19 Aug 2025 18:57:08 +0200 Subject: [PATCH 13/14] Update rules --- src/asyncapi_python_pants/rules.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/asyncapi_python_pants/rules.py b/src/asyncapi_python_pants/rules.py index d35fbe2..ea10c48 100644 --- a/src/asyncapi_python_pants/rules.py +++ b/src/asyncapi_python_pants/rules.py @@ -23,6 +23,8 @@ PexRequest, PexRequirements, ) +from pants.util.strutil import softwrap +import os from .targets import * @@ -30,19 +32,14 @@ async def generate_python_from_asyncapi( request: GeneratePythonFromAsyncapiRequest, ) -> GeneratedSources: - pex_filename = "asyncapi-python-codegen.pex" pex = await Get( Pex, PexRequest( - output_filename=pex_filename, + output_filename="asyncapi-python-codegen.pex", internal_only=True, - requirements=PexRequirements( - [ - "asyncapi_python_codegen", - ] - ), + requirements=PexRequirements([]), interpreter_constraints=InterpreterConstraints([">=3.9"]), - # Don't specify main - we'll handle execution manually + # No main parameter - creates a REPL-style PEX ), ) transitive_targets = await Get( @@ -68,16 +65,16 @@ async def generate_python_from_asyncapi( output_dir = "_generated_files" module_name = request.protocol_target.address.target_name - # Execute the PEX with python and -m flag + # Use python to execute the PEX instead of executing it directly result = await Get( ProcessResult, Process( argv=[ "python", # Use python interpreter - pex_filename, # PEX file + "asyncapi-python-codegen.pex", # PEX file (no ./ needed) "-m", - "asyncapi_python_codegen", # Module execution - "generate", # CLI command + "asyncapi_python_codegen", # Execute as module + "generate", # Your CLI command request.protocol_target[AsyncapiServiceField].value or "", f"{output_dir}/{module_name}", ], From b9c3c1dab35464b79a733b24ab8d75c17fd4bf51 Mon Sep 17 00:00:00 2001 From: Yaroslav Petrov Date: Tue, 19 Aug 2025 19:02:20 +0200 Subject: [PATCH 14/14] Update rules --- src/asyncapi_python_pants/rules.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/asyncapi_python_pants/rules.py b/src/asyncapi_python_pants/rules.py index ea10c48..56cd5b1 100644 --- a/src/asyncapi_python_pants/rules.py +++ b/src/asyncapi_python_pants/rules.py @@ -13,18 +13,17 @@ TransitiveTargetsRequest, ) from pants.engine.rules import rule, Get, MultiGet -from pants.engine.process import ProcessResult, Process +from pants.engine.process import ProcessResult from pants.source.source_root import SourceRoot, SourceRootRequest from pants.backend.python.util_rules.interpreter_constraints import ( InterpreterConstraints, ) from pants.backend.python.util_rules.pex import ( Pex, + PexProcess, PexRequest, PexRequirements, ) -from pants.util.strutil import softwrap -import os from .targets import * @@ -65,13 +64,12 @@ async def generate_python_from_asyncapi( output_dir = "_generated_files" module_name = request.protocol_target.address.target_name - # Use python to execute the PEX instead of executing it directly + # Use PexProcess to execute the PEX result = await Get( ProcessResult, - Process( + PexProcess( + pex, argv=[ - "python", # Use python interpreter - "asyncapi-python-codegen.pex", # PEX file (no ./ needed) "-m", "asyncapi_python_codegen", # Execute as module "generate", # Your CLI command