From be7a400714ea75270c06edf0830df814216be7bd 2005-10-15 07:57:05 From: fperez Date: 2005-10-15 07:57:05 Subject: [PATCH] New wildcard support. Lightly tested, so proceed with caution. We need to sync this on the nbshell/chainsaw branches. --- diff --git a/IPython/Magic.py b/IPython/Magic.py index cc6834a..9664073 100644 --- a/IPython/Magic.py +++ b/IPython/Magic.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Magic functions for InteractiveShell. -$Id: Magic.py 908 2005-09-26 16:05:48Z fperez $""" +$Id: Magic.py 919 2005-10-15 07:57:05Z fperez $""" #***************************************************************************** # Copyright (C) 2001 Janko Hauser and @@ -34,8 +34,9 @@ from cStringIO import StringIO from IPython.Struct import Struct from IPython.Itpl import Itpl, itpl, printpl,itplns from IPython.FakeModule import FakeModule -from IPython import OInspect from IPython.PyColorize import Parser +from IPython import OInspect +from IPython import wildcard from IPython.genutils import * # Globals to be set later by Magic constructor @@ -656,7 +657,63 @@ Currently the magic system has the following functions:\n""" 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] + + Note: ? can be used as a synonym for %psearch, at the beginning or at + the end: both a*? and ?a* are equivalent to '%psearch a*'. + + 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 search: + + %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. diff --git a/IPython/OInspect.py b/IPython/OInspect.py index dfe72a1..a93a3ed 100644 --- a/IPython/OInspect.py +++ b/IPython/OInspect.py @@ -6,7 +6,7 @@ Uses syntax highlighting for presenting the various information elements. Similar in spirit to the inspect module, but all calls take a name argument to reference the name under which an object is being read. -$Id: OInspect.py 575 2005-04-08 14:16:44Z fperez $ +$Id: OInspect.py 919 2005-10-15 07:57:05Z fperez $ """ #***************************************************************************** @@ -26,9 +26,10 @@ __all__ = ['Inspector','InspectColors'] import inspect,linecache,types,StringIO,string # IPython's own +from IPython import PyColorize from IPython.Itpl import itpl +from IPython.wildcard import choose_namespaces,list_namespace from IPython.genutils import page,indent,Term -from IPython import PyColorize from IPython.ColorANSI import * #**************************************************************************** @@ -396,3 +397,46 @@ class Inspector: 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)) diff --git a/IPython/wildcard.py b/IPython/wildcard.py new file mode 100644 index 0000000..29e30ec --- /dev/null +++ b/IPython/wildcard.py @@ -0,0 +1,157 @@ +# -*- coding: utf-8 -*- +"""Support for wildcard pattern matching in object inspection. + +$Id: OInspect.py 608 2005-07-06 17:52:32Z fperez $ +""" + +#***************************************************************************** +# Copyright (C) 2005 Jörgen Stenarson +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** + +from IPython import Release +__author__ = "Jörgen Stenarson " +__license__ = Release.license + +import __builtin__ +import types +import re +import pprint +import exceptions +import pdb +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: + 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) + 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] diff --git a/doc/ChangeLog b/doc/ChangeLog index e16fa0b..a103468 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,10 +1,19 @@ +2005-10-15 Fernando Perez + + * IPython/Magic.py (magic_psearch): new support for wildcard + patterns. Now, typing ?a*b will list all names which begin with a + and end in b, for example. The %psearch magic has full + docstrings. Many thanks to Jörgen Stenarson + , author of the patches + implementing this functionality. + 2005-09-27 Fernando Perez * Manual: fixed long-standing annoyance of double-dashes (as in --prefix=~, for example) being stripped in the HTML version. This is a latex2html bug, but a workaround was provided. Many thanks - to George K. Thiruvathukal for the detailed - help, and Michael Tobis for getting the ball + to George K. Thiruvathukal for the detailed + help, and Michael Tobis for getting the ball rolling. This seemingly small issue had tripped a number of users when first installing, so I'm glad to see it gone. @@ -76,7 +85,7 @@ * IPython/iplib.py (InteractiveShell.__init__): Trap exception if os.getcwd() fails at init time. Thanks to patch from David Remahl - . + . (InteractiveShell.__init__): prevent certain special magics from being shadowed by aliases. Closes http://www.scipy.net/roundup/ipython/issue41. @@ -174,7 +183,7 @@ slightly modified version of the patch in http://www.scipy.net/roundup/ipython/issue34, which also allows me to remove the previous try/except solution (which was costlier). - Thanks to Gaetan Lehmann for the fix. + Thanks to Gaetan Lehmann for the fix. 2005-06-08 Fernando Perez diff --git a/doc/manual_base.lyx b/doc/manual_base.lyx index f445ca5..e7e8550 100644 --- a/doc/manual_base.lyx +++ b/doc/manual_base.lyx @@ -9053,4 +9053,13 @@ Sakkis < gsakkis-AT-eden.rutgers.edu> \family default New matcher for tab-completing named arguments of user-defined functions. +\layout List +\labelwidthstring 00.00.0000 + +J�rgen\SpecialChar ~ +Stenarson +\family typewriter + +\family default + Wildcard support implementation for searching namespaces. \the_end