This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Direct sub-classing of pathlib.Path
Type: enhancement Stage: patch review
Components: Library (Lib) Versions: Python 3.11
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: FFY00, Kevin.Norris, Xtrem532, alexia, barneygale, bronger, elguavas, keithy, kfollstad, miss-islington, paul.moore, piotr.dobrogost, pitrou, projetmbc, qb-cea
Priority: normal Keywords: patch

Created on 2015-05-06 05:26 by projetmbc, last changed 2022-04-11 14:58 by admin.

Pull Requests
URL Status Linked Edit
PR 6248 closed qb-cea, 2018-03-26 08:41
PR 25240 merged barneygale, 2021-04-07 01:28
PR 25271 closed barneygale, 2021-04-08 01:24
PR 25701 merged barneygale, 2021-04-28 22:23
PR 26141 closed barneygale, 2021-05-15 11:29
PR 26438 closed kfollstad, 2021-05-28 21:35
PR 26708 closed barneygale, 2021-06-13 16:55
PR 26906 open kfollstad, 2021-06-24 23:04
PR 31085 open barneygale, 2022-02-02 18:00
PR 31691 open barneygale, 2022-03-05 02:18
Messages (28)
msg242643 - (view) Author: Christophe BAL (projetmbc) Date: 2015-05-06 05:26
Hello.

I have noticed a problem with the following code.

from pathlib import Path

class PPath(Path):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

test = PPath("dir", "test.txt")

This gives the following error message.

 
Traceback (most recent call last):
  File "/Users/projetmbc/test.py", line 14, in <module>
    test = PPath("dir", "test.txt")
  File "/anaconda/lib/python3.4/pathlib.py", line 907, in __new__
    self = cls._from_parts(args, init=False)
  File "/anaconda/lib/python3.4/pathlib.py", line 589, in _from_parts
    drv, root, parts = self._parse_args(args)
  File "/anaconda/lib/python3.4/pathlib.py", line 582, in _parse_args
    return cls._flavour.parse_parts(parts)
AttributeError: type object 'PPath' has no attribute '_flavour'

This breaks the sub-classing from Python point of view.

There is an ugly hack to sub-class Path but it's a bit unpythonic.
msg242651 - (view) Author: Paul Moore (paul.moore) * (Python committer) Date: 2015-05-06 09:15
One issue with your code - what would you expect str(test) to produce? "dir/test.txt" or "dir\test.txt"? That's the point of the "flavour" - is it a Windows path or a Unix path?

Agreed that an easier method of creating Path subclasses that handle this type of thing would be useful, but any solution needs to make sure that developers don't overlook the Windows vs Unix implications.

Can you give an actual use case (as opposed to the toy example)?
msg242656 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2015-05-06 11:05
The Path classes were not designed to be subclassable by the user.
I'm not against making subclassing easier, but someone will have to propose a viable approach for that.
msg242657 - (view) Author: Christophe BAL (projetmbc) Date: 2015-05-06 12:13
Hello.

I will give a real example in 5 hours after my job. I will try tomorrow a
solution to ease the subclassing using another dedicazted class PathPlus,
sorry for the name. The idea would be to use this new class for
customization, and also to define WindowsPath and PosixPath sub-classing
this new class. By default PathPlus would be an empty class. I do not know
if this works well. Maybe my idea is a bad one.

*Christophe BAL*
*Enseignant de mathématiques en Lycée **et développeur Python amateur*
*---*
*French math teacher in a "Lycée" **and **Python **amateur developer*

2015-05-06 13:05 GMT+02:00 Antoine Pitrou <report@bugs.python.org>:

>
> Antoine Pitrou added the comment:
>
> The Path classes were not designed to be subclassable by the user.
> I'm not against making subclassing easier, but someone will have to
> propose a viable approach for that.
>
> ----------
> versions: +Python 3.5 -Python 3.4
>
> _______________________________________
> Python tracker <report@bugs.python.org>
> <http://bugs.python.org/issue24132>
> _______________________________________
>
msg242689 - (view) Author: Christophe BAL (projetmbc) Date: 2015-05-06 16:50
Here are for example two extra methods that I have implemented.

def __sub__(cls, path):
    """
This magic method allows to use ``onepath - anotherpath`` instead of the
long
version ``onepath.relative_to(anotherpath)`` given by ``pathlib.Path``.
    """
    return cls.relative_to(path)

def _ppath_common_with(cls, paths):
    """
This method returns the path of the smaller common "folder" of the current
path
and at least one paths.

python::
    from mistool import os_use

    path   = os_use.PPath("/Users/projects/source/doc")
    path_1 = os_use.PPath("/Users/projects/README")
    path_2 = os_use.PPath("/Users/projects/source/misTool/os_use.py")

    print(path.common_with((path_1, path_2)))
    """
    if not isinstance(paths, (list, tuple)):
        paths = [paths]

    commonparts = list(cls.parts)

    for onepath in paths:
        i = 0

        for common, actual in zip(commonparts, onepath.parts):
            if common == actual:
                i += 1
            else:
                break

        commonparts = commonparts[:i]

        if not commonparts:
            break

    commonpath = pathlib.Path("")

    for part in commonparts:
        commonpath /= part

    return commonpath

*Christophe BAL*
*Enseignant de mathématiques en Lycée **et développeur Python amateur*
*---*
*French math teacher in a "Lycée" **and **Python **amateur developer*

2015-05-06 14:13 GMT+02:00 Christophe BAL <report@bugs.python.org>:

>
> Christophe BAL added the comment:
>
> Hello.
>
> I will give a real example in 5 hours after my job. I will try tomorrow a
> solution to ease the subclassing using another dedicazted class PathPlus,
> sorry for the name. The idea would be to use this new class for
> customization, and also to define WindowsPath and PosixPath sub-classing
> this new class. By default PathPlus would be an empty class. I do not know
> if this works well. Maybe my idea is a bad one.
>
> *Christophe BAL*
> *Enseignant de mathématiques en Lycée **et développeur Python amateur*
> *---*
> *French math teacher in a "Lycée" **and **Python **amateur developer*
>
> 2015-05-06 13:05 GMT+02:00 Antoine Pitrou <report@bugs.python.org>:
>
> >
> > Antoine Pitrou added the comment:
> >
> > The Path classes were not designed to be subclassable by the user.
> > I'm not against making subclassing easier, but someone will have to
> > propose a viable approach for that.
> >
> > ----------
> > versions: +Python 3.5 -Python 3.4
> >
> > _______________________________________
> > Python tracker <report@bugs.python.org>
> > <http://bugs.python.org/issue24132>
> > _______________________________________
> >
>
> ----------
>
> _______________________________________
> Python tracker <report@bugs.python.org>
> <http://bugs.python.org/issue24132>
> _______________________________________
>
msg242696 - (view) Author: Paul Moore (paul.moore) * (Python committer) Date: 2015-05-06 18:21
For that type of function, I'd suggest you use a standalone function rather than subclassing and methods or operator overloading. You don't gain enough to be worth the complexity of having to subclass path objects. And duck typing means that your function works for any subclass of (Pure)Path without change.
msg242699 - (view) Author: Christophe BAL (projetmbc) Date: 2015-05-06 18:41
I don't agree with you. I prefer to add new functionalities to the paths I
use. This is the power of OOP. It is easier and cleaner to use
*mypath.common_with(otherpath)*  than  *common_with(**mypath, **other path)*
.

Python is highly OOP, so you can't say *"don't use subclassing in your
case"*. As a user, I should have the possibility to use the method I want.

Another example is the use of  *onepath - anotherpath*  instead of
*onepath.relative_to(**another path)* . That's the power of the magic
method to add this kind of feature.

*Christophe BAL*
*Enseignant de mathématiques en Lycée **et développeur Python amateur*
*---*
*French math teacher in a "Lycée" **and **Python **amateur developer*

2015-05-06 20:21 GMT+02:00 Paul Moore <report@bugs.python.org>:

>
> Paul Moore added the comment:
>
> For that type of function, I'd suggest you use a standalone function
> rather than subclassing and methods or operator overloading. You don't gain
> enough to be worth the complexity of having to subclass path objects. And
> duck typing means that your function works for any subclass of (Pure)Path
> without change.
>
> ----------
>
> _______________________________________
> Python tracker <report@bugs.python.org>
> <http://bugs.python.org/issue24132>
> _______________________________________
>
msg242700 - (view) Author: Paul Moore (paul.moore) * (Python committer) Date: 2015-05-06 19:01
I have no problem with that - it's a style choice certainly.

As I said, I'd like to see simpler subclassing of pathlib objects. I just think it'll be quite hard to do (given the complexities of classes for Windows/Unix as well as pure and concrete paths). So if it's just about examples like this, I personally would take the easier route and just go with standalone functions. If someone else felt strongly enough to design and implement a subclassing solution, that's fine though.
msg242701 - (view) Author: Christophe BAL (projetmbc) Date: 2015-05-06 19:06
Are you the author of path lib ?

*Christophe BAL*
*Enseignant de mathématiques en Lycée **et développeur Python amateur*
*---*
*French math teacher in a "Lycée" **and **Python **amateur developer*

2015-05-06 21:01 GMT+02:00 Paul Moore <report@bugs.python.org>:

>
> Paul Moore added the comment:
>
> I have no problem with that - it's a style choice certainly.
>
> As I said, I'd like to see simpler subclassing of pathlib objects. I just
> think it'll be quite hard to do (given the complexities of classes for
> Windows/Unix as well as pure and concrete paths). So if it's just about
> examples like this, I personally would take the easier route and just go
> with standalone functions. If someone else felt strongly enough to design
> and implement a subclassing solution, that's fine though.
>
> ----------
>
> _______________________________________
> Python tracker <report@bugs.python.org>
> <http://bugs.python.org/issue24132>
> _______________________________________
>
msg242702 - (view) Author: Paul Moore (paul.moore) * (Python committer) Date: 2015-05-06 19:09
> Are you the author of path lib ?

Nope, that's Antoine.
msg242703 - (view) Author: Christophe BAL (projetmbc) Date: 2015-05-06 19:18
OK.
I will try to find a way to achieve an easier and cleaner way to sub class
pathlib.Path and co.

What is the good way to propose a patch ?

*Christophe BAL*
*Enseignant de mathématiques en Lycée **et développeur Python amateur*
*---*
*French math teacher in a "Lycée" **and **Python **amateur developer*

2015-05-06 21:09 GMT+02:00 Paul Moore <report@bugs.python.org>:

>
> Paul Moore added the comment:
>
> > Are you the author of path lib ?
>
> Nope, that's Antoine.
>
> ----------
>
> _______________________________________
> Python tracker <report@bugs.python.org>
> <http://bugs.python.org/issue24132>
> _______________________________________
>
msg242705 - (view) Author: Paul Moore (paul.moore) * (Python committer) Date: 2015-05-06 20:01
> What is the good way to propose a patch ?

If you have a patch, attach it here, and it will get reviewed.
msg246007 - (view) Author: Kevin Norris (Kevin.Norris) Date: 2015-07-01 00:27
If I were designing pathlib from scratch, I would not have a separate Path class.  I would instead do something like this:

In pathlib.py:

    if os.name == 'nt':
        Path = WindowsPath
    else:
        Path = PosixPath

Alternatively, Path() could be a factory function that picks one of those classes at runtime.

Of course, that still leaves the issue of where to put the method implementations which currently live in Path.  We could change the name of Path to _Path and use the code above to continue providing a Path alias, but that might be too confusing.  Another possibility is to pull those methods out into top-level functions and then alias them into methods in WindowsPath and PosixPath (perhaps using a decorator-like-thing to pass the flavor, instead of attaching it to the class).

The main thing, though, is that Path should not depend on its subclasses.  That really strikes me as poor design, since it produces issues like this one.
msg305799 - (view) Author: Stephen M. Gava (elguavas) * Date: 2017-11-08 01:01
Using a set of paths with special properties and formats in a project, thought "the cleanest oop way to do this is try out python's oop paths in pathlib". Subclassed Path to implement my extra (non platfor specific) properties and fell at the first hurdle because of this issue... 

for me pathlib does not provide oop paths if i can't subclass Path, for whatever reason.

reverted to treating paths as strings and writing functions to handle my special path properties and formats.

i was also surprised when i found another bug report on this issue that said it was closed for 3.7, great i thought this has been solved, but no, the other report was closed because it was about the same issue as this ancient report.
msg305811 - (view) Author: Paul Moore (paul.moore) * (Python committer) Date: 2017-11-08 08:55
@elguavas the problem is, no-one has proposed a patch. There's not likely to be much movement on this until someone provides one.
msg305827 - (view) Author: Christophe BAL (projetmbc) Date: 2017-11-08 12:59
For the moment, you can take a look at this little script that acheives 
subclassing of Path : 
https://github.com/bc-python/mistool/blob/master/mistool/os_use.py 
(search for class Path).

Le 08/11/2017 à 09:55, Paul Moore a écrit :
> Paul Moore <p.f.moore@gmail.com> added the comment:
>
> @elguavas the problem is, no-one has proposed a patch. There's not likely to be much movement on this until someone provides one.
>
> ----------
>
> _______________________________________
> Python tracker <report@bugs.python.org>
> <https://bugs.python.org/issue24132>
> _______________________________________
msg305830 - (view) Author: Christophe BAL (projetmbc) Date: 2017-11-08 13:04
Mistyping : /search for class PPath/ with two P.

Le 08/11/2017 à 13:59, Christophe BAL a écrit :
> Christophe BAL <projetmbc@gmail.com> added the comment:
>
> For the moment, you can take a look at this little script that acheives
> subclassing of Path :
> https://github.com/bc-python/mistool/blob/master/mistool/os_use.py
> (search for class Path).
>
> Le 08/11/2017 à 09:55, Paul Moore a écrit :
>> Paul Moore <p.f.moore@gmail.com> added the comment:
>>
>> @elguavas the problem is, no-one has proposed a patch. There's not likely to be much movement on this until someone provides one.
>>
>> ----------
>>
>> _______________________________________
>> Python tracker <report@bugs.python.org>
>> <https://bugs.python.org/issue24132>
>> _______________________________________
> ----------
>
> _______________________________________
> Python tracker <report@bugs.python.org>
> <https://bugs.python.org/issue24132>
> _______________________________________
msg305918 - (view) Author: Stephen M. Gava (elguavas) * Date: 2017-11-08 22:49
@paul.moore is the original contributor mia? i seem to remember pathlib as once being marked 'provisional', i think it should have stayed that way until this problem was resolved. easy to say i know ;) when i don't have a patch.

@projetmbc yes i found various work-arounds on the web and decided to not use any of them. really i feel this should be fixed as it's a jarring inconsistency with naturally expected behaviour for a class in python.

so i added my report to this as a topic bump because i don't think this should be forgotten about and in case anyone might come up with an idea how to fix it.
msg310634 - (view) Author: Keith (keithy) Date: 2018-01-24 21:13
Look at the architecture of Rio in Ruby (also ported to Squeak/Smalltalk)

Leave Path to handle path stuff, and have another class to handle Platform stuff.

https://rubygems.org/gems/rio/versions/0.6.0
msg314561 - (view) Author: (qb-cea) Date: 2018-03-28 00:30
Hi all,

I made a pull request proposing a fix for this issue. There is still quite a lot to be done:
 - I exposed some variables (and probably methods too) that used to be hidden;
 - I did not update the documentation;
 - I did not add a proper test.

I will try to fix those by the end of the week.

The patch mainly consists of two things:
 - having Path (resp. PurePath) be a variable pointing at either (Pure)PosixPath or (Pure)WindowsPath, depending on the platform (like Kevin Norris suggested);
 - introducing two new abstract classes _PurePath and ConcretePath from which PurePosixPath, PureWindowsPath and PosixPath, WindowsPath classes inherit;
 - removing the _Flavor classes, and redistributing their method to platform-specific classes.

Ideally I would like _PurePath to become a public class, but I could not come up with a proper name. Any feedback is more than welcome =]
msg314582 - (view) Author: Christophe BAL (projetmbc) Date: 2018-03-28 10:19
Hello.

What about AbstractPath instead of _PurePath ?

Le 28/03/2018 à 02:30, qb-cea a écrit :
> qb-cea <quentin.bouget@cea.fr> added the comment:
>
> Hi all,
>
> I made a pull request proposing a fix for this issue. There is still quite a lot to be done:
>   - I exposed some variables (and probably methods too) that used to be hidden;
>   - I did not update the documentation;
>   - I did not add a proper test.
>
> I will try to fix those by the end of the week.
>
> The patch mainly consists of two things:
>   - having Path (resp. PurePath) be a variable pointing at either (Pure)PosixPath or (Pure)WindowsPath, depending on the platform (like Kevin Norris suggested);
>   - introducing two new abstract classes _PurePath and ConcretePath from which PurePosixPath, PureWindowsPath and PosixPath, WindowsPath classes inherit;
>   - removing the _Flavor classes, and redistributing their method to platform-specific classes.
>
> Ideally I would like _PurePath to become a public class, but I could not come up with a proper name. Any feedback is more than welcome =]
>
> ----------
>
> _______________________________________
> Python tracker <report@bugs.python.org>
> <https://bugs.python.org/issue24132>
> _______________________________________
msg314625 - (view) Author: (qb-cea) Date: 2018-03-29 00:21
> What about AbstractPath instead of _PurePath ?

I will use this, thanks.
msg365277 - (view) Author: Barney Gale (barneygale) * Date: 2020-03-29 20:31
I'm taking another look at making `pathlib` extensible. There's some discussion here: https://discuss.python.org/t/make-pathlib-extensible/3428

List or preparatory bugfixes and tidy-ups: https://docs.google.com/spreadsheets/d/1TicFDMudKKA6CZcrscg1Xq9kt5Q8To8y0hADGw9u11I/edit#gid=0
msg381321 - (view) Author: (qb-cea) Date: 2020-11-18 10:11
Hi,

Thanks for reviving this! Feel free to reuse any code I wrote in my PR (or the whole PR itself), I do not think I will ever get around to finishing this work myself.
msg392695 - (view) Author: Barney Gale (barneygale) * Date: 2021-05-02 13:46
Progress report:

I've been working on tidying up the pathlib internals over the 3.9 and 3.10 releases. We're now in a position where:

- `pathlib._Flavour` is entirely pure, and doesn't make any `os` calls
- `pathlib._Accessor` handles all `os` access.

The internal abstractions are now much tighter, which allows us to begin refactoring them with confidence!

The next step is to remove accessors, in bpo-43012.

After that I'll finally be in a position to start working on this bug!
msg401946 - (view) Author: (alexia) Date: 2021-09-16 13:38
I agree this would be nice. For now, I'm doing this as a hack:

class Path(type(pathlib.Path())):
    ...
