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.
- seedgraph-0.2.1/.hypothesis/constants/087165afdcb0189b +4 -0
- seedgraph-0.2.1/.hypothesis/constants/81538bbb42f71e5a +4 -0
- seedgraph-0.2.1/.hypothesis/constants/e5efc23d4d56e78c +4 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/PKG-INFO +1 -1
- {seedgraph-0.2.0 → seedgraph-0.2.1}/pyproject.toml +1 -1
- {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/__init__.py +1 -1
- {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/sampling.py +15 -10
- {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_sampling.py +18 -3
- {seedgraph-0.2.0 → seedgraph-0.2.1}/uv.lock +1 -1
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.claude-plugin/marketplace.json +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.github/workflows/ci.yml +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.github/workflows/docs.yml +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.github/workflows/release.yml +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.gitignore +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/.gitignore +0 -0
- /seedgraph-0.2.0/.hypothesis/constants/087165afdcb0189b → /seedgraph-0.2.1/.hypothesis/constants/06f4b0e78426ca36 +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/075740dc9c073d2c +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/348eeae83fd80d78 +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/3d29fb9db75fe08b +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/57a0c8abd0c1ecff +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/57cd8d2a68e86184 +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/5ec26cad79d710a5 +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/64a720e42009115f +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/684b78828808d5b3 +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/7104d7e7ac613360 +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/7c52aa175993f729 +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/83b5a5f751a3f794 +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/8941f8a5a302e956 +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/8f532f940c694e09 +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/a5e0b22e1d1da221 +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/a91c6d64a94d166a +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/b5ed315ad3acee35 +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/c94b13735ed4f0a7 +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/d8a73fe2066f60f9 +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/.hypothesis/constants/eb397653996f1216 +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/CONTRIBUTING.md +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/LICENSE +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/README.md +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/benchmarks/bench_unique_repair.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/benchmarks/bench_weighted_draws.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/ci/smoke/pytest.ini +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/ci/smoke/test_user_project.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/conftest.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/context7.json +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/docs/agents.md +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/docs/index.md +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/docs/recipes/custom-columns.md +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/docs/recipes/fastapi.md +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/docs/recipes/pytest.md +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/docs/reference.md +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/mkdocs.yml +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/plugins/seedgraph/.claude-plugin/plugin.json +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/plugins/seedgraph/skills/seedgraph/SKILL.md +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/boundary.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/exceptions.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/generators.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/graph.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/py.typed +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/pytest_plugin.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/shape.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/uniqueness.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/src/seedgraph/verification.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/_legacy_uniqueness.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/_oracle.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/conftest.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_async.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_docs.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_fixtures.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_generators.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_install.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_many_to_many.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_overrides.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_parents.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_postgres.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_seed.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_selfref.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_shape.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_types.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_unique.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_unique_equivalence.py +0 -0
- {seedgraph-0.2.0 → seedgraph-0.2.1}/tests/test_verification.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: seedgraph
|
|
3
|
-
Version: 0.2.
|
|
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.
|
|
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"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Draw from
|
|
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
|
|
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
|
-
|
|
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(
|
|
46
|
-
keys, cumulative = _cumulative_table(
|
|
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
|
|
54
|
-
if table is None
|
|
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
|
|
69
|
-
table = CountingTable(
|
|
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.
|
|
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),
|
|
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
|
|
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
|