Skip to content
Closed
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
2 changes: 1 addition & 1 deletion .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:

strategy:
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12']
python-version: ['3.10', '3.11', '3.12', '3.14']

services:
rabbitmq:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- name: Install Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
python-version: '3.11'
cache: pip
cache-dependency-path: pyproject.toml

Expand All @@ -31,7 +31,7 @@ jobs:

strategy:
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12']
python-version: ['3.10', '3.11', '3.12', '3.14']

services:
rabbitmq:
Expand Down
5 changes: 3 additions & 2 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 All @@ -12,7 +13,7 @@ def get_version_from_module(content: str) -> str:
try:
module = ast.parse(content)
except SyntaxError as exception:
raise IOError('Unable to parse module.') from exception
raise OSError('Unable to parse module.') from exception

try:
return next(
Expand All @@ -23,7 +24,7 @@ def get_version_from_module(content: str) -> str:
if isinstance(target, ast.Name) and target.id == '__version__'
)
except StopIteration as exception:
raise IOError('Unable to find the `__version__` attribute in the module.') from exception
raise OSError('Unable to find the `__version__` attribute in the module.') from exception


if __name__ == '__main__':
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ repos:
- id: flynt

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.3
rev: v0.8.6
hooks:
- id: ruff-format
- id: ruff
Expand Down
1 change: 1 addition & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
This file only contains a selection of the most common options. For a full list see the documentation:
https://www.sphinx-doc.org/en/master/usage/configuration.html
"""

import aiida_shell

project = 'aiida-shell'
Expand Down
2 changes: 2 additions & 0 deletions docs/source/examples/include/scripts/gromacs.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#!/usr/bin/env runaiida
"""Simulation of lysozyme protein dynamics using GROMACS."""

import urllib.request

from aiida import engine, orm

from aiida_shell import launch_shell_job

# Download the lysozyme protein structure.
Expand Down
2 changes: 2 additions & 0 deletions docs/source/examples/include/scripts/lammps.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#!/usr/bin/env runaiida
"""Simulation of Lennard-Jones fluid using LAMMPS."""

from aiida import orm

from aiida_shell import launch_shell_job

script = """
Expand Down
2 changes: 2 additions & 0 deletions docs/source/examples/include/scripts/qe.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#!/usr/bin/env runaiida
"""Simulation of electronic band structure of GaAs using Quantum ESPRESSO."""

import urllib.request

from aiida import engine, orm

from aiida_shell import launch_shell_job

# Generate a folder with the required pseudopotentials
Expand Down
11 changes: 6 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,23 @@ classifiers = [
'Operating System :: POSIX :: Linux',
'Operating System :: MacOS :: MacOS X',
'Programming Language :: Python',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
'Programming Language :: Python :: 3.14',
'Topic :: Scientific/Engineering'
]
dependencies = [
'aiida-core~=2.6,>=2.6.1',
"aiida-core==2.9.0rc0",
'dill'
]
dynamic = ['description', 'version']
keywords = ['aiida', 'workflows']
license = {file = 'LICENSE.txt'}
name = 'aiida-shell'
readme = 'README.md'
requires-python = '>=3.9'
requires-python = '>=3.10'

[project.entry-points.'aiida.calculations']
'core.shell' = 'aiida_shell.calculations.shell:ShellJob'
Expand All @@ -43,9 +44,9 @@ requires-python = '>=3.9'

