|
| 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 |
@@ -1431,6 +1434,21 @@ class ShimadzuHPVX2Conditions(ConditionsBase): |
1431 | 1434 | } |
1432 | 1435 |
|
1433 | 1436 |
|
| 1437 | +class SourceExprChecker(ast.NodeVisitor): |
| 1438 | + def visit_Call(self, node): |
| 1439 | + raise ValueError("Function calls not allowed in source name patterns") |
| 1440 | + |
| 1441 | + |
| 1442 | +class SourceNameFormatter(Formatter): |
| 1443 | + """String formatter that evaluates simple operations like {modno + 2}""" |
| 1444 | + |
| 1445 | + def get_field(self, field_name, args, kwargs): |
| 1446 | + node = ast.parse(field_name, "<source pattern>", "eval") |
| 1447 | + SourceExprChecker().visit(node) |
| 1448 | + obj = eval(compile(node, "<source pattern>", "eval"), kwargs) |
| 1449 | + return obj, 0 |
| 1450 | + |
| 1451 | + |
1434 | 1452 | @dataclass |
1435 | 1453 | class DetectorModule: |
1436 | 1454 | """Detector module. |
@@ -1465,12 +1483,17 @@ class DetectorModule: |
1465 | 1483 | module_number: int | None |
1466 | 1484 | detector_type: str |
1467 | 1485 | legacy_uuid: int | None # Deprecated, do not use |
| 1486 | + source_name: str | None |
1468 | 1487 |
|
1469 | 1488 | def __post_init__(self): |
1470 | 1489 | if self.module_number is None: |
1471 | 1490 | # Try to fill in module number if missing. |
1472 | 1491 | self.module_number = int(re.findall(r"\d+", self.aggregator)[-1]) |
1473 | 1492 |
|
| 1493 | + if self.source_name is not None: |
| 1494 | + self.source_name = SourceNameFormatter().format( |
| 1495 | + self.source_name, modno=self.module_number) |
| 1496 | + |
1474 | 1497 | @property |
1475 | 1498 | def ccv_params(self): |
1476 | 1499 | """PDU arguments as needed for write_ccv().""" |
@@ -1528,7 +1551,7 @@ def get_da(x): |
1528 | 1551 | item['id'], item['physical_name'], item['karabo_da'], |
1529 | 1552 | self.identifier, item['virtual_device_name'], i, |
1530 | 1553 | item['module_number'], item['detector_type']['name'], |
1531 | | - item['uuid']) |
| 1554 | + item['uuid'], self._source_name_pattern) |
1532 | 1555 | else: |
1533 | 1556 | item.module_index = i |
1534 | 1557 |
|
@@ -1661,6 +1684,13 @@ def source_name_pattern(self) -> str: |
1661 | 1684 | 'incomplete detector entry in CalCat' |
1662 | 1685 | return self._source_name_pattern |
1663 | 1686 |
|
| 1687 | + @property |
| 1688 | + def source_names(self) -> str: |
| 1689 | + """Source names.""" |
| 1690 | + assert self._source_name_pattern is not None, \ |
| 1691 | + 'incomplete detector entry in CalCat' |
| 1692 | + return [pdu.source_name for pdu in self.pdus] |
| 1693 | + |
1664 | 1694 | @property |
1665 | 1695 | def first_module_index(self) -> int: |
1666 | 1696 | """Module index of the first module.""" |
|
0 commit comments