python / cpython

The Python programming language
https://www.python.org
Other
62.92k stars 30.14k forks source link

datetime.py implementation of .replace inconsistent with C implementation #75405

Closed pganssle closed 6 years ago

pganssle commented 7 years ago
BPO 31222
Nosy @vstinner, @stevendaprano, @bitdancer, @pganssle
PRs
  • python/cpython#4176
  • python/cpython#4356
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields: ```python assignee = None closed_at = created_at = labels = ['interpreter-core', '3.7'] title = 'datetime.py implementation of .replace inconsistent with C implementation' updated_at = user = 'https://github.com/pganssle' ``` bugs.python.org fields: ```python activity = actor = 'vstinner' assignee = 'none' closed = True closed_date = closer = 'vstinner' components = ['Interpreter Core'] creation = creator = 'p-ganssle' dependencies = [] files = [] hgrepos = [] issue_num = 31222 keywords = ['patch'] message_count = 8.0 messages = ['300377', '300378', '300379', '300382', '305240', '305984', '306000', '306017'] nosy_count = 4.0 nosy_names = ['vstinner', 'steven.daprano', 'r.david.murray', 'p-ganssle'] pr_nums = ['4176', '4356'] priority = 'normal' resolution = 'fixed' stage = 'resolved' status = 'closed' superseder = None type = None url = 'https://bugs.python.org/issue31222' versions = ['Python 3.6', 'Python 3.7'] ```

    pganssle commented 7 years ago

    In the .py implementation of datetime.replace (and date.replace and time.replace), the new datetime is created using the datetime type:

    https://github.com/python/cpython/blob/master/Lib/datetime.py#L1578

    But in the C source, it is created from type(self):

    https://github.com/python/cpython/blob/master/Modules/_datetimemodule.c#L5046

    I think the second should be the preferred behavior, so the datetime.py source should be updated to reflect that it's calling self.__class__(...) rather than datetime(...). I can prepare a PR if this would be desirable.

    (That said, I'm not 100% clear under what circumstances the code in datetime.py is actually *used*, so I'm not sure how to write tests for it - is datetime.py essentially documentation, or is there a way to explicitly fall back to it?)

    Per this issue on the pypy3 tracker:

    https://bitbucket.org/pypy/pypy/issues/2635/datetimereplace-always-returns

    stevendaprano commented 7 years ago

    I agree that returning type(self) or self.__class__ (not sure which is better) is the right thing to do.

    It might be possible to argue that the Python version is buggy, if the C version is treated as the reference implementation and the Python version has to duplicate that. Or if the documentation specifies the C version's behaviour.

    But if the precise behaviour is unspecified, or if the Python version is the reference implementation, its best to treat this as an enhancement rather than a bug fix and only apply it to 3.7.

    I'm not sure which is the right approach here.

    bitdancer commented 7 years ago

    See also bpo-20371.

    pganssle commented 7 years ago

    @r.david.murray In the other thread, you mention that the full test suite is run against the C and Python implementations, so that answers the question of how to write the tests.

    I think treating it as an enhancement in Python 3.7 makes a reasonable amount of sense - it's clearly under-specified at the moment and people are probably relying on the CPython behavior (dateutil definitely is in the latest stable release, but not on master). Saying "it's implementation-specific before Python 3.7 but in Python 3.7+, the spec says it should use self(type)" is fine by me. It's not particularly hard to work around if you're subclassing datetime anyway.

    Among the major libraries that provide their own datetime objects:

    I'd say for the most part it's not a major issue to change it even as a bugfix, particularly if the line we're going with is "it was always implementation-specific", but there's also no rush.

    pganssle commented 6 years ago

    Some time ago this was fixed in pypy3:

    https://bitbucket.org/pypy/pypy/issues/2635/datetimereplace-always-returns

    I made a PR fixing this for datetime, date and time.

    vstinner commented 6 years ago

    New changeset 191e993365ac3206f46132dcf46236471ec54bfa by Victor Stinner (Paul Ganssle) in branch 'master': bpo-31222: Make (datetime|date|time).replace return subclass type in Pure Python (bpo-4176) https://github.com/python/cpython/commit/191e993365ac3206f46132dcf46236471ec54bfa

    vstinner commented 6 years ago

    New changeset b9a40aca2935d2569191844c88f8b61269e383cb by Victor Stinner (Miss Islington (bot)) in branch '3.6': bpo-31222: Make (datetime|date|time).replace return subclass type in Pure Python (GH-4176) (bpo-4356) https://github.com/python/cpython/commit/b9a40aca2935d2569191844c88f8b61269e383cb

    vstinner commented 6 years ago

    Thanks Paul Ganssle for the bugfix! I merged your PR and backported it to Python 3.6. (Python 3.5 doesn't accept bugfixes anymore.)