From e78dc8cb1ff01e607516cb9aa9d0e0671d6b68da Mon Sep 17 00:00:00 2001 From: Ivanildo Barauna de Souza Junior Date: Tue, 18 Jun 2024 23:43:20 -0300 Subject: [PATCH 01/20] feat: Initial development of Retry Stratefies --- src/api_to_dataframe/__init__.py | 2 +- .../common/utils/retry_strategies.py | 9 ------- .../controller/client_builder.py | 5 ++-- src/api_to_dataframe/models/get_data.py | 26 +++++++++---------- src/api_to_dataframe/models/retainer.py | 19 +++++--------- 5 files changed, 23 insertions(+), 38 deletions(-) delete mode 100644 src/api_to_dataframe/common/utils/retry_strategies.py diff --git a/src/api_to_dataframe/__init__.py b/src/api_to_dataframe/__init__.py index 27e9c86..ff3c4df 100644 --- a/src/api_to_dataframe/__init__.py +++ b/src/api_to_dataframe/__init__.py @@ -1,2 +1,2 @@ from .controller.client_builder import ClientBuilder -from .common.utils.retry_strategies import RetryStrategies +from .models.retainer import RetryStrategies diff --git a/src/api_to_dataframe/common/utils/retry_strategies.py b/src/api_to_dataframe/common/utils/retry_strategies.py deleted file mode 100644 index 29bdf19..0000000 --- a/src/api_to_dataframe/common/utils/retry_strategies.py +++ /dev/null @@ -1,9 +0,0 @@ -from enum import Enum - - -class RetryStrategies(Enum): - NoRetryStrategy = 0 - LinearStrategy = 1 - ExponentialStrategy = 2 - CustomStrategy = 3 - diff --git a/src/api_to_dataframe/controller/client_builder.py b/src/api_to_dataframe/controller/client_builder.py index f6cd092..26d588c 100644 --- a/src/api_to_dataframe/controller/client_builder.py +++ b/src/api_to_dataframe/controller/client_builder.py @@ -1,4 +1,4 @@ -from api_to_dataframe.common.utils.retry_strategies import RetryStrategies +from api_to_dataframe.models.retainer import RetryStrategies from api_to_dataframe.models.get_data import GetData @@ -6,7 +6,7 @@ class ClientBuilder: def __init__(self, endpoint: str, headers: dict = {}, - retry_strategy: RetryStrategies = RetryStrategies.NoRetryStrategy, + retry_strategy: RetryStrategies = RetryStrategies.NoRetryStrategy(), timeout: int = 5): """ Initializes an instance of ClientBuilder. @@ -27,6 +27,7 @@ def __init__(self, self.timeout = timeout self.headers = headers + @RetryStrategies def get_api_data(self): """ Retrieves data from the API using the defined endpoint and retry strategy. diff --git a/src/api_to_dataframe/models/get_data.py b/src/api_to_dataframe/models/get_data.py index dbf7d51..a8fb3c5 100644 --- a/src/api_to_dataframe/models/get_data.py +++ b/src/api_to_dataframe/models/get_data.py @@ -1,27 +1,27 @@ import requests -from requests.exceptions import HTTPError, Timeout, RequestException +from requests.exceptions import HTTPError, Timeout import pandas as pd -from api_to_dataframe.common.utils.retry_strategies import RetryStrategies +# from api_to_dataframe.common.utils.retry_strategies import RetryStrategies +from api_to_dataframe.models.retainer import RetryStrategies class GetData: @staticmethod def get_response(endpoint: str, headers: dict, - retry_strategies: RetryStrategies, + retry_strategy: RetryStrategies, timeout: int): - try: - response = requests.get(endpoint, timeout=timeout, headers=headers) - response.raise_for_status() - except HTTPError as http_err: - print(f'HTTP error occurred: {http_err}') - raise http_err - except Timeout as timeout_err: - print(f'Timeout error occurred: {timeout_err}') - raise timeout_err - else: + + try_number = 0 + + response = requests.get(endpoint, timeout=timeout, headers=headers) + + if response.status_code == 200: return response + else: + try_number += 1 + # Retainer.strategy(retry_strategy) @staticmethod def to_dataframe(response): diff --git a/src/api_to_dataframe/models/retainer.py b/src/api_to_dataframe/models/retainer.py index e197021..98af739 100644 --- a/src/api_to_dataframe/models/retainer.py +++ b/src/api_to_dataframe/models/retainer.py @@ -1,15 +1,8 @@ -from api_to_dataframe.common.utils.retry_strategies import RetryStrategies +import time -class Retainer: - @staticmethod - def strategy(retry_strategy: RetryStrategies = RetryStrategies.NoRetryStrategy): - if retry_strategy == RetryStrategies.NoRetryStrategy: - print("::: NoRetryStrategy :::") - elif retry_strategy == RetryStrategies.LinearStrategy: - print("::: LinearStrategy :::") - elif retry_strategy == RetryStrategies.ExponentialStrategy: - print("::: ExponentialStrategy :::") - elif retry_strategy == RetryStrategies.CustomStrategy: - print("::: CustomStrategy :::") - +def RetryStrategies(func): + print("Chamada a função RetryStrategies, com os params:", func) + def wrapper(*args, **kwargs): + print("Chamada do wrapper com os argumentos: ", args, kwargs) + return wrapper From 9b669362cdac5c694686b9118433520e8025afe3 Mon Sep 17 00:00:00 2001 From: Ivanildo Barauna de Souza Junior Date: Thu, 20 Jun 2024 14:33:21 -0300 Subject: [PATCH 02/20] feat: Added new necessary params for setup retry strategy --- .../controller/client_builder.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/api_to_dataframe/controller/client_builder.py b/src/api_to_dataframe/controller/client_builder.py index 26d588c..96d2972 100644 --- a/src/api_to_dataframe/controller/client_builder.py +++ b/src/api_to_dataframe/controller/client_builder.py @@ -1,4 +1,5 @@ from api_to_dataframe.models.retainer import RetryStrategies +from api_to_dataframe.models.retainer import Strategies from api_to_dataframe.models.get_data import GetData @@ -6,15 +7,17 @@ class ClientBuilder: def __init__(self, endpoint: str, headers: dict = {}, - retry_strategy: RetryStrategies = RetryStrategies.NoRetryStrategy(), - timeout: int = 5): + retry_strategy: Strategies = Strategies.NoRetryStrategy, + retries: int = 3, + delay: int = 1, + connection_timeout: int = 2): """ Initializes an instance of ClientBuilder. Args: endpoint (str): The API endpoint to be accessed. retry_strategy (RetryStrategies, optional): The retry strategy for the request. Default is NoRetryStrategy. - timeout (int, optional): The timeout for the request. Default is 5 seconds. + connection_timeout (int, optional): The timeout for the request. Default is 5 seconds. Raises: ValueError: If the endpoint is empty. @@ -24,8 +27,10 @@ def __init__(self, else: self.endpoint = endpoint self.retry_strategy = retry_strategy - self.timeout = timeout + self.connection_timeout = connection_timeout self.headers = headers + self.retries = retries + self.delay = delay @RetryStrategies def get_api_data(self): @@ -35,7 +40,11 @@ def get_api_data(self): Returns: dict: The response from the API. """ - response = GetData.get_response(self.endpoint, self.headers, self.retry_strategy, self.timeout) + response = GetData.get_response( + endpoint=self.endpoint, + headers=self.headers, + connection_timeout=self.connection_timeout + ) return response @staticmethod From af8462bfc966c4796939977d3646dce9bf322c95 Mon Sep 17 00:00:00 2001 From: Ivanildo Barauna de Souza Junior Date: Thu, 20 Jun 2024 14:34:02 -0300 Subject: [PATCH 03/20] feat: added functional tests on jupyter notebook --- notebooks/example.ipynb | 200 ++++++++++++++++++++++++++-------------- 1 file changed, 133 insertions(+), 67 deletions(-) diff --git a/notebooks/example.ipynb b/notebooks/example.ipynb index 8f6a586..5916d39 100644 --- a/notebooks/example.ipynb +++ b/notebooks/example.ipynb @@ -2,97 +2,163 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2024-06-20T17:28:18.653294Z", + "start_time": "2024-06-20T17:28:13.772706Z" + } + }, + "source": "pip install --upgrade pip && pip install api-to-dataframe", "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Defaulting to user installation because normal site-packages is not writeable\n", - "Collecting api-to-dataframe\n", - " Downloading api_to_dataframe-0.0.3-py3-none-any.whl (4.0 kB)\n", - "Collecting coverage<8.0.0,>=7.5.3\n", - " Using cached coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl (204 kB)\n", - "Collecting pytest<9.0.0,>=8.2.2\n", - " Downloading pytest-8.2.2-py3-none-any.whl (339 kB)\n", - "\u001b[K |████████████████████████████████| 339 kB 7.5 MB/s eta 0:00:01\n", - "\u001b[?25hCollecting tomli>=1\n", - " Using cached tomli-2.0.1-py3-none-any.whl (12 kB)\n", - "Requirement already satisfied: exceptiongroup>=1.0.0rc8 in /Users/ivsouza/Library/Python/3.9/lib/python/site-packages (from pytest<9.0.0,>=8.2.2->api-to-dataframe) (1.2.0)\n", - "Requirement already satisfied: packaging in /Users/ivsouza/Library/Python/3.9/lib/python/site-packages (from pytest<9.0.0,>=8.2.2->api-to-dataframe) (23.2)\n", - "Collecting iniconfig\n", - " Using cached iniconfig-2.0.0-py3-none-any.whl (5.9 kB)\n", - "Collecting pluggy<2.0,>=1.5\n", - " Using cached pluggy-1.5.0-py3-none-any.whl (20 kB)\n", - "Installing collected packages: tomli, pluggy, iniconfig, pytest, coverage, api-to-dataframe\n", - "Successfully installed api-to-dataframe-0.0.3 coverage-7.5.3 iniconfig-2.0.0 pluggy-1.5.0 pytest-8.2.2 tomli-2.0.1\n", - "\u001b[33mWARNING: You are using pip version 21.2.4; however, version 24.0 is available.\n", - "You should consider upgrading via the '/Library/Developer/CommandLineTools/usr/bin/python3 -m pip install --upgrade pip' command.\u001b[0m\n", + "Requirement already satisfied: pip in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (24.0)\r\n", + "Requirement already satisfied: api-to-dataframe in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (1.2.0)\r\n", + "Requirement already satisfied: pandas<3.0.0,>=2.2.2 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from api-to-dataframe) (2.2.2)\r\n", + "Requirement already satisfied: requests<3.0.0,>=2.32.3 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from api-to-dataframe) (2.32.3)\r\n", + "Requirement already satisfied: numpy>=1.22.4 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from pandas<3.0.0,>=2.2.2->api-to-dataframe) (1.26.4)\r\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from pandas<3.0.0,>=2.2.2->api-to-dataframe) (2.9.0.post0)\r\n", + "Requirement already satisfied: pytz>=2020.1 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from pandas<3.0.0,>=2.2.2->api-to-dataframe) (2024.1)\r\n", + "Requirement already satisfied: tzdata>=2022.7 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from pandas<3.0.0,>=2.2.2->api-to-dataframe) (2024.1)\r\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from requests<3.0.0,>=2.32.3->api-to-dataframe) (3.3.2)\r\n", + "Requirement already satisfied: idna<4,>=2.5 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from requests<3.0.0,>=2.32.3->api-to-dataframe) (3.7)\r\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from requests<3.0.0,>=2.32.3->api-to-dataframe) (2.2.1)\r\n", + "Requirement already satisfied: certifi>=2017.4.17 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from requests<3.0.0,>=2.32.3->api-to-dataframe) (2024.6.2)\r\n", + "Requirement already satisfied: six>=1.5 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from python-dateutil>=2.8.2->pandas<3.0.0,>=2.2.2->api-to-dataframe) (1.16.0)\r\n", "Note: you may need to restart the kernel to use updated packages.\n" ] } ], - "source": [ - "pip install api-to-dataframe" - ] + "execution_count": 3 }, { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Defaulting to user installation because normal site-packages is not writeable\n", - "\u001b[31mERROR: You must give at least one requirement to install (see \"pip help install\")\u001b[0m\n", - "\u001b[33mWARNING: You are using pip version 21.2.4; however, version 24.0 is available.\n", - "You should consider upgrading via the '/Library/Developer/CommandLineTools/usr/bin/python3 -m pip install --upgrade pip' command.\u001b[0m\n", - "Note: you may need to restart the kernel to use updated packages.\n" - ] + "metadata": { + "ExecuteTime": { + "end_time": "2024-06-20T17:30:43.076831Z", + "start_time": "2024-06-20T17:30:42.981486Z" } - ], + }, + "cell_type": "code", "source": [ - "pip install --upgrade " - ] + "from api_to_dataframe import ClientBuilder, RetryStrategies\n", + "\n", + "client = ClientBuilder(\n", + " endpoint=\"https://brasilapi.com.br/api/banks/v1\", retry_strategy=RetryStrategies)\n", + "\n", + "api_data = client.get_api_data()\n", + "df = client.api_to_dataframe(api_data)" + ], + "outputs": [], + "execution_count": 6 }, { + "metadata": { + "ExecuteTime": { + "end_time": "2024-06-20T17:30:51.598804Z", + "start_time": "2024-06-20T17:30:51.571405Z" + } + }, "cell_type": "code", - "execution_count": 10, - "metadata": {}, + "source": "df.head()", "outputs": [ { - "ename": "AttributeError", - "evalue": "'ApiToDataframe' object has no attribute 'name'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[10], line 5\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mapi_to_dataframe\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m ApiToDataframe\n\u001b[1;32m 3\u001b[0m new \u001b[38;5;241m=\u001b[39m ApiToDataframe\u001b[38;5;241m.\u001b[39mApiToDataframe(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mIvanildo\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m----> 5\u001b[0m \u001b[43mnew\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msay_hello\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/Library/Python/3.9/lib/python/site-packages/api_to_dataframe/run.py:6\u001b[0m, in \u001b[0;36mApiToDataframe.say_hello\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21msay_hello\u001b[39m(\u001b[38;5;28mself\u001b[39m):\n\u001b[0;32m----> 6\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mHello \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mname\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n", - "\u001b[0;31mAttributeError\u001b[0m: 'ApiToDataframe' object has no attribute 'name'" - ] + "data": { + "text/plain": [ + " ispb name code \\\n", + "0 00000000 BCO DO BRASIL S.A. 1.0 \n", + "1 00000208 BRB - BCO DE BRASILIA S.A. 70.0 \n", + "2 00038121 Selic NaN \n", + "3 00038166 Bacen NaN \n", + "4 00122327 SANTINVEST S.A. - CFI 539.0 \n", + "\n", + " fullName \n", + "0 Banco do Brasil S.A. \n", + "1 BRB - BANCO DE BRASILIA S.A. \n", + "2 Banco Central do Brasil - Selic \n", + "3 Banco Central do Brasil \n", + "4 SANTINVEST S.A. - CREDITO, FINANCIAMENTO E INV... " + ], + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
ispbnamecodefullName
000000000BCO DO BRASIL S.A.1.0Banco do Brasil S.A.
100000208BRB - BCO DE BRASILIA S.A.70.0BRB - BANCO DE BRASILIA S.A.
200038121SelicNaNBanco Central do Brasil - Selic
300038166BacenNaNBanco Central do Brasil
400122327SANTINVEST S.A. - CFI539.0SANTINVEST S.A. - CREDITO, FINANCIAMENTO E INV...
\n", + "
" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" } ], - "source": [ - "from api_to_dataframe import ApiToDataframe\n", - "\n", - "new = ApiToDataframe.ApiToDataframe(\"Ivanildo\")\n", - "\n", - "new.say_hello()\n", - "\n", - "\n", - "\n" - ] + "execution_count": 7 }, { - "cell_type": "code", - "execution_count": null, "metadata": {}, + "cell_type": "code", "outputs": [], - "source": [] + "execution_count": null, + "source": "" } ], "metadata": { From 9f796fea56c6e5f3727a2f8ec203a51cf49334b6 Mon Sep 17 00:00:00 2001 From: Ivanildo Barauna de Souza Junior Date: Thu, 20 Jun 2024 14:34:57 -0300 Subject: [PATCH 04/20] feat: added raise_for_status for get raises on Retainer --- src/api_to_dataframe/models/get_data.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/api_to_dataframe/models/get_data.py b/src/api_to_dataframe/models/get_data.py index a8fb3c5..5b3147c 100644 --- a/src/api_to_dataframe/models/get_data.py +++ b/src/api_to_dataframe/models/get_data.py @@ -10,19 +10,13 @@ class GetData: @staticmethod def get_response(endpoint: str, headers: dict, - retry_strategy: RetryStrategies, - timeout: int): + connection_timeout: int): - try_number = 0 - response = requests.get(endpoint, timeout=timeout, headers=headers) - - if response.status_code == 200: - return response - else: - try_number += 1 - # Retainer.strategy(retry_strategy) + response = requests.get(endpoint, timeout=connection_timeout, headers=headers) + response.raise_for_status() + return response @staticmethod def to_dataframe(response): try: From f11ae213c8bd756a14ae0d2fda848ea3df25a2e0 Mon Sep 17 00:00:00 2001 From: Ivanildo Barauna de Souza Junior Date: Thu, 20 Jun 2024 14:35:19 -0300 Subject: [PATCH 05/20] feat: increase minor version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2e53844..194b409 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "api-to-dataframe" -version = "1.1.0" +version = "1.2.0" description = "A package to convert API responses to pandas dataframe" authors = ["IvanildoBarauna "] readme = "README.md" From 7082d9d2857295d35eca65f7893001a8482077c2 Mon Sep 17 00:00:00 2001 From: Ivanildo Barauna de Souza Junior Date: Thu, 20 Jun 2024 14:36:06 -0300 Subject: [PATCH 06/20] tests: added new import for Enum Strategies --- tests/test_models_get_data.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/tests/test_models_get_data.py b/tests/test_models_get_data.py index fb5f5cd..db26e62 100644 --- a/tests/test_models_get_data.py +++ b/tests/test_models_get_data.py @@ -4,11 +4,6 @@ from api_to_dataframe.models.get_data import GetData from api_to_dataframe.controller.client_builder import ClientBuilder -from api_to_dataframe.common.utils.retry_strategies import RetryStrategies - - -def test_get_response(): - assert True def test_to_dataframe(): @@ -42,9 +37,8 @@ def test_http_error(): with ((pytest.raises(requests.exceptions.HTTPError))): GetData.get_response( endpoint=endpoint, - retry_strategies=RetryStrategies.NoRetryStrategy, headers={}, - timeout=10) + connection_timeout=10) @responses.activate @@ -56,9 +50,8 @@ def test_timeout_error(): with pytest.raises(requests.exceptions.Timeout): GetData.get_response( endpoint=endpoint, - retry_strategies=RetryStrategies.NoRetryStrategy, headers={}, - timeout=10) + connection_timeout=10) @responses.activate @@ -73,6 +66,5 @@ def test_request_exception(): with pytest.raises(requests.exceptions.RequestException): GetData.get_response( endpoint=endpoint, - retry_strategies=RetryStrategies.NoRetryStrategy, headers={}, - timeout=10) + connection_timeout=10) From ed5f48b64e19ed65c58283cc637fb7240009bb90 Mon Sep 17 00:00:00 2001 From: Ivanildo Barauna de Souza Junior Date: Thu, 20 Jun 2024 14:36:25 -0300 Subject: [PATCH 07/20] feat: RetryStrategy development --- src/api_to_dataframe/models/retainer.py | 28 ++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/api_to_dataframe/models/retainer.py b/src/api_to_dataframe/models/retainer.py index 98af739..cf2b5d4 100644 --- a/src/api_to_dataframe/models/retainer.py +++ b/src/api_to_dataframe/models/retainer.py @@ -1,8 +1,30 @@ import time +from enum import Enum + + +class Strategies(Enum): + NoRetryStrategy = 0 + LinearRetryStrategy = 1 + ExponentialRetryStrategy = 2 def RetryStrategies(func): - print("Chamada a função RetryStrategies, com os params:", func) def wrapper(*args, **kwargs): - print("Chamada do wrapper com os argumentos: ", args, kwargs) - return wrapper + attemp = 0 + while attemp < args[0].retries: + try: + return func(*args, **kwargs) + except Exception as e: + attemp += 1 + + if args[0].retry_strategy == Strategies.NoRetryStrategy: + break + elif args[0].retry_strategy == Strategies.LinearRetryStrategy: + time.sleep(args[0].delay) + elif args[0].retry_strategy == Strategies.ExponentialRetryStrategy: + time.sleep(args[0].delay * 2 ** attemp) + + if attemp == args[0].retries: + print(f"Failed after {args[0].retries} attempts") + raise e + return wrapper \ No newline at end of file From dfc448e7c63af6b104bdb15741e9d48d38019d1c Mon Sep 17 00:00:00 2001 From: Ivanildo Barauna de Souza Junior Date: Thu, 20 Jun 2024 14:36:52 -0300 Subject: [PATCH 08/20] chore: JetBrains Configs --- .idea/api-to-dataframe.iml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.idea/api-to-dataframe.iml b/.idea/api-to-dataframe.iml index 0de63f1..85cf8cd 100644 --- a/.idea/api-to-dataframe.iml +++ b/.idea/api-to-dataframe.iml @@ -5,7 +5,7 @@ - + From d6702a2fdefc83b6dc0083494ead8af7ef5319e8 Mon Sep 17 00:00:00 2001 From: Ivanildo Barauna de Souza Junior Date: Thu, 20 Jun 2024 14:37:10 -0300 Subject: [PATCH 09/20] chore: adjust import from new retainer component --- src/api_to_dataframe/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api_to_dataframe/__init__.py b/src/api_to_dataframe/__init__.py index ff3c4df..5a5ce94 100644 --- a/src/api_to_dataframe/__init__.py +++ b/src/api_to_dataframe/__init__.py @@ -1,2 +1,2 @@ from .controller.client_builder import ClientBuilder -from .models.retainer import RetryStrategies +from .models.retainer import Strategies as RetryStrategies From de5c4634f2f7110879afb179fc1485fe8b121cee Mon Sep 17 00:00:00 2001 From: Ivanildo Barauna de Souza Junior Date: Thu, 20 Jun 2024 18:34:30 -0300 Subject: [PATCH 10/20] tests: added LinearStrategy tests for retainer --- tests/test_models_retainer.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/test_models_retainer.py diff --git a/tests/test_models_retainer.py b/tests/test_models_retainer.py new file mode 100644 index 0000000..e69de29 From 18cd496b7a4fa422bfba05e791eec4c3889919f1 Mon Sep 17 00:00:00 2001 From: Ivanildo Barauna de Souza Junior Date: Thu, 20 Jun 2024 18:35:17 -0300 Subject: [PATCH 11/20] chore: change default value parameters --- .../controller/client_builder.py | 67 ++++++++++++------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/src/api_to_dataframe/controller/client_builder.py b/src/api_to_dataframe/controller/client_builder.py index 96d2972..ae37dc5 100644 --- a/src/api_to_dataframe/controller/client_builder.py +++ b/src/api_to_dataframe/controller/client_builder.py @@ -6,31 +6,47 @@ class ClientBuilder: def __init__(self, endpoint: str, - headers: dict = {}, + headers: dict = None, retry_strategy: Strategies = Strategies.NoRetryStrategy, - retries: int = 3, + retries: int = 2, delay: int = 1, - connection_timeout: int = 2): + connection_timeout: int = 1): + """ - Initializes an instance of ClientBuilder. + Initializes the ClientBuilder object. - Args: - endpoint (str): The API endpoint to be accessed. - retry_strategy (RetryStrategies, optional): The retry strategy for the request. Default is NoRetryStrategy. - connection_timeout (int, optional): The timeout for the request. Default is 5 seconds. + Args: + endpoint (str): The API endpoint to connect to. + headers (dict, optional): The headers to use for the API request. Defaults to None. + retry_strategy (Strategies, optional): The strategy to use for retrying failed requests. Defaults to Strategies.NoRetryStrategy. + retries (int, optional): The number of times to retry a failed request. Defaults to 3. + delay (int, optional): The delay between retries in seconds. Defaults to 1. + connection_timeout (int, optional): The timeout for the connection in seconds. Defaults to 2. - Raises: - ValueError: If the endpoint is empty. + Raises: + ValueError: If endpoint is an empty string. + ValueError: If retries is not a non-negative integer. + ValueError: If delay is not a non-negative integer. + ValueError: If connection_timeout is not a non-negative integer. """ + + if headers is None: + headers = {} if endpoint == "": raise ValueError("::: endpoint param is mandatory :::") - else: - self.endpoint = endpoint - self.retry_strategy = retry_strategy - self.connection_timeout = connection_timeout - self.headers = headers - self.retries = retries - self.delay = delay + if not isinstance(retries, int) or retries < 0: + raise ValueError("retries must be a non-negative integer") + if not isinstance(delay, int) or delay < 0: + raise ValueError("delay must be a non-negative integer") + if not isinstance(connection_timeout, int) or connection_timeout < 0: + raise ValueError("connection_timeout must be a non-negative integer") + + self.endpoint = endpoint + self.retry_strategy = retry_strategy + self.connection_timeout = connection_timeout + self.headers = headers + self.retries = retries + self.delay = delay @RetryStrategies def get_api_data(self): @@ -40,6 +56,14 @@ def get_api_data(self): Returns: dict: The response from the API. """ + response = GetData.get_response( + endpoint=self.endpoint, + headers=self.headers, + connection_timeout=self.connection_timeout + ) + return response.json() + + def _get_raw_api_data(self): response = GetData.get_response( endpoint=self.endpoint, headers=self.headers, @@ -49,14 +73,5 @@ def get_api_data(self): @staticmethod def api_to_dataframe(response: dict): - """ - Converts the API response into a DataFrame. - - Args: - response (dict): The response from the API. - - Returns: - DataFrame: The data converted into a DataFrame. - """ df = GetData.to_dataframe(response) return df From f3520b65bc2bef6eb9efe3c6a74b081ed21a79b8 Mon Sep 17 00:00:00 2001 From: Ivanildo Barauna de Souza Junior Date: Thu, 20 Jun 2024 18:36:07 -0300 Subject: [PATCH 12/20] tests: development tests for retainer --- tests/test_models_retainer.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/test_models_retainer.py b/tests/test_models_retainer.py index e69de29..36084eb 100644 --- a/tests/test_models_retainer.py +++ b/tests/test_models_retainer.py @@ -0,0 +1,28 @@ +from api_to_dataframe import ClientBuilder, RetryStrategies +import requests +import time + + +def test_linear_strategy(): + endpoint = "https://api-to-dataframe/" + max_retries = 2 + client = ClientBuilder( + endpoint=endpoint, + retry_strategy=RetryStrategies.LinearRetryStrategy, + retries=max_retries, + delay=1, + connection_timeout=1 + ) + + retry_number = 0 + + while retry_number < max_retries: + try: + start = time.time() + client.get_api_data() + except requests.exceptions.RequestException as e: + end = time.time() + assert end - start >= client.delay + retry_number += 1 + + assert retry_number == max_retries From a41c3c37a0304e73125087ee9b67a11e07bb5511 Mon Sep 17 00:00:00 2001 From: Ivanildo Barauna de Souza Junior Date: Thu, 20 Jun 2024 18:36:40 -0300 Subject: [PATCH 13/20] tests: Added extreme scenarios for raises on ClientBuilder --- tests/test_controller_client_builder.py | 40 +++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/tests/test_controller_client_builder.py b/tests/test_controller_client_builder.py index 43a3684..876334d 100644 --- a/tests/test_controller_client_builder.py +++ b/tests/test_controller_client_builder.py @@ -17,10 +17,46 @@ def response_setup(): return new_client.get_api_data() -def test_constructor_without_param(): +def test_constructor_raises(): with pytest.raises(ValueError): new_client = ClientBuilder(endpoint="") + with pytest.raises(ValueError): + new_client = ClientBuilder( + endpoint="https://economia.awesomeapi.com.br/last/USD-BRL", + retries=-1 + ) + + with pytest.raises(ValueError): + new_client = ClientBuilder( + endpoint="https://economia.awesomeapi.com.br/last/USD-BRL", + delay=-1 + ) + + with pytest.raises(ValueError): + new_client = ClientBuilder( + endpoint="https://economia.awesomeapi.com.br/last/USD-BRL", + connection_timeout=-1 + ) + + with pytest.raises(ValueError): + new_client = ClientBuilder( + endpoint="https://economia.awesomeapi.com.br/last/USD-BRL", + retries="" + ) + + with pytest.raises(ValueError): + new_client = ClientBuilder( + endpoint="https://economia.awesomeapi.com.br/last/USD-BRL", + delay="" + ) + + with pytest.raises(ValueError): + new_client = ClientBuilder( + endpoint="https://economia.awesomeapi.com.br/last/USD-BRL", + connection_timeout="" + ) + def test_constructor_with_param(setup): expected_result = "https://economia.awesomeapi.com.br/last/USD-BRL" @@ -30,7 +66,7 @@ def test_constructor_with_param(setup): def test_response_to_json(setup): new_client = setup - response = new_client.get_api_data() + response = new_client._get_raw_api_data() assert isinstance(response, requests.Response) From 1addea269a71e3a62df37fafb869801d9c6c9420 Mon Sep 17 00:00:00 2001 From: Ivanildo Barauna de Souza Junior Date: Thu, 20 Jun 2024 18:37:06 -0300 Subject: [PATCH 14/20] chore: change reserved name of var --- src/api_to_dataframe/models/retainer.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/api_to_dataframe/models/retainer.py b/src/api_to_dataframe/models/retainer.py index cf2b5d4..ea0bf6b 100644 --- a/src/api_to_dataframe/models/retainer.py +++ b/src/api_to_dataframe/models/retainer.py @@ -10,21 +10,21 @@ class Strategies(Enum): def RetryStrategies(func): def wrapper(*args, **kwargs): - attemp = 0 - while attemp < args[0].retries: + retry_number = 0 + while retry_number < args[0].retries: try: return func(*args, **kwargs) except Exception as e: - attemp += 1 + retry_number += 1 if args[0].retry_strategy == Strategies.NoRetryStrategy: break elif args[0].retry_strategy == Strategies.LinearRetryStrategy: time.sleep(args[0].delay) elif args[0].retry_strategy == Strategies.ExponentialRetryStrategy: - time.sleep(args[0].delay * 2 ** attemp) + time.sleep(args[0].delay * 2 ** retry_number) - if attemp == args[0].retries: - print(f"Failed after {args[0].retries} attempts") + if retry_number == args[0].retries: + print(f"Failed after {args[0].retries} retries") raise e - return wrapper \ No newline at end of file + return wrapper From 8edd975f844f4b49f896906fa740a98c463d6a4c Mon Sep 17 00:00:00 2001 From: Ivanildo Barauna de Souza Junior Date: Thu, 20 Jun 2024 18:37:38 -0300 Subject: [PATCH 15/20] chore: return json in main lib function --- src/api_to_dataframe/models/get_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api_to_dataframe/models/get_data.py b/src/api_to_dataframe/models/get_data.py index 5b3147c..9e79fe3 100644 --- a/src/api_to_dataframe/models/get_data.py +++ b/src/api_to_dataframe/models/get_data.py @@ -20,7 +20,7 @@ def get_response(endpoint: str, @staticmethod def to_dataframe(response): try: - df = pd.DataFrame(response.json()) + df = pd.DataFrame(response) except Exception as err: raise TypeError(f"Invalid response for transform in dataframe: {err}") From b1c1b32bdad20dd004fdbd1c5892c4217fc31065 Mon Sep 17 00:00:00 2001 From: Ivanildo Barauna de Souza Junior Date: Thu, 20 Jun 2024 22:27:55 -0300 Subject: [PATCH 16/20] feat: change default retry for 1 --- src/api_to_dataframe/controller/client_builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api_to_dataframe/controller/client_builder.py b/src/api_to_dataframe/controller/client_builder.py index ae37dc5..b52ed9e 100644 --- a/src/api_to_dataframe/controller/client_builder.py +++ b/src/api_to_dataframe/controller/client_builder.py @@ -8,7 +8,7 @@ def __init__(self, endpoint: str, headers: dict = None, retry_strategy: Strategies = Strategies.NoRetryStrategy, - retries: int = 2, + retries: int = 1, delay: int = 1, connection_timeout: int = 1): From b0f50adffbcc86ab4ab1ebd2556ed4f007ac05ed Mon Sep 17 00:00:00 2001 From: Ivanildo Barauna de Souza Junior Date: Thu, 20 Jun 2024 22:28:39 -0300 Subject: [PATCH 17/20] feat: up raise for NoRetryStrategy --- src/api_to_dataframe/models/retainer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api_to_dataframe/models/retainer.py b/src/api_to_dataframe/models/retainer.py index ea0bf6b..cadd75c 100644 --- a/src/api_to_dataframe/models/retainer.py +++ b/src/api_to_dataframe/models/retainer.py @@ -18,7 +18,7 @@ def wrapper(*args, **kwargs): retry_number += 1 if args[0].retry_strategy == Strategies.NoRetryStrategy: - break + raise e elif args[0].retry_strategy == Strategies.LinearRetryStrategy: time.sleep(args[0].delay) elif args[0].retry_strategy == Strategies.ExponentialRetryStrategy: From 0be8cb41d014aa775af6fce5f88810aa9f6d96cf Mon Sep 17 00:00:00 2001 From: Ivanildo Barauna de Souza Junior Date: Thu, 20 Jun 2024 22:29:07 -0300 Subject: [PATCH 18/20] tests: increase codeCov to 100% --- tests/test_controller_client_builder.py | 2 +- tests/test_models_retainer.py | 37 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/tests/test_controller_client_builder.py b/tests/test_controller_client_builder.py index 876334d..db4b24d 100644 --- a/tests/test_controller_client_builder.py +++ b/tests/test_controller_client_builder.py @@ -2,7 +2,7 @@ import pandas as pd import requests -from api_to_dataframe import ClientBuilder, RetryStrategies +from api_to_dataframe import ClientBuilder @pytest.fixture() diff --git a/tests/test_models_retainer.py b/tests/test_models_retainer.py index 36084eb..94c8997 100644 --- a/tests/test_models_retainer.py +++ b/tests/test_models_retainer.py @@ -1,6 +1,7 @@ from api_to_dataframe import ClientBuilder, RetryStrategies import requests import time +import pytest def test_linear_strategy(): @@ -26,3 +27,39 @@ def test_linear_strategy(): retry_number += 1 assert retry_number == max_retries + + +def test_no_retry_strategy(): + endpoint = "https://api-to-dataframe/" + client = ClientBuilder( + endpoint=endpoint, + retry_strategy=RetryStrategies.NoRetryStrategy, + ) + + with pytest.raises(requests.exceptions.RequestException) as e: + client.get_api_data() + + +def test_exponential_strategy(): + endpoint = "https://api-to-dataframe/" + max_retries = 2 + client = ClientBuilder( + endpoint=endpoint, + retry_strategy=RetryStrategies.ExponentialRetryStrategy, + retries=max_retries, + delay=1, + connection_timeout=1 + ) + + retry_number = 0 + + while retry_number < max_retries: + try: + start = time.time() + client.get_api_data() + except requests.exceptions.RequestException as e: + end = time.time() + assert end - start >= client.delay * 2 ** retry_number + retry_number += 1 + + assert retry_number == max_retries From 291b47a8a4ea5e4b24042b19126d4e3d6cca1b8d Mon Sep 17 00:00:00 2001 From: Ivanildo Barauna de Souza Junior Date: Fri, 21 Jun 2024 00:56:16 -0300 Subject: [PATCH 19/20] chore: Update Documentation + set quantity of retry to 3 --- README.md | 21 ++- notebooks/example.ipynb | 137 ++++++++++++++---- .../controller/client_builder.py | 2 +- src/api_to_dataframe/models/retainer.py | 2 +- 4 files changed, 131 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 3e7b133..2ce9b7d 100644 --- a/README.md +++ b/README.md @@ -45,24 +45,37 @@ To install the package using poetry, use the following command: poetry add api-to-dataframe ``` -## How to use it +## User Guide ``` python ## Importing library from api_to_dataframe import ClientBuilder, RetryStrategies -# Create a client for simple ingest data from API (timeout 5 seconds) +# Create a client for simple ingest data from API (timeout 1 second) client = ClientBuilder(endpoint="https://api.example.com") -# if you can define timeout, use: (default is 5 seconds), with LinearStrategy (In development, actually don't nothing) and set headers: +# if you can define timeout with LinearStrategy and set headers: headers = { "application_name": "api_to_dataframe" } client = ClientBuilder(endpoint="https://api.example.com" ,retry_strategy=RetryStrategies.LinearStrategy - ,timeout=10 + ,connection_timeout=2 ,headers=headers) +""" + NOTE: by default the quantity of retries is 3 and the time between retries is 1 second, but you can define manually, like this: + +""" + +client = ClientBuilder(endpoint="https://api.example.com" + ,retry_strategy=RetryStrategies.LinearStrategy + ,connection_timeout=10 + ,headers=headers + ,retries=5 + ,delay=10) + + ### timeout, retry_strategy and headers are opcionals parameters # Get data from the API diff --git a/notebooks/example.ipynb b/notebooks/example.ipynb index 5916d39..df17caf 100644 --- a/notebooks/example.ipynb +++ b/notebooks/example.ipynb @@ -4,8 +4,8 @@ "cell_type": "code", "metadata": { "ExecuteTime": { - "end_time": "2024-06-20T17:28:18.653294Z", - "start_time": "2024-06-20T17:28:13.772706Z" + "end_time": "2024-06-21T03:43:17.568393Z", + "start_time": "2024-06-21T03:43:14.781816Z" } }, "source": "pip install --upgrade pip && pip install api-to-dataframe", @@ -14,30 +14,30 @@ "name": "stdout", "output_type": "stream", "text": [ - "Requirement already satisfied: pip in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (24.0)\r\n", - "Requirement already satisfied: api-to-dataframe in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (1.2.0)\r\n", - "Requirement already satisfied: pandas<3.0.0,>=2.2.2 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from api-to-dataframe) (2.2.2)\r\n", - "Requirement already satisfied: requests<3.0.0,>=2.32.3 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from api-to-dataframe) (2.32.3)\r\n", - "Requirement already satisfied: numpy>=1.22.4 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from pandas<3.0.0,>=2.2.2->api-to-dataframe) (1.26.4)\r\n", - "Requirement already satisfied: python-dateutil>=2.8.2 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from pandas<3.0.0,>=2.2.2->api-to-dataframe) (2.9.0.post0)\r\n", - "Requirement already satisfied: pytz>=2020.1 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from pandas<3.0.0,>=2.2.2->api-to-dataframe) (2024.1)\r\n", - "Requirement already satisfied: tzdata>=2022.7 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from pandas<3.0.0,>=2.2.2->api-to-dataframe) (2024.1)\r\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from requests<3.0.0,>=2.32.3->api-to-dataframe) (3.3.2)\r\n", - "Requirement already satisfied: idna<4,>=2.5 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from requests<3.0.0,>=2.32.3->api-to-dataframe) (3.7)\r\n", - "Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from requests<3.0.0,>=2.32.3->api-to-dataframe) (2.2.1)\r\n", - "Requirement already satisfied: certifi>=2017.4.17 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from requests<3.0.0,>=2.32.3->api-to-dataframe) (2024.6.2)\r\n", - "Requirement already satisfied: six>=1.5 in /Users/ivsouza/repos/personal_repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from python-dateutil>=2.8.2->pandas<3.0.0,>=2.2.2->api-to-dataframe) (1.16.0)\r\n", + "Requirement already satisfied: pip in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (24.1)\r\n", + "Requirement already satisfied: api-to-dataframe in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (1.0.1)\r\n", + "Requirement already satisfied: pandas<3.0.0,>=2.2.2 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from api-to-dataframe) (2.2.2)\r\n", + "Requirement already satisfied: requests<3.0.0,>=2.32.3 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from api-to-dataframe) (2.32.3)\r\n", + "Requirement already satisfied: numpy>=1.22.4 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from pandas<3.0.0,>=2.2.2->api-to-dataframe) (1.26.4)\r\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from pandas<3.0.0,>=2.2.2->api-to-dataframe) (2.9.0.post0)\r\n", + "Requirement already satisfied: pytz>=2020.1 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from pandas<3.0.0,>=2.2.2->api-to-dataframe) (2024.1)\r\n", + "Requirement already satisfied: tzdata>=2022.7 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from pandas<3.0.0,>=2.2.2->api-to-dataframe) (2024.1)\r\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from requests<3.0.0,>=2.32.3->api-to-dataframe) (3.3.2)\r\n", + "Requirement already satisfied: idna<4,>=2.5 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from requests<3.0.0,>=2.32.3->api-to-dataframe) (3.7)\r\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from requests<3.0.0,>=2.32.3->api-to-dataframe) (2.2.1)\r\n", + "Requirement already satisfied: certifi>=2017.4.17 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from requests<3.0.0,>=2.32.3->api-to-dataframe) (2024.6.2)\r\n", + "Requirement already satisfied: six>=1.5 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from python-dateutil>=2.8.2->pandas<3.0.0,>=2.2.2->api-to-dataframe) (1.16.0)\r\n", "Note: you may need to restart the kernel to use updated packages.\n" ] } ], - "execution_count": 3 + "execution_count": 13 }, { "metadata": { "ExecuteTime": { - "end_time": "2024-06-20T17:30:43.076831Z", - "start_time": "2024-06-20T17:30:42.981486Z" + "end_time": "2024-06-21T03:43:25.478264Z", + "start_time": "2024-06-21T03:43:21.227314Z" } }, "cell_type": "code", @@ -45,19 +45,70 @@ "from api_to_dataframe import ClientBuilder, RetryStrategies\n", "\n", "client = ClientBuilder(\n", - " endpoint=\"https://brasilapi.com.br/api/banks/v1\", retry_strategy=RetryStrategies)\n", + " endpoint=\"https://invalidendpoint.org.net\", retry_strategy=RetryStrategies.LinearRetryStrategy)\n", "\n", "api_data = client.get_api_data()\n", "df = client.api_to_dataframe(api_data)" ], - "outputs": [], - "execution_count": 6 + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Failed after 3 retries\n" + ] + }, + { + "ename": "SSLError", + "evalue": "HTTPSConnectionPool(host='invalidendpoint.org.net', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:1129)')))", + "output_type": "error", + "traceback": [ + "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[0;31mSSLCertVerificationError\u001B[0m Traceback (most recent call last)", + "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/urllib3/connectionpool.py:467\u001B[0m, in \u001B[0;36mHTTPConnectionPool._make_request\u001B[0;34m(self, conn, method, url, body, headers, retries, timeout, chunked, response_conn, preload_content, decode_content, enforce_content_length)\u001B[0m\n\u001B[1;32m 466\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[0;32m--> 467\u001B[0m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_validate_conn\u001B[49m\u001B[43m(\u001B[49m\u001B[43mconn\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 468\u001B[0m \u001B[38;5;28;01mexcept\u001B[39;00m (SocketTimeout, BaseSSLError) \u001B[38;5;28;01mas\u001B[39;00m e:\n", + "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/urllib3/connectionpool.py:1099\u001B[0m, in \u001B[0;36mHTTPSConnectionPool._validate_conn\u001B[0;34m(self, conn)\u001B[0m\n\u001B[1;32m 1098\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m conn\u001B[38;5;241m.\u001B[39mis_closed:\n\u001B[0;32m-> 1099\u001B[0m \u001B[43mconn\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mconnect\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 1101\u001B[0m \u001B[38;5;66;03m# TODO revise this, see https://github.com/urllib3/urllib3/issues/2791\u001B[39;00m\n", + "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/urllib3/connection.py:653\u001B[0m, in \u001B[0;36mHTTPSConnection.connect\u001B[0;34m(self)\u001B[0m\n\u001B[1;32m 651\u001B[0m server_hostname_rm_dot \u001B[38;5;241m=\u001B[39m server_hostname\u001B[38;5;241m.\u001B[39mrstrip(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124m.\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[0;32m--> 653\u001B[0m sock_and_verified \u001B[38;5;241m=\u001B[39m \u001B[43m_ssl_wrap_socket_and_match_hostname\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 654\u001B[0m \u001B[43m \u001B[49m\u001B[43msock\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43msock\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 655\u001B[0m \u001B[43m \u001B[49m\u001B[43mcert_reqs\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mcert_reqs\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 656\u001B[0m \u001B[43m \u001B[49m\u001B[43mssl_version\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mssl_version\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 657\u001B[0m \u001B[43m \u001B[49m\u001B[43mssl_minimum_version\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mssl_minimum_version\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 658\u001B[0m \u001B[43m \u001B[49m\u001B[43mssl_maximum_version\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mssl_maximum_version\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 659\u001B[0m \u001B[43m \u001B[49m\u001B[43mca_certs\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mca_certs\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 660\u001B[0m \u001B[43m \u001B[49m\u001B[43mca_cert_dir\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mca_cert_dir\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 661\u001B[0m \u001B[43m \u001B[49m\u001B[43mca_cert_data\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mca_cert_data\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 662\u001B[0m \u001B[43m \u001B[49m\u001B[43mcert_file\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mcert_file\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 663\u001B[0m \u001B[43m \u001B[49m\u001B[43mkey_file\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mkey_file\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 664\u001B[0m \u001B[43m \u001B[49m\u001B[43mkey_password\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mkey_password\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 665\u001B[0m \u001B[43m \u001B[49m\u001B[43mserver_hostname\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mserver_hostname_rm_dot\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 666\u001B[0m \u001B[43m \u001B[49m\u001B[43mssl_context\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mssl_context\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 667\u001B[0m \u001B[43m \u001B[49m\u001B[43mtls_in_tls\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mtls_in_tls\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 668\u001B[0m \u001B[43m \u001B[49m\u001B[43massert_hostname\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43massert_hostname\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 669\u001B[0m \u001B[43m \u001B[49m\u001B[43massert_fingerprint\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43massert_fingerprint\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 670\u001B[0m \u001B[43m\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 671\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39msock \u001B[38;5;241m=\u001B[39m sock_and_verified\u001B[38;5;241m.\u001B[39msocket\n", + "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/urllib3/connection.py:806\u001B[0m, in \u001B[0;36m_ssl_wrap_socket_and_match_hostname\u001B[0;34m(sock, cert_reqs, ssl_version, ssl_minimum_version, ssl_maximum_version, cert_file, key_file, key_password, ca_certs, ca_cert_dir, ca_cert_data, assert_hostname, assert_fingerprint, server_hostname, ssl_context, tls_in_tls)\u001B[0m\n\u001B[1;32m 804\u001B[0m server_hostname \u001B[38;5;241m=\u001B[39m normalized\n\u001B[0;32m--> 806\u001B[0m ssl_sock \u001B[38;5;241m=\u001B[39m \u001B[43mssl_wrap_socket\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 807\u001B[0m \u001B[43m \u001B[49m\u001B[43msock\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43msock\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 808\u001B[0m \u001B[43m \u001B[49m\u001B[43mkeyfile\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mkey_file\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 809\u001B[0m \u001B[43m \u001B[49m\u001B[43mcertfile\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mcert_file\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 810\u001B[0m \u001B[43m \u001B[49m\u001B[43mkey_password\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mkey_password\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 811\u001B[0m \u001B[43m \u001B[49m\u001B[43mca_certs\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mca_certs\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 812\u001B[0m \u001B[43m \u001B[49m\u001B[43mca_cert_dir\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mca_cert_dir\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 813\u001B[0m \u001B[43m \u001B[49m\u001B[43mca_cert_data\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mca_cert_data\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 814\u001B[0m \u001B[43m \u001B[49m\u001B[43mserver_hostname\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mserver_hostname\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 815\u001B[0m \u001B[43m \u001B[49m\u001B[43mssl_context\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mcontext\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 816\u001B[0m \u001B[43m \u001B[49m\u001B[43mtls_in_tls\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mtls_in_tls\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 817\u001B[0m \u001B[43m\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 819\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n", + "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/urllib3/util/ssl_.py:465\u001B[0m, in \u001B[0;36mssl_wrap_socket\u001B[0;34m(sock, keyfile, certfile, cert_reqs, ca_certs, server_hostname, ssl_version, ciphers, ssl_context, ca_cert_dir, key_password, ca_cert_data, tls_in_tls)\u001B[0m\n\u001B[1;32m 463\u001B[0m \u001B[38;5;28;01mpass\u001B[39;00m\n\u001B[0;32m--> 465\u001B[0m ssl_sock \u001B[38;5;241m=\u001B[39m \u001B[43m_ssl_wrap_socket_impl\u001B[49m\u001B[43m(\u001B[49m\u001B[43msock\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mcontext\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mtls_in_tls\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mserver_hostname\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 466\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m ssl_sock\n", + "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/urllib3/util/ssl_.py:509\u001B[0m, in \u001B[0;36m_ssl_wrap_socket_impl\u001B[0;34m(sock, ssl_context, tls_in_tls, server_hostname)\u001B[0m\n\u001B[1;32m 507\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m SSLTransport(sock, ssl_context, server_hostname)\n\u001B[0;32m--> 509\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43mssl_context\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mwrap_socket\u001B[49m\u001B[43m(\u001B[49m\u001B[43msock\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mserver_hostname\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mserver_hostname\u001B[49m\u001B[43m)\u001B[49m\n", + "File \u001B[0;32m~/.pyenv/versions/3.9.6/lib/python3.9/ssl.py:500\u001B[0m, in \u001B[0;36mSSLContext.wrap_socket\u001B[0;34m(self, sock, server_side, do_handshake_on_connect, suppress_ragged_eofs, server_hostname, session)\u001B[0m\n\u001B[1;32m 494\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mwrap_socket\u001B[39m(\u001B[38;5;28mself\u001B[39m, sock, server_side\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mFalse\u001B[39;00m,\n\u001B[1;32m 495\u001B[0m do_handshake_on_connect\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mTrue\u001B[39;00m,\n\u001B[1;32m 496\u001B[0m suppress_ragged_eofs\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mTrue\u001B[39;00m,\n\u001B[1;32m 497\u001B[0m server_hostname\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mNone\u001B[39;00m, session\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mNone\u001B[39;00m):\n\u001B[1;32m 498\u001B[0m \u001B[38;5;66;03m# SSLSocket class handles server_hostname encoding before it calls\u001B[39;00m\n\u001B[1;32m 499\u001B[0m \u001B[38;5;66;03m# ctx._wrap_socket()\u001B[39;00m\n\u001B[0;32m--> 500\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43msslsocket_class\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_create\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 501\u001B[0m \u001B[43m \u001B[49m\u001B[43msock\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43msock\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 502\u001B[0m \u001B[43m \u001B[49m\u001B[43mserver_side\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mserver_side\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 503\u001B[0m \u001B[43m \u001B[49m\u001B[43mdo_handshake_on_connect\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mdo_handshake_on_connect\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 504\u001B[0m \u001B[43m \u001B[49m\u001B[43msuppress_ragged_eofs\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43msuppress_ragged_eofs\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 505\u001B[0m \u001B[43m \u001B[49m\u001B[43mserver_hostname\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mserver_hostname\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 506\u001B[0m \u001B[43m \u001B[49m\u001B[43mcontext\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[43m,\u001B[49m\n\u001B[1;32m 507\u001B[0m \u001B[43m \u001B[49m\u001B[43msession\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43msession\u001B[49m\n\u001B[1;32m 508\u001B[0m \u001B[43m \u001B[49m\u001B[43m)\u001B[49m\n", + "File \u001B[0;32m~/.pyenv/versions/3.9.6/lib/python3.9/ssl.py:1040\u001B[0m, in \u001B[0;36mSSLSocket._create\u001B[0;34m(cls, sock, server_side, do_handshake_on_connect, suppress_ragged_eofs, server_hostname, context, session)\u001B[0m\n\u001B[1;32m 1039\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mValueError\u001B[39;00m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mdo_handshake_on_connect should not be specified for non-blocking sockets\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[0;32m-> 1040\u001B[0m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mdo_handshake\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 1041\u001B[0m \u001B[38;5;28;01mexcept\u001B[39;00m (\u001B[38;5;167;01mOSError\u001B[39;00m, \u001B[38;5;167;01mValueError\u001B[39;00m):\n", + "File \u001B[0;32m~/.pyenv/versions/3.9.6/lib/python3.9/ssl.py:1309\u001B[0m, in \u001B[0;36mSSLSocket.do_handshake\u001B[0;34m(self, block)\u001B[0m\n\u001B[1;32m 1308\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39msettimeout(\u001B[38;5;28;01mNone\u001B[39;00m)\n\u001B[0;32m-> 1309\u001B[0m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_sslobj\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mdo_handshake\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 1310\u001B[0m \u001B[38;5;28;01mfinally\u001B[39;00m:\n", + "\u001B[0;31mSSLCertVerificationError\u001B[0m: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:1129)", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001B[0;31mSSLError\u001B[0m Traceback (most recent call last)", + "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/urllib3/connectionpool.py:793\u001B[0m, in \u001B[0;36mHTTPConnectionPool.urlopen\u001B[0;34m(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)\u001B[0m\n\u001B[1;32m 792\u001B[0m \u001B[38;5;66;03m# Make the request on the HTTPConnection object\u001B[39;00m\n\u001B[0;32m--> 793\u001B[0m response \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_make_request\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 794\u001B[0m \u001B[43m \u001B[49m\u001B[43mconn\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 795\u001B[0m \u001B[43m \u001B[49m\u001B[43mmethod\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 796\u001B[0m \u001B[43m \u001B[49m\u001B[43murl\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 797\u001B[0m \u001B[43m \u001B[49m\u001B[43mtimeout\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mtimeout_obj\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 798\u001B[0m \u001B[43m \u001B[49m\u001B[43mbody\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mbody\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 799\u001B[0m \u001B[43m \u001B[49m\u001B[43mheaders\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mheaders\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 800\u001B[0m \u001B[43m \u001B[49m\u001B[43mchunked\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mchunked\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 801\u001B[0m \u001B[43m \u001B[49m\u001B[43mretries\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mretries\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 802\u001B[0m \u001B[43m \u001B[49m\u001B[43mresponse_conn\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mresponse_conn\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 803\u001B[0m \u001B[43m \u001B[49m\u001B[43mpreload_content\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mpreload_content\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 804\u001B[0m \u001B[43m \u001B[49m\u001B[43mdecode_content\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mdecode_content\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 805\u001B[0m \u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mresponse_kw\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 806\u001B[0m \u001B[43m\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 808\u001B[0m \u001B[38;5;66;03m# Everything went great!\u001B[39;00m\n", + "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/urllib3/connectionpool.py:491\u001B[0m, in \u001B[0;36mHTTPConnectionPool._make_request\u001B[0;34m(self, conn, method, url, body, headers, retries, timeout, chunked, response_conn, preload_content, decode_content, enforce_content_length)\u001B[0m\n\u001B[1;32m 490\u001B[0m new_e \u001B[38;5;241m=\u001B[39m _wrap_proxy_error(new_e, conn\u001B[38;5;241m.\u001B[39mproxy\u001B[38;5;241m.\u001B[39mscheme)\n\u001B[0;32m--> 491\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m new_e\n\u001B[1;32m 493\u001B[0m \u001B[38;5;66;03m# conn.request() calls http.client.*.request, not the method in\u001B[39;00m\n\u001B[1;32m 494\u001B[0m \u001B[38;5;66;03m# urllib3.request. It also calls makefile (recv) on the socket.\u001B[39;00m\n", + "\u001B[0;31mSSLError\u001B[0m: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:1129)", + "\nThe above exception was the direct cause of the following exception:\n", + "\u001B[0;31mMaxRetryError\u001B[0m Traceback (most recent call last)", + "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/requests/adapters.py:667\u001B[0m, in \u001B[0;36mHTTPAdapter.send\u001B[0;34m(self, request, stream, timeout, verify, cert, proxies)\u001B[0m\n\u001B[1;32m 666\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[0;32m--> 667\u001B[0m resp \u001B[38;5;241m=\u001B[39m \u001B[43mconn\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43murlopen\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 668\u001B[0m \u001B[43m \u001B[49m\u001B[43mmethod\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mrequest\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mmethod\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 669\u001B[0m \u001B[43m \u001B[49m\u001B[43murl\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43murl\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 670\u001B[0m \u001B[43m \u001B[49m\u001B[43mbody\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mrequest\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mbody\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 671\u001B[0m \u001B[43m \u001B[49m\u001B[43mheaders\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mrequest\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mheaders\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 672\u001B[0m \u001B[43m \u001B[49m\u001B[43mredirect\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43;01mFalse\u001B[39;49;00m\u001B[43m,\u001B[49m\n\u001B[1;32m 673\u001B[0m \u001B[43m \u001B[49m\u001B[43massert_same_host\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43;01mFalse\u001B[39;49;00m\u001B[43m,\u001B[49m\n\u001B[1;32m 674\u001B[0m \u001B[43m \u001B[49m\u001B[43mpreload_content\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43;01mFalse\u001B[39;49;00m\u001B[43m,\u001B[49m\n\u001B[1;32m 675\u001B[0m \u001B[43m \u001B[49m\u001B[43mdecode_content\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43;01mFalse\u001B[39;49;00m\u001B[43m,\u001B[49m\n\u001B[1;32m 676\u001B[0m \u001B[43m \u001B[49m\u001B[43mretries\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mmax_retries\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 677\u001B[0m \u001B[43m \u001B[49m\u001B[43mtimeout\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mtimeout\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 678\u001B[0m \u001B[43m \u001B[49m\u001B[43mchunked\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mchunked\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 679\u001B[0m \u001B[43m \u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 681\u001B[0m \u001B[38;5;28;01mexcept\u001B[39;00m (ProtocolError, \u001B[38;5;167;01mOSError\u001B[39;00m) \u001B[38;5;28;01mas\u001B[39;00m err:\n", + "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/urllib3/connectionpool.py:847\u001B[0m, in \u001B[0;36mHTTPConnectionPool.urlopen\u001B[0;34m(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)\u001B[0m\n\u001B[1;32m 845\u001B[0m new_e \u001B[38;5;241m=\u001B[39m ProtocolError(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mConnection aborted.\u001B[39m\u001B[38;5;124m\"\u001B[39m, new_e)\n\u001B[0;32m--> 847\u001B[0m retries \u001B[38;5;241m=\u001B[39m \u001B[43mretries\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mincrement\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 848\u001B[0m \u001B[43m \u001B[49m\u001B[43mmethod\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43murl\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43merror\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mnew_e\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43m_pool\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43m_stacktrace\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43msys\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mexc_info\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\u001B[43m[\u001B[49m\u001B[38;5;241;43m2\u001B[39;49m\u001B[43m]\u001B[49m\n\u001B[1;32m 849\u001B[0m \u001B[43m\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 850\u001B[0m retries\u001B[38;5;241m.\u001B[39msleep()\n", + "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/urllib3/util/retry.py:515\u001B[0m, in \u001B[0;36mRetry.increment\u001B[0;34m(self, method, url, response, error, _pool, _stacktrace)\u001B[0m\n\u001B[1;32m 514\u001B[0m reason \u001B[38;5;241m=\u001B[39m error \u001B[38;5;129;01mor\u001B[39;00m ResponseError(cause)\n\u001B[0;32m--> 515\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m MaxRetryError(_pool, url, reason) \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mreason\u001B[39;00m \u001B[38;5;66;03m# type: ignore[arg-type]\u001B[39;00m\n\u001B[1;32m 517\u001B[0m log\u001B[38;5;241m.\u001B[39mdebug(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mIncremented Retry for (url=\u001B[39m\u001B[38;5;124m'\u001B[39m\u001B[38;5;132;01m%s\u001B[39;00m\u001B[38;5;124m'\u001B[39m\u001B[38;5;124m): \u001B[39m\u001B[38;5;132;01m%r\u001B[39;00m\u001B[38;5;124m\"\u001B[39m, url, new_retry)\n", + "\u001B[0;31mMaxRetryError\u001B[0m: HTTPSConnectionPool(host='invalidendpoint.org.net', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:1129)')))", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001B[0;31mSSLError\u001B[0m Traceback (most recent call last)", + "Cell \u001B[0;32mIn[14], line 6\u001B[0m\n\u001B[1;32m 1\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mapi_to_dataframe\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m ClientBuilder, RetryStrategies\n\u001B[1;32m 3\u001B[0m client \u001B[38;5;241m=\u001B[39m ClientBuilder(\n\u001B[1;32m 4\u001B[0m endpoint\u001B[38;5;241m=\u001B[39m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mhttps://invalidendpoint.org.net\u001B[39m\u001B[38;5;124m\"\u001B[39m, retry_strategy\u001B[38;5;241m=\u001B[39mRetryStrategies\u001B[38;5;241m.\u001B[39mLinearRetryStrategy)\n\u001B[0;32m----> 6\u001B[0m api_data \u001B[38;5;241m=\u001B[39m \u001B[43mclient\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget_api_data\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 7\u001B[0m df \u001B[38;5;241m=\u001B[39m client\u001B[38;5;241m.\u001B[39mapi_to_dataframe(api_data)\n", + "File \u001B[0;32m~/repos/api-to-dataframe/src/api_to_dataframe/models/retainer.py:29\u001B[0m, in \u001B[0;36mRetryStrategies..wrapper\u001B[0;34m(*args, **kwargs)\u001B[0m\n\u001B[1;32m 27\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m retry_number \u001B[38;5;241m==\u001B[39m args[\u001B[38;5;241m0\u001B[39m]\u001B[38;5;241m.\u001B[39mretries:\n\u001B[1;32m 28\u001B[0m \u001B[38;5;28mprint\u001B[39m(\u001B[38;5;124mf\u001B[39m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mFailed after \u001B[39m\u001B[38;5;132;01m{\u001B[39;00mretry_number\u001B[38;5;132;01m}\u001B[39;00m\u001B[38;5;124m retries\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[0;32m---> 29\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m e\n", + "File \u001B[0;32m~/repos/api-to-dataframe/src/api_to_dataframe/models/retainer.py:16\u001B[0m, in \u001B[0;36mRetryStrategies..wrapper\u001B[0;34m(*args, **kwargs)\u001B[0m\n\u001B[1;32m 14\u001B[0m \u001B[38;5;28;01mwhile\u001B[39;00m retry_number \u001B[38;5;241m<\u001B[39m args[\u001B[38;5;241m0\u001B[39m]\u001B[38;5;241m.\u001B[39mretries:\n\u001B[1;32m 15\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[0;32m---> 16\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43mfunc\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43margs\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mkwargs\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 17\u001B[0m \u001B[38;5;28;01mexcept\u001B[39;00m \u001B[38;5;167;01mException\u001B[39;00m \u001B[38;5;28;01mas\u001B[39;00m e:\n\u001B[1;32m 18\u001B[0m retry_number \u001B[38;5;241m+\u001B[39m\u001B[38;5;241m=\u001B[39m \u001B[38;5;241m1\u001B[39m\n", + "File \u001B[0;32m~/repos/api-to-dataframe/src/api_to_dataframe/controller/client_builder.py:59\u001B[0m, in \u001B[0;36mClientBuilder.get_api_data\u001B[0;34m(self)\u001B[0m\n\u001B[1;32m 51\u001B[0m \u001B[38;5;129m@RetryStrategies\u001B[39m\n\u001B[1;32m 52\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mget_api_data\u001B[39m(\u001B[38;5;28mself\u001B[39m):\n\u001B[1;32m 53\u001B[0m \u001B[38;5;250m \u001B[39m\u001B[38;5;124;03m\"\"\"\u001B[39;00m\n\u001B[1;32m 54\u001B[0m \u001B[38;5;124;03m Retrieves data from the API using the defined endpoint and retry strategy.\u001B[39;00m\n\u001B[1;32m 55\u001B[0m \n\u001B[1;32m 56\u001B[0m \u001B[38;5;124;03m Returns:\u001B[39;00m\n\u001B[1;32m 57\u001B[0m \u001B[38;5;124;03m dict: The response from the API.\u001B[39;00m\n\u001B[1;32m 58\u001B[0m \u001B[38;5;124;03m \"\"\"\u001B[39;00m\n\u001B[0;32m---> 59\u001B[0m response \u001B[38;5;241m=\u001B[39m \u001B[43mGetData\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget_response\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 60\u001B[0m \u001B[43m \u001B[49m\u001B[43mendpoint\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mendpoint\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 61\u001B[0m \u001B[43m \u001B[49m\u001B[43mheaders\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mheaders\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 62\u001B[0m \u001B[43m \u001B[49m\u001B[43mconnection_timeout\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mconnection_timeout\u001B[49m\n\u001B[1;32m 63\u001B[0m \u001B[43m \u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 64\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m response\u001B[38;5;241m.\u001B[39mjson()\n", + "File \u001B[0;32m~/repos/api-to-dataframe/src/api_to_dataframe/models/get_data.py:17\u001B[0m, in \u001B[0;36mGetData.get_response\u001B[0;34m(endpoint, headers, connection_timeout)\u001B[0m\n\u001B[1;32m 10\u001B[0m \u001B[38;5;129m@staticmethod\u001B[39m\n\u001B[1;32m 11\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mget_response\u001B[39m(endpoint: \u001B[38;5;28mstr\u001B[39m,\n\u001B[1;32m 12\u001B[0m headers: \u001B[38;5;28mdict\u001B[39m,\n\u001B[1;32m 13\u001B[0m connection_timeout: \u001B[38;5;28mint\u001B[39m):\n\u001B[0;32m---> 17\u001B[0m response \u001B[38;5;241m=\u001B[39m \u001B[43mrequests\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget\u001B[49m\u001B[43m(\u001B[49m\u001B[43mendpoint\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mtimeout\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mconnection_timeout\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mheaders\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mheaders\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 18\u001B[0m response\u001B[38;5;241m.\u001B[39mraise_for_status()\n\u001B[1;32m 19\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m response\n", + "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/requests/api.py:73\u001B[0m, in \u001B[0;36mget\u001B[0;34m(url, params, **kwargs)\u001B[0m\n\u001B[1;32m 62\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mget\u001B[39m(url, params\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mNone\u001B[39;00m, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mkwargs):\n\u001B[1;32m 63\u001B[0m \u001B[38;5;250m \u001B[39m\u001B[38;5;124mr\u001B[39m\u001B[38;5;124;03m\"\"\"Sends a GET request.\u001B[39;00m\n\u001B[1;32m 64\u001B[0m \n\u001B[1;32m 65\u001B[0m \u001B[38;5;124;03m :param url: URL for the new :class:`Request` object.\u001B[39;00m\n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 70\u001B[0m \u001B[38;5;124;03m :rtype: requests.Response\u001B[39;00m\n\u001B[1;32m 71\u001B[0m \u001B[38;5;124;03m \"\"\"\u001B[39;00m\n\u001B[0;32m---> 73\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43mrequest\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mget\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43murl\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mparams\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mparams\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mkwargs\u001B[49m\u001B[43m)\u001B[49m\n", + "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/requests/api.py:59\u001B[0m, in \u001B[0;36mrequest\u001B[0;34m(method, url, **kwargs)\u001B[0m\n\u001B[1;32m 55\u001B[0m \u001B[38;5;66;03m# By using the 'with' statement we are sure the session is closed, thus we\u001B[39;00m\n\u001B[1;32m 56\u001B[0m \u001B[38;5;66;03m# avoid leaving sockets open which can trigger a ResourceWarning in some\u001B[39;00m\n\u001B[1;32m 57\u001B[0m \u001B[38;5;66;03m# cases, and look like a memory leak in others.\u001B[39;00m\n\u001B[1;32m 58\u001B[0m \u001B[38;5;28;01mwith\u001B[39;00m sessions\u001B[38;5;241m.\u001B[39mSession() \u001B[38;5;28;01mas\u001B[39;00m session:\n\u001B[0;32m---> 59\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43msession\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mrequest\u001B[49m\u001B[43m(\u001B[49m\u001B[43mmethod\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mmethod\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43murl\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43murl\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mkwargs\u001B[49m\u001B[43m)\u001B[49m\n", + "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/requests/sessions.py:589\u001B[0m, in \u001B[0;36mSession.request\u001B[0;34m(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json)\u001B[0m\n\u001B[1;32m 584\u001B[0m send_kwargs \u001B[38;5;241m=\u001B[39m {\n\u001B[1;32m 585\u001B[0m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mtimeout\u001B[39m\u001B[38;5;124m\"\u001B[39m: timeout,\n\u001B[1;32m 586\u001B[0m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mallow_redirects\u001B[39m\u001B[38;5;124m\"\u001B[39m: allow_redirects,\n\u001B[1;32m 587\u001B[0m }\n\u001B[1;32m 588\u001B[0m send_kwargs\u001B[38;5;241m.\u001B[39mupdate(settings)\n\u001B[0;32m--> 589\u001B[0m resp \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43msend\u001B[49m\u001B[43m(\u001B[49m\u001B[43mprep\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43msend_kwargs\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 591\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m resp\n", + "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/requests/sessions.py:703\u001B[0m, in \u001B[0;36mSession.send\u001B[0;34m(self, request, **kwargs)\u001B[0m\n\u001B[1;32m 700\u001B[0m start \u001B[38;5;241m=\u001B[39m preferred_clock()\n\u001B[1;32m 702\u001B[0m \u001B[38;5;66;03m# Send the request\u001B[39;00m\n\u001B[0;32m--> 703\u001B[0m r \u001B[38;5;241m=\u001B[39m \u001B[43madapter\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43msend\u001B[49m\u001B[43m(\u001B[49m\u001B[43mrequest\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mkwargs\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 705\u001B[0m \u001B[38;5;66;03m# Total elapsed time of the request (approximately)\u001B[39;00m\n\u001B[1;32m 706\u001B[0m elapsed \u001B[38;5;241m=\u001B[39m preferred_clock() \u001B[38;5;241m-\u001B[39m start\n", + "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/requests/adapters.py:698\u001B[0m, in \u001B[0;36mHTTPAdapter.send\u001B[0;34m(self, request, stream, timeout, verify, cert, proxies)\u001B[0m\n\u001B[1;32m 694\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m ProxyError(e, request\u001B[38;5;241m=\u001B[39mrequest)\n\u001B[1;32m 696\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28misinstance\u001B[39m(e\u001B[38;5;241m.\u001B[39mreason, _SSLError):\n\u001B[1;32m 697\u001B[0m \u001B[38;5;66;03m# This branch is for urllib3 v1.22 and later.\u001B[39;00m\n\u001B[0;32m--> 698\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m SSLError(e, request\u001B[38;5;241m=\u001B[39mrequest)\n\u001B[1;32m 700\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mConnectionError\u001B[39;00m(e, request\u001B[38;5;241m=\u001B[39mrequest)\n\u001B[1;32m 702\u001B[0m \u001B[38;5;28;01mexcept\u001B[39;00m ClosedPoolError \u001B[38;5;28;01mas\u001B[39;00m e:\n", + "\u001B[0;31mSSLError\u001B[0m: HTTPSConnectionPool(host='invalidendpoint.org.net', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:1129)')))" + ] + } + ], + "execution_count": 14 }, { "metadata": { "ExecuteTime": { - "end_time": "2024-06-20T17:30:51.598804Z", - "start_time": "2024-06-20T17:30:51.571405Z" + "end_time": "2024-06-21T03:41:07.994944Z", + "start_time": "2024-06-21T03:41:07.983369Z" } }, "cell_type": "code", @@ -146,12 +197,48 @@ "" ] }, - "execution_count": 7, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 7 + "execution_count": 9 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-06-21T03:41:08.001018Z", + "start_time": "2024-06-21T03:41:07.997693Z" + } + }, + "cell_type": "code", + "source": "", + "outputs": [], + "execution_count": 9 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-06-21T03:41:08.007672Z", + "start_time": "2024-06-21T03:41:08.004907Z" + } + }, + "cell_type": "code", + "source": "", + "outputs": [], + "execution_count": 9 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-06-21T03:41:08.012987Z", + "start_time": "2024-06-21T03:41:08.009705Z" + } + }, + "cell_type": "code", + "source": "", + "outputs": [], + "execution_count": 9 }, { "metadata": {}, diff --git a/src/api_to_dataframe/controller/client_builder.py b/src/api_to_dataframe/controller/client_builder.py index b52ed9e..3b731a4 100644 --- a/src/api_to_dataframe/controller/client_builder.py +++ b/src/api_to_dataframe/controller/client_builder.py @@ -8,7 +8,7 @@ def __init__(self, endpoint: str, headers: dict = None, retry_strategy: Strategies = Strategies.NoRetryStrategy, - retries: int = 1, + retries: int = 3, delay: int = 1, connection_timeout: int = 1): diff --git a/src/api_to_dataframe/models/retainer.py b/src/api_to_dataframe/models/retainer.py index cadd75c..9ad7137 100644 --- a/src/api_to_dataframe/models/retainer.py +++ b/src/api_to_dataframe/models/retainer.py @@ -25,6 +25,6 @@ def wrapper(*args, **kwargs): time.sleep(args[0].delay * 2 ** retry_number) if retry_number == args[0].retries: - print(f"Failed after {args[0].retries} retries") + print(f"Failed after {retry_number} retries using {args[0].retry_strategy}") raise e return wrapper From 7011b6de2fa78c3fe2f31cc176e38268ad2a1453 Mon Sep 17 00:00:00 2001 From: Ivanildo Barauna de Souza Junior Date: Fri, 21 Jun 2024 01:18:07 -0300 Subject: [PATCH 20/20] Update notebooks and evoluted project for Alpha phase --- notebooks/example.ipynb | 170 +++++++--------------------------------- pyproject.toml | 2 +- 2 files changed, 28 insertions(+), 144 deletions(-) diff --git a/notebooks/example.ipynb b/notebooks/example.ipynb index df17caf..34b6960 100644 --- a/notebooks/example.ipynb +++ b/notebooks/example.ipynb @@ -1,43 +1,25 @@ { "cells": [ { - "cell_type": "code", - "metadata": { - "ExecuteTime": { - "end_time": "2024-06-21T03:43:17.568393Z", - "start_time": "2024-06-21T03:43:14.781816Z" - } - }, - "source": "pip install --upgrade pip && pip install api-to-dataframe", - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Requirement already satisfied: pip in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (24.1)\r\n", - "Requirement already satisfied: api-to-dataframe in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (1.0.1)\r\n", - "Requirement already satisfied: pandas<3.0.0,>=2.2.2 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from api-to-dataframe) (2.2.2)\r\n", - "Requirement already satisfied: requests<3.0.0,>=2.32.3 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from api-to-dataframe) (2.32.3)\r\n", - "Requirement already satisfied: numpy>=1.22.4 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from pandas<3.0.0,>=2.2.2->api-to-dataframe) (1.26.4)\r\n", - "Requirement already satisfied: python-dateutil>=2.8.2 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from pandas<3.0.0,>=2.2.2->api-to-dataframe) (2.9.0.post0)\r\n", - "Requirement already satisfied: pytz>=2020.1 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from pandas<3.0.0,>=2.2.2->api-to-dataframe) (2024.1)\r\n", - "Requirement already satisfied: tzdata>=2022.7 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from pandas<3.0.0,>=2.2.2->api-to-dataframe) (2024.1)\r\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from requests<3.0.0,>=2.32.3->api-to-dataframe) (3.3.2)\r\n", - "Requirement already satisfied: idna<4,>=2.5 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from requests<3.0.0,>=2.32.3->api-to-dataframe) (3.7)\r\n", - "Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from requests<3.0.0,>=2.32.3->api-to-dataframe) (2.2.1)\r\n", - "Requirement already satisfied: certifi>=2017.4.17 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from requests<3.0.0,>=2.32.3->api-to-dataframe) (2024.6.2)\r\n", - "Requirement already satisfied: six>=1.5 in /Users/ivbarauna/repos/api-to-dataframe/.venv/lib/python3.9/site-packages (from python-dateutil>=2.8.2->pandas<3.0.0,>=2.2.2->api-to-dataframe) (1.16.0)\r\n", - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "execution_count": 13 + "metadata": {}, + "cell_type": "markdown", + "source": [ + "## Case use - Using Linear Strategy\n", + "For more examples see [User Guide on README](https://github.com/IvanildoBarauna/api-to-dataframe/blob/main/README.md#how-to-use-it)\n", + "\n", + "NOTE: Before ensure that api-to-dataframe is installed as a dependency, see how to do this [here](https://github.com/IvanildoBarauna/api-to-dataframe/blob/main/README.md#installation)" + ] + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "" }, { "metadata": { "ExecuteTime": { - "end_time": "2024-06-21T03:43:25.478264Z", - "start_time": "2024-06-21T03:43:21.227314Z" + "end_time": "2024-06-21T04:14:54.199749Z", + "start_time": "2024-06-21T04:14:54.124358Z" } }, "cell_type": "code", @@ -45,74 +27,19 @@ "from api_to_dataframe import ClientBuilder, RetryStrategies\n", "\n", "client = ClientBuilder(\n", - " endpoint=\"https://invalidendpoint.org.net\", retry_strategy=RetryStrategies.LinearRetryStrategy)\n", + " endpoint=\"https://brasilapi.com.br/api/banks/v1\",\n", + " retry_strategy=RetryStrategies.LinearRetryStrategy,\n", + " retries=3,\n", + " connection_timeout=1,\n", + " delay=1)\n", "\n", + "## Get response.json\n", "api_data = client.get_api_data()\n", - "df = client.api_to_dataframe(api_data)" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Failed after 3 retries\n" - ] - }, - { - "ename": "SSLError", - "evalue": "HTTPSConnectionPool(host='invalidendpoint.org.net', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:1129)')))", - "output_type": "error", - "traceback": [ - "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[0;31mSSLCertVerificationError\u001B[0m Traceback (most recent call last)", - "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/urllib3/connectionpool.py:467\u001B[0m, in \u001B[0;36mHTTPConnectionPool._make_request\u001B[0;34m(self, conn, method, url, body, headers, retries, timeout, chunked, response_conn, preload_content, decode_content, enforce_content_length)\u001B[0m\n\u001B[1;32m 466\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[0;32m--> 467\u001B[0m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_validate_conn\u001B[49m\u001B[43m(\u001B[49m\u001B[43mconn\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 468\u001B[0m \u001B[38;5;28;01mexcept\u001B[39;00m (SocketTimeout, BaseSSLError) \u001B[38;5;28;01mas\u001B[39;00m e:\n", - "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/urllib3/connectionpool.py:1099\u001B[0m, in \u001B[0;36mHTTPSConnectionPool._validate_conn\u001B[0;34m(self, conn)\u001B[0m\n\u001B[1;32m 1098\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m conn\u001B[38;5;241m.\u001B[39mis_closed:\n\u001B[0;32m-> 1099\u001B[0m \u001B[43mconn\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mconnect\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 1101\u001B[0m \u001B[38;5;66;03m# TODO revise this, see https://github.com/urllib3/urllib3/issues/2791\u001B[39;00m\n", - "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/urllib3/connection.py:653\u001B[0m, in \u001B[0;36mHTTPSConnection.connect\u001B[0;34m(self)\u001B[0m\n\u001B[1;32m 651\u001B[0m server_hostname_rm_dot \u001B[38;5;241m=\u001B[39m server_hostname\u001B[38;5;241m.\u001B[39mrstrip(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124m.\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[0;32m--> 653\u001B[0m sock_and_verified \u001B[38;5;241m=\u001B[39m \u001B[43m_ssl_wrap_socket_and_match_hostname\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 654\u001B[0m \u001B[43m \u001B[49m\u001B[43msock\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43msock\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 655\u001B[0m \u001B[43m \u001B[49m\u001B[43mcert_reqs\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mcert_reqs\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 656\u001B[0m \u001B[43m \u001B[49m\u001B[43mssl_version\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mssl_version\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 657\u001B[0m \u001B[43m \u001B[49m\u001B[43mssl_minimum_version\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mssl_minimum_version\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 658\u001B[0m \u001B[43m \u001B[49m\u001B[43mssl_maximum_version\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mssl_maximum_version\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 659\u001B[0m \u001B[43m \u001B[49m\u001B[43mca_certs\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mca_certs\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 660\u001B[0m \u001B[43m \u001B[49m\u001B[43mca_cert_dir\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mca_cert_dir\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 661\u001B[0m \u001B[43m \u001B[49m\u001B[43mca_cert_data\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mca_cert_data\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 662\u001B[0m \u001B[43m \u001B[49m\u001B[43mcert_file\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mcert_file\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 663\u001B[0m \u001B[43m \u001B[49m\u001B[43mkey_file\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mkey_file\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 664\u001B[0m \u001B[43m \u001B[49m\u001B[43mkey_password\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mkey_password\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 665\u001B[0m \u001B[43m \u001B[49m\u001B[43mserver_hostname\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mserver_hostname_rm_dot\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 666\u001B[0m \u001B[43m \u001B[49m\u001B[43mssl_context\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mssl_context\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 667\u001B[0m \u001B[43m \u001B[49m\u001B[43mtls_in_tls\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mtls_in_tls\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 668\u001B[0m \u001B[43m \u001B[49m\u001B[43massert_hostname\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43massert_hostname\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 669\u001B[0m \u001B[43m \u001B[49m\u001B[43massert_fingerprint\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43massert_fingerprint\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 670\u001B[0m \u001B[43m\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 671\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39msock \u001B[38;5;241m=\u001B[39m sock_and_verified\u001B[38;5;241m.\u001B[39msocket\n", - "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/urllib3/connection.py:806\u001B[0m, in \u001B[0;36m_ssl_wrap_socket_and_match_hostname\u001B[0;34m(sock, cert_reqs, ssl_version, ssl_minimum_version, ssl_maximum_version, cert_file, key_file, key_password, ca_certs, ca_cert_dir, ca_cert_data, assert_hostname, assert_fingerprint, server_hostname, ssl_context, tls_in_tls)\u001B[0m\n\u001B[1;32m 804\u001B[0m server_hostname \u001B[38;5;241m=\u001B[39m normalized\n\u001B[0;32m--> 806\u001B[0m ssl_sock \u001B[38;5;241m=\u001B[39m \u001B[43mssl_wrap_socket\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 807\u001B[0m \u001B[43m \u001B[49m\u001B[43msock\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43msock\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 808\u001B[0m \u001B[43m \u001B[49m\u001B[43mkeyfile\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mkey_file\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 809\u001B[0m \u001B[43m \u001B[49m\u001B[43mcertfile\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mcert_file\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 810\u001B[0m \u001B[43m \u001B[49m\u001B[43mkey_password\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mkey_password\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 811\u001B[0m \u001B[43m \u001B[49m\u001B[43mca_certs\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mca_certs\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 812\u001B[0m \u001B[43m \u001B[49m\u001B[43mca_cert_dir\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mca_cert_dir\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 813\u001B[0m \u001B[43m \u001B[49m\u001B[43mca_cert_data\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mca_cert_data\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 814\u001B[0m \u001B[43m \u001B[49m\u001B[43mserver_hostname\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mserver_hostname\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 815\u001B[0m \u001B[43m \u001B[49m\u001B[43mssl_context\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mcontext\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 816\u001B[0m \u001B[43m \u001B[49m\u001B[43mtls_in_tls\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mtls_in_tls\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 817\u001B[0m \u001B[43m\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 819\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n", - "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/urllib3/util/ssl_.py:465\u001B[0m, in \u001B[0;36mssl_wrap_socket\u001B[0;34m(sock, keyfile, certfile, cert_reqs, ca_certs, server_hostname, ssl_version, ciphers, ssl_context, ca_cert_dir, key_password, ca_cert_data, tls_in_tls)\u001B[0m\n\u001B[1;32m 463\u001B[0m \u001B[38;5;28;01mpass\u001B[39;00m\n\u001B[0;32m--> 465\u001B[0m ssl_sock \u001B[38;5;241m=\u001B[39m \u001B[43m_ssl_wrap_socket_impl\u001B[49m\u001B[43m(\u001B[49m\u001B[43msock\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mcontext\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mtls_in_tls\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mserver_hostname\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 466\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m ssl_sock\n", - "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/urllib3/util/ssl_.py:509\u001B[0m, in \u001B[0;36m_ssl_wrap_socket_impl\u001B[0;34m(sock, ssl_context, tls_in_tls, server_hostname)\u001B[0m\n\u001B[1;32m 507\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m SSLTransport(sock, ssl_context, server_hostname)\n\u001B[0;32m--> 509\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43mssl_context\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mwrap_socket\u001B[49m\u001B[43m(\u001B[49m\u001B[43msock\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mserver_hostname\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mserver_hostname\u001B[49m\u001B[43m)\u001B[49m\n", - "File \u001B[0;32m~/.pyenv/versions/3.9.6/lib/python3.9/ssl.py:500\u001B[0m, in \u001B[0;36mSSLContext.wrap_socket\u001B[0;34m(self, sock, server_side, do_handshake_on_connect, suppress_ragged_eofs, server_hostname, session)\u001B[0m\n\u001B[1;32m 494\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mwrap_socket\u001B[39m(\u001B[38;5;28mself\u001B[39m, sock, server_side\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mFalse\u001B[39;00m,\n\u001B[1;32m 495\u001B[0m do_handshake_on_connect\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mTrue\u001B[39;00m,\n\u001B[1;32m 496\u001B[0m suppress_ragged_eofs\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mTrue\u001B[39;00m,\n\u001B[1;32m 497\u001B[0m server_hostname\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mNone\u001B[39;00m, session\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mNone\u001B[39;00m):\n\u001B[1;32m 498\u001B[0m \u001B[38;5;66;03m# SSLSocket class handles server_hostname encoding before it calls\u001B[39;00m\n\u001B[1;32m 499\u001B[0m \u001B[38;5;66;03m# ctx._wrap_socket()\u001B[39;00m\n\u001B[0;32m--> 500\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43msslsocket_class\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_create\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 501\u001B[0m \u001B[43m \u001B[49m\u001B[43msock\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43msock\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 502\u001B[0m \u001B[43m \u001B[49m\u001B[43mserver_side\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mserver_side\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 503\u001B[0m \u001B[43m \u001B[49m\u001B[43mdo_handshake_on_connect\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mdo_handshake_on_connect\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 504\u001B[0m \u001B[43m \u001B[49m\u001B[43msuppress_ragged_eofs\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43msuppress_ragged_eofs\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 505\u001B[0m \u001B[43m \u001B[49m\u001B[43mserver_hostname\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mserver_hostname\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 506\u001B[0m \u001B[43m \u001B[49m\u001B[43mcontext\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[43m,\u001B[49m\n\u001B[1;32m 507\u001B[0m \u001B[43m \u001B[49m\u001B[43msession\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43msession\u001B[49m\n\u001B[1;32m 508\u001B[0m \u001B[43m \u001B[49m\u001B[43m)\u001B[49m\n", - "File \u001B[0;32m~/.pyenv/versions/3.9.6/lib/python3.9/ssl.py:1040\u001B[0m, in \u001B[0;36mSSLSocket._create\u001B[0;34m(cls, sock, server_side, do_handshake_on_connect, suppress_ragged_eofs, server_hostname, context, session)\u001B[0m\n\u001B[1;32m 1039\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mValueError\u001B[39;00m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mdo_handshake_on_connect should not be specified for non-blocking sockets\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[0;32m-> 1040\u001B[0m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mdo_handshake\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 1041\u001B[0m \u001B[38;5;28;01mexcept\u001B[39;00m (\u001B[38;5;167;01mOSError\u001B[39;00m, \u001B[38;5;167;01mValueError\u001B[39;00m):\n", - "File \u001B[0;32m~/.pyenv/versions/3.9.6/lib/python3.9/ssl.py:1309\u001B[0m, in \u001B[0;36mSSLSocket.do_handshake\u001B[0;34m(self, block)\u001B[0m\n\u001B[1;32m 1308\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39msettimeout(\u001B[38;5;28;01mNone\u001B[39;00m)\n\u001B[0;32m-> 1309\u001B[0m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_sslobj\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mdo_handshake\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 1310\u001B[0m \u001B[38;5;28;01mfinally\u001B[39;00m:\n", - "\u001B[0;31mSSLCertVerificationError\u001B[0m: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:1129)", - "\nDuring handling of the above exception, another exception occurred:\n", - "\u001B[0;31mSSLError\u001B[0m Traceback (most recent call last)", - "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/urllib3/connectionpool.py:793\u001B[0m, in \u001B[0;36mHTTPConnectionPool.urlopen\u001B[0;34m(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)\u001B[0m\n\u001B[1;32m 792\u001B[0m \u001B[38;5;66;03m# Make the request on the HTTPConnection object\u001B[39;00m\n\u001B[0;32m--> 793\u001B[0m response \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_make_request\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 794\u001B[0m \u001B[43m \u001B[49m\u001B[43mconn\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 795\u001B[0m \u001B[43m \u001B[49m\u001B[43mmethod\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 796\u001B[0m \u001B[43m \u001B[49m\u001B[43murl\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 797\u001B[0m \u001B[43m \u001B[49m\u001B[43mtimeout\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mtimeout_obj\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 798\u001B[0m \u001B[43m \u001B[49m\u001B[43mbody\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mbody\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 799\u001B[0m \u001B[43m \u001B[49m\u001B[43mheaders\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mheaders\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 800\u001B[0m \u001B[43m \u001B[49m\u001B[43mchunked\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mchunked\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 801\u001B[0m \u001B[43m \u001B[49m\u001B[43mretries\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mretries\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 802\u001B[0m \u001B[43m \u001B[49m\u001B[43mresponse_conn\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mresponse_conn\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 803\u001B[0m \u001B[43m \u001B[49m\u001B[43mpreload_content\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mpreload_content\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 804\u001B[0m \u001B[43m \u001B[49m\u001B[43mdecode_content\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mdecode_content\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 805\u001B[0m \u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mresponse_kw\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 806\u001B[0m \u001B[43m\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 808\u001B[0m \u001B[38;5;66;03m# Everything went great!\u001B[39;00m\n", - "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/urllib3/connectionpool.py:491\u001B[0m, in \u001B[0;36mHTTPConnectionPool._make_request\u001B[0;34m(self, conn, method, url, body, headers, retries, timeout, chunked, response_conn, preload_content, decode_content, enforce_content_length)\u001B[0m\n\u001B[1;32m 490\u001B[0m new_e \u001B[38;5;241m=\u001B[39m _wrap_proxy_error(new_e, conn\u001B[38;5;241m.\u001B[39mproxy\u001B[38;5;241m.\u001B[39mscheme)\n\u001B[0;32m--> 491\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m new_e\n\u001B[1;32m 493\u001B[0m \u001B[38;5;66;03m# conn.request() calls http.client.*.request, not the method in\u001B[39;00m\n\u001B[1;32m 494\u001B[0m \u001B[38;5;66;03m# urllib3.request. It also calls makefile (recv) on the socket.\u001B[39;00m\n", - "\u001B[0;31mSSLError\u001B[0m: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:1129)", - "\nThe above exception was the direct cause of the following exception:\n", - "\u001B[0;31mMaxRetryError\u001B[0m Traceback (most recent call last)", - "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/requests/adapters.py:667\u001B[0m, in \u001B[0;36mHTTPAdapter.send\u001B[0;34m(self, request, stream, timeout, verify, cert, proxies)\u001B[0m\n\u001B[1;32m 666\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[0;32m--> 667\u001B[0m resp \u001B[38;5;241m=\u001B[39m \u001B[43mconn\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43murlopen\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 668\u001B[0m \u001B[43m \u001B[49m\u001B[43mmethod\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mrequest\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mmethod\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 669\u001B[0m \u001B[43m \u001B[49m\u001B[43murl\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43murl\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 670\u001B[0m \u001B[43m \u001B[49m\u001B[43mbody\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mrequest\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mbody\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 671\u001B[0m \u001B[43m \u001B[49m\u001B[43mheaders\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mrequest\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mheaders\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 672\u001B[0m \u001B[43m \u001B[49m\u001B[43mredirect\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43;01mFalse\u001B[39;49;00m\u001B[43m,\u001B[49m\n\u001B[1;32m 673\u001B[0m \u001B[43m \u001B[49m\u001B[43massert_same_host\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43;01mFalse\u001B[39;49;00m\u001B[43m,\u001B[49m\n\u001B[1;32m 674\u001B[0m \u001B[43m \u001B[49m\u001B[43mpreload_content\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43;01mFalse\u001B[39;49;00m\u001B[43m,\u001B[49m\n\u001B[1;32m 675\u001B[0m \u001B[43m \u001B[49m\u001B[43mdecode_content\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43;01mFalse\u001B[39;49;00m\u001B[43m,\u001B[49m\n\u001B[1;32m 676\u001B[0m \u001B[43m \u001B[49m\u001B[43mretries\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mmax_retries\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 677\u001B[0m \u001B[43m \u001B[49m\u001B[43mtimeout\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mtimeout\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 678\u001B[0m \u001B[43m \u001B[49m\u001B[43mchunked\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mchunked\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 679\u001B[0m \u001B[43m \u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 681\u001B[0m \u001B[38;5;28;01mexcept\u001B[39;00m (ProtocolError, \u001B[38;5;167;01mOSError\u001B[39;00m) \u001B[38;5;28;01mas\u001B[39;00m err:\n", - "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/urllib3/connectionpool.py:847\u001B[0m, in \u001B[0;36mHTTPConnectionPool.urlopen\u001B[0;34m(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)\u001B[0m\n\u001B[1;32m 845\u001B[0m new_e \u001B[38;5;241m=\u001B[39m ProtocolError(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mConnection aborted.\u001B[39m\u001B[38;5;124m\"\u001B[39m, new_e)\n\u001B[0;32m--> 847\u001B[0m retries \u001B[38;5;241m=\u001B[39m \u001B[43mretries\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mincrement\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 848\u001B[0m \u001B[43m \u001B[49m\u001B[43mmethod\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43murl\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43merror\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mnew_e\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43m_pool\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43m_stacktrace\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43msys\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mexc_info\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\u001B[43m[\u001B[49m\u001B[38;5;241;43m2\u001B[39;49m\u001B[43m]\u001B[49m\n\u001B[1;32m 849\u001B[0m \u001B[43m\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 850\u001B[0m retries\u001B[38;5;241m.\u001B[39msleep()\n", - "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/urllib3/util/retry.py:515\u001B[0m, in \u001B[0;36mRetry.increment\u001B[0;34m(self, method, url, response, error, _pool, _stacktrace)\u001B[0m\n\u001B[1;32m 514\u001B[0m reason \u001B[38;5;241m=\u001B[39m error \u001B[38;5;129;01mor\u001B[39;00m ResponseError(cause)\n\u001B[0;32m--> 515\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m MaxRetryError(_pool, url, reason) \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mreason\u001B[39;00m \u001B[38;5;66;03m# type: ignore[arg-type]\u001B[39;00m\n\u001B[1;32m 517\u001B[0m log\u001B[38;5;241m.\u001B[39mdebug(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mIncremented Retry for (url=\u001B[39m\u001B[38;5;124m'\u001B[39m\u001B[38;5;132;01m%s\u001B[39;00m\u001B[38;5;124m'\u001B[39m\u001B[38;5;124m): \u001B[39m\u001B[38;5;132;01m%r\u001B[39;00m\u001B[38;5;124m\"\u001B[39m, url, new_retry)\n", - "\u001B[0;31mMaxRetryError\u001B[0m: HTTPSConnectionPool(host='invalidendpoint.org.net', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:1129)')))", - "\nDuring handling of the above exception, another exception occurred:\n", - "\u001B[0;31mSSLError\u001B[0m Traceback (most recent call last)", - "Cell \u001B[0;32mIn[14], line 6\u001B[0m\n\u001B[1;32m 1\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mapi_to_dataframe\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m ClientBuilder, RetryStrategies\n\u001B[1;32m 3\u001B[0m client \u001B[38;5;241m=\u001B[39m ClientBuilder(\n\u001B[1;32m 4\u001B[0m endpoint\u001B[38;5;241m=\u001B[39m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mhttps://invalidendpoint.org.net\u001B[39m\u001B[38;5;124m\"\u001B[39m, retry_strategy\u001B[38;5;241m=\u001B[39mRetryStrategies\u001B[38;5;241m.\u001B[39mLinearRetryStrategy)\n\u001B[0;32m----> 6\u001B[0m api_data \u001B[38;5;241m=\u001B[39m \u001B[43mclient\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget_api_data\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 7\u001B[0m df \u001B[38;5;241m=\u001B[39m client\u001B[38;5;241m.\u001B[39mapi_to_dataframe(api_data)\n", - "File \u001B[0;32m~/repos/api-to-dataframe/src/api_to_dataframe/models/retainer.py:29\u001B[0m, in \u001B[0;36mRetryStrategies..wrapper\u001B[0;34m(*args, **kwargs)\u001B[0m\n\u001B[1;32m 27\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m retry_number \u001B[38;5;241m==\u001B[39m args[\u001B[38;5;241m0\u001B[39m]\u001B[38;5;241m.\u001B[39mretries:\n\u001B[1;32m 28\u001B[0m \u001B[38;5;28mprint\u001B[39m(\u001B[38;5;124mf\u001B[39m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mFailed after \u001B[39m\u001B[38;5;132;01m{\u001B[39;00mretry_number\u001B[38;5;132;01m}\u001B[39;00m\u001B[38;5;124m retries\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[0;32m---> 29\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m e\n", - "File \u001B[0;32m~/repos/api-to-dataframe/src/api_to_dataframe/models/retainer.py:16\u001B[0m, in \u001B[0;36mRetryStrategies..wrapper\u001B[0;34m(*args, **kwargs)\u001B[0m\n\u001B[1;32m 14\u001B[0m \u001B[38;5;28;01mwhile\u001B[39;00m retry_number \u001B[38;5;241m<\u001B[39m args[\u001B[38;5;241m0\u001B[39m]\u001B[38;5;241m.\u001B[39mretries:\n\u001B[1;32m 15\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[0;32m---> 16\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43mfunc\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43margs\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mkwargs\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 17\u001B[0m \u001B[38;5;28;01mexcept\u001B[39;00m \u001B[38;5;167;01mException\u001B[39;00m \u001B[38;5;28;01mas\u001B[39;00m e:\n\u001B[1;32m 18\u001B[0m retry_number \u001B[38;5;241m+\u001B[39m\u001B[38;5;241m=\u001B[39m \u001B[38;5;241m1\u001B[39m\n", - "File \u001B[0;32m~/repos/api-to-dataframe/src/api_to_dataframe/controller/client_builder.py:59\u001B[0m, in \u001B[0;36mClientBuilder.get_api_data\u001B[0;34m(self)\u001B[0m\n\u001B[1;32m 51\u001B[0m \u001B[38;5;129m@RetryStrategies\u001B[39m\n\u001B[1;32m 52\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mget_api_data\u001B[39m(\u001B[38;5;28mself\u001B[39m):\n\u001B[1;32m 53\u001B[0m \u001B[38;5;250m \u001B[39m\u001B[38;5;124;03m\"\"\"\u001B[39;00m\n\u001B[1;32m 54\u001B[0m \u001B[38;5;124;03m Retrieves data from the API using the defined endpoint and retry strategy.\u001B[39;00m\n\u001B[1;32m 55\u001B[0m \n\u001B[1;32m 56\u001B[0m \u001B[38;5;124;03m Returns:\u001B[39;00m\n\u001B[1;32m 57\u001B[0m \u001B[38;5;124;03m dict: The response from the API.\u001B[39;00m\n\u001B[1;32m 58\u001B[0m \u001B[38;5;124;03m \"\"\"\u001B[39;00m\n\u001B[0;32m---> 59\u001B[0m response \u001B[38;5;241m=\u001B[39m \u001B[43mGetData\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget_response\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 60\u001B[0m \u001B[43m \u001B[49m\u001B[43mendpoint\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mendpoint\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 61\u001B[0m \u001B[43m \u001B[49m\u001B[43mheaders\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mheaders\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 62\u001B[0m \u001B[43m \u001B[49m\u001B[43mconnection_timeout\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mconnection_timeout\u001B[49m\n\u001B[1;32m 63\u001B[0m \u001B[43m \u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 64\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m response\u001B[38;5;241m.\u001B[39mjson()\n", - "File \u001B[0;32m~/repos/api-to-dataframe/src/api_to_dataframe/models/get_data.py:17\u001B[0m, in \u001B[0;36mGetData.get_response\u001B[0;34m(endpoint, headers, connection_timeout)\u001B[0m\n\u001B[1;32m 10\u001B[0m \u001B[38;5;129m@staticmethod\u001B[39m\n\u001B[1;32m 11\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mget_response\u001B[39m(endpoint: \u001B[38;5;28mstr\u001B[39m,\n\u001B[1;32m 12\u001B[0m headers: \u001B[38;5;28mdict\u001B[39m,\n\u001B[1;32m 13\u001B[0m connection_timeout: \u001B[38;5;28mint\u001B[39m):\n\u001B[0;32m---> 17\u001B[0m response \u001B[38;5;241m=\u001B[39m \u001B[43mrequests\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget\u001B[49m\u001B[43m(\u001B[49m\u001B[43mendpoint\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mtimeout\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mconnection_timeout\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mheaders\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mheaders\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 18\u001B[0m response\u001B[38;5;241m.\u001B[39mraise_for_status()\n\u001B[1;32m 19\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m response\n", - "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/requests/api.py:73\u001B[0m, in \u001B[0;36mget\u001B[0;34m(url, params, **kwargs)\u001B[0m\n\u001B[1;32m 62\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mget\u001B[39m(url, params\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mNone\u001B[39;00m, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mkwargs):\n\u001B[1;32m 63\u001B[0m \u001B[38;5;250m \u001B[39m\u001B[38;5;124mr\u001B[39m\u001B[38;5;124;03m\"\"\"Sends a GET request.\u001B[39;00m\n\u001B[1;32m 64\u001B[0m \n\u001B[1;32m 65\u001B[0m \u001B[38;5;124;03m :param url: URL for the new :class:`Request` object.\u001B[39;00m\n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 70\u001B[0m \u001B[38;5;124;03m :rtype: requests.Response\u001B[39;00m\n\u001B[1;32m 71\u001B[0m \u001B[38;5;124;03m \"\"\"\u001B[39;00m\n\u001B[0;32m---> 73\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43mrequest\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mget\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43murl\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mparams\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mparams\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mkwargs\u001B[49m\u001B[43m)\u001B[49m\n", - "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/requests/api.py:59\u001B[0m, in \u001B[0;36mrequest\u001B[0;34m(method, url, **kwargs)\u001B[0m\n\u001B[1;32m 55\u001B[0m \u001B[38;5;66;03m# By using the 'with' statement we are sure the session is closed, thus we\u001B[39;00m\n\u001B[1;32m 56\u001B[0m \u001B[38;5;66;03m# avoid leaving sockets open which can trigger a ResourceWarning in some\u001B[39;00m\n\u001B[1;32m 57\u001B[0m \u001B[38;5;66;03m# cases, and look like a memory leak in others.\u001B[39;00m\n\u001B[1;32m 58\u001B[0m \u001B[38;5;28;01mwith\u001B[39;00m sessions\u001B[38;5;241m.\u001B[39mSession() \u001B[38;5;28;01mas\u001B[39;00m session:\n\u001B[0;32m---> 59\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43msession\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mrequest\u001B[49m\u001B[43m(\u001B[49m\u001B[43mmethod\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mmethod\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43murl\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43murl\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mkwargs\u001B[49m\u001B[43m)\u001B[49m\n", - "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/requests/sessions.py:589\u001B[0m, in \u001B[0;36mSession.request\u001B[0;34m(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json)\u001B[0m\n\u001B[1;32m 584\u001B[0m send_kwargs \u001B[38;5;241m=\u001B[39m {\n\u001B[1;32m 585\u001B[0m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mtimeout\u001B[39m\u001B[38;5;124m\"\u001B[39m: timeout,\n\u001B[1;32m 586\u001B[0m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mallow_redirects\u001B[39m\u001B[38;5;124m\"\u001B[39m: allow_redirects,\n\u001B[1;32m 587\u001B[0m }\n\u001B[1;32m 588\u001B[0m send_kwargs\u001B[38;5;241m.\u001B[39mupdate(settings)\n\u001B[0;32m--> 589\u001B[0m resp \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43msend\u001B[49m\u001B[43m(\u001B[49m\u001B[43mprep\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43msend_kwargs\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 591\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m resp\n", - "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/requests/sessions.py:703\u001B[0m, in \u001B[0;36mSession.send\u001B[0;34m(self, request, **kwargs)\u001B[0m\n\u001B[1;32m 700\u001B[0m start \u001B[38;5;241m=\u001B[39m preferred_clock()\n\u001B[1;32m 702\u001B[0m \u001B[38;5;66;03m# Send the request\u001B[39;00m\n\u001B[0;32m--> 703\u001B[0m r \u001B[38;5;241m=\u001B[39m \u001B[43madapter\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43msend\u001B[49m\u001B[43m(\u001B[49m\u001B[43mrequest\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mkwargs\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 705\u001B[0m \u001B[38;5;66;03m# Total elapsed time of the request (approximately)\u001B[39;00m\n\u001B[1;32m 706\u001B[0m elapsed \u001B[38;5;241m=\u001B[39m preferred_clock() \u001B[38;5;241m-\u001B[39m start\n", - "File \u001B[0;32m~/repos/api-to-dataframe/.venv/lib/python3.9/site-packages/requests/adapters.py:698\u001B[0m, in \u001B[0;36mHTTPAdapter.send\u001B[0;34m(self, request, stream, timeout, verify, cert, proxies)\u001B[0m\n\u001B[1;32m 694\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m ProxyError(e, request\u001B[38;5;241m=\u001B[39mrequest)\n\u001B[1;32m 696\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28misinstance\u001B[39m(e\u001B[38;5;241m.\u001B[39mreason, _SSLError):\n\u001B[1;32m 697\u001B[0m \u001B[38;5;66;03m# This branch is for urllib3 v1.22 and later.\u001B[39;00m\n\u001B[0;32m--> 698\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m SSLError(e, request\u001B[38;5;241m=\u001B[39mrequest)\n\u001B[1;32m 700\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mConnectionError\u001B[39;00m(e, request\u001B[38;5;241m=\u001B[39mrequest)\n\u001B[1;32m 702\u001B[0m \u001B[38;5;28;01mexcept\u001B[39;00m ClosedPoolError \u001B[38;5;28;01mas\u001B[39;00m e:\n", - "\u001B[0;31mSSLError\u001B[0m: HTTPSConnectionPool(host='invalidendpoint.org.net', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:1129)')))" - ] - } + "\n", + "df = client.api_to_dataframe(api_data)\n", + "\n", + "df.head()" ], - "execution_count": 14 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2024-06-21T03:41:07.994944Z", - "start_time": "2024-06-21T03:41:07.983369Z" - } - }, - "cell_type": "code", - "source": "df.head()", "outputs": [ { "data": { @@ -197,55 +124,12 @@ "" ] }, - "execution_count": 9, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 9 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2024-06-21T03:41:08.001018Z", - "start_time": "2024-06-21T03:41:07.997693Z" - } - }, - "cell_type": "code", - "source": "", - "outputs": [], - "execution_count": 9 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2024-06-21T03:41:08.007672Z", - "start_time": "2024-06-21T03:41:08.004907Z" - } - }, - "cell_type": "code", - "source": "", - "outputs": [], - "execution_count": 9 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2024-06-21T03:41:08.012987Z", - "start_time": "2024-06-21T03:41:08.009705Z" - } - }, - "cell_type": "code", - "source": "", - "outputs": [], - "execution_count": 9 - }, - { - "metadata": {}, - "cell_type": "code", - "outputs": [], - "execution_count": null, - "source": "" + "execution_count": 21 } ], "metadata": { diff --git a/pyproject.toml b/pyproject.toml index 194b409..1d5f958 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ authors = ["IvanildoBarauna "] readme = "README.md" license = "MIT" classifiers=[ - "Development Status :: 2 - Pre-Alpha", + "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3",