Skip to content
Open
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
6 changes: 3 additions & 3 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
"image": "mcr.microsoft.com/devcontainers/python:3.12",
// Features to add to the dev container. More info: https://containers.dev/features.
"features": {
"ghcr.io/devcontainers-extra/features/poetry:2": {},
"ghcr.io/devcontainers-extra/features/uv:1": {},
"ghcr.io/devcontainers-extra/features/ruff:1": {},
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "poetry install",
"postCreateCommand": "uv sync --all-groups --locked",
// Configure tool-specific properties.
"customizations": {
"vscode": {
Expand All @@ -38,4 +38,4 @@
}
},
"remoteUser": "vscode"
}
}
8 changes: 3 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,8 @@ jobs:
with:
python-version: 3.12

- name: Install dependencies
run: |
pip install poetry
poetry install
- name: Set up uv
uses: astral-sh/setup-uv@v6

- name: Build the package
run: poetry build
run: uv build
11 changes: 6 additions & 5 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ jobs:
with:
python-version: 3.12

- name: Set up uv
uses: astral-sh/setup-uv@v6

- name: Install dependencies
run: |
pip install poetry
poetry install
run: uv sync --only-group dev --locked

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the project configuration files
fd -e "pyproject.toml" -e "uv.lock" -e "requirements.txt" | head -20

Repository: tjorim/pyrail

Length of output: 39


🏁 Script executed:

#!/bin/bash
# Look at the lint.yml workflow file
cat .github/workflows/lint.yml

Repository: tjorim/pyrail

Length of output: 593


🏁 Script executed:

#!/bin/bash
# Check if there's a test.yml mentioned in the review comment
cat .github/workflows/test.yml 2>/dev/null | head -60

Repository: tjorim/pyrail

Length of output: 530


🏁 Script executed:

#!/bin/bash
# List all files in the repo to find pyproject.toml
git ls-files | grep -E "(pyproject|setup\.py|uv\.lock)" | head -20

Repository: tjorim/pyrail

Length of output: 80


🏁 Script executed:

#!/bin/bash
# List root directory
ls -la

Repository: tjorim/pyrail

Length of output: 1199


🏁 Script executed:

#!/bin/bash
# Read pyproject.toml
cat -n pyproject.toml

Repository: tjorim/pyrail

Length of output: 2061


🏁 Script executed:

#!/bin/bash
# Check if pyrail source code imports aiohttp or mashumaro
rg "(import aiohttp|from aiohttp|import mashumaro|from mashumaro)" pyrail/

Repository: tjorim/pyrail

Length of output: 263


Replace --only-group dev with --group dev to include production dependencies during type checking.

The --only-group dev flag installs only the dev group (ruff, ty) and the package itself, excluding production dependencies. However, pyrail imports runtime dependencies (aiohttp and mashumaro) in its source code. Type checking with ty requires these dependencies to be available for import resolution; without them, type checking will fail or produce incomplete results.

Use uv sync --group dev --locked instead to include both production and dev dependencies.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/lint.yml at line 26, Replace the `uv sync` invocation that
currently uses the `--only-group dev` flag with `--group dev` so production
dependencies are included during type checking; specifically update the command
string `uv sync --only-group dev --locked` to `uv sync --group dev --locked` in
the workflow step that runs the type-check/lint job.


- name: Run Ruff
run: poetry run ruff check .
run: uv run ruff check .

- name: Run ty
run: poetry run ty check .
run: uv run ty check .
48 changes: 38 additions & 10 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ jobs:
with:
python-version: 3.12

- name: Install Poetry and dependencies
run: |
pip install poetry
poetry install
- name: Set up uv
uses: astral-sh/setup-uv@v6

- name: Install dependencies
run: uv sync --locked

- name: Extract version from Git tag
id: version
Expand All @@ -31,18 +32,45 @@ jobs:

- name: Bump version in pyproject.toml
run: |
poetry version $VERSION
poetry lock
python <<'PY'
from pathlib import Path
import re
import os

version = os.environ.get("VERSION")
if not version:
raise RuntimeError("VERSION environment variable is required but not set")
# major.minor.patch with optional pre-release/build metadata
semver_pattern = r"\d+\.\d+\.\d+(?:-[0-9A-Za-z]+(?:\.[0-9A-Za-z]+)*)?(?:\+[0-9A-Za-z]+(?:\.[0-9A-Za-z]+)*)?"
if not re.fullmatch(semver_pattern, version):
raise RuntimeError(f"VERSION is not a valid semantic version: {version}")
pyproject = Path("pyproject.toml")
content = pyproject.read_text()
updated, updated_count = re.subn(
r'(?m)^version = ".*"$',
f'version = "{version}"',
content,
count=1,
)
if updated_count != 1:
raise RuntimeError(
f"Expected exactly 1 version substitution in pyproject.toml, found {updated_count}"
)
pyproject.write_text(updated)
PY

- name: Push version bump to main
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git add pyproject.toml poetry.lock
git add pyproject.toml
git commit -m "Bump version to $VERSION"
git push origin HEAD:main # Push from detached HEAD directly to main
Comment on lines 62 to 68

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Skip the commit when the version rewrite is a no-op.

If the tag already matches [project].version, git commit fails with “nothing to commit” and the release stops before uv publish runs. Guard the commit/push behind a staged-diff check.

Proposed fix
     - name: Push version bump to main
       run: |
         git config user.name "GitHub Actions"
         git config user.email "actions@github.com"
         git add pyproject.toml
-        git commit -m "Bump version to $VERSION"
-        git push origin HEAD:main  # Push from detached HEAD directly to main
+        if git diff --cached --quiet; then
+          echo "pyproject.toml already matches $VERSION; skipping commit"
+        else
+          git commit -m "Bump version to $VERSION"
+          git push origin HEAD:main  # Push from detached HEAD directly to main
+        fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Push version bump to main
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git add pyproject.toml poetry.lock
git add pyproject.toml
git commit -m "Bump version to $VERSION"
git push origin HEAD:main # Push from detached HEAD directly to main
- name: Push version bump to main
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git add pyproject.toml
if git diff --cached --quiet; then
echo "pyproject.toml already matches $VERSION; skipping commit"
else
git commit -m "Bump version to $VERSION"
git push origin HEAD:main # Push from detached HEAD directly to main
fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/publish.yml around lines 62 - 68, The workflow step "Push
version bump to main" should skip committing/pushing when there are no staged
changes; replace the unconditional git commit/git push sequence by first
checking the staged diff (e.g., using git diff --staged --quiet or git diff
--cached --quiet) and only run git commit -m "Bump version to $VERSION" and git
push origin HEAD:main if that check indicates there are changes; keep the
existing git config and git add pyproject.toml but guard the commit/push behind
the staged-diff check so the job continues when the version rewrite is a no-op.


- name: Build the package and publish to PyPI
- name: Build the package
run: uv build

- name: Publish package to PyPI
env:
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }}
run: poetry publish --build
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
run: uv publish
9 changes: 5 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ jobs:
with:
python-version: 3.12

- name: Set up uv
uses: astral-sh/setup-uv@v6

- name: Install dependencies
run: |
pip install poetry
poetry install
run: uv sync --only-group test --locked

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if pyrail declares runtime dependencies in pyproject.toml

echo "=== Checking for runtime dependencies in [project.dependencies] ==="
if grep -A 20 '^\[project\.dependencies\]' pyproject.toml | grep -v '^#' | grep -v '^\[' | grep -q '^\S'; then
  echo "✗ Runtime dependencies found:"
  grep -A 20 '^\[project\.dependencies\]' pyproject.toml | grep -v '^#' | grep -v '^\[' | grep '^\S'
  echo ""
  echo "CONCERN: --only-group test may exclude these dependencies, breaking tests."
else
  echo "✓ No runtime dependencies declared"
fi

