refactor: improve exception handling

- clear exception context
- use TypeError instead of asserts
This commit is contained in:
ptrcnull 2024-07-27 20:13:32 +02:00
parent 5564a44d0d
commit 1eb51184e5
4 changed files with 34 additions and 23 deletions

View file

@ -1,8 +1,8 @@
import logging import logging
import os import os
import sys import sys
from typing import Any, Optional
from pathlib import Path from pathlib import Path
from typing import Any
import tomllib import tomllib
@ -40,15 +40,30 @@ def config_parse_dict(raw_conf: dict[str, Any], key: str) -> dict[str, str]:
sys.exit(1) sys.exit(1)
for k, v in raw_conf[key].items(): for k, v in raw_conf[key].items():
assert isinstance(k, str), f'"{k}" is not a string' if not isinstance(k, str):
assert isinstance(v, str), f'"{k}" value "{v}" is not a string' raise TypeError(f'"{k}" is not a string')
if not isinstance(v, str):
raise TypeError(f'"{k}" value "{v}" is not a string')
result: dict[str, str] = raw_conf[key] result: dict[str, str] = raw_conf[key]
return result return result
def config_parse_list(raw_conf: dict[str, Any], key: str) -> list[str]:
result: list[str] = []
def read_config(path: Optional[str]) -> Config: if not isinstance(raw_conf[key], list):
raise TypeError(f'"{key}" must be a list, not {type(raw_conf[key]).__name__}')
for item in raw_conf[key]:
if not isinstance(item, str):
raise TypeError(f'"{key}" list item must be a string')
result.append(item)
return result
def read_config(path: str | None) -> Config:
if not path: if not path:
# should be here only when running from hook # should be here only when running from hook
path = os.getenv('NYACME_CONFIG', '/etc/nyacme.toml') path = os.getenv('NYACME_CONFIG', '/etc/nyacme.toml')
@ -67,10 +82,7 @@ def read_config(path: Optional[str]) -> Config:
post_acquire = [] post_acquire = []
if 'post_acquire' in raw_conf: if 'post_acquire' in raw_conf:
assert isinstance(raw_conf['post_acquire'], list), 'post_acquire is not a list' post_acquire = config_parse_list(raw_conf, 'post_acquire')
for cmd in raw_conf['post_acquire']:
assert isinstance(cmd, str), 'post_acquire item has to be a string'
post_acquire.append(cmd)
c.post_acquire = post_acquire c.post_acquire = post_acquire
@ -83,9 +95,6 @@ def read_config(path: Optional[str]) -> Config:
log.error('missing "certificates"') log.error('missing "certificates"')
sys.exit(1) sys.exit(1)
c.certificates = [] c.certificates = config_parse_list(raw_conf, 'certificates')
for cert in raw_conf['certificates']:
assert isinstance(cert, str), 'certificate should be a string'
c.certificates.append(cert)
return c return c

View file

@ -29,7 +29,7 @@ class HEHandler(Handler):
except urllib.error.HTTPError as ex: except urllib.error.HTTPError as ex:
self.log.error('cannot set the record %s: %s', record_name, ex) self.log.error('cannot set the record %s: %s', record_name, ex)
res = ex.fp.read().decode('utf-8') res = ex.fp.read().decode('utf-8')
raise Exception(res) raise Exception(res) from ex
def create(self, record_name: str, record_value: str) -> None: def create(self, record_name: str, record_value: str) -> None:
self.log.info('creating %s with value %s', record_name, record_value) self.log.info('creating %s with value %s', record_name, record_value)

View file

@ -1,6 +1,6 @@
import json import json
import urllib.request import urllib.request
from typing import Any, Optional from typing import Any
from ..config import Config from ..config import Config
from .base import Handler from .base import Handler
@ -22,7 +22,7 @@ class HetznerHandler(Handler):
self.nameservers = zone['ns'] self.nameservers = zone['ns']
break break
def fetch(self, url: str, data: Optional[Any] = None, **kwargs: str) -> Any: def fetch(self, url: str, data: Any | None = None, **kwargs: str) -> Any:
req = urllib.request.Request('https://dns.hetzner.com/api/v1' + url) req = urllib.request.Request('https://dns.hetzner.com/api/v1' + url)
req.add_header('Auth-API-Token', self.secret) req.add_header('Auth-API-Token', self.secret)
method = 'GET' method = 'GET'
@ -39,9 +39,9 @@ class HetznerHandler(Handler):
self.log.error('cannot %s %s: %s', req.method, url, ex) self.log.error('cannot %s %s: %s', req.method, url, ex)
res = ex.fp.read().decode('utf-8') res = ex.fp.read().decode('utf-8')
try: try:
raise Exception(json.loads(res)['error']) raise Exception(json.loads(res)['error']) from None
except json.JSONDecodeError: except json.JSONDecodeError:
raise Exception(res) raise Exception(res) from None
def create(self, record_name: str, record_value: str) -> None: def create(self, record_name: str, record_value: str) -> None:
self.remove(record_name) self.remove(record_name)

View file

@ -17,9 +17,11 @@ class PorkbunHandler(Handler):
self.nameservers = self.fetch(f'/domain/getNs/{self.zone}')['ns'] self.nameservers = self.fetch(f'/domain/getNs/{self.zone}')['ns']
def fetch(self, url: str, data: dict[str, Any] = {}) -> Any: def fetch(self, url: str, data: dict[str, Any] | None = None) -> Any:
req = urllib.request.Request('https://api.porkbun.com/api/json/v3' + url) req = urllib.request.Request('https://api.porkbun.com/api/json/v3' + url)
if data is None:
data = {}
data['apikey'] = self.apikey data['apikey'] = self.apikey
data['secretapikey'] = self.secretapikey data['secretapikey'] = self.secretapikey
req.data = json.dumps(data).encode('utf-8') req.data = json.dumps(data).encode('utf-8')
@ -27,17 +29,17 @@ class PorkbunHandler(Handler):
req.method = 'POST' req.method = 'POST'
try: try:
with urllib.request.urlopen(req) as f: with urllib.request.urlopen(req) as f:
data = json.load(f) res = json.load(f)
if data['status'] != 'SUCCESS': if res['status'] != 'SUCCESS':
raise Exception(data['message']) raise Exception(res['message'])
return data return res
except urllib.error.HTTPError as ex: except urllib.error.HTTPError as ex:
self.log.error('cannot %s %s: %s', req.method, url, ex) self.log.error('cannot %s %s: %s', req.method, url, ex)
res = ex.fp.read().decode('utf-8') res = ex.fp.read().decode('utf-8')
try: try:
raise Exception(json.loads(res)['message']) raise Exception(json.loads(res)['message'])
except json.JSONDecodeError: except json.JSONDecodeError:
raise Exception(res) raise Exception(res) from None
def create(self, record_name: str, record_value: str) -> None: def create(self, record_name: str, record_value: str) -> None:
self.remove(record_name) self.remove(record_name)