This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Allow decimal.localcontext to accept keyword arguments to set context attributes
Type: Stage: patch review
Components: Library (Lib) Versions: Python 3.11
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: corona10, ncoghlan, sam_ezeh, steven.daprano
Priority: normal Keywords: patch

Created on 2022-03-27 10:16 by steven.daprano, last changed 2022-04-11 14:59 by admin.

Files
File name Uploaded Description Edit
sam_ezeh.patch sam_ezeh, 2022-04-01 19:30
Pull Requests
URL Status Linked Edit
PR 32242 open sam_ezeh, 2022-04-01 20:49
Messages (6)
msg416114 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2022-03-27 10:16
Whenever I use decimal and need to change the context temporarily, without fail I try to write

with decimal.localcontext(prec=10):
    ...

or similar. Then I get surprised that it fails, and re-write it as

with decimal.localcontext() as ctx:
    ctx.prec = 10
    ...

Let's make the first version work. localcontext should accept keyword arguments corresponding to the same arguments accepted by Context, and set them on the context given.

A proof-of-concept wrapper function:

def localcontext(ctx=None, **kwargs):
    if ctx is None:
        ctx = decimal.getcontext().copy()
    for key, value in kwargs.items():
        setattr(ctx, key, value)
    return decimal.localcontext(ctx)

I think this would be a valuable, and useful, improvement to the decimal API.
msg416364 - (view) Author: Alyssa Coghlan (ncoghlan) * (Python committer) Date: 2022-03-30 14:36
Seems reasonable to me, but I think a full implementation would want to throw an error for keyword args that don't already exist as context attributes (otherwise typos would fail silently)
msg416507 - (view) Author: Sam Ezeh (sam_ezeh) * Date: 2022-04-01 18:59
I'm looking into adding this

> Seems reasonable to me, but I think a full implementation would want to throw an error for keyword args that don't already exist as context attributes (otherwise typos would fail silently)

For _pydecimal, I think this would automatically happen automatically as Context.__setattr__ raises AttributeError when it's passed a name that isn't a context attribute.

For _decimal this can be done with keyword arguments and `PyArg_ParseTupleAndKeywords`.
msg416511 - (view) Author: Sam Ezeh (sam_ezeh) * Date: 2022-04-01 19:30
I've uploaded a patch and it seems to work, which I'm very proud of.

I'll create some tests, amend documentation and create a news entry. After that, I'll create a pull request on GitHub.
msg416513 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2022-04-01 19:35
I'm not sure what the implementation uses to enforce this, but decimal 
contexts already seem to reject arbitrary attributes. So a naive 
implementation that just setattr()'s the keyword arguments will 
automatically fail:

>>> from decimal import getcontext
>>> ctx = getcontext()
>>> setattr(ctx, 'precision', 10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'decimal.Context' object has no attribute 'precision'

But you are absolutely correct that however we enforce it, we should 
avoid allowing typos to silently fail.
msg416514 - (view) Author: Sam Ezeh (sam_ezeh) * Date: 2022-04-01 19:51
This is what functionality looks like when supplying incorrect attribute names with the patch.

Python 3.11.0a6+ (heads/bpo-47135-dirty:d4bb38f82b, Apr  1 2022, 20:01:56) [GCC 11.1.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import _pydecimal
>>> ctx = _pydecimal.getcontext()
>>> ctx.precision = 10
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/run/media/sam/OS/Git/cpython/Lib/_pydecimal.py", line 3974, in __setattr__
    raise AttributeError(
    ^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'decimal.Context' object has no attribute 'precision'
>>> with _pydecimal.localcontext(precision=10) as ctx:
...     pass
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/run/media/sam/OS/Git/cpython/Lib/_pydecimal.py", line 506, in localcontext
    setattr(ctx, key, value)
    ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/run/media/sam/OS/Git/cpython/Lib/_pydecimal.py", line 3974, in __setattr__
    raise AttributeError(
    ^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'decimal.Context' object has no attribute 'precision'
>>> import decimal
>>> ctx = decimal.getcontext()
>>> ctx.precision = 10
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'decimal.Context' object has no attribute 'precision'
>>> with decimal.localcontext(precision=10) as ctx:
...     pass
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'precision' is an invalid keyword argument for this function
>>>
History
Date User Action Args
2022-04-11 14:59:57adminsetgithub: 91291
2022-04-01 20:49:19sam_ezehsetstage: patch review
pull_requests: + pull_request30313
2022-04-01 19:51:26sam_ezehsetmessages: + msg416514
2022-04-01 19:35:28steven.dapranosetmessages: + msg416513
2022-04-01 19:30:59sam_ezehsetfiles: + sam_ezeh.patch
keywords: + patch
messages: + msg416511
2022-04-01 18:59:49sam_ezehsetnosy: + sam_ezeh
messages: + msg416507
2022-03-30 14:36:28ncoghlansetmessages: + msg416364
2022-03-29 16:04:27corona10setnosy: + corona10
2022-03-29 00:28:46rhettingersetnosy: + ncoghlan
2022-03-27 10:16:04steven.dapranocreate