Config with __slots__ and validation
The snippet can be accessed without any authentication.
Authored by
Françoise Conil
"""
https://docs.python.org/3.8/reference/datamodel.html#slots
__slots__ are implemented at the class level by creating descriptors (Implementing Descriptors) for each variable name.
"""
import collections
from http import HTTPStatus
import os
from urllib import request
from urllib.error import URLError
# For validation
ACCEPTED_FORMATS = ('json', 'csv', 'xml')
class Config:
__slots__ = ('name', 'mode', 'url', 'delay', 'format', 'maxitems')
def __init__(self, defaults):
# Initialisation
self.name = os.environ.get(defaults['name'].envvar, defaults['name'].default)
self.mode = os.environ.get(defaults['mode'].envvar, defaults['mode'].default)
self.url = os.environ.get(defaults['url'].envvar, defaults['url'].default)
self.delay = os.environ.get(defaults['delay'].envvar, defaults['delay'].default)
self.format = os.environ.get(defaults['format'].envvar, defaults['format'].default)
self.maxitems = os.environ.get(defaults['maxitems'].envvar, defaults['maxitems'].default)
def valid_format(self):
# Do we accept None value ?
if self.format not in ACCEPTED_FORMATS:
raise ValueError(f"Expected {self.format!r} to be one of {ACCEPTED_FORMATS!r}")
def valid_url(self):
# Do we accept None value ?
try:
response = request.urlopen(self.url)
except URLError as e:
# Avoid showing all stacktrace with URLError and OSError
# https://docs.python.org/3.9/tutorial/errors.html#exception-chaining
raise ValueError(f'Expected {self.url} should is triggering an error {e}') from None
else:
# What about moved content 301 (MOVED_PERMANENTLY) or
# cached content 304 (NOT_MODIFIED)
if response.status != HTTPStatus.OK:
raise ValueError(f'Expected {self.url} should be a valid URL {response.status}')
if __name__ == "__main__":
# Using namedtuple to better manipulated defaults
# https://docs.python.org/3.8/library/collections.html#collections.namedtuple
# ---------------------------------------------------------------------------
fparam = collections.namedtuple('FetcherParameters', ['name', 'envvar', 'description', 'default'])
fetcher_defaults = {
'name': fparam(name='name', envvar='HTTP_FETCHER_NAME', description='Name of the acquisition', default=None),
'mode': fparam(name='mode', envvar='HTTP_FETCHER_MODE', description='http method', default='GET'),
'url': fparam(name='url', envvar='HTTP_FETCHER_URL', description='URL to fetch', default=None),
'delay': fparam(name='delay', envvar='HTTP_FETCHER_DELAY', description='Delay between requests in seconds', default=3600),
'format': fparam(name='format', envvar='HTTP_FETCHER_FORMAT', description='File format', default='json'),
'maxitems': fparam(name='maxitems', envvar='HTTP_FETCHER_MAXITEMS', description='Max number of fetches', default=-1)
}
print(f"-- vars(Config) = {vars(Config)}")
c = Config(fetcher_defaults)
print("-- After instanciation")
for k in fetcher_defaults.keys():
print(f"c.{k} = {getattr(c, k)}, ")
c.format = 'xml'
c.valid_format()
print("-- After format change")
for k in fetcher_defaults.keys():
print(f"c.{k} = {getattr(c, k)}, ")
c.url = 'https://www.twitterfountain.com/'
#c.url = 'https://download.data.grandlyon.com/ws/ldata/velov.stations/all.json?maxfeatures=5&start=1'
c.valid_url()
Please register or sign in to comment