Ipython: Variables globales non définies dans l'utilisation interactive du shell ipython intégré

Créé le 10 mai 2010  ·  39Commentaires  ·  Source: ipython/ipython

Bogue original du Launchpad 399627: https://bugs.launchpad.net/ipython/+bug/399627
Rapporté par: h-fangohr (Hans Fangohr).

L'erreur peut être reproduite comme suit:

  1. Démarrez Python et démarrez la session ipython intégrée. En suivant le manuel d'ipython, nous faisons
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()

Dans la session ipython qui vient de démarrer, les variables globales ne sont parfois pas visibles. Deux exemples sont:

Exemple 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

Exemple 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

Il n'y a pas d'erreur si "b = 1; (lambda: b) ()" est placé dans un script, et ce script est exécuté en utilisant la commande "run" d'ipython.

Il n'y a pas d'erreur si "b = 1; (lambda: b) ()" est exécuté de manière interactive après avoir démarré ipython.

La seule façon dont l'erreur apparaît est si un shell IPython intégré est démarré à partir d'une invite Python ET que les commandes sont exécutées de manière interactive à l'invite.

Je trouve la même erreur en essayant ipython-0.9.1 (avec python2.4).

Le bug a été rapporté par Olivier Klein à l'équipe nmag (http://nmag.soton.ac.uk).

bug

Commentaire le plus utile

Comme indiqué sur https://github.com/inducer/pudb/issues/103 , une solution de contournement temporaire lors de l'utilisation d'un shell intégré est: globals().update(locals()) (après avoir défini les variables globales localement, uniquement).

Tous les 39 commentaires

[Commentaire LP 1 de: Fernando Perez, le 25/04/2010 23: 36: 38.673176 + 00: 00]

OK, je peux confirmer le problème (même sur le coffre), mais c'est difficile. Je suis juste pour le moment m'assurer que ce bogue est confirmé afin que nous continuions à le suivre, mais je ne sais pas comment le corriger.

Le problème est que dans les shells intégrés, nous essayons de mettre à jour l'espace de noms global pour utiliser la portée environnante (c'est le but d'un shell intégré, pour pouvoir voir ce qui vous entoure). Mais cela provoque alors l'échec de python à résoudre l'espace de noms interactif ipython lorsque des éléments imbriqués (comme des fonctions locales) sont définis. Voir la méthode mainloop () du shell intégré pour plus de détails.

Je dois réfléchir beaucoup plus à la façon de résoudre celui-ci, toutes les idées sont les bienvenues.

Au Launchpad ...

chairmanK a écrit il y a 20 heures:

Moi aussi. En plus des fonctions, ce bogue apparaît également dans les expressions du générateur.

Le composant équivalent dans trunk semble être IPython.frontend.terminal.InteractiveShellEmbed. Mais c'est cassé d'autres manières, et évidemment n'a pas été beaucoup testé. Quelqu'un sait-il quel est son avenir?

Cela pourrait-il être le même problème que le n ° 136?

Cela a été corrigé:

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

Quelqu'un peut-il expliquer un peu plus ce qui s'est passé dans le correctif de ce problème? Je rencontre toujours le problème, mais seulement lorsque je suis une couche supprimée via un appel de méthode, et seulement si je lance à partir d'un script, pas de manière interactive.

python 2.7.2 sur OSX 10.6.8, ipython 0.11 et 0.12 présentent tous deux un comportement similaire (en utilisant 0.12 pour ce commentaire)

C'est un problème avec notre projet qui comporte (en évidence) un shell IPython intégré.

testembed.py

from IPython import embed

def hi():
    embed()

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

Exécutez ceci sur la ligne de commande avec python testembed.py et voyez cette session:

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

Cependant, commentez l'appel à hi() et remplacez-le par l'appel 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]: 

Après avoir fouillé, je pense que cela a à voir avec le paramètre stack_depth utilisé ici et ce bloc: https://github.com/ipython/ipython/blob/master/IPython/frontend/terminal/embed.py # L185

Pensées?

C'est un peu compliqué, mais je pense que c'est une restriction en Python lui-même.

Dans le cas d'échec que vous montrez, time n'est pas réellement mis dans l'espace de noms global: parce que vous avez appelé embed à l'intérieur de la fonction hi , les nouvelles variables que vous créez interactivement sont locales à cela fonction. Idéalement, tim() devrait fonctionner comme une fermeture, se refermant sur la référence au module de temps. Cependant, les fermetures ne semblent fonctionner que lorsque la fonction contenant est compilée en une seule fois. Pour autant que je sache, il n'y a aucun moyen de définir une fermeture de manière dynamique. Cet exemple simple échoue:

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

outer()()

Ceci est probablement dû au fait que les portées imbriquées ont été ajoutées à Python relativement tard (elles étaient une future importation dans la version 2.1, et toujours active dans la version 2.2).

Ok, je pense que je comprends. On dirait que nous ne pouvons pas vraiment faire quoi que ce soit à ce sujet, à part transférer ce que j'écrirais de manière interactive dans un fichier puis le relire. Probablement trop complexe pour ce que nous voulons pouvoir faire de manière interactive.

Merci, btw.

Désolé de continuer à parler ici, mais cela rend les sessions interactives très maladroites:

Python régulier:

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

IPython régulier:

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

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

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

IPython intégré:

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

Je suppose qu'il n'y a pas grand-chose à faire, mais je ne comprends pas pourquoi nous pouvons créer une fermeture dynamique en python / ipython classique mais pas dans la version intégrée.

Par les bizarreries de la portée de Python, en Python / IPython standard, nous ne créons pas réellement de fermeture. d est une variable globale, et les règles d'accès à celles-ci fonctionnent différemment des fermetures. Chaque fonction garde une référence à l'espace de noms global où elle a été définie ( getkeys.func_globals ), afin qu'elle puisse accéder à toutes les variables qui y sont définies.

En revanche, lorsque vous effectuez une fermeture, Python attache des références à chaque variable sur laquelle il s'est fermé, comme déterminé par le compilateur - mais cela ne fonctionne que lorsque les fonctions interne et externe sont compilées en même temps. Cela ressemble à ceci:

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

Ceci est peut-être fait pour économiser de la mémoire - si la fermeture portait une référence à la portée locale où elle a été définie, aucune des variables de cette portée ne pourrait être libérée pendant que la fermeture était active.

J'utilise Python 2.7.2 et IPython 0.12.1 sur OSX 10.7.3 et j'ai toujours ce problème. Lorsque j'exécute ./manage.py shell de Django qui appelle IPython.embed() , le problème se produit. Cependant, en invoquant manuellement le embed() dans le shell Python ou à partir d'un simple fichier de script, il n'y a pas de problème.

Nous ne pouvons pas faire grand-chose directement à ce sujet, mais nous devrions probablement encourager les tiers à s'éloigner de embed pour des utilisations non triviales.

@takluyver Voulez-vous dire qu'il vaut mieux utiliser ipython directement dans ce cas?

Il est possible pour Django de démarrer IPython d'une manière qui ne causera pas ce problème, mais ce n'est pas ainsi que nous facilitons actuellement. Le problème se produit lorsque IPython démarre avec des espaces de noms locaux et globaux distincts. Il n'y a aucune raison pour que Django nécessite des espaces de noms locaux et globaux séparés, mais c'est ce qu'implique l'appel de embed() intérieur d'une fonction.

Pour référence, voici le code dans Django:
https://code.djangoproject.com/browser/django/trunk/django/core/management/commands/shell.py

@takluyver Cela a du sens, merci! J'ouvre un ticket Django pour cela.

@takluyver , maintenant que nous avons fusionné embed_kernel pour que toutes les pièces principales soient en place, voulez-vous vous attaquer un peu à un nettoyage de cela pour faciliter les utilisations un peu plus affinées?

Je vais jeter un oeil à quelle interface pourrait avoir le plus de sens.

J'ai toujours des problèmes avec ce problème. J'ai essayé de préciser exactement ce qui la cause, et le mieux que je puisse déterminer est que cela ne se produit que sur Ubuntu 12.04. J'ai essayé toutes les dernières versions sur d'autres serveurs et cela fonctionne bien. Mais chaque fois que je définis une fonction dans ipython ou que j'utilise% cpaste pour coller une fonction à partir d'un autre fichier, l'intérieur de cette fonction n'a pas accès à la portée globale. Cela rend pratiquement impossible de faire quoi que ce soit d'utile en termes d'écriture de fonctions à la volée.

J'ai toujours ce problème avec IPython 0.13 quand il est appelé à partir d'autres outils (par exemple la commande debugsqlshell de Django). C'est très frustrant que quelque chose d'aussi basique que de définir une fonction soit totalement cassé.

Je pense que embed () est la mauvaise interface pour ceux qui l'utilisent. embed () est
destiné davantage à examiner l'état d'un programme en cours d'exécution, il utilise donc
espaces de noms locaux et globaux séparés. Pour contourner ce problème, ipython
doit être démarré avec une seule interface. Désolé, je n'ai pas eu le temps de
déterminer quelle est la meilleure façon de le faire.

Pas seulement ubuntu. debian wheezy le montre également.

Pour info, le ticket Django @liokm créé ci-dessus est https://code.djangoproject.com/ticket/18204 , qui pointe maintenant vers https://code.djangoproject.com/ticket/17078 , qui semble avoir été corrigé dans le coffre . Il devrait atterrir en 1.5.

J'ai le même problème sur Ubuntu avec Ipython 0.13.2
screenshot from 2013-08-07 18 13 33

@bkvirendra qui est corrigé dans django 1.6

Mais 1.6 n'est même pas encore stable!

Mais 1.6 n'est même pas encore stable!

Les logiciels ne sont pas toujours stables et entre les versions, il peut encore y avoir des bogues. Mais il n'y a rien qui devrait être corrigé dans IPython. Même si nous faisons quelque chose ici, le correctif ne serait pas non plus stable dans IPython avant sa publication ...

Le problème persiste dans ipython == 4.2.0. Fait intéressant sous Windows, ce n'est pas un problème, mais avec les variables globales ubuntu ne sont pas reconnues.

Cas d'utilisation:

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

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

Le problème demeure. IPython 3.1.0, Debian Whezzy.

Si je me souviens bien, cela a été corrigé pendant un petit moment et semble être réintroduit (reproduit avec IPython 5.1.0 sur macOS ici: https://git.io/vPDrJ).

(modifier: je me souviens peut-être mal du correctif. Il est possible que je jette tout ce dont j'avais besoin dans un fichier de démarrage au lieu d'utiliser incorporer)

Juste au cas où cela aiderait quiconque, j'ai utilisé ce morceau de code comme solution de contournement

    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

J'appelle simplement cette fonction chaque fois que j'obtiens l'erreur de nom. Il place tous les locaux de votre image actuelle dans le dictionnaire global. C'est piraté mais cela fonctionne dans la plupart des circonstances.

Comme indiqué sur https://github.com/inducer/pudb/issues/103 , une solution de contournement temporaire lors de l'utilisation d'un shell intégré est: globals().update(locals()) (après avoir défini les variables globales localement, uniquement).

Hmm, le jalon de ce bogue peut-il être remplacé par l'une des prochaines versions?

Terminé.

cela sera-t-il jamais réglé?

AFAIK, mes commentaires d'il y a quelques années sont:

  1. Je pense que nous sommes limités par la façon dont Python fonctionne. Les fermetures et l'évaluation dynamique ne fonctionnent pas bien ensemble et IPython ne peut pas y remédier. Les gens ont trouvé des solutions de contournement pour déplacer des variables locales vers un espace de noms global, mais ce sont des hacks qui peuvent causer d'autres problèmes, car ils modifient l'espace de noms global. Je n'ai pas l'intention de mettre de telles solutions de contournement dans IPython; Je préfère laisser un problème avec quelque chose que nous ne faisons pas que d'en introduire un en essayant d'être trop intelligent.

  2. De nombreux endroits où les gens pensaient vouloir embed() , ils devraient en fait utiliser start_ipython() place et éviter ce genre de problème.

Je ne comprends pas et cela me met en colère.

désolé, j'étais frustré que les choses qui fonctionneraient si vous les saisissiez dans un fichier .py vide ne fonctionnaient pas ici

mais en relisant l'historique, il semble que ce soit un problème avec les manage.py shell Django en particulier, 90% du temps je suis en ipython je le fais mais il est facile d'oublier que c'est le cas

embarrassant je travaille beaucoup dans la version obsolète de Django (1.4)

selon le commentaire précédent, les nouvelles versions du shell Django utilisent ipython différemment et peuvent ne pas avoir le problème? par exemple https://code.djangoproject.com/ticket/17078

excuses pour le malentendu

Oui, je pense que cela a été corrigé pour Django 1.6. Si vous ne pouvez vraiment pas mettre à niveau, vous souhaiterez peut-être appliquer le correctif manuellement. On dirait que c'était ça: https://github.com/django/django/commit/3570ff734e93f493e023b912c9a97101f605f7f5

J'ai trouvé une solution de contournement aujourd'hui qui est publiée au # 10695 . Un traitement plus simple pour le cas où l'IPython n'est pas incorporé dans une fonction est montré à ce thread . Je ne suis pas un expert, veuillez donc aider à vérifier la validité.

Cette page vous a été utile?
0 / 5 - 0 notes