msg412354 - (view) Author: miss-islington (miss-islington) Date: 2022-02-02 12:38
New changeset 08f8301b21648d58d053e1a513db8ed32fbf37dd by Barney Gale in branch 'main':
bpo-43012: remove `pathlib._Accessor` (GH-25701)
https://github.com/python/cpython/commit/08f8301b21648d58d053e1a513db8ed32fbf37dd
msg414821 - (view) Author: Barney Gale (barneygale) * Date: 2022-03-09 23:54
If/when GH-31691 lands, I think this bug can be resolved: the original repro case will no longer raise AttributeError, and subclasses will be able to customize behaviour without needing to define further "flavour" or "accessor" subclasses.
History
Date User Action Args
2022-04-11 14:58:16adminsetgithub: 68320
2022-03-09 23:54:09barneygalesetmessages: + msg414821
2022-03-05 02:18:42barneygalesetpull_requests: + pull_request29812
2022-02-02 18:00:03barneygalesetpull_requests: + pull_request29269
2022-02-02 12:38:49miss-islingtonsetnosy: + miss-islington
messages: + msg412354
2022-01-01 01:45:43eric.araujosetversions: + Python 3.11, - Python 3.10
2021-09-16 13:38:03alexiasetnosy: + alexia
messages: + msg401946
2021-06-24 23:04:35kfollstadsetpull_requests: + pull_request25482
2021-06-13 16:55:20barneygalesetpull_requests: + pull_request25298
2021-05-29 14:29:26FFY00setnosy: + FFY00
2021-05-28 21:35:55kfollstadsetnosy: + kfollstad
pull_requests: + pull_request25030
2021-05-15 11:29:34barneygalesetpull_requests: + pull_request24778
2021-05-02 13:46:30barneygalesetmessages: + msg392695
2021-04-28 22:23:22barneygalesetpull_requests: + pull_request24392
2021-04-08 01:24:25barneygalesetpull_requests: + pull_request24005
2021-04-07 01:28:41barneygalesetstage: needs patch -> patch review
pull_requests: + pull_request23979
2021-03-26 11:02:12iritkatrielsetstage: patch review -> needs patch
type: behavior -> enhancement
components: + Library (Lib)
versions: + Python 3.10, - Python 3.6
2020-11-18 10:11:47qb-ceasetmessages: + msg381321
2020-11-16 18:13:56Xtrem532setnosy: + Xtrem532
2020-04-03 11:48:40piotr.dobrogostsetnosy: + piotr.dobrogost
2020-03-29 20:31:26barneygalesetnosy: + barneygale
messages: + msg365277
2018-03-29 00:21:19qb-ceasetmessages: + msg314625
2018-03-28 10:19:33projetmbcsetmessages: + msg314582
2018-03-28 00:30:38qb-ceasetmessages: + msg314561
2018-03-26 08:41:20qb-ceasetkeywords: + patch
stage: patch review
pull_requests: + pull_request5981
2018-01-31 11:58:12qb-ceasetnosy: + qb-cea
2018-01-30 10:59:05brongersetnosy: + bronger
2018-01-24 21:13:36keithysetnosy: + keithy
messages: + msg310634
2017-11-08 22:49:07elguavassetmessages: + msg305918
versions: + Python 3.6, - Python 3.5
2017-11-08 13:04:50projetmbcsetmessages: + msg305830
2017-11-08 12:59:46projetmbcsetmessages: + msg305827
2017-11-08 08:55:20paul.mooresetmessages: + msg305811
2017-11-08 01:01:38elguavassetnosy: + elguavas
messages: + msg305799
2017-07-18 12:42:29serhiy.storchakalinkissue30957 superseder
2015-07-01 00:27:56Kevin.Norrissetnosy: + Kevin.Norris
messages: + msg246007
2015-05-06 20:01:41paul.mooresetmessages: + msg242705
2015-05-06 19:18:59projetmbcsetmessages: + msg242703
2015-05-06 19:09:38paul.mooresetmessages: + msg242702
2015-05-06 19:06:30projetmbcsetmessages: + msg242701
2015-05-06 19:01:32paul.mooresetmessages: + msg242700
2015-05-06 18:41:32projetmbcsetmessages: + msg242699
2015-05-06 18:21:59paul.mooresetmessages: + msg242696
2015-05-06 16:50:43projetmbcsetmessages: + msg242689
2015-05-06 12:13:25projetmbcsetmessages: + msg242657
2015-05-06 11:05:57pitrousetmessages: + msg242656
versions: + Python 3.5, - Python 3.4
2015-05-06 09:15:30paul.mooresetnosy: + paul.moore
messages: + msg242651
2015-05-06 06:27:02projetmbcsettitle: Direct sub-classing of pathless.Path -> Direct sub-classing of pathlib.Path
2015-05-06 05:54:35ned.deilysetnosy: + pitrou
2015-05-06 05:26:53projetmbccreate