GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

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.

Sign up for GitHub

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

Closed
tiran wants to merge 13 commits into python:main from tiran:module_constants

Uh oh!

There was an error while loading. Please reload this page.

Closed

gh-86542: New C-APIs to simplify module attribute declaration #23286

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

Clear filters
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
Diff view

There are no files selected for viewing

145 changes: 145 additions & 0 deletions 145 Doc/c-api/module.rst
Show comments
View file
Open in desktop
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

@shihai1991 shihai1991 Nov 26, 2020

Copy link
Copy Markdown
Member

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.

All reactions
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

@encukou encukou Nov 16, 2021

Copy link
Copy Markdown
Member

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.

Suggested change
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.

All reactions

.. 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
16 changes: 16 additions & 0 deletions 16 Doc/data/refcounts.dat
Show comments
View file
Open in desktop
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
9 changes: 9 additions & 0 deletions 9 Include/modsupport.h
Show comments
View file
Open in desktop
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

@vstinner vstinner Nov 23, 2020

Copy link
Copy Markdown
Member

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.

All reactions

@encukou encukou Nov 24, 2020

Copy link
Copy Markdown
Member

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.

All reactions
#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
47 changes: 47 additions & 0 deletions 47 Include/moduleobject.h
Show comments
View file
Open in desktop
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

@vstinner vstinner Nov 23, 2020

Copy link
Copy Markdown
Member

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.

All reactions

@encukou encukou Nov 24, 2020

Copy link
Copy Markdown
Member

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.

All reactions
} 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
14 changes: 14 additions & 0 deletions 14 Lib/test/test_capi.py
Show comments
View file
Open in desktop
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()
3 changes: 3 additions & 0 deletions 3 Misc/NEWS.d/next/C API/2020-11-16-20-29-59.bpo-42376.vO9Tvu.rst
Show comments
View file
Open in desktop
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.
37 changes: 10 additions & 27 deletions 37 Modules/_hashopenssl.c
Show comments
View file
Open in desktop
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
17 changes: 4 additions & 13 deletions 17 Modules/_ssl.c
Show comments
View file
Open in desktop
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.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.