multiple fixes and re-subclass path to str

This commit is contained in:
Izalia Mae 2021-06-07 20:43:54 -04:00
parent 1ea42aeca9
commit b728df4bc0
12 changed files with 189 additions and 95 deletions

View file

@ -12,9 +12,9 @@ __version__ = '.'.join([str(v) for v in __version_tpl__])
from . import logging from . import logging
izzylog = logging.get_logger('IzzyLib') izzylog = logging.logger['IzzyLib']
from .dotdict import DotDict, LowerDotDict, DefaultDotDict, JsonEncoder from .dotdict import DotDict, LowerDotDict, DefaultDotDict, MultiDotDict, JsonEncoder
from .misc import * from .misc import *
from .cache import LruCache, TtlCache from .cache import LruCache, TtlCache
from .connection import Connection from .connection import Connection

View file

@ -107,6 +107,64 @@ class LowerDotDict(DotDict):
return super().update(data) return super().update(data)
class MultiDotDict(DotDict):
def __getattr__(self, key):
return self.__getitem__(key)
def __setitem__(self, key, value):
try:
self.__getitem__(key.lower(), False).append(value)
except KeyError as e:
super().__setitem__(key.lower(), [value])
def __getitem__(self, key, single=True):
values = super().__getitem__(key.lower())
if single:
try:
return values[0]
except IndexError:
return None
return values
def update(self, data):
for k,v in data.items():
self[k] = v
def get(self, key, default=None):
try:
return self.__getitem__(key)
except KeyError:
return default
def getall(self, key):
try:
return super().__getitem__(key)
except KeyError as e:
if not default:
raise e from None
return default
def delone(self, key, value):
self.__getitem__(key, False).remove(value)
def delete(self, key):
self.pop(key)
class JsonEncoder(json.JSONEncoder): class JsonEncoder(json.JSONEncoder):
def default(self, obj): def default(self, obj):
if not any(map(isinstance, [obj], [str, int, float, dict])): if not any(map(isinstance, [obj], [str, int, float, dict])):

View file

@ -81,12 +81,12 @@ class HttpUrllibClient:
filepath = Path(filepath) filepath = Path(filepath)
path = filepath.parent path = filepath.parent
if not path.exists() and not create_dirs: if not path.exists and not create_dirs:
raise FileNotFoundError(f'Path does not exist: {path}') raise FileNotFoundError(f'Path does not exist: {path}')
path.mkdir() path.mkdir()
if filepath.exists(): if filepath.exists:
kwargs['headers']['range'] = f'bytes={filepath.size}' kwargs['headers']['range'] = f'bytes={filepath.size}'
resp = self.request(url, *args, stream=True, **kwargs) resp = self.request(url, *args, stream=True, **kwargs)
@ -111,7 +111,7 @@ class HttpUrllibClient:
filepath = Path(filepath) filepath = Path(filepath)
path = filepath.parent path = filepath.parent
if not path.exists() and not create_dirs: if not path.exists and not create_dirs:
raise FileNotFoundError(f'Path does not exist: {path}') raise FileNotFoundError(f'Path does not exist: {path}')
path.mkdir() path.mkdir()
@ -122,7 +122,7 @@ class HttpUrllibClient:
raise exceptions.HttpFileDownloadedError(f'Failed to download {url}: {resp.status}, body: {resp.body}') raise exceptions.HttpFileDownloadedError(f'Failed to download {url}: {resp.status}, body: {resp.body}')
if not filename: if not filename:
filename = Path(url).stem() filename = Path(url).stem
byte = BytesIO() byte = BytesIO()
image = Image.open(BytesIO(resp.body)) image = Image.open(BytesIO(resp.body))

View file

@ -159,7 +159,7 @@ def import_from_path(mod_path):
mod_path = Path(mod_path) mod_path = Path(mod_path)
if mod_path.isdir(): if mod_path.isdir:
path = mod_path.join('__init__.py') path = mod_path.join('__init__.py')
name = path.name name = path.name
@ -167,7 +167,7 @@ def import_from_path(mod_path):
path = mod_path path = mod_path
name = path.name.replace('.py', '', -1) name = path.name.replace('.py', '', -1)
spec = util.spec_from_file_location(name, path.str()) spec = util.spec_from_file_location(name, path)
module = util.module_from_spec(spec) module = util.module_from_spec(spec)
spec.loader.exec_module(module) spec.loader.exec_module(module)
return module return module
@ -189,16 +189,16 @@ def nfs_check(path):
return return
proc = Path('/proc/mounts') proc = Path('/proc/mounts')
path = Path(path).resolve() path = Path(path).resolve
if not proc.exists(): if not proc.exists:
return True return True
with proc.open() as fd: with proc.open() as fd:
for line in fd: for line in fd:
line = line.split() line = line.split()
if line[2] == 'nfs' and line[1] in path.str(): if line[2] == 'nfs' and line[1] in path:
return True return True
return False return False
@ -281,7 +281,7 @@ def prompt(prompt, default=None, valtype=str, options=[], password=False):
ret = valtype(value) ret = valtype(value)
while valtype == Path and not ret.parent().exists(): while valtype == Path and not ret.parent.exists:
input_func('Parent directory doesn\'t exist') input_func('Parent directory doesn\'t exist')
ret = Path(input(prompt)) ret = Path(input(prompt))

View file

@ -1,14 +1,18 @@
import pathlib import os, shutil
from datetime import datetime
from functools import cached_property
from . import DotDict, JsonEncoder from . import DotDict, JsonEncoder
class Path(pathlib.Path): class Path(str):
def __init__(self, path, exist=True, missing=True, parents=True): def __init__(self, path, exist=True, missing=True, parents=True):
if str(path).startswith('~'): if str(path).startswith('~'):
path = pathlib.Path(path).expanduser() str.__new__(Path, os.path.expanduser(path))
super().__init__(path) else:
str.__new__(Path, path)
self.config = { self.config = {
'missing': missing, 'missing': missing,
@ -17,18 +21,18 @@ class Path(pathlib.Path):
} }
def __new__(cls, content):
return str.__new__(cls, content)
def __check_dir(self, path=None): def __check_dir(self, path=None):
target = self if not path else Path(path) target = self if not path else Path(path)
if not self.parents and not target.parent().exists(): if not self.parents and not target.parent.exists:
raise FileNotFoundError('Parent directories do not exist:', target.str()) raise FileNotFoundError('Parent directories do not exist:', target)
if not self.exist and target.exists(): if not self.exist and target.exists:
raise FileExistsError('File or directory already exists:', target.str()) raise FileExistsError('File or directory already exists:', target)
def __parse_perm_octal(self, mode):
return mode if type(mode) == oct else eval(f'0o{mode}')
def append(self, text): def append(self, text):
@ -41,8 +45,7 @@ class Path(pathlib.Path):
def chmod(self, mode=None): def chmod(self, mode=None):
octal = self.__parse_perm_octal(mode) os.chmod(self, mode)
super().chmod(octal)
def copy(self, path, overwrite=False): def copy(self, path, overwrite=False):
@ -51,21 +54,31 @@ class Path(pathlib.Path):
self.__check_dir(path) self.__check_dir(path)
if target.exists and overwrite: if target.exists and overwrite:
target.delete target.delete()
copyfile(self, target) copyfile(self, target)
def join(self, path): def delete(self):
return Path(self.joinpath(path)) if self.isdir:
rmtree(self)
else:
os.remove(self)
return not self.exists
def join(self, new_path):
return Path(os.path.join(self, new_path))
def json_load(self): def json_load(self):
return DotDict(self.read()) return DotDict(read(self))
def json_save(self, data, indent=None): def json_save(self, data, indent=None):
with self.open('w') as fp: with open(self, 'w') as fp:
fp.write(json.dumps(data, indent=indent, cls=JsonEncoder)) fp.write(json.dumps(data, indent=indent, cls=JsonEncoder))
@ -74,97 +87,121 @@ class Path(pathlib.Path):
self.__check_dir(path) self.__check_dir(path)
if target.exists(): if target.exists:
target.delete() target.delete()
self.symlink_to(path, target.isdir()) self.symlink_to(path, target.isdir)
def listdir(self, recursive=True):
if recursive:
return tuple(self.join(f) for dp, dn, fn in os.walk(self) for f in fn)
return [self.join(path) for path in os.listdir(self)]
def mkdir(self, mode=0o755): def mkdir(self, mode=0o755):
self.__path.mkdir(mode, self.parents, self.exist) if self.parents:
os.makedirs(self, mode, exist_ok=self.exist)
return True if self.__path.exists() else False else:
os.makedir(self, mode, exist_ok=self.exist)
return self.exists
def move(self, path, overwrite=False): def move(self, path, overwrite=False):
self.copy(path, overwrite=overwrite) if not overwrite and self.exists:
self.delete() raise FileExistsError(f'Refusing to move file to existing destination: {path}')
shutil.move(self, path)
def touch(self, mode=0o666): def open(self, *args, **kwargs):
octal = __parse_perm_octal(mode) return open(self, *args, **kwargs)
self.__path.touch(octal, self.exist)
return self.exists()
@property def read(self):
def delete(self): fd = open(self)
if self.isdir(): data = fd.read()
rmtree(self) fd.close()
else: return data
self.unlink()
return not self.exists
def readlines(self):
fd = open(self)
data = fd.readlines()
fd.close()
return data
def touch(self, mode=0o644, utime=None):
timestamp = utime or datetime.now().timestamp()
with self.open('w+') as fd:
os.utime(self, (timestamp, timestamp))
self.chmod(mode)
return self.exists
@property @property
def exists(self): def exists(self):
return super().exists() return os.path.exists(self)
@property @cached_property
def home(self): def home(self):
return Path(pathlib.Path.home()) return Path('~')
@property @cached_property
def isdir(self): def isdir(self):
return self.is_dir() return os.path.isdir(self)
@property @cached_property
def isfile(self): def isfile(self):
return self.is_file() return os.path.isfile(self)
@property @cached_property
def islink(self): def islink(self):
return self.is_symlink() return os.path.islink(self)
@property
def listdir(self, recursive=True):
paths = self.iterdir() if recursive else os.listdir(self)
return [Path(path) for path in paths]
@property @property
def mtime(self): def mtime(self):
return os.path.getmtime(self.str()) return os.path.getmtime(self)
@property @cached_property
def name(self):
return os.path.basename(self)
@cached_property
def parent(self): def parent(self):
return Path(super().parent) return Path(os.path.dirname(self))
@property @cached_property
def read(self):
return self.open().read()
@property
def readlines(self):
return self.open().readlines()
@property
def resolve(self): def resolve(self):
return Path(super().resolve()) return Path(os.path.abspath(self))
@property @property
def size(self): def size(self):
return self.stat().st_size return os.path.getsize(self)
@cached_property
def stem(self):
return os.path.basename(self).split('.')[0]
@cached_property
def suffix(self):
return os.path.splitext(self)[1]

View file

@ -1,11 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from setuptools import setup from setuptools import setup, find_namespace_packages
setup( setup(
name="IzzyLib Base", name="IzzyLib Base",
version='0.6.0', version='0.6.0',
packages=['izzylib'], packages=find_namespace_packages(include=['izzylib']),
python_requires='>=3.7.0', python_requires='>=3.7.0',
include_package_data=False, include_package_data=False,
author='Zoey Mae', author='Zoey Mae',

View file

@ -19,7 +19,7 @@ requires = [
setup( setup(
name="IzzyLib HTTP Server", name="IzzyLib HTTP Server",
version='0.6.0', version='0.6.0',
packages=['izzylib.http_server'], packages=find_namespace_packages(include=['izzylib.http_server']),
python_requires='>=3.7.0', python_requires='>=3.7.0',
install_requires=requires, install_requires=requires,
include_package_data=False, include_package_data=False,

View file

@ -12,7 +12,7 @@ requires = [
setup( setup(
name="IzzyLib Requests Client", name="IzzyLib Requests Client",
version='0.6.0', version='0.6.0',
packages=['izzylib.http_requests_client'], packages=find_namespace_packages(include=['izzylib.http_requests_client']),
python_requires='>=3.7.0', python_requires='>=3.7.0',
install_requires=requires, install_requires=requires,
include_package_data=False, include_package_data=False,

View file

@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from setuptools import setup from setuptools import setup, find_namespace_packages
requires = [ requires = [
@ -15,7 +15,7 @@ requires = [
setup( setup(
name="IzzyLib SQL", name="IzzyLib SQL",
version='0.6.0', version='0.6.0',
packages=['izzylib.sql'], packages=find_namespace_packages(include=['izzylib.sql']),
python_requires='>=3.7.0', python_requires='>=3.7.0',
install_requires=requires, install_requires=requires,
include_package_data=False, include_package_data=False,

View file

@ -22,7 +22,7 @@ class Template(Environment):
self.func_context = context self.func_context = context
for path in search: for path in search:
self.__add_search_path(path) self.__add_search_path(Path(path))
super().__init__( super().__init__(
loader=ChoiceLoader([FileSystemLoader(path) for path in self.search]), loader=ChoiceLoader([FileSystemLoader(path) for path in self.search]),
@ -46,13 +46,12 @@ class Template(Environment):
def __add_search_path(self, path): def __add_search_path(self, path):
tpl_path = Path(path) if not path.exists:
raise FileNotFoundError(f'Cannot find search path: {path}')
if not tpl_path.exists(): if path not in self.search:
raise FileNotFoundError('Cannot find search path:', tpl_path.str()) self.search.append(path)
if tpl_path.str() not in self.search:
self.search.append(tpl_path.str())
def setContext(self, context): def setContext(self, context):
if not hasattr(context, '__call__'): if not hasattr(context, '__call__'):

View file

@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from setuptools import setup from setuptools import setup, find_namespace_packages
requires = [ requires = [
@ -13,7 +13,7 @@ requires = [
setup( setup(
name="IzzyLib Templates", name="IzzyLib Templates",
version='0.6.0', version='0.6.0',
packages=['izzylib.template'], packages=find_namespace_packages(include=['izzylib.template']),
python_requires='>=3.7.0', python_requires='>=3.7.0',
install_requires=requires, install_requires=requires,
include_package_data=False, include_package_data=False,

View file

@ -13,7 +13,7 @@ requires = [
setup( setup(
name="IzzyLib TinyDB", name="IzzyLib TinyDB",
version='0.6.0', version='0.6.0',
packages=['izzylib.tinydb'], packages=find_namespace_packages(include=['izzylib.tinydb']),
python_requires='>=3.7.0', python_requires='>=3.7.0',
install_requires=requires, install_requires=requires,
include_package_data=False, include_package_data=False,