Source code for pyquickhelper.loghelper.repositories.pysvn_helper

# -*- coding: utf-8 -*-
"""
Gather all pysvn functionalities here. There might be some differences
between SVN version, pysvn client version, TortoiseSVN version and Python.
If such a case happens, most of the function will call ``svn`` using the command line.


:githublink:`%|py|8`
"""

import os
import sys
import datetime
import xml.etree.ElementTree as ET

from ..flog import fLOG, run_cmd
from ..convert_helper import str2datetime


[docs]def IsRepo(location, commandline=True): # pragma: no cover """ says if it a repository SVN :param location: (str) location :param commandline: (bool) use commandline or not :return: bool :githublink:`%|py|25` """ if location is None: location = os.path.normpath(os.path.abspath( os.path.join(os.path.split(__file__)[0], "..", "..", "..", ".."))) try: r = get_repo_version(location, commandline, log=False) return True and r is not None except Exception: return False
[docs]class RepoFile: # pragma: no cover """ mimic a svn file :githublink:`%|py|40` """
[docs] def __init__(self, **args): """ constructor :param args: list of members to add :githublink:`%|py|46` """ for k, v in args.items(): self.__dict__[k] = v if hasattr(self, "name") and '"' in self.name: # pylint: disable=E0203 #defa = sys.stdout.encoding if sys.stdout != None else "utf8" self.name = self.name.replace('"', "")
#self.name = self.name.encode(defa).decode("utf-8")
[docs] def __str__(self): """ usual :githublink:`%|py|58` """ return self.name
[docs]def repo_ls(full, commandline=True): # pragma: no cover """ run ``ls`` on a path :param full: full path :param commandline: use command line instead of pysvn :return: output of client.ls When a path includes a symbol ``@``, another one must added to the path to avoid the following error to happen: :: svn: E205000: Syntax error parsing peg revision 'something@somewhere.fr-' :githublink:`%|py|75` """ if not commandline: try: import pysvn client = pysvn.Client() entry = client.ls(full) return entry except Exception as e: typstr = str if "This client is too old to work with the working copy at" in typstr(e) or \ "No module named 'pysvn'" in typstr(e): if "@" in full: clean = full full += "@" else: clean = None cmd = "svn ls -r HEAD \"%s\"" % full.replace("\\", "/") out, err = run_cmd(cmd, wait=True, encerror="strict", encoding=sys.stdout.encoding if sys.stdout is not None else "utf8") if len(err) > 0: fLOG("problem with file ", full, err) raise Exception(err) def cleanf(s): if clean is None: return s else: return s.replace(clean + "@", clean) res = [RepoFile(name=os.path.join(cleanf(full), _.strip())) for _ in out.split("\n") if len(_) > 0] return res else: raise Exception("problem with file " + full) from e else: if "@" in full: clean = full full += "@" else: clean = None cmd = "svn ls -r HEAD \"%s\"" % full.replace("\\", "/") try: out, err = run_cmd(cmd, wait=True, encerror="strict", encoding=sys.stdout.encoding if sys.stdout is not None else "utf8") except Exception as e: raise Exception("issue with file or folder " + full) from e if len(err) > 0: fLOG("problem with file ", full, err) raise Exception(err) def cleanf(s): if clean is None: return s else: return s.replace(clean + "@", clean) res = [RepoFile(name=os.path.join(cleanf(full), _.strip())) for _ in out.split("\n") if len(_) > 0] return res
[docs]def __get_version_from_version_txt(path): # pragma: no cover """ private function, tries to find a file ``version.txt`` which should contains the version number (if svn is not present) :param path: folder to look, it will look to the the path of this file, some parents directories and finally this path :return: the version number .. warning:: If ``version.txt`` was not found, it throws an exception. :githublink:`%|py|148` """ file = os.path.split(__file__)[0] paths = [file, os.path.join(file, ".."), os.path.join(file, "..", ".."), os.path.join(file, "..", "..", ".."), path] for p in paths: fp = os.path.join(p, "version.txt") if os.path.exists(fp): with open(fp, "r") as f: return int(f.read().strip(" \n\r\t")) raise FileNotFoundError( "unable to find version.txt in\n" + "\n".join(paths))
[docs]def get_repo_log(path=None, file_detail=False, commandline=True): # pragma: no cover """ get the latest changes operated on a file in a folder or a subfolder :param path: path to look :param file_detail: if True, add impacted files :param commandline: if True, use the command line to get the version number, otherwise it uses pysvn :return: list of changes, each change is a list of 4-uple: - author - change number (int) - date (datetime) - comment The function use a command line if an error occurred. It uses the xml format: :: <logentry revision="161"> <author>xavier dupre</author> <date>2013-03-23T15:02:50.311828Z</date> <msg>pyquickhelper: first version</msg> </logentry> When a path includes a symbol ``@``, another one must added to the path to avoid the following error to happen: :: svn: E205000: Syntax error parsing peg revision 'something@somewhere.fr-' :githublink:`%|py|192` """ if path is None: path = os.path.normpath( os.path.abspath(os.path.join(os.path.split(__file__)[0], "..", "..", ".."))) if not commandline: try: import pysvn svnClient = pysvn.Client() if "@" in path: path += "@" version = get_repo_version(path) log = svnClient.log( path, revision_start=pysvn.Revision( pysvn.opt_revision_kind.number, 0), revision_end=pysvn.Revision( pysvn.opt_revision_kind.number, version), discover_changed_paths=True, strict_node_history=True, limit=0, include_merged_revisions=False, ) except Exception as e: typstr = str if "is not a working copy" in typstr(e): return [ ("", __get_version_from_version_txt(path), datetime.datetime.now(), "no repository")] elif "This client is too old to work with the working copy at" in typstr(e) or \ "No module named 'pysvn'" in typstr(e): return get_repo_log(path, file_detail, commandline=True) else: raise e else: if "@" in path: path += "@" cmd = "svn log -r HEAD:1 --xml \"%s\"" % path.replace("\\", "/") out, err = run_cmd(cmd, wait=True, encerror="strict", encoding=sys.stdout.encoding if sys.stdout is not None else "utf8") if len(err) > 0: fLOG("problem with file ", path, err) raise Exception(err) root = ET.fromstring(out) res = [] for i in root.iter('logentry'): revision = int(i.attrib['revision'].strip()) author = i.find("author").text.strip() t = i.find("msg").text msg = t.strip() if t is not None else "-" sdate = i.find("date").text.strip() dt = str2datetime(sdate.replace("T", " ").strip("Z ")) row = [author, revision, dt, msg] res.append(row) return res message = [] for info in log: message.append(("", info.revision.number, datetime.datetime.utcfromtimestamp(info.date), info.message)) if file_detail: for i, pt in enumerate(info.changed_paths): message.append(("file", info.revision.numbe, pt.data["action"], pt.data["path"])) if i > 100: message.append(" ...") break return message
[docs]def get_repo_version(path=None, commandline=True, log=False): # pragma: no cover """ get the latest check in number for a specific path :param path: path to look :param commandline: if True, use the command line to get the version number, otherwise it uses pysvn :param log: if True, returns the output instead of a boolean :return: integer (check in number) When a path includes a symbol ``@``, another one must added to the path to avoid the following error to happen: :: svn: E205000: Syntax error parsing peg revision 'something@somewhere.fr-' :githublink:`%|py|287` """ if path is None: path = os.path.normpath( os.path.abspath(os.path.join(os.path.split(__file__)[0], "..", "..", ".."))) if not commandline: try: import pysvn svnClient = pysvn.Client() path = "." if path is None else path.replace("\\", "/") if "@" in path: path += "@" info = svnClient.info2() infos = [_[1] for _ in info] revv = [_["rev"].number for _ in infos] revision = max(revv) return revision except Exception as e: typstr = str if "This client is too old to work with the working copy at" in typstr(e) or \ "No module named 'pysvn'" in typstr(e): return get_repo_version(path, commandline=True) elif "is not a working copy" in typstr(e): return __get_version_from_version_txt(path) else: raise e else: cmd = "svn info -r HEAD" if "@" in path: path += "@" if path is not None: cmd += " \"%s\"" % path.replace("\\", "/") out, err = run_cmd(cmd, wait=True, encerror="ignore", encoding=sys.stdout.encoding if sys.stdout is not None else "utf8", log_error=False) if len(err) > 0: if log: fLOG("problem with file ", path, err) if log: return "OUT\n{0}\n[svnerror]{1}\nCMD:\n{2}".format(out, err, cmd) else: raise Exception(err) lines = out.split("\n") lines = [_ for _ in lines if "Revision" in _] lines = lines[0].split(":") res = lines[1] if len(res) == 0: o, e = run_cmd("svn help", wait=True, log_error=False) if len(o) < 3: raise Exception( "the command 'svn help' should return something") return int(res)
[docs]def get_master_location(path=None, commandline=True): # pragma: no cover """ raises an exception :githublink:`%|py|348` """ raise NotImplementedError()
[docs]def get_nb_commits(path=None, commandline=True): # pragma: no cover """ returns the number of commit :param path: path to look :param commandline: if True, use the command line to get the version number, otherwise it uses pysvn :return: integer :githublink:`%|py|359` """ raise NotImplementedError()