Metadata-Version: 2.4
Name: python-fragment
Version: 0.4.2
Summary: Reverse engineered Fragment API that mimics the official webclient
Project-URL: Homepage, https://github.com/ren3104/python-fragment
Project-URL: Repository, https://github.com/ren3104/python-fragment
Project-URL: Bug Tracker, https://github.com/ren3104/python-fragment/issues
Author-email: ren3104 <2ren3104@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: fragment,telegram,ton
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: aiohttp>=3.3.0
Requires-Dist: requests
Requires-Dist: selectolax[cython]!=0.4.8,>=0.3.17
Requires-Dist: typing-extensions>=4.0.0; python_version < '3.11'
Provides-Extra: dev
Requires-Dist: pre-commit; extra == 'dev'
Requires-Dist: unasyncd==0.10.0; extra == 'dev'
Description-Content-Type: text/markdown

# python-fragment
<p align="center">
  <a href="https://github.com/ren3104/python-fragment/blob/main/LICENSE"><img src="https://img.shields.io/github/license/ren3104/python-fragment" alt="GitHub license"></a>
  <a href="https://pypi.org/project/python-fragment"><img src="https://img.shields.io/pypi/v/python-fragment?color=blue" alt="PyPi package version"></a>
  <a href="https://pypi.org/project/python-fragment"><img src="https://img.shields.io/pypi/pyversions/python-fragment.svg" alt="Supported python versions"></a>
</p>

An unofficial Python library for interacting with [Fragment](https://fragment.com) (Python 3.9+). The library scrapes Fragment's HTML pages and exposes a clean, fully typed interface.

## Features
- Both **synchronous** and **asynchronous** clients
- **Fast HTML parsing** powered by [selectolax](https://github.com/rushter/selectolax)
- **Fully typed** - all returned data is described with `TypedDict` schemas
- **Automatic retries** on network failures with exponential back-off

## Installation
```shell
pip install python-fragment
```

## Quick Start

### Async client
```python
import asyncio
import fragment

async def main():
  async with fragment.AsyncClient() as client:
    # Search usernames currently on auction
    usernames = await client.search_usernames(
      query="crypto",
      filter="auction",
      sort="price_desc"
    )
    for u in usernames[:3]:
      print(u)
      # {
      #   'username': 'crypto',
      #   'status': 'auction',
      #   'value': 15000.0,
      #   'datetime': '2024-08-01T12:00:00+00:00',
      #   'is_resale': False
      # }

asyncio.run(main())
```

### Sync client
```python
import fragment

with fragment.Client() as client:
  # Detailed info for a specific username
  info = client.username_info("durov")
  print(info["status"])
  print(info["bid_history"])
  print(info["ownership_history"])
```

## Proxy Usage

A proxy can be set for the entire client session or overridden per individual request. The interface is identical for both `AsyncClient` and `Client`.

### Client-level proxy

```python
import fragment

PROXY = "http://{user}:{password}@{host}:{port}"

# Async
async with fragment.AsyncClient(proxy=PROXY) as client:
  print(await client.username_info("crypto"))

# Sync
with fragment.Client(proxy=PROXY) as client:
  print(client.username_info("crypto"))
```

### Per-request proxy

```python
import fragment

PROXY = "http://{user}:{password}@{host}:{port}"

# Async
async with fragment.AsyncClient() as client:
  print(await client.username_info("crypto", proxy=PROXY))

# Sync
with fragment.Client() as client:
  print(client.username_info("crypto", proxy=PROXY))
```

## Error Handling
```python
import fragment
from fragment.errors import FragmentHTTPError, ParserError


async with fragment.AsyncClient() as client:
  try:
    info = await client.username_info("someusername")
  except FragmentHTTPError as e:
    print(f"HTTP error: {e}")
  except ParserError as e:
    print(f"Failed to parse Fragment HTML: {e}")
```

**`FragmentHTTPError`** - raised when the server returns a non-OK response.

**`ParserError`** - raised when the Fragment page structure has changed and data cannot be extracted.
