Setting Up Python Projects
Prerequisites
This is a Python-specific extension to myai's setting-up-projects.
Load setting-up-projects first for the project shape decision framework,
directory layout patterns, bootstrap checklist philosophy, graceful shutdown
strategy, and domain adaptation guidance. This skill provides only the
Python-specific tooling, config, and code examples.
Also requires engineering-principles (via myai bootstrap).
When to Use This Extension
Use setting-up-projects for all project bootstrap decisions, then load this
extension for Python-specific tooling when the project is Python.
For standalone scripts, use writing-scripts (myai). For backend/service repos,
start with setting-up-python-backends (Python-specific) after myai's
setting-up-backends. For architecture shape decisions on existing projects,
use architecting-python-changes.
Templates location
All templates, rules and docs are available at upstream source of this ruleset https://github.com/quick-brown-foxxx/coding_rules_python.
Python Project Layout
See setting-up-projects for the philosophy behind this layout.
project/
├── src/appname/
│ ├── __init__.py # __version__ = "0.1.0"
│ ├── __main__.py # Entry point
│ ├── constants.py # Shared constants
│ ├── core/ # Business logic
│ │ ├── models.py # Data types (dataclasses)
│ │ └── manager.py # Business operations
│ ├── cli/ # CLI interface
│ │ ├── commands.py # Command implementations
│ │ ├── parser.py # Argument parsing
│ │ └── output.py # Formatted output helpers
│ ├── ui/ # Qt GUI (if applicable)
│ │ ├── main_window.py
│ │ ├── dialogs/
│ │ └── widgets/
│ ├── utils/ # Stateless utilities
│ │ ├── paths.py
│ │ └── logging.py
│ ├── wrappers/ # Third-party lib wrappers
│ │ └── some_wrapper.py
│ └── stubs/ # Type stubs for untyped libs
├── tests/
│ ├── unit/
│ ├── integration/
│ ├── fixtures/
│ └── conftest.py
├── scripts/ # Dev utilities
│ └── check_type_ignore.py
├── docs/
│ └── coding_rules.md # Copy from rules/coding_rules.md
├── shared/ # Cross-cutting shared code copied from this repo
│ ├── logging/ # Logging + colored output (if needed)
│ └── shortcuts/ # Keyboard shortcuts (if PySide6 app)
├── shared_tests/ # Generic tests for copied shared modules
│ ├── test_shortcuts_base.py
│ └── test_shortcuts_manager.py
├── AGENTS.md # Copy from templates/AGENTS.md, customize
├── CLAUDE.md # Symlink → AGENTS.md
├── pyproject.toml # Copy from templates/pyproject.toml, customize
├── .pre-commit-config.yaml # Copy from templates/pre-commit-config.yaml
├── .gitignore # Copy from templates/gitignore
└── .vscode/
├── settings.json # Copy from templates/vscode_settings.json
└── extensions.json # Copy from templates/vscode_extensions.json
Python Setup Checklist
See setting-up-projects for the general bootstrap philosophy.
Create directory structure:
mkdir -p src/APPNAME tests/unit tests/integration tests/fixtures scripts docs .vscodeCopy baseline files and directories:
- Promote template files into the new project:
templates/pyproject.toml→pyproject.toml(update[project]section)templates/AGENTS.md→AGENTS.md(fill TODO sections)templates/pre-commit-config.yaml→.pre-commit-config.yamltemplates/gitignore→.gitignoretemplates/vscode_settings.json→.vscode/settings.jsontemplates/vscode_extensions.json→.vscode/extensions.json
- Copy
shared/andshared_tests/into the new project root if you need the provided building blocks. Trim unused shared modules and dependencies afterward. - Copy
rules/coding_rules.md→docs/coding_rules.md - Create symlink:
ln -s AGENTS.md CLAUDE.md - Canonical local bootstrap artifact:
skills/setting-up-python-projects/bootstrap_downstream_repo.sh SOURCE_REPO TARGET_REPO
- Promote template files into the new project:
Trim copied shared modules (if needed):
- Keep only the
shared/andshared_tests/subdirectories you actually use shared/logging/— colored logging, file rotating logs, CLI output (seesetting-up-logging)shared/shortcuts/— keyboard shortcuts for PySide6 apps (seesetting-up-shortcuts)- Keep matching generic tests in
shared_tests/beside the copied shared modules - Update import paths after copying if the project package name changes
- Keep only the
Create entry points:
# src/APPNAME/__init__.py __version__ = "0.1.0" # src/APPNAME/__main__.py from __future__ import annotations import sys def main() -> int: from APPNAME.bootstrap import create_services from APPNAME.cli import build_cli_app services = create_services(debug=False) app = build_cli_app(services) app(args=sys.argv[1:], prog_name="APPNAME", standalone_mode=False) return 0 if __name__ == "__main__": sys.exit(main())Keep
__main__.pythin. Assemble the real presentation layer elsewhere and let__main__.pydo only the final handoff. For multi-interface apps, use the pattern frombuilding-multi-ui-apps.Create initial test:
# tests/test_main.py from __future__ import annotations import sys import pytest from APPNAME.__main__ import main def test_main_runs(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(sys, "argv", ["APPNAME"]) assert main() == 0Initialize environment:
git init uv sync --all-extras --group dev uv run poe lint_full uv run poe testAfter setup, keep using project-local commands through
uv:uv run python,uv run pytest,uv run ruff,uv run basedpyright,uv run poe,uv run pre-commit. The default verification flow isuv run poe lint_fullfollowed byuv run poe test.Verify everything works:
uv run poe lint_fullpasses (basedpyright + Ruff check/format + custom linters)uv run poe testpasses
Python Graceful Shutdown
See setting-up-projects for the shutdown strategy decision framework.
Scripts and simple CLIs
# __main__.py
def main() -> int:
try:
return run()
except KeyboardInterrupt:
return 130 # 128 + SIGINT(2), Unix convention
Subprocess wrappers
Always pass start_new_session=True — creates a process group so you can kill the entire tree, not just the parent.
Quick subtask (immediate kill):
import os, signal, subprocess
process = subprocess.Popen(cmd, start_new_session=True)
try:
process.wait()
except KeyboardInterrupt:
os.killpg(process.pid, signal.SIGKILL)
Complex tool wrapper (escalation):
process = subprocess.Popen(cmd, start_new_session=True)
try:
process.wait()
except KeyboardInterrupt:
os.killpg(process.pid, signal.SIGTERM)
try:
process.wait(timeout=5.0)
except subprocess.TimeoutExpired:
os.killpg(process.pid, signal.SIGKILL)
Async subprocess (complex apps using asyncio):
process = await asyncio.create_subprocess_exec(*cmd, start_new_session=True)
try:
await process.wait()
except asyncio.CancelledError:
process.terminate()
try:
await asyncio.wait_for(process.wait(), timeout=5.0)
except TimeoutError:
process.kill()
raise
Bootstrap Script
Use skills/setting-up-python-projects/bootstrap_downstream_repo.sh as the canonical local bootstrap artifact. It promotes template files into place, copies shared/, shared_tests/, and docs files, creates CLAUDE.md, then runs uv sync --all-extras --group dev, uv run poe lint_full, and uv run poe test in the downstream repo.
Python-Specific Customization
See setting-up-projects for the general domain adaptation framework.
| Area | How to adapt |
|---|---|
| pyproject.toml | Adjust ruff rules, pytest markers, plugins, and narrowly-justified overrides for ecosystem gaps. Do not relax strict typing by default; document every real exception. |
| AGENTS.md | Fill TODO sections with project-specific architecture, key decisions, domain vocabulary, and workflows. This is the agent's primary orientation document — make it specific. Skills section: remove skills the project won't use (e.g. building-multi-ui-apps for a pure CLI), add domain-specific skills (e.g. building-qt-apps, setting-up-shortcuts). |
| coding_rules.md | Extend or override rules for the domain. Add domain-specific conventions (e.g. database migration rules, API versioning policy, data validation requirements). |
Wrapper enforcement with banned-api
When the project wraps third-party libraries (for typing, platform abstraction, or swappability), enforce wrapper usage via ruff's flake8-tidy-imports.banned-api in pyproject.toml:
[tool.ruff.lint.flake8-tidy-imports.banned-api]
"soundcard".msg = "Use src/wrappers/audio_backend.py instead"
"faster_whisper".msg = "Use src/wrappers/transcriber.py instead"
Wrap when a library is poorly typed (need typed facade), platform-specific (need abstraction layer), or swappable (need stable internal API). The template pyproject.toml has commented examples — uncomment and customize per project.
Inside the wrapper files themselves, suppress the ban with a per-file ruff ignore: "src/wrappers/*".msg = "" in the banned-api config, or use # noqa: TID251 on individual import lines.
Research before building
When setting up a project in an unfamiliar domain or with unfamiliar libraries:
- Research the domain's conventions — look up how well-maintained projects in the same space are structured
- Check library compatibility — verify libraries work together and with basedpyright strict mode (some libraries have poor type stubs; plan wrappers early)
- Identify domain-specific tooling — some domains have their own linters, formatters, or validation tools
- Check for basedpyright known issues — some libraries (numpy, pandas, SQLAlchemy) need specific configuration or stub packages to work cleanly in strict mode
Quick customization checklist
- Directory layout matches the domain, not the generic template
- Dependencies are domain-appropriate (researched, not guessed)
- AGENTS.md describes this project, not a generic Python project
- coding_rules.md has domain-specific additions if needed
- Test structure reflects what matters most for this project
- basedpyright config accounts for domain-specific library quirks
Handoff
- Use
setting-up-python-backendsfor backend repos (aftersetting-up-backends) - Use
building-multi-ui-appsfor GUI+CLI sharing a core - Use
writing-python-codefor implementation rules
Related Skills
setting-up-projects(myai) — Parent skill. Load first for project shape decisions and bootstrap philosophy.engineering-principles(myai) — Foundation. Language-agnostic philosophy.architecting-changes(myai) — Architecture decisions.writing-scripts(myai) — For single-file scripts (Python and TypeScript/Node examples).setting-up-python-backends— For backend/service repos.testing-python— Python testing setup.writing-python-code— Python coding rules.