Create an Application¶
This guide describes creating a benchmarking application within the TaPS framework.
Installation¶
A development environment needs to be configured first. Fork the repository and clone your fork locally. Then, configure a virtual environment with the TaPS package and development dependencies.
See Getting Started for Local Development for detailed instructions on running the linters and continuous integration tests.
Application Structure¶
Our example application is going to be called foobar.
All applications in TaPS are composed of two required components:
AppConfig and Config.
The AppConfig is a Pydantic BaseModel with some extra functionality for constructing a config instance from command line arguments.
The App has a run() method which is the entry point to running the applications.
The App¶
All applications are submodules of taps/apps/.
Our foobar application is simple so we will create a single file module named taps/apps/foobar.py.
More complex applications can create a subdirectory containing many submodules.
- Applications in TaPS are composed on tasks which are just Python functions.
Here, our task is the
print_messagefunction. - The
FoobarAppimplements theAppprotocol. - The
close()method can be used to close any stateful connection objects create in__init__or perform any clean up if needed. - Once
FoobarAppis instantiated by the CLI,FoobarApp.run()will be invoked. This method takes two arguments: aAppEngineand a path to the invocations run directory. Applications are free to use the run directory as needed, such as to store result files.
The AppEngine is the key abstraction of the TaPS framework.
The CLI arguments provided by the user for the compute engine, data management, and task logging logic are used to create a AppEngine instance which is then provided to the application.
AppEngine.submit() is the primary method that application will use to execute tasks asynchronously.
This method returns a TaskFuture object with a result() which will wait on the task to finish and return the result.
Alternatively, AppEngine.map() can be used to map a task onto a sequence of inputs, compute the tasks in parallel, and gather the results.
Importantly, a TaskFuture can also be passed as input to another tasks.
Doing so indicates to the AppEngine that there is a dependency between those two tasks.
The AppConfig¶
An AppConfig is registered with the TaPS CLI and defines (1) what arguments should be available in the CLI and (2) how to construct and App from the configuration.
Each App definition has a corresponding AppConfig defined in taps/run/apps/.
Here, we'll create a file taps/run/apps/foobar.py for our FoobarConfig.
This configuration will contain all of the parameters that the user is required to provide and any optional parameters..
- The
@register_app()decorator registers theFoobarConfigwith the TaPS CLI. The name specified in the decorator is the name under which the application will be available in the CLI. For example, here we can usepython -m taps.run foobar {args}to run our application. - The
AppConfigclass supports required arguments without default values (e.g.,message) and optional arguments with default values (e.g.,repeat). - The
create_app()method is required and is invoked by the CLI to create anAppinstance. - Note:
FoobarAppis imported inside ofcreate_app()to delay importing dependencies specific to the application until the user has decided which application they want to execute.
Dependencies¶
Applications which require extra dependencies should do one of the following.
- Add an optional dependencies section to
pyproject.toml. If the dependencies are pip installable, add a new section with the name of the application to the[project.optional-dependencies]section inpyproject.toml. For example: - Add installation instructions to the application's documentation. More complex applications may have dependencies which are not installable with pip. In this case, instructions should be provided in documentation, discussed in the next section.
Documentation¶
Each application should have an associated documentation page which describes (1) what the application is based on, (2) what the application does, and (3) how to run the application.
Application documentation is written using markdown files in docs/apps/.
For example, our foobar application will have a file called docs/apps/foobar.md.
Once your markdown file is written, it needs to be added to the documentation navigation tree.
First, modify docs/apps/index.md to contain a link to the file.
mkdocs.yml to contain the path to the markdown file within the "Apps" section of the docs.
Please keep the lists in each of these files alphabetized.
Once these files have been added, you can build the documentation.
This requires having installed TaPS with the docs option (e.g., pip install .[docs]).
Running the Application¶
Once an application is created and registered within TaPS, the application is available within the CLI.
Usingfoobar as the first positional argument indicates we want to execute the foobar application, and --help will print all of the required and optional arguments as specified in the FoobarConfig.
The arguments will be separated into sections, such as for arguments specific to foobar or for executor arguments.
The following command will execute the application to print "Hello, World!" three times.
We specify the thread-pool executor because this will allow our printing to show up in the main process.
$ python -m taps.run foobar --message 'Hello, World!' --repeat 3 --executor thread-pool
RUN (taps.run) :: Starting application (name=foobar)
...
APP (taps.apps.foobar) :: Hello, World!
APP (taps.apps.foobar) :: Hello, World!
APP (taps.apps.foobar) :: Hello, World!
RUN (taps.run) :: Finished application (name=foobar, runtime=0.00s)