Skip to content

Add c_time_t to ctypes #92869

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

Closed
thp opened this issue May 17, 2022 · 0 comments
Closed

Add c_time_t to ctypes #92869

thp opened this issue May 17, 2022 · 0 comments
Labels
3.12 only security fixes topic-ctypes type-feature A feature request or enhancement

Comments

@thp
Copy link
Contributor

thp commented May 17, 2022

Feature or enhancement

In order to make it possible to interoperate with C structs that contain time_t, add a new type c_time_t to the ctypes module that represents a C time_t.

Pitch

Imagine having a struct in some C library we want to bind using ctypes:

struct Something {
    int foo;
    time_t bar;
    const char *baz;
};

This is quite cumbersome to do right now, as the size of time_t depends on lots of factors:

time_t is special due to the year 2038 problem. Historically on UNIX, time_t was 32-bit. For 64-bit platforms, time_t is usually 64-bit, avoiding the problem. In 2020, some changes have landed in Linux 5.6 to enable 64-bit time_t support in 32-bit Linux. glibc also has some documentation on 64-bit time_t. According to PC/pyconfig.h in CPython, "MS VS2005 changes time_t to a 64-bit type on all platforms".

Also, Lib/test/test_time.py says (in test_insane_timestamps()): "It's possible that some platform maps time_t to double" (I don't know which platform has this, but it might make sense to also take care of that).

Based on my research, something like this (likely wrong, incomplete or inaccurate - definitely untested) needs to be done if one wants to have a ctypes time_t type right now:

import platform
import ctypes

if platform.system() == 'Windows':
    # Assume MSVC(?) - what about mingw/clang?
    time_t = ctypes.c_int64
elif ctypes.sizeof(ctypes.c_void_p) == ctypes.sizeof(ctypes.c_int64):
    # 64-bit platform of any kind - assume 64-bit time_t(?)
    time_t = ctypes.c_int64
else:
    # assume some kind of 32-bit platform(?)
    time_t = ctypes.c_int32

# ... use time_t here ...

Adding a new c_time_t type in ctypes that is the same as one of (depending on how the host system defines it): c_int32, c_uint32, c_int64, c_uint64 would fix this issues, and allow defining the above struct simply as:

class Something(ctypes.Structure):
    _fields_ = [
        ('foo', ctypes.c_int),
        ('bar', ctypes.c_time_t),
        ('baz', ctypes.c_char_p),
    ]

The CPython codebase already defines SIZEOF_TIME_T

grep'ing for the word time_t in the current CPython Git head:

% grep '\<time_t\>' **/*.py
Lib/lib2to3/tests/data/infinite_recursion.py:time_t = __darwin_time_t
Lib/lib2to3/tests/data/infinite_recursion.py:    ('check_time', time_t),
Lib/lib2to3/tests/data/infinite_recursion.py:    ('tv_sec', time_t),
Lib/lib2to3/tests/data/infinite_recursion.py:           'OSUnknownByteOrder', 'BN_MONT_CTX', 'ASN1_NULL', 'time_t',
Lib/test/datetimetester.py:        # It's possible that some platform maps time_t to double,
Lib/test/datetimetester.py:                    # converting a Python int to C time_t can raise a
Lib/test/datetimetester.py:                    # converting a Python int to C time_t can raise a
Lib/test/datetimetester.py:        # It's possible that some platform maps time_t to double,
Lib/test/datetimetester.py:        # It's possible that some platform maps time_t to double,
Lib/test/datetimetester.py:                    # System support for times around the end of 32-bit time_t
Lib/test/test_os.py:            # or utime(time_t)
Lib/test/test_time.py:        # It's possible that some platform maps time_t to double,
Lib/test/test_time.py:        for time_t in (-1, 2**30, 2**33, 2**60):
Lib/test/test_time.py:                time.localtime(time_t)
Lib/test/test_time.py:                self.skipTest("need 64-bit time_t")
Lib/test/test_time.py:                invalid_time_t = time_t
Lib/test/test_time.py:            self.skipTest("unable to find an invalid time_t value")
Lib/test/test_time.py:    # time_t is a 32-bit or 64-bit signed integer

It seems like some parts of this check for OverflowError with time.localtime() to distinguish between 64-bit and 32-bit time_t.

If nothing else, exposing SIZEOF_TIME_T (seems like it's either 4 or 8 bytes for most (all?) platforms CPython supports) might help a lot in defining proper structs containing time_t for interoperability.

Also the Y2038 Wikipedia article describes some platforms that deal with time_t specially:

  • Starting with NetBSD version 6.0 (released in October 2012), the NetBSD operating system uses a 64-bit time_t for both 32-bit and 64-bit architectures.
  • OpenBSD since version 5.5, released in May 2014, also uses a 64-bit time_t for both 32-bit and 64-bit architectures.
  • Linux originally used a 64-bit time_t for 64-bit architectures only; the pure 32-bit ABI was not changed due to backward compatibility. Starting with version 5.6, 64-bit time_t is supported on 32-bit architectures, too. This was done primarily for the sake of embedded Linux systems.
  • FreeBSD uses 64-bit time_t for all 32-bit and 64-bit architectures except 32-bit i386, which uses signed 32-bit time_t instead.
  • The x32 ABI for Linux (which defines an environment for programs with 32-bit addresses but running the processor in 64-bit mode) uses a 64-bit time_t.

So supporting time_t properly seems to be a bit more involved than "just" checking the pointer size, especially for the BSDs.

Previous discussion

This came up while trying to create ctypes-based bindings for libgpod (which defines structs with time_t) here: gpodder/gpodder#1289

@thp thp added the type-feature A feature request or enhancement label May 17, 2022
gpshead pushed a commit that referenced this issue Jul 3, 2022
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]
@gpshead gpshead added the 3.12 only security fixes label Jul 3, 2022
@gpshead gpshead closed this as completed Jul 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.12 only security fixes topic-ctypes type-feature A feature request or enhancement
Projects
None yet
Development

No branches or pull requests

3 participants