Changeset - 986efdcfaf64
[Not reviewed]
default
0 2 0
domruf - 9 years ago 2017-05-11 00:19:14
dominikruf@gmail.com
vcs: remove unused code and test of it

Neither git nor mercurial nodes have a _mimetype attribute. Only
test_nodes.test_mimetype monkey patches it and that to me seems just wrong.
Thus, remove that part and fix the test with some usefull assertions.

Found by pylint.
2 files changed with 7 insertions and 18 deletions:
0 comments (0 inline, 0 general)
kallithea/lib/vcs/nodes.py
Show inline comments
 
@@ -126,396 +126,386 @@ class Node(object):
 
    @LazyProperty
 
    def name(self):
 
        """
 
        Returns name of the node so if its path
 
        then only last part is returned.
 
        """
 
        return safe_unicode(self.path.rstrip('/').split('/')[-1])
 

	
 
    def _get_kind(self):
 
        return self._kind
 

	
 
    def _set_kind(self, kind):
 
        if hasattr(self, '_kind'):
 
            raise NodeError("Cannot change node's kind")
 
        else:
 
            self._kind = kind
 
            # Post setter check (path's trailing slash)
 
            if self.path.endswith('/'):
 
                raise NodeError("Node's path cannot end with slash")
 

	
 
    kind = property(_get_kind, _set_kind)
 

	
 
    def __cmp__(self, other):
 
        """
 
        Comparator using name of the node, needed for quick list sorting.
 
        """
 
        kind_cmp = cmp(self.kind, other.kind)
 
        if kind_cmp:
 
            return kind_cmp
 
        return cmp(self.name, other.name)
 

	
 
    def __eq__(self, other):
 
        for attr in ['name', 'path', 'kind']:
 
            if getattr(self, attr) != getattr(other, attr):
 
                return False
 
        if self.is_file():
 
            if self.content != other.content:
 
                return False
 
        else:
 
            # For DirNode's check without entering each dir
 
            self_nodes_paths = list(sorted(n.path for n in self.nodes))
 
            other_nodes_paths = list(sorted(n.path for n in self.nodes))
 
            if self_nodes_paths != other_nodes_paths:
 
                return False
 
        return True
 

	
 
    def __nq__(self, other):
 
        return not self.__eq__(other)
 

	
 
    def __repr__(self):
 
        return '<%s %r>' % (self.__class__.__name__, self.path)
 

	
 
    def __str__(self):
 
        return self.__repr__()
 

	
 
    def __unicode__(self):
 
        return self.name
 

	
 
    def get_parent_path(self):
 
        """
 
        Returns node's parent path or empty string if node is root.
 
        """
 
        if self.is_root():
 
            return ''
 
        return posixpath.dirname(self.path.rstrip('/')) + '/'
 

	
 
    def is_file(self):
 
        """
 
        Returns ``True`` if node's kind is ``NodeKind.FILE``, ``False``
 
        otherwise.
 
        """
 
        return self.kind == NodeKind.FILE
 

	
 
    def is_dir(self):
 
        """
 
        Returns ``True`` if node's kind is ``NodeKind.DIR``, ``False``
 
        otherwise.
 
        """
 
        return self.kind == NodeKind.DIR
 

	
 
    def is_root(self):
 
        """
 
        Returns ``True`` if node is a root node and ``False`` otherwise.
 
        """
 
        return self.kind == NodeKind.DIR and self.path == ''
 

	
 
    def is_submodule(self):
 
        """
 
        Returns ``True`` if node's kind is ``NodeKind.SUBMODULE``, ``False``
 
        otherwise.
 
        """
 
        return self.kind == NodeKind.SUBMODULE
 

	
 
    @LazyProperty
 
    def added(self):
 
        return self.state is NodeState.ADDED
 

	
 
    @LazyProperty
 
    def changed(self):
 
        return self.state is NodeState.CHANGED
 

	
 
    @LazyProperty
 
    def not_changed(self):
 
        return self.state is NodeState.NOT_CHANGED
 

	
 
    @LazyProperty
 
    def removed(self):
 
        return self.state is NodeState.REMOVED
 

	
 

	
 
