-
-
Notifications
You must be signed in to change notification settings - Fork 30.7k
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
subclasses of pathlib.PurePosixPath never call __init__ or __new__ #85281
Comments
I have a subclass GithubPath of PurePosixPath.
Calling
The reason seems to be that parent calls _from_parts which create a new object through Line 689 in cf18c9e
A hack is to subclass |
Note that this likely affect all methods which returns new Path by calling |
The workaround is to override _init(), which I agree is not desirable. This is the relevant code in PurePath, which is the super class of PurePosixPath: @classmethod
def _from_parsed_parts(cls, drv, root, parts, init=True):
self = object.__new__(cls)
self._drv = drv
self._root = root
self._parts = parts
if init:
self._init()
return self ... def _init(self):
# Overridden in concrete Path
pass To me, the clean way to get the desired behavior seems like it would be to have _init() call self.__init__(). def _init(self):
# Overridden in concrete Path
self.__init__() This fixes p.parent, but GithubPath() ends up calling GithubPath.__init__() twice – the first time by PurePath.__new__() calling PurePath._init() and the second time by the GithubPath object creation. |
Clarification: PurePath.__new__() calls PurePath._from_parts(), which then calls PurePath._init() |
Before solving this issue, I think it would be best to think on a more generic solution on how to make Pathlib more extensible. Related to: https://discuss.python.org/t/make-pathlib-extensible/3428 For instance, if childs created with Rather than __init__, maybe there should be some __post_init__ like dataclasses. |
Path.new should not call _from_parts because it breaks the specialization by directly using object.new. My suggestion (first draft) is to do the parsing of arguments inside an
I don't know what is the purpose of The class method By using init and avoid the direct call to |
The purpose of the _init() function in PurePath is to allow PurePath methods to call the Path subclass override _init() function when initializing a Path object. |
Adding __init__() to PurePath complicates things and doesn't provide any benefit. A subclass that calls super.__init__() ends up invoking object.__init__(), which is perfectly fine. I was able to find a solution by calling type(self)() instead of object.__new__() in most cases. I am working on a PR. |
This is not true, because the classmethod use the library shortcuts the Le jeu. 13 août 2020 à 15:27, Jeffrey Kintscher <report@bugs.python.org> a
|
This is not true, because the classmethod used the library shortcuts the Le jeu. 13 août 2020 à 15:31, Louis-Vincent Boudreault <
|
The current implementation calls object.__new__(cls), where cls is the child class type, from within a class method (@classmethod). This is fine for Path.__new__() and PurePath.__new__(), which are called by the child class's __new__(), because we don't want them to recursively call the child class's __new__() when the child class is created. This all works as expected when the child class is instantiated outside of Path and PurePath, and the child's __init__() gets called as expected. I don't see any point in making changes to this behavior. When one of approximately 20 PurePath and Path functions and properties instantiate a new child class object the same way PurePath.__new__() and Path.__new__() do, the child class's __new__() and __init__() functions are not called. This is the problem we are trying to solve. My fix is to add normal functions (i.e. not decorated with @classmethod) to instantiate child class objects using obj = type(self)() This creates a child class instance, and the child's __new__() and __init__() functions get called. Once I have finished re-plumbing Path and PurePath to use the new functions and created the necessary unit tests (to make sure I didn't break anything), I will also look at adding |
I created a PR that should provide the desired behavior: __init__() and __new__() get called in subclass objects that are created by Path and PurePath. Also, Path and PurePath now have __init__() functions, and the __new__() functions only return new objects and rely upon __init__() to perform the object initialization. |
I believe #100481 would address this. |
`PurePath` now normalises and splits paths only when necessary, e.g. when `.name` or `.parent` is accessed. The result is cached. This speeds up path object construction by around 4x. `PurePath.__fspath__()` now returns an unnormalised path, which should be transparent to filesystem APIs (else pathlib's normalisation is broken!). This extends the earlier performance improvement to most impure `Path` methods, and also speeds up pickling, `p.joinpath('bar')` and `p / 'bar'`. This also fixes pythonGH-76846 and pythonGH-85281 by unifying path constructors and adding an `__init__()` method.
…lasses (GH-102789) Fix an issue where `__new__()` and `__init__()` were not called on subclasses of `pathlib.PurePath` and `Path` in some circumstances. Paths are now normalized on-demand. This speeds up path construction, `p.joinpath(q)`, and `p / q`. Co-authored-by: Steve Dower <steve.dower@microsoft.com>
…pathlib subclasses (pythonGH-102789) Fix an issue where `__new__()` and `__init__()` were not called on subclasses of `pathlib.PurePath` and `Path` in some circumstances. Paths are now normalized on-demand. This speeds up path construction, `p.joinpath(q)`, and `p / q`. Co-authored-by: Steve Dower <steve.dower@microsoft.com>
…pathlib subclasses (pythonGH-102789) Fix an issue where `__new__()` and `__init__()` were not called on subclasses of `pathlib.PurePath` and `Path` in some circumstances. Paths are now normalized on-demand. This speeds up path construction, `p.joinpath(q)`, and `p / q`. Co-authored-by: Steve Dower <steve.dower@microsoft.com>
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: