1212import functools
1313import itertools
1414import posixpath
15+ import contextlib
1516import collections .abc
1617
1718from ._compat import (
@@ -602,13 +603,10 @@ class FastPath:
602603
603604 @functools .lru_cache () # type: ignore
604605 def __new__ (cls , root ):
605- self = object ().__new__ (cls )
606+ return super ().__new__ (cls )
607+
608+ def __init__ (self , root ):
606609 self .root = str (root )
607- self .base = os .path .basename (self .root ).lower ()
608- self .last_mtime = - 1
609- self .infos = {}
610- self .eggs = {}
611- return self
612610
613611 def joinpath (self , child ):
614612 return pathlib .Path (self .root , child )
@@ -624,47 +622,54 @@ def zip_children(self):
624622 zip_path = zipp .Path (self .root )
625623 names = zip_path .root .namelist ()
626624 self .joinpath = zip_path .joinpath
625+
627626 return dict .fromkeys (child .split (posixpath .sep , 1 )[0 ] for child in names )
628627
629- def update_cache (self ):
630- root = self .root or "."
631- try :
632- mtime = os .stat (root ).st_mtime
633- except OSError :
634- self .infos .clear ()
635- self .eggs .clear ()
636- self .last_mtime = - 1
637- return
638- if mtime == self .last_mtime :
639- return
640- self .infos .clear ()
641- self .eggs .clear ()
642- base_is_egg = self .base .endswith (".egg" )
643- for child in self .children ():
628+ def search (self , name ):
629+ return self .lookup (self .mtime ).search (name )
630+
631+ @property
632+ def mtime (self ):
633+ with contextlib .suppress (OSError ):
634+ return os .stat (self .root ).st_mtime
635+ FastPath .lookup .cache_clear ()
636+
637+ @functools .lru_cache ()
638+ def lookup (self , mtime ):
639+ return Lookup (self )
640+
641+
642+ class Lookup :
643+ def __init__ (self , path : FastPath ):
644+ base = os .path .basename (path .root ).lower ()
645+ base_is_egg = base .endswith (".egg" )
646+ self .infos = collections .defaultdict (list )
647+ self .eggs = collections .defaultdict (list )
648+
649+ for child in path .children ():
644650 low = child .lower ()
645651 if low .endswith ((".dist-info" , ".egg-info" )):
646652 # rpartition is faster than splitext and suitable for this purpose.
647653 name = low .rpartition ("." )[0 ].partition ("-" )[0 ]
648654 normalized = Prepared .normalize (name )
649- self .infos . setdefault ( normalized , []) .append (child )
655+ self .infos [ normalized ] .append (path . joinpath ( child ) )
650656 elif base_is_egg and low == "egg-info" :
651- name = self . base .rpartition ("." )[0 ].partition ("-" )[0 ]
657+ name = base .rpartition ("." )[0 ].partition ("-" )[0 ]
652658 legacy_normalized = Prepared .legacy_normalize (name )
653- self .eggs .setdefault (legacy_normalized , []).append (child )
654- self .last_mtime = mtime
659+ self .eggs [legacy_normalized ].append (path .joinpath (child ))
655660
656661 def search (self , prepared ):
657- self . update_cache ()
658- if prepared .name :
659- infos = self . infos . get ( prepared . normalized , [])
660- yield from map (self .joinpath , infos )
661- eggs = self . eggs . get ( prepared . legacy_normalized , [] )
662- yield from map ( self . joinpath , eggs )
663- else :
664- for infos in self . infos . values ():
665- yield from map (self .joinpath , infos )
666- for eggs in self . eggs . values ():
667- yield from map ( self . joinpath , eggs )
662+ infos = (
663+ self . infos [ prepared .normalized ]
664+ if prepared
665+ else itertools . chain . from_iterable (self .infos . values () )
666+ )
667+ eggs = (
668+ self . eggs [ prepared . legacy_normalized ]
669+ if prepared
670+ else itertools . chain . from_iterable (self .eggs . values () )
671+ )
672+ return itertools . chain ( infos , eggs )
668673
669674
670675class Prepared :
@@ -697,6 +702,9 @@ def legacy_normalize(name):
697702 """
698703 return name .lower ().replace ('-' , '_' )
699704
705+ def __bool__ (self ):
706+ return bool (self .name )
707+
700708
701709@install
702710class MetadataPathFinder (NullFinder , DistributionFinder ):
0 commit comments