def indent(code, spaces=4): '''indent a block of code with whitespace (default is 4 spaces)''' indent = indentsize(code) if type(spaces) is int: spaces = ' '*spaces # if '\t' is provided, will indent with a tab nspaces = indentsize(spaces) # blank lines (etc) need to be ignored lines = code.split('\n') ## stq = "'''"; dtq = '"""' ## in_stq = in_dtq = False for i in range(len(lines)): #FIXME: works... but shouldn't indent 2nd+ lines of multiline doc _indent = indentsize(lines[i]) if indent > _indent: continue lines[i] = spaces+lines[i] ## #FIXME: may fail when stq and dtq in same line (depends on ordering) ## nstq, ndtq = lines[i].count(stq), lines[i].count(dtq) ## if not in_dtq and not in_stq: ## lines[i] = spaces+lines[i] # we indent ## # entering a comment block ## if nstq%2: in_stq = not in_stq ## if ndtq%2: in_dtq = not in_dtq ## # leaving a comment block ## elif in_dtq and ndtq%2: in_dtq = not in_dtq ## elif in_stq and nstq%2: in_stq = not in_stq ## else: pass 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): #FIXME: works... but shouldn't outdent 2nd+ lines of multiline doc _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 #XXX: will this delete '\n' in some cases? if not all: return code[spaces:] return '\n'.join(_outdent(code.split('\n'), spaces=spaces, all=all))
def getcomments(pyObject): """Get lines of comments immediately preceding an object's source code. Returns None when source can't be found. """ try: lines, lnum = findsource(pyObject) except (IOError, TypeError): return None if ismodule(pyObject): # Look for a comment block at the top of the file. start = 0 if lines and lines[0][:2] == '#!': start = 1 while start < len(lines) and string.strip(lines[start]) in ('', '#'): start = start + 1 if start < len(lines) and lines[start][:1] == '#': comments = [] end = start while end < len(lines) and lines[end][:1] == '#': comments.append(string.expandtabs(lines[end])) end = end + 1 return string.join(comments, '') # Look for a preceding block of comments at the same indentation. elif lnum > 0: indent = indentsize(lines[lnum]) end = lnum - 1 if end >= 0 and string.lstrip(lines[end])[:1] == '#' and \ indentsize(lines[end]) == indent: comments = [string.lstrip(string.expandtabs(lines[end]))] if end > 0: end = end - 1 comment = string.lstrip(string.expandtabs(lines[end])) while comment[:1] == '#' and indentsize(lines[end]) == indent: comments[:0] = [comment] end = end - 1 if end < 0: break comment = string.lstrip(string.expandtabs(lines[end])) while comments and string.strip(comments[0]) == '#': comments[:1] = [] while comments and string.strip(comments[-1]) == '#': comments[-1:] = [] return string.join(comments, '')
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__' # process the line (removing leading whitespace, etc) lhs, rhs = line.split('lambda ', 1)[-1].split(":", 1) #FIXME: if !1 inputs try: #FIXME: unsafe _ = eval("lambda %s : %s" % (lhs, rhs), globals(), locals()) except: _ = dummy # get code objects, for comparison _, code = getcode(_).co_code, getcode(func).co_code # check if func is in closure _f = [line.count(i) for i in freevars(func).keys()] if not _f: # not in closure # check if code matches if _ == code: return True return False # weak check on freevars if not all(_f): return False #XXX: VERY WEAK # weak check on varnames and globalvars _f = varnames(func) _f = [line.count(i) for i in _f[0] + _f[1]] if _f and not all(_f): return False #XXX: VERY WEAK _f = [line.count(i) for i in globalvars(func).keys()] if _f and not all(_f): return False #XXX: VERY WEAK # check if func is a double lambda if (line.count('lambda ') > 1) and (lhs in freevars(func).keys()): _lhs, _rhs = rhs.split('lambda ', 1)[-1].split(":", 1) #FIXME: if !1 inputs try: #FIXME: unsafe _f = eval("lambda %s : %s" % (_lhs, _rhs), globals(), locals()) except: _f = dummy # get code objects, for comparison _, code = getcode(_f).co_code, getcode(func).co_code if len(_) != len(code): return False #NOTE: should be same code same order, but except for 't' and '\x88' _ = set((i, j) for (i, j) in zip(_, code) if i != j) if len(_) != 1: return False #('t','\x88') return True # check indentsize if not indentsize(line): return False #FIXME: is this a good check??? # check if code 'pattern' matches #XXX: or pattern match against dis.dis(code)? (or use uncompyle2?) _ = _.split(_[0]) # 't' #XXX: remove matching values if starts the same? _f = code.split(code[0]) # '\x88' #NOTE: should be same code different order, with different first element _ = dict( re.match('([\W\D\S])(.*)', _[i]).groups() for i in range(1, len(_))) _f = dict( re.match('([\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 get_internal_comments(object): lines, lnum = inspect.findsource(object) if len(lines) <= lnum + 1: # object is probably an emply module. return None indent = inspect.indentsize(lines[lnum+1]) comments = [] for line in lines[lnum + 1:]: comment = line.strip() # A line is a comment if it matches the indentation the first line and # begins with a '#' if inspect.indentsize(line) == indent and comment.startswith('#'): comments.append(comment) else: break if len(comments) == 0: return None else: return '\n'.join(comments)
def get_annotation_comment_and_line_by_source(container_object, variable_name, source_file): try: lines, lnum = get_source(container_object, source_file) except (OSError, TypeError): return None i = 1 while indentsize(lines[lnum + i]) == indentsize(lines[lnum]): i += 1 if len(lines) > lnum and indentsize(lines[lnum + i]) > indentsize(lines[lnum]): this_cls_indentsize = indentsize(lines[lnum + i]) l = lnum + i while l < len(lines): cur_indentsize = indentsize(lines[l]) if cur_indentsize < this_cls_indentsize and lines[l].strip() != "": break if cur_indentsize == this_cls_indentsize: expr = lines[l].strip().replace(" ", "") if expr.startswith(variable_name + ":"): lnum = l break l += 1 if lnum > 0: indent = indentsize(lines[lnum]) end = lnum - 1 if end >= 0 and lines[end].lstrip()[:1] == '#' and \ indentsize(lines[end]) == indent: comments = [lines[end].expandtabs().lstrip()] if end > 0: end = end - 1 comment = lines[end].expandtabs().lstrip() while comment[:1] == '#' and indentsize(lines[end]) == indent: comments[:0] = [comment] end = end - 1 if end < 0: break comment = lines[end].expandtabs().lstrip() while comments and comments[0].strip() == '#': comments[:1] = [] while comments and comments[-1].strip() == '#': comments[-1:] = [] return '\n'.join(comments), lnum return None, lnum
def _find_line_info_of_attr_in_source( frame: Optional[FrameType], key: str, attr: "AbstractComplexExtractor") -> _LineInfo: if frame is None: return _LineInfo(None, None, None, f"{key}={attr!r}") file = frame.f_code.co_filename firstlineno = frame.f_lineno firstline_idx = firstlineno - 1 try: lines, _ = inspect.findsource(frame) except OSError: # can't get the source code from python repl return _LineInfo(None, None, None, f"{key}={attr!r}") start_index = inspect.indentsize(lines[firstline_idx]) for lineno, line in enumerate(lines[firstline_idx + 1:], start=firstlineno + 1): # iterate line in the code block body cur_index = inspect.indentsize(line) if cur_index <= start_index: # reach end of the code block, # use code block firstlineno as SyntaxError.lineno line = lines[firstline_idx] lineno = firstlineno break if line.lstrip().startswith(key): # find the line as SyntaxError.text break else: # reach EOF, # use code block firstlineno as SyntaxError.lineno line = lines[firstline_idx] lineno = firstlineno offset = inspect.indentsize(line) line = line.strip() return _LineInfo(file, lineno, offset, line)
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__' # process the line (removing leading whitespace, etc) lhs,rhs = line.split('lambda ',1)[-1].split(":", 1) #FIXME: if !1 inputs try: #FIXME: unsafe _ = eval("lambda %s : %s" % (lhs,rhs), globals(),locals()) except: _ = dummy # get code objects, for comparison _, code = getcode(_).co_code, getcode(func).co_code # check if func is in closure _f = [line.count(i) for i in freevars(func).keys()] if not _f: # not in closure # check if code matches if _ == code: return True return False # weak check on freevars if not all(_f): return False #XXX: VERY WEAK # weak check on varnames and globalvars _f = varnames(func) _f = [line.count(i) for i in _f[0]+_f[1]] if _f and not all(_f): return False #XXX: VERY WEAK _f = [line.count(i) for i in globalvars(func).keys()] if _f and not all(_f): return False #XXX: VERY WEAK # check if func is a double lambda if (line.count('lambda ') > 1) and (lhs in freevars(func).keys()): _lhs,_rhs = rhs.split('lambda ',1)[-1].split(":",1) #FIXME: if !1 inputs try: #FIXME: unsafe _f = eval("lambda %s : %s" % (_lhs,_rhs), globals(),locals()) except: _f = dummy # get code objects, for comparison _, code = getcode(_f).co_code, getcode(func).co_code if len(_) != len(code): return False #NOTE: should be same code same order, but except for 't' and '\x88' _ = set((i,j) for (i,j) in zip(_,code) if i != j) if len(_) != 1: return False #('t','\x88') return True # check indentsize if not indentsize(line): return False #FIXME: is this a good check??? # check if code 'pattern' matches #XXX: or pattern match against dis.dis(code)? (or use uncompyle2?) _ = _.split(_[0]) # 't' #XXX: remove matching values if starts the same? _f = code.split(code[0]) # '\x88' #NOTE: should be same code different order, with different first element _ = 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 get_func_source_code(func): if isinstance(func, types.MethodType): if sys.version_info[0] == 3: src_code = inspect.getsource(func.__self__.__class__) else: im_class = get_method_defined_class(func) src_code = inspect.getsource(im_class) else: src_code = inspect.getsource(func) indent = inspect.indentsize(src_code) if indent > 0: lines = src_code.split("\n") src_code = "" for line in lines: src_code += line[indent:] + "\n" return src_code
def __init__(self, o): """Transcode a Python function or module In: - ``o`` -- Python function or module to transcode """ if hasattr(o, '_js_name'): # Already transcoded self.name = o._js_name self.javascript = o._js_code return src = inspect.getsource(o) classname = '' if isinstance(o, types.MethodType): classname = o.im_class.__name__ o = o.im_func if isinstance(o, types.FunctionType): if o.__module__ == '__main__': self.name = module = classname else: module = o.__module__.replace('.', '_') if classname: module += ('_' + classname) self.name = module + '_' self.name += o.func_name indent = inspect.indentsize(src) src = ''.join([s.expandtabs()[indent:] for s in src.splitlines(True)]) else: if o.__name__ == '__main__': self.name = module = '' else: self.name = module = o.__name__.replace('.', '_') self.javascript = str2js(src, module) # Keep the transcoded javascript o._js_name = self.name o._js_code = self.javascript
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] #XXX: 'enclosing' means: closures only? or classes and files? indent = indentsize(lines[lnum]) block = getblock(lines[lnum:]) #XXX: catch any TokenError here? 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) #pat3 = r'^(\s*class\s)'; pat3 = re.compile(pat3) #XXX: enclosing class? #FIXME: bound methods need enclosing class (and then instantiation) # *or* somehow apply a partial using the instance skip = 0 line = 0 blocks = []; _lnum = [] target = ''.join(block) while line <= lnum: #XXX: repeat lnum? or until line < lnum? # see if starts with ('def','lambda') and contains our target block if pat1.match(lines[line]): if not skip: try: code = getblock(lines[line:]) except TokenError: code = [lines[line]] if indentsize(lines[line]) > indent: #XXX: should be >= ? line += len(code) - skip elif target in ''.join(code): blocks.append(code) # save code block as the potential winner _lnum.append(line - skip) # save the line number for the match line += len(code) - skip else: line += 1 skip = 0 # find skip: the number of consecutive decorators elif pat2.match(lines[line]): try: code = getblock(lines[line:]) except TokenError: code = [lines[line]] skip = 1 for _line in code[1:]: # skip lines that are decorators if not pat2.match(_line): break skip += 1 line += skip # no match: reset skip and go to the next line else: line +=1 skip = 0 if not blocks: blocks = [block] _lnum = [lnum] if lstrip: blocks = [_outdent(block) for block in blocks] # return last match return (blocks, _lnum) if locate is True else blocks
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] #XXX: 'enclosing' means: closures only? or classes and files? indent = indentsize(lines[lnum]) block = getblock(lines[lnum:]) #XXX: catch any TokenError here? 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) #pat3 = r'^(\s*class\s)'; pat3 = re.compile(pat3) #XXX: enclosing class? #FIXME: bound methods need enclosing class (and then instantiation) # *or* somehow apply a partial using the instance skip = 0 line = 0 blocks = [] _lnum = [] target = ''.join(block) while line <= lnum: #XXX: repeat lnum? or until line < lnum? # see if starts with ('def','lambda') and contains our target block if pat1.match(lines[line]): if not skip: try: code = getblock(lines[line:]) except TokenError: code = [lines[line]] if indentsize(lines[line]) > indent: #XXX: should be >= ? line += len(code) - skip elif target in ''.join(code): blocks.append(code) # save code block as the potential winner _lnum.append(line - skip) # save the line number for the match line += len(code) - skip else: line += 1 skip = 0 # find skip: the number of consecutive decorators elif pat2.match(lines[line]): try: code = getblock(lines[line:]) except TokenError: code = [lines[line]] skip = 1 for _line in code[1:]: # skip lines that are decorators if not pat2.match(_line): break skip += 1 line += skip # no match: reset skip and go to the next line else: line += 1 skip = 0 if not blocks: blocks = [block] _lnum = [lnum] if lstrip: blocks = [_outdent(block) for block in blocks] # return last match return (blocks, _lnum) if locate is True else blocks
def __init__(cls, name: str, bases: Tuple[type], attr_dict: Dict[str, Any]): super().__init__(name, bases, attr_dict) field_names: List[str] = [] __init_args = inspect.getfullargspec(getattr(cls, "__init__")).args for key, attr in attr_dict.items(): if isinstance(type(attr), ComplexExtractorMeta): if key in __init_args[1:]: # Item's attribute overwrites the 'Item.__init__' parameters except first parameter. args = [] # type: List[Any] exc_args = None frame = inspect.currentframe() assert ( frame is not None ), "If running in an implementation without Python stack frame support this function returns None." try: outer_frame = frame.f_back filename = outer_frame.f_code.co_filename firstlineno = outer_frame.f_lineno firstline_idx = firstlineno - 1 lines = None try: lines, _ = inspect.findsource(outer_frame) except OSError: # can't get the source code from python repl pass if lines is not None: start_index = inspect.indentsize( lines[firstline_idx]) for lineno, line in enumerate( lines[firstline_idx + 1:], start=firstlineno + 1): # iterate line in the code block body cur_index = inspect.indentsize(line) if cur_index <= start_index: # reach end of the code block, use code block firstlineno as SyntaxError.lineno line = lines[firstline_idx] lineno = firstlineno break if line.lstrip().startswith(key): # find the line as SyntaxError.text break else: # reach EOF, use code block firstlineno as SyntaxError.lineno line = lines[firstline_idx] lineno = firstlineno offset = inspect.indentsize(line) line = line.strip() exc_args = (filename, lineno, offset, line) else: line = f"{key}={attr!r}" finally: del outer_frame del frame args.append( f"{line!r} overwriten the parameter {key!r} of '{name}.__init__' method. " f"Please using the optional parameter name={key!r} in {attr!r} to avoid overwriting parameter name." ) if exc_args is not None: args.append(exc_args) raise SyntaxError(*args) field_names.append(key) cls._field_names = field_names
def _func_to_code(self, func): lines = inspect.getsourcelines(func)[0] indent = inspect.indentsize(lines[0]) code = ''.join([ i[indent:] for i in lines ]) return code