pyaccesskit 0.1.0__py3-none-any.whl

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 (86) hide show
  1. pyaccesskit/AGENT_GUIDE.md +455 -0
  2. pyaccesskit/__init__.py +167 -0
  3. pyaccesskit/__main__.py +6 -0
  4. pyaccesskit/_backends/__init__.py +0 -0
  5. pyaccesskit/_backends/access/__init__.py +1 -0
  6. pyaccesskit/_backends/access/design.py +415 -0
  7. pyaccesskit/_backends/dao/__init__.py +1 -0
  8. pyaccesskit/_backends/dao/profile.py +40 -0
  9. pyaccesskit/_backends/dao/schema.py +805 -0
  10. pyaccesskit/_backends/dao/typemap.py +390 -0
  11. pyaccesskit/_backends/fake/__init__.py +3 -0
  12. pyaccesskit/_backends/fake/backend.py +680 -0
  13. pyaccesskit/_backends/protocols.py +339 -0
  14. pyaccesskit/_com/__init__.py +1 -0
  15. pyaccesskit/_com/constants.py +394 -0
  16. pyaccesskit/_com/dispatch.py +50 -0
  17. pyaccesskit/_com/errors.py +184 -0
  18. pyaccesskit/_com/gateway.py +199 -0
  19. pyaccesskit/_com/raw.py +164 -0
  20. pyaccesskit/_com/runtime.py +39 -0
  21. pyaccesskit/_com/variants.py +72 -0
  22. pyaccesskit/_engines/__init__.py +48 -0
  23. pyaccesskit/_engines/access.py +300 -0
  24. pyaccesskit/_engines/inproc.py +148 -0
  25. pyaccesskit/_engines/probe.py +231 -0
  26. pyaccesskit/_ledger.py +158 -0
  27. pyaccesskit/_ops/__init__.py +0 -0
  28. pyaccesskit/_ops/design.py +127 -0
  29. pyaccesskit/_ops/schema.py +471 -0
  30. pyaccesskit/_session/__init__.py +1 -0
  31. pyaccesskit/_session/protocols.py +78 -0
  32. pyaccesskit/_session/session.py +354 -0
  33. pyaccesskit/_text/__init__.py +0 -0
  34. pyaccesskit/_text/codec.py +114 -0
  35. pyaccesskit/_version.py +3 -0
  36. pyaccesskit/_win/__init__.py +1 -0
  37. pyaccesskit/_win/access_process.py +348 -0
  38. pyaccesskit/_win/console.py +56 -0
  39. pyaccesskit/_win/inspector.py +53 -0
  40. pyaccesskit/_win/job.py +65 -0
  41. pyaccesskit/_win/processes.py +159 -0
  42. pyaccesskit/_win/watchdog.py +253 -0
  43. pyaccesskit/cli/__init__.py +10 -0
  44. pyaccesskit/cli/_output.py +101 -0
  45. pyaccesskit/cli/agent.py +99 -0
  46. pyaccesskit/cli/app.py +54 -0
  47. pyaccesskit/cli/cleanup.py +56 -0
  48. pyaccesskit/cli/doctor.py +101 -0
  49. pyaccesskit/cli/inspection.py +223 -0
  50. pyaccesskit/database.py +296 -0
  51. pyaccesskit/diagnostics.py +319 -0
  52. pyaccesskit/enums.py +258 -0
  53. pyaccesskit/errors.py +407 -0
  54. pyaccesskit/forms/__init__.py +45 -0
  55. pyaccesskit/forms/builder.py +295 -0
  56. pyaccesskit/forms/collection.py +117 -0
  57. pyaccesskit/forms/controls.py +157 -0
  58. pyaccesskit/forms/layout.py +300 -0
  59. pyaccesskit/forms/spec.py +169 -0
  60. pyaccesskit/forms/vba.py +138 -0
  61. pyaccesskit/maintenance.py +32 -0
  62. pyaccesskit/modules.py +101 -0
  63. pyaccesskit/objects.py +81 -0
  64. pyaccesskit/options.py +40 -0
  65. pyaccesskit/properties.py +74 -0
  66. pyaccesskit/py.typed +0 -0
  67. pyaccesskit/queries.py +190 -0
  68. pyaccesskit/relationships.py +143 -0
  69. pyaccesskit/schema/__init__.py +73 -0
  70. pyaccesskit/schema/_base.py +55 -0
  71. pyaccesskit/schema/_reserved_words.py +55 -0
  72. pyaccesskit/schema/columns.py +609 -0
  73. pyaccesskit/schema/compat.py +57 -0
  74. pyaccesskit/schema/expressions.py +162 -0
  75. pyaccesskit/schema/indexes.py +114 -0
  76. pyaccesskit/schema/names.py +122 -0
  77. pyaccesskit/schema/queries.py +192 -0
  78. pyaccesskit/schema/relationships.py +132 -0
  79. pyaccesskit/schema/tables.py +178 -0
  80. pyaccesskit/tables.py +333 -0
  81. pyaccesskit/units.py +301 -0
  82. pyaccesskit-0.1.0.dist-info/METADATA +201 -0
  83. pyaccesskit-0.1.0.dist-info/RECORD +86 -0
  84. pyaccesskit-0.1.0.dist-info/WHEEL +4 -0
  85. pyaccesskit-0.1.0.dist-info/entry_points.txt +2 -0
  86. pyaccesskit-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,415 @@
