Ipython: Variables globales indefinidas en el uso interactivo del shell ipython integrado

Creado en 10 may. 2010  ·  39Comentarios  ·  Fuente: ipython/ipython

Error original de Launchpad 399627: https://bugs.launchpad.net/ipython/+bug/399627
Informado por: h-fangohr (Hans Fangohr).

El error se puede reproducir de la siguiente manera:

  1. Inicie Python e inicie la sesión ipython incorporada. Siguiendo el manual de ipython, hacemos
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()

Dentro de la sesión ipython recién iniciada, las variables globales a veces no son visibles. Dos ejemplos son:

Ejemplo 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

Ejemplo 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

No hay ningún error si "b = 1; (lambda: b) ()" se coloca en un script y este script se ejecuta usando el comando "run" de ipython.

No hay error si "b = 1; (lambda: b) ()" se ejecuta de forma interactiva después de haber iniciado ipython.

La única forma en que aparece el error es si un shell IPython incrustado se inicia desde un indicador de Python Y los comandos se ejecutan de forma interactiva en el indicador.

Encuentro el mismo error al intentar ipython-0.9.1 (con python2.4).

Olivier Klein informó del error al equipo de nmag (http://nmag.soton.ac.uk).

bug

Comentario más útil

Como se indica en https://github.com/inducer/pudb/issues/103 , una solución temporal al usar un shell incrustado es: globals().update(locals()) (solo después de definir las variables globales locales).

Todos 39 comentarios

[Comentario LP 1 por: Fernando Perez, el 2010-04-25 23: 36: 38.673176 + 00: 00]

De acuerdo, puedo confirmar el problema (incluso en el maletero), pero es difícil. Solo por ahora estoy asegurándome de que este error esté confirmado para seguir rastreando, pero no estoy seguro de cómo solucionarlo.

El problema es que en los shells incrustados, intentamos actualizar el espacio de nombres global para usar el alcance circundante (ese es el objetivo de un shell incrustado, para poder ver lo que te rodea). Pero esto luego hace que Python no resuelva el espacio de nombres interactivo de ipython cuando se definen cosas anidadas (como funciones locales). Consulte el método mainloop () del shell incrustado para obtener más detalles.

Necesito pensar mucho más sobre cómo solucionar este problema, cualquier idea es muy bienvenida.

En la plataforma de lanzamiento ...

chairmanK escribió hace 20 horas:

Yo también. Además de las funciones, este error también aparece en las expresiones del generador.

El componente equivalente en el tronco parece ser IPython.frontend.terminal.InteractiveShellEmbed. Pero eso está roto de otras maneras y, evidentemente, no se ha probado mucho. ¿Alguien sabe cuál es su futuro?

¿Podría ser este el mismo problema que el número 136?

Esto ahora se ha solucionado:

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]: 

¿Alguien puede explicar un poco más en qué consistió esto? Todavía encuentro el problema, pero solo cuando me eliminan una capa a través de una llamada a un método, y solo si ejecuto desde un script, no de forma interactiva.

python 2.7.2 en OSX 10.6.8, ipython 0.11 y 0.12 exhiben un comportamiento similar (usando 0.12 para este comentario)

Este es un problema con nuestro proyecto que presenta (de manera prominente) un shell IPython integrado.

testembed.py

from IPython import embed

def hi():
    embed()

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

Ejecute esto en la línea de comando con python testembed.py y vea esta sesión:

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]: 

Sin embargo, comente la llamada a hi() y reemplácela con la llamada 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]: 

Después de hurgar, creo que tiene que ver con el parámetro stack_depth utilizado aquí y este bloque: https://github.com/ipython/ipython/blob/master/IPython/frontend/terminal/embed.py # L185

Pensamientos

Esto es algo complicado, pero creo que es una restricción en Python.

En el caso de error que muestra, time no se coloca realmente en el espacio de nombres global: debido a que llamó a embed dentro de la función hi , las nuevas variables que cree interactivamente son locales para eso función. Idealmente, tim() debería funcionar como un cierre, cerrando la referencia al módulo de tiempo. Sin embargo, los cierres solo parecen funcionar cuando la función contenedora se compila de una vez. Por lo que puedo decir, no hay forma de definir un cierre dinámicamente. Este simple ejemplo falla:

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

