nutools/lib/nulib/python/nulib/paths.py

206 lines
6.6 KiB
Python

# -*- coding: utf-8 -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
"""Des fonctions pour gérer les chemins.
"""
__all__ = ('in_PATH', 'matches_name',
'KNOWN_BINARY_EXTS', 'fileext_is_binary',
'dirname', 'basename', 'splitall',
'abspath', 'abspath2', 'relpath', 'ppath',
'mkdirp', 'mkdirof',
)
import os, re
from os import path
from fnmatch import fnmatch
def in_PATH(cmd):
"""Retourner True si la commande cmd est dans le PATH et est exécutable.
"""
PATH = os.environ.get('PATH', None)
if not PATH: return False
for p in PATH.split(os.pathsep):
pf = path.join(p, cmd)
if os.access(pf, os.X_OK):
return True
else:
return False
def matches_name(pattern, name, basedir=''):
"""Retourner True si name correspond au pattern pattern
name étant exprimé relativement au répertoire basedir, si pattern se
termine par '/', alors la correspondance ne se fait que si name est
effectivement un répertoire sur le système de fichier. Sinon, la
correspondance se fait sur le nom de fichier, sans tenir compte du système
de fichier.
"""
if pattern[-1:] == '/':
pf = path.join(basedir, name)
pattern = pattern[:-1]
return path.isdir(pf) and fnmatch(name, pattern)
else:
return fnmatch(name, pattern)
KNOWN_BINARY_EXTS = (
'.avi', '.bin', '.bmp', '.bpt', '.bro', '.bz2', '.class', '.com',
'.dat', '.dll', '.doc', '.eps', '.ex_', '.exe', '.fm', '.gif', '.gz',
'.hqx', '.icns', '.ico', '.ins', '.jar', '.jpeg', '.jpg', '.lib',
'.mcp', '.mov', '.mp3', '.mpg', '.objectplant', '.ofp', '.opn',
'.ovl', '.pdf', '.png', '.ppt', '.pqg', '.prj', '.ps', '.pyc', '.pyd',
'.pyo', '.rpm', '.sit', '.sl', '.snd', '.so', '.so.3', '.srpm',
'.strings', '.swf', '.tar', '.tgz', '.tbz2', '.tif', '.tiff',
'.wordbreak', '.xbm', '.xls', '.zip',
)
def fileext_is_binary(file):
"""Retourner True si l'extension du fichier est celle d'un fichier binaire
"""
_, f = path.split(file)
if f == '.DS_Store': return True
_, fe = path.splitext(f)
return fe.lower() in KNOWN_BINARY_EXTS
def dirname(file):
"""Retourner le répertoire qui contient file.
dirname('/') --> '/'
"""
return path.split(file)[0]
def basename(file, strip_ext=None):
"""Retourner le nom de base de file, en enlevant éventuellement l'extension strip_ext
"""
bn = path.split(file)[1]
if strip_ext is not None:
n, e = path.splitext(bn)
if e == strip_ext: bn = n
return bn
RE_ROOT = re.compile(r'/*$')
def splitall(file):
"""Retourner tous les composants de file (répertoires et fichier)
Par exemple:
splitall("/a/b") --> ["a", "b"]
Cas particuliers:
splitall("") --> []
splitall("/") --> []
"""
dirs = []
while RE_ROOT.match(file) is None:
dir, name = path.split(file)
dirs.insert(0, name)
file = dir
return dirs
def _join(dir, name):
return path.normpath(path.join(dir, name))
def abspath(file, basedir=None, cwd=None):
"""Retourner le chemin absolu de file.
file est exprimé par rapport au répertoire basedir, qui est exprimé par
rapport à cwd s'il est relatif.
"""
if path.isabs(file): return file
if basedir is not None:
file = _join(basedir, file)
if path.isabs(file): return file
if cwd is None: cwd = os.getcwd()
return _join(cwd, file)
def abspath2(file, basedir=None, cwd=None):
"""Retourner le chemin absolu de file.
Si file a un chemin ou s'il existe dans cwd, il est exprimé par rapport à cwd.
Si file est sans chemin et qu'il n'existe pas dans cwd, il est exprimé par
rapport à basedir.
"""
if path.isabs(file): return file
if cwd is None: cwd = os.getcwd()
cwdfile = _join(cwd, file)
if path.exists(cwdfile): return cwdfile
if basedir is not None:
dir, _ = path.split(file)
if dir == "": file = _join(basedir, file)
if path.isabs(file): return file
return _join(cwd, file)
def relpath(file, basedir=None, cwd=None):
"""Retourner le chemin relatif pour accéder à file depuis cwd.
file est exprimé par rapport au répertoire basedir.
"""
if cwd is None: cwd = os.getcwd()
cwd = path.abspath(cwd)
cwdslash = cwd + '/'
if basedir is None: basedir = cwd
if not path.isabs(file): file = path.abspath(path.join(basedir, file))
file = path.normpath(file)
if file.startswith(cwdslash):
file = file[len(cwdslash):]
elif file == cwd:
file = '.'
else:
prefix = path.commonprefix([file, cwd])
if not prefix.endswith('/'):
pos = prefix.rfind('/')
if pos != - 1: prefix = prefix[:pos + 1]
file = file[len(prefix):]
cwd = cwd[len(prefix):]
levels = len(splitall(cwd))
relpath = ''
for _ in range(levels):
relpath = path.join('..', relpath)
file = path.join(relpath, file)
return file
def ppath(file, basedir=None, cwd=None, homedir=None):
"""Retourner si possible le chemin relatif pour accéder à file depuis cwd,
homedir, ou le chemin absolu, suivant les cas exposés ci-dessous. file est
exprimé par rapport au répertoire basedir.
Le chemin retourné par cette fonction est destiné à l'affichage.
- Si file est situé dans l'arborescence à partir de cwd, alors retourner un
chemin du genre relpath/to/file.
- Si file est situé dans le répertoire $HOME, alors retourner un chemine du
genre ~/path/to/file
- Sinon, retourner le chemin absolu /path/to/file
"""
if cwd is None: cwd = os.getcwd()
cwd = path.abspath(cwd)
if basedir is None: basedir = cwd
if not path.isabs(file): file = path.abspath(path.join(basedir, file))
file = path.normpath(file)
if homedir is None: homedir = path.expanduser('~')
homedir = path.abspath(homedir)
# depuis cwd
ppath = relpath(file, cwd=cwd)
if not ppath.startswith('../'): return ppath
# depuis ~
ppath = relpath(file, cwd=homedir)
if ppath == '.': return '~'
elif not ppath.startswith('../'): return '~/' + ppath
# sinon chemin absolu
return file
def mkdirp(dir):
"""Créer si nécessaire le répertoire
@return: True si le répertoire a été créé, False sinon
"""
if not path.isdir(dir):
os.makedirs(dir)
return True
return False
def mkdirof(file):
"""Créer si nécessaire le répertoire du fichier file
@return: True si le répertoire a été créé, False sinon
"""
dir, _ = path.split(file)
return mkdirp(dir)