]> granicus.if.org Git - python/commitdiff
Improve SyntaxErrors for bad future statements. Set file and location
authorJeremy Hylton <jeremy@alum.mit.edu>
Wed, 28 Feb 2001 17:47:12 +0000 (17:47 +0000)
committerJeremy Hylton <jeremy@alum.mit.edu>
Wed, 28 Feb 2001 17:47:12 +0000 (17:47 +0000)
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
Python/compile.c
Python/errors.c
Python/future.c

index c8c3d823118a6fc1dc8dd63a4698fa522dacfe86..7d8ed8466cb7156465d2428dd7a8adb7cc102b7e 100644 (file)
@@ -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
index c540d6b4e71a6993230c286e2865a22a59ee33ca..b44512271bf6a652387a25f9531be58cd643e9d5 100644 (file)
@@ -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;
index 908c0c188ec094c157db574b28443fdb2e0c6266..41050ce72105e75ab5ccca0f797f0b61345e8ff3 100644 (file)
@@ -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;
+}
index 37d547621961dafcf38d0822e951033c3200bc3d..89466f0699e96a68305617280f87fc647993d3b0 100644 (file)
@@ -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;