Skip to content

Commit

Permalink
Use Ruff for Python linting and formatting
Browse files Browse the repository at this point in the history
This replaces the black, pyupgrade, reorder-python-imports and flake8
hooks in pre-commit with ruff.
  • Loading branch information
replaceafill authored Jun 20, 2024
1 parent 7962be8 commit 549c786
Show file tree
Hide file tree
Showing 19 changed files with 85 additions and 109 deletions.
16 changes: 0 additions & 16 deletions .flake8

This file was deleted.

26 changes: 5 additions & 21 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -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:
Expand Down
9 changes: 5 additions & 4 deletions metsrw/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
"""METS reader and writer."""

import logging

from . import plugins
from .di import Dependency
from .di import feature_broker
from .di import FeatureBroker
from .di import feature_broker
from .di import has_class_methods
from .di import has_methods
from .di import is_class
Expand All @@ -20,18 +21,18 @@
from .metadata import SubSection
from .mets import METSDocument
from .utils import FILE_ID_PREFIX
from .utils import generate_mdtype_key
from .utils import GROUP_ID_PREFIX
from .utils import lxmlns
from .utils import NAMESPACES
from .utils import SCHEMA_LOCATIONS
from .utils import generate_mdtype_key
from .utils import lxmlns
from .utils import urldecode
from .utils import urlencode
from .validate import AM_PNTR_SCT_PATH
from .validate import AM_SCT_PATH
from .validate import METS_XSD_PATH
from .validate import get_schematron
from .validate import get_xmlschema
from .validate import METS_XSD_PATH
from .validate import report_string
from .validate import schematron_validate
from .validate import sct_report_string
Expand Down
11 changes: 6 additions & 5 deletions metsrw/di.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
See http://code.activestate.com/recipes/413268/
"""

from .plugins import premisrw


Expand All @@ -39,9 +40,9 @@ def provide(self, feature_name, provider, *args, **kwargs):
provider if it is callable.
"""
if not self.allow_replace:
assert feature_name not in self.providers, "Duplicate feature: {!r}".format(
feature_name
)
assert (
feature_name not in self.providers
), f"Duplicate feature: {feature_name!r}"
if callable(provider) and not isinstance(provider, type):
self.providers[feature_name] = lambda: provider(*args, **kwargs)
else:
Expand Down Expand Up @@ -96,8 +97,8 @@ def __get__(self, instance, owner):
obj = feature_broker[self.dependency_name]
for assertion in self.assertions:
assert assertion(obj), (
"The value {!r} of {!r} does not match the specified"
" criteria".format(obj, self.dependency_name)
f"The value {obj!r} of {self.dependency_name!r} does not match the specified"
" criteria"
)
return obj

Expand Down
10 changes: 4 additions & 6 deletions metsrw/fsentry.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,12 +184,10 @@ def from_fptr(cls, label, type_, fptr):
)

def __str__(self):
return "{s.type}: {s.path}".format(s=self)
return f"{self.type}: {self.path}"

def __repr__(self):
return "FSEntry(type={s.type!r}, path={s.path!r}, use={s.use!r}, label={s.label!r}, file_uuid={s.file_uuid!r}, checksum={s.checksum!r}, checksumtype={s.checksumtype!r}, fileid={s._fileid!r})".format(
s=self
)
return f"FSEntry(type={self.type!r}, path={self.path!r}, use={self.use!r}, label={self.label!r}, file_uuid={self.file_uuid!r}, checksum={self.checksum!r}, checksumtype={self.checksumtype!r}, fileid={self._fileid!r})"

# PROPERTIES

