{"record":{"id":"89e4c6336b2514e1","repo":"p-e-w/heretic","slug":"cls-name-must-not-define-init-use-an","errorCode":null,"errorMessage":"{cls.__name__} must not define __init__(). Use an optional init(ctx) method for plugin-specific initialization.","messagePattern":"(.+?) must not define __init__\\(\\)\\. Use an optional init\\(ctx\\) method for plugin-specific initialization\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"src/heretic/plugin.py","lineNumber":242,"sourceCode":"            if not isinstance(settings, settings_model):\n                raise TypeError(\n                    f\"{self.__class__.__name__}.settings must be an instance of \"\n                    f\"{settings_model.__name__}\"\n                )\n        self.settings = settings\n        self.heretic_settings = heretic_settings\n\n    @classmethod\n    def validate_contract(cls) -> None:\n        \"\"\"\n        Validate the plugin contract.\n\n        - Plugins must not define a constructor (`__init__`). Initialization is\n          handled by `Plugin.__init__` and an optional `init(ctx)` method.\n        - Plugin subclasses may define `settings: <BaseModelSubclass>` to declare a settings schema.\n        \"\"\"\n        if \"__init__\" in cls.__dict__:\n            raise TypeError(\n                f\"{cls.__name__} must not define __init__(). \"\n                \"Use an optional init(ctx) method for plugin-specific initialization.\"\n            )\n\n    @classmethod\n    def get_settings_model(cls) -> type[BaseModel] | None:\n        \"\"\"\n        Return the plugin settings model, if present.\n        - If the plugin has a `settings: <BaseModelSubclass>` type annotation,\n          that type is used as the settings schema.\n        - Otherwise: no settings schema.\n        \"\"\"\n\n        def unwrap_settings_type(tp: Any) -> Any:\n            \"\"\"Unwrap `Annotated[T, ...]`.\"\"\"\n            while True:\n                origin = get_origin(tp)\n                if origin is Annotated:","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/p-e-w/heretic/blob/bedb94ef117a271532ac2058447fbc165d5051bd/src/heretic/plugin.py#L224-L260","documentation":"Plugin subclasses are forbidden from defining their own __init__. The Plugin metaclass/base class handles construction itself, and plugin-specific initialization must go through an optional init(ctx) hook instead. This keeps plugin lifecycle uniform across the framework.","triggerScenarios":"Defining a plugin class (subclass of the heretic Plugin base) with an `__init__` method in its class body; validate_contract is invoked via _load_and_init_scorers when plugins are loaded.","commonSituations":"Porting a scorer class written as a plain class into a heretic plugin while keeping its old constructor; copying a pydantic model-style class that relies on custom __init__ logic.","solutions":["Remove the __init__ method from the plugin class.","Move initialization logic into an optional `init(self, ctx)` method.","Pass any needed configuration via the plugin's `settings` (a pydantic BaseModel) rather than constructor arguments."],"exampleFix":"// before\nclass MyScorer(Plugin):\n    def __init__(self, model):\n        self.model = model\n// after\nclass MyScorer(Plugin):\n    settings: MySettings\n    def init(self, ctx):\n        self.model = load_model(self.settings.model_name)","handlingStrategy":"validation","validationCode":"import inspect\nassert \"__init__\" not in MyPlugin.__dict__, \"Plugin must not define __init__; use init(ctx)\"","typeGuard":null,"tryCatchPattern":"try:\n    Plugin.validate_contract(MyPlugin)\nexcept TypeError as e:\n    if \"must not define __init__\" in str(e):\n        print(f\"Fix plugin {MyPlugin.__name__}: move init logic to init(ctx)\")\n    else:\n        raise","preventionTips":["Never write __init__ in Plugin subclasses; use init(ctx).","Ship a unit test that calls validate_contract on every plugin class.","Configure settings via a pydantic settings model instead of constructor args."],"tags":["python","plugin","api-misuse"],"backgroundTag":"plugin-contract-violation","analyzedSha":"bedb94ef117a271532ac2058447fbc165d5051bd","analyzedAt":"2026-08-29T08:38:06.692Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}