-
-
Notifications
You must be signed in to change notification settings - Fork 30.1k
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
pathlib.PurePath.parents rejects negative indexes #65240
Comments
>>> from pathlib import PurePath
>>> PurePath('a/b/c').parents[-2]
Traceback (most recent call last):
...
IndexError: -2 Sequences in Python interpret negative indexes as I've included the patch that fixes the issue and adds corresponding tests. No documentation changes are needed. |
I think this is a doc bug. That object shouldn't be called a sequence, since it isn't one. |
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. |
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. |
From https://docs.python.org/3/glossary.html#term-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? |
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] |
bpo-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 |
I created bpo-35498 about .parents rejecting slices as well. (It was pointed out this discussion would probably decide that issue's fate) 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. |
I checked conversation in bpo-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. |
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. |
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 "/" |
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. |
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'): |
Maxwell, in your case a more correct and obvious way is
although it may be not very efficient. Using the is_relative_to() method may be more efficient and obvious. |
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('.') |
Here's possible fix: #21799 |
Any thoughts about that folks? It's a pretty old bug, let's decide smth for it. |
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 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 p = Path(x)
assert p.parents[y] == tuple(p.parents)[y] For all values of 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. |
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. |
And it looks like a special case, so "Special cases aren't special enough to break the rules." |
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 |
Agree with that, it currently supports this behavior. |
Negative indexing is broken for absolute paths, see bpo-45414. |
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:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: