Chris@87: from __future__ import division, absolute_import, print_function Chris@87: Chris@87: import os Chris@87: import sys Chris@87: import types Chris@87: import re Chris@87: Chris@87: from numpy.core.numerictypes import issubclass_, issubsctype, issubdtype Chris@87: from numpy.core import ndarray, ufunc, asarray Chris@87: Chris@87: __all__ = [ Chris@87: 'issubclass_', 'issubsctype', 'issubdtype', 'deprecate', Chris@87: 'deprecate_with_doc', 'get_include', 'info', 'source', 'who', Chris@87: 'lookfor', 'byte_bounds', 'safe_eval' Chris@87: ] Chris@87: Chris@87: def get_include(): Chris@87: """ Chris@87: Return the directory that contains the NumPy \\*.h header files. Chris@87: Chris@87: Extension modules that need to compile against NumPy should use this Chris@87: function to locate the appropriate include directory. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: When using ``distutils``, for example in ``setup.py``. Chris@87: :: Chris@87: Chris@87: import numpy as np Chris@87: ... Chris@87: Extension('extension_name', ... Chris@87: include_dirs=[np.get_include()]) Chris@87: ... Chris@87: Chris@87: """ Chris@87: import numpy Chris@87: if numpy.show_config is None: Chris@87: # running from numpy source directory Chris@87: d = os.path.join(os.path.dirname(numpy.__file__), 'core', 'include') Chris@87: else: Chris@87: # using installed numpy core headers Chris@87: import numpy.core as core Chris@87: d = os.path.join(os.path.dirname(core.__file__), 'include') Chris@87: return d Chris@87: Chris@87: Chris@87: def _set_function_name(func, name): Chris@87: func.__name__ = name Chris@87: return func Chris@87: Chris@87: Chris@87: class _Deprecate(object): Chris@87: """ Chris@87: Decorator class to deprecate old functions. Chris@87: Chris@87: Refer to `deprecate` for details. Chris@87: Chris@87: See Also Chris@87: -------- Chris@87: deprecate Chris@87: Chris@87: """ Chris@87: Chris@87: def __init__(self, old_name=None, new_name=None, message=None): Chris@87: self.old_name = old_name Chris@87: self.new_name = new_name Chris@87: self.message = message Chris@87: Chris@87: def __call__(self, func, *args, **kwargs): Chris@87: """ Chris@87: Decorator call. Refer to ``decorate``. Chris@87: Chris@87: """ Chris@87: old_name = self.old_name Chris@87: new_name = self.new_name Chris@87: message = self.message Chris@87: Chris@87: import warnings Chris@87: if old_name is None: Chris@87: try: Chris@87: old_name = func.__name__ Chris@87: except AttributeError: Chris@87: old_name = func.__name__ Chris@87: if new_name is None: Chris@87: depdoc = "`%s` is deprecated!" % old_name Chris@87: else: Chris@87: depdoc = "`%s` is deprecated, use `%s` instead!" % \ Chris@87: (old_name, new_name) Chris@87: Chris@87: if message is not None: Chris@87: depdoc += "\n" + message Chris@87: Chris@87: def newfunc(*args,**kwds): Chris@87: """`arrayrange` is deprecated, use `arange` instead!""" Chris@87: warnings.warn(depdoc, DeprecationWarning) Chris@87: return func(*args, **kwds) Chris@87: Chris@87: newfunc = _set_function_name(newfunc, old_name) Chris@87: doc = func.__doc__ Chris@87: if doc is None: Chris@87: doc = depdoc Chris@87: else: Chris@87: doc = '\n\n'.join([depdoc, doc]) Chris@87: newfunc.__doc__ = doc Chris@87: try: Chris@87: d = func.__dict__ Chris@87: except AttributeError: Chris@87: pass Chris@87: else: Chris@87: newfunc.__dict__.update(d) Chris@87: return newfunc Chris@87: Chris@87: def deprecate(*args, **kwargs): Chris@87: """ Chris@87: Issues a DeprecationWarning, adds warning to `old_name`'s Chris@87: docstring, rebinds ``old_name.__name__`` and returns the new Chris@87: function object. Chris@87: Chris@87: This function may also be used as a decorator. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: func : function Chris@87: The function to be deprecated. Chris@87: old_name : str, optional Chris@87: The name of the function to be deprecated. Default is None, in Chris@87: which case the name of `func` is used. Chris@87: new_name : str, optional Chris@87: The new name for the function. Default is None, in which case the Chris@87: deprecation message is that `old_name` is deprecated. If given, the Chris@87: deprecation message is that `old_name` is deprecated and `new_name` Chris@87: should be used instead. Chris@87: message : str, optional Chris@87: Additional explanation of the deprecation. Displayed in the Chris@87: docstring after the warning. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: old_func : function Chris@87: The deprecated function. Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: Note that ``olduint`` returns a value after printing Deprecation Chris@87: Warning: Chris@87: Chris@87: >>> olduint = np.deprecate(np.uint) Chris@87: >>> olduint(6) Chris@87: /usr/lib/python2.5/site-packages/numpy/lib/utils.py:114: Chris@87: DeprecationWarning: uint32 is deprecated Chris@87: warnings.warn(str1, DeprecationWarning) Chris@87: 6 Chris@87: Chris@87: """ Chris@87: # Deprecate may be run as a function or as a decorator Chris@87: # If run as a function, we initialise the decorator class Chris@87: # and execute its __call__ method. Chris@87: Chris@87: if args: Chris@87: fn = args[0] Chris@87: args = args[1:] Chris@87: Chris@87: # backward compatibility -- can be removed Chris@87: # after next release Chris@87: if 'newname' in kwargs: Chris@87: kwargs['new_name'] = kwargs.pop('newname') Chris@87: if 'oldname' in kwargs: Chris@87: kwargs['old_name'] = kwargs.pop('oldname') Chris@87: Chris@87: return _Deprecate(*args, **kwargs)(fn) Chris@87: else: Chris@87: return _Deprecate(*args, **kwargs) Chris@87: Chris@87: deprecate_with_doc = lambda msg: _Deprecate(message=msg) Chris@87: Chris@87: Chris@87: #-------------------------------------------- Chris@87: # Determine if two arrays can share memory Chris@87: #-------------------------------------------- Chris@87: Chris@87: def byte_bounds(a): Chris@87: """ Chris@87: Returns pointers to the end-points of an array. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: a : ndarray Chris@87: Input array. It must conform to the Python-side of the array Chris@87: interface. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: (low, high) : tuple of 2 integers Chris@87: The first integer is the first byte of the array, the second Chris@87: integer is just past the last byte of the array. If `a` is not Chris@87: contiguous it will not use every byte between the (`low`, `high`) Chris@87: values. Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: >>> I = np.eye(2, dtype='f'); I.dtype Chris@87: dtype('float32') Chris@87: >>> low, high = np.byte_bounds(I) Chris@87: >>> high - low == I.size*I.itemsize Chris@87: True Chris@87: >>> I = np.eye(2, dtype='G'); I.dtype Chris@87: dtype('complex192') Chris@87: >>> low, high = np.byte_bounds(I) Chris@87: >>> high - low == I.size*I.itemsize Chris@87: True Chris@87: Chris@87: """ Chris@87: ai = a.__array_interface__ Chris@87: a_data = ai['data'][0] Chris@87: astrides = ai['strides'] Chris@87: ashape = ai['shape'] Chris@87: bytes_a = asarray(a).dtype.itemsize Chris@87: Chris@87: a_low = a_high = a_data Chris@87: if astrides is None: Chris@87: # contiguous case Chris@87: a_high += a.size * bytes_a Chris@87: else: Chris@87: for shape, stride in zip(ashape, astrides): Chris@87: if stride < 0: Chris@87: a_low += (shape-1)*stride Chris@87: else: Chris@87: a_high += (shape-1)*stride Chris@87: a_high += bytes_a Chris@87: return a_low, a_high Chris@87: Chris@87: Chris@87: #----------------------------------------------------------------------------- Chris@87: # Function for output and information on the variables used. Chris@87: #----------------------------------------------------------------------------- Chris@87: Chris@87: Chris@87: def who(vardict=None): Chris@87: """ Chris@87: Print the Numpy arrays in the given dictionary. Chris@87: Chris@87: If there is no dictionary passed in or `vardict` is None then returns Chris@87: Numpy arrays in the globals() dictionary (all Numpy arrays in the Chris@87: namespace). Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: vardict : dict, optional Chris@87: A dictionary possibly containing ndarrays. Default is globals(). Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: out : None Chris@87: Returns 'None'. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: Prints out the name, shape, bytes and type of all of the ndarrays Chris@87: present in `vardict`. Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: >>> a = np.arange(10) Chris@87: >>> b = np.ones(20) Chris@87: >>> np.who() Chris@87: Name Shape Bytes Type Chris@87: =========================================================== Chris@87: a 10 40 int32 Chris@87: b 20 160 float64 Chris@87: Upper bound on total bytes = 200 Chris@87: Chris@87: >>> d = {'x': np.arange(2.0), 'y': np.arange(3.0), 'txt': 'Some str', Chris@87: ... 'idx':5} Chris@87: >>> np.who(d) Chris@87: Name Shape Bytes Type Chris@87: =========================================================== Chris@87: y 3 24 float64 Chris@87: x 2 16 float64 Chris@87: Upper bound on total bytes = 40 Chris@87: Chris@87: """ Chris@87: if vardict is None: Chris@87: frame = sys._getframe().f_back Chris@87: vardict = frame.f_globals Chris@87: sta = [] Chris@87: cache = {} Chris@87: for name in vardict.keys(): Chris@87: if isinstance(vardict[name], ndarray): Chris@87: var = vardict[name] Chris@87: idv = id(var) Chris@87: if idv in cache.keys(): Chris@87: namestr = name + " (%s)" % cache[idv] Chris@87: original = 0 Chris@87: else: Chris@87: cache[idv] = name Chris@87: namestr = name Chris@87: original = 1 Chris@87: shapestr = " x ".join(map(str, var.shape)) Chris@87: bytestr = str(var.nbytes) Chris@87: sta.append([namestr, shapestr, bytestr, var.dtype.name, Chris@87: original]) Chris@87: Chris@87: maxname = 0 Chris@87: maxshape = 0 Chris@87: maxbyte = 0 Chris@87: totalbytes = 0 Chris@87: for k in range(len(sta)): Chris@87: val = sta[k] Chris@87: if maxname < len(val[0]): Chris@87: maxname = len(val[0]) Chris@87: if maxshape < len(val[1]): Chris@87: maxshape = len(val[1]) Chris@87: if maxbyte < len(val[2]): Chris@87: maxbyte = len(val[2]) Chris@87: if val[4]: Chris@87: totalbytes += int(val[2]) Chris@87: Chris@87: if len(sta) > 0: Chris@87: sp1 = max(10, maxname) Chris@87: sp2 = max(10, maxshape) Chris@87: sp3 = max(10, maxbyte) Chris@87: prval = "Name %s Shape %s Bytes %s Type" % (sp1*' ', sp2*' ', sp3*' ') Chris@87: print(prval + "\n" + "="*(len(prval)+5) + "\n") Chris@87: Chris@87: for k in range(len(sta)): Chris@87: val = sta[k] Chris@87: print("%s %s %s %s %s %s %s" % (val[0], ' '*(sp1-len(val[0])+4), Chris@87: val[1], ' '*(sp2-len(val[1])+5), Chris@87: val[2], ' '*(sp3-len(val[2])+5), Chris@87: val[3])) Chris@87: print("\nUpper bound on total bytes = %d" % totalbytes) Chris@87: return Chris@87: Chris@87: #----------------------------------------------------------------------------- Chris@87: Chris@87: Chris@87: # NOTE: pydoc defines a help function which works simliarly to this Chris@87: # except it uses a pager to take over the screen. Chris@87: Chris@87: # combine name and arguments and split to multiple lines of width Chris@87: # characters. End lines on a comma and begin argument list indented with Chris@87: # the rest of the arguments. Chris@87: def _split_line(name, arguments, width): Chris@87: firstwidth = len(name) Chris@87: k = firstwidth Chris@87: newstr = name Chris@87: sepstr = ", " Chris@87: arglist = arguments.split(sepstr) Chris@87: for argument in arglist: Chris@87: if k == firstwidth: Chris@87: addstr = "" Chris@87: else: Chris@87: addstr = sepstr Chris@87: k = k + len(argument) + len(addstr) Chris@87: if k > width: Chris@87: k = firstwidth + 1 + len(argument) Chris@87: newstr = newstr + ",\n" + " "*(firstwidth+2) + argument Chris@87: else: Chris@87: newstr = newstr + addstr + argument Chris@87: return newstr Chris@87: Chris@87: _namedict = None Chris@87: _dictlist = None Chris@87: Chris@87: # Traverse all module directories underneath globals Chris@87: # to see if something is defined Chris@87: def _makenamedict(module='numpy'): Chris@87: module = __import__(module, globals(), locals(), []) Chris@87: thedict = {module.__name__:module.__dict__} Chris@87: dictlist = [module.__name__] Chris@87: totraverse = [module.__dict__] Chris@87: while True: Chris@87: if len(totraverse) == 0: Chris@87: break Chris@87: thisdict = totraverse.pop(0) Chris@87: for x in thisdict.keys(): Chris@87: if isinstance(thisdict[x], types.ModuleType): Chris@87: modname = thisdict[x].__name__ Chris@87: if modname not in dictlist: Chris@87: moddict = thisdict[x].__dict__ Chris@87: dictlist.append(modname) Chris@87: totraverse.append(moddict) Chris@87: thedict[modname] = moddict Chris@87: return thedict, dictlist Chris@87: Chris@87: Chris@87: def _info(obj, output=sys.stdout): Chris@87: """Provide information about ndarray obj. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: obj: ndarray Chris@87: Must be ndarray, not checked. Chris@87: output: Chris@87: Where printed output goes. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: Copied over from the numarray module prior to its removal. Chris@87: Adapted somewhat as only numpy is an option now. Chris@87: Chris@87: Called by info. Chris@87: Chris@87: """ Chris@87: extra = "" Chris@87: tic = "" Chris@87: bp = lambda x: x Chris@87: cls = getattr(obj, '__class__', type(obj)) Chris@87: nm = getattr(cls, '__name__', cls) Chris@87: strides = obj.strides Chris@87: endian = obj.dtype.byteorder Chris@87: Chris@87: print("class: ", nm, file=output) Chris@87: print("shape: ", obj.shape, file=output) Chris@87: print("strides: ", strides, file=output) Chris@87: print("itemsize: ", obj.itemsize, file=output) Chris@87: print("aligned: ", bp(obj.flags.aligned), file=output) Chris@87: print("contiguous: ", bp(obj.flags.contiguous), file=output) Chris@87: print("fortran: ", obj.flags.fortran, file=output) Chris@87: print( Chris@87: "data pointer: %s%s" % (hex(obj.ctypes._as_parameter_.value), extra), Chris@87: file=output Chris@87: ) Chris@87: print("byteorder: ", end=' ', file=output) Chris@87: if endian in ['|', '=']: Chris@87: print("%s%s%s" % (tic, sys.byteorder, tic), file=output) Chris@87: byteswap = False Chris@87: elif endian == '>': Chris@87: print("%sbig%s" % (tic, tic), file=output) Chris@87: byteswap = sys.byteorder != "big" Chris@87: else: Chris@87: print("%slittle%s" % (tic, tic), file=output) Chris@87: byteswap = sys.byteorder != "little" Chris@87: print("byteswap: ", bp(byteswap), file=output) Chris@87: print("type: %s" % obj.dtype, file=output) Chris@87: Chris@87: Chris@87: def info(object=None, maxwidth=76, output=sys.stdout, toplevel='numpy'): Chris@87: """ Chris@87: Get help information for a function, class, or module. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: object : object or str, optional Chris@87: Input object or name to get information about. If `object` is a Chris@87: numpy object, its docstring is given. If it is a string, available Chris@87: modules are searched for matching objects. If None, information Chris@87: about `info` itself is returned. Chris@87: maxwidth : int, optional Chris@87: Printing width. Chris@87: output : file like object, optional Chris@87: File like object that the output is written to, default is Chris@87: ``stdout``. The object has to be opened in 'w' or 'a' mode. Chris@87: toplevel : str, optional Chris@87: Start search at this level. Chris@87: Chris@87: See Also Chris@87: -------- Chris@87: source, lookfor Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: When used interactively with an object, ``np.info(obj)`` is equivalent Chris@87: to ``help(obj)`` on the Python prompt or ``obj?`` on the IPython Chris@87: prompt. Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: >>> np.info(np.polyval) # doctest: +SKIP Chris@87: polyval(p, x) Chris@87: Evaluate the polynomial p at x. Chris@87: ... Chris@87: Chris@87: When using a string for `object` it is possible to get multiple results. Chris@87: Chris@87: >>> np.info('fft') # doctest: +SKIP Chris@87: *** Found in numpy *** Chris@87: Core FFT routines Chris@87: ... Chris@87: *** Found in numpy.fft *** Chris@87: fft(a, n=None, axis=-1) Chris@87: ... Chris@87: *** Repeat reference found in numpy.fft.fftpack *** Chris@87: *** Total of 3 references found. *** Chris@87: Chris@87: """ Chris@87: global _namedict, _dictlist Chris@87: # Local import to speed up numpy's import time. Chris@87: import pydoc Chris@87: import inspect Chris@87: Chris@87: if (hasattr(object, '_ppimport_importer') or Chris@87: hasattr(object, '_ppimport_module')): Chris@87: object = object._ppimport_module Chris@87: elif hasattr(object, '_ppimport_attr'): Chris@87: object = object._ppimport_attr Chris@87: Chris@87: if object is None: Chris@87: info(info) Chris@87: elif isinstance(object, ndarray): Chris@87: _info(object, output=output) Chris@87: elif isinstance(object, str): Chris@87: if _namedict is None: Chris@87: _namedict, _dictlist = _makenamedict(toplevel) Chris@87: numfound = 0 Chris@87: objlist = [] Chris@87: for namestr in _dictlist: Chris@87: try: Chris@87: obj = _namedict[namestr][object] Chris@87: if id(obj) in objlist: Chris@87: print("\n " Chris@87: "*** Repeat reference found in %s *** " % namestr, Chris@87: file=output Chris@87: ) Chris@87: else: Chris@87: objlist.append(id(obj)) Chris@87: print(" *** Found in %s ***" % namestr, file=output) Chris@87: info(obj) Chris@87: print("-"*maxwidth, file=output) Chris@87: numfound += 1 Chris@87: except KeyError: Chris@87: pass Chris@87: if numfound == 0: Chris@87: print("Help for %s not found." % object, file=output) Chris@87: else: Chris@87: print("\n " Chris@87: "*** Total of %d references found. ***" % numfound, Chris@87: file=output Chris@87: ) Chris@87: Chris@87: elif inspect.isfunction(object): Chris@87: name = object.__name__ Chris@87: arguments = inspect.formatargspec(*inspect.getargspec(object)) Chris@87: Chris@87: if len(name+arguments) > maxwidth: Chris@87: argstr = _split_line(name, arguments, maxwidth) Chris@87: else: Chris@87: argstr = name + arguments Chris@87: Chris@87: print(" " + argstr + "\n", file=output) Chris@87: print(inspect.getdoc(object), file=output) Chris@87: Chris@87: elif inspect.isclass(object): Chris@87: name = object.__name__ Chris@87: arguments = "()" Chris@87: try: Chris@87: if hasattr(object, '__init__'): Chris@87: arguments = inspect.formatargspec( Chris@87: *inspect.getargspec(object.__init__.__func__) Chris@87: ) Chris@87: arglist = arguments.split(', ') Chris@87: if len(arglist) > 1: Chris@87: arglist[1] = "("+arglist[1] Chris@87: arguments = ", ".join(arglist[1:]) Chris@87: except: Chris@87: pass Chris@87: Chris@87: if len(name+arguments) > maxwidth: Chris@87: argstr = _split_line(name, arguments, maxwidth) Chris@87: else: Chris@87: argstr = name + arguments Chris@87: Chris@87: print(" " + argstr + "\n", file=output) Chris@87: doc1 = inspect.getdoc(object) Chris@87: if doc1 is None: Chris@87: if hasattr(object, '__init__'): Chris@87: print(inspect.getdoc(object.__init__), file=output) Chris@87: else: Chris@87: print(inspect.getdoc(object), file=output) Chris@87: Chris@87: methods = pydoc.allmethods(object) Chris@87: if methods != []: Chris@87: print("\n\nMethods:\n", file=output) Chris@87: for meth in methods: Chris@87: if meth[0] == '_': Chris@87: continue Chris@87: thisobj = getattr(object, meth, None) Chris@87: if thisobj is not None: Chris@87: methstr, other = pydoc.splitdoc( Chris@87: inspect.getdoc(thisobj) or "None" Chris@87: ) Chris@87: print(" %s -- %s" % (meth, methstr), file=output) Chris@87: Chris@87: elif (sys.version_info[0] < 3 Chris@87: and isinstance(object, types.InstanceType)): Chris@87: # check for __call__ method Chris@87: # types.InstanceType is the type of the instances of oldstyle classes Chris@87: print("Instance of class: ", object.__class__.__name__, file=output) Chris@87: print(file=output) Chris@87: if hasattr(object, '__call__'): Chris@87: arguments = inspect.formatargspec( Chris@87: *inspect.getargspec(object.__call__.__func__) Chris@87: ) Chris@87: arglist = arguments.split(', ') Chris@87: if len(arglist) > 1: Chris@87: arglist[1] = "("+arglist[1] Chris@87: arguments = ", ".join(arglist[1:]) Chris@87: else: Chris@87: arguments = "()" Chris@87: Chris@87: if hasattr(object, 'name'): Chris@87: name = "%s" % object.name Chris@87: else: Chris@87: name = "" Chris@87: if len(name+arguments) > maxwidth: Chris@87: argstr = _split_line(name, arguments, maxwidth) Chris@87: else: Chris@87: argstr = name + arguments Chris@87: Chris@87: print(" " + argstr + "\n", file=output) Chris@87: doc = inspect.getdoc(object.__call__) Chris@87: if doc is not None: Chris@87: print(inspect.getdoc(object.__call__), file=output) Chris@87: print(inspect.getdoc(object), file=output) Chris@87: Chris@87: else: Chris@87: print(inspect.getdoc(object), file=output) Chris@87: Chris@87: elif inspect.ismethod(object): Chris@87: name = object.__name__ Chris@87: arguments = inspect.formatargspec( Chris@87: *inspect.getargspec(object.__func__) Chris@87: ) Chris@87: arglist = arguments.split(', ') Chris@87: if len(arglist) > 1: Chris@87: arglist[1] = "("+arglist[1] Chris@87: arguments = ", ".join(arglist[1:]) Chris@87: else: Chris@87: arguments = "()" Chris@87: Chris@87: if len(name+arguments) > maxwidth: Chris@87: argstr = _split_line(name, arguments, maxwidth) Chris@87: else: Chris@87: argstr = name + arguments Chris@87: Chris@87: print(" " + argstr + "\n", file=output) Chris@87: print(inspect.getdoc(object), file=output) Chris@87: Chris@87: elif hasattr(object, '__doc__'): Chris@87: print(inspect.getdoc(object), file=output) Chris@87: Chris@87: Chris@87: def source(object, output=sys.stdout): Chris@87: """ Chris@87: Print or write to a file the source code for a Numpy object. Chris@87: Chris@87: The source code is only returned for objects written in Python. Many Chris@87: functions and classes are defined in C and will therefore not return Chris@87: useful information. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: object : numpy object Chris@87: Input object. This can be any object (function, class, module, Chris@87: ...). Chris@87: output : file object, optional Chris@87: If `output` not supplied then source code is printed to screen Chris@87: (sys.stdout). File object must be created with either write 'w' or Chris@87: append 'a' modes. Chris@87: Chris@87: See Also Chris@87: -------- Chris@87: lookfor, info Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: >>> np.source(np.interp) #doctest: +SKIP Chris@87: In file: /usr/lib/python2.6/dist-packages/numpy/lib/function_base.py Chris@87: def interp(x, xp, fp, left=None, right=None): Chris@87: \"\"\".... (full docstring printed)\"\"\" Chris@87: if isinstance(x, (float, int, number)): Chris@87: return compiled_interp([x], xp, fp, left, right).item() Chris@87: else: Chris@87: return compiled_interp(x, xp, fp, left, right) Chris@87: Chris@87: The source code is only returned for objects written in Python. Chris@87: Chris@87: >>> np.source(np.array) #doctest: +SKIP Chris@87: Not available for this object. Chris@87: Chris@87: """ Chris@87: # Local import to speed up numpy's import time. Chris@87: import inspect Chris@87: try: Chris@87: print("In file: %s\n" % inspect.getsourcefile(object), file=output) Chris@87: print(inspect.getsource(object), file=output) Chris@87: except: Chris@87: print("Not available for this object.", file=output) Chris@87: Chris@87: Chris@87: # Cache for lookfor: {id(module): {name: (docstring, kind, index), ...}...} Chris@87: # where kind: "func", "class", "module", "object" Chris@87: # and index: index in breadth-first namespace traversal Chris@87: _lookfor_caches = {} Chris@87: Chris@87: # regexp whose match indicates that the string may contain a function Chris@87: # signature Chris@87: _function_signature_re = re.compile(r"[a-z0-9_]+\(.*[,=].*\)", re.I) Chris@87: Chris@87: def lookfor(what, module=None, import_modules=True, regenerate=False, Chris@87: output=None): Chris@87: """ Chris@87: Do a keyword search on docstrings. Chris@87: Chris@87: A list of of objects that matched the search is displayed, Chris@87: sorted by relevance. All given keywords need to be found in the Chris@87: docstring for it to be returned as a result, but the order does Chris@87: not matter. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: what : str Chris@87: String containing words to look for. Chris@87: module : str or list, optional Chris@87: Name of module(s) whose docstrings to go through. Chris@87: import_modules : bool, optional Chris@87: Whether to import sub-modules in packages. Default is True. Chris@87: regenerate : bool, optional Chris@87: Whether to re-generate the docstring cache. Default is False. Chris@87: output : file-like, optional Chris@87: File-like object to write the output to. If omitted, use a pager. Chris@87: Chris@87: See Also Chris@87: -------- Chris@87: source, info Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: Relevance is determined only roughly, by checking if the keywords occur Chris@87: in the function name, at the start of a docstring, etc. Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: >>> np.lookfor('binary representation') Chris@87: Search results for 'binary representation' Chris@87: ------------------------------------------ Chris@87: numpy.binary_repr Chris@87: Return the binary representation of the input number as a string. Chris@87: numpy.core.setup_common.long_double_representation Chris@87: Given a binary dump as given by GNU od -b, look for long double Chris@87: numpy.base_repr Chris@87: Return a string representation of a number in the given base system. Chris@87: ... Chris@87: Chris@87: """ Chris@87: import pydoc Chris@87: Chris@87: # Cache Chris@87: cache = _lookfor_generate_cache(module, import_modules, regenerate) Chris@87: Chris@87: # Search Chris@87: # XXX: maybe using a real stemming search engine would be better? Chris@87: found = [] Chris@87: whats = str(what).lower().split() Chris@87: if not whats: Chris@87: return Chris@87: Chris@87: for name, (docstring, kind, index) in cache.items(): Chris@87: if kind in ('module', 'object'): Chris@87: # don't show modules or objects Chris@87: continue Chris@87: ok = True Chris@87: doc = docstring.lower() Chris@87: for w in whats: Chris@87: if w not in doc: Chris@87: ok = False Chris@87: break Chris@87: if ok: Chris@87: found.append(name) Chris@87: Chris@87: # Relevance sort Chris@87: # XXX: this is full Harrison-Stetson heuristics now, Chris@87: # XXX: it probably could be improved Chris@87: Chris@87: kind_relevance = {'func': 1000, 'class': 1000, Chris@87: 'module': -1000, 'object': -1000} Chris@87: Chris@87: def relevance(name, docstr, kind, index): Chris@87: r = 0 Chris@87: # do the keywords occur within the start of the docstring? Chris@87: first_doc = "\n".join(docstr.lower().strip().split("\n")[:3]) Chris@87: r += sum([200 for w in whats if w in first_doc]) Chris@87: # do the keywords occur in the function name? Chris@87: r += sum([30 for w in whats if w in name]) Chris@87: # is the full name long? Chris@87: r += -len(name) * 5 Chris@87: # is the object of bad type? Chris@87: r += kind_relevance.get(kind, -1000) Chris@87: # is the object deep in namespace hierarchy? Chris@87: r += -name.count('.') * 10 Chris@87: r += max(-index / 100, -100) Chris@87: return r Chris@87: Chris@87: def relevance_value(a): Chris@87: return relevance(a, *cache[a]) Chris@87: found.sort(key=relevance_value) Chris@87: Chris@87: # Pretty-print Chris@87: s = "Search results for '%s'" % (' '.join(whats)) Chris@87: help_text = [s, "-"*len(s)] Chris@87: for name in found[::-1]: Chris@87: doc, kind, ix = cache[name] Chris@87: Chris@87: doclines = [line.strip() for line in doc.strip().split("\n") Chris@87: if line.strip()] Chris@87: Chris@87: # find a suitable short description Chris@87: try: Chris@87: first_doc = doclines[0].strip() Chris@87: if _function_signature_re.search(first_doc): Chris@87: first_doc = doclines[1].strip() Chris@87: except IndexError: Chris@87: first_doc = "" Chris@87: help_text.append("%s\n %s" % (name, first_doc)) Chris@87: Chris@87: if not found: Chris@87: help_text.append("Nothing found.") Chris@87: Chris@87: # Output Chris@87: if output is not None: Chris@87: output.write("\n".join(help_text)) Chris@87: elif len(help_text) > 10: Chris@87: pager = pydoc.getpager() Chris@87: pager("\n".join(help_text)) Chris@87: else: Chris@87: print("\n".join(help_text)) Chris@87: Chris@87: def _lookfor_generate_cache(module, import_modules, regenerate): Chris@87: """ Chris@87: Generate docstring cache for given module. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: module : str, None, module Chris@87: Module for which to generate docstring cache Chris@87: import_modules : bool Chris@87: Whether to import sub-modules in packages. Chris@87: regenerate : bool Chris@87: Re-generate the docstring cache Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: cache : dict {obj_full_name: (docstring, kind, index), ...} Chris@87: Docstring cache for the module, either cached one (regenerate=False) Chris@87: or newly generated. Chris@87: Chris@87: """ Chris@87: global _lookfor_caches Chris@87: # Local import to speed up numpy's import time. Chris@87: import inspect Chris@87: Chris@87: if sys.version_info[0] >= 3: Chris@87: # In Python3 stderr, stdout are text files. Chris@87: from io import StringIO Chris@87: else: Chris@87: from StringIO import StringIO Chris@87: Chris@87: if module is None: Chris@87: module = "numpy" Chris@87: Chris@87: if isinstance(module, str): Chris@87: try: Chris@87: __import__(module) Chris@87: except ImportError: Chris@87: return {} Chris@87: module = sys.modules[module] Chris@87: elif isinstance(module, list) or isinstance(module, tuple): Chris@87: cache = {} Chris@87: for mod in module: Chris@87: cache.update(_lookfor_generate_cache(mod, import_modules, Chris@87: regenerate)) Chris@87: return cache Chris@87: Chris@87: if id(module) in _lookfor_caches and not regenerate: Chris@87: return _lookfor_caches[id(module)] Chris@87: Chris@87: # walk items and collect docstrings Chris@87: cache = {} Chris@87: _lookfor_caches[id(module)] = cache Chris@87: seen = {} Chris@87: index = 0 Chris@87: stack = [(module.__name__, module)] Chris@87: while stack: Chris@87: name, item = stack.pop(0) Chris@87: if id(item) in seen: Chris@87: continue Chris@87: seen[id(item)] = True Chris@87: Chris@87: index += 1 Chris@87: kind = "object" Chris@87: Chris@87: if inspect.ismodule(item): Chris@87: kind = "module" Chris@87: try: Chris@87: _all = item.__all__ Chris@87: except AttributeError: Chris@87: _all = None Chris@87: Chris@87: # import sub-packages Chris@87: if import_modules and hasattr(item, '__path__'): Chris@87: for pth in item.__path__: Chris@87: for mod_path in os.listdir(pth): Chris@87: this_py = os.path.join(pth, mod_path) Chris@87: init_py = os.path.join(pth, mod_path, '__init__.py') Chris@87: if (os.path.isfile(this_py) and Chris@87: mod_path.endswith('.py')): Chris@87: to_import = mod_path[:-3] Chris@87: elif os.path.isfile(init_py): Chris@87: to_import = mod_path Chris@87: else: Chris@87: continue Chris@87: if to_import == '__init__': Chris@87: continue Chris@87: Chris@87: try: Chris@87: # Catch SystemExit, too Chris@87: base_exc = BaseException Chris@87: except NameError: Chris@87: # Python 2.4 doesn't have BaseException Chris@87: base_exc = Exception Chris@87: Chris@87: try: Chris@87: old_stdout = sys.stdout Chris@87: old_stderr = sys.stderr Chris@87: try: Chris@87: sys.stdout = StringIO() Chris@87: sys.stderr = StringIO() Chris@87: __import__("%s.%s" % (name, to_import)) Chris@87: finally: Chris@87: sys.stdout = old_stdout Chris@87: sys.stderr = old_stderr Chris@87: except base_exc: Chris@87: continue Chris@87: Chris@87: for n, v in _getmembers(item): Chris@87: try: Chris@87: item_name = getattr(v, '__name__', "%s.%s" % (name, n)) Chris@87: mod_name = getattr(v, '__module__', None) Chris@87: except NameError: Chris@87: # ref. SWIG's global cvars Chris@87: # NameError: Unknown C global variable Chris@87: item_name = "%s.%s" % (name, n) Chris@87: mod_name = None Chris@87: if '.' not in item_name and mod_name: Chris@87: item_name = "%s.%s" % (mod_name, item_name) Chris@87: Chris@87: if not item_name.startswith(name + '.'): Chris@87: # don't crawl "foreign" objects Chris@87: if isinstance(v, ufunc): Chris@87: # ... unless they are ufuncs Chris@87: pass Chris@87: else: Chris@87: continue Chris@87: elif not (inspect.ismodule(v) or _all is None or n in _all): Chris@87: continue Chris@87: stack.append(("%s.%s" % (name, n), v)) Chris@87: elif inspect.isclass(item): Chris@87: kind = "class" Chris@87: for n, v in _getmembers(item): Chris@87: stack.append(("%s.%s" % (name, n), v)) Chris@87: elif hasattr(item, "__call__"): Chris@87: kind = "func" Chris@87: Chris@87: try: Chris@87: doc = inspect.getdoc(item) Chris@87: except NameError: Chris@87: # ref SWIG's NameError: Unknown C global variable Chris@87: doc = None Chris@87: if doc is not None: Chris@87: cache[name] = (doc, kind, index) Chris@87: Chris@87: return cache Chris@87: Chris@87: def _getmembers(item): Chris@87: import inspect Chris@87: try: Chris@87: members = inspect.getmembers(item) Chris@87: except AttributeError: Chris@87: members = [(x, getattr(item, x)) for x in dir(item) Chris@87: if hasattr(item, x)] Chris@87: return members Chris@87: Chris@87: #----------------------------------------------------------------------------- Chris@87: Chris@87: # The following SafeEval class and company are adapted from Michael Spencer's Chris@87: # ASPN Python Cookbook recipe: Chris@87: # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/364469 Chris@87: # Accordingly it is mostly Copyright 2006 by Michael Spencer. Chris@87: # The recipe, like most of the other ASPN Python Cookbook recipes was made Chris@87: # available under the Python license. Chris@87: # http://www.python.org/license Chris@87: Chris@87: # It has been modified to: Chris@87: # * handle unary -/+ Chris@87: # * support True/False/None Chris@87: # * raise SyntaxError instead of a custom exception. Chris@87: Chris@87: class SafeEval(object): Chris@87: """ Chris@87: Object to evaluate constant string expressions. Chris@87: Chris@87: This includes strings with lists, dicts and tuples using the abstract Chris@87: syntax tree created by ``compiler.parse``. Chris@87: Chris@87: For an example of usage, see `safe_eval`. Chris@87: Chris@87: See Also Chris@87: -------- Chris@87: safe_eval Chris@87: Chris@87: """ Chris@87: Chris@87: if sys.version_info[0] < 3: Chris@87: def visit(self, node, **kw): Chris@87: cls = node.__class__ Chris@87: meth = getattr(self, 'visit'+cls.__name__, self.default) Chris@87: return meth(node, **kw) Chris@87: Chris@87: def default(self, node, **kw): Chris@87: raise SyntaxError("Unsupported source construct: %s" Chris@87: % node.__class__) Chris@87: Chris@87: def visitExpression(self, node, **kw): Chris@87: for child in node.getChildNodes(): Chris@87: return self.visit(child, **kw) Chris@87: Chris@87: def visitConst(self, node, **kw): Chris@87: return node.value Chris@87: Chris@87: def visitDict(self, node,**kw): Chris@87: return dict( Chris@87: [(self.visit(k), self.visit(v)) for k, v in node.items] Chris@87: ) Chris@87: Chris@87: def visitTuple(self, node, **kw): Chris@87: return tuple([self.visit(i) for i in node.nodes]) Chris@87: Chris@87: def visitList(self, node, **kw): Chris@87: return [self.visit(i) for i in node.nodes] Chris@87: Chris@87: def visitUnaryAdd(self, node, **kw): Chris@87: return +self.visit(node.getChildNodes()[0]) Chris@87: Chris@87: def visitUnarySub(self, node, **kw): Chris@87: return -self.visit(node.getChildNodes()[0]) Chris@87: Chris@87: def visitName(self, node, **kw): Chris@87: if node.name == 'False': Chris@87: return False Chris@87: elif node.name == 'True': Chris@87: return True Chris@87: elif node.name == 'None': Chris@87: return None Chris@87: else: Chris@87: raise SyntaxError("Unknown name: %s" % node.name) Chris@87: else: Chris@87: Chris@87: def visit(self, node): Chris@87: cls = node.__class__ Chris@87: meth = getattr(self, 'visit' + cls.__name__, self.default) Chris@87: return meth(node) Chris@87: Chris@87: def default(self, node): Chris@87: raise SyntaxError("Unsupported source construct: %s" Chris@87: % node.__class__) Chris@87: Chris@87: def visitExpression(self, node): Chris@87: return self.visit(node.body) Chris@87: Chris@87: def visitNum(self, node): Chris@87: return node.n Chris@87: Chris@87: def visitStr(self, node): Chris@87: return node.s Chris@87: Chris@87: def visitBytes(self, node): Chris@87: return node.s Chris@87: Chris@87: def visitDict(self, node,**kw): Chris@87: return dict([(self.visit(k), self.visit(v)) Chris@87: for k, v in zip(node.keys, node.values)]) Chris@87: Chris@87: def visitTuple(self, node): Chris@87: return tuple([self.visit(i) for i in node.elts]) Chris@87: Chris@87: def visitList(self, node): Chris@87: return [self.visit(i) for i in node.elts] Chris@87: Chris@87: def visitUnaryOp(self, node): Chris@87: import ast Chris@87: if isinstance(node.op, ast.UAdd): Chris@87: return +self.visit(node.operand) Chris@87: elif isinstance(node.op, ast.USub): Chris@87: return -self.visit(node.operand) Chris@87: else: Chris@87: raise SyntaxError("Unknown unary op: %r" % node.op) Chris@87: Chris@87: def visitName(self, node): Chris@87: if node.id == 'False': Chris@87: return False Chris@87: elif node.id == 'True': Chris@87: return True Chris@87: elif node.id == 'None': Chris@87: return None Chris@87: else: Chris@87: raise SyntaxError("Unknown name: %s" % node.id) Chris@87: Chris@87: def visitNameConstant(self, node): Chris@87: return node.value Chris@87: Chris@87: def safe_eval(source): Chris@87: """ Chris@87: Protected string evaluation. Chris@87: Chris@87: Evaluate a string containing a Python literal expression without Chris@87: allowing the execution of arbitrary non-literal code. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: source : str Chris@87: The string to evaluate. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: obj : object Chris@87: The result of evaluating `source`. Chris@87: Chris@87: Raises Chris@87: ------ Chris@87: SyntaxError Chris@87: If the code has invalid Python syntax, or if it contains Chris@87: non-literal code. Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: >>> np.safe_eval('1') Chris@87: 1 Chris@87: >>> np.safe_eval('[1, 2, 3]') Chris@87: [1, 2, 3] Chris@87: >>> np.safe_eval('{"foo": ("bar", 10.0)}') Chris@87: {'foo': ('bar', 10.0)} Chris@87: Chris@87: >>> np.safe_eval('import os') Chris@87: Traceback (most recent call last): Chris@87: ... Chris@87: SyntaxError: invalid syntax Chris@87: Chris@87: >>> np.safe_eval('open("/home/user/.ssh/id_dsa").read()') Chris@87: Traceback (most recent call last): Chris@87: ... Chris@87: SyntaxError: Unsupported source construct: compiler.ast.CallFunc Chris@87: Chris@87: """ Chris@87: # Local imports to speed up numpy's import time. Chris@87: import warnings Chris@87: Chris@87: with warnings.catch_warnings(): Chris@87: # compiler package is deprecated for 3.x, which is already solved Chris@87: # here Chris@87: warnings.simplefilter('ignore', DeprecationWarning) Chris@87: try: Chris@87: import compiler Chris@87: except ImportError: Chris@87: import ast as compiler Chris@87: Chris@87: walker = SafeEval() Chris@87: try: Chris@87: ast = compiler.parse(source, mode="eval") Chris@87: except SyntaxError: Chris@87: raise Chris@87: try: Chris@87: return walker.visit(ast) Chris@87: except SyntaxError: Chris@87: raise Chris@87: Chris@87: #-----------------------------------------------------------------------------