Skip to content

Commit

Permalink
Merge pull request #5 from ZeroIntensity/size-abi
Browse files Browse the repository at this point in the history
Size ABI
  • Loading branch information
ZeroIntensity authored Apr 30, 2024
2 parents 938bf2b + a6a3872 commit 58b89e4
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 162 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ jobs:
- name: Install PyTest
run: |
if [ "$RUNNER_OS" == "Windows" ]; then
pip install pytest pytest-asyncio
pip install pytest pytest-asyncio typing_extensions
else
pip install pytest pytest-asyncio pytest-memray
pip install pytest pytest-asyncio pytest-memray typing_extensions
fi
shell: bash

Expand Down
72 changes: 30 additions & 42 deletions include/awaitable.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,69 +11,56 @@ typedef int (*awaitcallback)(PyObject *, PyObject *);
typedef int (*awaitcallback_err)(PyObject *, PyObject *);

typedef struct _AwaitableObject AwaitableObject;
#define PYAWAITABLE_API_SIZE 10

void *awaitable_api[PYAWAITABLE_API_SIZE];

PyTypeObject AwaitableType;
PyTypeObject AwaitableGenWrapperType;
typedef struct _awaitable_abi {
Py_ssize_t size;
PyObject *(*awaitable_new)(void);
int (*awaitable_await)(PyObject *, PyObject *, awaitcallback, awaitcallback_err);
void (*awaitable_cancel)(PyObject *);
int (*awaitable_set_result)(PyObject *, PyObject *);
int (*awaitable_save)(PyObject *, Py_ssize_t, ...);
int (*awaitable_save_arb)(PyObject *, Py_ssize_t, ...);
int (*awaitable_unpack)(PyObject *, ...);
int (*awaitable_unpack_arb)(PyObject *, ...);
PyTypeObject *AwaitableType;
} AwaitableABI;

AwaitableABI* awaitable_abi = NULL;

// PyObject *awaitable_new(void);
typedef PyObject *(*_awaitable_new_type)(void);
#define awaitable_new ((_awaitable_new_type) awaitable_api[2])
#define awaitable_new awaitable_abi->awaitable_new

// int awaitable_await(PyObject *aw, PyObject *coro, awaitcallback cb, awaitcallback_err err);
typedef int (*_awaitable_await_type)(PyObject *, PyObject *, awaitcallback, awaitcallback_err);
#define awaitable_await ((_awaitable_await_type) awaitable_api[3])
#define awaitable_await awaitable_abi->awaitable_await

// void awaitable_cancel(PyObject *aw);
typedef void (*_awaitable_cancel_type)(PyObject *);
#define awaitable_cancel ((_awaitable_cancel_type) awaitable_api[4])
#define awaitable_cancel awaitable_abi->awaitable_cancel

// int awaitable_set_result(PyObject *awaitable, PyObject *result);
typedef int (*_awaitable_set_result_type)(PyObject *, PyObject *);
#define awaitable_set_result ((_awaitable_set_result_type) awaitable_api[5])
#define awaitable_set_result awaitable_abi->awaitable_set_result

// int awaitable_save(PyObject *awaitable, Py_ssize_t nargs, ...);
typedef int (*_awaitable_save_type)(PyObject *, Py_ssize_t, ...);
#define awaitable_save ((_awaitable_save_type) awaitable_api[6])
#define awaitable_save awaitable_abi->awaitable_save

// int awaitable_save_arb(PyObject *awaitable, Py_ssize_t nargs, ...);
#define awaitable_save_arb ((_awaitable_save_type) awaitable_api[7])
#define awaitable_save_arb awaitable_abi->awaitable_save_arb

// int awaitable_unpack(PyObject *awaitable, ...);
typedef int (*_awaitable_unpack_type)(PyObject *, ...);
#define awaitable_unpack ((_awaitable_unpack_type) awaitable_api[8])
#define awaitable_unpack awaitable_abi->awaitable_unpack

// int awaitable_unpack_arb(PyObject *awaitable, ...);
#define awaitable_unpack_arb ((_awaitable_unpack_type) awaitable_api[9])
#define awaitable_unpack_arb awaitable_abi->awaitable_unpack_arb

#define AwaitableType awaitable_abi->AwaitableType

static int
awaitable_init()
{
PyObject *pyawaitable = PyImport_ImportModule("pyawaitable");
if (pyawaitable == NULL)
return -1;

PyObject *c_api = PyObject_GetAttrString(pyawaitable, "_api");
Py_DECREF(pyawaitable);

if (c_api == NULL)
return -1;

if (!PyCapsule_CheckExact(c_api)) {
PyErr_SetString(PyExc_TypeError, "pyawaitable._api is not a capsule");
Py_DECREF(c_api);
return -1;
}

void** api = PyCapsule_GetPointer(c_api, NULL);
AwaitableType = *((PyTypeObject*) api[0]);
AwaitableGenWrapperType = *((PyTypeObject*) api[1]);

for (int i = 2; i < PYAWAITABLE_API_SIZE; ++i)
awaitable_api[i] = api[i];
AwaitableABI *capsule = PyCapsule_Import("pyawaitable.abi.v1", 0);
if (capsule == NULL)
return NULL;

awaitable_abi = capsule;
return 0;
}

Expand All @@ -87,7 +74,8 @@ awaitable_init()
#define PyAwaitable_UnpackValues awaitable_unpack
#define PyAwaitable_UnpackArbValues awaitable_unpack_arb
#define PyAwaitable_Init awaitable_init
#define PyAwaitable_API awaitable_api
#define PyAwaitable_ABI awaitable_abi
#define PyAwaitable_Type AwaitableType
#endif

#endif
65 changes: 65 additions & 0 deletions include/pyawaitable/backport.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#ifndef PYAWAITABLE_BACKPORT_H
#define PYAWAITABLE_BACKPORT_H

#ifndef _PyObject_Vectorcall
#define PyObject_CallNoArgs(o) PyObject_CallObject( \
o, \
NULL \
)
static PyObject* _PyObject_VectorcallBackport(PyObject* obj,
PyObject** args,
size_t nargsf, PyObject* kwargs) {
PyObject* tuple = PyTuple_New(nargsf);
if (!tuple) return NULL;
for (size_t i = 0; i < nargsf; i++) {
Py_INCREF(args[i]);
PyTuple_SET_ITEM(
tuple,
i,
args[i]
);
}
PyObject* o = PyObject_Call(
obj,
tuple,
kwargs
);
Py_DECREF(tuple);
return o;
}
#define PyObject_Vectorcall _PyObject_VectorcallBackport
#define PyObject_VectorcallDict _PyObject_FastCallDict
#endif

#if PY_VERSION_HEX < 0x030c0000
PyObject *PyErr_GetRaisedException(void) {
PyObject *type, *val, *tb;
PyErr_Fetch(&type, &val, &tb);
PyErr_NormalizeException(&type, &val, &tb);
Py_XDECREF(type);
Py_XDECREF(tb);
// technically some entry in the traceback might be lost; ignore that
return val;
}

void PyErr_SetRaisedException(PyObject *err) {
PyErr_Restore(err, NULL, NULL);
}
#endif


#ifndef Py_NewRef
static inline PyObject* Py_NewRef(PyObject* o) {
Py_INCREF(o);
return o;
}
#endif

#ifndef Py_XNewRef
static inline PyObject* Py_XNewRef(PyObject* o) {
Py_XINCREF(o);
return o;
}
#endif

#endif
9 changes: 4 additions & 5 deletions include/defines.h → include/pyawaitable/util.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
/*
* defines.h
*/
#pragma once
#ifndef PYAWAITABLE_UTIL_H
#define PYAWAITABLE_UTIL_H
#include <Python.h>
#include <structmember.h>
#include <datetime.h>
Expand Down Expand Up @@ -60,7 +58,7 @@ if (m == NULL) { \
if (PyType_Ready(&type) < 0) \
return NULL
#define PY_TYPE_ADD_TO_MODULE_OR_RETURN_NULL(name, type) \
if (PyModule_AddObject(m, Py_STRINGIFY(name), Py_NewRef(&type)) < 0) \
if (PyModule_AddObject(m, Py_STRINGIFY(name), Py_NewRef((PyObject *) &type)) < 0) \
return _DecrefModuleAndReturnNULL(m)
#define PY_ADD_CAPSULE_TO_MODULE_OR_RETURN_NULL(objname, ptr, capsuleName) \
PyObject *capsule = PyCapsule_New((void *)ptr, capsuleName, NULL); \
Expand All @@ -87,3 +85,4 @@ return m

#define PyType_CreateInstance(type, typeobj, args, kwargs) ((type *)_PyType_CreateInstance(typeobj, args, kwargs))
#define PyObject_GetCallableMethod(type, name) _PyObject_GetCallableMethod((PyObject*)type, name)
#endif
4 changes: 3 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
setup(
ext_modules=[
Extension(
"pyawaitable",
"_pyawaitable",
["./src/awaitable.c"],
include_dirs=["./include/"],
define_macros=[("PYAWAITABLE_IS_COMPILING", None)],
)
],
package_dir={"": "src"},
packages=["pyawaitable"],
data_files=[("include", ["./include/awaitable.h"])],
)
103 changes: 24 additions & 79 deletions src/awaitable.c
Original file line number Diff line number Diff line change
@@ -1,79 +1,21 @@
/* *INDENT-OFF* */
// This code follows PEP 7 and CPython ABI conventions
#include <Python.h>
#include "pyerrors.h"
// Stdlib headers
#include <stdarg.h>
#include <stdbool.h>
#include <awaitable.h>
/*
* To avoid any possible include conflicts if other
* headers have the same file name.
*/
#include "../include/defines.h"

#ifndef _PyObject_Vectorcall
#define PyObject_CallNoArgs(o) PyObject_CallObject( \
o, \
NULL \
)
static PyObject* _PyObject_VectorcallBackport(PyObject* obj,
PyObject** args,
size_t nargsf, PyObject* kwargs) {
PyObject* tuple = PyTuple_New(nargsf);
if (!tuple) return NULL;
for (size_t i = 0; i < nargsf; i++) {
Py_INCREF(args[i]);
PyTuple_SET_ITEM(
tuple,
i,
args[i]
);
}
PyObject* o = PyObject_Call(
obj,
tuple,
kwargs
);
Py_DECREF(tuple);
return o;
}
#define PyObject_Vectorcall _PyObject_VectorcallBackport
#define PyObject_VectorcallDict _PyObject_FastCallDict
#endif