1
+ # pyright: basic
2
+ """``Access.Application`` implementation of :class:`~pyaccesskit._backends.protocols.DesignBackend`.
3
+
4
+ Form building follows the flow verified in spike S6:
5
+
6
+ ``CreateForm`` (an unsaved, auto-named form in Design view) → header/footer via
7
+ ``RunCommand(acCmdFormHdrFtr)`` when needed → properties and section heights (``Section`` is an *indexed*
8
+ property, so it goes through the exact-argument gateway) → ``CreateControl`` for every control and attached
9
+ label → event properties + form module → ``DoCmd.Close(acSaveYes)`` → atomic swap with ``DoCmd.Rename``.
10
+
11
+ If anything fails before the save, the unsaved form is closed with ``acSaveNo`` and nothing is left behind.
12
+ When replacing, the old form is renamed to a hidden ``~pak_bak_…`` name first and restored on failure.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import contextlib
18
+ import tempfile
19
+ import uuid
20
+ from collections.abc import Callable
21
+ from pathlib import Path
22
+ from typing import Any
23
+
24
+ import pywintypes
25
+
26
+ from pyaccesskit._backends.protocols import ControlInfo
27
+ from pyaccesskit._com import constants as c
28
+ from pyaccesskit._com.gateway import Com, call, get, put
29
+ from pyaccesskit.enums import ControlKind, FormView, ObjectKind, RowSourceType, ScrollBars, Section
30
+ from pyaccesskit.errors import SpecError
31
+ from pyaccesskit.forms.controls import (
32
+ ButtonSpec,
33
+ CheckBoxSpec,
34
+ ComboBoxSpec,
35
+ LabelSpec,
36
+ TextBoxSpec,
37
+ )
38
+ from pyaccesskit.forms.layout import ResolvedForm
39
+ from pyaccesskit.units import Length
40
+
41
+ __all__ = ["AccessDesignBackend"]
42
+
43
+ AC_CMD_FORM_HDR_FTR = 36
44
+ _AC_TYPES = {
45
+ ObjectKind.FORM: c.AcObjectType.acForm,
46
+ ObjectKind.REPORT: c.AcObjectType.acReport,
47
+ ObjectKind.MACRO: c.AcObjectType.acMacro,
48
+ ObjectKind.MODULE: c.AcObjectType.acModule,
49
+ ObjectKind.QUERY: c.AcObjectType.acQuery,
50
+ }
51
+ _ALL_COLLECTIONS = {
52
+ ObjectKind.FORM: ("CurrentProject", "AllForms"),
53
+ ObjectKind.REPORT: ("CurrentProject", "AllReports"),
54
+ ObjectKind.MACRO: ("CurrentProject", "AllMacros"),
55
+ ObjectKind.MODULE: ("CurrentProject", "AllModules"),
56
+ ObjectKind.QUERY: ("CurrentData", "AllQueries"),
57
+ }
58
+ _CONTROL_TYPES = {
59
+ ControlKind.LABEL: c.AcControlType.acLabel,
60
+ ControlKind.TEXTBOX: c.AcControlType.acTextBox,
61
+ ControlKind.CHECKBOX: c.AcControlType.acCheckBox,
62
+ ControlKind.COMBOBOX: c.AcControlType.acComboBox,
63
+ ControlKind.BUTTON: c.AcControlType.acCommandButton,
64
+ }
65
+ _CONTROL_KINDS = {
66
+ int(c.AcControlType.acLabel): ControlKind.LABEL,
67
+ int(c.AcControlType.acTextBox): ControlKind.TEXTBOX,
68
+ int(c.AcControlType.acCheckBox): ControlKind.CHECKBOX,
69
+ int(c.AcControlType.acComboBox): ControlKind.COMBOBOX,
70
+ int(c.AcControlType.acListBox): ControlKind.LISTBOX,
71
+ int(c.AcControlType.acCommandButton): ControlKind.BUTTON,
72
+ int(c.AcControlType.acOptionGroup): ControlKind.OPTION_GROUP,
73
+ int(c.AcControlType.acOptionButton): ControlKind.OPTION_BUTTON,
74
+ int(c.AcControlType.acToggleButton): ControlKind.TOGGLE_BUTTON,
75
+ int(c.AcControlType.acSubform): ControlKind.SUBFORM,
76
+ int(c.AcControlType.acImage): ControlKind.IMAGE,
77
+ int(c.AcControlType.acLine): ControlKind.LINE,
78
+ int(c.AcControlType.acRectangle): ControlKind.RECTANGLE,
79
+ int(c.AcControlType.acTabCtl): ControlKind.TAB_CONTROL,
80
+ int(c.AcControlType.acPage): ControlKind.PAGE,
81
+ int(c.AcControlType.acAttachment): ControlKind.ATTACHMENT,
82
+ }
83
+ _SECTIONS = {Section.DETAIL: 0, Section.HEADER: 1, Section.FOOTER: 2}
84
+ _SECTION_OF = {value: key for key, value in _SECTIONS.items()}
85
+ _DEFAULT_VIEWS = {
86
+ FormView.SINGLE: 0,
87
+ FormView.CONTINUOUS: 1,
88
+ FormView.DATASHEET: 2,
89
+ FormView.SPLIT: 5,
90
+ }
91
+ _SCROLL_BARS = {
92
+ ScrollBars.NEITHER: 0,
93
+ ScrollBars.HORIZONTAL: 1,
94
+ ScrollBars.VERTICAL: 2,
95
+ ScrollBars.BOTH: 3,
96
+ }
97
+ _ROW_SOURCE_TYPES = {
98
+ RowSourceType.TABLE_QUERY: "Table/Query",
99
+ RowSourceType.VALUE_LIST: "Value List",
100
+ }
101
+
102
+
103
+ def _optional(obj: Any, name: str) -> Any:
104
+ """Read a property that not every control has (``None`` when it is missing)."""
105
+ try:
106
+ return get(obj, name)
107
+ except pywintypes.com_error:
108
+ return None
109
+
110
+
111
+ class AccessDesignBackend:
112
+ """Design operations through a live ``Access.Application`` with the database open."""
113
+
114
+ def __init__(self, *, com: Callable[[], Com], app: Callable[[], Any]) -> None:
115
+ self._com_getter = com
116
+ self._app_getter = app
117
+ self._temp: Path | None = None
118
+
119
+ @property
120
+ def _com(self) -> Com:
121
+ return self._com_getter()
122
+
123
+ def _app(self) -> Any:
124
+ return self._app_getter()
125
+
126
+ def _docmd(self) -> Any:
127
+ return get(self._app(), "DoCmd")
128
+
129
+ def _temp_file(self, suffix: str) -> Path:
130
+ if self._temp is None:
131
+ self._temp = Path(tempfile.mkdtemp(prefix="pyaccesskit-"))
132
+ return self._temp / f"{uuid.uuid4().hex}{suffix}"
133
+
134
+ def cleanup(self) -> None:
135
+ """Delete the private temp folder used for text import/export."""
136
+ if self._temp is not None:
137
+ for path in self._temp.glob("*"):
138
+ with contextlib.suppress(OSError):
139
+ path.unlink()
140
+ with contextlib.suppress(OSError):
141
+ self._temp.rmdir()
142
+ self._temp = None
143
+
144
+ # ------------------------------------------------------------------------------------ objects
145
+ def list_objects(self, kind: ObjectKind) -> list[str]:
146
+ owner, collection_name = _ALL_COLLECTIONS[kind]
147
+ with self._com.op(f"list {kind.value}s"):
148
+ collection = get(get(self._app(), owner), collection_name)
149
+ return [
150
+ str(get(get(collection, "Item", i), "Name"))
151
+ for i in range(int(get(collection, "Count")))
152
+ ]
153
+
154
+ def delete_object(self, kind: ObjectKind, name: str) -> None:
155
+ with self._com.op(f"delete {kind.value} {name!r}", kind=kind, name=name):
156
+ call(self._docmd(), "DeleteObject", int(_AC_TYPES[kind]), name)
157
+
158
+ def rename_object(self, kind: ObjectKind, old: str, new: str) -> None:
159
+ with self._com.op(f"rename {kind.value} {old!r} to {new!r}", kind=kind, name=old):
160
+ call(self._docmd(), "Rename", new, int(_AC_TYPES[kind]), old)
161
+
162
+ def export_text(self, kind: ObjectKind, name: str) -> bytes:
163
+ path = self._temp_file(".txt")
164
+ try:
165
+ with self._com.op(f"export {kind.value} {name!r} as text", kind=kind, name=name):
166
+ call(self._app(), "SaveAsText", int(_AC_TYPES[kind]), name, str(path))
167
+ return path.read_bytes()
168
+ finally:
169
+ with contextlib.suppress(OSError):
170
+ path.unlink()
171
+
172
+ def import_text(self, kind: ObjectKind, name: str, data: bytes) -> None:
173
+ path = self._temp_file(".txt")
174
+ path.write_bytes(data)
175
+ try:
176
+ with self._com.op(f"import {kind.value} {name!r} from text", kind=kind, name=name):
177
+ call(self._app(), "LoadFromText", int(_AC_TYPES[kind]), name, str(path))
178
+ finally:
179
+ with contextlib.suppress(OSError):
180
+ path.unlink()
181
+
182
+ # -------------------------------------------------------------------------------------- forms
183
+ def build_form(self, form: ResolvedForm, *, replace: bool) -> None:
184
+ spec = form.spec
185
+ app = self._app()
186
+ docmd = self._docmd()
187
+ with self._com.op(f"build form {spec.name!r}", kind=ObjectKind.FORM, name=spec.name):
188
+ frm = call(app, "CreateForm")
189
+ auto = str(get(frm, "Name"))
190
+ saved = False
191
+ try:
192
+ self._populate(app, frm, auto, form)
193
+ del frm
194
+ call(docmd, "Close", int(c.AcObjectType.acForm), auto, int(c.AcCloseSave.acSaveYes))
195
+ saved = True
196
+ finally:
197
+ if not saved:
198
+ with contextlib.suppress(pywintypes.com_error):
199
+ call(
200
+ docmd,
201
+ "Close",
202
+ int(c.AcObjectType.acForm),
203
+ auto,
204
+ int(c.AcCloseSave.acSaveNo),
205
+ )
206
+ self._install(auto, spec.name, replace=replace)
207
+
208
+ def _populate(self, app: Any, frm: Any, auto: str, form: ResolvedForm) -> None:
209
+ spec = form.spec
210
+ if form.has_header:
211
+ call(get(app, "DoCmd"), "RunCommand", AC_CMD_FORM_HDR_FTR)
212
+ if spec.record_source is not None:
213
+ put(frm, "RecordSource", spec.record_source)
214
+ if spec.caption is not None:
215
+ put(frm, "Caption", spec.caption)
216
+ for prop, value in (
217
+ ("DefaultView", _DEFAULT_VIEWS[spec.default_view]),
218
+ ("AllowAdditions", spec.allow_additions),
219
+ ("AllowEdits", spec.allow_edits),
220
+ ("AllowDeletions", spec.allow_deletions),
221
+ ("DataEntry", spec.data_entry),
222
+ ("NavigationButtons", spec.navigation_buttons),
223
+ ("RecordSelectors", spec.record_selectors),
224
+ ("DividingLines", spec.dividing_lines),
225
+ ("ScrollBars", _SCROLL_BARS[spec.scroll_bars]),
226
+ ("AutoCenter", spec.auto_center),
227
+ ("PopUp", spec.pop_up),
228
+ ("Modal", spec.modal),
229
+ ("Width", form.width.twips),
230
+ ):
231
+ put(frm, prop, value)
232
+ put(get(frm, "Section", _SECTIONS[Section.DETAIL]), "Height", form.detail_height.twips)
233
+ if form.has_header:
234
+ put(
235
+ get(frm, "Section", _SECTIONS[Section.HEADER]),
236
+ "Height",
237
+ (form.header_height or Length(0)).twips,
238
+ )
239
+ put(
240
+ get(frm, "Section", _SECTIONS[Section.FOOTER]),
241
+ "Height",
242
+ (form.footer_height or Length(0)).twips,
243
+ )
244
+
245
+ created: dict[str, Any] = {}
246
+ for resolved in form.controls:
247
+ control = resolved.spec
248
+ kind = control.control_kind
249
+ if kind not in _CONTROL_TYPES:
250
+ raise SpecError(f"control kind {kind.value!r} is not supported by the form builder")
251
+ rect = resolved.rect
252
+ column = control.bound_field or ""
253
+ ctl = call(
254
+ app,
255
+ "CreateControl",
256
+ auto,
257
+ int(_CONTROL_TYPES[kind]),
258
+ _SECTIONS[resolved.section],
259
+ "",
260
+ column,
261
+ rect.left.twips,
262
+ rect.top.twips,
263
+ rect.width.twips,
264
+ rect.height.twips,
265
+ )
266
+ put(ctl, "Name", resolved.name)
267
+ self._configure_control(ctl, control)
268
+ created[resolved.name] = ctl
269
+ label = resolved.label
270
+ if label is not None:
271
+ # Tabular layouts put labels in the header: those are free-standing, not attached.
272
+ parent = resolved.name if label.section is resolved.section else ""
273
+ lbl = call(
274
+ app,
275
+ "CreateControl",
276
+ auto,
277
+ int(c.AcControlType.acLabel),
278
+ _SECTIONS[label.section],
279
+ parent,
280
+ "",
281
+ label.rect.left.twips,
282
+ label.rect.top.twips,
283
+ label.rect.width.twips,
284
+ label.rect.height.twips,
285
+ )
286
+ put(lbl, "Name", label.name)
287
+ put(lbl, "Caption", label.caption)
288
+
289
+ for binding in form.events:
290
+ target = frm if binding.object_name == "Form" else created[binding.object_name]
291
+ put(target, binding.property_name, "[Event Procedure]")
292
+ if form.module_text is not None:
293
+ put(frm, "HasModule", True)
294
+ module = get(frm, "Module")
295
+ lines = int(get(module, "CountOfLines"))
296
+ if lines:
297
+ call(module, "DeleteLines", 1, lines)
298
+ call(module, "AddFromString", form.module_text)
299
+ for prop, value in spec.properties.items():
300
+ put(get(get(frm, "Properties"), "Item", prop), "Value", value)
301
+
302
+ @staticmethod
303
+ def _configure_control(ctl: Any, control: Any) -> None:
304
+ put(ctl, "Visible", control.visible)
305
+ if isinstance(control, (LabelSpec, ButtonSpec)):
306
+ put(ctl, "Caption", control.caption)
307
+ elif isinstance(control, (TextBoxSpec, CheckBoxSpec, ComboBoxSpec)):
308
+ put(ctl, "Enabled", control.enabled)
309
+ put(ctl, "Locked", control.locked)
310
+ if isinstance(control, TextBoxSpec):
311
+ if control.control_source is not None:
312
+ put(ctl, "ControlSource", control.control_source)
313
+ if control.format is not None:
314
+ put(ctl, "Format", control.format)
315
+ if isinstance(control, ComboBoxSpec):
316
+ put(ctl, "RowSourceType", _ROW_SOURCE_TYPES[control.row_source_type])
317
+ put(ctl, "RowSource", control.row_source)
318
+ put(ctl, "BoundColumn", control.bound_column)
319
+ put(ctl, "ColumnCount", control.column_count)
320
+ put(ctl, "LimitToList", control.limit_to_list)
321
+ if control.column_widths is not None:
322
+ put(
323
+ ctl,
324
+ "ColumnWidths",
325
+ ";".join(str(width.twips) for width in control.column_widths),
326
+ )
327
+ for prop, value in control.properties.items():
328
+ put(get(get(ctl, "Properties"), "Item", prop), "Value", value)
329
+
330
+ def _install(self, auto: str, name: str, *, replace: bool) -> None:
331
+ form_type = int(c.AcObjectType.acForm)
332
+ with self._com.op(f"install form {name!r}", kind=ObjectKind.FORM, name=name):
333
+ docmd = self._docmd()
334
+ backup: str | None = None
335
+ try:
336
+ if replace:
337
+ backup = f"~pak_bak_{uuid.uuid4().hex[:12]}"
338
+ call(docmd, "Rename", backup, form_type, name)
339
+ call(docmd, "Rename", name, form_type, auto)
340
+ except BaseException:
341
+ with contextlib.suppress(pywintypes.com_error):
342
+ call(docmd, "DeleteObject", form_type, auto)
343
+ if backup is not None:
344
+ with contextlib.suppress(pywintypes.com_error):
345
+ call(docmd, "Rename", name, form_type, backup)
346
+ raise
347
+ if backup is not None:
348
+ call(docmd, "DeleteObject", form_type, backup)
349
+
350
+ def _open_form(self, name: str, view: int) -> Any:
351
+ call(
352
+ self._docmd(),
353
+ "OpenForm",
354
+ name,
355
+ view,
356
+ "",
357
+ "",
358
+ int(c.AcFormOpenDataMode.acFormPropertySettings),
359
+ int(c.AcWindowMode.acHidden),
360
+ )
361
+ return get(get(self._app(), "Forms"), "Item", name)
362
+
363
+ def _close_form(self, name: str) -> None:
364
+ with contextlib.suppress(pywintypes.com_error):
365
+ call(
366
+ self._docmd(),
367
+ "Close",
368
+ int(c.AcObjectType.acForm),
369
+ name,
370
+ int(c.AcCloseSave.acSaveNo),
371
+ )
372
+
373
+ def form_controls(self, name: str) -> list[ControlInfo]:
374
+ with self._com.op(f"read controls of form {name!r}", kind=ObjectKind.FORM, name=name):
375
+ frm = self._open_form(name, int(c.AcFormView.acDesign))
376
+ try:
377
+ controls = get(frm, "Controls")
378
+ infos: list[ControlInfo] = []
379
+ for index in range(int(get(controls, "Count"))):
380
+ ctl = get(controls, "Item", index)
381
+ parent_obj = _optional(ctl, "Parent")
382
+ parent = str(get(parent_obj, "Name")) if parent_obj is not None else None
383
+ if parent is not None and parent.casefold() == name.casefold():
384
+ parent = None
385
+ control_source = _optional(ctl, "ControlSource")
386
+ caption = _optional(ctl, "Caption")
387
+ infos.append(
388
+ ControlInfo(
389
+ name=str(get(ctl, "Name")),
390
+ kind=_CONTROL_KINDS.get(
391
+ int(get(ctl, "ControlType")), ControlKind.OTHER
392
+ ),
393
+ section=_SECTION_OF.get(int(get(ctl, "Section"))),
394
+ left=Length(int(get(ctl, "Left"))),
395
+ top=Length(int(get(ctl, "Top"))),
396
+ width=Length(int(get(ctl, "Width"))),
397
+ height=Length(int(get(ctl, "Height"))),
398
+ control_source=str(control_source) if control_source else None,
399
+ caption=str(caption) if caption is not None else None,
400
+ parent=parent,
401
+ )
402
+ )
403
+ return infos
404
+ finally:
405
+ del frm
406
+ self._close_form(name)
407
+
408
+ def check_form_opens(self, name: str) -> None:
409
+ with self._com.op(f"open form {name!r}", kind=ObjectKind.FORM, name=name):
410
+ frm = self._open_form(name, int(c.AcFormView.acNormal))
411
+ try:
412
+ get(frm, "CurrentRecord")
413
+ finally:
414
+ del frm
415
+ self._close_form(name)
@@ -0,0 +1 @@
1
+ """DAO adapter (COM). Imported lazily by the engines."""
@@ -0,0 +1,40 @@
1
+ # pyright: basic
2
+ """The "native defaults" profile: database properties Access writes to new databases but DAO does not.
3
+
4
+ Recorded in spike S3 (ADR 0001) by comparing ``NewCurrentDatabase`` with DAO ``CreateDatabase``. Applying
5
+ them to DAO-created files makes them behave exactly like Access-created ones (e.g. tabbed documents
6
+ instead of legacy overlapping windows). ``AccessVersion`` is omitted: Access maintains it itself.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import Any
12
+
13
+ from pyaccesskit._backends.dao.schema import _prop, _set_prop
14
+ from pyaccesskit._backends.dao.typemap import DB_BOOLEAN, DB_BYTE, DB_LONG
15
+
16
+ __all__ = ["NATIVE_DEFAULTS", "apply_native_defaults"]
17
+
18
+ NATIVE_DEFAULTS: tuple[tuple[str, int, Any], ...] = (
19
+ ("ANSI Query Mode", DB_LONG, 0),
20
+ ("CheckTruncatedNumFields", DB_LONG, 1),
21
+ ("Clear Cache on Close", DB_LONG, 0),
22
+ ("Default Zoom Level", DB_LONG, 100),
23
+ ("NavPane Category", DB_LONG, 0),
24
+ ("Never Cache", DB_LONG, 0),
25
+ ("Option to enable Monaco SQL Editor", DB_LONG, 1),
26
+ ("Picture Property Storage Format", DB_LONG, 0),
27
+ ("Show Navigation Pane Search Bar", DB_LONG, 1),
28
+ ("ShowDocumentTabs", DB_BOOLEAN, True),
29
+ ("Themed Form Controls", DB_LONG, 1),
30
+ ("Use Microsoft Access 2007 compatible cache", DB_LONG, 0),
31
+ ("UseMDIMode", DB_BYTE, 0),
32
+ ("WebDesignMode", DB_BYTE, 0),
33
+ )
34
+
35
+
36
+ def apply_native_defaults(db: Any) -> None:
37
+ """Create any missing native-default property on a DAO ``Database`` (existing values are kept)."""
38
+ for name, dao_type, value in NATIVE_DEFAULTS:
39
+ if _prop(db, name) is None:
40
+ _set_prop(db, name, dao_type, value)