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,394 @@
1
+ """Access and DAO constants used by PyAccessKit's COM adapters (internal).
2
+
3
+ Generated by ``scripts/gen_constants.py`` from the installed type libraries — do not edit by hand.
4
+
5
+ * Microsoft Access 16.0 Object Library — ``{4AFFC9A0-5F99-101B-AF4E-00AA003F0F07}`` v9.0
6
+ * Microsoft Office 16.0 Access database engine Object Library — ``{4AC9E1DA-5BAD-4AC7-86E3-24F4CDCECA28}`` v12.0
7
+ """
8
+
9
+ # ruff: noqa: N815
10
+ from __future__ import annotations
11
+
12
+ from enum import IntEnum
13
+
14
+
15
+ class AcCloseSave(IntEnum):
16
+ """``AcCloseSave`` (Microsoft Access 16.0 Object Library)."""
17
+
18
+ acSavePrompt = 0
19
+ acSaveYes = 1
20
+ acSaveNo = 2
21
+
22
+
23
+ class AcControlType(IntEnum):
24
+ """``AcControlType`` (Microsoft Access 16.0 Object Library)."""
25
+
26
+ acLabel = 100
27
+ acRectangle = 101
28
+ acLine = 102
29
+ acImage = 103
30
+ acCommandButton = 104
31
+ acOptionButton = 105
32
+ acCheckBox = 106
33
+ acOptionGroup = 107
34
+ acBoundObjectFrame = 108
35
+ acTextBox = 109
36
+ acListBox = 110
37
+ acComboBox = 111
38
+ acSubform = 112
39
+ acObjectFrame = 114
40
+ acPageBreak = 118
41
+ acCustomControl = 119
42
+ acToggleButton = 122
43
+ acTabCtl = 123
44
+ acPage = 124
45
+ acAttachment = 126
46
+ acEmptyCell = 127
47
+ acWebBrowser = 128
48
+ acNavigationControl = 129
49
+ acNavigationButton = 130
50
+ acChart = 133
51
+ acEdgeBrowser = 134
52
+
53
+
54
+ class AcDefView(IntEnum):
55
+ """``AcDefView`` (Microsoft Access 16.0 Object Library)."""
56
+
57
+ acDefViewSingle = 0
58
+ acDefViewContinuous = 1
59
+ acDefViewDatasheet = 2
60
+ acDefViewPivotTable = 3
61
+ acDefViewPivotChart = 4
62
+ acDefViewSplitForm = 5
63
+
64
+
65
+ class AcFormOpenDataMode(IntEnum):
66
+ """``AcFormOpenDataMode`` (Microsoft Access 16.0 Object Library)."""
67
+
68
+ acFormPropertySettings = -1
69
+ acFormAdd = 0
70
+ acFormEdit = 1
71
+ acFormReadOnly = 2
72
+
73
+
74
+ class AcFormView(IntEnum):
75
+ """``AcFormView`` (Microsoft Access 16.0 Object Library)."""
76
+
77
+ acNormal = 0
78
+ acDesign = 1
79
+ acPreview = 2
80
+ acFormDS = 3
81
+ acFormPivotTable = 4
82
+ acFormPivotChart = 5
83
+ acLayout = 6
84
+
85
+
86
+ class AcModuleType(IntEnum):
87
+ """``AcModuleType`` (Microsoft Access 16.0 Object Library)."""
88
+
89
+ acStandardModule = 0
90
+ acClassModule = 1
91
+
92
+
93
+ class AcNewDatabaseFormat(IntEnum):
94
+ """``AcNewDatabaseFormat`` (Microsoft Access 16.0 Object Library)."""
95
+
96
+ acNewDatabaseFormatUserDefault = 0
97
+ acNewDatabaseFormatAccess2000 = 9
98
+ acNewDatabaseFormatAccess2002 = 10
99
+ acNewDatabaseFormatAccess2007 = 12
100
+
101
+
102
+ class AcObjectType(IntEnum):
103
+ """``AcObjectType`` (Microsoft Access 16.0 Object Library)."""
104
+
105
+ acDefault = -1
106
+ acTable = 0
107
+ acQuery = 1
108
+ acForm = 2
109
+ acReport = 3
110
+ acMacro = 4
111
+ acModule = 5
112
+ acDataAccessPage = 6
113
+ acServerView = 7
114
+ acDiagram = 8
115
+ acStoredProcedure = 9
116
+ acFunction = 10
117
+ acDatabaseProperties = 11
118
+ acTableDataMacro = 12
119
+
120
+
121
+ class AcQuitOption(IntEnum):
122
+ """``AcQuitOption`` (Microsoft Access 16.0 Object Library)."""
123
+
124
+ acQuitPrompt = 0
125
+ acQuitSaveAll = 1
126
+ acQuitSaveNone = 2
127
+
128
+
129
+ class AcSection(IntEnum):
130
+ """``AcSection`` (Microsoft Access 16.0 Object Library)."""
131
+
132
+ acDetail = 0
133
+ acHeader = 1
134
+ acFooter = 2
135
+ acPageHeader = 3
136
+ acPageFooter = 4
137
+ acGroupLevel1Header = 5
138
+ acGroupLevel1Footer = 6
139
+ acGroupLevel2Header = 7
140
+ acGroupLevel2Footer = 8
141
+
142
+
143
+ class AcSysCmdAction(IntEnum):
144
+ """``AcSysCmdAction`` (Microsoft Access 16.0 Object Library)."""
145
+
146
+ acSysCmdInitMeter = 1
147
+ acSysCmdUpdateMeter = 2
148
+ acSysCmdRemoveMeter = 3
149
+ acSysCmdSetStatus = 4
150
+ acSysCmdClearStatus = 5
151
+ acSysCmdRuntime = 6
152
+ acSysCmdAccessVer = 7
153
+ acSysCmdIniFile = 8
154
+ acSysCmdAccessDir = 9
155
+ acSysCmdGetObjectState = 10
156
+ acSysCmdClearHelpTopic = 11
157
+ acSysCmdProfile = 12
158
+ acSysCmdGetWorkgroupFile = 13
159
+ acSysCmdCompile = 603
160
+ acSysCmdGetMsoBuildNumber = 715
161
+ acSysCmdGetFullVersion = 720
162
+ acSysCmdGetVersion = 721
163
+ acSysCmdGetFullBuildNumber = 722
164
+ acSysCmdGetChannelName = 723
165
+ acSysCmdGetBitness = 724
166
+ acSysCmdGetBuildNumber = 725
167
+
168
+
169
+ class AcView(IntEnum):
170
+ """``AcView`` (Microsoft Access 16.0 Object Library)."""
171
+
172
+ acViewNormal = 0
173
+ acViewDesign = 1
174
+ acViewPreview = 2
175
+ acViewPivotTable = 3
176
+ acViewPivotChart = 4
177
+ acViewReport = 5
178
+ acViewLayout = 6
179
+ acViewSql = 7
180
+
181
+
182
+ class AcWindowMode(IntEnum):
183
+ """``AcWindowMode`` (Microsoft Access 16.0 Object Library)."""
184
+
185
+ acWindowNormal = 0
186
+ acHidden = 1
187
+ acIcon = 2
188
+ acDialog = 3
189
+
190
+
191
+ class DataTypeEnum(IntEnum):
192
+ """``DataTypeEnum`` (Microsoft Office 16.0 Access database engine Object Library)."""
193
+
194
+ dbBoolean = 1
195
+ dbByte = 2
196
+ dbInteger = 3
197
+ dbLong = 4
198
+ dbCurrency = 5
199
+ dbSingle = 6
200
+ dbDouble = 7
201
+ dbDate = 8
202
+ dbBinary = 9
203
+ dbText = 10
204
+ dbLongBinary = 11
205
+ dbMemo = 12
206
+ dbGUID = 15
207
+ dbBigInt = 16
208
+ dbVarBinary = 17
209
+ dbChar = 18
210
+ dbNumeric = 19
211
+ dbDecimal = 20
212
+ dbFloat = 21
213
+ dbTime = 22
214
+ dbTimeStamp = 23
215
+ dbDateTimeExtended = 26
216
+ dbAttachment = 101
217
+ dbComplexByte = 102
218
+ dbComplexInteger = 103
219
+ dbComplexLong = 104
220
+ dbComplexSingle = 105
221
+ dbComplexDouble = 106
222
+ dbComplexGUID = 107
223
+ dbComplexDecimal = 108
224
+ dbComplexText = 109
225
+
226
+
227
+ class DatabaseTypeEnum(IntEnum):
228
+ """``DatabaseTypeEnum`` (Microsoft Office 16.0 Access database engine Object Library)."""
229
+
230
+ dbVersion10 = 1
231
+ dbEncrypt = 2
232
+ dbDecrypt = 4
233
+ dbVersion11 = 8
234
+ dbVersion20 = 16
235
+ dbVersion30 = 32
236
+ dbVersion40 = 64
237
+ dbVersion120 = 128
238
+ dbVersion140 = 256
239
+ dbVersion150 = 512
240
+ dbVersion167 = 1024
241
+
242
+
243
+ class FieldAttributeEnum(IntEnum):
244
+ """``FieldAttributeEnum`` (Microsoft Office 16.0 Access database engine Object Library)."""
245
+
246
+ dbFixedField = 1
247
+ dbVariableField = 2
248
+ dbAutoIncrField = 16
249
+ dbUpdatableField = 32
250
+ dbSystemField = 8192
251
+ dbHyperlinkField = 32768
252
+ dbDescending = 1
253
+
254
+
255
+ class QueryDefTypeEnum(IntEnum):
256
+ """``QueryDefTypeEnum`` (Microsoft Office 16.0 Access database engine Object Library)."""
257
+
258
+ dbQSelect = 0
259
+ dbQProcedure = 224
260
+ dbQAction = 240
261
+ dbQCrosstab = 16
262
+ dbQDelete = 32
263
+ dbQUpdate = 48
264
+ dbQAppend = 64
265
+ dbQMakeTable = 80
266
+ dbQDDL = 96
267
+ dbQSQLPassThrough = 112
268
+ dbQSetOperation = 128
269
+ dbQSPTBulk = 144
270
+ dbQCompound = 160
271
+
272
+
273
+ class RecordsetOptionEnum(IntEnum):
274
+ """``RecordsetOptionEnum`` (Microsoft Office 16.0 Access database engine Object Library)."""
275
+
276
+ dbDenyWrite = 1
277
+ dbDenyRead = 2
278
+ dbReadOnly = 4
279
+ dbAppendOnly = 8
280
+ dbInconsistent = 16
281
+ dbConsistent = 32
282
+ dbSQLPassThrough = 64
283
+ dbFailOnError = 128
284
+ dbForwardOnly = 256
285
+ dbSeeChanges = 512
286
+ dbRunAsync = 1024
287
+ dbExecDirect = 2048
288
+
289
+
290
+ class RecordsetTypeEnum(IntEnum):
291
+ """``RecordsetTypeEnum`` (Microsoft Office 16.0 Access database engine Object Library)."""
292
+
293
+ dbOpenTable = 1
294
+ dbOpenDynaset = 2
295
+ dbOpenSnapshot = 4
296
+ dbOpenForwardOnly = 8
297
+ dbOpenDynamic = 16
298
+
299
+
300
+ class RelationAttributeEnum(IntEnum):
301
+ """``RelationAttributeEnum`` (Microsoft Office 16.0 Access database engine Object Library)."""
302
+
303
+ dbRelationUnique = 1
304
+ dbRelationDontEnforce = 2
305
+ dbRelationInherited = 4
306
+ dbRelationUpdateCascade = 256
307
+ dbRelationDeleteCascade = 4096
308
+ dbRelationLeft = 16777216
309
+ dbRelationRight = 33554432
310
+
311
+
312
+ class TableDefAttributeEnum(IntEnum):
313
+ """``TableDefAttributeEnum`` (Microsoft Office 16.0 Access database engine Object Library)."""
314
+
315
+ dbAttachExclusive = 65536
316
+ dbAttachSavePWD = 131072
317
+ dbSystemObject = -2147483646
318
+ dbAttachedTable = 1073741824
319
+ dbAttachedODBC = 536870912
320
+ dbHiddenObject = 1
321
+
322
+
323
+ # LanguageConstants (Microsoft Office 16.0 Access database engine Object Library)
324
+ dbLangArabic = ';LANGID=0x0401;CP=1256;COUNTRY=0'
325
+ dbLangCzech = ';LANGID=0x0405;CP=1250;COUNTRY=0'
326
+ dbLangDutch = ';LANGID=0x0413;CP=1252;COUNTRY=0'
327
+ dbLangGeneral = ';LANGID=0x0409;CP=1252;COUNTRY=0'
328
+ dbLangGreek = ';LANGID=0x0408;CP=1253;COUNTRY=0'
329
+ dbLangHebrew = ';LANGID=0x040D;CP=1255;COUNTRY=0'
330
+ dbLangHungarian = ';LANGID=0x040E;CP=1250;COUNTRY=0'
331
+ dbLangIcelandic = ';LANGID=0x040F;CP=1252;COUNTRY=0'
332
+ dbLangNordic = ';LANGID=0x041D;CP=1252;COUNTRY=0'
333
+ dbLangNorwDan = ';LANGID=0x0406;CP=1252;COUNTRY=0'
334
+ dbLangPolish = ';LANGID=0x0415;CP=1250;COUNTRY=0'
335
+ dbLangCyrillic = ';LANGID=0x0419;CP=1251;COUNTRY=0'
336
+ dbLangSpanish = ';LANGID=0x040A;CP=1252;COUNTRY=0'
337
+ dbLangSwedFin = ';LANGID=0x041D;CP=1252;COUNTRY=0'
338
+ dbLangTurkish = ';LANGID=0x041F;CP=1254;COUNTRY=0'
339
+ dbLangJapanese = ';LANGID=0x0411;CP=932;COUNTRY=0'
340
+ dbLangChineseSimplified = ';LANGID=0x0804;CP=936;COUNTRY=0'
341
+ dbLangChineseTraditional = ';LANGID=0x0404;CP=950;COUNTRY=0'
342
+ dbLangKorean = ';LANGID=0x0412;CP=949;COUNTRY=0'
343
+ dbLangThai = ';LANGID=0x041E;CP=874;COUNTRY=0'
344
+ dbLangSlovenian = ';LANGID=0x0424;CP=1250;COUNTRY=0'
345
+ dbLangJapaneseRadicalStrokeCount = ';LANGID=0x00040411;CP=65001;COUNTRY=0'
346
+ dbLangHindi = ';LANGID=0x00000439;CP=65001;COUNTRY=0'
347
+
348
+
349
+ class MsoAutomationSecurity(IntEnum):
350
+ """``Application.AutomationSecurity`` values (Office core type library)."""
351
+
352
+ msoAutomationSecurityLow = 1
353
+ msoAutomationSecurityByUI = 2
354
+ msoAutomationSecurityForceDisable = 3
355
+
356
+
357
+ class AdoSchema(IntEnum):
358
+ """ADO ``SchemaEnum`` values used for column introspection."""
359
+
360
+ adSchemaColumns = 4
361
+ adSchemaIndexes = 12
362
+
363
+
364
+ class FormScrollBars(IntEnum):
365
+ """Values of the Access ``Form.ScrollBars`` property."""
366
+
367
+ neither = 0
368
+ horizontal = 1
369
+ vertical = 2
370
+ both = 3
371
+
372
+
373
+ class TextFormat(IntEnum):
374
+ """Values of the Access ``TextFormat`` field property (Long Text)."""
375
+
376
+ plain_text = 0
377
+ rich_text = 1
378
+
379
+
380
+ # Well-known Win32/COM HRESULTs (as signed 32-bit integers, the way pywin32 reports them).
381
+ def _hresult(value: int) -> int:
382
+ return value - 0x1_0000_0000 if value & 0x8000_0000 else value
383
+
384
+
385
+ DISP_E_EXCEPTION = _hresult(0x80020009)
386
+ REGDB_E_CLASSNOTREG = _hresult(0x80040154)
387
+ CO_E_SERVER_EXEC_FAILURE = _hresult(0x80080005)
388
+ RPC_E_CALL_REJECTED = _hresult(0x80010001)
389
+ RPC_E_SERVERCALL_RETRYLATER = _hresult(0x8001010A)
390
+ RPC_E_DISCONNECTED = _hresult(0x80010108)
391
+ RPC_E_SERVERFAULT = _hresult(0x80010105)
392
+ RPC_S_SERVER_UNAVAILABLE = _hresult(0x800706BA)
393
+ RPC_S_CALL_FAILED = _hresult(0x800706BE)
394
+ CO_E_OBJNOTCONNECTED = _hresult(0x800401FD)
@@ -0,0 +1,50 @@
1
+ # pyright: basic, reportArgumentType=false, reportAttributeAccessIssue=false, reportIndexIssue=false
2
+ # (the types-pywin32 stubs are stricter than the COM runtime, e.g. unkOuter=None is valid)
3
+ """Creation of COM objects — always late-bound, never attached to a running instance.
4
+
5
+ * ``win32com.client.Dispatch(progid)`` first calls ``GetActiveObject`` and would *attach to a user's running
6
+ Access* (ADR 0001, F1). PyAccessKit always uses ``CoCreateInstanceEx`` instead.
7
+ * ``Dispatch``/``DispatchEx`` return early- or late-bound wrappers depending on the machine's makepy cache.
8
+ ``win32com.client.dynamic.Dispatch`` keeps the whole object graph late-bound, so behaviour is identical
9
+ on every machine.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from typing import Any
15
+
16
+ import pythoncom
17
+ import pywintypes
18
+ from win32com.client import dynamic
19
+
20
+ from pyaccesskit._com.runtime import ensure_com_initialized
21
+
22
+ __all__ = ["create_inproc", "create_local_server", "wrap"]
23
+
24
+
25
+ def wrap(obj: Any) -> Any:
26
+ """Wrap a raw ``PyIDispatch``/``PyIUnknown`` in a late-bound dispatch object."""
27
+ if isinstance(obj, pythoncom.TypeIIDs[pythoncom.IID_IDispatch]):
28
+ return dynamic.Dispatch(obj)
29
+ if isinstance(obj, pythoncom.TypeIIDs[pythoncom.IID_IUnknown]):
30
+ return dynamic.Dispatch(obj.QueryInterface(pythoncom.IID_IDispatch))
31
+ return obj
32
+
33
+
34
+ def create_local_server(progid: str) -> Any:
35
+ """Start a **new** instance of an out-of-process COM server (e.g. ``Access.Application``)."""
36
+ ensure_com_initialized()
37
+ clsid = pywintypes.IID(progid)
38
+ unknown = pythoncom.CoCreateInstanceEx(
39
+ clsid, None, pythoncom.CLSCTX_LOCAL_SERVER, None, (pythoncom.IID_IDispatch,)
40
+ )[0]
41
+ return dynamic.Dispatch(unknown)
42
+
43
+
44
+ def create_inproc(progid: str) -> Any:
45
+ """Load an in-process COM server (e.g. ``DAO.DBEngine.120``) into this Python process."""
46
+ ensure_com_initialized()
47
+ unknown = pythoncom.CoCreateInstance(
48
+ progid, None, pythoncom.CLSCTX_INPROC_SERVER, pythoncom.IID_IDispatch
49
+ )
50
+ return dynamic.Dispatch(unknown)
@@ -0,0 +1,184 @@
1
+ # pyright: basic
2
+ """Translate ``pywintypes.com_error`` into PyAccessKit exceptions.
3
+
4
+ Access and DAO report errors as ``DISP_E_EXCEPTION`` whose EXCEPINFO carries ``scode = 0x800A0000 | number``
5
+ (FACILITY_CONTROL) — verified for every number below in spike S9. DAO errors have a ``DAO.<Collection>``
6
+ source; Access application errors have no source but a filled description.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import traceback
12
+ from collections.abc import Sequence
13
+ from dataclasses import dataclass
14
+ from pathlib import Path
15
+ from typing import Any
16
+
17
+ from pyaccesskit._com import constants as c
18
+ from pyaccesskit.enums import ObjectKind
19
+ from pyaccesskit.errors import (
20
+ AccessProcessDiedError,
21
+ ComError,
22
+ DaoError,
23
+ DatabaseExistsError,
24
+ DatabaseLockedError,
25
+ DatabaseNotFoundError,
26
+ EngineUnavailableError,
27
+ ErrorDetails,
28
+ IntegrityViolationError,
29
+ InvalidPasswordError,
30
+ MissingParameterError,
31
+ ObjectExistsError,
32
+ ObjectInUseError,
33
+ ObjectNotFoundError,
34
+ PyAccessKitError,
35
+ ReadOnlyError,
36
+ RelationshipError,
37
+ SchemaError,
38
+ SqlSyntaxError,
39
+ UnrecognizedFormatError,
40
+ )
41
+
42
+ __all__ = ["OpContext", "details_from", "translate"]
43
+
44
+ FACILITY_CONTROL = 0x0A
45
+
46
+ NOT_FOUND = frozenset({3011, 3078, 3265, 2102, 2103, 2450, 2451, 2465, 7874})
47
+ EXISTS = frozenset({3010, 3012, 3191, 3284, 3367, 3380})
48
+ DB_EXISTS = frozenset({3204, 7865})
49
+ DB_NOT_FOUND = frozenset({3024, 3044})
50
+ LOCKED = frozenset({3006, 3045, 3050, 3356, 3734, 7866})
51
+ IN_USE = frozenset({3008, 3009, 3211, 3262})
52
+ PASSWORD = frozenset({3031})
53
+ FORMAT = frozenset({3049, 3343})
54
+ SQL_SYNTAX = frozenset(
55
+ {3075, 3122, 3129, 3131, 3134, 3141, 3144, 3145, 3292, 3293, 3306, 3319, 3346}
56
+ )
57
+ MISSING_PARAMETER = frozenset({3061})
58
+ RELATIONSHIP = frozenset({3366, 3368, 3609})
59
+ INTEGRITY = frozenset({3022, 3200, 3201})
60
+ SCHEMA = frozenset({3125, 3259, 3280, 3283, 3303, 3375})
61
+ READ_ONLY = frozenset({3027})
62
+ PROCESS_DIED = frozenset(
63
+ {
64
+ c.RPC_E_DISCONNECTED,
65
+ c.RPC_S_SERVER_UNAVAILABLE,
66
+ c.RPC_S_CALL_FAILED,
67
+ c.RPC_E_SERVERFAULT,
68
+ c.CO_E_OBJNOTCONNECTED,
69
+ }
70
+ )
71
+ UNAVAILABLE = frozenset(
72
+ {c.REGDB_E_CLASSNOTREG, c.CO_E_SERVER_EXEC_FAILURE, -2147221005}
73
+ ) # + CO_E_CLASSSTRING
74
+
75
+
76
+ @dataclass(frozen=True)
77
+ class OpContext:
78
+ """What PyAccessKit was doing when a COM call failed (used to build precise messages)."""
79
+
80
+ action: str
81
+ kind: ObjectKind | None = None
82
+ name: str | None = None
83
+ path: Path | None = None
84
+ sql: str | None = None
85
+
86
+
87
+ def _signed(value: int | None) -> int | None:
88
+ if value is None:
89
+ return None
90
+ value &= 0xFFFFFFFF
91
+ return value - 0x1_0000_0000 if value & 0x8000_0000 else value
92
+
93
+
94
+ def details_from(exc: Any, dao_errors: Sequence[DaoError] = ()) -> ErrorDetails:
95
+ """Extract :class:`ErrorDetails` from a ``pywintypes.com_error`` (or any exception with its ``args``)."""
96
+ args = tuple(getattr(exc, "args", ()))
97
+ hresult = _signed(args[0]) if args and isinstance(args[0], int) else None
98
+ message = args[1] if len(args) > 1 and isinstance(args[1], str) else None
99
+ excepinfo = args[2] if len(args) > 2 and isinstance(args[2], tuple) else None
100
+ source = description = None
101
+ scode = None
102
+ if excepinfo and len(excepinfo) >= 6:
103
+ source = excepinfo[1] or None
104
+ description = excepinfo[2] or None
105
+ scode = _signed(excepinfo[5]) if isinstance(excepinfo[5], int) else None
106
+ number = None
107
+ if scode is not None and ((scode >> 16) & 0x1FFF) == FACILITY_CONTROL:
108
+ number = scode & 0xFFFF
109
+ if description is None and scode is None:
110
+ description = message
111
+ matching: tuple[DaoError, ...] = ()
112
+ if dao_errors and number is not None and dao_errors[-1].number == number:
113
+ matching = tuple(
114
+ dao_errors
115
+ ) # DBEngine.Errors keeps stale entries; only trust a matching one
116
+ return ErrorDetails(
117
+ hresult=hresult,
118
+ scode=scode,
119
+ number=number,
120
+ source=source,
121
+ description=description.strip() if description else None,
122
+ dao_errors=matching,
123
+ )
124
+
125
+
126
+ def _message(ctx: OpContext, details: ErrorDetails) -> str:
127
+ summary = details.summary() or "unknown COM error"
128
+ return f"cannot {ctx.action}: {summary}"
129
+
130
+
131
+ def translate(
132
+ exc: BaseException, ctx: OpContext, dao_errors: Sequence[DaoError] = ()
133
+ ) -> PyAccessKitError:
134
+ """Map a COM error to the most specific :mod:`pyaccesskit.errors` exception.
135
+
136
+ The COM-holding locals of the original traceback are cleared (the traceback itself is kept for
137
+ debugging), so an exception kept alive by the caller cannot keep an Access process alive.
138
+ """
139
+ if exc.__traceback__ is not None:
140
+ traceback.clear_frames(exc.__traceback__)
141
+ details = details_from(exc, dao_errors)
142
+ number = details.number
143
+ message = _message(ctx, details)
144
+ common = {"operation": ctx.action, "details": details}
145
+
146
+ if number is not None:
147
+ if number in NOT_FOUND:
148
+ return ObjectNotFoundError(message, kind=ctx.kind, name=ctx.name, **common)
149
+ if number in EXISTS:
150
+ return ObjectExistsError(message, kind=ctx.kind, name=ctx.name, **common)
151
+ if number in DB_EXISTS:
152
+ return DatabaseExistsError(message, path=ctx.path, **common)
153
+ if number in DB_NOT_FOUND:
154
+ return DatabaseNotFoundError(message, path=ctx.path, **common)
155
+ if number in LOCKED:
156
+ return DatabaseLockedError(message, path=ctx.path, **common)
157
+ if number in IN_USE:
158
+ return ObjectInUseError(message, kind=ctx.kind, name=ctx.name, **common)
159
+ if number in PASSWORD:
160
+ return InvalidPasswordError(message, path=ctx.path, **common)
161
+ if number in FORMAT:
162
+ return UnrecognizedFormatError(message, path=ctx.path, **common)
163
+ if number in SQL_SYNTAX:
164
+ return SqlSyntaxError(message, sql=ctx.sql, **common)
165
+ if number in MISSING_PARAMETER:
166
+ return MissingParameterError(message, sql=ctx.sql, **common)
167
+ if number in RELATIONSHIP:
168
+ return RelationshipError(message, **common)
169
+ if number in INTEGRITY:
170
+ return IntegrityViolationError(message, **common)
171
+ if number in SCHEMA:
172
+ return SchemaError(message, **common)
173
+ if number in READ_ONLY:
174
+ return ReadOnlyError(message, **common)
175
+
176
+ for code in (details.scode, details.hresult):
177
+ if code in PROCESS_DIED:
178
+ return AccessProcessDiedError(
179
+ f"cannot {ctx.action}: the Microsoft Access process stopped responding or exited",
180
+ **common,
181
+ )
182
+ if code in UNAVAILABLE:
183
+ return EngineUnavailableError(message, **common)
184
+ return ComError(message, **common)