@@ -82,6 +82,9 @@ typedef struct {
82
82
/* Convert rationals for comparison */
83
83
PyObject * Rational ;
84
84
85
+ /* Invariant: NULL or pointer to _pydecimal.Decimal */
86
+ PyObject * PyDecimal ;
87
+
85
88
PyObject * SignalTuple ;
86
89
87
90
struct DecCondMap * signal_map ;
@@ -3336,56 +3339,6 @@ dotsep_as_utf8(const char *s)
3336
3339
return utf8 ;
3337
3340
}
3338
3341
3339
- /* copy of libmpdec _mpd_round() */
3340
- static void
3341
- _mpd_round (mpd_t * result , const mpd_t * a , mpd_ssize_t prec ,
3342
- const mpd_context_t * ctx , uint32_t * status )
3343
- {
3344
- mpd_ssize_t exp = a -> exp + a -> digits - prec ;
3345
-
3346
- if (prec <= 0 ) {
3347
- mpd_seterror (result , MPD_Invalid_operation , status );
3348
- return ;
3349
- }
3350
- if (mpd_isspecial (a ) || mpd_iszero (a )) {
3351
- mpd_qcopy (result , a , status );
3352
- return ;
3353
- }
3354
-
3355
- mpd_qrescale_fmt (result , a , exp , ctx , status );
3356
- if (result -> digits > prec ) {
3357
- mpd_qrescale_fmt (result , result , exp + 1 , ctx , status );
3358
- }
3359
- }
3360
-
3361
- /* Locate negative zero "z" option within a UTF-8 format spec string.
3362
- * Returns pointer to "z", else NULL.
3363
- * The portion of the spec we're working with is [[fill]align][sign][z] */
3364
- static const char *
3365
- format_spec_z_search (char const * fmt , Py_ssize_t size ) {
3366
- char const * pos = fmt ;
3367
- char const * fmt_end = fmt + size ;
3368
- /* skip over [[fill]align] (fill may be multi-byte character) */
3369
- pos += 1 ;
3370
- while (pos < fmt_end && * pos & 0x80 ) {
3371
- pos += 1 ;
3372
- }
3373
- if (pos < fmt_end && strchr ("<>=^" , * pos ) != NULL ) {
3374
- pos += 1 ;
3375
- } else {
3376
- /* fill not present-- skip over [align] */
3377
- pos = fmt ;
3378
- if (pos < fmt_end && strchr ("<>=^" , * pos ) != NULL ) {
3379
- pos += 1 ;
3380
- }
3381
- }
3382
- /* skip over [sign] */
3383
- if (pos < fmt_end && strchr ("+- " , * pos ) != NULL ) {
3384
- pos += 1 ;
3385
- }
3386
- return pos < fmt_end && * pos == 'z' ? pos : NULL ;
3387
- }
3388
-
3389
3342
static int
3390
3343
dict_get_item_string (PyObject * dict , const char * key , PyObject * * valueobj , const char * * valuestr )
3391
3344
{
@@ -3411,6 +3364,48 @@ dict_get_item_string(PyObject *dict, const char *key, PyObject **valueobj, const
3411
3364
return 0 ;
3412
3365
}
3413
3366
3367
+ /*
3368
+ * Fallback _pydecimal formatting for new format specifiers that mpdecimal does
3369
+ * not yet support. As documented, libmpdec follows the PEP-3101 format language:
3370
+ * https://www.bytereef.org/mpdecimal/doc/libmpdec/assign-convert.html#to-string
3371
+ */
3372
+ static PyObject *
3373
+ pydec_format (PyObject * dec , PyObject * context , PyObject * fmt , decimal_state * state )
3374
+ {
3375
+ PyObject * result ;
3376
+ PyObject * pydec ;
3377
+ PyObject * u ;
3378
+
3379
+ if (state -> PyDecimal == NULL ) {
3380
+ state -> PyDecimal = _PyImport_GetModuleAttrString ("_pydecimal" , "Decimal" );
3381
+ if (state -> PyDecimal == NULL ) {
3382
+ return NULL ;
3383
+ }
3384
+ }
3385
+
3386
+ u = dec_str (dec );
3387
+ if (u == NULL ) {
3388
+ return NULL ;
3389
+ }
3390
+
3391
+ pydec = PyObject_CallOneArg (state -> PyDecimal , u );
3392
+ Py_DECREF (u );
3393
+ if (pydec == NULL ) {
3394
+ return NULL ;
3395
+ }
3396
+
3397
+ result = PyObject_CallMethod (pydec , "__format__" , "(OO)" , fmt , context );
3398
+ Py_DECREF (pydec );
3399
+
3400
+ if (result == NULL && PyErr_ExceptionMatches (PyExc_ValueError )) {
3401
+ /* Do not confuse users with the _pydecimal exception */
3402
+ PyErr_Clear ();
3403
+ PyErr_SetString (PyExc_ValueError , "invalid format string" );
3404
+ }
3405
+
3406
+ return result ;
3407
+ }
3408
+
3414
3409
/* Formatted representation of a PyDecObject. */
3415
3410
static PyObject *
3416
3411
dec_format (PyObject * dec , PyObject * args )
@@ -3423,16 +3418,11 @@ dec_format(PyObject *dec, PyObject *args)
3423
3418
PyObject * fmtarg ;
3424
3419
PyObject * context ;
3425
3420
mpd_spec_t spec ;
3426
- char const * fmt ;
3427
- char * fmt_copy = NULL ;
3421
+ char * fmt ;
3428
3422
char * decstring = NULL ;
3429
3423
uint32_t status = 0 ;
3430
3424
int replace_fillchar = 0 ;
3431
- int no_neg_0 = 0 ;
3432
3425
Py_ssize_t size ;
3433
- mpd_t * mpd = MPD (dec );
3434
- mpd_uint_t dt [MPD_MINALLOC_MAX ];
3435
- mpd_t tmp = {MPD_STATIC |MPD_STATIC_DATA ,0 ,0 ,0 ,MPD_MINALLOC_MAX ,dt };
3436
3426
3437
3427
3438
3428
decimal_state * state = get_module_state_by_def (Py_TYPE (dec ));
@@ -3442,7 +3432,7 @@ dec_format(PyObject *dec, PyObject *args)
3442
3432
}
3443
3433
3444
3434
if (PyUnicode_Check (fmtarg )) {
3445
- fmt = PyUnicode_AsUTF8AndSize (fmtarg , & size );
3435
+ fmt = ( char * ) PyUnicode_AsUTF8AndSize (fmtarg , & size );
3446
3436
if (fmt == NULL ) {
3447
3437
return NULL ;
3448
3438
}
@@ -3454,35 +3444,15 @@ dec_format(PyObject *dec, PyObject *args)
3454
3444
}
3455
3445
}
3456
3446
3457
- /* NOTE: If https://github.com/python/cpython/pull/29438 lands, the
3458
- * format string manipulation below can be eliminated by enhancing
3459
- * the forked mpd_parse_fmt_str(). */
3460
3447
if (size > 0 && fmt [0 ] == '\0' ) {
3461
3448
/* NUL fill character: must be replaced with a valid UTF-8 char
3462
3449
before calling mpd_parse_fmt_str(). */
3463
3450
replace_fillchar = 1 ;
3464
- fmt = fmt_copy = dec_strdup (fmt , size );
3465
- if (fmt_copy == NULL ) {
3451
+ fmt = dec_strdup (fmt , size );
3452
+ if (fmt == NULL ) {
3466
3453
return NULL ;
3467
3454
}
3468
- fmt_copy [0 ] = '_' ;
3469
- }
3470
- /* Strip 'z' option, which isn't understood by mpd_parse_fmt_str().
3471
- * NOTE: fmt is always null terminated by PyUnicode_AsUTF8AndSize() */
3472
- char const * z_position = format_spec_z_search (fmt , size );
3473
- if (z_position != NULL ) {
3474
- no_neg_0 = 1 ;
3475
- size_t z_index = z_position - fmt ;
3476
- if (fmt_copy == NULL ) {
3477
- fmt = fmt_copy = dec_strdup (fmt , size );
3478
- if (fmt_copy == NULL ) {
3479
- return NULL ;
3480
- }
3481
- }
3482
- /* Shift characters (including null terminator) left,
3483
- overwriting the 'z' option. */
3484
- memmove (fmt_copy + z_index , fmt_copy + z_index + 1 , size - z_index );
3485
- size -= 1 ;
3455
+ fmt [0 ] = '_' ;
3486
3456
}
3487
3457
}
3488
3458
else {
@@ -3492,10 +3462,13 @@ dec_format(PyObject *dec, PyObject *args)
3492
3462
}
3493
3463
3494
3464
if (!mpd_parse_fmt_str (& spec , fmt , CtxCaps (context ))) {
3495
- PyErr_SetString (PyExc_ValueError ,
3496
- "invalid format string" );
3497
- goto finish ;
3465
+ if (replace_fillchar ) {
3466
+ PyMem_Free (fmt );
3467
+ }
3468
+
3469
+ return pydec_format (dec , context , fmtarg , state );
3498
3470
}
3471
+
3499
3472
if (replace_fillchar ) {
3500
3473
/* In order to avoid clobbering parts of UTF-8 thousands separators or
3501
3474
decimal points when the substitution is reversed later, the actual
@@ -3548,45 +3521,8 @@ dec_format(PyObject *dec, PyObject *args)
3548
3521
}
3549
3522
}
3550
3523
3551
- if (no_neg_0 && mpd_isnegative (mpd ) && !mpd_isspecial (mpd )) {
3552
- /* Round into a temporary (carefully mirroring the rounding
3553
- of mpd_qformat_spec()), and check if the result is negative zero.
3554
- If so, clear the sign and format the resulting positive zero. */
3555
- mpd_ssize_t prec ;
3556
- mpd_qcopy (& tmp , mpd , & status );
3557
- if (spec .prec >= 0 ) {
3558
- switch (spec .type ) {
3559
- case 'f' :
3560
- mpd_qrescale (& tmp , & tmp , - spec .prec , CTX (context ), & status );
3561
- break ;
3562
- case '%' :
3563
- tmp .exp += 2 ;
3564
- mpd_qrescale (& tmp , & tmp , - spec .prec , CTX (context ), & status );
3565
- break ;
3566
- case 'g' :
3567
- prec = (spec .prec == 0 ) ? 1 : spec .prec ;
3568
- if (tmp .digits > prec ) {
3569
- _mpd_round (& tmp , & tmp , prec , CTX (context ), & status );
3570
- }
3571
- break ;
3572
- case 'e' :
3573
- if (!mpd_iszero (& tmp )) {
3574
- _mpd_round (& tmp , & tmp , spec .prec + 1 , CTX (context ), & status );
3575
- }
3576
- break ;
3577
- }
3578
- }
3579
- if (status & MPD_Errors ) {
3580
- PyErr_SetString (PyExc_ValueError , "unexpected error when rounding" );
3581
- goto finish ;
3582
- }
3583
- if (mpd_iszero (& tmp )) {
3584
- mpd_set_positive (& tmp );
3585
- mpd = & tmp ;
3586
- }
3587
- }
3588
3524
3589
- decstring = mpd_qformat_spec (mpd , & spec , CTX (context ), & status );
3525
+ decstring = mpd_qformat_spec (MPD ( dec ) , & spec , CTX (context ), & status );
3590
3526
if (decstring == NULL ) {
3591
3527
if (status & MPD_Malloc_error ) {
3592
3528
PyErr_NoMemory ();
@@ -3609,7 +3545,7 @@ dec_format(PyObject *dec, PyObject *args)
3609
3545
Py_XDECREF (grouping );
3610
3546
Py_XDECREF (sep );
3611
3547
Py_XDECREF (dot );
3612
- if (fmt_copy ) PyMem_Free (fmt_copy );
3548
+ if (replace_fillchar ) PyMem_Free (fmt );
3613
3549
if (decstring ) mpd_free (decstring );
3614
3550
return result ;
3615
3551
}
@@ -5987,6 +5923,9 @@ _decimal_exec(PyObject *m)
5987
5923
Py_CLEAR (collections_abc );
5988
5924
Py_CLEAR (MutableMapping );
5989
5925
5926
+ /* For format specifiers not yet supported by libmpdec */
5927
+ state -> PyDecimal = NULL ;
5928
+
5990
5929
/* Add types to the module */
5991
5930
CHECK_INT (PyModule_AddType (m , state -> PyDec_Type ));
5992
5931
CHECK_INT (PyModule_AddType (m , state -> PyDecContext_Type ));
@@ -6192,6 +6131,7 @@ decimal_clear(PyObject *module)
6192
6131
Py_CLEAR (state -> extended_context_template );
6193
6132
Py_CLEAR (state -> Rational );
6194
6133
Py_CLEAR (state -> SignalTuple );
6134
+ Py_CLEAR (state -> PyDecimal );
6195
6135
6196
6136
PyMem_Free (state -> signal_map );
6197
6137
PyMem_Free (state -> cond_map );
0 commit comments