Skip to content

gh-74453: Deprecate os.path.commonprefix#144436

Merged
hugovk merged 7 commits intopython:mainfrom
sethmlarson:deprecate-os-path-commonprefix
Feb 5, 2026
Merged

gh-74453: Deprecate os.path.commonprefix#144436
hugovk merged 7 commits intopython:mainfrom
sethmlarson:deprecate-os-path-commonprefix

Conversation

@sethmlarson
Copy link
Contributor

@sethmlarson sethmlarson commented Feb 3, 2026

Follow-up from #144401, this PR moves to deprecate os.path.commonprefix() in favor of the correctly behaving os.path.commonpath() and the behavior-describing string.commonprefix().

I'm not sure if this is all I have to do to deprecate an API, if there is more documentation that needs to happen let me know.


📚 Documentation preview 📚: https://cpython-previews--144436.org.readthedocs.build/

Copy link
Member

@picnixz picnixz left a comment

Choose a reason for hiding this comment

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

You need a What's New entry for that.

Comment on lines 61 to 62
import os
m = tuple(map(os.fspath, m))
Copy link
Member

Choose a reason for hiding this comment

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

I'm not really fond of this. string.commonprefix() should be specific to strings and should not care about pathlike objects.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, this is only done for backwards compatibility. I would be happy to get rid of it, but I figured that this would be not allowed considering we're doing a "rename", not a new API.

Copy link
Member

Choose a reason for hiding this comment

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

There is no backwards compatibility here. We create a new function in a module that is not related to filepaths at all. I do not consider this as a rename, I really consider this as a new API. Instead, I would keep the implementation of os.path.commonpath as is and add in a follow-up PR string.commonprefix which does not do the fspath computation. We then document that os.path.commonpath should be reimplemented as string.commonprefix(tuple(map(os.fspath, m))) if needed.

Copy link
Member

Choose a reason for hiding this comment

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

I believe this new string.commonprefix() should be a direct, one-to-one, drop-in replacement for the old os.path.commonprefix().

We're not doing anyone favours by changing the behaviour and making them [have to decide whether they need to] jump through extra hoops.

Chances are they should be using os.path.commonpath() instead, but if not, let them just replace the module name and keep the same old behaviour.

I doubt there's significant performance improvement from changing it either.

Copy link
Member

Choose a reason for hiding this comment

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

For me this isn't about performance, it's about semantics. string contains string-related utilities, not path-related ones. I am really opposed to having string.commonprefix being smart here. I know that we are not necessarily doing the user a favor but I don't want to have unrelated utilities in string. It is, for me, the wrong module for that.

Copy link
Member

@hugovk hugovk left a comment

Choose a reason for hiding this comment

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

Please also list in Doc/deprecations/pending-removal-in-future.rst.

https://docs.python.org/dev/deprecations/index.html#pending-removal-in-future-versions

Comment on lines 61 to 62
import os
m = tuple(map(os.fspath, m))
Copy link
Member

Choose a reason for hiding this comment

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

I believe this new string.commonprefix() should be a direct, one-to-one, drop-in replacement for the old os.path.commonprefix().

We're not doing anyone favours by changing the behaviour and making them [have to decide whether they need to] jump through extra hoops.

Chances are they should be using os.path.commonpath() instead, but if not, let them just replace the module name and keep the same old behaviour.

I doubt there's significant performance improvement from changing it either.

sethmlarson and others added 2 commits February 3, 2026 12:28
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
@sethmlarson
Copy link
Contributor Author

Alright, most recent two commits should address all review comments. I have preserved backwards compatibility in the drop-in replacement string.commonprefix().

@sethmlarson sethmlarson requested review from hugovk and picnixz and removed request for picnixz February 3, 2026 18:33
Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
Copy link
Member

@picnixz picnixz left a comment

Choose a reason for hiding this comment

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

I am sorry, but I do not want string.commonprefix to be equivalent to os.path.commonprefix. Semantically it doesn't make sense to have an os-related utility in string.

In addition, we only talk about "strings" in the documentation. Having an implicit os.fspath conversion should be documented otherwise. If we want to mimic os.path.commonpath in the first place, I do not see why we can't just keep it but soft-deprecate it.

Note that commonprefix does not care about the fact that we are strings. It also works for any sequence, as long as the elements can be ordered (e.g., we can use commonprefix for list of lists):

>>> os.path.commonpath([[1], [1,3]])
[1]

Since we expose it in string, anyone using a non-string as the first argument will not be able to make it work (because we raise a TypeError in os.fspath). So I would expect a string-like object to be acceptable but because of os.fspath it wouldn't be. This would be contrary to what the string module stands for.

If we want a commonprefix utility, I would suggest adding one to itertools, and use the latter. I believe it makes more sense to have something related to commonprefix out there. Or for strings only, have it in difflib.

@bedevere-app
Copy link

bedevere-app bot commented Feb 3, 2026

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

And if you don't make the requested changes, you will be put in the comfy chair!

@sethmlarson
Copy link
Contributor Author

Because I want to prioritize warning users about this function, I am okay with not providing a string.commonprefix() function and simply raising a DeprecationWarning in this PR. I want to avoid this warning getting delayed further, as has happened for the past ~30 years since the confusing behavior has been reported.

sethmlarson and others added 2 commits February 3, 2026 21:56
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
@sethmlarson sethmlarson force-pushed the deprecate-os-path-commonprefix branch from d4c0bb4 to 166c39d Compare February 3, 2026 23:06
@sethmlarson
Copy link
Contributor Author

sethmlarson commented Feb 3, 2026

@picnixz @hugovk I have removed string.commonprefix in 166c39d, I will happily revert or change this if there is a suggestion that should be completed in this PR.

(I have made the requested changes; please review again)

@sethmlarson sethmlarson force-pushed the deprecate-os-path-commonprefix branch from 64e6aac to f58e68f Compare February 4, 2026 15:40
@sethmlarson
Copy link
Contributor Author

I have made the requested changes; please review again

@bedevere-app
Copy link

bedevere-app bot commented Feb 4, 2026

Thanks for making the requested changes!

@hugovk, @picnixz: please review the changes made to this pull request.

@bedevere-app bedevere-app bot requested a review from hugovk February 4, 2026 15:46
Copy link
Member

@vstinner vstinner left a comment

Choose a reason for hiding this comment

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

LGTM.

path_list = path_tail.split(sep) if path_tail else []
# Work out how much of the filepath is shared by start and path.
i = len(commonprefix([start_list, path_list]))
i = len(genericpath._commonprefix([start_list, path_list]))
Copy link
Member

Choose a reason for hiding this comment

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

Ah, this call is safe since it pass two lists of strings, and not two strings.

Copy link
Member

@hugovk hugovk left a comment

Choose a reason for hiding this comment

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

Thanks!

@picnixz
Copy link
Member

picnixz commented Feb 5, 2026

The question still remains about whether to expose string.commonprefix as something working on strings only (not paths), itertools.commonprefix (as working on any sequences) or the new seqtools module (I believe the discussion is stalled).

This would be a natural addition to that module, but that would also be a natural addition to more_itertools or another PyPI package dedicated to sequences utilities. OTOH, it'd still be possible to have it in itertools though supporting arbitrary iterables would make the implementation more complex.

OTOH, difflib could be used as an alternative (I mean, difflib seems to work for any sequence, except when we want to format the change but otherwise the diff algorithm looks generic?)

@vstinner
Copy link
Member

vstinner commented Feb 5, 2026

The question still remains about whether to expose string.commonprefix as something working on strings only (not paths), itertools.commonprefix (as working on any sequences) or the new seqtools module (I believe the discussion is stalled).

I'm not sure that we have to provide a replacement function. os.path.commonpath() should cover most usages.

@hugovk
Copy link
Member

hugovk commented Feb 5, 2026

Please open a new issue or discussion if you'd like a new method. We decided not to provide a replacement here, so it can be considered independently.

@picnixz
Copy link
Member

picnixz commented Feb 5, 2026

I personally don't think it's necessary to provide a pure replacement but I don't know whether Seth wants to

@sethmlarson
Copy link
Contributor Author

sethmlarson commented Feb 5, 2026

I don't feel that we need an exact replacement. string.commonprefix() only operating on strings seems fine, but if it's not an exact replacement then it can happen outside this PR since it's unrelated then.

@hugovk hugovk merged commit 957f9fe into python:main Feb 5, 2026
52 of 55 checks passed
@sethmlarson sethmlarson deleted the deprecate-os-path-commonprefix branch February 5, 2026 20:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants