diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 13309cca..212eb2c2 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,8 +1,7 @@ -**WARNING:** Please do not report issues about missing Django, see -[README](https://github.com/PyCQA/pylint-django#installation)! +**WARNING:** Please do not report issues about missing Django, see +[README](https://github.com/pylint-dev/pylint-django#installation)! **TODO:** make sure to post the output of `pip freeze` -**NOTES:** make sure you have the latest version of 3rd party packages -like `rest_framework`, `factory`, `model_utils`, etc. before reporting -issues! +**NOTES:** make sure you have the latest version of 3rd party packages like +`rest_framework`, `factory`, `model_utils`, etc. before reporting issues! diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 41b884b4..f2b3e760 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,110 +12,132 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.6] - toxenv: [django_not_installed, django_is_installed, flake8, pylint, readme] + python-version: ["3.12"] + toxenv: [django_not_installed, ruff, pylint, readme] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Execute tests run: | - pip install tox - pip install -e .[for_tests] + pip install -U pip poetry tox + poetry install - export TOXENV=${{ matrix.toxenv }} - export PYTHON=${{ matrix.python-version }} - tox + export TOXENV=${{ matrix.toxenv }} + export PYTHON=${{ matrix.python-version }} + tox - - test: - name: test latest / Django@${{ matrix.django-version }} / Python@${{ matrix.python-version }} + test_latest: + name: + test latest / Django@${{ matrix.django-version }} / Python@${{ + matrix.python-version }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: - python-version: [3.8, 3.9] - django-version: [-main, "4.0"] + python-version: ["3.10", "3.11", "3.12"] + django-version: ["5.0", "5.1", "-main"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Execute tests run: | - pip install tox - pip install -e .[for_tests] + pip install -U pip poetry tox + poetry install - export DJANGO=${{ matrix.django-version }} - export PYTHON=${{ matrix.python-version }} - export TOXENV=$(echo py${{ matrix.python-version }}-django${{ matrix.django-version }} | tr -d .) - tox + export DJANGO=${{ matrix.django-version }} + export PYTHON=${{ matrix.python-version }} + export TOXENV=$(echo py${{ matrix.python-version }}-django${{ matrix.django-version }} | tr -d .) + tox - name: Coveralls env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - pip install coveralls - coveralls --service=github + pip install coveralls + coveralls --service=github - test_deprecated: # testing older versions of python+django - name: test old stuff / Django@${{ matrix.django-version }} / Python@${{ matrix.python-version }} + test_maintained: + name: + test maintained / Django@${{ matrix.django-version }} / Python@${{ + matrix.python-version }} runs-on: ubuntu-latest - strategy: # TODO: use a YAML anchor - not supported in GitHub actions "yet" + strategy: fail-fast: false matrix: - python-version: [3.6, 3.7, 3.8, 3.9] - django-version: [3.2, 3.1, "3.0", "2.0", 1.11] + python-version: ["3.9", "3.10", "3.11", "3.12"] + django-version: ["4.0", "4.1", "4.2"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Execute tests run: | - pip install tox - pip install -e .[for_tests] + pip install -U pip poetry tox + poetry install + + export DJANGO=${{ matrix.django-version }} + export PYTHON=${{ matrix.python-version }} + export TOXENV=$(echo py${{ matrix.python-version }}-django${{ matrix.django-version }} | tr -d .) + tox + + test_deprecated: # testing older versions of python+django + name: + test old stuff / Django@${{ matrix.django-version }} / Python@${{ + matrix.python-version }} + runs-on: ubuntu-latest + strategy: # TODO: use a YAML anchor - not supported in GitHub actions "yet" + fail-fast: false + matrix: + python-version: [3.9] + django-version: [3.2, 3.1, "3.0", "2.2"] - export DJANGO=${{ matrix.django-version }} - export PYTHON=${{ matrix.python-version }} - export TOXENV=$(echo py${{ matrix.python-version }}-django${{ matrix.django-version }} | tr -d .) - tox + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} - - name: Coveralls - env: - COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Execute tests run: | - pip install coveralls - coveralls --service=github + pip install -U pip poetry tox + poetry install + + export DJANGO=${{ matrix.django-version }} + export PYTHON=${{ matrix.python-version }} + export TOXENV=$(echo py${{ matrix.python-version }}-django${{ matrix.django-version }} | tr -d .) + tox build_and_package_sanity: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6] + python-version: ["3.12"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Build run: | - pip install tox - pip install -e .[for_tests] - - ./scripts/build.sh + pip install -U pip poetry tox + poetry install + ./scripts/build.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..806a0f35 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,37 @@ +name: Release + +on: + release: + types: + - published + +env: + DEFAULT_PYTHON: 3.12 + +jobs: + release-pypi: + name: Upload release to PyPI + runs-on: ubuntu-latest + steps: + - name: Check out code from Github + uses: actions/checkout@v2.3.4 + - name: Set up Python ${{ env.DEFAULT_PYTHON }} + id: python + uses: actions/setup-python@v2.2.2 + with: + python-version: ${{ env.DEFAULT_PYTHON }} + - name: Install requirements + run: | + python -m pip install --disable-pip-version-check -U pip twine poetry "poetry-core<1.3.0" + - name: Build distributions + run: | + poetry build -f wheel + poetry build -f sdist + - name: Upload to PyPI + if: github.event_name == 'release' && startsWith(github.ref, 'refs/tags') + env: + TWINE_REPOSITORY: pypi + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} + run: | + twine upload --verbose dist/* diff --git a/.gitignore b/.gitignore index e365f2e6..ae188391 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,5 @@ nosetests.xml .idea env.txt .venv + +poetry.lock diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 235b3b79..ba5b0e14 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,26 +1,43 @@ -# https://pre-commit.com/ +ci: + skip: [pylint] + repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.1.0 + rev: v5.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: mixed-line-ending args: [--fix=lf] - id: debug-statements - # code formatting - - repo: https://github.com/PyCQA/flake8 - rev: 4.0.1 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: "v0.12.0" hooks: - - id: flake8 - args: [ --max-line-length=120 ] - - repo: https://github.com/psf/black - rev: 22.1.0 + - id: ruff + args: ["--fix"] + exclude: "tests/input/" + - id: ruff-format + exclude: ^pylint_django/tests/input.*$ + args: [--line-length=120] + - repo: https://github.com/tox-dev/pyproject-fmt + rev: "v2.6.0" hooks: - - id: black - args: [--safe, --line-length=120] - - repo: https://github.com/PyCQA/isort - rev: 5.10.1 + - id: pyproject-fmt + # https://pyproject-fmt.readthedocs.io/en/latest/#calculating-max-supported-python-version + additional_dependencies: ["tox>=4.9"] + - repo: https://github.com/codespell-project/codespell + rev: v2.4.1 hooks: - - id: isort - args: ['--profile', 'black'] + - id: codespell + args: ["--toml=pyproject.toml"] + additional_dependencies: + - tomli + - repo: local + hooks: + - id: pylint + name: pylint + entry: pylint + language: system + types: [python] + args: ["-rn", "-sn", "--fail-on=I"] + exclude: "tests/input/" diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6d023cb0..09b7e8a1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,13 +1,53 @@ Changelog ========= +Version 2.6.1 +------------- + +NOTICE +~~~~~~ + +We dropped support for Python 3.7, 3.8, and for pylint below 3.0. + +Bugfixes +~~~~~~~~ + +- Added Django aliases for ranges to support psycopg 2 and 3 (`#421 `_) +- Support for Python 3.12 datetime (`#427 `_) +- Fixed location of installed LICENSE file (`#431 `_) +- Fixed ForeignKeyStringChecker referencing linter config incorrectly (`#430 `_) + +Other +~~~~~ + +- CI now tests against Django 5.1 + +Version 2.6.0 (09 Oct. 2024) +---------------------------- + +Not released for lack of a release pipeline at the time the tag was created. + +Version 2.5.5 (14 May 2023) +--------------------------- + +NOTICE +~~~~~~ + +This version drops support for Python 3.6 + +Bugfixes +~~~~~~~~ + +- Fixed compatibility issue with datetime classes and python `3.12` (`#425 `_) + + Version 2.5.3 (25 Mär 2022) --------------------------- Bugfixes ~~~~~~~~ -- Fixed compatibility issue between pylint `2.12` and `2.13` to construct `ScopeConsumer` tuples correctly depending on version (`#358 `_) +- Fixed compatibility issue between pylint `2.12` and `2.13` to construct `ScopeConsumer` tuples correctly depending on version (`#358 `_) Other ~~~~~ @@ -20,7 +60,7 @@ Version 2.5.2 (18 Feb 2022) Bugfixes ~~~~~~~~ -- Fixed crash with assigning a class to a variable, and referencing the variable when subclassing (`#349 `_) +- Fixed crash with assigning a class to a variable, and referencing the variable when subclassing (`#349 `_) Other ~~~~~ @@ -34,7 +74,7 @@ Version 2.5.1 (16 Feb 2022) Bugfixes ~~~~~~~~ -- Fixed pickling for `augment_visit` (`#276 `_) +- Fixed pickling for `augment_visit` (`#276 `_) Version 2.5.0 (02 Jan 2022) --------------------------- @@ -42,23 +82,23 @@ Version 2.5.0 (02 Jan 2022) Bugfixes ~~~~~~~~ -- Fixed compatibility with astroid 2.9.1 (`#343 `_) +- Fixed compatibility with astroid 2.9.1 (`#343 `_) New ~~~ -- Removed false positive error of missing member in TextChoices tuples `#298 `_ (see pulls `#330 `_ and `#345 `_) -- Moved from Travis CI to GitHub Actions (`#366 `_ and `#340 `_) +- Removed false positive error of missing member in TextChoices tuples `#298 `_ (see pulls `#330 `_ and `#345 `_) +- Moved from Travis CI to GitHub Actions (`#366 `_ and `#340 `_) - Added pre-commit configuration and began enforcing black/isort code formatting -- Multiple test fixes (including `#338 `_) - newer versions of pylint expect a different format for the expected messages txt files. -- Bumped dependency for pylint-django-utils to get `multi-threaded pylint support `_ +- Multiple test fixes (including `#338 `_) - newer versions of pylint expect a different format for the expected messages txt files. +- Bumped dependency for pylint-django-utils to get `multi-threaded pylint support `_ Version 2.4.4 (26 Apr 2021) --------------------------- - Fix compatibility issues with pylint >= 2.8. Fixes - `#322 `_ and - `#323 `_ + `#322 `_ and + `#323 `_ Version 2.4.3 (09 Apr 2021) @@ -68,14 +108,14 @@ Version 2.4.3 (09 Apr 2021) - Teach pylint-django about all HTTP methods from the View class, not only ``get`` and ``post`` (Nicolás Quiroz) - Typo fixes for - `#314 `_ (John Sandall) + `#314 `_ (John Sandall) - Ignore ``unused-argument`` for ``*args``, ``**kwards`` in view method signatures Version 2.4.2 (08 Jan 2021) --------------------------- -- Moved `Faker` dependencies to test-only `#304 `_ +- Moved `Faker` dependencies to test-only `#304 `_ Version 2.4.1 (07 Jan 2021) @@ -88,14 +128,14 @@ Version 2.4.0 (06 Jan 2021) --------------------------- - Allowed configuration of the Django settings module to be used via a - commandline argument `#286 `_ + commandline argument `#286 `_ - If Django settings are not specified via a commandline argument or environment variable, an error is issued but defaults are loaded from Django, removing the - fatal error behaviour. `#277 `_ - and `#243 `_ + fatal error behaviour. `#277 `_ + and `#243 `_ - Fixed tests to work with pylint>2.6 - Fixed ``AttributeError: 'Subscript' object has no attribute 'name'`` error. - Closes `#284 `_ (@uy-rrodriguez) + Closes `#284 `_ (@uy-rrodriguez) - Pin Faker version to Prevent Asteroid Crash (James Pulec) - Remove Python 3.5 Support (EOL since Sept 2020 and Faker requires 3.6 anyway) (James Pulec) @@ -109,18 +149,18 @@ Version 2.3.0 (05 Aug 2020) (Bryan Mutai) - Start testing with Django 3.1 on Python 3.8 - Better error message when Django is not configured. Closes - `#277 `_ + `#277 `_ Version 2.2.0 (22 Jul 2020) --------------------------- - Rely on Django to resolve string references in ForeignKey fields. Refs - `#243 `_ (Alejandro Angulo) + `#243 `_ (Alejandro Angulo) - Suppress ``unused-argument`` for functions in migration modules. Fix - `#267 `_ + `#267 `_ - New checker for hard-coded ``auth.User``. Fix - `#244 `_ + `#244 `_ Version 2.1.0 (12 Jul 2020) @@ -131,7 +171,7 @@ Version 2.1.0 (12 Jul 2020) ``pylint_django.checkers.migrations`` - Add URL to project Changelog for PyPI (Ville Skyttä) - Fix failing test suite b/c of missing CSV dialect. Fix - `#268 `_ + `#268 `_ (Alejandro Angulo) @@ -146,7 +186,7 @@ Version 2.0.14 (25 Feb 2020) - Add support for Django 3.0 and Python 3.8 (Wayne Lambert) - Support ASGI. Fix - `#258 `_ (Sander Maijers) + `#258 `_ (Sander Maijers) Version 2.0.13 (23 Nov 2019), HackBulgaria edition @@ -154,7 +194,7 @@ Version 2.0.13 (23 Nov 2019), HackBulgaria edition - Suppress ``too-many-ancestors`` for class-based generic views - Add ``handler400``, ``handler403``, ``handler404`` to good_names. Fix - `#248 `_ + `#248 `_ Version 2.0.12 (04 Nov 2019) @@ -163,11 +203,11 @@ Version 2.0.12 (04 Nov 2019) - Fix too broad suppression of ``unused-argument`` warnings for functions and methods where the first argument is named ``request``. Now issues warnings for the rest of the arguments if they are unused. Fix - `#249 `_ (Pascal Urban) + `#249 `_ (Pascal Urban) - Pass arguments of ``scripts/test.sh`` to ``test_func/pytest`` to ease development (Pascal Urban) - Document behavior when ForeignKey fields are referenced as strings. Fix - `#241 `_ + `#241 `_ Version 2.0.11 (10 July 2019) @@ -180,11 +220,11 @@ Version 2.0.10 (07 July 2019), Novi Sad edition ----------------------------------------------- - Suppress ``no-member`` for ``ManyToManyField``. Fix - `#192 `_ and - `#237 `_ (Pierre Chiquet) + `#192 `_ and + `#237 `_ (Pierre Chiquet) - Fix ``UnboundLocalError`` with ``ForeignKey(to=)``. Fix - `#232 `_ (Sardorbek Imomaliev) + `#232 `_ (Sardorbek Imomaliev) Version 2.0.9 (26 April 2019) @@ -195,37 +235,37 @@ Version 2.0.9 (26 April 2019) that is used in this pattern ``app.Model`` and also there is some other ``bool`` const like ``null=True`` right after ``to``. (Sardorbek Imomaliev) - Don't crash if ForeignKey field doesn't have keyword arguments Fix - `#230 `_ + `#230 `_ Version 2.0.8 (18 April 2019) ----------------------------- - Support recursive (self) ForeignKey relations. Fix - `#208 `_ (Daniil Kharkov) + `#208 `_ (Daniil Kharkov) Version 2.0.7 (16 April 2019) ----------------------------- - Fixed ``AstroidImportError`` for ``DecimalField``. Fix - `#221 `_ (Daniil Kharkov) + `#221 `_ (Daniil Kharkov) - Add ``load_configuration()`` in ``pylint_django/__init__.py``. Fix #222 - `#222 `_ + `#222 `_ - Support ForeignKey relations with ``to`` keyword. Fix - `#223 `_ (Daniil Kharkov) + `#223 `_ (Daniil Kharkov) Version 2.0.6 (27 Feb 2019) --------------------------- - Updating dependency version of pylint-plugin-utils as pylint 2.3 release - was not compatible `#220 `_ + was not compatible `#220 `_ - Improvements to tox.ini: - `#217 `_ - and `#216 `_ (@aerostitch) + `#217 `_ + and `#216 `_ (@aerostitch) - Add support for new load_configuration hook of pylint - `#214 `_ (@matusvalo) + `#214 `_ (@matusvalo) - 'urlpatterns' no longer reported as an invalid constant name @@ -242,26 +282,26 @@ Version 2.0.4 (do not use) -------------------------- - Avoid traceback with concurrent execution. Fix - `#197 `_ + `#197 `_ - Suppress ``no-member`` errors for ``LazyFunction`` in factories - Suppress ``no-member`` errors for ``RelatedManager`` fields - Clean up compatibility code: - `PR #207 `_ + `PR #207 `_ Version 2.0.3 (do not use) -------------------------- -- Fixing compatability between ranges of astroid (2.0.4 -> 2.1) and +- Fixing compatibility between ranges of astroid (2.0.4 -> 2.1) and pylint (2.1.1 -> 2.2). - `#201 `_ and - `#202 `_ + `#201 `_ and + `#202 `_ Version 2.0.2 (26 Aug 2018) --------------------------- - Suppress false-positive no-self-argument in factory.post_generation. Fix - `#190 `_ (Federico Bond) + `#190 `_ (Federico Bond) Version 2.0.1 (20 Aug 2018) @@ -269,14 +309,14 @@ Version 2.0.1 (20 Aug 2018) - Enable testing with Django 2.1 - Add test for Model.objects.get_or_create(). Close - `#156 `__ + `#156 `__ - Add test for objects.exclude(). Close - `#177 `__ + `#177 `__ - Fix Instance of 'Model' has no 'id' member (no-member), fix Class 'UserCreationForm' has no 'declared_fields' member. Close - `#184 `__ + `#184 `__ - Fix for Instance of 'ManyToManyField' has no 'add' member. Close - `#163 `__ + `#163 `__ - Add test & fix for unused arguments on class based views @@ -294,10 +334,10 @@ Version 0.11.1 (25 May 2018), the DjangoCon Heidelberg edition - Enable test case for ``urlpatterns`` variable which was previously disabled - Disable ``unused-argument`` message for the ``request`` argument passed to view functions. Fix - `#155 `__ + `#155 `__ - Add transformations for ``model_utils`` managers instead of special-casing them. Fix - `#160 `__ + `#160 `__ Version 0.11 (18 April 2018), the TestCon Moscow edition @@ -318,29 +358,29 @@ Version 0.10.0 (10 April 2018) - Remove the compatibility layer for older astroid versions - Make flake8 happy. Fix - `#102 `__ + `#102 `__ - Fix: compatibility with Python < 3.6 caused by ``ModuleNotFoundError`` not available on older versions of Python (Juan Rial) - Show README and CHANGELOG on PyPI. Fix - `#122 `__ + `#122 `__ - Fix explicit unicode check with ``python_2_unicode_compatible`` base models (Federico Bond) - Suppress ``not-an-iterable`` message for 'objects'. Fix - `#117 `__ + `#117 `__ - Teach pylint_django that ``objects.all()`` is subscriptable. Fix - `#144 `__ + `#144 `__ - Suppress ``invalid-name`` for ``wsgi.application``. Fix - `#77 `__ + `#77 `__ - Add test for ``WSGIRequest.context``. Closes - `#78 `__ + `#78 `__ - Register transforms for ``FileField``. Fix - `#60 `__ + `#60 `__ - New checker ``pylint_django.checkers.db_performance``. Enables checking of migrations and reports when there's an ``AddField`` operation with a default value which may slow down applying migrations on large tables. This may also lead to production tables being locked while migrations are being applied. Fix - `#118 `__ + `#118 `__ - Suppress ``no-member`` for ``factory.SubFactory`` objects. Useful when model factories use ``factory.SubFactory()`` for foreign key relations. @@ -352,30 +392,30 @@ Version 0.9.4 (12 March 2018) - Add an optional dependency on Django - Fix the ``DjangoInstalledChecker`` so it can actually warn when Django isn't available -- Fix `#136 `__ by +- Fix `#136 `__ by adding automated build and sanity test scripts Version 0.9.3 (removed from PyPI) --------------------------------- -- Fix `#133 `__ and - `#134 `__ by +- Fix `#133 `__ and + `#134 `__ by including package data when building wheel and tar.gz packages for PyPI (Joseph Herlant) Version 0.9.2 (broken) ---------------------- -- Fix `#129 `__ - +- Fix `#129 `__ - Move tests under ``site-packages/pylint_django`` (Mr. Senko) -- Fix `#96 `__ - List +- Fix `#96 `__ - List Django as a dependency (Mr. Senko) Version 0.9.1 (26 Feb 2018) --------------------------- -- Fix `#123 `__ - - Update links after the move to PyCQA (Mr. Senko) +- Fix `#123 `__ - + Update links after the move to pylint-dev (Mr. Senko) - Add test for Meta class from django\_tables2 (Mr. Senko) - Fix flake8 complaints (Peter Bittner) - Add missing .txt and .rc test files to MANIFEST.in (Joseph Herlant) @@ -383,11 +423,11 @@ Version 0.9.1 (26 Feb 2018) Version 0.9 (25 Jan 2018) ------------------------- -- Fix `#120 `__ - +- Fix `#120 `__ - TypeError: 'NamesConsumer' object does not support indexing (Simone Basso) -- Fix `#110 `__ and - `#35 `__ - resolve +- Fix `#110 `__ and + `#35 `__ - resolve ForeignKey models specified as strings instead of class names (Mr. Senko) @@ -396,97 +436,97 @@ Version 0.8.0 (20 Jan 2018) - This is the last version to support Python 2. Issues a deprecation warning! -- `#109 `__, adding +- `#109 `__, adding 'urlpatterns', 'register', 'app\_name' to good names. Obsoletes - `#111 `__, fixes - `#108 `__ (Vinay + `#111 `__, fixes + `#108 `__ (Vinay Pai) - Add 'handler500' to good names (Mr. Senko) -- `#103 `__: Support +- `#103 `__: Support factory\_boy's DjangoModelFactory Meta class (Konstantinos Koukopoulos) -- `#100 `__: Fix +- `#100 `__: Fix E1101:Instance of '**proxy**\ ' has no 'format' member' when using .format() on a ugettext\_lazy translation. Fixes - `#80 `__ + `#80 `__ (canarduck) -- `#99 `__: Add tests +- `#99 `__: Add tests and transforms for DurationField, fixes - `#95 `__ (James M. + `#95 `__ (James M. Allen) -- `#92 `__: Add json +- `#92 `__: Add json field to WSGIRequest proxy (sjk4sc) -- `#84 `__: Add support +- `#84 `__: Add support for django.contrib.postgres.fields and UUIDField (Villiers Strauss) - Stop testing with older Django versions. Currently testing with Django 1.11.x and 2.0 - Stop testing on Python 2, no functional changes in the source code though - Update tests and require latest version of pylint (>=1.8), fixes - `#53 `__, - `#97 `__ -- `#81 `__ Fix + `#53 `__, + `#97 `__ +- `#81 `__ Fix 'duplicate-except' false negative for except blocks which catch the ``DoesNotExist`` exception. Version 0.7.4 ------------- -- `#88 `__ Fixed builds +- `#88 `__ Fixed builds with Django 1.10 (thanks to `federicobond `__) -- `#91 `__ Fixed race +- `#91 `__ Fixed race condition when running with pylint parallel execution mode (thanks to `jeremycarroll `__) -- `#64 `__ "Meta is +- `#64 `__ "Meta is old style class" now suppressed on BaseSerializer too (thanks to `unklphil `__) -- `#70 `__ Updating to +- `#70 `__ Updating to handle newer pylint/astroid versions (thanks to `iXce `__) Version 0.7.2 ------------- -- `#76 `__ Better +- `#76 `__ Better handling of mongoengine querysetmanager -- `#73 `__ - `#72 `__ Make package +- `#73 `__ + `#72 `__ Make package zip safe to help fix some path problems -- `#68 `__ Suppressed +- `#68 `__ Suppressed invalid constant warning for "app\_name" in urls.py -- `#67 `__ Fix +- `#67 `__ Fix view.args and view.kwargs -- `#66 `__ accessing +- `#66 `__ accessing \_meta no longer causes a protected-access warning as this is a public API as of Django 1.8 -- `#65 `__ Add support +- `#65 `__ Add support of mongoengine module. -- `#59 `__ Silence +- `#59 `__ Silence old-style-class for widget Meta Version 0.7.1 ------------- -- `#52 `__ - Fixed +- `#52 `__ - Fixed stupid mistake when using versioninfo Version 0.7 ----------- -- `#51 `__ - Fixed +- `#51 `__ - Fixed compatibility with pylint 1.5 / astroid 1.4.1 Version 0.6.1 ------------- -- `#43 `__ - Foreign +- `#43 `__ - Foreign key ID access (``somefk_id``) does not raise an 'attribute not found' warning -- `#31 `__ - Support +- `#31 `__ - Support for custom model managers (thanks `smirolo `__) -- `#48 `__ - Added +- `#48 `__ - Added support for django-restframework (thanks `mbertolacci `__) @@ -496,37 +536,37 @@ Version 0.6 - Pylint 1.4 dropped support for Python 2.6, therefore a constraint is added that pylint-django will only work with Python2.6 if pylint<=1.3 is installed -- `#40 `__ - pylint +- `#40 `__ - pylint 1.4 warned about View and Model classes not having enough public methods; this is suppressed -- `#37 `__ - fixed an +- `#37 `__ - fixed an infinite loop when using astroid 1.3.3+ -- `#36 `__ - no +- `#36 `__ - no longer warning about lack of ``__unicode__`` method on abstract model classes -- `PR #34 `__ - prevent +- `PR #34 `__ - prevent warning about use of ``super()`` on ModelManager classes Version 0.5.5 ------------- -- `PR #27 `__ - better +- `PR #27 `__ - better ``ForeignKey`` transforms, which now work when of the form ``othermodule.ModelClass``. This also fixes a problem where an inferred type would be ``_Yes`` and pylint would fail -- `PR #28 `__ - better +- `PR #28 `__ - better knowledge of ``ManyToManyField`` classes Version 0.5.4 ------------- -- Improved resiliance to inference failure when Django types cannot be +- Improved resilience to inference failure when Django types cannot be inferred (which can happen if Django is not on the system path Version 0.5.3 ------------- -- `Issue #25 `__ +- `Issue #25 `__ Fixing cases where a module defines ``get`` as a method Version 0.5.2 @@ -544,20 +584,20 @@ Version 0.5.1 Version 0.5 ----------- -- `Issue #7 `__ +- `Issue #7 `__ Improved handling of Django model fields -- `Issue #10 `__ No +- `Issue #10 `__ No warning about missing **unicode** if the Django python3/2 - compatability tools are used -- `Issue #11 `__ + compatibility tools are used +- `Issue #11 `__ Improved handling of Django form fields -- `Issue #12 `__ +- `Issue #12 `__ Improved handling of Django ImageField and FileField objects -- `Issue #14 `__ +- `Issue #14 `__ Models which do not define **unicode** but whose parents do now have a new error (W5103) instead of incorrectly warning about no **unicode** being present. -- `Issue #21 `__ +- `Issue #21 `__ ``ForeignKey`` and ``OneToOneField`` fields on models are replaced with instance of the type they refer to in the AST, which allows pylint to generate correct warnings about attributes they may or may @@ -575,9 +615,9 @@ Version 0.2 - Pylint now recognises ``BaseForm`` as an ancestor of ``Form`` and subclasses - Improved ``Form`` support -- `Issue #2 `__ - a +- `Issue #2 `__ - a subclass of a ``Model`` or ``Form`` also has warnings about a ``Meta`` class suppressed. -- `Issue #3 `__ - +- `Issue #3 `__ - ``Form`` and ``ModelForm`` subclasses no longer warn about ``Meta`` classes. diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 07160217..7bd39c12 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,22 +1,23 @@ -* [carlio](https://github.com/carlio) -* [mbarrien](https://github.com/mbarrien) -* [frost-nzcr4](https://github.com/frost-nzcr4) -* [ustun](https://github.com/ustun) -* [jproffitt](https://github.com/jproffitt) -* [lhupfeldt](https://github.com/lhupfeldt) -* [smirolo](https://github.com/smirolo) -* [mbertolacci](https://github.com/mbertolacci) -* [atodorov](https://github.com/atodorov) -* [bittner](https://github.com/bittner) -* [federicobond](https://github.com/federicobond) -* [matusvalo](https://github.com/matusvalo) -* [fadedDexofan](https://github.com/fadeddexofan) -* [imomaliev](https://github.com/imomaliev) -* [psrb](https://github.com/psrb) -* [WayneLambert](https://github.com/WayneLambert) -* [alejandro-angulo](https://github.com/alejandro-angulo) -* [brymut](https://github.com/brymut) -* [michael-k](https://github.com/michael-k) -* [naquiroz](https://github.com/naquiroz) -* [john-sandall](https://github.com/john-sandall) -* [dineshtrivedi](https://github.com/dineshtrivedi) +- [carlio](https://github.com/carlio) +- [mbarrien](https://github.com/mbarrien) +- [frost-nzcr4](https://github.com/frost-nzcr4) +- [ustun](https://github.com/ustun) +- [jproffitt](https://github.com/jproffitt) +- [lhupfeldt](https://github.com/lhupfeldt) +- [smirolo](https://github.com/smirolo) +- [mbertolacci](https://github.com/mbertolacci) +- [atodorov](https://github.com/atodorov) +- [bittner](https://github.com/bittner) +- [federicobond](https://github.com/federicobond) +- [matusvalo](https://github.com/matusvalo) +- [fadedDexofan](https://github.com/fadeddexofan) +- [imomaliev](https://github.com/imomaliev) +- [psrb](https://github.com/psrb) +- [WayneLambert](https://github.com/WayneLambert) +- [alejandro-angulo](https://github.com/alejandro-angulo) +- [brymut](https://github.com/brymut) +- [michael-k](https://github.com/michael-k) +- [naquiroz](https://github.com/naquiroz) +- [john-sandall](https://github.com/john-sandall) +- [dineshtrivedi](https://github.com/dineshtrivedi) +- [matejsp](https://github.com/matejsp) diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 9c19d076..00000000 --- a/LICENSE +++ /dev/null @@ -1,339 +0,0 @@ -GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - Pylint plugin for improving code analysis for when using Django - Copyright (C) 2013 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - {signature of Ty Coon}, 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/LICENSE b/LICENSE new file mode 120000 index 00000000..adc0c0d0 --- /dev/null +++ b/LICENSE @@ -0,0 +1 @@ +pylint_django/LICENSE \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 539e6bc5..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -include *.md -include *.rst -include LICENSE -include pylint_django/transforms/transforms/*.py -include pylint_django/tests/test_django_not_installed.sh -recursive-include pylint_django/tests/ *.py *.rc *.txt diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..fab465f6 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ + +install: + pip install poetry + poetry install + + +make test: + tox diff --git a/README.rst b/README.rst index fdd06bb0..eb1ef8fc 100644 --- a/README.rst +++ b/README.rst @@ -1,11 +1,11 @@ pylint-django ============= -.. image:: https://github.com/PyCQA/pylint-django/actions/workflows/build.yml/badge.svg - :target: https://github.com/PyCQA/pylint-django/actions/workflows/build.yml +.. image:: https://github.com/pylint-dev/pylint-django/actions/workflows/build.yml/badge.svg + :target: https://github.com/pylint-dev/pylint-django/actions/workflows/build.yml -.. image:: https://coveralls.io/repos/github/PyCQA/pylint-django/badge.svg?branch=master - :target: https://coveralls.io/github/PyCQA/pylint-django?branch=master +.. image:: https://coveralls.io/repos/github/pylint-dev/pylint-django/badge.svg?branch=master + :target: https://coveralls.io/github/pylint-dev/pylint-django?branch=master .. image:: https://img.shields.io/pypi/v/pylint-django.svg :target: https://pypi.python.org/pypi/pylint-django @@ -29,10 +29,10 @@ To install:: **WARNING:** ``pylint-django`` will not install ``Django`` by default because this causes more trouble than good, -`see discussion `__. If you wish +`see discussion `__. If you wish to automatically install the latest version of ``Django`` then:: - pip install pylint-django[with_django] + pip install pylint-django[with-django] otherwise sort out your testing environment and please **DO NOT** report issues about missing Django! diff --git a/SECURITY.md b/SECURITY.md index f2b86058..c2a303fe 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,17 +2,13 @@ ## Supported Versions -| Version | Supported | -| ------- | ------------------ | +| Version | Supported | +| ------------------------------------------------- | ------------------ | | [latest](https://pypi.org/project/pylint-django/) | :heavy_check_mark: | ## Reporting a Vulnerability -In case you have found a security problem with pylint-django *DO NOT* report -it into GitHub Issues. Instead go to -[https://tidelift.com/security](https://tidelift.com/security) -and follow the instructions there. - -At least one of the package maintainers ([@atodorov](http://github.com/atodorov)) -is a lifter at Tidelift and will be notified when you report the security -problem with them! +In case you have found a security problem with pylint-django _DO NOT_ report it into +GitHub Issues. Instead go to +[https://tidelift.com/security](https://tidelift.com/security) and follow the +instructions there. diff --git a/custom_dict.txt b/custom_dict.txt new file mode 100644 index 00000000..450d4fe9 --- /dev/null +++ b/custom_dict.txt @@ -0,0 +1 @@ +astroid diff --git a/pylint_django/LICENSE b/pylint_django/LICENSE new file mode 100644 index 00000000..9c19d076 --- /dev/null +++ b/pylint_django/LICENSE @@ -0,0 +1,339 @@ +GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + Pylint plugin for improving code analysis for when using Django + Copyright (C) 2013 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/pylint_django/__init__.py b/pylint_django/__init__.py index b0920575..27575371 100644 --- a/pylint_django/__init__.py +++ b/pylint_django/__init__.py @@ -1,12 +1,6 @@ """pylint_django module.""" -from __future__ import absolute_import - -import sys from pylint_django import plugin -if sys.version_info < (3,): - raise DeprecationWarning("Version 0.11.1 was the last to support Python 2. " "Please migrate to Python 3!") - register = plugin.register # pylint: disable=invalid-name load_configuration = plugin.load_configuration # pylint: disable=invalid-name diff --git a/pylint_django/__pkginfo__.py b/pylint_django/__pkginfo__.py index a6d71329..ebcf236c 100644 --- a/pylint_django/__pkginfo__.py +++ b/pylint_django/__pkginfo__.py @@ -1,2 +1,3 @@ """pkginfo.""" + BASE_ID = 51 diff --git a/pylint_django/augmentations/__init__.py b/pylint_django/augmentations/__init__.py index e067709d..a5eef379 100644 --- a/pylint_django/augmentations/__init__.py +++ b/pylint_django/augmentations/__init__.py @@ -1,4 +1,5 @@ """Augmentations.""" + # pylint: disable=invalid-name import functools import itertools @@ -11,23 +12,10 @@ from django import VERSION as django_version from django.utils import termcolors from django.views.generic.base import ContextMixin, RedirectView, View -from django.views.generic.dates import ( - DateMixin, - DayMixin, - MonthMixin, - WeekMixin, - YearMixin, -) -from django.views.generic.detail import ( - SingleObjectMixin, - SingleObjectTemplateResponseMixin, - TemplateResponseMixin, -) +from django.views.generic.dates import DateMixin, DayMixin, MonthMixin, WeekMixin, YearMixin +from django.views.generic.detail import SingleObjectMixin, SingleObjectTemplateResponseMixin, TemplateResponseMixin from django.views.generic.edit import DeletionMixin, FormMixin, ModelFormMixin -from django.views.generic.list import ( - MultipleObjectMixin, - MultipleObjectTemplateResponseMixin, -) +from django.views.generic.list import MultipleObjectMixin, MultipleObjectTemplateResponseMixin from pylint.checkers.base import DocStringChecker, NameChecker from pylint.checkers.classes import ClassChecker from pylint.checkers.design_analysis import MisdesignChecker @@ -42,7 +30,7 @@ # get its attributes that way - and this used to be the method - but unfortunately # there's no guarantee that Django is properly configured at that stage, and importing # anything from the django.db package causes an ImproperlyConfigured exception. -# Therefore we'll fall back on a hard-coded list of attributes which won't be as accurate, +# Therefore, we'll fall back on a hard-coded list of attributes which won't be as accurate, # but this is not 100% accurate anyway. MANAGER_ATTRS = { "none", @@ -337,7 +325,7 @@ def ignore_import_warnings_for_related_fields(orig_method, self, node): new_things[name] = stmts # ScopeConsumer changed between pylint 2.12 and 2.13 - # see https://github.com/PyCQA/pylint/issues/5970#issuecomment-1078778393 + # see https://github.com/pylint-dev/pylint/issues/5970#issuecomment-1078778393 if hasattr(consumer, "consumed_uncertain"): # this is pylint >= 2.13, and the ScopeConsumer tuple has an additional field sc_args = (new_things, consumer.consumed, consumer.consumed_uncertain, consumer.scope_type) @@ -378,12 +366,11 @@ class ModelB(models.Model): # if this is a X_set method, that's a pretty strong signal that this is the default # Django name, rather than one set by related_name quack = True - else: - # we will - if isinstance(node.parent, Attribute): - func_name = getattr(node.parent, "attrname", None) - if func_name in MANAGER_ATTRS: - quack = True + # we will + elif isinstance(node.parent, Attribute): + func_name = getattr(node.parent, "attrname", None) + if func_name in MANAGER_ATTRS: + quack = True if quack: children = list(node.get_children()) @@ -534,7 +521,7 @@ def _attribute_is_magic(node, attrs, parents): try: for cls in node.last_child().inferred(): if isinstance(cls, Super): - cls = cls._self_class # pylint: disable=protected-access + cls = cls._self_class # pylint: disable=protected-access # noqa:PLW2901 if node_is_subclass(cls, *parents) or cls.qname() in parents: return True except InferenceError: @@ -803,7 +790,8 @@ def pylint_newstyle_classdef_compat(linter, warning_name, augment): return suppress_message( linter, - getattr(NewStyleConflictChecker, "visit_classdef"), + # pylint: disable-next=no-member + NewStyleConflictChecker.visit_classdef, warning_name, augment, ) @@ -848,7 +836,7 @@ def apply_augmentations(linter): is_urls_module_valid_constant, ) - # supress errors when accessing magical class attributes + # suppress errors when accessing magical class attributes suppress_message(linter, TypeChecker.visit_attribute, "no-member", is_manager_attribute) suppress_message(linter, TypeChecker.visit_attribute, "no-member", is_admin_attribute) suppress_message(linter, TypeChecker.visit_attribute, "no-member", is_model_attribute) diff --git a/pylint_django/checkers/__init__.py b/pylint_django/checkers/__init__.py index 1a14b746..c1e12fa9 100644 --- a/pylint_django/checkers/__init__.py +++ b/pylint_django/checkers/__init__.py @@ -1,4 +1,5 @@ """Checkers.""" + from pylint_django.checkers.auth_user import AuthUserChecker from pylint_django.checkers.django_installed import DjangoInstalledChecker from pylint_django.checkers.foreign_key_strings import ForeignKeyStringsChecker diff --git a/pylint_django/checkers/auth_user.py b/pylint_django/checkers/auth_user.py index af6a6600..a54d5c35 100644 --- a/pylint_django/checkers/auth_user.py +++ b/pylint_django/checkers/auth_user.py @@ -1,12 +1,10 @@ -from pylint import checkers, interfaces -from pylint.checkers import utils +from pylint import checkers from pylint_django.__pkginfo__ import BASE_ID +from pylint_django.compat import check_messages class AuthUserChecker(checkers.BaseChecker): - __implements__ = (interfaces.IAstroidChecker,) - name = "auth-user-checker" msgs = { @@ -22,14 +20,14 @@ class AuthUserChecker(checkers.BaseChecker): ), } - @utils.check_messages("hard-coded-auth-user") + @check_messages("hard-coded-auth-user") def visit_const(self, node): # for now we don't check if the parent is a ForeignKey field # because the user model should not be hard-coded anywhere if node.value == "auth.User": self.add_message("hard-coded-auth-user", node=node) - @utils.check_messages("imported-auth-user") + @check_messages("imported-auth-user") def visit_importfrom(self, node): if node.modname == "django.contrib.auth.models": for imported_names in node.names: diff --git a/pylint_django/checkers/django_installed.py b/pylint_django/checkers/django_installed.py index 5a0becfd..07723b9e 100644 --- a/pylint_django/checkers/django_installed.py +++ b/pylint_django/checkers/django_installed.py @@ -1,9 +1,7 @@ -from __future__ import absolute_import - from pylint.checkers import BaseChecker -from pylint.checkers.utils import check_messages from pylint_django.__pkginfo__ import BASE_ID +from pylint_django.compat import check_messages class DjangoInstalledChecker(BaseChecker): diff --git a/pylint_django/checkers/foreign_key_strings.py b/pylint_django/checkers/foreign_key_strings.py index c145301e..20b102eb 100644 --- a/pylint_django/checkers/foreign_key_strings.py +++ b/pylint_django/checkers/foreign_key_strings.py @@ -1,11 +1,8 @@ -from __future__ import absolute_import - import astroid from pylint.checkers import BaseChecker -from pylint.checkers.utils import check_messages -from pylint.interfaces import IAstroidChecker from pylint_django.__pkginfo__ import BASE_ID +from pylint_django.compat import check_messages from pylint_django.transforms import foreignkey @@ -27,8 +24,6 @@ class ForeignKeyStringsChecker(BaseChecker): Some basic default settings were used, however this will lead to less accurate linting. Consider passing in an explicit Django configuration file to match your project to improve accuracy.""" - __implements__ = (IAstroidChecker,) - name = "Django foreign keys referenced by strings" options = ( @@ -67,7 +62,7 @@ def open(self): # Django is configured explicitly, and will use some basic defaults in that # case. However, as this is a WARNING not a FATAL, the error must be raised # with an AST node - only F and R messages are scope exempt (see - # https://github.com/PyCQA/pylint/blob/master/pylint/constants.py#L24) + # https://github.com/pylint-dev/pylint/blob/master/pylint/constants.py#L24) # However, testing to see if Django is configured happens in `open()` # before any modules are inspected, as Django needs to be configured with @@ -80,9 +75,7 @@ def open(self): # state is stashed in this property. try: - from django.core.exceptions import ( # pylint: disable=import-outside-toplevel - ImproperlyConfigured, - ) + from django.core.exceptions import ImproperlyConfigured # pylint: disable=import-outside-toplevel except ModuleNotFoundError: return @@ -90,46 +83,45 @@ def open(self): import django # pylint: disable=import-outside-toplevel django.setup() - from django.apps import ( # noqa pylint: disable=import-outside-toplevel,unused-import - apps, - ) + # pylint: disable-next=import-outside-toplevel,unused-import + from django.apps import apps # noqa: F401 - # flake8: noqa=F401, F403 except ImproperlyConfigured: # this means that Django wasn't able to configure itself using some defaults # provided (likely in a DJANGO_SETTINGS_MODULE environment variable) # so see if the user has specified a pylint option - if self.config.django_settings_module is None: + if hasattr(self, "linter"): + django_settings_module = self.linter.config.django_settings_module + else: + # TODO: remove this no-member ignore : this is to avoid the missing `config` for pylint 3+, + # and can be removed once pylint 2 + # pylint: disable=no-member + django_settings_module = self.linter.config.django_settings_module + + if django_settings_module is None: # we will warn the user that they haven't actually configured Django themselves self._raise_warning = True # but use django defaults then... - from django.conf import ( # pylint: disable=import-outside-toplevel - settings, - ) + from django.conf import settings # pylint: disable=import-outside-toplevel settings.configure() django.setup() else: # see if we can load the provided settings module try: - from django.conf import ( # pylint: disable=import-outside-toplevel - Settings, - settings, - ) + from django.conf import Settings, settings # pylint: disable=import-outside-toplevel - settings.configure(Settings(self.config.django_settings_module)) + settings.configure(Settings(django_settings_module)) django.setup() except ImportError: # we could not find the provided settings module... # at least here it is a fatal error so we can just raise this immediately self.add_message( "django-settings-module-not-found", - args=self.config.django_settings_module, + args=self.linter.config.django_settings_module, ) # however we'll trundle on with basic settings - from django.conf import ( # pylint: disable=import-outside-toplevel - settings, - ) + from django.conf import settings # pylint: disable=import-outside-toplevel settings.configure() django.setup() diff --git a/pylint_django/checkers/forms.py b/pylint_django/checkers/forms.py index 8614b739..dd490de0 100644 --- a/pylint_django/checkers/forms.py +++ b/pylint_django/checkers/forms.py @@ -1,10 +1,10 @@ """Models.""" + from astroid.nodes import Assign, AssignName, ClassDef from pylint.checkers import BaseChecker -from pylint.checkers.utils import check_messages -from pylint.interfaces import IAstroidChecker from pylint_django.__pkginfo__ import BASE_ID +from pylint_django.compat import check_messages from pylint_django.utils import node_is_subclass @@ -18,8 +18,6 @@ def _get_child_meta(node): class FormChecker(BaseChecker): """Django model checker.""" - __implements__ = IAstroidChecker - name = "django-form-checker" msgs = { f"W{BASE_ID}04": ( diff --git a/pylint_django/checkers/json_response.py b/pylint_django/checkers/json_response.py index 392e6092..904fd71a 100644 --- a/pylint_django/checkers/json_response.py +++ b/pylint_django/checkers/json_response.py @@ -1,16 +1,16 @@ # Copyright (c) 2018 Alexander Todorov # Licensed under the GPL 2.0: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint-django/blob/master/LICENSE +# For details: https://github.com/pylint-dev/pylint-django/blob/master/LICENSE """ Various suggestions about JSON http responses """ import astroid -from pylint import checkers, interfaces -from pylint.checkers import utils +from pylint import checkers from pylint_django.__pkginfo__ import BASE_ID +from pylint_django.compat import check_messages class JsonResponseChecker(checkers.BaseChecker): @@ -19,8 +19,6 @@ class JsonResponseChecker(checkers.BaseChecker): JSON data! """ - __implements__ = (interfaces.IAstroidChecker,) - # configuration section name name = "json-response-checker" msgs = { @@ -43,7 +41,7 @@ class JsonResponseChecker(checkers.BaseChecker): ), } - @utils.check_messages( + @check_messages( "http-response-with-json-dumps", "http-response-with-content-type-json", "redundant-content-type-for-json-response", diff --git a/pylint_django/checkers/migrations.py b/pylint_django/checkers/migrations.py index c8b9d075..c811a6aa 100644 --- a/pylint_django/checkers/migrations.py +++ b/pylint_django/checkers/migrations.py @@ -2,19 +2,19 @@ # Copyright (c) 2020 Bryan Mutai # Licensed under the GPL 2.0: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html -# For details: https://github.com/PyCQA/pylint-django/blob/master/LICENSE +# For details: https://github.com/pylint-dev/pylint-django/blob/master/LICENSE """ Various suggestions around migrations. Disabled by default! Enable with pylint --load-plugins=pylint_django.checkers.migrations """ import astroid -from pylint import checkers, interfaces -from pylint.checkers import utils +from pylint import checkers from pylint_plugin_utils import suppress_message from pylint_django import compat from pylint_django.__pkginfo__ import BASE_ID +from pylint_django.compat import check_messages from pylint_django.utils import is_migrations_module @@ -51,8 +51,6 @@ class NewDbFieldWithDefaultChecker(checkers.BaseChecker): desired default values. """ - __implements__ = (interfaces.IAstroidChecker,) - # configuration section name name = "new-db-field-with-default" msgs = { @@ -86,7 +84,7 @@ def visit_call(self, node): if node not in self._possible_offences[module]: self._possible_offences[module].append(node) - @utils.check_messages("new-db-field-with-default") + @check_messages("new-db-field-with-default") def close(self): def _path(node): return node.path @@ -113,19 +111,17 @@ def _path(node): class MissingBackwardsMigrationChecker(checkers.BaseChecker): - __implements__ = (interfaces.IAstroidChecker,) - name = "missing-backwards-migration-callable" msgs = { f"W{BASE_ID}97": ( "Always include backwards migration callable", "missing-backwards-migration-callable", - "Always include a backwards/reverse callable counterpart so that the migration is not irreversable.", + "Always include a backwards/reverse callable counterpart so that the migration is not irreversible.", ) } - @utils.check_messages("missing-backwards-migration-callable") + @check_messages("missing-backwards-migration-callable") def visit_call(self, node): try: module = node.frame().parent diff --git a/pylint_django/checkers/models.py b/pylint_django/checkers/models.py index 032b6f48..7ff130ad 100644 --- a/pylint_django/checkers/models.py +++ b/pylint_django/checkers/models.py @@ -1,11 +1,11 @@ """Models.""" + from astroid import Const from astroid.nodes import Assign, AssignName, ClassDef, FunctionDef from pylint.checkers import BaseChecker -from pylint.checkers.utils import check_messages -from pylint.interfaces import IAstroidChecker from pylint_django.__pkginfo__ import BASE_ID +from pylint_django.compat import check_messages from pylint_django.utils import PY3, node_is_subclass MESSAGES = { @@ -75,13 +75,11 @@ def _is_unicode_or_str_in_python_2_compatibility(method): class ModelChecker(BaseChecker): """Django model checker.""" - __implements__ = IAstroidChecker - name = "django-model-checker" msgs = MESSAGES @check_messages("model-missing-unicode") - def visit_classdef(self, node): + def visit_classdef(self, node): # noqa: PLR0911 """Class visitor.""" if not node_is_subclass(node, "django.db.models.base.Model", ".Model"): # we only care about models @@ -126,7 +124,7 @@ def visit_classdef(self, node): return # if the Django compatibility decorator is used then we don't emit a warning - # see https://github.com/PyCQA/pylint-django/issues/10 + # see https://github.com/pylint-dev/pylint-django/issues/10 if _has_python_2_unicode_compatible_decorator(node): return diff --git a/pylint_django/compat.py b/pylint_django/compat.py index 4a50aed3..9eeb128c 100644 --- a/pylint_django/compat.py +++ b/pylint_django/compat.py @@ -1,6 +1,7 @@ # flake8: noqa # pylint: skip-file -# no sane linter can figure out the hackiness in this compatability layer... +# no sane linter can figure out the hackiness in this compatibility layer... +import sys try: from astroid.nodes import AssignName, Attribute, ClassDef, FunctionDef, ImportFrom @@ -20,6 +21,11 @@ except ImportError: from astroid.util import Uninferable +try: + from pylint.checkers.utils import only_required_for_messages as check_messages +except (ImportError, ModuleNotFoundError): + from pylint.checkers.utils import check_messages + import pylint # pylint before version 2.3 does not support load_configuration() hook. @@ -28,3 +34,6 @@ LOAD_CONFIGURATION_SUPPORTED = tuple(pylint.__version__.split(".")) >= ("2", "3") except AttributeError: LOAD_CONFIGURATION_SUPPORTED = pylint.__pkginfo__.numversion >= (2, 3) + +# datetime module is compiled and moved to _pydatetime +COMPILED_DATETIME_CLASSES = sys.version_info >= (3, 12) diff --git a/pylint_django/plugin.py b/pylint_django/plugin.py index d3f45a27..dd4b68e2 100644 --- a/pylint_django/plugin.py +++ b/pylint_django/plugin.py @@ -1,10 +1,7 @@ """Common Django module.""" -from pylint.checkers.base import NameChecker -from pylint_plugin_utils import get_checker # we want to import the transforms to make sure they get added to the astroid manager, # however we don't actually access them directly, so we'll disable the warning -from pylint_django import transforms # noqa, pylint: disable=unused-import from pylint_django import compat from pylint_django.checkers import register_checkers @@ -13,8 +10,8 @@ def load_configuration(linter): """ Amend existing checker config. """ - name_checker = get_checker(linter, NameChecker) - name_checker.config.good_names += ( + linter.config.good_names += ( + "pk", "qs", "urlpatterns", "register", diff --git a/pylint_django/tests/input/external_django_tables2_noerror_meta_class.py b/pylint_django/tests/input/external_django_tables2_noerror_meta_class.py index d1711726..b476a33b 100644 --- a/pylint_django/tests/input/external_django_tables2_noerror_meta_class.py +++ b/pylint_django/tests/input/external_django_tables2_noerror_meta_class.py @@ -1,6 +1,6 @@ # Check that Meta class definitions for django_tables2 classes # don't produce old-style-class warnings, see -# https://github.com/PyCQA/pylint-django/issues/56 +# https://github.com/pylint-dev/pylint-django/issues/56 # pylint: disable=missing-docstring,too-few-public-methods diff --git a/pylint_django/tests/input/external_drf_noerror_serializer.py b/pylint_django/tests/input/external_drf_noerror_serializer.py index 814678da..9620541e 100644 --- a/pylint_django/tests/input/external_drf_noerror_serializer.py +++ b/pylint_django/tests/input/external_drf_noerror_serializer.py @@ -1,7 +1,7 @@ """ Checks that Pylint does not complain about DRF serializers """ -# pylint: disable=C0111,W5101 +# pylint: disable=C0111,W5101,use-symbolic-message-instead from rest_framework import serializers diff --git a/pylint_django/tests/input/external_model_utils_noerror_override_manager.py b/pylint_django/tests/input/external_model_utils_noerror_override_manager.py index 84d6038f..e1482ffb 100644 --- a/pylint_django/tests/input/external_model_utils_noerror_override_manager.py +++ b/pylint_django/tests/input/external_model_utils_noerror_override_manager.py @@ -1,7 +1,7 @@ # Check that when overriding the 'objects' attribite of a model class # with a manager from the django-model-utils package pylint-django # does not report not-an-iterator error, see -# https://github.com/PyCQA/pylint-django/issues/117 +# https://github.com/pylint-dev/pylint-django/issues/117 # pylint: disable=missing-docstring, invalid-name from django.db import models diff --git a/pylint_django/tests/input/external_psycopg3_noerror_postgres_fields.py b/pylint_django/tests/input/external_psycopg3_noerror_postgres_fields.py new file mode 100644 index 00000000..5febb28e --- /dev/null +++ b/pylint_django/tests/input/external_psycopg3_noerror_postgres_fields.py @@ -0,0 +1,16 @@ +""" +Checks that Pylint does not complain Postgres model fields. +""" +# pylint: disable=C0111,W5101 +from __future__ import print_function + +from django.contrib.postgres import fields +from django.db import models + + +class PostgresFieldsModel(models.Model): + period = fields.DateRangeField(null=False, blank=False) + + def rangefield_tests(self): + print(self.period.lower) + print(self.period.upper) diff --git a/pylint_django/tests/input/external_psycopg3_noerror_postgres_fields.rc b/pylint_django/tests/input/external_psycopg3_noerror_postgres_fields.rc new file mode 100644 index 00000000..148979b4 --- /dev/null +++ b/pylint_django/tests/input/external_psycopg3_noerror_postgres_fields.rc @@ -0,0 +1,2 @@ +[testoptions] +requires = psycopg3 diff --git a/pylint_django/tests/input/external_tastypie_noerror_foreign_key.py b/pylint_django/tests/input/external_tastypie_noerror_foreign_key.py index 68413e9a..f83d66f2 100644 --- a/pylint_django/tests/input/external_tastypie_noerror_foreign_key.py +++ b/pylint_django/tests/input/external_tastypie_noerror_foreign_key.py @@ -2,11 +2,11 @@ Checks that Pylint doesn't raise an error when a 'ForeignKey' appears in a non-django class -The real case is described as follow: +The real case is described as follows: The project use tastypie and django. tastypie has a `ForeignKey` field which has the same name as django's `ForeignKey`. -The issue is the lint trys resolving the `ForeignKey` for the +The issue is the lint tries resolving the `ForeignKey` for the tastypie `ForeignKey` which cause import error. """ from tastypie import fields diff --git a/pylint_django/tests/input/func_noerror_duplicate_except_doesnotexist.py b/pylint_django/tests/input/func_noerror_duplicate_except_doesnotexist.py index 8f020014..ed41385a 100644 --- a/pylint_django/tests/input/func_noerror_duplicate_except_doesnotexist.py +++ b/pylint_django/tests/input/func_noerror_duplicate_except_doesnotexist.py @@ -1,7 +1,7 @@ """ Checks that Pylint does not complain about duplicate except blocks catching DoesNotExist exceptions: -https://github.com/PyCQA/pylint-django/issues/81 +https://github.com/pylint-dev/pylint-django/issues/81 """ # pylint: disable=missing-docstring from django.db import models diff --git a/pylint_django/tests/input/func_noerror_foreignkeys.py b/pylint_django/tests/input/func_noerror_foreignkeys.py index e9cb5e81..69448a6b 100644 --- a/pylint_django/tests/input/func_noerror_foreignkeys.py +++ b/pylint_django/tests/input/func_noerror_foreignkeys.py @@ -70,7 +70,7 @@ class UserPreferences(models.Model): """ Used for testing FK which refers to another model by string, not model class, see - https://github.com/PyCQA/pylint-django/issues/35 + https://github.com/pylint-dev/pylint-django/issues/35 """ user = ForeignKey("User", on_delete=models.CASCADE) diff --git a/pylint_django/tests/input/func_noerror_form_fields.py b/pylint_django/tests/input/func_noerror_form_fields.py index d6ffbfef..dada077b 100644 --- a/pylint_django/tests/input/func_noerror_form_fields.py +++ b/pylint_django/tests/input/func_noerror_form_fields.py @@ -12,7 +12,6 @@ class ManyFieldsForm(forms.Form): - booleanfield = forms.BooleanField() charfield = forms.CharField(max_length=40, null=True) datetimefield = forms.DateTimeField(auto_now_add=True) diff --git a/pylint_django/tests/input/func_noerror_forms_py33.py b/pylint_django/tests/input/func_noerror_forms_py33.py index 7a8c2aee..831f161d 100644 --- a/pylint_django/tests/input/func_noerror_forms_py33.py +++ b/pylint_django/tests/input/func_noerror_forms_py33.py @@ -34,7 +34,6 @@ class Meta: class TestFormWidgetAssignment(forms.Form): - multi_field = forms.MultipleChoiceField(choices=[("1", "First"), ("2", "Second")]) class Meta: diff --git a/pylint_django/tests/input/func_noerror_generic_foreign_key.py b/pylint_django/tests/input/func_noerror_generic_foreign_key.py index 8cfe73b3..febcde0e 100644 --- a/pylint_django/tests/input/func_noerror_generic_foreign_key.py +++ b/pylint_django/tests/input/func_noerror_generic_foreign_key.py @@ -1,6 +1,6 @@ """ Checks that Pylint does not complain about GenericForeignKey fields: -https://github.com/PyCQA/pylint-django/issues/230 +https://github.com/pylint-dev/pylint-django/issues/230 """ # pylint: disable=missing-docstring diff --git a/pylint_django/tests/input/func_noerror_model_fields.py b/pylint_django/tests/input/func_noerror_model_fields.py index 3e5ffeb9..c0389ff7 100644 --- a/pylint_django/tests/input/func_noerror_model_fields.py +++ b/pylint_django/tests/input/func_noerror_model_fields.py @@ -12,7 +12,6 @@ class LotsOfFieldsModel(models.Model): - bigintegerfield = models.BigIntegerField() booleanfield = models.BooleanField(default=True) charfield = models.CharField(max_length=40, null=True) diff --git a/pylint_django/tests/input/func_noerror_model_objects.py b/pylint_django/tests/input/func_noerror_model_objects.py index 5cf27245..bb0c72aa 100644 --- a/pylint_django/tests/input/func_noerror_model_objects.py +++ b/pylint_django/tests/input/func_noerror_model_objects.py @@ -1,6 +1,6 @@ # Test that defining `objects` as a regular model manager # doesn't raise issues, see -# https://github.com/PyCQA/pylint-django/issues/144 +# https://github.com/pylint-dev/pylint-django/issues/144 # # pylint: disable=missing-docstring diff --git a/pylint_django/tests/input/func_noerror_model_unicode_lambda.py b/pylint_django/tests/input/func_noerror_model_unicode_lambda.py index da7bf9cb..a1db11b4 100644 --- a/pylint_django/tests/input/func_noerror_model_unicode_lambda.py +++ b/pylint_django/tests/input/func_noerror_model_unicode_lambda.py @@ -1,7 +1,7 @@ """ Ensures that django models without a __unicode__ method are flagged """ -# pylint: disable=missing-docstring,wrong-import-position +# pylint: disable=missing-docstring,wrong-import-position,unnecessary-lambda-assignment from django.db import models diff --git a/pylint_django/tests/input/func_noerror_protected_meta_access.py b/pylint_django/tests/input/func_noerror_protected_meta_access.py index 96e09adc..9c5938ed 100644 --- a/pylint_django/tests/input/func_noerror_protected_meta_access.py +++ b/pylint_django/tests/input/func_noerror_protected_meta_access.py @@ -2,7 +2,7 @@ Tests to make sure that access to _meta on a model does not raise a protected-access warning, as it is part of the public API since Django 1.8 -(see https://github.com/PyCQA/pylint-django/issues/66, +(see https://github.com/pylint-dev/pylint-django/issues/66, and https://docs.djangoproject.com/en/1.9/ref/models/meta/) """ # pylint: disable=missing-docstring diff --git a/pylint_django/tests/input/func_noerror_string_foreignkey.py b/pylint_django/tests/input/func_noerror_string_foreignkey.py index 8f9a730e..6f09158f 100644 --- a/pylint_django/tests/input/func_noerror_string_foreignkey.py +++ b/pylint_django/tests/input/func_noerror_string_foreignkey.py @@ -1,6 +1,6 @@ """ Checks that PyLint correctly handles string foreign keys -https://github.com/PyCQA/pylint-django/issues/243 +https://github.com/pylint-dev/pylint-django/issues/243 """ # pylint: disable=missing-docstring, hard-coded-auth-user from django.db import models diff --git a/pylint_django/tests/input/func_noerror_test_wsgi_request.py b/pylint_django/tests/input/func_noerror_test_wsgi_request.py index 9c9a3337..a5c761d3 100644 --- a/pylint_django/tests/input/func_noerror_test_wsgi_request.py +++ b/pylint_django/tests/input/func_noerror_test_wsgi_request.py @@ -1,6 +1,6 @@ """ Checks that Pylint does not complain about a standard test. See: -https://github.com/PyCQA/pylint-django/issues/78 +https://github.com/pylint-dev/pylint-django/issues/78 """ from django.db import models diff --git a/pylint_django/tests/input/func_noerror_unicode_py2_compatible.py b/pylint_django/tests/input/func_noerror_unicode_py2_compatible.py index dace2270..2cd04d27 100644 --- a/pylint_django/tests/input/func_noerror_unicode_py2_compatible.py +++ b/pylint_django/tests/input/func_noerror_unicode_py2_compatible.py @@ -1,8 +1,8 @@ """ Ensures that no '__unicode__ missing' warning is emitted if the -Django python3/2 compatability decorator is used +Django python3/2 compatibility decorator is used -See https://github.com/PyCQA/pylint-django/issues/10 +See https://github.com/pylint-dev/pylint-django/issues/10 """ from django.db import models diff --git a/pylint_django/tests/input/func_noerror_wsgi.py b/pylint_django/tests/input/func_noerror_wsgi.py index a9c38adf..f2c18d5c 100644 --- a/pylint_django/tests/input/func_noerror_wsgi.py +++ b/pylint_django/tests/input/func_noerror_wsgi.py @@ -3,7 +3,7 @@ Used to verify pylint_django doesn't produce invalid-name for the application variable. See: -https://github.com/PyCQA/pylint-django/issues/77 +https://github.com/pylint-dev/pylint-django/issues/77 """ import os diff --git a/pylint_django/tests/input/migrations/0002_new_column.py b/pylint_django/tests/input/migrations/0002_new_column.py index 0aee5447..6955c0c4 100644 --- a/pylint_django/tests/input/migrations/0002_new_column.py +++ b/pylint_django/tests/input/migrations/0002_new_column.py @@ -4,7 +4,7 @@ default value on a large table leads to DB performance issues. See: -https://github.com/PyCQA/pylint-django/issues/118 and +https://github.com/pylint-dev/pylint-django/issues/118 and https://docs.djangoproject.com/en/2.0/topics/migrations/#postgresql > ... adding columns with default values will cause a full rewrite of @@ -19,7 +19,6 @@ class Migration(migrations.Migration): - dependencies = [ ("input", "0001_noerror_initial"), ] diff --git a/pylint_django/tests/input/migrations/0002_new_column.txt b/pylint_django/tests/input/migrations/0002_new_column.txt index 794ac3e8..79d90bd3 100644 --- a/pylint_django/tests/input/migrations/0002_new_column.txt +++ b/pylint_django/tests/input/migrations/0002_new_column.txt @@ -1 +1 @@ -new-db-field-with-default:29:8:33:9:Migration:pylint_django.tests.input.migrations.0002_new_column AddField with default value:UNDEFINED +new-db-field-with-default:28:8:32:9:Migration:pylint_django.tests.input.migrations.0002_new_column AddField with default value:UNDEFINED diff --git a/pylint_django/tests/input/migrations/0003_without_backwards.py b/pylint_django/tests/input/migrations/0003_without_backwards.py index a61e5511..5ec670e3 100644 --- a/pylint_django/tests/input/migrations/0003_without_backwards.py +++ b/pylint_django/tests/input/migrations/0003_without_backwards.py @@ -7,7 +7,6 @@ def forwards_test(apps, schema_editor): class Migration(migrations.Migration): - operations = [ migrations.RunPython(), # [missing-backwards-migration-callable] migrations.RunPython(forwards_test), # [missing-backwards-migration-callable] diff --git a/pylint_django/tests/input/migrations/0003_without_backwards.txt b/pylint_django/tests/input/migrations/0003_without_backwards.txt index 8b83fc1f..b404ce29 100644 --- a/pylint_django/tests/input/migrations/0003_without_backwards.txt +++ b/pylint_django/tests/input/migrations/0003_without_backwards.txt @@ -1,4 +1,4 @@ -missing-backwards-migration-callable:12:8:12:30:Migration:Always include backwards migration callable:UNDEFINED -missing-backwards-migration-callable:13:8:13:43:Migration:Always include backwards migration callable:UNDEFINED -missing-backwards-migration-callable:14:8:14:48:Migration:Always include backwards migration callable:UNDEFINED -missing-backwards-migration-callable:15:8:15:62:Migration:Always include backwards migration callable:UNDEFINED +missing-backwards-migration-callable:11:8:11:30:Migration:Always include backwards migration callable:UNDEFINED +missing-backwards-migration-callable:12:8:12:43:Migration:Always include backwards migration callable:UNDEFINED +missing-backwards-migration-callable:13:8:13:48:Migration:Always include backwards migration callable:UNDEFINED +missing-backwards-migration-callable:14:8:14:62:Migration:Always include backwards migration callable:UNDEFINED diff --git a/pylint_django/tests/input/migrations/0004_noerror_with_backwards.py b/pylint_django/tests/input/migrations/0004_noerror_with_backwards.py index e385e02e..9460ac55 100644 --- a/pylint_django/tests/input/migrations/0004_noerror_with_backwards.py +++ b/pylint_django/tests/input/migrations/0004_noerror_with_backwards.py @@ -11,7 +11,6 @@ def backwards_test(apps, schema_editor): class Migration(migrations.Migration): - operations = [ migrations.RunPython(forwards_test, backwards_test), migrations.RunPython(forwards_test, reverse_code=backwards_test), diff --git a/pylint_django/tests/input/models/func_noerror_foreign_key_key_cls_unbound_in_same_package.py b/pylint_django/tests/input/models/func_noerror_foreign_key_key_cls_unbound_in_same_package.py index d25da4ad..7a7a55c8 100644 --- a/pylint_django/tests/input/models/func_noerror_foreign_key_key_cls_unbound_in_same_package.py +++ b/pylint_django/tests/input/models/func_noerror_foreign_key_key_cls_unbound_in_same_package.py @@ -1,14 +1,14 @@ """ Checks that Pylint does not crash with ForeignKey string reference pointing to model in module of models package. See -https://github.com/PyCQA/pylint-django/issues/232 +https://github.com/pylint-dev/pylint-django/issues/232 Note: the no-member disable is here b/c pylint-django doesn't know how to load models.author.Author. When pylint-django tries to load models referenced by a single string it assumes they are found in the same module it is inspecting. Hence it can't find the Author class here so it tells us it doesn't have an 'id' attribute. Also see: -https://github.com/PyCQA/pylint-django/issues/232#issuecomment-495242695 +https://github.com/pylint-dev/pylint-django/issues/232#issuecomment-495242695 """ # pylint: disable=missing-docstring, no-member from django.db import models diff --git a/pylint_django/tests/settings.py b/pylint_django/tests/settings.py index 075f4207..394c910b 100644 --- a/pylint_django/tests/settings.py +++ b/pylint_django/tests/settings.py @@ -1,4 +1,5 @@ SECRET_KEY = "fake-key" +USE_TZ = False INSTALLED_APPS = [ "django.contrib.auth", diff --git a/pylint_django/tests/test_django_not_installed.sh b/pylint_django/tests/test_django_not_installed.sh index 1b0d7fa8..57d7061f 100644 --- a/pylint_django/tests/test_django_not_installed.sh +++ b/pylint_django/tests/test_django_not_installed.sh @@ -1,2 +1,2 @@ #!/bin/bash -pylint --rcfile=tox.ini --load-plugins=pylint_django setup.py | grep django-not-configured +pylint --rcfile=tox.ini --load-plugins=pylint_django pylint_django/ | grep django-not-available diff --git a/pylint_django/tests/test_func.py b/pylint_django/tests/test_func.py index b446d5c7..51cfc6fc 100644 --- a/pylint_django/tests/test_func.py +++ b/pylint_django/tests/test_func.py @@ -17,11 +17,11 @@ if "test" not in csv.list_dialects(): - class test_dialect(csv.excel): + class TestDialect(csv.excel): delimiter = ":" lineterminator = "\n" - csv.register_dialect("test", test_dialect) + csv.register_dialect("test", TestDialect) lint_module_test.PYLINTRC = HERE / "testing_pylint.rc" except (ImportError, AttributeError): @@ -52,7 +52,9 @@ class PylintDjangoLintModuleTest(LintModuleTest): def __init__(self, test_file): # if hasattr(test_file, 'option_file') and test_file.option_file is None: - super(PylintDjangoLintModuleTest, self).__init__(test_file) + # pylint: disable=super-with-arguments + # TODO Fix this and the CI (?) + super(PylintDjangoLintModuleTest, self).__init__(test_file) # noqa: UP008 self._linter.load_plugin_modules(["pylint_django"]) self._linter.load_plugin_configuration() @@ -97,9 +99,9 @@ def _file_name(test): @pytest.mark.parametrize("test_file", TESTS, ids=TESTS_NAMES) def test_everything(test_file): # copied from pylint.tests.test_functional.test_functional - LintTest = PylintDjangoLintModuleTest(test_file) - LintTest.setUp() - LintTest._runTest() + lint_test = PylintDjangoLintModuleTest(test_file) + lint_test.setUp() + lint_test.runTest() # NOTE: define tests for the migrations checker! @@ -109,20 +111,17 @@ def test_everything(test_file): @pytest.mark.parametrize("test_file", MIGRATIONS_TESTS, ids=MIGRATIONS_TESTS_NAMES) def test_migrations_plugin(test_file): - LintTest = PylintDjangoMigrationsTest(test_file) - LintTest.setUp() - LintTest._runTest() + lint_test = PylintDjangoMigrationsTest(test_file) + lint_test.setUp() + lint_test.runTest() @pytest.mark.parametrize("test_file", MIGRATIONS_TESTS[:1], ids=MIGRATIONS_TESTS_NAMES[:1]) +@pytest.mark.skip # TODO currently skipped because ArgParser which pylint uses is not pickable. def test_linter_should_be_pickleable_with_pylint_django_plugin_installed(test_file): - LintTest = PylintDjangoMigrationsTest(test_file) - LintTest.setUp() - + lint_test = PylintDjangoMigrationsTest(test_file) + lint_test.setUp() + # pylint: disable=protected-access # LintModuleTest sets reporter to instance of FunctionalTestReporter that is not picklable - LintTest._linter.reporter = None - pickle.dumps(LintTest._linter) - - -if __name__ == "__main__": - sys.exit(pytest.main(sys.argv)) + lint_test._linter.reporter = None + pickle.dumps(lint_test._linter) diff --git a/pylint_django/transforms/__init__.py b/pylint_django/transforms/__init__.py index 11c56715..ac51e241 100644 --- a/pylint_django/transforms/__init__.py +++ b/pylint_django/transforms/__init__.py @@ -2,12 +2,13 @@ These transforms replace the Django types with adapted versions to provide additional typing and method inference to pylint. All of these transforms are considered "global" to pylint-django, in that all checks and improvements -requre them to be loaded. Additional transforms specific to checkers are loaded +require them to be loaded. Additional transforms specific to checkers are loaded by the checker rather than here. For example, the ForeignKeyStringsChecker loads the foreignkey.py transforms itself as it may be disabled independently of the rest of pylint-django """ + import os import re @@ -24,7 +25,7 @@ def fake_module_builder(): Build a fake module to use within transformations. @package_name is a parameter from the outer scope b/c according to the docs this can't receive any parameters. - http://pylint.pycqa.org/projects/astroid/en/latest/extending.html?highlight=MANAGER#module-extender-transforms + https://pylint.readthedocs.io/projects/astroid/en/latest/extending.html#module-extender-transforms """ transforms_dir = os.path.join(os.path.dirname(__file__), "transforms") transformed_name = re.sub(r"\.", "_", package_name) diff --git a/pylint_django/transforms/fields.py b/pylint_django/transforms/fields.py index 17fac843..f46e4131 100644 --- a/pylint_django/transforms/fields.py +++ b/pylint_django/transforms/fields.py @@ -1,7 +1,7 @@ from astroid import MANAGER, AstroidImportError, inference_tip, nodes from astroid.nodes import scoped_nodes -from pylint_django import utils +from pylint_django import compat, utils _STR_FIELDS = ( "CharField", @@ -46,8 +46,7 @@ def is_model_or_form_field(cls): return is_model_field(cls) or is_form_field(cls) -def apply_type_shim(cls, _context=None): # noqa - +def apply_type_shim(cls, _context=None): # pylint: disable=too-many-statements if cls.name in _STR_FIELDS: base_nodes = scoped_nodes.builtin_lookup("str") elif cls.name in _INT_FIELDS: @@ -62,13 +61,25 @@ def apply_type_shim(cls, _context=None): # noqa except AstroidImportError: base_nodes = MANAGER.ast_from_module_name("_pydecimal").lookup("Decimal") elif cls.name in ("SplitDateTimeField", "DateTimeField"): - base_nodes = MANAGER.ast_from_module_name("datetime").lookup("datetime") + if compat.COMPILED_DATETIME_CLASSES: + base_nodes = MANAGER.ast_from_module_name("_pydatetime").lookup("datetime") + else: + base_nodes = MANAGER.ast_from_module_name("datetime").lookup("datetime") elif cls.name == "TimeField": - base_nodes = MANAGER.ast_from_module_name("datetime").lookup("time") + if compat.COMPILED_DATETIME_CLASSES: + base_nodes = MANAGER.ast_from_module_name("_pydatetime").lookup("time") + else: + base_nodes = MANAGER.ast_from_module_name("datetime").lookup("time") elif cls.name == "DateField": - base_nodes = MANAGER.ast_from_module_name("datetime").lookup("date") + if compat.COMPILED_DATETIME_CLASSES: + base_nodes = MANAGER.ast_from_module_name("_pydatetime").lookup("date") + else: + base_nodes = MANAGER.ast_from_module_name("datetime").lookup("date") elif cls.name == "DurationField": - base_nodes = MANAGER.ast_from_module_name("datetime").lookup("timedelta") + if compat.COMPILED_DATETIME_CLASSES: + base_nodes = MANAGER.ast_from_module_name("_pydatetime").lookup("timedelta") + else: + base_nodes = MANAGER.ast_from_module_name("datetime").lookup("timedelta") elif cls.name == "UUIDField": base_nodes = MANAGER.ast_from_module_name("uuid").lookup("UUID") elif cls.name == "ManyToManyField": @@ -80,7 +91,10 @@ def apply_type_shim(cls, _context=None): # noqa elif cls.name in ("HStoreField", "JSONField"): base_nodes = scoped_nodes.builtin_lookup("dict") elif cls.name in _RANGE_FIELDS: - base_nodes = MANAGER.ast_from_module_name("psycopg2._range").lookup("Range") + try: + base_nodes = MANAGER.ast_from_module_name("django.db.backends.postgresql.psycopg_any").lookup("Range") + except AstroidImportError: + base_nodes = MANAGER.ast_from_module_name("psycopg2._range").lookup("Range") else: return iter([cls]) @@ -94,7 +108,7 @@ def apply_type_shim(cls, _context=None): # noqa else: base_nodes = list(base_nodes[1]) - return iter([cls] + base_nodes) + return iter([cls, *base_nodes]) def _valid_base_node(node, context): diff --git a/pylint_django/transforms/foreignkey.py b/pylint_django/transforms/foreignkey.py index fc5b1c6e..f22701f5 100644 --- a/pylint_django/transforms/foreignkey.py +++ b/pylint_django/transforms/foreignkey.py @@ -51,9 +51,7 @@ def _module_name_from_django_model_resolution(model_name, module_name): def infer_key_classes(node, context=None): - from django.core.exceptions import ( # pylint: disable=import-outside-toplevel - ImproperlyConfigured, - ) + from django.core.exceptions import ImproperlyConfigured # pylint: disable=import-outside-toplevel keyword_args = [] if node.keywords: diff --git a/pylint_django/utils.py b/pylint_django/utils.py index 205e3b3f..169e0879 100644 --- a/pylint_django/utils.py +++ b/pylint_django/utils.py @@ -1,4 +1,5 @@ """Utils.""" + import sys import astroid diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..a98e8029 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,115 @@ +[build-system] +build-backend = "poetry.core.masonry.api" + +requires = [ "poetry-core>=1" ] + +[tool.poetry] +name = "pylint-django" +version = "2.6.1" +readme = "README.rst" +description = "A Pylint plugin to help Pylint understand the Django web framework" +repository = "https://github.com/pylint-dev/pylint-django" +authors = [ "Carl Crowder " ] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "Intended Audience :: Developers", + "Operating System :: Unix", + "Topic :: Software Development :: Quality Assurance", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Framework :: Django :: 2.2", + "Framework :: Django :: 3", + "Framework :: Django :: 3.0", + "Framework :: Django :: 3.1", + "Framework :: Django :: 3.2", + "Framework :: Django :: 4", + "Framework :: Django :: 4.0", + "Framework :: Django :: 4.1", + "Framework :: Django :: 4.2", + "Framework :: Django :: 5.0", + "Framework :: Django :: 5.1", + "License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)", +] +keywords = [ "pylint", "django", "plugin" ] +packages = [ + { include = "pylint_django/" }, +] +include = [ "pylint_django/LICENSE" ] +exclude = [ "**/tests/**", "**/testutils.py", "**/tests.py" ] + +[tool.poetry.dependencies] +python = ">=3.9,<4.0" +pylint-plugin-utils = ">=0.8" +pylint = ">=3.0,<4" +Django = { version = ">=2.2", optional = true } + +[tool.poetry.group.dev.dependencies] +tox = ">=4.9" +pytest = "^7.3.1" +ruff = ">=0.6.9" +wheel = "^0.40" +pytest-cov = "^4" +django-tables2 = "^2.6" +factory-boy = "^3.3" +django-tastypie = "^0.14" +pre-commit = ">=1" + +[tool.poetry.extras] +with_django = [ "Django" ] + +[tool.black] +line-length = 120 + +[tool.ruff] +line-length = 120 +lint.select = [ + "B", # bugbear + "E", # pycodestyle + "F", # pyflakes + "FA100", # add future annotations + "I", # isort + "PGH004", # pygrep-hooks - Use specific rule codes when using noqa + "PIE", # flake8-pie + "PLC", # pylint convention + "PLE", # pylint error + "PLR", # pylint refactor + "PLR1714", # Consider merging multiple comparisons + "PLW", # pylint warning + "PYI", # flake8-pyi + "RUF", # ruff + "T100", # flake8-debugger + "UP", # pyupgrade + "W", # pycodestyle +] +lint.ignore = [ + "PLR0912", # Too many branches, worse than C901 + "PLR0915", # Too many statements, worse than C901 + "PLR2004", # Magic value used in comparison, opinionated + "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar` +] + +[tool.isort] +multi_line_output = 3 +include_trailing_comma = true +force_grid_wrap = 0 +use_parentheses = true +line_length = 120 + +[tool.pylint.main] +disable = [ + "missing-docstring", + "too-many-branches", + "too-many-return-statements", + "too-many-ancestors", + "too-few-public-methods", + "fixme", +] +ignore = "tests" +max-line-length = 120 + +[tool.codespell] +ignore-words = [ "custom_dict.txt" ] diff --git a/scripts/build.sh b/scripts/build.sh index 00ba5b91..e711ec5e 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -19,8 +19,7 @@ tox -e readme # then build the packages echo "..... Building PyPI packages" set -e -python setup.py sdist >/dev/null -python setup.py bdist_wheel >/dev/null +poetry build --quiet set +e # then run some sanity tests @@ -40,7 +39,7 @@ echo "..... Trying to verify that all source files are present" # remove pylint_django/*.egg-info/ generated during build find . -type d -name '*.egg-info' | xargs rm -rf -source_files=`find ./pylint_django/ -type f | sed 's|./||'` +source_files=`find ./pylint_django/ -type f ! -path '**/tests/**' | sed 's|./||'` # verify for .tar.gz package package_files=`tar -tvf dist/*.tar.gz` @@ -69,7 +68,7 @@ echo "..... Trying to install the new tarball inside a virtualenv" # note: installs with the optional dependency to verify this is still working virtualenv .venv/test-tarball source .venv/test-tarball/bin/activate -pip install -f dist/ pylint_django[with_django] +pip install -f dist/ pylint_django[with-django] pip freeze | grep Django deactivate rm -rf .venv/ @@ -77,7 +76,6 @@ rm -rf .venv/ echo "..... Trying to install the new wheel inside a virtualenv" virtualenv .venv/test-wheel source .venv/test-wheel/bin/activate -pip install pylint-plugin-utils # because it does not provide a wheel package pip install --only-binary :all: -f dist/ pylint_django deactivate rm -rf .venv/ diff --git a/scripts/test.sh b/scripts/test.sh index ca5be46c..6061b9dc 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -1,2 +1,13 @@ #!/bin/bash -python pylint_django/tests/test_func.py -v "$@" +# TODO: The following code duplicates requirements from pyproject.toml for test dependencies. This is because during +# the CI run, tox builds a package using the pyproject.toml and installs it; this means that it does not install +# dev-dependencies including the ones needed for tests to pass. Frustratingly, the "install extras" feature of +# tox does not appear to work to solve this by putting the test deps into an optional grouping. For now, this +# hardcoded list will have to do in order to fix the CI... see #376 +pip install pytest pytest-cov \ + "django-tables2>=2.6.0" \ + "factory-boy>=3.3.0" \ + "django-tastypie>=0.14.6" \ + "djangorestframework>=3.13.1" + +python -m pytest pylint_django/tests/test_func.py -v "$@" diff --git a/setup.py b/setup.py deleted file mode 100644 index 4434ffd2..00000000 --- a/setup.py +++ /dev/null @@ -1,59 +0,0 @@ -# -*- coding: UTF-8 -*- -""" -Setup module for Pylint plugin for Django. -""" -from setuptools import find_packages, setup - -with open("README.rst", encoding="utf-8") as readme, open("CHANGELOG.rst", encoding="utf-8") as changelog: - LONG_DESCRIPTION = readme.read() + "\n" + changelog.read() - -setup( - name="pylint-django", - url="https://github.com/PyCQA/pylint-django", - author="landscape.io", - author_email="code@landscape.io", - description="A Pylint plugin to help Pylint understand the Django web framework", - long_description=LONG_DESCRIPTION, - version="2.5.3", - packages=find_packages(), - include_package_data=True, - install_requires=[ - "pylint-plugin-utils>=0.7", - "pylint>=2.0,<3", - ], - extras_require={ - "with_django": ["Django"], - "for_tests": [ - "django_tables2", - "factory-boy", - "coverage", - "pytest", - "wheel", - "django-tastypie", - "pylint>=2.13", - ], - }, - license="GPLv2", - classifiers=[ - "Environment :: Console", - "Intended Audience :: Developers", - "Operating System :: Unix", - "Topic :: Software Development :: Quality Assurance", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Framework :: Django :: 1.11", - "Framework :: Django :: 2.0", - "Framework :: Django :: 2.2", - "Framework :: Django :: 3.0", - "Framework :: Django :: 3.1", - "Framework :: Django :: 3.2", - ], - keywords=["pylint", "django", "plugin"], - zip_safe=False, - project_urls={ - "Changelog": "https://github.com/PyCQA/pylint-django/blob/master/CHANGELOG.rst", - }, -) diff --git a/tox.ini b/tox.ini index 8debc021..c27b6b77 100644 --- a/tox.ini +++ b/tox.ini @@ -4,61 +4,51 @@ [tox] envlist = django_not_installed - django_is_installed flake8 pylint readme - py{36}-django{111,20,-master} - py{36,37,38,39}-django{22,30,31,32} - py{38,39}-django40 + py{39}-django{22,30,31,32} + py{39,310,311,312}-django{40,41,42} + py{310,311,312}-django{50,51,-main} requires = pip >=21.0.1 + poetry tox [testenv] commands = django_not_installed: bash pylint_django/tests/test_django_not_installed.sh - django_is_installed: pylint --rcfile=tox.ini --load-plugins=pylint_django --disable=E5110 setup.py - flake8: flake8 - pylint: pylint --rcfile=tox.ini -d missing-docstring,too-many-branches,too-many-return-statements,too-many-ancestors,fixme --ignore=tests pylint_django setup - readme: bash -c "python setup.py -q sdist && twine check dist/*" - py{36}-django{111,20,-main}: coverage run pylint_django/tests/test_func.py -v - py{36,37,38,39}-django{22,30,31,32}: coverage run pylint_django/tests/test_func.py -v + pylint: pylint pylint_django + readme: bash -c "poetry build && twine check dist/*" + py{38,39,310,311,312}-django{22,30,31,32,40,41,42,50}: bash scripts/test.sh --cov=pylint_django clean: find . -type f -name '*.pyc' -delete clean: find . -type d -name __pycache__ -delete clean: rm -rf build/ .cache/ dist/ .eggs/ pylint_django.egg-info/ .tox/ deps = - django_is_installed: Django - flake8: flake8 - pylint: pylint + ruff: ruff + pylint: pylint>3 pylint: Django readme: twine readme: wheel - django111: Django>=1.11,<2.0 - django20: Django>=2.0,<2.1 - django21: Django>=2.1,<2.2 django22: Django>=2.2,<3.0 django30: Django>=3.0,<3.1 django31: Django>=3.1,<3.2 django32: Django>=3.2,<4.0 django40: Django>=4.0,<4.1 + django41: Django>=4.1,<4.2 + django42: Django>=4.2,<4.3 + django50: Django>=5.0,<5.1 + django51: Django>=5.1,<5.2 django-main: Django - django-main: git+https://github.com/pycqa/astroid@main - django-main: git+https://github.com/pycqa/pylint@main + django-main: git+https://github.com/pylint-dev/astroid@main + django-main: git+https://github.com/pylint-dev/pylint@main setenv = PIP_DISABLE_PIP_VERSION_CHECK = 1 PYTHONPATH = . allowlist_externals = django_not_installed: bash readme: bash - py{36}-django{111,20,-main}: coverage - py{36,37,38,39}-django{22,30,31,32,40}: coverage + django{22,30,31,32,40,41,42,50,51,-main}: bash clean: find clean: rm - -[flake8] -max-line-length = 120 - -[FORMAT] -max-line-length=120 pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy