pyOpenVBA 5.2.1__tar.gz → 5.2.2__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.
Files changed (89) hide show
  1. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/PKG-INFO +1 -1
  2. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/docs/power_query.md +7 -0
  3. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/pyproject.toml +1 -1
  4. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/__init__.py +1 -1
  5. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/powerquery/_sheets.py +40 -9
  6. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/.gitignore +0 -0
  7. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/LICENSE.md +0 -0
  8. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/README.md +0 -0
  9. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/docs/architecture.md +0 -0
  10. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/docs/ms-ovba-implementation-guide_v2.md +0 -0
  11. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/docs/research/access_complex/README.md +0 -0
  12. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/docs/research/access_designs/README.md +0 -0
  13. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/docs/research/access_macros/README.md +0 -0
  14. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/docs/research/access_write/README.md +0 -0
  15. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/docs/research/pcode/README.md +0 -0
  16. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/docs/roadmap.md +0 -0
  17. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/__main__.py +0 -0
  18. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_deflate.py +0 -0
  19. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_host.py +0 -0
  20. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_oforms_pages.py +0 -0
  21. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_oforms_records.py +0 -0
  22. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_ppt_container.py +0 -0
  23. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_templates/__init__.py +0 -0
  24. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_templates/blank_files/blank_database.accdb +0 -0
  25. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_templates/blank_files/blank_database_module.accdb +0 -0
  26. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_templates/blank_files/blank_document.docm +0 -0
  27. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_templates/blank_files/blank_excel_addin.xlam +0 -0
  28. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_templates/blank_files/blank_presentation.pptm +0 -0
  29. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_templates/blank_files/blank_workbook.xlsb +0 -0
  30. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_templates/blank_files/blank_workbook.xlsm +0 -0
  31. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_templates/blank_files/blank_workbook.xlsx +0 -0
  32. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_templates/blank_files/engine_skeleton.accdb +0 -0
  33. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_templates/blank_files/engine_skeleton.mdb +0 -0
  34. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_templates/designs/form.blob +0 -0
  35. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_templates/designs/form.lvprop +0 -0
  36. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_templates/designs/form.propdata +0 -0
  37. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_templates/designs/form.prototypes +0 -0
  38. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_templates/designs/form.typeinfo +0 -0
  39. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_templates/designs/report.blob +0 -0
  40. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_templates/designs/report.lvprop +0 -0
  41. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_templates/designs/report.propdata +0 -0
  42. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_templates/designs/report.prototypes +0 -0
  43. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/_templates/designs/report.typeinfo +0 -0
  44. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/__init__.py +0 -0
  45. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_alloc.py +0 -0
  46. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_btree.py +0 -0
  47. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_collation.py +0 -0
  48. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_collation_general_legacy.py +0 -0
  49. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_compact.py +0 -0
  50. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_complex.py +0 -0
  51. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_datapage.py +0 -0
  52. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_ddl.py +0 -0
  53. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_designs.py +0 -0
  54. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_facade.py +0 -0
  55. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_format.py +0 -0
  56. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_index.py +0 -0
  57. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_lval.py +0 -0
  58. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_macros.py +0 -0
  59. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_pages.py +0 -0
  60. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_props.py +0 -0
  61. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_queries.py +0 -0
  62. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_rows.py +0 -0
  63. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_schema.py +0 -0
  64. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_sql.py +0 -0
  65. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_storage.py +0 -0
  66. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_tdef.py +0 -0
  67. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_validate.py +0 -0
  68. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/_vba.py +0 -0
  69. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access/database.py +0 -0
  70. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/access_read.py +0 -0
  71. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/cfb.py +0 -0
  72. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/excel.py +0 -0
  73. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/exceptions.py +0 -0
  74. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/forms.py +0 -0
  75. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/powerpoint.py +0 -0
  76. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/powerquery/__init__.py +0 -0
  77. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/powerquery/_binary.py +0 -0
  78. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/powerquery/_files.py +0 -0
  79. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/powerquery/_mashup.py +0 -0
  80. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/powerquery/_metadata.py +0 -0
  81. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/powerquery/_opc.py +0 -0
  82. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/powerquery/_package.py +0 -0
  83. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/powerquery/_refresh.py +0 -0
  84. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/powerquery/_section.py +0 -0
  85. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/powerquery/workbook.py +0 -0
  86. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/vba.py +0 -0
  87. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/vba_pcode.py +0 -0
  88. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/src/pyopenvba/word.py +0 -0
  89. {pyopenvba-5.2.1 → pyopenvba-5.2.2}/tests/fuzz_corpus/README.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pyOpenVBA
3
- Version: 5.2.1
3
+ Version: 5.2.2
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
@@ -221,6 +221,13 @@ wrong: **a connections part holding no connections is one Excel refuses**,
221
221
  so removing the last connection removes the part, its content type and
222
222
  its relationship as well.
223
223
 
224
+ The hidden name has a rule of its own. **`localSheetId` is the zero-based
225
+ position of the sheet the name belongs to** among the workbook's sheets,
226
+ not a constant, and it has to agree with the sheet the reference names.
227
+ Written as 0 it was right only while the table landed on the first sheet;
228
+ anywhere else Excel refuses to open the workbook, which is invisible in a
229
+ one-sheet file and so went unnoticed.
230
+
224
231
  ### Refresh control
225
232
 
226
233
  The boxes in Excel's Connection Properties dialog belong to that
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "pyOpenVBA"
7
- version = "5.2.1"
7
+ version = "5.2.2"
8
8
  description = "Read and write VBA macros and Excel Power Query in pure Python, no dependencies."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -291,4 +291,4 @@ def push_power_query(
291
291
  """
292
292
  return _push_queries(src_dir, workbook, out=out, encoding=encoding, remove_missing=remove_missing)
293
293
 
294
- __version__ = "5.2.1"
294
+ __version__ = "5.2.2"
@@ -169,13 +169,15 @@ def drop_content_type(package: OpcFile, part: str) -> None:
169
169
  )
170
170
 
171
171
 
172
- def sheet_part(package: OpcFile, sheet: str | int) -> tuple[str, str]:
173
- """The worksheet part for a sheet name or one-based position, and the
174
- name of the sheet."""
175
- workbook = package.read(_WORKBOOK).decode("utf-8")
176
- rels = package.read(_WORKBOOK_RELS).decode("utf-8")
177
- targets = _relationship_targets(rels)
178
- sheets: list[tuple[str, str]] = []
172
+ def sheet_entries(workbook: str) -> list[tuple[str, str]]:
173
+ """``(name, relationship id)`` for each sheet, in the order the
174
+ workbook lists them.
175
+
176
+ The order is load-bearing: a ``definedName`` names the sheet it is
177
+ local to by its position here, so the list is the one place that
178
+ settles both which part a sheet is and what index it has.
179
+ """
180
+ out: list[tuple[str, str]] = []
179
181
  for element in re.findall(r"<sheet\b[^>]*>", workbook):
180
182
  attributes = _attributes(element)
181
183
  # The relationship attribute carries a namespace prefix, and which
@@ -184,7 +186,27 @@ def sheet_part(package: OpcFile, sheet: str | int) -> tuple[str, str]:
184
186
  (value for key, value in attributes.items() if key.endswith(":id")), None
185
187
  )
186
188
  if "name" in attributes and identifier:
187
- sheets.append((attributes["name"], identifier))
189
+ out.append((attributes["name"], identifier))
190
+ return out
191
+
192
+
193
+ def sheet_index(package: OpcFile, name: str) -> int:
194
+ """A sheet's zero-based position among the workbook's sheets."""
195
+ sheets = sheet_entries(package.read(_WORKBOOK).decode("utf-8"))
196
+ for index, (found, _identifier) in enumerate(sheets):
197
+ if found == name:
198
+ return index
199
+ known = ", ".join(pair[0] for pair in sheets)
200
+ raise PowerQueryError(f"this workbook has no sheet named {name!r}; it has: {known}")
201
+
202
+
203
+ def sheet_part(package: OpcFile, sheet: str | int) -> tuple[str, str]:
204
+ """The worksheet part for a sheet name or one-based position, and the
205
+ name of the sheet."""
206
+ workbook = package.read(_WORKBOOK).decode("utf-8")
207
+ rels = package.read(_WORKBOOK_RELS).decode("utf-8")
208
+ targets = _relationship_targets(rels)
209
+ sheets = sheet_entries(workbook)
188
210
  if not sheets:
189
211
  raise PowerQueryError("this workbook lists no worksheets")
190
212
  if isinstance(sheet, int):
@@ -432,13 +454,22 @@ def _widen_dimension(sheet: str, reference: str) -> str:
432
454
 
433
455
 
434
456
  def _add_defined_name(package: OpcFile, sheet_name: str, number: int, reference: str) -> None:
457
+ """The hidden name Excel gives an external data range.
458
+
459
+ ``localSheetId`` is the zero-based position of the sheet the name
460
+ belongs to among the workbook's sheets, so it has to be looked up
461
+ rather than assumed. Writing a constant 0 was right only while the
462
+ table landed on the first sheet, and named a different sheet than the
463
+ reference did for any other, which Excel will not open.
464
+ """
465
+ local = sheet_index(package, sheet_name)
435
466
  raw = package.read(_WORKBOOK).decode("utf-8")
436
467
  first, last = reference.split(":")
437
468
  absolute = f"${column_letter(CellRef.parse(first).column)}${CellRef.parse(first).row}"
438
469
  absolute += f":${column_letter(CellRef.parse(last).column)}${CellRef.parse(last).row}"
439
470
  quoted = f"'{sheet_name}'" if re.search(r"[^A-Za-z0-9_]", sheet_name) else sheet_name
440
471
  defined = (
441
- f'<definedName name="ExternalData_{number}" localSheetId="0" hidden="1">'
472
+ f'<definedName name="ExternalData_{number}" localSheetId="{local}" hidden="1">'
442
473
  f"{quoted}!{absolute}</definedName>"
443
474
  )
444
475
  empty = re.search(r"<definedNames\s*/>", raw)
File without changes
File without changes
File without changes
File without changes