rats-apps 0.1.3__py3-none-any.whl → 0.1.3.dev8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
rats/apps/__init__.py CHANGED
@@ -1,62 +1,39 @@
1
1
  """
2
- Libraries to help create applications with a strong focus on composability and testability.
2
+ Provides a small set of libraries to help create new applications.
3
3
 
4
4
  Applications give you the ability to define a development experience to match your project's
5
5
  domain.
6
6
  """
7
7
 
8
8
  from ._annotations import (
9
- autoid,
10
- autoid_service,
9
+ AnnotatedContainer,
10
+ config,
11
+ container,
12
+ fallback_config,
11
13
  fallback_group,
12
14
  fallback_service,
13
15
  group,
14
16
  service,
15
17
  )
16
- from ._composite_container import CompositeContainer
17
- from ._container import (
18
- AnnotatedContainer, # type: ignore[reportDeprecated]
19
- Container,
20
- DuplicateServiceError,
21
- ServiceNotFoundError,
22
- ServiceProvider,
23
- container,
24
- )
25
- from ._executables import App, Executable
26
- from ._ids import ServiceId, T_ExecutableType
18
+ from ._container import Container, DuplicateServiceError, ServiceNotFoundError
19
+ from ._ids import ConfigId, ServiceId
27
20
  from ._namespaces import ProviderNamespaces
28
- from ._plugin_container import PluginContainers
29
- from ._plugins import PluginRunner
30
- from ._runtimes import NullRuntime, Runtime
31
21
  from ._scoping import autoscope
32
- from ._simple_apps import AppServices, SimpleApplication, StandardRuntime
33
22
 
34
23
  __all__ = [
35
24
  "AnnotatedContainer",
36
- "App",
37
- "CompositeContainer",
25
+ "ConfigId",
38
26
  "Container",
39
27
  "DuplicateServiceError",
40
- "Executable",
41
- "PluginContainers",
42
28
  "ProviderNamespaces",
43
- "ServiceProvider",
44
29
  "ServiceId",
45
30
  "ServiceNotFoundError",
46
- "autoid_service",
47
31
  "autoscope",
32
+ "config",
48
33
  "container",
49
- "PluginRunner",
34
+ "fallback_config",
50
35
  "fallback_group",
51
36
  "fallback_service",
52
37
  "group",
53
- "autoid",
54
38
  "service",
55
- "T_ExecutableType",
56
- "Runtime",
57
- "NullRuntime",
58
- "AppServices",
59
- "StandardRuntime",
60
- "StandardRuntime",
61
- "SimpleApplication",
62
39
  ]
rats/apps/_annotations.py CHANGED
@@ -1,74 +1,164 @@
1
- from collections.abc import Callable
2
- from typing import Any, ParamSpec
1
+ from collections import defaultdict
2
+ from collections.abc import Callable, Iterator
3
+ from functools import cache
4
+ from typing import Any, cast
3
5
 
4
- from rats import annotations
6
+ from typing_extensions import NamedTuple
5
7
 
6
- from ._ids import ServiceId, T_ServiceType
8
+ from ._container import Container
9
+ from ._ids import ConfigId, ServiceId, T_ConfigType, T_ServiceType
7
10
  from ._namespaces import ProviderNamespaces
8
- from ._scoping import scope_service_name
9
11
 
10
- P = ParamSpec("P")
12
+ DEFAULT_CONTAINER_GROUP = ServiceId[Container]("__default__")
11
13
 
12
14
 
13
- def service(
14
- service_id: ServiceId[T_ServiceType],
15
- ) -> Callable[[Callable[P, T_ServiceType]], Callable[P, T_ServiceType]]:
16
- """A service is anything you would create instances of?"""
17
- return annotations.annotation(ProviderNamespaces.SERVICES, service_id)
15
+ class GroupAnnotations(NamedTuple):
16
+ """
17
+ The list of service ids attached to a given function.
18
+
19
+ The `name` attribute is the name of the function, and the `namespace` attribute represents a
20
+ specific meaning for the group of services.
21
+ """
22
+
23
+ name: str
24
+ namespace: str
25
+ groups: tuple[ServiceId[Any], ...]
26
+
27
+
28
+ class FunctionAnnotations(NamedTuple):
29
+ """
30
+ Holds metadata about the annotated service provider.
31
+
32
+ Loosely inspired by: https://peps.python.org/pep-3107/.
33
+ """
34
+
35
+ providers: tuple[GroupAnnotations, ...]
36
+
37
+ def group_in_namespace(
38
+ self,
39
+ namespace: str,
40
+ group_id: ServiceId[T_ServiceType],
41
+ ) -> tuple[GroupAnnotations, ...]:
42
+ return tuple([x for x in self.with_namespace(namespace) if group_id in x.groups])
43
+
44
+ def with_namespace(
45
+ self,
46
+ namespace: str,
47
+ ) -> tuple[GroupAnnotations, ...]:
48
+ return tuple([x for x in self.providers if x.namespace == namespace])
49
+
50
+
51
+ class FunctionAnnotationsBuilder:
52
+ _service_ids: dict[str, list[ServiceId[Any]]]
53
+
54
+ def __init__(self) -> None:
55
+ self._service_ids = defaultdict(list)
56
+
57
+ def add(self, namespace: str, service_id: ServiceId[T_ServiceType]) -> None:
58
+ self._service_ids[namespace].append(service_id)
59
+
60
+ def make(self, name: str) -> tuple[GroupAnnotations, ...]:
61
+ return tuple(
62
+ [
63
+ GroupAnnotations(name=name, namespace=namespace, groups=tuple(services))
64
+ for namespace, services in self._service_ids.items()
65
+ ]
66
+ )
18
67
 
19
68
 
20
- def autoid_service(fn: Callable[P, T_ServiceType]) -> Callable[P, T_ServiceType]:
21
- _service_id = autoid(fn)
22
- return annotations.annotation(ProviderNamespaces.SERVICES, _service_id)(fn)
69
+ class AnnotatedContainer(Container):
70
+ def get_namespaced_group(
71
+ self,
72
+ namespace: str,
73
+ group_id: ServiceId[T_ServiceType],
74
+ ) -> Iterator[T_ServiceType]:
75
+ annotations = _extract_class_annotations(type(self))
76
+ containers = annotations.with_namespace(ProviderNamespaces.CONTAINERS)
77
+ groups = annotations.group_in_namespace(namespace, group_id)
78
+
79
+ for annotation in groups:
80
+ yield getattr(self, annotation.name)()
81
+
82
+ for container in containers:
83
+ c = getattr(self, container.name)()
84
+ yield from c.get_namespaced_group(namespace, group_id)
85
+
86
+
87
+ def service(
88
+ service_id: ServiceId[T_ServiceType],
89
+ ) -> Callable[..., Callable[..., T_ServiceType]]:
90
+ return fn_annotation_decorator(ProviderNamespaces.SERVICES, service_id)
23
91
 
24
92
 
25
93
  def group(
26
94
  group_id: ServiceId[T_ServiceType],
27
- ) -> Callable[[Callable[P, T_ServiceType]], Callable[P, T_ServiceType]]:
28
- """A group is a collection of services."""
29
- return annotations.annotation(ProviderNamespaces.GROUPS, group_id)
95
+ ) -> Callable[..., Callable[..., T_ServiceType]]:
96
+ return fn_annotation_decorator(ProviderNamespaces.GROUPS, group_id)
97
+
98
+
99
+ def config(
100
+ config_id: ConfigId[T_ConfigType],
101
+ ) -> Callable[..., Callable[..., T_ConfigType]]:
102
+ return fn_annotation_decorator(ProviderNamespaces.SERVICES, config_id)
30
103
 
31
104
 
32
105
  def fallback_service(
33
106
  service_id: ServiceId[T_ServiceType],
34
- ) -> Callable[[Callable[P, T_ServiceType]], Callable[P, T_ServiceType]]:
35
- """A fallback service gets used if no service is defined."""
36
- return annotations.annotation(
37
- ProviderNamespaces.FALLBACK_SERVICES,
38
- service_id,
39
- )
107
+ ) -> Callable[..., Callable[..., T_ServiceType]]:
108
+ return fn_annotation_decorator(ProviderNamespaces.FALLBACK_SERVICES, service_id)
40
109
 
41
110
 
42
111
  def fallback_group(
43
112
  group_id: ServiceId[T_ServiceType],
44
- ) -> Callable[[Callable[P, T_ServiceType]], Callable[P, T_ServiceType]]:
45
- """A fallback group gets used if no group is defined."""
46
- return annotations.annotation(
47
- ProviderNamespaces.FALLBACK_GROUPS,
48
- group_id,
49
- )
113
+ ) -> Callable[..., Callable[..., T_ServiceType]]:
114
+ return fn_annotation_decorator(ProviderNamespaces.FALLBACK_GROUPS, group_id)
50
115
 
51
116
 
52
- def autoid(method: Callable[..., T_ServiceType]) -> ServiceId[T_ServiceType]:
53
- """
54
- Get a service id for a method.
117
+ def fallback_config(
118
+ config_id: ConfigId[T_ConfigType],
119
+ ) -> Callable[..., Callable[..., T_ConfigType]]:
120
+ return fn_annotation_decorator(ProviderNamespaces.FALLBACK_SERVICES, config_id)
55
121
 
56
- The service id is constructed from the module, class and method name. It should be identical
57
- regardless of whether the method is bound or not, and regardless of the instance it is bound
58
- to.
59
122
 
60
- The service type is the return type of the method.
61
- """
62
- service_name = _get_method_service_id_name(method)
63
- return ServiceId[T_ServiceType](service_name)
123
+ def container(
124
+ group_id: ServiceId[T_ServiceType] = DEFAULT_CONTAINER_GROUP,
125
+ ) -> Callable[..., Callable[..., T_ServiceType]]:
126
+ return fn_annotation_decorator(ProviderNamespaces.CONTAINERS, group_id)
127
+
128
+
129
+ def fn_annotation_decorator(
130
+ namespace: str,
131
+ service_id: ServiceId[T_ServiceType],
132
+ ) -> Callable[..., Callable[..., T_ServiceType]]:
133
+ def wrapper(
134
+ fn: Callable[..., T_ServiceType],
135
+ ) -> Callable[..., T_ServiceType]:
136
+ _add_annotation(namespace, fn, service_id)
137
+ return cache(fn)
138
+
139
+ return wrapper
140
+
141
+
142
+ @cache
143
+ def _extract_class_annotations(cls: Any) -> FunctionAnnotations:
144
+ function_annotations: list[GroupAnnotations] = []
145
+ for method_name in dir(cls):
146
+ if method_name.startswith("_"):
147
+ continue
148
+
149
+ builder = _get_annotations_builder(getattr(cls, method_name))
150
+ function_annotations.extend(list(builder.make(method_name)))
151
+
152
+ return FunctionAnnotations(tuple(function_annotations))
153
+
64
154
 
155
+ def _add_annotation(namespace: str, fn: Any, service_id: ServiceId[T_ServiceType]) -> None:
156
+ builder = _get_annotations_builder(fn)
157
+ builder.add(namespace, service_id)
65
158
 
66
- def _get_method_service_id_name(method: Callable[..., Any]) -> str:
67
- tates = annotations.get_annotations(method).with_namespace(ProviderNamespaces.SERVICES)
68
159
 
69
- for a in tates.annotations:
70
- return a.groups[0].name
160
+ def _get_annotations_builder(fn: Any) -> FunctionAnnotationsBuilder:
161
+ if not hasattr(fn, "__rats_service_annotations__"):
162
+ fn.__rats_service_annotations__ = FunctionAnnotationsBuilder()
71
163
 
72
- module_name = method.__module__
73
- class_name, method_name = method.__qualname__.rsplit(".", 1)
74
- return scope_service_name(module_name, class_name, method_name)
164
+ return cast(FunctionAnnotationsBuilder, fn.__rats_service_annotations__)
@@ -1,13 +1,11 @@
1
1
  from collections.abc import Iterator
2
- from typing import final
3
2
 
4
3
  from ._container import Container
5
4
  from ._ids import ServiceId, T_ServiceType
6
5
 
7
6
 
8
- @final
9
7
  class CompositeContainer(Container):
10
- _containers: tuple[Container, ...]
8
+ _contailers: tuple[Container, ...]
11
9
 
12
10
  def __init__(self, *containers: Container) -> None:
13
11
  self._containers = containers
rats/apps/_container.py CHANGED
@@ -1,18 +1,10 @@
1
- import abc
2
- import logging
3
1
  from abc import abstractmethod
