Skip to content

gh-78857: Always return a 2-tuple from divmod() and PyNumber_Divmod(). #9301

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

serhiy-storchaka
Copy link
Member

@serhiy-storchaka serhiy-storchaka commented Sep 14, 2018

@@ -0,0 +1,2 @@
It is now guarantied that :func:`divmod` and :c:func`PyNumber_Divmod` always
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/guarantied/guaranteed/

@pganssle
Copy link
Member

@serhiy-storchaka Seems to be broken because magic mocks just return a magic mock. That could possibly be considered a bug in unittest.mock.MagicMock.

@skrah skrah removed their request for review September 17, 2018 20:17
}
if (!PyTuple_Check(result)) {
orig_result = result;
result = PySequence_Tuple(result);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could Py_DECREF(orig_result) right here, and then you could eliminate the 3 Py_(X)DECREF(orig_result)s below

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then there will be an undefined behavior in orig_result != NULL.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed the connection between Py_DECREF and free here, good point.

Setting a single boolean in this branch would eliminate the need for the orig_result != NULL, and would arguably be clearer - but I suppose that's bike-shedding

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I now remember why I used this way. Keeping the reference to orig_result allows to use it in the error/warning message. The current version does not use it, but I was going to make a separate PR for more detailed error messages for all operations.

self.assertEqual(divmod([], fake_rdivmod([2, 3])), (2, 3))
self.assertRaises(TypeError, divmod, [], fake_rdivmod(42))
self.assertRaises(TypeError, divmod, [], fake_rdivmod(()))
self.assertRaises(TypeError, divmod, [], fake_rdivmod((1, 2, 3)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be good to add a namedtuple example here, to state explicitly that tuple subclasses are intended to be supported.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hesitate. Should we allow tuple subclasses or require exact tuples? Most converting special methods (__int__, __index__, __float__, __complex__) are required to return an instance of exact type. From C side, if use the C API for unpacking the tuple, there is no difference between tuples and tuple subclasses. But there mey be difference at Python level if the tuple subclass overrides some methods like __add__, __iter__, __len__, __getitem__.

For now, I prefer to not promise the namedtuple support.

@@ -1836,7 +1813,7 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type)
PyObject *num = NULL;
PyObject *result = NULL;

tuple = checked_divmod(pyus, us_per_second);
tuple = PyNumber_Divmod(pyus, us_per_second);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't looked closely at any of the other stuff, but +1 on the _datetimemodule.c stuff. It is very nice that it is now possible to make this simplification.

Co-authored-by: Eric Wieser <wieser.eric@gmail.com>
@rhettinger
Copy link
Contributor

I don't think this should be applied. There isn't a known user issue being solved. Also, this changes a very old API so it might break some deployed code. Also, IIRC it created some issue with magic mocks.

@serhiy-storchaka
Copy link
Member Author

The problem is that virtually any use of PyNumber_Divmod() requires a boilerplate code to check that it is a tuple and its size is 2. User code often omit that check.

Examples:
https://github.com/lycantropos/symba/blob/e5be13c43d9da2c0920ccb351b934ae2953f92d4/src/symba.c
https://github.com/tmroeder/pyScore/blob/b460b5912048d22d83c0375d1b0e248f6883a8c2/src/crat/cratmodule.c

There were similar issues in Python core code, until they were fixed.

Alternatively we can provide a new public API for calling PyNumber_Divmod() and unpacking the result. It would not help the old code which uses PyNumber_Divmod() followed by PyTuple_GET_ITEM(), but it may make writing the new correct code less painful.

As for mocks, I think that it does not create some issue with magic mocks, but exposes some existing issues with magic mocks.

@rhettinger
Copy link
Contributor

-0

I would just let it be. ISTM that API restrictions are almost always more hassle than they are worth.

@rhettinger rhettinger removed their request for review May 3, 2022 06:29
Copy link
Member

@iritkatriel iritkatriel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has merge conflicts now.

@@ -764,6 +764,11 @@ Changes in the Python API
environment variable when the :option:`-E` or :option:`-I` command line
options are being used.

* :func:`divmod` and :c:func:`PyNumber_Divmod` will check the result of special
methods ``__divmod__`` and ``__rdivmod__`` and emit a deprecation warning if
it is not a tuple or raise an error if it's size is not 2.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's size --> its size

@bedevere-bot
Copy link

When you're done making the requested changes, leave the comment: I have made the requested changes; please review again.

Copy link

This PR is stale because it has been open for 30 days with no activity.

@github-actions github-actions bot added the stale Stale PR or inactive for long period of time. label Dec 15, 2024
@picnixz picnixz changed the title bpo-34676: Always return a 2-tuple from divmod() and PyNumber_Divmod(). gh-78857: Always return a 2-tuple from divmod() and PyNumber_Divmod(). Dec 15, 2024
@github-actions github-actions bot removed the stale Stale PR or inactive for long period of time. label Feb 27, 2025
Copy link

This PR is stale because it has been open for 30 days with no activity.

@github-actions github-actions bot added the stale Stale PR or inactive for long period of time. label Apr 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting changes stale Stale PR or inactive for long period of time. type-feature A feature request or enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants