Merge branch 'dataforms' into 'master'

Replace @nested_property with @property in dataforms

See merge request !124
This commit is contained in:
Yann Leboulanger 2017-08-27 22:08:48 +02:00
commit aa087c34ad
1 changed files with 268 additions and 297 deletions

View File

@ -49,14 +49,6 @@ class ExtendedNode(nbxmpp.Node, object):
extend.__class__ = cls extend.__class__ = cls
return extend return extend
# helper decorator to create properties in cleaner way
def nested_property(f):
ret = f()
p = {'doc': f.__doc__}
for v in ('fget', 'fset', 'fdel', 'doc'):
if v in ret.keys(): p[v]=ret[v]
return property(**p)
# helper to create fields from scratch # helper to create fields from scratch
def Field(typ, **attrs): def Field(typ, **attrs):
''' Helper function to create a field of given type. ''' ''' Helper function to create a field of given type. '''
@ -131,123 +123,115 @@ class DataField(ExtendedNode):
self.required = required self.required = required
self.options = options self.options = options
@nested_property @property
def type_(): # pylint: disable=E0202 def type_(self):
""" """
Type of field. Recognized values are: 'boolean', 'fixed', 'hidden', Type of field. Recognized values are: 'boolean', 'fixed', 'hidden',
'jid-multi', 'jid-single', 'list-multi', 'list-single', 'text-multi', 'jid-multi', 'jid-single', 'list-multi', 'list-single', 'text-multi',
'text-private', 'text-single'. If you set this to something different, 'text-private', 'text-single'. If you set this to something different,
DataField will store given name, but treat all data as text-single DataField will store given name, but treat all data as text-single
""" """
def fget(self): t = self.getAttr('type')
t = self.getAttr('type') if t is None:
if t is None: return 'text-single'
return 'text-single' return t
return t
def fset(self, value): @type_.setter
assert isinstance(value, str) def type_(self, value):
self.setAttr('type', value) assert isinstance(value, str)
self.setAttr('type', value)
return locals() @property
def var(self):
@nested_property
def var(): # pylint: disable=E0202
""" """
Field identifier Field identifier
""" """
def fget(self): return self.getAttr('var')
return self.getAttr('var')
def fset(self, value): @var.setter
assert isinstance(value, str) def var(self, value):
self.setAttr('var', value) assert isinstance(value, str)
self.setAttr('var', value)
def fdel(self): @var.deleter
self.delAttr('var') def var(self):
self.delAttr('var')
return locals() @property
def label(self):
@nested_property
def label(): # pylint: disable=E0202
""" """
Human-readable field name Human-readable field name
""" """
def fget(self): l = self.getAttr('label')
l = self.getAttr('label') if not l:
if not l: l = self.var
l = self.var return l
return l
def fset(self, value): @label.setter
assert isinstance(value, str) def label(self, value):
self.setAttr('label', value) assert isinstance(value, str)
self.setAttr('label', value)
def fdel(self): @label.deleter
if self.getAttr('label'): def label(self):
self.delAttr('label') if self.getAttr('label'):
self.delAttr('label')
return locals() @property
def description(self):
@nested_property
def description():
""" """
Human-readable description of field meaning Human-readable description of field meaning
""" """
def fget(self): return self.getTagData('desc') or ''
return self.getTagData('desc') or ''
def fset(self, value): @description.setter
assert isinstance(value, str) def description(self, value):
if value == '': assert isinstance(value, str)
fdel(self) if value == '':
else: del self.description
self.setTagData('desc', value) else:
self.setTagData('desc', value)
def fdel(self): @description.deleter
t = self.getTag('desc') def description(self):
if t is not None: t = self.getTag('desc')
self.delChild(t) if t is not None:
self.delChild(t)
return locals() @property
def required(self):
@nested_property
def required(): # pylint: disable=E0202
""" """
Controls whether this field required to fill. Boolean Controls whether this field required to fill. Boolean
""" """
def fget(self): return bool(self.getTag('required'))
return bool(self.getTag('required'))
def fset(self, value): @required.setter
t = self.getTag('required') def required(self, value):
if t and not value: t = self.getTag('required')
self.delChild(t) if t and not value:
elif not t and value: self.delChild(t)
self.addChild('required') elif not t and value:
self.addChild('required')
return locals() @property
def media(self):
@nested_property
def media():
""" """
Media data Media data
""" """
def fget(self): media = self.getTag('media', namespace=nbxmpp.NS_DATA_MEDIA)
media = self.getTag('media', namespace=nbxmpp.NS_DATA_MEDIA) if media:
if media: return Media(media)
return Media(media)
def fset(self, value): @media.setter
fdel(self) def media(self, value):
self.addChild(node=value) del self.media
self.addChild(node=value)
def fdel(self): @media.deleter
t = self.getTag('media') def media(self):
if t is not None: t = self.getTag('media')
self.delChild(t) if t is not None:
self.delChild(t)
return locals()
def is_valid(self): def is_valid(self):
return True return True
@ -256,146 +240,141 @@ class Uri(nbxmpp.Node):
def __init__(self, uri_tag): def __init__(self, uri_tag):
nbxmpp.Node.__init__(self, node=uri_tag) nbxmpp.Node.__init__(self, node=uri_tag)
@nested_property @property
def type_(): def type_(self):
""" """
uri type uri type
""" """
def fget(self): return self.getAttr('type')
return self.getAttr('type')
def fset(self, value): @type_.setter
self.setAttr('type', value) def type_(self, value):
self.setAttr('type', value)
def fdel(self): @type_.deleter
self.delAttr('type') def type_(self):
self.delAttr('type')
return locals() @property
def uri_data(self):
@nested_property
def uri_data():
""" """
uri data uri data
""" """
def fget(self): return self.getData()
return self.getData()
def fset(self, value): @uri_data.setter
self.setData(value) def uri_data(self, value):
self.setData(value)
def fdel(self): @uri_data.deleter
self.setData(None) def uri_data(self):
self.setData(None)
return locals()
class Media(nbxmpp.Node): class Media(nbxmpp.Node):
def __init__(self, media_tag): def __init__(self, media_tag):
nbxmpp.Node.__init__(self, node=media_tag) nbxmpp.Node.__init__(self, node=media_tag)
@nested_property @property
def uris(): def uris(self):
""" """
URIs of the media element. URIs of the media element.
""" """
def fget(self): return map(Uri, self.getTags('uri'))
return map(Uri, self.getTags('uri'))
def fset(self, value): @uris.setter
fdel(self) def uris(self, value):
for uri in value: del self.uris
self.addChild(node=uri) for uri in value:
self.addChild(node=uri)
def fdel(self): @uris.deleter
for element in self.getTags('uri'): def uris(self):
self.delChild(element) for element in self.getTags('uri'):
self.delChild(element)
return locals()
class BooleanField(DataField): class BooleanField(DataField):
@nested_property @property
def value(): # pylint: disable=E0202 def value(self):
""" """
Value of field. May contain True, False or None Value of field. May contain True, False or None
""" """
def fget(self): v = self.getTagData('value')
v = self.getTagData('value') if v in ('0', 'false'):
if v in ('0', 'false'): return False
return False if v in ('1', 'true'):
if v in ('1', 'true'): return True
return True if v is None:
if v is None: return False # default value is False
return False # default value is False raise WrongFieldValue
raise WrongFieldValue
def fset(self, value): @value.setter
self.setTagData('value', value and '1' or '0') def value(self, value):
self.setTagData('value', value and '1' or '0')
def fdel(self, value): @value.deleter
t = self.getTag('value') def value(self):
if t is not None: t = self.getTag('value')
self.delChild(t) if t is not None:
self.delChild(t)
return locals()
class StringField(DataField): class StringField(DataField):
""" """
Covers fields of types: fixed, hidden, text-private, text-single Covers fields of types: fixed, hidden, text-private, text-single
""" """
@nested_property @property
def value(): # pylint: disable=E0202 def value(self):
""" """
Value of field. May be any string Value of field. May be any string
""" """
def fget(self): return self.getTagData('value') or ''
return self.getTagData('value') or ''
def fset(self, value): @value.setter
assert isinstance(value, str) def value(self, value):
if value == '' and not self.required: assert isinstance(value, str)
return fdel(self) if value == '' and not self.required:
self.setTagData('value', value) del self.value
return
self.setTagData('value', value)
def fdel(self): @value.deleter
try: def value(self):
self.delChild(self.getTag('value')) try:
except ValueError: # if there already were no value tag self.delChild(self.getTag('value'))
pass except ValueError: # if there already were no value tag
pass
return locals()
class ListField(DataField): class ListField(DataField):
""" """
Covers fields of types: jid-multi, jid-single, list-multi, list-single Covers fields of types: jid-multi, jid-single, list-multi, list-single
""" """
@nested_property @property
def options(): # pylint: disable=E0202 def options(self):
""" """
Options Options
""" """
def fget(self): options = []
options = [] for element in self.getTags('option'):
for element in self.getTags('option'): v = element.getTagData('value')
v = element.getTagData('value') if v is None:
if v is None: raise WrongFieldValue
raise WrongFieldValue l = element.getAttr('label')
l = element.getAttr('label') if not l:
if not l: l = v
l = v options.append((l, v))
options.append((l, v)) return options
return options
def fset(self, values): @options.setter
fdel(self) def options(self, values):
for value, label in values: del self.options
self.addChild('option', {'label': label}).setTagData('value', value) for value, label in values:
self.addChild('option', {'label': label}).setTagData('value', value)
def fdel(self): @options.deleter
for element in self.getTags('option'): def options(self):
self.delChild(element) for element in self.getTags('option'):
self.delChild(element)
return locals()
def iter_options(self): def iter_options(self):
for element in self.iterTags('option'): for element in self.iterTags('option'):
@ -438,27 +417,26 @@ class ListMultiField(ListField):
Covers list-multi fields Covers list-multi fields
""" """
@nested_property @property
def values(): def values(self):
""" """
Values held in field Values held in field
""" """
def fget(self): values = []
values = [] for element in self.getTags('value'):
for element in self.getTags('value'): values.append(element.getData())
values.append(element.getData()) return values
return values
def fset(self, values): @values.setter
fdel(self) def values(self, values):
for value in values: del self.values
self.addChild('value').setData(value) for value in values:
self.addChild('value').setData(value)
def fdel(self): @values.deleter
for element in self.getTags('value'): def values(self):
self.delChild(element) for element in self.getTags('value'):
self.delChild(element)
return locals()
def iter_values(self): def iter_values(self):
for element in self.getTags('value'): for element in self.getTags('value'):
@ -488,29 +466,28 @@ class JidMultiField(ListMultiField):
return True return True
class TextMultiField(DataField): class TextMultiField(DataField):
@nested_property @property
def value(): # pylint: disable=E0202 def value(self):
""" """
Value held in field Value held in field
""" """
def fget(self): value = ''
value = '' for element in self.iterTags('value'):
for element in self.iterTags('value'): value += '\n' + element.getData()
value += '\n' + element.getData() return value[1:]
return value[1:]
def fset(self, value): @value.setter
fdel(self) def value(self, value):
if value == '': del self.value
return if value == '':
for line in value.split('\n'): return
self.addChild('value').setData(line) for line in value.split('\n'):
self.addChild('value').setData(line)
def fdel(self): @value.deleter
for element in self.getTags('value'): def value(self):
self.delChild(element) for element in self.getTags('value'):
self.delChild(element)
return locals()
class DataRecord(ExtendedNode): class DataRecord(ExtendedNode):
""" """
@ -539,26 +516,25 @@ class DataRecord(ExtendedNode):
self.delChild(field) self.delChild(field)
self.fields = fields self.fields = fields
@nested_property @property
def fields(): # pylint: disable=E0202 def fields(self):
""" """
List of fields in this record List of fields in this record
""" """
def fget(self): return self.getTags('field')
return self.getTags('field')
def fset(self, fields): @fields.setter
fdel(self) def fields(self, fields):
for field in fields: del self.fields
if not isinstance(field, DataField): for field in fields:
ExtendField(field) if not isinstance(field, DataField):
self.addChild(node=field) ExtendField(field)
self.addChild(node=field)
def fdel(self): @fields.deleter
for element in self.getTags('field'): def fields(self):
self.delChild(element) for element in self.getTags('field'):
self.delChild(element)
return locals()
def iter_fields(self): def iter_fields(self):
""" """
@ -597,68 +573,64 @@ class DataForm(ExtendedNode):
if instructions is not None: if instructions is not None:
self.instructions=instructions self.instructions=instructions
@nested_property @property
def type_(): # pylint: disable=E0202 def type_(self):
""" """
Type of the form. Must be one of: 'form', 'submit', 'cancel', 'result'. Type of the form. Must be one of: 'form', 'submit', 'cancel', 'result'.
'form' - this form is to be filled in; you will be able soon to do: 'form' - this form is to be filled in; you will be able soon to do:
filledform = DataForm(replyto=thisform) filledform = DataForm(replyto=thisform)
""" """
def fget(self): return self.getAttr('type')
return self.getAttr('type')
def fset(self, type_): @type_.setter
assert type_ in ('form', 'submit', 'cancel', 'result') def type_(self, type_):
self.setAttr('type', type_) assert type_ in ('form', 'submit', 'cancel', 'result')
self.setAttr('type', type_)
return locals() @property
def title(self):
@nested_property
def title(): # pylint: disable=E0202
""" """
Title of the form Title of the form
Human-readable, should not contain any \\r\\n. Human-readable, should not contain any \\r\\n.
""" """
def fget(self): return self.getTagData('title')
return self.getTagData('title')
def fset(self, title): @title.setter
self.setTagData('title', title) def title(self, title):
self.setTagData('title', title)
def fdel(self): @title.deleter
try: def title(self):
self.delChild('title') try:
except ValueError: self.delChild('title')
pass except ValueError:
pass
return locals() @property
def instructions(self):
@nested_property
def instructions(): # pylint: disable=E0202
""" """
Instructions for this form Instructions for this form
Human-readable, may contain \\r\\n. Human-readable, may contain \\r\\n.
""" """
# TODO: the same code is in TextMultiField. join them # TODO: the same code is in TextMultiField. join them
def fget(self): value = ''
value = '' for valuenode in self.getTags('instructions'):
for valuenode in self.getTags('instructions'): value += '\n' + valuenode.getData()
value += '\n' + valuenode.getData() return value[1:]
return value[1:]
def fset(self, value): @instructions.setter
fdel(self) def instructions(self, value):
if value == '': return del self.instructions
for line in value.split('\n'): if value == '': return
self.addChild('instructions').setData(line) for line in value.split('\n'):
self.addChild('instructions').setData(line)
def fdel(self): @instructions.deleter
for value in self.getTags('instructions'): def instructions(self):
self.delChild(value) for value in self.getTags('instructions'):
self.delChild(value)
return locals()
class SimpleDataForm(DataForm, DataRecord): class SimpleDataForm(DataForm, DataRecord):
def __init__(self, type_=None, title=None, instructions=None, fields=None, \ def __init__(self, type_=None, title=None, instructions=None, fields=None, \
@ -711,44 +683,43 @@ class MultipleDataForm(DataForm):
reported_tag = self.getTag('reported') reported_tag = self.getTag('reported')
self.reported = DataRecord(extend=reported_tag) self.reported = DataRecord(extend=reported_tag)
@nested_property @property
def items(): # pylint: disable=E0202 def items(self):
""" """
A list of all records A list of all records
""" """
def fget(self): return list(self.iter_records())
return list(self.iter_records())
def fset(self, records): @items.setter
fdel(self) def items(self, records):
for record in records: del self.items
if not isinstance(record, DataRecord): for record in records:
DataRecord(extend=record) if not isinstance(record, DataRecord):
self.addChild(node=record) DataRecord(extend=record)
self.addChild(node=record)
def fdel(self): @items.deleter
for record in self.getTags('item'): def items(self):
self.delChild(record) for record in self.getTags('item'):
self.delChild(record)
return locals()
def iter_records(self): def iter_records(self):
for record in self.getTags('item'): for record in self.getTags('item'):
yield record yield record
# @nested_property # @property
# def reported(): # def reported(self):
# """ # """
# DataRecord that contains descriptions of fields in records # DataRecord that contains descriptions of fields in records
# """ # """
# def fget(self): # return self.getTag('reported')
# return self.getTag('reported')
# def fset(self, record):
# try:
# self.delChild('reported')
# except:
# pass
# #
# record.setName('reported') # @reported.setter
# self.addChild(node=record) # def reported(self, record):
# return locals() # try:
# self.delChild('reported')
# except:
# pass
#
# record.setName('reported')
# self.addChild(node=record)