238 lines
7.1 KiB
Python
238 lines
7.1 KiB
Python
|
"""
|
||
|
A path to python objects located in filesystems.
|
||
|
|
||
|
Note: this is still experimental and may be removed
|
||
|
for the first stable release!
|
||
|
"""
|
||
|
from __future__ import generators
|
||
|
import py
|
||
|
from py.__.path import common
|
||
|
import sys
|
||
|
import inspect
|
||
|
moduletype = type(py)
|
||
|
|
||
|
class Extpy(common.PathBase):
|
||
|
""" path object for addressing python objects. """
|
||
|
sep = '.'
|
||
|
def __new__(cls, root, modpath=''):
|
||
|
if not isinstance(modpath, str):
|
||
|
raise TypeError("second 'modpath' argument must be a dotted name.")
|
||
|
if isinstance(root, str):
|
||
|
root = py.path.local(root)
|
||
|
|
||
|
self = object.__new__(cls)
|
||
|
if isinstance(root, Extpy):
|
||
|
# we don't want it nested, do we?
|
||
|
assert not modpath
|
||
|
root = root.root
|
||
|
self.modpath = modpath
|
||
|
self.root = root
|
||
|
return self
|
||
|
|
||
|
def __hash__(self):
|
||
|
return hash((self.root, self.modpath))
|
||
|
|
||
|
def __repr__(self):
|
||
|
return 'extpy(%r, %r)' % (self.root, self.modpath)
|
||
|
|
||
|
def __str__(self):
|
||
|
return "%s%s.%s" %(self.root, self.root.sep, self.modpath)
|
||
|
|
||
|
def join(self, *args):
|
||
|
for arg in args:
|
||
|
if not isinstance(arg, str):
|
||
|
raise TypeError, "non-strings not allowed in %r" % args
|
||
|
modpath = [x.strip('.') for x in ((self.modpath,)+args) if x]
|
||
|
modpath = self.sep.join(modpath)
|
||
|
return self.__class__(self.root, modpath)
|
||
|
|
||
|
def relto(self, other):
|
||
|
if self.root != other.root:
|
||
|
return ''
|
||
|
return super(Extpy, self).relto(other)
|
||
|
|
||
|
def dirpath(self, *args):
|
||
|
modpath = self.modpath.split(self.sep) [:-1]
|
||
|
modpath = self.sep.join(modpath+list(args))
|
||
|
return self.__class__(self.root, modpath)
|
||
|
|
||
|
def new(self, **kw):
|
||
|
""" create a modified version of this path.
|
||
|
the following keyword arguments modify various path parts:
|
||
|
modpath substitute module path
|
||
|
"""
|
||
|
cls = self.__class__
|
||
|
if 'modpath' in kw:
|
||
|
return cls(self.root, kw['modpath'])
|
||
|
if 'basename' in kw:
|
||
|
i = self.modpath.rfind('.')
|
||
|
if i != -1:
|
||
|
return cls(self.root, self.modpath[i+1:] + kw['basename'])
|
||
|
else:
|
||
|
return cls(self.root, kw['basename'])
|
||
|
return cls(self.root, self.modpath)
|
||
|
|
||
|
def _getbyspec(self, spec):
|
||
|
l = []
|
||
|
modparts = self.modpath.split(self.sep)
|
||
|
for name in spec.split(','):
|
||
|
if name == 'basename':
|
||
|
l.append(modparts[-1])
|
||
|
return l
|
||
|
|
||
|
def resolve(self):
|
||
|
"""return the python object, obtained from traversing from
|
||
|
the root along the modpath.
|
||
|
"""
|
||
|
rest = filter(None, self.modpath.split('.'))
|
||
|
target = self.getpymodule()
|
||
|
for name in rest:
|
||
|
try:
|
||
|
target = getattr(target, name)
|
||
|
except AttributeError:
|
||
|
raise py.error.ENOENT(target, name)
|
||
|
return target
|
||
|
|
||
|
def getpymodule(self):
|
||
|
if hasattr(self.root, 'resolve'):
|
||
|
return self.root.resolve()
|
||
|
else:
|
||
|
return self.root.getpymodule()
|
||
|
|
||
|
def listobj(self, fil=None, **kw):
|
||
|
l = []
|
||
|
for x in self.listdir(fil, **kw):
|
||
|
l.append(x.resolve())
|
||
|
return l
|
||
|
|
||
|
def listdir(self, fil=None, sort=True, **kw):
|
||
|
if kw:
|
||
|
if fil is None:
|
||
|
fil = lambda x: x.check(**kw)
|
||
|
else:
|
||
|
raise TypeError, "cannot take filter and keyword arguments"
|
||
|
elif isinstance(fil, str):
|
||
|
fil = common.fnmatch(fil)
|
||
|
obj = self.resolve()
|
||
|
l = []
|
||
|
#print "listdir on", self
|
||
|
if not hasattr(obj, '__dict__'):
|
||
|
raise py.error.ENOTDIR(self, "does not have a __dict__ attribute")
|
||
|
for name in dir(obj):
|
||
|
sub = self.join(name)
|
||
|
if not fil or fil(sub):
|
||
|
l.append(sub)
|
||
|
|
||
|
#print "listdir(%r) -> %r" %(self, l)
|
||
|
#print "listdir on", repr(self)
|
||
|
return l
|
||
|
|
||
|
def getfilelineno(self, scrapinit=0):
|
||
|
x = obj = self.resolve()
|
||
|
if inspect.ismodule(obj):
|
||
|
return obj.__file__, 0
|
||
|
if inspect.ismethod(obj):
|
||
|
obj = obj.im_func
|
||
|
if inspect.isfunction(obj):
|
||
|
obj = obj.func_code
|
||
|
if inspect.iscode(obj):
|
||
|
return py.path.local(obj.co_filename), obj.co_firstlineno - 1
|
||
|
else:
|
||
|
source, lineno = inspect.findsource(obj)
|
||
|
return x.getfile(), lineno - 1
|
||
|
|
||
|
def visit(self, fil=None, rec=None, ignore=None, seen=None):
|
||
|
def myrec(p, seen={id(self): True}):
|
||
|
if id(p) in seen:
|
||
|
return False
|
||
|
seen[id(p)] = True
|
||
|
if self.samefile(p):
|
||
|
return True
|
||
|
|
||
|
for x in super(Extpy, self).visit(fil=fil, rec=rec, ignore=ignore):
|
||
|
yield x
|
||
|
return
|
||
|
|
||
|
if seen is None:
|
||
|
seen = {id(self): True}
|
||
|
|
||
|
if isinstance(fil, str):
|
||
|
fil = common.fnmatch(fil)
|
||
|
if isinstance(rec, str):
|
||
|
rec = common.fnmatch(fil)
|
||
|
|
||
|
if ignore:
|
||
|
try:
|
||
|
l = self.listdir()
|
||
|
except ignore:
|
||
|
return
|
||
|
else:
|
||
|
l = self.listdir()
|
||
|
reclist = []
|
||
|
for p in l:
|
||
|
if fil is None or fil(p):
|
||
|
yield p
|
||
|
if id(p) not in seen:
|
||
|
try:
|
||
|
obj = p.resolve()
|
||
|
if inspect.isclass(obj) or inspect.ismodule(obj):
|
||
|
reclist.append(p)
|
||
|
finally:
|
||
|
seen[id(p)] = p
|
||
|
for p in reclist:
|
||
|
for i in p.visit(fil, rec, seen):
|
||
|
yield i
|
||
|
|
||
|
def samefile(self, other):
|
||
|
otherobj = other.resolve()
|
||
|
try:
|
||
|
x = inspect.getfile(otherobj)
|
||
|
except TypeError:
|
||
|
return False
|
||
|
if x.endswith('.pyc'):
|
||
|
x = x[:-1]
|
||
|
if str(self.root) == x:
|
||
|
return True
|
||
|
|
||
|
def read(self, mode='ignored'):
|
||
|
""" return a bytestring from looking at our underlying object.
|
||
|
|
||
|
mode parmeter exists for consistency, but is ignored."""
|
||
|
return str(self.resolve())
|
||
|
|
||
|
class Checkers(common.Checkers):
|
||
|
_depend_on_existence = (common.Checkers._depend_on_existence +
|
||
|
('func', 'class_', 'exists', 'dir'))
|
||
|
|
||
|
def _obj(self):
|
||
|
self._obj = self.path.resolve()
|
||
|
return self._obj
|
||
|
|
||
|
def exists(self):
|
||
|
obj = self._obj()
|
||
|
return True
|
||
|
|
||
|
def func(self):
|
||
|
ob = self._obj()
|
||
|
return inspect.isfunction(ob) or inspect.ismethod(ob)
|
||
|
|
||
|
def class_(self):
|
||
|
ob = self._obj()
|
||
|
return inspect.isclass(ob)
|
||
|
|
||
|
def isinstance(self, args):
|
||
|
return isinstance(self._obj(), args)
|
||
|
|
||
|
def dir(self):
|
||
|
obj = self._obj()
|
||
|
return inspect.isclass(obj) or inspect.ismodule(obj)
|
||
|
|
||
|
def file(self):
|
||
|
return not self.dir()
|
||
|
|
||
|
def genfunc(self):
|
||
|
try:
|
||
|
return self._obj().func_code.co_flags & 32
|
||
|
except AttributeError:
|
||
|
return False
|