registry-factory


Nameregistry-factory JSON
Version 0.1.1 PyPI version JSON
download
home_pagehttps://github.com/aidd-msca/registry-factory
SummaryAbstract codebase with utilities to register generic modules.
upload_time2023-02-01 10:20:06
maintainer
docs_urlNone
authorPeter Hartog
requires_python>=3.8,<4.0
licenseMIT
keywords registry factory codebase module registry accreditation system
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # RegistryFactory

![PyPI](https://img.shields.io/pypi/v/registry-factory)
![PyPI](https://img.shields.io/pypi/pyversions/registry-factory)
![PyPI](https://img.shields.io/github/license/aidd-msca/registry-factory)
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1jlyEd1yxhvFCN82YqEFI82q2n0k_y06F?usp=sharing)

An abstract implementation of the software design pattern called registry proposed in (Hartog et. al., 2023),
providing a factory for creating registries to which categorically similar modules can be organized.

**Content:**
**[Installation](#installation)**
| **[Dependencies](#dependencies)**
| **[Usage](#usage)**
| **[Citation](#citation)**
| **[Code of Conduct](#code-of-conduct)**

### Overview

The registry design patterns provides a way to organize modular
functionalities dynamically and achieve a unified, reusable, and interchangeable interface.
It extends the Factory design pattern without the explicit class dependency.
Additionally, the registry supports optional meta information such as versioning, accreditation,
testing, etc.
The UML diagrams show the differences between the factory and registry patterns.

<p align="center">
  <br>
  <img alt="UML diagram of the pattern" src="figures/registry_uml.png">
  <br>
<i>Created with BioRender.com</i>
 </p>

## Installation

The codebase can be installed from PyPI using `pip`, or your package manager of choice, with

```bash
$ pip install registry-factory
```

## Dependencies

No third-party dependencies are required to use the minimal functionality of the RegistryFactory.

## Usage

The workflow of creating a registry is the following. 1) Identify a part of the code that can be
separated from the rest. 2) Modularize the section to be independent of the rest of the code. 3)
Create a registry from the RegistryFactory. 4) Register any modules that provide similar
functionalities. 5) Call the optional module from the registry from the main workflow. See below.

<p align="center">
  <br>
  <img alt="Workflow" src="figures/registry_creation.png" width="750">
  <br>
<i>Created with BioRender.com</i>
 </p>

Further available options and use-cases are described in the following sections.

### A basic registry

A simple registry is created as such.

```Python
from registry_factory.factory import Factory

class Registries(Factory):
    TestRegistry = Factory.create_registry(shared=False)
```

Next, any models can be added to the ModelRegistry as such.

```Python
import torch.nn as nn

@Registries.ModelRegistry.register(call_name="simple_model")
class SimpleModel(nn.Module):
    ...
```

### Shared modules

A registry can be created to store shared modules. Shared modules are modules that are used in multiple registries (e.g. a model and a module).

```Python
from registry_factory.factory import Factory

class Registries(Factory):
    ModelRegistry = Factory.create_registry(shared=True)
    ModuleRegistry = Factory.create_registry(shared=True)

@Registries.ModelRegistry.register(call_name="encoder")
class Encoder(nn.Module):
    ...

Registries.ModuleRegistry.get("encoder")
```

### Arguments

A registry can be created to store modules with arguments. The arguments can be set when registering a module.

```Python
from registry_factory.factory import Factory

class Registries(Factory):
    ModelRegistry = Factory.create_registry(shared=True)

@Registries.ModelRegistry.register_arguments(key="simple_model")
@dataclass
class SimpleModelArguments:
    input_size: int
    output_size: int
```

Only dataclasses can be used as arguments.

### Versioning and accreditation

Two examples of additional meta information that can be stored in a registry is module versioning
and accreditation regarding how and to who credit should be attributed the module.

Versioning can be used to keep track of changes in a module. The version can be set when registering a module.

```Python
from registry_factory.factory import Factory
from registry_factory.checks.versioning import Versioning

class Registries(Factory):
    ModelRegistry = Factory.create_registry(checks=[Versioning(forced=False)])

@Registries.ModelRegistry.register(call_name="simple_model", version="1.0.0")
class SimpleModel(nn.Module):
    ...

Registries.ModelRegistry.get("simple_model") # Error, version not specified.
Registries.ModelRegistry.get("simple_model", version="1.0.0") # Returns the module.
```

Accreditation can be used to keep track of how and to who credit should be attributed the module.
The accreditation can be set when registering a module.

```Python
from registry_factory.factory import Factory
from registry_factory.checks.accreditation import Accreditation

class Registries(Factory):
    ModelRegistry = Factory.create_registry(checks=[Accreditation(forced=False)])

@Registries.ModelRegistry.register(
    call_name="simple_model",
    author="Author name",
    credit_type="reference",
    additional_information="Reference published work in (link)."
)
class SimpleModel(nn.Module):
    ...

Registries.ModelRegistry.get("simple_model")  # Returns the module.
Registries.ModelRegistry.get_info("simple_model")  # Returns all meta information including the accreditation information.
```

The reason why accreditation can return an object without specification is because the accreditation does not have "key" information. In the versioning module, the version is the key information which is used to grab the module from the registry. Without specifying the version, the registry will not know which module to return. In the accreditation module, the author, credit type, and additional information are not key information. Without specifying the author, credit type, and additional information, the registry will still know which module to return.

### Testing and Factory Patterns

We also provide defining tests and post checks applied to all modules in a registry. Define test
or post checks as follows when creating the registry.

```Python
class Pattern:
    """Test pattern."""

    def __init__(self):
        pass

    def hello_world(self):
        """Hello world."""
        print("Hello world")

class Registries(Factory):
    ModelRegistry = Factory.create_registry(
        shared=False, checks=[FactoryPattern(factory_pattern=Pattern, forced=False)]
    )

# No error, the module passes the test.
@ModelRegistry.register(
    call_name="hello_world"
)
class HelloWorld(Pattern):
    pass

# No error, the module passes the test.
@ModelRegistry.register(
    call_name="hello_world2"
)
class HelloWorld:
    def __init__(self):
        pass

    def hello_world(self):
        """Hello world."""
        print("Hello world")

# Error, the module does not pass the test.
@ModelRegistry.register(
    call_name="hello_world2"
)
class HelloWorld:
    def __init__(self):
        pass

    def goodday_world(self):
        """Good day world."""
        print("Good day world")
```

The factory also supports adding a callable test module to the registry. The callable test module can be specified to be called when a module is registered. The callable test module can be used to test the module when it is registered. The callable test module can be specified as follows when creating the registry.

```Python
class CallableTestModule:
    """Module to test."""

    def __init__(self, key: str, obj: Any, **kwargs):
        self.name = obj
        self.assert_name()

    def assert_name(self):
        assert self.name == "test", "Name is not test"



class Registries(Factory):
    ModelRegistry = Factory.create_registry(
        shared=False, checks=[Testing(test_module=CallableTestModule, forced=True)]
    )

Registries.ModelRegistry.register_prebuilt(key="name_test", obj="test") # No error, the module passes the test.
```

## Citation

Our paper in which we propose the registry design pattern, on which this package is built, is currently
available as a preprint. If you make use of the design pattern or this package please cite our work accordingly.

!!!!!! ADD PAPER LINK !!!!!!

<!-- ```
@inproceedings{hartog2023registry,
    title={Registry: a design pattern to promote code reuse in machine learning-based drug discovery},
    author={Hartog, Peter and Svensson, Emma and Mervin, Lewis and Genheden, Samuel and Engkvist, Ola and Tetko, Igor},
    year={2023},
    note={Preprint}
}
``` -->

### Funding

The work behind this package has received funding from the European Union’s Horizon 2020
research and innovation programme under the Marie Skłodowska-Curie
Actions, grant agreement “Advanced machine learning for Innovative Drug
Discovery (AIDD)” No 956832”. [Homepage](https://ai-dd.eu/).

![plot](figures/aidd.png)

## Code of Conduct

Everyone interacting in the codebase, issue trackers, chat rooms, and mailing lists is expected to follow the
[PyPA Code of Conduct](https://www.pypa.io/en/latest/code-of-conduct/).

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/aidd-msca/registry-factory",
    "name": "registry-factory",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8,<4.0",
    "maintainer_email": "",
    "keywords": "registry,factory,codebase,module registry,accreditation system",
    "author": "Peter Hartog",
    "author_email": "peter.hartog@hotmail.nl",
    "download_url": "https://files.pythonhosted.org/packages/c4/7c/0749eeb7c2e627bf8c960df8437f9b25ce3cc4b50bee78998179c584d658/registry_factory-0.1.1.tar.gz",
    "platform": null,
    "description": "# RegistryFactory\n\n![PyPI](https://img.shields.io/pypi/v/registry-factory)\n![PyPI](https://img.shields.io/pypi/pyversions/registry-factory)\n![PyPI](https://img.shields.io/github/license/aidd-msca/registry-factory)\n[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1jlyEd1yxhvFCN82YqEFI82q2n0k_y06F?usp=sharing)\n\nAn abstract implementation of the software design pattern called registry proposed in (Hartog et. al., 2023),\nproviding a factory for creating registries to which categorically similar modules can be organized.\n\n**Content:**\n**[Installation](#installation)**\n| **[Dependencies](#dependencies)**\n| **[Usage](#usage)**\n| **[Citation](#citation)**\n| **[Code of Conduct](#code-of-conduct)**\n\n### Overview\n\nThe registry design patterns provides a way to organize modular\nfunctionalities dynamically and achieve a unified, reusable, and interchangeable interface.\nIt extends the Factory design pattern without the explicit class dependency.\nAdditionally, the registry supports optional meta information such as versioning, accreditation,\ntesting, etc.\nThe UML diagrams show the differences between the factory and registry patterns.\n\n<p align=\"center\">\n  <br>\n  <img alt=\"UML diagram of the pattern\" src=\"figures/registry_uml.png\">\n  <br>\n<i>Created with BioRender.com</i>\n </p>\n\n## Installation\n\nThe codebase can be installed from PyPI using `pip`, or your package manager of choice, with\n\n```bash\n$ pip install registry-factory\n```\n\n## Dependencies\n\nNo third-party dependencies are required to use the minimal functionality of the RegistryFactory.\n\n## Usage\n\nThe workflow of creating a registry is the following. 1) Identify a part of the code that can be\nseparated from the rest. 2) Modularize the section to be independent of the rest of the code. 3)\nCreate a registry from the RegistryFactory. 4) Register any modules that provide similar\nfunctionalities. 5) Call the optional module from the registry from the main workflow. See below.\n\n<p align=\"center\">\n  <br>\n  <img alt=\"Workflow\" src=\"figures/registry_creation.png\" width=\"750\">\n  <br>\n<i>Created with BioRender.com</i>\n </p>\n\nFurther available options and use-cases are described in the following sections.\n\n### A basic registry\n\nA simple registry is created as such.\n\n```Python\nfrom registry_factory.factory import Factory\n\nclass Registries(Factory):\n    TestRegistry = Factory.create_registry(shared=False)\n```\n\nNext, any models can be added to the ModelRegistry as such.\n\n```Python\nimport torch.nn as nn\n\n@Registries.ModelRegistry.register(call_name=\"simple_model\")\nclass SimpleModel(nn.Module):\n    ...\n```\n\n### Shared modules\n\nA registry can be created to store shared modules. Shared modules are modules that are used in multiple registries (e.g. a model and a module).\n\n```Python\nfrom registry_factory.factory import Factory\n\nclass Registries(Factory):\n    ModelRegistry = Factory.create_registry(shared=True)\n    ModuleRegistry = Factory.create_registry(shared=True)\n\n@Registries.ModelRegistry.register(call_name=\"encoder\")\nclass Encoder(nn.Module):\n    ...\n\nRegistries.ModuleRegistry.get(\"encoder\")\n```\n\n### Arguments\n\nA registry can be created to store modules with arguments. The arguments can be set when registering a module.\n\n```Python\nfrom registry_factory.factory import Factory\n\nclass Registries(Factory):\n    ModelRegistry = Factory.create_registry(shared=True)\n\n@Registries.ModelRegistry.register_arguments(key=\"simple_model\")\n@dataclass\nclass SimpleModelArguments:\n    input_size: int\n    output_size: int\n```\n\nOnly dataclasses can be used as arguments.\n\n### Versioning and accreditation\n\nTwo examples of additional meta information that can be stored in a registry is module versioning\nand accreditation regarding how and to who credit should be attributed the module.\n\nVersioning can be used to keep track of changes in a module. The version can be set when registering a module.\n\n```Python\nfrom registry_factory.factory import Factory\nfrom registry_factory.checks.versioning import Versioning\n\nclass Registries(Factory):\n    ModelRegistry = Factory.create_registry(checks=[Versioning(forced=False)])\n\n@Registries.ModelRegistry.register(call_name=\"simple_model\", version=\"1.0.0\")\nclass SimpleModel(nn.Module):\n    ...\n\nRegistries.ModelRegistry.get(\"simple_model\") # Error, version not specified.\nRegistries.ModelRegistry.get(\"simple_model\", version=\"1.0.0\") # Returns the module.\n```\n\nAccreditation can be used to keep track of how and to who credit should be attributed the module.\nThe accreditation can be set when registering a module.\n\n```Python\nfrom registry_factory.factory import Factory\nfrom registry_factory.checks.accreditation import Accreditation\n\nclass Registries(Factory):\n    ModelRegistry = Factory.create_registry(checks=[Accreditation(forced=False)])\n\n@Registries.ModelRegistry.register(\n    call_name=\"simple_model\",\n    author=\"Author name\",\n    credit_type=\"reference\",\n    additional_information=\"Reference published work in (link).\"\n)\nclass SimpleModel(nn.Module):\n    ...\n\nRegistries.ModelRegistry.get(\"simple_model\")  # Returns the module.\nRegistries.ModelRegistry.get_info(\"simple_model\")  # Returns all meta information including the accreditation information.\n```\n\nThe reason why accreditation can return an object without specification is because the accreditation does not have \"key\" information. In the versioning module, the version is the key information which is used to grab the module from the registry. Without specifying the version, the registry will not know which module to return. In the accreditation module, the author, credit type, and additional information are not key information. Without specifying the author, credit type, and additional information, the registry will still know which module to return.\n\n### Testing and Factory Patterns\n\nWe also provide defining tests and post checks applied to all modules in a registry. Define test\nor post checks as follows when creating the registry.\n\n```Python\nclass Pattern:\n    \"\"\"Test pattern.\"\"\"\n\n    def __init__(self):\n        pass\n\n    def hello_world(self):\n        \"\"\"Hello world.\"\"\"\n        print(\"Hello world\")\n\nclass Registries(Factory):\n    ModelRegistry = Factory.create_registry(\n        shared=False, checks=[FactoryPattern(factory_pattern=Pattern, forced=False)]\n    )\n\n# No error, the module passes the test.\n@ModelRegistry.register(\n    call_name=\"hello_world\"\n)\nclass HelloWorld(Pattern):\n    pass\n\n# No error, the module passes the test.\n@ModelRegistry.register(\n    call_name=\"hello_world2\"\n)\nclass HelloWorld:\n    def __init__(self):\n        pass\n\n    def hello_world(self):\n        \"\"\"Hello world.\"\"\"\n        print(\"Hello world\")\n\n# Error, the module does not pass the test.\n@ModelRegistry.register(\n    call_name=\"hello_world2\"\n)\nclass HelloWorld:\n    def __init__(self):\n        pass\n\n    def goodday_world(self):\n        \"\"\"Good day world.\"\"\"\n        print(\"Good day world\")\n```\n\nThe factory also supports adding a callable test module to the registry. The callable test module can be specified to be called when a module is registered. The callable test module can be used to test the module when it is registered. The callable test module can be specified as follows when creating the registry.\n\n```Python\nclass CallableTestModule:\n    \"\"\"Module to test.\"\"\"\n\n    def __init__(self, key: str, obj: Any, **kwargs):\n        self.name = obj\n        self.assert_name()\n\n    def assert_name(self):\n        assert self.name == \"test\", \"Name is not test\"\n\n\n\nclass Registries(Factory):\n    ModelRegistry = Factory.create_registry(\n        shared=False, checks=[Testing(test_module=CallableTestModule, forced=True)]\n    )\n\nRegistries.ModelRegistry.register_prebuilt(key=\"name_test\", obj=\"test\") # No error, the module passes the test.\n```\n\n## Citation\n\nOur paper in which we propose the registry design pattern, on which this package is built, is currently\navailable as a preprint. If you make use of the design pattern or this package please cite our work accordingly.\n\n!!!!!! ADD PAPER LINK !!!!!!\n\n<!-- ```\n@inproceedings{hartog2023registry,\n    title={Registry: a design pattern to promote code reuse in machine learning-based drug discovery},\n    author={Hartog, Peter and Svensson, Emma and Mervin, Lewis and Genheden, Samuel and Engkvist, Ola and Tetko, Igor},\n    year={2023},\n    note={Preprint}\n}\n``` -->\n\n### Funding\n\nThe work behind this package has received funding from the European Union\u2019s Horizon 2020\nresearch and innovation programme under the Marie Sk\u0142odowska-Curie\nActions, grant agreement \u201cAdvanced machine learning for Innovative Drug\nDiscovery (AIDD)\u201d No 956832\u201d. [Homepage](https://ai-dd.eu/).\n\n![plot](figures/aidd.png)\n\n## Code of Conduct\n\nEveryone interacting in the codebase, issue trackers, chat rooms, and mailing lists is expected to follow the\n[PyPA Code of Conduct](https://www.pypa.io/en/latest/code-of-conduct/).\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Abstract codebase with utilities to register generic modules.",
    "version": "0.1.1",
    "split_keywords": [
        "registry",
        "factory",
        "codebase",
        "module registry",
        "accreditation system"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "cac0dbedd6482e8472473d3f7cd556370e1bc434ddc412bdf83b7558c4e2c1b3",
                "md5": "c46be933bb6f4ff71517e44aaa6d516e",
                "sha256": "e5d2d2da86b762fc18310e161a2a561551f82c49ff9263b56244f4d2d68f3a58"
            },
            "downloads": -1,
            "filename": "registry_factory-0.1.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "c46be933bb6f4ff71517e44aaa6d516e",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8,<4.0",
            "size": 17830,
            "upload_time": "2023-02-01T10:20:05",
            "upload_time_iso_8601": "2023-02-01T10:20:05.102676Z",
            "url": "https://files.pythonhosted.org/packages/ca/c0/dbedd6482e8472473d3f7cd556370e1bc434ddc412bdf83b7558c4e2c1b3/registry_factory-0.1.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "c47c0749eeb7c2e627bf8c960df8437f9b25ce3cc4b50bee78998179c584d658",
                "md5": "02733a5325cc3a35ca5c784e5ea00c38",
                "sha256": "e202fb9a6ddd1c568a6ad10528a2f6588d157dfb75ade78f738e2257229c14bd"
            },
            "downloads": -1,
            "filename": "registry_factory-0.1.1.tar.gz",
            "has_sig": false,
            "md5_digest": "02733a5325cc3a35ca5c784e5ea00c38",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8,<4.0",
            "size": 15369,
            "upload_time": "2023-02-01T10:20:06",
            "upload_time_iso_8601": "2023-02-01T10:20:06.489577Z",
            "url": "https://files.pythonhosted.org/packages/c4/7c/0749eeb7c2e627bf8c960df8437f9b25ce3cc4b50bee78998179c584d658/registry_factory-0.1.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-02-01 10:20:06",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "github_user": "aidd-msca",
    "github_project": "registry-factory",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "registry-factory"
}
        
Elapsed time: 0.03646s