From f652c18d80a6e7be0b0dc3ba31e305a7505922e3 Mon Sep 17 00:00:00 2001
From: Aarni Koskela
Date: Thu, 25 Jul 2024 08:53:18 +0300
Subject: [PATCH 1/7] Wire up formatting and linting with Ruff
---
.github/workflows/CI_pull_request.yml | 8 ++++++
.github/workflows/CI_push.yml | 8 ++++++
.pre-commit-config.yaml | 14 +++++++++++
doc/development/style_guide.rst | 3 ++-
ruff.toml | 35 +++++++++++++++++++++++++++
5 files changed, 67 insertions(+), 1 deletion(-)
create mode 100644 .pre-commit-config.yaml
create mode 100644 ruff.toml
diff --git a/.github/workflows/CI_pull_request.yml b/.github/workflows/CI_pull_request.yml
index debdf83..9219e4b 100644
--- a/.github/workflows/CI_pull_request.yml
+++ b/.github/workflows/CI_pull_request.yml
@@ -6,6 +6,14 @@ on:
schedule:
- cron: '15 2 * * TUE' # Also run the full test suite for PRs on the main branch every Tuesday night
jobs:
+ lint:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+ - uses: pre-commit/action@v3.0.1
run-full-test-suite:
name: Run full test suite
runs-on: ${{ matrix.os }}
diff --git a/.github/workflows/CI_push.yml b/.github/workflows/CI_push.yml
index 7ec6ab1..c38c853 100644
--- a/.github/workflows/CI_push.yml
+++ b/.github/workflows/CI_push.yml
@@ -6,6 +6,14 @@ on:
tags-ignore:
- "**"
jobs:
+ lint:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+ - uses: pre-commit/action@v3.0.1
run-unittests:
name: Run unittests
runs-on: ubuntu-latest
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..311f91b
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,14 @@
+repos:
+ - repo: https://github.com/astral-sh/ruff-pre-commit
+ rev: v0.6.4
+ hooks:
+ - id: ruff
+ args:
+ - --fix
+ - id: ruff-format
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.6.0
+ hooks:
+ - id: debug-statements
+ - id: end-of-file-fixer
+ - id: trailing-whitespace
diff --git a/doc/development/style_guide.rst b/doc/development/style_guide.rst
index e20b71f..0910989 100644
--- a/doc/development/style_guide.rst
+++ b/doc/development/style_guide.rst
@@ -5,7 +5,8 @@ Style guide
We aim to keep the `cotainr` code base and documentation consistent by following the style guides listed here.
- We follow the :pep:`8` style guide.
-- The Python source code is formatted using `black `_ .
+- The Python source code is formatted using `Ruff `_ .
+- The codebase is linted with `pre-commit `_.
- All `docstrings `_ are formatted according to the `numpydoc format `_.
- We reference the python interpreter executable as `python3` (not `python`) when explicitly calling the system python executable and as :data:`sys.executable` when reinvoking the interpreter as recommended in :pep:`394`.
diff --git a/ruff.toml b/ruff.toml
new file mode 100644
index 0000000..b478543
--- /dev/null
+++ b/ruff.toml
@@ -0,0 +1,35 @@
+target-version = "py38"
+line-length = 88
+
+[lint]
+extend-select = [
+ "B",
+ "E",
+ "F",
+ "W",
+]
+ignore = [
+ "E501", # ignore line length; auto-formatting will take care of what it can.
+]
+exclude = [
+ "examples/**/*.ipynb",
+]
+
+[lint.extend-per-file-ignores]
+"cotainr/tests/**/test_*.py" = [
+ # The non-standard way to import py.test fixtures in
+ # tests means we will need to ignore "imported but not used"
+ # as well as argument shadowing tests in test modules.
+ # It would be better to use standard `conftest` style fixture
+ # discovery.
+ "F401",
+ "F811",
+]
+"cotainr/tests/**.py" = [
+ "S101", # assertions are OK in tests
+]
+
+[format]
+exclude = [
+ "examples/**/*.ipynb",
+]
From a206a6e8aa2f46c2984853765f7860db2c3d441e Mon Sep 17 00:00:00 2001
From: Aarni Koskela
Date: Fri, 6 Sep 2024 14:12:22 +0300
Subject: [PATCH 2/7] Adjust comment to avoid bad reformatting
---
cotainr/tests/cli/test_cotainr_cli.py | 12 ++++++++----
cotainr/tests/tracing/test_log_dispatcher.py | 15 ++++++++++-----
2 files changed, 18 insertions(+), 9 deletions(-)
diff --git a/cotainr/tests/cli/test_cotainr_cli.py b/cotainr/tests/cli/test_cotainr_cli.py
index 175d54e..ff17251 100644
--- a/cotainr/tests/cli/test_cotainr_cli.py
+++ b/cotainr/tests/cli/test_cotainr_cli.py
@@ -184,7 +184,8 @@ def test_cotainr_critical_logging(
assert isinstance(handler, logging.StreamHandler)
# Check correct logging, incl. message format, coloring, log level, and output stream
- stdout, stderr = capsys.readouterr() # readouterr clears its content when returning
+ # readouterr clears its content when returning
+ stdout, stderr = capsys.readouterr()
assert stdout.rstrip("\n").split("\n") == stdout_msgs
assert stderr.rstrip("\n").split("\n") == stderr_msgs
@@ -225,7 +226,8 @@ def test_cotainr_debug_logging(
assert isinstance(handler, logging.StreamHandler)
# Check correct logging, incl. message format, coloring, log level, and output stream
- stdout, stderr = capsys.readouterr() # readouterr clears its content when returning
+ # readouterr clears its content when returning
+ stdout, stderr = capsys.readouterr()
actual_stdout_msgs = stdout.rstrip("\n").split("\n")
actual_stderr_msgs = stderr.rstrip("\n").split("\n")
assert len(actual_stdout_msgs) == len(expected_stdout_msgs)
@@ -275,7 +277,8 @@ def test_cotainr_info_logging(
assert isinstance(handler, logging.StreamHandler)
# Check correct logging, incl. message format, coloring, log level, and output stream
- stdout, stderr = capsys.readouterr() # readouterr clears its content when returning
+ # readouterr clears its content when returning
+ stdout, stderr = capsys.readouterr()
assert stdout.rstrip("\n").split("\n") == stdout_msgs
assert stderr.rstrip("\n").split("\n") == stderr_msgs
@@ -361,6 +364,7 @@ def test_no_color_on_console(
assert isinstance(handler, logging.StreamHandler)
# Check correct logging, incl. message format, coloring, log level, and output stream
- stdout, stderr = capsys.readouterr() # readouterr clears its content when returning
+ # readouterr clears its content when returning
+ stdout, stderr = capsys.readouterr()
assert stdout.rstrip("\n").split("\n") == stdout_msgs
assert stderr.rstrip("\n").split("\n") == stderr_msgs
diff --git a/cotainr/tests/tracing/test_log_dispatcher.py b/cotainr/tests/tracing/test_log_dispatcher.py
index c987303..8ffd969 100644
--- a/cotainr/tests/tracing/test_log_dispatcher.py
+++ b/cotainr/tests/tracing/test_log_dispatcher.py
@@ -108,7 +108,8 @@ def test_log_dispatcher_critical_logging(
assert isinstance(logger.handlers[0], logging.StreamHandler)
# Check correct logging, incl. message format, coloring, log level, and output stream
- stdout, stderr = capsys.readouterr() # readouterr clears its content when returning
+ # readouterr clears its content when returning
+ stdout, stderr = capsys.readouterr()
assert stdout.rstrip("\n").split("\n") == stdout_msgs
assert stderr.rstrip("\n").split("\n") == stderr_msgs
@@ -153,7 +154,8 @@ def test_log_dispatcher_debug_logging(
assert isinstance(logger.handlers[0], logging.StreamHandler)
# Check correct logging, incl. message format, coloring, log level, and output stream
- stdout, stderr = capsys.readouterr() # readouterr clears its content when returning
+ # readouterr clears its content when returning
+ stdout, stderr = capsys.readouterr()
actual_stdout_msgs = stdout.rstrip("\n").split("\n")
actual_stderr_msgs = stderr.rstrip("\n").split("\n")
assert len(actual_stdout_msgs) == len(expected_stdout_msgs)
@@ -211,7 +213,8 @@ def test_log_dispatcher_info_logging(
assert isinstance(logger.handlers[0], logging.StreamHandler)
# Check correct logging, incl. message format, coloring, log level, and output stream
- stdout, stderr = capsys.readouterr() # readouterr clears its content when returning
+ # readouterr clears its content when returning
+ stdout, stderr = capsys.readouterr()
assert stdout.rstrip("\n").split("\n") == stdout_msgs
assert stderr.rstrip("\n").split("\n") == stderr_msgs
@@ -255,7 +258,8 @@ def test_log_dispatcher_warning_logging(
assert isinstance(logger.handlers[0], logging.StreamHandler)
# Check correct logging, incl. message format, coloring, log level, and output stream
- stdout, stderr = capsys.readouterr() # readouterr clears its content when returning
+ # readouterr clears its content when returning
+ stdout, stderr = capsys.readouterr()
assert stdout.rstrip("\n").split("\n") == stdout_msgs
assert stderr.rstrip("\n").split("\n") == stderr_msgs
@@ -348,7 +352,8 @@ def test_no_color_on_console(
assert isinstance(logger.handlers[0], logging.StreamHandler)
# Check correct logging, incl. message format, coloring, log level, and output stream
- stdout, stderr = capsys.readouterr() # readouterr clears its content when returning
+ # readouterr clears its content when returning
+ stdout, stderr = capsys.readouterr()
assert stdout.rstrip("\n").split("\n") == stdout_msgs
assert stderr.rstrip("\n").split("\n") == stderr_msgs
From 7880ea89fa9aed1e443696bb6042f90613ee5bf1 Mon Sep 17 00:00:00 2001
From: Aarni Koskela
Date: Thu, 25 Jul 2024 09:18:41 +0300
Subject: [PATCH 3/7] Run ruff-format
---
cotainr/pack.py | 4 +---
cotainr/tests/cli/test_build.py | 4 +---
cotainr/tests/cli/test_cotainr_cli.py | 4 ++++
cotainr/tests/pack/test_conda_install.py | 4 +---
cotainr/tests/tracing/patches.py | 1 +
cotainr/tests/tracing/stubs.py | 1 -
cotainr/tests/tracing/test_coloured_output_formatter.py | 1 +
cotainr/tests/tracing/test_console_spinner.py | 1 +
cotainr/tests/tracing/test_log_dispatcher.py | 6 ------
cotainr/tests/tracing/test_message_spinner.py | 1 +
cotainr/tests/tracing/test_stream_write_proxy.py | 1 +
cotainr/tracing.py | 6 +++---
doc/create_switcher.py | 1 +
13 files changed, 16 insertions(+), 19 deletions(-)
diff --git a/cotainr/pack.py b/cotainr/pack.py
index 4c7575b..ad23e3a 100644
--- a/cotainr/pack.py
+++ b/cotainr/pack.py
@@ -301,9 +301,7 @@ def _download_miniforge_installer(self, *, installer_path):
# Make up to 3 attempts at downloading the installer
for retry in range(3):
try:
- with urllib.request.urlopen(
- miniforge_installer_url
- ) as url: # nosec B310
+ with urllib.request.urlopen(miniforge_installer_url) as url: # nosec B310
installer_path.write_bytes(url.read())
break
diff --git a/cotainr/tests/cli/test_build.py b/cotainr/tests/cli/test_build.py
index 7437b6d..6a8fe62 100644
--- a/cotainr/tests/cli/test_build.py
+++ b/cotainr/tests/cli/test_build.py
@@ -377,9 +377,7 @@ def test_include_conda_env(
conda_env_create_cmd,
conda_clean_cmd,
sandbox_build_cmd,
- ) = (
- capsys.readouterr().out.strip().split("\n")
- )
+ ) = capsys.readouterr().out.strip().split("\n")
assert sandbox_create_cmd.startswith("PATCH: Ran command in sandbox:")
assert all(
s in sandbox_create_cmd
diff --git a/cotainr/tests/cli/test_cotainr_cli.py b/cotainr/tests/cli/test_cotainr_cli.py
index ff17251..6d28a6b 100644
--- a/cotainr/tests/cli/test_cotainr_cli.py
+++ b/cotainr/tests/cli/test_cotainr_cli.py
@@ -6,6 +6,7 @@
- see the LICENSE file for details.
"""
+
import itertools
import logging
import re
@@ -138,14 +139,17 @@ def test_missing_subcommand(self, argparse_options_line, capsys):
argparse_options_line=argparse_options_line
)
+
class TestVersionMessage:
def test_main_version(self, capsys):
from cotainr import __version__ as _cotainr_version
+
with pytest.raises(SystemExit):
CotainrCLI(args=["--version"])
stdout = capsys.readouterr().out
assert stdout == f"cotainr {_cotainr_version}\n"
+
class Test_SetupCotainrCLILogging:
@pytest.mark.parametrize("verbosity", [-1, -2, -3, -5, -1000])
def test_cotainr_critical_logging(
diff --git a/cotainr/tests/pack/test_conda_install.py b/cotainr/tests/pack/test_conda_install.py
index 0f6f558..c6005c8 100644
--- a/cotainr/tests/pack/test_conda_install.py
+++ b/cotainr/tests/pack/test_conda_install.py
@@ -58,9 +58,7 @@ def test_beforehand_license_acceptance(
miniforge_license_accept_cmd,
_conda_bootstrap_cmd,
_conda_bootstrap_clean_cmd,
- ) = (
- capsys.readouterr().out.strip().split("\n")
- )
+ ) = capsys.readouterr().out.strip().split("\n")
assert miniforge_license_accept_cmd == (
"You have accepted the Miniforge installer license via the command line option "
"'--accept-licenses'."
diff --git a/cotainr/tests/tracing/patches.py b/cotainr/tests/tracing/patches.py
index 1d51203..b26e596 100644
--- a/cotainr/tests/tracing/patches.py
+++ b/cotainr/tests/tracing/patches.py
@@ -6,6 +6,7 @@
- see the LICENSE file for details.
"""
+
import contextlib
import pytest
diff --git a/cotainr/tests/tracing/stubs.py b/cotainr/tests/tracing/stubs.py
index 23e6218..db0a74e 100644
--- a/cotainr/tests/tracing/stubs.py
+++ b/cotainr/tests/tracing/stubs.py
@@ -7,7 +7,6 @@
"""
-
import contextlib
diff --git a/cotainr/tests/tracing/test_coloured_output_formatter.py b/cotainr/tests/tracing/test_coloured_output_formatter.py
index 30886cb..988bf46 100644
--- a/cotainr/tests/tracing/test_coloured_output_formatter.py
+++ b/cotainr/tests/tracing/test_coloured_output_formatter.py
@@ -6,6 +6,7 @@
- see the LICENSE file for details.
"""
+
import logging
import pytest
diff --git a/cotainr/tests/tracing/test_console_spinner.py b/cotainr/tests/tracing/test_console_spinner.py
index cfc987f..be21d26 100644
--- a/cotainr/tests/tracing/test_console_spinner.py
+++ b/cotainr/tests/tracing/test_console_spinner.py
@@ -6,6 +6,7 @@
- see the LICENSE file for details.
"""
+
import concurrent.futures
import sys
diff --git a/cotainr/tests/tracing/test_log_dispatcher.py b/cotainr/tests/tracing/test_log_dispatcher.py
index 8ffd969..a2dd2b8 100644
--- a/cotainr/tests/tracing/test_log_dispatcher.py
+++ b/cotainr/tests/tracing/test_log_dispatcher.py
@@ -94,7 +94,6 @@ def test_log_dispatcher_critical_logging(
# Log test messages to log dispatcher loggers
for logger in [log_dispatcher.logger_stdout, log_dispatcher.logger_stderr]:
-
# Log messages
for level, msg in log_level_msgs.items():
logger.log(level=level, msg=msg)
@@ -140,7 +139,6 @@ def test_log_dispatcher_debug_logging(
# Log test messages to log dispatcher loggers
for logger in [log_dispatcher.logger_stdout, log_dispatcher.logger_stderr]:
-
# Log messages
for level, msg in log_level_msgs.items():
logger.log(level=level, msg=msg)
@@ -199,7 +197,6 @@ def test_log_dispatcher_info_logging(
# Log test messages to log dispatcher loggers
for logger in [log_dispatcher.logger_stdout, log_dispatcher.logger_stderr]:
-
# Log messages
for level, msg in log_level_msgs.items():
logger.log(level=level, msg=msg)
@@ -244,7 +241,6 @@ def test_log_dispatcher_warning_logging(
# Log test messages to log dispatcher loggers
for logger in [log_dispatcher.logger_stdout, log_dispatcher.logger_stderr]:
-
# Log messages
for level, msg in log_level_msgs.items():
logger.log(level=level, msg=msg)
@@ -291,7 +287,6 @@ def test_log_to_file(
# Log test messages to log dispatcher loggers
for logger in [log_dispatcher.logger_stdout, log_dispatcher.logger_stderr]:
-
# Log messages
for level, msg in log_level_msgs.items():
logger.log(level=level, msg=msg)
@@ -338,7 +333,6 @@ def test_no_color_on_console(
# Log test messages to log dispatcher loggers
for logger in [log_dispatcher.logger_stdout, log_dispatcher.logger_stderr]:
-
# Log messages
for level, msg in log_level_msgs.items():
logger.log(level=level, msg=msg)
diff --git a/cotainr/tests/tracing/test_message_spinner.py b/cotainr/tests/tracing/test_message_spinner.py
index 26f7f31..cd18102 100644
--- a/cotainr/tests/tracing/test_message_spinner.py
+++ b/cotainr/tests/tracing/test_message_spinner.py
@@ -6,6 +6,7 @@
- see the LICENSE file for details.
"""
+
import io
import shutil
import time
diff --git a/cotainr/tests/tracing/test_stream_write_proxy.py b/cotainr/tests/tracing/test_stream_write_proxy.py
index 95e9385..8c79ca1 100644
--- a/cotainr/tests/tracing/test_stream_write_proxy.py
+++ b/cotainr/tests/tracing/test_stream_write_proxy.py
@@ -6,6 +6,7 @@
- see the LICENSE file for details.
"""
+
import io
import pytest
diff --git a/cotainr/tracing.py b/cotainr/tracing.py
index 314a83f..d9b9039 100644
--- a/cotainr/tracing.py
+++ b/cotainr/tracing.py
@@ -27,6 +27,7 @@
console_lock
The lock to acquire for manipulating the console messages.
"""
+
import builtins
import copy
import contextlib
@@ -505,8 +506,7 @@ def __init__(self, *, msg, stream):
self._print_width = (
# account for leading spinner + whitespace (2 chars)
# and trailing dots (3 chars)
- shutil.get_terminal_size()[0]
- - 5
+ shutil.get_terminal_size()[0] - 5
)
self._ansi_escape_re = re.compile(
# Based on r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])" from
@@ -525,7 +525,7 @@ def __init__(self, *, msg, stream):
# \033[2K erases the old line to avoid the extra two characters at the
# end of the line stemming from the shift of the line due to the
# leading spinner character and a whitespace
- self._clear_line_code = "\r" + "\x1B[2K"
+ self._clear_line_code = "\r" + "\x1b[2K"
self._reset_SGR = "\x1b[0m"
def start(self):
diff --git a/doc/create_switcher.py b/doc/create_switcher.py
index 3068e9d..ef55011 100644
--- a/doc/create_switcher.py
+++ b/doc/create_switcher.py
@@ -11,6 +11,7 @@
The switcher will contain the latest 4 releases. This should be run before making a tag for the latest version.
More information about releasing can be found here: https://cotainr.readthedocs.io/en/latest/development/releasing.html
"""
+
import json
from pathlib import Path
from re import match
From 5b32c02d83435cc352fbc47244e41b2a2efedf67 Mon Sep 17 00:00:00 2001
From: Aarni Koskela
Date: Thu, 25 Jul 2024 09:29:22 +0300
Subject: [PATCH 4/7] Fix Ruff complaints
---
cotainr/cli.py | 2 +-
cotainr/tests/cli/test_build.py | 4 ++--
cotainr/tests/cli/test_cli_utils.py | 5 ++++-
cotainr/tests/container/test_singularity_sandbox.py | 2 +-
cotainr/tests/pack/stubs.py | 6 ++++--
cotainr/tests/pack/test_conda_install.py | 2 +-
cotainr/tests/tracing/test_stream_write_proxy.py | 2 +-
doc/create_switcher.py | 2 +-
8 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/cotainr/cli.py b/cotainr/cli.py
index f049904..1d85608 100644
--- a/cotainr/cli.py
+++ b/cotainr/cli.py
@@ -51,7 +51,7 @@
class CotainrSubcommand(ABC):
"""Abstract base class for `CotainrCLI` subcommands."""
- @classmethod
+ @classmethod # noqa: B027
def add_arguments(cls, *, parser):
"""
Add command line arguments to arguments `parser`.
diff --git a/cotainr/tests/cli/test_build.py b/cotainr/tests/cli/test_build.py
index 6a8fe62..94aa2b0 100644
--- a/cotainr/tests/cli/test_build.py
+++ b/cotainr/tests/cli/test_build.py
@@ -190,7 +190,7 @@ def test_specifying_both_system_and_base_image(self, capsys):
Build.add_arguments(parser=parser)
with pytest.raises(SystemExit):
- args = parser.parse_args(
+ parser.parse_args(
args=shlex.split(
f"{image_path} --system {system} --base-image {base_image}"
)
@@ -275,7 +275,7 @@ def test_specifying_both_verbose_and_quiet(self, capsys):
base_image = "some_base_image_6021"
with pytest.raises(SystemExit):
- args = parser.parse_args(
+ parser.parse_args(
args=shlex.split(
f"{image_path} --base-image={base_image} --verbose --quiet"
)
diff --git a/cotainr/tests/cli/test_cli_utils.py b/cotainr/tests/cli/test_cli_utils.py
index 0d4a655..17aa91f 100644
--- a/cotainr/tests/cli/test_cli_utils.py
+++ b/cotainr/tests/cli/test_cli_utils.py
@@ -57,8 +57,11 @@ def test_description_whitespace_strip(self):
Parameters
----------
some_arg : str
- Much leading and trailing space
+ Much leading and trailing space
"""
+ # Doctor the docstring a little here, so editors etc. which eagerly
+ # strip whitespace don't mess up the test.
+ docstring = docstring.replace("space", "space" + " " * 10)
help_msg = _extract_help_from_docstring(arg="some_arg", docstring=docstring)
assert help_msg == "much leading and trailing space"
diff --git a/cotainr/tests/container/test_singularity_sandbox.py b/cotainr/tests/container/test_singularity_sandbox.py
index 8168353..e73ebbd 100644
--- a/cotainr/tests/container/test_singularity_sandbox.py
+++ b/cotainr/tests/container/test_singularity_sandbox.py
@@ -43,7 +43,7 @@ def test_setup_log_dispatcher(self):
class TestContext:
def test_add_verbosity_arg(self, capsys, patch_disable_stream_subprocess):
- with SingularitySandbox(base_image="my_base_image_6021") as sandbox:
+ with SingularitySandbox(base_image="my_base_image_6021"):
pass
stdout_lines = capsys.readouterr().out.rstrip("\n").split("\n")
assert "args=['singularity', '-q', " in stdout_lines[0]
diff --git a/cotainr/tests/pack/stubs.py b/cotainr/tests/pack/stubs.py
index d55e12d..5a0bf32 100644
--- a/cotainr/tests/pack/stubs.py
+++ b/cotainr/tests/pack/stubs.py
@@ -11,13 +11,15 @@
class StubLicensePopen(ABC):
- def __init__(self, args, stdin=None, stdout=None, text=None):
+ license_text: str
+
+ def __init__(self, args, stdin=None, stdout=None, text=None): # noqa: B027
pass
def __enter__(self):
return self
- def __exit__(self, exc_type, exc_value, traceback):
+ def __exit__(self, exc_type, exc_value, traceback): # noqa: B027
pass
def communicate(self, input=None):
diff --git a/cotainr/tests/pack/test_conda_install.py b/cotainr/tests/pack/test_conda_install.py
index c6005c8..d27337e 100644
--- a/cotainr/tests/pack/test_conda_install.py
+++ b/cotainr/tests/pack/test_conda_install.py
@@ -393,7 +393,7 @@ def test_miniforge_still_showing_license(
):
monkeypatch.setattr("builtins.input", factory_mock_input("yes"))
with SingularitySandbox(base_image="my_base_image_6021") as sandbox:
- conda_install = CondaInstall(sandbox=sandbox)
+ CondaInstall(sandbox=sandbox)
stdout = capsys.readouterr().out.strip()
diff --git a/cotainr/tests/tracing/test_stream_write_proxy.py b/cotainr/tests/tracing/test_stream_write_proxy.py
index 8c79ca1..44310dd 100644
--- a/cotainr/tests/tracing/test_stream_write_proxy.py
+++ b/cotainr/tests/tracing/test_stream_write_proxy.py
@@ -47,7 +47,7 @@ def test_failing_attribute_lookup_delegation(self):
AttributeError,
match=r"StringIO' object has no attribute 'some_nonsense_attribute_6021'$",
):
- stream_write_proxy.some_nonsense_attribute_6021
+ _ = stream_write_proxy.some_nonsense_attribute_6021
def test_successful_attribute_lookup_delegation(self):
stream = io.StringIO("some text 6021")
diff --git a/doc/create_switcher.py b/doc/create_switcher.py
index ef55011..7006706 100644
--- a/doc/create_switcher.py
+++ b/doc/create_switcher.py
@@ -30,7 +30,7 @@
).stdout.splitlines()
)
# Checking if the tag matches the versioning scheme YYYY.MM.MINOR
- if match("^20[0-9]{2}\.(0[1-9]|10|11|12)\.[0-9]+$", tag) is not None
+ if match(r"^20[0-9]{2}\.(0[1-9]|10|11|12)\.[0-9]+$", tag) is not None
]
tags.reverse()
From 568a30035691535ecb68def5c1ca07c0ab0425e8 Mon Sep 17 00:00:00 2001
From: Aarni Koskela
Date: Thu, 25 Jul 2024 09:32:44 +0300
Subject: [PATCH 5/7] Auto-fix file and line endings
---
.coveragerc | 2 +-
.gitignore | 2 +-
LICENSE | 2 +-
doc/_static/switcher.json | 2 +-
doc/_templates/cotainr_footer.html | 2 +-
doc/development/cli_internals.rst | 2 +-
doc/development/documentation.rst | 2 +-
doc/development/systems.rst | 2 +-
doc/development/tracing_logging.rst | 4 ++--
doc/index.rst | 4 ++--
doc/user_guide/conda_env.rst | 4 ++--
doc/user_guide/index.rst | 2 +-
docs-requirements.txt | 2 +-
pytest.ini | 2 +-
test-requirements.txt | 2 +-
15 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/.coveragerc b/.coveragerc
index 979bfe1..d46d9e4 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -1,4 +1,4 @@
[run]
source = cotainr
omit = cotainr/tests/*
-branch = True
\ No newline at end of file
+branch = True
diff --git a/.gitignore b/.gitignore
index a560add..7e5534e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -140,4 +140,4 @@ dmypy.json
# Sphinx
_build/
-systems.json
\ No newline at end of file
+systems.json
diff --git a/LICENSE b/LICENSE
index c29ce2f..4153cd3 100644
--- a/LICENSE
+++ b/LICENSE
@@ -284,4 +284,4 @@ the rights granted in Article 2 of this Licence and protect the covered Source
Code from exclusive appropriation.
All other changes or additions to this Appendix require the production of a new
-EUPL version.
\ No newline at end of file
+EUPL version.
diff --git a/doc/_static/switcher.json b/doc/_static/switcher.json
index d9fdcc9..0b03206 100644
--- a/doc/_static/switcher.json
+++ b/doc/_static/switcher.json
@@ -24,4 +24,4 @@
"version": "2022.12.0",
"url": "https://cotainr.readthedocs.io/en/2022.12.0/"
}
-]
\ No newline at end of file
+]
diff --git a/doc/_templates/cotainr_footer.html b/doc/_templates/cotainr_footer.html
index bcdbbcf..c39c9da 100644
--- a/doc/_templates/cotainr_footer.html
+++ b/doc/_templates/cotainr_footer.html
@@ -1,3 +1,3 @@
© Copyright {{copyright}}. See Licensing Information.
-
\ No newline at end of file
+
diff --git a/doc/development/cli_internals.rst b/doc/development/cli_internals.rst
index 2fb3807..6be6311 100644
--- a/doc/development/cli_internals.rst
+++ b/doc/development/cli_internals.rst
@@ -24,7 +24,7 @@ The command line interface is implemented in the :mod:`cotainr.cli` module. It c
The :class:`~cotainr.cli.CotainrCLI` class is intended to be used as:
.. code-block:: python
-
+
cli = CotainrCLI()
cli.subcommand.execute()
diff --git a/doc/development/documentation.rst b/doc/development/documentation.rst
index 90e261f..e6562d4 100644
--- a/doc/development/documentation.rst
+++ b/doc/development/documentation.rst
@@ -28,4 +28,4 @@ In order to build the HTML version of the documentation, you must have the Pytho
.. include:: ../../docs-requirements.txt
:literal:
-Also, in order to run the above :code:`make` commands, you muse have `make `_ installed.
\ No newline at end of file
+Also, in order to run the above :code:`make` commands, you muse have `make `_ installed.
diff --git a/doc/development/systems.rst b/doc/development/systems.rst
index c3af2d3..ec1ebf6 100644
--- a/doc/development/systems.rst
+++ b/doc/development/systems.rst
@@ -28,4 +28,4 @@ The base image path can be one of the support targets for `apptainer/singularity
"another-system-name": {
"base-image": "/path/to/file/on/system"
}
- }
\ No newline at end of file
+ }
diff --git a/doc/development/tracing_logging.rst b/doc/development/tracing_logging.rst
index 25cc82c..928a349 100644
--- a/doc/development/tracing_logging.rst
+++ b/doc/development/tracing_logging.rst
@@ -46,7 +46,7 @@ This setup is primarily implemented by two classes :class:`cotainr.tracing.LogDi
- Apply any filters to modify and/or remove log messages.
- Add colored console output based on log level, as implemented in :class:`cotainr.tracing.ColoredOutputFormatter`, if requested.
-The :class:`~cotainr.tracing.LogDispatcher` then defines methods :meth:`~cotainr.tracing.LogDispatcher.log_to_stdout` and :meth:`~cotainr.tracing.LogDispatcher.log_to_stderr` which may be used with subprocesses to log to `stdout` / `stderr`, respectively, at a message log level determined by the provided `map_log_level_func` function.
+The :class:`~cotainr.tracing.LogDispatcher` then defines methods :meth:`~cotainr.tracing.LogDispatcher.log_to_stdout` and :meth:`~cotainr.tracing.LogDispatcher.log_to_stderr` which may be used with subprocesses to log to `stdout` / `stderr`, respectively, at a message log level determined by the provided `map_log_level_func` function.
In order to take advantage of this machinery, CLI subcommands must:
@@ -94,4 +94,4 @@ Adding a console spinner
~~~~~~~~~~~~~~~~~~~~~~~~
For a given block of code, a spinner may be added to any console output to `stdout` / `stderr` from that code by running it in a :class:`cotainr.tracing.ConsoleSpinner` context.
-The spinner is implemented in the :class:`cotainr.tracing.MessageSpinner` class which manages a separate thread updating the spinner for each individual message. Within the :class:`~cotainr.tracing.ConsoleSpinner` context, the spinning message is updated by monkey patching :py:meth:`sys.stdout.write`/:py:meth:`std.stderr.write` with :class:`cotainr.tracing.StreamWriteProxy` wrappers that make sure to update the spinning message whenever something is written to :py:data:`sys.stdout`/:py:data:`sys.stderr`.
\ No newline at end of file
+The spinner is implemented in the :class:`cotainr.tracing.MessageSpinner` class which manages a separate thread updating the spinner for each individual message. Within the :class:`~cotainr.tracing.ConsoleSpinner` context, the spinning message is updated by monkey patching :py:meth:`sys.stdout.write`/:py:meth:`std.stderr.write` with :class:`cotainr.tracing.StreamWriteProxy` wrappers that make sure to update the spinning message whenever something is written to :py:data:`sys.stdout`/:py:data:`sys.stderr`.
diff --git a/doc/index.rst b/doc/index.rst
index 1236356..e83490b 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -13,7 +13,7 @@ cotainr documentation
:maxdepth: 1
:hidden:
:includehidden:
-
+
Getting Started
User Guide
API reference
@@ -92,4 +92,4 @@ cotainr documentation
To the development guide
.. _Apptainer: https://apptainer.org/
-.. _Singularity: https://sylabs.io/singularity/
\ No newline at end of file
+.. _Singularity: https://sylabs.io/singularity/
diff --git a/doc/user_guide/conda_env.rst b/doc/user_guide/conda_env.rst
index c5a50f8..930caa1 100644
--- a/doc/user_guide/conda_env.rst
+++ b/doc/user_guide/conda_env.rst
@@ -18,7 +18,7 @@ As an example, consider the following `conda environment file =1.5.0
pydata-sphinx-theme>=0.13.3
sphinx>=7.2.5
sphinx-design>=0.5.0
-myst-parser>=2.0.0
\ No newline at end of file
+myst-parser>=2.0.0
diff --git a/pytest.ini b/pytest.ini
index 773daf8..933f94c 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -6,4 +6,4 @@ junit_suite_name = cotainr_test_suite
markers =
conda_integration: marks tests of integration with conda/mamba
endtoend: marks end-to-end test cases
- singularity_integration: marks tests of integration with singularity
\ No newline at end of file
+ singularity_integration: marks tests of integration with singularity
diff --git a/test-requirements.txt b/test-requirements.txt
index e816bc6..8a7523c 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,2 +1,2 @@
pytest>=6.0
-pytest-cov>=2.10
\ No newline at end of file
+pytest-cov>=2.10
From 5689dcd1ad6fc5802f82862a53650c9020494a9a Mon Sep 17 00:00:00 2001
From: Aarni Koskela
Date: Fri, 6 Sep 2024 13:25:41 +0300
Subject: [PATCH 6/7] Apply suggestions from code review
Co-authored-by: Christian Schou Oxvig <5367057+Chroxvi@users.noreply.github.com>
---
.github/workflows/CI_pull_request.yml | 5 +++--
.github/workflows/CI_push.yml | 5 +++--
ruff.toml | 15 ++++++++-------
3 files changed, 14 insertions(+), 11 deletions(-)
diff --git a/.github/workflows/CI_pull_request.yml b/.github/workflows/CI_pull_request.yml
index 9219e4b..8fb5076 100644
--- a/.github/workflows/CI_pull_request.yml
+++ b/.github/workflows/CI_pull_request.yml
@@ -6,13 +6,14 @@ on:
schedule:
- cron: '15 2 * * TUE' # Also run the full test suite for PRs on the main branch every Tuesday night
jobs:
- lint:
+ lint-and-format:
+ name: Lint and check formatting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
- python-version: "3.12"
+ python-version: "3.x"
- uses: pre-commit/action@v3.0.1
run-full-test-suite:
name: Run full test suite
diff --git a/.github/workflows/CI_push.yml b/.github/workflows/CI_push.yml
index c38c853..8c3b3cc 100644
--- a/.github/workflows/CI_push.yml
+++ b/.github/workflows/CI_push.yml
@@ -6,13 +6,14 @@ on:
tags-ignore:
- "**"
jobs:
- lint:
+ lint-and-format:
+ name: Lint and check formatting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
- python-version: "3.12"
+ python-version: "3.x"
- uses: pre-commit/action@v3.0.1
run-unittests:
name: Run unittests
diff --git a/ruff.toml b/ruff.toml
index b478543..1468a37 100644
--- a/ruff.toml
+++ b/ruff.toml
@@ -2,11 +2,11 @@ target-version = "py38"
line-length = 88
[lint]
-extend-select = [
- "B",
- "E",
- "F",
- "W",
+select = [
+ "B", # flake8-bugbear
+ "E", # pycodestyle errors
+ "F", # Pyflakes
+ "W", # pycodestyle warnings
]
ignore = [
"E501", # ignore line length; auto-formatting will take care of what it can.
@@ -20,8 +20,6 @@ exclude = [
# The non-standard way to import py.test fixtures in
# tests means we will need to ignore "imported but not used"
# as well as argument shadowing tests in test modules.
- # It would be better to use standard `conftest` style fixture
- # discovery.
"F401",
"F811",
]
@@ -29,6 +27,9 @@ exclude = [
"S101", # assertions are OK in tests
]
+[lint.pydocstyle]
+convention = "numpy"
+
[format]
exclude = [
"examples/**/*.ipynb",
From 5b3f210e3af83a25b828f5dc6d31d762e36ea2a6 Mon Sep 17 00:00:00 2001
From: Aarni Koskela
Date: Tue, 10 Sep 2024 19:16:48 +0300
Subject: [PATCH 7/7] Document CI linting/formatting checks
---
doc/development/style_guide.rst | 17 +++++++++++++++++
doc/development/test_suite_ci_cd.rst | 9 +++++++--
2 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/doc/development/style_guide.rst b/doc/development/style_guide.rst
index 0910989..0ddd28e 100644
--- a/doc/development/style_guide.rst
+++ b/doc/development/style_guide.rst
@@ -20,3 +20,20 @@ Specifically, we use the following conventions:
- Force keyword only arguments to functions and methods.
- Use relative imports within the `cotainr` package when importing functionality from other modules. The imports must be done such that all objects imported are still references (not copies) which allows for monkey patching objects in their definition module in tests.
+
+pre-commit
+----------
+
+If you like, you can use `pre-commit `_ to automatically check and
+format your code as a git pre-commit hook. A simple way to install pre-commit and set up the
+hooks is to run something like
+
+.. code-block:: bash
+
+ pip install pre-commit
+ pre-commit install
+
+but this varies depending on your system and setup.
+
+The pre-commit configuration is stored in ``.pre-commit-config.yaml`` in the root of the repository.
+The `CI/CD workflow `_ uses the same pre-commit configuration for linting and formatting.
diff --git a/doc/development/test_suite_ci_cd.rst b/doc/development/test_suite_ci_cd.rst
index 9a85167..3293043 100644
--- a/doc/development/test_suite_ci_cd.rst
+++ b/doc/development/test_suite_ci_cd.rst
@@ -76,8 +76,13 @@ CI workflows
~~~~~~~~~~~~
The following CI `workflows `_ are implemented:
-- `CI_pull_requests.yml `_: Runs the unit tests, integration tests, and end-to-end tests on pull requests to the *main* branch. *All* Python versions and *stable* Singularity as well as *stable* Apptainer versions are tested.
-- `CI_push.yml `_: Runs the unit tests on pushes to all branches. Restricted to *stable* and *latest* Python versions.
+- `CI_pull_requests.yml `_:
+ Runs the unit tests, integration tests, and end-to-end tests on pull requests to the *main* branch.
+ *All* Python versions and *stable* Singularity as well as *stable* Apptainer versions are tested.
+ Lint and formatting checks (as described in the :ref:`style guide `) are also run and enforced.
+- `CI_push.yml `_:
+ Runs the unit tests on pushes to all branches. Restricted to *stable* and *latest* Python versions.
+ Lint and formatting checks (as described in the :ref:`style guide `) are also run and enforced.
.. _continuous_delivery: