conftest.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import re
  2. import tempfile
  3. import shutil
  4. import logging
  5. import os
  6. from pathlib import Path
  7. import pytest
  8. import yatest.common
  9. import parso
  10. from parso import cache
  11. from parso.utils import parse_version_string
  12. collect_ignore = ["setup.py"]
  13. _SUPPORTED_VERSIONS = '3.6', '3.7', '3.8', '3.9', '3.10'
  14. @pytest.fixture(scope='session')
  15. def clean_parso_cache():
  16. """
  17. Set the default cache directory to a temporary directory during tests.
  18. Note that you can't use built-in `tmpdir` and `monkeypatch`
  19. fixture here because their scope is 'function', which is not used
  20. in 'session' scope fixture.
  21. This fixture is activated in ../pytest.ini.
  22. """
  23. old = cache._default_cache_path
  24. tmp = tempfile.mkdtemp(prefix='parso-test-')
  25. cache._default_cache_path = Path(tmp)
  26. yield
  27. cache._default_cache_path = old
  28. shutil.rmtree(tmp)
  29. def pytest_addoption(parser):
  30. parser.addoption("--logging", "-L", action='store_true',
  31. help="Enables the logging output.")
  32. def pytest_generate_tests(metafunc):
  33. if 'normalizer_issue_case' in metafunc.fixturenames:
  34. base_dir = os.path.join(yatest.common.test_source_path(), 'normalizer_issue_files')
  35. cases = list(colllect_normalizer_tests(base_dir))
  36. metafunc.parametrize(
  37. 'normalizer_issue_case',
  38. cases,
  39. ids=[c.name for c in cases]
  40. )
  41. elif 'each_version' in metafunc.fixturenames:
  42. metafunc.parametrize('each_version', _SUPPORTED_VERSIONS)
  43. elif 'version_ge_py38' in metafunc.fixturenames:
  44. ge38 = set(_SUPPORTED_VERSIONS) - {'3.6', '3.7'}
  45. metafunc.parametrize('version_ge_py38', sorted(ge38))
  46. class NormalizerIssueCase:
  47. """
  48. Static Analysis cases lie in the static_analysis folder.
  49. The tests also start with `#!`, like the goto_definition tests.
  50. """
  51. def __init__(self, path):
  52. self.path = path
  53. self.name = os.path.basename(path)
  54. match = re.search(r'python([\d.]+)\.py', self.name)
  55. self.python_version = match and match.group(1)
  56. def colllect_normalizer_tests(base_dir):
  57. for f_name in os.listdir(base_dir):
  58. if f_name.endswith(".py"):
  59. path = os.path.join(base_dir, f_name)
  60. yield NormalizerIssueCase(path)
  61. def pytest_configure(config):
  62. if config.option.logging:
  63. root = logging.getLogger()
  64. root.setLevel(logging.DEBUG)
  65. #ch = logging.StreamHandler(sys.stdout)
  66. #ch.setLevel(logging.DEBUG)
  67. #formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
  68. #ch.setFormatter(formatter)
  69. #root.addHandler(ch)
  70. class Checker:
  71. def __init__(self, version, is_passing):
  72. self.version = version
  73. self._is_passing = is_passing
  74. self.grammar = parso.load_grammar(version=self.version)
  75. def parse(self, code):
  76. if self._is_passing:
  77. return parso.parse(code, version=self.version, error_recovery=False)
  78. else:
  79. self._invalid_syntax(code)
  80. def _invalid_syntax(self, code):
  81. with pytest.raises(parso.ParserSyntaxError):
  82. module = parso.parse(code, version=self.version, error_recovery=False)
  83. # For debugging
  84. print(module.children)
  85. def get_error(self, code):
  86. errors = list(self.grammar.iter_errors(self.grammar.parse(code)))
  87. assert bool(errors) != self._is_passing
  88. if errors:
  89. return errors[0]
  90. def get_error_message(self, code):
  91. error = self.get_error(code)
  92. if error is None:
  93. return
  94. return error.message
  95. def assert_no_error_in_passing(self, code):
  96. if self._is_passing:
  97. module = self.grammar.parse(code)
  98. assert not list(self.grammar.iter_errors(module))
  99. @pytest.fixture
  100. def works_not_in_py(each_version):
  101. return Checker(each_version, False)
  102. @pytest.fixture
  103. def works_in_py(each_version):
  104. return Checker(each_version, True)
  105. @pytest.fixture
  106. def works_ge_py38(each_version):
  107. version_info = parse_version_string(each_version)
  108. return Checker(each_version, version_info >= (3, 8))
  109. @pytest.fixture
  110. def works_ge_py39(each_version):
  111. version_info = parse_version_string(each_version)
  112. return Checker(each_version, version_info >= (3, 9))