Question

J'ai un fichier doctestable assez basique:

class Foo():
    """
    >>> 3+2
    5
    """

if __name__ in ("__main__", "__console__"):
    import doctest
    doctest.testmod(verbose=True)

qui fonctionne comme prévu lorsqu'il est exécuté directement par python.

Cependant, ipython, je reçois

1 items had no tests:
    __main__
0 tests in 1 items.
0 passed and 0 failed.
Test passed.

Étant donné que cela fait partie d'un projet Django et aura besoin d'accéder à toutes les variables appropriées et tel que manage.py met en place, je peux aussi l'exécuter par une commande modifiée, qui utilise de résultat code.InteractiveConsole, celui qui est __name__ se prépare à '__console__'.

Avec le code ci-dessus, j'obtenir le même résultat qu'avec ipython. J'ai essayé de changer la dernière ligne à ceci:

 this = __import__(__name__)
 doctest.testmod(this, verbose=True)

et je reçois un ImportError sur __console__, ce qui est logique, je suppose. Cela n'a aucun effet sur python ou ipython.

Alors, je voudrais être en mesure d'exécuter avec succès doctests par trois de ces méthodes, en particulier l'un InteractiveConsole, car je pense être besoin de magie poney Django assez rapidement.

Juste pour clarifier, voici ce que je attends:

Trying:
    3+2
Expecting:
    5
ok
1 items had no tests:
    __main__
1 items passed all tests:
   1 tests in __main__.Foo
1 tests in 2 items.
1 passed and 0 failed.
Test passed.
Était-ce utile?

La solution

Les travaux suivants:

$ ipython
...
In [1]: %run file.py

Trying:
    3+2
Expecting:
    5
ok
1 items had no tests:
    __main__
1 items passed all tests:
   1 tests in __main__.Foo
1 tests in 2 items.
1 passed and 0 failed.
Test passed.

In [2]: 

Je ne sais pas pourquoi ipython file.py ne fonctionne pas. Mais ce qui précède est au moins une solution de contournement.

EDIT:

J'ai trouvé la raison pour laquelle il ne fonctionne pas. Il est assez simple:

  • Si vous ne spécifiez pas le module à tester en doctest.testmod(), il suppose que vous voulez tester le module __main__.
  • Lorsque IPython exécute le fichier qui lui est passé sur la ligne de commande, le module __main__ est le __main__ de IPython, pas votre module. Alors doctest tente d'exécuter doctests dans le script d'entrée de IPython.

Les travaux suivants, mais se sent un peu bizarre:

if __name__ == '__main__':
    import doctest
    import the_current_module
    doctest.testmod(the_current_module)

Donc, fondamentalement, les importations de module lui-même (c'est la partie « se sent un peu bizarre »). Mais cela fonctionne. Quelque chose que je n'aime pas vers. cette approche est que chaque module doit inclure son propre nom dans la source.

EDIT 2:

Le script suivant, ipython_doctest, fait ipython se comportent de la façon dont vous voulez:

#! /usr/bin/env bash

echo "__IP.magic_run(\"$1\")" > __ipython_run.py
ipython __ipython_run.py

Le script crée un script python qui exécutera %run argname dans IPython.

Exemple:

$ ./ipython_doctest file.py
Trying:
    3+2
Expecting:
    5
ok
1 items had no tests:
    __main__
1 items passed all tests:
   1 tests in __main__.Foo
1 tests in 2 items.
1 passed and 0 failed.
Test passed.
Python 2.5 (r25:51908, Mar  7 2008, 03:27:42) 
Type "copyright", "credits" or "license" for more information.

IPython 0.9.1 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object'. ?object also works, ?? prints more.

In [1]:

Autres conseils

Le problème de la racine est que ipython joue des tours étranges avec __main__ (à travers son propre module FakeModule) de sorte que, au moment où doctest est Introspection que « module allégué » par son __dict__, Foo est PAS il -. donc ne doctest rECURSE pas en elle

Voici une solution:

class Foo():
    """
    >>> 3+2
    5
    """

if __name__ in ("__main__", "__console__"):
    import doctest, inspect, sys
    m = sys.modules['__main__']
    m.__test__ = dict((n,v) for (n,v) in globals().items()
                            if inspect.isclass(v))
    doctest.testmod(verbose=True)

Cette approche produit, comme l'a demandé:

$ ipython dot.py 
Trying:
    3+2
Expecting:
    5
ok
1 items had no tests:
    __main__
1 items passed all tests:
   1 tests in __main__.__test__.Foo
1 tests in 2 items.
1 passed and 0 failed.
Test passed.
Python 2.5.1 (r251:54863, Feb  6 2009, 19:02:12) 
  [[ snip snip ]]
In [1]: 

La seule définition __test__ globale ne fonctionne pas, encore une fois parce que le définir comme une approche globale de ce que vous pensez comme __main__ ne le fait pas dans le __dict__ de l'objet réel qui obtient récupéré par m = sys.modules['__main__'], et celui-ci est exactement l'expression doctest utilise en interne (en fait il utilise sys.modules.get, mais la précaution supplémentaire n'est pas nécessaire ici car nous savons que __main__ existe dans sys.modules ... il est tout simplement pas l'objet que vous attendez qu'il soit -!)

En outre, la mise juste m.__test__ = globals() ne directement fonctionne pas non plus, pour une autre raison: vérifie doctest que les valeurs __test__ sont des chaînes, des fonctions, des classes ou des modules, et sans une certaine sélection vous ne pouvez pas garantir que globals() satisfera cette condition ( en fait, il ne sera pas). Ici, je sélectionne seulement des classes, si vous voulez aussi des fonctions ou autres joyeusetés vous pouvez utiliser un or dans la clause if dans le Genexp au sein de l'appel dict.

Je ne sais pas exactement comment vous utilisez un shell Django qui est capable d'exécuter votre script (comme je crois python manage.py shell n'accepte pas d'arguments, vous devez faire quelque chose d'autre, et je ne peux pas deviner exactement quoi! -), mais une approche similaire devrait aider (si votre shell Django utilise ipython, la valeur par défaut lorsqu'il est disponible, ou Python ordinaire): réglage approprié __test__ dans l'objet que vous obtenez comme sys.modules['__main__'] (ou __console__, si c'est ce que vous êtes alors passer à doctest.testmod, je suppose) devrait fonctionner, car il imite ce doctest sera alors en train de faire en interne pour localiser vos chaînes de test.

Et, pour conclure, une réflexion philosophique sur le design, l'architecture, la simplicité, la transparence et la "magie noire" ...:

Tous ces efforts est essentiellement ce qui est nécessaire pour vaincre la « magie noire » qui ipython (et peut-être Django, mais il peut être une simple délégation partie à ipython) est en train de faire en votre nom pour votre « commodité » ... tout heure à laquelle deux cadres (ou plus ;-) font indépendamment chacun sa propre marque de magie noire, l'interopérabilité peut nécessiter soudainement des efforts importants et de devenir tout sauf pratique; -).

Je ne dis pas que la même pratique aurait pu être fournie (par un ou plusieurs des ipython, django et / ou doctests) sans magie noire, introspection, modules de faux, etc. ; les concepteurs et les mainteneurs de chacun de ces cadres sont superbes ingénieurs, et je suppose qu'ils ont fait leurs devoirs à fond, et accomplissent seulement le montant minimum de la magie noire qui est indispensable pour fournir la quantité de facilité d'utilisation, ils ont décidé qu'ils avaient besoin. , Même dans une telle situation, la « magie noire » se transforme soudain pourtant d'un rêve de convenance à un cauchemar de débogage dès que vous voulez faire quelque chose, même légèrement en dehors de ce avait conçu l'auteur du cadre.

OK, peut-être dans ce cas pas tout à fait cauchemar, mais je remarque que cette question a été ouvert un certain temps et même avec l'attrait de la prime, il n'a pas obtenu de réponses encore - si vous maintenant ont deux réponses à choisir de, la mienne en utilisant la __test__ caractéristique particulière de doctest, @ en utilisant la fonction particulière de __IP.magic_run de IronPython de codeape. Je préfère le mien parce qu'il ne repose pas sur quoi que ce soit interne ou en situation irrégulière - __test__ EST un fe documentéature de doctest, alors que __IP, avec ces deux grands qui se profilent underscores, crier « Fonctionnement profond, ne pas toucher » pour moi; -) ... si elle se casse à la prochaine version de point que je ne serais pas du tout surpris. Pourtant, question de goût -. Cette réponse peut sans doute être considéré comme plus « pratique »

Mais, c'est exactement mon point: la commodité peut venir à un prix énorme en termes de renoncer à la simplicité, la transparence, et / ou éviter les caractéristiques internes / sans-papiers / instables; ainsi, comme une leçon pour nous tous, le moins la magie noire et c nous pouvons sortir avec (même au prix de donner ici un epsilon de commodité et de là-bas), plus heureux nous serons tous à la longue (et plus heureux que nous allons faire d'autres développeurs qui ont besoin de tirer parti de nos efforts actuels dans l'avenir).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top