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.