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

Improve linting output and display of warnings #340

Merged
merged 6 commits into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ script:
- >
coverage run -a --source=opshin -m opshin build spending examples/smart_contracts/wrapped_token.py '{"bytes": "ae810731b5d21c0d182d89c60a1eff7095dffd1c0dce8707a8611099"}' '{"bytes": "4d494c4b"}' '{"int": 1000000}' --force-three-params
- >
test ! -n "$(coverage run -a --source=opshin -m opshin lint any examples/smart_contracts/wrapped_token.py)"
test ! -n "$(coverage run -a --source=opshin -m opshin lint any examples/smart_contracts/always_true.py)"
- >
test -n "$(coverage run -a --source=opshin -m opshin lint any examples/smart_contracts/wrapped_token.py)"
- >
test -n "$(coverage run -a --source=opshin -m opshin lint any examples/broken.py)"
- >
Expand Down
31 changes: 30 additions & 1 deletion opshin/__main__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import inspect

import argparse
import logging
import tempfile

import cbor2
Expand Down Expand Up @@ -28,7 +29,7 @@
Purpose,
PlutusContract,
)
from .util import CompilerError, data_from_json
from .util import CompilerError, data_from_json, OPSHIN_LOG_HANDLER
from .prelude import ScriptContext


Expand Down Expand Up @@ -227,6 +228,8 @@ def check_params(


def perform_command(args):
if args.verbose:
logging.basicConfig(level=logging.DEBUG)
command = Command(args.command)
purpose = Purpose(args.purpose)
input_file = args.input_file if args.input_file != "-" else sys.stdin
Expand Down Expand Up @@ -466,6 +469,12 @@ def parse_args():
action="version",
version=f"opshin {__version__} {__copyright__}",
)
a.add_argument(
"-v",
"--verbose",
action="store_true",
help="Enable verbose logging.",
)
a.add_argument(
"--recursion-limit",
default=sys.getrecursionlimit(),
Expand All @@ -479,8 +488,28 @@ def main():
args = parse_args()
sys.setrecursionlimit(args.recursion_limit)
if Command(args.command) != Command.lint:
OPSHIN_LOG_HANDLER.setFormatter(
logging.Formatter(
f"%(levelname)s for {args.input_file}:%(lineno)d %(message)s"
)
)
perform_command(args)
else:
OPSHIN_LOG_HANDLER.stream = sys.stdout
if args.output_format_json:
OPSHIN_LOG_HANDLER.setFormatter(
logging.Formatter(
'{"line":%(lineno)d,"column":%(col_offset)d,"error_class":"%(levelname)s","message":"%(message)s"}'
)
)
else:
OPSHIN_LOG_HANDLER.setFormatter(
logging.Formatter(
args.input_file
+ ":%(lineno)d:%(col_offset)d:%(levelname)s: %(message)s"
)
)

try:
perform_command(args)
except Exception as e:
Expand Down
8 changes: 3 additions & 5 deletions opshin/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@
RawPlutoExpr,
)

_LOGGER = logging.getLogger(__name__)


