ImportsLogger/hijack.py

95 lines
3.6 KiB
Python

if __name__ != "__main__":
raise Exception("Fucking don't. Thanks")
from pprint import pprint
from pathlib import Path
import importlib
import inspect
import logging
import runpy
import sys
del sys.argv[0]
try:
import pretty_traceback
pretty_traceback.install()
except ImportError:
pass
logging.basicConfig(
format="[%(levelname)s] In function %(funcName)s@%(lineno)d [%(name)s] => %(message)s",
datefmt="%d/%m/%Y %H:%M:%S %p",
level=logging.DEBUG,
)
def LoggerDecorator(fun, logger_name):
logger = logging.getLogger(logger_name)
def _(*a, **k):
logger.info(f"Called with {a}, {k}")
return getLogger(fun(*a, **k), "ret_" + logger_name)
return _
class LoggerClass:
def __init__(self, sup: "bitches", logger_name):
self.sup = sup # Poor man's super implementation /s
self.logger_name = logger_name
self.logger = logging.getLogger(logger_name)
def __call__(self, *a, **k):
self.logger.info(f"Init with {a}, {k}")
return self.sup.__init__(*a, **k)
def __getattr__(self, key):
if key in self.__dict__:
return self.__dict__[key]
real_value = getattr(self.sup, key)
self.logger.info(f"getattr({key}) -> {real_value}")
return getLogger(real_value, self.logger_name + "." + key)
def getLogger(value, logger_name="unknown"):
if type(value).__module__ != "builtins" or inspect.ismodule(value):
return LoggerClass(value, logger_name)
elif callable(value):
return LoggerDecorator(value, logger_name)
logging.debug(f"Returning real value {value}")
return value
def hijack(import_fun):
import sys
from pathlib import Path
def hijacked_import(name, globals_=None, locals_=None, fromlist=(), level=0):
if (
not globals_ # if for some strange reason globals_ is None, ignore it
or not globals_.get("__file__")
or (
Path(
__file__
).parent.resolve() # If the file is in the same dir (or one of its child), do NOT ignore it
not in Path(globals_.get("__file__")).resolve().parents
and globals_.get("__name__")
!= "__main__" # Or if the file has name main
)
# Another thing worth checking is if the thingy being imported is in sys.path[1:]
# which are commonly python's installed libraries. It might be useful when the
# shit is imported from another dir. But why the fuck am I even writing this since
# the only way is with importlib and importlib isn't being hijacked
):
return import_fun(name, globals_, locals_, fromlist, level)
logging.info(
f"Imported {name=}, pkg={globals_.get('__package__', '')!r}, {fromlist=}"
) # This is the code executed when the import is from "the stuff we want". In this case, just log it
return LoggerClass(import_fun(name, globals_, locals_, fromlist, level), name)
return hijacked_import
# I love how, since builtins is a module only for "main" you can't debug this with python -m pdb hijack_imports.py file.py
# nor breakpoint() since it imports shit. Pretty fucking nice.
# Fuck it
if not isinstance(__builtins__, dict):
__builtins__ = __builtins__.__dict__
__builtins__["__import__"] = hijack(__builtins__["__import__"])
if not sys.argv:
print("What should I run? Provide me the path to a .py file or -m path_to_a_pkg")
exit(1)
if sys.argv[0] == "-m":
del sys.argv[0]
runpy.run_module(sys.argv[0], run_name="__main__")
else:
runpy.run_path(sys.argv[0], run_name="__main__")