gh-92869: ctypes: Add c_time_t (#92870)

Adds `ctypes.c_time_t` to represent the C `time_t` type accurately as its size varies.

Primarily-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
Co-authored-by: Gregory P. Smith <greg@krypto.org> [Google]
This commit is contained in:
Thomas Perl 2022-07-03 20:58:02 +02:00 committed by GitHub
parent 39c29f753e
commit b296c7442b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 44 additions and 7 deletions

View file

@ -148,15 +148,14 @@ Calling functions
^^^^^^^^^^^^^^^^^
You can call these functions like any other Python callable. This example uses
the ``time()`` function, which returns system time in seconds since the Unix
epoch, and the ``GetModuleHandleA()`` function, which returns a win32 module
handle.
the ``rand()`` function, which takes no arguments and returns a pseudo-random integer::
This example calls both functions with a ``NULL`` pointer (``None`` should be used
as the ``NULL`` pointer)::
>>> print(libc.rand()) # doctest: +SKIP
1804289383
On Windows, you can call the ``GetModuleHandleA()`` function, which returns a win32 module
handle (passing ``None`` as single argument to call it with a ``NULL`` pointer)::
>>> print(libc.time(None)) # doctest: +SKIP
1150640792
>>> print(hex(windll.kernel32.GetModuleHandleA(None))) # doctest: +WINDOWS
0x1d000000
>>>
@ -247,6 +246,8 @@ Fundamental data types
| :class:`c_ssize_t` | :c:type:`ssize_t` or | int |
| | :c:type:`Py_ssize_t` | |
+----------------------+------------------------------------------+----------------------------+
| :class:`c_time_t` | :c:type:`time_t` | int |
+----------------------+------------------------------------------+----------------------------+
| :class:`c_float` | :c:type:`float` | float |
+----------------------+------------------------------------------+----------------------------+
| :class:`c_double` | :c:type:`double` | float |
@ -447,6 +448,21 @@ By default functions are assumed to return the C :c:type:`int` type. Other
return types can be specified by setting the :attr:`restype` attribute of the
function object.
The C prototype of ``time()`` is ``time_t time(time_t *)``. Because ``time_t``
might be of a different type than the default return type ``int``, you should
specify the ``restype``::
>>> libc.time.restype = c_time_t
The argument types can be specified using ``argtypes``::
>>> libc.time.argtypes = (POINTER(c_time_t),)
To call the function with a ``NULL`` pointer as first argument, use ``None``::
>>> print(libc.time(None)) # doctest: +SKIP
1150640792
Here is a more advanced example, it uses the ``strchr`` function, which expects
a string pointer and a char, and returns a pointer to a string::
@ -2275,6 +2291,13 @@ These are the fundamental ctypes data types:
.. versionadded:: 3.2
.. class:: c_time_t
Represents the C :c:type:`time_t` datatype.
.. versionadded:: 3.12
.. class:: c_ubyte
Represents the C :c:type:`unsigned char` datatype, it interprets the value as

View file

@ -11,6 +11,7 @@
from _ctypes import __version__ as _ctypes_version
from _ctypes import RTLD_LOCAL, RTLD_GLOBAL
from _ctypes import ArgumentError
from _ctypes import SIZEOF_TIME_T
from struct import calcsize as _calcsize
@ -563,4 +564,11 @@ def DllCanUnloadNow():
elif sizeof(kind) == 8: c_uint64 = kind
del(kind)
if SIZEOF_TIME_T == 8:
c_time_t = c_int64
elif SIZEOF_TIME_T == 4:
c_time_t = c_int32
else:
raise SystemError(f"Unexpected sizeof(time_t): {SIZEOF_TIME_T=}")
_reset_cache()

View file

@ -28,6 +28,9 @@ def test_size_t(self):
def test_ssize_t(self):
self.assertEqual(sizeof(c_void_p), sizeof(c_ssize_t))
def test_time_t(self):
self.assertEqual(sizeof(c_time_t), SIZEOF_TIME_T)
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,2 @@
Added :class:`~ctypes.c_time_t` to :mod:`ctypes`, which has the same size as
the :c:type:`time_t` type in C.

View file

@ -5784,6 +5784,7 @@ _ctypes_add_objects(PyObject *mod)
MOD_ADD("RTLD_GLOBAL", PyLong_FromLong(RTLD_GLOBAL));
MOD_ADD("CTYPES_MAX_ARGCOUNT", PyLong_FromLong(CTYPES_MAX_ARGCOUNT));
MOD_ADD("ArgumentError", Py_NewRef(PyExc_ArgError));
MOD_ADD("SIZEOF_TIME_T", PyLong_FromSsize_t(SIZEOF_TIME_T));
return 0;
#undef MOD_ADD
}