From 85dec90605d0914d9201cef90f8a27bbd41b340b Mon Sep 17 00:00:00 2001 From: Gary Yendell Date: Mon, 18 Dec 2023 15:26:02 +0000 Subject: [PATCH] Fix name validation Fix regex validation pattern for Device name Update convert to enforce PascalCase for Component names Fix usage of label instead of name Update tests --- schemas/pvi.device.schema.json | 14 +++++----- src/pvi/_convert/_template_convert.py | 4 +-- src/pvi/_format/screen.py | 8 +++--- src/pvi/device.py | 16 +++++++++++- .../output/simDetector.pvi.device.yaml | 2 +- tests/format/output/sub_screen.bob | 6 ++--- ...reen_Group_1.bob => sub_screen_Group1.bob} | 8 +++--- ...reen_Group_4.bob => sub_screen_Group4.bob} | 8 +++--- tests/test_api.py | 26 +++++++++---------- 9 files changed, 52 insertions(+), 40 deletions(-) rename tests/format/output/{sub_screen_Group_1.bob => sub_screen_Group1.bob} (95%) rename tests/format/output/{sub_screen_Group_4.bob => sub_screen_Group4.bob} (95%) diff --git a/schemas/pvi.device.schema.json b/schemas/pvi.device.schema.json index 5c96b6f9..8a5b118c 100644 --- a/schemas/pvi.device.schema.json +++ b/schemas/pvi.device.schema.json @@ -134,7 +134,7 @@ "properties": { "name": { "description": "PascalCase name to uniquely identify", - "pattern": "([A-Z][a-z0-9]*)*$", + "pattern": "^([A-Z][a-z0-9]*)*$", "title": "Name", "type": "string" }, @@ -207,7 +207,7 @@ "properties": { "name": { "description": "PascalCase name to uniquely identify", - "pattern": "([A-Z][a-z0-9]*)*$", + "pattern": "^([A-Z][a-z0-9]*)*$", "title": "Name", "type": "string" }, @@ -385,7 +385,7 @@ "properties": { "name": { "description": "PascalCase name to uniquely identify", - "pattern": "([A-Z][a-z0-9]*)*$", + "pattern": "^([A-Z][a-z0-9]*)*$", "title": "Name", "type": "string" }, @@ -472,7 +472,7 @@ "properties": { "name": { "description": "PascalCase name to uniquely identify", - "pattern": "([A-Z][a-z0-9]*)*$", + "pattern": "^([A-Z][a-z0-9]*)*$", "title": "Name", "type": "string" }, @@ -615,7 +615,7 @@ "properties": { "name": { "description": "PascalCase name to uniquely identify", - "pattern": "([A-Z][a-z0-9]*)*$", + "pattern": "^([A-Z][a-z0-9]*)*$", "title": "Name", "type": "string" }, @@ -649,7 +649,7 @@ "properties": { "name": { "description": "PascalCase name to uniquely identify", - "pattern": "([A-Z][a-z0-9]*)*$", + "pattern": "^([A-Z][a-z0-9]*)*$", "title": "Name", "type": "string" }, @@ -732,7 +732,7 @@ "properties": { "name": { "description": "PascalCase name to uniquely identify", - "pattern": "([A-Z][a-z0-9]*)*$", + "pattern": "^([A-Z][a-z0-9]*)*$", "title": "Name", "type": "string" }, diff --git a/src/pvi/_convert/_template_convert.py b/src/pvi/_convert/_template_convert.py index 573a28fd..14346bbb 100644 --- a/src/pvi/_convert/_template_convert.py +++ b/src/pvi/_convert/_template_convert.py @@ -2,7 +2,7 @@ from pathlib import Path from typing import List, Tuple -from pvi.device import ComponentUnion, Grid, Group, Tree +from pvi.device import ComponentUnion, Grid, Group, Tree, enforce_pascal_case from ._asyn_convert import ( Action, @@ -24,7 +24,7 @@ def __init__(self, templates: List[Path]): def convert(self) -> Tree: return [ Group( - name=template.stem, + name=enforce_pascal_case(template.stem), layout=Grid(labelled=True), children=template_components, ) diff --git a/src/pvi/_format/screen.py b/src/pvi/_format/screen.py index c5281377..1c45260f 100644 --- a/src/pvi/_format/screen.py +++ b/src/pvi/_format/screen.py @@ -457,15 +457,13 @@ def generate_component_formatters( ): # Convert W of Signal(R)W into SignalX for each button row_components = [ - SignalX(name=label, pv=c.pv, value=value) - for label, value in c.widget.actions.items() + SignalX(name=action, pv=c.pv, value=value) + for action, value in c.widget.actions.items() ] if isinstance(c, SignalRW): row_components += [ # TODO: Improve optional read_pv with property? - SignalR( - name=c.get_label(), pv=c.read_pv or c.pv, widget=c.read_widget - ) + SignalR(name=c.name, pv=c.read_pv or c.pv, widget=c.read_widget) ] else: row_components = [c] # Create one widget for row diff --git a/src/pvi/device.py b/src/pvi/device.py index 2c0f0e26..5ca0f6a2 100644 --- a/src/pvi/device.py +++ b/src/pvi/device.py @@ -23,6 +23,7 @@ from pvi.utils import find_pvi_yaml PASCAL_CASE_REGEX = re.compile(r"(? str: @@ -46,6 +47,19 @@ def to_snake_case(pascal_s: str) -> str: return PASCAL_CASE_REGEX.sub(lambda m: "_" + m.group().lower(), pascal_s)[1:] +def enforce_pascal_case(s: str) -> str: + """Enforce a pascal case string, removing any invalid characters. + + Args: + s: String to convert + + Returns: PascalCase string + + """ + s = NON_PASCAL_CHARS_RE.sub(lambda _: "", s) + return s[0].upper() + s[1:] + + class TextFormat(Enum): """Format to use for display of Text{Read,Write} widgets on a UI""" @@ -249,7 +263,7 @@ class SubScreen(Layout): class Named(BaseSettings): name: str = Field( description="PascalCase name to uniquely identify", - pattern=r"([A-Z][a-z0-9]*)*$", + pattern=r"^([A-Z][a-z0-9]*)*$", ) diff --git a/tests/convert/output/simDetector.pvi.device.yaml b/tests/convert/output/simDetector.pvi.device.yaml index 180c7284..a0364d78 100644 --- a/tests/convert/output/simDetector.pvi.device.yaml +++ b/tests/convert/output/simDetector.pvi.device.yaml @@ -4,7 +4,7 @@ parent: ADDriver children: - type: Group - name: simDetector + name: SimDetector layout: type: Grid labelled: true diff --git a/tests/format/output/sub_screen.bob b/tests/format/output/sub_screen.bob index 0db725f7..9aec59d7 100644 --- a/tests/format/output/sub_screen.bob +++ b/tests/format/output/sub_screen.bob @@ -50,7 +50,7 @@ OpenDisplay - sub_screen_Group_1.bob + sub_screen_Group1.bob tab Open Display @@ -71,7 +71,7 @@ true Label - Group 3 E + Group 3 E 0 0 120 @@ -102,7 +102,7 @@ OpenDisplay - sub_screen_Group_4.bob + sub_screen_Group4.bob tab Open Display diff --git a/tests/format/output/sub_screen_Group_1.bob b/tests/format/output/sub_screen_Group1.bob similarity index 95% rename from tests/format/output/sub_screen_Group_1.bob rename to tests/format/output/sub_screen_Group1.bob index 6a23346e..dbbb09f0 100644 --- a/tests/format/output/sub_screen_Group_1.bob +++ b/tests/format/output/sub_screen_Group1.bob @@ -9,7 +9,7 @@ Title TITLE - Group 1 + Group1 0 0 290 @@ -27,7 +27,7 @@ Label - Group 1 B + Group 1 B 22 30 120 @@ -55,7 +55,7 @@ true Label - Group 2 C + Group 2 C 0 0 120 @@ -76,7 +76,7 @@ Label - Group 2 D + Group 2 D 0 24 120 diff --git a/tests/format/output/sub_screen_Group_4.bob b/tests/format/output/sub_screen_Group4.bob similarity index 95% rename from tests/format/output/sub_screen_Group_4.bob rename to tests/format/output/sub_screen_Group4.bob index 16892f2d..33a8f670 100644 --- a/tests/format/output/sub_screen_Group_4.bob +++ b/tests/format/output/sub_screen_Group4.bob @@ -9,7 +9,7 @@ Title TITLE - Group 4 + Group4 0 0 290 @@ -27,7 +27,7 @@ Label - Group 4 F + Group 4 F 22 30 120 @@ -55,7 +55,7 @@ true Label - Group 5 G + Group 5 G 0 0 120 @@ -76,7 +76,7 @@ Label - Group 5 H + Group 5 H 0 24 120 diff --git a/tests/test_api.py b/tests/test_api.py index bf0d12a7..e7e9bd24 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -119,7 +119,7 @@ def test_pva_table_panda(tmp_path, helper): formatter = Formatter.deserialize(formatter_yaml) table = SignalR( - name="PandA ", + name="PandA", pv="PANDAQSRV:SEQ1:TABLE", widget=TableRead( widgets=[TextRead()] * 4 + [LED()] * 6 + [TextRead()] + [LED()] * 6 @@ -178,47 +178,47 @@ def test_group_sub_screen(tmp_path, helper): signals = [ SignalR(name="A", pv="A", widget=TextRead()), Group( - name="Group 1", + name="Group1", layout=SubScreen(), children=[ - SignalR(name="Group 1 B", pv="GROUP1:B", widget=TextRead()), + SignalR(name="Group1B", pv="GROUP1:B", widget=TextRead()), Group( - name="Group 2", + name="Group2", layout=Grid(), children=[ SignalR( - name="Group 2 C", pv="GROUP1:GROUP2:C", widget=TextRead() + name="Group2C", pv="GROUP1:GROUP2:C", widget=TextRead() ), SignalR( - name="Group 2 D", pv="GROUP1:GROUP2:D", widget=TextRead() + name="Group2D", pv="GROUP1:GROUP2:D", widget=TextRead() ), ], ), ], ), Group( - name="Group 3", + name="Group3", layout=Grid(), children=[ - SignalR(name="Group 3 E", pv="GROUP3:E", widget=TextRead()), + SignalR(name="Group3E", pv="GROUP3:E", widget=TextRead()), Group( - name="Group 4", + name="Group4", layout=SubScreen(), children=[ SignalR( - name="Group 4 F", pv="GROUP3:GROUP4:F", widget=TextRead() + name="Group4F", pv="GROUP3:GROUP4:F", widget=TextRead() ), Group( - name="Group 5", + name="Group5", layout=Grid(), children=[ SignalR( - name="Group 5 G", + name="Group5G", pv="GROUP3:GROUP4:GROUP5:G", widget=TextRead(), ), SignalR( - name="Group 5 H", + name="Group5H", pv="GROUP3:GROUP4:GROUP5:H", widget=TextRead(), ),