multiple fixes and re-subclass path to str
This commit is contained in:
parent
1ea42aeca9
commit
b728df4bc0
|
@ -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
|
||||||
|
|
|
@ -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])):
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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))
|
||||||
|
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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__'):
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue