You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@qpid.apache.org by GitBox <gi...@apache.org> on 2019/10/14 13:44:31 UTC

[GitHub] [qpid-proton] kpvdr commented on a change in pull request #192: Changed Python API to better handle strings where symbo…

kpvdr commented on a change in pull request #192: Changed Python API to better handle strings where symbo…
URL: https://github.com/apache/qpid-proton/pull/192#discussion_r334489552
 
 

 ##########
 File path: python/proton/_data.py
 ##########
 @@ -308,6 +308,222 @@ def __eq__(self, o):
             return False
 
 
+def _check_type(s, allow_ulong=False, throw=True):
+    if isinstance(s, symbol):
+        return s
+    if allow_ulong and isinstance(s, ulong):
+        return s
+    if isinstance(s, str):
+        # Must be py2 or py3 str
+        return symbol(s)
+    if isinstance(s, unicode):
+        # This must be python2 unicode as we already detected py3 str above
+        return symbol(s.encode('utf-8'))
+    if throw:
+        raise TypeError('Non-symbol type %s: %s' % (type(s), s))
+    return s
+
+def check_is_symbol(s, throw=True):
+    """
+    Check if s is a symbol type. If s is a string, convert to a symbol
+    and return it. If s is already a symbol, just return it.
+    For other types of s, if throw == True, raise a ``TypeError``,
+    otherwise return s without any changes.
+
+    :param s: Any object that should be checked
+    :type s: any
+    :param throw: If true, raise exception if non-allowed type is found.
+                  Otherwise, return non-allowed types without any changes.
+    :type throw: ``bool``
+    """
+    return _check_type(s, allow_ulong=False, throw=throw)
+
+def check_is_symbol_or_ulong(s, throw=True):
+    """
+    Check if s is a symbol or ulong type. If s is a string, convert to
+    a symbol and return it. If s is already a symbol, just return it.
+    For other types of s, if throw == True, raise a ``TypeError``,
+    otherwise return s without any changes.
+
+    :param s: Any object that should be checked
+    :type s: any
+    :param throw: If true, raise exception if non-allowed type is found.
+                  Otherwise, return non-allowed types without any changes.
+    :type throw: ``bool``
+    """
+    return _check_type(s, allow_ulong=True, throw=throw)
+
+
+class RestrictedKeyDict(dict):
+    """Parent class for :class:`PropertyDict` and :class:`AnnotationDict`"""
+    def __init__(self, validation_fn, e=None, throw=True, **kwargs):
+        super(RestrictedKeyDict, self).__init__()
+        self.validation_fn = validation_fn
+        self.throw = throw
+        self.update(e, **kwargs)
+
+    def __setitem__(self, key, value):
+        """Checks if the key is a :class:`symbol` type before setting the value"""
+        try:
+            return super(RestrictedKeyDict, self).__setitem__(self.validation_fn(key, self.throw), value)
+        except TypeError:
+            pass
+        # __setitem__() must raise a KeyError, not TypeError
+        raise KeyError('invalid non-symbol key: %s: %s' % (type(key), key))
+
+    def update(self, e=None, **kwargs):
+        """
+        Equivalent to dict.update(), but it was needed to call :meth:`__setitem__()`
+        instead of ``dict.__setitem__()``.
+        """
+        if e:
+            try:
+                for k in e:
+                    self.__setitem__(k, e[k])
+            except TypeError:
+                self.__setitem__(k[0], k[1]) # use tuple consumed from from zip
+                for (k, v) in e:
+                    self.__setitem__(k, v)
+        for k in kwargs:
+            self.__setitem__(k, kwargs[k])
+
+class PropertyDict(RestrictedKeyDict):
+    """
+    A dictionary that only takes :class:`symbol` types as a key.
+    However, if a string key is provided, it will be silently converted
+    into a symbol key.
+
+        >>> from proton import symbol, ulong, PropertyDict
+        >>> a = PropertyDict(one=1, two=2)
+        >>> b = PropertyDict({'one':1, symbol('two'):2})
+        >>> c = PropertyDict(zip(['one', symbol('two')], [1, 2]))
+        >>> d = PropertyDict([(symbol('one'), 1), ('two', 2)])
+        >>> e = PropertyDict(a)
+        >>> a == b == c == d == e
+        True
+
+    By default, non-string and non-symbol keys cause a KeyError to be raised:
+
+        >>> PropertyDict({'one':1, 2:'two'})
+          ...
+        KeyError: "invalid non-symbol key: <type 'int'>: 2"
+
+    but by setting throw=False, non-string and non-symbol keys will be ignored:
+
+        >>> PropertyDict({'one':1, 2:'two'}, throw=False)
+        PropertyDict({2: 'two', symbol(u'one'): 1})
+
+    """
+    def __init__(self, e=None, throw=True, **kwargs):
+        super(PropertyDict, self).__init__(check_is_symbol, e, throw, **kwargs)
+
+    def __repr__(self):
+        """ Representation of PropertyDict """
+        return 'PropertyDict(%s)' % super(PropertyDict, self).__repr__()
+
+
+class AnnotationDict(RestrictedKeyDict):
+    """
+    A dictionary that only takes :class:`symbol` or :class:`ulong` types
+    as a key. However, if a string key is provided, it will be silently
+    converted into a symbol key.
+
+        >>> from proton import symbol, ulong, AnnotationDict
+        >>> a = AnnotationDict(one=1, two=2)
+        >>> a[ulong(3)] = 'three'
+        >>> b = AnnotationDict({'one':1, symbol('two'):2, ulong(3):'three'})
+        >>> c = AnnotationDict(zip([symbol('one'), 'two', ulong(3)], [1, 2, 'three']))
+        >>> d = AnnotationDict([('one', 1), (symbol('two'), 2), (ulong(3), 'three')])
+        >>> e = AnnotationDict(a)
+        >>> a == b == c == d == e
+        True
+
+    By default, non-string, non-symbol and non-ulong keys cause a KeyError to be raised:
+
+        >>> AnnotationDict({'one': 1, 2: 'two'})
+          ...
+        KeyError: "invalid non-symbol key: <type 'int'>: 2"
+
 
 Review comment:
   I quite agree with this. If we are promoting strings to symbols, why not be consistent and promote ints to ulongs (provided they are positive)?

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@qpid.apache.org
For additional commands, e-mail: dev-help@qpid.apache.org