import six
import datetime
from .namespace import Namespace
from rook.exceptions import RookKeyNotFound, RookAttributeNotFound, RookInvalidMethodArguments
class PythonObjectNamespace(Namespace):
try:
BINARY_TYPES = (buffer, bytearray)
except NameError:
BINARY_TYPES = (bytearray, bytes)
class ObjectDumpConfig(object):
STRICT_MAX_DEPTH = 2
STRICT_MAX_WIDTH = 10
STRICT_MAX_COLLECTION_DEPTH = 2
STRICT_MAX_STRING = 128
DEFAULT_MAX_DEPTH = 4
DEFAULT_MAX_WIDTH = 10
DEFAULT_MAX_COLLECTION_DEPTH = 4
DEFAULT_MAX_STRING = 512
TOLERANT_MAX_DEPTH = 5
TOLERANT_MAX_WIDTH = 20
TOLERANT_MAX_COLLECTION_DEPTH = 5
TOLERANT_MAX_STRING = 4 * 1024
UNLIMITED_STRING = 64 * 1024
UNLIMITED_COLLECTION_WIDTH = 100
def __init__(self, max_depth=None, max_width=None, max_collection_dump=None, max_string=None):
self.max_depth = max_depth or self.DEFAULT_MAX_DEPTH
self.max_width = max_width or self.DEFAULT_MAX_WIDTH
self.max_collection_dump = max_collection_dump or self.DEFAULT_MAX_COLLECTION_DEPTH
self.max_string = max_string or self.DEFAULT_MAX_STRING
def __eq__(self, other):
return type(self) == type(other) and self.max_depth == other.max_depth and \
self.max_width == other.max_width and self.max_collection_dump == other.max_collection_dump and \
self.max_string == other.max_string
@classmethod
def strict_limits(cls, obj):
return cls(
cls.STRICT_MAX_DEPTH,
cls.STRICT_MAX_WIDTH,
cls.STRICT_MAX_COLLECTION_DEPTH,
cls.STRICT_MAX_STRING)
@classmethod
def default_limits(cls, obj):
return cls(
cls.DEFAULT_MAX_DEPTH,
cls.DEFAULT_MAX_WIDTH,
cls.DEFAULT_MAX_COLLECTION_DEPTH,
cls.DEFAULT_MAX_STRING)
@classmethod
def tolerant_limits(cls, obj):
return cls(
cls.TOLERANT_MAX_DEPTH,
cls.TOLERANT_MAX_WIDTH,
cls.TOLERANT_MAX_COLLECTION_DEPTH,
cls.TOLERANT_MAX_STRING)
@classmethod
def tailor_limits(cls, obj):
if isinstance(obj, six.string_types):
return PythonObjectNamespace.ObjectDumpConfig(1, 0, 0, cls.UNLIMITED_STRING)
if isinstance(obj, (list, dict, set, tuple)) and len(obj) > cls.TOLERANT_MAX_WIDTH:
return PythonObjectNamespace.ObjectDumpConfig(max_width=cls.UNLIMITED_COLLECTION_WIDTH)
else:
return PythonObjectNamespace.ObjectDumpConfig.tolerant_limits(obj)
def __init__(self, obj, dump_config=None, methods=()):
super(PythonObjectNamespace, self).__init__(methods + self.METHODS)
self.obj = obj
self.dump_config = dump_config or self.ObjectDumpConfig()
@classmethod
def get_common_type(cls, obj):
# NOTE: This list is partially duplicated with the list in NamespaceSerializer._load_variant
if obj is None:
return u'null'
elif isinstance(obj, six.integer_types):
return u'int'
elif isinstance(obj, six.string_types):
return u'string'
elif isinstance(obj, float):
return u'float'
elif isinstance(obj, cls.BINARY_TYPES):
return u'buffer'
elif isinstance(obj, datetime.datetime):
return u'datetime'
elif isinstance(obj, (list, set, tuple)):
from .collection_namespace import ListNamespace
return ListNamespace.get_common_type(obj)
elif isinstance(obj, (dict)):
return u'dict'
elif isinstance(obj, complex):
return u'complex'
else:
raise ValueError("Unknown type- %s", str(type(obj)))
def read_attribute(self, name):
try:
if hasattr(self.obj, '__dict__') and self.obj.__dict__ and name in self.obj.__dict__:
return PythonObjectNamespace(self.obj.__dict__[name])
if hasattr(self.obj, '__slots__') and self.obj.__slots__ and name in self.obj.__slots__:
return PythonObjectNamespace(self.obj.__slots__[name])
raise RookAttributeNotFound(name)
except AttributeError:
raise RookAttributeNotFound(name)
def read_key(self, key):
try:
return PythonObjectNamespace(self.obj[key])
except (KeyError, IndexError, TypeError):
# If key is a string and object is a dictionary check if any key within the dictionary stringifies to key
if isinstance(key, six.string_types) and isinstance(self.obj, dict):
for iteratorKey in self.obj:
if str(iteratorKey) == key: # The str() is the important part
return PythonObjectNamespace(self.obj[iteratorKey])
raise RookKeyNotFound(key)
def type(self, args=None):
return PythonObjectNamespace(self.serialize_type())
def serialize_type(self):
return str(type(self.obj))
def size(self, args=None):
return PythonObjectNamespace(len(self.obj))
def depth(self, args):
try:
self.dump_config.max_depth = int(args)
except ValueError:
raise RookInvalidMethodArguments('depth()', args)
return self
def width(self, args):
try:
self.dump_config.max_width = int(args)
except ValueError:
raise RookInvalidMethodArguments('width()', args)
return self
def collection_dump(self, args):
try:
self.dump_config.max_collection_dump = int(args)
except ValueError:
raise RookInvalidMethodArguments('collection_dump()', args)
return self
def string(self, args):
try:
self.dump_config.max_string = int(args)
except ValueError:
raise RookInvalidMethodArguments('string()', args)
return self
def limit(self, args):
try:
self.dump_config = PythonObjectNamespace.dump_configs[args.lower()]
except KeyError:
raise RookInvalidMethodArguments('limit()', args)
return self
def __hash__(self):
return hash(self.obj)
def __eq__(self, other):
return type(self) == other and self.obj == other.obj
def __nonzero__(self):
return bool(self.obj)
if six.PY3:
__bool__ = __nonzero__
dump_configs = {
'': ObjectDumpConfig.default_limits(ObjectDumpConfig),
'strict': ObjectDumpConfig.strict_limits(ObjectDumpConfig),
'default': ObjectDumpConfig.default_limits(ObjectDumpConfig),
'tolerant': ObjectDumpConfig.tolerant_limits(ObjectDumpConfig)}
METHODS = (type, size, depth, width, collection_dump, string, limit)