psyexp-core
Task-agnostic harness for PsychoPy experiments, shared across the lab's task
repos (heat-task, mid-task, mid-task-deterministic). It owns the plumbing
that every task duplicates; each task repo keeps only its own stimuli, trial
logic, and record schemas.
What's in here
| Module | Responsibility |
|---|---|
screen |
setup_screen() — open a fullscreen PsychoPy window, enable VSYNC, run a frame-timing calibration, and return a ScreenDiagnostics. |
diagnostics |
The ScreenDiagnostics dataclass (import-light; no PsychoPy). |
rundir |
make_run_dir(data_dir, label, session_started_at) — timestamped output directory. |
manifest |
write_manifest(...) + system_info() — JSON run manifest with system/display/process diagnostics and the resolved psyexp_core_version. App-specific fields are injected via header / study_params. |
recording |
CsvWriter base class (maps a dataclass record onto a fixed column schema). |
wizard |
questionary / prompt_toolkit setup-wizard primitives: shared styles, ask_text / ask_select / ask_confirm, PosFloatValidator, prompt_unique_name, quit_app. |
instructions |
page_through(...) — a self-paced, keypress-driven instruction pager. |
keyboard |
PTB / PsychoPy-event keyboard abstraction: build_keyboard / get_keys / wait_for_keys / clear_events, plus the timed-press API for response windows — get_presses (name + rt), reset_clock_on_flip / reset_clock / clock_time. |
Install
psyexp-core is published on PyPI, so
the released harness installs like any other dependency:
uv add psyexp-core # or: pip install psyexp-core
# your-task/pyproject.toml
dependencies = ["psyexp-core>=0.5"]
Requires Python 3.11+. On macOS the pyobjc-framework-quartz dependency is
pulled in automatically for display diagnostics.
Use from a task repo
You can keep the plain PyPI dependency above, or override the source while developing. For day-to-day work, point at a local checkout so edits are live without reinstalling:
# your-task/pyproject.toml
dependencies = ["psyexp-core"]
[tool.uv.sources]
psyexp-core = { path = "../psyexp-core", editable = true }
For a reproducible build pinned ahead of (or instead of) a PyPI release, pin a tagged git ref instead:
[tool.uv.sources]
psyexp-core = { git = "ssh://git@github.com/HAPNlab/psyexp-core.git", tag = "v0.5.1" }
write_manifest records the resolved psyexp_core_version so each run is
traceable back to a core version.
Co-developing core while a task repo keeps the git pin
Lab task repos (e.g. heat-task) commit the git-tag source above so clones
reproduce exactly, then overlay a local editable install for development:
uv pip install -e ../psyexp-core
Gotcha: uv run re-syncs the task venv from its uv.lock on every launch,
which reverts that editable install straight back to the pinned tag (symptoms:
your local core edits silently don't take effect). Set UV_NO_SYNC=1 in the task
repo (export it in your shell, or use uv run --no-sync) so the editable overlay
sticks; run a manual uv sync only when you change other deps, then re-run the
editable install. See heat-task's README ("Co-developing psyexp-core locally")
for the full workflow.
Releasing
Tagging and the PyPI upload are deliberately separate, so tags stay cheap to iterate on:
- Bump + lock + changelog, then tag
vX.Y.Z. The tag runsrelease.yml: the checks, a published GitHub Release, and a built wheel — but it does not upload to PyPI on its own. - The same run pauses on the
pypienvironment for reviewer approval. Approve it to upload to PyPI via Trusted Publishing (OIDC; no API token stored), or reject to abort.
PyPI versions are immutable, so retagging never republishes; bump the version to ship new code. See docs/releasing.md for the full process, SemVer policy, pre-releases, retag semantics, and the one-time PyPI setup.
Release files for psyexp-core 0.9.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 | |
|---|---|---|---|
| psyexp_core-0.9.0.tar.gz | 138.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| psyexp_core-0.9.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 163.3 kB
Release files / psyexp_core-0.9.0.tar.gz
| Download URL | psyexp_core-0.9.0.tar.gz |
|---|---|
| Size | 138.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
215fb7309541d9e13a0b6e3398a44b2b13276e7791dc38c7dd314aaf7d87e561
|
|
BLAKE2b-256 checksum How to use checksums |
e51b50431d9fbb725aa2f85e4948612e588188855035f1b79542f4ef6cc0e85c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Jul 6, 2026.
Transparency logRelease files / psyexp_core-0.9.0-py3-none-any.whl
| Download URL | psyexp_core-0.9.0-py3-none-any.whl |
|---|---|
| Size | 25.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5e6ed07bd4d7a0b8142d9fe0d6d2c7d9e73dcc7e293cf082243cb6e37c8bedb8
|
|
BLAKE2b-256 checksum How to use checksums |
966c56061abc8cbb10d2272cca1c46664f466c25950cab872b66cfe8e0070fc9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Jul 6, 2026.
Transparency log