]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-92869: ctypes: Add c_time_t (#92870)
authorThomas Perl <m@thp.io>
Sun, 3 Jul 2022 18:58:02 +0000 (20:58 +0200)
committerGitHub <noreply@github.com>
Sun, 3 Jul 2022 18:58:02 +0000 (11:58 -0700)
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]
Doc/library/ctypes.rst
Lib/ctypes/__init__.py
Lib/test/test_ctypes/test_sizes.py
Misc/NEWS.d/next/Library/2022-05-17-06-27-39.gh-issue-92869.t8oBkw.rst [new file with mode: 0644]
Modules/_ctypes/_ctypes.c

index 52950b551b7560a9565835d7b5780843127eb371..475737144d40c63669c57b9b673b5c280e4eeb62 100644 (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
index 26135ad96296acc6aeda25d8b686f780e0b9e2c4..b94b337a3157b7c5527d139955f0be0fd643949b 100644 (file)
@@ -11,6 +11,7 @@ from _ctypes import CFuncPtr as _CFuncPtr
 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 @@ for kind in [c_ushort, c_uint, c_ulong, c_ulonglong]:
     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()
index 4ceacbc29008027374aee2f2c9077ad8c63b2500..bf8d6ea35aa39946b6985cb16f22c8d66ef4aa20 100644 (file)
@@ -28,6 +28,9 @@ class SizesTestCase(unittest.TestCase):
     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()
diff --git a/Misc/NEWS.d/next/Library/2022-05-17-06-27-39.gh-issue-92869.t8oBkw.rst b/Misc/NEWS.d/next/Library/2022-05-17-06-27-39.gh-issue-92869.t8oBkw.rst
new file mode 100644 (file)
index 0000000..7787f34
--- /dev/null
@@ -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.
index 2c629d76beb3bd61b6a3c472d2dff53c89e0e458..a3c7c8c471e584f499a51803f07fe53cf1cd48dc 100644 (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
 }