Allow install under pypy (#2559)

Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com>
This commit is contained in:
Oliver Margetts 2021-11-14 03:46:15 +00:00 committed by GitHub
parent 1e0ec543ff
commit eb9d0396cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 59 additions and 13 deletions

View File

@ -24,7 +24,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "pypy-3.7"]
os: [ubuntu-latest, macOS-latest, windows-latest]
steps:
@ -41,9 +41,15 @@ jobs:
python -m pip install --upgrade tox
- name: Unit tests
if: "!startsWith(matrix.python-version, 'pypy')"
run: |
tox -e ci-py -- -v --color=yes
- name: Unit tests pypy
if: "startsWith(matrix.python-version, 'pypy')"
run: |
tox -e ci-pypy3 -- -v --color=yes
- name: Publish coverage to Coveralls
# If pushed / is a pull request against main repo AND
# we're running on Linux (this action only supports Linux)

View File

@ -6,6 +6,7 @@
- Warn about Python 2 deprecation in more cases by improving Python 2 only syntax
detection (#2592)
- Add experimental PyPy support (#2559)
- Add partial support for the match statement. As it's experimental, it's only enabled
when `--target-version py310` is explicitly specified (#2586)
- Add support for parenthesized with (#2586)

View File

@ -92,3 +92,8 @@ influence their behavior. While Black does its best to recognize such comments a
them in the right place, this detection is not and cannot be perfect. Therefore, you'll
sometimes have to manually move these comments to the right place after you format your
codebase with _Black_.
## Can I run black with PyPy?
Yes, there is support for PyPy 3.7 and higher. You cannot format Python 2 files under
PyPy, because PyPy's inbuilt ast module does not support this.

View File

@ -75,7 +75,7 @@ def get_long_description() -> str:
"click>=7.1.2",
"platformdirs>=2",
"tomli>=0.2.6,<2.0.0",
"typed-ast>=1.4.2; python_version < '3.8'",
"typed-ast>=1.4.2; python_version < '3.8' and implementation_name == 'cpython'",
"regex>=2020.1.8",
"pathspec>=0.9.0, <1",
"dataclasses>=0.6; python_version < '3.7'",

View File

@ -35,7 +35,7 @@ def read_cache(mode: Mode) -> Cache:
with cache_file.open("rb") as fobj:
try:
cache: Cache = pickle.load(fobj)
except (pickle.UnpicklingError, ValueError):
except (pickle.UnpicklingError, ValueError, IndexError):
return {}
return cache

View File

@ -2,6 +2,7 @@
Parse Python code and perform AST validation.
"""
import ast
import platform
import sys
from typing import Iterable, Iterator, List, Set, Union, Tuple
@ -15,10 +16,13 @@
from black.mode import TargetVersion, Feature, supports_feature
from black.nodes import syms
_IS_PYPY = platform.python_implementation() == "PyPy"
try:
from typed_ast import ast3, ast27
except ImportError:
if sys.version_info < (3, 8):
# Either our python version is too low, or we're on pypy
if sys.version_info < (3, 7) or (sys.version_info < (3, 8) and not _IS_PYPY):
print(
"The typed_ast package is required but not installed.\n"
"You can upgrade to Python 3.8+ or install typed_ast with\n"
@ -117,6 +121,9 @@ def parse_single_version(
if sys.version_info >= (3, 8) and version >= (3,):
return ast.parse(src, filename, feature_version=version)
elif version >= (3,):
if _IS_PYPY:
return ast3.parse(src, filename)
else:
return ast3.parse(src, filename, feature_version=version[1])
elif version == (2, 7):
return ast27.parse(src)
@ -151,6 +158,8 @@ def stringify_ast(
yield f"{' ' * depth}{node.__class__.__name__}("
for field in sorted(node._fields): # noqa: F402
# TypeIgnore will not be present using pypy < 3.8, so need for this
if not (_IS_PYPY and sys.version_info < (3, 8)):
# TypeIgnore has only one field 'lineno' which breaks this comparison
type_ignore_classes = (ast3.TypeIgnore, ast27.TypeIgnore)
if sys.version_info >= (3, 8):

View File

@ -944,7 +944,7 @@ def test_broken_symlink(self) -> None:
symlink = workspace / "broken_link.py"
try:
symlink.symlink_to("nonexistent.py")
except OSError as e:
except (OSError, NotImplementedError) as e:
self.skipTest(f"Can't create symlinks: {e}")
self.invokeBlack([str(workspace.resolve())])

27
tox.ini
View File

@ -1,5 +1,5 @@
[tox]
envlist = {,ci-}py{36,37,38,39,310},fuzz
envlist = {,ci-}py{36,37,38,39,310,py3},fuzz
[testenv]
setenv = PYTHONPATH = {toxinidir}/src
@ -31,6 +31,31 @@ commands =
--cov --cov-append {posargs}
coverage report
[testenv:{,ci-}pypy3]
setenv = PYTHONPATH = {toxinidir}/src
skip_install = True
recreate = True
deps =
-r{toxinidir}/test_requirements.txt
; a separate worker is required in ci due to https://foss.heptapod.net/pypy/pypy/-/issues/3317
; this seems to cause tox to wait forever
; remove this when pypy releases the bugfix
commands =
pip install -e .[d]
coverage erase
pytest tests --run-optional no_python2 \
--run-optional no_jupyter \
!ci: --numprocesses auto \
ci: --numprocesses 1 \
--cov {posargs}
pip install -e .[jupyter]
pytest tests --run-optional jupyter \
-m jupyter \
!ci: --numprocesses auto \
ci: --numprocesses 1 \
--cov --cov-append {posargs}
coverage report
[testenv:fuzz]
skip_install = True
deps =