Set up code formatting tools
- auto-format code with: + autoflake => * remove unused imports and variables * remove duplicate dict keys * expand star imports + black => enforce an uncompromising code style + isort => enforce a consistent import style compliant with Google's Python style guide - add nox session "format" to run these tools
This commit is contained in:
parent
ceabb00bab
commit
fb407631d9
5 changed files with 335 additions and 2 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
.cache/
|
||||
poetry.toml
|
||||
**/__pycache__/
|
||||
.venv/
|
||||
|
|
18
README.md
18
README.md
|
@ -8,6 +8,9 @@ The goal of the `lalib` project is to create
|
|||
by reading and writing code.
|
||||
|
||||
|
||||
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
|
||||
|
||||
|
||||
## Contributing & Development
|
||||
|
||||
This project is open for any kind of contribution,
|
||||
|
@ -65,6 +68,21 @@ To execute all default tasks, simply invoke:
|
|||
`nox`
|
||||
|
||||
|
||||
#### Code Formatting
|
||||
|
||||
We follow [Google's Python style guide](https://google.github.io/styleguide/pyguide.html).
|
||||
|
||||
During development,
|
||||
`nox -s format` may be helpful.
|
||||
It can be speed up by re-using a previously created environment
|
||||
with the `-R` flag.
|
||||
|
||||
This task formats all source code files with
|
||||
[autoflake](https://pypi.org/project/autoflake/),
|
||||
[black](https://pypi.org/project/black/), and
|
||||
[isort](https://pypi.org/project/isort/).
|
||||
|
||||
|
||||
### Branching Strategy
|
||||
|
||||
The branches in this repository follow the
|
||||
|
|
79
noxfile.py
79
noxfile.py
|
@ -1,6 +1,9 @@
|
|||
"""Maintenance tasks run in isolated environments."""
|
||||
|
||||
import collections
|
||||
import pathlib
|
||||
import re
|
||||
import tempfile
|
||||
from collections.abc import Mapping
|
||||
from typing import Any
|
||||
|
||||
|
@ -12,6 +15,9 @@ try:
|
|||
from nox_poetry import session as nox_session
|
||||
except ImportError:
|
||||
nox_session = nox.session
|
||||
nox_poetry_available = False
|
||||
else:
|
||||
nox_poetry_available = True
|
||||
|
||||
|
||||
def nested_defaultdict() -> collections.defaultdict[str, Any]:
|
||||
|
@ -67,10 +73,30 @@ nox.options.envdir = ".cache/nox"
|
|||
nox.options.error_on_external_run = True # only `git` and `poetry` are external
|
||||
nox.options.reuse_venv = "no"
|
||||
nox.options.sessions = ( # run by default when invoking `nox` on the CLI
|
||||
"format",
|
||||
)
|
||||
nox.options.stop_on_first_error = True
|
||||
|
||||
|
||||
@nox_session(name="format", python=MAIN_PYTHON)
|
||||
def format_(session: nox.Session) -> None:
|
||||
"""Format source files with `autoflake`, `black`, and `isort`."""
|
||||
start(session)
|
||||
|
||||
install_pinned(session, "autoflake", "black", "isort")
|
||||
|
||||
locations = session.posargs or SRC_LOCATIONS
|
||||
|
||||
session.run("autoflake", "--version")
|
||||
session.run("autoflake", *locations)
|
||||
|
||||
session.run("black", "--version")
|
||||
session.run("black", *locations)
|
||||
|
||||
session.run("isort", "--version-number")
|
||||
session.run("isort", *locations)
|
||||
|
||||
|
||||
def start(session: nox.Session) -> None:
|
||||
"""Show generic info about a session."""
|
||||
if session.posargs:
|
||||
|
@ -83,10 +109,63 @@ def start(session: nox.Session) -> None:
|
|||
session.run("python", "-c", "import os; print(os.getcwd())")
|
||||
session.run("python", "-c", 'import os; print(os.environ["PATH"])')
|
||||
|
||||
session.env["BLACK_CACHE_DIR"] = ".cache/black"
|
||||
session.env["PIP_CACHE_DIR"] = ".cache/pip"
|
||||
session.env["PIP_DISABLE_PIP_VERSION_CHECK"] = "true"
|
||||
|
||||
|
||||
def install_pinned(
|
||||
session: nox.Session,
|
||||
*packages_or_pip_args: str,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""Install packages respecting the "poetry.lock" file.
|
||||
|
||||
Wraps `nox.sessions.Session.install()` such that it installs
|
||||
packages respecting the pinned versions specified in poetry's
|
||||
lock file. This makes nox sessions more deterministic.
|
||||
"""
|
||||
session.debug("Install packages respecting the poetry.lock file")
|
||||
|
||||
session.run( # temporary fix to avoid poetry's future warning
|
||||
"poetry",
|
||||
"config",
|
||||
"--local",
|
||||
"warnings.export",
|
||||
"false",
|
||||
external=True,
|
||||
log=False, # because it's just a fix
|
||||
)
|
||||
|
||||
if nox_poetry_available:
|
||||
session.install(*packages_or_pip_args, **kwargs)
|
||||
return
|
||||
|
||||
with tempfile.NamedTemporaryFile() as requirements_txt:
|
||||
session.run(
|
||||
"poetry",
|
||||
"export",
|
||||
"--format=requirements.txt",
|
||||
f"--output={requirements_txt.name}",
|
||||
"--with=dev",
|
||||
"--without-hashes",
|
||||
external=True,
|
||||
)
|
||||
|
||||
# `pip install --constraint ...` raises an error if the
|
||||
# dependencies in requirements.txt contain "extras"
|
||||
# => Strip "package[extras]==1.2.3" into "package==1.2.3"
|
||||
dependencies = pathlib.Path(requirements_txt.name).read_text().split("\n")
|
||||
dependencies = [re.sub(r"\[.*\]==", "==", dep) for dep in dependencies]
|
||||
pathlib.Path(requirements_txt.name).write_text("\n".join(dependencies))
|
||||
|
||||
session.install(
|
||||
f"--constraint={requirements_txt.name}",
|
||||
*packages_or_pip_args,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
if MAIN_PYTHON not in SUPPORTED_PYTHONS:
|
||||
msg = f"MAIN_PYTHON version, v{MAIN_PYTHON}, is not in SUPPORTED_PYTHONS"
|
||||
raise RuntimeError(msg)
|
||||
|
|
184
poetry.lock
generated
184
poetry.lock
generated
|
@ -1,6 +1,186 @@
|
|||
package = []
|
||||
[[package]]
|
||||
name = "autoflake"
|
||||
version = "2.3.1"
|
||||
description = "Removes unused imports and unused variables"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "autoflake-2.3.1-py3-none-any.whl", hash = "sha256:3ae7495db9084b7b32818b4140e6dc4fc280b712fb414f5b8fe57b0a8e85a840"},
|
||||
{file = "autoflake-2.3.1.tar.gz", hash = "sha256:c98b75dc5b0a86459c4f01a1d32ac7eb4338ec4317a4469515ff1e687ecd909e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pyflakes = ">=3.0.0"
|
||||
tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""}
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "24.8.0"
|
||||
description = "The uncompromising code formatter."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"},
|
||||
{file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"},
|
||||
{file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"},
|
||||
{file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"},
|
||||
{file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"},
|
||||
{file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"},
|
||||
{file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"},
|
||||
{file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"},
|
||||
{file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"},
|
||||
{file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"},
|
||||
{file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"},
|
||||
{file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"},
|
||||
{file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"},
|
||||
{file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"},
|
||||
{file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"},
|
||||
{file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"},
|
||||
{file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"},
|
||||
{file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"},
|
||||
{file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"},
|
||||
{file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"},
|
||||
{file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"},
|
||||
{file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
click = ">=8.0.0"
|
||||
mypy-extensions = ">=0.4.3"
|
||||
packaging = ">=22.0"
|
||||
pathspec = ">=0.9.0"
|
||||
platformdirs = ">=2"
|
||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||
typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""}
|
||||
|
||||
[package.extras]
|
||||
colorama = ["colorama (>=0.4.3)"]
|
||||
d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"]
|
||||
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
|
||||
uvloop = ["uvloop (>=0.15.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.7"
|
||||
description = "Composable command line interface toolkit"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
|
||||
{file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
description = "Cross-platform colored terminal text."
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||
files = [
|
||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "isort"
|
||||
version = "5.13.2"
|
||||
description = "A Python utility / library to sort Python imports."
|
||||
optional = false
|
||||
python-versions = ">=3.8.0"
|
||||
files = [
|
||||
{file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"},
|
||||
{file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
colors = ["colorama (>=0.4.6)"]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "1.0.0"
|
||||
description = "Type system extensions for programs checked with the mypy type checker."
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
|
||||
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "24.1"
|
||||
description = "Core utilities for Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
|
||||
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.12.1"
|
||||
description = "Utility library for gitignore style pattern matching of file paths."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
|
||||
{file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "4.3.2"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "platformdirs-4.3.2-py3-none-any.whl", hash = "sha256:eb1c8582560b34ed4ba105009a4badf7f6f85768b30126f351328507b2beb617"},
|
||||
{file = "platformdirs-4.3.2.tar.gz", hash = "sha256:9e5e27a08aa095dd127b9f2e764d74254f482fef22b0970773bfba79d091ab8c"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"]
|
||||
type = ["mypy (>=1.11.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "pyflakes"
|
||||
version = "3.2.0"
|
||||
description = "passive checker of Python programs"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"},
|
||||
{file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.0.1"
|
||||
description = "A lil' TOML parser"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
||||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.12.2"
|
||||
description = "Backported and Experimental Type Hints for Python 3.8+"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
|
||||
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
|
||||
]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.9"
|
||||
content-hash = "0ef693f84017d54b0ea1d26b405d979fc57973167b307ccd9ed780ad9bdc1a3a"
|
||||
content-hash = "01960a0fd26ee6cb565391f829bae290abbf990c15496a4c93b323b0253a09fa"
|
||||
|
|
|
@ -32,6 +32,10 @@ python = "^3.9"
|
|||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
|
||||
# Code formatters
|
||||
autoflake = "^2.3"
|
||||
black = "^24.8"
|
||||
isort = "^5.13"
|
||||
|
||||
|
||||
[tool.poetry.urls]
|
||||
|
@ -40,6 +44,57 @@ python = "^3.9"
|
|||
|
||||
|
||||
|
||||
[tool.autoflake]
|
||||
# Source: https://github.com/PyCQA/autoflake#configuration
|
||||
|
||||
in-place = true
|
||||
recursive = true
|
||||
expand-star-imports = true
|
||||
remove-all-unused-imports = true
|
||||
ignore-init-module-imports = true # modifies "remove-all-unused-imports"
|
||||
remove-duplicate-keys = true
|
||||
remove-unused-variables = true
|
||||
|
||||
|
||||
|
||||
[tool.black]
|
||||
# Source: https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html
|
||||
|
||||
line-length = 88
|
||||
target-version = ["py312", "py311", "py310", "py39"]
|
||||
|
||||
|
||||
|
||||
[tool.isort]
|
||||
# Source: https://pycqa.github.io/isort/docs/configuration/options.html
|
||||
|
||||
known_first_party = ["lalib"]
|
||||
|
||||
atomic = true
|
||||
case_sensitive = true
|
||||
combine_star = true
|
||||
force_alphabetical_sort_within_sections = true
|
||||
lines_after_imports = 2
|
||||
remove_redundant_aliases = true
|
||||
|
||||
# Comply with black's style => Instead of: 'profile = "black"'
|
||||
# Source: https://pycqa.github.io/isort/docs/configuration/profiles.html
|
||||
ensure_newline_before_comments = true
|
||||
force_grid_wrap = 0
|
||||
include_trailing_comma = true
|
||||
line_length = 88
|
||||
multi_line_output = 3
|
||||
split_on_trailing_comma = true
|
||||
use_parentheses = true
|
||||
|
||||
# Comply with Google's Python style guide
|
||||
# => All imports go on a single line (with some exceptions)
|
||||
# Source: https://google.github.io/styleguide/pyguide.html#313-imports-formatting
|
||||
force_single_line = true
|
||||
single_line_exclusions = ["collections.abc", "typing"]
|
||||
|
||||
|
||||
|
||||
[build-system]
|
||||
|
||||
requires = ["poetry-core"]
|
||||
|
|
Loading…
Reference in a new issue