rats-apps 0.1.3.dev8__py3-none-any.whl → 0.1.3.dev9__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
@@ -7,17 +7,21 @@ domain.
7
7
 
8
8
  from ._annotations import (
9
9
  AnnotatedContainer,
10
+ autoid_service,
10
11
  config,
11
12
  container,
12
13
  fallback_config,
13
14
  fallback_group,
14
15
  fallback_service,
15
16
  group,
17
+ method_service_id,
16
18
  service,
17
19
  )
20
+ from ._composite_container import CompositeContainer
18
21
  from ._container import Container, DuplicateServiceError, ServiceNotFoundError
19
22
  from ._ids import ConfigId, ServiceId
20
23
  from ._namespaces import ProviderNamespaces
24
+ from ._plugin_container import PluginContainers
21
25
  from ._scoping import autoscope
22
26
 
23
27
  __all__ = [
@@ -36,4 +40,8 @@ __all__ = [
36
40
  "fallback_service",
37
41
  "group",
38
42
  "service",
43
+ "method_service_id",
44
+ "autoid_service",
45
+ "CompositeContainer",
46
+ "PluginContainers",
39
47
  ]
rats/apps/_annotations.py CHANGED
@@ -1,13 +1,14 @@
1
1
  from collections import defaultdict
2
2
  from collections.abc import Callable, Iterator
3
3
  from functools import cache
4
- from typing import Any, cast
4
+ from typing import Any, ParamSpec, cast
5
5
 
6
6
  from typing_extensions import NamedTuple
7
7
 
8
8
  from ._container import Container
9
9
  from ._ids import ConfigId, ServiceId, T_ConfigType, T_ServiceType
10
10
  from ._namespaces import ProviderNamespaces
11
+ from ._scoping import scope_service_name
11
12
 
12
13
  DEFAULT_CONTAINER_GROUP = ServiceId[Container]("__default__")
13
14
 
@@ -57,6 +58,9 @@ class FunctionAnnotationsBuilder:
57
58
  def add(self, namespace: str, service_id: ServiceId[T_ServiceType]) -> None:
58
59
  self._service_ids[namespace].append(service_id)
59
60
 
61
+ def get_service_names(self, namespace: str) -> tuple[str, ...]:
62
+ return tuple(s.name for s in self._service_ids.get(namespace, []))
63
+
60
64
  def make(self, name: str) -> tuple[GroupAnnotations, ...]:
61
65
  return tuple(
62
66
  [
@@ -84,57 +88,98 @@ class AnnotatedContainer(Container):
84
88
  yield from c.get_namespaced_group(namespace, group_id)
85
89
 
86
90
 
91
+ P = ParamSpec("P")
92
+
93
+
87
94
  def service(
88
95
  service_id: ServiceId[T_ServiceType],
89
- ) -> Callable[..., Callable[..., T_ServiceType]]:
96
+ ) -> Callable[[Callable[P, T_ServiceType]], Callable[P, T_ServiceType]]:
90
97
  return fn_annotation_decorator(ProviderNamespaces.SERVICES, service_id)
91
98
 
92
99
 
100
+ def autoid_service(fn: Callable[P, T_ServiceType]) -> Callable[P, T_ServiceType]:
101
+ _service_id = method_service_id(fn)
102
+ _add_annotation(ProviderNamespaces.SERVICES, fn, _service_id)
103
+ cached_fn = cache(fn)
104
+ return cast(Callable[P, T_ServiceType], cached_fn)
105
+
106
+
93
107
  def group(
94
108
  group_id: ServiceId[T_ServiceType],
95
- ) -> Callable[..., Callable[..., T_ServiceType]]:
109
+ ) -> Callable[[Callable[P, T_ServiceType]], Callable[P, T_ServiceType]]:
96
110
  return fn_annotation_decorator(ProviderNamespaces.GROUPS, group_id)
97
111
 
98
112
 
99
113
  def config(
100
114
  config_id: ConfigId[T_ConfigType],
101
- ) -> Callable[..., Callable[..., T_ConfigType]]:
115
+ ) -> Callable[[Callable[P, T_ConfigType]], Callable[P, T_ConfigType]]:
102
116
  return fn_annotation_decorator(ProviderNamespaces.SERVICES, config_id)
103
117
 
104
118
 
105
119
  def fallback_service(
106
120
  service_id: ServiceId[T_ServiceType],
107
- ) -> Callable[..., Callable[..., T_ServiceType]]:
121
+ ) -> Callable[[Callable[P, T_ServiceType]], Callable[P, T_ServiceType]]:
108
122
  return fn_annotation_decorator(ProviderNamespaces.FALLBACK_SERVICES, service_id)
109
123
 
110
124
 
111
125
  def fallback_group(
112
126
  group_id: ServiceId[T_ServiceType],
113
- ) -> Callable[..., Callable[..., T_ServiceType]]:
127
+ ) -> Callable[[Callable[P, T_ServiceType]], Callable[P, T_ServiceType]]:
114
128
  return fn_annotation_decorator(ProviderNamespaces.FALLBACK_GROUPS, group_id)
115
129
 
116
130
 
117
131
  def fallback_config(
118
132
  config_id: ConfigId[T_ConfigType],
119
- ) -> Callable[..., Callable[..., T_ConfigType]]:
133
+ ) -> Callable[[Callable[P, T_ConfigType]], Callable[P, T_ConfigType]]:
120
134
  return fn_annotation_decorator(ProviderNamespaces.FALLBACK_SERVICES, config_id)
121
135
 
122
136
 
123
137
  def container(
124
138
  group_id: ServiceId[T_ServiceType] = DEFAULT_CONTAINER_GROUP,
125
- ) -> Callable[..., Callable[..., T_ServiceType]]:
139
+ ) -> Callable[[Callable[P, T_ServiceType]], Callable[P, T_ServiceType]]:
126
140
  return fn_annotation_decorator(ProviderNamespaces.CONTAINERS, group_id)
127
141
 
128
142
 
143
+ def _get_method_service_id_name(method: Callable[..., Any]) -> str:
144
+ existing_names = _get_annotations_builder(method).get_service_names(
145
+ ProviderNamespaces.SERVICES
146
+ )
147
+ if existing_names:
148
+ return existing_names[0]
149
+ else:
150
+ module_name = method.__module__
151
+ class_name, method_name = method.__qualname__.rsplit(".", 1)
152
+ service_name = scope_service_name(module_name, class_name, method_name)
153
+ return service_name
154
+
155
+
156
+ def method_service_id(method: Callable[..., T_ServiceType]) -> ServiceId[T_ServiceType]:
157
+ """
158
+ Get a service id for a method.
159
+
160
+ The service id is constructed from the module, class and method name. It should be identical
161
+ regardless of whether the method is bound or not, and regardless of the instance it is bound
162
+ to.
163
+
164
+ The service type is the return type of the method.
165
+ """
166
+ service_name = _get_method_service_id_name(method)
167
+ return ServiceId[T_ServiceType](service_name)
168
+
169
+
129
170
  def fn_annotation_decorator(
130
171
  namespace: str,
131
172
  service_id: ServiceId[T_ServiceType],
132
- ) -> Callable[..., Callable[..., T_ServiceType]]:
173
+ ) -> Callable[[Callable[P, T_ServiceType]], Callable[P, T_ServiceType]]:
133
174
  def wrapper(
134
- fn: Callable[..., T_ServiceType],
135
- ) -> Callable[..., T_ServiceType]:
136
- _add_annotation(namespace, fn, service_id)
137
- return cache(fn)
175
+ fn: Callable[P, T_ServiceType],
176
+ ) -> Callable[P, T_ServiceType]:
177
+ _service_id = service_id
178
+ _add_annotation(namespace, fn, _service_id)
179
+ cached_fn = cache(fn)
180
+ # The static type of cached_fn should be correct, but it does not maintain the param-spec,
181
+ # so we need to cast.
182
+ return cast(Callable[P, T_ServiceType], cached_fn)
138
183
 
139
184
  return wrapper
140
185
 
rats/apps/_scoping.py CHANGED
@@ -8,6 +8,10 @@ T = TypeVar("T")
8
8
  P = ParamSpec("P")
9
9
 
10
10
 
11
+ def scope_service_name(module_name: str, cls_name: str, name: str) -> str:
12
+ return f"{module_name}:{cls_name}[{name}]"
13
+
14
+
11
15
  def autoscope(cls: type[T]) -> type[T]:
12
16
  """
13
17
  Decorator that replaces all ServiceId instances in the class with scoped ServiceId instances.
@@ -22,7 +26,7 @@ def autoscope(cls: type[T]) -> type[T]:
22
26
  if not isinstance(result, ServiceId):
23
27
  return result
24
28
 
25
- return ServiceId[Any](f"{cls.__module__}:{cls.__name__}[{result.name}]")
29
+ return ServiceId[Any](scope_service_name(cls.__module__, cls.__name__, result.name))
26
30
 
27
31
  return cast(FunctionType, wrapper)
28
32
 
@@ -37,7 +41,7 @@ def autoscope(cls: type[T]) -> type[T]:
37
41
  if not isinstance(non_ns, ServiceId):
38
42
  continue
39
43
 
40
- prop = ServiceId[Any](f"{cls.__module__}:{cls.__name__}[{non_ns.name}]")
44
+ prop = ServiceId[Any](scope_service_name(cls.__module__, cls.__name__, non_ns.name))
41
45
  setattr(cls, prop_name, prop)
42
46
 
43
47
  return cls
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: rats-apps
3
- Version: 0.1.3.dev8
3
+ Version: 0.1.3.dev9
4
4
  Summary: research analysis tools for building applications
5
5
  Home-page: https://github.com/microsoft/rats/
6
6
  License: MIT
@@ -1,13 +1,13 @@
1
- rats/apps/__init__.py,sha256=JYVss3L2R4uxdOp4Ozfg_cHWgRvCWq79OyVZefFw9wY,858
2
- rats/apps/_annotations.py,sha256=NGCw1JMZdPMoK_pE61iXgjeI1Xn7P5WXLt7t8zWligg,5125
1
+ rats/apps/__init__.py,sha256=5Mi9YL80YF6Z1Jy2uB_L1oWDG_w0Tl00yViprVuO_LQ,1099
2
+ rats/apps/_annotations.py,sha256=GT2VGkLO2Re0oZsVd6oQlm0WdJj5NSirGA0cpPmmiik,6993
3
3
  rats/apps/_composite_container.py,sha256=wSWVQWPin2xxIlEoSgk_D1rlc3N2gpTxQ2y9UFdqXy0,553
4
4
  rats/apps/_container.py,sha256=5uiCyxN6HS2z97XcTOFP-t72cNoB1U1sJMkMcfSfDps,3129
5
5
  rats/apps/_ids.py,sha256=dxWCPMpMA_vpaTDJEKNByIBJaX97Db203XqWLhaOezo,457
6
6
  rats/apps/_namespaces.py,sha256=THUV_Xj5PtweC23Ob-zsSpk8exC4fT-qRwjpQ6IDm0U,188
7
7
  rats/apps/_plugin_container.py,sha256=W_xQD2btc0N2dEb3c5tXM-ZZ4A4diMpkCjbOZdlXYuI,853
8
- rats/apps/_scoping.py,sha256=lRV1DDq-U4mr4WOQhvFjTiCQe2dKY95LNn6b0RXRjFA,1305
8
+ rats/apps/_scoping.py,sha256=plSVEq3rJ8JFAu2epVg2NQpuTbpSTA3a0Tha_DwJL_Y,1453
9
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,,
10
+ rats_apps-0.1.3.dev9.dist-info/METADATA,sha256=rWKB-G07Xc5Nm0CIT0W3fyNrXWDPg632uRdkrYF6Z0o,716
11
+ rats_apps-0.1.3.dev9.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
12
+ rats_apps-0.1.3.dev9.dist-info/entry_points.txt,sha256=Vu1IgAPQvL4xMJzW_OG2JSPFac7HalCHyXiRr0-OfCI,86
13
+ rats_apps-0.1.3.dev9.dist-info/RECORD,,