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