errortools 2.0.0__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {errortools-2.0.0/errortools.egg-info → errortools-2.2.0}/PKG-INFO +1 -1
- errortools-2.2.0/_errortools/descriptor/__init__.py +2 -0
- errortools-2.2.0/_errortools/descriptor/errormsg.py +37 -0
- errortools-2.2.0/_errortools/descriptor/nonblankmsg.py +52 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/version.py +2 -2
- errortools-2.2.0/_errortools/wrappers/__init__.py +2 -0
- errortools-2.2.0/_errortools/wrappers/cache.py +95 -0
- errortools-2.2.0/_errortools/wrappers/ignore.py +115 -0
- {errortools-2.0.0 → errortools-2.2.0/errortools.egg-info}/PKG-INFO +1 -1
- {errortools-2.0.0 → errortools-2.2.0}/errortools.egg-info/SOURCES.txt +6 -0
- {errortools-2.0.0 → errortools-2.2.0}/setup.py +1 -1
- {errortools-2.0.0 → errortools-2.2.0}/AUTHORS.txt +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/LICENSE.txt +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/README.md +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/__init__.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/_cli.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/classes/__init__.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/classes/abc.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/classes/errorcodes.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/classes/group.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/classes/warn.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/cli.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/const.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/decorator/__init__.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/decorator/cache.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/decorator/deprecated.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/future.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/ignore.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/logging/__init__.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/logging/base.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/logging/level.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/logging/logger.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/logging/record.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/logging/sink.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/metadata.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/methods/__init__.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/methods/errorattr.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/methods/errordelattr.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/methods/errorhasattr.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/methods/errorsetattr.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/partial.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/py.typed +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/raises.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/_errortools/typing.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/errortools/__init__.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/errortools/__main__.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/errortools.egg-info/dependency_links.txt +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/errortools.egg-info/entry_points.txt +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/errortools.egg-info/requires.txt +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/errortools.egg-info/top_level.txt +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/setup.cfg +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/tests/__init__.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/tests/conftest.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/tests/run_tests.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/tests/test_abc.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/tests/test_cache.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/tests/test_const.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/tests/test_decorator.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/tests/test_descriptor.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/tests/test_errorcodes.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/tests/test_groups.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/tests/test_ignore.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/tests/test_logging.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/tests/test_mixins.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/tests/test_partials.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/tests/test_raises.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/tests/test_typing.py +0 -0
- {errortools-2.0.0 → errortools-2.2.0}/tests/test_warnings.py +0 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from typing import Optional, NoReturn
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# NOTE: The attribute returns the preset message when accessed.
|
|
5
|
+
# Any attempt to modify or delete it raises an ``AttributeError``.
|
|
6
|
+
class ErrorMsg:
|
|
7
|
+
"""Descriptor that creates a read-only attribute with a fixed message.
|
|
8
|
+
|
|
9
|
+
Args:
|
|
10
|
+
message: The fixed string returned when the attribute is accessed.
|
|
11
|
+
|
|
12
|
+
Example:
|
|
13
|
+
|
|
14
|
+
>>> class MyClass:
|
|
15
|
+
... status = ErrorMsg("This attribute is read-only")
|
|
16
|
+
>>> obj = MyClass()
|
|
17
|
+
>>> obj.status
|
|
18
|
+
'This attribute is read-only'
|
|
19
|
+
>>> obj.status = "new" # doctest: +SKIP
|
|
20
|
+
AttributeError: Modification of this attribute is not allowed!
|
|
21
|
+
>>> del obj.status # doctest: +SKIP
|
|
22
|
+
AttributeError: Deletion of this attribute is not allowed!
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
__slots__ = ("_message",)
|
|
26
|
+
|
|
27
|
+
def __init__(self, message: str) -> None:
|
|
28
|
+
self._message = message
|
|
29
|
+
|
|
30
|
+
def __get__(self, instance: Optional[object], owner: type[object]) -> str:
|
|
31
|
+
return self._message
|
|
32
|
+
|
|
33
|
+
def __set__(self, instance: object, value: object) -> NoReturn:
|
|
34
|
+
raise AttributeError("Modification of this attribute is not allowed!")
|
|
35
|
+
|
|
36
|
+
def __delete__(self, instance: object) -> NoReturn:
|
|
37
|
+
raise AttributeError("Deletion of this attribute is not allowed!")
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from typing import Any, Optional
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# NOTE: Validates the value is a string and not empty
|
|
5
|
+
# after trimming leading/trailing whitespace.
|
|
6
|
+
# Stores the cleaned (stripped) value on the instance.
|
|
7
|
+
class NonBlankErrorMsg:
|
|
8
|
+
"""Descriptor that validates an attribute is a non-blank string after stripping whitespace.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
message: Name/label used in error messages for validation failures.
|
|
12
|
+
|
|
13
|
+
Example:
|
|
14
|
+
>>> class ApiError:
|
|
15
|
+
... message = NonBlankErrorMsg("Error message")
|
|
16
|
+
... def __init__(self, msg: str):
|
|
17
|
+
... self.message = msg
|
|
18
|
+
>>> err = ApiError("Invalid token")
|
|
19
|
+
>>> err.message
|
|
20
|
+
'Invalid token'
|
|
21
|
+
>>> err = ApiError(" ") # doctest: +SKIP
|
|
22
|
+
ValueError: Error message can't be blank, must provide a valid error message
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
__slots__ = ("_message",)
|
|
26
|
+
|
|
27
|
+
def __init__(self, message: str) -> None:
|
|
28
|
+
self._message = message
|
|
29
|
+
|
|
30
|
+
def __get__(self, instance: Optional[object], owner: type[object]) -> str:
|
|
31
|
+
if instance is None:
|
|
32
|
+
return self._message
|
|
33
|
+
return instance.__dict__[self._message] # type: ignore
|
|
34
|
+
|
|
35
|
+
def __set__(self, instance: object, value: Any) -> None:
|
|
36
|
+
validated_value = self.validate(self._message, value)
|
|
37
|
+
instance.__dict__[self._message] = validated_value
|
|
38
|
+
|
|
39
|
+
def __delete__(self, instance: object) -> None:
|
|
40
|
+
raise AttributeError("Deletion of this attribute is not allowed!")
|
|
41
|
+
|
|
42
|
+
def validate(self, name: str, value: str) -> str:
|
|
43
|
+
if not isinstance(value, str):
|
|
44
|
+
raise ValueError(f"{name} must be a string type")
|
|
45
|
+
|
|
46
|
+
stripped_value = value.strip()
|
|
47
|
+
if not stripped_value:
|
|
48
|
+
raise ValueError(
|
|
49
|
+
f"{name} can't be blank, must provide a valid error message"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
return stripped_value
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
from collections import OrderedDict
|
|
2
|
+
from collections.abc import Hashable, Callable
|
|
3
|
+
from typing import Any, Generic, TypeVar, Optional, TypeAlias, NamedTuple
|
|
4
|
+
|
|
5
|
+
_T = TypeVar("_T", bound=Callable[..., Any])
|
|
6
|
+
_Key: TypeAlias = tuple[
|
|
7
|
+
str, tuple[Hashable, ...], tuple[tuple[Hashable, Hashable], ...]
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CacheInfo(NamedTuple):
|
|
12
|
+
"""Cache statistics, compatible with functools.lru_cache CacheInfo."""
|
|
13
|
+
|
|
14
|
+
hits: int
|
|
15
|
+
misses: int
|
|
16
|
+
maxsize: int | None
|
|
17
|
+
currsize: int
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ErrorCacheWrapper(Generic[_T]):
|
|
21
|
+
"""Wrapper class for error-cached functions."""
|
|
22
|
+
|
|
23
|
+
def __init__(self, func: _T, maxsize: int | None = 128) -> None:
|
|
24
|
+
self.__wrapped__ = func # Required for inspect module compatibility
|
|
25
|
+
self._func_name = func.__name__
|
|
26
|
+
|
|
27
|
+
# Validate maxsize
|
|
28
|
+
if maxsize is not None and maxsize < 0:
|
|
29
|
+
raise ValueError(
|
|
30
|
+
f"maxsize must be None or a non-negative integer, got {maxsize!r}"
|
|
31
|
+
)
|
|
32
|
+
self._maxsize = maxsize
|
|
33
|
+
self._cache: OrderedDict[_Key, Exception] = OrderedDict()
|
|
34
|
+
|
|
35
|
+
# Cache statistics
|
|
36
|
+
self._hits = 0
|
|
37
|
+
self._misses = 0
|
|
38
|
+
|
|
39
|
+
def __call__(self, *args: Hashable, **kwargs: Hashable) -> Any:
|
|
40
|
+
"""Execute the wrapped function and cache exceptions if raised (with LRU)."""
|
|
41
|
+
cache_key = self._make_key(args, kwargs)
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
result = self.__wrapped__(*args, **kwargs)
|
|
45
|
+
except Exception as exc:
|
|
46
|
+
if cache_key in self._cache:
|
|
47
|
+
self._hits += 1
|
|
48
|
+
else:
|
|
49
|
+
self._misses += 1
|
|
50
|
+
|
|
51
|
+
# Store in cache only if maxsize allows it (maxsize=0 means no caching)
|
|
52
|
+
if self._maxsize != 0:
|
|
53
|
+
self._cache[cache_key] = exc
|
|
54
|
+
# Evict least recently used if maxsize is exceeded
|
|
55
|
+
if self._maxsize is not None and len(self._cache) > self._maxsize:
|
|
56
|
+
self._cache.popitem(last=False) # FIFO = LRU for insert order
|
|
57
|
+
raise
|
|
58
|
+
else:
|
|
59
|
+
# Auto-clear cache for successful calls
|
|
60
|
+
self._cache.pop(cache_key, None)
|
|
61
|
+
return result
|
|
62
|
+
|
|
63
|
+
def _make_key(
|
|
64
|
+
self, args: tuple[Hashable, ...], kwargs: dict[str, Hashable]
|
|
65
|
+
) -> _Key:
|
|
66
|
+
"""Generate a unique hashable key."""
|
|
67
|
+
sorted_kwargs = tuple(sorted(kwargs.items()))
|
|
68
|
+
return (self._func_name, args, sorted_kwargs)
|
|
69
|
+
|
|
70
|
+
# ---------------------- Cache control methods (like lru_cache) ----------------------
|
|
71
|
+
def get_cached_error(
|
|
72
|
+
self, *args: Hashable, **kwargs: Hashable
|
|
73
|
+
) -> Optional[Exception]:
|
|
74
|
+
"""Get the cached exception for the given arguments (if exists)."""
|
|
75
|
+
cache_key = self._make_key(args, kwargs)
|
|
76
|
+
if cache_key in self._cache:
|
|
77
|
+
self._hits += 1
|
|
78
|
+
self._cache.move_to_end(cache_key) # Update LRU order (most recent)
|
|
79
|
+
return self._cache[cache_key]
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
def clear_cache(self) -> None:
|
|
83
|
+
"""Clear all cached exceptions and reset statistics."""
|
|
84
|
+
self._cache.clear()
|
|
85
|
+
self._hits = 0
|
|
86
|
+
self._misses = 0
|
|
87
|
+
|
|
88
|
+
def cache_info(self) -> CacheInfo:
|
|
89
|
+
"""Return cache statistics as a named tuple (compatible with lru_cache)."""
|
|
90
|
+
return CacheInfo(
|
|
91
|
+
hits=self._hits,
|
|
92
|
+
misses=self._misses,
|
|
93
|
+
maxsize=self._maxsize,
|
|
94
|
+
currsize=len(self._cache),
|
|
95
|
+
)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import traceback
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from typing import Any, Generic, TypeVar, Optional, TypeAlias
|
|
4
|
+
|
|
5
|
+
_T = TypeVar("_T", bound=Callable[..., Any])
|
|
6
|
+
_ExcType: TypeAlias = type[Exception]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class IgnoredError:
|
|
10
|
+
"""Information holder for ignored exceptions."""
|
|
11
|
+
|
|
12
|
+
__slots__ = (
|
|
13
|
+
"name",
|
|
14
|
+
"be_ignore",
|
|
15
|
+
"count",
|
|
16
|
+
"traceback",
|
|
17
|
+
"exception",
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
def __init__(self) -> None:
|
|
21
|
+
self.name: Optional[str] = None
|
|
22
|
+
self.be_ignore: bool = False
|
|
23
|
+
self.count: int = 0
|
|
24
|
+
self.traceback: Optional[str] = None
|
|
25
|
+
self.exception: Optional[Exception] = None
|
|
26
|
+
|
|
27
|
+
def reset(self) -> None:
|
|
28
|
+
self.name = None
|
|
29
|
+
self.be_ignore = False
|
|
30
|
+
self.traceback = None
|
|
31
|
+
self.exception = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ErrorIgnoreWrapper(Generic[_T]):
|
|
35
|
+
"""Context manager & decorator to ignore specified exceptions with rich info.
|
|
36
|
+
|
|
37
|
+
Catches and suppresses the given exception types within a ``with`` block
|
|
38
|
+
or when used as a decorator, while recording detailed information about
|
|
39
|
+
any suppressed exception.
|
|
40
|
+
|
|
41
|
+
When used as a context manager, ``__enter__`` returns an ``IgnoredError``
|
|
42
|
+
instance that provides the following attributes after the block executes.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
# Attributes:
|
|
46
|
+
# be_ignore (bool):
|
|
47
|
+
# ``True`` if an exception was suppressed during the block,
|
|
48
|
+
# ``False`` otherwise.
|
|
49
|
+
|
|
50
|
+
# name (str | None):
|
|
51
|
+
# The class name of the suppressed exception
|
|
52
|
+
# (e.g. ``'KeyError'``, ``'ValueError'``).
|
|
53
|
+
# ``None`` if no exception occurred.
|
|
54
|
+
|
|
55
|
+
# count (int):
|
|
56
|
+
# Number of exceptions suppressed in this context block.
|
|
57
|
+
# Typically 1 unless the context manager is reused.
|
|
58
|
+
|
|
59
|
+
# exception (Exception | None):
|
|
60
|
+
# The original exception instance that was caught and suppressed.
|
|
61
|
+
# ``None`` if no exception occurred.
|
|
62
|
+
|
|
63
|
+
# traceback (str | None):
|
|
64
|
+
# Formatted traceback string showing where the suppressed exception
|
|
65
|
+
# occurred. Useful for debugging. ``None`` if no exception occurred.
|
|
66
|
+
|
|
67
|
+
# Example:
|
|
68
|
+
# >>> from errortools import ignore
|
|
69
|
+
# >>> with ignore(KeyError) as err:
|
|
70
|
+
# ... _ = {}["missing"]
|
|
71
|
+
# >>> err.be_ignore
|
|
72
|
+
# True
|
|
73
|
+
# >>> err.name
|
|
74
|
+
# 'KeyError'
|
|
75
|
+
|
|
76
|
+
def __init__(self, *excs: _ExcType) -> None:
|
|
77
|
+
for exc in excs:
|
|
78
|
+
if not isinstance(exc, type) or not issubclass(exc, Exception):
|
|
79
|
+
raise TypeError(f"Expected Exception subclass, got {exc!r}")
|
|
80
|
+
|
|
81
|
+
self._excs = excs
|
|
82
|
+
self._info = IgnoredError()
|
|
83
|
+
|
|
84
|
+
def __enter__(self) -> IgnoredError:
|
|
85
|
+
self._info.reset()
|
|
86
|
+
return self._info
|
|
87
|
+
|
|
88
|
+
def __exit__(
|
|
89
|
+
self,
|
|
90
|
+
exc_type: Optional[_ExcType],
|
|
91
|
+
exc_val: Optional[Exception],
|
|
92
|
+
exc_tb: Optional[Any],
|
|
93
|
+
) -> bool:
|
|
94
|
+
if exc_type is None:
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
if exc_type not in self._excs:
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
self._info.name = exc_type.__name__
|
|
101
|
+
self._info.be_ignore = True
|
|
102
|
+
self._info.count += 1
|
|
103
|
+
self._info.traceback = "".join(
|
|
104
|
+
traceback.format_exception(exc_type, exc_val, exc_tb)
|
|
105
|
+
)
|
|
106
|
+
self._info.exception = exc_val
|
|
107
|
+
return True
|
|
108
|
+
|
|
109
|
+
def __call__(self, func: _T) -> _T:
|
|
110
|
+
def wrapped(*args: Any, **kwargs: Any) -> Any:
|
|
111
|
+
with self:
|
|
112
|
+
return func(*args, **kwargs)
|
|
113
|
+
|
|
114
|
+
wrapped.__wrapped__ = func # type: ignore
|
|
115
|
+
return wrapped # type: ignore
|
|
@@ -22,6 +22,9 @@ _errortools/classes/warn.py
|
|
|
22
22
|
_errortools/decorator/__init__.py
|
|
23
23
|
_errortools/decorator/cache.py
|
|
24
24
|
_errortools/decorator/deprecated.py
|
|
25
|
+
_errortools/descriptor/__init__.py
|
|
26
|
+
_errortools/descriptor/errormsg.py
|
|
27
|
+
_errortools/descriptor/nonblankmsg.py
|
|
25
28
|
_errortools/logging/__init__.py
|
|
26
29
|
_errortools/logging/base.py
|
|
27
30
|
_errortools/logging/level.py
|
|
@@ -33,6 +36,9 @@ _errortools/methods/errorattr.py
|
|
|
33
36
|
_errortools/methods/errordelattr.py
|
|
34
37
|
_errortools/methods/errorhasattr.py
|
|
35
38
|
_errortools/methods/errorsetattr.py
|
|
39
|
+
_errortools/wrappers/__init__.py
|
|
40
|
+
_errortools/wrappers/cache.py
|
|
41
|
+
_errortools/wrappers/ignore.py
|
|
36
42
|
errortools/__init__.py
|
|
37
43
|
errortools/__main__.py
|
|
38
44
|
errortools.egg-info/PKG-INFO
|
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
|
|
3
3
|
setup(
|
|
4
4
|
name="errortools",
|
|
5
|
-
version="2.
|
|
5
|
+
version="2.2.0",
|
|
6
6
|
description="errortools - a toolset for working with Python exceptions and warnings and logging.",
|
|
7
7
|
long_description=open("README.md", encoding="utf-8").read(),
|
|
8
8
|
long_description_content_type="text/markdown",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|