mirror of
https://github.com/arsenetar/dupeguru.git
synced 2024-11-10 17:59:02 +00:00
185 lines
6.0 KiB
Python
185 lines
6.0 KiB
Python
|
# Created By: Virgil Dupras
|
||
|
# Created On: 2006/02/21
|
||
|
# Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||
|
|
||
|
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||
|
# which should be included with this package. The terms are also available at
|
||
|
# http://www.hardcoded.net/licenses/bsd_license
|
||
|
|
||
|
import logging
|
||
|
import os
|
||
|
import os.path as op
|
||
|
import shutil
|
||
|
import sys
|
||
|
from itertools import takewhile
|
||
|
|
||
|
class Path(tuple):
|
||
|
"""A handy class to work with paths.
|
||
|
|
||
|
path[index] returns a string
|
||
|
path[start:stop] returns a Path
|
||
|
start and stop can be int, but the can also be path instances. When start
|
||
|
or stop are Path like in refpath[p1:p2], it is the same thing as typing
|
||
|
refpath[len(p1):-len(p2)], except that it will only slice out stuff that are
|
||
|
equal. For example, 'a/b/c/d'['a/z':'z/d'] returns 'b/c', not ''.
|
||
|
See the test units for more details.
|
||
|
|
||
|
You can use the + operator, which is the same thing as with tuples, but
|
||
|
returns a Path.
|
||
|
|
||
|
In HS applications, all paths variable should be Path instances. These Path instances should
|
||
|
be converted to str only at the last moment (when it is needed in an external function, such
|
||
|
as os.rename)
|
||
|
"""
|
||
|
# Saves a little bit of memory usage
|
||
|
__slots__ = ()
|
||
|
|
||
|
def __new__(cls, value, separator=None):
|
||
|
def unicode_if_needed(s):
|
||
|
if isinstance(s, str):
|
||
|
return s
|
||
|
else:
|
||
|
try:
|
||
|
return str(s, sys.getfilesystemencoding())
|
||
|
except UnicodeDecodeError:
|
||
|
logging.warning("Could not decode %r", s)
|
||
|
raise
|
||
|
|
||
|
if isinstance(value, Path):
|
||
|
return value
|
||
|
if not separator:
|
||
|
separator = os.sep
|
||
|
if isinstance(value, bytes):
|
||
|
value = unicode_if_needed(value)
|
||
|
if isinstance(value, str):
|
||
|
if value:
|
||
|
if (separator not in value) and ('/' in value):
|
||
|
separator = '/'
|
||
|
value = value.split(separator)
|
||
|
else:
|
||
|
value = ()
|
||
|
else:
|
||
|
if any(isinstance(x, bytes) for x in value):
|
||
|
value = [unicode_if_needed(x) for x in value]
|
||
|
#value is a tuple/list
|
||
|
if any(separator in x for x in value):
|
||
|
#We have a component with a separator in it. Let's rejoin it, and generate another path.
|
||
|
return Path(separator.join(value), separator)
|
||
|
if (len(value) > 1) and (not value[-1]):
|
||
|
value = value[:-1] #We never want a path to end with a '' (because Path() can be called with a trailing slash ending path)
|
||
|
return tuple.__new__(cls, value)
|
||
|
|
||
|
def __add__(self, other):
|
||
|
other = Path(other)
|
||
|
if other and (not other[0]):
|
||
|
other = other[1:]
|
||
|
return Path(tuple.__add__(self, other))
|
||
|
|
||
|
def __contains__(self, item):
|
||
|
if isinstance(item, Path):
|
||
|
return item[:len(self)] == self
|
||
|
else:
|
||
|
return tuple.__contains__(self, item)
|
||
|
|
||
|
def __eq__(self, other):
|
||
|
return tuple.__eq__(self, Path(other))
|
||
|
|
||
|
def __getitem__(self, key):
|
||
|
if isinstance(key, slice):
|
||
|
if isinstance(key.start, Path):
|
||
|
equal_elems = list(takewhile(lambda pair: pair[0] == pair[1], zip(self, key.start)))
|
||
|
key = slice(len(equal_elems), key.stop, key.step)
|
||
|
if isinstance(key.stop, Path):
|
||
|
equal_elems = list(takewhile(lambda pair: pair[0] == pair[1], zip(reversed(self), reversed(key.stop))))
|
||
|
stop = -len(equal_elems) if equal_elems else None
|
||
|
key = slice(key.start, stop, key.step)
|
||
|
return Path(tuple.__getitem__(self, key))
|
||
|
else:
|
||
|
return tuple.__getitem__(self, key)
|
||
|
|
||
|
def __getslice__(self, i, j): #I have to override it because tuple uses it.
|
||
|
return Path(tuple.__getslice__(self, i, j))
|
||
|
|
||
|
def __hash__(self):
|
||
|
return tuple.__hash__(self)
|
||
|
|
||
|
def __ne__(self, other):
|
||
|
return not self.__eq__(other)
|
||
|
|
||
|
def __radd__(self, other):
|
||
|
return Path(other) + self
|
||
|
|
||
|
def __str__(self):
|
||
|
if len(self) == 1:
|
||
|
first = self[0]
|
||
|
if (len(first) == 2) and (first[1] == ':'): #Windows drive letter
|
||
|
return first + '\\'
|
||
|
elif not len(first): #root directory
|
||
|
return '/'
|
||
|
return os.sep.join(self)
|
||
|
|
||
|
def has_drive_letter(self):
|
||
|
if not self:
|
||
|
return False
|
||
|
first = self[0]
|
||
|
return (len(first) == 2) and (first[1] == ':')
|
||
|
|
||
|
def remove_drive_letter(self):
|
||
|
if self.has_drive_letter():
|
||
|
return self[1:]
|
||
|
else:
|
||
|
return self
|
||
|
|
||
|
def tobytes(self):
|
||
|
return str(self).encode(sys.getfilesystemencoding())
|
||
|
|
||
|
# OS method wrappers
|
||
|
def exists(self):
|
||
|
return op.exists(str(self))
|
||
|
|
||
|
def copy(self, dest_path):
|
||
|
return shutil.copy(str(self), str(dest_path))
|
||
|
|
||
|
def copytree(self, dest_path, *args, **kwargs):
|
||
|
return shutil.copytree(str(self), str(dest_path), *args, **kwargs)
|
||
|
|
||
|
def isdir(self):
|
||
|
return op.isdir(str(self))
|
||
|
|
||
|
def isfile(self):
|
||
|
return op.isfile(str(self))
|
||
|
|
||
|
def islink(self):
|
||
|
return op.islink(str(self))
|
||
|
|
||
|
def listdir(self):
|
||
|
return os.listdir(str(self))
|
||
|
|
||
|
def mkdir(self, *args, **kwargs):
|
||
|
return os.mkdir(str(self), *args, **kwargs)
|
||
|
|
||
|
def makedirs(self, *args, **kwargs):
|
||
|
return os.makedirs(str(self), *args, **kwargs)
|
||
|
|
||
|
def move(self, dest_path):
|
||
|
return shutil.move(str(self), str(dest_path))
|
||
|
|
||
|
def open(self, *args, **kwargs):
|
||
|
return open(str(self), *args, **kwargs)
|
||
|
|
||
|
def remove(self):
|
||
|
return os.remove(str(self))
|
||
|
|
||
|
def rename(self, dest_path):
|
||
|
return os.rename(str(self), str(dest_path))
|
||
|
|
||
|
def rmdir(self):
|
||
|
return os.rmdir(str(self))
|
||
|
|
||
|
def rmtree(self):
|
||
|
return shutil.rmtree(str(self))
|
||
|
|
||
|
def stat(self):
|
||
|
return os.stat(str(self))
|
||
|
|