Imprimer sans parenthèses messages d'erreur variables en utilisant Python 3
quand j'essaie d'utiliser print
sans parenthèses sur un nom simple en Python 3.4 je reçois:
>>> print max
Traceback (most recent call last):
...
File "<interactive input>", line 1
print max
^
SyntaxError: Missing parentheses in call to 'print'
Ok, maintenant j'ai compris, j'ai juste oublié de porter mon code Python 2.
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 le message est différent (et légèrement trompeur, puisque le marqueur est en dessous de la fonction max
).
pourquoi Python n'est-il pas capable de détecter le problème plus tôt?
Note: cette question a été inspirée par la confusion autour de cette question: Pandas lire.erreur de syntaxe csv , où quelques experts de Python ont manqué le vrai problème à cause du message d'erreur trompeur.
4 réponses
en regardant le code source pour exceptions.c
, juste au-dessus de _set_legacy_print_statement_msg
il y a ce joli commentaire de bloc:
/* 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 des infos intéressantes. En outre, dans la méthode SyntaxError_init
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 aussi que les références ci-dessus numéro # 21669 sur le bugtracker python avec quelques discussions entre l'auteur et Guido sur comment aller à ce sujet. Nous suivons donc le lapin (c'est-à-dire _report_missing_parentheses
) qui est tout en bas du dossier, et voir...
legacy_check_result = _check_for_legacy_statements(self, 0);
cependant, il y a des cas où cela est contourné et le message normal SyntaxError
est imprimé, voir réponse de MSeifert pour plus d'informations à ce sujet. Si nous allons d'une fonction jusqu'à _check_for_legacy_statements
nous enfin voir le contrôle réel pour les déclarations d'impression héritées.
/* 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 Python n'est-il pas capable de détecter le problème plus tôt?", Je dirais que le problème avec les parenthèses n'est pas ce qui est détecté; il est en fait interprété après l'erreur de syntaxe. C'est une erreur de syntaxe tout le temps, mais l'article mineur réel sur les parenthèses est attrapé par la suite juste pour donner un indice supplémentaire.
le message d'exception spécial pour print
utilisé comme déclaration au lieu de fonction est en fait mis en œuvre comme un cas spécial .
en gros, quand un SyntaxError
est créé, il appelle une fonction spéciale qui vérifie pour un print
déclaration basée sur la ligne l'exception se réfère.
toutefois, le premier test dans ce fonction (celle qui est responsable du message d'erreur "parenthèse manquante") est s'il y a une parenthèse d'ouverture dans la ligne. J'ai copié le code source de cette fonction (CPython 3.6.4) et j'ai marqué les lignes correspondantes avec "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éclenchera pas le message" parenthèse manquante "s'il y a n'importe quelle parenthèse d'ouverture dans la ligne. Cela conduit au message général SyntaxError
même si l'ouverture 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/variables séparés par un espace blanc est toujours la fin du second 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 vers le x
de max
, car c'est le dernier caractère du second nom. Tout ce qui suit le second nom (comme .
, (
, [
, ...) être ignoré, parce que Python a déjà trouvé un SyntaxError
, et il n'a pas besoin d'aller plus loin, parce que rien ne pourrait le rendre syntaxe valide.
peut-être que je ne comprends pas quelque chose, mais je ne vois pas pourquoi Python devrait signaler l'erreur plus tôt. print
est une fonction régulière, c'est-à-dire une variable référençant une fonction, donc ce sont tous des énoncés valides:
print(10)
print, max, 2
str(print)
print.__doc__
[print] + ['a', 'b']
{print: 2}
si je comprends bien, l'analyseur doit lire le prochain jeton complet après print
( max
dans ce cas-ci) afin de déterminer s'il y a une erreur de syntaxe. Elle ne peut pas simplement dire "échouer s'il n'y a pas d'ouverture". parenthèse", parce qu'il y a un certain nombre de tokens différents qui peuvent aller après print
selon le contexte actuel.
Je ne pense pas qu'il y ait un cas où print
puisse être suivi directement par un autre identifiant ou un littéral, donc vous pourriez argumenter que dès qu'il y a une lettre, un nombre ou des citations vous devriez arrêter, mais ce serait mélanger le travail de l'analyseur et du lexer.
dans les ajouts à ces excellentes réponses, sans même regarder le code source, nous aurions pu deviner que le message d'erreur spécial print
était un kludge:
:
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'est pas encore évalué, donc vous obtenez le message de syntaxe générique invalide au lieu du message de syntaxe piraté print
, ce qui prouve qu'il y a un kludge lorsque le premier jeton est print
.
une autre preuve si nécessaire:
>>> 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 spécifique apparaît toujours.