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.mkdir,open' or '--audit all'
  • Loading branch information
zas committed Sep 15, 2023
1 parent e2f1940 commit 5870cd2
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 0 deletions.
76 changes: 76 additions & 0 deletions picard/audit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# -*- coding: utf-8 -*-
#
# Picard, the next-generation MusicBrainz tagger
#
# Copyright (C) 2023 Laurent Monin
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

from collections import defaultdict

import sys


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"""
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
10 changes: 10 additions & 0 deletions picard/tagger.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
NatAlbum,
run_album_post_removal_processors,
)
from picard.audit import setup_audit
from picard.browser.browser import BrowserIntegration
from picard.browser.filelookup import FileLookup
from picard.cluster import (
Expand Down Expand Up @@ -256,6 +257,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 +1442,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 5870cd2

Please sign in to comment.