-
-
Notifications
You must be signed in to change notification settings - Fork 387
Commit
…dithook() That's a debugging feature. Try '--audit os.mkdir,open' or '--audit all'
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# -*- 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 not prefixes_string: | ||
return | ||
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 sorted(set(tuple(e.split('.')) for e in prefixes_string.split(',') if e)): | ||
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
# -*- 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 unittest.mock import patch | ||
|
||
from test.picardtestcase import PicardTestCase | ||
|
||
from picard.audit import ( | ||
event_match_prefixes, | ||
make_events_prefixes, | ||
prefixes_candidates_for_length, | ||
setup_audit, | ||
) | ||
|
||
|
||
class AuditTest(PicardTestCase): | ||
def test_make_events_prefixes(self): | ||
d = dict(make_events_prefixes('')) | ||
self.assertEqual(d, {}) | ||
d = dict(make_events_prefixes('a')) | ||
self.assertEqual(d, {1: [('a',)]}) | ||
d = dict(make_events_prefixes('a.b')) | ||
self.assertEqual(d, {2: [('a', 'b')]}) | ||
d = dict(make_events_prefixes('a.b,c.d,a.b')) | ||
self.assertEqual(d, {2: [('a', 'b'), ('c', 'd')]}) | ||
d = dict(make_events_prefixes('a,a.b,,a.b.c')) | ||
self.assertEqual(d, {1: [('a',)], 2: [('a', 'b')], 3: [('a', 'b', 'c')]}) | ||
|
||
def test_prefixes_candidates_for_length(self): | ||
d = make_events_prefixes('a,a.b,c.d,a.b.c,d.e.f,g.h.i') | ||
self.assertEqual(list(prefixes_candidates_for_length(0, d)), []) | ||
self.assertEqual(list(prefixes_candidates_for_length(1, d)), [('a',)]) | ||
self.assertEqual(list(prefixes_candidates_for_length(2, d)), [('a',), ('a', 'b'), ('c', 'd')]) | ||
expected = [('a',), ('a', 'b'), ('c', 'd'), ('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'h', 'i')] | ||
self.assertEqual(list(prefixes_candidates_for_length(3, d)), expected) | ||
Check warning Code scanning / Pylintpython3 (reported by Codacy) Trailing whitespace Warning test
Trailing whitespace
Check warning Code scanning / Pylint (reported by Codacy) Trailing whitespace Warning test
Trailing whitespace
|
||
self.assertEqual(list(prefixes_candidates_for_length(4, d)), expected) | ||
Check warning Code scanning / Pylintpython3 (reported by Codacy) Trailing whitespace Warning test
Trailing whitespace
Check warning Code scanning / Pylint (reported by Codacy) Trailing whitespace Warning test
Trailing whitespace
|
||
|
||
def test_event_match_prefixes(self): | ||
d = make_events_prefixes('a.b') | ||
self.assertEqual(event_match_prefixes('a', d), False) | ||
self.assertEqual(event_match_prefixes('a.b', d), ('a', 'b')) | ||
self.assertEqual(event_match_prefixes('a.b.c', d), ('a', 'b')) | ||
self.assertEqual(event_match_prefixes('b.c', d), False) | ||
|
||
Check warning Code scanning / Pylintpython3 (reported by Codacy) Trailing whitespace Warning test
Trailing whitespace
Check warning Code scanning / Pylint (reported by Codacy) Trailing whitespace Warning test
Trailing whitespace
|
||
def test_setup_audit_1(self): | ||
with patch('sys.addaudithook') as mock: | ||
setup_audit('a,b.c') | ||
self.assertTrue(mock.called) | ||
|
||
def test_setup_audit_2(self): | ||
with patch('sys.addaudithook') as mock: | ||
setup_audit('') | ||
self.assertFalse(mock.called) |