-
Notifications
You must be signed in to change notification settings - Fork 60
/
hosted_backend_api_client.py
228 lines (195 loc) 路 8.55 KB
/
hosted_backend_api_client.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#
# Copyright (c) 2019, Neptune Labs Sp. z o.o.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# pylint: disable=too-many-lines
import logging
import os
import platform
import sys
import click
import urllib3
from bravado.exception import HTTPNotFound
from bravado.requests_client import RequestsClient
from packaging import version
from neptune.api_exceptions import ProjectNotFound, WorkspaceNotFound
from neptune.backend import BackendApiClient, LeaderboardApiClient
from neptune.exceptions import STYLES, UnsupportedClientVersion
from neptune.internal.api_clients.credentials import Credentials
from neptune.internal.api_clients.hosted_api_clients.hosted_alpha_leaderboard_api_client import (
HostedAlphaLeaderboardApiClient,
)
from neptune.internal.api_clients.hosted_api_clients.mixins import HostedNeptuneMixin
from neptune.new.internal.backends.hosted_client import NeptuneResponseAdapter
from neptune.oauth import NeptuneAuthenticator
from neptune.projects import Project
from neptune.utils import (
NoopObject,
update_session_proxies,
with_api_exceptions_handler,
)
_logger = logging.getLogger(__name__)
class HostedNeptuneBackendApiClient(HostedNeptuneMixin, BackendApiClient):
@with_api_exceptions_handler
def __init__(self, api_token=None, proxies=None):
self._old_leaderboard_client = None
self._new_leaderboard_client = None
self._api_token = api_token
self._proxies = proxies
# This is not a top-level import because of circular dependencies
from neptune import __version__
self.client_lib_version = __version__
self.credentials = Credentials(api_token)
ssl_verify = True
if os.getenv("NEPTUNE_ALLOW_SELF_SIGNED_CERTIFICATE"):
urllib3.disable_warnings()
ssl_verify = False
self._http_client = RequestsClient(
ssl_verify=ssl_verify, response_adapter_class=NeptuneResponseAdapter
)
# for session re-creation we need to keep an authenticator-free version of http client
self._http_client_for_token = RequestsClient(
ssl_verify=ssl_verify, response_adapter_class=NeptuneResponseAdapter
)
user_agent = "neptune-client/{lib_version} ({system}, python {python_version})".format(
lib_version=self.client_lib_version,
system=platform.platform(),
python_version=platform.python_version(),
)
self.http_client.session.headers.update({"User-Agent": user_agent})
self._http_client_for_token.session.headers.update({"User-Agent": user_agent})
update_session_proxies(self.http_client.session, proxies)
update_session_proxies(self._http_client_for_token.session, proxies)
config_api_url = self.credentials.api_url_opt or self.credentials.token_origin_address
# We don't need to be able to resolve Neptune host if we use proxy
if proxies is None:
self._verify_host_resolution(config_api_url, self.credentials.token_origin_address)
# this backend client is used only for initial configuration and session re-creation
self.backend_client = self._get_swagger_client(
"{}/api/backend/swagger.json".format(config_api_url),
self._http_client_for_token,
)
self._client_config = self._create_client_config(
api_token=self.credentials.api_token, backend_client=self.backend_client
)
self._verify_version()
self.backend_swagger_client = self._get_swagger_client(
"{}/api/backend/swagger.json".format(self._client_config.api_url),
self.http_client,
)
self.authenticator = self._create_authenticator(
api_token=self.credentials.api_token,
ssl_verify=ssl_verify,
proxies=proxies,
backend_client=self.backend_client,
)
self.http_client.authenticator = self.authenticator
if sys.version_info >= (3, 7):
try:
# pylint: disable=no-member
os.register_at_fork(after_in_child=self._handle_fork_in_child)
except AttributeError:
pass
def _handle_fork_in_child(self):
self.backend_swagger_client = NoopObject()
@property
def api_address(self):
return self._client_config.api_url
@property
def http_client(self):
return self._http_client
@property
def display_address(self):
return self._client_config.display_url
@property
def proxies(self):
return self._proxies
@with_api_exceptions_handler
def get_project(self, project_qualified_name):
try:
response = self.backend_swagger_client.api.getProject(
projectIdentifier=project_qualified_name
).response()
warning = response.metadata.headers.get("X-Server-Warning")
if warning:
click.echo("{warning}{content}{end}".format(content=warning, **STYLES))
project = response.result
return Project(
backend=self.create_leaderboard_backend(project=project),
internal_id=project.id,
namespace=project.organizationName,
name=project.name,
)
except HTTPNotFound:
raise ProjectNotFound(project_qualified_name)
@with_api_exceptions_handler
def get_projects(self, namespace):
try:
r = self.backend_swagger_client.api.listProjects(
organizationIdentifier=namespace
).response()
return r.result.entries
except HTTPNotFound:
raise WorkspaceNotFound(namespace_name=namespace)
def create_leaderboard_backend(self, project) -> LeaderboardApiClient:
return self.get_new_leaderboard_client()
def get_new_leaderboard_client(self) -> HostedAlphaLeaderboardApiClient:
if self._new_leaderboard_client is None:
self._new_leaderboard_client = HostedAlphaLeaderboardApiClient(backend_api_client=self)
return self._new_leaderboard_client
@with_api_exceptions_handler
def _create_authenticator(self, api_token, ssl_verify, proxies, backend_client):
return NeptuneAuthenticator(api_token, backend_client, ssl_verify, proxies)
def _verify_version(self):
parsed_version = version.parse(self.client_lib_version)
if (
self._client_config.min_compatible_version
and self._client_config.min_compatible_version > parsed_version
):
click.echo(
"ERROR: Minimal supported client version is {} (installed: {}). Please upgrade neptune-client".format(
self._client_config.min_compatible_version, self.client_lib_version
),
sys.stderr,
)
raise UnsupportedClientVersion(
self.client_lib_version,
self._client_config.min_compatible_version,
self._client_config.max_compatible_version,
)
if (
self._client_config.max_compatible_version
and self._client_config.max_compatible_version < parsed_version
):
click.echo(
"ERROR: Maximal supported client version is {} (installed: {}). Please downgrade neptune-client".format(
self._client_config.max_compatible_version, self.client_lib_version
),
sys.stderr,
)
raise UnsupportedClientVersion(
self.client_lib_version,
self._client_config.min_compatible_version,
self._client_config.max_compatible_version,
)
if (
self._client_config.min_recommended_version
and self._client_config.min_recommended_version > parsed_version
):
click.echo(
"WARNING: We recommend an upgrade to a new version of neptune-client - {} (installed - {}).".format(
self._client_config.min_recommended_version, self.client_lib_version
),
sys.stderr,
)