2015-04-29 2 views
7

Учитывая модуль, содержащий:Как получить список всех не импортированных имен в модуле Python?

import stuff 
from foo import Foo 
from bar import * 

CST = True 

def func(): pass 

Как я могу определить функцию get_defined_objects, так что я могу сделать:

print(get_defined_objects('path.to.module')) 
{'CST': True, 'func', <function path.to.module.func>} 

Сейчас единственное решение, которое я могу себе представить, чтобы прочитать исходный файл модуля, выведите определенные имена с помощью re.search(r'^(?:def|class)?(\w+)(?:\s*=)?', затем импортируйте модуль и найдите пересечение с __dict__.

Есть ли что-то более чистые?

+0

Как насчет имен с ограниченным объемом или вам нужен только верхний уровень? Кроме того, вам нужны такие вещи, как 'for foo in ...:' и 'with ... as bar:'? – jonrsharpe

+0

Я действительно хочу, чтобы имена верхнего уровня были определены как переменные, функции и классы. –

+1

Итак, вы хотите напечатать все имена, определенные в данном модуле, но игнорировать имена, которые были импортированы? – geckon

ответ

3

Вот что-то для вас, чтобы начать с использованием ast. Обратите внимание, что этот код не охватывает все возможные случаи, хотя он должен обрабатывать, например. многократное присвоение. Рассмотрим, например, более подробные исследования структур данных и API ast, если вы хотите получить доступ к скомпилированному коду, например.

import ast 

with open('module.py') as f: 
    data = f.read() 
    tree = ast.parse(data) 
    elements = [el for el in tree.body if type(el) in (ast.Assign, ast.FunctionDef, ast.ClassDef)] 

result = {} 

for el in elements: 
    if type(el) == ast.Assign: 
     for t in el.targets: 
      if type(el.value) == ast.Call: 
       result[t.id] = el.value.func.id + '()' 
      else: 
       for attr in ['id', 'i', 's']: 
        try: 
         result[t.id] = getattr(el.value, attr) 
         break 
        except Exception as e: 
         pass 
    elif type(el) == ast.FunctionDef: 
     result[el.name] = '<function %s>' % el.name 
    else: 
     result[el.name] = '<class %s>' % el.name 

print result 
# 
+1

это сделаю. Большое спасибо, он не только решает мою проблему, но и научил меня использовать модуль ast. –

+0

Просто для будущих читателей, решение хорошо, но замените type() на isinstance(). –

-3

Непроверено

def unexported_names (module): 
    try: 
     return [name for name in module.__dict__ if name not in module.__all__] 
    except AttributeError: 
     return [name for name in module.__dict__ if name.startswith('_')] 
+0

К сожалению, это означает, что определена __all__, которая является переменной, которую необходимо записать вручную. Я не склонялся к голосованию, потому что это показывает честную попытку помочь, и я не упоминал об этом, хотя я уже говорил о \ _ \ _ a \ _ \ _ –

+0

. Он не предполагает, что __all__ определен; он возвращается к поиску имен с подчеркиванием-префиксами, если это не так. Тем не менее, он по-прежнему не выполняет то, что вы хотите (я не читал вопрос подробно изначально), потому что он не делает различия между именами, которые были импортированы в тестируемый модуль, и именами, которые были определены там в некоторых других путь. К сожалению, я не думаю, что сам Python делает это различие, поэтому ваша идея разбора исходного файла сама по себе может быть единственным способом сделать то, что вы говорите о попытке сделать. – Hammerite

+0

Может быть. Аст может помочь, но я не знаю, как его использовать. –

2

Байт-код взлома для Python 3.4+. Возможно из-за dis.get_instructions.

import dis 
import importlib 
from itertools import islice 
import marshal 
import os 


def consume_iterator(it, n=1): 
    next(islice(it, n, n), None) 


def get_defined_names(module_path): 
    path, module_name = os.path.split(module_path) 
    module_name = module_name[:-3] 
    module_object = importlib.import_module(module_name) 
    pyc_name = '{}.cpython-34.pyc'.format(module_name) 
    pyc_path = os.path.join(path, '__pycache__/', pyc_name) 

    with open(pyc_path, 'rb') as f: 
     f.read(12) # drop the first 12 bytes 
     code = marshal.load(f) 
     # dis.disassemble(code) # see the byte code 
     instructions = dis.get_instructions(code) 
     objects = {} 

     for instruction in instructions: 
      if instruction.opname == 'STORE_NAME': 
       objects[instruction.argval] = getattr(module_object, 
                 instruction.argval) 
      elif instruction.opname == 'IMPORT_NAME': 
       consume_iterator(instructions, 2) 
      elif instruction.opname == 'IMPORT_FROM': 
       consume_iterator(instructions, 1) 
     return objects 


print(get_defined_names('/Users/ashwini/py/so.py')) 

Для файла, как:

#/Users/ashwini/py/so.py 
import os 
from sys import argv, modules 
from math import * 
from itertools import product 


CST = True 

from itertools import permutations, combinations 
from itertools import chain 
E = 100 
from itertools import starmap 

def func(): pass 

for x in range(10): 
    pass 

class C: 
    a = 100 

d = 1 

Выход будет:

{'d': 1, 'E': 100, 'CST': True, 'x': 9, 'func': <function func at 0x10efd0510>, 'C': <class 'so.C'>} 

Гораздо более лучший способ, как кто-то уже упоминалось в комментариях будет разбора исходный код с использованием модуля ast и узнать имена переменных оттуда.

+0

Я поднимусь только потому, что это забавный хак. –

3
mod = "foo" 
import ast, inspect 
import importlib 

mod = importlib.import_module(mod) 
p = ast.parse(inspect.getsource(mod)) 

from collections import defaultdict 

data = defaultdict(defaultdict) 

for node in p.body: 
    if isinstance(node, (ast.ImportFrom, ast.Import)): 
     continue 
    if isinstance(node, (ast.ClassDef, ast.FunctionDef)): 
     data["classes"][node.name] = mod.__dict__[node.name] 
    elif isinstance(node, ast.Assign): 
     for trg in node.targets: 
      if isinstance(node.value, ast.Num): 
       data["assignments"][trg.id] = node.value.n 
      elif isinstance(node.value, ast.Str): 
       data["assignments"][trg.id] = node.value.s 
      else: 
       data["assignments"][trg.id] = mod.__dict__[trg.id] 

Выход:

Существует хорошее объяснение here, который перечисляет, что различные типы делают и их атрибуты, это основано на:

class Nodes(ast.NodeVisitor): 
    def __init__(self): 
     self.data = defaultdict() 
     super(Nodes, self).__init__() 

    def visit_FunctionDef(self, node): 
     self.data[node.name] = mod.__dict__[node.name] 
     print("In FunctionDef with funcion {}".format(node.name)) 

    def visit_ClassDef(self, node): 
     self.data[node.name] = mod.__dict__[node.name] 

    def visit_Assign(self, node): 
     for trg in node.targets: 
      if isinstance(node.value, (ast.Str, ast.Num, ast.Dict, ast.List, ast.ListComp, ast.NameConstant)): 
       self.data[trg.id] = mod.__dict__[trg.id] 
     self.generic_visit(node) 

    def visit_Name(self, node): 
     """ 
     class Name(idctx) 
     A variable name. id holds the name as a string 
     and ctx is either class Load class Store class Del. 
     """ 
     print("In Name with {}\n".format(node.id)) 
    # 
    def visit_Dict(self, node): 
     """ 
     class Dict(keys, values) 
     A dictionary. keys and values 
     hold lists of nodes with matching order 
     """ 
     print("In Dict keys = {}, values = {}\n".format(node.keys,node.values)) 


    def visit_Set(self,node): 
     """ 
     class Set(elts) 
     A set. elts holds a list of 
     nodes representing the elements. 
     """ 
     print("In Set elts = {}\n".format(node.elts)) 

    def visit_List(self, node): 
     """ 
     class List(eltsctx) 
     lts holds a list of nodes representing the elements. 
     ctx is Store if the container 
     is an assignment target 
     (i.e. (x,y)=pt), and Load otherwise. 
     """ 
     print("In List elts = {}\nctx = {}\n".format(node.elts,node.ctx)) 

    def visit_Tuple(self, node): 
     """ 
     class Tuple(eltsctx) 
     lts holds a list of nodes representing the elements. 
     ctx is Store if the container 
     is an assignment target 
     (i.e. (x,y)=pt), and Load otherwise. 
     """ 
     print("In Tuple elts = {}\nctx = {}\n".format(node.elts,node.ctx)) 

    def visit_NameConstant(self, node): 
     """ 
     class NameConstant(value) 
     True, False or None. "value" holds one of those constants. 
     """ 
     print("In NameConstant getting value {}\n".format(node.value)) 


    def visit_Load(self, node): 
     print("In Load with node {}\n".format(node.func)) 


    def visit_Call(self, node): 
     """ 
     class Call(func, args, keywords, starargs, kwargs) 
     A function call. func is the function, 
     which will often be a Name or Attribute object. Of the arguments: 
     args holds a list of the arguments passed by position. 
     keywords holds a list of keyword objects representing arguments 
     passed by keyword.starargs and kwargs each hold a single node, 
     for arguments passed as *args and **kwargs. 
     """ 
     print("In Call with node {}\n".format(node.func)) 


    def visit_Num(self, node): 
     print("In Num getting value {}\n".format(node.n)) 

    def visit_Str(self, node): 
     print("In Str getting value {}\n".format(node.s)) 
f = Nodes() 
f.visit(p) 
print(f.data) 
+0

Спасибо. Я принял ответ Вадима Ланды, потому что он получил меньше репутации, чем вы, но бот решил мою проблему. –

1

В то время как я принял ответ, он может «Мне больно публиковать решение, которое я использовал. Это смесь между другими предложениями:

import ast 
import inspect 
import importlib 

from types import ModuleType 

def extract_definitions(module): 
    """ Returns the name and value of objects defined at the top level of the given module. 

     :param module: A module object or the name of the module to import. 
     :return: A dict {'classes': {}, 'functions': {}, 'assignments': {}} containing defined objects in the module. 
    """ 

    if not isinstance(module, ModuleType): 
     module = importlib.import_module(module) 

    tree = ast.parse(inspect.getsource(module)) 

    definitions = {'classes': {}, 'functions': {}, 'assignments': {}} 

    for node in tree.body: 

     if isinstance(node, ast.ClassDef): 
      definitions["classes"][node.name] = getattr(module, node.name) 
     elif isinstance(node, ast.FunctionDef): 
      definitions["functions"][node.name] = getattr(module, node.name) 
     elif isinstance(node, ast.Assign): 
      # for unpacking, you need to loop on all names 
      for target in node.targets: 
       definitions["assignments"][target.id] = getattr(module, target.id) 

    return definitions 

Я добавил возможность импорта из строки или объекта модуля, а затем удаляется синтаксические значения и заменить его простой GetAttr из оригинального модуля.

+0

Этот ответ и пара вопросов очень полезны! .... Надеюсь, это будет похоже на ваши другие 3 пары Q-A! –

+0

Обратите внимание, что я уже нашел ограничения для этого решения: вы можете создавать динамические атрибуты для модуля объектов, и вы никогда не узнаете. Может быть, я должен отсканировать файл sys.module [__ name __]. Foo = bar. –

Смежные вопросы