Metadata-Version: 2.4
Name: whooing-py
Version: 0.2.1
Summary: Unofficial Python client library for the Whooing Developer API.
Project-URL: API-Documentation, https://whooing.com/api/docs
Project-URL: Changelog, https://github.com/flynnpark/whooing-py/blob/main/CHANGELOG.md
Project-URL: CLI-Documentation, https://github.com/flynnpark/whooing-py/blob/main/docs/CLI_USAGE.md
Project-URL: Issues, https://github.com/flynnpark/whooing-py/issues
Project-URL: Repository, https://github.com/flynnpark/whooing-py
Author: flynn
License-Expression: MIT
License-File: LICENSE
Keywords: accounting,api,client,finance,whooing
Classifier: Development Status :: 3 - Alpha
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 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: httpx<1,>=0.28
Requires-Dist: pydantic<3,>=2.10
Requires-Dist: python-dotenv<2,>=1.0
Requires-Dist: typer<1,>=0.12
Description-Content-Type: text/markdown

# whooing-py

[후잉가계부(Whooing)](https://whooing.com)의
[개발자 API](https://whooing.com/api/docs)를 위한 비공식 Python 클라이언트
라이브러리입니다.

## 상태

공식 문서 기준의 1차 클라이언트 구현이 들어간 상태입니다. 동기/비동기 클라이언트, API
key/Bearer/OAuth 1.0a 인증 헤더, OAuth 1.0a 앱 인증 플로우, Onetime PIN, OAuth 2.0
PKCE 토큰 헬퍼, 주요 API 리소스 호출 메서드, 응답 메타데이터와 오류 매핑을 제공합니다.

응답 스키마는 API 전역에서 폭이 넓기 때문에 현재는 `ApiResponse[JsonValue]` 형태로
`results`, `rest_of_api`, `code`, 원본 `raw`를 보존합니다. 자주 쓰는 도메인부터 별도
모델을 점진적으로 얹을 수 있는 구조입니다.

## 설치

Python 라이브러리로 사용할 때는 프로젝트 환경에 설치합니다.

```sh
python -m pip install whooing-py
```

CLI를 어느 디렉터리에서나 실행하려면 `uv`의 독립된 도구 환경에 설치합니다.

```sh
uv tool install whooing-py
whooing --help
```

이 방식은 시스템 Python이나 `asdf`로 관리하는 Python 환경을 변경하지 않습니다. 패키지와
의존성은 `uv`가 격리된 환경에서 관리하고, `whooing` 실행 파일만 현재 사용자의 실행 경로에
노출합니다. 따라서 시스템 전체 설치는 아니지만 현재 사용자에게는 전역 명령처럼 동작합니다.

`whooing` 명령을 찾지 못하면 실행 파일 디렉터리를 셸의 `PATH`에 추가하고 터미널을 다시
시작합니다.

```sh
uv tool update-shell
uv tool dir --bin
```

업데이트와 제거도 `uv`로 관리할 수 있습니다.

```sh
uv tool upgrade whooing-py
uv tool uninstall whooing-py
```

설치하지 않고 한 번만 실행하려면 다음 명령을 사용합니다.

```sh
uvx --from whooing-py whooing --help
```

Python 3.11 이상을 지원합니다.

## 도구

- Python: `asdf`로 관리
- 패키지 및 환경 관리: `uv`
- 빌드 백엔드: `hatchling`
- 정적 분석: `ruff`, `mypy`
- 테스트: `pytest`

## 설정

```sh
asdf install
uv sync --locked --dev
uv run --locked pre-commit install
```

CLI와 응답 검증 경로가 Pydantic 모델을 사용하므로 `pydantic`은 기본 의존성입니다.

## 개발

```sh
uv lock --check
uv run --locked pre-commit run --all-files
uv run --locked ruff check .
uv run --locked ruff format --check .
uv run --locked mypy src
uv run --locked pytest
uv build
```

기본 테스트 실행에서는 실제 후잉 API 통합 테스트를 제외합니다. 통합 테스트는 환경 변수가
있을 때 명시적으로 실행합니다.

```sh
WHOOING_API_KEY=... WHOOING_SECTION_ID=... uv run --locked pytest -m integration
```

이 프로젝트에서만 자동으로 인식되게 하려면 `.env.example`을 참고해 `.env`를 만듭니다.
`.env`는 Git에서 무시됩니다.

```sh
WHOOING_API_KEY=발급된_인증키
WHOOING_SECTION_ID=s123
```

이후에는 별도 export 없이 실행할 수 있습니다.

```sh
uv run --locked pytest -m integration
```

## 사용 예시

API key 기반 동기 호출:

```python
from whooing import WhooingClient

with WhooingClient(api_key="발급된_인증키") as client:
    sections = client.sections.list()
    print(sections.rest_of_api)
    print(sections.results)
```

Bearer token 기반 비동기 호출:

```python
from whooing import AsyncWhooingClient

async with AsyncWhooingClient(access_token="access-token") as client:
    response = await client.entries.list(section_id="s123", limit=20)
    print(response.results)
```

OAuth 2.0 PKCE 토큰 교환:

```python
from whooing import OAuth2TokenClient
from whooing.auth import build_authorization_url, create_pkce_challenge

challenge = create_pkce_challenge()
url = build_authorization_url(
    client_id="app_id",
    redirect_uri="http://localhost/callback",
    scopes=["read", "write"],
    state="csrf-token",
    challenge=challenge,
)

with OAuth2TokenClient() as oauth:
    token = oauth.exchange_code(
        client_id="app_id",
        code="callback-code",
        redirect_uri="http://localhost/callback",
        code_verifier=challenge.verifier,
    )
```

OAuth 1.0a 앱 인증:

```python
from whooing import AppAuthClient, OAuth1aAuth, WhooingClient

with AppAuthClient() as app_auth:
    request_token = app_auth.request_token(
        app_id="app_id",
        app_secret="app_secret",
        callback_uri="http://localhost/callback",
    )
    authorize_url = app_auth.build_authorization_url(token=request_token.token)
    access_token = app_auth.access_token(
        app_id="app_id",
        app_secret="app_secret",
        token=request_token.token,
        pin="pin",
    )

auth = OAuth1aAuth(
    app_id="app_id",
    app_secret="app_secret",
    token=access_token.token,
    token_secret=access_token.token_secret,
)

with WhooingClient(auth=auth) as client:
    user = client.users.get()
```

Onetime PIN 인증:

```python
from whooing import AppAuthClient

with AppAuthClient() as app_auth:
    access_token = app_auth.access_token_by_onetime(
        app_id="app_id",
        app_secret="app_secret",
        onetime_pin="pin",
    )
```

Pydantic 모델로 응답 파싱:

```python
from whooing import WhooingClient
from whooing.pydantic_models import Section


with WhooingClient(api_key="발급된_인증키") as client:
    response = client.sections.list()
    sections = response.parse_results_as(list[Section])
```

단일 객체 응답은 `response.parse_results(Model)`로, 전체 API 응답은
`response.parse(ResponseModel)`로 검증할 수 있습니다. 공식 리소스별 모델은
`whooing.pydantic_models`에 모아 두었습니다.

공통 응답 규칙까지 엄밀하게 검증하려면 strict envelope 모델을 사용할 수 있습니다.

```python
from whooing import WhooingClient
from whooing.pydantic_models import User, WhooingSuccessResponse

with WhooingClient(api_key="발급된_인증키") as client:
    response = client.users.get()
    parsed = response.parse(WhooingSuccessResponse[User])
```

`whooing.pydantic_models`에는 일반 API의 성공/204/오류 응답, OAuth 오류, OAuth2 토큰,
OAuth2 metadata, OAuth1 토큰 응답 모델도 포함되어 있습니다.

요청 모델 사용:

```python
from whooing import EntryInput, WhooingClient

entry = EntryInput(
    entry_date=20260607,
    left_account="expenses",
    left_account_id="x1",
    right_account="assets",
    right_account_id="x2",
    item="커피",
    money=5000,
)

with WhooingClient(api_key="발급된_인증키") as client:
    client.entries.create_entry(section_id="s123", entry=entry)
```

파라미터가 많은 API는 `AccountInput`, `BudgetInput`, `PostItInput`, `MessageInput` 같은
dataclass 요청 모델을 제공합니다. 문서에 없는 필드나 새로 추가된 필드는 기존
`**fields` 방식 또는 요청 모델의 `extra_fields`로 전달할 수 있습니다.

선택적 재시도:

```python
from whooing import RetryPolicy, WhooingClient

with WhooingClient(
    api_key="발급된_인증키",
    retry_policy=RetryPolicy(max_attempts=3, backoff_seconds=0.5),
) as client:
    response = client.users.get()
```

기본값은 재시도하지 않습니다. `RetryPolicy`를 명시한 경우에만 HTTP 429와 일시적인 5xx
응답을 제한된 횟수로 다시 시도합니다. 중복 생성을 막기 위해 POST는 기본 재시도 대상에서
제외됩니다. POST 재시도가 안전한 작업에서만 `retry_methods`에 `"POST"`를 명시적으로
추가합니다.

## CLI

CLI는 Typer 기반으로 제공되며, 프로필 저장, OAuth 헬퍼, 범용 API 요청, SDK 리소스
명령을 지원합니다. CLI 경계에서는 입력 payload를 Pydantic으로 검증하고, 전용 명령의
API 응답은 리소스별 Pydantic 응답 모델로 검증한 뒤 출력합니다.

개인용 CLI는 별도 App 등록 없이 후잉 AI 연동 키를 사용합니다. 다음 명령은 후잉 홈페이지를
열고 키를 숨김 입력으로 받은 뒤 실제 API 호출로 검증합니다. 성공하면 키와 후잉의 기본 섹션을
현재 프로필에 저장하므로 환경 변수나 셸 명령에 키를 노출할 필요가 없습니다.

```sh
whooing auth login
```

명령의 안내에 따라 후잉 `계정 > 비밀번호 및 보안 > +AI 연동`에서 키를 발급하고 프롬프트에
붙여 넣습니다. 입력값은 화면에 표시되지 않습니다.

로그인이 완료되면 일반 리소스 명령에서 `--section-id`를 생략할 수 있습니다.

```sh
whooing auth status
whooing accounts list
whooing entries latest

# 저장된 기본값 대신 다른 섹션을 일시적으로 사용
whooing accounts list --section-id s456

# 기본 섹션만 변경
whooing profile set --section-id s456

# 로컬 프로필 제거
whooing auth logout
```

브라우저를 자동으로 열 수 없는 환경에서는 `--no-browser`로 후잉 홈페이지 열기를 생략합니다.

```sh
whooing auth login --no-browser
```

기본 섹션의 우선순위는 명령의 `--section-id`, `WHOOING_SECTION_ID`, 현재 프로필 순서입니다.
프로필 파일은 소유자만 읽고 쓸 수 있도록 저장되지만 인증키가 포함되므로
공유하거나 버전 관리에 추가하지 않아야 합니다.

배포용 App ID를 이미 보유한 개발자는 OAuth 2.0 PKCE 로그인을 사용할 수 있습니다. 기본
scope는 `read`이며 쓰기 권한은 명시적으로 요청합니다.

```sh
whooing auth oauth-login --client-id APP_ID
whooing --profile writer auth oauth-login \
  --client-id APP_ID \
  --scope read \
  --scope write
```

OAuth 각 단계를 별도로 제어해야 할 때는 저수준 헬퍼도 사용할 수 있습니다.

```sh
whooing auth oauth2-url \
  --client-id app_id \
  --redirect-uri http://localhost/callback \
  --scope read \
  --scope write
```

프로필 저장:

```sh
whooing --profile default profile set \
  --api-key 발급된_인증키 \
  --section-id s123
whooing --profile default sections list
```

프로필에는 API key와 access token 중 하나만 저장됩니다. 인증 방식을 바꾸면 기존 인증 값은
제거되지만 저장된 기본 섹션은 유지됩니다.

현재 디렉터리에 `.env`가 있으면 CLI 실행 시 자동으로 읽습니다. 셸에 export된 글로벌 환경
변수가 있으면 그 값을 우선 사용하고, 없을 때 `.env`의 `WHOOING_API_KEY` 또는
`WHOOING_ACCESS_TOKEN`을 사용합니다. 환경 변수 값을 프로필에 영구 저장하려면 의도를
명시해 `--from-env`를 사용합니다.

```sh
whooing profile set --from-env
whooing sections list
```

직접 API 경로 호출:

```sh
whooing api request GET sections.json
whooing --output table accounts list --section-id s123
whooing entries create \
  --section-id s123 \
  --field entry_date=20260620 \
  --field l_account=expenses \
  --field l_account_id=x1 \
  --field r_account=assets \
  --field r_account_id=x2 \
  --field item=커피 \
  --field money=5000
```

### AI 에이전트에서 사용

CLI는 자동화에서 전체 API envelope을 보존하는 JSON 출력을 기본으로 사용합니다. 각 명령의
`--help` 설명에는 `[READ]`, `[WRITE]`, `[LOCAL WRITE]` 같은 안전성 태그가 표시됩니다. AI
에이전트는 전용 `[READ]` 명령으로 식별자와 현재 상태를 먼저 확인하고, 사용자가 명시적으로
변경을 요청한 경우에만 `[WRITE]` 명령을 실행해야 합니다.

```sh
whooing --help
whooing entries --help
whooing entries list --help
```

인증·섹션 선택 순서, 자연어 요청과 명령의 매핑, 출력 envelope, 종료 코드, 변경 명령의 전체
목록은 [CLI 사용 가이드](docs/CLI_USAGE.md)에 정의합니다. CLI 확장 방향과 라이브러리 경계는
[CLI 설계 메모](docs/CLI_DESIGN.md)를 기준으로 관리합니다.

## 리소스 구성

- `client.users`: 사용자 정보, 사용자 로그, 포인트 로그
- `client.sections`: 섹션 조회/생성/수정/삭제/정렬
- `client.accounts`: 항목 조회/생성/수정/삭제/정렬
- `client.entries`: 거래 조회/입력/수정/삭제, 최근 거래, 외부 입력 파싱, 거래 분석성 API
- `client.budgets`: 예산, 장기 예산 목표, 월별 자본 목표
- `client.reports`: 통합 보고서, 요약 보고서, 사용자 정의 보고서 행
- `client.extras`: 자주입력, 매월입력, 카드 보고서, 캘린더, 포스트잇, 쪽지, 게시판, 업로드, 알림

## 커버리지

현재 구현은 공식 문서의 인증 플로우와 주요 API 경로를 호출할 수 있는 래퍼를 제공합니다.
후잉 API 응답은 리소스별 형태가 넓기 때문에 기본 클라이언트 응답은 `ApiResponse[JsonValue]`로
원본 JSON을 보존하고, 필요한 사용자가 Pydantic 헬퍼로 검증하는 방식을 택했습니다.

문서의 특정 엔드포인트 래퍼가 빠져 있더라도 `client.request(...)`와
`async_client.request(...)`로 직접 호출할 수 있습니다. 누락 래퍼는 테스트 가능한 단위로
추가하는 것을 기본 방향으로 합니다.

세부 구현 현황은 [API 구현 커버리지](docs/API_COVERAGE.md)를 기준으로 관리합니다.
릴리스 절차는 [릴리스 체크리스트](docs/RELEASE.md)를 따릅니다.

## 설계 메모

- 런타임 HTTP 처리는 `httpx`를 사용합니다.
- API 모델은 가능한 범위에서 타입이 없는 딕셔너리 대신 명시적인 타입 구조로 정의합니다.
- 인증은 API key, Bearer token, OAuth 2.0 PKCE, OAuth 1.0a 앱 인증, OAuth 1.0a 헤더
  생성을 지원합니다.
- API 응답에는 요청 가능 횟수 정보가 포함되므로, 클라이언트 표면에서도 해당 메타데이터를
  버리지 않고 보존합니다.
- 동기/비동기 클라이언트가 같은 리소스 경로 생성 로직을 공유하도록 구성합니다.
- 재시도는 기본 동작으로 강제하지 않고, `RetryPolicy`를 명시한 경우에만 적용합니다.
- Pydantic은 응답 검증과 CLI 요청 검증에 사용합니다.
