72 lines
1.9 KiB
Python
72 lines
1.9 KiB
Python
# Taken from datalad_next.patches
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from importlib import import_module
|
|
from typing import Any
|
|
|
|
lgr = logging.getLogger('dump_things_service')
|
|
|
|
|
|
def apply_patch(
|
|
modname: str,
|
|
objname: str | None,
|
|
attrname: str,
|
|
patch: Any,
|
|
msg: str | None = None,
|
|
*,
|
|
expect_attr_present: bool = True,
|
|
):
|
|
"""
|
|
Monkey patch helper
|
|
|
|
Parameters
|
|
----------
|
|
modname: str
|
|
Importable name of the module with the patch target.
|
|
objname: str or None
|
|
If `None`, patch will target an attribute of the module,
|
|
otherwise an object name within the module can be specified.
|
|
attrname: str
|
|
Name of the attribute to replace with the patch, either
|
|
in the module or the given object (see ``objname``)
|
|
patch:
|
|
The identified attribute will be replaced with this object.
|
|
msg: str or None
|
|
If given, a debug-level log message with this text will be
|
|
emitted, otherwise a default message is generated.
|
|
expect_attr_present: bool
|
|
If True (default) an exception is raised when the target
|
|
attribute is not found.
|
|
|
|
Returns
|
|
-------
|
|
object
|
|
The original, unpatched attribute -- if ``expect_attr_present``
|
|
is enabled, or ``None`` otherwise.
|
|
|
|
Raises
|
|
------
|
|
ImportError
|
|
When the target module cannot be imported
|
|
AttributedError
|
|
When the target object is not found, or the target attribute is
|
|
not found.
|
|
"""
|
|
orig_attr = None
|
|
|
|
msg = msg or f'Apply patch to {modname}.{attrname}'
|
|
lgr.debug(msg)
|
|
|
|
# we want to fail on ImportError
|
|
mod = import_module(modname)
|
|
obj = getattr(mod, objname) if objname else mod
|
|
|
|
if expect_attr_present:
|
|
# we want to fail on a missing attribute/object
|
|
orig_attr = getattr(obj, attrname)
|
|
|
|
setattr(obj, attrname, patch)
|
|
|
|
return orig_attr
|