schemathesis 4.0.0a8__py3-none-any.whl → 4.0.0a9__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.
@@ -37,6 +37,8 @@ def json_recursive_strategy(strategy: st.SearchStrategy) -> st.SearchStrategy:
37
37
 
38
38
 
39
39
  BUFFER_SIZE = 8 * 1024
40
+ NEGATIVE_MODE_MAX_LENGTH_WITH_PATTERN = 100
41
+ NEGATIVE_MODE_MAX_ITEMS = 15
40
42
  FLOAT_STRATEGY: st.SearchStrategy = st.floats(allow_nan=False, allow_infinity=False).map(_replace_zero_with_nonzero)
41
43
  NUMERIC_STRATEGY: st.SearchStrategy = st.integers() | FLOAT_STRATEGY
42
44
  JSON_STRATEGY: st.SearchStrategy = st.recursive(
@@ -257,6 +259,9 @@ def _cover_positive_for_type(
257
259
  if ty == "object" or ty == "array":
258
260
  template_schema = _get_template_schema(schema, ty)
259
261
  template = ctx.generate_from_schema(template_schema)
262
+ elif "properties" in schema or "required" in schema:
263
+ template_schema = _get_template_schema(schema, "object")
264
+ template = ctx.generate_from_schema(template_schema)
260
265
  else:
261
266
  template = None
262
267
  if GenerationMode.POSITIVE in ctx.generation_modes:
@@ -295,6 +300,8 @@ def _cover_positive_for_type(
295
300
  yield from _positive_array(ctx, schema, cast(list, template))
296
301
  elif ty == "object":
297
302
  yield from _positive_object(ctx, schema, cast(dict, template))
303
+ elif "properties" in schema or "required" in schema:
304
+ yield from _positive_object(ctx, schema, cast(dict, template))
298
305
 
299
306
 
300
307
  @contextmanager
@@ -387,41 +394,58 @@ def cover_schema_iter(
387
394
  yield value_
388
395
  seen.add(k)
389
396
  elif key == "minLength" and 0 < value < BUFFER_SIZE:
390
- with suppress(InvalidArgument):
391
- min_length = max_length = value - 1
392
- new_schema = {**schema, "minLength": min_length, "maxLength": max_length}
393
- new_schema.setdefault("type", "string")
394
- if "pattern" in new_schema:
395
- new_schema["pattern"] = update_quantifier(schema["pattern"], min_length, max_length)
396
- if new_schema["pattern"] == schema["pattern"]:
397
- # Pattern wasn't updated, try to generate a valid value then shrink the string to the required length
398
- del new_schema["minLength"]
399
- del new_schema["maxLength"]
400
- value = ctx.generate_from_schema(new_schema)[:max_length]
401
- else:
402
- value = ctx.generate_from_schema(new_schema)
403
- else:
404
- value = ctx.generate_from_schema(new_schema)
397
+ if value == 1:
398
+ # In this case, the only possible negative string is an empty one
399
+ # The `pattern` value may require an non-empty one and the generation will fail
400
+ # However, it is fine to violate `pattern` here as it is negative string generation anyway
401
+ value = ""
405
402
  k = _to_hashable_key(value)
406
403
  if k not in seen:
407
404
  yield NegativeValue(
408
405
  value, description="String smaller than minLength", location=ctx.current_path
409
406
  )
410
407
  seen.add(k)
408
+ else:
409
+ with suppress(InvalidArgument):
410
+ min_length = max_length = value - 1
411
+ new_schema = {**schema, "minLength": min_length, "maxLength": max_length}
412
+ new_schema.setdefault("type", "string")
413
+ if "pattern" in new_schema:
414
+ new_schema["pattern"] = update_quantifier(schema["pattern"], min_length, max_length)
415
+ if new_schema["pattern"] == schema["pattern"]:
416
+ # Pattern wasn't updated, try to generate a valid value then shrink the string to the required length
417
+ del new_schema["minLength"]
418
+ del new_schema["maxLength"]
419
+ value = ctx.generate_from_schema(new_schema)[:max_length]
420
+ else:
421
+ value = ctx.generate_from_schema(new_schema)
422
+ else:
423
+ value = ctx.generate_from_schema(new_schema)
424
+ k = _to_hashable_key(value)
425
+ if k not in seen:
426
+ yield NegativeValue(
427
+ value, description="String smaller than minLength", location=ctx.current_path
428
+ )
429
+ seen.add(k)
411
430
  elif key == "maxLength" and value < BUFFER_SIZE:
412
431
  try:
413
432
  min_length = max_length = value + 1
414
433
  new_schema = {**schema, "minLength": min_length, "maxLength": max_length}
415
434
  new_schema.setdefault("type", "string")
416
435
  if "pattern" in new_schema:
417
- new_schema["pattern"] = update_quantifier(schema["pattern"], min_length, max_length)
418
- if new_schema["pattern"] == schema["pattern"]:
419
- # Pattern wasn't updated, try to generate a valid value then extend the string to the required length
420
- del new_schema["minLength"]
421
- del new_schema["maxLength"]
422
- value = ctx.generate_from_schema(new_schema).ljust(max_length, "0")
423
- else:
436
+ if value > NEGATIVE_MODE_MAX_LENGTH_WITH_PATTERN:
437
+ # Large `maxLength` value can be extremely slow to generate when combined with `pattern`
438
+ del new_schema["pattern"]
424
439
  value = ctx.generate_from_schema(new_schema)
440
+ else:
441
+ new_schema["pattern"] = update_quantifier(schema["pattern"], min_length, max_length)
442
+ if new_schema["pattern"] == schema["pattern"]:
443
+ # Pattern wasn't updated, try to generate a valid value then extend the string to the required length
444
+ del new_schema["minLength"]
445
+ del new_schema["maxLength"]
446
+ value = ctx.generate_from_schema(new_schema).ljust(max_length, "0")
447
+ else:
448
+ value = ctx.generate_from_schema(new_schema)
425
449
  else:
426
450
  value = ctx.generate_from_schema(new_schema)
427
451
  k = _to_hashable_key(value)
@@ -438,10 +462,22 @@ def cover_schema_iter(
438
462
  template = template or ctx.generate_from_schema(_get_template_schema(schema, "object"))
439
463
  yield from _negative_required(ctx, template, value)
440
464
  elif key == "maxItems" and isinstance(value, int) and value < BUFFER_SIZE:
441
- try:
442
- # Force the array to have one more item than allowed
443
- new_schema = {**schema, "minItems": value + 1, "maxItems": value + 1, "type": "array"}
465
+ if value > NEGATIVE_MODE_MAX_ITEMS:
466
+ # It could be extremely slow to generate large arrays
467
+ # Generate values up to the limit and reuse them to construct the final array
468
+ new_schema = {
469
+ **schema,
470
+ "minItems": NEGATIVE_MODE_MAX_ITEMS,
471
+ "maxItems": NEGATIVE_MODE_MAX_ITEMS,
472
+ "type": "array",
473
+ }
444
474
  array_value = ctx.generate_from_schema(new_schema)
475
+ # Extend the array to be of length value + 1 by repeating its own elements
476
+ diff = value + 1 - len(array_value)
477
+ if diff > 0:
478
+ array_value += (
479
+ array_value * (diff // len(array_value)) + array_value[: diff % len(array_value)]
480
+ )
445
481
  k = _to_hashable_key(array_value)
446
482
  if k not in seen:
447
483
  yield NegativeValue(
@@ -450,8 +486,21 @@ def cover_schema_iter(
450
486
  location=ctx.current_path,
451
487
  )
452
488
  seen.add(k)
453
- except (InvalidArgument, Unsatisfiable):
454
- pass
489
+ else:
490
+ try:
491
+ # Force the array to have one more item than allowed
492
+ new_schema = {**schema, "minItems": value + 1, "maxItems": value + 1, "type": "array"}
493
+ array_value = ctx.generate_from_schema(new_schema)
494
+ k = _to_hashable_key(array_value)
495
+ if k not in seen:
496
+ yield NegativeValue(
497
+ array_value,
498
+ description="Array with more items than allowed by maxItems",
499
+ location=ctx.current_path,
500
+ )
501
+ seen.add(k)
502
+ except (InvalidArgument, Unsatisfiable):
503
+ pass
455
504
  elif key == "minItems" and isinstance(value, int) and value > 0:
456
505
  try:
457
506
  # Force the array to have one less item than the minimum
@@ -683,19 +732,22 @@ def _positive_array(ctx: CoverageContext, schema: dict, template: list) -> Gener
683
732
 
684
733
  if example or examples or default:
685
734
  if example:
735
+ seen.add(_to_hashable_key(example))
686
736
  yield PositiveValue(example, description="Example value")
687
737
  if examples:
688
738
  for example in examples:
739
+ seen.add(_to_hashable_key(example))
689
740
  yield PositiveValue(example, description="Example value")
690
741
  if (
691
742
  default
692
743
  and not (example is not None and default == example)
693
744
  and not (examples is not None and any(default == ex for ex in examples))
694
745
  ):
746
+ seen.add(_to_hashable_key(default))
695
747
  yield PositiveValue(default, description="Default value")
696
748
  else:
697
749
  yield PositiveValue(template, description="Valid array")
698
- seen.add(len(template))
750
+ seen.add(_to_hashable_key(template))
699
751
 
700
752
  # Boundary and near-boundary sizes
701
753
  min_items = schema.get("minItems")
@@ -706,19 +758,19 @@ def _positive_array(ctx: CoverageContext, schema: dict, template: list) -> Gener
706
758
  # One item more than minimum if possible
707
759
  larger = min_items + 1
708
760
  if larger not in seen and (max_items is None or larger <= max_items):
709
- yield PositiveValue(
710
- ctx.generate_from_schema({**schema, "minItems": larger, "maxItems": larger}),
711
- description="Near-boundary items array",
712
- )
713
- seen.add(larger)
761
+ value = ctx.generate_from_schema({**schema, "minItems": larger, "maxItems": larger})
762
+ key = _to_hashable_key(value)
763
+ if key not in seen:
764
+ seen.add(key)
765
+ yield PositiveValue(value, description="Near-boundary items array")
714
766
 
715
767
  if max_items is not None:
716
768
  if max_items < BUFFER_SIZE and max_items not in seen:
717
- yield PositiveValue(
718
- ctx.generate_from_schema({**schema, "minItems": max_items}),
719
- description="Maximum items array",
720
- )
721
- seen.add(max_items)
769
+ value = ctx.generate_from_schema({**schema, "minItems": max_items})
770
+ key = _to_hashable_key(value)
771
+ if key not in seen:
772
+ seen.add(key)
773
+ yield PositiveValue(value, description="Maximum items array")
722
774
 
723
775
  # One item smaller than maximum if possible
724
776
  smaller = max_items - 1
@@ -728,11 +780,26 @@ def _positive_array(ctx: CoverageContext, schema: dict, template: list) -> Gener
728
780
  and smaller not in seen
729
781
  and (min_items is None or smaller >= min_items)
730
782
  ):
731
- yield PositiveValue(
732
- ctx.generate_from_schema({**schema, "minItems": smaller, "maxItems": smaller}),
733
- description="Near-boundary items array",
734
- )
735
- seen.add(smaller)
783
+ value = ctx.generate_from_schema({**schema, "minItems": smaller, "maxItems": smaller})
784
+ key = _to_hashable_key(value)
785
+ if key not in seen:
786
+ seen.add(key)
787
+ yield PositiveValue(value, description="Near-boundary items array")
788
+
789
+ if "items" in schema and "enum" in schema["items"] and isinstance(schema["items"]["enum"], list) and max_items != 0:
790
+ # Ensure there is enough items to pass `minItems` if it is specified
791
+ length = min_items or 1
792
+ for variant in schema["items"]["enum"]:
793
+ value = [variant] * length
794
+ key = _to_hashable_key(value)
795
+ if key not in seen:
796
+ seen.add(key)
797
+ yield PositiveValue(value, description="Enum value from available for items array")
798
+ elif min_items is None and max_items is None and "items" in schema and isinstance(schema["items"], dict):
799
+ # Otherwise only an empty array is generated
800
+ sub_schema = schema["items"]
801
+ for item in cover_schema_iter(ctx, sub_schema):
802
+ yield PositiveValue([item.value], description=f"Single-item array: {item.description}")
736
803
 
737
804
 
738
805
  def _positive_object(ctx: CoverageContext, schema: dict, template: dict) -> Generator[GeneratedValue, None, None]:
@@ -1,17 +1,18 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
+ import os
4
5
  from dataclasses import dataclass, field
5
6
  from enum import Enum
6
7
  from functools import wraps
7
8
  from itertools import combinations
8
- import os
9
9
  from time import perf_counter
10
10
  from typing import Any, Callable, Generator, Mapping
11
11
 
12
12
  import hypothesis
13
13
  from hypothesis import Phase
14
14
  from hypothesis import strategies as st
15
+ from hypothesis._settings import all_settings
15
16
  from hypothesis.errors import Unsatisfiable
16
17
  from jsonschema.exceptions import SchemaError
17
18
 
@@ -98,7 +99,11 @@ def create_test(
98
99
  # Merge the user-provided settings with the current ones
99
100
  settings = hypothesis.settings(
100
101
  settings,
101
- **{item: value for item, value in config.settings.__dict__.items() if value != getattr(default, item)},
102
+ **{
103
+ item: getattr(config.settings, item)
104
+ for item in all_settings
105
+ if getattr(config.settings, item) != getattr(default, item)
106
+ },
102
107
  )
103
108
 
104
109
  if Phase.explain in settings.phases:
@@ -69,13 +69,18 @@ def _handle_parsed_pattern(parsed: list, pattern: str, min_length: int | None, m
69
69
  trailing_anchor_length = _get_anchor_length(parsed[2][1])
70
70
  leading_anchor = pattern[:leading_anchor_length]
71
71
  trailing_anchor = pattern[-trailing_anchor_length:]
72
- return (
73
- leading_anchor
74
- + _update_quantifier(
75
- op, value, pattern[leading_anchor_length:-trailing_anchor_length], min_length, max_length
76
- )
77
- + trailing_anchor
78
- )
72
+ # Special case for patterns canonicalisation. Some frameworks generate `\\w\\W` instead of `.`
73
+ # Such patterns lead to significantly slower data generation
74
+ if op == sre.IN and _matches_anything(value):
75
+ op = sre.ANY
76
+ value = None
77
+ inner_pattern = "."
78
+ elif op in REPEATS and len(value[2]) == 1 and value[2][0][0] == sre.IN and _matches_anything(value[2][0][1]):
79
+ value = (value[0], value[1], [(sre.ANY, None)], *value[3:])
80
+ inner_pattern = "."
81
+ else:
82
+ inner_pattern = pattern[leading_anchor_length:-trailing_anchor_length]
83
+ return leading_anchor + _update_quantifier(op, value, inner_pattern, min_length, max_length) + trailing_anchor
79
84
  elif (
80
85
  len(parsed) > 3
81
86
  and parsed[0][0] == ANCHOR
@@ -86,6 +91,19 @@ def _handle_parsed_pattern(parsed: list, pattern: str, min_length: int | None, m
86
91
  return pattern
87
92
 
88
93
 
94
+ def _matches_anything(value: list) -> bool:
95
+ """Check if the given pattern is equivalent to '.' (match any character)."""
96
+ # Common forms: [\w\W], [\s\S], etc.
97
+ return value in (
98
+ [(sre.CATEGORY, sre.CATEGORY_WORD), (sre.CATEGORY, sre.CATEGORY_NOT_WORD)],
99
+ [(sre.CATEGORY, sre.CATEGORY_SPACE), (sre.CATEGORY, sre.CATEGORY_NOT_SPACE)],
100
+ [(sre.CATEGORY, sre.CATEGORY_DIGIT), (sre.CATEGORY, sre.CATEGORY_NOT_DIGIT)],
101
+ [(sre.CATEGORY, sre.CATEGORY_NOT_WORD), (sre.CATEGORY, sre.CATEGORY_WORD)],
102
+ [(sre.CATEGORY, sre.CATEGORY_NOT_SPACE), (sre.CATEGORY, sre.CATEGORY_SPACE)],
103
+ [(sre.CATEGORY, sre.CATEGORY_NOT_DIGIT), (sre.CATEGORY, sre.CATEGORY_DIGIT)],
104
+ )
105
+
106
+
89
107
  def _handle_anchored_pattern(parsed: list, pattern: str, min_length: int | None, max_length: int | None) -> str:
90
108
  """Update regex pattern with multiple quantified patterns to satisfy length constraints."""
91
109
  # Extract anchors
@@ -269,15 +287,28 @@ def _get_anchor_length(node_type: int) -> int:
269
287
  return 1 # ^ or $ or their multiline/locale/unicode variants
270
288
 
271
289
 
272
- def _update_quantifier(op: int, value: tuple, pattern: str, min_length: int | None, max_length: int | None) -> str:
290
+ def _update_quantifier(
291
+ op: int, value: tuple | None, pattern: str, min_length: int | None, max_length: int | None
292
+ ) -> str:
273
293
  """Update the quantifier based on the operation type and given constraints."""
274
- if op in REPEATS:
294
+ if op in REPEATS and value is not None:
275
295
  return _handle_repeat_quantifier(value, pattern, min_length, max_length)
276
296
  if op in (LITERAL, IN) and max_length != 0:
277
297
  return _handle_literal_or_in_quantifier(pattern, min_length, max_length)
298
+ if op == sre.ANY and value is None:
299
+ # Equivalent to `.` which is in turn is the same as `.{1}`
300
+ return _handle_repeat_quantifier(
301
+ SINGLE_ANY,
302
+ pattern,
303
+ min_length,
304
+ max_length,
305
+ )
278
306
  return pattern
279
307
 
280
308
 
309
+ SINGLE_ANY = sre_parse.parse(".{1}")[0][1]
310
+
311
+
281
312
  def _handle_repeat_quantifier(
282
313
  value: tuple[int, int, tuple], pattern: str, min_length: int | None, max_length: int | None
283
314
  ) -> str:
@@ -1195,7 +1195,11 @@ class OpenApi30(SwaggerV20):
1195
1195
  files = []
1196
1196
  definition = operation.definition.raw
1197
1197
  if "$ref" in definition["requestBody"]:
1198
- body = self.resolver.resolve_all(definition["requestBody"], RECURSION_DEPTH_LIMIT)
1198
+ self.resolver.push_scope(operation.definition.scope)
1199
+ try:
1200
+ body = self.resolver.resolve_all(definition["requestBody"], RECURSION_DEPTH_LIMIT)
1201
+ finally:
1202
+ self.resolver.pop_scope()
1199
1203
  else:
1200
1204
  body = definition["requestBody"]
1201
1205
  content = body["content"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: schemathesis
3
- Version: 4.0.0a8
3
+ Version: 4.0.0a9
4
4
  Summary: Property-based testing framework for Open API and GraphQL based apps
5
5
  Project-URL: Documentation, https://schemathesis.readthedocs.io/en/stable/
6
6
  Project-URL: Changelog, https://schemathesis.readthedocs.io/en/stable/changelog.html
@@ -74,13 +74,13 @@ schemathesis/engine/phases/unit/_pool.py,sha256=9OgmFd-ov1AAvcZGquK40PXkGLp7f2qC
74
74
  schemathesis/experimental/__init__.py,sha256=jYY3Mq6okqTRTMudPzcaT0JVjzJW5IN_ZVJdGU0stBs,2011
75
75
  schemathesis/generation/__init__.py,sha256=sWTRPTh-qDNkSfpM9rYI3v8zskH8_wFKUuPRg18fZI8,1627
76
76
  schemathesis/generation/case.py,sha256=Rt5MCUtPVYVQzNyjUx8magocPJpHV1svyuqQSTwUE-I,7306
77
- schemathesis/generation/coverage.py,sha256=vv2dbj_KAaqo8PCwMdyDWLyyAssk-YL5oTU3aCkgB1s,41185
77
+ schemathesis/generation/coverage.py,sha256=YQHmC_wVQ9WBuThNr62LFort9WqHM1DHUtexk-ALSV8,45471
78
78
  schemathesis/generation/meta.py,sha256=36h6m4E7jzLGa8TCvl7eBl_xUWLiRul3qxzexl5cB58,2515
79
79
  schemathesis/generation/modes.py,sha256=t_EvKr2aOXYMsEfdMu4lLF4KCGcX1LVVyvzTkcpJqhk,663
80
80
  schemathesis/generation/overrides.py,sha256=FhqcFoliEvgW6MZyFPYemfLgzKt3Miy8Cud7OMOCb7g,3045
81
81
  schemathesis/generation/targets.py,sha256=_rN2qgxTE2EfvygiN-Fy3WmDnRH0ERohdx3sKRDaYhU,2120
82
82
  schemathesis/generation/hypothesis/__init__.py,sha256=Rl7QwvMBMJI7pBqTydplX6bXC420n0EGQHVm-vZgaYQ,1204
83
- schemathesis/generation/hypothesis/builder.py,sha256=cSgFWQG-apIHNdW-PpIBDBjLw4RorihfZ4e_Ln3j2-w,30341
83
+ schemathesis/generation/hypothesis/builder.py,sha256=QDfZRpFjQ0KYFPgu2BVSlxop0TQL7fQc201jOMR4rSQ,30472
84
84
  schemathesis/generation/hypothesis/examples.py,sha256=6eGaKUEC3elmKsaqfKj1sLvM8EHc-PWT4NRBq4NI0Rs,1409
85
85
  schemathesis/generation/hypothesis/given.py,sha256=sTZR1of6XaHAPWtHx2_WLlZ50M8D5Rjux0GmWkWjDq4,2337
86
86
  schemathesis/generation/hypothesis/reporting.py,sha256=uDVow6Ya8YFkqQuOqRsjbzsbyP4KKfr3jA7ZaY4FuKY,279
@@ -121,9 +121,9 @@ schemathesis/specs/openapi/examples.py,sha256=Xvjp60QUcLaeGsJRbi2i6XM15_4uO0ceVo
121
121
  schemathesis/specs/openapi/formats.py,sha256=ViVF3aFeFI1ctwGQbiRDXhU3so82P0BCaF2aDDbUUm8,2816
122
122
  schemathesis/specs/openapi/media_types.py,sha256=ADedOaNWjbAtAekyaKmNj9fY6zBTeqcNqBEjN0EWNhI,1014
123
123
  schemathesis/specs/openapi/parameters.py,sha256=tVL61gDe9A8_jwoVKZZvpXKPerMyq7vkAvwdMsi44TI,14622
124
- schemathesis/specs/openapi/patterns.py,sha256=NLnGybcana_kYLVKVEjkEyAzdClAV0xKe4Oy4NVayMI,12834
124
+ schemathesis/specs/openapi/patterns.py,sha256=EQdf4net9QtwngKv36FEr7l0-3_afIMrrBdpKUWGWGc,14382
125
125
  schemathesis/specs/openapi/references.py,sha256=c8Ufa8hp6Dyf-gPn5lpmyqF_GtqXIBWoKkj3bk3WaPA,8871
126
- schemathesis/specs/openapi/schemas.py,sha256=zfGPFWnaI9_W8F8E8qCTzuYQRE5yDuGx7WGW4EH-QgI,55020
126
+ schemathesis/specs/openapi/schemas.py,sha256=dL1uLz_twgJZUdYBcs2JJ3b8ZlQH3nrGUg1p78pm9Os,55169
127
127
  schemathesis/specs/openapi/security.py,sha256=6UWYMhL-dPtkTineqqBFNKca1i4EuoTduw-EOLeE0aQ,7149
128
128
  schemathesis/specs/openapi/serialization.py,sha256=VdDLmeHqxlWM4cxQQcCkvrU6XurivolwEEaT13ohelA,11972
129
129
  schemathesis/specs/openapi/utils.py,sha256=ER4vJkdFVDIE7aKyxyYatuuHVRNutytezgE52pqZNE8,900
@@ -146,8 +146,8 @@ schemathesis/transport/prepare.py,sha256=qQ6zXBw5NN2AIM0bzLAc5Ryc3dmMb0R6xN14lnR
146
146
  schemathesis/transport/requests.py,sha256=j5wI1Uo_PnVuP1eV8l6ddsXosyxAPQ1mLSyWEZmTI9I,8747
147
147
  schemathesis/transport/serialization.py,sha256=jIMra1LqRGav0OX3Hx7mvORt38ll4cd2DKit2D58FN0,10531
148
148
  schemathesis/transport/wsgi.py,sha256=RWSuUXPrl91GxAy8a4jyNNozOWVMRBxKx_tljlWA_Lo,5697
149
- schemathesis-4.0.0a8.dist-info/METADATA,sha256=hdJd_ASqgZ75dZ1EW27p3mL6CAG179nTb-cm1zVtQqk,10427
150
- schemathesis-4.0.0a8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
151
- schemathesis-4.0.0a8.dist-info/entry_points.txt,sha256=hiK3un-xfgPdwj9uj16YVDtTNpO128bmk0U82SMv8ZQ,152
152
- schemathesis-4.0.0a8.dist-info/licenses/LICENSE,sha256=2Ve4J8v5jMQAWrT7r1nf3bI8Vflk3rZVQefiF2zpxwg,1121
153
- schemathesis-4.0.0a8.dist-info/RECORD,,
149
+ schemathesis-4.0.0a9.dist-info/METADATA,sha256=xL00zLDR06C2Jth4cyLvr-lrNBb5mDkognESM0xvJfI,10427
150
+ schemathesis-4.0.0a9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
151
+ schemathesis-4.0.0a9.dist-info/entry_points.txt,sha256=hiK3un-xfgPdwj9uj16YVDtTNpO128bmk0U82SMv8ZQ,152
152
+ schemathesis-4.0.0a9.dist-info/licenses/LICENSE,sha256=2Ve4J8v5jMQAWrT7r1nf3bI8Vflk3rZVQefiF2zpxwg,1121
153
+ schemathesis-4.0.0a9.dist-info/RECORD,,