diff --git a/data/templates/python/__init__.py.tpl b/data/templates/python/__init__.py.tpl new file mode 100644 index 0000000..71e88e1 --- /dev/null +++ b/data/templates/python/__init__.py.tpl @@ -0,0 +1,5 @@ +"""Placeholder for {name}, generated by `pkh new`.""" + + +def main() -> None: + print("Hello from {command}!") diff --git a/data/templates/python/manifest.yml b/data/templates/python/manifest.yml index d1e1845..a250f57 100644 --- a/data/templates/python/manifest.yml +++ b/data/templates/python/manifest.yml @@ -1,9 +1,13 @@ -## The `python` template: a PEP 517 project built with pybuild. The logic -## half — the pyproject.toml/setup.py probe, the skeleton bodies and the -## Build-Depends/architecture resolution for existing projects (backend -## package, pyproject presence, C-extension hints) — lives in -## src/new/templates/python.rs; the lists below are the fresh-skeleton -## baseline it starts from. +## The `python` template: a PEP 517 project built with pybuild. The +## skeleton bodies below are static data on the fresh-skeleton baseline +## (the setuptools backend): the module directory and the console-script +## entry point are named by the `{module_name}` placeholder python.rs +## derives from the package name — dpkg names may carry `+`/`.` and may +## start with a digit, none of which a Python module name may. The logic +## half — the pyproject.toml/setup.py probe and the Build-Depends / +## architecture resolution for existing projects (backend package, +## pyproject presence, C-extension hints) — lives in +## src/new/templates/python.rs. ## ## Schema: see src/new/templates/mod.rs. @@ -18,4 +22,8 @@ build_depends: - python3-setuptools architecture: all rules_dh_line: "dh $@ --with python3 --buildsystem=pybuild" -files: [] +files: + - path: pyproject.toml + template: pyproject.toml.tpl + - path: "{module_name}/__init__.py" + template: __init__.py.tpl diff --git a/data/templates/python/pyproject.toml.tpl b/data/templates/python/pyproject.toml.tpl new file mode 100644 index 0000000..91fdf74 --- /dev/null +++ b/data/templates/python/pyproject.toml.tpl @@ -0,0 +1,12 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" + +[project] +name = "{name}" +version = "{upstream_version}" +description = "{summary}" +requires-python = ">=3.8" + +[project.scripts] +{command} = "{module_name}:main" diff --git a/src/new/templates/mod.rs b/src/new/templates/mod.rs index c53fefc..9e2bdde 100644 --- a/src/new/templates/mod.rs +++ b/src/new/templates/mod.rs @@ -356,7 +356,16 @@ static TEMPLATE_SOURCES: &[TemplateSources] = &[ TemplateSources { id: TemplateId::PYTHON, manifest: include_str!("../../../data/templates/python/manifest.yml"), - tpls: &[], + tpls: &[ + ( + "pyproject.toml.tpl", + include_str!("../../../data/templates/python/pyproject.toml.tpl"), + ), + ( + "__init__.py.tpl", + include_str!("../../../data/templates/python/__init__.py.tpl"), + ), + ], hooks: Some(&python::HOOKS), }, TemplateSources { diff --git a/src/new/templates/python.rs b/src/new/templates/python.rs index 93ec132..4d560bf 100644 --- a/src/new/templates/python.rs +++ b/src/new/templates/python.rs @@ -4,18 +4,18 @@ //! …`) with a deliberately minimal line-oriented reader (see //! [`read_pyproject`]) — pkh has no TOML dependency, and only a handful of //! keys matter here. A bare `setup.py`/`setup.cfg` project falls back to -//! setuptools without the `pybuild-plugin-pyproject` helper. The manifest's -//! Build-Depends/architecture are the fresh-skeleton baseline the hooks -//! here resolve for an existing project (backend package, pyproject -//! presence, C-extension hints); the skeleton bodies stay in Rust until -//! they move into the manifest's `files:` list (the module name of -//! `[project.scripts]` is derived, not substituted). +//! setuptools without the `pybuild-plugin-pyproject` helper. The skeleton +//! bodies are manifest data (`data/templates/python/manifest.yml`), +//! named by the `{module_name}` placeholder this module derives from the +//! package name; the manifest's Build-Depends/architecture are the +//! fresh-skeleton baseline the hooks here resolve for an existing project +//! (backend package, pyproject presence, C-extension hints). use std::path::Path; use regex::Regex; -use super::{OutputFile, ProbeResult, TemplateHooks, source_dir_of}; +use super::{ProbeResult, TemplateHooks, source_dir_of}; use crate::new::options::NewOptions; /// The logic half of the python template. @@ -122,46 +122,12 @@ fn c_extension_hints(opts: &NewOptions) -> bool { } impl TemplateHooks for Hooks { - /// A minimal setuptools-based `pyproject.toml` with one console script, - /// plus the one-module package providing it. - fn skeleton_files(&self, opts: &NewOptions) -> Vec { - let module = module_name(opts); - vec![ - OutputFile::new( - "pyproject.toml", - format!( - "[build-system]\n\ - requires = [\"setuptools\"]\n\ - build-backend = \"setuptools.build_meta\"\n\ - \n\ - [project]\n\ - name = \"{name}\"\n\ - version = \"{version}\"\n\ - description = \"{summary}\"\n\ - requires-python = \">=3.8\"\n\ - \n\ - [project.scripts]\n\ - {command} = \"{module}:main\"\n", - name = opts.name, - version = opts.upstream_version, - summary = opts.summary, - command = opts.command, - module = module, - ), - ), - OutputFile::new( - format!("{module}/__init__.py"), - format!( - "\"\"\"Placeholder for {name}, generated by `pkh new`.\"\"\"\n\ - \n\ - \n\ - def main() -> None:\n\ - \x20 print(\"Hello from {command}!\")\n", - name = opts.name, - command = opts.command, - ), - ), - ] + /// The `{module_name}` value of the manifest's skeleton bodies (see + /// [`module_name`]): the derived Python identifier naming the skeleton + /// package directory and the console-script entry point of + /// `pyproject.toml`. + fn context(&self, opts: &NewOptions) -> Vec<(String, String)> { + vec![("module_name".to_string(), module_name(opts))] } /// Amend the manifest's Build-Depends (the fresh-skeleton baseline) @@ -385,23 +351,41 @@ mod tests { "dh $@ --with python3 --buildsystem=pybuild" ); + // Skeleton (manifest data): pyproject.toml + the module package, + // byte-exact. let skeleton = template.skeleton(&o); + assert_eq!(skeleton.len(), 2); let pyproject = skeleton .iter() .find(|f| f.path == "pyproject.toml") .expect("pyproject skeleton"); - assert!( - pyproject - .contents - .contains("build-backend = \"setuptools.build_meta\"") + assert_eq!( + pyproject.contents, + "[build-system]\n\ + requires = [\"setuptools\"]\n\ + build-backend = \"setuptools.build_meta\"\n\ + \n\ + [project]\n\ + name = \"mytool\"\n\ + version = \"0.1.0\"\n\ + description = \"A tool\"\n\ + requires-python = \">=3.8\"\n\ + \n\ + [project.scripts]\n\ + mytool = \"mytool:main\"\n" ); - assert!(pyproject.contents.contains("name = \"mytool\"")); - assert!(pyproject.contents.contains("mytool = \"mytool:main\"")); let module = skeleton .iter() .find(|f| f.path == "mytool/__init__.py") .expect("module skeleton"); - assert!(module.contents.contains("def main()")); + assert_eq!( + module.contents, + "\"\"\"Placeholder for mytool, generated by `pkh new`.\"\"\"\n\ + \n\ + \n\ + def main() -> None:\n\ + \x20 print(\"Hello from mytool!\")\n" + ); } /// dpkg names may carry `+`/`.` and start with a digit — none of which a