The functionality of mxdev can be extended by hooks. This is useful to generate additional scripts or files or automate any other setup steps related to mxdev's domain.
Extension configuration settings end up in the mx.ini file.
They can be added globally to the settings section, as dedicated config sections or package specific.
To avoid naming conflicts, all hook-related settings and config sections must be prefixed with a namespace.
It is recommended to use the package name containing the hook as a namespace.
This looks like so:
[settings]
myextension-global_setting = 1
[myextension-section]
setting = value
[foo.bar]
myextension-package_setting = 1The extension is implemented as a subclass of mxdev.Hook:
from mxdev import Hook
from mxdev import State
class MyExtension(Hook):
namespace = "myextension"
"""The namespace for this hook."""
def read(self, state: State) -> None:
"""Gets executed after mxdev read operation."""
# Access configuration from state
# - state.configuration.settings: main [settings] section
# - state.configuration.packages: package sections
# - state.configuration.hooks: hook-related sections
# Example: Access your hook's settings
global_setting = state.configuration.settings.get('myextension-global_setting')
# Example: Access hook-specific sections
for section_name, section_config in state.configuration.hooks.items():
if section_name.startswith('myextension-'):
# Process your hook's configuration
pass
def write(self, state: State) -> None:
"""Gets executed after mxdev write operation."""
# Generate additional files, scripts, etc.
# Access generated requirements/constraints from:
# - state.requirements
# - state.constraintsThe State object passed to hooks contains:
-
state.configuration: Configuration object with:settings: Dict of main [settings] sectionpackages: Dict of package sectionshooks: Dict of hook-related sections
-
state.requirements: List of requirement lines (after write phase) -
state.constraints: List of constraint lines (after write phase)
The hook must be registered as an entry point in the pyproject.toml of your package:
[project.entry-points.mxdev]
myextension = "mypackage:MyExtension"Replace:
myextension: The name users will referencemypackage: Your Python package nameMyExtension: Your Hook subclass
-
Read phase: mxdev reads configuration and fetches sources
- All hooks'
read()methods are called
- All hooks'
-
Write phase: mxdev writes requirements and constraints
- All hooks'
write()methods are called
- All hooks'
- Use your package name as namespace prefix
- All settings:
namespace-setting_name - All sections:
[namespace-section] - This prevents conflicts with other hooks
- Generate additional configuration files (e.g., buildout.cfg, docker-compose.yml)
- Create wrapper scripts for development
- Generate IDE project files
- Export dependency graphs
- Integrate with other tools (pytest, tox, pre-commit)
- Fail gracefully: If your hook can't complete, log warnings instead of raising exceptions
- Document your settings: Provide clear documentation for all configuration options
- Use namespaces: Always prefix settings with your namespace
- Be minimal: Don't add heavy dependencies to your hook package
- Test thoroughly: Hooks run after mxdev's core operations, ensure they don't break workflows