paulosoft-api-github 0.1.2
Dynamic GitHub REST API client with optional typed helpers
Requires Python: >=3.8

# Dynamic GitHub API Client

Minimal Python client for GitHub REST API with dynamic endpoints.

## Install

```bash
pip install paulosoft-api-github
```

## Quick Start

```python
import time
import requests

from api_github import GitHubClient

client = GitHubClient(token="...")

client.api.user.repos(per_page=100)
client.api.search.repositories(q="backend", per_page=50)
client.call("repos/octocat/Hello-World/issues", params={"per_page": 50})
catalogo = client.webservices_list()
```

## Authentication

```python
from api_github import GitHubClient

client = GitHubClient(token="YOUR_GITHUB_TOKEN")
```

## Configuration

```python
from api_github import GitHubClient

client = GitHubClient(
    token="YOUR_GITHUB_TOKEN",
    base_url="https://api.github.com",
    timeout=30,
    raise_for_status=True,
)
```

## List User Repositories with Token (Dynamic API)

```python
from api_github import GitHubClient

token = "YOUR_GITHUB_TOKEN"
client = GitHubClient(token=token)

repos = client.api.users.octocat.repos.paginate(per_page=100)
for repo in repos:
    print(repo["full_name"])
```

## Pagination

GitHub paginates list endpoints and advertises the next page via the `Link` header.
This library provides two helpers:

- Generator: `client.iter_paginated("users/octocat/repos", params={...})`
- Convenience: `.paginate(...)` on any dynamic API node

```python
from api_github import GitHubClient

client = GitHubClient(token="YOUR_GITHUB_TOKEN")

# Convenience: follow `Link: rel="next"` automatically
repos = client.api.users["octocat"].repos.paginate(per_page=100)
print(len(repos))

# Generator version (useful for streaming large result sets)
for repo in client.iter_paginated("users/octocat/repos", params={"per_page": 100}):
    print(repo["full_name"])
```

## Rate limit helper (optional)

```python
import time
import requests


def call_with_rate_limit(func):
    while True:
        try:
            return func()
        except requests.HTTPError as exc:
            response = exc.response
            if response is None:
                raise
            if response.status_code in (403, 429):
                reset = response.headers.get("X-RateLimit-Reset")
                if reset:
                    sleep_for = max(0, int(reset) - int(time.time()) + 1)
                else:
                    sleep_for = 5
                time.sleep(sleep_for)
                continue
            raise


def iter_pages(fetch_page, max_pages=10):
    page = 1
    while page <= max_pages:
        items = call_with_rate_limit(lambda: fetch_page(page))
        if not items:
            break
        for item in items:
            yield item
        page += 1
        time.sleep(0.2)
```

Notes:
- Adjust `max_pages` based on your expected repo count.
- For more accurate pagination, you can parse the `Link` header from responses.

## List Organization Repositories with Token (Dynamic API)

```python
from api_github import GitHubClient

token = "YOUR_GITHUB_TOKEN"
client = GitHubClient(token=token)

for repo in iter_pages(
    lambda page: client.api.orgs.myorg.repos(per_page=100, page=page, type="all")
):
    print(repo["full_name"])
```

## List Authenticated User Repositories (Dynamic API)

```python
from api_github import GitHubClient

token = "YOUR_GITHUB_TOKEN"
client = GitHubClient(token=token)

for repo in iter_pages(
    lambda page: client.api.user.repos(
        per_page=100,
        page=page,
        visibility="all",
        affiliation="owner,collaborator,organization_member",
    )
):
    print(repo["full_name"])
```

## Typed Helpers (Optional)

```python
from api_github import TypedGitHubClient

client = TypedGitHubClient(token="...")

client.repos.list(per_page=50)
client.repos.get("octocat", "Hello-World")
client.repos.list_issues("octocat", "Hello-World", state="open")
client.repos.create_issue("octocat", "Hello-World", title="Bug", body="Details")

client.users.get("octocat")
client.users.list(per_page=50)

client.issues.get("octocat", "Hello-World", 1)
client.issues.create_comment("octocat", "Hello-World", 1, body="Thanks!")
```

## Dynamic Format

```python
client.api.<service>.<action>(**params)
```

Examples:

```python
client.api.search.repositories(q="backend", per_page=100)
client.api.orgs.myorg.repos(per_page=100)
client.api.users.octocat.repos(per_page=100)
client.api.repos["octocat"]["Hello-World"].issues(per_page=50)
```

Notes:
- Use bracket access for path segments with hyphens or non-identifier names.
- The dynamic chain builds the full path and sends params as query or JSON.

## POST Calls

For endpoints that require POST (or other verbs), pass `http_method`:

```python
client.api.user.repos(
    http_method="POST",
    name="my-new-repo",
    private=True,
)
```

## Raw Endpoint

```python
client.call("search/repositories", params={"q": "backend", "per_page": 100})
```

## Error Handling

```python
from api_github import GitHubClient

client = GitHubClient(token="YOUR_GITHUB_TOKEN", raise_for_status=False)
response = client.call("rate_limit")
```

## Webservices List

```python
catalogo = client.webservices_list()
```

## Notes

- Uses `requests` under the hood.
- Non-GET methods send params as JSON body.
- If the response is JSON, it returns parsed JSON, otherwise text.