##// END OF EJS Templates
Merge branch 'issue-129'...
Thomas Kluyver -
r3359:26fb9558 merge
parent child Browse files
Show More
@@ -115,3 +115,34 b' class Tests (unittest.TestCase):'
115 115 show_all=True).keys()
116 116 a.sort()
117 117 self.assertEqual(a,res)
118
119 def test_dict_attributes(self):
120 """Dictionaries should be indexed by attributes, not by keys. This was
121 causing Github issue 129."""
122 ns = {"az":{"king":55}, "pq":{1:0}}
123 tests = [
124 ("a*", ["az"]),
125 ("az.k*", ["az.keys"]),
126 ("pq.k*", ["pq.keys"])
127 ]
128 for pat, res in tests:
129 res.sort()
130 a = wildcard.list_namespace(ns, "all", pat, ignore_case=False,
131 show_all=True).keys()
132 a.sort()
133 self.assertEqual(a, res)
134
135 def test_dict_dir(self):
136 class A(object):
137 def __init__(self):
138 self.a = 1
139 self.b = 2
140 def __getattribute__(self, name):
141 if name=="a":
142 raise AttributeError
143 return object.__getattribute__(self, name)
144
145 a = A()
146 adict = wildcard.dict_dir(a)
147 assert "a" not in adict # change to assertNotIn method in >= 2.7
148 self.assertEqual(adict["b"], 2)
@@ -4,6 +4,7 b''
4 4 Authors
5 5 -------
6 6 - JΓΆrgen Stenarson <jorgen.stenarson@bostream.nu>
7 - Thomas Kluyver
7 8 """
8 9
9 10 #*****************************************************************************
@@ -13,134 +14,98 b' Authors'
13 14 # the file COPYING, distributed as part of this software.
14 15 #*****************************************************************************
15 16
16 import __builtin__
17 17 import re
18 18 import types
19 19
20 20 from IPython.utils.dir2 import dir2
21 21
22 def create_typestr2type_dicts(dont_include_in_type2type2str=["lambda"]):
23 """Return dictionaries mapping lower case typename to type objects, from
24 the types package, and vice versa."""
25 typenamelist=[]
26 for tname in dir(types):
27 if tname[-4:]=="Type":
28 typenamelist.append(tname)
29 typestr2type={}
30 type2typestr={}
22 def create_typestr2type_dicts(dont_include_in_type2typestr=["lambda"]):
23 """Return dictionaries mapping lower case typename (e.g. 'tuple') to type
24 objects from the types package, and vice versa."""
25 typenamelist = [tname for tname in dir(types) if tname.endswith("Type")]
26 typestr2type, type2typestr = {}, {}
27
31 28 for tname in typenamelist:
32 name=tname[:-4].lower()
33 obj=getattr(types,tname)
34 typestr2type[name]=getattr(types,tname)
35 if name in dont_include_in_type2type2str:
36 type2typestr[obj]=name
37 return typestr2type,type2typestr
29 name = tname[:-4].lower() # Cut 'Type' off the end of the name
30 obj = getattr(types, tname)
31 typestr2type[name] = obj
32 if name not in dont_include_in_type2typestr:
33 type2typestr[obj] = name
34 return typestr2type, type2typestr
38 35
39 typestr2type,type2typestr=create_typestr2type_dicts()
36 typestr2type, type2typestr = create_typestr2type_dicts()
40 37
41 def is_type(obj,typestr_or_type):
42 """is_type(obj,typestr_or_type) verifies if obj is of a certain type or
43 group of types takes strings as parameters of the for 'tuple'<->TupleType
44 'all' matches all types. TODO: Should be extended for choosing more than
45 one type
46 """
47 if typestr_or_type=="all":
38 def is_type(obj, typestr_or_type):
39 """is_type(obj, typestr_or_type) verifies if obj is of a certain type. It
40 can take strings or actual python types for the second argument, i.e.
41 'tuple'<->TupleType. 'all' matches all types.
42
43 TODO: Should be extended for choosing more than one type."""
44 if typestr_or_type == "all":
48 45 return True
49 if type(typestr_or_type)==types.TypeType:
50 test_type=typestr_or_type
46 if type(typestr_or_type) == types.TypeType:
47 test_type = typestr_or_type
51 48 else:
52 test_type=typestr2type.get(typestr_or_type,False)
49 test_type = typestr2type.get(typestr_or_type, False)
53 50 if test_type:
54 return isinstance(obj,test_type)
55 else:
56 return False
51 return isinstance(obj, test_type)
52 return False
57 53
58 def show_hidden(str,show_all=False):
54 def show_hidden(str, show_all=False):
59 55 """Return true for strings starting with single _ if show_all is true."""
60 56 return show_all or str.startswith("__") or not str.startswith("_")
61 57
62 class NameSpace(object):
63 """NameSpace holds the dictionary for a namespace and implements filtering
64 on name and types"""
65 def __init__(self,obj,name_pattern="*",type_pattern="all",ignore_case=True,
66 show_all=True):
67 self.show_all = show_all #Hide names beginning with single _
68 self.object = obj
69 self.name_pattern = name_pattern
70 self.type_pattern = type_pattern
71 self.ignore_case = ignore_case
72
73 # We should only match EXACT dicts here, so DON'T use isinstance()
74 if type(obj) == types.DictType:
75 self._ns = obj
76 else:
77 kv = []
78 for key in dir2(obj):
79 if isinstance(key, basestring):
80 # This seemingly unnecessary try/except is actually needed
81 # because there is code out there with metaclasses that
82 # create 'write only' attributes, where a getattr() call
83 # will fail even if the attribute appears listed in the
84 # object's dictionary. Properties can actually do the same
85 # thing. In particular, Traits use this pattern
86 try:
87 kv.append((key,getattr(obj,key)))
88 except AttributeError:
89 pass
90 self._ns = dict(kv)
91
92 def get_ns(self):
93 """Return name space dictionary with objects matching type and name patterns."""
94 return self.filter(self.name_pattern,self.type_pattern)
95 ns=property(get_ns)
96
97 def get_ns_names(self):
98 """Return list of object names in namespace that match the patterns."""
99 return self.ns.keys()
100 ns_names=property(get_ns_names,doc="List of objects in name space that "
101 "match the type and name patterns.")
58 def dict_dir(obj):
59 """Produce a dictionary of an object's attributes. Builds on dir2 by
60 checking that a getattr() call actually succeeds."""
61 ns = {}
62 for key in dir2(obj):
63 # This seemingly unnecessary try/except is actually needed
64 # because there is code out there with metaclasses that
65 # create 'write only' attributes, where a getattr() call
66 # will fail even if the attribute appears listed in the
67 # object's dictionary. Properties can actually do the same
68 # thing. In particular, Traits use this pattern
69 try:
70 ns[key] = getattr(obj, key)
71 except AttributeError:
72 pass
73 return ns
102 74
103 def filter(self,name_pattern,type_pattern):
104 """Return dictionary of filtered namespace."""
105 def glob_filter(lista,name_pattern,hidehidden,ignore_case):
106 """Return list of elements in lista that match pattern."""
107 pattern=name_pattern.replace("*",".*").replace("?",".")
108 if ignore_case:
109 reg=re.compile(pattern+"$",re.I)
110 else:
111 reg=re.compile(pattern+"$")
112 result=[x for x in lista if reg.match(x) and show_hidden(x,hidehidden)]
113 return result
114 ns=self._ns
115 #Filter namespace by the name_pattern
116 all=[(x,ns[x]) for x in glob_filter(ns.keys(),name_pattern,
117 self.show_all,self.ignore_case)]
118 #Filter namespace by type_pattern
119 all=[(key,obj) for key,obj in all if is_type(obj,type_pattern)]
120 all=dict(all)
121 return all
122
123 #TODO: Implement dictionary like access to filtered name space?
75 def filter_ns(ns, name_pattern="*", type_pattern="all", ignore_case=True,
76 show_all=True):
77 """Filter a namespace dictionary by name pattern and item type."""
78 pattern = name_pattern.replace("*",".*").replace("?",".")
79 if ignore_case:
80 reg = re.compile(pattern+"$", re.I)
81 else:
82 reg = re.compile(pattern+"$")
83
84 # Check each one matches regex; shouldn't be hidden; of correct type.
85 return dict((key,obj) for key, obj in ns.iteritems() if reg.match(key) \
86 and show_hidden(key, show_all) \
87 and is_type(obj, type_pattern) )
124 88
125 def list_namespace(namespace,type_pattern,filter,ignore_case=False,show_all=False):
126 """Return dictionary of all objects in namespace that matches type_pattern
127 and filter."""
89 def list_namespace(namespace, type_pattern, filter, ignore_case=False, show_all=False):
90 """Return dictionary of all objects in a namespace dictionary that match
91 type_pattern and filter."""
128 92 pattern_list=filter.split(".")
129 if len(pattern_list)==1:
130 ns=NameSpace(namespace,name_pattern=pattern_list[0],type_pattern=type_pattern,
131 ignore_case=ignore_case,show_all=show_all)
132 return ns.ns
93 if len(pattern_list) == 1:
94 return filter_ns(namespace, name_pattern=pattern_list[0],
95 type_pattern=type_pattern,
96 ignore_case=ignore_case, show_all=show_all)
133 97 else:
134 98 # This is where we can change if all objects should be searched or
135 99 # only modules. Just change the type_pattern to module to search only
136 100 # modules
137 ns=NameSpace(namespace,name_pattern=pattern_list[0],type_pattern="all",
138 ignore_case=ignore_case,show_all=show_all)
139 res={}
140 nsdict=ns.ns
141 for name,obj in nsdict.iteritems():
142 ns=list_namespace(obj,type_pattern,".".join(pattern_list[1:]),
143 ignore_case=ignore_case,show_all=show_all)
144 for inner_name,inner_obj in ns.iteritems():
145 res["%s.%s"%(name,inner_name)]=inner_obj
146 return res
101 filtered = filter_ns(namespace, name_pattern=pattern_list[0],
102 type_pattern="all",
103 ignore_case=ignore_case, show_all=show_all)
104 results = {}
105 for name, obj in filtered.iteritems():
106 ns = list_namespace(dict_dir(obj), type_pattern,
107 ".".join(pattern_list[1:]),
108 ignore_case=ignore_case, show_all=show_all)
109 for inner_name, inner_obj in ns.iteritems():
110 results["%s.%s"%(name,inner_name)] = inner_obj
111 return results
General Comments 0
You need to be logged in to leave comments. Login now