Skip to content

Commit 3222ea0

Browse files
authored
gh-83714: Set os.statx() members to None if missing from stx_mask (#140216)
1 parent 237dca5 commit 3222ea0

File tree

2 files changed

+173
-68
lines changed

2 files changed

+173
-68
lines changed

Doc/library/os.rst

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3451,7 +3451,10 @@ features:
34513451

34523452
.. attribute:: stx_mnt_id
34533453

3454-
Mount ID.
3454+
Mount identifier.
3455+
3456+
Equal to ``None`` if :data:`STATX_MNT_ID` is missing from
3457+
:attr:`~os.statx_result.stx_mask`.
34553458

34563459
.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
34573460
userspace API headers >= 5.8.
@@ -3460,19 +3463,28 @@ features:
34603463

34613464
Direct I/O memory buffer alignment requirement.
34623465

3466+
Equal to ``None`` if :data:`STATX_DIOALIGN` is missing from
3467+
:attr:`~os.statx_result.stx_mask`.
3468+
34633469
.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
34643470
userspace API headers >= 6.1.
34653471

34663472
.. attribute:: stx_dio_offset_align
34673473

34683474
Direct I/O file offset alignment requirement.
34693475

3476+
Equal to ``None`` if :data:`STATX_DIOALIGN` is missing from
3477+
:attr:`~os.statx_result.stx_mask`.
3478+
34703479
.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
34713480
userspace API headers >= 6.1.
34723481

34733482
.. attribute:: stx_subvol
34743483

3475-
Subvolume ID.
3484+
Subvolume identifier.
3485+
3486+
Equal to ``None`` if :data:`STATX_SUBVOL` is missing from
3487+
:attr:`~os.statx_result.stx_mask`.
34763488

34773489
.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
34783490
userspace API headers >= 6.10.
@@ -3481,34 +3493,49 @@ features:
34813493

34823494
Minimum size for direct I/O with torn-write protection.
34833495

3496+
Equal to ``None`` if :data:`STATX_WRITE_ATOMIC` is missing from
3497+
:attr:`~os.statx_result.stx_mask`.
3498+
34843499
.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
34853500
userspace API headers >= 6.11.
34863501

34873502
.. attribute:: stx_atomic_write_unit_max
34883503

34893504
Maximum size for direct I/O with torn-write protection.
34903505

3506+
Equal to ``None`` if :data:`STATX_WRITE_ATOMIC` is missing from
3507+
:attr:`~os.statx_result.stx_mask`.
3508+
34913509
.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
34923510
userspace API headers >= 6.11.
34933511

34943512
.. attribute:: stx_atomic_write_unit_max_opt
34953513

34963514
Maximum optimized size for direct I/O with torn-write protection.
34973515

3516+
Equal to ``None`` if :data:`STATX_WRITE_ATOMIC` is missing from
3517+
:attr:`~os.statx_result.stx_mask`.
3518+
34983519
.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
34993520
userspace API headers >= 6.16.
35003521

35013522
.. attribute:: stx_atomic_write_segments_max
35023523

35033524
Maximum iovecs for direct I/O with torn-write protection.
35043525

3526+
Equal to ``None`` if :data:`STATX_WRITE_ATOMIC` is missing from
3527+
:attr:`~os.statx_result.stx_mask`.
3528+
35053529
.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
35063530
userspace API headers >= 6.11.
35073531

35083532
.. attribute:: stx_dio_read_offset_align
35093533

35103534
Direct I/O file offset alignment requirement for reads.
35113535

3536+
Equal to ``None`` if :data:`STATX_DIO_READ_ALIGN` is missing from
3537+
:attr:`~os.statx_result.stx_mask`.
3538+
35123539
.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
35133540
userspace API headers >= 6.14.
35143541

Modules/posixmodule.c

Lines changed: 144 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -3319,6 +3319,8 @@ typedef struct {
33193319
struct statx stx;
33203320
} Py_statx_result;
33213321

3322+
#define Py_statx_result_CAST(op) _Py_CAST(Py_statx_result*, (op))
3323+
33223324
#define M(attr, type, offset, doc) \
33233325
{attr, type, offset, Py_READONLY, PyDoc_STR(doc)}
33243326
#define MM(attr, type, member, doc) \
@@ -3330,85 +3332,166 @@ static PyMemberDef pystatx_result_members[] = {
33303332
MM(stx_mask, Py_T_UINT, mask, "member validity mask"),
33313333
MM(st_blksize, Py_T_UINT, blksize, "blocksize for filesystem I/O"),
33323334
MM(stx_attributes, Py_T_ULONGLONG, attributes, "Linux inode attribute bits"),
3333-
MM(st_nlink, Py_T_UINT, nlink, "number of hard links"),
3334-
MM(st_uid, Py_T_UINT, uid, "user ID of owner"),
3335-
MM(st_gid, Py_T_UINT, gid, "group ID of owner"),
33363335
MM(st_mode, Py_T_USHORT, mode, "protection bits"),
3337-
MM(st_ino, Py_T_ULONGLONG, ino, "inode"),
3338-
MM(st_size, Py_T_ULONGLONG, size, "total size, in bytes"),
3339-
MM(st_blocks, Py_T_ULONGLONG, blocks, "number of blocks allocated"),
33403336
MM(stx_attributes_mask, Py_T_ULONGLONG, attributes_mask,
33413337
"Mask of supported bits in stx_attributes"),
3342-
MX(st_atime, Py_T_DOUBLE, atime_sec, "time of last access"),
3343-
MX(st_birthtime, Py_T_DOUBLE, btime_sec, "time of creation"),
3344-
MX(st_ctime, Py_T_DOUBLE, ctime_sec, "time of last change"),
3345-
MX(st_mtime, Py_T_DOUBLE, mtime_sec, "time of last modification"),
33463338
MM(stx_rdev_major, Py_T_UINT, rdev_major, "represented device major number"),
33473339
MM(stx_rdev_minor, Py_T_UINT, rdev_minor, "represented device minor number"),
33483340
MX(st_rdev, Py_T_ULONGLONG, rdev, "device type (if inode device)"),
33493341
MM(stx_dev_major, Py_T_UINT, dev_major, "containing device major number"),
33503342
MM(stx_dev_minor, Py_T_UINT, dev_minor, "containing device minor number"),
33513343
MX(st_dev, Py_T_ULONGLONG, dev, "device"),
3352-
#ifdef STATX_MNT_ID
3353-
MM(stx_mnt_id, Py_T_ULONGLONG, mnt_id, "mount ID"),
3354-
#endif
3344+
{NULL},
3345+
};
3346+
3347+
#undef MX
3348+
#undef MM
3349+
#undef M
3350+
3351+
3352+
#define STATX_GET_UINT(ATTR, MEMBER, MASK) \
3353+
static PyObject* \
3354+
pystatx_result_get_##ATTR(PyObject *op, void *Py_UNUSED(context)) \
3355+
{ \
3356+
Py_statx_result *self = Py_statx_result_CAST(op); \
3357+
if (!(self->stx.stx_mask & MASK)) { \
3358+
Py_RETURN_NONE; \
3359+
} \
3360+
unsigned long value = self->stx.MEMBER; \
3361+
return PyLong_FromUnsignedLong(value); \
3362+
}
3363+
3364+
STATX_GET_UINT(st_uid, stx_uid, STATX_UID)
3365+
STATX_GET_UINT(st_gid, stx_gid, STATX_GID)
3366+
STATX_GET_UINT(st_nlink, stx_nlink, STATX_NLINK)
33553367
#ifdef STATX_DIOALIGN
3356-
MM(stx_dio_mem_align, Py_T_UINT, dio_mem_align,
3357-
"direct I/O memory buffer alignment"),
3358-
MM(stx_dio_offset_align, Py_T_UINT, dio_offset_align,
3359-
"direct I/O file offset alignment"),
3368+
STATX_GET_UINT(stx_dio_mem_align, stx_dio_mem_align, STATX_DIOALIGN)
3369+
STATX_GET_UINT(stx_dio_offset_align, stx_dio_offset_align, STATX_DIOALIGN)
33603370
#endif
3361-
#ifdef STATX_SUBVOL
3362-
MM(stx_subvol, Py_T_ULONGLONG, subvol, "subvolume ID"),
3371+
#ifdef STATX_DIO_READ_ALIGN
3372+
STATX_GET_UINT(stx_dio_read_offset_align, stx_dio_read_offset_align,
3373+
STATX_DIO_READ_ALIGN)
33633374
#endif
33643375
#ifdef STATX_WRITE_ATOMIC
3365-
MM(stx_atomic_write_unit_min, Py_T_UINT, atomic_write_unit_min,
3366-
"minimum size for direct I/O with torn-write protection"),
3367-
MM(stx_atomic_write_unit_max, Py_T_UINT, atomic_write_unit_max,
3368-
"maximum size for direct I/O with torn-write protection"),
3369-
MM(stx_atomic_write_segments_max, Py_T_UINT, atomic_write_segments_max,
3370-
"maximum iovecs for direct I/O with torn-write protection"),
3376+
STATX_GET_UINT(stx_atomic_write_unit_min, stx_atomic_write_unit_min,
3377+
STATX_WRITE_ATOMIC)
3378+
STATX_GET_UINT(stx_atomic_write_unit_max, stx_atomic_write_unit_max,
3379+
STATX_WRITE_ATOMIC)
3380+
STATX_GET_UINT(stx_atomic_write_segments_max, stx_atomic_write_segments_max,
3381+
STATX_WRITE_ATOMIC)
33713382
#endif
33723383
#ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT
3373-
MM(stx_atomic_write_unit_max_opt, Py_T_UINT, atomic_write_unit_max_opt,
3374-
"maximum optimized size for direct I/O with torn-write protection"),
3384+
STATX_GET_UINT(stx_atomic_write_unit_max_opt, stx_atomic_write_unit_max_opt,
3385+
STATX_WRITE_ATOMIC)
33753386
#endif
3376-
#ifdef STATX_DIO_READ_ALIGN
3377-
MM(stx_dio_read_offset_align, Py_T_UINT, dio_read_offset_align,
3378-
"direct I/O file offset alignment for reads"),
3387+
3388+
3389+
#define STATX_GET_ULONGLONG(ATTR, MEMBER, MASK) \
3390+
static PyObject* \
3391+
pystatx_result_get_##ATTR(PyObject *op, void *Py_UNUSED(context)) \
3392+
{ \
3393+
Py_statx_result *self = Py_statx_result_CAST(op); \
3394+
if (!(self->stx.stx_mask & MASK)) { \
3395+
Py_RETURN_NONE; \
3396+
} \
3397+
unsigned long long value = self->stx.MEMBER; \
3398+
return PyLong_FromUnsignedLongLong(value); \
3399+
}
3400+
3401+
STATX_GET_ULONGLONG(st_blocks, stx_blocks, STATX_BLOCKS)
3402+
STATX_GET_ULONGLONG(st_ino, stx_ino, STATX_INO)
3403+
STATX_GET_ULONGLONG(st_size, stx_size, STATX_SIZE)
3404+
#ifdef STATX_MNT_ID
3405+
STATX_GET_ULONGLONG(stx_mnt_id, stx_mnt_id, STATX_MNT_ID)
3406+
#endif
3407+
#ifdef STATX_SUBVOL
3408+
STATX_GET_ULONGLONG(stx_subvol, stx_subvol, STATX_SUBVOL)
33793409
#endif
3380-
{NULL},
3381-
};
33823410

