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 <[email protected]>
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