diff --git a/.vscode/settings.json b/.vscode/settings.json index 745b3fa6..7967ec99 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,5 +10,41 @@ "keepit", "notif", "werein" - ] + ], + + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll.ruff": "explicit", + "source.organizeImports.ruff": "explicit" + }, + "[python]": { + "editor.defaultFormatter": "charliermarsh.ruff", + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll.ruff": "explicit", + "source.organizeImports.ruff": "explicit" + }, + "editor.rulers": [ + 120 + ], + "editor.tabSize": 4 + }, + "ruff.enable": true, + "ruff.organizeImports": true, + "ruff.fixAll": true, + "files.exclude": { + "**/__pycache__": true, + "**/.pytest_cache": true, + "**/.mypy_cache": true, + "**/.ruff_cache": true + }, + "files.watcherExclude": { + "**/.git/objects/**": true, + "**/.git/subtree-cache/**": true, + "**/node_modules/**": true, + "**/.hg/store/**": true, + "**/.venv/**": true, + "**/.mypy_cache/**": true, + "**/.ruff_cache/**": true + } } \ No newline at end of file diff --git a/Makefile b/Makefile index 3792e323..76aedb95 100644 --- a/Makefile +++ b/Makefile @@ -68,4 +68,6 @@ update: ## Update dependencies uv sync --upgrade sync: ## Sync dependencies - uv sync \ No newline at end of file + uv sync + +check: clean format lint pre-commit test ## Run all checks and tests \ No newline at end of file diff --git a/src/mistapi/__api_request.py b/src/mistapi/__api_request.py index e224ac4b..d154cba1 100644 --- a/src/mistapi/__api_request.py +++ b/src/mistapi/__api_request.py @@ -8,7 +8,7 @@ This package is licensed under the MIT License. -------------------------------------------------------------------------------- -This module manages API requests with Mist Cloud. It is used to +This module manages API requests with Mist Cloud. It is used to * generate the URL based on the provided parameters * add the required HTTP Headers to the request * report error if any @@ -64,13 +64,16 @@ def _url(self, uri) -> str: """ logger.debug("apirequest:_url:https://%s%s", self._cloud_uri, uri) return f"https://{self._cloud_uri}{uri}" - + def _log_proxy(self) -> None: - pwd_regex = r':([^:@]*)@' + pwd_regex = r":([^:@]*)@" if self._session.proxies.get("https"): - logger.info(f"apirequest:sending request to proxy server {re.sub(pwd_regex, ':*********@', self._session.proxies['https'])}") - print(f"apirequest:sending request to proxy server {re.sub(pwd_regex, ':*********@', self._session.proxies['https'])}") - + logger.info( + f"apirequest:sending request to proxy server {re.sub(pwd_regex, ':*********@', self._session.proxies['https'])}" + ) + print( + f"apirequest:sending request to proxy server {re.sub(pwd_regex, ':*********@', self._session.proxies['https'])}" + ) def _next_apitoken(self) -> None: logger.info("apirequest:_next_apitoken:rotating API Token") @@ -190,7 +193,9 @@ def mist_get(self, uri: str, query: dict = None) -> APIResponse: return self.mist_get(uri, query) logger.error(f"apirequest:mist_get:HTTP error occurred: {http_err}") if resp: - logger.error(f"apirequest:mist_get:HTTP error description: {resp.json()}") + logger.error( + f"apirequest:mist_get:HTTP error description: {resp.json()}" + ) except Exception as err: logger.error(f"apirequest:mist_get:Other error occurred: {err}") logger.error("apirequest:mist_get:Exception occurred", exc_info=True) @@ -232,7 +237,7 @@ def mist_post(self, uri: str, body: dict = None) -> APIResponse: logger.debug(f"apirequest:mist_post:request body:{resp.request.body}") resp.raise_for_status() except requests.exceptions.ProxyError as proxy_error: - logger.error(f"apirequest:mist_post:Proxy Error: {proxy_error}") + logger.error(f"apirequest:mist_post:Proxy Error: {proxy_error}") proxy_failed = True except requests.exceptions.ConnectionError as connexion_error: logger.error(f"Capirequest:mist_post:Connection Error: {connexion_error}") @@ -246,7 +251,9 @@ def mist_post(self, uri: str, body: dict = None) -> APIResponse: return self.mist_post(uri, body) logger.error(f"apirequest:mist_post: HTTP error occurred: {http_err}") if resp: - logger.error(f"apirequest:mist_post: HTTP error description: {resp.json()}") + logger.error( + f"apirequest:mist_post: HTTP error description: {resp.json()}" + ) except Exception as err: logger.error(f"apirequest:mist_post: Other error occurred: {err}") logger.error("apirequest:mist_post: Exception occurred", exc_info=True) @@ -288,7 +295,7 @@ def mist_put(self, uri: str, body: dict = None) -> APIResponse: logger.debug(f"apirequest:mist_put:request body:{resp.request.body}") resp.raise_for_status() except requests.exceptions.ProxyError as proxy_error: - logger.error(f"apirequest:mist_put:Proxy Error: {proxy_error}") + logger.error(f"apirequest:mist_put:Proxy Error: {proxy_error}") proxy_failed = True except requests.exceptions.ConnectionError as connexion_error: logger.error(f"apirequest:mist_put:Connection Error: {connexion_error}") @@ -302,7 +309,9 @@ def mist_put(self, uri: str, body: dict = None) -> APIResponse: return self.mist_put(uri, body) logger.error(f"apirequest:mist_put: HTTP error occurred: {http_err}") if resp: - logger.error(f"apirequest:mist_put: HTTP error description: {resp.json()}") + logger.error( + f"apirequest:mist_put: HTTP error description: {resp.json()}" + ) except Exception as err: logger.error(f"apirequest:mist_put: Other error occurred: {err}") logger.error("apirequest:mist_put: Exception occurred", exc_info=True) @@ -336,7 +345,7 @@ def mist_delete(self, uri: str, query: dict = None) -> APIResponse: ) resp.raise_for_status() except requests.exceptions.ProxyError as proxy_error: - logger.error(f"apirequest:mist_delete:Proxy Error: {proxy_error}") + logger.error(f"apirequest:mist_delete:Proxy Error: {proxy_error}") proxy_failed = True except requests.exceptions.ConnectionError as connexion_error: logger.error(f"apirequest:mist_delete:Connection Error: {connexion_error}") @@ -403,7 +412,7 @@ def mist_post_file(self, uri: str, multipart_form_data: dict = {}) -> APIRespons None, json.dumps(multipart_form_data[key]), ) - except: + except (OSError, json.JSONDecodeError): logger.error( f"apirequest:mist_post_file:multipart_form_data:" f"Unable to parse JSON object {key} " @@ -427,10 +436,12 @@ def mist_post_file(self, uri: str, multipart_form_data: dict = {}) -> APIRespons ) resp.raise_for_status() except requests.exceptions.ProxyError as proxy_error: - logger.error(f"apirequest:mist_post_file:Proxy Error: {proxy_error}") + logger.error(f"apirequest:mist_post_file:Proxy Error: {proxy_error}") proxy_failed = True except requests.exceptions.ConnectionError as connexion_error: - logger.error(f"apirequest:mist_post_file:Connection Error: {connexion_error}") + logger.error( + f"apirequest:mist_post_file:Connection Error: {connexion_error}" + ) except HTTPError as http_err: if http_err.response.status_code == 429: logger.warning( @@ -441,7 +452,9 @@ def mist_post_file(self, uri: str, multipart_form_data: dict = {}) -> APIRespons return self.mist_post_file(uri, multipart_form_data) logger.error(f"apirequest:mist_post_file: HTTP error occurred: {http_err}") if resp: - logger.error(f"apirequest:mist_post_file: HTTP error description: {resp.json()}") + logger.error( + f"apirequest:mist_post_file: HTTP error description: {resp.json()}" + ) except Exception as err: logger.error(f"apirequest:mist_post_file: Other error occurred: {err}") logger.error("apirequest:mist_post_file: Exception occurred", exc_info=True) diff --git a/src/mistapi/__api_response.py b/src/mistapi/__api_response.py index b1daa00c..d4e483e2 100644 --- a/src/mistapi/__api_response.py +++ b/src/mistapi/__api_response.py @@ -20,12 +20,7 @@ class APIResponse: Class used to pass API Responses """ - def __init__( - self, - response: Response , - url: str, - proxy_error: bool = False - ) -> None: + def __init__(self, response: Response, url: str, proxy_error: bool = False) -> None: """ PARAMS ----------- @@ -41,7 +36,7 @@ def __init__( self.headers = None self.status_code = None self.proxy_error = proxy_error - + if response is not None: self.headers = response.headers self.status_code = response.status_code @@ -82,17 +77,17 @@ def _check_next(self) -> None: page = int(page_str) if limit * page < total: uri = f"/api/{self.url.split('/api/')[1]}" - self.next = uri.replace(f"page={page}", f"page={page+1}") + self.next = uri.replace(f"page={page}", f"page={page + 1}") logger.debug(f"apiresponse:_check_next:set next to {self.next}") - except: + except ValueError: logger.error( f"apiresponse:_check_next:" - f"unable to convert total({total})/limit({limit})/page({page}) to int" + f"unable to convert total({total_str})/limit({limit_str})/page({page_str}) to int" ) logger.error( - "apirequest:mist_post_file:Exception occurred", exc_info=True + "apiresponse:_check_next:Exception occurred", exc_info=True ) console.error( f"Unable to convert total " - f"({total})/limit({limit})/page({page}) to int" + f"({total_str})/limit({limit_str})/page({page_str}) to int" ) diff --git a/src/mistapi/__api_session.py b/src/mistapi/__api_session.py index b5c49d8d..1bd7db4d 100644 --- a/src/mistapi/__api_session.py +++ b/src/mistapi/__api_session.py @@ -94,10 +94,7 @@ def __init__( https_proxy : str, default None HTTPS Proxy to use to send the API Requests """ - logger.info( - "mistapi:init:package version %s", - __version__ - ) + logger.info("mistapi:init:package version %s", __version__) self._cloud_uri = None self.email = None self._password = None @@ -181,10 +178,7 @@ def _load_env(self, env_file=None) -> None: ) env_file = os.path.abspath(env_file) console.debug(f"Loading settings from {env_file}") - logger.debug( - "apisession:_load_env:loading settings from %s", - env_file - ) + logger.debug("apisession:_load_env:loading settings from %s", env_file) dotenv_path = Path(env_file) load_dotenv(dotenv_path=dotenv_path, override=True) # else: @@ -240,25 +234,18 @@ def set_cloud(self, cloud_uri: str) -> None: self._cloud_uri = cloud_uri if self._cloud_uri: logger.debug( - "apisession:set_cloud:Mist Cloud configured to %s", - self._cloud_uri + "apisession:set_cloud:Mist Cloud configured to %s", self._cloud_uri ) console.debug(f"Mist Cloud configured to {self._cloud_uri}") else: - logger.error( - "apisession:set_cloud: %s is not valid", - cloud_uri - ) + logger.error("apisession:set_cloud: %s is not valid", cloud_uri) console.error(f"{cloud_uri} is not valid") def get_cloud(self): """ Return the Mist Cloud currently configured """ - logger.debug( - "apisession:get_cloud:return %s", - self._cloud_uri - ) + logger.debug("apisession:get_cloud:return %s", self._cloud_uri) return self._cloud_uri def select_cloud(self) -> None: @@ -278,7 +265,7 @@ def select_cloud(self) -> None: i += 1 print() - resp = input(f"Select a Cloud (0 to {i-1}, or q to quit): ") + resp = input(f"Select a Cloud (0 to {i - 1}, or q to quit): ") logger.info("apisession:select_cloud:input is %s", resp) if resp == "q": sys.exit(0) @@ -296,22 +283,18 @@ def select_cloud(self) -> None: if resp_num >= 0 and resp_num < i: logger.info( "apisession:select_cloud:Mist Cloud is %s", - CLOUDS[resp_num]['host'] + CLOUDS[resp_num]["host"], ) self.set_cloud(CLOUDS[resp_num]["host"]) else: print(f"Please enter a number between 0 and {i}.") logger.error( - "apisession:select_cloud:%s is not a valid input", - resp + "apisession:select_cloud:%s is not a valid input", resp ) self.select_cloud() - except: + except ValueError: print("\r\nPlease enter a number.") - logger.error( - "apisession:select_cloud:%s is not a valid input", - resp - ) + logger.error("apisession:select_cloud:%s is not a valid input", resp) self.select_cloud() #################################### @@ -330,10 +313,7 @@ def set_email(self, email: str = None) -> None: self.email = email else: self.email = input("Login: ") - logger.info( - "apisession:set_email:email configured to %s", - self.email - ) + logger.info("apisession:set_email:email configured to %s", self.email) console.debug(f"Email configured to {self.email}") def set_password(self, password: str = None) -> None: @@ -369,10 +349,7 @@ def set_api_token(self, apitoken: str) -> None: token = token.strip() if token and token not in apitokens_out: apitokens_out.append(token) - logger.info( - "apisession:set_api_token:found %s API Tokens", - len(apitokens_out) - ) + logger.info("apisession:set_api_token:found %s API Tokens", len(apitokens_out)) if self._check_api_tokens(apitokens_out): self._apitoken = apitokens_out self._apitoken_index = 0 @@ -391,10 +368,9 @@ def _get_api_token_data(self, apitoken) -> (str, list): data = requests.get(url, headers=headers, proxies=self._proxies, timeout=30) data_json = data.json() logger.debug( - "apisession:_get_api_token_data:" - "info retrieved for token %s...%s", + "apisession:_get_api_token_data:info retrieved for token %s...%s", apitoken[:4], - apitoken[-4:] + apitoken[-4:], ) except requests.exceptions.ProxyError: logger.critical("apisession:_get_api_token_data:proxy not valid...") @@ -402,17 +378,16 @@ def _get_api_token_data(self, apitoken) -> (str, list): sys.exit(0) except requests.exceptions.ConnectionError as connexion_error: logger.critical( - "apirequest:mist_post:Connection Error: %s", - connexion_error + "apirequest:mist_post:Connection Error: %s", connexion_error ) console.critical("Connexion error...\r\n") sys.exit(0) - except: + except Exception: logger.error( "apisession:_get_api_token_data:" "unable to retrieve info for token %s...%s", apitoken[:4], - apitoken[-4:] + apitoken[-4:], ) logger.error( "apirequest:_get_api_token_data: Exception occurred", exc_info=True @@ -445,7 +420,7 @@ def _get_api_token_data(self, apitoken) -> (str, list): priv, token_type, apitoken[:4], - apitoken[-4:] + apitoken[-4:], ) return (token_type, token_privileges) @@ -480,7 +455,7 @@ def _check_api_tokens(self, apitokens) -> None: token_type, token_value, primary_token_type, - primary_token_value + primary_token_value, ) else: logger.critical( @@ -490,7 +465,7 @@ def _check_api_tokens(self, apitokens) -> None: token_type, token_value, primary_token_type, - primary_token_value + primary_token_value, ) logger.critical(" /!\\ API TOKEN CRITICAL ERROR /!\\") logger.critical( @@ -534,10 +509,7 @@ def _process_login(self, retry: bool = True) -> None: self._set_authenticated(True) else: error = resp.json().get("detail") - logger.error( - "apisession:_process_login:authentication failed:%s", - error - ) + logger.error("apisession:_process_login:authentication failed:%s", error) console.error(f"Authentication failed: {error}\r\n") self.email = None self._password = None @@ -627,8 +599,7 @@ def login_with_return( logger.info("apisession:login_with_return:get self") uri = "/api/v1/self" logger.info( - 'apisession:login_with_return: sending GET request to "%s"', - uri + 'apisession:login_with_return: sending GET request to "%s"', uri ) resp = self.mist_get(uri) @@ -641,15 +612,13 @@ def login_with_return( error = self._process_login(retry=False) if error: logger.error( - "apisession:login_with_return:login/pwd auth failed: %s", - error + "apisession:login_with_return:login/pwd auth failed: %s", error ) return {"authenticated": False, "error": error} logger.info("apisession:login_with_return:get self") uri = "/api/v1/self" logger.info( - 'apisession:login_with_return: sending GET request to "%s"', - uri + 'apisession:login_with_return: sending GET request to "%s"', uri ) resp = self.mist_get(uri) @@ -661,10 +630,7 @@ def login_with_return( logger.info("apisession:login_with_return:access authorized") return {"authenticated": True, "error": ""} else: - logger.error( - "apisession:login_with_return:access denied: %s", - resp.data - ) + logger.error("apisession:login_with_return:access denied: %s", resp.data) return {"authenticated": False, "error": resp.data} def logout(self) -> None: @@ -686,8 +652,11 @@ def logout(self) -> None: else: try: console.error(resp.data["detail"]) - except: - console.error(resp.raw_data) + except (KeyError, TypeError, AttributeError): + if isinstance(resp.raw_data, bytes): + console.error(resp.raw_data.decode("utf-8", errors="replace")) + else: + console.error(str(resp.raw_data)) def _set_authenticated(self, authentication_status: bool) -> None: """ @@ -704,7 +673,7 @@ def _set_authenticated(self, authentication_status: bool) -> None: logger.debug("apisession:_set_authenticated") logger.debug( "apisession:_set_authenticated:authentication_status is %s", - authentication_status + authentication_status, ) if authentication_status: self._authenticated = True @@ -728,9 +697,9 @@ def _set_authenticated(self, authentication_status: bool) -> None: ) logger.info( "apisession:_set_authenticated:HTTP session cookies extracted. Cookies extension is %s", - cookies_ext + cookies_ext, ) - except: + except (StopIteration, KeyError, AttributeError): cookies_ext = "" logger.error( "apisession:_set_authenticated:unable to extract HTTP session cookies" @@ -760,8 +729,7 @@ def get_authentication_status(self) -> bool: Return the authentication status. """ logger.debug( - "apisession:get_authentication_status:return %s", - self._authenticated + "apisession:get_authentication_status:return %s", self._authenticated ) return self._authenticated @@ -801,7 +769,7 @@ def create_api_token(self, token_name: str = None) -> APIResponse: logger.info( "apisession:create_api_token:" 'sending POST request to "/api/v1/self/apitokens" with name "%s"', - token_name + token_name, ) resp = self.mist_post("/api/v1/self/apitokens", body=body) return resp @@ -824,7 +792,7 @@ def delete_api_token(self, apitoken_id: str) -> APIResponse: logger.info( "apisession:delete_api_token:" 'sending DELETE request to "/api/v1/self/apitokens" with token_id "%s"', - apitoken_id + apitoken_id, ) uri = f"https://{self._cloud_uri}/api/v1/self/apitokens/{apitoken_id}" resp = self._session.delete(uri) @@ -864,7 +832,7 @@ def _two_factor_authentication(self, two_factor: str) -> bool: logger.error( "apisession:_two_factor_authentication:" "2FA authentication failed with error code: %s", - resp.status_code + resp.status_code, ) console.error( f"2FA authentication failed with error code: {resp.status_code}\r\n" @@ -911,7 +879,7 @@ def _getself(self) -> None: logger.info( "apisession:_getself:account used: %s %s", self.first_name, - self.last_name + self.last_name, ) return True elif resp.proxy_error: @@ -950,18 +918,13 @@ def get_privilege_by_org_id(self, org_id: str): """ logger.debug("apisession:get_privilege_by_org_id") org_priv = next( - ( - priv - for priv in self.privileges - if priv.get("org_id") == org_id - ), - None + (priv for priv in self.privileges if priv.get("org_id") == org_id), None ) if org_priv: logger.info( "apisession:get_privilege_by_org_id:" "org %s privileges found in user info", - org_id + org_id, ) logger.debug("apisession:get_privilege_by_org_id: %s", org_priv) return org_priv @@ -969,12 +932,12 @@ def get_privilege_by_org_id(self, org_id: str): logger.warning( "apisession:get_privilege_by_org_id:" "unable of find org %s privileges in user data", - org_id + org_id, ) logger.info( "apisession:get_privilege_by_org_id:" "trying to request org %s info from the Cloud", - org_id + org_id, ) uri = f"/api/v1/orgs/{org_id}" msp_id = None @@ -982,10 +945,9 @@ def get_privilege_by_org_id(self, org_id: str): resp = self.mist_get(uri) if resp.data and resp.data.get("msp_id"): logger.info( - "apisession:get_privilege_by_org_id:" - "org %s belong to msp_id %s", + "apisession:get_privilege_by_org_id:org %s belong to msp_id %s", {org_id}, - resp.data['msp_id'] + resp.data["msp_id"], ) msp_id = resp.data.get("msp_id") else: @@ -993,7 +955,7 @@ def get_privilege_by_org_id(self, org_id: str): "apisession:get_privilege_by_org_id:" "not able to find msp_id information in the org info" ) - except: + except Exception: logger.error( "apisession:get_privilege_by_org_id: error when retrieving org info" ) @@ -1013,7 +975,7 @@ def get_privilege_by_org_id(self, org_id: str): logger.warning( "apisession:get_privilege_by_org_id:" "unable of find msp %s privileges in user data", - msp_id + msp_id, ) else: return { diff --git a/src/mistapi/__init__.py b/src/mistapi/__init__.py index 2abfca58..891e0354 100644 --- a/src/mistapi/__init__.py +++ b/src/mistapi/__init__.py @@ -1,4 +1,4 @@ -''' +""" -------------------------------------------------------------------------------- ------------------------- Mist API Python CLI Session -------------------------- @@ -8,10 +8,12 @@ This package is licensed under the MIT License. -------------------------------------------------------------------------------- -''' +""" -from mistapi.__api_session import APISession -from mistapi import api -from mistapi import cli -from mistapi.__pagination import get_next, get_all -from mistapi.__version import __author__, __version__ +from mistapi.__api_session import APISession as APISession +from mistapi import api as api +from mistapi import cli as cli +from mistapi.__pagination import get_all as get_all +from mistapi.__pagination import get_next as get_next +from mistapi.__version import __author__ as __author__ +from mistapi.__version import __version__ as __version__ diff --git a/src/mistapi/__logger.py b/src/mistapi/__logger.py index ff6384e3..e8c67de1 100644 --- a/src/mistapi/__logger.py +++ b/src/mistapi/__logger.py @@ -1,4 +1,4 @@ -''' +""" -------------------------------------------------------------------------------- ------------------------- Mist API Python CLI Session -------------------------- @@ -8,54 +8,70 @@ This package is licensed under the MIT License. -------------------------------------------------------------------------------- -''' +""" + import logging import os + os.system("") + def magenta(text): - return '\033[0;35m' + text + '\033[0m' + return "\033[0;35m" + text + "\033[0m" + + def red(text): - return '\033[0;31m' + text + '\033[0m' + return "\033[0;31m" + text + "\033[0m" + + def yellow(text): - return '\033[0;33m' + text + '\033[0m' + return "\033[0;33m" + text + "\033[0m" + + def green(text): - return '\033[0;32m' + text + '\033[0m' + return "\033[0;32m" + text + "\033[0m" + + def white(text): - return '\033[0;37m' + text + '\033[0m' + return "\033[0;37m" + text + "\033[0m" + + def cyan(text): - return '\033[0;36m' + text + '\033[0m' + return "\033[0;36m" + text + "\033[0m" + + def blue(text): - return '\033[0;34m' + text + '\033[0m' + return "\033[0;34m" + text + "\033[0m" -class Console: - def __init__(self, level:int=20): +class Console: + def __init__(self, level: int = 20): self.level = level - def critical(self, message:str) -> None: + def critical(self, message: str) -> None: if self.level <= 50 and self.level > 0: print(f"[{magenta('CRITICAL ')}] {message}") - def error(self, message:str) -> None: + def error(self, message: str) -> None: if self.level <= 40 and self.level > 0: print(f"[{red(' ERROR ')}] {message}") - def warning(self, message:str) -> None: + def warning(self, message: str) -> None: if self.level <= 30 and self.level > 0: print(f"[{yellow(' WARNING ')}] {message}") - def info(self, message:str) -> None: + def info(self, message: str) -> None: if self.level <= 20 and self.level > 0: print(f"[{green(' INFO ')}] {message}") - def debug(self, message:str) -> None: + def debug(self, message: str) -> None: if self.level <= 10 and self.level > 0: print(f"[{white('DEBUG ')}] {message}") - - def _set_log_level(self,console_log_level:str=20, logging_log_level:int=10) -> None: - """ + def _set_log_level( + self, console_log_level: str = 20, logging_log_level: int = 10 + ) -> None: + """ set console and logging log level PARAMS diff --git a/src/mistapi/__models/privilege.py b/src/mistapi/__models/privilege.py index e9b1f1ad..762ae8d5 100644 --- a/src/mistapi/__models/privilege.py +++ b/src/mistapi/__models/privilege.py @@ -1,14 +1,23 @@ - from tabulate import tabulate + class Privileges: def __init__(self, privileges): self.privileges = [] for privilege in privileges: - self.privileges.append(_Privilege(privilege)) + self.privileges.append(_Privilege(privilege)) def __str__(self): - columns_headers = ["scope", "role", "name", "site_id", "org_name", "org_id", 'msp_name', "msp_id" ] + columns_headers = [ + "scope", + "role", + "name", + "site_id", + "org_name", + "org_id", + "msp_name", + "msp_id", + ] table = [] for entry in self.privileges: temp = [] @@ -22,7 +31,7 @@ def __str__(self): def display(self): return str(self) - + class _Privilege: def __init__(self, privilege): @@ -39,11 +48,20 @@ def __init__(self, privilege): for key, val in privilege.items(): setattr(self, key, val) - - def __str__(self): - fields = ["scope", "role", "org_id", "org_name", "msp_id", "msp_name", - "orggroup_ids", "name", "role", "site_id", "sitegroup_ids"] + fields = [ + "scope", + "role", + "org_id", + "org_name", + "msp_id", + "msp_name", + "orggroup_ids", + "name", + "role", + "site_id", + "sitegroup_ids", + ] string = "" for field in fields: if getattr(self, field) != "": diff --git a/src/mistapi/__pagination.py b/src/mistapi/__pagination.py index 4e8826a3..bfd0a9c5 100644 --- a/src/mistapi/__pagination.py +++ b/src/mistapi/__pagination.py @@ -1,4 +1,4 @@ -''' +""" -------------------------------------------------------------------------------- ------------------------- Mist API Python CLI Session -------------------------- @@ -8,11 +8,12 @@ This package is licensed under the MIT License. -------------------------------------------------------------------------------- -''' +""" from mistapi import APISession as _APISession from mistapi.__api_response import APIResponse as _APIResponse + def get_next(mist_session: _APISession, response: _APIResponse) -> _APIResponse: """ Get the next page when previous response does not include all the items @@ -34,6 +35,7 @@ def get_next(mist_session: _APISession, response: _APIResponse) -> _APIResponse: else: return None + def get_all(mist_session: _APISession, response: _APIResponse) -> list: """ Retrieve and return all the items after a first request diff --git a/src/mistapi/cli.py b/src/mistapi/cli.py index 02f83169..ccd40e11 100644 --- a/src/mistapi/cli.py +++ b/src/mistapi/cli.py @@ -36,24 +36,28 @@ def _test_choice(val, val_max): return val_int else: return -1 - except: + except ValueError: return -2 + ########################################### #### DECORATOR def is_authenticated(func): """ decorator to test if the mistapi.APISession is authenticated """ + def wrapper(*args, **kwargs): mist_session = args[0] if mist_session.get_authentication_status(): return func(*args, **kwargs) else: console.critical("Not authenticated... Exiting...") - console.critical("Please une the \"login()\" function first...") + console.critical('Please une the "login()" function first...') + return wrapper + ########################################### #### CLI SELECTIONS def _forge_privileges(mist_session: mistapi.APISession, msp_id: str): @@ -79,6 +83,7 @@ def _forge_privileges(mist_session: mistapi.APISession, msp_id: str): custom_privileges.append(mist_session.get_privilege_by_org_id(org["id"])) return custom_privileges + @is_authenticated def _select_msp(mist_session: mistapi.APISession) -> list: """ @@ -96,7 +101,9 @@ def _select_msp(mist_session: mistapi.APISession) -> list: list List of ORG privileges """ - msp_accounts = [ priv for priv in mist_session.privileges if priv.get("scope") == "msp" ] + msp_accounts = [ + priv for priv in mist_session.privileges if priv.get("scope") == "msp" + ] if len(msp_accounts) == 0: return mist_session.privileges else: @@ -118,7 +125,11 @@ def _select_msp(mist_session: mistapi.APISession) -> list: elif resp.lower() == "n": standalones = [] for priv in mist_session.privileges: - msp = [msp for msp in msp_accounts if msp.get("msp_id") == priv.get("msp_id", "xyz")] + msp = [ + msp + for msp in msp_accounts + if msp.get("msp_id") == priv.get("msp_id", "xyz") + ] if not msp: standalones.append(priv) return standalones @@ -126,12 +137,15 @@ def _select_msp(mist_session: mistapi.APISession) -> list: else: tested_val = _test_choice(resp, i) if tested_val >= 0: - return _forge_privileges(mist_session, msp_accounts[tested_val]["msp_id"]) + return _forge_privileges( + mist_session, msp_accounts[tested_val]["msp_id"] + ) elif tested_val == -1: print(f"{resp} is not part of the possibilities.") elif tested_val == -2: print("Only numbers are allowed.") + @is_authenticated def select_org(mist_session: mistapi.APISession, allow_many=False) -> list: """ @@ -160,14 +174,14 @@ def select_org(mist_session: mistapi.APISession, allow_many=False) -> list: resp_ids = [] print("\r\nAvailable organizations:") for privilege in data: - if privilege["scope"] == "org" and not privilege["org_id"] in org_ids: + if privilege["scope"] == "org" and privilege["org_id"] not in org_ids: i += 1 org_ids.append(privilege["org_id"]) print(f"{i}) {privilege['name']} (id: {privilege['org_id']})") orgs_with_sites = [] for privilege in data: - if privilege["scope"] == "site" and not privilege["org_id"] in org_ids: + if privilege["scope"] == "site" and privilege["org_id"] not in org_ids: index = _search_org(orgs_with_sites, privilege["org_id"]) if index is None: i += 1 @@ -178,7 +192,10 @@ def select_org(mist_session: mistapi.APISession, allow_many=False) -> list: "org_id": privilege["org_id"], "name": privilege["name"], "sites": [ - {"site_id": privilege["site_id"], "name": privilege["name"]} + { + "site_id": privilege["site_id"], + "name": privilege["name"], + } ], } ) @@ -219,6 +236,7 @@ def select_org(mist_session: mistapi.APISession, allow_many=False) -> list: if selection_validated: return resp_ids + @is_authenticated def select_site( mist_session: mistapi.APISession, org_id=None, allow_many=False @@ -297,7 +315,7 @@ def select_site( ########################################### #### DATA PROCESSING / DISPLAY -def extract_field(json_data:dict, field:str) -> any: +def extract_field(json_data: dict, field: str) -> any: """ function to extract the value of a key from complex JSON object @@ -306,7 +324,7 @@ def extract_field(json_data:dict, field:str) -> any: json_data : dict the JSON object containing the value field : str - the full path of the key we are looking for. + the full path of the key we are looking for. e.g. parent.child.key RETURNS @@ -367,14 +385,14 @@ def _json_to_array(json_data: object, fields: list) -> list: def display_list_of_json_as_table(data_list: list, fields: list) -> None: """ - Function using tabulate to display a list as a table + Function using tabulate to display a list as a table PARAMS ----------- data_list : list list to display fields : list - List of fields to display. + List of fields to display. """ table = [] for data in data_list: @@ -382,16 +400,16 @@ def display_list_of_json_as_table(data_list: list, fields: list) -> None: print(tabulate(table, headers=fields)) -def pretty_print(response:_APIResponse, fields:list=None): +def pretty_print(response: _APIResponse, fields: list = None) -> None: """ - Function using tabulate to display a mistapi Response content as a table + Function using tabulate to display a mistapi Response content as a table PARAMS ----------- response : _APIResponse Response from a mistapi Request to the Mist Cloud fields : list - List of fields to display. + List of fields to display. If None, the function automatically detects all the available fields """ if "result" in response: diff --git a/uv.lock b/uv.lock index 8f3b10e2..8106ce0f 100644 --- a/uv.lock +++ b/uv.lock @@ -249,39 +249,39 @@ toml = [ [[package]] name = "cryptography" -version = "45.0.3" +version = "45.0.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/13/1f/9fa001e74a1993a9cadd2333bb889e50c66327b8594ac538ab8a04f915b7/cryptography-45.0.3.tar.gz", hash = "sha256:ec21313dd335c51d7877baf2972569f40a4291b76a0ce51391523ae358d05899", size = 744738, upload-time = "2025-05-25T14:17:24.777Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/3d/ac361649a0bfffc105e2298b720d8b862330a767dab27c06adc2ddbef96a/cryptography-45.0.3-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d377dde61c5d67eb4311eace661c3efda46c62113ff56bf05e2d679e02aebb5b", size = 4205541, upload-time = "2025-05-25T14:16:14.333Z" }, - { url = "https://files.pythonhosted.org/packages/70/3e/c02a043750494d5c445f769e9c9f67e550d65060e0bfce52d91c1362693d/cryptography-45.0.3-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fae1e637f527750811588e4582988932c222f8251f7b7ea93739acb624e1487f", size = 4433275, upload-time = "2025-05-25T14:16:16.421Z" }, - { url = "https://files.pythonhosted.org/packages/40/7a/9af0bfd48784e80eef3eb6fd6fde96fe706b4fc156751ce1b2b965dada70/cryptography-45.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ca932e11218bcc9ef812aa497cdf669484870ecbcf2d99b765d6c27a86000942", size = 4209173, upload-time = "2025-05-25T14:16:18.163Z" }, - { url = "https://files.pythonhosted.org/packages/31/5f/d6f8753c8708912df52e67969e80ef70b8e8897306cd9eb8b98201f8c184/cryptography-45.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af3f92b1dc25621f5fad065288a44ac790c5798e986a34d393ab27d2b27fcff9", size = 3898150, upload-time = "2025-05-25T14:16:20.34Z" }, - { url = "https://files.pythonhosted.org/packages/8b/50/f256ab79c671fb066e47336706dc398c3b1e125f952e07d54ce82cf4011a/cryptography-45.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2f8f8f0b73b885ddd7f3d8c2b2234a7d3ba49002b0223f58cfde1bedd9563c56", size = 4466473, upload-time = "2025-05-25T14:16:22.605Z" }, - { url = "https://files.pythonhosted.org/packages/62/e7/312428336bb2df0848d0768ab5a062e11a32d18139447a76dfc19ada8eed/cryptography-45.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9cc80ce69032ffa528b5e16d217fa4d8d4bb7d6ba8659c1b4d74a1b0f4235fca", size = 4211890, upload-time = "2025-05-25T14:16:24.738Z" }, - { url = "https://files.pythonhosted.org/packages/e7/53/8a130e22c1e432b3c14896ec5eb7ac01fb53c6737e1d705df7e0efb647c6/cryptography-45.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c824c9281cb628015bfc3c59335163d4ca0540d49de4582d6c2637312907e4b1", size = 4466300, upload-time = "2025-05-25T14:16:26.768Z" }, - { url = "https://files.pythonhosted.org/packages/ba/75/6bb6579688ef805fd16a053005fce93944cdade465fc92ef32bbc5c40681/cryptography-45.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5833bb4355cb377ebd880457663a972cd044e7f49585aee39245c0d592904578", size = 4332483, upload-time = "2025-05-25T14:16:28.316Z" }, - { url = "https://files.pythonhosted.org/packages/2f/11/2538f4e1ce05c6c4f81f43c1ef2bd6de7ae5e24ee284460ff6c77e42ca77/cryptography-45.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9bb5bf55dcb69f7067d80354d0a348368da907345a2c448b0babc4215ccd3497", size = 4573714, upload-time = "2025-05-25T14:16:30.474Z" }, - { url = "https://files.pythonhosted.org/packages/46/c7/c7d05d0e133a09fc677b8a87953815c522697bdf025e5cac13ba419e7240/cryptography-45.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5edcb90da1843df85292ef3a313513766a78fbbb83f584a5a58fb001a5a9d57", size = 4196181, upload-time = "2025-05-25T14:16:37.934Z" }, - { url = "https://files.pythonhosted.org/packages/08/7a/6ad3aa796b18a683657cef930a986fac0045417e2dc428fd336cfc45ba52/cryptography-45.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38deed72285c7ed699864f964a3f4cf11ab3fb38e8d39cfcd96710cd2b5bb716", size = 4423370, upload-time = "2025-05-25T14:16:39.502Z" }, - { url = "https://files.pythonhosted.org/packages/4f/58/ec1461bfcb393525f597ac6a10a63938d18775b7803324072974b41a926b/cryptography-45.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5555365a50efe1f486eed6ac7062c33b97ccef409f5970a0b6f205a7cfab59c8", size = 4197839, upload-time = "2025-05-25T14:16:41.322Z" }, - { url = "https://files.pythonhosted.org/packages/d4/3d/5185b117c32ad4f40846f579369a80e710d6146c2baa8ce09d01612750db/cryptography-45.0.3-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9e4253ed8f5948a3589b3caee7ad9a5bf218ffd16869c516535325fece163dcc", size = 3886324, upload-time = "2025-05-25T14:16:43.041Z" }, - { url = "https://files.pythonhosted.org/packages/67/85/caba91a57d291a2ad46e74016d1f83ac294f08128b26e2a81e9b4f2d2555/cryptography-45.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cfd84777b4b6684955ce86156cfb5e08d75e80dc2585e10d69e47f014f0a5342", size = 4450447, upload-time = "2025-05-25T14:16:44.759Z" }, - { url = "https://files.pythonhosted.org/packages/ae/d1/164e3c9d559133a38279215c712b8ba38e77735d3412f37711b9f8f6f7e0/cryptography-45.0.3-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:a2b56de3417fd5f48773ad8e91abaa700b678dc7fe1e0c757e1ae340779acf7b", size = 4200576, upload-time = "2025-05-25T14:16:46.438Z" }, - { url = "https://files.pythonhosted.org/packages/71/7a/e002d5ce624ed46dfc32abe1deff32190f3ac47ede911789ee936f5a4255/cryptography-45.0.3-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:57a6500d459e8035e813bd8b51b671977fb149a8c95ed814989da682314d0782", size = 4450308, upload-time = "2025-05-25T14:16:48.228Z" }, - { url = "https://files.pythonhosted.org/packages/87/ad/3fbff9c28cf09b0a71e98af57d74f3662dea4a174b12acc493de00ea3f28/cryptography-45.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f22af3c78abfbc7cbcdf2c55d23c3e022e1a462ee2481011d518c7fb9c9f3d65", size = 4325125, upload-time = "2025-05-25T14:16:49.844Z" }, - { url = "https://files.pythonhosted.org/packages/f5/b4/51417d0cc01802304c1984d76e9592f15e4801abd44ef7ba657060520bf0/cryptography-45.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:232954730c362638544758a8160c4ee1b832dc011d2c41a306ad8f7cccc5bb0b", size = 4560038, upload-time = "2025-05-25T14:16:51.398Z" }, - { url = "https://files.pythonhosted.org/packages/45/0b/87556d3337f5e93c37fda0a0b5d3e7b4f23670777ce8820fce7962a7ed22/cryptography-45.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:fed5aaca1750e46db870874c9c273cd5182a9e9deb16f06f7bdffdb5c2bde4b9", size = 4142867, upload-time = "2025-05-25T14:16:58.459Z" }, - { url = "https://files.pythonhosted.org/packages/72/ba/21356dd0bcb922b820211336e735989fe2cf0d8eaac206335a0906a5a38c/cryptography-45.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:00094838ecc7c6594171e8c8a9166124c1197b074cfca23645cee573910d76bc", size = 4385000, upload-time = "2025-05-25T14:17:00.656Z" }, - { url = "https://files.pythonhosted.org/packages/2f/2b/71c78d18b804c317b66283be55e20329de5cd7e1aec28e4c5fbbe21fd046/cryptography-45.0.3-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:92d5f428c1a0439b2040435a1d6bc1b26ebf0af88b093c3628913dd464d13fa1", size = 4144195, upload-time = "2025-05-25T14:17:02.782Z" }, - { url = "https://files.pythonhosted.org/packages/55/3e/9f9b468ea779b4dbfef6af224804abd93fbcb2c48605d7443b44aea77979/cryptography-45.0.3-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:ec64ee375b5aaa354b2b273c921144a660a511f9df8785e6d1c942967106438e", size = 4384540, upload-time = "2025-05-25T14:17:04.49Z" }, - { url = "https://files.pythonhosted.org/packages/96/61/751ebea58c87b5be533c429f01996050a72c7283b59eee250275746632ea/cryptography-45.0.3-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:555e5e2d3a53b4fabeca32835878b2818b3f23966a4efb0d566689777c5a12c8", size = 4146964, upload-time = "2025-05-25T14:17:09.538Z" }, - { url = "https://files.pythonhosted.org/packages/8d/01/28c90601b199964de383da0b740b5156f5d71a1da25e7194fdf793d373ef/cryptography-45.0.3-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:25286aacb947286620a31f78f2ed1a32cded7be5d8b729ba3fb2c988457639e4", size = 4388103, upload-time = "2025-05-25T14:17:11.978Z" }, - { url = "https://files.pythonhosted.org/packages/3d/ec/cd892180b9e42897446ef35c62442f5b8b039c3d63a05f618aa87ec9ebb5/cryptography-45.0.3-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:050ce5209d5072472971e6efbfc8ec5a8f9a841de5a4db0ebd9c2e392cb81972", size = 4150031, upload-time = "2025-05-25T14:17:14.131Z" }, - { url = "https://files.pythonhosted.org/packages/db/d4/22628c2dedd99289960a682439c6d3aa248dff5215123ead94ac2d82f3f5/cryptography-45.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:dc10ec1e9f21f33420cc05214989544727e776286c1c16697178978327b95c9c", size = 4387389, upload-time = "2025-05-25T14:17:17.303Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/fe/c8/a2a376a8711c1e11708b9c9972e0c3223f5fc682552c82d8db844393d6ce/cryptography-45.0.4.tar.gz", hash = "sha256:7405ade85c83c37682c8fe65554759800a4a8c54b2d96e0f8ad114d31b808d57", size = 744890, upload-time = "2025-06-10T00:03:51.297Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/14/93b69f2af9ba832ad6618a03f8a034a5851dc9a3314336a3d71c252467e1/cryptography-45.0.4-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:680806cf63baa0039b920f4976f5f31b10e772de42f16310a6839d9f21a26b0d", size = 4205335, upload-time = "2025-06-10T00:02:41.64Z" }, + { url = "https://files.pythonhosted.org/packages/67/30/fae1000228634bf0b647fca80403db5ca9e3933b91dd060570689f0bd0f7/cryptography-45.0.4-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4ca0f52170e821bc8da6fc0cc565b7bb8ff8d90d36b5e9fdd68e8a86bdf72036", size = 4431487, upload-time = "2025-06-10T00:02:43.696Z" }, + { url = "https://files.pythonhosted.org/packages/6d/5a/7dffcf8cdf0cb3c2430de7404b327e3db64735747d641fc492539978caeb/cryptography-45.0.4-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f3fe7a5ae34d5a414957cc7f457e2b92076e72938423ac64d215722f6cf49a9e", size = 4208922, upload-time = "2025-06-10T00:02:45.334Z" }, + { url = "https://files.pythonhosted.org/packages/c6/f3/528729726eb6c3060fa3637253430547fbaaea95ab0535ea41baa4a6fbd8/cryptography-45.0.4-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:25eb4d4d3e54595dc8adebc6bbd5623588991d86591a78c2548ffb64797341e2", size = 3900433, upload-time = "2025-06-10T00:02:47.359Z" }, + { url = "https://files.pythonhosted.org/packages/d9/4a/67ba2e40f619e04d83c32f7e1d484c1538c0800a17c56a22ff07d092ccc1/cryptography-45.0.4-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:ce1678a2ccbe696cf3af15a75bb72ee008d7ff183c9228592ede9db467e64f1b", size = 4464163, upload-time = "2025-06-10T00:02:49.412Z" }, + { url = "https://files.pythonhosted.org/packages/7e/9a/b4d5aa83661483ac372464809c4b49b5022dbfe36b12fe9e323ca8512420/cryptography-45.0.4-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:49fe9155ab32721b9122975e168a6760d8ce4cffe423bcd7ca269ba41b5dfac1", size = 4208687, upload-time = "2025-06-10T00:02:50.976Z" }, + { url = "https://files.pythonhosted.org/packages/db/b7/a84bdcd19d9c02ec5807f2ec2d1456fd8451592c5ee353816c09250e3561/cryptography-45.0.4-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2882338b2a6e0bd337052e8b9007ced85c637da19ef9ecaf437744495c8c2999", size = 4463623, upload-time = "2025-06-10T00:02:52.542Z" }, + { url = "https://files.pythonhosted.org/packages/d8/84/69707d502d4d905021cac3fb59a316344e9f078b1da7fb43ecde5e10840a/cryptography-45.0.4-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:23b9c3ea30c3ed4db59e7b9619272e94891f8a3a5591d0b656a7582631ccf750", size = 4332447, upload-time = "2025-06-10T00:02:54.63Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ee/d4f2ab688e057e90ded24384e34838086a9b09963389a5ba6854b5876598/cryptography-45.0.4-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0a97c927497e3bc36b33987abb99bf17a9a175a19af38a892dc4bbb844d7ee2", size = 4572830, upload-time = "2025-06-10T00:02:56.689Z" }, + { url = "https://files.pythonhosted.org/packages/fe/51/8c584ed426093aac257462ae62d26ad61ef1cbf5b58d8b67e6e13c39960e/cryptography-45.0.4-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6a5bf57554e80f75a7db3d4b1dacaa2764611ae166ab42ea9a72bcdb5d577637", size = 4195746, upload-time = "2025-06-10T00:03:03.94Z" }, + { url = "https://files.pythonhosted.org/packages/5c/7d/4b0ca4d7af95a704eef2f8f80a8199ed236aaf185d55385ae1d1610c03c2/cryptography-45.0.4-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:46cf7088bf91bdc9b26f9c55636492c1cce3e7aaf8041bbf0243f5e5325cfb2d", size = 4424456, upload-time = "2025-06-10T00:03:05.589Z" }, + { url = "https://files.pythonhosted.org/packages/1d/45/5fabacbc6e76ff056f84d9f60eeac18819badf0cefc1b6612ee03d4ab678/cryptography-45.0.4-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7bedbe4cc930fa4b100fc845ea1ea5788fcd7ae9562e669989c11618ae8d76ee", size = 4198495, upload-time = "2025-06-10T00:03:09.172Z" }, + { url = "https://files.pythonhosted.org/packages/55/b7/ffc9945b290eb0a5d4dab9b7636706e3b5b92f14ee5d9d4449409d010d54/cryptography-45.0.4-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:eaa3e28ea2235b33220b949c5a0d6cf79baa80eab2eb5607ca8ab7525331b9ff", size = 3885540, upload-time = "2025-06-10T00:03:10.835Z" }, + { url = "https://files.pythonhosted.org/packages/7f/e3/57b010282346980475e77d414080acdcb3dab9a0be63071efc2041a2c6bd/cryptography-45.0.4-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:7ef2dde4fa9408475038fc9aadfc1fb2676b174e68356359632e980c661ec8f6", size = 4452052, upload-time = "2025-06-10T00:03:12.448Z" }, + { url = "https://files.pythonhosted.org/packages/37/e6/ddc4ac2558bf2ef517a358df26f45bc774a99bf4653e7ee34b5e749c03e3/cryptography-45.0.4-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:6a3511ae33f09094185d111160fd192c67aa0a2a8d19b54d36e4c78f651dc5ad", size = 4198024, upload-time = "2025-06-10T00:03:13.976Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c0/85fa358ddb063ec588aed4a6ea1df57dc3e3bc1712d87c8fa162d02a65fc/cryptography-45.0.4-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:06509dc70dd71fa56eaa138336244e2fbaf2ac164fc9b5e66828fccfd2b680d6", size = 4451442, upload-time = "2025-06-10T00:03:16.248Z" }, + { url = "https://files.pythonhosted.org/packages/33/67/362d6ec1492596e73da24e669a7fbbaeb1c428d6bf49a29f7a12acffd5dc/cryptography-45.0.4-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5f31e6b0a5a253f6aa49be67279be4a7e5a4ef259a9f33c69f7d1b1191939872", size = 4325038, upload-time = "2025-06-10T00:03:18.4Z" }, + { url = "https://files.pythonhosted.org/packages/53/75/82a14bf047a96a1b13ebb47fb9811c4f73096cfa2e2b17c86879687f9027/cryptography-45.0.4-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:944e9ccf67a9594137f942d5b52c8d238b1b4e46c7a0c2891b7ae6e01e7c80a4", size = 4560964, upload-time = "2025-06-10T00:03:20.06Z" }, + { url = "https://files.pythonhosted.org/packages/c4/b9/357f18064ec09d4807800d05a48f92f3b369056a12f995ff79549fbb31f1/cryptography-45.0.4-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7aad98a25ed8ac917fdd8a9c1e706e5a0956e06c498be1f713b61734333a4507", size = 4143732, upload-time = "2025-06-10T00:03:27.896Z" }, + { url = "https://files.pythonhosted.org/packages/c4/9c/7f7263b03d5db329093617648b9bd55c953de0b245e64e866e560f9aac07/cryptography-45.0.4-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3530382a43a0e524bc931f187fc69ef4c42828cf7d7f592f7f249f602b5a4ab0", size = 4385424, upload-time = "2025-06-10T00:03:29.992Z" }, + { url = "https://files.pythonhosted.org/packages/a6/5a/6aa9d8d5073d5acc0e04e95b2860ef2684b2bd2899d8795fc443013e263b/cryptography-45.0.4-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:6b613164cb8425e2f8db5849ffb84892e523bf6d26deb8f9bb76ae86181fa12b", size = 4142438, upload-time = "2025-06-10T00:03:31.782Z" }, + { url = "https://files.pythonhosted.org/packages/42/1c/71c638420f2cdd96d9c2b287fec515faf48679b33a2b583d0f1eda3a3375/cryptography-45.0.4-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:96d4819e25bf3b685199b304a0029ce4a3caf98947ce8a066c9137cc78ad2c58", size = 4384622, upload-time = "2025-06-10T00:03:33.491Z" }, + { url = "https://files.pythonhosted.org/packages/28/9a/a7d5bb87d149eb99a5abdc69a41e4e47b8001d767e5f403f78bfaafc7aa7/cryptography-45.0.4-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:03dbff8411206713185b8cebe31bc5c0eb544799a50c09035733716b386e61a4", size = 4146899, upload-time = "2025-06-10T00:03:38.659Z" }, + { url = "https://files.pythonhosted.org/packages/17/11/9361c2c71c42cc5c465cf294c8030e72fb0c87752bacbd7a3675245e3db3/cryptography-45.0.4-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:51dfbd4d26172d31150d84c19bbe06c68ea4b7f11bbc7b3a5e146b367c311349", size = 4388900, upload-time = "2025-06-10T00:03:40.233Z" }, + { url = "https://files.pythonhosted.org/packages/c0/76/f95b83359012ee0e670da3e41c164a0c256aeedd81886f878911581d852f/cryptography-45.0.4-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:0339a692de47084969500ee455e42c58e449461e0ec845a34a6a9b9bf7df7fb8", size = 4146422, upload-time = "2025-06-10T00:03:41.827Z" }, + { url = "https://files.pythonhosted.org/packages/09/ad/5429fcc4def93e577a5407988f89cf15305e64920203d4ac14601a9dc876/cryptography-45.0.4-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:0cf13c77d710131d33e63626bd55ae7c0efb701ebdc2b3a7952b9b23a0412862", size = 4388475, upload-time = "2025-06-10T00:03:43.493Z" }, ] [[package]] @@ -715,7 +715,7 @@ wheels = [ [[package]] name = "requests" -version = "2.32.3" +version = "2.32.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -723,9 +723,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" }, + { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, ] [[package]] @@ -913,9 +913,9 @@ wheels = [ [[package]] name = "zipp" -version = "3.22.0" +version = "3.23.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/12/b6/7b3d16792fdf94f146bed92be90b4eb4563569eca91513c8609aebf0c167/zipp-3.22.0.tar.gz", hash = "sha256:dd2f28c3ce4bc67507bfd3781d21b7bb2be31103b51a4553ad7d90b84e57ace5", size = 25257, upload-time = "2025-05-26T14:46:32.217Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ad/da/f64669af4cae46f17b90798a827519ce3737d31dbafad65d391e49643dc4/zipp-3.22.0-py3-none-any.whl", hash = "sha256:fe208f65f2aca48b81f9e6fd8cf7b8b32c26375266b009b413d45306b6148343", size = 9796, upload-time = "2025-05-26T14:46:30.775Z" }, + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, ]