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

Replace legacy API implementation with files() #221

Merged
merged 11 commits into from Jun 28, 2021

Conversation

FFY00
Copy link
Member

@FFY00 FFY00 commented May 12, 2021

No description provided.

@FFY00 FFY00 force-pushed the use-files-in-leacy-api branch 3 times, most recently from e3e681a to f68da1c Compare May 12, 2021 16:47
@FFY00
Copy link
Member Author

FFY00 commented May 12, 2021

@jaraco the first commit is bad, but I wonder if is something that we actually want to do? The Path class is clearly designed to not be used, but it needs to be working because the files() will use it. I think we have two options here.

  1. Replace DegenerateFiles.Path with pathlib.Path and force ResourceReader.contents() to iterate valid paths.

  2. Make DegenerateFiles.Path try to emulate the normal behavior with non-existent ResourceReader.contents() files.

I would greatly prefeer 1), which actually seems like the correct behavior, if I understand wrap_spec correctly. 2) is a bit tricky to implement, and I am having some issues.

@jaraco
Copy link
Member

jaraco commented May 12, 2021

@FFY00: Can we back up a bit? What expectation is missed that led you to draft the first commit (193ede9)? I see the commit message says "fix DegenerateFiles not exposing loader contents", but I'm unsure why that's needed. Would it be possible to simply deprecate contents altogether?

@FFY00
Copy link
Member Author

FFY00 commented May 12, 2021

Currently, tests are failing if I switch the contents implementation to the files API.

class ResourceLoaderTests(unittest.TestCase):

The current contents implementation will work fine if we create a module manually as done in tests.util.create_package, the files API will not. It will return an empty iterable because that is what the DegenerateFiles.Path implementation does.

def create_package(file, path, is_package=True, contents=()):

We could simply deprecate contents, but as discussed in #80, I am reimplementing the legacy API with files to make sure there is no edge case where it doesn't work, and as discovered here, there is.
I guess the question now is, do we care about this case? If we do, do we handle it in the files API, or do we come up with other solution?
To me, it seems wrong that DegenerateFiles will return a traversable that does not correctly represent the spec contents.

@jaraco
Copy link
Member

jaraco commented May 12, 2021

If DegenerateFiles.Path implements this behavior, then it betrays its namesake. That is, it's no longer degenerate if it's passing through to some underlying implementation (i.e. ResourceReader.contents).

Maybe the right thing to do here is to rename DegenerateFiles to CompatibilityFiles or FallbackFiles and then allow it to grow this functionality.

An important thing to remember regarding the ResourceReader is that it's an abstract concept. The tests here are capturing an abstract concept that other loaders may be implementing. So option (1) isn't viable (you can't assume the contents of a module could be represented by a pathlib.Path object).

As I see it, the question is - what should happen for a loader that implements 'contents' but not 'files' - should we wrap that implementation and create a files-like interface? My instinct is no... but that will break compatibility, and it's not even be possible in the general case (files implements features that the other APIs were incapable of supporting).

I think you're struggling with a lot of the challenges I've struggled with while trying to adapt everything to run on files().

I don't have a good recommendation at this time, but I'll try to loop back on it again later... and I welcome you to continue to explore it.

@FFY00
Copy link
Member Author

FFY00 commented May 12, 2021

Maybe the right thing to do here is to rename DegenerateFiles to CompatibilityFiles or FallbackFiles and then allow it to grow this functionality.

I agree.

As I see it, the question is - what should happen for a loader that implements 'contents' but not 'files' - should we wrap that implementation and create a files-like interface? My instinct is no... but that will break compatibility, and it's not even be possible in the general case (files implements features that the other APIs were incapable of supporting).

The thing is, this is already supported by the legacy API. I gave it another try and the issue I am running into is a lack of means to implement is_file, exists, is_dir -- they would need to be supported by the resource reader. Which is essentially the issue you described.

We could support these cases in the files API by making the returned traversable raise an unsupported operation error on such scenarios, which I feel is not a particularly bad idea.
The legacy contents API returns strings, so in files we could return traversables that would still hold that string value but have limited value besides that, we could still support open where ResourceReader can do it, but not is_file and friends.

This proposal makes it possible to easily replace contents usages with files, the possible issue here would be this somehow damaging expectations of the files API. But IMO this should not really be a big issue, as it is an edge case, and we could possibly add support to ResourceReader to optionally implement the missing functionality.
What do you think?

@FFY00 FFY00 force-pushed the use-files-in-leacy-api branch 3 times, most recently from 7055ce1 to 1a2ce97 Compare May 13, 2021 00:50
@FFY00
Copy link
Member Author

FFY00 commented May 13, 2021

I have a successful implementation that uses the approach I described.

@jaraco
Copy link
Member

jaraco commented May 20, 2021

we could possibly add support to ResourceReader to optionally implement the missing functionality

Since other resource providers implement the the concrete ResourceReader subclasses, any change would need to be compatible with the existing interface, so I suspect this approach is not worthwhile.

I notice the diffcov tests are failing with:

importlib_resources/_adapters.py (52.2%): Missing lines 28,40,56,61-62,65-66,69,76-77,80

Not a hard requirement, but I'd prefer if we had tests covering those code paths.

@FFY00
Copy link
Member Author

FFY00 commented May 21, 2021

Since other resource providers implement the the concrete ResourceReader subclasses, any change would need to be compatible with the existing interface, so I suspect this approach is not worthwhile.

Yes, makes sense, we want people moving away from ResourceReader to TraversableReader so extending ResourceReader does not make that much sense.

Discussion moved to #226.

@FFY00 FFY00 force-pushed the use-files-in-leacy-api branch 2 times, most recently from ba63f38 to 84658bc Compare May 21, 2021 00:16
@FFY00 FFY00 force-pushed the use-files-in-leacy-api branch 5 times, most recently from 69d19dd to 7cb935e Compare May 21, 2021 00:36
Signed-off-by: Filipe Laíns <lains@riseup.net>
@FFY00 FFY00 force-pushed the use-files-in-leacy-api branch 4 times, most recently from 979870a to a1b319c Compare May 29, 2021 18:22
@FFY00
Copy link
Member Author

FFY00 commented May 29, 2021

This is still missing tests for CompatibilityFiles, but is ready for review.

@FFY00 FFY00 marked this pull request as ready for review May 29, 2021 18:33
@FFY00 FFY00 requested a review from jaraco May 29, 2021 19:34
FFY00 added 9 commits May 29, 2021 20:47
Signed-off-by: Filipe Laíns <lains@riseup.net>
Signed-off-by: Filipe Laíns <lains@riseup.net>
The files() api makes the identification of resources blury. Here we
re-implement is_resource() by mirroring the previous logic to the best
extent we can. This is not fully correct, as the files() api is not
fully compatible with the legacy api, but it is pretty close and as
correct as we can get. The semantics for what constitutes resources have
always been blurry in the first place.

Signed-off-by: Filipe Laíns <lains@riseup.net>
Signed-off-by: Filipe Laíns <lains@riseup.net>
Signed-off-by: Filipe Laíns <lains@riseup.net>
Signed-off-by: Filipe Laíns <lains@riseup.net>
Signed-off-by: Filipe Laíns <lains@riseup.net>
Signed-off-by: Filipe Laíns <lains@riseup.net>
Signed-off-by: Filipe Laíns <lains@riseup.net>
@FFY00
Copy link
Member Author

FFY00 commented Jun 7, 2021

@jaraco friendly ping. Please have a look when you have time, I know it's a pretty big, and involved, change so don't feel pressured 😅

@FFY00
Copy link
Member Author

FFY00 commented Jun 21, 2021

@jaraco friendly ping.

@jaraco
Copy link
Member

jaraco commented Jun 22, 2021 via email

Copy link
Member

@jaraco jaraco left a comment

Choose a reason for hiding this comment

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

This work is excellent. It's clean, makes some smart decisions, but most importantly sets us up to be able to deprecate the legacy API. I have only the one serious reservation, and even that isn't a blocker to move forward, so I'll defer to your preference on whether to pursue those changes in this PR or a subsequent one.

importlib_resources/_compat.py Show resolved Hide resolved
importlib_resources/_common.py Outdated Show resolved Hide resolved
Signed-off-by: Filipe Laíns <lains@riseup.net>
@jaraco jaraco merged commit 5b2a675 into python:main Jun 28, 2021
jaraco added a commit that referenced this pull request Jun 28, 2021
@jaraco
Copy link
Member

jaraco commented Jun 28, 2021

Big thanks Filipe. Releasing as v5.2.0.

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.

None yet

2 participants