30 votes

L'impression sans parenthèses variant message d'erreur à l'aide de Python 3

Lorsque j'essaie d'utiliser print sans parenthèses sur un nom simple en Python 3.4-je obtenir:

>>> print max
Traceback (most recent call last):
  ...
  File "<interactive input>", line 1
    print max
            ^
SyntaxError: Missing parentheses in call to 'print'

Ok, je comprends maintenant, j'ai juste oublié de port mon Python 2 code.

Mais maintenant, quand j'essaie d'imprimer le résultat d'une fonction:

>>> print max([1,2])
Traceback (most recent call last):
    ...
    print max([1,2])
            ^
SyntaxError: invalid syntax

Ou:

print max.__call__(23)
        ^
SyntaxError: invalid syntax

(Notez que le curseur vers le caractère avant le premier point dans ce cas.)

Le message est différent (et un peu trompeuse, étant donné que le marqueur est en dessous de la max de la fonction).

Pourquoi n'est-ce pas Python capable de détecter le problème plus tôt?

Remarque: Cette question a été inspiré par la confusion autour de cette question: les Pandas lire.csv erreur de syntaxe, où un peu de Python experts raté le réel problème en raison de l'induire en erreur message d'erreur.

28voto

alkasm Points 10701

En regardant le code source pour exceptions.c, juste au-dessus de _set_legacy_print_statement_msg il y a ce joli bloc de commentaire:

/* To help with migration from Python 2, SyntaxError.__init__ applies some
 * heuristics to try to report a more meaningful exception when print and
 * exec are used like statements.
 *
 * The heuristics are currently expected to detect the following cases:
 *   - top level statement
 *   - statement in a nested suite
 *   - trailing section of a one line complex statement
 *
 * They're currently known not to trigger:
 *   - after a semi-colon
 *
 * The error message can be a bit odd in cases where the "arguments" are
 * completely illegal syntactically, but that isn't worth the hassle of
 * fixing.
 *
 * We also can't do anything about cases that are legal Python 3 syntax
 * but mean something entirely different from what they did in Python 2
 * (omitting the arguments entirely, printing items preceded by a unary plus
 * or minus, using the stream redirection syntax).
 */

Donc, il y a quelques infos intéressantes. De plus, en SyntaxError_init méthode dans le même fichier, nous pouvons voir

    /*
     * Issue #21669: Custom error for 'print' & 'exec' as statements
     *
     * Only applies to SyntaxError instances, not to subclasses such
     * as TabError or IndentationError (see issue #31161)
     */
    if ((PyObject*)Py_TYPE(self) == PyExc_SyntaxError &&
            self->text && PyUnicode_Check(self->text) &&
            _report_missing_parentheses(self) < 0) {
        return -1;
    }

Notez également que les références ci-dessus, le numéro 21669 sur le python bugtracker avec une discussion entre l'auteur et Guido sur la façon d'aller à ce sujet. Ainsi nous suivons le lapin (c'est - _report_missing_parentheses) qui est à la fin du fichier, et de voir...

legacy_check_result = _check_for_legacy_statements(self, 0);

Cependant, il existe certains cas où c'est ignorée et la normale SyntaxError message est imprimé, voir MSeifert de réponse de plus à ce sujet. Si nous allons jusqu'à _check_for_legacy_statements nous enfin voir le chèque pour l'héritage des instructions d'impression.

/* Check for legacy print statements */
if (print_prefix == NULL) {
    print_prefix = PyUnicode_InternFromString("print ");
    if (print_prefix == NULL) {
        return -1;
    }
}
if (PyUnicode_Tailmatch(self->text, print_prefix,
                        start, text_len, -1)) {

    return _set_legacy_print_statement_msg(self, start);
}

Donc, pour répondre à la question: "Pourquoi n'est-ce pas Python capable de détecter le problème plus tôt?", Je dirais que le problème avec des parenthèses n'est pas ce qui est détecté; il est réellement analysée après l'erreur de syntaxe. C'est une erreur de syntaxe tout le temps, mais la pièce mineure à propos de parenthèses est pris par la suite juste pour donner un indice supplémentaire.

17voto

MSeifert Points 6307

La spéciale de message d'exception pour print utilisé comme instruction au lieu d'où la fonction est effectivement mis en œuvre comme un cas spécial.

En gros quand un SyntaxError est créée, elle appelle une fonction spéciale qui vérifie print déclaration basé sur la ligne de l'exception se réfère.

Toutefois, le premier test de cette fonction (le responsable de la "parenthèse Manquante" message d'erreur) est si il existe une parenthèse d'ouverture dans la ligne. J'ai copié le code source de la fonction (Disponible 3.6.4) et j'ai marqué les lignes avec des "flèches":

static int
_report_missing_parentheses(PySyntaxErrorObject *self)
{
    Py_UCS4 left_paren = 40;
    Py_ssize_t left_paren_index;
    Py_ssize_t text_len = PyUnicode_GET_LENGTH(self->text);
    int legacy_check_result = 0;

    /* Skip entirely if there is an opening parenthesis <---------------------------- */
    left_paren_index = PyUnicode_FindChar(self->text, left_paren,
                                          0, text_len, 1);
    if (left_paren_index < -1) {
        return -1;
    }
    if (left_paren_index != -1) {
        /* Use default error message for any line with an opening parenthesis <------------ */
        return 0;
    }
    /* Handle the simple statement case */
    legacy_check_result = _check_for_legacy_statements(self, 0);
    if (legacy_check_result < 0) {
        return -1;

    }
    if (legacy_check_result == 0) {
        /* Handle the one-line complex statement case */
        Py_UCS4 colon = 58;
        Py_ssize_t colon_index;
        colon_index = PyUnicode_FindChar(self->text, colon,
                                         0, text_len, 1);
        if (colon_index < -1) {
            return -1;
        }
        if (colon_index >= 0 && colon_index < text_len) {
            /* Check again, starting from just after the colon */
            if (_check_for_legacy_statements(self, colon_index+1) < 0) {
                return -1;
            }
        }
    }
    return 0;
}

Cela signifie qu'il ne déclenchent pas la "parenthèse Manquante" le message si il n'y a aucune ouverture de la parenthèse dans la ligne. Qui mène à la générale SyntaxError message, même si l'ouverture de la parenthèse est dans un commentaire:

print 10  # what(
    print 10  # what(
           ^
SyntaxError: invalid syntax

Notez que la position du curseur pour deux noms de variables séparées par un espace blanc est toujours la fin de la deuxième nom:

>>> 10 100
    10 100
         ^
SyntaxError: invalid syntax

>>> name1 name2
    name1 name2
              ^
SyntaxError: invalid syntax

>>> name1 name2([1, 2])
    name1 name2([1, 2])
              ^
SyntaxError: invalid syntax

Il n'est donc pas étonnant que le curseur pointe sur l' x de max, parce que c'est le dernier caractère de la deuxième nom. Tout ce qui suit le deuxième nom (comme ., (, [, ...) est ignoré, parce que Python déjà trouvé un SyntaxError, et il n'a pas besoin d'aller plus loin, parce que rien ne pourrait la rendre valide la syntaxe.

4voto

jdehesa Points 22254

Peut-être que je ne suis pas à comprendre quelque chose, mais je ne vois pas pourquoi Python convient de souligner l'erreur plus tôt. print est une fonction régulière, qui est une variable fait référence à une fonction, donc ce sont tous des instructions valides:

print(10)
print, max, 2
str(print)
print.__doc__
[print] + ['a', 'b']
{print: 2}

Si je comprends bien, l'analyseur a besoin de lire le prochain jeton après print (max dans ce cas) afin de déterminer s'il y a une erreur de syntaxe. Il ne suffit pas de dire "l'échec si il n'y a pas de parenthèse ouvrante", parce qu'il y a un certain nombre de jetons différents qui peuvent aller après print selon le contexte actuel.

Je ne pense pas qu'il existe un cas où l' print peut être directement suivie par un autre identifiant ou un littéral, donc on pourrait dire que dès qu'il y a une lettre, un chiffre ou un devis, vous devez arrêter, mais que serait le mixage de l'analyseur syntaxique et l'analyseur lexical du travail.

4voto

Jean-François Fabre Points 94672

dans les ajouts à ces excellentes réponses, sans même regarder le code source, on peut facilement imaginer que l' print spécial message d'erreur était une bidouille:

donc:

print dfjdkf
           ^
SyntaxError: Missing parentheses in call to 'print'

mais:

>>> a = print
>>> a dsds
Traceback (most recent call last):
  File "<interactive input>", line 1
    a dsds
         ^
SyntaxError: invalid syntax

même si a == print mais à ce stade, il n'a pas encore été évalué, de sorte que vous obtenez le générique de syntaxe non valide message au lieu de l'piraté print de la syntaxe de message, ce qui prouve qu'il y a une bidouille quand le premier jeton est - print.

une autre preuve, si besoin:

>>> print = None
>>> print a
Traceback (most recent call last):
  File "C:\Python34\lib\code.py", line 63, in runsource
    print a
          ^
SyntaxError: Missing parentheses in call to 'print'

dans ce cas print == None, mais le message s'affiche toujours.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X