Skip to content

Commit a42fdc0

Browse files
committed
Defer mimetypes, locale, glob and runpy to their use sites
Four more single-use stdlib imports on the `import IPython` path: - `mimetypes` (and the `winreg`/`_winapi` probing it does) in `IPython.core.display`, used only to guess the type of an embedded video; - `locale` in `IPython.utils.encoding`, consulted only when the stream encoding is missing or ascii -- which it usually is not, so this was an import for a call that mostly never happened; - `glob` in `IPython.utils.path`, used only by `shellglob()`; - `runpy` in `IPython.core.interactiveshell`, used only by `safe_run_module()`. Making `mimetypes` lazy surfaced that `tests/test_path.py` registers a stub `winreg` module in `sys.modules` and never removes it, so any stdlib module importing `winreg` later in the session -- `mimetypes` does, to decide whether to read the Windows registry -- found the stub and tried to use it. The only test that needs `winreg` runs on Windows only, so import it there and leave `sys.modules` alone everywhere else. The encoding tests reached `locale` through `IPython.utils.encoding.locale`; patch the `locale` module itself, which is the same object either way.
1 parent cab037e commit a42fdc0

6 files changed

Lines changed: 19 additions & 28 deletions

File tree

IPython/core/display.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from enum import Enum
99
from dataclasses import dataclass, KW_ONLY
1010
from binascii import b2a_base64, hexlify
11-
import mimetypes
1211
import os
1312
import warnings
1413
from copy import deepcopy
@@ -1239,6 +1238,8 @@ def _repr_html_(self):
12391238
mimetype = self.mimetype
12401239
if self.filename is not None:
12411240
if not mimetype:
1241+
import mimetypes
1242+
12421243
mimetype, _ = mimetypes.guess_type(self.filename)
12431244

12441245
with open(self.filename, 'rb') as f:

IPython/core/interactiveshell.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import functools
1818
import os
1919
import re
20-
import runpy
2120
import sys
2221
import types
2322
import warnings
@@ -3148,6 +3147,8 @@ def safe_run_module(self, mod_name, where):
31483147
where : dict
31493148
The globals namespace.
31503149
"""
3150+
import runpy
3151+
31513152
try:
31523153
try:
31533154
where.update(

IPython/utils/encoding.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
# Imports
1616
# -----------------------------------------------------------------------------
1717
import sys
18-
import locale
1918
import warnings
2019
from typing import Any
2120

@@ -67,6 +66,8 @@ def getdefaultencoding(prefer_stream: object | bool = _sentinel) -> str:
6766
if prefer_stream:
6867
enc = get_stream_enc(sys.stdin)
6968
if not enc or enc == "ascii":
69+
import locale
70+
7071
try:
7172
# There are reports of getpreferredencoding raising errors
7273
# in some cases, which may well be fixed, but let's be conservative here.

IPython/utils/path.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import os
99
import sys
1010
import errno
11-
import glob
1211
import warnings
1312

1413
#-----------------------------------------------------------------------------
@@ -279,6 +278,8 @@ def shellglob(args):
279278
expanded = []
280279
# Do not unescape backslash in Windows as it is interpreted as
281280
# path separator:
281+
import glob
282+
282283
unescape = unescape_glob if sys.platform != 'win32' else lambda x: x
283284
for a in args:
284285
expanded.extend(glob.glob(a) or [unescape(a)])

tests/test_path.py

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import shutil
99
import sys
1010
import tempfile
11+
import importlib
1112
from contextlib import contextmanager
1213
from importlib import reload
1314
from os.path import abspath, join
@@ -27,25 +28,12 @@
2728
from IPython.testing.tools import make_tempfile
2829
from IPython.utils import path
2930

30-
# Platform-dependent imports
31-
try:
32-
import winreg as wreg
33-
except ImportError:
34-
# Fake _winreg module on non-windows platforms
35-
import types
36-
37-
wr_name = "winreg"
38-
sys.modules[wr_name] = types.ModuleType(wr_name)
39-
try:
40-
import winreg as wreg
41-
except ImportError:
42-
import _winreg as wreg
43-
44-
# Add entries that needs to be stubbed by the testing code
45-
(
46-
wreg.OpenKey,
47-
wreg.QueryValueEx,
48-
) = (None, None)
31+
# Platform-dependent imports. Only `test_get_home_dir_8` needs `winreg`, and
32+
# that test only runs on Windows; a stub module registered in `sys.modules`
33+
# here would stay there for the rest of the session, and any stdlib module
34+
# that probes for `winreg` later (`mimetypes` does) would find it and believe
35+
# it is running on Windows.
36+
wreg = importlib.import_module("winreg") if sys.platform == "win32" else None
4937

5038
# -----------------------------------------------------------------------------
5139
# Globals

tests/test_utils_misc.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Tests for small IPython.utils modules: data, timing, frame and encoding."""
22

33
import importlib
4+
import locale
45
import sys
56
import time
67
import types
@@ -199,9 +200,7 @@ def test_getdefaultencoding_from_stdin(monkeypatch):
199200

200201
def test_getdefaultencoding_prefers_locale_over_ascii(monkeypatch):
201202
monkeypatch.setattr(sys, "stdin", FakeStream("ascii"))
202-
monkeypatch.setattr(
203-
encoding_mod.locale, "getpreferredencoding", lambda: "latin-1"
204-
)
203+
monkeypatch.setattr(locale, "getpreferredencoding", lambda: "latin-1")
205204
assert encoding_mod.getdefaultencoding() == "latin-1"
206205

207206

@@ -211,13 +210,13 @@ def boom():
211210
raise RuntimeError("no locale")
212211

213212
monkeypatch.setattr(sys, "stdin", FakeStream("ascii"))
214-
monkeypatch.setattr(encoding_mod.locale, "getpreferredencoding", boom)
213+
monkeypatch.setattr(locale, "getpreferredencoding", boom)
215214
assert encoding_mod.getdefaultencoding() == "ascii"
216215

217216

218217
def test_getdefaultencoding_falls_back_to_sys(monkeypatch):
219218
monkeypatch.setattr(sys, "stdin", FakeStream(None))
220-
monkeypatch.setattr(encoding_mod.locale, "getpreferredencoding", lambda: "")
219+
monkeypatch.setattr(locale, "getpreferredencoding", lambda: "")
221220
assert encoding_mod.getdefaultencoding() == sys.getdefaultencoding()
222221

223222

0 commit comments

Comments
 (0)