diff --git a/base/izzylib/__init__.py b/base/izzylib/__init__.py index 6772c29..ddbee12 100644 --- a/base/izzylib/__init__.py +++ b/base/izzylib/__init__.py @@ -12,9 +12,9 @@ __version__ = '.'.join([str(v) for v in __version_tpl__]) 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 .cache import LruCache, TtlCache from .connection import Connection diff --git a/base/izzylib/dotdict.py b/base/izzylib/dotdict.py index 455f3a7..ec101ca 100644 --- a/base/izzylib/dotdict.py +++ b/base/izzylib/dotdict.py @@ -107,6 +107,64 @@ class LowerDotDict(DotDict): 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): def default(self, obj): if not any(map(isinstance, [obj], [str, int, float, dict])): diff --git a/base/izzylib/http_urllib_client.py b/base/izzylib/http_urllib_client.py index fc71193..ece6a5d 100644 --- a/base/izzylib/http_urllib_client.py +++ b/base/izzylib/http_urllib_client.py @@ -81,12 +81,12 @@ class HttpUrllibClient: filepath = Path(filepath) 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}') path.mkdir() - if filepath.exists(): + if filepath.exists: kwargs['headers']['range'] = f'bytes={filepath.size}' resp = self.request(url, *args, stream=True, **kwargs) @@ -111,7 +111,7 @@ class HttpUrllibClient: filepath = Path(filepath) 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}') path.mkdir() @@ -122,7 +122,7 @@ class HttpUrllibClient: raise exceptions.HttpFileDownloadedError(f'Failed to download {url}: {resp.status}, body: {resp.body}') if not filename: - filename = Path(url).stem() + filename = Path(url).stem byte = BytesIO() image = Image.open(BytesIO(resp.body)) diff --git a/base/izzylib/misc.py b/base/izzylib/misc.py index a567dd1..41d07d8 100644 --- a/base/izzylib/misc.py +++ b/base/izzylib/misc.py @@ -159,7 +159,7 @@ def import_from_path(mod_path): mod_path = Path(mod_path) - if mod_path.isdir(): + if mod_path.isdir: path = mod_path.join('__init__.py') name = path.name @@ -167,7 +167,7 @@ def import_from_path(mod_path): path = mod_path 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) spec.loader.exec_module(module) return module @@ -189,16 +189,16 @@ def nfs_check(path): return proc = Path('/proc/mounts') - path = Path(path).resolve() + path = Path(path).resolve - if not proc.exists(): + if not proc.exists: return True with proc.open() as fd: for line in fd: 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 False @@ -281,7 +281,7 @@ def prompt(prompt, default=None, valtype=str, options=[], password=False): 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') ret = Path(input(prompt)) diff --git a/base/izzylib/path.py b/base/izzylib/path.py index 1e53ce0..2df16db 100644 --- a/base/izzylib/path.py +++ b/base/izzylib/path.py @@ -1,14 +1,18 @@ -import pathlib +import os, shutil + +from datetime import datetime +from functools import cached_property from . import DotDict, JsonEncoder -class Path(pathlib.Path): +class Path(str): def __init__(self, path, exist=True, missing=True, parents=True): 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 = { '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): target = self if not path else Path(path) - if not self.parents and not target.parent().exists(): - raise FileNotFoundError('Parent directories do not exist:', target.str()) + if not self.parents and not target.parent.exists: + raise FileNotFoundError('Parent directories do not exist:', target) - if not self.exist and target.exists(): - raise FileExistsError('File or directory already exists:', target.str()) - - - def __parse_perm_octal(self, mode): - return mode if type(mode) == oct else eval(f'0o{mode}') + if not self.exist and target.exists: + raise FileExistsError('File or directory already exists:', target) def append(self, text): @@ -41,8 +45,7 @@ class Path(pathlib.Path): def chmod(self, mode=None): - octal = self.__parse_perm_octal(mode) - super().chmod(octal) + os.chmod(self, mode) def copy(self, path, overwrite=False): @@ -51,21 +54,31 @@ class Path(pathlib.Path): self.__check_dir(path) if target.exists and overwrite: - target.delete + target.delete() copyfile(self, target) - def join(self, path): - return Path(self.joinpath(path)) + def delete(self): + 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): - return DotDict(self.read()) + return DotDict(read(self)) 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)) @@ -74,97 +87,121 @@ class Path(pathlib.Path): self.__check_dir(path) - if target.exists(): + if target.exists: 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): - 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): - self.copy(path, overwrite=overwrite) - self.delete() + if not overwrite and self.exists: + raise FileExistsError(f'Refusing to move file to existing destination: {path}') + + shutil.move(self, path) - def touch(self, mode=0o666): - octal = __parse_perm_octal(mode) - self.__path.touch(octal, self.exist) - - return self.exists() + def open(self, *args, **kwargs): + return open(self, *args, **kwargs) - @property - def delete(self): - if self.isdir(): - rmtree(self) + def read(self): + fd = open(self) + data = fd.read() + fd.close() - else: - self.unlink() + return data - 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 def exists(self): - return super().exists() + return os.path.exists(self) - @property + @cached_property def home(self): - return Path(pathlib.Path.home()) + return Path('~') - @property + @cached_property def isdir(self): - return self.is_dir() + return os.path.isdir(self) - @property + @cached_property def isfile(self): - return self.is_file() + return os.path.isfile(self) - @property + @cached_property def islink(self): - return self.is_symlink() - - - @property - def listdir(self, recursive=True): - paths = self.iterdir() if recursive else os.listdir(self) - return [Path(path) for path in paths] + return os.path.islink(self) @property 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): - return Path(super().parent) + return Path(os.path.dirname(self)) - @property - def read(self): - return self.open().read() - - - @property - def readlines(self): - return self.open().readlines() - - - @property + @cached_property def resolve(self): - return Path(super().resolve()) + return Path(os.path.abspath(self)) @property 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] diff --git a/base/setup.py b/base/setup.py index 221dda8..6b78e70 100755 --- a/base/setup.py +++ b/base/setup.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 -from setuptools import setup +from setuptools import setup, find_namespace_packages setup( name="IzzyLib Base", version='0.6.0', - packages=['izzylib'], + packages=find_namespace_packages(include=['izzylib']), python_requires='>=3.7.0', include_package_data=False, author='Zoey Mae', diff --git a/http_server/setup.py b/http_server/setup.py index 5b5ea40..87ed6e1 100644 --- a/http_server/setup.py +++ b/http_server/setup.py @@ -19,7 +19,7 @@ requires = [ setup( name="IzzyLib HTTP Server", version='0.6.0', - packages=['izzylib.http_server'], + packages=find_namespace_packages(include=['izzylib.http_server']), python_requires='>=3.7.0', install_requires=requires, include_package_data=False, diff --git a/requests_client/setup.py b/requests_client/setup.py index ddb7ad7..2db89d2 100644 --- a/requests_client/setup.py +++ b/requests_client/setup.py @@ -12,7 +12,7 @@ requires = [ setup( name="IzzyLib Requests Client", version='0.6.0', - packages=['izzylib.http_requests_client'], + packages=find_namespace_packages(include=['izzylib.http_requests_client']), python_requires='>=3.7.0', install_requires=requires, include_package_data=False, diff --git a/sql/setup.py b/sql/setup.py index 46a7aa6..4d39128 100644 --- a/sql/setup.py +++ b/sql/setup.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -from setuptools import setup +from setuptools import setup, find_namespace_packages requires = [ @@ -15,7 +15,7 @@ requires = [ setup( name="IzzyLib SQL", version='0.6.0', - packages=['izzylib.sql'], + packages=find_namespace_packages(include=['izzylib.sql']), python_requires='>=3.7.0', install_requires=requires, include_package_data=False, diff --git a/template/izzylib/template/__init__.py b/template/izzylib/template/__init__.py index 0855bec..14ab6c3 100644 --- a/template/izzylib/template/__init__.py +++ b/template/izzylib/template/__init__.py @@ -22,7 +22,7 @@ class Template(Environment): self.func_context = context for path in search: - self.__add_search_path(path) + self.__add_search_path(Path(path)) super().__init__( loader=ChoiceLoader([FileSystemLoader(path) for path in self.search]), @@ -46,13 +46,12 @@ class Template(Environment): 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(): - raise FileNotFoundError('Cannot find search path:', tpl_path.str()) + if path not in self.search: + self.search.append(path) - if tpl_path.str() not in self.search: - self.search.append(tpl_path.str()) def setContext(self, context): if not hasattr(context, '__call__'): diff --git a/template/setup.py b/template/setup.py index 3d22a23..9c37147 100644 --- a/template/setup.py +++ b/template/setup.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -from setuptools import setup +from setuptools import setup, find_namespace_packages requires = [ @@ -13,7 +13,7 @@ requires = [ setup( name="IzzyLib Templates", version='0.6.0', - packages=['izzylib.template'], + packages=find_namespace_packages(include=['izzylib.template']), python_requires='>=3.7.0', install_requires=requires, include_package_data=False, diff --git a/tinydb/setup.py b/tinydb/setup.py index 6368abc..c02d993 100644 --- a/tinydb/setup.py +++ b/tinydb/setup.py @@ -13,7 +13,7 @@ requires = [ setup( name="IzzyLib TinyDB", version='0.6.0', - packages=['izzylib.tinydb'], + packages=find_namespace_packages(include=['izzylib.tinydb']), python_requires='>=3.7.0', install_requires=requires, include_package_data=False,