rats-apps


Namerats-apps JSON
Version 0.13.2 PyPI version JSON
download
home_pageNone
Summaryresearch analysis tools for building applications
upload_time2025-07-14 20:00:00
maintainerNone
docs_urlNone
authorElon Portugaly
requires_python<4.0,>=3.10
licenseMIT
keywords pipelines machine learning research
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ---
title: introduction
---

The `rats-apps` package helps create applications; a small set of modules that eliminate the most
common boilerplate code when creating applications of any kind. We do this mainly by providing a
set of libraries to define service containers, and using the service containers to hide the
complexity of creating services–like authentication, storage, or database clients–from other parts
of the system, allowing developers to focus on the business logic of the application. Often referred
to as [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection), we use our
service containers to separate the concerns of constructing objects and using them.

## Installation

Use `pip` or your favorite packaging tool to install the package from PyPI.

```bash
pip install rats-apps
```

## What is an application?

Let's start with a standard python script, and slowly migrate it to be a rats application in order
to explain a few main details. Creating a `__main__.py` file in a package makes it executable.
After the common python boilerplate code is added, this is typically what we find in the wild:

=== ":material-language-python: ~/code/src/foo/\_\_main\_\_.py"
    ```python
    def main() -> None:
        print("hello, world")


    if __name__ == "__main__":
        main()
    ```

=== ":material-console: ~/code"
    ```bash
    python -m foo
    ```

Using this pattern, we can define our application with a small modification, turning our `main()`
function into a runnable rats application, and then running it:

=== ":material-language-python: ~/code/src/foo/\_\_main\_\_.py"
    ```python
    from rats import apps


    def main() -> None:
        print("hello, world")


    if __name__ == "__main__":
        apps.run(apps.App(main))
    ```

!!! info
    The [rats.apps.App][] class is used to quickly turn a script entry point–`Callable[[], None]`
    –into an object with an `execute()` method, defined by our [rats.apps.Executable][]
    interface.

## Application Containers & Plugins

We can start to leverage [rats.apps][] to make our application easy to extend. Let's replace our use
of the [rats.apps.App][] wrapper and move our `main()` function into a class. Using the
[rats.apps.AppContainer][] and [rats.apps.PluginMixin][] classes, we finish adapting our example
to be a rats application.

=== ":material-language-python: ~/code/src/foo/\_\_main\_\_.py"

    ```python
        from rats import apps


        class Application(apps.AppContainer, apps.PluginMixin):
            def execute() -> None:
                print("hello, world")


        if __name__ == "__main__":
            apps.run_plugin(Application)
    ```

!!! info
    If you're making these changes to an existing code base, you should be able to run things to
    validate that everything still works as it did before. Your `main()` function is now in
    the `execute()` method of your new `Application` class, but none of the behavior of your
    application should have been affected.

Now that we have a fully defined rats application; we can use [rats.apps.Container][] instances to
make services available to our application while remaining decoupled from the details of how these
services are initialized. A common use case for this is to give our team access to the azure
storage clients without needing to specify the authentication details; allowing us to ensure our
application is functional in many compute environments.

=== ":material-language-python: ~/code/src/foo/\_\_init\_\_.py"

    ```python
        from ._plugin import PluginContainer

        __all__ = ["PluginContainer"]
    ```

=== ":material-language-python: ~/code/src/foo/\_\_main\_\_.py"

    ```python
        from rats import apps
        import foo


        class Application(apps.AppContainer, apps.PluginMixin):

            def execute() -> None:
                blob_client = self._app.get(foo.BLOB_SERVICE_ID)
                print(f"hello, world. loaded blob client: {blob_client}")

            @apps.container()
            def _plugins(self) -> apps.Container:
                return foo.PluginContainer(self._app)


        if __name__ == "__main__":
            apps.run_plugin(Application)
    ```

=== ":material-language-python: ~/code/src/foo/_plugin.py"

    ```python
        from rats import apps
        from azure.storage.blob import BlobServiceClient

        BLOB_SERVICE_ID = apps.ServiceId[BlobServiceClient]("blob-client")


        class PluginContainer(apps.Container, apps.PluginMixin):

            @apps.service(BLOB_SERVICE_ID)
            def _blob_client(self) -> BlobServiceClient:
                credential = DefaultAzureCredential()
                return BlobServiceClient(
                    account_url=f"https://example.blob.core.windows.net/",
                    credential=credential,
                )
    ```

!!! success
    Following these patterns, we can make services available to others with plugin containers; and
    we can combine these containers to create applications. The [rats.apps][] module has additional
    libraries to help define different types of applications, designed to help your solutions
    evolve as ideas mature. Our first runnable example was a single instance of the
    [rats.apps.AppContainer][] interface with an `execute()` method; but a larger project might
    have a few modules providing different aspects of the application's needs by sharing a set of
    service ids and a plugin container.

## Installable Plugins

We use the standard [Entry Points](https://packaging.python.org/en/latest/specifications/entry-points/)
mechanism to allow authors to make their application extensible. These plugins are instances of
[rats.apps.Container][] and loaded into applications like our previous examples.

=== ":material-file-settings: ~/code/pyproject.toml"
    ```toml
    [tool.poetry.plugins."foo.plugins"]
    "foo" = "foo:PluginContainer"
    ```

=== ":material-language-python: ~/code/src/foo/\_\_main\_\_.py"
    ```python
        from rats import apps
        import foo


        class Application(apps.AppContainer, apps.PluginMixin):

            def execute() -> None:
                blob_client = self._app.get(foo.BLOB_SERVICE_ID)
                print(f"hello, world. loaded blob client: {blob_client}")

            @apps.container()
            def _plugins(self) -> apps.Container:
                return apps.PythonEntryPointContainer("foo.plugins")


        if __name__ == "__main__":
            apps.run_plugin(Application)
    ```

!!! success
    We used [rats.app] to define an application, and used a service provided by a plugin container
    that is loaded through a python entry point. You can create a script entry in your
    `pyproject.toml` file to expose your application through a terminal command:

    === ":material-language-python: ~/code/src/foo/\_\_init\_\_.py"
        ```python
            from ._app import Application, main
            from ._plugin import PluginContainer


            __all__ = [
                "Application",
                "main",
                "PluginContainer",
            ]
        ```
    === ":material-file-settings: ~/code/pyproject.toml"
        ```toml
            [tool.poetry.plugins."foo.plugins"]
            "foo" = "foo:PluginContainer"

            [tool.poetry.scripts]
            foo = "foo:main"
        ```


            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "rats-apps",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.10",
    "maintainer_email": null,
    "keywords": "pipelines, machine learning, research",
    "author": "Elon Portugaly",
    "author_email": "elonp@microsoft.com",
    "download_url": null,
    "platform": null,
    "description": "---\ntitle: introduction\n---\n\nThe `rats-apps` package helps create applications; a small set of modules that eliminate the most\ncommon boilerplate code when creating applications of any kind. We do this mainly by providing a\nset of libraries to define service containers, and using the service containers to hide the\ncomplexity of creating services\u2013like authentication, storage, or database clients\u2013from other parts\nof the system, allowing developers to focus on the business logic of the application. Often referred\nto as [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection), we use our\nservice containers to separate the concerns of constructing objects and using them.\n\n## Installation\n\nUse `pip` or your favorite packaging tool to install the package from PyPI.\n\n```bash\npip install rats-apps\n```\n\n## What is an application?\n\nLet's start with a standard python script, and slowly migrate it to be a rats application in order\nto explain a few main details. Creating a `__main__.py` file in a package makes it executable.\nAfter the common python boilerplate code is added, this is typically what we find in the wild:\n\n=== \":material-language-python: ~/code/src/foo/\\_\\_main\\_\\_.py\"\n    ```python\n    def main() -> None:\n        print(\"hello, world\")\n\n\n    if __name__ == \"__main__\":\n        main()\n    ```\n\n=== \":material-console: ~/code\"\n    ```bash\n    python -m foo\n    ```\n\nUsing this pattern, we can define our application with a small modification, turning our `main()`\nfunction into a runnable rats application, and then running it:\n\n=== \":material-language-python: ~/code/src/foo/\\_\\_main\\_\\_.py\"\n    ```python\n    from rats import apps\n\n\n    def main() -> None:\n        print(\"hello, world\")\n\n\n    if __name__ == \"__main__\":\n        apps.run(apps.App(main))\n    ```\n\n!!! info\n    The [rats.apps.App][] class is used to quickly turn a script entry point\u2013`Callable[[], None]`\n    \u2013into an object with an `execute()` method, defined by our [rats.apps.Executable][]\n    interface.\n\n## Application Containers & Plugins\n\nWe can start to leverage [rats.apps][] to make our application easy to extend. Let's replace our use\nof the [rats.apps.App][] wrapper and move our `main()` function into a class. Using the\n[rats.apps.AppContainer][] and [rats.apps.PluginMixin][] classes, we finish adapting our example\nto be a rats application.\n\n=== \":material-language-python: ~/code/src/foo/\\_\\_main\\_\\_.py\"\n\n    ```python\n        from rats import apps\n\n\n        class Application(apps.AppContainer, apps.PluginMixin):\n            def execute() -> None:\n                print(\"hello, world\")\n\n\n        if __name__ == \"__main__\":\n            apps.run_plugin(Application)\n    ```\n\n!!! info\n    If you're making these changes to an existing code base, you should be able to run things to\n    validate that everything still works as it did before. Your `main()` function is now in\n    the `execute()` method of your new `Application` class, but none of the behavior of your\n    application should have been affected.\n\nNow that we have a fully defined rats application; we can use [rats.apps.Container][] instances to\nmake services available to our application while remaining decoupled from the details of how these\nservices are initialized. A common use case for this is to give our team access to the azure\nstorage clients without needing to specify the authentication details; allowing us to ensure our\napplication is functional in many compute environments.\n\n=== \":material-language-python: ~/code/src/foo/\\_\\_init\\_\\_.py\"\n\n    ```python\n        from ._plugin import PluginContainer\n\n        __all__ = [\"PluginContainer\"]\n    ```\n\n=== \":material-language-python: ~/code/src/foo/\\_\\_main\\_\\_.py\"\n\n    ```python\n        from rats import apps\n        import foo\n\n\n        class Application(apps.AppContainer, apps.PluginMixin):\n\n            def execute() -> None:\n                blob_client = self._app.get(foo.BLOB_SERVICE_ID)\n                print(f\"hello, world. loaded blob client: {blob_client}\")\n\n            @apps.container()\n            def _plugins(self) -> apps.Container:\n                return foo.PluginContainer(self._app)\n\n\n        if __name__ == \"__main__\":\n            apps.run_plugin(Application)\n    ```\n\n=== \":material-language-python: ~/code/src/foo/_plugin.py\"\n\n    ```python\n        from rats import apps\n        from azure.storage.blob import BlobServiceClient\n\n        BLOB_SERVICE_ID = apps.ServiceId[BlobServiceClient](\"blob-client\")\n\n\n        class PluginContainer(apps.Container, apps.PluginMixin):\n\n            @apps.service(BLOB_SERVICE_ID)\n            def _blob_client(self) -> BlobServiceClient:\n                credential = DefaultAzureCredential()\n                return BlobServiceClient(\n                    account_url=f\"https://example.blob.core.windows.net/\",\n                    credential=credential,\n                )\n    ```\n\n!!! success\n    Following these patterns, we can make services available to others with plugin containers; and\n    we can combine these containers to create applications. The [rats.apps][] module has additional\n    libraries to help define different types of applications, designed to help your solutions\n    evolve as ideas mature. Our first runnable example was a single instance of the\n    [rats.apps.AppContainer][] interface with an `execute()` method; but a larger project might\n    have a few modules providing different aspects of the application's needs by sharing a set of\n    service ids and a plugin container.\n\n## Installable Plugins\n\nWe use the standard [Entry Points](https://packaging.python.org/en/latest/specifications/entry-points/)\nmechanism to allow authors to make their application extensible. These plugins are instances of\n[rats.apps.Container][] and loaded into applications like our previous examples.\n\n=== \":material-file-settings: ~/code/pyproject.toml\"\n    ```toml\n    [tool.poetry.plugins.\"foo.plugins\"]\n    \"foo\" = \"foo:PluginContainer\"\n    ```\n\n=== \":material-language-python: ~/code/src/foo/\\_\\_main\\_\\_.py\"\n    ```python\n        from rats import apps\n        import foo\n\n\n        class Application(apps.AppContainer, apps.PluginMixin):\n\n            def execute() -> None:\n                blob_client = self._app.get(foo.BLOB_SERVICE_ID)\n                print(f\"hello, world. loaded blob client: {blob_client}\")\n\n            @apps.container()\n            def _plugins(self) -> apps.Container:\n                return apps.PythonEntryPointContainer(\"foo.plugins\")\n\n\n        if __name__ == \"__main__\":\n            apps.run_plugin(Application)\n    ```\n\n!!! success\n    We used [rats.app] to define an application, and used a service provided by a plugin container\n    that is loaded through a python entry point. You can create a script entry in your\n    `pyproject.toml` file to expose your application through a terminal command:\n\n    === \":material-language-python: ~/code/src/foo/\\_\\_init\\_\\_.py\"\n        ```python\n            from ._app import Application, main\n            from ._plugin import PluginContainer\n\n\n            __all__ = [\n                \"Application\",\n                \"main\",\n                \"PluginContainer\",\n            ]\n        ```\n    === \":material-file-settings: ~/code/pyproject.toml\"\n        ```toml\n            [tool.poetry.plugins.\"foo.plugins\"]\n            \"foo\" = \"foo:PluginContainer\"\n\n            [tool.poetry.scripts]\n            foo = \"foo:main\"\n        ```\n\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "research analysis tools for building applications",
    "version": "0.13.2",
    "project_urls": {
        "Documentation": "https://microsoft.github.io/rats",
        "Repository": "https://github.com/microsoft/rats"
    },
    "split_keywords": [
        "pipelines",
        " machine learning",
        " research"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "1a63a6d50350096f59df2a405da1e112944d333c1fbd6d90baaef7f7a9352484",
                "md5": "f1cb1c3afdf8d17b75be363444657a56",
                "sha256": "f6c7a02038cacbbc0b6ab12e205acbdcf68c31b6ca15177917e72e41e47abdcd"
            },
            "downloads": -1,
            "filename": "rats_apps-0.13.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "f1cb1c3afdf8d17b75be363444657a56",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.10",
            "size": 45184,
            "upload_time": "2025-07-14T20:00:00",
            "upload_time_iso_8601": "2025-07-14T20:00:00.901087Z",
            "url": "https://files.pythonhosted.org/packages/1a/63/a6d50350096f59df2a405da1e112944d333c1fbd6d90baaef7f7a9352484/rats_apps-0.13.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-14 20:00:00",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "microsoft",
    "github_project": "rats",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "rats-apps"
}
        
Elapsed time: 0.45614s