PyAccessKit
A modern, typed, Pythonic toolkit for building and modifying Microsoft Access databases (.accdb) and,
progressively, whole Access applications without raw pywin32 calls, DAO magic numbers or orphaned
MSACCESS.EXE processes.
from pyaccesskit import AccessDatabase, Column, Expr, Vba, cm
with AccessDatabase.create("crm.accdb") as db:
db.tables.create(
"Customers",
columns=[
Column.autonumber("CustomerID", primary_key=True),
Column.text("CustomerName", length=200, required=True),
Column.text("Email", length=255, unique=True),
Column.date_time("CreatedAt", default=Expr("Now()")),
],
)
db.tables.create(
"Orders",
columns=[
Column.autonumber("OrderID", primary_key=True),
Column.number("CustomerID", required=True), # Long Integer, as in Access
Column.currency("Total", default=0),
],
)
db.relationships.create("Customers.CustomerID", "Orders.CustomerID", cascade_delete=True)
db.queries.create("qryCustomers", "SELECT * FROM Customers ORDER BY CustomerName;")
with db.forms.create("frmCustomers", record_source="Customers", caption="Customers") as form:
form.textbox("CustomerName", label="Customer name", width=cm(8))
form.textbox("Email")
form.button("cmdClose", caption="Close", on_click=Vba("DoCmd.Close acForm, Me.Name"))
When the with block ends, the database file appears at crm.accdb, and any Access process PyAccessKit
started is closed. If the block raises, no file is created and nothing is left running.
Status: alpha (
0.1.0). The API may still change between 0.x releases.
Installation
pip install pyaccesskit
Requirements:
- Windows 10 or 11 and CPython 3.11–3.14 (64- or 32-bit; free-threaded builds are not supported).
- Microsoft Access 2016 or later, including Microsoft 365. Alternatively, the Microsoft 365 Access Runtime with a Python of the same bitness, for schema and data work only.
Check your machine with:
pyaccesskit doctor # which engines work here, and why not
pyaccesskit doctor --probe # also builds a scratch database end to end
The spec models (TableSpec, Column, FormSpec, units, layout…) are pure Python and also import on
Linux and macOS, so you can validate specs in CI without Access.
What you can do in 0.1
| Area | Highlights | Stability |
|---|---|---|
| Lifecycle | Atomic create(), open() (read-only/shared, exclusive, password), guaranteed cleanup, db.raw escape hatch |
stable-track |
| Tables | Every common column type, defaults as Python values or Expr, validation rules, captions, formats, input masks; indexes (composite, descending, unique, primary); add, drop and rename columns and indexes; to_spec() round-trips |
stable-track |
| Relationships | Single and composite keys, referential integrity, cascades, join types, all checked before Access sees them | stable-track |
| Queries & data | Saved queries (select, action, union, crosstab, pass-through), parameters bound by name, execute() / fetch_all() |
stable-track |
| Forms | Labels, text boxes, check boxes, combo boxes and buttons; stacked or tabular layout; VBA event procedures; atomic build-then-swap replacement | provisional |
| Modules & text I/O | Standard and class modules; SaveAsText/LoadFromText for forms, reports, macros, queries and modules, stored as UTF-8 |
provisional |
| Decimal columns | Column.decimal(precision=…, scale=…) via ADO DDL |
provisional |
Reports, linked tables, VBA references, and the declarative build/plan/apply workflow are planned
(see the roadmap).
Engines
PyAccessKit reaches the database in one of three ways and picks one for you (engine="auto"):
| Engine | When | Notes |
|---|---|---|
| In-process DAO | Python and Office have the same bitness | Fastest; never starts MSACCESS.EXE; enough for tables, relationships, queries and data |
| Access-hosted DAO | Bitness differs, or engine="access" |
A hidden Access instance that PyAccessKit owns hosts DAO; the database is not opened in the Access UI, so no startup code runs |
| Design session | First use of forms, modules or text I/O | The database becomes Access's current database; handles such as db.tables["X"] keep working across the switch |
| Python | Office | In-process DAO | Access transports |
|---|---|---|---|
| 64-bit | 64-bit | ✅ | ✅ |
| 32-bit | 32-bit | ✅ | ✅ |
| 64-bit | 32-bit (common with Microsoft 365) | ❌ | ✅ |
| 32-bit | 64-bit | ❌ | ✅ |
pyaccesskit doctor explains what applies to your machine.
Safety guarantees
- Never touches your own Access windows. PyAccessKit always starts a new Access instance
(
CoCreateInstanceEx, local server). It never attaches to a running Access (whichDispatch()does), so it can never close your work. - No orphaned
MSACCESS.EXE. Every Access process it starts is identified by PID, creation time and image, and placed in a kill-on-close job object. If Python crashes, Windows ends the process. The process is also recorded in an ownership ledger:pyaccesskit cleanupends leftovers from crashed sessions, and only when their Python owner is gone. Processes it did not start are never touched. - Exceptions clean up too. Closing runs every cleanup step even if one fails. Cleanup failures are attached as notes to the exception that caused the close, never masking it.
- Atomic creation.
AccessDatabase.create()builds in a hidden sibling file and moves it into place only on success. Tables are created all-or-nothing, and forms are built under a temporary name and swapped in. - No hangs on hidden dialogs. A watchdog watches the windows of our Access process. Unexpected
dialogs are dismissed and reported as
AccessDialogErrorwith their text. Calls that exceedcall_timeoutend the owned process, and Ctrl+C works during long calls. - No startup code by default. Databases are opened with macros disabled. Read-only sessions never open
the database in the Access UI, so
AutoExecand startup forms do not run.
Errors you can act on
COM errors are translated into specific exceptions. Each one carries what PyAccessKit was doing, the object involved, and the original Access or DAO error:
from pyaccesskit import AccessDatabase, IntegrityViolationError
with AccessDatabase.open("crm.accdb") as db:
try:
db.execute("INSERT INTO Orders (CustomerID, Total) VALUES (999, 10)")
except IntegrityViolationError as exc:
print(exc) # what failed, with Access's own explanation
print(exc.details.number) # 3201: there is no related record in Customers
Most mistakes are caught before Access is involved at all. A duplicate table name, an unknown column in a
relationship, or incompatible key types each raise a precise ObjectExistsError, SpecError or
RelationshipError.
Command line
pyaccesskit doctor [--json] [--probe] # environment report (exit code 3 if nothing works)
pyaccesskit inspect DB [--json] [--counts] # tables, relationships, queries, objects; read-only
pyaccesskit cleanup [--dry-run] [--json] # end orphaned Access processes started by PyAccessKit
pyaccesskit guide [--path] # the guide for AI coding agents
pyaccesskit schema [KIND] # JSON Schema of the specs
Exit codes: 0 success, 1 error, 2 usage error, 3 environment unusable.
Documentation
- Getting started and recipes
- Building with AI agents:
pyaccesskit guideprints a version-matched guide that lets coding agents write Access applications with PyAccessKit;pyaccesskit schemaprints the JSON Schemas of the specs; the site publishesllms.txtandllms-full.txt. - Concepts: engines, lifecycle, specs
- Guides: tables, relationships, queries, forms, modules, text I/O
- Reference: errors, data types, command line, and an API reference generated from the docstrings
- Environment & troubleshooting, FAQ, contributing, architecture decisions
- Examples:
examples/(04_inventory_app.pyis the complete, tested application the agent guide walks through)
License
MIT. See LICENSE.
Release files for pyaccesskit 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pyaccesskit-0.1.0.tar.gz | 246.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pyaccesskit-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 422.3 kB
Release files / pyaccesskit-0.1.0.tar.gz
| Download URL | pyaccesskit-0.1.0.tar.gz |
|---|---|
| Size | 246.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
359b073fbe07e0a53025f0fb227aa99a223b0bcdb4ce732578478f5e8ff78391
|
|
BLAKE2b-256 checksum How to use checksums |
dd533208819c45febae66ab1a7b3d8ba174a3e7d35d28662dee93da7354a7a09
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency logRelease files / pyaccesskit-0.1.0-py3-none-any.whl
| Download URL | pyaccesskit-0.1.0-py3-none-any.whl |
|---|---|
| Size | 176.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2d7bc6025327746b395f32f3b2f57ab1f96e51a4ae562cc76a51122a66f25c42
|
|
BLAKE2b-256 checksum How to use checksums |
909388e43ed1a71e91d04346ba7680e3a61f2e8d2201085eeddbaaf8909e1b62
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency log