Hello community, here is the log from the commit of package python-marshmallow for openSUSE:Factory checked in at 2020-04-01 19:19:34 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-marshmallow (Old) and /work/SRC/openSUSE:Factory/.python-marshmallow.new.3248 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-marshmallow" Wed Apr 1 19:19:34 2020 rev:13 rq:790511 version:3.5.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-marshmallow/python-marshmallow.changes 2019-11-14 23:52:57.464529787 +0100 +++ /work/SRC/openSUSE:Factory/.python-marshmallow.new.3248/python-marshmallow.changes 2020-04-01 19:19:47.151570192 +0200 @@ -1,0 +2,21 @@ +Wed Apr 1 09:10:03 UTC 2020 - [email protected] + +- version update to 3.5.1 + - Includes bug fix from 2.21.0. + - Fix list of nullable nested fields ``List(Nested(Field, allow_none=True)`` + (:issue:`1497`). Because this fix reverts an optimization introduced to + speed-up serialization and deserialization of lists of nested fields, a + negative impact on performance in this specific case is expected. + - Improve type coverage (:issue:`1479`). Thanks :user:`Reskov`. + - Fix typing for ``data`` param of ``Schema.load`` and ``ValidationError`` (:issue:`1492`). + Thanks :user:`mehdigmira` for reporting and thanks :user:`dfirst` for the PR. + - Remove unnecessary typecasts (:pr:`1500`). Thanks :user:`hukkinj1`. + - Remove useless ``_serialize`` override in ``UUID`` field (:pr:`1489`). + - ``fields.Nested`` may take a callable that returns a schema instance. + Use this to resolve order-of-declaration issues when schemas nest each other (:issue:`1146`). + - Passing the string ``"self"`` to ``fields.Nested`` is deprecated. + Use a callable instead. + - Fix typing for ``Number._format_num`` (:pr:`1466`). Thanks :user:`hukkinj1`. + - Make mypy stricter and remove dead code (:pr:`1467`). Thanks again, :user:`hukkinj1`. + +------------------------------------------------------------------- Old: ---- marshmallow-3.2.2.tar.gz New: ---- marshmallow-3.5.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-marshmallow.spec ++++++ --- /var/tmp/diff_new_pack.uiEnZU/_old 2020-04-01 19:19:48.491570795 +0200 +++ /var/tmp/diff_new_pack.uiEnZU/_new 2020-04-01 19:19:48.495570797 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-marshmallow # -# Copyright (c) 2019 SUSE LLC. +# Copyright (c) 2020 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-marshmallow -Version: 3.2.2 +Version: 3.5.1 Release: 0 Summary: ORM/ODM/framework-agnostic library to convert datatypes from/to Python types License: MIT AND BSD-3-Clause ++++++ marshmallow-3.2.2.tar.gz -> marshmallow-3.5.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/AUTHORS.rst new/marshmallow-3.5.1/AUTHORS.rst --- old/marshmallow-3.2.2/AUTHORS.rst 2019-11-04 22:14:53.000000000 +0100 +++ new/marshmallow-3.5.1/AUTHORS.rst 2020-03-05 14:05:11.000000000 +0100 @@ -140,3 +140,9 @@ - `@phrfpeixoto <https://github.com/phrfpeixoto>`_ - `@jceresini <https://github.com/jceresini>`_ - Nikolay Shebanov `@killthekitten <https://github.com/killthekitten>`_ +- Taneli Hukkinen `@hukkinj1 <https://github.com/hukkinj1>`_ +- `@Reskov <https://github.com/Reskov>`_ +- Albert Tugushev `@atugushev <https://github.com/atugushev>`_ +- `@dfirst <https://github.com/dfirst>`_ +- Tim Gates `@timgates42 <https://github.com/timgates42>`_ +- Nathan `@nbanmp <https://github.com/nbanmp>`_ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/CHANGELOG.rst new/marshmallow-3.5.1/CHANGELOG.rst --- old/marshmallow-3.2.2/CHANGELOG.rst 2019-11-04 22:14:53.000000000 +0100 +++ new/marshmallow-3.5.1/CHANGELOG.rst 2020-03-05 14:05:11.000000000 +0100 @@ -1,17 +1,107 @@ Changelog --------- +3.5.1 (2020-03-05) +****************** + +Bug fixes: + +- Includes bug fix from 2.21.0. + +3.5.0 (2020-02-19) +****************** + +Bug fixes: + +- Fix list of nullable nested fields ``List(Nested(Field, allow_none=True)`` + (:issue:`1497`). Because this fix reverts an optimization introduced to + speed-up serialization and deserialization of lists of nested fields, a + negative impact on performance in this specific case is expected. + +3.4.0 (2020-02-02) +****************** + +Features: + +- Improve type coverage (:issue:`1479`). Thanks :user:`Reskov`. + +Bug fixes: + +- Fix typing for ``data`` param of ``Schema.load`` and ``ValidationError`` (:issue:`1492`). + Thanks :user:`mehdigmira` for reporting and thanks :user:`dfirst` for the PR. + +Other changes: + +- Remove unnecessary typecasts (:pr:`1500`). Thanks :user:`hukkinj1`. +- Remove useless ``_serialize`` override in ``UUID`` field (:pr:`1489`). + +3.3.0 (2019-12-05) +****************** + +Features: + +- ``fields.Nested`` may take a callable that returns a schema instance. + Use this to resolve order-of-declaration issues when schemas nest each other (:issue:`1146`). + +.. code-block:: python + + # <3.3 + class AlbumSchema(Schema): + title = fields.Str() + artist = fields.Nested("ArtistSchema", only=("name",)) + + + class ArtistSchema(Schema): + name = fields.Str() + albums = fields.List(fields.Nested(AlbumSchema)) + + + # >=3.3 + class AlbumSchema(Schema): + title = fields.Str() + artist = fields.Nested(lambda: ArtistSchema(only=("name",))) + + + class ArtistSchema(Schema): + name = fields.Str() + albums = fields.List(fields.Nested(AlbumSchema)) + +Deprecations: + +- Passing the string ``"self"`` to ``fields.Nested`` is deprecated. + Use a callable instead. + +.. code-block:: python + + from marshmallow import Schema, fields + + # <3.3 + class PersonSchema(Schema): + partner = fields.Nested("self", exclude=("partner",)) + friends = fields.List(fields.Nested("self")) + + + # >=3.3 + class PersonSchema(Schema): + partner = fields.Nested(lambda: PersonSchema(exclude=("partner"))) + friends = fields.List(fields.Nested(lambda: PersonSchema())) + +Other changes: + +- Fix typing for ``Number._format_num`` (:pr:`1466`). Thanks :user:`hukkinj1`. +- Make mypy stricter and remove dead code (:pr:`1467`). Thanks again, :user:`hukkinj1`. + 3.2.2 (2019-11-04) ****************** Bug fixes: -- Don't load fields for which ``load_only`` and ``dump_only`` are both ``True`` (:pr:`1448`). +- Don't load fields for which ``load_only`` and ``dump_only`` are both ``True`` (:pr:`1448`). - Fix types in ``marshmallow.validate`` (:pr:`1446`). Support: -- Test against Python 3.8 (pr:`1431`). +- Test against Python 3.8 (:pr:`1431`). 3.2.1 (2019-09-30) ++++++++++++++++++ @@ -125,7 +215,7 @@ - *Backwards-incompatible*: Validation does not occur on serialization (:issue:`1132`). This significantly improves serialization performance. - *Backwards-incompatible*: ``DateTime`` does not affect timezone information - on serialization and deserialization (:issue:`1234`, :pr:`1287`). + on serialization and deserialization (:issue:`1234`, :pr:`1278`). - Add ``NaiveDateTime`` and ``AwareDateTime`` to enforce timezone awareness (:issue:`1234`, :pr:`1287`). - *Backwards-incompatible*: ``List`` does not wrap single values in a list on @@ -691,6 +781,18 @@ - Remove ``func`` parameter of ``fields.Function``. Remove ``method_name`` parameter of ``fields.Method`` (issue:`325`). Use the ``serialize`` parameter instead. - Remove ``extra`` parameter from ``Schema``. Use a ``@post_dump`` method to add additional data. +2.21.0 (2020-03-05) ++++++++++++++++++++ + +Bug fixes: + +- Don't match string-ending newlines in ``URL`` and ``Email`` fields + (:issue:`1522`). Thanks :user:`nbanmp` for the PR. + +Other changes: + +- Drop support for Python 3.4 (:pr:`1525`). + 2.20.5 (2019-09-15) +++++++++++++++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/LICENSE new/marshmallow-3.5.1/LICENSE --- old/marshmallow-3.2.2/LICENSE 2019-11-04 22:14:53.000000000 +0100 +++ new/marshmallow-3.5.1/LICENSE 2020-03-05 14:05:11.000000000 +0100 @@ -1,4 +1,4 @@ -Copyright 2019 Steven Loria and contributors +Copyright 2020 Steven Loria and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/PKG-INFO new/marshmallow-3.5.1/PKG-INFO --- old/marshmallow-3.2.2/PKG-INFO 2019-11-04 22:15:05.000000000 +0100 +++ new/marshmallow-3.5.1/PKG-INFO 2020-03-05 14:05:19.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: marshmallow -Version: 3.2.2 +Version: 3.5.1 Summary: A lightweight library for converting complex datatypes to and from native Python datatypes. Home-page: https://github.com/marshmallow-code/marshmallow Author: Steven Loria diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/docs/nesting.rst new/marshmallow-3.5.1/docs/nesting.rst --- old/marshmallow-3.2.2/docs/nesting.rst 2019-11-04 22:14:53.000000000 +0100 +++ new/marshmallow-3.5.1/docs/nesting.rst 2020-03-05 14:05:11.000000000 +0100 @@ -22,7 +22,7 @@ self.title = title self.author = author # A User object -Use a :class:`Nested <marshmallow.fields.Nested>` field to represent the relationship, passing in a nested schema class. +Use a :class:`Nested <marshmallow.fields.Nested>` field to represent the relationship, passing in a nested schema. .. code-block:: python @@ -53,24 +53,24 @@ # 'created_at': '2014-08-17T14:58:57.600623+00:00'}} .. note:: - If the field is a collection of nested objects, you must set ``many=True``. + If the field is a collection of nested objects, pass the `Nested <marshmallow.fields.Nested>` field to `List <marshmallow.fields.List>`. .. code-block:: python - collaborators = fields.Nested(UserSchema, many=True) + collaborators = fields.List(fields.Nested(UserSchema)) .. _specifying-nested-fields: Specifying Which Fields to Nest ------------------------------- -You can explicitly specify which attributes of the nested objects you want to serialize with the ``only`` argument. +You can explicitly specify which attributes of the nested objects you want to (de)serialize with the ``only`` argument to the schema. .. code-block:: python class BlogSchema2(Schema): title = fields.String() - author = fields.Nested(UserSchema, only=["email"]) + author = fields.Nested(UserSchema(only=("email",))) schema = BlogSchema2() @@ -81,7 +81,7 @@ # 'author': {'email': u'[email protected]'} # } -You can represent the attributes of deeply nested objects using dot delimiters. +Dotted paths may be passed to ``only`` and ``exclude`` to specify nested attributes. .. code-block:: python @@ -89,7 +89,7 @@ blog = fields.Nested(BlogSchema2) - schema = SiteSchema(only=["blog.author.email"]) + schema = SiteSchema(only=("blog.author.email",)) result = schema.dump(site) pprint(result) # { @@ -125,8 +125,6 @@ # } -You can also exclude fields by passing in an ``exclude`` list. This argument also allows representing the attributes of deeply nested objects using dot delimiters. - .. _partial-loading: Partial Loading @@ -168,27 +166,29 @@ Two-way Nesting --------------- -If you have two objects that nest each other, you can refer to a nested schema by its class name. This allows you to nest Schemas that have not yet been defined. - +If you have two objects that nest each other, you can pass a callable to `Nested <marshmallow.fields.Nested>`. +This allows you to resolve order-of-declaration issues, such as when one schema nests a schema that is declared below it. -For example, a representation of an ``Author`` model might include the books that have a foreign-key (many-to-one) relationship to it. Correspondingly, a representation of a ``Book`` will include its author representation. +For example, a representation of an ``Author`` model might include the books that have a many-to-one relationship to it. +Correspondingly, a representation of a ``Book`` will include its author representation. .. code-block:: python - class AuthorSchema(Schema): - # Make sure to use the 'only' or 'exclude' params + class BookSchema(Schema): + id = fields.Int(dump_only=True) + title = fields.Str() + + # Make sure to use the 'only' or 'exclude' # to avoid infinite recursion - books = fields.Nested("BookSchema", many=True, exclude=("author",)) + author = fields.Nested(lambda: AuthorSchema(only=("id", "title"))) - class Meta: - fields = ("id", "name", "books") + class AuthorSchema(Schema): + id = fields.Int(dump_only=True) + title = fields.Str() - class BookSchema(Schema): - author = fields.Nested(AuthorSchema, only=("id", "name")) + books = fields.List(fields.Nested(BookSchema(exclude=("author",)))) - class Meta: - fields = ("id", "title", "author") .. code-block:: python @@ -221,27 +221,54 @@ # ] # } +You can also pass a class name as a string to `Nested <marshmallow.fields.Nested>`. +This is useful for avoiding circular imports when your schemas are located in different modules. + +.. code-block:: python + + # books.py + from marshmallow import Schema, fields + + + class BookSchema(Schema): + id = fields.Int(dump_only=True) + title = fields.Str() + + author = fields.Nested("AuthorSchema", only=("id", "title")) + +.. code-block:: python + + # authors.py + from marshmallow import Schema, fields + + + class AuthorSchema(Schema): + id = fields.Int(dump_only=True) + title = fields.Str() + + books = fields.List(fields.Nested("BookSchema", exclude=("author",))) + .. note:: - If you need to, you can also pass the full, module-qualified path to `fields.Nested`. :: - books = fields.Nested('path.to.BookSchema', - many=True, exclude=('author', )) + If you have multiple schemas with the same class name, you must pass the full, module-qualified path. :: + + author = fields.Nested("authors.BookSchema", only=("id", "title")) .. _self-nesting: Nesting A Schema Within Itself ------------------------------ -If the object to be marshalled has a relationship to an object of the same type, you can nest the `Schema` within itself by passing ``"self"`` (with quotes) to the :class:`Nested <marshmallow.fields.Nested>` constructor. +If the object to be marshalled has a relationship to an object of the same type, you can nest the `Schema` within itself by passing a callable that returns an instance of the same schema. .. code-block:: python class UserSchema(Schema): name = fields.String() email = fields.Email() - friends = fields.Nested("self", many=True) # Use the 'exclude' argument to avoid infinite recursion - employer = fields.Nested("self", exclude=("employer",), default=None) + employer = fields.Nested(lambda: UserSchema(exclude=("employer",))) + friends = fields.List(fields.Nested(lambda: UserSchema())) user = User("Steve", "[email protected]") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/docs/upgrading.rst new/marshmallow-3.5.1/docs/upgrading.rst --- old/marshmallow-3.2.2/docs/upgrading.rst 2019-11-04 22:14:53.000000000 +0100 +++ new/marshmallow-3.5.1/docs/upgrading.rst 2020-03-05 14:05:11.000000000 +0100 @@ -3,6 +3,55 @@ This section documents migration paths to new releases. +Upgrading to 3.3 +++++++++++++++++ + +In 3.3, `fields.Nested <marshmallow.fields.Nested>` may take a callable that returns a schema instance. +Use this to resolve order-of-declaration issues when schemas nest each other. + +.. code-block:: python + + from marshmallow import Schema, fields + + # <3.3 + class AlbumSchema(Schema): + title = fields.Str() + artist = fields.Nested("ArtistSchema", only=("name",)) + + + class ArtistSchema(Schema): + name = fields.Str() + albums = fields.List(fields.Nested(AlbumSchema)) + + + # >=3.3 + class AlbumSchema(Schema): + title = fields.Str() + artist = fields.Nested(lambda: ArtistSchema(only=("name",))) + + + class ArtistSchema(Schema): + name = fields.Str() + albums = fields.List(fields.Nested(AlbumSchema)) + +A callable should also be used when nesting a schema within itself. +Passing ``"self"`` is deprecated. + +.. code-block:: python + + from marshmallow import Schema, fields + + # <3.3 + class PersonSchema(Schema): + partner = fields.Nested("self", exclude=("partner",)) + friends = fields.List(fields.Nested("self")) + + + # >=3.3 + class PersonSchema(Schema): + partner = fields.Nested(lambda: PersonSchema(exclude=("partner"))) + friends = fields.List(fields.Nested(lambda: PersonSchema())) + .. _upgrading_3_0: Upgrading to 3.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/docs/why.rst new/marshmallow-3.5.1/docs/why.rst --- old/marshmallow-3.2.2/docs/why.rst 2019-11-04 22:14:53.000000000 +0100 +++ new/marshmallow-3.5.1/docs/why.rst 2020-03-05 14:05:11.000000000 +0100 @@ -33,8 +33,8 @@ class GameStateSchema(Schema): _id = fields.UUID(required=True) - players = fields.Nested(PlayerSchema, many=True) score = fields.Nested(ScoreSchema) + players = fields.List(fields.Nested(PlayerSchema)) last_changed = fields.DateTime(format="rfc") class Meta: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/examples/peewee_example.py new/marshmallow-3.5.1/examples/peewee_example.py --- old/marshmallow-3.2.2/examples/peewee_example.py 2019-11-04 22:14:53.000000000 +0100 +++ new/marshmallow-3.5.1/examples/peewee_example.py 2020-03-05 14:05:11.000000000 +0100 @@ -77,7 +77,7 @@ class TodoSchema(Schema): id = fields.Int(dump_only=True) done = fields.Boolean(attribute="is_done", missing=False) - user = fields.Nested(UserSchema, exclude=("joined_on", "password"), dump_only=True) + user = fields.Nested(UserSchema(exclude=("joined_on", "password")), dump_only=True) content = fields.Str(required=True) posted_on = fields.DateTime(dump_only=True) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/pyproject.toml new/marshmallow-3.5.1/pyproject.toml --- old/marshmallow-3.2.2/pyproject.toml 1970-01-01 01:00:00.000000000 +0100 +++ new/marshmallow-3.5.1/pyproject.toml 2020-03-05 14:05:11.000000000 +0100 @@ -0,0 +1,3 @@ +[tool.black] +line-length = 88 +target-version = ['py35', 'py36', 'py37', 'py38'] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/setup.cfg new/marshmallow-3.5.1/setup.cfg --- old/marshmallow-3.2.2/setup.cfg 2019-11-04 22:15:05.000000000 +0100 +++ new/marshmallow-3.5.1/setup.cfg 2020-03-05 14:05:19.000000000 +0100 @@ -5,7 +5,7 @@ universal = 1 [flake8] -ignore = E203, E266, E501, W503, E731, B903 +extend-ignore = E203, E266, E501, E731, B903 max-line-length = 90 max-complexity = 18 select = B,C,E,F,W,T4,B9 @@ -16,6 +16,9 @@ [mypy] ignore_missing_imports = true +warn_unreachable = true +warn_unused_ignores = true +warn_redundant_casts = true [egg_info] tag_build = diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/setup.py new/marshmallow-3.5.1/setup.py --- old/marshmallow-3.2.2/setup.py 2019-11-04 22:14:53.000000000 +0100 +++ new/marshmallow-3.5.1/setup.py 2020-03-05 14:05:11.000000000 +0100 @@ -4,13 +4,13 @@ EXTRAS_REQUIRE = { "tests": ["pytest", "pytz", "simplejson"], "lint": [ - "mypy==0.740", + "mypy==0.761", "flake8==3.7.9", - "flake8-bugbear==19.8.0", - "pre-commit~=1.20", + "flake8-bugbear==20.1.4", + "pre-commit>=1.20,<3.0", ], "docs": [ - "sphinx==2.2.1", + "sphinx==2.4.3", "sphinx-issues==1.2.0", "alabaster==0.7.12", "sphinx-version-warning==1.1.2", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/src/marshmallow/__init__.py new/marshmallow-3.5.1/src/marshmallow/__init__.py --- old/marshmallow-3.2.2/src/marshmallow/__init__.py 2019-11-04 22:14:53.000000000 +0100 +++ new/marshmallow-3.5.1/src/marshmallow/__init__.py 2020-03-05 14:05:11.000000000 +0100 @@ -13,7 +13,7 @@ from marshmallow.exceptions import ValidationError from distutils.version import LooseVersion -__version__ = "3.2.2" +__version__ = "3.5.1" __version_info__ = tuple(LooseVersion(__version__).version) __all__ = [ "EXCLUDE", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/src/marshmallow/exceptions.py new/marshmallow-3.5.1/src/marshmallow/exceptions.py --- old/marshmallow-3.2.2/src/marshmallow/exceptions.py 2019-11-04 22:14:53.000000000 +0100 +++ new/marshmallow-3.5.1/src/marshmallow/exceptions.py 2020-03-05 14:05:11.000000000 +0100 @@ -27,7 +27,10 @@ self, message: typing.Union[str, typing.List, typing.Dict], field_name: str = SCHEMA, - data: typing.Mapping[str, typing.Any] = None, + data: typing.Union[ + typing.Mapping[str, typing.Any], + typing.Iterable[typing.Mapping[str, typing.Any]], + ] = None, valid_data: typing.Union[ typing.List[typing.Dict[str, typing.Any]], typing.Dict[str, typing.Any] ] = None, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/src/marshmallow/fields.py new/marshmallow-3.5.1/src/marshmallow/fields.py --- old/marshmallow-3.2.2/src/marshmallow/fields.py 2019-11-04 22:14:53.000000000 +0100 +++ new/marshmallow-3.5.1/src/marshmallow/fields.py 2020-03-05 14:05:11.000000000 +0100 @@ -146,8 +146,7 @@ attribute: str = None, validate: typing.Union[ typing.Callable[[typing.Any], typing.Any], - typing.Sequence[typing.Callable[[typing.Any], typing.Any]], - typing.Generator[typing.Callable[[typing.Any], typing.Any], None, None], + typing.Iterable[typing.Callable[[typing.Any], typing.Any]], ] = None, required: bool = False, allow_none: bool = None, @@ -160,20 +159,12 @@ self.attribute = attribute self.data_key = data_key self.validate = validate - if utils.is_iterable_but_not_string(validate): - if not utils.is_generator(validate): - self.validators = typing.cast( - typing.Sequence[typing.Callable[[typing.Any], typing.Any]], validate - ) - else: - validators = typing.cast( - typing.Sequence[typing.Callable[[typing.Any], typing.Any]], validate - ) - self.validators = list(validators) + if validate is None: + self.validators = [] elif callable(validate): self.validators = [validate] - elif validate is None: - self.validators = [] + elif utils.is_iterable_but_not_string(validate): + self.validators = list(validate) else: raise ValueError( "The 'validate' parameter must be a callable " @@ -437,10 +428,19 @@ Examples: :: - user = fields.Nested(UserSchema) - user2 = fields.Nested('UserSchema') # Equivalent to above - collaborators = fields.Nested(UserSchema, many=True, only=('id',)) - parent = fields.Nested('self') + class ChildSchema(Schema): + id = fields.Str() + name = fields.Str() + # Use lambda functions when you need two-way nesting or self-nesting + parent = fields.Nested(lambda: ParentSchema(only=("id",)), dump_only=True) + siblings = fields.List(fields.Nested(lambda: ChildSchema(only=("id", "name")))) + + class ParentSchema(Schema): + id = fields.Str() + children = fields.List( + fields.Nested(ChildSchema(only=("id", "parent", "siblings"))) + ) + spouse = fields.Nested(lambda: ParentSchema(only=("id",))) When passing a `Schema <marshmallow.Schema>` instance as the first argument, the instance's ``exclude``, ``only``, and ``many`` attributes will be respected. @@ -471,7 +471,7 @@ def __init__( self, - nested: typing.Union[SchemaABC, type, str], + nested: typing.Union[SchemaABC, type, str, typing.Callable[[], SchemaABC]], *, default: typing.Any = missing_, only: types.StrSequenceOrSet = None, @@ -483,10 +483,16 @@ # Raise error if only or exclude is passed as string, not list of strings if only is not None and not is_collection(only): raise StringNotCollectionError('"only" should be a collection of strings.') - if exclude is not None and not is_collection(exclude): + if not is_collection(exclude): raise StringNotCollectionError( '"exclude" should be a collection of strings.' ) + if nested == "self": + warnings.warn( + "Passing 'self' to `Nested` is deprecated. " + "Use `Nested(lambda: MySchema(...))` instead.", + DeprecationWarning, + ) self.nested = nested self.only = only self.exclude = exclude @@ -505,8 +511,13 @@ if not self._schema: # Inherit context from parent. context = getattr(self.parent, "context", {}) - if isinstance(self.nested, SchemaABC): - self._schema = copy.copy(self.nested) + if callable(self.nested) and not isinstance(self.nested, type): + nested = self.nested() + else: + nested = self.nested + + if isinstance(nested, SchemaABC): + self._schema = copy.copy(nested) self._schema.context.update(context) # Respect only and exclude passed from parent and re-initialize fields set_class = self._schema.set_class @@ -521,20 +532,17 @@ self._schema.exclude = set_class(self.exclude).union(original) self._schema._init_fields() else: - if isinstance(self.nested, type) and issubclass(self.nested, SchemaABC): - schema_class = self.nested - elif not isinstance(self.nested, (str, bytes)): + if isinstance(nested, type) and issubclass(nested, SchemaABC): + schema_class = nested + elif not isinstance(nested, (str, bytes)): raise ValueError( - "Nested fields must be passed a " - "Schema, not {}.".format(self.nested.__class__) + "`Nested` fields must be passed a " + "`Schema`, not {}.".format(nested.__class__) ) - elif self.nested == "self": - ret = self - while not isinstance(ret, SchemaABC): - ret = ret.parent - schema_class = ret.__class__ + elif nested == "self": + schema_class = self.root.__class__ else: - schema_class = class_registry.get_class(self.nested) + schema_class = class_registry.get_class(nested) self._schema = schema_class( many=self.many, only=self.only, @@ -553,33 +561,30 @@ if field.startswith(nested_field) ] - def _serialize(self, nested_obj, attr, obj, many=False, **kwargs): + def _serialize(self, nested_obj, attr, obj, **kwargs): # Load up the schema first. This allows a RegistryError to be raised # if an invalid schema name was passed schema = self.schema if nested_obj is None: return None - many = schema.many or self.many or many - return schema.dump(nested_obj, many=self.many or many) + many = schema.many or self.many + return schema.dump(nested_obj, many=many) - def _test_collection(self, value, many=False): - many = self.schema.many or self.many or many + def _test_collection(self, value): + many = self.schema.many or self.many if many and not utils.is_collection(value): raise self.make_error("type", input=value, type=value.__class__.__name__) - def _load(self, value, data, partial=None, many=False): - many = self.schema.many or self.many or many + def _load(self, value, data, partial=None): try: - valid_data = self.schema.load( - value, unknown=self.unknown, partial=partial, many=many - ) + valid_data = self.schema.load(value, unknown=self.unknown, partial=partial) except ValidationError as error: raise ValidationError( error.messages, valid_data=error.valid_data ) from error return valid_data - def _deserialize(self, value, attr, data, partial=None, many=False, **kwargs): + def _deserialize(self, value, attr, data, partial=None, **kwargs): """Same as :meth:`Field._deserialize` with additional ``partial`` argument. :param bool|tuple partial: For nested schemas, the ``partial`` @@ -588,8 +593,8 @@ .. versionchanged:: 3.0.0 Add ``partial`` parameter. """ - self._test_collection(value, many=many) - return self._load(value, data, partial=partial, many=many) + self._test_collection(value) + return self._load(value, data, partial=partial) class Pluck(Nested): @@ -617,7 +622,12 @@ :param kwargs: The same keyword arguments that :class:`Nested` receives. """ - def __init__(self, nested, field_name, **kwargs): + def __init__( + self, + nested: typing.Union[SchemaABC, type, str, typing.Callable[[], SchemaABC]], + field_name: str, + **kwargs + ): super().__init__(nested, only=(field_name,), **kwargs) self.field_name = field_name @@ -690,17 +700,11 @@ ) -> typing.Optional[typing.List[typing.Any]]: if value is None: return None - # Optimize dumping a list of Nested objects by calling dump(many=True) - if isinstance(self.inner, Nested) and not self.inner.many: - return self.inner._serialize(value, attr, obj, many=True, **kwargs) return [self.inner._serialize(each, attr, obj, **kwargs) for each in value] def _deserialize(self, value, attr, data, **kwargs) -> typing.List[typing.Any]: if not utils.is_collection(value): raise self.make_error("invalid") - # Optimize loading a list of Nested objects by calling load(many=True) - if isinstance(self.inner, Nested) and not self.inner.many: - return self.inner.deserialize(value, many=True, **kwargs) result = [] errors = {} @@ -843,10 +847,6 @@ except (ValueError, AttributeError, TypeError) as error: raise self.make_error("invalid_uuid") from error - def _serialize(self, value, attr, obj, **kwargs) -> typing.Optional[str]: - val = str(value) if value is not None else None - return super()._serialize(val, attr, obj, **kwargs) - def _deserialize(self, value, attr, data, **kwargs) -> typing.Optional[uuid.UUID]: return self._validated(value) @@ -865,11 +865,11 @@ "too_large": "Number too large.", } - def __init__(self, *, as_string=False, **kwargs): + def __init__(self, *, as_string: bool = False, **kwargs): self.as_string = as_string super().__init__(**kwargs) - def _format_num(self, value) -> _T: + def _format_num(self, value) -> typing.Any: """Return the number value for value, given this field's `num_type`.""" return self.num_type(value) @@ -943,7 +943,7 @@ "special": "Special numeric values (nan or infinity) are not permitted." } - def __init__(self, *, allow_nan=False, as_string=False, **kwargs): + def __init__(self, *, allow_nan: bool = False, as_string: bool = False, **kwargs): self.allow_nan = allow_nan super().__init__(as_string=as_string, **kwargs) @@ -1580,18 +1580,13 @@ self.relative = relative self.require_tld = require_tld # Insert validation into self.validators so that multiple errors can be stored. - original_validators = list(self.validators) - # FIXME: Why doesn't mypy think validate.URL is a callable here? - validator = typing.cast( - typing.Callable[[typing.Any], typing.Any], - validate.URL( - relative=self.relative, - schemes=schemes, - require_tld=self.require_tld, - error=self.error_messages["invalid"], - ), + validator = validate.URL( + relative=self.relative, + schemes=schemes, + require_tld=self.require_tld, + error=self.error_messages["invalid"], ) - self.validators = [validator] + original_validators + self.validators.insert(0, validator) class Email(String): @@ -1607,9 +1602,8 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Insert validation into self.validators so that multiple errors can be stored. - original_validators = list(self.validators) validator = validate.Email(error=self.error_messages["invalid"]) - self.validators = [validator] + original_validators + self.validators.insert(0, validator) class Method(Field): @@ -1635,7 +1629,7 @@ _CHECK_ATTRIBUTE = False - def __init__(self, serialize=None, deserialize=None, **kwargs): + def __init__(self, serialize: str = None, deserialize: str = None, **kwargs): # Set dump_only and load_only based on arguments kwargs["dump_only"] = bool(serialize) and not bool(deserialize) kwargs["load_only"] = bool(deserialize) and not bool(serialize) @@ -1735,7 +1729,7 @@ _CHECK_ATTRIBUTE = False - def __init__(self, constant, **kwargs): + def __init__(self, constant: typing.Any, **kwargs): super().__init__(**kwargs) self.constant = constant self.missing = constant diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/src/marshmallow/schema.py new/marshmallow-3.5.1/src/marshmallow/schema.py --- old/marshmallow-3.2.2/src/marshmallow/schema.py 2019-11-04 22:14:53.000000000 +0100 +++ new/marshmallow-3.5.1/src/marshmallow/schema.py 2020-03-05 14:05:11.000000000 +0100 @@ -133,7 +133,7 @@ inherited_fields: typing.List, dict_cls: type, ): - """Returns a dictionary of field_name => `Field` pairs declard on the class. + """Returns a dictionary of field_name => `Field` pairs declared on the class. This is exposed mainly so that plugins can add additional fields, e.g. fields computed from class Meta options. @@ -242,15 +242,18 @@ from marshmallow import Schema, fields + @dataclass class Album: title: str release_date: dt.date + class AlbumSchema(Schema): title = fields.Str() release_date = fields.Date() + album = Album("Beggars Banquet", dt.date(1968, 12, 6)) schema = AlbumSchema() data = schema.dump(album) @@ -373,7 +376,7 @@ # Raise error if only or exclude is passed as string, not list of strings if only is not None and not is_collection(only): raise StringNotCollectionError('"only" should be a list of strings') - if exclude is not None and not is_collection(exclude): + if not is_collection(exclude): raise StringNotCollectionError('"exclude" should be a list of strings') # copy declared fields from metaclass self.declared_fields = copy.deepcopy(self._declared_fields) @@ -579,7 +582,10 @@ def _deserialize( self, - data: typing.Union[typing.Dict, typing.Iterable], + data: typing.Union[ + typing.Mapping[str, typing.Any], + typing.Iterable[typing.Mapping[str, typing.Any]], + ], *, error_store: ErrorStore, many: bool = False, @@ -613,7 +619,7 @@ typing.cast( _T, self._deserialize( - d, + typing.cast(typing.Mapping[str, typing.Any], d), error_store=error_store, many=False, partial=partial, @@ -684,7 +690,10 @@ def load( self, - data: typing.Mapping, + data: typing.Union[ + typing.Mapping[str, typing.Any], + typing.Iterable[typing.Mapping[str, typing.Any]], + ], *, many: bool = None, partial: typing.Union[bool, types.StrSequenceOrSet] = None, @@ -797,7 +806,10 @@ def _do_load( self, - data: typing.Mapping, + data: typing.Union[ + typing.Mapping[str, typing.Any], + typing.Iterable[typing.Mapping[str, typing.Any]], + ], *, many: bool = None, partial: typing.Union[bool, types.StrSequenceOrSet] = None, @@ -1025,9 +1037,13 @@ try: field_obj._bind_to_schema(field_name, self) except TypeError as error: - # field declared as a class, not an instance - if isinstance(field_obj, type) and issubclass(field_obj, base.FieldABC): - msg = ( + # Field declared as a class, not an instance. Ignore type checking because + # we handle unsupported arg types, i.e. this is dead code from + # the type checker's perspective. + if isinstance(field_obj, type) and issubclass( # type: ignore + field_obj, base.FieldABC + ): + msg = ( # type: ignore 'Field for "{}" must be declared as a ' "Field instance, not a class. " 'Did you mean "fields.{}()"?'.format(field_name, field_obj.__name__) @@ -1193,12 +1209,7 @@ processor_kwargs = processor.__marshmallow_hook__[key] pass_original = processor_kwargs.get("pass_original", False) - if pass_many: - if pass_original: - data = processor(data, original_data, many=many, **kwargs) - else: - data = processor(data, many=many, **kwargs) - elif many: + if many and not pass_many: if pass_original: data = [ processor(item, original, many=many, **kwargs) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/src/marshmallow/validate.py new/marshmallow-3.5.1/src/marshmallow/validate.py --- old/marshmallow-3.2.2/src/marshmallow/validate.py 2019-11-04 22:14:53.000000000 +0100 +++ new/marshmallow-3.5.1/src/marshmallow/validate.py 2020-03-05 14:05:11.000000000 +0100 @@ -70,7 +70,7 @@ r")?" if relative else r"", # host is optional, allow for relative URLs - r"(?:/?|[/?]\S+)$", + r"(?:/?|[/?]\S+)\Z", ) ), re.IGNORECASE, @@ -134,19 +134,19 @@ """ USER_REGEX = re.compile( - r"(^[-!#$%&'*+/=?^`{}|~\w]+(\.[-!#$%&'*+/=?^`{}|~\w]+)*$" # dot-atom + r"(^[-!#$%&'*+/=?^`{}|~\w]+(\.[-!#$%&'*+/=?^`{}|~\w]+)*\Z" # dot-atom # quoted-string r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]' - r'|\\[\001-\011\013\014\016-\177])*"$)', + r'|\\[\001-\011\013\014\016-\177])*"\Z)', re.IGNORECASE | re.UNICODE, ) DOMAIN_REGEX = re.compile( # domain - r"(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+" r"(?:[A-Z]{2,6}|[A-Z0-9-]{2,})$" + r"(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+" r"(?:[A-Z]{2,6}|[A-Z0-9-]{2,})\Z" # literal form, ipv4 address (SMTP 4.1.3) r"|^\[(25[0-5]|2[0-4]\d|[0-1]?\d?\d)" - r"(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\]$", + r"(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\]\Z", re.IGNORECASE | re.UNICODE, ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/src/marshmallow.egg-info/PKG-INFO new/marshmallow-3.5.1/src/marshmallow.egg-info/PKG-INFO --- old/marshmallow-3.2.2/src/marshmallow.egg-info/PKG-INFO 2019-11-04 22:15:05.000000000 +0100 +++ new/marshmallow-3.5.1/src/marshmallow.egg-info/PKG-INFO 2020-03-05 14:05:19.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: marshmallow -Version: 3.2.2 +Version: 3.5.1 Summary: A lightweight library for converting complex datatypes to and from native Python datatypes. Home-page: https://github.com/marshmallow-code/marshmallow Author: Steven Loria diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/src/marshmallow.egg-info/SOURCES.txt new/marshmallow-3.5.1/src/marshmallow.egg-info/SOURCES.txt --- old/marshmallow-3.2.2/src/marshmallow.egg-info/SOURCES.txt 2019-11-04 22:15:05.000000000 +0100 +++ new/marshmallow-3.5.1/src/marshmallow.egg-info/SOURCES.txt 2020-03-05 14:05:19.000000000 +0100 @@ -5,6 +5,7 @@ MANIFEST.in NOTICE README.rst +pyproject.toml setup.cfg setup.py docs/.gitignore diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/src/marshmallow.egg-info/requires.txt new/marshmallow-3.5.1/src/marshmallow.egg-info/requires.txt --- old/marshmallow-3.2.2/src/marshmallow.egg-info/requires.txt 2019-11-04 22:15:05.000000000 +0100 +++ new/marshmallow-3.5.1/src/marshmallow.egg-info/requires.txt 2020-03-05 14:05:19.000000000 +0100 @@ -3,23 +3,23 @@ pytest pytz simplejson -mypy==0.740 +mypy==0.761 flake8==3.7.9 -flake8-bugbear==19.8.0 -pre-commit~=1.20 +flake8-bugbear==20.1.4 +pre-commit<3.0,>=1.20 tox [docs] -sphinx==2.2.1 +sphinx==2.4.3 sphinx-issues==1.2.0 alabaster==0.7.12 sphinx-version-warning==1.1.2 [lint] -mypy==0.740 +mypy==0.761 flake8==3.7.9 -flake8-bugbear==19.8.0 -pre-commit~=1.20 +flake8-bugbear==20.1.4 +pre-commit<3.0,>=1.20 [tests] pytest diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/tests/base.py new/marshmallow-3.5.1/tests/base.py --- old/marshmallow-3.2.2/tests/base.py 2019-11-04 22:14:53.000000000 +0100 +++ new/marshmallow-3.5.1/tests/base.py 2020-03-05 14:05:11.000000000 +0100 @@ -275,21 +275,21 @@ class BlogSchema(Schema): title = fields.String() user = fields.Nested(UserSchema) - collaborators = fields.Nested(UserSchema, many=True) + collaborators = fields.List(fields.Nested(UserSchema())) categories = fields.List(fields.String) id = fields.String() class BlogUserMetaSchema(Schema): user = fields.Nested(UserMetaSchema()) - collaborators = fields.Nested(UserMetaSchema, many=True) + collaborators = fields.List(fields.Nested(UserMetaSchema())) class BlogSchemaMeta(Schema): """Same as BlogSerializer but using ``fields`` options.""" user = fields.Nested(UserSchema) - collaborators = fields.Nested(UserSchema, many=True) + collaborators = fields.List(fields.Nested(UserSchema())) class Meta: fields = ("title", "user", "collaborators", "categories", "id") @@ -298,7 +298,7 @@ class BlogOnlySchema(Schema): title = fields.String() user = fields.Nested(UserSchema) - collaborators = fields.Nested(UserSchema, only=("id",), many=True) + collaborators = fields.List(fields.Nested(UserSchema(only=("id",)))) class BlogSchemaExclude(BlogSchema): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/tests/test_deserialization.py new/marshmallow-3.5.1/tests/test_deserialization.py --- old/marshmallow-3.2.2/tests/test_deserialization.py 2019-11-04 22:14:53.000000000 +0100 +++ new/marshmallow-3.5.1/tests/test_deserialization.py 2020-03-05 14:05:11.000000000 +0100 @@ -38,6 +38,15 @@ field = fields.Tuple([fields.String()], allow_none=True) assert field.deserialize(None) is None + def test_list_of_nested_allow_none_deserialize_none_to_none(self): + field = fields.List(fields.Nested(Schema(), allow_none=True)) + assert field.deserialize([None]) == [None] + + def test_list_of_nested_non_allow_none_deserialize_none_to_validation_error(self): + field = fields.List(fields.Nested(Schema(), allow_none=False)) + with pytest.raises(ValidationError): + field.deserialize([None]) + class TestFieldDeserialization: def test_float_field_deserialization(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/tests/test_fields.py new/marshmallow-3.5.1/tests/test_fields.py --- old/marshmallow-3.2.2/tests/test_fields.py 2019-11-04 22:14:53.000000000 +0100 +++ new/marshmallow-3.5.1/tests/test_fields.py 2020-03-05 14:05:11.000000000 +0100 @@ -377,6 +377,34 @@ "children": [{"name": "Lily"}] } + @pytest.mark.parametrize( + ("param", "expected_attribute", "expected_dump"), + ( + ("only", {"name"}, {"children": [{"name": "Lily"}]}), + ("exclude", {"name", "surname", "age"}, {"children": [{}]}), + ), + ) + def test_list_nested_lambda_only_and_exclude_merged_with_nested( + self, param, expected_attribute, expected_dump + ): + class Child(Schema): + name = fields.String() + surname = fields.String() + age = fields.Integer() + + class Family(Schema): + children = fields.List( + fields.Nested(lambda: Child(**{param: ("name", "surname")})) + ) + + schema = Family(**{param: ["children.name", "children.age"]}) + assert ( + getattr(schema.fields["children"].inner.schema, param) == expected_attribute + ) + + family = {"children": [{"name": "Lily", "surname": "Martinez", "age": 15}]} + assert schema.dump(family) == expected_dump + def test_list_nested_partial_propagated_to_nested(self): class Child(Schema): name = fields.String(required=True) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/tests/test_schema.py new/marshmallow-3.5.1/tests/test_schema.py --- old/marshmallow-3.2.2/tests/test_schema.py 2019-11-04 22:14:53.000000000 +0100 +++ new/marshmallow-3.5.1/tests/test_schema.py 2020-03-05 14:05:11.000000000 +0100 @@ -1136,11 +1136,9 @@ def test_nested_custom_set_not_implementing_getitem(): - """ - This test checks that Marshmallow can serialize implementations of - :mod:`collections.abc.MutableSequence`, with ``__getitem__`` arguments - that are not integers. - """ + # This test checks that marshmallow can serialize implementations of + # :mod:`collections.abc.MutableSequence`, with ``__getitem__`` arguments + # that are not integers. class ListLikeParent: """ @@ -1226,6 +1224,55 @@ assert "bah" not in grand_child +def test_nested_lambda(): + class ChildSchema(Schema): + id = fields.Str() + name = fields.Str() + parent = fields.Nested(lambda: ParentSchema(only=("id",)), dump_only=True) + siblings = fields.List(fields.Nested(lambda: ChildSchema(only=("id", "name")))) + + class ParentSchema(Schema): + id = fields.Str() + spouse = fields.Nested(lambda: ParentSchema(only=("id",))) + children = fields.List( + fields.Nested(lambda: ChildSchema(only=("id", "parent", "siblings"))) + ) + + sch = ParentSchema() + data_to_load = { + "id": "p1", + "spouse": {"id": "p2"}, + "children": [{"id": "c1", "siblings": [{"id": "c2", "name": "sis"}]}], + } + loaded = sch.load(data_to_load) + assert loaded == data_to_load + + data_to_dump = dict( + id="p2", + spouse=dict(id="p2"), + children=[ + dict( + id="c1", + name="bar", + parent=dict(id="p2"), + siblings=[dict(id="c2", name="sis")], + ) + ], + ) + dumped = sch.dump(data_to_dump) + assert dumped == { + "id": "p2", + "spouse": {"id": "p2"}, + "children": [ + { + "id": "c1", + "parent": {"id": "p2"}, + "siblings": [{"id": "c2", "name": "sis"}], + } + ], + } + + @pytest.mark.parametrize("data_key", ("f1", "f5", None)) def test_data_key_collision(data_key): class MySchema(Schema): @@ -1888,17 +1935,6 @@ ) return blog - class FlatBlogSchema(Schema): - name = fields.String() - user = fields.Nested(UserSchema, only="name") - collaborators = fields.Nested(UserSchema, only="name", many=True) - - s = FlatBlogSchema() - data = s.dump(blog) - assert data["user"] == blog.user.name - for i, name in enumerate(data["collaborators"]): - assert name == blog.collaborators[i].name - # regression test for https://github.com/marshmallow-code/marshmallow/issues/64 def test_nested_many_with_missing_attribute(self, user): class SimpleBlogSchema(Schema): @@ -2163,13 +2199,15 @@ def user(self, employer): return User(name="Tom", employer=employer, age=28) - def test_nesting_schema_within_itself(self, user, employer): - class SelfSchema(Schema): - name = fields.String() - age = fields.Integer() - employer = fields.Nested("self", exclude=("employer",)) + def test_nesting_schema_by_passing_lambda(self, user, employer): + class SelfReferencingSchema(Schema): + name = fields.Str() + age = fields.Int() + employer = fields.Nested( + lambda: SelfReferencingSchema(exclude=("employer",)) + ) - data = SelfSchema().dump(user) + data = SelfReferencingSchema().dump(user) assert data["name"] == user.name assert data["employer"]["name"] == employer.name assert data["employer"]["age"] == employer.age @@ -2185,9 +2223,24 @@ assert data["employer"]["name"] == employer.name assert data["employer"]["age"] == employer.age + def test_nesting_schema_self_string(self, user, employer): + with pytest.warns( + DeprecationWarning, match="Passing 'self' to `Nested` is deprecated" + ): + + class SelfSchema(Schema): + name = fields.String() + age = fields.Integer() + employer = fields.Nested("self", exclude=("employer",)) + + data = SelfSchema().dump(user) + assert data["name"] == user.name + assert data["employer"]["name"] == employer.name + assert data["employer"]["age"] == employer.age + def test_nesting_within_itself_meta(self, user, employer): class SelfSchema(Schema): - employer = fields.Nested("self", exclude=("employer",)) + employer = fields.Nested(lambda: SelfSchema(exclude=("employer",))) class Meta: additional = ("name", "age") @@ -2200,7 +2253,7 @@ def test_nested_self_with_only_param(self, user, employer): class SelfSchema(Schema): - employer = fields.Nested("self", only=("name",)) + employer = fields.Nested(lambda: SelfSchema(only=("name",))) class Meta: fields = ("name", "employer") @@ -2210,10 +2263,14 @@ assert data["employer"]["name"] == employer.name assert "age" not in data["employer"] - def test_multiple_pluck_self_field(self, user): + def test_multiple_pluck_self_lambda(self, user): class MultipleSelfSchema(Schema): - emp = fields.Pluck("self", "name", attribute="employer") - rels = fields.Pluck("self", "name", many=True, attribute="relatives") + emp = fields.Pluck( + lambda: MultipleSelfSchema(), "name", attribute="employer" + ) + rels = fields.Pluck( + lambda: MultipleSelfSchema(), "name", many=True, attribute="relatives" + ) class Meta: fields = ("name", "emp", "rels") @@ -2225,9 +2282,28 @@ relative = data["rels"][0] assert relative == user.relatives[0].name - def test_nested_self_many(self): + def test_multiple_pluck_self_string(self, user): + with pytest.warns( + DeprecationWarning, match="Passing 'self' to `Nested` is deprecated" + ): + + class MultipleSelfSchema(Schema): + emp = fields.Pluck("self", "name", attribute="employer") + rels = fields.Pluck("self", "name", many=True, attribute="relatives") + + class Meta: + fields = ("name", "emp", "rels") + + schema = MultipleSelfSchema() + user.relatives = [User(name="Bar", age=12), User(name="Baz", age=34)] + data = schema.dump(user) + assert len(data["rels"]) == len(user.relatives) + relative = data["rels"][0] + assert relative == user.relatives[0].name + + def test_nested_self_many_lambda(self): class SelfManySchema(Schema): - relatives = fields.Nested("self", many=True) + relatives = fields.Nested(lambda: SelfManySchema(), many=True) class Meta: additional = ("name", "age") @@ -2240,15 +2316,53 @@ assert data["relatives"][0]["name"] == person.relatives[0].name assert data["relatives"][0]["age"] == person.relatives[0].age + def test_nested_self_many_string(self): + with pytest.warns( + DeprecationWarning, match="Passing 'self' to `Nested` is deprecated" + ): + + class SelfManySchema(Schema): + relatives = fields.Nested("self", many=True) + + class Meta: + additional = ("name", "age") + + person = User(name="Foo") + person.relatives = [User(name="Bar", age=12), User(name="Baz", age=34)] + data = SelfManySchema().dump(person) + assert data["name"] == person.name + assert len(data["relatives"]) == len(person.relatives) + assert data["relatives"][0]["name"] == person.relatives[0].name + assert data["relatives"][0]["age"] == person.relatives[0].age + def test_nested_self_list(self): class SelfListSchema(Schema): - relatives = fields.List(fields.Nested("self")) + relatives = fields.List(fields.Nested(lambda: SelfListSchema())) class Meta: additional = ("name", "age") person = User(name="Foo") person.relatives = [User(name="Bar", age=12), User(name="Baz", age=34)] + data = SelfListSchema().dump(person) + assert data["name"] == person.name + assert len(data["relatives"]) == len(person.relatives) + assert data["relatives"][0]["name"] == person.relatives[0].name + assert data["relatives"][0]["age"] == person.relatives[0].age + + def test_nested_self_list_string(self): + with pytest.warns( + DeprecationWarning, match="Passing 'self' to `Nested` is deprecated" + ): + + class SelfListSchema(Schema): + relatives = fields.List(fields.Nested("self")) + + class Meta: + additional = ("name", "age") + + person = User(name="Foo") + person.relatives = [User(name="Bar", age=12), User(name="Baz", age=34)] data = SelfListSchema().dump(person) assert data["name"] == person.name assert len(data["relatives"]) == len(person.relatives) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/marshmallow-3.2.2/tests/test_validate.py new/marshmallow-3.5.1/tests/test_validate.py --- old/marshmallow-3.2.2/tests/test_validate.py 2019-11-04 22:14:53.000000000 +0100 +++ new/marshmallow-3.5.1/tests/test_validate.py 2020-03-05 14:05:11.000000000 +0100 @@ -41,6 +41,7 @@ "http:///example.com/", "https:///example.com/", "https://example.org\\", + "https://example.org\n", "ftp:///example.com/", "ftps:///example.com/", "http//example.org", @@ -85,6 +86,7 @@ "invalid_url", [ "http//example.org", + "http://example.org\n", "suppliers.html", "../icons/logo.gif", "icons/logo.gif", @@ -123,6 +125,7 @@ "invalid_url", [ "http//example", + "http://example\n", "http://.example.org", "http:///foo/bar", "http:// /foo/bar", @@ -197,6 +200,8 @@ @pytest.mark.parametrize( "invalid_email", [ + "niceandsimple\[email protected]", + "[email protected]\n", 'a"b(c)d,e:f;g<h>i[j\\k][email protected]', 'just"not"[email protected]', 'this is"not\[email protected]',
