Created
November 22, 2010 23:59
-
-
Save t0ster/710977 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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