avro-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From fo...@apache.org
Subject [avro] branch master updated: AVRO-2321: Avoid hasattr in Python 2 (#453)
Date Tue, 12 Feb 2019 15:24:26 GMT
This is an automated email from the ASF dual-hosted git repository.

fokko pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/avro.git


The following commit(s) were added to refs/heads/master by this push:
     new 2aa4946  AVRO-2321: Avoid hasattr in Python 2 (#453)
2aa4946 is described below

commit 2aa4946f42541369b31094538801c2caec251c67
Author: Michael A. Smith <michael@smith-li.com>
AuthorDate: Tue Feb 12 10:24:20 2019 -0500

    AVRO-2321: Avoid hasattr in Python 2 (#453)
---
 lang/py/src/avro/protocol.py | 41 ++++++++++++++---------------
 lang/py/src/avro/schema.py   | 62 +++++++++++++++++++++++---------------------
 2 files changed, 52 insertions(+), 51 deletions(-)

diff --git a/lang/py/src/avro/protocol.py b/lang/py/src/avro/protocol.py
index 68666cc..0cfaec1 100644
--- a/lang/py/src/avro/protocol.py
+++ b/lang/py/src/avro/protocol.py
@@ -5,9 +5,9 @@
 # to you under the Apache License, Version 2.0 (the
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
-# 
+#
 # http://www.apache.org/licenses/LICENSE-2.0
-# 
+#
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -62,12 +62,13 @@ class Protocol(object):
       if message_objects.has_key(name):
         fail_msg = 'Message name "%s" repeated.' % name
         raise ProtocolParseException(fail_msg)
-      elif not(hasattr(body, 'get') and callable(body.get)):
+      try:
+        request = body.get('request')
+        response = body.get('response')
+        errors = body.get('errors')
+      except AttributeError:
         fail_msg = 'Message name "%s" has non-object body %s.' % (name, body)
         raise ProtocolParseException(fail_msg)
-      request = body.get('request')
-      response = body.get('response')
-      errors = body.get('errors')
       message_objects[name] = Message(name, request, response, errors, names)
     return message_objects
 
@@ -79,21 +80,20 @@ class Protocol(object):
     elif not isinstance(name, basestring):
       fail_msg = 'The name property must be a string.'
       raise ProtocolParseException(fail_msg)
-    elif namespace is not None and not isinstance(namespace, basestring):
+    elif not (namespace is None or isinstance(namespace, basestring)):
       fail_msg = 'The namespace property must be a string.'
       raise ProtocolParseException(fail_msg)
-    elif types is not None and not isinstance(types, list):
+    elif not (types is None or isinstance(types, list)):
       fail_msg = 'The types property must be a list.'
       raise ProtocolParseException(fail_msg)
-    elif (messages is not None and 
-          not(hasattr(messages, 'get') and callable(messages.get))):
+    elif not (messages is None or callable(getattr(messages, 'get', None))):
       fail_msg = 'The messages property must be a JSON object.'
       raise ProtocolParseException(fail_msg)
 
     self._props = {}
     self.set_prop('name', name)
     type_names = schema.Names()
-    if namespace is not None: 
+    if namespace is not None:
       self.set_prop('namespace', namespace)
       type_names.default_namespace = namespace
     if types is not None:
@@ -105,7 +105,7 @@ class Protocol(object):
   # read-only properties
   name = property(lambda self: self.get_prop('name'))
   namespace = property(lambda self: self.get_prop('namespace'))
-  fullname = property(lambda self: 
+  fullname = property(lambda self:
                       schema.Name(self.name, self.namespace).fullname)
   types = property(lambda self: self.get_prop('types'))
   types_dict = property(lambda self: dict([(type.name, type)
@@ -118,13 +118,13 @@ class Protocol(object):
   def get_prop(self, key):
     return self.props.get(key)
   def set_prop(self, key, value):
-    self.props[key] = value  
+    self.props[key] = value
 
   def to_json(self):
     to_dump = {}
     to_dump['protocol'] = self.name
     names = schema.Names(default_namespace=self.namespace)
-    if self.namespace: 
+    if self.namespace:
       to_dump['namespace'] = self.namespace
     if self.types:
       to_dump['types'] = [ t.to_json(names) for t in self.types ]
@@ -149,7 +149,7 @@ class Message(object):
       fail_msg = 'Request property not a list: %s' % request
       raise ProtocolParseException(fail_msg)
     return schema.RecordSchema(None, None, request, names, 'request')
-  
+
   def _parse_response(self, response, names):
     if isinstance(response, basestring) and names.has_name(response, None):
       return names.get_name(response, None)
@@ -183,7 +183,7 @@ class Message(object):
   def get_prop(self, key):
     return self.props.get(key)
   def set_prop(self, key, value):
-    self.props[key] = value  
+    self.props[key] = value
 
   def __str__(self):
     return json.dumps(self.to_json())
@@ -200,17 +200,17 @@ class Message(object):
 
   def __eq__(self, that):
     return self.name == that.name and self.props == that.props
-      
+
 def make_avpr_object(json_data):
   """Build Avro Protocol from data parsed out of JSON string."""
-  if hasattr(json_data, 'get') and callable(json_data.get):
+  try:
     name = json_data.get('protocol')
     namespace = json_data.get('namespace')
     types = json_data.get('types')
     messages = json_data.get('messages')
-    return Protocol(name, namespace, types, messages)
-  else:
+  except AttributeError:
     raise ProtocolParseException('Not a JSON object: %s' % json_data)
+  return Protocol(name, namespace, types, messages)
 
 def parse(json_string):
   """Constructs the Protocol from the JSON text."""
@@ -221,4 +221,3 @@ def parse(json_string):
 
   # construct the Avro Protocol object
   return make_avpr_object(json_data)
-
diff --git a/lang/py/src/avro/schema.py b/lang/py/src/avro/schema.py
index 0737a94..012109f 100644
--- a/lang/py/src/avro/schema.py
+++ b/lang/py/src/avro/schema.py
@@ -5,9 +5,9 @@
 # to you under the Apache License, Version 2.0 (the
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
-# 
+#
 # http://www.apache.org/licenses/LICENSE-2.0
-# 
+#
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -113,6 +113,7 @@ class SchemaParseException(AvroException):
 
 class Schema(object):
   """Base class for all Schema classes."""
+  _props = None
   def __init__(self, type, other_props=None):
     # Ensure valid ctor args
     if not isinstance(type, basestring):
@@ -123,13 +124,14 @@ class Schema(object):
       raise SchemaParseException(fail_msg)
 
     # add members
-    if not hasattr(self, '_props'): self._props = {}
+    if self._props is None:
+      self._props = {}
     self.set_prop('type', type)
     self.type = type
     self._props.update(other_props or {})
 
   # Read-only properties dict. Printing schemas
-  # creates JSON properties directly from this dict. 
+  # creates JSON properties directly from this dict.
   props = property(lambda self: self._props)
 
   # Read-only property dict. Non-reserved properties
@@ -158,11 +160,11 @@ class Schema(object):
 
 class Name(object):
   """Class to describe Avro name."""
-  
+
   def __init__(self, name_attr, space_attr, default_space):
     """
     Formulate full name according to the specification.
-    
+
     @arg name_attr: name value read in schema or None.
     @arg space_attr: namespace value read in schema or None.
     @ard default_space: the current default space or None.
@@ -181,19 +183,19 @@ class Name(object):
     elif name_attr == "":
       fail_msg = 'Space must be non-empty string or None.'
       raise SchemaParseException(fail_msg)
-  
+
     if not (isinstance(default_space, basestring) or (default_space is None)):
       fail_msg = 'Default space must be non-empty string or None.'
       raise SchemaParseException(fail_msg)
     elif name_attr == "":
       fail_msg = 'Default must be non-empty string or None.'
       raise SchemaParseException(fail_msg)
-    
-    self._full = None; 
-    
+
+    self._full = None;
+
     if name_attr is None or name_attr == "":
         return;
-    
+
     if (name_attr.find('.') < 0):
       if (space_attr is not None) and (space_attr != ""):
         self._full = "%s.%s" % (space_attr, name_attr)
@@ -203,20 +205,20 @@ class Name(object):
         else:
           self._full = name_attr
     else:
-        self._full = name_attr         
-    
+        self._full = name_attr
+
   def __eq__(self, other):
     if not isinstance(other, Name):
         return False
     return (self.fullname == other.fullname)
-      
+
   fullname = property(lambda self: self._full)
 
   def get_space(self):
     """Back out a namespace from full name."""
     if self._full is None:
         return None
-    
+
     if (self._full.find('.') > 0):
       return self._full.rsplit(".", 1)[0]
     else:
@@ -227,17 +229,17 @@ class Names(object):
   def __init__(self, default_namespace=None):
       self.names = {}
       self.default_namespace = default_namespace
-      
+
   def has_name(self, name_attr, space_attr):
       test = Name(name_attr, space_attr, self.default_namespace).fullname
       return self.names.has_key(test)
-  
-  def get_name(self, name_attr, space_attr):    
+
+  def get_name(self, name_attr, space_attr):
       test = Name(name_attr, space_attr, self.default_namespace).fullname
       if not self.names.has_key(test):
           return None
       return self.names[test]
-  
+
   def prune_namespace(self, properties):
     """given a properties, return properties with namespace removed if
     it matches the own default namespace"""
@@ -258,14 +260,14 @@ class Names(object):
   def add_name(self, name_attr, space_attr, new_schema):
     """
     Add a new schema object to the name set.
-    
+
       @arg name_attr: name value read in schema
       @arg space_attr: namespace value read in schema.
-      
+
       @return: the Name that was just added.
     """
     to_add = Name(name_attr, space_attr, self.default_namespace)
-    
+
     if to_add.fullname in VALID_TYPES:
       fail_msg = '%s is a reserved type name.' % to_add.fullname
       raise SchemaParseException(fail_msg)
@@ -298,12 +300,12 @@ class NamedSchema(Schema):
 
     # Store name and namespace as they were read in origin schema
     self.set_prop('name', name)
-    if namespace is not None: 
+    if namespace is not None:
       self.set_prop('namespace', new_name.get_space())
 
     # Store full name as calculated from name, namespace
     self._fullname = new_name.fullname
-    
+
   def name_ref(self, names):
     if self.namespace == names.default_namespace:
       return self.name
@@ -699,8 +701,8 @@ class RecordSchema(NamedSchema):
     field_objects = []
     field_names = []
     for i, field in enumerate(field_data):
-      if hasattr(field, 'get') and callable(field.get):
-        type = field.get('type')        
+      if callable(getattr(field, 'get', None)):
+        type = field.get('type')
         name = field.get('name')
 
         # null values can have a default value of None
@@ -742,7 +744,7 @@ class RecordSchema(NamedSchema):
       NamedSchema.__init__(self, schema_type, name, namespace, names,
                            other_props)
 
-    if schema_type == 'record': 
+    if schema_type == 'record':
       old_default = names.default_namespace
       names.default_namespace = Name(name, namespace,
                                      names.default_namespace).get_space()
@@ -794,7 +796,7 @@ def get_other_props(all_props,reserved_props):
   Retrieve the non-reserved properties from a dictionary of properties
   @args reserved_props: The set of reserved properties to exclude
   """
-  if hasattr(all_props, 'items') and callable(all_props.items):
+  if callable(getattr(all_props, 'items', None)):
     return dict([(k,v) for (k,v) in all_props.items() if k not in
                  reserved_props ])
 
@@ -807,9 +809,9 @@ def make_avsc_object(json_data, names=None):
   """
   if names == None:
     names = Names()
-  
+
   # JSON object (non-union)
-  if hasattr(json_data, 'get') and callable(json_data.get):
+  if callable(getattr(json_data, 'get', None)):
     type = json_data.get('type')
     other_props = get_other_props(json_data, SCHEMA_RESERVED_PROPS)
     logical_type = None


Mime
View raw message