From 1811dcdf179368c1298f5a76bd02c6a05cd68a64 Mon Sep 17 00:00:00 2001 From: Scott Searcy Date: Wed, 5 Jun 2024 13:26:42 -0700 Subject: [PATCH] Switch to Ruff for formatting/linting TODO: update GitHub CI --- .editorconfig | 4 + .flake8 | 10 -- .pre-commit-config.yaml | 23 +++-- Makefile | 3 +- docs/conf.py | 2 +- meshinfo/aredn.py | 5 +- meshinfo/backup.py | 1 + meshinfo/collector.py | 3 +- meshinfo/config.py | 3 +- meshinfo/models/__init__.py | 3 +- meshinfo/network.py | 2 +- meshinfo/pshell.py | 1 + meshinfo/report.py | 1 + meshinfo/views/schema.py | 4 +- pdm.lock | 2 +- pyproject.toml | 58 +++++++++--- requirements.txt | 180 ++++++++++++++---------------------- 17 files changed, 151 insertions(+), 154 deletions(-) delete mode 100644 .flake8 diff --git a/.editorconfig b/.editorconfig index 36139ce..409f111 100644 --- a/.editorconfig +++ b/.editorconfig @@ -26,3 +26,7 @@ indent_size = 2 [*.rst] indent_style = space indent_size = 3 + +[*.toml] +indent_style = space +indent_size = 4 diff --git a/.flake8 b/.flake8 deleted file mode 100644 index b7d090c..0000000 --- a/.flake8 +++ /dev/null @@ -1,10 +0,0 @@ -[flake8] -max-line-length = 80 -select = C,E,F,W,B,B950 -ignore = E203, E501, E722, W503 -max_complexity = 12 -exclude = - .git - .tox - __pycache__ - alembic/versions diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2c74908..728dd90 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,18 +1,27 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: check-toml - id: check-yaml - id: end-of-file-fixer - id: trailing-whitespace - - repo: https://github.com/psf/black - rev: 23.10.1 + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version (should be consistent with `pdm.lock` + rev: v0.4.6 hooks: - - id: black + # Run the linter. + - id: ruff + # Run the formatter. + - id: ruff-format - - repo: https://github.com/pycqa/isort - rev: 5.12.0 + - repo: https://github.com/pdm-project/pdm + rev: 2.15.4 hooks: - - id: isort + # Ensure lock file is consistent with pyproject.toml + - id: pdm-lock-check + # Export python requirements + - id: pdm-export + args: ['-o', 'requirements.txt', '--without-hashes'] + files: ^pdm.lock$ diff --git a/Makefile b/Makefile index 7f45184..e325493 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,8 @@ pre-commit: pre-commit run --all-files lint: - flake8 meshinfo tests + ruff check . --fix + ruff format . mypy: mypy meshinfo tests diff --git a/docs/conf.py b/docs/conf.py index 6246f7e..24c0a27 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,7 +20,7 @@ # -- Project information ----------------------------------------------------- project = "Mesh Info" -copyright = "2022, Scott Searcy" +copyright = "Scott Searcy" # noqa: A001 author = "Scott Searcy" # The full version, including alpha/beta/rc tags diff --git a/meshinfo/aredn.py b/meshinfo/aredn.py index ef0f566..eae258f 100644 --- a/meshinfo/aredn.py +++ b/meshinfo/aredn.py @@ -333,9 +333,8 @@ def primary_interface(self) -> Optional[Interface]: if iface not in self.interfaces or not self.interfaces[iface].ip_address: continue return self.interfaces[iface] - else: - logger.warning("Unable to identify wireless interface") - return None + logger.warning("Unable to identify wireless interface") + return None @property def ip_address(self) -> str: diff --git a/meshinfo/backup.py b/meshinfo/backup.py index db0184a..e15d829 100644 --- a/meshinfo/backup.py +++ b/meshinfo/backup.py @@ -1,4 +1,5 @@ """Functionality for exporting and importing application data.""" + from __future__ import annotations import re diff --git a/meshinfo/collector.py b/meshinfo/collector.py index fcceccb..240c548 100644 --- a/meshinfo/collector.py +++ b/meshinfo/collector.py @@ -1,4 +1,5 @@ """Repeatedly collects data about the network and stores it to the database.""" + from __future__ import annotations import asyncio @@ -155,7 +156,7 @@ async def collector( except RuntimeError: raise ConnectionError( f"Failed to connect to OLSR daemon on {local_node} for network data" - ) + ) from None poller_finished = time.monotonic() poller_elapsed = poller_finished - start_time diff --git a/meshinfo/config.py b/meshinfo/config.py index c3f15de..d9bc402 100644 --- a/meshinfo/config.py +++ b/meshinfo/config.py @@ -159,7 +159,8 @@ def configure( configure_logging(app_config.log_level) logger.debug( - "Application configuration", **attrs.asdict(app_config) # type: ignore + "Application configuration", + **attrs.asdict(app_config), # type: ignore ) # configure Pyramid application diff --git a/meshinfo/models/__init__.py b/meshinfo/models/__init__.py index 010e64b..8a89368 100644 --- a/meshinfo/models/__init__.py +++ b/meshinfo/models/__init__.py @@ -1,4 +1,5 @@ """Configuration of SQLAlchemy database, copied from Pyramid Cookiecutter.""" + from __future__ import annotations import contextlib @@ -75,7 +76,7 @@ def get_tm_session(session_factory, transaction_manager, request=None): "info" dict. The "info" dict is the official namespace for developers to stash session-specific information. For more information, please see the SQLAlchemy docs: - https://docs.sqlalchemy.org/en/stable/orm/session_api.html#sqlalchemy.orm.session.Session.params.info # noqa + https://docs.sqlalchemy.org/en/stable/orm/session_api.html#sqlalchemy.orm.session.Session.params.info By placing the active ``request`` in the "info" dict, developers will be able to access the active Pyramid request from an instance of an SQLAlchemy diff --git a/meshinfo/network.py b/meshinfo/network.py index 75c4bf4..d2f396b 100644 --- a/meshinfo/network.py +++ b/meshinfo/network.py @@ -59,7 +59,7 @@ def datagram_received(self, data, addr): self.transport.close() def error_received(self, exc): - logger.warning("Error received: {!r}", exc) + logger.warning("Error received", error=exc) def connection_lost(self, exc): self.on_con_lost.set_result(self.received) diff --git a/meshinfo/pshell.py b/meshinfo/pshell.py index da04a2a..ed5bb51 100644 --- a/meshinfo/pshell.py +++ b/meshinfo/pshell.py @@ -1,4 +1,5 @@ """Configuration for Pyramid's interactive pshell.""" + from . import models diff --git a/meshinfo/report.py b/meshinfo/report.py index c445c0f..652fb48 100644 --- a/meshinfo/report.py +++ b/meshinfo/report.py @@ -1,4 +1,5 @@ """This process polls the network and displays the results to the user.""" + from __future__ import annotations import asyncio diff --git a/meshinfo/views/schema.py b/meshinfo/views/schema.py index 52f552c..a9ba405 100644 --- a/meshinfo/views/schema.py +++ b/meshinfo/views/schema.py @@ -14,8 +14,8 @@ def graph_params(params: dict) -> GraphParams: try: period = getattr(Period, params["period"].upper()) - except KeyError: - raise HTTPBadRequest("Invalid period for graph") + except KeyError as err: + raise HTTPBadRequest("Invalid period for graph") from err title = f"past {params['period'].lower()}" return GraphParams( diff --git a/pdm.lock b/pdm.lock index b72ea31..f119105 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev", "testing", "docs", "ruff", "mypy"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.1" -content_hash = "sha256:264d67ddd7fd974c3978a3339e5762320092e1e06fd5528bb8133678788b5cdb" +content_hash = "sha256:2e494f9b6f4f371c87a04fe38c0dc176de00272486dbf0849997434b280b7e7b" [[package]] name = "aiohttp" diff --git a/pyproject.toml b/pyproject.toml index a6c9ccb..6138ba1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,16 +6,16 @@ build-backend = "pdm.backend" name = "mesh-info" description = "Collect and display information about an AREDN mesh network." readme = "README.rst" -authors = [{name = "Scott Seary", email = "ki7onk@outlook.com"}] +authors = [{ name = "Scott Seary", email = "ki7onk@outlook.com" }] requires-python = ">=3.9" -license = {text = "GNU General Public License v3 (GPLv3)"} +license = { text = "GNU General Public License v3 (GPLv3)" } keywords = ["aredn", "mesh"] classifiers = [ - "Development Status :: 3 - Alpha", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", + "Development Status :: 3 - Alpha", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", ] dependencies = [ "aiohttp ~= 3.9.5", @@ -49,11 +49,6 @@ Documentation = "https://mesh-info-ki7onk.readthedocs.io/" [project.scripts] meshinfo = "meshinfo.cli:main" -[tool.isort] -profile = "black" -known_third_party = "alembic" - -[tool.pdm] [tool.pdm.dev-dependencies] testing = [ "Faker>=25.3.0", @@ -76,3 +71,42 @@ ruff = [ mypy = [ "mypy>=1.10.0", ] + +[tool.ruff] +target-version = "py39" + +[tool.ruff.format] +exclude = ["alembic/versions/*.py"] + +[tool.ruff.lint] +select = [ + "A", # flake8-builtins + "B", # flake8-bugbear" + "C4", # flake8-comprehensions + "C90", # mccabe + "E", "W", # pycodestyle + "F", # pyflakes + "I", # isort + "N", # pep8-naming + "PLC", "PLE", "PLW", # "PLR" # pylint + "YTT", # flake8-2020 +# "D", # pydocstyle +# "RET", # flake8-return +# "SIM", # flake8-simplify +# "UP", # pyupgrade +] +ignore = [ + "A003", # builtin-attribute-shadowing + "C416", # unnecessary `dict` comprehension + "D105", # magic-method + "D107", # public-init + "D203", # one-blank-line-before-class + "D213", # multi-line-summary-second-line + "PLR2004", # magic-value-comparison +] + +[tool.ruff.lint.isort] +known-third-party = ["alembic"] + +[tool.ruff.lint.mccabe] +max-complexity = 12 diff --git a/requirements.txt b/requirements.txt index 63325e6..667f68e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,138 +1,92 @@ -# -# This file is autogenerated by pip-compile with Python 3.9 -# by the following command: -# -# pip-compile --output-file=requirements.txt pyproject.toml -# +# This file is @generated by PDM. +# Please do not edit it manually. + aiohttp==3.9.5 - # via mesh-info (pyproject.toml) aiosignal==1.3.1 - # via aiohttp -alembic==1.12.1 - # via mesh-info (pyproject.toml) -async-timeout==4.0.3 ; python_version < "3.11" - # via - # aiohttp - # mesh-info (pyproject.toml) -attrs==23.1.0 - # via - # aiohttp - # environ-config - # mesh-info (pyproject.toml) +alabaster==0.7.16 +alembic==1.13.1 +async-timeout==4.0.3; python_version < "3.11" +attrs==23.2.0 +babel==2.15.0 +beautifulsoup4==4.12.3 +certifi==2024.2.2 +charset-normalizer==3.3.2 +colorama==0.4.6; sys_platform == "win32" +coverage==7.5.3 +docutils==0.20.1 environ-config==23.2.0 - # via mesh-info (pyproject.toml) -frozenlist==1.4.0 - # via - # aiohttp - # aiosignal -greenlet==3.0.1 - # via sqlalchemy +exceptiongroup==1.2.1; python_version < "3.11" +faker==25.3.0 +frozenlist==1.4.1 +greenlet==3.0.3; (platform_machine == "win32" or platform_machine == "WIN32" or platform_machine == "AMD64" or platform_machine == "amd64" or platform_machine == "x86_64" or platform_machine == "ppc64le" or platform_machine == "aarch64") and python_version >= "3" gunicorn==22.0.0 - # via mesh-info (pyproject.toml) -hupper==1.12 - # via pyramid +hupper==1.12.1 idna==3.7 - # via yarl +imagesize==1.4.1 +importlib-metadata==7.1.0; python_version < "3.10" +iniconfig==2.0.0 jinja2==3.1.4 - # via pyramid-jinja2 -mako==1.2.4 - # via alembic +mako==1.3.5 markdown-it-py==3.0.0 - # via rich -markupsafe==2.1.3 - # via - # jinja2 - # mako - # pyramid-jinja2 +markupsafe==2.1.5 mdurl==0.1.2 - # via markdown-it-py -multidict==6.0.4 - # via - # aiohttp - # yarl -packaging==23.2 - # via gunicorn -pastedeploy==3.0.1 - # via plaster-pastedeploy +multidict==6.0.5 +mypy==1.10.0 +mypy-extensions==1.0.0 +packaging==24.0 +pastedeploy==3.1.0 pendulum==2.1.2 - # via mesh-info (pyproject.toml) plaster==1.1.2 - # via - # plaster-pastedeploy - # pyramid plaster-pastedeploy==1.0.1 - # via pyramid platformdirs==2.6.2 - # via mesh-info (pyproject.toml) +pluggy==1.5.0 psycopg2==2.9.9 - # via mesh-info (pyproject.toml) -pygments==2.16.1 - # via rich +pygments==2.18.0 pyramid==2.0.2 - # via - # mesh-info (pyproject.toml) - # pyramid-jinja2 - # pyramid-retry - # pyramid-services - # pyramid-tm -pyramid-jinja2==2.10 - # via mesh-info (pyproject.toml) +pyramid-debugtoolbar==4.12.1 +pyramid-jinja2==2.10.1 +pyramid-mako==1.1.0 pyramid-retry==2.1.1 - # via mesh-info (pyproject.toml) pyramid-services==2.2 - # via mesh-info (pyproject.toml) pyramid-tm==2.5 - # via mesh-info (pyproject.toml) -python-dateutil==2.8.2 - # via pendulum +pytest==8.2.1 +pytest-asyncio==0.23.7 +pytest-cov==5.0.0 +pytest-mock==3.14.0 +python-dateutil==2.9.0.post0 python-dotenv==0.21.1 - # via mesh-info (pyproject.toml) pytzdata==2020.1 - # via pendulum -rich==13.6.0 - # via mesh-info (pyproject.toml) -rrdtool==0.1.16 - # via mesh-info (pyproject.toml) +requests==2.32.3 +rich==13.7.1 +rrdtool-bindings==0.2.0 +ruff==0.4.6 +setuptools==70.0.0 six==1.16.0 - # via python-dateutil -sqlalchemy==1.4.50 - # via - # alembic - # mesh-info (pyproject.toml) - # zope-sqlalchemy -structlog==23.2.0 - # via mesh-info (pyproject.toml) +snowballstemmer==2.2.0 +soupsieve==2.5 +sphinx==7.3.7 +sphinx-rtd-theme==2.0.0 +sphinxcontrib-applehelp==1.0.8 +sphinxcontrib-devhelp==1.0.6 +sphinxcontrib-htmlhelp==2.0.5 +sphinxcontrib-jquery==4.1 +sphinxcontrib-jsmath==1.0.1 +sphinxcontrib-qthelp==1.0.7 +sphinxcontrib-serializinghtml==1.1.10 +sqlalchemy==1.4.52 +structlog==23.3.0 +tomli==2.0.1; python_version < "3.11" transaction==3.1.0 - # via - # mesh-info (pyproject.toml) - # pyramid-tm - # zope-sqlalchemy translationstring==1.4 - # via pyramid -typing-extensions==4.8.0 - # via alembic -venusian==3.0.0 - # via pyramid +typing-extensions==4.11.0 +urllib3==2.2.1 +venusian==3.1.0 +waitress==3.0.0 webob==1.8.7 - # via pyramid -wired==0.3 - # via pyramid-services -yarl==1.9.2 - # via aiohttp +webtest==3.0.0 +wired==0.4 +yarl==1.9.4 +zipp==3.19.1; python_version < "3.10" zope-deprecation==5.0 - # via - # pyramid - # pyramid-jinja2 -zope-interface==6.1 - # via - # pyramid - # pyramid-retry - # pyramid-services - # transaction - # wired - # zope-sqlalchemy +zope-interface==6.4.post1 zope-sqlalchemy==1.6 - # via mesh-info (pyproject.toml) - -# The following packages are considered to be unsafe in a requirements file: -# setuptools