diff --git a/synapse/lib/types.py b/synapse/lib/types.py index 41b71ee216..e93c07f308 100644 --- a/synapse/lib/types.py +++ b/synapse/lib/types.py @@ -56,6 +56,12 @@ def __init__(self, modl, name, info, opts, skipinit=False): self.types = (self.name,) + self.info['bases'][::-1] self.opts = dict(self._opt_defs) + + for optn in opts.keys(): + if optn not in self.opts: + mesg = f'Type option {optn} is not valid for type {self.name}.' + raise s_exc.BadTypeDef(mesg=mesg) + self.opts.update(opts) self._type_norms = {} # python type to norm function map str: _norm_str @@ -541,18 +547,27 @@ class Array(Type): isarray = True ismutable = True + _opt_defs = ( + ('type', None), # type: ignore + ('uniq', True), # type: ignore + ('split', None), # type: ignore + ('sorted', True), # type: ignore + ('typeopts', None), # type: ignore + ) + def postTypeInit(self): - self.isuniq = self.opts.get('uniq', True) - self.issorted = self.opts.get('sorted', True) - self.splitstr = self.opts.get('split', None) + self.isuniq = self.opts.get('uniq') + self.issorted = self.opts.get('sorted') + self.splitstr = self.opts.get('split') typename = self.opts.get('type') if typename is None: mesg = 'Array type requires type= option.' raise s_exc.BadTypeDef(mesg=mesg) - typeopts = self.opts.get('typeopts', {}) + if (typeopts := self.opts.get('typeopts')) is None: + typeopts = {} basetype = self.modl.type(typename) if basetype is None: @@ -687,6 +702,11 @@ class Comp(Type): stortype = s_layer.STOR_TYPE_MSGP + _opt_defs = ( + ('sepr', None), # type: ignore + ('fields', ()), # type: ignore + ) + def postTypeInit(self): self.setNormFunc(list, self._normPyTuple) self.setNormFunc(tuple, self._normPyTuple) @@ -698,7 +718,7 @@ def postTypeInit(self): self._checkMutability() self.fieldtypes = {} - for fname, ftypename in self.opts.get('fields', ()): + for fname, ftypename in self.opts.get('fields'): if isinstance(ftypename, str): _type = self.modl.type(ftypename) else: @@ -713,7 +733,7 @@ def postTypeInit(self): self.fieldtypes[fname] = _type def _checkMutability(self): - for fname, ftypename in self.opts.get('fields', ()): + for fname, ftypename in self.opts.get('fields'): if isinstance(ftypename, (list, tuple)): ftypename = ftypename[0] @@ -1296,6 +1316,7 @@ class Int(IntBase): _opt_defs = ( ('size', 8), # type: ignore # Set the storage size of the integer type in bytes. ('signed', True), + ('enums', None), ('enums:strict', True), ('fmt', '%d'), # Set to an integer compatible format string to control repr. @@ -1316,6 +1337,13 @@ def postTypeInit(self): mesg = f'Invalid integer size ({self.size})' raise s_exc.BadTypeDef(mesg=mesg) + self.ismin = self.opts.get('ismin') + self.ismax = self.opts.get('ismax') + + if self.opts.get('ismin') and self.opts.get('ismax'): + mesg = f'Int type ({self.name}) has both ismin and ismax set.' + raise s_exc.BadTypeDef(mesg=mesg) + self.enumnorm = {} self.enumrepr = {} @@ -1363,10 +1391,10 @@ def postTypeInit(self): def merge(self, oldv, newv): - if self.opts.get('ismin'): + if self.ismin: return min(oldv, newv) - if self.opts.get('ismax'): + if self.ismax: return max(oldv, newv) return newv @@ -1994,6 +2022,11 @@ class Ndef(Type): stortype = s_layer.STOR_TYPE_NDEF + _opt_defs = ( + ('forms', None), # type: ignore + ('interface', None), # type: ignore + ) + def postTypeInit(self): self.setNormFunc(list, self._normPyTuple) self.setNormFunc(tuple, self._normPyTuple) @@ -2118,6 +2151,10 @@ class Data(Type): stortype = s_layer.STOR_TYPE_MSGP + _opt_defs = ( + ('schema', None), # type: ignore + ) + def postTypeInit(self): self.validator = None schema = self.opts.get('schema') @@ -2745,6 +2782,10 @@ def postTypeInit(self): self.ismin = self.opts.get('ismin') self.ismax = self.opts.get('ismax') + if self.ismin and self.ismax: + mesg = f'Time type ({self.name}) has both ismin and ismax set.' + raise s_exc.BadTypeDef(mesg=mesg) + precstr = self.opts.get('precision') self.prec = s_time.precisions.get(precstr) diff --git a/synapse/models/economic.py b/synapse/models/economic.py index 31464d8e55..5fc3942478 100644 --- a/synapse/models/economic.py +++ b/synapse/models/economic.py @@ -73,7 +73,7 @@ ('econ:invoice', ('guid', {}), { 'doc': 'An invoice issued requesting payment.'}), - ('econ:price', ('hugenum', {'norm': False}), { + ('econ:price', ('hugenum', {}), { 'doc': 'The amount of money expected, required, or given in payment for something.', 'ex': '2.20'}), diff --git a/synapse/models/geospace.py b/synapse/models/geospace.py index 9ccdfe836a..9fc19f87fb 100644 --- a/synapse/models/geospace.py +++ b/synapse/models/geospace.py @@ -218,11 +218,15 @@ class Dist(s_types.Int): + _opt_defs = ( + ('baseoff', 0), # type: ignore + ) + s_types.Int._opt_defs + def postTypeInit(self): s_types.Int.postTypeInit(self) self.setNormFunc(int, self._normPyInt) self.setNormFunc(str, self._normPyStr) - self.baseoff = self.opts.get('baseoff', 0) + self.baseoff = self.opts.get('baseoff') async def _normPyInt(self, valu, view=None): return valu, {} diff --git a/synapse/models/gov.py b/synapse/models/gov.py index eae6da34a3..0a340791a5 100644 --- a/synapse/models/gov.py +++ b/synapse/models/gov.py @@ -66,7 +66,7 @@ ), 'doc': 'A US Social Security Number (SSN).'}), - ('gov:us:zip', ('int', {'regex': '^[0-9]{5}'}), { + ('gov:us:zip', ('int', {'min': 0, 'max': 99999}), { 'doc': 'A US Postal Zip Code.'}), ('gov:us:cage', ('str', {'lower': True}), { diff --git a/synapse/models/inet.py b/synapse/models/inet.py index 2ba6a89672..d579ddebc8 100644 --- a/synapse/models/inet.py +++ b/synapse/models/inet.py @@ -110,6 +110,10 @@ class IPAddr(s_types.Type): stortype = s_layer.STOR_TYPE_IPADDR + _opt_defs = ( + ('version', None), # type: ignore + ) + def postTypeInit(self): self.setCmprCtor('>=', self._ctorCmprGe) @@ -370,6 +374,11 @@ async def cmpr(valu): class SockAddr(s_types.Str): + _opt_defs = ( + ('defport', None), # type: ignore + ('defproto', 'tcp'), # type: ignore + ) + s_types.Str._opt_defs + protos = ('tcp', 'udp', 'icmp', 'gre') noports = ('gre', 'icmp') @@ -383,8 +392,8 @@ def postTypeInit(self): self.porttype = self.modl.type('inet:port') self.prototype = self.modl.type('str').clone({'lower': True}) - self.defport = self.opts.get('defport', None) - self.defproto = self.opts.get('defproto', 'tcp') + self.defport = self.opts.get('defport') + self.defproto = self.opts.get('defproto') self.virtindx |= { 'ip': 'ip', diff --git a/synapse/models/syn.py b/synapse/models/syn.py index 043e4e1e23..a8e0a1aad6 100644 --- a/synapse/models/syn.py +++ b/synapse/models/syn.py @@ -245,7 +245,7 @@ async def _liftRuntSynTagProp(view, prop, cmprvalu=None): ('package', ('str', {}), { 'doc': 'Storm package which provided the command.'}), - ('svciden', ('guid', {'strip': True}), { + ('svciden', ('guid', {}), { 'doc': 'Storm service iden which provided the package.'}), ('deprecated', ('bool', {}), { diff --git a/synapse/tests/test_datamodel.py b/synapse/tests/test_datamodel.py index 405e92b930..c1bd7fe283 100644 --- a/synapse/tests/test_datamodel.py +++ b/synapse/tests/test_datamodel.py @@ -73,6 +73,10 @@ async def test_datamodel_basics(self): core.model.addFormProp('test:str', 'bar', ('newp', {}), {}) self.isin('No type named newp while declaring prop test:str:bar.', cm.exception.get('mesg')) + with self.raises(s_exc.BadTypeDef) as cm: + core.model.addType('_foo:type', 'int', {'foo': 'bar'}, {}) + self.isin('Type option foo is not valid', cm.exception.get('mesg')) + async def test_datamodel_formname(self): modl = s_datamodel.Model() mods = ( diff --git a/synapse/tests/test_lib_types.py b/synapse/tests/test_lib_types.py index f8bc7e90d8..0cede531e9 100644 --- a/synapse/tests/test_lib_types.py +++ b/synapse/tests/test_lib_types.py @@ -1045,6 +1045,9 @@ async def test_int(self): self.raises(s_exc.BadTypeDef, model.type('int').clone, {'enums': ((1, 'hehe'), (2, 'haha'), (3, 'HAHA'))}) self.raises(s_exc.BadTypeDef, model.type('int').clone, {'enums': ((1, 'hehe'), (2, 'haha'), (2, 'beep'))}) + with self.raises(s_exc.BadTypeDef): + model.type('int').clone({'ismin': True, 'ismax': True}) + async def test_float(self): model = s_datamodel.Model() t = model.type('float') @@ -1827,11 +1830,8 @@ async def test_time(self): self.gt(s_common.now(), (await ttime.norm('-1hour'))[0]) - tminmax = ttime.clone({'min': True, 'max': True}) - # Merge testing with tminmax - now = s_common.now() - self.eq(now + 1, tminmax.merge(now, now + 1)) - self.eq(now, tminmax.merge(now + 1, now)) + with self.raises(s_exc.BadTypeDef): + ttime.clone({'ismin': True, 'ismax': True}) async with self.getTestCore() as core: diff --git a/synapse/tests/test_model_syn.py b/synapse/tests/test_model_syn.py index a839439e39..47e6b30700 100644 --- a/synapse/tests/test_model_syn.py +++ b/synapse/tests/test_model_syn.py @@ -192,7 +192,7 @@ async def delExtModelConfigs(cortex): node = nodes[0] self.eq(('syn:type', 'comp'), node.ndef) self.none(node.get('subof')) - self.none(node.get('opts')) + self.eq({'sepr': None, 'fields': ()}, node.get('opts')) self.eq('synapse.lib.types.Comp', node.get('ctor')) self.eq('The base type for compound node fields.', node.get('doc')) @@ -200,7 +200,7 @@ async def delExtModelConfigs(cortex): self.len(1, nodes) node = nodes[0] self.eq(('syn:type', 'test:comp'), node.ndef) - self.eq({'fields': (('hehe', 'test:int'), ('haha', 'test:lower'))}, + self.eq({'fields': (('hehe', 'test:int'), ('haha', 'test:lower')), 'sepr': None}, node.get('opts')) self.eq('comp', node.get('subof')) self.eq('synapse.lib.types.Comp', node.get('ctor')) diff --git a/synapse/tests/utils.py b/synapse/tests/utils.py index a1fe29d93f..88b2e4bbbb 100644 --- a/synapse/tests/utils.py +++ b/synapse/tests/utils.py @@ -288,7 +288,7 @@ def repr(self, valu): }), ), 'types': ( - ('test:type10', ('test:type', {'foo': 10}), { + ('test:type10', ('test:type', {}), { 'doc': 'A fake type.'}), ('test:lower', ('str', {'lower': True}), {}), @@ -377,7 +377,7 @@ def repr(self, valu): ('intprop', ('int', {'min': 20, 'max': 30}), {}), ('int2', ('int', {}), {}), ('strprop', ('str', {'lower': 1}), {}), - ('guidprop', ('guid', {'lower': 1}), {}), + ('guidprop', ('guid', {}), {}), ('locprop', ('loc', {}), {}), )), @@ -493,7 +493,7 @@ def repr(self, valu): ('test:zeropad', {}, ()), ('test:ival', {}, ( ('interval', ('ival', {}), {}), - ('daymax', ('ival', {'precision': 'day', 'maxfill': True}), {}), + ('daymax', ('ival', {'precision': 'day'}), {}), )), ('test:pivtarg', {}, (