GeezerVison is showing github.com — gh-86542: New C-APIs to simplify module attribute declaration by tiran · Pull Request #23286 · python/cpython
gh-86542: New C-APIs to simplify module attribute declaration
#23286
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and
privacy statement . We’ll occasionally send you account related emails.
Already on GitHub?
Sign in
to your account
Uh oh!
There was an error while loading. Please reload this page.
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
436c1b0
Define module constants in PyModuleDef
tiran Nov 14, 2020
d93f2da
Add PyModule_AddTypeFromSpec
tiran Nov 15, 2020
a1c8240
PyModule_AddNew* helpers
tiran Nov 15, 2020
a277939
Rename new API to PyModuleConst_
tiran Nov 16, 2020
22e4f7f
Use Py_mod_constants instead of PyModuleDef.m_constants
tiran Nov 16, 2020
31f8189
Revert "Use Py_mod_constants instead of PyModuleDef.m_constants"
tiran Nov 16, 2020
366e810
Remove PyModuleDef.m_constants again
tiran Nov 16, 2020
bb41d82
bpo-42376: New C-APIs to simplify module attribute declaration
tiran Nov 16, 2020
751c560
ULong, private types, add module to call
tiran Nov 18, 2020
c34a4df
Add CAPI tests
tiran Nov 18, 2020
3e50f44
Typo
tiran Nov 18, 2020
00236fe
Use single base class feature from bpo-42423
tiran Nov 22, 2020
8458120
It's a strong ref
tiran Nov 22, 2020
File filter
Filter by extension
.c
(7)
.dat
(1)
.h
(2)
.py
(1)
.rst
(2)
All 5 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Hide whitespace
Diff view
Hide whitespace
There are no files selected for viewing
Original file line number
Diff line number
Diff line change
Expand Up
@@ -571,6 +571,151 @@ state:
.. versionadded:: 3.9
.. c:function:: PyTypeObject * PyModule_AddNewTypeFromSpec(PyObject *module, PyType_Spec *spec, PyObject *base)
Initialize a new type and add it to *module*.
The function is equivalent to :c:func:`PyType_FromModuleAndSpec` followed
by :c:func:`PyModule_AddType`. *base* must be either ``NULL``, a single
type object, or a tuple of types.
Return ``NULL`` on error; otherwise a ``PyTypeObject *``, which can
be assigned to a module state object.
.. versionadded:: 3.10
.. c:function:: PyObject * PyModule_AddNewException(PyObject *module, const char *name, const char *doc, PyObject *base, PyObject *dict)
Create a new exception and add it to *module*.
The function is equivalent to :c:func:`PyErr_NewExceptionWithDoc` followed
by :c:func:`PyModule_AddObjectRef`. The name of the exception object is
taken from the last component of *name* after dot.
Return ``NULL`` on error; otherwise ``PyObject *``, which can be assigned
to a module state object.
.. versionadded:: 3.10
.. c:function:: int PyModule_AddConstants(PyObject *module, PyModuleConst_Def *def)
Initialize module constants from a PyModuleConst_Def array. The function
provides a convenient way to declare module-level constants.
Return ``-1`` on error, ``0`` on success.
Example::
static PyObject*
Comment thread
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more .
nitpick: static PyObject * in here.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
example_call(PyObject *module)
{
return PyBytes_FromString("23");
}
#define EXAMPLE_INT 23
#define EXAMPLE_STRING "world"
static PyModuleConst_Def example_constants[] = {
PyModuleConst_None("none_value"),
PyModuleConst_Long("integer", 42),
PyModuleConst_ULong("unsigned", 42UL),
PyModuleConst_Bool("false_value", 0),
PyModuleConst_Bool("true_value", 1),
#ifdef Py_MATH_PI
PyModuleConst_Double("pi", Py_MATH_PI),
#endif
PyModuleConst_String("somestring", "Hello"),
PyModuleConst_Call("call", example_call),
PyModuleConst_LongMacro(EXAMPLE_INT),
PyModuleConst_StringMacro(EXAMPLE_STRING),
{NULL},
}
static int
example_init_constants(PyObject *module)
{
return PyModule_AddConstants(module, example_constants);
}
static PyModuleDef_Slot example_slots[] = {
{Py_mod_exec, example_init_constants},
{0, NULL}
};
.. c:type:: PyModuleConst_Def
The values for *type* and the definition of the *value* union are
internal implementation details. Use any of the ``PyModuleConst_`` macros
to define entries. The array must be terminated by an entry with name
set to ``NULL``.
.. c:member:: const char *name
Attribute name.
.. c:member:: int type
Attribute type.
.. c:member:: void *value
Value of the module constant definition, whose meaning depends on
*type*.
Comment thread
Comment on lines
+643
to
+659
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more .
The values for *type * and the definition of the *value * union are
internal implementation details. Use any of the ``PyModuleConst_ `` macros
to define entries. The array must be terminated by an entry with name
set to ``NULL ``.
.. c :member :: const char *name
Attribute name.
.. c :member :: int type
Attribute type.
.. c :member :: void *value
Value of the module constant definition, whose meaning depends on
*type *.
Definition of a module constant.
The members of this struct are internal implementation details.
To define entries, use only the ``PyModuleConst_ `` macros below,
and ``{NULL} `` to mark the end of the array.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
.. versionadded:: 3.10
.. c:macro:: PyModuleConst_None(name)
Add an entry for None.
.. versionadded:: 3.10
.. c:macro:: PyModuleConst_Long(name, value)
Add an entry for an integer constant.
.. versionadded:: 3.10
.. c:macro:: PyModuleConst_ULong(name, value)
Add an entry for an unsigned integer constant.
.. versionadded:: 3.10
.. c:macro:: PyModuleConst_Bool(name, value)
Add an entry for a bool constant. ``0`` is false, ``1`` is true.
.. versionadded:: 3.10
.. c:macro:: PyModuleConst_Double(name, value)
Add an entry for a float constant.
.. versionadded:: 3.10
.. c:macro:: PyModuleConst_String(name, value)
Add an entry for a string constant.
.. versionadded:: 3.10
.. c:macro:: PyModuleConst_Call(name, func)
Add an entry for a constant as returned by callback with signature
``PyObject* (*func)(PyObject *module)``.
.. versionadded:: 3.10
.. c:macro:: PyModuleConst_LongMacro(macro)
Add an entry for an int constant. The name and the value are taken from
*macro*.
.. versionadded:: 3.10
.. c:macro:: PyModuleConst_StringMacro(macro)
Add an entry for a string constant. The name and the value are taken from
*macro*.
.. versionadded:: 3.10
Module lookup
^^^^^^^^^^^^^
Expand Down
Original file line number
Diff line number
Diff line change
Expand Up
@@ -1325,6 +1325,10 @@ PyMethod_New:PyObject*:class:0:
PyMethod_Self:PyObject*::0:
PyMethod_Self:PyObject*:im:0:
PyModule_AddConstants:int:::
PyModule_AddConstants:PyObject*:module:0:
PyModule_AddConstants:PyModuleConst_Def*:def::
PyModule_AddFunctions:int:::
PyModule_AddFunctions:PyObject*:module:0:
PyModule_AddFunctions:PyMethodDef*:functions::
Expand All
@@ -1343,6 +1347,18 @@ PyModule_AddObject:PyObject*:module:0:
PyModule_AddObject:const char*:name::
PyModule_AddObject:PyObject*:value:+1:
PyModule_AddNewException:PyObject*::+1:
PyModule_AddNewException:PyObject*:module:0:
PyModule_AddNewException:const char*:name::
PyModule_AddNewException:const char*:doc::
PyModule_AddNewException:PyObject*:base:0:
PyModule_AddNewException:PyObject*:dict:0:
PyModule_AddNewTypeFromSpec:PyObject*::+1:
PyModule_AddNewTypeFromSpec:PyObject*:module:0:
PyModule_AddNewTypeFromSpec:PyType_spec*:spec::
PyModule_AddNewTypeFromSpec:PyObject*:base:0:
PyModule_AddStringConstant:int:::
PyModule_AddStringConstant:PyObject*:module:0:
PyModule_AddStringConstant:const char*:name::
Expand Down
Original file line number
Diff line number
Diff line change
Expand Up
@@ -154,6 +154,15 @@ PyAPI_FUNC(int) PyModule_AddType(PyObject *module, PyTypeObject *type);
#define PyModule_AddIntMacro(m, c) PyModule_AddIntConstant(m, #c, c)
#define PyModule_AddStringMacro(m, c) PyModule_AddStringConstant(m, #c, c)
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03100000
/* New in 3.9 */
PyAPI_FUNC(PyTypeObject *) PyModule_AddNewTypeFromSpec(
PyObject *module, PyType_Spec *spec, PyObject *base);
PyAPI_FUNC(PyObject *) PyModule_AddNewException(
PyObject *module, const char *name, const char *doc,
PyObject *base, PyObject *dict);
Comment thread
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more .
Could you start by adding these two functions in a separated PR, to make this PR shorter?
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more .
I don't really think that's necessary; the PR is not that big.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
#endif
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03050000
/* New in 3.5 */
PyAPI_FUNC(int) PyModule_SetDocString(PyObject *, const char *);
Expand Down
Original file line number
Diff line number
Diff line change
Expand Up
@@ -72,6 +72,53 @@ typedef struct PyModuleDef_Slot{
#endif /* New in 3.5 */
struct PyModuleConst_Def;
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03100000
/* New in 3.10 */
enum _PyModuleConst_type {
_PyModuleConst_none_type = 1,
_PyModuleConst_long_type = 2,
_PyModuleConst_ulong_type = 3,
_PyModuleConst_bool_type = 4,
_PyModuleConst_double_type = 5,
_PyModuleConst_string_type = 6,
_PyModuleConst_call_type = 7,
};
typedef struct PyModuleConst_Def {
const char *name;
enum _PyModuleConst_type type;
union {
const char *m_str;
long m_long;
unsigned long m_ulong;
double m_double;
PyObject* (*m_call)(PyObject *module);
Comment thread
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more .
I'm not sure how to make this member future-proof in term of stable ABI.
We should try to put a max_align_t inside, but this type requires C11. GCC defines it with:
typedef struct {
long long __max_align_ll __attribute__((__aligned__(__alignof__(long long))));
long double __max_align_ld __attribute__((__aligned__(__alignof__(long double))));
/* _Float128 is defined as a basic type, so max_align_t must be
sufficiently aligned for it. This code must work in C++, so we
use __float128 here; that is only available on some
architectures, but only on i386 is extra alignment needed for
__float128. */
#ifdef __i386__
__float128 __max_align_f128 __attribute__((__aligned__(__alignof(__float128))));
#endif
} max_align_t;
Maybe we can at least put long long and long double:
// Unused members added to make PyModuleConst_Def large enough
// to get a stable ABI and support future additions.
long long m_long_long;
long double m_long_double;
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more .
This is the only issue I found here :)
The stable ABI doesn't have a good story around evolving structs, and I think we should design a general mechanism for that rather than try to future-proof individual structs.
To move this PR forward, could we exclude PyModule_AddConstants & co. from the limited API for the time being?
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
} value;
} PyModuleConst_Def;
PyAPI_FUNC(int) PyModule_AddConstants(PyObject *, PyModuleConst_Def *);
#define PyModuleConst_None(name) \
{(name), _PyModuleConst_none_type, {.m_long=0}}
#define PyModuleConst_Long(name, value) \
{(name), _PyModuleConst_long_type, {.m_long=(value)}}
#define PyModuleConst_ULong(name, value) \
{(name), _PyModuleConst_ulong_type, {.m_ulong=(value)}}
#define PyModuleConst_Bool(name, value) \
{(name), _PyModuleConst_bool_type, {.m_long=(value)}}
#define PyModuleConst_Double(name, value) \
{(name), _PyModuleConst_double_type, {.m_double=(value)}}
#define PyModuleConst_String(name, value) \
{(name), _PyModuleConst_string_type, {.m_str=(value)}}
#define PyModuleConst_Call(name, value) \
{(name), _PyModuleConst_call_type, {.m_call=(value)}}
#define PyModuleConst_LongMacro(m) PyModuleConst_Long(#m, m)
#define PyModuleConst_StringMacro(m) PyModuleConst_String(#m, m)
#endif /* New in 3.10 */
typedef struct PyModuleDef{
PyModuleDef_Base m_base;
const char* m_name;
Expand Down
Original file line number
Diff line number
Diff line change
Expand Up
@@ -971,5 +971,19 @@ def test_state_access(self):
increment_count(1, 2, 3)
class Test_PyModuleConst_Def(unittest.TestCase):
def test_constants(self):
self.assertIs(_testcapi.const_none, None)
self.assertEqual(_testcapi.const_int, 42)
self.assertEqual(_testcapi.const_uint, _testcapi.ULONG_MAX)
self.assertIs(_testcapi.const_true, True)
self.assertIs(_testcapi.const_false, False)
self.assertEqual(_testcapi.const_almost_tau, 6.2831)
self.assertEqual(_testcapi.const_str, "Hello")
self.assertEqual(_testcapi.const_call, b"23")
self.assertEqual(_testcapi.CONST_INT, 7)
self.assertEqual(_testcapi.CONST_STRING, "world")
if __name__ == "__main__":
unittest.main()
Original file line number
Diff line number
Diff line change
@@ -0,0 +1,3 @@
Add new functions :c:func:`PyModule_AddConstants`,
:c:func:`PyModule_AddNewTypeFromSpec`, :c:func:`PyModule_AddNewException` to
simplify the declaration of attribute in modules.
Original file line number
Diff line number
Diff line change
Expand Up
@@ -2025,14 +2025,9 @@ hashlib_init_evptype(PyObject *module)
{
_hashlibstate *state = get_hashlib_state(module);
state->EVPtype = (PyTypeObject *)PyType_FromSpec(&EVPtype_spec);
if (state->EVPtype == NULL) {
return -1;
}
if (PyModule_AddType(module, state->EVPtype) < 0) {
return -1;
}
return 0;
state->EVPtype = PyModule_AddNewTypeFromSpec(
module, &EVPtype_spec, NULL);
return state->EVPtype == NULL ? -1 : 0;
}
static int
Expand All
@@ -2044,33 +2039,21 @@ hashlib_init_evpxoftype(PyObject *module)
if (state->EVPtype == NULL) {
return -1;
}
state->EVPXOFtype = (PyTypeObject *)PyType_FromSpecWithBases(
&EVPXOFtype_spec, (PyObject *)state->EVPtype
);
if (state->EVPXOFtype == NULL) {
return -1;
}
if (PyModule_AddType(module, state->EVPXOFtype) < 0) {
return -1;
}
state->EVPXOFtype = PyModule_AddNewTypeFromSpec(
module, &EVPXOFtype_spec, (PyObject *)state->EVPtype);
return state->EVPXOFtype == NULL ? -1 : 0;
#endif
return 0;
}
static int
hashlib_init_hmactype(PyObject *module)
{
_hashlibstate *state = get_hashlib_state(module);
_hashlibstate *state = get_hashlib_state(module);
state->HMACtype = (PyTypeObject *)PyType_FromSpec(&HMACtype_spec);
if (state->HMACtype == NULL) {
return -1;
}
if (PyModule_AddType(module, state->HMACtype) < 0) {
return -1;
}
return 0;
state->HMACtype = PyModule_AddNewTypeFromSpec(
module, &HMACtype_spec, NULL);
return state->HMACtype == NULL ? -1 : 0;
}
static int
Expand Down
Original file line number
Diff line number
Diff line change
Expand Up
@@ -5459,39 +5459,30 @@ static PyMethodDef PySSL_methods[] = {
static int
sslmodule_init_types(PyObject *module)
{
PySSLContext_Type = (PyTypeObject *)PyType_FromModuleAndSpec (
PySSLContext_Type = PyModule_AddNewTypeFromSpec (
module, &PySSLContext_spec, NULL
);
if (PySSLContext_Type == NULL)
return -1;
PySSLSocket_Type = (PyTypeObject *)PyType_FromModuleAndSpec (
PySSLSocket_Type = PyModule_AddNewTypeFromSpec (
module, &PySSLSocket_spec, NULL
);
if (PySSLSocket_Type == NULL)
return -1;
PySSLMemoryBIO_Type = (PyTypeObject *)PyType_FromModuleAndSpec (
PySSLMemoryBIO_Type = PyModule_AddNewTypeFromSpec (
module, &PySSLMemoryBIO_spec, NULL
);
if (PySSLMemoryBIO_Type == NULL)
return -1;
PySSLSession_Type = (PyTypeObject *)PyType_FromModuleAndSpec (
PySSLSession_Type = PyModule_AddNewTypeFromSpec (
module, &PySSLSession_spec, NULL
);
if (PySSLSession_Type == NULL)
return -1;
if (PyModule_AddType(module, PySSLContext_Type))
return -1;
if (PyModule_AddType(module, PySSLSocket_Type))
return -1;
if (PyModule_AddType(module, PySSLMemoryBIO_Type))
return -1;
if (PyModule_AddType(module, PySSLSession_Type))
return -1;
return 0;
}
Expand Down
Loading
Oops, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.