usse/funda-scraper/venv/lib/python3.10/site-packages/mypyc/lib-rt/misc_ops.c

786 lines
24 KiB
C
Raw Normal View History

2023-02-20 22:38:24 +00:00
// 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;
}