pyOpenVBA 6.3.4__tar.gz → 6.3.5__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.4 → pyopenvba-6.3.5}/PKG-INFO +11 -4
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/README.md +10 -3
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/pyproject.toml +1 -1
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/__init__.py +1 -1
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/__main__.py +6 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_host.py +10 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_pages.py +2 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_rows.py +19 -3
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_tdef.py +8 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/database.py +17 -1
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/cfb.py +21 -1
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/excel.py +5 -2
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/mlang/_parse.py +8 -2
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/powerpoint.py +3 -1
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/powerquery/_metadata.py +1 -1
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/powerquery/_package.py +13 -2
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/vba.py +21 -1
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/word.py +3 -1
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/tests/fuzz_corpus/README.md +18 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/.gitignore +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/LICENSE.md +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/docs/architecture.md +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/docs/excel_checklist.csv +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/docs/host_completeness.md +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/docs/ms-ovba-implementation-guide_v2.md +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/docs/power_query.md +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/docs/research/access_complex/README.md +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/docs/research/access_designs/README.md +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/docs/research/access_macros/README.md +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/docs/research/access_write/README.md +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/docs/research/pcode/README.md +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/docs/roadmap.md +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/docs/vba_runtime.md +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_a1.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_deflate.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_new_project.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_oforms_pages.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_oforms_records.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_package_signature.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_ppt_container.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_references.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_relationships.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/__init__.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/blank_files/blank_database.accdb +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/blank_files/blank_database_module.accdb +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/blank_files/blank_document.docm +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/blank_files/blank_excel_addin.xlam +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/blank_files/blank_presentation.pptm +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/blank_files/blank_workbook.xlsb +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/blank_files/blank_workbook.xlsm +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/blank_files/blank_workbook.xlsx +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/blank_files/engine_skeleton.accdb +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/blank_files/engine_skeleton.mdb +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/designs/form.blob +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/designs/form.lvprop +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/designs/form.prototypes +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/designs/form.typeinfo +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/designs/report.blob +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/designs/report.lvprop +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/designs/report.prototypes +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/designs/report.typeinfo +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_xml.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/__init__.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_alloc.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_btree.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_collation.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_collation_general_legacy.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_compact.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_complex.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_datapage.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_ddl.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_designs.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_facade.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_format.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_index.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_lval.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_macros.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_props.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_queries.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_schema.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_sql.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_storage.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_themes.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_validate.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access/_vba.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/access_read.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/__init__.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/__init__.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_arrays.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_autofill.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_autofilter.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_bridge.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_calc.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_cell_styles.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_clipboard.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_control_refs.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_controls.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_copy_names.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_dimensions.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_duplicates.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_dynamic_file.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_editing.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_engine_book.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_evaluate.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_events.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_find.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_font_rows.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_formats.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_formula_format.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_hyperlinks.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_hyperlinks_file.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_io.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_merges.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_model.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_named_api.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_names.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_notes.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_notes_file.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_number_format.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_protection.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_radios.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_refresh.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_region.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_row_formats.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_shape_api.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_shapes.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_shared.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_sheet_copy.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_sheet_move.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_sort.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_special_cells.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_spills.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_styles.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_table_edits.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_tables.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_text_columns.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_typing.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_validation.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_visible.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_windows.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/excel/_worksheet_functions.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/powerpoint/__init__.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/powerpoint/_bridge.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/powerpoint/_io.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/powerpoint/_model.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/word/__init__.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/word/_bridge.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/word/_io.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/apps/word/_model.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/exceptions.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/forms.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/__init__.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_arity.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/__init__.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/catalog.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/cells.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/collate.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/dates.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/evaluator.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/functions/__init__.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/functions/aggregate.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/functions/arithmetic.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/functions/arrays.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/functions/common.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/functions/conditional.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/functions/database.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/functions/dates.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/functions/distributions.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/functions/engineering.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/functions/financial.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/functions/information.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/functions/lambdas.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/functions/logical.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/functions/lookup.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/functions/matrix.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/functions/statistics.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/functions/subtotal.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/functions/text.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/host.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/lexer.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/nodes.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/numbers.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/parser.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/precise.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/reference.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/registry.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/special.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_calc/values.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_deep.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_display.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_formula2.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_inventory.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_parse.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_prefixes.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_r1c1.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_spell.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_structured.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/formula/_values.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/interpreter/__init__.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/interpreter/_ast.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/interpreter/_constants_data.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/interpreter/_intrinsics.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/interpreter/_inventory.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/interpreter/_inventory_data.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/interpreter/_lex.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/interpreter/_objects.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/interpreter/_parse.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/interpreter/_preprocess.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/interpreter/_runtime.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/interpreter/_scripting.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/interpreter/_values.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/mlang/__init__.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/mlang/_eval.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/mlang/_inventory.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/mlang/_inventory_data.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/mlang/_library.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/mlang/_values.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/powerquery/__init__.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/powerquery/_binary.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/powerquery/_files.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/powerquery/_mashup.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/powerquery/_opc.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/powerquery/_refresh.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/powerquery/_section.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/powerquery/_sheets.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/powerquery/workbook.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/shapes/__init__.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/shapes/_docx.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/shapes/_docx_text.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/shapes/_drawingml.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/shapes/_pptx.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/shapes/_values.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/shapes/_xlsx.py +0 -0
- {pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/vba_pcode.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pyOpenVBA
|
|
3
|
-
Version: 6.3.
|
|
3
|
+
Version: 6.3.5
|
|
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
|
|
@@ -43,6 +43,7 @@ Description-Content-Type: text/markdown
|
|
|
43
43
|
[](https://github.com/WilliamSmithEdward/pyOpenVBA/actions/workflows/security.yml)
|
|
44
44
|
[](https://github.com/WilliamSmithEdward/pyOpenVBA/actions/workflows/malware-scan.yml)
|
|
45
45
|
[](https://scorecard.dev/viewer/?uri=github.com/WilliamSmithEdward/pyOpenVBA)
|
|
46
|
+
[](https://www.bestpractices.dev/projects/15116)
|
|
46
47
|
[](https://github.com/WilliamSmithEdward/pyOpenVBA/blob/main/LICENSE.md)
|
|
47
48
|
[](https://pepy.tech/project/pyOpenVBA)
|
|
48
49
|
|
|
@@ -54,9 +55,9 @@ Works on Windows, macOS and Linux. Python 3.10 or newer.
|
|
|
54
55
|
|
|
55
56
|
VBA, four hosts and one API:
|
|
56
57
|
|
|
57
|
-
* Excel (`.xlsm`, `.xlsb`, `.xlam`, `.xls`)
|
|
58
|
-
* Word (`.docm`, `.dotm`, `.doc`)
|
|
59
|
-
* PowerPoint (`.pptm`, `.potm`, `.ppt`)
|
|
58
|
+
* Excel (`.xlsm`, `.xlsb`, `.xlam`, `.xls`; `.xltm`, `.xlt`, `.xla` read only)
|
|
59
|
+
* Word (`.docm`, `.dotm`, `.doc`; `.dot` read only)
|
|
60
|
+
* PowerPoint (`.pptm`, `.potm`, `.ppt`; `.ppsm`, `.ppam` read only)
|
|
60
61
|
* Access (`.accdb`, `.mdb`)
|
|
61
62
|
|
|
62
63
|
Power Query, in any Excel package:
|
|
@@ -886,6 +887,9 @@ implemented, what is not, and how it was measured is in
|
|
|
886
887
|
| `.xlsb` | Binary workbook | yes | yes | yes |
|
|
887
888
|
| `.xlam` | Macro-enabled add-in | yes | yes | yes |
|
|
888
889
|
| `.xls` | Legacy (Excel 97-2003) | yes | - | no |
|
|
890
|
+
| `.xltm` | Macro-enabled template | read | yes | no |
|
|
891
|
+
| `.xlt` | Legacy template | read | - | no |
|
|
892
|
+
| `.xla` | Legacy add-in | read | - | no |
|
|
889
893
|
|
|
890
894
|
A `.xlsx` file has no VBA project by design, and Power Query lives
|
|
891
895
|
outside the project, so it is read and written in every package above.
|
|
@@ -897,6 +901,7 @@ outside the project, so it is read and written in every package above.
|
|
|
897
901
|
| `.docm` | Macro-enabled document | yes | yes | yes |
|
|
898
902
|
| `.dotm` | Macro-enabled template | yes | yes | no |
|
|
899
903
|
| `.doc` | Legacy (Word 97-2003) | yes | yes | no |
|
|
904
|
+
| `.dot` | Legacy template | yes | no | no |
|
|
900
905
|
|
|
901
906
|
### PowerPoint
|
|
902
907
|
|
|
@@ -904,6 +909,8 @@ outside the project, so it is read and written in every package above.
|
|
|
904
909
|
|-----------|------------------------------|:----:|:-----:|:----------:|
|
|
905
910
|
| `.pptm` | Macro-enabled presentation | yes | yes | yes |
|
|
906
911
|
| `.potm` | Macro-enabled template | yes | yes | no |
|
|
912
|
+
| `.ppsm` | Macro-enabled show | yes | no | no |
|
|
913
|
+
| `.ppam` | Macro-enabled add-in | yes | no | no |
|
|
907
914
|
| `.ppt` | Legacy (PowerPoint 97-2003) | yes | yes | no |
|
|
908
915
|
|
|
909
916
|
### Files with no VBA project
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
[](https://github.com/WilliamSmithEdward/pyOpenVBA/actions/workflows/security.yml)
|
|
7
7
|
[](https://github.com/WilliamSmithEdward/pyOpenVBA/actions/workflows/malware-scan.yml)
|
|
8
8
|
[](https://scorecard.dev/viewer/?uri=github.com/WilliamSmithEdward/pyOpenVBA)
|
|
9
|
+
[](https://www.bestpractices.dev/projects/15116)
|
|
9
10
|
[](https://github.com/WilliamSmithEdward/pyOpenVBA/blob/main/LICENSE.md)
|
|
10
11
|
[](https://pepy.tech/project/pyOpenVBA)
|
|
11
12
|
|
|
@@ -17,9 +18,9 @@ Works on Windows, macOS and Linux. Python 3.10 or newer.
|
|
|
17
18
|
|
|
18
19
|
VBA, four hosts and one API:
|
|
19
20
|
|
|
20
|
-
* Excel (`.xlsm`, `.xlsb`, `.xlam`, `.xls`)
|
|
21
|
-
* Word (`.docm`, `.dotm`, `.doc`)
|
|
22
|
-
* PowerPoint (`.pptm`, `.potm`, `.ppt`)
|
|
21
|
+
* Excel (`.xlsm`, `.xlsb`, `.xlam`, `.xls`; `.xltm`, `.xlt`, `.xla` read only)
|
|
22
|
+
* Word (`.docm`, `.dotm`, `.doc`; `.dot` read only)
|
|
23
|
+
* PowerPoint (`.pptm`, `.potm`, `.ppt`; `.ppsm`, `.ppam` read only)
|
|
23
24
|
* Access (`.accdb`, `.mdb`)
|
|
24
25
|
|
|
25
26
|
Power Query, in any Excel package:
|
|
@@ -849,6 +850,9 @@ implemented, what is not, and how it was measured is in
|
|
|
849
850
|
| `.xlsb` | Binary workbook | yes | yes | yes |
|
|
850
851
|
| `.xlam` | Macro-enabled add-in | yes | yes | yes |
|
|
851
852
|
| `.xls` | Legacy (Excel 97-2003) | yes | - | no |
|
|
853
|
+
| `.xltm` | Macro-enabled template | read | yes | no |
|
|
854
|
+
| `.xlt` | Legacy template | read | - | no |
|
|
855
|
+
| `.xla` | Legacy add-in | read | - | no |
|
|
852
856
|
|
|
853
857
|
A `.xlsx` file has no VBA project by design, and Power Query lives
|
|
854
858
|
outside the project, so it is read and written in every package above.
|
|
@@ -860,6 +864,7 @@ outside the project, so it is read and written in every package above.
|
|
|
860
864
|
| `.docm` | Macro-enabled document | yes | yes | yes |
|
|
861
865
|
| `.dotm` | Macro-enabled template | yes | yes | no |
|
|
862
866
|
| `.doc` | Legacy (Word 97-2003) | yes | yes | no |
|
|
867
|
+
| `.dot` | Legacy template | yes | no | no |
|
|
863
868
|
|
|
864
869
|
### PowerPoint
|
|
865
870
|
|
|
@@ -867,6 +872,8 @@ outside the project, so it is read and written in every package above.
|
|
|
867
872
|
|-----------|------------------------------|:----:|:-----:|:----------:|
|
|
868
873
|
| `.pptm` | Macro-enabled presentation | yes | yes | yes |
|
|
869
874
|
| `.potm` | Macro-enabled template | yes | yes | no |
|
|
875
|
+
| `.ppsm` | Macro-enabled show | yes | no | no |
|
|
876
|
+
| `.ppam` | Macro-enabled add-in | yes | no | no |
|
|
870
877
|
| `.ppt` | Legacy (PowerPoint 97-2003) | yes | yes | no |
|
|
871
878
|
|
|
872
879
|
### Files with no VBA project
|
|
@@ -179,12 +179,18 @@ _HOST_BY_SUFFIX: dict[str, type] = {
|
|
|
179
179
|
".xlsm": ExcelFile,
|
|
180
180
|
".xlsb": ExcelFile,
|
|
181
181
|
".xlam": ExcelFile,
|
|
182
|
+
".xltm": ExcelFile,
|
|
182
183
|
".xls": ExcelFile,
|
|
184
|
+
".xlt": ExcelFile,
|
|
185
|
+
".xla": ExcelFile,
|
|
183
186
|
".docm": WordFile,
|
|
184
187
|
".dotm": WordFile,
|
|
185
188
|
".doc": WordFile,
|
|
189
|
+
".dot": WordFile,
|
|
186
190
|
".pptm": PowerPointFile,
|
|
187
191
|
".potm": PowerPointFile,
|
|
192
|
+
".ppsm": PowerPointFile,
|
|
193
|
+
".ppam": PowerPointFile,
|
|
188
194
|
".ppt": PowerPointFile,
|
|
189
195
|
}
|
|
190
196
|
|
|
@@ -91,6 +91,8 @@ class VBAHostFile(ReferenceManager):
|
|
|
91
91
|
_project_storage: ClassVar[str | None]
|
|
92
92
|
#: The formats add_vba_project gives a project to, each measured against its application.
|
|
93
93
|
_project_formats: ClassVar[frozenset[str]] = frozenset()
|
|
94
|
+
#: Formats that open and read but do not save: a writer for them has not been measured.
|
|
95
|
+
_read_only_formats: ClassVar[frozenset[str]] = frozenset()
|
|
94
96
|
#: The package part a zip-based file's project is related from.
|
|
95
97
|
_main_part: ClassVar[str] = ""
|
|
96
98
|
|
|
@@ -533,7 +535,15 @@ class VBAHostFile(ReferenceManager):
|
|
|
533
535
|
|
|
534
536
|
A file with no VBA project can hold no edit, since every write
|
|
535
537
|
refuses, so it is written out as it was read.
|
|
538
|
+
|
|
539
|
+
Templates and add-ins that share a supported layout (``.xltm``,
|
|
540
|
+
``.xlt``, ``.xla``, ``.dot``, ``.ppsm``, ``.ppam``) open read only:
|
|
541
|
+
saving one raises :class:`~pyopenvba.exceptions.UnsupportedFormatError`.
|
|
536
542
|
"""
|
|
543
|
+
if self._suffix in self._read_only_formats:
|
|
544
|
+
raise UnsupportedFormatError(
|
|
545
|
+
f"Saving a {self._suffix} file is not supported; it opens read only."
|
|
546
|
+
)
|
|
537
547
|
if not self._has_project:
|
|
538
548
|
if dest is not None:
|
|
539
549
|
Path(dest).write_bytes(self._container_raw)
|
|
@@ -409,6 +409,8 @@ def row_bytes(
|
|
|
409
409
|
target with ``overflow_target=True``, where the 0x8000 bit marks the
|
|
410
410
|
moved row rather than a deletion."""
|
|
411
411
|
slots = row_slots(page)
|
|
412
|
+
if not 0 <= slot < len(slots):
|
|
413
|
+
raise AccessError(f"slot {slot} out of range (0..{len(slots) - 1})")
|
|
412
414
|
entry = slots[slot]
|
|
413
415
|
if entry & ROW_DELETED and not overflow_target:
|
|
414
416
|
return None
|
|
@@ -184,9 +184,13 @@ def encode_text(text: str) -> bytes:
|
|
|
184
184
|
|
|
185
185
|
def decode_datetime(raw: bytes) -> _dt.datetime:
|
|
186
186
|
value = struct.unpack("<d", raw)[0]
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
187
|
+
try:
|
|
188
|
+
days = int(value)
|
|
189
|
+
fraction = abs(value - days)
|
|
190
|
+
return EPOCH + _dt.timedelta(days=days) + _dt.timedelta(days=fraction)
|
|
191
|
+
except (OverflowError, ValueError):
|
|
192
|
+
# Out of the year 1 to 9999, or not a number: a damaged value.
|
|
193
|
+
raise AccessError(f"date value {value!r} is outside the range a date can hold") from None
|
|
190
194
|
|
|
191
195
|
|
|
192
196
|
def encode_datetime(value: _dt.datetime) -> bytes:
|
|
@@ -455,11 +459,23 @@ def encode_row(
|
|
|
455
459
|
return bytes(row)
|
|
456
460
|
|
|
457
461
|
|
|
462
|
+
#: The bytes a fixed-size type's value takes; any other length is a damaged row.
|
|
463
|
+
_SCALAR_SIZES = {
|
|
464
|
+
TYPE_BYTE: 1, TYPE_INT: 2, TYPE_LONG: 4, TYPE_MONEY: 8, TYPE_FLOAT: 4, TYPE_DOUBLE: 8,
|
|
465
|
+
TYPE_DATETIME: 8, TYPE_GUID: 16, TYPE_COMPLEX: 4, TYPE_BIGINT: 8,
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
|
|
458
469
|
def decode_scalar(column: ColumnDef, raw: bytes) -> object:
|
|
459
470
|
"""Decode a non-long-value column. ``raw`` is the present value."""
|
|
460
471
|
code = column.type_code
|
|
461
472
|
if code == TYPE_BOOLEAN:
|
|
462
473
|
return True
|
|
474
|
+
size = _SCALAR_SIZES.get(code, 0)
|
|
475
|
+
if size and len(raw) != size:
|
|
476
|
+
raise AccessError(
|
|
477
|
+
f"column {column.name!r} holds {len(raw)} bytes where its type takes {size}; the row is damaged"
|
|
478
|
+
)
|
|
463
479
|
if code == TYPE_BYTE:
|
|
464
480
|
return raw[0]
|
|
465
481
|
if code == TYPE_INT:
|
|
@@ -333,10 +333,14 @@ def read_definition_bytes(store: PageStore, page: int) -> tuple[bytes, list[int]
|
|
|
333
333
|
|
|
334
334
|
def _read_name(buf: bytes, pos: int) -> tuple[str, int]:
|
|
335
335
|
"""A name is prefixed by its length as a word, and stored as UTF-16."""
|
|
336
|
+
if pos + 2 > len(buf):
|
|
337
|
+
raise AccessError("name runs past the end of the definition")
|
|
336
338
|
length = struct.unpack_from("<H", buf, pos)[0]
|
|
337
339
|
raw = buf[pos + 2 : pos + 2 + length]
|
|
338
340
|
if len(raw) != length:
|
|
339
341
|
raise AccessError("name runs past the end of the definition")
|
|
342
|
+
if length % 2:
|
|
343
|
+
raise AccessError(f"name is {length} bytes, an odd length for UTF-16")
|
|
340
344
|
return raw.decode("utf-16-le"), pos + 2 + length
|
|
341
345
|
|
|
342
346
|
|
|
@@ -463,6 +467,10 @@ def parse_table_definition(store: PageStore, page: int) -> TableDefinition:
|
|
|
463
467
|
pos += 2
|
|
464
468
|
if number == INDEX_COLUMN_UNUSED:
|
|
465
469
|
break
|
|
470
|
+
if pos + 8 > len(buf):
|
|
471
|
+
raise AccessError(
|
|
472
|
+
f"table definition at page {page} ends inside its usage-map block"
|
|
473
|
+
)
|
|
466
474
|
column_usage_maps[number] = (u32(pos), u32(pos + 4))
|
|
467
475
|
pos += 8
|
|
468
476
|
|
|
@@ -430,6 +430,22 @@ class Index:
|
|
|
430
430
|
return encode_key(key_values, self.columns)
|
|
431
431
|
|
|
432
432
|
|
|
433
|
+
class Row(dict[str, object]):
|
|
434
|
+
"""A decoded row: every column the table defines, by name.
|
|
435
|
+
|
|
436
|
+
A column the definition does not hold is an AccessError, not a bare
|
|
437
|
+
KeyError: when an engine table lacks one of its own columns, the
|
|
438
|
+
definition is damaged, and the message should say so (issue #45).
|
|
439
|
+
"""
|
|
440
|
+
|
|
441
|
+
def __init__(self, table: str = "") -> None:
|
|
442
|
+
super().__init__()
|
|
443
|
+
self.table = table
|
|
444
|
+
|
|
445
|
+
def __missing__(self, column: str) -> object:
|
|
446
|
+
raise AccessError(f"table {self.table!r} has no column {column!r}; its definition is damaged")
|
|
447
|
+
|
|
448
|
+
|
|
433
449
|
class Table:
|
|
434
450
|
"""A table: its definition plus the rows on its owned pages."""
|
|
435
451
|
|
|
@@ -778,7 +794,7 @@ class Table:
|
|
|
778
794
|
return rid if ref.kind == LongValueRef.KIND_INLINE else RowId(ref.page, ref.row)
|
|
779
795
|
|
|
780
796
|
def decode(self, raw: RawRow) -> dict[str, object]:
|
|
781
|
-
out
|
|
797
|
+
out = Row(self.name)
|
|
782
798
|
for column in self.columns:
|
|
783
799
|
value = raw.values.get(column.number)
|
|
784
800
|
if value is None:
|
|
@@ -254,6 +254,12 @@ class CFB:
|
|
|
254
254
|
|
|
255
255
|
if major_ver not in (3, 4):
|
|
256
256
|
raise CFBError(f"Unsupported CFB major version: {major_ver}")
|
|
257
|
+
# [MS-CFB] 2.2: 512-byte sectors in version 3, 4096 in version 4, and
|
|
258
|
+
# 64-byte mini sectors in both.
|
|
259
|
+
if sector_size_pow != (9 if major_ver == 3 else 12) or mini_sector_size_pow != 6:
|
|
260
|
+
raise CFBError(
|
|
261
|
+
f"Unsupported CFB sector sizes: shift {sector_size_pow}, mini shift {mini_sector_size_pow}."
|
|
262
|
+
)
|
|
257
263
|
|
|
258
264
|
self.sector_size = 1 << sector_size_pow # 512 or 4096
|
|
259
265
|
self.mini_sector_size = 1 << mini_sector_size_pow # 64
|
|
@@ -264,11 +270,22 @@ class CFB:
|
|
|
264
270
|
|
|
265
271
|
# Follow DIFAT chain for large files (> 109 FAT sectors)
|
|
266
272
|
if difat_start != _ENDOFCHAIN and num_difat_sectors > 0:
|
|
273
|
+
# The header's count is only a claim: a file cannot hold more
|
|
274
|
+
# sectors than its size allows, and a chain that returns to a
|
|
275
|
+
# sector it has visited would otherwise loop for that count.
|
|
276
|
+
if num_difat_sectors > max(0, len(self._data) // self.sector_size - 1):
|
|
277
|
+
raise CFBError(f"The header claims {num_difat_sectors} DIFAT sectors; the file is too short.")
|
|
267
278
|
sector = difat_start
|
|
279
|
+
seen: set[int] = set()
|
|
268
280
|
for _ in range(num_difat_sectors):
|
|
269
281
|
if sector in (_ENDOFCHAIN, _FREESECT):
|
|
270
282
|
break
|
|
283
|
+
if sector in seen:
|
|
284
|
+
raise CFBError(f"The DIFAT chain returns to sector {sector}.")
|
|
285
|
+
seen.add(sector)
|
|
271
286
|
sector_data = self._sector(sector)
|
|
287
|
+
if len(sector_data) < self.sector_size:
|
|
288
|
+
raise CFBError(f"DIFAT sector {sector} lies past the end of the file.")
|
|
272
289
|
entries_per_difat = (self.sector_size // 4) - 1
|
|
273
290
|
extra = [int(x) for x in struct.unpack_from(f"<{entries_per_difat}I", sector_data)]
|
|
274
291
|
difat.extend(extra)
|
|
@@ -310,7 +327,10 @@ class CFB:
|
|
|
310
327
|
self._mini_stream = bytes(ms_raw)
|
|
311
328
|
|
|
312
329
|
def _sector(self, index: int) -> bytes:
|
|
313
|
-
|
|
330
|
+
# The header takes the whole first sector: 512 bytes in version 3, and
|
|
331
|
+
# 512 bytes padded to 4096 in version 4 ([MS-CFB] 2.2), so sector n
|
|
332
|
+
# starts at (n + 1) * sector_size, not after the 512 header bytes.
|
|
333
|
+
offset = (index + 1) * self.sector_size
|
|
314
334
|
return self._data[offset: offset + self.sector_size]
|
|
315
335
|
|
|
316
336
|
def _chain(self, start: int) -> list[int]:
|
|
@@ -6,6 +6,8 @@ Supports:
|
|
|
6
6
|
- .xlsb (Binary workbook — ZIP containing xl/vbaProject.bin)
|
|
7
7
|
- .xlam (Macro-enabled add-in — same ZIP container as .xlsm)
|
|
8
8
|
- .xls (Legacy BIFF8 — the entire file is a CFB)
|
|
9
|
+
- .xltm, .xlt, .xla (templates and the legacy add-in: the .xlsm and
|
|
10
|
+
.xls containers; read only)
|
|
9
11
|
|
|
10
12
|
Usage
|
|
11
13
|
-----
|
|
@@ -27,8 +29,8 @@ from pyopenvba._host import VBAHostFile
|
|
|
27
29
|
from pyopenvba._new_project import WORKBOOK_BASE, document_header, excel_code_name_edits, excel_code_names
|
|
28
30
|
from pyopenvba.vba import VBAProject, split_attribute_header
|
|
29
31
|
|
|
30
|
-
_ZIP_FORMATS = frozenset({".xlsm", ".xlsb", ".xlam"})
|
|
31
|
-
_CFB_FORMATS = frozenset({".xls"})
|
|
32
|
+
_ZIP_FORMATS = frozenset({".xlsm", ".xlsb", ".xlam", ".xltm"})
|
|
33
|
+
_CFB_FORMATS = frozenset({".xls", ".xlt", ".xla"})
|
|
32
34
|
_VBA_ENTRY = "xl/vbaProject.bin"
|
|
33
35
|
|
|
34
36
|
|
|
@@ -49,6 +51,7 @@ class ExcelFile(VBAHostFile):
|
|
|
49
51
|
_application = "Excel"
|
|
50
52
|
_project_storage = "_VBA_PROJECT_CUR"
|
|
51
53
|
_project_formats = frozenset({".xlsm"})
|
|
54
|
+
_read_only_formats = frozenset({".xltm", ".xlt", ".xla"})
|
|
52
55
|
_main_part = "xl/workbook.xml"
|
|
53
56
|
|
|
54
57
|
# ------------------------------------------------------------------
|
|
@@ -219,7 +219,7 @@ def text_value(raw: str) -> str:
|
|
|
219
219
|
for piece in body[index + 2 : close].split(","):
|
|
220
220
|
if piece in _ESCAPES:
|
|
221
221
|
out.append(_ESCAPES[piece])
|
|
222
|
-
elif re.fullmatch(r"[0-9A-Fa-f]{4,8}", piece):
|
|
222
|
+
elif re.fullmatch(r"[0-9A-Fa-f]{4,8}", piece) and int(piece, 16) <= 0x10FFFF:
|
|
223
223
|
out.append(chr(int(piece, 16)))
|
|
224
224
|
else:
|
|
225
225
|
out.append(f"#({piece})")
|
|
@@ -624,4 +624,10 @@ def parse(source: str) -> Node:
|
|
|
624
624
|
body = source.strip()
|
|
625
625
|
if not body:
|
|
626
626
|
raise MSyntaxError("an empty formula")
|
|
627
|
-
|
|
627
|
+
try:
|
|
628
|
+
return Parser(tokenize(body), body).parse()
|
|
629
|
+
except RecursionError:
|
|
630
|
+
# The parser descends once per nesting level, so a formula nested
|
|
631
|
+
# past Python's recursion limit, as a hostile workbook can hold, is
|
|
632
|
+
# refused like any other malformed formula.
|
|
633
|
+
raise MSyntaxError("the formula nests too deeply to parse") from None
|
|
@@ -4,6 +4,7 @@ PowerPoint file handler.
|
|
|
4
4
|
Supports:
|
|
5
5
|
- .pptm (OOXML macro-enabled presentation -- ZIP containing ppt/vbaProject.bin)
|
|
6
6
|
- .potm (OOXML macro-enabled template -- ZIP containing ppt/vbaProject.bin)
|
|
7
|
+
- .ppsm, .ppam (show and add-in -- the .pptm container; read only)
|
|
7
8
|
- .ppt (Legacy PowerPoint -- a CFB whose 'PowerPoint Document' stream
|
|
8
9
|
embeds the VBA project as a deflated CFB; see
|
|
9
10
|
:mod:`pyopenvba._ppt_container`)
|
|
@@ -28,7 +29,7 @@ from pyopenvba._host import VBAHostFile
|
|
|
28
29
|
from pyopenvba.cfb import CFB
|
|
29
30
|
from pyopenvba.vba import VBAProject
|
|
30
31
|
|
|
31
|
-
_ZIP_FORMATS = frozenset({".pptm", ".potm"})
|
|
32
|
+
_ZIP_FORMATS = frozenset({".pptm", ".potm", ".ppsm", ".ppam"})
|
|
32
33
|
_CFB_FORMATS = frozenset({".ppt"})
|
|
33
34
|
_VBA_ENTRY = "ppt/vbaProject.bin"
|
|
34
35
|
|
|
@@ -51,6 +52,7 @@ class PowerPointFile(VBAHostFile):
|
|
|
51
52
|
# A binary presentation embeds its project in a record, found by _vba_cfb_bytes.
|
|
52
53
|
_project_storage = None
|
|
53
54
|
_project_formats = frozenset({".pptm"})
|
|
55
|
+
_read_only_formats = frozenset({".ppsm", ".ppam"})
|
|
54
56
|
_main_part = "ppt/presentation.xml"
|
|
55
57
|
|
|
56
58
|
# A new project, as PowerPoint makes one: empty, and not written until
|
|
@@ -468,7 +468,7 @@ def parse_items(xml: bytes) -> list[Item]:
|
|
|
468
468
|
"""The items a ``LocalPackageMetadataFile`` document lists."""
|
|
469
469
|
try:
|
|
470
470
|
root = ElementTree.fromstring(xml.decode("utf-8-sig"))
|
|
471
|
-
except ElementTree.ParseError as exc:
|
|
471
|
+
except (ElementTree.ParseError, UnicodeDecodeError) as exc:
|
|
472
472
|
raise PowerQueryError(f"the metadata XML does not parse: {exc}") from exc
|
|
473
473
|
if root.tag.rsplit("}", 1)[-1] != _ROOT:
|
|
474
474
|
raise PowerQueryError(f"the metadata document is <{root.tag}>, not <{_ROOT}>")
|
|
@@ -85,17 +85,28 @@ class Package:
|
|
|
85
85
|
parts: list[Part] = []
|
|
86
86
|
at = 0
|
|
87
87
|
while raw[at : at + 4] == b"PK\x03\x04":
|
|
88
|
+
if len(raw) < at + 30:
|
|
89
|
+
raise PowerQueryError("the package ends inside a part's header")
|
|
88
90
|
(_need, _flags, method, dos_time, dos_date, _crc, csize, usize, nlen, xlen) = struct.unpack_from(
|
|
89
91
|
"<HHHHHIIIHH", raw, at + 4
|
|
90
92
|
)
|
|
91
|
-
name = raw[at + 30 : at + 30 + nlen].decode("utf-8")
|
|
92
93
|
body_at = at + 30 + nlen + xlen
|
|
94
|
+
if len(raw) < body_at + csize:
|
|
95
|
+
raise PowerQueryError("the package ends inside a part")
|
|
96
|
+
try:
|
|
97
|
+
name = raw[at + 30 : at + 30 + nlen].decode("utf-8")
|
|
98
|
+
except UnicodeDecodeError as exc:
|
|
99
|
+
raise PowerQueryError(f"a package part's name is not UTF-8: {exc}") from exc
|
|
93
100
|
body = raw[body_at : body_at + csize]
|
|
94
101
|
if method == 0:
|
|
95
102
|
data = body
|
|
96
103
|
elif method == DEFLATED:
|
|
104
|
+
# Inflate no more than the size the header states, plus one
|
|
105
|
+
# byte to tell a longer part, so a small part cannot expand
|
|
106
|
+
# without limit.
|
|
107
|
+
inflater = zlib.decompressobj(-15)
|
|
97
108
|
try:
|
|
98
|
-
data =
|
|
109
|
+
data = inflater.decompress(body, usize + 1)
|
|
99
110
|
except zlib.error as exc:
|
|
100
111
|
raise PowerQueryError(f"the package part {name!r} does not inflate: {exc}") from exc
|
|
101
112
|
else:
|
|
@@ -892,6 +892,23 @@ class VBAModule:
|
|
|
892
892
|
)
|
|
893
893
|
|
|
894
894
|
|
|
895
|
+
#: Names the VBE will not give a module (VBComponents.Add fails with
|
|
896
|
+
#: 0x800AC3D4), measured in 64-bit Excel 16.0 (issue #40), and refused by a
|
|
897
|
+
#: rename too, which would make the same module. A
|
|
898
|
+
#: module so named still compiles, but code can never name it: ``Print.Hi``
|
|
899
|
+
#: is a syntax error and ``Me.Hi`` an invalid use of Me. Line, Width, Name,
|
|
900
|
+
#: Err, Mid, Time, Error, Reset, Beep, Load and Unload are accepted.
|
|
901
|
+
VBE_REFUSED_MODULE_NAMES = frozenset({
|
|
902
|
+
"circle", "pset", "scale", "print", "seek", "input", "get", "put", "open",
|
|
903
|
+
"tab", "spc", "debug", "me", "array", "lbound", "date", "stop",
|
|
904
|
+
})
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
def _check_module_name(name: str) -> None:
|
|
908
|
+
if name.casefold() in VBE_REFUSED_MODULE_NAMES:
|
|
909
|
+
raise ValueError(f"The VBE refuses {name!r} as a module name.")
|
|
910
|
+
|
|
911
|
+
|
|
895
912
|
@dataclass
|
|
896
913
|
class VBAProject:
|
|
897
914
|
"""Represents a parsed VBA project."""
|
|
@@ -963,8 +980,10 @@ class VBAProject:
|
|
|
963
980
|
``Attribute VB_Base`` ensured), so ``.cls`` files exported from
|
|
964
981
|
the VBE are accepted as-is. See :func:`normalize_class_source`.
|
|
965
982
|
|
|
966
|
-
Raises ``ValueError`` if a module with that name already exists
|
|
983
|
+
Raises ``ValueError`` if a module with that name already exists, or
|
|
984
|
+
if the VBE refuses the name (see :data:`VBE_REFUSED_MODULE_NAMES`).
|
|
967
985
|
"""
|
|
986
|
+
_check_module_name(name)
|
|
968
987
|
needle = name.casefold()
|
|
969
988
|
if any(m.name.casefold() == needle for m in self.modules):
|
|
970
989
|
raise ValueError(f"Module already exists: {name!r}")
|
|
@@ -1030,6 +1049,7 @@ class VBAProject:
|
|
|
1030
1049
|
module = self.get_module(old_name)
|
|
1031
1050
|
if module.name == new_name:
|
|
1032
1051
|
return module
|
|
1052
|
+
_check_module_name(new_name)
|
|
1033
1053
|
needle = new_name.casefold()
|
|
1034
1054
|
if needle != module.name.casefold() and any(
|
|
1035
1055
|
m.name.casefold() == needle for m in self.modules
|
|
@@ -5,6 +5,7 @@ Supports:
|
|
|
5
5
|
- .docm (OOXML macro-enabled document -- ZIP containing word/vbaProject.bin)
|
|
6
6
|
- .dotm (OOXML macro-enabled template -- ZIP containing word/vbaProject.bin)
|
|
7
7
|
- .doc (Legacy Word -- the entire file is a CFB)
|
|
8
|
+
- .dot (Legacy template -- the .doc container; read only)
|
|
8
9
|
|
|
9
10
|
Usage
|
|
10
11
|
-----
|
|
@@ -25,7 +26,7 @@ from pathlib import Path
|
|
|
25
26
|
from pyopenvba._host import VBAHostFile
|
|
26
27
|
|
|
27
28
|
_ZIP_FORMATS = frozenset({".docm", ".dotm"})
|
|
28
|
-
_CFB_FORMATS = frozenset({".doc"})
|
|
29
|
+
_CFB_FORMATS = frozenset({".doc", ".dot"})
|
|
29
30
|
_VBA_ENTRY = "word/vbaProject.bin"
|
|
30
31
|
|
|
31
32
|
|
|
@@ -46,6 +47,7 @@ class WordFile(VBAHostFile):
|
|
|
46
47
|
_application = "Word"
|
|
47
48
|
_project_storage = "Macros"
|
|
48
49
|
_project_formats = frozenset({".docm"})
|
|
50
|
+
_read_only_formats = frozenset({".dot"})
|
|
49
51
|
_main_part = "word/document.xml"
|
|
50
52
|
|
|
51
53
|
# A new project, as Word makes one: ThisDocument, from the Normal
|
|
@@ -19,11 +19,29 @@ crash, is treated as a fuzz regression.
|
|
|
19
19
|
| `dir/` | `pyopenvba.vba._parse_dir_stream()` | `VBAProjectError`, `UnicodeDecodeError`, `IndexError`, `struct.error` |
|
|
20
20
|
| `project/` | `pyopenvba.vba.parse_project_stream()` | `VBAProjectError`, `UnicodeDecodeError` |
|
|
21
21
|
| `projectwm/` | `pyopenvba.vba.parse_projectwm()` | `VBAProjectError`, `UnicodeDecodeError` |
|
|
22
|
+
| `mashup/` | `pyopenvba.powerquery._mashup.Mashup.parse()` | `PowerQueryError` |
|
|
23
|
+
| `mformula/` | `pyopenvba.mlang._parse.parse()` | `MSyntaxError` |
|
|
22
24
|
|
|
23
25
|
Each input file is **raw bytes** (extension is informational only;
|
|
24
26
|
`.bin` by convention). Filenames should describe the seed, e.g.
|
|
25
27
|
`empty.bin`, `truncated_header.bin`, `regression_2026_05_xxxx.bin`.
|
|
26
28
|
|
|
29
|
+
## Coverage-guided fuzzing
|
|
30
|
+
|
|
31
|
+
[`fuzz/fuzz_parsers.py`](../../fuzz/fuzz_parsers.py) runs each target under
|
|
32
|
+
Atheris, seeded from its directory here, with the same acceptable
|
|
33
|
+
exceptions. The Fuzz workflow runs every target daily and on each change to
|
|
34
|
+
the parsers. To run one yourself on Linux with Python 3.12 or later:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
python -m pip install --require-hashes -r .github/requirements/fuzz.txt
|
|
38
|
+
python -m pip install --no-deps --no-build-isolation -e .
|
|
39
|
+
python fuzz/fuzz_parsers.py decompress -max_total_time=60 tests/fuzz_corpus/decompress
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
A finding is written to a `crash-*` file. Minimise it, add it here as a
|
|
43
|
+
regression seed, and fix the parser.
|
|
44
|
+
|
|
27
45
|
## Adding a new seed
|
|
28
46
|
|
|
29
47
|
When a fuzz run discovers a new failure mode, drop the offending bytes
|
|
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.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/blank_files/blank_database.accdb
RENAMED
|
File without changes
|
{pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/blank_files/blank_database_module.accdb
RENAMED
|
File without changes
|
{pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/blank_files/blank_document.docm
RENAMED
|
File without changes
|
{pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/blank_files/blank_excel_addin.xlam
RENAMED
|
File without changes
|
{pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/blank_files/blank_presentation.pptm
RENAMED
|
File without changes
|
{pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/blank_files/blank_workbook.xlsb
RENAMED
|
File without changes
|
{pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/blank_files/blank_workbook.xlsm
RENAMED
|
File without changes
|
{pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/blank_files/blank_workbook.xlsx
RENAMED
|
File without changes
|
{pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/blank_files/engine_skeleton.accdb
RENAMED
|
File without changes
|
{pyopenvba-6.3.4 → pyopenvba-6.3.5}/src/pyopenvba/_templates/blank_files/engine_skeleton.mdb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|