diff -r 3a069e5593ef Python/bltinmodule.c --- a/Python/bltinmodule.c Wed May 04 23:26:04 2016 +0300 +++ b/Python/bltinmodule.c Fri May 06 10:03:43 2016 -0500 @@ -1310,6 +1310,115 @@ is exhausted, it is returned instead of raising StopIteration."); +/* AC: cannot convert yet, as needs arbitrary keyword arguments. */ +static PyObject * +builtin_public(PyObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *arg = NULL; + PyObject *globals = NULL; + PyObject *all = NULL; + + if (!PyArg_UnpackTuple(args, "public", 0, 1, &arg)) + return NULL; + + /* kwds can be empty, but the keys must be strings. */ + if (kwds != NULL && !PyArg_ValidateKeywordArguments(kwds)) + return NULL; + + if (kwds != NULL && !PyDict_Check(kwds)) { + PyErr_BadInternalCall(); + return NULL; + } + if (!(globals = PyEval_GetGlobals())) { + PyErr_Format(PyExc_TypeError, + "@public called with no active globals"); + return NULL; + } + if (!(all = PyDict_GetItemString(globals, "__all__"))) { + if (!(all = PyList_New(0))) + return NULL; + if (PyDict_SetItemString(globals, "__all__", all) < 0) + goto err; + } + else + /* Bump all's reference count since it's currently borrowed, and this + way we guarantee we own a reference for common exit cleanup. + */ + Py_INCREF(all); + + if (arg != NULL) { + PyObject *name = NULL; + + /* There is a single positional argument. This must have a "__name__" + attribute, which we will put in __all__. The keywords dictionary + must be empty. + */ + if (kwds != NULL && PyDict_Size(kwds) != 0) { + PyErr_Format(PyExc_TypeError, + "Positional and keywords are mutually exclusive"); + goto err; + } + if (!PyObject_HasAttrString(arg, "__name__")) { + PyErr_Format(PyExc_TypeError, + "Positional argument has no __name__"); + goto err; + } + if (!(name = PyObject_GetAttrString(arg, "__name__")) || + !PyUnicode_Check(name)) + { + Py_XDECREF(name); + goto err; + } + if (PyList_Append(all, name) < 0) { + Py_XDECREF(name); + goto err; + } + } + else if (kwds == NULL || PyDict_Size(kwds) == 0) { + PyErr_Format( + PyExc_TypeError, + "Either a single positional or keyword arguments required"); + goto err; + } + else { + /* There are only keyword arguments, so for each of these, insert the + key in __all__ *and* bind the name/key to the value in globals. We + force the use of globals here because we're modifying the global + __all__ so it doesn't make sense to touch locals. + */ + PyObject *key, *value; + Py_ssize_t pos = 0; + + while (PyDict_Next(kwds, &pos, &key, &value)) { + if (PyList_Append(all, key) < 0) + goto err; + if (PyDict_SetItem(globals, key, value) < 0) + goto err; + } + } + + Py_XDECREF(all); + Py_RETURN_NONE; + + err: + Py_XDECREF(all); + return NULL; +} + +PyDoc_STRVAR(public_doc, +"public(named, **kws)\n\ +\n\ +May be used as a decorator or called directly. When used as a decorator\n\ +the thing being decorated must have an __name__ attribute. The value of\n\ +__name__ will be inserted in the global __all__ list. In this use case\n\ +it is an error to also include keyword arguments.\n\ +\n\ +When called directly, it is an error to pass a positional argument. The\n\ +keys must be strings, and are inserted into the global __all__ list. The\n\ +mapping of keys to values is also inserted into the globals, thus creating\n\ +name bindings for each key/value pair."); + + /*[clinic input] setattr as builtin_setattr @@ -2618,6 +2727,7 @@ BUILTIN_ORD_METHODDEF BUILTIN_POW_METHODDEF {"print", (PyCFunction)builtin_print, METH_VARARGS | METH_KEYWORDS, print_doc}, + {"public", (PyCFunction)builtin_public, METH_VARARGS | METH_KEYWORDS, public_doc}, BUILTIN_REPR_METHODDEF {"round", (PyCFunction)builtin_round, METH_VARARGS | METH_KEYWORDS, round_doc}, BUILTIN_SETATTR_METHODDEF