File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -1031,7 +1031,6 @@ def __bool__(self):
10311031 return bool (self .name )
10321032
10331033
1034- @install
10351034class MetadataPathFinder (NullFinder , DistributionFinder ):
10361035 """A degenerate finder for distribution packages on the file system.
10371036
@@ -1310,3 +1309,6 @@ def importable_name(name):
13101309 return '.' not in name
13111310
13121311 return filter (importable_name , opt_names )
1312+
1313+
1314+ install (MetadataPathFinder )
Original file line number Diff line number Diff line change 66
77def install (cls ):
88 """
9- Class decorator for installation on sys.meta_path.
9+ Install cls on sys.meta_path.
1010
1111 Adds the backport DistributionFinder to sys.meta_path and
1212 attempts to disable the finder functionality of the stdlib
1313 DistributionFinder.
14+
15+ Call this only once the calling module is fully initialized, from
16+ its end. The installed finder resolves PathDistribution from that
17+ module's globals, so installing it any earlier publishes a finder
18+ that anything walking sys.meta_path would trip over (#525).
19+
20+ Returns nothing, so that use as a class decorator fails outright
21+ rather than reopening that window.
1422 """
1523 sys .meta_path .append (cls ())
1624 disable_stdlib_finder ()
17- return cls
1825
1926
2027def disable_stdlib_finder ():
Original file line number Diff line number Diff line change 1+ ``MetadataPathFinder `` is now installed on ``sys.meta_path `` only after the
2+ module is fully initialized. Previously it was installed partway through, so
3+ anything walking ``sys.meta_path `` during that window, such as another thread
4+ calling ``entry_points() ``, would raise ``NameError: name 'PathDistribution'
5+ is not defined ``. An import interrupted in that window left the half-built
6+ finder installed for the life of the process, failing every subsequent lookup.
Original file line number Diff line number Diff line change 1+ """
2+ The finder installed on ``sys.meta_path`` is particular to the backport;
3+ the stdlib reaches ``MetadataPathFinder`` through its own ``PathFinder``
4+ and so never publishes a finder of its own (#525).
5+ """
6+
7+ import contextlib
8+ import importlib .util
9+ import os
10+ import sys
11+ import unittest
12+ from unittest import mock
13+
14+ from importlib_metadata import _compat
15+
16+
17+ class FinderInstallTests (unittest .TestCase ):
18+ def test_installed_after_module_initialized (self ):
19+ """
20+ Install the finder only after the module can satisfy it.
21+
22+ An installed finder resolves ``PathDistribution`` from the module
23+ globals, so a finder installed before the module body completes is
24+ one that anything walking ``sys.meta_path`` would trip over.
25+ """
26+ snapshots = []
27+
28+ def capture (cls ):
29+ snapshots .append (set (sys ._getframe (1 ).f_globals ))
30+
31+ spec = importlib .util .find_spec ('importlib_metadata' )
32+ with contextlib .ExitStack () as stack :
33+ stack .enter_context (mock .patch .object (_compat , 'install' , capture ))
34+ # Don't let the re-executed module leak a fork handler.
35+ stack .enter_context (mock .patch .object (os , 'register_at_fork' , create = True ))
36+ spec .loader .exec_module (importlib .util .module_from_spec (spec ))
37+
38+ (globals_at_install ,) = snapshots
39+ assert 'PathDistribution' in globals_at_install
You can’t perform that action at this time.
0 commit comments