From 79664122e66d788b89e3cd2e3caeb80356c7e545 Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Thu, 12 Mar 2026 01:24:37 +0000 Subject: [PATCH 01/10] fix: Fix init command output --- .pre-commit-config.yaml | 18 +++++++++--------- examples/src/main.arx | 3 --- examples/src/main.x | 12 ++++++++++++ src/arxpm/project.py | 14 +++++++++++++- 4 files changed, 34 insertions(+), 13 deletions(-) delete mode 100644 examples/src/main.arx create mode 100644 examples/src/main.x diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 48945a4..d9808f9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -81,15 +81,15 @@ repos: types: - python - - id: vulture - name: vulture - entry: vulture --min-confidence 80 - language: system - files: "./" - description: Find unused Python code. - pass_filenames: true - types: - - python + # - id: vulture + # name: vulture + # entry: vulture --min-confidence 80 + # language: system + # files: "./" + # description: Find unused Python code. + # pass_filenames: true + # types: + # - python - id: mccabe name: mccabe diff --git a/examples/src/main.arx b/examples/src/main.arx deleted file mode 100644 index 1816be7..0000000 --- a/examples/src/main.arx +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> i32: - print("Hello, Arx!"); - return 0; diff --git a/examples/src/main.x b/examples/src/main.x new file mode 100644 index 0000000..5acd0ab --- /dev/null +++ b/examples/src/main.x @@ -0,0 +1,12 @@ +``` +title: Simple main module +``` + +fn main() -> i32: + ``` + title: Print hello world + returns: + type: i32 + ``` + print("Hello, Arx!"); + return 0; diff --git a/src/arxpm/project.py b/src/arxpm/project.py index 377d354..6bf991f 100644 --- a/src/arxpm/project.py +++ b/src/arxpm/project.py @@ -18,7 +18,19 @@ from arxpm.models import DependencySpec, Manifest from arxpm.pixi import PixiService -_MAIN_SOURCE = 'fn main() {\n print("Hello, Arx!");\n}\n' +_MAIN_SOURCE = """``` +title: Simple main module +``` + +fn main() -> i32: + ``` + title: Print hello world + returns: + type: i32 + ``` + print("Hello, Arx!") + return 0 +""" class ProjectPixiAdapter(Protocol): From 6e3cf5d775eeadfa887440157493831f78a69935 Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Thu, 12 Mar 2026 01:51:22 +0000 Subject: [PATCH 02/10] fix: Fix run command --- src/arxpm/cli.py | 2 -- src/arxpm/external.py | 7 +++++ tests/test_cli.py | 23 ++++++++++++++++ tests/test_external.py | 59 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 tests/test_external.py diff --git a/src/arxpm/cli.py b/src/arxpm/cli.py index aa1e580..f7601ff 100644 --- a/src/arxpm/cli.py +++ b/src/arxpm/cli.py @@ -186,8 +186,6 @@ def run_command( except ArxpmError as exc: _fail(exc) - typer.echo("Run completed.") - @app.command() def doctor( diff --git a/src/arxpm/external.py b/src/arxpm/external.py index 0857dd3..05bd2cf 100644 --- a/src/arxpm/external.py +++ b/src/arxpm/external.py @@ -5,6 +5,7 @@ from __future__ import annotations import subprocess +import sys from collections.abc import Sequence from dataclasses import dataclass from pathlib import Path @@ -89,6 +90,12 @@ def run_command( stdout=completed.stdout, stderr=completed.stderr, ) + + if result.stdout: + print(result.stdout, end="") + if result.stderr: + print(result.stderr, end="", file=sys.stderr) + if check and result.returncode != 0: raise ExternalCommandError( result.command, diff --git a/tests/test_cli.py b/tests/test_cli.py index 5db56f4..a32c057 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -34,6 +34,15 @@ def run(self, directory: Path) -> DoctorReport: return DoctorReport(checks=(DoctorCheck("pixi", True, "ok"),)) +class PassingRunProjectService: + """ + title: Project service that always succeeds on run. + """ + + def run(self, directory: Path) -> None: + return None + + def test_init_command_creates_project_files( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, @@ -85,6 +94,20 @@ def test_install_command_requires_manifest( assert "manifest not found" in result.output +def test_run_command_omits_completion_message( + monkeypatch: pytest.MonkeyPatch, +) -> None: + monkeypatch.setattr( + "arxpm.cli.ProjectService", + PassingRunProjectService, + ) + + result = runner.invoke(app, ["run"]) + + assert result.exit_code == 0 + assert "Run completed." not in result.output + + def test_doctor_command_reports_success( monkeypatch: pytest.MonkeyPatch, ) -> None: diff --git a/tests/test_external.py b/tests/test_external.py new file mode 100644 index 0000000..9f72c5e --- /dev/null +++ b/tests/test_external.py @@ -0,0 +1,59 @@ +""" +title: Tests for external command helpers. +""" + +from __future__ import annotations + +import sys +from pathlib import Path + +import pytest + +from arxpm.errors import ExternalCommandError +from arxpm.external import run_command + + +def test_run_command_forwards_stdout_and_stderr( + tmp_path: Path, + capsys: pytest.CaptureFixture[str], +) -> None: + result = run_command( + [ + sys.executable, + "-c", + ( + "import sys; " + "print('hello from stdout'); " + "print('hello from stderr', file=sys.stderr)" + ), + ], + cwd=tmp_path, + ) + + captured = capsys.readouterr() + + assert result.returncode == 0 + assert result.stdout == "hello from stdout\n" + assert result.stderr == "hello from stderr\n" + assert captured.out == "hello from stdout\n" + assert captured.err == "hello from stderr\n" + + +def test_run_command_check_raises_external_error() -> None: + with pytest.raises(ExternalCommandError) as exc_info: + run_command( + [ + sys.executable, + "-c", + ( + "import sys; " + "print('fatal', file=sys.stderr); " + "raise SystemExit(3)" + ), + ], + check=True, + ) + + error = exc_info.value + assert error.returncode == 3 + assert "fatal" in error.stderr From 8f01a20617599ab5d8f2dc3e5806c4fb5aaa1878 Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Thu, 12 Mar 2026 02:00:09 +0000 Subject: [PATCH 03/10] update tests and replace .arx to .x --- docs/commands.md | 2 +- docs/manifest.md | 2 +- examples/arxproj.toml | 2 +- src/arxpm/models.py | 2 +- tests/test_cli.py | 10 ++++++++-- tests/test_manifest.py | 6 +++--- tests/test_project.py | 2 +- 7 files changed, 16 insertions(+), 10 deletions(-) diff --git a/docs/commands.md b/docs/commands.md index a38dcf6..74ce9b0 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -12,7 +12,7 @@ arxpm init --directory ./my-project --no-pixi Effects: - creates `arxproj.toml` -- creates `src/main.arx` +- creates `src/main.x` - optionally creates/updates `pixi.toml` ## `arxpm add` diff --git a/docs/manifest.md b/docs/manifest.md index 8ddbc59..c5a9c01 100644 --- a/docs/manifest.md +++ b/docs/manifest.md @@ -11,7 +11,7 @@ version = "0.1.0" edition = "2026" [build] -entry = "src/main.arx" +entry = "src/main.x" out_dir = "build" [dependencies] diff --git a/examples/arxproj.toml b/examples/arxproj.toml index 32451a4..6426203 100644 --- a/examples/arxproj.toml +++ b/examples/arxproj.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2026" [build] -entry = "src/main.arx" +entry = "src/main.x" out_dir = "build" [dependencies] diff --git a/src/arxpm/models.py b/src/arxpm/models.py index 236c406..1524f40 100644 --- a/src/arxpm/models.py +++ b/src/arxpm/models.py @@ -48,7 +48,7 @@ class BuildConfig: type: str """ - entry: str = "src/main.arx" + entry: str = "src/main.x" out_dir: str = "build" def __post_init__(self) -> None: diff --git a/tests/test_cli.py b/tests/test_cli.py index a32c057..8594782 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -4,6 +4,7 @@ from __future__ import annotations +import tomllib from pathlib import Path import pytest @@ -52,8 +53,13 @@ def test_init_command_creates_project_files( result = runner.invoke(app, ["init", "--name", "hello-arx", "--no-pixi"]) assert result.exit_code == 0 - assert (tmp_path / "arxproj.toml").exists() - assert (tmp_path / "src" / "main.arx").exists() + manifest_path = tmp_path / "arxproj.toml" + assert manifest_path.exists() + + manifest_data = tomllib.loads(manifest_path.read_text(encoding="utf-8")) + entry = manifest_data["build"]["entry"] + assert isinstance(entry, str) + assert (tmp_path / entry).exists() def test_add_command_writes_registry_dependency( diff --git a/tests/test_manifest.py b/tests/test_manifest.py index b04fa05..de3babb 100644 --- a/tests/test_manifest.py +++ b/tests/test_manifest.py @@ -25,7 +25,7 @@ def test_manifest_round_trip(tmp_path: Path) -> None: assert loaded.project.name == "hello-arx" assert loaded.project.version == "0.1.0" - assert loaded.build.entry == "src/main.arx" + assert loaded.build.entry == "src/main.x" assert loaded.toolchain.linker == "clang" @@ -37,7 +37,7 @@ def test_manifest_parses_all_dependency_forms(tmp_path: Path) -> None: edition = "2026" [build] -entry = "src/main.arx" +entry = "src/main.x" out_dir = "build" [dependencies] @@ -69,7 +69,7 @@ def test_manifest_rejects_invalid_dependency_value(tmp_path: Path) -> None: edition = "2026" [build] -entry = "src/main.arx" +entry = "src/main.x" out_dir = "build" [dependencies] diff --git a/tests/test_project.py b/tests/test_project.py index 1199522..5f0e2b6 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -87,7 +87,7 @@ def test_build_and_run_delegate_to_pixi(tmp_path: Path) -> None: assert build_result.artifact == tmp_path / "build" / "demo" assert pixi.run_calls[0][1][:3] == [ "arx", - "src/main.arx", + "src/main.x", "--output-file", ] assert pixi.run_calls[-1][1] == ["build/demo"] From 1fdb06492a32bc074f57237d17cfcedb8d344f31 Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Thu, 12 Mar 2026 02:01:14 +0000 Subject: [PATCH 04/10] apply liner --- CHANGELOG.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b324931..a283d9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,15 @@ # Release Notes + --- # 1.0.0 (2026-03-08) - ### Bug Fixes -* Fix issues pointed by linter ([064cd65](https://github.com/arxlang/arxpm/commit/064cd6539e7baf1126ca24197d3c75c0bcee2105)) -* Fix smoke tests ([06714de](https://github.com/arxlang/arxpm/commit/06714de295bd63e2b8de6b0a91b693ff24bd8ff6)) +- Fix issues pointed by linter + ([064cd65](https://github.com/arxlang/arxpm/commit/064cd6539e7baf1126ca24197d3c75c0bcee2105)) +- Fix smoke tests + ([06714de](https://github.com/arxlang/arxpm/commit/06714de295bd63e2b8de6b0a91b693ff24bd8ff6)) # Release Notes From 1502b25f352d6c57d7ca666d1b140b5948325ed0 Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Thu, 12 Mar 2026 02:42:12 +0000 Subject: [PATCH 05/10] fix toml lib issue --- .github/workflows/main.yaml | 1 + poetry.lock | 6 +++--- pyproject.toml | 1 + src/arxpm/_toml.py | 12 ++++++++++++ src/arxpm/manifest.py | 2 +- src/arxpm/pixi.py | 2 +- tests/test_cli.py | 2 +- tests/test_pixi.py | 2 +- 8 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 src/arxpm/_toml.py diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index a2ef482..f14b8d5 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -30,6 +30,7 @@ jobs: needs: check-branch timeout-minutes: 10 strategy: + fail-fast: false matrix: python_version: - "3.10" diff --git a/poetry.lock b/poetry.lock index da30893..fd0c9a3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -4233,8 +4233,7 @@ version = "2.4.0" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "python_full_version <= \"3.11.0a6\"" +groups = ["main", "dev"] files = [ {file = "tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867"}, {file = "tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9"}, @@ -4284,6 +4283,7 @@ files = [ {file = "tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a"}, {file = "tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c"}, ] +markers = {main = "python_version == \"3.10\"", dev = "python_full_version <= \"3.11.0a6\""} [[package]] name = "tornado" @@ -4662,4 +4662,4 @@ test = ["coverage (>=5.3.1)", "prompt-toolkit (>=3.0.29)", "pygments (>=2.2)", " [metadata] lock-version = "2.1" python-versions = ">=3.10,<4" -content-hash = "d9d03c9ae9e681fd851ca49d750c562012e3539f3d17bee44bba47a2fd514228" +content-hash = "64abf142ee53fc45488c66dcd5f8456bd76070204114419900ad1cb7738512c3" diff --git a/pyproject.toml b/pyproject.toml index 2b7e699..5e7c271 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ authors = [{ name = "Ivan Ogasawara" }] dependencies = [ "typer>=0.12.3,<1.0.0", "arxlang >= 0.3.3", + "tomli>=2.0.1; python_version < '3.11'", ] [project.scripts] diff --git a/src/arxpm/_toml.py b/src/arxpm/_toml.py new file mode 100644 index 0000000..8ff934a --- /dev/null +++ b/src/arxpm/_toml.py @@ -0,0 +1,12 @@ +""" +title: TOML parser compatibility helpers. +""" + +from __future__ import annotations + +try: + import tomllib +except ModuleNotFoundError: # pragma: no cover - exercised on Python <3.11 + import tomli as tomllib # type: ignore[no-redef] + +__all__ = ["tomllib"] diff --git a/src/arxpm/manifest.py b/src/arxpm/manifest.py index 0f96418..1865e9b 100644 --- a/src/arxpm/manifest.py +++ b/src/arxpm/manifest.py @@ -5,9 +5,9 @@ from __future__ import annotations import json -import tomllib from pathlib import Path +from arxpm._toml import tomllib from arxpm.errors import ManifestError from arxpm.models import Manifest diff --git a/src/arxpm/pixi.py b/src/arxpm/pixi.py index 2fec061..32e5506 100644 --- a/src/arxpm/pixi.py +++ b/src/arxpm/pixi.py @@ -7,11 +7,11 @@ import json import re import shutil -import tomllib from collections.abc import Callable, Iterable, Mapping from pathlib import Path from typing import Any +from arxpm._toml import tomllib from arxpm.errors import ManifestError, MissingPixiError from arxpm.external import CommandResult, CommandRunner, run_command diff --git a/tests/test_cli.py b/tests/test_cli.py index 8594782..44981da 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -4,12 +4,12 @@ from __future__ import annotations -import tomllib from pathlib import Path import pytest from typer.testing import CliRunner +from arxpm._toml import tomllib from arxpm.cli import app from arxpm.doctor import DoctorCheck, DoctorReport from arxpm.errors import MissingPixiError diff --git a/tests/test_pixi.py b/tests/test_pixi.py index c04a282..1530565 100644 --- a/tests/test_pixi.py +++ b/tests/test_pixi.py @@ -4,12 +4,12 @@ from __future__ import annotations -import tomllib from collections.abc import Sequence from pathlib import Path import pytest +from arxpm._toml import tomllib from arxpm.errors import MissingPixiError from arxpm.external import CommandResult from arxpm.pixi import PixiService From 96297cbaf3ba42e053b8caf1e3142b823ecdc9b6 Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Thu, 12 Mar 2026 02:43:40 +0000 Subject: [PATCH 06/10] remove unnecessar assertion --- tests/test_cli.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 44981da..7575d4e 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -111,7 +111,6 @@ def test_run_command_omits_completion_message( result = runner.invoke(app, ["run"]) assert result.exit_code == 0 - assert "Run completed." not in result.output def test_doctor_command_reports_success( From 5dcbd071c7c7d283ba15c47bee50580abc51099a Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Thu, 12 Mar 2026 02:51:33 +0000 Subject: [PATCH 07/10] add changelog.md to prettier changelog --- .prettierignore | 1 + CHANGELOG.md | 8 +++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.prettierignore b/.prettierignore index 1cc39a6..e2eaf07 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,2 @@ docs/changelog.md +CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md index a283d9a..b324931 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,13 @@ # Release Notes - --- # 1.0.0 (2026-03-08) + ### Bug Fixes -- Fix issues pointed by linter - ([064cd65](https://github.com/arxlang/arxpm/commit/064cd6539e7baf1126ca24197d3c75c0bcee2105)) -- Fix smoke tests - ([06714de](https://github.com/arxlang/arxpm/commit/06714de295bd63e2b8de6b0a91b693ff24bd8ff6)) +* Fix issues pointed by linter ([064cd65](https://github.com/arxlang/arxpm/commit/064cd6539e7baf1126ca24197d3c75c0bcee2105)) +* Fix smoke tests ([06714de](https://github.com/arxlang/arxpm/commit/06714de295bd63e2b8de6b0a91b693ff24bd8ff6)) # Release Notes From 88813006795e8f4188c1fab896c4a0a6fb318af6 Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Thu, 12 Mar 2026 02:53:26 +0000 Subject: [PATCH 08/10] add support for py3.14 --- .github/workflows/main.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index f14b8d5..887cf12 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -37,6 +37,7 @@ jobs: - "3.11" - "3.12" - "3.13" + - "3.14" os: - "ubuntu" # - "macos" From 378a2f160025b107e09c4ffeef886da832fd9723 Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Thu, 12 Mar 2026 02:58:25 +0000 Subject: [PATCH 09/10] update docs --- README.md | 7 +++++++ docs/commands.md | 3 +++ docs/getting-started.md | 1 + docs/index.md | 7 +++++++ 4 files changed, 18 insertions(+) diff --git a/README.md b/README.md index e3447bf..73af768 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,17 @@ `arx` stays compiler-only. `arxpm` owns project manifests (`arxproj.toml`), workspace lifecycle, Pixi integration, and user-facing workflow commands. +## Compatibility + +- Python 3.10+ is supported. +- On Python 3.10, `arxpm` uses `tomli` as a compatibility fallback for + `tomllib`. + ## Architecture - `models.py`: typed manifest models. - `manifest.py`: `arxproj.toml` parsing and rendering. +- `_toml.py`: TOML parser compatibility shim (`tomllib`/`tomli`). - `pixi.py`: Pixi adapter and `pixi.toml` handling. - `project.py`: project workflows (`init`, `add`, `install`, `build`, `run`). - `doctor.py`: health checks for environment and manifest. diff --git a/docs/commands.md b/docs/commands.md index 74ce9b0..ecf43b8 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -58,6 +58,9 @@ arxpm run arxpm run --directory examples ``` +Build/compiler output and the application stdout/stderr are streamed directly; +`arxpm run` does not print an extra completion line. + ## `arxpm doctor` Report environment health and manifest status. diff --git a/docs/getting-started.md b/docs/getting-started.md index a811bd6..ae01ef6 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -4,6 +4,7 @@ This guide sets up `arxpm` for local development and smoke testing. ## Prerequisites +- Python 3.10+ - Conda or Mamba - Poetry diff --git a/docs/index.md b/docs/index.md index ebc72f1..efce0b4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -11,10 +11,17 @@ Arx projects use `arxproj.toml` as their project manifest. Python packaging is only for distributing `arxpm` itself. +## Compatibility + +- Python 3.10+ is supported. +- On Python 3.10, `arxpm` uses `tomli` as a compatibility fallback for + `tomllib`. + ## Architecture - `src/arxpm/models.py`: typed manifest models. - `src/arxpm/manifest.py`: parse/render `arxproj.toml`. +- `src/arxpm/_toml.py`: TOML parser compatibility shim (`tomllib`/`tomli`). - `src/arxpm/pixi.py`: Pixi detection and partial `pixi.toml` sync. - `src/arxpm/project.py`: `init`, `add`, `install`, `build`, `run`. - `src/arxpm/doctor.py`: environment and manifest checks. From f6e619fd4edd0b43facd3d4f9fc6106ecad0f5aa Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Thu, 12 Mar 2026 03:00:41 +0000 Subject: [PATCH 10/10] remove unnecessary comment in the docs --- docs/commands.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/commands.md b/docs/commands.md index ecf43b8..1502b57 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -59,7 +59,6 @@ arxpm run --directory examples ``` Build/compiler output and the application stdout/stderr are streamed directly; -`arxpm run` does not print an extra completion line. ## `arxpm doctor`