diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index 958f7ef..0000000 --- a/.appveyor.yml +++ /dev/null @@ -1,29 +0,0 @@ -skip_tags: true - -os: Visual Studio 2015 - -environment: - matrix: - - PYTHON: "C:\\Python27-x64" - - PYTHON: "C:\\Python35-x64" - - PYTHON: "C:\\Python36-x64" - - PYTHON: "C:\\Python37-x64" - - PYTHON: "C:\\Python38-x64" - -build_script: - - "git --no-pager log -n2" - - "echo %APPVEYOR_REPO_COMMIT%" - - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;;%PATH%" - - "python --version" - - "python -c \"import struct; print(struct.calcsize('P') * 8)\"" - - "pip install ." - - "pip install -Ur test-requirements.txt" - - "pip install codecov" - -test_script: - - "mkdir empty" - - "cd empty" - # Make sure it's being imported from where we expect - - "python -c \"import os, unasync; print(os.path.dirname(unasync.__file__))\"" - - "python -u -m pytest -ra -v -s --cov=unasync --cov-config=../.coveragerc ../tests" - - "codecov" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..518b8c3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,87 @@ +name: CI + +on: [push, pull_request] + +jobs: + Windows: + name: 'Windows (${{ matrix.python }})' + runs-on: 'windows-latest' + strategy: + fail-fast: false + matrix: + python: ['3.7', '3.8', '3.9', '3.10'] + + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + cache: pip + cache-dependency-path: test-requirements.txt + - name: Run tests + run: ./ci.sh + shell: bash + env: + # Should match 'name:' up above + JOB_NAME: 'Windows (${{ matrix.python }})' + + Ubuntu: + name: 'Ubuntu (${{ matrix.python }}${{ matrix.extra_name }})' + timeout-minutes: 10 + runs-on: 'ubuntu-latest' + strategy: + fail-fast: false + matrix: + python: ['3.7', '3.8', '3.9', '3.10', '3.11-dev'] + check_formatting: ['0'] + extra_name: [''] + include: + - python: '3.10' + check_formatting: '1' + extra_name: ', check formatting' + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup python + uses: actions/setup-python@v2 + if: "!endsWith(matrix.python, '-dev')" + with: + python-version: ${{ matrix.python }} + cache: pip + cache-dependency-path: test-requirements.txt + - name: Setup python (dev) + uses: deadsnakes/action@v2.0.2 + if: endsWith(matrix.python, '-dev') + with: + python-version: '${{ matrix.python }}' + - name: Run tests + run: ./ci.sh + env: + CHECK_FORMATTING: '${{ matrix.check_formatting }}' + # Should match 'name:' up above + JOB_NAME: 'Ubuntu (${{ matrix.python }}${{ matrix.extra_name }})' + + macOS: + name: 'macOS (${{ matrix.python }})' + timeout-minutes: 10 + runs-on: 'macos-latest' + strategy: + fail-fast: false + matrix: + python: ['3.7', '3.8', '3.9', '3.10'] + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + cache: pip + cache-dependency-path: test-requirements.txt + - name: Run tests + run: ./ci.sh + env: + # Should match 'name:' up above + JOB_NAME: 'macOS (${{ matrix.python }})' diff --git a/.readthedocs.yml b/.readthedocs.yml index 31c3c9c..da6abdf 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -5,12 +5,6 @@ formats: requirements_file: ci/rtd-requirements.txt -# Currently RTD's default image only has 3.5 -# This gets us 3.6 (and hopefully 3.7 in the future) -# https://docs.readthedocs.io/en/latest/yaml-config.html#build-image -build: - image: latest - python: version: 3 pip_install: True diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 86e0d68..0000000 --- a/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -language: python -dist: xenial - -matrix: - include: - # These are quick and often catch errors, so list them first - - python: 3.6 - env: CHECK_DOCS=1 - - python: 3.6 - env: CHECK_FORMATTING=1 - - python: pypy2.7-7.1.1 - - python: 2.7 - - python: pypy3.5 - - python: 3.5 - - python: 3.6 - - python: 3.7 - - python: 3.8-dev - - os: osx - language: generic - env: MACPYTHON=3.5.4 - - os: osx - language: generic - env: MACPYTHON=3.6.7 - - os: osx - language: generic - env: MACPYTHON=3.7.1 - -script: - - ci/travis.sh diff --git a/ci.sh b/ci.sh new file mode 100755 index 0000000..c1c2a18 --- /dev/null +++ b/ci.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +set -ex + +BLACK_VERSION=22.6.0 + +pip install -U pip setuptools wheel + +python setup.py sdist --formats=zip +pip install dist/*.zip + +if [ "$CHECK_FORMATTING" = "1" ]; then + pip install black==${BLACK_VERSION} isort>=5 + if ! black --diff setup.py src tests; then + cat <= 3: - encoding, _ = std_tokenize.detect_encoding(f.readline) - write_kwargs["encoding"] = encoding - f.seek(0) + encoding, _ = std_tokenize.detect_encoding(f.readline) + f.seek(0) tokens = _tokenize(f) tokens = self._unasync_tokens(tokens) result = _untokenize(tokens) outfilepath = filepath.replace(self.fromdir, self.todir) - _makedirs_existok(os.path.dirname(outfilepath)) - with open(outfilepath, "w", **write_kwargs) as f: - print(result, file=f, end="") + os.makedirs(os.path.dirname(outfilepath), exist_ok=True) + with open(outfilepath, "wb") as f: + f.write(result.encode(encoding)) def _unasync_tokens(self, tokens): # TODO __await__, ...? @@ -127,21 +122,12 @@ def unasync_files(fpath_list, rules): Token = collections.namedtuple("Token", ["type", "string", "start", "end", "line"]) -def _get_tokens(f): - if sys.version_info[0] == 2: - for tok in std_tokenize.generate_tokens(f.readline): - type_, string, start, end, line = tok - yield Token(type_, string, start, end, line) - else: - for tok in std_tokenize.tokenize(f.readline): - if tok.type == std_tokenize.ENCODING: - continue - yield tok - - def _tokenize(f): last_end = (1, 0) - for tok in _get_tokens(f): + for tok in std_tokenize.tokenize(f.readline): + if tok.type == std_tokenize.ENCODING: + continue + if last_end[0] < tok.start[0]: yield ("", std_tokenize.STRING, " \\\n") last_end = (tok.start[0], 0) @@ -161,14 +147,6 @@ def _untokenize(tokens): return "".join(space + tokval for space, tokval in tokens) -def _makedirs_existok(dir): - try: - os.makedirs(dir) - except OSError as e: - if e.errno != errno.EEXIST: - raise - - _DEFAULT_RULE = Rule(fromdir="/_async/", todir="/_sync/") @@ -201,7 +179,7 @@ def run(self): self.byte_compile(self.get_outputs(include_bytecode=0)) def build_module(self, module, module_file, package): - outfile, copied = orig.build_py.build_module(self, module, module_file, package) + outfile, copied = super().build_module(module, module_file, package) if copied: self._updated_files.append(outfile) return outfile, copied diff --git a/tests/data/async/classes.py b/tests/data/async/classes.py index 5a298ce..2ef9215 100644 --- a/tests/data/async/classes.py +++ b/tests/data/async/classes.py @@ -1,8 +1,8 @@ -class AsyncLock(object): +class AsyncLock: ... -class AsyncSocket(object): +class AsyncSocket: def __init__(self, send_lock: AsyncLock): ... diff --git a/tests/data/sync/classes.py b/tests/data/sync/classes.py index 845e2ab..37ba775 100644 --- a/tests/data/sync/classes.py +++ b/tests/data/sync/classes.py @@ -1,8 +1,8 @@ -class SyncLock(object): +class SyncLock: ... -class SyncSocket(object): +class SyncSocket: def __init__(self, send_lock: SyncLock): ... diff --git a/tests/test_unasync.py b/tests/test_unasync.py index 6f198d0..35e0c6c 100644 --- a/tests/test_unasync.py +++ b/tests/test_unasync.py @@ -1,6 +1,5 @@ import copy import errno -import io import os import shutil import subprocess @@ -12,7 +11,7 @@ TEST_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data") ASYNC_DIR = os.path.join(TEST_DIR, "async") SYNC_DIR = os.path.join(TEST_DIR, "sync") -TEST_FILES = sorted([f for f in os.listdir(ASYNC_DIR) if f.endswith(".py")]) +TEST_FILES = sorted(f for f in os.listdir(ASYNC_DIR) if f.endswith(".py")) def list_files(startpath): @@ -20,11 +19,11 @@ def list_files(startpath): for root, dirs, files in os.walk(startpath): level = root.replace(startpath, "").count(os.sep) indent = " " * 4 * (level) - output += "{}{}/".format(indent, os.path.basename(root)) + output += f"{indent}{os.path.basename(root)}/" output += "\n" subindent = " " * 4 * (level + 1) for f in files: - output += "{}{}".format(subindent, f) + output += f"{subindent}{f}" output += "\n" return output @@ -41,9 +40,9 @@ def test_unasync(tmpdir, source_file): rule._unasync_file(os.path.join(ASYNC_DIR, source_file)) encoding = "latin-1" if "encoding" in source_file else "utf-8" - with io.open(os.path.join(SYNC_DIR, source_file), encoding=encoding) as f: + with open(os.path.join(SYNC_DIR, source_file), encoding=encoding) as f: truth = f.read() - with io.open(os.path.join(str(tmpdir), source_file), encoding=encoding) as f: + with open(os.path.join(str(tmpdir), source_file), encoding=encoding) as f: unasynced_code = f.read() assert unasynced_code == truth @@ -57,9 +56,9 @@ def test_unasync_files(tmpdir): for source_file in TEST_FILES: encoding = "latin-1" if "encoding" in source_file else "utf-8" - with io.open(os.path.join(SYNC_DIR, source_file), encoding=encoding) as f: + with open(os.path.join(SYNC_DIR, source_file), encoding=encoding) as f: truth = f.read() - with io.open(os.path.join(str(tmpdir), source_file), encoding=encoding) as f: + with open(os.path.join(str(tmpdir), source_file), encoding=encoding) as f: unasynced_code = f.read() assert unasynced_code == truth @@ -139,13 +138,3 @@ def test_project_structure_after_customized_build_py_packages(tmpdir): with open(os.path.join(unasynced_dir_path, "tests/test_conn.py")) as f: assert "import hip\n" in f.read() - - -def test_makedirs_existok(monkeypatch): - def raises(*args, **kwargs): - # Unexpected OSError - raise OSError(errno.EPERM, "Operation not permitted") - - monkeypatch.setattr(os, "makedirs", raises) - with pytest.raises(OSError): - unasync._makedirs_existok("path")