aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas "Cakeisalie5" Touhey <thomas@touhey.fr>2018-10-05 13:19:38 +0200
committerThomas "Cakeisalie5" Touhey <thomas@touhey.fr>2018-10-05 13:19:38 +0200
commit01943b11bb2c6822aea034bb5939e0001386a138 (patch)
tree4d6afa01c4bba2457dfe5efeb4028132809931e3
parent729800755e4f341b57026e4531f64e72448db4c2 (diff)
Corrected a few things.
-rwxr-xr-xsgdfi/__main__.py11
-rwxr-xr-xsgdfi/_db.py28
-rwxr-xr-x[-rw-r--r--]sgdfi/_dbs/_funcdata.py0
-rwxr-xr-x[-rw-r--r--]sgdfi/_dbs/_regime.py0
-rwxr-xr-xsgdfi/_manager.py353
-rwxr-xr-xsgdfi/_repr.py210
-rwxr-xr-xsgdfi/_util.py50
7 files changed, 413 insertions, 239 deletions
diff --git a/sgdfi/__main__.py b/sgdfi/__main__.py
index 39ef0d1..4e6776e 100755
--- a/sgdfi/__main__.py
+++ b/sgdfi/__main__.py
@@ -22,9 +22,9 @@ def test_session():
s = get_session()
- #ret = s.get_ops('4', "yCbyTmNDHpp8CotDhWoEkQ==")
+ ret = s.get_ops('4', "yCbyTmNDHpp8CotDhWoEkQ==")
#ret = s.get_place('ONCt5ZDoIUZIXlYv9QyGuw==')
- ret = s.get_structure('oA0pOxhIxVX5eh6fTNvgzg==')
+ #ret = s.get_structure('oA0pOxhIxVX5eh6fTNvgzg==')
if type(ret) == list:
for e in ret:
@@ -43,16 +43,19 @@ def test_repr():
op = _Operation()
op.time = _datetime(2018, 10, 1, 0, 17, 38)
op.type = "Individu / Abonnement "
- op.author = "LEFEBVRE CAROLE"
+ op.author.name = "LEFEBVRE CAROLE"
op.fields = "Revue: Revue Louveteau-Jeannette, Type: Gratuit, " \
"Fin: 31/08/2019, Prix: 0,00€ "
ad = _Adherent()
ad.iid = "yCbyTmNDHpp8CotDhWoEkQ=="
+ ad.name = "TOUHEY THOMAS"
ad.function = _Function.CHEF_PIONNIER_CARAVELLE
- print(ad)
op.related.add(ad)
+ print(op)
+
+ #print(op.fields['Type'])
def test_save():
""" Test the saving. """
diff --git a/sgdfi/_db.py b/sgdfi/_db.py
index 03359a5..d1dc6aa 100755
--- a/sgdfi/_db.py
+++ b/sgdfi/_db.py
@@ -34,26 +34,38 @@ class Database:
# Feed the database with fetched (and possibly incomplete) data.
# ---
- def add_op(self, op):
- """ Add an operation. """
+ def add_structure(self, st):
+ """ Add a structure. """
# TODO
pass
- def add_place(self, pl):
- """ Add a place. """
+ def add_adherent(self, ad):
+ """ Add an adherent. """
# TODO
pass
- def add_structure(self, st):
- """ Add a structure. """
+ def add_rally_registration(self, rreg):
+ """ Add a rally registration. """
# TODO
pass
- def add_adherent(self, ad):
- """ Add an adherent. """
+ def add_camp(self, cp):
+ """ Add a camp. """
+
+ # TODO
+ pass
+
+ def add_place(self, pl):
+ """ Add a place. """
+
+ # TODO
+ pass
+
+ def add_op(self, op):
+ """ Add an operation. """
# TODO
pass
diff --git a/sgdfi/_dbs/_funcdata.py b/sgdfi/_dbs/_funcdata.py
index fbc7bb0..fbc7bb0 100644..100755
--- a/sgdfi/_dbs/_funcdata.py
+++ b/sgdfi/_dbs/_funcdata.py
diff --git a/sgdfi/_dbs/_regime.py b/sgdfi/_dbs/_regime.py
index 3bcff3c..3bcff3c 100644..100755
--- a/sgdfi/_dbs/_regime.py
+++ b/sgdfi/_dbs/_regime.py
diff --git a/sgdfi/_manager.py b/sgdfi/_manager.py
index 8442817..aec2eb4 100755
--- a/sgdfi/_manager.py
+++ b/sgdfi/_manager.py
@@ -82,30 +82,6 @@ class _AjaxField:
return self.__text
-class _OperationManager:
- """ The operations manager. """
-
- def __init__(self, main):
- self.__main = main
-
- def add(self, op):
- if not isinstance(op, _Operation):
- raise ValueError("Expected an 'Operation'.")
-
- self.__main.add_op(op)
-
-class _PlaceManager:
- """ The place manager. """
-
- def __init__(self, main):
- self.__main = main
-
- def add(self, pl):
- if not isinstance(pl, _Place):
- raise ValueError("Expected a 'Place'.")
-
- self.__main.add_place(pl)
-
class _StructureManager:
""" The structure manager. """
@@ -130,6 +106,54 @@ class _AdherentManager:
self.__main.add_adherent(ad)
+class _RallyRegistrationManager:
+ """ The rally registration manager. """
+
+ def __init__(self, main):
+ self.__main = main
+
+ def add(self, rreg):
+ if not isinstance(rreg, _RallyRegistration):
+ raise ValueError("Expected a 'RellyRegistration'.")
+
+ self.__main.add_rally_registration(rreg)
+
+class _CampManager:
+ """ The camp manager. """
+
+ def __init__(self, main):
+ self.__main = main
+
+ def add(self, cp):
+ if not isinstance(cp, _Camp):
+ raise ValueError("Expected a 'Camp'.")
+
+ self.__main.add_camp(cp)
+
+class _PlaceManager:
+ """ The place manager. """
+
+ def __init__(self, main):
+ self.__main = main
+
+ def add(self, pl):
+ if not isinstance(pl, _Place):
+ raise ValueError("Expected a 'Place'.")
+
+ self.__main.add_place(pl)
+
+class _OperationManager:
+ """ The operations manager. """
+
+ def __init__(self, main):
+ self.__main = main
+
+ def add(self, op):
+ if not isinstance(op, _Operation):
+ raise ValueError("Expected an 'Operation'.")
+
+ self.__main.add_op(op)
+
# The main class.
_type = type
@@ -154,23 +178,14 @@ class Manager:
# Make the data manager.
- data = _Database(cache_dir = cache_dir)
- self.__ops = _OperationManager(data)
- self.__pls = _PlaceManager(data)
- self.__sts = _StructureManager(data)
- self.__ads = _AdherentManager(data)
+ self.__db = _Database(cache_dir = cache_dir)
- @property
- def operations(self):
- """ The operations manager. """
-
- return self.__ops
-
- @property
- def places(self):
- """ The places manager. """
-
- return self.__pls
+ self.__sts = _StructureManager(self.__db)
+ self.__ads = _AdherentManager(self.__db)
+ self.__rrs = _RallyRegistrationManager(self.__db)
+ self.__cps = _CampManager(self.__db)
+ self.__pls = _PlaceManager(self.__db)
+ self.__ops = _OperationManager(self.__db)
@property
def structures(self):
@@ -184,6 +199,30 @@ class Manager:
return self.__ads
+ @property
+ def rally_registrations(self):
+ """ The rally registrations manager. """
+
+ return self.__rrs
+
+ @property
+ def camps(self):
+ """ The camps manager. """
+
+ return self.__cps
+
+ @property
+ def places(self):
+ """ The places manager. """
+
+ return self.__pls
+
+ @property
+ def operations(self):
+ """ The operations manager. """
+
+ return self.__ops
+
# ---
# Save file management.
# ---
@@ -403,119 +442,6 @@ class Manager:
# HTML pages and fragments decoding.
# ---
- def __feed_json_functions(self, content):
- """ Decode the JSON functions autocompletion data and return
- them without feeding the database. """
-
- def _sfix(code):
- if code[-1:] == "L" or code[:1] == "3":
- return "_L"
- if code[-1:] == "N" or code[:1] == "6":
- return "_N"
- if code[-1:] == "T" or code[:1] == "9":
- return "_T"
- return ""
-
- def _cname(cname):
- """ Make a canonical name out of a name. """
-
- cfilt = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_'
-
- cname = cname.casefold().upper()
- cname = cname.replace(' - ', ' ')
- cname = cname.replace(' !', '')
- cname = cname.replace(' ', ' ')
- cname = cname.replace("D'", '')
- cname = cname.replace(' ', '_')
- cname = cname.replace('É', 'E')
- cname = cname.replace('À', 'A')
- cname = ''.join(filter(lambda c: c in cfilt, cname))
-
- return cname
-
- # First phase: load from the JSON and store as an array under the
- # constant name.
-
- funcs = {}
-
- for idx, value in enumerate(content):
- iid = int(value['id'])
- name = value['name']
-
- code, rest = name.split('(')
- code = code.strip()
- rest = rest.split(')')[0]
-
- rest = rest.split('/')
- nm = rest[0].strip()
- if len(rest) == 1:
- nf = None
- else:
- nf = '/'.join(rest[1:])
- nf = nf.strip()
- if nf == nm:
- nf = None
-
- cnames = [_cname(nm)]
- if nf is not None and _cname(nf) != _cname(nm):
- cnames.append(_cname(nf))
-
- func = _FunctionRawData(iid, code, nm, nf, cnames, idx + 1)
-
- for cname in cnames:
- if not cname in funcs:
- funcs[cname] = []
- funcs[cname].append(func)
-
- # Second phase: disambiguation.
-
- while True:
- try:
- cname = next(cn for cn in funcs.keys() if len(funcs[cn]) > 1)
- except StopIteration:
- break
-
- disf = funcs[cname]
- del funcs[cname]
-
- # We ought to find a solution to make other canonical names.
-
- codes = [func.code for func in disf]
- sfix = [_sfix(code) for code in codes]
- if len(sfix) > len(set(sfix)):
- raise ValueError(f"Could not deduplicate using scale: {codes}")
-
- for func, suffix in zip(disf, sfix):
- # Delete all the current function references.
-
- for cname in func.cnames:
- try:
- f = funcs[cname]
- except KeyError:
- continue
-
- f.remove(func)
- if not f:
- del funcs[cname]
-
- # Replace all of the cnames.
-
- func = func.add_cname_suffix(suffix)
-
- # Add all of the references again.
-
- for cname in func.cnames:
- if not cname in funcs:
- funcs[cname] = []
- funcs[cname].append(func)
-
- # Third phase: flatten the functions and we're done!
-
- funcs = list(set(func[0] for func in funcs.values()))
- funcs.sort(key = lambda x: x.srcorder)
-
- return funcs
-
def __feed_html_operations(self, content):
""" Decode the HTML operations from a BeautifulSoup decoded
content and feed it into the manager's operations. """
@@ -1029,8 +955,125 @@ class Manager:
return people
# ---
- # Export functions as a Python file to replace the one
- # in `_dbs/_funcdata.py`.
+ # JSON documents decoding.
+ # ---
+
+ def __feed_json_functions(self, content):
+ """ Decode the JSON functions autocompletion data and return
+ them without feeding the database. """
+
+ def _sfix(code):
+ if code[-1:] == "L" or code[:1] == "3":
+ return "_L"
+ if code[-1:] == "N" or code[:1] == "6":
+ return "_N"
+ if code[-1:] == "T" or code[:1] == "9":
+ return "_T"
+ return ""
+
+ def _cname(cname):
+ """ Make a canonical name out of a name. """
+
+ cfilt = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_'
+
+ cname = cname.casefold().upper()
+ cname = cname.replace(' - ', ' ')
+ cname = cname.replace(' !', '')
+ cname = cname.replace(' ', ' ')
+ cname = cname.replace("D'", '')
+ cname = cname.replace(' ', '_')
+ cname = cname.replace('É', 'E')
+ cname = cname.replace('À', 'A')
+ cname = ''.join(filter(lambda c: c in cfilt, cname))
+
+ return cname
+
+ # First phase: load from the JSON and store as an array under the
+ # constant name.
+
+ funcs = {}
+
+ for idx, value in enumerate(content):
+ iid = int(value['id'])
+ name = value['name']
+
+ code, rest = name.split('(')
+ code = code.strip()
+ rest = rest.split(')')[0]
+
+ rest = rest.split('/')
+ nm = rest[0].strip()
+ if len(rest) == 1:
+ nf = None
+ else:
+ nf = '/'.join(rest[1:])
+ nf = nf.strip()
+ if nf == nm:
+ nf = None
+
+ cnames = [_cname(nm)]
+ if nf is not None and _cname(nf) != _cname(nm):
+ cnames.append(_cname(nf))
+
+ func = _FunctionRawData(iid, code, nm, nf, cnames, idx + 1)
+
+ for cname in cnames:
+ if not cname in funcs:
+ funcs[cname] = []
+ funcs[cname].append(func)
+
+ # Second phase: disambiguation.
+
+ while True:
+ try:
+ cname = next(cn for cn in funcs.keys() if len(funcs[cn]) > 1)
+ except StopIteration:
+ break
+
+ disf = funcs[cname]
+ del funcs[cname]
+
+ # We ought to find a solution to make other canonical names.
+
+ codes = [func.code for func in disf]
+ sfix = [_sfix(code) for code in codes]
+ if len(sfix) > len(set(sfix)):
+ raise ValueError(f"Could not deduplicate using scale: {codes}")
+
+ for func, suffix in zip(disf, sfix):
+ # Delete all the current function references.
+
+ for cname in func.cnames:
+ try:
+ f = funcs[cname]
+ except KeyError:
+ continue
+
+ f.remove(func)
+ if not f:
+ del funcs[cname]
+
+ # Replace all of the cnames.
+
+ func = func.add_cname_suffix(suffix)
+
+ # Add all of the references again.
+
+ for cname in func.cnames:
+ if not cname in funcs:
+ funcs[cname] = []
+ funcs[cname].append(func)
+
+ # Third phase: flatten the functions and we're done!
+
+ funcs = list(set(func[0] for func in funcs.values()))
+ funcs.sort(key = lambda x: x.srcorder)
+
+ return funcs
+
+ # ---
+ # Export dynamically gathered data such as functions as Python files
+ # to replace some of those in the `_dbs` submodule.
# ---
def export_functions(self, functions, to = _stdout):
diff --git a/sgdfi/_repr.py b/sgdfi/_repr.py
index aa9d97f..3d73312 100755
--- a/sgdfi/_repr.py
+++ b/sgdfi/_repr.py
@@ -11,80 +11,137 @@ from ._util import IID, Enum as _Enum, \
Base as _Base, Property as _Property, \
IIDProperty as _IIDProperty, DateProperty as _DateProperty, \
BoolProperty as _BoolProperty, EnumProperty as _EnumProperty, \
- ArrayProperty as _ArrayProperty, TextProperty as _TextProperty
+ ArrayProperty as _ArrayProperty, TextProperty as _TextProperty, \
+ ObjectProperty as _ObjectProperty, ValueProperty as _ValueProperty
from ._dbs import OperationType, OperationTypeData as _OperationTypeData, \
Function, FunctionRawData, FunctionData as _FunctionData, StructureType, \
StructureTypeData as _StructureTypeData, StructureStatus, \
StructureStatusData as _StructureStatusData, \
AllocationsRegime
-__all__ = ["IID", "Structure", "Adherent", "RallyRegistration", "Camp",
- "Place", "Operation", "OperationType", "Function", "StructureType",
- "StructureStatus", "AllocationsRegime"]
+__all__ = ["IID", "Title", "Structure", "Adherent", "RallyRegistration",
+ "Camp", "Place", "Operation", "OperationType", "Function",
+ "StructureType", "StructureStatus", "AllocationsRegime"]
# ---
-# Specific properties.
+# Internal classes.
# ---
-class _FunctionProperty(_Property):
- """ Function. """
+class Title(_Enum):
+ """ Unknown. """
+ UNKNOWN = 0
- def init(self):
- self.__data = None
+ """ Mister (monsieur). """
+ MISTER = 1
- def get(self):
- return self.__data
+ """ Miss (madame / mademoiselle). """
+ MISS = 2
+
+class _Name:
+ """ A person's name. """
+
+ def __init__(self, value = None):
+ self.__title = None
+ self.__first = None
+ self.__common = None
+ self.__last = None
- def set(self, value):
if value is None:
- self.__data = None
return
+ if type(value) != str:
+ raise ValueError("Expected a string.")
- self.__data = _FunctionData(value)
+ value = value.split()
- def delete(self):
- self.__data = None
+ # Try to decode the title (if there is one).
-class _OperationTypeProperty(_Property):
- """ Operation type. """
+ fst = value.pop(0)
+ title = fst.upper().replace('.', '')
+ if title == 'M':
+ self.__title = Title.MISTER
+ elif title == 'MME':
+ self.__title = Title.MISS
+ else:
+ self.__title = Title.UNKNOWN
+ value.insert(0, fst)
- def init(self):
- self.__data = None
+ # Get the common name.
- def get(self):
- return self.__data
+ self.__common = ' '.join(value[:-1])
+ self.__first = value[-1]
- def set(self, value):
- if value is None:
- self.__data = None
- return
+ def __repr__(self):
+ p = []
+ ful = str(self)
+ if ful:
+ p.append(f"full = {repr(ful)}")
+ if self.__common is not None and self.__last is not None and \
+ self.__last != self.__common:
+ p.append(f"last = {repr(self.__last)}")
- self.__data = _OperationTypeData(value)
+ return f"{self.__class__.__name__}({', '.join(p)})"
- def delete(self):
- self.__data = None
+ def __str__(self):
+ ops = []
-class _StructureTypeProperty(_Property):
- """ Structure type. """
+ titl = {Title.MISTER: "M.", Title.MISS: "Mme"}.get(self.__title, None)
+ if titl is not None:
+ ops.append(titl)
- def init(self):
- self.__data = None
+ if self.__common is not None:
+ ops.append(self.__common)
+ if self.__first is not None:
+ ops.append(self.__first)
- def get(self):
- return self.__data
+ return ' '.join(ops)
- def set(self, value):
+ @property
+ def title(self):
+ """ Title. """
+
+ return self.__title
+
+ @title.setter
+ def title(self, value):
+ try:
+ self.__title = Title(value)
+ except ValueError:
+ self.__title = Title.UNKNOWN
+
+ @property
+ def first(self):
+ """ First name (prénom). """
+
+ return self.__first
+
+ @property
+ def common(self):
+ """ Common name (nom de famille). """
+
+ return self.__common
+
+ @property
+ def last(self):
+ """ Last name (nom de jeune fille). """
+
+ return self.__last
+
+ @last.setter
+ def last(self, value):
if value is None:
- self.__data = None
+ self.__last = None
return
+ if type(value) != str:
+ raise ValueError("Expected a string.")
- self.__data = _StructureTypeData(value)
+ self.__last = value
- def delete(self):
- self.__data = None
+# ---
+# Specific properties.
+# ---
-class _StructureStatusProperty(_Property):
- """ Structure type. """
+class _FunctionProperty(_Property):
+ """ Function. """
def init(self):
self.__data = None
@@ -97,35 +154,24 @@ class _StructureStatusProperty(_Property):
self.__data = None
return
- self.__data = _StructureStatusData(value)
+ self.__data = _FunctionData(value)
def delete(self):
self.__data = None
-class _OperationFieldsProperty(_Property):
+class _OperationFields:
""" Operation fields. """
- def init(self, lastfield = None, sep = ','):
- pattern = f'([^{sep}\(]*(?P<par>\(([^\(\)]*(?&par)*)*\))*)*'
-
- self.__value = None
- self.__reg = _re.compile(pattern, _re.S)
- self.__sep = sep
-
- def get(self):
- return self.__value
-
- def set(self, value):
- if value is None:
- self.__value = None
- return
-
+ def __init__(self, value, sep = ','):
if type(value) != str:
raise ValueError("fields should be string")
+ pattern = f'([^{sep}\(]*(?P<par>\(([^\(\)]*(?&par)*)*\))*)*'
+ reg = _re.compile(pattern, _re.S)
+
fields = {}
lastfield = None
- for k in self.__reg.finditer(value):
+ for k in reg.finditer(value):
ma = k.group(0)
if not ma:
continue
@@ -143,7 +189,7 @@ class _OperationFieldsProperty(_Property):
if not lastkey:
continue
- fields[lastkey] += self.__sep + ma
+ fields[lastkey] += sep + ma
continue
# New group!
@@ -151,7 +197,33 @@ class _OperationFieldsProperty(_Property):
lastkey = g[0].strip()
fields[lastkey] = ':'.join(g[1:]).lstrip()
- self.__value = fields
+ self.__fields = fields
+
+ def __repr__(self):
+ return f"OperationFields({repr(self.__fields)})"
+
+ def __iter__(self):
+ return iter(self.__fields)
+
+ def __getitem__(self, key):
+ return self.__fields[key]
+
+class _OperationFieldsProperty(_Property):
+ """ Operation fields. """
+
+ def init(self, sep = ','):
+ self.__value = None
+ self.__sep = sep
+
+ def get(self):
+ return self.__value
+
+ def set(self, value):
+ if value is None:
+ self.__value = None
+ return
+
+ self.__value = _OperationFields(value, self.__sep)
def delete(self):
self.__value = None
@@ -164,12 +236,12 @@ class Structure(_Base):
""" A structure (unit, group, territory, …). """
iid = _IIDProperty()
- type = _StructureTypeProperty()
+ type = _ValueProperty(cls = _StructureTypeData)
name = _TextProperty()
code = _TextProperty() # TODO: public code thingy?
# TODO: private code thingy?
description = _TextProperty(not_empty = True)
- status = _StructureStatusProperty()
+ status = _ValueProperty(cls = _StructureStatusData)
hemisphere = _TextProperty()
address = _TextProperty(not_empty = True)
postal_code = _TextProperty()
@@ -189,14 +261,12 @@ class Adherent(_Base):
""" A person who is part of the organization. """
iid = _IIDProperty()
- common_name = _TextProperty()
- last_name = _TextProperty()
- first_name = _TextProperty()
+ name = _ValueProperty(cls = _Name, default = _Name())
# TODO: inscription (code adhérent, structure, fonction, fct 2ndaires,
# statut, début de mandat, fin de mandat).
- function = _FunctionProperty()
+ function = _ValueProperty(cls = _FunctionData)
address = _TextProperty()
postal_code = _TextProperty()
@@ -270,8 +340,8 @@ class Operation(_Base):
""" An event on the website.. """
time = _DateProperty()
- type = _OperationTypeProperty()
- author = _TextProperty()
+ type = _ValueProperty(cls = _OperationTypeData)
+ author = _ObjectProperty(types = (Adherent,), default = Adherent())
fields = _OperationFieldsProperty()
related = _ArrayProperty(types = (Structure, Adherent,
RallyRegistration, Camp, Place))
diff --git a/sgdfi/_util.py b/sgdfi/_util.py
index a119c83..088b1da 100755
--- a/sgdfi/_util.py
+++ b/sgdfi/_util.py
@@ -4,7 +4,7 @@
# This file is part of the sgdfi project, which is MIT-licensed.
#******************************************************************************
""" Base objects for the intranet objects representations, never used
- directly. """
+ by the end user of the module. """
from inspect import getargspec as _getargspec
from copy import deepcopy as _deepcopy
@@ -18,7 +18,7 @@ from pytz import timezone as _timezone
__all__ = ["IID", "Enum",
"Property", "IIDProperty", "DateProperty", "BoolProperty", "EnumProperty",
- "ArrayProperty", "TextProperty", "Base"]
+ "ArrayProperty", "TextProperty", "ObjectProperty", "ValueProperty", "Base"]
# ---
# Attribute helpers.
@@ -319,6 +319,52 @@ class ArrayProperty(Property):
def delete(self):
self.__array.empty()
+class ObjectProperty(Property):
+ """ An object property among specified types. """
+
+ def init(self, types = (), default = None):
+ self.__default = default
+ self.__types = types
+ self.__value = self.__default
+
+ def get(self):
+ return self.__value
+
+ def set(self, value):
+ if value is None:
+ self.__value = self.__default
+ return
+
+ if not any(isinstance(value, cls) for cls in self.__types):
+ raise ValueError("Shall be an instance of one of: " \
+ f"{repr(self.__types)}")
+
+ self.__value = value
+
+ def delete(self):
+ self.__value = self.__default
+
+class ValueProperty(Property):
+ """ A value property.
+ Basically uses a class to store its value. """
+
+ def init(self, cls, default = None):
+ self.__cls = cls
+ self.__data = default
+
+ def get(self):
+ return self.__data
+
+ def set(self, value):
+ if value is None:
+ self.__data = _deepcopy(default)
+ return
+
+ self.__data = self.__cls(value)
+
+ def delete(self):
+ self.__data = _deepcopy(default)
+
class TextProperty(Property):
""" A text property.
`lines` represents the maximum number of allowed lines.