Expand Down Expand Up @@ -439,8 +437,8 @@ def serialize_filesec(self):
flocat.set(utils.lxmlns("xlink") + "href", utils.urlencode(self.path))
except ValueError:
raise exceptions.SerializeError(
'Value "{}" (for attribute xlink:href) is not a valid'
" URL.".format(self.path)
f'Value "{self.path}" (for attribute xlink:href) is not a valid'
" URL."
)
flocat.set("LOCTYPE", "OTHER")
flocat.set("OTHERLOCTYPE", "SYSTEM")
Expand Down
17 changes: 6 additions & 11 deletions metsrw/metadata.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Classes for metadata sections of the METS. Include amdSec, dmdSec, techMD, rightsMD, sourceMD, digiprovMD, mdRef and mdWrap.
"""

import copy
import logging

Expand All @@ -9,7 +10,6 @@
from . import exceptions
from . import utils


LOGGER = logging.getLogger(__name__)


Expand Down Expand Up @@ -142,9 +142,7 @@ def parse(cls, element):
"""
if element.tag != cls.ALT_RECORD_ID_TAG:
raise exceptions.ParseError(
"AltRecordID got unexpected tag {}; expected {}".format(
element.tag, cls.ALT_RECORD_ID_TAG
)
f"AltRecordID got unexpected tag {element.tag}; expected {cls.ALT_RECORD_ID_TAG}"
)

return cls(element.text, id=element.get("ID"), type=element.get("TYPE"))
Expand Down Expand Up @@ -211,9 +209,7 @@ def parse(cls, element):
"""
if element.tag != cls.AGENT_TAG:
raise exceptions.ParseError(
"Agent got unexpected tag {}; expected {}".format(
element.tag, cls.AGENT_TAG
)
f"Agent got unexpected tag {element.tag}; expected {cls.AGENT_TAG}"
)

role = element.get("ROLE")
Expand Down Expand Up @@ -477,8 +473,7 @@ def parse(cls, root):
target = utils.urldecode(target)
except ValueError:
raise exceptions.ParseError(
'Value "{}" (of attribute xlink:href) is not a valid'
" URL.".format(target)
f'Value "{target}" (of attribute xlink:href) is not a valid' " URL."
)
loctype = root.get("LOCTYPE")
if not loctype:
Expand Down Expand Up @@ -516,8 +511,8 @@ def serialize(self):
el.attrib[utils.lxmlns("xlink") + "href"] = utils.urlencode(self.target)
except ValueError:
raise exceptions.SerializeError(
'Value "{}" (for attribute xlink:href) is not a valid'
" URL.".format(self.target)
f'Value "{self.target}" (for attribute xlink:href) is not a valid'
" URL."
)
el.attrib["MDTYPE"] = self.mdtype
el.attrib["LOCTYPE"] = self.loctype
Expand Down
6 changes: 2 additions & 4 deletions metsrw/mets.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import logging
import os
import sys
from collections import namedtuple
from collections import OrderedDict
from collections import namedtuple
from datetime import datetime

from lxml import etree
Expand All @@ -12,7 +12,6 @@
from . import metadata
from . import utils


LOGGER = logging.getLogger(__name__)

AIP_ENTRY_TYPE = "archival information package"
Expand Down Expand Up @@ -485,8 +484,7 @@ def _analyze_fptr(fptr_elem, tree, entry_type):
path = utils.urldecode(path)
except ValueError:
raise exceptions.ParseError(
'Value "{}" (of attribute xlink:href) is not a valid'
" URL.".format(path)
f'Value "{path}" (of attribute xlink:href) is not a valid' " URL."
)
amdids = file_elem.get("ADMID")
dmdids = file_elem.get("DMDID")
Expand Down
18 changes: 9 additions & 9 deletions metsrw/plugins/premisrw/__init__.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
"""PREMIS reader and writer."""

import logging

from .premis import PREMISAgent
from .premis import PREMISElement
from .premis import PREMISEvent
from .premis import PREMISObject
from .premis import PREMISRights
from .premis import data_find
from .premis import data_find_all
from .premis import data_find_text
from .premis import data_find_text_or_all
from .premis import data_to_premis
from .premis import premis_to_data
from .premis import PREMISAgent
from .premis import PREMISElement
from .premis import PREMISEvent
from .premis import PREMISObject
from .premis import PREMISRights
from .utils import camel_to_snake
from .utils import lxmlns
from .utils import NAMESPACES
from .utils import PREMIS_2_1_META
from .utils import PREMIS_2_1_NAMESPACE
Expand All @@ -37,10 +36,11 @@
from .utils import PREMIS_SCHEMA_LOCATION
from .utils import PREMIS_VERSION
from .utils import PREMIS_VERSIONS_MAP
from .utils import XSI_NAMESPACE
from .utils import camel_to_snake
from .utils import lxmlns
from .utils import snake_to_camel
from .utils import snake_to_camel_cap
from .utils import XSI_NAMESPACE


LOGGER = logging.getLogger(__name__)
LOGGER.addHandler(logging.NullHandler())
Expand Down
17 changes: 6 additions & 11 deletions metsrw/plugins/premisrw/premis.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- PREMISRights
"""

import abc
import json
import pprint
Expand Down Expand Up @@ -183,8 +184,8 @@ def __getattr__(self, attr_name):
)
)
raise AttributeError(
"Instance of {} has no attribute {}. Valid attributes"
" are\n{}".format(self.__class__, attr_name, valid_attributes)
f"Instance of {self.__class__} has no attribute {attr_name}. Valid attributes"
f" are\n{valid_attributes}"
)

def find(self, path):
Expand Down Expand Up @@ -391,8 +392,7 @@ def compression_details(self):
event_type = self.findtext("event_type")
if event_type != "compression":
raise AttributeError(
'PREMIS events of type "{}" have no compression'
" details".format(event_type)
f'PREMIS events of type "{event_type}" have no compression' " details"
)
parsed_compression_event_detail = self.parsed_event_detail
compression_program = _get_event_detail_attr(
Expand Down Expand Up @@ -435,8 +435,7 @@ def encryption_details(self):
event_type = self.findtext("event_type")
if event_type != "encryption":
raise AttributeError(
'PREMIS events of type "{}" have no encryption'
" details".format(event_type)
f'PREMIS events of type "{event_type}" have no encryption' " details"
)
parsed_encryption_event_detail = self.parsed_event_detail
encryption_program = _get_event_detail_attr(
Expand Down Expand Up @@ -846,11 +845,7 @@ def _get_event_detail_attr(attr, parsed_event_detail):
try:
return parsed_event_detail[attr]
except KeyError:
print(
"Unable to find attribute {} in event detail {}".format(
attr, parsed_event_detail
)
)
print(f"Unable to find attribute {attr} in event detail {parsed_event_detail}")
return "No value found"


Expand Down
1 change: 0 additions & 1 deletion metsrw/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from urllib.parse import urlparse
from urllib.parse import urlunparse


####################################
# LXML HELPER VALUES AND FUNCTIONS #
####################################
Expand Down
21 changes: 21 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ dev = [
"pip-tools",
"pytest-cov",
"pytest",
"ruff",
"sphinx-rtd-theme",
"sphinx==7.1.2",
"sphinxcontrib-applehelp==1.0.4",
Expand All @@ -72,6 +73,26 @@ dev = [
version = {attr = "metsrw.__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",
"UP031",
]

[tool.ruff.lint.isort]
force-single-line = true

[tool.pytest.ini_options]
python_files = [
"test_*.py",
Expand Down
2 changes: 2 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ pytest-cov==5.0.0
# via metsrw (pyproject.toml)
requests==2.32.3
# via sphinx
ruff==0.4.9
# via metsrw (pyproject.toml)
snowballstemmer==2.2.0
# via sphinx
sphinx==7.1.2
Expand Down
1 change: 0 additions & 1 deletion tests/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import metsrw.plugins.premisrw as premisrw


EX_AGT_1_IDENTIFIER_TYPE = "preservation system"
EX_AGT_1_IDENTIFIER_VALUE = "Archivematica-1.6.1"
EX_AGT_1_NAME = "Archivematica"
Expand Down
Loading

0 comments on commit 549c786

Please # to comment.