Hi, coming here from the discussion on https://github.com/python/importlib_metadata/issues/115. Trying to setup a developer env for importlib_metadata, are there some docs for this somewhere? Failed to find anything in the repo itself, so far, and my python3.11 -m venv .venv followed by pip install tox and tox is far from green.
#importlib-metadata
1 messages · Page 1 of 1 (latest)
there aren't any docs unfortunately
yeah, the recommended way to run the test suite is via tox
if it's green, then it should be good
for your issue, you'll want to add a new _read_files_pip_installed (or a similar name) and then add it between _read_files_distinfo and _read_files_egginfo in https://github.com/python/importlib_metadata/blob/5475a6e3d96e1f31441c90386e8d70514ab0e954/importlib_metadata/__init__.py#L479
then you want to add a test in test_main.py
something similar to the test I added here https://github.com/python/importlib_metadata/pull/432/files
instead of RECORD, you'll want to have your source
Thanks a lot for your pointers. Very useful!
Would be interested to know how you set up your development environment for a project like this. For example, given a new laptop, roughly what steps would you follow to get to a green tox run?
eh, I usually just run pytest directly
and tox if I am having trouble for some reason
to make sure it's not my environment that's the issue
I dug a little further into my tox failure. Haven't gotten to the bottom of it yet, but I think there's a problem when combined with the latest Mypy release (1.1.1): tox -- -vv tests/test_main.py fails on tests/test_main.py::mypy-status with:
importlib_metadata/__init__.py:435: error: Incompatible return value type (got "Message", expected "PackageMetadata") [return-value]
importlib_metadata/__init__.py:435: note: Following member(s) of "Message" have conflicts:
importlib_metadata/__init__.py:435: note: Expected:
importlib_metadata/__init__.py:435: note: def [_T] get_all(self, name: str, failobj: _T = ...) -> Union[List[Any], _T]
importlib_metadata/__init__.py:435: note: Got:
importlib_metadata/__init__.py:435: note: @overload
importlib_metadata/__init__.py:435: note: def get_all(self, name: str, failobj: None = ...) -> Optional[List[Any]]
importlib_metadata/__init__.py:435: note: @overload
importlib_metadata/__init__.py:435: note: def [_T] get_all(self, name: str, failobj: _T) -> Union[List[Any], _T]
Found 1 error in 1 file (checked 1 source file)
But with this diff the same tox command passes:
diff --git a/setup.cfg b/setup.cfg
index f5c7280..52eef95 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -45,6 +45,7 @@ testing =
pytest-mypy >= 0.9.1; \
# workaround for jaraco/skeleton#22
python_implementation != "PyPy"
+ mypy < 1.1
pytest-enabler >= 1.3
# local
Thanks. Running tox across the entire project, I ran into another failure in exercises.py::exercises::py:cached distribution. It failed while trying to run this:
subprocess.CalledProcessError: Command '('/home/jherland/code/importlib_metadata/.tox/python/bin/python', '-m', 'pip', 'install', '-t', PosixPath('/run/user/1000/pip-run-aofstq26'), 'importlib_metadata.git[perf]@git+git@github.com:python/importlib_metadata.git')' returned non-zero exit status 1.
The importlib_metadata.git[perf]@git+git@github.com:python/importlib_metadata.git specifier/URL does not make sense, and I traced it back to the origin URL for my git config:
$ git config remote.origin.url
git@github.com:python/importlib_metadata.git
I originally cloned this project using the Git URL suggested on the GitHub repo page. If I run with the suggested HTTPS URL instead:
https://github.com/python/importlib_metadata.git
the same command fails with a slightly different specifier/URL: importlib_metadata.git[perf]@git+https://github.com/python/importlib_metadata.git
Finally, removing the .git suffix from the HTTPS URL by hand, (git remote set-url origin https://github.com/python/importlib_metadata), I can get that test to pass. It seems somewhat brittle to have the tests rely on the origin URL in this manner?
hummm, interesting
I haven't run into that before
I wouldn't care about it much though, that's just the performance tests
to make sure we are aware if there's any significant performance regression
Created https://github.com/python/importlib_metadata/pull/437 now. @worldly crypt: Thanks for your help in getting up and running with this!
thanks! looks good to me overall, only thing that jumps out to me is that you shouldn't really need all that metadata in the tests
but I'll do a proper review later when I am at the computer
Thanks, I mostly copied from the example above, and I agree that a lot of the metadata can be dropped.
(refreshed the PR with unnecessary details removed from the tests, as well as some black fixes that escaped the first iteration)
I think I found a problem with https://github.com/python/importlib_metadata/pull/432 which was merged to main recently. I added a comment to the PR, but I don't know if that is enough to raise people's attention, so I mention it here as well.
looks like it's missing a str.isidentifier check. I'm also seeing .. and __pycache__. The latter shouldn't be added in the RECORD I thought?
yep, I overlooked that and it didn't get caught in the review
@sand arrow whats the correct way to "override" a installed distirbution with a dnyamic metadata finder
i would like to have something that when given a editable install, it replaces whatever uses only the dist.info - and also uses the pyproject.toml and setuptools_scm/vcs_versioning to get the metadata of a editable package (aka autoupdate entrypoints and/or version)
I install tox using pipx so it's availably globally across projects and avoids the bootstrapping of each repo.
I don't believe there's a mechanism to override the distribution finders - only to extend them.
You can extend them by adding a MetadataDistributionFinder to sys.meta_path (or insert at the beginning for priority). Entrypoints will de-duplicate based on normalized distribution name, so you'll only get entry points for the first one discovered.
So for entry points, extending the finders will effectively override.
For other functions (metadata, distribution) that rely on Distribution.from_name, it also chooses the first one found (https://github.com/python/importlib_metadata/blob/700f2c7d74543e3695163d5487155b92e6f04d65/importlib_metadata/__init__.py#L382).
So having a "enhanced" distribution discovered first is the implied mechanism
So from a practical standpoint, extending the search algorithm (https://importlib-resources.readthedocs.io/en/latest/using.html#extending) should have the effect you desire.
Yes. The one thing to be aware of is that the backport (importlib_metadata) appends its finder on import (https://github.com/python/importlib_metadata/blob/700f2c7d74543e3695163d5487155b92e6f04d65/importlib_metadata/_compat.py#L15). Since it's appending, it probably won't adversely affect any attempts to prepend.
This is Good news, this will enable both enhanced editables and a Mode for pytest that can handle collection in a work dir against installed packages in a safe manner (aka import from site but raise errors on stale files)
I'm unsure how well tested the expectation of "primacy of duplicate distributions" is. I wouldn't mind if someone wanted to add tests to importlib_metadata to properly capture an expectation of one distribution superseding another.
Another question i have, how does one best add fake namespace packages to sys.Modules i want to add worktree testroots to the import system in a manner that allows pep420 aware test collection in testing/tests folders without adding their parent to sys.path
Thanks for the warning, Will add a note on that to my feature proposal's on editables /pytest
It's been forever since I've worked on the namespace packages implementation. As much as possible, namespace packages are meant to appear just like regular modules with as small as possible variance for their purpose.
I can see how the "rendering" of a namespace package is detected: https://github.com/python/cpython/blob/0708437ad043657f992cb985fd5c37e1ac052f93/Lib/importlib/_bootstrap.py#L742
And where that loader is configured: https://github.com/python/cpython/blob/0708437ad043657f992cb985fd5c37e1ac052f93/Lib/importlib/_bootstrap.py#L662-L666
So probably, you create a types.ModuleType(name) and then assign mod.__spec__.loader = NamespaceLoader() with loader._path set to some list of paths served by that namespace and you'll have something that renders as a namespace package. You may have to initialize other attributes too, like __origin__=None.
The loader._path is probably [parent], where parent is the parent of the testing/tests folders.
I hope that helps.
@sand arrow thanks for the links, it seems i just need a finder that returns a spec for the folder either as namespace or as package (depending on whether __init__.py is added
@sand arrow any oppinion on adding full type annotations directly to importlib_metadata - the current lack is a minor pain in other fully typed codebases and im under the impression its doable in a complete manner
In case of positive, I'd volunteer the work
yeah, we should do that for the public API at least
we already do it for importlib_resources
there is a (nearly 2yo) PR adding annotations which was rejected: https://github.com/python/importlib_metadata/pull/342. I also more recently attempted to improve the typeshed stubs - that's gone nowhere: https://github.com/python/typeshed/pull/9217
I'm supportive of it. The rejection in 342 was mainly because adding the typing required changing the logic. I'd start with uncontroversial changes that don't require altering the behavior.
It'll also be easier once some of the deprecated interfaces are removed.
You're also welcome to start from 342 and address the comments in the PR.
I think a good first step would be to just type the public interfaces, and then go from there if it makes sense. https://github.com/python/importlib_metadata/pull/342 attempted typing everything right away, which unfortunately meant that the most beneficial contribution, having the public API typed, got blocked together with the rest of the PR.
Yikes, that one had multiple breaking changes
Based on the detail /approach it seems like 342 is not a suitable base 😱
anyone aware of examples for remote passover of importlib metadata metadata? im trying to add a dict/json meta path importer that allows send in of a distribution for bootstrapping purposes
anyone aware of a potential solution for https://github.com/pypa/setuptools-scm/issues/1013#issuecomment-2814014441
my current understanding (unverified) is that due to version numbers in dist info it seems that with a sufficiently messed up pythonpath importlib-metadata will find entrypoints of incompatible versions that will fail to import as packages shadow, but dist-infos have unique distinc names
as far as i understand any ports like system is affected due to pythonpath interaction
thanks
@sand arrow https://github.com/python/importlib_metadata/issues/516 seems like loads of fun
Any idea for a hack to sort that
Hi, this is the second time I wonder what importlib.metadata.Distribution’s public API is. Can someone please confirm that it’s the same as importlib_metadata.Distribution’s? https://importlib-metadata.readthedocs.io/en/stable/api.html#importlib_metadata.Distribution
I’ll happily make a CPython docs update to fix this once I know what’s actually true
GitHub
https://docs.python.org/3.13/library/importlib.metadata.html provides a narrative overview of various features of the module, but it does not contain reference documentation listing all the public ...
should be, excluding additions or deprecations
but yeah, I agree the CPython documentation is very lacking
there are also significant drifts between the stdlib version and the "upstream" one. Is there a plan to unify the development into the stdlib version only?
Lol yeah, and CRAZY
I was looking through the source of the 3.10 importlib.metadata module a few days ago and there was an internal import in a private method that imported pep517.build?!
(aside: so I then ended up on that 2016 pep517/pyproject-hooks rabbithole, and then on setuptools and then distutils and somehow I was reading py3k peps on this journey of curiosity... But I digress)
yeah, there are a couple places that happens, but it's very rare
it should only ever be the case for optional things that would be almost impossible to implement without using 3rd party packages
other example is datetime accepting the tzdata package as a source of the tz database
The drift should be small and definitively indicated in https://github.com/python/importlib_metadata/compare/main..cpython .
To the extent that there are changes downstream in stdlib, those need to be ported to importlib_metadata.
I made a PR with the remaining API reference for importlib.metadata: https://github.com/python/cpython/pull/143480/
Is the stdlib module stable enough, and backward compatible enough to effectively deprecate the standalone version for newer Pythons? If not, is it feasible at some release in the future? (Same question applies to both importlib.metadata and importlib.resources)
@mossy kayak How about here?
(I just created the channel)
I replicated your issue:
~ $ pip-run backports.entry_points_selectable -- -c "import importlib.metadata as md; md.distribution('backports-entry-points-selectable')"
Collecting backports.entry_points_selectable
Using cached backports.entry_points_selectable-1.1.0-py2.py3-none-any.whl (6.2 kB)
Installing collected packages: backports.entry-points-selectable
Successfully installed backports.entry-points-selectable-1.1.0
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/importlib/metadata.py", line 524, in distribution
return Distribution.from_name(distribution_name)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/importlib/metadata.py", line 187, in from_name
raise PackageNotFoundError(name)
importlib.metadata.PackageNotFoundError: backports-entry-points-selectable
Looks like importlib_metadata added support for normalized names in https://importlib-metadata.readthedocs.io/en/latest/history.html#v4-0-0
Also possibly relevant https://importlib-metadata.readthedocs.io/en/latest/history.html#v2-1-0
Confirmed - that issue was fixed in importlib_metadata 2.1.
~ $ pip-run backports.entry_points_selectable 'importlib_metadata<2.1' -- -c "import importlib_metadata as md; md.distribution('backports-entry-points-selectable'
)"
Collecting backports.entry_points_selectable
Using cached backports.entry_points_selectable-1.1.0-py2.py3-none-any.whl (6.2 kB)
Collecting importlib_metadata<2.1
Using cached importlib_metadata-2.0.0-py2.py3-none-any.whl (31 kB)
Collecting zipp>=0.5
Using cached zipp-3.5.0-py3-none-any.whl (5.7 kB)
Installing collected packages: zipp, importlib-metadata, backports.entry-points-selectable
Successfully installed backports.entry-points-selectable-1.1.0 importlib-metadata-2.0.0 zipp-3.5.0
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-run-satw542a/importlib_metadata/__init__.py", line 558, in distribution
return Distribution.from_name(distribution_name)
File "/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-run-satw542a/importlib_metadata/__init__.py", line 215, in from_name
raise PackageNotFoundError(name)
importlib_metadata.PackageNotFoundError: No package metadata was found for backports-entry-points-selectable
~ $ pip-run backports.entry_points_selectable 'importlib_metadata==2.1' -- -c "import importlib_metadata as md; md.distribution('backports-entry-points-selectable
')" && echo works \o/
Collecting backports.entry_points_selectable
Using cached backports.entry_points_selectable-1.1.0-py2.py3-none-any.whl (6.2 kB)
Collecting importlib_metadata==2.1
Using cached importlib_metadata-2.1.0-py2.py3-none-any.whl (10 kB)
Collecting zipp>=0.5
Using cached zipp-3.5.0-py3-none-any.whl (5.7 kB)
Installing collected packages: zipp, importlib-metadata, backports.entry-points-selectable
Successfully installed backports.entry-points-selectable-1.1.0 importlib-metadata-2.1.0 zipp-3.5.0
works \o/
Yeah, but in the above case importlib.metadat is pulled from python3.9 I think
And since importlib.metadata on Python 3.9 is equivalent to importlib_metadata==1.4, that explains why the issue occurs on Python 3.9.
😐 this leaves us in a weird place 😄 do I need to not use importlib-metadata from stdlib on 3.9? :-~
You can. And would that be so weird?
If you want a feature that's available in Python 3.10 on older Pythons, you use a backport.
And importlib_metadata is that backport.
shouldn't backports-entry-points-selectable handle this automatically
That backport was only created for a specific feature that needed special handling (selectable entry points). It doesn't back port anything else.
Still, I would recommend not using manglednormalized names.
Is there a way that the routine that's encountering this issue could instead simply use the official names for the package (backports.entry_points_selectable instead of backports-entry-points-selectable)?
The latter is used to de-duplicate for PyPI (PEP 503) but it's preferable to use the canonical name, which communicates more information than the normalized name.
I think https://github.com/tox-dev/tox/pull/2103/files does that
That seems like best approach, at least for compatibility.
is there a way to read the metadata from a zip?
If I understand correctly you can just pass in a path to a zip file like a regular path (the zip file should correspond to a directory) and things are supposed to just work
Something like this
dists = list(Distribution.discover(path="path/to.zip"))
thanks, it doesn't appear to work for me. I wonder if I've misunderstood. what I was hoping to do was read metadata from a whl/egg file
i get no errors but the result is an empty list
then use zipfile.Path which I linked you in the other channel
importlib.metadata.PathDistribution(your_zip_path)
it should discover
example
GitHub
A Python dependency resolver. Contribute to FFY00/python-resolver development by creating an account on GitHub.
*assuming you're using Python 3.8+
there's a backport so
path needs to be a list
you can also shorten it to list(distributions(path=["path/to.zip"]))
Ah right, because it's supposed to reflect sys.path 🤦♂️ I've tripped over this a hundred times
thanks so much for the help @worldly crypt @signal topaz @slim tundra
no problem
What is the best way to load a Distribution object from a wheel zip? It’d be best if the loading function also checks the name and version match, but that I can do myself if importlib-metadata does not do it
it doesn't, it's very annoying
:(
So I guess manually fetching out the dist-info directory’s name first (using ZipFile) and instantiating with PathDistribution(zipfile.Path(zf, dist-info-name)) is the best possible approach for now?
yes