2026-09-25 – Move SEAMM off conda: a uv-managed environment, PyPI only¶
Status (2026-09-27): complete and deployed. Phases 0-4 done: uv-managed
environment, PyPI-only packages, new Zenodo package list + universal lock (concept
record 22970126; the old 7789853 is frozen), CI on uv, seamm-manager released
(2026.9.26 through 2026.9.27: find_conda, datastore.ensure, sync_manager,
conservative pip policy for the plug-ins’ conda environment files,
update --latest), and all four machines migrated with an end-to-end job each
(this Mac, paul.local, ChemAI, MolSSI10; NOTES_phase4.rst). The 2026-09-27
incident (a shared conda environment’s torch and xnns upgraded by a plug-in’s
environment file) and its fixes are in NOTES_phase4.rst. What is left is
listed under Outstanding at the end.
Why¶
Eight core packages are installed from conda-forge while the ~45 plug-ins and the other core libraries are installed with pip, all into one conda environment. The mixed model causes recurring problems:
Release lag. A conda-forge feedstock publishes hours to days after the PyPI release and needs a separate PR per package. Measured in Phase 0: the production
molsystemwas three months behind PyPI.Two resolvers, one environment. Conda solves its packages ignoring everything pip installed; conda then runs
pip install -Uon the pip list; pip does see conda’s packages and upgrades them when asked; the next conda solve puts the conda copy back. This is the source of the “condapytorchreplaced the pip CUDA torch” and “pippymdireplaced conda’s MPI-linked MDI” incidents. Phase 0 showed the same thing in a dry run:conda update --allon a mixed environment would reinstall a condapillowover the pip one and walkbibtexparserpast the<2bound that four SEAMM packages declare.The installer cannot change a package’s channel (
install.py/update.pyraise NotImplementedError), so the metadata cannot be changed without breaking every existing installation.Environment-level rot.
create_envlists thedefaultschannel; production has apkgs/mainsqliteand a conda-forgelibsqlitethat both ownlib/libsqlite3.dylib.
The original reason for conda was compiled dependencies with no wheels,
chiefly openbabel and rdkit. That reason is gone (survey below).
The one thing pip cannot supply is the interpreter, with its tkinter and
sqlite3; uv supplies exactly that. Conda remains the right tool for
the external codes, which are not Python packages.
Survey: what actually needed conda¶
Packages on the conda-forge channel in seamm_packaging’s metadata.py:
molsystem seamm seamm-dashboard seamm-datastore
seamm-ff-util seamm-installer seamm-util seamm-widgets
plus reference-handler, which is conda-forge in production but absent
from that list. seamm-dashboard is being retired.
PyPI wheels for every compiled dependency, checked 2026-09-25:
Package |
Version |
Wheels |
|---|---|---|
openbabel |
3.2.1 |
official (upstream); macOS x86_64 + arm64, manylinux x86_64 + aarch64, Windows; cp310 – cp314 |
rdkit |
2026.3.6 |
macOS arm64, manylinux, Windows; cp310 – cp315 |
psutil, pillow |
current |
all platforms |
spglib, |
current |
all platforms |
pycifrw |
||
scipy, numpy, |
current |
all platforms |
statsmodels, |
||
sqlalchemy |
||
apsw |
3.53.4.0 |
all platforms (qcportal’s compiled dependency) |
kaleido, pmw |
current |
PyPI only – already pip |
What the interpreter must bring: tkinter with Tk, and sqlite3.
The uv-managed cpython-3.12.14 (python-build-standalone) brings
Tk 9.0.4 and SQLite 3.53.1. The python.org 3.12 installer brings Tk
8.6 and is the fallback interpreter if Tk 9 proves a problem; uv can
create the venv from it.
What Phase 0 established¶
Full detail in the two NOTES files. The short version:
Everything works on pip wheels, on macOS arm64, both in a conda environment with the conda copies removed (
NOTES_phase0) and on a conda-free uv-managed Python (NOTES_phase0_uv): openbabel, rdkit, molsystem round trips, datastore login,PIL.ImageTk.Tk 9.0.4 works headlessly for Pmw 2.1.1, seamm_widgets (incl. PeriodicTable, UnitEntry) and
TkFlowchart. Appearance not yet judged.A uv install of the core set takes 42 s; the venv is 521 MB (conda environment: 2.4 GB).
uv pip compile --universallocks it in 1 s.The in-place path is the hard part. After moving the SEAMM packages, ~150 conda Python packages remain and
conda update --allclobbers pip. A fresh environment has none of this. Hence the decision to drop in-place.Undeclared dependencies surface at once on a pip-only base. The universal lock had no
openbabelbecausemolsystem’sinstall_requiresdoes not list it (only the conda recipe did).
Retiring seamm-dashboard¶
All three fragile pins in the metadata (connexion <3.0,
flask-jwt-extended =4.5.3, pyjwt =2.9.0) belong to the dashboard and
go with it, as do ~20 Flask packages and sqlalchemy<2.0.
seamm-datastore stays: it is the data layer of seamm_webui (its
db.py and every router import the datastore models) and of
seamm_exec (exec_flowchart.py creates and updates the Job row). The
JobServer reads job status through sqlite3 directly and does not import
it. The core seamm package lists it in requirements.txt without
importing it; that dependency can be dropped.
Target architecture¶
Main environment. A uv-managed CPython 3.12 (later 3.13) in a venv at a
fixed location under the SEAMM root, e.g. ~/SEAMM/venv (decision
below). Every SEAMM package and every Python dependency from PyPI via
uv pip install. No conda anywhere in it.
Installer. seamm-manager (new package, decision 4) installed with
uv tool install into its own isolated environment, outside the
environment it manages, and also into the venv for the plug-in installer
scripts. It can
therefore create, delete and recreate the SEAMM venv freely, and updating
the installer (uv tool upgrade seamm-installer) never reinstalls a
package that is currently running.
Known-good set. seamm_packaging publishes, nightly, a universal
lock (uv pip compile --universal) alongside the package list. The
installer passes it as a constraints file by default; --no-constraints
opts out (default in development mode).
Code environments. Unchanged: the plug-in installers create conda
environments for Psi4, MOPAC, DFTB+, LAMMPS, xtb, Packmol, TorchANI via
conda-forge. The Conda class stays for this. Conda is required on the
machine only when one of those plug-ins is installed; the installer should
say so at that point rather than at bootstrap. Docker remains the other
route.
Migration. No in-place conversion. Users create the new environment with the bootstrap below, then delete (or keep, renamed) the old conda environment. The old package list on Zenodo is left as is, so old installations freeze cleanly rather than break (decision below).
Bootstrap (the whole user-facing story, same on macOS and Linux):
curl -LsSf https://astral.sh/uv/install.sh | sh
uv tool install seamm-manager
seamm-manager install all # creates the venv, installs, sets up services/apps
Phases¶
Phase 0 – validate (done)¶
Done 2026-09-25 for macOS arm64; see the NOTES. Remaining, before Phase 3 is finalized:
Open the flowchart editor and two or three step dialogs under Tk 9 by eye (Paul). A durable test environment for this is at
~/Work/SEAMM/Testing/uvenv(core + the MolSSI plug-ins); launch with~/Work/SEAMM/Testing/uvenv/bin/seamm. If Tk 9 is unacceptable, the python.org interpreter is the base anduv venv --python <path>points at it; the rest of the plan is unchanged.Run
phase0_checks.py(in the session scratchpad; recreate from the NOTES if lost) on ChemAI with a uv-managed Linux Python. Needs uv installed in that account – ask first.
Phase 1 – make the packages honest about their dependencies¶
Independent of the installer; ordinary PyPI releases. Pip only knows
install_requires, so every dependency that today lives in a conda
recipe or in the packaging metadata’s side-table must move into the
package.
Audit ``install_requires`` of
molsystem,seamm,seamm-datastore,seamm-ff-util,seamm-installer,seamm-util,seamm-widgets,reference-handleragainst theirconda/meta.yamlrun:lists and the metadatadependenciestables. Known gaps:molsystemlacksopenbabel;seamm-utillackskaleido. Check the plug-ins the same way where they have a recipe.Stdlib removals hidden by conda’s setuptools.
lammps_stepimportsGPUtil, which importsdistutils(gone in 3.12); it only loads in conda envs because they ship setuptools. Replace GPUtil with ashutil.which("nvidia-smi")check. Grep all plug-ins fordistutils,pkg_resources(torchani_step) andimp.molsystem/setup.py: fix the stale “openbabel has no wheels” comment.seamm/requirements.txt: dropseamm-datastore.seamm-installer: adduvhandling (Phase 3) but not a hard dependency on conda;psutiletc. are ordinary requirements.devtools/conda-envs/test_env.yamlin each: SEAMM deps in thepip:sublist per the existing CI convention (or switch CI to uv outright – separate decision, not required here).Delete or archive
conda/meta.yamland the feedstocks with a pointer to PyPI once Phase 4 is done. No further conda-forge releases are needed for correctness because nothing in the new design reads conda-forge.
Phase 1b – CI on uv instead of conda¶
Why: every package’s CI builds a hand-maintained
devtools/conda-envs/test_env.yaml with setup-miniconda and then runs
pip install . --no-deps, so install_requires is never exercised. The
env file is a second copy of the dependency list (hence check_deps.py),
49 of the 75 files take seamm from conda-forge and so inherit openbabel
from the conda recipe, and a fix released to PyPI is not testable
downstream until conda-forge catches up. molsystem’s missing openbabel
survived for years because CI could not see it; the energy_scan CI failure
on 2026-09-25 was the same mechanism.
What changes, in molssi-seamm/devops (one change, every package uses
the reusable workflows at @main):
The four workflows (
CI,BranchCI,Docs,Release) gain a uv path beside the conda path, selected per step withif: hashFiles('devtools/conda-envs/test_env.yaml') == ''. A package with the env file keeps the conda path unchanged; a package that deletes it gets uv. Migration is therefore per package, at its own pace.The uv path:
astral-sh/setup-uvwith the matrix Python,uv venv --seed .venv(seeded sopython -m pipinbuildDocs.shstill works), the venv’sbinonGITHUB_PATH, then oneuv pip install '.[test,docs]'with dependencies plus the fixed tooling set: pytest, pytest-cov, black, flake8, codecov, and the docs tools every package uses (pydata-sphinx-theme, sphinx-design, sphinx-copybutton, sphinxnotes-strike, sphinx-rtd-theme, rinohtype, pystemmer, pygments). uv ignores extras a package does not define, so[test,docs]costs nothing where absent and lets a package add its own.conda listbecomesuv pip list/pip list; thedeployjob (already conda-free) stops callingconda list.Compiled dependencies (openbabel, rdkit, numpy, scipy) come from wheels, which is exactly what users get.
Per package, when it next releases: delete test_env.yaml; add a
[test]/[docs] extra only if it needs something beyond the fixed
set. check_deps.py becomes unnecessary for converted packages. Update
the cookiecutter template first (drop test_env.yaml from the plug-in
and substep templates).
Validation: push the devops change on a branch, point one package’s
BranchCI caller at @<branch> on a throwaway branch with
test_env.yaml deleted, confirm lint/tests/docs pass on ubuntu and macOS
for 3.11 and 3.12, then merge devops to main. Pilot packages: molsystem
(compiled wheels) and seamm_widgets (Tk). Remember the reusable-workflow
rerun gotcha: a re-run keeps the old devops version; push a new commit.
Phase 2 – seamm_packaging: the package list and the lock¶
Implemented 2026-09-25 in seamm_packaging PR #1, as below, with these
details: the database is "format": 2 with python, lock, doi,
conceptdoi and zenodo_id fields; the first upload (no zenodo_id)
creates a new deposition with a pre-reserved DOI, later ones add a version;
a packaging_dry_run command creates and discards a draft;
resolve_packages resolves without touching Zenodo; the whole 57-package
set resolves in ~3 s to 776 lock lines. seamm-installer stays in the
list until seamm-manager is on PyPI.
Published. The first check_for_changes after the merge created Zenodo
record 22970127 under concept record 22970126 (SEAMM Package List,
CC-BY-4.0, files SEAMM_packages.json + seamm.lock.txt), committed as
seamm_packaging 2026.9.25.1. The old record (concept 7789853) is frozen. The
installer’s lookup is “latest version of concept 22970126”.
Metadata. Remove the
repositoryfield’s role (everything is PyPI); moveseamm-dashboardtoexcluded plug-ins; delete thedependenciesside-tables (their content now lives ininstall_requiresper Phase 1); thelibsqlitepin becomes a documentation note about interpreter builds, since SQLite now comes with the interpreter.Resolution. Replace the conda dry-run in
create_full_environmentwithuv pip compile --universal --python-version 3.12over the full package set. Output:SEAMM_packages.json(name, version, type, description – no channel) andseamm.lock.txt(the universal lock). Dropseamm.yml/seamm_pinned.yml. The nightly job installs uv with the official script.Zenodo. Publish to a new Zenodo record. The installer reads the record ID from code; the new installer reads the new record. The old record’s last version is left untouched so the old installer keeps working against a frozen list and old environments simply stop updating. (Decision below; the alternative, updating the old record, needs an old-installer release to avoid
NotImplementedError.)Keep the draft-reuse / discard-on-failure logic from 2026-09-19.
Phase 3 – seamm_manager: uv-hosted environment, direct installs¶
(Decision 4: this is a new package seamm_manager built from the
seamm_installer code base, not an in-place change to seamm_installer.
Everything below refers to the new package; seamm_installer is frozen.)
The bulk of the code work. Files: install.py, update.py,
uninstall.py, util.py (create_env, find_packages,
package_info), my.py, services.py, apps.py, mac.py /
linux.py, datastore.py, data/.
New ``Uv`` class (
uv.py) besideConda:python_install,venv,pip_install(packages, constraints=None, upgrade=False),pip_list(--format json),pip_uninstall,locate(finduvon PATH or at~/.local/bin/uv). Thin subprocess wrappers, likeConda.Environment model.
my.environmentbecomes a venv path under the root (default<root>/venv);my.pythonits interpreter. Created on firstinstallif absent;seamm-installer environment recreatedeletes and rebuilds it (cheap: 42 s). Noconda activateanywhere.Install / update / uninstall become one uv command each:
uv pip install [-c lock] pkg ...,uv pip install -U [-c lock] pkg ...,uv pip uninstall pkg .... Apinnedentry in the package list ispkg==ver. Delete: the conda/pypi split, channel comparison,NotImplementedErrorbranches,create_envand its dependency side-table, the environment yml writing. Keep writingenvironments/<stamp>.txtfromuv pip freezeas the audit trail.Constraints.
--constraints(default on) fetchesseamm.lock.txtfrom the Zenodo record and passes-c;--no-constraintsopts out, and is the default whenmy.development.Self-management. The installer is a uv tool;
seamm-installer updateupgrades it withuv tool upgrade seamm-installerfirst, then re-execs itself so the rest of the update runs on the new code. If the installer is found to be running from inside the SEAMM venv (developer setups), skip that step and say so.Services and apps.
services.py(launchd / systemd) andapps.pyembed the interpreter path; point them at the venv and provideseamm-installer services reinstallfor users moving from the conda environment. The datastore alembic step indatastore.pylocatesalembic.iniviaimportlib.metadataand needs the venv’s python.Code environments. Untouched.
Condais required lazily: the first time a plug-in installer needs it, check for conda and print how to get Miniforge if missing.seamm-webui. Its dedicated environment becomes a second uv venv (
<root>/venv-webui) created the same way;install_seamm_webuiininstall.pyalready only needs python + pip.Remove the
defaultschannel everywhere anddata/seamm.yml/development.yml(replaced by the bootstrap and byseamm-installer install developmentwhich is a uv install of the dev package list).Tests. Unit tests for
Uvcommand construction (with/without constraints, pins, upgrade), for the install/update planners with a mockedUv, and an integration test that bootstraps a venv in a temp root and installs one small package.Release to PyPI.
uv tool install seamm-installeris then the only install path.
Phase 4 – roll out, migrate ourselves, document¶
Our machines, one at a time, JobServer idle: this Mac (
~/SEAMMand~/SEAMM_DEV), ChemAI (seammaccount), MolSSI10. Rename the conda environment rather than delete it until the new one has run real jobs; reinstall services and apps; verify a flowchart end to end and the webui.Developer workflow.
make install(pip install .) works unchanged inside a uv venv; document creatingseamm-devasuv venv+uv pip install -eor the existing Makefiles.Docs. New installation page (the three-line bootstrap); a migration page (create new, reinstall services, remove old conda env, what stays in
~/SEAMM); the “neverconda update --all” warning is no longer needed because there is no conda environment; conda is documented only under the code plug-ins. Update the main molssi-seamm.github.io docs.Announcement / release notes for users on the old record: what “frozen” means and how to move.
Archive the eight feedstocks.
Decisions (Paul, 2026-09-25)¶
Tk 9: accepted. The uv-managed standalone Python is the interpreter as is. Any Tk 9 rendering quirks are fixed in seamm_widgets as found.
Venv location: ``<root>/venv``, i.e.
~/SEAMM/venvby default and~/SEAMM_DEV/venvfor the dev root, so--rootselects both.Old Zenodo record: frozen. The new package list and lock go to a new record. Old installers keep working against the last conda-era list and simply stop seeing updates; an announcement tells people how to migrate.
Installer: a new package, ``seamm_manager`` (PyPI
seamm-manager, CLIseamm-manager), installed as a uv tool outside the venv and into the venv as an ordinary package so the plug-ins’*-step-installerscripts keep working. It ships a thinseamm_installercompatibility module (re-exportinginstaller_base) until the ten plug-ins that import it switch at their next release.seamm_installeron PyPI and conda-forge stays frozen as the conda-era tool – no ambiguity about which tool a document means. New repo, not a rename.Python: 3.12 now, 3.13 once CI covers it. Add 3.13 to the devops matrix first; bump the default when green across packages (
seamm-manager environment recreatemakes the switch cheap).
Risks and open questions¶
uv governance. Astral is venture-funded and uv is at 0.x. Mitigation: uv only drives standard wheels into a standard venv; falling back to python.org Python plus pip is a documentation change, not a repackaging.
python-build-standalone quirks. Non-framework build on macOS (same as conda today),
libeditrather thanreadline, occasional C extensions that object to how it is built. Nothing showed in Phase 0; the Tk 9 by-eye check is the remaining exposure.Tk 9 appearance. Headless OK; visual unknown. Fallback exists.
Linux untested for the uv path as of this writing.
Users who skip the announcement keep a frozen but working conda environment indefinitely. Acceptable.
Constraints staleness. The lock is regenerated when the package list changes; add a scheduled regeneration as well so third-party security fixes flow.
HPC accounts without ``~/.local/bin`` on PATH. The uv installer handles this for interactive shells; document the one-line PATH addition for batch scripts and for the JobServer’s environment.
Windows. Every wheel exists and uv supplies the interpreter, so this removes the last conda-only obstacle. Not in scope, but now plausible.
Outstanding (as of 2026-09-27)¶
Retire the conda-era pieces, once the venv installations are trusted:
The conda-era dashboards still running on ChemAI and MolSSI10 (port 55055) beside the new webui. ChemAI is hands-off until Paul asks for a specific action.
The old conda
seammenvironments andSEAMM-Installer.appon this Mac, paul.local, ChemAI and MolSSI10, kept as fallbacks.~/SEAMM_DEVon this Mac (conda-based development root withdev_jobserver).The conda-forge feedstocks of the seven core packages: archive or leave dormant.
Communicate:
DONE 2026-09-27: the main docs site (molssi-seamm.github.io, PR #56, live) describes the uv bootstrap and the SEAMM Manager, with a migration page.
A migration announcement to users.
Still to do on the docs site: Managing the Dashboard shows the old Dashboard’s screens and the queue how-to runs
seamm-dashboard; both need the web interface’s equivalents. The graphical page reuses the SEAMM Installer screenshots.
Loose ends found along the way:
xnn_step: the
devbranch carries uncommitted D4 work (another session) that must be rebased ontomain(2026.9.27 pinnedxnns<0.2; move the pin forward when an xnns release loads the older checkpoints again).pyxtal-step (third party) imports
pkg_resourcesand its installer fails on Python 3.12: report upstream or exclude it from the package list.ChemAI’s jobserver unit has a hand-added
sh -lclogin-shell wrapper the manager’s parser cannot show; a--login-shelloption onservices createwould make it reproducible.MolSSI10 has a stale
calpolylogin session (rootloginctl terminate-user).update --allreruns every plug-in’sconda env updateeven when the environment file is unchanged, which is slow; skip unchanged files.When Zenodo is slow or down,
show(and anything else that fetches the package list) prints the raw exception chain fromfind_packages. It should say that Zenodo is unavailable and fall back to the cached list. Seen 2026-09-27 (15 s responses).GitHub Pages was never enabled for seamm_manager, so its documentation 404’d; enabled 2026-09-27 from the existing
gh-pagesbranch.DONE 2026-09-27 (seamm-manager 2026.9.27.1): the Mac apps’ shell-script executable made Apple Silicon Macs without Rosetta ask for it; replaced by a compiled universal launcher.
Later by design:
Python 3.13 in CI and as the venv default once the stack is checked on it.