Last active
December 28, 2015 00:09
-
-
Save tkfm-yamaguchi/7411336 to your computer and use it in GitHub Desktop.
ruby like pathname class for python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# coding: utf-8 | |
import os | |
from os import path | |
import re | |
import shutil | |
from itertools import chain | |
DEFAULT_SEPARATOR = "/" | |
def unixize_separator(target): | |
""" | |
Use this to the result of path.normpath and path.join | |
""" | |
if os.sep == DEFAULT_SEPARATOR: return target | |
return target.replace(os.sep, DEFAULT_SEPARATOR) | |
class Pathname(object): | |
""" | |
Pathname represents the name of a file or directory on the filesystem, | |
but basically not the file itself. | |
""" | |
def __init__(self, _path): | |
if type(_path) == type(self): | |
self._path = _path.toString() | |
else: | |
self._path = unixize_separator(path.normpath(str(_path))) | |
def __str__(self): | |
return self._path | |
def __unicode__(self): | |
return self._path.encode() | |
def toString(self): | |
return str(self) | |
def to_s(self): | |
return self.toString() | |
## Properties whose type is Boolean {{{ | |
def exists(self): | |
return path.exists(self._path) | |
def islink(self): | |
return path.islink(self._path) | |
def isfile(self): | |
if self.islink(): | |
return path.isfile(str(self.realpath())) | |
else: | |
return path.isfile(self._path) | |
def isdir(self): | |
if self.islink(): | |
return path.isfile(str(self.realpath())) | |
else: | |
return path.isdir(self._path) | |
# }}} | |
## Properties which returns Pathname instance {{{ | |
def dirname(self): | |
return Pathname(path.dirname(self._path)) | |
def basename(self): | |
return Pathname(path.basename(self._path)) | |
def extname(self): | |
return Pathname(path.splitext(self._path)[1]) | |
def realpath(self): | |
return Pathname(path.realpath(self._path)) | |
def children(self): | |
return map(lambda p: self.join(p), chain.from_iterable(os.walk(self._path).next()[1:])) | |
# }}} | |
## Path operations with antoher path(es) {{{ | |
def relative_path_from(self, start): | |
self_abs = path.abspath(str(self._path)) | |
start_abs = path.abspath(str(start)) | |
if self_abs == start_abs: | |
return Pathname(".") | |
else: | |
return Pathname(path.relpath(self_abs, start_abs)) | |
def join(self, *descendants): | |
""" | |
join the pathnames. | |
NOTE: | |
use slash regardless of OS's default separator. | |
This is intended to use the path in the system | |
which is according to UNIX like hierarchy separator | |
(i.e. URL, etc.). | |
It should be noticed that this is different behavior | |
from python's default(os.path.join) who uses the | |
separator depending on the OS's default | |
(i.e. backslash on windows, slash on *nix) | |
""" | |
joined = self._path | |
for descendant in descendants: | |
joined = os.path.join(joined, str(descendant)) | |
return Pathname(unixize_separator(joined)) | |
def walk(self, topdown=True, onerror=None, followlinks=False): | |
for root, dirnames, filenames in os.walk(self._path, topdown, onerror, followlinks): | |
yield root, dirnames, filenames | |
def swap_extname(self, new_ext): | |
if not self.isfile(): | |
raise Exception, "path should be file for the #swap_extname: {0}".format(self) | |
s_bname = self.basename().to_s() | |
s_extname = re.escape(self.extname().to_s()) | |
return self.dirname().join( re.sub(s_extname, new_ext, s_bname) ) | |
# }}} | |
## File system operations, means cp, mkdir ... etc. {{{ | |
def make_symlink(self, old, relative=False): | |
_old = old | |
if relative: | |
_old = Pathname(old).relative_path_from(self.dirname()) | |
return os.symlink(str(_old), str(self._path)) | |
def cp(self, dest): | |
_dest = Pathname(dest) | |
if self.isfile(): | |
if _dest.isdir(): | |
return shutil.copy2(self._path, _dest.toString()) | |
else: | |
return shutil.copyfile(self._path, _dest.toString()) | |
elif self.isdir(): | |
if _dest.isdir(): | |
# copytree throws [Errno 17] if self.basename() and | |
# _dest.basename() were the same. | |
# THAT IS WIRED. In order to the common sense, | |
# copy command (cp) copies the source directory itself | |
# into the target directory in such case. | |
# And what the worse thing, python has no method which | |
# copy the directory to another directory. | |
# How should we deal with the empty directory? | |
# They say "use mkdir" ? Sucks. | |
def copy_recursively_manually(src_base, dest_base=_dest): | |
_dest_base = dest_base.join(src_base.basename()) | |
_dest_base.mkdir() | |
for src_child in src_base.children(): | |
_dest_child = _dest_base.join(src_child.basename()) | |
if src_child.isfile(): | |
src_child.cp(_dest_base) | |
else: | |
copy_recursively_manually(src_child, _dest_base) | |
return copy_recursively_manually(self) | |
else: | |
raise Exception, "Invalid copy operation: dir to file: '{0}' to '{1}'".format(self._path, _dest.toString()) | |
else: | |
raise Exception, "#cp was called for the unkown file type; source:{0}, destination:{1}".format(self._path, _dest.toString()) | |
def mkdir(self): | |
if not self.exists(): | |
os.mkdir(self._path) | |
return self | |
def mkdir_p(self): | |
if not self.exists(): | |
os.makedirs(self._path) | |
return self | |
def delete(self): | |
if self.isfile() or self.islink(): | |
return os.unlink(self._path) | |
elif self.isdir(): | |
return os.rmdir(self._path) | |
else: | |
raise Exception, "Unkown type: {0}".format(self._path) | |
# }}} | |
## Utilities which assumes the path is text file {{{ | |
def read(self): | |
if not self.isfile(): | |
raise Exception, "#read should be called for file but '{0}' looks directory or something".format(self) | |
return open(str(self), "r").read().decode("utf8") | |
def write(self, text, mode="w", overwrite=False, encode="utf_8"): | |
if self.exists() and not overwrite: | |
raise Exception, "#write was called but file was exists: '{0}'".format(self) | |
with open(str(self), mode) as f: | |
return f.write(text.encode(encode)) | |
# }}} | |
# vim: fdm=marker commentstring=#\ %s |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment