twat-hatch is a powerful command-line tool for initializing modern Python projects. It helps you quickly scaffold new standalone packages or complex plugin-based architectures, adhering to current best practices in Python development.
twat-hatch is part of the twat collection of Python developer utilities, aimed at streamlining and enhancing the Python development workflow.
twat-hatch is designed for Python developers who want to:
- Bootstrap new projects rapidly: Get a well-structured project skeleton in seconds.
- Build extensible applications: Easily create systems with a core package and multiple installable plugins.
- Adhere to modern standards: Automatically set up projects with
pyproject.toml(PEP 621),src/layout, and robust tooling. - Focus on code, not boilerplate: Reduce the manual effort of setting up configurations, directory structures, and initial files.
- Accelerated Project Setup: Generates all the necessary boilerplate for a new Python package, including directory structure,
pyproject.toml, license, README, and basic test setup. - Plugin Architecture Support: Provides first-class support for creating plugin host packages and individual plugin packages, managing entry points and dependencies.
- Best Practices by Default:
- Uses
pyproject.tomlfor all package metadata and tool configuration (PEP 621). - Implements the
src/layout for cleaner package structure. - Integrates with modern tooling like
rufffor linting/formatting andmypyfor type checking.
- Uses
- Flexible Configuration: Uses a simple TOML file (
twat-hatch.toml) for defining project parameters, which can be generated interactively. - Developer Tool Integration: Optionally initializes Git repositories, creates GitHub repositories (via
ghCLI), and sets up MkDocs for documentation. - Type-Safe & Robust: Leverages Pydantic for configuration validation, ensuring your setup is correct from the start.
You can install twat-hatch using pip or uv:
pip install twat-hatchOr with uv:
uv pip install twat-hatchtwat-hatch primarily operates through its command-line interface (CLI).
The first step is to create a configuration file, typically named twat-hatch.toml. The init command helps you generate this file.
Interactive Mode:
If you run twat-hatch init without arguments, it will guide you through an interactive prompt:
twat-hatch initYou'll be asked for:
- Package type (standalone package, plugin, or plugin-host)
- Package name
- Author details
- Python version constraints
- License
- Optional features like MkDocs and Git repository initialization.
This will create a twat-hatch.toml file in your current directory.
Non-Interactive Mode:
You can also provide all configuration values as command-line options. For example:
twat-hatch init --name "my-cool-package" --author-name "Your Name" --author-email "you@example.com" --min-python "3.10" --license "MIT"This is useful for scripting or when you already know your desired configuration.
Specify Package Type:
Use the package_type argument to define the kind of project:
twat-hatch init --package-type package(default)twat-hatch init --package-type plugin --plugin-host "my-core-app"twat-hatch init --package-type plugin-host
To see what a typical twat-hatch.toml looks like for different package types without generating a file, use:
twat-hatch config show --package-type pluginThis will print an example configuration to the console. Supported types are package, plugin, and plugin-host.
Once you have your twat-hatch.toml (or a custom-named one), run the create command:
twat-hatch createBy default, it looks for twat-hatch.toml in the current directory. You can specify a different configuration file:
twat-hatch create --config-path "path/to/my-config.toml"This command will:
- Read the configuration.
- Generate the directory structure for each package specified.
- Populate files based on templates (e.g.,
pyproject.toml,__init__.py,README.md). - Initialize a Git repository and make an initial commit (if
use_vcsis enabled). - Optionally set up MkDocs.
Your new Python project(s) will be ready in the specified output directory!
While primarily a CLI tool, twat-hatch's core logic can be used programmatically.
Generating Packages:
from pathlib import Path
from twat_hatch import PackageInitializer
# Ensure you have a 'twat-hatch.toml' or provide the path to one
# For example, create a dummy config for demonstration:
config_content = """
[project]
packages = ["my-programmatic-package"]
output_dir = "generated_packages"
[author]
name = "Dev Team"
email = "dev@example.com"
github_username = "devteam"
[package]
min_python = "3.9"
license = "Apache-2.0"
development_status = "3 - Alpha"
[features]
use_vcs = false
"""
Path("my_prog_config.toml").write_text(config_content)
try:
# Initialize with the path to your config file
initializer = PackageInitializer(config_path="my_prog_config.toml")
# Create all packages defined in the configuration
initializer.initialize_all()
print("Package 'my-programmatic-package' created in 'generated_packages/'")
except FileNotFoundError:
print("Error: Configuration file not found.")
except Exception as e:
print(f"An error occurred: {e}")
finally:
# Clean up the dummy config
Path("my_prog_config.toml").unlink(missing_ok=True)Generating Configuration Programmatically:
You can also generate the twat-hatch.toml content itself using ConfigurationGenerator:
from twat_hatch.config import ConfigurationGenerator
from pathlib import Path
generator = ConfigurationGenerator()
config_str = generator.generate_config(
package_type="package",
name="another-package",
author_name="AI Coder",
author_email="ai@example.com",
github_username="aicoder",
min_python="3.10",
license="BSD-3-Clause",
use_mkdocs=True
)
# You can then write this string to a file
Path("generated_config.toml").write_text(config_str)
print("Configuration for 'another-package' written to generated_config.toml")
# Clean up
Path("generated_config.toml").unlink(missing_ok=True)This allows for more dynamic or automated project setup scenarios from within other Python scripts or tools.
This section provides a more detailed look into how twat-hatch works internally and outlines guidelines for coding and contributions.
twat-hatch leverages a combination of configuration parsing, templating, and command-line utilities to generate Python projects.
Core Components:
PackageInitializer(src/twat_hatch/hatch.py): This is the main orchestrator. It reads the configuration, determines the package types, and uses theTemplateEngineto generate files and directories. It also handles Git repository initialization and optional GitHub integration.PackageConfig(src/twat_hatch/hatch.py): A Pydantic model that defines the structure of thetwat-hatch.tomlconfiguration file. It's responsible for loading, validating, and providing type-safe access to configuration parameters.TemplateEngine(src/twat_hatch/hatch.py): Manages the Jinja2 templating environment. It loads templates from thesrc/twat_hatch/themes/directory and renders them with the context derived fromPackageConfig.ConfigurationGenerator(src/twat_hatch/config.py): Responsible for generating the content of atwat-hatch.tomlfile, either for display (viaconfig show) or for theinitcommand when not using interactive prompts directly. It uses Jinja2 templates (*.toml.j2) for this.ConfigurationPrompts(src/twat_hatch/__main__.py): Handles the interactive command-line prompts whentwat-hatch initis run. It uses therichlibrary for a better user experience.- CLI (
src/twat_hatch/__main__.py): The command-line interface is built using thefirelibrary, which maps Python functions to CLI commands (init,config,create).
Configuration (twat-hatch.toml):
This TOML file is the heart of your project definition. It's parsed by PackageConfig. Key sections include:
[project]:packages: A list of package names to generate (distribution names, e.g., "my-package").plugin_host(optional): The name of the package that will act as the host for plugins. If specified, other packages in thepackageslist are treated as plugins for this host.output_dir(optional): Directory where packages will be created (defaults to current directory).
[author]:name,email,github_username: Information used inpyproject.tomland other generated files.
[package]:min_python: Minimum Python version (e.g., "3.8").max_python(optional): Maximum Python version (e.g., "3.12").license: SPDX license identifier (e.g., "MIT", "Apache-2.0").development_status: PyPI classifier for development status (e.g., "4 - Beta").
[dependencies]:dependencies: List of runtime dependencies for all packages.plugin_dependencies: Additional dependencies specifically for plugin packages.dev_dependencies: Dependencies for the development environment (e.g.,pytest,ruff). Intwat-hatch.toml, this corresponds to[development.additional_dependencies]in the example template, butPackageConfigmaps it fromdevelopment_data.get("additional_dependencies", []). For clarity in this README, it's referred to asdev_dependenciesas it's a common term and matches thePackageConfigfield.
[features]:mkdocs: Boolean, enables MkDocs setup.semver: Boolean, for semantic versioning (primarily influences initial versioning "0.1.0"; ongoing versioning is VCS-based viahatch-vcs).vcs: Boolean, enables Git repository initialization.
[tools](optional, intwat-hatch.toml):ruff: Custom Ruff configuration to be embedded in the generatedpyproject.toml.mypy: Custom MyPy configuration to be embedded in the generatedpyproject.toml.
Templating System:
twat-hatch uses Jinja2 for file generation. Templates are located in src/twat_hatch/themes/.
- Themes:
_shared/: Common snippets or base files used by multiple themes.default/: Base templates applied to ALL package types duringtwat-hatch create. This includes common files like.gitignore, basicpyproject.tomlstructure, etc.package/: Templates specific to standalone packages. Also,package.toml.j2is used byConfigurationGeneratorfor generatingtwat-hatch.tomlcontent for thepackagetype.plugin/: Templates specific to plugin packages. Defines entry points inpyproject.tomlto register with the host. Also,plugin.toml.j2is used byConfigurationGeneratorforplugintype configurations.plugin_host/: Templates specific to plugin host packages. Includes logic in__init__.pyfor plugin discovery. Also,plugin_host.toml.j2is used byConfigurationGeneratorforplugin-hosttype configurations.mkdocs/: Templates for setting up MkDocs documentation (iffeatures.mkdocsis true).
- Layering (during
twat-hatch create): Themes are applied in a specific order. Thedefaulttheme is always applied first. Then, the specific package type theme (package,plugin, orplugin_host) is applied. Finally, optional feature themes likemkdocsare applied. This allows for overriding and extending base templates. - Context: Templates are rendered with a context dictionary derived from
PackageConfig. Key variables includename(distribution name),import_name,author_name,license,python_version_info(a dictionary with various Python version formats), etc. File and directory names containing__package_name__are renamed to the package's import name. Files starting withhidden.are renamed to have a leading dot (e.g.,hidden.gitignorebecomes.gitignore).
Package Types (generated by twat-hatch create):
- Standalone Package: A standard Python package with its own
pyproject.toml,src/directory, and tests. Generated usingdefaultand thenpackagethemes. - Plugin Host Package: A package designed to discover and load plugins.
- Its
pyproject.tomlmay define extras for installing plugins. - Its
src/<package_name>/__init__.pyoften includes logic to discover plugins usingimportlib.metadata.entry_pointsfor a specific group (e.g.,my_host_package.plugins). - Generated using
defaultand thenplugin_hostthemes.
- Its
- Plugin Package: A package that extends a plugin host.
- Its
pyproject.tomldefines an entry point under the host's plugin group (e.g.,[project.entry-points."my_host_package.plugins"] my_plugin = "my_plugin_package.module:entry_point"). - Generated using
defaultand thenpluginthemes.
- Its
Version Control (Git):
If features.vcs is true (default):
git initis run in the newly created package directory.- The default branch is renamed to
main. - All generated files are added (
git add .). - An initial commit is made (
git commit -m "Initial commit"). - If a
github_usernameis provided in the config and theghCLI tool is installed and authenticated,twat-hatchwill attempt to create a public GitHub repository and push the initial commit.
Python Version Handling (src/twat_hatch/utils.py):
The PyVer class is a utility to:
- Parse Python versions from strings (e.g., "3.10") or tuples.
- Generate
requires_pythonspecifiers forpyproject.toml(e.g.,">=3.10, <3.13"if a max version is given). - Produce a list of PyPI trove classifiers for supported Python versions.
- Provide version strings suitable for
ruff(target-version) andmypy(python_version).
We welcome contributions to twat-hatch! Please follow these guidelines:
Coding Conventions:
- Style: Code style is enforced by
Ruff. Please runruff format .andruff check .before committing. Configuration is inpyproject.toml. - Type Safety:
MyPyis used for static type checking. Ensure your code passesmypy src/ tests/. Configuration is inpyproject.toml. - Pre-commit Hooks: It's highly recommended to install and use
pre-commithooks provided in.pre-commit-config.yaml. Runpre-commit installin your cloned repository. This will automatically run checks before each commit. - Imports:
- Use absolute imports within the
src/twat_hatchpackage. - Imports are sorted by
Ruff(which implementsisortlogic). - Relative imports are generally discouraged for main package code, as per
flake8-tidy-importsconfiguration (ban-relative-imports = 'all').
- Use absolute imports within the
Project Structure:
src/Layout: All main source code resides insrc/twat_hatch/.tests/: Unit and integration tests are located here.src/twat_hatch/themes/: Contains Jinja2 templates for code generation.- Each subdirectory typically represents a theme or a component of a theme (e.g.,
default,package,plugin,plugin_host,mkdocs). - Templates for generated files usually end with
.j2(e.g.,pyproject.toml.j2). - Templates for
twat-hatch.tomlgeneration itself are also in this directory (e.g.,package.toml.j2,plugin.toml.j2,plugin_host.toml.j2).
- Each subdirectory typically represents a theme or a component of a theme (e.g.,
Dependency Management:
- Project dependencies are managed by
Hatchand defined inpyproject.toml. - Runtime dependencies are under
[project.dependencies]. - Development dependencies are listed under
[project.optional-dependencies](e.g.,dev,test). - The project uses
hatch-pip-compilewhich can be used to generate and manage constraints files for reproducible environments, though explicit lock files might not be versioned if broad compatibility is prioritized.
Testing:
pytestis the testing framework.- Write tests for new features and bug fixes in the
tests/directory. - Ensure all tests pass by running
pytestorhatch run test. - Aim for high test coverage. Check coverage with
hatch run test-cov.
Contribution Process:
- Fork the repository on GitHub.
- Clone your fork locally:
git clone https://github.com/YOUR_USERNAME/twat-hatch.git - Create a new branch for your feature or bug fix:
git checkout -b feature/your-feature-nameorbugfix/issue-number. - Make your changes.
- Run linters and tests locally:
hatch run lint:all(runs ruff format, ruff check, mypy)hatch run test(runs pytest)- Or use
pre-commit run --all-filesif hooks are installed.
- Commit your changes with a clear and descriptive commit message. Consider using Conventional Commits format (e.g.,
feat: add support for X,fix: resolve issue Y).- A good commit message subject line is concise (<=50 chars).
- The body (if needed) explains the "what" and "why" of the change.
- Push your branch to your fork:
git push origin feature/your-feature-name. - Open a Pull Request (PR) against the
mainbranch of thetwardoch/twat-hatchrepository. - Clearly describe your changes in the PR. Reference any related issues.
Versioning:
twat-hatchuseshatch-vcsfor versioning. The version is automatically derived from Git tags (e.g.,v0.1.0).- The version is written to
src/twat_hatch/__version__.pyduring the build process byhatch-vcs.
License:
By contributing, you agree that your contributions will be licensed under the MIT License, as found in the LICENSE file.