66"""
77
88import dataclasses
9+ import functools
910import pathlib
1011import re
1112import shutil
@@ -290,6 +291,7 @@ def whichprovides(cls, filepaths: typing.Collection[str]) -> dict[str, ProvidedB
290291 return results
291292
292293
294+ @functools .cache
293295def _package_providers () -> list [type [PackageProvider ]]:
294296 """Returns a list of package providers sorted in
295297 the order that they should be attempted.
@@ -305,6 +307,25 @@ def all_subclasses(cls):
305307 return sorted (all_subclasses (PackageProvider ), key = lambda p : p ._resolve_order )
306308
307309
310+ # Cache the value of PackageProvider.is_available()
311+ _PACKAGE_PROVIDERS_IS_AVAILABLE : dict [type [PackageProvider ], bool ] = {}
312+
313+
314+ def _available_package_providers () -> (
315+ typing .Generator [type [PackageProvider ], None , None ]
316+ ):
317+ """We use a generator here because PackageProviders might not
318+ all need to be queried for 'is_available()' if 'whichprovides()'
319+ is able to find matches for all file paths.
320+ """
321+ values_cache = _PACKAGE_PROVIDERS_IS_AVAILABLE
322+ for package_provider in _package_providers ():
323+ if package_provider not in values_cache :
324+ values_cache [package_provider ] = package_provider .is_available ()
325+ if values_cache [package_provider ]:
326+ yield package_provider
327+
328+
308329def whichprovides (filepath : typing .Union [str , list [str ]]) -> dict [str , ProvidedBy ]:
309330 """Return a package URL (PURL) for the package that provides a file"""
310331 if isinstance (filepath , str ):
@@ -318,12 +339,10 @@ def whichprovides(filepath: typing.Union[str, list[str]]) -> dict[str, ProvidedB
318339 str (pathlib .Path (filepath ).resolve ()): filepath for filepath in filepaths
319340 }
320341 filepath_provided_by : dict [str , ProvidedBy ] = {}
321- for package_provider in _package_providers ():
342+ for package_provider in _available_package_providers ():
322343 remaining = set (resolved_filepaths ) - set (filepath_provided_by )
323344 if not remaining :
324345 break
325- if not package_provider .is_available ():
326- continue
327346 results = package_provider .whichprovides (remaining )
328347 filepath_provided_by .update (results )
329348
0 commit comments