Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not use legacy wrapper in common package #1204

Merged
merged 4 commits into from
Feb 9, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions src/neptune/common/backends/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#
# Copyright (c) 2022, 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.
#
__all__ = ["with_api_exceptions_handler"]

import itertools
import logging
import os
import time

import requests
from bravado.exception import (
BravadoConnectionError,
BravadoTimeoutError,
HTTPBadGateway,
HTTPClientError,
HTTPForbidden,
HTTPGatewayTimeout,
HTTPInternalServerError,
HTTPRequestTimeout,
HTTPServiceUnavailable,
HTTPTooManyRequests,
HTTPUnauthorized,
)
from urllib3.exceptions import NewConnectionError

from neptune.common.envs import NEPTUNE_RETRIES_TIMEOUT_ENV
from neptune.common.exceptions import (
ClientHttpError,
Forbidden,
NeptuneConnectionLostException,
NeptuneInvalidApiTokenException,
NeptuneSSLVerificationError,
Unauthorized,
)

_logger = logging.getLogger(__name__)

MAX_RETRY_TIME = 30
retries_timeout = int(os.getenv(NEPTUNE_RETRIES_TIMEOUT_ENV, "60"))


def with_api_exceptions_handler(func):
def wrapper(*args, **kwargs):
last_exception = None
start_time = time.monotonic()
for retry in itertools.count(0):
if time.monotonic() - start_time > retries_timeout:
break

try:
return func(*args, **kwargs)
except requests.exceptions.InvalidHeader as e:
if "X-Neptune-Api-Token" in e.args[0]:
raise NeptuneInvalidApiTokenException()
raise
except requests.exceptions.SSLError as e:
raise NeptuneSSLVerificationError() from e
except (
BravadoConnectionError,
BravadoTimeoutError,
requests.exceptions.ConnectionError,
requests.exceptions.Timeout,
HTTPRequestTimeout,
HTTPServiceUnavailable,
HTTPGatewayTimeout,
HTTPBadGateway,
HTTPTooManyRequests,
HTTPInternalServerError,
NewConnectionError,
) as e:
time.sleep(min(2 ** min(10, retry), MAX_RETRY_TIME))
last_exception = e
continue
except HTTPUnauthorized:
raise Unauthorized()
except HTTPForbidden:
raise Forbidden()
except HTTPClientError as e:
raise ClientHttpError(e.status_code, e.response.text) from e
except requests.exceptions.RequestException as e:
if e.response is None:
raise
status_code = e.response.status_code
if status_code in (
HTTPRequestTimeout.status_code,
HTTPBadGateway.status_code,
HTTPServiceUnavailable.status_code,
HTTPGatewayTimeout.status_code,
HTTPTooManyRequests.status_code,
HTTPInternalServerError.status_code,
):
time.sleep(min(2 ** min(10, retry), MAX_RETRY_TIME))
last_exception = e
continue
elif status_code == HTTPUnauthorized.status_code:
raise Unauthorized()
elif status_code == HTTPForbidden.status_code:
raise Forbidden()
elif 400 <= status_code < 500:
raise ClientHttpError(status_code, e.response.text) from e
else:
raise
raise NeptuneConnectionLostException(last_exception) from last_exception

return wrapper
2 changes: 2 additions & 0 deletions src/neptune/common/envs.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@


API_TOKEN_ENV_NAME = "NEPTUNE_API_TOKEN"

NEPTUNE_RETRIES_TIMEOUT_ENV = "NEPTUNE_RETRIES_TIMEOUT"
170 changes: 170 additions & 0 deletions src/neptune/common/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,173 @@ def __init__(self, msg: str):
{correct}Need help?{end}-> https://docs.neptune.ai/getting_help
"""
super().__init__(message.format(msg=msg, **STYLES))


class ClientHttpError(NeptuneException):
def __init__(self, status, response):
self.status = status
self.response = response
message = """
{h1}
----ClientHttpError-----------------------------------------------------------------------
{end}
The Neptune server returned the status {fail}{status}{end}.

The server response was:
{fail}{response}{end}

Verify the correctness of your call or contact Neptune support.

{correct}Need help?{end}-> https://docs.neptune.ai/getting_help
"""
super().__init__(message.format(status=status, response=response, **STYLES))


class NeptuneApiException(NeptuneException):
pass


class Forbidden(NeptuneApiException):
def __init__(self):
message = """
{h1}
----Forbidden-----------------------------------------------------------------------
{end}
You don't have permission to access the given resource.

- Verify that your API token is correct.
See: https://app.neptune.ai/get_my_api_token

- Verify that the provided project name is correct.
The correct project name should look like this: {correct}WORKSPACE_NAME/PROJECT_NAME{end}
It has two parts:
- {correct}WORKSPACE_NAME{end}: can be your username or your organization name
- {correct}PROJECT_NAME{end}: the name specified for the project

- Ask your organization administrator to grant you necessary privileges to the project.

{correct}Need help?{end}-> https://docs.neptune.ai/getting_help
"""
super().__init__(message.format(**STYLES))


class Unauthorized(NeptuneApiException):
def __init__(self):
message = """
{h1}
----Unauthorized-----------------------------------------------------------------------
{end}
You don't have permission to access the given resource.

- Verify that your API token is correct.
See: https://app.neptune.ai/get_my_api_token

- Verify that the provided project name is correct.
The correct project name should look like this: {correct}WORKSPACE_NAME/PROJECT_NAME{end}
It has two parts:
- {correct}WORKSPACE_NAME{end}: can be your username or your organization name
- {correct}PROJECT_NAME{end}: the name specified for the project

- Ask your organization administrator to grant you necessary privileges to the project.

{correct}Need help?{end}-> https://docs.neptune.ai/getting_help
"""
super().__init__(message.format(**STYLES))


class InternalServerError(NeptuneApiException):
def __init__(self, response):
message = """
{h1}
----InternalServerError-----------------------------------------------------------------------
{end}
The Neptune client library encountered an unexpected internal server error.

The server response was:
{fail}{response}{end}

Please try again later or contact Neptune support.

{correct}Need help?{end}-> https://docs.neptune.ai/getting_help
"""
super().__init__(message.format(response=response, **STYLES))


class NeptuneConnectionLostException(NeptuneException):
def __init__(self, cause: Exception):
self.cause = cause
message = """
{h1}
----NeptuneConnectionLostException---------------------------------------------------------
{end}
The connection to the Neptune server was lost.
If you are using the asynchronous (default) connection mode, Neptune continues to locally track your metadata and continuously tries to re-establish a connection to the Neptune servers.
If the connection is not re-established, you can upload your data later with the Neptune Command Line Interface tool:
{bash}neptune sync -p workspace_name/project_name{end}

What should I do?
- Check if your computer is connected to the internet.
- If your connection is unstable, consider working in offline mode:
{python}run = neptune.init_run(mode="offline"){end}

You can find detailed instructions on the following doc pages:
- https://docs.neptune.ai/api/connection_modes/#offline-mode
- https://docs.neptune.ai/api/neptune_sync/

You may also want to check the following docs page:
- https://docs.neptune.ai/api/connection_modes/#connectivity-issues

{correct}Need help?{end}-> https://docs.neptune.ai/getting_help
""" # noqa: E501
super().__init__(message.format(**STYLES))


class NeptuneSSLVerificationError(NeptuneException):
def __init__(self):
message = """
{h1}
----NeptuneSSLVerificationError-----------------------------------------------------------------------
{end}

The Neptune client was unable to verify your SSL Certificate.

{bold}What could have gone wrong?{end}
- You are behind a proxy that inspects traffic to Neptune servers.
- Contact your network administrator
- The SSL/TLS certificate of your on-premises installation is not recognized due to a custom Certificate Authority (CA).
- To check, run the following command in your terminal:
{bash}curl https://<your_domain>/api/backend/echo {end}
- Where <your_domain> is the address that you use to access Neptune app, such as abc.com
- Contact your network administrator if you get the following output:
{fail}"curl: (60) server certificate verification failed..."{end}
- Your machine software is outdated.
- Minimal OS requirements:
- Windows >= XP SP3
- macOS >= 10.12.1
- Ubuntu >= 12.04
- Debian >= 8

{bold}What can I do?{end}
You can manually configure Neptune to skip all SSL checks. To do that,
set the NEPTUNE_ALLOW_SELF_SIGNED_CERTIFICATE environment variable to 'TRUE'.
{bold}Note: This might mean that your connection is less secure{end}.

Linux/Unix
In your terminal run:
{bash}export NEPTUNE_ALLOW_SELF_SIGNED_CERTIFICATE='TRUE'{end}

Windows
In your terminal run:
{bash}set NEPTUNE_ALLOW_SELF_SIGNED_CERTIFICATE='TRUE'{end}

Jupyter notebook
In your code cell:
{bash}%env NEPTUNE_ALLOW_SELF_SIGNED_CERTIFICATE='TRUE'{end}

You may also want to check the following docs page:
- https://docs.neptune.ai/api/environment_variables/#neptune_allow_self_signed_certificate


{correct}Need help?{end}-> https://docs.neptune.ai/getting_help
""" # noqa: E501
super().__init__(message.format(**STYLES))
6 changes: 2 additions & 4 deletions src/neptune/common/oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,9 @@
from requests.auth import AuthBase
from requests_oauthlib import OAuth2Session

from neptune.common.backends.utils import with_api_exceptions_handler
from neptune.common.exceptions import NeptuneInvalidApiTokenException
from neptune.common.utils import (
update_session_proxies,
with_api_exceptions_handler,
)
from neptune.common.utils import update_session_proxies

_decoding_options = {
"verify_signature": False,
Expand Down
Loading