Extending OneCode¶
OneCode principle overview¶
Extend with new elements inside a OneCode Project¶
Each OneCode Project contains by default a folder onecode_ext
inside its flows
folder. The
mechanism is already in place so that you can simply add your new elements and they will be
automatically registered as OneCode elements. Input and output elements respectively go to
flows/onecode_ext/input_elements/
and flows/onecode_ext/output_elements
folders. You only need
to follow these 2 instructions:
-
The Python filename must be snake case and the corresponding element class name must be Pascal case. For instance,
my_new_element.py
=>class MyNewElement
. -
Re-implement the
abstractmethod
defined in theInputElement
orOutputElement
base class according its documentation.
Here is a simple example:
# flows/onecode_ext/input_elements/my_header_element.py
from onecode import InputElement
class MyHeaderElement(InputElement):
def __init__(
self,
key: str,
value: str
):
super().__init__(key, value)
@property
def _value_type(self) -> type:
return str
def streamlit(
self,
id: str
) -> str:
return f"""
{self.key} = st.header('''{self.value}''')
"""
def _validate(
self,
value: str
) -> None:
pass
Then call it in your flow code:
# flows/my_flow.py
from onecode_ext import my_header_element
def run():
x = my_header_element('x', 'My Header!'))
# ...
As easy as that. If you're interested on how it works under the hood, check out the section below.
Tip
For a real use-case example, look at the
DeepLearning
project
Extend as a library¶
For use-cases going beyond the scope of a single OneCode Project, you may consider writing your own library. For example, you could have specific elements used across many projects and/or dedicated mode to be compatible with your internal tools such as a cloud platform.
To add new elements, you will manually code what onecode_ext
does automatically for you:
-
Set a separate folder for your input and output elements, then add the following code to its
__init__
files:# input elements from onecode import import_input import_input(__file__, __name__)
Refer to Registering input elements and Registering output elements for more information.# output elements from onecode import import_output import_output(__file__, __name__)
-
For any new element:
-
The Python filename must be snake case and the corresponding element class name must be Pascal case. For instance,
my_new_element.py
=>class MyNewElement
. -
Re-implement the
abstractmethod
defined in theInputElement
orOutputElement
base class according its documentation.
-
From there, to ensure your module is taken into account by CLI commands, add --module yourcode
to them:
# Start with streamlit
onecode-start --module <library_name>
# JSON parameter extraction
onecode-extract params.json --module <library_name>
You may also:
- add new
Mode
: define a mixin class and inherit all elements with the mixin - add new CLI: you may use the CLI Utilities or even start from the OneCode CLI original code as it is open source with MIT License.
Example
Here is an example of library called yourcode
extending OneCode and demonstrating:
- a new CLI extracting parameters as TOML
- a radio button widget accepting runtime [Expressionstips_and_tricks#using-runtime-expressions-in-elements)
Registering input elements¶
def import_input(
init_file: str,
module_name: str
) -> None:
Use this function to register your InputElement
. Here is what it does for you in details.
For each file located in the same folder as the init_file
path:
- import the corresponding class: the class name must be the Pascal-case of the filename
(e.g.
file_input.py
=>class FileInput
) - register the class as an element, i-e it will be recognized by the OneCode interpreter
- export the class as-is (allow for subclassing by a third-party)
-
export a new function matching the filename: it wrapps around the class for convenience by simply initializing it, then calling it.
Example
def file_input(*args, **kwargs): return FileInput(*args, **kwargs)() # This makes a convenient usage in the client code: x = onecode.file_input('test', 'file,txt') # Rather than writing: x = onecode.FileInput('test', 'file.txt')()
-
export:
- a new variable specifying the type of element being
INPUT
. - a new function returning the element import statements.
- a new function returning the element init statements.
Example
file_input_type = ElementType.INPUT def _file_input_importdef(): return FileInput.imports() file_input_imports = _file_input_importdef def _file_input_initdef(): return FileInput.init() file_input_init = _file_input_initdef
This is used internally by OneCode interpreter.
- a new variable specifying the type of element being
Parameters:
Name | Type | Description | Default |
---|---|---|---|
init_file |
str
|
path to the init.py file located in the same directory as the input elements. |
required |
module_name |
str
|
Python name of the module to import the elements under. The module will then be available for importing using regular Python import statements. |
required |
Registering output elements¶
def import_output(
init_file: str,
module_name: str
) -> None:
Use this function to register your OutputElement
. Here is what it does for you in details.
For each file located in the same folder as the init_file
path:
- import the corresponding class: the class name must be the Pascal-case of the filename
(e.g.
file_output.py
=>class FileOutput
) - register the class as an element, i-e it will be recognized by the OneCode interpreter
- export the class as-is (allow for subclassing by a third-party)
-
export a new function matching the filename: it wrapps around the class for convenience by simply initializing it, then calling it. There is one important difference with
InputElement
: a mechanism is in place to allow calling theOutputElement.static_call()
method when no argument are provided to the function. It is typically used internally by the OneCode interpreter to properly handle cases of dynamically defined parameters.Example
def file_output(*args, **kwargs): empty_ctor = not args and not kwargs return FileOutput.static_call(FileOutput) if empty_ctor else FileOutput(*args, **kwargs)() # This makes a convenient usage in the client code: x = onecode.file_output('test', 'file,txt') # Or for dynamically defined parameters: x = onecode.file_output('test', os.path.join(os.getcwd(), f'file_{my_var},txt') # Rather than writing: x = onecode.FileOutput('test', 'file.txt')()
-
export:
- a new variable specifying the type of element being
OUTPUT
. - a new function returning the element import statements.
- a new function returning the element init statements.
Example
file_output_type = ElementType.OUTPUT def _file_output_importdef(): return FileInput.imports() file_output_imports = _file_output_importdef def _file_output_initdef(): return FileInput.init() file_output_init = _file_output_initdef
This is used internally by OneCode interpreter
- a new variable specifying the type of element being
Parameters:
Name | Type | Description | Default |
---|---|---|---|
init_file |
str
|
path to the init.py file located in the same directory as the input elements. |
required |
module_name |
str
|
Python name of the module to import the elements under. The module will then be available for importing using regular Python import statements. |
required |