Metadata-Version: 2.4
Name: astroapi-python
Version: 1.0.0
Summary: Modern Python client for Astrology API v3
Project-URL: Homepage, https://github.com/astro-api/astroapi-python
Project-URL: Documentation, https://github.com/astro-api/astroapi-python#readme
Project-URL: Repository, https://github.com/astro-api/astroapi-python
Project-URL: Issues, https://github.com/astro-api/astroapi-python/issues
Author-email: Procoders <info@procoders.tech>
License: MIT
License-File: LICENSE
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27.0
Requires-Dist: pydantic>=2.0.0
Provides-Extra: dev
Requires-Dist: mypy>=1.9.0; extra == 'dev'
Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
Requires-Dist: pytest-httpx>=0.30.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: ruff>=0.3.0; extra == 'dev'
Description-Content-Type: text/markdown

# Astrology API Python Client
[![Get API Key](https://img.shields.io/badge/Get%20API%20Key-6C63FF?style=for-the-badge&logoColor=white)](https://dashboard.astrology-api.io/)
[![API Documentation](https://img.shields.io/badge/API%20Documentation-FCC624?style=for-the-badge&logoColor=black)](https://api.astrology-api.io/rapidoc)
[![Postman Collection](https://img.shields.io/badge/Postman%20Collection-FF6C37?style=for-the-badge&logo=postman&logoColor=white)](https://api.astrology-api.io/best-astrology-api-postman.json)

[![PyPI version](https://badge.fury.io/py/astroapi-python.svg)](https://badge.fury.io/py/astroapi-python)
[![Python](https://img.shields.io/pypi/pyversions/astroapi-python.svg)](https://pypi.org/project/astroapi-python/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![CI](https://github.com/astro-api/astroapi-python/workflows/CI/badge.svg)](https://github.com/astro-api/astroapi-python/actions)
[![codecov](https://codecov.io/gh/astro-api/astroapi-python/branch/main/graph/badge.svg)](https://codecov.io/gh/astro-api/astroapi-python)

Modern Python client library for the [Astrology API v3](https://api.astrology-api.io). Get planetary positions, generate charts, analyze transits, and more with a fully typed, developer-friendly interface.

## Features

- 🌟 **Full API Coverage**: Access all 150+ endpoints across 16 category clients
- 🔒 **Type-Safe**: Complete type hints with Pydantic models for validation
- ♻️ **Automatic Retry**: Built-in retry logic for transient failures
- 🐍 **Modern Python**: Requires Python 3.10+ with contemporary type annotations
- ✅ **100% Test Coverage**: Comprehensive test suite with full coverage
- 🚀 **Zero Dependencies**: Only httpx and pydantic as runtime dependencies
- 📖 **Rich Documentation**: Extensive docstrings and examples

## Installation

```bash
pip install astroapi-python
```

## Quick Start

```python
from astroapi import AstrologyClient, Subject, BirthData

# Initialize client (reads ASTROLOGY_API_KEY from environment)
client = AstrologyClient()

# Or provide API key directly
from astroapi.types import AstrologyClientConfig
config = AstrologyClientConfig(api_key="your_api_key_here")
client = AstrologyClient(config)

# Create a subject
subject = Subject(
    name="John Doe",
    birth_data=BirthData(
        year=1990,
        month=1,
        day=1,
        hour=12,
        minute=0,
        latitude=40.7128,
        longitude=-74.0060,
        timezone="America/New_York"
    )
)

# Get planetary positions
from astroapi.types import PlanetaryPositionsRequest
positions_request = PlanetaryPositionsRequest(
    year=1990, month=1, day=1, hour=12, minute=0,
    latitude=40.7128, longitude=-74.0060
)
positions = client.data.get_positions(positions_request)

# Generate natal chart
from astroapi.types import NatalChartRequest
chart_request = NatalChartRequest(subject=subject)
chart = client.charts.get_natal_chart(chart_request)

# Get daily horoscope
from astroapi.types import PersonalizedHoroscopeRequest
horoscope_request = PersonalizedHoroscopeRequest(
    subject=subject,
    horoscope_type="daily"
)
horoscope = client.horoscope.get_personal_daily_horoscope(horoscope_request)
```

## Configuration

### Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `ASTROLOGY_API_KEY` | Your API key | Required |
| `ASTROLOGY_API_BASE_URL` | Custom base URL | `https://api.astrology-api.io` |
| `ASTROLOGY_DEBUG` | Enable debug logging | `False` |

### Programmatic Configuration

```python
from astroapi import AstrologyClient
from astroapi.types import AstrologyClientConfig, RetryConfig

config = AstrologyClientConfig(
    api_key="your_api_key",
    base_url="https://api.astrology-api.io",
    timeout=10.0,  # seconds
    retry=RetryConfig(
        attempts=3,
        delay_ms=250,
        retry_status_codes=[408, 429, 500, 502, 503, 504]
    ),
    debug=True,
    logger=lambda msg, details: print(f"[LOG] {msg}: {details}")
)

client = AstrologyClient(config)
```

### Context Manager

```python
from astroapi import AstrologyClient

# Automatically cleanup resources
with AstrologyClient() as client:
    positions = client.data.get_positions(request)
    # Client is automatically closed when exiting the context
```

## Category Clients

The library organizes endpoints into 16 category clients:

| Client | Description | Key Methods |
|--------|-------------|-------------|
| **data** | Planetary positions, houses, aspects | `get_positions()`, `get_houses()`, `get_aspects()` |
| **charts** | Natal, synastry, composite, transit charts | `get_natal_chart()`, `get_synastry_chart()` |
| **horoscope** | Personalized and sun sign horoscopes | `get_personal_daily_horoscope()`, `get_sign_weekly_horoscope()` |
| **analysis** | Interpretations and reports | `get_natal_report()`, `get_synastry_report()` |
| **glossary** | Reference data (cities, planets, signs) | `get_cities()`, `get_planets()`, `get_zodiac_signs()` |
| **chinese** | BaZi and Chinese zodiac | `get_bazi()`, `get_zodiac_sign()` |
| **numerology** | Numerology calculations | `get_life_path_number()`, `get_expression_number()` |
| **tarot** | Tarot card draws and readings | `draw_cards()`, `get_reading()` |
| **eclipses** | Solar and lunar eclipses | `get_eclipses()`, `get_solar_eclipses()` |
| **lunar** | Moon phases and void of course | `get_moon_phase()`, `get_void_of_course()` |
| **astrocartography** | Location mapping | `get_lines()`, `get_relocated_chart()` |
| **traditional** | Dignities and receptions | `get_dignities()`, `get_receptions()` |
| **fixed_stars** | Fixed star positions | `get_positions()`, `get_conjunctions()` |
| **insights** | Specialized insights | `relationship.*`, `pet.*`, `wellness.*` |
| **svg** | SVG chart generation | `get_natal_chart_svg()`, `get_synastry_chart_svg()` |
| **enhanced** | Advanced calculations | `get_enhanced_natal_chart()`, `get_patterns()` |

## Examples

### Synastry Chart

```python
from astroapi import AstrologyClient, Subject, BirthData
from astroapi.types import SynastryChartRequest

client = AstrologyClient()

person1 = Subject(
    name="Alice",
    birth_data=BirthData(year=1985, month=3, day=15, hour=10, minute=30)
)

person2 = Subject(
    name="Bob",
    birth_data=BirthData(year=1987, month=7, day=22, hour=14, minute=45)
)

request = SynastryChartRequest(subject1=person1, subject2=person2)
synastry = client.charts.get_synastry_chart(request)

print(f"Inter-aspects: {len(synastry.inter_aspects)}")
```

### Transit Analysis

```python
from astroapi.types import TransitChartRequest, DateTimeLocation

natal_subject = Subject(birth_data=BirthData(year=1990, month=1, day=1))

transit_time = DateTimeLocation(
    year=2024, month=6, day=15, hour=12, minute=0,
    latitude=40.7128, longitude=-74.0060
)

request = TransitChartRequest(
    natal_subject=natal_subject,
    transit_datetime=transit_time
)

transits = client.charts.get_transit_chart(request)
```

### BaZi (Chinese Astrology)

```python
from astroapi.types import BaZiRequest

request = BaZiRequest(
    subject=subject,
    language="en"
)

bazi = client.chinese.get_bazi(request)
print(f"Day Master: {bazi.day_master}")
print(f"Year Pillar: {bazi.year_pillar.heavenly_stem} {bazi.year_pillar.earthly_branch}")
```

### Relationship Insights

```python
from astroapi.types import RelationshipInsightRequest

request = RelationshipInsightRequest(
    subject1=person1,
    subject2=person2,
    language="en"
)

compatibility = client.insights.relationship.get_compatibility(request)
print(f"Compatibility Score: {compatibility.scores}")
```

### SVG Chart Generation

```python
from astroapi.types import NatalChartRequest

request = NatalChartRequest(subject=subject)
svg_string = client.svg.get_natal_chart_svg(request)

# Save to file
with open("natal_chart.svg", "w") as f:
    f.write(svg_string)
```

## Error Handling

```python
from astroapi import AstrologyClient, AstrologyError
from astroapi.types import NatalChartRequest

client = AstrologyClient()

try:
    chart = client.charts.get_natal_chart(request)
except AstrologyError as e:
    print(f"Error: {e.message}")
    print(f"Status Code: {e.status_code}")
    print(f"Error Code: {e.code}")

    if e.is_client_error():
        print("Client error (4xx)")
    elif e.is_server_error():
        print("Server error (5xx)")
```

## Retry Logic

The client includes automatic retry for transient failures:

```python
from astroapi.types import AstrologyClientConfig, RetryConfig

config = AstrologyClientConfig(
    api_key="your_api_key",
    retry=RetryConfig(
        attempts=3,  # Retry up to 3 times
        delay_ms=250,  # Wait 250ms between retries
        retry_status_codes=[408, 429, 500, 502, 503, 504]
    )
)

client = AstrologyClient(config)
```

## Validation

All request parameters are validated before API calls:

```python
from astroapi import AstrologyError
from astroapi.types import BirthData

try:
    # Invalid year (out of range)
    birth_data = BirthData(year=1800, month=1, day=1)
except AstrologyError as e:
    print(e.message)  # "Year must be between 1900 and 2100, got 1800"
```

## Development

### Setup

```bash
# Clone repository
git clone https://github.com/astro-api/astroapi-python.git
cd astroapi-python

# Install dev dependencies
pip install -e ".[dev]"

# Install pre-commit hooks
pre-commit install
```

### Testing

The project has two test suites: **unit tests** (fast, mocked HTTP) and **integration tests** (real API calls or auto-mocked).

#### Unit Tests

```bash
# Run all unit tests with coverage (100% branch coverage enforced)
pytest

# Run a specific test file
pytest tests/unit/test_client.py

# Run a single test
pytest tests/unit/test_client.py::TestAstrologyClient::test_client_creation_with_config
```

#### Integration Tests

Integration tests verify every SDK endpoint against the real API. If the `ASTROLOGY_API_KEY` environment variable is set, tests call the live API. If not, the HTTP layer is automatically mocked so tests always pass regardless of API access.

```bash
# Mock mode (no API key required, runs in ~0.1s)
pytest tests/integration/ -m integration --no-cov

# Live mode (calls real API, runs in ~6 min)
ASTROLOGY_API_KEY=your_key pytest tests/integration/ -m integration --no-cov

# Run a single integration test file
pytest tests/integration/test_horoscope.py -m integration --no-cov
```

> Tests marked with `@pytest.mark.xfail` are endpoints known to be unavailable or returning unexpected formats in the current API version. In mock mode these show as `xpassed`; in live mode they show as `xfailed`.

### Linting and Formatting

```bash
# Lint code
ruff check src/ tests/

# Format code
ruff format src/ tests/

# Type check
mypy src/astroapi
```

## Versioning & Releases

This project uses [hatch-vcs](https://github.com/ofek/hatch-vcs) — the package version is derived automatically from git tags. There is no hardcoded version in the source code.

To release a new version:

```bash
git tag v1.2.0
git push origin v1.2.0
```

The [release workflow](.github/workflows/release.yml) will run CI checks, build the package with the correct version, publish it to PyPI, and create a GitHub Release.

## Contributing

Contributions are welcome! Please:

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes
4. Run tests and linting (`pytest && ruff check && mypy`)
5. Commit your changes (`git commit -m 'Add amazing feature'`)
6. Push to the branch (`git push origin feature/amazing-feature`)
7. Open a Pull Request

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Links

- **Documentation**: [API Documentation](https://api.astrology-api.io/docs)
- **PyPI**: [astroapi-python](https://pypi.org/project/astroapi-python/)
- **GitHub**: [astro-api/astroapi-python](https://github.com/astro-api/astroapi-python)
- **Issues**: [Bug Reports & Feature Requests](https://github.com/astro-api/astroapi-python/issues)

## Support

For questions and support:

- 📧 Email: info@procoders.tech
- 🐛 Issues: [GitHub Issues](https://github.com/astro-api/astroapi-python/issues)
- 📖 API Docs: [https://api.astrology-api.io/docs](https://api.astrology-api.io/docs)

---

Made with ❤️ by [Procoders](https://procoders.tech)
