Skip to content

Extending OneCode

OneCode principle overview

OneCode 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_elementsfolders. You only need to follow these 2 instructions:

  1. 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.

  2. Re-implement the abstractmethod defined in the InputElement or OutputElement 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__)
    
    # output elements
    from onecode import import_output
    
    import_output(__file__, __name__)
    
    Refer to Registering input elements and Registering output elements for more information.

  • For any new element:

    1. 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.

    2. Re-implement the abstractmethod defined in the InputElement or OutputElement 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:

  1. import the corresponding class: the class name must be the Pascal-case of the filename (e.g. file_input.py => class FileInput)
  2. register the class as an element, i-e it will be recognized by the OneCode interpreter
  3. export the class as-is (allow for subclassing by a third-party)
  4. 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')()
    
  5. export a new variable specifying the type of element being INPUT.

    Example

    file_input_type = ElementType.INPUT
    

    This is used internally by OneCode interpreter.

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:

  1. import the corresponding class: the class name must be the Pascal-case of the filename (e.g. file_output.py => class FileOutput)
  2. register the class as an element, i-e it will be recognized by the OneCode interpreter
  3. export the class as-is (allow for subclassing by a third-party)
  4. 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 the OutputElement.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')()
    
  5. export a new variable specifying the type of element being OUTPUT.

    Example

    file_output_type = ElementType.OUTPUT
    

    This is used internally by OneCode interpreter

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