pyOpenVBA 6.3.5__tar.gz → 6.4.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.
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/PKG-INFO +17 -9
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/README.md +16 -8
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/docs/architecture.md +7 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/pyproject.toml +1 -1
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/__init__.py +1 -1
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_host.py +51 -12
- pyopenvba-6.4.0/src/pyopenvba/_package_copy.py +172 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/powerquery/_opc.py +3 -1
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/.gitignore +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/LICENSE.md +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/docs/excel_checklist.csv +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/docs/host_completeness.md +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/docs/ms-ovba-implementation-guide_v2.md +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/docs/power_query.md +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/docs/research/access_complex/README.md +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/docs/research/access_designs/README.md +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/docs/research/access_macros/README.md +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/docs/research/access_write/README.md +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/docs/research/pcode/README.md +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/docs/roadmap.md +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/docs/vba_runtime.md +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/__main__.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_a1.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_deflate.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_new_project.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_oforms_pages.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_oforms_records.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_package_signature.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_ppt_container.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_references.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_relationships.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/__init__.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/blank_files/blank_database.accdb +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/blank_files/blank_database_module.accdb +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/blank_files/blank_document.docm +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/blank_files/blank_excel_addin.xlam +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/blank_files/blank_presentation.pptm +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/blank_files/blank_workbook.xlsb +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/blank_files/blank_workbook.xlsm +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/blank_files/blank_workbook.xlsx +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/blank_files/engine_skeleton.accdb +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/blank_files/engine_skeleton.mdb +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/designs/form.blob +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/designs/form.lvprop +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/designs/form.prototypes +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/designs/form.typeinfo +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/designs/report.blob +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/designs/report.lvprop +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/designs/report.prototypes +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/designs/report.typeinfo +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_xml.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/__init__.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_alloc.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_btree.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_collation.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_collation_general_legacy.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_compact.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_complex.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_datapage.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_ddl.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_designs.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_facade.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_format.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_index.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_lval.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_macros.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_pages.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_props.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_queries.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_rows.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_schema.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_sql.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_storage.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_tdef.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_themes.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_validate.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/_vba.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access/database.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/access_read.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/__init__.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/__init__.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_arrays.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_autofill.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_autofilter.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_bridge.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_calc.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_cell_styles.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_clipboard.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_control_refs.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_controls.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_copy_names.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_dimensions.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_duplicates.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_dynamic_file.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_editing.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_engine_book.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_evaluate.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_events.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_find.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_font_rows.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_formats.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_formula_format.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_hyperlinks.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_hyperlinks_file.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_io.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_merges.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_model.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_named_api.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_names.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_notes.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_notes_file.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_number_format.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_protection.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_radios.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_refresh.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_region.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_row_formats.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_shape_api.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_shapes.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_shared.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_sheet_copy.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_sheet_move.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_sort.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_special_cells.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_spills.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_styles.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_table_edits.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_tables.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_text_columns.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_typing.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_validation.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_visible.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_windows.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/excel/_worksheet_functions.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/powerpoint/__init__.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/powerpoint/_bridge.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/powerpoint/_io.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/powerpoint/_model.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/word/__init__.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/word/_bridge.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/word/_io.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/apps/word/_model.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/cfb.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/excel.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/exceptions.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/forms.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/__init__.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_arity.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/__init__.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/catalog.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/cells.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/collate.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/dates.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/evaluator.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/functions/__init__.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/functions/aggregate.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/functions/arithmetic.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/functions/arrays.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/functions/common.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/functions/conditional.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/functions/database.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/functions/dates.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/functions/distributions.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/functions/engineering.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/functions/financial.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/functions/information.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/functions/lambdas.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/functions/logical.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/functions/lookup.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/functions/matrix.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/functions/statistics.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/functions/subtotal.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/functions/text.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/host.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/lexer.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/nodes.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/numbers.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/parser.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/precise.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/reference.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/registry.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/special.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_calc/values.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_deep.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_display.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_formula2.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_inventory.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_parse.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_prefixes.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_r1c1.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_spell.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_structured.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/formula/_values.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/interpreter/__init__.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/interpreter/_ast.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/interpreter/_constants_data.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/interpreter/_intrinsics.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/interpreter/_inventory.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/interpreter/_inventory_data.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/interpreter/_lex.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/interpreter/_objects.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/interpreter/_parse.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/interpreter/_preprocess.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/interpreter/_runtime.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/interpreter/_scripting.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/interpreter/_values.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/mlang/__init__.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/mlang/_eval.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/mlang/_inventory.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/mlang/_inventory_data.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/mlang/_library.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/mlang/_parse.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/mlang/_values.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/powerpoint.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/powerquery/__init__.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/powerquery/_binary.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/powerquery/_files.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/powerquery/_mashup.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/powerquery/_metadata.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/powerquery/_package.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/powerquery/_refresh.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/powerquery/_section.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/powerquery/_sheets.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/powerquery/workbook.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/shapes/__init__.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/shapes/_docx.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/shapes/_docx_text.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/shapes/_drawingml.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/shapes/_pptx.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/shapes/_values.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/shapes/_xlsx.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/vba.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/vba_pcode.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/word.py +0 -0
- {pyopenvba-6.3.5 → pyopenvba-6.4.0}/tests/fuzz_corpus/README.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pyOpenVBA
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.4.0
|
|
4
4
|
Summary: Read and write VBA macros and Excel Power Query in pure Python, no dependencies.
|
|
5
5
|
Project-URL: Homepage, https://github.com/WilliamSmithEdward/pyOpenVBA
|
|
6
6
|
Project-URL: Repository, https://github.com/WilliamSmithEdward/pyOpenVBA
|
|
@@ -37,15 +37,15 @@ Description-Content-Type: text/markdown
|
|
|
37
37
|
|
|
38
38
|
# pyOpenVBA
|
|
39
39
|
|
|
40
|
-
[](https://pypi.org/project/pyOpenVBA/)
|
|
41
|
+
[](https://pypi.org/project/pyOpenVBA/)
|
|
42
|
+
[](https://pepy.tech/projects/pyOpenVBA)
|
|
43
|
+
[](https://github.com/WilliamSmithEdward/pyOpenVBA/actions/workflows/ci.yml)
|
|
44
|
+
[](https://github.com/WilliamSmithEdward/pyOpenVBA/actions/workflows/security.yml)
|
|
45
|
+
[](https://github.com/WilliamSmithEdward/pyOpenVBA/actions/workflows/malware-scan.yml)
|
|
45
46
|
[](https://scorecard.dev/viewer/?uri=github.com/WilliamSmithEdward/pyOpenVBA)
|
|
46
47
|
[](https://www.bestpractices.dev/projects/15116)
|
|
47
|
-
[](https://pepy.tech/project/pyOpenVBA)
|
|
48
|
+
[](https://github.com/WilliamSmithEdward/pyOpenVBA/blob/main/LICENSE.md)
|
|
49
49
|
|
|
50
50
|
**Read and write the code inside Office files, in pure Python: VBA macros
|
|
51
51
|
in four hosts, and Power Query in Excel.**
|
|
@@ -985,7 +985,15 @@ Every save is verified to reopen in the host application without the
|
|
|
985
985
|
|
|
986
986
|
## Safety guards
|
|
987
987
|
|
|
988
|
-
|
|
988
|
+
ZIP-based VBA saves copy untouched compressed parts by default, avoiding
|
|
989
|
+
recompression of the rest of the document. Existing corruption in those parts
|
|
990
|
+
is preserved without checking their compressed data or CRC. Use
|
|
991
|
+
`wb.save(full_rebuild=True)` (also on `WordFile` and `PowerPointFile`) to
|
|
992
|
+
decompress and rewrite every retained part and detect such damage. This does
|
|
993
|
+
not repair damaged parts. Unsupported packages fall back to a full rebuild;
|
|
994
|
+
legacy binary formats ignore this flag.
|
|
995
|
+
|
|
996
|
+
`save()` enforces the following protection and signature gates.
|
|
989
997
|
|
|
990
998
|
### Password-protected projects
|
|
991
999
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# pyOpenVBA
|
|
2
2
|
|
|
3
|
-
[](https://pypi.org/project/pyOpenVBA/)
|
|
4
|
+
[](https://pypi.org/project/pyOpenVBA/)
|
|
5
|
+
[](https://pepy.tech/projects/pyOpenVBA)
|
|
6
|
+
[](https://github.com/WilliamSmithEdward/pyOpenVBA/actions/workflows/ci.yml)
|
|
7
|
+
[](https://github.com/WilliamSmithEdward/pyOpenVBA/actions/workflows/security.yml)
|
|
8
|
+
[](https://github.com/WilliamSmithEdward/pyOpenVBA/actions/workflows/malware-scan.yml)
|
|
8
9
|
[](https://scorecard.dev/viewer/?uri=github.com/WilliamSmithEdward/pyOpenVBA)
|
|
9
10
|
[](https://www.bestpractices.dev/projects/15116)
|
|
10
|
-
[](https://pepy.tech/project/pyOpenVBA)
|
|
11
|
+
[](https://github.com/WilliamSmithEdward/pyOpenVBA/blob/main/LICENSE.md)
|
|
12
12
|
|
|
13
13
|
**Read and write the code inside Office files, in pure Python: VBA macros
|
|
14
14
|
in four hosts, and Power Query in Excel.**
|
|
@@ -948,7 +948,15 @@ Every save is verified to reopen in the host application without the
|
|
|
948
948
|
|
|
949
949
|
## Safety guards
|
|
950
950
|
|
|
951
|
-
|
|
951
|
+
ZIP-based VBA saves copy untouched compressed parts by default, avoiding
|
|
952
|
+
recompression of the rest of the document. Existing corruption in those parts
|
|
953
|
+
is preserved without checking their compressed data or CRC. Use
|
|
954
|
+
`wb.save(full_rebuild=True)` (also on `WordFile` and `PowerPointFile`) to
|
|
955
|
+
decompress and rewrite every retained part and detect such damage. This does
|
|
956
|
+
not repair damaged parts. Unsupported packages fall back to a full rebuild;
|
|
957
|
+
legacy binary formats ignore this flag.
|
|
958
|
+
|
|
959
|
+
`save()` enforces the following protection and signature gates.
|
|
952
960
|
|
|
953
961
|
### Password-protected projects
|
|
954
962
|
|
|
@@ -313,6 +313,13 @@ For the ZIP case, the VBA project is at the fixed path
|
|
|
313
313
|
`xl/vbaProject.bin`. On save, exactly that entry is replaced while
|
|
314
314
|
every other ZIP entry is preserved byte-for-byte including its
|
|
315
315
|
compression method, external attributes, create system, and timestamp.
|
|
316
|
+
By default, unchanged parts keep their compressed bytes (`_package_copy.py`),
|
|
317
|
+
avoiding decompression and recompression. They are not checked for damaged
|
|
318
|
+
compressed data or CRC mismatches. `save(full_rebuild=True)` uses the previous
|
|
319
|
+
`zipfile` writer, which decompresses and rewrites every retained part and
|
|
320
|
+
refuses damaged ones. ZIP64 and other unsupported packages fall back to that
|
|
321
|
+
writer automatically. Copying still takes time proportional to compressed
|
|
322
|
+
package size; it avoids work proportional to the expanded content.
|
|
316
323
|
|
|
317
324
|
For `.xls` and `.doc`, the entire file *is* the CFB; `cfb.to_bytes()`
|
|
318
325
|
is written straight to the output path.
|
|
@@ -20,6 +20,7 @@ from pathlib import Path
|
|
|
20
20
|
from typing import ClassVar, TypeVar
|
|
21
21
|
|
|
22
22
|
from pyopenvba._new_project import with_project
|
|
23
|
+
from pyopenvba._package_copy import copy_package
|
|
23
24
|
from pyopenvba._package_signature import signature_parts, without_signature
|
|
24
25
|
from pyopenvba._references import ReferenceManager, module_offset, reference_spans
|
|
25
26
|
from pyopenvba.cfb import CFB
|
|
@@ -108,6 +109,12 @@ class VBAHostFile(ReferenceManager):
|
|
|
108
109
|
# Set by add_vba_project: the project is not in the package yet, and these are its document modules.
|
|
109
110
|
self._project_added = False
|
|
110
111
|
self._document_names: set[str] = set()
|
|
112
|
+
# Package signature edits survive repeated saves and failed writes;
|
|
113
|
+
# the original ZIP remains the source of unchanged parts.
|
|
114
|
+
self._signature_edits: dict[str, bytes | None] = {}
|
|
115
|
+
# Edits already applied to the in-memory CFB are still mutations
|
|
116
|
+
# until a write succeeds, even if their dirty flags were consumed.
|
|
117
|
+
self._pending_mutation = False
|
|
111
118
|
self._open()
|
|
112
119
|
|
|
113
120
|
# ------------------------------------------------------------------
|
|
@@ -251,7 +258,7 @@ class VBAHostFile(ReferenceManager):
|
|
|
251
258
|
if not self._has_project:
|
|
252
259
|
return SignatureInfo()
|
|
253
260
|
info = detect_signature(self._get_cfb())
|
|
254
|
-
if self._zip is not None:
|
|
261
|
+
if self._zip is not None and not self._signature_edits:
|
|
255
262
|
parts = signature_parts(self._zip.namelist(), self._zip.read, self._vba_entry)
|
|
256
263
|
info.kinds += [kind for kind in dict.fromkeys(parts.values()) if kind not in info.kinds]
|
|
257
264
|
info.parts = list(parts)
|
|
@@ -501,6 +508,7 @@ class VBAHostFile(ReferenceManager):
|
|
|
501
508
|
*,
|
|
502
509
|
allow_protected: bool = False,
|
|
503
510
|
allow_invalidate_signature: bool = False,
|
|
511
|
+
full_rebuild: bool = False,
|
|
504
512
|
) -> None:
|
|
505
513
|
"""
|
|
506
514
|
Save the file, applying any pending module edits.
|
|
@@ -510,7 +518,14 @@ class VBAHostFile(ReferenceManager):
|
|
|
510
518
|
Only the ``vbaProject.bin`` entry is rewritten; every other ZIP
|
|
511
519
|
entry is preserved byte-for-byte along with its compression
|
|
512
520
|
method and metadata so the file's non-VBA structure remains
|
|
513
|
-
intact.
|
|
521
|
+
intact. By default, unchanged parts keep their compressed bytes;
|
|
522
|
+
they are not decompressed or checked for corruption. Set
|
|
523
|
+
``full_rebuild=True`` to decompress and rewrite every retained
|
|
524
|
+
part, checking its compressed data and CRC in the process. This
|
|
525
|
+
detects damage; it does not repair it. Packages the copying writer
|
|
526
|
+
cannot handle safely fall back to a full rebuild automatically.
|
|
527
|
+
Legacy raw-CFB formats write the CFB bytes directly and ignore
|
|
528
|
+
``full_rebuild``.
|
|
514
529
|
|
|
515
530
|
Safety gates:
|
|
516
531
|
|
|
@@ -533,8 +548,8 @@ class VBAHostFile(ReferenceManager):
|
|
|
533
548
|
to a UserForm's design counts: adding or removing a control
|
|
534
549
|
changes the form class's members.
|
|
535
550
|
|
|
536
|
-
A file with no VBA project
|
|
537
|
-
|
|
551
|
+
A file with no VBA project is copied as read unless
|
|
552
|
+
``full_rebuild=True`` requests rebuilding its ZIP container.
|
|
538
553
|
|
|
539
554
|
Templates and add-ins that share a supported layout (``.xltm``,
|
|
540
555
|
``.xlt``, ``.xla``, ``.dot``, ``.ppsm``, ``.ppam``) open read only:
|
|
@@ -545,7 +560,9 @@ class VBAHostFile(ReferenceManager):
|
|
|
545
560
|
f"Saving a {self._suffix} file is not supported; it opens read only."
|
|
546
561
|
)
|
|
547
562
|
if not self._has_project:
|
|
548
|
-
if
|
|
563
|
+
if full_rebuild and self._suffix in self._zip_formats:
|
|
564
|
+
self._write_container(dest, None, {}, full_rebuild=True)
|
|
565
|
+
elif dest is not None:
|
|
549
566
|
Path(dest).write_bytes(self._container_raw)
|
|
550
567
|
return
|
|
551
568
|
cfb = self._get_cfb()
|
|
@@ -561,9 +578,11 @@ class VBAHostFile(ReferenceManager):
|
|
|
561
578
|
for form in self._forms or ():
|
|
562
579
|
# Not any(...): that short-circuits, and every form has to be
|
|
563
580
|
# written, not just the first dirty one.
|
|
564
|
-
|
|
581
|
+
changed = form.write_back(cfb)
|
|
582
|
+
forms_dirty |= changed
|
|
583
|
+
self._pending_mutation |= changed
|
|
565
584
|
# What the package's other parts need: None drops a part.
|
|
566
|
-
package_edits
|
|
585
|
+
package_edits = dict(self._signature_edits)
|
|
567
586
|
|
|
568
587
|
if self._project is not None:
|
|
569
588
|
project = self._project
|
|
@@ -575,7 +594,8 @@ class VBAHostFile(ReferenceManager):
|
|
|
575
594
|
delete_names = set(project.pending_deletes)
|
|
576
595
|
has_source_edits = any(m.dirty for m in project.modules)
|
|
577
596
|
mutating = bool(
|
|
578
|
-
|
|
597
|
+
self._pending_mutation
|
|
598
|
+
or rename_map
|
|
579
599
|
or add_names
|
|
580
600
|
or delete_names
|
|
581
601
|
or has_source_edits
|
|
@@ -598,6 +618,8 @@ class VBAHostFile(ReferenceManager):
|
|
|
598
618
|
f"the {self._host_noun} inconsistent)."
|
|
599
619
|
)
|
|
600
620
|
|
|
621
|
+
self._pending_mutation |= mutating
|
|
622
|
+
|
|
601
623
|
# Safety gate 2: any change invalidates a present digital
|
|
602
624
|
# signature. Drop it and warn.
|
|
603
625
|
if mutating:
|
|
@@ -617,7 +639,8 @@ class VBAHostFile(ReferenceManager):
|
|
|
617
639
|
except KeyError:
|
|
618
640
|
pass
|
|
619
641
|
new_cfb_bytes = cfb.to_bytes()
|
|
620
|
-
self._write_container(dest, new_cfb_bytes, package_edits)
|
|
642
|
+
self._write_container(dest, new_cfb_bytes, package_edits, full_rebuild=full_rebuild)
|
|
643
|
+
self._pending_mutation = False
|
|
621
644
|
|
|
622
645
|
def _apply_project(self, cfb: CFB, project: VBAProject, *, mutating: bool) -> None:
|
|
623
646
|
"""Write ``project``'s pending renames, adds, deletes and edits into ``cfb``."""
|
|
@@ -744,12 +767,13 @@ class VBAHostFile(ReferenceManager):
|
|
|
744
767
|
if mutating:
|
|
745
768
|
invalidate_vba_project_cache(cfb)
|
|
746
769
|
|
|
747
|
-
def _write_container(self, dest: str | Path | None, new_cfb_bytes: bytes,
|
|
748
|
-
package_edits: dict[str, bytes | None]) -> None:
|
|
770
|
+
def _write_container(self, dest: str | Path | None, new_cfb_bytes: bytes | None,
|
|
771
|
+
package_edits: dict[str, bytes | None], *, full_rebuild: bool = False) -> None:
|
|
749
772
|
"""Write the file with ``new_cfb_bytes`` as its project and ``package_edits`` applied."""
|
|
750
773
|
out_path = Path(dest) if dest is not None else self._path
|
|
751
774
|
|
|
752
775
|
if self._suffix in self._cfb_formats:
|
|
776
|
+
assert new_cfb_bytes is not None
|
|
753
777
|
out_path.write_bytes(self._container_bytes(new_cfb_bytes))
|
|
754
778
|
return
|
|
755
779
|
|
|
@@ -765,12 +789,24 @@ class VBAHostFile(ReferenceManager):
|
|
|
765
789
|
package_edits.update(self._project_edits())
|
|
766
790
|
if self._writes_project(self._project):
|
|
767
791
|
package_edits.update(with_project(self._zip.namelist(), self._zip.read, self._main_part))
|
|
792
|
+
assert new_cfb_bytes is not None
|
|
768
793
|
added[self._vba_entry] = new_cfb_bytes
|
|
769
794
|
|
|
795
|
+
# Avoid inflating and recompressing untouched parts. Unsupported
|
|
796
|
+
# packages and an explicit full rebuild retain the zipfile path.
|
|
797
|
+
if not full_rebuild:
|
|
798
|
+
edits = dict(package_edits)
|
|
799
|
+
if new_cfb_bytes is not None:
|
|
800
|
+
edits[self._vba_entry] = new_cfb_bytes
|
|
801
|
+
packed = copy_package(self._container_raw, self._zip.infolist(), edits, added)
|
|
802
|
+
if packed is not None:
|
|
803
|
+
out_path.write_bytes(packed)
|
|
804
|
+
return
|
|
805
|
+
|
|
770
806
|
buf = io.BytesIO()
|
|
771
807
|
with zipfile.ZipFile(buf, "w") as out_zip:
|
|
772
808
|
for info in self._zip.infolist():
|
|
773
|
-
if info.filename == self._vba_entry:
|
|
809
|
+
if info.filename == self._vba_entry and new_cfb_bytes is not None:
|
|
774
810
|
out_info = zipfile.ZipInfo(
|
|
775
811
|
filename=info.filename,
|
|
776
812
|
date_time=info.date_time,
|
|
@@ -812,6 +848,8 @@ class VBAHostFile(ReferenceManager):
|
|
|
812
848
|
zip-based file keeps beside it come back as the package's edits,
|
|
813
849
|
None for each part that goes (see :mod:`pyopenvba._package_signature`).
|
|
814
850
|
"""
|
|
851
|
+
if self._signature_edits:
|
|
852
|
+
return dict(self._signature_edits)
|
|
815
853
|
found = detect_signature(cfb)
|
|
816
854
|
if found.present:
|
|
817
855
|
for sig_stream in (
|
|
@@ -834,6 +872,7 @@ class VBAHostFile(ReferenceManager):
|
|
|
834
872
|
kinds += [kind for kind in signature_parts(names, self._zip.read, self._vba_entry).values()
|
|
835
873
|
if kind not in kinds]
|
|
836
874
|
edits = without_signature(names, self._zip.read, self._vba_entry)
|
|
875
|
+
self._signature_edits.update(edits)
|
|
837
876
|
if kinds and not allow_invalidate_signature:
|
|
838
877
|
warnings.warn(
|
|
839
878
|
f"Dropped the stale VBA digital signature ({', '.join(kinds)}) "
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""Write a zip-based Office file with a few parts changed and the rest as they were stored.
|
|
2
|
+
|
|
3
|
+
Saving a VBA project changes one part, ``vbaProject.bin``, and at most a
|
|
4
|
+
handful of small parts beside it. Reading and recompressing everything
|
|
5
|
+
else costs time proportional to the expanded content. Copying unchanged
|
|
6
|
+
compressed bytes avoids that work, using the same container writer as
|
|
7
|
+
Power Query and the document surfaces. It still costs time and memory
|
|
8
|
+
proportional to the compressed package size.
|
|
9
|
+
A changed part is deflated by zlib at its default level, as ``zipfile``
|
|
10
|
+
deflates it, so it is stored as the same bytes as before this module
|
|
11
|
+
existed.
|
|
12
|
+
|
|
13
|
+
:func:`copy_package` answers None for a package it would not carry over
|
|
14
|
+
faithfully, and the caller then writes it with ``zipfile`` as it always
|
|
15
|
+
has. That covers ZIP64, a part compressed some other way than deflate
|
|
16
|
+
that has to be written anew, an encrypted part, a part name that is not
|
|
17
|
+
UTF-8, an unknown compression method, unsupported patched-data or
|
|
18
|
+
strong-encryption flags, and a package naming one part twice. It also
|
|
19
|
+
covers a package whose two directories disagree -- a part's own header naming it
|
|
20
|
+
differently, giving it other sizes in a ZIP64 record, or placing it over
|
|
21
|
+
another part or the central directory -- which ``zipfile`` refuses,
|
|
22
|
+
and so still does.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import struct
|
|
28
|
+
import zipfile
|
|
29
|
+
import zlib
|
|
30
|
+
from collections.abc import Mapping, Sequence
|
|
31
|
+
from dataclasses import replace
|
|
32
|
+
|
|
33
|
+
from pyopenvba.exceptions import PowerQueryError
|
|
34
|
+
from pyopenvba.powerquery._opc import Entry, OpcFile
|
|
35
|
+
|
|
36
|
+
_STORED = 0
|
|
37
|
+
_DEFLATED = 8
|
|
38
|
+
#: General purpose bit 0: the part is encrypted.
|
|
39
|
+
_ENCRYPTED = 0x0001
|
|
40
|
+
#: Patched data and strong encryption are refused by zipfile when reading.
|
|
41
|
+
_UNSUPPORTED_FLAGS = 0x0060
|
|
42
|
+
_UTF8 = 0x0800
|
|
43
|
+
#: General purpose bit 3: the sizes follow the part's bytes instead of standing in its header.
|
|
44
|
+
_DESCRIPTOR = 0x0008
|
|
45
|
+
#: The date ``zipfile`` gives a part with none, 1980-01-01, as [APPNOTE] 4.4.6 packs one.
|
|
46
|
+
_NO_DATE = 0x0021
|
|
47
|
+
#: A size or offset of this much is held in a ZIP64 record instead.
|
|
48
|
+
_ZIP64 = 0xFFFFFFFF
|
|
49
|
+
#: The extra field record that holds one.
|
|
50
|
+
_ZIP64_RECORD = 0x0001
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _has_zip64(extra: bytes) -> bool:
|
|
54
|
+
"""Whether an extra field holds a ZIP64 record, or cannot be read to say."""
|
|
55
|
+
at = 0
|
|
56
|
+
while at < len(extra):
|
|
57
|
+
if at + 4 > len(extra):
|
|
58
|
+
return True
|
|
59
|
+
kind, size = struct.unpack_from("<HH", extra, at)
|
|
60
|
+
if kind == _ZIP64_RECORD:
|
|
61
|
+
return True
|
|
62
|
+
at += 4 + size
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _pack(data: bytes, method: int) -> bytes | None:
|
|
67
|
+
"""``data`` as a part stored by ``method`` holds it; None for a method not written here."""
|
|
68
|
+
if method == _STORED:
|
|
69
|
+
return data
|
|
70
|
+
if method == _DEFLATED:
|
|
71
|
+
deflater = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -15)
|
|
72
|
+
return deflater.compress(data) + deflater.flush()
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def copy_package(
|
|
77
|
+
raw: bytes,
|
|
78
|
+
infos: Sequence[zipfile.ZipInfo],
|
|
79
|
+
edits: Mapping[str, bytes | None],
|
|
80
|
+
added: Mapping[str, bytes],
|
|
81
|
+
) -> bytes | None:
|
|
82
|
+
"""The package ``raw`` with ``edits`` applied and ``added`` appended, every other part as it was stored.
|
|
83
|
+
|
|
84
|
+
``infos`` are the package's entries as ``zipfile`` read them, which
|
|
85
|
+
is what the edits are named by. ``edits`` gives a part its new
|
|
86
|
+
bytes, or None to leave it out; a part is kept in its place, under
|
|
87
|
+
its own compression method, dates and attributes. ``added`` are
|
|
88
|
+
parts the package does not have yet, deflated and put last.
|
|
89
|
+
|
|
90
|
+
None means the package is one this does not write; see the module's
|
|
91
|
+
docstring.
|
|
92
|
+
"""
|
|
93
|
+
try:
|
|
94
|
+
package = OpcFile.parse(raw)
|
|
95
|
+
central_start = struct.unpack_from("<I", raw, raw.rfind(b"PK\x05\x06") + 16)[0]
|
|
96
|
+
except (PowerQueryError, UnicodeDecodeError, struct.error):
|
|
97
|
+
return None
|
|
98
|
+
if len(package.entries) != len(infos) or len({info.filename for info in infos}) != len(infos):
|
|
99
|
+
return None
|
|
100
|
+
|
|
101
|
+
entries: list[Entry] = []
|
|
102
|
+
# Where the last part's bytes ended: each part has to start past the one before it.
|
|
103
|
+
end = 0
|
|
104
|
+
for entry, info in zip(package.entries, infos, strict=True):
|
|
105
|
+
# The two readers have to agree on what the part is, or an edit could land on the wrong one.
|
|
106
|
+
if (
|
|
107
|
+
entry.name != info.filename
|
|
108
|
+
or len(entry.body) != info.compress_size
|
|
109
|
+
or entry.uncompressed_size != info.file_size
|
|
110
|
+
or entry.crc != info.CRC
|
|
111
|
+
or entry.method not in (zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED, zipfile.ZIP_BZIP2, zipfile.ZIP_LZMA)
|
|
112
|
+
or entry.flags & (_ENCRYPTED | _UNSUPPORTED_FLAGS)
|
|
113
|
+
or info.compress_size >= _ZIP64
|
|
114
|
+
or info.file_size >= _ZIP64
|
|
115
|
+
or _has_zip64(entry.local_extra)
|
|
116
|
+
or _has_zip64(entry.central_extra)
|
|
117
|
+
):
|
|
118
|
+
return None
|
|
119
|
+
# So do the part's own header and the directory: on its name, and on parts not lying over each other.
|
|
120
|
+
name = entry.name.encode("utf-8")
|
|
121
|
+
named = info.header_offset + 30
|
|
122
|
+
local_name_length, local_extra_length = struct.unpack_from("<HH", raw, info.header_offset + 26)
|
|
123
|
+
if (
|
|
124
|
+
local_name_length != len(name)
|
|
125
|
+
or local_extra_length != len(entry.local_extra)
|
|
126
|
+
or info.header_offset < end
|
|
127
|
+
or raw[named : named + local_name_length + local_extra_length] != name + entry.local_extra
|
|
128
|
+
):
|
|
129
|
+
return None
|
|
130
|
+
local_flags = struct.unpack_from("<H", raw, info.header_offset + 6)[0]
|
|
131
|
+
if (local_flags ^ entry.flags) & _UTF8:
|
|
132
|
+
return None
|
|
133
|
+
end = named + local_name_length + local_extra_length + len(entry.body)
|
|
134
|
+
if end > central_start:
|
|
135
|
+
return None
|
|
136
|
+
# The sizes are written in the header, so nothing follows the part's bytes to say them again.
|
|
137
|
+
flags = entry.flags & ~_DESCRIPTOR
|
|
138
|
+
if entry.name in edits:
|
|
139
|
+
data = edits[entry.name]
|
|
140
|
+
if data is None:
|
|
141
|
+
continue
|
|
142
|
+
body = _pack(data, entry.method)
|
|
143
|
+
if body is None:
|
|
144
|
+
return None
|
|
145
|
+
entry = replace(
|
|
146
|
+
entry, body=body, flags=flags, crc=zlib.crc32(data) & 0xFFFFFFFF, uncompressed_size=len(data)
|
|
147
|
+
)
|
|
148
|
+
elif flags != entry.flags:
|
|
149
|
+
entry = replace(entry, flags=flags)
|
|
150
|
+
entries.append(entry)
|
|
151
|
+
|
|
152
|
+
for name, data in added.items():
|
|
153
|
+
body = _pack(data, _DEFLATED)
|
|
154
|
+
assert body is not None
|
|
155
|
+
entries.append(
|
|
156
|
+
Entry(
|
|
157
|
+
name=name,
|
|
158
|
+
body=body,
|
|
159
|
+
method=_DEFLATED,
|
|
160
|
+
flags=0,
|
|
161
|
+
dos_time=0,
|
|
162
|
+
dos_date=_NO_DATE,
|
|
163
|
+
crc=zlib.crc32(data) & 0xFFFFFFFF,
|
|
164
|
+
uncompressed_size=len(data),
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
try:
|
|
169
|
+
return OpcFile(entries=entries).serialize()
|
|
170
|
+
except struct.error:
|
|
171
|
+
# Past what a ZIP holds without ZIP64: more than 65535 parts, or 4 GB.
|
|
172
|
+
return None
|
|
@@ -81,7 +81,9 @@ class OpcFile:
|
|
|
81
81
|
end = raw.rfind(_END)
|
|
82
82
|
if end < 0:
|
|
83
83
|
raise PowerQueryError("this file has no ZIP end record; it is not an Office package")
|
|
84
|
-
|
|
84
|
+
# A ZIP64 locator immediately precedes the end record. Its magic
|
|
85
|
+
# inside a stored part is ordinary content, not an archive marker.
|
|
86
|
+
if raw[max(0, end - 20) : end - 16] == _END64_LOCATOR:
|
|
85
87
|
raise PowerQueryError("ZIP64 packages are not handled here")
|
|
86
88
|
count, _size, offset = struct.unpack_from("<HII", raw, end + 10)
|
|
87
89
|
entries: list[Entry] = []
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/blank_files/blank_database.accdb
RENAMED
|
File without changes
|
{pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/blank_files/blank_database_module.accdb
RENAMED
|
File without changes
|
{pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/blank_files/blank_document.docm
RENAMED
|
File without changes
|
{pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/blank_files/blank_excel_addin.xlam
RENAMED
|
File without changes
|
{pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/blank_files/blank_presentation.pptm
RENAMED
|
File without changes
|
{pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/blank_files/blank_workbook.xlsb
RENAMED
|
File without changes
|
{pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/blank_files/blank_workbook.xlsm
RENAMED
|
File without changes
|
{pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/blank_files/blank_workbook.xlsx
RENAMED
|
File without changes
|
{pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/blank_files/engine_skeleton.accdb
RENAMED
|
File without changes
|
{pyopenvba-6.3.5 → pyopenvba-6.4.0}/src/pyopenvba/_templates/blank_files/engine_skeleton.mdb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|