Metadata-Version: 2.4
Name: shouldpy
Version: 2.0.0
Summary: AI-driven test assertion decorator for natural language testing
Author-email: zx <zhixiang.xzx@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/zhixiangxue/should-ai
Project-URL: Repository, https://github.com/zhixiangxue/should-ai
Project-URL: Documentation, https://github.com/zhixiangxue/should-ai#readme
Project-URL: Bug Tracker, https://github.com/zhixiangxue/should-ai/issues
Project-URL: Changelog, https://github.com/zhixiangxue/should-ai/blob/main/CHANGELOG.md
Keywords: testing,ai,assertion,decorator,llm,natural-language
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: chakpy>=0.3.6
Provides-Extra: dev
Requires-Dist: pytest>=6.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: python-dotenv>=1.0; extra == "dev"
Dynamic: license-file

# shouldpy
> *"Write your tests the way you always have. Let AI run one final check."*

[![PyPI](https://img.shields.io/pypi/v/shouldpy.svg)](https://pypi.org/project/shouldpy/)
[![Python](https://img.shields.io/pypi/pyversions/shouldpy.svg)](https://pypi.org/project/shouldpy/)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Downloads](https://img.shields.io/pypi/dm/shouldpy.svg)](https://pypi.org/project/shouldpy/)

## What is this?

A ~200-line decorator that wraps any test function with a natural-language assertion.
After the function runs, an LLM inspects all logs, print output, and return values to check whether the condition holds:

- **PASS** — passes silently
- **FAIL: reason** — raises `AssertionError`

> Does not replace `assert`. Does not force a `return`. Does not restructure your test cases.
> Just one decorator that lets an LLM double-check your work.

---

## Install

```bash
pip install shouldpy
```

---

## Quick start

```python
import should

should.use("openai/gpt-4o-mini", api_key="sk-...")

@should("The log should clearly indicate the order was created successfully")
def test_create_order():
    ...  # your existing test code, unchanged
    logging.info("Order created successfully")
    assert resp.status_code == 200
```

Run `pytest` →
- Your existing `assert` checks the status code
- AI additionally checks whether logs/output/return value satisfy the natural-language condition

Both must pass.

---

## Supported models

`should.use()` accepts any model URI supported by [chak](https://github.com/zhixiangxue/chak-ai):

```python
should.use("openai/gpt-4o-mini",        api_key="...")
should.use("anthropic/claude-3-5-sonnet", api_key="...")
should.use("deepseek/deepseek-chat",    api_key="...")
should.use("ollama/llama3",             base_url="http://localhost:11434")
```

---

## Async support

```python
@should("Async minor registration must be rejected")
async def test_async_register_minor():
    result = await async_register_user("Alice", 15)
    return result
```

Works out of the box — async functions get a fully async AI call.

---

## Notes

1. **Bring your own API key.** shouldpy calls the LLM you configure; it never manages credentials itself.
2. **Non-deterministic.** Best suited for exploratory testing, scripting, local spot-checks, and pre-review sanity passes. Do not use as a primary assertion in high-frequency CI pipelines.
3. **Every call hits the model** — slow, costs tokens, and may expose data. For sensitive workloads, use a local model (e.g. Ollama).

---

## Feedback

[https://github.com/zhixiangxue/should-ai](https://github.com/zhixiangxue/should-ai)

Issues, PRs, and stars are welcome.

---

## License

MIT © 2025 zx
