Skip to content

Tutorial

Installation

It's recommended to install Mahoraga with uv >=0.9.0:

uv tool install -U mahoraga

Server Configuration

Before starting Mahoraga, you need to initialize a directory (for example ~/.mahoraga) to hold its configuration and data:

uvx mahoraga new ~/.mahoraga
The default configuration may not be suitable for you. View and edit it with any text editor you like:
uvx pyvim ~/.mahoraga/mahoraga.toml
Inline documentations can be found inside the file.

Note

Running uvx without uv tool install will result in a full installation every time you run the tool. See uv tool documentation for details.

Server Deployment

Start the server directly to check if your configuration is correct:

cd ~/.mahoraga
uvx mahoraga run
Logs will appear in the console as well as ~/.mahoraga/log/mahoraga.log. If the server started successfully, you can configure your clients and try fetching some packages from Mahoraga. Then if everything goes well, press Ctrl+C to stop the server, and start it again in the background:

uvx mahoraga run &>/dev/null &
uvw tool run mahoraga run

For better performance, you can set up a non-Python server in front of Mahoraga. Packages cached on disk can be served within that server, so that subsequent requests won't go to Mahoraga again. We provide configuration files for Nginx and Caddy out-of-the-box.

You can find the following Nginx configuration files in ~/.mahoraga/nginx:

  • nginx.conf: Top-level configuration file for direct use.
  • mahoraga.conf: A snippet intended to be included in the http block of another file.

Note

For Linux and macOS, Nginx is available in conda-forge and can be installed by Pixi:

pixi global install nginx

To start the Nginx server, run nginx -c ~/.mahoraga/nginx/nginx.conf.

You can find the Caddy configuration file at ~/.mahoraga/Caddyfile. As the name suggests, it can be used as the top-level configuration file directly. In addition, it's also a valid snippet when imported to the root of another Caddyfile, since it doesn't contain a global options block.

Note

Caddy is available in conda-forge and can be installed by Pixi:

pixi global install caddy

To start the Caddy server, run cd ~/.mahoraga && caddy run.

When configuring the clients, make sure they don't communicate with Mahoraga directly, but through Nginx or Caddy.

Client Configuration

Note

Mahoraga serves on http://127.0.0.1:3450 by default. Replace it with the actual URL exposed to your clients.

uv

To get started on your client machine, install uv >=0.9.10 if you haven't got it elsewhere:

curl -LsSf http://127.0.0.1:3450/uv/uv-installer.sh |
    env UV_DOWNLOAD_URL="http://127.0.0.1:3450/uv" sh
PowerShell
$Env:UV_DOWNLOAD_URL = "http://127.0.0.1:3450/uv"
irm http://127.0.0.1:3450/uv/uv-installer.ps1 | iex

When upgrading uv installed in this way, run the same command again. We don't support uv self update at this moment.
Run the following script to let Mahoraga take over the requests from uv, Pixi and other Rattler-based tools. You can also download, view and edit the script before executing it.

UV_PYTHON_INSTALL_MIRROR=http://127.0.0.1:3450/python-build-standalone \
    uv run --default-index http://127.0.0.1:3450/pypi/simple \
    http://127.0.0.1:3450/static/client_config.py \
    http://127.0.0.1:3450
PowerShell
$Env:UV_PYTHON_INSTALL_MIRROR = "http://127.0.0.1:3450/python-build-standalone"
uv run --default-index http://127.0.0.1:3450/pypi/simple `
    http://127.0.0.1:3450/static/client_config.py `
    http://127.0.0.1:3450

Upgrading or downgrading uv to a specific version is not directly supported, however a small shell trick can work:

alias uv='uvx uv@0.9.10'
PowerShell
function uv {
    uvx 'uv@0.9.10' @args
}

Pixi

Note

This section is about Pixi installation. If you already have Pixi >=0.43.1 installed, you can skip it since Pixi configuration was done in the previous section.

There is no mirror for the standalone installer of Pixi as of now. Instead, run the following script to install Pixi when you have uv installed and configured:

uv run http://127.0.0.1:3450/static/get_pixi.py
By default, the script installs the latest version of Pixi to PIXI_HOME , replacing any existed version, and prepend $PIXI_HOME/bin to your PATH. To specify a version, pass it as CLI argument:
get_pixi.py '0.43.1'  # Exact version
get_pixi.py '0.43.*'  # Latest revision of a specific minor version
get_pixi.py '>=0.43.1,<1'  # Version range
The script respects the environment variable PIXI_NO_PATH_UPDATE if present.

Pyodide

Note

Mirror configuration requires Pyodide version 0.28.0 or later.

Pyodide Python distribution, wheels and JavaScript/WebAssembly runtime are all available in Mahoraga. To enable them, add the following line to your mahoraga.toml:

mahoraga.toml
[cors]
allow-origins = [
    "*",
]
Additionally, if you are using Nginx or Caddy, uncomment all blocks in your mahoraga.conf or Caddyfile like this:

mahoraga.conf
# if ($http_origin) {
#     add_header Access-Control-Allow-Origin *;
# }
Caddyfile
# header @mahoraga-has-origin Access-Control-Allow-Origin *

The frontend configuration depends on the library you directly use:

<!doctype html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script type="text/javascript" src="http://127.0.0.1:3450/pyodide/v0.29.4/full/pyodide.js"></script>
  </head>
  <body>
    <script type="text/javascript">
      async function main() {
        let pyodide = await loadPyodide();
        await pyodide.loadPackage("micropip");
        // Prefer Pyodide-maintained wheels over PyPI ones
        // If this is not desired, remove the following lines
        pyodide.runPython(`
            import micropip
            class _Transaction(micropip.transaction.Transaction):
                def __post_init__(self):
                    super().__post_init__()
                    self.search_pyodide_lock_first = True
            micropip.package_manager.Transaction = _Transaction
        `);
        const micropip = pyodide.pyimport("micropip");
        micropip.set_index_urls("http://127.0.0.1:3450/pypi/simple/{package_name}/?micropip=1");
        await micropip.install(["your_package"]);
        pyodide.runPython(`# Your Python code here`);
      }
      main();
    </script>
  </body>
</html>
<!doctype html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script type="module" src="http://127.0.0.1:3450/npm/@pyscript/core@0/dist/core.js"></script>
    <link rel="stylesheet" href="http://127.0.0.1:3450/npm/@pyscript/core@0/dist/core.css">
  </head>
  <body>
    <script type="py" config='{
      "index_urls": ["http://127.0.0.1:3450/pypi/simple/{package_name}/?micropip=1"],
      "interpreter": "http://127.0.0.1:3450/pyodide/v0.29.4/full/pyodide.mjs",
      "packages": ["your_package"]
    }'># Your Python code here</script>
  </body>
</html>
<!doctype html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
    <link rel="stylesheet" href="http://127.0.0.1:3450/npm/@stlite/browser@1/build/stlite.css">
  </head>
  <body>
    <div id="root"></div>
    <script type="module">
      import { mount } from "http://127.0.0.1:3450/npm/@stlite/browser@1/build/stlite.js";
      mount(
        {
          pyodideUrl: "http://127.0.0.1:3450/pyodide/v0.29.4/full/pyodide.js",
          requirements: [
            "http://127.0.0.1:3450/pypi/packages/py3/b/blinker/blinker-1.9.0-py3-none-any.whl",
            "http://127.0.0.1:3450/pypi/packages/py3/i/itsdangerous/itsdangerous-2.2.0-py3-none-any.whl",
            "http://127.0.0.1:3450/pypi/packages/py3/p/python-multipart/python_multipart-0.0.32-py3-none-any.whl",
            "http://127.0.0.1:3450/pypi/packages/py3/t/tenacity/tenacity-9.1.4-py3-none-any.whl",
          ],
          entrypoint: "your_app.py",
          files: {
            "your_app.py": `# Your Python code here`,
          },
        },
        document.getElementById("root"),
      );
    </script>
  </body>
</html>

pymanager

The next generation of the official Python installer for Windows, pymanager, can be downloaded from Mahoraga:

curl -O http://127.0.0.1:3450/python/pymanager/python-manager-26.3.msix
curl -O http://127.0.0.1:3450/python/pymanager/python-manager-26.3.msi

Note

Support for the command pymanager install hasn't been implemented in Mahoraga yet.