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.
- pyaccesskit/AGENT_GUIDE.md +455 -0
- pyaccesskit/__init__.py +167 -0
- pyaccesskit/__main__.py +6 -0
- pyaccesskit/_backends/__init__.py +0 -0
- pyaccesskit/_backends/access/__init__.py +1 -0
- pyaccesskit/_backends/access/design.py +415 -0
- pyaccesskit/_backends/dao/__init__.py +1 -0
- pyaccesskit/_backends/dao/profile.py +40 -0
- pyaccesskit/_backends/dao/schema.py +805 -0
- pyaccesskit/_backends/dao/typemap.py +390 -0
- pyaccesskit/_backends/fake/__init__.py +3 -0
- pyaccesskit/_backends/fake/backend.py +680 -0
- pyaccesskit/_backends/protocols.py +339 -0
- pyaccesskit/_com/__init__.py +1 -0
- pyaccesskit/_com/constants.py +394 -0
- pyaccesskit/_com/dispatch.py +50 -0
- pyaccesskit/_com/errors.py +184 -0
- pyaccesskit/_com/gateway.py +199 -0
- pyaccesskit/_com/raw.py +164 -0
- pyaccesskit/_com/runtime.py +39 -0
- pyaccesskit/_com/variants.py +72 -0
- pyaccesskit/_engines/__init__.py +48 -0
- pyaccesskit/_engines/access.py +300 -0
- pyaccesskit/_engines/inproc.py +148 -0
- pyaccesskit/_engines/probe.py +231 -0
- pyaccesskit/_ledger.py +158 -0
- pyaccesskit/_ops/__init__.py +0 -0
- pyaccesskit/_ops/design.py +127 -0
- pyaccesskit/_ops/schema.py +471 -0
- pyaccesskit/_session/__init__.py +1 -0
- pyaccesskit/_session/protocols.py +78 -0
- pyaccesskit/_session/session.py +354 -0
- pyaccesskit/_text/__init__.py +0 -0
- pyaccesskit/_text/codec.py +114 -0
- pyaccesskit/_version.py +3 -0
- pyaccesskit/_win/__init__.py +1 -0
- pyaccesskit/_win/access_process.py +348 -0
- pyaccesskit/_win/console.py +56 -0
- pyaccesskit/_win/inspector.py +53 -0
- pyaccesskit/_win/job.py +65 -0
- pyaccesskit/_win/processes.py +159 -0
- pyaccesskit/_win/watchdog.py +253 -0
- pyaccesskit/cli/__init__.py +10 -0
- pyaccesskit/cli/_output.py +101 -0
- pyaccesskit/cli/agent.py +99 -0
- pyaccesskit/cli/app.py +54 -0
- pyaccesskit/cli/cleanup.py +56 -0
- pyaccesskit/cli/doctor.py +101 -0
- pyaccesskit/cli/inspection.py +223 -0
- pyaccesskit/database.py +296 -0
- pyaccesskit/diagnostics.py +319 -0
- pyaccesskit/enums.py +258 -0
- pyaccesskit/errors.py +407 -0
- pyaccesskit/forms/__init__.py +45 -0
- pyaccesskit/forms/builder.py +295 -0
- pyaccesskit/forms/collection.py +117 -0
- pyaccesskit/forms/controls.py +157 -0
- pyaccesskit/forms/layout.py +300 -0
- pyaccesskit/forms/spec.py +169 -0
- pyaccesskit/forms/vba.py +138 -0
- pyaccesskit/maintenance.py +32 -0
- pyaccesskit/modules.py +101 -0
- pyaccesskit/objects.py +81 -0
- pyaccesskit/options.py +40 -0
- pyaccesskit/properties.py +74 -0
- pyaccesskit/py.typed +0 -0
- pyaccesskit/queries.py +190 -0
- pyaccesskit/relationships.py +143 -0
- pyaccesskit/schema/__init__.py +73 -0
- pyaccesskit/schema/_base.py +55 -0
- pyaccesskit/schema/_reserved_words.py +55 -0
- pyaccesskit/schema/columns.py +609 -0
- pyaccesskit/schema/compat.py +57 -0
- pyaccesskit/schema/expressions.py +162 -0
- pyaccesskit/schema/indexes.py +114 -0
- pyaccesskit/schema/names.py +122 -0
- pyaccesskit/schema/queries.py +192 -0
- pyaccesskit/schema/relationships.py +132 -0
- pyaccesskit/schema/tables.py +178 -0
- pyaccesskit/tables.py +333 -0
- pyaccesskit/units.py +301 -0
- pyaccesskit-0.1.0.dist-info/METADATA +201 -0
- pyaccesskit-0.1.0.dist-info/RECORD +86 -0
- pyaccesskit-0.1.0.dist-info/WHEEL +4 -0
- pyaccesskit-0.1.0.dist-info/entry_points.txt +2 -0
- pyaccesskit-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Immutable, validated specifications of database schema objects.
|
|
2
|
+
|
|
3
|
+
Specs are plain Python values (Pydantic models): they never touch Access. Collections such as
|
|
4
|
+
``db.tables`` turn them into real objects, and introspection turns real objects back into specs.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from pyaccesskit.schema._base import PropertyValue
|
|
8
|
+
from pyaccesskit.schema.columns import (
|
|
9
|
+
AutoNumberColumn,
|
|
10
|
+
Column,
|
|
11
|
+
ColumnBase,
|
|
12
|
+
ColumnSpec,
|
|
13
|
+
CurrencyColumn,
|
|
14
|
+
DateTimeColumn,
|
|
15
|
+
DecimalColumn,
|
|
16
|
+
HyperlinkColumn,
|
|
17
|
+
LongTextColumn,
|
|
18
|
+
NumberColumn,
|
|
19
|
+
OleObjectColumn,
|
|
20
|
+
TextColumn,
|
|
21
|
+
UnsupportedColumn,
|
|
22
|
+
YesNoColumn,
|
|
23
|
+
)
|
|
24
|
+
from pyaccesskit.schema.expressions import DefaultValue, Expr, parse_literal, render_literal
|
|
25
|
+
from pyaccesskit.schema.indexes import PRIMARY_KEY_NAME, IndexField, IndexSpec
|
|
26
|
+
from pyaccesskit.schema.names import check_name, lint_name, name_key, names_equal, quote_identifier
|
|
27
|
+
from pyaccesskit.schema.queries import (
|
|
28
|
+
PassThroughOptions,
|
|
29
|
+
QuerySpec,
|
|
30
|
+
detect_query_kind,
|
|
31
|
+
normalize_sql,
|
|
32
|
+
sql_equivalent,
|
|
33
|
+
)
|
|
34
|
+
from pyaccesskit.schema.relationships import ColumnRef, RelationshipSpec
|
|
35
|
+
from pyaccesskit.schema.tables import TableSpec
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"PRIMARY_KEY_NAME",
|
|
39
|
+
"AutoNumberColumn",
|
|
40
|
+
"Column",
|
|
41
|
+
"ColumnBase",
|
|
42
|
+
"ColumnRef",
|
|
43
|
+
"ColumnSpec",
|
|
44
|
+
"CurrencyColumn",
|
|
45
|
+
"DateTimeColumn",
|
|
46
|
+
"DecimalColumn",
|
|
47
|
+
"DefaultValue",
|
|
48
|
+
"Expr",
|
|
49
|
+
"HyperlinkColumn",
|
|
50
|
+
"IndexField",
|
|
51
|
+
"IndexSpec",
|
|
52
|
+
"LongTextColumn",
|
|
53
|
+
"NumberColumn",
|
|
54
|
+
"OleObjectColumn",
|
|
55
|
+
"PassThroughOptions",
|
|
56
|
+
"PropertyValue",
|
|
57
|
+
"QuerySpec",
|
|
58
|
+
"RelationshipSpec",
|
|
59
|
+
"TableSpec",
|
|
60
|
+
"TextColumn",
|
|
61
|
+
"UnsupportedColumn",
|
|
62
|
+
"YesNoColumn",
|
|
63
|
+
"check_name",
|
|
64
|
+
"detect_query_kind",
|
|
65
|
+
"lint_name",
|
|
66
|
+
"name_key",
|
|
67
|
+
"names_equal",
|
|
68
|
+
"normalize_sql",
|
|
69
|
+
"parse_literal",
|
|
70
|
+
"quote_identifier",
|
|
71
|
+
"render_literal",
|
|
72
|
+
"sql_equivalent",
|
|
73
|
+
]
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Shared base model and helpers for specification models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable, Sequence
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from decimal import Decimal
|
|
8
|
+
from typing import Annotated, Any, TypeVar
|
|
9
|
+
|
|
10
|
+
from pydantic import AfterValidator, BaseModel, ConfigDict, ValidationError
|
|
11
|
+
|
|
12
|
+
from pyaccesskit.errors import SpecError
|
|
13
|
+
|
|
14
|
+
PropertyValue = str | bool | int | float | Decimal | datetime | None
|
|
15
|
+
"""Values accepted in ``properties={...}`` escape-hatch dictionaries (raw Access/DAO properties)."""
|
|
16
|
+
|
|
17
|
+
_T = TypeVar("_T")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _as_tuple(value: Sequence[Any]) -> tuple[Any, ...]:
|
|
21
|
+
return tuple(value)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
Items = Annotated[Sequence[_T], AfterValidator(_as_tuple)]
|
|
25
|
+
"""A list-like spec field: any sequence is accepted (lists included), and it is stored as a tuple.
|
|
26
|
+
|
|
27
|
+
Type checkers derive a model's ``__init__`` from its annotations, so a ``tuple[...]`` field would reject the
|
|
28
|
+
lists people naturally write; this keeps the input type friendly and the stored value immutable.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class SpecModel(BaseModel):
|
|
33
|
+
"""Base class of every PyAccessKit spec: immutable, strict about unknown fields."""
|
|
34
|
+
|
|
35
|
+
model_config = ConfigDict(frozen=True, extra="forbid", validate_default=True)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def spec_error(exc: ValidationError, what: str) -> SpecError:
|
|
39
|
+
"""Convert a Pydantic ``ValidationError`` into a :class:`SpecError` with readable problems."""
|
|
40
|
+
problems: list[str] = []
|
|
41
|
+
for error in exc.errors(include_url=False):
|
|
42
|
+
location = ".".join(str(part) for part in error["loc"]) or what
|
|
43
|
+
message = error["msg"]
|
|
44
|
+
if message.startswith("Value error, "):
|
|
45
|
+
message = message.removeprefix("Value error, ")
|
|
46
|
+
problems.append(f"{location}: {message}")
|
|
47
|
+
return SpecError(f"invalid {what}", problems=tuple(problems))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def build(factory: Callable[..., _T], what: str, /, **kwargs: Any) -> _T:
|
|
51
|
+
"""Call ``factory(**kwargs)``, converting validation failures into :class:`SpecError`."""
|
|
52
|
+
try:
|
|
53
|
+
return factory(**kwargs)
|
|
54
|
+
except ValidationError as exc:
|
|
55
|
+
raise spec_error(exc, what) from exc
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Access and Access database engine reserved words.
|
|
2
|
+
|
|
3
|
+
Source: Microsoft Support, "Learn about Access reserved words and symbols" (retrieved 2026-09-26). Using
|
|
4
|
+
these as object or field names is legal but causes errors in expressions, SQL and VBA unless every
|
|
5
|
+
reference is bracketed, so PyAccessKit warns about them (``AccessNameWarning``).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
_ACCESS = """
|
|
11
|
+
ADD ALL Alphanumeric ALTER AND ANY Application AS ASC Assistant AUTOINCREMENT Avg BETWEEN BINARY BIT
|
|
12
|
+
BOOLEAN BY BYTE CHAR CHARACTER COLUMN CompactDatabase CONSTRAINT Container Count COUNTER CREATE
|
|
13
|
+
CreateDatabase CreateField CreateGroup CreateIndex CreateObject CreateProperty CreateRelation
|
|
14
|
+
CreateTableDef CreateUser CreateWorkspace CURRENCY CurrentUser DATABASE DATE DATETIME DELETE DESC
|
|
15
|
+
Description DISALLOW DISTINCT DISTINCTROW Document DOUBLE DROP Echo Else End Eqv Error EXISTS Exit FALSE
|
|
16
|
+
Field Fields FillCache FLOAT FLOAT4 FLOAT8 FOREIGN Form Forms FROM Full FUNCTION GENERAL GetObject
|
|
17
|
+
GetOption GotoPage GROUP GUID HAVING Idle IEEEDOUBLE IEEESINGLE If IGNORE Imp IN INDEX Indexes INNER
|
|
18
|
+
INSERT InsertText INT INTEGER INTEGER1 INTEGER2 INTEGER4 INTO IS JOIN KEY LastModified LEFT Level Like
|
|
19
|
+
LOGICAL LOGICAL1 LONG LONGBINARY LONGTEXT Macro Match Max Min Mod MEMO Module MONEY Move NAME
|
|
20
|
+
NewPassword NO Not Note NULL NUMBER NUMERIC Object OLEOBJECT OFF ON OpenRecordset OPTION OR ORDER
|
|
21
|
+
Orientation Outer OWNERACCESS Parameter PARAMETERS Partial PERCENT PIVOT PRIMARY PROCEDURE Property
|
|
22
|
+
Queries Query Quit REAL Recalc Recordset REFERENCES Refresh RefreshLink RegisterDatabase Relation Repaint
|
|
23
|
+
RepairDatabase Report Reports Requery RIGHT SCREEN SECTION SELECT SET SetFocus SetOption SHORT SINGLE
|
|
24
|
+
SMALLINT SOME SQL StDev StDevP STRING Sum TABLE TableDef TableDefs TableID TEXT TIME TIMESTAMP TOP
|
|
25
|
+
TRANSFORM TRUE Type UNION UNIQUE UPDATE USER VALUE VALUES Var VarP VARBINARY VARCHAR WHERE WITH Workspace
|
|
26
|
+
Xor Year YES YESNO
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
_ENGINE = """
|
|
30
|
+
ABSOLUTE ACTION ADD ADMINDB ALL ALLOCATE ALPHANUMERIC ALTER AND ANY ARE AS ASC ASSERTION AT AUTHORIZATION
|
|
31
|
+
AUTOINCREMENT AVG BAND BEGIN BETWEEN BINARY BIT BIT_LENGTH BNOT BOR BOTH BXOR BY BYTE CASCADE CASCADED
|
|
32
|
+
CASE CAST CATALOG CHAR CHARACTER CHAR_LENGTH CHARACTER_LENGTH CHECK CLOSE COALESCE COLLATE COLLATION
|
|
33
|
+
COLUMN COMMIT COMP COMPRESSION CONNECT CONNECTION CONSTRAINT CONSTRAINTS CONTAINER CONTINUE CONVERT
|
|
34
|
+
CORRESPONDING COUNT COUNTER CREATE CREATEDB CROSS CURRENCY CURRENT CURRENT_DATE CURRENT_TIME
|
|
35
|
+
CURRENT_TIMESTAMP CURRENT_USER CURSOR DATABASE DATE DATETIME DAY DEALLOCATE DEC DECIMAL DECLARE DEFAULT
|
|
36
|
+
DEFERRABLE DEFERRED DELETE DESC DESCRIBE DESCRIPTOR DIAGNOSTICS DISALLOW DISCONNECT DISTINCT DOMAIN
|
|
37
|
+
DOUBLE DROP ELSE END END-EXEC ESCAPE EXCEPT EXCEPTION EXCLUSIVECONNECT EXEC EXECUTE EXISTS EXTERNAL
|
|
38
|
+
EXTRACT FALSE FETCH FIRST FLOAT FLOAT4 FLOAT8 FOR FOREIGN FOUND FROM FULL GENERAL GET GLOBAL GO GOTO
|
|
39
|
+
GRANT GROUP GUID HAVING HOUR IDENTITY IEEEDOUBLE IEEESINGLE IGNORE IMAGE IMMEDIATE IN INDEX INDICATOR
|
|
40
|
+
INHERITABLE INITIALLY INNER INPUT INSENSITIVE INSERT INT INTEGER INTEGER1 INTEGER2 INTEGER4 INTERSECT
|
|
41
|
+
INTERVAL INTO IS ISOLATION JOIN KEY LANGUAGE LAST LEADING LEFT LEVEL LIKE LOCAL LOGICAL LOGICAL1 LONG
|
|
42
|
+
LONGBINARY LONGCHAR LONGTEXT LOWER MATCH MAX MEMO MIN MINUTE MODULE MONEY MONTH NAMES NATIONAL NATURAL
|
|
43
|
+
NCHAR NEXT NO NOT NOTE NULL NULLIF NUMBER NUMERIC OBJECT OCTET_LENGTH OF OLEOBJECT ON ONLY OPEN OPTION
|
|
44
|
+
OR ORDER OUTER OUTPUT OVERLAPS OWNERACCESS PAD PARAMETERS PARTIAL PASSWORD PERCENT PIVOT POSITION
|
|
45
|
+
PRECISION PREPARE PRESERVE PRIMARY PRIOR PRIVILEGES PROC PROCEDURE PUBLIC READ REAL REFERENCES RELATIVE
|
|
46
|
+
RESTRICT REVOKE RIGHT ROLLBACK ROWS SCHEMA SCROLL SECOND SECTION SELECT SELECTSCHEMA SELECTSECURITY
|
|
47
|
+
SESSION SESSION_USER SET SHORT SINGLE SIZE SMALLINT SOME SPACE SQL SQLCODE SQLERROR SQLSTATE STRING
|
|
48
|
+
SUBSTRING SUM SYSTEM_USER TABLE TABLEID TEMPORARY TEXT THEN TIME TIMESTAMP TIMEZONE_HOUR TIMEZONE_MINUTE
|
|
49
|
+
TO TOP TRAILING TRANSACTION TRANSFORM TRANSLATE TRANSLATION TRIM TRUE UNION UNIQUE UNIQUEIDENTIFIER
|
|
50
|
+
UNKNOWN UPDATE UPDATEIDENTITY UPDATEOWNER UPDATESECURITY UPPER USAGE USER USING VALUE VALUES VARBINARY
|
|
51
|
+
VARCHAR VARYING VIEW WHEN WHENEVER WHERE WITH WORK WRITE YEAR YESNO ZONE
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
RESERVED_WORDS: frozenset[str] = frozenset(word.casefold() for word in (_ACCESS + _ENGINE).split())
|
|
55
|
+
"""Case-folded Access + Access database engine reserved words."""
|