athena-python-docx 0.42.1__tar.gz → 0.44.0__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.42.1 → athena_python_docx-0.44.0}/PKG-INFO +8 -1
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/README.md +7 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/__init__.py +1 -1
- athena_python_docx-0.44.0/docx/_asset_token.py +354 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/_http.py +32 -32
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/_http_doc.py +134 -33
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/client.py +12 -9
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/document.py +9 -2
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/pyproject.toml +1 -1
- athena_python_docx-0.44.0/tests/test_asset_tokens.py +468 -0
- athena_python_docx-0.44.0/tests/test_create_workspace.py +82 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_document_create.py +32 -15
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_document_create_from_template.py +84 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/uv.lock +1 -1
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/.gitignore +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/AGENTS.md +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/CLAUDE.md +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/DOCX_EXEC_LAB.md +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/_athena_extension.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/_batching.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/_buffer.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/_capabilities.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/_citations.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/_execution.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/_image_utils.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/_postproc.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/_proxy.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/_ptc.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/_references.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/_run_order.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/_table_styles.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/_timeouts.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/_tracked.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/_xml_text.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/api.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/blkcntnr.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/bookmarks.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/charts.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/commands.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/comments.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/drawing/__init__.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/enum/__init__.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/enum/base.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/enum/dml.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/enum/section.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/enum/shape.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/enum/style.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/enum/table.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/enum/text.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/errors.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/exceptions.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/fields.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/footnotes.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/image/__init__.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/image/exceptions.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/image/image.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/math.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/opc/__init__.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/opc/coreprops.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/oxml/__init__.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/revisions.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/sdt.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/section.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/session.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/settings.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/shape.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/shared.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/styles/__init__.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/styles/_table.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/styles/latent.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/styles/style.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/styles/styles.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/table.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/text/__init__.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/text/font.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/text/hyperlink.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/text/pagebreak.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/text/paragraph.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/text/parfmt.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/text/run.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/text/tabstops.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/toc.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/docx/typing.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/scripts/docx_exec_lab.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/scripts/docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/scripts/dump_wire_fixtures.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/scripts/release.sh +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/scripts/round_trip_smoke.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/scripts/smoke_test_block_not_found.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/scripts/validate_find_replace_asset.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/__init__.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/conftest.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/METHODOLOGY.md +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/README.md +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/__init__.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/__init__.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/agent_documents.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/default_style_run_formatting.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/header_footer_pictures.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/less_used_writes.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/merged_cell_identity.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/paragraph_text_setter.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/picture_runs.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/proxy_equality.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/return_types.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/xml_incompatible_text.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/zero_and_negative_indents.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe_cases.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe_runner.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/auto_gen_cases.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/binary_round_trip.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/cases.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/complex_cases.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/coverage_report.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/extract.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/extreme_cases.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/fake_session.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/firm_templates/README.md +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/firm_templates/__init__.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/firm_templates/_runner.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/firm_templates/extractor.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/local_runner.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/mega_cases.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshot.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw16_header_footer_tables.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw17_header_letterhead_filled.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw18_header_table_properties.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw19_header_table_rows_columns.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw20_header_table_merges.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/ours_spec.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/parity_crawl.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/parity_diff.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/real_world_cases.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/round_trip_tests.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/stock_spec.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fixtures/field_reads.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fixtures/note_reference_reads.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fixtures/ruby_reads.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/fixtures/symbol_reads.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/live/__init__.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/live/baseline.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/live/fixture_scripts.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/live/reader.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/live/runner.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/live/sdk_driver.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/live/stock_driver.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/live/test_live_runner.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/parity/README.md +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/parity/__init__.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/parity/baseline_gaps.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/parity/compare.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/parity/intentional_deviations.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/parity/introspect.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/parity/reports/gap_report.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/parity/run_parity.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/parity/snapshots/athena_latest.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/parity/test_parity_gap.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_add_section.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_add_section_extract_items.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_alignment_values.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_athena_extension_creates_session_open.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_athena_extensions_contract.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_athena_extensions_registry.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_batch_chunking.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_batching_perf.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_battery_cases.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_block_not_found_error.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_buffer.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_cell_add_table_not_supported.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_cell_borders_spec.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_cell_fastpath_runs.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_cell_inner_format_stash.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_cell_inner_run_format_stash.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_cell_inner_run_guard.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_cell_merge.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_cell_nested_tables.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_cell_paragraphs_listed.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_cell_text_plain_fastpath.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_cell_text_replace_semantics.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_citations.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_collapsed_range_format.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_color_format_theme.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_command_dataclasses.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_commands.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_comment_paragraphs.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_comments.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_comments_first_read.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_content_control_value.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_core_properties.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_document_asset_id_property.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_document_clear.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_document_factory_validation.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_document_settings_session_open.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_docx_exec_lab.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_drawing_image.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_empty_hyperlinks.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_enum_parity.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_execution_scope.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_export_docx_clean.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_export_rescue_anchors.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_field_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_field_refresh.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_field_runs.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_find_replace_session_open.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_flushed_write_failures.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_http_transport.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_hyperlink_coalescing.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_image_url_data_uri.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_inline_page_break.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_inline_shape_alt_text.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_inline_shape_size.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_inline_shape_type.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_inline_shapes_floating.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_insert_deferred.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_iter_inner_content.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_latent_style_writes.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_line_clear_break.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_list_styles.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_listed_paragraph_reads.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_literal_no_break_hyphens.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_literal_soft_hyphens.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_merged_cell_identity.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_merged_cell_secondary_slot.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_merged_cells.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_missing_modules.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_nested_table_refused.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_note_reference_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_oxml_shim.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_page_break.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_paragraph_delete.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_paragraph_indent_reads.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_paragraph_runs_hyperlinks.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_paragraph_text_empty_run.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_paragraph_text_len_cache.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_parfmt_clears.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_parity_misc.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_parity_round2.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_pdf_export_and_page_renders.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_phase_a_behavior.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_phase_b_headers_footers.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_phase_c_tables.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_positional_cell_id.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_postproc_cell_format_rewrite.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_postproc_ref_restore.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_pr19766_review_fixes.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_proxy_equality.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_ptc.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_python_docx_api_parity.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_read_run_ranges.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_rendered_page_breaks.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_repeated_node_read.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_restart_numbering.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_revisions.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_round_trip_smoke_script.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_ruby_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_run_add_picture_anchor.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_run_break_inner_content.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_run_edges.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_run_formatting_wire.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_run_identity.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_run_later_runs.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_run_mark_inheritance.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_run_page_number_blocks.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_run_table_style_objects.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_run_text_python_docx.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_run_text_refreshes_paragraph.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_run_unmodeled_toggles.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_sdk_reads_served.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_section_read_lengths.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_sections_empty_list.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_add_picture.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_add_run.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_comments_get.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_document_audit.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_document_element.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_enum_section.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_font_audit.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_header_footer.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_hyperlink.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_inline_shape.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_misc.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_paragraph_style.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_parfmt.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_row_col_cell.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_run_add_break.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_run_style.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_run_style_strict.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_run_text.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_run_underline.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_section_audit.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_section_dimensions.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_section_onoff.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_settings.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_shared_audit.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_style.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_styles.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_table_audit.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_table_cell.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_table_dimensions.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_silent_stub_table_layout.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_smoke_integration.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_style_acceptance.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_style_delete.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_style_font.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_style_formatting_writes.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_style_metadata_writes.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_style_names.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_style_resolution.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_style_setters_contract.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_style_tab_stops.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_style_tab_stops_read.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_symbol_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_tab_stop_writes.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_table_add_row_grid_row.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_table_anchors.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_table_clears.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_table_direction.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_table_length_reads.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_table_listing_reads.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_table_read_reuse.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_table_set_cell_perf.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_table_style_id_resolution.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_temporarily_unavailable.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_text_control_characters.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_tracked_revisions_offsets.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_unmapped_alignment_reads.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_uv_lock_version.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_validate_find_replace_asset_script.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_versioned_read_reuse.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_wire_contract.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_xml_attr_guard.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/tests/test_xml_incompatible_text.py +0 -0
- {athena_python_docx-0.42.1 → athena_python_docx-0.44.0}/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.44.0
|
|
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>
|
|
@@ -81,6 +81,13 @@ Required when connecting to DOCX Studio (set by the Athena backend in the `execu
|
|
|
81
81
|
- `ATHENA_DOCX_BASE_URL` — DOCX Studio API base URL
|
|
82
82
|
- `ATHENA_DOCX_API_KEY` — Athena API key or service-issued DOCX Studio token
|
|
83
83
|
|
|
84
|
+
Without `ATHENA_DOCX_API_KEY` (and without `api_key=`), the SDK uses
|
|
85
|
+
`ATHENA_API_KEY`. When `ATHENA_API_URL` is set too (the bare Athena URL or one
|
|
86
|
+
ending in `/api/v0`), an opened document's calls carry a 15-minute token Athena
|
|
87
|
+
mints for that document alone (`POST /api/v0/assets/{asset_id}/studio-token`),
|
|
88
|
+
read-only for reads; `Document.create()` and its upload still send the key
|
|
89
|
+
(`docx/_asset_token.py`).
|
|
90
|
+
|
|
84
91
|
## License
|
|
85
92
|
|
|
86
93
|
MIT
|
|
@@ -56,6 +56,13 @@ Required when connecting to DOCX Studio (set by the Athena backend in the `execu
|
|
|
56
56
|
- `ATHENA_DOCX_BASE_URL` — DOCX Studio API base URL
|
|
57
57
|
- `ATHENA_DOCX_API_KEY` — Athena API key or service-issued DOCX Studio token
|
|
58
58
|
|
|
59
|
+
Without `ATHENA_DOCX_API_KEY` (and without `api_key=`), the SDK uses
|
|
60
|
+
`ATHENA_API_KEY`. When `ATHENA_API_URL` is set too (the bare Athena URL or one
|
|
61
|
+
ending in `/api/v0`), an opened document's calls carry a 15-minute token Athena
|
|
62
|
+
mints for that document alone (`POST /api/v0/assets/{asset_id}/studio-token`),
|
|
63
|
+
read-only for reads; `Document.create()` and its upload still send the key
|
|
64
|
+
(`docx/_asset_token.py`).
|
|
65
|
+
|
|
59
66
|
## License
|
|
60
67
|
|
|
61
68
|
MIT
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
"""Per-document DOCX Studio tokens, minted from Athena's public API.
|
|
2
|
+
|
|
3
|
+
A call on an existing Word document reaches DOCX Studio with, in this order:
|
|
4
|
+
|
|
5
|
+
1. the ``api_key`` the caller passed;
|
|
6
|
+
2. the studio's own key, ``ATHENA_DOCX_API_KEY``;
|
|
7
|
+
3. when ``ATHENA_API_KEY`` and ``ATHENA_API_URL`` are both set, a token for that
|
|
8
|
+
document alone, minted by
|
|
9
|
+
``POST {ATHENA_API_URL}/api/v0/assets/{asset_id}/studio-token`` with
|
|
10
|
+
``X-API-KEY: $ATHENA_API_KEY`` and sent as ``Authorization: Bearer``. It names
|
|
11
|
+
the caller, the document, one access level and DOCX Studio, and lasts 15
|
|
12
|
+
minutes;
|
|
13
|
+
4. otherwise ``ATHENA_API_KEY`` itself, as before.
|
|
14
|
+
|
|
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. A token goes only to its
|
|
17
|
+
own document's routes.
|
|
18
|
+
|
|
19
|
+
A token is minted once per (agora, key, asset, access) for the whole process,
|
|
20
|
+
again 90 seconds before it expires, and once more when the studio answers 401.
|
|
21
|
+
A read mints a read-only token (``{"access": "view"}``: the version check, the
|
|
22
|
+
layout, page renders, the clean export), a write a read-write one
|
|
23
|
+
(``{"access": "edit"}``, which Athena grants only to a caller who may edit).
|
|
24
|
+
Every command batch is a write to DOCX Studio, whose ``/commands`` route needs
|
|
25
|
+
edit access even for a batch that only queries. ``ATHENA_API_URL`` ends in
|
|
26
|
+
``/api/v0`` inside an Athena computer and is the bare agora URL in a session VM
|
|
27
|
+
or locally; both work.
|
|
28
|
+
|
|
29
|
+
Athena's refusals (400, 401, 403, 404, 422) raise at once, without a retry. A
|
|
30
|
+
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.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
import hashlib
|
|
37
|
+
import inspect
|
|
38
|
+
import os
|
|
39
|
+
import threading
|
|
40
|
+
import time
|
|
41
|
+
from collections import OrderedDict
|
|
42
|
+
from dataclasses import dataclass, field
|
|
43
|
+
from typing import Any
|
|
44
|
+
from urllib.parse import quote
|
|
45
|
+
|
|
46
|
+
import requests
|
|
47
|
+
from requests.adapters import HTTPAdapter
|
|
48
|
+
from urllib3.util.retry import Retry
|
|
49
|
+
|
|
50
|
+
from docx.errors import (
|
|
51
|
+
AuthenticationError,
|
|
52
|
+
DocxError,
|
|
53
|
+
DocxStudioTemporarilyUnavailable,
|
|
54
|
+
NotFoundError,
|
|
55
|
+
SessionError,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
API_KEY_ENV = "ATHENA_API_KEY"
|
|
59
|
+
API_URL_ENV = "ATHENA_API_URL"
|
|
60
|
+
STUDIO_KEY_ENV = "ATHENA_DOCX_API_KEY"
|
|
61
|
+
#: The token audience DOCX Studio verifies.
|
|
62
|
+
AUDIENCE = "docx-studio"
|
|
63
|
+
TOKEN_PREFIX = "aat_"
|
|
64
|
+
PUBLIC_API_PREFIX = "/api/v0"
|
|
65
|
+
|
|
66
|
+
#: The access a token is minted for.
|
|
67
|
+
VIEW = "view"
|
|
68
|
+
EDIT = "edit"
|
|
69
|
+
|
|
70
|
+
#: Minted again this long before Athena's ``expires_at_ms``.
|
|
71
|
+
REFRESH_BEFORE_EXPIRY_SECONDS = 90.0
|
|
72
|
+
#: The longest a token is held, whatever the local clock says (Athena's lifetime).
|
|
73
|
+
MAX_LIFETIME_SECONDS = 15 * 60.0
|
|
74
|
+
#: The shortest, so a clock running ahead of Athena's cannot mint per call.
|
|
75
|
+
MIN_LIFETIME_SECONDS = 30.0
|
|
76
|
+
MINT_TIMEOUT_SECONDS = 30.0
|
|
77
|
+
_MAX_CACHED_TOKENS = 256
|
|
78
|
+
_LOCK_STRIPES = 16
|
|
79
|
+
|
|
80
|
+
_KIND_BY_AUDIENCE = {
|
|
81
|
+
AUDIENCE: "a Word document",
|
|
82
|
+
"pptx-studio": "a presentation",
|
|
83
|
+
"xlsx-studio": "a spreadsheet",
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def public_api_base(url: str) -> str:
|
|
88
|
+
"""The public API base for ``url``, whether or not it already ends in it."""
|
|
89
|
+
base = url.strip().rstrip("/")
|
|
90
|
+
if base.endswith(PUBLIC_API_PREFIX):
|
|
91
|
+
return base
|
|
92
|
+
return base + PUBLIC_API_PREFIX
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@dataclass(frozen=True)
|
|
96
|
+
class StudioToken:
|
|
97
|
+
"""One minted token and what Athena said about it."""
|
|
98
|
+
|
|
99
|
+
token: str = field(repr=False)
|
|
100
|
+
asset_id: str
|
|
101
|
+
audience: str
|
|
102
|
+
#: ``"r"`` (reads only) or ``"rw"``.
|
|
103
|
+
access_type: str
|
|
104
|
+
#: ``time.monotonic()`` after which the token is minted again.
|
|
105
|
+
refresh_at: float
|
|
106
|
+
|
|
107
|
+
def is_fresh(self) -> bool:
|
|
108
|
+
return time.monotonic() < self.refresh_at
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class SigningUnavailable(Exception):
|
|
112
|
+
"""Athena's 503: this deployment signs no asset tokens."""
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class _TokenCache:
|
|
116
|
+
"""Process-wide tokens by (agora, key, asset, access), least recently used first."""
|
|
117
|
+
|
|
118
|
+
def __init__(self, max_entries: int) -> None:
|
|
119
|
+
self._entries: OrderedDict[tuple[str, str, str, str], StudioToken] = OrderedDict()
|
|
120
|
+
self._max_entries = max_entries
|
|
121
|
+
self._lock = threading.Lock()
|
|
122
|
+
# Minting holds one stripe, so threads wanting the same token wait for
|
|
123
|
+
# one mint instead of each minting their own.
|
|
124
|
+
self._mint_locks = tuple(threading.Lock() for _ in range(_LOCK_STRIPES))
|
|
125
|
+
|
|
126
|
+
def get(self, key: tuple[str, str, str, str]) -> StudioToken | None:
|
|
127
|
+
with self._lock:
|
|
128
|
+
token = self._entries.get(key)
|
|
129
|
+
if token is not None:
|
|
130
|
+
self._entries.move_to_end(key)
|
|
131
|
+
return token
|
|
132
|
+
|
|
133
|
+
def put(self, key: tuple[str, str, str, str], token: StudioToken) -> None:
|
|
134
|
+
with self._lock:
|
|
135
|
+
self._entries[key] = token
|
|
136
|
+
self._entries.move_to_end(key)
|
|
137
|
+
while len(self._entries) > self._max_entries:
|
|
138
|
+
self._entries.popitem(last=False)
|
|
139
|
+
|
|
140
|
+
def discard(self, key: tuple[str, str, str, str], token: StudioToken) -> None:
|
|
141
|
+
"""Forget ``token`` unless another thread already replaced it."""
|
|
142
|
+
with self._lock:
|
|
143
|
+
if self._entries.get(key) is token:
|
|
144
|
+
del self._entries[key]
|
|
145
|
+
|
|
146
|
+
def mint_lock(self, key: tuple[str, str, str, str]) -> threading.Lock:
|
|
147
|
+
return self._mint_locks[hash(key) % _LOCK_STRIPES]
|
|
148
|
+
|
|
149
|
+
def clear(self) -> None:
|
|
150
|
+
with self._lock:
|
|
151
|
+
self._entries.clear()
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
_CACHE = _TokenCache(_MAX_CACHED_TOKENS)
|
|
155
|
+
|
|
156
|
+
_SESSION: requests.Session | None = None
|
|
157
|
+
_SESSION_LOCK = threading.Lock()
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _mint_session() -> requests.Session:
|
|
161
|
+
"""One session for every mint: connection retries, and status retries for
|
|
162
|
+
429/502/504. A 503 is Athena's answer, not a transient fault, so it is not
|
|
163
|
+
retried; minting is safe to resend (each mint only signs a new token)."""
|
|
164
|
+
global _SESSION
|
|
165
|
+
with _SESSION_LOCK:
|
|
166
|
+
if _SESSION is None:
|
|
167
|
+
retry_kwargs: dict[str, Any] = {
|
|
168
|
+
"total": None,
|
|
169
|
+
"status": 2,
|
|
170
|
+
"connect": 2,
|
|
171
|
+
"read": 0,
|
|
172
|
+
"other": 0,
|
|
173
|
+
"backoff_factor": 0.5,
|
|
174
|
+
"status_forcelist": (429, 502, 504),
|
|
175
|
+
"allowed_methods": frozenset(["POST"]),
|
|
176
|
+
"raise_on_status": False,
|
|
177
|
+
"respect_retry_after_header": True,
|
|
178
|
+
}
|
|
179
|
+
if "backoff_max" in inspect.signature(Retry).parameters:
|
|
180
|
+
retry_kwargs["backoff_max"] = 5.0
|
|
181
|
+
session = requests.Session()
|
|
182
|
+
adapter = HTTPAdapter(max_retries=Retry(**retry_kwargs))
|
|
183
|
+
session.mount("http://", adapter)
|
|
184
|
+
session.mount("https://", adapter)
|
|
185
|
+
_SESSION = session
|
|
186
|
+
return _SESSION
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _detail(response: requests.Response) -> str:
|
|
190
|
+
"""Athena's reason (``{"detail": ...}``), else a short excerpt of the body."""
|
|
191
|
+
try:
|
|
192
|
+
body = response.json()
|
|
193
|
+
except ValueError:
|
|
194
|
+
return " ".join((response.text or "").split())[:200]
|
|
195
|
+
detail = body.get("detail") if isinstance(body, dict) else None
|
|
196
|
+
if isinstance(detail, dict):
|
|
197
|
+
detail = detail.get("message")
|
|
198
|
+
if isinstance(detail, str):
|
|
199
|
+
return detail.strip()
|
|
200
|
+
return ""
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _lifetime_seconds(expires_at_ms: int) -> float:
|
|
204
|
+
"""How long to hold a token, from Athena's expiry and the local clock."""
|
|
205
|
+
remaining = expires_at_ms / 1000.0 - time.time()
|
|
206
|
+
return min(max(remaining, MIN_LIFETIME_SECONDS), MAX_LIFETIME_SECONDS)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class TokenMinter:
|
|
210
|
+
"""Mints the studio tokens one ``ATHENA_API_KEY`` holds, through the cache."""
|
|
211
|
+
|
|
212
|
+
def __init__(self, *, api_url: str, api_key: str) -> None:
|
|
213
|
+
self.base = public_api_base(api_url)
|
|
214
|
+
self._api_key = api_key
|
|
215
|
+
self._fingerprint = hashlib.sha256(api_key.encode("utf-8")).hexdigest()
|
|
216
|
+
|
|
217
|
+
@classmethod
|
|
218
|
+
def from_environment(cls) -> TokenMinter | None:
|
|
219
|
+
"""The minter for ``ATHENA_API_KEY`` at ``ATHENA_API_URL``; None when either is unset."""
|
|
220
|
+
api_key = os.environ.get(API_KEY_ENV)
|
|
221
|
+
api_url = os.environ.get(API_URL_ENV)
|
|
222
|
+
if not api_key or not api_url or not api_url.strip():
|
|
223
|
+
return None
|
|
224
|
+
return cls(api_url=api_url, api_key=api_key)
|
|
225
|
+
|
|
226
|
+
def _key(self, *, asset_id: str, access: str) -> tuple[str, str, str, str]:
|
|
227
|
+
return (self.base, self._fingerprint, asset_id, access)
|
|
228
|
+
|
|
229
|
+
def token(self, *, asset_id: str, access: str) -> StudioToken:
|
|
230
|
+
"""The token for ``asset_id`` at ``access``: the cached one while fresh,
|
|
231
|
+
else a new one. Raises when Athena refuses it, when it is for another
|
|
232
|
+
studio, and :class:`SigningUnavailable` on a 503."""
|
|
233
|
+
key = self._key(asset_id=asset_id, access=access)
|
|
234
|
+
token = _CACHE.get(key)
|
|
235
|
+
if token is None or not token.is_fresh():
|
|
236
|
+
with _CACHE.mint_lock(key):
|
|
237
|
+
token = _CACHE.get(key)
|
|
238
|
+
if token is None or not token.is_fresh():
|
|
239
|
+
token = self._mint(asset_id=asset_id, access=access)
|
|
240
|
+
_CACHE.put(key, token)
|
|
241
|
+
if token.audience != AUDIENCE:
|
|
242
|
+
raise DocxError(
|
|
243
|
+
f"Asset {asset_id} is not a Word document: Athena edits it through "
|
|
244
|
+
f"{token.audience}. Open it with the Athena SDK for "
|
|
245
|
+
f"{_KIND_BY_AUDIENCE.get(token.audience, 'that kind of asset')}."
|
|
246
|
+
)
|
|
247
|
+
return token
|
|
248
|
+
|
|
249
|
+
def invalidate(self, *, token: StudioToken, access: str) -> None:
|
|
250
|
+
"""Forget ``token`` (the studio answered 401 to it), so the next call mints."""
|
|
251
|
+
_CACHE.discard(self._key(asset_id=token.asset_id, access=access), token)
|
|
252
|
+
|
|
253
|
+
def _mint(self, *, asset_id: str, access: str) -> StudioToken:
|
|
254
|
+
url = f"{self.base}/assets/{quote(asset_id, safe='')}/studio-token"
|
|
255
|
+
try:
|
|
256
|
+
response = _mint_session().post(
|
|
257
|
+
url,
|
|
258
|
+
json={"access": access},
|
|
259
|
+
headers={"X-API-KEY": self._api_key, "Accept": "application/json"},
|
|
260
|
+
timeout=MINT_TIMEOUT_SECONDS,
|
|
261
|
+
)
|
|
262
|
+
except requests.RequestException as exc:
|
|
263
|
+
raise SessionError(
|
|
264
|
+
f"Unable to reach Athena at {url} to mint a DOCX Studio token for "
|
|
265
|
+
f"{asset_id}: {exc}"
|
|
266
|
+
) from exc
|
|
267
|
+
try:
|
|
268
|
+
return self._read(response, asset_id=asset_id)
|
|
269
|
+
finally:
|
|
270
|
+
response.close()
|
|
271
|
+
|
|
272
|
+
@staticmethod
|
|
273
|
+
def _read(response: requests.Response, *, asset_id: str) -> StudioToken:
|
|
274
|
+
status = response.status_code
|
|
275
|
+
if status == 503:
|
|
276
|
+
raise SigningUnavailable(_detail(response) or "This deployment signs no asset tokens.")
|
|
277
|
+
detail = _detail(response)
|
|
278
|
+
reason = f"(HTTP {status}{': ' + detail if detail else ''})"
|
|
279
|
+
if status == 401:
|
|
280
|
+
raise AuthenticationError(
|
|
281
|
+
f"Athena refused ATHENA_API_KEY when asked for a DOCX Studio token for "
|
|
282
|
+
f"{asset_id} {reason}. The key is invalid, expired or revoked; retrying "
|
|
283
|
+
"with it will not help."
|
|
284
|
+
)
|
|
285
|
+
if status == 403:
|
|
286
|
+
raise AuthenticationError(f"Athena refused a DOCX Studio token for {asset_id} {reason}.")
|
|
287
|
+
if status == 404:
|
|
288
|
+
raise NotFoundError(
|
|
289
|
+
f"Athena refused a DOCX Studio token for {asset_id} {reason}: the document "
|
|
290
|
+
"does not exist, or this key may not read it.",
|
|
291
|
+
payload={"status": 404, "body": detail},
|
|
292
|
+
)
|
|
293
|
+
if status in (400, 422):
|
|
294
|
+
raise DocxError(f"Athena refused a DOCX Studio token for {asset_id} {reason}.")
|
|
295
|
+
if not response.ok:
|
|
296
|
+
raise DocxStudioTemporarilyUnavailable(
|
|
297
|
+
f"Athena could not mint a DOCX Studio token for {asset_id} {reason}.",
|
|
298
|
+
http_status=status,
|
|
299
|
+
)
|
|
300
|
+
try:
|
|
301
|
+
body = response.json()
|
|
302
|
+
except ValueError:
|
|
303
|
+
body = None
|
|
304
|
+
if not isinstance(body, dict):
|
|
305
|
+
body = {}
|
|
306
|
+
token = body.get("token")
|
|
307
|
+
audience = body.get("audience")
|
|
308
|
+
access_type = body.get("access_type")
|
|
309
|
+
expires_at_ms = body.get("expires_at_ms")
|
|
310
|
+
if (
|
|
311
|
+
not isinstance(token, str)
|
|
312
|
+
or not token.startswith(TOKEN_PREFIX)
|
|
313
|
+
or not isinstance(audience, str)
|
|
314
|
+
or access_type not in ("r", "rw")
|
|
315
|
+
or not isinstance(expires_at_ms, int)
|
|
316
|
+
or isinstance(expires_at_ms, bool)
|
|
317
|
+
or body.get("asset_id") != asset_id
|
|
318
|
+
):
|
|
319
|
+
raise SessionError(
|
|
320
|
+
f"Athena's studio token answer for {asset_id} is not one this SDK can use "
|
|
321
|
+
"(expected token, audience, access_type, expires_at_ms and this asset id)."
|
|
322
|
+
)
|
|
323
|
+
return StudioToken(
|
|
324
|
+
token=token,
|
|
325
|
+
asset_id=asset_id,
|
|
326
|
+
audience=audience,
|
|
327
|
+
access_type=access_type,
|
|
328
|
+
refresh_at=time.monotonic()
|
|
329
|
+
+ max(
|
|
330
|
+
_lifetime_seconds(expires_at_ms) - REFRESH_BEFORE_EXPIRY_SECONDS,
|
|
331
|
+
MIN_LIFETIME_SECONDS,
|
|
332
|
+
),
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def studio_credentials(*, api_key: str | None) -> tuple[str | None, TokenMinter | None]:
|
|
337
|
+
"""What a client authenticates with: ``(key, minter)``.
|
|
338
|
+
|
|
339
|
+
``key`` serves every route the minter does not: ``api_key`` when the caller
|
|
340
|
+
passed one, else ``ATHENA_DOCX_API_KEY``, else ``ATHENA_API_KEY``. ``minter``
|
|
341
|
+
mints per-document tokens when neither of the first two is set and
|
|
342
|
+
``ATHENA_API_KEY`` and ``ATHENA_API_URL`` are.
|
|
343
|
+
"""
|
|
344
|
+
if api_key:
|
|
345
|
+
return api_key, None
|
|
346
|
+
studio_key = os.environ.get(STUDIO_KEY_ENV)
|
|
347
|
+
if studio_key:
|
|
348
|
+
return studio_key, None
|
|
349
|
+
return os.environ.get(API_KEY_ENV), TokenMinter.from_environment()
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _clear_cache() -> None:
|
|
353
|
+
"""Forget every cached token (tests)."""
|
|
354
|
+
_CACHE.clear()
|
|
@@ -38,6 +38,20 @@ _API_KEY_ENV = "ATHENA_DOCX_API_KEY" # noqa: S105
|
|
|
38
38
|
_AGORA_BASE_URL_ENV = "ATHENA_AGORA_BASE_URL"
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
def _resolve_api_key(api_key: str | None) -> str | None:
|
|
42
|
+
"""The key a create or upload sends: ``api_key``, else the studio key.
|
|
43
|
+
|
|
44
|
+
``ATHENA_DOCX_API_KEY`` comes first, as on every other call this SDK
|
|
45
|
+
makes. Both create paths land outside Athena's public API (docx-studio's
|
|
46
|
+
``/docs/empty``, agora's ``/api/docx-studio/assets``), and inside an Athena
|
|
47
|
+
session VM or computer ``ATHENA_API_KEY`` is a credential only that public
|
|
48
|
+
API (``/api/v0``) accepts. ``ATHENA_API_KEY`` stays the fallback for an
|
|
49
|
+
environment that sets no studio key (local use with one personal key).
|
|
50
|
+
The new document is owned by the user the key belongs to.
|
|
51
|
+
"""
|
|
52
|
+
return api_key or os.environ.get(_API_KEY_ENV) or os.environ.get("ATHENA_API_KEY")
|
|
53
|
+
|
|
54
|
+
|
|
41
55
|
def _derive_agora_base_url(docx_base_url: str) -> str | None:
|
|
42
56
|
"""Best-effort derive an Agora base URL from a docx-studio base URL.
|
|
43
57
|
|
|
@@ -99,11 +113,11 @@ def create_empty_document(
|
|
|
99
113
|
Falls back to ``$ATHENA_DOCX_BASE_URL``.
|
|
100
114
|
api_key: Athena API key (PropelAuth user API key) or PropelAuth
|
|
101
115
|
access token. Sent as ``Authorization: Bearer <api_key>``.
|
|
102
|
-
Falls back to ``$ATHENA_DOCX_API_KEY``.
|
|
116
|
+
Falls back to ``$ATHENA_DOCX_API_KEY``, then ``$ATHENA_API_KEY``.
|
|
103
117
|
name: Optional display title.
|
|
104
118
|
parent_folder_id: Optional parent folder; defaults to workspace root.
|
|
105
|
-
workspace_id: Optional workspace UUID; defaults to
|
|
106
|
-
current workspace.
|
|
119
|
+
workspace_id: Optional workspace UUID; defaults to the sandbox run's
|
|
120
|
+
ATHENA_WORKSPACE_ID, then the caller's current workspace.
|
|
107
121
|
timeout: Request timeout in seconds.
|
|
108
122
|
|
|
109
123
|
Returns:
|
|
@@ -115,17 +129,7 @@ def create_empty_document(
|
|
|
115
129
|
DocxError: any other 4xx/5xx from the server.
|
|
116
130
|
"""
|
|
117
131
|
resolved_base: str | None = base_url or os.environ.get(_BASE_URL_ENV)
|
|
118
|
-
|
|
119
|
-
# is the user, not a service identity. ``ATHENA_API_KEY`` is the canonical
|
|
120
|
-
# per-user Athena key (a local API key in BYO/SSO mode that agora resolves to
|
|
121
|
-
# the real user); the docx-studio studio key (``ATHENA_DOCX_API_KEY``) is a
|
|
122
|
-
# deployment service credential used only for studio access-checks on
|
|
123
|
-
# EXISTING assets, and agora's create gate rejects it (no backing user). So
|
|
124
|
-
# prefer the user key here; only fall back to the studio key for callers that
|
|
125
|
-
# set just that.
|
|
126
|
-
resolved_key: str | None = (
|
|
127
|
-
api_key or os.environ.get("ATHENA_API_KEY") or os.environ.get(_API_KEY_ENV)
|
|
128
|
-
)
|
|
132
|
+
resolved_key: str | None = _resolve_api_key(api_key)
|
|
129
133
|
|
|
130
134
|
if not resolved_base:
|
|
131
135
|
raise SessionError(
|
|
@@ -149,8 +153,9 @@ def create_empty_document(
|
|
|
149
153
|
body["name"] = name
|
|
150
154
|
if parent_folder_id is not None:
|
|
151
155
|
body["parentFolderId"] = parent_folder_id
|
|
152
|
-
|
|
153
|
-
|
|
156
|
+
resolved_workspace = workspace_id or os.environ.get("ATHENA_WORKSPACE_ID")
|
|
157
|
+
if resolved_workspace:
|
|
158
|
+
body["workspaceId"] = resolved_workspace
|
|
154
159
|
|
|
155
160
|
# Lazy import — _http is loaded lazily by Document.create(), so the
|
|
156
161
|
# package is fully initialized by the time we reach this code.
|
|
@@ -232,6 +237,7 @@ def upload_document(
|
|
|
232
237
|
name: str | None = None,
|
|
233
238
|
parent_folder_id: str | None = None,
|
|
234
239
|
workspace_id: str | None = None,
|
|
240
|
+
source_asset_id: str | None = None,
|
|
235
241
|
timeout: float = 120.0,
|
|
236
242
|
) -> UploadAssetResult:
|
|
237
243
|
"""Upload a local ``.docx`` to agora and create a new Word document asset.
|
|
@@ -247,11 +253,12 @@ def upload_document(
|
|
|
247
253
|
agora_base_url: Explicit Agora base URL. Falls back to
|
|
248
254
|
``$ATHENA_AGORA_BASE_URL`` and finally to a host substitution
|
|
249
255
|
of ``docx-studio`` → ``agora`` against ``docx_base_url``.
|
|
250
|
-
api_key: Athena API key. Falls back to ``$ATHENA_DOCX_API_KEY
|
|
256
|
+
api_key: Athena API key. Falls back to ``$ATHENA_DOCX_API_KEY``, then
|
|
257
|
+
``$ATHENA_API_KEY``.
|
|
251
258
|
name: Optional asset title (defaults to the file's stem).
|
|
252
259
|
parent_folder_id: Optional parent folder for the new asset.
|
|
253
|
-
workspace_id: Optional workspace UUID; defaults to the
|
|
254
|
-
current workspace
|
|
260
|
+
workspace_id: Optional workspace UUID; defaults to the sandbox run's
|
|
261
|
+
ATHENA_WORKSPACE_ID, then the caller's current workspace in Agora.
|
|
255
262
|
timeout: Request timeout in seconds (uploads can be slow for big
|
|
256
263
|
decks/templates).
|
|
257
264
|
|
|
@@ -267,17 +274,7 @@ def upload_document(
|
|
|
267
274
|
import os
|
|
268
275
|
|
|
269
276
|
resolved_docx_base: str | None = docx_base_url or os.environ.get(_BASE_URL_ENV)
|
|
270
|
-
|
|
271
|
-
# is the user, not a service identity. ``ATHENA_API_KEY`` is the canonical
|
|
272
|
-
# per-user Athena key (a local API key in BYO/SSO mode that agora resolves to
|
|
273
|
-
# the real user); the docx-studio studio key (``ATHENA_DOCX_API_KEY``) is a
|
|
274
|
-
# deployment service credential used only for studio access-checks on
|
|
275
|
-
# EXISTING assets, and agora's create gate rejects it (no backing user). So
|
|
276
|
-
# prefer the user key here; only fall back to the studio key for callers that
|
|
277
|
-
# set just that.
|
|
278
|
-
resolved_key: str | None = (
|
|
279
|
-
api_key or os.environ.get("ATHENA_API_KEY") or os.environ.get(_API_KEY_ENV)
|
|
280
|
-
)
|
|
277
|
+
resolved_key: str | None = _resolve_api_key(api_key)
|
|
281
278
|
resolved_agora_base: str | None = (
|
|
282
279
|
agora_base_url
|
|
283
280
|
or os.environ.get(_AGORA_BASE_URL_ENV)
|
|
@@ -323,10 +320,13 @@ def upload_document(
|
|
|
323
320
|
"seed_author": "requester",
|
|
324
321
|
"seed_template": False,
|
|
325
322
|
}
|
|
323
|
+
if source_asset_id is not None:
|
|
324
|
+
create_body["source_asset_id"] = source_asset_id
|
|
326
325
|
if parent_folder_id is not None:
|
|
327
326
|
create_body["parent_folder_id"] = parent_folder_id
|
|
328
|
-
|
|
329
|
-
|
|
327
|
+
resolved_workspace = workspace_id or os.environ.get("ATHENA_WORKSPACE_ID")
|
|
328
|
+
if resolved_workspace:
|
|
329
|
+
create_body["workspace_id"] = resolved_workspace
|
|
330
330
|
|
|
331
331
|
create_url: str = resolved_agora_base.rstrip("/") + "/api/docx-studio/assets"
|
|
332
332
|
|