openmeter 1.0.0b54__py3-none-any.whl → 2.0.0__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.

Potentially problematic release.


This version of openmeter might be problematic. Click here for more details.

Files changed (132) hide show
  1. openmeter/__init__.py +89 -15
  2. openmeter/_base_client.py +2041 -0
  3. openmeter/_client.py +518 -70
  4. openmeter/_compat.py +221 -0
  5. openmeter/_constants.py +14 -0
  6. openmeter/_exceptions.py +108 -0
  7. openmeter/_files.py +127 -0
  8. openmeter/_models.py +777 -0
  9. openmeter/_qs.py +150 -0
  10. openmeter/_resource.py +43 -0
  11. openmeter/_response.py +820 -0
  12. openmeter/_streaming.py +333 -0
  13. openmeter/_types.py +222 -0
  14. openmeter/_utils/__init__.py +56 -0
  15. openmeter/_utils/_logs.py +25 -0
  16. openmeter/_utils/_proxy.py +63 -0
  17. openmeter/_utils/_reflection.py +42 -0
  18. openmeter/_utils/_streams.py +12 -0
  19. openmeter/_utils/_sync.py +81 -0
  20. openmeter/_utils/_transform.py +387 -0
  21. openmeter/_utils/_typing.py +120 -0
  22. openmeter/_utils/_utils.py +419 -0
  23. openmeter/_version.py +4 -0
  24. openmeter/lib/.keep +4 -0
  25. openmeter/py.typed +0 -1
  26. openmeter/resources/__init__.py +103 -0
  27. openmeter/resources/debug/__init__.py +33 -0
  28. openmeter/resources/debug/debug.py +102 -0
  29. openmeter/resources/debug/metrics.py +146 -0
  30. openmeter/resources/entitlements/__init__.py +47 -0
  31. openmeter/resources/entitlements/entitlements.py +450 -0
  32. openmeter/resources/entitlements/features.py +578 -0
  33. openmeter/resources/entitlements/grants.py +389 -0
  34. openmeter/resources/events.py +442 -0
  35. openmeter/resources/meters/__init__.py +33 -0
  36. openmeter/resources/meters/meters.py +666 -0
  37. openmeter/resources/meters/subjects.py +163 -0
  38. openmeter/resources/notifications/__init__.py +75 -0
  39. openmeter/resources/notifications/channels.py +686 -0
  40. openmeter/resources/notifications/events.py +365 -0
  41. openmeter/resources/notifications/notifications.py +198 -0
  42. openmeter/resources/notifications/rules.py +781 -0
  43. openmeter/resources/notifications/webhook.py +208 -0
  44. openmeter/resources/portal/__init__.py +47 -0
  45. openmeter/resources/portal/meters.py +230 -0
  46. openmeter/resources/portal/portal.py +112 -0
  47. openmeter/resources/portal/tokens.py +359 -0
  48. openmeter/resources/subjects/entitlements/__init__.py +33 -0
  49. openmeter/resources/subjects/entitlements/entitlements.py +1881 -0
  50. openmeter/resources/subjects/entitlements/grants.py +453 -0
  51. openmeter/resources/subjects.py +419 -0
  52. openmeter/types/__init__.py +21 -0
  53. openmeter/types/debug/__init__.py +5 -0
  54. openmeter/types/debug/metric_list_response.py +7 -0
  55. openmeter/types/entitlement.py +238 -0
  56. openmeter/types/entitlements/__init__.py +11 -0
  57. openmeter/types/entitlements/feature.py +61 -0
  58. openmeter/types/entitlements/feature_create_params.py +43 -0
  59. openmeter/types/entitlements/feature_list_params.py +23 -0
  60. openmeter/types/entitlements/grant_list_params.py +57 -0
  61. openmeter/types/entitlements/grant_list_response.py +11 -0
  62. openmeter/types/entitlements/grant_paginated_response.py +24 -0
  63. openmeter/types/entitlements/list_features_result.py +28 -0
  64. openmeter/types/event_ingest_params.py +46 -0
  65. openmeter/types/event_ingest_response.py +43 -0
  66. openmeter/types/event_list_params.py +22 -0
  67. openmeter/types/event_list_response.py +9 -0
  68. openmeter/types/ingested_event.py +59 -0
  69. openmeter/types/list_entitlements_result.py +28 -0
  70. openmeter/types/meter.py +53 -0
  71. openmeter/types/meter_create_params.py +50 -0
  72. openmeter/types/meter_list_response.py +9 -0
  73. openmeter/types/meter_query_params.py +50 -0
  74. openmeter/types/meter_query_result.py +35 -0
  75. openmeter/types/meters/__init__.py +5 -0
  76. openmeter/types/meters/subject_list_response.py +8 -0
  77. openmeter/types/notifications/__init__.py +18 -0
  78. openmeter/types/notifications/channel_create_params.py +34 -0
  79. openmeter/types/notifications/channel_list_params.py +41 -0
  80. openmeter/types/notifications/channel_list_response.py +24 -0
  81. openmeter/types/notifications/channel_update_params.py +34 -0
  82. openmeter/types/notifications/event_list_params.py +61 -0
  83. openmeter/types/notifications/event_list_response.py +24 -0
  84. openmeter/types/notifications/notification_channel.py +47 -0
  85. openmeter/types/notifications/notification_event.py +215 -0
  86. openmeter/types/notifications/notification_rule.py +70 -0
  87. openmeter/types/notifications/rule_create_params.py +39 -0
  88. openmeter/types/notifications/rule_list_params.py +54 -0
  89. openmeter/types/notifications/rule_list_response.py +24 -0
  90. openmeter/types/notifications/rule_update_params.py +39 -0
  91. openmeter/types/notifications/webhook_svix_params.py +26 -0
  92. openmeter/types/portal/__init__.py +10 -0
  93. openmeter/types/portal/meter_query_params.py +44 -0
  94. openmeter/types/portal/portal_token.py +28 -0
  95. openmeter/types/portal/token_create_params.py +17 -0
  96. openmeter/types/portal/token_invalidate_params.py +15 -0
  97. openmeter/types/portal/token_list_params.py +12 -0
  98. openmeter/types/portal/token_list_response.py +9 -0
  99. openmeter/types/shared/__init__.py +3 -0
  100. openmeter/types/subject.py +37 -0
  101. openmeter/types/subject_list_response.py +9 -0
  102. openmeter/types/subject_param.py +27 -0
  103. openmeter/types/subject_upsert_params.py +39 -0
  104. openmeter/types/subject_upsert_response.py +10 -0
  105. openmeter/types/subjects/__init__.py +13 -0
  106. openmeter/types/subjects/entitlement_history_params.py +35 -0
  107. openmeter/types/subjects/entitlement_history_response.py +98 -0
  108. openmeter/types/subjects/entitlement_list_response.py +10 -0
  109. openmeter/types/subjects/entitlements/__init__.py +8 -0
  110. openmeter/types/subjects/entitlements/entitlement_grant.py +103 -0
  111. openmeter/types/subjects/entitlements/grant_list_response.py +10 -0
  112. openmeter-2.0.0.dist-info/METADATA +396 -0
  113. openmeter-2.0.0.dist-info/RECORD +115 -0
  114. {openmeter-1.0.0b54.dist-info → openmeter-2.0.0.dist-info}/WHEEL +1 -1
  115. openmeter-2.0.0.dist-info/licenses/LICENSE +201 -0
  116. openmeter/_configuration.py +0 -36
  117. openmeter/_operations/__init__.py +0 -17
  118. openmeter/_operations/_operations.py +0 -2105
  119. openmeter/_operations/_patch.py +0 -20
  120. openmeter/_patch.py +0 -20
  121. openmeter/_serialization.py +0 -2008
  122. openmeter/_vendor.py +0 -24
  123. openmeter/aio/__init__.py +0 -21
  124. openmeter/aio/_client.py +0 -83
  125. openmeter/aio/_configuration.py +0 -36
  126. openmeter/aio/_operations/__init__.py +0 -17
  127. openmeter/aio/_operations/_operations.py +0 -1778
  128. openmeter/aio/_operations/_patch.py +0 -20
  129. openmeter/aio/_patch.py +0 -20
  130. openmeter/aio/_vendor.py +0 -24
  131. openmeter-1.0.0b54.dist-info/METADATA +0 -92
  132. openmeter-1.0.0b54.dist-info/RECORD +0 -21
openmeter/_client.py CHANGED
@@ -1,83 +1,531 @@
1
- # coding=utf-8
2
- # --------------------------------------------------------------------------
3
- # Code generated by Microsoft (R) AutoRest Code Generator (autorest: 3.10.2, generator: @autorest/python@6.9.4)
4
- # Changes may cause incorrect behavior and will be lost if the code is regenerated.
5
- # --------------------------------------------------------------------------
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
6
2
 
7
- from copy import deepcopy
8
- from typing import Any
3
+ from __future__ import annotations
9
4
 
10
- from azure.core import PipelineClient
11
- from azure.core.pipeline import policies
12
- from azure.core.rest import HttpRequest, HttpResponse
5
+ import os
6
+ from typing import Any, Dict, Union, Mapping, cast
7
+ from typing_extensions import Self, Literal, override
13
8
 
14
- from ._configuration import ClientConfiguration
15
- from ._operations import ClientOperationsMixin
16
- from ._serialization import Deserializer, Serializer
9
+ import httpx
17
10
 
11
+ from . import resources, _exceptions
12
+ from ._qs import Querystring
13
+ from ._types import (
14
+ NOT_GIVEN,
15
+ Omit,
16
+ Headers,
17
+ Timeout,
18
+ NotGiven,
19
+ Transport,
20
+ ProxiesTypes,
21
+ RequestOptions,
22
+ )
23
+ from ._utils import (
24
+ is_given,
25
+ get_async_library,
26
+ )
27
+ from ._version import __version__
28
+ from ._streaming import Stream as Stream, AsyncStream as AsyncStream
29
+ from ._exceptions import APIStatusError
30
+ from ._base_client import (
31
+ DEFAULT_MAX_RETRIES,
32
+ SyncAPIClient,
33
+ AsyncAPIClient,
34
+ )
18
35
 
19
- class Client(ClientOperationsMixin): # pylint: disable=client-accepts-api-version-keyword
20
- """Client.
36
+ __all__ = [
37
+ "ENVIRONMENTS",
38
+ "Timeout",
39
+ "Transport",
40
+ "ProxiesTypes",
41
+ "RequestOptions",
42
+ "resources",
43
+ "OpenMeter",
44
+ "AsyncOpenMeter",
45
+ "Client",
46
+ "AsyncClient",
47
+ ]
21
48
 
22
- :keyword endpoint: Service URL. Required. Default value is "".
23
- :paramtype endpoint: str
24
- """
49
+ ENVIRONMENTS: Dict[str, str] = {
50
+ "production": "https://openmeter.cloud",
51
+ "local": "https://127.0.0.1:8888",
52
+ }
25
53
 
26
- def __init__( # pylint: disable=missing-client-constructor-parameter-credential
27
- self, *, endpoint: str = "", **kwargs: Any
54
+
55
+ class OpenMeter(SyncAPIClient):
56
+ events: resources.EventsResource
57
+ meters: resources.MetersResource
58
+ subjects: resources.SubjectsResource
59
+ entitlements: resources.EntitlementsResource
60
+ notifications: resources.NotificationsResource
61
+ portal: resources.PortalResource
62
+ debug: resources.DebugResource
63
+ with_raw_response: OpenMeterWithRawResponse
64
+ with_streaming_response: OpenMeterWithStreamedResponse
65
+
66
+ # client options
67
+ cloud_api_token: str | None
68
+
69
+ _environment: Literal["production", "local"] | NotGiven
70
+
71
+ def __init__(
72
+ self,
73
+ *,
74
+ cloud_api_token: str | None = None,
75
+ environment: Literal["production", "local"] | NotGiven = NOT_GIVEN,
76
+ base_url: str | httpx.URL | None | NotGiven = NOT_GIVEN,
77
+ timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
78
+ max_retries: int = DEFAULT_MAX_RETRIES,
79
+ default_headers: Mapping[str, str] | None = None,
80
+ default_query: Mapping[str, object] | None = None,
81
+ # Configure a custom httpx client.
82
+ # We provide a `DefaultHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`.
83
+ # See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details.
84
+ http_client: httpx.Client | None = None,
85
+ # Enable or disable schema validation for data returned by the API.
86
+ # When enabled an error APIResponseValidationError is raised
87
+ # if the API responds with invalid data for the expected schema.
88
+ #
89
+ # This parameter may be removed or changed in the future.
90
+ # If you rely on this feature, please open a GitHub issue
91
+ # outlining your use-case to help us decide if it should be
92
+ # part of our public interface in the future.
93
+ _strict_response_validation: bool = False,
94
+ ) -> None:
95
+ """Construct a new synchronous OpenMeter client instance.
96
+
97
+ This automatically infers the `cloud_api_token` argument from the `CLOUD_API_TOKEN` environment variable if it is not provided.
98
+ """
99
+ if cloud_api_token is None:
100
+ cloud_api_token = os.environ.get("CLOUD_API_TOKEN")
101
+ self.cloud_api_token = cloud_api_token
102
+
103
+ self._environment = environment
104
+
105
+ base_url_env = os.environ.get("OPEN_METER_BASE_URL")
106
+ if is_given(base_url) and base_url is not None:
107
+ # cast required because mypy doesn't understand the type narrowing
108
+ base_url = cast("str | httpx.URL", base_url) # pyright: ignore[reportUnnecessaryCast]
109
+ elif is_given(environment):
110
+ if base_url_env and base_url is not None:
111
+ raise ValueError(
112
+ "Ambiguous URL; The `OPEN_METER_BASE_URL` env var and the `environment` argument are given. If you want to use the environment, you must pass base_url=None",
113
+ )
114
+
115
+ try:
116
+ base_url = ENVIRONMENTS[environment]
117
+ except KeyError as exc:
118
+ raise ValueError(f"Unknown environment: {environment}") from exc
119
+ elif base_url_env is not None:
120
+ base_url = base_url_env
121
+ else:
122
+ self._environment = environment = "production"
123
+
124
+ try:
125
+ base_url = ENVIRONMENTS[environment]
126
+ except KeyError as exc:
127
+ raise ValueError(f"Unknown environment: {environment}") from exc
128
+
129
+ super().__init__(
130
+ version=__version__,
131
+ base_url=base_url,
132
+ max_retries=max_retries,
133
+ timeout=timeout,
134
+ http_client=http_client,
135
+ custom_headers=default_headers,
136
+ custom_query=default_query,
137
+ _strict_response_validation=_strict_response_validation,
138
+ )
139
+
140
+ self.events = resources.EventsResource(self)
141
+ self.meters = resources.MetersResource(self)
142
+ self.subjects = resources.SubjectsResource(self)
143
+ self.entitlements = resources.EntitlementsResource(self)
144
+ self.notifications = resources.NotificationsResource(self)
145
+ self.portal = resources.PortalResource(self)
146
+ self.debug = resources.DebugResource(self)
147
+ self.with_raw_response = OpenMeterWithRawResponse(self)
148
+ self.with_streaming_response = OpenMeterWithStreamedResponse(self)
149
+
150
+ @property
151
+ @override
152
+ def qs(self) -> Querystring:
153
+ return Querystring(array_format="comma")
154
+
155
+ @property
156
+ @override
157
+ def auth_headers(self) -> dict[str, str]:
158
+ cloud_api_token = self.cloud_api_token
159
+ if cloud_api_token is None:
160
+ return {}
161
+ return {"Authorization": f"Bearer {cloud_api_token}"}
162
+
163
+ @property
164
+ @override
165
+ def default_headers(self) -> dict[str, str | Omit]:
166
+ return {
167
+ **super().default_headers,
168
+ "X-Stainless-Async": "false",
169
+ **self._custom_headers,
170
+ }
171
+
172
+ @override
173
+ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None:
174
+ if self.cloud_api_token and headers.get("Authorization"):
175
+ return
176
+ if isinstance(custom_headers.get("Authorization"), Omit):
177
+ return
178
+
179
+ raise TypeError(
180
+ '"Could not resolve authentication method. Expected the cloud_api_token to be set. Or for the `Authorization` headers to be explicitly omitted"'
181
+ )
182
+
183
+ def copy(
184
+ self,
185
+ *,
186
+ cloud_api_token: str | None = None,
187
+ environment: Literal["production", "local"] | None = None,
188
+ base_url: str | httpx.URL | None = None,
189
+ timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
190
+ http_client: httpx.Client | None = None,
191
+ max_retries: int | NotGiven = NOT_GIVEN,
192
+ default_headers: Mapping[str, str] | None = None,
193
+ set_default_headers: Mapping[str, str] | None = None,
194
+ default_query: Mapping[str, object] | None = None,
195
+ set_default_query: Mapping[str, object] | None = None,
196
+ _extra_kwargs: Mapping[str, Any] = {},
197
+ ) -> Self:
198
+ """
199
+ Create a new client instance re-using the same options given to the current client with optional overriding.
200
+ """
201
+ if default_headers is not None and set_default_headers is not None:
202
+ raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive")
203
+
204
+ if default_query is not None and set_default_query is not None:
205
+ raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive")
206
+
207
+ headers = self._custom_headers
208
+ if default_headers is not None:
209
+ headers = {**headers, **default_headers}
210
+ elif set_default_headers is not None:
211
+ headers = set_default_headers
212
+
213
+ params = self._custom_query
214
+ if default_query is not None:
215
+ params = {**params, **default_query}
216
+ elif set_default_query is not None:
217
+ params = set_default_query
218
+
219
+ http_client = http_client or self._client
220
+ return self.__class__(
221
+ cloud_api_token=cloud_api_token or self.cloud_api_token,
222
+ base_url=base_url or self.base_url,
223
+ environment=environment or self._environment,
224
+ timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
225
+ http_client=http_client,
226
+ max_retries=max_retries if is_given(max_retries) else self.max_retries,
227
+ default_headers=headers,
228
+ default_query=params,
229
+ **_extra_kwargs,
230
+ )
231
+
232
+ # Alias for `copy` for nicer inline usage, e.g.
233
+ # client.with_options(timeout=10).foo.create(...)
234
+ with_options = copy
235
+
236
+ @override
237
+ def _make_status_error(
238
+ self,
239
+ err_msg: str,
240
+ *,
241
+ body: object,
242
+ response: httpx.Response,
243
+ ) -> APIStatusError:
244
+ if response.status_code == 400:
245
+ return _exceptions.BadRequestError(err_msg, response=response, body=body)
246
+
247
+ if response.status_code == 401:
248
+ return _exceptions.AuthenticationError(err_msg, response=response, body=body)
249
+
250
+ if response.status_code == 403:
251
+ return _exceptions.PermissionDeniedError(err_msg, response=response, body=body)
252
+
253
+ if response.status_code == 404:
254
+ return _exceptions.NotFoundError(err_msg, response=response, body=body)
255
+
256
+ if response.status_code == 409:
257
+ return _exceptions.ConflictError(err_msg, response=response, body=body)
258
+
259
+ if response.status_code == 422:
260
+ return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body)
261
+
262
+ if response.status_code == 429:
263
+ return _exceptions.RateLimitError(err_msg, response=response, body=body)
264
+
265
+ if response.status_code >= 500:
266
+ return _exceptions.InternalServerError(err_msg, response=response, body=body)
267
+ return APIStatusError(err_msg, response=response, body=body)
268
+
269
+
270
+ class AsyncOpenMeter(AsyncAPIClient):
271
+ events: resources.AsyncEventsResource
272
+ meters: resources.AsyncMetersResource
273
+ subjects: resources.AsyncSubjectsResource
274
+ entitlements: resources.AsyncEntitlementsResource
275
+ notifications: resources.AsyncNotificationsResource
276
+ portal: resources.AsyncPortalResource
277
+ debug: resources.AsyncDebugResource
278
+ with_raw_response: AsyncOpenMeterWithRawResponse
279
+ with_streaming_response: AsyncOpenMeterWithStreamedResponse
280
+
281
+ # client options
282
+ cloud_api_token: str | None
283
+
284
+ _environment: Literal["production", "local"] | NotGiven
285
+
286
+ def __init__(
287
+ self,
288
+ *,
289
+ cloud_api_token: str | None = None,
290
+ environment: Literal["production", "local"] | NotGiven = NOT_GIVEN,
291
+ base_url: str | httpx.URL | None | NotGiven = NOT_GIVEN,
292
+ timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
293
+ max_retries: int = DEFAULT_MAX_RETRIES,
294
+ default_headers: Mapping[str, str] | None = None,
295
+ default_query: Mapping[str, object] | None = None,
296
+ # Configure a custom httpx client.
297
+ # We provide a `DefaultAsyncHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`.
298
+ # See the [httpx documentation](https://www.python-httpx.org/api/#asyncclient) for more details.
299
+ http_client: httpx.AsyncClient | None = None,
300
+ # Enable or disable schema validation for data returned by the API.
301
+ # When enabled an error APIResponseValidationError is raised
302
+ # if the API responds with invalid data for the expected schema.
303
+ #
304
+ # This parameter may be removed or changed in the future.
305
+ # If you rely on this feature, please open a GitHub issue
306
+ # outlining your use-case to help us decide if it should be
307
+ # part of our public interface in the future.
308
+ _strict_response_validation: bool = False,
28
309
  ) -> None:
29
- self._config = ClientConfiguration(**kwargs)
30
- _policies = kwargs.pop("policies", None)
31
- if _policies is None:
32
- _policies = [
33
- policies.RequestIdPolicy(**kwargs),
34
- self._config.headers_policy,
35
- self._config.user_agent_policy,
36
- self._config.proxy_policy,
37
- policies.ContentDecodePolicy(**kwargs),
38
- self._config.redirect_policy,
39
- self._config.retry_policy,
40
- self._config.authentication_policy,
41
- self._config.custom_hook_policy,
42
- self._config.logging_policy,
43
- policies.DistributedTracingPolicy(**kwargs),
44
- policies.SensitiveHeaderCleanupPolicy(**kwargs) if self._config.redirect_policy else None,
45
- self._config.http_logging_policy,
46
- ]
47
- self._client: PipelineClient = PipelineClient(base_url=endpoint, policies=_policies, **kwargs)
48
-
49
- self._serialize = Serializer()
50
- self._deserialize = Deserializer()
51
- self._serialize.client_side_validation = False
52
-
53
- def send_request(self, request: HttpRequest, **kwargs: Any) -> HttpResponse:
54
- """Runs the network request through the client's chained policies.
55
-
56
- >>> from azure.core.rest import HttpRequest
57
- >>> request = HttpRequest("GET", "https://www.example.org/")
58
- <HttpRequest [GET], url: 'https://www.example.org/'>
59
- >>> response = client.send_request(request)
60
- <HttpResponse: 200 OK>
61
-
62
- For more information on this code flow, see https://aka.ms/azsdk/dpcodegen/python/send_request
63
-
64
- :param request: The network request you want to make. Required.
65
- :type request: ~azure.core.rest.HttpRequest
66
- :keyword bool stream: Whether the response payload will be streamed. Defaults to False.
67
- :return: The response of your network call. Does not do error handling on your response.
68
- :rtype: ~azure.core.rest.HttpResponse
310
+ """Construct a new async OpenMeter client instance.
311
+
312
+ This automatically infers the `cloud_api_token` argument from the `CLOUD_API_TOKEN` environment variable if it is not provided.
69
313
  """
314
+ if cloud_api_token is None:
315
+ cloud_api_token = os.environ.get("CLOUD_API_TOKEN")
316
+ self.cloud_api_token = cloud_api_token
317
+
318
+ self._environment = environment
319
+
320
+ base_url_env = os.environ.get("OPEN_METER_BASE_URL")
321
+ if is_given(base_url) and base_url is not None:
322
+ # cast required because mypy doesn't understand the type narrowing
323
+ base_url = cast("str | httpx.URL", base_url) # pyright: ignore[reportUnnecessaryCast]
324
+ elif is_given(environment):
325
+ if base_url_env and base_url is not None:
326
+ raise ValueError(
327
+ "Ambiguous URL; The `OPEN_METER_BASE_URL` env var and the `environment` argument are given. If you want to use the environment, you must pass base_url=None",
328
+ )
329
+
330
+ try:
331
+ base_url = ENVIRONMENTS[environment]
332
+ except KeyError as exc:
333
+ raise ValueError(f"Unknown environment: {environment}") from exc
334
+ elif base_url_env is not None:
335
+ base_url = base_url_env
336
+ else:
337
+ self._environment = environment = "production"
338
+
339
+ try:
340
+ base_url = ENVIRONMENTS[environment]
341
+ except KeyError as exc:
342
+ raise ValueError(f"Unknown environment: {environment}") from exc
343
+
344
+ super().__init__(
345
+ version=__version__,
346
+ base_url=base_url,
347
+ max_retries=max_retries,
348
+ timeout=timeout,
349
+ http_client=http_client,
350
+ custom_headers=default_headers,
351
+ custom_query=default_query,
352
+ _strict_response_validation=_strict_response_validation,
353
+ )
354
+
355
+ self.events = resources.AsyncEventsResource(self)
356
+ self.meters = resources.AsyncMetersResource(self)
357
+ self.subjects = resources.AsyncSubjectsResource(self)
358
+ self.entitlements = resources.AsyncEntitlementsResource(self)
359
+ self.notifications = resources.AsyncNotificationsResource(self)
360
+ self.portal = resources.AsyncPortalResource(self)
361
+ self.debug = resources.AsyncDebugResource(self)
362
+ self.with_raw_response = AsyncOpenMeterWithRawResponse(self)
363
+ self.with_streaming_response = AsyncOpenMeterWithStreamedResponse(self)
364
+
365
+ @property
366
+ @override
367
+ def qs(self) -> Querystring:
368
+ return Querystring(array_format="comma")
369
+
370
+ @property
371
+ @override
372
+ def auth_headers(self) -> dict[str, str]:
373
+ cloud_api_token = self.cloud_api_token
374
+ if cloud_api_token is None:
375
+ return {}
376
+ return {"Authorization": f"Bearer {cloud_api_token}"}
377
+
378
+ @property
379
+ @override
380
+ def default_headers(self) -> dict[str, str | Omit]:
381
+ return {
382
+ **super().default_headers,
383
+ "X-Stainless-Async": f"async:{get_async_library()}",
384
+ **self._custom_headers,
385
+ }
386
+
387
+ @override
388
+ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None:
389
+ if self.cloud_api_token and headers.get("Authorization"):
390
+ return
391
+ if isinstance(custom_headers.get("Authorization"), Omit):
392
+ return
393
+
394
+ raise TypeError(
395
+ '"Could not resolve authentication method. Expected the cloud_api_token to be set. Or for the `Authorization` headers to be explicitly omitted"'
396
+ )
397
+
398
+ def copy(
399
+ self,
400
+ *,
401
+ cloud_api_token: str | None = None,
402
+ environment: Literal["production", "local"] | None = None,
403
+ base_url: str | httpx.URL | None = None,
404
+ timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
405
+ http_client: httpx.AsyncClient | None = None,
406
+ max_retries: int | NotGiven = NOT_GIVEN,
407
+ default_headers: Mapping[str, str] | None = None,
408
+ set_default_headers: Mapping[str, str] | None = None,
409
+ default_query: Mapping[str, object] | None = None,
410
+ set_default_query: Mapping[str, object] | None = None,
411
+ _extra_kwargs: Mapping[str, Any] = {},
412
+ ) -> Self:
413
+ """
414
+ Create a new client instance re-using the same options given to the current client with optional overriding.
415
+ """
416
+ if default_headers is not None and set_default_headers is not None:
417
+ raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive")
418
+
419
+ if default_query is not None and set_default_query is not None:
420
+ raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive")
421
+
422
+ headers = self._custom_headers
423
+ if default_headers is not None:
424
+ headers = {**headers, **default_headers}
425
+ elif set_default_headers is not None:
426
+ headers = set_default_headers
427
+
428
+ params = self._custom_query
429
+ if default_query is not None:
430
+ params = {**params, **default_query}
431
+ elif set_default_query is not None:
432
+ params = set_default_query
433
+
434
+ http_client = http_client or self._client
435
+ return self.__class__(
436
+ cloud_api_token=cloud_api_token or self.cloud_api_token,
437
+ base_url=base_url or self.base_url,
438
+ environment=environment or self._environment,
439
+ timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
440
+ http_client=http_client,
441
+ max_retries=max_retries if is_given(max_retries) else self.max_retries,
442
+ default_headers=headers,
443
+ default_query=params,
444
+ **_extra_kwargs,
445
+ )
446
+
447
+ # Alias for `copy` for nicer inline usage, e.g.
448
+ # client.with_options(timeout=10).foo.create(...)
449
+ with_options = copy
450
+
451
+ @override
452
+ def _make_status_error(
453
+ self,
454
+ err_msg: str,
455
+ *,
456
+ body: object,
457
+ response: httpx.Response,
458
+ ) -> APIStatusError:
459
+ if response.status_code == 400:
460
+ return _exceptions.BadRequestError(err_msg, response=response, body=body)
461
+
462
+ if response.status_code == 401:
463
+ return _exceptions.AuthenticationError(err_msg, response=response, body=body)
464
+
465
+ if response.status_code == 403:
466
+ return _exceptions.PermissionDeniedError(err_msg, response=response, body=body)
467
+
468
+ if response.status_code == 404:
469
+ return _exceptions.NotFoundError(err_msg, response=response, body=body)
470
+
471
+ if response.status_code == 409:
472
+ return _exceptions.ConflictError(err_msg, response=response, body=body)
473
+
474
+ if response.status_code == 422:
475
+ return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body)
476
+
477
+ if response.status_code == 429:
478
+ return _exceptions.RateLimitError(err_msg, response=response, body=body)
479
+
480
+ if response.status_code >= 500:
481
+ return _exceptions.InternalServerError(err_msg, response=response, body=body)
482
+ return APIStatusError(err_msg, response=response, body=body)
483
+
484
+
485
+ class OpenMeterWithRawResponse:
486
+ def __init__(self, client: OpenMeter) -> None:
487
+ self.events = resources.EventsResourceWithRawResponse(client.events)
488
+ self.meters = resources.MetersResourceWithRawResponse(client.meters)
489
+ self.subjects = resources.SubjectsResourceWithRawResponse(client.subjects)
490
+ self.entitlements = resources.EntitlementsResourceWithRawResponse(client.entitlements)
491
+ self.notifications = resources.NotificationsResourceWithRawResponse(client.notifications)
492
+ self.portal = resources.PortalResourceWithRawResponse(client.portal)
493
+ self.debug = resources.DebugResourceWithRawResponse(client.debug)
494
+
495
+
496
+ class AsyncOpenMeterWithRawResponse:
497
+ def __init__(self, client: AsyncOpenMeter) -> None:
498
+ self.events = resources.AsyncEventsResourceWithRawResponse(client.events)
499
+ self.meters = resources.AsyncMetersResourceWithRawResponse(client.meters)
500
+ self.subjects = resources.AsyncSubjectsResourceWithRawResponse(client.subjects)
501
+ self.entitlements = resources.AsyncEntitlementsResourceWithRawResponse(client.entitlements)
502
+ self.notifications = resources.AsyncNotificationsResourceWithRawResponse(client.notifications)
503
+ self.portal = resources.AsyncPortalResourceWithRawResponse(client.portal)
504
+ self.debug = resources.AsyncDebugResourceWithRawResponse(client.debug)
505
+
506
+
507
+ class OpenMeterWithStreamedResponse:
508
+ def __init__(self, client: OpenMeter) -> None:
509
+ self.events = resources.EventsResourceWithStreamingResponse(client.events)
510
+ self.meters = resources.MetersResourceWithStreamingResponse(client.meters)
511
+ self.subjects = resources.SubjectsResourceWithStreamingResponse(client.subjects)
512
+ self.entitlements = resources.EntitlementsResourceWithStreamingResponse(client.entitlements)
513
+ self.notifications = resources.NotificationsResourceWithStreamingResponse(client.notifications)
514
+ self.portal = resources.PortalResourceWithStreamingResponse(client.portal)
515
+ self.debug = resources.DebugResourceWithStreamingResponse(client.debug)
516
+
70
517
 
71
- request_copy = deepcopy(request)
72
- request_copy.url = self._client.format_url(request_copy.url)
73
- return self._client.send_request(request_copy, **kwargs) # type: ignore
518
+ class AsyncOpenMeterWithStreamedResponse:
519
+ def __init__(self, client: AsyncOpenMeter) -> None:
520
+ self.events = resources.AsyncEventsResourceWithStreamingResponse(client.events)
521
+ self.meters = resources.AsyncMetersResourceWithStreamingResponse(client.meters)
522
+ self.subjects = resources.AsyncSubjectsResourceWithStreamingResponse(client.subjects)
523
+ self.entitlements = resources.AsyncEntitlementsResourceWithStreamingResponse(client.entitlements)
524
+ self.notifications = resources.AsyncNotificationsResourceWithStreamingResponse(client.notifications)
525
+ self.portal = resources.AsyncPortalResourceWithStreamingResponse(client.portal)
526
+ self.debug = resources.AsyncDebugResourceWithStreamingResponse(client.debug)
74
527
 
75
- def close(self) -> None:
76
- self._client.close()
77
528
 
78
- def __enter__(self) -> "Client":
79
- self._client.__enter__()
80
- return self
529
+ Client = OpenMeter
81
530
 
82
- def __exit__(self, *exc_details: Any) -> None:
83
- self._client.__exit__(*exc_details)
531
+ AsyncClient = AsyncOpenMeter