Skip to content

Instantly share code, notes, and snippets.

@davtoh
Created August 26, 2015 16:37
Show Gist options
  • Select an option

  • Save davtoh/c6fd27541ac98d1f2b90 to your computer and use it in GitHub Desktop.

Select an option

Save davtoh/c6fd27541ac98d1f2b90 to your computer and use it in GitHub Desktop.
directory class for mutable string
__author__ = 'Davtoh'
import os,inspect,functools
import shutil
def getPath(filename=__file__):
"""get standard path from filename"""
body,base = os.path.splitext(filename)
if base != "":
body = os.path.dirname(body)
return os.path.abspath(body)
def mkPath(filepath):
"""make path for filename"""
path = getPath(filepath)
if not os.path.exists(path): os.makedirs(path)
return path
def rmPath(filepath):
"""remove path from filename"""
path = getPath(filepath)
shutil.rmtree(path)
return True
def getPathIndicator(path,pattern='/\\'):
"""
obtain path indicator
:param path: relative or absolute path (str)
:param pattern: guess characters to compare path (str)
:return: indicator (str)
"""
i = len(path)
while i and path[i-1] not in pattern: i -= 1
head, tail = path[:i], path[i:] # now tail has no slashes
# remove trailing slashes from head, unless it's all slashes
head2 = head
count = 0
while head2 and head2[-1] in pattern:
head2 = head2[:-1]
count += 1
character = head[len(head)-count:len(head)] # get the slash character
return character
def getShortenedPath(path,comp):
"""
path is controlled to give absolute path from relative path or integer
:param path: absolute path (str)
:param comp: pattern or relative path (str) or integer representing level of folder
determined by the separator Ex. C://level 1//level 2//...//level N or -1
:return: path before matched comp Ex: C://level 1//"comp" --> C://level 1
"""
if type(comp) is str:
if os.path.isabs(comp):
comp = os.path.relpath(path,comp)
return path.split(comp)[0]
else:
indicator = getPathIndicator(path)
return indicator.join(path.split(indicator)[:comp])
def decoratePath(relative,indicator=os.path.sep):
"""
decorated path is controlled to give absolute path from relative path
:param relative: int or path
:param indicator:
:return:
"""
def decorator(f):
@functools.wraps(f)
def wrapper(*args,**kwargs):
path = f(*args,**kwargs)
return indicator.join(getShortenedPath(path,relative).split(getPathIndicator(path)))
return wrapper
return decorator
def correctPath(relative,filename=__file__):
"""
get path corrected from its relative path or level index
:param relative: pattern or level in directory
:param filename: path or file name
:return: corrected path
"""
return decoratePath(relative)(getPath)(filename)
def joinPath(absolute,relative):
"""
joins an absolute path to a relative path => str
"""
return os.path.join(str(absolute), str(relative)) # ensures updated version is processed
def repr2list(data,level=0):
"""
converts the representation of a directory.repr to pickleable
:param data: directory.repr of the form ["string",directory,...,directory.repr]
:return: pickeable list
"""
if isinstance(data,list): # list defines levels of directories
if level == 0: # [level 0, ..., [level 1, [...[level N]...]], level 0]
for i,value in enumerate(data): # process several objects in the list
data[i] = repr2list(value,level+1)
return data
else: # convert anything to directory if not in level 0
return directory(data)
elif isinstance(data,str): # if string or directory
return data
else: # try to convert to directory
return directory(data)
def repr2str(data, ispath = True):
"""
converts the representation of a directory.repr to string
:param data: directory.repr of the form ["string",directory,...,directory.repr]
:return: converted string
"""
if isinstance(data,list):
if len(data)>1:
string = str(repr2str(data[0]))
for i in data[1:]:
if ispath:
string = joinPath(string,repr2str(i)) # join paths
else:
string += repr2str(i, ispath)
return string
else:
return str(data[0]) # get single path
elif isinstance(data, directory):
return repr2str(data.repr, data.ispath)
else:
return str(data) # object must be string
def correctSTRBuiltin(self):
"""
decorate all the built-in functions of class directory
:return: built-in decorated function
"""
def decorator(f):
@functools.wraps(f)
def wrapper(*args,**kwargs):
return getattr(str(self),f.__name__,f.__name__)(*args,**kwargs)
return wrapper
return decorator
class directory(str):
#repr = [""]
def __new__(cls, data, ispath = None, copy = False, **kwargs):
# data can be list, str, directory or dictionary
data = directory.filterdata(data,ispath,kwargs)
self = str.__new__(cls, repr2str(data["repr"],data["ispath"]))
if copy:
self.__dict__.update(data)
else:
self.__dict__ = data
return self
@staticmethod
def filterdata(data, ispath = None, kwargs=None):
if isinstance(data,directory):
data = data.__dict__
elif not isinstance(data,dict):
if isinstance(data,list):
data = {"repr":data}
else:
data = {"repr":[data]}
if ispath: data["ispath"]= ispath
if not data.has_key("ispath"): data["ispath"] = True # repr default
if not data.has_key("repr"):
data["repr"] = [""] # repr default
else:
data["repr"] = repr2list(data["repr"]) # ensure repr is maintained
if kwargs: data.update(kwargs) # update data with kwargs
return data
def __add__(self, other):
if type(other)==str: # it's exactly str an not directory
return joinPath(self,other)
return self.update_right(other)
def __sub__(self, other):
if type(other)==str: # it's exactly str an not directory
return joinPath(other,self)
return self.update_left(other)
def update_right(self, other):
self.repr.append(other) # it will be parsed at directory creation
return directory(self) # string is immutable and must be renewed
def update_left(self, other):
self.repr.insert(0,other) # it will be parsed at directory creation
return directory(self) # string is immutable and must be renewed
def update(self, data = None):
if isinstance(data,list): # if list
if len(data)>len(self.repr):
for i in xrange(len(self.repr)):
self.repr[i] = data[i]
for j in xrange(i+1,len(data)):
self.repr.append(data[j])
else:
for i in xrange(len(data)):
self.repr[i] = data[i]
del self.repr[i+1:]
return directory(self) # string is immutable and must be renewed
elif isinstance(data,dict): # if dictionary
self.__dict__.update(data)
return directory(self)
elif data: # if not list or dict
return self.update([data])
else: # if None return updated version
return directory(self)
def __str__(self):
return repr2str(self.repr, self.ispath)
def __repr__(self):
return str(self.repr)
# TODO: implement 2 versions: one with directory-like functionality and other basic for str support
"""
### MAGIC FUNCTIONS
def __nonzero__(self):
return self == self.update()
def __getitem__(self, item):
return self.repr[item]
def __len__(self):
return len(self.repr)"""
if __name__=="__main__":
import session as sn
import dill
## TESTS
a = directory("string1",sapo = "mamo")
b = a.update(["string2",["string4","string 5"]])
result = bool(a) # False
result = bool(b) # True
c = directory(["string3"])-a
sn.saveSession("test.pkl",{"d",a})
c = sn.readSession("test.pkl")
print type(a)==directory
print type(a) is directory
print a is directory
print a == directory
print "with str"
print type(a)==str
print type(a) is str
print a is str
print a == str
print isinstance(a,str)
print type(a)
print os.path.splitext(os.path.basename(__file__)) ### look here ###
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment