Skip to content

Commit b296c74

Browse files
authored
pythongh-92869: ctypes: Add c_time_t (python#92870)
Adds `ctypes.c_time_t` to represent the C `time_t` type accurately as its size varies. Primarily-authored-by: Adam Turner <[email protected]> Co-authored-by: Gregory P. Smith <[email protected]> [Google]
1 parent 39c29f7 commit b296c74

File tree

5 files changed

+44
-7
lines changed

5 files changed

+44
-7
lines changed

Doc/library/ctypes.rst

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -148,15 +148,14 @@ Calling functions
148148
^^^^^^^^^^^^^^^^^
149149

150150
You can call these functions like any other Python callable. This example uses
151-
the ``time()`` function, which returns system time in seconds since the Unix
152-
epoch, and the ``GetModuleHandleA()`` function, which returns a win32 module
153-
handle.
151+
the ``rand()`` function, which takes no arguments and returns a pseudo-random integer::
154152

155-
This example calls both functions with a ``NULL`` pointer (``None`` should be used
156-
as the ``NULL`` pointer)::
153+
>>> print(libc.rand()) # doctest: +SKIP
154+
1804289383
155+
156+
On Windows, you can call the ``GetModuleHandleA()`` function, which returns a win32 module
157+
handle (passing ``None`` as single argument to call it with a ``NULL`` pointer)::
157158

158-
>>> print(libc.time(None)) # doctest: +SKIP
159-
1150640792
160159
>>> print(hex(windll.kernel32.GetModuleHandleA(None))) # doctest: +WINDOWS
161160
0x1d000000
162161
>>>
@@ -247,6 +246,8 @@ Fundamental data types
247246
| :class:`c_ssize_t` | :c:type:`ssize_t` or | int |
248247
| | :c:type:`Py_ssize_t` | |
249248
+----------------------+------------------------------------------+----------------------------+
249+
| :class:`c_time_t` | :c:type:`time_t` | int |
250+
+----------------------+------------------------------------------+----------------------------+
250251
| :class:`c_float` | :c:type:`float` | float |
251252
+----------------------+------------------------------------------+----------------------------+
252253
| :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
447448
return types can be specified by setting the :attr:`restype` attribute of the
448449
function object.
449450

451+
The C prototype of ``time()`` is ``time_t time(time_t *)``. Because ``time_t``
452+
might be of a different type than the default return type ``int``, you should
453+
specify the ``restype``::
454+
455+
>>> libc.time.restype = c_time_t
456+
457+
The argument types can be specified using ``argtypes``::
458+
459+
>>> libc.time.argtypes = (POINTER(c_time_t),)
460+
461+
To call the function with a ``NULL`` pointer as first argument, use ``None``::
462+
463+
>>> print(libc.time(None)) # doctest: +SKIP
464+
1150640792
465+
450466
Here is a more advanced example, it uses the ``strchr`` function, which expects
451467
a string pointer and a char, and returns a pointer to a string::
452468

@@ -2275,6 +2291,13 @@ These are the fundamental ctypes data types:
22752291
.. versionadded:: 3.2
22762292

22772293

2294+
.. class:: c_time_t
2295+
2296+
Represents the C :c:type:`time_t` datatype.
2297+
2298+
.. versionadded:: 3.12
2299+
2300+
22782301
.. class:: c_ubyte
22792302

22802303
Represents the C :c:type:`unsigned char` datatype, it interprets the value as

Lib/ctypes/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from _ctypes import __version__ as _ctypes_version
1212
from _ctypes import RTLD_LOCAL, RTLD_GLOBAL
1313
from _ctypes import ArgumentError
14+
from _ctypes import SIZEOF_TIME_T
1415

1516
from struct import calcsize as _calcsize
1617

@@ -563,4 +564,11 @@ def DllCanUnloadNow():
563564
elif sizeof(kind) == 8: c_uint64 = kind
564565
del(kind)
565566

567+
if SIZEOF_TIME_T == 8:
568+
c_time_t = c_int64
569+
elif SIZEOF_TIME_T == 4:
570+
c_time_t = c_int32
571+
else:
572+
raise SystemError(f"Unexpected sizeof(time_t): {SIZEOF_TIME_T=}")
573+
566574
_reset_cache()

Lib/test/test_ctypes/test_sizes.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ def test_size_t(self):
2828
def test_ssize_t(self):
2929
self.assertEqual(sizeof(c_void_p), sizeof(c_ssize_t))
3030

31+
def test_time_t(self):
32+
self.assertEqual(sizeof(c_time_t), SIZEOF_TIME_T)
33+
3134

3235
if __name__ == "__main__":
3336
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Added :class:`~ctypes.c_time_t` to :mod:`ctypes`, which has the same size as
2+
the :c:type:`time_t` type in C.

Modules/_ctypes/_ctypes.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5784,6 +5784,7 @@ _ctypes_add_objects(PyObject *mod)
57845784
MOD_ADD("RTLD_GLOBAL", PyLong_FromLong(RTLD_GLOBAL));
57855785
MOD_ADD("CTYPES_MAX_ARGCOUNT", PyLong_FromLong(CTYPES_MAX_ARGCOUNT));
57865786
MOD_ADD("ArgumentError", Py_NewRef(PyExc_ArgError));
5787+
MOD_ADD("SIZEOF_TIME_T", PyLong_FromSsize_t(SIZEOF_TIME_T));
57875788
return 0;
57885789
#undef MOD_ADD
57895790
}

0 commit comments

Comments
 (0)