| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | """ |
| | Extensions to python's 'inspect' module, which can be used |
| | to retrieve information from live python objects. The methods |
| | defined in this module are augmented to facilitate access to |
| | source code of interactively defined functions and classes, |
| | as well as provide access to source code for objects defined |
| | in a file. |
| | """ |
| |
|
| | __all__ = ['findsource', 'getsourcelines', 'getsource', 'indent', 'outdent', \ |
| | '_wrap', 'dumpsource', 'getname', '_namespace', 'getimport', \ |
| | '_importable', 'importable','isdynamic', 'isfrommain'] |
| |
|
| | import linecache |
| | import re |
| | from inspect import (getblock, getfile, getmodule, getsourcefile, indentsize, |
| | isbuiltin, isclass, iscode, isframe, isfunction, ismethod, |
| | ismodule, istraceback) |
| | from tokenize import TokenError |
| |
|
| | from ._dill import IS_IPYTHON |
| |
|
| |
|
| | def isfrommain(obj): |
| | "check if object was built in __main__" |
| | module = getmodule(obj) |
| | if module and module.__name__ == '__main__': |
| | return True |
| | return False |
| |
|
| |
|
| | def isdynamic(obj): |
| | "check if object was built in the interpreter" |
| | try: file = getfile(obj) |
| | except TypeError: file = None |
| | if file == '<stdin>' and isfrommain(obj): |
| | return True |
| | return False |
| |
|
| |
|
| | def _matchlambda(func, line): |
| | """check if lambda object 'func' matches raw line of code 'line'""" |
| | from .detect import code as getcode |
| | from .detect import freevars, globalvars, varnames |
| | dummy = lambda : '__this_is_a_big_dummy_function__' |
| | |
| | lhs,rhs = line.split('lambda ',1)[-1].split(":", 1) |
| | try: |
| | _ = eval("lambda %s : %s" % (lhs,rhs), globals(),locals()) |
| | except Exception: _ = dummy |
| | |
| | _, code = getcode(_).co_code, getcode(func).co_code |
| | |
| | _f = [line.count(i) for i in freevars(func).keys()] |
| | if not _f: |
| | |
| | if _ == code: return True |
| | return False |
| | |
| | if not all(_f): return False |
| | |
| | _f = varnames(func) |
| | _f = [line.count(i) for i in _f[0]+_f[1]] |
| | if _f and not all(_f): return False |
| | _f = [line.count(i) for i in globalvars(func).keys()] |
| | if _f and not all(_f): return False |
| | |
| | if (line.count('lambda ') > 1) and (lhs in freevars(func).keys()): |
| | _lhs,_rhs = rhs.split('lambda ',1)[-1].split(":",1) |
| | try: |
| | _f = eval("lambda %s : %s" % (_lhs,_rhs), globals(),locals()) |
| | except Exception: _f = dummy |
| | |
| | _, code = getcode(_f).co_code, getcode(func).co_code |
| | if len(_) != len(code): return False |
| | |
| | _ = set((i,j) for (i,j) in zip(_,code) if i != j) |
| | if len(_) != 1: return False |
| | return True |
| | |
| | if not indentsize(line): return False |
| | |
| | |
| | _ = _.split(_[0]) |
| | _f = code.split(code[0]) |
| | |
| | _ = dict(re.match(r'([\W\D\S])(.*)', _[i]).groups() for i in range(1,len(_))) |
| | _f = dict(re.match(r'([\W\D\S])(.*)', _f[i]).groups() for i in range(1,len(_f))) |
| | if (_.keys() == _f.keys()) and (sorted(_.values()) == sorted(_f.values())): |
| | return True |
| | return False |
| |
|
| |
|
| | def findsource(object): |
| | """Return the entire source file and starting line number for an object. |
| | For interactively-defined objects, the 'file' is the interpreter's history. |
| | |
| | The argument may be a module, class, method, function, traceback, frame, |
| | or code object. The source code is returned as a list of all the lines |
| | in the file and the line number indexes a line in that list. An IOError |
| | is raised if the source code cannot be retrieved, while a TypeError is |
| | raised for objects where the source code is unavailable (e.g. builtins).""" |
| |
|
| | module = getmodule(object) |
| | try: file = getfile(module) |
| | except TypeError: file = None |
| | is_module_main = (module and module.__name__ == '__main__' and not file) |
| | if IS_IPYTHON and is_module_main: |
| | |
| | try: |
| | file = getfile(object) |
| | sourcefile = getsourcefile(object) |
| | except TypeError: |
| | if isclass(object): |
| | for object_method in filter(isfunction, object.__dict__.values()): |
| | |
| | file_candidate = getfile(object_method) |
| | if not file_candidate.startswith('<ipython-input-'): |
| | continue |
| | file = file_candidate |
| | sourcefile = getsourcefile(object_method) |
| | break |
| | if file: |
| | lines = linecache.getlines(file) |
| | else: |
| | |
| | history = '\n'.join(get_ipython().history_manager.input_hist_parsed) |
| | lines = [line + '\n' for line in history.splitlines()] |
| | |
| | elif is_module_main: |
| | try: |
| | import readline |
| | err = '' |
| | except ImportError: |
| | import sys |
| | err = sys.exc_info()[1].args[0] |
| | if sys.platform[:3] == 'win': |
| | err += ", please install 'pyreadline'" |
| | if err: |
| | raise IOError(err) |
| | lbuf = readline.get_current_history_length() |
| | lines = [readline.get_history_item(i)+'\n' for i in range(1,lbuf+1)] |
| | else: |
| | try: |
| | if not isclass(object) and isclass(type(object)): |
| | file = getfile(module) |
| | sourcefile = getsourcefile(module) |
| | else: |
| | file = getfile(object) |
| | sourcefile = getsourcefile(object) |
| | except (TypeError, AttributeError): |
| | file = getfile(object) |
| | sourcefile = getsourcefile(object) |
| | if not sourcefile and file[:1] + file[-1:] != '<>': |
| | raise IOError('source code not available') |
| | file = sourcefile if sourcefile else file |
| |
|
| | module = getmodule(object, file) |
| | if module: |
| | lines = linecache.getlines(file, module.__dict__) |
| | else: |
| | lines = linecache.getlines(file) |
| |
|
| | if not lines: |
| | raise IOError('could not extract source code') |
| |
|
| | |
| | if ismodule(object): |
| | return lines, 0 |
| |
|
| | |
| | name = pat1 = obj = '' |
| | pat2 = r'^(\s*@)' |
| | |
| | if ismethod(object): |
| | name = object.__name__ |
| | if name == '<lambda>': pat1 = r'(.*(?<!\w)lambda(:|\s))' |
| | else: pat1 = r'^(\s*def\s)' |
| | object = object.__func__ |
| | if isfunction(object): |
| | name = object.__name__ |
| | if name == '<lambda>': |
| | pat1 = r'(.*(?<!\w)lambda(:|\s))' |
| | obj = object |
| | else: pat1 = r'^(\s*def\s)' |
| | object = object.__code__ |
| | if istraceback(object): |
| | object = object.tb_frame |
| | if isframe(object): |
| | object = object.f_code |
| | if iscode(object): |
| | if not hasattr(object, 'co_firstlineno'): |
| | raise IOError('could not find function definition') |
| | stdin = object.co_filename == '<stdin>' |
| | if stdin: |
| | lnum = len(lines) - 1 |
| | if not pat1: pat1 = r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)' |
| | else: |
| | lnum = object.co_firstlineno - 1 |
| | pat1 = r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)' |
| | pat1 = re.compile(pat1); pat2 = re.compile(pat2) |
| | |
| | while lnum > 0: |
| | line = lines[lnum] |
| | if pat1.match(line): |
| | if not stdin: break |
| | if name == '<lambda>': |
| | if _matchlambda(obj, line): break |
| | else: |
| | if name in line: |
| | hats = 0 |
| | for _lnum in range(lnum-1,-1,-1): |
| | if pat2.match(lines[_lnum]): hats += 1 |
| | else: break |
| | lnum = lnum - hats |
| | break |
| | lnum = lnum - 1 |
| | return lines, lnum |
| |
|
| | try: |
| | if not isclass(object) and isclass(type(object)): |
| | object = object.__class__ |
| | |
| | except AttributeError: pass |
| | if isclass(object): |
| | name = object.__name__ |
| | pat = re.compile(r'^(\s*)class\s*' + name + r'\b') |
| | |
| | |
| | |
| | candidates = [] |
| | for i in range(len(lines)-1,-1,-1): |
| | match = pat.match(lines[i]) |
| | if match: |
| | |
| | if lines[i][0] == 'c': |
| | return lines, i |
| | |
| | candidates.append((match.group(1), i)) |
| | if candidates: |
| | |
| | |
| | candidates.sort() |
| | return lines, candidates[0][1] |
| | else: |
| | raise IOError('could not find class definition') |
| | raise IOError('could not find code object') |
| |
|
| |
|
| | def getblocks(object, lstrip=False, enclosing=False, locate=False): |
| | """Return a list of source lines and starting line number for an object. |
| | Interactively-defined objects refer to lines in the interpreter's history. |
| | |
| | If enclosing=True, then also return any enclosing code. |
| | If lstrip=True, ensure there is no indentation in the first line of code. |
| | If locate=True, then also return the line number for the block of code. |
| | |
| | DEPRECATED: use 'getsourcelines' instead |
| | """ |
| | lines, lnum = findsource(object) |
| |
|
| | if ismodule(object): |
| | if lstrip: lines = _outdent(lines) |
| | return ([lines], [0]) if locate is True else [lines] |
| |
|
| | |
| | indent = indentsize(lines[lnum]) |
| | block = getblock(lines[lnum:]) |
| |
|
| | if not enclosing or not indent: |
| | if lstrip: block = _outdent(block) |
| | return ([block], [lnum]) if locate is True else [block] |
| |
|
| | pat1 = r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))'; pat1 = re.compile(pat1) |
| | pat2 = r'^(\s*@)'; pat2 = re.compile(pat2) |
| | |
| | |
| | |
| |
|
| | skip = 0 |
| | line = 0 |
| | blocks = []; _lnum = [] |
| | target = ''.join(block) |
| | while line <= lnum: |
| | |
| | if pat1.match(lines[line]): |
| | if not skip: |
| | try: code = getblock(lines[line:]) |
| | except TokenError: code = [lines[line]] |
| | if indentsize(lines[line]) > indent: |
| | line += len(code) - skip |
| | elif target in ''.join(code): |
| | blocks.append(code) |
| | _lnum.append(line - skip) |
| | line += len(code) - skip |
| | else: |
| | line += 1 |
| | skip = 0 |
| | |
| | elif pat2.match(lines[line]): |
| | try: code = getblock(lines[line:]) |
| | except TokenError: code = [lines[line]] |
| | skip = 1 |
| | for _line in code[1:]: |
| | if not pat2.match(_line): break |
| | skip += 1 |
| | line += skip |
| | |
| | else: |
| | line +=1 |
| | skip = 0 |
| |
|
| | if not blocks: |
| | blocks = [block] |
| | _lnum = [lnum] |
| | if lstrip: blocks = [_outdent(block) for block in blocks] |
| | |
| | return (blocks, _lnum) if locate is True else blocks |
| |
|
| |
|
| | def getsourcelines(object, lstrip=False, enclosing=False): |
| | """Return a list of source lines and starting line number for an object. |
| | Interactively-defined objects refer to lines in the interpreter's history. |
| | |
| | The argument may be a module, class, method, function, traceback, frame, |
| | or code object. The source code is returned as a list of the lines |
| | corresponding to the object and the line number indicates where in the |
| | original source file the first line of code was found. An IOError is |
| | raised if the source code cannot be retrieved, while a TypeError is |
| | raised for objects where the source code is unavailable (e.g. builtins). |
| | |
| | If lstrip=True, ensure there is no indentation in the first line of code. |
| | If enclosing=True, then also return any enclosing code.""" |
| | code, n = getblocks(object, lstrip=lstrip, enclosing=enclosing, locate=True) |
| | return code[-1], n[-1] |
| |
|
| |
|
| | |
| | def getsource(object, alias='', lstrip=False, enclosing=False, \ |
| | force=False, builtin=False): |
| | """Return the text of the source code for an object. The source code for |
| | interactively-defined objects are extracted from the interpreter's history. |
| | |
| | The argument may be a module, class, method, function, traceback, frame, |
| | or code object. The source code is returned as a single string. An |
| | IOError is raised if the source code cannot be retrieved, while a |
| | TypeError is raised for objects where the source code is unavailable |
| | (e.g. builtins). |
| | |
| | If alias is provided, then add a line of code that renames the object. |
| | If lstrip=True, ensure there is no indentation in the first line of code. |
| | If enclosing=True, then also return any enclosing code. |
| | If force=True, catch (TypeError,IOError) and try to use import hooks. |
| | If builtin=True, force an import for any builtins |
| | """ |
| | |
| | hascode = _hascode(object) |
| | |
| | instance = _isinstance(object) |
| |
|
| | |
| | try: |
| | lines, lnum = getsourcelines(object, enclosing=enclosing) |
| | except (TypeError, IOError): |
| | if not force: |
| | raise |
| | if not getmodule(object): |
| | if not instance: return getimport(object, alias, builtin=builtin) |
| | |
| | _import = getimport(object, builtin=builtin) |
| | name = getname(object, force=True) |
| | _alias = "%s = " % alias if alias else "" |
| | if alias == name: _alias = "" |
| | return _import+_alias+"%s\n" % name |
| | else: |
| | if not instance: return getimport(object, alias, builtin=builtin) |
| | |
| | name = object.__class__.__name__ |
| | module = object.__module__ |
| | if module in ['builtins','__builtin__']: |
| | return getimport(object, alias, builtin=builtin) |
| | else: |
| | lines, lnum = ["%s = __import__('%s', fromlist=['%s']).%s\n" % (name,module,name,name)], 0 |
| | obj = eval(lines[0].lstrip(name + ' = ')) |
| | lines, lnum = getsourcelines(obj, enclosing=enclosing) |
| |
|
| | |
| | if lstrip or alias: |
| | lines = _outdent(lines) |
| |
|
| | |
| | if instance: |
| | if '(' in repr(object): lines.append('%r\n' % object) |
| | |
| | |
| | |
| | else: |
| | |
| | lines = dumpsource(object, alias='', new=force, enclose=False) |
| | lines, lnum = [line+'\n' for line in lines.split('\n')][:-1], 0 |
| | |
| |
|
| | |
| | if alias: |
| | if hascode: |
| | skip = 0 |
| | for line in lines: |
| | if not line.startswith('@'): break |
| | skip += 1 |
| | |
| | if lines[skip].lstrip().startswith('def '): |
| | if alias != object.__name__: |
| | lines.append('\n%s = %s\n' % (alias, object.__name__)) |
| | elif 'lambda ' in lines[skip]: |
| | if alias != lines[skip].split('=')[0].strip(): |
| | lines[skip] = '%s = %s' % (alias, lines[skip]) |
| | else: |
| | if alias != object.__name__: |
| | lines.append('\n%s = %s\n' % (alias, object.__name__)) |
| | else: |
| | if instance: |
| | if alias != lines[-1].split('=')[0].strip(): |
| | lines[-1] = ('%s = ' % alias) + lines[-1] |
| | else: |
| | name = getname(object, force=True) or object.__name__ |
| | if alias != name: |
| | lines.append('\n%s = %s\n' % (alias, name)) |
| | return ''.join(lines) |
| |
|
| |
|
| | def _hascode(object): |
| | '''True if object has an attribute that stores it's __code__''' |
| | return getattr(object,'__code__',None) or getattr(object,'func_code',None) |
| |
|
| | def _isinstance(object): |
| | '''True if object is a class instance type (and is not a builtin)''' |
| | if _hascode(object) or isclass(object) or ismodule(object): |
| | return False |
| | if istraceback(object) or isframe(object) or iscode(object): |
| | return False |
| | |
| | if not getmodule(object) and getmodule(type(object)).__name__ in ['numpy']: |
| | return True |
| | |
| | |
| | |
| | _types = ('<class ',"<type 'instance'>") |
| | if not repr(type(object)).startswith(_types): |
| | return False |
| | if not getmodule(object) or object.__module__ in ['builtins','__builtin__'] or getname(object, force=True) in ['array']: |
| | return False |
| | return True |
| |
|
| |
|
| | def _intypes(object): |
| | '''check if object is in the 'types' module''' |
| | import types |
| | |
| | if type(object) is not type(''): |
| | object = getname(object, force=True) |
| | if object == 'ellipsis': object = 'EllipsisType' |
| | return True if hasattr(types, object) else False |
| |
|
| |
|
| | def _isstring(object): |
| | '''check if object is a string-like type''' |
| | return isinstance(object, (str, bytes)) |
| |
|
| |
|
| | def indent(code, spaces=4): |
| | '''indent a block of code with whitespace (default is 4 spaces)''' |
| | indent = indentsize(code) |
| | from numbers import Integral |
| | if isinstance(spaces, Integral): spaces = ' '*spaces |
| | |
| | nspaces = indentsize(spaces) |
| | |
| | lines = code.split('\n') |
| | |
| | |
| | for i in range(len(lines)): |
| | |
| | _indent = indentsize(lines[i]) |
| | if indent > _indent: continue |
| | lines[i] = spaces+lines[i] |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | if lines[-1].strip() == '': lines[-1] = '' |
| | return '\n'.join(lines) |
| |
|
| |
|
| | def _outdent(lines, spaces=None, all=True): |
| | '''outdent lines of code, accounting for docs and line continuations''' |
| | indent = indentsize(lines[0]) |
| | if spaces is None or spaces > indent or spaces < 0: spaces = indent |
| | for i in range(len(lines) if all else 1): |
| | |
| | _indent = indentsize(lines[i]) |
| | if spaces > _indent: _spaces = _indent |
| | else: _spaces = spaces |
| | lines[i] = lines[i][_spaces:] |
| | return lines |
| |
|
| | def outdent(code, spaces=None, all=True): |
| | '''outdent a block of code (default is to strip all leading whitespace)''' |
| | indent = indentsize(code) |
| | if spaces is None or spaces > indent or spaces < 0: spaces = indent |
| | |
| | if not all: return code[spaces:] |
| | return '\n'.join(_outdent(code.split('\n'), spaces=spaces, all=all)) |
| |
|
| |
|
| | |
| | __globals__ = globals() |
| | __locals__ = locals() |
| | def _wrap(f): |
| | """ encapsulate a function and it's __import__ """ |
| | def func(*args, **kwds): |
| | try: |
| | |
| | exec(getimportable(f, alias='_'), __globals__, __locals__) |
| | except Exception: |
| | raise ImportError('cannot import name ' + f.__name__) |
| | return _(*args, **kwds) |
| | func.__name__ = f.__name__ |
| | func.__doc__ = f.__doc__ |
| | return func |
| |
|
| |
|
| | def _enclose(object, alias=''): |
| | """create a function enclosure around the source of some object""" |
| | |
| | dummy = '__this_is_a_big_dummy_enclosing_function__' |
| | stub = '__this_is_a_stub_variable__' |
| | code = 'def %s():\n' % dummy |
| | code += indent(getsource(object, alias=stub, lstrip=True, force=True)) |
| | code += indent('return %s\n' % stub) |
| | if alias: code += '%s = ' % alias |
| | code += '%s(); del %s\n' % (dummy, dummy) |
| | |
| | return code |
| |
|
| |
|
| | def dumpsource(object, alias='', new=False, enclose=True): |
| | """'dump to source', where the code includes a pickled object. |
| | |
| | If new=True and object is a class instance, then create a new |
| | instance using the unpacked class source code. If enclose, then |
| | create the object inside a function enclosure (thus minimizing |
| | any global namespace pollution). |
| | """ |
| | from dill import dumps |
| | pik = repr(dumps(object)) |
| | code = 'import dill\n' |
| | if enclose: |
| | stub = '__this_is_a_stub_variable__' |
| | pre = '%s = ' % stub |
| | new = False |
| | else: |
| | stub = alias |
| | pre = '%s = ' % stub if alias else alias |
| |
|
| | |
| | if not new or not _isinstance(object): |
| | code += pre + 'dill.loads(%s)\n' % pik |
| | else: |
| | code += getsource(object.__class__, alias='', lstrip=True, force=True) |
| | mod = repr(object.__module__) |
| | code += pre + 'dill.loads(%s.replace(b%s,bytes(__name__,"UTF-8")))\n' % (pik,mod) |
| | |
| |
|
| | if enclose: |
| | |
| | dummy = '__this_is_a_big_dummy_object__' |
| | dummy = _enclose(dummy, alias=alias) |
| | |
| | dummy = dummy.split('\n') |
| | code = dummy[0]+'\n' + indent(code) + '\n'.join(dummy[-3:]) |
| |
|
| | return code |
| |
|
| |
|
| | def getname(obj, force=False, fqn=False): |
| | """get the name of the object. for lambdas, get the name of the pointer """ |
| | if fqn: return '.'.join(_namespace(obj)) |
| | module = getmodule(obj) |
| | if not module: |
| | if not force: return None |
| | |
| | if hasattr(obj, 'dtype') and not obj.shape: |
| | return getname(obj.__class__) + "(" + repr(obj.tolist()) + ")" |
| | return repr(obj) |
| | try: |
| | |
| | |
| | name = obj.__name__ |
| | if name == '<lambda>': |
| | return getsource(obj).split('=',1)[0].strip() |
| | |
| | if module.__name__ in ['builtins','__builtin__']: |
| | if name == 'ellipsis': name = 'EllipsisType' |
| | return name |
| | except AttributeError: |
| | if not force: return None |
| | name = repr(obj) |
| | if name.startswith('<'): |
| | return None |
| | return name |
| |
|
| |
|
| | def _namespace(obj): |
| | """_namespace(obj); return namespace hierarchy (as a list of names) |
| | for the given object. For an instance, find the class hierarchy. |
| | |
| | For example: |
| | |
| | >>> from functools import partial |
| | >>> p = partial(int, base=2) |
| | >>> _namespace(p) |
| | [\'functools\', \'partial\'] |
| | """ |
| | |
| | |
| | try: |
| | module = qual = str(getmodule(obj)).split()[1].strip('>').strip('"').strip("'") |
| | qual = qual.split('.') |
| | if ismodule(obj): |
| | return qual |
| | |
| | name = getname(obj) or obj.__name__ |
| | |
| | if module in ['builtins','__builtin__']: |
| | if _intypes(name): return ['types'] + [name] |
| | return qual + [name] |
| | except Exception: pass |
| | |
| | if str(obj) in ['inf','nan','Inf','NaN']: |
| | return ['numpy'] + [str(obj)] |
| | |
| | module = getattr(obj.__class__, '__module__', None) |
| | qual = str(obj.__class__) |
| | try: qual = qual[qual.index("'")+1:-2] |
| | except ValueError: pass |
| | qual = qual.split(".") |
| | if module in ['builtins','__builtin__']: |
| | |
| | if qual[-1] == 'ellipsis': qual[-1] = 'EllipsisType' |
| | if _intypes(qual[-1]): module = 'types' |
| | qual = [module] + qual |
| | return qual |
| |
|
| |
|
| | |
| | def _getimport(head, tail, alias='', verify=True, builtin=False): |
| | """helper to build a likely import string from head and tail of namespace. |
| | ('head','tail') are used in the following context: "from head import tail" |
| | |
| | If verify=True, then test the import string before returning it. |
| | If builtin=True, then force an import for builtins where possible. |
| | If alias is provided, then rename the object on import. |
| | """ |
| | |
| | if tail in ['Ellipsis', 'NotImplemented'] and head in ['types']: |
| | head = len.__module__ |
| | elif tail in ['None'] and head in ['types']: |
| | _alias = '%s = ' % alias if alias else '' |
| | if alias == tail: _alias = '' |
| | return _alias+'%s\n' % tail |
| | |
| | |
| | if head in ['builtins','__builtin__']: |
| | |
| | if tail == 'ellipsis': tail = 'EllipsisType' |
| | if _intypes(tail): head = 'types' |
| | elif not builtin: |
| | _alias = '%s = ' % alias if alias else '' |
| | if alias == tail: _alias = '' |
| | return _alias+'%s\n' % tail |
| | else: pass |
| | |
| | if not head: _str = "import %s" % tail |
| | else: _str = "from %s import %s" % (head, tail) |
| | _alias = " as %s\n" % alias if alias else "\n" |
| | if alias == tail: _alias = "\n" |
| | _str += _alias |
| | |
| | |
| | |
| | if verify and not head.startswith('dill.'): |
| | |
| | try: exec(_str) |
| | except ImportError: |
| | _head = head.rsplit(".",1)[0] |
| | if not _head: raise |
| | if _head != head: |
| | _str = _getimport(_head, tail, alias, verify) |
| | return _str |
| |
|
| |
|
| | |
| | |
| | def getimport(obj, alias='', verify=True, builtin=False, enclosing=False): |
| | """get the likely import string for the given object |
| | |
| | obj is the object to inspect |
| | If verify=True, then test the import string before returning it. |
| | If builtin=True, then force an import for builtins where possible. |
| | If enclosing=True, get the import for the outermost enclosing callable. |
| | If alias is provided, then rename the object on import. |
| | """ |
| | if enclosing: |
| | from .detect import outermost |
| | _obj = outermost(obj) |
| | obj = _obj if _obj else obj |
| | |
| | qual = _namespace(obj) |
| | head = '.'.join(qual[:-1]) |
| | tail = qual[-1] |
| | |
| | try: |
| | name = repr(obj).split('<',1)[1].split('>',1)[1] |
| | name = None |
| | except Exception: |
| | if head in ['builtins','__builtin__']: |
| | name = repr(obj) |
| | elif _isinstance(obj): |
| | name = getname(obj, force=True).split('(')[0] |
| | else: |
| | name = repr(obj).split('(')[0] |
| | |
| | |
| | if name: |
| | try: return _getimport(head, name, alias, verify, builtin) |
| | except ImportError: pass |
| | except SyntaxError: |
| | if head in ['builtins','__builtin__']: |
| | _alias = '%s = ' % alias if alias else '' |
| | if alias == name: _alias = '' |
| | return _alias+'%s\n' % name |
| | else: pass |
| | try: |
| | |
| | |
| | return _getimport(head, tail, alias, verify, builtin) |
| | except ImportError: |
| | raise |
| | except SyntaxError: |
| | if head in ['builtins','__builtin__']: |
| | _alias = '%s = ' % alias if alias else '' |
| | if alias == tail: _alias = '' |
| | return _alias+'%s\n' % tail |
| | raise |
| |
|
| |
|
| | def _importable(obj, alias='', source=None, enclosing=False, force=True, \ |
| | builtin=True, lstrip=True): |
| | """get an import string (or the source code) for the given object |
| | |
| | This function will attempt to discover the name of the object, or the repr |
| | of the object, or the source code for the object. To attempt to force |
| | discovery of the source code, use source=True, to attempt to force the |
| | use of an import, use source=False; otherwise an import will be sought |
| | for objects not defined in __main__. The intent is to build a string |
| | that can be imported from a python file. obj is the object to inspect. |
| | If alias is provided, then rename the object with the given alias. |
| | |
| | If source=True, use these options: |
| | If enclosing=True, then also return any enclosing code. |
| | If force=True, catch (TypeError,IOError) and try to use import hooks. |
| | If lstrip=True, ensure there is no indentation in the first line of code. |
| | |
| | If source=False, use these options: |
| | If enclosing=True, get the import for the outermost enclosing callable. |
| | If force=True, then don't test the import string before returning it. |
| | If builtin=True, then force an import for builtins where possible. |
| | """ |
| | if source is None: |
| | source = True if isfrommain(obj) else False |
| | if source: |
| | try: |
| | return getsource(obj, alias, enclosing=enclosing, \ |
| | force=force, lstrip=lstrip, builtin=builtin) |
| | except Exception: pass |
| | try: |
| | if not _isinstance(obj): |
| | return getimport(obj, alias, enclosing=enclosing, \ |
| | verify=(not force), builtin=builtin) |
| | |
| | _import = getimport(obj, enclosing=enclosing, \ |
| | verify=(not force), builtin=builtin) |
| | name = getname(obj, force=True) |
| | if not name: |
| | raise AttributeError("object has no atribute '__name__'") |
| | _alias = "%s = " % alias if alias else "" |
| | if alias == name: _alias = "" |
| | return _import+_alias+"%s\n" % name |
| |
|
| | except Exception: pass |
| | if not source: |
| | try: |
| | return getsource(obj, alias, enclosing=enclosing, \ |
| | force=force, lstrip=lstrip, builtin=builtin) |
| | except Exception: pass |
| | |
| | |
| | |
| | obj = getname(obj, force=force) |
| | |
| | if not obj or obj.startswith('<'): |
| | raise AttributeError("object has no atribute '__name__'") |
| | _alias = '%s = ' % alias if alias else '' |
| | if alias == obj: _alias = '' |
| | return _alias+'%s\n' % obj |
| | |
| | |
| |
|
| | def _closuredimport(func, alias='', builtin=False): |
| | """get import for closured objects; return a dict of 'name' and 'import'""" |
| | import re |
| | from .detect import freevars, outermost |
| | free_vars = freevars(func) |
| | func_vars = {} |
| | |
| | for name,obj in list(free_vars.items()): |
| | if not isfunction(obj): continue |
| | |
| | fobj = free_vars.pop(name) |
| | src = getsource(fobj) |
| | if src.lstrip().startswith('@'): |
| | src = getimport(fobj, alias=alias, builtin=builtin) |
| | else: |
| | encl = outermost(func) |
| | |
| | pat = r'.*[\w\s]=\s*'+getname(encl)+r'\('+getname(fobj) |
| | mod = getname(getmodule(encl)) |
| | |
| | lines,_ = findsource(encl) |
| | candidate = [line for line in lines if getname(encl) in line and \ |
| | re.match(pat, line)] |
| | if not candidate: |
| | mod = getname(getmodule(fobj)) |
| | |
| | lines,_ = findsource(fobj) |
| | candidate = [line for line in lines \ |
| | if getname(fobj) in line and re.match(pat, line)] |
| | if not len(candidate): raise TypeError('import could not be found') |
| | candidate = candidate[-1] |
| | name = candidate.split('=',1)[0].split()[-1].strip() |
| | src = _getimport(mod, name, alias=alias, builtin=builtin) |
| | func_vars[name] = src |
| | if not func_vars: |
| | name = outermost(func) |
| | mod = getname(getmodule(name)) |
| | if not mod or name is func: |
| | name = getname(func, force=True) |
| | src = getimport(func, alias=alias, builtin=builtin) |
| | else: |
| | lines,_ = findsource(name) |
| | |
| | candidate = [line for line in lines if getname(name) in line and \ |
| | re.match(r'.*[\w\s]=\s*'+getname(name)+r'\(', line)] |
| | if not len(candidate): raise TypeError('import could not be found') |
| | candidate = candidate[-1] |
| | name = candidate.split('=',1)[0].split()[-1].strip() |
| | src = _getimport(mod, name, alias=alias, builtin=builtin) |
| | func_vars[name] = src |
| | return func_vars |
| |
|
| | |
| | def _closuredsource(func, alias=''): |
| | """get source code for closured objects; return a dict of 'name' |
| | and 'code blocks'""" |
| | |
| | |
| | |
| | |
| | from .detect import freevars |
| | free_vars = freevars(func) |
| | func_vars = {} |
| | |
| | for name,obj in list(free_vars.items()): |
| | if not isfunction(obj): |
| | |
| | free_vars[name] = getsource(obj, force=True, alias=name) |
| | continue |
| | |
| | fobj = free_vars.pop(name) |
| | src = getsource(fobj, alias) |
| | |
| | if not src.lstrip().startswith('@'): |
| | src = importable(fobj,alias=name) |
| | org = getsource(func, alias, enclosing=False, lstrip=True) |
| | src = (src, org) |
| | else: |
| | org = getsource(func, enclosing=True, lstrip=False) |
| | src = importable(fobj, alias, source=True) |
| | src = (org, src) |
| | func_vars[name] = src |
| | src = ''.join(free_vars.values()) |
| | if not func_vars: |
| | org = getsource(func, alias, force=True, enclosing=False, lstrip=True) |
| | src = (src, org) |
| | else: |
| | src = (src, None) |
| | func_vars[None] = src |
| | |
| | return func_vars |
| |
|
| | def importable(obj, alias='', source=None, builtin=True): |
| | """get an importable string (i.e. source code or the import string) |
| | for the given object, including any required objects from the enclosing |
| | and global scope |
| | |
| | This function will attempt to discover the name of the object, or the repr |
| | of the object, or the source code for the object. To attempt to force |
| | discovery of the source code, use source=True, to attempt to force the |
| | use of an import, use source=False; otherwise an import will be sought |
| | for objects not defined in __main__. The intent is to build a string |
| | that can be imported from a python file. |
| | |
| | obj is the object to inspect. If alias is provided, then rename the |
| | object with the given alias. If builtin=True, then force an import for |
| | builtins where possible. |
| | """ |
| | |
| | |
| | if source is None: |
| | source = True if isfrommain(obj) else False |
| | elif builtin and isbuiltin(obj): |
| | source = False |
| | tried_source = tried_import = False |
| | while True: |
| | if not source: |
| | try: |
| | if _isinstance(obj): |
| | return _importable(obj, alias, source=False, builtin=builtin) |
| | src = _closuredimport(obj, alias=alias, builtin=builtin) |
| | if len(src) == 0: |
| | raise NotImplementedError('not implemented') |
| | if len(src) > 1: |
| | raise NotImplementedError('not implemented') |
| | return list(src.values())[0] |
| | except Exception: |
| | if tried_source: raise |
| | tried_import = True |
| | |
| | try: |
| | src = _closuredsource(obj, alias=alias) |
| | if len(src) == 0: |
| | raise NotImplementedError('not implemented') |
| | |
| | def _code_stitcher(block): |
| | "stitch together the strings in tuple 'block'" |
| | if block[0] and block[-1]: block = '\n'.join(block) |
| | elif block[0]: block = block[0] |
| | elif block[-1]: block = block[-1] |
| | else: block = '' |
| | return block |
| | |
| | _src = _code_stitcher(src.pop(None)) |
| | _src = [_src] if _src else [] |
| | |
| | for xxx in src.values(): |
| | xxx = _code_stitcher(xxx) |
| | if xxx: _src.append(xxx) |
| | |
| | if not len(_src): |
| | src = '' |
| | elif len(_src) == 1: |
| | src = _src[0] |
| | else: |
| | src = '\n'.join(_src) |
| | |
| | from .detect import globalvars |
| | obj = globalvars(obj) |
| | obj = list(getsource(_obj,name,force=True) for (name,_obj) in obj.items() if not isbuiltin(_obj)) |
| | obj = '\n'.join(obj) if obj else '' |
| | |
| | if not obj: return src |
| | if not src: return obj |
| | return obj + src |
| | except Exception: |
| | if tried_import: raise |
| | tried_source = True |
| | source = not source |
| | |
| | return |
| |
|
| |
|
| | |
| | def getimportable(obj, alias='', byname=True, explicit=False): |
| | return importable(obj,alias,source=(not byname),builtin=explicit) |
| | |
| | def likely_import(obj, passive=False, explicit=False): |
| | return getimport(obj, verify=(not passive), builtin=explicit) |
| | def _likely_import(first, last, passive=False, explicit=True): |
| | return _getimport(first, last, verify=(not passive), builtin=explicit) |
| | _get_name = getname |
| | getblocks_from_history = getblocks |
| |
|
| |
|
| |
|
| | |
| |
|