Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'issue-129'

closes gh-129
closes gh-251
  • Loading branch information...
commit 26fb9558d5e3e39ec2980e6937e1db69ea999764 2 parents 15ce9bf + 581b235
@takluyver takluyver authored
Showing with 105 additions and 109 deletions.
  1. +31 −0 IPython/utils/tests/test_wildcard.py
  2. +74 −109 IPython/utils/wildcard.py
View
31 IPython/utils/tests/test_wildcard.py
@@ -115,3 +115,34 @@ def test_nocase_showall(self):
show_all=True).keys()
a.sort()
self.assertEqual(a,res)
+
+ def test_dict_attributes(self):
+ """Dictionaries should be indexed by attributes, not by keys. This was
+ causing Github issue 129."""
+ ns = {"az":{"king":55}, "pq":{1:0}}
+ tests = [
+ ("a*", ["az"]),
+ ("az.k*", ["az.keys"]),
+ ("pq.k*", ["pq.keys"])
+ ]
+ for pat, res in tests:
+ res.sort()
+ a = wildcard.list_namespace(ns, "all", pat, ignore_case=False,
+ show_all=True).keys()
+ a.sort()
+ self.assertEqual(a, res)
+
+ def test_dict_dir(self):
+ class A(object):
+ def __init__(self):
+ self.a = 1
+ self.b = 2
+ def __getattribute__(self, name):
+ if name=="a":
+ raise AttributeError
+ return object.__getattribute__(self, name)
+
+ a = A()
+ adict = wildcard.dict_dir(a)
+ assert "a" not in adict # change to assertNotIn method in >= 2.7
+ self.assertEqual(adict["b"], 2)
View
183 IPython/utils/wildcard.py
@@ -4,6 +4,7 @@
Authors
-------
- Jörgen Stenarson <jorgen.stenarson@bostream.nu>
+- Thomas Kluyver
"""
#*****************************************************************************
@@ -13,134 +14,98 @@
# the file COPYING, distributed as part of this software.
#*****************************************************************************
-import __builtin__
import re
import types
from IPython.utils.dir2 import dir2
-def create_typestr2type_dicts(dont_include_in_type2type2str=["lambda"]):
- """Return dictionaries mapping lower case typename to type objects, from
- the types package, and vice versa."""
- typenamelist=[]
- for tname in dir(types):
- if tname[-4:]=="Type":
- typenamelist.append(tname)
- typestr2type={}
- type2typestr={}
+def create_typestr2type_dicts(dont_include_in_type2typestr=["lambda"]):
+ """Return dictionaries mapping lower case typename (e.g. 'tuple') to type
+ objects from the types package, and vice versa."""
+ typenamelist = [tname for tname in dir(types) if tname.endswith("Type")]
+ typestr2type, type2typestr = {}, {}
+
for tname in typenamelist:
- name=tname[:-4].lower()
- obj=getattr(types,tname)
- typestr2type[name]=getattr(types,tname)
- if name in dont_include_in_type2type2str:
- type2typestr[obj]=name
- return typestr2type,type2typestr
+ name = tname[:-4].lower() # Cut 'Type' off the end of the name
+ obj = getattr(types, tname)
+ typestr2type[name] = obj
+ if name not in dont_include_in_type2typestr:
+ type2typestr[obj] = name
+ return typestr2type, type2typestr
-typestr2type,type2typestr=create_typestr2type_dicts()
+typestr2type, type2typestr = create_typestr2type_dicts()
-def is_type(obj,typestr_or_type):
- """is_type(obj,typestr_or_type) verifies if obj is of a certain type or
- group of types takes strings as parameters of the for 'tuple'<->TupleType
- 'all' matches all types. TODO: Should be extended for choosing more than
- one type
- """
- if typestr_or_type=="all":
+def is_type(obj, typestr_or_type):
+ """is_type(obj, typestr_or_type) verifies if obj is of a certain type. It
+ can take strings or actual python types for the second argument, i.e.
+ 'tuple'<->TupleType. 'all' matches all types.
+
+ TODO: Should be extended for choosing more than one type."""
+ if typestr_or_type == "all":
return True
- if type(typestr_or_type)==types.TypeType:
- test_type=typestr_or_type
+ if type(typestr_or_type) == types.TypeType:
+ test_type = typestr_or_type
else:
- test_type=typestr2type.get(typestr_or_type,False)
+ test_type = typestr2type.get(typestr_or_type, False)
if test_type:
- return isinstance(obj,test_type)
- else:
- return False
+ return isinstance(obj, test_type)
+ return False
-def show_hidden(str,show_all=False):
+def show_hidden(str, show_all=False):
"""Return true for strings starting with single _ if show_all is true."""
return show_all or str.startswith("__") or not str.startswith("_")
-class NameSpace(object):
- """NameSpace holds the dictionary for a namespace and implements filtering
- on name and types"""
- def __init__(self,obj,name_pattern="*",type_pattern="all",ignore_case=True,
- show_all=True):
- self.show_all = show_all #Hide names beginning with single _
- self.object = obj
- self.name_pattern = name_pattern
- self.type_pattern = type_pattern
- self.ignore_case = ignore_case
-
- # We should only match EXACT dicts here, so DON'T use isinstance()
- if type(obj) == types.DictType:
- self._ns = obj
- else:
- kv = []
- for key in dir2(obj):
- if isinstance(key, basestring):
- # This seemingly unnecessary try/except is actually needed
- # because there is code out there with metaclasses that
- # create 'write only' attributes, where a getattr() call
- # will fail even if the attribute appears listed in the
- # object's dictionary. Properties can actually do the same
- # thing. In particular, Traits use this pattern
- try:
- kv.append((key,getattr(obj,key)))
- except AttributeError:
- pass
- self._ns = dict(kv)
-
- def get_ns(self):
- """Return name space dictionary with objects matching type and name patterns."""
- return self.filter(self.name_pattern,self.type_pattern)
- ns=property(get_ns)
-
- def get_ns_names(self):
- """Return list of object names in namespace that match the patterns."""
- return self.ns.keys()
- ns_names=property(get_ns_names,doc="List of objects in name space that "
- "match the type and name patterns.")
+def dict_dir(obj):
+ """Produce a dictionary of an object's attributes. Builds on dir2 by
+ checking that a getattr() call actually succeeds."""
+ ns = {}
+ for key in dir2(obj):
+ # This seemingly unnecessary try/except is actually needed
+ # because there is code out there with metaclasses that
+ # create 'write only' attributes, where a getattr() call
+ # will fail even if the attribute appears listed in the
+ # object's dictionary. Properties can actually do the same
+ # thing. In particular, Traits use this pattern
+ try:
+ ns[key] = getattr(obj, key)
+ except AttributeError:
+ pass
+ return ns
- def filter(self,name_pattern,type_pattern):
- """Return dictionary of filtered namespace."""
- def glob_filter(lista,name_pattern,hidehidden,ignore_case):
- """Return list of elements in lista that match pattern."""
- pattern=name_pattern.replace("*",".*").replace("?",".")
- if ignore_case:
- reg=re.compile(pattern+"$",re.I)
- else:
- reg=re.compile(pattern+"$")
- result=[x for x in lista if reg.match(x) and show_hidden(x,hidehidden)]
- return result
- ns=self._ns
- #Filter namespace by the name_pattern
- all=[(x,ns[x]) for x in glob_filter(ns.keys(),name_pattern,
- self.show_all,self.ignore_case)]
- #Filter namespace by type_pattern
- all=[(key,obj) for key,obj in all if is_type(obj,type_pattern)]
- all=dict(all)
- return all
-
- #TODO: Implement dictionary like access to filtered name space?
+def filter_ns(ns, name_pattern="*", type_pattern="all", ignore_case=True,
+ show_all=True):
+ """Filter a namespace dictionary by name pattern and item type."""
+ pattern = name_pattern.replace("*",".*").replace("?",".")
+ if ignore_case:
+ reg = re.compile(pattern+"$", re.I)
+ else:
+ reg = re.compile(pattern+"$")
+
+ # Check each one matches regex; shouldn't be hidden; of correct type.
+ return dict((key,obj) for key, obj in ns.iteritems() if reg.match(key) \
+ and show_hidden(key, show_all) \
+ and is_type(obj, type_pattern) )
-def list_namespace(namespace,type_pattern,filter,ignore_case=False,show_all=False):
- """Return dictionary of all objects in namespace that matches type_pattern
- and filter."""
+def list_namespace(namespace, type_pattern, filter, ignore_case=False, show_all=False):
+ """Return dictionary of all objects in a namespace dictionary that match
+ type_pattern and filter."""
pattern_list=filter.split(".")
- if len(pattern_list)==1:
- ns=NameSpace(namespace,name_pattern=pattern_list[0],type_pattern=type_pattern,
- ignore_case=ignore_case,show_all=show_all)
- return ns.ns
+ if len(pattern_list) == 1:
+ return filter_ns(namespace, name_pattern=pattern_list[0],
+ type_pattern=type_pattern,
+ ignore_case=ignore_case, show_all=show_all)
else:
# This is where we can change if all objects should be searched or
# only modules. Just change the type_pattern to module to search only
# modules
- ns=NameSpace(namespace,name_pattern=pattern_list[0],type_pattern="all",
- ignore_case=ignore_case,show_all=show_all)
- res={}
- nsdict=ns.ns
- for name,obj in nsdict.iteritems():
- ns=list_namespace(obj,type_pattern,".".join(pattern_list[1:]),
- ignore_case=ignore_case,show_all=show_all)
- for inner_name,inner_obj in ns.iteritems():
- res["%s.%s"%(name,inner_name)]=inner_obj
- return res
+ filtered = filter_ns(namespace, name_pattern=pattern_list[0],
+ type_pattern="all",
+ ignore_case=ignore_case, show_all=show_all)
+ results = {}
+ for name, obj in filtered.iteritems():
+ ns = list_namespace(dict_dir(obj), type_pattern,
+ ".".join(pattern_list[1:]),
+ ignore_case=ignore_case, show_all=show_all)
+ for inner_name, inner_obj in ns.iteritems():
+ results["%s.%s"%(name,inner_name)] = inner_obj
+ return results
Please sign in to comment.
Something went wrong with that request. Please try again.