{"id":13701181,"url":"https://github.com/h2non/siringa","last_synced_at":"2026-01-07T05:33:26.987Z","repository":{"id":57467945,"uuid":"89154782","full_name":"h2non/siringa","owner":"h2non","description":"Minimalist dependency injection library for Python that embraces type annotations syntax ","archived":false,"fork":false,"pushed_at":"2018-03-15T11:32:18.000Z","size":43,"stargazers_count":54,"open_issues_count":3,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-18T11:25:59.882Z","etag":null,"topics":["dependency","dependency-injection","dependency-injection-container","injection-container","inversion-of-control","pattern-matching"],"latest_commit_sha":null,"homepage":"http://siringa.rtfd.io","language":"Hy","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/h2non.png","metadata":{"files":{"readme":"README.rst","changelog":"History.rst","contributing":"docs/contributing.rst","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-04-23T16:09:18.000Z","updated_at":"2024-03-27T14:34:46.000Z","dependencies_parsed_at":"2022-09-19T08:20:17.697Z","dependency_job_id":null,"html_url":"https://github.com/h2non/siringa","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/h2non%2Fsiringa","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/h2non%2Fsiringa/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/h2non%2Fsiringa/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/h2non%2Fsiringa/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/h2non","download_url":"https://codeload.github.com/h2non/siringa/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245984561,"owners_count":20704794,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dependency","dependency-injection","dependency-injection-container","injection-container","inversion-of-control","pattern-matching"],"created_at":"2024-08-02T20:01:21.252Z","updated_at":"2026-01-07T05:33:21.957Z","avatar_url":"https://github.com/h2non.png","language":"Hy","funding_links":[],"categories":["Hy"],"sub_categories":[],"readme":".. image:: http://i.imgur.com/sbWr5Xv.png\n   :width: 100%\n   :alt: siringa logo\n   :align: center\n\n\n|Build Status| |PyPI| |Coverage Status| |Documentation Status| |Stability| |Versions| |SayThanks|\n\nAbout\n-----\n\n``siringa`` (meaning ``syringe`` in Italian) is a minimalist, idiomatic `dependency injection`_/`inversion of control`_ library\nfor Python_, implemented in Hy_, a Lisp dialect for Python.\n\nTo get started, take a look to the `documentation`_, `API`_, `tutorial`_ and `examples`_.\n\nFeatures\n--------\n\n-  Simple, idiomatic and versatile `programmatic API`_.\n-  Annotation based dependency injection that is `PEP 3017`_ and `PEP 0484`_ friendly.\n-  First-class decorator driven dependency injection and registering.\n-  Ability to create multiple dependency containers.\n-  Hierarchical dependency containers based on inheritance.\n-  Dependency inference based on pattern-matching techniques.\n-  First-class support for dependency mocking for better testing.\n-  Highly tolerant: any type of object can be injected, including ``str``, ``generators``, ``coroutines`` ...\n-  Detects cyclic dependencies (work in progress).\n-  Small and (almost) dependency-free library.\n-  Works with CPython 3+ (because of Hy \u003c\u003e CPython AST compatibility)\n\nDesign philosophy\n-----------------\n\n-  Code instrumentation should be non-intrusive and idiomatic.\n-  Explicitness over implicitness: dependencies and injections much be explicitly defined.\n-  Python idiomatic: embrace decorators and type annotations.\n-  Minimalism: less enables more.\n-  Uniformity: there is only one way to declare and consume dependencies.\n-  Predictability: developer intentions must persist based on explicitly defined intention.\n-  Domain agnostic: do not enforce any domain-specific pattern.\n\nInstallation\n------------\n\nUsing ``pip`` package manager:\n\n.. code-block:: bash\n\n    pip install --upgrade siringa\n\nOr install the latest sources from Github:\n\n.. code-block:: bash\n\n    pip install -e git+git://github.com/h2non/siringa.git#egg=siringa\n\n\nTutorial\n--------\n\nImporting siringa\n^^^^^^^^^^^^^^^^^\n\n.. code-block:: python\n\n    import siringa\n\nInstrumenting dependencies\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n``siringa`` embraces type hints/arguments annotation Python syntax for\ndependency inference and pattern matching.\n\n.. code-block:: python\n\n    @siringa.inject\n    def task(x, y, logger: '!Logger'):\n        logger.info('task called with arguments: {}, {}'.format(x, y))\n        return x * y\n\n\nYou can optionally annotate dependencies via ``siringa`` type annotations:\n\n.. code-block:: python\n\n    from siringa import A\n\n    @siringa.inject\n    def task(x, y, logger: A('Logger')):\n        logger.info('task called with arguments: {}, {}'.format(x, y))\n        return x * y\n\n\nFinally, for a DRYer approach you can simply annotate dependencies with ``!`` annotation flag.\n\nIn this case, the argument name expression will be used for dependency inference.\n\n.. code-block:: python\n\n    from siringa import A\n\n    @siringa.inject\n    def task(x, y, Logger: '!'):\n        Logger.info('task called with arguments: {}, {}'.format(x, y))\n        return x * y\n\n\nRegistering dependencies\n^^^^^^^^^^^^^^^^^^^^^^^^\n\n``siringa`` allows you to rely on decorators for idiomatic dependencies registering.\n\nDependency name is dynamically inferred at registration time based on ``class`` or ``function`` name.\n\n.. code-block:: python\n\n    @siringa.register\n    class Logger(object):\n        logger = logging.getLogger('siringa')\n\n        @staticmethod\n        def info(msg, *args, **kw):\n            logger.info(msg, *args, **kw)\n\n\nHowever, you can define a custom dependency name by simply passing a ``string`` as first argument:\n\n.. code-block:: python\n\n    @siringa.register('MyCustomLogger')\n    class Logger(object):\n        ...\n\nFinally, you can register dependencies with a traditional function call, such as:\n\n.. code-block:: python\n\n    class Logger(object):\n        pass\n\n    siringa.register('MyCustomLogger', Logger)\n\n    class compute(x, y):\n        return x * y\n\n    siringa.register('multiply', compute)\n\n\nInvocation\n^^^^^^^^^^\n\n``siringa`` wraps callable object in the transparent and frictionless way abstracting things for developers.\n\nYou can invoke or instantiate any dependency injection instrumented object\nas you do traditionally in raw Python code and ``siringa`` will do the rest\ninferring and pattern-matching required dependencies accordingly for you.\n\nBelow is an example of how simple it is:\n\n.. code-block:: python\n\n    # Call our previously declared function in this tutorial.\n    # Here, siringa will transparently inject required dependencies accordingly,\n    # respecting the invokation arguments and order.\n    task(2, 2) # =\u003e 4\n\nLet's demostrate this with a featured example:\n\n.. code-block:: python\n\n    import siringa\n\n    @siringa.register\n    def mul(x, y):\n        return x * y\n\n    @siringa.register\n    def mul2(x, mul: '!mul'):\n        return mul(x, 2)\n\n    @siringa.register\n    def pow2(x):\n        return x ** 2\n\n    @siringa.inject\n    def compute(x, pow: '!pow2', mul: '!mul2'):\n        return pow(mul(x))\n\n    compute(2) # =\u003e 16\n\n\nYou can also use the invocation API in case that the target object\nwas not properly instrumented as dependency:\n\n.. code-block:: python\n\n    @siringa.register\n    def mul2(x):\n        return x * 2\n\n    # Note that the function was not instrumented yet!\n    def compute(x, mul: '!mul2'):\n        return mul(x)\n\n    siringa.invoke(compute, 2)\n\n\nCreate a new dependency container\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n``siringa`` provides a built-in global dependency container for usability purposes,\nbut you can create as much containers as you want.\n\nIn the ``siringa`` idioms, this means creating a new dependency layer which provides its\nown container and dependency injection API, pretty much as the global package API.\n\nYou can create a new dependencies ``layer`` such as:\n\n.. code-block:: python\n\n    layer = siringa.Layer('app')\n\n    # Then you can use the standard API\n    layer.register('print', print)\n\n    # Then you can use the standard API\n    @layer.inject\n    def mul2(x, print: '!'):\n        print('Argument:', x)\n        return x * 2\n\n    mul2(x)\n\nA dependency layer can inherit from a parent dependency layer.\n\nThis is particularly useful in order to create a hierarchy of dependency layers\nwhere you can consume and inject dependencies from a parent container.\n\n.. code-block:: python\n\n    parent = siringa.Layer('parent')\n    child = siringa.Layer('child', parent)\n\n    # Register a sample dependency within parent\n    @parent.register\n    def mul2(x):\n        return x * 2\n\n    # Verify that the dependency is injectable from child layer\n    parent.is_injectable('mul2') # True\n    child.is_injectable('mul2') # True\n\n    @child.inject\n    def compute(x, mul: '!mul2'):\n        return mul(x)\n\n    compute(2) # =\u003e 2\n\nMocking dependencies\n^^^^^^^^^^^^^^^^^^^^\n\n``siringa`` allows you to define mocks for dependencies, which is particularly useful during testing:\n\n.. code-block:: python\n\n    @siringa.register\n    class DB(object):\n        def query(self, sql):\n            return ['john', 'mike']\n\n    @siringa.mock('DB')\n    class DBMock(object):\n        def query(self, sql):\n            return ['foo', 'bar']\n\n    @siringa.inject\n    def run(sql, db: '!DB'):\n        return db().query(sql)\n\n    # Test mock call\n    assert run('SELECT name FROM foo') == ['foo', 'bar']\n\n    # Once done, clear all the mocks\n    siringa.unregister_mock('DB')\n\n    # Or alternatively clear all the registed mocks within the container\n    siringa.clear_mocks()\n\n    # Test read call\n    assert run('SELECT name FROM foo') == ['john', 'mike']\n\n\n.. _Python: http://python.org\n.. _Hy: http://docs.hylang.org/en/latest/\n.. _`dependency injection`: https://en.wikipedia.org/wiki/Dependency_injection\n.. _`inversion of control`: https://en.wikipedia.org/wiki/Inversion_of_control\n.. _`documentation`: http://siringa.readthedocs.io\n.. _`examples`: http://siringa.readthedocs.io/en/latest/examples.html\n.. _`API`: http://siringa.readthedocs.io/en/latest/api.html\n.. _`programmatic API`: http://siringa.readthedocs.io/en/latest/api.html\n.. _`tutorial`: http://siringa.readthedocs.io/en/latest/index.html#tutorial\n.. _`PEP 3017`: https://www.python.org/dev/peps/pep-3107/\n.. _`PEP 0484`: https://www.python.org/dev/peps/pep-0484/\n\n.. |Build Status| image:: https://travis-ci.org/h2non/siringa.svg?branch=master\n   :target: https://travis-ci.org/h2non/siringa\n.. |PyPI| image:: https://img.shields.io/pypi/v/siringa.svg?maxAge=2592000?style=flat-square\n   :target: https://pypi.python.org/pypi/siringa\n.. |Coverage Status| image:: https://coveralls.io/repos/github/h2non/siringa/badge.svg?branch=master\n   :target: https://coveralls.io/github/h2non/siringa?branch=master\n.. |Documentation Status| image:: https://readthedocs.org/projects/siringa/badge/?version=latest\n   :target: http://siringa.readthedocs.io/en/latest/?badge=latest\n.. |Stability| image:: https://img.shields.io/pypi/status/siringa.svg\n   :target: https://pypi.python.org/pypi/siringa\n   :alt: Stability\n.. |Versions| image:: https://img.shields.io/pypi/pyversions/siringa.svg\n   :target: https://pypi.python.org/pypi/siringa\n   :alt: Python Versions\n.. |SayThanks| image:: https://img.shields.io/badge/Say%20Thanks!-%F0%9F%A6%89-1EAEDB.svg\n   :target: https://saythanks.io/to/h2non\n   :alt: Say Thanks\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fh2non%2Fsiringa","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fh2non%2Fsiringa","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fh2non%2Fsiringa/lists"}