@@ -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
34033451static 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
34133496static PyObject *
34143497pystatx_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