#if PY_VERSION_HEX < 0x030c0000
PyObject *PyErr_GetRaisedException(void) {
PyObject *type, *val, *tb;
PyErr_Fetch(&type, &val, &tb);
PyErr_NormalizeException(&type, &val, &tb);
Py_XDECREF(type);
Py_XDECREF(tb);
// technically some entry in the traceback might be lost; ignore that
return val;
}
// Python headers
#include <Python.h>
#include "pyerrors.h"

void PyErr_SetRaisedException(PyObject *err) {
PyErr_Restore(err, NULL, NULL);
}
#endif
// PyAwaitable headers
#include <awaitable.h>
#include <pyawaitable/util.h>
#include <pyawaitable/backport.h>

// forward declaration
static PyTypeObject _AwaitableGenWrapperType;

#ifndef Py_NewRef
static inline PyObject* Py_NewRef(PyObject* o) {
Py_INCREF(o);
return o;
}
#endif

#ifndef Py_XNewRef
static inline PyObject* Py_XNewRef(PyObject* o) {
Py_XINCREF(o);
return o;
}
#endif

typedef struct {
PyObject *coro;
awaitcallback callback;
Expand Down Expand Up @@ -762,24 +704,27 @@ static PyModuleDef awaitable_module = {
-1
};

PyMODINIT_FUNC PyInit_pyawaitable()
static AwaitableABI _abi_interface = {
sizeof(AwaitableABI),
_awaitable_new,
_awaitable_await,
_awaitable_cancel,
_awaitable_set_result,
_awaitable_save,
_awaitable_save_arb,
_awaitable_unpack,
_awaitable_unpack_arb,
&_AwaitableType
};

PyMODINIT_FUNC PyInit__pyawaitable()
{
PY_TYPE_IS_READY_OR_RETURN_NULL(_AwaitableType);
PY_TYPE_IS_READY_OR_RETURN_NULL(_AwaitableGenWrapperType);
PY_CREATE_MODULE(awaitable_module);
PY_TYPE_ADD_TO_MODULE_OR_RETURN_NULL(_awaitable, _AwaitableType);
PY_TYPE_ADD_TO_MODULE_OR_RETURN_NULL(_genwrapper, _AwaitableGenWrapperType);

awaitable_api[0] = &_AwaitableType;
awaitable_api[1] = &_AwaitableGenWrapperType;
awaitable_api[2] = _awaitable_new;
awaitable_api[3] = _awaitable_await;
awaitable_api[4] = _awaitable_cancel;
awaitable_api[5] = _awaitable_set_result;
awaitable_api[6] = _awaitable_save;
awaitable_api[7] = _awaitable_save_arb;
awaitable_api[8] = _awaitable_unpack;
awaitable_api[9] = _awaitable_unpack_arb;
PY_ADD_CAPSULE_TO_MODULE_OR_RETURN_NULL(_api, awaitable_api, NULL);

PY_ADD_CAPSULE_TO_MODULE_OR_RETURN_NULL(abiv1, &_abi_interface, "pyawaitable.abi.v1");
return m;
}
16 changes: 16 additions & 0 deletions src/pyawaitable/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""
PyAwaitable - CPython API for asynchronous functions.
Docs: https://awaitable.zintensity.dev/
Source: https://github.com/ZeroIntensity/pyawaitable
You probably don't need this module from Python! This module is for use from the C API.
"""

from . import abi
from _pyawaitable import _awaitable
from typing import Type

__all__ = "abi", "Awaitable"

Awaitable: Type = _awaitable
5 changes: 5 additions & 0 deletions src/pyawaitable/abi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from _pyawaitable import abiv1

__all__ = "v1",

v1 = abiv1
Loading

0 comments on commit 58b89e4

Please sign in to comment.