laywerrobot/lib/python3.6/site-packages/astor/file_util.py

112 lines
3.2 KiB
Python
Raw Normal View History

2020-08-27 21:55:39 +02:00
# -*- coding: utf-8 -*-
"""
Part of the astor library for Python AST manipulation.
License: 3-clause BSD
Copyright (c) 2012-2015 Patrick Maupin
Copyright (c) 2013-2015 Berker Peksag
Functions that interact with the filesystem go here.
"""
import ast
import sys
import os
try:
from tokenize import open as fopen
except ImportError:
fopen = open
class CodeToAst(object):
"""Given a module, or a function that was compiled as part
of a module, re-compile the module into an AST and extract
the sub-AST for the function. Allow caching to reduce
number of compiles.
Also contains static helper utility functions to
look for python files, to parse python files, and to extract
the file/line information from a code object.
"""
@staticmethod
def find_py_files(srctree, ignore=None):
"""Return all the python files in a source tree
Ignores any path that contains the ignore string
This is not used by other class methods, but is
designed to be used in code that uses this class.
"""
if not os.path.isdir(srctree):
yield os.path.split(srctree)
for srcpath, _, fnames in os.walk(srctree):
# Avoid infinite recursion for silly users
if ignore is not None and ignore in srcpath:
continue
for fname in (x for x in fnames if x.endswith('.py')):
yield srcpath, fname
@staticmethod
def parse_file(fname):
"""Parse a python file into an AST.
This is a very thin wrapper around ast.parse
TODO: Handle encodings other than the default for Python 2
(issue #26)
"""
try:
with fopen(fname) as f:
fstr = f.read()
except IOError:
if fname != 'stdin':
raise
sys.stdout.write('\nReading from stdin:\n\n')
fstr = sys.stdin.read()
fstr = fstr.replace('\r\n', '\n').replace('\r', '\n')
if not fstr.endswith('\n'):
fstr += '\n'
return ast.parse(fstr, filename=fname)
@staticmethod
def get_file_info(codeobj):
"""Returns the file and line number of a code object.
If the code object has a __file__ attribute (e.g. if
it is a module), then the returned line number will
be 0
"""
fname = getattr(codeobj, '__file__', None)
linenum = 0
if fname is None:
func_code = codeobj.__code__
fname = func_code.co_filename
linenum = func_code.co_firstlineno
fname = fname.replace('.pyc', '.py')
return fname, linenum
def __init__(self, cache=None):
self.cache = cache or {}
def __call__(self, codeobj):
cache = self.cache
key = self.get_file_info(codeobj)
result = cache.get(key)
if result is not None:
return result
fname = key[0]
cache[(fname, 0)] = mod_ast = self.parse_file(fname)
for obj in mod_ast.body:
if not isinstance(obj, ast.FunctionDef):
continue
cache[(fname, obj.lineno)] = obj
return cache[key]
code_to_ast = CodeToAst()