{"id":47840667,"url":"https://github.com/aidd-msca/registry-factory","last_synced_at":"2026-04-03T20:38:40.374Z","repository":{"id":62593125,"uuid":"553026371","full_name":"aidd-msca/registry-factory","owner":"aidd-msca","description":"An abstract implementation of the registry design pattern proposed in (Hartog et. al., 2023). Provides a factory for registries that dynamically organize modular functionalities.","archived":false,"fork":false,"pushed_at":"2024-06-10T14:27:57.000Z","size":1910,"stargazers_count":11,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-01T04:11:07.147Z","etag":null,"topics":["code-reuse","design-pattern","drug-discovery","machine-learning","modularity","registration"],"latest_commit_sha":null,"homepage":"","language":"Python","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/aidd-msca.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE-OF-CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-10-17T15:42:10.000Z","updated_at":"2025-07-08T15:33:25.000Z","dependencies_parsed_at":"2024-06-09T10:47:02.998Z","dependency_job_id":null,"html_url":"https://github.com/aidd-msca/registry-factory","commit_stats":{"total_commits":45,"total_committers":3,"mean_commits":15.0,"dds":0.4666666666666667,"last_synced_commit":"7d80c8901d3941e39c7daff9b95e19b586f7af66"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/aidd-msca/registry-factory","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aidd-msca%2Fregistry-factory","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aidd-msca%2Fregistry-factory/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aidd-msca%2Fregistry-factory/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aidd-msca%2Fregistry-factory/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aidd-msca","download_url":"https://codeload.github.com/aidd-msca/registry-factory/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aidd-msca%2Fregistry-factory/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31375774,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-03T17:53:18.093Z","status":"ssl_error","status_checked_at":"2026-04-03T17:53:17.617Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["code-reuse","design-pattern","drug-discovery","machine-learning","modularity","registration"],"created_at":"2026-04-03T20:38:39.316Z","updated_at":"2026-04-03T20:38:40.340Z","avatar_url":"https://github.com/aidd-msca.png","language":"Python","readme":"# RegistryFactory\n\n[![PyPI version](https://badge.fury.io/py/registry-factory.svg)](https://badge.fury.io/py/registry-factory)\n[![PyPI](https://img.shields.io/github/license/aidd-msca/registry-factory)](LICENSE)\n![PyPI](https://img.shields.io/pypi/pyversions/registry-factory)\n[![GitHub Repo stars](https://img.shields.io/github/stars/aidd-msca/registry-factory)](https://github.com/aidd-msca/registry-factory/stargazers)\n\nAn abstract implementation of the software design pattern called Registry proposed by Hartog and Svensson et. al. (2024),\nproviding a factory for creating registries to organize categorically similar modules.\n\n**[Installation](#installation)**\n| **[Dependencies](#dependencies)**\n| **[Usage](#usage)**\n| **[Citation](#citation)**\n\n### Overview\n\nThe registry design pattern 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\u003cp align=\"center\"\u003e\n  \u003cbr\u003e\n  \u003cimg alt=\"UML diagram of the pattern\" src=\"figures/registry_uml.png\"\u003e\n  \u003cbr\u003e\n\u003ci\u003eCreated with BioRender.com\u003c/i\u003e\n \u003c/p\u003e\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\nOr from a local clone, with\n\n```bash\n$ conda env create -f env-dev.yaml\n$ conda activate registry_env\n$ poetry install\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\u003cp align=\"center\"\u003e\n  \u003cbr\u003e\n  \u003cimg alt=\"Workflow\" src=\"figures/registry_creation.png\" width=\"750\"\u003e\n  \u003cbr\u003e\n\u003ci\u003eCreated with BioRender.com\u003c/i\u003e\n \u003c/p\u003e\n\nAdditional available options and use cases are described in the following sections. See also [examples](examples).\n\n\u003c!-- ### A basic registry --\u003e\n\u003cdetails\u003e\n\u003csummary\u003e A basic registry \u003c/summary\u003e\nA simple registry is created as such.\n\n```Python\nfrom registry_factory.registry import Registry\n```\n\nNext, any models can be added to the registry as such.\n\n```Python\nimport torch.nn as nn\n\n@Registry.register(\"simple_model\")\nclass SimpleModel(nn.Module):\n    ...\n```\n\n\u003c/details\u003e\n\n\u003c!-- ### Shared modules --\u003e\n\u003cdetails\u003e\n\u003csummary\u003e Shared modules \u003c/summary\u003e\nTo specify specific registries and have them share modules, we use the Factory class. 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(\"model_registry\", shared=True)\n    ModuleRegistry = Factory.create_registry(\"module_registry\", shared=True)\n\n@Registries.ModelRegistry.register(\"encoder\")\nclass Encoder(nn.Module):\n    ...\n\nRegistries.ModuleRegistry.get(\"encoder\")\n```\n\n\u003c/details\u003e\n\n\u003c!-- ### Arguments --\u003e\n\u003cdetails\u003e\n\u003csummary\u003e Arguments \u003c/summary\u003e\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\nfrom dataclasses import dataclass\n\n\nclass Registries(Factory):\n    ModelRegistry = Factory.create_registry(\"model_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 for now.\n\n\u003c/details\u003e\n\n\u003c!-- ### Versioning and accreditation --\u003e\n\u003cdetails\u003e\n\u003csummary\u003e Versioning and accreditation \u003c/summary\u003e\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 whom credit should be attributed for a given 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(\"model_registry\", checks=[Accreditation(forced=False)])\n\n@Registries.ModelRegistry.register(\n    key=\"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 the accreditation system can return an object without specification is because the accreditation system lacks \"key\" information.\nIn the versioning module, the version is the key information that is used to grab the module from the registry.\nWithout specifying the version the registry will not know which module to return.\nTherefore, the author, credit type, and additional information are not key information in the accreditation system.\nWithout specifying the author, credit type, and additional information, the registry will still know which module to return.\n\n\u003c/details\u003e\n\n\u003c!-- ### Testing and Factory Patterns --\u003e\n\u003cdetails\u003e\n\u003csummary\u003e Testing and Factory Patterns \u003c/summary\u003e\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\nfrom registry_factory.factory import Factory\nfrom registry_factory.checks.factory_pattern import FactoryPattern\n\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        \"model_registry\", shared=False, checks=[FactoryPattern(factory_pattern=Pattern, forced=False)]\n    )\n\n# No error, the module passes the test.\n@ModelRegistry.register(key=\"hello_world\")\nclass HelloWorld(Pattern):\n    pass\n\n# No error, the module passes the test.\n@ModelRegistry.register(key=\"hello_world2\")\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(key=\"hello_world2\")\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\nfrom typing import Any\nfrom registry_factory.factory import Factory\nfrom registry_factory.checks.testing import Testing\n\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        \"model_registry\", 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.\nRegistries.ModelRegistry.register_prebuilt(key=\"name_test\", obj=\"not_test\") # Error, the module doesn't pass the test.\n```\n\n\u003c/details\u003e\n\n\u003c!-- ### Inserting hooks --\u003e\n\u003cdetails\u003e\n\u003csummary\u003e Hooks insertions \u003c/summary\u003e\n\nHere we outline the use of registries in code to create hooks for outside users. The example given below contains a function unaccessible by users that have two options.\n\n```Python\nfrom registry_factory.registry import Registry\n\n@Registry.register(\"option_1\")\ndef option_1() -\u003e int:\n    return 1\n\n@Registry.register(\"option_3\")\ndef option_3() -\u003e int:\n    return 3\n\ndef _some_hidden_function(a: str) -\u003e int:\n    try:\n        return print(Registry.get(f\"option_{a}\")())\n    except Exception as e:\n        raise RuntimeError(\"Error getting the option\", e)\n```\n\nWhen a new users uses this code and selects option two, it will cause an error as it has not yet been implemented.\n\n```Python\n_some_hidden_function(1) # Returns 1\n_some_hidden_function(3) # Returns 3\n_some_hidden_function(2) # Error\n```\n\nNormally, this would be the end, but with registries, the user can easily create a new function that will solve the issue.\n\n```Python\n@Registry.register(\"option_2\") # External user adds new option\ndef option_2() -\u003e int:\n    return 2\n\n_some_hidden_function(2) # Returns 2\n```\n\n\u003c/details\u003e\n\n\u003c!-- ### Compatibility wrapper --\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e Compatibility wrapper \u003c/summary\u003e\n\nAnother example of how to use registries, is to make two incompatible functions work through wrappers. Users can specify specific wrappers for functions and register them using the registry.\n\n```Python\nfrom registry_factory.factory import Factory\n\nclass Registries(Factory):\n    ModelRegistry = Factory.create_registry(name=\"model_registry\")\n\ndef func1():\n    return \"hello world\"\n\ndef func2():\n    return [\"hello universe\"]\n\ndef final_function(key: str) -\u003e str:\n    return Registries.ModelRegistry.get(key)()\n```\n\nHere the example will output the wrong versions if the objects are registered as is: one a string the other a list. You can easily use wrapper functions to register the objects in such a way that they output the correct types and become compatible.\n\n```Python\n# External user creates wrapper function to make both functions work with final function\ndef wrapper_function(func):\n    def wrapper(*args, **kwargs):\n        out = func(*args, **kwargs)\n        if type(out) is list:\n            return out[0]\n        else:\n            return out\n    return wrapper\n\nRegistries.ModelRegistry.register_prebuilt(wrapper_function(func1), \"world\")\nRegistries.ModelRegistry.register_prebuilt(wrapper_function(func2), \"universe\")\n\nprint(final_function(\"world\")) # -\u003e Hello world\nprint(final_function(\"universe\")) # -\u003e Hello universe\n```\n\n\u003c/details\u003e\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 use the design pattern or this package please cite our work accordingly.\n\n[paper link]\n\n\u003c!-- ```\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``` --\u003e\n\n### Funding\n\nThe work behind this package has received funding from the European Union’s Horizon 2020\nresearch and innovation programme under the Marie Skłodowska-Curie\nActions, grant agreement “Advanced machine learning for Innovative Drug\nDiscovery (AIDD)” No 956832”. [Homepage](https://ai-dd.eu/).\n\n![plot](figures/aidd.png)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faidd-msca%2Fregistry-factory","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faidd-msca%2Fregistry-factory","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faidd-msca%2Fregistry-factory/lists"}