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
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* Allow `xcengine image run` to pass arguments to the container (#57, #77)
* Add option for `xcengine image run` to open a browser window (#58)
* Add option to skip image build and just create Dockerfile and context (#60)
* Refactor code and improve test coverage (#40)

## Changes in 0.1.1

Expand Down
2 changes: 1 addition & 1 deletion docs/running-eoaps.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ to provide a path to a shell as a command, since this path will just be passed
as a parameter to the xcengine runner script. You need to set the entry point
as well, like this:

`docker --rm -it --entrypoint /usr/local/bin/_entrypoint.sh myimage:1 bash`
`docker run --rm -it --entrypoint /usr/local/bin/_entrypoint.sh myimage:1 bash`

This resets the entry point to the usual micromamba-docker entry point, which
sets up the Python environment, then runs bash within that environment.
Expand Down
9 changes: 9 additions & 0 deletions test/data/parameters.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
foo:
default: 10
type: int
bar:
default: 20
type: int
baz:
default: 30
type: int
40 changes: 39 additions & 1 deletion test/test_util.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
import logging
import os
import sys
from collections import namedtuple
from pathlib import Path
from unittest import mock
from unittest.mock import patch

import pystac
import pytest
import xarray as xr
from xcube.server.webservers.tornado import TornadoFramework

from xcengine.util import clear_directory, write_stac, save_datasets
from xcengine.util import (
clear_directory,
write_stac,
save_datasets,
)
import xcengine.util


@pytest.fixture
Expand Down Expand Up @@ -86,3 +97,30 @@ def outdir(ds_id):
assert catalogue_path.is_file()
else:
assert not catalogue_path.exists()


def test_start_server():
import xcube.core.new

framework_patch = mock.MagicMock()
framework_patch.get_framework_class.return_value = TornadoFramework
server_object_patch = mock.MagicMock()
server_module_patch = mock.MagicMock()
server_module_patch.Server.return_value = server_object_patch
with patch.dict(
sys.modules,
{
"xcube.server.framework": framework_patch,
"xcube.server.server": server_module_patch,
},
):
xcengine.util.start_server(
{"ds1": xcube.core.new.new_cube()},
{},
namedtuple("Args", "batch from_saved xcube_viewer_api_url")(
False, False, "http://localhost:8000/"
),
logging.getLogger(),
)

server_object_patch.start.assert_called_once()
34 changes: 31 additions & 3 deletions test/test_wrapper.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import os
import pathlib
from unittest.mock import patch, Mock
import pytest
import xcengine


@pytest.mark.parametrize("cli_args", [["--verbose"], ["--batch"]])
@pytest.mark.parametrize(
"cli_args", [["--verbose"], ["--batch"], ["--server"]]
)
def test_wrapper(tmp_path, monkeypatch, cli_args):
import xcengine

with patch("sys.argv", ["wrapper.py"] + cli_args):
for path in xcengine.__path__:
Expand All @@ -15,9 +18,34 @@ def test_wrapper(tmp_path, monkeypatch, cli_args):
os.environ["XC_USER_CODE_PATH"] = str(user_code_path)
from xcengine import wrapper

with patch("util.save_datasets", save_datasets_mock := Mock()):
with (
patch("util.save_datasets", save_datasets_mock := Mock()),
patch("util.start_server", start_server_mock := Mock()),
):
xcengine.wrapper.main()

assert save_datasets_mock.call_count == (
1 if "--batch" in cli_args else 0
)

assert start_server_mock.call_count == (
1 if "--server" in cli_args else 0
)


@patch("sys.argv", ["wrapper.py", "--bar", "17"])
@patch.dict(os.environ, {"xce_baz": "42"})
def test_xce_set_params(tmp_path, monkeypatch):
for path in xcengine.__path__:
monkeypatch.syspath_prepend(path)
(user_code_path := tmp_path / "user_code.py").touch()
os.environ["XC_USER_CODE_PATH"] = str(user_code_path)
from xcengine import wrapper

wrapper.__xce_set_params(pathlib.Path(__file__).parent / "data/dummy")
# NB: __xce_set_params doesn't explicitly set default values from
# the parameter configuration (like "foo" here), since the user code
# will set these anyway.
assert "foo" not in wrapper.__dict__
assert wrapper.__dict__["bar"] == 17
assert wrapper.__dict__["baz"] == 42
47 changes: 45 additions & 2 deletions xcengine/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
# Permissions are hereby granted under the terms of the MIT License:
# https://opensource.org/licenses/MIT.

from collections import namedtuple
from datetime import datetime
import json
import pathlib
import shutil
from typing import NamedTuple, Any, Mapping
from typing import NamedTuple, Mapping

import pystac
import xarray as xr
Expand Down Expand Up @@ -122,3 +122,46 @@ def save_datasets(
if eoap_mode:
write_stac(datasets, output_path)
return saved_datasets


def start_server(datasets, saved_datasets, args, logger):
import xcube.util.plugin
import xcube.webapi.viewer
from xcube.server.server import Server
from xcube.server.framework import get_framework_class

xcube.util.plugin.init_plugins()
server = Server(framework=get_framework_class("tornado")(), config={})
dataset_context = server.ctx.get_api_ctx("datasets")
for name in datasets:
dataset = (
xr.open_zarr(saved_datasets[name])
if args.batch and args.from_saved
else datasets[name]
)
dataset_context.add_dataset(dataset, name, style="bar")
logger.info("Added " + name)
logo_data = (
pathlib.Path(xcube.webapi.viewer.__file__).parent
/ "dist"
/ "images"
/ "logo.png"
).read_bytes()

viewer_context = server.ctx.get_api_ctx("viewer")
viewer_context.config_items = {
"config.json": json.dumps(
{
"server": {"url": args.xcube_viewer_api_url},
"branding": {
# "layerVisibilities": {
# # Set the default basemap.
# "baseMaps.CartoDB.Dark Matter": True
# }
},
}
),
"images/logo.png": logo_data,
}
logger.info(f"Starting server on port {server.ctx.config['port']}...")
server.start()
43 changes: 3 additions & 40 deletions xcengine/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
# https://opensource.org/licenses/MIT.


import json
import logging
import os
import pathlib
Expand All @@ -20,9 +19,9 @@
logging.basicConfig(level=logging.INFO)


def __xce_set_params():
def __xce_set_params(config_locator: str | pathlib.Path = sys.argv[0]):
params = parameters.NotebookParameters.from_yaml_file(
pathlib.Path(sys.argv[0]).parent / "parameters.yaml"
pathlib.Path(config_locator).parent / "parameters.yaml"
)
globals().update(params.read_params_combined(sys.argv))

Expand All @@ -42,8 +41,6 @@ def __xce_set_params():
import pathlib

import xarray as xr
from xcube.server.server import Server
from xcube.server.framework import get_framework_class
import xcube.util.plugin
import xcube.core.new
import xcube.webapi.viewer
Expand Down Expand Up @@ -80,41 +77,7 @@ def main():
)

if args.server:
xcube.util.plugin.init_plugins()
server = Server(framework=get_framework_class("tornado")(), config={})
dataset_context = server.ctx.get_api_ctx("datasets")
for name in datasets:
dataset = (
xr.open_zarr(saved_datasets[name])
if args.batch and args.from_saved
else datasets[name]
)
dataset_context.add_dataset(dataset, name, style="bar")
LOGGER.info("Added " + name)
logo_data = (
pathlib.Path(xcube.webapi.viewer.__file__).parent
/ "dist"
/ "images"
/ "logo.png"
).read_bytes()

viewer_context = server.ctx.get_api_ctx("viewer")
viewer_context.config_items = {
"config.json": json.dumps(
{
"server": {"url": args.xcube_viewer_api_url},
"branding": {
# "layerVisibilities": {
# # Set the default basemap.
# "baseMaps.CartoDB.Dark Matter": True
# }
},
}
),
"images/logo.png": logo_data,
}
LOGGER.info(f"Starting server on port {server.ctx.config['port']}...")
server.start()
util.start_server(datasets, saved_datasets, args, LOGGER)


if __name__ == "__main__":
Expand Down