Project Structure
Repository layout
Connectors follow a src/ directory structure.
├── device-name
├── CHANGELOG.md
├── README.md
├── pyproject.toml
├── uv.lock
├── scripts
└── example.py
├── src
└── unitelabs
└── package_name
├── __init__.py
├── __main__.py
├── config.py
└── io
├── __init__.py
├── errors.py
└── protocol.py
└── features
└── random_number_provider
├── __init__.py
└── random_number_provider.py
└── tests
├── conftest.py
├── features
└── test_random_number_provider.py
└── io
└── test_protocol.py
io/ talks to the device and has no knowledge of SiLA; features/ defines the SiLA interface and calls into io/. See Connector Architecture for why the layers are split this way.
For file and folder names, see Naming Conventions. For the tests/ layout, see Testing.
Split a large protocol
Once the protocol class gets too long, split it into delegates, one submodule per physical module of the device:
└── io
├── protocol.py
└── commands
├── pipette
├── core_96
└── heater_shaker
Name and document the commands in io/ the way the vendor's own documentation does, so the protocol layer stays traceable to the manual. Translating to the UniteLabs standard vocabulary happens in features/.
Configuration
Keep device-specific configuration in a dedicated config.py rather than inline in __init__.py. The entry point stays clean, and the config is easy to find and test on its own. See the Configuration Guide for the full details.
Don't commit config.json. It is a development-only file, created by config create or connector dev, and nothing keeps it in sync with config.py. Add it to .gitignore.
Custom exceptions
Put custom exception types for hardware-level errors in io/errors.py, close to the code that raises them. Exceptions specific to the SiLA interface can live in the feature file, or in features/errors.py when several features share them.
Debugging commands and standard feature extensions
Put debugging and developer-helper commands, such as jogging for calibration or sending raw commands, in their own feature. Don't mix them into the connector's functional features.
When your device needs a command or property that a standard SiLA feature doesn't define, add it directly to that feature class. Don't create a new feature for it or move it into the debugging feature.
Order within a file
Order a class from top to bottom:
- Constructor
- Properties
- Public methods
- Private methods
A first-time reader wants to know what the class does before its internal details. Give private methods self-descriptive names, so the file reads top to bottom without jumping around.
Feature classes shouldn't need private helpers at all: keep business logic in io/.
README and CHANGELOG
Every connector should have both:
README.md: what the connector does, which device models it supports and has been tested against, how to configure and run it, and any known limitations.CHANGELOG.md: a record of every release, with what changed, what was fixed, and what was added. This is the first place someone looks when an upgrade breaks something.
Last updated