Skip to content

Commit cc43bef

Browse files
authored
Merge pull request #71 from xcube-dev/pont-41-environment
Look for environment.yml automatically
2 parents 3d7e2ea + ad14534 commit cc43bef

File tree

3 files changed

+38
-14
lines changed

3 files changed

+38
-14
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* Improve documentation (#54, #55)
77
* Improve type annotations and checks (#68)
88
* Include Dockerfile in built images (#55)
9+
* Look for environment.yml automatically (#41)
910

1011
## Changes in 0.1.1
1112

test/test_core.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,35 @@
2929

3030
@patch("xcengine.core.ScriptCreator.__init__")
3131
@pytest.mark.parametrize("tag", [None, "bar"])
32-
@pytest.mark.parametrize("use_env", [False, True])
33-
def test_image_builder_init(init_mock, tmp_path, tag, use_env):
32+
@pytest.mark.parametrize("env_file_name", ["environment.yml", "foo.yaml", None])
33+
@pytest.mark.parametrize("use_env_file_param", [False, True])
34+
def test_image_builder_init(
35+
init_mock,
36+
tmp_path: pathlib.Path,
37+
tag: str | None,
38+
env_file_name: str | None,
39+
use_env_file_param: bool,
40+
):
3441
nb_path = tmp_path / "foo.ipynb"
3542
nb_path.touch()
36-
if use_env:
37-
environment = tmp_path / "environment.yml"
38-
environment.touch()
43+
if env_file_name is not None:
44+
environment_path = tmp_path / env_file_name
45+
environment_path.touch()
3946
else:
40-
environment = None
47+
environment_path = None
4148
build_path = tmp_path / "build"
4249
build_path.mkdir()
4350
init_mock.return_value = None
4451
ib = ImageBuilder(
4552
notebook=nb_path,
46-
environment=environment,
53+
environment=environment_path if use_env_file_param else None,
4754
build_dir=build_path,
4855
tag=tag,
4956
)
5057
assert ib.notebook == nb_path
5158
assert ib.build_dir == build_path
52-
assert ib.environment == environment
59+
expected_env = environment_path if (use_env_file_param or env_file_name == "environment.yml") else None
60+
assert ib.environment == expected_env
5361
if tag is None:
5462
assert abs(
5563
datetime.datetime.now(datetime.UTC)
@@ -97,12 +105,13 @@ def test_runner_init_with_image():
97105
)
98106
assert runner.image == image
99107

108+
100109
@pytest.mark.parametrize("keep", [False, True])
101110
def test_runner_run_keep(keep: bool):
102111
runner = xcengine.core.ContainerRunner(
103112
image := Mock(docker.models.images.Image),
104113
None,
105-
client := Mock(DockerClient)
114+
client := Mock(DockerClient),
106115
)
107116
image.tags = []
108117
client.containers.run.return_value = (container := MagicMock(Container))
@@ -118,26 +127,32 @@ def test_runner_sigint():
118127
runner = xcengine.core.ContainerRunner(
119128
image := Mock(docker.models.images.Image),
120129
None,
121-
client := Mock(DockerClient)
130+
client := Mock(DockerClient),
122131
)
123132
image.tags = []
124133
client.containers.run.return_value = (container := Mock(Container))
125134
container.status = "running"
135+
126136
def container_stop():
127137
container.status = "stopped"
138+
128139
container.stop = container_stop
129140
pid = os.getpid()
130141

131142
old_alarm_handler = signal.getsignal(signal.SIGALRM)
143+
132144
class AlarmException(Exception):
133145
pass
146+
134147
def alarm_handler(signum, frame):
135148
raise AlarmException()
149+
136150
signal.signal(signal.SIGALRM, alarm_handler)
137151

138152
def interrupt_process():
139153
time.sleep(1) # allow one second for runner to start
140154
os.kill(pid, signal.SIGINT)
155+
141156
thread = threading.Thread(target=interrupt_process, daemon=True)
142157
thread.start()
143158

xcengine/core.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ class ImageBuilder:
179179
"""
180180

181181
tag_format: ClassVar[str] = "%Y.%m.%d.%H.%M.%S"
182+
environment: pathlib.Path | None = None
182183

183184
def __init__(
184185
self,
@@ -206,7 +207,9 @@ def __init__(
206207
else:
207208
self.tag = tag
208209

209-
if environment is None:
210+
if environment is not None:
211+
self.environment = environment
212+
else:
210213
LOGGER.info(
211214
"Looking for environment file configuration in the notebook."
212215
)
@@ -218,9 +221,14 @@ def __init__(
218221
self.environment = notebook.parent / nb_env
219222
else:
220223
LOGGER.info(f"No environment specified in notebook.")
221-
self.environment = None
222-
else:
223-
self.environment = environment
224+
LOGGER.info(f"Looking for a file named \"environment.yml\".")
225+
notebook_sibling = notebook.parent / "environment.yml"
226+
if notebook_sibling.is_file():
227+
self.environment = notebook_sibling
228+
LOGGER.info(f"Using environment from {notebook_sibling}")
229+
else:
230+
LOGGER.info(f"No environment found at {notebook_sibling}")
231+
self.environment = None
224232

225233
def build(self) -> Image:
226234
self.script_creator.convert_notebook_to_script(self.build_dir)

0 commit comments

Comments
 (0)