Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/importlib_metadata/_compat/localize.py: 46%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

35 statements  

1from __future__ import annotations 

2 

3import warnings 

4from typing import TYPE_CHECKING, cast 

5 

6import importlib_metadata 

7 

8from ..compat import py312 

9from ..compat.py314 import lazy_importer 

10 

11__lazy_modules__ = {'importlib.metadata', 'importlib_metadata'} 

12""" 

13Imports to defer until first use (PEP 810). 

14 

15Only a foreign distribution reaches the code that reads these, so the 

16cost belongs to that caller and not to every import of the package. 

17""" 

18 

19with lazy_importer(): 

20 import importlib.metadata 

21 

22 from .. import _adapters 

23 

24if TYPE_CHECKING: 

25 import email.message 

26 

27 from .._meta import SimplePath 

28 

29 

30def dist( 

31 dist: importlib_metadata.Distribution | importlib.metadata.Distribution, 

32) -> importlib_metadata.Distribution: 

33 """ 

34 Ensure dist is an :class:`importlib_metadata.Distribution`. 

35 

36 >>> import importlib.metadata 

37 >>> stdlib = importlib.metadata.PathDistribution('foo') 

38 >>> type(stdlib) 

39 <class 'importlib.metadata.PathDistribution'> 

40 >>> local = dist(stdlib) 

41 >>> type(local) 

42 <class 'importlib_metadata.PathDistribution'> 

43 

44 A provider of its own Distribution subclass is passed through, as 

45 long as it implements the interface consumers expect. 

46 

47 >>> class CustomDist(importlib.metadata.Distribution): 

48 ... def read_text(self, name): 

49 ... return 

50 ... def locate_file(self, name): 

51 ... return 

52 >>> custom = CustomDist() 

53 >>> dist(custom) is custom 

54 True 

55 

56 One that doesn't is passed through too, but names what it omits. 

57 

58 >>> class Incomplete: 

59 ... def read_text(self, name): 

60 ... return 

61 >>> import pytest 

62 >>> with pytest.warns(UserWarning, match="is missing at, discover"): 

63 ... local = dist(Incomplete()) 

64 >>> local is not None 

65 True 

66 """ 

67 if isinstance(dist, importlib_metadata.Distribution): 

68 return dist 

69 

70 if isinstance(dist, importlib.metadata.PathDistribution): 

71 return importlib_metadata.PathDistribution(cast('SimplePath', dist._path)) 

72 # Warn on what the interface requires and the class omits, rather than 

73 # on the class not being one of ours; a provider can act on the former. 

74 # Also tolerates pytest having replaced importlib_metadata, as the 

75 # replaced class still conforms. 

76 if not py312.conforms(dist, importlib_metadata.IDistribution): 

77 warnings.warn( 

78 f"Distribution subclass {dist.__class__} is missing " 

79 f"{', '.join(unimplemented(dist))}" 

80 ) 

81 return cast('importlib_metadata.Distribution', dist) 

82 

83 

84def unimplemented(dist) -> list[str]: 

85 """ 

86 The members IDistribution requires that dist's class doesn't supply. 

87 """ 

88 required = py312.required(importlib_metadata.IDistribution) 

89 return sorted(attr for attr in required if not hasattr(type(dist), attr)) 

90 

91 

92def message( 

93 input: _adapters.Message | email.message.Message, 

94) -> _adapters.Message: 

95 """ 

96 Ensure a message is adapted to an importlib_metadata.Message. 

97 

98 >>> import email.message 

99 >>> stdlib = email.message.Message() 

100 >>> local = message(stdlib) 

101 >>> type(local) 

102 <class 'importlib_metadata._adapters.Message'> 

103 """ 

104 if isinstance(input, _adapters.Message): 

105 return input 

106 return _adapters.Message(input) 

107 

108 

109def package_path( 

110 input: importlib_metadata.PackagePath | importlib.metadata.PackagePath, 

111) -> importlib_metadata.PackagePath: 

112 """ 

113 Ensure a package path is adapted to an importlib_metadata.PackagePath. 

114 

115 >>> import importlib.metadata 

116 >>> stdlib = importlib.metadata.PackagePath('foo') 

117 >>> type(stdlib) 

118 <class 'importlib.metadata.PackagePath'> 

119 >>> local = package_path(stdlib) 

120 >>> type(local) 

121 <class 'importlib_metadata.PackagePath'> 

122 """ 

123 if isinstance(input, importlib_metadata.PackagePath): 

124 return input 

125 replacement = importlib_metadata.PackagePath(input) 

126 vars(replacement).update(vars(input)) 

127 return replacement