Файл "<строкa>" в трассировке питона

Я нахожусь в центре рефакторинга огромного модуля py для пакетов - чтобы не нарушать существующий код, я переместил его содержимое в модуль package/__init__.py (Добавление кода в __init __. py) и продолжал раскалывать его оттуда. В какой-то момент я заметил, что в своих следствиях я получаю:

Traceback (most recent call last):
      File "<string>", line 656, in DoItemMenu
      File "bash\balt.py", line 2109, in PopupMenu
        link.AppendToMenu(menu,parent,*args)
      File "bash\balt.py", line 2225, in AppendToMenu
        for link in self.links: link.AppendToMenu(subMenu,window,data)
    ...

где строки в File "<string>" соответствуют конкретному модулю package/__init__.py. Кроме того, отладчик PyCharm отображает строку "frame not available" и не входит в строки в __init__.py. Зачем? Связано ли это с шаблоном импорта?

Код импортируется класс launcher:

class UnicodeImporter(object):
    def find_module(self,fullname,path=None):
        if isinstance(fullname,unicode):
            fullname = fullname.replace(u'.',u'\\')
            exts = (u'.pyc',u'.pyo',u'.py')
        else:
            fullname = fullname.replace('.','\\')
            exts = ('.pyc','.pyo','.py')
        if os.path.exists(fullname) and os.path.isdir(fullname):
            return self
        for ext in exts:
            if os.path.exists(fullname+ext):
                return self

    def load_module(self,fullname):
        if fullname in sys.modules:
            return sys.modules[fullname]
        else:
            sys.modules[fullname] = imp.new_module(fullname)
        if isinstance(fullname,unicode):
            filename = fullname.replace(u'.',u'\\')
            ext = u'.py'
            initfile = u'__init__'
        else:
            filename = fullname.replace('.','\\')
            ext = '.py'
            initfile = '__init__'
        if os.path.exists(filename+ext):
            try:
                with open(filename+ext,'U') as fp:
                    mod = imp.load_source(fullname,filename+ext,fp)
                    sys.modules[fullname] = mod
                    mod.__loader__ = self
                    return mod
            except:
                print 'fail', filename+ext
                raise
        mod = sys.modules[fullname]
        mod.__loader__ = self
        mod.__file__ = os.path.join(os.getcwd(),filename)
        mod.__path__ = [filename]
        #init file
        initfile = os.path.join(filename,initfile+ext)
        if os.path.exists(initfile):
            with open(initfile,'U') as fp:
                code = fp.read()
            exec code in mod.__dict__
        return mod

Ответ 1

Код не импортируется традиционным способом; вместо код запуска используется exec statement для загрузки __init__.py.

Отказывание потока в функции запуска load_module() для пакета (так что это не путь к модулю):

# the fullname module isn't yet loaded
sys.modules[fullname] = imp.new_module(fullname)
initfile = '__init__'  # or u'__init__' if a unicode path was used

# if no .py file was found, so not a module
mod = sys.modules[fullname]
mod.__loader__ = self
mod.__file__ = os.path.join(os.getcwd(),filename)
mod.__path__ = [filename]
#init file
initfile = os.path.join(filename,initfile+ext)
if os.path.exists(initfile):
    with open(initfile,'U') as fp:
        code = fp.read()
    exec code in mod.__dict__
return mod

Это создает пустой объект модуля, загружает исходный код вручную и выполняет его как строку, передавая в пространстве имен модулей как глобальные переменные для исполняемого кода. Результирующий объект кода всегда будет перечислять <string> в tracebacks:

>>> import imp
>>> mod = imp.new_module('foo.bar')
>>> mod.__file__ = r'C:\some\location\foo\bar'
>>> mod.__path__ = [r'foo\bar']
>>> exec 'raise ValueError("oops")' in mod.__dict__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
ValueError: oops

Поскольку нет имени файла, связанного с кодом, PyCharm не может найти исходный источник.

Обходной способ заключается в использовании функции compile() для создания объекта кода сначала и присоединения имени файла к этому:

>>> exec compile('raise ValueError("oops")', r'C:\some\location\foo\bar\__init__.py', 'exec') in mod.__dict__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\some\location\foo\bar\__init__.py", line 1, in <module>
ValueError: oops

Обратите внимание, что я включил __init__.py в имя файла; переведя его обратно в пусковую установку, которую вы использовали:

if os.path.exists(initfile):
    with open(initfile,'U') as fp:
        code = fp.read()
    exec compile(code, initfile, 'exec') in mod.__dict__