From 4e3685be1b53680b1894da6bba96141ed582b123 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Fri, 3 Sep 2010 00:38:49 -0700 Subject: [PATCH] Move object inspection machinery out of the magics code. Later we'll put this into a standalone object, but the least intrusive change for now is to put it in the main interactive shell object. At least now it's not embedded in the magics code, and we can start using this programatically via the messaging api. --- IPython/core/interactiveshell.py | 138 ++++++++++++++++++++++++++++++- IPython/core/magic.py | 130 ----------------------------- 2 files changed, 136 insertions(+), 132 deletions(-) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index d14aae9d21d..74003de58e8 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -18,6 +18,7 @@ from __future__ import absolute_import import __builtin__ +import __future__ import abc import codeop import exceptions @@ -40,7 +41,7 @@ from IPython.core.builtin_trap import BuiltinTrap from IPython.core.display_trap import DisplayTrap from IPython.core.displayhook import DisplayHook -from IPython.core.error import UsageError +from IPython.core.error import TryNext, UsageError from IPython.core.extensions import ExtensionManager from IPython.core.fakemodule import FakeModule, init_fakemod_dict from IPython.core.inputlist import InputList @@ -48,7 +49,7 @@ from IPython.core.magic import Magic from IPython.core.payload import PayloadManager from IPython.core.plugin import PluginManager -from IPython.core.prefilter import PrefilterManager +from IPython.core.prefilter import PrefilterManager, ESC_MAGIC from IPython.external.Itpl import ItplNS from IPython.utils import PyColorize from IPython.utils import io @@ -1025,6 +1026,139 @@ def push(self, variables, interactive=True): for name,val in vdict.iteritems(): config_ns[name] = val + #------------------------------------------------------------------------- + # Things related to object introspection + #------------------------------------------------------------------------- + def _ofind(self, oname, namespaces=None): + """Find an object in the available namespaces. + + self._ofind(oname) -> dict with keys: found,obj,ospace,ismagic + + Has special code to detect magic functions. + """ + oname = oname.strip() + alias_ns = None + if namespaces is None: + # Namespaces to search in: + # Put them in a list. The order is important so that we + # find things in the same order that Python finds them. + namespaces = [ ('Interactive', self.shell.user_ns), + ('IPython internal', self.shell.internal_ns), + ('Python builtin', __builtin__.__dict__), + ('Alias', self.shell.alias_manager.alias_table), + ] + alias_ns = self.shell.alias_manager.alias_table + + # initialize results to 'null' + found = False; obj = None; ospace = None; ds = None; + ismagic = False; isalias = False; parent = None + + # We need to special-case 'print', which as of python2.6 registers as a + # function but should only be treated as one if print_function was + # loaded with a future import. In this case, just bail. + if (oname == 'print' and not (self.shell.compile.compiler.flags & + __future__.CO_FUTURE_PRINT_FUNCTION)): + return {'found':found, 'obj':obj, 'namespace':ospace, + 'ismagic':ismagic, 'isalias':isalias, 'parent':parent} + + # Look for the given name by splitting it in parts. If the head is + # found, then we look for all the remaining parts as members, and only + # declare success if we can find them all. + oname_parts = oname.split('.') + oname_head, oname_rest = oname_parts[0],oname_parts[1:] + for nsname,ns in namespaces: + try: + obj = ns[oname_head] + except KeyError: + continue + else: + #print 'oname_rest:', oname_rest # dbg + for part in oname_rest: + try: + parent = obj + obj = getattr(obj,part) + except: + # Blanket except b/c some badly implemented objects + # allow __getattr__ to raise exceptions other than + # AttributeError, which then crashes IPython. + break + else: + # If we finish the for loop (no break), we got all members + found = True + ospace = nsname + if ns == alias_ns: + isalias = True + break # namespace loop + + # Try to see if it's magic + if not found: + if oname.startswith(ESC_MAGIC): + oname = oname[1:] + obj = getattr(self,'magic_'+oname,None) + if obj is not None: + found = True + ospace = 'IPython internal' + ismagic = True + + # Last try: special-case some literals like '', [], {}, etc: + if not found and oname_head in ["''",'""','[]','{}','()']: + obj = eval(oname_head) + found = True + ospace = 'Interactive' + + return {'found':found, 'obj':obj, 'namespace':ospace, + 'ismagic':ismagic, 'isalias':isalias, 'parent':parent} + + def _inspect(self,meth,oname,namespaces=None,**kw): + """Generic interface to the inspector system. + + This function is meant to be called by pdef, pdoc & friends.""" + + #oname = oname.strip() + #print '1- oname: <%r>' % oname # dbg + try: + oname = oname.strip().encode('ascii') + #print '2- oname: <%r>' % oname # dbg + except UnicodeEncodeError: + print 'Python identifiers can only contain ascii characters.' + return 'not found' + + info = Struct(self._ofind(oname, namespaces)) + + if info.found: + try: + IPython.utils.generics.inspect_object(info.obj) + return + except TryNext: + pass + # Get the docstring of the class property if it exists. + path = oname.split('.') + root = '.'.join(path[:-1]) + if info.parent is not None: + try: + target = getattr(info.parent, '__class__') + # The object belongs to a class instance. + try: + target = getattr(target, path[-1]) + # The class defines the object. + if isinstance(target, property): + oname = root + '.__class__.' + path[-1] + info = Struct(self._ofind(oname)) + except AttributeError: pass + except AttributeError: pass + + pmethod = getattr(self.shell.inspector,meth) + formatter = info.ismagic and self.format_screen or None + if meth == 'pdoc': + pmethod(info.obj,oname,formatter) + elif meth == 'pinfo': + pmethod(info.obj,oname,formatter,info,**kw) + else: + pmethod(info.obj,oname) + else: + print 'Object `%s` not found.' % oname + return 'not found' # so callers can take other action + #------------------------------------------------------------------------- # Things related to history management #------------------------------------------------------------------------- diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 0287f3d8e79..d72a4f09004 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -207,87 +207,7 @@ def extract_input_slices(self,slices,raw=False): fin = ini+1 cmds.append(hist[ini:fin]) return cmds - - def _ofind(self, oname, namespaces=None): - """Find an object in the available namespaces. - - self._ofind(oname) -> dict with keys: found,obj,ospace,ismagic - - Has special code to detect magic functions. - """ - oname = oname.strip() - alias_ns = None - if namespaces is None: - # Namespaces to search in: - # Put them in a list. The order is important so that we - # find things in the same order that Python finds them. - namespaces = [ ('Interactive', self.shell.user_ns), - ('IPython internal', self.shell.internal_ns), - ('Python builtin', __builtin__.__dict__), - ('Alias', self.shell.alias_manager.alias_table), - ] - alias_ns = self.shell.alias_manager.alias_table - - # initialize results to 'null' - found = False; obj = None; ospace = None; ds = None; - ismagic = False; isalias = False; parent = None - - # We need to special-case 'print', which as of python2.6 registers as a - # function but should only be treated as one if print_function was - # loaded with a future import. In this case, just bail. - if (oname == 'print' and not (self.shell.compile.compiler.flags & - __future__.CO_FUTURE_PRINT_FUNCTION)): - return {'found':found, 'obj':obj, 'namespace':ospace, - 'ismagic':ismagic, 'isalias':isalias, 'parent':parent} - - # Look for the given name by splitting it in parts. If the head is - # found, then we look for all the remaining parts as members, and only - # declare success if we can find them all. - oname_parts = oname.split('.') - oname_head, oname_rest = oname_parts[0],oname_parts[1:] - for nsname,ns in namespaces: - try: - obj = ns[oname_head] - except KeyError: - continue - else: - #print 'oname_rest:', oname_rest # dbg - for part in oname_rest: - try: - parent = obj - obj = getattr(obj,part) - except: - # Blanket except b/c some badly implemented objects - # allow __getattr__ to raise exceptions other than - # AttributeError, which then crashes IPython. - break - else: - # If we finish the for loop (no break), we got all members - found = True - ospace = nsname - if ns == alias_ns: - isalias = True - break # namespace loop - - # Try to see if it's magic - if not found: - if oname.startswith(ESC_MAGIC): - oname = oname[1:] - obj = getattr(self,'magic_'+oname,None) - if obj is not None: - found = True - ospace = 'IPython internal' - ismagic = True - - # Last try: special-case some literals like '', [], {}, etc: - if not found and oname_head in ["''",'""','[]','{}','()']: - obj = eval(oname_head) - found = True - ospace = 'Interactive' - return {'found':found, 'obj':obj, 'namespace':ospace, - 'ismagic':ismagic, 'isalias':isalias, 'parent':parent} - def arg_err(self,func): """Print docstring if incorrect arguments were passed""" print 'Error in arguments:' @@ -708,56 +628,6 @@ def magic_pfile(self, parameter_s=''): return page.page(self.shell.inspector.format(file(filename).read())) - def _inspect(self,meth,oname,namespaces=None,**kw): - """Generic interface to the inspector system. - - This function is meant to be called by pdef, pdoc & friends.""" - - #oname = oname.strip() - #print '1- oname: <%r>' % oname # dbg - try: - oname = oname.strip().encode('ascii') - #print '2- oname: <%r>' % oname # dbg - except UnicodeEncodeError: - print 'Python identifiers can only contain ascii characters.' - return 'not found' - - info = Struct(self._ofind(oname, namespaces)) - - if info.found: - try: - IPython.utils.generics.inspect_object(info.obj) - return - except TryNext: - pass - # Get the docstring of the class property if it exists. - path = oname.split('.') - root = '.'.join(path[:-1]) - if info.parent is not None: - try: - target = getattr(info.parent, '__class__') - # The object belongs to a class instance. - try: - target = getattr(target, path[-1]) - # The class defines the object. - if isinstance(target, property): - oname = root + '.__class__.' + path[-1] - info = Struct(self._ofind(oname)) - except AttributeError: pass - except AttributeError: pass - - pmethod = getattr(self.shell.inspector,meth) - formatter = info.ismagic and self.format_screen or None - if meth == 'pdoc': - pmethod(info.obj,oname,formatter) - elif meth == 'pinfo': - pmethod(info.obj,oname,formatter,info,**kw) - else: - pmethod(info.obj,oname) - else: - print 'Object `%s` not found.' % oname - return 'not found' # so callers can take other action - def magic_psearch(self, parameter_s=''): """Search for object in namespaces by wildcard.