kordoc Python SDK
Python 에서 kordoc 으로 한국 문서(HWP·HWPX·PDF·DOCX·XLSX·PPTX …)를 파싱합니다.
상주 kordoc 엔진 워커(kordoc parse-worker --protocol 2)를 띄워 재사용하므로 문서마다 Node 를 새로 실행하지 않습니다.
파싱은 엔진 한 곳에서 하며 결과는 Node parse() 와 같습니다.
설치
이 패키지는 엔진을 설치하지 않습니다. 실행 환경에 Node.js(20 이상)와 kordoc 엔진이 따로 있어야 합니다.
npm i -g kordoc # 엔진 (PATH 에 kordoc)
pip install -U "kordoc>=0.2.0" # SDK (Python 3.11 이상, 외부 의존성 없음)
엔진 위치는 KordocConfig(node=..., cli=...), 환경변수 KORDOC_NODE·KORDOC_CLI, PATH 의 node·kordoc 순으로 찾습니다.
cli 에는 엔진의 dist/cli.js(또는 npm 이 만든 kordoc 링크)를 줍니다. 실행 인자는 배열로 넘기며 shell·npx 를 쓰지 않습니다.
Windows 의 kordoc.cmd·pnpm 셸 shim 처럼 node 로 실행할 수 없는 래퍼를 찾으면 옆의 node_modules/kordoc/dist/cli.js 로 바꿔 쓰고, 그것도 없으면 dist/cli.js 를 지정하라는 오류를 냅니다.
지정한 경로에 파일이 없으면 워커를 띄우기 전에 KordocStartError 를 냅니다.
폐쇄망에서는 오프라인 번들을 풀고 그 안의 엔진을 가리키면 됩니다.
from kordoc import KordocClient, KordocConfig
config = KordocConfig(
node="/opt/node/bin/node",
cli="/opt/kordoc-offline/node_modules/kordoc/dist/cli.js",
env={"KORDOC_OFFLINE": "1"},
)
사용
from kordoc import KordocClient
with KordocClient() as client:
result = client.parse("문서.hwpx", table_format="gfm") # 중첩 표를 부모·자식 트리로 편 GFM
if result.success:
print(result.markdown)
else:
print(result.code, result.error) # 문서를 읽지 못한 것은 예외가 아니다
import asyncio
from kordoc import AsyncKordocClient
async def main() -> None:
async with AsyncKordocClient(max_workers=2) as client:
results = await asyncio.gather(*(client.parse(p) for p in ["a.hwp", "b.pdf"]))
asyncio.run(main())
parse(path, **options)·parse_bytes(data, **options)·warmup(path, **options)·close()(aclose())with/async with를 나가면 소유 워커를 모두 종료·회수합니다. 패키지 import·클라이언트 생성만으로는 프로세스를 띄우지 않습니다.result.raw는 엔진 결과 JSON 원본입니다(알 수 없는 필드 포함).markdown·blocks·pages·metadata·warnings·images·ocr_lines를 꺼내 쓸 수 있습니다.result.raise_for_error()는 파싱 실패를KordocParseFailed로 바꿉니다.
파싱 옵션
버전: 아래 OCR 줄·모델 선택 API는 Python SDK 0.2.0 이상과 kordoc 엔진 4.22.0 이상이 필요합니다. npm 엔진과 Python SDK는 별도 버전으로 배포됩니다.
키워드 인자로 줍니다. 빼면 엔진 기본값이고, False 를 주면 실제로 끕니다(ocr=False 는 OCR 끔).
table_format("gfm") · html_tables · layout_tables("visual"·"keep") · classify_tables · tables · plain · script_tags ·
remove_header_footer · dedupe_running_headers · keep_trailing_empty_cols · keep_empty_paragraphs · include_field_placeholders ·
pages("1-3" 또는 [1, 3]) · images · inline_images · ocr(True·False·"force") · formula_ocr · password ·
ocr_lines(True·"all") · ocr_det("mobile"·"server"·OcrModelFile) · ocr_det_long_side · ocr_rec·ocr_rec_dict(OcrModelFile)
from kordoc import KordocClient, OcrModelFile
with KordocClient() as client:
r = client.parse("스캔본.pdf", ocr=True, ocr_lines=True, ocr_det="server", ocr_det_long_side=1920,
ocr_rec=OcrModelFile("models/my-rec.onnx", sha256="…")) # 상대 경로는 이 프로세스 기준 절대 경로로 보낸다
for page in r.ocr_lines or []: # list[OcrPageLines] — 옵션을 안 주면 None
for line in page.lines: # OcrLine(text, box=OcrBox(x, y, w, h), confidence, dropped)
print(page.page_number, line.text, line.confidence, line.box)
상자는 OCR 래스터 픽셀입니다(page.scale = px/pt). 좌표 규약과 모델 검증은 OCR 줄·모델 선택을 따릅니다.
함수 값(OCR 프로바이더, 진행 콜백)과 입력 경로를 바꾸는 옵션은 받지 않습니다. 잘못된 옵션은 KordocProtocolError(code="INVALID_OPTIONS")입니다.
JSON 으로 나타낼 수 없는 값(float("nan")·float("inf"), 순환 참조 등)은 워커에 보내기 전에 ValueError 로 돌려주며, 워커를 바꾸지 않습니다.
이미지
| 방식 | 사용 | 동작 |
|---|---|---|
| inline (기본) | parse(p) |
이미지 바이트를 결과 JSON 에 base64 로 받습니다 |
| files | parse(p, image_transport="files", assets_dir="/data/assets") |
assets_dir 아래 요청마다 새 디렉터리에 파일로 받습니다 |
result.images[i].read() 를 부를 때만 바이트를 해독하거나 파일을 읽습니다. filename 은 Markdown 이 가리키는 이름, path 는 files 방식의 실제 파일입니다.
files 방식의 파일은 호출자 소유이며 클라이언트를 닫아도 남습니다. 이미지가 많은 문서는 files 방식이 응답을 작게 유지합니다.
제한 시간·취소로 실행 중인 워커를 종료하면 그 요청이 쓰던 assets_dir/kordoc-<id>-* 디렉터리가 남을 수 있습니다 — assets_dir 정리는 호출자가 합니다.
images=False 는 이미지 바이트 추출 자체를 생략하는 엔진 옵션으로, 전송 방식과 별개입니다.
워커 수명
설정 (KordocConfig) |
기본 | 동작 |
|---|---|---|
max_workers |
1 | 상주 워커 수. 워커 하나는 한 번에 요청 하나만 처리합니다. 코어 수로 자동으로 늘리지 않습니다 |
max_queue |
64 | 워커를 기다리는 요청 수 상한. 넘으면 KordocQueueFull |
request_timeout |
없음 | 요청 제한 시간(초). 대기 큐 입장부터 결과 수신까지 — 빈 자리에서 교체 워커가 시작·워밍업하기를 기다리는 시간도 들어갑니다 |
start_timeout |
30 | 워커가 ready 를 보낼 때까지 |
warmup_timeout |
300 | 워밍업 문서 한 건 |
close_timeout |
10 | 닫을 때 진행 중 요청을 기다리는 시간 |
max_worker_rss_bytes |
끔 | 응답의 rss 가 넘으면 그 워커를 다음 작업 전에 새 워커로 바꿉니다 |
- 시작:
start()를 겹쳐 부르거나start()없이 첫 요청을 동시에 보내도 워커는max_workers개만 띄웁니다. 동기 클라이언트는 여러 스레드에서 함께 써도 됩니다.start()가 제한 시간·취소로 끊기면 같은 시작을 기다리는 다른 호출이 없을 때만 띄우던 워커를 끝내고, 있으면 시작을 이어 가 그쪽이 워커를 받습니다. - 제한 시간·취소: 대기 중인 요청은 큐에서만 빠집니다. 실행 중인 요청은 담당 워커를 즉시 종료·회수합니다(엔진 파싱은 중간에 멈출 수 없어서입니다). 다음 요청은 새 워커가 받습니다. 늦게 온 응답이 다른 요청에 섞이지 않습니다. 비동기에서는 Task 취소가 같은 규칙을 따릅니다.
- 교체 워커와 제한 시간: 교체 워커가 시작·워밍업하는 동안 기다리던 요청이 제한 시간·취소로 끝나도 시작·워밍업은 이어 가고, 다음 요청이 그 워커를 받습니다. 그래서
request_timeout이 시작·워밍업보다 짧으면 교체 직후 몇 요청이 시간 초과된 뒤 회복합니다. 교체 직후 요청까지 성공시키려면request_timeout을 ready 시간 + 워밍업 문서 파싱 시간 + 요청 파싱 시간보다 길게 잡습니다. - 장애: 워커가 끝나거나 응답이 깨지면 그 요청은
KordocWorkerCrashed로 끝나고 자동 재시도하지 않습니다. 다음 요청에서 새 워커를 띄웁니다. 응답 직후 워커가 끝나 SDK 가 아직 알아채지 못했으면 다음 요청이 그 워커를 받아KordocWorkerCrashed로 끝날 수 있습니다. 재시도는 호출자가 정합니다. - RSS 교체:
max_worker_rss_bytes를 켜면 작업이 끝난 뒤 응답의 rss(완료 시점 값, 처리 중 최댓값 아님)를 보고 교체합니다. N 건마다 재시작하지 않습니다. 풀 메모리는worker_info()의 rss 합으로 봅니다. Node heap 상한은 별도로env={"NODE_OPTIONS": "--max-old-space-size=4096"}처럼 정합니다. - 워밍업:
warmup(대표 문서, **options)은 모든 워커에서 그 문서를 실제로 파싱하고, 교체된 워커도 요청을 받기 전에 같은 문서로 워밍업합니다. 결과(WarmupReport)의 경고는 그대로 전달합니다. OCR 을 건너뛴 경고가 있으면 OCR 준비가 된 것이 아닙니다. 워밍업은 처리 경로를 한 번 지나게 할 뿐 최적화 완료를 보장하지 않습니다.- 문서를 읽지 못한 것, 워밍업 중 워커 장애,
warmup_timeout초과는 예외가 아니라 결과로 돌려줍니다(닫힌 클라이언트·큐 포화·워커 시작 실패는 예외입니다). 교체 워커는 한 워커에서라도 성공한 문서로만 워밍업합니다. 워커를 죽이는 문서 때문에 이후 요청이 모두 실패하지 않습니다. - 교체 워커의 워밍업이 실패해도 요청은 실패하지 않습니다(결과는
worker_info()의warmup). warmup()을 겹쳐 부르면 하나씩 실행됩니다.
- 문서를 읽지 못한 것, 워밍업 중 워커 장애,
- 닫기: 새 요청을 막고, 대기 요청과 시작·교체 워커를 기다리던 요청은
KordocClosed로 끝내고(띄우던 워커는 바로 종료합니다), 진행 요청은close_timeout까지 기다린 뒤 워커를 종료합니다.- 닫기는 한 번만 실행됩니다. 겹쳐 부르거나 취소된
aclose()를 다시 부르면 그 닫기가 끝날 때까지 기다립니다. 취소된aclose()도 닫기는 이어 갑니다. - 동기
close()가 Ctrl-C 로 끊기면 진행 요청을 더 기다리지 않고 워커를 바로 종료하며, 다른 스레드에서 기다리던 호출은KordocClosed로 끝납니다.start()가 워커를 띄우는 동안 Ctrl-C 로 끊기면 띄우던 워커를 남기지 않습니다.
- 닫기는 한 번만 실행됩니다. 겹쳐 부르거나 취소된
바이트 입력
parse_bytes(data) 는 바이트를 SDK 소유 임시 디렉터리(temp_dir, 기본 시스템 임시 폴더 아래 kordoc-sdk-*)에 파일로 쓴 뒤 파싱합니다.
큰 입력을 NDJSON 에 다시 싣지 않기 위해서이며 임시 파일 I/O 가 생깁니다. 성공·실패·취소와 관계없이 끝나면 지우고(쓰는 중에 취소되면 쓰기가 끝나는 대로), 닫을 때는 진행 중인 쓰기를 기다린 뒤 디렉터리도 지웁니다.
원본 파일 경로가 필요한 DRM 문서 대체 경로는 바이트 입력에서 파일 입력과 같게 동작하지 않을 수 있습니다.
그 밖에
- 워커 환경은 현재 환경을 물려받고
env를 덧붙입니다(KORDOC_OFFLINE,KORDOC_MODEL_CACHE등). SDK 는 요청 본문·암호를 로그에 남기지 않습니다. KORDOC_ROOT는 MCP 서버의 접근 제한입니다. SDK 는 같은 샌드박스를 제공하지 않으며, 넘긴 경로를 그대로 읽습니다.- 워커 프로토콜: docs/parse-worker-protocol.md
개발
npm ci && npm run build # 저장소 루트 — 엔진(dist/cli.js)
cd sdk/python
python -m venv .venv && . .venv/bin/activate
python -m pip install -e '.[test]'
python -m pytest # 실제 엔진 + 합성 문서(tests/fixtures/sdk-docs.ts) + 장애 주입 워커
python -m build
Metadata
Release files for kordoc 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| kordoc-0.2.0.tar.gz | 43.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| kordoc-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 72.2 kB
Release files / kordoc-0.2.0.tar.gz
| Download URL | kordoc-0.2.0.tar.gz |
|---|---|
| Size | 43.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a85a9a013cca1606bf2c5058187645aecb45ef9c328ceb8d6c29b62b9df5e862
|
|
BLAKE2b-256 checksum How to use checksums |
c770279affc58d82cf2ff8c3f7f70f27c0bbe6ff8014fd6dd04c45450c81a6d4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency logRelease files / kordoc-0.2.0-py3-none-any.whl
| Download URL | kordoc-0.2.0-py3-none-any.whl |
|---|---|
| Size | 28.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5a6cd4de2a840f6af770a48a9a045fdb612309dd1a03a444c5fdd38bac603ae8
|
|
BLAKE2b-256 checksum How to use checksums |
5e0ed5150db2b283189a37235ce3f4fb6063e2c8c2013bc0efc1d04a207843d0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency log