seedgraph 0.2.0__tar.gz → 0.2.1__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 (81) hide show
  1. seedgraph-0.2.1/.hypothesis/constants/087165afdcb0189b +4 -0
  2. seedgraph-0.2.1/.hypothesis/constants/81538bbb42f71e5a +4 -0
  3. seedgraph-0.2.1/.hypothesis/constants/e5efc23d4d56e78c +4 -0
  4. {seedgraph-0.2.0 → seedgraph-0.2.1}/PKG-INFO +1 -1
  5. {seedgraph-0.2.0 → seedgraph-0.2.1}/pyproject.toml +1 -1
  6. {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/__init__.py +1 -1
  7. {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/sampling.py +15 -10
  8. {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_sampling.py +18 -3
  9. {seedgraph-0.2.0 → seedgraph-0.2.1}/uv.lock +1 -1
  10. {seedgraph-0.2.0 → seedgraph-0.2.1}/.claude-plugin/marketplace.json +0 -0
  11. {seedgraph-0.2.0 → seedgraph-0.2.1}/.github/workflows/ci.yml +0 -0
  12. {seedgraph-0.2.0 → seedgraph-0.2.1}/.github/workflows/docs.yml +0 -0
  13. {seedgraph-0.2.0 → seedgraph-0.2.1}/.github/workflows/release.yml +0 -0
  14. {seedgraph-0.2.0 → seedgraph-0.2.1}/.gitignore +0 -0
  15. {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/.gitignore +0 -0
  16. /seedgraph-0.2.0/.hypothesis/constants/087165afdcb0189b → /seedgraph-0.2.1/.hypothesis/constants/06f4b0e78426ca36 +0 -0
  17. {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/075740dc9c073d2c +0 -0
  18. {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/348eeae83fd80d78 +0 -0
  19. {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/3d29fb9db75fe08b +0 -0
  20. {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/57a0c8abd0c1ecff +0 -0
  21. {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/57cd8d2a68e86184 +0 -0
  22. {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/5ec26cad79d710a5 +0 -0
  23. {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/64a720e42009115f +0 -0
  24. {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/684b78828808d5b3 +0 -0
  25. {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/7104d7e7ac613360 +0 -0
  26. {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/7c52aa175993f729 +0 -0
  27. {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/83b5a5f751a3f794 +0 -0
  28. {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/8941f8a5a302e956 +0 -0
  29. {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/8f532f940c694e09 +0 -0
  30. {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/a5e0b22e1d1da221 +0 -0
  31. {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/a91c6d64a94d166a +0 -0
  32. {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/b5ed315ad3acee35 +0 -0
  33. {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/c94b13735ed4f0a7 +0 -0
  34. {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/d8a73fe2066f60f9 +0 -0
  35. {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/eb397653996f1216 +0 -0
  36. {seedgraph-0.2.0 → seedgraph-0.2.1}/CONTRIBUTING.md +0 -0
  37. {seedgraph-0.2.0 → seedgraph-0.2.1}/LICENSE +0 -0
  38. {seedgraph-0.2.0 → seedgraph-0.2.1}/README.md +0 -0
  39. {seedgraph-0.2.0 → seedgraph-0.2.1}/benchmarks/bench_unique_repair.py +0 -0
  40. {seedgraph-0.2.0 → seedgraph-0.2.1}/benchmarks/bench_weighted_draws.py +0 -0
  41. {seedgraph-0.2.0 → seedgraph-0.2.1}/ci/smoke/pytest.ini +0 -0
  42. {seedgraph-0.2.0 → seedgraph-0.2.1}/ci/smoke/test_user_project.py +0 -0
  43. {seedgraph-0.2.0 → seedgraph-0.2.1}/conftest.py +0 -0
  44. {seedgraph-0.2.0 → seedgraph-0.2.1}/context7.json +0 -0
  45. {seedgraph-0.2.0 → seedgraph-0.2.1}/docs/agents.md +0 -0
  46. {seedgraph-0.2.0 → seedgraph-0.2.1}/docs/index.md +0 -0
  47. {seedgraph-0.2.0 → seedgraph-0.2.1}/docs/recipes/custom-columns.md +0 -0
  48. {seedgraph-0.2.0 → seedgraph-0.2.1}/docs/recipes/fastapi.md +0 -0
  49. {seedgraph-0.2.0 → seedgraph-0.2.1}/docs/recipes/pytest.md +0 -0
  50. {seedgraph-0.2.0 → seedgraph-0.2.1}/docs/reference.md +0 -0
  51. {seedgraph-0.2.0 → seedgraph-0.2.1}/mkdocs.yml +0 -0
  52. {seedgraph-0.2.0 → seedgraph-0.2.1}/plugins/seedgraph/.claude-plugin/plugin.json +0 -0
  53. {seedgraph-0.2.0 → seedgraph-0.2.1}/plugins/seedgraph/skills/seedgraph/SKILL.md +0 -0
  54. {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/boundary.py +0 -0
  55. {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/exceptions.py +0 -0
  56. {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/generators.py +0 -0
  57. {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/graph.py +0 -0
  58. {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/py.typed +0 -0
  59. {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/pytest_plugin.py +0 -0
  60. {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/shape.py +0 -0
  61. {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/uniqueness.py +0 -0
  62. {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/verification.py +0 -0
  63. {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/_legacy_uniqueness.py +0 -0
  64. {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/_oracle.py +0 -0
  65. {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/conftest.py +0 -0
  66. {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_async.py +0 -0
  67. {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_docs.py +0 -0
  68. {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_fixtures.py +0 -0
  69. {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_generators.py +0 -0
  70. {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_install.py +0 -0
  71. {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_many_to_many.py +0 -0
  72. {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_overrides.py +0 -0
  73. {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_parents.py +0 -0
  74. {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_postgres.py +0 -0
  75. {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_seed.py +0 -0
  76. {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_selfref.py +0 -0
  77. {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_shape.py +0 -0
  78. {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_types.py +0 -0
  79. {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_unique.py +0 -0
  80. {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_unique_equivalence.py +0 -0
  81. {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_verification.py +0 -0
@@ -0,0 +1,4 @@
1
+ # file: /Users/rachidj/DEV/seedgraph/src/seedgraph/sampling.py
2
+ # hypothesis_version: 6.170.9
3
+
4
+ ['__use_weighting__', 'a', 'b', 'c', 'elements', 'factories', 'generator', 'length', 'providers', 'random', 'self', 'unique', 'use_weighting']
@@ -0,0 +1,4 @@
1
+ # file: /Users/rachidj/DEV/seedgraph/src/seedgraph/__init__.py
2
+ # hypothesis_version: 6.170.9
3
+
4
+ ['0.2.0', 'AmbiguousParentError', 'Graph', 'IncoherentGraphError', 'SeedgraphError', 'UnknownShapeKeyError', '__version__', 'seed', 'seed_async', 'sqlite', 'unreachable']
@@ -0,0 +1,4 @@
1
+ # file: /Users/rachidj/DEV/seedgraph/src/seedgraph/sampling.py
2
+ # hypothesis_version: 6.170.9
3
+
4
+ ['__use_weighting__', 'a', 'b', 'c', 'elements', 'factories', 'generator', 'length', 'providers', 'random', 'self', 'unique', 'use_weighting']
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: seedgraph
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Seed your SQLAlchemy models as a referentially-consistent graph — one call, shared parents, verified links.
5
5
  Project-URL: Homepage, https://github.com/jrachid/seedgraph
6
6
  Project-URL: Documentation, https://jrachid.github.io/seedgraph/
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "seedgraph"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "Seed your SQLAlchemy models as a referentially-consistent graph — one call, shared parents, verified links."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -45,7 +45,7 @@ from seedgraph.verification import IncoherentGraphError, verify_graph
45
45
  if TYPE_CHECKING:
46
46
  from sqlalchemy.ext.asyncio import AsyncSession
47
47
 
48
- __version__ = "0.2.0"
48
+ __version__ = "0.2.1"
49
49
 
50
50
  __all__ = [
51
51
  "AmbiguousParentError",
@@ -1,4 +1,4 @@
1
- """Draw from Faker's weighted tables through cumulative weights computed once, with the exact values Faker itself draws."""
1
+ """Draw from the weighted tables Faker ships through cumulative weights computed once, with the exact values Faker itself draws."""
2
2
 
3
3
  import inspect
4
4
  from collections import OrderedDict
@@ -12,12 +12,15 @@ from faker.providers import BaseProvider
12
12
 
13
13
  __all__ = ["install_cumulative_sampling"]
14
14
 
15
- CACHE_ATTRIBUTE = "_seedgraph_cumulative"
16
15
  FAKER_SIGNATURE = ["self", "elements", "length", "unique", "use_weighting"]
17
16
 
17
+ # The tables the providers carry as class attributes, by id: only these never change, so only these keep cached weights.
18
+ _shipped: dict[int, OrderedDict[Any, float]] = {}
19
+ _cumulative: dict[int, tuple[tuple[Any, ...], list[float]]] = {}
20
+
18
21
 
19
22
  def install_cumulative_sampling(fake: Faker) -> None:
20
- """Give each provider of this Faker a random_elements that reuses each weighted table's cumulative weights.
23
+ """Give each provider of this Faker a random_elements that reuses the cumulative weights of the tables Faker ships.
21
24
 
22
25
  Installs nothing when one of the Faker internals it relies on is missing.
23
26
  """
@@ -27,6 +30,8 @@ def install_cumulative_sampling(fake: Faker) -> None:
27
30
  if not all(hasattr(p, "__use_weighting__") and hasattr(getattr(p, "generator", None), "random") for p in providers):
28
31
  return
29
32
  for provider in providers:
33
+ for cls in type(provider).__mro__:
34
+ _shipped.update((id(table), table) for table in vars(cls).values() if isinstance(table, OrderedDict))
30
35
  provider.random_elements = MethodType(_random_elements, provider)
31
36
 
32
37
 
@@ -38,20 +43,20 @@ def _random_elements(
38
43
  use_weighting: bool | None = None,
39
44
  ) -> Sequence[Any]:
40
45
  weighting = provider.__use_weighting__ if use_weighting is None else use_weighting
41
- if unique or not weighting or not isinstance(elements, OrderedDict):
46
+ shipped = _shipped.get(id(elements))
47
+ if unique or not weighting or shipped is None or shipped is not elements:
42
48
  return BaseProvider.random_elements(provider, elements, length, unique, use_weighting)
43
49
  rng = provider.generator.random
44
50
  if length is None:
45
- length = rng.randint(1, len(elements))
46
- keys, cumulative = _cumulative_table(elements)
51
+ length = rng.randint(1, len(shipped))
52
+ keys, cumulative = _cumulative_table(shipped)
47
53
  # Given cum_weights, random.choices skips its accumulate pass and bisects the very totals it would have built.
48
54
  drawn: list[Any] = rng.choices(keys, cum_weights=cumulative, k=length)
49
55
  return drawn
50
56
 
51
57
 
52
58
  def _cumulative_table(elements: "OrderedDict[Any, float]") -> tuple[tuple[Any, ...], list[float]]:
53
- table: tuple[tuple[Any, ...], list[float]] | None = getattr(elements, CACHE_ATTRIBUTE, None)
54
- if table is None or len(table[0]) != len(elements):
55
- table = (tuple(elements), list(accumulate(elements.values())))
56
- setattr(elements, CACHE_ATTRIBUTE, table)
59
+ table = _cumulative.get(id(elements))
60
+ if table is None:
61
+ table = _cumulative[id(elements)] = (tuple(elements), list(accumulate(elements.values())))
57
62
  return table
@@ -5,6 +5,7 @@ from collections import OrderedDict
5
5
  import pytest
6
6
  from faker import Faker
7
7
  from faker.providers import BaseProvider
8
+ from faker.providers.person import en_US
8
9
  from hypothesis import given
9
10
  from hypothesis import strategies as st
10
11
  from sqlalchemy import Column, ForeignKey, Integer, String, create_engine
@@ -65,16 +66,30 @@ def _pair(locale, seed_value):
65
66
  return untouched, seedgraph_fake
66
67
 
67
68
 
68
- def test_a_weighted_table_drawn_many_times_reads_its_weights_once():
69
- table = CountingTable([("rare", 1), ("common", 8), ("medium", 3)])
69
+ def test_a_table_faker_ships_reads_its_weights_once_however_many_draws(monkeypatch):
70
+ table = CountingTable(en_US.Provider.last_names)
71
+ monkeypatch.setattr(en_US.Provider, "last_names", table)
70
72
  fake = GenerationState().fake
71
73
 
72
74
  for _ in range(50):
73
- fake.random_element(table)
75
+ fake.last_name()
74
76
 
75
77
  assert table.reads == 1
76
78
 
77
79
 
80
+ def test_a_custom_table_changed_in_place_is_drawn_with_its_new_weights_like_faker_does():
81
+ table = OrderedDict([("before", 1), ("after", 0)])
82
+ untouched, seedgraph_fake = _pair(DEFAULT_LOCALE, 0)
83
+ draws = []
84
+ for fake in (untouched, seedgraph_fake):
85
+ table["before"], table["after"] = 1, 0
86
+ first = fake.random_element(table)
87
+ table["before"], table["after"] = 0, 1
88
+ draws.append((first, fake.random_element(table)))
89
+
90
+ assert draws[0] == draws[1] == ("before", "after")
91
+
92
+
78
93
  @given(
79
94
  locale=st.sampled_from(LOCALES),
80
95
  seed_value=st.integers(0, 2**32 - 1),
@@ -1590,7 +1590,7 @@ wheels = [
1590
1590
 
1591
1591
  [[package]]
1592
1592
  name = "seedgraph"
1593
- version = "0.2.0"
1593
+ version = "0.2.1"
1594
1594
  source = { editable = "." }
1595
1595
  dependencies = [
1596
1596
  { name = "faker" },
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