26
26
import shlex
27
27
from collections import deque
28
28
from textwrap import dedent
29
- from typing import List , Dict , Any , Tuple
29
+ from typing import List , Dict , Any , Optional , Tuple
30
30
31
31
from metomi .isodatetime .data import Duration , TimePoint
32
32
from metomi .isodatetime .dumpers import TimePointDumper
@@ -373,7 +373,7 @@ def coerce_range(cls, value, keys):
373
373
return Range ((int (min_ ), int (max_ )))
374
374
375
375
@classmethod
376
- def coerce_str (cls , value , keys ):
376
+ def coerce_str (cls , value , keys ) -> str :
377
377
"""Coerce value to a string.
378
378
379
379
Examples:
@@ -385,7 +385,7 @@ def coerce_str(cls, value, keys):
385
385
"""
386
386
if isinstance (value , list ):
387
387
# handle graph string merging
388
- vraw = []
388
+ vraw : List [ str ] = []
389
389
vals = [value ]
390
390
while vals :
391
391
val = vals .pop ()
@@ -512,41 +512,46 @@ def parse_int_range(cls, value):
512
512
return None
513
513
514
514
@classmethod
515
- def strip_and_unquote (cls , keys , value ):
515
+ def _unquote (cls , keys : List [str ], value : str ) -> Optional [str ]:
516
+ """Unquote value."""
517
+ for substr , rec in (
518
+ ("'''" , cls ._REC_MULTI_LINE_SINGLE ),
519
+ ('"""' , cls ._REC_MULTI_LINE_DOUBLE ),
520
+ ('"' , cls ._REC_DQ_VALUE ),
521
+ ("'" , cls ._REC_SQ_VALUE )
522
+ ):
523
+ if value .startswith (substr ):
524
+ match = rec .match (value )
525
+ if not match :
526
+ raise IllegalValueError ("string" , keys , value )
527
+ return match [1 ]
528
+ return None
529
+
530
+ @classmethod
531
+ def strip_and_unquote (cls , keys : List [str ], value : str ) -> str :
516
532
"""Remove leading and trailing spaces and unquote value.
517
533
518
534
Args:
519
- keys (list) :
535
+ keys:
520
536
Keys in nested dict that represents the raw configuration.
521
- value (str) :
537
+ value:
522
538
String value in raw configuration.
523
539
524
- Return (str) :
540
+ Return:
525
541
Processed value.
526
542
527
543
Examples:
528
544
>>> ParsecValidator.strip_and_unquote(None, '" foo "')
529
545
'foo'
530
546
531
547
"""
532
- for substr , rec in [
533
- ["'''" , cls ._REC_MULTI_LINE_SINGLE ],
534
- ['"""' , cls ._REC_MULTI_LINE_DOUBLE ],
535
- ['"' , cls ._REC_DQ_VALUE ],
536
- ["'" , cls ._REC_SQ_VALUE ]]:
537
- if value .startswith (substr ):
538
- match = rec .match (value )
539
- if not match :
540
- raise IllegalValueError ("string" , keys , value )
541
- value = match .groups ()[0 ]
542
- break
543
- else :
544
- # unquoted
545
- value = value .split (r'#' , 1 )[0 ]
548
+ val = cls ._unquote (keys , value )
549
+ if val is None :
550
+ val = value .split (r'#' , 1 )[0 ]
546
551
547
552
# Note strip() removes leading and trailing whitespace, including
548
553
# initial newlines on a multiline string:
549
- return dedent (value ).strip ()
554
+ return dedent (val ).strip ()
550
555
551
556
@classmethod
552
557
def strip_and_unquote_list (cls , keys , value ):
@@ -1136,23 +1141,25 @@ def _coerce_type(cls, value):
1136
1141
return val
1137
1142
1138
1143
1139
- # BACK COMPAT: BroadcastConfigValidator
1140
- # The DB at 8.0.x stores Interval values as neither ISO8601 duration
1141
- # string or DurationFloat. This has been fixed at 8.1.0, and
1142
- # the following class acts as a bridge between fixed and broken.
1143
- # url:
1144
- # https://github.com/cylc/cylc-flow/pull/5138
1145
- # from:
1146
- # 8.0.x
1147
- # to:
1148
- # 8.1.x
1149
- # remove at:
1150
- # 8.x
1151
1144
class BroadcastConfigValidator (CylcConfigValidator ):
1152
1145
"""Validate and Coerce DB loaded broadcast config to internal objects."""
1153
1146
def __init__ (self ):
1154
1147
CylcConfigValidator .__init__ (self )
1155
1148
1149
+ @classmethod
1150
+ def coerce_str (cls , value , keys ) -> str :
1151
+ """Coerce value to a string. Unquotes & strips lead/trail whitespace.
1152
+
1153
+ Prevents ParsecValidator from assuming '#' means comments;
1154
+ '#' has valid uses in shell script such as parameter substitution.
1155
+
1156
+ Examples:
1157
+ >>> BroadcastConfigValidator.coerce_str('echo "${FOO#*bar}"', None)
1158
+ 'echo "${FOO#*bar}"'
1159
+ """
1160
+ val = ParsecValidator ._unquote (keys , value ) or value
1161
+ return dedent (val ).strip ()
1162
+
1156
1163
@classmethod
1157
1164
def strip_and_unquote_list (cls , keys , value ):
1158
1165
"""Remove leading and trailing spaces and unquote list value.
@@ -1177,6 +1184,18 @@ def strip_and_unquote_list(cls, keys, value):
1177
1184
value = value .lstrip ('[' ).rstrip (']' )
1178
1185
return ParsecValidator .strip_and_unquote_list (keys , value )
1179
1186
1187
+ # BACK COMPAT: BroadcastConfigValidator.coerce_interval
1188
+ # The DB at 8.0.x stores Interval values as neither ISO8601 duration
1189
+ # string or DurationFloat. This has been fixed at 8.1.0, and
1190
+ # the following method acts as a bridge between fixed and broken.
1191
+ # url:
1192
+ # https://github.com/cylc/cylc-flow/pull/5138
1193
+ # from:
1194
+ # 8.0.x
1195
+ # to:
1196
+ # 8.1.x
1197
+ # remove at:
1198
+ # 8.x
1180
1199
@classmethod
1181
1200
def coerce_interval (cls , value , keys ):
1182
1201
"""Coerce an ISO 8601 interval into seconds.
0 commit comments