Skip to content

Commit

Permalink
Make GlobalObject only allow dotted names.
Browse files Browse the repository at this point in the history
Fixes #6

Also make all fields provide the ``value`` and ``field`` when they
raise an exception.
  • Loading branch information
jamadden committed Sep 25, 2018
1 parent cd96619 commit 1e217c8
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 19 deletions.
8 changes: 8 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ Changes
doctests on both Python 2 and Python 3. See `issue 21
<https://github.com/zopefoundation/zope.configuration/issues/21>`_.

- Fix ``GlobalObject`` and ``GlobalInterface`` fields to only accept
dotted names instead of names with ``/``. Previously, slash
delimited names could result in incorrect imports. See `issue 6
<https://github.com/zopefoundation/zope.configuration/issues/6>`_.

- Fix the schema fields to include the ``value`` and ``field`` values
on exceptions they raise.

4.1.0 (2017-04-26)
------------------

Expand Down
52 changes: 38 additions & 14 deletions src/zope/configuration/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@

from zope.interface import implementer
from zope.schema import Bool as schema_Bool
from zope.schema import DottedName
from zope.schema import Field
from zope.schema import InterfaceField
from zope.schema import List
from zope.schema import Text
from zope.schema import TextLine
from zope.schema import ValidationError
from zope.schema.interfaces import IFromUnicode
from zope.schema.interfaces import InvalidValue

from zope.configuration.exceptions import ConfigurationError
from zope.configuration.interfaces import InvalidToken
Expand All @@ -38,21 +40,32 @@

@implementer(IFromUnicode)
class PythonIdentifier(TextLine):
"""This field describes a python identifier, i.e. a variable name.
"""
def fromUnicode(self, u):
return u.strip()
This field describes a python identifier, i.e. a variable name.
Empty strings are allowed.
"""

def fromUnicode(self, value):
return value.strip()

def _validate(self, value):
super(PythonIdentifier, self)._validate(value)
if pyidentifierPattern.match(value) is None:
raise ValidationError(value)
raise ValidationError(value).with_field_and_value(self, value)


@implementer(IFromUnicode)
class GlobalObject(Field):
"""An object that can be accessed as a module global.
"""
An object that can be accessed as a module global.
The special value ``*`` indicates a value of `None`; this is
not validated against the *value_type*.
"""

_DOT_VALIDATOR = DottedName()

def __init__(self, value_type=None, **kw):
self.value_type = value_type
super(GlobalObject, self).__init__(**kw)
Expand All @@ -62,17 +75,26 @@ def _validate(self, value):
if self.value_type is not None:
self.value_type.validate(value)

def fromUnicode(self, u):
name = str(u.strip())
def fromUnicode(self, value):
name = str(value.strip())

# special case, mostly for interfaces
if name == '*':
return None

try:
# Leading dots are allowed here to indicate current
# package.
to_validate = name[1:] if name.startswith('.') else name
self._DOT_VALIDATOR.validate(to_validate)
except ValidationError as v:
v.with_field_and_value(self, name)
raise

try:
value = self.context.resolve(name)
except ConfigurationError as v:
raise ValidationError(v)
raise ValidationError(v).with_field_and_value(self, name)

self.validate(value)
return value
Expand All @@ -99,7 +121,7 @@ def fromUnicode(self, u):
try:
v = vt.fromUnicode(s)
except ValidationError as v:
raise InvalidToken("%s in %s" % (v, u))
raise InvalidToken("%s in %s" % (v, u)).with_field_and_value(self, s)
else:
values.append(v)
else:
Expand Down Expand Up @@ -131,13 +153,15 @@ class Bool(schema_Bool):
Values may be input (in upper or lower case) as any of:
yes, no, y, n, true, false, t, or f.
"""
def fromUnicode(self, u):
u = u.lower()
if u in ('1', 'true', 'yes', 't', 'y'):
def fromUnicode(self, value):
value = value.lower()
if value in ('1', 'true', 'yes', 't', 'y'):
return True
if u in ('0', 'false', 'no', 'f', 'n'):
if value in ('0', 'false', 'no', 'f', 'n'):
return False
raise ValidationError
# Unlike the superclass, anything else is invalid.
raise InvalidValue().with_field_and_value(self, value)



@implementer(IFromUnicode)
Expand Down
34 changes: 29 additions & 5 deletions src/zope/configuration/tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,13 @@ def test_fromUnicode_strips_ws(self):
def test__validate_miss(self):
from zope.schema import ValidationError
pi = self._makeOne()
with self.assertRaises(ValidationError):
with self.assertRaises(ValidationError) as exc:
pi._validate(u'not-an-identifier')

ex = exc.exception
self.assertIs(ex.field, pi)
self.assertEqual(ex.value, 'not-an-identifier')

def test__validate_hit(self):
pi = self._makeOne()
pi._validate(u'is_an_identifier')
Expand Down Expand Up @@ -107,9 +111,12 @@ def resolve(self, name):
go = self._makeOne()
context = Context()
bound = go.bind(context)
with self.assertRaises(ValidationError):
with self.assertRaises(ValidationError) as exc:
bound.fromUnicode('tried')
self.assertEqual(context._resolved, 'tried')
ex = exc.exception
self.assertIs(ex.field, bound)
self.assertEqual(ex.value, 'tried')

def test_fromUnicode_w_resolve_success(self):
_target = object()
Expand Down Expand Up @@ -141,6 +148,16 @@ def resolve(self, name):
bound.fromUnicode('tried')
self.assertEqual(context._resolved, 'tried')

def test_fromUnicode_rejects_slash(self):
from zope.schema import ValidationError
_target = object()
field = self._makeOne()
with self.assertRaises(ValidationError) as exc:
field.fromUnicode('foo/bar')
ex = exc.exception
self.assertIs(ex.field, field)
self.assertEqual(ex.value, 'foo/bar')


class GlobalInterfaceTests(unittest.TestCase, _ConformsToIFromUnicode):

Expand Down Expand Up @@ -179,9 +196,13 @@ def test_fromUnicode_invalid(self):
from zope.schema import Int
from zope.configuration.interfaces import InvalidToken
tok = self._makeOne(value_type=Int(min=0))
with self.assertRaises(InvalidToken):
with self.assertRaises(InvalidToken) as exc:
tok.fromUnicode(u' 1 -1 3 ')

ex = exc.exception
self.assertIs(ex.field, tok)
self.assertEqual(ex.value, '-1')


class PathTests(unittest.TestCase, _ConformsToIFromUnicode):

Expand Down Expand Up @@ -234,10 +255,13 @@ def test_fromUnicode_w_false_values(self):
self.assertEqual(bo.fromUnicode(value), False)

def test_fromUnicode_w_invalid(self):
from zope.schema import ValidationError
from zope.schema.interfaces import InvalidValue
bo = self._makeOne()
with self.assertRaises(ValidationError):
with self.assertRaises(InvalidValue) as exc:
bo.fromUnicode('notvalid')
ex = exc.exception
self.assertIs(ex.field, bo)
self.assertEqual(ex.value, 'notvalid')


class MessageIDTests(unittest.TestCase, _ConformsToIFromUnicode):
Expand Down

0 comments on commit 1e217c8

Please sign in to comment.