From a7947e70ed710ce41b10b81c360f4a34fdbd7151 Mon Sep 17 00:00:00 2001 From: Joshua Munn Date: Fri, 11 Aug 2023 13:08:40 +0100 Subject: [PATCH 01/25] Install ruff, apply fixes --- docs/conf.py | 4 +--- ruff.toml | 25 +++++++++++++++++++++++++ setup.py | 5 +---- src/wagtail_factories/blocks.py | 6 +++--- src/wagtail_factories/builder.py | 4 ++-- src/wagtail_factories/factories.py | 6 ++---- tests/settings.py | 2 +- tests/test_blocks.py | 2 +- tests/test_factories.py | 2 +- tests/test_stream_block.py | 12 ++++++------ tests/testapp/factories.py | 1 - tests/testapp/stream_block_factories.py | 1 - 12 files changed, 43 insertions(+), 27 deletions(-) create mode 100644 ruff.toml diff --git a/docs/conf.py b/docs/conf.py index 815b1bf..8ce572d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # wagtail-factories documentation build configuration file, created by # sphinx-quickstart on Sat Aug 27 19:39:59 2016. @@ -20,7 +19,6 @@ # import os # import sys # sys.path.insert(0, os.path.abspath('.')) -import pkg_resources # -- General configuration ------------------------------------------------ @@ -51,7 +49,7 @@ # General information about the project. project = 'wagtail-factories' -copyright = u'2016, Michael van Tellingen' +copyright = '2016, Michael van Tellingen' author = 'Michael van Tellingen' # The version info for the project you're documenting, acts as replacement for diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..8a4aacc --- /dev/null +++ b/ruff.toml @@ -0,0 +1,25 @@ +# E501: Line too long +# S101: flake8-bandit Use of assert detected +ignore = ["E501","B028", "S101"] + +exclude = ["dist","build","venv",".venv",".tox",".git"] +line-length = 88 + +select = [ + "E", # pycodestyle errors + "F", # pyflakes + "I", # isort + "B", # flake8-bugbear + "BLE", # flake8-blind-except + "C4", # flake8-comprehensions + "S", # flake8-bandit + "T10", # flake8-debugger + "T20", # flake8-print + "UP", # pyupgrade + "W", # pycodestyle warnings +] + +target-version = "py38" + +[isort] +known-first-party = ["src"] \ No newline at end of file diff --git a/setup.py b/setup.py index 858d2dc..3b85500 100755 --- a/setup.py +++ b/setup.py @@ -18,10 +18,7 @@ "pytest-pythonpath==0.7.3", "psycopg2>=2.3.1", "coverage==6.0", - "isort==5.10.0", - "flake8==4.0.0", - "flake8-blind-except==0.1.1", - "flake8-debugger==4.1.2", + "ruff==0.0.284", ] with open("README.rst") as fh: diff --git a/src/wagtail_factories/blocks.py b/src/wagtail_factories/blocks.py index bfc00e8..e36d9ff 100644 --- a/src/wagtail_factories/blocks.py +++ b/src/wagtail_factories/blocks.py @@ -35,9 +35,9 @@ class StreamBlockFactory(factory.Factory): def _generate(cls, strategy, params): if cls._meta.abstract and not hasattr(cls, "__generate_abstract__"): raise factory.errors.FactoryError( - "Cannot generate instances of abstract factory %(f)s; " - "Ensure %(f)s.Meta.model is set and %(f)s.Meta.abstract " - "is either not set or False." % dict(f=cls.__name__) + "Cannot generate instances of abstract factory {f}; " + "Ensure {f}.Meta.model is set and {f}.Meta.abstract " + "is either not set or False.".format(**{"f": cls.__name__}) ) step = cls._builder_class(cls._meta, params, strategy) return step.build() diff --git a/src/wagtail_factories/builder.py b/src/wagtail_factories/builder.py index debd307..2114774 100644 --- a/src/wagtail_factories/builder.py +++ b/src/wagtail_factories/builder.py @@ -77,12 +77,12 @@ def get_block_declarations(self, factory_meta, extras): try: i, name, *params = k.split("__", maxsplit=2) key = int(i) - except (ValueError, TypeError): + except (ValueError, TypeError) as err: raise InvalidDeclaration( "StreamFieldFactory declarations must be of the form " "=, __=value or " f"____=value, got: {k}" - ) + ) from err if key in indexed_block_names and indexed_block_names[key] != name: raise DuplicateDeclaration( f"Multiple declarations for index {key} at this level of nesting " diff --git a/src/wagtail_factories/factories.py b/src/wagtail_factories/factories.py index 7958915..7b8e681 100644 --- a/src/wagtail_factories/factories.py +++ b/src/wagtail_factories/factories.py @@ -76,8 +76,7 @@ def _get_or_create(cls, model_class, *args, **kwargs): manager = cls._get_manager(model_class) assert "defaults" not in cls._meta.django_get_or_create, ( "'defaults' is a reserved keyword for get_or_create " - "(in %s._meta.django_get_or_create=%r)" - % (cls, cls._meta.django_get_or_create) + f"(in {cls}._meta.django_get_or_create={cls._meta.django_get_or_create!r})" ) lookup_fields = {} @@ -85,8 +84,7 @@ def _get_or_create(cls, model_class, *args, **kwargs): if field not in kwargs: raise errors.FactoryError( "django_get_or_create - " - "Unable to find initialization value for '%s' in factory %s" - % (field, cls.__name__) + "Unable to find initialization value for '{}' in factory {}".format(field, cls.__name__) ) lookup_fields[field] = kwargs[field] diff --git a/tests/settings.py b/tests/settings.py index 9b38a44..a4d100d 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -7,7 +7,7 @@ ROOT_URLCONF = "tests.urls" -SECRET_KEY = "Gx8sMKAtnA69TR9lyAlLuSnozUv3kxdscHkpwEjatZRVQQ0laMY69KL4XPxvr3KY" +SECRET_KEY = "Gx8sMKAtnA69TR9lyAlLuSnozUv3kxdscHkpwEjatZRVQQ0laMY69KL4XPxvr3KY" # NOQA DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}} diff --git a/tests/test_blocks.py b/tests/test_blocks.py index 9cd03bd..0572637 100644 --- a/tests/test_blocks.py +++ b/tests/test_blocks.py @@ -1,12 +1,12 @@ from collections import OrderedDict import pytest +import wagtail_factories from wagtail.blocks import StructValue from wagtail.documents.models import Document from wagtail.images.models import Image from wagtail.models import Page -import wagtail_factories from tests.testapp.factories import ( MyBlockFactory, MyBlockItemFactory, diff --git a/tests/test_factories.py b/tests/test_factories.py index 237ee50..43cc164 100644 --- a/tests/test_factories.py +++ b/tests/test_factories.py @@ -1,8 +1,8 @@ import pytest +import wagtail_factories from wagtail import blocks from wagtail.models import Page, Site -import wagtail_factories from tests.testapp.factories import MyTestPageFactory, MyTestPageGetOrCreateFactory diff --git a/tests/test_stream_block.py b/tests/test_stream_block.py index b257032..20f3aa5 100644 --- a/tests/test_stream_block.py +++ b/tests/test_stream_block.py @@ -1,7 +1,12 @@ +import wagtail_factories from django.test import TestCase from wagtail import blocks +from wagtail_factories.builder import ( + DuplicateDeclaration, + InvalidDeclaration, + UnknownChildBlockFactory, +) -import wagtail_factories from tests.testapp.stream_block_factories import ( PageWithNestedStreamBlockFactory, PageWithSimpleStructBlockNestedDeepDefaultsFactory, @@ -11,11 +16,6 @@ PageWithStreamBlockInListBlockFactory, PageWithStreamBlockInStructBlockFactory, ) -from wagtail_factories.builder import ( - DuplicateDeclaration, - InvalidDeclaration, - UnknownChildBlockFactory, -) class PageTreeTestCase(TestCase): diff --git a/tests/testapp/factories.py b/tests/testapp/factories.py index 91ba935..17f1cf2 100644 --- a/tests/testapp/factories.py +++ b/tests/testapp/factories.py @@ -1,5 +1,4 @@ import factory - import wagtail_factories from . import models diff --git a/tests/testapp/stream_block_factories.py b/tests/testapp/stream_block_factories.py index ba7610e..aaa4ed2 100644 --- a/tests/testapp/stream_block_factories.py +++ b/tests/testapp/stream_block_factories.py @@ -1,5 +1,4 @@ import factory - import wagtail_factories from . import models From af3b6260c7d36b6d8157f1fabe3c36a8af27997d Mon Sep 17 00:00:00 2001 From: Joshua Munn Date: Fri, 11 Aug 2023 13:12:12 +0100 Subject: [PATCH 02/25] Add flake8-pytest-style ruff rule and apply fixes --- ruff.toml | 1 + tests/conftest.py | 2 +- tests/test_blocks.py | 20 ++++++------ tests/test_factories.py | 30 ++++++++--------- tests/test_stream_block.py | 67 +++++++++++++++++--------------------- 5 files changed, 57 insertions(+), 63 deletions(-) diff --git a/ruff.toml b/ruff.toml index 8a4aacc..9191897 100644 --- a/ruff.toml +++ b/ruff.toml @@ -12,6 +12,7 @@ select = [ "B", # flake8-bugbear "BLE", # flake8-blind-except "C4", # flake8-comprehensions + "PT", # flake8-pytest-style "S", # flake8-bandit "T10", # flake8-debugger "T20", # flake8-print diff --git a/tests/conftest.py b/tests/conftest.py index ec825a6..da4229d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,6 +11,6 @@ def pytest_configure(): @pytest.fixture(scope="session", autouse=True) -def custom_settings(tmpdir_factory): +def _custom_settings(tmpdir_factory): overrides = override_settings(MEDIA_ROOT=str(tmpdir_factory.mktemp("media"))) overrides.enable() diff --git a/tests/test_blocks.py b/tests/test_blocks.py index 0572637..6d5163a 100644 --- a/tests/test_blocks.py +++ b/tests/test_blocks.py @@ -25,7 +25,7 @@ def eq_list_block_values(p, q): return all(map(lambda x, y: x.value == y.value, p.bound_blocks, q.bound_blocks)) -@pytest.mark.django_db +@pytest.mark.django_db() def test_list_block_factory(): computed = MyBlockFactory( items__0__label="label-1", @@ -59,7 +59,7 @@ def test_list_block_factory(): assert eq_list_block_values(computed["items"], expected["items"]) -@pytest.mark.django_db +@pytest.mark.django_db() def test_block_factory(): computed = MyBlockFactory(image__image__title="blub") expected = MyBlockFactory._meta.model().clean( @@ -101,7 +101,7 @@ def test_block_factory_build(): assert computed == expected -@pytest.mark.django_db +@pytest.mark.django_db() def test_block_factory_subkwarg(): computed = MyBlockFactory(item__label="my-label", item__value=20, image__image=None) expected = MyBlockFactory._meta.model().clean( @@ -119,7 +119,7 @@ def test_block_factory_subkwarg(): assert computed == expected -@pytest.mark.django_db +@pytest.mark.django_db() def test_custom_page_streamfield_data_complex(): assert Image.objects.count() == 0 @@ -184,7 +184,7 @@ def test_custom_page_streamfield_data_complex(): assert "block-image" in content -@pytest.mark.django_db +@pytest.mark.django_db() def test_custom_page_streamfield_default_blocks(): assert Image.objects.count() == 0 @@ -207,7 +207,7 @@ def test_custom_page_streamfield_default_blocks(): assert content.count("block-image") == 2 -@pytest.mark.django_db +@pytest.mark.django_db() def test_page_chooser_block(): value = wagtail_factories.PageChooserBlockFactory() page = Page.objects.last() @@ -215,7 +215,7 @@ def test_page_chooser_block(): assert value == page -@pytest.mark.django_db +@pytest.mark.django_db() def test_image_chooser_block(): value = wagtail_factories.ImageChooserBlockFactory() image = Image.objects.last() @@ -223,7 +223,7 @@ def test_image_chooser_block(): assert value == image -@pytest.mark.django_db +@pytest.mark.django_db() def test_document_chooser_block(): value = wagtail_factories.DocumentChooserBlockFactory() document = Document.objects.last() @@ -231,9 +231,9 @@ def test_document_chooser_block(): assert value == document -@pytest.mark.django_db +@pytest.mark.django_db() @pytest.mark.parametrize( - "Model, ModelChooserBlockFactory", + ("Model", "ModelChooserBlockFactory"), [ (Page, wagtail_factories.PageChooserBlockFactory), (Image, wagtail_factories.ImageChooserBlockFactory), diff --git a/tests/test_factories.py b/tests/test_factories.py index 43cc164..3101d08 100644 --- a/tests/test_factories.py +++ b/tests/test_factories.py @@ -6,28 +6,28 @@ from tests.testapp.factories import MyTestPageFactory, MyTestPageGetOrCreateFactory -@pytest.mark.django_db +@pytest.mark.django_db() def test_page_no_args_or_kwargs(): page = wagtail_factories.PageFactory(parent=None) assert page.title == "Test page" assert page.slug == "test-page" -@pytest.mark.django_db +@pytest.mark.django_db() def test_page_build_no_args_or_kwargs(): page = wagtail_factories.PageFactory.build(parent=None) assert page.title == "Test page" assert page.slug == "test-page" -@pytest.mark.django_db +@pytest.mark.django_db() def test_page_build_no_parent(): page = wagtail_factories.PageFactory.build() assert page.title == "Test page" assert page.slug == "test-page" -@pytest.mark.django_db +@pytest.mark.django_db() def test_page_multiple_roots(): # Make sure the default root pages are removed created by wagtail # migrations @@ -40,7 +40,7 @@ def test_page_multiple_roots(): assert Page.get_root_nodes().count() == 3 -@pytest.mark.django_db +@pytest.mark.django_db() def test_page_multiple_nested(): root = wagtail_factories.PageFactory(parent=None) page_1 = wagtail_factories.PageFactory(parent=root, slug="page-1") @@ -59,7 +59,7 @@ def test_page_multiple_nested(): assert len(page_2.get_children()) == 4 -@pytest.mark.django_db +@pytest.mark.django_db() def test_page_multiple_nested_structure_at_once(): Page.objects.all().delete() @@ -81,7 +81,7 @@ def test_page_multiple_nested_structure_at_once(): assert page.get_parent().get_parent().get_parent() is None -@pytest.mark.django_db +@pytest.mark.django_db() def test_custom_page_streamfield(): root_page = wagtail_factories.PageFactory(parent=None) page = MyTestPageFactory(parent=root_page) @@ -89,7 +89,7 @@ def test_custom_page_streamfield(): assert len(page.body) == 0 -@pytest.mark.django_db +@pytest.mark.django_db() def test_custom_page_streamfield_data(): root_page = wagtail_factories.PageFactory(parent=None) values = ["bla-1", "bla-2"] @@ -109,13 +109,13 @@ def test_custom_page_streamfield_data(): assert list(page.body[0].value) == values -@pytest.mark.django_db +@pytest.mark.django_db() def test_site_no_args_or_kwargs(): site = wagtail_factories.SiteFactory() assert site.root_page.depth == 1 -@pytest.mark.django_db +@pytest.mark.django_db() def test_site_multiple_no_args_or_kwargs(): Site.objects.all().delete() @@ -126,13 +126,13 @@ def test_site_multiple_no_args_or_kwargs(): assert Site.objects.count() == 4 -@pytest.mark.django_db +@pytest.mark.django_db() def test_image_no_args_or_kwargs(): image = wagtail_factories.ImageFactory() assert image.collection.name == "Test collection" -@pytest.mark.django_db +@pytest.mark.django_db() def test_image_add_to_collection(): root_collection = wagtail_factories.CollectionFactory(parent=None) @@ -142,7 +142,7 @@ def test_image_add_to_collection(): assert image.collection.name == "new" -@pytest.mark.django_db +@pytest.mark.django_db() def test_get_or_create(): root_page = wagtail_factories.PageFactory(parent=None) page_1 = MyTestPageGetOrCreateFactory( @@ -155,7 +155,7 @@ def test_get_or_create(): assert page_1.pk == page_2.pk -@pytest.mark.django_db +@pytest.mark.django_db() def test_get_or_create_with_root(): root_page = wagtail_factories.PageFactory(parent=None) page_1 = MyTestPageGetOrCreateFactory(slug="foobar", parent=root_page) @@ -164,7 +164,7 @@ def test_get_or_create_with_root(): assert page_1.pk == page_2.pk -@pytest.mark.django_db +@pytest.mark.django_db() def test_document_add_to_collection(): root_collection = wagtail_factories.CollectionFactory(parent=None) document = wagtail_factories.DocumentFactory( diff --git a/tests/test_stream_block.py b/tests/test_stream_block.py index 20f3aa5..ebbcc19 100644 --- a/tests/test_stream_block.py +++ b/tests/test_stream_block.py @@ -30,44 +30,44 @@ def test_page_with_stream_block(self): parent=self.root_page, body__0__struct_block__title="foo", ) - self.assertEqual(page.body[0].block_type, "struct_block") - self.assertEqual(page.body[0].value["title"], "foo") + assert page.body[0].block_type == "struct_block" + assert page.body[0].value["title"] == "foo" def test_stream_block_with_struct_block_lazy_attrs(self): page = PageWithStreamBlockFactory( parent=self.root_page, body__0="struct_block", ) - self.assertEqual(page.body[0].block_type, "struct_block") - self.assertEqual(page.body[0].value["title"], "lazy function foobar") + assert page.body[0].block_type == "struct_block" + assert page.body[0].value["title"] == "lazy function foobar" def test_page_with_stream_block_default_value(self): page = PageWithStreamBlockFactory( parent=self.root_page, body__0="struct_block", ) - self.assertEqual(page.body[0].value["title"], "lazy function foobar") + assert page.body[0].value["title"] == "lazy function foobar" def test_page_with_nested_stream_block(self): page = PageWithNestedStreamBlockFactory( parent=self.root_page, body__0__inner_stream__0__struct_block__title="foo", ) - self.assertEqual(page.body[0].value[0].value["title"], "foo") + assert page.body[0].value[0].value["title"] == "foo" def test_page_with_nested_stream_block_default_value(self): page = PageWithNestedStreamBlockFactory( parent=self.root_page, body__0__inner_stream__0="struct_block", ) - self.assertEqual(page.body[0].value[0].value["title"], "lazy function foobar") + assert page.body[0].value[0].value["title"] == "lazy function foobar" def test_page_with_deeply_nested_stream_block(self): page = PageWithStreamBlockInStructBlockFactory( parent=self.root_page, body__0__struct_block__inner_stream__0__struct_block__title="foo", ) - self.assertEqual(page.body[0].value["inner_stream"][0].value["title"], "foo") + assert page.body[0].value["inner_stream"][0].value["title"] == "foo" def test_page_with_deeply_nested_stream_block_defaults(self): page = PageWithStreamBlockInStructBlockFactory( @@ -76,34 +76,27 @@ def test_page_with_deeply_nested_stream_block_defaults(self): body__1__struct_block__inner_stream__0="struct_block", ) - self.assertEqual(page.body[0].value["inner_stream"][0].block_type, "char_block") + assert page.body[0].value["inner_stream"][0].block_type == "char_block" # Default value for the CharBlock is None - self.assertIsNone(page.body[0].value["inner_stream"][0].value) + assert page.body[0].value["inner_stream"][0].value is None - self.assertEqual( - page.body[1].value["inner_stream"][0].block_type, "struct_block" - ) - self.assertEqual( - page.body[1].value["inner_stream"][0].value["title"], "lazy function foobar" - ) + assert page.body[1].value["inner_stream"][0].block_type == "struct_block" + assert page.body[1].value["inner_stream"][0].value["title"] == "lazy function foobar" def test_page_with_deeply_nested_stream_block_in_list_block(self): page = PageWithStreamBlockInListBlockFactory( parent=self.root_page, body__0__list_block__0__0__struct_block__title="foo", ) - self.assertEqual(page.body[0].value[0][0].value["title"], "foo") + assert page.body[0].value[0][0].value["title"] == "foo" def test_computed_values_on_struct_block_in_nested_stream(self): page = PageWithSimpleStructBlockNestedFactory( body__0__inner_stream__0="simple_struct_block" ) - self.assertEqual(page.body[0].value[0].value["boolean"], True) - self.assertEqual(page.body[0].value[0].value["text"][:4], "True") - self.assertEqual( - page.body[0].value[0].value["text"][4:], - str(page.body[0].value[0].value["number"]), - ) + assert page.body[0].value[0].value["boolean"] is True + assert page.body[0].value[0].value["text"][:4] == "True" + assert page.body[0].value[0].value["text"][4:] == str(page.body[0].value[0].value["number"]) def test_factory_with_anonymous_stream_block_in_tree(self): # The inner_stream child block is defined as an "anonymous" StreamBlock (i.e. declared @@ -116,26 +109,26 @@ def test_factory_with_anonymous_stream_block_in_tree(self): body__0__inner_stream__0__simple_struct_block__number=number, body__0__inner_stream__0__simple_struct_block__boolean=boolean, ) - self.assertEqual(page.body[0].value[0].block_type, "simple_struct_block") - self.assertEqual(page.body[0].value[0].value["text"], text) - self.assertEqual(page.body[0].value[0].value["number"], number) - self.assertEqual(page.body[0].value[0].value["boolean"], boolean) + assert page.body[0].value[0].block_type == "simple_struct_block" + assert page.body[0].value[0].value["text"] == text + assert page.body[0].value[0].value["number"] == number + assert page.body[0].value[0].value["boolean"] == boolean def test_sub_factory_defaults(self): page = PageWithSimpleStructBlockNestedDefaultsFactory( body__0__inner_stream__0="simple_struct_block", ) - self.assertEqual(page.body[0].value[0].value["text"], "default text") - self.assertEqual(page.body[0].value[0].value["number"], 11) - self.assertEqual(page.body[0].value[0].value["boolean"], False) + assert page.body[0].value[0].value["text"] == "default text" + assert page.body[0].value[0].value["number"] == 11 + assert page.body[0].value[0].value["boolean"] is False def test_sub_factory_deep_defaults(self): page = PageWithSimpleStructBlockNestedDeepDefaultsFactory( body__0="inner_stream", ) - self.assertEqual(page.body[0].value[0].value["text"], "deep text") - self.assertEqual(page.body[0].value[0].value["number"], 111) - self.assertEqual(page.body[0].value[1].value["text"], "deep text 2") + assert page.body[0].value[0].value["text"] == "deep text" + assert page.body[0].value[0].value["number"] == 111 + assert page.body[0].value[1].value["text"] == "deep text 2" class EmptyStreamValueTestCase(PageTreeTestCase): @@ -144,15 +137,15 @@ class EmptyStreamValueTestCase(PageTreeTestCase): def test_page_without_stream_field_params(self): page = PageWithStreamBlockFactory(parent=self.root_page) - self.assertEqual(len(page.body), 0) - self.assertIsInstance(page.body, blocks.StreamValue) + assert len(page.body) == 0 + assert isinstance(page.body, blocks.StreamValue) def test_nested_stream_block_default_value(self): page = PageWithNestedStreamBlockFactory( parent=self.root_page, body__0="inner_stream" ) - self.assertEqual(len(page.body[0].value), 0) - self.assertIsInstance(page.body[0].value, blocks.StreamValue) + assert len(page.body[0].value) == 0 + assert isinstance(page.body[0].value, blocks.StreamValue) class StreamFieldFactoryErrorsTestCase(PageTreeTestCase): From e6fd4879010a9e2683bf839806790b4a43548538 Mon Sep 17 00:00:00 2001 From: Joshua Munn Date: Fri, 11 Aug 2023 13:13:16 +0100 Subject: [PATCH 03/25] Remove psycopg2 --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 3b85500..2d6fcaa 100755 --- a/setup.py +++ b/setup.py @@ -16,7 +16,6 @@ "pytest-django==4.5.0", "pytest-cov==3.0.0", "pytest-pythonpath==0.7.3", - "psycopg2>=2.3.1", "coverage==6.0", "ruff==0.0.284", ] From 954636c51e566ff320d39500d684435c044d1dea Mon Sep 17 00:00:00 2001 From: Joshua Munn Date: Fri, 11 Aug 2023 13:55:18 +0100 Subject: [PATCH 04/25] Drop Python 3.7 --- .github/workflows/python-tox.yml | 5 ----- setup.py | 1 - 2 files changed, 6 deletions(-) diff --git a/.github/workflows/python-tox.yml b/.github/workflows/python-tox.yml index 66684c4..b13df59 100644 --- a/.github/workflows/python-tox.yml +++ b/.github/workflows/python-tox.yml @@ -24,7 +24,6 @@ jobs: max-parallel: 4 matrix: python-version: - - {version: 3.7, tox: 37} - {version: 3.8, tox: 38} - {version: 3.9, tox: 39} - {version: '3.10', tox: 310} @@ -33,10 +32,6 @@ jobs: wagtail: [41, 42, 50] factoryboy: [32] exclude: - - python-version: {version: 3.7, tox: 37} - django: 40 - - python-version: {version: 3.7, tox: 37} - django: 41 - python-version: {version: '3.11', tox: 311} django: 40 - python-version: {version: '3.11', tox: 311} diff --git a/setup.py b/setup.py index 2d6fcaa..0f018e3 100755 --- a/setup.py +++ b/setup.py @@ -50,7 +50,6 @@ "Intended Audience :: Developers", "Operating System :: OS Independent", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", From 9b71a743edb2098fcfce6e6588657f92a74dd212 Mon Sep 17 00:00:00 2001 From: Joshua Munn Date: Fri, 11 Aug 2023 14:24:53 +0100 Subject: [PATCH 05/25] Rename README.rst -> README.md --- README.rst => README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename README.rst => README.md (100%) diff --git a/README.rst b/README.md similarity index 100% rename from README.rst rename to README.md From 9ec797f204abb35e9a31c9db9eb7616514ccd83b Mon Sep 17 00:00:00 2001 From: Joshua Munn Date: Fri, 11 Aug 2023 14:32:10 +0100 Subject: [PATCH 06/25] Convert README to markdown --- MANIFEST.in | 2 +- README.md | 314 +++++++++++++++++++++++++--------------------------- setup.py | 4 +- 3 files changed, 155 insertions(+), 165 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 0c57ed1..bc772ba 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,7 +4,7 @@ recursive-exclude * * graft src/wagtail_factories -include README.rst +include README.md include CHANGES include setup.py diff --git a/README.md b/README.md index cbe0099..b34560b 100644 --- a/README.md +++ b/README.md @@ -1,190 +1,180 @@ -================= -wagtail-factories -================= +# wagtail-factories -Factory boy classes for Wagtail CMS - -.. start-no-pypi - -Status ------- - -.. image:: https://readthedocs.org/projects/wagtail-factories/badge/?version=latest - :target: https://readthedocs.org/projects/wagtail-factories/ - -.. image:: https://github.com/wagtail/wagtail-factories/workflows/Python%20Tests/badge.svg - :target: https://github.com/wagtail/wagtail-factories/actions?query=workflow%3A%22Python+Tests%22 - -.. image:: https://img.shields.io/pypi/v/wagtail-factories.svg - :target: https://pypi.python.org/pypi/wagtail-factories/ - - -.. end-no-pypi - - - -Installation -============ - -.. code-block:: shell - - pip install wagtail-factories - - - -Usage -===== - -Documentation is still in progress, but see the `tests`_ for more examples. - -.. _tests: https://github.com/wagtail/wagtail-factories/tree/main/tests - -.. code-block:: python - - import wagtail_factories - from . import models - - - class MyCarouselItemFactory(wagtail_factories.StructBlockFactory): - label = 'my-label' - image = factory.SubFactory( - wagtail_factories.ImageChooserBlockFactory) - - class Meta: - model = models.MyBlockItem - - - class MyCarouselFactory(wagtail_factories.StructBlockFactory): - title = "Carousel title" - items = wagtail_factories.ListBlockFactory( - MyCarouselItemFactory) - - class Meta: - model = models.MyCarousel - - - class MyNewsPageFactory(wagtail_factories.PageFactory): - class Meta: - model = models.MyNewsPage +Factory boy classes for Wagtail CMS - class MyNewsPageChooserBlockFactory(wagtail_factories.PageChooserBlockFactory): - page = factory.SubFactory(MyNewsPageFactory) +## Status +[![image](https://readthedocs.org/projects/wagtail-factories/badge/?version=latest)](https://readthedocs.org/projects/wagtail-factories/) +[![image](https://github.com/wagtail/wagtail-factories/workflows/Python%20Tests/badge.svg)](https://github.com/wagtail/wagtail-factories/actions?query=workflow%3A%22Python+Tests%22) +[![image](https://img.shields.io/pypi/v/wagtail-factories.svg)](https://pypi.python.org/pypi/wagtail-factories/) - class MyTestPageFactory(wagtail_factories.PageFactory): - body = wagtail_factories.StreamFieldFactory({ - 'carousel': factory.SubFactory(MyCarouselFactory), - 'news_page': factory.SubFactory(MyNewsPageChooserBlockFactory), - }) +## Installation - class Meta: - model = models.MyTestPage +``` shell +pip install wagtail-factories +``` +## Usage - def test_my_page(): - root_page = wagtail_factories.PageFactory(parent=None) - my_page = MyTestPageFactory( - parent=root_page, - body__0__carousel__items__0__label='Slide 1', - body__0__carousel__items__0__image__image__title='Image Slide 1', - body__0__carousel__items__1__label='Slide 2', - body__0__carousel__items__1__image__image__title='Image Slide 2', - body__0__carousel__items__2__label='Slide 3', - body__0__carousel__items__2__image__image__title='Image Slide 3', - body__1__news_page__page__title="News", - ) +Documentation is still in progress, but see the +[tests](https://github.com/wagtail/wagtail-factories/tree/main/tests) +for more examples. +``` python +import wagtail_factories +from . import models -Using StreamBlockFactory -======================== -``StreamBlockFactory`` can be used in conjunction with the other block factory types to create complex, nested ``StreamValues``, much like how ``StreamBlock`` can be used to declare the blocks for a complex ``StreamField``. +class MyCarouselItemFactory(wagtail_factories.StructBlockFactory): + label = 'my-label' + image = factory.SubFactory( + wagtail_factories.ImageChooserBlockFactory) -First, define your ``StreamBlockFactory`` subclass, using ``factory.SubFactory`` to wrap child block declarations. Be sure to include your ``StreamBlock`` subclass as the model attribute on the inner ``Meta`` class. + class Meta: + model = models.MyBlockItem -.. code-block:: python - class MyStreamBlockFactory(wagtail_factories.StreamBlockFactory): - my_struct_block = factory.SubFactory(MyStructBlockFactory) +class MyCarouselFactory(wagtail_factories.StructBlockFactory): + title = "Carousel title" + items = wagtail_factories.ListBlockFactory( + MyCarouselItemFactory) - class Meta: - model = MyStreamBlock + class Meta: + model = models.MyCarousel -Then include your ``StreamBlockFactory`` subclass on a model factory as the argument to a ``StreamFieldFactory``. +class MyNewsPageFactory(wagtail_factories.PageFactory): + class Meta: + model = models.MyNewsPage -.. code-block:: python - class MyPageFactory(wagtail_factories.PageFactory): - body = wagtail_factories.StreamFieldFactory(MyStreamBlockFactory) +class MyNewsPageChooserBlockFactory(wagtail_factories.PageChooserBlockFactory): + page = factory.SubFactory(MyNewsPageFactory) - class Meta: - model = MyPage +class MyTestPageFactory(wagtail_factories.PageFactory): + body = wagtail_factories.StreamFieldFactory({ + 'carousel': factory.SubFactory(MyCarouselFactory), + 'news_page': factory.SubFactory(MyNewsPageChooserBlockFactory), + }) -You can then use a modified version of factory_boy's deep object declaration syntax to build up ``StreamValues`` on the fly. + class Meta: + model = models.MyTestPage -.. code-block:: python - MyPageFactory( - body__0__my_struct_block__some_field="some value", - body__0__my_struct_block__some_other_field="some other value", +def test_my_page(): + root_page = wagtail_factories.PageFactory(parent=None) + my_page = MyTestPageFactory( + parent=root_page, + body__0__carousel__items__0__label='Slide 1', + body__0__carousel__items__0__image__image__title='Image Slide 1', + body__0__carousel__items__1__label='Slide 2', + body__0__carousel__items__1__image__image__title='Image Slide 2', + body__0__carousel__items__2__label='Slide 3', + body__0__carousel__items__2__image__image__title='Image Slide 3', + body__1__news_page__page__title="News", + ) +``` + +### Using StreamBlockFactory + +`StreamBlockFactory` can be used in conjunction with the other block +factory types to create complex, nested `StreamValues`, much like how +`StreamBlock` can be used to declare the blocks for a complex +`StreamField`. + +First, define your `StreamBlockFactory` subclass, using +`factory.SubFactory` to wrap child block declarations. Be sure to +include your `StreamBlock` subclass as the model attribute on the inner +`Meta` class. + +``` python +class MyStreamBlockFactory(wagtail_factories.StreamBlockFactory): + my_struct_block = factory.SubFactory(MyStructBlockFactory) + + class Meta: + model = MyStreamBlock +``` + +Then include your `StreamBlockFactory` subclass on a model factory as +the argument to a `StreamFieldFactory`. + +``` python +class MyPageFactory(wagtail_factories.PageFactory): + body = wagtail_factories.StreamFieldFactory(MyStreamBlockFactory) + + class Meta: + model = MyPage +``` + +You can then use a modified version of factory\_boy\'s deep object +declaration syntax to build up `StreamValues` on the fly. + +``` python +MyPageFactory( + body__0__my_struct_block__some_field="some value", + body__0__my_struct_block__some_other_field="some other value", +) +``` + +To generate the default value for a block factory, terminate your +declaration at the index and provide the block name as the value. + +``` python +MyPageFactory(body__0="my_struct_block") +``` + +### Alternative StreamFieldFactory declaration syntax + +Prior to version 3.0, `StreamFieldFactory` could only be used by +providing a dict mapping block names to block factory classes as the +single argument, for example: + +``` python +class MyTestPageWithStreamFieldFactory(wagtail_factories.PageFactory): + body = wagtail_factories.StreamFieldFactory( + { + "char_array": wagtail_factories.ListBlockFactory( + wagtail_factories.CharBlockFactory + ), + "int_array": wagtail_factories.ListBlockFactory( + wagtail_factories.IntegerBlockFactory + ), + "struct": MyBlockFactory, + "image": wagtail_factories.ImageChooserBlockFactory, + } ) + class Meta: + model = models.MyTestPage +``` + +This style of declaration is still supported, with the caveat that +nested stream blocks are not supported for this approach. From version +3.0, all `BlockFactory` values in a `StreamFieldFactory` definition of +this style *must* be wrapped in factory\_boy `SubFactories`. For +example, the above example must be updated to the following for 3.0 +compatibility. + +``` python +class MyTestPageWithStreamFieldFactory(wagtail_factories.PageFactory): + body = wagtail_factories.StreamFieldFactory( + { + "char_array": wagtail_factories.ListBlockFactory( + wagtail_factories.CharBlockFactory + ), + "int_array": wagtail_factories.ListBlockFactory( + wagtail_factories.IntegerBlockFactory + ), + "struct": factory.SubFactory(MyBlockFactory), + "image": factory.SubFactory(wagtail_factories.ImageChooserBlockFactory), + } + ) -To generate the default value for a block factory, terminate your declaration at the index and provide the block name as the value. - -.. code-block:: python - - MyPageFactory(body__0="my_struct_block") - - -Alternative StreamFieldFactory declaration syntax -================================================= - -Prior to version 3.0, ``StreamFieldFactory`` could only be used by providing a dict mapping block names to block factory classes as the single argument, for example: - -.. code-block:: python - - class MyTestPageWithStreamFieldFactory(wagtail_factories.PageFactory): - body = wagtail_factories.StreamFieldFactory( - { - "char_array": wagtail_factories.ListBlockFactory( - wagtail_factories.CharBlockFactory - ), - "int_array": wagtail_factories.ListBlockFactory( - wagtail_factories.IntegerBlockFactory - ), - "struct": MyBlockFactory, - "image": wagtail_factories.ImageChooserBlockFactory, - } - ) - - class Meta: - model = models.MyTestPage - - -This style of declaration is still supported, with the caveat that nested stream blocks are not supported for this approach. From version 3.0, all ``BlockFactory`` values in a ``StreamFieldFactory`` definition of this style *must* be wrapped in factory_boy ``SubFactories``. For example, the above example must be updated to the following for 3.0 compatibility. - -.. code-block:: python - - class MyTestPageWithStreamFieldFactory(wagtail_factories.PageFactory): - body = wagtail_factories.StreamFieldFactory( - { - "char_array": wagtail_factories.ListBlockFactory( - wagtail_factories.CharBlockFactory - ), - "int_array": wagtail_factories.ListBlockFactory( - wagtail_factories.IntegerBlockFactory - ), - "struct": factory.SubFactory(MyBlockFactory), - "image": factory.SubFactory(wagtail_factories.ImageChooserBlockFactory), - } - ) - - class Meta: - model = models.MyTestPage - + class Meta: + model = models.MyTestPage +``` -This requirement does *not* apply to ``ListBlockFactory``, which is a subclass of ``SubFactory``. +This requirement does *not* apply to `ListBlockFactory`, which is a +subclass of `SubFactory`. diff --git a/setup.py b/setup.py index 0f018e3..8b5b5e3 100755 --- a/setup.py +++ b/setup.py @@ -20,9 +20,9 @@ "ruff==0.0.284", ] -with open("README.rst") as fh: +with open("README.md") as fh: long_description = re.sub( - "^.. start-no-pypi.*^.. end-no-pypi", "", fh.read(), flags=re.M | re.S + r"^## Status.*\n(## Installation)", r"\1", fh.read(), flags=re.M | re.S ) setup( From 4a9249e22ddc01c992708e60e4eb5befca49d291 Mon Sep 17 00:00:00 2001 From: Joshua Munn Date: Thu, 15 Feb 2024 22:07:48 +0000 Subject: [PATCH 07/25] Bump ruff and update config --- ruff.toml | 17 +++++++++-------- setup.py | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/ruff.toml b/ruff.toml index 9191897..c98eaf3 100644 --- a/ruff.toml +++ b/ruff.toml @@ -1,9 +1,12 @@ -# E501: Line too long -# S101: flake8-bandit Use of assert detected -ignore = ["E501","B028", "S101"] - exclude = ["dist","build","venv",".venv",".tox",".git"] line-length = 88 +target-version = "py38" + +[lint] +ignore = [ + "E501", # line too long + "S101", # flake8-bandit Use of assert detected +] select = [ "E", # pycodestyle errors @@ -20,7 +23,5 @@ select = [ "W", # pycodestyle warnings ] -target-version = "py38" - -[isort] -known-first-party = ["src"] \ No newline at end of file +[lint.isort] +known-first-party = ["src"] diff --git a/setup.py b/setup.py index 8b5b5e3..e412ff4 100755 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ "pytest-cov==3.0.0", "pytest-pythonpath==0.7.3", "coverage==6.0", - "ruff==0.0.284", + "ruff==0.2.1", ] with open("README.md") as fh: From ba5d41248b099c8ab269b80d7755073f7555dda9 Mon Sep 17 00:00:00 2001 From: Joshua Munn Date: Thu, 15 Feb 2024 22:08:32 +0000 Subject: [PATCH 08/25] Apply ruff fixes and formatting --- src/wagtail_factories/blocks.py | 2 +- src/wagtail_factories/factories.py | 6 +++--- tests/test_stream_block.py | 20 +++++++++++--------- tests/testapp/factories.py | 1 - tests/testapp/migrations/0001_initial.py | 1 - 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/wagtail_factories/blocks.py b/src/wagtail_factories/blocks.py index e36d9ff..f2c6a5f 100644 --- a/src/wagtail_factories/blocks.py +++ b/src/wagtail_factories/blocks.py @@ -148,7 +148,7 @@ class Meta: def _construct_struct_value(cls, block_class, params): return blocks.StructValue( block_class(), - [(name, value) for name, value in params.items()], + list(params.items()), ) @classmethod diff --git a/src/wagtail_factories/factories.py b/src/wagtail_factories/factories.py index 7b8e681..ff39ddc 100644 --- a/src/wagtail_factories/factories.py +++ b/src/wagtail_factories/factories.py @@ -20,7 +20,6 @@ class ParentNodeFactory(ParameteredAttribute): - EXTEND_CONTAINERS = True FORCE_SEQUENCE = False UNROLL_CONTEXT_BEFORE_EVALUATION = False @@ -42,7 +41,6 @@ def generate(self, step, params): class MP_NodeFactory(DjangoModelFactory): - parent = ParentNodeFactory() @classmethod @@ -84,7 +82,9 @@ def _get_or_create(cls, model_class, *args, **kwargs): if field not in kwargs: raise errors.FactoryError( "django_get_or_create - " - "Unable to find initialization value for '{}' in factory {}".format(field, cls.__name__) + "Unable to find initialization value for '{}' in factory {}".format( + field, cls.__name__ + ) ) lookup_fields[field] = kwargs[field] diff --git a/tests/test_stream_block.py b/tests/test_stream_block.py index ebbcc19..e4c9ed5 100644 --- a/tests/test_stream_block.py +++ b/tests/test_stream_block.py @@ -1,3 +1,4 @@ +import pytest import wagtail_factories from django.test import TestCase from wagtail import blocks @@ -81,7 +82,10 @@ def test_page_with_deeply_nested_stream_block_defaults(self): assert page.body[0].value["inner_stream"][0].value is None assert page.body[1].value["inner_stream"][0].block_type == "struct_block" - assert page.body[1].value["inner_stream"][0].value["title"] == "lazy function foobar" + assert ( + page.body[1].value["inner_stream"][0].value["title"] + == "lazy function foobar" + ) def test_page_with_deeply_nested_stream_block_in_list_block(self): page = PageWithStreamBlockInListBlockFactory( @@ -96,7 +100,9 @@ def test_computed_values_on_struct_block_in_nested_stream(self): ) assert page.body[0].value[0].value["boolean"] is True assert page.body[0].value[0].value["text"][:4] == "True" - assert page.body[0].value[0].value["text"][4:] == str(page.body[0].value[0].value["number"]) + assert page.body[0].value[0].value["text"][4:] == str( + page.body[0].value[0].value["number"] + ) def test_factory_with_anonymous_stream_block_in_tree(self): # The inner_stream child block is defined as an "anonymous" StreamBlock (i.e. declared @@ -156,9 +162,7 @@ def test_raises_missing_index(self): ) for params, missing_index in test_values: with self.subTest(params=params): - with self.assertRaisesRegex( - InvalidDeclaration, f"missing required index {missing_index}" - ): + with pytest.raises(InvalidDeclaration, match=f"missing required index {missing_index}"): PageWithStreamBlockFactory(**params) def test_raises_duplicate_declaration(self): @@ -177,11 +181,9 @@ def test_raises_duplicate_declaration(self): ) for params, msg in test_values: with self.subTest(params=params): - with self.assertRaisesRegex(DuplicateDeclaration, msg): + with pytest.raises(DuplicateDeclaration, match=msg): PageWithStreamBlockFactory(**params) def test_raises_unknown_child_block(self): - with self.assertRaisesRegex( - UnknownChildBlockFactory, "No factory defined for block 'foobar'" - ): + with pytest.raises(UnknownChildBlockFactory, match="No factory defined for block 'foobar'"): PageWithStreamBlockFactory(body__0="foobar") diff --git a/tests/testapp/factories.py b/tests/testapp/factories.py index 17f1cf2..676918e 100644 --- a/tests/testapp/factories.py +++ b/tests/testapp/factories.py @@ -38,7 +38,6 @@ class Meta: class MyTestPageWithStreamFieldFactory(wagtail_factories.PageFactory): - body = wagtail_factories.StreamFieldFactory( { "char_array": wagtail_factories.ListBlockFactory( diff --git a/tests/testapp/migrations/0001_initial.py b/tests/testapp/migrations/0001_initial.py index 1f24c1f..5d4f1a4 100644 --- a/tests/testapp/migrations/0001_initial.py +++ b/tests/testapp/migrations/0001_initial.py @@ -11,7 +11,6 @@ class Migration(migrations.Migration): - initial = True dependencies = [ From 3cdc8d0ad8cdfa84970d279a48850f2a1dbc71a2 Mon Sep 17 00:00:00 2001 From: Joshua Munn Date: Sun, 18 Feb 2024 16:37:12 +0000 Subject: [PATCH 09/25] Use extend-select in ruff conf --- ruff.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruff.toml b/ruff.toml index c98eaf3..0a6ffc5 100644 --- a/ruff.toml +++ b/ruff.toml @@ -8,7 +8,7 @@ ignore = [ "S101", # flake8-bandit Use of assert detected ] -select = [ +extend-select = [ "E", # pycodestyle errors "F", # pyflakes "I", # isort From 681b7a6deeb14738f269ec88bbd61d83df89d909 Mon Sep 17 00:00:00 2001 From: Lauren Date: Wed, 4 Oct 2023 09:59:11 +0100 Subject: [PATCH 10/25] Support Wagtail 5.1, drop Python 3.7 --- CHANGES | 2 ++ tox.ini | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index 3ae8a5b..172b4ac 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,7 @@ Unreleased ========== +- Update for Wagtail 5.1 +- Drop support for Django 3.7 4.1.0 ===== diff --git a/tox.ini b/tox.ini index fca61d4..ba855d0 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,9 @@ [tox] minversion = 3.14.6 envlist = - py{37,38,39,310}-django32-wagtail{41,42,50}-factoryboy32 - py{38,39,310}-django40-wagtail{41,42,50}-factoryboy32 - py{38,39,310,311}-django41-wagtail{41,42,50}-factoryboy32 + py{38,39,310}-django32-wagtail{41,42,50,51}-factoryboy32 + py{38,39,310}-django40-wagtail{41,42,50,51}-factoryboy32 + py{38,39,310,311}-django41-wagtail{41,42,50,51}-factoryboy32 coverage-report lint @@ -26,7 +26,7 @@ deps = factoryboy32: factory-boy>=3.2,<3.3 [testenv:coverage-report] -basepython = python3.7 +basepython = python3.8 deps = coverage pip_pre = true skip_install = true @@ -35,7 +35,7 @@ commands = coverage report [testenv:lint] -basepython = python3.7 +basepython = python3.8 deps = flake8 commands = flake8 src tests setup.py From 142ff3302678eb07a1d1f3c32610dc3ab8dde603 Mon Sep 17 00:00:00 2001 From: Lauren Date: Wed, 4 Oct 2023 11:16:11 +0100 Subject: [PATCH 11/25] Streamline testing matrix --- tox.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index ba855d0..0799fe5 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,9 @@ [tox] minversion = 3.14.6 envlist = - py{38,39,310}-django32-wagtail{41,42,50,51}-factoryboy32 - py{38,39,310}-django40-wagtail{41,42,50,51}-factoryboy32 - py{38,39,310,311}-django41-wagtail{41,42,50,51}-factoryboy32 + py{38,39,310}-django{32,41}-wagtail{41,42,50,51}-factoryboy32 + py311-django41-wagtail{41,42,50,51}-factoryboy32 + py311-django42-wtagtail{50,51}-factoryboy32 coverage-report lint From 233da417f4304d9981b7d3f1fed2e352fd8392d8 Mon Sep 17 00:00:00 2001 From: Lauren Date: Wed, 4 Oct 2023 11:17:27 +0100 Subject: [PATCH 12/25] Streamline testing matrix 2 --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 0799fe5..a06eabb 100644 --- a/tox.ini +++ b/tox.ini @@ -3,7 +3,7 @@ minversion = 3.14.6 envlist = py{38,39,310}-django{32,41}-wagtail{41,42,50,51}-factoryboy32 py311-django41-wagtail{41,42,50,51}-factoryboy32 - py311-django42-wtagtail{50,51}-factoryboy32 + py311-django42-wagtail{50,51}-factoryboy32 coverage-report lint From f413260dfa1b5ec92cdb2039b5e307249fbcc1dd Mon Sep 17 00:00:00 2001 From: Katherine Domingo Date: Thu, 16 Nov 2023 11:26:27 +0800 Subject: [PATCH 13/25] Add Wagtail 5.2 to test matrix, Drop tests for Wagtail 4.2 and 5.0 as they have reached EOL --- tox.ini | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/tox.ini b/tox.ini index a06eabb..38d9e84 100644 --- a/tox.ini +++ b/tox.ini @@ -1,12 +1,19 @@ [tox] minversion = 3.14.6 envlist = - py{38,39,310}-django{32,41}-wagtail{41,42,50,51}-factoryboy32 - py311-django41-wagtail{41,42,50,51}-factoryboy32 - py311-django42-wagtail{50,51}-factoryboy32 + py{38,39,310}-django{32,41}-wagtail{41,51,52}-factoryboy32 + py311-django41-wagtail{41,51,52}-factoryboy32 + py311-django42-wagtail{51,52}-factoryboy32 coverage-report lint +[gh-actions] +python = + 3.8: py38 + 3.9: py39 + 3.10: py310 + 3.11: py311 + [testenv] commands = coverage run --parallel -m pytest {posargs} extras = test @@ -20,9 +27,9 @@ deps = django32: django>=3.2,<3.3 django40: django>=4.0,<4.1 django41: django>=4.1,<5.0 - wagtail41: wagtail>=4.1,<4.2 - wagtail42: wagtail>=4.2,<5.0 - wagtail50: wagtail>=5.0,<5.1 + wagtail41: wagtail>=4.1,<5.0 + wagtail51: wagtail>=5.1,<5.2 + wagtail52: wagtail>=5.2,<5.3 factoryboy32: factory-boy>=3.2,<3.3 [testenv:coverage-report] From df1f7d9060784baf1a48c4ad155944cdd58a6cdd Mon Sep 17 00:00:00 2001 From: Katherine Domingo Date: Thu, 16 Nov 2023 11:26:43 +0800 Subject: [PATCH 14/25] Update workflow to use tox-gh-actions --- .github/workflows/python-tox.yml | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/.github/workflows/python-tox.yml b/.github/workflows/python-tox.yml index b13df59..7780885 100644 --- a/.github/workflows/python-tox.yml +++ b/.github/workflows/python-tox.yml @@ -21,36 +21,18 @@ jobs: - 5432:5432 strategy: - max-parallel: 4 matrix: - python-version: - - {version: 3.8, tox: 38} - - {version: 3.9, tox: 39} - - {version: '3.10', tox: 310} - - {version: '3.11', tox: 311} - django: [32, 40, 41] - wagtail: [41, 42, 50] - factoryboy: [32] - exclude: - - python-version: {version: '3.11', tox: 311} - django: 40 - - python-version: {version: '3.11', tox: 311} - django: 32 + python-version: ["3.8", "3.9", "3.10", "3.11"] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: - python-version: ${{ matrix.python-version.version }} + python-version: ${{ matrix.python-version }} - name: Install dependencies run: | - python -m pip install --upgrade pip tox + python -m pip install --upgrade pip + python -m pip install tox tox-gh-actions - name: Test with tox - env: - TEST_DB_NAME: wagtail_factories - TEST_DB_USER: wagtail_factories - TEST_DB_PASSWORD: secret - TOX_ENV: "py${{ matrix.python-version.tox }}-django${{ matrix.django }}-wagtail${{ matrix.wagtail }}-factoryboy${{ matrix.factoryboy }}" - run: | - tox -e $TOX_ENV + run: tox From 9b6673a9e1c7e96aca47317babf0b8642cae8ecd Mon Sep 17 00:00:00 2001 From: Katherine Domingo Date: Thu, 16 Nov 2023 11:36:18 +0800 Subject: [PATCH 15/25] Set STATIC_URL --- tests/settings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/settings.py b/tests/settings.py index a4d100d..0a15956 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -82,3 +82,5 @@ WAGTAILDOCS_SERVE_METHOD = "direct" WAGTAILADMIN_BASE_URL = "http://example.com" + +STATIC_URL = "/static/" \ No newline at end of file From bef9aa9d48d67d763f61b4c795a7911c10cb36b5 Mon Sep 17 00:00:00 2001 From: Kat Date: Fri, 17 Nov 2023 11:36:13 +0800 Subject: [PATCH 16/25] Add a newline to tests/settings.py Co-authored-by: Nick Moreton --- tests/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/settings.py b/tests/settings.py index 0a15956..73ddac6 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -83,4 +83,4 @@ WAGTAILADMIN_BASE_URL = "http://example.com" -STATIC_URL = "/static/" \ No newline at end of file +STATIC_URL = "/static/" From 9ff068a5c95681cb94c0fcfc9ab55c7f9689a7cc Mon Sep 17 00:00:00 2001 From: Joshua Munn Date: Sun, 18 Feb 2024 17:35:53 +0000 Subject: [PATCH 17/25] Drop wagtail<5.2,django<4.2, add wagtail6.0, django 5.0 Use ruff as linter in tox.ini --- setup.py | 10 +++++----- tests/settings.py | 1 - tests/test_stream_block.py | 8 ++++++-- tox.ini | 29 +++++++++++------------------ 4 files changed, 22 insertions(+), 26 deletions(-) diff --git a/setup.py b/setup.py index e412ff4..02816f5 100755 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ install_requires = [ "factory-boy>=3.2", - "wagtail>=4.1", + "wagtail>=5.2", ] docs_require = [ @@ -54,15 +54,15 @@ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Framework :: Django", - "Framework :: Django :: 3.2", - "Framework :: Django :: 4.0", - "Framework :: Django :: 4.1", + "Framework :: Django :: 4.2", + "Framework :: Django :: 5.0", "Framework :: Wagtail", - "Framework :: Wagtail :: 4", "Framework :: Wagtail :: 5", + "Framework :: Wagtail :: 6", ], zip_safe=False, ) diff --git a/tests/settings.py b/tests/settings.py index 73ddac6..1d13c34 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -19,7 +19,6 @@ "wagtail.contrib.frontend_cache", "wagtail.contrib.search_promotions", "wagtail.contrib.settings", - "wagtail.contrib.modeladmin", "wagtail.contrib.table_block", "wagtail.contrib.forms", "wagtail.search", diff --git a/tests/test_stream_block.py b/tests/test_stream_block.py index e4c9ed5..f563ccd 100644 --- a/tests/test_stream_block.py +++ b/tests/test_stream_block.py @@ -162,7 +162,9 @@ def test_raises_missing_index(self): ) for params, missing_index in test_values: with self.subTest(params=params): - with pytest.raises(InvalidDeclaration, match=f"missing required index {missing_index}"): + with pytest.raises( + InvalidDeclaration, match=f"missing required index {missing_index}" + ): PageWithStreamBlockFactory(**params) def test_raises_duplicate_declaration(self): @@ -185,5 +187,7 @@ def test_raises_duplicate_declaration(self): PageWithStreamBlockFactory(**params) def test_raises_unknown_child_block(self): - with pytest.raises(UnknownChildBlockFactory, match="No factory defined for block 'foobar'"): + with pytest.raises( + UnknownChildBlockFactory, match="No factory defined for block 'foobar'" + ): PageWithStreamBlockFactory(body__0="foobar") diff --git a/tox.ini b/tox.ini index 38d9e84..1ad4c5b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,8 @@ [tox] -minversion = 3.14.6 +minversion = 3.21.4 envlist = - py{38,39,310}-django{32,41}-wagtail{41,51,52}-factoryboy32 - py311-django41-wagtail{41,51,52}-factoryboy32 - py311-django42-wagtail{51,52}-factoryboy32 + py{38,39,310,311}-django42-wagtail{52,60}-factoryboy{32,33} + py{310,311,312}-django50-wagtail60-factoryboy{32,33} coverage-report lint @@ -13,24 +12,18 @@ python = 3.9: py39 3.10: py310 3.11: py311 + 3.12: py312 [testenv] commands = coverage run --parallel -m pytest {posargs} extras = test -passenv = - TEST_DB_NAME - TEST_DB_USER - TEST_DB_PASSWORD - TEST_DB_HOST - TEST_DB_PORT deps = - django32: django>=3.2,<3.3 - django40: django>=4.0,<4.1 - django41: django>=4.1,<5.0 - wagtail41: wagtail>=4.1,<5.0 - wagtail51: wagtail>=5.1,<5.2 - wagtail52: wagtail>=5.2,<5.3 + django42: django>=4.2,<5.0 + django50: django>=5.0,<5.1 + wagtail52: wagtail>=5.2,<6.0 + wagtail60: wagtail>=6.0,<6.1 factoryboy32: factory-boy>=3.2,<3.3 + factoryboy33: factory-boy>=3.3,<3.4 [testenv:coverage-report] basepython = python3.8 @@ -45,5 +38,5 @@ commands = basepython = python3.8 deps = flake8 commands = - flake8 src tests setup.py - isort --recursive --check-only --diff src tests + ruff format --check src/ tests/ + ruff check src/ tests/ From 3f84eb3285ae0b14f1c87dea115618676f76e87f Mon Sep 17 00:00:00 2001 From: Joshua Munn Date: Sun, 18 Feb 2024 17:38:38 +0000 Subject: [PATCH 18/25] Add py3.12 to github actions, remove postgres service --- .github/workflows/python-tox.yml | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/.github/workflows/python-tox.yml b/.github/workflows/python-tox.yml index 7780885..ff2204a 100644 --- a/.github/workflows/python-tox.yml +++ b/.github/workflows/python-tox.yml @@ -5,29 +5,15 @@ on: [push, pull_request] jobs: build: runs-on: ubuntu-latest - services: - postgres: - image: postgres - env: - POSTGRES_PASSWORD: secret - POSTGRES_USER: wagtail_factories - POSTGRES_DB: wagtail_factories - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 5432:5432 strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies From 2b992163ee29b8421b7a2dd4e2ae691b924066aa Mon Sep 17 00:00:00 2001 From: Joshua Munn Date: Sun, 18 Feb 2024 17:44:24 +0000 Subject: [PATCH 19/25] Update changelog --- CHANGES | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 172b4ac..c07bd6b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,11 @@ Unreleased ========== -- Update for Wagtail 5.1 -- Drop support for Django 3.7 +- Update for Wagtail >5.1 +- Drop support for Django <4.2 +- Drop support for Wagtail <5.2 +- Add support for Django 5 +- Add support for Wagtail 6 +- Add support for Python 3.12 4.1.0 ===== From ab64809a3048626d369c9b3c0d4f1a2e09b6a503 Mon Sep 17 00:00:00 2001 From: Joshua Munn Date: Sun, 18 Feb 2024 22:37:28 +0000 Subject: [PATCH 20/25] Use better dummy secret key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dan BraghiÈ™ --- tests/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/settings.py b/tests/settings.py index 1d13c34..6704677 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -7,7 +7,7 @@ ROOT_URLCONF = "tests.urls" -SECRET_KEY = "Gx8sMKAtnA69TR9lyAlLuSnozUv3kxdscHkpwEjatZRVQQ0laMY69KL4XPxvr3KY" # NOQA +SECRET_KEY = "not-a-secret-key" # noqa: S105 DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}} From 68b0524fbee2570a5cbe1dd32d55486baa6ac4a5 Mon Sep 17 00:00:00 2001 From: Joshua Munn Date: Sun, 18 Feb 2024 22:39:07 +0000 Subject: [PATCH 21/25] Reformat docs conf --- docs/conf.py | 89 ++++++++++++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 41 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 8ce572d..ac64d3a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -32,32 +32,32 @@ extensions = [] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. # # source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'wagtail-factories' +project = "wagtail-factories" copyright = '2016, Michael van Tellingen' -author = 'Michael van Tellingen' +author = "Michael van Tellingen" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '4.1.0' +version = "4.1.0" release = version # The language for content autogenerated by Sphinx. Refer to documentation @@ -79,7 +79,7 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # The reST default role (used for this markup: `text`) to use for all # documents. @@ -101,7 +101,7 @@ # show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] @@ -118,7 +118,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'alabaster' +html_theme = "alabaster" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -126,12 +126,12 @@ # # html_theme_options = {} html_theme_options = { - 'github_user': 'wagtail', - 'github_banner': True, - 'github_repo': 'wagtail-factories', - 'travis_button': True, - 'codecov_button': True, - 'analytics_id': 'UA-75907833-5', + "github_user": "wagtail", + "github_banner": True, + "github_repo": "wagtail-factories", + "travis_button": True, + "codecov_button": True, + "analytics_id": "UA-75907833-5", } # Add any paths that contain custom themes here, relative to this directory. @@ -160,7 +160,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied @@ -183,8 +183,8 @@ # # html_sidebars = {} html_sidebars = { - '*': [ - 'sidebar-intro.html', + "*": [ + "sidebar-intro.html", ] } @@ -245,34 +245,36 @@ # html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'wagtail-factoriesdoc' +htmlhelp_basename = "wagtail-factoriesdoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'wagtail-factories.tex', 'wagtail-factories Documentation', - 'Michael van Tellingen', 'manual'), + ( + master_doc, + "wagtail-factories.tex", + "wagtail-factories Documentation", + "Michael van Tellingen", + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of @@ -313,8 +315,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'wagtail-factories', 'wagtail-factories Documentation', - [author], 1) + (master_doc, "wagtail-factories", "wagtail-factories Documentation", [author], 1) ] # If true, show URL addresses after external links. @@ -328,9 +329,15 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'wagtail-factories', 'wagtail-factories Documentation', - author, 'wagtail-factories', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "wagtail-factories", + "wagtail-factories Documentation", + author, + "wagtail-factories", + "One line description of project.", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. From 3cc0114a74ddcf2d7dd0f451b505b720144ecac7 Mon Sep 17 00:00:00 2001 From: Joshua Munn Date: Sun, 18 Feb 2024 22:40:21 +0000 Subject: [PATCH 22/25] Change tox basepython to 3.11 --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 1ad4c5b..ff2251b 100644 --- a/tox.ini +++ b/tox.ini @@ -26,7 +26,7 @@ deps = factoryboy33: factory-boy>=3.3,<3.4 [testenv:coverage-report] -basepython = python3.8 +basepython = python3.11 deps = coverage pip_pre = true skip_install = true @@ -35,7 +35,7 @@ commands = coverage report [testenv:lint] -basepython = python3.8 +basepython = python3.11 deps = flake8 commands = ruff format --check src/ tests/ From 247a33970420b9b084c473e924b3e70bcafd83d1 Mon Sep 17 00:00:00 2001 From: Joshua Munn Date: Sun, 18 Feb 2024 22:41:14 +0000 Subject: [PATCH 23/25] Change ruff entrypoint in tox conf --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index ff2251b..caac9e3 100644 --- a/tox.ini +++ b/tox.ini @@ -38,5 +38,5 @@ commands = basepython = python3.11 deps = flake8 commands = - ruff format --check src/ tests/ - ruff check src/ tests/ + ruff format --check . + ruff check . From 38893d30672f02f97a9722be742cbaf2f05ba0de Mon Sep 17 00:00:00 2001 From: Joshua Munn Date: Sun, 18 Feb 2024 22:49:04 +0000 Subject: [PATCH 24/25] Remove assertions from source modules --- src/wagtail_factories/factories.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/wagtail_factories/factories.py b/src/wagtail_factories/factories.py index ff39ddc..80221d1 100644 --- a/src/wagtail_factories/factories.py +++ b/src/wagtail_factories/factories.py @@ -56,7 +56,6 @@ def _create(cls, model_class, *args, **kwargs): instance = cls._get_or_create(model_class, *args, parent=parent, **kwargs) else: instance = cls._create_instance(model_class, parent, kwargs) - assert instance.pk return instance @classmethod @@ -72,10 +71,11 @@ def _create_instance(cls, model_class, parent, kwargs): def _get_or_create(cls, model_class, *args, **kwargs): """Create an instance of the model through objects.get_or_create.""" manager = cls._get_manager(model_class) - assert "defaults" not in cls._meta.django_get_or_create, ( - "'defaults' is a reserved keyword for get_or_create " - f"(in {cls}._meta.django_get_or_create={cls._meta.django_get_or_create!r})" - ) + if "defaults" in cls._meta.django_get_or_create: + raise RuntimeError( + "'defaults' is a reserved keyword for get_or_create " + f"(in {cls}._meta.django_get_or_create={cls._meta.django_get_or_create!r})" + ) lookup_fields = {} for field in cls._meta.django_get_or_create: From 05c94cca27c325f75f13b248c7419dfc49a4bb70 Mon Sep 17 00:00:00 2001 From: Joshua Munn Date: Sun, 18 Feb 2024 22:49:25 +0000 Subject: [PATCH 25/25] Only ignore "S101 - use of assert" in tests --- ruff.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ruff.toml b/ruff.toml index 0a6ffc5..033f26d 100644 --- a/ruff.toml +++ b/ruff.toml @@ -5,7 +5,6 @@ target-version = "py38" [lint] ignore = [ "E501", # line too long - "S101", # flake8-bandit Use of assert detected ] extend-select = [ @@ -23,5 +22,10 @@ extend-select = [ "W", # pycodestyle warnings ] +[lint.extend-per-file-ignores] +"tests/test_*.py" = [ + "S101", # flake8-bandit Use of assert detected +] + [lint.isort] known-first-party = ["src"]