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
8 changes: 8 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,9 @@ Example project manifest:
name = "geometry"
version = "0.1.0"
requires-arx = ">=1.0"
dependencies = [
"sciarx>=0.0.3,<1",
]

[environment]
kind = "conda"
Expand All @@ -269,6 +272,11 @@ for example `">=1.0,<2"`. `[build-system].dependencies` declares installable
packages needed to build the project. If omitted, Arx defaults to `arxlang`; if
`requires-arx` is present, the default build dependency uses that constraint.

Use `[project].dependencies` to declare runtime Arx package dependencies.
Entries use standard dependency requirement strings, so packages may be
unconstrained (`"sciarx"`), version constrained (`"sciarx>=0.0.3,<1"`), or
direct references (`"sciarx @ ../sciarx"`).

Use `__init__.x` as the package root. Arx uses `src/` as the default source root
when `[build].src_dir` is omitted. Inside a nested module such as
`geometry.shapes.area`, use relative `from` imports for nearby modules and
Expand Down
3 changes: 1 addition & 2 deletions packages/arx/src/arx/schema/arxproject.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@
},
"dependency": {
"type": "string",
"minLength": 1,
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*( @ \\S+)?$"
"minLength": 1
},
"dependency_group_name": {
"type": "string",
Expand Down
19 changes: 9 additions & 10 deletions packages/arx/src/arx/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@
import tomli as tomllib

DEFAULT_CONFIG_FILENAME = ".arxproject.toml"
_DEPENDENCY_PATTERN = re.compile(
r"^(?P<name>[A-Za-z0-9][A-Za-z0-9._-]*)(?: @ (?P<location>\S+))?$"
)
_DEPENDENCY_GROUP_NAME_PATTERN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]*$")
_DEPENDENCY_GROUP_NORMALIZE_PATTERN = re.compile(r"[-_.]+")
_DEFAULT_SRC_DIR = "src"
Expand Down Expand Up @@ -501,7 +498,7 @@ def _reject_arxpm_sections(data: dict[str, Any]) -> None:
raise ArxProjectError(
".arxproject.toml does not support [arxpm] sections. "
"Declare dependencies in [project] using "
'dependencies = ["name", "name @ ../path"].'
'dependencies = ["name", "name>=1.0,<2", "name @ ../path"].'
)


Expand Down Expand Up @@ -530,12 +527,14 @@ def _validate_dependency(value: str, location: str) -> None:
location:
type: str
"""
if _DEPENDENCY_PATTERN.fullmatch(value) is not None:
return
raise ArxProjectError(
f".arxproject.toml {location} must be either a package name "
'like "http" or a direct reference like "mylib @ ../mylib".'
)
try:
Requirement(value)
except InvalidRequirement as err:
raise ArxProjectError(
f".arxproject.toml {location} must be a valid dependency "
'requirement like "http", "sciarx>=0.0.3,<1", or '
'"mylib @ ../mylib".'
) from err


def _validate_project(data: dict[str, Any]) -> None:
Expand Down
25 changes: 23 additions & 2 deletions packages/arx/tests/python/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
edition = "2026"
dependencies = [
"http",
"sciarx-utils>=0.0.3,<1",
"mylib @ ../mylib",
]
authors = [
Expand Down Expand Up @@ -92,6 +93,7 @@ def test_load_settings_from_text_full_example() -> None:
assert settings.project.edition == "2026"
assert settings.project.dependencies == (
"http",
"sciarx-utils>=0.0.3,<1",
"mylib @ ../mylib",
)
assert settings.project.authors[0].name == "Ivan Ogasawara"
Expand Down Expand Up @@ -278,10 +280,13 @@ def test_build_system_dependencies_reject_invalid_requirement() -> None:
"dependency",
[
"http",
"sciarx>=0.0.3",
"sciarx>=0.0.3,<1",
"sciarx==0.0.3",
"mylib @ ../mylib",
"utils @ git+https://example.com/utils.git",
],
ids=["registry", "path", "git"],
ids=["registry", "lower-bound", "bounded", "exact", "path", "git"],
)
def test_project_dependencies_support_canonical_strings(
dependency: str,
Expand All @@ -299,6 +304,19 @@ def test_project_dependencies_support_canonical_strings(
assert settings.dependency_groups == {}


def test_project_dependencies_reject_invalid_requirements() -> None:
"""
title: Reject invalid requirement strings in runtime dependencies.
"""
content = _project_toml('dependencies = ["bad dep @"]\n')

with pytest.raises(
ArxProjectError,
match=r"project\.dependencies\[0\]",
):
load_settings_from_text(content)


def test_dependency_groups_with_plain_string_entries_parse() -> None:
"""
title: Parse non-runtime dependency groups from top-level settings.
Expand Down Expand Up @@ -388,7 +406,10 @@ def test_dependency_group_entries_reuse_dependency_string_rules() -> None:
"""
).lstrip()

with pytest.raises(ArxProjectError, match="schema validation"):
with pytest.raises(
ArxProjectError,
match=r"dependency-groups\.dev\[0\]",
):
load_settings_from_text(content)


Expand Down
Loading