bpo-37001: Makes symtable.symtable have parity with compile for input (#13483)
* Makes symtable.symtable have parity for accepted datatypes for source code as compile() * Add NEWS blurb
This commit is contained in:
parent
ab0716ed1e
commit
415406999d
|
@ -119,10 +119,23 @@ PyAPI_FUNC(struct symtable *) Py_SymtableString(
|
||||||
const char *filename, /* decoded from the filesystem encoding */
|
const char *filename, /* decoded from the filesystem encoding */
|
||||||
int start);
|
int start);
|
||||||
#ifndef Py_LIMITED_API
|
#ifndef Py_LIMITED_API
|
||||||
|
PyAPI_FUNC(const char *) _Py_SourceAsString(
|
||||||
|
PyObject *cmd,
|
||||||
|
const char *funcname,
|
||||||
|
const char *what,
|
||||||
|
PyCompilerFlags *cf,
|
||||||
|
PyObject **cmd_copy);
|
||||||
|
|
||||||
PyAPI_FUNC(struct symtable *) Py_SymtableStringObject(
|
PyAPI_FUNC(struct symtable *) Py_SymtableStringObject(
|
||||||
const char *str,
|
const char *str,
|
||||||
PyObject *filename,
|
PyObject *filename,
|
||||||
int start);
|
int start);
|
||||||
|
|
||||||
|
PyAPI_FUNC(struct symtable *) _Py_SymtableStringObjectFlags(
|
||||||
|
const char *str,
|
||||||
|
PyObject *filename,
|
||||||
|
int start,
|
||||||
|
PyCompilerFlags *flags);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
PyAPI_FUNC(void) PyErr_Print(void);
|
PyAPI_FUNC(void) PyErr_Print(void);
|
||||||
|
|
|
@ -215,6 +215,15 @@ class SymtableTest(unittest.TestCase):
|
||||||
def test_exec(self):
|
def test_exec(self):
|
||||||
symbols = symtable.symtable("def f(x): return x", "?", "exec")
|
symbols = symtable.symtable("def f(x): return x", "?", "exec")
|
||||||
|
|
||||||
|
def test_bytes(self):
|
||||||
|
top = symtable.symtable(TEST_CODE.encode('utf8'), "?", "exec")
|
||||||
|
self.assertIsNotNone(find_block(top, "Mine"))
|
||||||
|
|
||||||
|
code = b'# -*- coding: iso8859-15 -*-\nclass \xb4: pass\n'
|
||||||
|
|
||||||
|
top = symtable.symtable(code, "?", "exec")
|
||||||
|
self.assertIsNotNone(find_block(top, "\u017d"))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
:func:`symtable.symtable` now accepts the same input types for source code as the
|
||||||
|
built-in :func:`compile` function. Patch by Dino Viehland.
|
|
@ -3,7 +3,7 @@ preserve
|
||||||
[clinic start generated code]*/
|
[clinic start generated code]*/
|
||||||
|
|
||||||
PyDoc_STRVAR(_symtable_symtable__doc__,
|
PyDoc_STRVAR(_symtable_symtable__doc__,
|
||||||
"symtable($module, str, filename, startstr, /)\n"
|
"symtable($module, source, filename, startstr, /)\n"
|
||||||
"--\n"
|
"--\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Return symbol and scope dictionaries used internally by compiler.");
|
"Return symbol and scope dictionaries used internally by compiler.");
|
||||||
|
@ -12,33 +12,21 @@ PyDoc_STRVAR(_symtable_symtable__doc__,
|
||||||
{"symtable", (PyCFunction)(void(*)(void))_symtable_symtable, METH_FASTCALL, _symtable_symtable__doc__},
|
{"symtable", (PyCFunction)(void(*)(void))_symtable_symtable, METH_FASTCALL, _symtable_symtable__doc__},
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
_symtable_symtable_impl(PyObject *module, const char *str,
|
_symtable_symtable_impl(PyObject *module, PyObject *source,
|
||||||
PyObject *filename, const char *startstr);
|
PyObject *filename, const char *startstr);
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
_symtable_symtable(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
_symtable_symtable(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||||
{
|
{
|
||||||
PyObject *return_value = NULL;
|
PyObject *return_value = NULL;
|
||||||
const char *str;
|
PyObject *source;
|
||||||
PyObject *filename;
|
PyObject *filename;
|
||||||
const char *startstr;
|
const char *startstr;
|
||||||
|
|
||||||
if (!_PyArg_CheckPositional("symtable", nargs, 3, 3)) {
|
if (!_PyArg_CheckPositional("symtable", nargs, 3, 3)) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (!PyUnicode_Check(args[0])) {
|
source = args[0];
|
||||||
_PyArg_BadArgument("symtable", 1, "str", args[0]);
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
Py_ssize_t str_length;
|
|
||||||
str = PyUnicode_AsUTF8AndSize(args[0], &str_length);
|
|
||||||
if (str == NULL) {
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
if (strlen(str) != (size_t)str_length) {
|
|
||||||
PyErr_SetString(PyExc_ValueError, "embedded null character");
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
if (!PyUnicode_FSDecoder(args[1], &filename)) {
|
if (!PyUnicode_FSDecoder(args[1], &filename)) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
@ -55,9 +43,9 @@ _symtable_symtable(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||||
PyErr_SetString(PyExc_ValueError, "embedded null character");
|
PyErr_SetString(PyExc_ValueError, "embedded null character");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
return_value = _symtable_symtable_impl(module, str, filename, startstr);
|
return_value = _symtable_symtable_impl(module, source, filename, startstr);
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
return return_value;
|
return return_value;
|
||||||
}
|
}
|
||||||
/*[clinic end generated code: output=be1cca59de019984 input=a9049054013a1b77]*/
|
/*[clinic end generated code: output=de655625eee705f4 input=a9049054013a1b77]*/
|
||||||
|
|
|
@ -14,7 +14,7 @@ module _symtable
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
_symtable.symtable
|
_symtable.symtable
|
||||||
|
|
||||||
str: str
|
source: object
|
||||||
filename: object(converter='PyUnicode_FSDecoder')
|
filename: object(converter='PyUnicode_FSDecoder')
|
||||||
startstr: str
|
startstr: str
|
||||||
/
|
/
|
||||||
|
@ -23,13 +23,23 @@ Return symbol and scope dictionaries used internally by compiler.
|
||||||
[clinic start generated code]*/
|
[clinic start generated code]*/
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
_symtable_symtable_impl(PyObject *module, const char *str,
|
_symtable_symtable_impl(PyObject *module, PyObject *source,
|
||||||
PyObject *filename, const char *startstr)
|
PyObject *filename, const char *startstr)
|
||||||
/*[clinic end generated code: output=914b369c9b785956 input=6c615e84d5f408e3]*/
|
/*[clinic end generated code: output=59eb0d5fc7285ac4 input=9dd8a50c0c36a4d7]*/
|
||||||
{
|
{
|
||||||
struct symtable *st;
|
struct symtable *st;
|
||||||
PyObject *t;
|
PyObject *t;
|
||||||
int start;
|
int start;
|
||||||
|
PyCompilerFlags cf;
|
||||||
|
PyObject *source_copy = NULL;
|
||||||
|
|
||||||
|
cf.cf_flags = PyCF_SOURCE_IS_UTF8;
|
||||||
|
cf.cf_feature_version = PY_MINOR_VERSION;
|
||||||
|
|
||||||
|
const char *str = _Py_SourceAsString(source, "symtable", "string or bytes", &cf, &source_copy);
|
||||||
|
if (str == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (strcmp(startstr, "exec") == 0)
|
if (strcmp(startstr, "exec") == 0)
|
||||||
start = Py_file_input;
|
start = Py_file_input;
|
||||||
|
@ -41,12 +51,15 @@ _symtable_symtable_impl(PyObject *module, const char *str,
|
||||||
PyErr_SetString(PyExc_ValueError,
|
PyErr_SetString(PyExc_ValueError,
|
||||||
"symtable() arg 3 must be 'exec' or 'eval' or 'single'");
|
"symtable() arg 3 must be 'exec' or 'eval' or 'single'");
|
||||||
Py_DECREF(filename);
|
Py_DECREF(filename);
|
||||||
|
Py_XDECREF(source_copy);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
st = Py_SymtableStringObject(str, filename, start);
|
st = _Py_SymtableStringObjectFlags(str, filename, start, &cf);
|
||||||
Py_DECREF(filename);
|
Py_DECREF(filename);
|
||||||
if (st == NULL)
|
Py_XDECREF(source_copy);
|
||||||
|
if (st == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
t = (PyObject *)st->st_top;
|
t = (PyObject *)st->st_top;
|
||||||
Py_INCREF(t);
|
Py_INCREF(t);
|
||||||
PyMem_Free((void *)st->st_future);
|
PyMem_Free((void *)st->st_future);
|
||||||
|
|
|
@ -687,55 +687,6 @@ builtin_chr_impl(PyObject *module, int i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static const char *
|
|
||||||
source_as_string(PyObject *cmd, const char *funcname, const char *what, PyCompilerFlags *cf, PyObject **cmd_copy)
|
|
||||||
{
|
|
||||||
const char *str;
|
|
||||||
Py_ssize_t size;
|
|
||||||
Py_buffer view;
|
|
||||||
|
|
||||||
*cmd_copy = NULL;
|
|
||||||
if (PyUnicode_Check(cmd)) {
|
|
||||||
cf->cf_flags |= PyCF_IGNORE_COOKIE;
|
|
||||||
str = PyUnicode_AsUTF8AndSize(cmd, &size);
|
|
||||||
if (str == NULL)
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
else if (PyBytes_Check(cmd)) {
|
|
||||||
str = PyBytes_AS_STRING(cmd);
|
|
||||||
size = PyBytes_GET_SIZE(cmd);
|
|
||||||
}
|
|
||||||
else if (PyByteArray_Check(cmd)) {
|
|
||||||
str = PyByteArray_AS_STRING(cmd);
|
|
||||||
size = PyByteArray_GET_SIZE(cmd);
|
|
||||||
}
|
|
||||||
else if (PyObject_GetBuffer(cmd, &view, PyBUF_SIMPLE) == 0) {
|
|
||||||
/* Copy to NUL-terminated buffer. */
|
|
||||||
*cmd_copy = PyBytes_FromStringAndSize(
|
|
||||||
(const char *)view.buf, view.len);
|
|
||||||
PyBuffer_Release(&view);
|
|
||||||
if (*cmd_copy == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
str = PyBytes_AS_STRING(*cmd_copy);
|
|
||||||
size = PyBytes_GET_SIZE(*cmd_copy);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
PyErr_Format(PyExc_TypeError,
|
|
||||||
"%s() arg 1 must be a %s object",
|
|
||||||
funcname, what);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strlen(str) != (size_t)size) {
|
|
||||||
PyErr_SetString(PyExc_ValueError,
|
|
||||||
"source code string cannot contain null bytes");
|
|
||||||
Py_CLEAR(*cmd_copy);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
compile as builtin_compile
|
compile as builtin_compile
|
||||||
|
|
||||||
|
@ -855,7 +806,7 @@ builtin_compile_impl(PyObject *module, PyObject *source, PyObject *filename,
|
||||||
goto finally;
|
goto finally;
|
||||||
}
|
}
|
||||||
|
|
||||||
str = source_as_string(source, "compile", "string, bytes or AST", &cf, &source_copy);
|
str = _Py_SourceAsString(source, "compile", "string, bytes or AST", &cf, &source_copy);
|
||||||
if (str == NULL)
|
if (str == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
@ -991,7 +942,7 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals,
|
||||||
|
|
||||||
cf.cf_flags = PyCF_SOURCE_IS_UTF8;
|
cf.cf_flags = PyCF_SOURCE_IS_UTF8;
|
||||||
cf.cf_feature_version = PY_MINOR_VERSION;
|
cf.cf_feature_version = PY_MINOR_VERSION;
|
||||||
str = source_as_string(source, "eval", "string, bytes or code", &cf, &source_copy);
|
str = _Py_SourceAsString(source, "eval", "string, bytes or code", &cf, &source_copy);
|
||||||
if (str == NULL)
|
if (str == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -1083,7 +1034,7 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
|
||||||
PyCompilerFlags cf;
|
PyCompilerFlags cf;
|
||||||
cf.cf_flags = PyCF_SOURCE_IS_UTF8;
|
cf.cf_flags = PyCF_SOURCE_IS_UTF8;
|
||||||
cf.cf_feature_version = PY_MINOR_VERSION;
|
cf.cf_feature_version = PY_MINOR_VERSION;
|
||||||
str = source_as_string(source, "exec",
|
str = _Py_SourceAsString(source, "exec",
|
||||||
"string, bytes or code", &cf,
|
"string, bytes or code", &cf,
|
||||||
&source_copy);
|
&source_copy);
|
||||||
if (str == NULL)
|
if (str == NULL)
|
||||||
|
|
|
@ -1231,21 +1231,77 @@ PyCompileString(const char *str, const char *filename, int start)
|
||||||
return Py_CompileStringFlags(str, filename, start, NULL);
|
return Py_CompileStringFlags(str, filename, start, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
_Py_SourceAsString(PyObject *cmd, const char *funcname, const char *what, PyCompilerFlags *cf, PyObject **cmd_copy)
|
||||||
|
{
|
||||||
|
const char *str;
|
||||||
|
Py_ssize_t size;
|
||||||
|
Py_buffer view;
|
||||||
|
|
||||||
|
*cmd_copy = NULL;
|
||||||
|
if (PyUnicode_Check(cmd)) {
|
||||||
|
cf->cf_flags |= PyCF_IGNORE_COOKIE;
|
||||||
|
str = PyUnicode_AsUTF8AndSize(cmd, &size);
|
||||||
|
if (str == NULL)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else if (PyBytes_Check(cmd)) {
|
||||||
|
str = PyBytes_AS_STRING(cmd);
|
||||||
|
size = PyBytes_GET_SIZE(cmd);
|
||||||
|
}
|
||||||
|
else if (PyByteArray_Check(cmd)) {
|
||||||
|
str = PyByteArray_AS_STRING(cmd);
|
||||||
|
size = PyByteArray_GET_SIZE(cmd);
|
||||||
|
}
|
||||||
|
else if (PyObject_GetBuffer(cmd, &view, PyBUF_SIMPLE) == 0) {
|
||||||
|
/* Copy to NUL-terminated buffer. */
|
||||||
|
*cmd_copy = PyBytes_FromStringAndSize(
|
||||||
|
(const char *)view.buf, view.len);
|
||||||
|
PyBuffer_Release(&view);
|
||||||
|
if (*cmd_copy == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
str = PyBytes_AS_STRING(*cmd_copy);
|
||||||
|
size = PyBytes_GET_SIZE(*cmd_copy);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
"%s() arg 1 must be a %s object",
|
||||||
|
funcname, what);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(str) != (size_t)size) {
|
||||||
|
PyErr_SetString(PyExc_ValueError,
|
||||||
|
"source code string cannot contain null bytes");
|
||||||
|
Py_CLEAR(*cmd_copy);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
struct symtable *
|
struct symtable *
|
||||||
Py_SymtableStringObject(const char *str, PyObject *filename, int start)
|
Py_SymtableStringObject(const char *str, PyObject *filename, int start)
|
||||||
|
{
|
||||||
|
PyCompilerFlags flags;
|
||||||
|
|
||||||
|
flags.cf_flags = 0;
|
||||||
|
flags.cf_feature_version = PY_MINOR_VERSION;
|
||||||
|
return _Py_SymtableStringObjectFlags(str, filename, start, &flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct symtable *
|
||||||
|
_Py_SymtableStringObjectFlags(const char *str, PyObject *filename, int start, PyCompilerFlags *flags)
|
||||||
{
|
{
|
||||||
struct symtable *st;
|
struct symtable *st;
|
||||||
mod_ty mod;
|
mod_ty mod;
|
||||||
PyCompilerFlags flags;
|
|
||||||
PyArena *arena;
|
PyArena *arena;
|
||||||
|
|
||||||
arena = PyArena_New();
|
arena = PyArena_New();
|
||||||
if (arena == NULL)
|
if (arena == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
flags.cf_flags = 0;
|
mod = PyParser_ASTFromStringObject(str, filename, start, flags, arena);
|
||||||
flags.cf_feature_version = PY_MINOR_VERSION;
|
|
||||||
mod = PyParser_ASTFromStringObject(str, filename, start, &flags, arena);
|
|
||||||
if (mod == NULL) {
|
if (mod == NULL) {
|
||||||
PyArena_Free(arena);
|
PyArena_Free(arena);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
Loading…
Reference in New Issue