echo ""
echo "=== Checking for runtime dependencies in [project] dependencies array ==="
python3 << 'EOF'
import tomllib
with open('pyproject.toml', 'rb') as f:
    data = tomllib.load(f)
    deps = data.get('project', {}).get('dependencies', [])
    if deps:
        print(f"✗ Runtime dependencies found: {deps}")
        print("\nCONCERN: --only-group test may exclude these dependencies, breaking tests.")
    else:
        print("✓ No runtime dependencies in dependencies array")
EOF

Repository: tjorim/pyrail

Length of output: 402


🏁 Script executed:

cat pyproject.toml

Repository: tjorim/pyrail

Length of output: 1620


🏁 Script executed:

# Check the structure of dependency groups and how they're defined
python3 << 'EOF'
import tomllib
with open('pyproject.toml', 'rb') as f:
    data = tomllib.load(f)
    
print("=== Project Dependencies (runtime) ===")
runtime_deps = data.get('project', {}).get('dependencies', [])
print(runtime_deps if runtime_deps else "None")

print("\n=== Dependency Groups ===")
dep_groups = data.get('dependency-groups', {})
if dep_groups:
    for group_name, deps in dep_groups.items():
        print(f"\n[{group_name}]")
        for dep in deps:
            print(f"  {dep}")
else:
    print("No dependency-groups found")

print("\n=== Check for [project.optional-dependencies] ===")
opt_deps = data.get('project', {}).get('optional-dependencies', {})
if opt_deps:
    for group_name, deps in opt_deps.items():
        print(f"\n[{group_name}]")
        for dep in deps:
            print(f"  {dep}")
else:
    print("None")
EOF

Repository: tjorim/pyrail

Length of output: 385


🌐 Web query:

uv sync --only-group behavior local package dependencies

💡 Result:

In uv, uv sync --only-group &lt;name&gt; installs only the dependencies from the specified dependency group(s), omitting the project itself. The semantics match --only-dev: “the project will not be included” and --only-group also excludes default groups [1][2].\n\nHowever, when your dependency graph includes local package dependencies, uv may still need to evaluate/build packages outside the selected group during resolution/lockfile creation, because it must compute dependencies transitively and/or build metadata. uv’s own maintainers and issue reports indicate that even when a group is excluded, uv can still fetch/build to compute dependencies (and those packages may be “downloaded and tried to build” even if not ultimately installed) [3][4][5].\n\nKey takeaways for “local package dependencies”:\n1) “Only-group” controls what ends up installed; it does not necessarily prevent uv from resolving/inspecting other parts of the dependency graph during lockfile/resolution.\n- uv’s docs describe exclusion of the project itself, and exclusion of default groups for --only-group [1][2].\n- Issue reports show cases where uv still evaluates or downloads/builds packages from excluded groups or sources while computing dependency information [3][5].\n\n2) If you want to avoid re-resolving/evaluating after the lockfile is created, use --frozen (or otherwise rely on an up-to-date uv.lock). Maintainers note that uv will resolve to update the lockfile unless you freeze it [6].\n\nPractical guidance:\n- If you run uv sync --only-group &lt;grp&gt; and you observe local-path dependencies getting built/evaluated that “shouldn’t be there,” that is likely part of resolution/metadata evaluation, not necessarily final installation.\n- For CI/docker-style installs where you want deterministic behavior and minimal evaluation, generate uv.lock once, then use uv sync --frozen --only-group &lt;grp&gt;.

Citations:


Use uv sync --group test --locked to include the package and its runtime dependencies.

The --only-group test flag installs only the test dependency group while omitting the project itself and its runtime dependencies. Since pyrail declares runtime dependencies (aiohttp>=3.13.5,<4.0.0 and mashumaro[orjson]>=3.21,<4.0.0), the tests will fail with import errors when the package tries to import these modules.

