Skip to content

API reference

importpatch

Post-import hooks: run a function on a module right after it is imported.

Hooks come from three places, merged: the importpatch entry-point group of every installed distribution (name = the module to patch, value = the patcher, package.module.function), the configuration files, and :func:add. The wheel installs importpatch.pth, so :func:bootstrap runs at the start of every interpreter in the environment; IMPORTPATCH_DISABLE=1 turns it off.

A hook that cannot be imported or that raises is logged on the importpatch logger and skipped: it never fails the import of the module it patches.

add(target, patcher)

Run patcher on module target after it is imported, or now if it already is. patcher is a dotted name: a function, called as function(module), or a module, whose importpatch(target, module) is called. pkg.mod:function (the entry-point spelling) is the same patcher as pkg.mod.function. Adding the same pair twice has no effect.

Source code in src/importpatch/__init__.py
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
def add(target: str, patcher: str) -> None:
    """Run ``patcher`` on module ``target`` after it is imported, or now if it
    already is. ``patcher`` is a dotted name: a function, called as
    ``function(module)``, or a module, whose ``importpatch(target, module)``
    is called. ``pkg.mod:function`` (the entry-point spelling) is the same
    patcher as ``pkg.mod.function``. Adding the same pair twice has no
    effect."""
    patcher = _normalise(patcher)
    _install_finder()
    with _lock:
        patchers = _registry.setdefault(target, [])
        if patcher in patchers:
            return
        patchers.append(patcher)
    module = sys.modules.get(target)
    if module is not None:
        _run(target, module, [patcher])

remove(target, patcher=None)

Stop running patcher (every patcher when None) on future imports of target. Patches already applied stay applied.

Source code in src/importpatch/__init__.py
179
180
181
182
183
184
185
186
def remove(target: str, patcher: "str | None" = None) -> None:
    """Stop running ``patcher`` (every patcher when ``None``) on future imports
    of ``target``. Patches already applied stay applied."""
    with _lock:
        if patcher is None:
            _registry.pop(target, None)
        elif _normalise(patcher) in _registry.get(target, ()):
            _registry[target].remove(_normalise(patcher))

patches()

Every registered target and its patchers, in registration order.

Source code in src/importpatch/__init__.py
189
190
191
192
def patches() -> "dict[str, tuple[str, ...]]":
    """Every registered target and its patchers, in registration order."""
    with _lock:
        return {target: tuple(found) for target, found in _registry.items() if found}

bootstrap()

Install the import hook and register the patchers of the entry points, then the system configuration file, then the user's, each able to enable or disable what the earlier ones registered. Run once per interpreter by importpatch.pth; repeated calls do nothing.

System files: $IMPORTPATCH_SYSTEM_CONFIG, else %PROGRAMDATA%\pyimportpatch.cfg on Windows and /etc/pyimportpatch.cfg elsewhere. User files: $IMPORTPATCH_CONFIG, else %APPDATA%\pyimportpatch.cfg on Windows and $XDG_CONFIG_HOME/pyimportpatch.cfg (default ~/.config) elsewhere; not read in isolated mode (python -I) or with -s. Either variable may list several files separated by os.pathsep, applied in order.

Source code in src/importpatch/__init__.py
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
def bootstrap() -> None:
    """Install the import hook and register the patchers of the entry
    points, then the system configuration file, then the user's, each able
    to enable or disable what the earlier ones registered. Run once per
    interpreter by ``importpatch.pth``; repeated calls do nothing.

    System files: ``$IMPORTPATCH_SYSTEM_CONFIG``, else
    ``%PROGRAMDATA%\\pyimportpatch.cfg`` on Windows and
    ``/etc/pyimportpatch.cfg`` elsewhere. User files:
    ``$IMPORTPATCH_CONFIG``, else ``%APPDATA%\\pyimportpatch.cfg`` on Windows
    and ``$XDG_CONFIG_HOME/pyimportpatch.cfg`` (default ``~/.config``)
    elsewhere; not read in isolated mode (``python -I``) or with ``-s``.
    Either variable may list several files separated by ``os.pathsep``,
    applied in order.
    """
    global _bootstrapped
    if _bootstrapped or _disabled():
        return
    _bootstrapped = True
    _install_finder()
    entries: "list[tuple[str, str, bool]]" = []
    try:
        entries.extend((target, patcher, True) for target, patcher in _entry_points())
    except Exception:
        _log("cannot read the %s entry points", _GROUP, exc_info=True)
    paths = _system_config_paths()
    if not (sys.flags.isolated or sys.flags.no_user_site):
        paths += _config_paths()
    for path in paths:
        entries.extend(_read_config(path))
    for target, patcher in _merge(entries):
        add(target, patcher)