Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,52 @@ We follow scientific Python coding standards to maintain consistency:
- Use snake_case for functions/variables, CamelCase for classes
- Use double quotes for strings

## Dependency Management

EthoPy pins dependencies in two layers, and they do different jobs.

### `pyproject.toml`

Declares the core dependencies with loose ranges. Upper bounds are added only where a package has a track record of breaking releases, currently `datajoint`, `setuptools`, `numpy`, `pandas` and `scipy`. Do not add a cap to every dependency. Blanket caps cause resolution conflicts for anyone installing EthoPy alongside other packages, and each cap needs a release to lift.

When adding a new core dependency, add it without an upper bound unless you have evidence of a breakage, and refresh the lock file in the same pull request.

### `requirements-lock.txt`

One pinned version per package for the entire tree, including transitive dependencies. It is a single cross-platform file: environment markers cover the few packages that differ by operating system or Python version, so there is no per-OS variant to keep in sync.

Versions are chosen as the newest one actually running on a verified EthoPy machine that still supports the whole declared Python range, with the resolver enforcing mutual consistency. The point is to record what actually ran, not what a resolver believes should work.

Hardware and analysis packages are deliberately excluded. Every such import in EthoPy is lazy, so the core runs without them, and they are installed per machine.

To refresh it:

1. On each machine you care about, build a fresh virtual environment, install EthoPy without
the lock file, run the test suite and a real experimental session, then capture
`pip freeze --exclude-editable`. Do not skip the session; some breakages only appear at
runtime with hardware attached.
2. Regenerate the cross-platform pin set, capped at the versions you just verified:
```bash
uv pip compile pyproject.toml --universal --python-version <lowest you support> \
--constraint <verified versions> -o requirements-lock.txt
```
3. Confirm it still resolves on every target before committing:
```bash
uv pip compile requirements-lock.txt --python-version 3.9.2 \
--python-platform aarch64-unknown-linux-gnu
```
Repeat for each Python version and architecture you support. Any version drift means the
pins are inconsistent.
4. Update the header comment with the source machines, Python versions and date.

### If nobody refreshes this

The lock file will age. Installs will keep working, reproducing an increasingly old
environment, and EthoPy will fall behind the ecosystem. This is the intended failure mode and
it is preferred to the alternative. An install that reproduces a two-year-old working
environment is still a working install; an install that silently picks up an untested major
release is not.

## Testing Guidelines

Ethopy has specific testing requirements due to its database connections:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ EthoPy provides a flexible, state-based system for designing and running behavio

### Requirements

- Python 3.8 or higher
- Python 3.9 to 3.11
- Maria DB Database
- EthoPy requires database setup before running experiments. Follow our step-by-step guide: **[Getting Started Guide](https://ef-lab.github.io/ethopy_package/getting_started/)** for a complete setup from installation to first experiment

Expand Down
41 changes: 38 additions & 3 deletions docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,42 @@ Ready to contribute? Here's how to set up Ethopy for local development:

9. Submit a pull request through the GitHub website.

## Dependency Management

EthoPy constrains dependencies in two layers, and they do different jobs.

### `pyproject.toml`

Declares the core dependencies with loose ranges. Upper bounds are added only where a package has a track record of breaking releases, currently `datajoint`, `setuptools`, `numpy`, `pandas` and `scipy`. Do not cap every dependency. Blanket caps cause resolution conflicts for anyone installing EthoPy alongside other packages, and each one needs a release to lift.

When adding a new core dependency, add it without an upper bound unless you have evidence of a breakage, and refresh the lock file in the same pull request.

### `requirements-lock.txt`

One pinned version per package for the entire tree, including transitive dependencies, and what [Installation](installation.md) points every machine at. It is a single cross-platform file: environment markers cover the few packages that differ by operating system or Python version, so there is no per-OS variant to keep in sync.

Versions are chosen as the newest one actually running on a verified EthoPy machine that still supports the whole declared Python range, with the resolver enforcing mutual consistency. Hardware and analysis packages are excluded, since every such import in EthoPy is lazy and they are installed per machine.

To refresh it:

1. On each machine you care about, build a fresh virtual environment, install EthoPy without the lock file, run the test suite and a real experimental session, then capture `pip freeze --exclude-editable`. Do not skip the session, some breakages only appear at runtime with hardware attached.
2. Regenerate the cross-platform pin set, capped at the versions you just verified:
```bash
uv pip compile pyproject.toml --universal --python-version <lowest you support> \
--constraint <verified versions> -o requirements-lock.txt
```
3. Confirm it still resolves on every target before committing:
```bash
uv pip compile requirements-lock.txt --python-version 3.9.2 \
--python-platform aarch64-unknown-linux-gnu
```
Repeat for each Python version and architecture you support. Any version drift means the pins are inconsistent.
4. Update the header comment with the source machines, Python versions and date.

### If nobody refreshes this

The lock file will age. Installs keep working, reproducing an increasingly old environment, and EthoPy falls behind the ecosystem. This is the intended failure mode and it is preferred to the alternative. An install that reproduces a two-year-old working environment is still a working install. An install that silently picks up an untested major release is not.

## Pull Request Guidelines

Before you submit a pull request, check that it meets these guidelines:
Expand All @@ -105,6 +141,5 @@ Before you submit a pull request, check that it meets these guidelines:
2. If the pull request adds functionality, the docs should be updated.
Put your new functionality into a function with a docstring, and add
the feature to the list in README.md
3. The pull request should work for Python 3.8 and later, and
for PyPy. Check <https://github.com/ef-lab/ethopy_package/pulls> and make sure that the tests pass for all
supported Python versions.
3. The pull request should work for Python 3.9 through 3.11, the range declared by
`requires-python`. Check <https://github.com/ef-lab/ethopy_package/pulls> and make sure that the tests pass for all supported Python versions.
2 changes: 1 addition & 1 deletion docs/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This guide will walk you through the process of setting up and running your firs

Before starting, ensure you have:

- Python 3.8 or higher (but less than 3.12) installed
- Python 3.9 or higher (but less than 3.12) installed
- MariaDB database
!!! tip Database setup
We recommend using [Docker](https://www.docker.com/blog/getting-started-with-docker-desktop/) for setting up a new database.
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ EthoPy provides a flexible, state-based system for designing and running behavio

### Requirements

- Python 3.8 or higher
- Python 3.9 to 3.11
- Maria DB Database
- EthoPy requires database setup before running experiments. Follow our step-by-step guide **[Getting Started](https://ef-lab.github.io/ethopy_package/getting_started/)** for a complete setup from installation to first experiment

Expand Down
32 changes: 27 additions & 5 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Requirements

- Python >=3.8,<3.12
- Python >=3.9,<3.12
- Docker (for database setup)
- Dependencies: numpy, pandas, datajoint, pygame, pillow, and more (automatically installed)

Expand All @@ -16,16 +16,38 @@ To install EthoPy with basic features, run:
pip install ethopy
```

This is the preferred method as it will install the most recent stable release.
This resolves the most recent versions allowed by EthoPy's declared dependency ranges. Those versions may be newer than anything that has been tested on a rig.

### Reproducible Installation (recommended for rigs)

`requirements-lock.txt` in the repository records the exact versions of every package, including transitive dependencies, from a Raspberry Pi rig that is known to run correctly. Install that environment first, then install EthoPy without letting it re-resolve:

```bash
pip install -r https://raw.githubusercontent.com/ef-lab/ethopy_package/main/requirements-lock.txt
pip install --no-deps ethopy
```

This works on every supported platform. The lock file uses environment markers rather than being tied to one operating system, and is verified to install from wheels on Python 3.9.2, 3.10 and 3.11, on both aarch64 (Raspberry Pi) and x86_64 (Linux, macOS, Windows). No pin requires a compiler.

The locked versions are refreshed by hand, so they will lag behind the newest releases. That is intentional. See [Dependency Management](contributing.md#dependency-management) for the refresh procedure.

#### Per-machine packages

The lock file covers only EthoPy's core dependencies. Hardware and analysis packages are excluded, because every one of them is a lazy import and the core runs without them. Install them on top of the lock file, only where they are needed:

```bash
# Raspberry Pi rigs
pip install RPi.GPIO pigpio pyserial picamera2 opencv-python

# DeepLabCut setups, such as the openfield workstation
pip install deeplabcut-live scikit-video
```

### Optional Features

For additional functionality:

```bash
# For 3D object support
pip install "ethopy[obj]"

# For development
pip install "ethopy[dev]"

Expand Down
2 changes: 1 addition & 1 deletion docs/raspberry_pi.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Once your Raspberry Pi is set up, you can connect to it from your computer's ter
python --version
```

EthoPy requires Python >=3.8, < 3.12
EthoPy requires Python >=3.9, < 3.12

2. Setting Up a Virtual Environment

Expand Down
11 changes: 5 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "ethopy"
dynamic = ["version"]
description = "State control system for automated, high-throughput behavioral training."
readme = "README.md"
requires-python = ">=3.8"
requires-python = ">=3.9,<3.12"
license = {text = "MIT License"}
authors = [
{name = "Emmanouil Froudarakis", email = "frouman@imbb.forth.gr"},
Expand All @@ -14,7 +14,6 @@ classifiers = [
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Natural Language :: English",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
Expand All @@ -24,12 +23,12 @@ dependencies = [
"click>=8.1.0",
"datajoint<2.0.0",
"imageio",
"pandas>=2.0.0",
"pandas>=2.0.0,<4",
"h5py",
"pygame",
"pygame-menu",
"numpy",
"scipy",
"numpy<3",
"scipy<2",
"pyopengl",
"imageio-ffmpeg",
"pyfiglet",
Expand Down Expand Up @@ -79,7 +78,7 @@ packages = ["src/ethopy"]
only-include = ["src/ethopy"]

[tool.mypy]
python_version = "3.8"
python_version = "3.9"
disallow_untyped_defs = true
ignore_missing_imports = true
disable_error_code = ["attr-defined", "misc"]
Expand Down
126 changes: 126 additions & 0 deletions requirements-lock.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# EthoPy locked environment
#
# One pinned version per package, covering EthoPy's full dependency tree.
# Environment markers (the "; ..." clauses) handle the few packages that differ
# by operating system or Python version, so this stays a single file.
#
# VERIFIED TO RESOLVE AND INSTALL FROM WHEELS ON
# Python 3.9.2+, 3.10, 3.11
# aarch64 (Raspberry Pi) and x86_64 (Linux, macOS, Windows)
# All six Linux combinations were checked with no version drift. Every pin has a
# wheel; nothing needs a compiler.
#
# HOW THESE VERSIONS WERE CHOSEN
# Each is the newest version actually running on a verified EthoPy machine that
# still supports the whole Python range, with the resolver enforcing mutual
# consistency. 80 of the 87 pins are running in the fleet today; the rest are
# marked below.
#
# sources : ef-rp163, ef-rp160 (Raspberry Pi 4, Python 3.9.2, aarch64)
# openfield (workstation, Python 3.10.12, x86_64)
# date : 2026-09-03
#
# INSTALL
# pip install -r requirements-lock.txt
# pip install --no-deps ethopy
#
# PYTHON 3.9 IS THE BINDING CONSTRAINT
# click, h5py, imageio, matplotlib and scikit-learn are held below the versions
# on the openfield workstation because their newer releases require Python 3.10.
# If the Raspberry Pi rigs move off Python 3.9, this file can advance a long way.
#
# NOT INCLUDED: PER-MACHINE PACKAGES
# Every hardware and analysis import in EthoPy is lazy, so the core runs without
# these. Install them on top of this file, only on the machines that need them:
#
# Raspberry Pi : RPi.GPIO pigpio pyserial picamera2 opencv-python
# DeepLabCut rig : deeplabcut-live scikit-video
#
# Refreshing this file is a manual step. See CONTRIBUTING.md.
appdirs==1.4.4
appnope==1.0.0 ; sys_platform == 'darwin' # not in the fleet; resolver choice
argon2-cffi==25.1.0 # runs on openfield
argon2-cffi-bindings==25.1.0 # runs on openfield
asttokens==3.0.1 # runs on openfield
attrs==26.1.0
backcall==0.2.0 # runs on rp163, rp160
blinker==1.9.0 # runs on openfield
certifi==2026.4.22 # runs on openfield
cffi==2.0.0 # runs on openfield
click==8.1.8 # runs on rp163, rp160
colorama==0.4.6 ; sys_platform == 'win32' # runs on openfield
contourpy==1.1.0 # runs on rp163, rp160
cryptography==48.0.0 # runs on openfield
cycler==0.12.1 # runs on openfield
datajoint==0.14.9 # runs on openfield
decorator==5.2.1 # runs on openfield
deepdiff==8.6.2 # not in the fleet; resolver choice
exceptiongroup==1.3.1 ; python_full_version < '3.11' # runs on openfield
executing==2.2.1 # runs on openfield
faker==37.12.0 # not in the fleet; resolver choice
flask==3.1.3 # runs on openfield
fonttools==4.42.1 # runs on rp163, rp160
h5py==3.14.0 # runs on rp163
hdmf==4.3.1
imageio==2.31.3 # runs on rp163, rp160
imageio-ffmpeg==0.6.0 # runs on openfield
importlib-metadata==8.4.0 ; python_full_version < '3.10' # not in the fleet; resolver choice
importlib-resources==6.0.1 ; python_full_version < '3.10' # runs on rp163, rp160
ipython==8.15.0 # runs on rp163, rp160
itsdangerous==2.2.0 # runs on openfield
jedi==0.18.0 # runs on rp163, rp160
jinja2==3.1.6 # runs on openfield
joblib==1.5.3 # runs on openfield
jsonschema==4.25.1 # runs on rp163, rp160
jsonschema-specifications==2025.9.1
kiwisolver==1.4.5 # runs on rp163, rp160
markupsafe==3.0.3 # runs on openfield
matplotlib==3.7.2 # runs on rp163, rp160
matplotlib-inline==0.2.2 # runs on openfield
minio==7.2.20 # runs on openfield
networkx==2.6.3 # runs on rp163, rp160
numpy==1.26.4 # runs on openfield
orderly-set==5.5.0 # runs on openfield
otumat==0.3.1
packaging==26.2 # runs on openfield
pandas==2.3.3 # runs on openfield
parso==0.8.7 # runs on openfield
pexpect==4.9.0 ; sys_platform != 'win32' # runs on openfield
pickleshare==0.7.5 # runs on rp163, rp160
pillow==10.0.0 # runs on rp163, rp160
platformdirs==4.4.0 # runs on rp163, rp160
prompt-toolkit==3.0.52 # runs on openfield
ptyprocess==0.7.0 ; sys_platform != 'win32' # runs on openfield
pure-eval==0.2.3 # runs on openfield
pycparser==2.22 ; implementation_name != 'PyPy' # runs on rp160
pycryptodome==3.23.0 # runs on openfield
pydot==4.0.0 # not in the fleet; resolver choice
pyfiglet==1.0.4 # runs on rp163, openfield
pygame==2.6.1 # runs on openfield
pygame-menu==4.5.2 # runs on openfield
pygments==2.20.0 # runs on openfield
pymysql==1.1.3 # runs on openfield
pynwb==3.1.3
pyopengl==3.1.10 # runs on openfield
pyparsing==3.0.9 # runs on rp163, rp160
pyperclip==1.11.0 # runs on openfield
python-dateutil==2.9.0.post0 # runs on openfield
pytz==2026.2 # runs on openfield
referencing==0.36.2 # runs on rp163, rp160
rpds-py==0.27.1 # runs on rp163, rp160
ruamel-yaml==0.19.1
scikit-learn==1.4.2 # runs on rp163, rp160
scipy==1.13.1 # not in the fleet; resolver choice
setuptools==81.0.0 # not in the fleet; resolver choice
six==1.17.0 # runs on openfield
stack-data==0.6.3 # runs on openfield
threadpoolctl==3.6.0 # runs on openfield
tqdm==4.67.3 # runs on openfield
traitlets==5.15.0 # runs on openfield
typing-extensions==4.15.0 # runs on rp163, openfield
tzdata==2026.2 # runs on openfield
urllib3==1.26.5 # runs on rp163, rp160
watchdog==6.0.0 # runs on openfield
wcwidth==0.7.0 # runs on openfield
werkzeug==3.1.8 # runs on openfield
zipp==3.16.2 ; python_full_version < '3.10' # runs on rp163, rp160
Loading