Skip to content

Write a class app

How to inherit from scinexus app base classes, or using the define_app decorator, specifying input/output type hints.

Using inheritance from a base class

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
from collections.abc import Callable
from citeable import Software
from scinexus import ComposableApp

my_cite = Software(
    author=["Doe, J", "Smith, A"],
    title="My Sequence Filter",
    year=2025,
    url="https://example.com/my-filter",
    version="0.1.0",
)


class my_app(  # (1)!
    ComposableApp[str, str],  # (2)!
    cite=my_cite,  # (3)!
):
    def __init__(self, convert: Callable[[str], str]):
        self.convert = convert

    def main(self, val: str) -> str:  # (4)!
        return self.convert(val)
  1. We suggest naming your apps using the PEP8 naming style for functions (lowercase separated by underscores) because the instances will be used like functions.
  2. We type hint the input / output types with the base class.
  3. We assign the citation in the class definition.
  4. Your class must have a main() method with type hints specified for its first argument and its return type.

Using the define_app decorator

How to use @define_app on a class with a main() method, configure it via __init__ parameters, and control behaviour with the app_type parameter.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
from collections.abc import Callable
from citeable import Software
from scinexus import ComposableApp

my_cite = Software(
    author=["Doe, J", "Smith, A"],
    title="My Sequence Filter",
    year=2025,
    url="https://example.com/my-filter",
    version="0.1.0",
)


@define_app(cite=my_cite)  # (1)!
class my_app:
    def __init__(self, convert: Callable[[str], str]):
        self.convert = convert

    def main(self, val: str) -> str:  # (2)!
        return self.convert(val)
  1. The define_app decorator is used. You can specify the app_type here, which we don't in this case, and assign your citation.
  2. Your class must have a main() method with type hints specified for its first argument and its return type.

Specifying the app type

The define_app decorator has a default app_type of "generic". This means the app does data transformation and does not load or write data. The supported app types are indicated by the AppType enum:

1
2
3
4
5
6
from scinexus.composable import AppType

print(list(AppType))

# [<AppType.LOADER: 'loader'>, <AppType.WRITER: 'writer'>, <AppType.GENERIC:
# 'generic'>, <AppType.NON_COMPOSABLE: 'non_composable'>]

If your app is not intended to be composed sequentially with other apps, set it to non-composable:

from scinexus import define_app, AppType


@define_app(app_type=AppType.NON_COMPOSABLE)
class my_standalone_app:
    def main(self, val: str) -> str:
        return val.upper()

Handling NotCompleted values

By default, apps skip NotCompleted inputs — they propagate through the pipeline without calling main(). If your app needs access to NotCompleted instances (e.g. you are developing a writer that records failures), set skip_not_completed=False:

from scinexus import define_app, NotCompleted


@define_app(skip_not_completed=False)
class my_writer:
    def main(self, val: str) -> str:
        if isinstance(val, NotCompleted):
            # handle the failure
            ...
        return val