From af474c78d8ffc28d7f4d22e04f4e9b78105af902 Mon Sep 17 00:00:00 2001 From: Alexander Schlarb Date: Fri, 18 Dec 2020 02:20:24 +0100 Subject: [PATCH 1/8] Fix tests on platforms where the Python interpreter is not named `python` (b/c it is named `python3` only like in Debian) --- tests/test_unasync.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/test_unasync.py b/tests/test_unasync.py index 6f198d0..bf020c5 100644 --- a/tests/test_unasync.py +++ b/tests/test_unasync.py @@ -4,6 +4,7 @@ import os import shutil import subprocess +import sys import pytest @@ -72,9 +73,9 @@ def test_build_py_modules(tmpdir): env = copy.copy(os.environ) env["PYTHONPATH"] = os.path.realpath(os.path.join(TEST_DIR, "..")) - subprocess.check_call(["python", "setup.py", "build"], cwd=mod_dir, env=env) + subprocess.check_call([sys.executable, "setup.py", "build"], cwd=mod_dir, env=env) # Calling it twice to test the "if not copied" branch - subprocess.check_call(["python", "setup.py", "build"], cwd=mod_dir, env=env) + subprocess.check_call([sys.executable, "setup.py", "build"], cwd=mod_dir, env=env) unasynced = os.path.join(mod_dir, "build/lib/_sync/some_file.py") tree_build_dir = list_files(mod_dir) @@ -92,7 +93,7 @@ def test_build_py_packages(tmpdir): env = copy.copy(os.environ) env["PYTHONPATH"] = os.path.realpath(os.path.join(TEST_DIR, "..")) - subprocess.check_call(["python", "setup.py", "build"], cwd=pkg_dir, env=env) + subprocess.check_call([sys.executable, "setup.py", "build"], cwd=pkg_dir, env=env) unasynced = os.path.join(pkg_dir, "build/lib/example_pkg/_sync/__init__.py") @@ -109,7 +110,7 @@ def test_project_structure_after_build_py_packages(tmpdir): env = copy.copy(os.environ) env["PYTHONPATH"] = os.path.realpath(os.path.join(TEST_DIR, "..")) - subprocess.check_call(["python", "setup.py", "build"], cwd=pkg_dir, env=env) + subprocess.check_call([sys.executable, "setup.py", "build"], cwd=pkg_dir, env=env) _async_dir_tree = list_files( os.path.join(source_pkg_dir, "src/example_pkg/_async/.") @@ -129,7 +130,7 @@ def test_project_structure_after_customized_build_py_packages(tmpdir): env = copy.copy(os.environ) env["PYTHONPATH"] = os.path.realpath(os.path.join(TEST_DIR, "..")) - subprocess.check_call(["python", "setup.py", "build"], cwd=pkg_dir, env=env) + subprocess.check_call([sys.executable, "setup.py", "build"], cwd=pkg_dir, env=env) _async_dir_tree = list_files(os.path.join(source_pkg_dir, "src/ahip/.")) unasynced_dir_path = os.path.join(pkg_dir, "build/lib/hip/.") From ffc5108a64be0a40d39b2df017f507e37a200746 Mon Sep 17 00:00:00 2001 From: Alexander Schlarb Date: Fri, 18 Dec 2020 02:26:59 +0100 Subject: [PATCH 2/8] Fix tests on platforms that do not pre-sort their `readdir` results (e.g. Linux) --- tests/test_unasync.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_unasync.py b/tests/test_unasync.py index bf020c5..f4b08eb 100644 --- a/tests/test_unasync.py +++ b/tests/test_unasync.py @@ -19,6 +19,11 @@ def list_files(startpath): output = "" for root, dirs, files in os.walk(startpath): + # Ensure that we do not capture the directory inode order on + # platforms that don't pre-sort `readdir` results + dirs.sort() + files.sort() + level = root.replace(startpath, "").count(os.sep) indent = " " * 4 * (level) output += "{}{}/".format(indent, os.path.basename(root)) From 10e449eef339fde125d942050f2aa0dba8413896 Mon Sep 17 00:00:00 2001 From: Alexander Schlarb Date: Fri, 18 Dec 2020 02:56:55 +0100 Subject: [PATCH 3/8] Skip Python 2/3 only sections on each of these to achive actual 100% coverage when running on each --- .coveragerc | 1 + .coveragerc-py2 | 10 ++++++++++ ci/travis.sh | 12 +++++++++++- src/unasync/__init__.py | 6 +++--- 4 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 .coveragerc-py2 diff --git a/.coveragerc b/.coveragerc index 5e913bd..cac94c3 100644 --- a/.coveragerc +++ b/.coveragerc @@ -7,3 +7,4 @@ precision = 1 exclude_lines = pragma: no cover abc.abstractmethod + \# PY2 diff --git a/.coveragerc-py2 b/.coveragerc-py2 new file mode 100644 index 0000000..571cf8a --- /dev/null +++ b/.coveragerc-py2 @@ -0,0 +1,10 @@ +[run] +branch=True +source=unasync + +[report] +precision = 1 +exclude_lines = + pragma: no cover + abc.abstractmethod + \# PY3 diff --git a/ci/travis.sh b/ci/travis.sh index 7319260..7ad4fc0 100755 --- a/ci/travis.sh +++ b/ci/travis.sh @@ -51,6 +51,16 @@ if [ "$USE_PYPY_RELEASE_VERSION" != "" ]; then source testenv/bin/activate fi +case "${MACPYTHON:-${TRAVIS_PYTHON_VERSION:-}}" in + 2*) + COVERAGE_FILE=.coveragerc-py2 + ;; + + *) + COVERAGE_FILE=.coveragerc + ;; +esac + pip install -U pip setuptools wheel if [ "$CHECK_FORMATTING" = "1" ]; then @@ -91,7 +101,7 @@ else mkdir empty cd empty - pytest -ra -v --cov=unasync --cov-config=../.coveragerc --verbose ../tests + pytest -ra -v --cov=unasync --cov-config="../${COVERAGE_FILE}" --verbose ../tests bash <(curl -s https://codecov.io/bash) fi diff --git a/src/unasync/__init__.py b/src/unasync/__init__.py index db4c0d9..4cd34c5 100644 --- a/src/unasync/__init__.py +++ b/src/unasync/__init__.py @@ -67,7 +67,7 @@ def _match(self, filepath): def _unasync_file(self, filepath): with open(filepath, "rb") as f: write_kwargs = {} - if sys.version_info[0] >= 3: + if sys.version_info[0] >= 3: # PY3 # pragma: no branch encoding, _ = std_tokenize.detect_encoding(f.readline) write_kwargs["encoding"] = encoding f.seek(0) @@ -128,11 +128,11 @@ def unasync_files(fpath_list, rules): def _get_tokens(f): - if sys.version_info[0] == 2: + if sys.version_info[0] == 2: # PY2 for tok in std_tokenize.generate_tokens(f.readline): type_, string, start, end, line = tok yield Token(type_, string, start, end, line) - else: + else: # PY3 for tok in std_tokenize.tokenize(f.readline): if tok.type == std_tokenize.ENCODING: continue From 95267baca50b5159a5b9a4b2fa9d6c9442bf3699 Mon Sep 17 00:00:00 2001 From: Alexander Schlarb Date: Sun, 6 Sep 2020 23:17:52 +0200 Subject: [PATCH 4/8] Copy whitespace from original file to preserve spaces vs tabs --- src/unasync/__init__.py | 2 +- tests/data/async/tabs.py | 8 ++++++++ tests/data/sync/tabs.py | 8 ++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 tests/data/async/tabs.py create mode 100644 tests/data/sync/tabs.py diff --git a/src/unasync/__init__.py b/src/unasync/__init__.py index 4cd34c5..58cf3b0 100644 --- a/src/unasync/__init__.py +++ b/src/unasync/__init__.py @@ -149,7 +149,7 @@ def _tokenize(f): space = "" if tok.start > last_end: assert tok.start[0] == last_end[0] - space = " " * (tok.start[1] - last_end[1]) + space = tok.line[last_end[1] : tok.start[1]] yield (space, tok.type, tok.string) last_end = tok.end diff --git a/tests/data/async/tabs.py b/tests/data/async/tabs.py new file mode 100644 index 0000000..27c8ca2 --- /dev/null +++ b/tests/data/async/tabs.py @@ -0,0 +1,8 @@ +# fmt: off +async def dummy(): + await dummy2() # This line is indented with a tab that should be preserved +# fmt: on + + +async def dummy2(): + await dummy() # This one uses 4 spaces and these should also be preserved diff --git a/tests/data/sync/tabs.py b/tests/data/sync/tabs.py new file mode 100644 index 0000000..9e9f48b --- /dev/null +++ b/tests/data/sync/tabs.py @@ -0,0 +1,8 @@ +# fmt: off +def dummy(): + dummy2() # This line is indented with a tab that should be preserved +# fmt: on + + +def dummy2(): + dummy() # This one uses 4 spaces and these should also be preserved From 600807e1f43fb4401f5f3af7984e1eee0f2f908f Mon Sep 17 00:00:00 2001 From: Alexander Schlarb Date: Mon, 7 Sep 2020 13:26:35 +0200 Subject: [PATCH 5/8] Add support for type comments (PEP-484#type-comments) Implementation: Detect type comments by their `# type: ` prefix, recursively unasyncify the type declaration part as new token stream, update the otherwise unchanged token value. --- src/unasync/__init__.py | 54 ++++++++++++++++++++++++++++++++++++-- tests/data/async/typing.py | 23 ++++++++++++++++ tests/data/sync/typing.py | 23 ++++++++++++++++ 3 files changed, 98 insertions(+), 2 deletions(-) diff --git a/src/unasync/__init__.py b/src/unasync/__init__.py index 58cf3b0..52af030 100644 --- a/src/unasync/__init__.py +++ b/src/unasync/__init__.py @@ -1,9 +1,11 @@ +# -*- encoding: utf8 -*- """Top-level package for unasync.""" from __future__ import print_function import collections import errno +import io import os import sys import tokenize as std_tokenize @@ -34,6 +36,22 @@ "StopAsyncIteration": "StopIteration", } +_TYPE_COMMENT_PREFIX = "# type: " + + +if sys.version_info[0] == 2: # PY2 + + def isidentifier(s): + return all([c.isalnum() or c == "_" for c in s]) + + StringIO = io.BytesIO +else: # PY3 + + def isidentifier(s): + return s.isidentifier() + + StringIO = io.StringIO + class Rule: """A single set of rules for 'unasync'ing file(s)""" @@ -95,6 +113,31 @@ def _unasync_tokens(self, tokens): elif toknum == std_tokenize.STRING: left_quote, name, right_quote = tokval[0], tokval[1:-1], tokval[-1] tokval = left_quote + self._unasync_name(name) + right_quote + elif toknum == std_tokenize.COMMENT and tokval.startswith( + _TYPE_COMMENT_PREFIX + ): + type_decl, suffix = tokval[len(_TYPE_COMMENT_PREFIX) :], "" + if "#" in type_decl: + type_decl, suffix = type_decl.split("#", 1) + suffix = "#" + suffix + type_decl_stripped = type_decl.strip() + + # Do not process `type: ignore` or `type: ignore[…]` as these aren't actual identifiers + is_type_ignore = type_decl_stripped == "ignore" + is_type_ignore |= type_decl_stripped.startswith( + "ignore" + ) and not isidentifier(type_decl_stripped[0:7]) + if not is_type_ignore: + # Preserve trailing whitespace since the tokenizer won't + trailing_space_len = len(type_decl) - len(type_decl.rstrip()) + if trailing_space_len > 0: + suffix = type_decl[-trailing_space_len:] + suffix + type_decl = type_decl[:-trailing_space_len] + type_decl = _untokenize( + self._unasync_tokens(_tokenize(StringIO(type_decl))) + ) + + tokval = _TYPE_COMMENT_PREFIX + type_decl + suffix if used_space is None: used_space = space yield (used_space, tokval) @@ -133,7 +176,11 @@ def _get_tokens(f): type_, string, start, end, line = tok yield Token(type_, string, start, end, line) else: # PY3 - for tok in std_tokenize.tokenize(f.readline): + if isinstance(f, io.TextIOBase): + gen = std_tokenize.generate_tokens(f.readline) + else: + gen = std_tokenize.tokenize(f.readline) + for tok in gen: if tok.type == std_tokenize.ENCODING: continue yield tok @@ -143,7 +190,10 @@ def _tokenize(f): last_end = (1, 0) for tok in _get_tokens(f): if last_end[0] < tok.start[0]: - yield ("", std_tokenize.STRING, " \\\n") + # Somehow Python 3.5 and below produce the ENDMARKER in a way that + # causes superfluous continuation lines to be generated + if tok.type != std_tokenize.ENDMARKER: + yield ("", std_tokenize.STRING, " \\\n") last_end = (tok.start[0], 0) space = "" diff --git a/tests/data/async/typing.py b/tests/data/async/typing.py index 4f0cd41..64bcfb6 100644 --- a/tests/data/async/typing.py +++ b/tests/data/async/typing.py @@ -3,3 +3,26 @@ typing.AsyncIterable[bytes] typing.AsyncIterator[bytes] typing.AsyncGenerator[bytes] + +# A typed function that takes the first item of an (a)sync iterator and returns it +async def func1(a: typing.AsyncIterable[int]) -> str: + it: typing.AsyncIterator[int] = a.__aiter__() + b: int = await it.__anext__() + return str(b) + + +# Same as the above but using old-style typings (mainly for Python 2.7 – 3.5 compatibility) +async def func2(a): # type: (typing.AsyncIterable[int]) -> str + it = a.__aiter__() # type: typing.AsyncIterator[int] + b = await it.__anext__() # type: int + return str(b) + + +# And some funky edge cases to at least cover the relevant at all in this test +a: int = 5 +b: str = a # type: ignore # This is the actual comment and the type declaration silences the warning that would otherwise happen +c: str = a # type: ignore2 # This is the actual comment and the declaration declares another type, both of which are wrong + +# fmt: off +# And some genuine trailing whitespace (uww…) +z = a # type: int diff --git a/tests/data/sync/typing.py b/tests/data/sync/typing.py index 268e7c7..213b048 100644 --- a/tests/data/sync/typing.py +++ b/tests/data/sync/typing.py @@ -3,3 +3,26 @@ typing.Iterable[bytes] typing.Iterator[bytes] typing.Generator[bytes] + +# A typed function that takes the first item of an (a)sync iterator and returns it +def func1(a: typing.Iterable[int]) -> str: + it: typing.Iterator[int] = a.__iter__() + b: int = it.__next__() + return str(b) + + +# Same as the above but using old-style typings (mainly for Python 2.7 – 3.5 compatibility) +def func2(a): # type: (typing.Iterable[int]) -> str + it = a.__iter__() # type: typing.Iterator[int] + b = it.__next__() # type: int + return str(b) + + +# And some funky edge cases to at least cover the relevant at all in this test +a: int = 5 +b: str = a # type: ignore # This is the actual comment and the type declaration silences the warning that would otherwise happen +c: str = a # type: ignore2 # This is the actual comment and the declaration declares another type, both of which are wrong + +# fmt: off +# And some genuine trailing whitespace (uww…) +z = a # type: int From 4527a95fc65d388427cc41651c930368eb5ed4f4 Mon Sep 17 00:00:00 2001 From: Alexander Schlarb Date: Mon, 7 Sep 2020 14:14:23 +0200 Subject: [PATCH 6/8] Add support for forward-reference typings (PEP-484#forward-references) Implementation: A context tracker monitors the incomping token stream and reports whether we are currently in a typing context or not, if we are in a typing context then the normal replacement code treats string values as a new token string to unasyncify recursively. --- src/unasync/__init__.py | 82 ++++++++++++++++++++++++++++++++-- tests/data/async/typing_py3.py | 13 ++++++ tests/data/sync/typing_py3.py | 13 ++++++ tests/test_unasync.py | 3 ++ 4 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 tests/data/async/typing_py3.py create mode 100644 tests/data/sync/typing_py3.py diff --git a/src/unasync/__init__.py b/src/unasync/__init__.py index 52af030..6974209 100644 --- a/src/unasync/__init__.py +++ b/src/unasync/__init__.py @@ -100,7 +100,57 @@ def _unasync_file(self, filepath): def _unasync_tokens(self, tokens): # TODO __await__, ...? used_space = None + context = None # Can be `None`, `"func_decl"`, `"func_name"`, `"arg_list"`, `"arg_list_end"`, `"return_type"` + brace_depth = 0 + typing_ctx = False + for space, toknum, tokval in tokens: + # Update context state tracker + if context is None and toknum == std_tokenize.NAME and tokval == "def": + context = "func_decl" + elif context == "func_decl" and toknum == std_tokenize.NAME: + context = "func_name" + elif context == "func_name" and toknum == std_tokenize.OP and tokval == "(": + context = "arg_list" + elif context == "arg_list": + if toknum == std_tokenize.OP and tokval in ("(", "["): + brace_depth += 1 + elif ( + toknum == std_tokenize.OP + and tokval in (")", "]") + and brace_depth >= 1 + ): + brace_depth -= 1 + elif toknum == std_tokenize.OP and tokval == ")": + context = "arg_list_end" + elif toknum == std_tokenize.OP and tokval == ":" and brace_depth < 1: + typing_ctx = True + elif toknum == std_tokenize.OP and tokval == "," and brace_depth < 1: + typing_ctx = False + elif ( + context == "arg_list_end" + and toknum == std_tokenize.OP + and tokval == "->" + ): + context = "return_type" + typing_ctx = True + elif context == "return_type": + if toknum == std_tokenize.OP and tokval in ("(", "["): + brace_depth += 1 + elif ( + toknum == std_tokenize.OP + and tokval in (")", "]") + and brace_depth >= 1 + ): + brace_depth -= 1 + elif toknum == std_tokenize.OP and tokval == ":": + context = None + typing_ctx = False + else: # Something unexpected happend - reset state + context = None + brace_depth = 0 + typing_ctx = False + if tokval in ["async", "await"]: # When removing async or await, we want to use the whitespace that # was before async/await before the next token so that @@ -111,8 +161,34 @@ def _unasync_tokens(self, tokens): if toknum == std_tokenize.NAME: tokval = self._unasync_name(tokval) elif toknum == std_tokenize.STRING: - left_quote, name, right_quote = tokval[0], tokval[1:-1], tokval[-1] - tokval = left_quote + self._unasync_name(name) + right_quote + # Strings in typing context are forward-references and should be unasyncified + quote = "" + prefix = "" + while ord(tokval[0]) in range(ord("a"), ord("z") + 1): + prefix += tokval[0] + tokval = tokval[1:] + + if tokval.startswith('"""') and tokval.endswith('"""'): + quote = '"""' # Broken syntax highlighters workaround: """ + elif tokval.startswith("'''") and tokval.endswith("'''"): + quote = "'''" # Broken syntax highlighters wokraround: ''' + elif tokval.startswith('"') and tokval.endswith('"'): + quote = '"' + elif tokval.startswith( # pragma: no branch + "'" + ) and tokval.endswith("'"): + quote = "'" + assert ( + len(quote) > 0 + ), "Quoting style of string {0!r} unknown".format(tokval) + stringval = tokval[len(quote) : -len(quote)] + if typing_ctx: + stringval = _untokenize( + self._unasync_tokens(_tokenize(StringIO(stringval))) + ) + else: + stringval = self._unasync_name(stringval) + tokval = prefix + quote + stringval + quote elif toknum == std_tokenize.COMMENT and tokval.startswith( _TYPE_COMMENT_PREFIX ): @@ -193,7 +269,7 @@ def _tokenize(f): # Somehow Python 3.5 and below produce the ENDMARKER in a way that # causes superfluous continuation lines to be generated if tok.type != std_tokenize.ENDMARKER: - yield ("", std_tokenize.STRING, " \\\n") + yield (" ", std_tokenize.NEWLINE, "\\\n") last_end = (tok.start[0], 0) space = "" diff --git a/tests/data/async/typing_py3.py b/tests/data/async/typing_py3.py new file mode 100644 index 0000000..cd4e213 --- /dev/null +++ b/tests/data/async/typing_py3.py @@ -0,0 +1,13 @@ +# fmt: off +# A forward-reference typed function that returns an iterator for an (a)sync iterable +async def aiter1(a: "typing.AsyncIterable[int]") -> 'typing.AsyncIterable[int]': + return a.__aiter__() + +# Same as the above but using tripple-quoted strings +async def aiter2(a: """typing.AsyncIterable[int]""") -> r'''typing.AsyncIterable[int]''': + return a.__aiter__() + +# Same as the above but without forward-references +async def aiter3(a: typing.AsyncIterable[int]) -> typing.AsyncIterable[int]: + return a.__aiter__() +# fmt: on diff --git a/tests/data/sync/typing_py3.py b/tests/data/sync/typing_py3.py new file mode 100644 index 0000000..cfad1f0 --- /dev/null +++ b/tests/data/sync/typing_py3.py @@ -0,0 +1,13 @@ +# fmt: off +# A forward-reference typed function that returns an iterator for an (a)sync iterable +def aiter1(a: "typing.Iterable[int]") -> 'typing.Iterable[int]': + return a.__iter__() + +# Same as the above but using tripple-quoted strings +def aiter2(a: """typing.Iterable[int]""") -> r'''typing.Iterable[int]''': + return a.__iter__() + +# Same as the above but without forward-references +def aiter3(a: typing.Iterable[int]) -> typing.Iterable[int]: + return a.__iter__() +# fmt: on diff --git a/tests/test_unasync.py b/tests/test_unasync.py index f4b08eb..825b43d 100644 --- a/tests/test_unasync.py +++ b/tests/test_unasync.py @@ -15,6 +15,9 @@ SYNC_DIR = os.path.join(TEST_DIR, "sync") TEST_FILES = sorted([f for f in os.listdir(ASYNC_DIR) if f.endswith(".py")]) +if sys.version_info[0] == 2: + TEST_FILES.remove("typing_py3.py") + def list_files(startpath): output = "" From c4813157bb8f6ee12ffe7d7505169b883dd28ac4 Mon Sep 17 00:00:00 2001 From: Alexander Schlarb Date: Mon, 7 Sep 2020 16:08:27 +0200 Subject: [PATCH 7/8] Do not fail when receiving `pathlib.Path` objects as filepaths anywhere --- src/unasync/__init__.py | 12 ++++++++++-- test-requirements.txt | 3 ++- tests/test_unasync.py | 11 +++++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/unasync/__init__.py b/src/unasync/__init__.py index 6974209..24bd971 100644 --- a/src/unasync/__init__.py +++ b/src/unasync/__init__.py @@ -52,13 +52,18 @@ def isidentifier(s): StringIO = io.StringIO +if hasattr(os, "fspath"): # PY3 + fspath = os.fspath +else: # PY2 + fspath = str + class Rule: """A single set of rules for 'unasync'ing file(s)""" def __init__(self, fromdir, todir, additional_replacements=None): - self.fromdir = fromdir.replace("/", os.sep) - self.todir = todir.replace("/", os.sep) + self.fromdir = fspath(fromdir).replace("/", os.sep) + self.todir = fspath(todir).replace("/", os.sep) # Add any additional user-defined token replacements to our list. self.token_replacements = _ASYNC_TO_SYNC.copy() @@ -69,6 +74,8 @@ def _match(self, filepath): """Determines if a Rule matches a given filepath and if so returns a higher comparable value if the match is more specific. """ + filepath = fspath(filepath) + file_segments = [x for x in filepath.split(os.sep) if x] from_segments = [x for x in self.fromdir.split(os.sep) if x] len_from_segments = len(from_segments) @@ -83,6 +90,7 @@ def _match(self, filepath): return False def _unasync_file(self, filepath): + filepath = fspath(filepath) with open(filepath, "rb") as f: write_kwargs = {} if sys.version_info[0] >= 3: # PY3 # pragma: no branch diff --git a/test-requirements.txt b/test-requirements.txt index 2c7cf23..55b2caa 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,2 +1,3 @@ pytest>=4.3.0 -pytest-cov \ No newline at end of file +pytest-cov +pathlib2 ; python_version < '3.5' \ No newline at end of file diff --git a/tests/test_unasync.py b/tests/test_unasync.py index 825b43d..511b956 100644 --- a/tests/test_unasync.py +++ b/tests/test_unasync.py @@ -2,6 +2,11 @@ import errno import io import os + +try: + import pathlib +except ImportError: + import pathlib2 as pathlib import shutil import subprocess import sys @@ -43,6 +48,12 @@ def test_rule_on_short_path(): assert rule._match("/ahip/") is False +def test_rule_with_pathlib_path(): + path_async_base = pathlib.Path("/ahip") + path_sync_base = pathlib.Path("/hip") + unasync.Rule(path_async_base / "tests", path_sync_base / "tests") + + @pytest.mark.parametrize("source_file", TEST_FILES) def test_unasync(tmpdir, source_file): From 36991657efb04aa6c39dbaa89f1d87330c3f24b4 Mon Sep 17 00:00:00 2001 From: Alexander Schlarb Date: Tue, 22 Dec 2020 17:28:55 +0100 Subject: [PATCH 8/8] TEMP Try fixing Python 2 coverage as suggested --- .coveragerc | 3 +++ .coveragerc-py2 | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.coveragerc b/.coveragerc index cac94c3..96752f1 100644 --- a/.coveragerc +++ b/.coveragerc @@ -2,6 +2,9 @@ branch=True source=unasync +[paths] +source = src/unasync + [report] precision = 1 exclude_lines = diff --git a/.coveragerc-py2 b/.coveragerc-py2 index 571cf8a..c69e158 100644 --- a/.coveragerc-py2 +++ b/.coveragerc-py2 @@ -2,6 +2,9 @@ branch=True source=unasync +[paths] +source = src/unasync + [report] precision = 1 exclude_lines =