Spencer McGuire spencerm
  • Joined on 2026-06-16

mfileskit (0.1.2)

Published 2026-10-15 22:22:50 +00:00 by spencerm

Installation

pip install --index-url https://gt.pescoinc.biz/api/packages/spencerm/pypi/simple/ --extra-index-url https://pypi.org/simple mfileskit

About this package

Read-only access to M-Files vault metadata and files over the M-Files Web Service (REST) API

MFilesKit

Read-only Python access to PESCO's M-Files vault (metadata and files) over the M-Files Web Service REST API. See PLAN.md for the design and roadmap, and CHANGELOG.md for releases.

Using MFilesKit in your own app or scheduled job? Start with docs/INSTALL.md. This README is for working on MFilesKit itself.

Install (development)

python -m venv .venv
.venv\Scripts\python -m pip install -e ".[dev]"
.venv\Scripts\mfileskit doctor          # checks settings, password, server, sign-in and permissions

Configure

Copy .env.example to .env and fill in MFILES_URL. Don't put the password in .env. Either:

  • store it once in Windows Credential Manager: .venv\Scripts\keyring set mfileskit mfileskitRO, or
  • set MFILES_PASSWORD in the environment of the process that runs the library.

The CLI prompts for it if neither is set.

Use

from mfileskit import Vault

with Vault.from_env() as vault:
    print(vault.info.name)
    drawing = vault.schema.cls("Drawing (DWG)")
    rev = vault.schema.property("Revision number", cls=drawing.id)
mfileskit info                         # sign in, show vault and schema counts
mfileskit schema                       # list object types
mfileskit schema --type Document       # classes of an object type
mfileskit schema --class "Drawing (DWG)"   # properties of a class
mfileskit schema --find drawing        # search class and property names

Drawings (Packet Station's lookup)

mfileskit drawings status L073-76-2 K978-01-6H13         # released rev, draft rev, drafting status
mfileskit drawings status --in data\feed\want.json       # batch, JSON keyed by want.json item id
mfileskit drawings fetch L073-76-2 --rev 3 --out x.pdf   # download only if the released rev is 3
from mfileskit.drawings import DrawingLookup

with Vault.from_env() as vault:
    lookup = DrawingLookup(vault)
    st = lookup.status("L073-76-2")
    if lookup.matches(st, visual_rev="3").attach:
        lookup.download(st, "L073762X r3.pdf")

See docs/packet-station.md for how drawings are laid out in the vault.

Bulk export and sync

mfileskit count --class "Drawing Production (PDF)"                        # list only, no downloads
mfileskit export --class "Drawing Production (PDF)" --ext pdf --out D:\exports\drawings
mfileskit export --class "Drawing Production (PDF)" --ext pdf --out D:\exports\drawings              # again: only changes
mfileskit export --class "Drawing Production (PDF)" --ext pdf --out D:\exports\drawings --reconcile  # also record deletions

Current drawings only, kept as a mirror that drops replaced revs:

mfileskit export --class "Drawing Production (PDF)" --ext pdf --current --prune --out D:\exports\current-drawings
  • --current is shorthand for --where "Controlled Status=Effective". --where "PROPERTY=VALUE|VALUE" works with any property's display text, and can be repeated.
  • The filter is applied after listing the whole class. That way, when a drawing becomes Superseded, the export notices: it writes a deleted line with the reason, and --prune deletes its files.
  • --prune sweeps the whole folder on every run. It keeps only each object's latest exported version, and deletes the files of removed objects. Running it once on a folder that was exported without --prune tidies that folder too. It only ever deletes version folders (files/<type>/<id>/<version>/) inside the export folder, never anything in M-Files. A folder that can't be deleted (a file open in a viewer) is logged and retried on the next run.

Output, one job per folder:

  • files/<type>/<id>/<version>/<file>
  • manifest.jsonl: one line per exported object version, with all properties (name, display text, and the raw value or lookup IDs) and its files (path, size, SHA-256). Deletions are added as {"deleted": true, "reason": ...} lines. When reading, keep the last line per (type, id).
  • .mfileskit/state.sqlite: what was exported, plus the checkpoint and the job's scope (classes, type, filters, --ext, --no-files). A run with a different scope is refused: use a new folder.
  • .mfileskit/lock.sqlite: held while a run is in progress, so a second run on the folder fails at once.
  • .mfileskit/runs.jsonl: a summary of each run.

Searches stop at 500 results. The export gets around this by splitting the query into "Last modified" time windows, and it checks every result against the window it asked for. The checkpoint only advances after a complete run with no failures, so if a run fails, just run it again.

Names are matched case-insensitively. A few property names are reused in the vault (e.g. "Description"). Pass cls= to narrow them, or use the ID or GUID.

Release

  1. Bump __version__ in src/mfileskit/__init__.py and add a CHANGELOG.md entry.
  2. Run the tests, then build:
    .venv\Scripts\python -m pytest
    .venv\Scripts\python -m build
    
  3. Upload to the Gitea package registry. This needs a Gitea access token with write:package scope; paste it when twine asks for the password:
    .venv\Scripts\python -m twine upload --repository-url https://gt.pescoinc.biz/api/packages/spencerm/pypi -u spencerm dist\*
    
  4. If any runtime dependency version changed, update requirements/runtime-lock.txt. The first section lists the versions the tests ran with. The second section has dependencies that only apply to Python 3.11 or Linux; for those, pick releases that are at least 14 days old. Then mirror them to the registry for every platform and Python version, so installs keep working from PESCO's registry alone.
    • Resolving for another platform needs uv, because pip applies sys_platform conditions using the machine it runs on.
    • Repeat the steps below for x86_64-pc-windows-msvc with --platform win_amd64, and for x86_64-manylinux_2_17 with --platform manylinux2014_x86_64.
    • Repeat for each Python version 3.11–3.14.
    echo mfileskit==<version> > requirements.in
    uv pip compile requirements.in -c requirements\runtime-lock.txt --find-links dist --python-platform x86_64-manylinux_2_17 --python-version 3.11 -o resolved.txt
    .venv\Scripts\python -m pip download -r resolved.txt --no-deps --only-binary=:all: --platform manylinux2014_x86_64 --implementation cp --python-version 3.11 -d wheelhouse
    .venv\Scripts\python -m twine upload --skip-existing --repository-url https://gt.pescoinc.biz/api/packages/spencerm/pypi -u spencerm wheelhouse\*.whl
    
    requirements.in is a scratch file (not committed) holding the single line mfileskit==<version>. Remove that line from resolved.txt before downloading, since the package itself is uploaded from dist.
  5. Tag the commit you built from: git tag v<version> and git push --tags. The registry won't take a second upload of the same version, so any fix after uploading needs a new version number.

Test

.venv\Scripts\python -m pytest

Requirements

Requires Python: >=3.11
Details
PyPI
2026-10-15 22:22:50 +00:00
13
Proprietary - PESCO internal use
97 KiB
Assets (2)
Versions (4) View all
0.1.3 2026-10-19
0.1.2 2026-10-15
0.1.1 2026-10-15
0.1.0 2026-10-15