{"id":2841,"library":"venusian","title":"Venusian","description":"Venusian is a Python library that enables framework authors to defer actions typically performed by decorators at import time. Instead, these actions are executed during a separate 'scan' phase. This allows for more flexible configuration and improved testability of decorated code. The library is actively maintained, with the current version being 3.1.1, and supports CPython and PyPy versions 3.7 and above.","status":"active","version":"3.1.1","language":"en","source_language":"en","source_url":"https://github.com/Pylons/venusian","tags":["decorators","framework-utilities","reflection","metaprogramming","configuration"],"install":[{"cmd":"pip install venusian","lang":"bash","label":"Install stable release"}],"dependencies":[],"imports":[{"note":"Used to initiate the deferred decorator action scan.","wrong":null,"symbol":"Scanner","correct":"from venusian import Scanner"},{"note":"Attaches a callback to a decorated object for later processing during a scan.","wrong":null,"symbol":"attach","correct":"venusian.attach(wrapped, callback)"}],"quickstart":{"code":"import venusian\n\n# Define a custom decorator that uses venusian.attach\nclass mydecorator:\n    def __init__(self, category='default'):\n        self.category = category\n\n    def __call__(self, wrapped):\n        def callback(scanner, name, obj):\n            # This function is called during the scan phase\n            print(f\"Scanning {obj.__name__} in category '{self.category}'\")\n            # You might register `obj` or `name` in a registry here\n            scanner.registry.append((name, obj, self.category))\n        venusian.attach(wrapped, callback, category=self.category)\n        return wrapped # Return the original object, untouched at import time\n\n# Define some functions to be decorated\n@mydecorator(category='views')\ndef my_view():\n    return \"Hello, Venusian!\"\n\n@mydecorator(category='api')\ndef my_api_endpoint():\n    return {\"data\": \"from api\"}\n\n# Create a scanner instance and a custom registry\nscanner = venusian.Scanner(registry=[])\n\n# Scan the current module (or a specific package/module)\n# The callbacks attached via mydecorator will now be executed\nscanner.scan(__name__)\n\n# Access the collected information from the registry\nprint(\"\\n--- Collected Registry Data ---\")\nfor name, obj, category in scanner.registry:\n    print(f\"Name: {name}, Object: {obj.__name__}, Category: {category}\")\n\n# Verify that original functions are callable\nprint(f\"\\nDirect call to my_view: {my_view()}\")\n","lang":"python","description":"This example demonstrates how to create a custom decorator that uses `venusian.attach` to defer its actions. A `venusian.Scanner` is then used to explicitly trigger these deferred actions, collecting metadata about the decorated functions into a custom registry."},"warnings":[{"fix":"Upgrade to Python 3.5+ to use `venusian >= 3.0.0`.","message":"Versions 3.0.0 and 2.0.0 dropped support for Python 2.7, 3.3, and 3.4. Projects on these older Python versions must remain on `venusian < 2.0.0` or `venusian < 3.0.0` respectively (for Python 3.x).","severity":"breaking","affected_versions":"< 3.0.0"},{"fix":"Upgrade to Python 3.7+ to use `venusian >= 3.1.0`.","message":"Version 3.1.0 removed support for Python 3.5 and 3.6. Users on these Python versions should use `venusian < 3.1.0` if they cannot upgrade their Python interpreter.","severity":"breaking","affected_versions":"< 3.1.0"},{"fix":"Pass an `onerror` callable to `scanner.scan(..., onerror=my_error_handler_func)` to customize error handling.","message":"By default, `venusian.Scanner.scan()` will propagate `ImportError` exceptions encountered while trying to import modules. To suppress or handle these errors, provide an `onerror` callback function to the `scan()` method.","severity":"gotcha","affected_versions":"All versions (since 1.0)"},{"fix":"If decorator inheritance is desired, it must be explicitly handled within your custom decorator logic or scan phase, for example, by re-attaching or copying callbacks from the superclass during the scan.","message":"Venusian decorators attached to a class are not implicitly inherited by subclasses. Each class declaration should have its own local set of callbacks; callbacks added via decorations are not inherited from a superclass.","severity":"gotcha","affected_versions":"All versions (fixed in 0.4 / 1.0 but design decision)"}],"env_vars":null,"last_verified":"2026-04-10T00:00:00.000Z","next_check":"2026-07-09T00:00:00.000Z"}