summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Hylton <jeremy@alum.mit.edu>2001-02-28 17:47:12 (GMT)
committerJeremy Hylton <jeremy@alum.mit.edu>2001-02-28 17:47:12 (GMT)
commitad3d3f2f3f19833f59fd7e9ec59e1714e0986e08 (patch)
tree3b1b99d9f5330c80768ceb14484050a3ff7162d7
parent5687ffe0c56a827b538589fc46e79dfacfd857f3 (diff)
downloadcpython-ad3d3f2f3f19833f59fd7e9ec59e1714e0986e08.zip
cpython-ad3d3f2f3f19833f59fd7e9ec59e1714e0986e08.tar.gz
cpython-ad3d3f2f3f19833f59fd7e9ec59e1714e0986e08.tar.bz2
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().
-rw-r--r--Include/pyerrors.h4
-rw-r--r--Python/compile.c90
-rw-r--r--Python/errors.c79
-rw-r--r--Python/future.c34
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;