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

chore[ci]: enable python3.12 tests #3860

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ jobs:
- name: Install Dependencies
run: pip install .[lint]

- name: Debug dependencies
run: pip freeze

- name: Run Black
run: black --check -C --force-exclude=vyper/version.py ./vyper ./tests ./setup.py

Expand Down Expand Up @@ -93,9 +96,10 @@ jobs:
opt-mode: gas
debug: false
evm-version: shanghai

# TODO 3.12 doesn't work yet, investigate - may be hypothesis issue
#- python-version: ["3.12", "312"]
- python-version: ["3.12", "312"]
opt-mode: gas
debug: false
evm-version: shanghai

name: py${{ matrix.python-version[1] }}-opt-${{ matrix.opt-mode }}${{ matrix.debug && '-debug' || '' }}-${{ matrix.evm-version }}

Expand All @@ -114,6 +118,9 @@ jobs:
- name: Install dependencies
run: pip install .[test]

- name: Debug dependencies
run: pip freeze

- name: Run tests
run: |
pytest \
Expand Down
8 changes: 5 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

extras_require = {
"test": [
"pytest>=6.2.5,<7.0",
"pytest>=8.0,<9.0",
"pytest-cov>=2.10,<3.0",
"pytest-instafail>=0.4,<1.0",
"pytest-xdist>=2.5,<3.0",
Expand All @@ -19,8 +19,9 @@
"web3==6.0.0",
"tox>=3.15,<4.0",
"lark==1.1.9",
"hypothesis[lark]>=5.37.1,<6.0",
"eth-stdlib==0.2.6",
"hypothesis[lark]>=6.0,<7.0",
"eth-stdlib==0.2.7",
"setuptools",
],
"lint": [
"black==23.12.0",
Expand Down Expand Up @@ -115,6 +116,7 @@ def _global_version(version):
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
],
package_data={"vyper.ast": ["grammar.lark"]},
data_files=[("", [hash_file_rel_path])],
Expand Down
17 changes: 12 additions & 5 deletions tests/functional/builtins/codegen/test_slice.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import pytest
from hypothesis import given, settings

from vyper.compiler.settings import OptimizationLevel
from vyper.compiler import compile_code
from vyper.compiler.settings import OptimizationLevel, Settings
from vyper.exceptions import ArgumentException, TypeMismatch

_fun_bytes32_bounds = [(0, 32), (3, 29), (27, 5), (0, 5), (5, 3), (30, 2)]
Expand Down Expand Up @@ -32,6 +33,12 @@ def slice_tower_test(inp1: Bytes[50]) -> Bytes[50]:
_bytes_1024 = st.binary(min_size=0, max_size=1024)


def _fail_contract(code, opt_level, exceptions):
settings = Settings(optimize=opt_level)
with pytest.raises(exceptions):
compile_code(code, settings)


@pytest.mark.parametrize("use_literal_start", (True, False))
@pytest.mark.parametrize("use_literal_length", (True, False))
@pytest.mark.parametrize("opt_level", list(OptimizationLevel))
Expand All @@ -40,7 +47,6 @@ def slice_tower_test(inp1: Bytes[50]) -> Bytes[50]:
@pytest.mark.fuzzing
def test_slice_immutable(
get_contract,
assert_compile_failed,
tx_failed,
opt_level,
bytesdata,
Expand Down Expand Up @@ -76,7 +82,8 @@ def _get_contract():
or (use_literal_start and start > length_bound)
or (use_literal_length and length == 0)
):
assert_compile_failed(lambda: _get_contract(), ArgumentException)
_fail_contract(code, opt_level, ArgumentException)

elif start + length > len(bytesdata) or (len(bytesdata) > length_bound):
# deploy fail
with tx_failed():
Expand All @@ -95,7 +102,6 @@ def _get_contract():
@pytest.mark.fuzzing
def test_slice_bytes_fuzz(
get_contract,
assert_compile_failed,
tx_failed,
opt_level,
location,
Expand Down Expand Up @@ -173,7 +179,8 @@ def _get_contract():
)

if compile_time_oob or slice_output_too_large:
assert_compile_failed(lambda: _get_contract(), (ArgumentException, TypeMismatch))
_fail_contract(code, opt_level, (ArgumentException, TypeMismatch))

elif location == "code" and len(bytesdata) > length_bound:
# deploy fail
with tx_failed():
Expand Down
2 changes: 1 addition & 1 deletion tests/functional/builtins/folding/test_powmod.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from tests.utils import parse_and_fold

st_uint256 = st.integers(min_value=0, max_value=2**256)
st_uint256 = st.integers(min_value=0, max_value=(2**256 - 1))


@pytest.mark.fuzzing
Expand Down
2 changes: 1 addition & 1 deletion tests/functional/codegen/types/test_bytes_zero_padding.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def get_count(counter: uint256) -> Bytes[24]:


@pytest.mark.fuzzing
@hypothesis.given(value=hypothesis.strategies.integers(min_value=0, max_value=2**64))
@hypothesis.given(value=hypothesis.strategies.integers(min_value=0, max_value=(2**64 - 1)))
def test_zero_pad_range(little_endian_contract, value):
actual_bytes = value.to_bytes(8, byteorder="little")
contract_bytes = little_endian_contract.get_count(value)
Expand Down
42 changes: 19 additions & 23 deletions tests/functional/grammar/test_grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,36 +37,31 @@ def test_basic_grammar_empty():
assert len(tree.children) == 0


def utf8_encodable(terminal: str) -> bool:
try:
if "\x00" not in terminal and "\\ " not in terminal and "\x0c" not in terminal:
terminal.encode("utf-8-sig")
return True
else:
return False
except UnicodeEncodeError: # pragma: no cover
# Very rarely, a "." in some terminal regex will generate a surrogate
# character that cannot be encoded as UTF-8. We apply this filter to
# ensure it doesn't happen at runtime, but don't worry about coverage.
return False
def fix_terminal(terminal: str) -> bool:
# these throw exceptions in the grammar
for bad in ("\x00", "\\ ", "\x0c"):
terminal = terminal.replace(bad, " ")
return terminal


ALLOWED_CHARS = st.characters(codec="utf-8", min_codepoint=1)


# With help from hyposmith
# https://github.com/Zac-HD/hypothesmith/blob/master/src/hypothesmith/syntactic.py
class GrammarStrategy(LarkStrategy):
def __init__(self, grammar, start, explicit_strategies):
super().__init__(grammar, start, explicit_strategies)
super().__init__(grammar, start, explicit_strategies, alphabet=ALLOWED_CHARS)
self.terminal_strategies = {
k: v.map(lambda s: s.replace("\0", "")).filter(utf8_encodable)
for k, v in self.terminal_strategies.items() # type: ignore
k: v.map(fix_terminal) for k, v in self.terminal_strategies.items() # type: ignore
}

def draw_symbol(self, data, symbol, draw_state): # type: ignore
count = len(draw_state.result)
count = len(draw_state)
super().draw_symbol(data, symbol, draw_state)
try:
compile(
source="".join(draw_state.result[count:])
source="".join(draw_state[count:])
.replace("contract", "class")
.replace("struct", "class"), # HACK: Python ast.parse
filename="<string>",
Expand Down Expand Up @@ -102,10 +97,11 @@ def has_no_docstrings(c):


@pytest.mark.fuzzing
@given(code=from_grammar().filter(lambda c: utf8_encodable(c)))
@hypothesis.settings(max_examples=500, suppress_health_check=[HealthCheck.too_slow])
@given(code=from_grammar())
@hypothesis.settings(
max_examples=500, suppress_health_check=[HealthCheck.too_slow, HealthCheck.filter_too_much]
)
def test_grammar_bruteforce(code):
if utf8_encodable(code):
_, _, _, reformatted_code = pre_parse(code + "\n")
tree = parse_to_ast(reformatted_code)
assert isinstance(tree, Module)
_, _, _, reformatted_code = pre_parse(code + "\n")
tree = parse_to_ast(reformatted_code)
assert isinstance(tree, Module)
10 changes: 5 additions & 5 deletions tests/functional/syntax/test_structs.py
Original file line number Diff line number Diff line change
Expand Up @@ -589,9 +589,9 @@ def foo():
with warnings.catch_warnings(record=True) as w:
assert compiler.compile_code(code) is not None

expected = "Instantiating a struct using a dictionary is deprecated "
expected += "as of v0.4.0 and will be disallowed in a future release. "
expected += "Use kwargs instead e.g. Foo(a=1, b=2)"
expected = "Instantiating a struct using a dictionary is deprecated "
expected += "as of v0.4.0 and will be disallowed in a future release. "
expected += "Use kwargs instead e.g. Foo(a=1, b=2)"

assert len(w) == 1
assert str(w[0].message).startswith(expected)
assert len(w) == 1, [s.message for s in w]
assert str(w[0].message).startswith(expected)
2 changes: 0 additions & 2 deletions vyper/ast/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,6 @@ def is_literal_value(self):
class Num(Constant):
# inherited class for all numeric constant node types
__slots__ = ()
_translated_fields = {"n": "value"}

@property
def n(self):
Expand Down Expand Up @@ -843,7 +842,6 @@ class Hex(Constant):
"""

__slots__ = ()
_translated_fields = {"n": "value"}

def validate(self):
if "_" in self.value:
Expand Down
12 changes: 6 additions & 6 deletions vyper/ast/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ def visit_Num(self, node):
node.col_offset,
)
node.ast_type = "Hex"
node.n = value
node.value = value

elif value.lower()[:2] == "0b":
node.ast_type = "Bytes"
Expand All @@ -449,15 +449,15 @@ def visit_Num(self, node):
)
node.value = int(value, 2).to_bytes(len(value) // 8, "big")

elif isinstance(node.n, float):
elif isinstance(node.value, float):
node.ast_type = "Decimal"
node.n = Decimal(value)
node.value = Decimal(value)

elif isinstance(node.n, int):
elif isinstance(node.value, int):
node.ast_type = "Int"

else:
raise CompilerPanic(f"Unexpected type for Constant value: {type(node.n).__name__}")
else: # pragma: nocover
raise CompilerPanic(f"Unexpected type for Constant value: {type(node.value).__name__}")

return node

Expand Down
Loading