| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
pn init my_app now creates ./my_app/ and scaffolds into it, matching
cargo new, create-react-app, and django-admin startproject, and matching
what docs/examples.md already describes. pn init with no name is
unchanged and still scaffolds into the current directory.
The named form refuses to proceed when the target directory exists and
is not empty, unless --force is passed, and always refuses when the
target is a regular file since --force cannot resolve that. Both
refusals keep the existing "Refusing to overwrite" prefix. The closing
hint now leads with "cd my_app" when a name was given.
Four existing tests assumed the old layout and now point their
follow-up commands at the created subdirectory. One of them,
test_cli_doctor_runs, was passing without exercising a scaffolded
project at all; retargeting restores what it was written to cover.
The name argument must be a single relative directory component, and the
resolved target must be a direct child of the current directory. Before this
change the name never touched the filesystem, so path-like names were
harmless; now they would resolve elsewhere, and `pn init .. --force` or a
symlink at ./<name> would overwrite files outside the current directory.
Note that Path("..").name returns "..", so ".." is rejected by name rather
than by the component check. --force lifts neither guard.
Deliberately not addressed: any project-name charset restriction, which is a
product decision; and TOML escaping for names containing quotes or control
characters, which predates this change since name always flowed straight into
the config.
The getting-started walkthrough and the CONTRIBUTING snippet both left you outside the project they had just created, and the CLI reference bullet didn't say where a named project lands. Documents the two new refusal messages in troubleshooting, following that file's convention of one heading per literal error string.
|
Thanks for a great first contribution. The guards, tests, and docs all went beyond what the issue asked for. Merged as v0.30.0. On your questions: agreed on keeping the trailing separator strict, and pn init "my app" is fine as shipped. If you want a small follow-up, quoting the hint would be welcome. And yes, please open the issue for the unescaped name in pythonnative.toml. |
Sorry, something went wrong.
Got it, thanks! |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
What
pythonnative.toml, and .gitignore into it.
named after it.
unless --force is passed. An existing but empty directory is fine.
Why
Closes #21. Every comparable scaffolder (cargo new, create-react-app,
django-admin startproject) creates the directory, and docs/examples.md
already told people to cd my-app after pn init my-app, which didn't work.
Newcomers were surprised to find their working directory filled in.
How (brief)
init_project() resolves a target directory up front and points app/,
pythonnative.toml, and .gitignore at it; everything downstream is
unchanged. Four guards run before any filesystem work:
a project name;
refuses a symlink at ./<name> pointing elsewhere;
into a directory;
--force lifts only the last of these. Every refusal keeps the CLI's existing
Refusing to ... phrasing.
The first two guards deserve a note. Before this change the name argument never
touched the filesystem, so path-like names were harmless: they only fed the
project name and the app id. Now the name resolves to a path, which means
pn init .. --force, or a symlink at ./<name>, would write outside the
current directory. Both are closed here rather than left for a follow-up, since
this change is what introduces the exposure. Worth knowing that Path("..").name
returns "..", so ".." is rejected by name rather than by the component check.
target.mkdir() is never called: app_dir.mkdir(parents=True) already creates
the parent. No new helpers, imports, or dependencies.
Testing
./scripts/check.sh passes locally (Ruff, Black, MyPy, build, pytest, E2E
coverage), and mkdocs build --strict is clean.
New tests in tests/test_cli.py cover the named form creating and populating
./my_app/ with the cwd otherwise untouched, the no-name form scaffolding into
the cwd with a deterministic derived id, the non-empty refusal including that
--force scaffolds over rather than wiping, an existing empty target
succeeding without --force, an existing file refused both ways, path-like
names across seven forms, and symlinked targets with and without --force.
The traversal and symlink tests are mutation-checked: with the relevant guard
removed they fail, and the escape assertions compare the external file
byte-for-byte rather than only counting directory entries, so they pin the
overwrite rather than a side effect of it.
Five existing tests assumed the old layout and now point their follow-up
commands at the created subdirectory, keeping the MyApp argument so the
com.example.myapp derivation stays covered. One of them,
test_cli_doctor_runs, was passing without exercising a scaffolded project at
all, since pn doctor prints its banner and exits 1 even with no config;
retargeting restores what it was written to cover.
test_cli_init_refuses_overwrite needed no change and is untouched.
Risks/Impact
Behavior change for anyone scripting pn init <name> and expecting output in
the current directory: artifacts now land in ./<name>/, so follow-up commands
need a cd or a path. The no-name form is a drop-in.
Docs/Follow-ups
Updated docs/getting-started.md, docs/api/cli.md (both the hand-written
bullet and the mkdocstrings-rendered docstring), CONTRIBUTING.md, and
docs/meta/troubleshooting.md with an entry per new message, following that
file's convention. docs/examples.md needed no change; it already described
this behavior.
Two things I'd rather you(@owenthcarey) decide than assume:
check. Normalizing it would add ambiguity for an argument documented as a
project name, so I left it strict.
work. Happy to reject such names or quote the hint if you'd prefer.
Separately, and out of scope here: a project name containing a quote or a
control character isn't escaped when written into pythonnative.toml. That
predates this change, since the name always flowed straight into the config.
I can open an issue if it's useful.
Closes #21
Demo