|
| 1 | + |
| 2 | +import ast |
1 | 3 | import json |
2 | 4 | import re |
3 | 5 | from collections.abc import Mapping |
|
8 | 10 | from functools import lru_cache |
9 | 11 | from operator import index |
10 | 12 | from pathlib import Path |
| 13 | +from string import Formatter |
11 | 14 | from typing import Dict, List, Optional, Union |
12 | 15 | from urllib.parse import urljoin |
13 | 16 | from warnings import warn |
@@ -1368,6 +1371,21 @@ class ShimadzuHPVX2Conditions(ConditionsBase): |
1368 | 1371 | } |
1369 | 1372 |
|
1370 | 1373 |
|
| 1374 | +class SourceExprChecker(ast.NodeVisitor): |
| 1375 | + def visit_Call(self, node): |
| 1376 | + raise ValueError("Function calls not allowed in source name patterns") |
| 1377 | + |
| 1378 | + |
| 1379 | +class SourceNameFormatter(Formatter): |
| 1380 | + """String formatter that evaluates simple operations like {modno + 2}""" |
| 1381 | + |
| 1382 | + def get_field(self, field_name, args, kwargs): |
| 1383 | + node = ast.parse(field_name, "<source pattern>", "eval") |
| 1384 | + SourceExprChecker().visit(node) |
| 1385 | + obj = eval(compile(node, "<source pattern>", "eval"), kwargs) |
| 1386 | + return obj, 0 |
| 1387 | + |
| 1388 | + |
1371 | 1389 | @dataclass |
1372 | 1390 | class DetectorModule: |
1373 | 1391 | """Detector module. |
@@ -1402,12 +1420,17 @@ class DetectorModule: |
1402 | 1420 | module_number: int | None |
1403 | 1421 | detector_type: str |
1404 | 1422 | legacy_uuid: int | None # Deprecated, do not use |
| 1423 | + source_name: str | None |
1405 | 1424 |
|
1406 | 1425 | def __post_init__(self): |
1407 | 1426 | if self.module_number is None: |
1408 | 1427 | # Try to fill in module number if missing. |
1409 | 1428 | self.module_number = int(re.findall(r"\d+", self.aggregator)[-1]) |
1410 | 1429 |
|
| 1430 | + if self.source_name is not None: |
| 1431 | + self.source_name = SourceNameFormatter().format( |
| 1432 | + self.source_name, modno=self.module_number) |
| 1433 | + |
1411 | 1434 | @property |
1412 | 1435 | def ccv_params(self): |
1413 | 1436 | """PDU arguments as needed for write_ccv().""" |
@@ -1465,7 +1488,7 @@ def get_da(x): |
1465 | 1488 | item['id'], item['physical_name'], item['karabo_da'], |
1466 | 1489 | self.identifier, item['virtual_device_name'], i, |
1467 | 1490 | item['module_number'], item['detector_type']['name'], |
1468 | | - item['uuid']) |
| 1491 | + item['uuid'], self._source_name_pattern) |
1469 | 1492 | else: |
1470 | 1493 | item.module_index = i |
1471 | 1494 |
|
@@ -1598,6 +1621,13 @@ def source_name_pattern(self) -> str: |
1598 | 1621 | 'incomplete detector entry in CalCat' |
1599 | 1622 | return self._source_name_pattern |
1600 | 1623 |
|
| 1624 | + @property |
| 1625 | + def source_names(self) -> str: |
| 1626 | + """Source names.""" |
| 1627 | + assert self._source_name_pattern is not None, \ |
| 1628 | + 'incomplete detector entry in CalCat' |
| 1629 | + return [pdu.source_name for pdu in self.pdus] |
| 1630 | + |
1601 | 1631 | @property |
1602 | 1632 | def first_module_index(self) -> int: |
1603 | 1633 | """Module index of the first module.""" |
|
0 commit comments