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()