Skip to content
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

bpo-24132: Add direct subclassing of PurePath/Path in pathlib #26906

Closed
wants to merge 7 commits into from

Conversation

kfollstad
Copy link
Contributor

@kfollstad kfollstad commented Jun 24, 2021

I submit for your consideration a new working version (with documentation) of an extensible, subclassable PurePath/Path to close bpo-24132, a 6-year-old bug. I had previously submitted a PR which added classes to pathlib as a path to solving this, but these commits introduce no new public classes, breaking changes, or deviations from PEP 428.

The crux of the issue here is that Path (and PurePath) are factories which generate their subclasses upon instantiation and as such are actually dependent upon their flavoured subclasses. To get around this, we attach _flavour to PurePath/Path (and any non PurePosixPath/PureWindowsPath/PosixPath/WindowsPath class) at time of instantiation. Then any subclass will have the essential _flavour attribute available to it. Moreover, in the case that the class being instantiated is PurePath/Path, instead of instantiating another new instance, we just update the class attributes in place so that it becomes its flavoured subclass. My thanks goes out to Barney Gale for making suggestions that inspired this approach.

In addition, I should mention that @barneygale is working on implementing an AbstractPath interface which will allow for even further extensibility of pathlib. His idea thread on this is here. This was an active consideration as I was writing this, and I specifically chose an implementation which I think works with what he is doing. (Barney, I hope that you find that this to be true - I look forward to your comments.) To verify this, I created a stub version of AbstractPath derived from these commits which I hope further shows the viability of this approach. That code can be found here.

https://bugs.python.org/issue24132

Add tests to verify previously untested black-box class properties of
PurePath and Path in pathlib. Collectively, these tests verify all the
ways the classes represent themselves via isinstance, issubclass,
__repr__(), etc. Moreover all class instances spawned by PurePath or
Path are also similarly tested.
Add expected failing tests to pathlib that will verify the ability
of PurePath and Path to be subclassed by users. As part of this,
test all of the black-box class properties of the newly subclassed
Path-like class instances, and the instances generated by their methods.
Existing is_mount method in Path was Posix only and not cross-
platform even though it was in a cross-platform base class. The existing
design relied upon being overriden later in WindowsPath. Consolidated
code from both into new Path.is_mount() which is now cross-plaform
similiar to all of the other Path methods.
Replace hardcorded references to pathlib.Path to facilitate making
Path extensible.
Replace factory functionality of PurePath and Path with a psuedo-factory
which appears to generate it's derived subclasses, but instead just
masquerades the new instance as one of the subclasses. Doing so makes
the resultant versions of PurePath and Path directly subclassable and as
such allows them to pass all of the previously failing subclass tests.
Add tests mirroring existing comparison tests for PurePosixPath
and PureWindowsPath but extending them to direct subclasses of PurePath.
Add documentation highlighting the new direct subclass functionality of
PurePath and Path. Also add new is_posix & is_windows doctest conditions
so that system specific doctest code can still be tested instead of just
being taken as a literal block and always skipped.
@barneygale
Copy link
Contributor

Great stuff! Will review in the next couple of days.

from os import name as system_name
is_posix = system_name == 'posix'
is_windows = system_name == 'nt'
del system_name
Copy link
Contributor Author

@kfollstad kfollstad Jun 25, 2021

Choose a reason for hiding this comment

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

Or IS_POSIX/IS_WINDOWS? I am unaware of any precedence for the naming convention here. However having the ability to skip tests depending on platform, gives one the ability to actually have sphinx doctest the code in the documentation rather than just skipping it with :: and trusting that it is correct (as is the current practice throughout much of pathlib's documentation).

Copy link
Member

Choose a reason for hiding this comment

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

It doesnt really matter in this snippet that’s invisible to readers of the doc 🙂

I think lower case looks fine.

@kfollstad kfollstad marked this pull request as draft June 26, 2021 08:44
@github-actions
Copy link

This PR is stale because it has been open for 30 days with no activity.

@github-actions github-actions bot added the stale Stale PR or inactive for long period of time. label Jul 27, 2021
class will not generate a differently named class:

.. doctest::
:pyversion: > 3.11
Copy link
Member

Choose a reason for hiding this comment

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

This is surprising to me: python docs document one version, so what is this for?
(also > and not >=?)

>>> [Path('/dir').is_absolute, MyPath('/dir').is_absolute()]
[True, True]
>>> [Path().home().drive, MyPath().home().drive]
['', '']
Copy link
Member

Choose a reason for hiding this comment

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

A minor point: the lists here are a little bit distracting (we are looking at methods/properties, not working with lists).
Could use separate lines, or >>> Path().home().drive, MyPath().home().drive (which will have parens in the output but that’s fine, we’re used to commas being sometimes multiple things, sometimes a tuple object).

@barneygale
Copy link
Contributor

Hey @kfollstad, FYI, I've now got another PR up that makes subclassing possible: #31691

@github-actions github-actions bot removed the stale Stale PR or inactive for long period of time. label Aug 10, 2022
@barneygale
Copy link
Contributor

Hey @kfollstad, the original bug has now been solved soon I'm going to close this PR. Hope that's OK! Thank you for working on this -- your implementation was helpful while I was working through this problem.

@barneygale barneygale closed this Feb 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants