Source code for nxpy.svn.svn

# nxpy.svn package -----------------------------------------------------------

# Copyright Nicola Musatti 2010 - 2018
# Use, modification, and distribution are subject to the Boost Software
# License, Version 1.0. (See accompanying file LICENSE.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)

# See http://nxpy.sourceforge.net for library home page. ---------------------

r"""
Subversion client wrapper.

Only supports versions 1.6, 1.7, 1.8 and 1.9, others might work but have not been
tested.
Requires at least Python 2.6.

"""

# Ensures that tests are skipped rather than broken when run with Python 2.5
from __future__ import with_statement
from __future__ import absolute_import
 
import logging
import os
import os.path
import re

import six

import nxpy.command.command
import nxpy.command.option
import nxpy.core.error
import nxpy.core.past
import nxpy.core.temp_file
import nxpy.svn.url

import nxpy.core._impl.log

_log = logging.getLogger(__name__)


[docs]class Info(object): r"""Represents the output of the ``svn info`` command in a structured way.""" _outRe = re.compile(r"([^:]+):\s+(.*)")
[docs] def __init__(self, out): for l in out.split("\n"): match = Info._outRe.match(l) if match: if match.group(1) == "Path": self.path = os.path.realpath(match.group(2)) elif match.group(1) == "URL": self.url = match.group(2) elif match.group(1) == "Repository Root": self.root = match.group(2)
[docs] def __str__(self): return " - ".join((self.path, str(self.url), self.root))
[docs]class Status(object): r"""Represents the output of one line of the ``svn status`` command in a structured way."""
[docs] def __init__(self, line): self.item = line[8:] self.state = line[0] self.properties = line[1] self.locked = line[2] == 'L' self.add_with_history = line[3] == '+' self.switched = line[4] == 'S' self.lock = line[5] self.tree_conflict = line[6] == 'C'
[docs] def __str__(self): return ( self.state + self.properties + ( 'L' if self.locked else ' ') + ( '+' if self.add_with_history else ' ' ) + ( 'S' if self.switched else ' ' ) + self.lock + ( 'C' if self.tree_conflict else ' ' ) + ' ' + self.item )
_config = nxpy.command.option.Config( bool_opts = ( "ignore_externals", "keep_local", "parents", "quiet", "stop_on_copy", "summarize", "verbose", "version", "xml" ), value_opts = ( "file", "message", "password", "revision", "username", "value" ), opposite_opts = { "ignore" : "--no-ignore" }, mapped_opts = { "ignore_externals" : "--ignore-externals", "keep_local" : "--keep-local", "stop_on_copy" : "--stop-on-copy", "value" : "", "version" : "--version" } )
[docs]class Parser(nxpy.command.option.Parser): r"""Allows passing *nxpy.svn.url.Url* instances as arguments to *Svn*'s methods."""
[docs] def __init__(self, command, arguments, options, **defaults): super(Parser, self).__init__(_config, command, [ self._pathtostring(a) for a in arguments ], options, **defaults)
def _pathtostring(self, path): if isinstance(path, nxpy.svn.url.Url): return str(path) elif isinstance(path, six.string_types): return path else: raise nxpy.core.error.ArgumentError()
[docs]class Svn(nxpy.command.command.Command): r"""The actual wrapper."""
[docs] def __init__(self, debug=None): super(Svn, self).__init__("svn --non-interactive", debug) self._version = None
_versionRe = re.compile(r"(\d+)\.(\d+)\.(\d+)")
[docs] def version(self): r"""Return the version of the available `svn` command as a tuple.""" op = Parser("", (), {}, version=True) out = self.run(op)[0] match = Svn._versionRe.search(out) version = ( int(match.group(1)), int(match.group(2)), int(match.group(3))) return version
[docs] def info(self, *targets): op = Parser("info", targets, {}) out = self.run(op)[0] return Info(out)
[docs] def list(self, *targets): op = Parser("list", targets, {}) out = self.run(op)[0] return out.split()
[docs] def mkdir(self, *targets, **options): op = Parser("mkdir", targets, options, parents=False, username="", password="", message="\"[" + __name__ + "] Make dir(s) " + ", ".join(targets) + "\"") self.run(op)
[docs] def status(self, *targets, **options): op = Parser("status", targets, options, ignore=True, quiet=True, ignore_externals=True) out = self.run(op)[0] ignore_externals = options.get('ignore_externals', True) res = [] for l in out.split("\n"): if len(l) > 0 and ( not ignore_externals or l[0] != "X" ): res.append(Status(l)) return res
[docs] def import_(self, src, dest, debug=None, **options): op = Parser("import", ( src, dest ), {}, username="", password="", message="\"[" + __name__ + "] Import from " + src + "\"") self.run(op, debug)
[docs] def checkout(self, src, dest, debug=None, **options): op = Parser("checkout", ( src, dest ), options, username="", password="", ignore_externals=True) self.run(op, debug)
[docs] def update(self, *targets, **options): op = Parser("update", targets, options, ignore_externals=True) self.run(op)
[docs] def commit(self, src, debug=None, **options): op = Parser("commit", ( src, ), options, username="", password="", message="\"[" + __name__ + "] Commit from " + src + "\"") self.run(op, debug)
[docs] def copy(self, src, dest, debug=None, **options): op = Parser("copy", ( src, dest ), options, ignore_externals=True, username="", password="", message="\"[" + __name__ + "] Copy from " + src + "\"") self.run(op, debug)
def _delete_paths(self, debug, *paths, **options): op = Parser("delete", paths, options, keep_local=False) self.run(op, debug) def _delete_urls(self, debug, *urls, **options): op = Parser("delete", urls, options, username="", password="", message="\"[" + __name__ + "] Delete " + ", ".join([ str(u) for u in urls ]) + "\"") self.run(op, debug)
[docs] def delete(self, *targets, **options): debug = options.get("debug") if debug is not None: del options["debug"] paths = [] urls = [] for t in targets: if not isinstance(t, nxpy.svn.url.Url) and os.access(t, os.R_OK): paths.append(t) else: urls.append(t) if len(paths) > 0: self._delete_paths(debug, *paths, **options) if len(urls) > 0: self._delete_urls(debug, *urls, **options)
[docs] def propget(self, name, *targets): op = Parser("propget", ( name, ) + targets, {}) out = self.run(op)[0] return out.strip()
[docs] def getexternals(self, d): r""" Return *d*'s ``svn:externals`` property as a dictionary of directory - URL pairs. Note that only a limited subset of the externals syntax is supported: either the pre-svn 1.5 one (directory - URL) or the same with inverted elements. Throw *nxpy.svn.url.BadUrlError* if an external URL is malformed. """ out = self.propget("svn:externals", d) res = {} for l in out.split("\n"): # Python 2.7 incompatibility # dir, url = l.rsplit(maxsplit=1) dir, url = l.rsplit(None, 1) try: nxpy.svn.url.Url(url) except IndexError: # Python 2.7 incompatibility # url, dir = l.split(maxsplit=1) url, dir = l.split(None, 1) nxpy.svn.url.Url(url) res[dir] = url return res
[docs] def getignore(self, d): out = self.propget("svn:ignore", d) return list(out.split("\n"))
[docs] def propset(self, name, *targets, **options): debug = options.get("debug") value = options.get("value") if value: del options["value"] op = Parser("propset", ( name, value ) + targets, options, username="", password="") else: op = Parser("propset", ( name, ) + targets, options, file="", username="", password="") self.run(op, debug)
[docs] def setexternals(self, externals, d, username="", password=""): nxpy.core.past.enforce_at_least(nxpy.core.past.V_2_6) with nxpy.core.temp_file.TempFile(mode="w+", prefix="svn_setexternals") as f: for k, v in six.iteritems(externals): f.write(k + "\t" + v + "\n") f.seek(0, os.SEEK_SET) _log.debug(f.read()) f.close() self.propset("svn:externals", d, file=f.name, username=username, password=password)
[docs] def setignore(self, ignore, d, username="", password=""): nxpy.core.past.enforce_at_least(nxpy.core.past.V_2_6) with nxpy.core.temp_file.TempFile(mode="w+", prefix="svn_setignore") as f: for v in ignore: f.write(v + "\n") f.seek(0, os.SEEK_SET) _log.debug(f.read()) f.close() self.propset("svn:ignore", d, file=f.name, username=username, password=password)
[docs] def diff(self, *targets, **options): op = Parser("diff", targets, options, summarize=False, revision=None) out = self.run(op)[0] return out
[docs] def cat(self, *targets, **options): op = Parser("cat", targets, options, revision=None) out = self.run(op)[0] return out
[docs] def log(self, src, **options): op = Parser("log", ( src, ), options, verbose=False, stop_on_copy=False, revision=None, xml=False) out = self.run(op)[0] return out