126 lines
4.0 KiB
Python
Executable File
126 lines
4.0 KiB
Python
Executable File
"""
|
|
Code that deals with search and classifying root directories.
|
|
"""
|
|
# This file is part of the Snakefood open source package.
|
|
# See http://furius.ca/snakefood/ for licensing details.
|
|
|
|
import os, logging
|
|
from os.path import *
|
|
from dircache import listdir
|
|
|
|
from snakefood.util import is_python, filter_separate
|
|
|
|
__all__ = ('find_roots', 'find_package_root', 'relfile',)
|
|
|
|
|
|
|
|
def find_roots(list_dirofn, ignores):
|
|
"""
|
|
Given a list of directories or filenames, find Python files and calculate
|
|
the entire list of roots.
|
|
"""
|
|
inroots = set()
|
|
for fn in map(realpath, list_dirofn):
|
|
|
|
# Search up the directory tree for a root.
|
|
root = find_package_root(fn, ignores)
|
|
if root:
|
|
inroots.add(root)
|
|
elif isfile(fn):
|
|
inroots.add(dirname(fn))
|
|
else:
|
|
assert isdir(fn)
|
|
|
|
# If the given file is not sitting within a root, search below the
|
|
# directory tree for available roots.
|
|
downroots = search_for_roots(fn, ignores)
|
|
if downroots:
|
|
inroots.update(downroots)
|
|
else:
|
|
logging.warning("Directory '%s' does live or include any roots." % fn)
|
|
return sorted(inroots)
|
|
|
|
def find_package_root(fn, ignores):
|
|
"Search up the directory tree for a package root."
|
|
if not isdir(fn):
|
|
fn = dirname(fn)
|
|
while is_package_dir(fn):
|
|
assert fn
|
|
fn = dirname(fn)
|
|
if fn and is_package_root(fn, ignores):
|
|
return fn
|
|
|
|
def search_for_roots(dn, ignores):
|
|
"""Search below the directory tree for package roots. The recursive search
|
|
does not move inside the package root when one is found."""
|
|
if not isdir(dn):
|
|
dn = dirname(dn)
|
|
roots = []
|
|
for root, dirs, files in os.walk(dn):
|
|
for d in list(dirs):
|
|
if d in ignores:
|
|
dirs.remove(d)
|
|
if is_package_root(root, ignores):
|
|
roots.append(root)
|
|
dirs[:] = []
|
|
return roots
|
|
|
|
def is_package_dir(dn):
|
|
"""Return true if this is a directory within a package."""
|
|
return exists(join(dn, '__init__.py'))
|
|
|
|
|
|
def is_package_root(dn, ignores):
|
|
"""Return true if this is a package root. A package root is a directory
|
|
that could be used as a PYTHONPATH entry."""
|
|
|
|
if not exists(dn) or exists(join(dn, '__init__.py')):
|
|
return False
|
|
|
|
else:
|
|
dirfiles = (join(dn, x) for x in listdir(dn))
|
|
subdirs, files = filter_separate(isdir, dirfiles)
|
|
|
|
# Check if the directory contains Python files.
|
|
pyfiles = []
|
|
for x in files:
|
|
bx = basename(x)
|
|
if bx in ignores:
|
|
continue
|
|
if bx.endswith('.so') or is_python(x):
|
|
pyfiles.append(bx)
|
|
|
|
# Note: we need to check for a 'site-packages' subdirectory because some
|
|
# distributions package arch-specific files in a different place and
|
|
# have no .py files in /usr/lib/pythonVER, but a single 'config' or
|
|
# 'site-packages' directory instead. These aren't packages either.
|
|
if join(dn, 'site-packages') in subdirs:
|
|
return True
|
|
|
|
# Check if the directory contains Python packages.
|
|
for sub in subdirs:
|
|
bsub = basename(sub)
|
|
# Note: Make use of the fact that dotted directory names cannot be
|
|
# imported as packages for culling away branches by removing those
|
|
# subdirectories that have dots in them.
|
|
if '.' in bsub or bsub in ignores:
|
|
continue
|
|
if exists(join(sub, '__init__.py')):
|
|
return True
|
|
|
|
return False
|
|
|
|
def relfile(fn, ignores):
|
|
"Return pairs of (package root, relative filename)."
|
|
root = find_package_root(realpath(fn), ignores)
|
|
if root is None:
|
|
root = dirname(fn)
|
|
rlen = basename(fn)
|
|
else:
|
|
rlen = fn[len(root)+1:]
|
|
|
|
assert root is not None and rlen is not None
|
|
return root, rlen
|
|
|
|
|