{"id":14618,"library":"hatchling-autoextras-hook","title":"Hatchling Auto Extras Hook (Custom Metadata Hook Pattern)","description":"This entry describes a common pattern for creating a custom metadata hook within Hatchling (version 1.x), enabling automatic generation or modification of project extras. While 'hatchling-autoextras-hook' exists as a PyPI package, its low version and lack of dedicated documentation suggest it's more of an illustrative or niche implementation rather than a widely used standalone plugin. The typical approach involves implementing a custom metadata hook using `hatchling` itself, which allows dynamic modification of project metadata during the build process, including `optional-dependencies` (extras). Hatchling is a modern, extensible build backend for Python projects, with a rapid release cadence.","status":"active","version":"0.1.3","language":"en","source_language":"en","source_url":"https://hatch.pypa.io/latest/plugins/metadata-hook/reference/","tags":["hatchling","build-system","metadata-hook","extras","dynamic-metadata","pyproject.toml"],"install":[{"cmd":"pip install hatchling","lang":"bash","label":"Install Hatchling (core library)"},{"cmd":"pip install hatchling-autoextras-hook","lang":"bash","label":"Install the specific PyPI package (if used as a dependency)"}],"dependencies":[{"reason":"Provides the build backend and metadata hook interface.","package":"hatchling"}],"imports":[{"symbol":"MetadataHookInterface","correct":"from hatchling.metadata.plugin.interface import MetadataHookInterface"},{"note":"The most common pattern is to write a custom hook in your project (e.g., `hatch_build.py`), rather than importing a specific `AutoExtrasHook` from a third-party package, if 'autoextras' is a custom logic for your project.","wrong":"from hatchling_autoextras_hook import AutoExtrasHook","symbol":"CustomMetadataHook","correct":"from hatch_build import CustomMetadataHook"}],"quickstart":{"code":"# pyproject.toml\n[build-system]\nrequires = [\"hatchling\"]\nbuild-backend = \"hatchling.build\"\n\n[project]\nname = \"my-project\"\nversion = \"0.1.0\"\ndynamic = [\"optional-dependencies\"]\n\n[tool.hatch.metadata.hooks.custom]\npath = \"hatch_build.py\"\n\n# hatch_build.py\nfrom hatchling.metadata.plugin.interface import MetadataHookInterface\n\nclass CustomMetadataHook(MetadataHookInterface):\n    PLUGIN_NAME = 'custom' # This name is fixed for custom hooks defined via `path`\n\n    def update(self, metadata: dict) -> None:\n        # Example: dynamically generate 'docs' extra\n        if 'docs' not in metadata.get('optional-dependencies', {}):\n            metadata.setdefault('optional-dependencies', {})\n            metadata['optional-dependencies']['docs'] = [\n                \"sphinx>=5.0\",\n                \"myst-parser\",\n            ]\n        \n        # Example: dynamically generate 'testing' extra based on some logic\n        if 'TEST_ENV' in self.config: # Access hook configuration\n            metadata.setdefault('optional-dependencies', {})\n            metadata['optional-dependencies']['testing'] = [\n                \"pytest>=7.0\",\n                \"pytest-cov\",\n            ]\n\n# To see effect, you would typically run:\n# hatch build","lang":"python","description":"This example demonstrates how to set up a custom metadata hook (`hatch_build.py`) to dynamically add or modify extras (optional-dependencies) in your `pyproject.toml`. The `dynamic = [\"optional-dependencies\"]` declaration is crucial. The hook's `update` method receives the project's metadata and can modify it in-place. Configuration can be passed to the hook via `pyproject.toml`."},"warnings":[{"fix":"If your custom metadata hook dynamically adds dependencies that are required *by the build environment itself*, you must ensure these are either statically declared in `build-system.requires` or that your hook explicitly manages adding them to the build environment's path/dependencies if that's your intended use case, typically by manually adjusting `sys.path` within the hook or using a build hook in conjunction for runtime dependencies.","message":"Hatchling 1.16.0 changed how environment dependency resolution interacts with metadata hooks. Previously, metadata hooks might be inadvertently triggered during dependency resolution. This was rectified, meaning dynamic dependencies set via a metadata hook are not available for the build environment's own dependency resolution unless explicitly handled.","severity":"breaking","affected_versions":">=1.16.0"},{"fix":"Always declare any metadata fields that your hook modifies (e.g., `optional-dependencies`, `dependencies`, `authors`) in the `[project] dynamic = [...]` section of your `pyproject.toml`. Failure to do so will result in your dynamic changes being ignored. If a field is declared `dynamic`, it must *not* have a static definition in `pyproject.toml`.","message":"For `hatchling` to respect dynamically set metadata fields (like `optional-dependencies`), they must be explicitly listed in the `[project] dynamic = [...]` array in your `pyproject.toml`.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Be aware that for custom hooks defined by path, the logical name for configuration purposes is always `custom`. Any `PLUGIN_NAME` defined in your Python class will not alter this behavior. This also means you can only have one `custom` metadata hook per project defined this way.","message":"When creating a custom metadata hook by specifying `path = \"your_hook.py\"` under `[tool.hatch.metadata.hooks.custom]`, the `PLUGIN_NAME` attribute within your `MetadataHookInterface` subclass is ignored. The plugin name will always be `custom`.","severity":"gotcha","affected_versions":"All versions"},{"fix":"If your metadata hook needs to access code from your package, avoid direct `import your_package`. Instead, consider adding your package's source directory (e.g., `src/`) to `sys.path` within the hook, or refactor the logic to be standalone and not depend on the partially built package. If the dependencies are truly build-time only, they should be in `build-system.requires`.","message":"Importing your own package's code within a metadata hook can lead to circular dependency issues or incorrect behavior because the package is not yet fully built or installed in the isolated build environment.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-14T00:00:00.000Z","next_check":"2026-07-13T00:00:00.000Z","problems":[],"ecosystem":"pypi"}