Metadata-Version: 2.4
Name: layoutor
Version: 1.0.0
Summary: Python SDK for the Layoutor PDF translation separation API
Author: Droidrive Inc.
License: MIT License
        
        Copyright (c) 2026 Droidrive Inc.
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Requires-Python: >=3.10
Requires-Dist: httpx==0.28.1
Description-Content-Type: text/markdown

# Layoutor Python SDK

`layoutor` is the typed Python client for the Layoutor PDF translation separation API. It uploads a PDF, gives its translation units to your callback, submits the callback's complete translation, and returns the composed PDF bytes.

## Installation

```sh
python -m pip install layoutor
```

Python 3.10 or newer is required. Set `LAYOUTOR_API_KEY`, or pass `api_key` to the client explicitly.

```sh
export LAYOUTOR_API_KEY=lk_live_your_key
```

API keys must be `lk_live_` followed by exactly 43 URL-safe characters (`A-Z`, `a-z`, `0-9`, `_`, or `-`).
Every other value, including the empty string, produces `invalid_api_key`; an unspecified key produces
`invalid_request`. Error messages and request IDs redact every occurrence of the configured key,
including occurrences embedded in a longer string. They also redact every string matching
`lk_live_[A-Za-z0-9_-]{43}`. A non-key identifier with that exact shape is therefore intentionally redacted as a
false positive. Error codes are never redacted.

## Translate a document

```python
from pathlib import Path
from layoutor import LayoutorClient

def translate(units, context):
    # Replace this example with your translator. Keep every translatable id.
    return [
        {"id": unit["id"], "target": unit["source"]}
        for unit in units
        if unit["translate"]
    ]

with LayoutorClient() as client:
    result = client.translate_document(
        Path("document.pdf"),
        source_lang="en",
        target_lang="ja",
        translate=translate,
    )
    Path("document.ja.pdf").write_bytes(result["pdf"])
```

`translate` receives the units list and a context containing `notes` and the current `document`. A document remains in `ready` state for only 10 minutes; finish the callback and submission before the `document["expires_at"]` deadline. Units with `translate: false` are always submitted with their source text. An async callback may be used with `AsyncLayoutorClient`.

The clients use a deterministic idempotency key for each file and language pair. If document creation replays an earlier run that is already composing, completed, failed, or expired, they obtain a new signed upload and recreate the document once with a randomized suffix. If a caller-supplied `idempotency_key` was used, or the recreation is also a replay, they instead raise `LayoutorError` with code `document_replayed` and include the replayed status in the message.

If submit transport is ambiguous but polling proves acceptance, `submission["accepted_without_result"]` is true and its result counters are `None` because no submit response was retrieved.

Both clients also expose the implemented API operations directly: `create_upload`, `put_upload`, `create_document`,
`get_document`, `delete_document`, `get_document_units`, `get_document_units_xliff`, `submit_document_units`,
`submit_document_units_xliff`, `get_document_pdf`, and `get_current_account`.

The clients retry transport errors on reads and safe writes, HTTP 503, and transient HTTP 429 capacity errors. Monthly quota errors are returned immediately as `LayoutorError`.
Request retries and upload/submit recovery use a 180-second deadline. After the deadline, the clients start no new
request or retry wait and raise `LayoutorError` with code `retry_exhausted` when recovery would need one. A request
started before the deadline may finish afterward, and its response remains valid; a successful response is still
treated as successful.

Custom `base_url` values must use HTTPS; HTTP is accepted only for `localhost`, IPv4 `127.0.0.0/8`, and IPv6
`[::1]` loopback development endpoints.
