Ipython: Глобальные переменные undefined при интерактивном использовании встроенной оболочки ipython

Созданный на 10 мая 2010  ·  39Комментарии  ·  Источник: ipython/ipython

Оригинальная ошибка Launchpad 399627: https://bugs.launchpad.net/ipython/+bug/399627
Сообщает: h-fangohr (Ханс Фангор).

Ошибка может быть воспроизведена следующим образом:

  1. Запустите Python и запустите встроенный сеанс ipython. Следуя руководству ipython, мы делаем
fangohr<strong i="11">@eta</strong>:~$ python
Python 2.4.3 (#1, Jun  8 2009, 14:09:06) 
[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from IPython.Shell import IPShellEmbed
>>> ipshell=IPShellEmbed()
>>> ipshell()

В рамках только что запущенного сеанса ipython глобальные переменные иногда не видны. Вот два примера:

Пример 1:

In [1]: a=1

In [2]: def f(x):
   ...:     return a*x
   ...: 

In [3]: f(2)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)

/home/fangohr/<ipython console> 

/home/fangohr/<ipython console> in f(x)

NameError: global name 'a' is not defined

Пример 2:

In [4]: b=1

In [5]: (lambda :b)()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)

/home/fangohr/<ipython console> 

/home/fangohr/<ipython console> in <lambda>()

NameError: global name 'b' is not defined

Ошибка не возникает, если в сценарий помещается «b = 1; (lambda: b) ()», и этот сценарий выполняется с помощью команды ipython «run».

Ошибка отсутствует, если "b = 1; (lambda: b) ()" выполняется интерактивно после запуска ipython.

Кажется, что ошибка возникает только в том случае, если встроенная оболочка IPython запускается из приглашения Python И команды выполняются в интерактивном режиме в приглашении.

Я обнаружил ту же ошибку при попытке ipython-0.9.1 (с python2.4).

Об ошибке сообщил Оливье Кляйн команде nmag (http://nmag.soton.ac.uk).

Самый полезный комментарий

Как указано на https://github.com/inducer/pudb/issues/103 , временный обходной путь при использовании встроенной оболочки: globals().update(locals()) (только после определения локальных глобальных переменных).

Все 39 Комментарий

[LP, комментарий 1, автор: Фернандо Перес, 25 апреля 2010 г., 23:36: 38.673176 + 00:00]

Хорошо, я могу подтвердить проблему (даже на стволе), но это сложно. Я просто пока убеждаюсь, что эта ошибка подтверждена, поэтому мы продолжаем отслеживать ее, но я не знаю, как ее исправить.

Проблема в том, что во встроенных оболочках мы пытаемся обновить глобальное пространство имен для использования окружающей области (это точка встроенной оболочки, чтобы иметь возможность видеть, что вас окружает). Но это затем приводит к тому, что python не может разрешить интерактивное пространство имен ipython, когда определены вложенные вещи (например, локальные функции). Подробнее см. Метод встроенной оболочки mainloop ().

Мне нужно больше думать о том, как это исправить, любые идеи приветствуются.

На стартовой площадке ...

ChairmanK написал 20 часов назад:

Я тоже. Помимо функций, эта ошибка также появляется в выражениях генератора.

Эквивалентный компонент в магистрали выглядит IPython.frontend.terminal.InteractiveShellEmbed. Но это сломано по-другому, и, очевидно, не так много проверено. Кто-нибудь знает, каково его будущее?

Может быть, это та же проблема, что и №136?

Теперь это было исправлено:

amirbar[ipython]> python
Python 2.7.2+ (default, Oct  4 2011, 20:06:09) 
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from IPython import embed
>>> embed()
Python 2.7.2+ (default, Oct  4 2011, 20:06:09) 
Type "copyright", "credits" or "license" for more information.

IPython 0.12.dev -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: a = 1

In [2]: def f(x):
   ...:     return a*x
   ...: 

In [3]: f(3)
Out[3]: 3

In [4]: b = 1

In [5]: (lambda : b)()
Out[5]: 1

In [6]: 

Может ли кто-нибудь объяснить немного больше, что было сделано для исправления этого? Я все еще сталкиваюсь с проблемой, но только когда я удаляю один слой с помощью вызова метода, и только если я запускаю из сценария, а не в интерактивном режиме.

python 2.7.2 в OSX 10.6.8, ipython 0.11 и 0.12 демонстрируют аналогичное поведение (с использованием 0.12 для этого комментария)

Это проблема нашего проекта, который (в основном) включает встроенную оболочку IPython.

testembed.py

from IPython import embed

def hi():
    embed()

if __name__ == '__main__':
    #embed()
    hi()

Запустите это в командной строке с помощью python testembed.py и посмотрите этот сеанс:

Python 2.7.2 (default, Aug 29 2011, 12:33:18) 
Type "copyright", "credits" or "license" for more information.

IPython 0.12 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: import time

In [2]: def tim():
   ...:     print time.time()
   ...:     

In [3]: tim()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
[snip] in <module>()
----> 1 tim()

[snip] in tim()
      1 def tim():
----> 2     print time.time()
      3 

NameError: global name 'time' is not defined

In [4]: 

Однако закомментируйте вызов hi() и замените его вызовом embed() :

Python 2.7.2 (default, Aug 29 2011, 12:33:18) 
Type "copyright", "credits" or "license" for more information.

IPython 0.12 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: import time

In [2]: def tim():
    print time.time()
   ...:     

In [3]: tim()
1328639444.29

In [4]: 

Покопавшись, я думаю, что это связано с используемым здесь параметром stack_depth и этим блоком: https://github.com/ipython/ipython/blob/master/IPython/frontend/terminal/embed.py # L185

Мысли?

Это несколько сложно, но я считаю, что это ограничение самого Python.

В указанном вами неудачном случае time фактически не помещается в глобальное пространство имен: поскольку вы вызвали embed внутри функции hi , новые переменные, которые вы создаете в интерактивном режиме, локальны для этого функция. В идеале tim() должно работать как закрытие, закрывая ссылку на модуль времени. Однако кажется, что замыкания работают только тогда, когда содержащая функция компилируется за один раз. Насколько я могу судить, нет возможности определять закрытие динамически. Этот простой пример не работает:

def outer():
    import time
    exec("def inner(): return time.time()")
    return inner

outer()()

Вероятно, это связано с тем, что вложенные области были добавлены в Python относительно поздно (они были импортированы в будущем в 2.1 и всегда были включены в 2.2).

Хорошо, думаю, я понимаю. Похоже, что тогда мы действительно ничего не можем с этим поделать, кроме как сбросить то, что я бы записал в интерактивном режиме, в файл и затем прочитать этот файл обратно. Вероятно, слишком сложно для того, что мы хотим делать в интерактивном режиме.

Спасибо, кстати.

Извините, что продолжаю твердить здесь, но это просто делает интерактивные сессии очень неуклюжими:

Обычный питон:

>>> d={'one':1, 'two':2}
>>> getkeys=lambda: d.keys()
>>> getkeys()
['two', 'one']

Обычный IPython:

In [1]: d={'one':1, 'two':2}

In [2]: getkeys=lambda: d.keys()

In [3]: getkeys()
Out[3]: ['two', 'one']

Встроенный IPython:

>>> from IPython import embed
>>> embed()
Python 2.7.2 (default, Aug 29 2011, 12:33:18) 
Type "copyright", "credits" or "license" for more information.

IPython 0.12.dev -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: d={'one':1, 'two':2}

In [2]: getkeys=lambda: d.keys()

In [3]: getkeys()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/Users/asadeveloper/Documents/Dev/code/pyon/bin/python in <module>()
----> 1 getkeys()

/Users/asadeveloper/Documents/Dev/code/pyon/bin/python in <lambda>()
----> 1 getkeys=lambda: d.keys()

NameError: global name 'd' is not defined

Я думаю, что можно сделать не так много, но я не понимаю, почему мы можем создавать динамическое закрытие в обычном python / ipython, но не во встроенной версии.

Судя по странностям области видимости Python, в стандартном Python / IPython мы фактически не создаем замыкание. d - это глобальная переменная, и правила доступа к ней работают иначе, чем закрытие. Каждая функция хранит ссылку на глобальное пространство имен, в котором она была определена ( getkeys.func_globals ), поэтому она может получить доступ к любым определенным там переменным.

Напротив, когда вы выполняете закрытие, Python присоединяет ссылки к каждой переменной, над которой он закрылся, как это определено компилятором, но это работает только тогда, когда внутренние и внешние функции компилируются одновременно. Выглядит это так:

In [8]: def outer():
    a = 1
    def inner():
        return a
    return inner
   ...: 

In [9]: f = outer()

In [10]: f
Out[10]: <function __main__.inner>

In [11]: f.func_closure
Out[11]: (<cell at 0x9f5e344: int object at 0x9a830b0>,)

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

Я использую Python 2.7.2 и IPython 0.12.1 в OSX 10.7.3, и эта проблема все еще возникает. Когда я запускаю ./manage.py shell в Django, который вызывает IPython.embed() , возникает проблема. Однако при ручном вызове embed() в оболочке Python или из простого файла сценария проблем нет.

Мы мало что можем с этим поделать, но мы, вероятно, должны рекомендовать третьим сторонам отказаться от embed для нетривиального использования.

@takluyver Вы имеете в виду, что в этом случае лучше использовать ipython напрямую?

Django может запустить IPython способом, который не вызовет этой проблемы, но в настоящее время мы не упрощаем этот способ. Проблема возникает, когда IPython запускается с отдельными локальным и глобальным пространствами имен. Нет причин, по которым Django требует отдельных локальных и глобальных пространств имен, но это то, что подразумевает вызов embed() внутри функции.

Для справки вот код в Django:
https://code.djangoproject.com/browser/django/trunk/django/core/management/commands/shell.py

@takluyver В этом есть смысл, спасибо! Я открываю для этого билет Django.

@takluyver , теперь, когда мы объединили embed_kernel, так что все основные части на месте, не хотите ли вы немного очистить это, чтобы упростить использование более тонкой настройки?

Я посмотрю, какой интерфейс может иметь наибольший смысл.

У меня все еще возникают проблемы с этой проблемой. Я попытался сузить круг конкретных причин, и лучшее, что я могу определить, - это то, что это происходит только в Ubuntu 12.04. Я перепробовал все последние версии на других серверах, и все работает нормально. Но каждый раз, когда я определяю функцию в ipython или использую% cpaste для вставки функции из другого файла, внутренняя часть этой функции не имеет доступа к глобальной области. Это делает практически невозможным что-либо полезное с точки зрения написания функций на лету.

У меня все еще возникает проблема с IPython 0.13, когда он вызывается из других инструментов (например, команды debugsqlshell в Django). Очень неприятно, что что-то столь простое, как определение функции, полностью не работает.

Я думаю, что embed () - неподходящий интерфейс для тех, кто использует. embed () - это
предназначен больше для изучения состояния запущенной программы, поэтому он использует
отдельные локальные и глобальные пространства имен. Чтобы обойти эту проблему, ipython
нужно запускать с помощью единого интерфейса. Извини, у меня не было времени
выяснить, как лучше всего это сделать.

Не только убунту. Debian wheezy также показывает это.

К вашему сведению, созданный выше билет Django @liokm - это https://code.djangoproject.com/ticket/18204 , который теперь указывает на https://code.djangoproject.com/ticket/17078 , который, похоже, был исправлен в багажнике. . Он должен приземлиться через 1.5.

У меня такая же проблема с Ubuntu с Ipython 0.13.2.
screenshot from 2013-08-07 18 13 33

@bkvirendra , исправленный в django 1.6

Но 1.6 еще даже не стабильна!

Но 1.6 еще даже не стабильна!

Программное обеспечение не всегда стабильно, и между выпусками все еще могут быть ошибки. Но в IPython нет ничего, что нужно было бы исправить. Даже если мы что-то сделаем здесь, исправления не будет в стабильной версии IPython до его выпуска ...

Проблема все еще сохраняется в ipython == 4.2.0. Что интересно, под окнами это не проблема, но вместе с глобальными переменными ubuntu они не распознаются.

Пример использования:

from ipywidgets import interact, FloatSlider, IntSlider,RadioButtons, Dropdown

@interact(sv1 = radio_1, Scenario = drop_1, sv2 = slider_2)
def update_map(sv1,sv2, Scenario):

Проблема остается. IPython 3.1.0, Debian Whezzy.

Если я правильно помню, это было исправлено на некоторое время и, похоже, было повторно введено (воспроизведено с использованием IPython 5.1.0 на macOS здесь: https://git.io/vPDrJ).

(править: возможно, я неправильно запомнил исправление. Возможно, я просто бросил все, что мне нужно, в файл запуска вместо использования встраивания)

На всякий случай это поможет кому-нибудь, я использовал этот фрагмент кода в качестве обходного пути.

    def fix_embed_globals(N=0):
        # Get the parent frame
        import inspect
        frame_level0 = inspect.currentframe()
        frame_cur = frame_level0
        N = 2  # I may have this value off by one. I rewrote this function to use only standard calls
        strict = False
        for _ix in range(N + 1):
            frame_next = frame_cur.f_back
            if frame_next is None:
                if strict:
                    raise AssertionError('Frame level %r is root' % _ix)
                else:
                    break
            frame_cur = frame_next
        parent_frame = frame_cur
        # Add locals to parent globals
        parent_frame.f_locals.update(parent_frame.f_globals)
        parent_frame.f_globals['_didfixipyglobals'] = True

Я просто вызываю эту функцию всякий раз, когда получаю ошибку имени. Он помещает всех локальных в текущий кадр в глобальный словарь. Его хакерский, но он работает в большинстве случаев.

Как указано на https://github.com/inducer/pudb/issues/103 , временный обходной путь при использовании встроенной оболочки: globals().update(locals()) (только после определения локальных глобальных переменных).

Хм, можно ли изменить веху этого бага на одну из следующих версий?

Готово.

это когда-нибудь будет исправлено?

AFAIK, мои комментарии, сделанные несколько лет назад:

  1. Я считаю, что мы ограничены тем, как работает сам Python. Замыкания и динамическая оценка несовместимы вместе, и IPython не может это исправить. Люди нашли обходные пути для перемещения локальных переменных в глобальное пространство имен, но это хаки, которые могут вызвать другие проблемы, поскольку они изменяют глобальное пространство имен. Я не планирую добавлять такие обходные пути в IPython; Я лучше оставлю проблему с тем, чем мы не занимаемся, чем представлю ее, пытаясь быть слишком умными.

  2. Во многих местах, где люди думали, что им нужно embed() , им следует использовать вместо них start_ipython() и избегать подобных проблем.

Я не понимаю, и это меня злит.

извините, я был разочарован тем, что вещи, которые будут работать, если вы напечатаете их в пустой файл .py, здесь не работали

но перечитывая историю, кажется, что это проблема с manage.py shell Django в частности, 90% времени я нахожусь в ipython, я делаю это, но легко забыть, что это так

стыдно, что я много работаю над устаревшей версией Django (1.4)

согласно предыдущему комментарию, более новые версии оболочки Django используют ipython по-другому и могут не иметь проблемы? например https://code.djangoproject.com/ticket/17078

извинения за недоразумение

Ага, я думаю, что это было исправлено для Django 1.6. Если вы действительно не можете выполнить обновление, возможно, вы захотите применить исправление вручную. Похоже, вот оно что: https://github.com/django/django/commit/3570ff734e93f493e023b912c9a97101f605f7f5

Сегодня я нашел обходной путь, опубликованный на # 10695 . В этом потоке показано более простое решение для случая, когда IPython не встроен в функцию. Я не эксперт, поэтому, пожалуйста, помогите проверить правильность.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги