Skip to content

Commit 3eb9148

Browse files
authored
Merge pull request #218 from thodkatz/fix-eof
Proper test cleanup and resolve EOF Error
2 parents cdb2c4a + a0d5695 commit 3eb9148

7 files changed

Lines changed: 86 additions & 81 deletions

File tree

.circleci/config.yml

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ version: 2
22
jobs:
33
checkout_code:
44
docker:
5-
- image: condaforge/mambaforge
5+
- image: condaforge/miniforge3
66
working_directory: ~/repo
77
steps:
88
- checkout
@@ -18,41 +18,37 @@ jobs:
1818
TIKTORCH_ENV_NAME: tiktorch-server-env
1919
TIKTORCH_ENV_PREFIX: /opt/conda/envs/tiktorch-server-env
2020
docker:
21-
- image: condaforge/mambaforge
21+
- image: condaforge/miniforge3
2222
working_directory: ~/repo
2323
steps:
2424
- restore_cache:
2525
keys:
2626
- v1-repo-{{ .Environment.CIRCLE_SHA1 }}
2727
- restore_cache:
2828
keys:
29-
- v11-dependencies-{{ checksum "environment.yml" }}
30-
29+
- v11-dependencies-{{ .Environment.CIRCLE_SHA1 }}
3130
- run: conda config --set channel_priority strict
32-
- run: mamba update -n base -c conda-forge --update-all
33-
- run: mamba install -c conda-forge conda-build make boa
31+
- run: conda update -n base -c conda-forge --update-all
32+
- run: conda install -c conda-forge conda-build make boa
3433
- run: |
35-
if [ ! -d ${TIKTORCH_ENV_PREFIX} ]; then
3634
echo "Creating new environment ${TIKTORCH_ENV_NAME}"
3735
make devenv
38-
fi
39-
4036
- save_cache:
4137
paths:
4238
- /opt/conda/envs
43-
key: v11-dependencies-{{ checksum "environment.yml" }}
39+
key: v11-dependencies-{{ .Environment.CIRCLE_SHA1 }}
4440

4541
pre_commit_check:
4642
docker:
47-
- image: condaforge/mambaforge
43+
- image: condaforge/miniforge3
4844
working_directory: ~/repo
4945
steps:
5046
- restore_cache:
5147
keys:
5248
- v1-repo-{{ .Environment.CIRCLE_SHA1 }}
5349
- restore_cache:
5450
keys:
55-
- v11-dependencies-{{ checksum "environment.yml" }}
51+
- v11-dependencies-{{ .Environment.CIRCLE_SHA1 }}
5652

5753
- run:
5854
name: run pre-commit
@@ -63,15 +59,15 @@ jobs:
6359
6460
tests:
6561
docker:
66-
- image: condaforge/mambaforge
62+
- image: condaforge/miniforge3
6763
working_directory: ~/repo
6864
steps:
6965
- restore_cache:
7066
keys:
7167
- v1-repo-{{ .Environment.CIRCLE_SHA1 }}
7268
- restore_cache:
7369
keys:
74-
- v11-dependencies-{{ checksum "environment.yml" }}
70+
- v11-dependencies-{{ .Environment.CIRCLE_SHA1 }}
7571

7672
- run:
7773
name: run tests
@@ -83,15 +79,15 @@ jobs:
8379
8480
build_conda_packages:
8581
docker:
86-
- image: condaforge/mambaforge
82+
- image: condaforge/miniforge3
8783
working_directory: ~/repo
8884
steps:
8985
- restore_cache:
9086
keys:
9187
- v1-repo-{{ .Environment.CIRCLE_SHA1 }}
9288

93-
- run: mamba config --set channel_priority strict
94-
- run: mamba install -c conda-forge conda-build anaconda-client boa
89+
- run: conda config --set channel_priority strict
90+
- run: conda install -c conda-forge conda-build anaconda-client boa
9591
- run:
9692
name: build packages
9793
command: |

Makefile

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,7 @@
11
SHELL=/bin/bash
22
ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
33
TIKTORCH_ENV_NAME ?= tiktorch-server-env
4-
5-
sample_model:
6-
cd tests/data/dummy && zip -r $(ROOT_DIR)/dummy.tmodel ./*
7-
8-
unet2d:
9-
cd tests/data/unet2d && zip -r $(ROOT_DIR)/unet2d.tmodel ./*
10-
11-
unet2d_onnx:
12-
cd tests/data/unet2d_onnx && zip -r $(ROOT_DIR)/onnx.tmodel ./*
13-
14-
dummy_tf:
15-
cd tests/data/dummy_tensorflow && zip -r $(ROOT_DIR)/dummy_tf.tmodel ./*
4+
SUBMODULES = ./vendor/core-bioimage-io-python ./vendor/spec-bioimage-io
165

176
protos:
187
python -m grpc_tools.protoc -I./proto --python_out=tiktorch/proto/ --grpc_python_out=tiktorch/proto/ ./proto/*.proto
@@ -21,18 +10,23 @@ protos:
2110
version:
2211
python -c "import sys; print(sys.version)"
2312

24-
2513
devenv:
2614
. $$(conda info --base)/etc/profile.d/conda.sh
2715
mamba env create --file environment.yml --name $(TIKTORCH_ENV_NAME)
28-
conda run -n $(TIKTORCH_ENV_NAME) pip install . ./vendor/core-bioimage-io-python ./vendor/spec-bioimage-io
16+
make install_submodules
2917

3018
run_server:
3119
. $$(conda info --base)/etc/profile.d/conda.sh; conda activate $(TIKTORCH_ENV_NAME); python -m tiktorch.server
3220

21+
install_submodules:
22+
@echo "Installing submodules $(SUBMODULES)"
23+
@for package in $(SUBMODULES) ; do \
24+
echo $$package ; \
25+
conda run -n $(TIKTORCH_ENV_NAME) pip install -e $$package ; \
26+
done
3327

3428
remove_devenv:
3529
conda env remove --yes --name $(TIKTORCH_ENV_NAME)
3630

3731

38-
.PHONY: protos version sample_model devenv remove_devenv dummy_tf
32+
.PHONY: *

tests/test_server/test_grpc/test_inference_servicer.py

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ def grpc_servicer(data_store):
2727
return inference_servicer.InferenceServicer(TorchDevicePool(), SessionManager(), data_store)
2828

2929

30+
@pytest.fixture(autouse=True)
31+
def clean(grpc_servicer):
32+
yield
33+
grpc_servicer.close_all_sessions()
34+
35+
3036
@pytest.fixture(scope="module")
3137
def grpc_stub_cls(grpc_channel):
3238
return inference_pb2_grpc.InferenceStub
@@ -47,15 +53,13 @@ def method_requiring_session(self, request, grpc_stub):
4753
def test_model_session_creation(self, grpc_stub, bioimageio_model_bytes):
4854
model = grpc_stub.CreateModelSession(valid_model_request(bioimageio_model_bytes))
4955
assert model.id
50-
grpc_stub.CloseModelSession(model)
5156

5257
def test_model_session_creation_using_upload_id(self, grpc_stub, data_store, bioimageio_dummy_explicit_model_bytes):
5358
id_ = data_store.put(bioimageio_dummy_explicit_model_bytes.getvalue())
5459

5560
rq = inference_pb2.CreateModelSessionRequest(model_uri=f"upload://{id_}", deviceIds=["cpu"])
5661
model = grpc_stub.CreateModelSession(rq)
5762
assert model.id
58-
grpc_stub.CloseModelSession(model)
5963

6064
def test_model_session_creation_using_random_uri(self, grpc_stub):
6165
rq = inference_pb2.CreateModelSessionRequest(model_uri="randomSchema://", deviceIds=["cpu"])
@@ -92,36 +96,28 @@ def test_if_model_create_fails_devices_are_released(self, grpc_stub):
9296
model_blob=inference_pb2.Blob(content=b""), deviceIds=["cpu"]
9397
)
9498

95-
model = None
9699
with pytest.raises(Exception):
97-
model = grpc_stub.CreateModelSession(model_req)
100+
grpc_stub.CreateModelSession(model_req)
98101

99102
device_by_id = self._query_devices(grpc_stub)
100103
assert "cpu" in device_by_id
101104
assert inference_pb2.Device.Status.AVAILABLE == device_by_id["cpu"].status
102105

103-
if model:
104-
grpc_stub.CloseModelSession(model)
105-
106106
def test_use_device(self, grpc_stub, bioimageio_model_bytes):
107107
device_by_id = self._query_devices(grpc_stub)
108108
assert "cpu" in device_by_id
109109
assert inference_pb2.Device.Status.AVAILABLE == device_by_id["cpu"].status
110110

111-
model = grpc_stub.CreateModelSession(valid_model_request(bioimageio_model_bytes, device_ids=["cpu"]))
111+
grpc_stub.CreateModelSession(valid_model_request(bioimageio_model_bytes, device_ids=["cpu"]))
112112

113113
device_by_id = self._query_devices(grpc_stub)
114114
assert "cpu" in device_by_id
115115
assert inference_pb2.Device.Status.IN_USE == device_by_id["cpu"].status
116116

117-
grpc_stub.CloseModelSession(model)
118-
119117
def test_using_same_device_fails(self, grpc_stub, bioimageio_model_bytes):
120-
model = grpc_stub.CreateModelSession(valid_model_request(bioimageio_model_bytes, device_ids=["cpu"]))
118+
grpc_stub.CreateModelSession(valid_model_request(bioimageio_model_bytes, device_ids=["cpu"]))
121119
with pytest.raises(grpc.RpcError):
122-
model = grpc_stub.CreateModelSession(valid_model_request(bioimageio_model_bytes, device_ids=["cpu"]))
123-
124-
grpc_stub.CloseModelSession(model)
120+
grpc_stub.CreateModelSession(valid_model_request(bioimageio_model_bytes, device_ids=["cpu"]))
125121

126122
def test_closing_session_releases_devices(self, grpc_stub, bioimageio_model_bytes):
127123
model = grpc_stub.CreateModelSession(valid_model_request(bioimageio_model_bytes, device_ids=["cpu"]))
@@ -163,8 +159,6 @@ def test_call_predict_valid_explicit(self, grpc_stub, bioimageio_dummy_explicit_
163159
input_tensors = [converters.xarray_to_pb_tensor(input_tensor_id, arr)]
164160
res = grpc_stub.Predict(inference_pb2.PredictRequest(modelSessionId=model.id, tensors=input_tensors))
165161

166-
grpc_stub.CloseModelSession(model)
167-
168162
assert len(res.tensors) == 1
169163
assert res.tensors[0].tensorId == output_tensor_id
170164
assert_array_equal(expected, converters.pb_tensor_to_numpy(res.tensors[0]))
@@ -175,7 +169,6 @@ def test_call_predict_invalid_shape_explicit(self, grpc_stub, bioimageio_dummy_e
175169
input_tensors = [converters.xarray_to_pb_tensor("input", arr)]
176170
with pytest.raises(grpc.RpcError):
177171
grpc_stub.Predict(inference_pb2.PredictRequest(modelSessionId=model.id, tensors=input_tensors))
178-
grpc_stub.CloseModelSession(model)
179172

180173
@pytest.mark.parametrize(
181174
"shape",
@@ -187,7 +180,6 @@ def test_call_predict_invalid_shape_parameterized(self, grpc_stub, shape, bioima
187180
input_tensors = [converters.xarray_to_pb_tensor("param", arr)]
188181
with pytest.raises(grpc.RpcError):
189182
grpc_stub.Predict(inference_pb2.PredictRequest(modelSessionId=model.id, tensors=input_tensors))
190-
grpc_stub.CloseModelSession(model)
191183

192184
def test_call_predict_invalid_tensor_ids(self, grpc_stub, bioimageio_dummy_model):
193185
model_bytes, _ = bioimageio_dummy_model
@@ -197,7 +189,6 @@ def test_call_predict_invalid_tensor_ids(self, grpc_stub, bioimageio_dummy_model
197189
with pytest.raises(grpc.RpcError) as error:
198190
grpc_stub.Predict(inference_pb2.PredictRequest(modelSessionId=model.id, tensors=input_tensors))
199191
assert error.value.details().startswith("Exception calling application: Spec invalidTensorName doesn't exist")
200-
grpc_stub.CloseModelSession(model)
201192

202193
def test_call_predict_invalid_axes(self, grpc_stub, bioimageio_dummy_model):
203194
model_bytes, tensor_id = bioimageio_dummy_model
@@ -207,15 +198,13 @@ def test_call_predict_invalid_axes(self, grpc_stub, bioimageio_dummy_model):
207198
with pytest.raises(grpc.RpcError) as error:
208199
grpc_stub.Predict(inference_pb2.PredictRequest(modelSessionId=model.id, tensors=input_tensors))
209200
assert error.value.details().startswith("Exception calling application: Incompatible axes")
210-
grpc_stub.CloseModelSession(model)
211201

212202
@pytest.mark.parametrize("shape", [(1, 1, 64, 64), (1, 1, 66, 65), (1, 1, 68, 66), (1, 1, 70, 67)])
213203
def test_call_predict_valid_shape_parameterized(self, grpc_stub, shape, bioimageio_dummy_param_model_bytes):
214204
model = grpc_stub.CreateModelSession(valid_model_request(bioimageio_dummy_param_model_bytes))
215205
arr = xr.DataArray(np.arange(np.prod(shape)).reshape(*shape), dims=("b", "c", "x", "y"))
216206
input_tensors = [converters.xarray_to_pb_tensor("param", arr)]
217207
grpc_stub.Predict(inference_pb2.PredictRequest(modelSessionId=model.id, tensors=input_tensors))
218-
grpc_stub.CloseModelSession(model)
219208

220209
@pytest.mark.skip
221210
def test_call_predict_tf(self, grpc_stub, bioimageio_dummy_tensorflow_model_bytes):
@@ -227,8 +216,6 @@ def test_call_predict_tf(self, grpc_stub, bioimageio_dummy_tensorflow_model_byte
227216
input_tensors = [converters.xarray_to_pb_tensor(input_tensor_id, arr)]
228217
res = grpc_stub.Predict(inference_pb2.PredictRequest(modelSessionId=model.id, tensors=input_tensors))
229218

230-
grpc_stub.CloseModelSession(model)
231-
232219
assert len(res.tensors) == 1
233220
assert res.tensors[0].tensorId == output_tensor_id
234221
assert_array_equal(expected, converters.pb_tensor_to_numpy(res.tensors[0]))

tiktorch/rpc/mp.py

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,8 @@ def __init__(self, api, conn: Connection):
270270
self._logger = None
271271
self._conn = conn
272272
self._results_queue = queue.Queue()
273-
self._start_result_sender(conn)
273+
self._start_result_sender = threading.Thread(target=self._sender, name="MPResultSender")
274+
self._start_result_sender.start()
274275

275276
@property
276277
def logger(self):
@@ -281,20 +282,17 @@ def logger(self):
281282
def _send(self, msg):
282283
self._results_queue.put(msg)
283284

284-
def _start_result_sender(self, conn):
285-
def _sender():
286-
while True:
287-
try:
288-
result = self._results_queue.get()
289-
if result is self._sentinel:
290-
break
291-
292-
conn.send(result)
293-
except Exception:
294-
self.logger.exception("Error in result sender")
285+
def _sender(self):
286+
while True:
287+
try:
288+
result = self._results_queue.get()
289+
if result is self._sentinel:
290+
self.logger.debug("[MPServer] result sender thread stopped")
291+
break
295292

296-
t = threading.Thread(target=_sender, name="MPResultSender")
297-
t.start()
293+
self._conn.send(result)
294+
except Exception:
295+
self.logger.exception("Error in result sender")
298296

299297
def _send_result(self, fut):
300298
if fut.cancelled():
@@ -317,7 +315,7 @@ def _make_future(self):
317315
return f
318316

319317
def _call_method(self, call: MethodCall):
320-
self.logger.debug("[id: %s] Recieved '%s' method call", call.id, call.method_name)
318+
self.logger.debug("[id: %s] Received '%s' method call", call.id, call.method_name)
321319
fut = self._make_future()
322320
self._futures.put(call.id, fut)
323321

@@ -341,7 +339,7 @@ def _call_method(self, call: MethodCall):
341339
return fut
342340

343341
def _cancel_request(self, cancel: Cancellation):
344-
self.logger.debug("[id: %s] Recieved cancel request", cancel.id)
342+
self.logger.debug("[id: %s] Received cancel request", cancel.id)
345343
fut = self._futures.pop_id(cancel.id)
346344
if fut:
347345
fut.cancel()
@@ -373,3 +371,7 @@ def listen(self):
373371
break
374372
except Exception:
375373
self.logger.error("Error in main loop", exc_info=1)
374+
375+
self._start_result_sender.join()
376+
self._conn.close()
377+
self.logger.debug("Closing connection")

0 commit comments

Comments
 (0)