@@ -711,13 +711,11 @@ class Wildcard(Raw):
711
711
712
712
:param cls_or_instance: The field type the list will contain.
713
713
'''
714
- exclude = []
715
- # keep a track of the last object
716
- _idx = 0
714
+ exclude = set ()
717
715
# cache the flat object
718
716
_flat = None
719
717
_obj = None
720
- _cache = []
718
+ _cache = set ()
721
719
_last = None
722
720
723
721
def __init__ (self , cls_or_instance , ** kwargs ):
@@ -733,43 +731,63 @@ def __init__(self, cls_or_instance, **kwargs):
733
731
self .container = cls_or_instance
734
732
735
733
def _flatten (self , obj ):
736
- if obj == self ._obj and self ._flat :
734
+ if obj is None :
735
+ return None
736
+ if obj == self ._obj and self ._flat is not None :
737
737
return self ._flat
738
738
if isinstance (obj , dict ):
739
- self ._flat = obj .items ()
739
+ self ._flat = [ x for x in obj .items ()]
740
740
else :
741
- attributes = inspect .getmembers (obj , lambda a : not (inspect .isroutine (a )))
742
- self ._flat = [x for x in attributes if match_attributes (x )]
743
741
744
- self ._idx = 0
745
- self ._cache = []
742
+ def __match_attributes (attribute ):
743
+ attr_name , attr_obj = attribute
744
+ if inspect .isroutine (attr_obj ) or \
745
+ (attr_name .startswith ('__' ) and attr_name .endswith ('__' )):
746
+ return False
747
+ return True
748
+
749
+ attributes = inspect .getmembers (obj )
750
+ self ._flat = [x for x in attributes if __match_attributes (x )]
751
+
752
+ self ._cache = set ()
746
753
self ._obj = obj
747
754
return self ._flat
748
755
749
756
@property
750
757
def key (self ):
751
758
return self ._last
752
759
760
+ def reset (self ):
761
+ self .exclude = set ()
762
+ self ._flat = None
763
+ self ._obj = None
764
+ self ._cache = set ()
765
+ self ._last = None
766
+
753
767
def output (self , key , obj , ordered = False ):
754
- flat = self ._flatten (obj )
755
768
value = None
756
769
reg = fnmatch .translate (key )
757
770
758
- for idx , (objkey , val ) in enumerate (flat ):
759
- if idx < self ._idx :
760
- continue
761
- if objkey not in self ._cache and \
762
- objkey not in self .exclude and \
763
- re .match (reg , objkey , re .IGNORECASE ):
764
- value = val
765
- self ._cache .append (objkey )
766
- self ._last = objkey
767
- self ._idx = idx
768
- break
771
+ if self ._flatten (obj ):
772
+ while True :
773
+ try :
774
+ # we are using pop() so that we don't
775
+ # loop over the whole object every time dropping the
776
+ # complexity to O(n)
777
+ (objkey , val ) = self ._flat .pop ()
778
+ if objkey not in self ._cache and \
779
+ objkey not in self .exclude and \
780
+ re .match (reg , objkey , re .IGNORECASE ):
781
+ value = val
782
+ self ._cache .add (objkey )
783
+ self ._last = objkey
784
+ break
785
+ except IndexError :
786
+ break
769
787
770
788
if value is None :
771
789
if self .default is not None :
772
- return self .default
790
+ return self .container . format ( self . default )
773
791
return None
774
792
775
793
return self .container .format (value )
@@ -786,10 +804,3 @@ def clone(self, mask=None):
786
804
if mask :
787
805
model = mask .apply (model )
788
806
return self .__class__ (model , ** kwargs )
789
-
790
-
791
- def match_attributes (attribute ):
792
- attr_name , _ = attribute
793
- if attr_name .startswith ('__' ) and attr_name .endswith ('__' ):
794
- return False
795
- return True
0 commit comments