[project.optional-dependencies]
dev = [
'mypy==1.6.1',
'mypy~=2.3.0',
'pre-commit',
'pytest~=6.2',
'pytest~=8.4',
'pytest-regressions'
]
docs = [
Expand Down
5 changes: 3 additions & 2 deletions src/aiida_shell/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""AiiDA plugin that makes running shell commands easy."""

__version__ = '0.8.2'

from .calculations import ShellJob
Expand All @@ -7,10 +8,10 @@
from .parsers import ShellParser

__all__ = (
'ShellJob',
'EntryPointData',
'PickledData',
'ShellCode',
'launch_shell_job',
'ShellJob',
'ShellParser',
'launch_shell_job',
)
1 change: 1 addition & 0 deletions src/aiida_shell/calculations/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Module for :mod:`aiida_shell.calculations`."""

from .shell import ShellJob

__all__ = ('ShellJob',)
1 change: 1 addition & 0 deletions src/aiida_shell/calculations/shell.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Implementation of :class:`aiida.engine.CalcJob` to make it easy to run an arbitrary shell command on a computer."""

from __future__ import annotations

import inspect
Expand Down
1 change: 1 addition & 0 deletions src/aiida_shell/data/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Module for :mod:`aiida_shell.data`."""

from .code import ShellCode
from .entry_point import EntryPointData
from .pickled import PickledData
Expand Down
1 change: 1 addition & 0 deletions src/aiida_shell/data/code.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Code that represents a shell command."""

from __future__ import annotations

import typing as t
Expand Down
3 changes: 2 additions & 1 deletion src/aiida_shell/data/entry_point.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
"""Data plugin to store a reference to an entry point."""

from __future__ import annotations

import typing as t
from importlib.metadata import EntryPoint

from aiida.common.exceptions import EntryPointError
from aiida.common.lang import type_check
from aiida.common.log import AIIDA_LOGGER
from aiida.orm import Data
from aiida.plugins.entry_point import get_entry_point
from aiida.plugins.utils import PluginVersionProvider
from importlib_metadata import EntryPoint

__all__ = ('EntryPointData',)

Expand Down
1 change: 1 addition & 0 deletions src/aiida_shell/data/pickled.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Data plugin to store (almost) any Python object by pickling it."""

from __future__ import annotations

import importlib.metadata
Expand Down
1 change: 1 addition & 0 deletions src/aiida_shell/launch.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Convenience wrapper function to simplify the interface to launch a :class:`aiida_shell.ShellJob` job."""

from __future__ import annotations

import logging
Expand Down
1 change: 1 addition & 0 deletions src/aiida_shell/parsers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Module for :mod:`aiida_shell.parsers`."""

from .shell import ShellParser

__all__ = ('ShellParser',)
1 change: 1 addition & 0 deletions src/aiida_shell/parsers/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
# For further information please visit http://www.aiida.net #
###########################################################################
"""Parser for a :class:`aiida_shell.ShellJob` job."""

from __future__ import annotations

import pathlib
Expand Down
26 changes: 16 additions & 10 deletions tests/calculations/test_shell.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
"""Tests for the :mod:`aiida_shell.calculations.shell` module."""

import pathlib
import shlex

import pytest
from aiida.common.datastructures import CodeInfo
from aiida.engine import run_get_node
from aiida.orm import Data, Float, FolderData, Int, List, RemoteData, SinglefileData, Str
from aiida.orm import Data, Float, FolderData, Int, List, Log, RemoteData, SinglefileData, Str

from aiida_shell.calculations.shell import ShellJob
from aiida_shell.data import EntryPointData, PickledData

Expand Down Expand Up @@ -90,7 +92,7 @@ def test_nodes_folder_data(generate_calc_job, generate_code, tmp_path):
def test_nodes_remote_data(generate_calc_job, generate_code, tmp_path, aiida_localhost, use_symlinks):
"""Test the ``nodes`` input with ``RemoteData`` nodes."""
inputs = {
'code': generate_code(),
'code': generate_code(computer_label=aiida_localhost.label),
'arguments': [],
'nodes': {
'remote': RemoteData(remote_path=str(tmp_path.absolute()), computer=aiida_localhost),
Expand Down Expand Up @@ -119,7 +121,7 @@ def test_nodes_remote_data_filename(generate_calc_job, generate_code, tmp_path,
remote_data_b = RemoteData(remote_path=str(remote_path_b.absolute()), computer=aiida_localhost)

inputs = {
'code': generate_code(),
'code': generate_code(computer_label=aiida_localhost.label),
'arguments': ['{remote_a}'],
'nodes': {
'remote_a': remote_data_a,
Expand Down Expand Up @@ -451,7 +453,7 @@ def parser(dirpath):
assert node.outputs.string == value


def test_input_output_filename_overlap(generate_calc_job, generate_code, tmp_path, caplog):
def test_input_output_filename_overlap(generate_calc_job, generate_code, tmp_path):
"""Test functionality when input and output filenames overlap."""
code = generate_code()

Expand Down Expand Up @@ -479,18 +481,22 @@ def test_input_output_filename_overlap(generate_calc_job, generate_code, tmp_pat

# If the filename clash is due to an "implicit" filename, instead of raising, the filename of the node should be
# automatically made unique and a warning logged to make the user aware.
dirpath, calc_info = generate_calc_job(
'core.shell',
inputs={
def generate_inputs():
return {
'code': code,
'nodes': {'file': SinglefileData.from_string('content', filename='stdout')},
},
)
}

dirpath, calc_info = generate_calc_job('core.shell', inputs=generate_inputs())
process = generate_calc_job('core.shell', inputs=generate_inputs(), return_process=True)

code_info = calc_info.codes_info[0]
filenames = [p.name for p in dirpath.iterdir()]
assert code_info.stdout_name not in filenames
assert code_info.stderr_name not in filenames
assert 'filename `stdout` for node `file` overlaps' in caplog.records[0].message

message = 'filename `stdout` for node `file` overlaps'
assert any(message in entry.message for entry in Log.collection.get_logs_for(process.node))

# If the contents of a ``FolderData`` overlap with a reserved filename, an exception is raised. This is done because
# not doing everything will most likely fail the calculation as some input files will be overwritten. The plugin can
Expand Down
4 changes: 3 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Module with test fixtures."""

from __future__ import annotations

import collections
Expand All @@ -18,6 +19,7 @@
from aiida.manage.manager import get_manager
from aiida.orm import CalcJobNode, Computer, FolderData
from aiida.plugins import CalculationFactory, ParserFactory

from aiida_shell import ShellCode

pytest_plugins = 'aiida.tools.pytest_fixtures'
Expand Down Expand Up @@ -86,7 +88,7 @@ def factory(
manager = get_manager()
runner = manager.get_runner()

process_class: t.Type['CalcJob'] = CalculationFactory(entry_point_name) # type: ignore[assignment]
process_class: t.Type[CalcJob] = CalculationFactory(entry_point_name) # type: ignore[assignment]
process: CalcJob = instantiate_process(runner, process_class, **inputs or {}) # type: ignore[assignment]

if presubmit:
Expand Down
2 changes: 2 additions & 0 deletions tests/data/test_code.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Tests for :mod:`aiida_shell.data.code`."""

import pytest

from aiida_shell.data.code import ShellCode


Expand Down
5 changes: 4 additions & 1 deletion tests/data/test_entry_point.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
"""Tests for the :mod:`aiida_shell.data.entry_point` module."""

from importlib.metadata import EntryPoint

import pytest
from aiida.orm import load_node
from aiida.plugins.entry_point import get_entry_point

from aiida_shell.data.entry_point import EntryPointData
from importlib_metadata import EntryPoint

InvalidEntryPoint = EntryPoint('b', 'a', group='c')
InconsistentEntryPoint = EntryPoint('b', 'aiida_shell.data.pickled:PickledData', group='c')
Expand Down
2 changes: 2 additions & 0 deletions tests/data/test_pickled.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"""Tests for the :mod:`aiida_shell.data.pickled` module."""

import dill
import pytest
from aiida.orm import Node, load_node

from aiida_shell.data.pickled import PickledData


Expand Down
2 changes: 2 additions & 0 deletions tests/parsers/test_shell.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
"""Tests for the :mod:`aiida_shell.parsers.shell` module."""

import copy
import pathlib

import pytest
from aiida.orm import FolderData, List, SinglefileData

from aiida_shell.calculations.shell import ShellJob


Expand Down
2 changes: 2 additions & 0 deletions tests/test_launch.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Tests for the :mod:`aiida_shell.launch` module."""

import datetime
import json
import pathlib
Expand All @@ -7,6 +8,7 @@
import pytest
from aiida.engine import WorkChain, run_get_node, workfunction
from aiida.orm import AbstractCode, Computer, Float, Int, RemoteData, SinglefileData, Str

from aiida_shell.calculations.shell import ShellJob
from aiida_shell.launch import launch_shell_job, prepare_computer

Expand Down
4 changes: 3 additions & 1 deletion tests/test_version.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"""Tests for the :mod:`aiida_shell` module."""
import aiida_shell

from packaging.version import Version, parse

import aiida_shell


def test_version():
"""Test that :attr:`aiida_shell.__version__` is a PEP-440 compatible version identifier."""
Expand Down
Loading