Skip to content

Commit

Permalink
ci: Run tests on PRs and lint (#9)
Browse files Browse the repository at this point in the history
* ci: run tests on all PRs

* ci: Add linting step to CI

* lint: fix linitng errors

* fix: hypothesis test stability
  • Loading branch information
StylishTriangles authored Aug 5, 2022
1 parent 5b3ab8b commit 80d172a
Show file tree
Hide file tree
Showing 9 changed files with 61 additions and 33 deletions.
32 changes: 20 additions & 12 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,12 @@ on:
push:
branches: [ $default-branch ]
pull_request:
branches: [ $default-branch ]
# Allows to run this workflow manually from the Actions tab
workflow_dispatch:

permissions:
contents: read

jobs:
build:

test:
name: pytest
runs-on: ubuntu-latest

steps:
Expand All @@ -26,12 +22,24 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install -r requirements_dev.txt
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings.
flake8 pytealext --count --exit-zero --max-complexity=10 --max-line-length=120 --statistics --ignore=E203,W503
- name: Test with pytest
run: |
pytest
lint:
name: pylint
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements_dev.txt
- name: Lint with pylint
run: |
pylint pytealext
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# can either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once).
disable=missing-module-docstring,invalid-name,wildcard-import
disable=missing-module-docstring,invalid-name,too-many-arguments

[FORMAT]
max-line-length=120
Expand Down
13 changes: 9 additions & 4 deletions pytealext/evaluator/evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@


def int_to_trimmed_bytes(value: int) -> bytes:
byte_length = (max(value.bit_length(), 1) + 7) // 8
"""Convert an integer into a big-endian byte array with no leading zero bytes
Special case: zero is converted to a single zero byte
"""
byte_length = (value.bit_length() + 7) // 8
byte_length = max(1, byte_length) # handle zero special case
return value.to_bytes(byte_length, "big")


Expand Down Expand Up @@ -39,7 +44,7 @@ def __init__(self, line_number):
MaxGlobalStateSize = 64


class EvalContext:
class EvalContext: # pylint: disable=too-few-public-methods
"""
Class containing the execution environment for an application call
"""
Expand Down Expand Up @@ -71,7 +76,7 @@ def split128(val: int):
return val // INTEGER_SIZE, val % INTEGER_SIZE


def eval_teal( # noqa: C901
def eval_teal( # pylint: disable=too-many-locals,too-many-branches,too-many-statements
lines: list[str],
return_stack=True,
context: EvalContext or None = None,
Expand Down Expand Up @@ -511,7 +516,7 @@ def eval_teal( # noqa: C901
app = stack.pop()
account = stack.pop()
if app != 0 or account != 0:
raise Exception("app_local_get_ex is only supported with 0" " as the account and application parameter")
raise Exception("app_local_get_ex is only supported with 0 as the account and application parameter")
if not isinstance(key, bytes):
raise Panic("app_local_get_ex key must be a bytes value", current_line)
val = context.local_state.get(key, 0)
Expand Down
2 changes: 1 addition & 1 deletion pytealext/muldiv64.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def __teal__(self, options: CompileOptions) -> tuple[TealBlock, TealSimpleBlock]
return assemble_steps(self._get_steps(), options)

def __str__(self):
return "(MulDiv64 {} {} {})".format(self.m1, self.m2, self.d)
return f"(MulDiv64 {self.m1} {self.m2} {self.d})"

def type_of(self):
return TealType.uint64
Expand Down
11 changes: 5 additions & 6 deletions pytealext/serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
CompileOptions,
Concat,
Expr,
Extract,
ExtractUint16,
ExtractUint32,
ExtractUint64,
Expand Down Expand Up @@ -75,17 +74,17 @@ def SerializeIntegers(*ints: Expr, width: int = 64) -> Expr:
"""
if width == 64:
return Concat(*[Itob(i) for i in ints])
elif width == 32:
if width == 32:
# extract the lowest 32 bits (4 bytes) from each integer
return Concat(*[ExtractSL(4, 4, Itob(i)) for i in ints])
elif width == 16:
if width == 16:
# extract the lowest 16 bits (2 bytes) from each integer
return Concat(*[ExtractSL(6, 2, Itob(i)) for i in ints])
else:
raise ValueError(f"Invalid width: {width}")

raise ValueError(f"Invalid width: {width}")


class DeserializeIntegers:
class DeserializeIntegers: # pylint: disable=too-few-public-methods
"""
Deserialize bytes into a sequence of integers.
The deserialized integers are put in the provided slots.
Expand Down
2 changes: 1 addition & 1 deletion pytealext/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def key_at_index(self, index: Union[int, Expr]) -> Expr:
"""
Get the actual key (bytes) that will be used to access the state information
"""
if isinstance(index, int):
if isinstance(index, int): # pylint: disable=no-else-return
if isinstance(self._prefix, str):
# index: int, prefix: str
return Bytes(self._prefix.encode("utf-8") + index.to_bytes(8, "big"))
Expand Down
2 changes: 1 addition & 1 deletion requirements_dev.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
-r requirements.txt
hypothesis
pytest
flake8
pylint
26 changes: 22 additions & 4 deletions tests/evaluator_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,11 @@ def test_equals():

def test_math_ops_fail_with_byte_type():
ops = ["addw", "mulw", "/", "%", "+", "-", "*", "&&", "||", ">", "<"]
bad_programs = [["byte 0x01", "byte 0x02"], ["byte 0x03", "int 4"], ["int 5", "byte 0x06"]]
bad_programs = [
["byte 0x01", "byte 0x02"],
["byte 0x03", "int 4"],
["int 5", "byte 0x06"],
]
for op in ops:
for bad_program in bad_programs:
bp = bad_program + [op]
Expand Down Expand Up @@ -155,7 +159,11 @@ def test_local_state():

program = Seq(
*[
App.localPut(Int(0), Bytes(key), (Bytes(value) if isinstance(value, bytes) else Int(value)))
App.localPut(
Int(0),
Bytes(key),
(Bytes(value) if isinstance(value, bytes) else Int(value)),
)
for key, value in mapping.items()
],
And(
Expand All @@ -181,11 +189,10 @@ def test_local_state():

@given(
i=st.integers(min_value=0, max_value=2**10),
j=st.integers(min_value=0, max_value=64),
j=st.integers(min_value=1, max_value=64),
)
def test_exp(i: int, j: int):
assume(i**j <= 2**64 - 1)
assume(not (i == 0 and j == 0))
i_int = Int(i)
j_int = Int(j)

Expand All @@ -200,6 +207,17 @@ def test_exp(i: int, j: int):
assert stack[0] == 1


@pytest.mark.parametrize("base", [1, 2, 1000000007, 2**64 - 1])
def test_exp_zero_exponent(base: int):
expr = Eq(Exp(Int(base), Int(0)), Int(1))
expr_asm = compileTeal(expr, Mode.Application, version=VERSION)

stack, _ = eval_teal(expr_asm.splitlines())

assert len(stack) == 1
assert stack[0] == 1


def test_exp_fails_for_zeroes():
i_int = Int(0)
j_int = Int(0)
Expand Down
4 changes: 1 addition & 3 deletions tests/inner_txn_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
TxnType,
)

from pytealext.inner_transactions import * # pylint: disable=unused-wildcard-import
from pytealext.inner_transactions import * # pylint: disable=unused-wildcard-import,wildcard-import

# According to PEP 468 order in kwargs is preserved, so we can safely check equality of Seqs

Expand Down Expand Up @@ -215,8 +215,6 @@ def test_example_gtxn_compiles():

from pyteal import AppParam, Approve, MethodSignature, compileTeal

from pytealext import InnerAssetTransferTxn, InnerNoOpTxn, MakeInnerGroupTxn

app_to_call = Int(12345)
amount_to_deposit = Int(100)
asset_id = Int(222)
Expand Down

0 comments on commit 80d172a

Please sign in to comment.