From ad3d3f2f3f19833f59fd7e9ec59e1714e0986e08 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Wed, 28 Feb 2001 17:47:12 +0000 Subject: Improve SyntaxErrors for bad future statements. Set file and location for errors raised in future.c. Move some helper functions from compile.c to errors.c and make them API functions: PyErr_SyntaxLocation() and PyErr_ProgramText(). --- Include/pyerrors.h | 4 +++ Python/compile.c | 90 ++++-------------------------------------------------- Python/errors.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++ Python/future.c | 34 +++++++++++++++------ 4 files changed, 113 insertions(+), 94 deletions(-) diff --git a/Include/pyerrors.h b/Include/pyerrors.h index c8c3d82..7d8ed84 100644 --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -98,6 +98,10 @@ extern DL_IMPORT(int) PyErr_Warn(PyObject *, char *); /* In sigcheck.c or signalmodule.c */ extern DL_IMPORT(int) PyErr_CheckSignals(void); extern DL_IMPORT(void) PyErr_SetInterrupt(void); + +/* Support for adding program text to SyntaxErrors */ +extern DL_IMPORT(void) PyErr_SyntaxLocation(char *, int); +extern DL_IMPORT(PyObject *) PyErr_ProgramText(char *, int); #ifdef __cplusplus diff --git a/Python/compile.c b/Python/compile.c index c540d6b..b445122 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -381,49 +381,6 @@ int is_free(int v) return 0; } -/* com_fetch_program_text will attempt to load the line of text that - the exception refers to. If it fails, it will return NULL but will - not set an exception. - - XXX The functionality of this function is quite similar to the - functionality in tb_displayline() in traceback.c. -*/ - -static PyObject * -fetch_program_text(char *filename, int lineno) -{ - FILE *fp; - int i; - char linebuf[1000]; - - if (filename == NULL || lineno <= 0) - return NULL; - fp = fopen(filename, "r"); - if (fp == NULL) - return NULL; - for (i = 0; i < lineno; i++) { - char *pLastChar = &linebuf[sizeof(linebuf) - 2]; - do { - *pLastChar = '\0'; - if (fgets(linebuf, sizeof linebuf, fp) == NULL) - break; - /* fgets read *something*; if it didn't get as - far as pLastChar, it must have found a newline - or hit the end of the file; if pLastChar is \n, - it obviously found a newline; else we haven't - yet seen a newline, so must continue */ - } while (*pLastChar != '\0' && *pLastChar != '\n'); - } - fclose(fp); - if (i == lineno) { - char *p = linebuf; - while (*p == ' ' || *p == '\t' || *p == '\014') - p++; - return PyString_FromString(p); - } - return NULL; -} - static void com_error(struct compiling *c, PyObject *exc, char *msg) { @@ -445,7 +402,7 @@ com_error(struct compiling *c, PyObject *exc, char *msg) if (v == NULL) return; /* MemoryError, too bad */ - line = fetch_program_text(c->c_filename, c->c_lineno); + line = PyErr_ProgramText(c->c_filename, c->c_lineno); if (line == NULL) { Py_INCREF(Py_None); line = Py_None; @@ -4028,41 +3985,6 @@ get_ref_type(struct compiling *c, char *name) /* Helper function for setting lineno and filename */ -static void -set_error_location(char *filename, int lineno) -{ - PyObject *exc, *v, *tb, *tmp; - - /* add attributes for the line number and filename for the error */ - PyErr_Fetch(&exc, &v, &tb); - PyErr_NormalizeException(&exc, &v, &tb); - tmp = PyInt_FromLong(lineno); - if (tmp == NULL) - PyErr_Clear(); - else { - if (PyObject_SetAttrString(v, "lineno", tmp)) - PyErr_Clear(); - Py_DECREF(tmp); - } - if (filename != NULL) { - tmp = PyString_FromString(filename); - if (tmp == NULL) - PyErr_Clear(); - else { - if (PyObject_SetAttrString(v, "filename", tmp)) - PyErr_Clear(); - Py_DECREF(tmp); - } - - tmp = fetch_program_text(filename, lineno); - if (tmp) { - PyObject_SetAttrString(v, "text", tmp); - Py_DECREF(tmp); - } - } - PyErr_Restore(exc, v, tb); -} - static int symtable_build(struct compiling *c, node *n) { @@ -4198,7 +4120,7 @@ symtable_update_flags(struct compiling *c, PySymtableEntryObject *ste, PyErr_Format(PyExc_SyntaxError, ILLEGAL_DYNAMIC_SCOPE, PyString_AS_STRING(ste->ste_name)); - set_error_location(c->c_symtable->st_filename, + PyErr_SyntaxLocation(c->c_symtable->st_filename, ste->ste_lineno); return -1; } else { @@ -4273,7 +4195,7 @@ symtable_load_symbols(struct compiling *c) if (flags & DEF_PARAM) { PyErr_Format(PyExc_SyntaxError, LOCAL_GLOBAL, PyString_AS_STRING(name)); - set_error_location(st->st_filename, + PyErr_SyntaxLocation(st->st_filename, ste->ste_lineno); st->st_errors++; goto fail; @@ -4581,7 +4503,7 @@ symtable_add_def_o(struct symtable *st, PyObject *dict, if ((flag & DEF_PARAM) && (val & DEF_PARAM)) { PyErr_Format(PyExc_SyntaxError, DUPLICATE_ARGUMENT, PyString_AsString(name)); - set_error_location(st->st_filename, + PyErr_SyntaxLocation(st->st_filename, st->st_cur->ste_lineno); return -1; } @@ -4904,7 +4826,7 @@ symtable_global(struct symtable *st, node *n) PyErr_Format(PyExc_SyntaxError, "name '%.400s' is local and global", name); - set_error_location(st->st_filename, + PyErr_SyntaxLocation(st->st_filename, st->st_cur->ste_lineno); st->st_errors++; return; @@ -4958,7 +4880,7 @@ symtable_import(struct symtable *st, node *n) if (n->n_lineno >= st->st_future->ff_last_lineno) { PyErr_SetString(PyExc_SyntaxError, LATE_FUTURE); - set_error_location(st->st_filename, + PyErr_SyntaxLocation(st->st_filename, n->n_lineno); st->st_errors++; return; diff --git a/Python/errors.c b/Python/errors.c index 908c0c1..41050ce 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -622,3 +622,82 @@ PyErr_Warn(PyObject *category, char *message) return 0; } } + +void +PyErr_SyntaxLocation(char *filename, int lineno) +{ + PyObject *exc, *v, *tb, *tmp; + + /* add attributes for the line number and filename for the error */ + PyErr_Fetch(&exc, &v, &tb); + PyErr_NormalizeException(&exc, &v, &tb); + /* XXX check that it is, indeed, a syntax error */ + tmp = PyInt_FromLong(lineno); + if (tmp == NULL) + PyErr_Clear(); + else { + if (PyObject_SetAttrString(v, "lineno", tmp)) + PyErr_Clear(); + Py_DECREF(tmp); + } + if (filename != NULL) { + tmp = PyString_FromString(filename); + if (tmp == NULL) + PyErr_Clear(); + else { + if (PyObject_SetAttrString(v, "filename", tmp)) + PyErr_Clear(); + Py_DECREF(tmp); + } + + tmp = PyErr_ProgramText(filename, lineno); + if (tmp) { + PyObject_SetAttrString(v, "text", tmp); + Py_DECREF(tmp); + } + } + PyErr_Restore(exc, v, tb); +} + +/* com_fetch_program_text will attempt to load the line of text that + the exception refers to. If it fails, it will return NULL but will + not set an exception. + + XXX The functionality of this function is quite similar to the + functionality in tb_displayline() in traceback.c. +*/ + +PyObject * +PyErr_ProgramText(char *filename, int lineno) +{ + FILE *fp; + int i; + char linebuf[1000]; + + if (filename == NULL || lineno <= 0) + return NULL; + fp = fopen(filename, "r"); + if (fp == NULL) + return NULL; + for (i = 0; i < lineno; i++) { + char *pLastChar = &linebuf[sizeof(linebuf) - 2]; + do { + *pLastChar = '\0'; + if (fgets(linebuf, sizeof linebuf, fp) == NULL) + break; + /* fgets read *something*; if it didn't get as + far as pLastChar, it must have found a newline + or hit the end of the file; if pLastChar is \n, + it obviously found a newline; else we haven't + yet seen a newline, so must continue */ + } while (*pLastChar != '\0' && *pLastChar != '\n'); + } + fclose(fp); + if (i == lineno) { + char *p = linebuf; + while (*p == ' ' || *p == '\t' || *p == '\014') + p++; + return PyString_FromString(p); + } + return NULL; +} diff --git a/Python/future.c b/Python/future.c index 37d5476..89466f0 100644 --- a/Python/future.c +++ b/Python/future.c @@ -6,24 +6,40 @@ #include "symtable.h" #define UNDEFINED_FUTURE_FEATURE "future feature %.100s is not defined" +#define FUTURE_IMPORT_STAR "future statement does not support import *" #define FUTURE_POSSIBLE(FF) ((FF)->ff_last_lineno == -1) static int -future_check_features(PyFutureFeatures *ff, node *n) +future_check_features(PyFutureFeatures *ff, node *n, char *filename) { int i; char *feature; + node *ch; REQ(n, import_stmt); /* must by from __future__ import ... */ for (i = 3; i < NCH(n); ++i) { - feature = STR(CHILD(CHILD(n, i), 0)); + ch = CHILD(n, i); + if (TYPE(ch) == STAR) { + PyErr_SetString(PyExc_SyntaxError, + FUTURE_IMPORT_STAR); + PyErr_SyntaxLocation(filename, ch->n_lineno); + return -1; + } + REQ(ch, import_as_name); + feature = STR(CHILD(ch, 0)); if (strcmp(feature, FUTURE_NESTED_SCOPES) == 0) { ff->ff_nested_scopes = 1; + } else if (strcmp(feature, "braces") == 0) { + PyErr_SetString(PyExc_SyntaxError, + "not a chance"); + PyErr_SyntaxLocation(filename, CHILD(ch, 0)->n_lineno); + return -1; } else { PyErr_Format(PyExc_SyntaxError, UNDEFINED_FUTURE_FEATURE, feature); + PyErr_SyntaxLocation(filename, CHILD(ch, 0)->n_lineno); return -1; } } @@ -36,6 +52,7 @@ future_error(node *n, char *filename) PyErr_SetString(PyExc_SyntaxError, "from __future__ imports must occur at the " "beginning of the file"); + PyErr_SyntaxLocation(filename, n->n_lineno); /* XXX set filename and lineno */ } @@ -45,8 +62,10 @@ single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE file_input: (NEWLINE | stmt)* ENDMARKER stmt: simple_stmt | compound_stmt simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE -small_stmt: expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt | import_stmt | global_stmt | exec_stmt | assert_stmt -import_stmt: 'import' dotted_as_name (',' dotted_as_name)* | 'from' dotted_name 'import' ('*' | import_as_name (',' import_as_name)*) +small_stmt: expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt + | import_stmt | global_stmt | exec_stmt | assert_stmt +import_stmt: 'import' dotted_as_name (',' dotted_as_name)* + | 'from' dotted_name 'import' ('*' | import_as_name (',' import_as_name)*) import_as_name: NAME [NAME NAME] dotted_as_name: dotted_name [NAME NAME] dotted_name: NAME ('.' NAME)* @@ -64,11 +83,6 @@ future_parse(PyFutureFeatures *ff, node *n, char *filename) int i, r; loop: -/* fprintf(stderr, "future_parse(%d, %d, %s, %d)\n", - TYPE(n), NCH(n), (n == NULL) ? "NULL" : STR(n), - n->n_lineno); -*/ - switch (TYPE(n)) { case single_input: @@ -162,7 +176,7 @@ future_parse(PyFutureFeatures *ff, node *n, char *filename) name = CHILD(n, 1); if (strcmp(STR(CHILD(name, 0)), "__future__") != 0) return 0; - if (future_check_features(ff, n) < 0) + if (future_check_features(ff, n, filename) < 0) return -1; ff->ff_last_lineno = n->n_lineno + 1; return 1; -- cgit v0.12