Merge lp:~cjwatson/storm/clarify-no-allow-none-default-none-exception into lp:storm

Proposed by Colin Watson
Status: Merged
Merged at revision: 576
Proposed branch: lp:~cjwatson/storm/clarify-no-allow-none-default-none-exception
Merge into: lp:storm
Diff against target: 168 lines (+64/-16)
6 files modified
NEWS (+6/-0)
storm/cextensions.c (+9/-0)
storm/tests/mocker.py (+4/-0)
storm/tests/properties.py (+8/-0)
storm/tests/variables.py (+30/-13)
storm/variables.py (+7/-3)
To merge this branch: bzr merge lp:~cjwatson/storm/clarify-no-allow-none-default-none-exception
Reviewer Review Type Date Requested Status
Ioana Lasc (community) Approve
Review via email: mp+416974@code.launchpad.net

Commit message

Clarify exception for a property with allow_none=False and default=None.

Description of the change

Creating a property with `allow_none=False, default=None` doesn't make sense, but it's sometimes easy to set things up that way by accident during refactoring. Storm immediately raises `NoneError` when creating the variable in that case. However, it's not obvious that the exception is due to the bad default, and I spent some time with a colleague today debugging a situation that turned out to be due to this.

Change the exception message from "None isn't acceptable as a value" to "None isn't acceptable as a default value" in this case, to clue future travellers into the fact that the problem relates to the default.

