Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-37001: Makes symtable.symtable have parity with compile for input #13483

Merged
merged 6 commits into from
May 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Include/pythonrun.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,23 @@ PyAPI_FUNC(struct symtable *) Py_SymtableString(
const char *filename, /* decoded from the filesystem encoding */
int start);
#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(
const char *str,
PyObject *filename,
int start);

PyAPI_FUNC(struct symtable *) _Py_SymtableStringObjectFlags(
const char *str,
PyObject *filename,
int start,
PyCompilerFlags *flags);
#endif

PyAPI_FUNC(void) PyErr_Print(void);
Expand Down
9 changes: 9 additions & 0 deletions Lib/test/test_symtable.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,15 @@ def test_single(self):
def test_exec(self):
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__':
unittest.main()
Original file line number Diff line number Diff line change
@@ -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.
24 changes: 6 additions & 18 deletions Modules/clinic/symtablemodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 18 additions & 5 deletions Modules/symtablemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module _symtable
/*[clinic input]
_symtable.symtable

str: str
source: object
filename: object(converter='PyUnicode_FSDecoder')
startstr: str
/
Expand All @@ -23,13 +23,23 @@ Return symbol and scope dictionaries used internally by compiler.
[clinic start generated code]*/

static PyObject *
_symtable_symtable_impl(PyObject *module, const char *str,
_symtable_symtable_impl(PyObject *module, PyObject *source,
PyObject *filename, const char *startstr)
/*[clinic end generated code: output=914b369c9b785956 input=6c615e84d5f408e3]*/
/*[clinic end generated code: output=59eb0d5fc7285ac4 input=9dd8a50c0c36a4d7]*/
{
struct symtable *st;
PyObject *t;
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)
start = Py_file_input;
Expand All @@ -41,12 +51,15 @@ _symtable_symtable_impl(PyObject *module, const char *str,
PyErr_SetString(PyExc_ValueError,
"symtable() arg 3 must be 'exec' or 'eval' or 'single'");
Py_DECREF(filename);
Py_XDECREF(source_copy);
return NULL;
}
st = Py_SymtableStringObject(str, filename, start);
st = _Py_SymtableStringObjectFlags(str, filename, start, &cf);
Py_DECREF(filename);
if (st == NULL)
Py_XDECREF(source_copy);
if (st == NULL) {
return NULL;
}
t = (PyObject *)st->st_top;
Py_INCREF(t);
PyMem_Free((void *)st->st_future);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaked source_copy.

Expand Down
55 changes: 3 additions & 52 deletions Python/bltinmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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]
compile as builtin_compile

Expand Down Expand Up @@ -855,7 +806,7 @@ builtin_compile_impl(PyObject *module, PyObject *source, PyObject *filename,
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)
goto error;

Expand Down Expand Up @@ -987,7 +938,7 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals,

cf.cf_flags = PyCF_SOURCE_IS_UTF8;
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)
return NULL;

Expand Down Expand Up @@ -1075,7 +1026,7 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
PyCompilerFlags cf;
cf.cf_flags = PyCF_SOURCE_IS_UTF8;
cf.cf_feature_version = PY_MINOR_VERSION;
str = source_as_string(source, "exec",
str = _Py_SourceAsString(source, "exec",
"string, bytes or code", &cf,
&source_copy);
if (str == NULL)
Expand Down
64 changes: 60 additions & 4 deletions Python/pythonrun.c
Original file line number Diff line number Diff line change
Expand Up @@ -1184,21 +1184,77 @@ PyCompileString(const char *str, const char *filename, int start)
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 *
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;
mod_ty mod;
PyCompilerFlags flags;
PyArena *arena;

arena = PyArena_New();
if (arena == NULL)
return NULL;

flags.cf_flags = 0;
flags.cf_feature_version = PY_MINOR_VERSION;
mod = PyParser_ASTFromStringObject(str, filename, start, &flags, arena);
mod = PyParser_ASTFromStringObject(str, filename, start, flags, arena);
if (mod == NULL) {
PyArena_Free(arena);
return NULL;
Expand Down