366 lines
11 KiB
Python
366 lines
11 KiB
Python
"""
|
|
Implementation of a rudimentary file system
|
|
|
|
TODO:
|
|
grab the
|
|
"""
|
|
|
|
import os, sys, shutil, pathlib, re
|
|
from dataclasses import dataclass
|
|
|
|
import filetype as ft
|
|
from send2trash import send2trash
|
|
|
|
from sl4ng import show, delevel, gather, nameSpacer, ffplay, commons, nopes
|
|
# from magnitudes import rep
|
|
from .magnitudes import represent# as rp
|
|
# from magnitudes import *
|
|
|
|
forbiddenChars = r'\/:?*<>|"'
|
|
|
|
formats = {
|
|
'pics':['bmp','png','jpg','jpeg','tiff',],
|
|
'music':['mp3','m4a','wav','ogg','wma','flac','aiff','alac',],
|
|
'videos':['mp4','wmv',],
|
|
'docs':['doc','docx','pdf','xlsx','pptx','ppt','xls','csv',],
|
|
}
|
|
|
|
orders = {
|
|
1:'deca',
|
|
2:'hecto',
|
|
3:'kilo',
|
|
6:'mega',
|
|
9:'giga',
|
|
12:'tera',
|
|
15:'peta',
|
|
18:'exa',
|
|
21:'zetta',
|
|
24:'yotta'
|
|
}
|
|
|
|
def trim(path,edge=os.sep):
|
|
out = path[:]
|
|
while out.startswith(edge):
|
|
out = out[1:]
|
|
while out.endswith(edge):
|
|
out = out[:-1]
|
|
return out
|
|
|
|
|
|
# @dataclass
|
|
# class size:
|
|
# val:int
|
|
class size(int):
|
|
# def __repr__(self):
|
|
# rep = round(self*10**-3)
|
|
# if len(st)
|
|
# return f'{round(self*10**-3):,} kb'
|
|
def __repr__(self):
|
|
return rp(self)
|
|
# def __str__(self):
|
|
# return str(self.val)
|
|
# def __add__(self,other):
|
|
# return self.val + size(other)
|
|
# def __truediv__(self,other):
|
|
# return self.val + size(other)
|
|
# def __sub__(self,other):
|
|
# return self.val + size(other)
|
|
# def __mul__(self,other):
|
|
# return self.val + size(other)
|
|
# pass
|
|
# def __str__(self):
|
|
# return str(int(self))
|
|
# def __repr__(self,dim='bytes',long=False,lower=False,precision=2,sep='-'):
|
|
# orders = {
|
|
# 3:'kilo',
|
|
# 6:'mega',
|
|
# 9:'giga',
|
|
# 12:'tera',
|
|
# 15:'peta',
|
|
# 18:'exa',
|
|
# 21:'zetta',
|
|
# 24:'yotta',
|
|
# }
|
|
|
|
# sredro = {v:k for k,v in orders.items()}
|
|
|
|
# pretty = lambda number,unit='': f'{number:,} {unit}'.strip()
|
|
# setcase = lambda unit,lower=False: [unit.upper().strip(),unit.lower().strip()][lower]
|
|
# setlength = lambda mag,dim,long=False,sep='-': ('',sep)[long].join(((mag[0],dim[0]),(mag,dim))[long])
|
|
|
|
|
|
# mags = tuple(sorted(orders.keys()))
|
|
# booly = lambda i: len(str(int(self))) < len(str(10**mags[i+1]))
|
|
# fits = tuple(nopes((booly(i) for i in range(len(mags)-1)),True))
|
|
# fits = tuple(filter(booly,range(len(mags)-1)))
|
|
# mag = orders[mags[min(fits) if fits else len(mags)-1]]
|
|
# unit = setcase(setlength(mag,dim,long,['',sep][long]),lower)
|
|
# number = round(self*10**-sredro[mag],precision)
|
|
# return pretty(number,unit).lower() if lower else pretty(number,unit).upper()
|
|
@dataclass
|
|
class _pathLike:
|
|
path:str
|
|
|
|
def __str__(self):
|
|
return self.path
|
|
def __repr__(self):
|
|
return str(self)
|
|
def __hash__(self):
|
|
return hash(str(self))
|
|
|
|
@property
|
|
def exists(self):
|
|
return os.path.exists(self.path)
|
|
@property
|
|
def up(self):
|
|
return address(delevel(str(self))).obj
|
|
@property
|
|
def name(self):
|
|
return os.path.split(self.path)[1]
|
|
@property
|
|
def title(self):
|
|
return os.path.splitext(self.name)[0]
|
|
@property
|
|
def ancestors(self):
|
|
level = []
|
|
p = self.path[:]
|
|
while p != delevel(p):
|
|
p = delevel(p)
|
|
level.append(p)
|
|
return tuple(address(i).obj for i in level)[::-1]
|
|
@property
|
|
def siblings(self):
|
|
# return tuple(i for i in self.delevel() if isinstance(i,type(self)))
|
|
return tuple(i for i in self.up if isinstance(i,type(self)))
|
|
@property
|
|
def depth(self):
|
|
return len(self.ancestors)
|
|
@property
|
|
def root(self):
|
|
return self.ancestors[0]#.split(':')[0]
|
|
@property
|
|
def peers(self):
|
|
return self.delevel().content
|
|
@property
|
|
def stat(self):
|
|
"""
|
|
return os.stat(str(self))
|
|
"""
|
|
return os.stat(str(self))
|
|
|
|
def delevel(self,steps=1,path=False):
|
|
return delevel(self.path,steps) if path else directory(delevel(self.path,steps))
|
|
def heritage(self):
|
|
print(f'\nheritage({self.name.title()})')
|
|
ancs = list(self.ancestors)
|
|
ancs.append(self.path)
|
|
for i,anc in enumerate(ancs):
|
|
print('\t'+('','.'*i)[i>0]+i*' '+[i for i in str(anc).split(os.sep) if i][-1])
|
|
|
|
def touch(self):
|
|
p = str(self)
|
|
pathlib.Path(p).touch()
|
|
self = address(p).obj
|
|
return self
|
|
def delete(self,recycle=True):
|
|
send2trash(self.path) if recycle else os.remove(self.path)
|
|
del self
|
|
def rename(self,new,relative=False):
|
|
new = new if not relative else os.path.join(delevel(self.path),new)
|
|
os.makedirs(delevel(new),exist_ok=True)
|
|
os.rename(self.path,new)
|
|
self = address(new).obj
|
|
return self
|
|
def clone(self,new=None,relative=False,touch=False):
|
|
if new:
|
|
if relative:
|
|
new = nameSpacer(os.path.join(delevel(self.path),new))
|
|
else:
|
|
new = nameSpacer(os.path.join(delevel(self.path),self.name))
|
|
os.makedirs(delevel(new),exist_ok=True)
|
|
shutil.copy2(self.path,new) #if touch else shutil.copy2(self.path,new)
|
|
out = address(new).obj
|
|
return out.touch() if touch else out
|
|
|
|
move = rename
|
|
copy = clone
|
|
|
|
|
|
@dataclass
|
|
class address(_pathLike):
|
|
"""
|
|
A systemic pointer to the location of the data associated with a file system object
|
|
"""
|
|
def __init__(self,path:str):
|
|
super(address,self).__init__(path)
|
|
# assert os.path.exists(path), f'"{path}" is not a valid address on this system'
|
|
assert self.exists, f'"{path}" is not a valid address on this system'
|
|
|
|
|
|
@property
|
|
def isdir(self):
|
|
return os.path.isdir(self.path)
|
|
@property
|
|
def isfile(self):
|
|
return os.path.isfile(self.path)
|
|
@property
|
|
def obj(self):
|
|
if self.isdir:
|
|
return directory(self.path)
|
|
elif self.isfile:
|
|
return file(self.path)
|
|
|
|
def expose(self):
|
|
os.startfile(self.path)
|
|
return self
|
|
|
|
|
|
# class directory(_pathLike):
|
|
class directory(address):
|
|
def __init__(self,path:str):
|
|
path = os.path.abspath(trim(path))
|
|
assert address(path)#.isdir, f'"{path}" is not a directory'
|
|
super(directory,self).__init__(path)
|
|
self._ind = -1
|
|
# self.
|
|
def __bool__(self):
|
|
return len(os.listdir(self.path))>0
|
|
def __len__(self):
|
|
return len(self.content)
|
|
def __iter__(self):
|
|
return self
|
|
def __next__(self):
|
|
if self._ind<len(self)-1:
|
|
self._ind += 1
|
|
return self.content[self._ind]
|
|
self._ind = -1
|
|
raise StopIteration
|
|
def __getitem__(self,item):
|
|
if any(re.search(f'^{item}$',i.name,re.I) for i in self.content):
|
|
# for i in nopes((re.search(f'^{item}$',i.name,re.I) for i in self.content),True):
|
|
return address(os.path.join(self.path,item)).obj
|
|
raise ValueError(f'The folder "{self.name}" does not contain anything called "{item}"')
|
|
|
|
@property
|
|
def children(self):
|
|
return tuple(addy.obj for i in os.listdir(self.path) if (addy:=address(os.path.join(str(self),i))).isdir)
|
|
@property
|
|
def files(self):
|
|
return tuple(addy.obj for i in os.listdir(self.path) if (addy:=address(os.path.join(self.path,i))).isfile)
|
|
@property
|
|
def content(self):
|
|
return tuple(address(os.path.join(self.path,i)).obj for i in os.listdir(self.path))
|
|
@property
|
|
def leaves(self):
|
|
return tuple(self.gather())
|
|
@property
|
|
def branches(self):
|
|
return tuple(set(i.delevel() for i in self.gather()))
|
|
@property
|
|
def size(self):
|
|
return size(sum(file.size for file in self.leaves))
|
|
@property
|
|
def mime(self):
|
|
return tuple(set(file.mime for file in self.gather()))
|
|
@property
|
|
def kind(self):
|
|
return tuple(set(m.split('/')[0] for m in self.mime))
|
|
@property
|
|
def ext(self):
|
|
return tuple(set(f.ext for f in self.gather()))
|
|
@property
|
|
def isroot(self):
|
|
return not self.depth
|
|
|
|
def enter(self):
|
|
os.chdir(self.path)
|
|
def gather(self,names:bool=False,walk:bool=True,ext:str=None,paths=False):
|
|
if paths:
|
|
yield from gather(str(self),names,walk,ext)
|
|
else:
|
|
yield from set(file(path) for path in gather(str(self),names,walk,ext))
|
|
# for path in gather(str(self),names,walk,ext):
|
|
# yield file(path)
|
|
def add(self,new):
|
|
if (obj:=address(new).obj):
|
|
return obj.move(self)
|
|
else:
|
|
raise ValueError(f'"{new}" does is neither a file or directory')
|
|
|
|
def moveContent(self,other):
|
|
# assert address(trim(str(other))).isdir, f'"{other}" is not a viable directory here'
|
|
assert address((str(other))).isdir, f'"{other}" is not a viable directory here'
|
|
for f in self.gather():
|
|
# rest = f.path[len(self.path)+1:]
|
|
ending = trim(f.path[len(self.path)+1:])
|
|
new = os.path.join(trim(str(other)),ending)
|
|
# os.makedirs(delevel(new))
|
|
print(self)
|
|
print(f)
|
|
print(ending)
|
|
print(new)
|
|
print()
|
|
def show(self, indentation:int=1, enum:bool=False, start:int=1, indentor:str='\t'):
|
|
assert indentation>0, f'"{indentation}" is not a viable indentation level'
|
|
print((indentation-1)*'\t'+self.name)
|
|
show(self.content,indentation,enum,start,indentor)
|
|
|
|
extension = ext
|
|
|
|
|
|
class file(_pathLike):
|
|
def __init__(self,path:str):
|
|
path = os.path.abspath(trim(path))
|
|
assert address(path)#, f'"{path}" is not a file'
|
|
super(file,self).__init__(path)
|
|
self._stream = None
|
|
def __enter__(self):
|
|
self._stream = open(str(self),'b')
|
|
return self
|
|
def __exit__(self):
|
|
self._stream.close()
|
|
self._stream = None
|
|
|
|
@property
|
|
def size(self):
|
|
return size(os.stat(str(self)).st_size)
|
|
@property
|
|
def mime(self):
|
|
return match.MIME if (match:=ft.guess(str(self))) else 'UNKNOWN'
|
|
@property
|
|
def kind(self):
|
|
return self.mime.split('/')[0] if (match:=ft.guess(str(self))) else 'UNKNOWN'
|
|
@property
|
|
def ext(self):
|
|
return os.path.splitext(self.name)[1]
|
|
|
|
extension = ext
|
|
|
|
|
|
user = directory(commons['home'])
|
|
root = user.up.up
|
|
|
|
if __name__ == '__main__':
|
|
fp = r'c:\users\kenneth\pyo_rec.wav'
|
|
dp = r'c:\users\kenneth\videos'
|
|
|
|
# fp = r'C:\Users\Kenneth\Music\Collection\slowthai\The Bottom _ North Nights\02 North Nights.mp3'#[:-1]
|
|
# dp = delevel(fp,1)#[:-1]
|
|
|
|
# d = address(dp).obj
|
|
# f = address(fp).obj
|
|
|
|
d = directory(dp)
|
|
f = file(fp)
|
|
|
|
system = (d,f)
|
|
[print(i) for i in system]
|
|
[print(i.size) for i in system]
|
|
print()
|
|
# [show(i.delevel()) for i in system]
|
|
# [show(i.delevel()) for i in system]
|
|
|
|
print(forbiddenChars)
|
|
|
|
# with f as fob:
|
|
# print(fob) |