X-Git-Url: https://wannabe.guru.org/gitweb/?a=blobdiff_plain;f=bootstrap.py;h=117468623b94e578894561d0ff6fede8a9a33135;hb=532df2c5b57c7517dfb3dddd8c1358fbadf8baf3;hp=35747865b7aacc38fa61f6005af6c5bd5833ff69;hpb=5c212d7639f62fcb936f9d7a0bbe704a9f7b213d;p=python_utils.git diff --git a/bootstrap.py b/bootstrap.py index 3574786..1174686 100644 --- a/bootstrap.py +++ b/bootstrap.py @@ -1,5 +1,15 @@ #!/usr/bin/env python3 +# © Copyright 2021-2022, Scott Gasch + +"""This is a module for wrapping around python programs and doing some +minor setup and tear down work for them. With it, you can break into +pdb on unhandled top level exceptions, profile your code by passing a +commandline argument in, audit module import events, examine where +memory is being used in your program, and so on. + +""" + import functools import importlib import logging @@ -101,6 +111,15 @@ def handle_uncaught_exception(exc_type, exc_value, exc_tb): class ImportInterceptor(importlib.abc.MetaPathFinder): + """An interceptor that always allows module load events but dumps a + record into the log and onto stdout when modules are loaded and + produces an audit of who imported what at the end of the run. It + can't see any load events that happen before it, though, so move + bootstrap up in your __main__'s import list just temporarily to + get a good view. + + """ + def __init__(self): import collect.trie @@ -111,9 +130,9 @@ class ImportInterceptor(importlib.abc.MetaPathFinder): def repopulate_modules_by_filename(self): self.module_by_filename_cache.clear() - for mod in sys.modules: - if hasattr(sys.modules[mod], '__file__'): - fname = getattr(sys.modules[mod], '__file__') + for _, mod in sys.modules.copy().items(): # copy here because modules is volatile + if hasattr(mod, '__file__'): + fname = getattr(mod, '__file__') else: fname = 'unknown' self.module_by_filename_cache[fname] = mod @@ -226,10 +245,16 @@ def initialize(entry_point): # Try to figure out the name of the program entry point. Then # parse configuration (based on cmdline flags, environment vars # etc...) - if '__globals__' in entry_point.__dict__ and '__file__' in entry_point.__globals__: - config.parse(entry_point.__globals__['__file__']) - else: - config.parse(None) + entry_filename = None + entry_descr = None + try: + entry_filename = entry_point.__code__.co_filename + entry_descr = entry_point.__code__.__repr__() + except Exception: + if '__globals__' in entry_point.__dict__ and '__file__' in entry_point.__globals__: + entry_filename = entry_point.__globals__['__file__'] + entry_descr = entry_filename + config.parse(entry_filename) if config.config['trace_memory']: import tracemalloc @@ -243,12 +268,15 @@ def initialize(entry_point): # Maybe log some info about the python interpreter itself. logger.debug( - 'Platform: %s, maxint=0x%x, byteorder=%s', - sys.platform, sys.maxsize, sys.byteorder + 'Platform: %s, maxint=0x%x, byteorder=%s', sys.platform, sys.maxsize, sys.byteorder ) logger.debug('Python interpreter version: %s', sys.version) logger.debug('Python implementation: %s', sys.implementation) logger.debug('Python C API version: %s', sys.api_version) + if __debug__: + logger.debug('Python interpreter running in __debug__ mode.') + else: + logger.debug('Python interpreter running in optimized mode.') logger.debug('Python path: %s', sys.path) # Log something about the site_config, many things use it. @@ -273,7 +301,7 @@ def initialize(entry_point): random.seed(random_seed) # Do it, invoke the user's code. Pay attention to how long it takes. - logger.debug('Starting %s (program entry point)', entry_point.__name__) + logger.debug('Starting %s (program entry point)', entry_descr) ret = None import stopwatch @@ -293,7 +321,7 @@ def initialize(entry_point): with stopwatch.Timer() as t: ret = entry_point(*args, **kwargs) - logger.debug('%s (program entry point) returned %s.', entry_point.__name__, ret) + logger.debug('%s (program entry point) returned %s.', entry_descr, ret) if config.config['trace_memory']: snapshot = tracemalloc.take_snapshot() @@ -320,7 +348,12 @@ def initialize(entry_point): 'child system: %.4fs\n' 'machine uptime: %.4fs\n' 'walltime: %.4fs', - utime, stime, cutime, cstime, elapsed_time, walltime + utime, + stime, + cutime, + cstime, + elapsed_time, + walltime, ) # If it doesn't return cleanly, call attention to the return value.