rebulk 4.2.0__tar.gz → 4.2.2__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.
- {rebulk-4.2.0 → rebulk-4.2.2}/PKG-INFO +1 -1
- {rebulk-4.2.0 → rebulk-4.2.2}/pyproject.toml +6 -1
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/__version__.py +1 -2
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/chain.py +4 -1
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/loose.py +3 -35
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/match.py +5 -1
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/pattern.py +23 -40
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/rebulk.py +0 -3
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/test/test_chain.py +21 -1
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/test/test_match.py +13 -1
- rebulk-4.2.2/rebulk/test/test_utils.py +29 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/toposort.py +7 -15
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/utils.py +17 -4
- {rebulk-4.2.0 → rebulk-4.2.2}/LICENSE +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/README.md +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/__init__.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/builder.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/debug.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/formatters.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/introspector.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/key.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/processors.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/py.typed +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/remodule.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/rules.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/test/__init__.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/test/default_rules_module.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/test/rebulk_rules_module.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/test/rules_module.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/test/test_debug.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/test/test_introspector.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/test/test_key.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/test/test_loose.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/test/test_model.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/test/test_pattern.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/test/test_processors.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/test/test_rebulk.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/test/test_rules.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/test/test_toposort.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/test/test_validators.py +0 -0
- {rebulk-4.2.0 → rebulk-4.2.2}/rebulk/validators.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "rebulk"
|
|
3
|
-
version = "4.2.
|
|
3
|
+
version = "4.2.2"
|
|
4
4
|
description = "Rebulk - Define simple search patterns in bulk to perform advanced matching on any string."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -105,6 +105,11 @@ select = [
|
|
|
105
105
|
python_version = "3.10"
|
|
106
106
|
files = ["rebulk"]
|
|
107
107
|
strict = true
|
|
108
|
+
|
|
109
|
+
[[tool.mypy.overrides]]
|
|
110
|
+
# `regex` (the optional `native` extra) ships no type information; scope the
|
|
111
|
+
# relaxation to it instead of silencing missing imports across the whole project.
|
|
112
|
+
module = ["regex.*"]
|
|
108
113
|
ignore_missing_imports = true
|
|
109
114
|
|
|
110
115
|
[tool.pytest.ini_options]
|
|
@@ -216,7 +216,10 @@ class Chain(Pattern, Builder):
|
|
|
216
216
|
@staticmethod
|
|
217
217
|
def _group_by_match_index(matches: Iterable[Match]) -> dict[int, list[Match]]:
|
|
218
218
|
grouped_matches_dict: dict[int, list[Match]] = {}
|
|
219
|
-
|
|
219
|
+
# groupby only groups consecutive equal keys, so sort by match_index first
|
|
220
|
+
# to avoid splitting (and overwriting) groups when matches are unordered.
|
|
221
|
+
sorted_matches = sorted(matches, key=lambda m: m.match_index)
|
|
222
|
+
for match_index, match in itertools.groupby(sorted_matches, lambda m: m.match_index):
|
|
220
223
|
grouped_matches_dict[match_index] = list(match)
|
|
221
224
|
return grouped_matches_dict
|
|
222
225
|
|
|
@@ -5,17 +5,9 @@ Various utilities functions
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
-
from inspect import isclass
|
|
8
|
+
from inspect import getfullargspec, isclass
|
|
9
9
|
from typing import TYPE_CHECKING, Any, cast
|
|
10
10
|
|
|
11
|
-
try:
|
|
12
|
-
from inspect import getfullargspec as getargspec
|
|
13
|
-
|
|
14
|
-
_FULLARGSPEC_SUPPORTED = True
|
|
15
|
-
except ImportError:
|
|
16
|
-
_FULLARGSPEC_SUPPORTED = False
|
|
17
|
-
from inspect import getargspec # type: ignore[assignment] # removed in Python 3.11, dead on supported versions
|
|
18
|
-
|
|
19
11
|
from .utils import is_iterable
|
|
20
12
|
|
|
21
13
|
if TYPE_CHECKING:
|
|
@@ -66,7 +58,7 @@ def function_args(callable_: Any, *args: Any, **kwargs: Any) -> tuple[Any, Any]:
|
|
|
66
58
|
:return: (args, kwargs) matching the function signature
|
|
67
59
|
:rtype: tuple
|
|
68
60
|
"""
|
|
69
|
-
argspec =
|
|
61
|
+
argspec = getfullargspec(callable_)
|
|
70
62
|
return argspec_args(argspec, False, *args, **kwargs)
|
|
71
63
|
|
|
72
64
|
|
|
@@ -83,7 +75,7 @@ def constructor_args(class_: Any, *args: Any, **kwargs: Any) -> tuple[Any, Any]:
|
|
|
83
75
|
:return: (args, kwargs) matching the function signature
|
|
84
76
|
:rtype: tuple
|
|
85
77
|
"""
|
|
86
|
-
argspec =
|
|
78
|
+
argspec = getfullargspec(_constructor(class_))
|
|
87
79
|
return argspec_args(argspec, True, *args, **kwargs)
|
|
88
80
|
|
|
89
81
|
|
|
@@ -107,30 +99,6 @@ def argspec_args(argspec: FullArgSpec, constructor: bool, *args: Any, **kwargs:
|
|
|
107
99
|
return call_args, call_kwarg
|
|
108
100
|
|
|
109
101
|
|
|
110
|
-
if not _FULLARGSPEC_SUPPORTED:
|
|
111
|
-
|
|
112
|
-
def argspec_args_legacy(argspec: Any, constructor: bool, *args: Any, **kwargs: Any) -> tuple[Any, Any]:
|
|
113
|
-
"""
|
|
114
|
-
Return (args, kwargs) matching the argspec object
|
|
115
|
-
|
|
116
|
-
:param argspec: argspec to use
|
|
117
|
-
:type argspec: argspec
|
|
118
|
-
:param constructor: is it a constructor ?
|
|
119
|
-
:type constructor: bool
|
|
120
|
-
:param args:
|
|
121
|
-
:type args:
|
|
122
|
-
:param kwargs:
|
|
123
|
-
:type kwargs:
|
|
124
|
-
:return: (args, kwargs) matching the function signature
|
|
125
|
-
:rtype: tuple
|
|
126
|
-
"""
|
|
127
|
-
call_kwarg = kwargs if argspec.keywords else {k: kwargs[k] for k in kwargs if k in argspec.args}
|
|
128
|
-
call_args = args if argspec.varargs else args[: len(argspec.args) - (1 if constructor else 0)]
|
|
129
|
-
return call_args, call_kwarg
|
|
130
|
-
|
|
131
|
-
argspec_args = argspec_args_legacy
|
|
132
|
-
|
|
133
|
-
|
|
134
102
|
def ensure_list(param: Any) -> list[Any]:
|
|
135
103
|
"""
|
|
136
104
|
Retrieves a list from given parameter.
|
|
@@ -1164,7 +1164,11 @@ class Match:
|
|
|
1164
1164
|
return self.end - self.start
|
|
1165
1165
|
|
|
1166
1166
|
def __hash__(self) -> int:
|
|
1167
|
-
|
|
1167
|
+
# Hash on the span only (a subset of the __eq__ fields), never on the
|
|
1168
|
+
# mutable value: a match kept in a set/dict must stay findable after its
|
|
1169
|
+
# value is changed. Equal matches still share a hash; same-span matches
|
|
1170
|
+
# with different values collide harmlessly and __eq__ tells them apart.
|
|
1171
|
+
return hash((Match, self.start, self.end))
|
|
1168
1172
|
|
|
1169
1173
|
def __eq__(self, other: object) -> bool:
|
|
1170
1174
|
if isinstance(other, Match):
|
|
@@ -20,6 +20,11 @@ if TYPE_CHECKING:
|
|
|
20
20
|
from collections.abc import Callable, Iterator, Sequence
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
def _callable_or_none(value: Any) -> Callable[..., Any] | None:
|
|
24
|
+
"""Return ``value`` if it is callable, else ``None``."""
|
|
25
|
+
return value if callable(value) else None
|
|
26
|
+
|
|
27
|
+
|
|
23
28
|
class BasePattern(metaclass=ABCMeta):
|
|
24
29
|
"""
|
|
25
30
|
Base class for Pattern like objects
|
|
@@ -144,21 +149,9 @@ class Pattern(BasePattern, metaclass=ABCMeta):
|
|
|
144
149
|
self._log_level = log_level
|
|
145
150
|
self._properties = properties
|
|
146
151
|
self.defined_at = debug.defined_at()
|
|
147
|
-
self.post_processor
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
else:
|
|
151
|
-
self.post_processor = post_processor
|
|
152
|
-
self.pre_match_processor: Callable[..., Any] | None
|
|
153
|
-
if not callable(pre_match_processor):
|
|
154
|
-
self.pre_match_processor = None
|
|
155
|
-
else:
|
|
156
|
-
self.pre_match_processor = pre_match_processor
|
|
157
|
-
self.post_match_processor: Callable[..., Any] | None
|
|
158
|
-
if not callable(post_match_processor):
|
|
159
|
-
self.post_match_processor = None
|
|
160
|
-
else:
|
|
161
|
-
self.post_match_processor = post_match_processor
|
|
152
|
+
self.post_processor = _callable_or_none(post_processor)
|
|
153
|
+
self.pre_match_processor = _callable_or_none(pre_match_processor)
|
|
154
|
+
self.post_match_processor = _callable_or_none(post_match_processor)
|
|
162
155
|
|
|
163
156
|
@property
|
|
164
157
|
def log_level(self) -> int:
|
|
@@ -517,32 +510,22 @@ class RePattern(Pattern):
|
|
|
517
510
|
for i in range(1, pattern.groups + 1):
|
|
518
511
|
name = names.get(i, main_match.name)
|
|
519
512
|
if self.repeated_captures:
|
|
520
|
-
|
|
521
|
-
child_match = Match(
|
|
522
|
-
start,
|
|
523
|
-
end,
|
|
524
|
-
name=name,
|
|
525
|
-
parent=main_match,
|
|
526
|
-
pattern=self,
|
|
527
|
-
input_string=input_string,
|
|
528
|
-
**self._children_match_kwargs,
|
|
529
|
-
)
|
|
530
|
-
if child_match:
|
|
531
|
-
main_match.children.append(child_match)
|
|
513
|
+
spans = match_object.spans(i)
|
|
532
514
|
else:
|
|
533
|
-
|
|
534
|
-
if
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
515
|
+
span = match_object.span(i)
|
|
516
|
+
spans = [span] if span[0] > -1 and span[1] > -1 else []
|
|
517
|
+
for child_start, child_end in spans:
|
|
518
|
+
child_match = Match(
|
|
519
|
+
child_start,
|
|
520
|
+
child_end,
|
|
521
|
+
name=name,
|
|
522
|
+
parent=main_match,
|
|
523
|
+
pattern=self,
|
|
524
|
+
input_string=input_string,
|
|
525
|
+
**self._children_match_kwargs,
|
|
526
|
+
)
|
|
527
|
+
if child_match:
|
|
528
|
+
main_match.children.append(child_match)
|
|
546
529
|
|
|
547
530
|
if main_match:
|
|
548
531
|
yield main_match
|
|
@@ -189,9 +189,6 @@ class Rebulk(Builder):
|
|
|
189
189
|
pattern_matches = pattern.matches(cast("str", matches.input_string), context)
|
|
190
190
|
if pattern_matches:
|
|
191
191
|
log(pattern.log_level, "Pattern has %s match(es). (%s)", len(pattern_matches), pattern)
|
|
192
|
-
else:
|
|
193
|
-
pass
|
|
194
|
-
# log(pattern.log_level, "Pattern doesn't match. (%s)" % (pattern,))
|
|
195
192
|
for match in pattern_matches:
|
|
196
193
|
if match.marker:
|
|
197
194
|
log(pattern.log_level, "Marker found. (%s)", match)
|
|
@@ -8,11 +8,12 @@ from typing import TYPE_CHECKING, Any
|
|
|
8
8
|
from rebulk.pattern import FunctionalPattern, RePattern, StringPattern
|
|
9
9
|
|
|
10
10
|
from ..chain import Chain
|
|
11
|
+
from ..match import Match
|
|
11
12
|
from ..rebulk import Rebulk
|
|
12
13
|
from ..validators import chars_surround
|
|
13
14
|
|
|
14
15
|
if TYPE_CHECKING:
|
|
15
|
-
from ..match import
|
|
16
|
+
from ..match import Matches
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
def test_chain_close() -> None:
|
|
@@ -489,3 +490,22 @@ def test_chain_breaker_defaults2() -> None:
|
|
|
489
490
|
matches[0].value = 1
|
|
490
491
|
matches[1].value = 2
|
|
491
492
|
matches[2].value = 3
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def test_group_by_match_index_sorts_unordered_input() -> None:
|
|
496
|
+
# groupby only groups consecutive equal keys, so _group_by_match_index must
|
|
497
|
+
# sort by match_index first; otherwise unordered matches split (and overwrite)
|
|
498
|
+
# groups sharing an index.
|
|
499
|
+
matches: list[Match] = []
|
|
500
|
+
for start, match_index in ((0, 2), (5, 0), (10, 1), (15, 2)):
|
|
501
|
+
match = Match(start, start + 1, name="test")
|
|
502
|
+
match.match_index = match_index
|
|
503
|
+
matches.append(match)
|
|
504
|
+
|
|
505
|
+
grouped = Chain._group_by_match_index(matches)
|
|
506
|
+
|
|
507
|
+
assert sorted(grouped) == [0, 1, 2]
|
|
508
|
+
assert [m.start for m in grouped[0]] == [5]
|
|
509
|
+
assert [m.start for m in grouped[1]] == [10]
|
|
510
|
+
# both match_index=2 matches are kept, in their original relative order
|
|
511
|
+
assert [m.start for m in grouped[2]] == [0, 15]
|
|
@@ -51,12 +51,24 @@ class TestMatchClass:
|
|
|
51
51
|
other = object()
|
|
52
52
|
|
|
53
53
|
assert hash(match1) != hash(match2)
|
|
54
|
-
|
|
54
|
+
# same span, different value: hashes collide (hash must not depend on the
|
|
55
|
+
# mutable value); equality still tells them apart below.
|
|
56
|
+
assert hash(match1) == hash(match3)
|
|
55
57
|
|
|
56
58
|
assert match1 != other
|
|
57
59
|
assert match1 != match2
|
|
58
60
|
assert match1 != match3
|
|
59
61
|
|
|
62
|
+
def test_hash_is_stable_under_value_mutation(self) -> None:
|
|
63
|
+
match = Match(1, 3, value="es")
|
|
64
|
+
bucket = {match}
|
|
65
|
+
|
|
66
|
+
match.value = "changed"
|
|
67
|
+
|
|
68
|
+
# The hash must not change when the value is mutated, so the match stays
|
|
69
|
+
# findable in the set it was added to.
|
|
70
|
+
assert match in bucket
|
|
71
|
+
|
|
60
72
|
def test_length(self) -> None:
|
|
61
73
|
match1 = Match(0, 4, value="test")
|
|
62
74
|
match2 = Match(0, 2, value="spanIsUsed")
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from ..utils import extend_safe
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_extend_safe_hashable() -> None:
|
|
10
|
+
target = [1, 2]
|
|
11
|
+
extend_safe(target, [2, 3, 3, 4])
|
|
12
|
+
assert target == [1, 2, 3, 4]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_extend_safe_unhashable() -> None:
|
|
16
|
+
# Unhashable elements (e.g. introspected list/dict property values) must
|
|
17
|
+
# still be deduplicated via linear membership instead of raising.
|
|
18
|
+
target: list[Any] = [["a"]]
|
|
19
|
+
extend_safe(target, [["a"], ["b"], ["b"]])
|
|
20
|
+
assert target == [["a"], ["b"]]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_extend_safe_mixed_falls_back_without_duplicating() -> None:
|
|
24
|
+
# Hashable elements go through the fast set path; the first unhashable one
|
|
25
|
+
# switches to the linear fallback, and already-added elements are not
|
|
26
|
+
# appended twice.
|
|
27
|
+
target: list[Any] = [1]
|
|
28
|
+
extend_safe(target, [1, 2, ["x"], ["x"], 2])
|
|
29
|
+
assert target == [1, 2, ["x"]]
|
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
|
-
#
|
|
2
|
+
# Vendored from toposort 1.4 (https://bitbucket.org/ericvsmith/toposort),
|
|
3
|
+
# Copyright 2014 True Blade Systems, Inc., licensed under the Apache License,
|
|
4
|
+
# Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0).
|
|
3
5
|
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
# You may obtain a copy of the License at
|
|
7
|
-
#
|
|
8
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
#
|
|
10
|
-
# Original:
|
|
11
|
-
# - https://bitbucket.org/ericvsmith/toposort (1.4)
|
|
12
|
-
# Modifications:
|
|
13
|
-
# - merged Pull request #2 for CyclicDependency error
|
|
14
|
-
# - import reduce as original name
|
|
15
|
-
# - support python 2.6 dict comprehension
|
|
6
|
+
# Local changes: CyclicDependency error (upstream pull request #2), Python 3
|
|
7
|
+
# only, fully type-annotated.
|
|
16
8
|
|
|
17
9
|
from __future__ import annotations
|
|
18
10
|
|
|
@@ -27,7 +19,7 @@ _T = TypeVar("_T")
|
|
|
27
19
|
|
|
28
20
|
class CyclicDependency(ValueError):
|
|
29
21
|
def __init__(self, cyclic: dict[_T, set[_T]]) -> None:
|
|
30
|
-
s = "Cyclic dependencies exist among these items: {
|
|
22
|
+
s = f"Cyclic dependencies exist among these items: {', '.join(repr(x) for x in cyclic.items())}"
|
|
31
23
|
super().__init__(s)
|
|
32
24
|
self.cyclic = cyclic
|
|
33
25
|
|
|
@@ -38,7 +30,7 @@ def toposort(data: dict[_T, set[_T]]) -> Iterator[set[_T]]:
|
|
|
38
30
|
and whose values are a set of dependent items. Output is a list of
|
|
39
31
|
sets in topological order. The first set consists of items with no
|
|
40
32
|
dependences, each subsequent set consists of items that depend upon
|
|
41
|
-
items in the
|
|
33
|
+
items in the preceding sets.
|
|
42
34
|
:param data:
|
|
43
35
|
:type data:
|
|
44
36
|
:return:
|
|
@@ -102,15 +102,28 @@ def is_iterable(obj: Any) -> bool:
|
|
|
102
102
|
|
|
103
103
|
def extend_safe(target: list[_T], source: Iterable[_T]) -> None:
|
|
104
104
|
"""
|
|
105
|
-
Extends
|
|
105
|
+
Extends target with elements from source that are not already present.
|
|
106
|
+
|
|
107
|
+
Uses a set for O(1) membership checks when elements are hashable (the common
|
|
108
|
+
case: patterns and rules are deduplicated on every ``matches()`` call), and
|
|
109
|
+
falls back to linear membership when they are not (e.g. unhashable
|
|
110
|
+
introspected property values).
|
|
111
|
+
|
|
106
112
|
:param target:
|
|
107
113
|
:type target: list
|
|
108
114
|
:param source:
|
|
109
115
|
:type source: list
|
|
110
116
|
"""
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
117
|
+
try:
|
|
118
|
+
seen = set(target)
|
|
119
|
+
for elt in source:
|
|
120
|
+
if elt not in seen:
|
|
121
|
+
target.append(elt)
|
|
122
|
+
seen.add(elt)
|
|
123
|
+
except TypeError: # unhashable element: fall back to linear membership checks
|
|
124
|
+
for elt in source:
|
|
125
|
+
if elt not in target:
|
|
126
|
+
target.append(elt)
|
|
114
127
|
|
|
115
128
|
|
|
116
129
|
class _Ref:
|
|
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
|