From e97b72c1ca8178f12966a11e3af4dc395ed24b71 Mon Sep 17 00:00:00 2001 From: Douglas Cerna Date: Thu, 20 Jun 2024 08:22:09 -0600 Subject: [PATCH] Use Ruff for Python linting and formatting This replaces the black, pyupgrade, reorder-python-imports and flake8 hooks in pre-commit with ruff. --- .flake8 | 16 ---------------- .pre-commit-config.yaml | 26 +++++--------------------- amclient/amclient.py | 17 ++++++----------- amclient/amclientargs.py | 4 +--- amclient/defaults.py | 3 +-- amclient/utils.py | 2 +- pyproject.toml | 20 ++++++++++++++++++++ requirements-dev.txt | 2 ++ tests/test_amclient.py | 19 +++++-------------- 9 files changed, 41 insertions(+), 68 deletions(-) delete mode 100644 .flake8 diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 28052a5..0000000 --- a/.flake8 +++ /dev/null @@ -1,16 +0,0 @@ -[flake8] -exclude = .tox, .git, __pycache__, .cache, build, dist, *.pyc, *.egg-info, .eggs -# Error codes: -# - https://flake8.pycqa.org/en/latest/user/error-codes.html -# - https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes -# - https://github.com/PyCQA/flake8-bugbear#list-of-warnings -# -# E203: whitespace before `,`, `;` or `:` -# E402: module level import not at top of file -# E501: line too long -# W503: line break before binary operator -ignore = - E203, - E402, - E501, - W503 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ad50ca7..9f8d1c1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,26 +1,10 @@ repos: -- repo: https://github.com/asottile/pyupgrade - rev: v3.16.0 +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.4.9 hooks: - - id: pyupgrade - args: [--py38-plus] -- repo: https://github.com/asottile/reorder_python_imports - rev: v3.13.0 - hooks: - - id: reorder-python-imports - args: [--py38-plus] -- repo: https://github.com/psf/black - rev: "23.12.1" - hooks: - - id: black - args: [--safe, --quiet] -- repo: https://github.com/pycqa/flake8 - rev: "7.1.0" - hooks: - - id: flake8 - additional_dependencies: - - flake8-bugbear==24.4.26 - - flake8-comprehensions==3.14.0 + - id: ruff + args: [--fix, --exit-non-zero-on-fix] + - id: ruff-format - repo: https://github.com/igorshubovych/markdownlint-cli rev: v0.41.0 hooks: diff --git a/amclient/amclient.py b/amclient/amclient.py index b0361dc..72109e7 100755 --- a/amclient/amclient.py +++ b/amclient/amclient.py @@ -4,6 +4,7 @@ Module and CLI that holds functionality for interacting with the various Archivematica APIs. """ + import base64 import binascii import io @@ -218,7 +219,7 @@ def _close_completed_units(self, unit_type): if _completed_units is None: msg = ( "Something went wrong when attempting to retrieve the" - " completed {}s.".format(unit_type) + f" completed {unit_type}s." ) LOGGER.warning(msg) else: @@ -500,9 +501,7 @@ def get_processing_config(self, assume_json=False): return the default processing config from the AM server. """ return utils._call_url_json( - "{}/api/processing-configuration/{}".format( - self.am_url, self.processing_config - ), + f"{self.am_url}/api/processing-configuration/{self.processing_config}", headers=self._am_auth_headers(), assume_json=assume_json, ) @@ -682,9 +681,7 @@ def validate_csv(self, validator, file_obj): url = f"{self.am_url}/api/v2beta/validate/{validator}/" if not (isinstance(file_obj, io.TextIOBase) or hasattr(file_obj, "read")): raise TypeError( - "Expected an io.TextIOWrapper file object but got {} instead".format( - type(file_obj) - ) + f"Expected an io.TextIOWrapper file object but got {type(file_obj)} instead" ) data = file_obj.read() headers = self._am_auth_headers() @@ -720,9 +717,7 @@ def extract_file(self): then the stream contents are output to the console. """ self.output_mode = "" # TODO: don't overwrite mode - url = "{}/api/v2/file/{}/extract_file/?relative_path_to_file={}".format( - self.ss_url, self.package_uuid, self.relative_path - ) + url = f"{self.ss_url}/api/v2/file/{self.package_uuid}/extract_file/?relative_path_to_file={self.relative_path}" response = requests.get(url, params=self._ss_auth(), stream=True) if getattr(self, "stream", None): if getattr(self, "cli_call", None): @@ -787,7 +782,7 @@ def list_location_purposes(self): def create_location(self): """Create a new location in the Storage Service.""" - if not self.location_purpose.upper() in self.list_location_purposes(): + if self.location_purpose.upper() not in self.list_location_purposes(): return { "error": "location purpose not permitted", "valid_purposes": self.list_location_purposes(), diff --git a/amclient/amclientargs.py b/amclient/amclientargs.py index c1915a4..1eb9e4c 100644 --- a/amclient/amclientargs.py +++ b/amclient/amclientargs.py @@ -113,9 +113,7 @@ PROCESSING_CONFIG = Opt( name="processing-config", metavar="PROCESSING", - help="Processing configuration. Default: {}".format( - defaults.DEFAULT_PROCESSING_CONFIG - ), + help=f"Processing configuration. Default: {defaults.DEFAULT_PROCESSING_CONFIG}", default=defaults.DEFAULT_PROCESSING_CONFIG, type=None, ) diff --git a/amclient/defaults.py b/amclient/defaults.py index b66e1fc..6cdd961 100644 --- a/amclient/defaults.py +++ b/amclient/defaults.py @@ -3,7 +3,6 @@ import re from tempfile import mkdtemp - DEF_AM_URL = "http://127.0.0.1:62080" DEF_SS_URL = "http://127.0.0.1:62081" DEF_USER_NAME = "test" @@ -13,7 +12,7 @@ UNDECODABLE = "UNABLE TO DECODE" UNDEC_MSG = ( "Unable to decode a transfer source component; giving up and" - " returning {}".format(UNDECODABLE) + f" returning {UNDECODABLE}" ) # Global for logfile if not set. diff --git a/amclient/utils.py b/amclient/utils.py index 6719c08..06e0936 100644 --- a/amclient/utils.py +++ b/amclient/utils.py @@ -1,4 +1,5 @@ """Where you put stuff when you can't think of a good name for a module.""" + import logging from pathlib import Path @@ -7,7 +8,6 @@ from . import errors - LOGGER = logging.getLogger("amclient") METHOD_GET = "GET" diff --git a/pyproject.toml b/pyproject.toml index 744aa9e..2cd0154 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,12 +61,32 @@ dev = [ "pip-tools", "pytest-cov", "pytest", + "ruff", ] [tool.setuptools.dynamic] version = {attr = "amclient.version.__version__"} readme = {file = ["README.md"], content-type = "text/markdown"} +[tool.ruff.lint] +# Rule reference: https://docs.astral.sh/ruff/rules/ +select = [ + "B", + "C4", + "E", + "F", + "I", + "UP", + "W", +] +ignore = [ + "B904", + "E501", +] + +[tool.ruff.lint.isort] +force-single-line = true + [tool.pytest.ini_options] python_files = [ "test_*.py", diff --git a/requirements-dev.txt b/requirements-dev.txt index 925e4ff..b9d30dd 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -44,6 +44,8 @@ pytest-cov==5.0.0 # via amclient (pyproject.toml) requests==2.32.3 # via amclient (pyproject.toml) +ruff==0.4.9 + # via amclient (pyproject.toml) tomli==2.0.1 # via # build diff --git a/tests/test_amclient.py b/tests/test_amclient.py index 7157f16..44a6f81 100644 --- a/tests/test_amclient.py +++ b/tests/test_amclient.py @@ -13,7 +13,6 @@ from amclient import amclient from amclient import errors - AM_URL = "http://192.168.168.192" SS_URL = "http://192.168.168.192:8000" AM_USER_NAME = "test" @@ -1822,11 +1821,9 @@ def test_reingest_aip(call_url: mock.Mock): message = response["message"] assert error is False assert message == ( - "Package {aip_uuid} sent " + f"Package {aip_uuid} sent " "to pipeline Archivematica on 4e2f66a7a29f " - "({pipeline_uuid}) for re-ingest".format( - aip_uuid=aip_uuid, pipeline_uuid=pipeline_uuid - ) + f"({pipeline_uuid}) for re-ingest" ) assert call_url.mock_calls == [ @@ -2399,9 +2396,7 @@ def test_extract_individual_file(requests_get: mock.Mock, tmp_path): tmp_dir.mkdir() filename_to_test = "bird.mp3" package_uuid = "2ad1bf0d-23fa-44e0-a128-9feadfe22c42" - path = "amclient-transfer_1-{}/data/objects/{}".format( - package_uuid, filename_to_test - ) + path = f"amclient-transfer_1-{package_uuid}/data/objects/{filename_to_test}" filename = "bird_download.mp3" response = amclient.AMClient( ss_api_key=SS_API_KEY, @@ -2454,9 +2449,7 @@ def test_extract_and_stream_individual_file(requests_get: mock.Mock, tmp_path): tmp_dir.mkdir() filename_to_test = "bird.mp3" package_uuid = "2ad1bf0d-23fa-44e0-a128-9feadfe22c42" - path = "amclient-transfer_1-{}/data/objects/{}".format( - package_uuid, filename_to_test - ) + path = f"amclient-transfer_1-{package_uuid}/data/objects/{filename_to_test}" response = amclient.AMClient( ss_api_key=SS_API_KEY, ss_user_name=SS_USER_NAME, @@ -2509,9 +2502,7 @@ def test_extract_and_stream_individual_file_cli( tmp_dir.mkdir() filename_to_test = "bird.mp3" package_uuid = "2ad1bf0d-23fa-44e0-a128-9feadfe22c42" - path = "amclient-transfer_1-{}/data/objects/{}".format( - package_uuid, filename_to_test - ) + path = f"amclient-transfer_1-{package_uuid}/data/objects/{filename_to_test}" amclient.AMClient( ss_api_key=SS_API_KEY, ss_user_name=SS_USER_NAME,