Return a duplicate of the context.
+ :class:`!Context` objects also support :func:`copy.replace`,
+ which returns a duplicate with the specified fields replaced.
+ Fields which are not specified keep the values
+ they have in the original context.
+
+ .. versionchanged:: next
+ Added support for :func:`copy.replace`.
+
.. method:: copy_decimal(num, /)
Return a copy of the Decimal instance num.
return nc
__copy__ = copy
+ def __replace__(self, /, **changes):
+ """Returns a copy of self with the specified attributes replaced."""
+ unexpected = changes.keys() - _context_attributes
+ if unexpected:
+ raise TypeError(f'__replace__() got an unexpected keyword '
+ f'argument {min(unexpected)!r}')
+ nc = self.copy()
+ for name, value in changes.items():
+ if name in ('flags', 'traps') and isinstance(value, list):
+ # As in the constructor, accept a list of signals.
+ value = dict((s, int(s in value)) for s in _signals + value)
+ setattr(nc, name, value)
+ return nc
+
def _raise_error(self, condition, explanation = None, *args):
"""Handles an error
self.assertEqual(k1, k2)
self.assertEqual(c.flags, d.flags)
+ def test_replace(self):
+ Context = self.decimal.Context
+ Inexact = self.decimal.Inexact
+ Overflow = self.decimal.Overflow
+ ROUND_UP = self.decimal.ROUND_UP
+
+ c = Context(prec=10, Emin=-99, capitals=0)
+ c.flags[Inexact] = True
+ d = copy.replace(c, prec=20, rounding=ROUND_UP)
+ self.assertEqual(d.prec, 20)
+ self.assertEqual(d.rounding, ROUND_UP)
+ # Not replaced attributes are inherited from the original context.
+ self.assertEqual(d.Emin, -99)
+ self.assertEqual(d.capitals, 0)
+ self.assertEqual(d.Emax, c.Emax)
+ self.assertEqual(d.clamp, c.clamp)
+ self.assertTrue(d.flags[Inexact])
+ self.assertEqual(d.traps, c.traps)
+ # The copy is deep and the original context is left unchanged.
+ self.assertIsNot(d.flags, c.flags)
+ self.assertIsNot(d.traps, c.traps)
+ self.assertEqual(c.prec, 10)
+ self.assertEqual(c.rounding, Context().rounding)
+
+ # As in the constructor, flags and traps can be given as a list.
+ d = copy.replace(c, flags=[Overflow])
+ self.assertTrue(d.flags[Overflow])
+ self.assertFalse(d.flags[Inexact])
+
+ self.assertRaises(TypeError, copy.replace, c, prek=1)
+ self.assertRaises(TypeError, copy.replace, c, prec='spam')
+ # Unlike in the constructor, None is not a valid value.
+ self.assertRaises(TypeError, copy.replace, c, prec=None)
+ self.assertRaises(TypeError, copy.replace, c, flags=None)
+
def test__clamp(self):
# In Python 3.2, the private attribute `_clamp` was made
# public (issue 8540), with the old `_clamp` becoming a
--- /dev/null
+:class:`decimal.Context` objects now support :func:`copy.replace`.
return PyObject_GenericSetAttr(self, name, value);
}
+/* In the constructor and in localcontext() None means "not specified". */
+#define NONE_TO_NULL(x) ((x) == Py_None ? NULL : (x))
+
+/* Set the given attributes. An attribute is left unchanged if the
+ corresponding argument is NULL. */
static int
context_setattrs(PyObject *self, PyObject *prec, PyObject *rounding,
PyObject *emin, PyObject *emax, PyObject *capitals,
PyObject *clamp, PyObject *status, PyObject *traps) {
int ret;
- if (prec != Py_None && context_setprec(self, prec, NULL) < 0) {
+ if (prec != NULL && context_setprec(self, prec, NULL) < 0) {
return -1;
}
- if (rounding != Py_None && context_setround(self, rounding, NULL) < 0) {
+ if (rounding != NULL && context_setround(self, rounding, NULL) < 0) {
return -1;
}
- if (emin != Py_None && context_setemin(self, emin, NULL) < 0) {
+ if (emin != NULL && context_setemin(self, emin, NULL) < 0) {
return -1;
}
- if (emax != Py_None && context_setemax(self, emax, NULL) < 0) {
+ if (emax != NULL && context_setemax(self, emax, NULL) < 0) {
return -1;
}
- if (capitals != Py_None && context_setcapitals(self, capitals, NULL) < 0) {
+ if (capitals != NULL && context_setcapitals(self, capitals, NULL) < 0) {
return -1;
}
- if (clamp != Py_None && context_setclamp(self, clamp, NULL) < 0) {
+ if (clamp != NULL && context_setclamp(self, clamp, NULL) < 0) {
return -1;
}
- if (traps != Py_None) {
+ if (traps != NULL) {
if (PyList_Check(traps)) {
ret = context_settraps_list(self, traps);
}
return ret;
}
}
- if (status != Py_None) {
+ if (status != NULL) {
if (PyList_Check(status)) {
ret = context_setstatus_list(self, status);
}
PyObject *clamp, PyObject *status, PyObject *traps)
/*[clinic end generated code: output=8bfdc59fbe862f44 input=45c704b93cd02959]*/
{
+ /* The context has already been initialized with the default values. */
return context_setattrs(
- self, prec, rounding,
- emin, emax, capitals,
- clamp, status, traps
+ self, NONE_TO_NULL(prec), NONE_TO_NULL(rounding),
+ NONE_TO_NULL(emin), NONE_TO_NULL(emax), NONE_TO_NULL(capitals),
+ NONE_TO_NULL(clamp), NONE_TO_NULL(status), NONE_TO_NULL(traps)
);
}
return context_copy(state, self);
}
+/*[clinic input]
+@text_signature "($self, /, **changes)"
+_decimal.Context.__replace__
+
+ cls: defining_class
+ *
+ prec: object = NULL
+ rounding: object = NULL
+ Emin as emin: object = NULL
+ Emax as emax: object = NULL
+ capitals: object = NULL
+ clamp: object = NULL
+ flags as status: object = NULL
+ traps: object = NULL
+
+Return a copy of the context with the specified attributes replaced.
+[clinic start generated code]*/
+
+static PyObject *
+_decimal_Context___replace___impl(PyObject *self, PyTypeObject *cls,
+ PyObject *prec, PyObject *rounding,
+ PyObject *emin, PyObject *emax,
+ PyObject *capitals, PyObject *clamp,
+ PyObject *status, PyObject *traps)
+/*[clinic end generated code: output=375ef0392df682ec input=414d9d00b25af6f3]*/
+{
+ decimal_state *state = PyType_GetModuleState(cls);
+
+ PyObject *result = context_copy(state, self);
+ if (result == NULL) {
+ return NULL;
+ }
+ if (context_setattrs(result, prec, rounding, emin, emax, capitals,
+ clamp, status, traps) < 0)
+ {
+ Py_DECREF(result);
+ return NULL;
+ }
+ return result;
+}
+
/*[clinic input]
_decimal.Context.__reduce__ = _decimal.Context.copy
}
int ret = context_setattrs(
- local_copy, prec, rounding,
- Emin, Emax, capitals,
- clamp, flags, traps
+ local_copy, NONE_TO_NULL(prec), NONE_TO_NULL(rounding),
+ NONE_TO_NULL(Emin), NONE_TO_NULL(Emax), NONE_TO_NULL(capitals),
+ NONE_TO_NULL(clamp), NONE_TO_NULL(flags), NONE_TO_NULL(traps)
);
if (ret < 0) {
Py_DECREF(local_copy);
/* Miscellaneous */
_DECIMAL_CONTEXT___COPY___METHODDEF
+ _DECIMAL_CONTEXT___REPLACE___METHODDEF
_DECIMAL_CONTEXT___REDUCE___METHODDEF
_DECIMAL_CONTEXT_COPY_METHODDEF
_DECIMAL_CONTEXT_CREATE_DECIMAL_METHODDEF
return _decimal_Context___copy___impl(self, cls);
}
+PyDoc_STRVAR(_decimal_Context___replace____doc__,
+"__replace__($self, /, **changes)\n"
+"--\n"
+"\n"
+"Return a copy of the context with the specified attributes replaced.");
+
+#define _DECIMAL_CONTEXT___REPLACE___METHODDEF \
+ {"__replace__", _PyCFunction_CAST(_decimal_Context___replace__), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context___replace____doc__},
+
+static PyObject *
+_decimal_Context___replace___impl(PyObject *self, PyTypeObject *cls,
+ PyObject *prec, PyObject *rounding,
+ PyObject *emin, PyObject *emax,
+ PyObject *capitals, PyObject *clamp,
+ PyObject *status, PyObject *traps);
+
+static PyObject *
+_decimal_Context___replace__(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+
+ #define NUM_KEYWORDS 8
+ static struct {
+ PyGC_Head _this_is_not_used;
+ PyObject_VAR_HEAD
+ Py_hash_t ob_hash;
+ PyObject *ob_item[NUM_KEYWORDS];
+ } _kwtuple = {
+ .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
+ .ob_hash = -1,
+ .ob_item = { &_Py_ID(prec), &_Py_ID(rounding), &_Py_ID(Emin), &_Py_ID(Emax), &_Py_ID(capitals), &_Py_ID(clamp), &_Py_ID(flags), &_Py_ID(traps), },
+ };
+ #undef NUM_KEYWORDS
+ #define KWTUPLE (&_kwtuple.ob_base.ob_base)
+
+ #else // !Py_BUILD_CORE
+ # define KWTUPLE NULL
+ #endif // !Py_BUILD_CORE
+
+ static const char * const _keywords[] = {"prec", "rounding", "Emin", "Emax", "capitals", "clamp", "flags", "traps", NULL};
+ static _PyArg_Parser _parser = {
+ .keywords = _keywords,
+ .fname = "__replace__",
+ .kwtuple = KWTUPLE,
+ };
+ #undef KWTUPLE
+ PyObject *argsbuf[8];
+ Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0;
+ PyObject *prec = NULL;
+ PyObject *rounding = NULL;
+ PyObject *emin = NULL;
+ PyObject *emax = NULL;
+ PyObject *capitals = NULL;
+ PyObject *clamp = NULL;
+ PyObject *status = NULL;
+ PyObject *traps = NULL;
+
+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
+ /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
+ if (!args) {
+ goto exit;
+ }
+ if (!noptargs) {
+ goto skip_optional_kwonly;
+ }
+ if (args[0]) {
+ prec = args[0];
+ if (!--noptargs) {
+ goto skip_optional_kwonly;
+ }
+ }
+ if (args[1]) {
+ rounding = args[1];
+ if (!--noptargs) {
+ goto skip_optional_kwonly;
+ }
+ }
+ if (args[2]) {
+ emin = args[2];
+ if (!--noptargs) {
+ goto skip_optional_kwonly;
+ }
+ }
+ if (args[3]) {
+ emax = args[3];
+ if (!--noptargs) {
+ goto skip_optional_kwonly;
+ }
+ }
+ if (args[4]) {
+ capitals = args[4];
+ if (!--noptargs) {
+ goto skip_optional_kwonly;
+ }
+ }
+ if (args[5]) {
+ clamp = args[5];
+ if (!--noptargs) {
+ goto skip_optional_kwonly;
+ }
+ }
+ if (args[6]) {
+ status = args[6];
+ if (!--noptargs) {
+ goto skip_optional_kwonly;
+ }
+ }
+ traps = args[7];
+skip_optional_kwonly:
+ return_value = _decimal_Context___replace___impl(self, cls, prec, rounding, emin, emax, capitals, clamp, status, traps);
+
+exit:
+ return return_value;
+}
+
PyDoc_STRVAR(_decimal_Context___reduce____doc__,
"__reduce__($self, /)\n"
"--\n"
#ifndef _DECIMAL_CONTEXT_APPLY_METHODDEF
#define _DECIMAL_CONTEXT_APPLY_METHODDEF
#endif /* !defined(_DECIMAL_CONTEXT_APPLY_METHODDEF) */
-/*[clinic end generated code: output=0eb835634388294e input=a9049054013a1b77]*/
+/*[clinic end generated code: output=75ab23467fa0eee3 input=a9049054013a1b77]*/