class FileNode(Node):
 
    """
 
    Class representing file nodes.
 

	
 
    :attribute: path: path to the node, relative to repository's root
 
    :attribute: content: if given arbitrary sets content of the file
 
    :attribute: changeset: if given, first time content is accessed, callback
 
    :attribute: mode: octal stat mode for a node. Default is 0100644.
 
    """
 

	
 
    def __init__(self, path, content=None, changeset=None, mode=None):
 
        """
 
        Only one of ``content`` and ``changeset`` may be given. Passing both
 
        would raise ``NodeError`` exception.
 

	
 
        :param path: relative path to the node
 
        :param content: content may be passed to constructor
 
        :param changeset: if given, will use it to lazily fetch content
 
        :param mode: octal representation of ST_MODE (i.e. 0100644)
 
        """
 

	
 
        if content and changeset:
 
            raise NodeError("Cannot use both content and changeset")
 
        super(FileNode, self).__init__(path, kind=NodeKind.FILE)
 
        self.changeset = changeset
 
        self._content = content
 
        self._mode = mode or 0100644
 

	
 
    @LazyProperty
 
    def mode(self):
 
        """
 
        Returns lazily mode of the FileNode. If ``changeset`` is not set, would
 
        use value given at initialization or 0100644 (default).
 
        """
 
        if self.changeset:
 
            mode = self.changeset.get_file_mode(self.path)
 
        else:
 
            mode = self._mode
 
        return mode
 

	
 
    def _get_content(self):
 
        if self.changeset:
 
            content = self.changeset.get_file_content(self.path)
 
        else:
 
            content = self._content
 
        return content
 

	
 
    @property
 
    def content(self):
 
        """
 
        Returns lazily content of the FileNode. If possible, would try to
 
        decode content from UTF-8.
 
        """
 
        content = self._get_content()
 

	
 
        if bool(content and '\0' in content):
 
            return content
 
        return safe_unicode(content)
 

	
 
    @LazyProperty
 
    def size(self):
 
        if self.changeset:
 
            return self.changeset.get_file_size(self.path)
 
        raise NodeError("Cannot retrieve size of the file without related "
 
            "changeset attribute")
 

	
 
    @LazyProperty
 
    def message(self):
 
        if self.changeset:
 
            return self.last_changeset.message
 
        raise NodeError("Cannot retrieve message of the file without related "
 
            "changeset attribute")
 

	
 
    @LazyProperty
 
    def last_changeset(self):
 
        if self.changeset:
 
            return self.changeset.get_file_changeset(self.path)
 
        raise NodeError("Cannot retrieve last changeset of the file without "
 
            "related changeset attribute")
 

	
 
    def get_mimetype(self):
 
        """
 
        Mimetype is calculated based on the file's content. If ``_mimetype``
 
        attribute is available, it will be returned (backends which store
 
        mimetypes or can easily recognize them, should set this private
 
        attribute to indicate that type should *NOT* be calculated).
 
        Mimetype is calculated based on the file's content.
 
        """
 
        if hasattr(self, '_mimetype'):
 
            if (isinstance(self._mimetype, (tuple, list,)) and
 
                len(self._mimetype) == 2):
 
                return self._mimetype
 
            else:
 
                raise NodeError('given _mimetype attribute must be an 2 '
 
                                'element list or tuple')
 

	
 
        mtype, encoding = mimetypes.guess_type(self.name)
 

	
 
        if mtype is None:
 
            if self.is_binary:
 
                mtype = 'application/octet-stream'
 
                encoding = None
 
            else:
 
                mtype = 'text/plain'
 
                encoding = None
 

	
 
                #try with pygments
 
                try:
 
                    from pygments import lexers
 
                    mt = lexers.get_lexer_for_filename(self.name).mimetypes
 
                except lexers.ClassNotFound:
 
                    mt = None
 

	
 
                if mt:
 
                    mtype = mt[0]
 

	
 
        return mtype, encoding
 

	
 
    @LazyProperty
 
    def mimetype(self):
 
        """
 
        Wrapper around full mimetype info. It returns only type of fetched
 
        mimetype without the encoding part. use get_mimetype function to fetch
 
        full set of (type,encoding)
 
        """
 
        return self.get_mimetype()[0]
 

	
 
    @LazyProperty
 
    def mimetype_main(self):
 
        return self.mimetype.split('/')[0]
 

	
 
    @LazyProperty
 
    def lexer(self):
 
        """
 
        Returns pygment's lexer class. Would try to guess lexer taking file's
 
        content, name and mimetype.
 
        """
 
        from pygments import lexers
 
        try:
 
            lexer = lexers.guess_lexer_for_filename(self.name, self.content, stripnl=False)
 
        except lexers.ClassNotFound:
 
            lexer = lexers.TextLexer(stripnl=False)
 
        # returns first alias
 
        return lexer
 

	
 
    @LazyProperty
 
    def lexer_alias(self):
 
        """
 
        Returns first alias of the lexer guessed for this file.
 
        """
 
        return self.lexer.aliases[0]
 

	
 
    @LazyProperty
 
    def history(self):
 
        """
 
        Returns a list of changeset for this file in which the file was changed
 
        """
 
        if self.changeset is None:
 
            raise NodeError('Unable to get changeset for this FileNode')
 
        return self.changeset.get_file_history(self.path)
 

	
 
    @LazyProperty
 
    def annotate(self):
 
        """
 
        Returns a list of three element tuples with lineno,changeset and line
 
        """
 
        if self.changeset is None:
 
            raise NodeError('Unable to get changeset for this FileNode')
 
        return self.changeset.get_file_annotate(self.path)
 

	
 
    @LazyProperty
 
    def state(self):
 
        if not self.changeset:
 
            raise NodeError("Cannot check state of the node if it's not "
 
                "linked with changeset")
 
        elif self.path in (node.path for node in self.changeset.added):
 
            return NodeState.ADDED
 
        elif self.path in (node.path for node in self.changeset.changed):
 
            return NodeState.CHANGED
 
        else:
 
            return NodeState.NOT_CHANGED
 

	
 
    @property
 
    def is_binary(self):
 
        """
 
        Returns True if file has binary content.
 
        """
 
        _bin = '\0' in self._get_content()
 
        return _bin
 

	
 
    def is_browser_compatible_image(self):
 
        return self.mimetype in [
 
            "image/gif",
 
            "image/jpeg",
 
            "image/png",
 
            "image/bmp"
 
        ]
 

	
 
    @LazyProperty
 
    def extension(self):
 
        """Returns filenode extension"""
 
        return self.name.split('.')[-1]
 

	
 
    @property
 
    def is_executable(self):
 
        """
 
        Returns ``True`` if file has executable flag turned on.
 
        """
 
        return bool(self.mode & stat.S_IXUSR)
 

	
 
    def __repr__(self):
 
        return '<%s %r @ %s>' % (self.__class__.__name__, self.path,
 
                                 getattr(self.changeset, 'short_id', ''))
 

	
 

	
 
class RemovedFileNode(FileNode):
 
    """
 
    Dummy FileNode class - trying to access any public attribute except path,
 
    name, kind or state (or methods/attributes checking those two) would raise
 
    RemovedFileNodeError.
 
    """
 
    ALLOWED_ATTRIBUTES = [
 
        'name', 'path', 'state', 'is_root', 'is_file', 'is_dir', 'kind',
 
        'added', 'changed', 'not_changed', 'removed'
 
    ]
 

	
 
    def __init__(self, path):
 
        """
 
        :param path: relative path to the node
 
        """
 
        super(RemovedFileNode, self).__init__(path=path)
 

	
 
    def __getattribute__(self, attr):
 
        if attr.startswith('_') or attr in RemovedFileNode.ALLOWED_ATTRIBUTES:
 
            return super(RemovedFileNode, self).__getattribute__(attr)
 
        raise RemovedFileNodeError("Cannot access attribute %s on "
 
            "RemovedFileNode" % attr)
 

	
 
    @LazyProperty
 
    def state(self):
 
        return NodeState.REMOVED
 

	
 

	
 
class DirNode(Node):
 
    """
 
    DirNode stores list of files and directories within this node.
 
    Nodes may be used standalone but within repository context they
 
    lazily fetch data within same repository's changeset.
 
    """
 

	
 
    def __init__(self, path, nodes=(), changeset=None):
 
        """
 
        Only one of ``nodes`` and ``changeset`` may be given. Passing both
 
        would raise ``NodeError`` exception.
 

	
 
        :param path: relative path to the node
 
        :param nodes: content may be passed to constructor
 
        :param changeset: if given, will use it to lazily fetch content
 
        :param size: always 0 for ``DirNode``
 
        """
 
        if nodes and changeset:
 
            raise NodeError("Cannot use both nodes and changeset")
 
        super(DirNode, self).__init__(path, NodeKind.DIR)
 
        self.changeset = changeset
 
        self._nodes = nodes
 

	
 
    @LazyProperty
 
    def content(self):
 
        raise NodeError("%s represents a dir and has no ``content`` attribute"
 
            % self)
 

	
 
    @LazyProperty
 
    def nodes(self):
 
        if self.changeset:
 
            nodes = self.changeset.get_nodes(self.path)
 
        else:
 
            nodes = self._nodes
 
        self._nodes_dict = dict((node.path, node) for node in nodes)
 
        return sorted(nodes)
 

	
 
    @LazyProperty
 
    def files(self):
 
        return sorted((node for node in self.nodes if node.is_file()))
 

	
 
    @LazyProperty
 
    def dirs(self):
 
        return sorted((node for node in self.nodes if node.is_dir()))
kallithea/tests/vcs/test_nodes.py
Show inline comments
 

	
 
import stat
 
import mimetypes
 
from kallithea.lib.vcs.nodes import DirNode
 
from kallithea.lib.vcs.nodes import FileNode
 
from kallithea.lib.vcs.nodes import Node
 
from kallithea.lib.vcs.nodes import NodeError
 
from kallithea.lib.vcs.nodes import NodeKind
 
from kallithea.lib.vcs.utils.compat import unittest
 

	
 

	
 
class NodeBasicTest(unittest.TestCase):
 

	
 
    def test_init(self):
 
        """
 
        Cannot initialize Node objects with path with slash at the beginning.
 
        """
 
        wrong_paths = (
 
            '/foo',
 
            '/foo/bar'
 
        )
 
        for path in wrong_paths:
 
            self.assertRaises(NodeError, Node, path, NodeKind.FILE)
 

	
 
        wrong_paths = (
 
            '/foo/',
 
            '/foo/bar/'
 
        )
 
        for path in wrong_paths:
 
            self.assertRaises(NodeError, Node, path, NodeKind.DIR)
 

	
 
    def test_name(self):
 
        node = Node('', NodeKind.DIR)
 
        self.assertEqual(node.name, '')
 

	
 
        node = Node('path', NodeKind.FILE)
 
        self.assertEqual(node.name, 'path')
 

	
 
        node = Node('path/', NodeKind.DIR)
 
        self.assertEqual(node.name, 'path')
 

	
 
        node = Node('some/path', NodeKind.FILE)
 
        self.assertEqual(node.name, 'path')
 

	
 
        node = Node('some/path/', NodeKind.DIR)
 
        self.assertEqual(node.name, 'path')
 

	
 
    def test_root_node(self):
 
        self.assertRaises(NodeError, Node, '', NodeKind.FILE)
 

	
 
    def test_kind_setter(self):
 
        node = Node('', NodeKind.DIR)
 
        self.assertRaises(NodeError, setattr, node, 'kind', NodeKind.FILE)
 

	
 
    def _test_parent_path(self, node_path, expected_parent_path):
 
        """
 
        Tests if node's parent path are properly computed.
 
        """
 
        node = Node(node_path, NodeKind.DIR)
 
        parent_path = node.get_parent_path()
 
        self.assertTrue(parent_path.endswith('/') or \
 
            node.is_root() and parent_path == '')
 
        self.assertEqual(parent_path, expected_parent_path,
 
            "Node's path is %r and parent path is %r but should be %r"
 
            % (node.path, parent_path, expected_parent_path))
 

	
 
    def test_parent_path(self):
 
        test_paths = (
 
            # (node_path, expected_parent_path)
 
            ('', ''),
 
            ('some/path/', 'some/'),
 
            ('some/longer/path/', 'some/longer/'),
 
        )
 
        for node_path, expected_parent_path in test_paths:
 
            self._test_parent_path(node_path, expected_parent_path)
 

	
 
    '''
 
    def _test_trailing_slash(self, path):
 
        if not path.endswith('/'):
 
            pytest.fail("Trailing slash tests needs paths to end with slash")
 
        for kind in NodeKind.FILE, NodeKind.DIR:
 
            self.assertRaises(NodeError, Node, path=path, kind=kind)
 

	
 
    def test_trailing_slash(self):
 
        for path in ('/', 'foo/', 'foo/bar/', 'foo/bar/biz/'):
 
            self._test_trailing_slash(path)
 
    '''
 

	
 
    def test_is_file(self):
 
        node = Node('any', NodeKind.FILE)
 
        self.assertTrue(node.is_file())
 

	
 
        node = FileNode('any')
 
        self.assertTrue(node.is_file())
 
        self.assertRaises(AttributeError, getattr, node, 'nodes')
 

	
 
    def test_is_dir(self):
 
        node = Node('any_dir', NodeKind.DIR)
 
        self.assertTrue(node.is_dir())
 

	
 
        node = DirNode('any_dir')
 

	
 
        self.assertTrue(node.is_dir())
 
        self.assertRaises(NodeError, getattr, node, 'content')
 

	
 
    def test_dir_node_iter(self):
 
        nodes = [
 
            DirNode('docs'),
 
            DirNode('tests'),
 
            FileNode('bar'),
 
            FileNode('foo'),
 
            FileNode('readme.txt'),
 
            FileNode('setup.py'),
 
        ]
 
        dirnode = DirNode('', nodes=nodes)
 
        for node in dirnode:
 
            node == dirnode.get_node(node.path)
 

	
 
    def test_node_state(self):
 
        """
 
        Without link to changeset nodes should raise NodeError.
 
        """
 
        node = FileNode('anything')
 
        self.assertRaises(NodeError, getattr, node, 'state')
 
        node = DirNode('anything')
 
        self.assertRaises(NodeError, getattr, node, 'state')
 

	
 
    def test_file_node_stat(self):
 
        node = FileNode('foobar', 'empty... almost')
 
        mode = node.mode  # default should be 0100644
 
        self.assertTrue(mode & stat.S_IRUSR)
 
        self.assertTrue(mode & stat.S_IWUSR)
 
        self.assertTrue(mode & stat.S_IRGRP)
 
        self.assertTrue(mode & stat.S_IROTH)
 
        self.assertFalse(mode & stat.S_IWGRP)
 
        self.assertFalse(mode & stat.S_IWOTH)
 
        self.assertFalse(mode & stat.S_IXUSR)
 
        self.assertFalse(mode & stat.S_IXGRP)
 
        self.assertFalse(mode & stat.S_IXOTH)
 

	
 
    def test_file_node_is_executable(self):
 
        node = FileNode('foobar', 'empty... almost', mode=0100755)
 
        self.assertTrue(node.is_executable)
 

	
 
        node = FileNode('foobar', 'empty... almost', mode=0100500)
 
        self.assertTrue(node.is_executable)
 

	
 
        node = FileNode('foobar', 'empty... almost', mode=0100644)
 
        self.assertFalse(node.is_executable)
 

	
 
    def test_mimetype(self):
 
        py_node = FileNode('test.py')
 
        tar_node = FileNode('test.tar.gz')
 

	
 
        ext = 'CustomExtension'
 

	
 
        my_node2 = FileNode('myfile2')
 
        my_node2._mimetype = [ext]
 
        my_node2._content = 'foobar'
 

	
 
        my_node3 = FileNode('myfile3')
 
        my_node3._mimetype = [ext,ext]
 
        my_node3._content = '\0foobar'
 

	
 
        self.assertEqual(py_node.mimetype, mimetypes.guess_type(py_node.name)[0])
 
        self.assertEqual(py_node.get_mimetype(), mimetypes.guess_type(py_node.name))
 

	
 
        self.assertEqual(tar_node.mimetype, mimetypes.guess_type(tar_node.name)[0])
 
        self.assertEqual(tar_node.get_mimetype(), mimetypes.guess_type(tar_node.name))
 

	
 
        self.assertRaises(NodeError,my_node2.get_mimetype)
 
        self.assertEqual(my_node2.mimetype, 'text/plain')
 
        self.assertEqual(my_node2.get_mimetype(), ('text/plain', None))
 

	
 
        self.assertEqual(my_node3.mimetype,ext)
 
        self.assertEqual(my_node3.get_mimetype(),[ext,ext])
 
        self.assertEqual(my_node3.mimetype, 'application/octet-stream')
 
        self.assertEqual(my_node3.get_mimetype(), ('application/octet-stream', None))
 

	
 
