diff --git a/pytezos/tools/templates/jsondiff.html b/assets/jsondiff.html
similarity index 100%
rename from pytezos/tools/templates/jsondiff.html
rename to assets/jsondiff.html
diff --git a/assets/kernel.js b/assets/kernel.js
new file mode 100644
index 000000000..3814a5a54
--- /dev/null
+++ b/assets/kernel.js
@@ -0,0 +1,78 @@
+define([
+ 'codemirror/lib/codemirror',
+ 'codemirror/addon/mode/simple'
+], function(CodeMirror) {
+ var onload = function() {
+ CodeMirror.defineSimpleMode("michelson", {
+ start: [
+ // delimiters
+ { regex: /[;\{\(]/, token: "variable", next: "start" },
+ // string
+ { regex: /"(?:[^\\]|\\.)*?(?:"|$)/, token: "string" },
+ // bytes
+ { regex: /(?<=\s|^)(?:0x[0-9a-f]+)(?=\s|;|\}|$)/i, token: "string" },
+ // int
+ { regex: /(?<=\s|^)(?:[+-]?[0-9]+\.?[0-9]*)(?=\s|;|\}|$)/, token: "string" },
+ // comment
+ { regex: /#.*/, token: "comment" },
+ { regex: /\/\*/, token: "comment", next: "comment" },
+ // block
+ { regex: /(?<=\s|^)(?:parameter|storage|code)(?=\s|$)/, token: "keyword" },
+ // data
+ { regex: /(?<=\s|^)(?:Unit|True|False|Pair|Left|Right|Some|None|Elt)(?=\s|;|\)|$)/, token: "keyword" },
+ // instruction
+ { regex: /(?<=\s|^)(?:CAST|RENAME|DROP|DUP|SWAP|PUSH|SOME|NONE|UNIT|IF_NONE|PAIR|CAR|CDR|LEFT|RIGHT|IF_LEFT|IF_RIGHT|NIL|CONS|IF_CONS|SIZE|EMPTY_SET|EMPTY_MAP|MAP|ITER|MEM|GET|UPDATE|IF|LOOP|LOOP_LEFT|LAMBDA|EXEC|DIP|FAILWITH|CONCAT|SLICE|PACK|UNPACK|ADD|SUB|MUL|EDIV|ABS|NEG|LSL|LSR|OR|AND|XOR|NOT|COMPARE|EQ|NEQ|LT|GT|LE|GE|CHECK_SIGNATURE|BLAKE2B|SHA256|SHA512|HASH_KEY|DIG|DUG|EMPTY_BIG_MAP|APPLY)(?=\s|;|\}|$)/, token: "meta"},
+ { regex: /(?<=\s|^)(?:SELF|CONTRACT|TRANSFER_TOKENS|SET_DELEGATE|CREATE_CONTRACT|IMPLICIT_ACCOUNT|NOW|AMOUNT|BALANCE|STEPS_TO_QUOTA|SOURCE|SENDER|ADDRESS|CHAIN_ID)(?=\s|;|\}|$)/, token: "operator"},
+ // type
+ { regex: /(?<=\s|^)(?:option|list|set|contract|pair|or|lambda|map|big_map)(?=\s|\)|$)/, token: "builtin" },
+ { regex: /(?<=\s|^)(?:key|unit|signature|operation|address|int|nat|string|bytes|mutez|bool|key_hash|timestamp|chain_id)(?=\s|\)|\}|;|$)/, token: "builtin" },
+ // macros
+ { regex: /(?<=\s|^)(?:IF_SOME|FAIL|ASSERT|ASSERT_NONE|ASSERT_SOME|ASSERT_LEFT|ASSERT_RIGHT|UNPAIR|(?:SET|MAP)_C[AD]+R)(?=\s|;|\}|$)/, token: "string-2" },
+ { regex: /(?<=\s|^)(?:DII+P|C[AD]{2,}R|DUU+P|P[PAI]{3,}R|UNP[PAI]{3,}R)(?=\s|;|\}|$)/, token: "string-2" },
+ { regex: /(?<=\s|^)(?:(?:CMP|IF|IFCMP|ASSERT_|ASSERT_CMP)(?:EQ|NEQ|LT|GT|LE|GE))(?=\s|;|\}|\{|$)/, token: "string-2" },
+ // annotations
+ { regex: /(?<=\s|^)(?:%[A-z_0-9%@]*)/, token: "atom" },
+ { regex: /(?<=\s|^)(?:@[A-z_0-9%]+)(?=\s|$)/, token: "atom" },
+ { regex: /(?<=\s|^)(?::[A-z_0-9]+)(?=\s|$)/, token: "atom" },
+ // helpers
+ { regex: /(?<=\s|;|\{|^)(?:PUT|TOP)/, token: "def" },
+ // fallback
+ { regex: /[^\s]+/, token: "variable"}
+ ],
+ comment: [
+ { regex: /.*?\*\//, token: "comment", next: "start" },
+ { regex: /.*/, token: "comment" }
+ ],
+ meta: {
+ dontIndentStates: ["comment"],
+ lineComment: "#",
+ blockCommentStart: "/*",
+ blockCommentEnd: "*/"
+ }
+ });
+ CodeMirror.defineMIME("text/x-michelson", "michelson");
+ CodeMirror.modeInfo.push({
+ ext: ["tz"],
+ mime: "text/x-michelson",
+ mode: "michelson",
+ name: "Michelson"
+ });
+
+ // Force mode on refresh
+ // Big thanks to https://github.com/kelvich for this solution
+ IPython.CodeCell.options_default["cm_config"]["mode"] = "michelson";
+ [...document.querySelectorAll('.code_cell .CodeMirror')].forEach(c => {
+ c.CodeMirror.setOption('mode', 'michelson');
+ });
+ Jupyter.notebook.get_cells().forEach(function(c) {
+ if (c.cell_type == "code") {
+ c._options.cm_config['mode'] = 'michelson';
+ }
+ else if (c.cell_type == "markdown") {
+ c.unrender();
+ c.render();
+ }
+ });
+ }
+ return { onload: onload }
+});
\ No newline at end of file
diff --git a/pytezos/tools/templates/unidiff.html b/assets/unidiff.html
similarity index 100%
rename from pytezos/tools/templates/unidiff.html
rename to assets/unidiff.html
diff --git a/pytezos/cli.py b/pytezos/cli.py
index 3e2873f36..0e501492d 100644
--- a/pytezos/cli.py
+++ b/pytezos/cli.py
@@ -1,12 +1,27 @@
import fire
-from os.path import abspath
+import json
+import os
+import shutil
+from os.path import abspath, dirname, join
from glob import glob
from pprint import pprint
+from ipykernel.kernelapp import IPKernelApp
+from jupyter_client.kernelspec import KernelSpecManager
+from tempfile import TemporaryDirectory
from pytezos import pytezos, Contract, RpcError
from pytezos.operation.result import OperationResult
from pytezos.michelson.docstring import generate_docstring
from pytezos.tools.github import create_deployment, create_deployment_status
+from pytezos.repl.kernel import MichelsonKernel
+
+kernel_js_path = join(dirname(dirname(__file__)), 'assets', 'kernel.js')
+kernel_json = {
+ "argv": ['pytezos', 'kernel', 'run', "-file", "{connection_file}"],
+ "display_name": "Michelson",
+ "language": "michelson",
+ "codemirror_mode": "michelson"
+}
def make_bcd_link(network, address):
@@ -39,6 +54,29 @@ def get_contract(path):
class PyTezosCli:
+ def kernel(self, action, file=None):
+ """
+ Manage Jupyter kernel for the Michelson language
+ :param action: One of `install`, `remove`, `run`
+ :param file: connection settings (for running a kernel)
+ """
+ if action == 'install':
+ kernel_spec = KernelSpecManager()
+ with TemporaryDirectory() as td:
+ os.chmod(td, 0o755)
+ shutil.copy(kernel_js_path, join(td, 'kernel.js'))
+ with open(join(td, 'kernel.json'), 'w') as f:
+ json.dump(kernel_json, f, sort_keys=True)
+ kernel_spec.install_kernel_spec(td, 'michelson', user=os.environ['USER'])
+ elif action == 'remove':
+ kernel_spec = KernelSpecManager()
+ kernel_spec.remove_kernel_spec('michelson')
+ elif action == 'run':
+ argv = ['-f', file] if file else None
+ return IPKernelApp.launch_instance(kernel_class=MichelsonKernel, argv=argv)
+ else:
+ assert False, action
+
def storage(self, action, path=None):
"""
:param action: One of `schema`, `default`
diff --git a/pytezos/michelson/contract.py b/pytezos/michelson/contract.py
index 44412dd39..fe24b2ce3 100644
--- a/pytezos/michelson/contract.py
+++ b/pytezos/michelson/contract.py
@@ -60,10 +60,15 @@ def encode(self, data):
"""
if isinstance(data, dict) and len(data) == 1:
entrypoint = next(iter(data))
- value = encode_micheline(data[entrypoint], self.schema, root=self._get_entry_root(entrypoint))
+ if not any(map(lambda x: x.get('fieldname') == entrypoint, self.schema.metadata.values())):
+ entrypoint = 'default' # prevent auto-generated entrypoint names, like `rrrllll`
else:
entrypoint = 'default'
+
+ if entrypoint == 'default':
value = encode_micheline(data, self.schema)
+ else:
+ value = encode_micheline(data[entrypoint], self.schema, root=self._get_entry_root(entrypoint))
return dict(entrypoint=entrypoint, value=value)
diff --git a/pytezos/michelson/formatter.py b/pytezos/michelson/formatter.py
index 87918bd1b..acc3be18f 100644
--- a/pytezos/michelson/formatter.py
+++ b/pytezos/michelson/formatter.py
@@ -1,5 +1,6 @@
import json
from datetime import datetime
+from pprint import pformat
line_size = 100
@@ -113,4 +114,4 @@ def micheline_to_michelson(data, inline=False):
try:
return format_node(data, inline=inline, is_root=True)
except (KeyError, IndexError, TypeError):
- raise MichelsonFormatterError('Failed to format Micheline expression')
+ raise MichelsonFormatterError(f'Failed to format expression: {pformat(data, compact=True)}')
diff --git a/pytezos/michelson/grammar.py b/pytezos/michelson/grammar.py
index 3cea877ae..a6c2d6ecd 100644
--- a/pytezos/michelson/grammar.py
+++ b/pytezos/michelson/grammar.py
@@ -1,5 +1,5 @@
# Inspired by https://github.com/jansorg/tezos-intellij/blob/master/grammar/michelson.bnf
-
+from pprint import pformat
from ply.lex import Lexer, lex
from ply.yacc import yacc
import re
@@ -9,7 +9,13 @@
class MichelsonParserError(ValueError):
- pass
+
+ def __init__(self, err_node, message=None):
+ message = message or f'failed to parse expression {pformat(err_node, compact=True)}'
+ super(MichelsonParserError, self).__init__(message)
+ self.message = message
+ self.line = err_node.lineno(0)
+ self.pos = err_node.lexpos(0)
class Sequence(list):
@@ -74,11 +80,14 @@ def p_instr_subseq(self, p):
def p_expr(self, p):
'''expr : PRIM annots args'''
- expr = expand_macro(
- prim=p[1],
- annots=p[2] or [],
- args=p[3] or []
- )
+ try:
+ expr = expand_macro(
+ prim=p[1],
+ annots=p[2] or [],
+ args=p[3] or []
+ )
+ except AssertionError as e:
+ raise MichelsonParserError(p, str(e))
p[0] = Sequence(expr) if isinstance(expr, list) else expr
def p_annots(self, p):
diff --git a/pytezos/michelson/macros.py b/pytezos/michelson/macros.py
index 3f44ee57d..fbf9bcbda 100644
--- a/pytezos/michelson/macros.py
+++ b/pytezos/michelson/macros.py
@@ -69,7 +69,7 @@ def expand_macro(prim, annots, args, internal=False):
res = handler(groups[0], annots, args)
return res if internal else seq(res)
- assert False, f'Unknown macro: {prim}'
+ assert False, f'unknown primitive {prim}'
def get_field_annots(annots):
diff --git a/pytezos/repl/control.py b/pytezos/repl/control.py
index 5ae18e38e..0d2b65f1e 100644
--- a/pytezos/repl/control.py
+++ b/pytezos/repl/control.py
@@ -1,5 +1,6 @@
import functools
from copy import deepcopy
+from pprint import pformat
from pytezos.repl.stack import Stack
from pytezos.repl.types import StackItem, assert_stack_type, assert_expr_equal, Option, Lambda, Bool, List, Or, Pair, \
@@ -10,7 +11,7 @@
def assert_no_annots(prim, annots):
- assert not annots, f'{prim}: unexpected annotations {annots}'
+ assert not annots, f'unexpected annotations {annots}'
def instruction(prim, args_len=0):
@@ -30,12 +31,12 @@ def wrapper(*args, **kwargs):
def parse_instruction(code_expr):
prim = code_expr.get('prim')
- if prim not in instructions:
- raise MichelsonRuntimeError.init('unknown instruction', prim) from None
+ if prim not in instructions:
+ raise MichelsonRuntimeError.init('unknown instruction', prim)
args_len, handler = instructions[prim]
args = code_expr.get('args', [])
if len(args) != args_len:
- raise MichelsonRuntimeError.init(f'expected {args_len} arg(s), got {len(args)}', prim) from None
+ raise MichelsonRuntimeError.init(f'expected {args_len} arg(s), got {len(args)}', prim)
annots = code_expr.get('annots', [])
return prim, args, annots, handler
@@ -49,13 +50,13 @@ def do_interpret(stack: Stack, code_expr):
try:
res = handler(stack, prim, args, annots)
except AssertionError as e:
- raise MichelsonRuntimeError.init(e.args, prim) from None
+ raise MichelsonRuntimeError.init(str(e), prim)
except MichelsonRuntimeError as e:
- raise MichelsonRuntimeError.wrap(e, prim) from None
+ raise MichelsonRuntimeError.wrap(e, prim)
else:
return res
else:
- assert False, f'unexpected code expression: {code_expr}'
+ assert False, f'unexpected code expression {pformat(code_expr, compact=True)}'
@instruction('PUSH', args_len=2)
@@ -124,7 +125,7 @@ def do_exec(stack: Stack, prim, args, annots):
do_interpret(lmbda_stack, lmbda.code)
ret = lmbda_stack.pop()
lmbda.assert_ret_type(ret)
- assert len(lmbda_stack) == 0, f'lambda stack is not empty: {lmbda_stack}'
+ assert len(lmbda_stack) == 0, f'lambda stack is not empty {lmbda_stack}'
return stack.ins(ret, annots=annots)
@@ -139,7 +140,7 @@ def do_apply(stack: Stack, prim, args, annots):
def do_failwith(stack: Stack, prim, args, annots):
assert_no_annots(prim, annots)
top = stack.pop()
- raise ValueError(top)
+ assert False, repr(top)
@instruction('IF', args_len=2)
@@ -249,15 +250,16 @@ def do_map(stack: Stack, prim, args, annots):
stack.ins(Pair.new(key, val))
do_interpret(stack, args[0])
else:
- assert False, f'Unexpected type: {type(container)}'
+ assert False, f'unexpected type {type(container)}'
@instruction('CAST', args_len=1)
def do_cast(stack: Stack, prim, args, annots):
+ assert_no_annots(prim, annots)
top = stack.pop()
assert_expr_equal(args[0], top.type_expr)
top.type_expr = args[0]
- return stack.ins(top, annots=annots)
+ return stack.ins(top)
@instruction('RENAME')
diff --git a/pytezos/repl/kernel.py b/pytezos/repl/kernel.py
index 98b886af3..c1ef72221 100644
--- a/pytezos/repl/kernel.py
+++ b/pytezos/repl/kernel.py
@@ -1,43 +1,46 @@
+from copy import deepcopy
from ipykernel.kernelbase import Kernel
+from tabulate import tabulate
+from pprint import pformat
from pytezos.michelson.grammar import MichelsonParserError
from pytezos.michelson.converter import michelson_to_micheline, micheline_to_michelson
-from pytezos.repl.stack import Stack, StackItem
from pytezos.repl.control import do_interpret, MichelsonRuntimeError
+from pytezos.repl.arithmetic import *
+from pytezos.repl.structures import *
-class MichelsonInterpreter:
-
- def __init__(self):
- self.stack = Stack([])
-
- def run(self, code):
- try:
- expr = michelson_to_micheline(code)
- except MichelsonParserError as e:
- return
-
- backup = self.stack.copy()
+def item_to_row(item: StackItem):
+ row = {
+ 'value_expression': micheline_to_michelson(item.val_expr),
+ 'type_expression': micheline_to_michelson(item.type_expr)
+ }
+ if item.val_annot is not None:
+ row['value_annotation'] = item.val_annot
+ return row
- try:
- res = do_interpret(self.stack, expr)
- except MichelsonRuntimeError as e:
- self.stack = backup
- return
- if isinstance(res, StackItem):
- return micheline_to_michelson(res.val_expr)
+def parse_error(error) -> tuple:
+ ename = type(error).__name__
+ if isinstance(error, MichelsonRuntimeError):
+ evalue, traceback = error.message, ['at ' + ' -> '.join(error.trace)]
+ elif isinstance(error, MichelsonParserError):
+ evalue, traceback = error.message, [f'at line {error.line}, pos {error.pos}']
+ else:
+ evalue, traceback = pformat(error.args, compact=True), []
+ return ename, evalue, [f'{ename}: {evalue}'] + traceback
class MichelsonKernel(Kernel):
implementation = 'IMichelson'
implementation_version = '0.1.0'
language_info = {
+ 'name': 'Michelson',
'mimetype': 'text/x-michelson',
'file_extension': 'tz',
'codemirror_mode': 'michelson'
}
- banner = 'Tezos VM language'
+ banner = 'Michelson (Tezos VM language)'
help_links = [
'https://michelson.nomadic-labs.com/',
'https://tezos.gitlab.io/whitedoc/michelson.html'
@@ -45,26 +48,47 @@ class MichelsonKernel(Kernel):
def __init__(self, **kwargs):
super(MichelsonKernel, self).__init__(**kwargs)
- self.interpreter = MichelsonInterpreter()
+ self.stack = Stack([])
+
+ def send_error(self, error):
+ ename, evalue, traceback = parse_error(error)
+ self.send_response(self.iopub_socket, 'stream', {'name': 'stderr', 'text': '\n'.join(traceback)})
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'ename': ename,
+ 'evalue': evalue,
+ 'traceback': traceback
+ }
+
+ def send_result(self, silent=False, result=None):
+ if not silent and result is not None:
+ if not isinstance(result, list):
+ result = [result]
+ text = tabulate(list(map(item_to_row, result)))
+ self.send_response(self.iopub_socket, 'stream', {'name': 'stdout', 'text': text})
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
def do_execute(self, code, silent, store_history=True, user_expressions=None, allow_stdin=False):
try:
- self.interpreter.run(code)
- except MichelsonRuntimeError:
- pass
- except MichelsonParserError:
- pass
- else:
- pass
-
- if not silent:
- stream_content = {'name': 'stdout', 'text': code}
- self.send_response(self.iopub_socket, 'stream', stream_content)
-
- return {'status': 'ok',
- 'execution_count': self.execution_count,
- 'payload': [],
- 'user_expressions': {}}
+ code_expr = michelson_to_micheline(code)
+ except Exception as e:
+ return self.send_error(e)
+
+ backup = deepcopy(self.stack)
+
+ try:
+ res = do_interpret(self.stack, code_expr)
+ except Exception as e:
+ self.stack = backup
+ return self.send_error(e)
+
+ return self.send_result(silent, res)
def do_complete(self, code, cursor_pos):
pass
diff --git a/pytezos/repl/parser.py b/pytezos/repl/parser.py
index f08b3925a..8205b6049 100644
--- a/pytezos/repl/parser.py
+++ b/pytezos/repl/parser.py
@@ -12,17 +12,21 @@
class MichelsonRuntimeError(ValueError):
def __init__(self, message, trace):
- super(MichelsonRuntimeError, self).__init__(message)
+ super(MichelsonRuntimeError, self).__init__(f'{message}: {" -> ".join(trace)}')
self.message = message
self.trace = trace
- @staticmethod
- def init(message, prim) -> 'MichelsonRuntimeError':
- return MichelsonRuntimeError(message, trace=[prim])
+ @classmethod
+ def init(cls, message, prim):
+ return cls(message, trace=[prim])
- @staticmethod
- def wrap(error: 'MichelsonRuntimeError', prim) -> 'MichelsonRuntimeError':
- return MichelsonRuntimeError(error.message, trace=[prim] + error.trace)
+ @classmethod
+ def wrap(cls, error: 'MichelsonRuntimeError', prim):
+ return cls(error.message, trace=[prim] + error.trace)
+
+
+class MichelsonTypeCheckError(MichelsonRuntimeError):
+ pass
def primitive(prim, args_len=0):
@@ -37,11 +41,11 @@ def wrapper(*args, **kwargs):
def assert_type(value, exp_type):
- assert isinstance(value, exp_type), f'expected {exp_type}, got {type(value)}'
+ assert isinstance(value, exp_type), f'expected {exp_type.__name__}, got {type(value).__name__}'
def parse_prim_expr(expr) -> tuple:
- assert isinstance(expr, dict), f'expected dict, got {type(expr)}'
+ assert isinstance(expr, dict), f'expected dict, got {type(expr).__name__}'
assert 'prim' in expr, f'prim field is absent'
return expr['prim'], expr.get('args', [])
@@ -89,7 +93,7 @@ def get_core_val(val_expr, core_type):
def get_string(val_expr):
- return int(get_core_val(val_expr, core_type='string'))
+ return get_core_val(val_expr, core_type='string')
def get_int(val_expr):
@@ -164,10 +168,10 @@ def assert_big_map_val(type_expr):
def parse_type(type_expr) -> Tuple[str, list, Callable]:
prim, args = parse_prim_expr(type_expr)
if prim not in parsers:
- raise MichelsonRuntimeError.init('unknown primitive', prim) from None
+ raise MichelsonRuntimeError.init('unknown primitive', prim)
args_len, func = parsers[prim]
if len(args) != args_len:
- raise MichelsonRuntimeError.init('expected {args_len} arg(s), got {len(args)}', prim) from None
+ raise MichelsonRuntimeError.init(f'expected {args_len} arg(s), got {len(args)}', prim)
return prim, args, func
@@ -176,9 +180,9 @@ def parse_value(val_expr, type_expr):
try:
res = func(val_expr, args)
except AssertionError as e:
- raise MichelsonRuntimeError(e.args, prim) from None
+ raise MichelsonTypeCheckError.init(str(e), prim)
except MichelsonRuntimeError as e:
- raise MichelsonRuntimeError.wrap(e, prim) from None
+ raise MichelsonTypeCheckError.wrap(e, prim)
else:
return res
diff --git a/pytezos/repl/stack.py b/pytezos/repl/stack.py
index 908b581d8..67aadc9da 100644
--- a/pytezos/repl/stack.py
+++ b/pytezos/repl/stack.py
@@ -1,4 +1,3 @@
-from copy import deepcopy
from pprint import pformat
from typing import List
@@ -21,10 +20,9 @@ def ins_many(self, items: list, index: int = 0):
def ins(self, item: StackItem, index: int = 0, annots=None):
assert_stack_item(item)
assert_pushable(item.type_expr)
- item.val_expr['annots'] = annots or []
- self.items.insert(index, item)
+ self.items.insert(index, item.rename(annots))
if index == 0:
- return self.items[0]
+ return self.items[index]
def peek(self):
assert len(self.items) > 0, 'stack is empty'
@@ -39,15 +37,10 @@ def pop(self, index: int = 0):
return res[0]
def pop2(self):
- res = self.pop_many(count=2)
- return tuple(*res)
+ return tuple(self.pop_many(count=2))
def pop3(self):
- res = self.pop_many(count=3)
- return tuple(*res)
-
- def copy(self) -> 'Stack':
- return Stack(deepcopy(self.items))
+ return tuple(self.pop_many(count=3))
def __len__(self) -> int:
return len(self.items)
diff --git a/pytezos/repl/types.py b/pytezos/repl/types.py
index 4764c72ea..a07a3f14a 100644
--- a/pytezos/repl/types.py
+++ b/pytezos/repl/types.py
@@ -1,4 +1,5 @@
from pprint import pformat
+from copy import deepcopy
from pytezos.encoding import is_pkh
from pytezos.repl.parser import parse_value, parse_prim_expr, assert_expr_equal, assert_comparable, \
@@ -30,10 +31,11 @@ class StackItem:
prim = ''
__cls__ = {}
- def __init__(self, *args, val_expr, type_expr):
+ def __init__(self, *args, val_expr, type_expr, **kwargs):
self.val_expr = val_expr
self.type_expr = type_expr
- self._val = parse_value(val_expr, type_expr)
+ self.val_annot = kwargs.get('val_annot')
+ self._val = kwargs.get('val', parse_value(val_expr, type_expr))
@staticmethod
def _get_type(type_expr):
@@ -90,61 +92,74 @@ def __len__(self):
def __repr__(self):
return pformat(self._val)
+ def __deepcopy__(self, memodict={}):
+ return type(self)(val=deepcopy(self._val),
+ val_expr=deepcopy(self.val_expr),
+ type_expr=deepcopy(self.type_expr),
+ val_annot=self.val_annot)
+
def arg_types(self):
return [self._get_type(x) for x in self.type_expr['args']]
+ def rename(self, annots: list):
+ val_annot = next(filter(lambda x: x.startswith('@'), annots or []), None)
+ return type(self)(val=deepcopy(self._val),
+ val_expr=deepcopy(self.val_expr),
+ type_expr=deepcopy(self.type_expr),
+ val_annot=val_annot)
+
class String(StackItem, prim='string'):
- def __init__(self, value='', val_expr=None, type_expr=None):
+ def __init__(self, value='', val_expr=None, type_expr=None, **kwargs):
assert isinstance(value, str)
super(String, self).__init__(
val_expr=val_expr or {'string': value},
- type_expr=type_expr or {'prim': self.prim})
+ type_expr=type_expr or {'prim': self.prim}, **kwargs)
class Int(StackItem, prim='int'):
- def __init__(self, value=0, val_expr=None, type_expr=None):
+ def __init__(self, value=0, val_expr=None, type_expr=None, **kwargs):
assert_type(value, int)
assert isinstance(value, int)
super(Int, self).__init__(
val_expr=val_expr or {'int': str(value)},
- type_expr=type_expr or {'prim': self.prim})
+ type_expr=type_expr or {'prim': self.prim}, **kwargs)
class Bytes(StackItem, prim='bytes'):
- def __init__(self, value=b'', type_expr=None, val_expr=None):
+ def __init__(self, value=b'', type_expr=None, val_expr=None, **kwargs):
assert_type(value, bytes)
super(Bytes, self).__init__(
val_expr=val_expr or {'bytes': value.hex()},
- type_expr=type_expr or {'prim': self.prim})
+ type_expr=type_expr or {'prim': self.prim}, **kwargs)
class Nat(Int, prim='nat'):
- def __init__(self, value=0, val_expr=None, type_expr=None):
+ def __init__(self, value=0, val_expr=None, type_expr=None, **kwargs):
assert_type(value, int)
assert value >= 0, 'expected non-negative value'
- super(Nat, self).__init__(value, val_expr=val_expr, type_expr=type_expr)
+ super(Nat, self).__init__(value, val_expr=val_expr, type_expr=type_expr, **kwargs)
class Bool(StackItem, prim='bool'):
- def __init__(self, value=False, val_expr=None, type_expr=None):
+ def __init__(self, value=False, val_expr=None, type_expr=None, **kwargs):
assert_type(value, bool)
super(Bool, self).__init__(
- val_expr=val_expr or {'int': str(value)},
- type_expr=type_expr or {'prim': self.prim})
+ val_expr=val_expr or {'prim': str(value)},
+ type_expr=type_expr or {'prim': self.prim}, **kwargs)
class Unit(StackItem, prim='unit'):
- def __init__(self, val_expr=None, type_expr=None):
+ def __init__(self, val_expr=None, type_expr=None, **kwargs):
super(Unit, self).__init__(
val_expr=val_expr or {'prim': 'Unit'},
- type_expr=type_expr or {'prim': self.prim})
+ type_expr=type_expr or {'prim': self.prim}, **kwargs)
class List(StackItem, prim='list', args_len=1):
@@ -350,9 +365,9 @@ class Key(StackItem, prim='key'):
class KeyHash(String, prim='key_hash'):
- def __init__(self, value, val_expr=None, type_expr=None):
+ def __init__(self, value, val_expr=None, type_expr=None, **kwargs):
assert is_pkh(value), f'expected key hash, got {value}'
- super(KeyHash, self).__init__(value, val_expr=val_expr, type_expr=type_expr)
+ super(KeyHash, self).__init__(value, val_expr=val_expr, type_expr=type_expr, **kwargs)
class Signature(StackItem, prim='signature'):
diff --git a/pytezos/tools/diff.py b/pytezos/tools/diff.py
index 24f20c15d..f2f213fe5 100644
--- a/pytezos/tools/diff.py
+++ b/pytezos/tools/diff.py
@@ -1,11 +1,12 @@
-import os
import re
import difflib
import simplejson as json
+from os.path import dirname, join
# Based on https://gist.github.com/noporpoise/16e731849eb1231e86d78f9dfeca3abc
_no_eol = "\ No newline at end of file"
_hdr_pat = re.compile('^@@ -(\d+),?(\d+)? \+(\d+),?(\d+)? @@$')
+proj_dir = dirname(dirname(dirname(__file__)))
def make_patch(a, b, filename, context_size=0):
@@ -70,7 +71,7 @@ def apply_patch(source, patch, revert=False):
def read_template(filename):
- with open(os.path.join(os.path.dirname(__file__), 'templates', filename)) as f:
+ with open(join(proj_dir, 'assets', filename)) as f:
return f.read()