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