Skip to content

gh-92869: ctypes: Add c_time_t #92870

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

Merged
merged 15 commits into from
Jul 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 30 additions & 7 deletions Doc/library/ctypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
>>>
Expand Down Expand Up @@ -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 |
Expand Down Expand Up @@ -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::

Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions Lib/ctypes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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()
3 changes: 3 additions & 0 deletions Lib/test/test_ctypes/test_sizes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Original file line number Diff line number Diff line change
@@ -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.
1 change: 1 addition & 0 deletions Modules/_ctypes/_ctypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down