firebase-admin 7.5.0__tar.gz → 7.6.0__tar.gz
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.
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/PKG-INFO +1 -1
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/__about__.py +1 -1
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/app_check.py +53 -9
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/ml.py +140 -4
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin.egg-info/PKG-INFO +1 -1
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_app_check.py +136 -5
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_ml.py +87 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/LICENSE +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/README.md +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/__init__.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/_auth_client.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/_auth_providers.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/_auth_utils.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/_http_client.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/_messaging_encoder.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/_messaging_utils.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/_retry.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/_rfc3339.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/_sseclient.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/_token_gen.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/_user_identifier.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/_user_import.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/_user_mgt.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/_utils.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/auth.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/credentials.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/db.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/exceptions.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/firestore.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/firestore_async.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/functions.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/instance_id.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/messaging.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/phone_number_verification.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/project_management.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/remote_config.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/storage.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin/tenant_mgt.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin.egg-info/SOURCES.txt +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin.egg-info/dependency_links.txt +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin.egg-info/requires.txt +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/firebase_admin.egg-info/top_level.txt +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/setup.cfg +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/setup.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_app.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_auth_providers.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_credentials.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_db.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_exceptions.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_firestore.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_firestore_async.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_functions.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_http_client.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_instance_id.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_messaging.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_phone_number_verification.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_project_management.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_remote_config.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_retry.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_rfc3339.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_sseclient.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_storage.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_tenant_mgt.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_token_gen.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/test_user_mgt.py +0 -0
- {firebase_admin-7.5.0 → firebase_admin-7.6.0}/tests/testutils.py +0 -0
|
@@ -15,38 +15,47 @@
|
|
|
15
15
|
"""Firebase App Check module."""
|
|
16
16
|
|
|
17
17
|
from typing import Any, Dict
|
|
18
|
+
import requests
|
|
18
19
|
import jwt
|
|
19
20
|
from jwt import PyJWKClient, ExpiredSignatureError, InvalidTokenError, DecodeError
|
|
20
21
|
from jwt import InvalidAudienceError, InvalidIssuerError, InvalidSignatureError
|
|
21
|
-
from firebase_admin import _utils
|
|
22
|
+
from firebase_admin import _http_client, _utils, exceptions
|
|
22
23
|
|
|
23
24
|
_APP_CHECK_ATTRIBUTE = '_app_check'
|
|
24
25
|
|
|
25
26
|
def _get_app_check_service(app) -> Any:
|
|
26
27
|
return _utils.get_app_service(app, _APP_CHECK_ATTRIBUTE, _AppCheckService)
|
|
27
28
|
|
|
28
|
-
def verify_token(token: str, app=None) -> Dict[str, Any]:
|
|
29
|
-
"""Verifies a Firebase App Check token.
|
|
29
|
+
def verify_token(token: str, app=None, consume: bool = False) -> Dict[str, Any]:
|
|
30
|
+
"""Verifies a Firebase App Check token, optionally consuming limited-use tokens.
|
|
30
31
|
|
|
31
32
|
Args:
|
|
32
33
|
token: A token from App Check.
|
|
33
34
|
app: An App instance (optional).
|
|
35
|
+
consume: Set to ``True`` only if the token is a limited-use (one-time) token
|
|
36
|
+
that should be consumed upon verification (optional, defaults to ``False``).
|
|
34
37
|
|
|
35
38
|
Returns:
|
|
36
|
-
Dict[str, Any]: The token's decoded claims.
|
|
39
|
+
Dict[str, Any]: The token's decoded claims. If ``consume`` is ``True``, the dictionary
|
|
40
|
+
also includes an ``already_consumed`` boolean key indicating whether the token was
|
|
41
|
+
previously consumed.
|
|
37
42
|
|
|
38
43
|
Raises:
|
|
39
|
-
ValueError: If the app's ``project_id``
|
|
40
|
-
|
|
44
|
+
ValueError: If ``consume`` is not a boolean, or if the app's ``project_id``
|
|
45
|
+
is invalid or unspecified, or if the token's headers or payload are invalid.
|
|
46
|
+
FirebaseError: If an error occurs while communicating with the App Check service.
|
|
41
47
|
PyJWKClientError: If PyJWKClient fails to fetch a valid signing key.
|
|
42
48
|
"""
|
|
43
|
-
return _get_app_check_service(app).verify_token(token)
|
|
49
|
+
return _get_app_check_service(app).verify_token(token, consume=consume)
|
|
44
50
|
|
|
45
51
|
class _AppCheckService:
|
|
46
52
|
"""Service class that implements Firebase App Check functionality."""
|
|
47
53
|
|
|
48
54
|
_APP_CHECK_ISSUER = 'https://firebaseappcheck.googleapis.com/'
|
|
49
55
|
_JWKS_URL = 'https://firebaseappcheck.googleapis.com/v1/jwks'
|
|
56
|
+
_VERIFY_URL_FORMAT = (
|
|
57
|
+
'https://firebaseappcheck.googleapis.com/v1/projects/{project_id}:verifyAppCheckToken'
|
|
58
|
+
)
|
|
50
59
|
_project_id = None
|
|
51
60
|
_scoped_project_id = None
|
|
52
61
|
_jwks_client = None
|
|
@@ -68,11 +77,15 @@ class _AppCheckService:
|
|
|
68
77
|
# Default lifespan is 300 seconds (5 minutes) so we change it to 21600 seconds (6 hours).
|
|
69
78
|
self._jwks_client = PyJWKClient(
|
|
70
79
|
self._JWKS_URL, lifespan=21600, headers=self._APP_CHECK_HEADERS)
|
|
80
|
+
timeout = app.options.get('httpTimeout', _http_client.DEFAULT_TIMEOUT_SECONDS)
|
|
81
|
+
self._http_client = _http_client.JsonHttpClient(
|
|
82
|
+
credential=app.credential.get_credential(), timeout=timeout)
|
|
71
83
|
|
|
72
84
|
|
|
73
|
-
def verify_token(self, token: str) -> Dict[str, Any]:
|
|
74
|
-
"""Verifies a Firebase App Check token."""
|
|
85
|
+
def verify_token(self, token: str, consume: bool = False) -> Dict[str, Any]:
|
|
86
|
+
"""Verifies a Firebase App Check token, optionally consuming limited-use tokens."""
|
|
75
87
|
_Validators.check_string("app check token", token)
|
|
88
|
+
_Validators.check_boolean("consume", consume)
|
|
76
89
|
|
|
77
90
|
# Obtain the Firebase App Check Public Keys
|
|
78
91
|
# Note: It is not recommended to hard code these keys as they rotate,
|
|
@@ -87,8 +100,32 @@ class _AppCheckService:
|
|
|
87
100
|
) from exception
|
|
88
101
|
|
|
89
102
|
verified_claims['app_id'] = verified_claims.get('sub')
|
|
103
|
+
|
|
104
|
+
if consume:
|
|
105
|
+
verified_claims['already_consumed'] = self._verify_replay_protection(token)
|
|
106
|
+
|
|
90
107
|
return verified_claims
|
|
91
108
|
|
|
109
|
+
def _verify_replay_protection(self, token: str) -> bool:
|
|
110
|
+
"""Verifies replay protection with the backend and returns the alreadyConsumed status."""
|
|
111
|
+
url = self._VERIFY_URL_FORMAT.format(project_id=self._project_id)
|
|
112
|
+
try:
|
|
113
|
+
body = self._http_client.body('post', url, json={'app_check_token': token})
|
|
114
|
+
except requests.exceptions.RequestException as error:
|
|
115
|
+
raise _utils.handle_platform_error_from_requests(error)
|
|
116
|
+
except ValueError as error:
|
|
117
|
+
raise exceptions.UnknownError(
|
|
118
|
+
f'Unexpected response from App Check service: {error}'
|
|
119
|
+
) from error
|
|
120
|
+
|
|
121
|
+
if not isinstance(body, dict):
|
|
122
|
+
raise exceptions.UnknownError(
|
|
123
|
+
'Unexpected response from App Check service. '
|
|
124
|
+
f'Expected a JSON object, but got {type(body).__name__}.'
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
return bool(body.get('alreadyConsumed', False))
|
|
128
|
+
|
|
92
129
|
def _has_valid_token_headers(self, headers: Any) -> None:
|
|
93
130
|
"""Checks whether the token has valid headers for App Check."""
|
|
94
131
|
# Ensure the token's header has type JWT
|
|
@@ -159,3 +196,10 @@ class _Validators:
|
|
|
159
196
|
raise ValueError(f'{label} "{value}" must be a non-empty string.')
|
|
160
197
|
if not isinstance(value, str):
|
|
161
198
|
raise ValueError(f'{label} "{value}" must be a string.')
|
|
199
|
+
|
|
200
|
+
@classmethod
|
|
201
|
+
def check_boolean(cls, label: str, value: Any):
|
|
202
|
+
"""Checks if the given value is a boolean."""
|
|
203
|
+
if not isinstance(value, bool):
|
|
204
|
+
raise ValueError(f'{label} must be a boolean.')
|
|
205
|
+
return value
|
|
@@ -14,16 +14,25 @@
|
|
|
14
14
|
|
|
15
15
|
"""Firebase ML module.
|
|
16
16
|
|
|
17
|
+
.. deprecated::
|
|
18
|
+
Firebase ML is deprecated and will be shut down on June 15, 2027.
|
|
19
|
+
To host custom models, you must migrate to another solution. You can use Cloud Storage for
|
|
20
|
+
Firebase as an alternative for hosting custom models. For more info, see
|
|
21
|
+
https://firebase.google.com/docs/ml/migrate-to-cloud-storage
|
|
22
|
+
|
|
17
23
|
This module contains functions for creating, updating, getting, listing,
|
|
18
24
|
deleting, publishing and unpublishing Firebase ML models.
|
|
19
25
|
"""
|
|
20
26
|
|
|
27
|
+
# pylint: disable=too-many-lines
|
|
21
28
|
|
|
22
29
|
import datetime
|
|
30
|
+
import os
|
|
23
31
|
import re
|
|
32
|
+
import sys
|
|
24
33
|
import time
|
|
25
|
-
import os
|
|
26
34
|
from urllib import parse
|
|
35
|
+
import warnings
|
|
27
36
|
|
|
28
37
|
import requests
|
|
29
38
|
|
|
@@ -46,6 +55,30 @@ try:
|
|
|
46
55
|
except ImportError:
|
|
47
56
|
_TF_ENABLED = False
|
|
48
57
|
|
|
58
|
+
_DEPRECATION_WARNING = (
|
|
59
|
+
'Firebase ML is deprecated and will be shut down on June 15, 2027. '
|
|
60
|
+
'To host custom models, you must migrate to another solution. You can use Cloud Storage for '
|
|
61
|
+
'Firebase as an alternative for hosting custom models. For more info, see '
|
|
62
|
+
'https://firebase.google.com/docs/ml/migrate-to-cloud-storage'
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _is_internal_call():
|
|
67
|
+
"""Checks if the current call originated from within the ML module."""
|
|
68
|
+
try:
|
|
69
|
+
# pylint: disable=protected-access
|
|
70
|
+
frame = sys._getframe(2)
|
|
71
|
+
while frame:
|
|
72
|
+
if frame.f_code.co_name in (
|
|
73
|
+
'from_dict', '_update_from_dict', 'handle_operation', 'list_models'
|
|
74
|
+
):
|
|
75
|
+
return True
|
|
76
|
+
frame = frame.f_back
|
|
77
|
+
except (AttributeError, ValueError):
|
|
78
|
+
pass
|
|
79
|
+
return False
|
|
80
|
+
|
|
81
|
+
|
|
49
82
|
_ML_ATTRIBUTE = '_ml'
|
|
50
83
|
_MAX_PAGE_SIZE = 100
|
|
51
84
|
_MODEL_ID_PATTERN = re.compile(r'^[A-Za-z0-9_-]{1,60}$')
|
|
@@ -77,6 +110,12 @@ def _get_ml_service(app):
|
|
|
77
110
|
def create_model(model, app=None):
|
|
78
111
|
"""Creates a model in the current Firebase project.
|
|
79
112
|
|
|
113
|
+
.. deprecated::
|
|
114
|
+
Firebase ML is deprecated and will be shut down on June 15, 2027.
|
|
115
|
+
To host custom models, you must migrate to another solution. You can use Cloud Storage for
|
|
116
|
+
Firebase as an alternative for hosting custom models. For more info, see
|
|
117
|
+
https://firebase.google.com/docs/ml/migrate-to-cloud-storage
|
|
118
|
+
|
|
80
119
|
Args:
|
|
81
120
|
model: An ml.Model to create.
|
|
82
121
|
app: A Firebase app instance (or None to use the default app).
|
|
@@ -84,6 +123,8 @@ def create_model(model, app=None):
|
|
|
84
123
|
Returns:
|
|
85
124
|
Model: The model that was created in Firebase ML.
|
|
86
125
|
"""
|
|
126
|
+
if not _is_internal_call():
|
|
127
|
+
warnings.warn(_DEPRECATION_WARNING, DeprecationWarning, stacklevel=2)
|
|
87
128
|
ml_service = _get_ml_service(app)
|
|
88
129
|
return Model.from_dict(ml_service.create_model(model), app=app)
|
|
89
130
|
|
|
@@ -91,6 +132,12 @@ def create_model(model, app=None):
|
|
|
91
132
|
def update_model(model, app=None):
|
|
92
133
|
"""Updates a model's metadata or model file.
|
|
93
134
|
|
|
135
|
+
.. deprecated::
|
|
136
|
+
Firebase ML is deprecated and will be shut down on June 15, 2027.
|
|
137
|
+
To host custom models, you must migrate to another solution. You can use Cloud Storage for
|
|
138
|
+
Firebase as an alternative for hosting custom models. For more info, see
|
|
139
|
+
https://firebase.google.com/docs/ml/migrate-to-cloud-storage
|
|
140
|
+
|
|
94
141
|
Args:
|
|
95
142
|
model: The ml.Model to update.
|
|
96
143
|
app: A Firebase app instance (or None to use the default app).
|
|
@@ -98,6 +145,8 @@ def update_model(model, app=None):
|
|
|
98
145
|
Returns:
|
|
99
146
|
Model: The updated model.
|
|
100
147
|
"""
|
|
148
|
+
if not _is_internal_call():
|
|
149
|
+
warnings.warn(_DEPRECATION_WARNING, DeprecationWarning, stacklevel=2)
|
|
101
150
|
ml_service = _get_ml_service(app)
|
|
102
151
|
return Model.from_dict(ml_service.update_model(model), app=app)
|
|
103
152
|
|
|
@@ -105,6 +154,12 @@ def update_model(model, app=None):
|
|
|
105
154
|
def publish_model(model_id, app=None):
|
|
106
155
|
"""Publishes a Firebase ML model.
|
|
107
156
|
|
|
157
|
+
.. deprecated::
|
|
158
|
+
Firebase ML is deprecated and will be shut down on June 15, 2027.
|
|
159
|
+
To host custom models, you must migrate to another solution. You can use Cloud Storage for
|
|
160
|
+
Firebase as an alternative for hosting custom models. For more info, see
|
|
161
|
+
https://firebase.google.com/docs/ml/migrate-to-cloud-storage
|
|
162
|
+
|
|
108
163
|
A published model can be downloaded to client apps.
|
|
109
164
|
|
|
110
165
|
Args:
|
|
@@ -114,6 +169,8 @@ def publish_model(model_id, app=None):
|
|
|
114
169
|
Returns:
|
|
115
170
|
Model: The published model.
|
|
116
171
|
"""
|
|
172
|
+
if not _is_internal_call():
|
|
173
|
+
warnings.warn(_DEPRECATION_WARNING, DeprecationWarning, stacklevel=2)
|
|
117
174
|
ml_service = _get_ml_service(app)
|
|
118
175
|
return Model.from_dict(ml_service.set_published(model_id, publish=True), app=app)
|
|
119
176
|
|
|
@@ -121,6 +178,12 @@ def publish_model(model_id, app=None):
|
|
|
121
178
|
def unpublish_model(model_id, app=None):
|
|
122
179
|
"""Unpublishes a Firebase ML model.
|
|
123
180
|
|
|
181
|
+
.. deprecated::
|
|
182
|
+
Firebase ML is deprecated and will be shut down on June 15, 2027.
|
|
183
|
+
To host custom models, you must migrate to another solution. You can use Cloud Storage for
|
|
184
|
+
Firebase as an alternative for hosting custom models. For more info, see
|
|
185
|
+
https://firebase.google.com/docs/ml/migrate-to-cloud-storage
|
|
186
|
+
|
|
124
187
|
Args:
|
|
125
188
|
model_id: The id of the model to unpublish.
|
|
126
189
|
app: A Firebase app instance (or None to use the default app).
|
|
@@ -128,6 +191,8 @@ def unpublish_model(model_id, app=None):
|
|
|
128
191
|
Returns:
|
|
129
192
|
Model: The unpublished model.
|
|
130
193
|
"""
|
|
194
|
+
if not _is_internal_call():
|
|
195
|
+
warnings.warn(_DEPRECATION_WARNING, DeprecationWarning, stacklevel=2)
|
|
131
196
|
ml_service = _get_ml_service(app)
|
|
132
197
|
return Model.from_dict(ml_service.set_published(model_id, publish=False), app=app)
|
|
133
198
|
|
|
@@ -135,6 +200,12 @@ def unpublish_model(model_id, app=None):
|
|
|
135
200
|
def get_model(model_id, app=None):
|
|
136
201
|
"""Gets the model specified by the given ID.
|
|
137
202
|
|
|
203
|
+
.. deprecated::
|
|
204
|
+
Firebase ML is deprecated and will be shut down on June 15, 2027.
|
|
205
|
+
To host custom models, you must migrate to another solution. You can use Cloud Storage for
|
|
206
|
+
Firebase as an alternative for hosting custom models. For more info, see
|
|
207
|
+
https://firebase.google.com/docs/ml/migrate-to-cloud-storage
|
|
208
|
+
|
|
138
209
|
Args:
|
|
139
210
|
model_id: The id of the model to get.
|
|
140
211
|
app: A Firebase app instance (or None to use the default app).
|
|
@@ -142,6 +213,8 @@ def get_model(model_id, app=None):
|
|
|
142
213
|
Returns:
|
|
143
214
|
Model: The requested model.
|
|
144
215
|
"""
|
|
216
|
+
if not _is_internal_call():
|
|
217
|
+
warnings.warn(_DEPRECATION_WARNING, DeprecationWarning, stacklevel=2)
|
|
145
218
|
ml_service = _get_ml_service(app)
|
|
146
219
|
return Model.from_dict(ml_service.get_model(model_id), app=app)
|
|
147
220
|
|
|
@@ -149,6 +222,12 @@ def get_model(model_id, app=None):
|
|
|
149
222
|
def list_models(list_filter=None, page_size=None, page_token=None, app=None):
|
|
150
223
|
"""Lists the current project's models.
|
|
151
224
|
|
|
225
|
+
.. deprecated::
|
|
226
|
+
Firebase ML is deprecated and will be shut down on June 15, 2027.
|
|
227
|
+
To host custom models, you must migrate to another solution. You can use Cloud Storage for
|
|
228
|
+
Firebase as an alternative for hosting custom models. For more info, see
|
|
229
|
+
https://firebase.google.com/docs/ml/migrate-to-cloud-storage
|
|
230
|
+
|
|
152
231
|
Args:
|
|
153
232
|
list_filter: a list filter string such as ``tags:'tag_1'``. None will return all models.
|
|
154
233
|
page_size: A number between 1 and 100 inclusive that specifies the maximum
|
|
@@ -160,6 +239,8 @@ def list_models(list_filter=None, page_size=None, page_token=None, app=None):
|
|
|
160
239
|
Returns:
|
|
161
240
|
ListModelsPage: A (filtered) list of models.
|
|
162
241
|
"""
|
|
242
|
+
if not _is_internal_call():
|
|
243
|
+
warnings.warn(_DEPRECATION_WARNING, DeprecationWarning, stacklevel=2)
|
|
163
244
|
ml_service = _get_ml_service(app)
|
|
164
245
|
return ListModelsPage(
|
|
165
246
|
ml_service.list_models, list_filter, page_size, page_token, app=app)
|
|
@@ -168,10 +249,18 @@ def list_models(list_filter=None, page_size=None, page_token=None, app=None):
|
|
|
168
249
|
def delete_model(model_id, app=None):
|
|
169
250
|
"""Deletes a model from the current project.
|
|
170
251
|
|
|
252
|
+
.. deprecated::
|
|
253
|
+
Firebase ML is deprecated and will be shut down on June 15, 2027.
|
|
254
|
+
To host custom models, you must migrate to another solution. You can use Cloud Storage for
|
|
255
|
+
Firebase as an alternative for hosting custom models. For more info, see
|
|
256
|
+
https://firebase.google.com/docs/ml/migrate-to-cloud-storage
|
|
257
|
+
|
|
171
258
|
Args:
|
|
172
259
|
model_id: The id of the model you wish to delete.
|
|
173
260
|
app: A Firebase app instance (or None to use the default app).
|
|
174
261
|
"""
|
|
262
|
+
if not _is_internal_call():
|
|
263
|
+
warnings.warn(_DEPRECATION_WARNING, DeprecationWarning, stacklevel=2)
|
|
175
264
|
ml_service = _get_ml_service(app)
|
|
176
265
|
ml_service.delete_model(model_id)
|
|
177
266
|
|
|
@@ -179,12 +268,20 @@ def delete_model(model_id, app=None):
|
|
|
179
268
|
class Model:
|
|
180
269
|
"""A Firebase ML Model object.
|
|
181
270
|
|
|
271
|
+
.. deprecated::
|
|
272
|
+
Firebase ML is deprecated and will be shut down on June 15, 2027.
|
|
273
|
+
To host custom models, you must migrate to another solution. You can use Cloud Storage for
|
|
274
|
+
Firebase as an alternative for hosting custom models. For more info, see
|
|
275
|
+
https://firebase.google.com/docs/ml/migrate-to-cloud-storage
|
|
276
|
+
|
|
182
277
|
Args:
|
|
183
278
|
display_name: The display name of your model - used to identify your model in code.
|
|
184
279
|
tags: Optional list of strings associated with your model. Can be used in list queries.
|
|
185
280
|
model_format: A subclass of ModelFormat. (e.g. TFLiteFormat) Specifies the model details.
|
|
186
281
|
"""
|
|
187
282
|
def __init__(self, display_name=None, tags=None, model_format=None):
|
|
283
|
+
if not _is_internal_call():
|
|
284
|
+
warnings.warn(_DEPRECATION_WARNING, DeprecationWarning, stacklevel=2)
|
|
188
285
|
self._app = None # Only needed for wait_for_unlo
|
|
189
286
|
self._data = {}
|
|
190
287
|
self._model_format = None
|
|
@@ -342,7 +439,14 @@ class Model:
|
|
|
342
439
|
|
|
343
440
|
|
|
344
441
|
class ModelFormat:
|
|
345
|
-
"""Abstract base class representing a Model Format such as TFLite.
|
|
442
|
+
"""Abstract base class representing a Model Format such as TFLite.
|
|
443
|
+
|
|
444
|
+
.. deprecated::
|
|
445
|
+
Firebase ML is deprecated and will be shut down on June 15, 2027.
|
|
446
|
+
To host custom models, you must migrate to another solution. You can use Cloud Storage for
|
|
447
|
+
Firebase as an alternative for hosting custom models. For more info, see
|
|
448
|
+
https://firebase.google.com/docs/ml/migrate-to-cloud-storage
|
|
449
|
+
"""
|
|
346
450
|
def as_dict(self, for_upload=False):
|
|
347
451
|
"""Returns a serializable representation of the object."""
|
|
348
452
|
raise NotImplementedError
|
|
@@ -351,10 +455,18 @@ class ModelFormat:
|
|
|
351
455
|
class TFLiteFormat(ModelFormat):
|
|
352
456
|
"""Model format representing a TFLite model.
|
|
353
457
|
|
|
458
|
+
.. deprecated::
|
|
459
|
+
Firebase ML is deprecated and will be shut down on June 15, 2027.
|
|
460
|
+
To host custom models, you must migrate to another solution. You can use Cloud Storage for
|
|
461
|
+
Firebase as an alternative for hosting custom models. For more info, see
|
|
462
|
+
https://firebase.google.com/docs/ml/migrate-to-cloud-storage
|
|
463
|
+
|
|
354
464
|
Args:
|
|
355
465
|
model_source: A TFLiteModelSource sub class. Specifies the details of the model source.
|
|
356
466
|
"""
|
|
357
467
|
def __init__(self, model_source=None):
|
|
468
|
+
if not _is_internal_call():
|
|
469
|
+
warnings.warn(_DEPRECATION_WARNING, DeprecationWarning, stacklevel=2)
|
|
358
470
|
self._data = {}
|
|
359
471
|
self._model_source = None
|
|
360
472
|
|
|
@@ -412,7 +524,14 @@ class TFLiteFormat(ModelFormat):
|
|
|
412
524
|
|
|
413
525
|
|
|
414
526
|
class TFLiteModelSource:
|
|
415
|
-
"""Abstract base class representing a model source for TFLite format models.
|
|
527
|
+
"""Abstract base class representing a model source for TFLite format models.
|
|
528
|
+
|
|
529
|
+
.. deprecated::
|
|
530
|
+
Firebase ML is deprecated and will be shut down on June 15, 2027.
|
|
531
|
+
To host custom models, you must migrate to another solution. You can use Cloud Storage for
|
|
532
|
+
Firebase as an alternative for hosting custom models. For more info, see
|
|
533
|
+
https://firebase.google.com/docs/ml/migrate-to-cloud-storage
|
|
534
|
+
"""
|
|
416
535
|
def as_dict(self, for_upload=False):
|
|
417
536
|
"""Returns a serializable representation of the object."""
|
|
418
537
|
raise NotImplementedError
|
|
@@ -466,11 +585,20 @@ class _CloudStorageClient:
|
|
|
466
585
|
|
|
467
586
|
|
|
468
587
|
class TFLiteGCSModelSource(TFLiteModelSource):
|
|
469
|
-
"""TFLite model source representing a tflite model file stored in GCS.
|
|
588
|
+
"""TFLite model source representing a tflite model file stored in GCS.
|
|
589
|
+
|
|
590
|
+
.. deprecated::
|
|
591
|
+
Firebase ML is deprecated and will be shut down on June 15, 2027.
|
|
592
|
+
To host custom models, you must migrate to another solution. You can use Cloud Storage for
|
|
593
|
+
Firebase as an alternative for hosting custom models. For more info, see
|
|
594
|
+
https://firebase.google.com/docs/ml/migrate-to-cloud-storage
|
|
595
|
+
"""
|
|
470
596
|
|
|
471
597
|
_STORAGE_CLIENT = _CloudStorageClient()
|
|
472
598
|
|
|
473
599
|
def __init__(self, gcs_tflite_uri, app=None):
|
|
600
|
+
if not _is_internal_call():
|
|
601
|
+
warnings.warn(_DEPRECATION_WARNING, DeprecationWarning, stacklevel=2)
|
|
474
602
|
self._app = app
|
|
475
603
|
self._gcs_tflite_uri = _validate_gcs_tflite_uri(gcs_tflite_uri)
|
|
476
604
|
|
|
@@ -600,12 +728,20 @@ class TFLiteGCSModelSource(TFLiteModelSource):
|
|
|
600
728
|
class ListModelsPage:
|
|
601
729
|
"""Represents a page of models in a Firebase project.
|
|
602
730
|
|
|
731
|
+
.. deprecated::
|
|
732
|
+
Firebase ML is deprecated and will be shut down on June 15, 2027.
|
|
733
|
+
To host custom models, you must migrate to another solution. You can use Cloud Storage for
|
|
734
|
+
Firebase as an alternative for hosting custom models. For more info, see
|
|
735
|
+
https://firebase.google.com/docs/ml/migrate-to-cloud-storage
|
|
736
|
+
|
|
603
737
|
Provides methods for traversing the models included in this page, as well as
|
|
604
738
|
retrieving subsequent pages of models. The iterator returned by
|
|
605
739
|
``iterate_all()`` can be used to iterate through all the models in the
|
|
606
740
|
Firebase project starting from this page.
|
|
607
741
|
"""
|
|
608
742
|
def __init__(self, list_models_func, list_filter, page_size, page_token, app):
|
|
743
|
+
if not _is_internal_call():
|
|
744
|
+
warnings.warn(_DEPRECATION_WARNING, DeprecationWarning, stacklevel=2)
|
|
609
745
|
self._list_models_func = list_models_func
|
|
610
746
|
self._list_filter = list_filter
|
|
611
747
|
self._page_size = page_size
|
|
@@ -15,11 +15,12 @@
|
|
|
15
15
|
"""Test cases for the firebase_admin.app_check module."""
|
|
16
16
|
import base64
|
|
17
17
|
import pytest
|
|
18
|
+
import requests
|
|
18
19
|
|
|
19
20
|
from jwt import PyJWK, InvalidAudienceError, InvalidIssuerError
|
|
20
21
|
from jwt import ExpiredSignatureError, InvalidSignatureError
|
|
21
22
|
import firebase_admin
|
|
22
|
-
from firebase_admin import app_check
|
|
23
|
+
from firebase_admin import app_check, exceptions
|
|
23
24
|
from tests import testutils
|
|
24
25
|
|
|
25
26
|
NON_STRING_ARGS = [[], tuple(), {}, True, False, 1, 0]
|
|
@@ -58,6 +59,15 @@ class TestBatch:
|
|
|
58
59
|
def teardown_class(cls):
|
|
59
60
|
testutils.cleanup_apps()
|
|
60
61
|
|
|
62
|
+
|
|
63
|
+
@pytest.fixture
|
|
64
|
+
def mock_jwt(mocker):
|
|
65
|
+
"""Mocks JWT decoding and JWKS key retrieval for a valid App Check token."""
|
|
66
|
+
mocker.patch("jwt.decode", return_value=JWT_PAYLOAD_SAMPLE)
|
|
67
|
+
mocker.patch("jwt.PyJWKClient.get_signing_key_from_jwt", return_value=PyJWK(signing_key))
|
|
68
|
+
mocker.patch("jwt.get_unverified_header", return_value=JWT_PAYLOAD_SAMPLE.get("headers"))
|
|
69
|
+
|
|
70
|
+
|
|
61
71
|
class TestVerifyToken(TestBatch):
|
|
62
72
|
|
|
63
73
|
def test_no_project_id(self):
|
|
@@ -74,6 +84,12 @@ class TestVerifyToken(TestBatch):
|
|
|
74
84
|
expected = f'app check token "{token}" must be a string.'
|
|
75
85
|
assert str(excinfo.value) == expected
|
|
76
86
|
|
|
87
|
+
@pytest.mark.parametrize('consume', [[], tuple(), {}, 1, 0, 'true', 'false', None])
|
|
88
|
+
def test_verify_token_with_non_boolean_consume_raises_error(self, consume):
|
|
89
|
+
with pytest.raises(ValueError) as excinfo:
|
|
90
|
+
app_check.verify_token('app_check_token', consume=consume)
|
|
91
|
+
assert str(excinfo.value) == 'consume must be a boolean.'
|
|
92
|
+
|
|
77
93
|
def test_has_valid_token_headers(self):
|
|
78
94
|
app = firebase_admin.get_app()
|
|
79
95
|
app_check_service = app_check._get_app_check_service(app)
|
|
@@ -221,10 +237,8 @@ class TestVerifyToken(TestBatch):
|
|
|
221
237
|
f'"{sub_number}" must be a string.')
|
|
222
238
|
assert str(excinfo.value) == expected
|
|
223
239
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
mocker.patch("jwt.PyJWKClient.get_signing_key_from_jwt", return_value=PyJWK(signing_key))
|
|
227
|
-
mocker.patch("jwt.get_unverified_header", return_value=JWT_PAYLOAD_SAMPLE.get("headers"))
|
|
240
|
+
@pytest.mark.usefixtures("mock_jwt")
|
|
241
|
+
def test_verify_token(self):
|
|
228
242
|
app = firebase_admin.get_app()
|
|
229
243
|
|
|
230
244
|
payload = app_check.verify_token("encoded", app)
|
|
@@ -273,3 +287,120 @@ class TestVerifyToken(TestBatch):
|
|
|
273
287
|
|
|
274
288
|
expected = 'Token does not contain the correct "iss" (issuer).'
|
|
275
289
|
assert str(excinfo.value) == expected
|
|
290
|
+
|
|
291
|
+
@pytest.mark.usefixtures("mock_jwt")
|
|
292
|
+
@pytest.mark.parametrize('consume_kwargs', [{}, {'consume': False}])
|
|
293
|
+
def test_verify_token_with_consume_false_makes_no_http_call(
|
|
294
|
+
self, mocker, consume_kwargs
|
|
295
|
+
):
|
|
296
|
+
app = firebase_admin.get_app()
|
|
297
|
+
app_check_service = app_check._get_app_check_service(app)
|
|
298
|
+
mock_body = mocker.patch.object(app_check_service._http_client, "body")
|
|
299
|
+
|
|
300
|
+
payload = app_check.verify_token("encoded", app=app, **consume_kwargs)
|
|
301
|
+
expected = JWT_PAYLOAD_SAMPLE.copy()
|
|
302
|
+
expected["app_id"] = APP_ID
|
|
303
|
+
assert payload == expected
|
|
304
|
+
assert 'already_consumed' not in payload
|
|
305
|
+
mock_body.assert_not_called()
|
|
306
|
+
|
|
307
|
+
@pytest.mark.usefixtures("mock_jwt")
|
|
308
|
+
def test_verify_token_with_consume_true_not_consumed(self, mocker):
|
|
309
|
+
app = firebase_admin.get_app()
|
|
310
|
+
app_check_service = app_check._get_app_check_service(app)
|
|
311
|
+
mock_body = mocker.patch.object(
|
|
312
|
+
app_check_service._http_client, "body", return_value={"alreadyConsumed": False}
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
payload = app_check.verify_token("encoded", app=app, consume=True)
|
|
316
|
+
expected = JWT_PAYLOAD_SAMPLE.copy()
|
|
317
|
+
expected["app_id"] = APP_ID
|
|
318
|
+
expected["already_consumed"] = False
|
|
319
|
+
assert payload == expected
|
|
320
|
+
|
|
321
|
+
expected_url = (
|
|
322
|
+
f"https://firebaseappcheck.googleapis.com/v1/projects/{PROJECT_ID}:verifyAppCheckToken"
|
|
323
|
+
)
|
|
324
|
+
mock_body.assert_called_once_with(
|
|
325
|
+
"post", expected_url, json={"app_check_token": "encoded"}
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
@pytest.mark.usefixtures("mock_jwt")
|
|
329
|
+
def test_verify_token_with_consume_true_already_consumed(self, mocker):
|
|
330
|
+
app = firebase_admin.get_app()
|
|
331
|
+
app_check_service = app_check._get_app_check_service(app)
|
|
332
|
+
mock_body = mocker.patch.object(
|
|
333
|
+
app_check_service._http_client, "body", return_value={"alreadyConsumed": True}
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
payload = app_check.verify_token("encoded", app=app, consume=True)
|
|
337
|
+
expected = JWT_PAYLOAD_SAMPLE.copy()
|
|
338
|
+
expected["app_id"] = APP_ID
|
|
339
|
+
expected["already_consumed"] = True
|
|
340
|
+
assert payload == expected
|
|
341
|
+
|
|
342
|
+
expected_url = (
|
|
343
|
+
f"https://firebaseappcheck.googleapis.com/v1/projects/{PROJECT_ID}:verifyAppCheckToken"
|
|
344
|
+
)
|
|
345
|
+
mock_body.assert_called_once_with(
|
|
346
|
+
"post", expected_url, json={"app_check_token": "encoded"}
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
@pytest.mark.usefixtures("mock_jwt")
|
|
350
|
+
def test_verify_token_with_consume_true_backend_error(self, mocker):
|
|
351
|
+
app = firebase_admin.get_app()
|
|
352
|
+
app_check_service = app_check._get_app_check_service(app)
|
|
353
|
+
|
|
354
|
+
req_exc = requests.exceptions.RequestException("Backend error")
|
|
355
|
+
mocker.patch.object(app_check_service._http_client, "body", side_effect=req_exc)
|
|
356
|
+
|
|
357
|
+
with pytest.raises(exceptions.UnknownError) as excinfo:
|
|
358
|
+
app_check.verify_token("encoded", app=app, consume=True)
|
|
359
|
+
assert "Unknown error while making a remote service call" in str(excinfo.value)
|
|
360
|
+
|
|
361
|
+
@pytest.mark.usefixtures("mock_jwt")
|
|
362
|
+
def test_verify_token_with_consume_true_http_error(self, mocker):
|
|
363
|
+
app = firebase_admin.get_app()
|
|
364
|
+
app_check_service = app_check._get_app_check_service(app)
|
|
365
|
+
|
|
366
|
+
response = requests.Response()
|
|
367
|
+
response.status_code = 403
|
|
368
|
+
response._content = (
|
|
369
|
+
b'{"error": {"status": "PERMISSION_DENIED", "message": "Permission denied."}}'
|
|
370
|
+
)
|
|
371
|
+
http_exc = requests.exceptions.HTTPError(response=response)
|
|
372
|
+
mocker.patch.object(app_check_service._http_client, "body", side_effect=http_exc)
|
|
373
|
+
|
|
374
|
+
with pytest.raises(exceptions.PermissionDeniedError) as excinfo:
|
|
375
|
+
app_check.verify_token("encoded", app=app, consume=True)
|
|
376
|
+
assert "Permission denied." in str(excinfo.value)
|
|
377
|
+
|
|
378
|
+
@pytest.mark.usefixtures("mock_jwt")
|
|
379
|
+
@pytest.mark.parametrize('malformed_body', ['string_response', [1, 2], 123, None])
|
|
380
|
+
def test_verify_token_with_consume_true_malformed_response_raises_error(
|
|
381
|
+
self, mocker, malformed_body
|
|
382
|
+
):
|
|
383
|
+
app = firebase_admin.get_app()
|
|
384
|
+
app_check_service = app_check._get_app_check_service(app)
|
|
385
|
+
mocker.patch.object(app_check_service._http_client, "body", return_value=malformed_body)
|
|
386
|
+
|
|
387
|
+
with pytest.raises(exceptions.UnknownError) as excinfo:
|
|
388
|
+
app_check.verify_token("encoded", app=app, consume=True)
|
|
389
|
+
assert 'Unexpected response from App Check service' in str(excinfo.value)
|
|
390
|
+
|
|
391
|
+
@pytest.mark.usefixtures("mock_jwt")
|
|
392
|
+
def test_verify_token_with_consume_true_json_decode_error(self, mocker):
|
|
393
|
+
app = firebase_admin.get_app()
|
|
394
|
+
app_check_service = app_check._get_app_check_service(app)
|
|
395
|
+
mocker.patch.object(
|
|
396
|
+
app_check_service._http_client,
|
|
397
|
+
"body",
|
|
398
|
+
side_effect=ValueError("Expecting value: line 1 column 1 (char 0)"),
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
with pytest.raises(exceptions.UnknownError) as excinfo:
|
|
402
|
+
app_check.verify_token("encoded", app=app, consume=True)
|
|
403
|
+
expected_msg = (
|
|
404
|
+
"Unexpected response from App Check service: Expecting value: line 1 column 1 (char 0)"
|
|
405
|
+
)
|
|
406
|
+
assert expected_msg in str(excinfo.value)
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"""Test cases for the firebase_admin.ml module."""
|
|
16
16
|
|
|
17
17
|
import json
|
|
18
|
+
import warnings
|
|
18
19
|
|
|
19
20
|
import pytest
|
|
20
21
|
|
|
@@ -1086,3 +1087,89 @@ class TestListModels:
|
|
|
1086
1087
|
assert len(page.models) == 0
|
|
1087
1088
|
models = list(page.iterate_all())
|
|
1088
1089
|
assert len(models) == 0
|
|
1090
|
+
|
|
1091
|
+
|
|
1092
|
+
class TestMLDeprecation:
|
|
1093
|
+
"""Tests for verifying deprecation warnings in the ML module."""
|
|
1094
|
+
|
|
1095
|
+
DEPRECATION_MSG = (
|
|
1096
|
+
r'Firebase ML is deprecated and will be shut down on June 15, 2027\. '
|
|
1097
|
+
r'To host custom models, you must migrate to another solution\. '
|
|
1098
|
+
r'You can use Cloud Storage for Firebase as an alternative for hosting custom models\. '
|
|
1099
|
+
r'For more info, see https://firebase\.google\.com/docs/ml/migrate-to-cloud-storage'
|
|
1100
|
+
)
|
|
1101
|
+
|
|
1102
|
+
@classmethod
|
|
1103
|
+
def setup_class(cls):
|
|
1104
|
+
cred = testutils.MockCredential()
|
|
1105
|
+
firebase_admin.initialize_app(cred, {'projectId': PROJECT_ID})
|
|
1106
|
+
ml._MLService.POLL_BASE_WAIT_TIME_SECONDS = 0.1
|
|
1107
|
+
ml.TFLiteGCSModelSource._STORAGE_CLIENT = _TestStorageClient()
|
|
1108
|
+
|
|
1109
|
+
@classmethod
|
|
1110
|
+
def teardown_class(cls):
|
|
1111
|
+
testutils.cleanup_apps()
|
|
1112
|
+
|
|
1113
|
+
def test_model_init_deprecation_warning(self):
|
|
1114
|
+
with pytest.warns(DeprecationWarning, match=self.DEPRECATION_MSG):
|
|
1115
|
+
ml.Model()
|
|
1116
|
+
|
|
1117
|
+
def test_tflite_format_init_deprecation_warning(self):
|
|
1118
|
+
with pytest.warns(DeprecationWarning, match=self.DEPRECATION_MSG):
|
|
1119
|
+
ml.TFLiteFormat()
|
|
1120
|
+
|
|
1121
|
+
def test_tflite_gcs_model_source_init_deprecation_warning(self):
|
|
1122
|
+
with pytest.warns(DeprecationWarning, match=self.DEPRECATION_MSG):
|
|
1123
|
+
ml.TFLiteGCSModelSource(GCS_TFLITE_URI)
|
|
1124
|
+
|
|
1125
|
+
def test_get_model_deprecation_warning(self):
|
|
1126
|
+
instrument_ml_service(status=200, payload=DEFAULT_GET_RESPONSE)
|
|
1127
|
+
with pytest.warns(DeprecationWarning, match=self.DEPRECATION_MSG):
|
|
1128
|
+
ml.get_model(MODEL_ID_1)
|
|
1129
|
+
|
|
1130
|
+
def test_list_models_deprecation_warning(self):
|
|
1131
|
+
instrument_ml_service(status=200, payload=ONE_PAGE_LIST_RESPONSE)
|
|
1132
|
+
with pytest.warns(DeprecationWarning, match=self.DEPRECATION_MSG):
|
|
1133
|
+
ml.list_models()
|
|
1134
|
+
|
|
1135
|
+
def test_delete_model_deprecation_warning(self):
|
|
1136
|
+
instrument_ml_service(status=200, payload=EMPTY_RESPONSE)
|
|
1137
|
+
with pytest.warns(DeprecationWarning, match=self.DEPRECATION_MSG):
|
|
1138
|
+
ml.delete_model(MODEL_ID_1)
|
|
1139
|
+
|
|
1140
|
+
def test_create_model_deprecation_warning(self):
|
|
1141
|
+
instrument_ml_service(status=200, payload=OPERATION_DONE_RESPONSE)
|
|
1142
|
+
with pytest.warns(DeprecationWarning, match=self.DEPRECATION_MSG):
|
|
1143
|
+
ml.create_model(ml.Model(display_name=DISPLAY_NAME_1))
|
|
1144
|
+
|
|
1145
|
+
def test_update_model_deprecation_warning(self):
|
|
1146
|
+
instrument_ml_service(status=200, payload=OPERATION_DONE_RESPONSE)
|
|
1147
|
+
with pytest.warns(DeprecationWarning, match=self.DEPRECATION_MSG):
|
|
1148
|
+
ml.update_model(ml.Model(display_name=DISPLAY_NAME_1))
|
|
1149
|
+
|
|
1150
|
+
def test_publish_model_deprecation_warning(self):
|
|
1151
|
+
instrument_ml_service(status=200, payload=OPERATION_DONE_RESPONSE)
|
|
1152
|
+
with pytest.warns(DeprecationWarning, match=self.DEPRECATION_MSG):
|
|
1153
|
+
ml.publish_model(MODEL_ID_1)
|
|
1154
|
+
|
|
1155
|
+
def test_unpublish_model_deprecation_warning(self):
|
|
1156
|
+
instrument_ml_service(status=200, payload=OPERATION_DONE_RESPONSE)
|
|
1157
|
+
with pytest.warns(DeprecationWarning, match=self.DEPRECATION_MSG):
|
|
1158
|
+
ml.unpublish_model(MODEL_ID_1)
|
|
1159
|
+
|
|
1160
|
+
def test_single_warning_on_get_model(self):
|
|
1161
|
+
instrument_ml_service(status=200, payload=DEFAULT_GET_RESPONSE)
|
|
1162
|
+
with warnings.catch_warnings(record=True) as captured_warnings:
|
|
1163
|
+
warnings.simplefilter('always')
|
|
1164
|
+
ml.get_model(MODEL_ID_1)
|
|
1165
|
+
assert len(captured_warnings) == 1
|
|
1166
|
+
assert issubclass(captured_warnings[0].category, DeprecationWarning)
|
|
1167
|
+
|
|
1168
|
+
def test_single_warning_on_list_models(self):
|
|
1169
|
+
instrument_ml_service(status=200, payload=ONE_PAGE_LIST_RESPONSE)
|
|
1170
|
+
with warnings.catch_warnings(record=True) as captured_warnings:
|
|
1171
|
+
warnings.simplefilter('always')
|
|
1172
|
+
page = ml.list_models()
|
|
1173
|
+
_ = page.models
|
|
1174
|
+
assert len(captured_warnings) == 1
|
|
1175
|
+
assert issubclass(captured_warnings[0].category, DeprecationWarning)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|