Drop patches cherry-picked from upstream
Nilesh Patra
2 years ago
0 | From 4b0070a4f30cbf6d5e12e6274b242b62ea11c81b Mon Sep 17 00:00:00 2001 | |
1 | From: Delgan <delgan.py@gmail.com> | |
2 | Date: Fri, 21 Jan 2022 11:21:58 +0100 | |
3 | Subject: [PATCH] Remove use of "pickle.loads()" to comply with security tools | |
4 | (#563) | |
5 | ||
6 | --- | |
7 | loguru/_recattrs.py | 18 +++++++----------- | |
8 | tests/test_add_option_enqueue.py | 28 +++++++++++++++++++++++++--- | |
9 | 2 files changed, 32 insertions(+), 14 deletions(-) | |
10 | ||
11 | --- a/loguru/_recattrs.py | |
12 | +++ b/loguru/_recattrs.py | |
13 | @@ -64,18 +64,14 @@ | |
14 | return "(type=%r, value=%r, traceback=%r)" % (self.type, self.value, self.traceback) | |
15 | ||
16 | def __reduce__(self): | |
17 | + # The traceback is not picklable so we need to remove it. Also, some custom exception | |
18 | + # values aren't picklable either. For user convenience, we try first to serialize it and | |
19 | + # we remove the value in case or error. As an optimization, we could have re-used the | |
20 | + # dumped value during unpickling, but this requires using "pickle.loads()" which is | |
21 | + # flagged as insecure by some security tools. | |
22 | try: | |
23 | - pickled_value = pickle.dumps(self.value) | |
24 | + pickle.dumps(self.value) | |
25 | except pickle.PickleError: | |
26 | return (RecordException, (self.type, None, None)) | |
27 | else: | |
28 | - return (RecordException._from_pickled_value, (self.type, pickled_value, None)) | |
29 | - | |
30 | - @classmethod | |
31 | - def _from_pickled_value(cls, type_, pickled_value, traceback_): | |
32 | - try: | |
33 | - value = pickle.loads(pickled_value) | |
34 | - except pickle.PickleError: | |
35 | - return cls(type_, None, traceback_) | |
36 | - else: | |
37 | - return cls(type_, value, traceback_) | |
38 | + return (RecordException, (self.type, self.value, None)) | |
39 | --- a/tests/test_add_option_enqueue.py | |
40 | +++ b/tests/test_add_option_enqueue.py | |
41 | @@ -212,8 +212,7 @@ | |
42 | assert err == "".join("%d\n" % i for i in range(10)) | |
43 | ||
44 | ||
45 | -@pytest.mark.parametrize("arg", [NotPicklable(), NotUnpicklable()]) | |
46 | -def test_logging_not_picklable_exception(arg): | |
47 | +def test_logging_not_picklable_exception(): | |
48 | exception = None | |
49 | ||
50 | def sink(message): | |
51 | @@ -223,7 +222,30 @@ | |
52 | logger.add(sink, enqueue=True, catch=False) | |
53 | ||
54 | try: | |
55 | - raise ValueError(arg) | |
56 | + raise ValueError(NotPicklable()) | |
57 | + except Exception: | |
58 | + logger.exception("Oups") | |
59 | + | |
60 | + logger.remove() | |
61 | + | |
62 | + type_, value, traceback_ = exception | |
63 | + assert type_ is ValueError | |
64 | + assert value is None | |
65 | + assert traceback_ is None | |
66 | + | |
67 | + | |
68 | +@pytest.mark.xfail(reason="No way to safely deserialize exception yet") | |
69 | +def test_logging_not_unpicklable_exception(): | |
70 | + exception = None | |
71 | + | |
72 | + def sink(message): | |
73 | + nonlocal exception | |
74 | + exception = message.record["exception"] | |
75 | + | |
76 | + logger.add(sink, enqueue=True, catch=False) | |
77 | + | |
78 | + try: | |
79 | + raise ValueError(NotUnpicklable()) | |
80 | except Exception: | |
81 | logger.exception("Oups") | |
82 |
0 | From 31cf758ee9d22dbfa125f38153782fe20ac9dce5 Mon Sep 17 00:00:00 2001 | |
1 | From: Delgan <delgan.py@gmail.com> | |
2 | Date: Sat, 19 Dec 2020 16:29:07 +0100 | |
3 | Subject: [PATCH] Fix failing tests due to new "excepthook" in threads | |
4 | ||
5 | --- | |
6 | tests/test_add_option_enqueue.py | 44 +++++++++++++++++++++++++------- | |
7 | 1 file changed, 35 insertions(+), 9 deletions(-) | |
8 | ||
9 | --- a/tests/test_add_option_enqueue.py | |
10 | +++ b/tests/test_add_option_enqueue.py | |
11 | @@ -4,6 +4,9 @@ | |
12 | import re | |
13 | import sys | |
14 | import pickle | |
15 | +import contextlib | |
16 | +import threading | |
17 | +import traceback | |
18 | ||
19 | ||
20 | class NotPicklable: | |
21 | @@ -29,6 +32,27 @@ | |
22 | print(message, end="") | |
23 | ||
24 | ||
25 | +@contextlib.contextmanager | |
26 | +def default_threading_excepthook(): | |
27 | + if not hasattr(threading, "excepthook"): | |
28 | + yield | |
29 | + return | |
30 | + | |
31 | + # Pytest added "PytestUnhandledThreadExceptionWarning", we need to | |
32 | + # remove it temporarily for somes tests checking exceptions in threads. | |
33 | + | |
34 | + def excepthook(args): | |
35 | + print("Exception in thread:", file=sys.stderr, flush=True) | |
36 | + traceback.print_exception( | |
37 | + args.exc_type, args.exc_value, args.exc_traceback, file=sys.stderr | |
38 | + ) | |
39 | + | |
40 | + old_excepthook = threading.excepthook | |
41 | + threading.excepthook = excepthook | |
42 | + yield | |
43 | + threading.excepthook = old_excepthook | |
44 | + | |
45 | + | |
46 | def test_enqueue(): | |
47 | x = [] | |
48 | ||
49 | @@ -140,10 +164,11 @@ | |
50 | def test_not_caught_exception_queue_get(writer, capsys): | |
51 | logger.add(writer, enqueue=True, catch=False, format="{message}") | |
52 | ||
53 | - logger.info("It's fine") | |
54 | - logger.bind(broken=NotUnpicklable()).info("Bye bye...") | |
55 | - logger.info("It's not fine") | |
56 | - logger.remove() | |
57 | + with default_threading_excepthook(): | |
58 | + logger.info("It's fine") | |
59 | + logger.bind(broken=NotUnpicklable()).info("Bye bye...") | |
60 | + logger.info("It's not fine") | |
61 | + logger.remove() | |
62 | ||
63 | out, err = capsys.readouterr() | |
64 | lines = err.strip().splitlines() | |
65 | @@ -153,13 +178,14 @@ | |
66 | assert lines[-1].endswith("UnpicklingError: You shall not de-serialize me!") | |
67 | ||
68 | ||
69 | -def test_not_caught_exception_sink_write(capsys): | |
70 | +def test_not_caught_exception_sink_write(monkeypatch, capsys): | |
71 | logger.add(NotWritable(), enqueue=True, catch=False, format="{message}") | |
72 | ||
73 | - logger.info("It's fine") | |
74 | - logger.bind(fail=True).info("Bye bye...") | |
75 | - logger.info("It's not fine") | |
76 | - logger.remove() | |
77 | + with default_threading_excepthook(): | |
78 | + logger.info("It's fine") | |
79 | + logger.bind(fail=True).info("Bye bye...") | |
80 | + logger.info("It's not fine") | |
81 | + logger.remove() | |
82 | ||
83 | out, err = capsys.readouterr() | |
84 | lines = err.strip().splitlines() |