4
- from collections.abc import Callable, Iterator
5
- from typing import Generic, ParamSpec, Protocol
2
+ from collections.abc import Iterator
3
+ from typing import Generic, Protocol
6
4
 
7
- from typing_extensions import deprecated
8
-
9
- from rats import annotations
10
-
11
- from ._ids import ServiceId, T_ServiceType, Tco_ServiceType
5
+ from ._ids import ServiceId, T_ServiceType, Tco_ConfigType, Tco_ServiceType
12
6
  from ._namespaces import ProviderNamespaces
13
7
 
14
- logger = logging.getLogger(__name__)
15
-
16
8
 
17
9
  class ServiceProvider(Protocol[Tco_ServiceType]):
18
10
  @abstractmethod
@@ -20,62 +12,22 @@ class ServiceProvider(Protocol[Tco_ServiceType]):
20
12
  """Return the service instance."""
21
13
 
22
14
 
23
- class GroupProvider(Protocol[Tco_ServiceType]):
15
+ class ConfigProvider(ServiceProvider[Tco_ConfigType], Protocol[Tco_ConfigType]):
24
16
  @abstractmethod
25
- def __call__(self) -> Iterator[Tco_ServiceType]:
26
- """Return the group instances."""
17
+ def __call__(self) -> Tco_ConfigType:
18
+ """Return the config instance."""
27
19
 
28
20
 
29
21
  class Container(Protocol):
30
- """
31
- Main interface for service containers.
32
-
33
- The default methods in this protocol attempt to find service providers that have been
34
- annotated.
35
-
36
- Example:
37
- .. code-block:: python
38
-
39
- from rats import apps
40
-
41
-
42
- class MyStorageClient:
43
- def save(self, data: str) -> None:
44
- print(f"Saving data: {data}")
45
-
46
-
47
- class MyPluginServices:
48
- STORAGE_CLIENT = ServiceId[MyStorageClient]("storage-client")
49
-
50
-
51
- class MyPluginContainer(apps.Container):
52
- @apps.service(MyPluginServices.STORAGE_CLIENT)
53
- def _storage_client() -> MyStorageClient:
54
- return MyStorageClient()
55
-
56
-
57
- container = MyPluginContainer()
58
- storage_client = container.get(MyPluginServices.STORAGE_CLIENT)
59
- storage_client.save("Hello, world!")
60
- """
22
+ """Main interface for service containers."""
61
23
 
62
24
  def has(self, service_id: ServiceId[T_ServiceType]) -> bool:
63
- """
64
- Check if a service is provided by this container.
65
-
66
- Example:
67
- .. code-block:: python
68
-
69
- if not container.has(MyPluginServices.STORAGE_CLIENT):
70
- print("Did you forget to configure a storage client?")
71
- """
72
25
  try:
73
26
  return self.get(service_id) is not None
74
27
  except ServiceNotFoundError:
75
28
  return False
76
29
 
77
30
  def has_group(self, group_id: ServiceId[T_ServiceType]) -> bool:
78
- """Check if a service group has at least one provider in the container."""
79
31
  try:
80
32
  return next(self.get_group(group_id)) is not None
81
33
  except StopIteration:
@@ -112,61 +64,13 @@ class Container(Protocol):
112
64
 
113
65
  yield from self.get_namespaced_group(ProviderNamespaces.GROUPS, group_id)
114
66
 
67
+ @abstractmethod
115
68
  def get_namespaced_group(
116
69
  self,
117
70
  namespace: str,
118
71
  group_id: ServiceId[T_ServiceType],
119
72
  ) -> Iterator[T_ServiceType]:
120
73
  """Retrieve a service group by its id, within a given service namespace."""
121
- tates = annotations.get_class_annotations(type(self))
122
- containers = tates.with_namespace(ProviderNamespaces.CONTAINERS)
123
- groups = tates.with_group(namespace, group_id)
124
-
125
- for annotation in groups.annotations:
126
- if not hasattr(self, f"__rats_cache_{annotation.name}"):
127
- setattr(self, f"__rats_cache_{annotation.name}", getattr(self, annotation.name)())
128
-
129
- yield getattr(self, f"__rats_cache_{annotation.name}")
130
-
131
- for annotation in containers.annotations:
132
- if not hasattr(self, f"__rats_container_cache_{annotation.name}"):
133
- setattr(
134
- self,
135
- f"__rats_container_cache_{annotation.name}",
136
- getattr(self, annotation.name)(),
137
- )
138
-
139
- c = getattr(self, f"__rats_container_cache_{annotation.name}")
140
- yield from c.get_namespaced_group(namespace, group_id)
141
-
142
-
143
- @deprecated(
144
- " ".join(
145
- [
146
- "AnnotatedContainer is deprecated and will be removed in the next major release.",
147
- "The functionality has been moved into the apps.Container protocol.",
148
- "Please extend apps.Container directly.",
149
- ]
150
- ),
151
- stacklevel=2,
152
- )
153
- class AnnotatedContainer(Container, abc.ABC):
154
- """
155
- A Container implementation that extracts providers from its annotated methods.
156
-
157
- .. deprecated:: 0.1.3
158
- The behavior of this class has been made the default within ``Container``.
159
- """
160
-
161
-
162
- DEFAULT_CONTAINER_GROUP = ServiceId[Container]("__default__")
163
- P = ParamSpec("P")
164
-
165
-
166
- def container(
167
- group_id: ServiceId[T_ServiceType] = DEFAULT_CONTAINER_GROUP,
168
- ) -> Callable[[Callable[P, T_ServiceType]], Callable[P, T_ServiceType]]:
169
- return annotations.annotation(ProviderNamespaces.CONTAINERS, group_id)
170
74
 
171
75
 
172
76
  class ServiceNotFoundError(RuntimeError, Generic[T_ServiceType]):
rats/apps/_ids.py CHANGED
@@ -1,13 +1,17 @@
1
+ import typing
1
2
  from typing import Generic, TypeVar
2
3
 
3
4
  from typing_extensions import NamedTuple
4
5
 
5
- from ._executables import Executable
6
-
7
6
  T_ServiceType = TypeVar("T_ServiceType")
8
- T_ExecutableType = TypeVar("T_ExecutableType", bound=Executable)
7
+ T_ConfigType = TypeVar("T_ConfigType", bound=typing.NamedTuple)
9
8
  Tco_ServiceType = TypeVar("Tco_ServiceType", covariant=True)
9
+ Tco_ConfigType = TypeVar("Tco_ConfigType", bound=NamedTuple, covariant=True)
10
10
 
11
11
 
12
12
  class ServiceId(NamedTuple, Generic[T_ServiceType]):
13
13
  name: str
14
+
15
+
16
+ class ConfigId(ServiceId[T_ConfigType]):
17
+ pass
@@ -7,24 +7,12 @@ from ._ids import ServiceId, T_ServiceType
7
7
 
8
8
 
9
9
  class PluginContainers(Container):
10
- """
11
- A container that loads plugins using importlib.metadata.entry_points.
12
-
13
- When looking for groups, the container loads the specified entry_points and defers the lookups
14
- to the plugins. Plugin containers are expected to be Callable[[Container], Container] objects,
15
- where the input container is typically the root application container.
16
-
17
- TODO: How do we better specify the API for plugins without relying on documentation?
18
- """
19
-
20
10
  _app: Container
21
11
  _group: str
22
- _names: tuple[str, ...]
23
12
 
24
- def __init__(self, app: Container, group: str, *names: str) -> None:
13
+ def __init__(self, app: Container, group: str) -> None:
25
14
  self._app = app
26
15
  self._group = group
27
- self._names = names
28
16
 
29
17
  def get_namespaced_group(
30
18
  self,
@@ -37,7 +25,4 @@ class PluginContainers(Container):
37
25
  @cache # noqa: B019
38
26
  def _load_containers(self) -> Iterable[Container]:
39
27
  entries = entry_points(group=self._group)
40
- return tuple(entry.load()(self._app) for entry in entries if self._is_enabled(entry.name))
41
-
42
- def _is_enabled(self, plugin_name: str) -> bool:
43
- return len(self._names) == 0 or plugin_name in self._names
28
+ return tuple(entry.load()(self._app) for entry in entries)
rats/apps/_scoping.py CHANGED
@@ -22,7 +22,7 @@ def autoscope(cls: type[T]) -> type[T]:
22
22
  if not isinstance(result, ServiceId):
23
23
  return result
24
24
 
25
- return ServiceId[Any](scope_service_name(cls.__module__, cls.__name__, result.name))
25
+ return ServiceId[Any](f"{cls.__module__}:{cls.__name__}[{result.name}]")
26
26
 
27
27
  return cast(FunctionType, wrapper)
28
28
 
@@ -37,11 +37,7 @@ def autoscope(cls: type[T]) -> type[T]:
37
37
  if not isinstance(non_ns, ServiceId):
38
38
  continue
39
39
 
40
- prop = ServiceId[Any](scope_service_name(cls.__module__, cls.__name__, non_ns.name))
40
+ prop = ServiceId[Any](f"{cls.__module__}:{cls.__name__}[{non_ns.name}]")
41
41
  setattr(cls, prop_name, prop)
42
42
 
43
43
  return cls
44
-
45
-
46
- def scope_service_name(module_name: str, cls_name: str, name: str) -> str:
47
- return f"{module_name}:{cls_name}[{name}]"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: rats-apps
3
- Version: 0.1.3
3
+ Version: 0.1.3.dev8
4
4
  Summary: research analysis tools for building applications
5
5
  Home-page: https://github.com/microsoft/rats/
6
6
  License: MIT
@@ -11,8 +11,6 @@ Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.10
12
12
  Classifier: Programming Language :: Python :: 3.11
13
13
  Classifier: Programming Language :: Python :: 3.12
14
- Requires-Dist: click
15
- Requires-Dist: colorlog
16
14
  Requires-Dist: typing_extensions
17
15
  Project-URL: Documentation, https://microsoft.github.io/rats/
18
16
  Project-URL: Repository, https://github.com/microsoft/rats/
@@ -0,0 +1,13 @@
1
+ rats/apps/__init__.py,sha256=JYVss3L2R4uxdOp4Ozfg_cHWgRvCWq79OyVZefFw9wY,858
2
+ rats/apps/_annotations.py,sha256=NGCw1JMZdPMoK_pE61iXgjeI1Xn7P5WXLt7t8zWligg,5125
3
+ rats/apps/_composite_container.py,sha256=wSWVQWPin2xxIlEoSgk_D1rlc3N2gpTxQ2y9UFdqXy0,553
4
+ rats/apps/_container.py,sha256=5uiCyxN6HS2z97XcTOFP-t72cNoB1U1sJMkMcfSfDps,3129
5
+ rats/apps/_ids.py,sha256=dxWCPMpMA_vpaTDJEKNByIBJaX97Db203XqWLhaOezo,457
6
+ rats/apps/_namespaces.py,sha256=THUV_Xj5PtweC23Ob-zsSpk8exC4fT-qRwjpQ6IDm0U,188
7
+ rats/apps/_plugin_container.py,sha256=W_xQD2btc0N2dEb3c5tXM-ZZ4A4diMpkCjbOZdlXYuI,853
8
+ rats/apps/_scoping.py,sha256=lRV1DDq-U4mr4WOQhvFjTiCQe2dKY95LNn6b0RXRjFA,1305
9
+ rats/apps/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ rats_apps-0.1.3.dev8.dist-info/METADATA,sha256=mZp5dVow8ScTYqG5JaKsEPzABPF-b3r55srRYgJJ9y8,716
11
+ rats_apps-0.1.3.dev8.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
12
+ rats_apps-0.1.3.dev8.dist-info/entry_points.txt,sha256=Vu1IgAPQvL4xMJzW_OG2JSPFac7HalCHyXiRr0-OfCI,86
13
+ rats_apps-0.1.3.dev8.dist-info/RECORD,,
@@ -0,0 +1,3 @@
1
+ [rats-apps.test-plugins]
2
+ example-storage=rats_test.apps.example:ExampleStoragePlugin
3
+
@@ -1,25 +0,0 @@
1
- """
2
- General purpose library to attach annotations to functions.
3
-
4
- Annotations are typically, but not exclusively, attached using decorators.
5
- """
6
-
7
- from ._functions import (
8
- AnnotationsContainer,
9
- DecoratorType,
10
- GroupAnnotations,
11
- T_GroupType,
12
- annotation,
13
- get_annotations,
14
- get_class_annotations,
15
- )
16
-
17
- __all__ = [
18
- "annotation",
19
- "DecoratorType",
20
- "AnnotationsContainer",
21
- "get_annotations",
22
- "get_class_annotations",
23
- "GroupAnnotations",
24
- "T_GroupType",
25
- ]