Thanks for the ping - I'd lost track of the issue.
That sounds reasonable but as_file returns a context manager:
>>> as_file(files("jupyterlab_server.test_data"))
<contextlib._GeneratorContextManager object at 0x7f22b9b6a480>
Aah. yes. My mistake. 🤦
I'd forgotten that as_file returns a context manager. The reason it returns a context manager is because Traversable objects may not exist on the file system, and making them exist on the file system (its purpose) implicitly creates state that needs to be cleaned up.
If you're willing to assume that your resource providers are always providing resources that are present in one package on the file system and that cleanup will never be necessary, you could enter the context and never exit:
draft @ touch foo/__init__.py
draft @ pip-run importlib_resources -- -q
>>> import importlib_resources as res
>>> dir = res.as_file(res.files('foo')).__enter__()
>>> dir
PosixPath('/Users/jaraco/draft/foo')
Here's a real-world example (not recommended) of a project abusing the context manager to ensure that the data is present on the file system for the duration of the process.
However, in python/importlib_resources#286, I learned that you have a namespace package and thus you get a MultiplexedPath object from files():
draft @ mkdir -p ns1/foo
draft @ mkdir -p ns2/foo
draft @ env PYTHONPATH=ns1:ns2 pip-run importlib_resources -q
Python 3.11.4 (main, Jun 20 2023, 17:23:00) [Clang 14.0.3 (clang-1403.0.22.14.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> ^D
draft @ env PYTHONPATH=ns1:ns2 pip-run importlib_resources -- -q
>>> import importlib_resources as res
>>> mp = res.files('foo')
>>> mp
MultiplexedPath('/Users/jaraco/draft/ns1/foo', '/Users/jaraco/draft/ns2/foo')
As you can see illustrated above, the fact that foo is a namespace package means its contents can exist in multiple locations, so traversing with .joinpath("dummy") or explicitly accessing ._paths[0] is simply collapsing the multiplexed path into the first possible path.
This technique wouldn't work if the resource you desire is present in a later package:
draft @ touch ns2/foo/data.txt
draft @ env PYTHONPATH=ns1:ns2 pip-run importlib_resources -- -q
>>> import importlib_resources as res
>>> mp = res.files('foo')
>>> mp.joinpath('data.txt').exists()
True
>>> mp.joinpath('dummy').parent.joinpath('data.txt').exists()
False
>>> with res.as_file(mp) as foo_res:
... print(foo_res)
... print(list(foo_res.iterdir()))
...
/var/folders/sx/n5gkrgfx6zd91ymxr2sr9wvw00n8zm/T/tmp6hd8ding/foo
[PosixPath('/var/folders/sx/n5gkrgfx6zd91ymxr2sr9wvw00n8zm/T/tmp6hd8ding/foo/data.txt')]
>>> foo_res.exists()
False
That's why MultiplexedPath exists - to honor resources that may exist across one or more paths.
But I suspect what's happening is you're not intending or expecting to get a namespace package or multiplexed path, but instead, you're happening to get one because you're accessing test_data not as a directory but as a package.
When you specify files('jupyterlab_server.test_data'), you're saying, give me the resources for the Python package jupyterlab_server.test_data, same as if you'd said import jupyterlab_server.test_data as td; files(td).
And because Python will magically import any directory as a namespace package, that's why you're getting the namespace package and the MultiplexedPath.
draft @ mkdir -p pkg/data
draft @ touch pkg/__init__.py
draft @ pip-run importlib_resources -- -q
>>> res.files('pkg.data')
MultiplexedPath('/Users/jaraco/draft/pkg/data')
My guess is if you instead were to use files('jupyterlab_server').joinpath('test_data'), you would get a non-multiplexed traversable that you could assume (at your risk) is a pathlib.Path:
draft @ mkdir -p pkg/data
draft @ touch pkg/__init__.py
draft @ pip-run importlib_resources -- -q
>>> import importlib_resources as res
>>> res.files('pkg').joinpath('data')
PosixPath('/Users/jaraco/draft/pkg/data')
(and you could cast(Traversable, data) to satisfy mypy).
I think the real question here is - what are you trying to accomplish with jupyterlab_server's test data? Are you wanting to do anything with it other than list the contents and read files? If so, perhaps your best bet would be to just use the Traversable interface.
The only safe way to access a Traversable is to use its limited interface or rely on as_file to ensure it's materialized in the file system (for the duration of a context manager).