rebulk 4.2.1__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.
Files changed (41) hide show
  1. {rebulk-4.2.1 → rebulk-4.2.2}/PKG-INFO +1 -1
  2. {rebulk-4.2.1 → rebulk-4.2.2}/pyproject.toml +6 -1
  3. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/__version__.py +1 -2
  4. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/loose.py +3 -35
  5. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/match.py +5 -1
  6. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/pattern.py +23 -40
  7. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/rebulk.py +0 -3
  8. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/test/test_match.py +13 -1
  9. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/toposort.py +7 -15
  10. {rebulk-4.2.1 → rebulk-4.2.2}/LICENSE +0 -0
  11. {rebulk-4.2.1 → rebulk-4.2.2}/README.md +0 -0
  12. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/__init__.py +0 -0
  13. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/builder.py +0 -0
  14. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/chain.py +0 -0
  15. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/debug.py +0 -0
  16. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/formatters.py +0 -0
  17. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/introspector.py +0 -0
  18. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/key.py +0 -0
  19. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/processors.py +0 -0
  20. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/py.typed +0 -0
  21. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/remodule.py +0 -0
  22. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/rules.py +0 -0
  23. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/test/__init__.py +0 -0
  24. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/test/default_rules_module.py +0 -0
  25. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/test/rebulk_rules_module.py +0 -0
  26. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/test/rules_module.py +0 -0
  27. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/test/test_chain.py +0 -0
  28. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/test/test_debug.py +0 -0
  29. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/test/test_introspector.py +0 -0
  30. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/test/test_key.py +0 -0
  31. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/test/test_loose.py +0 -0
  32. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/test/test_model.py +0 -0
  33. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/test/test_pattern.py +0 -0
  34. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/test/test_processors.py +0 -0
  35. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/test/test_rebulk.py +0 -0
  36. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/test/test_rules.py +0 -0
  37. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/test/test_toposort.py +0 -0
  38. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/test/test_utils.py +0 -0
  39. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/test/test_validators.py +0 -0
  40. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/utils.py +0 -0
  41. {rebulk-4.2.1 → rebulk-4.2.2}/rebulk/validators.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rebulk
3
- Version: 4.2.1
3
+ Version: 4.2.2
4
4
  Summary: Rebulk - Define simple search patterns in bulk to perform advanced matching on any string.
5
5
  Keywords: re,regexp,regular expression,search,pattern,string,match
6
6
  Author: Rémi Alvergnat
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "rebulk"
3
- version = "4.2.1"
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]
@@ -3,5 +3,4 @@
3
3
  Version module
4
4
  """
5
5
 
6
- # pragma: no cover
7
- __version__ = "4.2.1"
6
+ __version__ = "4.2.2"
@@ -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 = getargspec(callable_)
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 = getargspec(_constructor(class_))
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
- return hash(Match) + hash(self.start) + hash(self.end) + hash(self.value)
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: Callable[..., Any] | None
148
- if not callable(post_processor):
149
- self.post_processor = None
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
- for start, end in match_object.spans(i):
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
- start, end = match_object.span(i)
534
- if start > -1 and end > -1:
535
- child_match = Match(
536
- start,
537
- end,
538
- name=name,
539
- parent=main_match,
540
- pattern=self,
541
- input_string=input_string,
542
- **self._children_match_kwargs,
543
- )
544
- if child_match:
545
- main_match.children.append(child_match)
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)
@@ -51,12 +51,24 @@ class TestMatchClass:
51
51
  other = object()
52
52
 
53
53
  assert hash(match1) != hash(match2)
54
- assert hash(match1) != hash(match3)
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")
@@ -1,18 +1,10 @@
1
1
  #!/usr/bin/env python
2
- # Copyright 2014 True Blade Systems, Inc.
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
- # Licensed under the Apache License, Version 2.0 (the "License");
5
- # you may not use this file except in compliance with the License.
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: {}".format(", ".join(repr(x) for x in cyclic.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 preceeding sets.
33
+ items in the preceding sets.
42
34
  :param data:
43
35
  :type data:
44
36
  :return:
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