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
1 change: 1 addition & 0 deletions .github/workflows/update_changelog.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/bin/bash
"""Script for updating the `CHANGELOG.md` based on the commits since the latest release tag."""

import re
import subprocess
from pathlib import Path
Expand Down
7 changes: 4 additions & 3 deletions .github/workflows/validate_release_tag.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Validate that the version in the tag label matches the version of the package."""

import argparse
import ast
from pathlib import Path
Expand Down Expand Up @@ -30,9 +31,9 @@ def get_version_from_module(content: str) -> str:
parser = argparse.ArgumentParser()
parser.add_argument("GITHUB_REF", help="The GITHUB_REF environmental variable")
args = parser.parse_args()
assert args.GITHUB_REF.startswith(
"refs/tags/v"
), f'GITHUB_REF should start with "refs/tags/v": {args.GITHUB_REF}'
assert args.GITHUB_REF.startswith("refs/tags/v"), (
f'GITHUB_REF should start with "refs/tags/v": {args.GITHUB_REF}'
)
tag_version = args.GITHUB_REF[11:]
package_version = get_version_from_module(
Path("aiida_project/__init__.py").read_text(encoding="utf-8")
Expand Down
24 changes: 5 additions & 19 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,12 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
Copy link
Member

Choose a reason for hiding this comment

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

Do we still need these old hooks when we switch to Ruff? I think we can also remove the exclude above.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

These are still useful for non-python files, but it's up to you if that is worth it.

Copy link
Member

Choose a reason for hiding this comment

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

Ahh, I missed the indent. Let's leave them for now, I'll experiment with it later 👍


- repo: https://github.com/asottile/pyupgrade
rev: v3.3.1
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.13.1
hooks:
- id: pyupgrade
args: [--py37-plus]

- repo: https://github.com/PyCQA/isort
rev: 5.12.0
hooks:
- id: isort

- repo: https://github.com/psf/black
rev: 23.1.0
hooks:
- id: black

- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.252
hooks:
- id: ruff
- id: ruff-format
- id: ruff-check
args: [--fix, --exit-non-zero-on-fix]

# NOTE: mypy needs to be installed in the same environment as the package
# and all of its 3rd party dependencies.
Expand Down
7 changes: 3 additions & 4 deletions aiida_project/commands/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
import sys
from datetime import datetime
from pathlib import Path
from typing import List, Optional
from typing import Annotated, Optional

import typer
from rich import print, prompt
from typing_extensions import Annotated

from ..project import EngineType, load_project_class
from ..shell import ShellType, load_shell
Expand Down Expand Up @@ -84,12 +83,12 @@ def init(shell: Optional[ShellType] = None):


@app.command()
def create(
def create( # noqa: PLR0912
name: str,
engine: EngineType = EngineType.venv,
core_version: str = "latest",
plugins: Annotated[
List[str], typer.Option("--plugin", "-p", help="Extra plugins to install.")
list[str], typer.Option("--plugin", "-p", help="Extra plugins to install.")
] = [],
python: Annotated[
Optional[str],
Expand Down
5 changes: 3 additions & 2 deletions aiida_project/config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

from pathlib import Path
from typing import Optional

import dotenv
from pydantic_settings import BaseSettings, SettingsConfigDict
Expand All @@ -20,7 +21,7 @@ class ProjectConfig(BaseSettings):

aiida_venv_dir: Path = Path(Path.home(), ".aiida_venvs")
aiida_project_dir: Path = Path(Path.home(), "project")
aiida_default_python_path: Optional[Path] = None
aiida_default_python_path: Path | None = None
aiida_project_structure: dict = DEFAULT_PROJECT_STRUCTURE
aiida_project_shell: str = "bash"
model_config = SettingsConfigDict(
Expand Down
2 changes: 1 addition & 1 deletion aiida_project/project/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

__all__ = [
"EngineType",
"load_project_class",
"ProjectDict",
"load_project_class",
]
9 changes: 5 additions & 4 deletions aiida_project/project/core.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
from __future__ import annotations

from enum import Enum
from pathlib import Path
from typing import Dict, Type, Union

from ..config import ProjectConfig
from .base import BaseProject
from .conda import CondaProject
from .venv import VenvProject


def load_project_class(engine_type: str) -> Type[BaseProject]:
def load_project_class(engine_type: str) -> type[BaseProject]:
"""Load the project class corresponding the engine type."""
engine_project_dict = {
"venv": VenvProject,
Expand All @@ -31,7 +32,7 @@ def __init__(self):
self._projects_path.joinpath("conda").mkdir(parents=True, exist_ok=True)

@property
def projects(self) -> Dict[str, BaseProject]:
def projects(self) -> dict[str, BaseProject]:
projects = {}
for project_file in self._projects_path.glob("**/*.json"):
engine = load_project_class(str(project_file.parent.name))
Expand All @@ -44,7 +45,7 @@ def add_project(self, project: BaseProject) -> None:
with Path(self._projects_path, project.engine, f"{project.name}.json").open("w") as handle:
handle.write(project.json())

def remove_project(self, project: Union[str, BaseProject]) -> None:
def remove_project(self, project: str | BaseProject) -> None:
"""Remove a project from the configuration files."""
project = self.projects[project] if isinstance(project, str) else project
Path(self._projects_path, project.engine, f"{project.name}.json").unlink()
5 changes: 3 additions & 2 deletions aiida_project/project/venv.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import shutil
import subprocess
from pathlib import Path
from typing import ClassVar

from aiida_project.config import ProjectConfig
from aiida_project.project.base import BaseProject
Expand All @@ -11,12 +12,12 @@ class VenvProject(BaseProject):

_engine = "venv"

shell_activate_mapping: dict = {
shell_activate_mapping: ClassVar[dict[str, str]] = {
"bash": "activate",
"zsh": "activate",
"fish": "activate.fish",
}
shell_deactivate_mapping: dict = {
shell_deactivate_mapping: ClassVar[dict[str, str]] = {
"bash": "deactivate () {",
"zsh": "deactivate () {",
"fish": 'function deactivate -d "Exit virtual environment"',
Expand Down
1 change: 1 addition & 0 deletions aiida_project/shell.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Functionality to support various shells with `aiida-project`."""

