Skip to content

Commit

Permalink
Add a command-line option to enable audit, using Python 3.8 sys.addau…
Browse files Browse the repository at this point in the history
…dithook()

That's a debugging feature.

Try '--audit os' or '--audit all'
  • Loading branch information
zas committed Sep 15, 2023
1 parent e2f1940 commit 0114eb6
Showing 1 changed file with 63 additions and 0 deletions.
63 changes: 63 additions & 0 deletions picard/tagger.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,60 @@ def __str__(self):
return f"files: {repr(self.files)} mbids: f{repr(self.mbids)} urls: {repr(self.urls)}"


def setup_audit(prefixes_string):
"""Setup audit hook according to `audit` command-line option"""
if 'all' in prefixes_string.split(','):
def event_match(event):
return ('all', )
else:
# prebuild the dict, constant
PREFIXES_DICT = make_events_prefixes(prefixes_string)

def event_match(event):
return event_match_prefixes(event, PREFIXES_DICT)

def audit(event, args):
# we can't use log here, as it generates events
matched = event_match(event)
if matched:
matched = '.'.join(matched)
print(f'audit:{matched}: {event} args={args}')

sys.addaudithook(audit)


def make_events_prefixes(prefixes_string):
"""Build a dict with keys = length of prefix"""
from collections import defaultdict
d = defaultdict(list)
for p in set(tuple(e.split('.')) for e in prefixes_string.split(',')):
d[len(p)].append(p)
return d


def prefixes_candidates_for_length(length, prefixes_dict):
"""Generate prefixes that may match this length"""
for plen, v in prefixes_dict.items():
if length >= plen:
yield from v


def event_match_prefixes(event, prefixes_dict):
"""Matches event against prefixes
Typical case: we want to match `os.mkdir` if prefix is `os` or `os.mkdir`
but not the reverse: if prefix is `os.mkdir` we don't want to match an event named `os`
It returns False, or the matched prefix
"""
ev = tuple(event.split('.'))
ev_len = len(ev)
# only use candidates that may have a chance to match
for p in prefixes_candidates_for_length(ev_len, prefixes_dict):
# check that all elements of ev are in p
if all(v == ev[i] for i, v in enumerate(p)):
return p
return False


class Tagger(QtWidgets.QApplication):

tagger_stats_changed = QtCore.pyqtSignal()
Expand Down Expand Up @@ -256,6 +310,10 @@ def __init__(self, picard_args, localedir, autoupdate, pipe_handler=None):
if picard_args.debug or "PICARD_DEBUG" in os.environ:
self.set_log_level(logging.DEBUG)

if sys.version_info[:3] > (3, 8):
if picard_args.audit:
setup_audit(picard_args.audit)

# Main thread pool used for most background tasks
self.thread_pool = QtCore.QThreadPool(self)
# Two threads are needed for the pipe handler and command processing.
Expand Down Expand Up @@ -1437,6 +1495,11 @@ def process_picard_args():
parser.add_argument("-display", nargs=1, help=argparse.SUPPRESS)

# Picard specific arguments
if sys.version_info[:3] > (3, 8):
parser.add_argument("-a", "--audit", action='store',
default=None,
help="audit events passed as a comma-separated list, prefixes supported, "
"use all to match any (see https://docs.python.org/3/library/audit_events.html#audit-events)")
parser.add_argument("-c", "--config-file", action='store',
default=None,
help="location of the configuration file")
Expand Down

0 comments on commit 0114eb6

Please sign in to comment.