Upload index.html with huggingface_hub
Browse files- index.html +692 -17
index.html
CHANGED
|
@@ -1,19 +1,694 @@
|
|
| 1 |
<!doctype html>
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
</html>
|
|
|
|
| 1 |
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
| 6 |
+
<title>Flit Migration guide — map setup files to flit config</title>
|
| 7 |
+
<meta name="description" content="This free browser tool maps setup.py, setup.cfg, and older project configuration to replacement Flit settings and shows build blockers.">
|
| 8 |
+
<style>
|
| 9 |
+
:root{color-scheme:light}body{margin:0;font:15px/1.55 system-ui,-apple-system,"Segoe UI",sans-serif;background:#faf9f5;color:#1a1512}
|
| 10 |
+
.wrap{max-width:960px;margin:0 auto;padding:28px 18px 60px}h1{font-size:26px;margin:0 0 6px}.lede{color:#4a403a;margin:0 0 16px}
|
| 11 |
+
textarea{width:100%;min-height:260px;font:13px/1.45 ui-monospace,Menlo,Consolas,monospace;padding:12px;border:1px solid #d9d0c4;border-radius:8px;box-sizing:border-box}
|
| 12 |
+
.bar{display:flex;gap:10px;align-items:center;margin:12px 0}button{font:600 15px system-ui;padding:10px 18px;border:0;border-radius:8px;background:#1a1512;color:#fff;cursor:pointer}
|
| 13 |
+
input[type=date]{font:14px system-ui;padding:8px;border:1px solid #d9d0c4;border-radius:6px}
|
| 14 |
+
table{border-collapse:collapse;width:100%;font-size:14px;margin-top:10px}th,td{text-align:left;padding:8px 10px;border-bottom:1px solid #eee;vertical-align:top}th{font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:#7a6e65}
|
| 15 |
+
.sev.error{color:#a8551f;font-weight:700}.sev.warn,.sev.warning{color:#96691f}.ok{color:#2a6446;font-weight:600}.sum{font-weight:600}
|
| 16 |
+
.note{font-size:13px;color:#7a6e65;margin-top:18px}#paid{margin-top:22px;padding:14px 16px;border:1px solid #e2d8cb;border-radius:10px;background:#fffdfa}
|
| 17 |
+
</style>
|
| 18 |
+
<script data-grow-initializer="">!(function(){window.growMe||((window.growMe=function(e){window.growMe._.push(e);}),(window.growMe._=[]));var e=document.createElement("script");(e.type="text/javascript"),(e.src="https://faves.grow.me/main.js"),(e.defer=!0),e.setAttribute("data-grow-faves-site-id","U2l0ZTphZjljMzgyMC0yMmU5LTRhYjgtYTIxNS03OTRhMGY1N2ExYzM=");var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t);})();</script>
|
| 19 |
+
<!--yj-canon-s101--><link rel="canonical" href="https://getreadystack.com/tools/flit-migration-check"><style data-yj-mfit="1">#out{max-width:100%}#out td,#out th{overflow-wrap:anywhere}#out pre,#out code{white-space:pre-wrap;overflow-wrap:anywhere}@media (max-width:640px){table{display:block;max-width:100%;overflow-x:auto}pre{max-width:100%;overflow-x:auto}code,a{overflow-wrap:anywhere}img,svg,video,canvas,iframe{max-width:100%}textarea,input,select{max-width:100%;box-sizing:border-box}.yj-lgbar{position:static!important;justify-content:flex-end;margin:0 0 10px}h1,h2,h3,h4,p,li,dd,dt,blockquote,figcaption,summary,label,.res,.lead{overflow-wrap:anywhere}label{max-width:100%;box-sizing:border-box}#out table:not(:has(tr>:nth-child(4))){display:table;table-layout:fixed;width:100%}#out table:has(tr>:nth-child(4)),#out table:has(tr>:nth-child(4)) tbody,#out table:has(tr>:nth-child(4)) tr,#out table:has(tr>:nth-child(4)) td{display:block;width:auto;overflow:visible}#out table:has(tr>:nth-child(4)) thead{display:none}#out table:has(tr>:nth-child(4)) tr{padding:10px 0;border-bottom:1px solid rgba(128,128,128,.28)}#out table:has(tr>:nth-child(4)) td{border:0;padding:1px 0}#out table:has(tr>:nth-child(4)) td:nth-child(-n+3){display:inline-block;margin-right:10px;font-size:12px;opacity:.78}}</style>
|
| 20 |
+
</head>
|
| 21 |
+
<body>
|
| 22 |
+
<div class="wrap">
|
| 23 |
+
<h1>Flit Migration guide — map setup files to flit config</h1>
|
| 24 |
+
<p class="lede">Flit migration map for Python packages: finds every setup.py, setup.cfg and setuptools/old-flit pyproject.toml line and prints the exact flit_core 4 [project] line that replaces it, plus the blockers Flit cannot build. Runs entirely in your browser — nothing is uploaded.</p>
|
| 25 |
+
<textarea id="src" spellcheck="false"># setup.py for "tidyframe" -- the file this project still builds from
|
| 26 |
+
import os
|
| 27 |
+
from setuptools import setup, find_packages
|
| 28 |
+
|
| 29 |
+
here = os.getcwd()
|
| 30 |
+
with open(os.path.join(here, "README.md")) as f:
|
| 31 |
+
readme = f.read()
|
| 32 |
+
|
| 33 |
+
setup(
|
| 34 |
+
name="tidyframe",
|
| 35 |
+
version="2.4.1",
|
| 36 |
+
description="Tidy helpers for pandas frames",
|
| 37 |
+
long_description=readme,
|
| 38 |
+
long_description_content_type="text/markdown",
|
| 39 |
+
author="Dana Example",
|
| 40 |
+
author_email="dana@example.org",
|
| 41 |
+
url="https://github.com/example/tidyframe",
|
| 42 |
+
license="MIT",
|
| 43 |
+
packages=find_packages(exclude=["tests"]),
|
| 44 |
+
package_dir={"": "src"},
|
| 45 |
+
include_package_data=True,
|
| 46 |
+
package_data={"tidyframe": ["schemas/*.json"]},
|
| 47 |
+
data_files=[("share/man/man1", ["docs/tidyframe.1"])],
|
| 48 |
+
python_requires=">=3.8",
|
| 49 |
+
setup_requires=["wheel"],
|
| 50 |
+
install_requires=["pandas>=2.0", "click>=8"],
|
| 51 |
+
extras_require={"test": ["pytest>=7"], "docs": ["sphinx"]},
|
| 52 |
+
entry_points={
|
| 53 |
+
"console_scripts": ["tidyframe=tidyframe.cli:main"],
|
| 54 |
+
"pandas_plugins": ["tidy=tidyframe.plugin:register"],
|
| 55 |
+
},
|
| 56 |
+
classifiers=["License :: OSI Approved :: MIT License"],
|
| 57 |
+
)</textarea>
|
| 58 |
+
<div class="bar"><button id="go">Check</button><label>as of <input type="date" id="today"></label></div>
|
| 59 |
+
<div id="out" aria-live="polite"></div>
|
| 60 |
+
<div id="paid"><strong>Free here:</strong> Check the open setup.py, setup.cfg or pyproject.toml: every setuptools or old-flit line with its line number and the exact flit_core 4 replacement line, plus the blockers Flit cannot build. <strong>In the editor:</strong> Whole workspace: one dated migration plan mapping every item in every package to its flit_core 4 replacement, and a team CI gate that fails a build adding new setuptools-only use. — that part asks for a licence key. <a href="https://getreadystack.com/api/buy/cl/polar_cl_VI4ER6LWKbRiLZHzzqUpAK77hPReaZmifZSkj19xgaN">Workspace migration plan + team CI gate — $29 once</a></div>
|
| 61 |
+
<p class="note">Same engine as the VS Code extension, byte for byte.</p>
|
| 62 |
+
</div>
|
| 63 |
+
<script>var FLIT_RULES = [
|
| 64 |
+
{
|
| 65 |
+
"id": "SP01",
|
| 66 |
+
"check": "setuptools_setup_py",
|
| 67 |
+
"files": [
|
| 68 |
+
"setup.py"
|
| 69 |
+
],
|
| 70 |
+
"re": "^\\s*(from\\s+(setuptools|distutils)[\\w.]*\\s+import|import\\s+(setuptools|distutils))",
|
| 71 |
+
"sev": "warn",
|
| 72 |
+
"msg": "setup.py drives this build; flit_core reads no setup.py",
|
| 73 |
+
"fix": "delete setup.py; in pyproject.toml write [build-system] requires = [\"flit_core \u003e=3.11,\u003c5\"] and build-backend = \"flit_core.buildapi\"",
|
| 74 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 75 |
+
},
|
| 76 |
+
{
|
| 77 |
+
"id": "SP02",
|
| 78 |
+
"check": "compiled_extension",
|
| 79 |
+
"files": [
|
| 80 |
+
"setup.py",
|
| 81 |
+
"setup.cfg"
|
| 82 |
+
],
|
| 83 |
+
"re": "\\bext_modules\\s*=|\\bExtension\\s*\\(|\\bcythonize\\s*\\(|\\bPybind11Extension\\b",
|
| 84 |
+
"sev": "error",
|
| 85 |
+
"msg": "BLOCKER: compiled extension; Flit builds pure Python packages only (\"If your package needs a build step, you won't be able to use Flit\")",
|
| 86 |
+
"fix": "keep setuptools for this package, or split the pure-Python part into its own Flit package",
|
| 87 |
+
"source": "https://flit.pypa.io/en/stable/rationale.html"
|
| 88 |
+
},
|
| 89 |
+
{
|
| 90 |
+
"id": "SP03",
|
| 91 |
+
"check": "custom_cmdclass",
|
| 92 |
+
"files": [
|
| 93 |
+
"setup.py",
|
| 94 |
+
"setup.cfg"
|
| 95 |
+
],
|
| 96 |
+
"re": "\\bcmdclass\\s*=",
|
| 97 |
+
"sev": "error",
|
| 98 |
+
"msg": "BLOCKER: custom build command (cmdclass); flit_core runs no build hooks",
|
| 99 |
+
"fix": "move the generated file into the source tree before the build, or keep setuptools",
|
| 100 |
+
"source": "https://flit.pypa.io/en/stable/rationale.html"
|
| 101 |
+
},
|
| 102 |
+
{
|
| 103 |
+
"id": "SP04",
|
| 104 |
+
"check": "setup_requires",
|
| 105 |
+
"files": [
|
| 106 |
+
"setup.py",
|
| 107 |
+
"setup.cfg"
|
| 108 |
+
],
|
| 109 |
+
"re": "\\bsetup_requires\\s*=",
|
| 110 |
+
"sev": "warn",
|
| 111 |
+
"msg": "setup_requires has no Flit equivalent",
|
| 112 |
+
"fix": "list build tools in [build-system] requires; runtime needs go in [project] dependencies",
|
| 113 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 114 |
+
},
|
| 115 |
+
{
|
| 116 |
+
"id": "SP05",
|
| 117 |
+
"check": "install_requires",
|
| 118 |
+
"files": [
|
| 119 |
+
"setup.py",
|
| 120 |
+
"setup.cfg"
|
| 121 |
+
],
|
| 122 |
+
"re": "\\binstall_requires\\s*=",
|
| 123 |
+
"sev": "warn",
|
| 124 |
+
"msg": "install_requires",
|
| 125 |
+
"fix": "[project] dependencies = [\"requests \u003e=2.6\", ...] (same specifiers and ; markers)",
|
| 126 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 127 |
+
},
|
| 128 |
+
{
|
| 129 |
+
"id": "SP06",
|
| 130 |
+
"check": "extras_require",
|
| 131 |
+
"files": [
|
| 132 |
+
"setup.py",
|
| 133 |
+
"setup.cfg"
|
| 134 |
+
],
|
| 135 |
+
"re": "\\bextras_require\\s*=|^\\s*\\[options\\.extras_require\\]",
|
| 136 |
+
"sev": "warn",
|
| 137 |
+
"msg": "extras_require",
|
| 138 |
+
"fix": "[project.optional-dependencies] test = [\"pytest \u003e=2.7.3\"]",
|
| 139 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 140 |
+
},
|
| 141 |
+
{
|
| 142 |
+
"id": "SP07",
|
| 143 |
+
"check": "console_scripts",
|
| 144 |
+
"files": [
|
| 145 |
+
"setup.py",
|
| 146 |
+
"setup.cfg"
|
| 147 |
+
],
|
| 148 |
+
"re": "console_scripts",
|
| 149 |
+
"sev": "warn",
|
| 150 |
+
"msg": "console_scripts entry point",
|
| 151 |
+
"fix": "[project.scripts] mycmd = \"mypkg:main\"",
|
| 152 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 153 |
+
},
|
| 154 |
+
{
|
| 155 |
+
"id": "SP08",
|
| 156 |
+
"check": "gui_scripts",
|
| 157 |
+
"files": [
|
| 158 |
+
"setup.py",
|
| 159 |
+
"setup.cfg"
|
| 160 |
+
],
|
| 161 |
+
"re": "gui_scripts",
|
| 162 |
+
"sev": "warn",
|
| 163 |
+
"msg": "gui_scripts entry point",
|
| 164 |
+
"fix": "[project.gui-scripts] mygui = \"mypkg.gui:main\"",
|
| 165 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 166 |
+
},
|
| 167 |
+
{
|
| 168 |
+
"id": "SP09",
|
| 169 |
+
"check": "entry_points",
|
| 170 |
+
"files": [
|
| 171 |
+
"setup.py",
|
| 172 |
+
"setup.cfg"
|
| 173 |
+
],
|
| 174 |
+
"re": "\\bentry_points\\s*=|^\\s*\\[options\\.entry_points\\]",
|
| 175 |
+
"sev": "info",
|
| 176 |
+
"msg": "entry_points block",
|
| 177 |
+
"fix": "other groups become [project.entry-points.\"group.name\"] (quote a dotted group); console_scripts/gui_scripts go to [project.scripts]/[project.gui-scripts]",
|
| 178 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 179 |
+
},
|
| 180 |
+
{
|
| 181 |
+
"id": "SP10",
|
| 182 |
+
"check": "data_files",
|
| 183 |
+
"files": [
|
| 184 |
+
"setup.py",
|
| 185 |
+
"setup.cfg"
|
| 186 |
+
],
|
| 187 |
+
"re": "\\bdata_files\\s*=",
|
| 188 |
+
"sev": "warn",
|
| 189 |
+
"msg": "data_files installs files outside the package",
|
| 190 |
+
"fix": "put them under data/ and add [tool.flit.external-data] directory = \"data\" (flit_core \u003e=3.7)",
|
| 191 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 192 |
+
},
|
| 193 |
+
{
|
| 194 |
+
"id": "SP11",
|
| 195 |
+
"check": "package_data",
|
| 196 |
+
"files": [
|
| 197 |
+
"setup.py",
|
| 198 |
+
"setup.cfg"
|
| 199 |
+
],
|
| 200 |
+
"re": "\\b(package_data|include_package_data|exclude_package_data)\\s*=|^\\s*\\[options\\.package_data\\]",
|
| 201 |
+
"sev": "info",
|
| 202 |
+
"msg": "package_data / include_package_data",
|
| 203 |
+
"fix": "delete it: Flit includes data files inside the package folder automatically",
|
| 204 |
+
"source": "https://flit.pypa.io/en/stable/rationale.html"
|
| 205 |
+
},
|
| 206 |
+
{
|
| 207 |
+
"id": "SP12",
|
| 208 |
+
"check": "package_dir",
|
| 209 |
+
"files": [
|
| 210 |
+
"setup.py",
|
| 211 |
+
"setup.cfg"
|
| 212 |
+
],
|
| 213 |
+
"re": "\\bpackage_dir\\s*=",
|
| 214 |
+
"sev": "info",
|
| 215 |
+
"msg": "package_dir",
|
| 216 |
+
"fix": "delete it: Flit finds the package next to pyproject.toml or in src/; if the import name differs from the PyPI name add [tool.flit.module] name = \"importname\"",
|
| 217 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 218 |
+
},
|
| 219 |
+
{
|
| 220 |
+
"id": "SP13",
|
| 221 |
+
"check": "find_packages",
|
| 222 |
+
"files": [
|
| 223 |
+
"setup.py",
|
| 224 |
+
"setup.cfg"
|
| 225 |
+
],
|
| 226 |
+
"re": "\\bfind_(namespace_)?packages\\s*\\(|^\\s*packages\\s*=",
|
| 227 |
+
"sev": "info",
|
| 228 |
+
"msg": "packages / find_packages()",
|
| 229 |
+
"fix": "delete it: name only the top-level import package; Flit includes its subpackages automatically",
|
| 230 |
+
"source": "https://flit.pypa.io/en/stable/rationale.html"
|
| 231 |
+
},
|
| 232 |
+
{
|
| 233 |
+
"id": "SP14",
|
| 234 |
+
"check": "static_version",
|
| 235 |
+
"files": [
|
| 236 |
+
"setup.py",
|
| 237 |
+
"setup.cfg"
|
| 238 |
+
],
|
| 239 |
+
"re": "^\\s*version\\s*=\\s*(['\\\"]|[\\d])",
|
| 240 |
+
"sev": "info",
|
| 241 |
+
"msg": "version set in setup metadata",
|
| 242 |
+
"fix": "[project] version = \"1.2.3\", or drop it and add dynamic = [\"version\"] so Flit reads {DV} from the package",
|
| 243 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 244 |
+
},
|
| 245 |
+
{
|
| 246 |
+
"id": "SP15",
|
| 247 |
+
"check": "attr_version",
|
| 248 |
+
"files": [
|
| 249 |
+
"setup.py",
|
| 250 |
+
"setup.cfg"
|
| 251 |
+
],
|
| 252 |
+
"re": "version\\s*=\\s*attr\\s*:|version\\s*=\\s*(get_version|read_version)\\s*\\(",
|
| 253 |
+
"sev": "info",
|
| 254 |
+
"msg": "version read from code at build time",
|
| 255 |
+
"fix": "dynamic = [\"version\"]; Flit reads {DV} from the top-level module itself",
|
| 256 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 257 |
+
},
|
| 258 |
+
{
|
| 259 |
+
"id": "SP16",
|
| 260 |
+
"check": "scm_version",
|
| 261 |
+
"files": [
|
| 262 |
+
"setup.py",
|
| 263 |
+
"setup.cfg",
|
| 264 |
+
"pyproject.toml"
|
| 265 |
+
],
|
| 266 |
+
"re": "use_scm_version|setuptools_scm|setuptools-scm|versioneer",
|
| 267 |
+
"sev": "error",
|
| 268 |
+
"msg": "BLOCKER: version from git tags (setuptools_scm/versioneer); Flit dynamic accepts only version, description, import-names, import-namespaces",
|
| 269 |
+
"fix": "write the version into {DV} in the package and add dynamic = [\"version\"]",
|
| 270 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 271 |
+
},
|
| 272 |
+
{
|
| 273 |
+
"id": "SP17",
|
| 274 |
+
"check": "python_requires",
|
| 275 |
+
"files": [
|
| 276 |
+
"setup.py",
|
| 277 |
+
"setup.cfg"
|
| 278 |
+
],
|
| 279 |
+
"re": "\\bpython_requires\\s*=",
|
| 280 |
+
"sev": "warn",
|
| 281 |
+
"msg": "python_requires",
|
| 282 |
+
"fix": "[project] requires-python = \"\u003e=3.8\" (flit_core 4 itself needs Python 3.8+ to build)",
|
| 283 |
+
"source": "https://flit.pypa.io/en/stable/history.html"
|
| 284 |
+
},
|
| 285 |
+
{
|
| 286 |
+
"id": "SP18",
|
| 287 |
+
"check": "long_description",
|
| 288 |
+
"files": [
|
| 289 |
+
"setup.py",
|
| 290 |
+
"setup.cfg"
|
| 291 |
+
],
|
| 292 |
+
"re": "\\blong_description\\s*=|\\blong_description_content_type\\s*=",
|
| 293 |
+
"sev": "warn",
|
| 294 |
+
"msg": "long_description",
|
| 295 |
+
"fix": "[project] readme = \"README.md\" (.md, .rst or .txt picks the content type)",
|
| 296 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 297 |
+
},
|
| 298 |
+
{
|
| 299 |
+
"id": "SP19",
|
| 300 |
+
"check": "legacy_license",
|
| 301 |
+
"files": [
|
| 302 |
+
"setup.py",
|
| 303 |
+
"setup.cfg"
|
| 304 |
+
],
|
| 305 |
+
"re": "^\\s*license(_files?)?\\s*=",
|
| 306 |
+
"sev": "warn",
|
| 307 |
+
"msg": "license set in setup metadata",
|
| 308 |
+
"fix": "[project] license = \"MIT\" (a valid SPDX expression, flit_core \u003e=3.11) and license-files = [\"LICENSE*\"]",
|
| 309 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 310 |
+
},
|
| 311 |
+
{
|
| 312 |
+
"id": "SP20",
|
| 313 |
+
"check": "script_files",
|
| 314 |
+
"files": [
|
| 315 |
+
"setup.py",
|
| 316 |
+
"setup.cfg"
|
| 317 |
+
],
|
| 318 |
+
"re": "^\\s*scripts\\s*=",
|
| 319 |
+
"sev": "warn",
|
| 320 |
+
"msg": "scripts= copies raw script files; Flit has no script-files field",
|
| 321 |
+
"fix": "turn each script into a function and add [project.scripts] name = \"mypkg.cli:main\"",
|
| 322 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 323 |
+
},
|
| 324 |
+
{
|
| 325 |
+
"id": "SP21",
|
| 326 |
+
"check": "author_fields",
|
| 327 |
+
"files": [
|
| 328 |
+
"setup.py",
|
| 329 |
+
"setup.cfg"
|
| 330 |
+
],
|
| 331 |
+
"re": "\\b(author|author_email|maintainer|maintainer_email)\\s*=",
|
| 332 |
+
"sev": "info",
|
| 333 |
+
"msg": "author / author_email",
|
| 334 |
+
"fix": "[project] authors = [{ name = \"Jane Doe\", email = \"jane@example.org\" }]",
|
| 335 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 336 |
+
},
|
| 337 |
+
{
|
| 338 |
+
"id": "SP22",
|
| 339 |
+
"check": "url_fields",
|
| 340 |
+
"files": [
|
| 341 |
+
"setup.py",
|
| 342 |
+
"setup.cfg"
|
| 343 |
+
],
|
| 344 |
+
"re": "\\b(url|project_urls|download_url)\\s*=",
|
| 345 |
+
"sev": "info",
|
| 346 |
+
"msg": "url / project_urls",
|
| 347 |
+
"fix": "[project.urls] Source = \"https://github.com/you/pkg\"",
|
| 348 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 349 |
+
},
|
| 350 |
+
{
|
| 351 |
+
"id": "SP23",
|
| 352 |
+
"check": "classifiers",
|
| 353 |
+
"files": [
|
| 354 |
+
"setup.py",
|
| 355 |
+
"setup.cfg"
|
| 356 |
+
],
|
| 357 |
+
"re": "\\bclassifiers\\s*=",
|
| 358 |
+
"sev": "info",
|
| 359 |
+
"msg": "classifiers",
|
| 360 |
+
"fix": "[project] classifiers = [...] (add \"Private :: Do Not Upload\" to block an accidental PyPI upload)",
|
| 361 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 362 |
+
},
|
| 363 |
+
{
|
| 364 |
+
"id": "SP24",
|
| 365 |
+
"check": "name_description",
|
| 366 |
+
"files": [
|
| 367 |
+
"setup.py",
|
| 368 |
+
"setup.cfg"
|
| 369 |
+
],
|
| 370 |
+
"re": "^\\s*(name|description)\\s*=",
|
| 371 |
+
"sev": "info",
|
| 372 |
+
"msg": "name / description",
|
| 373 |
+
"fix": "[project] name = \"tidyframe\"; description = \"...\" or dynamic = [\"description\"] to take the module docstring",
|
| 374 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 375 |
+
},
|
| 376 |
+
{
|
| 377 |
+
"id": "SC01",
|
| 378 |
+
"check": "setup_cfg_metadata",
|
| 379 |
+
"files": [
|
| 380 |
+
"setup.cfg"
|
| 381 |
+
],
|
| 382 |
+
"re": "^\\s*\\[(metadata|options)\\]",
|
| 383 |
+
"sev": "warn",
|
| 384 |
+
"msg": "setup.cfg [metadata]/[options] section",
|
| 385 |
+
"fix": "move each key into the standard [project] table of pyproject.toml, then delete setup.cfg",
|
| 386 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 387 |
+
},
|
| 388 |
+
{
|
| 389 |
+
"id": "PP01",
|
| 390 |
+
"check": "non_flit_backend",
|
| 391 |
+
"files": [
|
| 392 |
+
"pyproject.toml"
|
| 393 |
+
],
|
| 394 |
+
"re": "^\\s*build-backend\\s*=\\s*\\\"(?!flit_core\\.buildapi)",
|
| 395 |
+
"sev": "warn",
|
| 396 |
+
"msg": "build-backend is not flit_core",
|
| 397 |
+
"fix": "build-backend = \"flit_core.buildapi\"",
|
| 398 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 399 |
+
},
|
| 400 |
+
{
|
| 401 |
+
"id": "PP02",
|
| 402 |
+
"check": "setuptools_requires",
|
| 403 |
+
"files": [
|
| 404 |
+
"pyproject.toml"
|
| 405 |
+
],
|
| 406 |
+
"re": "^\\s*requires\\s*=.*\\b(setuptools|wheel|hatchling|poetry-core|poetry_core|pdm-backend)\\b",
|
| 407 |
+
"sev": "warn",
|
| 408 |
+
"msg": "[build-system] requires pulls another backend",
|
| 409 |
+
"fix": "requires = [\"flit_core \u003e=3.11,\u003c5\"]",
|
| 410 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 411 |
+
},
|
| 412 |
+
{
|
| 413 |
+
"id": "PP03",
|
| 414 |
+
"check": "tool_flit_metadata",
|
| 415 |
+
"files": [
|
| 416 |
+
"pyproject.toml"
|
| 417 |
+
],
|
| 418 |
+
"re": "^\\s*\\[tool\\.flit\\.metadata(\\.[\\w-]+)?\\]",
|
| 419 |
+
"sev": "error",
|
| 420 |
+
"msg": "[tool.flit.metadata] is no longer read by flit_core 4.0 (on PyPI 2026-08-04)",
|
| 421 |
+
"fix": "move the fields into [project], or pin requires = [\"flit_core \u003e=2,\u003c4\"]",
|
| 422 |
+
"source": "https://flit.pypa.io/en/stable/history.html"
|
| 423 |
+
},
|
| 424 |
+
{
|
| 425 |
+
"id": "PP04",
|
| 426 |
+
"check": "tool_flit_scripts",
|
| 427 |
+
"files": [
|
| 428 |
+
"pyproject.toml"
|
| 429 |
+
],
|
| 430 |
+
"re": "^\\s*\\[tool\\.flit\\.(scripts|entrypoints[\\w.\\\"-]*)\\]",
|
| 431 |
+
"sev": "error",
|
| 432 |
+
"msg": "old Flit 3 scripts/entrypoints table (belongs to the [tool.flit.metadata] layout)",
|
| 433 |
+
"fix": "[project.scripts] and [project.entry-points.\"group\"]",
|
| 434 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 435 |
+
},
|
| 436 |
+
{
|
| 437 |
+
"id": "PP05",
|
| 438 |
+
"check": "flit_core_no_upper",
|
| 439 |
+
"files": [
|
| 440 |
+
"pyproject.toml"
|
| 441 |
+
],
|
| 442 |
+
"re": null,
|
| 443 |
+
"sev": "warn",
|
| 444 |
+
"msg": "flit_core requirement has no \u003c5 upper bound",
|
| 445 |
+
"fix": "requires = [\"flit_core \u003e=3.11,\u003c5\"] (\"all packages should specify \u003c5\")",
|
| 446 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 447 |
+
},
|
| 448 |
+
{
|
| 449 |
+
"id": "PP06",
|
| 450 |
+
"check": "flit_core_lt4",
|
| 451 |
+
"files": [
|
| 452 |
+
"pyproject.toml"
|
| 453 |
+
],
|
| 454 |
+
"re": null,
|
| 455 |
+
"sev": "warn",
|
| 456 |
+
"msg": "flit_core \u003c4 pin on a [project]-table package",
|
| 457 |
+
"fix": "change \u003c4 to \u003c5: flit_core 4.0 only drops [tool.flit.metadata]; [project] packages build unchanged",
|
| 458 |
+
"source": "https://flit.pypa.io/en/stable/history.html"
|
| 459 |
+
},
|
| 460 |
+
{
|
| 461 |
+
"id": "PP07",
|
| 462 |
+
"check": "spdx_needs_311",
|
| 463 |
+
"files": [
|
| 464 |
+
"pyproject.toml"
|
| 465 |
+
],
|
| 466 |
+
"re": null,
|
| 467 |
+
"sev": "error",
|
| 468 |
+
"msg": "license = \"SPDX\" string needs flit_core \u003e=3.11 but the requirement allows older",
|
| 469 |
+
"fix": "requires = [\"flit_core \u003e=3.11,\u003c5\"]",
|
| 470 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 471 |
+
},
|
| 472 |
+
{
|
| 473 |
+
"id": "PP08",
|
| 474 |
+
"check": "spdx_with_needs_41",
|
| 475 |
+
"files": [
|
| 476 |
+
"pyproject.toml"
|
| 477 |
+
],
|
| 478 |
+
"re": null,
|
| 479 |
+
"sev": "error",
|
| 480 |
+
"msg": "license expression with WITH needs flit_core \u003e=4.1 (released 2026-09-16)",
|
| 481 |
+
"fix": "requires = [\"flit_core \u003e=4.1,\u003c5\"]",
|
| 482 |
+
"source": "https://flit.pypa.io/en/stable/history.html"
|
| 483 |
+
},
|
| 484 |
+
{
|
| 485 |
+
"id": "PP09",
|
| 486 |
+
"check": "import_names_needs_4",
|
| 487 |
+
"files": [
|
| 488 |
+
"pyproject.toml"
|
| 489 |
+
],
|
| 490 |
+
"re": null,
|
| 491 |
+
"sev": "error",
|
| 492 |
+
"msg": "import-names / import-namespaces need flit_core \u003e=4",
|
| 493 |
+
"fix": "requires = [\"flit_core \u003e=4,\u003c5\"]",
|
| 494 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 495 |
+
},
|
| 496 |
+
{
|
| 497 |
+
"id": "PP10",
|
| 498 |
+
"check": "bad_dynamic",
|
| 499 |
+
"files": [
|
| 500 |
+
"pyproject.toml"
|
| 501 |
+
],
|
| 502 |
+
"re": null,
|
| 503 |
+
"sev": "error",
|
| 504 |
+
"msg": "dynamic lists a field Flit cannot fill (only version, description, import-names, import-namespaces)",
|
| 505 |
+
"fix": "write that field out in [project]",
|
| 506 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 507 |
+
},
|
| 508 |
+
{
|
| 509 |
+
"id": "PP11",
|
| 510 |
+
"check": "console_scripts_group",
|
| 511 |
+
"files": [
|
| 512 |
+
"pyproject.toml"
|
| 513 |
+
],
|
| 514 |
+
"re": "^\\s*\\[project\\.entry-points\\.\\\"?(console_scripts|gui_scripts)\\\"?\\]",
|
| 515 |
+
"sev": "error",
|
| 516 |
+
"msg": "[project.entry-points.console_scripts] is not allowed",
|
| 517 |
+
"fix": "[project.scripts] (or [project.gui-scripts])",
|
| 518 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 519 |
+
},
|
| 520 |
+
{
|
| 521 |
+
"id": "PP12",
|
| 522 |
+
"check": "tool_setuptools",
|
| 523 |
+
"files": [
|
| 524 |
+
"pyproject.toml"
|
| 525 |
+
],
|
| 526 |
+
"re": "^\\s*\\[tool\\.setuptools[\\w.\\\"-]*\\]",
|
| 527 |
+
"sev": "info",
|
| 528 |
+
"msg": "[tool.setuptools] table has no effect under flit_core",
|
| 529 |
+
"fix": "package-data: delete (automatic); packages.find/package-dir: [tool.flit.module] name = \"importname\"; data-files: [tool.flit.external-data]",
|
| 530 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 531 |
+
},
|
| 532 |
+
{
|
| 533 |
+
"id": "PP13",
|
| 534 |
+
"check": "tool_hatch_poetry",
|
| 535 |
+
"files": [
|
| 536 |
+
"pyproject.toml"
|
| 537 |
+
],
|
| 538 |
+
"re": "^\\s*\\[tool\\.(hatch|poetry)[\\w.\\\"-]*\\]",
|
| 539 |
+
"sev": "info",
|
| 540 |
+
"msg": "[tool.hatch]/[tool.poetry] table is ignored by flit_core",
|
| 541 |
+
"fix": "dependencies/scripts go into [project]; build.targets.sdist include/exclude becomes [tool.flit.sdist] include/exclude",
|
| 542 |
+
"source": "https://flit.pypa.io/en/stable/pyproject_toml.html"
|
| 543 |
+
}
|
| 544 |
+
];</script>
|
| 545 |
+
<script>/* Flit Migration Check engine: setup.py / setup.cfg / pyproject.toml -> flit_core 4 map. Runs in Node and in the browser. */
|
| 546 |
+
(function () {
|
| 547 |
+
var RULES = (typeof module !== 'undefined') ? require('./rules.json') : window.FLIT_RULES;
|
| 548 |
+
var DV = '_' + '_version_' + '_';
|
| 549 |
+
var DYN_OK = ['version', 'description', 'import-names', 'import-namespaces'];
|
| 550 |
+
|
| 551 |
+
function detectKind(text, opts) {
|
| 552 |
+
var f = (opts && opts.filename) ? String(opts.filename).toLowerCase() : '';
|
| 553 |
+
if (/pyproject\.toml$/.test(f)) return 'pyproject.toml';
|
| 554 |
+
if (/setup\.cfg$/.test(f)) return 'setup.cfg';
|
| 555 |
+
if (/\.py$/.test(f)) return 'setup.py';
|
| 556 |
+
if (/^\s*\[(build-system|project|tool\.[\w-]+)[\].]/m.test(text)) return 'pyproject.toml';
|
| 557 |
+
if (/^\s*\[(metadata|options[\w.]*)\]/m.test(text)) return 'setup.cfg';
|
| 558 |
+
if (/\bsetup\s*\(|\bimport\s+setuptools|\bfrom\s+(setuptools|distutils)/.test(text)) return 'setup.py';
|
| 559 |
+
return '';
|
| 560 |
+
}
|
| 561 |
+
|
| 562 |
+
function ver(s) {
|
| 563 |
+
var p = String(s).split('.').map(function (x) { return parseInt(x, 10) || 0; });
|
| 564 |
+
return (p[0] || 0) * 10000 + (p[1] || 0) * 100 + (p[2] || 0);
|
| 565 |
+
}
|
| 566 |
+
|
| 567 |
+
function fmt(rule) {
|
| 568 |
+
return (rule.msg + ' -> ' + rule.fix).split('{DV}').join(DV);
|
| 569 |
+
}
|
| 570 |
+
|
| 571 |
+
function check(text, opts) {
|
| 572 |
+
opts = opts || {};
|
| 573 |
+
var kind = detectKind(text, opts);
|
| 574 |
+
var findings = [];
|
| 575 |
+
if (!kind) return { kind: '', findings: findings, blockers: 0, ready: false };
|
| 576 |
+
var lines = String(text).split(/\r?\n/);
|
| 577 |
+
var byId = {};
|
| 578 |
+
RULES.forEach(function (r) { byId[r.id] = r; });
|
| 579 |
+
function add(rule, line) {
|
| 580 |
+
findings.push({ check: rule.id + ' ' + rule.check, sev: rule.sev, msg: fmt(rule), line: line, fix: rule.fix.split('{DV}').join(DV), source: rule.source });
|
| 581 |
+
}
|
| 582 |
+
var section = '';
|
| 583 |
+
var st = { reqLine: 0, req: '', inReq: false, licLine: 0, lic: '', impLine: 0, hasProject: false, hasFlitMeta: false };
|
| 584 |
+
lines.forEach(function (raw, i) {
|
| 585 |
+
var ln = i + 1;
|
| 586 |
+
var t = raw.trim();
|
| 587 |
+
if (!t || t.charAt(0) === '#' || (kind === 'setup.cfg' && t.charAt(0) === ';')) return;
|
| 588 |
+
var h = /^\[([^\]]+)\]/.exec(t);
|
| 589 |
+
if (h && kind !== 'setup.py') section = h[1].trim();
|
| 590 |
+
RULES.forEach(function (r) {
|
| 591 |
+
if (!r.re || r.files.indexOf(kind) < 0) return;
|
| 592 |
+
if (new RegExp(r.re).test(raw)) add(r, ln);
|
| 593 |
+
});
|
| 594 |
+
if (kind !== 'pyproject.toml') return;
|
| 595 |
+
if (section === 'project') st.hasProject = true;
|
| 596 |
+
if (/^tool\.flit\.metadata/.test(section)) st.hasFlitMeta = true;
|
| 597 |
+
if (section === 'build-system' && /^requires\s*=/.test(t)) { st.reqLine = ln; st.inReq = true; st.req = ''; }
|
| 598 |
+
if (st.inReq) { st.req += ' ' + t; if (/\]/.test(t)) st.inReq = false; }
|
| 599 |
+
if (section === 'project') {
|
| 600 |
+
var lic = /^license\s*=\s*"([^"]*)"/.exec(t);
|
| 601 |
+
if (lic) { st.licLine = ln; st.lic = lic[1]; }
|
| 602 |
+
if (/^import-(names|namespaces)\s*=/.test(t) && !st.impLine) st.impLine = ln;
|
| 603 |
+
var dyn = /^dynamic\s*=\s*\[([^\]]*)\]/.exec(t);
|
| 604 |
+
if (dyn) {
|
| 605 |
+
var items = (dyn[1].match(/"([^"]+)"|'([^']+)'/g) || []).map(function (s) { return s.slice(1, -1); });
|
| 606 |
+
if (items.indexOf('import-names') >= 0 || items.indexOf('import-namespaces') >= 0) st.impLine = st.impLine || ln;
|
| 607 |
+
var bad = items.filter(function (x) { return DYN_OK.indexOf(x) < 0; });
|
| 608 |
+
if (bad.length) {
|
| 609 |
+
var r10 = byId.PP10;
|
| 610 |
+
findings.push({ check: r10.id + ' ' + r10.check, sev: r10.sev, msg: fmt(r10) + ' (' + bad.join(', ') + ')', line: ln, fix: r10.fix, source: r10.source });
|
| 611 |
+
}
|
| 612 |
+
}
|
| 613 |
+
}
|
| 614 |
+
});
|
| 615 |
+
if (kind === 'pyproject.toml') {
|
| 616 |
+
var m = /flit[_-]core\s*([^"'\]]*)/i.exec(st.req);
|
| 617 |
+
if (m) {
|
| 618 |
+
var spec = m[1];
|
| 619 |
+
var lo = /(?:>=|~=|==)\s*([\d.]+)/.exec(spec);
|
| 620 |
+
var lower = lo ? ver(lo[1]) : 0;
|
| 621 |
+
if (/<\s*4\b/.test(spec)) { if (st.hasProject && !st.hasFlitMeta) add(byId.PP06, st.reqLine); }
|
| 622 |
+
else if (!/<\s*5\b/.test(spec)) add(byId.PP05, st.reqLine);
|
| 623 |
+
if (st.licLine && lower < ver('3.11')) add(byId.PP07, st.licLine);
|
| 624 |
+
if (st.licLine && /\bWITH\b/.test(st.lic) && lower < ver('4.1')) add(byId.PP08, st.licLine);
|
| 625 |
+
if (st.impLine && lower < ver('4')) add(byId.PP09, st.impLine);
|
| 626 |
+
}
|
| 627 |
+
}
|
| 628 |
+
findings.sort(function (a, b) { return a.line - b.line; });
|
| 629 |
+
var blockers = findings.filter(function (f) { return /BLOCKER/.test(f.msg); }).length;
|
| 630 |
+
return { kind: kind, findings: findings, blockers: blockers, ready: findings.length === 0 };
|
| 631 |
+
}
|
| 632 |
+
|
| 633 |
+
var api = { engine: { check: check, detectKind: detectKind }, RULES: RULES, RULE_COUNT: RULES.length };
|
| 634 |
+
if (typeof module !== 'undefined') module.exports = api;
|
| 635 |
+
if (typeof window !== 'undefined') window.FLITENGINE = api;
|
| 636 |
+
})();
|
| 637 |
+
</script>
|
| 638 |
+
<script>// ⛔뼈대 — kit/scaffold.py 가 찍는다 (s143). findings [{check,sev,msg,line?}] → 글/HTML. 같은 파일이 Node 와 브라우저에서 산다.
|
| 639 |
+
'use strict';
|
| 640 |
+
function escapeHtml(s) { return String(s == null ? '' : s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); }
|
| 641 |
+
function summaryLine(res) {
|
| 642 |
+
const f = (res && res.findings) || []; const e = f.filter(function (x) { return String(x.sev || 'error').toLowerCase().startsWith('err'); }).length;
|
| 643 |
+
return f.length ? (f.length + ' finding' + (f.length === 1 ? '' : 's') + ' (' + e + ' error' + (e === 1 ? '' : 's') + ')') : 'Clean — no findings.';
|
| 644 |
+
}
|
| 645 |
+
function toText(res, label) {
|
| 646 |
+
const f = (res && res.findings) || [];
|
| 647 |
+
const lines = ['== ' + (label || '') + ' — ' + summaryLine(res)];
|
| 648 |
+
f.forEach(function (x) { lines.push(' ' + String(x.sev || 'error').toUpperCase().slice(0, 4) + ' ' + (x.line ? 'L' + x.line + ' ' : '') + (x.check || x.id || '') + ' ' + (x.msg || x.message || '')); });
|
| 649 |
+
return lines.join('\n');
|
| 650 |
+
}
|
| 651 |
+
function toHtml(res) {
|
| 652 |
+
const f = (res && res.findings) || [];
|
| 653 |
+
if (!f.length) return '<p class="ok">' + escapeHtml(summaryLine(res)) + '</p>';
|
| 654 |
+
const rows = f.map(function (x) { return '<tr><td class="sev ' + escapeHtml(String(x.sev || 'error')) + '">' + escapeHtml(String(x.sev || 'error')) + '</td><td>' + (x.line ? 'L' + escapeHtml(x.line) : '') + '</td><td><code>' + escapeHtml(x.check || x.id || '') + '</code></td><td>' + escapeHtml(x.msg || x.message || '') + '</td></tr>'; }).join('');
|
| 655 |
+
return '<p class="sum">' + escapeHtml(summaryLine(res)) + '</p><table><thead><tr><th>Level</th><th>Line</th><th>Check</th><th>What</th></tr></thead><tbody>' + rows + '</tbody></table>';
|
| 656 |
+
}
|
| 657 |
+
var FLITREPORT = { escapeHtml: escapeHtml, summaryLine: summaryLine, toText: toText, toHtml: toHtml };
|
| 658 |
+
if (typeof module !== 'undefined' && module.exports) module.exports = FLITREPORT;
|
| 659 |
+
else window.FLITREPORT = FLITREPORT;
|
| 660 |
+
</script>
|
| 661 |
+
<script>
|
| 662 |
+
(function(){
|
| 663 |
+
var src=document.getElementById('src'), out=document.getElementById('out'), go=document.getElementById('go'), today=document.getElementById('today');
|
| 664 |
+
today.value=new Date().toISOString().slice(0,10);
|
| 665 |
+
function run(){ try { var res=window.FLITENGINE.engine.check(src.value,{today:today.value}); out.innerHTML=window.FLITREPORT.toHtml(res); } catch(e){ out.innerHTML='<p class="sev error">Could not run: '+String(e.message||e)+'</p>'; } }
|
| 666 |
+
go.onclick=run; if (src.value.trim()) run();
|
| 667 |
+
})();
|
| 668 |
+
</script>
|
| 669 |
+
|
| 670 |
+
|
| 671 |
+
<section data-yj-install="free" style="margin:2rem auto 1rem;max-width:46rem;padding:1.25rem 1.25rem;border:1px solid #cfe3d6;border-radius:12px;background:#f6fbf8;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .4rem;font-size:1.2rem">Install free</h2><p style="margin:0 0 .8rem;color:#41505f">Free for the file open in your editor - no key, no limit. The workspace sweep and the report ask for a key.</p><a href="https://open-vsx.org/extension/readystack/flit-migration-check" rel="noopener" data-yj-install-door="ovsx" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'flit-migration-check',src:'ovsx',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Open VSX (Cursor, VSCodium)</a><p style="margin:.6rem 0 0;font-size:.92rem;color:#41505f"><a href="https://getreadystack.com/api/buy/team" rel="nofollow" style="color:#0f7b3f;text-decoration:underline">Team? One key for every ReadyStack linter, 5 seats, $149 once</a></p></section>
|
| 672 |
+
<section data-yj-alert="rule" style="margin:1rem auto 1.5rem;max-width:46rem;padding:1.1rem 1.25rem;border:1px solid #d9dee6;border-radius:12px;background:#fbfcfd;font:15px/1.55 system-ui,-apple-system,Segoe UI,sans-serif"><b style="font-size:1.05rem">Get one email when this rule changes</b><div style="opacity:.78;margin:.35rem 0 .8rem">We watch the regulation and vendor sources behind Flit Migration Check: setup.py to flit_core 4 every day. When a rule changes, you get a single email with what changed and the updated check. No newsletter.</div><div style="display:flex;gap:8px;flex-wrap:wrap"><input id="yjae" type="email" placeholder="you@example.com" style="flex:1;min-width:210px;padding:11px 13px;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:#fff;color:inherit"><button id="yjab" style="padding:11px 18px;border-radius:8px;border:0;cursor:pointer;background:#0f7b3f;color:#fff;font:600 15px system-ui,sans-serif">Notify me</button></div><div id="yjao" hidden style="margin-top:.6rem;color:#0f7b3f">You're on the list. One email when it changes.</div><script>(function(){var b=document.getElementById("yjab"),e=document.getElementById("yjae");if(!b||!e)return;b.addEventListener("click",function(){var v=e.value.trim();if(!/.+@.+\..+/.test(v))return;try{localStorage.setItem("yj_em","1");}catch(_){}fetch(location.origin+"/api/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:v,slug:"flit-migration-check",lang:"en",source:"rule_alert"+((b.closest&&b.closest("section")&&b.closest("section").getAttribute("data-yj-moved"))?"_at_result":"")})}).then(function(r){var o=document.getElementById("yjao");o.hidden=false;if(!r.ok){o.textContent="Could not save - please try again.";}}).catch(function(){var o=document.getElementById("yjao");o.hidden=false;o.textContent="Could not save - please try again.";});});})();</script><script>(function(){var e=document.getElementById("yjae"),s=e&&e.closest?e.closest("section"):null,o=document.getElementById("out");if(!s||!o)return;var d=false;function mv(ev){if(d)return;if(ev&&ev.target&&s.contains(ev.target))return;d=true;var b=o;while(b.parentNode&&b.parentNode!==document.body&&b.parentNode.tagName!=="MAIN"&&b.parentNode.tagName!=="ARTICLE")b=b.parentNode;if(!b.parentNode||b===s||b.contains(s))return;try{b.parentNode.insertBefore(s,b.nextSibling);s.setAttribute("data-yj-moved","1");}catch(_){}}document.addEventListener("input",mv,true);document.addEventListener("change",mv,true);document.addEventListener("click",function(ev){var b=ev.target&&ev.target.closest?ev.target.closest("button,input[type=submit]"):null;if(b&&!b.closest("[data-yj],[data-yj-cta],[data-yj-install],[data-yj-alert],.yj-find,.yj-related,#yj-ask,.yjp"))mv(ev);},true);})();</script></section>
|
| 673 |
+
|
| 674 |
+
<section data-yj-cta="paid" style="margin:3rem auto;max-width:46rem;padding:1.5rem 1.25rem;border:1px solid #d8dee9;border-radius:12px;background:#fbfcfe;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .5rem;font-size:1.25rem">Get the full version <span class="yj-price">$29</span></h2><p class="yj-cta-paid" style="margin:.25rem 0 .6rem;color:#1f2a37">Workspace key ($29 once): every setup.py, setup.cfg and pyproject.toml in the workspace as one dated migration plan, each item mapped to its flit_core 4 replacement. Team key ($149): licence and end-of-life exposure report across projects plus a CI gate that fails a build adding new setuptools-only </p><p class="yj-cta-pay" style="margin:.25rem 0 1rem;color:#5b6b7c;font-size:.94rem">One payment, one licence key for this tool. The key is shown right after payment.</p><a href="https://getreadystack.com/api/buy/cl/polar_cl_VI4ER6LWKbRiLZHzzqUpAK77hPReaZmifZSkj19xgaN" rel="nofollow" style="display:inline-block;padding:.7rem 1.3rem;border-radius:8px;background:#1b64f2;color:#fff;text-decoration:none;font-weight:600">Buy the full version — $29</a></section>
|
| 675 |
+
<!--yj-share-s101--><div style="margin:28px 0 8px;text-align:center"><button id="yjcp" type="button" style="font:inherit;padding:10px 18px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Copy link</button><button id="yjimg" type="button" style="font:inherit;padding:10px 18px;margin-left:8px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Save result as image</button></div><script>(function(){var b=document.getElementById("yjimg");if(!b)return;b.addEventListener("click",function(){var o=document.getElementById("out");var txt=(o&&(o.innerText||o.textContent)||"").trim();if(!txt){b.textContent="(no result yet)";setTimeout(function(){b.textContent="Save result as image";},1500);return;}var lines=txt.split(String.fromCharCode(10)).filter(function(x){return x.trim();}).slice(0,10);var W=1200,P=56,c=document.createElement("canvas");c.width=W;var lh=38,H=P*2+70+lines.length*lh+60;c.height=H;var x=c.getContext("2d");x.fillStyle="#1a1512";x.fillRect(0,0,W,H);x.fillStyle="#f3e9d2";x.font="600 34px system-ui,Segoe UI,Arial";var t=(document.title||"").split("|")[0].trim();x.fillText(t.slice(0,54),P,P+34);x.font="22px ui-monospace,Menlo,Consolas,monospace";x.fillStyle="#e8dcc4";for(var i=0;i<lines.length;i++){x.fillText(lines[i].slice(0,88),P,P+70+i*lh+22);}x.font="600 22px system-ui,Segoe UI,Arial";x.fillStyle="#c9a227";var d=location.hostname.replace("www.","");x.fillText(d,W-P-x.measureText(d).width,H-P+6);c.toBlob(function(bl){var u=URL.createObjectURL(bl),a=document.createElement("a");a.href=u;a.download=(t||"result").replace(/[^a-z0-9]+/gi,"-").toLowerCase()+"-"+d+".png";document.body.appendChild(a);a.click();setTimeout(function(){URL.revokeObjectURL(u);a.remove();},2000);},"image/png");});})();</script><script>(function(){var b=document.getElementById("yjcp");if(!b)return;var t=b.textContent;b.addEventListener("click",function(){var u=location.href.split("#")[0];function d(){b.textContent="Link copied";setTimeout(function(){b.textContent=t;},1800);}if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(u).then(d,function(){prompt("",u);});}else{prompt("",u);}});})();</script>
|
| 676 |
+
<script data-yj="yj-view-beacon">(function(){try{var p=location.pathname;var k='yjv:'+p;if(sessionStorage.getItem(k))return;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",ref:(document.referrer||''),src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}catch(e){}})();</script>
|
| 677 |
+
|
| 678 |
+
<script data-yj="yj-use-beacon">(function(){try{var o=document.getElementById('out');if(!o)return;var p=location.pathname,k='yju:'+p,sent=false;function fire(){if(sent||sessionStorage.getItem(k))return;if(!(o.textContent||'').replace(/\s+/g,'').length)return;sent=true;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",t:'use',src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}var mo=new MutationObserver(function(){fire();if(sent)mo.disconnect();});mo.observe(o,{childList:true,characterData:true,subtree:true});}catch(e){}})();</script><div class="yj-home-find"><div class="yj-lgbar"><a href="/" class="yj-lg on">EN</a><a href="/de/" class="yj-lg">DE</a><a href="/ja/" class="yj-lg">JA</a><a href="/es/" class="yj-lg">ES</a><a href="/pt/" class="yj-lg">PT</a></div><section class="yj-find"><h2>Find a tool</h2><input id="yjq" type="search" autocomplete="off" placeholder="Type a product name, format or task"><div id="yjr"></div></section><style>.yj-lgbar{position:absolute;top:14px;right:18px;display:flex;gap:6px;z-index:60}.yj-lg{font:600 12px/1 system-ui,sans-serif;padding:6px 9px;border-radius:4px;text-decoration:none;color:#8b96a3;border:1px solid rgba(140,150,165,.28)}.yj-lg:hover{color:#e8eaed;border-color:rgba(160,175,195,.6)}.yj-lg.on{color:#0e1116;background:#cfd6de;border-color:#cfd6de}.yj-find{max-width:760px;margin:34px auto 8px;padding:0 20px}.yj-find h2{font:600 20px/1.3 system-ui,sans-serif;margin:0 0 10px}#yjq{width:100%;box-sizing:border-box;padding:13px 15px;font:400 15.5px system-ui,sans-serif;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:rgba(255,255,255,.04);color:inherit}#yjq:focus{outline:none;border-color:#7FD1C4}#yjr{margin-top:10px;display:flex;flex-direction:column;gap:6px}#yjr a{display:flex;justify-content:space-between;gap:12px;padding:11px 14px;border:1px solid rgba(140,150,165,.22);border-radius:6px;text-decoration:none;font:400 14.5px/1.4 system-ui,sans-serif;color:inherit}#yjr a:hover{border-color:#7FD1C4}#yjr .pr{font:600 13px system-ui,sans-serif;color:#5fbf9a;white-space:nowrap}</style><script>(function(){var q=document.getElementById("yjq"),r=document.getElementById("yjr"),D=null;function load(){if(D)return Promise.resolve(D);return fetch("/search-index.json").then(function(x){return x.json();}).then(function(j){D=j;return j;}).catch(function(){D=[];return D;});}function esc(t){return String(t).replace(/[&<>"]/g,function(c){return {"&":"&","<":"<",">":">",'"':"""}[c];});}function run(){var v=q.value.trim().toLowerCase();if(v.length<2){r.innerHTML="";return;}load().then(function(j){var w=v.split(/\s+/),o=[];for(var i=0;i<j.length&&o.length<12;i++){var t=j[i].t.toLowerCase();var ok=true;for(var k=0;k<w.length;k++){if(t.indexOf(w[k])<0){ok=false;break;}}if(ok)o.push(j[i]);}r.innerHTML=o.length?o.map(function(x){return "<a href=\""+esc(x.u)+"\">"+"<span>"+esc(x.t)+"</span>"+(x.p?"<span class=\"pr\">$"+x.p+"</span>":"")+"</a>";}).join(""):"";});}q.addEventListener("input",run);q.addEventListener("focus",load);})();</script></div><section data-yj="yj-facts-v1" style="max-width:920px;margin:48px auto 0;padding:0 20px;font-family:system-ui,sans-serif"><style>.yj-article p{font:400 15.5px/1.7 system-ui,sans-serif;color:#26313b;margin:0 0 12px;max-width:72ch}.yj-article h2,.yj-article h3,.yj-article h4{font:600 17px/1.35 system-ui,sans-serif;color:#1b1f24;margin:22px 0 8px}.yj-article table{border-collapse:collapse;margin:10px 0 16px;font:14px/1.45 system-ui,sans-serif}.yj-article th,.yj-article td{border:1px solid #e2e6ea;padding:6px 10px;text-align:left}.yj-article th{background:#f4f6f8}.yj-article pre{background:#0f1720;color:#e6edf3;padding:12px 14px;border-radius:8px;overflow:auto;font:13px/1.5 ui-monospace,Menlo,monospace}.yj-article code{font:13px ui-monospace,Menlo,monospace;background:#f1f3f5;padding:1px 4px;border-radius:3px}.yj-article pre code{background:none;padding:0}.yj-article ul,.yj-article ol{font:15px/1.6 system-ui,sans-serif;color:#26313b}</style><p style="font:400 13px/1.5 system-ui,sans-serif;color:#6b7680;margin:0 0 14px"><time datetime="2026-09-30">Updated 2026-09-30</time> · ReadyStack</p><script type="application/ld+json">{"@context":"https://schema.org","@type":"WebPage","dateModified":"2026-09-30","author":{"@type":"Organization","name":"ReadyStack"}}</script><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Worked example</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 16px;color:#5b6570">Real numbers from this tool, line by line.</p><div class="yj-article"><p><img src="https://getreadystack.com/img/promo/sku428290_result_card.jpg" alt="Flit Migration Check: setup.py to flit_core 4" loading="lazy" style="max-width:100%;border-radius:6px"></p>
|
| 679 |
+
<p>Six setup.py lines from a sample Python package show what a Flit migration really is: install_requires becomes [project] dependencies, extras_require becomes [project.optional-dependencies], a console_scripts entry becomes [project.scripts], data_files becomes [tool.flit.external-data], python_requires becomes requires-python, and license="MIT" becomes an SPDX license string that needs flit_core >=3.11. If you maintain a pure-Python library on PyPI and you are moving from setuptools, Hatch or the old Flit 3 layout to flit_core 4, this is the list you would otherwise write by hand.</p>
|
| 680 |
+
<h3>Migrate from setuptools to Flit: what changed in August and September 2026</h3>
|
| 681 |
+
<p>flit_core 4.0 (on PyPI 2026-08-04) no longer reads [tool.flit.metadata]. The Flit release history says it plainly: the old table "is no longer used for project metadata"; use the [project] table, or build with flit_core <4. A package whose [build-system] asks for "flit_core >=3.2" with no upper bound now receives flit_core 4 when pip builds it from the sdist, and the build stops.</p>
|
| 682 |
+
<p>flit_core 4.1 (on PyPI 2026-09-16) accepts license expressions with WITH, such as "Apache-2.0 WITH LLVM-exception". That line needs "flit_core >=4.1,<5" in requires. A plain SPDX expression like "MIT" needs flit_core >=3.11, and import-names needs flit_core >=4. The Flit docs also ask every package to pin "<5".</p>
|
| 683 |
+
<h3>What the checker does</h3>
|
| 684 |
+
<p>Flit Migration Check is a VS Code extension with a free web version that runs the same engine. Open setup.py, setup.cfg or pyproject.toml and it prints one finding per line: the line number, what the line is, and the flit_core 4 line that replaces it. It has 38 rules, and each one names its source page on flit.pypa.io.</p>
|
| 685 |
+
<p>The bundled sample setup.py gives 22 findings on 22 lines (10 warnings, 12 notes, 0 blockers). A finished flit_core 4 pyproject.toml gives 0 findings, which is the point where you delete setup.py.</p>
|
| 686 |
+
<p>Three pyproject.toml cases show how the answer changes with the input:</p>
|
| 687 |
+
<ul><li>An old Flit 3 file with [tool.flit.metadata], [tool.flit.scripts] and "flit_core >=3.2": 3 findings. The metadata table, the scripts table, and the missing <5 bound.</li><li>A Hatch file with hatchling, dynamic = ["version", "dependencies"] and [project.entry-points.console_scripts]: 5 findings. Flit fills only version, description, import-names and import-namespaces from dynamic, and console_scripts belongs in [project.scripts].</li><li>A Flit 3.x file pinned "flit_core >=3.2,<4" with license = "Apache-2.0 WITH LLVM-exception": 3 findings. The <4 pin can become <5, and the WITH license needs flit_core >=4.1.</li></ul>
|
| 688 |
+
<h3>Blockers: when not to use Flit</h3>
|
| 689 |
+
<p>Flit builds pure Python only: "If your package needs a build step, you won't be able to use Flit". The checker marks ext_modules, Extension(, cythonize( and custom cmdclass as BLOCKER lines. Version from git tags (setuptools_scm, versioneer) is also a blocker: write the version into the package and add dynamic = ["version"].</p>
|
| 690 |
+
<h3>Before twine upload</h3>
|
| 691 |
+
<p>PyPI rejects an upload file over 100.0 MiB by default, and it refuses invalid Trove classifiers and a name too close to an existing project. The classifiers rule reminds you that "Private :: Do Not Upload" blocks an accidental upload.</p>
|
| 692 |
+
<h3>Free and paid</h3>
|
| 693 |
+
<p>The single-file map is free with no limit. A workspace key turns every setup.py, setup.cfg and pyproject.toml in the workspace into one dated migration plan. A team key adds an exposure report across projects and a CI gate that fails a build adding new setuptools-only use.</p></div><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:30px 0 10px;color:#1b1f24">15 seconds — what it actually does</h2><video controls muted playsinline preload="none" poster="/img/promo/sku428290_result_card.jpg" style="width:100%;max-width:360px;border-radius:10px;display:block;margin:0 auto"><source src="/media/sku428290_short.mp4" type="video/mp4"></video><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:34px 0 6px;color:#1b1f24">Questions people ask</h2><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What does Flit Migration Check do?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">It reads the open setup.py, setup.cfg or pyproject.toml in VS Code and lists every line that still belongs to setuptools, Hatch, Poetry or the old Flit 3 layout, with its line number and the exact flit_core 4 line that replaces it. Compiled extensions and custom cmdclass are marked BLOCKER because Flit builds pure Python only. It has 38 rules.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Who is Flit Migration Check for?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Python library maintainers moving a pure-Python package from setup.py or setuptools to Flit, teams whose Flit 3 packages still use [tool.flit.metadata], and anyone switching from Hatch or Poetry to flit_core. flit_core 4.0 (on PyPI 2026-08-04) no longer reads [tool.flit.metadata], so an unpinned old-layout package no longer builds from its sdist.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Why not just ask a chatbot or read the Flit docs?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">The docs are free and correct, but you must map each setup() argument yourself. A chatbot's packaging advice from before August 2026 cannot know that flit_core 4.0 (on PyPI 2026-08-04) no longer reads [tool.flit.metadata], or that flit_core 4.1 (on PyPI 2026-09-16) accepts license expressions with WITH. The checker reads your actual file line by line, offline, and quotes the flit docs for each replacement.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What is free and what needs a key?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">The single-file map is free with no limit: every finding, line number and replacement line; the bundled sample setup.py gives 22 findings on 22 lines (10 warnings, 12 notes, 0 blockers). A $29 workspace key adds one dated migration plan across every setup.py, setup.cfg and pyproject.toml. A $149 team key adds the cross-project exposure report and a CI gate that fails a build adding new setuptools-only use.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What does it cost compared with doing it by hand?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">By hand you read three flit.pypa.io pages and translate each setup() argument, then learn about a wrong flit_core bound when the sdist build fails. The free map does the translation per file. The workspace key is $29 once, one licence key per person or team seat, and it is not a subscription.</p></details><script type="application/ld+json" data-yj="yj-faq-ld">{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "What does Flit Migration Check do?", "acceptedAnswer": {"@type": "Answer", "text": "It reads the open setup.py, setup.cfg or pyproject.toml in VS Code and lists every line that still belongs to setuptools, Hatch, Poetry or the old Flit 3 layout, with its line number and the exact flit_core 4 line that replaces it. Compiled extensions and custom cmdclass are marked BLOCKER because Flit builds pure Python only. It has 38 rules."}}, {"@type": "Question", "name": "Who is Flit Migration Check for?", "acceptedAnswer": {"@type": "Answer", "text": "Python library maintainers moving a pure-Python package from setup.py or setuptools to Flit, teams whose Flit 3 packages still use [tool.flit.metadata], and anyone switching from Hatch or Poetry to flit_core. flit_core 4.0 (on PyPI 2026-08-04) no longer reads [tool.flit.metadata], so an unpinned old-layout package no longer builds from its sdist."}}, {"@type": "Question", "name": "Why not just ask a chatbot or read the Flit docs?", "acceptedAnswer": {"@type": "Answer", "text": "The docs are free and correct, but you must map each setup() argument yourself. A chatbot's packaging advice from before August 2026 cannot know that flit_core 4.0 (on PyPI 2026-08-04) no longer reads [tool.flit.metadata], or that flit_core 4.1 (on PyPI 2026-09-16) accepts license expressions with WITH. The checker reads your actual file line by line, offline, and quotes the flit docs for each replacement."}}, {"@type": "Question", "name": "What is free and what needs a key?", "acceptedAnswer": {"@type": "Answer", "text": "The single-file map is free with no limit: every finding, line number and replacement line; the bundled sample setup.py gives 22 findings on 22 lines (10 warnings, 12 notes, 0 blockers). A $29 workspace key adds one dated migration plan across every setup.py, setup.cfg and pyproject.toml. A $149 team key adds the cross-project exposure report and a CI gate that fails a build adding new setuptools-only use."}}, {"@type": "Question", "name": "What does it cost compared with doing it by hand?", "acceptedAnswer": {"@type": "Answer", "text": "By hand you read three flit.pypa.io pages and translate each setup() argument, then learn about a wrong flit_core bound when the sdist build fails. The free map does the translation per file. The workspace key is $29 once, one licence key per person or team seat, and it is not a subscription."}}]}</script><div id="yj-ask" style="margin:36px 0 0;padding:18px 18px 16px;border:1px solid #e2e6ea;border-radius:10px;background:#fbfbfc"><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:0 0 4px;color:#1b1f24">Ask about this tool</h2><p style="font:400 13.5px/1.5 system-ui,sans-serif;margin:0 0 12px;color:#5b6570">One question, answered by the person who built it. Your email only if you want the answer sent.</p><form onsubmit="return yjAsk(event)" style="display:grid;gap:8px"><textarea name="question" required minlength="10" maxlength="2000" rows="3" placeholder="Your question" style="font:14px/1.5 system-ui,sans-serif;padding:10px;border:1px solid #cfd6dd;border-radius:6px"></textarea><div style="display:flex;gap:8px;flex-wrap:wrap"><input type="email" name="email" placeholder="Email (optional)" style="flex:1;min-width:200px;font:14px system-ui,sans-serif;padding:9px 10px;border:1px solid #cfd6dd;border-radius:6px"><button type="submit" style="font:600 14px system-ui,sans-serif;padding:9px 18px;border:0;border-radius:6px;background:#1b1f24;color:#fff;cursor:pointer">Send</button></div><p id="yj-ask-msg" style="font:13px system-ui,sans-serif;margin:2px 0 0;color:#0a6238;min-height:1em"></p></form><script>function yjAsk(e){e.preventDefault();var f=e.target,m=document.getElementById("yj-ask-msg");m.style.color="#0a6238";fetch(location.origin+"/api/ask",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sku:428290,slug:"flit-migration-check",lang:"en",question:f.question.value,email:f.email.value,page:location.pathname})}).then(function(r){if(!r.ok)throw 0;m.textContent="Thanks \u2014 noted. Answers land on this page and, if you left an email, in your inbox.";f.question.value="";}).catch(function(){m.style.color="#a8551f";m.textContent="Could not send. Please try again in a minute.";});return false;}</script></div></section><section class="yj-related" style="max-width:920px;margin:56px auto 40px;padding:0 20px"><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Same job, next tool</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 20px;color:#5b6570">Free in the browser. The full version is a one-time licence.</p><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px"><a class="yj-rel-c" href="https://getreadystack.com/tools/spdx-license-field-lint"><span class="yj-rel-t">SPDX License Field Lint for package.json, pyproject.toml and Cargo.toml</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/personal-data-map-dsar-audit"><span class="yj-rel-t">Personal Data Map: find every column a GDPR subject access request has to reac</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/pyproject-release-gate"><span class="yj-rel-t">pyproject.toml Release Gate (PEP 639)</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/revit-door-families"><span class="yj-rel-t">Revit Door Families - UK Door Block Library in mm: 14 doors on standard 838 x </span><span class="yj-rel-p">$100</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/almalinux-migration-lint"><span class="yj-rel-t">AlmaLinux Migration Lint — CentOS 7/8</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/autohotkey-toml-devicetree-snippets"><span class="yj-rel-t">AutoHotkey v2, TOML and DeviceTree checks for VS Code</span><span class="yj-rel-p">$29</span></a></div><style>.yj-rel-c{display:flex;flex-direction:column;gap:8px;padding:15px 16px;border:1px solid #e2e6ea;border-radius:6px;text-decoration:none;background:#fff}.yj-rel-c:hover{border-color:#b9c2cb;background:#fafbfc}.yj-rel-t{font:500 14.5px/1.42 system-ui,sans-serif;color:#1b1f24}.yj-rel-p{font:600 13px system-ui,sans-serif;color:#0a6238}</style></section></body>
|
| 694 |
</html>
|