diff --git a/.circleci/config.yml b/.circleci/config.yml index 093cadf..052d6cc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,7 @@ jobs: nightly-wagtail-test: docker: - - image: circleci/python:3.8 + - image: circleci/python:3.9 steps: - checkout - run: git clone git@github.com:wagtail/wagtail.git diff --git a/.circleci/report_nightly_build_failure.py b/.circleci/report_nightly_build_failure.py index ef54fe5..bfd1a5d 100644 --- a/.circleci/report_nightly_build_failure.py +++ b/.circleci/report_nightly_build_failure.py @@ -3,16 +3,23 @@ This reports an error to the #nightly-build-failures Slack channel. """ + import os + import requests -if 'SLACK_WEBHOOK_URL' in os.environ: +if "SLACK_WEBHOOK_URL" in os.environ: print("Reporting to #nightly-build-failures slack channel") - response = requests.post(os.environ['SLACK_WEBHOOK_URL'], json={ - "text": "A Nightly build failed. See " + os.environ['CIRCLE_BUILD_URL'], - }) + response = requests.post( + os.environ["SLACK_WEBHOOK_URL"], + json={ + "text": "A Nightly build failed. See " + os.environ["CIRCLE_BUILD_URL"], + }, + ) print("Slack responded with:", response) else: - print("Unable to report to #nightly-build-failures slack channel because SLACK_WEBHOOK_URL is not set") + print( + "Unable to report to #nightly-build-failures slack channel because SLACK_WEBHOOK_URL is not set" + ) diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..068d2e8 --- /dev/null +++ b/.flake8 @@ -0,0 +1,5 @@ +[flake8] +max-line-length = 88 +exclude = .tox,.git,*/migrations/*,venv,dist,build,*.pyc,*.egg-info +ignore = E203,E231,E266,E501,W503,B950,F405 +max-complexity = 11 diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..810c44f --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,8 @@ +# git-blame ignored revisions +# To configure, run +# git config blame.ignoreRevsFile .git-blame-ignore-revs +# Requires Git > 2.23 +# See https://git-scm.com/docs/git-blame#Documentation/git-blame.txt---ignore-revs-fileltfilegt + +# Reformat with Black +b1212e3346b3a193aa1a10417eb2b68170a4e339 diff --git a/.github/workflows/publish-test.yml b/.github/workflows/publish-test.yml index 4e06981..b00e572 100644 --- a/.github/workflows/publish-test.yml +++ b/.github/workflows/publish-test.yml @@ -13,17 +13,17 @@ jobs: needs: ['test'] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions/setup-python@v3 + - uses: actions/setup-python@v5 with: - python-version: 3.11 + python-version: 3.13 - run: | - pip install setuptools - python setup.py sdist + pip install build + python -m build . - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: ./dist @@ -38,7 +38,7 @@ jobs: # Mandatory for trusted publishing id-token: write steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 - name: Publish package distributions to TestPyPI uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e4f25fc..5dbdd6a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,17 +14,17 @@ jobs: needs: ['test'] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions/setup-python@v3 + - uses: actions/setup-python@v5 with: - python-version: 3.11 + python-version: 3.13 - run: | - pip install setuptools - python setup.py sdist + pip install build + python -m build . - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: ./dist @@ -39,7 +39,7 @@ jobs: # Mandatory for trusted publishing id-token: write steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bade5f6..c9b3e19 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,36 +8,49 @@ on: workflow_call: # Allow for the publish workflows to run the tests by calling this workflow jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: 3.9 + cache: 'pip' + - name: Install dependencies + run: | + python -m pip install .[test] + - name: Run linting + run: | + make format test: name: Python ${{ matrix.python}} ${{ matrix.toxenv }} runs-on: ubuntu-latest continue-on-error: ${{ matrix.experimental }} strategy: matrix: - python: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python: ["3.9", "3.10", "3.11", "3.12", "3.13"] experimental: [false] toxenv: ["py"] include: - # Linting - - python: "3.8" - toxenv: flake8 - experimental: false - - python: "3.8" - toxenv: isort - experimental: false # Future Wagtail release from main branch (allowed to fail) - - python: "3.12" + - python: "3.13" toxenv: wagtailmain experimental: true steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Set up Python ${{ matrix.python }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} + cache: 'pip' - name: Install dependencies run: | - python -m pip install setuptools tox tox-py + python -m pip install tox tox-py coverage # This step runs only for jobs NOT in the include matrix - name: Run tox targets for Python ${{ matrix.python }} @@ -45,11 +58,23 @@ jobs: run: | tox --py current - # This step runs only for jobs in the include matrix and covers linting + # This step runs only for jobs in the include matrix - name: Run tox targets for Python ${{ matrix.python }} (${{ matrix.toxenv }}) if: ${{ matrix.toxenv != 'py' }} run: | tox -e ${{ matrix.toxenv }} env: TOXENV: ${{ matrix.toxenv }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Generate coverage report + run: | + coverage combine + coverage xml + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + file: coverage.xml + flags: unittests + fail_ci_if_error: true diff --git a/.gitignore b/.gitignore index 518348d..89edb21 100644 --- a/.gitignore +++ b/.gitignore @@ -3,12 +3,15 @@ __pycache__/ .cache/ .tox/ .coverage +.coverage.* .DS_Store .python-version *.egg-info/ *.py[cod] coverage.xml +htmlcov/ build/ dist/ +venv/ src/wagtailbakery/build/ src/wagtailbakery/tests/public/ diff --git a/CHANGELOG.md b/CHANGELOG.md index e9d8b95..23cdbf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added -- Compatibility with Wagtail 6.0 and 6.1. [#95](https://github.com/wagtail-nest/wagtail-bakery/pull/95) -- Compatibility with Django 5.0. [#95](https://github.com/wagtail-nest/wagtail-bakery/pull/95) +- Compatibility with Python 3.12 and 3.13 +- Compatibility with Wagtail 6.1, 6.2 and 6.3. +- Compatibility with Django 5.0 and 5.1. +- Switch `setup.py` to `pyproject.toml`. +- Linting with `black`, `isort` and `flake8`. +- Added back coverage reporting. ### Fixed @@ -21,6 +25,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Drop support for Wagtail before 5.2. [#95](https://github.com/wagtail-nest/wagtail-bakery/pull/95) - Drop support for Django before 4.2. [#95](https://github.com/wagtail-nest/wagtail-bakery/pull/95) +- Drop Python 3.8 support as it is end of life. ## [0.8.0](https://github.com/wagtail-nest/wagtail-bakery/compare/0.7.0...0.8.0) diff --git a/MANIFEST.in b/MANIFEST.in index e3f7812..8ba64a1 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -5,7 +5,6 @@ include CHANGELOG.md include LICENSE include MANIFEST.in include README.md -include setup.cfg -include setup.py +include pyproject.toml recursive-include src/wagtailbakery *.html *.py diff --git a/Makefile b/Makefile index 319c0fa..fb0ef6a 100644 --- a/Makefile +++ b/Makefile @@ -1,26 +1,28 @@ -.PHONY: help all clean install flake8 isort lint test +.PHONY: help all clean install format test .DEFAULT_GOAL := help help: ## See what commands are available. @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36mmake %-15s\033[0m # %s\n", $$1, $$2}' -all: install clean test lint ## Install, test and lint the project. +all: clean install format test ## Install, format and test the project. clean: ## Remove Python file artifacts. - find . -type f -name "*.py[co]" -delete - find . -type d -name "__pycache__" -delete + find . -name '*.pyc' -exec rm -rf {} + + find . -name '__pycache__' -exec rm -rf {} + + find . -name '*.egg-info' -exec rm -rf {} + + rm -rf build dist install: ## Install dependencies. pip install -e .[test] -flake8: ## Run flake8 on the project. - flake8 src/ - -isort: ## Run isort on the project. - isort --check-only --diff --recursive src/ - -lint: flake8 isort ## Lint the project. +format: ## Format the code. + black . + isort . + flake8 . test: ## Test the project. - py.test + pytest --cov + +cov: ## Generate coverage report (manually open htmlcov/index.html in browser) + pytest --cov --cov-report html diff --git a/README.md b/README.md index 0e4f0ff..cc5088c 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ A set of helpers for baking your Django Wagtail site out as flat files. [![License: MIT](https://img.shields.io/pypi/l/wagtail-bakery)](https://github.com/wagtail-nest/wagtail-bakery/blob/main/LICENSE) [![Build Status](https://github.com/wagtail-nest/wagtail-bakery/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/wagtail-nest/wagtail-bakery/actions/workflows/test.yml) +[![codecov](https://codecov.io/gh/wagtail-nest/wagtail-bakery/graph/badge.svg?token=5SbMtmHcal)](https://codecov.io/gh/wagtail-nest/wagtail-bakery) [![Version](https://img.shields.io/pypi/v/wagtail-bakery.svg)](https://pypi.python.org/pypi/wagtail-bakery/) [![Monthly downloads](https://img.shields.io/pypi/dm/wagtail-bakery.svg?logo=Downloads)](https://pypi.python.org/pypi/wagtail-bakery/) @@ -31,8 +32,8 @@ If you want to send an encrypted email (optional), the public key ID for securit ## Supported Versions -- Python 3.8 - 3.12 -- Django 4.2 - 5.0 +- Python 3.9 - 3.13 +- Django 4.2 - 5.1 - Wagtail >= 5.2 We aim to support the Wagtail versions as [supported](http://docs.wagtail.org/en/latest/releases/upgrading.html) by Wagtail (current LTS, current stable). @@ -163,7 +164,7 @@ Test as follow: ### Releases 1. Create a new branch (e.g. `release/1.1.3`) for the release of the new version. -1. Update the version number in `setup.py` following [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +1. Update the version number in `pyproject.toml` following [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 1. Update `CHANGELOG.md`. 1. On GitHub, create a pull request and squash merge it. 1. On GitHub, if this is a minor release bump (for example `1.1.0` or `1.2.0` but not `1.1.1`, `1.2.3`), create a `stable/1.1.x` branch from `main`. @@ -179,6 +180,8 @@ Test as follow: ## Credits +`wagtail-bakery` was originally developed by [Rob Moorman](https://github.com/robmoorman) and is now maintained by the Wagtail Nest team. + Thanks to [@mhnbcu](https://github.com/mhnbcu/wagtailbakery) for bringing this idea up initially, and [Django Bakery](https://github.com/datadesk/django-bakery) for providing the initial bakery package. diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..809bfc0 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,4 @@ +coverage: + precision: 1 + round: nearest + range: "70...100" diff --git a/examples/aws/example/migrations/0001_initial.py b/examples/aws/example/migrations/0001_initial.py index ad3e02a..33288e2 100644 --- a/examples/aws/example/migrations/0001_initial.py +++ b/examples/aws/example/migrations/0001_initial.py @@ -11,52 +11,116 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('wagtailcore', '0083_workflowcontenttype'), + ("wagtailcore", "0083_workflowcontenttype"), ] operations = [ migrations.CreateModel( - name='BlogListPage', + name="BlogListPage", fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), - ('body', wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock())], use_json_field=True)), + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.page", + ), + ), + ( + "body", + wagtail.fields.StreamField( + [("paragraph", wagtail.blocks.RichTextBlock())], + use_json_field=True, + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, - bases=('wagtailcore.page', models.Model), + bases=("wagtailcore.page", models.Model), ), migrations.CreateModel( - name='BlogPostPage', + name="BlogPostPage", fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), - ('body', wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock())], use_json_field=True)), + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.page", + ), + ), + ( + "body", + wagtail.fields.StreamField( + [("paragraph", wagtail.blocks.RichTextBlock())], + use_json_field=True, + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, - bases=('wagtailcore.page', models.Model), + bases=("wagtailcore.page", models.Model), ), migrations.CreateModel( - name='GenericPage', + name="GenericPage", fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), - ('body', wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock())], use_json_field=True)), + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.page", + ), + ), + ( + "body", + wagtail.fields.StreamField( + [("paragraph", wagtail.blocks.RichTextBlock())], + use_json_field=True, + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, - bases=('wagtailcore.page', models.Model), + bases=("wagtailcore.page", models.Model), ), migrations.CreateModel( - name='HomePage', + name="HomePage", fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), - ('body', wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock())], use_json_field=True)), + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.page", + ), + ), + ( + "body", + wagtail.fields.StreamField( + [("paragraph", wagtail.blocks.RichTextBlock())], + use_json_field=True, + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, - bases=('wagtailcore.page', models.Model), + bases=("wagtailcore.page", models.Model), ), ] diff --git a/examples/aws/example/models.py b/examples/aws/example/models.py index 65a562d..4d39101 100644 --- a/examples/aws/example/models.py +++ b/examples/aws/example/models.py @@ -8,15 +8,13 @@ class AbstractExamplePage(Page, AutoPublishingWagtailBakeryModel): body = StreamField( - [ - ('paragraph', blocks.RichTextBlock()) - ], + [("paragraph", blocks.RichTextBlock())], use_json_field=True, ) content_panels = [ - FieldPanel('title'), - FieldPanel('body') + FieldPanel("title"), + FieldPanel("body"), ] class Meta: diff --git a/examples/aws/example/settings.py b/examples/aws/example/settings.py index 9e1f606..e16cac1 100755 --- a/examples/aws/example/settings.py +++ b/examples/aws/example/settings.py @@ -24,92 +24,86 @@ # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '_vyer8o8)0jv9xbwmq8vf)9j(*#plkr+pryg*@0j9*tg5p(6ze' +SECRET_KEY = "_vyer8o8)0jv9xbwmq8vf)9j(*#plkr+pryg*@0j9*tg5p(6ze" # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = ['*'] +ALLOWED_HOSTS = ["*"] # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - - 'wagtail.contrib.forms', - 'wagtail.contrib.redirects', - 'wagtail.embeds', - 'wagtail.sites', - 'wagtail.users', - 'wagtail.snippets', - 'wagtail.documents', - 'wagtail.images', - 'wagtail.search', - 'wagtail.admin', - 'wagtail', - - 'wagtail.contrib.settings', - - 'modelcluster', - 'taggit', - - 'bakery', - 'wagtailbakery', - - 'example', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "wagtail.contrib.forms", + "wagtail.contrib.redirects", + "wagtail.embeds", + "wagtail.sites", + "wagtail.users", + "wagtail.snippets", + "wagtail.documents", + "wagtail.images", + "wagtail.search", + "wagtail.admin", + "wagtail", + "wagtail.contrib.settings", + "modelcluster", + "taggit", + "bakery", + "wagtailbakery", + "example", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - - 'wagtail.contrib.redirects.middleware.RedirectMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "wagtail.contrib.redirects.middleware.RedirectMiddleware", ] -ROOT_URLCONF = 'example.urls' +ROOT_URLCONF = "example.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [ - os.path.join(BASE_DIR, 'templates'), + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [ + os.path.join(BASE_DIR, "templates"), ], - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + "loaders": [ + "django.template.loaders.filesystem.Loader", + "django.template.loaders.app_directories.Loader", ], - 'loaders': [ - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', - ] }, }, ] -WSGI_APPLICATION = 'example.wsgi.application' +WSGI_APPLICATION = "example.wsgi.application" # Database # https://docs.djangoproject.com/en/1.10/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": os.path.join(BASE_DIR, "db.sqlite3"), } } @@ -123,9 +117,9 @@ # Internationalization # https://docs.djangoproject.com/en/1.10/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -137,22 +131,20 @@ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.10/howto/static-files/ -MEDIA_URL = '/media/' -MEDIA_ROOT = os.path.join(BASE_DIR, 'public/media') +MEDIA_URL = "/media/" +MEDIA_ROOT = os.path.join(BASE_DIR, "public/media") -STATIC_URL = '/static/' -STATIC_ROOT = os.path.join(BASE_DIR, 'public/static') +STATIC_URL = "/static/" +STATIC_ROOT = os.path.join(BASE_DIR, "public/static") -STATICFILES_DIRS = [ - os.path.join(BASE_DIR, 'static') -] +STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")] # Wagtail # https://docs.wagtail.org/en/stable/getting_started/integrating_into_django.html -WAGTAIL_SITE_NAME = 'Example' -WAGTAILADMIN_BASE_URL = 'http://127.0.0.1:8000' +WAGTAIL_SITE_NAME = "Example" +WAGTAILADMIN_BASE_URL = "http://127.0.0.1:8000" # Wagtail bakery @@ -160,13 +152,11 @@ ALLOW_BAKERY_AUTO_PUBLISHING = True -BUILD_DIR = os.path.join(BASE_DIR, '../build') +BUILD_DIR = os.path.join(BASE_DIR, "../build") -BAKERY_VIEWS = ( - 'wagtailbakery.views.AllPublishedPagesView', -) +BAKERY_VIEWS = ("wagtailbakery.views.AllPublishedPagesView",) -AWS_BUCKET_NAME = 'wagtail-bakery' +AWS_BUCKET_NAME = "wagtail-bakery" -AWS_ACCESS_KEY_ID = env.str('AWS_ACCESS_KEY_ID', '') -AWS_SECRET_ACCESS_KEY = env.str('AWS_SECRET_ACCESS_KEY', '') +AWS_ACCESS_KEY_ID = env.str("AWS_ACCESS_KEY_ID", "") +AWS_SECRET_ACCESS_KEY = env.str("AWS_SECRET_ACCESS_KEY", "") diff --git a/examples/aws/example/urls.py b/examples/aws/example/urls.py index 38b0be4..8f68426 100755 --- a/examples/aws/example/urls.py +++ b/examples/aws/example/urls.py @@ -13,6 +13,7 @@ 1. Import the include() function: from django.urls import path, include 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ + import os from django.conf import settings @@ -23,11 +24,11 @@ from wagtail.documents import urls as wagtaildocs_urls urlpatterns = [ - path('admin/', admin.site.urls), - path('cms/', include(wagtailadmin_urls)), - path('documents/', include(wagtaildocs_urls)), - path('pages/', include(wagtail_urls)), - path('', include(wagtail_urls)), + path("admin/", admin.site.urls), + path("cms/", include(wagtailadmin_urls)), + path("documents/", include(wagtaildocs_urls)), + path("pages/", include(wagtail_urls)), + path("", include(wagtail_urls)), ] if settings.DEBUG: @@ -36,4 +37,5 @@ urlpatterns += staticfiles_urlpatterns() urlpatterns += static( - settings.MEDIA_URL, document_root=os.path.join(settings.MEDIA_ROOT)) + settings.MEDIA_URL, document_root=os.path.join(settings.MEDIA_ROOT) + ) diff --git a/examples/multisite/example/migrations/0001_initial.py b/examples/multisite/example/migrations/0001_initial.py index 4695827..aada6f0 100644 --- a/examples/multisite/example/migrations/0001_initial.py +++ b/examples/multisite/example/migrations/0001_initial.py @@ -11,52 +11,116 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('wagtailcore', '0083_workflowcontenttype'), + ("wagtailcore", "0083_workflowcontenttype"), ] operations = [ migrations.CreateModel( - name='BlogListPage', + name="BlogListPage", fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), - ('body', wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock())], use_json_field=True)), + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.page", + ), + ), + ( + "body", + wagtail.fields.StreamField( + [("paragraph", wagtail.blocks.RichTextBlock())], + use_json_field=True, + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, - bases=('wagtailcore.page',), + bases=("wagtailcore.page",), ), migrations.CreateModel( - name='BlogPostPage', + name="BlogPostPage", fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), - ('body', wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock())], use_json_field=True)), + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.page", + ), + ), + ( + "body", + wagtail.fields.StreamField( + [("paragraph", wagtail.blocks.RichTextBlock())], + use_json_field=True, + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, - bases=('wagtailcore.page',), + bases=("wagtailcore.page",), ), migrations.CreateModel( - name='GenericPage', + name="GenericPage", fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), - ('body', wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock())], use_json_field=True)), + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.page", + ), + ), + ( + "body", + wagtail.fields.StreamField( + [("paragraph", wagtail.blocks.RichTextBlock())], + use_json_field=True, + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, - bases=('wagtailcore.page',), + bases=("wagtailcore.page",), ), migrations.CreateModel( - name='HomePage', + name="HomePage", fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), - ('body', wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock())], use_json_field=True)), + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.page", + ), + ), + ( + "body", + wagtail.fields.StreamField( + [("paragraph", wagtail.blocks.RichTextBlock())], + use_json_field=True, + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, - bases=('wagtailcore.page',), + bases=("wagtailcore.page",), ), ] diff --git a/examples/multisite/example/models.py b/examples/multisite/example/models.py index f78d099..19a8d95 100644 --- a/examples/multisite/example/models.py +++ b/examples/multisite/example/models.py @@ -6,15 +6,13 @@ class AbstractExamplePage(Page): body = StreamField( - [ - ('paragraph', blocks.RichTextBlock()) - ], + [("paragraph", blocks.RichTextBlock())], use_json_field=True, ) content_panels = [ - FieldPanel('title'), - FieldPanel('body') + FieldPanel("title"), + FieldPanel("body"), ] class Meta: diff --git a/examples/multisite/example/settings.py b/examples/multisite/example/settings.py index 0894211..a94e312 100755 --- a/examples/multisite/example/settings.py +++ b/examples/multisite/example/settings.py @@ -20,92 +20,86 @@ # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '_vyer8o8)0jv9xbwmq8vf)9j(*#plkr+pryg*@0j9*tg5p(6ze' +SECRET_KEY = "_vyer8o8)0jv9xbwmq8vf)9j(*#plkr+pryg*@0j9*tg5p(6ze" # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = ['*'] +ALLOWED_HOSTS = ["*"] # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - - 'wagtail.contrib.forms', - 'wagtail.contrib.redirects', - 'wagtail.embeds', - 'wagtail.sites', - 'wagtail.users', - 'wagtail.snippets', - 'wagtail.documents', - 'wagtail.images', - 'wagtail.search', - 'wagtail.admin', - 'wagtail', - - 'wagtail.contrib.settings', - - 'modelcluster', - 'taggit', - - 'bakery', - 'wagtailbakery', - - 'example', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "wagtail.contrib.forms", + "wagtail.contrib.redirects", + "wagtail.embeds", + "wagtail.sites", + "wagtail.users", + "wagtail.snippets", + "wagtail.documents", + "wagtail.images", + "wagtail.search", + "wagtail.admin", + "wagtail", + "wagtail.contrib.settings", + "modelcluster", + "taggit", + "bakery", + "wagtailbakery", + "example", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - - 'wagtail.contrib.redirects.middleware.RedirectMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "wagtail.contrib.redirects.middleware.RedirectMiddleware", ] -ROOT_URLCONF = 'example.urls' +ROOT_URLCONF = "example.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [ - os.path.join(BASE_DIR, 'templates'), + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [ + os.path.join(BASE_DIR, "templates"), ], - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + "loaders": [ + "django.template.loaders.filesystem.Loader", + "django.template.loaders.app_directories.Loader", ], - 'loaders': [ - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', - ] }, }, ] -WSGI_APPLICATION = 'example.wsgi.application' +WSGI_APPLICATION = "example.wsgi.application" # Database # https://docs.djangoproject.com/en/1.10/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": os.path.join(BASE_DIR, "db.sqlite3"), } } @@ -119,9 +113,9 @@ # Internationalization # https://docs.djangoproject.com/en/1.10/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -133,31 +127,27 @@ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.10/howto/static-files/ -MEDIA_URL = '/media/' -MEDIA_ROOT = os.path.join(BASE_DIR, 'public/media') +MEDIA_URL = "/media/" +MEDIA_ROOT = os.path.join(BASE_DIR, "public/media") -STATIC_URL = '/static/' -STATIC_ROOT = os.path.join(BASE_DIR, 'public/static') +STATIC_URL = "/static/" +STATIC_ROOT = os.path.join(BASE_DIR, "public/static") -STATICFILES_DIRS = [ - os.path.join(BASE_DIR, 'static') -] +STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")] # Wagtail # https://docs.wagtail.org/en/stable/getting_started/integrating_into_django.html -WAGTAIL_SITE_NAME = 'Example' -WAGTAILADMIN_BASE_URL = 'http://127.0.0.1:8000' +WAGTAIL_SITE_NAME = "Example" +WAGTAILADMIN_BASE_URL = "http://127.0.0.1:8000" # Wagtail bakery # https://github.com/wagtail-nest/wagtail-bakery -BUILD_DIR = os.path.join(BASE_DIR, '../build') +BUILD_DIR = os.path.join(BASE_DIR, "../build") BAKERY_MULTISITE = True -BAKERY_VIEWS = ( - 'wagtailbakery.views.AllPublishedPagesView', -) +BAKERY_VIEWS = ("wagtailbakery.views.AllPublishedPagesView",) diff --git a/examples/multisite/example/urls.py b/examples/multisite/example/urls.py index 38b0be4..8f68426 100755 --- a/examples/multisite/example/urls.py +++ b/examples/multisite/example/urls.py @@ -13,6 +13,7 @@ 1. Import the include() function: from django.urls import path, include 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ + import os from django.conf import settings @@ -23,11 +24,11 @@ from wagtail.documents import urls as wagtaildocs_urls urlpatterns = [ - path('admin/', admin.site.urls), - path('cms/', include(wagtailadmin_urls)), - path('documents/', include(wagtaildocs_urls)), - path('pages/', include(wagtail_urls)), - path('', include(wagtail_urls)), + path("admin/", admin.site.urls), + path("cms/", include(wagtailadmin_urls)), + path("documents/", include(wagtaildocs_urls)), + path("pages/", include(wagtail_urls)), + path("", include(wagtail_urls)), ] if settings.DEBUG: @@ -36,4 +37,5 @@ urlpatterns += staticfiles_urlpatterns() urlpatterns += static( - settings.MEDIA_URL, document_root=os.path.join(settings.MEDIA_ROOT)) + settings.MEDIA_URL, document_root=os.path.join(settings.MEDIA_ROOT) + ) diff --git a/examples/site/example/migrations/0001_initial.py b/examples/site/example/migrations/0001_initial.py index 4695827..aada6f0 100644 --- a/examples/site/example/migrations/0001_initial.py +++ b/examples/site/example/migrations/0001_initial.py @@ -11,52 +11,116 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('wagtailcore', '0083_workflowcontenttype'), + ("wagtailcore", "0083_workflowcontenttype"), ] operations = [ migrations.CreateModel( - name='BlogListPage', + name="BlogListPage", fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), - ('body', wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock())], use_json_field=True)), + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.page", + ), + ), + ( + "body", + wagtail.fields.StreamField( + [("paragraph", wagtail.blocks.RichTextBlock())], + use_json_field=True, + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, - bases=('wagtailcore.page',), + bases=("wagtailcore.page",), ), migrations.CreateModel( - name='BlogPostPage', + name="BlogPostPage", fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), - ('body', wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock())], use_json_field=True)), + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.page", + ), + ), + ( + "body", + wagtail.fields.StreamField( + [("paragraph", wagtail.blocks.RichTextBlock())], + use_json_field=True, + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, - bases=('wagtailcore.page',), + bases=("wagtailcore.page",), ), migrations.CreateModel( - name='GenericPage', + name="GenericPage", fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), - ('body', wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock())], use_json_field=True)), + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.page", + ), + ), + ( + "body", + wagtail.fields.StreamField( + [("paragraph", wagtail.blocks.RichTextBlock())], + use_json_field=True, + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, - bases=('wagtailcore.page',), + bases=("wagtailcore.page",), ), migrations.CreateModel( - name='HomePage', + name="HomePage", fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), - ('body', wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock())], use_json_field=True)), + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.page", + ), + ), + ( + "body", + wagtail.fields.StreamField( + [("paragraph", wagtail.blocks.RichTextBlock())], + use_json_field=True, + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, - bases=('wagtailcore.page',), + bases=("wagtailcore.page",), ), ] diff --git a/examples/site/example/models.py b/examples/site/example/models.py index f78d099..19a8d95 100644 --- a/examples/site/example/models.py +++ b/examples/site/example/models.py @@ -6,15 +6,13 @@ class AbstractExamplePage(Page): body = StreamField( - [ - ('paragraph', blocks.RichTextBlock()) - ], + [("paragraph", blocks.RichTextBlock())], use_json_field=True, ) content_panels = [ - FieldPanel('title'), - FieldPanel('body') + FieldPanel("title"), + FieldPanel("body"), ] class Meta: diff --git a/examples/site/example/settings.py b/examples/site/example/settings.py index f2d9824..ea2cb32 100755 --- a/examples/site/example/settings.py +++ b/examples/site/example/settings.py @@ -20,92 +20,86 @@ # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '_vyer8o8)0jv9xbwmq8vf)9j(*#plkr+pryg*@0j9*tg5p(6ze' +SECRET_KEY = "_vyer8o8)0jv9xbwmq8vf)9j(*#plkr+pryg*@0j9*tg5p(6ze" # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = ['*'] +ALLOWED_HOSTS = ["*"] # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - - 'wagtail.contrib.forms', - 'wagtail.contrib.redirects', - 'wagtail.embeds', - 'wagtail.sites', - 'wagtail.users', - 'wagtail.snippets', - 'wagtail.documents', - 'wagtail.images', - 'wagtail.search', - 'wagtail.admin', - 'wagtail', - - 'wagtail.contrib.settings', - - 'modelcluster', - 'taggit', - - 'bakery', - 'wagtailbakery', - - 'example', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "wagtail.contrib.forms", + "wagtail.contrib.redirects", + "wagtail.embeds", + "wagtail.sites", + "wagtail.users", + "wagtail.snippets", + "wagtail.documents", + "wagtail.images", + "wagtail.search", + "wagtail.admin", + "wagtail", + "wagtail.contrib.settings", + "modelcluster", + "taggit", + "bakery", + "wagtailbakery", + "example", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - - 'wagtail.contrib.redirects.middleware.RedirectMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "wagtail.contrib.redirects.middleware.RedirectMiddleware", ] -ROOT_URLCONF = 'example.urls' +ROOT_URLCONF = "example.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [ - os.path.join(BASE_DIR, 'templates'), + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [ + os.path.join(BASE_DIR, "templates"), ], - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + "loaders": [ + "django.template.loaders.filesystem.Loader", + "django.template.loaders.app_directories.Loader", ], - 'loaders': [ - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', - ] }, }, ] -WSGI_APPLICATION = 'example.wsgi.application' +WSGI_APPLICATION = "example.wsgi.application" # Database # https://docs.djangoproject.com/en/1.10/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": os.path.join(BASE_DIR, "db.sqlite3"), } } @@ -119,9 +113,9 @@ # Internationalization # https://docs.djangoproject.com/en/1.10/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -133,29 +127,25 @@ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.10/howto/static-files/ -MEDIA_URL = '/media/' -MEDIA_ROOT = os.path.join(BASE_DIR, 'public/media') +MEDIA_URL = "/media/" +MEDIA_ROOT = os.path.join(BASE_DIR, "public/media") -STATIC_URL = '/static/' -STATIC_ROOT = os.path.join(BASE_DIR, 'public/static') +STATIC_URL = "/static/" +STATIC_ROOT = os.path.join(BASE_DIR, "public/static") -STATICFILES_DIRS = [ - os.path.join(BASE_DIR, 'static') -] +STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")] # Wagtail # https://docs.wagtail.org/en/stable/getting_started/integrating_into_django.html -WAGTAIL_SITE_NAME = 'Example' -WAGTAILADMIN_BASE_URL = 'http://127.0.0.1:8000' +WAGTAIL_SITE_NAME = "Example" +WAGTAILADMIN_BASE_URL = "http://127.0.0.1:8000" # Wagtail bakery # https://github.com/wagtail/wagtail-bakery -BUILD_DIR = os.path.join(BASE_DIR, '../build') +BUILD_DIR = os.path.join(BASE_DIR, "../build") -BAKERY_VIEWS = ( - 'wagtailbakery.views.AllPublishedPagesView', -) +BAKERY_VIEWS = ("wagtailbakery.views.AllPublishedPagesView",) diff --git a/examples/site/example/urls.py b/examples/site/example/urls.py index 318ae21..cc0c9e6 100755 --- a/examples/site/example/urls.py +++ b/examples/site/example/urls.py @@ -13,6 +13,7 @@ 1. Import the include() function: from django.urls import path, include 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ + import os from django.conf import settings @@ -23,11 +24,11 @@ from wagtail.documents import urls as wagtaildocs_urls urlpatterns = [ - path('admin/', admin.site.urls), - path('cms/', include(wagtailadmin_urls)), - path('documents/', include(wagtaildocs_urls)), - path('pages/', include(wagtail_urls)), - path('', include(wagtail_urls)), + path("admin/", admin.site.urls), + path("cms/", include(wagtailadmin_urls)), + path("documents/", include(wagtaildocs_urls)), + path("pages/", include(wagtail_urls)), + path("", include(wagtail_urls)), ] if settings.DEBUG: @@ -36,4 +37,5 @@ urlpatterns += staticfiles_urlpatterns() urlpatterns += static( - settings.MEDIA_URL, document_root=os.path.join(settings.MEDIA_ROOT)) + settings.MEDIA_URL, document_root=os.path.join(settings.MEDIA_ROOT) + ) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e8939cd --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,98 @@ +[build-system] +requires = ["setuptools >= 61.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "wagtail-bakery" +version = "0.9.0" +description = "A set of helpers for baking your Django Wagtail site out as flat files." +readme = "README.md" +authors = [ + { name = "Wagtail Nest team", email = "hello@wagtail.org" }, +] +requires-python = ">=3.9" +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: Web Environment", + "Framework :: Django", + "Framework :: Wagtail", + "Framework :: Wagtail :: 5", + "Framework :: Wagtail :: 6", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Topic :: Software Development :: Build Tools", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", +] +dependencies = [ + "django-bakery~=0.13.1", + "wagtail>=5.2", +] + +[project.optional-dependencies] +test = [ + "black>=24,<25", + "coverage[toml]>=7.2.7,<8.0", + "factory-boy", + "flake8", + "flake8-black", + "flake8-isort", + "isort", + "pytest-cov", + "pytest-django", + "pytest", +] + +[project.urls] +Changelog = "https://github.com/wagtail-nest/wagtail-bakery/blob/main/CHANGELOG.md" +Homepage = "https://github.com/wagtail-nest/wagtail-bakery/" +"Issue tracker" = "https://github.com/wagtail-nest/wagtail-bakery/issues/" +Source = "https://github.com/wagtail-nest/wagtail-bakery/" + +[tool.isort] +force_grid_wrap = 0 +include_trailing_comma = true +line_length = 88 +multi_line_output = 3 +use_parentheses = true + +[tool.black] +line-length = 88 + +[tool.coverage.run] +branch = true +source_pkgs = ["wagtailbakery"] + +omit = ["tests/*"] + +[tool.coverage.paths] +source = ["src/wagtailbakery"] + +[tool.coverage.report] +show_missing = true +exclude_lines = [ + # Have to re-enable the standard pragma + "pragma: no cover", + + # Don't complain about missing debug-only code: + "def __repr__", + "if self.debug", + + # Don't complain if tests don't hit defensive assertion code: + "raise AssertionError", + "raise NotImplementedError", + + # Don't complain if non-runnable code isn't run: + "if 0:", + "if __name__ == .__main__.:", + + # Don't complain about abstract methods, they aren't run: + "@(abc.)?abstractmethod", + + # Nor complain about type checking + "if TYPE_CHECKING:", +] diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 253a633..0000000 --- a/setup.cfg +++ /dev/null @@ -1,32 +0,0 @@ -[coverage:run] -omit=src/wagtailbakery/tests/* - -[flake8] -# D100: Missing docstring in public module -# D101: Missing docstring in public class -# D102: Missing docstring in public method -# D103: Missing docstring in public function -# D105: Missing docstring in magic method -# D200: One-line docstring should fit on one line with quotes -# D202: No blank lines allowed after function docstring -# D204: 1 blank line required after docstring -# D205: 1 blank line required between summary line and description -# D209: Multi-line docstring closing quotes should be on a separate line -# D400: First line should end with a period -# D401: First line should be in imperative mood -# E303: Too many blank lines -# E501: Line too long -# W503: line break before binary operator (superseded by W504 line break after binary operator) -# N805: First argument of a method should be named 'self' -# N806: Variable in function should be lowercase -ignore = D100,D101,D102,D103,D105,D200,D202,D204,D205,D209,D400,D401,E303,E501,W503,N805,N806 -exclude=*/migrations/ -max-line-length = 120 - -[isort] -line_length=100 -multi_line_output=4 -known_third_party=bakery,django,factory,pytest,wagtail - -[tool:pytest] -testpaths=tests diff --git a/setup.py b/setup.py deleted file mode 100644 index 82d507c..0000000 --- a/setup.py +++ /dev/null @@ -1,57 +0,0 @@ -from setuptools import find_packages, setup - -with open('README.md', 'r') as fh: - long_description = fh.read() - -install_requires = [ - 'django-bakery~=0.13.1', - 'wagtail>=4.1', -] - -test_requires = [ - 'factory-boy', - 'flake8', - 'isort', - 'pytest', - 'pytest-cov', - 'pytest-django', -] - -setup( - name='wagtail-bakery', - version='0.8.0', - description='A set of helpers for baking your Django Wagtail site out as flat files.', - long_description=long_description, - long_description_content_type='text/markdown', - author='Rob Moorman and Wagtail Core Team', - author_email='hello@wagtail.org', - url="https://github.com/wagtail-nest/wagtail-bakery/", - project_urls={ - "Source": "https://github.com/wagtail-nest/wagtail-bakery/", - "Issue tracker": "https://github.com/wagtail-nest/wagtail-bakery/issues/", - "Changelog": "https://github.com/wagtail-nest/wagtail-bakery/blob/main/CHANGELOG.md", - }, - install_requires=install_requires, - tests_require=test_requires, - extras_require={'test': test_requires}, - package_dir={'': 'src'}, - packages=find_packages('src'), - include_package_data=True, - zip_safe=False, - license='MIT', - classifiers=[ - 'Environment :: Web Environment', - 'License :: OSI Approved :: MIT License', - 'Framework :: Django', - 'Framework :: Wagtail', - 'Framework :: Wagtail :: 5', - 'Framework :: Wagtail :: 6', - 'Operating System :: Unix', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.12', - ], -) diff --git a/src/wagtailbakery/__init__.py b/src/wagtailbakery/__init__.py index f34d0b5..83ed1c1 100644 --- a/src/wagtailbakery/__init__.py +++ b/src/wagtailbakery/__init__.py @@ -1 +1 @@ -default_app_config = 'wagtailbakery.apps.WagtailBakeryAppConfig' +default_app_config = "wagtailbakery.apps.WagtailBakeryAppConfig" diff --git a/src/wagtailbakery/api_views.py b/src/wagtailbakery/api_views.py index 586dd18..eaf16d8 100644 --- a/src/wagtailbakery/api_views.py +++ b/src/wagtailbakery/api_views.py @@ -18,9 +18,14 @@ class APIResponseError(Exception): def handle_api_error(response): if response.status_code == 400: - raise APIResponseError("API error: " + json.loads(response.render().content.decode('UTF-8'))['message']) + raise APIResponseError( + "API error: " + + json.loads(response.render().content.decode("UTF-8"))["message"] + ) - raise APIResponseError("Unexpected status code returned from API: %d" % response.status_code) + raise APIResponseError( + "Unexpected status code returned from API: %d" % response.status_code + ) class APIListingView(BuildableMixin): @@ -65,6 +70,7 @@ class APIDetailView(BuildableMixin): get_queryset: A method that returns a queryset of objects to include """ + @property def build_method(self): return self.build_queryset @@ -99,10 +105,12 @@ def unbuild_object(self, obj): def get_content(self, obj): # Create a dummy request - request = self.create_request('/?format=json&fields=*') - request.wagtailapi_router = WagtailAPIRouter('') + request = self.create_request("/?format=json&fields=*") + request.wagtailapi_router = WagtailAPIRouter("") - response = self.endpoint_class.as_view({'get': 'detail_view'})(request, pk=obj.pk) + response = self.endpoint_class.as_view({"get": "detail_view"})( + request, pk=obj.pk + ) if response.status_code == 200: return response.render().content @@ -116,13 +124,14 @@ class PagesAPIDetailView(APIDetailView): URL example: /api/pages/detail/1.json """ + endpoint_class = PagesAPIViewSet def get_build_path(self, page): - return 'api/pages/detail/{pk}.json'.format(pk=page.pk) + return "api/pages/detail/{pk}.json".format(pk=page.pk) def get_queryset(self): - if getattr(settings, 'BAKERY_MULTISITE', False): + if getattr(settings, "BAKERY_MULTISITE", False): return Page.objects.all().public().live() else: site = Site.objects.get(is_default_site=True) @@ -135,22 +144,32 @@ class PagesAPIListingView(APIListingView): URL example: /api/pages/1.json """ + def get_build_path(self, page_num): - return 'api/pages/{page_num}.json'.format(page_num=page_num) + return "api/pages/{page_num}.json".format(page_num=page_num) def fetch_page_listing(self, page_num, model=None): if model: - url = '/?format=json&fields=*&limit={}&offset={}&type={}.{}'.format(self.results_per_page, self.results_per_page * page_num, model._meta.app_label, model.__name__) + url = "/?format=json&fields=*&limit={}&offset={}&type={}.{}".format( + self.results_per_page, + self.results_per_page * page_num, + model._meta.app_label, + model.__name__, + ) else: - url = '/?format=json&fields=*&limit={}&offset={}'.format(self.results_per_page, self.results_per_page * page_num) + url = "/?format=json&fields=*&limit={}&offset={}".format( + self.results_per_page, self.results_per_page * page_num + ) request = self.create_request(url) - request.wagtailapi_router = WagtailAPIRouter('') - response = PagesAPIViewSet.as_view({'get': 'listing_view'})(request) + request.wagtailapi_router = WagtailAPIRouter("") + response = PagesAPIViewSet.as_view({"get": "listing_view"})(request) if response.status_code == 200: content = response.render().content - has_next_page = json.loads(content.decode('UTF-8'))['meta']['total_count'] > self.results_per_page * (page_num + 1) + has_next_page = json.loads(content.decode("UTF-8"))["meta"][ + "total_count" + ] > self.results_per_page * (page_num + 1) return content, has_next_page handle_api_error(response) @@ -166,8 +185,9 @@ class TypedPagesAPIListingView(PagesAPIListingView): URL example: /api/pages/blog_BlogPage/1.json """ + def get_build_path(self, model, page_num): - return 'api/pages/{app_label}_{class_name}/{page_num}.json'.format( + return "api/pages/{app_label}_{class_name}/{page_num}.json".format( app_label=model._meta.app_label, class_name=model.__name__, page_num=page_num, @@ -179,7 +199,9 @@ def get_content(self, model, page_num): def get_page_models(self): return [ content_type.model_class() - for content_type in ContentType.objects.filter(id__in=Page.objects.values_list('content_type_id', flat=True)) + for content_type in ContentType.objects.filter( + id__in=Page.objects.values_list("content_type_id", flat=True) + ) ] def build(self): diff --git a/src/wagtailbakery/apps.py b/src/wagtailbakery/apps.py index eeff3f1..69f2e10 100644 --- a/src/wagtailbakery/apps.py +++ b/src/wagtailbakery/apps.py @@ -4,8 +4,8 @@ class WagtailBakeryAppConfig(AppConfig): - name = 'wagtailbakery' - label = 'wagtailbakery' + name = "wagtailbakery" + label = "wagtailbakery" verbose_name = "Wagtail bakery" def ready(self): diff --git a/src/wagtailbakery/models.py b/src/wagtailbakery/models.py index 6cd979f..7b52472 100644 --- a/src/wagtailbakery/models.py +++ b/src/wagtailbakery/models.py @@ -5,7 +5,8 @@ class BuildableWagtailBakeryModel(BuildableModel): """ Buildable Wagtail bakery page model mixin class. """ - detail_views = ['wagtailbakery.views.WagtailBakeryView'] + + detail_views = ["wagtailbakery.views.WagtailBakeryView"] def _build_related(self): # TODO: Build related pages with get_static_site_paths @@ -19,8 +20,9 @@ class AutoPublishingWagtailBakeryModel(AutoPublishingBuildableModel): """ Auto publishing Wagtail bakery page model mixin class. """ - detail_views = ['wagtailbakery.views.WagtailBakeryView'] - publication_status_field = 'live' + + detail_views = ["wagtailbakery.views.WagtailBakeryView"] + publication_status_field = "live" def _build_related(self): # TODO: Build related pages with get_static_site_paths diff --git a/src/wagtailbakery/signal_handlers.py b/src/wagtailbakery/signal_handlers.py index 9d3259d..5d31f8f 100644 --- a/src/wagtailbakery/signal_handlers.py +++ b/src/wagtailbakery/signal_handlers.py @@ -16,7 +16,7 @@ def handle_unpublish(sender, instance, **kwargs): def register_signal_handlers(): - page_published.connect( - handle_publish, dispatch_uid='wagtailbakery_page_published') + page_published.connect(handle_publish, dispatch_uid="wagtailbakery_page_published") page_unpublished.connect( - handle_unpublish, dispatch_uid='wagtailbakery_page_unpublished') + handle_unpublish, dispatch_uid="wagtailbakery_page_unpublished" + ) diff --git a/src/wagtailbakery/views.py b/src/wagtailbakery/views.py index 057c88d..c51c4a4 100644 --- a/src/wagtailbakery/views.py +++ b/src/wagtailbakery/views.py @@ -18,6 +18,7 @@ class WagtailBakeryView(BuildableDetailView): An abstract class that can be inherited to create a buildable view that can be added to BAKERY_VIEWS setting. """ + def __init__(self, *args, **kwargs): self.handler = BaseHandler() self.handler.load_middleware() @@ -32,35 +33,34 @@ def get_content(self, obj): response = self.get(self.request) if isinstance(response, HttpResponseRedirect): return self.get_redirect_content(response, obj) - if hasattr(response, 'render'): + if hasattr(response, "render"): return response.render().content - if hasattr(response, 'content'): + if hasattr(response, "content"): return response.content raise AttributeError( - "'%s' object has no attribute 'render' or 'content'" % response) + "'%s' object has no attribute 'render' or 'content'" % response + ) def get_redirect_content(self, response, obj): context = { - 'page': obj, - 'self': obj, - 'redirect_url': response.url, + "page": obj, + "self": obj, + "redirect_url": response.url, } - content = render( - self.request, 'wagtailbakery/redirect.html', context).content + content = render(self.request, "wagtailbakery/redirect.html", context).content return response.make_bytes(content) def get_build_path(self, obj): url = self.get_url(obj) - if url.startswith('http'): + if url.startswith("http"): # Multisite has absolute urls url_parsed = urlparse(url) path = unquote(url_parsed.path[1:]) hostname = url_parsed.hostname - if getattr(settings, 'BAKERY_MULTISITE', False): - build_path = os.path.join( - settings.BUILD_DIR, hostname, path) + if getattr(settings, "BAKERY_MULTISITE", False): + build_path = os.path.join(settings.BUILD_DIR, hostname, path) else: build_path = os.path.join(settings.BUILD_DIR, path) else: @@ -72,7 +72,7 @@ def get_build_path(self, obj): os.path.exists(build_path) or os.makedirs(build_path) # Always append index.html at the end of the path - return os.path.join(build_path, 'index.html') + return os.path.join(build_path, "index.html") def get_url(self, obj): """Return Wagtail page url instead of Django's get_absolute_url.""" @@ -89,8 +89,7 @@ def build_object(self, obj): """ site = obj.get_site() logger.debug("Building %s" % obj) - self.request = RequestFactory( - SERVER_NAME=site.hostname).get(self.get_url(obj)) + self.request = RequestFactory(SERVER_NAME=site.hostname).get(self.get_url(obj)) self.set_kwargs(obj) path = self.get_build_path(obj) self.build_file(path, self.get_content(obj)) @@ -118,8 +117,9 @@ class AllPagesView(WagtailBakeryView): 'wagtailbakery.views.AllPagesView', ) """ + def get_queryset(self): - if getattr(settings, 'BAKERY_MULTISITE', False): + if getattr(settings, "BAKERY_MULTISITE", False): return Page.objects.all().public() else: site = Site.objects.get(is_default_site=True) @@ -138,6 +138,7 @@ class AllPublishedPagesView(AllPagesView): 'wagtailbakery.views.AllPublishedPagesView', ) """ + def get_queryset(self): pages = super().get_queryset() return pages.live() diff --git a/tests/conftest.py b/tests/conftest.py index 8a5bd49..e9cadf5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,91 +4,88 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -pytest_plugins = 'tests.fixtures' +pytest_plugins = "tests.fixtures" def pytest_configure(): wagtail_apps = [ - 'wagtail.contrib.forms', - 'wagtail.contrib.redirects', - 'wagtail.embeds', - 'wagtail.sites', - 'wagtail.users', - 'wagtail.snippets', - 'wagtail.documents', - 'wagtail.images', - 'wagtail.search', - 'wagtail.admin', - 'wagtail', + "wagtail.contrib.forms", + "wagtail.contrib.redirects", + "wagtail.embeds", + "wagtail.sites", + "wagtail.users", + "wagtail.snippets", + "wagtail.documents", + "wagtail.images", + "wagtail.search", + "wagtail.admin", + "wagtail", ] wagtail_middleware = [ - 'wagtail.contrib.redirects.middleware.RedirectMiddleware', + "wagtail.contrib.redirects.middleware.RedirectMiddleware", ] settings.configure( SECRET_KEY="secret_for_testing_only", DATABASES={ - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": os.path.join(BASE_DIR, "db.sqlite3"), } }, INSTALLED_APPS=[ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - - ] + wagtail_apps + [ - - 'modelcluster', - 'taggit', - - 'bakery', - 'wagtailbakery', - - 'tests', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + ] + + wagtail_apps + + [ + "modelcluster", + "taggit", + "bakery", + "wagtailbakery", + "tests", ], MIDDLEWARE=[ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.locale.LocaleMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - ] + wagtail_middleware, - ROOT_URLCONF='tests.urls', - ALLOWED_HOSTS='*', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.locale.LocaleMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + ] + + wagtail_middleware, + ROOT_URLCONF="tests.urls", + ALLOWED_HOSTS="*", TEMPLATES=[ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [ - os.path.join(BASE_DIR, 'templates'), + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [ + os.path.join(BASE_DIR, "templates"), ], - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + "loaders": [ + "django.template.loaders.filesystem.Loader", + "django.template.loaders.app_directories.Loader", ], - 'loaders': [ - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', - ] }, }, ], - WAGTAIL_SITE_NAME='Wagtail Bakery', - BUILD_DIR=os.path.join(BASE_DIR, 'build'), - BAKERY_VIEWS=( - 'wagtailbakery.views.AllPagesView', - ), + WAGTAIL_SITE_NAME="Wagtail Bakery", + BUILD_DIR=os.path.join(BASE_DIR, "build"), + BAKERY_VIEWS=("wagtailbakery.views.AllPagesView",), CELERY_ALWAYS_EAGER=True, STATIC_URL="/static/", ) diff --git a/tests/factories/page.py b/tests/factories/page.py index 6a21408..5693d16 100644 --- a/tests/factories/page.py +++ b/tests/factories/page.py @@ -5,10 +5,10 @@ class PageFactory(factory.django.DjangoModelFactory): depth = 2 numchild = 0 - path = '00010002' - title = 'Page' - slug = 'page' - url_path = '/page/' + path = "00010002" + title = "Page" + slug = "page" + url_path = "/page/" class Meta: model = Page diff --git a/tests/factories/site.py b/tests/factories/site.py index 0910f0d..d61bf5e 100644 --- a/tests/factories/site.py +++ b/tests/factories/site.py @@ -3,10 +3,10 @@ class SiteFactory(factory.django.DjangoModelFactory): - hostname = 'localhost' + hostname = "localhost" port = 80 is_default_site = True class Meta: model = Site - django_get_or_create = ('hostname', 'port') + django_get_or_create = ("hostname", "port") diff --git a/tests/fixtures.py b/tests/fixtures.py index 8a7278e..586fc83 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -5,7 +5,7 @@ def page(): from wagtail.models import Page - return Page.objects.get(slug='home') + return Page.objects.get(slug="home") @pytest.fixture @@ -20,27 +20,35 @@ def page_tree(page): from tests.factories.page import PageFactory # /first - first_page = PageFactory(depth=3, path='000100010001', slug='first', numchild=2) + first_page = PageFactory(depth=3, path="000100010001", slug="first", numchild=2) # /first/first - PageFactory(depth=4, path=f'{first_page.path}0001', slug='first') + PageFactory(depth=4, path=f"{first_page.path}0001", slug="first") # /first/second - PageFactory(depth=4, path=f'{first_page.path}0002', slug='second') + PageFactory(depth=4, path=f"{first_page.path}0002", slug="second") # /second - PageFactory(depth=3, path='000100010002', slug='second') + PageFactory(depth=3, path="000100010002", slug="second") # /third - PageFactory(depth=3, path='000100010003', slug='third') + PageFactory(depth=3, path="000100010003", slug="third") # /unicode-children - unicode_page = PageFactory(depth=3, path='000100010004', slug='unicode-children', numchild=3) + unicode_page = PageFactory( + depth=3, path="000100010004", slug="unicode-children", numchild=3 + ) # /unicode-children/latin-capital-letter-i-with-diaeresis-Ï - PageFactory(depth=4, path=f'{unicode_page.path}0001', slug='latin-capital-letter-i-with-diaeresis-Ï') + PageFactory( + depth=4, + path=f"{unicode_page.path}0001", + slug="latin-capital-letter-i-with-diaeresis-Ï", + ) # /unicode-children/cyrillic-capital-letter-ya-Я - PageFactory(depth=4, path=f'{unicode_page.path}0002', slug='cyrillic-capital-letter-ya-Я') + PageFactory( + depth=4, path=f"{unicode_page.path}0002", slug="cyrillic-capital-letter-ya-Я" + ) # /unicode-children/cjk-fire-火 - PageFactory(depth=4, path=f'{unicode_page.path}0003', slug='cjk-fire-火') + PageFactory(depth=4, path=f"{unicode_page.path}0003", slug="cjk-fire-火") page.numchild = 4 page.save() @@ -53,16 +61,13 @@ def multisite(site): from tests.factories.page import PageFactory from tests.factories.site import SiteFactory - page_2 = PageFactory(path='00010003', slug='page-2') - site_2 = SiteFactory( - hostname='site_2', is_default_site=False, root_page=page_2) + page_2 = PageFactory(path="00010003", slug="page-2") + site_2 = SiteFactory(hostname="site_2", is_default_site=False, root_page=page_2) - page_3 = PageFactory(path='00010004', slug='page-3') - site_3 = SiteFactory( - hostname='site_3', is_default_site=False, root_page=page_3) + page_3 = PageFactory(path="00010004", slug="page-3") + site_3 = SiteFactory(hostname="site_3", is_default_site=False, root_page=page_3) - page_4 = PageFactory(path='00010005', slug='page-4') - site_4 = SiteFactory( - hostname='site_4', is_default_site=False, root_page=page_4) + page_4 = PageFactory(path="00010005", slug="page-4") + site_4 = SiteFactory(hostname="site_4", is_default_site=False, root_page=page_4) return [site, site_2, site_3, site_4] diff --git a/tests/integration/test_api_views.py b/tests/integration/test_api_views.py index b8ae40d..e51e28c 100644 --- a/tests/integration/test_api_views.py +++ b/tests/integration/test_api_views.py @@ -3,21 +3,24 @@ import pytest from wagtailbakery.api_views import ( - PagesAPIDetailView, PagesAPIListingView, TypedPagesAPIListingView) + PagesAPIDetailView, + PagesAPIListingView, + TypedPagesAPIListingView, +) from ..models import EventPage -DEFAULT_PAGE_FIELDS = {'id', 'meta', 'title'} +DEFAULT_PAGE_FIELDS = {"id", "meta", "title"} DEFAULT_PAGE_META_FIELDS = { - 'type', - 'show_in_menus', - 'search_description', - 'first_published_at', - 'slug', - 'html_url', - 'seo_title', - 'locale', - 'alias_of', + "type", + "show_in_menus", + "search_description", + "first_published_at", + "slug", + "html_url", + "seo_title", + "locale", + "alias_of", } @@ -27,17 +30,17 @@ def test_wagtail_bakery_pages_api_detail_view(page_tree): # Check build path build_path = view.get_build_path(page_tree) - assert build_path == 'api/pages/detail/2.json' + assert build_path == "api/pages/detail/2.json" # Check child build path for first child page child_page = page_tree.get_descendants().first() build_path = view.get_build_path(child_page) - assert build_path == 'api/pages/detail/3.json' + assert build_path == "api/pages/detail/3.json" # Check child build path of the first grandchild page child_page = child_page.get_descendants().first() build_path = view.get_build_path(child_page) - assert build_path == 'api/pages/detail/4.json' + assert build_path == "api/pages/detail/4.json" @pytest.mark.django_db @@ -48,7 +51,7 @@ def test_wagtail_bakery_pages_api_detail_view_build_path_for_multisite(multisite # Check build path for homepage build_path = view.get_build_path(page) - assert build_path == 'api/pages/detail/2.json' + assert build_path == "api/pages/detail/2.json" @pytest.mark.django_db @@ -56,27 +59,27 @@ def test_wagtail_bakery_pages_api_detail_view_content(page_tree): view = PagesAPIDetailView() # Check content for homepage - content = json.loads(view.get_content(page_tree).decode('UTF-8')) + content = json.loads(view.get_content(page_tree).decode("UTF-8")) assert set(content.keys()) == DEFAULT_PAGE_FIELDS - assert set(content['meta'].keys()) == DEFAULT_PAGE_META_FIELDS.union({'parent'}) - assert content['id'] == 2 - assert content['title'] == "Welcome to your new Wagtail site!" + assert set(content["meta"].keys()) == DEFAULT_PAGE_META_FIELDS.union({"parent"}) + assert content["id"] == 2 + assert content["title"] == "Welcome to your new Wagtail site!" # Check content for first child page child_page = page_tree.get_descendants().first() - content = json.loads(view.get_content(child_page).decode('UTF-8')) + content = json.loads(view.get_content(child_page).decode("UTF-8")) assert set(content.keys()) == DEFAULT_PAGE_FIELDS - assert set(content['meta'].keys()) == DEFAULT_PAGE_META_FIELDS.union({'parent'}) - assert content['id'] == 3 - assert content['title'] == "Page" + assert set(content["meta"].keys()) == DEFAULT_PAGE_META_FIELDS.union({"parent"}) + assert content["id"] == 3 + assert content["title"] == "Page" # Check content of the first grandchild page grandchild_page = child_page.get_descendants().first() - content = json.loads(view.get_content(grandchild_page).decode('UTF-8')) + content = json.loads(view.get_content(grandchild_page).decode("UTF-8")) assert set(content.keys()) == DEFAULT_PAGE_FIELDS - assert set(content['meta'].keys()) == DEFAULT_PAGE_META_FIELDS.union({'parent'}) - assert content['id'] == 4 - assert content['title'] == "Page" + assert set(content["meta"].keys()) == DEFAULT_PAGE_META_FIELDS.union({"parent"}) + assert content["id"] == 4 + assert content["title"] == "Page" @pytest.mark.django_db @@ -85,11 +88,11 @@ def test_wagtail_bakery_pages_api_listing_view(): # Check build path build_path = view.get_build_path(0) - assert build_path == 'api/pages/0.json' + assert build_path == "api/pages/0.json" # Check build path for second page build_path = view.get_build_path(1) - assert build_path == 'api/pages/1.json' + assert build_path == "api/pages/1.json" @pytest.mark.django_db @@ -98,7 +101,7 @@ def test_wagtail_bakery_pages_api_listing_view_build_path_for_multisite(multisit # Check build path for homepage build_path = view.get_build_path(0) - assert build_path == 'api/pages/0.json' + assert build_path == "api/pages/0.json" @pytest.mark.django_db @@ -106,13 +109,13 @@ def test_wagtail_bakery_pages_api_listing_view_content(page_tree): view = PagesAPIListingView() # Check content - content = json.loads(view.get_content(0)[0].decode('UTF-8')) - assert set(content.keys()) == {'meta', 'items'} - assert set(content['meta'].keys()) == {'total_count'} + content = json.loads(view.get_content(0)[0].decode("UTF-8")) + assert set(content.keys()) == {"meta", "items"} + assert set(content["meta"].keys()) == {"total_count"} - page = content['items'][0] + page = content["items"][0] assert set(page.keys()) == DEFAULT_PAGE_FIELDS - assert set(page['meta'].keys()) == DEFAULT_PAGE_META_FIELDS + assert set(page["meta"].keys()) == DEFAULT_PAGE_META_FIELDS @pytest.mark.django_db @@ -121,37 +124,41 @@ def test_wagtail_bakery_typed_pages_api_listing_view(): # Check build path build_path = view.get_build_path(EventPage, 0) - assert build_path == 'api/pages/tests_EventPage/0.json' + assert build_path == "api/pages/tests_EventPage/0.json" # Check build path for second page build_path = view.get_build_path(EventPage, 1) - assert build_path == 'api/pages/tests_EventPage/1.json' + assert build_path == "api/pages/tests_EventPage/1.json" @pytest.mark.django_db -def test_wagtail_bakery_typed_pages_api_listing_view_build_path_for_multisite(multisite): +def test_wagtail_bakery_typed_pages_api_listing_view_build_path_for_multisite( + multisite, +): view = TypedPagesAPIListingView() # Check build path for homepage build_path = view.get_build_path(EventPage, 0) - assert build_path == 'api/pages/tests_EventPage/0.json' + assert build_path == "api/pages/tests_EventPage/0.json" @pytest.mark.django_db def test_wagtail_bakery_typed_pages_api_listing_view_content(page_tree): view = TypedPagesAPIListingView() - page_tree.add_child(instance=EventPage( - title="Test event page", - slug="test-event-page", - )) + page_tree.add_child( + instance=EventPage( + title="Test event page", + slug="test-event-page", + ) + ) # Check content - content = json.loads(view.get_content(EventPage, 0)[0].decode('UTF-8')) - assert set(content.keys()) == {'meta', 'items'} - assert set(content['meta'].keys()) == {'total_count'} + content = json.loads(view.get_content(EventPage, 0)[0].decode("UTF-8")) + assert set(content.keys()) == {"meta", "items"} + assert set(content["meta"].keys()) == {"total_count"} - page = content['items'][0] + page = content["items"][0] assert set(page.keys()) == DEFAULT_PAGE_FIELDS - assert set(page['meta'].keys()) == DEFAULT_PAGE_META_FIELDS - assert page['title'] == "Test event page" + assert set(page["meta"].keys()) == DEFAULT_PAGE_META_FIELDS + assert page["title"] == "Test event page" diff --git a/tests/integration/test_models.py b/tests/integration/test_models.py index e0c47a3..e11eb74 100644 --- a/tests/integration/test_models.py +++ b/tests/integration/test_models.py @@ -4,13 +4,13 @@ @pytest.mark.django_db def test_page(page): # Check if local url is returned when a single site is used - assert page.url == '/' + assert page.url == "/" @pytest.mark.django_db def test_site(site): assert site.is_default_site - assert site.hostname == 'localhost' + assert site.hostname == "localhost" @pytest.mark.django_db @@ -19,16 +19,16 @@ def test_page_tree(page_tree): assert children.count() == 4 # Check if children of homepage return correct urls - assert children[0].url == '/first/' - assert children[1].url == '/second/' - assert children[2].url == '/third/' - assert children[3].url == '/unicode-children/' + assert children[0].url == "/first/" + assert children[1].url == "/second/" + assert children[2].url == "/third/" + assert children[3].url == "/unicode-children/" # Check if children of first child return correct urls grandchildren = children[0].get_children() assert grandchildren.count() == 2 - assert grandchildren[0].url == '/first/first/' - assert grandchildren[1].url == '/first/second/' + assert grandchildren[0].url == "/first/first/" + assert grandchildren[1].url == "/first/second/" # Check if children of unicode-children return correct urls grandchildren = children[3].get_children() @@ -37,10 +37,13 @@ def test_page_tree(page_tree): # Unicode characters are URL-encoded with `url` but not `url_path`. # However manual testing shows that the page is indeed accessible with the non-encoded string. assert grandchildren[0].url_path == ( - '/home/unicode-children/latin-capital-letter-i-with-diaeresis-Ï/' + "/home/unicode-children/latin-capital-letter-i-with-diaeresis-Ï/" ) - assert grandchildren[1].url_path == '/home/unicode-children/cyrillic-capital-letter-ya-Я/' - assert grandchildren[2].url_path == '/home/unicode-children/cjk-fire-火/' + assert ( + grandchildren[1].url_path + == "/home/unicode-children/cyrillic-capital-letter-ya-Я/" + ) + assert grandchildren[2].url_path == "/home/unicode-children/cjk-fire-火/" @pytest.mark.django_db @@ -48,7 +51,7 @@ def test_multisite(multisite): assert len(multisite) == 4 # Check if full urls are returned when multiple sites are used - assert multisite[0].root_page.url == 'http://localhost/' - assert multisite[1].root_page.url == 'http://site_2/' - assert multisite[2].root_page.url == 'http://site_3/' - assert multisite[3].root_page.url == 'http://site_4/' + assert multisite[0].root_page.url == "http://localhost/" + assert multisite[1].root_page.url == "http://site_2/" + assert multisite[2].root_page.url == "http://site_3/" + assert multisite[3].root_page.url == "http://site_4/" diff --git a/tests/integration/test_redirect.py b/tests/integration/test_redirect.py index 895a200..411b483 100644 --- a/tests/integration/test_redirect.py +++ b/tests/integration/test_redirect.py @@ -11,9 +11,9 @@ def test_page_has_redirect_response(site): redirect_page = RedirectPage.objects.create( depth=1, - path='0002', - title='Page', - slug='page', + path="0002", + title="Page", + slug="page", ) # Make redirect page the root page @@ -21,9 +21,12 @@ def test_page_has_redirect_response(site): site.save() # Build static files - management.call_command('build', '--skip-static', '--skip-media') - assert os.path.exists(os.path.join(settings.BUILD_DIR, 'index.html')) + management.call_command("build", "--skip-static", "--skip-media") + assert os.path.exists(os.path.join(settings.BUILD_DIR, "index.html")) # Check if meta tag is present - content = open(os.path.join(settings.BUILD_DIR, 'index.html')).read() - assert '' in content # noqa + content = open(os.path.join(settings.BUILD_DIR, "index.html")).read() + assert ( + '' + in content + ) # noqa diff --git a/tests/integration/test_routablepagemixin.py b/tests/integration/test_routablepagemixin.py index ce4c4a6..53a8eb4 100644 --- a/tests/integration/test_routablepagemixin.py +++ b/tests/integration/test_routablepagemixin.py @@ -11,9 +11,9 @@ def test_page_has_multiple_routes(site): redirect_page = EventPage.objects.create( depth=1, - path='0002', - title='Page', - slug='page', + path="0002", + title="Page", + slug="page", ) # Make event page the root page @@ -21,5 +21,7 @@ def test_page_has_multiple_routes(site): site.save() # Build static files - management.call_command('build', '--skip-static', '--skip-media') - assert os.path.exists(os.path.join(settings.BUILD_DIR, 'eventpage', 'page', 'index.html')) + management.call_command("build", "--skip-static", "--skip-media") + assert os.path.exists( + os.path.join(settings.BUILD_DIR, "eventpage", "page", "index.html") + ) diff --git a/tests/integration/test_views.py b/tests/integration/test_views.py index e6b341c..449d4df 100644 --- a/tests/integration/test_views.py +++ b/tests/integration/test_views.py @@ -10,17 +10,17 @@ def test_wagtail_bakery_view_get_url(page_tree): # Check url for homepage url = view.get_url(page_tree) - assert url == '/' + assert url == "/" # Check child url for first child page child_page = page_tree.get_descendants().first() url = view.get_url(child_page) - assert url == '/first/' + assert url == "/first/" # Check child url of the first grandchild page child_page = child_page.get_descendants().first() url = view.get_url(child_page) - assert url == '/first/first/' + assert url == "/first/first/" @pytest.mark.django_db @@ -29,38 +29,39 @@ def test_wagtail_bakery_view_build_path(page_tree): # Check build path for homepage build_path = view.get_build_path(page_tree) - assert build_path == settings.BUILD_DIR + '/index.html' + assert build_path == settings.BUILD_DIR + "/index.html" # Check build path for first child page child_page = page_tree.get_children().first() build_path = view.get_build_path(child_page) - assert build_path == settings.BUILD_DIR + '/first/index.html' + assert build_path == settings.BUILD_DIR + "/first/index.html" # Check build path of the first grandchild page grandchild_page = child_page.get_children().first() build_path = view.get_build_path(grandchild_page) - assert build_path == settings.BUILD_DIR + '/first/first/index.html' + assert build_path == settings.BUILD_DIR + "/first/first/index.html" # Check build path for unicode-children child page child_page = page_tree.get_children().last() build_path = view.get_build_path(child_page) - assert build_path == settings.BUILD_DIR + '/unicode-children/index.html' + assert build_path == settings.BUILD_DIR + "/unicode-children/index.html" # Check build path for unicode-children grandchild pages grandchild_pages = child_page.get_children().all() build_path = view.get_build_path(grandchild_pages[0]) assert build_path == ( - settings.BUILD_DIR + '/unicode-children/latin-capital-letter-i-with-diaeresis-Ï/index.html' + settings.BUILD_DIR + + "/unicode-children/latin-capital-letter-i-with-diaeresis-Ï/index.html" ) build_path = view.get_build_path(grandchild_pages[1]) assert build_path == ( - settings.BUILD_DIR + '/unicode-children/cyrillic-capital-letter-ya-Я/index.html' + settings.BUILD_DIR + "/unicode-children/cyrillic-capital-letter-ya-Я/index.html" ) build_path = view.get_build_path(grandchild_pages[2]) - assert build_path == settings.BUILD_DIR + '/unicode-children/cjk-fire-火/index.html' + assert build_path == settings.BUILD_DIR + "/unicode-children/cjk-fire-火/index.html" @pytest.mark.django_db @@ -71,8 +72,7 @@ def test_wagtail_bakery_view_build_path_for_multisite(multisite): # Check build path for homepage build_path = view.get_build_path(page) - assert build_path == '%s/index.html' % ( - settings.BUILD_DIR) + assert build_path == "%s/index.html" % (settings.BUILD_DIR) @pytest.mark.django_db diff --git a/tests/migrations/0001_initial.py b/tests/migrations/0001_initial.py index 371d6e0..53adab7 100644 --- a/tests/migrations/0001_initial.py +++ b/tests/migrations/0001_initial.py @@ -9,18 +9,28 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('wagtailcore', '0032_add_bulk_delete_page_permission'), + ("wagtailcore", "0032_add_bulk_delete_page_permission"), ] operations = [ migrations.CreateModel( - name='RedirectPage', + name="RedirectPage", fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')), + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.Page", + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, - bases=('wagtailcore.page',), + bases=("wagtailcore.page",), ), ] diff --git a/tests/migrations/0002_eventpage.py b/tests/migrations/0002_eventpage.py index 8509c4e..449b389 100644 --- a/tests/migrations/0002_eventpage.py +++ b/tests/migrations/0002_eventpage.py @@ -8,19 +8,29 @@ class Migration(migrations.Migration): dependencies = [ - ('wagtailcore', '0032_add_bulk_delete_page_permission'), - ('tests', '0001_initial'), + ("wagtailcore", "0032_add_bulk_delete_page_permission"), + ("tests", "0001_initial"), ] operations = [ migrations.CreateModel( - name='EventPage', + name="EventPage", fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')), + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.Page", + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, - bases=(RoutablePageMixin, 'wagtailcore.page'), + bases=(RoutablePageMixin, "wagtailcore.page"), ), ] diff --git a/tests/models.py b/tests/models.py index 6e104b1..a28ef19 100644 --- a/tests/models.py +++ b/tests/models.py @@ -5,27 +5,35 @@ class RedirectPage(Page): def serve(self, request, *args, **kwargs): - return redirect('http://www.example.com/') + return redirect("http://www.example.com/") class EventPage(RoutablePageMixin, Page): - @route(r'^$') + @route(r"^$") def current_events(self, request): - return render(request, 'current_events.html', { - 'page': self, - }) + return render( + request, + "current_events.html", + { + "page": self, + }, + ) - @route(r'^year/(\d+)/$') - @route(r'^year/current/$') + @route(r"^year/(\d+)/$") + @route(r"^year/current/$") def events_for_year(self, request, year=None): - return render(request, 'events_for_year.html', { - 'page': self, - 'year': year, - }) + return render( + request, + "events_for_year.html", + { + "page": self, + "year": year, + }, + ) def get_static_site_paths(self): return super().get_static_site_paths() @property def url(self): - return '/eventpage/{}'.format(self.slug) + return "/eventpage/{}".format(self.slug) diff --git a/tests/urls.py b/tests/urls.py index 886b9ca..1b41f46 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -3,6 +3,6 @@ from wagtail.admin import urls as wagtailadmin_urls urlpatterns = [ - path('admin/', include(wagtailadmin_urls)), - path('', include(wagtail_urls)), + path("admin/", include(wagtailadmin_urls)), + path("", include(wagtail_urls)), ] diff --git a/tox.ini b/tox.ini index 15b1130..e8b361b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,39 +1,33 @@ -[variables] -linting_folders=src/wagtailbakery/ tests/ examples/ - [tox] +# Necessary because we are using pyproject.toml +isolated_build = true envlist= - py{38,39,310,311}-django42-wagtail52 # Wagtail 5.2 LTS - all supported Pythons - supported Django - py{310,311,312}-django50-wagtail52 # Wagtail 5.2 LTS - All supported Pythons for Django 5.0 - py{38,39,310,311,312}-django42-wagtail{60,61} # Wagtail 6.0 and 6.1 - all supported Python - supported Django - py{310,311,312}-django50-wagtail{60,61} # Wagtail 6.0 and 6.1 - all supported Python - supported Django + py{39,310,311}-django42-wagtail52 # Wagtail 5.2 LTS - all supported Pythons - supported Django + py{310,311,312}-django50-wagtail52 # Wagtail 5.2 LTS - all supported Pythons for Django 5.0 + py{310,311,312}-django50-wagtail62 # Wagtail 6.2 - all supported Python - all supported Pythons for Django 5.0 + py{310,311,312,313}-django51-wagtail63 # Wagtail 6.3 - all supported Python - all supported Pythons for Django 5.1 wagtailmain # Wagtail main latest compatible version [testenv] -commands=py.test --cov=wagtailbakery --cov-report=xml {posargs} +commands=pytest --cov=wagtailbakery {posargs} +setenv= + COVERAGE_FILE=.coverage.{envname} + COVERAGE_CORE=sysmon deps= django42: django>=4.2,<5 django50: django>=5.0,<5.1 - wagtail52: wagtail>=5.2,<5.3 # Current LTS - wagtail60: wagtail>=6.0,<6.1 - wagtail61: wagtail>=6.1,<6.2 + django51: django>=5.1,<5.2 + wagtail52: wagtail>=5.2,<5.3 # Old LTS + wagtail62: wagtail>=6.2,<6.3 + wagtail63: wagtail>=6.3,<6.4 # Current LTS extras=test [testenv:wagtailmain] -commands=py.test --cov=wagtailbakery --cov-report=xml {posargs} +commands=pytest --cov=wagtailbakery {posargs} +setenv= + COVERAGE_FILE=.coverage.{envname} + COVERAGE_CORE=sysmon deps= - django>=5.0,<5.1 + django>=5.1,<5.2 git+https://github.com/wagtail/wagtail.git@main#egg=Wagtail extras=test - -[testenv:flake8] -commands=flake8 {[variables]linting_folders} -basepython=python3.8 -skip_install=true -deps=flake8 - -[testenv:isort] -commands=isort --check-only --diff {[variables]linting_folders} -basepython=python3.8 -skip_install=true -deps=isort