athena-python-docx 0.44.0__tar.gz → 0.45.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/PKG-INFO +1 -1
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/__init__.py +1 -1
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/_asset_token.py +46 -3
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/_http.py +139 -2
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/_http_doc.py +16 -1
- athena_python_docx-0.45.1/docx/_public_create.py +270 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/document.py +5 -1
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/pyproject.toml +1 -1
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_asset_tokens.py +99 -26
- athena_python_docx-0.45.1/tests/test_public_create.py +536 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/uv.lock +1 -1
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/.gitignore +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/AGENTS.md +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/CLAUDE.md +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/DOCX_EXEC_LAB.md +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/README.md +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/_athena_extension.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/_batching.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/_buffer.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/_capabilities.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/_citations.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/_execution.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/_image_utils.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/_postproc.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/_proxy.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/_ptc.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/_references.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/_run_order.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/_table_styles.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/_timeouts.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/_tracked.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/_xml_text.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/api.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/blkcntnr.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/bookmarks.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/charts.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/client.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/commands.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/comments.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/drawing/__init__.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/enum/__init__.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/enum/base.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/enum/dml.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/enum/section.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/enum/shape.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/enum/style.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/enum/table.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/enum/text.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/errors.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/exceptions.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/fields.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/footnotes.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/image/__init__.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/image/exceptions.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/image/image.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/math.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/opc/__init__.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/opc/coreprops.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/oxml/__init__.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/revisions.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/sdt.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/section.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/session.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/settings.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/shape.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/shared.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/styles/__init__.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/styles/_table.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/styles/latent.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/styles/style.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/styles/styles.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/table.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/text/__init__.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/text/font.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/text/hyperlink.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/text/pagebreak.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/text/paragraph.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/text/parfmt.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/text/run.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/text/tabstops.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/toc.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/docx/typing.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/scripts/docx_exec_lab.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/scripts/docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/scripts/dump_wire_fixtures.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/scripts/release.sh +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/scripts/round_trip_smoke.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/scripts/smoke_test_block_not_found.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/scripts/validate_find_replace_asset.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/__init__.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/conftest.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/METHODOLOGY.md +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/README.md +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/__init__.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/ab_probe/__init__.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/ab_probe/agent_documents.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/ab_probe/default_style_run_formatting.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/ab_probe/header_footer_pictures.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/ab_probe/less_used_writes.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/ab_probe/merged_cell_identity.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/ab_probe/paragraph_text_setter.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/ab_probe/picture_runs.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/ab_probe/proxy_equality.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/ab_probe/return_types.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/ab_probe/xml_incompatible_text.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/ab_probe/zero_and_negative_indents.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/ab_probe_cases.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/ab_probe_runner.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/auto_gen_cases.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/binary_round_trip.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/cases.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/complex_cases.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/coverage_report.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/extract.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/extreme_cases.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/fake_session.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/firm_templates/README.md +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/firm_templates/__init__.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/firm_templates/_runner.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/firm_templates/extractor.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/local_runner.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/mega_cases.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshot.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/rw16_header_footer_tables.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/rw17_header_letterhead_filled.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/rw18_header_table_properties.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/rw19_header_table_rows_columns.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/op_snapshots/rw20_header_table_merges.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/ours_spec.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/parity_crawl.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/parity_diff.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/real_world_cases.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/round_trip_tests.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/stock_spec.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fidelity/test_e2e_against_staging.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fixtures/field_reads.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fixtures/note_reference_reads.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fixtures/ruby_reads.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/fixtures/symbol_reads.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/live/__init__.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/live/baseline.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/live/fixture_scripts.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/live/reader.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/live/runner.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/live/sdk_driver.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/live/stock_driver.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/live/test_live_runner.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/parity/README.md +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/parity/__init__.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/parity/baseline_gaps.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/parity/compare.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/parity/intentional_deviations.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/parity/introspect.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/parity/reports/gap_report.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/parity/run_parity.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/parity/snapshots/athena_latest.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/parity/test_parity_gap.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_add_section.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_add_section_extract_items.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_alignment_values.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_athena_extension_creates_session_open.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_athena_extensions_contract.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_athena_extensions_registry.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_batch_chunking.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_batching_perf.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_battery_cases.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_block_not_found_error.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_buffer.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_cell_add_table_not_supported.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_cell_borders_spec.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_cell_fastpath_runs.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_cell_inner_format_stash.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_cell_inner_run_format_stash.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_cell_inner_run_guard.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_cell_merge.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_cell_nested_tables.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_cell_paragraphs_listed.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_cell_text_plain_fastpath.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_cell_text_replace_semantics.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_citations.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_collapsed_range_format.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_color_format_theme.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_command_dataclasses.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_commands.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_comment_paragraphs.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_comments.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_comments_first_read.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_content_control_value.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_core_properties.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_create_workspace.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_document_asset_id_property.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_document_clear.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_document_create.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_document_create_from_template.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_document_factory_validation.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_document_settings_session_open.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_docx_exec_lab.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_drawing_image.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_e2e_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_empty_hyperlinks.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_enum_parity.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_execution_scope.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_export_docx_clean.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_export_rescue_anchors.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_field_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_field_refresh.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_field_runs.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_find_replace_session_open.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_flushed_write_failures.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_http_transport.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_hyperlink_coalescing.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_image_url_data_uri.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_inline_page_break.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_inline_shape_alt_text.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_inline_shape_size.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_inline_shape_type.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_inline_shapes_floating.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_insert_deferred.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_iter_inner_content.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_latent_style_writes.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_line_clear_break.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_list_styles.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_listed_paragraph_reads.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_literal_no_break_hyphens.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_literal_soft_hyphens.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_merged_cell_identity.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_merged_cell_secondary_slot.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_merged_cells.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_missing_modules.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_nested_table_refused.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_note_reference_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_oxml_shim.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_page_break.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_paragraph_delete.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_paragraph_indent_reads.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_paragraph_runs_hyperlinks.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_paragraph_text_empty_run.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_paragraph_text_len_cache.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_parfmt_clears.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_parity_misc.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_parity_round2.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_pdf_export_and_page_renders.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_phase_a_behavior.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_phase_b_headers_footers.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_phase_c_tables.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_positional_cell_id.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_postproc_cell_format_rewrite.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_postproc_ref_restore.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_pr19766_review_fixes.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_proxy_equality.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_ptc.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_python_docx_api_parity.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_read_run_ranges.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_rendered_page_breaks.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_repeated_node_read.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_restart_numbering.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_revisions.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_round_trip_smoke_script.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_ruby_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_run_add_picture_anchor.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_run_break_inner_content.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_run_edges.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_run_formatting_wire.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_run_identity.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_run_later_runs.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_run_mark_inheritance.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_run_page_number_blocks.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_run_table_style_objects.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_run_text_python_docx.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_run_text_refreshes_paragraph.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_run_unmodeled_toggles.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_sdk_reads_served.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_section_read_lengths.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_sections_empty_list.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_add_paragraph_style.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_add_picture.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_add_run.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_comments_add_comment.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_comments_get.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_document_audit.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_document_element.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_enum_section.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_font_audit.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_header_footer.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_hyperlink.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_inline_shape.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_misc.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_paragraph_strict.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_paragraph_style.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_parfmt.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_row_col_cell.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_run_add_break.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_run_bool_setters.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_run_style.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_run_style_strict.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_run_text.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_run_underline.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_section_audit.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_section_dimensions.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_section_onoff.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_settings.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_shared_audit.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_style.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_styles.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_table_audit.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_table_cell.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_table_dimensions.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_silent_stub_table_layout.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_smoke_integration.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_style_acceptance.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_style_delete.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_style_font.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_style_formatting_writes.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_style_metadata_writes.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_style_names.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_style_resolution.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_style_setters_contract.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_style_tab_stops.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_style_tab_stops_read.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_symbol_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_tab_stop_writes.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_table_add_row_grid_row.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_table_anchors.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_table_clears.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_table_direction.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_table_length_reads.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_table_listing_reads.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_table_read_reuse.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_table_set_cell_perf.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_table_style_id_resolution.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_temporarily_unavailable.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_text_control_characters.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_tracked_revisions_offsets.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_unmapped_alignment_reads.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_uv_lock_version.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_validate_find_replace_asset_script.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_versioned_read_reuse.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_wire_contract.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_xml_attr_guard.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_xml_incompatible_text.py +0 -0
- {athena_python_docx-0.44.0 → athena_python_docx-0.45.1}/tests/test_zod_wire_contract.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: athena-python-docx
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.45.1
|
|
4
4
|
Summary: Drop-in replacement for python-docx that connects to Athena's DOCX Studio collaborative document stack
|
|
5
5
|
Project-URL: Homepage, https://athenaintelligence.ai
|
|
6
6
|
Author-email: Athena Intelligence <engineering@athenaintelligence.ai>
|
|
@@ -13,8 +13,17 @@ A call on an existing Word document reaches DOCX Studio with, in this order:
|
|
|
13
13
|
4. otherwise ``ATHENA_API_KEY`` itself, as before.
|
|
14
14
|
|
|
15
15
|
Creating a document (``Document.create``, its upload) keeps the key of 1, 2 or
|
|
16
|
-
4: a document that does not exist yet has no token.
|
|
17
|
-
|
|
16
|
+
4: a document that does not exist yet has no token. In case 3 Athena's public
|
|
17
|
+
API creates it instead (``docx._public_create``). A token goes only to its own
|
|
18
|
+
document's routes.
|
|
19
|
+
|
|
20
|
+
An Athena runtime credential (a computer's, a session VM's or a notebook
|
|
21
|
+
kernel's ``ATHENA_API_KEY``: ``athcomp_``, ``athsvm_``, ``athnb_``) is never
|
|
22
|
+
sent to DOCX Studio, which takes none, nor to Athena's older creation routes
|
|
23
|
+
outside its public API: a call that would send one (another document's routes,
|
|
24
|
+
an Athena without the studio-token route, no ``ATHENA_API_URL``, a create or an
|
|
25
|
+
upload Athena's public API cannot take) refuses before any request, naming what
|
|
26
|
+
to set (:func:`is_runtime_credential`).
|
|
18
27
|
|
|
19
28
|
A token is minted once per (agora, key, asset, access) for the whole process,
|
|
20
29
|
again 90 seconds before it expires, and once more when the studio answers 401.
|
|
@@ -28,7 +37,9 @@ or locally; both work.
|
|
|
28
37
|
|
|
29
38
|
Athena's refusals (400, 401, 403, 404, 422) raise at once, without a retry. A
|
|
30
39
|
deployment that signs no asset tokens answers 503: the client then uses the
|
|
31
|
-
studio key when one is set, and otherwise raises an error that names it.
|
|
40
|
+
studio key when one is set, and otherwise raises an error that names it. An
|
|
41
|
+
Athena older than the route answers FastAPI's bare 404 (``{"detail": "Not
|
|
42
|
+
Found"}``): the client then sends ``ATHENA_API_KEY``, as before the route.
|
|
32
43
|
"""
|
|
33
44
|
|
|
34
45
|
from __future__ import annotations
|
|
@@ -67,6 +78,16 @@ PUBLIC_API_PREFIX = "/api/v0"
|
|
|
67
78
|
VIEW = "view"
|
|
68
79
|
EDIT = "edit"
|
|
69
80
|
|
|
81
|
+
#: How the credentials Athena mints for a runtime begin: a computer's, a session
|
|
82
|
+
#: VM's and a notebook kernel's own ``ATHENA_API_KEY``. Only Athena's public API
|
|
83
|
+
#: takes one; a studio never does.
|
|
84
|
+
RUNTIME_CREDENTIAL_PREFIXES = ("athcomp_", "athsvm_", "athnb_")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def is_runtime_credential(key: str | None) -> bool:
|
|
88
|
+
"""Whether ``key`` is a credential Athena minted for a runtime, which no studio takes."""
|
|
89
|
+
return bool(key) and str(key).startswith(RUNTIME_CREDENTIAL_PREFIXES)
|
|
90
|
+
|
|
70
91
|
#: Minted again this long before Athena's ``expires_at_ms``.
|
|
71
92
|
REFRESH_BEFORE_EXPIRY_SECONDS = 90.0
|
|
72
93
|
#: The longest a token is held, whatever the local clock says (Athena's lifetime).
|
|
@@ -112,6 +133,26 @@ class SigningUnavailable(Exception):
|
|
|
112
133
|
"""Athena's 503: this deployment signs no asset tokens."""
|
|
113
134
|
|
|
114
135
|
|
|
136
|
+
class MintRouteMissing(Exception):
|
|
137
|
+
"""A 404 with FastAPI's bare route-not-found body: the Athena at
|
|
138
|
+
``ATHENA_API_URL`` predates the studio-token route (a self-hosted release
|
|
139
|
+
older than the SDK). The client then sends ``ATHENA_API_KEY``, as before."""
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
#: What FastAPI answers for a path no route matches. Athena's own 404 from the
|
|
143
|
+
#: studio-token route names the asset ("Asset not found."), so the two never mix.
|
|
144
|
+
_ROUTE_NOT_FOUND_BODY = {"detail": "Not Found"}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _is_route_missing(response: requests.Response) -> bool:
|
|
148
|
+
if response.status_code != 404:
|
|
149
|
+
return False
|
|
150
|
+
try:
|
|
151
|
+
return bool(response.json() == _ROUTE_NOT_FOUND_BODY)
|
|
152
|
+
except ValueError:
|
|
153
|
+
return False
|
|
154
|
+
|
|
155
|
+
|
|
115
156
|
class _TokenCache:
|
|
116
157
|
"""Process-wide tokens by (agora, key, asset, access), least recently used first."""
|
|
117
158
|
|
|
@@ -274,6 +315,8 @@ class TokenMinter:
|
|
|
274
315
|
status = response.status_code
|
|
275
316
|
if status == 503:
|
|
276
317
|
raise SigningUnavailable(_detail(response) or "This deployment signs no asset tokens.")
|
|
318
|
+
if _is_route_missing(response):
|
|
319
|
+
raise MintRouteMissing(f"no studio-token route at {response.url}")
|
|
277
320
|
detail = _detail(response)
|
|
278
321
|
reason = f"(HTTP {status}{': ' + detail if detail else ''})"
|
|
279
322
|
if status == 401:
|
|
@@ -45,13 +45,38 @@ def _resolve_api_key(api_key: str | None) -> str | None:
|
|
|
45
45
|
makes. Both create paths land outside Athena's public API (docx-studio's
|
|
46
46
|
``/docs/empty``, agora's ``/api/docx-studio/assets``), and inside an Athena
|
|
47
47
|
session VM or computer ``ATHENA_API_KEY`` is a credential only that public
|
|
48
|
-
API (``/api/v0``) accepts
|
|
49
|
-
|
|
48
|
+
API (``/api/v0``) accepts: there (neither key set, ``ATHENA_API_URL`` set)
|
|
49
|
+
Athena's public API creates the document instead (``docx._public_create``).
|
|
50
|
+
``ATHENA_API_KEY`` stays the fallback for an environment that sets no
|
|
51
|
+
studio key and no ``ATHENA_API_URL`` (local use with one personal key).
|
|
50
52
|
The new document is owned by the user the key belongs to.
|
|
51
53
|
"""
|
|
52
54
|
return api_key or os.environ.get(_API_KEY_ENV) or os.environ.get("ATHENA_API_KEY")
|
|
53
55
|
|
|
54
56
|
|
|
57
|
+
def _refuse_runtime_credential(*, key: str | None, action: str) -> None:
|
|
58
|
+
"""Refuse, before any request, to send an Athena runtime credential where
|
|
59
|
+
Athena's public API could not take the call.
|
|
60
|
+
|
|
61
|
+
A computer's, session VM's or notebook's own ``ATHENA_API_KEY`` is accepted
|
|
62
|
+
only by Athena's public API (``/api/v0``). Reaching this point means that
|
|
63
|
+
API could not take the create (no ``ATHENA_API_URL``, or an Athena older
|
|
64
|
+
than its creation routes), and DOCX Studio and agora's older routes refuse
|
|
65
|
+
the credential.
|
|
66
|
+
"""
|
|
67
|
+
from docx._asset_token import is_runtime_credential
|
|
68
|
+
|
|
69
|
+
if is_runtime_credential(key):
|
|
70
|
+
raise AuthenticationError(
|
|
71
|
+
f"Cannot {action}: ATHENA_API_KEY here is an Athena runtime credential (a "
|
|
72
|
+
"computer's, session's or notebook's own key), which only Athena's public "
|
|
73
|
+
"API accepts, and that API could not take this call (ATHENA_API_URL is "
|
|
74
|
+
"unset, or that Athena predates it). Set ATHENA_API_URL, or set "
|
|
75
|
+
"ATHENA_DOCX_API_KEY (or pass api_key=) to an Athena API key DOCX Studio "
|
|
76
|
+
"accepts.",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
55
80
|
def _derive_agora_base_url(docx_base_url: str) -> str | None:
|
|
56
81
|
"""Best-effort derive an Agora base URL from a docx-studio base URL.
|
|
57
82
|
|
|
@@ -137,12 +162,21 @@ def create_empty_document(
|
|
|
137
162
|
"Pass base_url= to Document.create() or set the env var "
|
|
138
163
|
'(e.g. ATHENA_DOCX_BASE_URL="https://docx-studio.prd.athenaintel.com").',
|
|
139
164
|
)
|
|
165
|
+
created = _create_through_athena(
|
|
166
|
+
api_key=api_key,
|
|
167
|
+
name=name,
|
|
168
|
+
parent_folder_id=parent_folder_id,
|
|
169
|
+
workspace_id=workspace_id,
|
|
170
|
+
)
|
|
171
|
+
if created is not None:
|
|
172
|
+
return created
|
|
140
173
|
if not resolved_key:
|
|
141
174
|
raise AuthenticationError(
|
|
142
175
|
f"Missing api_key and {_API_KEY_ENV} env var. "
|
|
143
176
|
"Pass api_key= to Document.create() or set the env var "
|
|
144
177
|
"(or set ATHENA_API_KEY as a fallback).",
|
|
145
178
|
)
|
|
179
|
+
_refuse_runtime_credential(key=resolved_key, action="create the document on DOCX Studio")
|
|
146
180
|
|
|
147
181
|
url: str = resolved_base.rstrip("/") + "/docs/empty"
|
|
148
182
|
# The document is born as its creator's: its first write is signed as the
|
|
@@ -221,6 +255,44 @@ def create_empty_document(
|
|
|
221
255
|
)
|
|
222
256
|
|
|
223
257
|
|
|
258
|
+
def _create_through_athena(
|
|
259
|
+
*,
|
|
260
|
+
api_key: str | None,
|
|
261
|
+
name: str | None,
|
|
262
|
+
parent_folder_id: str | None,
|
|
263
|
+
workspace_id: str | None,
|
|
264
|
+
) -> CreateAssetResult | None:
|
|
265
|
+
"""A new document Athena's public API made, or None for docx-studio's path.
|
|
266
|
+
|
|
267
|
+
Athena creates it when no ``api_key`` and no ``ATHENA_DOCX_API_KEY`` are set
|
|
268
|
+
and ``ATHENA_API_KEY`` and ``ATHENA_API_URL`` are (``docx._public_create``).
|
|
269
|
+
None otherwise, and when that Athena predates the public create.
|
|
270
|
+
"""
|
|
271
|
+
from docx._public_create import (
|
|
272
|
+
PublicApi,
|
|
273
|
+
PublicCreateUnavailable,
|
|
274
|
+
create_word_document,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
api = PublicApi.for_creation(api_key=api_key)
|
|
278
|
+
if api is None:
|
|
279
|
+
return None
|
|
280
|
+
try:
|
|
281
|
+
created = create_word_document(
|
|
282
|
+
api=api,
|
|
283
|
+
name=name,
|
|
284
|
+
parent_folder_id=parent_folder_id,
|
|
285
|
+
workspace_id=workspace_id or os.environ.get("ATHENA_WORKSPACE_ID"),
|
|
286
|
+
)
|
|
287
|
+
except PublicCreateUnavailable:
|
|
288
|
+
return None
|
|
289
|
+
return CreateAssetResult(
|
|
290
|
+
asset_id=created["asset_id"],
|
|
291
|
+
name=created["title"],
|
|
292
|
+
workspace_id=created["workspace_id"] or "",
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
|
|
224
296
|
class UploadAssetResult(TypedDict):
|
|
225
297
|
"""Parsed response from the create-and-import upload flow."""
|
|
226
298
|
|
|
@@ -228,6 +300,58 @@ class UploadAssetResult(TypedDict):
|
|
|
228
300
|
name: str
|
|
229
301
|
|
|
230
302
|
|
|
303
|
+
def _upload_through_athena(
|
|
304
|
+
*,
|
|
305
|
+
docx_path: str,
|
|
306
|
+
api_key: str | None,
|
|
307
|
+
name: str | None,
|
|
308
|
+
parent_folder_id: str | None,
|
|
309
|
+
workspace_id: str | None,
|
|
310
|
+
source_asset_id: str | None,
|
|
311
|
+
timeout: float,
|
|
312
|
+
) -> UploadAssetResult | None:
|
|
313
|
+
"""A document Athena made from the ``.docx`` at ``docx_path``, or None.
|
|
314
|
+
|
|
315
|
+
Athena's ``POST /api/v0/assets/create-from-file`` makes it in one request
|
|
316
|
+
when no ``api_key`` and no ``ATHENA_DOCX_API_KEY`` are set and
|
|
317
|
+
``ATHENA_API_KEY`` and ``ATHENA_API_URL`` are (``docx._public_create``).
|
|
318
|
+
None otherwise, and when that Athena predates the route: the upload then
|
|
319
|
+
goes through agora's docx-studio bridge, as before.
|
|
320
|
+
"""
|
|
321
|
+
from docx._public_create import (
|
|
322
|
+
PublicApi,
|
|
323
|
+
PublicCreateUnavailable,
|
|
324
|
+
create_word_document_from_file,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
api = PublicApi.for_creation(api_key=api_key)
|
|
328
|
+
if api is None:
|
|
329
|
+
return None
|
|
330
|
+
if not os.path.exists(docx_path):
|
|
331
|
+
raise SessionError(f"DOCX file not found: {docx_path}")
|
|
332
|
+
if not docx_path.lower().endswith(".docx"):
|
|
333
|
+
raise SessionError(
|
|
334
|
+
f"Only .docx files are accepted by the upload endpoint (got {docx_path})",
|
|
335
|
+
)
|
|
336
|
+
title = name
|
|
337
|
+
if title is None:
|
|
338
|
+
base = os.path.basename(docx_path)
|
|
339
|
+
title = base[:-5] if base.lower().endswith(".docx") else base
|
|
340
|
+
try:
|
|
341
|
+
created = create_word_document_from_file(
|
|
342
|
+
api=api,
|
|
343
|
+
docx_path=docx_path,
|
|
344
|
+
title=title,
|
|
345
|
+
parent_folder_id=parent_folder_id,
|
|
346
|
+
workspace_id=workspace_id or os.environ.get("ATHENA_WORKSPACE_ID"),
|
|
347
|
+
source_asset_id=source_asset_id,
|
|
348
|
+
timeout=timeout,
|
|
349
|
+
)
|
|
350
|
+
except PublicCreateUnavailable:
|
|
351
|
+
return None
|
|
352
|
+
return UploadAssetResult(asset_id=created["asset_id"], name=created["title"])
|
|
353
|
+
|
|
354
|
+
|
|
231
355
|
def upload_document(
|
|
232
356
|
*,
|
|
233
357
|
docx_path: str,
|
|
@@ -273,6 +397,18 @@ def upload_document(
|
|
|
273
397
|
import base64
|
|
274
398
|
import os
|
|
275
399
|
|
|
400
|
+
uploaded = _upload_through_athena(
|
|
401
|
+
docx_path=docx_path,
|
|
402
|
+
api_key=api_key,
|
|
403
|
+
name=name,
|
|
404
|
+
parent_folder_id=parent_folder_id,
|
|
405
|
+
workspace_id=workspace_id,
|
|
406
|
+
source_asset_id=source_asset_id,
|
|
407
|
+
timeout=timeout,
|
|
408
|
+
)
|
|
409
|
+
if uploaded is not None:
|
|
410
|
+
return uploaded
|
|
411
|
+
|
|
276
412
|
resolved_docx_base: str | None = docx_base_url or os.environ.get(_BASE_URL_ENV)
|
|
277
413
|
resolved_key: str | None = _resolve_api_key(api_key)
|
|
278
414
|
resolved_agora_base: str | None = (
|
|
@@ -294,6 +430,7 @@ def upload_document(
|
|
|
294
430
|
"Pass api_key= to Document.create() or set the env var "
|
|
295
431
|
"(or set ATHENA_API_KEY as a fallback).",
|
|
296
432
|
)
|
|
433
|
+
_refuse_runtime_credential(key=resolved_key, action="upload the document through agora")
|
|
297
434
|
|
|
298
435
|
if not os.path.exists(docx_path):
|
|
299
436
|
raise SessionError(f"DOCX file not found: {docx_path}")
|
|
@@ -47,9 +47,11 @@ from docx._asset_token import (
|
|
|
47
47
|
EDIT,
|
|
48
48
|
STUDIO_KEY_ENV,
|
|
49
49
|
VIEW,
|
|
50
|
+
MintRouteMissing,
|
|
50
51
|
SigningUnavailable,
|
|
51
52
|
StudioToken,
|
|
52
53
|
TokenMinter,
|
|
54
|
+
is_runtime_credential,
|
|
53
55
|
)
|
|
54
56
|
from docx._batching import call_abandoned, note_progress
|
|
55
57
|
from docx._buffer import CommandBuffer, keep_reported_message
|
|
@@ -1127,6 +1129,10 @@ class HttpClient:
|
|
|
1127
1129
|
token = minter.token(asset_id=asset_id, access=access)
|
|
1128
1130
|
except SigningUnavailable as exc:
|
|
1129
1131
|
return self._without_asset_tokens(asset_id=asset_id, reason=str(exc)), None
|
|
1132
|
+
except MintRouteMissing:
|
|
1133
|
+
# The Athena at ATHENA_API_URL predates the route: the key, as before.
|
|
1134
|
+
self._asset_tokens = None
|
|
1135
|
+
return self._api_key, None
|
|
1130
1136
|
return token.token, (token, access)
|
|
1131
1137
|
|
|
1132
1138
|
def _without_asset_tokens(self, *, asset_id: str, reason: str) -> str:
|
|
@@ -1150,6 +1156,15 @@ class HttpClient:
|
|
|
1150
1156
|
is sent once more with a newly minted one: docx-studio refused it before
|
|
1151
1157
|
acting on it, so nothing is applied twice."""
|
|
1152
1158
|
bearer, grant = self._bearer(asset_id=asset_id, access=access)
|
|
1159
|
+
if grant is None and is_runtime_credential(bearer):
|
|
1160
|
+
raise AuthenticationError(
|
|
1161
|
+
f"DOCX Studio takes no Athena runtime credential, and this client "
|
|
1162
|
+
f"carries no token of {asset_id}. ATHENA_API_KEY here is a computer's, "
|
|
1163
|
+
"session's or notebook's own key, which only Athena's public API "
|
|
1164
|
+
"accepts. Open the document by its asset id (asset_...) with "
|
|
1165
|
+
"ATHENA_API_URL set, or set ATHENA_DOCX_API_KEY (or pass api_key=) to "
|
|
1166
|
+
"an Athena API key DOCX Studio accepts."
|
|
1167
|
+
)
|
|
1153
1168
|
try:
|
|
1154
1169
|
return send(bearer)
|
|
1155
1170
|
except AuthenticationError as exc:
|
|
@@ -1160,7 +1175,7 @@ class HttpClient:
|
|
|
1160
1175
|
minter.invalidate(token=refused, access=granted_access)
|
|
1161
1176
|
try:
|
|
1162
1177
|
fresh = minter.token(asset_id=asset_id, access=granted_access)
|
|
1163
|
-
except SigningUnavailable:
|
|
1178
|
+
except (SigningUnavailable, MintRouteMissing):
|
|
1164
1179
|
raise exc from None
|
|
1165
1180
|
if fresh.token == refused.token:
|
|
1166
1181
|
raise
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"""Creating a Word document through Athena's public API when no studio key is set.
|
|
2
|
+
|
|
3
|
+
``Document.create()`` creates a document with the key every other call sends
|
|
4
|
+
(``api_key``, ``ATHENA_DOCX_API_KEY``, then ``ATHENA_API_KEY``), except when
|
|
5
|
+
neither of the first two is set and ``ATHENA_API_KEY`` and ``ATHENA_API_URL``
|
|
6
|
+
both are. That is a computer's or a session VM's scoped credential, which
|
|
7
|
+
Athena's public API (``/api/v0``) takes and neither docx-studio's ``/docs/empty``
|
|
8
|
+
nor agora's ``/api/docx-studio/assets`` does, so Athena creates the document:
|
|
9
|
+
|
|
10
|
+
* a document holding Athena's default template:
|
|
11
|
+
``POST {ATHENA_API_URL}/api/v0/assets/create`` with
|
|
12
|
+
``{"asset_type": "word_document", ...}``;
|
|
13
|
+
* a document holding a local ``.docx`` (``Document.create(docx=...)``):
|
|
14
|
+
``POST {ATHENA_API_URL}/api/v0/assets/create-from-file`` (multipart ``file``,
|
|
15
|
+
query ``title``, ``parent_folder_id``, ``workspace_id``, ``source_asset_id``).
|
|
16
|
+
|
|
17
|
+
Both send ``X-API-KEY: $ATHENA_API_KEY`` and, inside an agent run, the run's
|
|
18
|
+
``X-Athena-Session-Credential`` (``ATHENA_CLI_SESSION_KEY``), so the document's
|
|
19
|
+
first write records the run. Athena writes it with its own studio credential,
|
|
20
|
+
as the key's user, and answers once the document holds its content; its own
|
|
21
|
+
calls then carry a token minted for it (``docx._asset_token``).
|
|
22
|
+
|
|
23
|
+
An Athena older than these routes answers 422 naming the asset type, or
|
|
24
|
+
FastAPI's bare 404 or 405: the caller then creates through docx-studio and agora
|
|
25
|
+
with ``ATHENA_API_KEY``, as before (:class:`PublicCreateUnavailable`). Every
|
|
26
|
+
other refusal raises. A create is not resent once its request may have reached
|
|
27
|
+
Athena: a resend could make a second document.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import inspect
|
|
33
|
+
import os
|
|
34
|
+
from dataclasses import dataclass, field
|
|
35
|
+
from pathlib import Path
|
|
36
|
+
from typing import Any, TypedDict
|
|
37
|
+
|
|
38
|
+
import requests
|
|
39
|
+
from requests.adapters import HTTPAdapter
|
|
40
|
+
from urllib3.util.retry import Retry
|
|
41
|
+
|
|
42
|
+
from docx._asset_token import API_KEY_ENV, API_URL_ENV, STUDIO_KEY_ENV, public_api_base
|
|
43
|
+
from docx.errors import AuthenticationError, DocxError, SessionError
|
|
44
|
+
|
|
45
|
+
#: The credential the athena CLI's session VM exports for one ``execute`` call.
|
|
46
|
+
SESSION_CREDENTIAL_ENV = "ATHENA_CLI_SESSION_KEY"
|
|
47
|
+
#: The header Athena reads it from to name the agent run a write belongs to.
|
|
48
|
+
SESSION_CREDENTIAL_HEADER = "X-Athena-Session-Credential"
|
|
49
|
+
#: Athena waits for docx-studio's seed or import before it answers.
|
|
50
|
+
CREATE_TIMEOUT_SECONDS = 120.0
|
|
51
|
+
_DOCX_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
|
52
|
+
# What FastAPI answers for a path no route serves (404) or serves with other
|
|
53
|
+
# methods only (405: ``/assets/{asset_id}`` takes GET and PATCH).
|
|
54
|
+
_ROUTE_MISSING_BODIES = ({"detail": "Not Found"}, {"detail": "Method Not Allowed"})
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class PublicCreateUnavailable(Exception):
|
|
58
|
+
"""The Athena at ``ATHENA_API_URL`` predates creating a Word document through it."""
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class CreatedDocument(TypedDict):
|
|
62
|
+
"""What Athena answered: the new document's id, title and workspace."""
|
|
63
|
+
|
|
64
|
+
asset_id: str
|
|
65
|
+
title: str
|
|
66
|
+
workspace_id: str | None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass(frozen=True)
|
|
70
|
+
class PublicApi:
|
|
71
|
+
"""Athena's public API, reached with ``ATHENA_API_KEY``."""
|
|
72
|
+
|
|
73
|
+
base: str
|
|
74
|
+
api_key: str = field(repr=False)
|
|
75
|
+
|
|
76
|
+
@classmethod
|
|
77
|
+
def for_creation(cls, *, api_key: str | None) -> PublicApi | None:
|
|
78
|
+
"""The public API a new document is created through, or None.
|
|
79
|
+
|
|
80
|
+
None when the caller passed ``api_key`` or ``ATHENA_DOCX_API_KEY`` is
|
|
81
|
+
set (they create on docx-studio, as before), or when ``ATHENA_API_KEY``
|
|
82
|
+
or ``ATHENA_API_URL`` is unset.
|
|
83
|
+
"""
|
|
84
|
+
if api_key or os.environ.get(STUDIO_KEY_ENV):
|
|
85
|
+
return None
|
|
86
|
+
athena_key = os.environ.get(API_KEY_ENV)
|
|
87
|
+
api_url = os.environ.get(API_URL_ENV)
|
|
88
|
+
if not athena_key or not api_url or not api_url.strip():
|
|
89
|
+
return None
|
|
90
|
+
return cls(base=public_api_base(api_url), api_key=athena_key)
|
|
91
|
+
|
|
92
|
+
def headers(self) -> dict[str, str]:
|
|
93
|
+
from docx import __version__
|
|
94
|
+
|
|
95
|
+
headers = {
|
|
96
|
+
"X-API-KEY": self.api_key,
|
|
97
|
+
"Accept": "application/json",
|
|
98
|
+
"User-Agent": f"athena-python-docx/{__version__}",
|
|
99
|
+
}
|
|
100
|
+
session_credential = os.environ.get(SESSION_CREDENTIAL_ENV)
|
|
101
|
+
if session_credential:
|
|
102
|
+
headers[SESSION_CREDENTIAL_HEADER] = session_credential
|
|
103
|
+
return headers
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _create_session() -> requests.Session:
|
|
107
|
+
"""Connection retries only: a create that may have reached Athena is never resent."""
|
|
108
|
+
retry_kwargs: dict[str, Any] = {
|
|
109
|
+
"total": None,
|
|
110
|
+
"status": 0,
|
|
111
|
+
"connect": 2,
|
|
112
|
+
"read": 0,
|
|
113
|
+
"other": 0,
|
|
114
|
+
"backoff_factor": 0.5,
|
|
115
|
+
"allowed_methods": frozenset(["POST"]),
|
|
116
|
+
"raise_on_status": False,
|
|
117
|
+
}
|
|
118
|
+
if "backoff_max" in inspect.signature(Retry).parameters:
|
|
119
|
+
retry_kwargs["backoff_max"] = 5.0
|
|
120
|
+
session = requests.Session()
|
|
121
|
+
adapter = HTTPAdapter(max_retries=Retry(**retry_kwargs))
|
|
122
|
+
session.mount("http://", adapter)
|
|
123
|
+
session.mount("https://", adapter)
|
|
124
|
+
return session
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _json(response: requests.Response) -> Any:
|
|
128
|
+
try:
|
|
129
|
+
return response.json()
|
|
130
|
+
except ValueError:
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _detail(body: Any, response: requests.Response) -> str:
|
|
135
|
+
"""Athena's reason: ``detail`` as text, or the first validation message."""
|
|
136
|
+
detail = body.get("detail") if isinstance(body, dict) else None
|
|
137
|
+
if isinstance(detail, str):
|
|
138
|
+
return detail.strip()
|
|
139
|
+
if isinstance(detail, list) and detail and isinstance(detail[0], dict):
|
|
140
|
+
message = detail[0].get("msg")
|
|
141
|
+
if isinstance(message, str):
|
|
142
|
+
return message.strip()
|
|
143
|
+
return " ".join((response.text or "").split())[:200]
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _is_unavailable(*, status: int, body: Any) -> bool:
|
|
147
|
+
"""An Athena that predates the route, or the ``word_document`` asset type."""
|
|
148
|
+
if status in (404, 405) and body in _ROUTE_MISSING_BODIES:
|
|
149
|
+
return True
|
|
150
|
+
if status != 422 or not isinstance(body, dict):
|
|
151
|
+
return False
|
|
152
|
+
errors = body.get("detail")
|
|
153
|
+
return isinstance(errors, list) and any(
|
|
154
|
+
isinstance(error, dict)
|
|
155
|
+
and "asset_type" in (error.get("loc") or [])
|
|
156
|
+
and "Unsupported asset type" in str(error.get("msg", ""))
|
|
157
|
+
for error in errors
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _created(response: requests.Response, *, what: str) -> CreatedDocument:
|
|
162
|
+
status = response.status_code
|
|
163
|
+
body = _json(response)
|
|
164
|
+
if _is_unavailable(status=status, body=body):
|
|
165
|
+
raise PublicCreateUnavailable(f"{response.url} answered HTTP {status}")
|
|
166
|
+
if status == 200 and isinstance(body, dict):
|
|
167
|
+
asset_id = body.get("asset_id")
|
|
168
|
+
title = body.get("title")
|
|
169
|
+
workspace_id = body.get("workspace_id")
|
|
170
|
+
if (
|
|
171
|
+
isinstance(asset_id, str)
|
|
172
|
+
and asset_id.startswith("asset_")
|
|
173
|
+
and isinstance(title, str)
|
|
174
|
+
and (workspace_id is None or isinstance(workspace_id, str))
|
|
175
|
+
):
|
|
176
|
+
return CreatedDocument(asset_id=asset_id, title=title, workspace_id=workspace_id)
|
|
177
|
+
detail = _detail(body, response)
|
|
178
|
+
if status in (401, 403):
|
|
179
|
+
raise AuthenticationError(
|
|
180
|
+
f"Athena refused to create {what} with ATHENA_API_KEY (HTTP {status})"
|
|
181
|
+
f"{': ' + detail if detail else ''}",
|
|
182
|
+
)
|
|
183
|
+
if status == 200:
|
|
184
|
+
raise SessionError(f"Athena's answer to creating {what} is not one this SDK can use: {detail}")
|
|
185
|
+
raise DocxError(f"Athena could not create {what} (HTTP {status}){': ' + detail if detail else ''}")
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _post(
|
|
189
|
+
*,
|
|
190
|
+
session: requests.Session,
|
|
191
|
+
url: str,
|
|
192
|
+
what: str,
|
|
193
|
+
**kwargs: Any,
|
|
194
|
+
) -> CreatedDocument:
|
|
195
|
+
try:
|
|
196
|
+
response = session.post(url, **kwargs)
|
|
197
|
+
except requests.RequestException as exc:
|
|
198
|
+
raise SessionError(f"Unable to reach Athena at {url} to create {what}: {exc}") from exc
|
|
199
|
+
try:
|
|
200
|
+
return _created(response, what=what)
|
|
201
|
+
finally:
|
|
202
|
+
response.close()
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def create_word_document(
|
|
206
|
+
*,
|
|
207
|
+
api: PublicApi,
|
|
208
|
+
name: str | None,
|
|
209
|
+
parent_folder_id: str | None,
|
|
210
|
+
workspace_id: str | None,
|
|
211
|
+
) -> CreatedDocument:
|
|
212
|
+
"""Have Athena create a Word document holding its default template."""
|
|
213
|
+
payload: dict[str, str] = {"asset_type": "word_document"}
|
|
214
|
+
if name is not None:
|
|
215
|
+
payload["title"] = name
|
|
216
|
+
if parent_folder_id is not None:
|
|
217
|
+
payload["parent_folder_id"] = parent_folder_id
|
|
218
|
+
if workspace_id is not None:
|
|
219
|
+
payload["workspace_id"] = workspace_id
|
|
220
|
+
session = _create_session()
|
|
221
|
+
try:
|
|
222
|
+
return _post(
|
|
223
|
+
session=session,
|
|
224
|
+
url=f"{api.base}/assets/create",
|
|
225
|
+
what="a Word document",
|
|
226
|
+
json=payload,
|
|
227
|
+
headers=api.headers(),
|
|
228
|
+
timeout=CREATE_TIMEOUT_SECONDS,
|
|
229
|
+
)
|
|
230
|
+
finally:
|
|
231
|
+
session.close()
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def create_word_document_from_file(
|
|
235
|
+
*,
|
|
236
|
+
api: PublicApi,
|
|
237
|
+
docx_path: str,
|
|
238
|
+
title: str,
|
|
239
|
+
parent_folder_id: str | None,
|
|
240
|
+
workspace_id: str | None,
|
|
241
|
+
source_asset_id: str | None,
|
|
242
|
+
timeout: float,
|
|
243
|
+
) -> CreatedDocument:
|
|
244
|
+
"""Have Athena create a Word document holding the ``.docx`` at ``docx_path``.
|
|
245
|
+
|
|
246
|
+
Athena answers once the file is imported, and leaves nothing behind when the
|
|
247
|
+
import fails.
|
|
248
|
+
"""
|
|
249
|
+
params: dict[str, str] = {"title": title}
|
|
250
|
+
if parent_folder_id is not None:
|
|
251
|
+
params["parent_folder_id"] = parent_folder_id
|
|
252
|
+
if workspace_id is not None:
|
|
253
|
+
params["workspace_id"] = workspace_id
|
|
254
|
+
if source_asset_id is not None:
|
|
255
|
+
params["source_asset_id"] = source_asset_id
|
|
256
|
+
file_path = Path(docx_path)
|
|
257
|
+
session = _create_session()
|
|
258
|
+
try:
|
|
259
|
+
with file_path.open("rb") as handle:
|
|
260
|
+
return _post(
|
|
261
|
+
session=session,
|
|
262
|
+
url=f"{api.base}/assets/create-from-file",
|
|
263
|
+
what=f"a Word document from {file_path.name}",
|
|
264
|
+
params=params,
|
|
265
|
+
files={"file": (file_path.name, handle, _DOCX_CONTENT_TYPE)},
|
|
266
|
+
headers=api.headers(),
|
|
267
|
+
timeout=max(timeout, CREATE_TIMEOUT_SECONDS),
|
|
268
|
+
)
|
|
269
|
+
finally:
|
|
270
|
+
session.close()
|
|
@@ -282,7 +282,11 @@ class Document:
|
|
|
282
282
|
api_key: Athena API key (PropelAuth user API key) or
|
|
283
283
|
access token. Falls back to ``$ATHENA_DOCX_API_KEY``, then
|
|
284
284
|
``$ATHENA_API_KEY``, as every other call does; the upload path
|
|
285
|
-
(``docx=``) sends the same key to agora.
|
|
285
|
+
(``docx=``) sends the same key to agora. With neither of the
|
|
286
|
+
first two set and both ``$ATHENA_API_KEY`` and
|
|
287
|
+
``$ATHENA_API_URL`` set (a computer's or session VM's scoped
|
|
288
|
+
key), Athena's public API creates the document instead, from
|
|
289
|
+
the template or from ``docx`` (``docx._public_create``).
|
|
286
290
|
parent_folder_id: Optional parent folder; defaults to
|
|
287
291
|
the source document's folder when source_asset_id is set,
|
|
288
292
|
otherwise workspace root.
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "athena-python-docx"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.45.1"
|
|
8
8
|
description = "Drop-in replacement for python-docx that connects to Athena's DOCX Studio collaborative document stack"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|