3383-
#undef MX
3384-
#undef MM
3385-
#undef M
33863411

3387-
static PyObject *
3388-
pystatx_result_get_nsec(PyObject *op, void *context)
3389-
{
3390-
uint16_t offset = (uintptr_t)context;
3391-
struct statx_timestamp *ts = (void*)op + offset;
3392-
_posixstate *state = PyType_GetModuleState(Py_TYPE(op));
3393-
assert(state != NULL);
3394-
return stat_nanosecond_timestamp(state, ts->tv_sec, ts->tv_nsec);
3395-
}
3412+
#define STATX_GET_DOUBLE(ATTR, MEMBER, MASK) \
3413+
static PyObject* \
3414+
pystatx_result_get_##ATTR(PyObject *op, void *Py_UNUSED(context)) \
3415+
{ \
3416+
Py_statx_result *self = Py_statx_result_CAST(op); \
3417+
if (!(self->stx.stx_mask & MASK)) { \
3418+
Py_RETURN_NONE; \
3419+
} \
3420+
double sec = self->MEMBER; \
3421+
return PyFloat_FromDouble(sec); \
3422+
}
3423+
3424+
STATX_GET_DOUBLE(st_atime, atime_sec, STATX_ATIME)
3425+
STATX_GET_DOUBLE(st_birthtime, btime_sec, STATX_BTIME)
3426+
STATX_GET_DOUBLE(st_ctime, ctime_sec, STATX_CTIME)
3427+
STATX_GET_DOUBLE(st_mtime, mtime_sec, STATX_MTIME)
3428+
3429+
#define STATX_GET_NSEC(ATTR, MEMBER, MASK) \
3430+
static PyObject* \
3431+
pystatx_result_get_##ATTR(PyObject *op, void *context) \
3432+
{ \
3433+
Py_statx_result *self = Py_statx_result_CAST(op); \
3434+
if (!(self->stx.stx_mask & MASK)) { \
3435+
Py_RETURN_NONE; \
3436+
} \
3437+
struct statx_timestamp *ts = &self->stx.MEMBER; \
3438+
_posixstate *state = PyType_GetModuleState(Py_TYPE(op)); \
3439+
assert(state != NULL); \
3440+
return stat_nanosecond_timestamp(state, ts->tv_sec, ts->tv_nsec); \
3441+
}
3442+
3443+
STATX_GET_NSEC(st_atime_ns, stx_atime, STATX_ATIME)
3444+
STATX_GET_NSEC(st_birthtime_ns, stx_btime, STATX_BTIME)
3445+
STATX_GET_NSEC(st_ctime_ns, stx_ctime, STATX_CTIME)
3446+
STATX_GET_NSEC(st_mtime_ns, stx_mtime, STATX_MTIME)
33963447

3397-
/* The low 16 bits of the context pointer are the offset from the start of
3398-
Py_statx_result to the struct statx member. */
3399-
#define GM(attr, type, member, doc) \
3400-
{#attr, pystatx_result_get_##type, NULL, PyDoc_STR(doc), \
3401-
(void *)(offsetof(Py_statx_result, stx.stx_##member))}
3448+
#define G(attr, doc) \
3449+
{#attr, pystatx_result_get_##attr, NULL, PyDoc_STR(doc), NULL}
34023450

34033451
static PyGetSetDef pystatx_result_getset[] = {
3404-
GM(st_atime_ns, nsec, atime, "time of last access in nanoseconds"),
3405-
GM(st_birthtime_ns, nsec, btime, "time of creation in nanoseconds"),
3406-
GM(st_ctime_ns, nsec, ctime, "time of last change in nanoseconds"),
3407-
GM(st_mtime_ns, nsec, mtime, "time of last modification in nanoseconds"),
3452+
G(st_nlink, "number of hard links"),
3453+
G(st_uid, "user ID of owner"),
3454+
G(st_gid, "group ID of owner"),
3455+
G(st_ino, "inode"),
3456+
G(st_size, "total size, in bytes"),
3457+
G(st_blocks, "number of blocks allocated"),
3458+
G(st_atime, "time of last access"),
3459+
G(st_atime_ns, "time of last access in nanoseconds"),
3460+
G(st_birthtime, "time of creation"),
3461+
G(st_birthtime_ns, "time of creation in nanoseconds"),
3462+
G(st_ctime, "time of last change"),
3463+
G(st_ctime_ns, "time of last change in nanoseconds"),
3464+
G(st_mtime, "time of last modification"),
3465+
G(st_mtime_ns, "time of last modification in nanoseconds"),
3466+
#ifdef STATX_MNT_ID
3467+
G(stx_mnt_id, "mount ID"),
3468+
#endif
3469+
#ifdef STATX_DIOALIGN
3470+
G(stx_dio_mem_align, "direct I/O memory buffer alignment"),
3471+
G(stx_dio_offset_align, "direct I/O file offset alignment"),
3472+
#endif
3473+
#ifdef STATX_SUBVOL
3474+
G(stx_subvol, "subvolume ID"),
3475+
#endif
3476+
#ifdef STATX_WRITE_ATOMIC
3477+
G(stx_atomic_write_unit_min,
3478+
"minimum size for direct I/O with torn-write protection"),
3479+
G(stx_atomic_write_unit_max,
3480+
"maximum size for direct I/O with torn-write protection"),
3481+
G(stx_atomic_write_segments_max,
3482+
"maximum iovecs for direct I/O with torn-write protection"),
3483+
#endif
3484+
#ifdef STATX_DIO_READ_ALIGN
3485+
G(stx_dio_read_offset_align, "direct I/O file offset alignment for reads"),
3486+
#endif
3487+
#ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT
3488+
G(stx_atomic_write_unit_max_opt,
3489+
"maximum optimized size for direct I/O with torn-write protection"),
3490+
#endif
34083491
{NULL},
34093492
};
34103493

3411-
#undef GM
3494+
#undef G
34123495

34133496
static PyObject *
34143497
pystatx_result_repr(PyObject *op)
@@ -3446,24 +3529,19 @@ pystatx_result_repr(PyObject *op)
34463529
Py_DECREF(o);
34473530
}
34483531

3449-
if (Py_ARRAY_LENGTH(pystatx_result_members) > 1
3450-
&& Py_ARRAY_LENGTH(pystatx_result_getset) > 1) {
3451-
WRITE_ASCII(", ");
3452-
}
3453-
34543532
for (size_t i = 0; i < Py_ARRAY_LENGTH(pystatx_result_getset) - 1; ++i) {
3455-
if (i > 0) {
3456-
WRITE_ASCII(", ");
3457-
}
3458-
34593533
PyGetSetDef *d = &pystatx_result_getset[i];
3460-
WRITE_ASCII(d->name);
3461-
WRITE_ASCII("=");
3462-
34633534
PyObject *o = d->get(op, d->closure);
34643535
if (o == NULL) {
34653536
goto error;
34663537
}
3538+
if (o == Py_None) {
3539+
continue;
3540+
}
3541+
3542+
WRITE_ASCII(", ");
3543+
WRITE_ASCII(d->name);
3544+
WRITE_ASCII("=");
34673545
if (PyUnicodeWriter_WriteRepr(writer, o) < 0) {
34683546
Py_DECREF(o);
34693547
goto error;

0 commit comments

Comments
 (0)