From e01b00578102596c8195c140b8351d523d15645a Mon Sep 17 00:00:00 2001 From: "Ronny V." Date: Tue, 19 Nov 2024 15:26:50 +0100 Subject: [PATCH] v1.0.0 (#1) --- .ambient-package-update/metadata.py | 39 +++ .../templates/snippets/content.tpl | 6 + .../templates/snippets/tagline.tpl | 1 + .editorconfig | 18 ++ .github/workflows/ci.yml | 88 +++++++ .gitignore | 136 ++++++++++ .pre-commit-config.yaml | 56 +++++ .readthedocs.yaml | 31 +++ LICENSE.md | 21 ++ MANIFEST.in | 4 + README.md | 144 ++++++++++- SECURITY.md | 18 ++ django_removals/__init__.py | 3 + django_removals/apps.py | 13 + django_removals/checks/__init__.py | 0 django_removals/checks/settings.py | 120 +++++++++ docs/Makefile | 20 ++ docs/conf.py | 82 ++++++ docs/make.bat | 35 +++ manage.py | 23 ++ pyproject.toml | 233 ++++++++++++++++++ scripts/unix/install_requirements.sh | 3 + scripts/unix/publish_to_pypi.sh | 2 + scripts/windows/install_requirements.ps1 | 3 + scripts/windows/publish_to_pypi.ps1 | 2 + settings.py | 62 +++++ testapp/__init__.py | 0 testapp/urls.py | 7 + tests/__init__.py | 0 tests/checks/__init__.py | 0 tests/checks/test_settings.py | 55 +++++ 31 files changed, 1223 insertions(+), 2 deletions(-) create mode 100644 .ambient-package-update/metadata.py create mode 100644 .ambient-package-update/templates/snippets/content.tpl create mode 100644 .ambient-package-update/templates/snippets/tagline.tpl create mode 100644 .editorconfig create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 .readthedocs.yaml create mode 100644 LICENSE.md create mode 100644 MANIFEST.in create mode 100644 SECURITY.md create mode 100644 django_removals/__init__.py create mode 100644 django_removals/apps.py create mode 100644 django_removals/checks/__init__.py create mode 100644 django_removals/checks/settings.py create mode 100644 docs/Makefile create mode 100644 docs/conf.py create mode 100644 docs/make.bat create mode 100644 manage.py create mode 100644 pyproject.toml create mode 100644 scripts/unix/install_requirements.sh create mode 100644 scripts/unix/publish_to_pypi.sh create mode 100644 scripts/windows/install_requirements.ps1 create mode 100644 scripts/windows/publish_to_pypi.ps1 create mode 100644 settings.py create mode 100644 testapp/__init__.py create mode 100644 testapp/urls.py create mode 100644 tests/__init__.py create mode 100644 tests/checks/__init__.py create mode 100644 tests/checks/test_settings.py diff --git a/.ambient-package-update/metadata.py b/.ambient-package-update/metadata.py new file mode 100644 index 0000000..54b99d0 --- /dev/null +++ b/.ambient-package-update/metadata.py @@ -0,0 +1,39 @@ +from ambient_package_update.metadata.author import PackageAuthor +from ambient_package_update.metadata.constants import ( + DEV_DEPENDENCIES, + LICENSE_MIT, + SUPPORTED_DJANGO_VERSIONS, + SUPPORTED_PYTHON_VERSIONS, +) +from ambient_package_update.metadata.maintainer import PackageMaintainer +from ambient_package_update.metadata.package import PackageMetadata +from ambient_package_update.metadata.readme import ReadmeContent + +METADATA = PackageMetadata( + package_name="django-removals", + github_package_group="ambient-innovation", + authors=[ + PackageAuthor( + name="Ambient Digital", + email="hello@ambient.digital", + ), + ], + maintainer=PackageMaintainer(name="Ambient Digital", url="https://ambient.digital/", email="hello@ambient.digital"), + company="Ambient Innovation: GmbH", + license=LICENSE_MIT, + license_year=2024, + development_status="5 - Production/Stable", + has_migrations=False, + readme_content=ReadmeContent(uses_internationalisation=False), + dependencies=[ + f"Django>={SUPPORTED_DJANGO_VERSIONS[0]}", + ], + supported_django_versions=SUPPORTED_DJANGO_VERSIONS, + supported_python_versions=SUPPORTED_PYTHON_VERSIONS, + optional_dependencies={ + "dev": [ + *DEV_DEPENDENCIES, + ], + }, + ruff_ignore_list=[], +) diff --git a/.ambient-package-update/templates/snippets/content.tpl b/.ambient-package-update/templates/snippets/content.tpl new file mode 100644 index 0000000..acd031b --- /dev/null +++ b/.ambient-package-update/templates/snippets/content.tpl @@ -0,0 +1,6 @@ +## Features + +This package will throw [Django system checks](https://docs.djangoproject.com/en/dev/topics/checks/) +warnings for all known removals from Django v1.0 to today. + +It focuses on Django settings but might also add more checks in the future. diff --git a/.ambient-package-update/templates/snippets/tagline.tpl b/.ambient-package-update/templates/snippets/tagline.tpl new file mode 100644 index 0000000..7750cfe --- /dev/null +++ b/.ambient-package-update/templates/snippets/tagline.tpl @@ -0,0 +1 @@ +Welcome to the **django-removals** - a maintainer's best friend for finding removed features in your Django project diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f0886e3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +# http://editorconfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{py,rst,ini}] +indent_style = space +indent_size = 4 +ij_continuation_indent_size = 8 + +[*.yml] +indent_style = space +indent_size = 2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..5d104c9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,88 @@ +name: Unit tests + +on: + push: + branches: + - master + pull_request: + +jobs: + linting: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install required packages + run: pip install pre-commit + + - name: Run pre-commit hooks + run: pre-commit run --all-files --hook-stage push + + + tests: + name: Python ${{ matrix.python-version }}, django ${{ matrix.django-version }} + runs-on: ubuntu-24.04 + strategy: + matrix: + python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', ] + django-version: ['42', '50', '51', ] + + exclude: + - python-version: '3.9' + django-version: 50 + - python-version: '3.9' + django-version: 51 + + steps: + - uses: actions/checkout@v4 + - name: setup python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install tox + run: pip install tox + - name: Run Tests + env: + TOXENV: django${{ matrix.django-version }} + run: tox + - name: Upload coverage data + uses: actions/upload-artifact@v4 + with: + name: coverage-data-${{ matrix.python-version }}-${{ matrix.django-version }} + path: '${{ github.workspace }}/.coverage.*' + include-hidden-files: true + if-no-files-found: error + + coverage: + name: Coverage + runs-on: ubuntu-24.04 + needs: tests + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install dependencies + run: python -m pip install --upgrade coverage[toml] + + - name: Download data + uses: actions/download-artifact@v4 + with: + path: ${{ github.workspace }} + pattern: coverage-data-* + merge-multiple: true + + - name: Combine coverage and fail if it's <100.0% + run: | + python -m coverage combine + python -m coverage html --skip-covered --skip-empty + python -m coverage report --fail-under=100.0 + echo "## Coverage summary" >> $GITHUB_STEP_SUMMARY + python -m coverage report --format=markdown >> $GITHUB_STEP_SUMMARY diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c733f2e --- /dev/null +++ b/.gitignore @@ -0,0 +1,136 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ + +# Requirements.txt is managed by pip-tools +requirements.txt diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..5ed3d60 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,56 @@ +# you find the full pre-commit-tools docu under: +# https://pre-commit.com/ + +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.7.4 + hooks: + # Run the Ruff linter. + - id: ruff + args: [--fix, --exit-non-zero-on-fix] + # Run the Ruff formatter. + - id: ruff-format + + - repo: https://github.com/adamchainz/blacken-docs + rev: 1.19.1 + hooks: + - id: blacken-docs + additional_dependencies: + - black==24.10.0 + files: '(?:README\.md|docs\/.*\.(?:md|rst))' + + - repo: https://github.com/asottile/pyupgrade + rev: v3.19.0 + hooks: + - id: pyupgrade + args: [ --py39-plus ] + stages: [ pre-push ] + + - repo: https://github.com/adamchainz/django-upgrade + rev: 1.22.1 + hooks: + - id: django-upgrade + args: [--target-version, "4.2"] + stages: [ pre-push ] + + - repo: https://github.com/adamchainz/djade-pre-commit + rev: 1.3.2 + hooks: + - id: djade + args: [--target-version, "4.2"] + exclude: | + (?x)^( + charts/.* + |.*\.py + )$ + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: no-commit-to-branch + args: + [ + "--pattern", + '^^(?!(?:feature|hotfix|bugfix|refactor|maintenance)/[\w\d\-_#]+).*$', + ] + stages: [ pre-commit ] diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..5f1ffc9 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,31 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the OS, Python version and other tools you might need +build: + os: ubuntu-24.04 + tools: + python: "3.12" + +# Build documentation in the "docs/" directory with Sphinx +sphinx: + configuration: docs/conf.py + +# Optionally build your docs in additional formats such as PDF and ePub +# formats: +# - pdf +# - epub + +# Optional but recommended, declare the Python requirements required +# to build your documentation +# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +python: + install: + - method: pip + path: . + extra_requirements: + - dev diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..8c9e8a2 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Ambient Innovation: GmbH + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..b1293f7 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,4 @@ +include README.md +include LICENSE.md +recursive-exclude * *.pyc +recursive-include django-removals *.py *.html *.js *.cfg *.mo *.po diff --git a/README.md b/README.md index 791ddfe..61ad47a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,142 @@ -# django-removals -Package to check for known Django removals and deprecations +[![PyPI release](https://img.shields.io/pypi/v/django-removals.svg)](https://pypi.org/project/django-removals/) +[![Downloads](https://static.pepy.tech/badge/django-removals)](https://pepy.tech/project/django-removals) +[![Coverage](https://img.shields.io/badge/Coverage-100.0%25-success)](https://github.com/ambient-innovation/django-removals/actions?workflow=CI) +[![Linting](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) +[![Coding Style](https://img.shields.io/badge/code%20style-Ruff-000000.svg)](https://github.com/astral-sh/ruff) +[![Documentation Status](https://readthedocs.org/projects/django-removals/badge/?version=latest)](https://django-removals.readthedocs.io/en/latest/?badge=latest) + +Welcome to the **django-removals** - a maintainer's best friend for finding removed features in your Django project + +* [PyPI](https://pypi.org/project/django-removals/) +* [GitHub](https://github.com/ambient-innovation/django-removals) +* [Full documentation](https://django-removals.readthedocs.io/en/latest/index.html) +* Creator & Maintainer: [Ambient Digital](https://ambient.digital/) + + +## Features + +This package will throw [Django system checks](https://docs.djangoproject.com/en/dev/topics/checks/) +warnings for all known removals from Django v1.0 to today. + +It focuses on Django settings but might also add more checks in the future. + +## Installation + +- Install the package via pip: + + `pip install django-removals` + + or via pipenv: + + `pipenv install django-removals` + +- Add module to `INSTALLED_APPS` within the main django `settings.py`: + + ```` + INSTALLED_APPS = ( + ... + 'django_removals', + ) + ```` + + + +## Contribute + +### Setup package for development + +- Create a Python virtualenv and activate it +- Install "pip-tools" with `pip install -U pip-tools` +- Compile the requirements with `pip-compile --extra dev, -o requirements.txt pyproject.toml --resolver=backtracking` +- Sync the dependencies with your virtualenv with `pip-sync` + +### Add functionality + +- Create a new branch for your feature +- Change the dependency in your requirements.txt to a local (editable) one that points to your local file system: + `-e /Users/workspace/django-removals` or via pip `pip install -e /Users/workspace/django-removals` +- Ensure the code passes the tests +- Create a pull request + +### Run tests + +- Run tests + ```` + pytest --ds settings tests + ```` + +- Check coverage + ```` + coverage run -m pytest --ds settings tests + coverage report -m + ```` + +### Git hooks (via pre-commit) + +We use pre-push hooks to ensure that only linted code reaches our remote repository and pipelines aren't triggered in +vain. + +To enable the configured pre-push hooks, you need to [install](https://pre-commit.com/) pre-commit and run once: + + pre-commit install -t pre-push -t pre-commit --install-hooks + +This will permanently install the git hooks for both, frontend and backend, in your local +[`.git/hooks`](./.git/hooks) folder. +The hooks are configured in the [`.pre-commit-config.yaml`](templates/.pre-commit-config.yaml.tpl). + +You can check whether hooks work as intended using the [run](https://pre-commit.com/#pre-commit-run) command: + + pre-commit run [hook-id] [options] + +Example: run single hook + + pre-commit run ruff --all-files --hook-stage push + +Example: run all hooks of pre-push stage + + pre-commit run --all-files --hook-stage push + +### Update documentation + +- To build the documentation, run: `sphinx-build docs/ docs/_build/html/`. +- Open `docs/_build/html/index.html` to see the documentation. + + + +### Publish to ReadTheDocs.io + +- Fetch the latest changes in GitHub mirror and push them +- Trigger new build at ReadTheDocs.io (follow instructions in admin panel at RTD) if the GitHub webhook is not yet set + up. + +### Publish to PyPi + +- Update documentation about new/changed functionality + +- Update the `Changelog` + +- Increment version in main `__init__.py` + +- Create pull request / merge to master + +- This project uses the flit package to publish to PyPI. Thus, publishing should be as easy as running: + ``` + flit publish + ``` + + To publish to TestPyPI use the following to ensure that you have set up your .pypirc as + shown [here](https://flit.readthedocs.io/en/latest/upload.html#using-pypirc) and use the following command: + + ``` + flit publish --repository testpypi + ``` + +### Maintenance + +Please note that this package supports the [ambient-package-update](https://pypi.org/project/ambient-package-update/). +So you don't have to worry about the maintenance of this package. This updater is rendering all important +configuration and setup files. It works similar to well-known updaters like `pyupgrade` or `django-upgrade`. + +To run an update, refer to the [documentation page](https://pypi.org/project/ambient-package-update/) +of the "ambient-package-update". + diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..ca06266 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,18 @@ +# Reporting Security Issues + +The maintainers of this package take security bugs seriously. We appreciate your efforts to responsibly +disclose your findings, and will make every effort to acknowledge your contributions. + +To report a security issue, please use the GitHub Security +Advisory ["Report a Vulnerability"](https://github.com/ambient-innovation/django-removals/security/advisories/new) +tab. + +The maintainers will send a response indicating the next steps in handling your report. After the initial reply to +your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask +for additional information or guidance. + +Report security bugs in third-party modules to the person or team maintaining the module. + +## Source + +This file was inspired by: https://github.com/electron/electron/blob/main/SECURITY.md diff --git a/django_removals/__init__.py b/django_removals/__init__.py new file mode 100644 index 0000000..5f4c7de --- /dev/null +++ b/django_removals/__init__.py @@ -0,0 +1,3 @@ +"""Tool for finding removed features in your Django project""" + +__version__ = "1.0.0" diff --git a/django_removals/apps.py b/django_removals/apps.py new file mode 100644 index 0000000..2c25fee --- /dev/null +++ b/django_removals/apps.py @@ -0,0 +1,13 @@ +from django.apps import AppConfig +from django.core.checks import register +from django.utils.translation import gettext_lazy as _ + +from django_removals.checks.settings import check_removed_settings + + +class DjangoRemovalsConfig(AppConfig): + name = "django_removals" + verbose_name = _("Django Removals") + + def ready(self): + register(check_removed_settings) diff --git a/django_removals/checks/__init__.py b/django_removals/checks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/django_removals/checks/settings.py b/django_removals/checks/settings.py new file mode 100644 index 0000000..b25d1d0 --- /dev/null +++ b/django_removals/checks/settings.py @@ -0,0 +1,120 @@ +import django +from django.conf import settings +from django.core.checks import Warning + +REMOVED_SETTINGS = { + 1.2: { + "DATABASE_ENGINE", + "DATABASE_HOST", + "DATABASE_NAME", + "DATABASE_OPTIONS", + "DATABASE_PASSWORD", + "DATABASE_PORT", + "DATABASE_USER", + "TEST_DATABASE_CHARSET", + "TEST_DATABASE_COLLATION", + "TEST_DATABASE_NAME", + }, + 1.4: { + "TRANSACTIONS_MANAGED", + }, + 1.5: { + "AUTH_PROFILE_MODULE", + }, + 1.7: { + "SOUTH_DATABASE_ADAPTER", + "SOUTH_DATABASE_ADAPTERS", + "SOUTH_AUTO_FREEZE_APP", + "SOUTH_TESTS_MIGRATE", + "SOUTH_LOGGING_ON", + "SOUTH_LOGGING_FILE", + "SOUTH_MIGRATION_MODULES", + "SOUTH_USE_PYC", + "TEST_CREATE", + "TEST_USER_CREATE", + "TEST_PASSWD", + "TEST_DATABASE_ENGINE", + "TEST_DATABASE_HOST", + "TEST_DATABASE_OPTIONS", + "TEST_DATABASE_PASSWORD", + "TEST_DATABASE_PORT", + "TEST_DATABASE_USER", + }, + 1.8: { + "SEND_BROKEN_LINK_EMAILS", + "CACHE_MIDDLEWARE_ANONYMOUS_ONLY", + }, + 1.10: { + "ALLOWED_INCLUDE_ROOTS", + "LOGOUT_URL", + "TEMPLATE_CONTEXT_PROCESSORS", + "TEMPLATE_DEBUG", + "TEMPLATE_DIRS", + "TEMPLATE_LOADERS", + "TEMPLATE_STRING_IF_INVALID", + }, + 2.0: { + "MIDDLEWARE_CLASSES", + }, + 2.1: { + "USE_ETAGS", + "SECURE_BROWSER_XSS_FILTER", + }, + 3.0: { + "DEFAULT_CONTENT_TYPE", + "PASSWORD_RESET_TIMEOUT_DAYS", + }, + 3.1: { + "DEFAULT_FILE_STORAGE", + "FILE_CHARSET", + }, + 4.0: { + "DEFAULT_HASHING_ALGORITHM", + }, + 5.0: { + "USE_L10N", + "USE_DEPRECATED_PYTZ", + "CSRF_COOKIE_MASKED", + }, + 5.1: { + "STATICFILES_STORAGE", + }, +} + + +def check_removed_settings(**kwargs): + """ + This check warns users who still use deprecated settings variables. + """ + + warnings = [] + # Iterate all settings variables... + for setting_name in dir(settings): + # Iterate all known removals... + for django_version, removed_settings in REMOVED_SETTINGS.items(): + # If our installed Django version is older than the upcoming removals, we ignore them... + if django_version > django.VERSION[0] + django.VERSION[1] / 10: + continue + # Check if we have a match... + if setting_name.isupper() and setting_name in removed_settings: + # Convert Django version to string and use it in warning code + django_version_as_numbers = str(django_version).replace(".", "") + warning_id = f"W0{django_version_as_numbers}/{setting_name.lower()}" + + # Make Django version URL friendly to be able to link to the official documentation + django_version_for_url = str(django_version).replace(".", "-") + + # Create system check warning for the given match + warnings.append( + Warning( + f"The {setting_name!r} setting was removed in Django {django_version} and its use is not " + f"recommended.", + hint=f"Please refer to the documentation: " + f"https://docs.djangoproject.com/en/stable/releases/{django_version}/" + f"#features-removed-in-{django_version_for_url}.", + obj=setting_name, + id=f"removals.{warning_id}", + ) + ) + + return warnings diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..88b2a89 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,82 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys + +import django +from django.conf import settings + +sys.path.insert(0, os.path.abspath("..")) # so that we can access the "django-removals" package +settings.configure( + INSTALLED_APPS=[ + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "django_removals", + ], + SECRET_KEY="ASDFjklö123456890", +) +django.setup() + +from django_removals import __version__ # noqa: E402 + +# -- Project information ----------------------------------------------------- + +project = "django-removals" +copyright = "2024, Ambient Innovation: GmbH" # noqa: A001 +author = "Ambient Digital " +version = __version__ +release = __version__ + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named "sphinx.ext.*") or your custom +# ones. +extensions = [ + "sphinx_rtd_theme", + "sphinx.ext.autodoc", + "m2r2", +] + +source_suffix = [".rst", ".md"] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = "sphinx_rtd_theme" +html_theme_options = { + "display_version": False, + "style_external_links": False, +} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] + +# Set master doc file +master_doc = "index" diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..922152e --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..ad96ea7 --- /dev/null +++ b/manage.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" + +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( # noqa: TRY003 + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..1c7f983 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,233 @@ +[build-system] +requires = ["flit_core>=3.4"] +build-backend = "flit_core.buildapi" + +[project] +name = "django-removals" +authors = [ + {'name' = 'Ambient Digital', 'email' = 'hello@ambient.digital'}, +] +readme = "README.md" +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Environment :: Web Environment", + "Framework :: Django", + "Framework :: Django :: 4.2", + "Framework :: Django :: 5.0", + "Framework :: Django :: 5.1", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Utilities", +] +dynamic = ["version", "description"] +license = {"file" = "LICENSE.md"} +dependencies = [ + 'Django>=4.2', +] + + +[project.optional-dependencies] +dev = [ + 'typer~=0.12', + 'freezegun~=1.5', + 'pytest-django~=4.9', + 'pytest-mock~=3.14', + 'coverage~=7.6', + 'pre-commit~=4.0', + 'ruff~=0.6', + 'sphinx~=7.1', + 'sphinx-rtd-theme~=2.0', + 'm2r2==0.3.3.post2', + 'mistune<2.0.0', + 'flit~=3.9', + 'keyring~=25.4', + 'ambient-package-update', +] + +[tool.flit.module] +name = "django_removals" + +[project.urls] +'Homepage' = 'https://github.com/ambient-innovation/django-removals/' +'Documentation' = 'https://django-removals.readthedocs.io/en/latest/index.html' +'Maintained by' = 'https://ambient.digital/' +'Bugtracker' = 'https://github.com/ambient-innovation/django-removals/issues' +'Changelog' = 'https://django-removals.readthedocs.io/en/latest/features/changelog.html' + +[tool.ruff] +lint.select = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # Pyflakes + "N", # pep8-naming + "I", # isort + "B", # flake8-bugbear + "A", # flake8-builtins + "DTZ", # flake8-datetimez + "DJ", # flake8-django + "TD", # flake8-to-do + "RUF", # Ruff-specific rules + "YTT", # Avoid non-future-prove usages of "sys" + "C4", # Checks for unnecessary conversions + "PIE", # Bunch of useful rules + "INT", # Validates your gettext translation strings + "PERF", # PerfLint + "PGH", # No all-purpose "# noqa" and eval validation + "PL", # PyLint + "LOG", # flake8-logging + "TID", # flake8-tidy-imports + "PLR2004", # Magic numbers + "BLE", # Checks for except clauses that catch all exceptions + "ANN401", # Checks that function arguments are annotated with a more specific type than Any + "TRY", # Clean try/except + "ERA", # Commented out code +] +lint.ignore = [ +] + +# Allow autofix for all enabled rules (when `--fix`) is provided. +lint.fixable = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # Pyflakes + "N", # pep8-naming + "I", # isort + "B", # flake8-bugbear + "A", # flake8-builtins + "DTZ", # flake8-datetimez + "DJ", # flake8-django + "TD", # flake8-to-do + "RUF", # Ruff-specific rules + "YTT", # Avoid non-future-prove usages of "sys" + "C4", # Checks for unnecessary conversions + "PIE", # Bunch of useful rules + "INT", # Validates your gettext translation strings + "PERF", # PerfLint + "PGH", # No all-purpose "# noqa" and eval validation + "PL", # PyLint + "LOG", # flake8-logging + "TID", # flake8-tidy-imports + "PLR2004", # Magic numbers + "BLE", # Checks for except clauses that catch all exceptions + "ANN401", # Checks that function arguments are annotated with a more specific type than Any + "TRY", # Clean try/except + "ERA", # Commented out code +] +lint.unfixable = [] + +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".hg", + ".mypy_cache", + ".nox", + ".pants.d", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "venv", + "*/migrations/*" +] + +# Same as Black. +line-length = 120 + +# Allow unused variables when underscore-prefixed. +lint.dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +# Assume Python 3.12 +target-version = "py312" + +[tool.ruff.format] +# Like Black, use double quotes for strings. +quote-style = "double" + +# Like Black, indent with spaces, rather than tabs. +indent-style = "space" + +# Like Black, respect magic trailing commas. +skip-magic-trailing-comma = false + +# Like Black, automatically detect the appropriate line ending. +line-ending = "auto" + +[tool.tox] +legacy_tox_ini = """ +[testenv] +# Django deprecation overview: https://www.djangoproject.com/download/ +deps = + django42: Django==4.2.* + django50: Django==5.0.* + django51: Django==5.1.* +extras = dev, +commands = + coverage run -m pytest --ds settings tests + +[gh-actions] +python = + 3.9: py39 + 3.10: py310 + 3.11: py311 + 3.12: py312 + 3.13: py313 +""" + +[tool.pytest.ini_options] +python_files = [ + "tests.py", + "test_*.py", + "*_tests.py", +] + +[tool.coverage.run] +branch = true +parallel = true +source = [ + "django_removals", + "tests", +] +omit = [ + "setup.py", + "*_test.py", + "tests.py", + "testapp/*", + "tests/*", +] + +[tool.coverage.report] +precision = 2 +show_missing = true +# Regexes for lines to exclude from consideration +exclude_also = [ + # Don't complain if tests don't hit defensive assertion code: + "raise AssertionError", + "raise NotImplementedError", + # Don't check type hinting imports + "if typing.TYPE_CHECKING:", + "if TYPE_CHECKING:", +] + +[tool.coverage.path] +source = [ + "django_removals", + ".tox/**/site-packages", +] diff --git a/scripts/unix/install_requirements.sh b/scripts/unix/install_requirements.sh new file mode 100644 index 0000000..36adaba --- /dev/null +++ b/scripts/unix/install_requirements.sh @@ -0,0 +1,3 @@ +pip install -U pip-tools +pip-compile --extra dev, -o requirements.txt pyproject.toml --resolver=backtracking +pip-sync diff --git a/scripts/unix/publish_to_pypi.sh b/scripts/unix/publish_to_pypi.sh new file mode 100644 index 0000000..83a3063 --- /dev/null +++ b/scripts/unix/publish_to_pypi.sh @@ -0,0 +1,2 @@ + flit publish --repository testpypi + flit publish diff --git a/scripts/windows/install_requirements.ps1 b/scripts/windows/install_requirements.ps1 new file mode 100644 index 0000000..36adaba --- /dev/null +++ b/scripts/windows/install_requirements.ps1 @@ -0,0 +1,3 @@ +pip install -U pip-tools +pip-compile --extra dev, -o requirements.txt pyproject.toml --resolver=backtracking +pip-sync diff --git a/scripts/windows/publish_to_pypi.ps1 b/scripts/windows/publish_to_pypi.ps1 new file mode 100644 index 0000000..83a3063 --- /dev/null +++ b/scripts/windows/publish_to_pypi.ps1 @@ -0,0 +1,2 @@ + flit publish --repository testpypi + flit publish diff --git a/settings.py b/settings.py new file mode 100644 index 0000000..5083e0d --- /dev/null +++ b/settings.py @@ -0,0 +1,62 @@ +from pathlib import Path + +BASE_PATH = Path(__file__).resolve(strict=True).parent + +INSTALLED_APPS = ( + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "django_removals", + "testapp", +) + +DEBUG = False + +ALLOWED_HOSTS = ["localhost:8000"] + +SECRET_KEY = "ASDFjklö123456890" + +# Routing +ROOT_URLCONF = "testapp.urls" + +DEFAULT_AUTO_FIELD = "django.db.models.AutoField" + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": "db.sqlite", + } +} + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": ["templates"], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + "debug": True, + }, + }, +] + +MIDDLEWARE = ( + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", +) + +USE_TZ = True +TIME_ZONE = "UTC" diff --git a/testapp/__init__.py b/testapp/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/testapp/urls.py b/testapp/urls.py new file mode 100644 index 0000000..b34c35e --- /dev/null +++ b/testapp/urls.py @@ -0,0 +1,7 @@ +from django.contrib import admin +from django.urls import path + +urlpatterns = [ + # django Admin + path("admin/", admin.site.urls), +] diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/checks/__init__.py b/tests/checks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/checks/test_settings.py b/tests/checks/test_settings.py new file mode 100644 index 0000000..4e210e7 --- /dev/null +++ b/tests/checks/test_settings.py @@ -0,0 +1,55 @@ +from unittest import mock + +import django +from django.core import checks +from django.test import SimpleTestCase, override_settings + + +class RemovedSettingsCheckTests(SimpleTestCase): + @override_settings(TRANSACTIONS_MANAGED=True) + def test_check_removed_settings_found_match(self): + all_issues = checks.run_checks(tags=None) + + self.assertGreaterEqual(len(all_issues), 1) + + self.assertIn( + checks.Warning( + "The 'TRANSACTIONS_MANAGED' setting was removed in Django 1.4 and its use is not recommended.", + hint="Please refer to the documentation: https://docs.djangoproject.com/en/stable/releases/" + "1.4/#features-removed-in-1-4.", + obj="TRANSACTIONS_MANAGED", + id="removals.W014/transactions_managed", + ), + all_issues, + ) + + @override_settings(SILENCED_SYSTEM_CHECKS=("removals.W014/transactions_managed",)) + def test_ignoring_specific_warning_works(self): + all_issues = checks.run_checks(tags=None) + + self.assertNotIn( + checks.Warning( + "The 'TRANSACTIONS_MANAGED' setting was removed in Django 1.4 and its use is not recommended.", + hint="Please refer to the documentation: https://docs.djangoproject.com/en/stable/releases/" + "1.4/#features-removed-in-1-4.", + obj="TRANSACTIONS_MANAGED", + id="removals.W014/transactions_managed", + ), + all_issues, + ) + + @override_settings(USE_L10N=True) + @mock.patch.object(django, "VERSION", new=(4, 2, 0)) + def test_dont_check_newer_than_installed_django_versions(self, *args): + all_issues = checks.run_checks(tags=None) + + self.assertNotIn( + checks.Warning( + "The 'USE_L10N' setting was removed in Django 5.0 and its use is not recommended.", + hint="Please refer to the documentation: https://docs.djangoproject.com/en/stable/releases/" + "5.0/#features-removed-in-5-0.", + obj="USE_L10N", + id="removals.W050/use_l10n", + ), + all_issues, + )