Migrating App To Tapyr

Important

The migration guide was written before Shiny 1.0 release, and tapyr 0.2 release. Most of the information should still be valid, the only larger change is that uv is now used instead of poetry for managing the environment. Instead of poetry add ... you should use uv add ..., and to run the a script you should use uv run shiny run app.py. uv guide on managing dependencies in projects.

General Steps

To migrate an app to Tapyr, you need to do the following the following steps:

  1. Create repository from template
  2. Open it in the devcontainer and select python interpreter
  3. Update the project name and pyproject.toml
  4. Add required dependencies to pyproject.toml
  5. Install pre-commit hooks
  6. Migrate ui and server functions to stock_app_tapyr/view/root/server.py and stock_app_tapyr/view/root/ui.py. Move utility functions to stock_app_tapyr/logic or other, more suitable directories.
  7. Adjust imports in the stock_app_tapyr directory to reflect the new project structure.
  8. Run linter and formatter to fix any issues.
  9. Fix the tests.
  10. Fix any issues reported by the linter and type checker.
  11. Finally, adjust the README.md file!

Now let’s go through a simple example of migrating an app to Tapyr.

Simple App Example

We will migrate the stock-app from Shiny for Python templates to Tapyr.

Create Repository From Template

Go to the tapyr repo and click on the “Use this template” button, follow the instructions to create a new repository.

85140d7

Open It In The Devcontainer

Open the repository in the devcontainer by clicking ctrl+shift+p and selecting Remote-Containers: Reopen in Container.

Now open any python file and select the python interpreter by clicking on the python version in the bottom left corner. The name of the interpreter should be stock_app_tapyr-xxxxx (the beginning should match the poetry environment name).

Update Project Name and pyproject.toml

Update the project name in the pyproject.toml file and the tapyr_template directory to stock_app_tapyr. Adjust all the imports in the tapyr_template directory to reflect the new project name.

73fd3f6

Add Required Dependencies to pyproject.toml

Either add them manually or use the poetry add. I’ve decided to just run

poetry add plotly yfinance pandas shinywidgets faicons cufflinks
Tip

If you edit pyproject.toml manually, you need to run poetry lock and poetry install to update the environment.

1a0c91d

Install Pre-commit Hooks

Just run pre-commit install to install the pre-commit hooks.

Migrate UI and Server Functions

Now from the stock-app/app-core.py file extract the server to stock_app_tapyr/view/root/server.py and the ui to stock_app_tapyr/view/root/ui.py.

Remove unused imports, they are greyed out in the editor.

c642a98

Migrate Utility Functions

Move the stock-app/stocks.py to stock_app_tapyr/logic/stocks.py.

Adjust the imports in ui.py from from stocks import stocks to from stock_app_tapyr.logic.stocks import stocks.

We can run the app to see if everything works.

shiny run app.py

c642a98

It doesn’t, we see that in stock-app they include css in the ui.py file. We already do it, so we need to remove this line.

We run app one more time and we see that styling is a bit off. That’s because we forget to actually copy the css contents to www/style.css. We do it now.

43b3223

The works as expected now 🎉.

Run Linter and Formatter

This is simply pre-commit run --all-files.

Now our code is well formatted.

Fix the tests

We remove the utils test and function.

stock_app_tapyr/logic/utils.py
tests/unit/test_utils.py

In the test_ui.py we’re removing the test_footer function and leave only the test_startup function.

9da9a08

Fix pyright issues

Linter is happy, but pyright is not.

Two small things have to be fixed. Those might seem a bit more advanced, but please check the resulting commit.

  1. We need to add the return type to def get_change(): -> def get_change() -> float: in server.py
  2. In ui.py variable start is used in ui.input_date_range function. start is of type pd.Timestamp and ui.input_date_range expects date. At least when it comes to typing, because we see that it works fine. We have to case the start to date type. Then we have another issue, because it can be pd.NaT (not a time) so we narrow the type with if not isinstance(start, date): check.

954a149

Adjust the README.md file

Now we can adjust the README.md and describe the app.

Summary

Congratulation 🎉, you’ve successfully migrated the app to Tapyr. Now you have a clean and well-structured project that is easy to maintain and extend. The first test is already there!