From 38ecbaebf51d712d4836337f7c9ef949d64e33e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Wed, 19 Jul 2023 11:03:54 +0200 Subject: [PATCH 01/41] Preparing release 3.3.0 --- docs/changelog.rst | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 73a3df6e..9e4e1fcb 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -3,7 +3,7 @@ ChangeLog .. Note for v4.x: don't forget to check "Deprecated" sections for removal. -3.3.0 (unreleased) +3.3.0 (2023-07-19) ------------------ *New:* diff --git a/setup.cfg b/setup.cfg index 2e8749c2..be6a16d8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = factory_boy -version = 3.2.1.dev0 +version = 3.3.0 description = A versatile test fixtures replacement based on thoughtbot's factory_bot for Ruby. long_description = file: README.rst # https://docutils.sourceforge.io/FAQ.html#what-s-the-official-mime-type-for-restructuredtext-data From 86b87f4275dbc799b2e5d047d04dc7f0f78193f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Wed, 19 Jul 2023 11:04:09 +0200 Subject: [PATCH 02/41] Back to development: 3.3.1 --- docs/changelog.rst | 6 ++++++ setup.cfg | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 9e4e1fcb..3236deae 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -3,6 +3,12 @@ ChangeLog .. Note for v4.x: don't forget to check "Deprecated" sections for removal. +3.3.1 (unreleased) +------------------ + +- Nothing changed yet. + + 3.3.0 (2023-07-19) ------------------ diff --git a/setup.cfg b/setup.cfg index be6a16d8..50cf3a22 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = factory_boy -version = 3.3.0 +version = 3.3.1.dev0 description = A versatile test fixtures replacement based on thoughtbot's factory_bot for Ruby. long_description = file: README.rst # https://docutils.sourceforge.io/FAQ.html#what-s-the-official-mime-type-for-restructuredtext-data From f0859075b048b85c8ed44c051f18070cbd20d03d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Wed, 19 Jul 2023 15:27:41 +0200 Subject: [PATCH 03/41] Fix test_version That test used to hardcode the current dev version; this conflicts with the goal of storing said version only in setup.cfg. Further issues might be caused by `pip install -e .[dev]`; in that case, the check will have to be further loosened. --- tests/test_version.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/test_version.py b/tests/test_version.py index 52291dc4..86a57bd4 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -1,10 +1,19 @@ # Copyright: See the LICENSE file. +import pathlib import unittest import factory +SETUP_CFG_VERSION_PREFIX = "version =" class VersionTestCase(unittest.TestCase): + def get_setupcfg_version(self): + setup_cfg_path = pathlib.Path(__file__).parent.parent / "setup.cfg" + with setup_cfg_path.open("r") as f: + for line in f: + if line.startswith(SETUP_CFG_VERSION_PREFIX): + return line[len(SETUP_CFG_VERSION_PREFIX):].strip() + def test_version(self): - self.assertEqual(factory.__version__, "3.2.1.dev0") + self.assertEqual(factory.__version__, self.get_setupcfg_version()) From 2a371906dbd80b895b0030c301a823936eaf3a8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Wed, 19 Jul 2023 15:30:11 +0200 Subject: [PATCH 04/41] Fix linting issue in test_version --- tests/test_version.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_version.py b/tests/test_version.py index 86a57bd4..4fceda4a 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -7,6 +7,7 @@ SETUP_CFG_VERSION_PREFIX = "version =" + class VersionTestCase(unittest.TestCase): def get_setupcfg_version(self): setup_cfg_path = pathlib.Path(__file__).parent.parent / "setup.cfg" From f83e0f672039de0c6b7e5ba7d738cccdd592ca57 Mon Sep 17 00:00:00 2001 From: Jessica Gadling Date: Mon, 24 Jul 2023 13:33:57 -0700 Subject: [PATCH 05/41] Don't require setting sqlalchemy_session to None when using a factory. --- factory/alchemy.py | 7 +++++-- tests/test_alchemy.py | 1 - 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/factory/alchemy.py b/factory/alchemy.py index 4e7ad6a2..f934ce5d 100644 --- a/factory/alchemy.py +++ b/factory/alchemy.py @@ -24,8 +24,11 @@ def _check_sqlalchemy_session_persistence(self, meta, value): @staticmethod def _check_has_sqlalchemy_session_set(meta, value): - if value and meta.sqlalchemy_session: - raise RuntimeError("Provide either a sqlalchemy_session or a sqlalchemy_session_factory, not both") + try: + if value and meta.sqlalchemy_session: + raise RuntimeError("Provide either a sqlalchemy_session or a sqlalchemy_session_factory, not both") + except AttributeError: + pass def _build_default_options(self): return super()._build_default_options() + [ diff --git a/tests/test_alchemy.py b/tests/test_alchemy.py index 9cac036b..19e4f5ee 100644 --- a/tests/test_alchemy.py +++ b/tests/test_alchemy.py @@ -286,7 +286,6 @@ def test_create_get_session_from_sqlalchemy_session_factory(self): class SessionGetterFactory(SQLAlchemyModelFactory): class Meta: model = models.StandardModel - sqlalchemy_session = None sqlalchemy_session_factory = lambda: models.session id = factory.Sequence(lambda n: n) From e02146f4bf0f8876ccbed34f0c6ebcd2712f1271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Mon, 31 Jul 2023 19:00:42 +0200 Subject: [PATCH 06/41] Add changelog entry for f83e0f672039de0c6b7e5ba7d738cccdd592ca57 Missed in #1032. --- docs/changelog.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 3236deae..6a397b89 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -6,7 +6,10 @@ ChangeLog 3.3.1 (unreleased) ------------------ -- Nothing changed yet. +*Bugfix:* + +- :issue:`1031`: Do not require :attr:`~factory.alchemy.SQLAlchemyOptions.sqlalchemy_session` when + :attr:`~factory.alchemy.SQLAlchemyOptions.sqlalchemy_session_factory` is provided. 3.3.0 (2023-07-19) From 7e4ac8620b7b0f95c9bca4f1ad0174f8e547792d Mon Sep 17 00:00:00 2001 From: Samuel Searles-Bryant Date: Fri, 11 Aug 2023 18:18:43 +0100 Subject: [PATCH 07/41] Add version added to docs for `Transformer` This was added in version 3.3.0, but has no marker that says that. This change adds the marker to make it clear to users reading the 'stable' docs that this might not be available in their version. --- docs/reference.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/reference.rst b/docs/reference.rst index 9efd1a22..238b56c4 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -909,6 +909,8 @@ Transformer .. class:: Transformer(default_value, *, transform) + .. versionadded:: 3.3.0 + A :class:`Transformer` applies a ``transform`` function to the provided value before to set the transformed value on the generated object. From f9574a1dcfca232e7fe1ed63bb6ac447e1f754ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Mon, 4 Sep 2023 10:50:23 +0200 Subject: [PATCH 08/41] Add support for Django 4.2 --- docs/changelog.rst | 3 +++ setup.cfg | 1 + tox.ini | 1 + 3 files changed, 5 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 6a397b89..f71f3ff2 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,6 +5,9 @@ ChangeLog 3.3.1 (unreleased) ------------------ +*New:* + +- Add support for Django 4.2 *Bugfix:* diff --git a/setup.cfg b/setup.cfg index 50cf3a22..e3be4711 100644 --- a/setup.cfg +++ b/setup.cfg @@ -18,6 +18,7 @@ classifiers = Framework :: Django :: 3.2 Framework :: Django :: 4.0 Framework :: Django :: 4.1 + Framework :: Django :: 4.2 Intended Audience :: Developers License :: OSI Approved :: MIT License Operating System :: OS Independent diff --git a/tox.ini b/tox.ini index a234315f..db1b1398 100644 --- a/tox.ini +++ b/tox.ini @@ -40,6 +40,7 @@ deps = django32: Django>=3.2,<3.3 django40: Django>=4.0,<4.1 django41: Django>=4.1,<4.2 + django42: Django>=4.2,<5.0 djangomain: https://github.com/django/django/archive/main.tar.gz py{37,38,39,310,311}-postgres: psycopg2-binary pypy{37,38,39}-postgres: psycopg2cffi From 63c2df43ea640adb08d68c2b09c542667691f588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Mon, 4 Sep 2023 14:12:56 +0200 Subject: [PATCH 09/41] Drop support for Django 4.0 --- docs/changelog.rst | 3 +++ setup.cfg | 1 - tox.ini | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index f71f3ff2..bc8606cb 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -14,6 +14,9 @@ ChangeLog - :issue:`1031`: Do not require :attr:`~factory.alchemy.SQLAlchemyOptions.sqlalchemy_session` when :attr:`~factory.alchemy.SQLAlchemyOptions.sqlalchemy_session_factory` is provided. +*Removed:* + +- Drop support for Django 4.0 3.3.0 (2023-07-19) ------------------ diff --git a/setup.cfg b/setup.cfg index e3be4711..49dd0d51 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,6 @@ classifiers = Development Status :: 5 - Production/Stable Framework :: Django Framework :: Django :: 3.2 - Framework :: Django :: 4.0 Framework :: Django :: 4.1 Framework :: Django :: 4.2 Intended Audience :: Developers diff --git a/tox.ini b/tox.ini index db1b1398..c9a03758 100644 --- a/tox.ini +++ b/tox.ini @@ -38,7 +38,6 @@ deps = mongo: mongoengine django{32,40,41,main}: Pillow django32: Django>=3.2,<3.3 - django40: Django>=4.0,<4.1 django41: Django>=4.1,<4.2 django42: Django>=4.2,<5.0 djangomain: https://github.com/django/django/archive/main.tar.gz From fb744fd96005291b98fb354632d3c17232e238bd Mon Sep 17 00:00:00 2001 From: sarahboyce Date: Tue, 19 Sep 2023 16:49:43 +0200 Subject: [PATCH 10/41] Fixed mute_signals restoring signals receiver order. --- factory/django.py | 2 +- tests/test_django.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/factory/django.py b/factory/django.py index 87b6fd55..9526b775 100644 --- a/factory/django.py +++ b/factory/django.py @@ -311,7 +311,7 @@ def __exit__(self, exc_type, exc_value, traceback): logger.debug('mute_signals: Restoring signal handlers %r', receivers) - signal.receivers += receivers + signal.receivers = receivers + signal.receivers with signal.lock: # Django uses some caching for its signals. # Since we're bypassing signal.connect and signal.disconnect, diff --git a/tests/test_django.py b/tests/test_django.py index 19729e07..d2615e29 100644 --- a/tests/test_django.py +++ b/tests/test_django.py @@ -941,6 +941,22 @@ def test_receiver_created_during_model_instantiation_is_not_lost(self): self.assertTrue(self.handlers.created_during_instantiation.called) + def test_signal_receiver_order_restored_after_mute_signals(self): + def must_be_first(*args, **kwargs): + self.handlers.do_stuff(1) + + def must_be_second(*args, **kwargs): + self.handlers.do_stuff(2) + + signals.post_save.connect(must_be_first) + with factory.django.mute_signals(signals.post_save): + WithSignalsFactory(post_save_signal_receiver=must_be_second) + self.handlers.do_stuff.assert_has_calls([mock.call(2)]) + + self.handlers.reset_mock() + WithSignalsFactory(post_save_signal_receiver=must_be_second) + self.handlers.do_stuff.assert_has_calls([mock.call(1), mock.call(2)]) + def test_signal_cache(self): with factory.django.mute_signals(signals.pre_save, signals.post_save): signals.post_save.connect(self.handlers.mute_block_receiver) From 5eb79fe7ecd63b6e6f798d38c312a2ee23bd38ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Tue, 19 Sep 2023 18:02:00 +0200 Subject: [PATCH 11/41] Tighten assertions --- tests/test_django.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_django.py b/tests/test_django.py index d2615e29..066d7920 100644 --- a/tests/test_django.py +++ b/tests/test_django.py @@ -951,11 +951,11 @@ def must_be_second(*args, **kwargs): signals.post_save.connect(must_be_first) with factory.django.mute_signals(signals.post_save): WithSignalsFactory(post_save_signal_receiver=must_be_second) - self.handlers.do_stuff.assert_has_calls([mock.call(2)]) + self.assertEqual(self.handlers.do_stuff.call_args_list, [mock.call(2)]) self.handlers.reset_mock() WithSignalsFactory(post_save_signal_receiver=must_be_second) - self.handlers.do_stuff.assert_has_calls([mock.call(1), mock.call(2)]) + self.assertEqual(self.handlers.do_stuff.call_args_list, [mock.call(1), mock.call(2)]) def test_signal_cache(self): with factory.django.mute_signals(signals.pre_save, signals.post_save): From 3ea1117aa9a313d8619541ea4575d7628095ff53 Mon Sep 17 00:00:00 2001 From: Jaap Roes Date: Wed, 20 Sep 2023 16:08:35 +0200 Subject: [PATCH 12/41] Add note about creating unusable passwords --- docs/orms.rst | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/orms.rst b/docs/orms.rst index 3839707c..5967e1bc 100644 --- a/docs/orms.rst +++ b/docs/orms.rst @@ -128,7 +128,11 @@ Extra fields .. method:: __init__(self, password) - :param str password: Default password. + :param str or None password: Default password. + + .. note:: When the ``password`` argument is ``None``, the resulting password is + unusable as if ``set_unusable_password()`` were used. This is distinct + from setting the password to an empty string. .. code-block:: python @@ -149,6 +153,10 @@ Extra fields >>> other_user = UserFactory.create(password='other_pw') >>> check_password('other_pw', other_user.password) True + >>> # Set unusable password + >>> no_password_user = UserFactory.create(password=None) + >>> no_password_user.has_usable_password() + False .. class:: FileField From 45d7a95c84ac3d7767e3e5428023addc4c77f38e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Wed, 4 Oct 2023 10:54:03 +0200 Subject: [PATCH 13/41] Add support for Python 3.12 --- .github/workflows/test.yml | 1 + Makefile | 1 + docs/changelog.rst | 1 + setup.cfg | 1 + tox.ini | 1 + 5 files changed, 5 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f50bd24c..77ff9a18 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,6 +22,7 @@ jobs: - "3.9" - "3.10" - "3.11" + - "3.12" - "pypy-3.7" - "pypy-3.8" - "pypy-3.9" diff --git a/Makefile b/Makefile index ad35a6fc..927689a9 100644 --- a/Makefile +++ b/Makefile @@ -64,6 +64,7 @@ test: -Wdefault:"set_output_charset() is deprecated":DeprecationWarning:: \ -Wdefault:"parameter codeset is deprecated":DeprecationWarning:: \ -Wdefault:"'cgi' is deprecated and slated for removal in Python 3.13":DeprecationWarning:: \ + -Wdefault:"datetime.datetime.utcfromtimestamp() is deprecated and scheduled for removal in a future version.":DeprecationWarning:: \ -m unittest # DOC: Test the examples diff --git a/docs/changelog.rst b/docs/changelog.rst index bc8606cb..036254e8 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -8,6 +8,7 @@ ChangeLog *New:* - Add support for Django 4.2 +- Add support for Python 3.12 *Bugfix:* diff --git a/setup.cfg b/setup.cfg index 49dd0d51..8a9c4f4e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -29,6 +29,7 @@ classifiers = 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 Topic :: Software Development :: Testing diff --git a/tox.ini b/tox.ini index c9a03758..287ea48c 100644 --- a/tox.ini +++ b/tox.ini @@ -18,6 +18,7 @@ python = 3.9: py39 3.10: py310 3.11: py311 + 3.12: py312 pypy-3.7: pypy37 pypy-3.8: pypy38 pypy-3.9: pypy39 From 1c99a464588046737f1b34ddf678d78cd6683511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Mon, 17 Jul 2023 14:59:02 +0200 Subject: [PATCH 14/41] Remove support for EOL Python 3.7 --- .github/workflows/test.yml | 2 -- Makefile | 1 - docs/changelog.rst | 1 + readthedocs.yml | 6 ++++++ setup.cfg | 4 +--- tox.ini | 10 ++++------ 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 77ff9a18..06fe75c3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,13 +17,11 @@ jobs: fail-fast: false matrix: python-version: - - "3.7" - "3.8" - "3.9" - "3.10" - "3.11" - "3.12" - - "pypy-3.7" - "pypy-3.8" - "pypy-3.9" database-type: diff --git a/Makefile b/Makefile index 927689a9..a31a9fb8 100644 --- a/Makefile +++ b/Makefile @@ -59,7 +59,6 @@ test: -X dev \ -Werror \ -Wdefault:"the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses":DeprecationWarning:distutils: \ - -Wdefault:"Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working":DeprecationWarning:: \ -Wdefault:"Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3, and in 3.9 it will stop working":DeprecationWarning:: \ -Wdefault:"set_output_charset() is deprecated":DeprecationWarning:: \ -Wdefault:"parameter codeset is deprecated":DeprecationWarning:: \ diff --git a/docs/changelog.rst b/docs/changelog.rst index 036254e8..7c34c589 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -67,6 +67,7 @@ ChangeLog - Drop support for Django 3.0 - Drop support for Django 3.1 - Drop support for Python 3.6 + - Drop support for Python 3.7 3.2.1 (2021-10-26) ------------------ diff --git a/readthedocs.yml b/readthedocs.yml index 57cad8d1..883f654f 100644 --- a/readthedocs.yml +++ b/readthedocs.yml @@ -1,5 +1,11 @@ --- version: 2 + +build: + os: ubuntu-22.04 + tools: + python: "3.11" + python: install: - method: pip diff --git a/setup.cfg b/setup.cfg index 8a9c4f4e..bc242d4f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -24,7 +24,6 @@ classifiers = Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 @@ -37,10 +36,9 @@ classifiers = [options] packages = factory -python_requires = >=3.7 +python_requires = >=3.8 install_requires = Faker>=0.7.0 - importlib_metadata;python_version<"3.8" [options.extras_require] dev = diff --git a/tox.ini b/tox.ini index 287ea48c..d4873869 100644 --- a/tox.ini +++ b/tox.ini @@ -5,21 +5,19 @@ envlist = docs examples linkcheck - py{37,38,39,310,311,py37,py38,py39}-sqlite - py{37,38,39,310,311,py37,py38,py39}-django32-mongo-alchemy-{sqlite,postgres} + py{38,39,310,311,py38,py39}-sqlite + py{38,39,310,311,py38,py39}-django32-mongo-alchemy-{sqlite,postgres} py{38,39,310,311,py38,py39}-django40-mongo-alchemy-{sqlite,postgres} py{38,39,310,311,py38,py39}-django41-mongo-alchemy-{sqlite,postgres} py310-djangomain-mongo-alchemy-{sqlite,postgres} [gh-actions] python = - 3.7: py37 3.8: py38 3.9: py39 3.10: py310 3.11: py311 3.12: py312 - pypy-3.7: pypy37 pypy-3.8: pypy38 pypy-3.9: pypy39 @@ -42,8 +40,8 @@ deps = django41: Django>=4.1,<4.2 django42: Django>=4.2,<5.0 djangomain: https://github.com/django/django/archive/main.tar.gz - py{37,38,39,310,311}-postgres: psycopg2-binary - pypy{37,38,39}-postgres: psycopg2cffi + py{38,39,310,311}-postgres: psycopg2-binary + pypy{38,39}-postgres: psycopg2cffi setenv = py: DJANGO_SETTINGS_MODULE=tests.djapp.settings From 504404713d32ec6c9295f77737a85967cbd7a325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Thu, 16 Nov 2023 20:43:24 +0100 Subject: [PATCH 15/41] Add support for Django 5.0 --- docs/changelog.rst | 1 + setup.cfg | 1 + tox.ini | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 7c34c589..b98bcfce 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -8,6 +8,7 @@ ChangeLog *New:* - Add support for Django 4.2 +- Add support for Django 5.0 - Add support for Python 3.12 *Bugfix:* diff --git a/setup.cfg b/setup.cfg index bc242d4f..3ba2b7aa 100644 --- a/setup.cfg +++ b/setup.cfg @@ -18,6 +18,7 @@ classifiers = Framework :: Django :: 3.2 Framework :: Django :: 4.1 Framework :: Django :: 4.2 + Framework :: Django :: 5.0 Intended Audience :: Developers License :: OSI Approved :: MIT License Operating System :: OS Independent diff --git a/tox.ini b/tox.ini index d4873869..efe79b67 100644 --- a/tox.ini +++ b/tox.ini @@ -35,10 +35,11 @@ deps = alchemy: SQLAlchemy alchemy: sqlalchemy_utils mongo: mongoengine - django{32,40,41,main}: Pillow + django{32,41,42,50,main}: Pillow django32: Django>=3.2,<3.3 django41: Django>=4.1,<4.2 django42: Django>=4.2,<5.0 + django50: Django>=5.0b1,<5.1 djangomain: https://github.com/django/django/archive/main.tar.gz py{38,39,310,311}-postgres: psycopg2-binary pypy{38,39}-postgres: psycopg2cffi From cbbc20e5c866fa23aac11e4c76b018af7e3144c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Fri, 17 Nov 2023 15:04:26 +0100 Subject: [PATCH 16/41] Point License badge to the License on GitHub Instead of going to the root of the project on PyPI. Co-authored-by: irtazaakram --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index e5ae4112..3d3677bf 100644 --- a/README.rst +++ b/README.rst @@ -20,7 +20,7 @@ factory_boy :alt: Wheel status .. image:: https://img.shields.io/pypi/l/factory_boy.svg - :target: https://pypi.org/project/factory-boy/ + :target: https://github.com/FactoryBoy/factory_boy/blob/master/LICENSE :alt: License factory_boy is a fixtures replacement based on thoughtbot's `factory_bot `_. From 27c0196bdcff334caf505a1d115341641c54e68a Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Wed, 6 Dec 2023 14:23:43 +0100 Subject: [PATCH 17/41] GitHub Actions: Test PyPy 3.10 Also, upgrade GitHub Actions `checkout`. --- .github/workflows/test.yml | 4 ++-- tox.ini | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 06fe75c3..6a0985ea 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,8 +22,8 @@ jobs: - "3.10" - "3.11" - "3.12" - - "pypy-3.8" - "pypy-3.9" + - "pypy-3.10" database-type: - "sqlite" - "postgres" @@ -42,7 +42,7 @@ jobs: POSTGRES_PASSWORD: password steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 diff --git a/tox.ini b/tox.ini index efe79b67..256a88d6 100644 --- a/tox.ini +++ b/tox.ini @@ -5,10 +5,10 @@ envlist = docs examples linkcheck - py{38,39,310,311,py38,py39}-sqlite - py{38,39,310,311,py38,py39}-django32-mongo-alchemy-{sqlite,postgres} - py{38,39,310,311,py38,py39}-django40-mongo-alchemy-{sqlite,postgres} - py{38,39,310,311,py38,py39}-django41-mongo-alchemy-{sqlite,postgres} + py{38,39,310,311,py39,py310}-sqlite + py{38,39,310,311,py39,py310}-django32-mongo-alchemy-{sqlite,postgres} + py{38,39,310,311,py39,py310}-django40-mongo-alchemy-{sqlite,postgres} + py{38,39,310,311,py39,py310}-django41-mongo-alchemy-{sqlite,postgres} py310-djangomain-mongo-alchemy-{sqlite,postgres} [gh-actions] @@ -18,8 +18,8 @@ python = 3.10: py310 3.11: py311 3.12: py312 - pypy-3.8: pypy38 pypy-3.9: pypy39 + pypy-3.10: pypy310 [gh-actions:env] DATABASE_TYPE = @@ -39,10 +39,10 @@ deps = django32: Django>=3.2,<3.3 django41: Django>=4.1,<4.2 django42: Django>=4.2,<5.0 - django50: Django>=5.0b1,<5.1 + django50: Django>=5.0,<5.1 djangomain: https://github.com/django/django/archive/main.tar.gz py{38,39,310,311}-postgres: psycopg2-binary - pypy{38,39}-postgres: psycopg2cffi + pypy{39,310}-postgres: psycopg2cffi setenv = py: DJANGO_SETTINGS_MODULE=tests.djapp.settings From eda50227855c46c66006e74cad0b32a2d6f98044 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Wed, 6 Dec 2023 22:32:06 +0100 Subject: [PATCH 18/41] actions/setup-python@v5 --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6a0985ea..dc279ad4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -45,7 +45,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} cache: pip From 5aaf869bafcb7e9ce4eeda4db876cab163eee973 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Wed, 6 Dec 2023 14:58:12 +0100 Subject: [PATCH 19/41] tox.ini: Enable testing on Django v5.0 --- tox.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 256a88d6..ab6cb343 100644 --- a/tox.ini +++ b/tox.ini @@ -9,6 +9,7 @@ envlist = py{38,39,310,311,py39,py310}-django32-mongo-alchemy-{sqlite,postgres} py{38,39,310,311,py39,py310}-django40-mongo-alchemy-{sqlite,postgres} py{38,39,310,311,py39,py310}-django41-mongo-alchemy-{sqlite,postgres} + py{310,311,312}-django50-mongo-alchemy-{sqlite,postgres} py310-djangomain-mongo-alchemy-{sqlite,postgres} [gh-actions] @@ -41,7 +42,7 @@ deps = django42: Django>=4.2,<5.0 django50: Django>=5.0,<5.1 djangomain: https://github.com/django/django/archive/main.tar.gz - py{38,39,310,311}-postgres: psycopg2-binary + py{38,39,310,311,312}-postgres: psycopg2-binary pypy{39,310}-postgres: psycopg2cffi setenv = From 36eb3cad20617f8a8ecb569d914cc608df314309 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Wed, 6 Dec 2023 15:10:54 +0100 Subject: [PATCH 20/41] Django v4.2 now supports Python v3.12 --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index ab6cb343..314f40b0 100644 --- a/tox.ini +++ b/tox.ini @@ -5,7 +5,7 @@ envlist = docs examples linkcheck - py{38,39,310,311,py39,py310}-sqlite + py{38,39,310,311,312,py39,py310}-sqlite py{38,39,310,311,py39,py310}-django32-mongo-alchemy-{sqlite,postgres} py{38,39,310,311,py39,py310}-django40-mongo-alchemy-{sqlite,postgres} py{38,39,310,311,py39,py310}-django41-mongo-alchemy-{sqlite,postgres} From 8a50a8ed1badfe5675e93b9fc3e1ca4905862b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Thu, 21 Dec 2023 14:07:52 +0100 Subject: [PATCH 21/41] Update envlist for Django 4.2 and drop unsupported Django 4.0 --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 314f40b0..0f12ef01 100644 --- a/tox.ini +++ b/tox.ini @@ -7,8 +7,8 @@ envlist = linkcheck py{38,39,310,311,312,py39,py310}-sqlite py{38,39,310,311,py39,py310}-django32-mongo-alchemy-{sqlite,postgres} - py{38,39,310,311,py39,py310}-django40-mongo-alchemy-{sqlite,postgres} py{38,39,310,311,py39,py310}-django41-mongo-alchemy-{sqlite,postgres} + py{38,39,310,311,312,py39,py310}-django42-mongo-alchemy-{sqlite,postgres} py{310,311,312}-django50-mongo-alchemy-{sqlite,postgres} py310-djangomain-mongo-alchemy-{sqlite,postgres} From 69809cfc74cb31519d2558722bbce5a9123c2d11 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Thu, 21 Dec 2023 19:19:37 +0100 Subject: [PATCH 22/41] Disable py{py39,py310}-django42-mongo-alchemy-postgres --- tox.ini | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 0f12ef01..9010d318 100644 --- a/tox.ini +++ b/tox.ini @@ -8,7 +8,9 @@ envlist = py{38,39,310,311,312,py39,py310}-sqlite py{38,39,310,311,py39,py310}-django32-mongo-alchemy-{sqlite,postgres} py{38,39,310,311,py39,py310}-django41-mongo-alchemy-{sqlite,postgres} - py{38,39,310,311,312,py39,py310}-django42-mongo-alchemy-{sqlite,postgres} + py{38,39,310,311,312}-django42-mongo-alchemy-{sqlite,postgres} + py{py39,py310}-django42-mongo-alchemy-sqlite, + # py{py39,py310}-django42-mongo-alchemy-postgres # TODO: Fix me! py{310,311,312}-django50-mongo-alchemy-{sqlite,postgres} py310-djangomain-mongo-alchemy-{sqlite,postgres} From 68de8e75c6862588dd265d96567bcf34c079186b Mon Sep 17 00:00:00 2001 From: Serg Tereshchenko Date: Wed, 12 Jan 2022 18:41:00 +0200 Subject: [PATCH 23/41] Add basic typing support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only `Factory.build()` and `Factory.create()` are properly typed, provided the class is declared as `class UserFactory(Factory[User]):`. Relies on mypy for tests. Reviewed-By: Raphaël Barrois --- Makefile | 1 + docs/changelog.rst | 1 + factory/__init__.py | 6 ++++-- factory/base.py | 21 ++++++++++++++------- factory/django.py | 7 ++++--- factory/faker.py | 3 ++- setup.cfg | 1 + tests/test_typing.py | 31 +++++++++++++++++++++++++++++++ tox.ini | 1 + 9 files changed, 59 insertions(+), 13 deletions(-) create mode 100644 tests/test_typing.py diff --git a/Makefile b/Makefile index a31a9fb8..0dbae867 100644 --- a/Makefile +++ b/Makefile @@ -54,6 +54,7 @@ testall: # DOC: Run tests for the currently installed version # Remove cgi warning when dropping support for Django<=4.1. test: + mypy --ignore-missing-imports tests/test_typing.py python \ -b \ -X dev \ diff --git a/docs/changelog.rst b/docs/changelog.rst index b98bcfce..6397f107 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -10,6 +10,7 @@ ChangeLog - Add support for Django 4.2 - Add support for Django 5.0 - Add support for Python 3.12 +- :issue:`903`: Add basic typing annotations *Bugfix:* diff --git a/factory/__init__.py b/factory/__init__.py index bdc3ac0d..8b26dddc 100644 --- a/factory/__init__.py +++ b/factory/__init__.py @@ -1,5 +1,7 @@ # Copyright: See the LICENSE file. +import sys + from .base import ( BaseDictFactory, BaseListFactory, @@ -70,10 +72,10 @@ pass __author__ = 'Raphaël Barrois ' -try: +if sys.version_info >= (3, 8): # Python 3.8+ import importlib.metadata as importlib_metadata -except ImportError: +else: import importlib_metadata __version__ = importlib_metadata.version("factory_boy") diff --git a/factory/base.py b/factory/base.py index 36b2359a..8d499501 100644 --- a/factory/base.py +++ b/factory/base.py @@ -4,11 +4,14 @@ import collections import logging import warnings +from typing import Generic, List, Type, TypeVar from . import builder, declarations, enums, errors, utils logger = logging.getLogger('factory.generate') +T = TypeVar('T') + # Factory metaclasses @@ -405,7 +408,7 @@ def reset(self, next_value=0): self.seq = next_value -class BaseFactory: +class BaseFactory(Generic[T]): """Factory base support for sequences, attributes and stubs.""" # Backwards compatibility @@ -506,12 +509,12 @@ def _create(cls, model_class, *args, **kwargs): return model_class(*args, **kwargs) @classmethod - def build(cls, **kwargs): + def build(cls, **kwargs) -> T: """Build an instance of the associated class, with overridden attrs.""" return cls._generate(enums.BUILD_STRATEGY, kwargs) @classmethod - def build_batch(cls, size, **kwargs): + def build_batch(cls, size: int, **kwargs) -> List[T]: """Build a batch of instances of the given class, with overridden attrs. Args: @@ -523,12 +526,12 @@ def build_batch(cls, size, **kwargs): return [cls.build(**kwargs) for _ in range(size)] @classmethod - def create(cls, **kwargs): + def create(cls, **kwargs) -> T: """Create an instance of the associated class, with overridden attrs.""" return cls._generate(enums.CREATE_STRATEGY, kwargs) @classmethod - def create_batch(cls, size, **kwargs): + def create_batch(cls, size: int, **kwargs) -> List[T]: """Create a batch of instances of the given class, with overridden attrs. Args: @@ -627,18 +630,22 @@ def simple_generate_batch(cls, create, size, **kwargs): return cls.generate_batch(strategy, size, **kwargs) -class Factory(BaseFactory, metaclass=FactoryMetaClass): +class Factory(BaseFactory[T], metaclass=FactoryMetaClass): """Factory base with build and create support. This class has the ability to support multiple ORMs by using custom creation functions. """ + # Backwards compatibility + AssociatedClassError: Type[Exception] + class Meta(BaseMeta): pass -# Backwards compatibility +# Add the association after metaclass execution. +# Otherwise, AssociatedClassError would be detected as a declaration. Factory.AssociatedClassError = errors.AssociatedClassError diff --git a/factory/django.py b/factory/django.py index 9526b775..b53fd5b5 100644 --- a/factory/django.py +++ b/factory/django.py @@ -9,6 +9,7 @@ import logging import os import warnings +from typing import Dict, TypeVar from django.contrib.auth.hashers import make_password from django.core import files as django_files @@ -20,9 +21,9 @@ DEFAULT_DB_ALIAS = 'default' # Same as django.db.DEFAULT_DB_ALIAS +T = TypeVar("T") - -_LAZY_LOADS = {} +_LAZY_LOADS: Dict[str, object] = {} def get_model(app, model): @@ -72,7 +73,7 @@ def get_model_class(self): return self.model -class DjangoModelFactory(base.Factory): +class DjangoModelFactory(base.Factory[T]): """Factory for Django models. This makes sure that the 'sequence' field of created objects is a new id. diff --git a/factory/faker.py b/factory/faker.py index 6ed2e28c..88ae644c 100644 --- a/factory/faker.py +++ b/factory/faker.py @@ -14,6 +14,7 @@ class Meta: import contextlib +from typing import Dict import faker import faker.config @@ -47,7 +48,7 @@ def evaluate(self, instance, step, extra): subfaker = self._get_faker(locale) return subfaker.format(self.provider, **extra) - _FAKER_REGISTRY = {} + _FAKER_REGISTRY: Dict[str, faker.Faker] = {} _DEFAULT_LOCALE = faker.config.DEFAULT_LOCALE @classmethod diff --git a/setup.cfg b/setup.cfg index 3ba2b7aa..13b09b91 100644 --- a/setup.cfg +++ b/setup.cfg @@ -47,6 +47,7 @@ dev = Django flake8 isort + mypy Pillow SQLAlchemy sqlalchemy_utils diff --git a/tests/test_typing.py b/tests/test_typing.py new file mode 100644 index 00000000..c2f8b564 --- /dev/null +++ b/tests/test_typing.py @@ -0,0 +1,31 @@ +# Copyright: See the LICENSE file. + +import dataclasses +import unittest + +import factory + + +@dataclasses.dataclass +class User: + name: str + email: str + id: int + + +class TypingTests(unittest.TestCase): + + def test_simple_factory(self) -> None: + + class UserFactory(factory.Factory[User]): + name = "John Doe" + email = "john.doe@example.org" + id = 42 + + class Meta: + model = User + + result: User + result = UserFactory.build() + result = UserFactory.create() + self.assertEqual(result.name, "John Doe") diff --git a/tox.ini b/tox.ini index 9010d318..d842c759 100644 --- a/tox.ini +++ b/tox.ini @@ -35,6 +35,7 @@ passenv = POSTGRES_HOST POSTGRES_DATABASE deps = + mypy alchemy: SQLAlchemy alchemy: sqlalchemy_utils mongo: mongoengine From 121d3f1fa55ca6c2ee44a7dcfb96242b0133403f Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Tue, 6 Feb 2024 10:56:44 +0100 Subject: [PATCH 24/41] Remove unnecessary py3.8 compat path --- factory/__init__.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/factory/__init__.py b/factory/__init__.py index 8b26dddc..62042a2a 100644 --- a/factory/__init__.py +++ b/factory/__init__.py @@ -1,6 +1,6 @@ # Copyright: See the LICENSE file. -import sys +import importlib.metadata from .base import ( BaseDictFactory, @@ -72,10 +72,4 @@ pass __author__ = 'Raphaël Barrois ' -if sys.version_info >= (3, 8): - # Python 3.8+ - import importlib.metadata as importlib_metadata -else: - import importlib_metadata - -__version__ = importlib_metadata.version("factory_boy") +__version__ = importlib.metadata.version("factory_boy") From 159576788e6a553610bf11a045ed32021d34ebfe Mon Sep 17 00:00:00 2001 From: ThTomate Date: Sat, 2 Mar 2024 19:03:20 +0100 Subject: [PATCH 25/41] =?UTF-8?q?Docs:=20Remove=20old/wrong=20hint=20on=20?= =?UTF-8?q?create(=E2=80=A6)=20being=20used=20internally?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/reference.rst | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/docs/reference.rst b/docs/reference.rst index 238b56c4..130e194c 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -576,17 +576,6 @@ factory_boy supports two main strategies for generating instances, plus stubs. >>> obj.save() >>> return obj - .. OHAI_VIM* - - .. warning:: For backward compatibility reasons, the default behavior of - factory_boy is to call ``MyClass.objects.create(*args, **kwargs)`` - when using the ``create`` strategy. - - That policy will be used if the - :attr:`associated class ` has an ``objects`` - attribute *and* the :meth:`~Factory._create` classmethod of the - :class:`Factory` wasn't overridden. - .. function:: use_strategy(strategy) From dca3a16e9a4f731a3fe217e0ef6fa558796110aa Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Wed, 17 Jan 2024 08:59:23 +0100 Subject: [PATCH 26/41] Improve docstrings of `build`/`create` classmethods --- factory/base.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/factory/base.py b/factory/base.py index 8d499501..454513be 100644 --- a/factory/base.py +++ b/factory/base.py @@ -510,13 +510,18 @@ def _create(cls, model_class, *args, **kwargs): @classmethod def build(cls, **kwargs) -> T: - """Build an instance of the associated class, with overridden attrs.""" + """Build an instance of the associated class, with overridden attrs. + + The instance will not be saved and persisted to any datastore. + """ return cls._generate(enums.BUILD_STRATEGY, kwargs) @classmethod def build_batch(cls, size: int, **kwargs) -> List[T]: """Build a batch of instances of the given class, with overridden attrs. + The instances will not be saved and persisted to any datastore. + Args: size (int): the number of instances to build @@ -527,13 +532,18 @@ def build_batch(cls, size: int, **kwargs) -> List[T]: @classmethod def create(cls, **kwargs) -> T: - """Create an instance of the associated class, with overridden attrs.""" + """Create an instance of the associated class, with overridden attrs. + + The instance will be saved and persisted in the appropriate datastore. + """ return cls._generate(enums.CREATE_STRATEGY, kwargs) @classmethod def create_batch(cls, size: int, **kwargs) -> List[T]: """Create a batch of instances of the given class, with overridden attrs. + The instances will be saved and persisted in the appropriate datastore. + Args: size (int): the number of instances to create From bcfc8ec3f2f59b65b86b0f6218da6f8fd1e32657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Tue, 2 May 2023 22:46:20 +0200 Subject: [PATCH 27/41] Use dependabot to update GitHub actions Co-authored-by: Christian Clauss --- .github/dependabot.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..d4880e03 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + groups: + GitHub_Actions: + patterns: + - "*" # Group all Actions updates into a single larger pull request From 85d5cc189b158012016089057cdb0173ea996f76 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 22:45:29 +0000 Subject: [PATCH 28/41] Bump the github_actions group with 2 updates Bumps the github_actions group with 2 updates: [actions/checkout](https://github.com/actions/checkout) and [actions/setup-python](https://github.com/actions/setup-python). Updates `actions/checkout` from 3 to 4 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) Updates `actions/setup-python` from 4 to 5 - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major dependency-group: github_actions - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major dependency-group: github_actions ... Signed-off-by: dependabot[bot] --- .github/workflows/check.yml | 4 ++-- .github/workflows/linkcheck.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index a06aae03..6493ac71 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -25,10 +25,10 @@ jobs: TOXENV: ${{ matrix.tox-environment }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3' cache: pip diff --git a/.github/workflows/linkcheck.yml b/.github/workflows/linkcheck.yml index bbfdafd3..a8b6e7de 100644 --- a/.github/workflows/linkcheck.yml +++ b/.github/workflows/linkcheck.yml @@ -9,10 +9,10 @@ jobs: name: Linkcheck runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3' From a81beb4d49efc21542afb67b6b9c19c1442af89c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Tue, 19 Mar 2024 11:23:20 +0100 Subject: [PATCH 29/41] Drop support for Django 4.1 Upstream dropped support in January 2024. --- Makefile | 2 +- docs/changelog.rst | 1 + setup.cfg | 1 - tox.ini | 4 +--- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 0dbae867..9e0210bc 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,7 @@ testall: tox # DOC: Run tests for the currently installed version -# Remove cgi warning when dropping support for Django<=4.1. +# Remove cgi warning when dropping support for Django 3.2. test: mypy --ignore-missing-imports tests/test_typing.py python \ diff --git a/docs/changelog.rst b/docs/changelog.rst index 6397f107..c5fc0767 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -20,6 +20,7 @@ ChangeLog *Removed:* - Drop support for Django 4.0 +- Stop advertising and verifying support for Django 4.1 3.3.0 (2023-07-19) ------------------ diff --git a/setup.cfg b/setup.cfg index 13b09b91..4f93b98c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,6 @@ classifiers = Development Status :: 5 - Production/Stable Framework :: Django Framework :: Django :: 3.2 - Framework :: Django :: 4.1 Framework :: Django :: 4.2 Framework :: Django :: 5.0 Intended Audience :: Developers diff --git a/tox.ini b/tox.ini index d842c759..17dfdb50 100644 --- a/tox.ini +++ b/tox.ini @@ -7,7 +7,6 @@ envlist = linkcheck py{38,39,310,311,312,py39,py310}-sqlite py{38,39,310,311,py39,py310}-django32-mongo-alchemy-{sqlite,postgres} - py{38,39,310,311,py39,py310}-django41-mongo-alchemy-{sqlite,postgres} py{38,39,310,311,312}-django42-mongo-alchemy-{sqlite,postgres} py{py39,py310}-django42-mongo-alchemy-sqlite, # py{py39,py310}-django42-mongo-alchemy-postgres # TODO: Fix me! @@ -39,9 +38,8 @@ deps = alchemy: SQLAlchemy alchemy: sqlalchemy_utils mongo: mongoengine - django{32,41,42,50,main}: Pillow + django{32,42,50,main}: Pillow django32: Django>=3.2,<3.3 - django41: Django>=4.1,<4.2 django42: Django>=4.2,<5.0 django50: Django>=5.0,<5.1 djangomain: https://github.com/django/django/archive/main.tar.gz From 7ed1e5417e06c3d83e0495a67508b3868de53823 Mon Sep 17 00:00:00 2001 From: Jeff Widman Date: Tue, 23 Apr 2024 17:01:40 -0600 Subject: [PATCH 30/41] Don't duplicate CI checks on PRs By default the `pull_request` trigger will run on every push to the PR branch. So we are wasting CI minutes and electricity by running our CI checks twice on every push to a PR branch. Instead, this makes checks run 1x per PR, and then also run on every merge to `master`, to ensure that `master` stays green. This latter check is normally useless, but occasionally if there's drift of some kind between when CI runs on a PR and when it's merged, then this can help identify the issue. A more common pattern is simply to only run on PR's, but given we haven't previously been enforcing "only merge via PR" (https://github.com/FactoryBoy/factory_boy/issues/1073) I thought might be best to keep checking `master` as well until that's changed. The one thing we stop doing with this change is checking on push to branches that aren't PR branches... ie, if a maintainer is working on testing something. But they may not even care about running CI on this branch, and if they do, it's easy to run the tests locally, or open a draft PR...So I don't see the point of preserving that behavior. --- .github/workflows/check.yml | 6 ++++-- .github/workflows/test.yml | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 6493ac71..9d837066 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -1,8 +1,10 @@ name: Check on: - - push - - pull_request + push: + branches: + - "master" + pull_request: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dc279ad4..9d734533 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,8 +1,10 @@ name: Test on: - - push - - pull_request + push: + branches: + - "master" + pull_request: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} From 01c0a73e5c28bf6e6e35def5caf286ec8d292168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Skar=C5=BCy=C5=84ski?= Date: Tue, 28 Mar 2023 16:13:12 +0200 Subject: [PATCH 31/41] test: add regression test for #965 --- tests/test_regression.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/test_regression.py b/tests/test_regression.py index a9ea1c66..2cca0bda 100644 --- a/tests/test_regression.py +++ b/tests/test_regression.py @@ -51,3 +51,24 @@ class Params: unknown_author = AuthorFactory(unknown=True) self.assertEqual("", unknown_author.fullname) + + def test_evaluated_without_locale(self): + """Regression test for `KeyError: 'locale'` raised in `evaluate`. + + See #965 + + """ + class AuthorFactory(factory.Factory): + fullname = factory.Faker("name") + pseudonym = factory.Maybe( + decider=factory.Faker("pybool"), + yes_declaration="yes", + no_declaration="no", + ) + + class Meta: + model = Author + + author = AuthorFactory() + + self.assertIn(author.pseudonym, ["yes", "no"]) From d6349de49b647bb8b7f5d620757a919463f27f7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Tue, 18 Apr 2023 21:38:00 +0200 Subject: [PATCH 32/41] Call evaluate_pre() instead of evaluate() on Maybe decider e19142cb6e049e079cd4af36775715fcda47cb8c introduced evaluate_pre to perform context unrolling before to call the semi-public evaluate(). The Maybe decider was not updated at that time, but its context need to be unrolled. --- factory/declarations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/factory/declarations.py b/factory/declarations.py index 70abe35c..951b45f3 100644 --- a/factory/declarations.py +++ b/factory/declarations.py @@ -536,7 +536,7 @@ def evaluate_post(self, instance, step, overrides): return target def evaluate_pre(self, instance, step, overrides): - choice = self.decider.evaluate(instance=instance, step=step, extra={}) + choice = self.decider.evaluate_pre(instance=instance, step=step, overrides={}) target = self.yes if choice else self.no # The value can't be POST_INSTANTIATION, checked in __init__; # evaluate it as `evaluate_pre` From 8aaa29be8ad67abf5649a55d88478373c7db44cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Sat, 17 Aug 2024 17:47:44 +0200 Subject: [PATCH 33/41] Improve readability of alchemy checker Avoid broad `except AttributeError`. --- factory/alchemy.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/factory/alchemy.py b/factory/alchemy.py index f934ce5d..e782fbd8 100644 --- a/factory/alchemy.py +++ b/factory/alchemy.py @@ -24,11 +24,8 @@ def _check_sqlalchemy_session_persistence(self, meta, value): @staticmethod def _check_has_sqlalchemy_session_set(meta, value): - try: - if value and meta.sqlalchemy_session: - raise RuntimeError("Provide either a sqlalchemy_session or a sqlalchemy_session_factory, not both") - except AttributeError: - pass + if value is not None and getattr(meta, "sqlalchemy_session", None) is not None: + raise RuntimeError("Provide either a sqlalchemy_session or a sqlalchemy_session_factory, not both") def _build_default_options(self): return super()._build_default_options() + [ From f8456f5e3965bd1d9ea1dd5b6299e0d73783c382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Sun, 18 Aug 2024 18:47:44 +0200 Subject: [PATCH 34/41] Remove various (obsolete) warning exemption flags Most of those warnings are no longer an issue with current dependency versions. --- Makefile | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Makefile b/Makefile index 9e0210bc..4d52a184 100644 --- a/Makefile +++ b/Makefile @@ -59,12 +59,6 @@ test: -b \ -X dev \ -Werror \ - -Wdefault:"the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses":DeprecationWarning:distutils: \ - -Wdefault:"Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3, and in 3.9 it will stop working":DeprecationWarning:: \ - -Wdefault:"set_output_charset() is deprecated":DeprecationWarning:: \ - -Wdefault:"parameter codeset is deprecated":DeprecationWarning:: \ - -Wdefault:"'cgi' is deprecated and slated for removal in Python 3.13":DeprecationWarning:: \ - -Wdefault:"datetime.datetime.utcfromtimestamp() is deprecated and scheduled for removal in a future version.":DeprecationWarning:: \ -m unittest # DOC: Test the examples From 819acce99374b55ba9c57a224669b7c8920ed474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Sun, 18 Aug 2024 18:50:23 +0200 Subject: [PATCH 35/41] Run the test suite against Mongomock See: #1083 Closes: #1081 --- .github/workflows/test.yml | 5 ----- Makefile | 3 +++ docs/changelog.rst | 1 + docs/spelling_wordlist.txt | 1 + setup.cfg | 1 + tests/test_mongoengine.py | 3 +++ tox.ini | 4 +++- 7 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9d734533..29a7e881 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,11 +31,6 @@ jobs: - "postgres" services: - mongodb: - image: mongo - ports: - - 27017:27017 - postgresdb: image: postgres:alpine ports: diff --git a/Makefile b/Makefile index 4d52a184..778fd785 100644 --- a/Makefile +++ b/Makefile @@ -59,6 +59,9 @@ test: -b \ -X dev \ -Werror \ + -Wignore:::mongomock: \ + -Wignore:::mongomock.__version__: \ + -Wignore:::pkg_resources: \ -m unittest # DOC: Test the examples diff --git a/docs/changelog.rst b/docs/changelog.rst index c5fc0767..10abcc52 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -11,6 +11,7 @@ ChangeLog - Add support for Django 5.0 - Add support for Python 3.12 - :issue:`903`: Add basic typing annotations +- Run the test suite against ``mongomock`` instead of an actual MongoDB server *Bugfix:* diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt index 95d4be7f..1c62c8cd 100644 --- a/docs/spelling_wordlist.txt +++ b/docs/spelling_wordlist.txt @@ -19,6 +19,7 @@ kwargs metaclass misconfiguration Mogo +MongoDB mongoengine pre prepend diff --git a/setup.cfg b/setup.cfg index 4f93b98c..1882a205 100644 --- a/setup.cfg +++ b/setup.cfg @@ -51,6 +51,7 @@ dev = SQLAlchemy sqlalchemy_utils mongoengine + mongomock wheel>=0.32.0 tox zest.releaser[recommended] diff --git a/tests/test_mongoengine.py b/tests/test_mongoengine.py index bc930fca..ea1ae687 100644 --- a/tests/test_mongoengine.py +++ b/tests/test_mongoengine.py @@ -10,6 +10,8 @@ except ImportError: raise unittest.SkipTest("mongodb tests disabled.") +import mongomock + import factory from factory.mongoengine import MongoEngineFactory @@ -52,6 +54,7 @@ def setUpClass(cls): db=cls.db_name, host=cls.db_host, port=cls.db_port, + mongo_client_class=mongomock.MongoClient, # PyMongo>=2.1 requires an explicit read_preference. read_preference=mongo_rp.ReadPreference.PRIMARY, # PyMongo>=2.1 has a 20s timeout, use 100ms instead diff --git a/tox.ini b/tox.ini index 17dfdb50..ca21be5c 100644 --- a/tox.ini +++ b/tox.ini @@ -30,7 +30,6 @@ DATABASE_TYPE = [testenv] passenv = - MONGO_HOST POSTGRES_HOST POSTGRES_DATABASE deps = @@ -38,6 +37,9 @@ deps = alchemy: SQLAlchemy alchemy: sqlalchemy_utils mongo: mongoengine + mongo: mongomock + # mongomock imports pkg_resources, provided by setuptools. + mongo: setuptools>=66.1.1 django{32,42,50,main}: Pillow django32: Django>=3.2,<3.3 django42: Django>=4.2,<5.0 From c2188f714361de781acaefe45aa05cc4d59f1756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Sun, 18 Aug 2024 18:59:50 +0200 Subject: [PATCH 36/41] Stop testing against PostgreSQL factory_boy never interacts with the database directly; all access is mediated through mature ORMs (Django, SQLAlchemy). Any difference in behaviour regarding the databases would have to be handled by those ORMs, not on our level. This reduces the size of the test matrix, and simplifies the test setup code. See: #1077 --- .github/workflows/test.yml | 13 +----------- README.rst | 2 -- setup.cfg | 1 - tests/alchemyapp/models.py | 25 +---------------------- tests/djapp/settings_pg.py | 41 -------------------------------------- tests/test_alchemy.py | 13 ------------ tox.ini | 25 ++++++----------------- 7 files changed, 8 insertions(+), 112 deletions(-) delete mode 100644 tests/djapp/settings_pg.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 29a7e881..2b381638 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ concurrency: jobs: tests: - name: Python ${{ matrix.python-version }}, Database ${{ matrix.database-type }} + name: Python ${{ matrix.python-version }} runs-on: ubuntu-latest strategy: @@ -26,17 +26,6 @@ jobs: - "3.12" - "pypy-3.9" - "pypy-3.10" - database-type: - - "sqlite" - - "postgres" - - services: - postgresdb: - image: postgres:alpine - ports: - - 5432:5432 - env: - POSTGRES_PASSWORD: password steps: - uses: actions/checkout@v4 diff --git a/README.rst b/README.rst index 3d3677bf..cd926690 100644 --- a/README.rst +++ b/README.rst @@ -405,11 +405,9 @@ To test with a specific framework version, you may use a ``tox`` target: # run tests inside a specific environment (django) $ tox -e py310-djangomain - $ tox -e py310-djangomain-postgres # run tests inside a specific environment (alchemy) $ tox -e py310-alchemy - $ tox -e py310-alchemy-postgres # run tests inside a specific environment (mongoengine) $ tox -e py310-mongo diff --git a/setup.cfg b/setup.cfg index 1882a205..d22ae33e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -49,7 +49,6 @@ dev = mypy Pillow SQLAlchemy - sqlalchemy_utils mongoengine mongomock wheel>=0.32.0 diff --git a/tests/alchemyapp/models.py b/tests/alchemyapp/models.py index 20e60aab..42e05176 100644 --- a/tests/alchemyapp/models.py +++ b/tests/alchemyapp/models.py @@ -2,34 +2,11 @@ """Helpers for testing SQLAlchemy apps.""" -import os from sqlalchemy import Column, Integer, Unicode, create_engine from sqlalchemy.orm import declarative_base, scoped_session, sessionmaker -try: - import psycopg2 # noqa: F401 - USING_POSTGRES = True -except ImportError: - try: - # pypy does not support `psycopg2` or `psycopg2-binary` - # This is a package that only gets installed with pypy, and it needs to be - # initialized for it to work properly. It mimic `psycopg2` 1-to-1 - from psycopg2cffi import compat - compat.register() - USING_POSTGRES = True - except ImportError: - USING_POSTGRES = False - -if USING_POSTGRES: - pg_database = 'alch_' + os.environ.get('POSTGRES_DATABASE', 'factory_boy_test') - pg_user = os.environ.get('POSTGRES_USER', 'postgres') - pg_password = os.environ.get('POSTGRES_PASSWORD', 'password') - pg_host = os.environ.get('POSTGRES_HOST', 'localhost') - pg_port = os.environ.get('POSTGRES_PORT', '5432') - engine_name = f'postgresql+psycopg2://{pg_user}:{pg_password}@{pg_host}:{pg_port}/{pg_database}' -else: - engine_name = 'sqlite://' +engine_name = 'sqlite://' session = scoped_session(sessionmaker()) engine = create_engine(engine_name) diff --git a/tests/djapp/settings_pg.py b/tests/djapp/settings_pg.py deleted file mode 100644 index de54922c..00000000 --- a/tests/djapp/settings_pg.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright: See the LICENSE file. - -"""Settings for factory_boy/Django tests.""" - -import os - -from .settings import * # noqa: F401, F403 - -try: - # pypy does not support `psycopg2` or `psycopg2-binary` - # This is a package that only gets installed with pypy, and it needs to be - # initialized for it to work properly. It mimic `psycopg2` 1-to-1 - from psycopg2cffi import compat - compat.register() -except ImportError: - pass - -postgres_user = os.environ.get('POSTGRES_USER', 'postgres') -postgres_name = os.environ.get('POSTGRES_DATABASE', 'factory_boy_test') -postgres_password = os.environ.get('POSTGRES_PASSWORD', 'password') -postgres_host = os.environ.get('POSTGRES_HOST', 'localhost') -postgres_port = os.environ.get('POSTGRES_PORT', '5432') - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': postgres_name, - 'USER': postgres_user, - 'PASSWORD': postgres_password, - 'HOST': postgres_host, - 'PORT': postgres_port, - }, - 'replica': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': postgres_name + '_rp', - 'USER': postgres_user, - 'PASSWORD': postgres_password, - 'HOST': postgres_host, - 'PORT': postgres_port, - } -} diff --git a/tests/test_alchemy.py b/tests/test_alchemy.py index 19e4f5ee..6b568ce4 100644 --- a/tests/test_alchemy.py +++ b/tests/test_alchemy.py @@ -10,8 +10,6 @@ except ImportError: raise unittest.SkipTest("sqlalchemy tests disabled.") -from sqlalchemy_utils import create_database, database_exists, drop_database - import factory from factory.alchemy import SQLAlchemyModelFactory @@ -77,17 +75,6 @@ class Meta: text = factory.Sequence(lambda n: "text%s" % n) -if models.USING_POSTGRES: - # sqlite test database gets created/destroyed automatically, postgres does not. - - def setUpModule(): - if not database_exists(models.engine.url): - create_database(models.engine.url) - - def tearDownModule(): - drop_database(models.engine.url) - - class TransactionTestCase(unittest.TestCase): def setUp(self): models.Base.metadata.create_all(models.engine) diff --git a/tox.ini b/tox.ini index ca21be5c..4d5438e1 100644 --- a/tox.ini +++ b/tox.ini @@ -5,13 +5,12 @@ envlist = docs examples linkcheck - py{38,39,310,311,312,py39,py310}-sqlite - py{38,39,310,311,py39,py310}-django32-mongo-alchemy-{sqlite,postgres} - py{38,39,310,311,312}-django42-mongo-alchemy-{sqlite,postgres} - py{py39,py310}-django42-mongo-alchemy-sqlite, - # py{py39,py310}-django42-mongo-alchemy-postgres # TODO: Fix me! - py{310,311,312}-django50-mongo-alchemy-{sqlite,postgres} - py310-djangomain-mongo-alchemy-{sqlite,postgres} + py{38,39,310,311,312,py39,py310} + py{38,39,310,311,py39,py310}-django32-mongo-alchemy + py{38,39,310,311,312}-django42-mongo-alchemy + py{py39,py310}-django42-mongo-alchemy + py{310,311,312}-django50-mongo-alchemy + py310-djangomain-mongo-alchemy [gh-actions] python = @@ -23,19 +22,10 @@ python = pypy-3.9: pypy39 pypy-3.10: pypy310 -[gh-actions:env] -DATABASE_TYPE = - sqlite: sqlite - postgres: postgres - [testenv] -passenv = - POSTGRES_HOST - POSTGRES_DATABASE deps = mypy alchemy: SQLAlchemy - alchemy: sqlalchemy_utils mongo: mongoengine mongo: mongomock # mongomock imports pkg_resources, provided by setuptools. @@ -45,12 +35,9 @@ deps = django42: Django>=4.2,<5.0 django50: Django>=5.0,<5.1 djangomain: https://github.com/django/django/archive/main.tar.gz - py{38,39,310,311,312}-postgres: psycopg2-binary - pypy{39,310}-postgres: psycopg2cffi setenv = py: DJANGO_SETTINGS_MODULE=tests.djapp.settings - postgres: DJANGO_SETTINGS_MODULE=tests.djapp.settings_pg allowlist_externals = make commands = make test From ce3911451b4571903d5e74871e911739eaa9481c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Sun, 18 Aug 2024 19:07:28 +0200 Subject: [PATCH 37/41] Stop testing for Django 3.2 Upstream support has been dropped in April 2024. --- docs/changelog.rst | 3 +-- setup.cfg | 1 - tox.ini | 4 +--- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 10abcc52..0b8e4dd6 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -20,8 +20,7 @@ ChangeLog *Removed:* -- Drop support for Django 4.0 -- Stop advertising and verifying support for Django 4.1 +- Stop advertising and verifying support for Django 3.2, 4.0, 4.1 3.3.0 (2023-07-19) ------------------ diff --git a/setup.cfg b/setup.cfg index d22ae33e..dd1c00e9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -15,7 +15,6 @@ license = MIT classifiers = Development Status :: 5 - Production/Stable Framework :: Django - Framework :: Django :: 3.2 Framework :: Django :: 4.2 Framework :: Django :: 5.0 Intended Audience :: Developers diff --git a/tox.ini b/tox.ini index 4d5438e1..95b88d47 100644 --- a/tox.ini +++ b/tox.ini @@ -6,7 +6,6 @@ envlist = examples linkcheck py{38,39,310,311,312,py39,py310} - py{38,39,310,311,py39,py310}-django32-mongo-alchemy py{38,39,310,311,312}-django42-mongo-alchemy py{py39,py310}-django42-mongo-alchemy py{310,311,312}-django50-mongo-alchemy @@ -30,8 +29,7 @@ deps = mongo: mongomock # mongomock imports pkg_resources, provided by setuptools. mongo: setuptools>=66.1.1 - django{32,42,50,main}: Pillow - django32: Django>=3.2,<3.3 + django{42,50,main}: Pillow django42: Django>=4.2,<5.0 django50: Django>=5.0,<5.1 djangomain: https://github.com/django/django/archive/main.tar.gz From a7d06b93a5ad87c3222be8b4380c83af8d4bb3ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Sun, 18 Aug 2024 19:11:54 +0200 Subject: [PATCH 38/41] Target Django 5.x tests at version 5.1 --- docs/changelog.rst | 2 +- setup.cfg | 2 +- tox.ini | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 0b8e4dd6..9df2a88e 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -8,7 +8,7 @@ ChangeLog *New:* - Add support for Django 4.2 -- Add support for Django 5.0 +- Add support for Django 5.1 - Add support for Python 3.12 - :issue:`903`: Add basic typing annotations - Run the test suite against ``mongomock`` instead of an actual MongoDB server diff --git a/setup.cfg b/setup.cfg index dd1c00e9..ccbfe6fb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ classifiers = Development Status :: 5 - Production/Stable Framework :: Django Framework :: Django :: 4.2 - Framework :: Django :: 5.0 + Framework :: Django :: 5.1 Intended Audience :: Developers License :: OSI Approved :: MIT License Operating System :: OS Independent diff --git a/tox.ini b/tox.ini index 95b88d47..2bca8a08 100644 --- a/tox.ini +++ b/tox.ini @@ -8,7 +8,8 @@ envlist = py{38,39,310,311,312,py39,py310} py{38,39,310,311,312}-django42-mongo-alchemy py{py39,py310}-django42-mongo-alchemy - py{310,311,312}-django50-mongo-alchemy + py{310,311,312}-django51-mongo-alchemy + pypy310-django51-mongo-alchemy py310-djangomain-mongo-alchemy [gh-actions] @@ -29,9 +30,9 @@ deps = mongo: mongomock # mongomock imports pkg_resources, provided by setuptools. mongo: setuptools>=66.1.1 - django{42,50,main}: Pillow + django{42,51,main}: Pillow django42: Django>=4.2,<5.0 - django50: Django>=5.0,<5.1 + django51: Django>=5.1,<5.2 djangomain: https://github.com/django/django/archive/main.tar.gz setenv = From 0ee3c9d6458d004c6229014e31f762f1e5d1b733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Sun, 18 Aug 2024 21:36:17 +0200 Subject: [PATCH 39/41] Enable "nitpicky" mode on Sphinx And fix related missing references. Closes: #929 --- Makefile | 2 +- docs/changelog.rst | 102 +++++++++++++++++++++--------------------- docs/conf.py | 6 ++- docs/examples.rst | 2 +- docs/fuzzy.rst | 2 +- docs/ideas.rst | 2 +- docs/internals.rst | 8 ++-- docs/introduction.rst | 6 +-- docs/orms.rst | 53 +++++++++++----------- docs/recipes.rst | 8 ++-- docs/reference.rst | 93 ++++++++++++++++++++------------------ 11 files changed, 146 insertions(+), 138 deletions(-) diff --git a/Makefile b/Makefile index 778fd785..f7404eba 100644 --- a/Makefile +++ b/Makefile @@ -104,7 +104,7 @@ TAGS: # DOC: Compile the documentation doc: - $(MAKE) -C $(DOC_DIR) SPHINXOPTS=-W html + $(MAKE) -C $(DOC_DIR) SPHINXOPTS="-n -W" html linkcheck: $(MAKE) -C $(DOC_DIR) linkcheck diff --git a/docs/changelog.rst b/docs/changelog.rst index 9df2a88e..fd49f447 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -40,16 +40,15 @@ ChangeLog - Make :meth:`~factory.django.mute_signals` mute signals during post-generation. - - :issue:`775`: Change the signature for :meth:`~factory.alchemy.SQLAlchemyModelFactory._save` and - :meth:`~factory.alchemy.SQLAlchemyModelFactory._get_or_create` to avoid argument names clashes with a field named - ``session``. + - :issue:`775`: Change the signature for :class:`~factory.alchemy.SQLAlchemyModelFactory`'s ``_save`` and + ``_get_or_create`` methods to avoid argument names clashes with a field named ``session``. *Deprecated:* - :class:`~factory.django.DjangoModelFactory` will stop issuing a second call to :meth:`~django.db.models.Model.save` on the created instance when :ref:`post-generation-hooks` return a value. - To help with the transition, :class:`factory.django.DjangoModelFactory._after_postgeneration` raises a + To help with the transition, :class:`factory.django.DjangoModelFactory`'s ``_after_postgeneration`` raises a :class:`DeprecationWarning` when calling :meth:`~django.db.models.Model.save`. Inspect your :class:`~factory.django.DjangoModelFactory` subclasses: @@ -61,7 +60,7 @@ ChangeLog - call :meth:`django.db.models.Model.save` in the :class:`~factory.PostGeneration` hook that modifies the instance, or - - override :class:`~factory.django.DjangoModelFactory._after_postgeneration` to + - override the :class:`~factory.Factory._after_postgeneration` method to :meth:`~django.db.models.Model.save` the instance. *Removed:* @@ -82,9 +81,8 @@ ChangeLog - Do not override signals receivers registered in a :meth:`~factory.django.mute_signals` context. - - :issue:`775`: Change the signature for :meth:`~factory.alchemy.SQLAlchemyModelFactory._save` and - :meth:`~factory.alchemy.SQLAlchemyModelFactory._get_or_create` to avoid argument names clashes with a field named - ``session``. + - :issue:`775`: Change the signature for :class:`~factory.alchemy.SQLAlchemyModelFactory`'s ``_save`` and + ``_get_or_create`` methods to avoid argument names clashes with a field named ``session``. 3.2.0 (2020-12-28) ------------------ @@ -138,7 +136,7 @@ Breaking changes The following aliases were removed: -+------------------------------------------------+---------------------------------------------------+ ++================================================+===================================================+ | Broken alias | New import | +================================================+===================================================+ | ``from factory import DjangoModelFactory`` | ``from factory.django import DjangoModelFactory`` | @@ -150,7 +148,7 @@ The following aliases were removed: | ``from factory.fuzzy import set_random_state`` | ``from factory.random import set_random_state`` | +------------------------------------------------+---------------------------------------------------+ | ``from factory.fuzzy import reseed_random`` | ``from factory.random import reseed_random`` | -+------------------------------------------------+---------------------------------------------------+ ++================================================+===================================================+ *Removed:* @@ -167,14 +165,14 @@ The following aliases were removed: - Add support for Python 3.8 - Add support for Django 2.2 and 3.0 - - Report misconfiguration when a :py:class:`~factory.Factory` is used as the :py:attr:`~factory.Factory.model` for another :py:class:`~factory.Factory`. + - Report misconfiguration when a :py:class:`~factory.Factory` is used as the :py:attr:`~factory.FactoryOptions.model` for another :py:class:`~factory.Factory`. - Allow configuring the color palette of :py:class:`~factory.django.ImageField`. - - :py:meth:`get_random_state()` now represents the state of Faker and ``factory_boy`` fuzzy attributes. + - :py:meth:`~factory.random.get_random_state()` now represents the state of Faker and ``factory_boy`` fuzzy attributes. - Add SQLAlchemy ``get_or_create`` support *Improvements:* - - :issue:`561`: Display a developer-friendly error message when providing a model instead of a factory in a :class:`~factory.declarations.SubFactory` class. + - :issue:`561`: Display a developer-friendly error message when providing a model instead of a factory in a :class:`~factory.SubFactory` class. *Bug fix:* @@ -192,11 +190,11 @@ The following aliases were removed: - Add support for Python 3.7 - Add support for Django 2.1 - - Add :attr:`~factory.fuzzy.FuzzyChoice.getter` to :class:`~factory.fuzzy.FuzzyChoice` that mimics + - Add ``getter`` to :class:`~factory.fuzzy.FuzzyChoice` that mimics the behavior of ``getter`` in :class:`~factory.Iterator` - - Make the ``extra_kwargs`` parameter of :meth:`~factory.faker.Faker.generate` optional + - Make the ``extra_kwargs`` parameter of :class:`~factory.Faker`'s ``generate`` method optional - Add :class:`~factory.RelatedFactoryList` class for one-to-many support, thanks `Sean Harrington `_. - - Make the `locale` argument for :class:`~factory.faker.Faker` keyword-only + - Make the `locale` argument for :class:`~factory.Faker` keyword-only *Bug fix:* @@ -219,7 +217,7 @@ The following aliases were removed: - Fix :class:`~factory.fuzzy.FuzzyFloat` to return a 15 decimal digits precision float by default - :issue:`451`: Restore :class:`~factory.django.FileField` to a - :class:`~factory.declarations.ParameteredAttribute`, relying on composition to parse the provided parameters. + ``factory.declarations.ParameteredAttribute``, relying on composition to parse the provided parameters. - :issue:`389`: Fix random state management with ``faker``. - :issue:`466`: Restore mixing :class:`~factory.Trait` and :meth:`~factory.post_generation`. @@ -233,7 +231,7 @@ The following aliases were removed: *New:* - - :issue:`397`: Allow a :class:`factory.Maybe` to contain a :class:`~factory.PostGenerationDeclaration`. + - :issue:`397`: Allow a :class:`factory.Maybe` to contain a :class:`~factory.PostGeneration` declaration. This also applies to :class:`factory.Trait`, since they use a :class:`factory.Maybe` declaration internally. .. _v2.9.2: @@ -355,8 +353,8 @@ corner cases and weird behaviors. - :issue:`201`: Properly handle custom Django managers when dealing with abstract Django models. - :issue:`212`: Fix :meth:`factory.django.mute_signals` to handle Django's signal caching - - :issue:`228`: Don't load :func:`django.apps.apps.get_model()` until required - - :pr:`219`: Stop using :meth:`mogo.model.Model.new()`, deprecated 4 years ago. + - :issue:`228`: Don't load ``django.apps.apps.get_model()`` until required + - :pr:`219`: Stop using ``mogo.model.Model.new()``, deprecated 4 years ago. .. _v2.5.2: @@ -465,19 +463,19 @@ This takes care of all ``FACTORY_FOR`` occurrences; the files containing other a For :class:`factory.Factory`: - * Rename :attr:`~factory.Factory.FACTORY_FOR` to :attr:`~factory.FactoryOptions.model` - * Rename :attr:`~factory.Factory.ABSTRACT_FACTORY` to :attr:`~factory.FactoryOptions.abstract` - * Rename :attr:`~factory.Factory.FACTORY_STRATEGY` to :attr:`~factory.FactoryOptions.strategy` - * Rename :attr:`~factory.Factory.FACTORY_ARG_PARAMETERS` to :attr:`~factory.FactoryOptions.inline_args` - * Rename :attr:`~factory.Factory.FACTORY_HIDDEN_ARGS` to :attr:`~factory.FactoryOptions.exclude` + * Rename ``factory.Factory.FACTORY_FOR`` to :attr:`~factory.FactoryOptions.model` + * Rename ``factory.Factory.ABSTRACT_FACTORY`` to :attr:`~factory.FactoryOptions.abstract` + * Rename ``factory.Factory.FACTORY_STRATEGY`` to :attr:`~factory.FactoryOptions.strategy` + * Rename ``factory.Factory.FACTORY_ARG_PARAMETERS`` to :attr:`~factory.FactoryOptions.inline_args` + * Rename ``factory.Factory.FACTORY_HIDDEN_ARGS`` to :attr:`~factory.FactoryOptions.exclude` For :class:`factory.django.DjangoModelFactory`: - * Rename :attr:`~factory.django.DjangoModelFactory.FACTORY_DJANGO_GET_OR_CREATE` to :attr:`~factory.django.DjangoOptions.django_get_or_create` + * Rename ``factory.django.DjangoModelFactory.FACTORY_DJANGO_GET_OR_CREATE`` to :attr:`~factory.django.DjangoOptions.django_get_or_create` For :class:`factory.alchemy.SQLAlchemyModelFactory`: - * Rename :attr:`~factory.alchemy.SQLAlchemyModelFactory.FACTORY_SESSION` to :attr:`~factory.alchemy.SQLAlchemyOptions.sqlalchemy_session` + * Rename ``factory.alchemy.SQLAlchemyModelFactory.FACTORY_SESSION`` to :attr:`~factory.alchemy.SQLAlchemyOptions.sqlalchemy_session` .. _v2.3.1: @@ -539,9 +537,9 @@ This takes care of all ``FACTORY_FOR`` occurrences; the files containing other a *New:* - - The :class:`~factory.Factory.ABSTRACT_FACTORY` keyword is now optional, and automatically set + - The ``factory.Factory.ABSTRACT_FACTORY`` keyword is now optional, and automatically set to ``True`` if neither the :class:`~factory.Factory` subclass nor its parent declare the - :class:`~factory.Factory.FACTORY_FOR` attribute (:issue:`74`) + ``factory.Factory.FACTORY_FOR`` attribute (:issue:`74`) .. _v2.1.1: @@ -562,8 +560,8 @@ This takes care of all ``FACTORY_FOR`` occurrences; the files containing other a - Add :class:`~factory.fuzzy.FuzzyDate` thanks to `saulshanabrook `_ - Add :class:`~factory.fuzzy.FuzzyDateTime` and :class:`~factory.fuzzy.FuzzyNaiveDateTime`. - - Add a :attr:`~factory.builder.Resolver.factory_parent` attribute to the - :class:`~factory.builder.Resolver` passed to :class:`~factory.LazyAttribute`, in order to access + - Add a ``factory_parent`` attribute to the + ``factory.builder.Resolver`` passed to :class:`~factory.LazyAttribute`, in order to access fields defined in wrapping factories. - Move :class:`~factory.django.DjangoModelFactory` and :class:`~factory.mogo.MogoFactory` to their own modules (:mod:`factory.django` and :mod:`factory.mogo`) @@ -594,7 +592,7 @@ This takes care of all ``FACTORY_FOR`` occurrences; the files containing other a *New:* - - When :attr:`~factory.django.DjangoModelFactory.FACTORY_DJANGO_GET_OR_CREATE` is + - When ``factory.django.DjangoModelFactory.FACTORY_DJANGO_GET_OR_CREATE`` is empty, use ``Model.objects.create()`` instead of ``Model.objects.get_or_create``. @@ -606,7 +604,7 @@ This takes care of all ``FACTORY_FOR`` occurrences; the files containing other a *New:* - Don't push ``defaults`` to ``get_or_create`` when - :attr:`~factory.django.DjangoModelFactory.FACTORY_DJANGO_GET_OR_CREATE` is not set. + ``factory.django.DjangoModelFactory.FACTORY_DJANGO_GET_OR_CREATE`` is not set. .. _v2.0.0: @@ -618,11 +616,11 @@ This takes care of all ``FACTORY_FOR`` occurrences; the files containing other a - Allow overriding the base factory class for :func:`~factory.make_factory` and friends. - Add support for Python3 (Thanks to `kmike `_ and `nkryptic `_) - - The default :attr:`~factory.Sequence.type` for :class:`~factory.Sequence` is now :obj:`int` - - Fields listed in :attr:`~factory.Factory.FACTORY_HIDDEN_ARGS` won't be passed to + - The default type for :class:`~factory.Sequence` is now :obj:`int` + - Fields listed in ``factory.Factory.FACTORY_HIDDEN_ARGS`` won't be passed to the associated class' constructor - Add support for ``get_or_create`` in :class:`~factory.django.DjangoModelFactory`, - through :attr:`~factory.django.DjangoModelFactory.FACTORY_DJANGO_GET_OR_CREATE`. + through ``factory.django.DjangoModelFactory.FACTORY_DJANGO_GET_OR_CREATE``. - Add support for :mod:`~factory.fuzzy` attribute definitions. - The :class:`Sequence` counter can be overridden when calling a generating function - Add :class:`~factory.Dict` and :class:`~factory.List` declarations (Closes :issue:`18`). @@ -630,12 +628,12 @@ This takes care of all ``FACTORY_FOR`` occurrences; the files containing other a *Removed:* - Remove associated class discovery - - Remove :class:`~factory.InfiniteIterator` and :func:`~factory.infinite_iterator` - - Remove :class:`~factory.CircularSubFactory` + - Remove ``factory.InfiniteIterator`` and ``factory.infinite_iterator`` + - Remove ``factory.CircularSubFactory`` - Remove ``extract_prefix`` kwarg to post-generation hooks. - Stop defaulting to Django's ``Foo.objects.create()`` when "creating" instances - Remove STRATEGY_* - - Remove :meth:`~factory.Factory.set_building_function` / :meth:`~factory.Factory.set_creation_function` + - Remove ``factory.Factory.set_building_function`` / ``factory.Factory.set_creation_function`` .. _v1.3.0: @@ -659,8 +657,8 @@ New - **The Factory class:** - Better creation/building customization hooks at :meth:`factory.Factory._build` and :meth:`factory.Factory.create` - Add support for passing non-kwarg parameters to a :class:`~factory.Factory` - wrapped class through :attr:`~factory.Factory.FACTORY_ARG_PARAMETERS`. - - Keep the :attr:`~factory.Factory.FACTORY_FOR` attribute in :class:`~factory.Factory` classes + wrapped class through ``FACTORY_ARG_PARAMETERS``. + - Keep the ``FACTORY_FOR`` attribute in :class:`~factory.Factory` classes - **Declarations:** - Allow :class:`~factory.SubFactory` to solve circular dependencies between factories @@ -681,14 +679,14 @@ Pending deprecation The following features have been deprecated and will be removed in an upcoming release. - **Declarations:** - - :class:`~factory.InfiniteIterator` is deprecated in favor of :class:`~factory.Iterator` - - :class:`~factory.CircularSubFactory` is deprecated in favor of :class:`~factory.SubFactory` + - ``factory.InfiniteIterator`` is deprecated in favor of :class:`~factory.Iterator` + - ``factory.CircularSubFactory`` is deprecated in favor of :class:`~factory.SubFactory` - The ``extract_prefix`` argument to :meth:`~factory.post_generation` is now deprecated - **Factory:** - - Usage of :meth:`~factory.Factory.set_creation_function` and :meth:`~factory.Factory.set_building_function` + - Usage of ``factory.Factory.set_creation_function`` and ``factory.Factory.set_building_function`` are now deprecated - - Implicit associated class discovery is no longer supported, you must set the :attr:`~factory.Factory.FACTORY_FOR` + - Implicit associated class discovery is no longer supported, you must set the ``FACTORY_FOR`` attribute on all :class:`~factory.Factory` subclasses @@ -725,7 +723,7 @@ In order to upgrade client code, apply the following rules: *New:* - - Add :class:`~factory.CircularSubFactory` to solve circular dependencies between factories + - Add ``factory.CircularSubFactory`` to solve circular dependencies between factories .. _v1.1.5: @@ -735,7 +733,7 @@ In order to upgrade client code, apply the following rules: *Bug fix:* - - Fix :class:`~factory.PostGenerationDeclaration` and derived classes. + - Fix ``factory.PostGenerationDeclaration`` and derived classes. .. _v1.1.4: @@ -769,7 +767,7 @@ In order to upgrade client code, apply the following rules: *New:* - - Add :class:`~factory.Iterator` and :class:`~factory.InfiniteIterator` for :class:`~factory.Factory` attribute declarations. + - Add :class:`~factory.Iterator` and ``factory.InfiniteIterator`` for :class:`~factory.Factory` attribute declarations. - Provide :func:`~factory.Factory.generate` and :func:`~factory.Factory.simple_generate`, that allow specifying the instantiation strategy directly. Also provides :func:`~factory.Factory.generate_batch` and :func:`~factory.Factory.simple_generate_batch`. @@ -792,7 +790,7 @@ In order to upgrade client code, apply the following rules: *New:* - Improve the :class:`~factory.SelfAttribute` syntax to fetch sub-attributes using the ``foo.bar`` syntax; - - Add :class:`~factory.ContainerAttribute` to fetch attributes from the container of a :class:`~factory.SubFactory`. + - Add ``factory.ContainerAttribute`` to fetch attributes from the container of a :class:`~factory.SubFactory`. - Provide the :func:`~factory.make_factory` helper: ``MyClassFactory = make_factory(MyClass, x=3, y=4)`` - Add :func:`~factory.build`, :func:`~factory.create`, :func:`~factory.stub` helpers @@ -802,7 +800,7 @@ In order to upgrade client code, apply the following rules: *Deprecation:* - - Auto-discovery of :attr:`~factory.Factory.FACTORY_FOR` based on class name is now deprecated + - Auto-discovery of ``factory.Factory.FACTORY_FOR`` based on class name is now deprecated .. _v1.0.4: @@ -815,9 +813,9 @@ In order to upgrade client code, apply the following rules: - Improve the algorithm for populating a :class:`~factory.Factory` attributes dict - Add ``python setup.py test`` command to run the test suite - Allow custom build functions - - Introduce :data:`~factory.MOGO_BUILD` build function + - Introduce ``factory.MOGO_BUILD`` build function - Add support for inheriting from multiple :class:`~factory.Factory` - - Base :class:`~factory.Factory` classes can now be declared :attr:`abstract `. + - Base :class:`~factory.Factory` classes can now be declared abstract through ``factory.Factory.ABSTRACT_FACTORY``. - Provide :class:`~factory.django.DjangoModelFactory`, whose :class:`~factory.Sequence` counter starts at the next free database id - Introduce :class:`~factory.SelfAttribute`, a shortcut for ``factory.LazyAttribute(lambda o: o.foo.bar.baz``. diff --git a/docs/conf.py b/docs/conf.py index 876a1c49..dc4a3863 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,7 +21,7 @@ project = 'Factory Boy' copyright = '2011-2015, Raphaël Barrois, Mark Sandstrom' -author = 'adfasf' +author = 'Raphaël Barrois, Mark Sandstrom' # The full version, including alpha/beta/rc tags release = factory.__version__ @@ -84,6 +84,10 @@ 'https://docs.djangoproject.com/en/dev/', 'https://docs.djangoproject.com/en/dev/_objects/', ), + 'mongoengine': ( + 'https://mongoengine-odm.readthedocs.io/', + None, + ), 'sqlalchemy': ( 'https://docs.sqlalchemy.org/en/latest/', 'https://docs.sqlalchemy.org/en/latest/objects.inv', diff --git a/docs/examples.rst b/docs/examples.rst index 7d868f8c..bd27af6a 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -76,7 +76,7 @@ And now, we'll define the related factories: -We have now defined basic factories for our :class:`~Account` and :class:`~Profile` classes. +We have now defined basic factories for our ``Account`` and ``Profile`` classes. If we commonly use a specific variant of our objects, we can refine a factory accordingly: diff --git a/docs/fuzzy.rst b/docs/fuzzy.rst index ca251656..bef86778 100644 --- a/docs/fuzzy.rst +++ b/docs/fuzzy.rst @@ -354,4 +354,4 @@ They should inherit from the :class:`BaseFuzzyAttribute` class, and override its Custom :class:`BaseFuzzyAttribute` subclasses **MUST** use :obj:`factory.random.randgen` as a randomness source; this ensures that data they generate can be regenerated using the simple state from - :meth:`get_random_state`. + :meth:`factory.random.get_random_state`. diff --git a/docs/ideas.rst b/docs/ideas.rst index 6e3962d4..b9f3691b 100644 --- a/docs/ideas.rst +++ b/docs/ideas.rst @@ -4,6 +4,6 @@ Ideas This is a list of future features that may be incorporated into factory_boy: -* When a :class:`Factory` is built or created, pass the calling context throughout the calling chain instead of custom solutions everywhere +* When a :class:`~factory.Factory` is built or created, pass the calling context throughout the calling chain instead of custom solutions everywhere * Define a proper set of rules for the support of third-party ORMs * Properly evaluate nested declarations (e.g ``factory.fuzzy.FuzzyDate(start_date=factory.SelfAttribute('since'))``) diff --git a/docs/internals.rst b/docs/internals.rst index d71afbec..62ca3fed 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -57,11 +57,11 @@ First, decide the strategy: use the strategy defined at the :attr:`class Meta ` level -Then, we'll pass the strategy and passed-in overrides to the :meth:`~Factory._generate` method. +Then, we'll pass the strategy and passed-in overrides to the ``Factory._generate`` method. -.. note:: According to the project road map, a future version will use a :meth:`~Factory._generate_batch` at its core instead. +.. note:: According to the project road map, a future version will use a ``Factory._generate_batch`` at its core instead. -A factory's :meth:`~Factory._generate` function actually delegates to a ``StepBuilder()`` object. +A factory's ``Factory._generate`` function actually delegates to a ``StepBuilder()`` object. This object will carry the overall "build an object" context (strategy, depth, and possibly other). @@ -81,7 +81,7 @@ Instantiating, Step 3: Building the object 1. The ``StepBuilder`` fetches the attributes computed by the ``Resolver``. 2. It applies renaming/adjustment rules -3. It passes them to the :meth:`FactoryOptions.instantiate` method, which +3. It passes them to the ``FactoryOptions.instantiate`` method, which forwards to the proper methods. 4. Post-declaration are applied (in declaration order) diff --git a/docs/introduction.rst b/docs/introduction.rst index e9ad3248..d3b169a0 100644 --- a/docs/introduction.rst +++ b/docs/introduction.rst @@ -112,7 +112,7 @@ This is achieved with the :class:`~factory.Sequence` declaration: >>> UserFactory() -.. note:: For more complex situations, you may also use the :meth:`~factory.@sequence` decorator (note that ``self`` is not added as first parameter): +.. note:: For more complex situations, you may also use the :meth:`@factory.sequence ` decorator (note that ``self`` is not added as first parameter): .. code-block:: python @@ -152,7 +152,7 @@ argument and returning the value for the field: .. note:: For complex cases when you happen to write a specific function, - the :meth:`~factory.@lazy_attribute` decorator should be more appropriate. + the :meth:`@factory.lazy_attribute ` decorator should be more appropriate. LazyAttribute @@ -185,7 +185,7 @@ taking the object being built and returning the value for the field: -.. note:: As for :class:`~factory.Sequence`, a :meth:`~factory.@lazy_attribute` decorator is available: +.. note:: As for :class:`~factory.Sequence`, a :meth:`@factory.lazy_attribute ` decorator is available: .. code-block:: python diff --git a/docs/orms.rst b/docs/orms.rst index 5967e1bc..7f9d1ef0 100644 --- a/docs/orms.rst +++ b/docs/orms.rst @@ -13,7 +13,7 @@ adding dedicated features. Django ------ -.. currentmodule:: factory.django +.. module:: factory.django The first versions of factory_boy were designed specifically for Django, @@ -111,11 +111,10 @@ All factories for a Django :class:`~django.db.models.Model` should use the .. attribute:: skip_postgeneration_save - Transitional option to prevent - :meth:`~factory.django.DjangoModelFactory._after_postgeneration` from - issuing a duplicate call to :meth:`~django.db.models.Model.save` on the - created instance when :class:`factory.PostGeneration` hooks return a - value. + Transitional option to prevent :class:`~factory.django.DjangoModelFactory`'s + ``_after_postgeneration`` from issuing a duplicate call to + :meth:`~django.db.models.Model.save` on the created instance when + :class:`factory.PostGeneration` hooks return a value. Extra fields @@ -167,9 +166,9 @@ Extra fields :param str from_path: Use data from the file located at ``from_path``, and keep its filename - :param file from_file: Use the contents of the provided file object; use its filename + :param io.BytesIO from_file: Use the contents of the provided file object; use its filename if available, unless ``filename`` is also provided. - :param func from_func: Use function that returns a file object + :param Callable from_func: Use function that returns a file object :param bytes data: Use the provided bytes as file contents :param str filename: The filename for the FileField @@ -200,9 +199,9 @@ Extra fields :param str from_path: Use data from the file located at ``from_path``, and keep its filename - :param file from_file: Use the contents of the provided file object; use its filename + :param io.BytesIO from_file: Use the contents of the provided file object; use its filename if available - :param func from_func: Use function that returns a file object + :param Callable from_func: Use function that returns a file object :param str filename: The filename for the ImageField :param int width: The width of the generated image (default: ``100``) :param int height: The height of the generated image (default: ``100``) @@ -272,7 +271,7 @@ To work around this problem, use the :meth:`mute_signals()` decorator/context ma Mogo ---- -.. currentmodule:: factory.mogo +.. module:: factory.mogo factory_boy supports `Mogo`_-style models, through the :class:`MogoFactory` class. @@ -294,7 +293,7 @@ factory_boy supports `Mogo`_-style models, through the :class:`MogoFactory` clas MongoEngine ----------- -.. currentmodule:: factory.mongoengine +.. module:: factory.mongoengine factory_boy supports `MongoEngine`_-style models, through the :class:`MongoEngineFactory` class. @@ -312,8 +311,8 @@ factory_boy supports `MongoEngine`_-style models, through the :class:`MongoEngin * :func:`~factory.Factory.create()` builds an instance through ``__init__`` then saves it. - .. note:: If the :attr:`associated class ` is a :class:`mongoengine.EmbeddedDocument`, + the :class:`~MongoEngineFactory`'s ``create`` function won't "save" it, since this wouldn't make sense. This feature makes it possible to use :class:`~factory.SubFactory` to create embedded document. @@ -349,7 +348,7 @@ A minimalist example: SQLAlchemy ---------- -.. currentmodule:: factory.alchemy +.. module:: factory.alchemy Factory_boy also supports `SQLAlchemy`_ models through the :class:`SQLAlchemyModelFactory` class. @@ -364,12 +363,12 @@ To work, this class needs an `SQLAlchemy`_ session object affected to the :attr: This class provides the following features: - * :func:`~factory.Factory.create()` uses :meth:`sqlalchemy.orm.session.Session.add` + * :func:`~factory.Factory.create()` uses :meth:`sqlalchemy.orm.Session.add` .. class:: SQLAlchemyOptions(factory.base.FactoryOptions) - In addition to the usual parameters available in :class:`class Meta `, + In addition to the usual parameters available in :class:`class Meta `, a :class:`SQLAlchemyModelFactory` also supports the following settings: .. attribute:: sqlalchemy_session @@ -403,8 +402,8 @@ To work, this class needs an `SQLAlchemy`_ session object affected to the :attr: Valid values are: * ``None``: do nothing - * ``'flush'``: perform a session :meth:`~sqlalchemy.orm.session.Session.flush` - * ``'commit'``: perform a session :meth:`~sqlalchemy.orm.session.Session.commit` + * ``'flush'``: perform a session :meth:`~sqlalchemy.orm.Session.flush` + * ``'commit'``: perform a session :meth:`~sqlalchemy.orm.Session.commit` The default value is ``None``. @@ -413,8 +412,8 @@ To work, this class needs an `SQLAlchemy`_ session object affected to the :attr: .. versionadded:: 3.0.0 Fields whose name are passed in this list will be used to perform a - :meth:`Model.query.one_or_none() ` - or the usual :meth:`Session.add() `: + :meth:`Model.query.one_or_none() ` + or the usual :meth:`Session.add() `: .. code-block:: python @@ -516,15 +515,15 @@ there is no "global" session management system. The most common pattern when working with unit tests and ``factory_boy`` is to use `SQLAlchemy`_'s :class:`sqlalchemy.orm.scoping.scoped_session`: -* The test runner configures some project-wide :class:`~sqlalchemy.orm.scoping.scoped_session` +* The test runner configures some project-wide :class:`~sqlalchemy.orm.scoped_session` * Each :class:`~SQLAlchemyModelFactory` subclass uses this - :class:`~sqlalchemy.orm.scoping.scoped_session` as its :attr:`~SQLAlchemyOptions.sqlalchemy_session` + :class:`~sqlalchemy.orm.scoped_session` as its :attr:`~SQLAlchemyOptions.sqlalchemy_session` * The :meth:`~unittest.TestCase.tearDown` method of tests calls - :meth:`Session.remove ` + :meth:`Session.remove ` to reset the session. .. note:: See the excellent :ref:`SQLAlchemy guide on scoped_session ` - for details of :class:`~sqlalchemy.orm.scoping.scoped_session`'s usage. + for details of :class:`~sqlalchemy.orm.scoped_session`'s usage. The basic idea is that declarative parts of the code (including factories) need a simple way to access the "current session", @@ -536,7 +535,7 @@ is to use `SQLAlchemy`_'s :class:`sqlalchemy.orm.scoping.scoped_session`: Here is an example layout: -- A global (test-only?) file holds the :class:`~sqlalchemy.orm.scoping.scoped_session`: +- A global (test-only?) file holds the :class:`~sqlalchemy.orm.scoped_session`: .. code-block:: python @@ -568,7 +567,7 @@ Here is an example layout: name = factory.Sequence(lambda n: "User %d" % n) -- The test runner configures the :class:`~sqlalchemy.orm.scoping.scoped_session` when it starts: +- The test runner configures the :class:`~sqlalchemy.orm.scoped_session` when it starts: .. code-block:: python diff --git a/docs/recipes.rst b/docs/recipes.rst index a1717ad5..457bcf9c 100644 --- a/docs/recipes.rst +++ b/docs/recipes.rst @@ -89,7 +89,7 @@ use a :class:`~factory.RelatedFactory` declaration: ) -When a :class:`UserFactory` is instantiated, factory_boy will call +When a ``UserFactory`` is instantiated, factory_boy will call ``UserLogFactory(user=that_user, action=...)`` just before returning the created ``User``. @@ -101,7 +101,7 @@ using a :class:`~django.db.models.OneToOneField` from the ``Profile`` to the ``U A typical way to create those profiles was to hook a post-save signal to the ``User`` model. -Prior to version 2.9, the solution to this was to override the :meth:`~factory.Factory._generate` method on the factory. +Prior to version 2.9, the solution to this was to override the ``factory.Factory._generate`` method on the factory. Since version 2.9, the :meth:`~factory.django.mute_signals` decorator should be used: @@ -216,8 +216,8 @@ Many-to-many relation with a 'through' -------------------------------------- -If only one link is required, this can be simply performed with a :class:`RelatedFactory`. -If more links are needed, simply add more :class:`RelatedFactory` declarations: +If only one link is required, this can be simply performed with a :class:`~factory.RelatedFactory`. +If more links are needed, simply add more :class:`~factory.RelatedFactory` declarations: .. code-block:: python diff --git a/docs/reference.rst b/docs/reference.rst index 130e194c..2122b9f7 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -1,7 +1,7 @@ Reference ========= -.. currentmodule:: factory +.. module:: factory This section offers an in-depth description of factory_boy features. @@ -181,17 +181,19 @@ Attributes and methods **Base functions:** - The :class:`Factory` class provides a few methods for getting objects; - the usual way being to simply call the class: + .. classmethod:: __call__(**kwargs) - .. code-block:: pycon + The :class:`Factory` class provides a few methods for getting objects; + the usual way being to simply call the class: + + .. code-block:: pycon - >>> UserFactory() # Calls UserFactory.create() - >>> UserFactory(login='john') # Calls UserFactory.create(login='john') + >>> UserFactory() # Calls UserFactory.create() + >>> UserFactory(login='john') # Calls UserFactory.create(login='john') - Under the hood, factory_boy will define the :class:`Factory` - :meth:`~object.__new__` method to call the default :ref:`strategy ` - of the :class:`Factory`. + Under the hood, factory_boy will define the :class:`Factory` + :meth:`~object.__new__` method to call the default :ref:`strategy ` + of the :class:`Factory`. A specific strategy for getting instance can be selected by calling the @@ -203,7 +205,7 @@ Attributes and methods .. classmethod:: build_batch(cls, size, **kwargs) - Provides a list of :obj:`size` instances from the :class:`Factory`, + Provides a list of ``size`` instances from the :class:`Factory`, through the 'build' strategy. @@ -213,7 +215,7 @@ Attributes and methods .. classmethod:: create_batch(cls, size, **kwargs) - Provides a list of :obj:`size` instances from the :class:`Factory`, + Provides a list of ``size`` instances from the :class:`Factory`, through the 'create' strategy. @@ -223,16 +225,16 @@ Attributes and methods .. classmethod:: stub_batch(cls, size, **kwargs) - Provides a list of :obj:`size` stubs from the :class:`Factory`. + Provides a list of ``size`` stubs from the :class:`Factory`. .. classmethod:: generate(cls, strategy, **kwargs) - Provide a new instance, with the provided :obj:`strategy`. + Provide a new instance, with the provided ``strategy``. .. classmethod:: generate_batch(cls, strategy, size, **kwargs) - Provides a list of :obj:`size` instances using the specified strategy. + Provides a list of ``size`` instances using the specified strategy. .. classmethod:: simple_generate(cls, create, **kwargs) @@ -241,8 +243,8 @@ Attributes and methods .. classmethod:: simple_generate_batch(cls, create, size, **kwargs) - Provides a list of :obj:`size` instances, either built or created - according to :obj:`create`. + Provides a list of ``size`` instances, either built or created + according to ``create``. **Extension points:** @@ -585,7 +587,7 @@ factory_boy supports two main strategies for generating instances, plus stubs. *Decorator* - Change the default strategy of the decorated :class:`Factory` to the chosen :obj:`strategy`: + Change the default strategy of the decorated :class:`Factory` to the chosen ``strategy``: .. code-block:: python @@ -626,7 +628,7 @@ factory_boy supports two main strategies for generating instances, plus stubs. .. function:: debug(logger='factory', stream=None) :param str logger: The name of the logger to enable debug for - :param file stream: The stream to send debug output to, defaults to :obj:`sys.stderr` + :param io.StringIO stream: The stream to send debug output to, defaults to :obj:`sys.stderr` Context manager to help debugging factory_boy behavior. It will temporarily put the target logger (e.g ``'factory'``) in debug mode, @@ -821,7 +823,7 @@ Decorator The class :class:`LazyFunction` does not provide a decorator. -For complex cases, use :meth:`LazyAttribute.lazy_attribute` directly. +For complex cases, use :meth:`~factory.lazy_attribute` directly. LazyAttribute """"""""""""" @@ -855,7 +857,7 @@ accept the object being built as sole argument, and return a value. The object passed to :class:`LazyAttribute` is not an instance of the target class, -but instead a :class:`~builder.Resolver`: a temporary container that computes +but instead a ``builder.Resolver``: a temporary container that computes the value of all declared fields. @@ -1158,7 +1160,7 @@ The :class:`SubFactory` attribute should be called with: .. note:: When passing an actual :class:`~factory.Factory` for the - :attr:`~factory.SubFactory.factory` argument, make sure to pass + :class:`~factory.SubFactory`'s ``factory`` argument, make sure to pass the class and not instance (i.e no ``()`` after the class): .. code-block:: python @@ -1365,7 +1367,7 @@ Obviously, this "follow parents" ability also handles overriding some attributes This feature is also available to :class:`LazyAttribute` and :class:`LazyAttributeSequence`, -through the :attr:`~builder.Resolver.factory_parent` attribute of the passed-in object: +through the ``factory_parent`` attribute of the passed-in object: .. code-block:: python @@ -1819,7 +1821,7 @@ RelatedFactoryList in a future version for increased consistency with other declarations. .. note:: - Note that using a ``lambda`` for :attr:`size` allows the number of related objects per + Note that using a ``lambda`` for ``size`` allows the number of related objects per parents object to vary. This is useful for testing, when you likely don't want your mock data to have parent objects with the exact same, static number of related objects. @@ -2047,18 +2049,18 @@ Internally, helper methods use :func:`make_factory` to create a new :class:`Factory` according to the method name. Please note, that all Factories created with this methods inherit from the -:class:`factory.base.Factory` class. For full support of your ``ORM``, specify +:class:`factory.Factory` class. For full support of your ``ORM``, specify a base class with the ``FACTORY_CLASS`` parameter as shown in :func:`make_factory` examples. .. function:: build(klass, FACTORY_CLASS=None, **kwargs) .. function:: build_batch(klass, size, FACTORY_CLASS=None, **kwargs) - Create a factory for :obj:`klass` using declarations passed in kwargs; + Create a factory for ``klass`` using declarations passed in kwargs; return an instance built from that factory with :data:`BUILD_STRATEGY`, - or a list of :obj:`size` instances (for :func:`build_batch`). + or a list of ``size`` instances (for :func:`build_batch`). - :param class klass: Class of the instance to build + :param type klass: Class of the instance to build :param int size: Number of instances to build :param kwargs: Declarations to use for the generated factory :param FACTORY_CLASS: Alternate base class (instead of :class:`Factory`) @@ -2068,11 +2070,11 @@ a base class with the ``FACTORY_CLASS`` parameter as shown in .. function:: create(klass, FACTORY_CLASS=None, **kwargs) .. function:: create_batch(klass, size, FACTORY_CLASS=None, **kwargs) - Create a factory for :obj:`klass` using declarations passed in kwargs; + Create a factory for ``klass`` using declarations passed in kwargs; return an instance created from that factory with :data:`CREATE_STRATEGY`, - or a list of :obj:`size` instances (for :func:`create_batch`). + or a list of ``size`` instances (for :func:`create_batch`). - :param class klass: Class of the instance to create + :param type klass: Class of the instance to create :param int size: Number of instances to create :param kwargs: Declarations to use for the generated factory :param FACTORY_CLASS: Alternate base class (instead of :class:`Factory`) @@ -2082,11 +2084,11 @@ a base class with the ``FACTORY_CLASS`` parameter as shown in .. function:: stub(klass, FACTORY_CLASS=None, **kwargs) .. function:: stub_batch(klass, size, FACTORY_CLASS=None, **kwargs) - Create a factory for :obj:`klass` using declarations passed in kwargs; + Create a factory for ``klass`` using declarations passed in kwargs; return an instance stubbed from that factory with :data:`STUB_STRATEGY`, - or a list of :obj:`size` instances (for :func:`stub_batch`). + or a list of ``size`` instances (for :func:`stub_batch`). - :param class klass: Class of the instance to stub + :param type klass: Class of the instance to stub :param int size: Number of instances to stub :param kwargs: Declarations to use for the generated factory :param FACTORY_CLASS: Alternate base class (instead of :class:`Factory`) @@ -2096,11 +2098,11 @@ a base class with the ``FACTORY_CLASS`` parameter as shown in .. function:: generate(klass, strategy, FACTORY_CLASS=None, **kwargs) .. function:: generate_batch(klass, strategy, size, FACTORY_CLASS=None, **kwargs) - Create a factory for :obj:`klass` using declarations passed in kwargs; - return an instance generated from that factory with the :obj:`strategy` strategy, - or a list of :obj:`size` instances (for :func:`generate_batch`). + Create a factory for ``klass`` using declarations passed in kwargs; + return an instance generated from that factory with the ``strategy`` strategy, + or a list of ``size`` instances (for :func:`generate_batch`). - :param class klass: Class of the instance to generate + :param type klass: Class of the instance to generate :param str strategy: The strategy to use :param int size: Number of instances to generate :param kwargs: Declarations to use for the generated factory @@ -2111,11 +2113,11 @@ a base class with the ``FACTORY_CLASS`` parameter as shown in .. function:: simple_generate(klass, create, FACTORY_CLASS=None, **kwargs) .. function:: simple_generate_batch(klass, create, size, FACTORY_CLASS=None, **kwargs) - Create a factory for :obj:`klass` using declarations passed in kwargs; - return an instance generated from that factory according to the :obj:`create` flag, - or a list of :obj:`size` instances (for :func:`simple_generate_batch`). + Create a factory for ``klass`` using declarations passed in kwargs; + return an instance generated from that factory according to the ``create`` flag, + or a list of ``size`` instances (for :func:`simple_generate_batch`). - :param class klass: Class of the instance to generate + :param type klass: Class of the instance to generate :param bool create: Whether to build (``False``) or create (``True``) instances :param int size: Number of instances to generate :param kwargs: Declarations to use for the generated factory @@ -2125,7 +2127,7 @@ a base class with the ``FACTORY_CLASS`` parameter as shown in Randomness management --------------------- -.. currentmodule:: factory.random +.. module:: factory.random Using :mod:`random` in factories allows to "fuzz" a program efficiently. However, it's sometimes required to *reproduce* a failing test. @@ -2147,6 +2149,11 @@ of :class:`random.Random`, which can be managed through the :mod:`factory.random .. method:: reseed_random(seed) The :meth:`reseed_random` function allows to load a chosen seed into the random generator. - That seed can be anything accepted by :meth:`random.seed`. + That seed can be anything accepted by :func:`random.seed`. + +.. data:: randgen + + The :class:`random.Random` global instance used by :mod:`factory.fuzzy` + and :class:`factory.Faker`. See :ref:`recipe-random-management` for help in using those methods in a test setup. From c38732fb4aa0e9e8ec6a77d7dd08f17c2043155d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Sun, 18 Aug 2024 21:40:45 +0200 Subject: [PATCH 40/41] Preparing release 3.3.1 --- docs/changelog.rst | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index fd49f447..024609e9 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -3,7 +3,7 @@ ChangeLog .. Note for v4.x: don't forget to check "Deprecated" sections for removal. -3.3.1 (unreleased) +3.3.1 (2024-08-18) ------------------ *New:* diff --git a/setup.cfg b/setup.cfg index ccbfe6fb..ee24cbd1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = factory_boy -version = 3.3.1.dev0 +version = 3.3.1 description = A versatile test fixtures replacement based on thoughtbot's factory_bot for Ruby. long_description = file: README.rst # https://docutils.sourceforge.io/FAQ.html#what-s-the-official-mime-type-for-restructuredtext-data From ac49fb40ec424276c3cd3ca0925ba99a626f05f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Sun, 18 Aug 2024 21:41:06 +0200 Subject: [PATCH 41/41] Back to development: 3.3.2 --- docs/changelog.rst | 6 ++++++ setup.cfg | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 024609e9..29475a92 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -3,6 +3,12 @@ ChangeLog .. Note for v4.x: don't forget to check "Deprecated" sections for removal. +3.3.2 (unreleased) +------------------ + +- Nothing changed yet. + + 3.3.1 (2024-08-18) ------------------ *New:* diff --git a/setup.cfg b/setup.cfg index ee24cbd1..1ae6bcbf 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = factory_boy -version = 3.3.1 +version = 3.3.2.dev0 description = A versatile test fixtures replacement based on thoughtbot's factory_bot for Ruby. long_description = file: README.rst # https://docutils.sourceforge.io/FAQ.html#what-s-the-official-mime-type-for-restructuredtext-data