ovld 0.5.0__tar.gz → 0.5.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 (68) hide show
  1. {ovld-0.5.0 → ovld-0.5.2}/PKG-INFO +1 -1
  2. {ovld-0.5.0 → ovld-0.5.2}/pyproject.toml +2 -2
  3. {ovld-0.5.0 → ovld-0.5.2}/src/ovld/codegen.py +5 -2
  4. {ovld-0.5.0 → ovld-0.5.2}/src/ovld/core.py +16 -14
  5. {ovld-0.5.0 → ovld-0.5.2}/src/ovld/medley.py +46 -36
  6. {ovld-0.5.0 → ovld-0.5.2}/src/ovld/mro.py +1 -1
  7. {ovld-0.5.0 → ovld-0.5.2}/src/ovld/recode.py +14 -6
  8. {ovld-0.5.0 → ovld-0.5.2}/src/ovld/utils.py +4 -0
  9. ovld-0.5.2/src/ovld/version.py +1 -0
  10. {ovld-0.5.0 → ovld-0.5.2}/tests/test_codegen.py +14 -0
  11. {ovld-0.5.0 → ovld-0.5.2}/uv.lock +12 -13
  12. ovld-0.5.0/src/ovld/version.py +0 -1
  13. {ovld-0.5.0 → ovld-0.5.2}/.github/workflows/publish.yml +0 -0
  14. {ovld-0.5.0 → ovld-0.5.2}/.github/workflows/python-package.yml +0 -0
  15. {ovld-0.5.0 → ovld-0.5.2}/.gitignore +0 -0
  16. {ovld-0.5.0 → ovld-0.5.2}/.python-version +0 -0
  17. {ovld-0.5.0 → ovld-0.5.2}/.readthedocs.yaml +0 -0
  18. {ovld-0.5.0 → ovld-0.5.2}/LICENSE +0 -0
  19. {ovld-0.5.0 → ovld-0.5.2}/README.md +0 -0
  20. {ovld-0.5.0 → ovld-0.5.2}/benchmarks/__init__.py +0 -0
  21. {ovld-0.5.0 → ovld-0.5.2}/benchmarks/common.py +0 -0
  22. {ovld-0.5.0 → ovld-0.5.2}/benchmarks/test_add.py +0 -0
  23. {ovld-0.5.0 → ovld-0.5.2}/benchmarks/test_ast.py +0 -0
  24. {ovld-0.5.0 → ovld-0.5.2}/benchmarks/test_calc.py +0 -0
  25. {ovld-0.5.0 → ovld-0.5.2}/benchmarks/test_fib.py +0 -0
  26. {ovld-0.5.0 → ovld-0.5.2}/benchmarks/test_multer.py +0 -0
  27. {ovld-0.5.0 → ovld-0.5.2}/benchmarks/test_regexp.py +0 -0
  28. {ovld-0.5.0 → ovld-0.5.2}/benchmarks/test_trivial.py +0 -0
  29. {ovld-0.5.0 → ovld-0.5.2}/benchmarks/test_tweaknum.py +0 -0
  30. {ovld-0.5.0 → ovld-0.5.2}/docs/codegen.md +0 -0
  31. {ovld-0.5.0 → ovld-0.5.2}/docs/compare.md +0 -0
  32. {ovld-0.5.0 → ovld-0.5.2}/docs/dependent.md +0 -0
  33. {ovld-0.5.0 → ovld-0.5.2}/docs/features.md +0 -0
  34. {ovld-0.5.0 → ovld-0.5.2}/docs/index.md +0 -0
  35. {ovld-0.5.0 → ovld-0.5.2}/docs/medley.md +0 -0
  36. {ovld-0.5.0 → ovld-0.5.2}/docs/types.md +0 -0
  37. {ovld-0.5.0 → ovld-0.5.2}/docs/usage.md +0 -0
  38. {ovld-0.5.0 → ovld-0.5.2}/mkdocs.yml +0 -0
  39. {ovld-0.5.0 → ovld-0.5.2}/src/ovld/__init__.py +0 -0
  40. {ovld-0.5.0 → ovld-0.5.2}/src/ovld/abc.py +0 -0
  41. {ovld-0.5.0 → ovld-0.5.2}/src/ovld/dependent.py +0 -0
  42. {ovld-0.5.0 → ovld-0.5.2}/src/ovld/py.typed +0 -0
  43. {ovld-0.5.0 → ovld-0.5.2}/src/ovld/signatures.py +0 -0
  44. {ovld-0.5.0 → ovld-0.5.2}/src/ovld/typemap.py +0 -0
  45. {ovld-0.5.0 → ovld-0.5.2}/src/ovld/types.py +0 -0
  46. {ovld-0.5.0 → ovld-0.5.2}/tests/__init__.py +0 -0
  47. {ovld-0.5.0 → ovld-0.5.2}/tests/modules/gingerbread.py +0 -0
  48. {ovld-0.5.0 → ovld-0.5.2}/tests/test_abc.py +0 -0
  49. {ovld-0.5.0 → ovld-0.5.2}/tests/test_codegen/test_dataclass_gen.txt +0 -0
  50. {ovld-0.5.0 → ovld-0.5.2}/tests/test_codegen/test_method.txt +0 -0
  51. {ovld-0.5.0 → ovld-0.5.2}/tests/test_codegen/test_method_metaclass.txt +0 -0
  52. {ovld-0.5.0 → ovld-0.5.2}/tests/test_codegen/test_method_per_instance.txt +0 -0
  53. {ovld-0.5.0 → ovld-0.5.2}/tests/test_codegen/test_simple.txt +0 -0
  54. {ovld-0.5.0 → ovld-0.5.2}/tests/test_codegen/test_variant_generation.txt +0 -0
  55. {ovld-0.5.0 → ovld-0.5.2}/tests/test_dependent.py +0 -0
  56. {ovld-0.5.0 → ovld-0.5.2}/tests/test_examples.py +0 -0
  57. {ovld-0.5.0 → ovld-0.5.2}/tests/test_global.py +0 -0
  58. {ovld-0.5.0 → ovld-0.5.2}/tests/test_medley.py +0 -0
  59. {ovld-0.5.0 → ovld-0.5.2}/tests/test_mro.py +0 -0
  60. {ovld-0.5.0 → ovld-0.5.2}/tests/test_ovld/test_display.txt +0 -0
  61. {ovld-0.5.0 → ovld-0.5.2}/tests/test_ovld/test_display_more.txt +0 -0
  62. {ovld-0.5.0 → ovld-0.5.2}/tests/test_ovld/test_doc.txt +0 -0
  63. {ovld-0.5.0 → ovld-0.5.2}/tests/test_ovld/test_doc2.txt +0 -0
  64. {ovld-0.5.0 → ovld-0.5.2}/tests/test_ovld/test_method_doc.txt +0 -0
  65. {ovld-0.5.0 → ovld-0.5.2}/tests/test_ovld.py +0 -0
  66. {ovld-0.5.0 → ovld-0.5.2}/tests/test_typemap.py +0 -0
  67. {ovld-0.5.0 → ovld-0.5.2}/tests/test_types.py +0 -0
  68. {ovld-0.5.0 → ovld-0.5.2}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ovld
3
- Version: 0.5.0
3
+ Version: 0.5.2
4
4
  Summary: Overloading Python functions
5
5
  Project-URL: Homepage, https://ovld.readthedocs.io/en/latest/
6
6
  Project-URL: Documentation, https://ovld.readthedocs.io/en/latest/
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ovld"
3
- version = "0.5.0"
3
+ version = "0.5.2"
4
4
  description = "Overloading Python functions"
5
5
  authors = [
6
6
  { name = "Olivier Breuleux", email = "breuleux@gmail.com" }
@@ -32,7 +32,7 @@ benchmark = [
32
32
  "multimethod>=1.12",
33
33
  "multipledispatch>=1.0.0",
34
34
  "plum-dispatch>=2.5.2",
35
- "runtype>=0.5.0",
35
+ "runtype>=0.5.2",
36
36
  ]
37
37
 
38
38
  [tool.hatch.build.targets.wheel]
@@ -6,7 +6,7 @@ from ast import _splitlines_no_ff as splitlines
6
6
  from itertools import count
7
7
  from types import FunctionType
8
8
 
9
- from .utils import MISSING, NameDatabase, sigstring
9
+ from .utils import MISSING, NameDatabase, keyword_decorator, sigstring
10
10
 
11
11
  _current = count()
12
12
 
@@ -298,6 +298,9 @@ def codegen_specializer(typemap, fn, tup):
298
298
  return func
299
299
 
300
300
 
301
- def code_generator(fn):
301
+ @keyword_decorator
302
+ def code_generator(fn, priority=0):
302
303
  fn.specializer = codegen_specializer
304
+ if priority:
305
+ fn.priority = priority
303
306
  return fn
@@ -18,6 +18,7 @@ from .signatures import ArgumentAnalyzer, LazySignature, Signature
18
18
  from .typemap import MultiTypeMap
19
19
  from .utils import (
20
20
  MISSING,
21
+ ResolutionError,
21
22
  UsageError,
22
23
  keyword_decorator,
23
24
  sigstring,
@@ -51,6 +52,7 @@ def bootstrap_dispatch(ov, name):
51
52
  dispatch.add_mixins = ov.add_mixins
52
53
  dispatch.unregister = ov.unregister
53
54
  dispatch.next = ov.next
55
+ dispatch.first_entry = first_entry
54
56
  return dispatch
55
57
 
56
58
 
@@ -95,6 +97,7 @@ class Ovld:
95
97
  self._locked = False
96
98
  self.mixins = []
97
99
  self.argument_analysis = ArgumentAnalyzer()
100
+ self.dispatch = bootstrap_dispatch(self, name=self.shortname)
98
101
  self.add_mixins(*mixins)
99
102
 
100
103
  @property
@@ -162,12 +165,12 @@ class Ovld:
162
165
  def _key_error(self, key, possibilities=None):
163
166
  typenames = sigstring(key)
164
167
  if not possibilities:
165
- return TypeError(f"No method in {self} for argument types [{typenames}]")
168
+ return ResolutionError(f"No method in {self} for argument types [{typenames}]")
166
169
  else:
167
170
  hlp = ""
168
171
  for c in possibilities:
169
172
  hlp += f"* {c.handler.__name__} (priority: {c.priority}, specificity: {list(c.specificity)})\n"
170
- return TypeError(
173
+ return ResolutionError(
171
174
  f"Ambiguous resolution in {self} for"
172
175
  f" argument types [{typenames}]\n"
173
176
  f"Candidates are:\n{hlp}"
@@ -176,10 +179,11 @@ class Ovld:
176
179
 
177
180
  def rename(self, name, shortname=None):
178
181
  """Rename this Ovld."""
179
- self.name = name
180
- self.shortname = shortname or name
181
- self.__name__ = self.shortname
182
- self.dispatch = bootstrap_dispatch(self, name=self.shortname)
182
+ if name != self.name:
183
+ self.name = name
184
+ self.shortname = shortname or name
185
+ self.__name__ = self.shortname
186
+ self.dispatch = bootstrap_dispatch(self, name=self.shortname)
183
187
 
184
188
  def __set_name__(self, inst, name):
185
189
  self.rename(name)
@@ -217,8 +221,6 @@ class Ovld:
217
221
 
218
222
  self.analyze_arguments()
219
223
  dispatch = generate_dispatch(self, self.argument_analysis)
220
- if not hasattr(self, "dispatch"):
221
- self.dispatch = bootstrap_dispatch(self, name=self.shortname)
222
224
  self.dispatch.__code__ = rename_code(dispatch.__code__, self.shortname)
223
225
  self.dispatch.__kwdefaults__ = dispatch.__kwdefaults__
224
226
  self.dispatch.__annotations__ = dispatch.__annotations__
@@ -258,6 +260,7 @@ class Ovld:
258
260
  """Register a function."""
259
261
  if fn is None:
260
262
  return partial(self._register, priority=priority)
263
+ priority = getattr(fn, "priority", priority)
261
264
  return self._register(fn, priority)
262
265
 
263
266
  def _register(self, fn, priority):
@@ -290,13 +293,16 @@ class Ovld:
290
293
  self._update()
291
294
 
292
295
  def _update(self):
293
- if self._compiled:
294
- self.compile()
296
+ self.reset()
295
297
  for child in self.children:
296
298
  child._update()
297
299
  if hasattr(self, "dispatch"):
298
300
  self.dispatch.__doc__ = self.mkdoc()
299
301
 
302
+ def reset(self):
303
+ self._compiled = False
304
+ self.dispatch.__code__ = self.dispatch.first_entry.__code__
305
+
300
306
  def copy(self, mixins=[], linkback=False):
301
307
  """Create a copy of this Ovld.
302
308
 
@@ -319,8 +325,6 @@ class Ovld:
319
325
  return ov
320
326
 
321
327
  def __get__(self, obj, cls):
322
- if not self._compiled:
323
- self.compile()
324
328
  return self.dispatch.__get__(obj, cls)
325
329
 
326
330
  def __call__(self, *args, **kwargs): # pragma: no cover
@@ -328,8 +332,6 @@ class Ovld:
328
332
 
329
333
  This should be replaced by an auto-generated function.
330
334
  """
331
- if not self._compiled:
332
- self.compile()
333
335
  return self.dispatch(*args, **kwargs)
334
336
 
335
337
  def next(self, *args):
@@ -110,7 +110,7 @@ class ChainAll(ImplList):
110
110
  class BuildOvld(Combiner):
111
111
  def __init__(self, field=None, ovld=None):
112
112
  super().__init__(field)
113
- self.ovld = ovld or Ovld(linkback=True)
113
+ self.ovld = ovld or Ovld(name=field, linkback=True)
114
114
  self.pending = []
115
115
  if field is not None:
116
116
  self.__set_name__(None, field)
@@ -125,7 +125,7 @@ class BuildOvld(Combiner):
125
125
  self.pending.clear()
126
126
  if not self.ovld.defns:
127
127
  return ABSENT
128
- self.ovld.compile()
128
+ self.ovld.reset()
129
129
  return self.ovld.dispatch
130
130
 
131
131
  def copy(self):
@@ -144,10 +144,14 @@ class BuildOvld(Combiner):
144
144
 
145
145
 
146
146
  class medley_cls_dict(dict):
147
- def __init__(self, bases):
147
+ def __init__(self, bases, default_combiner=None):
148
+ if default_combiner is None:
149
+ (default_combiner,) = {b._ovld_default_combiner for b in bases}
148
150
  super().__init__()
149
151
  self._combiners = {}
152
+ self._default_combiner = default_combiner
150
153
  self.set_direct("_ovld_combiners", self._combiners)
154
+ self.set_direct("_ovld_default_combiner", default_combiner)
151
155
  self._basic = set()
152
156
  for base in bases:
153
157
  for attr, combiner in getattr(base, "_ovld_combiners", {}).items():
@@ -174,8 +178,10 @@ class medley_cls_dict(dict):
174
178
 
175
179
  combiner = self._combiners.get(attr, None)
176
180
  if combiner is None:
177
- if inspect.isfunction(value) or isinstance(value, Ovld):
181
+ if to_ovld(value, force=False):
178
182
  combiner = BuildOvld(attr)
183
+ elif inspect.isfunction(value):
184
+ combiner = self._default_combiner(attr)
179
185
  else:
180
186
  combiner = KeepLast(attr)
181
187
  self._combiners[attr] = combiner
@@ -207,6 +213,19 @@ def specialize(cls, key):
207
213
  return new_t
208
214
 
209
215
 
216
+ def remap_field(dc_field, require_default=False):
217
+ if require_default:
218
+ if dc_field.default is MISSING:
219
+ # NOTE: we do not accept default_factory, because we need the default value to be set
220
+ # in the class so that existing instances of classes[0] can see it.
221
+ raise TypeError(
222
+ f"Dataclass field '{dc_field.name}' must have a default value (not a default_factory) in order to be melded in."
223
+ )
224
+ dc_field = copy(dc_field)
225
+ dc_field.kw_only = True
226
+ return dc_field
227
+
228
+
210
229
  class MedleyMC(type):
211
230
  def __subclasscheck__(cls, subclass):
212
231
  if getattr(cls, "_ovld_medleys", None):
@@ -214,10 +233,10 @@ class MedleyMC(type):
214
233
  return super().__subclasscheck__(subclass)
215
234
 
216
235
  @classmethod
217
- def __prepare__(mcls, name, bases):
218
- return medley_cls_dict(bases)
236
+ def __prepare__(mcls, name, bases, default_combiner=None):
237
+ return medley_cls_dict(bases, default_combiner=default_combiner)
219
238
 
220
- def __new__(mcls, name, bases, namespace):
239
+ def __new__(mcls, name, bases, namespace, default_combiner=None):
221
240
  result = super().__new__(mcls, name, bases, namespace)
222
241
  for attr, combiner in result._ovld_combiners.items():
223
242
  if (value := combiner.get(result)) is not ABSENT:
@@ -236,13 +255,16 @@ class MedleyMC(type):
236
255
  ]
237
256
  return dc
238
257
 
239
- def extend(cls, *others):
240
- if not others:
258
+ def extend(cls, *others, extend_subclasses=True):
259
+ if not others: # pragma: no cover
241
260
  return cls
242
- melded = meld_classes((cls, *others), require_defaults=True)
261
+ all_fields = [(f.name, f.type, f) for f in fields(cls)]
262
+ for other in others:
263
+ all_fields += [(f.name, f.type, remap_field(f, True)) for f in fields(other)]
264
+ melded = make_dataclass("_", fields=all_fields)
243
265
  for other in others:
244
266
  for k, v in vars(other).items():
245
- if k in ["__module__", "__firstlineno__"]:
267
+ if k in ["__module__", "__firstlineno__", "__static_attributes__"]:
246
268
  continue
247
269
  elif comb := cls._ovld_combiners.get(k):
248
270
  comb.juxtapose(v)
@@ -250,9 +272,10 @@ class MedleyMC(type):
250
272
  elif not k.startswith("_ovld_") and not k.startswith("__"):
251
273
  setattr(cls, k, v)
252
274
  cls.__init__ = melded.__init__
253
- for subcls in cls.__subclasses__():
254
- subothers = [o for o in others if not issubclass(subcls, o)]
255
- subcls.extend(*subothers)
275
+ if extend_subclasses:
276
+ for subcls in cls.__subclasses__():
277
+ subothers = [o for o in others if not issubclass(subcls, o)]
278
+ subcls.extend(*subothers, extend_subclasses=False)
256
279
  return cls
257
280
 
258
281
  def __add__(cls, other):
@@ -290,7 +313,7 @@ def use_combiner(combiner):
290
313
  return deco
291
314
 
292
315
 
293
- class Medley(metaclass=MedleyMC):
316
+ class Medley(metaclass=MedleyMC, default_combiner=BuildOvld):
294
317
  __post_init__ = RunAll()
295
318
  __add__ = KeepLast()
296
319
  __sub__ = KeepLast()
@@ -313,13 +336,10 @@ def unmeld_classes(main: type, exclude: type):
313
336
  _meld_classes_cache = {}
314
337
 
315
338
 
316
- def meld_classes(classes, require_defaults=False):
339
+ def meld_classes(classes):
317
340
  medleys = {}
318
- for i, cls in enumerate(classes):
319
- if require_defaults and i == 0:
320
- medleys[cls] = True
321
- else:
322
- medleys.update({x: True for x in getattr(cls, "_ovld_medleys", [cls])})
341
+ for cls in classes:
342
+ medleys.update({x: True for x in getattr(cls, "_ovld_medleys", [cls])})
323
343
  for cls in classes:
324
344
  if not hasattr(cls, "_ovld_medleys"):
325
345
  for base in cls.mro():
@@ -329,36 +349,26 @@ def meld_classes(classes, require_defaults=False):
329
349
  if len(medleys) == 1:
330
350
  return medleys[0]
331
351
 
332
- cache_key = (medleys, require_defaults)
352
+ cache_key = medleys
333
353
  if cache_key in _meld_classes_cache:
334
354
  return _meld_classes_cache[cache_key]
335
355
 
336
- def remap_field(dc_field, require_default):
337
- if require_default:
338
- if dc_field.default is MISSING:
339
- # NOTE: we do not accept default_factory, because we need the default value to be set
340
- # in the class so that existing instances of classes[0] can see it.
341
- raise TypeError(
342
- f"Dataclass field '{dc_field.name}' must have a default value (not a default_factory) in order to be melded in."
343
- )
344
- dc_field = copy(dc_field)
345
- dc_field.kw_only = True
346
- return dc_field
347
-
348
356
  cg_fields = set()
349
357
  dc_fields = []
350
358
 
351
359
  for base in medleys:
352
- rqdef = require_defaults and base is not medleys[0]
353
360
  cg_fields.update(base._ovld_codegen_fields)
354
361
  dc_fields.extend(
355
- (f.name, f.type, remap_field(f, rqdef)) for f in base.__dataclass_fields__.values()
362
+ (f.name, f.type, remap_field(f)) for f in base.__dataclass_fields__.values()
356
363
  )
357
364
 
358
365
  merged = medley_cls_dict(medleys)
359
366
  merged.set_direct("_ovld_codegen_fields", tuple(cg_fields))
360
367
  merged.set_direct("_ovld_medleys", tuple(medleys))
361
368
 
369
+ if "__qualname__" in merged._combiners:
370
+ del merged._combiners["__qualname__"]
371
+
362
372
  result = make_dataclass(
363
373
  cls_name="+".join(sorted(c.__name__ for c in medleys)),
364
374
  bases=tuple(medleys),
@@ -137,7 +137,7 @@ def subclasscheck(t1, t2):
137
137
  if o1 or o2:
138
138
  o1 = o1 or t1
139
139
  o2 = o2 or t2
140
- if issubclass(o1, o2):
140
+ if isinstance(o1, type) and isinstance(o2, type) and issubclass(o1, o2):
141
141
  if o2 is t2: # pragma: no cover
142
142
  return True
143
143
  else:
@@ -405,11 +405,8 @@ def _search_names(co, specials, glb, closure=None):
405
405
  if isinstance(co, CodeType):
406
406
  if closure is not None:
407
407
  for varname, cell in zip(co.co_freevars, closure):
408
- try:
409
- if any(cell.cell_contents is v for v in values):
410
- yield varname
411
- except ValueError: # cell is empty
412
- pass
408
+ if any(cell.cell_contents is v for v in values):
409
+ yield varname
413
410
  for name in co.co_names:
414
411
  if any(glb.get(name, None) is v for v in values):
415
412
  yield name
@@ -499,7 +496,18 @@ def recode(fn, ovld, syms, newname):
499
496
  if fn.__closure__:
500
497
  new = closure_wrap(new.body[0], "irrelevant", fn.__code__.co_freevars)
501
498
  ast.fix_missing_locations(new)
502
- ast.increment_lineno(new, fn.__code__.co_firstlineno - 1)
499
+ line_delta = fn.__code__.co_firstlineno - 1
500
+ col_delta = len(firstline := src.split("\n", 1)[0]) - len(firstline.lstrip())
501
+ for node in ast.walk(new):
502
+ if hasattr(node, "lineno"):
503
+ node.lineno += line_delta
504
+ if hasattr(node, "end_lineno"):
505
+ node.end_lineno += line_delta
506
+ if hasattr(node, "col_offset"):
507
+ node.col_offset += col_delta
508
+ if hasattr(node, "end_col_offset"):
509
+ node.end_col_offset += col_delta
510
+
503
511
  res = compile(new, mode="exec", filename=fn.__code__.co_filename)
504
512
  if fn.__closure__:
505
513
  res = [x for x in res.co_consts if isinstance(x, CodeType)][0]
@@ -65,6 +65,10 @@ class UsageError(Exception):
65
65
  pass
66
66
 
67
67
 
68
+ class ResolutionError(TypeError):
69
+ pass
70
+
71
+
68
72
  class SpecialForm:
69
73
  def __init__(self, name, message=None):
70
74
  self.__name = name
@@ -0,0 +1 @@
1
+ version = "0.5.2"
@@ -307,3 +307,17 @@ def test_def():
307
307
  assert df.create_body(["x", "y"]).fill() == "return x + 1234"
308
308
  with pytest.raises(ValueError):
309
309
  df.create_expression(["x", "y"])
310
+
311
+
312
+ def test_codegen_priority():
313
+ @ovld
314
+ @code_generator
315
+ def f(x: int):
316
+ return Lambda(..., "'A'")
317
+
318
+ @ovld
319
+ @code_generator(priority=1)
320
+ def f(x: object):
321
+ return Lambda(..., "'B'")
322
+
323
+ assert f(2) == "B"
@@ -1,5 +1,4 @@
1
1
  version = 1
2
- revision = 1
3
2
  requires-python = ">=3.9"
4
3
 
5
4
  [[package]]
@@ -16,7 +15,7 @@ name = "click"
16
15
  version = "8.1.8"
17
16
  source = { registry = "https://pypi.org/simple" }
18
17
  dependencies = [
19
- { name = "colorama", marker = "sys_platform == 'win32'" },
18
+ { name = "colorama", marker = "platform_system == 'Windows'" },
20
19
  ]
21
20
  sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
22
21
  wheels = [
@@ -172,14 +171,14 @@ wheels = [
172
171
 
173
172
  [[package]]
174
173
  name = "markdown"
175
- version = "3.7"
174
+ version = "3.8"
176
175
  source = { registry = "https://pypi.org/simple" }
177
176
  dependencies = [
178
177
  { name = "importlib-metadata", marker = "python_full_version < '3.10'" },
179
178
  ]
180
- sdist = { url = "https://files.pythonhosted.org/packages/54/28/3af612670f82f4c056911fbbbb42760255801b3068c48de792d354ff4472/markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2", size = 357086 }
179
+ sdist = { url = "https://files.pythonhosted.org/packages/2f/15/222b423b0b88689c266d9eac4e61396fe2cc53464459d6a37618ac863b24/markdown-3.8.tar.gz", hash = "sha256:7df81e63f0df5c4b24b7d156eb81e4690595239b7d70937d0409f1b0de319c6f", size = 360906 }
181
180
  wheels = [
182
- { url = "https://files.pythonhosted.org/packages/3f/08/83871f3c50fc983b88547c196d11cf8c3340e37c32d2e9d6152abe2c61f7/Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803", size = 106349 },
181
+ { url = "https://files.pythonhosted.org/packages/51/3f/afe76f8e2246ffbc867440cbcf90525264df0e658f8a5ca1f872b3f6192a/markdown-3.8-py3-none-any.whl", hash = "sha256:794a929b79c5af141ef5ab0f2f642d0f7b1872981250230e72682346f7cc90dc", size = 106210 },
183
182
  ]
184
183
 
185
184
  [[package]]
@@ -286,7 +285,7 @@ version = "1.6.1"
286
285
  source = { registry = "https://pypi.org/simple" }
287
286
  dependencies = [
288
287
  { name = "click" },
289
- { name = "colorama", marker = "sys_platform == 'win32'" },
288
+ { name = "colorama", marker = "platform_system == 'Windows'" },
290
289
  { name = "ghp-import" },
291
290
  { name = "importlib-metadata", marker = "python_full_version < '3.10'" },
292
291
  { name = "jinja2" },
@@ -340,7 +339,7 @@ wheels = [
340
339
 
341
340
  [[package]]
342
341
  name = "ovld"
343
- version = "0.4.6"
342
+ version = "0.5.0"
344
343
  source = { editable = "." }
345
344
 
346
345
  [package.dev-dependencies]
@@ -477,15 +476,15 @@ wheels = [
477
476
 
478
477
  [[package]]
479
478
  name = "pytest-cov"
480
- version = "6.1.0"
479
+ version = "6.1.1"
481
480
  source = { registry = "https://pypi.org/simple" }
482
481
  dependencies = [
483
482
  { name = "coverage", extra = ["toml"] },
484
483
  { name = "pytest" },
485
484
  ]
486
- sdist = { url = "https://files.pythonhosted.org/packages/34/8c/039a7793f23f5cb666c834da9e944123f498ccc0753bed5fbfb2e2c11f87/pytest_cov-6.1.0.tar.gz", hash = "sha256:ec55e828c66755e5b74a21bd7cc03c303a9f928389c0563e50ba454a6dbe71db", size = 66651 }
485
+ sdist = { url = "https://files.pythonhosted.org/packages/25/69/5f1e57f6c5a39f81411b550027bf72842c4567ff5fd572bed1edc9e4b5d9/pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a", size = 66857 }
487
486
  wheels = [
488
- { url = "https://files.pythonhosted.org/packages/e1/c5/8d6ffe9fc8f7f57b3662156ae8a34f2b8e7a754c73b48e689ce43145e98c/pytest_cov-6.1.0-py3-none-any.whl", hash = "sha256:cd7e1d54981d5185ef2b8d64b50172ce97e6f357e6df5cb103e828c7f993e201", size = 23743 },
487
+ { url = "https://files.pythonhosted.org/packages/28/d0/def53b4a790cfb21483016430ed828f64830dd981ebe1089971cd10cab25/pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde", size = 23841 },
489
488
  ]
490
489
 
491
490
  [[package]]
@@ -664,11 +663,11 @@ wheels = [
664
663
 
665
664
  [[package]]
666
665
  name = "typing-extensions"
667
- version = "4.13.0"
666
+ version = "4.13.2"
668
667
  source = { registry = "https://pypi.org/simple" }
669
- sdist = { url = "https://files.pythonhosted.org/packages/0e/3e/b00a62db91a83fff600de219b6ea9908e6918664899a2d85db222f4fbf19/typing_extensions-4.13.0.tar.gz", hash = "sha256:0a4ac55a5820789d87e297727d229866c9650f6521b64206413c4fbada24d95b", size = 106520 }
668
+ sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967 }
670
669
  wheels = [
671
- { url = "https://files.pythonhosted.org/packages/e0/86/39b65d676ec5732de17b7e3c476e45bb80ec64eb50737a8dce1a4178aba1/typing_extensions-4.13.0-py3-none-any.whl", hash = "sha256:c8dd92cc0d6425a97c18fbb9d1954e5ff92c1ca881a309c45f06ebc0b79058e5", size = 45683 },
670
+ { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 },
672
671
  ]
673
672
 
674
673
  [[package]]
@@ -1 +0,0 @@
1
- version = "0.5.0"
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