786 lines
24 KiB
C
786 lines
24 KiB
C
|
// Misc primitive operations + C helpers
|
||
|
//
|
||
|
// These are registered in mypyc.primitives.misc_ops.
|
||
|
|
||
|
#include <Python.h>
|
||
|
#include "CPy.h"
|
||
|
|
||
|
PyObject *CPy_GetCoro(PyObject *obj)
|
||
|
{
|
||
|
// If the type has an __await__ method, call it,
|
||
|
// otherwise, fallback to calling __iter__.
|
||
|
PyAsyncMethods* async_struct = Py_TYPE(obj)->tp_as_async;
|
||
|
if (async_struct != NULL && async_struct->am_await != NULL) {
|
||
|
return (async_struct->am_await)(obj);
|
||
|
} else {
|
||
|
// TODO: We should check that the type is a generator decorated with
|
||
|
// asyncio.coroutine
|
||
|
return PyObject_GetIter(obj);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PyObject *CPyIter_Send(PyObject *iter, PyObject *val)
|
||
|
{
|
||
|
// Do a send, or a next if second arg is None.
|
||
|
// (This behavior is to match the PEP 380 spec for yield from.)
|
||
|
_Py_IDENTIFIER(send);
|
||
|
if (Py_IsNone(val)) {
|
||
|
return CPyIter_Next(iter);
|
||
|
} else {
|
||
|
return _PyObject_CallMethodIdOneArg(iter, &PyId_send, val);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// A somewhat hairy implementation of specifically most of the error handling
|
||
|
// in `yield from` error handling. The point here is to reduce code size.
|
||
|
//
|
||
|
// This implements most of the bodies of the `except` blocks in the
|
||
|
// pseudocode in PEP 380.
|
||
|
//
|
||
|
// Returns true (1) if a StopIteration was received and we should return.
|
||
|
// Returns false (0) if a value should be yielded.
|
||
|
// In both cases the value is stored in outp.
|
||
|
// Signals an error (2) if the an exception should be propagated.
|
||
|
int CPy_YieldFromErrorHandle(PyObject *iter, PyObject **outp)
|
||
|
{
|
||
|
_Py_IDENTIFIER(close);
|
||
|
_Py_IDENTIFIER(throw);
|
||
|
PyObject *exc_type = (PyObject *)Py_TYPE(CPy_ExcState()->exc_value);
|
||
|
PyObject *type, *value, *traceback;
|
||
|
PyObject *_m;
|
||
|
PyObject *res;
|
||
|
*outp = NULL;
|
||
|
|
||
|
if (PyErr_GivenExceptionMatches(exc_type, PyExc_GeneratorExit)) {
|
||
|
_m = _PyObject_GetAttrId(iter, &PyId_close);
|
||
|
if (_m) {
|
||
|
res = PyObject_CallNoArgs(_m);
|
||
|
Py_DECREF(_m);
|
||
|
if (!res)
|
||
|
return 2;
|
||
|
Py_DECREF(res);
|
||
|
} else if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
||
|
PyErr_Clear();
|
||
|
} else {
|
||
|
return 2;
|
||
|
}
|
||
|
} else {
|
||
|
_m = _PyObject_GetAttrId(iter, &PyId_throw);
|
||
|
if (_m) {
|
||
|
_CPy_GetExcInfo(&type, &value, &traceback);
|
||
|
res = PyObject_CallFunctionObjArgs(_m, type, value, traceback, NULL);
|
||
|
Py_DECREF(type);
|
||
|
Py_DECREF(value);
|
||
|
Py_DECREF(traceback);
|
||
|
Py_DECREF(_m);
|
||
|
if (res) {
|
||
|
*outp = res;
|
||
|
return 0;
|
||
|
} else {
|
||
|
res = CPy_FetchStopIterationValue();
|
||
|
if (res) {
|
||
|
*outp = res;
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
} else if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
||
|
PyErr_Clear();
|
||
|
} else {
|
||
|
return 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CPy_Reraise();
|
||
|
return 2;
|
||
|
}
|
||
|
|
||
|
PyObject *CPy_FetchStopIterationValue(void)
|
||
|
{
|
||
|
PyObject *val = NULL;
|
||
|
_PyGen_FetchStopIterationValue(&val);
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
static bool _CPy_IsSafeMetaClass(PyTypeObject *metaclass) {
|
||
|
// mypyc classes can't work with metaclasses in
|
||
|
// general. Through some various nasty hacks we *do*
|
||
|
// manage to work with TypingMeta and its friends.
|
||
|
if (metaclass == &PyType_Type)
|
||
|
return true;
|
||
|
PyObject *module = PyObject_GetAttrString((PyObject *)metaclass, "__module__");
|
||
|
if (!module) {
|
||
|
PyErr_Clear();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool matches = false;
|
||
|
if (PyUnicode_CompareWithASCIIString(module, "typing") == 0 &&
|
||
|
(strcmp(metaclass->tp_name, "TypingMeta") == 0
|
||
|
|| strcmp(metaclass->tp_name, "GenericMeta") == 0
|
||
|
|| strcmp(metaclass->tp_name, "_ProtocolMeta") == 0)) {
|
||
|
matches = true;
|
||
|
} else if (PyUnicode_CompareWithASCIIString(module, "typing_extensions") == 0 &&
|
||
|
strcmp(metaclass->tp_name, "_ProtocolMeta") == 0) {
|
||
|
matches = true;
|
||
|
} else if (PyUnicode_CompareWithASCIIString(module, "abc") == 0 &&
|
||
|
strcmp(metaclass->tp_name, "ABCMeta") == 0) {
|
||
|
matches = true;
|
||
|
}
|
||
|
Py_DECREF(module);
|
||
|
return matches;
|
||
|
}
|
||
|
|
||
|
// Create a heap type based on a template non-heap type.
|
||
|
// This is super hacky and maybe we should suck it up and use PyType_FromSpec instead.
|
||
|
// We allow bases to be NULL to represent just inheriting from object.
|
||
|
// We don't support NULL bases and a non-type metaclass.
|
||
|
PyObject *CPyType_FromTemplate(PyObject *template,
|
||
|
PyObject *orig_bases,
|
||
|
PyObject *modname) {
|
||
|
PyTypeObject *template_ = (PyTypeObject *)template;
|
||
|
PyHeapTypeObject *t = NULL;
|
||
|
PyTypeObject *dummy_class = NULL;
|
||
|
PyObject *name = NULL;
|
||
|
PyObject *bases = NULL;
|
||
|
PyObject *slots;
|
||
|
|
||
|
// If the type of the class (the metaclass) is NULL, we default it
|
||
|
// to being type. (This allows us to avoid needing to initialize
|
||
|
// it explicitly on windows.)
|
||
|
if (!Py_TYPE(template_)) {
|
||
|
Py_SET_TYPE(template_, &PyType_Type);
|
||
|
}
|
||
|
PyTypeObject *metaclass = Py_TYPE(template_);
|
||
|
|
||
|
if (orig_bases) {
|
||
|
bases = update_bases(orig_bases);
|
||
|
// update_bases doesn't increment the refcount if nothing changes,
|
||
|
// so we do it to make sure we have distinct "references" to both
|
||
|
if (bases == orig_bases)
|
||
|
Py_INCREF(bases);
|
||
|
|
||
|
// Find the appropriate metaclass from our base classes. We
|
||
|
// care about this because Generic uses a metaclass prior to
|
||
|
// Python 3.7.
|
||
|
metaclass = _PyType_CalculateMetaclass(metaclass, bases);
|
||
|
if (!metaclass)
|
||
|
goto error;
|
||
|
|
||
|
if (!_CPy_IsSafeMetaClass(metaclass)) {
|
||
|
PyErr_SetString(PyExc_TypeError, "mypyc classes can't have a metaclass");
|
||
|
goto error;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
name = PyUnicode_FromString(template_->tp_name);
|
||
|
if (!name)
|
||
|
goto error;
|
||
|
|
||
|
// If there is a metaclass other than type, we would like to call
|
||
|
// its __new__ function. Unfortunately there doesn't seem to be a
|
||
|
// good way to mix a C extension class and creating it via a
|
||
|
// metaclass. We need to do it anyways, though, in order to
|
||
|
// support subclassing Generic[T] prior to Python 3.7.
|
||
|
//
|
||
|
// We solve this with a kind of atrocious hack: create a parallel
|
||
|
// class using the metaclass, determine the bases of the real
|
||
|
// class by pulling them out of the parallel class, creating the
|
||
|
// real class, and then merging its dict back into the original
|
||
|
// class. There are lots of cases where this won't really work,
|
||
|
// but for the case of GenericMeta setting a bunch of properties
|
||
|
// on the class we should be fine.
|
||
|
if (metaclass != &PyType_Type) {
|
||
|
assert(bases && "non-type metaclasses require non-NULL bases");
|
||
|
|
||
|
PyObject *ns = PyDict_New();
|
||
|
if (!ns)
|
||
|
goto error;
|
||
|
|
||
|
if (bases != orig_bases) {
|
||
|
if (PyDict_SetItemString(ns, "__orig_bases__", orig_bases) < 0)
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
dummy_class = (PyTypeObject *)PyObject_CallFunctionObjArgs(
|
||
|
(PyObject *)metaclass, name, bases, ns, NULL);
|
||
|
Py_DECREF(ns);
|
||
|
if (!dummy_class)
|
||
|
goto error;
|
||
|
|
||
|
Py_DECREF(bases);
|
||
|
bases = dummy_class->tp_bases;
|
||
|
Py_INCREF(bases);
|
||
|
}
|
||
|
|
||
|
// Allocate the type and then copy the main stuff in.
|
||
|
t = (PyHeapTypeObject*)PyType_GenericAlloc(&PyType_Type, 0);
|
||
|
if (!t)
|
||
|
goto error;
|
||
|
memcpy((char *)t + sizeof(PyVarObject),
|
||
|
(char *)template_ + sizeof(PyVarObject),
|
||
|
sizeof(PyTypeObject) - sizeof(PyVarObject));
|
||
|
|
||
|
if (bases != orig_bases) {
|
||
|
if (PyObject_SetAttrString((PyObject *)t, "__orig_bases__", orig_bases) < 0)
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
// Having tp_base set is I think required for stuff to get
|
||
|
// inherited in PyType_Ready, which we needed for subclassing
|
||
|
// BaseException. XXX: Taking the first element is wrong I think though.
|
||
|
if (bases) {
|
||
|
t->ht_type.tp_base = (PyTypeObject *)PyTuple_GET_ITEM(bases, 0);
|
||
|
Py_INCREF((PyObject *)t->ht_type.tp_base);
|
||
|
}
|
||
|
|
||
|
t->ht_name = name;
|
||
|
Py_INCREF(name);
|
||
|
t->ht_qualname = name;
|
||
|
t->ht_type.tp_bases = bases;
|
||
|
// references stolen so NULL these out
|
||
|
bases = name = NULL;
|
||
|
|
||
|
if (PyType_Ready((PyTypeObject *)t) < 0)
|
||
|
goto error;
|
||
|
|
||
|
assert(t->ht_type.tp_base != NULL);
|
||
|
|
||
|
// XXX: This is a terrible hack to work around a cpython check on
|
||
|
// the mro. It was needed for mypy.stats. I need to investigate
|
||
|
// what is actually going on here.
|
||
|
Py_INCREF(metaclass);
|
||
|
Py_SET_TYPE(t, metaclass);
|
||
|
|
||
|
if (dummy_class) {
|
||
|
if (PyDict_Merge(t->ht_type.tp_dict, dummy_class->tp_dict, 0) != 0)
|
||
|
goto error;
|
||
|
// This is the *really* tasteless bit. GenericMeta's __new__
|
||
|
// in certain versions of typing sets _gorg to point back to
|
||
|
// the class. We need to override it to keep it from pointing
|
||
|
// to the proxy.
|
||
|
if (PyDict_SetItemString(t->ht_type.tp_dict, "_gorg", (PyObject *)t) < 0)
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
// Reject anything that would give us a nontrivial __slots__,
|
||
|
// because the layout will conflict
|
||
|
slots = PyObject_GetAttrString((PyObject *)t, "__slots__");
|
||
|
if (slots) {
|
||
|
// don't fail on an empty __slots__
|
||
|
int is_true = PyObject_IsTrue(slots);
|
||
|
Py_DECREF(slots);
|
||
|
if (is_true > 0)
|
||
|
PyErr_SetString(PyExc_TypeError, "mypyc classes can't have __slots__");
|
||
|
if (is_true != 0)
|
||
|
goto error;
|
||
|
} else {
|
||
|
PyErr_Clear();
|
||
|
}
|
||
|
|
||
|
if (PyObject_SetAttrString((PyObject *)t, "__module__", modname) < 0)
|
||
|
goto error;
|
||
|
|
||
|
if (init_subclass((PyTypeObject *)t, NULL))
|
||
|
goto error;
|
||
|
|
||
|
Py_XDECREF(dummy_class);
|
||
|
|
||
|
return (PyObject *)t;
|
||
|
|
||
|
error:
|
||
|
Py_XDECREF(t);
|
||
|
Py_XDECREF(bases);
|
||
|
Py_XDECREF(dummy_class);
|
||
|
Py_XDECREF(name);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static int _CPy_UpdateObjFromDict(PyObject *obj, PyObject *dict)
|
||
|
{
|
||
|
Py_ssize_t pos = 0;
|
||
|
PyObject *key, *value;
|
||
|
while (PyDict_Next(dict, &pos, &key, &value)) {
|
||
|
if (PyObject_SetAttr(obj, key, value) != 0) {
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Support for our partial built-in support for dataclasses.
|
||
|
*
|
||
|
* Take a class we want to make a dataclass, remove any descriptors
|
||
|
* for annotated attributes, swap in the actual values of the class
|
||
|
* variables invoke dataclass, and then restore all of the
|
||
|
* descriptors.
|
||
|
*
|
||
|
* The purpose of all this is that dataclasses uses the values of
|
||
|
* class variables to drive which attributes are required and what the
|
||
|
* default values/factories are for optional attributes. This means
|
||
|
* that the class dict needs to contain those values instead of getset
|
||
|
* descriptors for the attributes when we invoke dataclass.
|
||
|
*
|
||
|
* We need to remove descriptors for attributes even when there is no
|
||
|
* default value for them, or else dataclass will think the descriptor
|
||
|
* is the default value. We remove only the attributes, since we don't
|
||
|
* want dataclasses to try generating functions when they are already
|
||
|
* implemented.
|
||
|
*
|
||
|
* Args:
|
||
|
* dataclass_dec: The decorator to apply
|
||
|
* tp: The class we are making a dataclass
|
||
|
* dict: The dictionary containing values that dataclasses needs
|
||
|
* annotations: The type annotation dictionary
|
||
|
*/
|
||
|
int
|
||
|
CPyDataclass_SleightOfHand(PyObject *dataclass_dec, PyObject *tp,
|
||
|
PyObject *dict, PyObject *annotations) {
|
||
|
PyTypeObject *ttp = (PyTypeObject *)tp;
|
||
|
Py_ssize_t pos;
|
||
|
PyObject *res;
|
||
|
|
||
|
/* Make a copy of the original class __dict__ */
|
||
|
PyObject *orig_dict = PyDict_Copy(ttp->tp_dict);
|
||
|
if (!orig_dict) {
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
/* Delete anything that had an annotation */
|
||
|
pos = 0;
|
||
|
PyObject *key;
|
||
|
while (PyDict_Next(annotations, &pos, &key, NULL)) {
|
||
|
if (PyObject_DelAttr(tp, key) != 0) {
|
||
|
goto fail;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Copy in all the attributes that we want dataclass to see */
|
||
|
if (_CPy_UpdateObjFromDict(tp, dict) != 0) {
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
/* Run the @dataclass descriptor */
|
||
|
res = PyObject_CallOneArg(dataclass_dec, tp);
|
||
|
if (!res) {
|
||
|
goto fail;
|
||
|
}
|
||
|
Py_DECREF(res);
|
||
|
|
||
|
/* Copy back the original contents of the dict */
|
||
|
if (_CPy_UpdateObjFromDict(tp, orig_dict) != 0) {
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
Py_DECREF(orig_dict);
|
||
|
return 1;
|
||
|
|
||
|
fail:
|
||
|
Py_XDECREF(orig_dict);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Support for pickling; reusable getstate and setstate functions
|
||
|
PyObject *
|
||
|
CPyPickle_SetState(PyObject *obj, PyObject *state)
|
||
|
{
|
||
|
if (_CPy_UpdateObjFromDict(obj, state) != 0) {
|
||
|
return NULL;
|
||
|
}
|
||
|
Py_RETURN_NONE;
|
||
|
}
|
||
|
|
||
|
PyObject *
|
||
|
CPyPickle_GetState(PyObject *obj)
|
||
|
{
|
||
|
PyObject *attrs = NULL, *state = NULL;
|
||
|
|
||
|
attrs = PyObject_GetAttrString((PyObject *)Py_TYPE(obj), "__mypyc_attrs__");
|
||
|
if (!attrs) {
|
||
|
goto fail;
|
||
|
}
|
||
|
if (!PyTuple_Check(attrs)) {
|
||
|
PyErr_SetString(PyExc_TypeError, "__mypyc_attrs__ is not a tuple");
|
||
|
goto fail;
|
||
|
}
|
||
|
state = PyDict_New();
|
||
|
if (!state) {
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
// Collect all the values of attributes in __mypyc_attrs__
|
||
|
// Attributes that are missing we just ignore
|
||
|
int i;
|
||
|
for (i = 0; i < PyTuple_GET_SIZE(attrs); i++) {
|
||
|
PyObject *key = PyTuple_GET_ITEM(attrs, i);
|
||
|
PyObject *value = PyObject_GetAttr(obj, key);
|
||
|
if (!value) {
|
||
|
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
||
|
PyErr_Clear();
|
||
|
continue;
|
||
|
}
|
||
|
goto fail;
|
||
|
}
|
||
|
int result = PyDict_SetItem(state, key, value);
|
||
|
Py_DECREF(value);
|
||
|
if (result != 0) {
|
||
|
goto fail;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Py_DECREF(attrs);
|
||
|
|
||
|
return state;
|
||
|
fail:
|
||
|
Py_XDECREF(attrs);
|
||
|
Py_XDECREF(state);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
CPyTagged CPyTagged_Id(PyObject *o) {
|
||
|
return CPyTagged_FromVoidPtr(o);
|
||
|
}
|
||
|
|
||
|
#define MAX_INT_CHARS 22
|
||
|
#define _PyUnicode_LENGTH(op) \
|
||
|
(((PyASCIIObject *)(op))->length)
|
||
|
|
||
|
// using snprintf or PyUnicode_FromFormat was way slower than
|
||
|
// boxing the int and calling PyObject_Str on it, so we implement our own
|
||
|
static int fmt_ssize_t(char *out, Py_ssize_t n) {
|
||
|
bool neg = n < 0;
|
||
|
if (neg) n = -n;
|
||
|
|
||
|
// buf gets filled backward and then we copy it forward
|
||
|
char buf[MAX_INT_CHARS];
|
||
|
int i = 0;
|
||
|
do {
|
||
|
buf[i] = (n % 10) + '0';
|
||
|
n /= 10;
|
||
|
i++;
|
||
|
} while (n);
|
||
|
|
||
|
|
||
|
int len = i;
|
||
|
int j = 0;
|
||
|
if (neg) {
|
||
|
out[j++] = '-';
|
||
|
len++;
|
||
|
}
|
||
|
|
||
|
for (; j < len; j++, i--) {
|
||
|
out[j] = buf[i-1];
|
||
|
}
|
||
|
out[j] = '\0';
|
||
|
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
static PyObject *CPyTagged_ShortToStr(Py_ssize_t n) {
|
||
|
PyObject *obj = PyUnicode_New(MAX_INT_CHARS, 127);
|
||
|
if (!obj) return NULL;
|
||
|
int len = fmt_ssize_t((char *)PyUnicode_1BYTE_DATA(obj), n);
|
||
|
_PyUnicode_LENGTH(obj) = len;
|
||
|
return obj;
|
||
|
}
|
||
|
|
||
|
PyObject *CPyTagged_Str(CPyTagged n) {
|
||
|
if (CPyTagged_CheckShort(n)) {
|
||
|
return CPyTagged_ShortToStr(CPyTagged_ShortAsSsize_t(n));
|
||
|
} else {
|
||
|
return PyObject_Str(CPyTagged_AsObject(n));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CPyDebug_Print(const char *msg) {
|
||
|
printf("%s\n", msg);
|
||
|
fflush(stdout);
|
||
|
}
|
||
|
|
||
|
int CPySequence_CheckUnpackCount(PyObject *sequence, Py_ssize_t expected) {
|
||
|
Py_ssize_t actual = Py_SIZE(sequence);
|
||
|
if (unlikely(actual != expected)) {
|
||
|
if (actual < expected) {
|
||
|
PyErr_Format(PyExc_ValueError, "not enough values to unpack (expected %zd, got %zd)",
|
||
|
expected, actual);
|
||
|
} else {
|
||
|
PyErr_Format(PyExc_ValueError, "too many values to unpack (expected %zd)", expected);
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Parse an integer (size_t) encoded as a variable-length binary sequence.
|
||
|
static const char *parse_int(const char *s, size_t *len) {
|
||
|
Py_ssize_t n = 0;
|
||
|
while ((unsigned char)*s >= 0x80) {
|
||
|
n = (n << 7) + (*s & 0x7f);
|
||
|
s++;
|
||
|
}
|
||
|
n = (n << 7) | *s++;
|
||
|
*len = n;
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
// Initialize static constant array of literal values
|
||
|
int CPyStatics_Initialize(PyObject **statics,
|
||
|
const char * const *strings,
|
||
|
const char * const *bytestrings,
|
||
|
const char * const *ints,
|
||
|
const double *floats,
|
||
|
const double *complex_numbers,
|
||
|
const int *tuples) {
|
||
|
PyObject **result = statics;
|
||
|
// Start with some hard-coded values
|
||
|
*result++ = Py_None;
|
||
|
Py_INCREF(Py_None);
|
||
|
*result++ = Py_False;
|
||
|
Py_INCREF(Py_False);
|
||
|
*result++ = Py_True;
|
||
|
Py_INCREF(Py_True);
|
||
|
if (strings) {
|
||
|
for (; **strings != '\0'; strings++) {
|
||
|
size_t num;
|
||
|
const char *data = *strings;
|
||
|
data = parse_int(data, &num);
|
||
|
while (num-- > 0) {
|
||
|
size_t len;
|
||
|
data = parse_int(data, &len);
|
||
|
PyObject *obj = PyUnicode_FromStringAndSize(data, len);
|
||
|
if (obj == NULL) {
|
||
|
return -1;
|
||
|
}
|
||
|
PyUnicode_InternInPlace(&obj);
|
||
|
*result++ = obj;
|
||
|
data += len;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (bytestrings) {
|
||
|
for (; **bytestrings != '\0'; bytestrings++) {
|
||
|
size_t num;
|
||
|
const char *data = *bytestrings;
|
||
|
data = parse_int(data, &num);
|
||
|
while (num-- > 0) {
|
||
|
size_t len;
|
||
|
data = parse_int(data, &len);
|
||
|
PyObject *obj = PyBytes_FromStringAndSize(data, len);
|
||
|
if (obj == NULL) {
|
||
|
return -1;
|
||
|
}
|
||
|
*result++ = obj;
|
||
|
data += len;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (ints) {
|
||
|
for (; **ints != '\0'; ints++) {
|
||
|
size_t num;
|
||
|
const char *data = *ints;
|
||
|
data = parse_int(data, &num);
|
||
|
while (num-- > 0) {
|
||
|
char *end;
|
||
|
PyObject *obj = PyLong_FromString(data, &end, 10);
|
||
|
if (obj == NULL) {
|
||
|
return -1;
|
||
|
}
|
||
|
data = end;
|
||
|
data++;
|
||
|
*result++ = obj;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (floats) {
|
||
|
size_t num = (size_t)*floats++;
|
||
|
while (num-- > 0) {
|
||
|
PyObject *obj = PyFloat_FromDouble(*floats++);
|
||
|
if (obj == NULL) {
|
||
|
return -1;
|
||
|
}
|
||
|
*result++ = obj;
|
||
|
}
|
||
|
}
|
||
|
if (complex_numbers) {
|
||
|
size_t num = (size_t)*complex_numbers++;
|
||
|
while (num-- > 0) {
|
||
|
double real = *complex_numbers++;
|
||
|
double imag = *complex_numbers++;
|
||
|
PyObject *obj = PyComplex_FromDoubles(real, imag);
|
||
|
if (obj == NULL) {
|
||
|
return -1;
|
||
|
}
|
||
|
*result++ = obj;
|
||
|
}
|
||
|
}
|
||
|
if (tuples) {
|
||
|
int num = *tuples++;
|
||
|
while (num-- > 0) {
|
||
|
int num_items = *tuples++;
|
||
|
PyObject *obj = PyTuple_New(num_items);
|
||
|
if (obj == NULL) {
|
||
|
return -1;
|
||
|
}
|
||
|
int i;
|
||
|
for (i = 0; i < num_items; i++) {
|
||
|
PyObject *item = statics[*tuples++];
|
||
|
Py_INCREF(item);
|
||
|
PyTuple_SET_ITEM(obj, i, item);
|
||
|
}
|
||
|
*result++ = obj;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Call super(type(self), self)
|
||
|
PyObject *
|
||
|
CPy_Super(PyObject *builtins, PyObject *self) {
|
||
|
PyObject *super_type = PyObject_GetAttrString(builtins, "super");
|
||
|
if (!super_type)
|
||
|
return NULL;
|
||
|
PyObject *result = PyObject_CallFunctionObjArgs(
|
||
|
super_type, (PyObject*)Py_TYPE(self), self, NULL);
|
||
|
Py_DECREF(super_type);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
// This helper function is a simplification of cpython/ceval.c/import_from()
|
||
|
PyObject *CPyImport_ImportFrom(PyObject *module, PyObject *package_name,
|
||
|
PyObject *import_name, PyObject *as_name) {
|
||
|
// check if the imported module has an attribute by that name
|
||
|
PyObject *x = PyObject_GetAttr(module, import_name);
|
||
|
if (x == NULL) {
|
||
|
// if not, attempt to import a submodule with that name
|
||
|
PyObject *fullmodname = PyUnicode_FromFormat("%U.%U", package_name, import_name);
|
||
|
if (fullmodname == NULL) {
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
// The following code is a simplification of cpython/import.c/PyImport_GetModule()
|
||
|
x = PyObject_GetItem(module, fullmodname);
|
||
|
Py_DECREF(fullmodname);
|
||
|
if (x == NULL) {
|
||
|
goto fail;
|
||
|
}
|
||
|
}
|
||
|
return x;
|
||
|
|
||
|
fail:
|
||
|
PyErr_Clear();
|
||
|
PyObject *package_path = PyModule_GetFilenameObject(module);
|
||
|
PyObject *errmsg = PyUnicode_FromFormat("cannot import name %R from %R (%S)",
|
||
|
import_name, package_name, package_path);
|
||
|
// NULL checks for errmsg and package_name done by PyErr_SetImportError.
|
||
|
PyErr_SetImportError(errmsg, package_name, package_path);
|
||
|
Py_DECREF(package_path);
|
||
|
Py_DECREF(errmsg);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// From CPython
|
||
|
static PyObject *
|
||
|
CPy_BinopTypeError(PyObject *left, PyObject *right, const char *op) {
|
||
|
PyErr_Format(PyExc_TypeError,
|
||
|
"unsupported operand type(s) for %.100s: "
|
||
|
"'%.100s' and '%.100s'",
|
||
|
op,
|
||
|
Py_TYPE(left)->tp_name,
|
||
|
Py_TYPE(right)->tp_name);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
PyObject *
|
||
|
CPy_CallReverseOpMethod(PyObject *left,
|
||
|
PyObject *right,
|
||
|
const char *op,
|
||
|
_Py_Identifier *method) {
|
||
|
// Look up reverse method
|
||
|
PyObject *m = _PyObject_GetAttrId(right, method);
|
||
|
if (m == NULL) {
|
||
|
// If reverse method not defined, generate TypeError instead AttributeError
|
||
|
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
||
|
CPy_BinopTypeError(left, right, op);
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
// Call reverse method
|
||
|
PyObject *result = PyObject_CallOneArg(m, left);
|
||
|
Py_DECREF(m);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
PyObject *CPySingledispatch_RegisterFunction(PyObject *singledispatch_func,
|
||
|
PyObject *cls,
|
||
|
PyObject *func) {
|
||
|
PyObject *registry = PyObject_GetAttrString(singledispatch_func, "registry");
|
||
|
PyObject *register_func = NULL;
|
||
|
PyObject *typing = NULL;
|
||
|
PyObject *get_type_hints = NULL;
|
||
|
PyObject *type_hints = NULL;
|
||
|
|
||
|
if (registry == NULL) goto fail;
|
||
|
if (func == NULL) {
|
||
|
// one argument case
|
||
|
if (PyType_Check(cls)) {
|
||
|
// passed a class
|
||
|
// bind cls to the first argument so that register gets called again with both the
|
||
|
// class and the function
|
||
|
register_func = PyObject_GetAttrString(singledispatch_func, "register");
|
||
|
if (register_func == NULL) goto fail;
|
||
|
return PyMethod_New(register_func, cls);
|
||
|
}
|
||
|
// passed a function
|
||
|
PyObject *annotations = PyFunction_GetAnnotations(cls);
|
||
|
const char *invalid_first_arg_msg =
|
||
|
"Invalid first argument to `register()`: %R. "
|
||
|
"Use either `@register(some_class)` or plain `@register` "
|
||
|
"on an annotated function.";
|
||
|
|
||
|
if (annotations == NULL) {
|
||
|
PyErr_Format(PyExc_TypeError, invalid_first_arg_msg, cls);
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
Py_INCREF(annotations);
|
||
|
|
||
|
func = cls;
|
||
|
typing = PyImport_ImportModule("typing");
|
||
|
if (typing == NULL) goto fail;
|
||
|
get_type_hints = PyObject_GetAttrString(typing, "get_type_hints");
|
||
|
|
||
|
type_hints = PyObject_CallOneArg(get_type_hints, func);
|
||
|
PyObject *argname;
|
||
|
Py_ssize_t pos = 0;
|
||
|
if (!PyDict_Next(type_hints, &pos, &argname, &cls)) {
|
||
|
// the functools implementation raises the same type error if annotations is an empty dict
|
||
|
PyErr_Format(PyExc_TypeError, invalid_first_arg_msg, cls);
|
||
|
goto fail;
|
||
|
}
|
||
|
if (!PyType_Check(cls)) {
|
||
|
const char *invalid_annotation_msg = "Invalid annotation for %R. %R is not a class.";
|
||
|
PyErr_Format(PyExc_TypeError, invalid_annotation_msg, argname, cls);
|
||
|
goto fail;
|
||
|
}
|
||
|
}
|
||
|
if (PyDict_SetItem(registry, cls, func) == -1) {
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
// clear the cache so we consider the newly added function when dispatching
|
||
|
PyObject *dispatch_cache = PyObject_GetAttrString(singledispatch_func, "dispatch_cache");
|
||
|
if (dispatch_cache == NULL) goto fail;
|
||
|
PyDict_Clear(dispatch_cache);
|
||
|
|
||
|
Py_INCREF(func);
|
||
|
return func;
|
||
|
|
||
|
fail:
|
||
|
Py_XDECREF(registry);
|
||
|
Py_XDECREF(register_func);
|
||
|
Py_XDECREF(typing);
|
||
|
Py_XDECREF(get_type_hints);
|
||
|
Py_XDECREF(type_hints);
|
||
|
return NULL;
|
||
|
|
||
|
}
|