From a2fa25b78c9609f618e1a166a8d1a4581bce10d7 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 14 Oct 2025 14:02:02 +0300 Subject: [PATCH 1/2] [3.14] gh-102431: Clarify constraints on operands of Decimal logical operations (GH-102836) Sync C/Python implementation of the decimal: logical_ops for contexts. (cherry picked from commit 6ecf77dbdec7838e9ce2298cb8d16e8c2250da81) Co-authored-by: Sergey B Kirpichev --- Lib/_pydecimal.py | 20 ++++- ...-03-21-10-59-40.gh-issue-102431.eUDnf4.rst | 2 + Modules/_decimal/docstrings.h | 88 +++++++++++++++++-- 3 files changed, 98 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-03-21-10-59-40.gh-issue-102431.eUDnf4.rst diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index 9b8e42a2342536..97a629fe92ccec 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -3340,7 +3340,10 @@ def _fill_logical(self, context, opa, opb): return opa, opb def logical_and(self, other, context=None): - """Applies an 'and' operation between self and other's digits.""" + """Applies an 'and' operation between self and other's digits. + + Both self and other must be logical numbers. + """ if context is None: context = getcontext() @@ -3357,14 +3360,20 @@ def logical_and(self, other, context=None): return _dec_from_triple(0, result.lstrip('0') or '0', 0) def logical_invert(self, context=None): - """Invert all its digits.""" + """Invert all its digits. + + The self must be logical number. + """ if context is None: context = getcontext() return self.logical_xor(_dec_from_triple(0,'1'*context.prec,0), context) def logical_or(self, other, context=None): - """Applies an 'or' operation between self and other's digits.""" + """Applies an 'or' operation between self and other's digits. + + Both self and other must be logical numbers. + """ if context is None: context = getcontext() @@ -3381,7 +3390,10 @@ def logical_or(self, other, context=None): return _dec_from_triple(0, result.lstrip('0') or '0', 0) def logical_xor(self, other, context=None): - """Applies an 'xor' operation between self and other's digits.""" + """Applies an 'xor' operation between self and other's digits. + + Both self and other must be logical numbers. + """ if context is None: context = getcontext() diff --git a/Misc/NEWS.d/next/Library/2023-03-21-10-59-40.gh-issue-102431.eUDnf4.rst b/Misc/NEWS.d/next/Library/2023-03-21-10-59-40.gh-issue-102431.eUDnf4.rst new file mode 100644 index 00000000000000..e82ddb6e1011ad --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-03-21-10-59-40.gh-issue-102431.eUDnf4.rst @@ -0,0 +1,2 @@ +Clarify constraints for "logical" arguments in methods of +:class:`decimal.Context`. diff --git a/Modules/_decimal/docstrings.h b/Modules/_decimal/docstrings.h index 77017a92252cb8..231b7c33af9241 100644 --- a/Modules/_decimal/docstrings.h +++ b/Modules/_decimal/docstrings.h @@ -292,22 +292,26 @@ an infinity then Decimal('Infinity') is returned.\n\ PyDoc_STRVAR(doc_logical_and, "logical_and($self, /, other, context=None)\n--\n\n\ -Return the digit-wise 'and' of the two (logical) operands.\n\ +Applies an 'and' operation between self and other's digits.\n\n\ +Both self and other must be logical numbers.\n\ \n"); PyDoc_STRVAR(doc_logical_invert, "logical_invert($self, /, context=None)\n--\n\n\ -Return the digit-wise inversion of the (logical) operand.\n\ +Invert all its digits.\n\n\ +The self must be logical number.\n\ \n"); PyDoc_STRVAR(doc_logical_or, "logical_or($self, /, other, context=None)\n--\n\n\ -Return the digit-wise 'or' of the two (logical) operands.\n\ +Applies an 'or' operation between self and other's digits.\n\n\ +Both self and other must be logical numbers. \n\ \n"); PyDoc_STRVAR(doc_logical_xor, "logical_xor($self, /, other, context=None)\n--\n\n\ -Return the digit-wise 'exclusive or' of the two (logical) operands.\n\ +Applies an 'xor' operation between self and other's digits.\n\n\ +Both self and other must be logical numbers.\n\ \n"); PyDoc_STRVAR(doc_max, @@ -712,22 +716,90 @@ Return the exponent of the magnitude of the operand's MSD.\n\ PyDoc_STRVAR(doc_ctx_logical_and, "logical_and($self, x, y, /)\n--\n\n\ -Digit-wise and of x and y.\n\ +Applies the logical operation 'and' between each operand's digits.\n\n\ +The operands must be both logical numbers.\n\n\ + >>> ExtendedContext.logical_and(Decimal('0'), Decimal('0')\n\ + Decimal('0'\n\ + >>> ExtendedContext.logical_and(Decimal('0'), Decimal('1')\n\ + Decimal('0'\n\ + >>> ExtendedContext.logical_and(Decimal('1'), Decimal('0')\n\ + Decimal('0'\n\ + >>> ExtendedContext.logical_and(Decimal('1'), Decimal('1')\n\ + Decimal('1'\n\ + >>> ExtendedContext.logical_and(Decimal('1100'), Decimal('1010')\n\ + Decimal('1000'\n\ + >>> ExtendedContext.logical_and(Decimal('1111'), Decimal('10')\n\ + Decimal('10'\n\ + >>> ExtendedContext.logical_and(110, 1101\n\ + Decimal('100'\n\ + >>> ExtendedContext.logical_and(Decimal(110), 1101\n\ + Decimal('100'\n\ + >>> ExtendedContext.logical_and(110, Decimal(1101)\n\ + Decimal('100'\n\ \n"); PyDoc_STRVAR(doc_ctx_logical_invert, "logical_invert($self, x, /)\n--\n\n\ -Invert all digits of x.\n\ +Invert all the digits in the operand.\n\n\ +The operand must be a logical number.\n\n\ + >>> ExtendedContext.logical_invert(Decimal('0')\n\ + Decimal('111111111'\n\ + >>> ExtendedContext.logical_invert(Decimal('1')\n\ + Decimal('111111110'\n\ + >>> ExtendedContext.logical_invert(Decimal('111111111')\n\ + Decimal('0'\n\ + >>> ExtendedContext.logical_invert(Decimal('101010101')\n\ + Decimal('10101010'\n\ + >>> ExtendedContext.logical_invert(1101\n\ + Decimal('111110010'\n\ \n"); PyDoc_STRVAR(doc_ctx_logical_or, "logical_or($self, x, y, /)\n--\n\n\ -Digit-wise or of x and y.\n\ +Applies the logical operation 'or' between each operand's digits.\n\n\ +The operands must be both logical numbers.\n\n\ + >>> ExtendedContext.logical_or(Decimal('0'), Decimal('0')\n\ + Decimal('0'\n\ + >>> ExtendedContext.logical_or(Decimal('0'), Decimal('1')\n\ + Decimal('1'\n\ + >>> ExtendedContext.logical_or(Decimal('1'), Decimal('0')\n\ + Decimal('1'\n\ + >>> ExtendedContext.logical_or(Decimal('1'), Decimal('1')\n\ + Decimal('1'\n\ + >>> ExtendedContext.logical_or(Decimal('1100'), Decimal('1010')\n\ + Decimal('1110'\n\ + >>> ExtendedContext.logical_or(Decimal('1110'), Decimal('10')\n\ + Decimal('1110'\n\ + >>> ExtendedContext.logical_or(110, 1101\n\ + Decimal('1111'\n\ + >>> ExtendedContext.logical_or(Decimal(110), 1101\n\ + Decimal('1111'\n\ + >>> ExtendedContext.logical_or(110, Decimal(1101)\n\ + Decimal('1111'\n\ \n"); PyDoc_STRVAR(doc_ctx_logical_xor, "logical_xor($self, x, y, /)\n--\n\n\ -Digit-wise xor of x and y.\n\ +Applies the logical operation 'xor' between each operand's digits.\n\n\ +The operands must be both logical numbers.\n\n\ + >>> ExtendedContext.logical_xor(Decimal('0'), Decimal('0')\n\ + Decimal('0'\n\ + >>> ExtendedContext.logical_xor(Decimal('0'), Decimal('1')\n\ + Decimal('1'\n\ + >>> ExtendedContext.logical_xor(Decimal('1'), Decimal('0')\n\ + Decimal('1'\n\ + >>> ExtendedContext.logical_xor(Decimal('1'), Decimal('1')\n\ + Decimal('0'\n\ + >>> ExtendedContext.logical_xor(Decimal('1100'), Decimal('1010')\n\ + Decimal('110'\n\ + >>> ExtendedContext.logical_xor(Decimal('1111'), Decimal('10')\n\ + Decimal('1101'\n\ + >>> ExtendedContext.logical_xor(110, 1101\n\ + Decimal('1011'\n\ + >>> ExtendedContext.logical_xor(Decimal(110), 1101\n\ + Decimal('1011'\n\ + >>> ExtendedContext.logical_xor(110, Decimal(1101)\n\ + Decimal('1011'\n\ \n"); PyDoc_STRVAR(doc_ctx_max, From a1f252c1e47d6e9758153da8bd5ee97794db2b3e Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 14 Oct 2025 16:12:52 +0300 Subject: [PATCH 2/2] correct vim replacement --- Modules/_decimal/docstrings.h | 128 +++++++++++++++++----------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/Modules/_decimal/docstrings.h b/Modules/_decimal/docstrings.h index 231b7c33af9241..9c2da5e35f919a 100644 --- a/Modules/_decimal/docstrings.h +++ b/Modules/_decimal/docstrings.h @@ -718,88 +718,88 @@ PyDoc_STRVAR(doc_ctx_logical_and, "logical_and($self, x, y, /)\n--\n\n\ Applies the logical operation 'and' between each operand's digits.\n\n\ The operands must be both logical numbers.\n\n\ - >>> ExtendedContext.logical_and(Decimal('0'), Decimal('0')\n\ - Decimal('0'\n\ - >>> ExtendedContext.logical_and(Decimal('0'), Decimal('1')\n\ - Decimal('0'\n\ - >>> ExtendedContext.logical_and(Decimal('1'), Decimal('0')\n\ - Decimal('0'\n\ - >>> ExtendedContext.logical_and(Decimal('1'), Decimal('1')\n\ - Decimal('1'\n\ - >>> ExtendedContext.logical_and(Decimal('1100'), Decimal('1010')\n\ - Decimal('1000'\n\ - >>> ExtendedContext.logical_and(Decimal('1111'), Decimal('10')\n\ - Decimal('10'\n\ - >>> ExtendedContext.logical_and(110, 1101\n\ - Decimal('100'\n\ - >>> ExtendedContext.logical_and(Decimal(110), 1101\n\ - Decimal('100'\n\ - >>> ExtendedContext.logical_and(110, Decimal(1101)\n\ - Decimal('100'\n\ + >>> ExtendedContext.logical_and(Decimal('0'), Decimal('0'))\n\ + Decimal('0')\n\ + >>> ExtendedContext.logical_and(Decimal('0'), Decimal('1'))\n\ + Decimal('0')\n\ + >>> ExtendedContext.logical_and(Decimal('1'), Decimal('0'))\n\ + Decimal('0')\n\ + >>> ExtendedContext.logical_and(Decimal('1'), Decimal('1'))\n\ + Decimal('1')\n\ + >>> ExtendedContext.logical_and(Decimal('1100'), Decimal('1010'))\n\ + Decimal('1000')\n\ + >>> ExtendedContext.logical_and(Decimal('1111'), Decimal('10'))\n\ + Decimal('10')\n\ + >>> ExtendedContext.logical_and(110, 1101)\n\ + Decimal('100')\n\ + >>> ExtendedContext.logical_and(Decimal(110), 1101)\n\ + Decimal('100')\n\ + >>> ExtendedContext.logical_and(110, Decimal(1101))\n\ + Decimal('100')\n\ \n"); PyDoc_STRVAR(doc_ctx_logical_invert, "logical_invert($self, x, /)\n--\n\n\ Invert all the digits in the operand.\n\n\ The operand must be a logical number.\n\n\ - >>> ExtendedContext.logical_invert(Decimal('0')\n\ - Decimal('111111111'\n\ - >>> ExtendedContext.logical_invert(Decimal('1')\n\ - Decimal('111111110'\n\ - >>> ExtendedContext.logical_invert(Decimal('111111111')\n\ - Decimal('0'\n\ - >>> ExtendedContext.logical_invert(Decimal('101010101')\n\ - Decimal('10101010'\n\ - >>> ExtendedContext.logical_invert(1101\n\ - Decimal('111110010'\n\ + >>> ExtendedContext.logical_invert(Decimal('0'))\n\ + Decimal('111111111')\n\ + >>> ExtendedContext.logical_invert(Decimal('1'))\n\ + Decimal('111111110')\n\ + >>> ExtendedContext.logical_invert(Decimal('111111111'))\n\ + Decimal('0')\n\ + >>> ExtendedContext.logical_invert(Decimal('101010101'))\n\ + Decimal('10101010')\n\ + >>> ExtendedContext.logical_invert(1101)\n\ + Decimal('111110010')\n\ \n"); PyDoc_STRVAR(doc_ctx_logical_or, "logical_or($self, x, y, /)\n--\n\n\ Applies the logical operation 'or' between each operand's digits.\n\n\ The operands must be both logical numbers.\n\n\ - >>> ExtendedContext.logical_or(Decimal('0'), Decimal('0')\n\ - Decimal('0'\n\ - >>> ExtendedContext.logical_or(Decimal('0'), Decimal('1')\n\ - Decimal('1'\n\ - >>> ExtendedContext.logical_or(Decimal('1'), Decimal('0')\n\ - Decimal('1'\n\ - >>> ExtendedContext.logical_or(Decimal('1'), Decimal('1')\n\ - Decimal('1'\n\ - >>> ExtendedContext.logical_or(Decimal('1100'), Decimal('1010')\n\ - Decimal('1110'\n\ - >>> ExtendedContext.logical_or(Decimal('1110'), Decimal('10')\n\ - Decimal('1110'\n\ - >>> ExtendedContext.logical_or(110, 1101\n\ - Decimal('1111'\n\ - >>> ExtendedContext.logical_or(Decimal(110), 1101\n\ - Decimal('1111'\n\ - >>> ExtendedContext.logical_or(110, Decimal(1101)\n\ - Decimal('1111'\n\ + >>> ExtendedContext.logical_or(Decimal('0'), Decimal('0'))\n\ + Decimal('0')\n\ + >>> ExtendedContext.logical_or(Decimal('0'), Decimal('1'))\n\ + Decimal('1')\n\ + >>> ExtendedContext.logical_or(Decimal('1'), Decimal('0'))\n\ + Decimal('1')\n\ + >>> ExtendedContext.logical_or(Decimal('1'), Decimal('1'))\n\ + Decimal('1')\n\ + >>> ExtendedContext.logical_or(Decimal('1100'), Decimal('1010'))\n\ + Decimal('1110')\n\ + >>> ExtendedContext.logical_or(Decimal('1110'), Decimal('10'))\n\ + Decimal('1110')\n\ + >>> ExtendedContext.logical_or(110, 1101)\n\ + Decimal('1111')\n\ + >>> ExtendedContext.logical_or(Decimal(110), 1101)\n\ + Decimal('1111')\n\ + >>> ExtendedContext.logical_or(110, Decimal(1101))\n\ + Decimal('1111')\n\ \n"); PyDoc_STRVAR(doc_ctx_logical_xor, "logical_xor($self, x, y, /)\n--\n\n\ Applies the logical operation 'xor' between each operand's digits.\n\n\ The operands must be both logical numbers.\n\n\ - >>> ExtendedContext.logical_xor(Decimal('0'), Decimal('0')\n\ - Decimal('0'\n\ - >>> ExtendedContext.logical_xor(Decimal('0'), Decimal('1')\n\ - Decimal('1'\n\ - >>> ExtendedContext.logical_xor(Decimal('1'), Decimal('0')\n\ - Decimal('1'\n\ - >>> ExtendedContext.logical_xor(Decimal('1'), Decimal('1')\n\ - Decimal('0'\n\ - >>> ExtendedContext.logical_xor(Decimal('1100'), Decimal('1010')\n\ - Decimal('110'\n\ - >>> ExtendedContext.logical_xor(Decimal('1111'), Decimal('10')\n\ - Decimal('1101'\n\ - >>> ExtendedContext.logical_xor(110, 1101\n\ - Decimal('1011'\n\ - >>> ExtendedContext.logical_xor(Decimal(110), 1101\n\ - Decimal('1011'\n\ - >>> ExtendedContext.logical_xor(110, Decimal(1101)\n\ - Decimal('1011'\n\ + >>> ExtendedContext.logical_xor(Decimal('0'), Decimal('0'))\n\ + Decimal('0')\n\ + >>> ExtendedContext.logical_xor(Decimal('0'), Decimal('1'))\n\ + Decimal('1')\n\ + >>> ExtendedContext.logical_xor(Decimal('1'), Decimal('0'))\n\ + Decimal('1')\n\ + >>> ExtendedContext.logical_xor(Decimal('1'), Decimal('1'))\n\ + Decimal('0')\n\ + >>> ExtendedContext.logical_xor(Decimal('1100'), Decimal('1010'))\n\ + Decimal('110')\n\ + >>> ExtendedContext.logical_xor(Decimal('1111'), Decimal('10'))\n\ + Decimal('1101')\n\ + >>> ExtendedContext.logical_xor(110, 1101)\n\ + Decimal('1011')\n\ + >>> ExtendedContext.logical_xor(Decimal(110), 1101)\n\ + Decimal('1011')\n\ + >>> ExtendedContext.logical_xor(110, Decimal(1101))\n\ + Decimal('1011')\n\ \n"); PyDoc_STRVAR(doc_ctx_max,