Thought-Through Package Structure

The problem

You’re staring a Shiny for Python app so you create app.py file. The app grows and you add more and more code to app.py. So you start to split the code into multiple files. You suddenly realize that you have a lot of files and you don’t know where to put them. After putting files into different directories, you realize that you have to import them in a weird way

Then you think that some proper code formatting would be nice. And linting. Then testing. Well, and maybe some pre-commit in GitHub actions. And more…

The solution

Tapyr comes with a predefined package structure that looks as follows:

Example tapyr structure
.
├── .devcontainer
│   └── devcontainer.json
├── .github
│   └── workflows
│       ├── main.yaml
│       └── pre-commit.yml
├── .gitignore
├── .pre-commit-config.yaml
├── LICENSE
├── README.md
├── app.py
├── notebooks
│   └── 01_experiments.ipynb
├── pyproject.toml
├── quality_checks.sh
├── requirements.txt
├── ruff.toml
├── tapyr_template
│   ├── __init__.py
│   ├── logic
│   │   ├── __init__.py
│   │   └── utils.py
│   ├── settings.py
│   └── view
│       ├── __init__.py
│       └── root
│           ├── __init__.py
│           ├── server.py
│           └── ui.py
├── tests
│   ├── conftest.py
│   ├── helpers
│   │   └── logging_helpers.py
│   ├── end2end
│   │   ├── __init__.py
│   │   ├── conftest.py
│   │   └── test_ui.py
│   └── unit
│       └── test_utils.py
├── uv.lock
└── www
    ├── favicon.ico
    ├── images
    │   └── tapyr.png
    └── style.css

There are a lot of files and directories here, let’s go through them in the order of importance.

Real Source Code

  • app.py - Main application file.
  • tapyr_template - Main package directory.

tapyr_template/logic

This directory contains the main logic of the application, classes and functions that are not aware of the Shiny framework.

Template contains an example of a simple divide function that is wrapped in loguru catch decorator.

tapyr_template/view

The view directory should contain code which describes the user interface of your application and relies upon the reactive capabilities of Shiny. Here is where we will use the functions defined in logic, and where the core app functionality will be defined.

If you are not familiar with Shiny Modules, please take the time to read up on the concept. In short, using modules we can isolate paired Shiny UI/Server code, and we prevent overlap of reactivity. This allows us to namespace the running module and use it multiple times in the same application. This is a very important concept to shortly summarize, but if this is new to you just remember that if you want to reference a UI element in the server, it needs to be namespaced. Each UI/server module pair should have its own directory in the view directory just like tapyr_template/view/root with server.py and ui.py files. Note that in tapyr_template/view/root/__init__.py we expose the UI and server functions, this simplifies the importing.

Tests

  • tests/end2end - End2end UI tests using playwright.
  • tests/unit - Unit tests using pytest.
  • tests/helpers - Test helpers.
  • quality_checks.sh - Script to run all tests type checking. Useful for CI/CD, but can be used locally as well!

More information about testing in the Testing document.

Tip

You can run all tests using ./quality_checks.sh script.

notebooks directory

  • This is a directory for prototyping with Jupyter notebooks.
  • You may want to create a scripts directory instead, if you prefer to prototype with scripts.

Project configuration

  • pyproject.toml - uv and python project configuration.
  • ruff.toml - Ruff configuration.
  • tapyr_template/settings.py - Pydantic settings configuration.
  • uv.lock - uv lock file, uv maintains this file on it’s own.

www directory

  • favicon.ico - Favicon for the app.
  • images - The example directory for static assets.
  • style.css - CSS file for the app.

Miscellaneous

  • .devcontainer/devcontainer.json - Development container configuration.
  • .github/workflows/main.yml - GitHub Actions workflow for CI/CD.
  • .github/workflows/pre-commit.yml - Pre-commit actions workflow.
  • .pre-commit-config.yaml - Pre-commit configuration.
  • .gitignore - Standard .gitignore file.
  • notebooks/01_experiments.ipynb - an example Jupyter notebook for quick prototyping.
  • requirements.txt - Since we’re using uv, this file is only used when we deploy to Connect.
  • README.md - Basic README file.