Skip to content

Commit

Permalink
chore(ci): switch from flake8 to ruff
Browse files Browse the repository at this point in the history
Switch from using [flake8](https://pypi.org/project/flake8) to [ruff](https://pypi.org/project/ruff) as the primary linter for the project. Ruff is orders of magnitude faster than alternative tools while integrating more functionality behind a single, common interface. This should help speed up linting for the project considerably and allow for much more sophisticated linting options and configurations. As a consequence of that, the following changes are included as part of this migration:

- Removal of flake8 from the project’s dependencies and pre-commit hooks.
- Activation of additional linting rules supported by `ruff` and the accompanying changes on the source code to conform to these rules.
- Upgrading of existing dependencies.
- Removal of `tox.ini`. Tox configuration is now included in the `pyproject.toml` file. All the project configuration is now housed in one file. This is in line with [PEP 621](https://peps.python.org/pep-0621) guidelines.
- Minor fixes and improvements.
  • Loading branch information
kennedykori committed Apr 12, 2023
1 parent 0a8a200 commit 6b407dd
Show file tree
Hide file tree
Showing 57 changed files with 890 additions and 586 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,9 @@ GitHub.sublime-settings
# Session
Session.vim

# Ruff cache
.ruff_cache

# Temporary
.netrwhist
.~lock.*
Expand Down
15 changes: 7 additions & 8 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ repos:
- id: check-yaml
- id: check-added-large-files

- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: "v0.0.261"
hooks:
- id: ruff
args: [ --fix, --exit-non-zero-on-fix ]

- repo: https://github.com/psf/black
rev: 23.3.0
hooks:
Expand All @@ -24,14 +30,7 @@ repos:
rev: v3.3.1
hooks:
- id: pyupgrade
args: [--py310-plus]

- repo: https://github.com/pycqa/flake8
rev: 6.0.0
hooks:
- id: flake8
args: ["--config=tox.ini"]
additional_dependencies: [flake8-isort]
args: ["--py310-plus"]

# sets up .pre-commit-ci.yaml to ensure pre-commit dependencies stay up to date
ci:
Expand Down
57 changes: 30 additions & 27 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from typing import Any, Final, cast

import yaml
from yaml import Loader

from app.core import DataSourceType
from app.lib import (
Expand All @@ -28,7 +27,7 @@

_SUPPORTED_DATA_SOURCE_TYPES_CONFIG_KEY: Final[
str
] = "SUPPORTED_DATA_SOURCE_TYPES" # noqa
] = "SUPPORTED_DATA_SOURCE_TYPES"

_DEFAULT_CONFIG: Final[dict[str, Any]] = {
_LOGGING_CONFIG_KEY: {
Expand All @@ -39,15 +38,15 @@
"format": (
"%(levelname)s: %(asctime)s %(module)s "
"%(process)d %(thread)d %(message)s"
)
}
),
},
},
"handlers": {
"console": {
"level": "DEBUG",
"class": "logging.StreamHandler",
"formatter": "verbose",
}
},
},
"root": {"level": "INFO", "handlers": ["console"]},
},
Expand Down Expand Up @@ -86,32 +85,33 @@ def _load_config_file(
# TODO: Ensure that a valid config file path was given and if not raise an
# appropriate Exception.
with open(config_file_path, "rb") as config_file:
return yaml.load(config_file, Loader=Loader)
return yaml.safe_load(config_file)


def _load_settings_initializers(
initializers_dotted_paths: Sequence[str],
) -> Sequence[SettingInitializer]:
initializers: list[SettingInitializer] = list()
initializers: list[SettingInitializer] = []
for _initializer_dotted_path in initializers_dotted_paths:
try:
initializer_klass: type[SettingInitializer]
initializer_klass = import_string_as_klass(
_initializer_dotted_path, SettingInitializer
_initializer_dotted_path,
SettingInitializer,
)
initializers.append(initializer_klass()) # type: ignore
except ImportError as exp:
raise ImproperlyConfiguredError(
message='"%s" does not seem to be a valid path.'
% _initializer_dotted_path
% _initializer_dotted_path,
) from exp
except TypeError as exp:
raise ImproperlyConfiguredError(
message=(
'Invalid value, "%s" is either not class or is not a '
'subclass of "app.lib.SettingInitializer".'
% _initializer_dotted_path
)
),
) from exp

return initializers
Expand All @@ -127,7 +127,7 @@ class _DefaultTransportFactoryInitializer(SettingInitializer):
def setting(self) -> str:
return _DEFAULT_TRANSPORT_FACTORY_CONFIG_KEY

def execute(self, an_input: str | None) -> Any:
def execute(self, an_input: str | None) -> str | None:
# If the default transport setting has not been provided or is empty,
# do nothing.
if not an_input:
Expand All @@ -136,20 +136,21 @@ def execute(self, an_input: str | None) -> Any:
if type(an_input) is not str:
raise ImproperlyConfiguredError(
message='The value of the "%s" setting must be a string'
% _DEFAULT_TRANSPORT_FACTORY_CONFIG_KEY
% _DEFAULT_TRANSPORT_FACTORY_CONFIG_KEY,
)

default_transport_factory: DefaultTransportFactory
try:
default_transport_factory = cast(
DefaultTransportFactory, import_string(an_input)
DefaultTransportFactory,
import_string(an_input),
)
global registry
registry.default_transport_factory = default_transport_factory
except (ImportError, TypeError) as exp:
raise ImproperlyConfiguredError(
message="Unable to import the default transport factory at "
'"%s". Ensure a valid path was given.' % an_input
'"%s". Ensure a valid path was given.' % an_input,
) from exp

return an_input
Expand All @@ -162,9 +163,9 @@ class _LoggingInitializer(SettingInitializer):
def setting(self) -> str:
return _LOGGING_CONFIG_KEY

def execute(self, an_input: Mapping[str, Any] | None) -> Any:
def execute(self, an_input: Mapping[str, Any] | None) -> Mapping[str, Any]:
logging_config: dict[str, Any] = dict(
an_input or _DEFAULT_CONFIG[self.setting]
an_input or _DEFAULT_CONFIG[self.setting],
)
dictConfig(logging_config)
return logging_config
Expand All @@ -180,17 +181,17 @@ class _SupportedDataSourceTypesInitializer(SettingInitializer):
def setting(self) -> str:
return _SUPPORTED_DATA_SOURCE_TYPES_CONFIG_KEY

def execute(self, an_input: Sequence[str] | None) -> Any:
def execute(self, an_input: Sequence[str] | None) -> Sequence[str]:
supported_dst: Sequence[str] = (
an_input or _DEFAULT_CONFIG[self.setting]
)
global registry
_dst: DataSourceType # noqa: F842
_dst: DataSourceType
registry.data_source_types = {
_dst.code: _dst
for _dst in map(
lambda _s: self._dotted_path_to_data_source_type_klass(_s)(),
supported_dst,
for _dst in (
self._dotted_path_to_data_source_type_klass(_s)()
for _s in supported_dst
)
}
return supported_dst
Expand All @@ -202,19 +203,20 @@ def _dotted_path_to_data_source_type_klass(
try:
data_source_type_klass: type[DataSourceType]
data_source_type_klass = import_string_as_klass(
dotted_path, DataSourceType
dotted_path,
DataSourceType,
)
return data_source_type_klass
except ImportError as exp:
raise ImproperlyConfiguredError(
message='"%s" does not seem to be a valid path.' % dotted_path
message='"%s" does not seem to be a valid path.' % dotted_path,
) from exp
except TypeError as exp:
raise ImproperlyConfiguredError(
message=(
'Invalid value, "%s" is either not class or is not a '
'subclass of "app.core.DataSourceType".' % dotted_path
)
),
) from exp


Expand Down Expand Up @@ -252,14 +254,15 @@ def setup(
_initializers: list[Any] = list(settings_initializers or [])
_initializers.extend(
_load_settings_initializers(
_settings_dict.get(_SETTINGS_INITIALIZERS_CONFIG_KEY, tuple())
)
_settings_dict.get(_SETTINGS_INITIALIZERS_CONFIG_KEY, ()),
),
)
_initializers.insert(0, _LoggingInitializer())
_initializers.insert(1, _SupportedDataSourceTypesInitializer())
_initializers.insert(2, _DefaultTransportFactoryInitializer())

global settings
settings = Config( # type: ignore
settings=_settings_dict, settings_initializers=_initializers
settings=_settings_dict,
settings_initializers=_initializers,
)
13 changes: 8 additions & 5 deletions app/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ def argparse_factory(prog_name: str = __title__) -> ArgumentParser:
),
)
parser.add_argument(
"--version", action="version", version="%(prog)s " + __version__
"--version",
action="version",
version="%(prog)s " + __version__,
)

return parser
Expand All @@ -94,9 +96,9 @@ def main_pipeline_factory(
_transport: Transport
_transport = (
transport
or app.registry.get_default_transport_factory_or_raise( # noqa
or app.registry.get_default_transport_factory_or_raise(
error_message="The default transport factory is required by the "
"main application pipeline."
"main application pipeline.",
)()
)
return Pipeline(
Expand Down Expand Up @@ -125,10 +127,11 @@ def main() -> None: # pragma: no cover
transport_factory = app.registry.get_default_transport_factory_or_raise()
with transport_factory() as transport:
main_pipeline: Pipeline[
Sequence[DataSourceType], Sequence[UploadExtractResult]
Sequence[DataSourceType],
Sequence[UploadExtractResult],
] = main_pipeline_factory(transport=transport)
main_pipeline.execute(tuple(app.registry.data_source_types.values()))
print("Done ...")
print("Done ...") # noqa: T201


if __name__ == "__main__": # pragma: no cover
Expand Down

0 comments on commit 6b407dd

Please sign in to comment.