featuremonkey recognizes python packages, modules, classes, functions and methods as being part of the FST.
FSTs are declared as modules or classes depending on the preference of the user. modules and classes can be mixed arbitrarily.
Note
when using classes, please make sure to use new style classes. Old style classes are completely unsupported by featuremonkey - because they are old and are removed from the python language with 3.0. To create a new style class, simply inherit from object or another new style class explicitely.
FSTs specify introductions and refinements of structures contained in the global interpreter state. This is done by defining specially crafted names inside the FST module/class.
Introductions are useful to add new attributes to existing packages/modules/classes/instances.
An introduction is specified by creating a name starting with introduce_ followed by the name to introduce directly inside the FST module/class. The attribute value will be used like so to derive the value to introduce:
Example:
class TestFST1(object):
#introduce name ``a`` with value ``7``
introduce_a = 7
#introduce name ``b`` with value ``6``
def introduce_b(self):
return 6
#introduce method ``foo`` that returns ``42`` when called
def introduce_foo(self):
def foo(self):
return 42
return foo
Warning
Names can only be introduced if they do not already exist in the current interpreter state. Otherwise compose will raise a CompositionError. If that happens, the product may be in an inconsistent state. Consider restarting the whole product!
Refinements are used to refine existing attributes of packages/modules/classes/instances.
An introduction is specified much like an introduction. It is done by creating a name starting with refine_ followed by the name to refine directly inside the FST module/class. The attribute value will be used like so to derive the value to introduce:
Example:
class TestFST1(object):
#refine name ``a`` with value ``7``
refine_a = 7
#refine name ``b`` with value ``6``
def introduce_b(self, original):
return 6
#refine method ``foo`` to make it return double the value of before.
def refine_foo(self, original):
def foo(self):
return orginal(self) * 2
return foo
Note
when calling original in a method refinement(for both classes and instances), you need to explicitely pass self as first parameter to original.
Warning
Names can only be refined if they exist in the current interpreter state. Otherwise compose will raise a CompositionError. If that happens, the product may be in an inconsistent state. Consider restarting the whole product!
FSTs can be nested to refine nested structures of the interpreter state. To create a child FST node, create a name starting with child_ followed by the nested name. The value must be either a FST class or instance or a FST module. As an example, consider a refinement to the os module. We want to introduce os.foo and also refine os.path.join. We could do this by composing a FST on os to introduce foo and then composing another FST on os.path that refines join. Alternatively, we can use FST nesting and specify it as follows:
class os(object):
introduce_foo = 123
class child_path(object):
def refine_join(self, original):
def join(*elems):
return original(elems)
return join
Got it?
compose applies multiple fsts onto a base implementation. Pass the base implementation as last parameter. fsts are merged from RIGHT TO LEFT (like function application) e.g.:
compose(MyFST(), MyClass)
register list of things for composition using compose()
compose_later takes a list of fsts. The last element specifies the base module as string things are composed directly after the base module is imported by application code
selects the features given as string e.g passing ‘hello’ and ‘world’ will result in imports of ‘hello’ and ‘world’. Then, if possible ‘hello.feature’ and ‘world.feature’ are imported and select is called in each feature module.