|
|
|
@ -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 Cython 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 |