from __future__ import annotations

from enum import Enum
Expand Down
32 changes: 28 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ classifiers = [
keywords = ["aiida", "workflows"]
requires-python = ">=3.9"
dependencies = [
"py~=1.11",
"pydantic~=2.7",
"pydantic-settings~=2.2",
"python-dotenv~=1.0",
Expand All @@ -48,11 +47,36 @@ dev = [
"types-pyyaml~=6.0",
]

[dependency-groups]
Copy link
Collaborator Author

@danielhollas danielhollas Sep 30, 2025

Choose a reason for hiding this comment

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

This makes development with uv easier since uv installs dev dependency group by default.

So e.g. one can run uv run pytest straight after cloning a repo and it will work.

For a general info about the (new) concept of "dependency groups" see PEP-735.

In principle this should obviate the need for the dev extras. The only disadvantage is that the dependency group is a very new thing and would not work with older pip (or other installation managers) versions.

(feel free to split this comment into a separate commit :-) )

Copy link
Member

Choose a reason for hiding this comment

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

Haha, well, if you insist! 😉

Thanks for the note on "dependency groups"! Always good to read a PEP in the morning.

dev = [
"aiida-project[dev]",
]

[tool.mypy]
plugins = ['pydantic.mypy']

[tool.black]
line-length = 100

[tool.ruff]
line-length = 100

[tool.ruff.lint]
# TODO: PLW1510 should be enabled and fixed!
# See `ruff rule PLW1510`
ignore = [
'PLW2901', # `for` loop variable overwritten by assignment target
'PLW1510', # `subprocess.run` without explicit `check` argument
'PLC0415', # `import` should be at the top-level of a file
]

select = [
Copy link
Member

Choose a reason for hiding this comment

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

Heh, I actually didn't know you had to select rules, since I'm used to using Ruff through hatch, which automatically selects a bunch of rules in its default settings.

'E', # pydocstyle
'W', # pydocstyle
'F', # pyflakes
'I', # isort
'N', # pep8-naming
'UP', # pyupgrade
'PLC', # pylint-convention
'PLE', # pylint-error
'PLR', # pylint-refactor
'PLW', # pylint-warning
'RUF', # ruff-specific rules
]