@@ -47,7 +47,7 @@ def loads(
47
47
content : bytes ,
48
48
* ,
49
49
uri : str ,
50
- ) -> Inventory :
50
+ ) -> _Inventory :
51
51
format_line , _ , content = content .partition (b'\n ' )
52
52
format_line = format_line .rstrip () # remove trailing \r or spaces
53
53
if format_line == b'# Sphinx inventory version 2' :
@@ -64,14 +64,14 @@ def loads(
64
64
65
65
@classmethod
66
66
def load (cls , stream : _SupportsRead , uri : str , joinfunc : _JoinFunc ) -> Inventory :
67
- return cls .loads (stream .read (), uri = uri )
67
+ return cls .loads (stream .read (), uri = uri ). data
68
68
69
69
@classmethod
70
- def _loads_v1 (cls , lines : Sequence [str ], * , uri : str ) -> Inventory :
70
+ def _loads_v1 (cls , lines : Sequence [str ], * , uri : str ) -> _Inventory :
71
71
if len (lines ) < 2 :
72
72
msg = 'invalid inventory header: missing project name or version'
73
73
raise ValueError (msg )
74
- invdata : Inventory = {}
74
+ inv = _Inventory ({})
75
75
projname = lines [0 ].rstrip ()[11 :] # Project name
76
76
version = lines [1 ].rstrip ()[11 :] # Project version
77
77
for line in lines [2 :]:
@@ -84,25 +84,25 @@ def _loads_v1(cls, lines: Sequence[str], *, uri: str) -> Inventory:
84
84
else :
85
85
item_type = f'py:{ item_type } '
86
86
location += f'#{ name } '
87
- invdata . setdefault ( item_type , {})[ name ] = _InventoryItem (
87
+ inv [ item_type , name ] = _InventoryItem (
88
88
project_name = projname ,
89
89
project_version = version ,
90
90
uri = location ,
91
91
display_name = '-' ,
92
92
)
93
- return invdata
93
+ return inv
94
94
95
95
@classmethod
96
- def _loads_v2 (cls , inv_data : bytes , * , uri : str ) -> Inventory :
96
+ def _loads_v2 (cls , inv_data : bytes , * , uri : str ) -> _Inventory :
97
97
try :
98
98
line_1 , line_2 , check_line , compressed = inv_data .split (b'\n ' , maxsplit = 3 )
99
99
except ValueError :
100
100
msg = 'invalid inventory header: missing project name or version'
101
101
raise ValueError (msg ) from None
102
- invdata : Inventory = {}
102
+ inv = _Inventory ({})
103
103
projname = line_1 .rstrip ()[11 :].decode () # Project name
104
104
version = line_2 .rstrip ()[11 :].decode () # Project version
105
- # definition -> priority, location, display name
105
+ # definition -> ( priority, location, display name)
106
106
potential_ambiguities : dict [str , tuple [str , str , str ]] = {}
107
107
actual_ambiguities = set ()
108
108
if b'zlib' not in check_line : # '... compressed using zlib'
@@ -125,7 +125,7 @@ def _loads_v2(cls, inv_data: bytes, *, uri: str) -> Inventory:
125
125
#
126
126
# Note: To avoid the regex DoS, this is implemented in python (refs: #8175)
127
127
continue
128
- if type == 'py:module' and type in invdata and name in invdata [ type ] :
128
+ if type == 'py:module' and ( type , name ) in inv :
129
129
# due to a bug in 1.1 and below,
130
130
# two inventory entries are created
131
131
# for Python modules, and the first
@@ -154,7 +154,7 @@ def _loads_v2(cls, inv_data: bytes, *, uri: str) -> Inventory:
154
154
if location .endswith ('$' ):
155
155
location = location [:- 1 ] + name
156
156
location = posixpath .join (uri , location )
157
- invdata . setdefault ( type , {})[ name ] = _InventoryItem (
157
+ inv [ type , name ] = _InventoryItem (
158
158
project_name = projname ,
159
159
project_version = version ,
160
160
uri = location ,
@@ -168,7 +168,7 @@ def _loads_v2(cls, inv_data: bytes, *, uri: str) -> Inventory:
168
168
type = 'intersphinx' ,
169
169
subtype = 'external' ,
170
170
)
171
- return invdata
171
+ return inv
172
172
173
173
@classmethod
174
174
def dump (
@@ -206,6 +206,41 @@ def escape(string: str) -> str:
206
206
f .write (compressor .flush ())
207
207
208
208
209
+ class _Inventory :
210
+ """Inventory data in memory."""
211
+
212
+ __slots__ = ('data' ,)
213
+
214
+ data : dict [str , dict [str , _InventoryItem ]]
215
+
216
+ def __init__ (self , data : dict [str , dict [str , _InventoryItem ]], / ) -> None :
217
+ # type -> name -> _InventoryItem
218
+ self .data : dict [str , dict [str , _InventoryItem ]] = data
219
+
220
+ def __repr__ (self ) -> str :
221
+ return f'_Inventory({ self .data !r} )'
222
+
223
+ def __eq__ (self , other : object ) -> bool :
224
+ if not isinstance (other , _Inventory ):
225
+ return NotImplemented
226
+ return self .data == other .data
227
+
228
+ def __hash__ (self ) -> int :
229
+ return hash (self .data )
230
+
231
+ def __getitem__ (self , item : tuple [str , str ]) -> _InventoryItem :
232
+ obj_type , name = item
233
+ return self .data .setdefault (obj_type , {})[name ]
234
+
235
+ def __setitem__ (self , item : tuple [str , str ], value : _InventoryItem ) -> None :
236
+ obj_type , name = item
237
+ self .data .setdefault (obj_type , {})[name ] = value
238
+
239
+ def __contains__ (self , item : tuple [str , str ]) -> bool :
240
+ obj_type , name = item
241
+ return obj_type in self .data and name in self .data [obj_type ]
242
+
243
+
209
244
class _InventoryItem :
210
245
__slots__ = 'project_name' , 'project_version' , 'uri' , 'display_name'
211
246
0 commit comments