Annotations & Properties¶
Flatland provides two options for annotating schemas and data.
Standard Python¶
Element schemas are normal Python classes and can be extended in all of the usual ways. For example, you can add an attribute when subclassing:
from flatland import String
class Textbox(String):
tooltip = 'Undefined'
Once an attribute has been added to an element class, its value can be
overridden by further subclassing or, more compactly, with the
using()
schema constructor:
class Password(Textbox):
tooltip = 'Enter your password'
Password = Textbox.using(tooltip='Enter your password')
assert Password.tooltip == 'Enter your password'
Both are equivalent, and the custom tooltip
will be inherited by
any subclasses of Password
. Likewise, instances of Password
will have the attribute as well.
el = Password()
assert el.tooltip == 'Enter your password'
And because the Element()
constructor allows overriding any
schema attribute by keyword argument, individual element instances can
be constructed with own values, masking the value provided by their
class.
password_match = Textbox(tooltip='Enter your password again')
assert password_match.tooltip == 'Enter your password again'
Properties¶
Another option for annotation is the properties
mapping of element classes and instances. Unlike class attributes,
almost any object you like can be used as the key in the mapping.
The unique feature of properties
is data inheritance:
from flatland import String
# Textboxes are Strings with tooltips
Textbox = String.with_properties(tooltip='Undefined')
# A Password is a Textbox with a custom tooltip message
Password = Textbox.with_properties(tooltip='Enter your password')
assert Textbox.properties['tooltip'] == 'Undefined'
assert Password.properties['tooltip'] == 'Enter your password'
Annotations made on a schema are visible to itself and any subclasses, but not to its parents.
# Add disabled to all Textboxes
Textbox.properties['disabled'] = False
# disabled is inherited from Textbox
assert Password.properties['disabled'] is False
# changes in a subclass do not affect the parent
del Password.properties['disabled']
assert 'disabled' in Textbox.properties
Annotating With Properties¶
To create a new schema that includes additional properties, construct
it with with_properties()
:
Textbox = String.with_properties(tooltip='Undefined')
Or if the schema has already been created, manipulate its
properties
mapping:
class Textbox(String):
pass
Textbox.properties['tooltip'] = 'Undefined'
The properties
mapping is implemented as a view over
the Element schema inheritance hierarchy. If annotations are added to
a superclass such as String
, they
are visible immediately to all Strings and subclasses.
Private Annotations¶
To create a schema with completely unrelated properties, not
inheriting from its superclass at all, declare it with
using()
:
Alone = Textbox.using(properties={'something': 'else'})
assert 'tooltip' not in Alone.properties
Or when subclassing longhand, construct a
Properties
collection explicitly.
from flatland import Properties
class Alone(Textbox):
properties = Properties(something='else')
assert 'tooltip' not in Alone.properties
An instance may also have a private collection of properties. This can be done either at or after construction:
solo1 = Textbox(properties={'something': 'else'})
solo2 = Textbox()
solo2.properties = {'something': 'else'}
Textbox.properties['background_color'] = 'red'
assert 'background_color' not in solo1.properties
assert 'background_color' not in solo2.properties