33from __future__ import annotations
44
55import concurrent .futures
6+ import io
67import os .path
78import posixpath
89import time
2021if TYPE_CHECKING :
2122 from pathlib import Path
2223
23- from urllib3 .response import HTTPResponse
24-
2524 from sphinx .application import Sphinx
2625 from sphinx .config import Config
2726 from sphinx .ext .intersphinx ._shared import (
3130 InventoryName ,
3231 InventoryURI ,
3332 )
34- from sphinx .util .typing import Inventory , _ReadableStream
33+ from sphinx .util .typing import Inventory
3534
3635
3736def validate_intersphinx_mapping (app : Sphinx , config : Config ) -> None :
@@ -297,13 +296,38 @@ def _fetch_inventory(
297296 # and *inv_location* (actual location of the inventory file)
298297 # can be local or remote URIs
299298 if '://' in target_uri :
300- # case: inv URI points to remote resource; strip any existing auth
299+ # inv URI points to remote resource; strip any existing auth
301300 target_uri = _strip_basic_auth (target_uri )
301+ if '://' in inv_location :
302+ raw_data , target_uri = _fetch_inventory_url (
303+ target_uri = target_uri , inv_location = inv_location , config = config
304+ )
305+ else :
306+ raw_data = _fetch_inventory_file (inv_location = inv_location , srcdir = srcdir )
307+
308+ stream = io .BytesIO (raw_data )
302309 try :
303- if '://' in inv_location :
304- f : _ReadableStream [bytes ] = _read_from_url (inv_location , config = config )
305- else :
306- f = open (os .path .join (srcdir , inv_location ), 'rb' ) # NoQA: SIM115
310+ invdata = InventoryFile .load (stream , target_uri , posixpath .join )
311+ except ValueError as exc :
312+ msg = f'unknown or unsupported inventory version: { exc !r} '
313+ raise ValueError (msg ) from exc
314+ return invdata
315+
316+
317+ def _fetch_inventory_url (
318+ * , target_uri : InventoryURI , inv_location : str , config : Config
319+ ) -> tuple [bytes , str ]:
320+ try :
321+ with requests .get (
322+ inv_location ,
323+ stream = True ,
324+ timeout = config .intersphinx_timeout ,
325+ _user_agent = config .user_agent ,
326+ _tls_info = (config .tls_verify , config .tls_cacerts ),
327+ ) as r :
328+ r .raise_for_status ()
329+ raw_data = r .content
330+ new_inv_location = r .url
307331 except Exception as err :
308332 err .args = (
309333 'intersphinx inventory %r not fetchable due to %s: %s' ,
@@ -312,25 +336,25 @@ def _fetch_inventory(
312336 str (err ),
313337 )
314338 raise
339+
340+ if inv_location != new_inv_location :
341+ msg = __ ('intersphinx inventory has moved: %s -> %s' )
342+ LOGGER .info (msg , inv_location , new_inv_location )
343+
344+ if target_uri in {
345+ inv_location ,
346+ os .path .dirname (inv_location ),
347+ os .path .dirname (inv_location ) + '/' ,
348+ }:
349+ target_uri = os .path .dirname (new_inv_location )
350+
351+ return raw_data , target_uri
352+
353+
354+ def _fetch_inventory_file (* , inv_location : str , srcdir : Path ) -> bytes :
315355 try :
316- if hasattr (f , 'url' ):
317- new_inv_location = f .url
318- if inv_location != new_inv_location :
319- msg = __ ('intersphinx inventory has moved: %s -> %s' )
320- LOGGER .info (msg , inv_location , new_inv_location )
321-
322- if target_uri in {
323- inv_location ,
324- os .path .dirname (inv_location ),
325- os .path .dirname (inv_location ) + '/' ,
326- }:
327- target_uri = os .path .dirname (new_inv_location )
328- with f :
329- try :
330- invdata = InventoryFile .load (f , target_uri , posixpath .join )
331- except ValueError as exc :
332- msg = f'unknown or unsupported inventory version: { exc !r} '
333- raise ValueError (msg ) from exc
356+ with open (srcdir / inv_location , 'rb' ) as f :
357+ raw_data = f .read ()
334358 except Exception as err :
335359 err .args = (
336360 'intersphinx inventory %r not readable due to %s: %s' ,
@@ -339,8 +363,7 @@ def _fetch_inventory(
339363 str (err ),
340364 )
341365 raise
342- else :
343- return invdata
366+ return raw_data
344367
345368
346369def _get_safe_url (url : str ) -> str :
@@ -387,37 +410,3 @@ def _strip_basic_auth(url: str) -> str:
387410 if '@' in frags [1 ]:
388411 frags [1 ] = frags [1 ].split ('@' )[1 ]
389412 return urlunsplit (frags )
390-
391-
392- def _read_from_url (url : str , * , config : Config ) -> HTTPResponse :
393- """Reads data from *url* with an HTTP *GET*.
394-
395- This function supports fetching from resources which use basic HTTP auth as
396- laid out by RFC1738 § 3.1. See § 5 for grammar definitions for URLs.
397-
398- .. seealso:
399-
400- https://www.ietf.org/rfc/rfc1738.txt
401-
402- :param url: URL of an HTTP resource
403- :type url: ``str``
404-
405- :return: data read from resource described by *url*
406- :rtype: ``file``-like object
407- """
408- r = requests .get (
409- url ,
410- stream = True ,
411- timeout = config .intersphinx_timeout ,
412- _user_agent = config .user_agent ,
413- _tls_info = (config .tls_verify , config .tls_cacerts ),
414- )
415- r .raise_for_status ()
416-
417- # For inv_location / new_inv_location
418- r .raw .url = r .url # type: ignore[union-attr]
419-
420- # Decode content-body based on the header.
421- # xref: https://github.com/psf/requests/issues/2155
422- r .raw .decode_content = True
423- return r .raw
0 commit comments