1def optimize_exception_check(attr_name):
 2    def decorator(func):
 3        def wrapper(self, exc_type):
 4            cache = getattr(self, attr_name)
 5            if exc_type in cache:
 6                return True
 7
 8            result = func(self, exc_type)  # True or False
 9            if result:
10                cache.add(exc_type)
11            return result
12        return wrapper
13    return decorator
14
15
16class ProtectedSection:
17    __counter = 0
18
19    def __init__(self, log=(), suppress=()):
20        if log is not None:
21            for exc in log:
22                if not issubclass(exc, Exception):
23                    raise TypeError("Not a valid exception type")
24
25        if suppress is not None:
26            for exc in suppress:
27                if not issubclass(exc, Exception):
28                    raise TypeError("Not a valid exception type")
29
30        self._current_exception = None  # Saves the specific or last "updated" exception in case of reuse of an already created instance
31        self.log_exceptions = set(log)
32        self.suppress_exceptions = set(suppress)
33        self._checked_exceptions_logs = set()  # Optimization to avoid checking one exception n times
34        self._checked_exceptions_suppress = set()  # Optimization to avoid checking one exception n times
35        self.exceptions_by_session = {}  # Dictionary to store exceptions with their session ID
36
37    @optimize_exception_check("_checked_exceptions_logs")
38    def is_exception_logged(self, exc_type):
39        return exc_type in self.log_exceptions
40
41    @optimize_exception_check("_checked_exceptions_suppress")
42    def is_exception_suppressed(self, exc_type):
43        return exc_type in self.suppress_exceptions
44
45    def __enter__(self):
46        self._current_exception = None
47        return self
48
49    def __exit__(self, exc_type, exc_value, traceback):
50        if exc_type:
51            if self.is_exception_logged(exc_type):
52                self.exceptions_by_session[self.__counter] = exc_value
53                self.__counter += 1
54                self._current_exception = exc_value
55                return True
56
57            if self.is_exception_suppressed(exc_type):
58                self._current_exception = None
59                self.__counter += 1
60                return True
61
62        return False
63
64    @property
65    def exception(self):
66        return self._current_exception
67
68    @property
69    def exceptions(self):
70        return self.exceptions_by_session
71
72    def get_exception_by_session(self, session_id):
73        return self.exceptions_by_session.get(session_id, None)
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
|   
        Георги Кунчев
         09.11.2024 17:26Хаха, ок. Определено взе задачата навътре. Давам ти точка за усилията.  
Мисля, че е далеч от очакванията, но щом си решил, ще го поощря. | 
|   
        Йоан Байчев
         09.11.2024 13:40Относно проблема с преизползването на създадена инстанция, тъй като в примера cm1 и cm2 се държат като референции към инстанцията protected_section, създадох променлива "exceptions_by_session", която да пази конкретната грешка за конкретната "референция", като работи с индексация, което вече би било отговорност на потребителя, спрямо желанието му да види съответната грешка на н-тото преизползване. Променливата _current_exception пази грешката на най-актуалната референция, нейната цел е да  бъде динамично изпозлвана в блока на with. | 
|   
        Георги Кунчев
         09.11.2024 10:10Харесва ми, че искаш да оптимизираш, но имам няколко коментара.  
* Двата декоратора правят едно и също нещо, просто с различни променливи. Можеш да опиташ да ги обединиш.
* Можеш да приложиш оптимизация като просто инициализираш `self.log_exceptions` и `self.suppress_exceptions` като сетове от кортежите, които влизат. Времето за търсене пак ще е константно. Ако кажеш - да, но губя време да направя сет от кортежа - така е, но така или иначе тази оптимизация ще работи само ако в последователните си блокове използваш една единствена инстанция. Ако за всеки `with` инициализираш наново, новата инстанция няма идея какво се е случило с останалите. С други думи, конвертирането към сет ще се случи само веднъж.    
```
protected_section = ProtectedSection(log=(ValueError, ))
with protected_section as cm1:
    raise ValueError('Text 1')
print(cm1.exception)
with protected_section as cm2:
    raise ValueError('Text 2')
print(cm2.exception)
```
ПП: Държа да спомена, че има риск от използването на една инстанция, защото след презиползването ѝ губиш информация за грешката, която е породена в предишните използвания, тъй като инстанцията може да държи в себе си само една грешка. В края на изпълнението на кода по-горе, `cm1` ще държи същата грешка, която е сетната при изпълнение на втория блок. | 
|   
        Йоан Байчев
         09.11.2024 01:58Тази оптимизация би била най-полезна в сценарии с множество последователни блокове "with ProtectedSection(...) as err:", изпълнявани често и съдържащи по-големи кортежи за проверка, тъй като елиминира повтарящите се линейни търсения. (вложени блокове) | 
|   
        Йоан Байчев
         08.11.2024 16:52Целта на ExceptionStore е да осигури начин за запис на изключения, които не са подтиснати или логнати, така че те да могат да бъдат анализирани или обработени в бъдеще. | 
| f | 1 | def optimize_exception_check(attr_name): | f | 1 | def optimize_exception_check(attr_name): | 
| 2 | def decorator(func): | 2 | def decorator(func): | ||
| 3 | def wrapper(self, exc_type): | 3 | def wrapper(self, exc_type): | ||
| 4 | cache = getattr(self, attr_name) | 4 | cache = getattr(self, attr_name) | ||
| 5 | if exc_type in cache: | 5 | if exc_type in cache: | ||
| 6 | return True | 6 | return True | ||
| 7 | 7 | ||||
| 8 | result = func(self, exc_type) # True or False | 8 | result = func(self, exc_type) # True or False | ||
| 9 | if result: | 9 | if result: | ||
| 10 | cache.add(exc_type) | 10 | cache.add(exc_type) | ||
| 11 | return result | 11 | return result | ||
| 12 | return wrapper | 12 | return wrapper | ||
| 13 | return decorator | 13 | return decorator | ||
| 14 | 14 | ||||
| 15 | 15 | ||||
| 16 | class ProtectedSection: | 16 | class ProtectedSection: | ||
| 17 | __counter = 0 | 17 | __counter = 0 | ||
| 18 | 18 | ||||
| 19 | def __init__(self, log=(), suppress=()): | 19 | def __init__(self, log=(), suppress=()): | ||
| 20 | if log is not None: | 20 | if log is not None: | ||
| 21 | for exc in log: | 21 | for exc in log: | ||
| 22 | if not issubclass(exc, Exception): | 22 | if not issubclass(exc, Exception): | ||
| 23 | raise TypeError("Not a valid exception type") | 23 | raise TypeError("Not a valid exception type") | ||
| 24 | 24 | ||||
| 25 | if suppress is not None: | 25 | if suppress is not None: | ||
| 26 | for exc in suppress: | 26 | for exc in suppress: | ||
| 27 | if not issubclass(exc, Exception): | 27 | if not issubclass(exc, Exception): | ||
| 28 | raise TypeError("Not a valid exception type") | 28 | raise TypeError("Not a valid exception type") | ||
| 29 | 29 | ||||
| 30 | self._current_exception = None # Saves the specific or last "updated" exception in case of reuse of an already created instance | 30 | self._current_exception = None # Saves the specific or last "updated" exception in case of reuse of an already created instance | ||
| 31 | self.log_exceptions = set(log) | 31 | self.log_exceptions = set(log) | ||
| 32 | self.suppress_exceptions = set(suppress) | 32 | self.suppress_exceptions = set(suppress) | ||
| 33 | self._checked_exceptions_logs = set() # Optimization to avoid checking one exception n times | 33 | self._checked_exceptions_logs = set() # Optimization to avoid checking one exception n times | ||
| 34 | self._checked_exceptions_suppress = set() # Optimization to avoid checking one exception n times | 34 | self._checked_exceptions_suppress = set() # Optimization to avoid checking one exception n times | ||
| 35 | self.exceptions_by_session = {} # Dictionary to store exceptions with their session ID | 35 | self.exceptions_by_session = {} # Dictionary to store exceptions with their session ID | ||
| 36 | 36 | ||||
| 37 | @optimize_exception_check("_checked_exceptions_logs") | 37 | @optimize_exception_check("_checked_exceptions_logs") | ||
| 38 | def is_exception_logged(self, exc_type): | 38 | def is_exception_logged(self, exc_type): | ||
| 39 | return exc_type in self.log_exceptions | 39 | return exc_type in self.log_exceptions | ||
| 40 | 40 | ||||
| 41 | @optimize_exception_check("_checked_exceptions_suppress") | 41 | @optimize_exception_check("_checked_exceptions_suppress") | ||
| 42 | def is_exception_suppressed(self, exc_type): | 42 | def is_exception_suppressed(self, exc_type): | ||
| 43 | return exc_type in self.suppress_exceptions | 43 | return exc_type in self.suppress_exceptions | ||
| 44 | 44 | ||||
| 45 | def __enter__(self): | 45 | def __enter__(self): | ||
| t | t | 46 | self._current_exception = None | ||
| 46 | return self | 47 | return self | ||
| 47 | 48 | ||||
| 48 | def __exit__(self, exc_type, exc_value, traceback): | 49 | def __exit__(self, exc_type, exc_value, traceback): | ||
| 49 | if exc_type: | 50 | if exc_type: | ||
| 50 | if self.is_exception_logged(exc_type): | 51 | if self.is_exception_logged(exc_type): | ||
| 51 | self.exceptions_by_session[self.__counter] = exc_value | 52 | self.exceptions_by_session[self.__counter] = exc_value | ||
| 52 | self.__counter += 1 | 53 | self.__counter += 1 | ||
| 53 | self._current_exception = exc_value | 54 | self._current_exception = exc_value | ||
| 54 | return True | 55 | return True | ||
| 55 | 56 | ||||
| 56 | if self.is_exception_suppressed(exc_type): | 57 | if self.is_exception_suppressed(exc_type): | ||
| 57 | self._current_exception = None | 58 | self._current_exception = None | ||
| 58 | self.__counter += 1 | 59 | self.__counter += 1 | ||
| 59 | return True | 60 | return True | ||
| 60 | 61 | ||||
| 61 | return False | 62 | return False | ||
| 62 | 63 | ||||
| 63 | @property | 64 | @property | ||
| 64 | def exception(self): | 65 | def exception(self): | ||
| 65 | return self._current_exception | 66 | return self._current_exception | ||
| 66 | 67 | ||||
| 67 | @property | 68 | @property | ||
| 68 | def exceptions(self): | 69 | def exceptions(self): | ||
| 69 | return self.exceptions_by_session | 70 | return self.exceptions_by_session | ||
| 70 | 71 | ||||
| 71 | def get_exception_by_session(self, session_id): | 72 | def get_exception_by_session(self, session_id): | ||
| 72 | return self.exceptions_by_session.get(session_id, None) | 73 | return self.exceptions_by_session.get(session_id, None) | 
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| 
 | 
 | |||||||||
| f | 1 | def optimize_exception_check(attr_name): | f | 1 | def optimize_exception_check(attr_name): | 
| 2 | def decorator(func): | 2 | def decorator(func): | ||
| 3 | def wrapper(self, exc_type): | 3 | def wrapper(self, exc_type): | ||
| 4 | cache = getattr(self, attr_name) | 4 | cache = getattr(self, attr_name) | ||
| 5 | if exc_type in cache: | 5 | if exc_type in cache: | ||
| 6 | return True | 6 | return True | ||
| 7 | 7 | ||||
| n | 8 | result = func(self, exc_type) # true or false | n | 8 | result = func(self, exc_type) # True or False | 
| 9 | if result: | 9 | if result: | ||
| 10 | cache.add(exc_type) | 10 | cache.add(exc_type) | ||
| 11 | return result | 11 | return result | ||
| 12 | return wrapper | 12 | return wrapper | ||
| 13 | return decorator | 13 | return decorator | ||
| 14 | 14 | ||||
| 15 | 15 | ||||
| 16 | class ProtectedSection: | 16 | class ProtectedSection: | ||
| n | n | 17 | __counter = 0 | ||
| 18 | |||||
| 17 | def __init__(self, log=(), suppress=()): | 19 | def __init__(self, log=(), suppress=()): | ||
| 18 | if log is not None: | 20 | if log is not None: | ||
| 19 | for exc in log: | 21 | for exc in log: | ||
| 20 | if not issubclass(exc, Exception): | 22 | if not issubclass(exc, Exception): | ||
| 21 | raise TypeError("Not a valid exception type") | 23 | raise TypeError("Not a valid exception type") | ||
| 22 | 24 | ||||
| 23 | if suppress is not None: | 25 | if suppress is not None: | ||
| 24 | for exc in suppress: | 26 | for exc in suppress: | ||
| 25 | if not issubclass(exc, Exception): | 27 | if not issubclass(exc, Exception): | ||
| 26 | raise TypeError("Not a valid exception type") | 28 | raise TypeError("Not a valid exception type") | ||
| 27 | 29 | ||||
| n | 28 | self.exception = None | n | 30 | self._current_exception = None # Saves the specific or last "updated" exception in case of reuse of an already created instance | 
| 29 | self.log_exceptions = set(log) | 31 | self.log_exceptions = set(log) | ||
| 30 | self.suppress_exceptions = set(suppress) | 32 | self.suppress_exceptions = set(suppress) | ||
| 31 | self._checked_exceptions_logs = set() # Optimization to avoid checking one exception n times | 33 | self._checked_exceptions_logs = set() # Optimization to avoid checking one exception n times | ||
| 32 | self._checked_exceptions_suppress = set() # Optimization to avoid checking one exception n times | 34 | self._checked_exceptions_suppress = set() # Optimization to avoid checking one exception n times | ||
| n | n | 35 | self.exceptions_by_session = {} # Dictionary to store exceptions with their session ID | ||
| 33 | 36 | ||||
| 34 | @optimize_exception_check("_checked_exceptions_logs") | 37 | @optimize_exception_check("_checked_exceptions_logs") | ||
| 35 | def is_exception_logged(self, exc_type): | 38 | def is_exception_logged(self, exc_type): | ||
| 36 | return exc_type in self.log_exceptions | 39 | return exc_type in self.log_exceptions | ||
| 37 | 40 | ||||
| 38 | @optimize_exception_check("_checked_exceptions_suppress") | 41 | @optimize_exception_check("_checked_exceptions_suppress") | ||
| 39 | def is_exception_suppressed(self, exc_type): | 42 | def is_exception_suppressed(self, exc_type): | ||
| 40 | return exc_type in self.suppress_exceptions | 43 | return exc_type in self.suppress_exceptions | ||
| 41 | 44 | ||||
| 42 | def __enter__(self): | 45 | def __enter__(self): | ||
| 43 | return self | 46 | return self | ||
| 44 | 47 | ||||
| 45 | def __exit__(self, exc_type, exc_value, traceback): | 48 | def __exit__(self, exc_type, exc_value, traceback): | ||
| 46 | if exc_type: | 49 | if exc_type: | ||
| 47 | if self.is_exception_logged(exc_type): | 50 | if self.is_exception_logged(exc_type): | ||
| n | n | 51 | self.exceptions_by_session[self.__counter] = exc_value | ||
| 52 | self.__counter += 1 | ||||
| 48 | self.exception = exc_value | 53 | self._current_exception = exc_value | ||
| 49 | return True | 54 | return True | ||
| 50 | 55 | ||||
| 51 | if self.is_exception_suppressed(exc_type): | 56 | if self.is_exception_suppressed(exc_type): | ||
| n | 52 | self.exception = None | n | 57 | self._current_exception = None | 
| 58 | self.__counter += 1 | ||||
| 53 | return True | 59 | return True | ||
| 54 | 60 | ||||
| 55 | return False | 61 | return False | ||
| t | t | 62 | |||
| 63 | @property | ||||
| 64 | def exception(self): | ||||
| 65 | return self._current_exception | ||||
| 66 | |||||
| 67 | @property | ||||
| 68 | def exceptions(self): | ||||
| 69 | return self.exceptions_by_session | ||||
| 70 | |||||
| 71 | def get_exception_by_session(self, session_id): | ||||
| 72 | return self.exceptions_by_session.get(session_id, None) | 
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| 
 | 
 | |||||||||
