forgot to add misc.py

This commit is contained in:
Izalia Mae 2021-10-24 12:58:37 -04:00
parent e7d2b87a45
commit 5f79d2796f

View file

@ -0,0 +1,300 @@
from datetime import datetime, timezone, timedelta
from .. import logging, boolean
from ..dotdict import DotDict
from ..path import Path
UtcTime = timezone.utc
LocalTime = datetime.now(UtcTime).astimezone().tzinfo
cookie_params = {
'Expires': 'expires',
'Max-Age': 'maxage',
'Domain': 'domain',
'Path': 'path',
'SameSite': 'samesite',
'Secure': 'secure',
'HttpOnly': 'httponly'
}
class Cookies(DotDict):
def __setitem__(self, key, value):
if type(value) != CookieItem:
value = CookieItem(key, value)
super().__setitem__(key, value)
class Headers(DotDict):
def __getattr__(self, key):
return self[key.replace('_', '-')]
def __setattr__(self, key, value):
self[key.replace('_', '-')] = value
def __getitem__(self, key):
return super().__getitem__(key.title())
def __setitem__(self, key, value):
key = key.title()
if key in ['Cookie', 'Set-Cookie']:
logging.warning('Do not set the "Cookie" or "Set-Cookie" headers')
return
try:
self[key].append(value)
except KeyError:
super().__setitem__(key, HeaderItem(key, value))
def getone(self, key, default=None):
try:
return self[key].one()
except (KeyError, IndexError):
return default
def setall(self, key, value):
self[key].set(value)
#def update(self, data):
#for k,v in data.items():
#self.__setitem__(k,v)
class CookieItem:
def __init__(self, key, value, **kwargs):
self.key = key
self.value = value
self.args = DotDict()
for k,v in kwargs.items():
if k not in cookie_params.values():
raise AttributeError(f'Not a valid cookie parameter: {key}')
setattr(self, k, v)
def __str__(self):
text = f'{self.key}={self.value}'
if self.expires:
text += f'; Expires={self.expires.strftime("%a, %d %b %Y %H:%M:%S GMT")}'
if self.maxage != None:
text += f'; Max-Age={self.maxage}'
if self.domain:
text += f'; Domain={self.domain}'
if self.path:
text += f'; Path={self.path}'
if self.samesite:
text += f'; SameSite={self.samesite}'
if self.secure:
text += f'; Secure'
if self.httponly:
text += f'; HttpOnly'
return text
@classmethod
def from_string(cls, data):
kwargs = {}
for idx, pairs in enumerate(data.split(';')):
try:
k, v = pairs.split('=', 1)
v = v.strip()
except:
k, v = pairs, True
k = k.replace(' ', '')
if isinstance(v, str) and v.startswith('"') and v.endswith('"'):
v = v[1:-1]
if idx == 0:
key = k
value = v
elif k in cookie_params.keys():
kwargs[cookie_params[k]] = v
else:
logging.info(f'Invalid key/value pair for cookie: {k} = {v}')
return cls(key, value, **kwargs)
@property
def expires(self):
return self.args.get('Expires')
@expires.setter
def expires(self, data):
if isinstance(data, str):
data = datetime.strptime(data, '%a, %d %b %Y %H:%M:%S GMT').replace(tzinfo=UtcTime)
elif isinstance(data, int) or isinstance(data, float):
data = datetime.fromtimestamp(data).replace(tzinfo=UtcTime)
elif isinstance(data, datetime):
if not data.tzinfo:
data = data.replace(tzinfo=UtcTime)
elif isinstance(data, timedelta):
data = datetime.now(UtcTime) + data
else:
raise TypeError(f'Expires must be a http date string, timestamp, or datetime object, not {data.__class__.__name__}')
self.args['Expires'] = data
@property
def maxage(self):
return self.args.get('Max-Age')
@maxage.setter
def maxage(self, data):
if isinstance(data, int):
pass
elif isinstance(date, timedelta):
data = data.seconds
elif isinstance(date, datetime):
data = (datetime.now() - date).seconds
else:
raise TypeError(f'Max-Age must be an integer, timedelta object, or datetime object, not {data.__class__.__name__}')
self.args['Max-Age'] = data
@property
def domain(self):
return self.args.get('Domain')
@domain.setter
def domain(self, data):
if not isinstance(data, str):
raise ValueError(f'Domain must be a string, not {data.__class__.__name__}')
self.args['Domain'] = data
@property
def path(self):
return self.args.get('Path')
@path.setter
def path(self, data):
if not isinstance(data, str):
raise ValueError(f'Path must be a string or izzylib.Path object, not {data.__class__.__name__}')
self.args['Path'] = Path(data)
@property
def secure(self):
return self.args.get('Secure')
@secure.setter
def secure(self, data):
self.args['Secure'] = boolean(data)
@property
def httponly(self):
return self.args.get('HttpOnly')
@httponly.setter
def httponly(self, data):
self.args['HttpOnly'] = boolean(data)
@property
def samesite(self):
return self.args.get('SameSite')
@samesite.setter
def samesite(self, data):
if isinstance(data, bool):
data = 'Strict' if data else 'None'
elif isinstance(data, str) and data.title() in ['Strict', 'Lax', 'None']:
data = data.title()
else:
raise TypeError(f'SameSite must be a boolean or one of Strict, Lax, or None, not {data.__class__.__name__}')
self.args['SameSite'] = data
self.args['Secure'] = True
def as_dict(self):
data = DotDict({self.key: self.value})
data.update(self.args)
return data
def set_defaults(self):
for key in list(self.args.keys()):
del self.args[key]
def set_delete(self):
self.args.pop('Expires', None)
self.maxage = 0
return self
class HeaderItem(list):
def __init__(self, key, values):
super().__init__()
self.update(values)
self.key = key
def __str__(self):
return ','.join(str(v) for v in self)
def set(self, *value):
self.clear()
self.append(value)
def one(self):
return self[0]
def update(self, *items):
for item in items:
self.append(item)