Skip to content

Commit

Permalink
bpo-37001: Makes symtable.symtable have parity with compile for input (
Browse files Browse the repository at this point in the history
…python#13483)

* Makes symtable.symtable have parity for accepted datatypes
for source code as compile()

* Add NEWS blurb
  • Loading branch information
DinoV authored May 28, 2019
1 parent ab0716e commit 4154069
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 79 deletions.
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);
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 @@ -991,7 +942,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 @@ -1083,7 +1034,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 @@ -1231,21 +1231,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

0 comments on commit 4154069

Please sign in to comment.