To post a comment you must log in.
Revision history for this message
Ioana Lasc (ilasc) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'NEWS'
--- NEWS 2021-05-25 09:19:59 +0000
+++ NEWS 2022-03-16 17:03:57 +0000
@@ -1,6 +1,12 @@
10.2610.26
2====2====
33
4Improvements
5------------
6
7- Clarify exception when creating a property with both allow_none=False and
8 default=None.
9
4Bug fixes10Bug fixes
5---------11---------
612
713
=== modified file 'storm/cextensions.c'
--- storm/cextensions.c 2019-09-30 22:58:36 +0000
+++ storm/cextensions.c 2022-03-16 17:03:57 +0000
@@ -581,6 +581,15 @@
581 /* self._allow_none = False */581 /* self._allow_none = False */
582 Py_INCREF(Py_False);582 Py_INCREF(Py_False);
583 REPLACE(self->_allow_none, Py_False);583 REPLACE(self->_allow_none, Py_False);
584 /* if value is None: */
585 if (value == Py_None) {
586 /* raise_none_error(column, default=True) */
587 tmp = PyObject_CallFunctionObjArgs(raise_none_error, column,
588 Py_True, NULL);
589 /* tmp should always be NULL here. */
590 Py_XDECREF(tmp);
591 goto error;
592 }
584 }593 }
585594
586 /* if value is not Undef: */595 /* if value is not Undef: */
587596
=== modified file 'storm/tests/mocker.py'
--- storm/tests/mocker.py 2019-11-21 16:06:52 +0000
+++ storm/tests/mocker.py 2022-03-16 17:03:57 +0000
@@ -310,6 +310,10 @@
310 failUnlessIdentical = failUnlessIs310 failUnlessIdentical = failUnlessIs
311 failIfIdentical = failIfIs311 failIfIdentical = failIfIs
312312
313 if sys.version_info < (3, 2):
314 def assertRaisesRegex(self, *args, **kwargs):
315 return self.assertRaisesRegexp(*args, **kwargs)
316
313317
314# --------------------------------------------------------------------318# --------------------------------------------------------------------
315# Mocker.319# Mocker.
316320
=== modified file 'storm/tests/properties.py'
--- storm/tests/properties.py 2020-02-10 15:30:23 +0000
+++ storm/tests/properties.py 2022-03-16 17:03:57 +0000
@@ -846,6 +846,14 @@
846 self.assertRaises(NoneError, variable.set, None)846 self.assertRaises(NoneError, variable.set, None)
847 self.assertEqual(variable.get(), value)847 self.assertEqual(variable.get(), value)
848848
849 # Test default=None and allow_none=False (incoherent).
850 Class.prop = func(name="name", default=None, allow_none=False)
851 column = Class.prop.__get__(None, Class)
852 self.assertEqual(column.name, "name")
853 self.assertEqual(column.table, Class)
854
855 self.assertRaises(NoneError, column.variable_factory)
856
849 # Test default_factory.857 # Test default_factory.
850 Class.prop = func(name="name", default_factory=lambda:value)858 Class.prop = func(name="name", default_factory=lambda:value)
851 column = Class.prop.__get__(None, Class)859 column = Class.prop.__get__(None, Class)
852860
=== modified file 'storm/tests/variables.py'
--- storm/tests/variables.py 2020-02-10 15:30:23 +0000
+++ storm/tests/variables.py 2022-03-16 17:03:57 +0000
@@ -136,27 +136,44 @@
136136
137 def test_set_none_with_allow_none(self):137 def test_set_none_with_allow_none(self):
138 variable = CustomVariable(allow_none=False)138 variable = CustomVariable(allow_none=False)
139 self.assertRaises(NoneError, variable.set, None)139 self.assertRaisesRegex(
140 NoneError, r"^None isn't acceptable as a value$",
141 variable.set, None)
140142
141 def test_set_none_with_allow_none_and_column(self):143 def test_set_none_with_allow_none_and_column(self):
142 column = Column("column_name")144 column = Column("column_name")
143 variable = CustomVariable(allow_none=False, column=column)145 variable = CustomVariable(allow_none=False, column=column)
144 try:146 self.assertRaisesRegex(
145 variable.set(None)147 NoneError, r"^None isn't acceptable as a value for column_name$",
146 except NoneError as e:148 variable.set, None)
147 self.assertTrue("column_name" in str(e))
148 else:
149 self.fail("NoneError not raised")
150149
151 def test_set_none_with_allow_none_and_column_with_table(self):150 def test_set_none_with_allow_none_and_column_with_table(self):
152 column = Column("column_name", SQLToken("table_name"))151 column = Column("column_name", SQLToken("table_name"))
153 variable = CustomVariable(allow_none=False, column=column)152 variable = CustomVariable(allow_none=False, column=column)
154 try:153 self.assertRaisesRegex(
155 variable.set(None)154 NoneError,
156 except NoneError as e:155 r"^None isn't acceptable as a value for table_name.column_name$",
157 self.assertTrue("table_name.column_name" in str(e))156 variable.set, None)
158 else:157
159 self.fail("NoneError not raised")158 def test_set_default_none_with_allow_none(self):
159 self.assertRaisesRegex(
160 NoneError, r"^None isn't acceptable as a default value$",
161 CustomVariable, allow_none=False, value=None)
162
163 def test_set_default_none_with_allow_none_and_column(self):
164 column = Column("column_name")
165 self.assertRaisesRegex(
166 NoneError,
167 r"^None isn't acceptable as a default value for column_name$",
168 CustomVariable, allow_none=False, value=None, column=column)
169
170 def test_set_default_none_with_allow_none_and_column_with_table(self):
171 column = Column("column_name", SQLToken("table_name"))
172 self.assertRaisesRegex(
173 NoneError,
174 r"^None isn't acceptable as a default value for "
175 r"table_name.column_name$",
176 CustomVariable, allow_none=False, value=None, column=column)
160177
161 def test_set_with_validator(self):178 def test_set_with_validator(self):
162 args = []179 args = []
163180
=== modified file 'storm/variables.py'
--- storm/variables.py 2020-05-26 10:37:54 +0000
+++ storm/variables.py 2022-03-16 17:03:57 +0000
@@ -69,9 +69,10 @@
69 __slots__ = ()69 __slots__ = ()
7070
7171
72def raise_none_error(column):72def raise_none_error(column, default=False):
73 description = "default value" if default else "value"
73 if not column:74 if not column:
74 raise NoneError("None isn't acceptable as a value")75 raise NoneError("None isn't acceptable as a %s" % description)
75 else:76 else:
76 from storm.expr import compile, CompileError77 from storm.expr import compile, CompileError
77 name = column.name78 name = column.name
@@ -81,7 +82,8 @@
81 name = "%s.%s" % (table, name)82 name = "%s.%s" % (table, name)
82 except CompileError:83 except CompileError:
83 pass84 pass
84 raise NoneError("None isn't acceptable as a value for %s" % name)85 raise NoneError(
86 "None isn't acceptable as a %s for %s" % (description, name))
8587
8688
87VariableFactory = partial89VariableFactory = partial
@@ -139,6 +141,8 @@
139 """141 """
140 if not allow_none:142 if not allow_none:
141 self._allow_none = False143 self._allow_none = False
144 if value is None:
145 raise_none_error(column, default=True)
142 if value is not Undef:146 if value is not Undef:
143 self.set(value, from_db)147 self.set(value, from_db)
144 elif value_factory is not Undef:148 elif value_factory is not Undef:

Subscribers

People subscribed via source and target branches

to status/vote changes: