parser parse org docs commands python python-3.x syntax-error python-internals

parse - python libraries list



Impresión sin paréntesis con mensaje de error variable usando Python 3 (4)

Además de esas excelentes respuestas, sin siquiera mirar el código fuente, podríamos haber adivinado que el mensaje de error especial de print era un problema:

asi que:

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

pero:

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

incluso si a == print pero en esa etapa, aún no se ha evaluado, así que obtienes el mensaje de sintaxis no válido genérico en lugar del mensaje de sintaxis de print pirateado, lo que demuestra que hay un kludge cuando se print el primer token.

Otra prueba si es necesario:

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

en ese caso, print == None , pero el mensaje específico sigue apareciendo.

Cuando trato de usar la print sin paréntesis en un nombre simple en Python 3.4, obtengo:

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

Ok, ahora lo entiendo, me olvidé de portar mi código Python 2.

Pero ahora cuando intento imprimir el resultado de una función:

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

O:

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

(Tenga en cuenta que el cursor está apuntando al carácter antes del primer punto en ese caso).

El mensaje es diferente (y ligeramente engañoso, ya que el marcador está debajo de la función max ).

¿Por qué Python no puede detectar el problema antes?

Nota: esta pregunta se inspiró en la confusión en torno a esta pregunta: Pandas read.csv error de sintaxis , donde algunos expertos de Python no entendieron el verdadero problema debido al mensaje de error engañoso.


El mensaje de excepción especial para la print utiliza como declaración en lugar de como función se implementa como un caso especial .

En términos generales, cuando se crea un SyntaxError , llama a una función especial que verifica una declaración de print basada en la línea a la que se refiere la excepción.

Sin embargo, la primera prueba en esta función (la responsable del mensaje de error "Paréntesis perdidos") es si hay algún paréntesis de apertura en la línea. Copié el código fuente de esa función (CPython 3.6.4) y marqué las líneas relevantes con "flechas":

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

Eso significa que no activará el mensaje "Paréntesis perdidos" si hay algún paréntesis de apertura en la línea. Eso lleva al mensaje general SyntaxError incluso si el paréntesis de apertura está en un comentario:

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

Tenga en cuenta que la posición del cursor para dos nombres / variables separadas por un espacio en blanco siempre es el final del segundo nombre:

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

Así que no es de extrañar que el cursor apunte a la x de max , porque es el último carácter del segundo nombre. Todo lo que sigue al segundo nombre (como . , ( , [ , ...) se ignora, porque Python ya encontró un SyntaxError , y no necesita ir más allá, porque nada podría hacer que la sintaxis sea válida.


Mirando el código fuente de exceptions.c , justo arriba de _set_legacy_print_statement_msg hay un bonito comentario de bloque:

/* 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). */

Así que hay alguna información interesante. Además, en el método SyntaxError_init en el mismo archivo, podemos ver

/* * 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; }

Tenga en cuenta también que las referencias anteriores emiten el número 21669 en el controlador de errores de Python con una discusión entre el autor y Guido sobre cómo hacerlo. Así que seguimos al conejo (es decir, _report_missing_parentheses ) que se encuentra en la parte inferior del archivo, y vemos ...

legacy_check_result = _check_for_legacy_statements(self, 0);

Sin embargo, hay algunos casos en los que esto se omite y se SyntaxError mensaje normal SyntaxError Consulte la respuesta de MSeifert para obtener más información al respecto. Si subimos una función a _check_for_legacy_statements , finalmente vemos la comprobación real de las declaraciones de impresión heredadas.

/* 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); }

Entonces, para responder a la pregunta: "¿Por qué Python no puede detectar el problema antes?", Diría que el problema con paréntesis no es lo que se detecta; en realidad se analiza después del error de sintaxis. Es un error de sintaxis todo el tiempo, pero la pieza menor real sobre paréntesis se captura después solo para dar una pista adicional.


Quizás no entiendo algo, pero no veo por qué Python debería señalar el error antes. print es una función regular, es una variable que hace referencia a una función, por lo que estas son todas las declaraciones válidas:

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

Como lo entiendo, el analizador necesita leer el siguiente token completo después de la print ( max En este caso) para determinar si hay un error de sintaxis. No puede decir simplemente "fallar si no hay un paréntesis abierto", porque hay una cantidad de tokens diferentes que pueden ir después de la print según el contexto actual.

No creo que exista un caso en el que la print esté seguida directamente por otro identificador o un literal, por lo que podría argumentar que tan pronto como haya una letra, un número o comillas, debe parar, pero eso sería una mezcla del analizador. y el trabajo del lexer.