diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/all_core_tests.yml similarity index 59% rename from .github/workflows/ci_tests.yml rename to .github/workflows/all_core_tests.yml index d0593d4..a359c50 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/all_core_tests.yml @@ -1,21 +1,27 @@ -name: ci-tests +name: 'PR: Run core tests' -on: push +on: pull_request jobs: all_checks: - name: Run all tests, lints, etc. (Python 3.11) + name: plus lints, etc. on Python 3.13 runs-on: ubuntu-latest - if: "!contains(github.event.head_commit.message, '[skip ci]')" + if: ${{ !contains(toJson(github.event), '[skip ci]') }} steps: - name: Check out repo - uses: actions/checkout@v2 + uses: actions/checkout@v6 + with: + persist-credentials: false - name: Install Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v6 with: - python-version: '3.11' + python-version: '3.13' + cache: 'pip' + cache-dependency-path: | + requirements-ci.txt + requirements-flake8.txt - name: Update pip & setuptools run: python -m pip install -U pip setuptools @@ -34,26 +40,27 @@ jobs: - name: Lint code run: tox -e flake8 - - name: Transmit to Codecov - run: codecov - just_tests: - name: Run only the test suite + name: for Python ${{ matrix.python }} runs-on: ubuntu-latest strategy: matrix: - python: ['3.8', '3.9', '3.10', '3.11', '3.12'] - if: "!contains(github.event.head_commit.message, '[skip ci]')" + python: ['3.10', '3.11', '3.12', '3.14'] + if: '!contains(github.event.head_commit.message, ''[skip ci]'')' steps: - name: Check out repo - uses: actions/checkout@v2 + uses: actions/checkout@v6 + with: + persist-credentials: false - name: Install Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python }} + cache: 'pip' + cache-dependency-path: requirements-ci.txt - name: Update pip & setuptools run: python -m pip install -U pip setuptools diff --git a/.github/workflows/release_platform_tests.yml b/.github/workflows/release_platform_tests.yml new file mode 100644 index 0000000..3fbb638 --- /dev/null +++ b/.github/workflows/release_platform_tests.yml @@ -0,0 +1,51 @@ +name: 'RELEASE: Run cross-platform tests' + +on: + pull_request: + types: + - opened + - reopened + - synchronize + - ready_for_review + branches: + - stable + +jobs: + just_tests: + name: ${{ matrix.os }} python ${{ matrix.py }} + runs-on: ${{ matrix.os }} + concurrency: + group: ${{ github.workflow }}-${{ matrix.os }}-${{ matrix.py }}-${{ github.ref }} + cancel-in-progress: true + if: ${{ !github.event.pull_request.draft && !contains(toJson(github.event), '[skip ci]') }} + strategy: + matrix: + os: ['windows-latest', 'macos-latest'] + py: ['3.10', '3.11', '3.12', '3.13'] + + steps: + - name: Check out repo + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Install Python + uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.py }} + cache: 'pip' + cache-dependency-path: requirements-ci.txt + + - name: Update pip & setuptools + run: python -m pip install -U pip setuptools + + - name: Install & report CI dependencies + run: | + python -m pip install -U --force-reinstall -r requirements-ci.txt + python --version + pip list + + - name: Run tests + run: | + pytest --cov + tox -e sdist_install diff --git a/.github/workflows/release_sdist_test.yml b/.github/workflows/release_sdist_test.yml new file mode 100644 index 0000000..df32bac --- /dev/null +++ b/.github/workflows/release_sdist_test.yml @@ -0,0 +1,76 @@ +name: 'RELEASE: Check sdist' + +on: + pull_request: + types: + - opened + - reopened + - synchronize + - ready_for_review + branches: + - stable + +jobs: + sdist_build_and_check: + name: builds & is testable + runs-on: 'ubuntu-latest' + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + if: ${{ !github.event.pull_request.draft && !contains(toJson(github.event), '[skip ci]')}} + + steps: + - name: Check out repo + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Install Python + uses: actions/setup-python@v6 + with: + python-version: '3.14' + cache: 'pip' + cache-dependency-path: | + requirements-dev.txt + requirements-flake8.txt + + - name: Install 'build' package + run: python -m pip install build + + - name: Build sdist + run: | + python -m build -s + ls -lah dist + + - name: Create sandbox + run: mkdir sandbox + + - name: Unpack sdist in sandbox + run: | + cp dist/*.gz sandbox/ + cd sandbox + tar xvf *.gz + + - name: Create venv + run: | + cd sandbox + python -m venv env + + # Only the dir of the unpacked sdist will have a digit in its name + - name: Store sdist unpack path + run: echo "UNPACK_PATH=$( find sandbox -maxdepth 1 -type d -regex 'sandbox/.+[0-9].+' )" >> $GITHUB_ENV + + - name: Report sdist unpack path + run: echo $UNPACK_PATH + + - name: Install dev req'ts to venv + run: | + source sandbox/env/bin/activate + cd "$UNPACK_PATH" + python -m pip install -r requirements-dev.txt + + - name: Run test suite in sandbox + run: | + source sandbox/env/bin/activate + cd "$UNPACK_PATH" + pytest diff --git a/.github/workflows/release_testdir_coverage.yml b/.github/workflows/release_testdir_coverage.yml new file mode 100644 index 0000000..03e3cb3 --- /dev/null +++ b/.github/workflows/release_testdir_coverage.yml @@ -0,0 +1,40 @@ +name: 'RELEASE: Ensure all tests ran' + +on: + pull_request: + types: + - opened + - reopened + - synchronize + - ready_for_review + branches: + - stable + +jobs: + testdir_coverage: + name: via coverage check + runs-on: 'ubuntu-latest' + if: ${{ !github.event.pull_request.draft && !contains(toJson(github.event), '[skip ci]')}} + + steps: + - name: Check out repo + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Install Python + uses: actions/setup-python@v6 + with: + python-version: '3.14' + cache: 'pip' + cache-dependency-path: | + requirements-ci.txt + + - name: Install CI requirements + run: pip install -r requirements-ci.txt + + - name: Run pytest with coverage + run: pytest --cov + + - name: Check 100% test execution + run: coverage report --include="tests/*" --fail-under=100 diff --git a/AUTHORS.md b/AUTHORS.md index 24aebe9..96fd011 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -2,7 +2,7 @@ Credits ======= `flake8-absolute-import` is authored and maintained by Brian Skinn -([Blog](https://bskinn.github.io)) ([Twitter](https://twitter.com/btskinn)). The +([Blog](https://bskinn.github.io)) ([Mastodon](https://fosstodon.org/@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). diff --git a/CHANGELOG.md b/CHANGELOG.md index ed90d11..5788c05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,80 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project strives to adhere (mostly) to -[Semantic Versioning](http://semver.org/spec/v2.0.0.html). +and this project follows an extension of [Semantic +Versioning](http://semver.org/spec/v2.0.0.html), adding a fourth `ADMIN` version +segment. + +The plugin interface changes implied by bumps in these SemVer versions are +specific to the error codes emitted by the plugin as part of flake8 execution: + +- **Major** + - Any addition or removal of an error code emitted by the plugin. + - Any change to the default behavior of the plugin with respect to an existing + error code. +- **Minor** + - Addition of new, configurable behavior of the plugin related to an existing + error code, which is not expected to alter the error code emitting behavior + of any existing user configurations. +- **Patch** + - Bugfixes to align the plugin's actual behavior with its stated behavior with + regard to error codes emitted. +- **Admin** + - Releases with changes that are expected to have no effect on plugin + behavior. + + +### [1.0.0.3] - 2025-11-27 + +This release contains significant changes, but none that should alter the +behavior of the plugin. Thus, it is being considered an administrative release +despite its scale. + +#### Dependencies + +- Add formal support for Python 3.13 and 3.14. + +- Drop formal support for Python 3.8 and 3.9. + +- Increase the minimum permitted Python version to install the package to Python + 3.10. + +#### Internal + +- Add types to the project code. + +#### Administrative + +- Upgrade `checkout` and `setup-python` actions. + +- Convert Azure Pipelines jobs to GitHub Actions and remove Azure config. + - The cross-platform auth/auth required to keep Pipelines working just wasn't + worth the effort to put in place. + - The cross-platform tests, sdist-install check, and test-dir coverage check + now all run only on PRs to `stable`. + - Credentials persistence was disabled for all. + - All were implemented with pip caching. + +- Convert core tests workflow to run on PR, not push, and modernize. + - Cache pip. + - Disable credentials persistence. + - Advance Python versions. + +- Remove CodeCov from workflows and requirements. + - The project/team is far too small for it to be valuable. + +- Update copyright end years. + +- Convert `README` to Markdown. + +- Modernize `pyproject.toml` and `setuptools` config. + +- Add multiple `tox` envs: + - `black` + - `mypy` + - `isort` + - `check` (rollup of `isort`, `black`, `flake8`, `mypy`) + - `build` ### [1.0.0.2] - 2023-10-08 diff --git a/LICENSE.txt b/LICENSE.txt index 3742dc3..12728c3 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2019-2023 Brian Skinn +Copyright (c) 2019-2025 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 8606d0b..0673235 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ -include AUTHORS.md LICENSE.txt README.rst CHANGELOG.md pyproject.toml +include AUTHORS.md LICENSE.txt README.md CHANGELOG.md pyproject.toml include requirements-dev.txt requirements-flake8.txt tox.ini +include src/flake8_absolute_import/py.typed diff --git a/README.md b/README.md new file mode 100644 index 0000000..51c5a69 --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ +## flake8-absolute-import: A flake8 plugin to require absolute imports + +#### Current Development Version + +[![GitHub Workflow Status][workflow badge]][workflow link target] + +#### Most Recent Stable Release + +[![PyPI Version][pypi badge]][pypi link target] +![Python Versions][python versions badge] + +#### Info + +[![MIT License][license badge]][license link target] +[![black formatted][black badge]][black link target] +[![PePY stats][pepy badge]][pepy link target] + +---- + +### Don't like relative imports? + +#### Lint 'em out! + +`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. + +Relative imports raise the `ABS101` error code: + +```py + from foo import bar # OK + from .foo import bar # ABS101 +``` + +---- + +Available on [PyPI][pypi link target] (`pip install flake8-absolute-import`). +`flake8` should automatically detect and load the plugin. `flake8`>=5.0 is +required. + +Source on [GitHub][gh repo]. Bug reports and feature requests are welcomed at +the [Issues][gh issues] page there. + +Copyright (c) Brian Skinn 2019-2025 + +The `flake8-absolute-import` documentation (currently docstrings and README) is +licensed under a [Creative Commons Attribution 4.0 International License][cc-by] +(CC-BY). The `flake8-absolute-import` codebase is released under the [MIT +License]. See [`LICENSE.txt`] for full license terms. + +[`ImportFrom`]: https://docs.python.org/3/library/ast.html#ast.ImportFrom +[`LICENSE.txt`]: https://github.com/bskinn/flake8-absolute-import/blob/main/LICENSE.txt + +[black badge]: https://img.shields.io/badge/code%20style-black-000000.svg +[black link target]: https://github.com/psf/black + +[cc-by]: http://creativecommons.org/licenses/by/4.0/ + +[gh issues]: https://github.com/bskinn/flake8-absolute-import/issues +[gh repo]: https://github.com/bskinn/flake8-absolute-import + +[license badge]: https://img.shields.io/github/license/mashape/apistatus.svg +[license link target]: https://github.com/bskinn/flake8-absolute-import/blob/stable/LICENSE.txt + +[MIT License]: https://opensource.org/licenses/MIT + +[pepy badge]: https://pepy.tech/badge/flake8-absolute-import/month +[pepy link target]: https://pepy.tech/projects/flake8-absolute-import?timeRange=threeMonths&category=version&includeCIDownloads=true&granularity=daily&viewType=line&versions=1.0.0.3%2C1.0.0.2%2C1.0.0.1%2C1.0 + +[pypi badge]: https://img.shields.io/pypi/v/flake8-absolute-import.svg?logo=pypi +[pypi link target]: https://pypi.org/project/flake8-absolute-import + +[python versions badge]: https://img.shields.io/pypi/pyversions/flake8-absolute-import.svg?logo=python + +[workflow badge]: https://img.shields.io/github/actions/workflow/status/bskinn/flake8-absolute-import/all_core_tests.yml?branch=main&logo=github +[workflow link target]: https://github.com/bskinn/flake8-absolute-import/actions diff --git a/README.rst b/README.rst deleted file mode 100644 index 7044895..0000000 --- a/README.rst +++ /dev/null @@ -1,72 +0,0 @@ -flake8-absolute-import -====================== - -*flake8 plugin to require absolute imports* - -**Current Development Version:** - -.. 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 - -.. image:: https://codecov.io/gh/bskinn/flake8-absolute-import/branch/main/graph/badge.svg - :target: https://codecov.io/gh/bskinn/flake8-absolute-import - -**Most Recent Stable Release:** - -.. image:: https://img.shields.io/pypi/v/flake8-absolute-import.svg?logo=pypi - :target: https://pypi.org/project/flake8-absolute-import - -.. image:: https://img.shields.io/pypi/pyversions/flake8-absolute-import.svg?logo=python - -**Info:** - -.. image:: https://img.shields.io/github/license/mashape/apistatus.svg - :target: https://github.com/bskinn/flake8-absolute-import/blob/stable/LICENSE.txt - -.. 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 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. - -Relative imports raise the ``ABS101`` error code: - -.. code:: python - - from foo import bar # OK - from .foo import bar # ABS101 - ----- - -Available on `PyPI `__ -(``pip install flake8-absolute-import``). ``flake8`` should automatically -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-2023 - -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-coretest.yml b/azure-coretest.yml deleted file mode 100644 index 9d89f6a..0000000 --- a/azure-coretest.yml +++ /dev/null @@ -1,33 +0,0 @@ -parameters: - pythons: [] - platforms: [] - -jobs: -- ${{ each python in parameters.pythons }}: - - ${{ each platform in parameters.platforms }}: - - job: ${{ coalesce(python.value.name, python.key) }}_${{ coalesce(platform, 'linux') }} - variables: - ${{ if eq(platform, 'linux') }}: - image: 'Ubuntu-latest' - ${{ if eq(platform, 'windows') }}: - image: 'windows-latest' - ${{ if eq(platform, 'macOs') }}: - image: 'macOS-latest' - ${{ if notIn(platform, 'macOs', 'linux', 'windows') }}: - image: 'Ubuntu-latest' - - pool: - vmImage: $[ variables.image ] - - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '${{ python.value.spec }}' - architecture: 'x64' - displayName: Use cached Python ${{ python.value.spec }} for tests. - - - script: pip install -r requirements-ci.txt - displayName: Install CI requirements - - - script: pytest - displayName: Run pytest (Python ${{ python.value.spec }}) diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index 910ca23..0000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,58 +0,0 @@ -trigger: - - release-* - -pr: - branches: - include: - - main - - stable - - -jobs: -- template: azure-coretest.yml - parameters: - pythons: - 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.11' - - - script: pip install -U tox - displayName: Install tox - - - script: tox -e flake8 - displayName: Lint the codebase - -- job: testdir_coverage - pool: - vmImage: 'Ubuntu-latest' - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '3.11' - - - script: pip install -r requirements-ci.txt - displayName: Install CI requirements - - - script: pytest --cov - displayName: Run pytest with coverage - - - script: coverage report --include="tests/*" --fail-under=100 - displayName: Check 100% test execution diff --git a/azure-sdisttest.yml b/azure-sdisttest.yml deleted file mode 100644 index 9f7e2a3..0000000 --- a/azure-sdisttest.yml +++ /dev/null @@ -1,74 +0,0 @@ -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 cbc890c..3fe321e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [build-system] requires = [ "wheel", - "setuptools>=61.2", + "setuptools>=77", ] build-backend = "setuptools.build_meta" @@ -11,9 +11,11 @@ description = "flake8 plugin to require absolute imports" authors = [ { name = "Brian Skinn", email = "brian.skinn@gmail.com" }, ] +license = "MIT" +license-files = [ + "LICENSE.txt", +] classifiers = [ - "License :: OSI Approved", - "License :: OSI Approved :: MIT License", "Natural Language :: English", "Framework :: Flake8", "Intended Audience :: Developers", @@ -21,21 +23,23 @@ classifiers = [ "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", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Topic :: Software Development :: Quality Assurance", + "Typing :: Typed", "Development Status :: 5 - Production/Stable", ] keywords = [ + "flake8", "flake8-plugin", "linting", "absolute-imports", "relative-imports", ] -requires-python = ">=3.6" +requires-python = ">=3.10" dependencies = [ "flake8>=5.0", ] @@ -47,12 +51,8 @@ dynamic = [ [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" @@ -60,9 +60,6 @@ ABS1 = "flake8_absolute_import:Plugin" platforms = [ "any", ] -license-files = [ - "LICENSE.txt", -] include-package-data = false [tool.setuptools.dynamic] @@ -81,17 +78,32 @@ namespaces = false 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 ) ''' + +[tool.mypy] +mypy_path = "src" +warn_return_any = true +warn_unused_configs = true +exclude = '''(?x)( + ^env/ + ) + ''' + +[[tool.mypy.overrides]] +module = [ + "pytest", +] +ignore_missing_imports = true diff --git a/requirements-ci.txt b/requirements-ci.txt index d4e2d78..36b2a91 100644 --- a/requirements-ci.txt +++ b/requirements-ci.txt @@ -1,4 +1,3 @@ -codecov pytest>=4.4.0 pytest-cov tox diff --git a/requirements-dev.txt b/requirements-dev.txt index b8537c1..d44b096 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,9 +1,8 @@ -build pytest>=4.4.0 pytest-cov restview tox +types-setuptools twine wget --r requirements-flake8.txt -e . diff --git a/setup.py b/setup.py index 19adc57..e6f732a 100644 --- a/setup.py +++ b/setup.py @@ -1,21 +1,22 @@ import re from pathlib import Path from setuptools import find_packages, setup +from typing import Any, cast -exec_ns = {} +exec_ns: dict[str, Any] = {} exec( Path("src", "flake8_absolute_import", "version.py").read_text(encoding="utf-8"), exec_ns, ) -__version__ = exec_ns["__version__"] +__version__ = cast(str, exec_ns["__version__"]) NAME = "flake8-absolute-import" version_override = None def readme(): - content = Path("README.rst").read_text() + content = Path("README.md").read_text(encoding="utf-8") new_ver = version_override if version_override else __version__ @@ -39,5 +40,5 @@ def content_update(content, pattern, sub): setup( long_description=readme(), - long_description_content_type="text/x-rst", + long_description_content_type="text/markdown", ) diff --git a/src/flake8_absolute_import/__init__.py b/src/flake8_absolute_import/__init__.py index 5fe1ff3..7705159 100644 --- a/src/flake8_absolute_import/__init__.py +++ b/src/flake8_absolute_import/__init__.py @@ -9,7 +9,7 @@ 6 Sep 2019 **Copyright** - \(c) Brian Skinn 2019-2023 + \(c) Brian Skinn 2019-2025 **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 63ba5a2..3fb5567 100644 --- a/src/flake8_absolute_import/core.py +++ b/src/flake8_absolute_import/core.py @@ -9,7 +9,7 @@ 6 Sep 2019 **Copyright** - \(c) Brian Skinn 2019-2023 + \(c) Brian Skinn 2019-2025 **Source Repository** http://github.com/bskinn/flake8-absolute-import @@ -22,21 +22,21 @@ """ import ast +from typing import Generator from flake8_absolute_import.version import __version__ - -ABS101 = "ABS101 Relative import found" +ABS101: str = "ABS101 Relative import found" class Visitor(ast.NodeVisitor): """NodeVisitor to report relative imports.""" - def __init__(self): + def __init__(self) -> None: """Create a Visitor with empty errors list.""" - self.errors = [] + self.errors: list[tuple[int, int, str]] = [] - def visit_ImportFrom(self, node): # noqa: N802 + def visit_ImportFrom(self, node) -> None: # noqa: N802 """Implement check for relative import.""" if node.level > 0: self.errors.append((node.lineno, node.col_offset, ABS101)) @@ -47,14 +47,14 @@ def visit_ImportFrom(self, node): # noqa: N802 class Plugin: """Core plugin class for flake8-absolute-import.""" - name = "flake8-absolute-import" + name: str = "flake8-absolute-import" version = __version__ - def __init__(self, tree): + def __init__(self, tree: ast.Module) -> None: """Create plugin instance from the provided AST.""" self._tree = tree - def run(self): + def run(self) -> Generator[tuple[int, int, str, type], None, None]: """Traverse the AST and collect the errors.""" visitor = Visitor() visitor.visit(self._tree) diff --git a/src/flake8_absolute_import/py.typed b/src/flake8_absolute_import/py.typed new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/flake8_absolute_import/py.typed @@ -0,0 +1 @@ + diff --git a/src/flake8_absolute_import/version.py b/src/flake8_absolute_import/version.py index e7b00e5..653d993 100644 --- a/src/flake8_absolute_import/version.py +++ b/src/flake8_absolute_import/version.py @@ -9,7 +9,7 @@ 6 Sep 2019 **Copyright** - \(c) Brian Skinn 2019-2023 + \(c) Brian Skinn 2019-2025 **Source Repository** http://github.com/bskinn/flake8-absolute-import @@ -21,4 +21,4 @@ """ -__version__ = "1.0.0.3.dev0" +__version__: str = "1.0.0.4.dev0" diff --git a/tests/test_plugin.py b/tests/test_plugin.py index ed24f3c..8da4732 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -9,7 +9,7 @@ 6 Sep 2019 **Copyright** - \(c) Brian Skinn 2019-2023 + \(c) Brian Skinn 2019-2025 **Source Repository** http://github.com/bskinn/flake8-absolute-import diff --git a/tox.ini b/tox.ini index 1334960..304fd4f 100644 --- a/tox.ini +++ b/tox.ini @@ -2,8 +2,8 @@ minversion=2.0 isolated_build=True envlist= - py3{7,8,9,10,11,12}-f8_{min,latest} - py3{11,12}-f8_{5_x} + py3{10,11,12,13,14}-f8_{min,latest} + py3{13,14}-f8_{5_x,6_x} sdist_install flake8 @@ -17,16 +17,24 @@ deps= f8_latest: flake8 f8_min: flake8==5.0 f8_5_x: flake8~=5.0 + f8_6_x: flake8~=6.0 + +[testenv:win32] +platform=win32 +basepython= + py313: python3.13 + py312: python3.12 + py311: python3.11 + py310: python3.10 [testenv:linux] platform=linux basepython= + py314: python3.14 + py313: python3.13 py312: python3.12 py311: python3.11 py310: python3.10 - py39: python3.9 - py38: python3.8 - py37: python3.7 [testenv:sdist_install] commands= @@ -40,6 +48,52 @@ commands= flake8 --version flake8 tests src +[testenv:black] +skip_install=True +deps=black +commands= + black {posargs} . + +[testenv:mypy] +description=Type-check with mypy +skip_install=True +deps=mypy +commands= + mypy --version + mypy {posargs} src tests + +[testenv:isort] +description=Sort, group, and coalesce imports +skip_install=True +deps=isort +commands= + isort --version + isort {posargs} src tests + +[testenv:check] +description=Run isort, black, flake8, and mypy +skip_install=True +deps= + isort + black + -r requirements-flake8.txt + mypy +commands= + isort --version + isort {posargs} src tests + black {posargs} . + flake8 --version + flake8 tests src + mypy --version + mypy {posargs} src tests + +[testenv:build] +description=Build project to wheel and sdist +skip_install=True +deps=build +commands= + python -m build + [pytest] addopts = -v -rsX -W error::Warning --strict-markers xfail_strict = True