You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Packaging metadata is spread across four files, three of which are legacy setuptools/distutils formats:
file
contents
status
setup.py
all real metadata: name, version, deps, author, URLs, long_description
imperative, PEP 517 discourages it
setup.cfg
only [metadata] description_file = README.md
dead — setup.py already sets long_description, so this does nothing
MANIFEST
a tracked file whose first line reads # file GENERATED by distutils, do NOT edit
stale build artifact, checked in by accident
doc/requirements.txt
Sphinx docs dependencies
separate file, hand-synced with install_requires
Consolidate all of it into a single declarative PEP 621 pyproject.toml.
Because setup.py goes away, check_pypi_packaging.yml can no longer build with python setup.py sdist bdist_wheel, so it moves to python -m build — the PEP 517 front end release.yml already uses. That subsumes #326, which therefore does not need doing separately.
Where the version lives
Decision: version under [project] in pyproject.toml is the single source of truth, and scadnano.__version__ is read back from the installed distribution's metadata via importlib.metadata. Bumping a release is a one-line edit.
This replaces the previous arrangement, where the truth was a __version__ string literal in scadnano/scadnano.py that setup.py and doc/conf.py each parsed with their own copy of an extract_version() helper that searched for a magic trailing comment and split the line on =.
An earlier draft of this issue proposed keeping the literal in source and having pyproject.toml read it via dynamic + version = {attr = "scadnano.__version__"}. That attribute path was wrong — scadnano/__init__.py does from scadnano.scadnano import *, and import * skips names beginning with an underscore, so scadnano.__version__ did not exist at all. Fixed separately by exporting it explicitly.
Consequence: installation becomes mandatory
Reading the version from distribution metadata means import scadnano requires the package to be installed. Copying scadnano.py into a working directory and importing it with nothing installed — previously a documented, supported workflow — now raises PackageNotFoundError. That is intended: scadnano becomes a normal pip-installed package. It requires retiring that workflow from README.md and CONTRIBUTING.md, and fixing 21 example scripts that import origami_rectangle and modifications as top-level modules (which only resolves when the package directory itself is on sys.path, so those were already broken for every pip-installed user).
Consequence: stale metadata under editable installs
pip install -e . makes the source live, not the metadata. pip writes scadnano-<version>.dist-info/METADATA once, at install time, with the version baked into the directory name. Bump the version without reinstalling and scadnano.__version__ keeps reporting the old value — which is then written into the "version" field of every .sc file produced.
Two ways to handle it were considered:
A. Detect it. A test asserting scadnano.__version__ equals the pyproject.toml version, turning silent staleness into an immediate failure naming the fix.
B. Avoid it. Keep the literal in scadnano.py and have pyproject.toml read it via dynamic + attr:. Eliminates the problem entirely, since an editable install reads the live source — but re-enables importing from a bare checkout, undoing the mandatory-install property above.
Going with A. The window is narrow (between bumping and reinstalling), pip install -e . takes seconds, and run_unit_tests.yml installs immediately before running the suite, so the guard cannot fire spuriously in CI.
Also worth fixing while in there
tests_require is silently ignored.setup.py passes tests_require=['openpyxl'], which setuptools no longer recognizes — it warns UserWarning: Unknown distribution option: 'tests_require' on every build. Becomes a tests extra. Noted previously in check_pypi_packaging.yml uses deprecated python setup.py sdist bdist_wheel #326.
doc/requirements.txt folds in as a docs extra, so Read the Docs and the Docs Check workflow install one declared list instead of two hand-synced ones.
MANIFEST should be deleted and gitignored. It is a generated distutils artifact, not MANIFEST.in, and nothing reads it.
CI never installed the package.run_unit_tests.yml installed only openpyxl and tabulate and relied on the source tree being importable — exactly the workflow being removed. It needs pip install -e .[tests].
check_pypi_packaging.yml built the artifacts and threw them away. It should install the built wheel into a clean virtualenv and import it from a directory containing no source tree, asserting the version matches and a design serializes. Running from the repo root proves nothing: the flat layout means the checkout shadows the installed package. (A fuller fix for that shadowing is move the package to a src/ layout so tests exercise the installed package #345.)
Verifying the migration
Compare artifacts before and after:
python -m build produces scadnano-<version>.tar.gz and scadnano-<version>-py3-none-any.whl, and the wheel contains __init__.py, scadnano.py, modifications.py, origami_rectangle.py.
twine check dist/* passes, confirming the README still renders on PyPI — worth checking explicitly, since long_description_content_type='text/markdown; variant=GFM' carries over into readme.content-type and a mistake there is only visible on the PyPI page.
Expect metadata to move to the modern PEP 621/639 spellings: Home-page → Project-URL, Author/Author-email combined, License: MIT → License-Expression: MIT. One field is genuinely lost: Download-URL, which was an f-string interpolating the version into a GitHub archive zip. That cannot be expressed in static TOML, and PyPI does not use it.
Packaging metadata is spread across four files, three of which are legacy setuptools/distutils formats:
setup.pylong_descriptionsetup.cfg[metadata] description_file = README.mdsetup.pyalready setslong_description, so this does nothingMANIFEST# file GENERATED by distutils, do NOT editdoc/requirements.txtinstall_requiresConsolidate all of it into a single declarative PEP 621
pyproject.toml.Because
setup.pygoes away,check_pypi_packaging.ymlcan no longer build withpython setup.py sdist bdist_wheel, so it moves topython -m build— the PEP 517 front endrelease.ymlalready uses. That subsumes #326, which therefore does not need doing separately.Where the version lives
Decision:
versionunder[project]inpyproject.tomlis the single source of truth, andscadnano.__version__is read back from the installed distribution's metadata viaimportlib.metadata. Bumping a release is a one-line edit.This replaces the previous arrangement, where the truth was a
__version__string literal inscadnano/scadnano.pythatsetup.pyanddoc/conf.pyeach parsed with their own copy of anextract_version()helper that searched for a magic trailing comment and split the line on=.An earlier draft of this issue proposed keeping the literal in source and having
pyproject.tomlread it viadynamic+version = {attr = "scadnano.__version__"}. That attribute path was wrong —scadnano/__init__.pydoesfrom scadnano.scadnano import *, andimport *skips names beginning with an underscore, soscadnano.__version__did not exist at all. Fixed separately by exporting it explicitly.Consequence: installation becomes mandatory
Reading the version from distribution metadata means
import scadnanorequires the package to be installed. Copyingscadnano.pyinto a working directory and importing it with nothing installed — previously a documented, supported workflow — now raisesPackageNotFoundError. That is intended: scadnano becomes a normal pip-installed package. It requires retiring that workflow from README.md and CONTRIBUTING.md, and fixing 21 example scripts that importorigami_rectangleandmodificationsas top-level modules (which only resolves when the package directory itself is onsys.path, so those were already broken for every pip-installed user).Consequence: stale metadata under editable installs
pip install -e .makes the source live, not the metadata. pip writesscadnano-<version>.dist-info/METADATAonce, at install time, with the version baked into the directory name. Bump the version without reinstalling andscadnano.__version__keeps reporting the old value — which is then written into the"version"field of every.scfile produced.Two ways to handle it were considered:
scadnano.__version__equals thepyproject.tomlversion, turning silent staleness into an immediate failure naming the fix.scadnano.pyand havepyproject.tomlread it viadynamic+attr:. Eliminates the problem entirely, since an editable install reads the live source — but re-enables importing from a bare checkout, undoing the mandatory-install property above.Going with A. The window is narrow (between bumping and reinstalling),
pip install -e .takes seconds, andrun_unit_tests.ymlinstalls immediately before running the suite, so the guard cannot fire spuriously in CI.Also worth fixing while in there
tests_requireis silently ignored.setup.pypassestests_require=['openpyxl'], which setuptools no longer recognizes — it warnsUserWarning: Unknown distribution option: 'tests_require'on every build. Becomes atestsextra. Noted previously in check_pypi_packaging.yml uses deprecatedpython setup.py sdist bdist_wheel#326.doc/requirements.txtfolds in as adocsextra, so Read the Docs and theDocs Checkworkflow install one declared list instead of two hand-synced ones.MANIFESTshould be deleted and gitignored. It is a generated distutils artifact, notMANIFEST.in, and nothing reads it.run_unit_tests.ymlinstalled onlyopenpyxlandtabulateand relied on the source tree being importable — exactly the workflow being removed. It needspip install -e .[tests].check_pypi_packaging.ymlbuilt the artifacts and threw them away. It should install the built wheel into a clean virtualenv and import it from a directory containing no source tree, asserting the version matches and a design serializes. Running from the repo root proves nothing: the flat layout means the checkout shadows the installed package. (A fuller fix for that shadowing is move the package to a src/ layout so tests exercise the installed package #345.)Verifying the migration
Compare artifacts before and after:
python -m buildproducesscadnano-<version>.tar.gzandscadnano-<version>-py3-none-any.whl, and the wheel contains__init__.py,scadnano.py,modifications.py,origami_rectangle.py.twine check dist/*passes, confirming the README still renders on PyPI — worth checking explicitly, sincelong_description_content_type='text/markdown; variant=GFM'carries over intoreadme.content-typeand a mistake there is only visible on the PyPI page.Home-page→Project-URL,Author/Author-emailcombined,License: MIT→License-Expression: MIT. One field is genuinely lost:Download-URL, which was an f-string interpolating the version into a GitHub archive zip. That cannot be expressed in static TOML, and PyPI does not use it.