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

Merge absolute imports into feature/abi #288

Merged
merged 14 commits into from
Apr 21, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
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
7 changes: 7 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,11 @@ per-file-ignores =
pyteal/__init__.py: F401, F403
pyteal/ir/ops.py: E221
tests/module_test.py: F401, F403
tests/*.py: I252
# Temporarily ignore until merge completes
pyteal/ast/abi/*.py: I252
# End temporarty ignore

# from flake8-tidy-imports
ban-relative-imports = true

62 changes: 54 additions & 8 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,79 @@

If you are interested in contributing to the project, we welcome and thank you. We want to make the best decentralized and effective blockchain platform available and we appreciate your willingness to help us.



# Filing Issues
## Filing Issues

Did you discover a bug? Do you have a feature request? Filing issues is an easy way anyone can contribute and helps us improve PyTeal. We use GitHub Issues to track all known bugs and feature requests.

Before logging an issue be sure to check current issues, check the [Developer Frequently Asked Questions](https://developer.algorand.org/docs/developer-faq) and [GitHub issues][issues_url] to see if your issue is described there.
Before logging an issue be sure to check current issues, check the [open GitHub issues][issues_url] to see if your issue is described there.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 for removing dead link

If you’d like to contribute to any of the repositories, please file a [GitHub issue][issues_url] using the issues menu item. Make sure to specify whether you are describing a bug or a new enhancement using the **Bug report** or **Feature request** button.

See the GitHub help guide for more information on [filing an issue](https://help.github.com/en/articles/creating-an-issue).

# Contribution Model
## Contribution Model

For each of our repositories we use the same model for contributing code. Developers wanting to contribute must create pull requests. This process is described in the GitHub [Creating a pull request from a fork](https://help.github.com/en/articles/creating-a-pull-request-from-a-fork) documentation. Each pull request should be initiated against the master branch in the Algorand repository. After a pull request is submitted the core development team will review the submission and communicate with the developer using the comments sections of the PR. After the submission is reviewed and approved, it will be merged into the master branch of the source. These changes will be merged to our release branch on the next viable release date.
For each of our repositories we use the same model for contributing code. Developers wanting to contribute must create pull requests. This process is described in the GitHub [Creating a pull request from a fork](https://help.github.com/en/articles/creating-a-pull-request-from-a-fork) documentation. Each pull request should be initiated against the master branch in the Algorand repository. After a pull request is submitted the core development team will review the submission and communicate with the developer using the comments sections of the PR. After the submission is reviewed and approved, it will be merged into the master branch of the source. These changes will be merged to our release branch on the next viable release date.

# Code Guidelines
## Code Guidelines

We make a best-effort attempt to adhere to [PEP 8 - Style Guide for Python Code](https://www.python.org/dev/peps/pep-0008/). Keep the following context in mind:
* Our default stance is to run linter checks during the build process.
* Notable exception: [naming conventions](https://peps.python.org/pep-0008/#naming-conventions).

## Naming Convention Guidelines
### Naming Convention Guidelines
Since PyTeal aims for backwards compatibility, it's _not_ straightforward to change naming conventions in public APIs. Consequently, the repo contains some deviations from PEP 8 naming conventions.

In order to retain a consistent style, we prefer to continue deviating from PEP 8 naming conventions in the following cases. We try to balance minimizing exceptions against providing a consistent style for existing software.
* Enums - Define with lowercase camelcase. Example: https://github.com/algorand/pyteal/blob/7c953f600113abcb9a31df68165b61a2c897f591/pyteal/ast/txn.py#L37
* Factory methods - Define following [class name](https://peps.python.org/pep-0008/#class-names) conventions. Example: https://github.com/algorand/pyteal/blob/7c953f600113abcb9a31df68165b61a2c897f591/pyteal/ast/ternaryexpr.py#L63

Since it's challenging to enforce these exceptions with a linter, we rely on PR creators and reviewers to make a best-effort attempt to enforce agreed upon naming conventions.

### Module Guidelines

Every directory containing source code should be a Python module, meaning it should have an `__init__.py` file. This `__init__.py` file is responsible for exporting all public objects (i.e. classes, functions, and constants) for use outside of that module.

Modules may be created inside of other modules, in which case the deeper module is called a submodule or child module, and the module that contains it is called the parent module. For example, `pyteal` is the parent module to `pyteal.ast`.

A sibling module is defined as a different child module of the parent module. For example, `pyteal.ast` and `pyteal.ir` are sibling modules.

### Import Guidelines

#### In Runtime Code

When a runtime file in this codebase needs to import another module/file in this codebase, you should import the absolute path of the module/file, not the relative one, using the `from X import Y` method.
michaeldiamant marked this conversation as resolved.
Show resolved Hide resolved

With regard to modules, there are two ways to import an object from this codebase:

* When importing an object from the same module or a parent module, you should import the full path of the source file that defines the object. For example:
* (Import from same module): If `pyteal/ast/seq.py` needs to import `Expr` defined in `pyteal/ast/expr.py`, it should use `from pyteal.ast.expr import Expr`. **DO NOT** use `from pyteal.ast import Expr` or `from pyteal import Expr`, as this will cause an unnecessary circular dependency.
* (Import from parent module): If `pyteal/ast/seq.py` needs to import `TealType` defined in `pyteal/types.py`, it should use `from pyteal.types import TealType`. **DO NOT** use `from pyteal import TealType`, as this will cause an unnecessary circular dependency.

* When importing an object from a child module or sibling module, you should import the entire module folder and access the desired object from there. Do not directly import the file that defines the object. For example:
* (Import from child module): If `pyteal/compiler/compiler.py` needs to import `OptimizeOptions` from `pyteal/compiler/optimizer/optimizer.py`, it should use `from pyteal.compiler.optimizer import OptimizeOptions`. **DO NOT** use `from pyteal.compiler.optimizer.optimizer import OptimizeOptions`, as this will bypass the file `pyteal/compiler/optimizer/__init__.py`, which carefully defines the exports for the `pyteal.compiler.optimizer` module.
* (Import from sibling module): If `pyteal/compiler/compiler.py` needs to import `Expr` defined in `pyteal/ast/expr.py`, it should use `from pyteal.ast import Expr`. **DO NOT** use `from pyteal import Expr`, as this will cause an unnecessary circular dependency, nor `from pyteal.ast.expr import Expr`, as this will bypass the file `pyteal/ast/__init__.py`, which carefully defines the
exports for the `pyteal.ast` module.

When this approach is followed properly, circular dependencies can happen in two ways:
1. **Intra-module circular dependencies**, e.g. `m1/a.py` imports `m1/b.py` which imports `m1/a.py`.
When this happens, normally one of the files only needs to import the other to implement something,
so the import statements can be moved from the top of the file to the body of the function that
needs them. This breaks the import cycle.

2. **Inter-module circular dependencies**, e.g. `m1/a.py` imports module `m1/m2/__init__.py` which
imports `m1/m2/x.py` which imports `m1/a.py`. To avoid this, make sure that any objects/files in
`m1` that need to be exposed to deeper modules do not rely on any objects from that deeper module.
If that isn’t possible, then perhaps `m1/a.py` belongs in the `m1/m2/` module.

#### In Test Code

Test code is typically any file that ends with `_test.py` or is in the top-level `tests` folder. In
order to have a strong guarantee that we are testing in a similar environment to consumers of this
library, test code is encouraged to import _only_ the top-level PyTeal module, either with
`from pyteal import X, Y, Z` or `import pyteal as pt`.

This way we can be sure all objects that should be publicly exported are in fact accessible from the top-level module.

The only exception to this should be if the tests are for a non-exported object, in which case it is
necessary to import the object according to the runtime rules in the previous section.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

# PyTeal: Algorand Smart Contracts in Python

[![Build Status](https://travis-ci.com/algorand/pyteal.svg?branch=master)](https://travis-ci.com/algorand/pyteal)
[![Build Status](https://github.com/algorand/pyteal/actions/workflows/build.yml/badge.svg)](https://github.com/algorand/pyteal/actions)
[![PyPI version](https://badge.fury.io/py/pyteal.svg)](https://badge.fury.io/py/pyteal)
[![Documentation Status](https://readthedocs.org/projects/pyteal/badge/?version=latest)](https://pyteal.readthedocs.io/en/latest/?badge=latest)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
Expand Down
21 changes: 13 additions & 8 deletions pyteal/__init__.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
from .ast import *
from .ast import __all__ as ast_all
from .ir import *
from .ir import __all__ as ir_all
from .compiler import (
from pyteal.ast import *
from pyteal.ast import __all__ as ast_all
from pyteal.ir import *
from pyteal.ir import __all__ as ir_all
from pyteal.compiler import (
MAX_TEAL_VERSION,
MIN_TEAL_VERSION,
DEFAULT_TEAL_VERSION,
CompileOptions,
compileTeal,
OptimizeOptions,
)
from .types import TealType
from .errors import TealInternalError, TealTypeError, TealInputError, TealCompileError
from .config import MAX_GROUP_SIZE, NUM_SLOTS
from pyteal.types import TealType
from pyteal.errors import (
TealInternalError,
TealTypeError,
TealInputError,
TealCompileError,
)
from pyteal.config import MAX_GROUP_SIZE, NUM_SLOTS

# begin __all__
__all__ = (
Expand Down
21 changes: 13 additions & 8 deletions pyteal/__init__.pyi
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
## File generated from scripts/generate_init.py.
## DO NOT EDIT DIRECTLY

from .ast import *
from .ast import __all__ as ast_all
from .ir import *
from .ir import __all__ as ir_all
from .compiler import (
from pyteal.ast import *
from pyteal.ast import __all__ as ast_all
from pyteal.ir import *
from pyteal.ir import __all__ as ir_all
from pyteal.compiler import (
MAX_TEAL_VERSION,
MIN_TEAL_VERSION,
DEFAULT_TEAL_VERSION,
CompileOptions,
compileTeal,
OptimizeOptions,
)
from .types import TealType
from .errors import TealInternalError, TealTypeError, TealInputError, TealCompileError
from .config import MAX_GROUP_SIZE, NUM_SLOTS
from pyteal.types import TealType
from pyteal.errors import (
TealInternalError,
TealTypeError,
TealInputError,
TealCompileError,
)
from pyteal.config import MAX_GROUP_SIZE, NUM_SLOTS

__all__ = [
"AccountParam",
Expand Down
92 changes: 50 additions & 42 deletions pyteal/ast/__init__.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,43 @@
# abstract types
from .expr import Expr
from pyteal.ast.expr import Expr

# basic types
from .leafexpr import LeafExpr
from .addr import Addr
from .bytes import Bytes
from .int import Int, EnumInt
from .methodsig import MethodSignature
from pyteal.ast.leafexpr import LeafExpr
from pyteal.ast.addr import Addr
from pyteal.ast.bytes import Bytes
from pyteal.ast.int import Int, EnumInt
from pyteal.ast.methodsig import MethodSignature

# properties
from .arg import Arg
from .txn import TxnType, TxnField, TxnExpr, TxnaExpr, TxnArray, TxnObject, Txn
from .gtxn import GtxnExpr, GtxnaExpr, TxnGroup, Gtxn
from .gaid import GeneratedID
from .gitxn import Gitxn, GitxnExpr, GitxnaExpr, InnerTxnGroup
from .gload import ImportScratchValue
from .global_ import Global, GlobalField
from .app import App, AppField, OnComplete, AppParam
from .asset import AssetHolding, AssetParam
from .acct import AccountParam
from pyteal.ast.arg import Arg
from pyteal.ast.txn import (
TxnType,
TxnField,
TxnExpr,
TxnaExpr,
TxnArray,
TxnObject,
Txn,
)
from pyteal.ast.gtxn import GtxnExpr, GtxnaExpr, TxnGroup, Gtxn
from pyteal.ast.gaid import GeneratedID
from pyteal.ast.gitxn import Gitxn, GitxnExpr, GitxnaExpr, InnerTxnGroup
from pyteal.ast.gload import ImportScratchValue
from pyteal.ast.global_ import Global, GlobalField
from pyteal.ast.app import App, AppField, OnComplete, AppParam
from pyteal.ast.asset import AssetHolding, AssetParam
from pyteal.ast.acct import AccountParam

# inner txns
from .itxn import InnerTxnBuilder, InnerTxn, InnerTxnAction
from pyteal.ast.itxn import InnerTxnBuilder, InnerTxn, InnerTxnAction

# meta
from .array import Array
from .tmpl import Tmpl
from .nonce import Nonce
from pyteal.ast.array import Array
from pyteal.ast.tmpl import Tmpl
from pyteal.ast.nonce import Nonce

# unary ops
from .unaryexpr import (
from pyteal.ast.unaryexpr import (
UnaryExpr,
Btoi,
Itob,
Expand All @@ -51,7 +59,7 @@
)

# binary ops
from .binaryexpr import (
from pyteal.ast.binaryexpr import (
BinaryExpr,
Minus,
Div,
Expand Down Expand Up @@ -90,47 +98,47 @@
)

# ternary ops
from .ternaryexpr import Divw, Ed25519Verify, SetBit, SetByte
from .substring import Substring, Extract, Suffix
from pyteal.ast.ternaryexpr import Divw, Ed25519Verify, SetBit, SetByte
from pyteal.ast.substring import Substring, Extract, Suffix

# more ops
from .naryexpr import NaryExpr, Add, Mul, And, Or, Concat
from .widemath import WideRatio
from pyteal.ast.naryexpr import NaryExpr, Add, Mul, And, Or, Concat
from pyteal.ast.widemath import WideRatio

# control flow
from .if_ import If
from .cond import Cond
from .seq import Seq
from .assert_ import Assert
from .err import Err
from .return_ import Return, Approve, Reject
from .subroutine import (
from pyteal.ast.if_ import If
from pyteal.ast.cond import Cond
from pyteal.ast.seq import Seq
from pyteal.ast.assert_ import Assert
from pyteal.ast.err import Err
from pyteal.ast.return_ import Return, Approve, Reject
from pyteal.ast.subroutine import (
Subroutine,
SubroutineDefinition,
SubroutineDeclaration,
SubroutineCall,
SubroutineFnWrapper,
)
from .while_ import While
from .for_ import For
from .break_ import Break
from .continue_ import Continue
from pyteal.ast.while_ import While
from pyteal.ast.for_ import For
from pyteal.ast.break_ import Break
from pyteal.ast.continue_ import Continue


# misc
from .scratch import (
from pyteal.ast.scratch import (
ScratchIndex,
ScratchLoad,
ScratchSlot,
ScratchStackStore,
ScratchStore,
)
from .scratchvar import DynamicScratchVar, ScratchVar
from .maybe import MaybeValue
from .multi import MultiValue
from pyteal.ast.scratchvar import DynamicScratchVar, ScratchVar
from pyteal.ast.maybe import MaybeValue
from pyteal.ast.multi import MultiValue

# abi
from . import abi
from pyteal.ast import abi

__all__ = [
"Expr",
Expand Down
31 changes: 18 additions & 13 deletions pyteal/ast/abi/address_test.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from ... import *
import pyteal as pt
from pyteal import abi

options = CompileOptions(version=5)
options = pt.CompileOptions(version=5)


def test_AddressTypeSpec_str():
Expand Down Expand Up @@ -33,10 +34,12 @@ def test_AddressTypeSpec_eq():
def test_Address_encode():
value = abi.Address()
expr = value.encode()
assert expr.type_of() == TealType.bytes
assert expr.type_of() == pt.TealType.bytes
assert expr.has_return() is False

expected = TealSimpleBlock([TealOp(expr, Op.load, value.stored_value.slot)])
expected = pt.TealSimpleBlock(
[pt.TealOp(expr, pt.Op.load, value.stored_value.slot)]
)
actual, _ = expr.__teal__(options)
assert actual == expected

Expand All @@ -46,31 +49,33 @@ def test_Address_decode():

value = abi.Address()
for value_to_set in [urandom(abi.ADDRESS_LENGTH) for x in range(10)]:
expr = value.decode(Bytes(value_to_set))
expr = value.decode(pt.Bytes(value_to_set))

assert expr.type_of() == TealType.none
assert expr.type_of() == pt.TealType.none
assert expr.has_return() is False

expected = TealSimpleBlock(
expected = pt.TealSimpleBlock(
[
TealOp(None, Op.byte, f"0x{value_to_set.hex()}"),
TealOp(None, Op.store, value.stored_value.slot),
pt.TealOp(None, pt.Op.byte, f"0x{value_to_set.hex()}"),
pt.TealOp(None, pt.Op.store, value.stored_value.slot),
]
)
actual, _ = expr.__teal__(options)
actual.addIncoming()
actual = TealBlock.NormalizeBlocks(actual)
actual = pt.TealBlock.NormalizeBlocks(actual)

with TealComponent.Context.ignoreExprEquality():
with pt.TealComponent.Context.ignoreExprEquality():
assert actual == expected


def test_Address_get():
value = abi.Address()
expr = value.get()
assert expr.type_of() == TealType.bytes
assert expr.type_of() == pt.TealType.bytes
assert expr.has_return() is False

expected = TealSimpleBlock([TealOp(expr, Op.load, value.stored_value.slot)])
expected = pt.TealSimpleBlock(
[pt.TealOp(expr, pt.Op.load, value.stored_value.slot)]
)
actual, _ = expr.__teal__(options)
assert actual == expected
Loading