Member customization: advanced techniques
In the basics, we covered how to customize the behavior of a member by using prefixed method in a class definition. However this is not the only way to customize members. The following section will first describe how a member determine the action to take at each step of the getting, setting process, before giving more details about the behaviors taht can be used to build custom members.
Members inner working
For each step of the process described of Introducing the members, Members check the mode identifying the action it should take. Based on this flag, it will either call a builtin function or the appropriate user provided function or method.
One can inspect the mode set on the member by accesing the matching attribute as described in the table below. This attribute is a tuple containing two items. The first item is the flag value matching the member behavior, the second item depend on the value of the flag.
The behavior of the member can be modified by calling the matching set_
method, to set the flag to a new and provide the additional item matching the
flag value.
For all steps of the getattr, setattr process, you can invoke them separatly
by calling the mathcing do_
method. Among them, do_full_validate()
is special in that it will run both the validate and post_validate steps on the
provided value.
The following table summarizes the different steps along with the flags and the aforementionned attributes and methods:
Steps |
Mode |
Mode (getter/setter) |
Manual running |
---|---|---|---|
getattr |
|||
post_getattr |
|||
setattr |
|||
post_setattr |
|||
validate |
|||
post_validate |
|||
default_value |
Note
Delattr works on the same model but the flag is not exposed as part of the
public API. You can still access the mode using delattr_mode()
Behaviors for custom members
In order to create custom members, you can either subclass Member
and set
the modes in the __init__
method, or set the modes after instantiating the
member. The modes that can be used in conjunction with custom callable or
methods are listed below and expected signature of the callable or the method
can be directly inferred from the mode. When specifying a method, the second
item of the mode should be the name of the method. In the following, Object
always refers to an Atom
subclass instance and Name to the member name.
GetAttr
:CallObject_Object
CallObject_ObjectName
ObjectMethod
ObjectMethod_Name
MemberMethod_Object
PostGetAttr
:ObjectMethod_Value
ObjectMethod_NameValue
MemberMethod_ObjectValue
SetAttr
:CallObject_ObjectValue
CallObject_ObjectNameValue
ObjectMethod_Value
ObjectMethod_NameValue
MemberMethod_ObjectValue
PostSetAttr
:ObjectMethod_OldNew
ObjectMethod_NameOldNew
MemberMethod_ObjectOldNew
DefaultValue
:CallObject
CallObject_Object
CallObject_ObjectName
ObjectMethod
ObjectMethod_Name
MemberMethod_Object
Validate
:ObjectMethod_OldNew
ObjectMethod_NameOldNew
MemberMethod_ObjectOldNew
PostValidate
:ObjectMethod_OldNew
ObjectMethod_NameOldNew
MemberMethod_ObjectOldNew
GetState
:ObjectMethod_Name
MemberMethod_Object
Note
It is recommended to avoid customizing specialized members that may make some assumptions regarding the values of the other modes.
Using __init_subclass__
Starting with atom 0.10.0, __init_subclass__
can be meaningfully used to
customize members as illustrated in Pickling Example.
When accessing members in __init_subclass__
one should however be careful
to only modify members that belong to the class being customized. Otherwise
a parent class could see its behavior change which is definitively undesirable.
To avoid this issue clone_if_needed()
can be used to clone a member if it does
not belong to a class and update the class to use the clone. It returns the
original member or its clone.