Change to uv sync --group test --locked to ensure both the package and its runtime dependencies are installed alongside the test dependencies.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/test.yml at line 26, The workflow currently runs "uv sync
--only-group test --locked", which installs only test dependencies and omits the
package and its runtime deps; update the GitHub Actions step to use "uv sync
--group test --locked" so the package itself and its runtime dependencies (e.g.,
aiohttp, mashumaro[orjson]) are installed alongside the test group, preventing
import errors during tests.


- name: Run tests
run: poetry run pytest
run: uv run pytest
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,33 +163,37 @@ The devcontainer setup includes all necessary dependencies and tools for develop
code .
```
3. Once VS Code opens, it will detect the devcontainer configuration and prompt you to reopen the project in a container. Click "Reopen in Container" to start the development environment.
4. To sync dependencies from the lock file, run:
```bash
uv sync --all-groups --locked
```

### Running Tests

To run the tests, use the following command in the terminal within the devcontainer:

```bash
pytest
uv run pytest
```

### Code Style

We use ruff for code formatting and linting. To check your code style, run:

```bash
ruff check .
uv run ruff check .
```

To automatically fix style issues, run:

```bash
ruff check . --fix
uv run ruff check . --fix
```

We use ty for type checking:

```bash
ty check .
uv run ty check .
```

## Logging
Expand All @@ -216,7 +220,7 @@ Contributions are welcome! Here's how you can contribute to pyRail:
### Pull Requests

1. Fork the repository and create your branch from `main`.
2. Ensure your code adheres to the project's style guidelines (run `ruff check .`).
2. Ensure your code adheres to the project's style guidelines (run `uv run ruff check .`).
3. Add or update tests as necessary.
4. Update documentation to reflect your changes.
5. Submit a pull request with a clear title and description.
Expand Down
44 changes: 22 additions & 22 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,34 +1,31 @@
[tool.poetry]
[project]
name = "pyrail"
version = "0.4.1"
description = "A Python wrapper for the iRail API"
authors = ["Jorim Tielemans <tielemans.jorim@gmail.com>"]
authors = [{name = "Jorim Tielemans", email = "tielemans.jorim@gmail.com"}]
license = "Apache-2.0"
readme = "README.md"
homepage = "https://github.com/tjorim/pyrail"
repository = "https://github.com/tjorim/pyrail"
documentation = "https://github.com/tjorim/pyrail"
keywords = ["irail", "nmbs"]
classifiers = [
"Topic :: Software Development :: Libraries :: Python Modules"
]
packages = [
{ include = "pyrail" },
urls = { Homepage = "https://github.com/tjorim/pyrail", Repository = "https://github.com/tjorim/pyrail", Documentation = "https://github.com/tjorim/pyrail" }
requires-python = ">=3.12,<4.0"
dependencies = [
"aiohttp>=3.13.5,<4.0.0",
"mashumaro[orjson]>=3.21,<4.0.0",
]

[tool.poetry.dependencies]
python = "^3.12"
aiohttp = "^3.13.5"
mashumaro = {extras = ["orjson"], version = "^3.21"}

[tool.poetry.group.test.dependencies]
pytest = ">=8.3.4,<10.0.0"
pytest-mock = "^3.14.0"
pytest-asyncio = ">=0.25.1,<1.4.0"

[tool.poetry.group.dev.dependencies]
ruff = ">=0.15.12,<0.16.0"
ty = "^0.0.35"
[dependency-groups]
test = [
"pytest>=8.3.4,<10.0.0",
"pytest-mock>=3.14.0,<4.0.0",
"pytest-asyncio>=0.25.1,<1.4.0",
]
dev = [
"ruff>=0.15.12,<0.16.0",
"ty>=0.0.35,<0.0.36",
]

[tool.ruff]
line-length = 120
Expand Down Expand Up @@ -58,6 +55,9 @@ split-on-trailing-comma = false
[tool.ruff.lint.pydocstyle]
property-decorators = ["propcache.cached_property"]

[tool.hatch.build.targets.wheel]
packages = ["pyrail"]

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
requires = ["hatchling>=1.27.0"]
build-backend = "hatchling.build"
Loading
Loading