From 6b8e6f1da97825bea6536708db47c7a30516d1a6 Mon Sep 17 00:00:00 2001 From: Leonardo Cavallucci Date: Tue, 8 Aug 2023 11:10:19 +0200 Subject: [PATCH] Upgrade package tooling, drop Django<3.2, DjangoCMS<3.9 and Python<3.9 --- .bumpversion.cfg | 25 ++++ .github/ISSUE_TEMPLATE/--bug-report.md | 48 +++++++ .github/ISSUE_TEMPLATE/--feature-request.md | 42 ++++++ .github/workflows/codeql.yml | 76 ++++++++++ .github/workflows/lint.yml | 42 ++++++ .github/workflows/logger.yml | 11 ++ .github/workflows/publish.yml | 38 +++++ .github/workflows/test.yml | 60 ++++++++ .pre-commit-config.yaml | 53 +++++++ .pyup.yml | 7 + .travis.yml | 2 - CONTRIBUTING.rst | 114 +++++++++++---- HISTORY.rst | 41 ++++-- MANIFEST.in | 1 + README.rst | 25 ++-- changes/33.feature | 1 + cms_helper.py | 104 +++++++------- djangocms_multisite/__init__.py | 2 +- djangocms_multisite/middleware.py | 17 +-- pyproject.toml | 48 +++++++ requirements-test.txt | 4 +- setup.cfg | 61 +++++--- setup.py | 53 +------ tasks.py | 122 ++++++++++++++++ tests/test_middleware.py | 145 ++++++++++---------- tests/test_utils/urls1.py | 16 +-- tests/test_utils/urls2.py | 16 +-- tests/utils.py | 5 +- tox.ini | 121 +++++++++++++--- 29 files changed, 1009 insertions(+), 291 deletions(-) create mode 100644 .bumpversion.cfg create mode 100644 .github/ISSUE_TEMPLATE/--bug-report.md create mode 100644 .github/ISSUE_TEMPLATE/--feature-request.md create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/logger.yml create mode 100644 .github/workflows/publish.yml create mode 100644 .github/workflows/test.yml create mode 100644 .pre-commit-config.yaml create mode 100644 .pyup.yml create mode 100644 changes/33.feature create mode 100644 pyproject.toml mode change 100755 => 100644 setup.py create mode 100644 tasks.py diff --git a/.bumpversion.cfg b/.bumpversion.cfg new file mode 100644 index 0000000..bcf7feb --- /dev/null +++ b/.bumpversion.cfg @@ -0,0 +1,25 @@ +[bumpversion] +current_version = 0.3.1 +parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\.?)(?P[a-z]*)(?P\d*) +serialize = + {major}.{minor}.{patch}.{release}{relver} + {major}.{minor}.{patch} +commit = True +tag = False +sign_tags = True +tag_name = {new_version} +message = Release {new_version} + +[bumpversion:part:release] +optional_value = gamma +values = + dev + a + b + rc + gamma + +[bumpversion:part:relver] +first_value = 1 + +[bumpversion:file:djangocms_multisite/__init__.py] diff --git a/.github/ISSUE_TEMPLATE/--bug-report.md b/.github/ISSUE_TEMPLATE/--bug-report.md new file mode 100644 index 0000000..19d08d8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/--bug-report.md @@ -0,0 +1,48 @@ +--- +name: "\U0001F41B Bug report" +about: Create a report to help us improve +title: '' +labels: 'type: bug' +assignees: '' + +--- + + + +## Description + + + +## Steps to reproduce + + + +## Versions + + + +## Expected behaviour + + + +## Actual behaviour + + + +## Additional information + + diff --git a/.github/ISSUE_TEMPLATE/--feature-request.md b/.github/ISSUE_TEMPLATE/--feature-request.md new file mode 100644 index 0000000..5b74e5f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/--feature-request.md @@ -0,0 +1,42 @@ +--- +name: "\U0001F389 Feature request" +about: Share your idea, let's discuss it! +title: '' +labels: 'type: feature' +assignees: '' + +--- + + + +## Description + + + +## Use cases + + + +## Proposed solution + + + +## Alternatives + + + +## Additional information + + diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..0310222 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,76 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '37 3 * * 3' + +jobs: + analyze: + name: Analyze + runs-on: 'ubuntu-latest' + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'python' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Use only 'java' to analyze code written in Java, Kotlin or both + # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..701b10e --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,42 @@ +name: Code quality + +on: [push, pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '[skip ci]')" + strategy: + matrix: + python-version: ["3.11.x"] + toxenv: [ruff, isort, black, pypi-description, docs, towncrier] + steps: + - uses: actions/checkout@v3 + with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.ref }} + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Cache pip + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ matrix.toxenv }} + restore-keys: | + ${{ runner.os }}-pip-${{ matrix.toxenv }} + - name: Cache tox + uses: actions/cache@v3 + with: + path: .tox + key: ${{ runner.os }}-lint-${{ matrix.toxenv }}-${{ hashFiles('setup.cfg') }} + restore-keys: | + ${{ runner.os }}-lint-${{ matrix.toxenv }}- + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools tox>4 + - name: Test with tox + if: ${{ matrix.toxenv != 'towncrier' || (!contains(github.event.head_commit.message, '[pre-commit.ci]') && !contains(github.event.pull_request.body, 'pre-commit.ci start')) }} + run: | + tox -e${{ matrix.toxenv }} diff --git a/.github/workflows/logger.yml b/.github/workflows/logger.yml new file mode 100644 index 0000000..7b47896 --- /dev/null +++ b/.github/workflows/logger.yml @@ -0,0 +1,11 @@ +name: Event Logger +on: push + +jobs: + log-github-event-goodies: + name: "LOG Everything on GitHub Event" + runs-on: ubuntu-latest + steps: + - name: Logging + run: | + echo '${{toJSON(github.event)}}' diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..641c0a7 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,38 @@ +name: Upload Python Package + +on: + release: + types: [published,prereleased] + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + - name: Cache pip + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ matrix.toxenv }} + restore-keys: | + ${{ runner.os }}-pip-${{ matrix.toxenv }} + - name: Cache tox + uses: actions/cache@v3 + with: + path: .tox + key: ${{ runner.os }}-tox-release-${{ hashFiles('setup.cfg') }} + restore-keys: | + ${{ runner.os }}-tox-release- + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools tox>4 + - name: Build and publish + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: | + tox -erelease diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..8bc4fe0 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,60 @@ +name: Tox tests + +on: [push, pull_request] + +jobs: + test: + if: "!contains(github.event.head_commit.message, '[skip ci]')" + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.continue-on-error }} + strategy: + matrix: + python-version: ["3.11", "3.10", "3.9"] + django: [42, 41, 32] + cms: [311, 39] + continue-on-error: [true] + exclude: + - django: 41 + cms: 39 + - django: 42 + cms: 39 + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Cache pip + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ matrix.toxenv }} + restore-keys: | + ${{ runner.os }}-pip-${{ matrix.toxenv }} + - name: Cache tox + uses: actions/cache@v3 + with: + path: .tox + key: ${{ runner.os }}-tox-${{ format('{{py{0}-django{1}-cms{2}}}', matrix.python-version, matrix.django, matrix.cms) }}-${{ hashFiles('setup.cfg') }} + restore-keys: | + ${{ runner.os }}-tox-${{ format('{{py{0}-django{1}-cms{2}}}', matrix.python-version, matrix.django, matrix.cms) }}- + - name: Install dependencies + run: | + sudo apt-get install gettext + python -m pip install --upgrade pip setuptools tox>4 + - name: Test with tox + env: + TOX_ENV: ${{ format('py-django{1}-cms{2}', matrix.python-version, matrix.django, matrix.cms) }} + COMMAND: coverage run + COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_SERVICE_NAME: github + run: | + tox -e$TOX_ENV + .tox/$TOX_ENV/bin/coverage xml + .tox/$TOX_ENV/bin/coveralls + - uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + flags: unittests + files: ./coverage.xml + fail_ci_if_error: false diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..2f6c968 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,53 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +exclude: "(.idea|node_modules|.tox)" +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: trailing-whitespace + exclude: ".+cfg" + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + - id: check-builtin-literals + - id: check-executables-have-shebangs + - id: check-merge-conflict + - id: check-toml + - id: fix-encoding-pragma + args: + - --remove + - repo: https://github.com/PyCQA/isort + rev: "5.12.0" + hooks: + - id: isort + - repo: https://github.com/psf/black + rev: 23.7.0 + hooks: + - id: black + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: 'v0.0.281' + hooks: + - id: ruff + - repo: https://github.com/asottile/pyupgrade + rev: v3.10.1 + hooks: + - id: pyupgrade + args: + - --py3-plus + - repo: https://github.com/adamchainz/django-upgrade + rev: "1.14.0" + hooks: + - id: django-upgrade + args: [--target-version, "3.2"] + - repo: local + hooks: + - id: towncrier + name: towncrier + entry: inv towncrier-check + language: system + pass_filenames: false + always_run: true +ci: + skip: + - towncrier diff --git a/.pyup.yml b/.pyup.yml new file mode 100644 index 0000000..2809577 --- /dev/null +++ b/.pyup.yml @@ -0,0 +1,7 @@ +update: all +pin: False +branch: +schedule: "every day" +search: True +branch_prefix: pyup/ +close_prs: True diff --git a/.travis.yml b/.travis.yml index d2cadbb..1667ff6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,5 +62,3 @@ matrix: cache: directories: - $HOME/.cache/pip - - diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index b616dd9..cc6cb46 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -1,6 +1,6 @@ -============ +############ Contributing -============ +############ Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. @@ -8,10 +8,10 @@ little bit helps, and credit will always be given. You can contribute in many ways: Types of Contributions ----------------------- +********************** Report Bugs -~~~~~~~~~~~ +=========== Report bugs at https://github.com/nephila/djangocms-multisite/issues. @@ -22,26 +22,26 @@ If you are reporting a bug, please include: * Detailed steps to reproduce the bug. Fix Bugs -~~~~~~~~ +======== Look through the GitHub issues for bugs. Anything tagged with "bug" is open to whoever wants to implement it. Implement Features -~~~~~~~~~~~~~~~~~~ +================== Look through the GitHub issues for features. Anything tagged with "feature" is open to whoever wants to implement it. Write Documentation -~~~~~~~~~~~~~~~~~~~ +=================== djangocms-multisite could always use more documentation, whether as part of the official djangocms-multisite docs, in docstrings, or even on the web in blog posts, articles, and such. Submit Feedback -~~~~~~~~~~~~~~~ +=============== The best way to send feedback is to file an issue at https://github.com/nephila/djangocms-multisite/issues. @@ -52,21 +52,24 @@ If you are proposing a feature: * Remember that this is a volunteer-driven project, and that contributions are welcome :) +************ Get Started! ------------- +************ -Ready to contribute? Here's how to set up `djangocms-multisite` for local development. +Ready to contribute? Here's how to set up ``djangocms-multisite`` for local development. -1. Fork the `djangocms-multisite` repo on GitHub. +1. Fork the ``djangocms-multisite`` repo on GitHub. 2. Clone your fork locally:: $ git clone git@github.com:your_name_here/djangocms-multisite.git -3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:: +3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper + installed, this is how you set up your fork for local development:: $ mkvirtualenv djangocms-multisite $ cd djangocms-multisite/ - $ python setup.py develop + $ pip install -r requirements-test.txt + $ pip install -e . 4. Create a branch for local development:: @@ -77,11 +80,9 @@ Now you can make your changes locally. 5. When you're done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox:: - $ flake8 djangocms_multisite tests - $ python setup.py test $ tox -To get flake8 and tox, just pip install them into your virtualenv. +To get tox, pip install it into your virtualenv. 6. Commit your changes and push your branch to GitHub:: @@ -91,22 +92,75 @@ To get flake8 and tox, just pip install them into your virtualenv. 7. Submit a pull request through the GitHub website. -Pull Request Guidelines ------------------------ +Development tips +---------------- -Before you submit a pull request, check that it meets these guidelines: +This project allows you to use `pre-commit `_ to ensure an easy compliance +to the project code styles. + +If you want to use it, install it globally (for example with ``pip3 install --user precommit``, +but check `installation instruction `_. +When first cloning the project ensure you install the git hooks by running ``pre-commit install``. + +From now on every commit will be checked against our code style. + +Check also the available tox environments with ``tox -l``: the ones not marked with a python version number are tools +to help you work on the project buy checking / formatting code style, running docs etc. -1. The pull request should include tests. -2. If the pull request adds functionality, the docs should be updated. Put - your new functionality into a function with a docstring, and add the - feature to the list in README.rst. -3. The pull request should work for Python 2.6, 2.7, and 3.3, and for PyPy. Check - https://travis-ci.org/nephila/djangocms-multisite/pull_requests - and make sure that the tests pass for all supported Python versions. +Testing tips +---------------- +You can test your project using any specific combination of python, django and django cms. -Tips ----- +For example ``tox -py37-django30-cms37`` runs the tests on python 3.7, Django 3.0 and django CMS 3.7. -To run a subset of tests:: - $ python -m unittest tests.test_djangocms_multisite +Pull Request Guidelines +======================= + +Before you submit a pull request, check that it meets these guidelines: + +#. Pull request must be named with the following naming scheme: + + ``/(-)-description`` + + See below for available types. + +#. The pull request should include tests. +#. If the pull request adds functionality, the docs should be updated. + Documentation must be added in ``docs`` directory, and must include usage + information for the end user. + In case of public API method, add extended docstrings with full parameters + description and usage example. +#. Add a changes file in ``changes`` directory describing the contribution in + one line. It will be added automatically to the history file upon release. + File must be named as ``.`` with type being: + + * ``.feature``: For new features. + * ``.bugfix``: For bug fixes. + * ``.doc``: For documentation improvement. + * ``.removal``: For deprecation or removal of public API. + * ``.misc``: For general issues. + + Check `towncrier`_ documentation for more details. + +#. The pull request should work for all python / django / django CMS versions + declared in tox.ini. + Check the CI and make sure that the tests pass for all supported versions. + +Release a version +================= + +#. Update authors file +#. Merge ``develop`` on ``master`` branch +#. Bump release via task: ``inv tag-release (major|minor|patch)`` +#. Update changelog via towncrier: ``towncrier --yes`` +#. Commit changelog with ``git commit --amend`` to merge with bumpversion commit +#. Create tag ``git tag `` +#. Push tag to github +#. Publish the release from the tags page +#. If pipeline succeeds, push ``master`` +#. Merge ``master`` back on ``develop`` +#. Bump developement version via task: ``inv tag-dev -l (major|minor|patch)`` +#. Push ``develop`` + +.. _towncrier: https://pypi.org/project/towncrier/#news-fragments diff --git a/HISTORY.rst b/HISTORY.rst index 04ea180..cd2e242 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,27 +1,42 @@ .. :changelog: +******* History -======= +******* -0.3.1 (unreleased) -^^^^^^^^^^^^^^^^^^ +.. towncrier release notes start -* Fix history formatting preventing proper pypi release +0.3.1 (2020-07-06) +================== + +Bugfixes +-------- + +- Fix history formatting preventing proper pypi release 0.3.0 (2020-07-05) -^^^^^^^^^^^^^^^^^^ +================== + +Features +-------- -* Add Django 2.2 / django CMS 3.7+ support -* Drop older Django / django CMS versions +- Add Django 2.2 / django CMS 3.7+ support +- Drop older Django / django CMS versions 0.2.0 (2018-09-25) -^^^^^^^^^^^^^^^^^^ +================== -* Add Django 1.11 / django CMS 3.4+ support -* Drop older Django / django CMS versions -* Improve documentation +Features +-------- + +- Add Django 1.11 / django CMS 3.4+ support +- Drop older Django / django CMS versions +- Improve documentation 0.1.0 (unreleased) -^^^^^^^^^^^^^^^^^^ +================== + +Features +-------- -* First experimental release +- First experimental release diff --git a/MANIFEST.in b/MANIFEST.in index d352a68..e16a729 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,4 +3,5 @@ include CONTRIBUTING.rst include HISTORY.rst include LICENSE include README.rst +include Makefile recursive-include djangocms_multisite *.html *.png *.gif *js *jpg *jpeg *svg *py *css *po *mo diff --git a/README.rst b/README.rst index ff17bae..9fd1dbc 100644 --- a/README.rst +++ b/README.rst @@ -6,8 +6,9 @@ djangocms-multisite |Gitter| |PyPiVersion| |PyVersion| |Status| |TestCoverage| |TestCoveralls| |CodeClimate| |License| +******* Support -======= +******* Supported *Django* versions: @@ -18,19 +19,22 @@ Supported django CMS versions: * django CMS 3.7 +*********** Assumptions -=========== +*********** #. A virtualenv up and running #. DjangoCMS working +************ Installation -============ +************ ``pip install djangocms-multisite`` +***** Usage -===== +***** #. Open your ``settings.py`` file @@ -95,30 +99,31 @@ Usage #. Run ``python manage.py migrate`` to apply the `django-multisite `_ migrations - +******************** Settings explanation -==================== +******************** MULTISITE_CMS_URLS -^^^^^^^^^^^^^^^^^^ +================== Dictionary (or OrderedDict) containing the mapping between the domain (as configured in django ``sites``) and the corresponding urlconf. MULTISITE_CMS_FALLBACK -^^^^^^^^^^^^^^^^^^^^^^ +====================== The default domain to load if any of the above does not match. MULTISITE_CMS_ALIASES -^^^^^^^^^^^^^^^^^^^^^ +===================== Dictionary (or OrderedDict) containing the mapping between the domain (as configured in django ``sites``) and a list of aliases. This is optional if all the aliases are configured as ``django-multisite`` aliases +*************** Troubleshooting -=============== +*************** * Domains in ``MULTISITE_CMS_URLS`` must be the same created in your database (via the interface in ``Home › Sites › Sites``). diff --git a/changes/33.feature b/changes/33.feature new file mode 100644 index 0000000..53f5405 --- /dev/null +++ b/changes/33.feature @@ -0,0 +1 @@ +Upgrade package tooling, drop Django<3.2, DjangoCMS<3.9 and Python<3.9 diff --git a/cms_helper.py b/cms_helper.py index e8ae1cf..47658e3 100755 --- a/cms_helper.py +++ b/cms_helper.py @@ -1,100 +1,106 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- -from __future__ import absolute_import, print_function, unicode_literals from tempfile import mkdtemp -from multisite import SiteID +from multisite import SiteID, checks # NOQA -def gettext(s): return s +def gettext(s): + return s # NOQA HELPER_SETTINGS = dict( - ROOT_URLCONF='tests.test_utils.urls1', + ROOT_URLCONF="tests.test_utils.urls1", INSTALLED_APPS=[ - 'multisite', - 'djangocms_text_ckeditor', + "multisite", + "djangocms_text_ckeditor", ], - LANGUAGE_CODE='en', - LANGUAGES=( - ('en', gettext('English')), - ), + LANGUAGE_CODE="en", + LANGUAGES=(("en", gettext("English")),), CMS_LANGUAGES={ 1: [ { - 'code': 'en', - 'name': gettext('English'), - 'public': True, + "code": "en", + "name": gettext("English"), + "public": True, }, ], 2: [ { - 'code': 'en', - 'name': gettext('English'), - 'public': True, + "code": "en", + "name": gettext("English"), + "public": True, }, ], - 'default': { - 'hide_untranslated': False, + "default": { + "hide_untranslated": False, }, }, MIDDLEWARE_CLASSES=( - 'multisite.middleware.DynamicSiteMiddleware', - 'djangocms_multisite.middleware.CMSMultiSiteMiddleware', + "multisite.middleware.DynamicSiteMiddleware", + "djangocms_multisite.middleware.CMSMultiSiteMiddleware", ), MIGRATION_MODULES={}, USE_TZ=True, - TIME_ZONE='UTC', + TIME_ZONE="UTC", FILE_UPLOAD_TEMP_DIR=mkdtemp(), SITE_ID=SiteID(default=1), MULTISITE_CMS_URLS={ - 'www.example.com': 'tests.test_utils.urls1', - 'www.example2.com': 'tests.test_utils.urls2', + "www.example.com": "tests.test_utils.urls1", + "www.example2.com": "tests.test_utils.urls2", }, MULTISITE_CMS_ALIASES={ - 'www.example.com': ('alias1.example.com', 'alias2.example.com',), - 'www.example2.com': ('alias1.example2.com', 'alias2.example2.com', 'alias3.example2.com:8000'), + "www.example.com": ( + "alias1.example.com", + "alias2.example.com", + ), + "www.example2.com": ("alias1.example2.com", "alias2.example2.com", "alias3.example2.com:8000"), }, - MULTISITE_CMS_FALLBACK='www.example.com', - ALLOWED_HOSTS=['*'], + MULTISITE_CMS_FALLBACK="www.example.com", + ALLOWED_HOSTS=["*"], ) try: import djangocms_blog # NOQA - HELPER_SETTINGS['INSTALLED_APPS'].extend([ - 'filer', - 'easy_thumbnails', - 'aldryn_apphooks_config', - 'cmsplugin_filer_image', - 'parler', - 'taggit', - 'taggit_autosuggest', - 'meta', - 'djangocms_blog', - ]) - HELPER_SETTINGS['THUMBNAIL_PROCESSORS'] = ( - 'easy_thumbnails.processors.colorspace', - 'easy_thumbnails.processors.autocrop', - 'filer.thumbnail_processors.scale_and_crop_with_subject_location', - 'easy_thumbnails.processors.filters', + + HELPER_SETTINGS["INSTALLED_APPS"].extend( + [ + "filer", + "easy_thumbnails", + "aldryn_apphooks_config", + "cmsplugin_filer_image", + "parler", + "taggit", + "taggit_autosuggest", + "meta", + "djangocms_blog", + ] + ) + HELPER_SETTINGS["THUMBNAIL_PROCESSORS"] = ( + "easy_thumbnails.processors.colorspace", + "easy_thumbnails.processors.autocrop", + "filer.thumbnail_processors.scale_and_crop_with_subject_location", + "easy_thumbnails.processors.filters", ) - HELPER_SETTINGS['META_SITE_PROTOCOL'] = 'http' - HELPER_SETTINGS['META_USE_SITES'] = True + HELPER_SETTINGS["META_SITE_PROTOCOL"] = "http" + HELPER_SETTINGS["META_USE_SITES"] = True except ImportError: pass def run(): from djangocms_helper import runner - runner.cms('djangocms_multisite') + + runner.cms("djangocms_multisite") def setup(): import sys + from djangocms_helper import runner - runner.setup('djangocms_multisite', sys.modules[__name__], use_cms=True) + + runner.setup("djangocms_multisite", sys.modules[__name__], use_cms=True) -if __name__ == '__main__': +if __name__ == "__main__": run() diff --git a/djangocms_multisite/__init__.py b/djangocms_multisite/__init__.py index e1424ed..260c070 100644 --- a/djangocms_multisite/__init__.py +++ b/djangocms_multisite/__init__.py @@ -1 +1 @@ -__version__ = '0.3.1' +__version__ = "0.3.1" diff --git a/djangocms_multisite/middleware.py b/djangocms_multisite/middleware.py index eac275b..da93dce 100644 --- a/djangocms_multisite/middleware.py +++ b/djangocms_multisite/middleware.py @@ -8,14 +8,13 @@ class CMSMultiSiteMiddleware(MiddlewareMixin): - @staticmethod def _get_sites(): - return getattr(settings, 'MULTISITE_CMS_URLS', {}) + return getattr(settings, "MULTISITE_CMS_URLS", {}) @staticmethod def _get_aliases(): - return getattr(settings, 'MULTISITE_CMS_ALIASES', {}) + return getattr(settings, "MULTISITE_CMS_ALIASES", {}) @classmethod def _get_domain(cls, request): @@ -25,7 +24,7 @@ def _get_domain(cls, request): sites = cls._get_sites() aliases = cls._get_aliases() parsed = urlparse(request.build_absolute_uri()) - host = parsed.hostname.split(':')[0] + host = parsed.hostname.split(":")[0] if host in sites: return host else: @@ -44,16 +43,12 @@ def _get_urlconf(cls, domain): resulting in setting the default urlconf. """ sites = cls._get_sites() - MULTISITE_CMS_FALLBACK = getattr(settings, 'MULTISITE_CMS_FALLBACK', '') # noqa + MULTISITE_CMS_FALLBACK = getattr(settings, "MULTISITE_CMS_FALLBACK", "") # noqa try: urlconf = sites[domain] except KeyError: urlconf = None - if ( - not urlconf and - MULTISITE_CMS_FALLBACK and - MULTISITE_CMS_FALLBACK in sites.keys() - ): + if not urlconf and MULTISITE_CMS_FALLBACK and MULTISITE_CMS_FALLBACK in sites.keys(): urlconf = sites[MULTISITE_CMS_FALLBACK] return urlconf @@ -68,7 +63,7 @@ def process_request(self, request): reload_urlconf() def process_response(self, request, response): - patch_vary_headers(response, ('Host',)) + patch_vary_headers(response, ("Host",)) # set back to default urlconf set_urlconf(None) return response diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8186771 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,48 @@ +[build-system] +requires = ["setuptools>=40.6.0", "wheel"] +build-backend = "setuptools.build_meta" + +[tool.black] +line-length = 119 +target-version = ["py310"] + +[tool.towncrier] +package = "djangocms_multisite" +directory = "changes" +filename = "HISTORY.rst" +title_format = "{version} ({project_date})" + +[tool.interrogate] +ignore-init-method = true +ignore-init-module = true +ignore-magic = false +ignore-semiprivate = false +ignore-private = false +ignore-module = true +ignore-nested-functions = true +fail-under = 0 +exclude = ["docs", ".tox"] +ignore-regex = ["^get$", "^mock_.*", ".*BaseClass.*"] +verbose = 0 +quiet = false +whitelist-regex = [] +color = true + +[tool.isort] +profile = "black" +combine_as_imports = true +default_section = "THIRDPARTY" +force_grid_wrap = 0 +include_trailing_comma = true +known_first_party = "djangocms_multisite" +line_length = 119 +multi_line_output = 3 +use_parentheses = true + +[tool.ruff] +ignore = [] +line-length = 119 +target-version = "py310" + +[tool.ruff.mccabe] +max-complexity = 10 diff --git a/requirements-test.txt b/requirements-test.txt index 2345d03..7cea711 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -r requirements.txt -coverage -coveralls +coverage>5 +coveralls>2 mock>=1.0.1 nose>=1.3.0 django-nose>=1.2 diff --git a/setup.cfg b/setup.cfg index a189366..9a1cd59 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,19 +1,48 @@ -[flake8] -exclude = *.egg-info,.git,.settings,.tox,build,dist,docs,requirements,tmp,*migrations*,*south_migrations*,tests,data -max-line-length = 119 - [metadata] -license-file = LICENSE +name = djangocms-multisite +version = attr: djangocms_multisite.__version__ +url = https://github.com/nephila/djangocms-multisite +author = Iacopo Spalletti +author_email = i.spalletti@nephila.digital +description = django-multisite support for django CMS +long_description = file: README.rst, HISTORY.rst +long_description_content_type = text/x-rst +license = BSD +license_file = LICENSE +keywords = djangocms-multisite, django +classifiers = + Development Status :: 5 - Production/Stable + Framework :: Django + Intended Audience :: Developers + License :: OSI Approved :: BSD License + Natural Language :: English + Framework :: Django + Framework :: Django :: 3.2 + Framework :: Django :: 4.1 + Framework :: Django :: 4.2 + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 -[wheel] -universal = 1 +[options] +include_package_data = True +install_requires = + django-multisite + django-cms +setup_requires = + setuptools +packages = djangocms_multisite +python_requires = >=3.7 +test_suite = cms_helper.run +zip_safe = False + +[options.package_data] +* = *.txt, *.rst +djangocms_multisite = *.html *.png *.gif *js *jpg *jpeg *svg *py *mo *po -[isort] -line_length = 119 -skip = migrations, .tox -combine_as_imports = true -default_section = THIRDPARTY -include_trailing_comma = true -known_first_party = djangocms_multisite -multi_line_output = 5 -not_skip = __init__.py +[sdist] +formats = zip + +[bdist_wheel] +universal = 1 diff --git a/setup.py b/setup.py old mode 100755 new mode 100644 index cc8b619..b908cbe --- a/setup.py +++ b/setup.py @@ -1,52 +1,3 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -import djangocms_multisite +import setuptools -try: - from setuptools import setup -except ImportError: - from distutils.core import setup - -version = djangocms_multisite.__version__ - - -readme = open('README.rst').read() -history = open('HISTORY.rst').read().replace('.. :changelog:', '') - - -setup( - name='djangocms-multisite', - version=version, - description='django-multisite supporto for django CMS', - long_description=readme + '\n\n' + history, - author='Iacopo Spalletti', - author_email='i.spalletti@nephila.it', - url='https://github.com/nephila/djangocms-multisite', - packages=[ - 'djangocms_multisite', - ], - include_package_data=True, - install_requires=[ - 'django-multisite', - 'django-cms', - ], - license='BSD', - zip_safe=False, - keywords='djangocms-multisite, django', - test_suite='cms_helper.run', - classifiers=[ - 'Development Status :: 3 - Alpha', - 'Framework :: Django', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Natural Language :: English', - 'Framework :: Django', - 'Framework :: Django :: 2.2', - 'Framework :: Django :: 3.0', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - ], -) +setuptools.setup() diff --git a/tasks.py b/tasks.py new file mode 100644 index 0000000..35a5ce9 --- /dev/null +++ b/tasks.py @@ -0,0 +1,122 @@ +import io +import os +import re +import sys +from glob import glob + +from invoke import task + +#: branch prefixes for which some checks are skipped +SPECIAL_BRANCHES = ("master", "develop", "release", "support/4.0.x") + + +@task +def clean(c): + """Remove artifacts and binary files.""" + c.run("python setup.py clean --all") + patterns = ["build", "dist"] + patterns.extend(glob("*.egg*")) + patterns.append("**/*.pyc") + for pattern in patterns: + c.run("rm -rf {}".format(pattern)) + + +@task +def lint(c): + """Run linting tox environments.""" + c.run("tox -epep8,isort,black,pypi-description") + + +@task # NOQA +def format(c): # NOQA + """Run code formatting tasks.""" + c.run("tox -eblacken,isort_format") + + +@task +def towncrier_check(c): # NOQA + """Check towncrier files.""" + output = io.StringIO() + c.run("git branch --contains HEAD", out_stream=output) + skipped_branch_prefix = ["pull/", "develop", "master", "HEAD"] + # cleanup branch names by removing PR-only names in local, remote and disconnected branches to ensure the current + # (i.e. user defined) branch name is used + branches = list( + filter( + lambda x: x and all(not x.startswith(part) for part in skipped_branch_prefix), + ( + branch.replace("origin/", "").replace("remotes/", "").strip("* (") + for branch in output.getvalue().split("\n") + ), + ) + ) + print("Candidate branches", ", ".join(output.getvalue().split("\n"))) + if not branches: + # if no branch name matches, we are in one of the excluded branches above, so we just exit + print("Skip check, branch excluded by configuration") + return + branch = branches[0] + towncrier_file = None + for branch in branches: + if any(branch.startswith(prefix) for prefix in SPECIAL_BRANCHES): + sys.exit(0) + try: + parts = re.search(r"(?P\w+)/\D*(?P\d+)\D*", branch).groups() + towncrier_file = os.path.join("changes", "{1}.{0}".format(*parts)) + if not os.path.exists(towncrier_file) or os.path.getsize(towncrier_file) == 0: + print( + "=========================\n" + "Current tree does not contain the towncrier file {} or file is empty\n" + "please check CONTRIBUTING documentation.\n" + "=========================" + "".format(towncrier_file) + ) + sys.exit(2) + else: + break + except AttributeError: + pass + if not towncrier_file: + print( + "=========================\n" + "Branch {} does not respect the '/(-)-description' format\n" + "=========================\n" + "".format(branch) + ) + sys.exit(1) + + +@task +def test(c): + """Run test in local environment.""" + c.run("python setup.py test") + + +@task +def test_all(c): + """Run all tox environments.""" + c.run("tox") + + +@task +def coverage(c): + """Run test with coverage in local environment.""" + c.run("coverage erase") + c.run("run setup.py test") + c.run("report -m") + + +@task +def tag_release(c, level, new_version=""): + """Tag release version.""" + if new_version: + new_version = f" --new-version {new_version}" + c.run(f"bumpversion --list {level} --no-tag{new_version}") + + +@task +def tag_dev(c, level="patch", new_version=""): + """Tag development version.""" + if new_version: + new_version = f" --new-version {new_version}" + c.run(f"bumpversion --list {level} --message='Bump develop version [ci skip]' --no-tag{new_version}") diff --git a/tests/test_middleware.py b/tests/test_middleware.py index 226b9b9..cca7eee 100644 --- a/tests/test_middleware.py +++ b/tests/test_middleware.py @@ -1,3 +1,5 @@ +from unittest import mock + from app_helper.base_test import BaseTestCase from django.contrib.sites.models import Site from django.http import Http404, HttpResponse @@ -14,87 +16,90 @@ class CMSMultiSiteMiddlewareTest(BaseTestCase): def setUp(self): Site.objects.all().delete() - self.site = Site.objects.create(pk=1, domain='www.example.com') - self.site2 = Site.objects.create(pk=2, domain='www.example2.com') + self.site = Site.objects.create(pk=1, domain="www.example.com") + self.site2 = Site.objects.create(pk=2, domain="www.example2.com") def test_match_domain(self): """Resolve the request domain against the list of configured main and aliases.""" - request = RequestFactory(host='www.example.com').get('/') - self.assertEqual(CMSMultiSiteMiddleware._get_domain(request), 'www.example.com') + request = RequestFactory(host="www.example.com").get("/") + self.assertEqual(CMSMultiSiteMiddleware._get_domain(request), "www.example.com") - request = RequestFactory(host='alias1.example.com').get('/') - self.assertEqual(CMSMultiSiteMiddleware._get_domain(request), 'www.example.com') + request = RequestFactory(host="alias1.example.com").get("/") + self.assertEqual(CMSMultiSiteMiddleware._get_domain(request), "www.example.com") - request = RequestFactory(host='alias3.example.com').get('/') + request = RequestFactory(host="alias3.example.com").get("/") self.assertEqual(CMSMultiSiteMiddleware._get_domain(request), None) - request = RequestFactory(host='blabla.com').get('/') + request = RequestFactory(host="blabla.com").get("/") self.assertEqual(CMSMultiSiteMiddleware._get_domain(request), None) - request = RequestFactory(host='www.example2.com').get('/') - self.assertEqual(CMSMultiSiteMiddleware._get_domain(request), 'www.example2.com') + request = RequestFactory(host="www.example2.com").get("/") + self.assertEqual(CMSMultiSiteMiddleware._get_domain(request), "www.example2.com") - request = RequestFactory(host='alias2.example2.com').get('/') - self.assertEqual(CMSMultiSiteMiddleware._get_domain(request), 'www.example2.com') + request = RequestFactory(host="alias2.example2.com").get("/") + self.assertEqual(CMSMultiSiteMiddleware._get_domain(request), "www.example2.com") # port is always ignored, only domain is considered - request = RequestFactory(host='alias2.example2.com:8000').get('/') - self.assertEqual(CMSMultiSiteMiddleware._get_domain(request), 'www.example2.com') + request = RequestFactory(host="alias2.example2.com:8000").get("/") + self.assertEqual(CMSMultiSiteMiddleware._get_domain(request), "www.example2.com") # don't set port in MULTISITE_CMS_ALIASES, otherwise it will not be matched - request = RequestFactory(host='alias3.example2.com:8000').get('/') + request = RequestFactory(host="alias3.example2.com:8000").get("/") self.assertEqual(CMSMultiSiteMiddleware._get_domain(request), None) def test_match_urlconf(self): """Match main domain return the correct one - Any other domain -including alias- return the default.""" - self.assertEqual(CMSMultiSiteMiddleware._get_urlconf('www.example.com'), 'tests.test_utils.urls1') - self.assertEqual(CMSMultiSiteMiddleware._get_urlconf('www.example2.com'), 'tests.test_utils.urls2') - self.assertEqual(CMSMultiSiteMiddleware._get_urlconf('alias1.example.com'), 'tests.test_utils.urls1') - self.assertEqual(CMSMultiSiteMiddleware._get_urlconf('alias2.example2.com'), 'tests.test_utils.urls1') + self.assertEqual(CMSMultiSiteMiddleware._get_urlconf("www.example.com"), "tests.test_utils.urls1") + self.assertEqual(CMSMultiSiteMiddleware._get_urlconf("www.example2.com"), "tests.test_utils.urls2") + self.assertEqual(CMSMultiSiteMiddleware._get_urlconf("alias1.example.com"), "tests.test_utils.urls1") + self.assertEqual(CMSMultiSiteMiddleware._get_urlconf("alias2.example2.com"), "tests.test_utils.urls1") @override_settings(SITE_ID=1) def test_process_site_1(self): - request = RequestFactory(host='www.example.com').get('/') - CMSMultiSiteMiddleware().process_request(request) - self.assertEqual(get_urlconf(), 'tests.test_utils.urls1') + request = RequestFactory(host="www.example.com").get("/") + get_response = mock.MagicMock() + CMSMultiSiteMiddleware(get_response).process_request(request) + self.assertEqual(get_urlconf(), "tests.test_utils.urls1") - request = RequestFactory(host='alias1.example.com').get('/') - CMSMultiSiteMiddleware().process_request(request) - self.assertEqual(get_urlconf(), 'tests.test_utils.urls1') + request = RequestFactory(host="alias1.example.com").get("/") + CMSMultiSiteMiddleware(get_response).process_request(request) + self.assertEqual(get_urlconf(), "tests.test_utils.urls1") @override_settings(SITE_ID=2) def test_process_site_2(self): - request = RequestFactory(host='www.example2.com').get('/') - CMSMultiSiteMiddleware().process_request(request) - self.assertEqual(get_urlconf(), 'tests.test_utils.urls2') + request = RequestFactory(host="www.example2.com").get("/") + get_response = mock.MagicMock() + CMSMultiSiteMiddleware(get_response).process_request(request) + self.assertEqual(get_urlconf(), "tests.test_utils.urls2") - request = RequestFactory(host='alias2.example2.com').get('/') - CMSMultiSiteMiddleware().process_request(request) - self.assertEqual(get_urlconf(), 'tests.test_utils.urls2') + request = RequestFactory(host="alias2.example2.com").get("/") + CMSMultiSiteMiddleware(get_response).process_request(request) + self.assertEqual(get_urlconf(), "tests.test_utils.urls2") # don't set port in MULTISITE_CMS_ALIASES, otherwise it will not be matched - request = RequestFactory(host='alias3.example2.com').get('/') - CMSMultiSiteMiddleware().process_request(request) - self.assertEqual(get_urlconf(), 'tests.test_utils.urls1') + request = RequestFactory(host="alias3.example2.com").get("/") + CMSMultiSiteMiddleware(get_response).process_request(request) + self.assertEqual(get_urlconf(), "tests.test_utils.urls1") # don't set port in MULTISITE_CMS_ALIASES, otherwise it will not be matched - request = RequestFactory(host='alias3.example2.com:8000').get('/') - CMSMultiSiteMiddleware().process_request(request) - self.assertEqual(get_urlconf(), 'tests.test_utils.urls1') + request = RequestFactory(host="alias3.example2.com:8000").get("/") + CMSMultiSiteMiddleware(get_response).process_request(request) + self.assertEqual(get_urlconf(), "tests.test_utils.urls1") @override_settings(SITE_ID=2) def test_process_reponse(self): - request = RequestFactory(host='www.example2.com').get('/') - CMSMultiSiteMiddleware().process_request(request) - self.assertEqual(get_urlconf(), 'tests.test_utils.urls2') - CMSMultiSiteMiddleware().process_response(request, HttpResponse('')) + request = RequestFactory(host="www.example2.com").get("/") + get_response = mock.MagicMock() + CMSMultiSiteMiddleware(get_response).process_request(request) + self.assertEqual(get_urlconf(), "tests.test_utils.urls2") + CMSMultiSiteMiddleware(get_response).process_response(request, HttpResponse("")) # Default is restored after request is processed self.assertIsNone(get_urlconf()) - request = RequestFactory(host='alias2.example2.com').get('/') - CMSMultiSiteMiddleware().process_request(request) - self.assertEqual(get_urlconf(), 'tests.test_utils.urls2') - CMSMultiSiteMiddleware().process_response(request, HttpResponse('')) + request = RequestFactory(host="alias2.example2.com").get("/") + CMSMultiSiteMiddleware(get_response).process_request(request) + self.assertEqual(get_urlconf(), "tests.test_utils.urls2") + CMSMultiSiteMiddleware(get_response).process_response(request, HttpResponse("")) # Default is restored after request is processed self.assertIsNone(get_urlconf()) @@ -102,37 +107,39 @@ def test_process_reponse(self): class CMSMultiSiteMiddlewareAliasTest(BaseTestCase): def setUp(self): Site.objects.all().delete() - self.site = Site.objects.create(pk=1, domain='www.example.com') - self.site2 = Site.objects.create(pk=2, domain='www.example2.com') - Alias.objects.create(domain='alias1.example.com', site=self.site) - Alias.objects.create(domain='alias2.example.com', site=self.site, redirect_to_canonical=True) + self.site = Site.objects.create(pk=1, domain="www.example.com") + self.site2 = Site.objects.create(pk=2, domain="www.example2.com") + Alias.objects.create(domain="alias1.example.com", site=self.site) + Alias.objects.create(domain="alias2.example.com", site=self.site, redirect_to_canonical=True) - Alias.objects.create(domain='alias1.example2.com', site=self.site2) - Alias.objects.create(domain='alias2.example2.com', site=self.site2, redirect_to_canonical=True) + Alias.objects.create(domain="alias1.example2.com", site=self.site2) + Alias.objects.create(domain="alias2.example2.com", site=self.site2, redirect_to_canonical=True) def test_process_site_1(self): - request = RequestFactory(host='www.example.com').get('/') - DynamicSiteMiddleware().process_request(request) - CMSMultiSiteMiddleware().process_request(request) - self.assertEqual(get_urlconf(), 'tests.test_utils.urls1') + request = RequestFactory(host="www.example.com").get("/") + get_response = mock.MagicMock() + DynamicSiteMiddleware(get_response).process_request(request) + CMSMultiSiteMiddleware(get_response).process_request(request) + self.assertEqual(get_urlconf(), "tests.test_utils.urls1") - request = RequestFactory(host='alias1.example.com').get('/') - DynamicSiteMiddleware().process_request(request) - CMSMultiSiteMiddleware().process_request(request) - self.assertEqual(get_urlconf(), 'tests.test_utils.urls1') + request = RequestFactory(host="alias1.example.com").get("/") + DynamicSiteMiddleware(get_response).process_request(request) + CMSMultiSiteMiddleware(get_response).process_request(request) + self.assertEqual(get_urlconf(), "tests.test_utils.urls1") def test_process_site_2(self): - request = RequestFactory(host='www.example2.com').get('/') - DynamicSiteMiddleware().process_request(request) - CMSMultiSiteMiddleware().process_request(request) - self.assertEqual(get_urlconf(), 'tests.test_utils.urls2') + request = RequestFactory(host="www.example2.com").get("/") + get_response = mock.MagicMock() + DynamicSiteMiddleware(get_response).process_request(request) + CMSMultiSiteMiddleware(get_response).process_request(request) + self.assertEqual(get_urlconf(), "tests.test_utils.urls2") - request = RequestFactory(host='alias2.example2.com').get('/') - DynamicSiteMiddleware().process_request(request) - CMSMultiSiteMiddleware().process_request(request) - self.assertEqual(get_urlconf(), 'tests.test_utils.urls2') + request = RequestFactory(host="alias2.example2.com").get("/") + DynamicSiteMiddleware(get_response).process_request(request) + CMSMultiSiteMiddleware(get_response).process_request(request) + self.assertEqual(get_urlconf(), "tests.test_utils.urls2") # aliases not configured on django-multisite will not be recognizes - request = RequestFactory(host='alias3.example2.com').get('/') + request = RequestFactory(host="alias3.example2.com").get("/") with self.assertRaises(Http404): - DynamicSiteMiddleware().process_request(request) + DynamicSiteMiddleware(get_response).process_request(request) diff --git a/tests/test_utils/urls1.py b/tests/test_utils/urls1.py index 540a416..3e46fa7 100644 --- a/tests/test_utils/urls1.py +++ b/tests/test_utils/urls1.py @@ -1,25 +1,25 @@ from cms.utils.conf import get_cms_setting from django.conf import settings -from django.conf.urls import include, url from django.conf.urls.i18n import i18n_patterns from django.contrib import admin from django.contrib.staticfiles.urls import staticfiles_urlpatterns +from django.urls import include, path, re_path from django.views.i18n import JavaScriptCatalog from django.views.static import serve admin.autodiscover() urlpatterns = [ - url(r'^media/(?P.*)$', serve, - {'document_root': settings.MEDIA_ROOT, 'show_indexes': True}), - url(r'^media/cms/(?P.*)$', serve, - {'document_root': get_cms_setting('MEDIA_ROOT'), 'show_indexes': True}), - url(r'^jsi18n/$', JavaScriptCatalog.as_view(), name='javascript-catalog'), + re_path(r"^media/(?P.*)$", serve, {"document_root": settings.MEDIA_ROOT, "show_indexes": True}), + re_path( + r"^media/cms/(?P.*)$", serve, {"document_root": get_cms_setting("MEDIA_ROOT"), "show_indexes": True} + ), + path("jsi18n/", JavaScriptCatalog.as_view(), name="javascript-catalog"), ] urlpatterns += staticfiles_urlpatterns() urlpatterns += i18n_patterns( - url(r'^admin/', admin.site.urls), - url(r'^', include('cms.urls')), + re_path(r"^admin/", admin.site.urls), + path("", include("cms.urls")), ) diff --git a/tests/test_utils/urls2.py b/tests/test_utils/urls2.py index 6efc265..480b26b 100644 --- a/tests/test_utils/urls2.py +++ b/tests/test_utils/urls2.py @@ -1,25 +1,25 @@ from cms.utils.conf import get_cms_setting from django.conf import settings -from django.conf.urls import include, url from django.conf.urls.i18n import i18n_patterns from django.contrib import admin from django.contrib.staticfiles.urls import staticfiles_urlpatterns +from django.urls import include, path, re_path from django.views.i18n import JavaScriptCatalog from django.views.static import serve admin.autodiscover() urlpatterns = [ - url(r'^media/(?P.*)$', serve, - {'document_root': settings.MEDIA_ROOT, 'show_indexes': True}), - url(r'^media/cms/(?P.*)$', serve, - {'document_root': get_cms_setting('MEDIA_ROOT'), 'show_indexes': True}), - url(r'^jsi18n/$', JavaScriptCatalog.as_view(), name='javascript-catalog'), + re_path(r"^media/(?P.*)$", serve, {"document_root": settings.MEDIA_ROOT, "show_indexes": True}), + re_path( + r"^media/cms/(?P.*)$", serve, {"document_root": get_cms_setting("MEDIA_ROOT"), "show_indexes": True} + ), + path("jsi18n/", JavaScriptCatalog.as_view(), name="javascript-catalog"), ] urlpatterns += staticfiles_urlpatterns() urlpatterns += i18n_patterns( - url(r'^admin/', admin.site.urls), - url(r'^subpath/', include('cms.urls')), + re_path(r"^admin/", admin.site.urls), + path("subpath/", include("cms.urls")), ) diff --git a/tests/utils.py b/tests/utils.py index 0c97ed1..680166a 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -2,13 +2,12 @@ class RequestFactory(DjangoRequestFactory): - # borrowed from django-multisite, we can't import it directly due to pytest upstream dependencies def __init__(self, host): - super(RequestFactory, self).__init__() + super().__init__() self.host = host def get(self, path, data=None, host=None, **extra): if host is None: host = self.host - return super(RequestFactory, self).get(path=path, data=data, HTTP_HOST=host, **extra) + return super().get(path=path, data=data, HTTP_HOST=host, **extra) diff --git a/tox.ini b/tox.ini index 3b9d951..c1a9802 100644 --- a/tox.ini +++ b/tox.ini @@ -1,32 +1,117 @@ [tox] -envlist = pep8,isort,py{38,37,36,35}-django{30,22}-cms{37} +envlist = + black + blacken + docs + isort + isort_format + ruff + pypi-description + towncrier + py{311,310,39}-django{42,41}-cms{311} + py{311,310,39}-django{32}-cms{311,39} +minversion = 3.22 [testenv] -commands = {env:COMMAND:python} cms_helper.py +commands = {env:COMMAND:python} cms_helper.py djangocms_multisite test {posargs} deps = - django30: Django>=3.0,<3.1 - django22: Django>=2.2,<3.0 - cms37: https://github.com/divio/django-cms/archive/release/3.7.x.zip - cms37: djangocms-text-ckeditor>=3.5 + django32: Django~=3.2.0 + django41: Django~=4.1.0 + django42: Django~=4.2.0 + cms39: https://github.com/django-cms/django-cms/archive/release/3.9.x.zip + cms311: https://github.com/yakky/django-cms/archive/release/3.11.x.zip + djangocms-text-ckeditor>=4 + https://github.com/protoroto/django-multisite/archive/refs/heads/feature/add-django4-support.zip -r{toxinidir}/requirements-test.txt +passenv = + COMMAND + PYTEST_* +[testenv:ruff] +commands = + {envpython} -m ruff check djangocms_multisite tests {posargs} + {envpython} -minterrogate -c pyproject.toml djangocms_multisite tests +deps = + interrogate + ruff +skip_install = true [testenv:isort] -deps = isort<5.0 -commands = isort -c -rc -df +commands = + {envpython} -m isort -c --df djangocms_multisite tests +deps = isort>=5.12.0,<5.13.0 +skip_install = true + +[testenv:isort_format] +commands = + {envpython} -m isort djangocms_multisite tests +deps = {[testenv:isort]deps} skip_install = true -[testenv:pep8] -deps = flake8 -commands = flake8 djangocms_multisite tests +[testenv:black] +commands = + {envpython} -m black --check --diff . +deps = black skip_install = true -[testenv:docs] +[testenv:blacken] +commands = + {envpython} -m black . +deps = {[testenv:black]deps} +skip_install = true + +[testenv:towncrier] +commands = + {envpython} -m invoke towncrier-check deps = - sphinx - sphinx-rtd-theme - -rrequirements-test.txt -changedir=docs + invoke skip_install = true -commands= - sphinx-build -W -b html -d {envtmpdir}/doctrees . {toxinidir}/docs/_build/html + +[testenv:pypi-description] +commands = + {envpython} -m invoke clean + {envpython} -m check_manifest + {envpython} -m build . + {envpython} -m twine check dist/* +deps = + invoke + check-manifest + build + twine +skip_install = true + +[testenv:release] +commands = + {envpython} -m invoke clean + {envpython} -m check_manifest + {envpython} -m build . + {envpython} -m twine upload {posargs} dist/* +deps = {[testenv:pypi-description]deps} +passenv = + TWINE_* +skip_install = true + +[check-manifest] +ignore = + .* + *.ini + *.toml + *.json + *.txt + *.yml + *.yaml + .tx/** + changes/** + cms_helper.py + aldryn_config.py + tasks.py + tests/** + *.mo +ignore-bad-ideas = + *.mo + +[pytest] +DJANGO_SETTINGS_MODULE = cms_helper +python_files = test_*.py +traceback = short +addopts = --reuse-db