File: public.c

package info (click to toggle)
python-public 0.5-1
  • links: PTS, VCS
  • area: main
  • in suites: buster, stretch
  • size: 448 kB
  • ctags: 69
  • sloc: python: 382; ansic: 110; makefile: 8; sh: 3
file content (153 lines) | stat: -rw-r--r-- 4,488 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
Copyright (C) 2016 Barry Warsaw

This project is licensed under the terms of the Apache 2.0 License.  See
LICENSE.txt for details.
*/

#include <Python.h>


static PyObject *
public(PyObject *self, PyObject *args, PyObject *kwds)
{
    PyObject *arg = NULL;
    PyObject *globals = NULL;
    PyObject *all = NULL;
    PyObject *rtn = 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 (!(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 byebye;
    }
    else {
        /* Make sure __all__ is a list, raising an exception if not.  Bump
           all's reference count since it's currently borrowed, and this way
           we guarantee we own a reference for common exit cleanup.
        */
        if (!PyList_Check(all)) {
            PyErr_Format(PyExc_ValueError,
                         "__all__ must be a list not: %S",
                         all->ob_type);
            goto byebye;
        }
        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 byebye;
        }
        if (!PyObject_HasAttrString(arg, "__name__")) {
            PyErr_Format(PyExc_TypeError,
                         "Positional argument has no __name__");
            goto byebye;
        }
        if (!(name = PyObject_GetAttrString(arg, "__name__")) ||
            !PyUnicode_Check(name))
        {
            PyErr_Format(PyExc_TypeError, "Bad __name__ value");
            Py_XDECREF(name);
            goto byebye;
        }
        if (PyList_Append(all, name) < 0) {
            Py_DECREF(name);
            goto byebye;
        }
        Py_DECREF(name);
        rtn = arg;
    }
    else if (kwds == NULL || PyDict_Size(kwds) == 0) {
        PyErr_Format(
            PyExc_TypeError,
            "Either a single positional or keyword arguments required");
        goto byebye;
    }
    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 byebye;
            if (PyDict_SetItem(globals, key, value) < 0)
                goto byebye;
        }
        rtn = Py_None;
    }

  byebye:
    Py_DECREF(all);
    Py_XINCREF(rtn);
    return rtn;
}

PyDoc_STRVAR(public_doc,
"public(named)\n\
public(**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.");


static PyMethodDef public_methods[] = {
    {"public",  (PyCFunction)public, METH_VARARGS | METH_KEYWORDS, public_doc},
    {NULL,  NULL}
};


static struct PyModuleDef public_def = {
    PyModuleDef_HEAD_INIT,
    "public",
    NULL,
    0,                          /* size of module state */
    public_methods,
    NULL
};


PyObject *
PyInit__public(void)
{
    return PyModule_Create(&public_def);
}

/*
 * Local Variables:
 * c-file-style: "python3"
 * End:
 */