Issue21041
Created on 2014-03-23 22:16 by akira, last changed 2022-04-11 14:58 by admin. This issue is now closed.
| Files | ||||
|---|---|---|---|---|
| File name | Uploaded | Description | Edit | |
| pathlib-parents-allow-negative-index.patch | akira, 2014-03-23 22:16 | the fix and tests | review | |
| allowNegativeIndexParents.patch | victorg, 2020-03-04 08:13 | the fix w/o tests | ||
| Pull Requests | |||
|---|---|---|---|
| URL | Status | Linked | Edit |
| PR 21799 | merged | ypank, 2020-08-10 07:25 | |
| Messages (26) | |||
|---|---|---|---|
| msg214642 - (view) | Author: Akira Li (akira) * | Date: 2014-03-23 22:16 | |
`pathlib.PurePath.parents` is a sequence [1] but it rejects negative indexes:
>>> from pathlib import PurePath
>>> PurePath('a/b/c').parents[-2]
Traceback (most recent call last):
...
IndexError: -2
Sequences in Python interpret negative indexes as `len(seq) + i` [2]
I've included the patch that fixes the issue and adds corresponding tests. No documentation changes are needed.
[1]: http://docs.python.org/3/library/pathlib#pathlib.PurePath.parents
[2]: http://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range
|
|||
| msg214709 - (view) | Author: R. David Murray (r.david.murray) * ![]() |
Date: 2014-03-24 18:55 | |
I think this is a doc bug. That object shouldn't be called a sequence, since it isn't one. |
|||
| msg214716 - (view) | Author: Antoine Pitrou (pitrou) * ![]() |
Date: 2014-03-24 19:50 | |
Well, it is a sequence, it's just that it doesn't respect the convention about negative indices :-) As to why they are disallowed, I don't remember exactly (!) but I think it's because the exact semantics would be confusing otherwise. |
|||
| msg214717 - (view) | Author: R. David Murray (r.david.murray) * ![]() |
Date: 2014-03-24 19:57 | |
Which is exactly what I mean by saying it is not a sequence. It is 'sequence-like'. Kind of like email Messages are dict-like: they share many methods and behaviors, but the exact behaviors and semantics are different. |
|||
| msg215746 - (view) | Author: Akira Li (akira) * | Date: 2014-04-08 09:05 | |
From https://docs.python.org/3/glossary.html#term-sequence > An iterable which supports efficient element access using integer indices via the __getitem__() special method and defines a __len__() method that returns the length of the sequence. .parents *is* a sequence. And it *is* confusing that it doesn't accept negative indexes -- that is how I've encountered the bug. Antoine, could you elaborate on what are the negative consequences of negative indexes to justify breaking the expectations? |
|||
| msg223048 - (view) | Author: Barry A. Warsaw (barry) * ![]() |
Date: 2014-07-14 18:55 | |
Aren't negative indexes well defined in Python? E.g.
>>> p = Path('/tmp/tmp123/foo/bar/baz.xz')
>>> p.parents[len(p.parents)-2]
PosixPath('/tmp')
p.parents[-2] should == p.parents[len(p.parents)-2]
|
|||
| msg223054 - (view) | Author: Akira Li (akira) * | Date: 2014-07-14 20:16 | |
> Aren't negative indexes well defined in Python? yes. I've provided the link to Python docs [1] in msg214642 that explicitly defines the behavior: > If i or j is negative, the index is relative to the end of the string: > len(s) + i or len(s) + j is substituted. But note that -0 is still 0. [1]: https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range |
|||
| msg223059 - (view) | Author: Mark Lawrence (BreamoreBoy) * | Date: 2014-07-14 20:59 | |
#7951 has an interesting debate on negative indexes that is possibly applicable here. |
|||
| msg225503 - (view) | Author: Akira Li (akira) * | Date: 2014-08-18 19:17 | |
> #7951 has an interesting debate on negative indexes that is possibly applicable here. Mark could you point to a message that explains why p.parents[-2] is worse than p.parents[len(p.parents)-2]? |
|||
| msg332008 - (view) | Author: Joshua Cannon (thejcannon) * | Date: 2018-12-17 15:06 | |
I created issue35498 about .parents rejecting slices as well. (It was pointed out this discussion would probably decide that issue's fate) I think that .parents looking like a duck, but not quacking like one isn't very pythonic. Besides, the fact that p.parents[len(p.parents)-2] is allowed but p.parents[-2] is not just seems like extra steps. There's also list(p.parents)[-2], which is still not ideal. In either case, I'd imagine authors to put a comment like "PathLib .parents doesn't support negative indexes", which goes to show clients are expecting negative indices to work. I see that this issue is several years old. I'm happy to shepherd it if it needs further contributions. |
|||
| msg352281 - (view) | Author: Julien Palard (mdk) * ![]() |
Date: 2019-09-13 10:30 | |
I checked conversation in #7951, tells about an ambiguity because it could be an index from a sequence or a key for a dict, like {-1: "foo"}. Here there is no such confusion. Confusion *may* arrise from the fact that it's not composed of parts, but more like it's already sliced, I mean it does NOT look like: ['/', 'home', 'mdk', 'clones', 'python'] It's more like: ['/home/mdk/clones/python', '/home/mdk/clones', '/home/mdk', '/home', '/'] In fact I'd say it behave more like a function call than a sequence access, I read: pathlib.Path.cwd().parents[1] a bit like: pathlib.Path.cwd().parents(go_down=1) It may explain why negative indices or slices were initially not implemented: It already looks like the result of a slice. |
|||
| msg352322 - (view) | Author: Joshua Cannon (thejcannon) * | Date: 2019-09-13 13:26 | |
> it may explain why negative indices or slices were initially not implemented: It already looks like the result of a slice. Sure the values of the sequence could be thought of as being increasingly smaller slices of some other sequence, however I don't think it changes the fact that "parents" is a sequence, and sequences have well-defined semantics for negative indices and slices. Semantics which people expect, and have to find smelly workarounds for. |
|||
| msg363337 - (view) | Author: victorg (victorg) | Date: 2020-03-04 08:13 | |
Allow negative indexes that could be usefull.
Example: to compare 2 or more Path, if they come from the same top directory
from pathlib import Path
a = Path("/a/testpy/cpython/config.log")
b = Path("/b/testpy/cpython/config.log")
c = Path("/a/otherfolder/text.txt")
print(f"a.parents[-2] == b.parents[-2] -> {a.parents[-2] == b.parents[-2]}") # False
print(f"a.parents[-2] == c.parents[-2] -> {a.parents[-2] == c.parents[-2]}") # True
# index = -2 because -1 is "/"
|
|||
| msg363743 - (view) | Author: Julin (ju-sh) * | Date: 2020-03-09 15:54 | |
Can't this be implemented? This is something that a user would expect. Intuitive. And it looks as if it is an easy change to make that doesn't disturb anything else. |
|||
| msg373269 - (view) | Author: Maxwell Ballenger (maxballenger) | Date: 2020-07-08 01:41 | |
Use case: I want to see if a Path is a descendent of /tmp.
if filepath.parents[-2] == Path('tmp'):
turns into
if filepath.parents[len(filepath.parents)-2] == Path('tmp'):
|
|||
| msg373281 - (view) | Author: Serhiy Storchaka (serhiy.storchaka) * ![]() |
Date: 2020-07-08 06:57 | |
Maxwell, in your case a more correct and obvious way is
Path('/tmp') in filepath.parents
although it may be not very efficient. Using the is_relative_to() method may be more efficient and obvious.
|
|||
| msg375099 - (view) | Author: Yaroslav Pankovych (ypank) * | Date: 2020-08-10 07:25 | |
That's kinda weird for python. I mean, in regular list/etc if I need the last element, I'd normally do list[-1], but here to get the last parent, I need to actually know how many parents do I have.
So now, I can do something like this:
>>> parents_count = len(path.parents) - 1
>>> path.parents[parents_count]
PosixPath('.')
|
|||
| msg375100 - (view) | Author: Yaroslav Pankovych (ypank) * | Date: 2020-08-10 07:25 | |
Here's possible fix: https://github.com/python/cpython/pull/21799 |
|||
| msg375788 - (view) | Author: Yaroslav Pankovych (ypank) * | Date: 2020-08-22 08:41 | |
Any thoughts about that folks? It's a pretty old bug, let's decide smth for it. |
|||
| msg381452 - (view) | Author: Paul Ganssle (p-ganssle) * ![]() |
Date: 2020-11-19 18:30 | |
I am not seeing any compelling reasons to avoid supporting negative indexes *or* slices here.
If I had to guess about the confusing semantics of negative indices, I would guess it's the fact that the index in the -1 position for a non-empty Path will always be `Path('.')`. Since that's not terribly useful, it might be reasonable to have negative indices start counting at `len(p)-2`.
That said, I don't think this is a big deal, and I think we have more speculation on why this was avoided in the first place than we have actual objections to changing it, so I vote for changing it.
I think our best option is to say that the semantics of indexing `.parents` should be the same as indexing the result of casting it to a tuple, so this should be true:
p = Path(x)
assert p.parents[y] == tuple(p.parents)[y]
For all values of `x` and `y`.
I've gone ahead and changed the version support matrix to 3.10 only, since I think that this was a deliberate choice and we should be considering this an enhancement rather than a bugfix. That said, I'll admit that it's on the borderline — the semantics of sequences are unambiguous (see, which says that sequences support both slices and negative indices: https://docs.python.org/3/library/stdtypes.html#typesseq ), and PEP 428 explicitly says that .parents returns a "an immutable sequence of the path's logical ancestors": https://www.python.org/dev/peps/pep-0428/#sequence-like-access . So if someone is motivated to try and make the case that this is a bugfix that could be backported to earlier supported versions, I won't stand in their way.
|
|||
| msg381563 - (view) | Author: Yaroslav Pankovych (ypank) * | Date: 2020-11-21 14:10 | |
That makes sense, but should we have this behaviour only for negative indices? We'll end up with something lie: path.parents[len(path.parents) - 1] != path.parents[-1] I think that is should be consistent regardless of negative/positive indices. |
|||
| msg381654 - (view) | Author: Yaroslav Pankovych (ypank) * | Date: 2020-11-23 08:57 | |
And it looks like a special case, so "Special cases aren't special enough to break the rules." |
|||
| msg381670 - (view) | Author: Paul Ganssle (p-ganssle) * ![]() |
Date: 2020-11-23 15:11 | |
I think you may have confused my thoughts as to why this might be considered ambiguous with an actual suggestion for what the semantics should be.
I think that we should stick with `p.parents[x] == tuple(p.parents)[x]` for any valid value of `x`, which means that `p.parents[-1]` will always be `Path('.')` for any non-empty `p`.
|
|||
| msg381678 - (view) | Author: Yaroslav Pankovych (ypank) * | Date: 2020-11-23 16:40 | |
Agree with that, it currently supports this behavior. |
|||
| msg381694 - (view) | Author: Paul Ganssle (p-ganssle) * ![]() |
Date: 2020-11-23 20:06 | |
New changeset 79d2e62c008446fbbc6f264bb8a30e2d38b6ff58 by Yaroslav Pankovych in branch 'master': Added support for negative indexes to PurePath.parents (GH-21799) https://github.com/python/cpython/commit/79d2e62c008446fbbc6f264bb8a30e2d38b6ff58 |
|||
| msg403489 - (view) | Author: Josh Rosenberg (josh.r) * ![]() |
Date: 2021-10-08 18:12 | |
Negative indexing is broken for absolute paths, see #45414. |
|||
| History | |||
|---|---|---|---|
| Date | User | Action | Args |
| 2022-04-11 14:58:00 | admin | set | github: 65240 |
| 2021-10-08 18:12:25 | josh.r | set | nosy:
+ josh.r messages: + msg403489 |
| 2020-11-23 20:06:38 | p-ganssle | set | status: open -> closed resolution: fixed messages: + msg381694 stage: patch review -> resolved |
| 2020-11-23 16:40:58 | ypank | set | messages: + msg381678 |
| 2020-11-23 15:11:05 | p-ganssle | set | messages: + msg381670 |
| 2020-11-23 08:57:53 | ypank | set | messages: + msg381654 |
| 2020-11-21 14:10:23 | ypank | set | messages: + msg381563 |
| 2020-11-20 15:46:16 | p-ganssle | unlink | issue35498 dependencies |
| 2020-11-19 18:30:47 | p-ganssle | set | nosy:
+ p-ganssle messages:
+ msg381452 |
| 2020-08-22 08:41:43 | ypank | set | messages: + msg375788 |
| 2020-08-10 07:25:43 | ypank | set | messages: + msg375100 |
| 2020-08-10 07:25:09 | ypank | set | versions:
+ Python 3.5, Python 3.6, Python 3.7, Python 3.9, Python 3.10 nosy: + ypank messages:
+ msg375099 stage: patch review |
| 2020-08-10 04:13:11 | xtreak | link | issue41511 superseder |
| 2020-07-08 06:57:03 | serhiy.storchaka | set | nosy:
+ serhiy.storchaka messages: + msg373281 |
| 2020-07-08 01:41:55 | maxballenger | set | nosy:
+ maxballenger messages: + msg373269 |
| 2020-03-09 15:54:43 | ju-sh | set | nosy:
+ ju-sh messages: + msg363743 |
| 2020-03-04 08:13:16 | victorg | set | files:
+ allowNegativeIndexParents.patch nosy: + victorg messages: + msg363337 |
| 2019-09-13 13:26:18 | thejcannon | set | messages: + msg352322 |
| 2019-09-13 10:30:40 | mdk | set | nosy:
+ mdk messages: + msg352281 |
| 2018-12-17 15:06:05 | thejcannon | set | nosy:
+ thejcannon messages: + msg332008 |
| 2018-12-16 16:35:30 | BreamoreBoy | set | nosy:
- BreamoreBoy |
| 2018-12-16 11:27:24 | serhiy.storchaka | set | type: behavior -> enhancement versions: + Python 3.8, - Python 3.4, Python 3.5 |
| 2018-12-16 11:27:04 | serhiy.storchaka | link | issue35498 dependencies |
| 2014-08-18 19:17:12 | akira | set | messages: + msg225503 |
| 2014-07-14 20:59:14 | BreamoreBoy | set | nosy:
+ BreamoreBoy messages: + msg223059 |
| 2014-07-14 20:16:46 | akira | set | messages: + msg223054 |
| 2014-07-14 18:55:58 | barry | set | messages: + msg223048 |
| 2014-07-14 18:53:23 | barry | set | nosy:
+ barry |
| 2014-04-08 09:05:31 | akira | set | messages: + msg215746 |
| 2014-03-24 19:57:38 | r.david.murray | set | messages: + msg214717 |
| 2014-03-24 19:50:16 | pitrou | set | messages: + msg214716 |
| 2014-03-24 18:55:41 | r.david.murray | set | messages: + msg214709 |
| 2014-03-24 18:53:35 | r.david.murray | set | nosy:
+ pitrou, r.david.murray |
| 2014-03-24 18:50:12 | akira | set | type: behavior |
| 2014-03-23 22:16:51 | akira | create | |

