| @ -1,38 +1,83 @@ | |||
| __all__ = ["audit", "addaudithook"] | |||
| import os | |||
| import sys | |||
| # Python 3.8+ | |||
| # DEV: We could check `sys.version_info >= (3, 8)`, but if auditing ever gets | |||
| # back ported we want to take advantage of that | |||
| std_audit = None | |||
| std_addaudithook = None | |||
| if hasattr(sys, "audit") and hasattr(sys, "addaudithook"): | |||
| audit = sys.audit | |||
| addaudithook = sys.addaudithook | |||
| else: | |||
| std_audit = sys.audit | |||
| std_addaudithook = sys.addaudithook | |||
| # Try to import CPython version | |||
| csysaudit_audit = None | |||
| csysaudit_addaudithook = None | |||
| try: | |||
| from . import _csysaudit | |||
| csysaudit_audit = _csysaudit.audit | |||
| csysaudit_addaudithook = _csysaudit.addaudithook | |||
| except ImportError: | |||
| pass | |||
| # Pure-python implementation | |||
| _hooks = list() | |||
| def py_audit(event, *args): | |||
| global _hooks | |||
| for hook in _hooks: | |||
| hook(event, args) | |||
| def py_addaudithook(callback): | |||
| global _hooks | |||
| # https://docs.python.org/3.8/library/sys.html#sys.addaudithook | |||
| # Raise an auditing event `sys.addaudithook` with no arguments. | |||
| # If any existing hooks raise an exception derived from RuntimeError, | |||
| # the new hook will not be added and the exception suppressed. | |||
| # As a result, callers cannot assume that their hook has been added | |||
| # unless they control all existing hooks. | |||
| try: | |||
| from ._csysaudit import audit, addaudithook | |||
| except ImportError: | |||
| _hooks = list() | |||
| def audit(event, *args): | |||
| global _hooks | |||
| # Grab a copy of hooks so we don't need to lock here | |||
| for hook in _hooks[:]: | |||
| hook(event, args) | |||
| def addaudithook(callback): | |||
| global _hooks | |||
| # https://docs.python.org/3.8/library/sys.html#sys.addaudithook | |||
| # Raise an auditing event `sys.addaudithook` with no arguments. | |||
| # If any existing hooks raise an exception derived from RuntimeError, | |||
| # the new hook will not be added and the exception suppressed. | |||
| # As a result, callers cannot assume that their hook has been added | |||
| # unless they control all existing hooks. | |||
| try: | |||
| audit("sys.addaudithook") | |||
| except RuntimeError: | |||
| return | |||
| if callback not in _hooks: | |||
| _hooks.append(callback) | |||
| audit("sys.addaudithook") | |||
| except RuntimeError: | |||
| return | |||
| _hooks.append(callback) | |||
| # Choose the best implementation | |||
| # DEV: We still import/create all of them | |||
| # so we can easily access each implementation | |||
| # for testing | |||
| SYSAUDIT_IMPL = os.getenv("SYSAUDIT_IMPL") | |||
| if SYSAUDIT_IMPL: | |||
| if SYSAUDIT_IMPL == "stdlib": | |||
| audit = std_audit | |||
| addaudithook = std_addaudithook | |||
| elif SYSAUDIT_IMPL == "csysaudit": | |||
| audit = csysaudit_audit | |||
| addaudithook = csysaudit_addaudithook | |||
| elif SYSAUDIT_IMPL == "pysysaudit": | |||
| audit = py_audit | |||
| addaudithook = py_addaudithook | |||
| else: | |||
| raise ValueError( | |||
| "SYSAUDIT_IMPL must be one of ('stdlib', 'csysaudit', 'pysysaudit')" | |||
| ) | |||
| else: | |||
| if std_audit and std_addaudithook: | |||
| audit = std_audit | |||
| addaudithook = std_addaudithook | |||
| elif csysaudit_audit and csysaudit_addaudithook: | |||
| audit = csysaudit_audit | |||
| addaudithook = csysaudit_addaudithook | |||
| else: | |||
| audit = py_audit | |||
| addaudithook = py_addaudithook | |||