Предизвикателства > Безгрешен блок > Решения > Решението на Йоан Байчев

Резултати
1 точки от тестове
1 точки от учител

2 точки общо

2 успешни теста
0 неуспешни теста
Код

 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 е да осигури начин за запис на изключения, които не са подтиснати или логнати, така че те да могат да бъдат анализирани или обработени в бъдеще.
История

f1def optimize_exception_check(attr_name):f1def 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 True6                return True
77
8            result = func(self, exc_type)  # True or False8            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 result11            return result
12        return wrapper12        return wrapper
13    return decorator13    return decorator
1414
1515
16class ProtectedSection:16class ProtectedSection:
17    __counter = 017    __counter = 0
1818
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")
2424
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")
2929
30        self._current_exception = None  # Saves the specific or last "updated" exception in case of reuse of an already created instance30        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 times33        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 times34        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 ID35        self.exceptions_by_session = {}  # Dictionary to store exceptions with their session ID
3636
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_exceptions39        return exc_type in self.log_exceptions
4040
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_exceptions43        return exc_type in self.suppress_exceptions
4444
45    def __enter__(self):45    def __enter__(self):
tt46        self._current_exception = None
46        return self47        return self
4748
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_value52                self.exceptions_by_session[self.__counter] = exc_value
52                self.__counter += 153                self.__counter += 1
53                self._current_exception = exc_value54                self._current_exception = exc_value
54                return True55                return True
5556
56            if self.is_exception_suppressed(exc_type):57            if self.is_exception_suppressed(exc_type):
57                self._current_exception = None58                self._current_exception = None
58                self.__counter += 159                self.__counter += 1
59                return True60                return True
6061
61        return False62        return False
6263
63    @property64    @property
64    def exception(self):65    def exception(self):
65        return self._current_exception66        return self._current_exception
6667
67    @property68    @property
68    def exceptions(self):69    def exceptions(self):
69        return self.exceptions_by_session70        return self.exceptions_by_session
7071
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
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1def optimize_exception_check(attr_name):f1def 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 True6                return True
77
n8            result = func(self, exc_type) # true or falsen8            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 result11            return result
12        return wrapper12        return wrapper
13    return decorator13    return decorator
1414
1515
16class ProtectedSection:16class ProtectedSection:
nn17    __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")
2224
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")
2729
n28        self.exception = Nonen30        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 times33        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 times34        self._checked_exceptions_suppress = set()  # Optimization to avoid checking one exception n times
nn35        self.exceptions_by_session = {}  # Dictionary to store exceptions with their session ID
3336
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_exceptions39        return exc_type in self.log_exceptions
3740
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_exceptions43        return exc_type in self.suppress_exceptions
4144
42    def __enter__(self):45    def __enter__(self):
43        return self46        return self
4447
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):
nn51                self.exceptions_by_session[self.__counter] = exc_value
52                self.__counter += 1
48                self.exception = exc_value53                self._current_exception = exc_value
49                return True54                return True
5055
51            if self.is_exception_suppressed(exc_type):56            if self.is_exception_suppressed(exc_type):
n52                self.exception = Nonen57                self._current_exception = None
58                self.__counter += 1
53                return True59                return True
5460
55        return False61        return False
tt62 
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
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

n1def optimize_exception_check_for_logs(func):n1def 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
57
n6        result = func(self, exc_type) # 1 or 0n8            result = func(self, exc_type) # true or false
7        self._checked_exceptions_logs[exc_type] = result9            if result:
8 10                cache.add(exc_type)
9        return result11            return result
10    return wrapper12        return wrapper
11 13    return decorator
12def 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
2214
2315
24class ProtectedSection:16class 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")
3022
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")
3527
n36        self.log_exceptions = logn
37        self.suppress_exceptions = suppress
38        self.exception = None28        self.exception = None
nn29        self.log_exceptions = set(log)
30        self.suppress_exceptions = set(suppress)
39        self._checked_exceptions_logs = {}  # Optimization to avoid checking one exception n times31        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 times32        self._checked_exceptions_suppress = set()  # Optimization to avoid checking one exception n times
4133
n42    @optimize_exception_check_for_logsn34    @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_exceptions36        return exc_type in self.log_exceptions
4537
t46    @optimize_exception_check_for_suppresst38    @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_exceptions40        return exc_type in self.suppress_exceptions
4941
50    def __enter__(self):42    def __enter__(self):
51        return self43        return self
5244
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_value48                self.exception = exc_value
57                return True49                return True
5850
59            if self.is_exception_suppressed(exc_type):51            if self.is_exception_suppressed(exc_type):
60                self.exception = None52                self.exception = None
61                return True53                return True
6254
63        return False55        return False
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

n1class ExceptionStore:n1def 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 = None4            return self._checked_exceptions_logs[exc_type]
55
n6    def log_exception(self, exc_value, exc_type):n6        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_value8 
9        return result
10    return wrapper
11 
12def 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
922
1023
n11class ProtectedSection(ExceptionStore):n24class ProtectedSection:
12    def __init__(self, log=(), suppress=()):25    def __init__(self, log=(), suppress=()):
nn26        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("Musbe exception types")29                    raise TypeError("Noa valid exception type")
15        if not all(issubclass(exc, Exception) for exc in suppress):
16            raise TypeError("Must be exception types")
1730
n18        super().__init__()n31        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 = log36        self.log_exceptions = log
20        self.suppress = suppress37        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
2149
22    def __enter__(self):50    def __enter__(self):
23        return self51        return self
2452
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:
n27            if exc_type in self.log:n55            if self.is_exception_logged(exc_type):
28                self.exception = exc_value56                self.exception = exc_value
29                return True57                return True
n30            elif exc_type in self.suppress:n58 
59            if self.is_exception_suppressed(exc_type):
60                self.exception = None
31                return True61                return True
t32            else:t62 
33                self.log_exception(exc_value, exc_type)
34                return False
35        return False63        return False
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1class ExceptionStore:f1class ExceptionStore:
2    def __init__(self):2    def __init__(self):
3        self.unhandled_exceptions = []3        self.unhandled_exceptions = []
4        self.exception = None4        self.exception = None
55
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_value8        self.exception = exc_value
99
1010
11class ProtectedSection(ExceptionStore):11class ProtectedSection(ExceptionStore):
12    def __init__(self, log=(), suppress=()):12    def __init__(self, log=(), suppress=()):
n13        if not all(isinstance(exc, type) and issubclass(exc, Exception) for exc in log):n13        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")
1717
18        super().__init__()18        super().__init__()
19        self.log = log19        self.log = log
20        self.suppress = suppress20        self.suppress = suppress
2121
22    def __enter__(self):22    def __enter__(self):
23        return self23        return self
2424
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:
n27            if any(issubclass(exc_type, log_exc) for log_exc in self.log):n27            if exc_type in self.log:
28                self.exception = exc_value28                self.exception = exc_value
29                return True29                return True
t30            elif any(issubclass(exc_type, suppress_exc) for suppress_exc in self.suppress):t30            elif exc_type in self.suppress:
31                return True31                return True
32            else:32            else:
33                self.log_exception(exc_value, exc_type)33                self.log_exception(exc_value, exc_type)
34                return False34                return False
35        return False35        return False
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op