diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..443d3cc5 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +# all end-of-lines are normalized to LF when written to the repository +# https://git-scm.com/docs/gitattributes#_text +* text=auto + +# force all text files on the working dir to have LF line endings +# https://git-scm.com/docs/gitattributes#_eol +* text eol=lf diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..05f2c776 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,73 @@ +on: push + +jobs: + tests: + # Set the type of machine to run on + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-20.04, windows-2019] + python-version: [3.9, 3.8, 3.7, 3.6] + environment: ['3.8', '3.9', '3.7', '3.6', 'interpreter'] + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + submodules: recursive + + - uses: actions/setup-python@v2 + if: ${{ matrix.environment != 'interpreter' }} + with: + python-version: ${{ matrix.environment }} + + - uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: 'pip install .[testing]' + + - name: Run tests + run: python -m pytest + env: + JEDI_TEST_ENVIRONMENT: ${{ matrix.environment }} + + code-quality: + runs-on: ubuntu-20.04 + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Install dependencies + run: 'pip install .[qa]' + + - name: Run tests + run: | + python -m flake8 jedi setup.py + python -m mypy jedi sith.py + + coverage: + runs-on: ubuntu-20.04 + + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Install dependencies + run: 'pip install .[testing] coverage' + + - name: Run tests + run: | + python -m coverage run --source jedi -m pytest + python -m coverage report + + - name: Upload coverage data + run: | + pip install --quiet codecov coveralls + python -m coverage xml + python -m coverage report -m + bash <(curl -s https://codecov.io/bash) -X gcov -X coveragepy -X search -X fix -X xcode -f coverage.xml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e16e2689..00000000 --- a/.travis.yml +++ /dev/null @@ -1,74 +0,0 @@ -dist: xenial -language: python -python: - - 3.9-dev - - 3.8 - - 3.7 - - 3.6 - -env: - - JEDI_TEST_ENVIRONMENT=38 - - JEDI_TEST_ENVIRONMENT=39 - - JEDI_TEST_ENVIRONMENT=37 - - JEDI_TEST_ENVIRONMENT=36 - - JEDI_TEST_ENVIRONMENT=interpreter - -matrix: - include: - - python: 3.8 - script: - - 'pip install coverage' - - 'coverage run --source jedi -m pytest' - - 'coverage report' - after_script: - - | - pip install --quiet codecov coveralls - coverage xml - coverage report -m - coveralls - bash <(curl -s https://codecov.io/bash) -X gcov -X coveragepy -X search -X fix -X xcode -f coverage.xml - - python: 3.8 - install: - - 'pip install .[qa]' - script: - - 'flake8 jedi setup.py' - - 'mypy jedi sith.py' -install: - - sudo apt-get -y install python3-venv - - pip install .[testing] -script: - - | - # Setup/install Python for $JEDI_TEST_ENVIRONMENT. - set -ex - test_env_version=${JEDI_TEST_ENVIRONMENT:0:1}.${JEDI_TEST_ENVIRONMENT:1:1} - if [ "$TRAVIS_PYTHON_VERSION" != "$test_env_version" ] && [ "$JEDI_TEST_ENVIRONMENT" != "interpreter" ]; then - python_bin=python$test_env_version - python_path="$(which $python_bin || true)" - if [ -z "$python_path" ]; then - # Only required for JEDI_TEST_ENVIRONMENT=38, because it's not always - # available. - download_name=python-$test_env_version - if [ "$JEDI_TEST_ENVIRONMENT" == "39" ]; then - wget https://storage.googleapis.com/travis-ci-language-archives/python/binaries/ubuntu/16.04/x86_64/python-3.9-dev.tar.bz2 - sudo tar xjf python-3.9-dev.tar.bz2 --directory / opt/python - ln -s "/opt/python/3.9-dev/bin/python" /home/travis/bin/python3.9 - else - wget https://s3.amazonaws.com/travis-python-archives/binaries/ubuntu/16.04/x86_64/$download_name.tar.bz2 - sudo tar xjf $download_name.tar.bz2 --directory / opt/python - ln -s "/opt/python/${test_env_version}/bin/python" /home/travis/bin/$python_bin - fi - elif [ "${python_path#/opt/pyenv/shims}" != "$python_path" ]; then - # Activate pyenv version (required with JEDI_TEST_ENVIRONMENT=36). - pyenv_bin="$(pyenv whence --path "$python_bin" | head -n1)" - ln -s "$pyenv_bin" /home/travis/bin/$python_bin - fi - $python_bin --version - python_ver=$($python_bin -c 'import sys; print("%d%d" % sys.version_info[0:2])') - if [ "$JEDI_TEST_ENVIRONMENT" != "$python_ver" ]; then - echo "Unexpected Python version for $JEDI_TEST_ENVIRONMENT: $python_ver" - set +ex - exit 2 - fi - fi - set +ex - - pytest diff --git a/README.rst b/README.rst index f63feb8d..7d0d7106 100644 --- a/README.rst +++ b/README.rst @@ -10,17 +10,9 @@ Jedi - an awesome autocompletion, static analysis and refactoring library for Py :target: https://github.com/davidhalter/jedi/issues :alt: The resolution time is the median time an issue or pull request stays open. -.. image:: https://travis-ci.org/davidhalter/jedi.svg?branch=master - :target: https://travis-ci.org/davidhalter/jedi - :alt: Linux Tests - -.. image:: https://ci.appveyor.com/api/projects/status/mgva3bbawyma1new/branch/master?svg=true - :target: https://ci.appveyor.com/project/davidhalter/jedi/branch/master - :alt: Windows Tests - -.. image:: https://coveralls.io/repos/davidhalter/jedi/badge.svg?branch=master - :target: https://coveralls.io/r/davidhalter/jedi - :alt: Coverage status +.. image:: https://github.com/davidhalter/jedi/workflows/ci/badge.svg?branch=master + :target: https://github.com/davidhalter/jedi/actions + :alt: Tests .. image:: https://pepy.tech/badge/jedi :target: https://pepy.tech/project/jedi diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index df1ed05d..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,17 +0,0 @@ -environment: - matrix: - - PYTHON_PATH: C:\Python37 - JEDI_TEST_ENVIRONMENT: 37 - - PYTHON_PATH: C:\Python37 - JEDI_TEST_ENVIRONMENT: 36 - - - PYTHON_PATH: C:\Python36 - JEDI_TEST_ENVIRONMENT: 37 - - PYTHON_PATH: C:\Python36 - JEDI_TEST_ENVIRONMENT: 36 -install: - - git submodule update --init --recursive - - set PATH=%PYTHON_PATH%;%PYTHON_PATH%\Scripts;%PATH% - - pip install .[testing] -build_script: - - pytest diff --git a/conftest.py b/conftest.py index c6d02216..eeb531c3 100644 --- a/conftest.py +++ b/conftest.py @@ -100,7 +100,9 @@ def environment(request): if request.config.option.interpreter_env or version == 'interpreter': return InterpreterEnvironment() - return get_system_environment(version[0] + '.' + version[1:]) + if '.' not in version: + version = version[0] + '.' + version[1:] + return get_system_environment(version) @pytest.fixture(scope='session') diff --git a/docs/docs/testing.rst b/docs/docs/testing.rst index fdb36658..223cc292 100644 --- a/docs/docs/testing.rst +++ b/docs/docs/testing.rst @@ -12,8 +12,8 @@ easy as:: python3.8 -m pytest -Tests are also run automatically on `Travis CI -`_. +Tests are also run automatically on `GitHub Actions +`_. You want to add a test for |jedi|? Great! We love that. Normally you should write your tests as :ref:`Blackbox Tests `. Most tests would diff --git a/docs/index.rst b/docs/index.rst index 40117d49..78435fbf 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -18,13 +18,9 @@ Jedi - an awesome autocompletion, static analysis and refactoring library for Py :target: https://github.com/davidhalter/jedi/issues :alt: The resolution time is the median time an issue or pull request stays open. -.. image:: https://travis-ci.org/davidhalter/jedi.svg?branch=master - :target: https://travis-ci.org/davidhalter/jedi - :alt: Linux Tests - -.. image:: https://ci.appveyor.com/api/projects/status/mgva3bbawyma1new/branch/master?svg=true - :target: https://ci.appveyor.com/project/davidhalter/jedi/branch/master - :alt: Windows Tests +.. image:: https://github.com/davidhalter/jedi/workflows/ci/badge.svg?branch=master + :target: https://github.com/davidhalter/jedi/actions + :alt: Tests .. image:: https://coveralls.io/repos/davidhalter/jedi/badge.svg?branch=master :target: https://coveralls.io/r/davidhalter/jedi @@ -73,5 +69,4 @@ mailing list: https://groups.google.com/g/jedi-announce. To subscribe you can simply send an empty email to ``jedi-announce+subscribe@googlegroups.com``. - `Source Code on Github `_ -- `Travis Testing `_ - `Python Package Index `_ diff --git a/jedi/plugins/pytest.py b/jedi/plugins/pytest.py index cec23733..d0dfba01 100644 --- a/jedi/plugins/pytest.py +++ b/jedi/plugins/pytest.py @@ -5,6 +5,7 @@ from jedi.inference.cache import inference_state_method_cache from jedi.inference.imports import load_module_from_path from jedi.inference.filters import ParserTreeFilter from jedi.inference.base_value import NO_VALUES, ValueSet +from jedi.inference.helpers import infer_call_of_leaf _PYTEST_FIXTURE_MODULES = [ ('_pytest', 'monkeypatch'), @@ -147,18 +148,35 @@ class FixtureFilter(ParserTreeFilter): def _filter(self, names): for name in super()._filter(names): funcdef = name.parent + # Class fixtures are not supported if funcdef.type == 'funcdef': - # Class fixtures are not supported decorated = funcdef.parent if decorated.type == 'decorated' and self._is_fixture(decorated): yield name def _is_fixture(self, decorated): - for decorator in decorated.children: + decorators = decorated.children[0] + if decorators.type == 'decorators': + decorators = decorators.children + else: + decorators = [decorators] + for decorator in decorators: dotted_name = decorator.children[1] # A heuristic, this makes it faster. if 'fixture' in dotted_name.get_code(): - for value in self.parent_context.infer_node(dotted_name): + if dotted_name.type == 'atom_expr': + # Since Python3.9 a decorator does not have dotted names + # anymore. + last_trailer = dotted_name.children[-1] + last_leaf = last_trailer.get_last_leaf() + if last_leaf == ')': + values = infer_call_of_leaf( + self.parent_context, last_leaf, cut_own_trailer=True) + else: + values = self.parent_context.infer_node(dotted_name) + else: + values = self.parent_context.infer_node(dotted_name) + for value in values: if value.name.get_qualified_names(include_module_names=True) \ == ('_pytest', 'fixtures', 'fixture'): return True diff --git a/test/refactor.py b/test/refactor.py index 582beeb6..7598bc7d 100644 --- a/test/refactor.py +++ b/test/refactor.py @@ -82,9 +82,9 @@ def _collect_file_tests(code, path, lines_to_execute): yield RefactoringCase(name, first, line_nr, index, path, kwargs, type_, second) if match is None: - raise Exception("Didn't match any test") + raise Exception(f"Didn't match any test for {path}, {code!r}") if match.end() != len(code): - raise Exception("Didn't match until the end of the file in %s" % path) + raise Exception(f"Didn't match until the end of the file in {path}") def collect_dir_tests(base_dir, test_files): diff --git a/test/test_api/test_api.py b/test/test_api/test_api.py index 51c0da3b..d2ebe62b 100644 --- a/test/test_api/test_api.py +++ b/test/test_api/test_api.py @@ -169,7 +169,8 @@ def test_reference_description(Script): def test_get_line_code(Script): def get_line_code(source, line=None, **kwargs): - return Script(source).complete(line=line)[0].get_line_code(**kwargs) + # On Windows replace \r + return Script(source).complete(line=line)[0].get_line_code(**kwargs).replace('\r', '') # On builtin assert get_line_code('abs') == 'def abs(__n: SupportsAbs[_T]) -> _T: ...\n' diff --git a/test/test_inference/test_extension.py b/test/test_inference/test_extension.py index 962a8f9c..85cd7c01 100644 --- a/test/test_inference/test_extension.py +++ b/test/test_inference/test_extension.py @@ -13,17 +13,15 @@ def test_completions(Script): assert len(s.complete()) >= 15 -def test_get_signatures_extension(Script): +def test_get_signatures_extension(Script, environment): if os.name == 'nt': func = 'LoadLibrary' - params = 1 else: func = 'dlopen' - params = 2 s = Script('import _ctypes; _ctypes.%s(' % (func,)) sigs = s.get_signatures() assert len(sigs) == 1 - assert len(sigs[0].params) == params + assert len(sigs[0].params) in (1, 2) def test_get_signatures_stdlib(Script): diff --git a/test/test_inference/test_gradual/test_conversion.py b/test/test_inference/test_gradual/test_conversion.py index c77a06b0..ea9ea013 100644 --- a/test/test_inference/test_gradual/test_conversion.py +++ b/test/test_inference/test_gradual/test_conversion.py @@ -69,11 +69,12 @@ def test_stub_get_line_code(Script): code = 'from abc import ABC; ABC' script = Script(code) d, = script.goto(only_stubs=True) - assert d.get_line_code() == 'class ABC(metaclass=ABCMeta): ...\n' + # Replace \r for tests on Windows + assert d.get_line_code().replace('\r', '') == 'class ABC(metaclass=ABCMeta): ...\n' del parser_cache[script._inference_state.latest_grammar._hashed][d.module_path] d, = Script(path=d.module_path).goto(d.line, d.column, only_stubs=True) assert d.is_stub() - assert d.get_line_code() == 'class ABC(metaclass=ABCMeta): ...\n' + assert d.get_line_code().replace('\r', '') == 'class ABC(metaclass=ABCMeta): ...\n' def test_os_stat_result(Script):