Skip to content

Commit

Permalink
Use __init__subclass__ instead of a metaclass for service registration
Browse files Browse the repository at this point in the history
This syntax is a lot nicer and even makes adding keyword arguments 
really easy.
  • Loading branch information
Askaholic committed May 3, 2021
1 parent 4d81156 commit 593a58b
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 17 deletions.
28 changes: 11 additions & 17 deletions server/core/service.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,28 @@
import re
from typing import Dict, List
from typing import Any, Dict, List, Optional

from .dependency_injector import DependencyInjector

CASE_PATTERN = re.compile(r"(?<!^)(?=[A-Z])")
DependencyGraph = Dict[str, List[str]]


class ServiceMeta(type):
"""
For tracking which Services have been defined.
"""

# Mapping from parameter name to class
services: Dict[str, type] = {}
service_registry: Dict[str, type] = {}

def __new__(cls, name, bases, attrs):
klass = type.__new__(cls, name, bases, attrs)
if name != "Service":
arg_name = snake_case(name)
cls.services[arg_name] = klass
return klass


class Service(metaclass=ServiceMeta):
class Service():
"""
All services should inherit from this class.
Services are singleton objects which manage some server task.
"""
def __init_subclass__(cls, name: Optional[str] = None, **kwargs: Any):
"""
For tracking which services have been defined.
"""
super().__init_subclass__(**kwargs)
arg_name = name or snake_case(cls.__name__)
service_registry[arg_name] = cls

async def initialize(self) -> None:
"""
Expand Down Expand Up @@ -57,7 +51,7 @@ def create_services(injectables: Dict[str, object] = {}) -> Dict[str, Service]:
injector = DependencyInjector()
injector.add_injectables(**injectables)

return injector.build_classes(ServiceMeta.services)
return injector.build_classes(service_registry)


def snake_case(string: str) -> str:
Expand Down
21 changes: 21 additions & 0 deletions tests/unit_tests/core/test_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import mock

from server.core import Service


def test_service_registry():
with mock.patch("server.core.service.service_registry", {}) as registry:
class Foo(Service):
pass

assert registry["foo"] is Foo
assert registry == {"foo": Foo}


def test_service_registry_name_override():
with mock.patch("server.core.service.service_registry", {}) as registry:
class Foo(Service, name="FooService"):
pass

assert registry["FooService"] is Foo
assert registry == {"FooService": Foo}

0 comments on commit 593a58b

Please sign in to comment.