BoolOpMap = {
And: plt.And,
Expand Down Expand Up @@ -230,7 +228,7 @@ def visit_Module(self, node: TypedModule) -> plt.AST:
else:
possible_types = [second_last_arg.typ]
if any(isinstance(t, UnitType) for t in possible_types):
_LOGGER.warning(
OPSHIN_LOGGER.warning(
"The redeemer is annotated to be 'None'. This value is usually encoded in PlutusData with constructor id 0 and no fields. If you want the script to double function as minting and spending script, annotate the second argument with 'NoRedeemer'."
)
enable_double_func_mint_spend = not any(
Expand All @@ -239,7 +237,7 @@ def visit_Module(self, node: TypedModule) -> plt.AST:
for t in possible_types
)
if not enable_double_func_mint_spend:
_LOGGER.warning(
OPSHIN_LOGGER.warning(
"The second argument to the validator function potentially has constructor id 0. The validator will not be able to double function as minting script and spending script."
)

Expand Down Expand Up @@ -331,7 +329,7 @@ def visit_Constant(self, node: TypedConstant) -> plt.AST:
except ValueError:
pass
else:
_LOGGER.warning(
OPSHIN_LOGGER.warning(
f"The string {node.value} looks like it is supposed to be a hex-encoded bytestring but is actually utf8-encoded. Try using `bytes.fromhex('{node.value.decode()}')` instead."
)
plt_val = plt.UPLCConstant(rec_constant_map(node.value))
Expand Down
10 changes: 4 additions & 6 deletions opshin/optimize/optimize_const_folding.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,13 @@
except NameError:
from astunparse import unparse

from ..util import CompilingNodeTransformer, CompilingNodeVisitor
from ..util import CompilingNodeTransformer, CompilingNodeVisitor, OPSHIN_LOGGER
from ..type_inference import INITIAL_SCOPE

"""
Pre-evaluates constant statements
"""

_LOGGER = logging.getLogger(__name__)

ACCEPTED_ATOMIC_TYPES = [
int,
str,
Expand Down Expand Up @@ -227,7 +225,7 @@ def update_constants(self, node):
try:
exec(unparse(node), g, l)
except Exception as e:
_LOGGER.debug(e)
OPSHIN_LOGGER.debug(e)
else:
# the class is defined and added to the globals
self.scopes_constants[-1].update(l)
Expand Down Expand Up @@ -259,7 +257,7 @@ def visit_FunctionDef(self, node: FunctionDef) -> FunctionDef:
# we need to pass the global dict as local dict here to make closures possible (rec functions)
exec(unparse(node), g, g)
except Exception as e:
_LOGGER.debug(e)
OPSHIN_LOGGER.debug(e)
else:
# the class is defined and added to the globals
self.scopes_constants[-1][node.name] = g[node.name]
Expand Down Expand Up @@ -331,7 +329,7 @@ def generic_visit(self, node: AST):
l = self._constant_vars()
node_eval = eval(node_source, g, l)
except Exception as e:
_LOGGER.debug(e)
OPSHIN_LOGGER.debug(e)
return node

if any(
Expand Down
2 changes: 0 additions & 2 deletions opshin/type_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@

# from frozendict import frozendict

_LOGGER = logging.getLogger(__name__)


INITIAL_SCOPE = {
# class annotations
Expand Down
2 changes: 0 additions & 2 deletions opshin/typed_ast.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from .types import *

_LOGGER = logging.getLogger(__name__)


class TypedAST(AST):
typ: Type
Expand Down
4 changes: 1 addition & 3 deletions opshin/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@

from .util import *

_LOGGER = logging.getLogger(__name__)


class TypeInferenceError(AssertionError):
pass
Expand Down Expand Up @@ -244,7 +242,7 @@ def cmp(self, op: cmpop, o: "Type") -> plt.AST:
return super().cmp(op, o)

def stringify(self, recursive: bool = False) -> plt.AST:
_LOGGER.warning(
OPSHIN_LOGGER.warning(
"Serializing AnyType will result in RawPlutusData (CBOR representation) to be printed without additional type information. Annotate types where possible to avoid this warning."
)
return OLambda(
Expand Down
38 changes: 38 additions & 0 deletions opshin/util.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from _ast import Name, Store, ClassDef, FunctionDef, Load
from collections import defaultdict
from copy import copy, deepcopy
import logging

import typing

Expand All @@ -15,6 +16,40 @@
import pluthon as plt
from hashlib import sha256

OPSHIN_LOGGER = logging.getLogger("opshin")
OPSHIN_LOG_HANDLER = logging.StreamHandler()
OPSHIN_LOGGER.addHandler(OPSHIN_LOG_HANDLER)


class FileContextFilter(logging.Filter):
"""
This is a filter which injects contextual information into the log.

The information is about the currently inspected AST node.
The information needs to be updated inside the NodeTransformer and NodeVisitor classes.
"""

file_name = "unknown"
node: ast.AST = None

def filter(self, record):

if self.node is None:
record.lineno = 1
record.col_offset = 0
record.end_lineno = 1
record.end_col_offset = 0
else:
record.lineno = self.node.lineno
record.col_offset = self.node.col_offset
record.end_lineno = self.node.end_lineno
record.end_col_offset = self.node.end_col_offset
return True


OPSHIN_LOG_CONTEXT_FILTER = FileContextFilter()
OPSHIN_LOG_HANDLER.addFilter(OPSHIN_LOG_CONTEXT_FILTER)


def distinct(xs: list):
"""Returns true iff the list consists of distinct elements"""
Expand All @@ -24,6 +59,7 @@ def distinct(xs: list):
class TypedNodeTransformer(ast.NodeTransformer):
def visit(self, node):
"""Visit a node."""
OPSHIN_LOG_CONTEXT_FILTER.node = node
node_class_name = node.__class__.__name__
if node_class_name.startswith("Typed"):
node_class_name = node_class_name[len("Typed") :]
Expand All @@ -35,6 +71,7 @@ def visit(self, node):
class TypedNodeVisitor(ast.NodeVisitor):
def visit(self, node):
"""Visit a node."""
OPSHIN_LOG_CONTEXT_FILTER.node = node
node_class_name = node.__class__.__name__
if node_class_name.startswith("Typed"):
node_class_name = node_class_name[len("Typed") :]
Expand All @@ -54,6 +91,7 @@ class CompilingNodeTransformer(TypedNodeTransformer):
step = "Node transformation"

def visit(self, node):
OPSHIN_LOG_CONTEXT_FILTER.node = node
try:
return super().visit(node)
except Exception as e:
Expand Down