##// END OF EJS Templates
Fix for issue 129. Tests in previous commit now pass.
Thomas Kluyver -
Show More
@@ -1,146 +1,164 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Support for wildcard pattern matching in object inspection.
3 3
4 4 Authors
5 5 -------
6 6 - Jörgen Stenarson <jorgen.stenarson@bostream.nu>
7 7 """
8 8
9 9 #*****************************************************************************
10 10 # Copyright (C) 2005 Jörgen Stenarson <jorgen.stenarson@bostream.nu>
11 11 #
12 12 # Distributed under the terms of the BSD License. The full license is in
13 13 # the file COPYING, distributed as part of this software.
14 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 22 def create_typestr2type_dicts(dont_include_in_type2type2str=["lambda"]):
23 23 """Return dictionaries mapping lower case typename to type objects, from
24 24 the types package, and vice versa."""
25 25 typenamelist=[]
26 26 for tname in dir(types):
27 27 if tname[-4:]=="Type":
28 28 typenamelist.append(tname)
29 29 typestr2type={}
30 30 type2typestr={}
31 31 for tname in typenamelist:
32 32 name=tname[:-4].lower()
33 33 obj=getattr(types,tname)
34 34 typestr2type[name]=getattr(types,tname)
35 35 if name in dont_include_in_type2type2str:
36 36 type2typestr[obj]=name
37 37 return typestr2type,type2typestr
38 38
39 39 typestr2type,type2typestr=create_typestr2type_dicts()
40 40
41 41 def is_type(obj,typestr_or_type):
42 42 """is_type(obj,typestr_or_type) verifies if obj is of a certain type or
43 43 group of types takes strings as parameters of the for 'tuple'<->TupleType
44 44 'all' matches all types. TODO: Should be extended for choosing more than
45 45 one type
46 46 """
47 47 if typestr_or_type=="all":
48 48 return True
49 49 if type(typestr_or_type)==types.TypeType:
50 50 test_type=typestr_or_type
51 51 else:
52 52 test_type=typestr2type.get(typestr_or_type,False)
53 53 if test_type:
54 54 return isinstance(obj,test_type)
55 55 else:
56 56 return False
57 57
58 58 def show_hidden(str,show_all=False):
59 59 """Return true for strings starting with single _ if show_all is true."""
60 60 return show_all or str.startswith("__") or not str.startswith("_")
61 61
62 62 class NameSpace(object):
63 63 """NameSpace holds the dictionary for a namespace and implements filtering
64 64 on name and types"""
65 def __init__(self,obj,name_pattern="*",type_pattern="all",ignore_case=True,
66 show_all=True):
65 def __init__(self, ns_dict, name_pattern="*", type_pattern="all",
66 ignore_case=True, show_all=True):
67 67 self.show_all = show_all #Hide names beginning with single _
68 self.object = obj
69 68 self.name_pattern = name_pattern
70 69 self.type_pattern = type_pattern
71 70 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)
71 self._ns = ns_dict
72
73 @classmethod
74 def from_object(cls, obj, *args, **kwargs):
75 """Instantiate a namespace by constructing a dictionary of an object's
76 attributes. A class method, returns a new NameSpace instance."""
77 attrs = {}
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 attrs[key] = getattr(obj,key)
88 except AttributeError:
89 pass
90 return cls(attrs, *args, **kwargs)
91 91
92 92 def get_ns(self):
93 93 """Return name space dictionary with objects matching type and name patterns."""
94 94 return self.filter(self.name_pattern,self.type_pattern)
95 95 ns=property(get_ns)
96 96
97 97 def get_ns_names(self):
98 98 """Return list of object names in namespace that match the patterns."""
99 99 return self.ns.keys()
100 100 ns_names=property(get_ns_names,doc="List of objects in name space that "
101 101 "match the type and name patterns.")
102 102
103 103 def filter(self,name_pattern,type_pattern):
104 104 """Return dictionary of filtered namespace."""
105 105 def glob_filter(lista,name_pattern,hidehidden,ignore_case):
106 106 """Return list of elements in lista that match pattern."""
107 107 pattern=name_pattern.replace("*",".*").replace("?",".")
108 108 if ignore_case:
109 109 reg=re.compile(pattern+"$",re.I)
110 110 else:
111 111 reg=re.compile(pattern+"$")
112 112 result=[x for x in lista if reg.match(x) and show_hidden(x,hidehidden)]
113 113 return result
114 114 ns=self._ns
115 115 #Filter namespace by the name_pattern
116 116 all=[(x,ns[x]) for x in glob_filter(ns.keys(),name_pattern,
117 117 self.show_all,self.ignore_case)]
118 118 #Filter namespace by type_pattern
119 119 all=[(key,obj) for key,obj in all if is_type(obj,type_pattern)]
120 120 all=dict(all)
121 121 return all
122 122
123 123 #TODO: Implement dictionary like access to filtered name space?
124 124
125 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."""
126 """Return dictionary of all objects in a namespace dictionary that match
127 type_pattern and filter."""
128 128 pattern_list=filter.split(".")
129 129 if len(pattern_list)==1:
130 130 ns=NameSpace(namespace,name_pattern=pattern_list[0],type_pattern=type_pattern,
131 131 ignore_case=ignore_case,show_all=show_all)
132 132 return ns.ns
133 133 else:
134 134 # This is where we can change if all objects should be searched or
135 135 # only modules. Just change the type_pattern to module to search only
136 136 # modules
137 137 ns=NameSpace(namespace,name_pattern=pattern_list[0],type_pattern="all",
138 138 ignore_case=ignore_case,show_all=show_all)
139 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)
140 for name,obj in ns.ns.iteritems():
141 ns = list_object_namespace(obj, type_pattern, pattern_list[1:],
142 ignore_case=ignore_case, show_all=show_all)
143 for inner_name, inner_obj in ns.iteritems():
144 res["%s.%s"%(name,inner_name)]=inner_obj
145 return res
146
147 def list_object_namespace(ns_obj, type_pattern, pattern_list, ignore_case=False,
148 show_all=False):
149 """Return dictionary of all attributes of an object which match type_pattern
150 and filter."""
151 if len(pattern_list)==1:
152 ns=NameSpace.from_object(ns_obj, name_pattern=pattern_list[0],
153 type_pattern=type_pattern, ignore_case=ignore_case, show_all=show_all)
154 return ns.ns
155 else:
156 ns=NameSpace.from_object(ns_obj, name_pattern=pattern_list[0],
157 type_pattern="all", ignore_case=ignore_case, show_all=show_all)
158 res={}
159 for name,obj in ns.ns.iteritems():
160 ns=list_object_namespace(obj, type_pattern, pattern_list[1:],
161 ignore_case=ignore_case, show_all=show_all)
144 162 for inner_name,inner_obj in ns.iteritems():
145 163 res["%s.%s"%(name,inner_name)]=inner_obj
146 164 return res
General Comments 0
You need to be logged in to leave comments. Login now