Index: Magic.py
===================================================================
--- Magic.py	(revision 909)
+++ Magic.py	(working copy)
@@ -38,6 +38,8 @@
 from IPython.PyColorize import Parser
 from IPython.genutils import *
 
+
+import IPython.wildcard as wildcard
 # Globals to be set later by Magic constructor
 MAGIC_PREFIX = ''
 MAGIC_ESCAPE = ''
@@ -656,8 +658,60 @@
                re.match('(pinfo )?(\?*)(.*?)(\??$)',parameter_s).groups()
         if pinfo or qmark1 or qmark2:
             detail_level = 1
-        self._inspect('pinfo',oname,detail_level=detail_level)
+	if "*" in oname:
+	    self.magic_psearch(oname)
+	else:
+	    self._inspect('pinfo',oname,detail_level=detail_level)
 
+    def magic_psearch(self, parameter_s=''):
+        """Search for object in namespaces by wildcard
+
+	%psearch PATTERN [OBJECT TYPE] [-NAMESPACE]* [+NAMESPACE]* [-a] [-c]
+	? can be used as a synonym for %psearch
+	
+	PATTERN
+	where PATTERN is a string containing * as a wildcard similar
+	to its use in a shell.  The pattern is matched in all
+	namespaces on the search path. By default objects starting
+	with a single _ are not matched, many IPython generated
+	objects have a single underscore. The default is case
+	insensitive matching. Matching is also done on the attributes
+	of objects and not only on the objects in a module.
+
+	[OBJECT TYPE] 
+	Is the name of a python type from the types
+	module. The name is given in lowercase without the ending
+	type, ex. StringType is written string. By adding a type here
+	only objects matching the given type are matched. Using all
+	here makes the pattern match all types (this is the default).
+
+	[-NAMESPACE]* [+NAMESPACE]* 
+	The possible namespaces are builtin, user, internal,
+	alias. Where builtin and user are default. Builtin contains
+	the python module builtin, user contains all imported
+	namespaces, alias only contain the shell aliases and no python
+	objects, internal contains objects used by IPython. The
+	namespaces on the search path are removed by -namespace and
+	added by +namespace.
+
+	[-a] makes the pattern match even objects with a single underscore.
+	[-c] makes the pattern case sensitive.
+    
+	examples:
+	%psearch a*            list objects beginning with an a
+	%psearch a* function   list all functions beginning with an a
+	%psearch re.e*         list objects beginning with an e in module re
+	%psearch r*.e*         list objects that starts with e in modules starting in r
+	%psearch r*.* string   list all strings in modules beginning with r 
+
+	case sensitve serach:
+	%psearch a* -c         list all object beginning with lower case a
+
+	Show objects beginning with a single _
+	%psearch _* -a         list objects beginning with underscore         
+	"""
+        self.shell.inspector.psearch(parameter_s,shell=self.shell)
+
     def magic_who_ls(self, parameter_s=''):
         """Return a sorted list of all interactive variables.
 
Index: OInspect.py
===================================================================
--- OInspect.py	(revision 909)
+++ OInspect.py	(working copy)
@@ -31,6 +31,8 @@
 from IPython import PyColorize
 from IPython.ColorANSI import *
 
+import wildcard
+from wildcard import choose_namespaces,list_namespace
 #****************************************************************************
 # Builtin color schemes
 
@@ -396,3 +398,46 @@
         if output:
             page(output)
         # end pinfo
+
+    def psearch(self,oname='',formatter = None,shell=None):
+	"""Search namespaces with wildcards for objects.
+
+        Optional arguments:
+        
+        - oname: rest of the commandline containging pattern and options
+
+        - formatter:  Not used
+
+        - shell:      The shell object from the Magic class. Needed to 
+	access the namespaces
+
+    """
+	option_list=["-c","-a"]
+	import pdb
+#	pdb.set_trace()
+	cmds=oname.split()
+	filter=""
+	type_pattern="all"
+	ns_cmds=[]
+	options=[x for x in cmds if x in option_list]
+	ignorecase="-c" not in options
+	showhidden="-a" in options
+	ns_cmds=[x for x in cmds if x[0] in "-+" and x not in option_list]
+	cmds=[x for x in cmds if x[0] not in "-+"]
+	if len(cmds)>2:  #assume we want to choose name spaces. 
+			 #Rather poor design forces the use of a typepattern in order to choose name spaces
+	    cmds=cmds[:2]
+	if len(cmds)==2:
+	    filter,type_pattern=cmds
+	elif len(cmds)==1:
+	    filter=cmds[0].strip()
+
+	do_list=choose_namespaces(shell,ns_cmds)
+
+	search_result=[]
+	for ns in do_list:
+	    tmp_res=list(list_namespace(ns,type_pattern,filter,ignorecase=ignorecase,showhidden=showhidden))
+	    search_result.extend(tmp_res)
+	search_result.sort()
+
+	page("\n".join(search_result))
Index: wildcard.py
===================================================================
--- wildcard.py	(revision 0)
+++ wildcard.py	(revision 0)
@@ -0,0 +1,139 @@
+import types,re,pprint,exceptions,pdb
+import __builtin__
+import IPython.genutils as genutils
+
+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={}
+    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
+
+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":
+	return True
+    if type(typestr_or_type)==types.TypeType:
+        test_type=typestr_or_type
+    else:
+	test_type=typestr2type.get(typestr_or_type,False)
+    if test_type:
+	return isinstance(obj,test_type)
+    else:
+	return False
+
+def show_hidden(str,showhidden=False):
+    """Return true for strings starting with single _ if showhidden is true."""
+    return showhidden 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,namepattern="*",typepattern="all",ignorecase=True,showhidden=True):
+	self.showhidden=showhidden #Hide names beginning with single _
+        self.object=obj
+	self.namepattern=namepattern
+	self.typepattern=typepattern
+	self.ignorecase=ignorecase
+	if type(obj)==type(dict()):
+	    self._ns=obj
+	else:
+	    try:
+		self._ns=self.object.__dict__
+	    except exceptions.AttributeError:
+		self._ns=dict([(key,getattr(self.object,key)) 
+			   for key in dir(self.object)])
+    def get_ns(self):
+	"""Return name space dictionary with objects matching type and name patterns."""
+	return self.filter(self.namepattern,self.typepattern)
+    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 filter(self,namepattern,typepattern):
+	"""Return dictionary of filtered namespace."""
+        def glob_filter(lista,namepattern,hidehidden,ignorecase):
+	    """Return list of elements in lista that match pattern."""
+            pattern=namepattern.replace("*",".*")
+	    if ignorecase:
+#		print "ignoring 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 namepattern
+        all=[(x,ns[x]) for x in glob_filter(ns.keys(),namepattern,self.showhidden,self.ignorecase)]
+	#Filter namespace by typepattern
+        all=[(key,obj) for key,obj in all if is_type(obj,typepattern)]
+        all=dict(all)
+        return all
+
+    #TODO: Implement dictionary like access to filtered name space?
+
+def list_namespace(namespace,typepattern,filter,ignorecase=False,showhidden=False):
+    """Return dictionary of all objects in namespace that matches typepattern and filter."""
+    patternlist=filter.split(".")
+    if len(patternlist)==1:
+	ns=NameSpace(namespace,
+		      namepattern=patternlist[0],
+		      typepattern=typepattern,ignorecase=ignorecase,showhidden=showhidden)
+#	import pdb
+#	pdb.set_trace()
+	return ns.ns
+    if len(patternlist)>1:
+	#This is where we can change if all objects should be searched or only moduleas
+	#Just change the typepattern to module to search only modules
+	ns=NameSpace(namespace,
+		      namepattern=patternlist[0],
+		      typepattern="all",ignorecase=ignorecase,showhidden=showhidden)
+	res={}
+	nsdict=ns.ns
+	for name,obj in nsdict.iteritems():
+	    ns=list_namespace(obj,typepattern,".".join(patternlist[1:]),ignorecase=ignorecase,showhidden=showhidden)
+	    for inner_name,inner_obj in ns.iteritems():
+		res["%s.%s"%(name,inner_name)]=inner_obj
+	return res
+
+
+def choose_namespaces(shell,cmds):
+    """Returns a list of namespaces modified by arguments."""
+    nslist=genutils.mkdict(user=shell.user_ns,internal=shell.internal_ns,
+		builtin=__builtin__.__dict__,alias=shell.alias_table)
+    default_list=["user","builtin"]  # Should this list be a user option??
+    for cmd in cmds:
+	if cmd[0]=="-":  #remove from defaultlist
+	    if cmd[1:] in default_list:
+		default_list.remove(cmd[1:])
+	elif cmd[0]=="+":
+	    if cmd[1:] not in default_list and cmd[1:]in nslist:
+		default_list.append(cmd[1:])
+	else:
+	    if cmd in nslist:
+		default_list.append(cmd[1:])
+    return [nslist[x] for x in default_list]
+
+if __name__=="__main__":
+    pass
+
