Skip to content

Packaging

Packaging and distribution

This document covers how simweave should be distributed, and how EdgeWeave consumes it.

The short answer

Publish simweave as a standalone package on PyPI. EdgeWeave imports it as an ordinary third-party dependency. The Electron installer bundles a Python runtime that has simweave pre-installed so end users get a zero-setup experience; power users who only want the engine pip install simweave on their own.

This is the right call for three reasons:

  1. EdgeWeave is an optional front-end, not the delivery vehicle. The library has real value without the GUI — for notebooks, for CI-embedded Monte Carlo sweeps, for headless batch runs on a cluster.
  2. Code generated by EdgeWeave has to import simweave. If the library only ships inside the Electron bundle, generated scripts don't run anywhere else. A PyPI release makes generated code portable.
  3. Two distribution channels means two release cadences. EdgeWeave can update its UI independently of the engine, and the engine can fix a Monte Carlo bug without you rebuilding the Electron app.
                 +----------------------+
                 |    GitHub repo       |
                 |  simweave (src/)       |
                 +----------------------+
                           |
              +------------+------------+
              |                         |
              v                         v
        +-----------+             +------------+
        |   PyPI    |             |  EdgeWeave |
        | simweave    | <- depends- |  Electron  |
        | (wheel)   |             |  bundle    |
        +-----------+             +------------+
              |                         |
              v                         v
      pip install simweave         ships a Python venv
      (CLI / notebook            with simweave prebundled
       / script user)            (GUI user)

Why not bundle inside EdgeWeave only?

  • Generated Python won't run on a fresh machine without EdgeWeave.
  • Users who want the engine in a Jupyter notebook, a Django app, or a FastAPI service are forced to install an entire Electron app. Many won't.
  • You lose the PyPI release graph (versions, yanks, dependency resolvers' understanding of simweave>=0.3).
  • CI environments can't install from an Electron bundle.

Why not vendor-copy inside EdgeWeave?

The EdgeWeave repo could in principle contain a copy of simweave and install it as a local dependency. That drifts. Either:

  • Treat EdgeWeave's copy as read-only (pull from PyPI at build time — then you might as well just declare a dependency), or
  • Allow EdgeWeave to patch locally, in which case EdgeWeave- generated code no longer works against stock PyPI simweave.

Both end badly.

Name on PyPI

The package name in pyproject.toml is currently simweave. Check availability on PyPI before tagging a release — the name is short and may be taken. Options if it is:

  • simweave-core — clear suffix, good for a package you'll later split
  • pysimweave — traditional Python prefix
  • A brandable name specific to your project (e.g., tempo-sim, atomclock-sim). This is the moment to pick, because renaming post-publication is painful.

If renaming, change:

  1. [project].name in pyproject.toml
  2. The folder src/simweave/ — rename to match
  3. Imports throughout — simweave<newname> is a simple sed
  4. CI workflow references
  5. README / PACKAGING / demos

Or keep the package name simweave on PyPI and the importable module simweave — even if it collides you can typically reclaim a dormant name via PyPI's name reuse policy.

First release checklist

Before tagging v0.1.0:

  • PyPI name reserved (register a dummy 0.0.0a0 upload if needed)
  • LICENSE file present (MIT is declared in pyproject.toml but should also be a top-level file)
  • CHANGELOG.md seeded with the initial release notes
  • README.md renders correctly on PyPI (no relative links that won't resolve off-repo; use absolute GitHub URLs for badges)
  • pyproject.toml includes [project.urls] pointing at the repo, docs, and issue tracker
  • python -m build && twine check dist/* passes locally
  • pip install -e .[dev] && pytest is green
  • Trusted publishing configured on PyPI (preferred over uploading API tokens to GitHub secrets)
# Add this to pyproject.toml before release
[project.urls]
Homepage = "https://github.com/<you>/sim_engine"
Repository = "https://github.com/<you>/sim_engine"
Issues = "https://github.com/<you>/sim_engine/issues"
Documentation = "https://github.com/<you>/sim_engine#readme"

Release workflow

The repo ships a two-stage release flow at .github/workflows/release.yml:

  1. Pre-release tags (any tag with a letter or dash — e.g. v0.1.0rc1, v0.1.0-alpha) upload to TestPyPI only.
  2. Stable semver tags (e.g. v0.1.0) upload to PyPI.

Cutting a release:

# 1. bump version in pyproject.toml
# 2. update CHANGELOG.md
# 3. commit and tag
git commit -am "release: 0.1.0"
git tag v0.1.0
git push --follow-tags

GitHub Actions picks up the tag, builds a wheel + sdist, runs twine check, and publishes via PyPI trusted publishing (OIDC — no secrets to manage).

Pull the release back down on your Electron build machine with pip install simweave==0.1.0 and you have a reproducible EdgeWeave build.

EdgeWeave integration

Depending on simweave from EdgeWeave

Inside EdgeWeave's Python side (edgeweave/pyproject.toml or requirements.txt):

dependencies = [
    "simweave>=0.1,<0.2",   # pin to minor until 1.0
    ...
]

The Electron packager has to embed the Python runtime and all dependencies. The typical pattern is:

  1. Build a .venv at package time with pip install -r requirements.txt.
  2. Use pyinstaller, briefcase, or python-build-standalone to produce a redistributable Python. python-build-standalone is the lightest if you just need a headless embedded interpreter.
  3. Ship app.asar + the .venv/ alongside — the Electron main process spawns python pointing at that interpreter.

Code generation contract

EdgeWeave's generated Python should import from the public API surface, not internal modules:

# Good
from simweave import Queue, Service, Warehouse, a_star

# Bad
from simweave.discrete._internal import _WorkChannel

The top-level simweave/__init__.py re-exports every stable name; anything not re-exported is not part of the API contract.

Versioning contract

  • Bug fixes → patch (0.1.00.1.1)
  • New features that are additive → minor (0.1.00.2.0)
  • Breaking changes → major (0.x1.0.0)

EdgeWeave should pin to a minor range (simweave>=0.1,<0.2) pre-1.0 and to a major range (simweave>=1,<2) once we hit 1.0. That way we can fix bugs without breaking existing EdgeWeave builds, and users upgrading EdgeWeave get compatible engine upgrades automatically.

Distribution sanity-check

After each release, install the published wheel into a fresh venv and run the demos:

python -m venv /tmp/simweave-check
source /tmp/simweave-check/bin/activate
pip install simweave==0.1.0
python - <<'PY'
from simweave import Queue, Service, SimEnvironment, Entity
q = Queue(maxlen=5)
print("simweave installed OK:", q)
PY

This is fast enough to be worth automating as a post-publish step in the release workflow.

Wheels: pure-Python today, binary tomorrow

simweave is pure Python today — no compiled extensions — so the wheel is py3-none-any.whl and works everywhere Python 3.10+ runs. That's the ideal state.

When we eventually add Cython or Numba-compiled inner loops, the story changes:

  • Numba: stays pure-Python at distribution time; JIT happens on the user's machine at first call. Preferred.
  • Cython: requires platform-specific wheels (manylinux, macosx_x86, macosx_arm64, win_amd64). Use cibuildwheel in CI; prebuilt binary wheels for each platform become a chore but buy 2–5× over Numba on the hot path. Only worth it for genuinely hot code.

If we take the Cython path, add a [build-system] section pulling in cython and numpy as build requires, and add a cibuildwheel job to the release workflow.