class NodeContentTest(unittest.TestCase):
 

	
 
    def test_if_binary(self):
 
        data = """\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f??a\x00\x00\x00\x04gAMA\x00\x00\xaf?7\x05\x8a?\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq?e<\x00\x00\x025IDAT8?\xa5\x93?K\x94Q\x14\x87\x9f\xf7?Q\x1bs4?\x03\x9a\xa8?B\x02\x8b$\x10[U;i\x13?6h?&h[?"\x14j?\xa2M\x7fB\x14F\x9aQ?&\x842?\x0b\x89"\x82??!?\x9c!\x9c2l??{N\x8bW\x9dY\xb4\t/\x1c?=\x9b?}????\xa9*;9!?\x83\x91?[?\\v*?D\x04\'`EpNp\xa2X\'U?pVq"Sw.\x1e?\x08\x01D?jw????\xbc??7{|\x9b?\x89$\x01??W@\x15\x9c\x05q`Lt/\x97?\x94\xa1d?\x18~?\x18?\x18W[%\xb0?\x83??\x14\x88\x8dB?\xa6H\tL\tl\x19>/\x01`\xac\xabx?\x9cl\nx\xb0\x98\x07\x95\x88D$"q[\x19?d\x00(o\n\xa0??\x7f\xb9\xa4?\x1bF\x1f\x8e\xac\xa8?j??eUU}?.?\x9f\x8cE??x\x94??\r\xbdtoJU5"0N\x10U?\x00??V\t\x02\x9f\x81?U?\x00\x9eM\xae2?r\x9b7\x83\x82\x8aP3????.?&"?\xb7ZP \x0c<?O\xa5\t}\xb8?\x99\xa6?\x87?\x1di|/\xa0??0\xbe\x1fp?d&\x1a\xad\x95\x8a\x07?\t*\x10??b:?d?.\x13C\x8a?\x12\xbe\xbf\x8e?{???\x08?\x80\xa7\x13+d\x13>J?\x80\x15T\x95\x9a\x00??S\x8c\r?\xa1\x03\x07?\x96\x9b\xa7\xab=E??\xa4\xb3?\x19q??B\x91=\x8d??k?J\x0bV"??\xf7x?\xa1\x00?\\.\x87\x87???\x02F@D\x99],??\x10#?X\xb7=\xb9\x10?Z\x1by???cI??\x1ag?\x92\xbc?T?t[\x92\x81?<_\x17~\x92\x88?H%?\x10Q\x02\x9f\n\x81qQ\x0bm?\x1bX?\xb1AK\xa6\x9e\xb9?u\xb2?1\xbe|/\x92M@\xa2!F?\xa9>"\r<DT?>\x92\x8e?>\x9a9Qv\x127?a\xac?Y?8?:??]X???9\x80\xb7?u?\x0b#BZ\x8d=\x1d?p\x00\x00\x00\x00IEND\xaeB`\x82"""
 
        filenode = FileNode('calendar.png', content=data)
 
        self.assertTrue(filenode.is_binary)
 

	
 

	
 
if __name__ == '__main__':
 
    unittest.main()
0 comments (0 inline, 0 general)