Skip to content

Commit 6532631

Browse files
fix: typing, add pyrefly, migrate to pixi (#34)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * CI/CD updated to use pixi for dependency, build, and publish workflows; gitignore updated for pixi lock files. * **New Features** * Environment specs/base enhanced with improved identity handling, source-path management, and more reliable hashing/equality. * **Refactor** * Simplified environment base initialization and runtime attribute handling. * **Tests** * Removed legacy registry test class and associated test helpers. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 50d05de commit 6532631

6 files changed

Lines changed: 115 additions & 101 deletions

File tree

.github/workflows/release-please.yml

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,29 +22,17 @@ jobs:
2222
needs: release-please
2323
if: ${{ needs.release-please.outputs.release_created }}
2424
steps:
25-
- uses: actions/checkout@v3
25+
- uses: actions/checkout@v6
2626

27-
- uses: actions/setup-python@v2
28-
with:
29-
python-version: "3.11"
30-
31-
- name: Install poetry
32-
run: pip install poetry
33-
34-
- name: Determine dependencies
35-
run: poetry lock
27+
- name: Setup pixi
28+
uses: prefix-dev/setup-pixi@v0
3629

37-
- uses: actions/setup-python@v4
38-
with:
39-
python-version: "3.11"
40-
cache: poetry
41-
42-
- name: Install Dependencies using Poetry
30+
- name: Build source and wheel distribution + check build
4331
run: |
44-
poetry install
32+
pixi run check-build
4533
46-
- name: Publish to PyPi
47-
env:
48-
PYPI_USERNAME: __token__
49-
PYPI_PASSWORD: ${{ secrets.PYPI_TOKEN }}
50-
run: poetry publish --build --username $PYPI_USERNAME --password $PYPI_PASSWORD
34+
- name: Publish to PyPI
35+
uses: pypa/gh-action-pypi-publish@release/v1
36+
with:
37+
user: __token__
38+
password: ${{ secrets.PYPI_TOKEN }}

.github/workflows/test.yml

Lines changed: 20 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -8,62 +8,40 @@ on:
88
branches-ignore: []
99

1010
jobs:
11-
linting:
11+
formatting:
1212
runs-on: ubuntu-latest
1313
steps:
1414
- name: Check out the code
15-
uses: actions/checkout@v3
16-
17-
- uses: actions/setup-python@v4
18-
with:
19-
python-version: "3.11"
20-
21-
- name: Install poetry
22-
run: pip install poetry
23-
24-
- name: Determine dependencies
25-
run: poetry lock
15+
uses: actions/checkout@v5
2616

27-
- uses: actions/setup-python@v4
28-
with:
29-
python-version: "3.11"
30-
cache: poetry
31-
32-
- name: Install Dependencies using Poetry
33-
run: poetry install
17+
- name: Setup pixi
18+
uses: prefix-dev/setup-pixi@v0
3419

3520
- name: Check formatting
36-
run: poetry run ruff format --check .
37-
38-
- name: Lint
39-
run: poetry run ruff check .
21+
run: pixi run format --check .
4022

41-
testing:
23+
linting:
4224
runs-on: ubuntu-latest
4325
steps:
44-
- uses: actions/checkout@v3
45-
46-
- uses: actions/setup-python@v4
47-
with:
48-
python-version: "3.11"
26+
- name: Check out the code
27+
uses: actions/checkout@v5
4928

50-
- name: Install poetry
51-
run: pip install poetry
29+
- name: Setup pixi
30+
uses: prefix-dev/setup-pixi@v0
5231

53-
- name: Determine dependencies
54-
run: poetry lock
32+
- name: Check code
33+
run: pixi run lint
5534

56-
- uses: actions/setup-python@v4
57-
with:
58-
python-version: "3.11"
59-
cache: poetry
35+
testing:
36+
runs-on: ubuntu-latest
37+
steps:
38+
- uses: actions/checkout@v6
6039

61-
- name: Install dependencies
62-
run: |
63-
poetry install
40+
- name: Setup pixi
41+
uses: prefix-dev/setup-pixi@v0
6442

6543
- name: Run pytest
66-
run: poetry run coverage run -m pytest tests/tests.py
44+
run: pixi run test -v
6745

6846
- name: Run Coverage
69-
run: poetry run coverage report -m
47+
run: pixi run coverage-report

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ __pycache__/
33
*.py[cod]
44
*$py.class
55

6+
pixi.lock
7+
68
# C extensions
79
*.so
810

@@ -159,4 +161,4 @@ cython_debug/
159161
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
160162
#.idea/
161163

162-
poetry.lock
164+
poetry.lock

pyproject.toml

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,72 @@
1-
[tool.poetry]
2-
authors = ["Johannes Köster <johannes.koester@uni-due.de>"]
1+
[project]
2+
authors = [
3+
{ name = "Johannes Köster", email = "johannes.koester@uni-due.de" }
4+
]
35
description = "This package provides a stable interface for interactions between Snakemake and its software deployment plugins."
46
license = "MIT"
57
name = "snakemake-interface-software-deployment-plugins"
6-
packages = [{include = "snakemake_interface_software_deployment_plugins"}]
78
readme = "README.md"
89
version = "0.9.0"
10+
requires-python = ">=3.11,<4.0"
11+
dependencies = [
12+
"argparse-dataclass >=2.0.0,<3.0",
13+
"snakemake-interface-common >=1.17.4,<2.0.0"
14+
]
915

10-
[tool.poetry.dependencies]
11-
argparse-dataclass = "^2.0.0"
12-
python = "^3.11"
13-
snakemake-interface-common = "^1.17.4"
16+
[project.urls]
17+
repository = "https://github.com/snakemake/snakemake-interface-software-deployment-plugins"
1418

15-
[tool.poetry.group.dev.dependencies]
16-
coverage = {extras = ["toml"], version = "^6.3.1"}
17-
flake8-bugbear = "^22.1.11"
18-
pytest = "^7.0"
19-
ruff = "^0.9.9"
20-
snakemake-software-deployment-plugin-envmodules = "^0.1.2"
19+
[build-system]
20+
requires = ["hatchling"]
21+
build-backend = "hatchling.build"
22+
23+
[tool.pixi.pypi-dependencies]
24+
snakemake-interface-software-deployment-plugins = { path = ".", editable = true }
25+
26+
[tool.pixi.workspace]
27+
channels = ["conda-forge"]
28+
platforms = ["osx-arm64", "linux-64"]
29+
30+
[tool.pixi.environments]
31+
dev = { features = ["dev"] }
32+
publish = { features = ["publish"] }
33+
34+
[tool.pixi.feature.dev.dependencies]
35+
pytest = ">=8.3.5,<9"
36+
ruff = ">=0.10.0,<0.11"
37+
pytest-cov = ">=6.0.0,<7"
38+
pyrefly = ">=0.52.0,<0.53"
2139

22-
[tool.coverage.run]
23-
omit = [".*", "*/site-packages/*"]
40+
[tool.pixi.feature.dev.pypi-dependencies]
41+
snakemake-software-deployment-plugin-envmodules = ">=0.1.2,<1.0"
42+
43+
[tool.pixi.feature.dev.tasks]
44+
format = "ruff format"
45+
lint = "ruff check"
46+
typecheck = "pyrefly check"
47+
qc = { depends-on = ["format", "lint"] }
48+
coverage-report = "coverage report -m"
49+
50+
[tool.pixi.feature.dev.tasks.test]
51+
cmd = [
52+
"pytest",
53+
"--cov=snakemake_software_deployment_plugin_envmodules",
54+
"--cov-report=xml:coverage-report/coverage.xml",
55+
"--cov-report=term-missing",
56+
"tests/test_interface.py",
57+
]
2458

2559
[tool.coverage.report]
26-
fail_under = 60
60+
exclude_lines = ["pass", "\\.\\.\\."]
61+
fail_under = 63.0
2762

28-
[build-system]
29-
build-backend = "poetry.core.masonry.api"
30-
requires = ["poetry-core"]
63+
[tool.pixi.feature.publish.dependencies]
64+
twine = ">=6.1.0,<7"
65+
python-build = ">=1.2.2,<2"
66+
67+
68+
[tool.pixi.feature.publish.tasks]
69+
build = { cmd = "python -m build", description = "Build the package into the dist/ directory" }
70+
check-build = { cmd = "python -m twine check dist/*", depends-on = [
71+
"build",
72+
], description = "Check that the package can be uploaded" }

snakemake_interface_software_deployment_plugins/__init__.py

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55

66
from abc import ABC, abstractmethod
77
from copy import copy
8+
from inspect import getmodule
89
from dataclasses import dataclass, field
910
import hashlib
1011
from pathlib import Path
1112
import shutil
13+
from types import ModuleType
1214
from typing import (
1315
Any,
1416
ClassVar,
@@ -19,6 +21,7 @@
1921
Self,
2022
Tuple,
2123
Type,
24+
TypeVar,
2225
Union,
2326
)
2427
import subprocess as sp
@@ -43,16 +46,22 @@ class EnvSpecSourceFile:
4346

4447

4548
class EnvSpecBase(ABC):
49+
@classmethod
50+
def module(cls) -> ModuleType:
51+
class_module = getmodule(cls)
52+
assert class_module is not None, f"bug: cannot detect class module of {cls}"
53+
return class_module
54+
4655
def technical_init(self):
4756
"""This has to be called by Snakemake upon initialization"""
4857
self.within: Optional["EnvSpecBase"] = None
4958
self.fallback: Optional["EnvSpecBase"] = None
50-
self.kind: str = self.__class__.__module__.common_settings.provides
59+
self.kind: str = self.module().common_settings.provides
5160
self._obj_hash: Optional[int] = None
5261

5362
@classmethod
5463
def env_cls(cls):
55-
return cls.__module__.EnvBase
64+
return cls.module().EnvBase
5665

5766
@classmethod
5867
@abstractmethod
@@ -148,19 +157,11 @@ def run(self, cmd: str, **kwargs) -> sp.CompletedProcess:
148157
return sp.run([self.executable] + self.args + [self.command_arg, cmd], **kwargs)
149158

150159

160+
TSettings = TypeVar("TSettings", bound="SoftwareDeploymentSettingsBase")
161+
162+
151163
class EnvBase(ABC):
152164
_cache: ClassVar[Dict[Tuple[Type["EnvBase"], Optional["EnvBase"]], Any]] = {}
153-
spec: EnvSpecBase
154-
within: Optional["EnvBase"]
155-
settings: Optional[SoftwareDeploymentSettingsBase]
156-
shell_executable: ShellExecutable
157-
tempdir: Path
158-
_cache_prefix: Path
159-
_deployment_prefix: Path
160-
_pinfile_prefix: Path
161-
_managed_hash_store: Optional[str] = None
162-
_managed_deployment_hash_store: Optional[str] = None
163-
_obj_hash: Optional[int] = None
164165

165166
def __init__(
166167
self,
@@ -174,15 +175,18 @@ def __init__(
174175
deployment_prefix: Path,
175176
pinfile_prefix: Path,
176177
):
177-
self.spec: EnvSpecBase = spec
178-
self.within: Optional["EnvBase"] = within
178+
self.spec = spec
179+
self.within = within
179180
self.settings: Optional[SoftwareDeploymentSettingsBase] = settings
180181
self.shell_executable = shell_executable
181182
self.tempdir = tempdir
182183
self.source_cache: Path = source_cache
183184
self._deployment_prefix: Path = deployment_prefix
184185
self._cache_prefix: Path = cache_prefix
185186
self._pinfile_prefix: Path = pinfile_prefix
187+
self._managed_hash_store: Optional[str] = None
188+
self._managed_deployment_hash_store: Optional[str] = None
189+
self._obj_hash: Optional[int] = None
186190
self.__post_init__()
187191

188192
def __post_init__(self) -> None: # noqa B027

tests/tests.py renamed to tests/test_interface.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ def get_test_plugin_name(self) -> str:
2020

2121
def validate_plugin(self, plugin: PluginBase):
2222
assert plugin.settings_cls is None
23-
assert plugin.env_cls is not None
24-
assert plugin.env_spec_cls is not None
23+
assert plugin.env_cls is not None # pyrefly: ignore[missing-attribute]
24+
assert plugin.env_spec_cls is not None # pyrefly: ignore[missing-attribute]
2525

2626
def validate_settings(self, settings: SettingsBase, plugin: PluginBase):
2727
# assert isinstance(settings, plugin.settings_cls)

0 commit comments

Comments
 (0)