| n | 1 | def optimize_exception_check_for_logs(func): | n | 1 | def optimize_exception_check(attr_name): | 
| 2 | def decorator(func): | ||||
| 2 | def wrapper(self, exc_type): | 3 | def wrapper(self, exc_type): | ||
| 3 | if exc_type in self._checked_exceptions_logs: | 4 | cache = getattr(self, attr_name) | ||
| 4 | return self._checked_exceptions_logs[exc_type] | 5 | if exc_type in cache: | ||
| 6 | return True | ||||
| 5 | 7 | ||||
| n | 6 | result = func(self, exc_type) # 1 or 0 | n | 8 | result = func(self, exc_type) # true or false | 
| 7 | self._checked_exceptions_logs[exc_type] = result | 9 | if result: | ||
| 8 | 10 | cache.add(exc_type) | |||
| 9 | return result | 11 | return result | ||
| 10 | return wrapper | 12 | return wrapper | ||
| 11 | 13 | return decorator | |||
| 12 | def optimize_exception_check_for_suppress(func): | ||||
| 13 | def wrapper(self, exc_type): | ||||
| 14 | if exc_type in self._checked_exceptions_suppress: | ||||
| 15 | return self._checked_exceptions_suppress[exc_type] | ||||
| 16 | |||||
| 17 | result = func(self, exc_type) # 1 or 0 | ||||
| 18 | self._checked_exceptions_suppress[exc_type] = result | ||||
| 19 | |||||
| 20 | return result | ||||
| 21 | return wrapper | ||||
| 22 | 14 | ||||
| 23 | 15 | ||||
| 24 | class ProtectedSection: | 16 | class ProtectedSection: | ||
| 25 | def __init__(self, log=(), suppress=()): | 17 | def __init__(self, log=(), suppress=()): | ||
| 26 | if log is not None: | 18 | if log is not None: | ||
| 27 | for exc in log: | 19 | for exc in log: | ||
| 28 | if not issubclass(exc, Exception): | 20 | if not issubclass(exc, Exception): | ||
| 29 | raise TypeError("Not a valid exception type") | 21 | raise TypeError("Not a valid exception type") | ||
| 30 | 22 | ||||
| 31 | if suppress is not None: | 23 | if suppress is not None: | ||
| 32 | for exc in suppress: | 24 | for exc in suppress: | ||
| 33 | if not issubclass(exc, Exception): | 25 | if not issubclass(exc, Exception): | ||
| 34 | raise TypeError("Not a valid exception type") | 26 | raise TypeError("Not a valid exception type") | ||
| 35 | 27 | ||||
| n | 36 | self.log_exceptions = log | n | ||
| 37 | self.suppress_exceptions = suppress | ||||
| 38 | self.exception = None | 28 | self.exception = None | ||
| n | n | 29 | self.log_exceptions = set(log) | ||
| 30 | self.suppress_exceptions = set(suppress) | ||||
| 39 | self._checked_exceptions_logs = {} # Optimization to avoid checking one exception n times | 31 | self._checked_exceptions_logs = set() # Optimization to avoid checking one exception n times | ||
| 40 | self._checked_exceptions_suppress = {} # Optimization to avoid checking one exception n times | 32 | self._checked_exceptions_suppress = set() # Optimization to avoid checking one exception n times | ||
| 41 | 33 | ||||
| n | 42 | @optimize_exception_check_for_logs | n | 34 | @optimize_exception_check("_checked_exceptions_logs") | 
| 43 | def is_exception_logged(self, exc_type): | 35 | def is_exception_logged(self, exc_type): | ||
| 44 | return exc_type in self.log_exceptions | 36 | return exc_type in self.log_exceptions | ||
| 45 | 37 | ||||
| t | 46 | @optimize_exception_check_for_suppress | t | 38 | @optimize_exception_check("_checked_exceptions_suppress") | 
| 47 | def is_exception_suppressed(self, exc_type): | 39 | def is_exception_suppressed(self, exc_type): | ||
| 48 | return exc_type in self.suppress_exceptions | 40 | return exc_type in self.suppress_exceptions | ||
| 49 | 41 | ||||
| 50 | def __enter__(self): | 42 | def __enter__(self): | ||
| 51 | return self | 43 | return self | ||
| 52 | 44 | ||||
| 53 | def __exit__(self, exc_type, exc_value, traceback): | 45 | def __exit__(self, exc_type, exc_value, traceback): | ||
| 54 | if exc_type: | 46 | if exc_type: | ||
| 55 | if self.is_exception_logged(exc_type): | 47 | if self.is_exception_logged(exc_type): | ||
| 56 | self.exception = exc_value | 48 | self.exception = exc_value | ||
| 57 | return True | 49 | return True | ||
| 58 | 50 | ||||
| 59 | if self.is_exception_suppressed(exc_type): | 51 | if self.is_exception_suppressed(exc_type): | ||
| 60 | self.exception = None | 52 | self.exception = None | ||
| 61 | return True | 53 | return True | ||
| 62 | 54 | ||||
| 63 | return False | 55 | return False | 
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| 
 | 
 | |||||||||
| n | 1 | class ExceptionStore: | n | 1 | def optimize_exception_check_for_logs(func): | 
| 2 | def __init__(self): | 2 | def wrapper(self, exc_type): | ||
| 3 | self.unhandled_exceptions = [] | 3 | if exc_type in self._checked_exceptions_logs: | ||
| 4 | self.exception = None | 4 | return self._checked_exceptions_logs[exc_type] | ||
| 5 | 5 | ||||
| n | 6 | def log_exception(self, exc_value, exc_type): | n | 6 | result = func(self, exc_type) # 1 or 0 | 
| 7 | self.unhandled_exceptions.append((exc_value, exc_type)) | 7 | self._checked_exceptions_logs[exc_type] = result | ||
| 8 | self.exception = exc_value | 8 | |||
| 9 | return result | ||||
| 10 | return wrapper | ||||
| 11 | |||||
| 12 | def optimize_exception_check_for_suppress(func): | ||||
| 13 | def wrapper(self, exc_type): | ||||
| 14 | if exc_type in self._checked_exceptions_suppress: | ||||
| 15 | return self._checked_exceptions_suppress[exc_type] | ||||
| 16 | |||||
| 17 | result = func(self, exc_type) # 1 or 0 | ||||
| 18 | self._checked_exceptions_suppress[exc_type] = result | ||||
| 19 | |||||
| 20 | return result | ||||
| 21 | return wrapper | ||||
| 9 | 22 | ||||
| 10 | 23 | ||||
| n | 11 | class ProtectedSection(ExceptionStore): | n | 24 | class ProtectedSection: | 
| 12 | def __init__(self, log=(), suppress=()): | 25 | def __init__(self, log=(), suppress=()): | ||
| n | n | 26 | if log is not None: | ||
| 27 | for exc in log: | ||||
| 13 | if not all(issubclass(exc, Exception) for exc in log): | 28 | if not issubclass(exc, Exception): | ||
| 14 | raise TypeError("Must be exception types") | 29 | raise TypeError("Not a valid exception type") | ||
| 15 | if not all(issubclass(exc, Exception) for exc in suppress): | ||||
| 16 | raise TypeError("Must be exception types") | ||||
| 17 | 30 | ||||
| n | 18 | super().__init__() | n | 31 | if suppress is not None: | 
| 32 | for exc in suppress: | ||||
| 33 | if not issubclass(exc, Exception): | ||||
| 34 | raise TypeError("Not a valid exception type") | ||||
| 35 | |||||
| 19 | self.log = log | 36 | self.log_exceptions = log | ||
| 20 | self.suppress = suppress | 37 | self.suppress_exceptions = suppress | ||
| 38 | self.exception = None | ||||
| 39 | self._checked_exceptions_logs = {} # Optimization to avoid checking one exception n times | ||||
| 40 | self._checked_exceptions_suppress = {} # Optimization to avoid checking one exception n times | ||||
| 41 | |||||
| 42 | @optimize_exception_check_for_logs | ||||
| 43 | def is_exception_logged(self, exc_type): | ||||
| 44 | return exc_type in self.log_exceptions | ||||
| 45 | |||||
| 46 | @optimize_exception_check_for_suppress | ||||
| 47 | def is_exception_suppressed(self, exc_type): | ||||
| 48 | return exc_type in self.suppress_exceptions | ||||
| 21 | 49 | ||||
| 22 | def __enter__(self): | 50 | def __enter__(self): | ||
| 23 | return self | 51 | return self | ||
| 24 | 52 | ||||
| 25 | def __exit__(self, exc_type, exc_value, traceback): | 53 | def __exit__(self, exc_type, exc_value, traceback): | ||
| 26 | if exc_type: | 54 | if exc_type: | ||
| n | 27 | if exc_type in self.log: | n | 55 | if self.is_exception_logged(exc_type): | 
| 28 | self.exception = exc_value | 56 | self.exception = exc_value | ||
| 29 | return True | 57 | return True | ||
| n | 30 | elif exc_type in self.suppress: | n | 58 | |
| 59 | if self.is_exception_suppressed(exc_type): | ||||
| 60 | self.exception = None | ||||
| 31 | return True | 61 | return True | ||
| t | 32 | else: | t | 62 | |
| 33 | self.log_exception(exc_value, exc_type) | ||||
| 34 | return False | ||||
| 35 | return False | 63 | return False | 
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| 
 | 
 | |||||||||
| f | 1 | class ExceptionStore: | f | 1 | class ExceptionStore: | 
| 2 | def __init__(self): | 2 | def __init__(self): | ||
| 3 | self.unhandled_exceptions = [] | 3 | self.unhandled_exceptions = [] | ||
| 4 | self.exception = None | 4 | self.exception = None | ||
| 5 | 5 | ||||
| 6 | def log_exception(self, exc_value, exc_type): | 6 | def log_exception(self, exc_value, exc_type): | ||
| 7 | self.unhandled_exceptions.append((exc_value, exc_type)) | 7 | self.unhandled_exceptions.append((exc_value, exc_type)) | ||
| 8 | self.exception = exc_value | 8 | self.exception = exc_value | ||
| 9 | 9 | ||||
| 10 | 10 | ||||
| 11 | class ProtectedSection(ExceptionStore): | 11 | class ProtectedSection(ExceptionStore): | ||
| 12 | def __init__(self, log=(), suppress=()): | 12 | def __init__(self, log=(), suppress=()): | ||
| n | 13 | if not all(isinstance(exc, type) and issubclass(exc, Exception) for exc in log): | n | 13 | if not all(issubclass(exc, Exception) for exc in log): | 
| 14 | raise TypeError("Мust be exception types") | 14 | raise TypeError("Must be exception types") | ||
| 15 | if not all(isinstance(exc, type) and issubclass(exc, Exception) for exc in suppress): | 15 | if not all(issubclass(exc, Exception) for exc in suppress): | ||
| 16 | raise TypeError("Мust be exception types") | 16 | raise TypeError("Must be exception types") | ||
| 17 | 17 | ||||
| 18 | super().__init__() | 18 | super().__init__() | ||
| 19 | self.log = log | 19 | self.log = log | ||
| 20 | self.suppress = suppress | 20 | self.suppress = suppress | ||
| 21 | 21 | ||||
| 22 | def __enter__(self): | 22 | def __enter__(self): | ||
| 23 | return self | 23 | return self | ||
| 24 | 24 | ||||
| 25 | def __exit__(self, exc_type, exc_value, traceback): | 25 | def __exit__(self, exc_type, exc_value, traceback): | ||
| 26 | if exc_type: | 26 | if exc_type: | ||
| n | 27 | if any(issubclass(exc_type, log_exc) for log_exc in self.log): | n | 27 | if exc_type in self.log: | 
| 28 | self.exception = exc_value | 28 | self.exception = exc_value | ||
| 29 | return True | 29 | return True | ||
| t | 30 | elif any(issubclass(exc_type, suppress_exc) for suppress_exc in self.suppress): | t | 30 | elif exc_type in self.suppress: | 
| 31 | return True | 31 | return True | ||
| 32 | else: | 32 | else: | ||
| 33 | self.log_exception(exc_value, exc_type) | 33 | self.log_exception(exc_value, exc_type) | ||
| 34 | return False | 34 | return False | ||
| 35 | return False | 35 | return False | 
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| 
 | 
 | |||||||||