Skip to content

Commit

Permalink
Merge branch 'master' into fix/internal-functions-not-in-metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
sandbubbles committed Dec 12, 2024
2 parents 3f90084 + 12ab491 commit e618cda
Show file tree
Hide file tree
Showing 40 changed files with 660 additions and 88 deletions.
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ jobs:
# modes across all python versions - one is enough
- python-version: ["3.10", "310"]
- python-version: ["3.12", "312"]
- python-version: ["3.13", "313"]

# os-specific rules
- os: windows
Expand Down
15 changes: 15 additions & 0 deletions docs/using-modules.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,21 @@ The ``_times_two()`` helper function in the above module can be immediately used
The other functions cannot be used yet, because they touch the ``ownable`` module's state. There are two ways to declare a module so that its state can be used.

Using a module as an interface
==============================

A module can be used as an interface with the ``__at__`` syntax.

.. code-block:: vyper
import ownable
an_ownable: ownable.__interface__
def call_ownable(addr: address):
self.an_ownable = ownable.__at__(addr)
self.an_ownable.transfer_ownership(...)
Initializing a module
=====================

Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ def _global_version(version):
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
],
package_data={"vyper.ast": ["grammar.lark"]},
data_files=[("", [hash_file_rel_path])],
Expand Down
17 changes: 17 additions & 0 deletions tests/functional/codegen/features/test_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -1254,6 +1254,23 @@ def foo():
assert log.topics == [event_id, topic1, topic2, topic3]


valid_list = [
# test constant folding inside raw_log
"""
topic: constant(bytes32) = 0x1212121212121210212801291212121212121210121212121212121212121212
@external
def foo():
raw_log([[topic]][0], b'')
"""
]


@pytest.mark.parametrize("code", valid_list)
def test_raw_log_pass(code):
assert compile_code(code) is not None


