From 79182bbb00dd5686b9f47aeba0a75a935692a0f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Clgen=20Sar=C4=B1kavak?= Date: Fri, 8 Nov 2024 17:23:13 +0300 Subject: [PATCH 01/12] Create dependabot config for github-actions (#512) --- .github/dependabot.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..7f11a985 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,9 @@ +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" From 42441415d0a368d4fc6a677d88fd925583bcec11 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Nov 2024 06:45:11 -0800 Subject: [PATCH 02/12] Bump actions/cache from 3 to 4 (#513) Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/python-package.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 1989bee4..f58ac29e 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -24,7 +24,7 @@ jobs: uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ matrix.python-version }}-pip-${{ hashFiles('**/requirements.txt') }} @@ -45,7 +45,7 @@ jobs: steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ matrix.python-version }}-pip-${{ hashFiles('**/requirements.txt') }} @@ -69,7 +69,7 @@ jobs: steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ matrix.python-version }}-pip-${{ hashFiles('**/requirements.txt') }} @@ -90,7 +90,7 @@ jobs: steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ matrix.python-version }}-pip-${{ hashFiles('**/requirements.txt') }} From 36a8ebc789644beb46f1a8c8b3843758153f5698 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Nov 2024 07:12:19 -0800 Subject: [PATCH 03/12] Bump actions/checkout from 3 to 4 (#515) Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/python-package.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index f58ac29e..4be4b283 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -19,7 +19,7 @@ jobs: python-version: [ 3.8, 3.9, '3.10', '3.11' ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: @@ -43,7 +43,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 - uses: actions/cache@v4 with: @@ -67,7 +67,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 - uses: actions/cache@v4 with: @@ -88,7 +88,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 - uses: actions/cache@v4 with: @@ -117,7 +117,7 @@ jobs: needs: [test, lint, i18n] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 - uses: casperdcl/deploy-pypi@v2 with: From 0470ec2929dc15f31633f9eb2fcc0d86451d15ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Nov 2024 07:18:10 -0800 Subject: [PATCH 04/12] Bump actions/setup-python from 4 to 5 (#514) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/python-package.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 4be4b283..070a97f7 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -21,7 +21,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - uses: actions/cache@v4 @@ -44,7 +44,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 - uses: actions/cache@v4 with: path: ~/.cache/pip @@ -68,7 +68,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 - uses: actions/cache@v4 with: path: ~/.cache/pip @@ -89,7 +89,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 - uses: actions/cache@v4 with: path: ~/.cache/pip @@ -118,7 +118,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 - uses: casperdcl/deploy-pypi@v2 with: password: ${{ secrets.PYPI_API_TOKEN }} From a0e9f71814edffbe41488551def43f15c9502e03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Clgen=20Sar=C4=B1kavak?= Date: Sat, 9 Nov 2024 02:43:55 +0300 Subject: [PATCH 05/12] Add Python 3.12 support (#516) Also added missing supported Django version 4.2 to trove classifiers --- .github/workflows/python-package.yml | 2 +- pyproject.toml | 2 ++ tox.ini | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 070a97f7..726214a2 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ 3.8, 3.9, '3.10', '3.11' ] + python-version: [ 3.8, 3.9, '3.10', '3.11', 3.12 ] steps: - uses: actions/checkout@v4 diff --git a/pyproject.toml b/pyproject.toml index 541f237d..dad47045 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,7 @@ classifiers = [ "Framework :: Django :: 3.2", "Framework :: Django :: 4.0", "Framework :: Django :: 4.1", + "Framework :: Django :: 4.2", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", @@ -27,6 +28,7 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Topic :: Software Development :: Libraries :: Python Modules", ] requires-python = ">=3.8" diff --git a/tox.ini b/tox.ini index 4e60d680..ba5eb972 100644 --- a/tox.ini +++ b/tox.ini @@ -3,6 +3,7 @@ envlist = py{38,39,310}-django{32} py{38,39,310}-django{40,41,42} py{311}-django{41,42} + py{312}-django{42} isolated_build = True [gh-actions] @@ -11,6 +12,7 @@ python = 3.9: py39 3.10: py310 3.11: py311 + 3.12: py312 [testenv] allowlist_externals = ./run.sh From 29b36b6f8f65849cf3cadc349618149b3b218cf7 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 9 Nov 2024 16:49:42 +0100 Subject: [PATCH 06/12] Use ruff to lint Python code (#518) --- .flake8 | 8 -- .github/workflows/python-package.yml | 9 +-- .pre-commit-config.yaml | 11 +-- CONTRIBUTING.rst | 4 +- docs/about/contributing.rst | 4 +- docs/conf.py | 3 +- docs/starting/configuring.rst | 2 +- docs/usage/decorators.rst | 2 +- docs/usage/templates.rst | 4 +- docs/usage/views.rst | 2 +- pyproject.toml | 87 +++++++++++++++++++++ requirements.txt | 2 +- requirements/test.txt | 2 +- run.sh | 4 +- test_settings.py | 2 +- waffle/__init__.py | 10 +-- waffle/locale/ru/LC_MESSAGES/django.po | 4 +- waffle/management/commands/waffle_delete.py | 6 +- waffle/management/commands/waffle_flag.py | 32 ++++---- waffle/management/commands/waffle_sample.py | 4 +- waffle/management/commands/waffle_switch.py | 6 +- waffle/templatetags/waffle_tags.py | 9 +-- waffle/tests/test_middleware.py | 4 +- 23 files changed, 147 insertions(+), 74 deletions(-) delete mode 100644 .flake8 diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 5840e7ac..00000000 --- a/.flake8 +++ /dev/null @@ -1,8 +0,0 @@ -[flake8] -# Use a line length of 120 instead of pep8's default 79 -max-line-length = 120 -# Files not checked: -# - migrations: most of these are autogenerated and don't need a check -# - docs: contains autogenerated code that doesn't need a check -exclude = */migrations/*,docs -ignore = E731 diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 726214a2..91a2d18b 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -15,8 +15,9 @@ jobs: test: runs-on: ubuntu-latest strategy: + fail-fast: false matrix: - python-version: [ 3.8, 3.9, '3.10', '3.11', 3.12 ] + python-version: [ 3.8, 3.9, '3.10', 3.11, 3.12 ] steps: - uses: actions/checkout@v4 @@ -57,11 +58,7 @@ jobs: python -m pip install tox-gh-actions if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Lint - run: | - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + run: ruff check --output-format=github . typecheck: runs-on: ubuntu-latest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c1646cb0..0fb46da5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,10 +1,11 @@ repos: -- repo: https://github.com/pycqa/flake8 - rev: '7.1.1' - hooks: - - id: flake8 - exclude: docs/conf.py|test_settings.py - repo: https://github.com/pre-commit/pre-commit-hooks rev: 'v5.0.0' hooks: - id: end-of-file-fixer + - id: trailing-whitespace + +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.7.3 + hooks: + - id: ruff diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 5469d078..abf6ccce 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -68,7 +68,7 @@ To be mergeable, patches must: - not change existing tests without a *very* good reason, - add tests for new code (bug fixes should include regression tests, new features should have relevant tests), -- not introduce any new flake8_ errors (run ``./run.sh lint``), +- not introduce any new ruff_ errors (run ``./run.sh lint``), - not introduce any new mypy_ errors (run ``./run.sh typecheck``), - include updated source translations (run ``./run.sh makemessages`` and ``./run.sh compilemessages``), - document any new features, and @@ -80,6 +80,6 @@ with it. .. _open a new issue: https://github.com/jazzband/django-waffle/issues/new .. _Fork: https://github.com/jazzband/django-waffle/fork -.. _flake8: https://pypi.python.org/pypi/flake8 +.. _ruff: https://pypi.python.org/pypi/ruff .. _mypy: https://www.mypy-lang.org/ .. _good commit message: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html diff --git a/docs/about/contributing.rst b/docs/about/contributing.rst index 8c27b249..0499b6d4 100644 --- a/docs/about/contributing.rst +++ b/docs/about/contributing.rst @@ -60,7 +60,7 @@ To be mergeable, patches must: - not change existing tests without a *very* good reason, - add tests for new code (bug fixes should include regression tests, new features should have relevant tests), -- not introduce any new flake8_ errors (run ``./run.sh lint``), +- not introduce any new ruff_ errors (run ``./run.sh lint``), - document any new features, and - have a `good commit message`_. @@ -70,5 +70,5 @@ with it. .. _open a new issue: https://github.com/jazzband/django-waffle/issues/new .. _Fork: https://github.com/jazzband/django-waffle/fork -.. _flake8: https://pypi.python.org/pypi/flake8 +.. _ruff: https://pypi.python.org/pypi/ruff .. _good commit message: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html diff --git a/docs/conf.py b/docs/conf.py index 993dfeb7..f65dc044 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -9,7 +9,8 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys, os +import sys +import os # 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 diff --git a/docs/starting/configuring.rst b/docs/starting/configuring.rst index e09341fb..fe48bcb2 100644 --- a/docs/starting/configuring.rst +++ b/docs/starting/configuring.rst @@ -100,7 +100,7 @@ behavior. The value describes the level of wanted warning, possible values are all levels know by pythons default logging, e.g. ``logging.WARNING``. Defaults to ``None``. - + ``WAFFLE_ENABLE_ADMIN_PAGES`` Enables the default admin pages for Waffle models. This is True by default, diff --git a/docs/usage/decorators.rst b/docs/usage/decorators.rst index 47e0eab4..b787826e 100644 --- a/docs/usage/decorators.rst +++ b/docs/usage/decorators.rst @@ -25,7 +25,7 @@ Flags @waffle_flag('flag_name') def myview(request): pass - + @waffle_flag('flag_name', 'url_name_to_redirect_to') def myotherview(request): pass diff --git a/docs/usage/templates.rst b/docs/usage/templates.rst index 903b3724..bc32f060 100644 --- a/docs/usage/templates.rst +++ b/docs/usage/templates.rst @@ -11,7 +11,7 @@ features on the front-end. It includes support for both Django's built-in templates and for Jinja2_. .. warning:: - + Before using samples in templates, see the warning in the :ref:`Sample chapter `. @@ -92,7 +92,7 @@ Switches -------- :: - + {% if waffle.switch('switch_name') %} switch_name is active! {% endif %} diff --git a/docs/usage/views.rst b/docs/usage/views.rst index e82d8ab8..966cebf1 100644 --- a/docs/usage/views.rst +++ b/docs/usage/views.rst @@ -49,5 +49,5 @@ Samples Returns ``True`` if the sample is active, else ``False``. .. warning:: - + See the warning in the :ref:`Sample chapter `. diff --git a/pyproject.toml b/pyproject.toml index dad47045..17b00a17 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,3 +62,90 @@ strict_equality = true [[tool.mypy.overrides]] module = ["django.*"] ignore_missing_imports = true + +[tool.ruff] +line-length = 120 +target-version = "py38" + +[tool.ruff.lint] +select = [ + "AIR", # Airflow + "ASYNC", # flake8-async + "BLE", # flake8-blind-except + "C90", # McCabe cyclomatic complexity + "DJ", # flake8-django + "DTZ", # flake8-datetimez + "E", # pycodestyle errors + "F", # Pyflakes + "FIX", # flake8-fixme + "FLY", # flynt + "G", # flake8-logging-format + "ICN", # flake8-import-conventions + "INP", # flake8-no-pep420 + "INT", # flake8-gettext + "NPY", # NumPy-specific rules + "PD", # pandas-vet + "PIE", # flake8-pie + "PL", # Pylint + "PYI", # flake8-pyi + "RSE", # flake8-raise + "SLOT", # flake8-slots + "T10", # flake8-debugger + "T20", # flake8-print + "TD", # flake8-todos + "TID", # flake8-tidy-imports + "UP", # pyupgrade + "W", # pycodestyle warnings + "YTT", # flake8-2020 + # "A", # flake8-builtins + # "ANN", # flake8-annotations + # "ARG", # flake8-unused-arguments + # "B", # flake8-bugbear + # "C4", # flake8-comprehensions + # "COM", # flake8-commas + # "CPY", # flake8-copyright + # "D", # pydocstyle + # "EM", # flake8-errmsg + # "ERA", # eradicate + # "EXE", # flake8-executable + # "FA", # flake8-future-annotations + # "FBT", # flake8-boolean-trap + # "I", # isort + # "ISC", # flake8-implicit-str-concat + # "N", # pep8-naming + # "PERF", # Perflint + # "PGH", # pygrep-hooks + # "PT", # flake8-pytest-style + # "PTH", # flake8-use-pathlib + # "Q", # flake8-quotes + # "RET", # flake8-return + # "RUF", # Ruff-specific rules + # "S", # flake8-bandit + # "SIM", # flake8-simplify + # "SLF", # flake8-self + # "TCH", # flake8-type-checking + # "TRY", # tryceratops +] +# Files not checked: +# - migrations: most of these are autogenerated and don't need a check +# - docs: contains autogenerated code that doesn't need a check +exclude = [ + "*/migrations/*", + "docs", +] +ignore = ["F401"] + +[tool.ruff.lint.mccabe] +max-complexity = 23 + +[tool.ruff.lint.per-file-ignores] +"docs/conf.py" = ["INP001"] +"test_app/models.py" = ["DJ008"] # FIXME +"waffle/models.py" = ["DJ012", "PYI019"] # FIXME + +[tool.ruff.lint.pylint] +allow-magic-value-types = ["float", "int", "str"] +max-args = 6 # default is 5 +max-branches = 23 # default is 12 +max-returns = 13 # default is 6 +max-statements = 51 # default is 50 diff --git a/requirements.txt b/requirements.txt index fc631674..a7a45320 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,6 @@ Django django-jinja>=2.4.1,<3 transifex-client -flake8 mypy +ruff tox diff --git a/requirements/test.txt b/requirements/test.txt index cc984225..0b86f4f4 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -1,2 +1,2 @@ -flake8 +ruff tox diff --git a/run.sh b/run.sh index 36c86ec1..c2ac5e8d 100755 --- a/run.sh +++ b/run.sh @@ -6,7 +6,7 @@ export DJANGO_SETTINGS_MODULE="test_settings" usage() { echo "USAGE: $0 [command]" echo " test - run the waffle tests" - echo " lint - run flake8" + echo " lint - run ruff" echo " typecheck - run mypy" echo " shell - open the Django shell" echo " makemigrations - create a schema migration" @@ -20,7 +20,7 @@ case "$CMD" in "test" ) DJANGO_SETTINGS_MODULE=test_settings django-admin test waffle $@ ;; "lint" ) - flake8 waffle $@ ;; + ruff check ;; "typecheck" ) mypy waffle $@ ;; "shell" ) diff --git a/test_settings.py b/test_settings.py index 97f15ce9..d19ee4de 100644 --- a/test_settings.py +++ b/test_settings.py @@ -10,7 +10,7 @@ # Make filepaths relative to settings. ROOT = os.path.dirname(os.path.abspath(__file__)) -path = lambda *a: os.path.join(ROOT, *a) +path = lambda *a: os.path.join(ROOT, *a) # noqa: E731 DEBUG = True TEST_RUNNER = 'django.test.runner.DiscoverRunner' diff --git a/waffle/__init__.py b/waffle/__init__.py index 36cbd438..5e979ca3 100755 --- a/waffle/__init__.py +++ b/waffle/__init__.py @@ -42,7 +42,7 @@ def get_waffle_sample_model() -> type[AbstractBaseSample]: def get_waffle_model(setting_name: str) -> ( - type[AbstractBaseFlag] | type[AbstractBaseSwitch] | type[AbstractBaseSample] + type[AbstractBaseFlag | AbstractBaseSwitch | AbstractBaseSample] ): """ Returns the waffle Flag model that is active in this project. @@ -63,12 +63,8 @@ def get_waffle_model(setting_name: str) -> ( try: return django_apps.get_model(flag_model_name) except ValueError: - raise ImproperlyConfigured("WAFFLE_{} must be of the form 'app_label.model_name'".format( - setting_name - )) + raise ImproperlyConfigured(f"WAFFLE_{setting_name} must be of the form 'app_label.model_name'") except LookupError: raise ImproperlyConfigured( - "WAFFLE_{} refers to model '{}' that has not been installed".format( - setting_name, flag_model_name - ) + f"WAFFLE_{setting_name} refers to model '{flag_model_name}' that has not been installed" ) diff --git a/waffle/locale/ru/LC_MESSAGES/django.po b/waffle/locale/ru/LC_MESSAGES/django.po index 64a976aa..50fa3cc0 100644 --- a/waffle/locale/ru/LC_MESSAGES/django.po +++ b/waffle/locale/ru/LC_MESSAGES/django.po @@ -2,10 +2,10 @@ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. -# +# # Translators: # Clinton Blackburn , 2021 -# +# #, fuzzy msgid "" msgstr "" diff --git a/waffle/management/commands/waffle_delete.py b/waffle/management/commands/waffle_delete.py index dc9e77e5..a9c15663 100644 --- a/waffle/management/commands/waffle_delete.py +++ b/waffle/management/commands/waffle_delete.py @@ -38,7 +38,7 @@ def handle(self, *args: Any, **options: Any) -> None: flag_queryset = get_waffle_flag_model().objects.filter(name__in=flags) flag_count = flag_queryset.count() flag_queryset.delete() - self.stdout.write('Deleted %s Flags' % flag_count) + self.stdout.write(f'Deleted {flag_count} Flags') switches = options['switch_names'] if switches: @@ -47,11 +47,11 @@ def handle(self, *args: Any, **options: Any) -> None: ) switch_count = switches_queryset.count() switches_queryset.delete() - self.stdout.write('Deleted %s Switches' % switch_count) + self.stdout.write(f'Deleted {switch_count} Switches') samples = options['sample_names'] if samples: sample_queryset = get_waffle_sample_model().objects.filter(name__in=samples) sample_count = sample_queryset.count() sample_queryset.delete() - self.stdout.write('Deleted %s Samples' % sample_count) + self.stdout.write(f'Deleted {sample_count} Samples') diff --git a/waffle/management/commands/waffle_flag.py b/waffle/management/commands/waffle_flag.py index 052150bc..9b393ac8 100644 --- a/waffle/management/commands/waffle_flag.py +++ b/waffle/management/commands/waffle_flag.py @@ -104,19 +104,19 @@ def handle(self, *args: Any, **options: Any) -> None: if options['list_flags']: self.stdout.write('Flags:') for flag in get_waffle_flag_model().objects.iterator(): - self.stdout.write('NAME: %s' % flag.name) - self.stdout.write('SUPERUSERS: %s' % flag.superusers) - self.stdout.write('EVERYONE: %s' % flag.everyone) - self.stdout.write('AUTHENTICATED: %s' % flag.authenticated) - self.stdout.write('PERCENT: %s' % flag.percent) - self.stdout.write('TESTING: %s' % flag.testing) - self.stdout.write('ROLLOUT: %s' % flag.rollout) - self.stdout.write('STAFF: %s' % flag.staff) - self.stdout.write('GROUPS: %s' % list( - flag.groups.values_list('name', flat=True)) + self.stdout.write(f'NAME: {flag.name}') + self.stdout.write(f'SUPERUSERS: {flag.superusers}') + self.stdout.write(f'EVERYONE: {flag.everyone}') + self.stdout.write(f'AUTHENTICATED: {flag.authenticated}') + self.stdout.write(f'PERCENT: {flag.percent}') + self.stdout.write(f'TESTING: {flag.testing}') + self.stdout.write(f'ROLLOUT: {flag.rollout}') + self.stdout.write(f'STAFF: {flag.staff}') + self.stdout.write('GROUPS: {}'.format(list( + flag.groups.values_list('name', flat=True))) ) - self.stdout.write('USERS: %s' % list( - flag.users.values_list(UserModel.USERNAME_FIELD, flat=True)) + self.stdout.write('USERS: {}'.format(list( + flag.users.values_list(UserModel.USERNAME_FIELD, flat=True))) ) self.stdout.write('') return @@ -129,7 +129,7 @@ def handle(self, *args: Any, **options: Any) -> None: if options['create']: flag, created = get_waffle_flag_model().objects.get_or_create(name=flag_name) if created: - self.stdout.write('Creating flag: %s' % flag_name) + self.stdout.write(f'Creating flag: {flag_name}') else: try: flag = get_waffle_flag_model().objects.get(name=flag_name) @@ -149,7 +149,7 @@ def handle(self, *args: Any, **options: Any) -> None: group_instance = Group.objects.get(name=group) group_hash[group_instance.name] = group_instance.id except Group.DoesNotExist: - raise CommandError('Group %s does not exist' % group) + raise CommandError(f'Group {group} does not exist') # If 'append' was not passed, we clear related groups if not options['append']: flag.groups.clear() @@ -168,11 +168,11 @@ def handle(self, *args: Any, **options: Any) -> None: ) user_hash.add(user_instance) except UserModel.DoesNotExist: - raise CommandError('User %s does not exist' % username) + raise CommandError(f'User {username} does not exist') # If 'append' was not passed, we clear related users if not options['append']: flag.users.clear() - self.stdout.write('Setting user(s): %s' % user_hash) + self.stdout.write(f'Setting user(s): {user_hash}') # for user in user_hash: flag.users.add(*[user.id for user in user_hash]) elif hasattr(flag, option): diff --git a/waffle/management/commands/waffle_sample.py b/waffle/management/commands/waffle_sample.py index 311887b9..e931917b 100644 --- a/waffle/management/commands/waffle_sample.py +++ b/waffle/management/commands/waffle_sample.py @@ -48,7 +48,7 @@ def handle(self, *args: Any, **options: Any) -> None: try: percent = float(percent) if not (0.0 <= percent <= 100.0): - raise ValueError() + raise ValueError except ValueError: raise CommandError('You need to enter a valid percentage value.') @@ -56,7 +56,7 @@ def handle(self, *args: Any, **options: Any) -> None: sample, created = get_waffle_sample_model().objects.get_or_create( name=sample_name, defaults={'percent': 0}) if created: - self.stdout.write('Creating sample: %s' % sample_name) + self.stdout.write(f'Creating sample: {sample_name}') else: try: sample = get_waffle_sample_model().objects.get(name=sample_name) diff --git a/waffle/management/commands/waffle_switch.py b/waffle/management/commands/waffle_switch.py index 016bba4f..e9d26f45 100644 --- a/waffle/management/commands/waffle_switch.py +++ b/waffle/management/commands/waffle_switch.py @@ -8,8 +8,8 @@ def on_off_bool(string: str) -> bool: if string not in ['on', 'off']: - raise ArgumentTypeError("invalid choice: %r (choose from 'on', " - "'off')" % string) + raise ArgumentTypeError(f"invalid choice: {string!r} (choose from 'on', " + "'off')") return string == 'on' @@ -60,7 +60,7 @@ def handle(self, *args: Any, **options: Any) -> None: name=switch_name ) if created: - self.stdout.write('Creating switch: %s' % switch_name) + self.stdout.write(f'Creating switch: {switch_name}') else: try: switch = get_waffle_switch_model().objects.get(name=switch_name) diff --git a/waffle/templatetags/waffle_tags.py b/waffle/templatetags/waffle_tags.py index 38783f1e..62f5bb7a 100644 --- a/waffle/templatetags/waffle_tags.py +++ b/waffle/templatetags/waffle_tags.py @@ -20,7 +20,7 @@ def __init__(self, nodelist_true, nodelist_false, condition, name, self.compiled_name = compiled_name def __repr__(self): - return '' % self.name + return f'' def __iter__(self): yield from self.nodelist_true @@ -41,16 +41,15 @@ def render(self, context): def handle_token(cls, parser, token, kind, condition): bits = token.split_contents() if len(bits) < 2: - raise template.TemplateSyntaxError("%r tag requires an argument" % - bits[0]) + raise template.TemplateSyntaxError(f"{bits[0]!r} tag requires an argument") name = bits[1] compiled_name = parser.compile_filter(name) - nodelist_true = parser.parse(('else', 'end%s' % kind)) + nodelist_true = parser.parse(('else', f'end{kind}')) token = parser.next_token() if token.contents == 'else': - nodelist_false = parser.parse(('end%s' % kind,)) + nodelist_false = parser.parse((f'end{kind}',)) parser.delete_first_token() else: nodelist_false = template.NodeList() diff --git a/waffle/tests/test_middleware.py b/waffle/tests/test_middleware.py index a37cbc07..11af7c91 100644 --- a/waffle/tests/test_middleware.py +++ b/waffle/tests/test_middleware.py @@ -29,7 +29,7 @@ def test_rollout_cookies(): resp = HttpResponse() resp = WaffleMiddleware().process_response(get, resp) for k in get.waffles: - cookie = 'dwf_%s' % k + cookie = f'dwf_{k}' assert cookie in resp.cookies assert str(get.waffles[k][0]) == resp.cookies[cookie].value if get.waffles[k][1]: @@ -44,6 +44,6 @@ def test_testing_cookies(): resp = HttpResponse() resp = WaffleMiddleware().process_response(get, resp) for k in get.waffle_tests: - cookie = 'dwft_%s' % k + cookie = f'dwft_{k}' assert str(get.waffle_tests[k]) == resp.cookies[cookie].value assert not resp.cookies[cookie]['max-age'] From a53e2cd21c207c491f04c7f09cb696dbc2e5da8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Clgen=20Sar=C4=B1kavak?= Date: Sat, 9 Nov 2024 23:29:50 +0300 Subject: [PATCH 07/12] [improvement] Add Django 5.0 support (#519) --- pyproject.toml | 1 + tox.ini | 13 +++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 17b00a17..63bf8091 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,7 @@ classifiers = [ "Framework :: Django :: 4.0", "Framework :: Django :: 4.1", "Framework :: Django :: 4.2", + "Framework :: Django :: 5.0", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", diff --git a/tox.ini b/tox.ini index ba5eb972..94f29cb9 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,9 @@ [tox] envlist = - py{38,39,310}-django{32} - py{38,39,310}-django{40,41,42} - py{311}-django{41,42} - py{312}-django{42} + py{38,39}-django{32,40,41,42} + py{310}-django{32,40,41,42,50} + py{311}-django{41,42,50} + py{312}-django{42,50} isolated_build = True [gh-actions] @@ -21,6 +21,7 @@ deps = django40: Django>=4.0,<4.1 django41: Django>=4.1,<4.2 django42: Django>=4.2,<4.3 + django50: Django>=5.0,<5.1 djangomain: https://github.com/django/django/archive/main.tar.gz -r{toxinidir}/requirements.txt commands = @@ -28,7 +29,7 @@ commands = [testenv:i18n] deps = - Django>=3.2,<4.2 + Django>=3.2,<5.1 -r{toxinidir}/requirements.txt commands = ./run.sh makemessages @@ -37,7 +38,7 @@ commands = [testenv:typecheck] deps = - Django>=3.2,<4.3 + Django>=3.2,<5.1 -r{toxinidir}/requirements.txt commands = ./run.sh typecheck From a44ad5d15329f3cf9ad875561334750231e23015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Clgen=20Sar=C4=B1kavak?= Date: Sun, 10 Nov 2024 18:57:07 +0300 Subject: [PATCH 08/12] [improvement] Add Django 5.1 support (#520) --- pyproject.toml | 1 + tox.ini | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 63bf8091..14068107 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ classifiers = [ "Framework :: Django :: 4.1", "Framework :: Django :: 4.2", "Framework :: Django :: 5.0", + "Framework :: Django :: 5.1", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", diff --git a/tox.ini b/tox.ini index 94f29cb9..5023d2e8 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,9 @@ [tox] envlist = py{38,39}-django{32,40,41,42} - py{310}-django{32,40,41,42,50} - py{311}-django{41,42,50} - py{312}-django{42,50} + py{310}-django{32,40,41,42,50,51} + py{311}-django{41,42,50,51} + py{312}-django{42,50,51} isolated_build = True [gh-actions] @@ -22,6 +22,7 @@ deps = django41: Django>=4.1,<4.2 django42: Django>=4.2,<4.3 django50: Django>=5.0,<5.1 + django51: Django>=5.1,<5.2 djangomain: https://github.com/django/django/archive/main.tar.gz -r{toxinidir}/requirements.txt commands = @@ -29,7 +30,7 @@ commands = [testenv:i18n] deps = - Django>=3.2,<5.1 + Django>=3.2,<5.2 -r{toxinidir}/requirements.txt commands = ./run.sh makemessages @@ -38,7 +39,7 @@ commands = [testenv:typecheck] deps = - Django>=3.2,<5.1 + Django>=3.2,<5.2 -r{toxinidir}/requirements.txt commands = ./run.sh typecheck From 380fa484d006adef72716c823934faeb4fde68d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Clgen=20Sar=C4=B1kavak?= Date: Sun, 10 Nov 2024 21:06:53 +0300 Subject: [PATCH 09/12] [improvement] Add support for Python 3.13 (#523) --- .github/workflows/python-package.yml | 2 +- pyproject.toml | 1 + tox.ini | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 91a2d18b..604823a8 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -17,7 +17,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ 3.8, 3.9, '3.10', 3.11, 3.12 ] + python-version: [ 3.8, 3.9, '3.10', 3.11, 3.12, 3.13 ] steps: - uses: actions/checkout@v4 diff --git a/pyproject.toml b/pyproject.toml index 14068107..0b67f54d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,7 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Topic :: Software Development :: Libraries :: Python Modules", ] requires-python = ">=3.8" diff --git a/tox.ini b/tox.ini index 5023d2e8..48a208c0 100644 --- a/tox.ini +++ b/tox.ini @@ -4,6 +4,7 @@ envlist = py{310}-django{32,40,41,42,50,51} py{311}-django{41,42,50,51} py{312}-django{42,50,51} + py{313}-django{51} isolated_build = True [gh-actions] @@ -13,6 +14,7 @@ python = 3.10: py310 3.11: py311 3.12: py312 + 3.13: py313 [testenv] allowlist_externals = ./run.sh From e003a0871c210f85ee1bd170fead77319e8fef26 Mon Sep 17 00:00:00 2001 From: Graham Knapp <32717635+dancergraham@users.noreply.github.com> Date: Thu, 14 Nov 2024 18:55:28 +0100 Subject: [PATCH 10/12] Create .readthedocs.yaml for documentation configuration (#525) * Create .readthedocs.yaml to fix broken documentation building pipeline * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .readthedocs.yaml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .readthedocs.yaml diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 00000000..a7184468 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,27 @@ +# Read the Docs configuration file for Sphinx projects +# 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-22.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: +# - requirements: docs/requirements.txt From 86a1ea5aafbb412d93935e8aeadf57e516751008 Mon Sep 17 00:00:00 2001 From: Clinton Blackburn Date: Fri, 15 Nov 2024 09:29:56 -0800 Subject: [PATCH 11/12] Preparing to release 4.2.0 (#527) --- CHANGES | 7 +++++++ docs/conf.py | 4 ++-- waffle/__init__.py | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 30c4d5a1..5fced7cc 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,13 @@ Waffle Changelog ================ +v4.2.0 +====== +- Joined Jazzband (https://jazzband.co/) +- Linting improvements and cleanups +- Added support for Django 5.0 and 5.1 +- Added support for Python 3.13 + v4.1.0 ====== - Updated `is_active_for_user` method to account for `everyone` option diff --git a/docs/conf.py b/docs/conf.py index f65dc044..06d55864 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -47,9 +47,9 @@ # built documents. # # The short X.Y version. -version = '4.1' +version = '4.2' # The full version, including alpha/beta/rc tags. -release = '4.1.0' +release = '4.2.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/waffle/__init__.py b/waffle/__init__.py index 5e979ca3..6c706d8a 100755 --- a/waffle/__init__.py +++ b/waffle/__init__.py @@ -11,7 +11,7 @@ if TYPE_CHECKING: from waffle.models import AbstractBaseFlag, AbstractBaseSample, AbstractBaseSwitch -__version__ = '4.1.0' +__version__ = '4.2.0' def flag_is_active(request: HttpRequest, flag_name: str, read_only: bool = False) -> bool | None: From b697b317f1695fd5501437437a9d0f63df8fd2d4 Mon Sep 17 00:00:00 2001 From: Graham Knapp <32717635+dancergraham@users.noreply.github.com> Date: Sat, 16 Nov 2024 21:23:31 +0100 Subject: [PATCH 12/12] Update documentation (#530) * build: add [docs] dependencies group * fix: Gargoyle is no longer supported * fix: minor updates * fix: remove deprecated easy install and jingo references * fix: remove deprecated syncdb ref * fix: remove ancient upgrading instructions * Revert "fix: remove ancient upgrading instructions" This reverts commit c3cafe14a123a2d4fd6820d1fdcf837ad95ca68e. * fix: update broken link * fix: remove unmaintained framework --- docs/about/why-waffle.rst | 15 ++------------- docs/starting/installation.rst | 23 +++-------------------- docs/testing/automated.rst | 6 ++---- docs/testing/user.rst | 2 +- pyproject.toml | 2 ++ 5 files changed, 10 insertions(+), 38 deletions(-) diff --git a/docs/about/why-waffle.rst b/docs/about/why-waffle.rst index c845d812..77ac596c 100644 --- a/docs/about/why-waffle.rst +++ b/docs/about/why-waffle.rst @@ -19,31 +19,20 @@ Waffle :ref:`aims to ` Waffle has an `active community`_ and gets `fairly steady updates`_. -vs Gargoyle -=========== - -The other major, active feature flag tool for Django is Disqus's -Gargoyle_. Both support similar features, though Gargoyle offers more -options for building custom segments in exchange for some more -complexity and requirements. - - Waffle in Production ==================== -Despite its pre-1.0 version number, Waffle has been used in production -for years at places like Mozilla, Yipit and TodaysMeet. +Waffle has been used in production for years at places like Mozilla, Yipit and TodaysMeet. - Mozilla (Support, MDN, Addons, etc) - TodaysMeet - Yipit (If you're using Waffle in production and don't mind being included -here, let me know or add yourself in a pull request!) +here, let us know or add yourself in a pull request!) .. _Feature flags: http://code.flickr.net/2009/12/02/flipping-out/ .. _several options: https://www.djangopackages.com/grids/g/feature-flip/ .. _active community: https://github.com/jazzband/django-waffle/graphs/contributors .. _fairly steady updates: https://github.com/jazzband/django-waffle/pulse/monthly -.. _Gargoyle: https://github.com/disqus/gargoyle diff --git a/docs/starting/installation.rst b/docs/starting/installation.rst index 5a693592..f97ed371 100644 --- a/docs/starting/installation.rst +++ b/docs/starting/installation.rst @@ -11,13 +11,11 @@ met, installing Waffle is a simple process. Getting Waffle ============== -Waffle is `hosted on PyPI`_ and can be installed with ``pip`` or -``easy_install``: +Waffle is `hosted on PyPI`_ and can be installed with ``pip`` .. code-block:: shell $ pip install django-waffle - $ easy_install django-waffle Waffle is also available `on GitHub`_. In general, ``master`` should be stable, but use caution depending on unreleased versions. @@ -80,33 +78,18 @@ With django-jinja_, add the extension to the ``extensions`` list:: # ... ] -With jingo_, add it to the ``JINJA_CONFIG['extensions']`` list:: - - JINJA_CONFIG = { - 'extensions': [ - # ... - 'waffle.jinja.WaffleExtension', - ], - # ... - } - - .. _installation-settings-migrations: Database Schema =============== Waffle includes `Django migrations`_ for creating the correct database -schema. If using Django >= 1.7, simply run the ``migrate`` management -command after adding Waffle to ``INSTALLED_APPS``: +schema. Simply run the ``migrate`` management command after adding Waffle to +``INSTALLED_APPS``: .. code-block:: shell $ django-admin.py migrate -If you're using a version of Django without migrations, you can run -``syncdb`` to create the Waffle tables. - .. _Django migrations: https://docs.djangoproject.com/en/dev/topics/migrations/ .. _django-jinja: https://pypi.python.org/pypi/django-jinja/ -.. _jingo: http://jingo.readthedocs.org/ diff --git a/docs/testing/automated.rst b/docs/testing/automated.rst index 9da4f532..3d5c0de0 100644 --- a/docs/testing/automated.rst +++ b/docs/testing/automated.rst @@ -53,9 +53,8 @@ Tests that run in a separate process, such as Selenium tests, may not have access to the test database or the ability to mock Waffle values. For tests that make HTTP requests to the system-under-test (e.g. with -Selenium_ or PhantomJS_) the ``WAFFLE_OVERRIDE`` :ref:`setting -` makes it possible to control the value of any -*Flag* via the querystring. +Selenium_) the ``WAFFLE_OVERRIDE`` :ref:`setting ` +makes it possible to control the value of any *Flag* via the querystring. .. highlight:: http @@ -72,4 +71,3 @@ or that it is "off":: .. _mock: http://pypi.python.org/pypi/mock/ .. _fudge: http://farmdev.com/projects/fudge/ .. _Selenium: http://www.seleniumhq.org/ -.. _PhantomJS: http://phantomjs.org/ diff --git a/docs/testing/user.rst b/docs/testing/user.rst index 2069c94c..b8290ec7 100644 --- a/docs/testing/user.rst +++ b/docs/testing/user.rst @@ -159,6 +159,6 @@ Wow, good work! You can use similar methods to derive the impact on other factors. -.. _session variables: https://developers.google.com/analytics/devguides/collection/upgrade/reference/gajs-analyticsjs#custom-vars +.. _session variables: https://support.google.com/analytics/answer/9191807?hl=en .. _#80: https://github.com/jazzband/django-waffle/issues/80 .. _StatsD: https://github.com/etsy/statsd diff --git a/pyproject.toml b/pyproject.toml index 0b67f54d..b9057c32 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,6 +36,8 @@ classifiers = [ ] requires-python = ">=3.8" dependencies = ["django>=3.2"] +[project.optional-dependencies] +docs = ["sphinx"] [project.urls] Homepage = "http://github.com/django-waffle/django-waffle"