fix transport and display of node-properties

Change-Id: I35a3021768e386a5ce922c8e24128d0bc3a039be
This commit is contained in:
Enrico Faulhaber
2017-09-12 16:10:22 +02:00
parent 7d5b211a0e
commit 96ac437fd3
18 changed files with 292 additions and 225 deletions

View File

@ -37,10 +37,10 @@ from secop.version import get_version
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.autodoc',
'sphinx.ext.intersphinx',
'sphinx.ext.todo',
'sphinx.ext.mathjax',
'sphinx.ext.viewcode']
'sphinx.ext.intersphinx',
'sphinx.ext.todo',
'sphinx.ext.mathjax',
'sphinx.ext.viewcode']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@ -179,7 +179,6 @@ texinfo_documents = [
]
# -- Options for Epub output ----------------------------------------------
# Bibliographic Dublin Core info.
@ -201,6 +200,5 @@ epub_copyright = copyright
epub_exclude_files = ['search.html']
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'https://docs.python.org/': None}

View File

@ -1,6 +1,5 @@
[equipment]
id=MLZ_amagnet(Garfield)
.visibility=expert
[equipment MLZ_amagnet(Garfield)]
visibility=expert
foo=bar
[interface tcp]

View File

@ -1,5 +1,5 @@
[equipment]
id=ccr12
[node ccr12]
description = CCR12 box of MLZ Sample environment group
[interface tcp]
interface=tcp

View File

@ -1,14 +1,9 @@
[equipment]
[equipment cryo_7]
# set SEC-node properties
id=cryo_7
description = short description
This is a very long description providing all the glory details in all the glory details about the stuff we are describing
.description = short description
This is a very long description providing all the glory details in all the glory details about the stuff we are describing
[interface tcp]
interface=tcp

View File

@ -1,5 +1,5 @@
[equipment]
id=Equipment_ID_for_demonstration
[equipment Equipment_ID_for_demonstration]
description = virtual modules to play around with
[interface tcp]
interface=tcp

View File

@ -1,5 +1,4 @@
[equipment]
id=see_demo_equipment
[equipment see_demo_equipment]
[client]
connectto=0.0.0.0

View File

@ -1,5 +1,12 @@
[equipment]
id=test config
[node test config]
description=description of the testing sec-node
.
Here should be the long description.
It can be very long.
.
a single . on a line gets stripped so you can make paragraphs.
.
These texts are supposed to be possibly very long.
[interface tcp]
interface=tcp

View File

@ -317,7 +317,8 @@ class Client(object):
if data:
self._cache.setdefault(modname, {})[pname] = Value(*data)
else:
self.log.warning('got malformed answer! (spec data)' % (spec, data))
self.log.warning(
'got malformed answer! (spec data)' % (spec, data))
# self.log.info('cache: %s:%s=%r (was: %s)', modname, pname, data, previous)
if spec in self.callbacks:
for func in self.callbacks[spec]:

View File

@ -340,25 +340,30 @@ class BoolType(DataType):
class ArrayOf(DataType):
def __init__(self, subtype, minsize_or_size=None, maxsize=None):
if maxsize is None:
maxsize = minsize_or_size
self.minsize = minsize_or_size
self.maxsize = maxsize
if self.minsize is not None and self.maxsize is not None and \
self.minsize > self.maxsize:
raise ValueError('minsize must be less than or equal to maxsize!')
def __init__(self, subtype, minsize=0, maxsize=None):
if not isinstance(subtype, DataType):
raise ValueError(
'ArrayOf only works with DataType objs as first argument!')
# if only one arg is given, it is maxsize!
if minsize and not maxsize:
maxsize = minsize
minsize = 0
self.as_json = ['array', subtype.as_json, maxsize]
elif maxsize:
self.as_json = ['array', subtype.as_json, maxsize, minsize]
else:
self.as_json = ['array', subtype.as_json]
self.minsize = minsize or 0
self.maxsize = maxsize
self.subtype = subtype
self.as_json = ['array', self.subtype.as_json,
self.maxsize, self.minsize]
if self.minsize is not None and self.minsize < 0:
if self.maxsize is not None and self.minsize > maxsize:
raise ValueError('minsize must be less than or equal to maxsize!')
if self.minsize < 0:
raise ValueError('Minimum size must be >= 0!')
if self.maxsize is not None and self.maxsize < 1:
raise ValueError('Maximum size must be >= 1!')
if self.minsize is not None and self.maxsize is not None and self.minsize > self.maxsize:
if self.maxsize is not None and self.minsize > self.maxsize:
raise ValueError('Maximum size must be >= Minimum size')
def __repr__(self):
@ -534,10 +539,12 @@ DATATYPES = dict(
bool=lambda: BoolType(),
int=lambda _min=None, _max=None: IntRange(_min, _max),
double=lambda _min=None, _max=None: FloatRange(_min, _max),
blob=lambda _min=None, _max=None: BLOBType(_min, _max),
string=lambda _min=None, _max=None: StringType(_min, _max),
array=lambda subtype, _min=None, _max=None: ArrayOf(
get_datatype(subtype), _min, _max),
blob=lambda _max=None, _min=None: BLOBType(
_min, _max) if _min else BLOBType(_max),
string=lambda _max=None, _min=None: StringType(
_min, _max) if _min else StringType(_max),
array=lambda subtype, _max=None, _min=None: ArrayOf(
get_datatype(subtype), _min, _max) if _min else ArrayOf(getdatatype(subtype), _min),
tuple=lambda subtypes: TupleOf(*map(get_datatype, subtypes)),
enum=lambda kwds: EnumType(**kwds),
struct=lambda named_subtypes: StructOf(

View File

@ -43,7 +43,8 @@ class NodeCtrl(QWidget):
self.contactPointLabel.setText(self._node.contactPoint)
self.equipmentIdLabel.setText(self._node.equipmentId)
self.protocolVersionLabel.setText(self._node.protocolVersion)
self.nodeDescriptionLabel.setText(self._node.describingData.get('description','no description available'))
self.nodeDescriptionLabel.setText(self._node.describingData['properties'].get(
'description', 'no description available'))
self._clearLog()
# now populate modules tab
@ -168,7 +169,8 @@ class ReadableWidget(QWidget):
self._node = node
self._module = module
self._status_type = self._node.getProperties(self._module, 'status').get('datatype')
self._status_type = self._node.getProperties(
self._module, 'status').get('datatype')
params = self._node.getProperties(self._module, 'value')
datatype = params.get('datatype', StringType())
@ -198,9 +200,11 @@ class ReadableWidget(QWidget):
if pname in params:
return params[pname].value
try:
# if queried, we get the qualifiers as well, but don't want them here
# if queried, we get the qualifiers as well, but don't want them
# here
import mlzlog
mlzlog.getLogger('cached values').warn('no cached value for %s:%s' % (self._module, pname))
mlzlog.getLogger('cached values').warn(
'no cached value for %s:%s' % (self._module, pname))
val = self._node.getParameter(self._module, pname)[0]
return val
except Exception:

View File

@ -82,109 +82,119 @@ p, li { white-space: pre-wrap; }
<string>NodeInfo</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Contact point:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="contactPointLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Equipment ID:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="equipmentIdLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Protocol version:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="protocolVersionLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Description:</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QLabel" name="nodeDescriptionLabel">
<property name="text">
<string>Description
long line</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
<widget class="QScrollArea" name="scrollArea_2">
<property name="widgetResizable">
<bool>true</bool>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
<widget class="QWidget" name="scrollAreaWidgetContents_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>610</width>
<height>413</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Contact point:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="contactPointLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Equipment ID:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="equipmentIdLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Protocol version:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="protocolVersionLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Description:</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QLabel" name="nodeDescriptionLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>aaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaa</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
<zorder>label_2</zorder>
<zorder>gridLayoutWidget</zorder>
</widget>
</widget>
</item>
</layout>
</widget>

View File

@ -49,7 +49,6 @@ CONFIG = {
}
class lazy_property(object):
"""A property that calculates its value only once."""

View File

@ -49,7 +49,12 @@ from secop.lib import formatExtendedStack, formatException
class Dispatcher(object):
def __init__(self, logger, options):
self.equipment_id = options.pop('equipment_id')
# to avoid errors, we want to eat all options here
self.equipment_id = options['equipment_id']
self.nodeopts = {}
for k in list(options):
self.nodeopts[k] = options.pop(k)
self.log = logger
# map ALL modulename -> moduleobj
self._modules = {}
@ -227,6 +232,7 @@ class Dispatcher(object):
result['equipment_id'] = self.equipment_id
result['firmware'] = 'The SECoP playground'
result['version'] = "2017.07"
result.update(self.nodeopts)
# XXX: what else?
return result

View File

@ -107,7 +107,8 @@ class Server(object):
deviceopts = []
interfaceopts = []
equipment_id = 'unknown'
equipment_id = None
nodeopts = []
for section in parser.sections():
if section.lower().startswith('device '):
# device section
@ -135,11 +136,30 @@ class Server(object):
(self._cfgfile, ifname))
# all went well so far
interfaceopts.append([ifname, ifopts])
if parser.has_option('equipment', 'id'):
equipment_id = parser.get('equipment', 'id').replace(' ', '_')
if section.lower().startswith('equipment ') or section.lower().startswith('node '):
if equipment_id is not None:
raise ConfigError('cfgfile %r: only one [node <id>] section allowed, found another [%s]!' % (
self._cfgfile, section))
# equipment/node settings
equipment_id = section.split(' ', 1)[1].replace(' ', '_')
nodeopts = dict(item for item in parser.items(section))
nodeopts['equipment_id'] = equipment_id
nodeopts['id'] = equipment_id
# MAGIC: transform \n.\n into \n\n which are normally stripped
# by the ini parser
for k in nodeopts:
v = nodeopts[k]
while '\n.\n' in v:
v = v.replace('\n.\n', '\n\n')
nodeopts[k] = v
if equipment_id is None:
self.log.error('Need a [node <id>] section, none found!')
raise ConfigError(
'cfgfile %r: need an [node <id>] option!' % (self._cfgfile))
self._dispatcher = self._buildObject(
'Dispatcher', Dispatcher, dict(equipment_id=equipment_id))
'Dispatcher', Dispatcher, nodeopts)
self._processInterfaceOptions(interfaceopts)
self._processModuleOptions(deviceopts)

View File

@ -75,7 +75,8 @@ class GarfieldMagnet(SequencerMixin, Drivable):
default=(1.0, 0.0, 0.0, 0.0, 0.0)),
'calibrationtable': PARAM('Map of Coefficients for calibration per symmetry setting',
datatype=StructOf(symmetric=ArrayOf(FloatRange(), 5, 5),
short=ArrayOf(FloatRange(), 5, 5),
short=ArrayOf(
FloatRange(), 5, 5),
asymmetric=ArrayOf(FloatRange(), 5, 5)), export=False),
}

View File

@ -35,17 +35,17 @@ uidir = path.join(path.dirname(__file__), 'secop', 'gui', 'ui')
uis = [path.join('gui', 'ui', entry) for entry in listdir(uidir)]
setup(
name = 'secop-core',
version = secop.version.get_version(),
license = 'GPL',
author = 'Enrico Faulhaber',
author_email = 'enrico.faulhaber@frm2.tum.de',
description = 'SECoP Playground core system',
packages = find_packages(),
package_data = {'secop': ['RELEASE-VERSION'] + uis},
data_files = [('/etc/init.d', ['etc/secop-server'])],
scripts = scripts,
classifiers = [
name='secop-core',
version=secop.version.get_version(),
license='GPL',
author='Enrico Faulhaber',
author_email='enrico.faulhaber@frm2.tum.de',
description='SECoP Playground core system',
packages=find_packages(),
package_data={'secop': ['RELEASE-VERSION'] + uis},
data_files=[('/etc/init.d', ['etc/secop-server'])],
scripts=scripts,
classifiers=[
'Development Status :: 6 - Mature',
'Intended Audience :: Developers',
'Intended Audience :: Science/Research',

View File

@ -24,14 +24,17 @@
import pytest
import sys
sys.path.insert(0, sys.path[0]+'/..')
sys.path.insert(0, sys.path[0] + '/..')
from collections import OrderedDict
from secop.client.baseclient import Client
# define Test-only connection object
class TestConnect(object):
callbacks = []
def writeline(self, line):
pass
@ -49,35 +52,37 @@ def clientobj(request):
print (" TEARDOWN ClientObj")
def test_describing_data_decode(clientobj):
assert OrderedDict([('a',1)]) == clientobj._decode_list_to_ordereddict(['a',1])
assert {'modules':{}, 'properties':{}} == clientobj._decode_substruct(['modules'],{})
describing_data = {'equipment_id': 'eid',
'modules': ['LN2', {'commands': [],
'interfaces': ['Readable', 'Module'],
'parameters': ['value', {'datatype': ['double'],
'description': 'current value',
def test_describing_data_decode(clientobj):
assert OrderedDict(
[('a', 1)]) == clientobj._decode_list_to_ordereddict(['a', 1])
assert {'modules': {}, 'properties': {}
} == clientobj._decode_substruct(['modules'], {})
describing_data = {'equipment_id': 'eid',
'modules': ['LN2', {'commands': [],
'interfaces': ['Readable', 'Module'],
'parameters': ['value', {'datatype': ['double'],
'description': 'current value',
'readonly': True,
}
]
}
]
}
decoded_data = {'modules': {'LN2': {'commands': {},
'parameters': {'value': {'datatype': ['double'],
'description': 'current value',
}
]
}
]
}
decoded_data = {'modules': {'LN2': {'commands': {},
'parameters': {'value': {'datatype': ['double'],
'description': 'current value',
'readonly': True,
}
},
}
},
'properties': {'interfaces': ['Readable', 'Module']}
}
},
}
},
'properties': {'equipment_id': 'eid',
}
}
}
}
a = clientobj._decode_substruct(['modules'], describing_data)
for modname, module in a['modules'].items():
a['modules'][modname] = clientobj._decode_substruct(['parameters', 'commands'], module)
a['modules'][modname] = clientobj._decode_substruct(
['parameters', 'commands'], module)
assert a == decoded_data

View File

@ -25,7 +25,7 @@
import pytest
import sys
sys.path.insert(0, sys.path[0]+'/..')
sys.path.insert(0, sys.path[0] + '/..')
from secop.datatypes import DataType, FloatRange, IntRange, \
EnumType, BLOBType, StringType, BoolType, ArrayOf, TupleOf, StructOf, \
@ -41,6 +41,7 @@ def test_DataType():
dt.validate('')
dt.export()
def test_FloatRange():
dt = FloatRange(-3.14, 3.14)
assert dt.as_json == ['double', -3.14, 3.14]
@ -52,16 +53,17 @@ def test_FloatRange():
with pytest.raises(ValueError):
dt.validate('XX')
with pytest.raises(ValueError):
dt.validate([19,'X'])
dt.validate([19, 'X'])
dt.validate(1)
dt.validate(0)
assert dt.export(-2.718) == -2.718
with pytest.raises(ValueError):
FloatRange('x','Y')
FloatRange('x', 'Y')
dt = FloatRange()
assert dt.as_json == ['double']
def test_IntRange():
dt = IntRange(-3, 3)
assert dt.as_json == ['int', -3, 3]
@ -73,21 +75,22 @@ def test_IntRange():
with pytest.raises(ValueError):
dt.validate('XX')
with pytest.raises(ValueError):
dt.validate([19,'X'])
dt.validate([19, 'X'])
dt.validate(1)
dt.validate(0)
with pytest.raises(ValueError):
IntRange('xc','Yx')
IntRange('xc', 'Yx')
dt = IntRange()
assert dt.as_json == ['int']
def test_EnumType():
# test constructor catching illegal arguments
with pytest.raises(ValueError):
EnumType(1)
with pytest.raises(ValueError):
EnumType('a',b=0)
EnumType('a', b=0)
dt = EnumType(a=3, c=7, stuff=1)
assert dt.as_json == ['enum', dict(a=3, c=7, stuff=1)]
@ -99,7 +102,7 @@ def test_EnumType():
with pytest.raises(ValueError):
dt.validate('XX')
with pytest.raises(TypeError):
dt.validate([19,'X'])
dt.validate([19, 'X'])
assert dt.validate('a') == 'a'
assert dt.validate('stuff') == 'stuff'
@ -113,8 +116,14 @@ def test_EnumType():
with pytest.raises(ValueError):
dt.export(2)
def test_BLOBType():
# test constructor catching illegal arguments
dt = BLOBType()
assert dt.as_json == ['blob']
dt = BLOBType(10)
assert dt.as_json == ['blob', 10]
dt = BLOBType(3, 10)
assert dt.as_json == ['blob', 10, 3]
@ -135,6 +144,11 @@ def test_BLOBType():
def test_StringType():
# test constructor catching illegal arguments
dt = StringType()
assert dt.as_json == ['string']
dt = StringType(12)
assert dt.as_json == ['string', 12]
dt = StringType(4, 11)
assert dt.as_json == ['string', 11, 4]
@ -178,16 +192,21 @@ def test_ArrayOf():
# test constructor catching illegal arguments
with pytest.raises(ValueError):
ArrayOf(int)
dt = ArrayOf(IntRange(-10,10),1,3)
dt = ArrayOf(IntRange(-10, 10))
assert dt.as_json == ['array', ['int', -10, 10]]
dt = ArrayOf(IntRange(-10, 10), 5)
assert dt.as_json == ['array', ['int', -10, 10], 5]
dt = ArrayOf(IntRange(-10, 10), 1, 3)
assert dt.as_json == ['array', ['int', -10, 10], 3, 1]
with pytest.raises(ValueError):
dt.validate(9)
with pytest.raises(ValueError):
dt.validate('av')
assert dt.validate([1,2,3]) == [1,2,3]
assert dt.validate([1, 2, 3]) == [1, 2, 3]
assert dt.export([1,2,3]) == [1,2,3]
assert dt.export([1, 2, 3]) == [1, 2, 3]
def test_TupleOf():
@ -195,17 +214,17 @@ def test_TupleOf():
with pytest.raises(ValueError):
TupleOf(2)
dt = TupleOf(IntRange(-10,10), BoolType())
dt = TupleOf(IntRange(-10, 10), BoolType())
assert dt.as_json == ['tuple', [['int', -10, 10], ['bool']]]
with pytest.raises(ValueError):
dt.validate(9)
with pytest.raises(ValueError):
dt.validate([99,'X'])
dt.validate([99, 'X'])
assert dt.validate([1,True]) == [1,True]
assert dt.validate([1, True]) == [1, True]
assert dt.export([1,True]) == [1,True]
assert dt.export([1, True]) == [1, True]
def test_StructOf():
@ -218,19 +237,20 @@ def test_StructOf():
dt = StructOf(a_string=StringType(), an_int=IntRange(0, 999))
assert dt.as_json == ['struct', {'a_string': ['string'],
'an_int': ['int', 0, 999],
}]
}]
with pytest.raises(ValueError):
dt.validate(9)
with pytest.raises(ValueError):
dt.validate([99,'X'])
dt.validate([99, 'X'])
with pytest.raises(ValueError):
dt.validate(dict(a_string='XXX', an_int=1811))
assert dt.validate(dict(a_string='XXX', an_int=8)) == {'a_string': 'XXX',
'an_int': 8}
assert dt.export({'an_int':13, 'a_string':'WFEC'}) == {'a_string': 'WFEC',
'an_int': 13}
assert dt.export({'an_int': 13, 'a_string': 'WFEC'}) == {'a_string': 'WFEC',
'an_int': 13}
def test_get_datatype():
with pytest.raises(ValueError):
@ -252,51 +272,46 @@ def test_get_datatype():
assert isinstance(get_datatype(['int', -10, 10]), IntRange)
with pytest.raises(ValueError):
get_datatype(['int',10, -10])
get_datatype(['int', 10, -10])
with pytest.raises(ValueError):
get_datatype(['int', 1, 2, 3])
assert isinstance(get_datatype(['double']), FloatRange)
assert isinstance(get_datatype(['double', -2.718]), FloatRange)
assert isinstance(get_datatype(['double', None, 3.14]), FloatRange)
assert isinstance(get_datatype(['double', -9.9, 11.1]), FloatRange)
with pytest.raises(ValueError):
get_datatype(['double',10, -10])
get_datatype(['double', 10, -10])
with pytest.raises(ValueError):
get_datatype(['double', 1, 2, 3])
with pytest.raises(ValueError):
get_datatype(['enum'])
assert isinstance(get_datatype(['enum', dict(a=-2.718)]), EnumType)
with pytest.raises(ValueError):
get_datatype(['enum',10, -10])
get_datatype(['enum', 10, -10])
with pytest.raises(ValueError):
get_datatype(['enum', [1, 2, 3]])
assert isinstance(get_datatype(['blob']), BLOBType)
assert isinstance(get_datatype(['blob', 1]), BLOBType)
assert isinstance(get_datatype(['blob', 1, 10]), BLOBType)
with pytest.raises(ValueError):
get_datatype(['blob',10, -10])
get_datatype(['blob', 10, -10])
with pytest.raises(ValueError):
get_datatype(['blob',10, -10, 1])
get_datatype(['blob', 10, -10, 1])
assert isinstance(get_datatype(['string']), StringType)
assert isinstance(get_datatype(['string', 1]), StringType)
assert isinstance(get_datatype(['string', 1, 10]), StringType)
with pytest.raises(ValueError):
get_datatype(['string',10, -10])
get_datatype(['string', 10, -10])
with pytest.raises(ValueError):
get_datatype(['string',10, -10, 1])
get_datatype(['string', 10, -10, 1])
with pytest.raises(ValueError):
get_datatype(['array'])
@ -314,7 +329,6 @@ def test_get_datatype():
assert isinstance(get_datatype(['array', ['blob'], 1, 10]), ArrayOf)
with pytest.raises(ValueError):
get_datatype(['tuple'])
with pytest.raises(ValueError):
@ -322,15 +336,15 @@ def test_get_datatype():
with pytest.raises(ValueError):
get_datatype(['tuple', [1], 2, 3])
assert isinstance(get_datatype(['tuple', [['blob']]]), TupleOf)
assert isinstance(get_datatype(['tuple', [['blob']]]).subtypes[0], BLOBType)
assert isinstance(get_datatype(
['tuple', [['blob']]]).subtypes[0], BLOBType)
with pytest.raises(ValueError):
get_datatype(['tuple', [['blob']], -10])
with pytest.raises(ValueError):
get_datatype(['tuple', [['blob']], -10, 10])
assert isinstance(get_datatype(['tuple', [['blob'],['int']]]), TupleOf)
assert isinstance(get_datatype(['tuple', [['blob'], ['int']]]), TupleOf)
with pytest.raises(ValueError):
get_datatype(['struct'])
@ -338,12 +352,14 @@ def test_get_datatype():
get_datatype(['struct', 1])
with pytest.raises(ValueError):
get_datatype(['struct', [1], 2, 3])
assert isinstance(get_datatype(['struct', {'blob':['blob']}]), StructOf)
assert isinstance(get_datatype(['struct', {'blob':['blob']}]).named_subtypes['blob'], BLOBType)
assert isinstance(get_datatype(['struct', {'blob': ['blob']}]), StructOf)
assert isinstance(get_datatype(
['struct', {'blob': ['blob']}]).named_subtypes['blob'], BLOBType)
with pytest.raises(ValueError):
get_datatype(['struct', [['blob']], -10])
with pytest.raises(ValueError):
get_datatype(['struct', [['blob']], -10, 10])
assert isinstance(get_datatype(['struct', {'blob':['blob'], 'int':['int']}]), StructOf)
assert isinstance(get_datatype(
['struct', {'blob': ['blob'], 'int':['int']}]), StructOf)