fixtureforge 2.0.1__tar.gz → 2.1.0__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 (48) hide show
  1. fixtureforge-2.1.0/PKG-INFO +427 -0
  2. fixtureforge-2.1.0/README.md +392 -0
  3. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/pyproject.toml +9 -2
  4. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/__init__.py +1 -1
  5. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/core/generator.py +18 -12
  6. fixtureforge-2.1.0/src/fixtureforge/pytest_plugin.py +264 -0
  7. fixtureforge-2.0.1/PKG-INFO +0 -50
  8. fixtureforge-2.0.1/README.md +0 -16
  9. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/LICENSE +0 -0
  10. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/ai/__init__.py +0 -0
  11. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/ai/cache.py +0 -0
  12. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/ai/engine.py +0 -0
  13. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/ai/prompts.py +0 -0
  14. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/cli/__init__.py +0 -0
  15. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/cli/commands.py +0 -0
  16. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/config/__init__.py +0 -0
  17. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/config/flags.py +0 -0
  18. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/core/__init__.py +0 -0
  19. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/core/analyzer.py +0 -0
  20. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/core/batch_engine.py +0 -0
  21. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/core/compression.py +0 -0
  22. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/core/dataset.py +0 -0
  23. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/core/exporter.py +0 -0
  24. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/core/parser.py +0 -0
  25. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/core/recipe.py +0 -0
  26. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/core/router.py +0 -0
  27. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/core/streamer.py +0 -0
  28. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/core/swarm.py +0 -0
  29. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/integrations/__init__.py +0 -0
  30. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/integrations/github.py +0 -0
  31. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/integrations/jira.py +0 -0
  32. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/library/__init__.py +0 -0
  33. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/library/sharing.py +0 -0
  34. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/library/storage.py +0 -0
  35. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/memory/__init__.py +0 -0
  36. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/memory/dream.py +0 -0
  37. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/memory/store.py +0 -0
  38. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/providers/__init__.py +0 -0
  39. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/providers/anthropic.py +0 -0
  40. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/providers/base.py +0 -0
  41. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/providers/factory.py +0 -0
  42. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/providers/gemini.py +0 -0
  43. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/providers/groq.py +0 -0
  44. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/providers/ollama.py +0 -0
  45. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/providers/openai.py +0 -0
  46. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/pyproject.toml +0 -0
  47. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/security/__init__.py +0 -0
  48. {fixtureforge-2.0.1 → fixtureforge-2.1.0}/src/fixtureforge/security/permissions.py +0 -0
@@ -0,0 +1,427 @@
1
+ Metadata-Version: 2.4
2
+ Name: fixtureforge
3
+ Version: 2.1.0
4
+ Summary: Agentic Test Data Harness: memory, multi-agent swarms, permission gates, coverage analysis. Provider-agnostic (Gemini, OpenAI, Anthropic, Ollama).
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Keywords: testing,fixtures,test-data,qa,automation,synthetic-data,llm,pytest
8
+ Author: Yaniv Metuku
9
+ Requires-Python: >=3.11,<4.0
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Provides-Extra: all
17
+ Provides-Extra: anthropic
18
+ Provides-Extra: gemini
19
+ Provides-Extra: openai
20
+ Provides-Extra: sql
21
+ Requires-Dist: anthropic (>=0.18.0,<0.19.0) ; extra == "anthropic" or extra == "all"
22
+ Requires-Dist: click (>=8.1.0,<9.0.0)
23
+ Requires-Dist: faker (>=22.0.0,<23.0.0)
24
+ Requires-Dist: google-genai (>=1.0.0,<2.0.0) ; extra == "gemini" or extra == "all"
25
+ Requires-Dist: openai (>=1.0.0,<2.0.0) ; extra == "openai" or extra == "all"
26
+ Requires-Dist: pydantic (>=2.5.0,<3.0.0)
27
+ Requires-Dist: pyyaml (>=6.0,<7.0)
28
+ Requires-Dist: requests (>=2.31.0,<3.0.0)
29
+ Requires-Dist: rich (>=13.7.0,<14.0.0)
30
+ Requires-Dist: sqlalchemy (>=2.0.0,<3.0.0) ; extra == "sql" or extra == "all"
31
+ Project-URL: Homepage, https://fixtureforge.dev
32
+ Project-URL: Repository, https://github.com/Yaniv2809/fixtureforge
33
+ Description-Content-Type: text/markdown
34
+
35
+ # FixtureForge
36
+
37
+ **Agentic Test Data Harness for Python.**
38
+ Generate realistic, context-aware fixtures — deterministic in CI, AI-powered in development.
39
+
40
+ [![PyPI version](https://img.shields.io/pypi/v/fixtureforge.svg)](https://pypi.org/project/fixtureforge/)
41
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
42
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
43
+
44
+ ---
45
+
46
+ ## The Problem
47
+
48
+ ```python
49
+ # This is what most test data looks like:
50
+ user = User(name="Test User", email="test@test.com", bio="Lorem ipsum...")
51
+
52
+ # It doesn't catch real-world edge cases.
53
+ # It doesn't feel like production data.
54
+ # And writing 500 of them by hand? Not happening.
55
+ ```
56
+
57
+ FixtureForge solves this in two modes:
58
+
59
+ ```python
60
+ # CI mode — deterministic, zero AI, seed-controlled. Same seed = same data. Always.
61
+ forge = Forge(use_ai=False, seed=42)
62
+ users = forge.create_batch(User, count=500)
63
+
64
+ # Dev mode — AI-generated, context-aware, realistic
65
+ forge = Forge()
66
+ reviews = forge.create_batch(Review, count=50, context="angry holiday customers")
67
+ ```
68
+
69
+ ---
70
+
71
+ ## Installation
72
+
73
+ ```bash
74
+ pip install fixtureforge
75
+ ```
76
+
77
+ With your preferred AI provider:
78
+
79
+ ```bash
80
+ pip install "fixtureforge[anthropic]" # Claude
81
+ pip install "fixtureforge[openai]" # GPT
82
+ pip install "fixtureforge[gemini]" # Google Gemini
83
+ pip install "fixtureforge[all]" # All providers
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Quick Start
89
+
90
+ ```python
91
+ from fixtureforge import Forge
92
+ from pydantic import BaseModel
93
+
94
+ class User(BaseModel):
95
+ id: int
96
+ name: str
97
+ email: str
98
+ bio: str
99
+
100
+ forge = Forge() # auto-detects provider from env vars
101
+ users = forge.create_batch(User, count=50, context="SaaS platform users")
102
+ ```
103
+
104
+ That's it. FixtureForge:
105
+ - Assigns sequential IDs automatically
106
+ - Generates `name` and `email` with Faker (zero API cost)
107
+ - Sends only `bio` to the AI — in a single batch call for all 50 records
108
+
109
+ ---
110
+
111
+ ## Core Concepts
112
+
113
+ ### Intelligent Field Routing
114
+
115
+ Every field is classified into a tier. Only semantic fields hit the AI:
116
+
117
+ | Tier | Fields | Generator | Cost |
118
+ |------|--------|-----------|------|
119
+ | **Structural** | `id`, `user_id`, `order_id` | Internal counters / FK registry | Free |
120
+ | **Standard** | `name`, `email`, `phone`, `address`, `date` | Faker | Free |
121
+ | **Computed** | `@computed_field` properties | Pydantic | Free |
122
+ | **Semantic** | `bio`, `description`, `review`, `message` | LLM (batched) | API tokens |
123
+
124
+ 100 users with 2 semantic fields = **2 API calls**, not 200.
125
+
126
+ ### CI Mode vs Dev Mode
127
+
128
+ ```python
129
+ # CI — fully deterministic, no network, reproducible
130
+ forge = Forge(use_ai=False, seed=42)
131
+
132
+ # Dev — AI-powered, realistic context
133
+ forge = Forge(provider_name="anthropic", model="claude-haiku-4-5-20251001")
134
+
135
+ # Large datasets — seed+interpolation, constant cost regardless of count
136
+ forge.create_large(Order, count=100_000, seed_ratio=0.01) # pays for ~1k, delivers 100k
137
+ ```
138
+
139
+ ### Verbose Mode
140
+
141
+ See exactly where each value comes from:
142
+
143
+ ```python
144
+ forge = Forge(use_ai=False, seed=42, verbose=True)
145
+ user = forge.create(User)
146
+
147
+ # [structural] id = 1
148
+ # [faker] name = 'Allison Hill'
149
+ # [faker] email = 'donaldgarcia@example.net'
150
+ # [ai] bio = 'Passionate developer with 8 years...'
151
+ ```
152
+
153
+ ---
154
+
155
+ ## Providers
156
+
157
+ FixtureForge auto-detects your provider from environment variables:
158
+
159
+ ```bash
160
+ export ANTHROPIC_API_KEY=... # → Claude (default: claude-haiku-4-5-20251001)
161
+ export OPENAI_API_KEY=... # → GPT (default: gpt-4o-mini)
162
+ export GOOGLE_API_KEY=... # → Gemini (default: gemini-2.0-flash)
163
+ export GROQ_API_KEY=... # → Groq (default: llama-3.3-70b-versatile)
164
+ # No key? → Ollama (localhost:11434) → Deterministic-only
165
+ ```
166
+
167
+ Or be explicit:
168
+
169
+ ```python
170
+ forge = Forge(provider_name="anthropic", model="claude-sonnet-4-6")
171
+ forge = Forge(provider_name="ollama", model="llama3.2")
172
+ forge = Forge(use_ai=False) # zero cost, zero network
173
+ ```
174
+
175
+ ---
176
+
177
+ ## Foreign Key Relationships
178
+
179
+ Register parent records first — child FKs resolve automatically:
180
+
181
+ ```python
182
+ # Step 1: generate customers
183
+ customers = forge.create_batch(Customer, count=10)
184
+
185
+ # Step 2: orders automatically reference real customer IDs
186
+ orders = forge.create_batch(Order, count=100)
187
+ # order.customer_id → always a valid customer.id
188
+ ```
189
+
190
+ ---
191
+
192
+ ## DataSwarms — Parallel Multi-Model Generation
193
+
194
+ Generate multiple models in parallel with shared AI cache.
195
+ The first model warms the cache; every subsequent model inherits it (~90% cheaper per model).
196
+
197
+ ```python
198
+ results = forge.swarm(
199
+ models=[User, Order, Product, Payment],
200
+ counts=[10, 50, 100, 30],
201
+ contexts=["SaaS users", "E-commerce orders", None, None],
202
+ )
203
+
204
+ # returns:
205
+ # {
206
+ # "User": [...10 users...],
207
+ # "Order": [...50 orders...],
208
+ # "Product": [...100 products...],
209
+ # "Payment": [...30 payments...],
210
+ # }
211
+ ```
212
+
213
+ 5 models ≈ cost of 1.5 models.
214
+
215
+ ---
216
+
217
+ ## Permission Gates
218
+
219
+ FixtureForge classifies models by data sensitivity and gates dangerous operations:
220
+
221
+ ```python
222
+ class SafeUser(BaseModel):
223
+ id: int
224
+ name: str # SAFE — auto-approved
225
+
226
+ class CustomerProfile(BaseModel):
227
+ id: int
228
+ ssn: str # SENSITIVE — requires FORGE_ALLOW_PII=1
229
+ salary: float # SENSITIVE
230
+
231
+ class SecurityTest(BaseModel):
232
+ id: int
233
+ sql_injection: str # DANGEROUS — requires interactive confirmation
234
+ ```
235
+
236
+ ```python
237
+ # PII auto-approved
238
+ forge = Forge(allow_pii=True)
239
+
240
+ # CI/headless — dangerous ops silently rejected
241
+ forge = Forge(interactive=False)
242
+ ```
243
+
244
+ Three levels: `safe` (auto) → `sensitive` (env gate) → `dangerous` (human prompt).
245
+
246
+ ---
247
+
248
+ ## Domain Rules — ForgeMemory
249
+
250
+ Persist business rules that survive across sessions.
251
+ Rules are re-read on every generation call — update a rule, next call respects it immediately.
252
+
253
+ ```python
254
+ forge.memory.add_rule("financial", "Users under 18 get restricted account type")
255
+ forge.memory.add_rule("user", "Israeli phone numbers use format 05x-xxx-xxxx")
256
+ forge.memory.add_rule("orders", "Max 3 active loans per customer at any time")
257
+
258
+ # Rules inject into AI prompts automatically
259
+ users = forge.create_batch(User, count=50, context="Israeli SaaS platform")
260
+ ```
261
+
262
+ **Skeptical Memory** — rules are hints, not truth. FixtureForge validates stored rules against the live schema before every generation call.
263
+
264
+ **Progressive Forgetting** — field names and types are never stored (re-derivable from the model). Only business rules that exist nowhere else in the code are kept.
265
+
266
+ ---
267
+
268
+ ## ForgeDream — Coverage Analysis
269
+
270
+ Find gaps in your test-data coverage automatically:
271
+
272
+ ```python
273
+ import os
274
+ os.environ["FORGE_FLAG_DREAM"] = "1"
275
+
276
+ report = forge.dream(models=[User, Order], force=True)
277
+ print(report.summary())
278
+
279
+ # ForgeDream Report - 2026-04-08
280
+ # Coverage gaps found : 3
281
+ # Rule conflicts found : 0
282
+ # Top gaps:
283
+ # [User.age] no_boundary : No boundary-value rules for numeric field 'age'
284
+ # [User.email] no_invalid : No invalid-data rules for well-known field 'email'
285
+ # [Order.total] no_boundary: No boundary-value rules for numeric field 'total'
286
+ ```
287
+
288
+ Four phases: **Orient** (read index) → **Gather** (find gaps) → **Consolidate** (merge rules) → **Prune** (trim to ≤200 lines).
289
+
290
+ Report saved as `.forge/coverage_gaps.json`.
291
+
292
+ ---
293
+
294
+ ## Streaming — Memory-Safe Large Datasets
295
+
296
+ ```python
297
+ # Lazy evaluation — writes to disk one record at a time
298
+ for user in forge.create_stream(User, count=1_000_000, filename="users.json"):
299
+ pass # process one record, never loads all into memory
300
+ ```
301
+
302
+ Supports `.json`, `.csv`, `.sql` output formats.
303
+
304
+ ---
305
+
306
+ ## Export
307
+
308
+ ```python
309
+ from fixtureforge.core.exporter import DataExporter
310
+
311
+ users = forge.create_batch(User, count=100)
312
+ DataExporter.to_json(users, "users.json")
313
+ DataExporter.to_csv(users, "users.csv")
314
+ DataExporter.to_sql(users, "users.sql", table_name="users")
315
+ ```
316
+
317
+ ---
318
+
319
+ ## Response Cache
320
+
321
+ AI responses are cached locally for 7 days. Identical requests cost nothing after the first call.
322
+
323
+ ```python
324
+ forge = Forge(use_cache=True) # default — saves to ~/.fixtureforge/cache/
325
+ forge = Forge(use_cache=False) # disable caching
326
+ ```
327
+
328
+ ---
329
+
330
+ ## Feature Flags
331
+
332
+ ```python
333
+ from fixtureforge.config import is_enabled, flag_summary
334
+
335
+ flag_summary()
336
+ # {
337
+ # 'FORGE_SWARMS': True, # shipped
338
+ # 'FORGE_PERMISSIONS': True, # shipped
339
+ # 'FORGE_COMPRESSION': True, # shipped
340
+ # 'FORGE_MCP': True, # shipped
341
+ # 'FORGE_DREAM': False, # enable with FORGE_FLAG_DREAM=1
342
+ # 'FORGE_KAIROS': False, # coming in v2.x
343
+ # 'FORGE_ULTRAPLAN': False, # coming in v2.x
344
+ # }
345
+ ```
346
+
347
+ Enable any staged feature with an env var:
348
+
349
+ ```bash
350
+ FORGE_FLAG_DREAM=1 python run_tests.py
351
+ ```
352
+
353
+ ---
354
+
355
+ ## Stats & Diagnostics
356
+
357
+ ```python
358
+ forge.stats()
359
+ # {
360
+ # "registry": {"user": 50, "order": 200},
361
+ # "session_tokens": 1240,
362
+ # "memory": {"topics": 3, "total_kb": 2.4},
363
+ # "flags": {"FORGE_SWARMS": True, "FORGE_PERMISSIONS": True}
364
+ # }
365
+
366
+ forge.clear_registry() # reset FK registry between independent test scenarios
367
+ ```
368
+
369
+ ---
370
+
371
+ ## Architecture
372
+
373
+ ```
374
+ FixtureForge v2.0
375
+ ├── Config Layer feature flags, env-var overrides
376
+ ├── Security Layer safe / sensitive / dangerous gates, mailbox pattern
377
+ ├── Memory Layer FORGE.md pointer index, on-demand topic files
378
+ ├── Generation Layer IntelligentRouter, SmartBatchEngine, DataSwarms
379
+ ├── Compression Layer Micro → Auto → Full (three-layer pipeline)
380
+ ├── Export Layer JSON / CSV / SQL / streaming
381
+ └── Background Layer ForgeDream coverage analysis (feature-flagged)
382
+ ```
383
+
384
+ **Provider-agnostic**: Claude, GPT, Gemini, Groq, Ollama, or no AI at all.
385
+ **Pydantic v2 native**: full support for `@computed_field`, validators, and constrained types.
386
+ **CI-safe**: `seed=` parameter guarantees identical output across runs.
387
+
388
+ ---
389
+
390
+ ## Comparison
391
+
392
+ | | FixtureForge | factory_boy | faker | hypothesis |
393
+ |---|---|---|---|---|
394
+ | AI-generated context | Yes | No | No | No |
395
+ | Deterministic (seed=) | Yes | Yes | Yes | Yes |
396
+ | FK relationships | Auto | Manual | No | No |
397
+ | Coverage analysis | Yes | No | No | Partial |
398
+ | CI-safe mode | Yes | Yes | Yes | Yes |
399
+ | Large datasets | Yes (100k+) | Manual | Manual | No |
400
+ | Permission gates | Yes | No | No | No |
401
+
402
+ FixtureForge is not a replacement for `faker` — it uses `faker` internally. It's not a replacement for `hypothesis` — it solves a different problem. It adds the layer between "I need realistic data" and "I need it to feel like production".
403
+
404
+ ---
405
+
406
+ ## Requirements
407
+
408
+ - Python 3.11+
409
+ - pydantic >= 2.5
410
+ - faker >= 22.0
411
+
412
+ AI providers are optional extras — the core works with zero dependencies beyond pydantic and faker.
413
+
414
+ ---
415
+
416
+ ## License
417
+
418
+ MIT — see [LICENSE](LICENSE).
419
+
420
+ ---
421
+
422
+ ## Links
423
+
424
+ - **PyPI**: https://pypi.org/project/fixtureforge/
425
+ - **Repository**: https://github.com/Yaniv2809/fixtureforge
426
+ - **Issues**: https://github.com/Yaniv2809/fixtureforge/issues
427
+