wishful 0.2.1__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.
@@ -0,0 +1,401 @@
1
+ Metadata-Version: 2.3
2
+ Name: wishful
3
+ Version: 0.2.1
4
+ Summary: Wishful thinking for Python
5
+ Author: Pyro
6
+ Author-email: Pyro <pyros.sd.models@gmail.com>
7
+ Requires-Dist: litellm>=1.40.0
8
+ Requires-Dist: rich>=13.7.0
9
+ Requires-Dist: python-dotenv>=1.0.1
10
+ Requires-Dist: pydantic>=2.12.4
11
+ Requires-Dist: loguru>=0.7.3
12
+ Requires-Python: >=3.12
13
+ Description-Content-Type: text/markdown
14
+
15
+
16
+
17
+ <p align="center">
18
+ <img alt="Wishful Banner" src="docs-site/src/content/imgs/wishful_logo (5).jpg" width="800">
19
+ </p>
20
+
21
+ <p align="center">
22
+ <a href="https://badge.fury.io/py/wishful"><img src="https://badge.fury.io/py/wishful.svg" alt="PyPI version"></a>
23
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.12+-blue.svg" alt="Python 3.12+"></a>
24
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
25
+ <a href="https://github.com/pyros-projects/wishful"><img src="https://img.shields.io/badge/tests-83%20passed-brightgreen.svg" alt="Tests"></a>
26
+ <a href="https://github.com/pyros-projects/wishful"><img src="https://img.shields.io/badge/coverage-80%25-green.svg" alt="Coverage"></a>
27
+ <a href="https://github.com/astral-sh/ruff"><img src="https://img.shields.io/badge/code%20style-ruff-000000.svg" alt="Code style: ruff"></a>
28
+ </p>
29
+
30
+ <p align="center">
31
+ <em>"Import your wildest dreams"</em>
32
+ </p>
33
+
34
+
35
+ Stop writing boilerplate. Start wishing for it instead.
36
+
37
+ **wishful** turns your wildest import dreams into reality. Just write the import you _wish_ existed, and an LLM conjures up the code on the spot. The first run? Pure magic. Every run after? Blazing fast, because it's cached like real Python.
38
+
39
+ Think of it as **wishful thinking, but for imports**. The kind that actually works.
40
+
41
+ ## ✨ Quick Wish
42
+
43
+ **1. Install the dream**
44
+
45
+ ```bash
46
+ pip install wishful
47
+ ```
48
+
49
+ **2. Set your API key** (any provider supported by [litellm](https://docs.litellm.ai/))
50
+
51
+ ```bash
52
+ export OPENAI_API_KEY=your_key_here
53
+ # or AZURE_API_KEY, ANTHROPIC_API_KEY, etc.
54
+ ```
55
+
56
+ **3. Import your wildest fantasies**
57
+
58
+ ```python
59
+ from wishful.static.text import extract_emails
60
+ from wishful.static.dates import to_yyyy_mm_dd
61
+
62
+ raw = "Contact us at team@example.com or sales@demo.dev"
63
+ print(extract_emails(raw)) # ['team@example.com', 'sales@demo.dev']
64
+ print(to_yyyy_mm_dd("31.12.2025")) # '2025-12-31'
65
+ ```
66
+
67
+ **What just happened?**
68
+
69
+ - **First import**: wishful waves its wand 🪄, asks the LLM to write `extract_emails` and `to_yyyy_mm_dd`, validates the code for safety, and caches it to `.wishful/text.py` and `.wishful/dates.py`.
70
+ - **Every subsequent run**: instant. Just regular Python imports. No latency, no drama, no API calls.
71
+
72
+ It's like having a junior dev who never sleeps and always delivers exactly what you asked for (well, _almost_ always).
73
+
74
+ > 💡 **Pro tip**: Use `wishful.static.*` for cached imports (recommended) or `wishful.dynamic.*` for runtime-aware regeneration. See [Static vs Dynamic](#-static-vs-dynamic-when-to-use-which) below.
75
+
76
+ ---
77
+
78
+ ## 🎯 Wishful Guidance: Help the AI Read Your Mind
79
+
80
+ Want better results? Drop hints. Literal comments. wishful reads the code _around_ your import and forwards that context to the LLM. It's like pair programming, but your partner is a disembodied intelligence with questionable opinions about semicolons.
81
+
82
+ ```python
83
+ # desired: parse standard nginx combined logs into list of dicts
84
+ from wishful.static.logs import parse_nginx_logs
85
+
86
+ records = parse_nginx_logs(Path("/var/log/nginx/access.log").read_text())
87
+ ```
88
+
89
+ ---
90
+
91
+ ## 🎨 Type Registry: Teach the AI Your Data Structures
92
+
93
+ Want the LLM to generate functions that return **properly structured data**? Register your types with `@wishful.type`:
94
+
95
+ ### Pydantic Models with Constraints
96
+
97
+ ```python
98
+ from pydantic import BaseModel, Field
99
+ import wishful
100
+
101
+ @wishful.type
102
+ class ProjectPlan(BaseModel):
103
+ """Project plan written by master yoda from star wars."""
104
+ project_brief: str
105
+ milestones: list[str] = Field(description="list of milestones", min_length=10)
106
+ budget: float = Field(gt=0, description="project budget in USD")
107
+
108
+ # Now the LLM knows about ProjectPlan and will respect Field constraints!
109
+ from wishful.static.pm import project_plan_generator
110
+
111
+ plan = project_plan_generator(idea="sudoku web app")
112
+ print(plan.milestones)
113
+ # ['Decide, you must, key features.', 'Wireframe, you will, the interface.', ...]
114
+ # ^ 10+ milestones in Yoda-speak because of the docstring! 🎭
115
+ ```
116
+
117
+ **What's happening here?**
118
+ - The `@wishful.type` decorator registers your Pydantic model
119
+ - The **docstring** influences the LLM's tone/style (Yoda-speak!)
120
+ - **Field constraints** (`min_length=10`, `gt=0`) are actually enforced
121
+ - Generated code uses your exact type definition
122
+
123
+ ### Dataclasses and TypedDict Too
124
+
125
+ ```python
126
+ from dataclasses import dataclass
127
+ from typing import TypedDict
128
+
129
+ @wishful.type(output_for="parse_user_data")
130
+ @dataclass
131
+ class UserProfile:
132
+ """User profile with name, email, and age."""
133
+ name: str
134
+ email: str
135
+ age: int
136
+
137
+ class ProductInfo(TypedDict):
138
+ """Product information."""
139
+ name: str
140
+ price: float
141
+ in_stock: bool
142
+
143
+ # Tell the LLM multiple functions use this type
144
+ wishful.type(ProductInfo, output_for=["parse_product", "create_product"])
145
+ ```
146
+
147
+ The LLM will generate functions that return instances of your registered types. It's like having an API contract, but the implementation writes itself. ✨
148
+
149
+ ---
150
+
151
+ ## 🔄 Static vs Dynamic: When to Use Which
152
+
153
+ wishful supports two import modes:
154
+
155
+ ### `wishful.static.*` — Cached & Consistent (Default)
156
+
157
+ ```python
158
+ from wishful.static.text import extract_emails
159
+ ```
160
+
161
+ - ✅ **Cached**: Generated once, reused forever
162
+ - ✅ **Fast**: No LLM calls after first import
163
+ - ✅ **Editable**: Tweak `.wishful/text.py` directly
164
+ - 👉 **Use for**: utilities, parsers, validators, anything stable
165
+
166
+ ### `wishful.dynamic.*` — Runtime-Aware & Fresh
167
+
168
+ ```python
169
+ # when importing as dynamic module all bets are off
170
+ import wishful.dynamic.content as magical_content
171
+
172
+ my_intro = magical_content.create_a_cosmic_horrorstory_intro()
173
+ ```
174
+
175
+ - 🔄 **Regenerates**: Fresh LLM call on every import
176
+ - 🎯 **Context-aware**: Captures runtime context each time
177
+ - 🎨 **Creative**: Different results on each run
178
+ - 👉 **Use for**: creative content, experiments, testing variations
179
+
180
+ **Note**: Dynamic imports always regenerate and never use the cache, even if a cached version exists. This ensures fresh, context-aware results every time.
181
+
182
+ ---
183
+
184
+ ## 🗄️ Cache Ops: Because Sometimes Wishes Need Revising
185
+
186
+ ```python
187
+ import wishful
188
+
189
+ # See what you've wished for
190
+ wishful.inspect_cache() # ['.wishful/text.py', '.wishful/dates.py']
191
+
192
+ # Regenerate a module
193
+ wishful.regenerate("wishful.static.text")
194
+
195
+ # Force fresh import (useful for dynamic imports in loops)
196
+ story = wishful.reimport('wishful.dynamic.story')
197
+
198
+ # Nuclear option: forget everything
199
+ wishful.clear_cache()
200
+ ```
201
+
202
+ **CLI**: `wishful inspect`, `wishful clear`, `wishful regen <module>`
203
+
204
+ The cache is just regular Python files in `.wishful/`. Want to tweak the generated code? Edit it directly. It's your wish, after all.
205
+
206
+ ---
207
+
208
+ ## ⚙️ Configuration: Fine-Tune Your Wishes
209
+
210
+ ```python
211
+ import wishful
212
+
213
+ wishful.configure(
214
+ model="gpt-4o-mini", # Switch models like changing channels
215
+ cache_dir="/tmp/.wishful", # Hide your wishes somewhere else
216
+ spinner=False, # Silence the "generating..." spinner
217
+ review=True, # Review code before it runs
218
+ context_radius=6, # Lines of context (default: 3)
219
+ allow_unsafe=False, # Keep safety rails ON (recommended)
220
+ )
221
+ ```
222
+
223
+ **Environment variables**: `WISHFUL_MODEL`, `WISHFUL_CACHE_DIR`, `WISHFUL_REVIEW`, `WISHFUL_DEBUG`, `WISHFUL_UNSAFE`, `WISHFUL_SPINNER`, `WISHFUL_MAX_TOKENS`, `WISHFUL_TEMPERATURE`, `WISHFUL_CONTEXT_RADIUS`
224
+
225
+ ---
226
+
227
+ ## 🛡️ Safety Rails: Wishful Isn't _That_ Reckless
228
+
229
+ Generated code gets AST-scanned to block dangerous patterns: forbidden imports (`os`, `subprocess`, `sys`), `eval()`/`exec()`, unsafe file operations, and system calls.
230
+
231
+ **Override at your own peril**: `WISHFUL_UNSAFE=1` or `allow_unsafe=True` turns off the guardrails.
232
+
233
+ ---
234
+
235
+ ## 🧪 Testing: Wishes Without Consequences
236
+
237
+ Need deterministic, offline behavior? Set `WISHFUL_FAKE_LLM=1` and wishful generates placeholder stubs instead of hitting the network. Perfect for CI, unit tests, or when your Wi-Fi is acting up.
238
+
239
+ ```bash
240
+ export WISHFUL_FAKE_LLM=1
241
+ python my_tests.py # No API calls, just predictable stubs
242
+ ```
243
+
244
+ ---
245
+
246
+ ## 🔮 How the Magic Actually Works
247
+
248
+ 1. **Import hook** intercepts `wishful.static.*` and `wishful.dynamic.*` imports
249
+ 2. **Cache check**: `static` imports load instantly if cached; `dynamic` always regenerates
250
+ 3. **Context discovery**: Captures nearby comments, code, and registered type schemas
251
+ 4. **LLM generation**: Generates code via `litellm` based on your import + context
252
+ 5. **Safety validation**: AST-parsed and checked for dangerous patterns
253
+ 6. **Execution**: Code is cached to `.wishful/`, compiled, and executed
254
+ 7. **Transparency**: Just plain Python files. Edit them. Commit them. They're yours.
255
+
256
+ It's import hooks meets LLMs meets type-aware code generation meets "why didn't this exist already?"
257
+
258
+ ---
259
+
260
+ ## 🎭 Fun with Wishful Thinking
261
+
262
+ ```python
263
+ # Cosmic horror stories? Just import it.
264
+ from wishful.static.story import cosmic_horror_intro
265
+
266
+ intro = cosmic_horror_intro(
267
+ setting="a deserted amusement park",
268
+ word_count_at_least=100
269
+ )
270
+ print(intro) # 🎢👻
271
+
272
+ # Math that writes itself
273
+ from wishful.static.numbers import primes_from_to, sum_list
274
+
275
+ total = sum_list(list=primes_from_to(1, 100))
276
+ print(total) # 1060
277
+
278
+ # Because who has time to write date parsers?
279
+ from wishful.static.dates import parse_fuzzy_date
280
+
281
+ print(parse_fuzzy_date("next Tuesday")) # Your guess is as good as mine
282
+
283
+ # Want different results each time? Use dynamic imports!
284
+ from wishful.dynamic.jokes import programming_joke
285
+
286
+ print(programming_joke()) # New joke on every import 🎲
287
+ ```
288
+
289
+ ---
290
+
291
+ ## 💻 Development: Working with This Repo
292
+
293
+ This project uses [uv](https://docs.astral.sh/uv/) for blazing-fast Python package management.
294
+
295
+ ### Setup
296
+
297
+ ```bash
298
+ # Install uv if needed
299
+ curl -LsSf https://astral.sh/uv/install.sh | sh
300
+
301
+ # Clone the repo
302
+ git clone https://github.com/pyros-projects/wishful.git
303
+ cd wishful
304
+
305
+ # Install dependencies (uv handles everything)
306
+ uv sync
307
+ ```
308
+
309
+ ### Running Tests
310
+
311
+ ```bash
312
+ # Run the full test suite
313
+ uv run pytest tests/ -v
314
+
315
+ # Run a specific test file
316
+ uv run pytest tests/test_import_hook.py -v
317
+
318
+ # Run with coverage
319
+ uv run pytest --cov=wishful tests/
320
+ ```
321
+
322
+ ### Running Examples
323
+
324
+ All examples support `WISHFUL_FAKE_LLM=1` for deterministic testing:
325
+
326
+ ```bash
327
+ # Run with fake LLM (no API calls)
328
+ WISHFUL_FAKE_LLM=1 uv run python examples/00_quick_start.py
329
+
330
+ # Run with real LLM (requires API keys)
331
+ uv run python examples/00_quick_start.py
332
+ ```
333
+
334
+ ### Adding Dependencies
335
+
336
+ ```bash
337
+ # Add a runtime dependency
338
+ uv add package-name
339
+
340
+ # Add a dev dependency
341
+ uv add --dev package-name
342
+
343
+ # Update all dependencies
344
+ uv lock --upgrade
345
+ ```
346
+
347
+ ### Project Structure
348
+
349
+ ```
350
+ wishful/
351
+ ├── src/wishful/ # Main package
352
+ │ ├── __init__.py # Public API
353
+ │ ├── __main__.py # CLI interface
354
+ │ ├── config.py # Configuration
355
+ │ ├── cache/ # Cache management
356
+ │ ├── core/ # Import hooks & discovery
357
+ │ ├── llm/ # LLM integration
358
+ │ ├── types/ # Type registry system
359
+ │ └── safety/ # Safety validation
360
+ ├── tests/ # Test suite (83 tests, 80% coverage)
361
+ ├── examples/ # Usage examples
362
+ │ ├── 07_typed_outputs.py # Type registry showcase
363
+ │ ├── 08_dynamic_vs_static.py # Static vs dynamic modes
364
+ │ └── 09_context_shenanigans.py # Context discovery
365
+ └── pyproject.toml # Project config
366
+ ```
367
+
368
+ ---
369
+
370
+ ## 🤔 FAQ (Frequently Asked Wishes)
371
+
372
+ **Q: Is this production-ready?**
373
+ A: Define "production." 🙃
374
+
375
+ **Q: Can I make the LLM follow a specific style?**
376
+ A: Yes! Use docstrings in `@wishful.type` decorated classes. Want Yoda-speak? Add `"""Written by master yoda from star wars."""` — the LLM will actually do it.
377
+
378
+ **Q: Do type hints and Pydantic constraints actually work?**
379
+ A: Surprisingly, yes! Field constraints like `min_length=10` or `gt=0` are serialized and sent to the LLM, which respects them.
380
+
381
+ **Q: What if the LLM generates bad code?**
382
+ A: That's what the cache is for. Check `.wishful/`, tweak it, commit it, and it's locked in.
383
+
384
+ **Q: Can I use this with OpenAI/Claude/local models?**
385
+ A: Yes! Built on `litellm`, so anything it supports works here.
386
+
387
+ **Q: What if I import something that doesn't make sense?**
388
+ A: The LLM will do its best. Results may vary. Hilarity may ensue.
389
+
390
+ **Q: Is this just lazy programming?**
391
+ A: It's not lazy. It's _efficient wishful thinking_. 😎
392
+
393
+ ---
394
+
395
+ ## 📜 License
396
+
397
+ MIT.
398
+
399
+ **Go forth and wish responsibly.** ✨
400
+
401
+ Your imports will never be the same.
@@ -0,0 +1,26 @@
1
+ wishful/__init__.py,sha256=SREPLh8rfv44gQJkpmJZRJKyouMP14_d6wPaSWyGaA0,3008
2
+ wishful/__main__.py,sha256=U6yainnjWX4aKbfEzvci4UdekpDV16G7wCp5PQu8O2A,1890
3
+ wishful/cache/__init__.py,sha256=dsgWLI7e9YM8DZQECymxF20BmeJVD-3j9BaTRTcEcyg,382
4
+ wishful/cache/manager.py,sha256=gDNkxZduR5FBOnHJBP4Blcb88-4FL9nnfDT7m-5sKh4,2166
5
+ wishful/config.py,sha256=bwbGZvNTK0wyOLTrkF63bEAa4dOLK_iWlgmY0d4RdRg,6317
6
+ wishful/core/__init__.py,sha256=25icSsv7zjbW_VjXTZd2EkvTN4mGc1dTwHs_cmAgWpY,168
7
+ wishful/core/discovery.py,sha256=rVQoiM2K9J1QGrsOF1BOnfQz-9qrLlDNd52axp5tK_k,8380
8
+ wishful/core/finder.py,sha256=nA-j-tPD6vvjDyyGvGXxgU8AFO5vYGoT5KvKiOEKIjs,2639
9
+ wishful/core/loader.py,sha256=ZKEelSVbbqZAF2YehgJ2kVOdxYUDoLgjcHzTgh9t1OI,10862
10
+ wishful/dynamic/__init__.py,sha256=JWQQAh-vv-b7NGRccX0fvm_BvNmn55evF2c6SnAU3t8,289
11
+ wishful/dynamic/__init__.pyi,sha256=HYTnwSv7lBLm9vLBDtOXmRoHKlPy8jDkTseO4pcz4BY,221
12
+ wishful/llm/__init__.py,sha256=c5Ij-Rj381r4jocCPcOxjSwrjuTTMrUBpZjGD69Mmt0,143
13
+ wishful/llm/client.py,sha256=tsrP7SWtPr3UjbVgaK5yGy3h2nO3C4UlZZiAAS5zHIk,3186
14
+ wishful/llm/prompts.py,sha256=gAjw6xo8QbuJFNfqKMwaJo28lY8n2Jhv0yTkhbwM4V4,2610
15
+ wishful/logging.py,sha256=qaGJ5AhLjdRxIdwxqWu0n0aICnrhUZndKyd4UVc_RI4,2207
16
+ wishful/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ wishful/safety/__init__.py,sha256=RgJ5ieL4H0GVvJ5zHDoSHnadOtmnXfLL8lNPZGOVJU8,139
18
+ wishful/safety/validator.py,sha256=-el8imnD-tfxlAeWuue3DqyLg1WghWXtLly2Tqpcyuo,3933
19
+ wishful/static/__init__.py,sha256=tdSIyrmrY2nyB5JBndK0Yv-VfnS79po31VOh-QuRhNc,253
20
+ wishful/static/__init__.pyi,sha256=dXwPiDeTWRUHhg7aYNj_NwbKI449Ifp3nT_nA1gTT2s,219
21
+ wishful/types/__init__.py,sha256=PPqeBPIOCugYD_hJhHr9VLmxAgqa2c42L8SRckTH0mw,390
22
+ wishful/types/registry.py,sha256=Z7I8WxQ9Y97JKNOs7yP_BWp_XcAMJaZ5KLnQdlVwLhg,13643
23
+ wishful/ui.py,sha256=lnhaAJhD7ep6kPs8aXe2gMXzH_N8yy0q8ZbuexBhxkI,665
24
+ wishful-0.2.1.dist-info/WHEEL,sha256=YUH1mBqsx8Dh2cQG2rlcuRYUhJddG9iClegy4IgnHik,79
25
+ wishful-0.2.1.dist-info/METADATA,sha256=-E3FOAyR91NJpKyzYprqEFd1_oeb6OQiUX2W0jRsYtc,13011
26
+ wishful-0.2.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.9.11
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any