<p>pespin has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/c/osmo-gsm-tester/+/18388">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">schema: Allow objects registering their own schema types<br><br>Change-Id: I998c8674a55531909bfeac420064c3f238cea126<br>---<br>A selftest/schema_test/schema_case_06.conf<br>M selftest/schema_test/schema_test.ok<br>M selftest/schema_test/schema_test.py<br>M src/osmo_gsm_tester/core/schema.py<br>4 files changed, 90 insertions(+), 17 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/osmo-gsm-tester refs/changes/88/18388/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/selftest/schema_test/schema_case_06.conf b/selftest/schema_test/schema_case_06.conf</span><br><span>new file mode 100644</span><br><span>index 0000000..ea7f45f</span><br><span>--- /dev/null</span><br><span>+++ b/selftest/schema_test/schema_case_06.conf</span><br><span>@@ -0,0 +1,29 @@</span><br><span style="color: hsl(120, 100%, 40%);">+schema:</span><br><span style="color: hsl(120, 100%, 40%);">+  handover:</span><br><span style="color: hsl(120, 100%, 40%);">+    threshold: 'uint'</span><br><span style="color: hsl(120, 100%, 40%);">+    myvar: 'test_type'</span><br><span style="color: hsl(120, 100%, 40%);">+    anothervar: 'another_type'</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+tests:</span><br><span style="color: hsl(120, 100%, 40%);">+   - foobar:</span><br><span style="color: hsl(120, 100%, 40%);">+       prefix:</span><br><span style="color: hsl(120, 100%, 40%);">+         handover:</span><br><span style="color: hsl(120, 100%, 40%);">+           myvar: 'valid_value1'</span><br><span style="color: hsl(120, 100%, 40%);">+           anothervar: 'unique_val_ok'</span><br><span style="color: hsl(120, 100%, 40%);">+           threshold: 2</span><br><span style="color: hsl(120, 100%, 40%);">+   - foobar:</span><br><span style="color: hsl(120, 100%, 40%);">+       prefix:</span><br><span style="color: hsl(120, 100%, 40%);">+         handover:</span><br><span style="color: hsl(120, 100%, 40%);">+           myvar: 'valid_value2'</span><br><span style="color: hsl(120, 100%, 40%);">+   - foobar:</span><br><span style="color: hsl(120, 100%, 40%);">+       prefix:</span><br><span style="color: hsl(120, 100%, 40%);">+         handover:</span><br><span style="color: hsl(120, 100%, 40%);">+           threshold: 0</span><br><span style="color: hsl(120, 100%, 40%);">+   - foobar:</span><br><span style="color: hsl(120, 100%, 40%);">+       prefix:</span><br><span style="color: hsl(120, 100%, 40%);">+         handover:</span><br><span style="color: hsl(120, 100%, 40%);">+             myvar: 'invalid_val'</span><br><span style="color: hsl(120, 100%, 40%);">+   - foobar:</span><br><span style="color: hsl(120, 100%, 40%);">+       prefix:</span><br><span style="color: hsl(120, 100%, 40%);">+         handover:</span><br><span style="color: hsl(120, 100%, 40%);">+             anothervar: 'another_invalid_val'</span><br><span>diff --git a/selftest/schema_test/schema_test.ok b/selftest/schema_test/schema_test.ok</span><br><span>index 2c4cd6a..846caae 100644</span><br><span>--- a/selftest/schema_test/schema_test.ok</span><br><span>+++ b/selftest/schema_test/schema_test.ok</span><br><span>@@ -61,3 +61,20 @@</span><br><span> --- -: ERR: ValueError: config item is a list, should be 'str': 'foobar.prefix.hey.ho.letsgo[]'</span><br><span> Validation: Error</span><br><span> ----------------------</span><br><span style="color: hsl(120, 100%, 40%);">+schema_case_06.conf:</span><br><span style="color: hsl(120, 100%, 40%);">+{'foobar.prefix.handover.anothervar': 'another_type',</span><br><span style="color: hsl(120, 100%, 40%);">+ 'foobar.prefix.handover.myvar': 'test_type',</span><br><span style="color: hsl(120, 100%, 40%);">+ 'foobar.prefix.handover.threshold': 'uint'}</span><br><span style="color: hsl(120, 100%, 40%);">+validating tests[0]</span><br><span style="color: hsl(120, 100%, 40%);">+Validation: OK</span><br><span style="color: hsl(120, 100%, 40%);">+validating tests[1]</span><br><span style="color: hsl(120, 100%, 40%);">+Validation: OK</span><br><span style="color: hsl(120, 100%, 40%);">+validating tests[2]</span><br><span style="color: hsl(120, 100%, 40%);">+Validation: OK</span><br><span style="color: hsl(120, 100%, 40%);">+validating tests[3]</span><br><span style="color: hsl(120, 100%, 40%);">+--- foobar.prefix.handover.myvar: ERR: ValueError: Invalid value 'invalid_val' for schema type 'test_type' (validator: test_validator)</span><br><span style="color: hsl(120, 100%, 40%);">+Validation: Error</span><br><span style="color: hsl(120, 100%, 40%);">+validating tests[4]</span><br><span style="color: hsl(120, 100%, 40%);">+--- foobar.prefix.handover.anothervar: ERR: ValueError: Invalid value 'another_invalid_val' for schema type 'another_type' (validator: <lambda>)</span><br><span style="color: hsl(120, 100%, 40%);">+Validation: Error</span><br><span style="color: hsl(120, 100%, 40%);">+----------------------</span><br><span>diff --git a/selftest/schema_test/schema_test.py b/selftest/schema_test/schema_test.py</span><br><span>index 3cf2799..bffa601 100755</span><br><span>--- a/selftest/schema_test/schema_test.py</span><br><span>+++ b/selftest/schema_test/schema_test.py</span><br><span>@@ -25,6 +25,13 @@</span><br><span>             li.append(f)</span><br><span>     return sorted(li)</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+def test_validator(val):</span><br><span style="color: hsl(120, 100%, 40%);">+    return val in ('valid_value1', 'valid_value2')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+schema.register_schema_types({'test_type': test_validator,</span><br><span style="color: hsl(120, 100%, 40%);">+                              'another_type': lambda val: val == 'unique_val_ok'})</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> print('==== Testing dynamically generated schemas ====')</span><br><span> for f in get_case_list(_prep.script_dir):</span><br><span>     print('%s:' % f)</span><br><span>diff --git a/src/osmo_gsm_tester/core/schema.py b/src/osmo_gsm_tester/core/schema.py</span><br><span>index 9055c5b..70b4c8c 100644</span><br><span>--- a/src/osmo_gsm_tester/core/schema.py</span><br><span>+++ b/src/osmo_gsm_tester/core/schema.py</span><br><span>@@ -36,12 +36,12 @@</span><br><span>             break;</span><br><span>         if not regex.fullmatch(val):</span><br><span>             break;</span><br><span style="color: hsl(0, 100%, 40%);">-        return</span><br><span style="color: hsl(120, 100%, 40%);">+        return True</span><br><span>     raise ValueError('Invalid %s: %r' % (name, val))</span><br><span> </span><br><span> def band(val):</span><br><span>     if val in ('GSM-900', 'GSM-1800', 'GSM-1900'):</span><br><span style="color: hsl(0, 100%, 40%);">-        return</span><br><span style="color: hsl(120, 100%, 40%);">+        return True</span><br><span>     raise ValueError('Unknown GSM band: %r' % val)</span><br><span> </span><br><span> def ipv4(val):</span><br><span>@@ -49,27 +49,30 @@</span><br><span>     els = [int(el) for el in val.split('.')]</span><br><span>     if not all([el >= 0 and el <= 255 for el in els]):</span><br><span>         raise ValueError('Invalid IPv4 address: %r' % val)</span><br><span style="color: hsl(120, 100%, 40%);">+    return True</span><br><span> </span><br><span> def hwaddr(val):</span><br><span style="color: hsl(0, 100%, 40%);">-    match_re('hardware address', HWADDR_RE, val)</span><br><span style="color: hsl(120, 100%, 40%);">+    return match_re('hardware address', HWADDR_RE, val)</span><br><span> </span><br><span> def imsi(val):</span><br><span style="color: hsl(0, 100%, 40%);">-    match_re('IMSI', IMSI_RE, val)</span><br><span style="color: hsl(120, 100%, 40%);">+    return match_re('IMSI', IMSI_RE, val)</span><br><span> </span><br><span> def ki(val):</span><br><span style="color: hsl(0, 100%, 40%);">-    match_re('KI', KI_RE, val)</span><br><span style="color: hsl(120, 100%, 40%);">+    return match_re('KI', KI_RE, val)</span><br><span> </span><br><span> def msisdn(val):</span><br><span style="color: hsl(0, 100%, 40%);">-    match_re('MSISDN', MSISDN_RE, val)</span><br><span style="color: hsl(120, 100%, 40%);">+    return match_re('MSISDN', MSISDN_RE, val)</span><br><span> </span><br><span> def auth_algo(val):</span><br><span>     if val not in util.ENUM_OSMO_AUTH_ALGO:</span><br><span>         raise ValueError('Unknown Authentication Algorithm: %r' % val)</span><br><span style="color: hsl(120, 100%, 40%);">+    return True</span><br><span> </span><br><span> def uint(val):</span><br><span>     n = int(val)</span><br><span>     if n < 0:</span><br><span>         raise ValueError('Positive value expected instead of %d' % n)</span><br><span style="color: hsl(120, 100%, 40%);">+    return True</span><br><span> </span><br><span> def uint8(val):</span><br><span>     n = int(val)</span><br><span>@@ -77,6 +80,7 @@</span><br><span>         raise ValueError('Positive value expected instead of %d' % n)</span><br><span>     if n > 255: # 2^8 - 1</span><br><span>         raise ValueError('Value %d too big, max value is 255' % n)</span><br><span style="color: hsl(120, 100%, 40%);">+    return True</span><br><span> </span><br><span> def uint16(val):</span><br><span>     n = int(val)</span><br><span>@@ -84,57 +88,64 @@</span><br><span>         raise ValueError('Positive value expected instead of %d' % n)</span><br><span>     if n > 65535: # 2^16 - 1</span><br><span>         raise ValueError('Value %d too big, max value is 65535' % n)</span><br><span style="color: hsl(120, 100%, 40%);">+    return True</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def bool_str(val):</span><br><span style="color: hsl(120, 100%, 40%);">+    # str2bool will raise an exception if unable to parse it</span><br><span style="color: hsl(120, 100%, 40%);">+    util.str2bool(val)</span><br><span style="color: hsl(120, 100%, 40%);">+    return True</span><br><span> </span><br><span> def times(val):</span><br><span>     n = int(val)</span><br><span>     if n < 1:</span><br><span>         raise ValueError('Positive value >0 expected instead of %d' % n)</span><br><span style="color: hsl(120, 100%, 40%);">+    return True</span><br><span> </span><br><span> def cipher(val):</span><br><span>     if val in ('a5_0', 'a5_1', 'a5_2', 'a5_3', 'a5_4', 'a5_5', 'a5_6', 'a5_7'):</span><br><span style="color: hsl(0, 100%, 40%);">-        return</span><br><span style="color: hsl(120, 100%, 40%);">+        return True</span><br><span>     raise ValueError('Unknown Cipher value: %r' % val)</span><br><span> </span><br><span> def modem_feature(val):</span><br><span>     if val in ('sms', 'gprs', 'voice', 'ussd', 'sim', '2g', '3g', '4g'):</span><br><span style="color: hsl(0, 100%, 40%);">-        return</span><br><span style="color: hsl(120, 100%, 40%);">+        return True</span><br><span>     raise ValueError('Unknown Modem Feature: %r' % val)</span><br><span> </span><br><span> def phy_channel_config(val):</span><br><span>     if val in ('CCCH', 'CCCH+SDCCH4', 'TCH/F', 'TCH/H', 'SDCCH8', 'PDCH',</span><br><span>                'TCH/F_PDCH', 'CCCH+SDCCH4+CBCH', 'SDCCH8+CBCH','TCH/F_TCH/H_PDCH'):</span><br><span style="color: hsl(0, 100%, 40%);">-        return</span><br><span style="color: hsl(120, 100%, 40%);">+        return True</span><br><span>     raise ValueError('Unknown Physical channel config: %r' % val)</span><br><span> </span><br><span> def channel_allocator(val):</span><br><span>     if val in ('ascending', 'descending'):</span><br><span style="color: hsl(0, 100%, 40%);">-        return</span><br><span style="color: hsl(120, 100%, 40%);">+        return True</span><br><span>     raise ValueError('Unknown Channel Allocator Policy %r' % val)</span><br><span> </span><br><span> def gprs_mode(val):</span><br><span>     if val in ('none', 'gprs', 'egprs'):</span><br><span style="color: hsl(0, 100%, 40%);">-        return</span><br><span style="color: hsl(120, 100%, 40%);">+        return True</span><br><span>     raise ValueError('Unknown GPRS mode %r' % val)</span><br><span> </span><br><span> def codec(val):</span><br><span>     if val in ('hr1', 'hr2', 'hr3', 'fr1', 'fr2', 'fr3'):</span><br><span style="color: hsl(0, 100%, 40%);">-        return</span><br><span style="color: hsl(120, 100%, 40%);">+        return True</span><br><span>     raise ValueError('Unknown Codec value: %r' % val)</span><br><span> </span><br><span> def osmo_trx_clock_ref(val):</span><br><span>     if val in ('internal', 'external', 'gspdo'):</span><br><span style="color: hsl(0, 100%, 40%);">-        return</span><br><span style="color: hsl(120, 100%, 40%);">+        return True</span><br><span>     raise ValueError('Unknown OsmoTRX clock reference value: %r' % val)</span><br><span> </span><br><span> def lte_transmission_mode(val):</span><br><span>     n = int(val)</span><br><span>     if n <= 4:</span><br><span style="color: hsl(0, 100%, 40%);">-        return</span><br><span style="color: hsl(120, 100%, 40%);">+        return True</span><br><span>     raise ValueError('LTE Transmission Mode %d not in expected range' % n)</span><br><span> </span><br><span> def duration(val):</span><br><span>     if val.isdecimal() or val.endswith('m') or val.endswith('h'):</span><br><span style="color: hsl(0, 100%, 40%);">-        return</span><br><span style="color: hsl(120, 100%, 40%);">+        return True</span><br><span>     raise ValueError('Invalid duration value: %r' % val)</span><br><span> </span><br><span> INT = 'int'</span><br><span>@@ -163,7 +174,7 @@</span><br><span>         INT: int,</span><br><span>         STR: str,</span><br><span>         UINT: uint,</span><br><span style="color: hsl(0, 100%, 40%);">-        BOOL_STR: util.str2bool,</span><br><span style="color: hsl(120, 100%, 40%);">+        BOOL_STR: bool_str,</span><br><span>         BAND: band,</span><br><span>         IPV4: ipv4,</span><br><span>         HWADDR: hwaddr,</span><br><span>@@ -310,7 +321,9 @@</span><br><span> </span><br><span>         log.ctx(path)</span><br><span>         type_validator = SCHEMA_TYPES.get(want_type)</span><br><span style="color: hsl(0, 100%, 40%);">-        type_validator(value)</span><br><span style="color: hsl(120, 100%, 40%);">+        valid = type_validator(value)</span><br><span style="color: hsl(120, 100%, 40%);">+        if not valid:</span><br><span style="color: hsl(120, 100%, 40%);">+            raise ValueError('Invalid value %r for schema type \'%s\' (validator: %s)' % (value, want_type, type_validator.__name__))</span><br><span> </span><br><span>     def nest(parent_path, config, schema):</span><br><span>         if parent_path:</span><br><span>@@ -369,6 +382,13 @@</span><br><span> _WANT_SCHEMA = None</span><br><span> _ALL_SCHEMA = None</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+def register_schema_types(schema_type_attr):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Register schema types to be used by schema attributes.</span><br><span style="color: hsl(120, 100%, 40%);">+       For instance: register_resource_schema_attributes({ 'fruit': lambda val: val in ('banana', 'apple') })</span><br><span style="color: hsl(120, 100%, 40%);">+    """</span><br><span style="color: hsl(120, 100%, 40%);">+    global SCHEMA_TYPES</span><br><span style="color: hsl(120, 100%, 40%);">+    combine(SCHEMA_TYPES, schema_type_attr)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def register_resource_schema(obj_class_str, obj_attr_dict):</span><br><span>     """Register schema attributes for a resource type.</span><br><span>        For instance: register_resource_schema_attributes('modem', {'type': schema.STR, 'ki': schema.KI})</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-gsm-tester/+/18388">change 18388</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/osmo-gsm-tester/+/18388"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-gsm-tester </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I998c8674a55531909bfeac420064c3f238cea126 </div>
<div style="display:none"> Gerrit-Change-Number: 18388 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>