Repository: Pylons/colander

Issue:
Why not allow `None` in an appstruct serialized by Number?
relates to part of #186

A commit was made a while ago that removed the ability to pass `None` as an appstruct value to a `Number` type ( commit 513d860 ).  The commit says it's reverting a change related to **Strings** but then made this change to `Number`.  It seems like an odd change and is removing useful functionality.

Basically, it comes down to the fact `colander.null` is not equal to `None`.  This makes sense in the cases where `None` is a valid serialization.  From the docs:

> Note that colander.null has no relationship to the built-in Python None value. colander.null is used instead of None because None is a potentially valid value for some serializations and deserializations, and using it as a sentinel would prevent None from being used in this way.

However, in the cases where `None` is not a valid value, why not treat it as equal to `colander.null` for simplicity?  The very common use case is when using an SQL DB for storing of values.  An integer column that's nullable can store both integers and None.

Incidentally, where is the case where `None` is a valid value?


Discussion:

Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index 43c932c..669d34e 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,3 +1,10 @@
+Unreleased
+==========
+
+- Number once again will allow you to serialize None to colander.null, this
+  reverts an accidental revert. See
+  https://github.com/Pylons/colander/issues/204#issuecomment-459556100
+
 1.6.0 (2019-01-31)
 ==================
 
diff --git a/colander/__init__.py b/colander/__init__.py
index 8bddfd6..728eceb 100644
--- a/colander/__init__.py
+++ b/colander/__init__.py
@@ -1399,7 +1399,7 @@ class Number(SchemaType):
     num = None
 
     def serialize(self, node, appstruct):
-        if appstruct is null:
+        if appstruct in (null, None):
             return null
 
         try:


Test patch:
diff --git a/colander/tests/test_colander.py b/colander/tests/test_colander.py
index 12b904e..0d911d7 100644
--- a/colander/tests/test_colander.py
+++ b/colander/tests/test_colander.py
@@ -1752,6 +1752,14 @@ class TestInteger(unittest.TestCase):
         result = typ.serialize(node, val)
         self.assertEqual(result, colander.null)
 
+    def test_serialize_none(self):
+        import colander
+
+        node = DummySchemaNode(None)
+        typ = self._makeOne()
+        result = typ.serialize(node, None)
+        self.assertEqual(result, colander.null)
+
     def test_deserialize_emptystring(self):
         import colander
 
@@ -1812,6 +1820,21 @@ class TestFloat(unittest.TestCase):
         result = typ.serialize(node, val)
         self.assertEqual(result, colander.null)
 
+    def test_serialize_none(self):
+        import colander
+
+        node = DummySchemaNode(None)
+        typ = self._makeOne()
+        result = typ.serialize(node, None)
+        self.assertEqual(result, colander.null)
+
+    def test_serialize_zero(self):
+        val = 0
+        node = DummySchemaNode(None)
+        typ = self._makeOne()
+        result = typ.serialize(node, val)
+        self.assertEqual(result, '0.0')
+
     def test_serialize_emptystring(self):
         import colander
 
@@ -1865,6 +1888,21 @@ class TestDecimal(unittest.TestCase):
         result = typ.serialize(node, val)
         self.assertEqual(result, colander.null)
 
+    def test_serialize_none(self):
+        import colander
+
+        node = DummySchemaNode(None)
+        typ = self._makeOne()
+        result = typ.serialize(node, None)
+        self.assertEqual(result, colander.null)
+
+    def test_serialize_zero(self):
+        val = 0
+        node = DummySchemaNode(None)
+        typ = self._makeOne()
+        result = typ.serialize(node, val)
+        self.assertEqual(result, '0')
+
     def test_serialize_emptystring(self):
         import colander