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