omlish 0.0.0.dev247__py3-none-any.whl → 0.0.0.dev249__py3-none-any.whl

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 (49) hide show
  1. omlish/.manifests.json +2 -2
  2. omlish/__about__.py +2 -2
  3. omlish/collections/cache/descriptor.py +0 -2
  4. omlish/concurrent/executors.py +0 -1
  5. omlish/concurrent/futures.py +0 -1
  6. omlish/dataclasses/impl/descriptors.py +0 -2
  7. omlish/diag/replserver/server.py +0 -1
  8. omlish/dispatch/_dispatch2.py +7 -3
  9. omlish/dispatch/_dispatch3.py +7 -3
  10. omlish/dispatch/dispatch.py +29 -63
  11. omlish/dispatch/functions.py +1 -1
  12. omlish/dispatch/impls.py +69 -0
  13. omlish/dispatch/methods.py +1 -1
  14. omlish/dynamic.py +0 -3
  15. omlish/formats/json/stream/build.py +2 -0
  16. omlish/formats/yaml.py +0 -3
  17. omlish/graphs/dags.py +0 -2
  18. omlish/graphs/domination.py +0 -5
  19. omlish/graphs/dot/items.py +0 -1
  20. omlish/graphs/dot/rendering.py +0 -1
  21. omlish/graphs/trees.py +0 -1
  22. omlish/inject/binder.py +0 -10
  23. omlish/iterators/iterators.py +0 -4
  24. omlish/lang/__init__.py +14 -0
  25. omlish/lang/cached/function.py +83 -41
  26. omlish/lang/collections.py +36 -0
  27. omlish/lang/descriptors.py +0 -1
  28. omlish/lang/params.py +261 -0
  29. omlish/lifecycles/base.py +0 -1
  30. omlish/lifecycles/contextmanagers.py +0 -1
  31. omlish/lifecycles/controller.py +0 -2
  32. omlish/lifecycles/manager.py +0 -1
  33. omlish/marshal/trivial/any.py +0 -1
  34. omlish/math/bits.py +0 -1
  35. omlish/math/stats.py +0 -1
  36. omlish/term/codes.py +0 -1
  37. omlish/testing/pytest/plugins/logging.py +0 -1
  38. omlish/testing/pytest/plugins/pydevd.py +0 -1
  39. omlish/testing/pytest/plugins/repeat.py +0 -1
  40. omlish/testing/pytest/plugins/skips.py +0 -1
  41. omlish/testing/pytest/plugins/spacing.py +0 -1
  42. omlish/testing/pytest/plugins/switches.py +0 -1
  43. omlish/text/delimit.py +0 -1
  44. {omlish-0.0.0.dev247.dist-info → omlish-0.0.0.dev249.dist-info}/METADATA +1 -1
  45. {omlish-0.0.0.dev247.dist-info → omlish-0.0.0.dev249.dist-info}/RECORD +49 -47
  46. {omlish-0.0.0.dev247.dist-info → omlish-0.0.0.dev249.dist-info}/LICENSE +0 -0
  47. {omlish-0.0.0.dev247.dist-info → omlish-0.0.0.dev249.dist-info}/WHEEL +0 -0
  48. {omlish-0.0.0.dev247.dist-info → omlish-0.0.0.dev249.dist-info}/entry_points.txt +0 -0
  49. {omlish-0.0.0.dev247.dist-info → omlish-0.0.0.dev249.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,7 @@
1
1
  """
2
2
  TODO:
3
+ - !! reconcile A().f() with A.f(A())
4
+ - unbound descriptor *should* still hit instance cache
3
5
  - integrate / expose with collections.cache
4
6
  - weakrefs (selectable by arg)
5
7
  - more rigorous descriptor pickling
@@ -18,11 +20,18 @@ from ..contextmanagers import DefaultLockable
18
20
  from ..contextmanagers import default_lock
19
21
  from ..descriptors import unwrap_func
20
22
  from ..descriptors import unwrap_func_with_partials
23
+ from ..params import KwargsParam
24
+ from ..params import Param
25
+ from ..params import ParamSeparator
26
+ from ..params import ParamSpec
27
+ from ..params import ValueParam
28
+ from ..params import param_render
21
29
 
22
30
 
23
31
  P = ta.ParamSpec('P')
24
32
  T = ta.TypeVar('T')
25
33
  CallableT = ta.TypeVar('CallableT', bound=ta.Callable)
34
+ CacheKeyMaker: ta.TypeAlias = ta.Callable[..., tuple]
26
35
 
27
36
 
28
37
  ##
@@ -32,66 +41,100 @@ def _nullary_cache_key_maker():
32
41
  return ()
33
42
 
34
43
 
35
- def _simple_cache_key_maker(*args, **kwargs):
36
- return (args, tuple(sorted(kwargs.items())))
44
+ def _self_cache_key_maker(self):
45
+ return (self,)
37
46
 
38
47
 
39
- def _make_cache_key_maker(fn, *, simple=False, bound=False):
40
- if simple:
41
- return _simple_cache_key_maker
48
+ _PRE_MADE_CACHE_KEY_MAKERS = [
49
+ _nullary_cache_key_maker,
50
+ _self_cache_key_maker,
51
+ ]
52
+
53
+
54
+ _PRE_MADE_CACHE_KEY_MAKERS_BY_PARAM_SPEC: ta.Mapping[ParamSpec, CacheKeyMaker] = {
55
+ ParamSpec.of_signature(inspect.signature(fn)): fn # type: ignore
56
+ for fn in _PRE_MADE_CACHE_KEY_MAKERS
57
+ }
58
+
59
+
60
+ ##
42
61
 
43
- fn, partials = unwrap_func_with_partials(fn)
44
62
 
63
+ def _make_unwrapped_cache_key_maker(
64
+ fn: ta.Callable,
65
+ *,
66
+ bound: bool = False,
67
+ ) -> CacheKeyMaker:
45
68
  if inspect.isgeneratorfunction(fn) or inspect.iscoroutinefunction(fn):
46
69
  raise TypeError(fn)
47
70
 
48
- sig = inspect.signature(fn)
49
- sig_params = list(sig.parameters.values())[1 if bound else 0:]
50
- if not sig_params:
71
+ ps = ParamSpec.of_signature(
72
+ inspect.signature(fn),
73
+ offset=1 if bound else 0,
74
+ strip_annotations=True,
75
+ )
76
+
77
+ if not len(ps):
51
78
  return _nullary_cache_key_maker
52
79
 
53
- ns = {}
80
+ if not ps.has_defaults:
81
+ try:
82
+ return _PRE_MADE_CACHE_KEY_MAKERS_BY_PARAM_SPEC[ps]
83
+ except KeyError:
84
+ pass
85
+
86
+ builtin_pfx = '__cache_key_maker__'
87
+ ns = {
88
+ (builtin_tuple := builtin_pfx + 'tuple'): tuple,
89
+ (builtin_sorted := builtin_pfx + 'sorted'): sorted,
90
+ }
91
+
54
92
  src_params = []
55
93
  src_vals = []
56
94
  kwargs_name = None
57
- render_pos_only_separator = False
58
- render_kw_only_separator = True
59
- for p in sig_params:
60
- formatted = p.name
61
- if p.default is not inspect.Parameter.empty:
95
+ for p in ps:
96
+ if isinstance(p, ParamSeparator):
97
+ src_params.append(p.value)
98
+ continue
99
+
100
+ if not isinstance(p, Param):
101
+ raise TypeError(p)
102
+
103
+ if isinstance(p, ValueParam) and p.default.present:
62
104
  ns[p.name] = p.default
63
- formatted = f'{formatted}={formatted}'
64
- kind = p.kind
65
- if kind == inspect.Parameter.VAR_POSITIONAL:
66
- formatted = '*' + formatted
67
- elif kind == inspect.Parameter.VAR_KEYWORD:
68
- formatted = '**' + formatted
69
- if kind == inspect.Parameter.POSITIONAL_ONLY:
70
- render_pos_only_separator = True
71
- elif render_pos_only_separator:
72
- src_params.append('/')
73
- render_pos_only_separator = False
74
- if kind == inspect.Parameter.VAR_POSITIONAL:
75
- render_kw_only_separator = False
76
- elif kind == inspect.Parameter.KEYWORD_ONLY and render_kw_only_separator:
77
- src_params.append('*')
78
- render_kw_only_separator = False
79
- src_params.append(formatted)
80
- if kind == inspect.Parameter.VAR_KEYWORD:
105
+
106
+ src_params.append(param_render(
107
+ p,
108
+ render_default=lambda _: p.name, # noqa
109
+ ))
110
+
111
+ if isinstance(p, KwargsParam):
81
112
  kwargs_name = p.name
82
113
  else:
83
114
  src_vals.append(p.name)
84
- if render_pos_only_separator:
85
- src_params.append('/')
86
115
 
87
- kwa = f', __builtins__.tuple(__builtins__.sorted({kwargs_name}.items()))' if kwargs_name else ''
116
+ if kwargs_name is not None:
117
+ src_vals.append(f'{builtin_tuple}({builtin_sorted}({kwargs_name}.items()))')
118
+
88
119
  rendered = (
89
120
  f'def __func__({", ".join(src_params)}):\n'
90
- f' return ({", ".join(src_vals)}{kwa})\n'
121
+ f' return ({", ".join(src_vals)}{"," if len(src_vals) == 1 else ""})\n'
91
122
  )
92
123
  exec(rendered, ns)
93
124
 
94
- kfn = ns['__func__']
125
+ kfn: CacheKeyMaker = ns['__func__'] # type: ignore[assignment]
126
+ return kfn
127
+
128
+
129
+ def _make_cache_key_maker(
130
+ fn: ta.Any,
131
+ *,
132
+ bound: bool = False,
133
+ ):
134
+ fn, partials = unwrap_func_with_partials(fn)
135
+
136
+ kfn = _make_unwrapped_cache_key_maker(fn, bound=bound)
137
+
95
138
  for part in partials[::-1]:
96
139
  kfn = functools.partial(kfn, *part.args, **part.keywords)
97
140
 
@@ -105,7 +148,6 @@ class _CachedFunction(ta.Generic[T], Abstract):
105
148
  @dc.dataclass(frozen=True)
106
149
  class Opts:
107
150
  map_maker: ta.Callable[[], ta.MutableMapping] = dict
108
- simple_key: bool = False
109
151
  lock: DefaultLockable = None
110
152
  transient: bool = False
111
153
 
@@ -122,7 +164,7 @@ class _CachedFunction(ta.Generic[T], Abstract):
122
164
 
123
165
  self._fn = (fn,)
124
166
  self._opts = opts
125
- self._key_maker = key_maker if key_maker is not None else _make_cache_key_maker(fn, simple=opts.simple_key)
167
+ self._key_maker = key_maker if key_maker is not None else _make_cache_key_maker(fn)
126
168
 
127
169
  self._lock = default_lock(opts.lock, False)() if opts.lock is not None else None
128
170
  self._values = values if values is not None else opts.map_maker()
@@ -278,7 +320,7 @@ class _DescriptorCachedFunction(_CachedFunction[T]):
278
320
  name = self._name
279
321
  bound_fn = fn.__get__(instance, owner)
280
322
  if self._bound_key_maker is None:
281
- self._bound_key_maker = _make_cache_key_maker(fn, simple=self._opts.simple_key, bound=True)
323
+ self._bound_key_maker = _make_cache_key_maker(fn, bound=True)
282
324
 
283
325
  bound = self.__class__(
284
326
  fn,
@@ -48,3 +48,39 @@ def merge_dicts(
48
48
  out[k] = v
49
49
 
50
50
  return out
51
+
52
+
53
+ ##
54
+
55
+
56
+ class _EmptyMap(ta.Mapping[K, V]):
57
+ def __init_subclass__(cls, **kwargs):
58
+ raise TypeError
59
+
60
+ def __new__(cls, *args, **kwargs):
61
+ if args or kwargs:
62
+ raise TypeError
63
+ return _EMPTY_MAP
64
+
65
+ def __repr__(self) -> str:
66
+ return f'{self.__class__.__name__}()'
67
+
68
+ def __init__(self) -> None:
69
+ super().__init__()
70
+
71
+ def __getitem__(self, k: K) -> V:
72
+ raise KeyError
73
+
74
+ def __len__(self) -> int:
75
+ return 0
76
+
77
+ def __iter__(self) -> ta.Iterator[K]:
78
+ return
79
+ yield # noqa
80
+
81
+
82
+ _EMPTY_MAP = object.__new__(_EmptyMap)
83
+
84
+
85
+ def empty_map() -> ta.Mapping[K, V]:
86
+ return _EMPTY_MAP
@@ -204,7 +204,6 @@ class AccessForbiddenError(Exception):
204
204
 
205
205
 
206
206
  class AccessForbiddenDescriptor:
207
-
208
207
  def __init__(self, name: str | None = None) -> None:
209
208
  super().__init__()
210
209
 
omlish/lang/params.py ADDED
@@ -0,0 +1,261 @@
1
+ """
2
+ TODO:
3
+ - check validity
4
+ """
5
+ import dataclasses as dc
6
+ import enum
7
+ import inspect
8
+ import typing as ta
9
+
10
+ from .classes.abstract import Abstract
11
+ from .classes.restrict import Final
12
+ from .classes.restrict import Sealed
13
+ from .maybes import Maybe
14
+ from .maybes import empty
15
+ from .maybes import just
16
+
17
+
18
+ T = ta.TypeVar('T')
19
+
20
+
21
+ ##
22
+
23
+
24
+ @dc.dataclass(frozen=True)
25
+ class Param(Sealed, Abstract):
26
+ name: str
27
+
28
+ prefix: ta.ClassVar[str] = ''
29
+
30
+ @property
31
+ def name_with_prefix(self) -> str:
32
+ return f'{self.prefix}{self.name}'
33
+
34
+
35
+ #
36
+
37
+
38
+ @dc.dataclass(frozen=True)
39
+ class VariadicParam(Param, Abstract):
40
+ annotation: Maybe = empty()
41
+
42
+
43
+ @dc.dataclass(frozen=True)
44
+ class ArgsParam(VariadicParam, Final):
45
+ prefix: ta.ClassVar[str] = '*'
46
+
47
+
48
+ @dc.dataclass(frozen=True)
49
+ class KwargsParam(VariadicParam, Final):
50
+ prefix: ta.ClassVar[str] = '**'
51
+
52
+
53
+ #
54
+
55
+
56
+ @dc.dataclass(frozen=True, unsafe_hash=True)
57
+ class ValueParam(Param):
58
+ default: Maybe = empty()
59
+ annotation: Maybe = empty()
60
+
61
+
62
+ @dc.dataclass(frozen=True, unsafe_hash=True)
63
+ class PosOnlyParam(ValueParam, Final):
64
+ pass
65
+
66
+
67
+ @dc.dataclass(frozen=True, unsafe_hash=True)
68
+ class KwOnlyParam(ValueParam, Final):
69
+ pass
70
+
71
+
72
+ #
73
+
74
+
75
+ class ParamSeparator(enum.Enum):
76
+ POS_ONLY = '/'
77
+ KW_ONLY = '*'
78
+
79
+
80
+ ##
81
+
82
+
83
+ def _inspect_empty_to_maybe(o: T) -> Maybe[T]:
84
+ if o is inspect.Parameter.empty:
85
+ return empty()
86
+ else:
87
+ return just(o)
88
+
89
+
90
+ class ParamSpec(ta.Sequence[Param], Final):
91
+ def __init__(self, *ps: Param) -> None:
92
+ super().__init__()
93
+
94
+ self._ps = ps
95
+
96
+ self._hash: int | None = None
97
+
98
+ self._has_defaults: bool | None = None
99
+ self._has_annotations: bool | None = None
100
+
101
+ self._with_seps: tuple[Param | ParamSeparator, ...] | None = None
102
+
103
+ #
104
+
105
+ @classmethod
106
+ def of_signature(
107
+ cls,
108
+ sig: inspect.Signature,
109
+ *,
110
+ offset: int = 0,
111
+ strip_defaults: bool = False,
112
+ strip_annotations: bool = False,
113
+ ) -> 'ParamSpec':
114
+ ps: list[Param] = []
115
+
116
+ ip: inspect.Parameter
117
+ for i, ip in enumerate(sig.parameters.values()):
118
+ if i < offset:
119
+ continue
120
+
121
+ dfl = _inspect_empty_to_maybe(ip.default) if not strip_defaults else empty()
122
+ ann = _inspect_empty_to_maybe(ip.annotation) if not strip_annotations else empty()
123
+
124
+ if ip.kind == inspect.Parameter.POSITIONAL_ONLY:
125
+ ps.append(PosOnlyParam(ip.name, dfl, ann))
126
+
127
+ elif ip.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:
128
+ ps.append(ValueParam(ip.name, dfl, ann))
129
+
130
+ elif ip.kind == inspect.Parameter.VAR_POSITIONAL:
131
+ ps.append(ArgsParam(ip.name))
132
+
133
+ elif ip.kind == inspect.Parameter.VAR_KEYWORD:
134
+ ps.append(KwargsParam(ip.name))
135
+
136
+ elif ip.kind == inspect.Parameter.KEYWORD_ONLY:
137
+ ps.append(KwOnlyParam(ip.name, dfl, ann))
138
+
139
+ else:
140
+ raise ValueError(ip.kind)
141
+
142
+ return cls(*ps)
143
+
144
+ #
145
+
146
+ def __repr__(self) -> str:
147
+ return f'{self.__class__.__name__}({", ".join(map(repr, self._ps))})'
148
+
149
+ def __hash__(self) -> int:
150
+ if (h := self._hash) is not None:
151
+ return h
152
+ self._hash = h = hash(self._ps)
153
+ return h
154
+
155
+ def __eq__(self, other: object) -> bool:
156
+ if type(other) is not ParamSpec:
157
+ raise TypeError(other)
158
+ return self._ps == other._ps # noqa
159
+
160
+ #
161
+
162
+ @property
163
+ def has_defaults(self) -> bool:
164
+ if (hd := self._has_defaults) is not None:
165
+ return hd
166
+ self._has_defaults = hd = any(
167
+ isinstance(p, ValueParam) and p.default.present
168
+ for p in self._ps
169
+ )
170
+ return hd
171
+
172
+ @property
173
+ def has_annotations(self) -> bool:
174
+ if (ha := self._has_defaults) is not None:
175
+ return ha
176
+ self._has_annotations = ha = any(
177
+ isinstance(p, (VariadicParam, ValueParam)) and p.annotation.present
178
+ for p in self._ps
179
+ )
180
+ return ha
181
+
182
+ #
183
+
184
+ @property
185
+ def with_seps(self) -> ta.Sequence[Param | ParamSeparator]:
186
+ if (ws := self._with_seps) is not None:
187
+ return ws
188
+
189
+ l: list[Param | ParamSeparator] = []
190
+ needs_pos_only = False
191
+ seen_kw_only = False
192
+ for p in self._ps:
193
+ if isinstance(p, PosOnlyParam):
194
+ needs_pos_only = True
195
+ elif needs_pos_only:
196
+ l.append(ParamSeparator.POS_ONLY)
197
+ needs_pos_only = False
198
+
199
+ if isinstance(p, KwOnlyParam) and not seen_kw_only:
200
+ l.append(ParamSeparator.KW_ONLY)
201
+ seen_kw_only = True
202
+ elif isinstance(p, KwargsParam):
203
+ seen_kw_only = True
204
+
205
+ l.append(p)
206
+
207
+ self._with_seps = ws = tuple(l)
208
+ return ws
209
+
210
+ #
211
+
212
+ @ta.overload
213
+ def __getitem__(self, index: int) -> Param: ...
214
+
215
+ @ta.overload
216
+ def __getitem__(self, index: slice) -> ta.Sequence[Param]: ...
217
+
218
+ def __getitem__(self, index):
219
+ return self._ps[index]
220
+
221
+ def __len__(self) -> int:
222
+ return len(self._ps)
223
+
224
+
225
+ ##
226
+
227
+
228
+ def param_render(
229
+ p: Param | ParamSeparator,
230
+ *,
231
+ render_default: ta.Callable[[ta.Any], str] | None = None,
232
+ render_annotation: ta.Callable[[ta.Any], str] | None = None,
233
+ ) -> str:
234
+ if isinstance(p, Param):
235
+ dfl_s: str | None = None
236
+ if isinstance(p, ValueParam) and p.default.present:
237
+ if render_default is None:
238
+ raise ValueError(f'Param {p.name} has a default but no default renderer provided')
239
+ dfl_s = render_default(p.default.must())
240
+
241
+ ann_s: str | None = None
242
+ if isinstance(p, (VariadicParam, ValueParam)) and p.annotation.present:
243
+ if render_annotation is None:
244
+ raise ValueError(f'Param {p.name} has an annotation but no annotation renderer provided')
245
+ ann_s = render_annotation(p.annotation.must())
246
+
247
+ if ann_s is not None:
248
+ if dfl_s is not None:
249
+ return f'{p.name_with_prefix}: {ann_s} = {dfl_s}'
250
+ else:
251
+ return f'{p.name_with_prefix}: {ann_s}'
252
+ elif dfl_s is not None:
253
+ return f'{p.name_with_prefix}={dfl_s}'
254
+ else:
255
+ return p.name_with_prefix
256
+
257
+ elif isinstance(p, ParamSeparator):
258
+ return p.value
259
+
260
+ else:
261
+ raise TypeError(p)
omlish/lifecycles/base.py CHANGED
@@ -9,7 +9,6 @@ LifecycleCallback: ta.TypeAlias = ta.Callable[[LifecycleT], None]
9
9
 
10
10
 
11
11
  class Lifecycle:
12
-
13
12
  def lifecycle_construct(self) -> None:
14
13
  pass
15
14
 
@@ -28,7 +28,6 @@ class ContextManagerLifecycle(Lifecycle, lang.Final, ta.Generic[ContextManagerT]
28
28
 
29
29
 
30
30
  class LifecycleContextManager(ta.Generic[LifecycleT]):
31
-
32
31
  def __init__(self, lifecycle: LifecycleT) -> None:
33
32
  super().__init__()
34
33
  self._lifecycle = lifecycle
@@ -14,7 +14,6 @@ LifecycleT = ta.TypeVar('LifecycleT', bound='Lifecycle')
14
14
 
15
15
 
16
16
  class LifecycleListener(ta.Generic[LifecycleT]):
17
-
18
17
  def on_starting(self, obj: LifecycleT) -> None:
19
18
  pass
20
19
 
@@ -29,7 +28,6 @@ class LifecycleListener(ta.Generic[LifecycleT]):
29
28
 
30
29
 
31
30
  class LifecycleController(Lifecycle, ta.Generic[LifecycleT]):
32
-
33
31
  def __init__(
34
32
  self,
35
33
  lifecycle: LifecycleT,
@@ -13,7 +13,6 @@ from .states import LifecycleStates
13
13
 
14
14
 
15
15
  class LifecycleManager(AbstractLifecycle):
16
-
17
16
  @dc.dataclass(frozen=True)
18
17
  class Entry(lang.Final):
19
18
  controller: LifecycleController
@@ -11,7 +11,6 @@ from ..values import Value
11
11
 
12
12
 
13
13
  class AnyMarshalerUnmarshaler(Marshaler, Unmarshaler):
14
-
15
14
  def marshal(self, ctx: MarshalContext, o: ta.Any) -> Value:
16
15
  return ctx.make(type(o)).marshal(ctx, o)
17
16
 
omlish/math/bits.py CHANGED
@@ -44,7 +44,6 @@ def _gen_tuple_proxy_method(name):
44
44
 
45
45
 
46
46
  class FixedWidthInt(int):
47
-
48
47
  BITS: ta.ClassVar[int]
49
48
  SIGNED: ta.ClassVar[bool]
50
49
 
omlish/math/stats.py CHANGED
@@ -234,7 +234,6 @@ class Stats(ta.Sequence[float]):
234
234
 
235
235
 
236
236
  class SamplingHistogram:
237
-
238
237
  @dc.dataclass(frozen=True)
239
238
  class Entry:
240
239
  value: float
omlish/term/codes.py CHANGED
@@ -27,7 +27,6 @@ def strip_ansi_codes(s: str) -> str:
27
27
 
28
28
 
29
29
  class ControlSequence:
30
-
31
30
  def __init__(self, fn: ta.Callable[..., str], desc: str) -> None:
32
31
  super().__init__()
33
32
  self._fn = fn
@@ -4,7 +4,6 @@ from ._registry import register
4
4
 
5
5
  @register
6
6
  class LoggingPlugin:
7
-
8
7
  def pytest_addoption(self, parser):
9
8
  parser.addoption('--log', action='store', help='Configures logging with given log level')
10
9
 
@@ -4,7 +4,6 @@ from ._registry import register
4
4
 
5
5
  @register
6
6
  class PydevdPlugin:
7
-
8
7
  def pytest_addoption(self, parser):
9
8
  parser.addoption('--no-pydevd', action='store_true', default=False, help='Disables pydevd debugging')
10
9
 
@@ -6,7 +6,6 @@ PARAM_NAME = '__repeat'
6
6
 
7
7
  @register
8
8
  class RepeatPlugin:
9
-
10
9
  def pytest_addoption(self, parser):
11
10
  parser.addoption('--repeat', action='store', type=int, help='Number of times to repeat each test')
12
11
 
@@ -10,7 +10,6 @@ from ._registry import register
10
10
 
11
11
  @register
12
12
  class SkipsPlugin:
13
-
14
13
  def pytest_collection_modifyitems(self, session, items):
15
14
  dct: dict[str, set[str]] = {}
16
15
  for arg in session.config.args:
@@ -3,7 +3,6 @@ from ._registry import register
3
3
 
4
4
  @register
5
5
  class SpacingPlugin:
6
-
7
6
  def pytest_addoption(self, parser):
8
7
  parser.addoption('--newlines-before', action='store', type=int, help='Adds newlines before tests')
9
8
  parser.addoption('--newlines-after', action='store', type=int, help='Adds newlines after tests')
@@ -75,7 +75,6 @@ def get_specified_switches(obj: Configable) -> ta.Mapping[str, SwitchState]:
75
75
 
76
76
  @register
77
77
  class SwitchesPlugin:
78
-
79
78
  def pytest_configure(self, config):
80
79
  for sw in SWITCH_ATTRS:
81
80
  config.addinivalue_line('markers', f'{sw}: mark test as {sw}')
omlish/text/delimit.py CHANGED
@@ -7,7 +7,6 @@ import typing as ta
7
7
 
8
8
 
9
9
  class DelimitedEscaping:
10
-
11
10
  def __init__(
12
11
  self,
13
12
  delimit_char: str,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: omlish
3
- Version: 0.0.0.dev247
3
+ Version: 0.0.0.dev249
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause