Metadata-Version: 2.4
Name: proxywhirl
Version: 0.3.3
Summary: Advanced Python proxy rotation library with auto-fetching, validation, and persistence
Author-email: Wyatt Walsh <wyattowalsh@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/wyattowalsh/proxywhirl
Project-URL: Documentation, https://www.proxywhirl.com/docs/
Project-URL: Repository, https://github.com/wyattowalsh/proxywhirl
Project-URL: Issues, https://github.com/wyattowalsh/proxywhirl/issues
Keywords: proxy,rotation,http,socks,async,fetcher,validator
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: Proxy Servers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx[socks]>=0.25.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: pydantic-settings>=2.0.0
Requires-Dist: tenacity>=8.2.0
Requires-Dist: loguru>=0.7.0
Requires-Dist: respx>=0.22.0
Requires-Dist: beautifulsoup4>=4.14.2
Requires-Dist: cryptography>=44.0.1
Requires-Dist: sqlmodel>=0.0.27
Requires-Dist: aiosqlite>=0.21.0
Requires-Dist: greenlet>=3.2.4
Requires-Dist: typer>=0.9.0
Requires-Dist: rich>=13.0.0
Requires-Dist: filelock>=3.12.0
Requires-Dist: platformdirs>=3.0.0
Requires-Dist: tomli-w>=1.0.0
Requires-Dist: tomli>=2.3.0
Requires-Dist: fastapi>=0.100.0
Requires-Dist: uvicorn[standard]>=0.24.0
Requires-Dist: slowapi>=0.1.9
Requires-Dist: python-multipart>=0.0.18
Requires-Dist: portalocker>=2.8.0
Requires-Dist: pyyaml>=6.0.0
Requires-Dist: prometheus-client>=0.19.0
Requires-Dist: httpx-socks>=0.11.0
Requires-Dist: playwright>=1.55.0
Requires-Dist: geoip2>=5.1.0
Requires-Dist: aiofiles>=25.1.0
Requires-Dist: pyrate-limiter>=3.9.0
Requires-Dist: alembic>=1.16.5
Requires-Dist: starlette>=0.49.1
Requires-Dist: aiorwlock>=1.4.0
Requires-Dist: readerwriterlock>=1.0.9
Requires-Dist: aiobreaker>=1.2.0
Provides-Extra: storage
Requires-Dist: sqlmodel>=0.0.14; extra == "storage"
Provides-Extra: security
Requires-Dist: cryptography>=41.0.0; extra == "security"
Provides-Extra: js
Requires-Dist: playwright>=1.40.0; extra == "js"
Provides-Extra: analytics
Requires-Dist: pandas>=2.0.0; extra == "analytics"
Requires-Dist: numpy>=1.24.0; extra == "analytics"
Requires-Dist: scikit-learn>=1.3.0; extra == "analytics"
Provides-Extra: mcp
Requires-Dist: fastmcp>=0.1.0; python_version >= "3.10" and extra == "mcp"
Provides-Extra: all
Requires-Dist: sqlmodel>=0.0.14; extra == "all"
Requires-Dist: cryptography>=41.0.0; extra == "all"
Requires-Dist: playwright>=1.40.0; extra == "all"
Requires-Dist: pandas>=2.0.0; extra == "all"
Requires-Dist: numpy>=1.24.0; extra == "all"
Requires-Dist: scikit-learn>=1.3.0; extra == "all"
Requires-Dist: fastmcp>=0.1.0; python_version >= "3.10" and extra == "all"
Provides-Extra: docs
Requires-Dist: sphinx>=7.4.0; extra == "docs"
Requires-Dist: myst-parser>=2.0.0; extra == "docs"
Requires-Dist: shibuya>=2024.4.0; extra == "docs"
Requires-Dist: sphinx-copybutton>=0.5.2; extra == "docs"
Requires-Dist: sphinx-design>=0.6.0; extra == "docs"
Requires-Dist: sphinxcontrib-mermaid>=0.9.2; extra == "docs"
Requires-Dist: sphinx-docsearch>=0.2.0b3; extra == "docs"
Requires-Dist: linkify-it-py>=2.0.2; extra == "docs"
Requires-Dist: sphinx-autoapi>=3.6.1; extra == "docs"
Provides-Extra: dev
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
Requires-Dist: pytest-xdist>=3.5.0; extra == "dev"
Requires-Dist: pytest-mock>=3.12.0; extra == "dev"
Requires-Dist: pytest-timeout>=2.2.0; extra == "dev"
Requires-Dist: pytest-benchmark>=4.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
Requires-Dist: pytest-rerunfailures>=13.0; extra == "dev"
Requires-Dist: pytest-watcher>=0.4.0; extra == "dev"
Requires-Dist: pytest-sugar>=1.0.0; extra == "dev"
Requires-Dist: pytest-pretty>=1.2.0; extra == "dev"
Requires-Dist: pytest-html>=4.1.0; extra == "dev"
Requires-Dist: pytest-icdiff>=0.9; extra == "dev"
Requires-Dist: pytest-memray>=1.6.0; extra == "dev"
Requires-Dist: hypothesis>=6.100.0; extra == "dev"
Requires-Dist: syrupy>=4.6.0; extra == "dev"
Requires-Dist: respx>=0.22.0; extra == "dev"
Requires-Dist: polyfactory>=2.15.0; extra == "dev"
Requires-Dist: faker>=24.0.0; extra == "dev"
Requires-Dist: ruff>=0.3.0; extra == "dev"
Requires-Dist: ty>=0.0.7; extra == "dev"
Dynamic: license-file

<div align="center">

<!-- Hero -->
<picture>
  <source media="(prefers-color-scheme: dark)" srcset="docs/assets/whirl.svg">
  <source media="(prefers-color-scheme: light)" srcset="docs/assets/whirl.svg">
  <img src="docs/assets/whirl.svg" alt="ProxyWhirl" width="700"/>
</picture>

<br/><br/>

<!-- One-liner -->
<h3>🌀 Intelligent proxy rotation that just works</h3>

<br/>

<!-- Badges row 1: Key stats -->
[![PyPI](https://img.shields.io/pypi/v/proxywhirl?style=for-the-badge&logo=pypi&logoColor=white&color=3b82f6&labelColor=1e293b)](https://pypi.org/project/proxywhirl/)
&nbsp;
[![Downloads](https://img.shields.io/pypi/dm/proxywhirl?style=for-the-badge&logo=download&logoColor=white&color=22c55e&labelColor=1e293b)](https://pypi.org/project/proxywhirl/)
&nbsp;
[![Python](https://img.shields.io/badge/3.9+-a855f7?style=for-the-badge&logo=python&logoColor=white&labelColor=1e293b)](https://python.org)

<!-- Live stats dashboard -->
<br/>
<img src="docs/assets/stats-animated.svg" alt="Stats" width="700"/>
<br/><br/>

<!-- Navigation pills -->
[<kbd> <br> 📖 Docs <br> </kbd>](https://www.proxywhirl.com/docs/)&nbsp;&nbsp;
[<kbd> <br> 🚀 Examples <br> </kbd>](examples.ipynb)&nbsp;&nbsp;
[<kbd> <br> 💬 Discussions <br> </kbd>](https://github.com/wyattowalsh/proxywhirl/discussions)

</div>

<br/>

---

<br/>

## ⚡ 30-Second Setup

```bash
pip install proxywhirl
```

```python
from proxywhirl import ProxyWhirl

rotator = ProxyWhirl(proxies=["http://p1:8080", "http://p2:8080"])
response = rotator.get("https://api.example.com/data")
# Dead proxies auto-ejected ✓ | Slow ones deprioritized ✓ | Fast ones favored ✓
```

<br/>

---

<br/>

<div align="center">

## 🎯 Why ProxyWhirl?

<table>
<tr>
<td align="center" width="25%">
<br/>
<img src="https://api.iconify.design/carbon:rotate-360.svg?color=%2306b6d4" width="48"/>
<br/><br/>
<strong>9 Strategies</strong>
<br/>
<sub>Round-robin, weighted, geo-targeted, performance-based & more</sub>
<br/><br/>
</td>
<td align="center" width="25%">
<br/>
<img src="https://api.iconify.design/carbon:cloud-download.svg?color=%238b5cf6" width="48"/>
<br/><br/>
<strong>114 Sources</strong>
<br/>
<sub>Auto-fetch from built-in providers with validation</sub>
<br/><br/>
</td>
<td align="center" width="25%">
<br/>
<img src="https://api.iconify.design/carbon:health-cross.svg?color=%2322c55e" width="48"/>
<br/><br/>
<strong>Self-Healing</strong>
<br/>
<sub>Auto-eject dead proxies, circuit breakers, recovery</sub>
<br/><br/>
</td>
<td align="center" width="25%">
<br/>
<img src="https://api.iconify.design/carbon:lightning.svg?color=%23f97316" width="48"/>
<br/><br/>
<strong>Blazing Fast</strong>
<br/>
<sub>Async-first, &lt;3μs selection, zero blocking</sub>
<br/><br/>
</td>
</tr>
</table>

</div>

<br/>

---

<br/>

## 🔄 Rotation Strategies

```python
# Switch strategies on the fly
rotator = ProxyWhirl(strategy="performance-based")
rotator.set_strategy("geo-targeted", preferences={"US": [...], "EU": [...]})
```

| Strategy | Best For |
|:---------|:---------|
| `round-robin` | Even distribution |
| `random` | Unpredictable patterns |
| `weighted` | Favor reliable proxies |
| `least-used` | Even load balance |
| `performance-based` | Lowest latency |
| `geo-targeted` | Regional routing |
| `session-persistence` | Sticky sessions |
| `cost-aware` | Budget optimization |
| `composite` | Custom pipelines |

<br/>

---

<br/>

## 🎣 Auto-Fetch Proxies

```python
from proxywhirl import ProxyFetcher

# Grab 300+ validated proxies in seconds
proxies = await ProxyFetcher().fetch_all(validate=True)
```

<br/>

---

<br/>

## 🖥️ Multiple Interfaces

<table>
<tr>
<td width="33%">

**Python API**
```python
rotator.get(url)
rotator.post(url, json=data)
async with AsyncProxyWhirl() as async_rotator:
    await async_rotator.get(url)
```

</td>
<td width="33%">

**CLI**
```bash
proxywhirl fetch
proxywhirl pool list
proxywhirl health
```

</td>
<td width="33%">

**REST API**
```bash
docker-compose up -d
curl localhost:8000/api/v1/proxies
```

</td>
</tr>
</table>

<br/>

---

<br/>

<div align="center">

## 📚 Learn More

[<kbd> <br> &nbsp;&nbsp;📖 Full Documentation&nbsp;&nbsp; <br> </kbd>](https://www.proxywhirl.com/docs/)&nbsp;&nbsp;&nbsp;
[<kbd> <br> &nbsp;&nbsp;🎯 Strategy Guide&nbsp;&nbsp; <br> </kbd>](docs/source/guides/advanced-strategies.md)&nbsp;&nbsp;&nbsp;
[<kbd> <br> &nbsp;&nbsp;🤖 MCP Server&nbsp;&nbsp; <br> </kbd>](docs/source/guides/mcp-server.md)&nbsp;&nbsp;&nbsp;
[<kbd> <br> &nbsp;&nbsp;📓 Examples&nbsp;&nbsp; <br> </kbd>](examples.ipynb)

<br/><br/>

---

<br/>

[![Star History](https://api.star-history.com/svg?repos=wyattowalsh/proxywhirl&type=Date&theme=dark)](https://star-history.com/#wyattowalsh/proxywhirl&Date)

<br/>

**[GitHub](https://github.com/wyattowalsh/proxywhirl)** · **[Issues](https://github.com/wyattowalsh/proxywhirl/issues)** · **[MIT License](LICENSE)**

<sub>Built with ❤️ for ethical web automation</sub>

</div>
