From afc23c983adbe6aae96be324788572cb19d4e4e4 Mon Sep 17 00:00:00 2001 From: Alexander Goscinski Date: Wed, 24 Jul 2024 14:45:41 +0200 Subject: [PATCH 1/7] update cookiecutter --- .github/workflows/ci.yml | 50 ++---- .github/workflows/publish-on-pypi.yml | 16 +- .pre-commit-config.yaml | 39 +--- conftest.py | 9 +- docs/source/conf.py | 81 ++++----- examples/example_01.py | 39 ++-- pyproject.toml | 167 ++++++++++-------- {aiida_diff => src/aiida_diff}/__init__.py | 2 +- .../aiida_diff}/calculations.py | 35 ++-- {aiida_diff => src/aiida_diff}/cli.py | 26 +-- .../aiida_diff}/data/__init__.py | 21 ++- {aiida_diff => src/aiida_diff}/helpers.py | 15 +- {aiida_diff => src/aiida_diff}/parsers.py | 15 +- tests/__init__.py | 3 +- tests/test_calculations.py | 31 ++-- tests/test_cli.py | 15 +- 16 files changed, 268 insertions(+), 296 deletions(-) rename {aiida_diff => src/aiida_diff}/__init__.py (84%) rename {aiida_diff => src/aiida_diff}/calculations.py (70%) rename {aiida_diff => src/aiida_diff}/cli.py (74%) rename {aiida_diff => src/aiida_diff}/data/__init__.py (87%) rename {aiida_diff => src/aiida_diff}/helpers.py (88%) rename {aiida_diff => src/aiida_diff}/parsers.py (78%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 969f876..97b31c6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ jobs: timeout-minutes: 30 strategy: matrix: - python-version: ['3.10'] + python-version: ['3.12'] aiida-version: ['stable'] services: @@ -32,54 +32,40 @@ jobs: - 5672:5672 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - - name: Install python dependencies - run: | - pip install --upgrade pip - pip install -e .[testing] - - name: Run test suite env: - # show timings of tests PYTEST_ADDOPTS: "--durations=0" - run: pytest --cov aiida_diff --cov-append . + run: | + hatch test --cover docs: runs-on: ubuntu-latest timeout-minutes: 15 steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.10 - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 with: - python-version: "3.10" - - name: Install python dependencies - run: | - pip install --upgrade pip - pip install -e .[docs] + python-version: ${{ matrix.python-version }} - name: Build docs - run: cd docs && make + run: | + hatch run docs:build - pre-commit: + hatch: runs-on: ubuntu-latest timeout-minutes: 15 steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.10 - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 with: - python-version: "3.10" - - name: Install python dependencies - run: | - pip install --upgrade pip - pip install -e .[pre-commit,docs,testing] - - name: Run pre-commit + python-version: ${{ matrix.python-version }} + - name: Run formatter and linter run: | - pre-commit install - pre-commit run --all-files || ( git status --short ; git diff ; exit 1 ) + hatch fmt --check diff --git a/.github/workflows/publish-on-pypi.yml b/.github/workflows/publish-on-pypi.yml index 77a8d39..cf29648 100644 --- a/.github/workflows/publish-on-pypi.yml +++ b/.github/workflows/publish-on-pypi.yml @@ -15,19 +15,19 @@ jobs: - name: Checkout repository uses: actions/checkout@v2 - - name: Set up Python 3.10 - uses: actions/setup-python@v1 + - name: Set up Python 3.12 + uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.12' - - name: Install flit + - name: Install hatch run: | python -m pip install --upgrade pip - python -m pip install flit~=3.4 + python -m pip install hatch~=1.12.0 - name: Build and publish run: | - flit publish + hatch publish env: - FLIT_USERNAME: __token__ - FLIT_PASSWORD: ${{ secrets.pypi_token }} + HATCH_INDEX_USER: __token__ + HATCH_INDEX_AUTH: ${{ secrets.pypi_token }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ad3236b..46dbcb7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,39 +1,8 @@ -# Install pre-commit hooks via: -# pre-commit install repos: -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 - hooks: - - id: end-of-file-fixer - - id: mixed-line-ending - - id: trailing-whitespace - - id: check-json - -- repo: https://github.com/asottile/pyupgrade - rev: v3.3.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: 22.12.0 - hooks: - - id: black - - repo: local hooks: - - id: pylint + - id: fmt + name: fmt + entry: hatch fmt language: system - types: [file, python] - name: pylint - description: "This hook runs the pylint static code analyzer" - exclude: &exclude_files > - (?x)^( - docs/.*| - )$ - entry: pylint + types: [python] diff --git a/conftest.py b/conftest.py index 19937db..7d76365 100644 --- a/conftest.py +++ b/conftest.py @@ -1,15 +1,16 @@ """pytest fixtures for simplified testing.""" + import pytest -pytest_plugins = ["aiida.manage.tests.pytest_fixtures"] +pytest_plugins = ['aiida.manage.tests.pytest_fixtures'] -@pytest.fixture(scope="function", autouse=True) +@pytest.fixture(scope='function', autouse=True) def clear_database_auto(clear_database): # pylint: disable=unused-argument """Automatically clear database in between tests.""" -@pytest.fixture(scope="function") +@pytest.fixture(scope='function') def diff_code(aiida_local_code_factory): """Get a diff code.""" - return aiida_local_code_factory(executable="diff", entry_point="diff") + return aiida_local_code_factory(executable='diff', entry_point='diff') diff --git a/docs/source/conf.py b/docs/source/conf.py index 28e15a2..8509287 100755 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -14,15 +14,14 @@ import sys import time +import aiida_diff from aiida import load_profile from aiida.storage.sqlite_temp import SqliteTempBackend -import aiida_diff - # -- AiiDA-related setup -------------------------------------------------- # Load AiiDA profile -temp_profile = SqliteTempBackend.create_profile("temp-profile") +temp_profile = SqliteTempBackend.create_profile('temp-profile') load_profile(temp_profile, allow_switch=True) # -- General configuration ------------------------------------------------ @@ -35,45 +34,43 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - "sphinx.ext.autodoc", - "sphinx.ext.mathjax", - "sphinx.ext.intersphinx", - "sphinx.ext.viewcode", - "sphinxcontrib.contentui", - "aiida.sphinxext", + 'sphinx.ext.autodoc', + 'sphinx.ext.mathjax', + 'sphinx.ext.intersphinx', + 'sphinx.ext.viewcode', + 'sphinxcontrib.contentui', + 'aiida.sphinxext', ] intersphinx_mapping = { - "python": ("https://docs.python.org/3", None), - "aiida": ("https://aiida.readthedocs.io/projects/aiida-core/en/latest", None), + 'python': ('https://docs.python.org/3', None), + 'aiida': ('https://aiida.readthedocs.io/projects/aiida-core/en/latest', None), } # Add any paths that contain templates here, relative to this directory. -templates_path = ["_templates"] +templates_path = ['_templates'] # The suffix of source filenames. -source_suffix = ".rst" +source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. # ~ master_doc = 'index' -master_doc = "index" +master_doc = 'index' # General information about the project. -project = "aiida-diff" -copyright_first_year = "2024" -copyright_owners = "The AiiDA Team" +project = 'aiida-diff' +copyright_first_year = '2024' +copyright_owners = 'The AiiDA Team' current_year = str(time.localtime().tm_year) copyright_year_string = ( - current_year - if current_year == copyright_first_year - else f"{copyright_first_year}-{current_year}" + current_year if current_year == copyright_first_year else f'{copyright_first_year}-{current_year}' ) # pylint: disable=redefined-builtin -copyright = f"{copyright_year_string}, {copyright_owners}. All rights reserved" +copyright = f'{copyright_year_string}, {copyright_owners}. All rights reserved' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -82,14 +79,14 @@ # The full version, including alpha/beta/rc tags. release = aiida_diff.__version__ # The short X.Y version. -version = ".".join(release.split(".")[:2]) +version = '.'.join(release.split('.')[:2]) # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = "en" +language = 'en' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: @@ -107,13 +104,13 @@ show_authors = True # The name of the Pygments (syntax highlighting) style to use. -pygments_style = "sphinx" +pygments_style = 'sphinx' # -- Options for HTML output ---------------------------------------------- -html_theme = "furo" -html_logo = "images/AiiDA_transparent_logo.png" -html_title = f"aiida-diff v{release}" +html_theme = 'furo' +html_logo = 'images/AiiDA_transparent_logo.png' +html_title = f'aiida-diff v{release}' html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. @@ -170,7 +167,7 @@ # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -html_use_opensearch = "https://aiida-diff.readthedocs.io" +html_use_opensearch = 'https://aiida-diff.readthedocs.io' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None @@ -179,12 +176,12 @@ # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' -html_search_language = "en" +html_search_language = 'en' # Warnings to ignore when using the -n (nitpicky) option # We should ignore any python built-in exception, for instance nitpick_ignore = [ - ("py:class", "Logger"), + ('py:class', 'Logger'), ] @@ -197,8 +194,8 @@ def run_apidoc(_): See also https://github.com/rtfd/readthedocs.org/issues/1139 """ source_dir = os.path.abspath(os.path.dirname(__file__)) - apidoc_dir = os.path.join(source_dir, "apidoc") - package_dir = os.path.join(source_dir, os.pardir, os.pardir, "aiida_diff") + apidoc_dir = os.path.join(source_dir, 'apidoc') + package_dir = os.path.join(source_dir, os.pardir, os.pardir, 'src', 'aiida_diff') # In #1139, they suggest the route below, but this ended up # calling sphinx-build, not sphinx-apidoc @@ -207,27 +204,25 @@ def run_apidoc(_): import subprocess - cmd_path = "sphinx-apidoc" - if hasattr(sys, "real_prefix"): # Check to see if we are in a virtualenv + cmd_path = 'sphinx-apidoc' + if hasattr(sys, 'real_prefix'): # Check to see if we are in a virtualenv # If we are, assemble the path manually - cmd_path = os.path.abspath(os.path.join(sys.prefix, "bin", "sphinx-apidoc")) + cmd_path = os.path.abspath(os.path.join(sys.prefix, 'bin', 'sphinx-apidoc')) options = [ - "-o", + '-o', apidoc_dir, package_dir, - "--private", - "--force", - "--no-toc", + '--private', + '--force', + '--no-toc', ] # See https://stackoverflow.com/a/30144019 env = os.environ.copy() - env[ - "SPHINX_APIDOC_OPTIONS" - ] = "members,special-members,private-members,undoc-members,show-inheritance" + env['SPHINX_APIDOC_OPTIONS'] = 'members,special-members,private-members,undoc-members,show-inheritance' subprocess.check_call([cmd_path] + options, env=env) def setup(app): - app.connect("builder-inited", run_apidoc) + app.connect('builder-inited', run_apidoc) diff --git a/examples/example_01.py b/examples/example_01.py index b71881b..b11401d 100644 --- a/examples/example_01.py +++ b/examples/example_01.py @@ -3,16 +3,15 @@ Usage: ./example_01.py """ + from os import path import click - from aiida import cmdline, engine from aiida.plugins import CalculationFactory, DataFactory - from aiida_diff import helpers -INPUT_DIR = path.join(path.dirname(path.realpath(__file__)), "input_files") +INPUT_DIR = path.join(path.dirname(path.realpath(__file__)), 'input_files') def test_run(diff_code): @@ -23,34 +22,36 @@ def test_run(diff_code): if not diff_code: # get code computer = helpers.get_computer() - diff_code = helpers.get_code(entry_point="diff", computer=computer) + diff_code = helpers.get_code( + entry_point='diff', computer=computer + ) # Prepare input parameters - DiffParameters = DataFactory("diff") - parameters = DiffParameters({"ignore-case": True}) + diff_parameters = DataFactory('diff') + parameters = diff_parameters({'ignore-case': True}) - SinglefileData = DataFactory("core.singlefile") - file1 = SinglefileData(file=path.join(INPUT_DIR, "file1.txt")) - file2 = SinglefileData(file=path.join(INPUT_DIR, "file2.txt")) + singlefile_data = DataFactory('core.singlefile') + file1 = singlefile_data(file=path.join(INPUT_DIR, 'file1.txt')) + file2 = singlefile_data(file=path.join(INPUT_DIR, 'file2.txt')) # set up calculation inputs = { - "code": diff_code, - "parameters": parameters, - "file1": file1, - "file2": file2, - "metadata": { - "description": "Test job submission with the aiida_diff plugin", + 'code': diff_code, + 'parameters': parameters, + 'file1': file1, + 'file2': file2, + 'metadata': { + 'description': 'Test job submission with the aiida_diff plugin', }, } # Note: in order to submit your calculation to the aiida daemon, do: # from aiida.engine import submit # future = submit(CalculationFactory('diff'), **inputs) - result = engine.run(CalculationFactory("diff"), **inputs) + result = engine.run(CalculationFactory('diff'), **inputs) - computed_diff = result["diff"].get_content() - print(f"Computed diff between files: \n{computed_diff}") + computed_diff = result['diff'].get_content() + print(f'Computed diff between files: \n{computed_diff}') @click.command() @@ -68,5 +69,5 @@ def cli(code): test_run(code) -if __name__ == "__main__": +if __name__ == '__main__': cli() # pylint: disable=no-value-for-parameter diff --git a/pyproject.toml b/pyproject.toml index 39a9cdb..bbe388d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,12 +1,11 @@ [build-system] -# build the package with [flit](https://flit.readthedocs.io) -requires = ["flit_core >=3.4,<4"] -build-backend = "flit_core.buildapi" +requires = ["hatchling"] +build-backend = "hatchling.build" [project] # See https://www.python.org/dev/peps/pep-0621/ name = "aiida-diff" -dynamic = ["version"] # read from aiida_diff/__init__.py +dynamic = ["version"] # read from aiida_diff/src/__init__.py description = "AiiDA demo plugin that wraps the `diff` executable for computing the difference between two files." authors = [{name = "The AiiDA Team"}] readme = "README.md" @@ -20,35 +19,20 @@ classifiers = [ "Framework :: AiiDA" ] keywords = ["aiida", "plugin"] -requires-python = ">=3.7" +requires-python = ">=3.9" dependencies = [ "aiida-core>=2.5,<3", "voluptuous" ] -[project.urls] -Source = "https://github.com/aiidateam/aiida-diff" - [project.optional-dependencies] -testing = [ - "pgtest~=1.3.1", - "wheel~=0.31", - "coverage[toml]", - "pytest~=6.0", - "pytest-cov" -] pre-commit = [ - "pre-commit~=2.2", - "pylint~=2.15.10" -] -docs = [ - "sphinx", - "sphinxcontrib-contentui", - "sphinxcontrib-details-directive", - "furo", - "markupsafe<2.1" + 'pre-commit~=3.5', ] +[project.urls] +Source = "https://github.com/aiidateam/aiida-diff" + [project.entry-points."aiida.data"] "diff" = "aiida_diff.data:DiffParameters" @@ -61,22 +45,10 @@ docs = [ [project.entry-points."aiida.cmdline.data"] "diff" = "aiida_diff.cli:data_cli" -[tool.flit.module] -name = "aiida_diff" - -[tool.pylint.format] -max-line-length = 125 - -[tool.pylint.messages_control] -disable = [ - "too-many-ancestors", - "invalid-name", - "duplicate-code", -] - [tool.pytest.ini_options] # Configuration for [pytest](https://docs.pytest.org) python_files = "test_*.py example_*.py" +addopts = "--pdbcls=IPython.terminal.debugger:TerminalPdb" filterwarnings = [ "ignore::DeprecationWarning:aiida:", "ignore:Creating AiiDA configuration folder:", @@ -84,39 +56,94 @@ filterwarnings = [ "ignore::DeprecationWarning:yaml:", ] + [tool.coverage.run] # Configuration of [coverage.py](https://coverage.readthedocs.io) # reporting which lines of your plugin are covered by tests -source=["aiida_diff"] - -[tool.isort] -# Configuration of [isort](https://isort.readthedocs.io) -line_length = 120 -force_sort_within_sections = true -sections = ['FUTURE', 'STDLIB', 'THIRDPARTY', 'AIIDA', 'FIRSTPARTY', 'LOCALFOLDER'] -known_aiida = ['aiida'] - -[tool.tox] -legacy_tox_ini = """ -[tox] -envlist = py38 - -[testenv] -usedevelop=True - -[testenv:py{37,38,39,310}] -description = Run the test suite against a python version -extras = testing -commands = pytest {posargs} - -[testenv:pre-commit] -description = Run the pre-commit checks -extras = pre-commit -commands = pre-commit run {posargs} - -[testenv:docs] -description = Build the documentation -extras = docs -commands = sphinx-build -nW --keep-going -b html {posargs} docs/source docs/build/html -commands_post = echo "open file://{toxinidir}/docs/build/html/index.html" -""" +source = ["src/aiida_diff"] + +[tool.ruff] +line-length = 120 + +[tool.ruff.format] +quote-style = 'single' + +[tool.ruff.lint] +ignore = [ + 'F403', # Star imports unable to detect undefined names + 'F405', # Import may be undefined or defined from star imports + 'PLR0911', # Too many return statements + 'PLR0912', # Too many branches + 'PLR0913', # Too many arguments in function definition + 'PLR0915', # Too many statements + 'PLR2004', # Magic value used in comparison + 'RUF005', # Consider iterable unpacking instead of concatenation + 'RUF012' # Mutable class attributes should be annotated with `typing.ClassVar` +] +select = [ + 'E', # pydocstyle + 'W', # pydocstyle + 'F', # pyflakes + 'I', # isort + 'N', # pep8-naming + 'PLC', # pylint-convention + 'PLE', # pylint-error + 'PLR', # pylint-refactor + 'PLW', # pylint-warning + 'RUF' # ruff +] + +## Hatch configurations + +[tool.hatch.version] +path = "src/aiida_diff/__init__.py" + +[tool.hatch.envs.hatch-test] +dependencies = [ + 'pgtest~=1.3,>=1.3.1', + 'coverage~=7.0', + 'pytest~=7.0', + "pytest-cov~=4.1", + "ipdb" +] + +[tool.hatch.envs.hatch-test.scripts] +# These are the efault scripts provided by hatch. +# The have been copied to make the execution more transparent + +# This command is run with the command `hatch test` +run = "pytest{env:HATCH_TEST_ARGS:} {args}" +# The three commands below are run with the command `hatch test --coverage` +run-cov = "coverage run -m pytest{env:HATCH_TEST_ARGS:} {args}" +cov-combine = "coverage combine" +cov-report = "coverage report" + +[[tool.hatch.envs.hatch-test.matrix]] +python = ["3.9", "3.10", "3.11", "3.12"] + +[tool.hatch.envs.hatch-static-analysis] +dependencies = ["ruff==0.4.3"] + +[tool.hatch.envs.hatch-static-analysis.scripts] +# Fixes are executed with `hatch fmt`. +# Checks are executed with `hatch fmt --check`. + +format-check = "ruff format --check --config pyproject.toml {args:.}" +format-fix = "ruff format --config pyproject.toml {args:.}" +lint-check = "ruff check --config pyproject.toml {args:.}" +lint-fix = "ruff check --config pyproject.toml --fix --exit-non-zero-on-fix --show-fixes {args:.}" + +[tool.hatch.envs.docs] +description = "Build the documentation" +dependencies = [ + "sphinx", + "sphinxcontrib-contentui", + "sphinxcontrib-details-directive", + "furo", + "markupsafe<2.1" +] + +[tool.hatch.envs.docs.scripts] +build = [ + "make -C docs" +] diff --git a/aiida_diff/__init__.py b/src/aiida_diff/__init__.py similarity index 84% rename from aiida_diff/__init__.py rename to src/aiida_diff/__init__.py index 02ee885..dc48564 100644 --- a/aiida_diff/__init__.py +++ b/src/aiida_diff/__init__.py @@ -4,4 +4,4 @@ AiiDA demo plugin that wraps the `diff` executable for computing the difference between two files. """ -__version__ = "1.2.0" +__version__ = '1.2.0' diff --git a/aiida_diff/calculations.py b/src/aiida_diff/calculations.py similarity index 70% rename from aiida_diff/calculations.py rename to src/aiida_diff/calculations.py index b0a4aa7..10bf0e6 100644 --- a/aiida_diff/calculations.py +++ b/src/aiida_diff/calculations.py @@ -3,12 +3,13 @@ Register calculations via the "aiida.calculations" entry point in setup.json. """ + from aiida.common import datastructures from aiida.engine import CalcJob from aiida.orm import SinglefileData from aiida.plugins import DataFactory -DiffParameters = DataFactory("diff") +DiffParameters = DataFactory('diff') class DiffCalculation(CalcJob): @@ -24,37 +25,31 @@ def define(cls, spec): super().define(spec) # set default values for AiiDA options - spec.inputs["metadata"]["options"]["resources"].default = { - "num_machines": 1, - "num_mpiprocs_per_machine": 1, + spec.inputs['metadata']['options']['resources'].default = { + 'num_machines': 1, + 'num_mpiprocs_per_machine': 1, } - spec.inputs["metadata"]["options"]["parser_name"].default = "diff" + spec.inputs['metadata']['options']['parser_name'].default = 'diff' # new ports + spec.input('metadata.options.output_filename', valid_type=str, default='patch.diff') spec.input( - "metadata.options.output_filename", valid_type=str, default="patch.diff" - ) - spec.input( - "parameters", + 'parameters', valid_type=DiffParameters, - help="Command line parameters for diff", - ) - spec.input( - "file1", valid_type=SinglefileData, help="First file to be compared." - ) - spec.input( - "file2", valid_type=SinglefileData, help="Second file to be compared." + help='Command line parameters for diff', ) + spec.input('file1', valid_type=SinglefileData, help='First file to be compared.') + spec.input('file2', valid_type=SinglefileData, help='Second file to be compared.') spec.output( - "diff", + 'diff', valid_type=SinglefileData, - help="diff between file1 and file2.", + help='diff between file1 and file2.', ) spec.exit_code( 300, - "ERROR_MISSING_OUTPUT_FILES", - message="Calculation did not produce all expected output files.", + 'ERROR_MISSING_OUTPUT_FILES', + message='Calculation did not produce all expected output files.', ) def prepare_for_submission(self, folder): diff --git a/aiida_diff/cli.py b/src/aiida_diff/cli.py similarity index 74% rename from aiida_diff/cli.py rename to src/aiida_diff/cli.py index 462e259..2b1ab73 100644 --- a/aiida_diff/cli.py +++ b/src/aiida_diff/cli.py @@ -5,10 +5,10 @@ directly into the 'verdi' command by using AiiDA-specific entry points like "aiida.cmdline.data" (both in the setup.json file). """ + import sys import click - from aiida.cmdline.commands.cmd_data import verdi_data from aiida.cmdline.params.types import DataParamType from aiida.cmdline.utils import decorators @@ -17,37 +17,37 @@ # See aiida.cmdline.data entry point in setup.json -@verdi_data.group("diff") +@verdi_data.group('diff') def data_cli(): """Command line interface for aiida-diff""" -@data_cli.command("list") +@data_cli.command('list') @decorators.with_dbenv() def list_(): # pylint: disable=redefined-builtin """ Display all DiffParameters nodes """ - DiffParameters = DataFactory("diff") + diff_parameters = DataFactory('diff') qb = QueryBuilder() - qb.append(DiffParameters) + qb.append(diff_parameters) results = qb.all() - s = "" + s = '' for result in results: obj = result[0] - s += f"{str(obj)}, pk: {obj.pk}\n" + s += f'{obj!s}, pk: {obj.pk}\n' sys.stdout.write(s) -@data_cli.command("export") -@click.argument("node", metavar="IDENTIFIER", type=DataParamType()) +@data_cli.command('export') +@click.argument('node', metavar='IDENTIFIER', type=DataParamType()) @click.option( - "--outfile", - "-o", + '--outfile', + '-o', type=click.Path(dir_okay=False), - help="Write output to file (default: print to stdout).", + help='Write output to file (default: print to stdout).', ) @decorators.with_dbenv() def export(node, outfile): @@ -55,7 +55,7 @@ def export(node, outfile): string = str(node) if outfile: - with open(outfile, "w", encoding="utf8") as f: + with open(outfile, 'w', encoding='utf8') as f: f.write(string) else: click.echo(string) diff --git a/aiida_diff/data/__init__.py b/src/aiida_diff/data/__init__.py similarity index 87% rename from aiida_diff/data/__init__.py rename to src/aiida_diff/data/__init__.py index f2aa313..8334d39 100644 --- a/aiida_diff/data/__init__.py +++ b/src/aiida_diff/data/__init__.py @@ -1,21 +1,20 @@ -""" -Data types provided by plugin +"""Data types provided by plugin Register data types via the "aiida.data" entry point in setup.json. """ + # You can directly use or subclass aiida.orm.data.Data # or any other data type listed under 'verdi data' -from voluptuous import Optional, Schema - from aiida.orm import Dict +from voluptuous import Optional, Schema # A subset of diff's command line options cmdline_options = { - Optional("ignore-case"): bool, - Optional("ignore-file-name-case"): bool, - Optional("ignore-tab-expansion"): bool, - Optional("ignore-space-change"): bool, - Optional("ignore-all-space"): bool, + Optional('ignore-case'): bool, + Optional('ignore-file-name-case'): bool, + Optional('ignore-tab-expansion'): bool, + Optional('ignore-space-change'): bool, + Optional('ignore-all-space'): bool, } @@ -73,7 +72,7 @@ def cmdline_params(self, file1_name, file2_name): pm_dict = self.get_dict() for option, enabled in pm_dict.items(): if enabled: - parameters += ["--" + option] + parameters += ['--' + option] parameters += [file1_name, file2_name] @@ -89,5 +88,5 @@ def __str__(self): """ string = super().__str__() - string += "\n" + str(self.get_dict()) + string += '\n' + str(self.get_dict()) return string diff --git a/aiida_diff/helpers.py b/src/aiida_diff/helpers.py similarity index 88% rename from aiida_diff/helpers.py rename to src/aiida_diff/helpers.py index b080d2c..16c7dde 100644 --- a/aiida_diff/helpers.py +++ b/src/aiida_diff/helpers.py @@ -1,4 +1,4 @@ -""" Helper functions for automatically setting up computer & code. +"""Helper functions for automatically setting up computer & code. Helper functions for setting up 1. An AiiDA localhost computer @@ -7,16 +7,17 @@ Note: Point 2 is made possible by the fact that the ``diff`` executable is available in the PATH on almost any UNIX system. """ + import shutil import tempfile from aiida.common.exceptions import NotExistent from aiida.orm import Code, Computer -LOCALHOST_NAME = "localhost-test" +LOCALHOST_NAME = 'localhost-test' executables = { - "diff": "diff", + 'diff': 'diff', } @@ -53,11 +54,11 @@ def get_computer(name=LOCALHOST_NAME, workdir=None): computer = Computer( label=name, - description="localhost computer set up by aiida_diff tests", + description='localhost computer set up by aiida_diff tests', hostname=name, workdir=workdir, - transport_type="core.local", - scheduler_type="core.direct", + transport_type='core.local', + scheduler_type='core.direct', ) computer.store() computer.set_minimum_job_poll_interval(0.0) @@ -84,7 +85,7 @@ def get_code(entry_point, computer): ) from exc codes = Code.objects.find( # pylint: disable=no-member - filters={"label": executable} + filters={'label': executable} ) if codes: return codes[0] diff --git a/aiida_diff/parsers.py b/src/aiida_diff/parsers.py similarity index 78% rename from aiida_diff/parsers.py rename to src/aiida_diff/parsers.py index e832035..d36c538 100644 --- a/aiida_diff/parsers.py +++ b/src/aiida_diff/parsers.py @@ -3,13 +3,14 @@ Register parsers via the "aiida.parsers" entry point in setup.json. """ + from aiida.common import exceptions from aiida.engine import ExitCode from aiida.orm import SinglefileData from aiida.parsers.parser import Parser from aiida.plugins import CalculationFactory -DiffCalculation = CalculationFactory("diff") +DiffCalculation = CalculationFactory('diff') class DiffParser(Parser): @@ -28,7 +29,7 @@ def __init__(self, node): """ super().__init__(node) if not issubclass(node.process_class, DiffCalculation): - raise exceptions.ParsingError("Can only parse DiffCalculation") + raise exceptions.ParsingError('Can only parse DiffCalculation') def parse(self, **kwargs): """ @@ -36,22 +37,20 @@ def parse(self, **kwargs): :returns: an exit code, if parsing fails (or nothing if parsing succeeds) """ - output_filename = self.node.get_option("output_filename") + output_filename = self.node.get_option('output_filename') # Check that folder content is as expected files_retrieved = self.retrieved.list_object_names() files_expected = [output_filename] # Note: set(A) <= set(B) checks whether A is a subset of B if not set(files_expected) <= set(files_retrieved): - self.logger.error( - f"Found files '{files_retrieved}', expected to find '{files_expected}'" - ) + self.logger.error(f"Found files '{files_retrieved}', expected to find '{files_expected}'") return self.exit_codes.ERROR_MISSING_OUTPUT_FILES # add output file self.logger.info(f"Parsing '{output_filename}'") - with self.retrieved.open(output_filename, "rb") as handle: + with self.retrieved.open(output_filename, 'rb') as handle: output_node = SinglefileData(file=handle) - self.out("diff", output_node) + self.out('diff', output_node) return ExitCode(0) diff --git a/tests/__init__.py b/tests/__init__.py index 94cbb42..484b467 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,8 +1,9 @@ -""" Tests for the plugin. +"""Tests for the plugin. Includes both tests written in unittest style (test_cli.py) and tests written in pytest style (test_calculations.py). """ + import os TEST_DIR = os.path.dirname(os.path.realpath(__file__)) diff --git a/tests/test_calculations.py b/tests/test_calculations.py index 9d27370..0156dac 100644 --- a/tests/test_calculations.py +++ b/tests/test_calculations.py @@ -1,4 +1,5 @@ -""" Tests for calculations.""" +"""Tests for calculations.""" + import os from aiida.engine import run @@ -13,25 +14,25 @@ def test_process(diff_code): note this does not test that the expected outputs are created of output parsing""" # Prepare input parameters - DiffParameters = DataFactory("diff") - parameters = DiffParameters({"ignore-case": True}) + diff_parameters = DataFactory('diff') + parameters = diff_parameters({'ignore-case': True}) - file1 = SinglefileData(file=os.path.join(TEST_DIR, "input_files", "file1.txt")) - file2 = SinglefileData(file=os.path.join(TEST_DIR, "input_files", "file2.txt")) + file1 = SinglefileData(file=os.path.join(TEST_DIR, 'input_files', 'file1.txt')) + file2 = SinglefileData(file=os.path.join(TEST_DIR, 'input_files', 'file2.txt')) # set up calculation inputs = { - "code": diff_code, - "parameters": parameters, - "file1": file1, - "file2": file2, - "metadata": { - "options": {"max_wallclock_seconds": 30}, + 'code': diff_code, + 'parameters': parameters, + 'file1': file1, + 'file2': file2, + 'metadata': { + 'options': {'max_wallclock_seconds': 30}, }, } - result = run(CalculationFactory("diff"), **inputs) - computed_diff = result["diff"].get_content() + result = run(CalculationFactory('diff'), **inputs) + computed_diff = result['diff'].get_content() - assert "content1" in computed_diff - assert "content2" in computed_diff + assert 'content1' in computed_diff + assert 'content2' in computed_diff diff --git a/tests/test_cli.py b/tests/test_cli.py index 704c9e7..c58ca14 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,9 +1,8 @@ -""" Tests for command line interface.""" -from click.testing import CliRunner +"""Tests for command line interface.""" from aiida.plugins import DataFactory - from aiida_diff.cli import export, list_ +from click.testing import CliRunner # pylint: disable=attribute-defined-outside-init @@ -12,8 +11,8 @@ class TestDataCli: def setup_method(self): """Prepare nodes for cli tests.""" - DiffParameters = DataFactory("diff") - self.parameters = DiffParameters({"ignore-case": True}) + diff_parameters = DataFactory('diff') + self.parameters = diff_parameters({'ignore-case': True}) self.parameters.store() self.runner = CliRunner() @@ -31,7 +30,5 @@ def test_data_diff_export(self): Tests that it can be reached and that it shows the contents of the node we have set up. """ - result = self.runner.invoke( - export, [str(self.parameters.pk)], catch_exceptions=False - ) - assert "ignore-case" in result.output + result = self.runner.invoke(export, [str(self.parameters.pk)], catch_exceptions=False) + assert 'ignore-case' in result.output From 765a62d25f28734709d0638a8fc35bddb3d932a5 Mon Sep 17 00:00:00 2001 From: Alexander Goscinski Date: Wed, 24 Jul 2024 14:51:41 +0200 Subject: [PATCH 2/7] update cc --- .github/workflows/ci.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 97b31c6..c38a039 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,6 +38,9 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + - name: Install project manager + run: | + pip install hatch - name: Run test suite env: PYTEST_ADDOPTS: "--durations=0" @@ -53,11 +56,14 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + - name: Install project manager + run: | + pip install hatch - name: Build docs run: | hatch run docs:build - hatch: + fmt: runs-on: ubuntu-latest timeout-minutes: 15 steps: @@ -66,6 +72,9 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + - name: Install project manager + run: | + pip install hatch - name: Run formatter and linter run: | hatch fmt --check From 0289ff5ea5b2337c75a66db0f9afe53e6982d18f Mon Sep 17 00:00:00 2001 From: Alexander Goscinski Date: Wed, 24 Jul 2024 15:09:46 +0200 Subject: [PATCH 3/7] update cc --- .github/workflows/ci.yml | 7 ++++++- examples/example_01.py | 4 +--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c38a039..12c9a11 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,11 @@ name: ci -on: [push, pull_request] +on: + push: + # only pushes to main trigger + branches: [main] + pull_request: + # always triggered jobs: diff --git a/examples/example_01.py b/examples/example_01.py index b11401d..e0d9c05 100644 --- a/examples/example_01.py +++ b/examples/example_01.py @@ -22,9 +22,7 @@ def test_run(diff_code): if not diff_code: # get code computer = helpers.get_computer() - diff_code = helpers.get_code( - entry_point='diff', computer=computer - ) + diff_code = helpers.get_code(entry_point='diff', computer=computer) # Prepare input parameters diff_parameters = DataFactory('diff') From 4df078babd4cda7e45c0e5d6e458f9e0c1a57790 Mon Sep 17 00:00:00 2001 From: Alexander Goscinski Date: Wed, 24 Jul 2024 16:05:51 +0200 Subject: [PATCH 4/7] update cc --- docs/source/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/conf.py b/docs/source/conf.py index 8509287..40aff75 100755 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -182,6 +182,7 @@ # We should ignore any python built-in exception, for instance nitpick_ignore = [ ('py:class', 'Logger'), + ('py:class', 'QbFields'), # Warning started to appear with aiida 2.6 ] From bd6e45b2c51577713b7a1981f21376f31dc0d966 Mon Sep 17 00:00:00 2001 From: Alexander Goscinski Date: Wed, 24 Jul 2024 17:57:20 +0200 Subject: [PATCH 5/7] update cc --- .github/workflows/ci.yml | 2 +- .pre-commit-config.yaml | 11 +++-- conftest.py | 8 ++-- docs/source/conf.py | 76 ++++++++++++++++----------------- examples/example_01.py | 34 +++++++-------- pyproject.toml | 3 -- src/aiida_diff/__init__.py | 2 +- src/aiida_diff/calculations.py | 28 ++++++------ src/aiida_diff/cli.py | 22 +++++----- src/aiida_diff/data/__init__.py | 14 +++--- src/aiida_diff/helpers.py | 12 +++--- src/aiida_diff/parsers.py | 10 ++--- tests/test_calculations.py | 28 ++++++------ tests/test_cli.py | 6 +-- 14 files changed, 129 insertions(+), 127 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 12c9a11..50ee5cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,7 +68,7 @@ jobs: run: | hatch run docs:build - fmt: + static-analysis: runs-on: ubuntu-latest timeout-minutes: 15 steps: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 46dbcb7..4d839fd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,8 +1,13 @@ repos: - repo: local hooks: - - id: fmt - name: fmt - entry: hatch fmt + - id: format + name: format + entry: hatch fmt -f + language: system + types: [python] + - id: lint + name: lint + entry: hatch fmt -l language: system types: [python] diff --git a/conftest.py b/conftest.py index 7d76365..dfc42bf 100644 --- a/conftest.py +++ b/conftest.py @@ -2,15 +2,15 @@ import pytest -pytest_plugins = ['aiida.manage.tests.pytest_fixtures'] +pytest_plugins = ["aiida.manage.tests.pytest_fixtures"] -@pytest.fixture(scope='function', autouse=True) +@pytest.fixture(scope="function", autouse=True) def clear_database_auto(clear_database): # pylint: disable=unused-argument """Automatically clear database in between tests.""" -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def diff_code(aiida_local_code_factory): """Get a diff code.""" - return aiida_local_code_factory(executable='diff', entry_point='diff') + return aiida_local_code_factory(executable="diff", entry_point="diff") diff --git a/docs/source/conf.py b/docs/source/conf.py index 40aff75..8d9db62 100755 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -21,7 +21,7 @@ # -- AiiDA-related setup -------------------------------------------------- # Load AiiDA profile -temp_profile = SqliteTempBackend.create_profile('temp-profile') +temp_profile = SqliteTempBackend.create_profile("temp-profile") load_profile(temp_profile, allow_switch=True) # -- General configuration ------------------------------------------------ @@ -34,43 +34,43 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.mathjax', - 'sphinx.ext.intersphinx', - 'sphinx.ext.viewcode', - 'sphinxcontrib.contentui', - 'aiida.sphinxext', + "sphinx.ext.autodoc", + "sphinx.ext.mathjax", + "sphinx.ext.intersphinx", + "sphinx.ext.viewcode", + "sphinxcontrib.contentui", + "aiida.sphinxext", ] intersphinx_mapping = { - 'python': ('https://docs.python.org/3', None), - 'aiida': ('https://aiida.readthedocs.io/projects/aiida-core/en/latest', None), + "python": ("https://docs.python.org/3", None), + "aiida": ("https://aiida.readthedocs.io/projects/aiida-core/en/latest", None), } # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. # ~ master_doc = 'index' -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'aiida-diff' -copyright_first_year = '2024' -copyright_owners = 'The AiiDA Team' +project = "aiida-diff" +copyright_first_year = "2024" +copyright_owners = "The AiiDA Team" current_year = str(time.localtime().tm_year) copyright_year_string = ( - current_year if current_year == copyright_first_year else f'{copyright_first_year}-{current_year}' + current_year if current_year == copyright_first_year else f"{copyright_first_year}-{current_year}" ) # pylint: disable=redefined-builtin -copyright = f'{copyright_year_string}, {copyright_owners}. All rights reserved' +copyright = f"{copyright_year_string}, {copyright_owners}. All rights reserved" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -79,14 +79,14 @@ # The full version, including alpha/beta/rc tags. release = aiida_diff.__version__ # The short X.Y version. -version = '.'.join(release.split('.')[:2]) +version = ".".join(release.split(".")[:2]) # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = 'en' +language = "en" # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: @@ -104,13 +104,13 @@ show_authors = True # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # -- Options for HTML output ---------------------------------------------- -html_theme = 'furo' -html_logo = 'images/AiiDA_transparent_logo.png' -html_title = f'aiida-diff v{release}' +html_theme = "furo" +html_logo = "images/AiiDA_transparent_logo.png" +html_title = f"aiida-diff v{release}" html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. @@ -167,7 +167,7 @@ # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -html_use_opensearch = 'https://aiida-diff.readthedocs.io' +html_use_opensearch = "https://aiida-diff.readthedocs.io" # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None @@ -176,13 +176,13 @@ # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' -html_search_language = 'en' +html_search_language = "en" # Warnings to ignore when using the -n (nitpicky) option # We should ignore any python built-in exception, for instance nitpick_ignore = [ - ('py:class', 'Logger'), - ('py:class', 'QbFields'), # Warning started to appear with aiida 2.6 + ("py:class", "Logger"), + ("py:class", "QbFields"), # Warning started to appear with aiida 2.6 ] @@ -195,8 +195,8 @@ def run_apidoc(_): See also https://github.com/rtfd/readthedocs.org/issues/1139 """ source_dir = os.path.abspath(os.path.dirname(__file__)) - apidoc_dir = os.path.join(source_dir, 'apidoc') - package_dir = os.path.join(source_dir, os.pardir, os.pardir, 'src', 'aiida_diff') + apidoc_dir = os.path.join(source_dir, "apidoc") + package_dir = os.path.join(source_dir, os.pardir, os.pardir, "src", "aiida_diff") # In #1139, they suggest the route below, but this ended up # calling sphinx-build, not sphinx-apidoc @@ -205,25 +205,25 @@ def run_apidoc(_): import subprocess - cmd_path = 'sphinx-apidoc' - if hasattr(sys, 'real_prefix'): # Check to see if we are in a virtualenv + cmd_path = "sphinx-apidoc" + if hasattr(sys, "real_prefix"): # Check to see if we are in a virtualenv # If we are, assemble the path manually - cmd_path = os.path.abspath(os.path.join(sys.prefix, 'bin', 'sphinx-apidoc')) + cmd_path = os.path.abspath(os.path.join(sys.prefix, "bin", "sphinx-apidoc")) options = [ - '-o', + "-o", apidoc_dir, package_dir, - '--private', - '--force', - '--no-toc', + "--private", + "--force", + "--no-toc", ] # See https://stackoverflow.com/a/30144019 env = os.environ.copy() - env['SPHINX_APIDOC_OPTIONS'] = 'members,special-members,private-members,undoc-members,show-inheritance' + env["SPHINX_APIDOC_OPTIONS"] = "members,special-members,private-members,undoc-members,show-inheritance" subprocess.check_call([cmd_path] + options, env=env) def setup(app): - app.connect('builder-inited', run_apidoc) + app.connect("builder-inited", run_apidoc) diff --git a/examples/example_01.py b/examples/example_01.py index e0d9c05..48202d4 100644 --- a/examples/example_01.py +++ b/examples/example_01.py @@ -11,7 +11,7 @@ from aiida.plugins import CalculationFactory, DataFactory from aiida_diff import helpers -INPUT_DIR = path.join(path.dirname(path.realpath(__file__)), 'input_files') +INPUT_DIR = path.join(path.dirname(path.realpath(__file__)), "input_files") def test_run(diff_code): @@ -22,34 +22,34 @@ def test_run(diff_code): if not diff_code: # get code computer = helpers.get_computer() - diff_code = helpers.get_code(entry_point='diff', computer=computer) + diff_code = helpers.get_code(entry_point="diff", computer=computer) # Prepare input parameters - diff_parameters = DataFactory('diff') - parameters = diff_parameters({'ignore-case': True}) + diff_parameters = DataFactory("diff") + parameters = diff_parameters({"ignore-case": True}) - singlefile_data = DataFactory('core.singlefile') - file1 = singlefile_data(file=path.join(INPUT_DIR, 'file1.txt')) - file2 = singlefile_data(file=path.join(INPUT_DIR, 'file2.txt')) + singlefile_data = DataFactory("core.singlefile") + file1 = singlefile_data(file=path.join(INPUT_DIR, "file1.txt")) + file2 = singlefile_data(file=path.join(INPUT_DIR, "file2.txt")) # set up calculation inputs = { - 'code': diff_code, - 'parameters': parameters, - 'file1': file1, - 'file2': file2, - 'metadata': { - 'description': 'Test job submission with the aiida_diff plugin', + "code": diff_code, + "parameters": parameters, + "file1": file1, + "file2": file2, + "metadata": { + "description": "Test job submission with the aiida_diff plugin", }, } # Note: in order to submit your calculation to the aiida daemon, do: # from aiida.engine import submit # future = submit(CalculationFactory('diff'), **inputs) - result = engine.run(CalculationFactory('diff'), **inputs) + result = engine.run(CalculationFactory("diff"), **inputs) - computed_diff = result['diff'].get_content() - print(f'Computed diff between files: \n{computed_diff}') + computed_diff = result["diff"].get_content() + print(f"Computed diff between files: \n{computed_diff}") @click.command() @@ -67,5 +67,5 @@ def cli(code): test_run(code) -if __name__ == '__main__': +if __name__ == "__main__": cli() # pylint: disable=no-value-for-parameter diff --git a/pyproject.toml b/pyproject.toml index bbe388d..e4f502a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,9 +65,6 @@ source = ["src/aiida_diff"] [tool.ruff] line-length = 120 -[tool.ruff.format] -quote-style = 'single' - [tool.ruff.lint] ignore = [ 'F403', # Star imports unable to detect undefined names diff --git a/src/aiida_diff/__init__.py b/src/aiida_diff/__init__.py index dc48564..02ee885 100644 --- a/src/aiida_diff/__init__.py +++ b/src/aiida_diff/__init__.py @@ -4,4 +4,4 @@ AiiDA demo plugin that wraps the `diff` executable for computing the difference between two files. """ -__version__ = '1.2.0' +__version__ = "1.2.0" diff --git a/src/aiida_diff/calculations.py b/src/aiida_diff/calculations.py index 10bf0e6..ed6a785 100644 --- a/src/aiida_diff/calculations.py +++ b/src/aiida_diff/calculations.py @@ -9,7 +9,7 @@ from aiida.orm import SinglefileData from aiida.plugins import DataFactory -DiffParameters = DataFactory('diff') +DiffParameters = DataFactory("diff") class DiffCalculation(CalcJob): @@ -25,31 +25,31 @@ def define(cls, spec): super().define(spec) # set default values for AiiDA options - spec.inputs['metadata']['options']['resources'].default = { - 'num_machines': 1, - 'num_mpiprocs_per_machine': 1, + spec.inputs["metadata"]["options"]["resources"].default = { + "num_machines": 1, + "num_mpiprocs_per_machine": 1, } - spec.inputs['metadata']['options']['parser_name'].default = 'diff' + spec.inputs["metadata"]["options"]["parser_name"].default = "diff" # new ports - spec.input('metadata.options.output_filename', valid_type=str, default='patch.diff') + spec.input("metadata.options.output_filename", valid_type=str, default="patch.diff") spec.input( - 'parameters', + "parameters", valid_type=DiffParameters, - help='Command line parameters for diff', + help="Command line parameters for diff", ) - spec.input('file1', valid_type=SinglefileData, help='First file to be compared.') - spec.input('file2', valid_type=SinglefileData, help='Second file to be compared.') + spec.input("file1", valid_type=SinglefileData, help="First file to be compared.") + spec.input("file2", valid_type=SinglefileData, help="Second file to be compared.") spec.output( - 'diff', + "diff", valid_type=SinglefileData, - help='diff between file1 and file2.', + help="diff between file1 and file2.", ) spec.exit_code( 300, - 'ERROR_MISSING_OUTPUT_FILES', - message='Calculation did not produce all expected output files.', + "ERROR_MISSING_OUTPUT_FILES", + message="Calculation did not produce all expected output files.", ) def prepare_for_submission(self, folder): diff --git a/src/aiida_diff/cli.py b/src/aiida_diff/cli.py index 2b1ab73..8aecc1e 100644 --- a/src/aiida_diff/cli.py +++ b/src/aiida_diff/cli.py @@ -17,37 +17,37 @@ # See aiida.cmdline.data entry point in setup.json -@verdi_data.group('diff') +@verdi_data.group("diff") def data_cli(): """Command line interface for aiida-diff""" -@data_cli.command('list') +@data_cli.command("list") @decorators.with_dbenv() def list_(): # pylint: disable=redefined-builtin """ Display all DiffParameters nodes """ - diff_parameters = DataFactory('diff') + diff_parameters = DataFactory("diff") qb = QueryBuilder() qb.append(diff_parameters) results = qb.all() - s = '' + s = "" for result in results: obj = result[0] - s += f'{obj!s}, pk: {obj.pk}\n' + s += f"{obj!s}, pk: {obj.pk}\n" sys.stdout.write(s) -@data_cli.command('export') -@click.argument('node', metavar='IDENTIFIER', type=DataParamType()) +@data_cli.command("export") +@click.argument("node", metavar="IDENTIFIER", type=DataParamType()) @click.option( - '--outfile', - '-o', + "--outfile", + "-o", type=click.Path(dir_okay=False), - help='Write output to file (default: print to stdout).', + help="Write output to file (default: print to stdout).", ) @decorators.with_dbenv() def export(node, outfile): @@ -55,7 +55,7 @@ def export(node, outfile): string = str(node) if outfile: - with open(outfile, 'w', encoding='utf8') as f: + with open(outfile, "w", encoding="utf8") as f: f.write(string) else: click.echo(string) diff --git a/src/aiida_diff/data/__init__.py b/src/aiida_diff/data/__init__.py index 8334d39..2b39992 100644 --- a/src/aiida_diff/data/__init__.py +++ b/src/aiida_diff/data/__init__.py @@ -10,11 +10,11 @@ # A subset of diff's command line options cmdline_options = { - Optional('ignore-case'): bool, - Optional('ignore-file-name-case'): bool, - Optional('ignore-tab-expansion'): bool, - Optional('ignore-space-change'): bool, - Optional('ignore-all-space'): bool, + Optional("ignore-case"): bool, + Optional("ignore-file-name-case"): bool, + Optional("ignore-tab-expansion"): bool, + Optional("ignore-space-change"): bool, + Optional("ignore-all-space"): bool, } @@ -72,7 +72,7 @@ def cmdline_params(self, file1_name, file2_name): pm_dict = self.get_dict() for option, enabled in pm_dict.items(): if enabled: - parameters += ['--' + option] + parameters += ["--" + option] parameters += [file1_name, file2_name] @@ -88,5 +88,5 @@ def __str__(self): """ string = super().__str__() - string += '\n' + str(self.get_dict()) + string += "\n" + str(self.get_dict()) return string diff --git a/src/aiida_diff/helpers.py b/src/aiida_diff/helpers.py index 16c7dde..c63a968 100644 --- a/src/aiida_diff/helpers.py +++ b/src/aiida_diff/helpers.py @@ -14,10 +14,10 @@ from aiida.common.exceptions import NotExistent from aiida.orm import Code, Computer -LOCALHOST_NAME = 'localhost-test' +LOCALHOST_NAME = "localhost-test" executables = { - 'diff': 'diff', + "diff": "diff", } @@ -54,11 +54,11 @@ def get_computer(name=LOCALHOST_NAME, workdir=None): computer = Computer( label=name, - description='localhost computer set up by aiida_diff tests', + description="localhost computer set up by aiida_diff tests", hostname=name, workdir=workdir, - transport_type='core.local', - scheduler_type='core.direct', + transport_type="core.local", + scheduler_type="core.direct", ) computer.store() computer.set_minimum_job_poll_interval(0.0) @@ -85,7 +85,7 @@ def get_code(entry_point, computer): ) from exc codes = Code.objects.find( # pylint: disable=no-member - filters={'label': executable} + filters={"label": executable} ) if codes: return codes[0] diff --git a/src/aiida_diff/parsers.py b/src/aiida_diff/parsers.py index d36c538..fd03408 100644 --- a/src/aiida_diff/parsers.py +++ b/src/aiida_diff/parsers.py @@ -10,7 +10,7 @@ from aiida.parsers.parser import Parser from aiida.plugins import CalculationFactory -DiffCalculation = CalculationFactory('diff') +DiffCalculation = CalculationFactory("diff") class DiffParser(Parser): @@ -29,7 +29,7 @@ def __init__(self, node): """ super().__init__(node) if not issubclass(node.process_class, DiffCalculation): - raise exceptions.ParsingError('Can only parse DiffCalculation') + raise exceptions.ParsingError("Can only parse DiffCalculation") def parse(self, **kwargs): """ @@ -37,7 +37,7 @@ def parse(self, **kwargs): :returns: an exit code, if parsing fails (or nothing if parsing succeeds) """ - output_filename = self.node.get_option('output_filename') + output_filename = self.node.get_option("output_filename") # Check that folder content is as expected files_retrieved = self.retrieved.list_object_names() @@ -49,8 +49,8 @@ def parse(self, **kwargs): # add output file self.logger.info(f"Parsing '{output_filename}'") - with self.retrieved.open(output_filename, 'rb') as handle: + with self.retrieved.open(output_filename, "rb") as handle: output_node = SinglefileData(file=handle) - self.out('diff', output_node) + self.out("diff", output_node) return ExitCode(0) diff --git a/tests/test_calculations.py b/tests/test_calculations.py index 0156dac..d22c38c 100644 --- a/tests/test_calculations.py +++ b/tests/test_calculations.py @@ -14,25 +14,25 @@ def test_process(diff_code): note this does not test that the expected outputs are created of output parsing""" # Prepare input parameters - diff_parameters = DataFactory('diff') - parameters = diff_parameters({'ignore-case': True}) + diff_parameters = DataFactory("diff") + parameters = diff_parameters({"ignore-case": True}) - file1 = SinglefileData(file=os.path.join(TEST_DIR, 'input_files', 'file1.txt')) - file2 = SinglefileData(file=os.path.join(TEST_DIR, 'input_files', 'file2.txt')) + file1 = SinglefileData(file=os.path.join(TEST_DIR, "input_files", "file1.txt")) + file2 = SinglefileData(file=os.path.join(TEST_DIR, "input_files", "file2.txt")) # set up calculation inputs = { - 'code': diff_code, - 'parameters': parameters, - 'file1': file1, - 'file2': file2, - 'metadata': { - 'options': {'max_wallclock_seconds': 30}, + "code": diff_code, + "parameters": parameters, + "file1": file1, + "file2": file2, + "metadata": { + "options": {"max_wallclock_seconds": 30}, }, } - result = run(CalculationFactory('diff'), **inputs) - computed_diff = result['diff'].get_content() + result = run(CalculationFactory("diff"), **inputs) + computed_diff = result["diff"].get_content() - assert 'content1' in computed_diff - assert 'content2' in computed_diff + assert "content1" in computed_diff + assert "content2" in computed_diff diff --git a/tests/test_cli.py b/tests/test_cli.py index c58ca14..dea39d2 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -11,8 +11,8 @@ class TestDataCli: def setup_method(self): """Prepare nodes for cli tests.""" - diff_parameters = DataFactory('diff') - self.parameters = diff_parameters({'ignore-case': True}) + diff_parameters = DataFactory("diff") + self.parameters = diff_parameters({"ignore-case": True}) self.parameters.store() self.runner = CliRunner() @@ -31,4 +31,4 @@ def test_data_diff_export(self): we have set up. """ result = self.runner.invoke(export, [str(self.parameters.pk)], catch_exceptions=False) - assert 'ignore-case' in result.output + assert "ignore-case" in result.output From bf6ab06c2e5f5dc719186083f8f6a12e9b3f96ab Mon Sep 17 00:00:00 2001 From: Alexander Goscinski Date: Wed, 24 Jul 2024 18:17:33 +0200 Subject: [PATCH 6/7] updates cc --- pyproject.toml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e4f502a..2805a99 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,13 @@ dependencies = [ pre-commit = [ 'pre-commit~=3.5', ] +docs = [ + "sphinx", + "sphinxcontrib-contentui", + "sphinxcontrib-details-directive", + "furo", + "markupsafe<2.1" +] [project.urls] Source = "https://github.com/aiidateam/aiida-diff" @@ -131,14 +138,7 @@ lint-check = "ruff check --config pyproject.toml {args:.}" lint-fix = "ruff check --config pyproject.toml --fix --exit-non-zero-on-fix --show-fixes {args:.}" [tool.hatch.envs.docs] -description = "Build the documentation" -dependencies = [ - "sphinx", - "sphinxcontrib-contentui", - "sphinxcontrib-details-directive", - "furo", - "markupsafe<2.1" -] +features = "docs" [tool.hatch.envs.docs.scripts] build = [ From 274dcbd319b1749c8eb561db254ae022af7cfc51 Mon Sep 17 00:00:00 2001 From: Alexander Goscinski Date: Wed, 24 Jul 2024 18:23:13 +0200 Subject: [PATCH 7/7] updates cc --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2805a99..8d70c8d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -138,7 +138,7 @@ lint-check = "ruff check --config pyproject.toml {args:.}" lint-fix = "ruff check --config pyproject.toml --fix --exit-non-zero-on-fix --show-fixes {args:.}" [tool.hatch.envs.docs] -features = "docs" +features = ["docs"] [tool.hatch.envs.docs.scripts] build = [