From e656c23ffc75894207f80e3351e52c3c37ec3160 Mon Sep 17 00:00:00 2001 From: margaretkennedy Date: Sat, 26 Sep 2026 08:04:14 -0400 Subject: [PATCH] Address remaining README review feedback - Fix duplicated `server = server =` assignment in root README snippets - Rename my_dh_cli entry function `app` to `main` to match my_dh_toolkit - Explain deferred deephaven imports in my_dh_cli (code comment + README) - Split the long my_dh_toolkit __init__.py explanation into short bullets - Remove redundant toolkit summary under the pattern table - Note that toolkit's queries.py/utils.py are deliberate copies Co-Authored-By: Claude Opus 5.5 --- README.md | 23 +++++++++++++---------- my_dh_cli/README.md | 2 +- my_dh_cli/pyproject.toml | 2 +- my_dh_cli/src/my_dh_cli/__main__.py | 4 ++-- my_dh_cli/src/my_dh_cli/cli.py | 6 ++++-- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 43faace..02a4033 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,6 @@ This repository shows how to package Python code that uses [Deephaven Community | [`my_dh_cli/`](my_dh_cli/) | Command line tool only | A `my-dh-query` terminal command | | [`my_dh_toolkit/`](my_dh_toolkit/) | Library and command line tools combined | Importable functions plus `my-dh-toolkit-query` and `my-dh-toolkit-process` commands | -`my_dh_toolkit` is the other two patterns merged into a single package: its library modules play the same role as `my_dh_library`, and its commands play the same role as `my_dh_cli`. Its commands also call its own library functions, so the same code is reachable from Python and from the terminal. - All three examples follow the [Python Packaging User Guide](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/) conventions: a `pyproject.toml` file for metadata, dependencies, and entry points, and the src-layout for source code. This repository accompanies the [Packaging custom code and dependencies](https://deephaven.io/core/docs/how-to-guides/sysadmin/setuptools-deployment/) guide, which explains the underlying concepts in depth. ## Choose an example @@ -72,7 +70,7 @@ A library that uses Deephaven needs a running server in the same process, so sta ```python # A Deephaven server must be running before deephaven modules are imported. from deephaven_server import Server -server = server = Server(port=10000, jvm_args=["-Xmx4g"]) +server = Server(port=10000, jvm_args=["-Xmx4g"]) server.start() # Import and use the installed library. @@ -109,7 +107,7 @@ The command comes from one line in `pyproject.toml`: ```toml [project.scripts] -my-dh-query = "my_dh_cli.cli:app" +my-dh-query = "my_dh_cli.cli:main" ``` ### Try it @@ -126,13 +124,15 @@ The command starts its own Deephaven server, reads the CSV file, adds a computed ### What to study - [`pyproject.toml`](my_dh_cli/pyproject.toml): the `[project.scripts]` section maps the command name to a function. -- [`cli.py`](my_dh_cli/src/my_dh_cli/cli.py): a [Click](https://click.palletsprojects.com/) command that starts the Deephaven server itself, so it works as a standalone tool. +- [`cli.py`](my_dh_cli/src/my_dh_cli/cli.py): a [Click](https://click.palletsprojects.com/) command that starts the Deephaven server itself, so it works as a standalone tool. It imports `deephaven` inside the function that runs after the server has started, not at the top of the module, because `deephaven` modules can't be imported until a server is running. - [`__main__.py`](my_dh_cli/src/my_dh_cli/__main__.py): allows `python -m my_dh_cli data/sample.csv` as an alternative during development. ## Example 3: `my_dh_toolkit` — a library and command line tools in one package In this example, one package provides both interfaces. Python users import its query functions, just as in `my_dh_library`; terminal users run its installed commands, just as in `my_dh_cli`. The commands call the package's own library functions, so there is one implementation behind both interfaces. +`queries.py` and `utils.py` are copies of the `my_dh_library` modules rather than a dependency on that package. The duplication is deliberate: it keeps each example self-contained, so you can copy any one of them on its own. + ``` my_dh_toolkit/ ├── src/ @@ -166,7 +166,7 @@ The same installation also provides the library. In a Python session, start a De ```python # A Deephaven server must be running before deephaven modules are imported. from deephaven_server import Server -server = server = Server(port=10000, jvm_args=["-Xmx4g"]) +server = Server(port=10000, jvm_args=["-Xmx4g"]) server.start() # Import and use the installed library. @@ -181,8 +181,11 @@ print(f"{filtered.size} of {data.size} rows have Score > 75") ### What to study - [`pyproject.toml`](my_dh_toolkit/pyproject.toml): a single `[project.scripts]` section defines both commands. -- [`__init__.py`](my_dh_toolkit/src/my_dh_toolkit/__init__.py): contains no imports, and that is deliberate. Importing any `deephaven` module fails unless a Deephaven server is already running in the process. When a command such as `my-dh-toolkit-query` starts, Python imports the `my_dh_toolkit` package before the command has started its server. If `__init__.py` imported the query functions, that import chain would reach `deephaven` and every command would fail at startup. Keeping `__init__.py` empty and importing the library from its submodules (`my_dh_toolkit.queries`, `my_dh_toolkit.utils`) avoids the problem. `my_dh_library` can safely re-export its functions from `__init__.py` because it has no commands: it is only ever imported after a server is running. -- [`query.py`](my_dh_toolkit/src/my_dh_toolkit/query.py) and [`process.py`](my_dh_toolkit/src/my_dh_toolkit/process.py): one module per command, each defining the command's `main()`. Both import `my_dh_toolkit.queries` and `my_dh_toolkit.utils` *inside* the function that runs after the server has started — that is how a command module can reuse library code that depends on `deephaven`. +- [`__init__.py`](my_dh_toolkit/src/my_dh_toolkit/__init__.py): deliberately contains no imports. + - Importing any `deephaven` module fails unless a Deephaven server is already running in the process. + - When a command such as `my-dh-toolkit-query` starts, Python imports the `my_dh_toolkit` package before the command has started its server. If `__init__.py` imported the query functions, the import would reach `deephaven` and every command would fail at startup. + - For this reason, Python users import the library from its submodules (`my_dh_toolkit.queries`, `my_dh_toolkit.utils`). `my_dh_library` can re-export its functions from `__init__.py` because it has no commands, so it is only imported after a server is running. +- [`query.py`](my_dh_toolkit/src/my_dh_toolkit/query.py) and [`process.py`](my_dh_toolkit/src/my_dh_toolkit/process.py): one module per command, each defining the command's `main()`. Like `my_dh_cli`, both import `deephaven` and the library modules inside the function that runs after the server has started. ## Adapt an example for your own project @@ -208,10 +211,10 @@ Each example is a template. To turn one into your own package: name = "my_tool" [project.scripts] - my-tool = "my_tool.cli:app" + my-tool = "my_tool.cli:main" ``` -4. **Update internal imports** to the new package name (for example, `from my_tool.cli import app` in `__main__.py`). +4. **Update internal imports** to the new package name (for example, `from my_tool.cli import main` in `__main__.py`). 5. **Replace the example logic** with your own code, and add any packages it needs to `dependencies` in `pyproject.toml`. Keep `deephaven-server` in the list so it installs automatically. diff --git a/my_dh_cli/README.md b/my_dh_cli/README.md index ee1b8e4..391256e 100644 --- a/my_dh_cli/README.md +++ b/my_dh_cli/README.md @@ -6,7 +6,7 @@ The command is defined by the `[project.scripts]` entry point in [`pyproject.tom ```toml [project.scripts] -my-dh-query = "my_dh_cli.cli:app" +my-dh-query = "my_dh_cli.cli:main" ``` ## Installation diff --git a/my_dh_cli/pyproject.toml b/my_dh_cli/pyproject.toml index 7890660..0674c7f 100644 --- a/my_dh_cli/pyproject.toml +++ b/my_dh_cli/pyproject.toml @@ -16,7 +16,7 @@ dependencies = [ ] [project.scripts] -my-dh-query = "my_dh_cli.cli:app" +my-dh-query = "my_dh_cli.cli:main" [tool.setuptools.packages.find] where = ["src"] diff --git a/my_dh_cli/src/my_dh_cli/__main__.py b/my_dh_cli/src/my_dh_cli/__main__.py index ff7364e..2bf95cd 100644 --- a/my_dh_cli/src/my_dh_cli/__main__.py +++ b/my_dh_cli/src/my_dh_cli/__main__.py @@ -1,4 +1,4 @@ -from my_dh_cli.cli import app +from my_dh_cli.cli import main if __name__ == "__main__": - app() + main() diff --git a/my_dh_cli/src/my_dh_cli/cli.py b/my_dh_cli/src/my_dh_cli/cli.py index 55b6be4..6232a0a 100644 --- a/my_dh_cli/src/my_dh_cli/cli.py +++ b/my_dh_cli/src/my_dh_cli/cli.py @@ -3,6 +3,8 @@ def my_dh_query(input_file: str, verbose: bool = False): """Read a CSV file and perform a simple query operation on the data.""" + # Imported here, not at module level: deephaven requires a running server. + # The entry point starts the server first, then calls this function. from deephaven import read_csv from pathlib import Path @@ -39,7 +41,7 @@ def my_dh_query(input_file: str, verbose: bool = False): @click.command() @click.argument("input_file", type=click.Path(exists=True)) @click.option("--verbose", "-v", is_flag=True, help="Enable verbose output") -def app(input_file: str, verbose: bool) -> None: +def main(input_file: str, verbose: bool) -> None: """Process data with Deephaven.""" from deephaven_server import Server @@ -51,4 +53,4 @@ def app(input_file: str, verbose: bool) -> None: if __name__ == "__main__": - app() + main()