sources refactor

This commit is contained in:
Dmitry Belyaev 2024-02-25 14:44:16 +03:00
parent 20ef74f32b
commit 3131f2d0a0
Signed by: b4tman
GPG Key ID: 41A00BF15EA7E5F3
3 changed files with 63 additions and 48 deletions

View File

@ -6,7 +6,7 @@ from httpx import AsyncHTTPTransport
class IPAddreses(NamedTuple): class IPAddreses(NamedTuple):
"""набор из названия источника и IP адресов, результат одного из источников""" """Набор из названия источника и IP адресов, результат одного из источников"""
source_name: str source_name: str
ipv4: str ipv4: str
@ -14,7 +14,7 @@ class IPAddreses(NamedTuple):
class BaseSourceProvider(ABC): class BaseSourceProvider(ABC):
"""базовый класс для провайдеров источников""" """Базовый класс для провайдеров источников"""
_childs = {} _childs = {}
registred = {} registred = {}
@ -35,14 +35,22 @@ class BaseSourceProvider(ABC):
self.post_init() self.post_init()
def post_init(self): def post_init(self):
"""метод для переопределения пост инициализации""" """Метод для переопределения пост инициализации"""
... ...
def __str__(self): def __str__(self):
return f"{self.__class__.__name__}: {self.name}" return f"{self.__class__.__name__}: {self.name}"
def filter_ipv4(self, value: str) -> str:
"""Функция для проверки валидности IPv4 адреса, возвращает "" если адрес неправильный или пустой"""
return value if value and valid_ipv4(value) else ""
def filter_ipv6(self, value: str) -> str:
"""Функция для проверки валидности IPv6 адреса, возвращает "" если адрес неправильный или пустой"""
return value if value and valid_ipv6(value) else ""
async def fetch_all(self) -> IPAddreses: async def fetch_all(self) -> IPAddreses:
"""метод для получения всех ip адресов сразу""" """Метод для получения всех IP адресов сразу"""
results = await asyncio.gather( results = await asyncio.gather(
self.fetch_v4(), self.fetch_v6(), return_exceptions=True self.fetch_v4(), self.fetch_v6(), return_exceptions=True
) )
@ -51,13 +59,23 @@ class BaseSourceProvider(ABC):
self.name, *("" if isinstance(i, Exception) else i for i in results) self.name, *("" if isinstance(i, Exception) else i for i in results)
) )
async def fetch_v4(self) -> str:
"""Метод внешнего интерфейса для получения IPv4"""
ipv4 = await self.fetch_v4_impl()
return self.filter_ipv4(ipv4)
async def fetch_v6(self) -> str:
"""Метод внешнего интерфейса для получения IPv6"""
ipv6 = await self.fetch_v6_impl()
return self.filter_ipv6(ipv6)
def __init_subclass__(cls) -> None: def __init_subclass__(cls) -> None:
BaseSourceProvider._childs[cls.__name__] = cls BaseSourceProvider._childs[cls.__name__] = cls
return super().__init_subclass__() return super().__init_subclass__()
@classmethod @classmethod
def validate_source_config(cls, name: str, config: dict): def validate_source_config(cls, name: str, config: dict):
"""метод валидации конфигурации для провайдера""" """Метод валидации конфигурации для провайдера"""
if "provider" not in config: if "provider" not in config:
return False return False
prov_name = config["provider"] prov_name = config["provider"]
@ -73,7 +91,7 @@ class BaseSourceProvider(ABC):
ipv4t: AsyncHTTPTransport, ipv4t: AsyncHTTPTransport,
ipv6t: AsyncHTTPTransport, ipv6t: AsyncHTTPTransport,
): ):
"""метод регистрации провайдера по конфигурации""" """Метод регистрации провайдера по конфигурации"""
if not cls.validate_source_config(name, config): if not cls.validate_source_config(name, config):
return return
@ -81,18 +99,18 @@ class BaseSourceProvider(ABC):
cls.registred[name] = provider(name, config, ipv4t, ipv6t) cls.registred[name] = provider(name, config, ipv4t, ipv6t)
@abstractmethod @abstractmethod
async def fetch_v4(self) -> str: async def fetch_v4_impl(self) -> Optional[str]:
"""необходимый метод для реализации получения ipv4""" """Необходимый метод для реализации получения IPv4"""
... ...
@abstractmethod @abstractmethod
async def fetch_v6(self) -> str: async def fetch_v6_impl(self) -> Optional[str]:
"""необходимый метод для реализации получения ipv6""" """Необходимый метод для реализации получения IPv6"""
... ...
class BaseOutputProvider(ABC): class BaseOutputProvider(ABC):
"""базовый класс для провайдеров вывода""" """Базовый класс для провайдеров вывода"""
_childs = {} _childs = {}
registred = {} registred = {}
@ -109,7 +127,7 @@ class BaseOutputProvider(ABC):
self.post_init() self.post_init()
def post_init(self): def post_init(self):
"""метод для переопределения пост инициализации""" """Метод для переопределения пост инициализации"""
... ...
def __init_subclass__(cls) -> None: def __init_subclass__(cls) -> None:
@ -120,14 +138,14 @@ class BaseOutputProvider(ABC):
return f"{self.__class__.__name__}: {self.name}" return f"{self.__class__.__name__}: {self.name}"
def best_transport(self, addr_v4: str, addr_v6: str) -> AsyncHTTPTransport: def best_transport(self, addr_v4: str, addr_v6: str) -> AsyncHTTPTransport:
"""метод выбирает лучший транспорт для отправки адресов (либо ipv4 либо ipv6)""" """Метод выбирает лучший транспорт для отправки адресов (либо ipv4 либо ipv6)"""
if addr_v6: if addr_v6:
return self.ipv6t return self.ipv6t
return self.ipv4t return self.ipv4t
@classmethod @classmethod
def validate_source_config(cls, name: str, config: dict): def validate_source_config(cls, name: str, config: dict):
"""метод валидации конфигурации для провайдера""" """Метод валидации конфигурации для провайдера"""
if "provider" not in config: if "provider" not in config:
return False return False
@ -144,24 +162,24 @@ class BaseOutputProvider(ABC):
ipv4t: AsyncHTTPTransport, ipv4t: AsyncHTTPTransport,
ipv6t: AsyncHTTPTransport, ipv6t: AsyncHTTPTransport,
): ):
"""метод регистрации провайдера по конфигурации""" """Метод регистрации провайдера по конфигурации"""
if not cls.validate_source_config(name, config): if not cls.validate_source_config(name, config):
return return
provider = cls._childs[config["provider"]] provider = cls._childs[config["provider"]]
cls.registred[name] = provider(name, config, ipv4t, ipv6t) cls.registred[name] = provider(name, config, ipv4t, ipv6t)
async def set_addrs(self, source_provider: str, addr_v4: str, addr_v6: str): async def set_addrs(self, source_provider: str, addr_v4: str, addr_v6: str):
"""метод внешнего интерфейса для отправки адресов""" """Метод внешнего интерфейса для отправки адресов"""
return await self.set_addrs_imp(source_provider, addr_v4, addr_v6) return await self.set_addrs_imp(source_provider, addr_v4, addr_v6)
@abstractmethod @abstractmethod
async def set_addrs_imp(self, source_provider: str, addr_v4: str, addr_v6: str): async def set_addrs_imp(self, source_provider: str, addr_v4: str, addr_v6: str):
"""необходимый метод для реализации отправки/вывода IP аддресов""" """Необходимый метод для реализации отправки/вывода IP аддресов"""
... ...
class BaseFilterProvider(ABC): class BaseFilterProvider(ABC):
"""базовый класс для провайдеров фильтров""" """Базовый класс для провайдеров фильтров"""
_childs = {} _childs = {}
registred = {} registred = {}
@ -178,7 +196,7 @@ class BaseFilterProvider(ABC):
self.post_init() self.post_init()
def post_init(self): def post_init(self):
"""метод для переопределения пост инициализации""" """Метод для переопределения пост инициализации"""
... ...
def __init_subclass__(cls) -> None: def __init_subclass__(cls) -> None:
@ -190,7 +208,7 @@ class BaseFilterProvider(ABC):
@classmethod @classmethod
def validate_source_config(cls, name: str, config: dict) -> bool: def validate_source_config(cls, name: str, config: dict) -> bool:
"""метод валидации конфигурации для провайдера""" """Метод валидации конфигурации для провайдера"""
if "provider" not in config: if "provider" not in config:
return False return False
@ -207,7 +225,7 @@ class BaseFilterProvider(ABC):
ipv4t: AsyncHTTPTransport, ipv4t: AsyncHTTPTransport,
ipv6t: AsyncHTTPTransport, ipv6t: AsyncHTTPTransport,
): ):
"""метод регистрации провайдера по конфигурации""" """Метод регистрации провайдера по конфигурации"""
if not cls.validate_source_config(name, config): if not cls.validate_source_config(name, config):
return return
@ -215,20 +233,10 @@ class BaseFilterProvider(ABC):
cls.registred[name] = provider(name, config, ipv4t, ipv6t) cls.registred[name] = provider(name, config, ipv4t, ipv6t)
async def check(self, source_provider: str, addr_v4: str, addr_v6: str) -> bool: async def check(self, source_provider: str, addr_v4: str, addr_v6: str) -> bool:
"""метод внешнего интерфейса для проверки адресов""" """Метод внешнего интерфейса для проверки адресов"""
return await self.check_imp(source_provider, addr_v4, addr_v6) return await self.check_imp(source_provider, addr_v4, addr_v6)
@abstractmethod @abstractmethod
async def check_imp(self, source_provider: str, addr_v4: str, addr_v6: str) -> bool: async def check_imp(self, source_provider: str, addr_v4: str, addr_v6: str) -> bool:
"""необходимый метод реализации проверки""" """Необходимый метод реализации проверки"""
... ...
def filter_ipv4(value: str) -> Optional[str]:
"""функция для проверки валидности IPv4 адреса, возвращает None если адрес неправильный или пустой"""
return value and valid_ipv4(value) and value or None
def filter_ipv6(value: str) -> Optional[str]:
"""функция для проверки валидности IPv6 адреса, возвращает None если адрес неправильный или пустой"""
return value and valid_ipv6(value) and value or None

