Skip to content

Commit 16ed7f1

Browse files
committed
Apply Context classes to the Analyser via entry points instead of direct imports to break dependency chains
1 parent d4d4ac0 commit 16ed7f1

1 file changed

Lines changed: 79 additions & 49 deletions

File tree

src/murfey/client/analyser.py

Lines changed: 79 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,15 @@
88

99
from __future__ import annotations
1010

11+
import functools
1112
import logging
1213
import queue
1314
import threading
15+
from importlib.metadata import entry_points
1416
from pathlib import Path
1517
from typing import Type
1618

1719
from murfey.client.context import Context
18-
from murfey.client.contexts.atlas import AtlasContext
19-
from murfey.client.contexts.clem import CLEMContext
20-
from murfey.client.contexts.fib import FIBContext
21-
from murfey.client.contexts.spa import SPAContext
22-
from murfey.client.contexts.spa_metadata import SPAMetadataContext
23-
from murfey.client.contexts.sxt import SXTContext
24-
from murfey.client.contexts.tomo import TomographyContext
25-
from murfey.client.contexts.tomo_metadata import TomographyMetadataContext
2620
from murfey.client.destinations import find_longest_data_directory
2721
from murfey.client.instance_environment import MurfeyInstanceEnvironment
2822
from murfey.client.rsync import RSyncerUpdate, TransferResult
@@ -33,6 +27,23 @@
3327
logger = logging.getLogger("murfey.client.analyser")
3428

3529

30+
# Load the Context entry points as a list upon initialisation
31+
context_eps = list(entry_points(group="murfey.contexts"))
32+
33+
34+
@functools.lru_cache(maxsize=1)
35+
def _get_context(name: str):
36+
"""
37+
Load the desired context from the configured list of entry points.
38+
Returns None if the entry point is not found
39+
"""
40+
if context := [ep for ep in context_eps if ep.name == name]:
41+
return context[0]
42+
else:
43+
logger.warning(f"Could not find entry point for {name!r}")
44+
return None
45+
46+
3647
class Analyser(Observer):
3748
def __init__(
3849
self,
@@ -145,7 +156,9 @@ def _find_context(self, file_path: Path) -> bool:
145156
)
146157
)
147158
):
148-
self._context = CLEMContext(
159+
if (context := _get_context("CLEMContext")) is None:
160+
return False
161+
self._context = context.load()(
149162
"leica",
150163
self._basepath,
151164
self._murfey_config,
@@ -166,7 +179,9 @@ def _find_context(self, file_path: Path) -> bool:
166179
and "Sites" in file_path.parts
167180
)
168181
):
169-
self._context = FIBContext(
182+
if (context := _get_context("FIBContext")) is None:
183+
return False
184+
self._context = context.load()(
170185
"autotem",
171186
self._basepath,
172187
self._murfey_config,
@@ -183,7 +198,9 @@ def _find_context(self, file_path: Path) -> bool:
183198
all(path in file_path.parts for path in ("LayersData", "Layer"))
184199
)
185200
):
186-
self._context = FIBContext(
201+
if (context := _get_context("FIBContext")) is None:
202+
return False
203+
self._context = context.load()(
187204
"maps",
188205
self._basepath,
189206
self._murfey_config,
@@ -196,7 +213,9 @@ def _find_context(self, file_path: Path) -> bool:
196213
# Image metadata stored in "features.json" file
197214
file_path.name == "features.json" or ()
198215
):
199-
self._context = FIBContext(
216+
if (context := _get_context("FIBContext")) is None:
217+
return False
218+
self._context = context.load()(
200219
"meteor",
201220
self._basepath,
202221
self._murfey_config,
@@ -208,7 +227,9 @@ def _find_context(self, file_path: Path) -> bool:
208227
# SXT workflow checks
209228
# -----------------------------------------------------------------------------
210229
if file_path.suffix in (".txrm", ".xrm"):
211-
self._context = SXTContext(
230+
if (context := _get_context("SXTContext")) is None:
231+
return False
232+
self._context = context.load()(
212233
"zeiss",
213234
self._basepath,
214235
self._murfey_config,
@@ -220,7 +241,9 @@ def _find_context(self, file_path: Path) -> bool:
220241
# Tomography and SPA workflow checks
221242
# -----------------------------------------------------------------------------
222243
if "atlas" in file_path.parts:
223-
self._context = AtlasContext(
244+
if (context := _get_context("AtlasContext")) is None:
245+
return False
246+
self._context = context.load()(
224247
"serialem" if self._serialem else "epu",
225248
self._basepath,
226249
self._murfey_config,
@@ -229,7 +252,9 @@ def _find_context(self, file_path: Path) -> bool:
229252
return True
230253

231254
if "Metadata" in file_path.parts or file_path.name == "EpuSession.dm":
232-
self._context = SPAMetadataContext(
255+
if (context := _get_context("SPAMetadataContext")) is None:
256+
return False
257+
self._context = context.load()(
233258
"epu",
234259
self._basepath,
235260
self._murfey_config,
@@ -242,7 +267,9 @@ def _find_context(self, file_path: Path) -> bool:
242267
or "Thumbnails" in file_path.parts
243268
or file_path.name == "Session.dm"
244269
):
245-
self._context = TomographyMetadataContext(
270+
if (context := _get_context("TomographyMetadataContext")) is None:
271+
return False
272+
self._context = context.load()(
246273
"tomo",
247274
self._basepath,
248275
self._murfey_config,
@@ -263,7 +290,9 @@ def _find_context(self, file_path: Path) -> bool:
263290
]:
264291
if not self._context:
265292
logger.info("Acquisition software: EPU")
266-
self._context = SPAContext(
293+
if (context := _get_context("SPAContext")) is None:
294+
return False
295+
self._context = context.load()(
267296
"epu",
268297
self._basepath,
269298
self._murfey_config,
@@ -282,7 +311,9 @@ def _find_context(self, file_path: Path) -> bool:
282311
):
283312
if not self._context:
284313
logger.info("Acquisition software: tomo")
285-
self._context = TomographyContext(
314+
if (context := _get_context("TomographyContext")) is None:
315+
return False
316+
self._context = context.load()(
286317
"tomo",
287318
self._basepath,
288319
self._murfey_config,
@@ -322,24 +353,26 @@ def _analyse(self):
322353
or transferred_file.name == "EpuSession.dm"
323354
and not self._context
324355
):
325-
self._context = SPAMetadataContext(
326-
"epu",
327-
self._basepath,
328-
self._murfey_config,
329-
self._token,
330-
)
356+
if context := _get_context("SPAMetadataContext"):
357+
self._context = context.load()(
358+
"epu",
359+
self._basepath,
360+
self._murfey_config,
361+
self._token,
362+
)
331363
elif (
332364
"Batch" in transferred_file.parts
333365
or "SearchMaps" in transferred_file.parts
334366
or transferred_file.name == "Session.dm"
335367
and not self._context
336368
):
337-
self._context = TomographyMetadataContext(
338-
"tomo",
339-
self._basepath,
340-
self._murfey_config,
341-
self._token,
342-
)
369+
if context := _get_context("TomographyMetadataContext"):
370+
self._context = context.load()(
371+
"tomo",
372+
self._basepath,
373+
self._murfey_config,
374+
self._token,
375+
)
343376
self.post_transfer(transferred_file)
344377
else:
345378
dc_metadata = {}
@@ -386,7 +419,7 @@ def _analyse(self):
386419
)
387420
except Exception as e:
388421
logger.error(f"Exception encountered: {e}")
389-
if not isinstance(self._context, AtlasContext):
422+
if "AtlasContext" not in str(self._context):
390423
if not dc_metadata:
391424
try:
392425
dc_metadata = self._context.gather_metadata(
@@ -417,24 +450,21 @@ def _analyse(self):
417450
)
418451
self.notify(dc_metadata)
419452

420-
# If a file with a CLEM context is identified, immediately post it
421-
elif isinstance(self._context, CLEMContext):
453+
# Ccontexts that can be immediately posted without additional work
454+
elif "CLEMContext" not in str(self._context):
422455
logger.debug(
423-
f"File {transferred_file.name!r} will be processed as part of CLEM workflow"
456+
f"File {transferred_file.name!r} is part of CLEM workflow"
424457
)
425458
self.post_transfer(transferred_file)
426-
427-
elif isinstance(self._context, FIBContext):
459+
elif "FIBContext" not in str(self._context):
428460
logger.debug(
429-
f"File {transferred_file.name!r} will be processed as part of the FIB workflow"
461+
f"File {transferred_file.name!r} is part of the FIB workflow"
430462
)
431463
self.post_transfer(transferred_file)
432-
433-
elif isinstance(self._context, SXTContext):
464+
elif "SXTContext" not in str(self._context):
434465
logger.debug(f"File {transferred_file.name!r} is an SXT file")
435466
self.post_transfer(transferred_file)
436-
437-
elif isinstance(self._context, AtlasContext):
467+
elif "AtlasContext" not in str(self._context):
438468
logger.debug(f"File {transferred_file.name!r} is part of the atlas")
439469
self.post_transfer(transferred_file)
440470

@@ -480,14 +510,14 @@ def _analyse(self):
480510
self._context._acquisition_software
481511
)
482512
self.notify(dc_metadata)
483-
elif isinstance(
484-
self._context,
485-
(
486-
SPAContext,
487-
SPAMetadataContext,
488-
TomographyContext,
489-
TomographyMetadataContext,
490-
),
513+
elif any(
514+
context in str(self._context)
515+
for context in (
516+
"SPAContext",
517+
"SPAMetadataContext",
518+
"TomographyContext",
519+
"TomographyMetadataContext",
520+
)
491521
):
492522
context = str(self._context).split(" ")[0].split(".")[-1]
493523
logger.debug(

0 commit comments

Comments
 (0)