Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ ignore =
W503
# W504: line break after binary operator (flake8 is not PEP8 compliant)
W504
# B042: Invalid.__init__ documents and uses kwargs (msg/value); public API.
B042
show-source = True
max-line-length = 79
11 changes: 7 additions & 4 deletions .github/workflows/ci-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ on:
jobs:
test:
strategy:
fail-fast: false
matrix:
py:
- "3.7"
- "3.8"
- "3.9"
- "3.10"
Expand All @@ -25,16 +25,19 @@ jobs:
os:
- "ubuntu-latest"
- "windows-latest"
# later versions no longer support Python < 3.10.
- "macos-11"
- "macos-13"
architecture:
- x64
- x86
exclude:
# Linux and macOS don't have x86 python
- os: "ubuntu-latest"
architecture: x86
- os: "macos-11"
- os: "macos-13"
architecture: x86
# setup-python has no Windows PyPy 3.8 x86 builds
- os: "windows-latest"
py: "pypy-3.8"
architecture: x86
name: "Python: ${{ matrix.py }}-${{ matrix.architecture }} on ${{ matrix.os }}"
runs-on: ${{ matrix.os }}
Expand Down
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
# out serve to show the default value.

import sys, os, datetime
import pkg_resources
import pylons_sphinx_themes

# General configuration
Expand Down Expand Up @@ -41,7 +40,8 @@
# other places throughout the built documents.
#
# The short X.Y version.
version = pkg_resources.get_distribution('colander').version
from importlib.metadata import version as _get_version
version = _get_version('colander')
# The full version, including alpha/beta/rc tags.
release = version

Expand Down
6 changes: 6 additions & 0 deletions src/colander/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -957,6 +957,8 @@ def callback(subnode, subcstruct):

def flatten(self, node, appstruct, prefix='', listitem=False):
result = {}
if appstruct is null:
return result
if listitem:
selfprefix = prefix
else:
Expand Down Expand Up @@ -1096,6 +1098,8 @@ def callback(subnode, subval):

def flatten(self, node, appstruct, prefix='', listitem=False):
result = {}
if appstruct is null:
return result
if listitem:
selfprefix = prefix
else:
Expand Down Expand Up @@ -1362,6 +1366,8 @@ def callback(subnode, subcstruct):

def flatten(self, node, appstruct, prefix='', listitem=False):
result = {}
if appstruct is null:
return result
if listitem:
selfprefix = prefix
else:
Expand Down
51 changes: 51 additions & 0 deletions tests/test_colander.py
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,14 @@ def test_flatten(self):
result = typ.flatten(node, {'a': 1, 'b': 2})
self.assertEqual(result, {'node.appstruct': 2})

def test_flatten_null(self):
from colander import null

node = DummySchemaNode(None, name='node')
node.children = [DummySchemaNode(DummyType(), name='a')]
typ = self._makeOne()
self.assertEqual(typ.flatten(node, null), {})

def test_flatten_listitem(self):
node = DummySchemaNode(None, name='node')
int1 = DummyType()
Expand Down Expand Up @@ -1371,6 +1379,17 @@ def test_flatten(self):
result = typ.flatten(node, (1, 2))
self.assertEqual(result, {'node.appstruct': 2})

def test_flatten_null(self):
from colander import null

node = DummySchemaNode(None, name='node')
node.children = [
DummySchemaNode(DummyType(), name='a'),
DummySchemaNode(DummyType(), name='b'),
]
typ = self._makeOne()
self.assertEqual(typ.flatten(node, null), {})

def test_flatten_listitem(self):
node = DummySchemaNode(None, name='node')
int1 = DummyType()
Expand Down Expand Up @@ -1732,6 +1751,14 @@ def test_flatten(self):
result = typ.flatten(node, [1, 2])
self.assertEqual(result, {'node.0': 1, 'node.1': 2})

def test_flatten_null(self):
from colander import null

node = DummySchemaNode(None, name='node')
node.children = [DummySchemaNode(DummyType(), name='foo')]
typ = self._makeOne()
self.assertEqual(typ.flatten(node, null), {})

def test_flatten_with_integer(self):
from colander import Integer

Expand Down Expand Up @@ -4080,6 +4107,30 @@ class MySchema(colander.Schema):
result = node.deserialize(expected)
self.assertEqual(result, expected)

def test_flatten_after_deserialize_drop_containers(self):
# missing=drop omits the key from the appstruct; flatten must not
# raise when Mapping walks that absent child as colander.null.
class Seq(colander.SequenceSchema):
item = colander.SchemaNode(colander.String())

class Tup(colander.TupleSchema):
x = colander.SchemaNode(colander.Int())
y = colander.SchemaNode(colander.Int())

class Inner(colander.MappingSchema):
z = colander.SchemaNode(colander.String())

class MySchema(colander.Schema):
title = colander.SchemaNode(colander.String())
items = Seq(missing=colander.drop)
point = Tup(missing=colander.drop)
inner = Inner(missing=colander.drop)

node = MySchema()
appstruct = node.deserialize({'title': 't'})
self.assertEqual(appstruct, {'title': 't'})
self.assertEqual(node.flatten(appstruct), {'title': 't'})

def test_serialize_drop_default(self):
class MySchema(colander.Schema):
a = colander.SchemaNode(colander.String())
Expand Down
Loading