View File

@ -6,27 +6,33 @@ from pddnsc.base import BaseSourceProvider
class DummySource(BaseSourceProvider): class DummySource(BaseSourceProvider):
"""имитация получения пустых адресов""" """Имитация получения пустых адресов"""
async def fetch_v4(self) -> str: async def fetch_v4_impl(self) -> str:
result = await asyncio.sleep(self.config.get("delay", 1), result="") result = await asyncio.sleep(self.config.get("delay", 1), result="")
return result return result
async def fetch_v6(self) -> str: async def fetch_v6_impl(self) -> str:
result = await asyncio.sleep(self.config.get("delay", 1), result="") result = await asyncio.sleep(self.config.get("delay", 1), result="")
return result return result
class FakeSource(BaseSourceProvider): class FakeSource(BaseSourceProvider):
"""имитация получения заданных в конфигурации адресов""" """Имитация получения заданных в конфигурации адресов
async def fetch_v4(self) -> str: Конфигурация:
- ipv4: строка IPv4, по умолчанию "127.0.0.1"
- ipv6: строка IPv6, по умолчанию "::1"
"""
async def fetch_v4_impl(self) -> str:
result = await asyncio.sleep( result = await asyncio.sleep(
self.config.get("delay", 1), result=self.config.get("ipv4", "127.0.0.1") self.config.get("delay", 1), result=self.config.get("ipv4", "127.0.0.1")
) )
return result return result
async def fetch_v6(self) -> str: async def fetch_v6_impl(self) -> str:
result = await asyncio.sleep( result = await asyncio.sleep(
self.config.get("delay", 1), result=self.config.get("ipv6", "::1") self.config.get("delay", 1), result=self.config.get("ipv6", "::1")
) )

View File

@ -1,6 +1,7 @@
from typing import Optional
import httpx import httpx
from pddnsc.base import BaseSourceProvider, filter_ipv4, filter_ipv6 from pddnsc.base import BaseSourceProvider
class GenericHttpSource(BaseSourceProvider): class GenericHttpSource(BaseSourceProvider):
@ -18,7 +19,7 @@ class GenericHttpSource(BaseSourceProvider):
self.url_v6 = self.config.get("url_v6") self.url_v6 = self.config.get("url_v6")
self.headers = self.config.get("headers", {}) self.headers = self.config.get("headers", {})
async def fetch_v4(self) -> str: async def fetch_v4_impl(self) -> Optional[str]:
if not self.url_v4: if not self.url_v4:
return None return None
@ -29,9 +30,9 @@ class GenericHttpSource(BaseSourceProvider):
response = await client.get(self.url_v4) response = await client.get(self.url_v4)
if response.is_success: if response.is_success:
result = response.text.strip() result = response.text.strip()
return filter_ipv4(result) return result
async def fetch_v6(self) -> str: async def fetch_v6_impl(self) -> Optional[str]:
if not self.url_v6: if not self.url_v6:
return None return None
@ -42,7 +43,7 @@ class GenericHttpSource(BaseSourceProvider):
response = await client.get(self.url_v6) response = await client.get(self.url_v6)
if response.is_success: if response.is_success:
result = response.text.strip() result = response.text.strip()
return filter_ipv6(result) return result
class GenericHttpJsonSource(BaseSourceProvider): class GenericHttpJsonSource(BaseSourceProvider):
@ -68,7 +69,7 @@ class GenericHttpJsonSource(BaseSourceProvider):
headers = {} headers = {}
self.headers = headers.update(self.config.get("headers", {})) self.headers = headers.update(self.config.get("headers", {}))
async def fetch_v4(self) -> str: async def fetch_v4_impl(self) -> Optional[str]:
if not self.url_v4 or self.key_v4 is None: if not self.url_v4 or self.key_v4 is None:
return None return None
@ -79,9 +80,9 @@ class GenericHttpJsonSource(BaseSourceProvider):
response = await client.get(self.url_v4) response = await client.get(self.url_v4)
if response.is_success: if response.is_success:
result = response.json()[self.key_v4].strip() result = response.json()[self.key_v4].strip()
return filter_ipv4(result) return result
async def fetch_v6(self) -> str: async def fetch_v6_impl(self) -> Optional[str]:
if not self.url_v6 or self.key_v6 is None: if not self.url_v6 or self.key_v6 is None:
return None return None
@ -92,4 +93,4 @@ class GenericHttpJsonSource(BaseSourceProvider):
response = await client.get(self.url_v6) response = await client.get(self.url_v6)
if response.is_success: if response.is_success:
result = response.json()[self.key_v6].strip() result = response.json()[self.key_v6].strip()
return filter_ipv6(result) return result