---
title: How to use Deephaven in a local development environment (Python)
sidebar_label: Local Query Development
---

## Set up your environment

This guide shows you how to create a Python project that uses Deephaven libraries for local development and testing. Because `deephaven_enterprise.database` resolves Java classes at import time, you must configure both Python packages and the Java classpath.

### Prerequisites

- **Python 3.10+**: Required by the Core+ worker package.
- **Java 17+**: Set `JAVA_HOME` to your Java installation.
- **Maven**: Used to download Core+ JARs.
- **Deephaven repository credentials**: Required for downloading Core+ artifacts.

### Install Python packages

Create a virtual environment and install the required packages:

```bash
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate
```

Copy the requirements file and worker wheel from your Deephaven server, then install version-matched dependencies:

```bash
# Copy requirements.txt and worker wheel from server
scp your-server:/usr/illumon/coreplus/latest/py/resources/requirements.txt .
scp your-server:/usr/illumon/coreplus/latest/py/wheel/deephaven_coreplus_worker-VAR::COREPLUS_DHE_VERSION-py3-none-any.whl .

# Install version-matched dependencies, then the worker wheel
pip install -r requirements.txt
pip install deephaven_coreplus_worker-VAR::COREPLUS_DHE_VERSION-py3-none-any.whl
pip install pytest
```

> [!IMPORTANT]
> The `requirements.txt` from the server specifies the `deephaven-core` version tested with your Core+ installation. Installing an unpinned `deephaven-core` from PyPI can leave the Python wrapper and Java JARs on incompatible versions.

### Download Core+ JARs

The `deephaven_enterprise.database` module requires Core+ Java classes. Use Maven to download the JARs from the Deephaven repository.

First, add your Deephaven credentials to `~/.m2/settings.xml`:

```xml
<settings>
  <servers>
    <server>
      <id>deephaven</id>
      <username>YOUR_USERNAME</username>
      <password>YOUR_PASSWORD</password>
    </server>
  </servers>
</settings>
```

Then create a `pom.xml` to download dependencies:

```xml
<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>local</groupId>
  <artifactId>deephaven-local-dev</artifactId>
  <version>1.0</version>
  <dependencies>
    <dependency>
      <groupId>io.deephaven.coreplus</groupId>
      <artifactId>Database</artifactId>
      <version>VAR::COREPLUS_DHC_VERSION-VAR::COREPLUS_DHE_VERSION</version>
    </dependency>
    <dependency>
      <groupId>io.deephaven</groupId>
      <artifactId>deephaven-engine-test-utils</artifactId>
      <version>VAR::COREPLUS_DHC_VERSION</version>
    </dependency>
  </dependencies>
  <repositories>
    <repository>
      <id>deephaven</id>
      <url>https://repo.deephaven.io/maven/release</url>
    </repository>
    <repository>
      <id>confluent</id>
      <url>https://packages.confluent.io/maven</url>
    </repository>
  </repositories>
</project>
```

Then run:

```bash
mvn dependency:copy-dependencies -DoutputDirectory=lib
```

### Initialize the JVM

Before importing any `deephaven_enterprise` modules, initialize the JVM with the Core+ classpath. Create a `conftest.py` file for pytest:

```python
# conftest.py

import atexit
import glob
import tempfile

# Must initialize JVM before importing deephaven modules
from deephaven_internal import jvm

# Collect all JARs
classpath = glob.glob("lib/*.jar")

# Create a temporary data directory and register cleanup
_data_dir = tempfile.TemporaryDirectory()
atexit.register(_data_dir.cleanup)

jvm.init_jvm(
    jvm_maxmem="2G",
    jvm_classpath=classpath,
    jvm_properties={
        "Configuration.rootFile": "dh-defaults.prop",
        "deephaven.dataDir": _data_dir.name,
    },
    jvm_options={
        "--add-opens=java.base/java.nio=ALL-UNNAMED",
        "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED",
        "--add-exports=java.management/sun.management=ALL-UNNAMED",
    },
)
```

> [!IMPORTANT]
> The JVM must be initialized before any `deephaven` or `deephaven_enterprise` imports. Place JVM initialization in `conftest.py` so it runs before test collection.

## Local unit testing

It may be helpful to have some local test data you can use to test your query's correctness. The steps below extract query logic into a testable function, then add pytest fixtures and tests for it.

The following query runs on a Deephaven worker and calculates the average and mid prices of stocks for a given day:

```python
# This code runs on a Deephaven worker, not locally
import query_utils

from deephaven_enterprise.database import db
from deephaven.time import dh_today

date = dh_today()

avg_stock_prices = query_utils.get_avg_stock_price_table(db, date)
mid_stock_prices = query_utils.get_mid_stock_price_table(db, date)
```

The query logic is extracted into helper functions in a separate module. These functions accept a `Database` parameter instead of importing `db` directly, making them testable with a mock:

```python
# query_utils.py

from typing import TYPE_CHECKING

from deephaven.table import Table

if TYPE_CHECKING:
    from deephaven_enterprise.database import Database


def get_avg_stock_price_table(db: "Database", date: str) -> Table:
    return (
        db.historical_table("LearnDeephaven", "StockTrades")
        .where(f"Date = `{date}`")
        .view(["USym", "Last"])
        .avg_by("USym")
    )


def get_mid_stock_price_table(db: "Database", date: str) -> Table:
    return (
        db.historical_table("LearnDeephaven", "StockQuotes")
        .where(f"Date = `{date}`")
        .last_by("USym")
        .update_view("Mid = (Bid + Ask) / 2")
    )
```

We need some test data. Create the following CSV files:

**tests/resources/StockTrades.csv** — Sample trade data for the average price test:

```text
Date,Timestamp,SecurityType,Exchange,USym,Sym,Last,Size,Source,ExchangeId,ExchangeTimestamp,SaleCondition
2017-08-25,2017-08-25T13:19:17.419592579-04:00,Stock,Arca,AAPL,AAPL,159.37,15,Normal,1045,2017-08-25T13:19:17.419592579-04:00,@FTI
2017-08-25,2017-08-25T13:19:17.419592579-04:00,Stock,Arca,AAPL,AAPL,159.48,85,Normal,1046,2017-08-25T13:19:17.419592579-04:00,@FTI
2017-08-25,2017-08-25T13:19:17.419592579-04:00,Stock,Arca,AAPL,AAPL,159.48,75,Normal,1047,2017-08-25T13:19:17.419592579-04:00,@TI
2017-08-25,2017-08-25T13:19:17.419592579-04:00,Stock,Arca,AAPL,AAPL,159.48,25,Normal,1048,2017-08-25T13:19:17.419592579-04:00,@FTI
2017-08-25,2017-08-25T13:19:17.419592579-04:00,Stock,Nasdaq,AAPL,AAPL,159.55,70,Normal,1055,2017-08-25T13:19:17.419592579-04:00,@TI
2017-08-25,2017-08-25T13:19:17.419592579-04:00,Stock,Arca,GOOG,GOOG,922.6,1,Normal,1406,2017-08-25T13:19:17.419592579-04:00,@TI
2017-08-25,2017-08-25T13:19:17.419592579-04:00,Stock,Arca,GOOG,GOOG,924.7,1,Normal,1410,2017-08-25T13:19:17.419592579-04:00,@TI
2017-08-25,2017-08-25T13:19:17.419592579-04:00,Stock,Nasdaq,GOOG,GOOG,924.7,1,Normal,1418,2017-08-25T13:19:17.419592579-04:00,@TI
2017-08-25,2017-08-25T13:19:17.419592579-04:00,Stock,Nasdaq,GOOG,GOOG,922.9,11,Normal,1420,2017-08-25T13:19:17.419592579-04:00,@FTI
2017-08-25,2017-08-25T13:19:17.419592579-04:00,Stock,Nasdaq,GOOG,GOOG,922.9,8,Normal,1421,2017-08-25T13:19:17.419592579-04:00,@FTI
```

**tests/resources/StockQuotes.csv** — Sample quote data for the mid price test:

```text
Date,Timestamp,USym,Bid,Ask
2017-08-25,2017-08-25T13:19:17.419592579-04:00,AAPL,159.40,159.50
2017-08-25,2017-08-25T13:19:18.419592579-04:00,AAPL,159.42,159.52
2017-08-25,2017-08-25T13:19:17.419592579-04:00,GOOG,922.50,923.00
2017-08-25,2017-08-25T13:19:18.419592579-04:00,GOOG,922.60,923.10
2017-08-25,2017-08-25T13:19:17.419592579-04:00,PFE,33.25,33.32
2017-08-25,2017-08-25T13:19:18.419592579-04:00,PFE,33.27,33.30
```

1. Add a fixture to `conftest.py` for the execution context. Merely importing `deephaven` does not create a working execution context — the default context is only wired up once a Deephaven script session has started, which does not happen here. Instead, build one directly from the same `TestExecutionContext` the Groovy guide uses, which is self-contained and does not require a running server. The execution context is required for table operations like [`where`](/core/docs/reference/table-operations/filter/where) and [`update_view`](/core/docs/reference/table-operations/select/update-view).

```python
# conftest.py (add to the file created earlier)

import pytest
import jpy

from deephaven.execution_context import ExecutionContext

_JTestExecutionContext = jpy.get_type(
    "io.deephaven.engine.context.TestExecutionContext"
)
_JControlledUpdateGraph = jpy.get_type(
    "io.deephaven.engine.testutil.ControlledUpdateGraph"
)


@pytest.fixture(scope="module")
def execution_context():
    j_exec_ctx = _JTestExecutionContext.createForUnitTests()
    # getUpdateGraph() returns the UpdateGraph interface; cast to ControlledUpdateGraph for the two methods below.
    j_update_graph = jpy.cast(j_exec_ctx.getUpdateGraph(), _JControlledUpdateGraph)
    j_update_graph.enableUnitTestMode()
    j_update_graph.resetForUnitTests(False)

    ctx = ExecutionContext(j_exec_ctx=j_exec_ctx)
    with ctx:
        yield ctx
```

2. Add a fixture for a mocked `Database`. Mocking the `Database` to read test data from CSV files is easier than creating a real `Database` instance.

```python
# conftest.py (continued)

from unittest.mock import MagicMock

from deephaven.csv import read as read_csv


@pytest.fixture(scope="module")
def mock_db(execution_context):
    db = MagicMock()

    # Load test data
    stock_trades = read_csv("tests/resources/StockTrades.csv")
    stock_quotes = read_csv("tests/resources/StockQuotes.csv")

    # Configure mock to return test data
    db.historical_table.side_effect = lambda namespace, table_name: {
        ("LearnDeephaven", "StockTrades"): stock_trades,
        ("LearnDeephaven", "StockQuotes"): stock_quotes,
    }.get((namespace, table_name))

    return db
```

3. Use the methods described in the Core [Extract table values guide](/core/docs/how-to-guides/extract-table-value/) to test your queries:

```python
import query_utils

TEST_DATE = "2017-08-25"


def test_avg_price_query(mock_db):
    result = query_utils.get_avg_stock_price_table(mock_db, TEST_DATE)

    assert result.size > 0

    aapl_result = result.where("USym = `AAPL`")
    aapl_avg = get_first_double_from_column(aapl_result, "Last")
    assert abs(aapl_avg - 159.472) < 0.0002

    goog_result = result.where("USym = `GOOG`")
    goog_avg = get_first_double_from_column(goog_result, "Last")
    assert abs(goog_avg - 923.56) < 0.0002


def test_mid_price_query(mock_db):
    result = query_utils.get_mid_stock_price_table(mock_db, TEST_DATE)

    assert result.size > 0

    pfe_result = result.where("USym = `PFE`")
    pfe_mid = get_first_double_from_column(pfe_result, "Mid")
    assert pfe_mid == 33.285


def get_first_double_from_column(table, column: str) -> float:
    # To extract data from a specific cell in a Table,
    # we first need the row key for that row position.
    # Row 0 does not necessarily have row key 0.
    row_key = table.j_table.getRowSet().get(0)
    # Use the ColumnSource to get the value of the column for that row key
    return table.j_table.getColumnSource(column).getDouble(row_key)
```

<details>
<summary>The full `conftest.py`:</summary>

```python
# tests/conftest.py

import atexit
import glob
import tempfile

import pytest
from unittest.mock import MagicMock

# Initialize JVM before importing deephaven modules
from deephaven_internal import jvm

# Collect all JARs
classpath = glob.glob("lib/*.jar")

# Create a temporary data directory and register cleanup
_data_dir = tempfile.TemporaryDirectory()
atexit.register(_data_dir.cleanup)

jvm.init_jvm(
    jvm_maxmem="2G",
    jvm_classpath=classpath,
    jvm_properties={
        "Configuration.rootFile": "dh-defaults.prop",
        "deephaven.dataDir": _data_dir.name,
    },
    jvm_options={
        "--add-opens=java.base/java.nio=ALL-UNNAMED",
        "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED",
        "--add-exports=java.management/sun.management=ALL-UNNAMED",
    },
)

# Now safe to import deephaven modules
import jpy
from deephaven.execution_context import ExecutionContext
from deephaven.csv import read as read_csv

_JTestExecutionContext = jpy.get_type(
    "io.deephaven.engine.context.TestExecutionContext"
)
_JControlledUpdateGraph = jpy.get_type(
    "io.deephaven.engine.testutil.ControlledUpdateGraph"
)


@pytest.fixture(scope="module")
def execution_context():
    j_exec_ctx = _JTestExecutionContext.createForUnitTests()
    # getUpdateGraph() returns the UpdateGraph interface; cast to ControlledUpdateGraph for the two methods below.
    j_update_graph = jpy.cast(j_exec_ctx.getUpdateGraph(), _JControlledUpdateGraph)
    j_update_graph.enableUnitTestMode()
    j_update_graph.resetForUnitTests(False)

    ctx = ExecutionContext(j_exec_ctx=j_exec_ctx)
    with ctx:
        yield ctx


@pytest.fixture(scope="module")
def mock_db(execution_context):
    db = MagicMock()

    # Load test data
    stock_trades = read_csv("tests/resources/StockTrades.csv")
    stock_quotes = read_csv("tests/resources/StockQuotes.csv")

    # Configure mock to return test data
    db.historical_table.side_effect = lambda namespace, table_name: {
        ("LearnDeephaven", "StockTrades"): stock_trades,
        ("LearnDeephaven", "StockQuotes"): stock_quotes,
    }.get((namespace, table_name))

    return db
```

</details>

<details>
<summary>The full test file:</summary>

```python
# tests/test_query_utils.py

import query_utils


TEST_DATE = "2017-08-25"


def test_avg_price_query(mock_db):
    result = query_utils.get_avg_stock_price_table(mock_db, TEST_DATE)

    assert result.size > 0

    aapl_result = result.where("USym = `AAPL`")
    aapl_avg = get_first_double_from_column(aapl_result, "Last")
    assert abs(aapl_avg - 159.472) < 0.0002

    goog_result = result.where("USym = `GOOG`")
    goog_avg = get_first_double_from_column(goog_result, "Last")
    assert abs(goog_avg - 923.56) < 0.0002


def test_mid_price_query(mock_db):
    result = query_utils.get_mid_stock_price_table(mock_db, TEST_DATE)

    assert result.size > 0

    pfe_result = result.where("USym = `PFE`")
    pfe_mid = get_first_double_from_column(pfe_result, "Mid")
    assert pfe_mid == 33.285


def get_first_double_from_column(table, column: str) -> float:
    # To extract data from a specific cell in a Table,
    # we first need the row key for that row position.
    # Row 0 does not necessarily have row key 0.
    row_key = table.j_table.getRowSet().get(0)
    # Use the ColumnSource to get the value of the column for that row key
    return table.j_table.getColumnSource(column).getDouble(row_key)
```

</details>

## Connect to a remote DB

The steps above cover testing query logic locally, without a running Deephaven server. Client applications can also connect to Deephaven server installations to run queries on a remote database. See the [Core+ Python Client](../../clients/python/coreplus-python-client.md) for more information.

## Related documentation

- [Execution context](/core/docs/conceptual/execution-context/)
- [Extract table values](/core/docs/how-to-guides/extract-table-value/)
- [`Database` class](https://docs.deephaven.io/pycoreplus/2026.01/worker/autoapi/deephaven_enterprise/database/index.html#deephaven_enterprise.database.Database)
