Skip to content

Instantly share code, notes, and snippets.

@t0ster
Created November 22, 2010 23:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save t0ster/710977 to your computer and use it in GitHub Desktop.
Save t0ster/710977 to your computer and use it in GitHub Desktop.
class Null(object):
def __repr__(self):
return "<Null>"
def __str__(self):
return ''
def __nonzero__(self):
return False
class ResultAttrFactory(type):
_cache = {}
@classmethod
def prepare(cls, base, result):
dict_ = ResultAttr.__dict__.copy()
dict_.update({
'_ResultAttr__base': base,
'_ResultAttr__result': result})
return ('ResultAttr', (base,), dict_)
def __new__(cls, base, result):
if (base, result) in cls._cache:
type_ = cls._cache[(base, result)]
else:
type_ = super(ResultAttrFactory, cls).__new__(
cls, *cls.prepare(base, result))
cls._cache[(base, result)] = type_
return type_
def __init__(cls, base, result):
pass
class ResultAttr:
"""Should be used only with ResultAttrFactory"""
@staticmethod
def __new__(cls, arg1, name):
return cls.__base.__new__(cls, arg1)
def __init__(self, arg1, name):
self.__name = name
super(self.__class__, self).__init__(arg1)
def get_result_attr(self, name):
if self.__result.is_denorm_attr(name):
attr = getattr(self.__result, name, None)
else:
attr = getattr(self.__result, name)
return attr
def __getattr__(self, name):
lookup_name = "%s__%s" % (self.__name, name)
attr = self.get_result_attr(lookup_name)
if type(attr).__name__ == 'ResultAttr':
type_ = attr.__base
elif attr is None:
type_ = Null
else:
type_ = type(attr)
result_attr = ResultAttrFactory(
type_, self.__result)(attr, lookup_name)
return result_attr
class BaseResult(object):
"""
>>> class Result(BaseResult):
... def __init__(self, *args, **kwargs):
... self.x = 35
... self.y = 5
... self.y__a = 3
... self.y__b = 'hello'
... self.y__c__x = [1, 2, 3]
... super(Result, self, *args, **kwargs)
>>> r = Result()
>>> r.x
35
>>> type(r.x)
<type 'int'>
>>> r.y
5
>>> type(r.y) # doctest:+ELLIPSIS
<class '....ResultAttr'>
>>> r.y.a
3
>>> r.y.b
'hello'
>>> r.y.c # Is there any way to raise AtrributeError here?
<Null>
>>> r.y.d
Traceback (most recent call last):
AttributeError: 'Result' object has no attribute 'y__d'
>>> r.y.c.x
[1, 2, 3]
"""
def is_denorm_attr(self, name):
return bool([k for k in self.__dict__.keys() if "%s__" % name in k])
def __getattribute__(self, name):
attr = super(BaseResult, self).__getattribute__(name)
if name in ('__dict__', 'is_denorm_attr'):
return attr
if self.is_denorm_attr(name):
return ResultAttrFactory(type(attr), self)(attr, name)
else:
return attr
if __name__ == '__main__':
import doctest
doctest.testmod()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment