From 780dd5a62491d27963eb258c8409aed76c56c559 Mon Sep 17 00:00:00 2001 From: sonny Date: Sun, 5 Jan 2025 17:08:28 +0100 Subject: [PATCH 1/2] Replace `dig` usage --- transip_client/cli.py | 18 +- transip_client/main.py | 50 +++-- transip_client/tests/tests.py | 385 ++++++++++++++++++---------------- 3 files changed, 246 insertions(+), 207 deletions(-) diff --git a/transip_client/cli.py b/transip_client/cli.py index 2aa1c1b..861f4bc 100644 --- a/transip_client/cli.py +++ b/transip_client/cli.py @@ -3,8 +3,7 @@ import click from transip_client.main import detect -DEFAULT_DNS = "myip.opendns.com" -DEFAULT_DNS_NAME = "@resolver1.opendns.com" +DEFAULT_SERVICE = "https://api.ipify.org" DEFAULT_API_URL = "https://api.transip.nl/v6" @@ -13,11 +12,18 @@ DEFAULT_API_URL = "https://api.transip.nl/v6" @click.option("--token", envvar="TOKEN") @click.option("--login", envvar="LOGIN") @click.option("--private-key-path", envvar="PRIVATE_KEY_PATH") -@click.option("--dns", envvar="DNS", default=DEFAULT_DNS) -@click.option("--dns-name", envvar="DNS_NAME", default=DEFAULT_DNS_NAME) +@click.option("--service", envvar="SERVICE", default=DEFAULT_SERVICE) @click.option("--api-url", envvar="API_URL", default=DEFAULT_API_URL) @click.option("--read-only/--write", envvar="READ_ONLY", default=False) -def run(domains, token, login, private_key_path, dns, dns_name, api_url, read_only): +def run( + domains: list[str], + token: str, + login: str, + private_key_path: str, + service: str, + api_url: str, + read_only: bool, +) -> None: if not domains: raise ValueError("No domain(s) specified") @@ -40,7 +46,7 @@ def run(domains, token, login, private_key_path, dns, dns_name, api_url, read_on detect( domains, - (dns, dns_name), + service, (private_key_path, login), token, api_url, diff --git a/transip_client/main.py b/transip_client/main.py index e4f8d1c..bb85a45 100644 --- a/transip_client/main.py +++ b/transip_client/main.py @@ -1,10 +1,10 @@ import base64 import json import logging -import subprocess import time from concurrent.futures import ThreadPoolExecutor, as_completed +from typing import Generator import requests @@ -17,19 +17,17 @@ from cryptography.hazmat.primitives.hashes import SHA512 logger = logging.getLogger(__name__) -def _get_ip(resolvers): +def _get_ip(service: str) -> str: try: - output = subprocess.check_output( - ["dig", "+short", *resolvers], - stderr=subprocess.STDOUT, - ) - except subprocess.CalledProcessError as e: - raise OSError("Unable to retrieve current IP") from e + response = requests.get(service, timeout=10) + response.raise_for_status() + except requests.RequestException as e: + raise OSError(f"Unable to retrieve current IP from {service}") from e - return output.decode("utf-8").strip() + return response.text -def _get_token(private_key_path, login, api_url): +def _get_token(private_key_path: str, login: str, api_url: str) -> str: request = requests.Request( "POST", f"{api_url}/auth", @@ -66,13 +64,14 @@ def _get_token(private_key_path, login, api_url): return response_data["token"] -def _get_domain(domain, token, api_url): +def _get_domain(domain: str, token: str, api_url: str) -> requests.Response: headers = {"Authorization": f"Bearer {token}"} - return requests.get(f"{api_url}/domains/{domain}/dns", headers=headers) -def _get_domain_data(domains, token, api_url): +def _get_domain_data( + domains: list[str], token: str, api_url: str +) -> Generator[dict, None, None]: with ThreadPoolExecutor(max_workers=10) as executor: futures = { executor.submit(_get_domain, domain, token, api_url): domain @@ -80,19 +79,21 @@ def _get_domain_data(domains, token, api_url): } for future in as_completed(futures): - response = future.result() domain = futures[future] try: + response = future.result() response.raise_for_status() - except requests.HTTPError as e: + except requests.HTTPError: logger.exception(f"Failed retrieving information for {domain}") continue yield {"domain": domain, **response.json()} -def _update_domain(domain, payload, api_url, token): +def _update_domain( + domain: str, payload: dict, api_url: str, token: str +) -> requests.Response: headers = {"Authorization": f"Bearer {token}"} return requests.put( @@ -100,7 +101,9 @@ def _update_domain(domain, payload, api_url, token): ) -def _update_domains(updated_domains, api_url, token, read_only): +def _update_domains( + updated_domains: dict, api_url: str, token: str, read_only: bool +) -> None: if read_only: return @@ -123,14 +126,21 @@ def _update_domains(updated_domains, api_url, token, read_only): logger.info(f"Updated domain {domain}") -def detect(domains, resolvers, credentials, token, api_url, read_only): - ip = _get_ip(resolvers) - updated_domains = {} +def detect( + domains: list[str], + service: str, + credentials: tuple[str, str], + token: str, + api_url: str, + read_only: bool, +) -> None: + ip = _get_ip(service) if all(credentials): token = _get_token(*credentials, api_url) domain_data = _get_domain_data(domains, token, api_url) + updated_domains = {} for data in domain_data: dns_entries = data["dnsEntries"] diff --git a/transip_client/tests/tests.py b/transip_client/tests/tests.py index 604e471..fc911d8 100644 --- a/transip_client/tests/tests.py +++ b/transip_client/tests/tests.py @@ -1,21 +1,18 @@ import json import os -from unittest import TestCase -from unittest.mock import call, patch +from unittest import TestCase, skip +from unittest.mock import call, patch, Mock from pathlib import Path from click.testing import CliRunner from requests import HTTPError -from transip_client.cli import DEFAULT_API_URL, run +from transip_client.cli import DEFAULT_API_URL, DEFAULT_SERVICE, run class RunTestCase(TestCase): def setUp(self): - patcher = patch("transip_client.main.subprocess.check_output") - self.mocked_dns = patcher.start() - patcher = patch("transip_client.main.requests.get") self.mocked_get = patcher.start() @@ -28,30 +25,36 @@ class RunTestCase(TestCase): self.runner = CliRunner() def test_simple(self): - self.mocked_dns.return_value = b"111.420\n" - self.mocked_get.return_value.json.return_value = { - "dnsEntries": [ - { - "name": "@", - "expire": 60, - "type": "A", - "content": "111.421", + self.mocked_get.side_effect = [ + Mock(text="111.420"), + Mock( + json=lambda: { + "dnsEntries": [ + { + "name": "@", + "expire": 60, + "type": "A", + "content": "111.421", + } + ], + "_links": [ + { + "rel": "self", + "link": "https://api.transip.nl/v6/domains/foobar.com/dns", + }, + { + "rel": "domain", + "link": "https://api.transip.nl/v6/domains/foobar.com", + }, + ], } - ], - "_links": [ - { - "rel": "self", - "link": "https://api.transip.nl/v6/domains/foobar.com/dns", - }, - { - "rel": "domain", - "link": "https://api.transip.nl/v6/domains/foobar.com", - }, - ], - } + ), + ] with self.assertLogs("transip_client.main", level="INFO") as logger: - result = self.runner.invoke(run, ["foobar.com"], env={"TOKEN": "token"}) + result = self.runner.invoke( + run, "foobar.com", env={"TOKEN": "token"}, catch_exceptions=False + ) self.assertEqual( logger.output, ["INFO:transip_client.main:Updated domain foobar.com"] @@ -84,11 +87,12 @@ class RunTestCase(TestCase): ) def test_error_response(self): - self.mocked_dns.return_value = b"111.420\n" - self.mocked_get.return_value.raise_for_status.side_effect = HTTPError + self.mocked_get.side_effect = [Mock(text="111.420"), HTTPError] with self.assertLogs("transip_client.main", level="INFO") as logger: - result = self.runner.invoke(run, ["foobar.com"], env={"TOKEN": "token"}) + result = self.runner.invoke( + run, ["foobar.com"], env={"TOKEN": "token"}, catch_exceptions=False + ) error_log = logger.output[0] @@ -103,27 +107,31 @@ class RunTestCase(TestCase): self.mocked_put.assert_not_called() def test_matching_ip(self): - self.mocked_dns.return_value = b"111.420\n" - self.mocked_get.return_value.json.return_value = { - "dnsEntries": [ - { - "name": "@", - "expire": 60, - "type": "A", - "content": "111.420", + self.mocked_get.side_effect = [ + Mock(text="111.420"), + Mock( + json=lambda: { + "dnsEntries": [ + { + "name": "@", + "expire": 60, + "type": "A", + "content": "111.420", + } + ], + "_links": [ + { + "rel": "self", + "link": "https://api.transip.nl/v6/domains/foobar.com/dns", + }, + { + "rel": "domain", + "link": "https://api.transip.nl/v6/domains/foobar.com", + }, + ], } - ], - "_links": [ - { - "rel": "self", - "link": "https://api.transip.nl/v6/domains/foobar.com/dns", - }, - { - "rel": "domain", - "link": "https://api.transip.nl/v6/domains/foobar.com", - }, - ], - } + ), + ] result = self.runner.invoke(run, ["foobar.com"], env={"TOKEN": "token"}) @@ -137,27 +145,31 @@ class RunTestCase(TestCase): self.mocked_put.assert_not_called() def test_readonly(self): - self.mocked_dns.return_value = b"111.420\n" - self.mocked_get.return_value.json.return_value = { - "dnsEntries": [ - { - "name": "@", - "expire": 60, - "type": "A", - "content": "111.421", + self.mocked_get.side_effect = [ + Mock(text="111.420"), + Mock( + json=lambda: { + "dnsEntries": [ + { + "name": "@", + "expire": 60, + "type": "A", + "content": "111.421", + } + ], + "_links": [ + { + "rel": "self", + "link": "https://api.transip.nl/v6/domains/foobar.com/dns", + }, + { + "rel": "domain", + "link": "https://api.transip.nl/v6/domains/foobar.com", + }, + ], } - ], - "_links": [ - { - "rel": "self", - "link": "https://api.transip.nl/v6/domains/foobar.com/dns", - }, - { - "rel": "domain", - "link": "https://api.transip.nl/v6/domains/foobar.com", - }, - ], - } + ), + ] result = self.runner.invoke( run, ["foobar.com", "--read-only"], env={"TOKEN": "token"} @@ -173,27 +185,31 @@ class RunTestCase(TestCase): self.mocked_put.assert_not_called() def test_different_api_url(self): - self.mocked_dns.return_value = b"111.420\n" - self.mocked_get.return_value.json.return_value = { - "dnsEntries": [ - { - "name": "@", - "expire": 60, - "type": "A", - "content": "111.421", + self.mocked_get.side_effect = [ + Mock(text="111.420"), + Mock( + json=lambda: { + "dnsEntries": [ + { + "name": "@", + "expire": 60, + "type": "A", + "content": "111.421", + } + ], + "_links": [ + { + "rel": "self", + "link": "https://api.transip.nl/v6/domains/foobar.com/dns", + }, + { + "rel": "domain", + "link": "https://api.transip.nl/v6/domains/foobar.com", + }, + ], } - ], - "_links": [ - { - "rel": "self", - "link": "https://api.transip.nl/v6/domains/foobar.com/dns", - }, - { - "rel": "domain", - "link": "https://api.transip.nl/v6/domains/foobar.com", - }, - ], - } + ), + ] with self.assertLogs("transip_client.main", level="INFO") as logger: result = self.runner.invoke( @@ -233,27 +249,31 @@ class RunTestCase(TestCase): ) def test_env_var(self): - self.mocked_dns.return_value = b"111.420\n" - self.mocked_get.return_value.json.return_value = { - "dnsEntries": [ - { - "name": "@", - "expire": 60, - "type": "A", - "content": "111.421", + self.mocked_get.side_effect = [ + Mock(text="111.420"), + Mock( + json=lambda: { + "dnsEntries": [ + { + "name": "@", + "expire": 60, + "type": "A", + "content": "111.421", + } + ], + "_links": [ + { + "rel": "self", + "link": "https://api.transip.nl/v6/domains/foobar.com/dns", + }, + { + "rel": "domain", + "link": "https://api.transip.nl/v6/domains/foobar.com", + }, + ], } - ], - "_links": [ - { - "rel": "self", - "link": "https://api.transip.nl/v6/domains/foobar.com/dns", - }, - { - "rel": "domain", - "link": "https://api.transip.nl/v6/domains/foobar.com", - }, - ], - } + ), + ] with self.assertLogs("transip_client.main", level="INFO") as logger: result = self.runner.invoke( @@ -296,48 +316,52 @@ class RunTestCase(TestCase): ) def test_multi_arg_env_var(self): - self.mocked_dns.return_value = b"111.420\n" - self.mocked_get.return_value.json.side_effect = [ - { - "dnsEntries": [ - { - "name": "@", - "expire": 60, - "type": "A", - "content": "111.421", - } - ], - "_links": [ - { - "rel": "self", - "link": "https://api.transip.nl/v6/domains/foobar.com/dns", - }, - { - "rel": "domain", - "link": "https://api.transip.nl/v6/domains/foobar.com", - }, - ], - }, - { - "dnsEntries": [ - { - "name": "@", - "expire": 60, - "type": "A", - "content": "111.421", - } - ], - "_links": [ - { - "rel": "self", - "link": "https://api.transip.nl/v6/domains/foofoo.com/dns", - }, - { - "rel": "domain", - "link": "https://api.transip.nl/v6/domains/foofoo.com", - }, - ], - }, + self.mocked_get.side_effect = [ + Mock(text="111.420"), + Mock( + json=lambda: { + "dnsEntries": [ + { + "name": "@", + "expire": 60, + "type": "A", + "content": "111.421", + } + ], + "_links": [ + { + "rel": "self", + "link": "https://api.transip.nl/v6/domains/foobar.com/dns", + }, + { + "rel": "domain", + "link": "https://api.transip.nl/v6/domains/foobar.com", + }, + ], + }, + ), + Mock( + json=lambda: { + "dnsEntries": [ + { + "name": "@", + "expire": 60, + "type": "A", + "content": "111.421", + } + ], + "_links": [ + { + "rel": "self", + "link": "https://api.transip.nl/v6/domains/foofoo.com/dns", + }, + { + "rel": "domain", + "link": "https://api.transip.nl/v6/domains/foofoo.com", + }, + ], + } + ), ] with self.assertLogs("transip_client.main", level="INFO") as logger: @@ -357,18 +381,15 @@ class RunTestCase(TestCase): self.assertEqual(result.exit_code, 0) expected_calls = [ + call(DEFAULT_SERVICE, timeout=10), call( f"{DEFAULT_API_URL}/domains/foobar.com/dns", headers={"Authorization": "Bearer token"}, ), - call().raise_for_status(), - call().json(), call( f"{DEFAULT_API_URL}/domains/foofoo.com/dns", headers={"Authorization": "Bearer token"}, ), - call().raise_for_status(), - call().json(), ] # use any_order because of the asynchronous requests @@ -393,13 +414,11 @@ class RunTestCase(TestCase): data=expected_json, headers={"Authorization": "Bearer token"}, ), - call().raise_for_status(), call( f"{DEFAULT_API_URL}/domains/foofoo.com/dns", data=expected_json, headers={"Authorization": "Bearer token"}, ), - call().raise_for_status(), ] self.mocked_put.assert_has_calls(expected_calls, any_order=True) @@ -420,7 +439,7 @@ class RunTestCase(TestCase): self.assertEqual( str(result.exception), "Either a token or a login name with a path to a private key need" - " to be specified" + " to be specified", ) self.mocked_get.assert_not_called() @@ -432,34 +451,38 @@ class RunTestCase(TestCase): self.assertEqual(result.exit_code, 1) self.assertEqual( str(result.exception), - "Both a login name and the path to a private key need to be specified" + "Both a login name and the path to a private key need to be specified", ) self.mocked_get.assert_not_called() self.mocked_put.assert_not_called() def test_login_with_private_key_path(self): - self.mocked_dns.return_value = b"111.420\n" - self.mocked_get.return_value.json.return_value = { - "dnsEntries": [ - { - "name": "@", - "expire": 60, - "type": "A", - "content": "111.421", + self.mocked_get.side_effect = [ + Mock(text="111.420"), + Mock( + json=lambda: { + "dnsEntries": [ + { + "name": "@", + "expire": 60, + "type": "A", + "content": "111.421", + } + ], + "_links": [ + { + "rel": "self", + "link": "https://api.transip.nl/v6/domains/foobar.com/dns", + }, + { + "rel": "domain", + "link": "https://api.transip.nl/v6/domains/foobar.com", + }, + ], } - ], - "_links": [ - { - "rel": "self", - "link": "https://api.transip.nl/v6/domains/foobar.com/dns", - }, - { - "rel": "domain", - "link": "https://api.transip.nl/v6/domains/foobar.com", - }, - ], - } + ), + ] self.mocked_session.return_value.json.return_value = {"token": "FOOBAR"} @@ -471,7 +494,7 @@ class RunTestCase(TestCase): result = self.runner.invoke( run, ["foobar.com"], - env={"LOGIN": "foo", "PRIVATE_KEY_PATH": str(private_key_path)} + env={"LOGIN": "foo", "PRIVATE_KEY_PATH": str(private_key_path)}, ) self.assertEqual( From ee31311db76b5e638d4c28ba6f61897002e5979c Mon Sep 17 00:00:00 2001 From: sonny Date: Sun, 5 Jan 2025 17:11:44 +0100 Subject: [PATCH 2/2] Replace development tools --- pyproject.toml | 6 +-- uv.lock | 127 +++++++++++-------------------------------------- 2 files changed, 29 insertions(+), 104 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9096dda..533b693 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,14 +9,12 @@ dependencies = [ 'click>=8.0.1', 'python-dotenv>=0.15.0', 'requests>=2.25.1', - 'cryptography>=3.4.7' + 'cryptography>=3.4.7', ] [project.optional-dependencies] development = [ - 'black>=20.8b1', - 'isort>=5.6.4', - 'autoflake>=1.4', + "ruff>=0.8.6", ] ci = ['coverage>=5.3.1'] diff --git a/uv.lock b/uv.lock index ffd03f7..a431850 100644 --- a/uv.lock +++ b/uv.lock @@ -1,46 +1,6 @@ version = 1 requires-python = ">=3.11" -[[package]] -name = "autoflake" -version = "2.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyflakes" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2a/cb/486f912d6171bc5748c311a2984a301f4e2d054833a1da78485866c71522/autoflake-2.3.1.tar.gz", hash = "sha256:c98b75dc5b0a86459c4f01a1d32ac7eb4338ec4317a4469515ff1e687ecd909e", size = 27642 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/ee/3fd29bf416eb4f1c5579cf12bf393ae954099258abd7bde03c4f9716ef6b/autoflake-2.3.1-py3-none-any.whl", hash = "sha256:3ae7495db9084b7b32818b4140e6dc4fc280b712fb414f5b8fe57b0a8e85a840", size = 32483 }, -] - -[[package]] -name = "black" -version = "24.10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "mypy-extensions" }, - { name = "packaging" }, - { name = "pathspec" }, - { name = "platformdirs" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d8/0d/cc2fb42b8c50d80143221515dd7e4766995bd07c56c9a3ed30baf080b6dc/black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875", size = 645813 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/cc/7496bb63a9b06a954d3d0ac9fe7a73f3bf1cd92d7a58877c27f4ad1e9d41/black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad", size = 1607468 }, - { url = "https://files.pythonhosted.org/packages/2b/e3/69a738fb5ba18b5422f50b4f143544c664d7da40f09c13969b2fd52900e0/black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50", size = 1437270 }, - { url = "https://files.pythonhosted.org/packages/c9/9b/2db8045b45844665c720dcfe292fdaf2e49825810c0103e1191515fc101a/black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392", size = 1737061 }, - { url = "https://files.pythonhosted.org/packages/a3/95/17d4a09a5be5f8c65aa4a361444d95edc45def0de887810f508d3f65db7a/black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175", size = 1423293 }, - { url = "https://files.pythonhosted.org/packages/90/04/bf74c71f592bcd761610bbf67e23e6a3cff824780761f536512437f1e655/black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3", size = 1644256 }, - { url = "https://files.pythonhosted.org/packages/4c/ea/a77bab4cf1887f4b2e0bce5516ea0b3ff7d04ba96af21d65024629afedb6/black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65", size = 1448534 }, - { url = "https://files.pythonhosted.org/packages/4e/3e/443ef8bc1fbda78e61f79157f303893f3fddf19ca3c8989b163eb3469a12/black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f", size = 1761892 }, - { url = "https://files.pythonhosted.org/packages/52/93/eac95ff229049a6901bc84fec6908a5124b8a0b7c26ea766b3b8a5debd22/black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8", size = 1434796 }, - { url = "https://files.pythonhosted.org/packages/d0/a0/a993f58d4ecfba035e61fca4e9f64a2ecae838fc9f33ab798c62173ed75c/black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981", size = 1643986 }, - { url = "https://files.pythonhosted.org/packages/37/d5/602d0ef5dfcace3fb4f79c436762f130abd9ee8d950fa2abdbf8bbc555e0/black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b", size = 1448085 }, - { url = "https://files.pythonhosted.org/packages/47/6d/a3a239e938960df1a662b93d6230d4f3e9b4a22982d060fc38c42f45a56b/black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2", size = 1760928 }, - { url = "https://files.pythonhosted.org/packages/dd/cf/af018e13b0eddfb434df4d9cd1b2b7892bab119f7a20123e93f6910982e8/black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b", size = 1436875 }, - { url = "https://files.pythonhosted.org/packages/8d/a7/4b27c50537ebca8bec139b872861f9d2bf501c5ec51fcf897cb924d9e264/black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d", size = 206898 }, -] - [[package]] name = "certifi" version = "2024.12.14" @@ -252,51 +212,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, ] -[[package]] -name = "isort" -version = "5.13.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/87/f9/c1eb8635a24e87ade2efce21e3ce8cd6b8630bb685ddc9cdaca1349b2eb5/isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109", size = 175303 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/b3/8def84f539e7d2289a02f0524b944b15d7c75dab7628bedf1c4f0992029c/isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6", size = 92310 }, -] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, -] - -[[package]] -name = "packaging" -version = "24.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, -] - -[[package]] -name = "platformdirs" -version = "4.3.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, -] - [[package]] name = "pycparser" version = "2.22" @@ -306,15 +221,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 }, ] -[[package]] -name = "pyflakes" -version = "3.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/f9/669d8c9c86613c9d568757c7f5824bd3197d7b1c6c27553bc5618a27cce2/pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f", size = 63788 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/d7/f1b7db88d8e4417c5d47adad627a93547f44bdc9028372dbd2313f34a855/pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a", size = 62725 }, -] - [[package]] name = "python-dotenv" version = "1.0.1" @@ -339,6 +245,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, ] +[[package]] +name = "ruff" +version = "0.8.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/00/089db7890ea3be5709e3ece6e46408d6f1e876026ec3fd081ee585fef209/ruff-0.8.6.tar.gz", hash = "sha256:dcad24b81b62650b0eb8814f576fc65cfee8674772a6e24c9b747911801eeaa5", size = 3473116 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/28/aa07903694637c2fa394a9f4fe93cf861ad8b09f1282fa650ef07ff9fe97/ruff-0.8.6-py3-none-linux_armv6l.whl", hash = "sha256:defed167955d42c68b407e8f2e6f56ba52520e790aba4ca707a9c88619e580e3", size = 10628735 }, + { url = "https://files.pythonhosted.org/packages/2b/43/827bb1448f1fcb0fb42e9c6edf8fb067ca8244923bf0ddf12b7bf949065c/ruff-0.8.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:54799ca3d67ae5e0b7a7ac234baa657a9c1784b48ec954a094da7c206e0365b1", size = 10386758 }, + { url = "https://files.pythonhosted.org/packages/df/93/fc852a81c3cd315b14676db3b8327d2bb2d7508649ad60bfdb966d60738d/ruff-0.8.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e88b8f6d901477c41559ba540beeb5a671e14cd29ebd5683903572f4b40a9807", size = 10007808 }, + { url = "https://files.pythonhosted.org/packages/94/e9/e0ed4af1794335fb280c4fac180f2bf40f6a3b859cae93a5a3ada27325ae/ruff-0.8.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0509e8da430228236a18a677fcdb0c1f102dd26d5520f71f79b094963322ed25", size = 10861031 }, + { url = "https://files.pythonhosted.org/packages/82/68/da0db02f5ecb2ce912c2bef2aa9fcb8915c31e9bc363969cfaaddbc4c1c2/ruff-0.8.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91a7ddb221779871cf226100e677b5ea38c2d54e9e2c8ed847450ebbdf99b32d", size = 10388246 }, + { url = "https://files.pythonhosted.org/packages/ac/1d/b85383db181639019b50eb277c2ee48f9f5168f4f7c287376f2b6e2a6dc2/ruff-0.8.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:248b1fb3f739d01d528cc50b35ee9c4812aa58cc5935998e776bf8ed5b251e75", size = 11424693 }, + { url = "https://files.pythonhosted.org/packages/ac/b7/30bc78a37648d31bfc7ba7105b108cb9091cd925f249aa533038ebc5a96f/ruff-0.8.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:bc3c083c50390cf69e7e1b5a5a7303898966be973664ec0c4a4acea82c1d4315", size = 12141921 }, + { url = "https://files.pythonhosted.org/packages/60/b3/ee0a14cf6a1fbd6965b601c88d5625d250b97caf0534181e151504498f86/ruff-0.8.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52d587092ab8df308635762386f45f4638badb0866355b2b86760f6d3c076188", size = 11692419 }, + { url = "https://files.pythonhosted.org/packages/ef/d6/c597062b2931ba3e3861e80bd2b147ca12b3370afc3889af46f29209037f/ruff-0.8.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:61323159cf21bc3897674e5adb27cd9e7700bab6b84de40d7be28c3d46dc67cf", size = 12981648 }, + { url = "https://files.pythonhosted.org/packages/68/84/21f578c2a4144917985f1f4011171aeff94ab18dfa5303ac632da2f9af36/ruff-0.8.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ae4478b1471fc0c44ed52a6fb787e641a2ac58b1c1f91763bafbc2faddc5117", size = 11251801 }, + { url = "https://files.pythonhosted.org/packages/6c/aa/1ac02537c8edeb13e0955b5db86b5c050a1dcba54f6d49ab567decaa59c1/ruff-0.8.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0c000a471d519b3e6cfc9c6680025d923b4ca140ce3e4612d1a2ef58e11f11fe", size = 10849857 }, + { url = "https://files.pythonhosted.org/packages/eb/00/020cb222252d833956cb3b07e0e40c9d4b984fbb2dc3923075c8f944497d/ruff-0.8.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9257aa841e9e8d9b727423086f0fa9a86b6b420fbf4bf9e1465d1250ce8e4d8d", size = 10470852 }, + { url = "https://files.pythonhosted.org/packages/00/56/e6d6578202a0141cd52299fe5acb38b2d873565f4670c7a5373b637cf58d/ruff-0.8.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:45a56f61b24682f6f6709636949ae8cc82ae229d8d773b4c76c09ec83964a95a", size = 10972997 }, + { url = "https://files.pythonhosted.org/packages/be/31/dd0db1f4796bda30dea7592f106f3a67a8f00bcd3a50df889fbac58e2786/ruff-0.8.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:496dd38a53aa173481a7d8866bcd6451bd934d06976a2505028a50583e001b76", size = 11317760 }, + { url = "https://files.pythonhosted.org/packages/d4/70/cfcb693dc294e034c6fed837fa2ec98b27cc97a26db5d049345364f504bf/ruff-0.8.6-py3-none-win32.whl", hash = "sha256:e169ea1b9eae61c99b257dc83b9ee6c76f89042752cb2d83486a7d6e48e8f764", size = 8799729 }, + { url = "https://files.pythonhosted.org/packages/60/22/ae6bcaa0edc83af42751bd193138bfb7598b2990939d3e40494d6c00698c/ruff-0.8.6-py3-none-win_amd64.whl", hash = "sha256:f1d70bef3d16fdc897ee290d7d20da3cbe4e26349f62e8a0274e7a3f4ce7a905", size = 9673857 }, + { url = "https://files.pythonhosted.org/packages/91/f8/3765e053acd07baa055c96b2065c7fab91f911b3c076dfea71006666f5b0/ruff-0.8.6-py3-none-win_arm64.whl", hash = "sha256:7d7fc2377a04b6e04ffe588caad613d0c460eb2ecba4c0ccbbfe2bc973cbc162", size = 9149556 }, +] + [[package]] name = "sentry-sdk" version = "2.19.2" @@ -368,9 +299,7 @@ ci = [ { name = "coverage" }, ] development = [ - { name = "autoflake" }, - { name = "black" }, - { name = "isort" }, + { name = "ruff" }, ] sentry-enabled = [ { name = "sentry-sdk" }, @@ -378,14 +307,12 @@ sentry-enabled = [ [package.metadata] requires-dist = [ - { name = "autoflake", marker = "extra == 'development'", specifier = ">=1.4" }, - { name = "black", marker = "extra == 'development'", specifier = ">=20.8b1" }, { name = "click", specifier = ">=8.0.1" }, { name = "coverage", marker = "extra == 'ci'", specifier = ">=5.3.1" }, { name = "cryptography", specifier = ">=3.4.7" }, - { name = "isort", marker = "extra == 'development'", specifier = ">=5.6.4" }, { name = "python-dotenv", specifier = ">=0.15.0" }, { name = "requests", specifier = ">=2.25.1" }, + { name = "ruff", marker = "extra == 'development'", specifier = ">=0.8.6" }, { name = "sentry-sdk", marker = "extra == 'sentry-enabled'", specifier = ">=0.19.5" }, ]