| #include "Python.h" | |
| #include "pycore_ast.h" // _PyAST_GetDocString() | |
| #include "pycore_pyerrors.h" // _Py_CalculateSuggestions() | |
| #include "pycore_symtable.h" // _PyFutureFeatures | |
| #include "pycore_unicodeobject.h" // _PyUnicode_EqualToASCIIString() | |
| #define UNDEFINED_FUTURE_FEATURE "future feature '%.100s' is not defined" | |
| static int | |
| future_check_features(_PyFutureFeatures *ff, stmt_ty s, PyObject *filename) | |
| { | |
| Py_ssize_t i; | |
| assert(s->kind == ImportFrom_kind); | |
| asdl_alias_seq *names = s->v.ImportFrom.names; | |
| for (i = 0; i < asdl_seq_LEN(names); i++) { | |
| alias_ty name = (alias_ty)asdl_seq_GET(names, i); | |
| const char *feature = PyUnicode_AsUTF8(name->name); | |
| if (!feature) | |
| return 0; | |
| if (strcmp(feature, FUTURE_NESTED_SCOPES) == 0) { | |
| continue; | |
| } else if (strcmp(feature, FUTURE_GENERATORS) == 0) { | |
| continue; | |
| } else if (strcmp(feature, FUTURE_DIVISION) == 0) { | |
| continue; | |
| } else if (strcmp(feature, FUTURE_ABSOLUTE_IMPORT) == 0) { | |
| continue; | |
| } else if (strcmp(feature, FUTURE_WITH_STATEMENT) == 0) { | |
| continue; | |
| } else if (strcmp(feature, FUTURE_PRINT_FUNCTION) == 0) { | |
| continue; | |
| } else if (strcmp(feature, FUTURE_UNICODE_LITERALS) == 0) { | |
| continue; | |
| } else if (strcmp(feature, FUTURE_BARRY_AS_BDFL) == 0) { | |
| ff->ff_features |= CO_FUTURE_BARRY_AS_BDFL; | |
| } else if (strcmp(feature, FUTURE_GENERATOR_STOP) == 0) { | |
| continue; | |
| } else if (strcmp(feature, FUTURE_ANNOTATIONS) == 0) { | |
| ff->ff_features |= CO_FUTURE_ANNOTATIONS; | |
| } else if (strcmp(feature, "braces") == 0) { | |
| PyErr_SetString(PyExc_SyntaxError, | |
| "not a chance"); | |
| PyErr_RangedSyntaxLocationObject(filename, | |
| name->lineno, | |
| name->col_offset + 1, | |
| name->end_lineno, | |
| name->end_col_offset + 1); | |
| return 0; | |
| } else { | |
| // Keep this list in sync with the feature checks above. | |
| PyObject *future_features = Py_BuildValue("[ssssssssss]", | |
| FUTURE_NESTED_SCOPES, | |
| FUTURE_GENERATORS, | |
| FUTURE_DIVISION, | |
| FUTURE_ABSOLUTE_IMPORT, | |
| FUTURE_WITH_STATEMENT, | |
| FUTURE_PRINT_FUNCTION, | |
| FUTURE_UNICODE_LITERALS, | |
| FUTURE_BARRY_AS_BDFL, | |
| FUTURE_GENERATOR_STOP, | |
| FUTURE_ANNOTATIONS); | |
| PyObject *suggestion = NULL; | |
| if (future_features != NULL) { | |
| suggestion = _Py_CalculateSuggestions(future_features, | |
| name->name); | |
| } | |
| if (suggestion != NULL) { | |
| PyErr_Format(PyExc_SyntaxError, | |
| UNDEFINED_FUTURE_FEATURE ". Did you mean: %R?", | |
| feature, suggestion); | |
| Py_DECREF(suggestion); | |
| } | |
| else { | |
| // Do not fail on missing suggestion, | |
| // just show the default message. | |
| PyErr_Format(PyExc_SyntaxError, | |
| UNDEFINED_FUTURE_FEATURE, | |
| feature); | |
| } | |
| Py_XDECREF(future_features); | |
| PyErr_RangedSyntaxLocationObject(filename, | |
| name->lineno, | |
| name->col_offset + 1, | |
| name->end_lineno, | |
| name->end_col_offset + 1); | |
| return 0; | |
| } | |
| } | |
| return 1; | |
| } | |
| static int | |
| future_parse(_PyFutureFeatures *ff, mod_ty mod, PyObject *filename) | |
| { | |
| if (!(mod->kind == Module_kind || mod->kind == Interactive_kind)) { | |
| return 1; | |
| } | |
| Py_ssize_t n = asdl_seq_LEN(mod->v.Module.body); | |
| if (n == 0) { | |
| return 1; | |
| } | |
| Py_ssize_t i = 0; | |
| if (_PyAST_GetDocString(mod->v.Module.body) != NULL) { | |
| i++; | |
| } | |
| for (; i < n; i++) { | |
| stmt_ty s = (stmt_ty)asdl_seq_GET(mod->v.Module.body, i); | |
| /* The only things that can precede a future statement | |
| * are another future statement and a doc string. | |
| */ | |
| if (s->kind == ImportFrom_kind && s->v.ImportFrom.level == 0) { | |
| identifier modname = s->v.ImportFrom.module; | |
| if (modname && | |
| _PyUnicode_EqualToASCIIString(modname, "__future__")) { | |
| if (!future_check_features(ff, s, filename)) { | |
| return 0; | |
| } | |
| ff->ff_location = SRC_LOCATION_FROM_AST(s); | |
| } | |
| else { | |
| return 1; | |
| } | |
| } | |
| else { | |
| return 1; | |
| } | |
| } | |
| return 1; | |
| } | |
| int | |
| _PyFuture_FromAST(mod_ty mod, PyObject *filename, _PyFutureFeatures *ff) | |
| { | |
| ff->ff_features = 0; | |
| ff->ff_location = (_Py_SourceLocation){-1, -1, -1, -1}; | |
| if (!future_parse(ff, mod, filename)) { | |
| return 0; | |
| } | |
| return 1; | |
| } |