Skip to content

Setup

Get your development environment ready to build neops function blocks.

Prerequisites

You need three things installed:

Tool Version Purpose
Python 3.12+ Runtime for function blocks
uv latest Fast Python package manager
Code editor any VS Code or PyCharm recommended

Installing uv

curl -LsSf https://astral.sh/uv/install.sh | sh

See docs.astral.sh/uv for other installation methods.

Project Setup

Scaffold a new project with uv:

uv init my-neops-worker
cd my-neops-worker
uv add neops_worker_sdk
Prefer pip?

If you prefer pip over uv, create a virtual environment and install manually:

mkdir my-neops-worker && cd my-neops-worker
python -m venv .venv && source .venv/bin/activate
pip install neops_worker_sdk
pip install pytest pytest-asyncio  # for testing

Replace the generated pyproject.toml with a configuration tailored for neops development:

[project]
name = "my-neops-function-blocks"
version = "0.1.0"
description = "My first neops function blocks"
requires-python = ">=3.12"

dependencies = [
    "neops_worker_sdk",
]

[project.optional-dependencies]
test = [
    "pytest>=8.4.1",
    "pytest-asyncio>=1.1.0",
    "neops-remote-lab>=1.3.0",
]

[tool.ruff]
line-length = 120
target-version = "py312"

[tool.ruff.lint]
select = ["E", "F", "W", "I", "UP", "B"]

[tool.pytest.ini_options]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"

This gives you:

  • The neops_worker_sdk runtime dependency
  • Test dependencies (pytest, pytest-asyncio, neops-remote-lab) as optional extras
  • Sensible ruff and pytest configuration

Install everything, including test dependencies:

uv sync --all-extras

Tooling

Linting & Formatting — Ruff

We use Ruff for linting and formatting. It is a single, extremely fast tool (written in Rust) that replaces flake8, isort, and Black. The pyproject.toml above already includes a good baseline configuration.

Add Ruff as a development dependency so every contributor uses the same version:

uv add --dev ruff

Then run it from your project:

uv run ruff check .    # lint
uv run ruff format .   # format

Type Checking — Pyrefly

We use Pyrefly (by Meta) for type checking. It is a Rust-based type checker and language server that provides fast, accurate analysis for modern Python.

Add it as a development dependency:

uv add --dev pyrefly

Then run it:

uv run pyrefly check .
Why typed Python matters

neops function blocks rely heavily on type annotations. Here is why that matters:

  • Auto-completion — your editor understands parameter and return types
  • Early bug detection — type checkers catch mismatches before runtime
  • Schema generation — Pydantic models (used for parameters and results) derive JSON schemas from type hints automatically

Compare:

def run(params, device):
    ...

vs.

async def run(self, params: EchoParams, context: WorkflowContext) -> FunctionBlockResult[EchoResult]:
    ...

The typed version gives your editor, your tests, and the neops platform everything they need to validate your code before it ever touches a device.

IDE Recommendations

=== "VS Code"

**Recommended extensions:**

| Extension | ID | Purpose |
|---|---|---|
| Python | `ms-python.python` | Core Python support |
| Pyrefly | `meta.pyrefly` | Type checking, auto-completion, go-to-definition |
| Ruff | `charliermarsh.ruff` | Fast linting and formatting |

[Pyrefly](https://pyrefly.org/en/docs/IDE/) acts as a full language server —
it provides inline type errors, hover information, and code navigation out of
the box. It replaces Pylance for type analysis, so you only need one of the two.

**Recommended settings** (`.vscode/settings.json`):

```json
{
    "[python]": {
        "editor.defaultFormatter": "charliermarsh.ruff",
        "editor.formatOnSave": true
    }
}
```

!!! note "Alternative: Pylance"

    If you prefer Microsoft's [Pylance](https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance)
    (`ms-python.vscode-pylance`), it works well too — add
    `"python.analysis.typeCheckingMode": "basic"` to your settings.
    Pyrefly disables Pylance by default when both are installed; keep only one
    active to avoid duplicate diagnostics.

=== "PyCharm"

PyCharm has excellent built-in Python support — type checking, refactoring, and
debugging work out of the box with fewer plugins.

**Recommended plugins:**

| Plugin | Purpose |
|---|---|
| Pydantic | Enhanced support for Pydantic models (auto-completion, validation) |

**Configuration tips:**

- **Ruff integration:** Go to *Settings > Tools > External Tools*, add a new
  tool with program `uv`, arguments `run ruff check $FilePath$`, and working
  directory `$ProjectFileDir$`. Add a second entry with arguments
  `run ruff format $FilePath$` for formatting. Assign keyboard shortcuts or
  configure file watchers to run on save.
- **Pyrefly for type checking:** Add an external tool with program `uv`,
  arguments `run pyrefly check $FilePath$`, and working directory
  `$ProjectFileDir$`. This gives you the same type analysis as CI, beyond
  PyCharm's built-in inspector.

Environment Configuration

neops workers discover the platform and function blocks through environment variables. Create a .env file in your project root:

URL_BLACKBOARD=https://your-neops-instance.example.com
DIR_FUNCTION_BLOCKS=./my_function_blocks
Variable Description
URL_BLACKBOARD URL of your neops instance's blackboard API
DIR_FUNCTION_BLOCKS Directory where the worker discovers your function block modules

Tip

During local development with the test framework, these variables are not required -- the test harness provides its own context.


Next: Write your first function block