Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Python 3.11 and PyPy 3.9 #2333

Merged
merged 10 commits into from
Aug 30, 2022
8 changes: 4 additions & 4 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ jobs:
matrix:
name-prefix: ['']
os: [ubuntu-latest]
python: [3.7, 3.8, 3.9, '3.10', pypy-3.8]
python: [3.7, 3.8, 3.9, '3.10', '3.11-dev', pypy-3.9]
include:
# To keep the overall number of runs low, we test Windows
# only on the latest CPython.
- name-prefix: 'win-'
os: windows-latest
python: '3.10'
python: '3.11-dev'

name: ${{ format('{0}{1}', matrix.name-prefix, matrix.python) }}
runs-on: ${{ matrix.os }}
Expand All @@ -28,8 +28,8 @@ jobs:

steps:
- run: git config --global core.autocrlf false
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python }}
- run: pip install . && rm -r hy
Expand Down
4 changes: 4 additions & 0 deletions NEWS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ Other Breaking Changes
`(dfor x (range 5) [x (* 2 x)])` is now `(dfor x (range 5) x (* 2
x))`.

New Features
------------------------------
* Python 3.11 is now supported.

Bug Fixes
------------------------------
* Fixed `hy.repr` of `slice` objects with non-integer arguments.
Expand Down
1 change: 1 addition & 0 deletions hy/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
PY3_8 = sys.version_info >= (3, 8)
PY3_9 = sys.version_info >= (3, 9)
PY3_10 = sys.version_info >= (3, 10)
PY3_11 = sys.version_info >= (3, 11)
PYPY = platform.python_implementation() == "PyPy"


Expand Down
11 changes: 6 additions & 5 deletions hy/cmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from pathlib import Path

import hy
from hy._compat import PY3_9
from hy._compat import PY3_9, PYPY
from hy.compiler import HyASTCompiler, hy_ast_compile_flags, hy_compile, hy_eval
from hy.completer import Completer, completion
from hy.errors import (
Expand Down Expand Up @@ -684,10 +684,11 @@ def proc_opt(opt, arg=None, item=None, i=None):
set_path(filename)
# Ensure __file__ is set correctly in the code we're about
# to run.
if PY3_9 and not filename.is_absolute():
filename = Path.cwd() / filename
if PY3_9 and platform.system() == "Windows":
filename = os.path.normpath(filename)
if PY3_9 and not PYPY:
if not filename.is_absolute():
filename = Path.cwd() / filename
if platform.system() == "Windows":
filename = os.path.normpath(filename)

try:
sys.argv = argv
Expand Down
19 changes: 17 additions & 2 deletions hy/macros.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import inspect
import os
import pkgutil
import re
import sys
import traceback
import warnings
Expand All @@ -11,7 +12,7 @@
from funcparserlib.parser import NoParseError

import hy.compiler
from hy._compat import code_replace
from hy._compat import PY3_11, code_replace
from hy.errors import (
HyLanguageError,
HyMacroExpansionError,
Expand Down Expand Up @@ -439,7 +440,21 @@ def macroexpand_1(tree, module, compiler=None):
def rename_function(f, new_name):
"""Create a copy of a function, but with a new name."""
f = type(f)(
code_replace(f.__code__, co_name=new_name),
code_replace(
f.__code__,
co_name=new_name,
**(
{
"co_qualname": re.sub(
r"\.[^.+]\Z", "." + new_name, f.__code__.co_qualname
)
if "." in f.__code__.co_qualname
else new_name
}
if PY3_11
else {}
),
),
f.__globals__,
str(new_name),
f.__defaults__,
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def run(self):
version=__version__,
setup_requires=["wheel"] + requires,
install_requires=requires,
python_requires=">= 3.7, < 3.11",
python_requires=">= 3.7, < 3.12",
entry_points={
"console_scripts": [
"hy = hy.cmdline:hy_main",
Expand Down Expand Up @@ -81,6 +81,7 @@ def run(self):
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Topic :: Software Development :: Code Generators",
"Topic :: Software Development :: Compilers",
"Topic :: Software Development :: Libraries",
Expand Down
2 changes: 1 addition & 1 deletion tests/macros/test_macro_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def test_preprocessor_exceptions():
"""Test that macro expansion raises appropriate exceptions"""
with pytest.raises(HyMacroExpansionError) as excinfo:
macroexpand(read("(when)"), __name__, HyASTCompiler(__name__))
assert "_hy_anon_" not in excinfo.value.msg
assert "TypeError: when()" in excinfo.value.msg


def test_macroexpand_nan():
Expand Down
9 changes: 5 additions & 4 deletions tests/test_bin.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import pytest

from hy._compat import PY3_9
from hy._compat import PY3_9, PYPY


def pyr(s=""):
Expand Down Expand Up @@ -522,6 +522,7 @@ def test_macro_require():
assert output.strip() == "abc"


@pytest.mark.skipif(PYPY, reason = 'https://foss.heptapod.net/pypy/pypy/-/issues/3800')
def test_tracebacks():
"""Make sure the printed tracebacks are correct."""

Expand Down Expand Up @@ -551,7 +552,6 @@ def req_err(x):

output, error = run_cmd('hy -i "(require not-a-real-module)"')
assert output.startswith("=> ")
print(error.splitlines())
req_err(error.splitlines()[2])

# Modeled after
Expand Down Expand Up @@ -587,7 +587,8 @@ def req_err(x):
# File "<string>", line 1, in <module>
# NameError: name 'a' is not defined
output, error = run_cmd('hy -c "(print a)"', expect=1)
error_lines = error.splitlines()
# Filter out the underline added by Python 3.11.
error_lines = [x for x in error.splitlines() if set(x) != {" ", "^"}]
assert error_lines[3] == ' File "<string>", line 1, in <module>'
# PyPy will add "global" to this error message, so we work around that.
assert error_lines[-1].strip().replace(" global", "") == (
Expand Down Expand Up @@ -663,7 +664,7 @@ def test_uufileuu(tmp_path, monkeypatch):
(tmp_path / "realdir" / "pyex.py").write_text('print(__file__)')

def file_is(arg, expected_py3_9):
expected = expected_py3_9 if PY3_9 else Path(arg)
expected = expected_py3_9 if PY3_9 and not PYPY else Path(arg)
output, _ = run_cmd("python3 " + shlex.quote(arg + "pyex.py"))
assert output.rstrip() == str(expected / "pyex.py")
output, _ = run_cmd("hy " + shlex.quote(arg + "hyex.hy"))
Expand Down
2 changes: 1 addition & 1 deletion tests/test_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def check_trace_output(capsys, execinfo, expected):
hy_exc_handler(execinfo.type, execinfo.value, execinfo.tb)
captured_w_filtering = capsys.readouterr()[-1].strip("\n")

output = [x.rstrip() for x in captured_w_filtering.split("\n")]
output = [x.rstrip() for x in captured_w_filtering.split("\n") if "^^^" not in x]

# Make sure the filtered frames aren't the same as the unfiltered ones.
assert output != captured_wo_filtering.split("\n")
Expand Down