diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index aa7074464..3ac7f633e 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -44,10 +44,11 @@ jobs: - name: Install other dependencies run: | python -m pip install --upgrade pip - pip install -U pip flake8 fastimport setuptools pytest pytest-cov + pip install -U pip ruff fastimport setuptools pytest pytest-cov - name: Style checks run: | - python -m flake8 subvertpy + python -m ruff check subvertpy + python -m ruff format --check subvertpy - name: Build (Linux) run: | pip install -e . diff --git a/.travis.yml b/.travis.yml index a10ec6b39..8dd3b7969 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,7 +26,7 @@ script: - make check - make style install: - - travis_retry pip install -U pip coverage codecov flake8 + - travis_retry pip install -U pip coverage codecov ruff before_install: - wget https://archive.apache.org/dist/subversion/subversion-${SVN_VERSION}.tar.gz - tar xvfz subversion-${SVN_VERSION}.tar.gz diff --git a/Makefile b/Makefile index 0df116c20..f0e75155a 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ PYTHON = python3 -FLAKE8 ?= flake8 +RUFF ?= ruff PYDOCTOR = pydoctor PYDOCTOR_OPTIONS ?= SETUP = $(PYTHON) setup.py @@ -44,4 +44,5 @@ pydoctor: $(PYDOCTOR) $(PYDOCTOR_OPTIONS) --introspect-c-modules -c subvertpy.cfg --make-html style: - $(FLAKE8) + $(RUFF) check + $(RUFF) format --check diff --git a/examples/ra_commit.py b/examples/ra_commit.py index acfc7eebe..b48515981 100755 --- a/examples/ra_commit.py +++ b/examples/ra_commit.py @@ -13,8 +13,7 @@ # Note that a username provider needs to be provided, so that Subversion # knows who to record as the author of new commits made over this connection. repo_url = "file://%s" % os.path.abspath("tmprepo") -conn = RemoteAccess(repo_url, - auth=Auth([get_username_provider()])) +conn = RemoteAccess(repo_url, auth=Auth([get_username_provider()])) # Simple commit that adds a directory editor = conn.get_commit_editor({"svn:log": "Commit message"}) diff --git a/examples/ra_log.py b/examples/ra_log.py index 43a9cdc84..dc426fc77 100755 --- a/examples/ra_log.py +++ b/examples/ra_log.py @@ -5,9 +5,9 @@ conn = RemoteAccess("svn://svn.samba.org/subvertpy/trunk") -for (changed_paths, rev, revprops, has_children) in conn.iter_log( - paths=None, start=0, end=conn.get_latest_revnum(), - discover_changed_paths=True): +for changed_paths, rev, revprops, has_children in conn.iter_log( + paths=None, start=0, end=conn.get_latest_revnum(), discover_changed_paths=True +): print("=" * 79) print("%d:" % rev) print("Revision properties:") @@ -16,6 +16,5 @@ print("") print("Changed paths") - for path, (action, from_path, from_rev, node_kind) in ( - changed_paths.items()): + for path, (action, from_path, from_rev, node_kind) in changed_paths.items(): print(" %s (%s)" % (path, action)) diff --git a/examples/ra_replay.py b/examples/ra_replay.py index a29fca907..eee40a056 100755 --- a/examples/ra_replay.py +++ b/examples/ra_replay.py @@ -4,12 +4,12 @@ from subvertpy.ra import RemoteAccess, Auth, get_username_provider -conn = RemoteAccess("svn://svn.gnome.org/svn/gnome-specimen/trunk", - auth=Auth([get_username_provider()])) +conn = RemoteAccess( + "svn://svn.gnome.org/svn/gnome-specimen/trunk", auth=Auth([get_username_provider()]) +) class MyFileEditor: - def change_prop(self, key, value): print("Change prop: %s -> %r" % (key, value)) @@ -17,6 +17,7 @@ def apply_textdelta(self, base_checksum): # This should return a function that can receive delta windows def apply_window(x): pass + return apply_window def close(self): @@ -24,7 +25,6 @@ def close(self): class MyDirEditor: - def open_directory(self, *args): print("Open dir: %s (base revnum: %r)" % args) return MyDirEditor() @@ -38,8 +38,7 @@ def open_file(self, *args): return MyFileEditor() def add_file(self, path, copyfrom_path=None, copyfrom_rev=-1): - print("Add file: %s (from %r:%r)" % - (path, copyfrom_path, copyfrom_rev)) + print("Add file: %s (from %r:%r)" % (path, copyfrom_path, copyfrom_rev)) return MyFileEditor() def change_prop(self, key, value): @@ -53,7 +52,6 @@ def close(self): class MyEditor: - def set_target_revision(self, revnum): print("Target revision: %d" % revnum) diff --git a/examples/ra_shell.py b/examples/ra_shell.py index a11b51d5e..9bc04e0d4 100755 --- a/examples/ra_shell.py +++ b/examples/ra_shell.py @@ -29,7 +29,6 @@ def log_printer(changed_paths, rev, revprops, has_children=None): class RaCmd(cmd.Cmd): - @staticmethod def parse_path_revnum(line): args = line.split(" ") @@ -59,7 +58,7 @@ def do_ls(self, args): def do_cat(self, args): path, revnum = self.parse_path_revnum(args) - outf = getattr(sys.stdout, 'buffer', sys.stdout) + outf = getattr(sys.stdout, "buffer", sys.stdout) (fetched_rev, props) = conn.get_file(path, outf, revnum) def do_reparent(self, args): @@ -93,8 +92,13 @@ def do_get_repos_root(self, args): print(conn.get_repos_root()) def do_log(self, args): - conn.get_log(callback=log_printer, paths=None, start=0, - end=conn.get_latest_revnum(), discover_changed_paths=True) + conn.get_log( + callback=log_printer, + paths=None, + start=0, + end=conn.get_latest_revnum(), + discover_changed_paths=True, + ) cmdline = RaCmd() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..e0c12635b --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,9 @@ +[tool.ruff] +exclude = [ + "build", + ".git", + "build-pypy", + ".tox", + "subversion-*", + "serf-*", +] diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index a5ffc6832..000000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[flake8] -exclude = build,.git,build-pypy,.tox,subversion-*,serf-* diff --git a/setup.py b/setup.py index 87198bcc1..ad54e572d 100755 --- a/setup.py +++ b/setup.py @@ -23,8 +23,8 @@ def config_value(command, env, args): except OSError as e: if e.errno == errno.ENOENT: raise Exception( - "%s not found. Please set %s environment variable" % ( - command, env)) + "%s not found. Please set %s environment variable" % (command, env) + ) raise @@ -39,19 +39,17 @@ def apu_config(args): def apr_build_data(): """Determine the APR header file location.""" try: - includedir = os.environ['APR_INCLUDE_DIR'] + includedir = os.environ["APR_INCLUDE_DIR"] except KeyError: if os.name != "nt": includedir = apr_config(["--includedir"]) if not os.path.isdir(includedir): raise Exception("APR development headers not found") try: - extra_link_flags = split_shell_results( - os.environ['APR_LINK_FLAGS']) + extra_link_flags = split_shell_results(os.environ["APR_LINK_FLAGS"]) except KeyError: if os.name != "nt": - extra_link_flags = split_shell_results( - apr_config(["--link-ld", "--libs"])) + extra_link_flags = split_shell_results(apr_config(["--link-ld", "--libs"])) else: extra_link_flags = [] return (includedir, extra_link_flags) @@ -60,19 +58,17 @@ def apr_build_data(): def apu_build_data(): """Determine the APR util header file location.""" try: - includedir = os.environ['APU_INCLUDE_DIR'] + includedir = os.environ["APU_INCLUDE_DIR"] except KeyError: if os.name != "nt": includedir = apu_config(["--includedir"]) if not os.path.isdir(includedir): raise Exception("APR util development headers not found") try: - extra_link_flags = split_shell_results( - os.environ['APU_LINK_FLAGS']) + extra_link_flags = split_shell_results(os.environ["APU_LINK_FLAGS"]) except KeyError: if os.name != "nt": - extra_link_flags = split_shell_results( - apu_config(["--link-ld", "--libs"])) + extra_link_flags = split_shell_results(apu_config(["--link-ld", "--libs"])) else: extra_link_flags = [] return (includedir, extra_link_flags) @@ -81,8 +77,7 @@ def apu_build_data(): def svn_build_data(): """Determine the Subversion header file location.""" if "SVN_HEADER_PATH" in os.environ and "SVN_LIBRARY_PATH" in os.environ: - return ([os.getenv("SVN_HEADER_PATH")], - [os.getenv("SVN_LIBRARY_PATH")], []) + return ([os.getenv("SVN_HEADER_PATH")], [os.getenv("SVN_LIBRARY_PATH")], []) svn_prefix = os.getenv("SVN_PREFIX") if svn_prefix is None: basedirs = ["/usr/local", "/usr", "/opt/homebrew", "/opt/local"] @@ -92,12 +87,16 @@ def svn_build_data(): svn_prefix = basedir break if svn_prefix is not None: - return ([os.path.join(svn_prefix, "include/subversion-1")], - [os.path.join(svn_prefix, "lib"), - os.path.join(svn_prefix, "lib", "db48")], []) - raise Exception("Subversion development files not found. " - "Please set SVN_PREFIX or (SVN_LIBRARY_PATH and " - "SVN_HEADER_PATH) environment variable. ") + return ( + [os.path.join(svn_prefix, "include/subversion-1")], + [os.path.join(svn_prefix, "lib"), os.path.join(svn_prefix, "lib", "db48")], + [], + ) + raise Exception( + "Subversion development files not found. " + "Please set SVN_PREFIX or (SVN_LIBRARY_PATH and " + "SVN_HEADER_PATH) environment variable. " + ) def is_keychain_provider_available(): @@ -108,24 +107,28 @@ def is_keychain_provider_available(): abd = apr_build_data() sbd = svn_build_data() gcc_command_args = ( - ['gcc'] + ['-I' + inc for inc in sbd[0]] + - ['-L' + lib for lib in sbd[1]] + - ['-I' + abd[0], '-lsvn_subr-1', '-x', 'c', '-']) + ["gcc"] + + ["-I" + inc for inc in sbd[0]] + + ["-L" + lib for lib in sbd[1]] + + ["-I" + abd[0], "-lsvn_subr-1", "-x", "c", "-"] + ) gcc = subprocess.Popen( - gcc_command_args, - stdin=subprocess.PIPE, universal_newlines=True, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + gcc_command_args, + stdin=subprocess.PIPE, + universal_newlines=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) gcc.communicate(""" #include int main(int argc, const char* arv[]) { svn_auth_get_keychain_simple_provider(NULL, NULL); } """) - return (gcc.returncode == 0) + return gcc.returncode == 0 class VersionQuery(object): - def __init__(self, filename): self.filename = filename f = open(filename, "rU") @@ -135,12 +138,11 @@ def __init__(self, filename): f.close() def grep(self, what): - m = re.search(r"^#define\s+%s\s+(\d+)\s*$" % (what,), self.text, - re.MULTILINE) + m = re.search(r"^#define\s+%s\s+(\d+)\s*$" % (what,), self.text, re.MULTILINE) if not m: raise Exception( - "Definition for %s was not found in file %s." % - (what, self.filename)) + "Definition for %s was not found in file %s." % (what, self.filename) + ) return int(m.group(1)) @@ -150,10 +152,9 @@ def grep(self, what): class SvnExtension(Extension): - def __init__(self, name, *args, **kwargs): - if sys.platform == 'win32': - libraries = kwargs.get('libraries', []) + if sys.platform == "win32": + libraries = kwargs.get("libraries", []) libs = [("lib" + lib) for lib in libraries] modified = True while modified: @@ -164,27 +165,26 @@ def __init__(self, name, *args, **kwargs): modified = True libs.append(extra) kwargs["libraries"] = libs - kwargs["include_dirs"] = ([apr_includedir, apu_includedir] + - svn_includedirs + ["subvertpy"]) + kwargs["include_dirs"] = ( + [apr_includedir, apu_includedir] + svn_includedirs + ["subvertpy"] + ) kwargs["library_dirs"] = svn_libdirs # Note that the apr-util link flags are not included here, as # subvertpy only uses some apr util constants but does not use # the library directly. - kwargs["extra_link_args"] = ( - apr_link_flags + apu_link_flags + svn_link_flags) - if os.name == 'nt': + kwargs["extra_link_args"] = apr_link_flags + apu_link_flags + svn_link_flags + if os.name == "nt": # APR needs WIN32 defined. kwargs["define_macros"] = [("WIN32", None)] - if sys.platform == 'darwin': + if sys.platform == "darwin": # on Mac OS X, we need to check for Keychain availability if is_keychain_provider_available(): if "define_macros" not in kwargs: kwargs["define_macros"] = [] - kwargs["define_macros"].extend(( - ('DARWIN', None), - ('SVN_KEYCHAIN_PROVIDER_AVAILABLE', '1')) - ) - if sys.platform in ('darwin', 'linux'): + kwargs["define_macros"].extend( + (("DARWIN", None), ("SVN_KEYCHAIN_PROVIDER_AVAILABLE", "1")) + ) + if sys.platform in ("darwin", "linux"): kwargs["extra_compile_args"] = [ "-std=c99" # for GCC >=15 as it is using c23 by default ] @@ -210,7 +210,7 @@ def source_path(filename): "mswsock", "version", "ole32", - ] +] repos_deep_deps = [ @@ -222,7 +222,7 @@ def source_path(filename): "libsvn_delta-1", "libapr-1", "libaprutil-1", - ] +] ra_deep_deps = [ @@ -231,7 +231,7 @@ def source_path(filename): "libsvn_repos-1", "libapr-1", "libaprutil-1", - ] +] deep_deps = { @@ -245,33 +245,45 @@ def subvertpy_modules(): return [ SvnExtension( "subvertpy.client", - [source_path(n) - for n in ("client.c", "editor.c", "util.c", "_ra.c", "wc.c")], - libraries=["svn_client-1", "svn_diff-1", "svn_delta-1", - "svn_wc-1", "svn_ra-1", "svn_subr-1"]), + [ + source_path(n) + for n in ("client.c", "editor.c", "util.c", "_ra.c", "wc.c") + ], + libraries=[ + "svn_client-1", + "svn_diff-1", + "svn_delta-1", + "svn_wc-1", + "svn_ra-1", + "svn_subr-1", + ], + ), SvnExtension( "subvertpy._ra", [source_path(n) for n in ("_ra.c", "util.c", "editor.c")], - libraries=["svn_delta-1", "svn_ra-1", "svn_subr-1"]), + libraries=["svn_delta-1", "svn_ra-1", "svn_subr-1"], + ), SvnExtension( - "subvertpy.repos", [source_path(n) for n in ("repos.c", "util.c")], - libraries=["svn_repos-1", "svn_subr-1"]), + "subvertpy.repos", + [source_path(n) for n in ("repos.c", "util.c")], + libraries=["svn_repos-1", "svn_subr-1"], + ), SvnExtension( "subvertpy.wc", - [source_path(n) for n in - ["wc.c", "util.c", "editor.c"]], - libraries=["svn_wc-1", "svn_diff-1", "svn_delta-1", "svn_subr-1"]), + [source_path(n) for n in ["wc.c", "util.c", "editor.c"]], + libraries=["svn_wc-1", "svn_diff-1", "svn_delta-1", "svn_subr-1"], + ), SvnExtension( "subvertpy.subr", - [source_path(n) - for n in ["util.c", "subr.c"]], - libraries=["svn_subr-1"]), - ] + [source_path(n) for n in ["util.c", "subr.c"]], + libraries=["svn_subr-1"], + ), + ] def package_data(): - if sys.platform == 'win32': - return {'subvertpy': ['subvertpy/*.dll']} + if sys.platform == "win32": + return {"subvertpy": ["subvertpy/*.dll"]} else: return {} @@ -281,17 +293,18 @@ def package_data(): if __name__ == "__main__": - setup(name='subvertpy', - description='Alternative Python bindings for Subversion', - keywords='svn subvertpy subversion bindings', - version=subvertpy_version_string, - url='https://jelmer.uk/subvertpy', - download_url="https://jelmer.uk/subvertpy/tarball/subvertpy-%s/" % ( - subvertpy_version_string, ), - license='LGPLv2.1 or later', - author='Jelmer Vernooij', - author_email='jelmer@jelmer.uk', - long_description=""" + setup( + name="subvertpy", + description="Alternative Python bindings for Subversion", + keywords="svn subvertpy subversion bindings", + version=subvertpy_version_string, + url="https://jelmer.uk/subvertpy", + download_url="https://jelmer.uk/subvertpy/tarball/subvertpy-%s/" + % (subvertpy_version_string,), + license="LGPLv2.1 or later", + author="Jelmer Vernooij", + author_email="jelmer@jelmer.uk", + long_description=""" Alternative Python bindings for Subversion. The goal is to have complete, portable and "Pythonic" Python bindings. @@ -311,20 +324,20 @@ def package_data(): work on Windows as well as most POSIX-based platforms (including Linux, BSDs and Mac OS X). """, - packages=['subvertpy', 'subvertpy.tests'], - package_data=package_data(), - ext_modules=subvertpy_modules(), - scripts=['bin/subvertpy-fast-export'], - classifiers=[ - 'Development Status :: 4 - Beta', - 'License :: OSI Approved :: GNU General Public ' - 'License v2 or later (GPLv2+)', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: Implementation :: CPython', - 'Operating System :: POSIX', - 'Topic :: Software Development :: Version Control', - ], - ) + packages=["subvertpy", "subvertpy.tests"], + package_data=package_data(), + ext_modules=subvertpy_modules(), + scripts=["bin/subvertpy-fast-export"], + classifiers=[ + "Development Status :: 4 - Beta", + "License :: OSI Approved :: GNU General Public " + "License v2 or later (GPLv2+)", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: Implementation :: CPython", + "Operating System :: POSIX", + "Topic :: Software Development :: Version Control", + ], + ) diff --git a/subvertpy/__init__.py b/subvertpy/__init__.py index 496eefe0e..7f530f65f 100644 --- a/subvertpy/__init__.py +++ b/subvertpy/__init__.py @@ -92,8 +92,8 @@ ERR_EAI_NONAME = 670008 ERR_UNKNOWN_HOSTNAME = 670002 -AUTH_PARAM_DEFAULT_USERNAME = 'svn:auth:username' -AUTH_PARAM_DEFAULT_PASSWORD = 'svn:auth:password' +AUTH_PARAM_DEFAULT_USERNAME = "svn:auth:username" +AUTH_PARAM_DEFAULT_PASSWORD = "svn:auth:password" SSL_NOTYETVALID = 0x00000001 SSL_EXPIRED = 0x00000002 @@ -117,6 +117,7 @@ def _check_mtime(m): :param m: Python module that is a C extension """ import os + (base, _) = os.path.splitext(m.__file__) c_file = "%s.c" % base if not os.path.exists(c_file): @@ -128,8 +129,8 @@ def _check_mtime(m): if platform.system() == "Windows": dll_dirs = [ - dll_dir for dll_dir in os.environ.get( - "SUBVERTPY_DLL_PATH", "").split(';') + dll_dir + for dll_dir in os.environ.get("SUBVERTPY_DLL_PATH", "").split(";") if dll_dir ] for path in dll_dirs: @@ -139,9 +140,11 @@ def _check_mtime(m): try: from subvertpy import client, _ra, repos, wc + for x in client, _ra, repos, wc: if not _check_mtime(x): from warnings import warn + warn("subvertpy extensions are outdated and need to be rebuilt") break except ImportError as e: diff --git a/subvertpy/delta.py b/subvertpy/delta.py index 0e3544909..9384d2313 100644 --- a/subvertpy/delta.py +++ b/subvertpy/delta.py @@ -20,7 +20,7 @@ from hashlib import ( md5, - ) +) TXDELTA_SOURCE = 0 @@ -47,7 +47,7 @@ def apply_txdelta_window(sbuf, window): :return: Target buffer """ (sview_offset, sview_len, tview_len, src_ops, ops, new_data) = window - sview = sbuf[sview_offset:sview_offset+sview_len] + sview = sbuf[sview_offset : sview_offset + sview_len] tview = txdelta_apply_ops(src_ops, ops, new_data, sview) if len(tview) != tview_len: raise AssertionError("%d != %d" % (len(tview), tview_len)) @@ -66,6 +66,7 @@ def apply_window(window): if window is None: return # Last call target_chunks.append(apply_txdelta_window(sbuf, window)) + return apply_window @@ -75,10 +76,12 @@ def apply_txdelta_handler(sbuf, target_stream): :param sbuf: Source buffer :param target_stream: Target stream """ + def apply_window(window): if window is None: return # Last call target_stream.write(apply_txdelta_window(sbuf, window)) + return apply_window @@ -92,15 +95,15 @@ def txdelta_apply_ops(src_ops, ops, new_data, sview): :return: Result data """ tview = bytearray() - for (action, offset, length) in ops: + for action, offset, length in ops: if action == TXDELTA_SOURCE: # Copy from source area. - tview.extend(sview[offset:offset+length]) + tview.extend(sview[offset : offset + length]) elif action == TXDELTA_TARGET: for i in range(length): - tview.append(tview[offset+i]) + tview.append(tview[offset + i]) elif action == TXDELTA_NEW: - tview.extend(new_data[offset:offset+length]) + tview.extend(new_data[offset : offset + length]) else: raise Exception("Invalid delta instruction code") return tview @@ -152,7 +155,7 @@ def encode_length(len): cont = 1 else: cont = 0 - ret.append(((len >> (n * 7)) & 0x7f) | (cont << 7)) + ret.append(((len >> (n * 7)) & 0x7F) | (cont << 7)) return ret @@ -167,7 +170,7 @@ def decode_length(text): ret = 0 next = True while next: - ret = (ret << 7) | (text[0] & 0x7f) + ret = (ret << 7) | (text[0] & 0x7F) next = (text[0] >> 7) & 0x1 text = text[1:] return ret, text @@ -183,7 +186,7 @@ def pack_svndiff_instruction(diff_params): :return: encoded text """ (action, offset, length) = diff_params - if length < 0x3f: + if length < 0x3F: text = bytearray(((action << 6) + length,)) else: text = bytearray((action << 6,)) + encode_length(length) @@ -199,7 +202,7 @@ def unpack_svndiff_instruction(text): :return: tuple with operation, remaining text """ action = text[0] >> 6 - length = text[0] & 0x3f + length = text[0] & 0x3F text = text[1:] assert action in (TXDELTA_NEW, TXDELTA_SOURCE, TXDELTA_TARGET) if length == 0: @@ -221,9 +224,11 @@ def pack_svndiff0_window(window): :return: Packed diff (as bytestring) """ (sview_offset, sview_len, tview_len, src_ops, ops, new_data) = window - ret = (encode_length(sview_offset) + - encode_length(sview_len) + - encode_length(tview_len)) + ret = ( + encode_length(sview_offset) + + encode_length(sview_len) + + encode_length(tview_len) + ) instrdata = bytearray() for op in ops: diff --git a/subvertpy/marshall.py b/subvertpy/marshall.py index cdd843ea9..8263a007c 100644 --- a/subvertpy/marshall.py +++ b/subvertpy/marshall.py @@ -30,7 +30,8 @@ def __repr__(self): return self.txt def __eq__(self, other): - return (isinstance(other, literal) and self.txt == other.txt) + return isinstance(other, literal) and self.txt == other.txt + # 1. Syntactic structure # ---------------------- @@ -87,7 +88,7 @@ def unmarshall(x): :param x: Bytes to parse :return: tuple with unpacked item and remaining bytes """ - whitespace = frozenset(b'\n ') + whitespace = frozenset(b"\n ") if len(x) == 0: raise NeedMoreData("Not enough data") if x[0:1] == b"(": # list follows @@ -107,7 +108,7 @@ def unmarshall(x): if len(x) <= 1: raise NeedMoreData("Missing whitespace") - if not x[1] in whitespace: + if x[1] not in whitespace: raise MarshallError("Expected space, got '%c'" % x[1]) return (x[2:], ret) @@ -124,7 +125,7 @@ def unmarshall(x): elif x[0:1] == b":": if len(x) < num: raise NeedMoreData("Expected string of length %r" % num) - return (x[num+2:], x[1:num+1]) + return (x[num + 2 :], x[1 : num + 1]) elif not x: raise MarshallError("Expected whitespace, got end of string.") else: @@ -133,7 +134,7 @@ def unmarshall(x): ret = bytearray() # Parse literal try: - while x[:1].isalpha() or x[:1].isdigit() or x[0:1] == b'-': + while x[:1].isalpha() or x[:1].isdigit() or x[0:1] == b"-": ret.append(x[0]) x = x[1:] except IndexError: @@ -142,7 +143,7 @@ def unmarshall(x): if not x: raise MarshallError("Expected whitespace, got end of string.") - if not x[0] in whitespace: + if x[0] not in whitespace: raise MarshallError("Expected whitespace, got '%c'" % x[0]) return (x[1:], literal(ret.decode("ascii"))) diff --git a/subvertpy/properties.py b/subvertpy/properties.py index 84cc4cbfa..7ee53452b 100644 --- a/subvertpy/properties.py +++ b/subvertpy/properties.py @@ -22,6 +22,7 @@ import bisect import calendar import time + try: import urlparse except ImportError: @@ -38,7 +39,7 @@ def is_valid_property_name(prop): :param prop: Property name :return: Whether prop is a valid property name """ - if not prop[0].isalnum() and not prop[0] in ":_": + if not prop[0].isalnum() and prop[0] not in ":_": return False for c in prop[1:]: if not c.isalnum() and c not in "-:._": @@ -53,10 +54,18 @@ def time_to_cstring(timestamp): :return: string with date """ tm_usec = timestamp % 1000000 - (tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_wday, tm_yday, - tm_isdst) = time.gmtime(timestamp / 1000000) + (tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_wday, tm_yday, tm_isdst) = ( + time.gmtime(timestamp / 1000000) + ) return "%04d-%02d-%02dT%02d:%02d:%02d.%06dZ" % ( - tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_usec) + tm_year, + tm_mon, + tm_mday, + tm_hour, + tm_min, + tm_sec, + tm_usec, + ) def time_from_cstring(text): @@ -69,7 +78,7 @@ def time_from_cstring(text): assert usecstr[-1] == "Z" tm_usec = int(usecstr[:-1]) tm = time.strptime(basestr, "%Y-%m-%dT%H:%M:%S") - return (int(calendar.timegm(tm)) * 1000000 + tm_usec) + return int(calendar.timegm(tm)) * 1000000 + tm_usec def parse_externals_description(base_url, val): @@ -82,8 +91,10 @@ def parse_externals_description(base_url, val): as value. revnum is the revision number and is set to None if not applicable. """ + def is_url(u): - return ("://" in u) + return "://" in u + ret = {} for line in val.splitlines(): if line == "" or line[0] == "#": @@ -123,11 +134,13 @@ def is_url(u): raise InvalidExternalsDescription() if relurl.startswith("//"): raise NotImplementedError( - "Relative to the scheme externals not yet supported") + "Relative to the scheme externals not yet supported" + ) if relurl.startswith("^/"): raise NotImplementedError( - "Relative to the repository root externals not yet supported") - ret[path] = (revno, urlparse.urljoin(base_url+"/", relurl)) + "Relative to the repository root externals not yet supported" + ) + ret[path] = (revno, urlparse.urljoin(base_url + "/", relurl)) return ret @@ -162,6 +175,7 @@ def generate_mergeinfo_property(merges): :param merges: dictionary mapping paths to lists of ranges :return: Property contents """ + def formatrange(range_params): (start, end, inheritable) = range_params suffix = "" @@ -171,8 +185,9 @@ def formatrange(range_params): return "%d%s" % (start, suffix) else: return "%d-%d%s" % (start, end, suffix) + text = "" - for (path, ranges) in merges.items(): + for path, ranges in merges.items(): assert path.startswith("/") text += "%s:%s\n" % (path, ",".join(map(formatrange, ranges))) return text @@ -188,8 +203,8 @@ def range_includes_revnum(ranges, revnum): i = bisect.bisect(ranges, (revnum, revnum, True)) if i == 0: return False - (start, end, inheritable) = ranges[i-1] - return (start <= revnum <= end) + (start, end, inheritable) = ranges[i - 1] + return start <= revnum <= end def range_add_revnum(ranges, revnum, inheritable=True): @@ -207,19 +222,19 @@ def range_add_revnum(ranges, revnum, inheritable=True): return ranges i = bisect.bisect(ranges, item) if i > 0: - (start, end, inh) = ranges[i-1] - if (start <= revnum <= end): + (start, end, inh) = ranges[i - 1] + if start <= revnum <= end: # already there return ranges - if end == revnum-1: + if end == revnum - 1: # Extend previous range - ranges[i-1] = (start, end+1, inh) + ranges[i - 1] = (start, end + 1, inh) return ranges if i < len(ranges): (start, end, inh) = ranges[i] - if start-1 == revnum: + if start - 1 == revnum: # Extend next range - ranges[i] = (start-1, end, inh) + ranges[i] = (start - 1, end, inh) return ranges ranges.insert(i, item) return ranges @@ -255,25 +270,25 @@ def mergeinfo_add_revision(mergeinfo, path, revnum): return mergeinfo -PROP_EXECUTABLE = 'svn:executable' -PROP_EXECUTABLE_VALUE = b'*' -PROP_EXTERNALS = 'svn:externals' -PROP_IGNORE = 'svn:ignore' -PROP_KEYWORDS = 'svn:keywords' -PROP_MIME_TYPE = 'svn:mime-type' -PROP_MERGEINFO = 'svn:mergeinfo' -PROP_NEEDS_LOCK = 'svn:needs-lock' -PROP_NEEDS_LOCK_VALUE = b'*' -PROP_PREFIX = 'svn:' -PROP_SPECIAL = 'svn:special' -PROP_SPECIAL_VALUE = b'*' -PROP_WC_PREFIX = 'svn:wc:' -PROP_ENTRY_PREFIX = 'svn:entry' -PROP_ENTRY_COMMITTED_DATE = 'svn:entry:committed-date' -PROP_ENTRY_COMMITTED_REV = 'svn:entry:committed-rev' -PROP_ENTRY_LAST_AUTHOR = 'svn:entry:last-author' -PROP_ENTRY_LOCK_TOKEN = 'svn:entry:lock-token' -PROP_ENTRY_UUID = 'svn:entry:uuid' +PROP_EXECUTABLE = "svn:executable" +PROP_EXECUTABLE_VALUE = b"*" +PROP_EXTERNALS = "svn:externals" +PROP_IGNORE = "svn:ignore" +PROP_KEYWORDS = "svn:keywords" +PROP_MIME_TYPE = "svn:mime-type" +PROP_MERGEINFO = "svn:mergeinfo" +PROP_NEEDS_LOCK = "svn:needs-lock" +PROP_NEEDS_LOCK_VALUE = b"*" +PROP_PREFIX = "svn:" +PROP_SPECIAL = "svn:special" +PROP_SPECIAL_VALUE = b"*" +PROP_WC_PREFIX = "svn:wc:" +PROP_ENTRY_PREFIX = "svn:entry" +PROP_ENTRY_COMMITTED_DATE = "svn:entry:committed-date" +PROP_ENTRY_COMMITTED_REV = "svn:entry:committed-rev" +PROP_ENTRY_LAST_AUTHOR = "svn:entry:last-author" +PROP_ENTRY_LOCK_TOKEN = "svn:entry:lock-token" +PROP_ENTRY_UUID = "svn:entry:uuid" PROP_REVISION_LOG = "svn:log" PROP_REVISION_AUTHOR = "svn:author" diff --git a/subvertpy/ra.py b/subvertpy/ra.py index 95011702b..0c147bbe9 100644 --- a/subvertpy/ra.py +++ b/subvertpy/ra.py @@ -29,13 +29,13 @@ from urllib.parse import splittype url_handlers = { - "svn": _ra.RemoteAccess, - # "svn": ra_svn.Client, - "svn+ssh": _ra.RemoteAccess, - # "svn+ssh": ra_svn.Client, - "http": _ra.RemoteAccess, - "https": _ra.RemoteAccess, - "file": _ra.RemoteAccess, + "svn": _ra.RemoteAccess, + # "svn": ra_svn.Client, + "svn+ssh": _ra.RemoteAccess, + # "svn+ssh": ra_svn.Client, + "http": _ra.RemoteAccess, + "https": _ra.RemoteAccess, + "file": _ra.RemoteAccess, } diff --git a/subvertpy/ra_svn.py b/subvertpy/ra_svn.py index eae6a3110..8e6dab7c0 100644 --- a/subvertpy/ra_svn.py +++ b/subvertpy/ra_svn.py @@ -26,6 +26,7 @@ import socket import subprocess from errno import EPIPE + try: import urlparse except ImportError: @@ -40,18 +41,18 @@ NODE_NONE, SubversionException, properties, - ) +) from subvertpy.delta import ( pack_svndiff0_window, unpack_svndiff0, SVNDIFF0_HEADER, - ) +) from subvertpy.marshall import ( NeedMoreData, literal, marshall, unmarshall, - ) +) from subvertpy.ra import ( DIRENT_CREATED_REV, DIRENT_HAS_PROPS, @@ -59,16 +60,16 @@ DIRENT_LAST_AUTHOR, DIRENT_SIZE, DIRENT_TIME, - ) +) from subvertpy.server import ( generate_random_id, - ) +) class SSHSubprocess(object): """A socket-like object that talks to an ssh subprocess via pipes.""" - __slots__ = ('proc') + __slots__ = "proc" def __init__(self, proc): self.proc = proc @@ -89,17 +90,16 @@ def get_filelike_channels(self): class SSHVendor(object): - def connect_ssh(self, username, password, host, port, command): - args = ['ssh', '-x'] + args = ["ssh", "-x"] if port is not None: - args.extend(['-p', str(port)]) + args.extend(["-p", str(port)]) if username is not None: host = "%s@%s" % (username, host) args.append(host) - proc = subprocess.Popen(args + command, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE) + proc = subprocess.Popen( + args + command, stdin=subprocess.PIPE, stdout=subprocess.PIPE + ) return SSHSubprocess(proc) @@ -108,7 +108,6 @@ def connect_ssh(self, username, password, host, port, command): class SVNConnection(object): - def __init__(self, recv_fn, send_fn): self.inbuffer = "" self.recv_fn = recv_fn @@ -158,8 +157,7 @@ def feed_editor(conn, editor): if len(args[3]) == 0: token = tokens[args[1]].add_directory(args[0]) else: - token = tokens[args[1]].add_directory( - args[0], args[3][0], args[4][0]) + token = tokens[args[1]].add_directory(args[0], args[3][0], args[4][0]) tokens[args[2]] = token elif command == "open-dir": tokens[args[2]] = tokens[args[1]].open_directory(args[0], args[3]) @@ -176,18 +174,15 @@ def feed_editor(conn, editor): if len(args[3]) == 0: token = tokens[args[1]].add_file(args[0]) else: - token = tokens[args[1]].add_file( - args[0], args[3][0], args[4][0]) + token = tokens[args[1]].add_file(args[0], args[3][0], args[4][0]) tokens[args[2]] = token elif command == "open-file": tokens[args[2]] = tokens[args[1]].open_file(args[0], args[3]) elif command == "apply-textdelta": if len(args[1]) == 0: - txdelta_handler[args[0]] = tokens[args[0]].apply_textdelta( - None) + txdelta_handler[args[0]] = tokens[args[0]].apply_textdelta(None) else: - txdelta_handler[args[0]] = tokens[args[0]].apply_textdelta( - args[1][0]) + txdelta_handler[args[0]] = tokens[args[0]].apply_textdelta(args[1][0]) diff[args[0]] = "" elif command == "textdelta-chunk": diff[args[0]] += args[1] @@ -217,15 +212,13 @@ def feed_editor(conn, editor): class Reporter(object): - - __slots__ = ('conn', 'editor') + __slots__ = ("conn", "editor") def __init__(self, conn, editor): self.conn = conn self.editor = editor - def set_path(self, path, rev, start_empty=False, lock_token=None, - depth=None): + def set_path(self, path, rev, start_empty=False, lock_token=None, depth=None): args = [path, rev, start_empty] if lock_token is not None: args.append([lock_token]) @@ -239,8 +232,7 @@ def set_path(self, path, rev, start_empty=False, lock_token=None, def delete_path(self, path): self.conn.send_msg([literal("delete-path"), [path]]) - def link_path(self, path, url, rev, start_empty=False, lock_token=None, - depth=None): + def link_path(self, path, url, rev, start_empty=False, lock_token=None, depth=None): args = [path, url, rev, start_empty] if lock_token is not None: args.append([lock_token]) @@ -263,8 +255,7 @@ def abort(self): class Editor(object): - - __slots__ = ('conn') + __slots__ = "conn" def __init__(self, conn): self.conn = conn @@ -290,8 +281,7 @@ def abort(self): class DirectoryEditor(object): - - __slots__ = ('conn', 'id') + __slots__ = ("conn", "id") def __init__(self, conn, id): self.conn = conn @@ -305,21 +295,18 @@ def add_file(self, path, copyfrom_path=None, copyfrom_rev=-1): copyfrom_data = [copyfrom_path, copyfrom_rev] else: copyfrom_data = [] - self.conn.send_msg([literal("add-file"), - [path, self.id, child, copyfrom_data]]) + self.conn.send_msg([literal("add-file"), [path, self.id, child, copyfrom_data]]) return FileEditor(self.conn, child) def open_file(self, path, base_revnum): self._is_last_open() child = generate_random_id() - self.conn.send_msg([literal("open-file"), - [path, self.id, child, base_revnum]]) + self.conn.send_msg([literal("open-file"), [path, self.id, child, base_revnum]]) return FileEditor(self.conn, child) def delete_entry(self, path, base_revnum): self._is_last_open() - self.conn.send_msg([literal("delete-entry"), - [path, base_revnum, self.id]]) + self.conn.send_msg([literal("delete-entry"), [path, base_revnum, self.id]]) def add_directory(self, path, copyfrom_path=None, copyfrom_rev=-1): self._is_last_open() @@ -328,15 +315,13 @@ def add_directory(self, path, copyfrom_path=None, copyfrom_rev=-1): copyfrom_data = [copyfrom_path, copyfrom_rev] else: copyfrom_data = [] - self.conn.send_msg([literal("add-dir"), - [path, self.id, child, copyfrom_data]]) + self.conn.send_msg([literal("add-dir"), [path, self.id, child, copyfrom_data]]) return DirectoryEditor(self.conn, child) def open_directory(self, path, base_revnum): self._is_last_open() child = generate_random_id() - self.conn.send_msg([literal("open-dir"), - [path, self.id, child, base_revnum]]) + self.conn.send_msg([literal("open-dir"), [path, self.id, child, base_revnum]]) return DirectoryEditor(self.conn, child) def change_prop(self, name, value): @@ -345,8 +330,7 @@ def change_prop(self, name, value): value = [] else: value = [value] - self.conn.send_msg([literal("change-dir-prop"), - [self.id, name, value]]) + self.conn.send_msg([literal("change-dir-prop"), [self.id, name, value]]) def _is_last_open(self): assert self.conn._open_ids[-1] == self.id @@ -358,8 +342,7 @@ def close(self): class FileEditor(object): - - __slots__ = ('conn', 'id') + __slots__ = ("conn", "id") def __init__(self, conn, id): self.conn = conn @@ -384,17 +367,17 @@ def apply_textdelta(self, base_checksum=None): base_check = [] else: base_check = [base_checksum] - self.conn.send_msg([literal("apply-textdelta"), - [self.id, base_check]]) - self.conn.send_msg([literal("textdelta-chunk"), - [self.id, SVNDIFF0_HEADER]]) + self.conn.send_msg([literal("apply-textdelta"), [self.id, base_check]]) + self.conn.send_msg([literal("textdelta-chunk"), [self.id, SVNDIFF0_HEADER]]) def send_textdelta(delta): if delta is None: self.conn.send_msg([literal("textdelta-end"), [self.id]]) else: - self.conn.send_msg([literal("textdelta-chunk"), - [self.id, pack_svndiff0_window(delta)]]) + self.conn.send_msg( + [literal("textdelta-chunk"), [self.id, pack_svndiff0_window(delta)]] + ) + return send_textdelta def change_prop(self, name, value): @@ -403,8 +386,7 @@ def change_prop(self, name, value): value = [] else: value = [value] - self.conn.send_msg([literal("change-file-prop"), - [self.id, name, value]]) + self.conn.send_msg([literal("change-file-prop"), [self.id, name, value]]) def mark_busy(unbound): @@ -429,7 +411,7 @@ def unmarshall_dirent(d): "size": d[2], "has-props": bool(d[3]), "created-rev": d[4], - } + } if d[5] != []: ret["created-date"] = d[5] if d[6] != []: @@ -438,9 +420,15 @@ def unmarshall_dirent(d): class SVNClient(SVNConnection): - - def __init__(self, url, progress_cb=None, auth=None, config=None, - client_string_func=None, open_tmp_file_func=None): + def __init__( + self, + url, + progress_cb=None, + auth=None, + config=None, + client_string_func=None, + open_tmp_file_func=None, + ): self.url = url (type, opaque) = urlparse.splittype(url) assert type in ("svn", "svn+ssh") @@ -455,19 +443,23 @@ def __init__(self, url, progress_cb=None, auth=None, config=None, else: (recv_func, send_func) = self._connect_ssh(host) super(SVNClient, self).__init__(recv_func, send_func) - (min_version, max_version, _, self._server_capabilities) = ( - self._recv_greeting()) + (min_version, max_version, _, self._server_capabilities) = self._recv_greeting() self.send_msg( - [max_version, - [literal(x) for x in CAPABILITIES - if x in self._server_capabilities], - self.url]) + [ + max_version, + [literal(x) for x in CAPABILITIES if x in self._server_capabilities], + self.url, + ] + ) (self._server_mechanisms, mech_arg) = self._unpack() if self._server_mechanisms != []: # FIXME: Support other mechanisms as well - self.send_msg([literal("ANONYMOUS"), - [base64.b64encode( - "anonymous@%s" % socket.gethostname())]]) + self.send_msg( + [ + literal("ANONYMOUS"), + [base64.b64encode("anonymous@%s" % socket.gethostname())], + ] + ) self.recv_msg() msg = self._unpack() if len(msg) > 2: @@ -499,11 +491,11 @@ def _recv_greeting(self): def _connect(self, host): (host, port) = urlparse.splitnport(host, SVN_PORT) sockaddrs = socket.getaddrinfo( - host, port, socket.AF_UNSPEC, - socket.SOCK_STREAM, 0, 0) + host, port, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, 0 + ) self._socket = None - last_err = RuntimeError('no addresses for %s:%s' % (host, port)) - for (family, socktype, proto, canonname, sockaddr) in sockaddrs: + last_err = RuntimeError("no addresses for %s:%s" % (host, port)) + for family, socktype, proto, canonname, sockaddr in sockaddrs: try: self._socket = socket.socket(family, socktype, proto) self._socket.connect(sockaddr) @@ -527,7 +519,8 @@ def _connect_ssh(self, host): password = None (host, port) = urlparse.splitnport(host, 22) self._tunnel = get_ssh_vendor().connect_ssh( - user, password, host, port, ["svnserve", "-t"]) + user, password, host, port, ["svnserve", "-t"] + ) return (self._tunnel.recv, self._tunnel.send) def get_file_revs(self, path, start, end, file_rev_handler): @@ -535,8 +528,9 @@ def get_file_revs(self, path, start, end, file_rev_handler): @mark_busy def get_locations(self, path, peg_revision, location_revisions): - self.send_msg([literal("get-locations"), [path, peg_revision, - location_revisions]]) + self.send_msg( + [literal("get-locations"), [path, peg_revision, location_revisions]] + ) self._recv_ack() ret = {} while True: @@ -558,12 +552,12 @@ def lock(self, path_revs, comment, steal_lock, lock_func): def unlock(self, path_tokens, break_lock, lock_func): raise NotImplementedError(self.unlock) - def mergeinfo(self, paths, revision=-1, inherit=None, - include_descendants=False): + def mergeinfo(self, paths, revision=-1, inherit=None, include_descendants=False): raise NotImplementedError(self.mergeinfo) - def location_segments(self, path, start_revision, end_revision, - include_merged_revisions=False): + def location_segments( + self, path, start_revision, end_revision, include_merged_revisions=False + ): args = [path] if start_revision is None or start_revision == -1: args.append([]) @@ -600,8 +594,12 @@ def check_path(self, path, revision=None): self.send_msg([literal("check-path"), args]) self._recv_ack() ret = self._unpack()[0] - return {"dir": NODE_DIR, "file": NODE_FILE, "unknown": NODE_UNKNOWN, - "none": NODE_NONE}[ret] + return { + "dir": NODE_DIR, + "file": NODE_FILE, + "unknown": NODE_UNKNOWN, + "none": NODE_NONE, + }[ret] def get_lock(self, path): self.send_msg([literal("get-lock"), [path]]) @@ -613,8 +611,9 @@ def get_lock(self, path): return ret[0] @mark_busy - def get_dir(self, path, revision=-1, dirent_fields=0, want_props=True, - want_contents=True): + def get_dir( + self, path, revision=-1, dirent_fields=0, want_props=True, want_contents=True + ): args = [path] if revision is None or revision == -1: args.append([]) @@ -677,8 +676,9 @@ def change_rev_prop(self, rev, name, value): self._recv_ack() self._unparse() - def get_commit_editor(self, revprops, callback=None, lock_tokens=None, - keep_locks=False): + def get_commit_editor( + self, revprops, callback=None, lock_tokens=None, keep_locks=False + ): args = [revprops[properties.PROP_REVISION_LOG]] if lock_tokens is not None: args.append(list(lock_tokens.items())) @@ -706,21 +706,24 @@ def rev_prop(self, revision, name): return ret[0] @mark_busy - def replay(self, revision, low_water_mark, update_editor, - send_deltas=True): - self.send_msg([literal("replay"), [revision, low_water_mark, - send_deltas]]) + def replay(self, revision, low_water_mark, update_editor, send_deltas=True): + self.send_msg([literal("replay"), [revision, low_water_mark, send_deltas]]) self._recv_ack() feed_editor(self, update_editor) self._unpack() @mark_busy - def replay_range(self, start_revision, end_revision, low_water_mark, cbs, - send_deltas=True): - self.send_msg([literal("replay-range"), [start_revision, end_revision, - low_water_mark, send_deltas]]) + def replay_range( + self, start_revision, end_revision, low_water_mark, cbs, send_deltas=True + ): + self.send_msg( + [ + literal("replay-range"), + [start_revision, end_revision, low_water_mark, send_deltas], + ] + ) self._recv_ack() - for i in range(start_revision, end_revision+1): + for i in range(start_revision, end_revision + 1): msg = self.recv_msg() assert msg[0] == "revprops" edit = cbs[0](i, dict(msg[1])) @@ -728,8 +731,15 @@ def replay_range(self, start_revision, end_revision, low_water_mark, cbs, cbs[1](i, dict(msg[1]), edit) self._unpack() - def do_switch(self, revision_to_update_to, update_target, recurse, - switch_url, update_editor, depth=None): + def do_switch( + self, + revision_to_update_to, + update_target, + recurse, + switch_url, + update_editor, + depth=None, + ): args = [] if revision_to_update_to is None or revision_to_update_to == -1: args.append([]) @@ -750,8 +760,9 @@ def do_switch(self, revision_to_update_to, update_target, recurse, self.busy = False raise - def do_update(self, revision_to_update_to, update_target, recurse, - update_editor, depth=None): + def do_update( + self, revision_to_update_to, update_target, recurse, update_editor, depth=None + ): args = [] if revision_to_update_to is None or revision_to_update_to == -1: args.append([]) @@ -771,16 +782,23 @@ def do_update(self, revision_to_update_to, update_target, recurse, self.busy = False raise - def do_diff(self, revision_to_update, diff_target, versus_url, diff_editor, - recurse=True, ignore_ancestry=False, text_deltas=False, - depth=None): + def do_diff( + self, + revision_to_update, + diff_target, + versus_url, + diff_editor, + recurse=True, + ignore_ancestry=False, + text_deltas=False, + depth=None, + ): args = [] if revision_to_update is None or revision_to_update == -1: args.append([]) else: args.append([revision_to_update]) - args += [diff_target, recurse, ignore_ancestry, versus_url, - text_deltas] + args += [diff_target, recurse, ignore_ancestry, versus_url, text_deltas] if depth is not None: args.append(literal(depth)) self.busy = True @@ -818,9 +836,17 @@ def get_uuid(self): return self._uuid @mark_busy - def log(self, paths, start, end, limit=0, discover_changed_paths=True, - strict_node_history=True, include_merged_revisions=True, - revprops=None): + def log( + self, + paths, + start, + end, + limit=0, + discover_changed_paths=True, + strict_node_history=True, + include_merged_revisions=True, + revprops=None, + ): args = [paths] if start is None or start == -1: args.append([]) @@ -877,7 +903,7 @@ def log(self, paths, start, end, limit=0, discover_changed_paths=True, self._unpack() def get_log(self, callback, *args, **kwargs): - for (paths, rev, props, has_children) in self.log(*args, **kwargs): + for paths, rev, props, has_children in self.log(*args, **kwargs): if has_children is None: callback(paths, rev, props) else: @@ -891,7 +917,6 @@ def get_log(self, callback, *args, **kwargs): class SVNServer(SVNConnection): - def __init__(self, backend, recv_fn, send_fn, logf=None): self.backend = backend self._stop = False @@ -899,8 +924,11 @@ def __init__(self, backend, recv_fn, send_fn, logf=None): super(SVNServer, self).__init__(recv_fn, send_fn) self.send_success( - MIN_VERSION, MAX_VERSION, [literal(x) for x in MECHANISMS], - [literal(x) for x in CAPABILITIES]) + MIN_VERSION, + MAX_VERSION, + [literal(x) for x in MECHANISMS], + [literal(x) for x in CAPABILITIES], + ) def send_mechs(self): self.send_success([literal(x) for x in MECHANISMS], "") @@ -913,8 +941,8 @@ def send_ack(self): def send_unknown(self, cmd): self.send_failure( - [ERR_RA_SVN_UNKNOWN_CMD, - "Unknown command '%s'" % cmd, __file__, 52]) + [ERR_RA_SVN_UNKNOWN_CMD, "Unknown command '%s'" % cmd, __file__, 52] + ) def get_latest_rev(self): self.send_ack() @@ -927,15 +955,29 @@ def check_path(self, path, rev): revnum = rev[0] kind = self.repo_backend.check_path(path, revnum) self.send_ack() - self.send_success(literal({ - NODE_NONE: "none", - NODE_DIR: "dir", - NODE_FILE: "file", - NODE_UNKNOWN: "unknown"}[kind])) - - def log(self, target_path, start_rev, end_rev, changed_paths, - strict_node, limit=None, include_merged_revisions=False, - all_revprops=None, revprops=None): + self.send_success( + literal( + { + NODE_NONE: "none", + NODE_DIR: "dir", + NODE_FILE: "file", + NODE_UNKNOWN: "unknown", + }[kind] + ) + ) + + def log( + self, + target_path, + start_rev, + end_rev, + changed_paths, + strict_node, + limit=None, + include_merged_revisions=False, + all_revprops=None, + revprops=None, + ): def send_revision(revno, author, date, message, changed_paths=None): changes = [] if changed_paths is not None: @@ -945,6 +987,7 @@ def send_revision(revno, author, date, message, changed_paths=None): else: changes.append((p, literal(action), ())) self.send_msg([changes, revno, [author], [date], [message]]) + self.send_ack() if len(start_rev) == 0: start_revnum = None @@ -954,15 +997,21 @@ def send_revision(revno, author, date, message, changed_paths=None): end_revnum = None else: end_revnum = end_rev[0] - self.repo_backend.log(send_revision, target_path, start_revnum, - end_revnum, changed_paths, strict_node, limit) + self.repo_backend.log( + send_revision, + target_path, + start_revnum, + end_revnum, + changed_paths, + strict_node, + limit, + ) self.send_msg(literal("done")) self.send_success() def open_backend(self, url): (rooturl, location) = urlparse.splithost(url) - self.repo_backend, self.relpath = self.backend.open_repository( - location) + self.repo_backend, self.relpath = self.backend.open_repository(location) def reparent(self, parent): self.open_backend(parent) @@ -979,8 +1028,13 @@ def stat(self, path, rev): if dirent is None: self.send_success([]) else: - args = [dirent["name"], dirent["kind"], dirent["size"], - dirent["has-props"], dirent["created-rev"]] + args = [ + dirent["name"], + dirent["kind"], + dirent["size"], + dirent["has-props"], + dirent["created-rev"], + ] if "created-date" in dirent: args.append([dirent["created-date"]]) else: @@ -992,8 +1046,9 @@ def stat(self, path, rev): self.send_success([args]) def commit(self, logmsg, locks, keep_locks=False, rev_props=None): - self.send_failure([ERR_UNSUPPORTED_FEATURE, - "commit not yet supported", __file__, 42]) + self.send_failure( + [ERR_UNSUPPORTED_FEATURE, "commit not yet supported", __file__, 42] + ) def rev_proplist(self, revnum): self.send_ack() @@ -1016,8 +1071,7 @@ def get_locations(self, path, peg_revnum, revnums): self.send_msg(literal("done")) self.send_success() - def update(self, rev, target, recurse, depth=None, - send_copyfrom_param=True): + def update(self, rev, target, recurse, depth=None, send_copyfrom_param=True): self.send_ack() while True: msg = self.recv_msg() @@ -1037,31 +1091,30 @@ def update(self, rev, target, recurse, depth=None, if client_result[0] == "success": return else: - self.mutter("Client reported error during update: %r" % - client_result) + self.mutter("Client reported error during update: %r" % client_result) # Needs to be sent back to the client to display self.send_failure(client_result[1][0]) commands = { - "get-latest-rev": get_latest_rev, - "log": log, - "update": update, - "check-path": check_path, - "reparent": reparent, - "stat": stat, - "commit": commit, - "rev-proplist": rev_proplist, - "rev-prop": rev_prop, - "get-locations": get_locations, - # FIXME: get-dated-rev - # FIXME: get-file - # FIXME: get-dir - # FIXME: check-path - # FIXME: switch - # FIXME: status - # FIXME: diff - # FIXME: get-file-revs - # FIXME: replay + "get-latest-rev": get_latest_rev, + "log": log, + "update": update, + "check-path": check_path, + "reparent": reparent, + "stat": stat, + "commit": commit, + "rev-proplist": rev_proplist, + "rev-prop": rev_prop, + "get-locations": get_locations, + # FIXME: get-dated-rev + # FIXME: get-file + # FIXME: get-dir + # FIXME: check-path + # FIXME: switch + # FIXME: status + # FIXME: diff + # FIXME: get-file-revs + # FIXME: replay } def send_auth_request(self): @@ -1111,16 +1164,14 @@ def mutter(self, text): class TCPSVNRequestHandler(StreamRequestHandler): - def __init__(self, request, client_address, server): self._server = server - StreamRequestHandler.__init__( - self, request, client_address, server) + StreamRequestHandler.__init__(self, request, client_address, server) def handle(self): server = SVNServer( - self._server._backend, self.rfile.read, - self.wfile.write, self._server._logf) + self._server._backend, self.rfile.read, self.wfile.write, self._server._logf + ) try: server.serve() except socket.error as e: @@ -1130,7 +1181,6 @@ def handle(self): class TCPSVNServer(TCPServer): - allow_reuse_address = True serve = TCPServer.serve_forever diff --git a/subvertpy/server.py b/subvertpy/server.py index 02283eab6..141a5bc92 100644 --- a/subvertpy/server.py +++ b/subvertpy/server.py @@ -27,19 +27,27 @@ def open_repository(self, location): def generate_random_id(): """Create a UUID for a repository.""" import uuid + return str(uuid.uuid4()) class ServerRepositoryBackend(object): - def get_uuid(self): raise NotImplementedError(self.get_uuid) def get_latest_revnum(self): raise NotImplementedError(self.get_latest_revnum) - def log(self, send_revision, target_path, start_rev, end_rev, - changed_paths, strict_node, limit): + def log( + self, + send_revision, + target_path, + start_rev, + end_rev, + changed_paths, + strict_node, + limit, + ): raise NotImplementedError(self.log) def update(self, editor, revnum, target_path, recurse=True): diff --git a/subvertpy/tests/__init__.py b/subvertpy/tests/__init__.py index 71b35763b..2cb9383aa 100644 --- a/subvertpy/tests/__init__.py +++ b/subvertpy/tests/__init__.py @@ -16,8 +16,8 @@ """Tests for subvertpy.""" -__author__ = 'Jelmer Vernooij ' -__docformat__ = 'restructuredText' +__author__ = "Jelmer Vernooij " +__docformat__ = "restructuredText" from io import BytesIO import os @@ -26,6 +26,7 @@ import sys import tempfile import unittest + try: import urlparse except ImportError: @@ -41,11 +42,11 @@ properties, ra, repos, - ) +) from subvertpy.ra import ( Auth, RemoteAccess, - ) +) def rmtree_with_readonly(path): @@ -53,11 +54,13 @@ def rmtree_with_readonly(path): In Windows a read-only file cannot be removed, and shutil.rmtree fails. """ + def force_rm_handle(remove_path, path, excinfo): os.chmod( - path, - os.stat(path).st_mode | stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH) + path, os.stat(path).st_mode | stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH + ) remove_path(path) + shutil.rmtree(path, onerror=force_rm_handle) @@ -72,11 +75,14 @@ def assertIsInstance(self, obj, kls, msg=None): if not isinstance(obj, kls): if msg is None: msg = "%r is an instance of %s rather than %s" % ( - obj, obj.__class__, kls) + obj, + obj.__class__, + kls, + ) self.fail(msg) def assertIs(self, left, right, message=None): - if not (left is right): + if left is not right: if message is not None: raise AssertionError(message) else: @@ -147,8 +153,9 @@ def change_prop(self, name, value): def open_dir(self, path): self.close_children() - child = TestDirEditor(self.dir.open_directory(path, -1), self.baseurl, - self.revnum) + child = TestDirEditor( + self.dir.open_directory(path, -1), self.baseurl, self.revnum + ) self.children.append(child) return child @@ -161,24 +168,27 @@ def open_file(self, path): def add_dir(self, path, copyfrom_path=None, copyfrom_rev=-1): self.close_children() if copyfrom_path is not None: - copyfrom_path = urlparse.urljoin(self.baseurl+"/", copyfrom_path) + copyfrom_path = urlparse.urljoin(self.baseurl + "/", copyfrom_path) if copyfrom_path is not None and copyfrom_rev == -1: copyfrom_rev = self.revnum - assert (copyfrom_path is None and copyfrom_rev == -1) or \ - (copyfrom_path is not None and copyfrom_rev > -1) - child = TestDirEditor(self.dir.add_directory(path, copyfrom_path, - copyfrom_rev), self.baseurl, self.revnum) + assert (copyfrom_path is None and copyfrom_rev == -1) or ( + copyfrom_path is not None and copyfrom_rev > -1 + ) + child = TestDirEditor( + self.dir.add_directory(path, copyfrom_path, copyfrom_rev), + self.baseurl, + self.revnum, + ) self.children.append(child) return child def add_file(self, path, copyfrom_path=None, copyfrom_rev=-1): self.close_children() if copyfrom_path is not None: - copyfrom_path = urlparse.urljoin(self.baseurl+"/", copyfrom_path) + copyfrom_path = urlparse.urljoin(self.baseurl + "/", copyfrom_path) if copyfrom_path is not None and copyfrom_rev == -1: copyfrom_rev = self.revnum - child = TestFileEditor(self.dir.add_file(path, copyfrom_path, - copyfrom_rev)) + child = TestFileEditor(self.dir.add_file(path, copyfrom_path, copyfrom_rev)) self.children.append(child) return child @@ -212,11 +222,15 @@ class SubversionTestCase(TestCaseInTempDir): def _init_client(self): self.client_ctx = client.Client() - self.client_ctx.auth = Auth([ra.get_simple_provider(), - ra.get_username_provider(), - ra.get_ssl_client_cert_file_provider(), - ra.get_ssl_client_cert_pw_file_provider(), - ra.get_ssl_server_trust_file_provider()]) + self.client_ctx.auth = Auth( + [ + ra.get_simple_provider(), + ra.get_username_provider(), + ra.get_ssl_client_cert_file_provider(), + ra.get_ssl_client_cert_pw_file_provider(), + ra.get_ssl_server_trust_file_provider(), + ] + ) self.client_ctx.log_msg_func = self.log_message_func # self.client_ctx.notify_func = lambda err: mutter("Error: %s" % err) @@ -241,20 +255,18 @@ def make_repository(self, relpath, allow_revprop_changes=True): repos.create(abspath) if allow_revprop_changes: - if sys.platform == 'win32': - revprop_hook = os.path.join( - abspath, "hooks", "pre-revprop-change.bat") - with open(revprop_hook, 'w') as f: + if sys.platform == "win32": + revprop_hook = os.path.join(abspath, "hooks", "pre-revprop-change.bat") + with open(revprop_hook, "w") as f: f.write("exit 0\n") else: - revprop_hook = os.path.join( - abspath, "hooks", "pre-revprop-change") - with open(revprop_hook, 'w') as f: + revprop_hook = os.path.join(abspath, "hooks", "pre-revprop-change") + with open(revprop_hook, "w") as f: f.write("#!/bin/sh\n") os.chmod(revprop_hook, os.stat(revprop_hook).st_mode | 0o111) - if sys.platform == 'win32': - return 'file:%s' % pathname2url(abspath) + if sys.platform == "win32": + return "file:%s" % pathname2url(abspath) else: return "file://%s" % abspath @@ -317,13 +329,14 @@ def client_commit(self, dir, message=None, recursive=True): :param dir: List of paths to commit. """ - olddir = os.path.abspath('.') + olddir = os.path.abspath(".") self.next_message = message os.chdir(dir) info = [] def add_info(*args): info.append(args) + self.client_ctx.commit(["."], recursive, False, callback=add_info) os.chdir(olddir) assert len(info) == 1 @@ -349,14 +362,26 @@ def client_log(self, url, start_revnum, stop_revnum): def rcvr(orig_paths, rev, revprops, has_children=None): ret[rev] = ( - orig_paths, - revprops.get(properties.PROP_REVISION_AUTHOR), - revprops.get(properties.PROP_REVISION_DATE), - revprops.get(properties.PROP_REVISION_LOG)) - r.get_log(rcvr, [u""], start_revnum, stop_revnum, 0, True, True, - revprops=[properties.PROP_REVISION_AUTHOR, - properties.PROP_REVISION_DATE, - properties.PROP_REVISION_LOG]) + orig_paths, + revprops.get(properties.PROP_REVISION_AUTHOR), + revprops.get(properties.PROP_REVISION_DATE), + revprops.get(properties.PROP_REVISION_LOG), + ) + + r.get_log( + rcvr, + [""], + start_revnum, + stop_revnum, + 0, + True, + True, + revprops=[ + properties.PROP_REVISION_AUTHOR, + properties.PROP_REVISION_DATE, + properties.PROP_REVISION_LOG, + ], + ) return ret def client_delete(self, relpath): @@ -402,7 +427,7 @@ def build_tree(self, files): os.makedirs(os.path.dirname(name)) except OSError: pass - with open(name, 'wb') as f: + with open(name, "wb") as f: f.write(content) def make_client(self, repospath, clientpath, allow_revprop_changes=True): @@ -414,7 +439,8 @@ def make_client(self, repospath, clientpath, allow_revprop_changes=True): :return: Repository URL. """ repos_url = self.make_repository( - repospath, allow_revprop_changes=allow_revprop_changes) + repospath, allow_revprop_changes=allow_revprop_changes + ) self.make_checkout(repos_url, clientpath) return repos_url @@ -433,26 +459,28 @@ def get_commit_editor(self, url, message="Test commit"): :return: Commit editor object """ ra_ctx = RemoteAccess( - url.encode("utf-8"), auth=Auth([ra.get_username_provider()])) + url.encode("utf-8"), auth=Auth([ra.get_username_provider()]) + ) revnum = ra_ctx.get_latest_revnum() - return TestCommitEditor(ra_ctx.get_commit_editor( - {"svn:log": message}), ra_ctx.url, revnum) + return TestCommitEditor( + ra_ctx.get_commit_editor({"svn:log": message}), ra_ctx.url, revnum + ) def test_suite(): names = [ - 'client', - 'core', - 'delta', - 'marshall', - 'properties', - 'ra', - 'repos', - 'server', - 'subr', - 'wc', - ] - module_names = ['subvertpy.tests.test_' + name for name in names] + "client", + "core", + "delta", + "marshall", + "properties", + "ra", + "repos", + "server", + "subr", + "wc", + ] + module_names = ["subvertpy.tests.test_" + name for name in names] result = unittest.TestSuite() loader = unittest.TestLoader() suite = loader.loadTestsFromNames(module_names) diff --git a/subvertpy/tests/test_client.py b/subvertpy/tests/test_client.py index 3a6e3de24..ddea35c59 100644 --- a/subvertpy/tests/test_client.py +++ b/subvertpy/tests/test_client.py @@ -25,15 +25,14 @@ SubversionException, client, ra, - ) +) from subvertpy.tests import ( SubversionTestCase, TestCase, - ) +) class VersionTest(TestCase): - def test_version_length(self): self.assertEqual(4, len(client.version())) @@ -45,7 +44,6 @@ def test_api_version_later_same(self): class TestClient(SubversionTestCase): - def setUp(self): super(TestClient, self).setUp() @@ -73,7 +71,8 @@ def test_commit_start(self): self.build_tree({"dc/foo": None}) self.client = client.Client( auth=ra.Auth([ra.get_username_provider()]), - log_msg_func=lambda c: "Bmessage") + log_msg_func=lambda c: "Bmessage", + ) self.client.add("dc/foo") self.client.commit(["dc"]) r = ra.RemoteAccess(self.repos_url) @@ -96,8 +95,9 @@ def test_export_new_option(self): self.build_tree({"dc/foo": b"bla"}) self.client.add("dc/foo") self.client.commit(["dc"]) - self.client.export(self.repos_url, "de", ignore_externals=True, - ignore_keywords=True) + self.client.export( + self.repos_url, "de", ignore_externals=True, ignore_keywords=True + ) self.assertEqual(["foo"], os.listdir("de")) def test_get_config(self): @@ -105,17 +105,18 @@ def test_get_config(self): try: base_dir = tempfile.mkdtemp() base_dir_basename = os.path.basename(base_dir) - svn_cfg_dir = os.path.join(base_dir, '.subversion') + svn_cfg_dir = os.path.join(base_dir, ".subversion") os.mkdir(svn_cfg_dir) - with open(os.path.join(svn_cfg_dir, 'config'), 'w') as svn_cfg: - svn_cfg.write('[miscellany]\n') - svn_cfg.write('global-ignores = %s' % base_dir_basename) + with open(os.path.join(svn_cfg_dir, "config"), "w") as svn_cfg: + svn_cfg.write("[miscellany]\n") + svn_cfg.write("global-ignores = %s" % base_dir_basename) config = client.get_config(svn_cfg_dir) self.assertIsInstance(config, client.Config) ignores = config.get_default_ignores() self.assertTrue( - base_dir_basename.encode('utf-8') in ignores, - "no %r in %r" % (base_dir_basename, ignores)) + base_dir_basename.encode("utf-8") in ignores, + "no %r in %r" % (base_dir_basename, ignores), + ) finally: shutil.rmtree(base_dir) @@ -133,7 +134,8 @@ def test_diff(self): (outf, errf) = self.client.diff(1, 2, self.repos_url, self.repos_url) self.addCleanup(outf.close) self.addCleanup(errf.close) - self.assertEqual(b"""Index: foo + self.assertEqual( + b"""Index: foo =================================================================== --- foo\t(revision 1) +++ foo\t(revision 2) @@ -142,7 +144,9 @@ def test_diff(self): \\ No newline at end of file +foo2 \\ No newline at end of file -""".splitlines(), outf.read().splitlines()) +""".splitlines(), + outf.read().splitlines(), + ) self.assertEqual(b"", errf.read()) def assertCatEquals(self, value, revision=None): @@ -172,8 +176,8 @@ def assertLogEntryMessageEquals(self, expected, entry): def assertLogEntryDateAlmostEquals(self, expected, entry, delta): actual = datetime.strptime( - entry["revprops"]["svn:date"].decode('utf-8'), - "%Y-%m-%dT%H:%M:%S.%fZ") + entry["revprops"]["svn:date"].decode("utf-8"), "%Y-%m-%dT%H:%M:%S.%fZ" + ) self.assertTrue((actual - expected) < delta) def test_log(self): @@ -183,12 +187,15 @@ def test_log(self): delta = timedelta(hours=1) def cb(changed_paths, revision, revprops, has_children=False): - log_entries.append({ - 'changed_paths': changed_paths, - 'revision': revision, - 'revprops': revprops, - 'has_children': has_children, - }) + log_entries.append( + { + "changed_paths": changed_paths, + "revision": revision, + "revprops": revprops, + "has_children": has_children, + } + ) + self.build_tree({"dc/foo": b"bla"}) self.client.add("dc/foo") self.client.log_msg_func = lambda c: commit_msg_1 @@ -200,18 +207,20 @@ def cb(changed_paths, revision, revprops, has_children=False): self.assertEqual(1, log_entries[0]["revision"]) self.assertLogEntryMessageEquals(commit_msg_1, log_entries[0]) self.assertLogEntryDateAlmostEquals(commit_1_dt, log_entries[0], delta) - self.build_tree({ - "dc/foo": b"blabla", - "dc/bar": b"blablabla", - }) + self.build_tree( + { + "dc/foo": b"blabla", + "dc/bar": b"blablabla", + } + ) self.client.add("dc/bar") self.client.log_msg_func = lambda c: commit_msg_2 self.client.commit(["dc"]) commit_2_dt = datetime.utcnow() log_entries = [] self.client.log( - cb, "dc/foo", start_rev="HEAD", end_rev=1, - discover_changed_paths=True) + cb, "dc/foo", start_rev="HEAD", end_rev=1, discover_changed_paths=True + ) self.assertEqual(2, len(log_entries)) self.assertLogEntryChangedPathsEquals(["/foo", "/bar"], log_entries[0]) self.assertEqual(2, log_entries[0]["revision"]) @@ -222,8 +231,9 @@ def cb(changed_paths, revision, revprops, has_children=False): self.assertLogEntryMessageEquals(commit_msg_1, log_entries[1]) self.assertLogEntryDateAlmostEquals(commit_1_dt, log_entries[1], delta) log_entries = [] - self.client.log(cb, "dc/foo", start_rev=2, end_rev=2, - discover_changed_paths=True) + self.client.log( + cb, "dc/foo", start_rev=2, end_rev=2, discover_changed_paths=True + ) self.assertEqual(1, len(log_entries)) self.assertLogEntryChangedPathsEquals(["/foo", "/bar"], log_entries[0]) self.assertEqual(2, log_entries[0]["revision"]) @@ -255,7 +265,8 @@ def test_set_get_prop_with_path(self): self.client_set_prop("dc/foo", "svn:eol-style", "native") self.client_commit("dc", message="Commit") self.assertEqual( - self.client_get_prop("dc/foo", "svn:eol-style", "HEAD"), b"native") + self.client_get_prop("dc/foo", "svn:eol-style", "HEAD"), b"native" + ) def test_set_get_prop_with_url(self): self.build_tree({"dc/foo": b"bla"}) @@ -263,7 +274,6 @@ def test_set_get_prop_with_url(self): self.client_set_prop("dc/foo", "svn:eol-style", "native") self.client_commit("dc", message="Commit") self.assertEqual( - self.client_get_prop( - self.repos_url + "/foo", "svn:eol-style", "HEAD"), - b"native" + self.client_get_prop(self.repos_url + "/foo", "svn:eol-style", "HEAD"), + b"native", ) diff --git a/subvertpy/tests/test_core.py b/subvertpy/tests/test_core.py index f608aca31..ca4899951 100644 --- a/subvertpy/tests/test_core.py +++ b/subvertpy/tests/test_core.py @@ -20,10 +20,8 @@ class TestCore(TestCase): - def setUp(self): super(TestCore, self).setUp() def test_exc(self): - self.assertTrue( - isinstance(subvertpy.SubversionException("foo", 1), Exception)) + self.assertTrue(isinstance(subvertpy.SubversionException("foo", 1), Exception)) diff --git a/subvertpy/tests/test_delta.py b/subvertpy/tests/test_delta.py index 20853d1e9..cfda231a9 100644 --- a/subvertpy/tests/test_delta.py +++ b/subvertpy/tests/test_delta.py @@ -24,13 +24,14 @@ send_stream, unpack_svndiff0, apply_txdelta_handler, - TXDELTA_NEW, TXDELTA_SOURCE, TXDELTA_TARGET, - ) + TXDELTA_NEW, + TXDELTA_SOURCE, + TXDELTA_TARGET, +) from subvertpy.tests import TestCase class DeltaTests(TestCase): - def setUp(self): super(DeltaTests, self).setUp() self.windows = [] @@ -41,8 +42,7 @@ def storing_window_handler(self, window): def test_send_stream(self): stream = BytesIO(b"foo") send_stream(stream, self.storing_window_handler) - self.assertEqual([(0, 0, 3, 0, [(2, 0, 3)], b'foo'), None], - self.windows) + self.assertEqual([(0, 0, 3, 0, [(2, 0, 3)], b"foo"), None], self.windows) def test_apply_delta(self): stream = BytesIO() @@ -55,7 +55,6 @@ def test_apply_delta(self): (TXDELTA_SOURCE, 0, len(source)), (TXDELTA_TARGET, len(new), len(b"(s")), # Copy "(s" (TXDELTA_TARGET, len(b"(n"), len(b"ew)")), # Copy "ew)" - # Copy as target is generated (TXDELTA_TARGET, len(new + source), len(b"(sew)") * 2), ) @@ -68,7 +67,6 @@ def test_apply_delta(self): class MarshallTests(TestCase): - def test_encode_length(self): self.assertEqual(bytearray(b"\x81\x02"), encode_length(130)) @@ -76,7 +74,5 @@ def test_roundtrip_length(self): self.assertEqual((42, bytes()), decode_length(encode_length(42))) def test_roundtrip_window(self): - mywindow = (0, 0, 3, 1, [(2, 0, 3)], b'foo') - self.assertEqual( - [mywindow], - list(unpack_svndiff0(pack_svndiff0([mywindow])))) + mywindow = (0, 0, 3, 1, [(2, 0, 3)], b"foo") + self.assertEqual([mywindow], list(unpack_svndiff0(pack_svndiff0([mywindow])))) diff --git a/subvertpy/tests/test_marshall.py b/subvertpy/tests/test_marshall.py index 47d659dcc..f54b99a9e 100644 --- a/subvertpy/tests/test_marshall.py +++ b/subvertpy/tests/test_marshall.py @@ -21,12 +21,11 @@ literal, marshall, unmarshall, - ) +) from subvertpy.tests import TestCase class TestMarshalling(TestCase): - def test_literal_txt(self): line = literal("foo") self.assertEqual("foo", line.txt) @@ -68,16 +67,16 @@ def test_marshall_string_space(self): self.assertEqual(b"5:bla l ", marshall("bla l")) def test_unmarshall_string(self): - self.assertEqual((b'', b"bla l"), unmarshall(b"5:bla l")) + self.assertEqual((b"", b"bla l"), unmarshall(b"5:bla l")) def test_unmarshall_list(self): - self.assertEqual((b'', [4, 5]), unmarshall(b"( 4 5 ) ")) + self.assertEqual((b"", [4, 5]), unmarshall(b"( 4 5 ) ")) def test_unmarshall_int(self): - self.assertEqual((b'', 2), unmarshall(b"2 ")) + self.assertEqual((b"", 2), unmarshall(b"2 ")) def test_unmarshall_literal(self): - self.assertEqual((b'', literal("x")), unmarshall(b"x ")) + self.assertEqual((b"", literal("x")), unmarshall(b"x ")) def test_unmarshall_empty(self): self.assertRaises(MarshallError, unmarshall, b"") diff --git a/subvertpy/tests/test_properties.py b/subvertpy/tests/test_properties.py index 996f03a39..d41976478 100644 --- a/subvertpy/tests/test_properties.py +++ b/subvertpy/tests/test_properties.py @@ -21,144 +21,188 @@ from subvertpy import properties from subvertpy.tests import ( TestCase, - ) +) class TestProperties(TestCase): - def setUp(self): super(TestProperties, self).setUp() def test_time_from_cstring(self): self.assertEqual( - 1225704780716938, - properties.time_from_cstring("2008-11-03T09:33:00.716938Z")) + 1225704780716938, + properties.time_from_cstring("2008-11-03T09:33:00.716938Z"), + ) def test_time_from_cstring_independent_from_dst(self): - old_tz = os.environ.get('TZ', None) + old_tz = os.environ.get("TZ", None) # On Windows, there is no tzset function, so skip this test. - if getattr(time, 'tzset', None) is None: + if getattr(time, "tzset", None) is None: raise SkipTest("tzset not available on Windows") try: # First specify a fixed timezone with known DST (late March to late # October) - os.environ['TZ'] = 'Europe/London' + os.environ["TZ"] = "Europe/London" time.tzset() # Now test a time within that DST self.assertEqual( 1275295762430000, - properties.time_from_cstring("2010-05-31T08:49:22.430000Z")) + properties.time_from_cstring("2010-05-31T08:49:22.430000Z"), + ) finally: if old_tz is None: - del os.environ['TZ'] + del os.environ["TZ"] else: - os.environ['TZ'] = old_tz + os.environ["TZ"] = old_tz time.tzset() def test_time_to_cstring(self): self.assertEqual( - "2008-11-03T09:33:00.716938Z", - properties.time_to_cstring(1225704780716938)) + "2008-11-03T09:33:00.716938Z", properties.time_to_cstring(1225704780716938) + ) class TestExternalsParser(TestCase): def test_parse_root_relative_externals(self): self.assertRaises( - NotImplementedError, - properties.parse_externals_description, - "http://example.com", "third-party/skins ^/foo") + NotImplementedError, + properties.parse_externals_description, + "http://example.com", + "third-party/skins ^/foo", + ) def test_parse_scheme_relative_externals(self): self.assertRaises( - NotImplementedError, properties.parse_externals_description, - "http://example.com", "third-party/skins //foo") + NotImplementedError, + properties.parse_externals_description, + "http://example.com", + "third-party/skins //foo", + ) def test_parse_externals(self): - self.assertEqual({ - 'third-party/sounds': - (None, "http://sounds.red-bean.com/repos"), - 'third-party/skins': - (None, "http://skins.red-bean.com/repositories/skinproj"), - 'third-party/skins/toolkit': - (21, "http://svn.red-bean.com/repos/skin-maker")}, + self.assertEqual( + { + "third-party/sounds": (None, "http://sounds.red-bean.com/repos"), + "third-party/skins": ( + None, + "http://skins.red-bean.com/repositories/skinproj", + ), + "third-party/skins/toolkit": ( + 21, + "http://svn.red-bean.com/repos/skin-maker", + ), + }, properties.parse_externals_description( - "http://example.com", """\ + "http://example.com", + """\ third-party/sounds http://sounds.red-bean.com/repos third-party/skins http://skins.red-bean.com/repositories/skinproj -third-party/skins/toolkit -r21 http://svn.red-bean.com/repos/skin-maker""")) +third-party/skins/toolkit -r21 http://svn.red-bean.com/repos/skin-maker""", + ), + ) def test_parse_externals_space_revno(self): - self.assertEqual({ - 'third-party/skins/toolkit': - (21, "http://svn.red-bean.com/repos/skin-maker")}, - properties.parse_externals_description("http://example.com", """\ -third-party/skins/toolkit -r 21 http://svn.red-bean.com/repos/skin-maker""")) + self.assertEqual( + { + "third-party/skins/toolkit": ( + 21, + "http://svn.red-bean.com/repos/skin-maker", + ) + }, + properties.parse_externals_description( + "http://example.com", + """\ +third-party/skins/toolkit -r 21 http://svn.red-bean.com/repos/skin-maker""", + ), + ) def test_parse_externals_swapped(self): self.assertEqual( - {'third-party/sounds': (None, "http://sounds.red-bean.com/repos")}, - properties.parse_externals_description("http://example.com", """\ + {"third-party/sounds": (None, "http://sounds.red-bean.com/repos")}, + properties.parse_externals_description( + "http://example.com", + """\ http://sounds.red-bean.com/repos third-party/sounds -""")) +""", + ), + ) def test_parse_comment(self): - self.assertEqual({ - 'third-party/sounds': (None, "http://sounds.red-bean.com/repos") - }, - properties.parse_externals_description("http://example.com/", """\ + self.assertEqual( + {"third-party/sounds": (None, "http://sounds.red-bean.com/repos")}, + properties.parse_externals_description( + "http://example.com/", + """\ third-party/sounds http://sounds.red-bean.com/repos #third-party/skins http://skins.red-bean.com/repositories/skinproj -#third-party/skins/toolkit -r21 http://svn.red-bean.com/repos/skin-maker""")) +#third-party/skins/toolkit -r21 http://svn.red-bean.com/repos/skin-maker""", + ), + ) def test_parse_relative(self): - self.assertEqual({ - 'third-party/sounds': (None, "http://example.com/branches/other"), - }, + self.assertEqual( + { + "third-party/sounds": (None, "http://example.com/branches/other"), + }, properties.parse_externals_description( "http://example.com/trunk", - "third-party/sounds ../branches/other")) + "third-party/sounds ../branches/other", + ), + ) def test_parse_repos_root_relative(self): - self.assertEqual({ - 'third-party/sounds': - (None, "http://example.com/bar/bla/branches/other"), - }, + self.assertEqual( + { + "third-party/sounds": ( + None, + "http://example.com/bar/bla/branches/other", + ), + }, properties.parse_externals_description( "http://example.com/trunk", - "third-party/sounds /bar/bla/branches/other")) + "third-party/sounds /bar/bla/branches/other", + ), + ) def test_parse_invalid_missing_url(self): """No URL specified.""" self.assertRaises( properties.InvalidExternalsDescription, lambda: properties.parse_externals_description( - "http://example.com/", "bla")) + "http://example.com/", "bla" + ), + ) def test_parse_invalid_too_much_data(self): """No URL specified.""" self.assertRaises( properties.InvalidExternalsDescription, lambda: properties.parse_externals_description( - None, "bla -R40 http://bla/")) + None, "bla -R40 http://bla/" + ), + ) class MergeInfoPropertyParserTests(TestCase): def test_simple_range(self): self.assertEqual( - {"/trunk": [(1, 2, True)]}, - properties.parse_mergeinfo_property("/trunk:1-2\n")) + {"/trunk": [(1, 2, True)]}, + properties.parse_mergeinfo_property("/trunk:1-2\n"), + ) def test_simple_range_uninheritable(self): self.assertEqual( - {"/trunk": [(1, 2, False)]}, - properties.parse_mergeinfo_property("/trunk:1-2*\n")) + {"/trunk": [(1, 2, False)]}, + properties.parse_mergeinfo_property("/trunk:1-2*\n"), + ) def test_simple_individual(self): self.assertEqual( - {"/trunk": [(1, 1, True)]}, - properties.parse_mergeinfo_property("/trunk:1\n")) + {"/trunk": [(1, 1, True)]}, + properties.parse_mergeinfo_property("/trunk:1\n"), + ) def test_empty(self): self.assertEqual({}, properties.parse_mergeinfo_property("")) @@ -168,13 +212,14 @@ class MergeInfoPropertyCreatorTests(TestCase): def test_simple_range(self): self.assertEqual( "/trunk:1-2\n", - properties.generate_mergeinfo_property({"/trunk": [(1, 2, True)]})) + properties.generate_mergeinfo_property({"/trunk": [(1, 2, True)]}), + ) def test_simple_individual(self): self.assertEqual( "/trunk:1\n", - properties.generate_mergeinfo_property( - {"/trunk": [(1, 1, True)]})) + properties.generate_mergeinfo_property({"/trunk": [(1, 1, True)]}), + ) def test_empty(self): self.assertEqual("", properties.generate_mergeinfo_property({})) @@ -186,61 +231,62 @@ def test_add_revnum_empty(self): def test_add_revnum_before(self): self.assertEqual( - [(2, 2, True), (8, 8, True)], - properties.range_add_revnum([(2, 2, True)], 8)) + [(2, 2, True), (8, 8, True)], properties.range_add_revnum([(2, 2, True)], 8) + ) def test_add_revnum_included(self): - self.assertEqual( - [(1, 3, True)], - properties.range_add_revnum([(1, 3, True)], 2)) + self.assertEqual([(1, 3, True)], properties.range_add_revnum([(1, 3, True)], 2)) def test_add_revnum_after(self): self.assertEqual( - [(1, 3, True), (5, 5, True)], - properties.range_add_revnum([(1, 3, True)], 5)) + [(1, 3, True), (5, 5, True)], properties.range_add_revnum([(1, 3, True)], 5) + ) def test_add_revnum_extend_before(self): - self.assertEqual( - [(1, 3, True)], - properties.range_add_revnum([(2, 3, True)], 1)) + self.assertEqual([(1, 3, True)], properties.range_add_revnum([(2, 3, True)], 1)) def test_add_revnum_extend_after(self): - self.assertEqual( - [(1, 3, True)], - properties.range_add_revnum([(1, 2, True)], 3)) + self.assertEqual([(1, 3, True)], properties.range_add_revnum([(1, 2, True)], 3)) def test_revnum_includes_empty(self): self.assertFalse(properties.range_includes_revnum([], 2)) def test_revnum_includes_oor(self): self.assertFalse( - properties.range_includes_revnum( - [(1, 3, True), (4, 5, True)], 10)) + properties.range_includes_revnum([(1, 3, True), (4, 5, True)], 10) + ) def test_revnum_includes_in(self): self.assertTrue( - properties.range_includes_revnum( - [(1, 3, True), (4, 5, True)], 2)) + properties.range_includes_revnum([(1, 3, True), (4, 5, True)], 2) + ) class MergeInfoIncludeTests(TestCase): - def test_includes_individual(self): self.assertTrue( properties.mergeinfo_includes_revision( - {"/trunk": [(1, 1, True)]}, "/trunk", 1)) + {"/trunk": [(1, 1, True)]}, "/trunk", 1 + ) + ) def test_includes_range(self): self.assertTrue( properties.mergeinfo_includes_revision( - {"/trunk": [(1, 5, True)]}, "/trunk", 3)) + {"/trunk": [(1, 5, True)]}, "/trunk", 3 + ) + ) def test_includes_invalid_path(self): self.assertFalse( properties.mergeinfo_includes_revision( - {"/somepath": [(1, 5, True)]}, "/trunk", 3)) + {"/somepath": [(1, 5, True)]}, "/trunk", 3 + ) + ) def test_includes_invalid_revnum(self): self.assertFalse( properties.mergeinfo_includes_revision( - {"/trunk": [(1, 5, True)]}, "/trunk", 30)) + {"/trunk": [(1, 5, True)]}, "/trunk", 30 + ) + ) diff --git a/subvertpy/tests/test_ra.py b/subvertpy/tests/test_ra.py index 32ff67d67..21cd64340 100644 --- a/subvertpy/tests/test_ra.py +++ b/subvertpy/tests/test_ra.py @@ -18,18 +18,18 @@ from io import BytesIO from subvertpy import ( - NODE_DIR, NODE_NONE, + NODE_DIR, + NODE_NONE, SubversionException, ra, - ) +) from subvertpy.tests import ( SubversionTestCase, TestCase, - ) +) class VersionTest(TestCase): - def test_version_length(self): self.assertEqual(4, len(ra.version())) @@ -41,18 +41,17 @@ def test_api_version_later_same(self): class TestRemoteAccessUnknown(TestCase): - def test_unknown_url(self): self.assertRaises(SubversionException, ra.RemoteAccess, "bla://") class TestRemoteAccess(SubversionTestCase): - def setUp(self): super(TestRemoteAccess, self).setUp() self.repos_url = self.make_repository("d") self.ra = ra.RemoteAccess( - self.repos_url, auth=ra.Auth([ra.get_username_provider()])) + self.repos_url, auth=ra.Auth([ra.get_username_provider()]) + ) def tearDown(self): del self.ra @@ -67,8 +66,7 @@ def do_commit(self): dc.close() def test_repr(self): - self.assertEqual("RemoteAccess(\"%s\")" % self.repos_url, - repr(self.ra)) + self.assertEqual('RemoteAccess("%s")' % self.repos_url, repr(self.ra)) def test_latest_revnum(self): self.assertEqual(0, self.ra.get_latest_revnum()) @@ -102,8 +100,7 @@ def test_get_dir_leading_slash(self): def test_get_dir_kind(self): self.do_commit() - (dirents, fetch_rev, props) = self.ra.get_dir( - "/", 1, fields=ra.DIRENT_KIND) + (dirents, fetch_rev, props) = self.ra.get_dir("/", 1, fields=ra.DIRENT_KIND) self.assertIsInstance(props, dict) self.assertEqual(1, fetch_rev) self.assertEqual(NODE_DIR, dirents["foo"]["kind"]) @@ -119,29 +116,35 @@ def test_do_diff(self): self.do_commit() class MyFileEditor: + def change_prop(self, name, val): + pass - def change_prop(self, name, val): pass - - def close(self, checksum=None): pass + def close(self, checksum=None): + pass class MyDirEditor: + def change_prop(self, name, val): + pass - def change_prop(self, name, val): pass - - def add_directory(self, *args): return MyDirEditor() + def add_directory(self, *args): + return MyDirEditor() - def add_file(self, *args): return MyFileEditor() + def add_file(self, *args): + return MyFileEditor() - def close(self): pass + def close(self): + pass class MyEditor: - - def set_target_revision(self, rev): pass + def set_target_revision(self, rev): + pass def open_root(self, base_rev): return MyDirEditor() - def close(self): pass + def close(self): + pass + reporter = self.ra.do_diff(1, "", self.ra.get_repos_root(), MyEditor()) reporter.set_path("", 0, True) reporter.finish() @@ -149,11 +152,20 @@ def close(self): pass self.assertRaises(RuntimeError, reporter.set_path, "", 0, True) def test_iter_log_invalid(self): - self.assertRaises(SubversionException, list, self.ra.iter_log( - ["idontexist"], 0, 0, revprops=[ - "svn:date", "svn:author", "svn:log"])) - self.assertRaises(SubversionException, list, self.ra.iter_log( - [""], 0, 1000, revprops=["svn:date", "svn:author", "svn:log"])) + self.assertRaises( + SubversionException, + list, + self.ra.iter_log( + ["idontexist"], 0, 0, revprops=["svn:date", "svn:author", "svn:log"] + ), + ) + self.assertRaises( + SubversionException, + list, + self.ra.iter_log( + [""], 0, 1000, revprops=["svn:date", "svn:author", "svn:log"] + ), + ) def test_iter_log(self): def check_results(returned): @@ -170,19 +182,27 @@ def check_results(returned): (paths, revnum, props) = returned[1] else: (paths, revnum, props, has_children) = returned[1] - self.assertEqual({'/foo': ('A', None, -1, NODE_DIR)}, paths) + self.assertEqual({"/foo": ("A", None, -1, NODE_DIR)}, paths) self.assertEqual(revnum, 1) - self.assertEqual(set(["svn:date", "svn:author", "svn:log"]), - set(props.keys())) + self.assertEqual( + set(["svn:date", "svn:author", "svn:log"]), set(props.keys()) + ) - returned = list(self.ra.iter_log( - [""], 0, 0, revprops=["svn:date", "svn:author", "svn:log"])) + returned = list( + self.ra.iter_log([""], 0, 0, revprops=["svn:date", "svn:author", "svn:log"]) + ) self.assertEqual(1, len(returned)) self.do_commit() - returned = list(self.ra.iter_log( - None, 0, 1, discover_changed_paths=True, - strict_node_history=False, - revprops=["svn:date", "svn:author", "svn:log"])) + returned = list( + self.ra.iter_log( + None, + 0, + 1, + discover_changed_paths=True, + strict_node_history=False, + revprops=["svn:date", "svn:author", "svn:log"], + ) + ) check_results(returned) def test_get_log(self): @@ -205,18 +225,25 @@ def check_results(returned): (paths, revnum, props) = returned[1] else: (paths, revnum, props, has_children) = returned[1] - self.assertEqual({'/foo': ('A', None, -1)}, paths) + self.assertEqual({"/foo": ("A", None, -1)}, paths) self.assertEqual(revnum, 1) - self.assertEqual(set(["svn:date", "svn:author", "svn:log"]), - set(props.keys())) - self.ra.get_log(cb, [""], 0, 0, - revprops=["svn:date", "svn:author", "svn:log"]) + self.assertEqual( + set(["svn:date", "svn:author", "svn:log"]), set(props.keys()) + ) + + self.ra.get_log(cb, [""], 0, 0, revprops=["svn:date", "svn:author", "svn:log"]) self.assertEqual(1, len(returned)) self.do_commit() returned = [] - self.ra.get_log(cb, None, 0, 1, discover_changed_paths=True, - strict_node_history=False, - revprops=["svn:date", "svn:author", "svn:log"]) + self.ra.get_log( + cb, + None, + 0, + 1, + discover_changed_paths=True, + strict_node_history=False, + revprops=["svn:date", "svn:author", "svn:log"], + ) check_results(returned) def test_get_log_cancel(self): @@ -227,12 +254,18 @@ def cb(*args): self.do_commit() self.assertRaises( KeyError, - self.ra.get_log, cb, [""], 0, 0, - revprops=["svn:date", "svn:author", "svn:log"]) + self.ra.get_log, + cb, + [""], + 0, + 0, + revprops=["svn:date", "svn:author", "svn:log"], + ) def test_get_commit_editor_double_close(self): def mycb(*args): pass + editor = self.ra.get_commit_editor({"svn:log": "foo"}, mycb) dir = editor.open_root() dir.close() @@ -244,15 +277,17 @@ def mycb(*args): def test_get_commit_editor_busy(self): def mycb(rev): pass + editor = self.ra.get_commit_editor({"svn:log": "foo"}, mycb) self.assertRaises( - ra.BusyException, self.ra.get_commit_editor, - {"svn:log": "foo"}, mycb) + ra.BusyException, self.ra.get_commit_editor, {"svn:log": "foo"}, mycb + ) editor.abort() def test_get_commit_editor_double_open(self): def mycb(rev): pass + editor = self.ra.get_commit_editor({"svn:log": "foo"}, mycb) root = editor.open_root() root.add_directory("somedir") @@ -266,8 +301,8 @@ def mycb(paths, rev, revprops): pass editor = self.ra.get_commit_editor( - {"svn:log": "foo", "bar:foo": "bla", - "svn:custom:blie": "bloe"}, mycb) + {"svn:log": "foo", "bar:foo": "bla", "svn:custom:blie": "bloe"}, mycb + ) root = editor.open_root() root.add_directory("somedir").close() root.close() @@ -275,13 +310,15 @@ def mycb(paths, rev, revprops): revprops = self.ra.rev_proplist(1) self.assertEqual( - set(['bar:foo', 'svn:author', 'svn:custom:blie', 'svn:date', - 'svn:log']), - set(revprops.keys()), "result: %r" % revprops) + set(["bar:foo", "svn:author", "svn:custom:blie", "svn:date", "svn:log"]), + set(revprops.keys()), + "result: %r" % revprops, + ) def test_get_commit_editor_context_manager(self): def mycb(paths, rev, revprops): pass + editor = self.ra.get_commit_editor({"svn:log": "foo"}, mycb) self.assertIs(editor, editor.__enter__()) dir = editor.open_root(0) @@ -294,6 +331,7 @@ def mycb(paths, rev, revprops): def test_get_commit_editor(self): def mycb(paths, rev, revprops): pass + editor = self.ra.get_commit_editor({"svn:log": "foo"}, mycb) dir = editor.open_root(0) subdir = dir.add_directory("foo") @@ -378,9 +416,9 @@ def test_stat(self): ret = self.ra.stat("bar", 1) self.assertEqual( - set(['last_author', 'kind', 'created_rev', 'has_props', 'time', - 'size']), - set(ret.keys())) + set(["last_author", "kind", "created_rev", "has_props", "time", "size"]), + set(ret.keys()), + ) def test_get_locations_dir(self): cb = self.commit_editor() @@ -396,71 +434,102 @@ def test_get_locations_dir(self): cb.close() self.assertEqual( - {1: "/bar", 2: "/bla"}, - self.ra.get_locations("bla", 2, [1, 2])) + {1: "/bar", 2: "/bla"}, self.ra.get_locations("bla", 2, [1, 2]) + ) self.assertEqual( - {1: "/bar", 2: "/bar"}, - self.ra.get_locations("bar", 1, [1, 2])) + {1: "/bar", 2: "/bar"}, self.ra.get_locations("bar", 1, [1, 2]) + ) self.assertEqual( - {1: "/bar", 2: "/bar"}, - self.ra.get_locations("bar", 2, [1, 2])) + {1: "/bar", 2: "/bar"}, self.ra.get_locations("bar", 2, [1, 2]) + ) self.assertEqual( - {1: "/bar", 2: "/bla", 3: "/bla"}, - self.ra.get_locations("bla", 3, [1, 2, 3])) + {1: "/bar", 2: "/bla", 3: "/bla"}, + self.ra.get_locations("bla", 3, [1, 2, 3]), + ) class AuthTests(TestCase): - def test_not_list(self): self.assertRaises(TypeError, ra.Auth, ra.get_simple_provider()) def test_not_registered(self): auth = ra.Auth([]) self.assertRaises( - SubversionException, auth.credentials, "svn.simple", "MyRealm") + SubversionException, auth.credentials, "svn.simple", "MyRealm" + ) def test_simple(self): - auth = ra.Auth([ra.get_simple_prompt_provider( - lambda realm, uname, may_save: ("foo", "geheim", False), 0)]) + auth = ra.Auth( + [ + ra.get_simple_prompt_provider( + lambda realm, uname, may_save: ("foo", "geheim", False), 0 + ) + ] + ) creds = auth.credentials("svn.simple", "MyRealm") self.assertEqual(("foo", "geheim", 0), next(creds)) self.assertRaises(StopIteration, next, creds) def test_username(self): - auth = ra.Auth([ra.get_username_prompt_provider( - lambda realm, may_save: ("somebody", False), 0)]) + auth = ra.Auth( + [ + ra.get_username_prompt_provider( + lambda realm, may_save: ("somebody", False), 0 + ) + ] + ) creds = auth.credentials("svn.username", "MyRealm") self.assertEqual(("somebody", 0), next(creds)) self.assertRaises(StopIteration, next, creds) def test_client_cert(self): - auth = ra.Auth([ra.get_ssl_client_cert_prompt_provider( - lambda realm, may_save: ("filename", False), 0)]) + auth = ra.Auth( + [ + ra.get_ssl_client_cert_prompt_provider( + lambda realm, may_save: ("filename", False), 0 + ) + ] + ) creds = auth.credentials("svn.ssl.client-cert", "MyRealm") self.assertEqual(("filename", False), next(creds)) self.assertRaises(StopIteration, next, creds) def test_client_cert_pw(self): - auth = ra.Auth([ra.get_ssl_client_cert_pw_prompt_provider( - lambda realm, may_save: ("supergeheim", False), 0)]) + auth = ra.Auth( + [ + ra.get_ssl_client_cert_pw_prompt_provider( + lambda realm, may_save: ("supergeheim", False), 0 + ) + ] + ) creds = auth.credentials("svn.ssl.client-passphrase", "MyRealm") self.assertEqual(("supergeheim", False), next(creds)) self.assertRaises(StopIteration, next, creds) def test_server_trust(self): - auth = ra.Auth([ra.get_ssl_server_trust_prompt_provider( - lambda realm, failures, certinfo, may_save: (42, False))]) + auth = ra.Auth( + [ + ra.get_ssl_server_trust_prompt_provider( + lambda realm, failures, certinfo, may_save: (42, False) + ) + ] + ) auth.set_parameter("svn:auth:ssl:failures", 23) creds = auth.credentials("svn.ssl.server", "MyRealm") self.assertEqual((42, 0), next(creds)) self.assertRaises(StopIteration, next, creds) def test_server_untrust(self): - auth = ra.Auth([ra.get_ssl_server_trust_prompt_provider( - lambda realm, failures, certinfo, may_save: None)]) + auth = ra.Auth( + [ + ra.get_ssl_server_trust_prompt_provider( + lambda realm, failures, certinfo, may_save: None + ) + ] + ) auth.set_parameter("svn:auth:ssl:failures", 23) creds = auth.credentials("svn.ssl.server", "MyRealm") self.assertRaises(StopIteration, next, creds) @@ -471,6 +540,7 @@ def test_retry(self): def inc_foo(realm, may_save): self.i += 1 return ("somebody%d" % self.i, False) + auth = ra.Auth([ra.get_username_prompt_provider(inc_foo, 2)]) creds = auth.credentials("svn.username", "MyRealm") self.assertEqual(("somebody1", 0), next(creds)) diff --git a/subvertpy/tests/test_repos.py b/subvertpy/tests/test_repos.py index 282a531ce..22afa0385 100644 --- a/subvertpy/tests/test_repos.py +++ b/subvertpy/tests/test_repos.py @@ -24,7 +24,6 @@ class VersionTest(TestCase): - def test_version_length(self): self.assertEqual(4, len(repos.version())) @@ -36,7 +35,6 @@ def test_api_version_later_same(self): class TestRepository(TestCaseInTempDir): - def setUp(self): super(TestRepository, self).setUp() @@ -51,7 +49,7 @@ def test_verify_fs(self): r = repos.create(os.path.join(self.test_dir, "foo")) f = BytesIO() r.verify_fs(f, 0, 0) - self.assertEqual(b'* Verified revision 0.\n', f.getvalue()) + self.assertEqual(b"* Verified revision 0.\n", f.getvalue()) def test_open(self): repos.create(os.path.join(self.test_dir, "foo")) @@ -67,16 +65,19 @@ def test_youngest_rev(self): def test_rev_root(self): repos.create(os.path.join(self.test_dir, "foo")) - self.assertTrue( - repos.Repository("foo").fs().revision_root(0) is not None) + self.assertTrue(repos.Repository("foo").fs().revision_root(0) is not None) def test_load_fs_invalid(self): r = repos.create(os.path.join(self.test_dir, "foo")) dumpfile = b"Malformed" feedback = BytesIO() self.assertRaises( - SubversionException, r.load_fs, BytesIO(dumpfile), - feedback, repos.LOAD_UUID_DEFAULT) + SubversionException, + r.load_fs, + BytesIO(dumpfile), + feedback, + repos.LOAD_UUID_DEFAULT, + ) def test_load_fs(self): r = repos.create(os.path.join(self.test_dir, "foo")) @@ -97,19 +98,19 @@ def test_load_fs(self): """).encode("ascii") feedback = BytesIO() r.load_fs(BytesIO(dumpfile), feedback, repos.LOAD_UUID_DEFAULT) - self.assertEqual(r.fs().get_uuid(), - "38f0a982-fd1f-4e00-aa6b-a20720f4b9ca") + self.assertEqual(r.fs().get_uuid(), "38f0a982-fd1f-4e00-aa6b-a20720f4b9ca") def test_rev_props(self): repos.create(os.path.join(self.test_dir, "foo")) self.assertEqual( - ["svn:date"], - list(repos.Repository("foo").fs().revision_proplist(0).keys())) + ["svn:date"], list(repos.Repository("foo").fs().revision_proplist(0).keys()) + ) def test_rev_root_invalid(self): repos.create(os.path.join(self.test_dir, "foo")) - self.assertRaises(SubversionException, - repos.Repository("foo").fs().revision_root, 1) + self.assertRaises( + SubversionException, repos.Repository("foo").fs().revision_root, 1 + ) def test_pack_fs(self): r = repos.create(os.path.join(self.test_dir, "foo")) @@ -138,7 +139,6 @@ def test_is_file(self): class StreamTests(TestCase): - def test_read(self): s = repos.Stream() self.assertEqual(b"", s.read()) diff --git a/subvertpy/tests/test_subr.py b/subvertpy/tests/test_subr.py index a2e8bd3e1..b2d93e8b2 100644 --- a/subvertpy/tests/test_subr.py +++ b/subvertpy/tests/test_subr.py @@ -22,47 +22,41 @@ uri_canonicalize, dirent_canonicalize, abspath, - ) +) class UriCanonicalizeTests(TestCase): - def test_canonicalize(self): self.assertEqual( - 'https://www.example.com', - uri_canonicalize('https://www.example.com/')) + "https://www.example.com", uri_canonicalize("https://www.example.com/") + ) self.assertEqual( - 'https://www.example.com(bla)', - uri_canonicalize('https://www.example.com(bla)')) + "https://www.example.com(bla)", + uri_canonicalize("https://www.example.com(bla)"), + ) self.assertEqual( - 'https://www.example.com/(bla)', - uri_canonicalize('https://www.example.com/(bla%29')) + "https://www.example.com/(bla)", + uri_canonicalize("https://www.example.com/(bla%29"), + ) class DirentCanonicalizeTests(TestCase): - def test_canonicalize(self): - self.assertEqual( - '/foo/bar', - dirent_canonicalize('/foo/bar')) - self.assertEqual( - '/foo/bar', - dirent_canonicalize('/foo//bar')) + self.assertEqual("/foo/bar", dirent_canonicalize("/foo/bar")) + self.assertEqual("/foo/bar", dirent_canonicalize("/foo//bar")) class AbspathTests(TestCase): - def test_abspath(self): - path = '/foo/bar' if os.name != 'nt' else 'C:/foo/bar' - self.assertEqual( - path, - abspath(path)) + path = "/foo/bar" if os.name != "nt" else "C:/foo/bar" + self.assertEqual(path, abspath(path)) # os.getcwd() returns '/foo/bar' on linux/macos # while it returns 'c:\\foo\\bar' on windows self.assertEqual( - os.path.join(os.getcwd(), 'bar').replace("\\", "/").lower(), - abspath('bar').lower()) + os.path.join(os.getcwd(), "bar").replace("\\", "/").lower(), + abspath("bar").lower(), + ) self.assertEqual( - os.path.join( - os.getcwd(), 'bar', 'foo').replace("\\", "/").lower(), - abspath('bar/foo').lower()) + os.path.join(os.getcwd(), "bar", "foo").replace("\\", "/").lower(), + abspath("bar/foo").lower(), + ) diff --git a/subvertpy/tests/test_wc.py b/subvertpy/tests/test_wc.py index 9ec90bf15..c1013328d 100644 --- a/subvertpy/tests/test_wc.py +++ b/subvertpy/tests/test_wc.py @@ -17,15 +17,14 @@ from subvertpy import ( wc, - ) +) from subvertpy.tests import ( SubversionTestCase, TestCase, - ) +) class VersionTest(TestCase): - def test_version_length(self): self.assertEqual(4, len(wc.version())) @@ -37,7 +36,6 @@ def test_api_version_later_same(self): class AdmTests(TestCase): - def test_get_adm_dir(self): self.assertEqual(".svn", wc.get_adm_dir()) @@ -66,7 +64,6 @@ def test_match_ignore_list(self): class WcTests(SubversionTestCase): - def test_revision_status(self): self.make_client("repos", "checkout") ret = wc.revision_status("checkout")