mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Add `sphinx.util._importer` (#12762)
This commit is contained in:
@@ -13,6 +13,10 @@ Incompatible changes
|
||||
Deprecated
|
||||
----------
|
||||
|
||||
* #12762: Deprecate ``sphinx.util.import_object``.
|
||||
Use :py:func:`importlib.import_module` instead.
|
||||
Patch by Adam Turner.
|
||||
|
||||
Features added
|
||||
--------------
|
||||
|
||||
|
||||
@@ -22,6 +22,11 @@ The following is a list of deprecated interfaces.
|
||||
- Removed
|
||||
- Alternatives
|
||||
|
||||
* - ``sphinx.util.import_object``
|
||||
- 8.1
|
||||
- 10.0
|
||||
- ``importlib.import_module``
|
||||
|
||||
* - ``sphinx.ext.intersphinx.normalize_intersphinx_mapping``
|
||||
- 8.0
|
||||
- 10.0
|
||||
|
||||
@@ -19,10 +19,10 @@ from sphinx.locale import __
|
||||
from sphinx.util import (
|
||||
UnicodeDecodeErrorHandler,
|
||||
get_filetype,
|
||||
import_object,
|
||||
logging,
|
||||
rst,
|
||||
)
|
||||
from sphinx.util._importer import import_object
|
||||
from sphinx.util.build_phase import BuildPhase
|
||||
from sphinx.util.console import bold
|
||||
from sphinx.util.display import progress_message, status_iterator
|
||||
@@ -139,8 +139,11 @@ class Builder:
|
||||
def create_template_bridge(self) -> None:
|
||||
"""Return the template bridge configured."""
|
||||
if self.config.template_bridge:
|
||||
self.templates = import_object(self.config.template_bridge,
|
||||
'template_bridge setting')()
|
||||
template_bridge_cls = import_object(
|
||||
self.config.template_bridge,
|
||||
source='template_bridge setting'
|
||||
)
|
||||
self.templates = template_bridge_cls()
|
||||
else:
|
||||
from sphinx.jinja2glue import BuiltinTemplateLoader
|
||||
self.templates = BuiltinTemplateLoader()
|
||||
|
||||
@@ -29,7 +29,7 @@ except ImportError:
|
||||
|
||||
from sphinx.errors import ExtensionError, SphinxError
|
||||
from sphinx.search import SearchLanguage
|
||||
from sphinx.util import import_object
|
||||
from sphinx.util._importer import import_object
|
||||
|
||||
|
||||
class BaseSplitter:
|
||||
@@ -505,12 +505,18 @@ class SearchJapanese(SearchLanguage):
|
||||
language_name = 'Japanese'
|
||||
|
||||
def init(self, options: dict[str, str]) -> None:
|
||||
dotted_path = options.get('type', 'sphinx.search.ja.DefaultSplitter')
|
||||
try:
|
||||
self.splitter = import_object(dotted_path)(options)
|
||||
except ExtensionError as exc:
|
||||
raise ExtensionError("Splitter module %r can't be imported" %
|
||||
dotted_path) from exc
|
||||
dotted_path = options.get('type')
|
||||
if dotted_path is None:
|
||||
self.splitter = DefaultSplitter(options)
|
||||
else:
|
||||
try:
|
||||
splitter_cls = import_object(
|
||||
dotted_path, "html_search_options['type'] setting"
|
||||
)
|
||||
self.splitter = splitter_cls(options)
|
||||
except ExtensionError as exc:
|
||||
msg = f"Splitter module {dotted_path!r} can't be imported"
|
||||
raise ExtensionError(msg) from exc
|
||||
|
||||
def split(self, input: str) -> list[str]:
|
||||
return self.splitter.split(input)
|
||||
|
||||
@@ -6,15 +6,14 @@ import hashlib
|
||||
import os
|
||||
import posixpath
|
||||
import re
|
||||
from importlib import import_module
|
||||
from os import path
|
||||
from typing import IO, Any
|
||||
from urllib.parse import parse_qsl, quote_plus, urlencode, urlsplit, urlunsplit
|
||||
|
||||
from sphinx.errors import ExtensionError, FiletypeNotFoundError
|
||||
from sphinx.errors import FiletypeNotFoundError
|
||||
from sphinx.locale import __
|
||||
from sphinx.util import _importer, logging
|
||||
from sphinx.util import index_entries as _index_entries
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.console import strip_colors # NoQA: F401
|
||||
from sphinx.util.matching import patfilter # NoQA: F401
|
||||
from sphinx.util.nodes import ( # NoQA: F401
|
||||
@@ -217,27 +216,6 @@ def parselinenos(spec: str, total: int) -> list[int]:
|
||||
return items
|
||||
|
||||
|
||||
def import_object(objname: str, source: str | None = None) -> Any:
|
||||
"""Import python object by qualname."""
|
||||
try:
|
||||
objpath = objname.split('.')
|
||||
modname = objpath.pop(0)
|
||||
obj = import_module(modname)
|
||||
for name in objpath:
|
||||
modname += '.' + name
|
||||
try:
|
||||
obj = getattr(obj, name)
|
||||
except AttributeError:
|
||||
obj = import_module(modname)
|
||||
|
||||
return obj
|
||||
except (AttributeError, ImportError) as exc:
|
||||
if source:
|
||||
raise ExtensionError('Could not import %s (needed for %s)' %
|
||||
(objname, source), exc) from exc
|
||||
raise ExtensionError('Could not import %s' % objname, exc) from exc
|
||||
|
||||
|
||||
def encode_uri(uri: str) -> str:
|
||||
split = list(urlsplit(uri))
|
||||
split[1] = split[1].encode('idna').decode('ascii')
|
||||
@@ -262,6 +240,7 @@ _DEPRECATED_OBJECTS: dict[str, tuple[Any, str, tuple[int, int]]] = {
|
||||
(9, 0)),
|
||||
'md5': (_md5, '', (9, 0)),
|
||||
'sha1': (_sha1, '', (9, 0)),
|
||||
'import_object': (_importer.import_object, '', (10, 0)),
|
||||
}
|
||||
|
||||
|
||||
|
||||
27
sphinx/util/_importer.py
Normal file
27
sphinx/util/_importer.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from importlib import import_module
|
||||
from typing import Any
|
||||
|
||||
from sphinx.errors import ExtensionError
|
||||
|
||||
|
||||
def import_object(object_name: str, /, source: str = '') -> Any:
|
||||
"""Import python object by qualname."""
|
||||
obj_path = object_name.split('.')
|
||||
module_name = obj_path.pop(0)
|
||||
try:
|
||||
obj = import_module(module_name)
|
||||
for name in obj_path:
|
||||
module_name += '.' + name
|
||||
try:
|
||||
obj = getattr(obj, name)
|
||||
except AttributeError:
|
||||
obj = import_module(module_name)
|
||||
except (AttributeError, ImportError) as exc:
|
||||
if source:
|
||||
msg = f'Could not import {object_name} (needed for {source})'
|
||||
raise ExtensionError(msg, exc) from exc
|
||||
msg = f'Could not import {object_name}'
|
||||
raise ExtensionError(msg, exc) from exc
|
||||
return obj
|
||||
@@ -5,8 +5,7 @@ import tempfile
|
||||
|
||||
import pytest
|
||||
|
||||
from sphinx.errors import ExtensionError
|
||||
from sphinx.util import encode_uri, import_object, parselinenos
|
||||
from sphinx.util import encode_uri, parselinenos
|
||||
from sphinx.util.osutil import ensuredir
|
||||
|
||||
|
||||
@@ -40,26 +39,6 @@ def test_ensuredir():
|
||||
assert os.path.isdir(path)
|
||||
|
||||
|
||||
def test_import_object():
|
||||
module = import_object('sphinx')
|
||||
assert module.__name__ == 'sphinx'
|
||||
|
||||
module = import_object('sphinx.application')
|
||||
assert module.__name__ == 'sphinx.application'
|
||||
|
||||
obj = import_object('sphinx.application.Sphinx')
|
||||
assert obj.__name__ == 'Sphinx'
|
||||
|
||||
with pytest.raises(ExtensionError) as exc:
|
||||
import_object('sphinx.unknown_module')
|
||||
assert exc.value.args[0] == 'Could not import sphinx.unknown_module'
|
||||
|
||||
with pytest.raises(ExtensionError) as exc:
|
||||
import_object('sphinx.unknown_module', 'my extension')
|
||||
expected = 'Could not import sphinx.unknown_module (needed for my extension)'
|
||||
assert exc.value.args[0] == expected
|
||||
|
||||
|
||||
def test_parselinenos():
|
||||
assert parselinenos('1,2,3', 10) == [0, 1, 2]
|
||||
assert parselinenos('4, 5, 6', 10) == [3, 4, 5]
|
||||
|
||||
26
tests/test_util/test_util_importer.py
Normal file
26
tests/test_util/test_util_importer.py
Normal file
@@ -0,0 +1,26 @@
|
||||
"""Test sphinx.util._importer."""
|
||||
|
||||
import pytest
|
||||
|
||||
from sphinx.errors import ExtensionError
|
||||
from sphinx.util._importer import import_object
|
||||
|
||||
|
||||
def test_import_object():
|
||||
module = import_object('sphinx')
|
||||
assert module.__name__ == 'sphinx'
|
||||
|
||||
module = import_object('sphinx.application')
|
||||
assert module.__name__ == 'sphinx.application'
|
||||
|
||||
obj = import_object('sphinx.application.Sphinx')
|
||||
assert obj.__name__ == 'Sphinx'
|
||||
|
||||
with pytest.raises(ExtensionError) as exc:
|
||||
import_object('sphinx.unknown_module')
|
||||
assert exc.value.args[0] == 'Could not import sphinx.unknown_module'
|
||||
|
||||
with pytest.raises(ExtensionError) as exc:
|
||||
import_object('sphinx.unknown_module', 'my extension')
|
||||
expected = 'Could not import sphinx.unknown_module (needed for my extension)'
|
||||
assert exc.value.args[0] == expected
|
||||
Reference in New Issue
Block a user