{"action":"created","issue":{"url":"https://api.github.com/repos/pcah/python-clean-architecture/issues/9","repository_url":"https://api.github.com/repos/pcah/python-clean-architecture","labels_url":"https://api.github.com/repos/pcah/python-clean-architecture/issues/9/labels{/name}","comments_url":"https://api.github.com/repos/pcah/python-clean-architecture/issues/9/comments","events_url":"https://api.github.com/repos/pcah/python-clean-architecture/issues/9/events","html_url":"https://github.com/pcah/python-clean-architecture/issues/9","id":381917098,"node_id":"MDU6SXNzdWUzODE5MTcwOTg=","number":9,"title":"Dependency Injection: Scopes","user":{"login":"lhaze","id":2180285,"node_id":"MDQ6VXNlcjIxODAyODU=","avatar_url":"https://avatars0.githubusercontent.com/u/2180285?v=4","gravatar_id":"","url":"https://api.github.com/users/lhaze","html_url":"https://github.com/lhaze","followers_url":"https://api.github.com/users/lhaze/followers","following_url":"https://api.github.com/users/lhaze/following{/other_user}","gists_url":"https://api.github.com/users/lhaze/gists{/gist_id}","starred_url":"https://api.github.com/users/lhaze/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lhaze/subscriptions","organizations_url":"https://api.github.com/users/lhaze/orgs","repos_url":"https://api.github.com/users/lhaze/repos","events_url":"https://api.github.com/users/lhaze/events{/privacy}","received_events_url":"https://api.github.com/users/lhaze/received_events","type":"User","site_admin":false},"labels":[{"id":125257027,"node_id":"MDU6TGFiZWwxMjUyNTcwMjc=","url":"https://api.github.com/repos/pcah/python-clean-architecture/labels/enhancement","name":"enhancement","color":"84b6eb","default":true},{"id":125257028,"node_id":"MDU6TGFiZWwxMjUyNTcwMjg=","url":"https://api.github.com/repos/pcah/python-clean-architecture/labels/help%20wanted","name":"help wanted","color":"6aea6d","default":true},{"id":1131298774,"node_id":"MDU6TGFiZWwxMTMxMjk4Nzc0","url":"https://api.github.com/repos/pcah/python-clean-architecture/labels/important","name":"important","color":"ef7b96","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":{"url":"https://api.github.com/repos/pcah/python-clean-architecture/milestones/3","html_url":"https://github.com/pcah/python-clean-architecture/milestone/3","labels_url":"https://api.github.com/repos/pcah/python-clean-architecture/milestones/3/labels","id":3823237,"node_id":"MDk6TWlsZXN0b25lMzgyMzIzNw==","number":3,"title":"0.1","description":"Alpha release.","creator":{"login":"lhaze","id":2180285,"node_id":"MDQ6VXNlcjIxODAyODU=","avatar_url":"https://avatars0.githubusercontent.com/u/2180285?v=4","gravatar_id":"","url":"https://api.github.com/users/lhaze","html_url":"https://github.com/lhaze","followers_url":"https://api.github.com/users/lhaze/followers","following_url":"https://api.github.com/users/lhaze/following{/other_user}","gists_url":"https://api.github.com/users/lhaze/gists{/gist_id}","starred_url":"https://api.github.com/users/lhaze/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lhaze/subscriptions","organizations_url":"https://api.github.com/users/lhaze/orgs","repos_url":"https://api.github.com/users/lhaze/repos","events_url":"https://api.github.com/users/lhaze/events{/privacy}","received_events_url":"https://api.github.com/users/lhaze/received_events","type":"User","site_admin":false},"open_issues":11,"closed_issues":7,"state":"open","created_at":"2018-11-15T00:57:04Z","updated_at":"2018-11-28T10:37:31Z","due_on":null,"closed_at":null},"comments":1,"created_at":"2018-11-18T01:03:44Z","updated_at":"2018-11-28T10:57:51Z","closed_at":null,"author_association":"COLLABORATOR","body":"Follows #1 \r\n\r\nWhen a constructor is registered in the DI Container, registrar can define a scope of the instance created by the constructor. I think of them as an Enum of objects (*functions? objects with state?*), that know when they want to create a new instance and when to return the old one.\r\n\r\nI think of them as something like the code below:\r\n\r\n```python\r\nfrom enum import Enum\r\nimport typing as t\r\n\r\nfrom pca.utils.functools import reify\r\n\r\n\r\nclass Container:\r\n\r\n    _singleton_objects = {}\r\n\r\n    @reify\r\n    def _thread_local(self):\r\n        from threading import local\r\n        return local()\r\n\r\n    def find_by_interface(self, interface):\r\n        raise NotImplementedError\r\n\r\n    def register_by_interface(self, interface):\r\n        raise NotImplementedError\r\n\r\n\r\ndef instance_scope(container: Container, constructor: t.Callable) -> t.Any:\r\n    \"\"\"Every injection makes a new instance.\"\"\"\r\n    return constructor()\r\n\r\n\r\ndef thread_scope(container: Container, constructor: t.Callable) -> t.Any:\r\n    _thread_local = container._thread_local\r\n    try:\r\n        objects = _thread_local.objects\r\n    except AttributeError:\r\n        objects = {}\r\n        _thread_local.objects = objects\r\n\r\n    if constructor in objects:\r\n        return objects[constructor]\r\n    else:\r\n        obj = constructor()\r\n        objects[constructor] = obj\r\n    return obj\r\n\r\n\r\ndef request_scope(container: Container, constructor: t.Callable) -> t.Any:\r\n    \"\"\"\r\n    Created once per request in the container. Uses Container to find the request\r\n    (whatever it is in your application), so don't get caught into infinite recursion\r\n    using the scope onto request.\r\n    \"\"\"\r\n    # TODO should request has the same API find/register_by_interface?\r\n    # TODO or should request generate some UUID which is used as a key for the container\r\n    # TODO but when & how to clean objects for requests that are already dead\r\n\r\n    request = container.find_by_interface(IRequest)\r\n    try:\r\n        return request.find_by_interface[constructor]\r\n    except KeyError:\r\n        obj = constructor()\r\n        request.register_by_interface[constructor] = obj\r\n        return obj\r\n\r\n\r\ndef session_scope(container: Container, constructor: t.Callable) -> t.Any:\r\n    # TODO same problem as in the request_scope\r\n    raise NotImplementedError\r\n\r\n\r\ndef singleton_scope(container: Container, constructor: t.Callable) -> t.Any:\r\n    \"\"\"Created only once in the container.\"\"\"\r\n    try:\r\n        return container._singleton_objects[constructor]\r\n    except KeyError:\r\n        obj = constructor()\r\n        container._singleton_objects[constructor] = obj\r\n        return obj\r\n\r\n\r\nclass Scopes(Enum):\r\n    INSTANCE = instance_scope  # every injection makes a new instance\r\n    REQUEST = request_scope  # per a \"request\" (whatever a request is in your application)\r\n    SESSION = session_scope  # per session (whatever is a user session in your application)\r\n    THREAD = thread_scope  # per thread (via `threading.local`)\r\n    SINGLETON = singleton_scope  # always the same instance in this container\r\n\r\n    def get_object(self, container: Container, constructor: t.Callable):\r\n        return self.value(container, constructor)\r\n```"},"comment":{"url":"https://api.github.com/repos/pcah/python-clean-architecture/issues/comments/442406366","html_url":"https://github.com/pcah/python-clean-architecture/issues/9#issuecomment-442406366","issue_url":"https://api.github.com/repos/pcah/python-clean-architecture/issues/9","id":442406366,"node_id":"MDEyOklzc3VlQ29tbWVudDQ0MjQwNjM2Ng==","user":{"login":"lhaze","id":2180285,"node_id":"MDQ6VXNlcjIxODAyODU=","avatar_url":"https://avatars0.githubusercontent.com/u/2180285?v=4","gravatar_id":"","url":"https://api.github.com/users/lhaze","html_url":"https://github.com/lhaze","followers_url":"https://api.github.com/users/lhaze/followers","following_url":"https://api.github.com/users/lhaze/following{/other_user}","gists_url":"https://api.github.com/users/lhaze/gists{/gist_id}","starred_url":"https://api.github.com/users/lhaze/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lhaze/subscriptions","organizations_url":"https://api.github.com/users/lhaze/orgs","repos_url":"https://api.github.com/users/lhaze/repos","events_url":"https://api.github.com/users/lhaze/events{/privacy}","received_events_url":"https://api.github.com/users/lhaze/received_events","type":"User","site_admin":false},"created_at":"2018-11-28T10:57:51Z","updated_at":"2018-11-28T10:57:51Z","author_association":"COLLABORATOR","body":"Flag with _Important_: goals of incoming version (see [roadmap](https://github.com/pcah/python-clean-architecture/blob/master/ROADMAP.md)) will need _session_ scope and _request_ scope, at least in the context of CLI."}}
{"actor":{"display_login":"lhaze"}}