From bc114174d96f73c935596d98ff79b6e6dd7fbe0e Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Fri, 1 Apr 2022 20:35:07 +0100 Subject: [PATCH 01/19] Add attribute keyword arguments to decimal.localcontext --- Lib/_pydecimal.py | 7 +- Modules/_decimal/_decimal.c | 151 +++++++++++++++++++++++------------- 2 files changed, 100 insertions(+), 58 deletions(-) diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index f6d9ddf42e4734..47f86cb1f0b594 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -464,7 +464,7 @@ def setcontext(context): del contextvars # Don't contaminate the namespace -def localcontext(ctx=None): +def localcontext(ctx=None, **kwargs): """Return a context manager for a copy of the supplied context Uses a copy of the current context if no context is specified @@ -500,7 +500,10 @@ def sin(x): >>> print(getcontext().prec) 28 """ - if ctx is None: ctx = getcontext() + if ctx is None: + ctx = getcontext() + for key, value in kwargs.items(): + setattr(ctx, key, value) return _ContextManager(ctx) diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 35a115676a71be..266d480bb36306 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -1156,6 +1156,67 @@ context_setattr(PyObject *self, PyObject *name, PyObject *value) return PyObject_GenericSetAttr(self, name, value); } +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) { + return -1; + } + if (rounding != Py_None && context_setround(self, rounding, NULL) < 0) { + return -1; + } + if (emin != Py_None && context_setemin(self, emin, NULL) < 0) { + return -1; + } + if (emax != Py_None && context_setemax(self, emax, NULL) < 0) { + return -1; + } + if (capitals != Py_None && context_setcapitals(self, capitals, NULL) < 0) { + return -1; + } + if (clamp != Py_None && context_setclamp(self, clamp, NULL) < 0) { + return -1; + } + + if (traps != Py_None) { + if (PyList_Check(traps)) { + ret = context_settraps_list(self, traps); + } +#ifdef EXTRA_FUNCTIONALITY + else if (PyLong_Check(traps)) { + ret = context_settraps(self, traps, NULL); + } +#endif + else { + ret = context_settraps_dict(self, traps); + } + if (ret < 0) { + return ret; + } + } + if (status != Py_None) { + if (PyList_Check(status)) { + ret = context_setstatus_list(self, status); + } +#ifdef EXTRA_FUNCTIONALITY + else if (PyLong_Check(status)) { + ret = context_setstatus(self, status, NULL); + } +#endif + else { + ret = context_setstatus_dict(self, status); + } + if (ret < 0) { + return ret; + } + } + + return 0; +} + static PyObject * context_clear_traps(PyObject *self, PyObject *dummy UNUSED) { @@ -1255,7 +1316,6 @@ context_init(PyObject *self, PyObject *args, PyObject *kwds) PyObject *clamp = Py_None; PyObject *status = Py_None; PyObject *traps = Py_None; - int ret; assert(PyTuple_Check(args)); @@ -1267,59 +1327,11 @@ context_init(PyObject *self, PyObject *args, PyObject *kwds) return -1; } - if (prec != Py_None && context_setprec(self, prec, NULL) < 0) { - return -1; - } - if (rounding != Py_None && context_setround(self, rounding, NULL) < 0) { - return -1; - } - if (emin != Py_None && context_setemin(self, emin, NULL) < 0) { - return -1; - } - if (emax != Py_None && context_setemax(self, emax, NULL) < 0) { - return -1; - } - if (capitals != Py_None && context_setcapitals(self, capitals, NULL) < 0) { - return -1; - } - if (clamp != Py_None && context_setclamp(self, clamp, NULL) < 0) { - return -1; - } - - if (traps != Py_None) { - if (PyList_Check(traps)) { - ret = context_settraps_list(self, traps); - } -#ifdef EXTRA_FUNCTIONALITY - else if (PyLong_Check(traps)) { - ret = context_settraps(self, traps, NULL); - } -#endif - else { - ret = context_settraps_dict(self, traps); - } - if (ret < 0) { - return ret; - } - } - if (status != Py_None) { - if (PyList_Check(status)) { - ret = context_setstatus_list(self, status); - } -#ifdef EXTRA_FUNCTIONALITY - else if (PyLong_Check(status)) { - ret = context_setstatus(self, status, NULL); - } -#endif - else { - ret = context_setstatus_dict(self, status); - } - if (ret < 0) { - return ret; - } - } - - return 0; + return context_setattrs( + self, prec, rounding, + emin, emax, capitals, + clamp, status, traps + ); } static PyObject * @@ -1721,13 +1733,29 @@ PyDec_SetCurrentContext(PyObject *self UNUSED, PyObject *v) static PyObject * ctxmanager_new(PyTypeObject *type UNUSED, PyObject *args, PyObject *kwds) { - static char *kwlist[] = {"ctx", NULL}; + static char *kwlist[] = { + "ctx", "prec", "rounding", + "Emin", "Emax", "capitals", + "clamp", "flags", "traps", + NULL + }; PyDecContextManagerObject *self; PyObject *local = Py_None; PyObject *global; + PyObject *prec = Py_None; + PyObject *rounding = Py_None; + PyObject *Emin = Py_None; + PyObject *Emax = Py_None; + PyObject *capitals = Py_None; + PyObject *clamp = Py_None; + PyObject *flags = Py_None; + PyObject *traps = Py_None; + + CURRENT_CONTEXT(global); - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &local)) { + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOOOOOOO", kwlist, &local, + &prec, &rounding, &Emin, &Emax, &capitals, &clamp, &flags, &traps)) { return NULL; } if (local == Py_None) { @@ -1754,6 +1782,17 @@ ctxmanager_new(PyTypeObject *type UNUSED, PyObject *args, PyObject *kwds) self->global = global; Py_INCREF(self->global); + int ret = context_setattrs( + self->local, prec, rounding, + Emin, Emax, capitals, + clamp, flags, traps + ); + + if(ret < 0){ + return NULL; + }; + + return (PyObject *)self; } From 16617094346da726f5e40b613b2c5bf64964de7c Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Fri, 1 Apr 2022 21:38:15 +0100 Subject: [PATCH 02/19] Add documentation, write tests --- Doc/library/decimal.rst | 12 ++++++++++-- Lib/_pydecimal.py | 5 ++++- Lib/test/test_decimal.py | 15 +++++++++++++++ Modules/_decimal/docstrings.h | 2 +- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index e759c5cf23b9e7..0a3e17d2a92650 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -925,12 +925,13 @@ Each thread has its own current context which is accessed or changed using the You can also use the :keyword:`with` statement and the :func:`localcontext` function to temporarily change the active context. -.. function:: localcontext(ctx=None) +.. function:: localcontext(ctx=None, \*\*kwargs) Return a context manager that will set the current context for the active thread to a copy of *ctx* on entry to the with-statement and restore the previous context when exiting the with-statement. If no context is specified, a copy of the - current context is used. + current context is used. The *kwargs* argument is used to specify the attributes + of the context that will be returned. For example, the following code sets the current decimal precision to 42 places, performs a calculation, and then automatically restores the previous context:: @@ -942,10 +943,17 @@ function to temporarily change the active context. s = calculate_something() s = +s # Round the final result back to the default precision + +Raises :exc:`TypeError` if *kwargs* supplies an attribute that :class:`Context` doesn't +support. + +Raises :exc:`ValueError` if *kwargs* supplies an invalid value for an attribute. + New contexts can also be created using the :class:`Context` constructor described below. In addition, the module provides three pre-made contexts: + .. class:: BasicContext This is a standard context defined by the General Decimal Arithmetic diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index 47f86cb1f0b594..a1e50576aac3c9 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -503,7 +503,10 @@ def sin(x): if ctx is None: ctx = getcontext() for key, value in kwargs.items(): - setattr(ctx, key, value) + try: + setattr(ctx, key, value) + except: + raise TypeError(f"'{key}' is an invalid keyword argument for this function") return _ContextManager(ctx) diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index b68cfbef23f16d..bd3fd11585e6ab 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -3602,6 +3602,21 @@ def test_localcontextarg(self): self.assertIsNot(new_ctx, set_ctx, 'did not copy the context') self.assertIs(set_ctx, enter_ctx, '__enter__ returned wrong context') + def test_localcontext_kwargs(self): + with self.decimal.localcontext( + prec=10, rounding=ROUND_HALF_DOWN, + Emin=-20, Emax=20, capitals=0, + clamp=1 + ) as ctx: + self.assertEqual(ctx.prec, 10) + self.assertEqual(ctx.rounding, self.decimal.ROUND_HALF_DOWN) + self.assertEqual(ctx.Emin, -20) + self.assertEqual(ctx.Emax, 20) + self.assertEqual(ctx.capitals, 0) + self.assertEqual(ctx.clamp, 1) + + self.assertRaises(TypeError, self.decimal.localcontext, precision=10) + def test_nested_with_statements(self): # Use a copy of the supplied context in the block Decimal = self.decimal.Decimal diff --git a/Modules/_decimal/docstrings.h b/Modules/_decimal/docstrings.h index f7fd6e79529984..a1823cdd32b74c 100644 --- a/Modules/_decimal/docstrings.h +++ b/Modules/_decimal/docstrings.h @@ -30,7 +30,7 @@ Set a new default context.\n\ \n"); PyDoc_STRVAR(doc_localcontext, -"localcontext($module, /, ctx=None)\n--\n\n\ +"localcontext($module, /, ctx=None, **kwargs)\n--\n\n\ Return a context manager that will set the default context to a copy of ctx\n\ on entry to the with-statement and restore the previous default context when\n\ exiting the with-statement. If no context is specified, a copy of the current\n\ From e58def6d3880ea6a9f1e6965459bd8d9bf87e975 Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Fri, 1 Apr 2022 21:45:52 +0100 Subject: [PATCH 03/19] Add news entry --- .../NEWS.d/next/Library/2022-04-01-21-44-00.bpo-47135.TvkKB-.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2022-04-01-21-44-00.bpo-47135.TvkKB-.rst diff --git a/Misc/NEWS.d/next/Library/2022-04-01-21-44-00.bpo-47135.TvkKB-.rst b/Misc/NEWS.d/next/Library/2022-04-01-21-44-00.bpo-47135.TvkKB-.rst new file mode 100644 index 00000000000000..2323c22c007e97 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-04-01-21-44-00.bpo-47135.TvkKB-.rst @@ -0,0 +1 @@ +:meth:`decimal.localcontext` now accepts context attributes via keyword arguments From 4b01b1b8c66df7c96433b79bd76db69326cb246a Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Fri, 1 Apr 2022 21:53:55 +0100 Subject: [PATCH 04/19] Amend documentation --- Doc/library/decimal.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index 0a3e17d2a92650..a16d3756410304 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -931,7 +931,7 @@ function to temporarily change the active context. to a copy of *ctx* on entry to the with-statement and restore the previous context when exiting the with-statement. If no context is specified, a copy of the current context is used. The *kwargs* argument is used to specify the attributes - of the context that will be returned. + of the new context. For example, the following code sets the current decimal precision to 42 places, performs a calculation, and then automatically restores the previous context:: From 6fde168d702fdafa21104a10602e4e8a9fa71f36 Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Fri, 1 Apr 2022 21:54:39 +0100 Subject: [PATCH 05/19] Update documentation --- Doc/library/decimal.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index a16d3756410304..bed44818b5a29f 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -930,7 +930,7 @@ function to temporarily change the active context. Return a context manager that will set the current context for the active thread to a copy of *ctx* on entry to the with-statement and restore the previous context when exiting the with-statement. If no context is specified, a copy of the - current context is used. The *kwargs* argument is used to specify the attributes + current context is used. The *kwargs* argument is used to set the attributes of the new context. For example, the following code sets the current decimal precision to 42 places, From 7129b9e1691da90f9978357722fe203571e2a5d7 Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Mon, 4 Apr 2022 06:59:27 +0100 Subject: [PATCH 06/19] Catch specific exceptions, Add ValueError tests --- Lib/_pydecimal.py | 2 +- Lib/test/test_decimal.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index a1e50576aac3c9..38d967afce7fbd 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -505,7 +505,7 @@ def sin(x): for key, value in kwargs.items(): try: setattr(ctx, key, value) - except: + except AttributeError: raise TypeError(f"'{key}' is an invalid keyword argument for this function") return _ContextManager(ctx) diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index bd3fd11585e6ab..0256151b210eca 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -3616,6 +3616,10 @@ def test_localcontext_kwargs(self): self.assertEqual(ctx.clamp, 1) self.assertRaises(TypeError, self.decimal.localcontext, precision=10) + self.assertRaises(ValueError, self.decimal.localcontext, Emin=1) + self.assertRaises(ValueError, self.decimal.localcontext, Emax=-1) + self.assertRaises(ValueError, self.decimal.localcontext, capitals=2) + self.assertRaises(ValueError, self.decimal.localcontext, clamp=2) def test_nested_with_statements(self): # Use a copy of the supplied context in the block From 20d26db24b4ca56696964b37ef88188736c4fd96 Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Mon, 4 Apr 2022 07:02:02 +0100 Subject: [PATCH 07/19] Free self --- Modules/_decimal/_decimal.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 266d480bb36306..7ac8f0a724e0c1 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -1788,10 +1788,10 @@ ctxmanager_new(PyTypeObject *type UNUSED, PyObject *args, PyObject *kwds) clamp, flags, traps ); - if(ret < 0){ + if (ret < 0) { + Py_DECREF(self); return NULL; - }; - + } return (PyObject *)self; } From f5834d16f7e5210b1561bd2a1e8d249893e70e4b Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Mon, 4 Apr 2022 07:48:01 +0100 Subject: [PATCH 08/19] Rewrite attribute validation --- Doc/library/decimal.rst | 10 +++++----- Lib/_pydecimal.py | 7 ++++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index bed44818b5a29f..552a642b142665 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -943,17 +943,17 @@ function to temporarily change the active context. s = calculate_something() s = +s # Round the final result back to the default precision + .. versionchanged:: 3.11 + Raises :exc:`TypeError` if *kwargs* supplies an attribute that :class:`Context` doesn't + support. -Raises :exc:`TypeError` if *kwargs* supplies an attribute that :class:`Context` doesn't -support. - -Raises :exc:`ValueError` if *kwargs* supplies an invalid value for an attribute. + .. versionchanged:: 3.11 + Raises :exc:`ValueError` if *kwargs* supplies an invalid value for an attribute. New contexts can also be created using the :class:`Context` constructor described below. In addition, the module provides three pre-made contexts: - .. class:: BasicContext This is a standard context defined by the General Decimal Arithmetic diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index 38d967afce7fbd..c0a45c96457b00 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -441,6 +441,8 @@ class FloatOperation(DecimalException, TypeError): _current_context_var = contextvars.ContextVar('decimal_context') +_context_attributes = ['prec', 'Emin', 'Emax', 'capitals', 'clamp', 'rounding', 'flags', 'traps'] + def getcontext(): """Returns this thread's context. @@ -503,10 +505,9 @@ def sin(x): if ctx is None: ctx = getcontext() for key, value in kwargs.items(): - try: - setattr(ctx, key, value) - except AttributeError: + if not key in _context_attributes: raise TypeError(f"'{key}' is an invalid keyword argument for this function") + setattr(ctx, key, value) return _ContextManager(ctx) From 054361502165b9bc04cc3973e92f3f7b95eedf8f Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Mon, 4 Apr 2022 07:54:05 +0100 Subject: [PATCH 09/19] Amend documentation, Add tests --- Doc/library/decimal.rst | 2 +- Lib/test/test_decimal.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index 552a642b142665..0a165b60cf019e 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -948,7 +948,7 @@ function to temporarily change the active context. support. .. versionchanged:: 3.11 - Raises :exc:`ValueError` if *kwargs* supplies an invalid value for an attribute. + Raises either :exc:`TypeError` or :exc:`ValueError` if *kwargs* supplies an invalid value for an attribute. New contexts can also be created using the :class:`Context` constructor described below. In addition, the module provides three pre-made contexts: diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 0256151b210eca..0a541a37a317e0 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -3616,11 +3616,18 @@ def test_localcontext_kwargs(self): self.assertEqual(ctx.clamp, 1) self.assertRaises(TypeError, self.decimal.localcontext, precision=10) + self.assertRaises(ValueError, self.decimal.localcontext, Emin=1) self.assertRaises(ValueError, self.decimal.localcontext, Emax=-1) self.assertRaises(ValueError, self.decimal.localcontext, capitals=2) self.assertRaises(ValueError, self.decimal.localcontext, clamp=2) + self.assertRaises(TypeError, self.decimal.localcontext, rounding="") + self.assertRaises(TypeError, self.decimal.localcontext, flags="") + self.assertRaises(TypeError, self.decimal.localcontext, traps="") + self.assertRaises(TypeError, self.decimal.localcontext, Emin="") + self.assertRaises(TypeError, self.decimal.localcontext, Emax="") + def test_nested_with_statements(self): # Use a copy of the supplied context in the block Decimal = self.decimal.Decimal From 7429298902d11f6daae4694e7f9cc9ac994f6000 Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Mon, 4 Apr 2022 07:57:42 +0100 Subject: [PATCH 10/19] Test prividing with an integer raises TypeError --- Lib/test/test_decimal.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 0a541a37a317e0..dcbf9b4ee6f281 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -3623,6 +3623,8 @@ def test_localcontext_kwargs(self): self.assertRaises(ValueError, self.decimal.localcontext, clamp=2) self.assertRaises(TypeError, self.decimal.localcontext, rounding="") + self.assertRaises(TypeError, self.decimal.localcontext, rounding=1) + self.assertRaises(TypeError, self.decimal.localcontext, flags="") self.assertRaises(TypeError, self.decimal.localcontext, traps="") self.assertRaises(TypeError, self.decimal.localcontext, Emin="") From 43531863a4f9d2e927f7668d9bde2c8210bd7f0e Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Mon, 4 Apr 2022 09:20:35 +0100 Subject: [PATCH 11/19] Promote use of keyword arguments in documentation --- Doc/Makefile | 4 ++-- Doc/library/decimal.rst | 14 +++++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Doc/Makefile b/Doc/Makefile index 61a7ce0d0981f0..55f3ccb6c78504 100644 --- a/Doc/Makefile +++ b/Doc/Makefile @@ -155,8 +155,8 @@ venv: echo "To recreate it, remove it first with \`make clean-venv'."; \ else \ $(PYTHON) -m venv $(VENVDIR); \ - $(VENVDIR)/bin/python3 -m pip install -U pip setuptools; \ - $(VENVDIR)/bin/python3 -m pip install -r requirements.txt; \ + $(VENVDIR)/bin/python3 -m pip --cert ~/.ssh/ags.pem install -U pip setuptools; \ + $(VENVDIR)/bin/python3 -m pip --cert ~/.ssh/ags.pem install -r requirements.txt; \ echo "The venv has been created in the $(VENVDIR) directory"; \ fi diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index 0a165b60cf019e..08382d238a6686 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -943,12 +943,20 @@ function to temporarily change the active context. s = calculate_something() s = +s # Round the final result back to the default precision + Using keyword arguments, the code would be the following:: + + from decimal import localcontext + + with localcontext(prec=42) as ctx: + s = calculate_something() + s = +s + .. versionchanged:: 3.11 - Raises :exc:`TypeError` if *kwargs* supplies an attribute that :class:`Context` doesn't - support. + Raises :exc:`TypeError` if *kwargs* supplies an attribute that :class:`Context` doesn't + support. .. versionchanged:: 3.11 - Raises either :exc:`TypeError` or :exc:`ValueError` if *kwargs* supplies an invalid value for an attribute. + Raises either :exc:`TypeError` or :exc:`ValueError` if *kwargs* supplies an invalid value for an attribute. New contexts can also be created using the :class:`Context` constructor described below. In addition, the module provides three pre-made contexts: From 0775ccffbc16f90cac02c954715c1e3f2fb9d836 Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Mon, 4 Apr 2022 14:22:52 +0100 Subject: [PATCH 12/19] Revert changes to Makefile --- Doc/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/Makefile b/Doc/Makefile index 55f3ccb6c78504..61a7ce0d0981f0 100644 --- a/Doc/Makefile +++ b/Doc/Makefile @@ -155,8 +155,8 @@ venv: echo "To recreate it, remove it first with \`make clean-venv'."; \ else \ $(PYTHON) -m venv $(VENVDIR); \ - $(VENVDIR)/bin/python3 -m pip --cert ~/.ssh/ags.pem install -U pip setuptools; \ - $(VENVDIR)/bin/python3 -m pip --cert ~/.ssh/ags.pem install -r requirements.txt; \ + $(VENVDIR)/bin/python3 -m pip install -U pip setuptools; \ + $(VENVDIR)/bin/python3 -m pip install -r requirements.txt; \ echo "The venv has been created in the $(VENVDIR) directory"; \ fi From 32c5ae0f08d32e184f5b0dd9f511347025fe928e Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Mon, 4 Apr 2022 16:51:39 +0100 Subject: [PATCH 13/19] Use frozen set to store context attributes Co-authored-by: Jelle Zijlstra --- Lib/_pydecimal.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index c0a45c96457b00..68700d7a295b4b 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -441,7 +441,9 @@ class FloatOperation(DecimalException, TypeError): _current_context_var = contextvars.ContextVar('decimal_context') -_context_attributes = ['prec', 'Emin', 'Emax', 'capitals', 'clamp', 'rounding', 'flags', 'traps'] +_context_attributes = frozenset( + ['prec', 'Emin', 'Emax', 'capitals', 'clamp', 'rounding', 'flags', 'traps'] +) def getcontext(): """Returns this thread's context. From 12acb22b88670214b2f6708f5b08c1ae1f17f3aa Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Mon, 4 Apr 2022 17:08:05 +0100 Subject: [PATCH 14/19] Correctly check whether context attributes are valid Co-authored-by: Jelle Zijlstra --- Lib/_pydecimal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index 68700d7a295b4b..989bbf708bb02b 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -507,7 +507,7 @@ def sin(x): if ctx is None: ctx = getcontext() for key, value in kwargs.items(): - if not key in _context_attributes: + if key not in _context_attributes: raise TypeError(f"'{key}' is an invalid keyword argument for this function") setattr(ctx, key, value) return _ContextManager(ctx) From 0a1a6244d04b17e8efc0ddfadfd2e95a9f0661db Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Mon, 4 Apr 2022 21:48:24 +0100 Subject: [PATCH 15/19] Update decimal.rst --- Doc/library/decimal.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index 08382d238a6686..df324daa0c46e4 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -950,13 +950,13 @@ function to temporarily change the active context. with localcontext(prec=42) as ctx: s = calculate_something() s = +s + + Raises :exc:`TypeError` if *kwargs* supplies an attribute that :class:`Context` doesn't + support. Raises either :exc:`TypeError` or :exc:`ValueError` if *kwargs* supplies an + invalid value for an attribute. .. versionchanged:: 3.11 - Raises :exc:`TypeError` if *kwargs* supplies an attribute that :class:`Context` doesn't - support. - - .. versionchanged:: 3.11 - Raises either :exc:`TypeError` or :exc:`ValueError` if *kwargs* supplies an invalid value for an attribute. + :meth:`localcontext` now supports setting context attributes through the use of keyword arguments. New contexts can also be created using the :class:`Context` constructor described below. In addition, the module provides three pre-made contexts: From 88862ac55553f0eb55e0103f815424670ace604b Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Mon, 4 Apr 2022 21:53:39 +0100 Subject: [PATCH 16/19] Update decimal.rst --- Doc/library/decimal.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index df324daa0c46e4..2ad84f20b5560f 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -950,7 +950,7 @@ function to temporarily change the active context. with localcontext(prec=42) as ctx: s = calculate_something() s = +s - + Raises :exc:`TypeError` if *kwargs* supplies an attribute that :class:`Context` doesn't support. Raises either :exc:`TypeError` or :exc:`ValueError` if *kwargs* supplies an invalid value for an attribute. From 21339f148c0321ea0d9480d395bfe1f77cbce4e9 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 13 Apr 2022 18:34:56 -0700 Subject: [PATCH 17/19] Update Modules/_decimal/_decimal.c --- Modules/_decimal/_decimal.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 7ac8f0a724e0c1..288dc0841322c2 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -1752,7 +1752,6 @@ ctxmanager_new(PyTypeObject *type UNUSED, PyObject *args, PyObject *kwds) PyObject *flags = Py_None; PyObject *traps = Py_None; - CURRENT_CONTEXT(global); if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOOOOOOO", kwlist, &local, &prec, &rounding, &Emin, &Emax, &capitals, &clamp, &flags, &traps)) { From df8e7196e36df93070d4f0168c805b4d3e3983b6 Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Thu, 14 Apr 2022 09:32:00 +0100 Subject: [PATCH 18/19] Don't overwrite existing context in _pydecimal --- Lib/_pydecimal.py | 12 +++++++----- Lib/test/test_decimal.py | 6 ++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index 0157ff74d38cee..56ac795a150fc1 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -506,11 +506,13 @@ def sin(x): """ if ctx is None: ctx = getcontext() - for key, value in kwargs.items(): - if key not in _context_attributes: - raise TypeError(f"'{key}' is an invalid keyword argument for this function") - setattr(ctx, key, value) - return _ContextManager(ctx) + ctx_manager = _ContextManager(ctx) + with ctx_manager as ctx: + for key, value in kwargs.items(): + if key not in _context_attributes: + raise TypeError(f"'{key}' is an invalid keyword argument for this function") + setattr(ctx, key, value) + return ctx_manager ##### Decimal class ####################################################### diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 9c6e5960bbe22b..96f8f7f32c4548 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -3693,6 +3693,12 @@ def test_localcontext_kwargs(self): self.assertRaises(TypeError, self.decimal.localcontext, Emin="") self.assertRaises(TypeError, self.decimal.localcontext, Emax="") + def test_local_context_kwargs_does_not_overwrite_existing_argument(self): + ctx = self.decimal.getcontext() + ctx.prec = 28 + with self.decimal.localcontext(prec=10) as ctx2: + self.assertEqual(ctx.prec, 28) + def test_nested_with_statements(self): # Use a copy of the supplied context in the block Decimal = self.decimal.Decimal From ee34ca77bd32a9231723dabc65d3af621bcdc7f7 Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Thu, 14 Apr 2022 16:21:59 +0100 Subject: [PATCH 19/19] Set attributes through ContextManage.new_context --- Lib/_pydecimal.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index 56ac795a150fc1..f9d6c9901f1f31 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -507,11 +507,10 @@ def sin(x): if ctx is None: ctx = getcontext() ctx_manager = _ContextManager(ctx) - with ctx_manager as ctx: - for key, value in kwargs.items(): - if key not in _context_attributes: - raise TypeError(f"'{key}' is an invalid keyword argument for this function") - setattr(ctx, key, value) + for key, value in kwargs.items(): + if key not in _context_attributes: + raise TypeError(f"'{key}' is an invalid keyword argument for this function") + setattr(ctx_manager.new_context, key, value) return ctx_manager