fail_list = [
(
"""
Expand Down
23 changes: 23 additions & 0 deletions tests/functional/codegen/modules/test_exports.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,3 +440,26 @@ def __init__():
# call `c.__default__()`
env.message_call(c.address)
assert c.counter() == 6


def test_inline_interface_export(make_input_bundle, get_contract):
lib1 = """
interface IAsset:
def asset() -> address: view
implements: IAsset
@external
@view
def asset() -> address:
return self
"""
main = """
import lib1
exports: lib1.IAsset
"""
input_bundle = make_input_bundle({"lib1.vy": lib1})
c = get_contract(main, input_bundle=input_bundle)

assert c.asset() == c.address
36 changes: 35 additions & 1 deletion tests/functional/codegen/modules/test_interface_imports.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import pytest


def test_import_interface_types(make_input_bundle, get_contract):
ifaces = """
interface IFoo:
Expand Down Expand Up @@ -50,16 +53,47 @@ def foo() -> bool:
# check that this typechecks both directions
a: lib1.IERC20 = IERC20(msg.sender)
b: lib2.IERC20 = IERC20(msg.sender)
c: IERC20 = lib1.IERC20(msg.sender) # allowed in call position
# return the equality so we can sanity check it
return a == b
return a == b and b == c
"""
input_bundle = make_input_bundle({"lib1.vy": lib1, "lib2.vy": lib2})
c = get_contract(main, input_bundle=input_bundle)

assert c.foo() is True


@pytest.mark.parametrize("interface_syntax", ["__at__", "__interface__"])
def test_intrinsic_interface(get_contract, make_input_bundle, interface_syntax):
lib = """
@external
@view
def foo() -> uint256:
# detect self call
if msg.sender == self:
return 4
else:
return 5
"""

main = f"""
import lib
exports: lib.__interface__
@external
@view
def bar() -> uint256:
return staticcall lib.{interface_syntax}(self).foo()
"""
input_bundle = make_input_bundle({"lib.vy": lib})
c = get_contract(main, input_bundle=input_bundle)

assert c.foo() == 5
assert c.bar() == 4


def test_import_interface_flags(make_input_bundle, get_contract):
ifaces = """
flag Foo:
Expand Down
89 changes: 89 additions & 0 deletions tests/functional/codegen/test_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -774,3 +774,92 @@ def foo(s: MyStruct) -> MyStruct:
assert "b: uint256" in out
assert "struct Voter:" in out
assert "voted: bool" in out


def test_intrinsic_interface_instantiation(make_input_bundle, get_contract):
lib1 = """
@external
@view
def foo():
pass
"""
main = """
import lib1
i: lib1.__interface__
@external
def bar() -> lib1.__interface__:
self.i = lib1.__at__(self)
return self.i
"""
input_bundle = make_input_bundle({"lib1.vy": lib1})
c = get_contract(main, input_bundle=input_bundle)

assert c.bar() == c.address


def test_intrinsic_interface_converts(make_input_bundle, get_contract):
lib1 = """
@external
@view
def foo():
pass
"""
main = """
import lib1
@external
def bar() -> lib1.__interface__:
return lib1.__at__(self)
"""
input_bundle = make_input_bundle({"lib1.vy": lib1})
c = get_contract(main, input_bundle=input_bundle)

assert c.bar() == c.address


def test_intrinsic_interface_kws(env, make_input_bundle, get_contract):
value = 10**5
lib1 = f"""
@external
@payable
def foo(a: address):
send(a, {value})
"""
main = f"""
import lib1
exports: lib1.__interface__
@external
def bar(a: address):
extcall lib1.__at__(self).foo(a, value={value})
"""
input_bundle = make_input_bundle({"lib1.vy": lib1})
c = get_contract(main, input_bundle=input_bundle)
env.set_balance(c.address, value)
original_balance = env.get_balance(env.deployer)
c.bar(env.deployer)
assert env.get_balance(env.deployer) == original_balance + value


def test_intrinsic_interface_defaults(env, make_input_bundle, get_contract):
lib1 = """
@external
@payable
def foo(i: uint256=1) -> uint256:
return i
"""
main = """
import lib1
exports: lib1.__interface__
@external
def bar() -> uint256:
return extcall lib1.__at__(self).foo()
"""
input_bundle = make_input_bundle({"lib1.vy": lib1})
c = get_contract(main, input_bundle=input_bundle)
assert c.bar() == 1
14 changes: 14 additions & 0 deletions tests/functional/codegen/types/numbers/test_exponents.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,17 @@ def foo(b: int128) -> int128:
c.foo(max_power)
with tx_failed():
c.foo(max_power + 1)


valid_list = [
"""
@external
def foo() -> uint256:
return (10**18)**2
"""
]


@pytest.mark.parametrize("good_code", valid_list)
def test_exponent_success(good_code):
assert compile_code(good_code) is not None
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ def foo():
def foo():
a: bytes32 = keccak256("ѓtest")
""",
# test constant folding inside of `convert()`
"""
BAR: constant(uint16) = 256
@external
def foo():
a: uint8 = convert(BAR, uint8)
""",
]


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ def foo():
"""
a: constant(address) = 0x3cd751e6b0078be393132286c442345e5dc49699
""",
# test constant folding inside `convert()`
"""
BAR: constant(Bytes[5]) = b"vyper"
@external
def foo():
a: Bytes[4] = convert(BAR, Bytes[4])
""",
]


Expand Down
34 changes: 33 additions & 1 deletion tests/functional/syntax/modules/test_deploy_visibility.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest

from vyper.compiler import compile_code
from vyper.exceptions import CallViolation
from vyper.exceptions import CallViolation, UnknownAttribute


def test_call_deploy_from_external(make_input_bundle):
Expand All @@ -25,3 +25,35 @@ def foo():
compile_code(main, input_bundle=input_bundle)

assert e.value.message == "Cannot call an @deploy function from an @external function!"


@pytest.mark.parametrize("interface_syntax", ["__interface__", "__at__"])
def test_module_interface_init(make_input_bundle, tmp_path, interface_syntax):
lib1 = """
#lib1.vy
k: uint256
@external
def bar():
pass
@deploy
def __init__():
self.k = 10
"""
input_bundle = make_input_bundle({"lib1.vy": lib1})

code = f"""
import lib1
@deploy
def __init__():
lib1.{interface_syntax}(self).__init__()
"""

with pytest.raises(UnknownAttribute) as e:
compile_code(code, input_bundle=input_bundle)

# as_posix() for windows tests
lib1_path = (tmp_path / "lib1.vy").as_posix()
assert e.value.message == f"interface {lib1_path} has no member '__init__'."
Loading

0 comments on commit e618cda

Please sign in to comment.