diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index 805ec67..d0593d4 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -4,7 +4,7 @@ on: push jobs: all_checks: - name: Run all tests, lints, etc. (Python 3.10) + name: Run all tests, lints, etc. (Python 3.11) runs-on: ubuntu-latest if: "!contains(github.event.head_commit.message, '[skip ci]')" @@ -15,7 +15,7 @@ jobs: - name: Install Python uses: actions/setup-python@v2 with: - python-version: '3.10' + python-version: '3.11' - name: Update pip & setuptools run: python -m pip install -U pip setuptools @@ -43,7 +43,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python: ['3.6', '3.7', '3.8', '3.9'] + python: ['3.8', '3.9', '3.10', '3.11', '3.12'] if: "!contains(github.event.head_commit.message, '[skip ci]')" steps: @@ -68,4 +68,3 @@ jobs: run: | pytest --cov tox -e sdist_install - \ No newline at end of file diff --git a/AUTHORS.md b/AUTHORS.md index 11ef574..24aebe9 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1,8 +1,16 @@ Credits ======= -`flake8-absolute-import` is authored and maintained by Brian Skinn ([Blog](https://bskinn.github.io)) ([Twitter](https://twitter.com/btskinn)). The skeleton of the AST-based implementation used for this plugin was shamelessly swiped from [`flake8-2020`](https://github.com/asottile/flake8-2020) by [Anthony Sottile](https://github.com/asottile). +`flake8-absolute-import` is authored and maintained by Brian Skinn +([Blog](https://bskinn.github.io)) ([Twitter](https://twitter.com/btskinn)). The +skeleton of the AST-based implementation used for this plugin was shamelessly +swiped from [`flake8-2020`](https://github.com/asottile/flake8-2020) by +[Anthony Sottile](https://github.com/asottile). -While there is disagreement about the upsides and downsides of relative imports in Python, as best this author can tell there are numerous projects and developers out there who desire to hold strictly to absolute imports in their code. The goal of this flake8 plugin is to simplify enforcement of this policy. +While there is disagreement about the upsides and downsides of relative imports +in Python, as best this author can tell there are numerous projects and +developers out there who desire to hold strictly to absolute imports in their +code. The goal of this flake8 plugin is to simplify enforcement of this policy. -v1.0 provides a single catch-all error, `ABS101`, raised on any relative imports found. +v1.0 provides a single catch-all error, `ABS101`, raised on any relative imports +found. diff --git a/CHANGELOG.md b/CHANGELOG.md index f1b7eea..ed90d11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,45 @@ and this project strives to adhere (mostly) to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -### [1.0.0.1] - ####-##-## +### [1.0.0.2] - 2023-10-08 + +This is an administrative release, primarily to update officially supported +Python and flake8 versions. There should be no plugin behavior changes between +this version and v1.0.0.1. + +#### Dependencies + +- Support for Python 3.12 was officially added + +- Support for Python 3.6 and 3.7 was officially removed, though the plugin + should continue to work for Python 3.6+ + +- The minimum supported flake8 version is now 5.0 + +#### Administrative + +- `MANIFEST.in` was augmented with the files needed to allow the test suite to + run from an unpacked sdist. + +- The build metadata was completely migrated from `setup.cfg` to + `pyproject.toml`, and the maximum possible metadata is now drawn from + `pyproject.toml` (all but `long_description`). + +- The read of the project version and the load of the README contents in + `setup.py` was modernized/improved. + +#### Testing + +- Python versions in the GitHub Actions and Azure Pipelines matrices were updated. + +- An Azure Pipelines job was added to confirm that a built sdist carries all the + files needed to allow the test suite to run. + +- Obscure parameters/variables in some tests were given better names, and string + formatting was upgraded to use f-strings. + + +### [1.0.0.1] - 2021-12-04 This is an administrative/metadata release, primarily to update officially supported Python versions. There should be no behavior changes between @@ -15,36 +53,36 @@ this version and v1.0.0. #### Dependencies -- Support for Python 3.10 and 3.11-dev was officially added +- Support for Python 3.10 and 3.11-dev was officially added. -- Support for Python 3.5 was officially removed, though the plugin should - remain compatible +- Support for Python 3.5 was officially removed, though the plugin should remain + compatible. #### Testing -- Support for running the `tox` matrix on Windows was removed (it doesn't - seem to work right with the batch-files-in-bin-folder approach I use for - multiple Pythons in development) +- Support for running the `tox` matrix on Windows was removed (it doesn't seem + to work right with the batch-files-in-bin-folder approach I use for multiple + Pythons in development). -- Routine CI switched from Travis CI to GitHub Actions +- Routine CI was switched from Travis CI to GitHub Actions. -- CI and `tox` versions updated to focus on Python 3.10 +- CI and `tox` versions were updated to focus on Python 3.10. -- `pytest` and `coverage` configs revised to avoid `source`/`include` collision - in `coverage` +- `pytest` and `coverage` configs were revised to avoid `source`/`include` + collision in `coverage`. #### Administrative - `setuptools` configuration has been mostly ported to `setup.cfg`, except for the - pieces that still need to be defined dynamically + pieces that still need to be defined dynamically. - Built artifacts for distribution should now be created using `build`, which - has been added to `requirements-dev.txt` + has been added to `requirements-dev.txt`. ### [1.0.0] - 2019-09-09 #### Features -- Detect any relative imports and report with error code ABS101 +- Detect any relative imports and report with error code ABS101. diff --git a/LICENSE.txt b/LICENSE.txt index 7f9d634..3742dc3 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2019-2021 Brian Skinn +Copyright (c) 2019-2023 Brian Skinn Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/MANIFEST.in b/MANIFEST.in index de0a14a..8606d0b 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1,2 @@ -include LICENSE.txt README.rst CHANGELOG.md pyproject.toml +include AUTHORS.md LICENSE.txt README.rst CHANGELOG.md pyproject.toml +include requirements-dev.txt requirements-flake8.txt tox.ini diff --git a/README.rst b/README.rst index 4baf27e..7044895 100644 --- a/README.rst +++ b/README.rst @@ -5,7 +5,7 @@ flake8-absolute-import **Current Development Version:** -.. image:: https://img.shields.io/github/workflow/status/bskinn/flake8-absolute-import/ci-tests?logo=github +.. image:: https://img.shields.io/github/actions/workflow/status/bskinn/flake8-absolute-import/ci_tests.yml?branch=main&logo=github :alt: GitHub Workflow Status :target: https://github.com/bskinn/flake8-absolute-import/actions @@ -27,13 +27,16 @@ flake8-absolute-import .. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/psf/black +.. image:: https://pepy.tech/badge/flake8-absolute-import/month + :target: https://pepy.tech/project/flake8-absolute-import + ---- *Don't like relative imports?* Lint 'em out! -``flake8-absolute-import`` uses a simple check of the AST for each +``flake8-absolute-import`` uses a direct check of the AST for each ``from x import y`` statement to flag relative imports. Specifically, it checks for a nonzero *level* attribute on each |ImportFrom|_ node. @@ -49,16 +52,21 @@ Relative imports raise the ``ABS101`` error code: Available on `PyPI `__ (``pip install flake8-absolute-import``). ``flake8`` should automatically -detect and load the plugin. ``flake8``>=3.7 is required. +detect and load the plugin. ``flake8``>=5.0 is required. Source on `GitHub `__. Bug reports and feature requests are welcomed at the `Issues `__ page there. -Copyright (c) Brian Skinn 2019-2021 +Copyright (c) Brian Skinn 2019-2023 -License: The MIT License. See `LICENSE.txt `__ -for full license terms. +The ``flake8-absolute-import`` documentation (including docstrings and README) +is licensed under a +`Creative Commons Attribution 4.0 International License `__ +(CC-BY). The ``flake8-absolute-import`` codebase is released under the +`MIT License `__. See +`LICENSE.txt `__ for +full license terms. .. _ImportFrom: https://greentreesnakes.readthedocs.io/en/latest/nodes.html#ImportFrom .. |ImportFrom| replace:: ``ImportFrom`` diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9aae50a..910ca23 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -12,30 +12,31 @@ jobs: - template: azure-coretest.yml parameters: pythons: - py37: - spec: '3.7' py38: spec: '3.8' py39: spec: '3.9' py310: spec: '3.10' + py311: + spec: '3.11' + py312: + spec: '3.12' platforms: [linux, windows, macOs] +- template: azure-sdisttest.yml + - job: flake8 pool: vmImage: 'Ubuntu-latest' steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.10' + versionSpec: '3.11' - script: pip install -U tox displayName: Install tox - - script: pip install -r requirements-flake8.txt - displayName: Install flake8 & plugins - - script: tox -e flake8 displayName: Lint the codebase @@ -45,7 +46,7 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.10' + versionSpec: '3.11' - script: pip install -r requirements-ci.txt displayName: Install CI requirements diff --git a/azure-sdisttest.yml b/azure-sdisttest.yml new file mode 100644 index 0000000..9f7e2a3 --- /dev/null +++ b/azure-sdisttest.yml @@ -0,0 +1,74 @@ +jobs: +- job: testable_sdist + displayName: Ensure sdist is testable + + variables: + pip_cache_dir: $(Pipeline.Workspace)/.pip + + pool: + vmImage: 'Ubuntu-latest' + + steps: + - task: UsePythonVersion@0 + inputs: + versionSpec: '3.11' + + - task: Cache@2 + inputs: + key: 'pip | "$(Agent.OS)" | requirements-dev.txt | requirements-flake8.txt' + restoreKeys: | + pip | "$(Agent.OS)" + path: $(pip_cache_dir) + displayName: Cache pip + + - script: python -m pip install build + displayName: Install 'build' package + + - script: | + python -m build -s + ls -lah dist + displayName: Build sdist + + - script: | + mkdir sandbox + displayName: Create sandbox + + - script: | + cp dist/*.gz sandbox/ + cd sandbox + tar xvf *.gz + displayName: Unpack sdist in sandbox + + - script: | + cd sandbox + python -m venv env + displayName: Create venv + + # Only the dir of the unpacked sdist will have a digit in its name + - script: | + cd sandbox + echo $( find . -maxdepth 1 -type d -regex "./.+[0-9].+" ) + displayName: Check unpack dir name + + - script: | + cd sandbox + source env/bin/activate + cd $( find . -maxdepth 1 -type d -regex "./.+[0-9].+" ) + python -m pip install -r requirements-dev.txt + displayName: Install dev req'ts to venv + + - script: | + cd sandbox + source env/bin/activate + cd $( find . -maxdepth 1 -type d -regex "./.+[0-9].+" ) + cd doc + O=-Ean make html + displayName: Build docs in sandbox (skipped, no docs) + condition: false + + - script: | + cd sandbox + source env/bin/activate + cd $( find . -maxdepth 1 -type d -regex "./.+[0-9].+" ) + pytest + displayName: Run test suite in sandbox diff --git a/pyproject.toml b/pyproject.toml index e28a124..cbc890c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,22 +1,97 @@ [build-system] -requires = ["wheel", "setuptools"] +requires = [ + "wheel", + "setuptools>=61.2", +] build-backend = "setuptools.build_meta" +[project] +name = "flake8-absolute-import" +description = "flake8 plugin to require absolute imports" +authors = [ + { name = "Brian Skinn", email = "brian.skinn@gmail.com" }, +] +classifiers = [ + "License :: OSI Approved", + "License :: OSI Approved :: MIT License", + "Natural Language :: English", + "Framework :: Flake8", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Software Development :: Quality Assurance", + "Development Status :: 5 - Production/Stable", +] +keywords = [ + "flake8-plugin", + "linting", + "absolute-imports", + "relative-imports", +] +requires-python = ">=3.6" +dependencies = [ + "flake8>=5.0", +] +dynamic = [ + "version", + "readme", +] + +[project.urls] +Homepage = "https://github.com/bskinn/flake8-absolute-import" +Changelog = "https://github.com/bskinn/flake8-absolute-import/blob/main/CHANGELOG.md" +Thank = "https://twitter.com/btskinn" +Donate = "https://github.com/sponsors/bskinn" + +[project.license] +text = "MIT License" + +[project.entry-points."flake8.extension"] +ABS1 = "flake8_absolute_import:Plugin" + +[tool.setuptools] +platforms = [ + "any", +] +license-files = [ + "LICENSE.txt", +] +include-package-data = false + +[tool.setuptools.dynamic] +version = {attr = "flake8_absolute_import.version.__version__"} + +[tool.setuptools.package-dir] +"" = "src" + +[tool.setuptools.packages.find] +where = [ + "src", +] +namespaces = false + [tool.black] line-length = 88 include = ''' ( - ^/tests/.*[.]py$ - | ^/src/flake8_absolute_import/.*[.]py$ - | ^/setup[.]py - | ^/conftest[.]py + ^tests/.*[.]py$ + | ^src/flake8_absolute_import/.*[.]py$ + | ^setup[.]py + | ^conftest[.]py ) ''' exclude = ''' ( __pycache__ - | ^/[.] - | ^/doc - | ^/env + | ^[.] + | ^doc + | ^env ) ''' diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index fea4b50..0000000 --- a/setup.cfg +++ /dev/null @@ -1,48 +0,0 @@ -[metadata] -name = flake8-absolute-import -description = flake8 plugin to require absolute imports -url = https://github.com/bskinn/flake8-absolute-import -project_urls = - Changelog=https://github.com/bskinn/flake8-absolute-import/blob/main/CHANGELOG.md - Thank=https://twitter.com/btskinn - Donate=https://github.com/sponsors/bskinn -license = MIT License -license_file = LICENSE.txt -platforms = any -author = Brian Skinn -author_email = bskinn@alum.mit.edu -classifiers = - License :: OSI Approved - License :: OSI Approved :: MIT License - Natural Language :: English - Framework :: Flake8 - Intended Audience :: Developers - Operating System :: OS Independent - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Topic :: Software Development :: Quality Assurance - Development Status :: 5 - Production/Stable -keywords = flake8-plugin, linting, absolute-imports, relative-imports - -[options] -install_requires = - flake8>=3.7 - -python_requires = >=3.4 -packages = find: -package_dir = - =src - -[options.packages.find] -where = src - -[options.entry_points] -flake8.extension = - ABS1=flake8_absolute_import:Plugin diff --git a/setup.py b/setup.py index 2d91920..19adc57 100644 --- a/setup.py +++ b/setup.py @@ -3,18 +3,19 @@ from setuptools import find_packages, setup -with Path("src", "flake8_absolute_import", "version.py").open() as f: - exec(f.read()) +exec_ns = {} +exec( + Path("src", "flake8_absolute_import", "version.py").read_text(encoding="utf-8"), + exec_ns, +) +__version__ = exec_ns["__version__"] NAME = "flake8-absolute-import" - - version_override = None def readme(): - with open("README.rst", "r") as f: - content = f.read() + content = Path("README.rst").read_text() new_ver = version_override if version_override else __version__ @@ -25,20 +26,18 @@ def content_update(content, pattern, sub): # Docs reference updates to current release version, for PyPI # This one gets the badge image content = content_update( - content, r"(?<=/readthedocs/{0}/)\S+?(?=\.svg$)".format(NAME), "v" + new_ver + content, rf"(?<=/readthedocs/{NAME}/)\S+?(?=\.svg$)", "v" + new_ver ) # This one gets the RtD links content = content_update( - content, r"(?<={0}\.readthedocs\.io/en/)\S+?(?=/)".format(NAME), "v" + new_ver + content, rf"(?<={NAME}\.readthedocs\.io/en/)\S+?(?=/)", "v" + new_ver ) return content setup( - name=NAME, - version=__version__, long_description=readme(), long_description_content_type="text/x-rst", ) diff --git a/src/flake8_absolute_import/__init__.py b/src/flake8_absolute_import/__init__.py index 808ebb0..5fe1ff3 100644 --- a/src/flake8_absolute_import/__init__.py +++ b/src/flake8_absolute_import/__init__.py @@ -3,13 +3,13 @@ flake8 plugin to require absolute imports **Author** - Brian Skinn (bskinn@alum.mit.edu) + Brian Skinn (brian.skinn@gmail.com) **File Created** 6 Sep 2019 **Copyright** - \(c) Brian Skinn 2019-2021 + \(c) Brian Skinn 2019-2023 **Source Repository** http://github.com/bskinn/flake8-absolute-import diff --git a/src/flake8_absolute_import/core.py b/src/flake8_absolute_import/core.py index 1073dbf..63ba5a2 100644 --- a/src/flake8_absolute_import/core.py +++ b/src/flake8_absolute_import/core.py @@ -3,13 +3,13 @@ flake8 plugin to require absolute imports **Author** - Brian Skinn (bskinn@alum.mit.edu) + Brian Skinn (brian.skinn@gmail.com) **File Created** 6 Sep 2019 **Copyright** - \(c) Brian Skinn 2019-2021 + \(c) Brian Skinn 2019-2023 **Source Repository** http://github.com/bskinn/flake8-absolute-import diff --git a/src/flake8_absolute_import/version.py b/src/flake8_absolute_import/version.py index 157a9e4..50b0805 100644 --- a/src/flake8_absolute_import/version.py +++ b/src/flake8_absolute_import/version.py @@ -3,13 +3,13 @@ flake8 plugin to require absolute imports **Author** - Brian Skinn (bskinn@alum.mit.edu) + Brian Skinn (brian.skinn@gmail.com) **File Created** 6 Sep 2019 **Copyright** - \(c) Brian Skinn 2019-2021 + \(c) Brian Skinn 2019-2023 **Source Repository** http://github.com/bskinn/flake8-absolute-import @@ -21,4 +21,4 @@ """ -__version__ = "1.0.0.1" +__version__ = "1.0.0.2" diff --git a/tests/test_plugin.py b/tests/test_plugin.py index a206235..ed24f3c 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -3,13 +3,13 @@ flake8 plugin to require absolute imports **Author** - Brian Skinn (bskinn@alum.mit.edu) + Brian Skinn (brian.skinn@gmail.com) **File Created** 6 Sep 2019 **Copyright** - \(c) Brian Skinn 2019-2021 + \(c) Brian Skinn 2019-2023 **Source Repository** http://github.com/bskinn/flake8-absolute-import @@ -29,14 +29,14 @@ from flake8_absolute_import import Plugin -def is_relative(s): - """Indicate if a given 'from s' import location is a relative import.""" - return s.startswith(".") +def is_relative(import_source): + """Indicate if a given 'from {source}' import location is a relative import.""" + return import_source.startswith(".") -def format_id(i): +def format_id(id_): """Provide parametrization id formatting for the given id.""" - return "{0} (expect {1}error)".format(i, "" if is_relative(i) else "no ") + return f"{id_} (expect {'' if is_relative(id_) else 'no '}error)" @pytest.mark.parametrize("code", ["import sys", "import flake8_absolute_import"]) @@ -106,11 +106,11 @@ def test_multilevel_relative_import(): def test_func_imports(impfrom): """Confirm plugin works for imports in functions.""" code = dedent( - """ + f""" def func(): - from {} import foo + from {impfrom} import foo """ - ).format(impfrom) + ) tree = ast.parse(code) assert (len(list(Plugin(tree).run())) == 1) == (impfrom.startswith(".")) @@ -120,11 +120,11 @@ def func(): def test_class_imports(impfrom): """Confirm plugin works for imports in class bodies.""" code = dedent( - """ + f""" class Bar: - from {} import foo + from {impfrom} import foo """ - ).format(impfrom) + ) tree = ast.parse(code) assert (len(list(Plugin(tree).run())) == 1) == (impfrom.startswith(".")) @@ -134,12 +134,12 @@ class Bar: def test_method_imports(impfrom): """Confirm plugin works for imports in class methods.""" code = dedent( - """ + f""" class Bar: def baz(self): - from {} import foo + from {impfrom} import foo """ - ).format(impfrom) + ) tree = ast.parse(code) assert (len(list(Plugin(tree).run())) == 1) == (impfrom.startswith(".")) diff --git a/tox.ini b/tox.ini index 88d37aa..1334960 100644 --- a/tox.ini +++ b/tox.ini @@ -2,33 +2,31 @@ minversion=2.0 isolated_build=True envlist= - py3{6,7,8,9,10,11}-f8_{3_7_0,latest} - py3{10}-f8_3_{7_x,8_x,9_x} + py3{7,8,9,10,11,12}-f8_{min,latest} + py3{11,12}-f8_{5_x} sdist_install + flake8 [testenv] commands= - pytest flake8 --version - flake8 tests + pytest deps= pytest f8_latest: flake8 - f8_3_7_x: flake8~=3.7.0 - f8_3_7_0: flake8==3.7.0 - f8_3_8_x: flake8~=3.8.0 - f8_3_9_x: flake8~=3.9.0 + f8_min: flake8==5.0 + f8_5_x: flake8~=5.0 [testenv:linux] platform=linux basepython= + py312: python3.12 py311: python3.11 py310: python3.10 py39: python3.9 py38: python3.8 py37: python3.7 - py36: python3.6 [testenv:sdist_install] commands=