outer()()

Esto probablemente se deba a que los ámbitos anidados se agregaron a Python relativamente tarde (fueron una importación futura en 2.1 y siempre están activos en 2.2).

Ok, creo que lo entiendo. Parece que realmente no podemos hacer nada al respecto, aparte de descargar lo que escribiría de forma interactiva en un archivo y luego volver a leer ese archivo. Probablemente demasiado complejo para lo que queremos poder hacer de forma interactiva.

Gracias, por cierto.

Lamento seguir insistiendo aquí, pero hace que las sesiones interactivas se sientan muy torpes:

Pitón regular:

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

IPython regular:

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

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

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

IPython integrado:

>>> 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

Supongo que no se puede hacer mucho, pero no entiendo por qué podemos crear un cierre dinámico en python / ipython normal pero no en la versión incrustada.

Por las rarezas del alcance de Python, en Python / IPython estándar, en realidad no estamos creando un cierre. d es una variable global, y las reglas para acceder a ellas funcionan de manera diferente a los cierres. Cada función mantiene una referencia al espacio de nombres global donde se definió ( getkeys.func_globals ), por lo que puede acceder a cualquier variable definida allí.

Por el contrario, cuando realiza un cierre, Python adjunta referencias a cada variable sobre la que se ha cerrado, según lo determinado por el compilador, pero eso solo funciona cuando las funciones internas y externas se compilan al mismo tiempo. Se parece a esto:

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>,)

Esto posiblemente se haga para ahorrar memoria: si el cierre lleva una referencia al alcance local donde se definió, ninguna de las variables de ese alcance podría liberarse mientras el cierre estaba activo.

Estoy usando Python 2.7.2 e IPython 0.12.1 en OSX 10.7.3 y todavía tengo este problema. Cuando ejecuto ./manage.py shell de Django que invoca IPython.embed() , se produce el problema. Sin embargo, invocando manualmente embed() en el shell de Python o desde un archivo de script simple, no hay problema.

No hay mucho que podamos hacer directamente al respecto, pero probablemente deberíamos alentar a terceros a que se alejen de embed para usos no triviales.

@takluyver ¿Quiere decir que es mejor usar ipython directamente en este caso?

Es posible que Django inicie IPython de una manera que no cause este problema, pero esa no es la forma en que actualmente lo hacemos fácil. El problema ocurre cuando IPython comienza con espacios de nombres locales y globales separados. No hay ninguna razón por la que Django requiera espacios de nombres locales y globales separados, pero eso es lo que implica llamar a embed() dentro de una función.

Como referencia, aquí está el código en Django:
https://code.djangoproject.com/browser/django/trunk/django/core/management/commands/shell.py

@takluyver ¡ Eso tiene sentido, gracias! Estoy abriendo un ticket de Django para esto.

@takluyver , ahora que hemos fusionado embed_kernel para que todas las piezas principales estén en su lugar, ¿quieres abordar un poco una limpieza de esto para facilitar los usos un poco más precisos?

Echaré un vistazo a qué interfaz podría tener más sentido.

Todavía tengo problemas con este problema. Intenté reducir exactamente qué lo está causando, y lo mejor que puedo determinar es que solo ocurre en Ubuntu 12.04. Probé todas las últimas versiones en otros servidores y funciona bien. Pero cada vez que defino una función en ipython o uso% cpaste para pegar una función de otro archivo, el interior de esa función no tiene acceso al alcance global. Hace que sea básicamente imposible hacer algo útil en términos de funciones de escritura sobre la marcha.

Todavía tengo este problema con IPython 0.13 cuando se llama desde otras herramientas (por ejemplo, el comando debugsqlshell de Django). Es muy frustrante que algo tan básico como definir una función esté totalmente roto.

Creo que incrustar () es la interfaz incorrecta para los que están usando. embed () es
destinado más a examinar el estado de un programa en ejecución, por lo que utiliza
espacios de nombres locales y globales separados. Para solucionar este problema, ipython
debe iniciarse con una única interfaz. Lo siento, no he tenido tiempo de
averigüe cuál es la mejor manera de hacerlo.

No solo ubuntu. debian wheezy también muestra eso.

Para su información, el ticket de Django @liokm creado anteriormente es https://code.djangoproject.com/ticket/18204 , que ahora apunta a https://code.djangoproject.com/ticket/17078 , que parece haber sido corregido en el tronco . Debería aterrizar en 1.5.

Tengo el mismo problema en Ubuntu con Ipython 0.13.2
screenshot from 2013-08-07 18 13 33

@bkvirendra que está arreglado en django 1.6

¡Pero 1.6 ni siquiera es estable todavía!

¡Pero 1.6 ni siquiera es estable todavía!

El software no siempre es estable y, entre versiones, es posible que todavía haya errores. Pero no hay nada que deba solucionarse en IPython. Incluso si hacemos algo aquí, la solución tampoco estaría en IPython estable antes de su lanzamiento ...

El problema aún persiste en ipython == 4.2.0. Curiosamente, en Windows no hay problema, pero junto con ubuntu, las variables globales no se reconocen.

Caso de uso:

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

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

El problema persiste. IPython 3.1.0, Debian Whezzy.

Si mal no recuerdo, esto se solucionó por un tiempo y parece que se reintrodujo (reproducido usando IPython 5.1.0 en macOS aquí: https://git.io/vPDrJ).

(editar: puede que esté recordando mal la solución. Es posible que haya arrojado todo lo que necesitaba en un archivo de inicio en lugar de usar incrustar)

En caso de que esto ayude a alguien, he estado usando este código como solución alternativa

    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

Solo llamo a esa función cada vez que obtengo el error de nombre. Pone todos los locales en su marco actual en el diccionario global. Es hacky pero funciona en la mayoría de circunstancias.

Como se indica en https://github.com/inducer/pudb/issues/103 , una solución temporal al usar un shell incrustado es: globals().update(locals()) (solo después de definir las variables globales locales).

Hmm, ¿se puede cambiar el hito de este error a una de las próximas versiones?

Hecho.

¿Se solucionará esto alguna vez?

AFAIK, mis comentarios de hace unos años son:

  1. Creo que estamos limitados por la forma en que Python funciona. Los cierres y la evaluación dinámica no funcionan bien juntos, e IPython no puede solucionarlo. La gente ha encontrado soluciones alternativas para mover variables locales a un espacio de nombres global, pero esos son hacks que pueden causar otros problemas, porque están modificando el espacio de nombres global. No planeo poner tales soluciones en IPython; Prefiero dejar un problema con algo que no estamos haciendo que presentar uno tratando de ser demasiado inteligente.

  2. En muchos lugares donde la gente pensaba que querían embed() , en realidad deberían usar start_ipython() y evitar este tipo de problema.

No entiendo y me enoja.

lo siento, me frustró que las cosas que funcionarían si las escribiera en un archivo .py vacío no funcionaran aquí

pero releyendo el historial, parece que este es un problema con el manage.py shell Django específicamente, el 90% del tiempo que estoy en ipython lo hago, pero es fácil olvidar que ese es el caso

vergonzosamente estoy trabajando mucho en la versión obsoleta de Django (1.4)

de acuerdo con el comentario anterior, las versiones más nuevas de Django shell usan ipython de manera diferente y es posible que no tengan el problema. por ejemplo, https://code.djangoproject.com/ticket/17078

disculpas por el malentendido

Sí, creo que se solucionó para Django 1.6. Si realmente no puede actualizar, es posible que desee aplicar la corrección manualmente. Parece que esto fue todo: https://github.com/django/django/commit/3570ff734e93f493e023b912c9a97101f605f7f5

Encontré una solución alternativa hoy que se publica en # 10695 . En este hilo se muestra un tratamiento más simple para el caso en el que IPython no está incrustado dentro de una función. No soy un experto, así que ayúdenme a comprobar la validez.

¿Fue útil esta página
0 / 5 - 0 calificaciones