##// END OF EJS Templates
release: added 2 ee-only features to release notes
release: added 2 ee-only features to release notes

File last commit:

r5651:bad147da default
r5661:2ba2d4b2 default
Show More
nodes.py
788 lines | 25.0 KiB | text/x-python | PythonLexer
core: updated copyright to 2024
r5608 # Copyright (C) 2014-2024 RhodeCode GmbH
project: added all source files and assets
r1 #
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
"""
Module holding everything related to vcs nodes, with vcs2 architecture.
"""
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647
vcs-support: bulk of changes for python3
r5075 import functools
largefiles: enabled download of largefiles for git and mercurial from web interface....
r1577 import os
project: added all source files and assets
r1 import stat
from zope.cachedescriptors.property import Lazy as LazyProperty
lexers: added small extensions table to extend syntaxt hl for file sources....
r796 from rhodecode.config.conf import LANGUAGES_EXTENSIONS_MAP
vcs-support: bulk of changes for python3
r5075 from rhodecode.lib.str_utils import safe_str, safe_bytes
from rhodecode.lib.hash_utils import md5
project: added all source files and assets
r1 from rhodecode.lib.vcs import path as vcspath
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 from rhodecode.lib.vcs.backends.base import EmptyCommit
project: added all source files and assets
r1 from rhodecode.lib.vcs.conf.mtypes import get_mimetypes_db
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 from rhodecode.lib.vcs.exceptions import NodeError
from rhodecode.lib.vcs_common import NodeKind, FILEMODE_DEFAULT
project: added all source files and assets
r1
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 LARGEFILE_PREFIX = ".hglf"
project: added all source files and assets
r1
class NodeState:
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 ADDED = "added"
CHANGED = "changed"
NOT_CHANGED = "not changed"
REMOVED = "removed"
vcs-support: bulk of changes for python3
r5075
project: added all source files and assets
r1
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 # TODO: not sure if that should be bytes or str ?
# most probably bytes because content should be bytes and we check it
BIN_BYTE_MARKER = b"\0"
project: added all source files and assets
r1
vcs-support: bulk of changes for python3
r5075 @functools.total_ordering
project: added all source files and assets
r1 class Node(object):
"""
Simplest class representing file or directory on repository. SCM backends
should use ``FileNode`` and ``DirNode`` subclasses rather than ``Node``
directly.
Node's ``path`` cannot start with slash as we operate on *relative* paths
only. Moreover, every single node is identified by the ``path`` attribute,
so it cannot end with slash, too. Otherwise, path could lead to mistakes.
"""
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647
vcs-support: bulk of changes for python3
r5075 # RTLO marker allows swapping text, and certain
# security attacks could be used with this
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 RTLO_MARKER = "\u202e"
vcs-support: bulk of changes for python3
r5075
project: added all source files and assets
r1 commit = None
vcs-support: bulk of changes for python3
r5075 def __init__(self, path: bytes, kind):
project: added all source files and assets
r1 self._validate_path(path) # can throw exception if path is invalid
vcs-support: bulk of changes for python3
r5075
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 self.bytes_path: bytes = path.rstrip(b"/") # store for mixed encoding, and raw version
self.str_path: str = safe_str(self.bytes_path) # we store paths as str
self.path: str = self.str_path
vcs-support: bulk of changes for python3
r5075
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 if self.bytes_path == b"" and kind != NodeKind.DIR:
raise NodeError("Only DirNode and its subclasses may be initialized with empty path")
project: added all source files and assets
r1 self.kind = kind
if self.is_root() and not self.is_dir():
raise NodeError("Root node cannot be FILE kind")
vcs-support: bulk of changes for python3
r5075 def __eq__(self, other):
if type(self) is not type(other):
return False
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 for attr in ["name", "path", "kind"]:
vcs-support: bulk of changes for python3
r5075 if getattr(self, attr) != getattr(other, attr):
return False
if self.is_file():
# FileNode compare, we need to fallback to content compare
return None
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 __lt__(self, other):
if self.kind < other.kind:
return True
if self.kind > other.kind:
return False
if self.path < other.path:
return True
if self.path > other.path:
return False
def __repr__(self):
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 maybe_path = getattr(self, "path", "UNKNOWN_PATH")
return f"<{self.__class__.__name__} {maybe_path!r}>"
vcs-support: bulk of changes for python3
r5075
def __str__(self):
return self.name
def _validate_path(self, path: bytes):
self._assert_bytes(path)
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 if path.startswith(b"/"):
project: added all source files and assets
r1 raise NodeError(
vcs-support: bulk of changes for python3
r5075 f"Cannot initialize Node objects with slash at "
f"the beginning as only relative paths are supported. "
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 f"Got {path}"
)
vcs-support: bulk of changes for python3
r5075
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 @classmethod
def _assert_bytes(cls, value):
vcs-support: bulk of changes for python3
r5075 if not isinstance(value, bytes):
raise TypeError(f"Bytes required as input, got {type(value)} of {value}.")
project: added all source files and assets
r1
@LazyProperty
def parent(self):
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 parent_path: bytes = self.get_parent_path()
project: added all source files and assets
r1 if parent_path:
if self.commit:
return self.commit.get_node(parent_path)
return DirNode(parent_path)
return None
@LazyProperty
files: remove rigth-to-left override character for display in files....
r2162 def has_rtlo(self):
"""Detects if a path has right-to-left-override marker"""
vcs-support: bulk of changes for python3
r5075 return self.RTLO_MARKER in self.str_path
files: remove rigth-to-left override character for display in files....
r2162
@LazyProperty
project: added all source files and assets
r1 def dir_path(self):
"""
Returns name of the directory from full path of this vcs node. Empty
string is returned if there's no directory in the path
"""
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 _parts = self.path.rstrip("/").rsplit("/", 1)
project: added all source files and assets
r1 if len(_parts) == 2:
vcs-support: bulk of changes for python3
r5075 return _parts[0]
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 return ""
project: added all source files and assets
r1
@LazyProperty
def name(self):
"""
Returns name of the node so if its path
then only last part is returned.
"""
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 return self.str_path.rstrip("/").split("/")[-1]
project: added all source files and assets
r1
@property
def kind(self):
return self._kind
@kind.setter
def kind(self, kind):
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 if hasattr(self, "_kind"):
project: added all source files and assets
r1 raise NodeError("Cannot change node's kind")
else:
self._kind = kind
# Post setter check (path's trailing slash)
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 if self.str_path.endswith("/"):
project: added all source files and assets
r1 raise NodeError("Node's path cannot end with slash")
vcs-support: bulk of changes for python3
r5075 def get_parent_path(self) -> bytes:
project: added all source files and assets
r1 """
Returns node's parent path or empty string if node is root.
"""
if self.is_root():
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 return b""
str_path = vcspath.dirname(self.bytes_path.rstrip(b"/")) + b"/"
vcs-support: bulk of changes for python3
r5075
return safe_bytes(str_path)
project: added all source files and assets
r1
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.
"""
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 return self.kind == NodeKind.DIR and self.path == ""
project: added all source files and assets
r1
def is_submodule(self):
"""
Returns ``True`` if node's kind is ``NodeKind.SUBMODULE``, ``False``
otherwise.
"""
return self.kind == NodeKind.SUBMODULE
def is_largefile(self):
"""
Returns ``True`` if node's kind is ``NodeKind.LARGEFILE``, ``False``
otherwise
"""
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 return self.kind == NodeKind.LARGE_FILE
project: added all source files and assets
r1
def is_link(self):
if self.commit:
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 return self.commit.is_link(self.bytes_path)
project: added all source files and assets
r1 return False
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: commit: if given, first time content is accessed, callback
:attribute: mode: stat mode for a node. Default is `FILEMODE_DEFAULT`.
"""
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647
vcs: added possibility to pre-load attributes for FileNodes.
r1355 _filter_pre_load = []
project: added all source files and assets
r1
fix(file-caching): fixed cases when old cache was used before changes to operate on bytestrings
r5651 def __init__(self, path: bytes, content: bytes | None = None, commit=None, mode=None, pre_load=None, pre_load_data=None):
project: added all source files and assets
r1 """
Only one of ``content`` and ``commit`` 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 commit: if given, will use it to lazily fetch content
:param mode: ST_MODE (i.e. 0100644)
"""
if content and commit:
raise NodeError("Cannot use both content and commit")
vcs-support: bulk of changes for python3
r5075
super().__init__(path, kind=NodeKind.FILE)
project: added all source files and assets
r1 self.commit = commit
vcs-support: bulk of changes for python3
r5075 if content and not isinstance(content, bytes):
# File content is one thing that inherently must be bytes
# we support passing str too, and convert the content
content = safe_bytes(content)
project: added all source files and assets
r1 self._content = content
self._mode = mode or FILEMODE_DEFAULT
fix(file-caching): fixed cases when old cache was used before changes to operate on bytestrings
r5651 if pre_load_data:
self._store_pre_load(pre_load_data)
else:
self._set_bulk_properties(pre_load)
vcs: added possibility to pre-load attributes for FileNodes.
r1355
vcs-support: bulk of changes for python3
r5075 def __eq__(self, other):
libs: more python3 reformats
r5091 eq = super().__eq__(other)
vcs-support: bulk of changes for python3
r5075 if eq is not None:
return eq
return self.content == other.content
def __hash__(self):
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 raw_id = getattr(self.commit, "raw_id", "")
vcs-support: bulk of changes for python3
r5075 return hash((self.path, raw_id))
def __lt__(self, other):
libs: more python3 reformats
r5091 lt = super().__lt__(other)
vcs-support: bulk of changes for python3
r5075 if lt is not None:
return lt
return self.content < other.content
def __repr__(self):
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 short_id = getattr(self.commit, "short_id", "")
return f"<{self.__class__.__name__} path={self.str_path!r}, short_id={short_id}>"
vcs-support: bulk of changes for python3
r5075
vcs: added possibility to pre-load attributes for FileNodes.
r1355 def _set_bulk_properties(self, pre_load):
if not pre_load:
return
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 pre_load = [entry for entry in pre_load if entry not in self._filter_pre_load]
vcs: added possibility to pre-load attributes for FileNodes.
r1355 if not pre_load:
return
vcs-support: bulk of changes for python3
r5075 remote = self.commit.get_remote()
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 result = remote.bulk_file_request(self.commit.raw_id, self.bytes_path, pre_load)
vcs-support: bulk of changes for python3
r5075
fix(file-caching): fixed cases when old cache was used before changes to operate on bytestrings
r5651 self._store_pre_load(result.items())
def _store_pre_load(self, pre_load_data):
for attr, value in pre_load_data:
vcs-support: bulk of changes for python3
r5075 if attr == "flags":
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 self.__dict__["mode"] = safe_str(value)
vcs-support: bulk of changes for python3
r5075 elif attr == "size":
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 self.__dict__["size"] = value
vcs-support: bulk of changes for python3
r5075 elif attr == "data":
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 self.__dict__["_content"] = value
vcs-support: bulk of changes for python3
r5075 elif attr == "is_binary":
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 self.__dict__["is_binary"] = value
vcs-support: bulk of changes for python3
r5075 elif attr == "md5":
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 self.__dict__["md5"] = value
vcs-support: bulk of changes for python3
r5075 else:
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 raise ValueError(f"Unsupported attr in bulk_property: {attr}")
vcs: added possibility to pre-load attributes for FileNodes.
r1355
project: added all source files and assets
r1 @LazyProperty
def mode(self):
"""
Returns lazily mode of the FileNode. If `commit` is not set, would
use value given at initialization or `FILEMODE_DEFAULT` (default).
"""
if self.commit:
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 mode = self.commit.get_file_mode(self.bytes_path)
project: added all source files and assets
r1 else:
mode = self._mode
return mode
dan
scm: change ._get_content() to .raw_bytes attribute to file nodes to...
r501 @LazyProperty
vcs-support: bulk of changes for python3
r5075 def raw_bytes(self) -> bytes:
dan
scm: change ._get_content() to .raw_bytes attribute to file nodes to...
r501 """
Returns lazily the raw bytes of the FileNode.
"""
project: added all source files and assets
r1 if self.commit:
dan
scm: change ._get_content() to .raw_bytes attribute to file nodes to...
r501 if self._content is None:
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 self._content = self.commit.get_file_content(self.bytes_path)
dan
scm: change ._get_content() to .raw_bytes attribute to file nodes to...
r501 content = self._content
project: added all source files and assets
r1 else:
content = self._content
return content
vcs-support: bulk of changes for python3
r5075 def content_uncached(self):
"""
Returns lazily content of the FileNode.
"""
if self.commit:
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 content = self.commit.get_file_content(self.bytes_path)
vcs-support: bulk of changes for python3
r5075 else:
content = self._content
return content
dan
file-nodes: added streaming remote attributes for vcsserver....
r3895 def stream_bytes(self):
"""
Returns an iterator that will stream the content of the file directly from
vcsserver without loading it to memory.
"""
if self.commit:
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 return self.commit.get_file_content_streamed(self.bytes_path)
dan
vcsserver: made binary content check be calculated on vcsserver...
r3896 raise NodeError("Cannot retrieve stream_bytes without related commit attribute")
dan
file-nodes: added streaming remote attributes for vcsserver....
r3895
api: expose new functions for FTS...
r3460 def metadata_uncached(self):
"""
Returns md5, binary flag of the file node, without any cache usage.
"""
api: allow uncached content fetching....
r3479 content = self.content_uncached()
api: expose new functions for FTS...
r3460
vcs-support: bulk of changes for python3
r5075 is_binary = bool(content and BIN_BYTE_MARKER in content)
api: expose new functions for FTS...
r3460 size = 0
if content:
size = len(content)
api: allow uncached content fetching....
r3479
return is_binary, md5(content), size, content
vcs-support: bulk of changes for python3
r5075 @LazyProperty
def content(self) -> bytes:
api: allow uncached content fetching....
r3479 """
vcs-support: bulk of changes for python3
r5075 Returns lazily content of the FileNode.
"""
content = self.raw_bytes
if content and not isinstance(content, bytes):
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 raise ValueError(f"Content is of type {type(content)} instead of bytes")
api: allow uncached content fetching....
r3479 return content
api: expose new functions for FTS...
r3460
dan
scm: change ._get_content() to .raw_bytes attribute to file nodes to...
r501 @LazyProperty
vcs-support: bulk of changes for python3
r5075 def str_content(self) -> str:
return safe_str(self.raw_bytes)
project: added all source files and assets
r1
@LazyProperty
def size(self):
if self.commit:
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 return self.commit.get_file_size(self.bytes_path)
raise NodeError("Cannot retrieve size of the file without related commit attribute")
project: added all source files and assets
r1
@LazyProperty
def message(self):
if self.commit:
return self.last_commit.message
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 raise NodeError("Cannot retrieve message of the file without related " "commit attribute")
project: added all source files and assets
r1
@LazyProperty
def last_commit(self):
if self.commit:
vcs: optimized pre-load attributes for better caching.
r3850 pre_load = ["author", "date", "message", "parents"]
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 return self.commit.get_path_commit(self.bytes_path, pre_load=pre_load)
raise NodeError("Cannot retrieve last commit of the file without related commit attribute")
project: added all source files and assets
r1
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).
"""
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 if hasattr(self, "_mimetype"):
if isinstance(self._mimetype, (tuple, list)) and len(self._mimetype) == 2:
project: added all source files and assets
r1 return self._mimetype
else:
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 raise NodeError("given _mimetype attribute must be an 2 element list or tuple")
project: added all source files and assets
r1
db = get_mimetypes_db()
mtype, encoding = db.guess_type(self.name)
if mtype is None:
largefiles: added fix for downloading largefiles without an extension in name.
r4666 if not self.is_largefile() and self.is_binary:
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 mtype = "application/octet-stream"
project: added all source files and assets
r1 encoding = None
else:
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 mtype = "text/plain"
project: added all source files and assets
r1 encoding = None
# try with pygments
try:
from pygments.lexers import get_lexer_for_filename
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647
project: added all source files and assets
r1 mt = get_lexer_for_filename(self.name).mimetypes
except Exception:
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):
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 return self.mimetype.split("/")[0]
project: added all source files and assets
r1
vcs: refactor get_lexer for nodes so it can be used in external code.
r1357 @classmethod
def get_lexer(cls, filename, content=None):
project: added all source files and assets
r1 from pygments import lexers
lexers: added small extensions table to extend syntaxt hl for file sources....
r796
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 extension = filename.split(".")[-1]
lexers: added small extensions table to extend syntaxt hl for file sources....
r796 lexer = None
vcs: refactor get_lexer for nodes so it can be used in external code.
r1357
project: added all source files and assets
r1 try:
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 lexer = lexers.guess_lexer_for_filename(filename, content, stripnl=False)
project: added all source files and assets
r1 except lexers.ClassNotFound:
vcs-support: bulk of changes for python3
r5075 pass
lexers: added small extensions table to extend syntaxt hl for file sources....
r796
# try our EXTENSION_MAP
if not lexer:
try:
vcs: refactor get_lexer for nodes so it can be used in external code.
r1357 lexer_class = LANGUAGES_EXTENSIONS_MAP.get(extension)
lexers: added small extensions table to extend syntaxt hl for file sources....
r796 if lexer_class:
lexer = lexers.get_lexer_by_name(lexer_class[0])
except lexers.ClassNotFound:
vcs-support: bulk of changes for python3
r5075 pass
lexers: added small extensions table to extend syntaxt hl for file sources....
r796
if not lexer:
project: added all source files and assets
r1 lexer = lexers.TextLexer(stripnl=False)
lexers: added small extensions table to extend syntaxt hl for file sources....
r796
project: added all source files and assets
r1 return lexer
@LazyProperty
vcs: refactor get_lexer for nodes so it can be used in external code.
r1357 def lexer(self):
"""
Returns pygment's lexer class. Would try to guess lexer taking file's
content, name and mimetype.
"""
vcs-support: bulk of changes for python3
r5075 # TODO: this is more proper, but super heavy on investigating the type based on the content
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 # self.get_lexer(self.name, self.content)
vcs-support: bulk of changes for python3
r5075
return self.get_lexer(self.name)
vcs: refactor get_lexer for nodes so it can be used in external code.
r1357
@LazyProperty
project: added all source files and assets
r1 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 commit for this file in which the file was changed
"""
if self.commit is None:
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 raise NodeError("Unable to get commit for this FileNode")
return self.commit.get_path_history(self.bytes_path)
project: added all source files and assets
r1
@LazyProperty
def annotate(self):
"""
Returns a list of three element tuples with lineno, commit and line
"""
if self.commit is None:
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 raise NodeError("Unable to get commit for this FileNode")
vcs: optimized pre-load attributes for better caching.
r3850 pre_load = ["author", "date", "message", "parents"]
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 return self.commit.get_file_annotate(self.bytes_path, pre_load=pre_load)
project: added all source files and assets
r1
dan
scm: change ._get_content() to .raw_bytes attribute to file nodes to...
r501 @LazyProperty
project: added all source files and assets
r1 def is_binary(self):
"""
Returns True if file has binary content.
"""
dan
vcsserver: made binary content check be calculated on vcsserver...
r3896 if self.commit:
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 return self.commit.is_node_binary(self.bytes_path)
dan
vcsserver: made binary content check be calculated on vcsserver...
r3896 else:
raw_bytes = self._content
vcs-support: bulk of changes for python3
r5075 return bool(raw_bytes and BIN_BYTE_MARKER in raw_bytes)
@LazyProperty
def md5(self):
"""
Returns md5 of the file node.
"""
if self.commit:
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 return self.commit.node_md5_hash(self.bytes_path)
vcs-support: bulk of changes for python3
r5075 else:
raw_bytes = self._content
# TODO: this sucks, we're computing md5 on potentially super big stream data...
return md5(raw_bytes)
project: added all source files and assets
r1
@LazyProperty
def extension(self):
"""Returns filenode extension"""
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 return self.name.split(".")[-1]
project: added all source files and assets
r1
@property
def is_executable(self):
"""
Returns ``True`` if file has executable flag turned on.
"""
return bool(self.mode & stat.S_IXUSR)
def get_largefile_node(self):
"""
Try to return a Mercurial FileNode from this node. It does internal
checks inside largefile store, if that file exist there it will
create special instance of LargeFileNode which can get content from
LF store.
"""
largefiles: enabled download of largefiles for git and mercurial from web interface....
r1577 if self.commit:
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 return self.commit.get_largefile_node(self.bytes_path)
project: added all source files and assets
r1
vcs-support: bulk of changes for python3
r5075 def count_lines(self, content: str | bytes, count_empty=False):
if isinstance(content, str):
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 newline_marker = "\n"
vcs-support: bulk of changes for python3
r5075 elif isinstance(content, bytes):
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 newline_marker = b"\n"
vcs-support: bulk of changes for python3
r5075 else:
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 raise ValueError("content must be bytes or str got {type(content)} instead")
nodes: expose line counts in node information. This would be used in full text search
r3962
if count_empty:
all_lines = 0
empty_lines = 0
for line in content.splitlines(True):
vcs-support: bulk of changes for python3
r5075 if line == newline_marker:
nodes: expose line counts in node information. This would be used in full text search
r3962 empty_lines += 1
all_lines += 1
return all_lines, all_lines - empty_lines
else:
# fast method
vcs-support: bulk of changes for python3
r5075 empty_lines = all_lines = content.count(newline_marker)
nodes: expose line counts in node information. This would be used in full text search
r3962 if all_lines == 0 and content:
# one-line without a newline
empty_lines = all_lines = 1
return all_lines, empty_lines
project: added all source files and assets
r1 def lines(self, count_empty=False):
all_lines, empty_lines = 0, 0
if not self.is_binary:
dan
scm: change ._get_content() to .raw_bytes attribute to file nodes to...
r501 content = self.content
nodes: expose line counts in node information. This would be used in full text search
r3962 all_lines, empty_lines = self.count_lines(content, count_empty=count_empty)
project: added all source files and assets
r1 return all_lines, empty_lines
class DirNode(Node):
"""
DirNode stores list of files and directories within this node.
Nodes may be used standalone but within repository context they
quick-filter: use a dedicated method for fetching quick filter nodes....
r3925 lazily fetch data within same repository's commit.
project: added all source files and assets
r1 """
vcs-support: bulk of changes for python3
r5075 def __init__(self, path, nodes=(), commit=None, default_pre_load=None):
project: added all source files and assets
r1 """
Only one of ``nodes`` and ``commit`` 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 commit: if given, will use it to lazily fetch content
"""
if nodes and commit:
raise NodeError("Cannot use both nodes and commit")
libs: more python3 reformats
r5091 super().__init__(path, NodeKind.DIR)
project: added all source files and assets
r1 self.commit = commit
self._nodes = nodes
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 self.default_pre_load = default_pre_load or ["is_binary", "size"]
vcs-support: bulk of changes for python3
r5075
def __iter__(self):
libs: more python3 reformats
r5091 yield from self.nodes
vcs-support: bulk of changes for python3
r5075
def __eq__(self, other):
libs: more python3 reformats
r5091 eq = super().__eq__(other)
vcs-support: bulk of changes for python3
r5075 if eq is not None:
return eq
# 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))
return self_nodes_paths == other_nodes_paths
def __lt__(self, other):
libs: more python3 reformats
r5091 lt = super().__lt__(other)
vcs-support: bulk of changes for python3
r5075 if lt is not None:
return lt
# 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))
return self_nodes_paths < other_nodes_paths
project: added all source files and assets
r1
@LazyProperty
def content(self):
vcs-support: bulk of changes for python3
r5075 raise NodeError(f"{self} represents a dir and has no `content` attribute")
project: added all source files and assets
r1
@LazyProperty
def nodes(self):
if self.commit:
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 nodes = self.commit.get_nodes(self.bytes_path, pre_load=self.default_pre_load)
project: added all source files and assets
r1 else:
nodes = self._nodes
return sorted(nodes)
@LazyProperty
def files(self):
libs: more python3 reformats
r5091 return sorted(node for node in self.nodes if node.is_file())
project: added all source files and assets
r1
@LazyProperty
def dirs(self):
libs: more python3 reformats
r5091 return sorted(node for node in self.nodes if node.is_dir())
project: added all source files and assets
r1
@LazyProperty
def state(self):
raise NodeError("Cannot access state of DirNode")
@LazyProperty
def size(self):
size = 0
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 for root, dirs, files in self.commit.walk(self.bytes_path):
project: added all source files and assets
r1 for f in files:
size += f.size
return size
vcs: rename get_file_history to get_path_history as it better reflects what it does.
r3275 @LazyProperty
def last_commit(self):
if self.commit:
vcs: optimized pre-load attributes for better caching.
r3850 pre_load = ["author", "date", "message", "parents"]
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 return self.commit.get_path_commit(self.bytes_path, pre_load=pre_load)
raise NodeError("Cannot retrieve last commit of the file without related commit attribute")
vcs: rename get_file_history to get_path_history as it better reflects what it does.
r3275
project: added all source files and assets
r1 def __repr__(self):
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 short_id = getattr(self.commit, "short_id", "")
return f"<{self.__class__.__name__} path={self.str_path!r}, short_id={short_id}>"
project: added all source files and assets
r1
class RootNode(DirNode):
"""
DirNode being the root node of the repository.
"""
def __init__(self, nodes=(), commit=None):
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 super().__init__(path=b"", nodes=nodes, commit=commit)
project: added all source files and assets
r1
def __repr__(self):
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 short_id = getattr(self.commit, "short_id", "")
return f"<{self.__class__.__name__} path={self.str_path!r}, short_id={short_id}>"
project: added all source files and assets
r1
class SubModuleNode(Node):
"""
represents a SubModule of Git or SubRepo of Mercurial
"""
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647
project: added all source files and assets
r1 is_binary = False
size = 0
def __init__(self, name, url=None, commit=None, alias=None):
fix(hg-items): fixed missing submodules code and add forgotten stats item
r5649 self.path: bytes = name
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 self.str_path: str = safe_str(self.path) # we store paths as str
project: added all source files and assets
r1 self.kind = NodeKind.SUBMODULE
self.alias = alias
# we have to use EmptyCommit here since this can point to svn/git/hg
# submodules we cannot get from repository
fix(hg-items): fixed missing submodules code and add forgotten stats item
r5649 self.commit = EmptyCommit(safe_str(commit), alias=alias)
self.url = safe_str(url) or self._extract_submodule_url()
project: added all source files and assets
r1
def __repr__(self):
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 short_id = getattr(self.commit, "short_id", "")
return f"<{self.__class__.__name__} {self.str_path!r} @ {short_id}>"
project: added all source files and assets
r1
def _extract_submodule_url(self):
fix(hg-items): fixed missing submodules code and add forgotten stats item
r5649 # TODO: find a way to parse gits submodule file and extract the linking URL
return safe_str(self.path)
project: added all source files and assets
r1
@LazyProperty
def name(self):
"""
Returns name of the node so if its path
then only last part is returned.
"""
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 org = self.str_path.rstrip("/").split("/")[-1]
return f"{org} @ {self.commit.short_id}"
project: added all source files and assets
r1
class LargeFileNode(FileNode):
largefiles: enabled download of largefiles for git and mercurial from web interface....
r1577 def __init__(self, path, url=None, commit=None, alias=None, org_path=None):
vcs-support: bulk of changes for python3
r5075 self._validate_path(path) # can throw exception if path is invalid
self.org_path = org_path # as stored in VCS as LF pointer
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 self.bytes_path = path.rstrip(b"/") # store for __repr__
self.str_path = safe_str(self.bytes_path)
self.path = self.str_path
vcs-support: bulk of changes for python3
r5075
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 self.kind = NodeKind.LARGE_FILE
largefiles: enabled download of largefiles for git and mercurial from web interface....
r1577 self.alias = alias
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 self._content = b""
largefiles: enabled download of largefiles for git and mercurial from web interface....
r1577
vcs-support: bulk of changes for python3
r5075 def _validate_path(self, path: bytes):
project: added all source files and assets
r1 """
vcs-support: bulk of changes for python3
r5075 we override check since the LargeFileNode path is system absolute, but we check for bytes only
project: added all source files and assets
r1 """
vcs-support: bulk of changes for python3
r5075 self._assert_bytes(path)
project: added all source files and assets
r1
largefiles: enabled download of largefiles for git and mercurial from web interface....
r1577 def __repr__(self):
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 return f"<{self.__class__.__name__} {self.org_path} -> {self.str_path!r}>"
largefiles: enabled download of largefiles for git and mercurial from web interface....
r1577
@LazyProperty
def size(self):
return os.stat(self.path).st_size
@LazyProperty
dan
scm: change ._get_content() to .raw_bytes attribute to file nodes to...
r501 def raw_bytes(self):
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 with open(self.path, "rb") as f:
nodes: fetching largefiles content is commit independent because the pointes is...
r1627 content = f.read()
largefiles: enabled download of largefiles for git and mercurial from web interface....
r1577 return content
@LazyProperty
def name(self):
"""
Overwrites name to be the org lf path
"""
return self.org_path
dan
file-nodes: added streaming remote attributes for vcsserver....
r3895
def stream_bytes(self):
fix(encoding for file): fixed support of non utf-8 files in all backends
r5647 with open(self.path, "rb") as stream:
dan
file-nodes: added streaming remote attributes for vcsserver....
r3895 while True:
data = stream.read(16 * 1024)
if not data:
break
yield data