- Python 97.1%
- Shell 1.7%
- Dockerfile 1%
- Mako 0.2%
|
|
||
|---|---|---|
| .direnv | ||
| .github/workflows | ||
| .jobs | ||
| .vscode | ||
| applications | ||
| config | ||
| containers | ||
| migrations | ||
| packages | ||
| sandbox | ||
| scripts | ||
| .dockerignore | ||
| .envrc | ||
| .gitignore | ||
| alembic.ini | ||
| cli.py | ||
| noxfile.py | ||
| pyproject.toml | ||
| README.md | ||
| ruff.toml | ||
| uv.lock | ||
WeatherData Monorepo
I use weather APIs frequently to help me learn programming things. I'm continuing that trend by learning to build a monorepo with Python and uv.
Table of Contents
Requirements
uv: The packages (and the repository itself) are managed withuv.- If you haven't already tried it, you should!
uvcan even install Python for you, meaninguvis the only dependency you need to install for this monorepo.
- (Optional) Python: If you want to install Python (or already have it installed),
uvwill use the system version of your Python.
Setup
- Clone this repository
git clone https://github.com/redjax/weatherdata
- Create your configuration files
- Copy the following files in
config/:- General configurations:
settings.toml->settings.local.toml.secrets.toml->.secrets.local.toml
- Database configuration:
database/settings.toml->database/settings.local.tomldatabase/.secrets.toml->database/.secrets.local.toml
- weatherapi configuration:
- NOTE: You need to sign up for a free API key on WeatherAPI's site.
weatherapi/settings.toml->weatherapi/settings.local.tomlweatherapi/.secrets.toml->weatherapi/.secrets.local.toml
- General configurations:
- Copy the following files in
- Run
uv syncto install required packages- This repository uses
uvworkspaces to build modules inapplications/andpackages. - Each of the projects in these directories is a Python package with a
pyproject.tomlfile, intialized withuv init --package. - When
uvruns any part of this repository, it will build everything and then execute the command you ran. - If you want to manually build the project, you can run
uv build
- This repository uses
Usage
Once you have installed the project (read the setup instructions), you can run different entrypoints with uv run.
You can also run the project's cli with: uv run cli --help.
For example, to launch the weather_cli app, you can run:
uv run python -m weather_cli
Or to see the sandbox demo, you can just run:
uv run sandbox/demo/demo.py
You can omit python in your uv run commands; uv knows what you're trying to do when you run a .py file 😉 The exception to this rule is when running packages as a module, as you would with python -m. You still need to add that to your uv command.
Using the monorepo
Developing in a monorepo is different from developing in other "flatter" repositories. In a regular repository, you might only have 1 service or application defined in your code. In a monorepo, you can define many services/applications and shared dependencies. You can bring various parts of the application together by installing modules in other modules (like the depends package, which import its settings in the [tool.uv.sources] section from the settings module: settings = { workspace = true }), import from other areas of the monorepo, and share code more easily between different applications and services.
Each type of repository has its purpose and place; I am choosing to structure this project as a monorepo so I can learn how to manage and work with them. Neither is better than the other, a good developer will choose when a monorepository format is right for their project.
Adding packages
To add new packages to the app, create a path in packages/ and initialize it with uv init --package. Note that if you're using VSCode and want type completion to work, you also need to edit the settings.json file in the .vscode/ directory, adding the path to "python.analysis.extraPaths": [].
For example, to add a package named nb_functions, you might run the following commands:
## Create the directory where the package will live
mkdir -pv ./packages/notebook-functions
cd ./packages/notebook-functions
## Create the src directory & __init__.py/main.py files
mkdir -pv ./src/nb_functions
touch ./src/nb_functions/{__init__,main}.py
## Initialize the uv package
uv init --package
## Add code from another module in this repository, i.e. http-lib
uv add http-lib
Note that the source code name (nb_functions) differs from the parent path name (notebook-functions). You need to modify the new pyproject.toml file that was created when you ran uv init --package.
When a package's name differs from the parent directory like this, you will see an error like this when you try to add packages, build, or run anything with uv:
ValueError: Unable to determine which files to ship inside the wheel using the following heuristics: https://hatch.pypa.io/latest/plugins/builder/wheel/#default-file-selection
The most likely cause of this is that there is no directory that matches the name of your project (notebook-functions).
At least one file selection option must be defined in the `tool.hatch.build.targets.wheel` table, see: https://hatch.pypa.io/latest/config/build/
As an example, if you intend to ship a directory named `foo` that resides within a `src` directory located at the root of your project, you can define the following:
[tool.hatch.build.targets.wheel]
packages = ["src/foo"]
The fix for this is details in that last part of the error message. We need to add a section like the following to tell uv where the code it needs to build for this package lives:
## packages/notebook-functions/src/nb_functions/pyproject.toml
...
[tool.hatch.build.targets.wheel]
packages = ["src/nb_functions"]
...
This fixes the build error. Remember this when you are naming package and application parent directories.
Finally, if you're using VSCode, add the new path to your settings.json file:
{
...,
"python.analysis.extraPaths": [
...,
"./packages/notebook-functions/src",
]
}
Adding applications
Applications import from packages/ and other areas of the repository, joining different parts of the code into functional apps meant to be built, distributed, and used.
For example, the weather_cli application exposes a CLI written in cyclopts that a user can run to call different parts of the code, like requesting the current weather.
Building an application is pretty much the same as a package. The main difference is when you initialize it, you run:
uv init --application --package
The 'sandbox'
The sandbox/ path is a place where I can prototype or test small portions of the repository. The demo application is a constantly evolving file I use put together pre-packaged function calls by importing code from applications, packages, etc, and writing scripts like I might if I were to install these packages outside of this repository.
Sandbox code is just that; it is not packaged, it's not meant to be distributed, and scripts may come and go (I often delete things in the sandbox once I've integrated the code into the app somewhere else).
Notes
Fix memcached crash on Raspberry Pi
On Raspberry Pi (and probably other ARM CPUs), the libmemcached package must be installed on the host.
- Debian:
apt install -y libmemcached-dev - RedHat:
dnf install -y memcached libmemcached