From 0afa00aca8a144214fcd0b8c9a3b87cdfb066da4 Mon Sep 17 00:00:00 2001 From: Tom Herman Date: Sun, 10 Dec 2023 14:10:47 +0200 Subject: [PATCH 1/6] add flake8 and modify flipjump for it to pass also add as [tests] extra: pytest-cov, mypy, tox --- .flake8 | 16 ++++++++++++++ flipjump/assembler/fj_parser.py | 2 +- flipjump/assembler/inner_classes/ops.py | 2 +- flipjump/assembler/preprocessor.py | 6 ++--- flipjump/fjm/fjm_consts.py | 2 +- flipjump/fjm/fjm_reader.py | 4 ++-- flipjump/fjm/fjm_writer.py | 4 ++-- flipjump/flipjump_cli.py | 22 +++++++++---------- .../interpretter/debugging/breakpoints.py | 2 +- flipjump/interpretter/fjm_run.py | 2 +- pyproject.toml | 9 +++++++- 11 files changed, 47 insertions(+), 24 deletions(-) create mode 100644 .flake8 diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..c799a5a --- /dev/null +++ b/.flake8 @@ -0,0 +1,16 @@ +[flake8] +exclude = + .git, + __pycache__, + .tox, + tests/inout + tests/compiled + venv + +max-line-length=120 + +per-file-ignores = + # imported but unused + flipjump/__init__.py: F401 + # redefinition of unused / undefined name + flipjump/assembler/fj_parser.py: F811, F821 diff --git a/flipjump/assembler/fj_parser.py b/flipjump/assembler/fj_parser.py index 94585bf..f9a9e08 100644 --- a/flipjump/assembler/fj_parser.py +++ b/flipjump/assembler/fj_parser.py @@ -734,7 +734,7 @@ def parse_macro_tree(input_files: List[Tuple[str, Path]], memory_width: int, war files_seen: Set[Union[str, Path]] = set() if not input_files: - raise FlipJumpParsingException(f"The FlipJump parser got an empty files list.") + raise FlipJumpParsingException("The FlipJump parser got an empty files list.") lexer = FJLexer() parser = FJParser(memory_width, warning_as_errors, input_files[0]) diff --git a/flipjump/assembler/inner_classes/ops.py b/flipjump/assembler/inner_classes/ops.py index 337e3ff..e455a20 100644 --- a/flipjump/assembler/inner_classes/ops.py +++ b/flipjump/assembler/inner_classes/ops.py @@ -48,7 +48,7 @@ def __hash__(self): return hash(self.to_tuple()) def __eq__(self, other): - return type(other) == MacroName and self.to_tuple() == other.to_tuple() + return isinstance(other, MacroName) and self.to_tuple() == other.to_tuple() def __repr__(self): return str(self) diff --git a/flipjump/assembler/preprocessor.py b/flipjump/assembler/preprocessor.py index 4d16570..94e86e3 100644 --- a/flipjump/assembler/preprocessor.py +++ b/flipjump/assembler/preprocessor.py @@ -27,7 +27,7 @@ def macro_resolve_error(curr_tree: CurrTree, msg='', *, orig_exception: BaseExce @param msg: the message to show on error @param orig_exception: if not None, raise from this base error. """ - error_str = f"Macro Resolve Error" + (f':\n {msg}\n' if msg else '.\n') + error_str = "Macro Resolve Error" + (f':\n {msg}\n' if msg else '.\n') if curr_tree: error_str += 'Macro call trace:\n' for i, op in enumerate(curr_tree): @@ -68,8 +68,8 @@ def __enter__(self): f"In {self.calling_op.code_position}.") self.curr_tree.append(self.calling_op) if self.max_recursion_depth is not None and len(self.curr_tree) > self.max_recursion_depth: - macro_resolve_error(self.curr_tree, f"The maximal macro-expansion recursive depth was reached. " - f"change the max_recursion_depth variable.") + macro_resolve_error(self.curr_tree, "The maximal macro-expansion recursive depth was reached. " + "change the max_recursion_depth variable.") def __exit__(self, exc_type, exc_val, exc_tb): self.curr_tree.pop() diff --git a/flipjump/fjm/fjm_consts.py b/flipjump/fjm/fjm_consts.py index 66d4b5a..047aecb 100644 --- a/flipjump/fjm/fjm_consts.py +++ b/flipjump/fjm/fjm_consts.py @@ -6,7 +6,7 @@ """ struct { u16 fj_magic; // 'F' + 'J'<<8 (0x4a46) - u16 word_size; // number of bits in the memory / a memory-word. also called "w". + u16 word_size; // number of bits in the memory / a memory-word. also called "w". u64 version; u64 segment_num; { // for versions > 0 diff --git a/flipjump/fjm/fjm_reader.py b/flipjump/fjm/fjm_reader.py index defef60..8784fe2 100644 --- a/flipjump/fjm/fjm_reader.py +++ b/flipjump/fjm/fjm_reader.py @@ -72,7 +72,7 @@ def _decompress_data(compressed_data: bytes) -> bytes: try: return lzma.decompress(compressed_data, format=_LZMA_FORMAT, filters=_LZMA_DECOMPRESSION_FILTERS) except lzma.LZMAError as e: - raise FlipJumpReadFjmException(f'Error: The compressed data is damaged; Unable to decompress.') from e + raise FlipJumpReadFjmException('Error: The compressed data is damaged; Unable to decompress.') from e def _read_decompressed_data(self, fjm_file: BinaryIO) -> List[int]: """ @@ -182,7 +182,7 @@ def get_word(self, bit_address: int) -> int: if bit_offset == 0: return self._get_memory_word(word_address) if word_address == ((1 << self.memory_width) - 1): - raise FlipJumpRuntimeMemoryException(f'Accessed outside of memory (beyond the last bit).') + raise FlipJumpRuntimeMemoryException('Accessed outside of memory (beyond the last bit).') lsw = self._get_memory_word(word_address) msw = self._get_memory_word(word_address + 1) diff --git a/flipjump/fjm/fjm_writer.py b/flipjump/fjm/fjm_writer.py index bc95ac9..724286c 100644 --- a/flipjump/fjm/fjm_writer.py +++ b/flipjump/fjm/fjm_writer.py @@ -35,7 +35,7 @@ def __init__(self, output_file: Path, memory_width: int, version: FJMVersion, if flags < 0 or flags >= (1 << 64): raise FlipJumpWriteFjmException(f"flags must be a 64bit positive number, not {flags}") if FJMVersion.BaseVersion == version and flags != 0: - raise FlipJumpWriteFjmException(f"version 0 does not support the flags option") + raise FlipJumpWriteFjmException("version 0 does not support the flags option") if FJMVersion.CompressedVersion == version: if lzma_preset not in range(10): raise FlipJumpWriteFjmException("version 3 requires an LZMA preset (0-9, faster->smaller).") @@ -56,7 +56,7 @@ def _compress_data(self, data: bytes) -> bytes: return lzma.compress(data, format=_LZMA_FORMAT, filters=_lzma_compression_filters(2 * self.word_size, self.lzma_preset)) except lzma.LZMAError as e: - raise FlipJumpWriteFjmException(f'Error: Unable to compress the data.') from e + raise FlipJumpWriteFjmException('Error: Unable to compress the data.') from e def write_to_file(self) -> None: """ diff --git a/flipjump/flipjump_cli.py b/flipjump/flipjump_cli.py index 275e3d1..0f97628 100644 --- a/flipjump/flipjump_cli.py +++ b/flipjump/flipjump_cli.py @@ -137,7 +137,7 @@ def get_fjm_file_path(args: argparse.Namespace, error_func: ErrorFunc, temp_dir_ if out_fjm_file is None: if args.asm: - error_func(f'assemble-only is used, but no outfile is specified.') + error_func('assemble-only is used, but no outfile is specified.') out_fjm_file = os.path.join(temp_dir_name, 'out.fjm') elif not args.run and not out_fjm_file.endswith('.fjm'): error_func(f'output file {out_fjm_file} is not a .fjm file.') @@ -282,16 +282,16 @@ def get_argument_parser() -> argparse.ArgumentParser: """ return argparse.ArgumentParser( description='Assemble and Run FlipJump programs.', - usage=f'fj [--asm | --run] [arguments] files [files ...]\n' - f'example usage:\n' - f' fj a.fj b.fj // assemble and run\n' - f' fj a.fj b.fj -o out.fjm // assemble save and run\n' - f' fj code.fj -d -B swap_start exit_label // assemble and debug\n\n' - f' fj --asm -o o.fjm a.fj -d dir/debug.fjd // assemble and save debug info\n' - f' fj --asm -o out.fjm a.fj b.fj --no_stl -w 32 ' - f'// assemble without the standard library, 32 bit memory\n\n' - f' fj --run prog.fjm // just run\n' - f' fj --run o.fjm -d dir/debug.fjd -B label // run and debug\n ' + usage='fj [--asm | --run] [arguments] files [files ...]\n' + 'example usage:\n' + ' fj a.fj b.fj // assemble and run\n' + ' fj a.fj b.fj -o out.fjm // assemble save and run\n' + ' fj code.fj -d -B swap_start exit_label // assemble and debug\n\n' + ' fj --asm -o o.fjm a.fj -d dir/debug.fjd // assemble and save debug info\n' + ' fj --asm -o out.fjm a.fj b.fj --no_stl -w 32 ' + '// assemble without the standard library, 32 bit memory\n\n' + ' fj --run prog.fjm // just run\n' + ' fj --run o.fjm -d dir/debug.fjd -B label // run and debug\n ' ) diff --git a/flipjump/interpretter/debugging/breakpoints.py b/flipjump/interpretter/debugging/breakpoints.py index 9728416..c1c58f5 100644 --- a/flipjump/interpretter/debugging/breakpoints.py +++ b/flipjump/interpretter/debugging/breakpoints.py @@ -363,7 +363,7 @@ def load_labels_dictionary(debugging_file: Optional[Path], labels_file_needed: b """ if debugging_file is None: if labels_file_needed: - print(f"Warning: debugging labels can't be found! no debugging file specified.") + print("Warning: debugging labels can't be found! no debugging file specified.") return {} if not debugging_file.is_file(): diff --git a/flipjump/interpretter/fjm_run.py b/flipjump/interpretter/fjm_run.py index 8e8a4c5..52f887c 100644 --- a/flipjump/interpretter/fjm_run.py +++ b/flipjump/interpretter/fjm_run.py @@ -49,7 +49,7 @@ def print(self, *, if TerminationCause.Looping != self.termination_cause: if self.last_ops_addresses is not None: labels_handler_missing_string = '' if labels_handler is not None and labels_handler.address_to_label \ - else f'**** You may want to use debugging flags for more debugging info ****\n\n' + else '**** You may want to use debugging flags for more debugging info ****\n\n' last_ops_str = f'\n\n{labels_handler_missing_string}' \ f'Last {len(self.last_ops_addresses)} ops were at these addresses ' \ f'(The most-recent op, the one that failed, is first):\n ' \ diff --git a/pyproject.toml b/pyproject.toml index 7b5e844..2e9216c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,9 +52,16 @@ plotly = { version = "^5.16.1", optional = true } pytest = { version = "^7.4.0", optional = true } pytest-ordering = { version = "^0.6", optional = true } pytest-xdist = { version = "^3.3.1", optional = true } +pytest-cov = { version = "^4.1.0", optional = true } +mypy = { version = "^1.7.1", optional = true } +flake8 = { version = "^6.1.0", optional = true } +tox = { version = "^4.11.4", optional = true } [tool.poetry.extras] -tests = ["pytest", "pytest-ordering", "pytest-xdist"] +tests = [ + "pytest", "pytest-ordering", "pytest-xdist", "pytest-cov", + "mypy", "flake8", "tox", +] stats = ["plotly"] #docs = [sphinx, sphinx_rtd_theme] From 4330e492336d3ddeff9ef862a762363e89bd7a12 Mon Sep 17 00:00:00 2001 From: Tom Herman Date: Sun, 10 Dec 2023 20:57:04 +0200 Subject: [PATCH 2/6] fix typings according to mypy, now mypy Success - tests + manual tests are passing - not using mypy --strict yet. --- flipjump/assembler/assembler.py | 6 ++-- flipjump/assembler/fj_parser.py | 15 ++++++---- flipjump/assembler/inner_classes/expr.py | 9 ++++-- flipjump/assembler/inner_classes/ops.py | 6 ++-- flipjump/assembler/preprocessor.py | 14 ++++----- flipjump/fjm/fjm_reader.py | 14 +++++++-- flipjump/fjm/fjm_writer.py | 8 ++--- flipjump/flipjump_cli.py | 22 +++++++------- flipjump/flipjump_quickstart.py | 8 ++--- .../interpretter/debugging/breakpoints.py | 29 ++++++++++--------- .../debugging/macro_usage_graph.py | 2 +- .../interpretter/debugging/message_boxes.py | 5 ++-- flipjump/interpretter/fjm_run.py | 4 +-- flipjump/interpretter/io_devices/BrokenIO.py | 3 ++ flipjump/interpretter/io_devices/IODevice.py | 4 +++ flipjump/py.typed | 0 flipjump/utils/classes.py | 2 +- flipjump/utils/functions.py | 19 ++++++------ pyproject.toml | 10 +++++++ tests/conftest.py | 2 +- tests/test_fj.py | 12 ++------ 21 files changed, 112 insertions(+), 82 deletions(-) create mode 100644 flipjump/py.typed diff --git a/flipjump/assembler/assembler.py b/flipjump/assembler/assembler.py index 8e1ec20..4b13d2b 100644 --- a/flipjump/assembler/assembler.py +++ b/flipjump/assembler/assembler.py @@ -81,7 +81,7 @@ def __init__(self, memory_width: int, first_segment: NewSegment, labels: Dict[st self.padding_ops_indices: List[int] = [] # indices in self.fj_words # return_address -> { (f3, f2, f1, f0) -> start_flip_address } - self.wflips_dict: Dict[int, Dict[Tuple[int, ...]]] = defaultdict(lambda: {}) + self.wflips_dict: Dict[int, Dict[Tuple[int, ...], int]] = defaultdict(lambda: {}) def get_wflip_spot(self) -> WFlipSpot: if self.padding_ops_indices: @@ -180,7 +180,7 @@ def labels_resolve(ops: Deque[LastPhaseOp], labels: Dict[str, int], @param memory_width: the memory-width @param fjm_writer: [out]: the .fjm file writer """ - first_segment: NewSegment = ops.popleft() + first_segment = ops.popleft() if not isinstance(first_segment, NewSegment): raise FlipJumpAssemblerException(f"The first op must be of type NewSegment (and not {first_segment}).") @@ -223,7 +223,7 @@ def assemble(input_files: List[Tuple[str, Path]], debugging_file_path: Optional[Path] = None, show_statistics: bool = False, print_time: bool = True, - max_recursion_depth: Optional[int] = DEFAULT_MAX_MACRO_RECURSION_DEPTH, + max_recursion_depth: int = DEFAULT_MAX_MACRO_RECURSION_DEPTH, ) -> None: """ runs the assembly pipeline. assembles the input files to a .fjm. diff --git a/flipjump/assembler/fj_parser.py b/flipjump/assembler/fj_parser.py index f9a9e08..8b73e9a 100644 --- a/flipjump/assembler/fj_parser.py +++ b/flipjump/assembler/fj_parser.py @@ -11,14 +11,19 @@ from flipjump.utils.exceptions import FlipJumpExprException, FlipJumpParsingException from flipjump.assembler.inner_classes.expr import Expr, get_minimized_expr from flipjump.assembler.inner_classes.ops import get_used_labels, get_declared_labels, \ - CodePosition, MacroName, Op, Macro, initial_macro_name, \ + CodePosition, MacroName, Op, Macro, INITIAL_MACRO_NAME, \ MacroCall, RepCall, FlipJump, WordFlip, Label, Segment, Reserve, Pad -global curr_file, curr_file_short_name, curr_text, error_occurred, all_errors, curr_namespace +curr_file: Path +curr_file_short_name: str +curr_text: str +curr_namespace: List[str] +all_errors: str +error_occurred: bool def get_position(lineno: int) -> CodePosition: - return CodePosition(curr_file, curr_file_short_name, lineno) + return CodePosition(str(curr_file), curr_file_short_name, lineno) def syntax_error(lineno: int, msg='') -> None: @@ -217,7 +222,7 @@ def __init__(self, memory_width: int, warning_as_errors: bool, first_file: Tuple self.consts: Dict[str, Expr] = {'w': Expr(memory_width)} self.warning_as_errors: bool = warning_as_errors self.macros: Dict[MacroName, Macro] = { - initial_macro_name: Macro([], [], [], '', _get_main_macro_code_position(first_file)) + INITIAL_MACRO_NAME: Macro([], [], [], '', _get_main_macro_code_position(first_file)) } def validate_free_macro_name(self, name: MacroName, lineno: int) -> None: @@ -362,7 +367,7 @@ def error(self, token: Token) -> None: @_('definable_line_statements') def program(self, p: ParsedRule) -> None: ops = p.definable_line_statements - self.macros[initial_macro_name].ops += ops + self.macros[INITIAL_MACRO_NAME].ops += ops # noinspection PyUnresolvedReferences @_('definable_line_statements NL definable_line_statement') diff --git a/flipjump/assembler/inner_classes/expr.py b/flipjump/assembler/inner_classes/expr.py index 111b0f2..e41ed31 100644 --- a/flipjump/assembler/inner_classes/expr.py +++ b/flipjump/assembler/inner_classes/expr.py @@ -1,14 +1,17 @@ from __future__ import annotations from operator import mul, add, sub, floordiv, lshift, rshift, mod, xor, or_, and_ -from typing import Union, Tuple, Set, Dict +from typing import Union, Tuple, Set, Dict, Callable from flipjump.utils.exceptions import FlipJumpExprException # dictionary from a math-op string, to its pythonic function. # @note: if changed, update Expr.__str__(). -op_string_to_function = { +UNARY_TYPE = Callable[[int], int] +BINARY_TYPE = Callable[[int, int], int] +TRINARY_TYPE = Callable[[int, int, int], int] +op_string_to_function: Dict[str, Union[UNARY_TYPE, BINARY_TYPE, TRINARY_TYPE]] = { '+': add, '-': sub, '*': mul, '/': floordiv, '%': mod, '<<': lshift, '>>': rshift, '^': xor, '|': or_, '&': and_, '#': lambda x: x.bit_length(), @@ -67,7 +70,7 @@ def eval_new(self, params_dict: Dict[str, Expr]) -> Expr: evaluated_args: Tuple[Expr, ...] = tuple(e.eval_new(params_dict) for e in args) if all(isinstance(e.value, int) for e in evaluated_args): try: - return Expr(op_string_to_function[op](*(arg.value for arg in evaluated_args))) + return Expr(op_string_to_function[op](*(arg.value for arg in evaluated_args))) # type: ignore[arg-type] except Exception as e: raise FlipJumpExprException(f'{repr(e)}. bad math operation ({op}): {str(self)}.') return Expr((op, evaluated_args)) diff --git a/flipjump/assembler/inner_classes/ops.py b/flipjump/assembler/inner_classes/ops.py index e455a20..e9b6627 100644 --- a/flipjump/assembler/inner_classes/ops.py +++ b/flipjump/assembler/inner_classes/ops.py @@ -55,9 +55,9 @@ def __repr__(self): # The macro that holds the ops that are outside any macro. -initial_macro_name = MacroName('') -initial_args = [] -initial_labels_prefix = '' +INITIAL_MACRO_NAME = MacroName('') +INITIAL_ARGS: List[Expr] = [] +INITIAL_LABELS_PREFIX = '' class FlipJump: diff --git a/flipjump/assembler/preprocessor.py b/flipjump/assembler/preprocessor.py index 94e86e3..6ec5114 100644 --- a/flipjump/assembler/preprocessor.py +++ b/flipjump/assembler/preprocessor.py @@ -2,7 +2,7 @@ import collections import sys -from typing import Dict, Tuple, Iterable, Union, Deque, Set, List, Optional +from typing import Dict, Tuple, Iterable, Union, Deque, Set, List, Optional, NoReturn from flipjump.interpretter.debugging.macro_usage_graph import show_macro_usage_pie_graph from flipjump.utils.constants import MACRO_SEPARATOR_STRING, STARTING_LABEL_IN_MACROS_STRING, \ @@ -11,7 +11,7 @@ from flipjump.assembler.inner_classes.expr import Expr from flipjump.assembler.inner_classes.ops import FlipJump, WordFlip, Label, Segment, Reserve, MacroCall, RepCall, \ CodePosition, Macro, LastPhaseOp, MacroName, NewSegment, ReserveBits, Pad, Padding, \ - initial_macro_name, initial_args, initial_labels_prefix + INITIAL_MACRO_NAME, INITIAL_ARGS, INITIAL_LABELS_PREFIX CurrTree = Deque[Union[MacroCall, RepCall]] OpsQueue = Deque[LastPhaseOp] @@ -20,7 +20,7 @@ wflip_start_label = '_.wflip_area_start_' -def macro_resolve_error(curr_tree: CurrTree, msg='', *, orig_exception: BaseException = None) -> None: +def macro_resolve_error(curr_tree: CurrTree, msg='', *, orig_exception: Optional[BaseException] = None) -> NoReturn: """ raise a descriptive error (with the macro-expansion trace). @param curr_tree: the ops in the macro-calling path to arrive in this macro @@ -77,7 +77,7 @@ def __exit__(self, exc_type, exc_val, exc_tb): def __init__(self, memory_width: int, macros: Dict[MacroName, Macro], - max_recursion_depth: Optional[int]): + max_recursion_depth: int): """ @param memory_width: the memory-width @param macros: parser's result; the dictionary from the macro names to the macro declaration @@ -89,7 +89,7 @@ def __init__(self, self.curr_address: int = 0 - self.macro_code_size = collections.defaultdict(lambda: 0) + self.macro_code_size: Dict[str, int] = collections.defaultdict(lambda: 0) self.curr_tree: CurrTree = collections.deque() @@ -317,7 +317,7 @@ def resolve_macros(memory_width: int, macros: Dict[MacroName, Macro], *, show_statistics: bool = False, - max_recursion_depth: Optional[int] = DEFAULT_MAX_MACRO_RECURSION_DEPTH, + max_recursion_depth: int = DEFAULT_MAX_MACRO_RECURSION_DEPTH, ) -> Tuple[OpsQueue, LabelsDict]: """ unwind the macro tree to a serialized-queue of ops, @@ -330,7 +330,7 @@ def resolve_macros(memory_width: int, @return: tuple of the queue of ops, and the labels' dictionary """ preprocessor_data = PreprocessorData(memory_width, macros, max_recursion_depth) - resolve_macro_aux(preprocessor_data, initial_macro_name, initial_args, initial_labels_prefix) + resolve_macro_aux(preprocessor_data, INITIAL_MACRO_NAME, INITIAL_ARGS, INITIAL_LABELS_PREFIX) preprocessor_data.finish(show_statistics) return preprocessor_data.get_result_ops_and_labels() diff --git a/flipjump/fjm/fjm_reader.py b/flipjump/fjm/fjm_reader.py index 8784fe2..70e4e8f 100644 --- a/flipjump/fjm/fjm_reader.py +++ b/flipjump/fjm/fjm_reader.py @@ -4,7 +4,7 @@ from pathlib import Path from struct import unpack from time import sleep -from typing import BinaryIO, List, Tuple +from typing import BinaryIO, List, Tuple, Dict from flipjump.fjm.fjm_consts import (FJ_MAGIC, _reserved_dict_threshold, _header_base_format, _header_extension_format, _header_base_size, _header_extension_size, _segment_format, _segment_size, @@ -27,6 +27,14 @@ class Reader: """ Used for reading a .fjm file from memory. """ + garbage_handling: GarbageHandling + magic: int + memory_width: int + version: FJMVersion + segment_num: int + memory: Dict[int, int] + zeros_boundaries: List[Tuple[int, int]] + def __init__(self, input_file: Path, *, garbage_handling: GarbageHandling = GarbageHandling.Stop): """ The .fjm-file reader @@ -47,9 +55,9 @@ def __init__(self, input_file: Path, *, garbage_handling: GarbageHandling = Garb raise FlipJumpReadFjmException(exception_message) from se def _init_header_fields(self, fjm_file: BinaryIO) -> None: - self.magic, self.memory_width, self.version, self.segment_num = \ + self.magic, self.memory_width, version, self.segment_num = \ unpack(_header_base_format, fjm_file.read(_header_base_size)) - self.version = FJMVersion(self.version) + self.version = FJMVersion(version) if FJMVersion.BaseVersion == self.version: self.flags, self.reserved = 0, 0 else: diff --git a/flipjump/fjm/fjm_writer.py b/flipjump/fjm/fjm_writer.py index 724286c..cad1fda 100644 --- a/flipjump/fjm/fjm_writer.py +++ b/flipjump/fjm/fjm_writer.py @@ -1,7 +1,7 @@ import lzma from pathlib import Path from struct import pack -from typing import List +from typing import List, Tuple from flipjump.fjm.fjm_consts import FJ_MAGIC, _header_base_format, _header_extension_format, _segment_format, \ SUPPORTED_VERSIONS_NAMES, _LZMA_FORMAT, _lzma_compression_filters, FJMVersion @@ -46,10 +46,10 @@ def __init__(self, output_file: Path, memory_width: int, version: FJMVersion, self.word_size = memory_width self.version = version self.flags = flags - self.reserved = 0 + self.reserved: int = 0 - self.segments = [] - self.data = [] # words array + self.segments: List[Tuple[int, int, int, int]] = [] + self.data: List[int] = [] # words array def _compress_data(self, data: bytes) -> bytes: try: diff --git a/flipjump/flipjump_cli.py b/flipjump/flipjump_cli.py index 0f97628..2b33d06 100644 --- a/flipjump/flipjump_cli.py +++ b/flipjump/flipjump_cli.py @@ -3,7 +3,7 @@ import os from pathlib import Path from tempfile import TemporaryDirectory -from typing import Tuple, List, Callable, Optional +from typing import Tuple, List, Callable, Optional, Union from flipjump import flipjump_quickstart from flipjump.assembler import assembler @@ -106,7 +106,7 @@ def get_version(version: Optional[int], is_outfile_specified: bool) -> FJMVersio return FJMVersion.NormalVersion -def assemble(out_fjm_file: Path, debug_file: Path, args: argparse.Namespace, error_func: ErrorFunc) -> None: +def assemble(out_fjm_file: Path, debug_file: Optional[Path], args: argparse.Namespace, error_func: ErrorFunc) -> None: """ prepare and verify arguments, and assemble the .fj files. @param out_fjm_file: the to-be-compiled .fjm-file path @@ -153,7 +153,7 @@ def get_debug_file_path(args: argparse.Namespace, error_func: ErrorFunc, temp_di @param temp_dir_name: a temporary directory that files can safely be created in @return: the debug-file path. If debug flag isn't set, and it's unneeded, return None """ - debug_file = args.debug + debug_file: Optional[str] = args.debug # can be None, '' (should be temp), or path_string debug_file_needed = not args.asm and any((args.breakpoint, args.breakpoint_contains)) if debug_file is None and debug_file_needed: @@ -162,19 +162,19 @@ def get_debug_file_path(args: argparse.Namespace, error_func: ErrorFunc, temp_di if args.werror: error_func(parser_warning) print(f"{parser_warning} Debugging data will be saved.") - debug_file = True + debug_file = '' - if debug_file is True: + if debug_file == '': if args.asm: error_func('assemble-only is used with the debug flag, but no debug file is specified.') if args.run: error_func('run-only is used with the debug flag, but no debug file is specified.') debug_file = os.path.join(temp_dir_name, 'debug.fjd') - if isinstance(debug_file, str): - debug_file = Path(debug_file) + if debug_file is None: + return None - return debug_file + return Path(debug_file) def add_run_only_arguments(parser: argparse.ArgumentParser) -> None: @@ -249,7 +249,7 @@ def add_universal_arguments(parser: argparse.ArgumentParser) -> None: parser.add_argument('files', help="the .fj files to assemble (if run-only, the .fjm file to run)", nargs='+') parser.add_argument('-s', '--silent', action='store_true', help="don't show assemble & run times, and run statistics") - parser.add_argument('-d', '--debug', nargs='?', const=True, metavar='PATH', + parser.add_argument('-d', '--debug', nargs='?', const='', metavar='PATH', type=str, help="debug-file path (used for breakpoints). If you both assemble & run, " "you may use this option without specifying a path, and a temporary file will be used") @@ -303,9 +303,9 @@ def parse_arguments(*, cmd_line_args: Optional[List[str]] = None) -> Tuple[argpa """ parser = get_argument_parser() add_arguments(parser) - cmd_line_args = parser.parse_args(args=cmd_line_args) + parsed_args = parser.parse_args(args=cmd_line_args) - return cmd_line_args, parser.error + return parsed_args, parser.error def execute_assemble_run(args: argparse.Namespace, error_func: ErrorFunc) -> None: diff --git a/flipjump/flipjump_quickstart.py b/flipjump/flipjump_quickstart.py index d443b79..5f64e2d 100644 --- a/flipjump/flipjump_quickstart.py +++ b/flipjump/flipjump_quickstart.py @@ -27,7 +27,7 @@ def assemble(fj_file_paths: List[Path], debugging_file_path: Optional[Path] = None, show_statistics: bool = False, print_time: bool = True, - max_recursion_depth: Optional[int] = DEFAULT_MAX_MACRO_RECURSION_DEPTH, + max_recursion_depth: int = DEFAULT_MAX_MACRO_RECURSION_DEPTH, ) -> None: """ runs the assembly pipeline. assembles the input files to a .fjm. @@ -211,7 +211,7 @@ def assemble_and_run(fj_file_paths: List[Path], warning_as_errors: bool = True, show_statistics: bool = False, print_time: bool = True, - max_recursion_depth: Optional[int] = DEFAULT_MAX_MACRO_RECURSION_DEPTH, + max_recursion_depth: int = DEFAULT_MAX_MACRO_RECURSION_DEPTH, io_device: Optional[IODevice] = None, show_trace: bool = False, @@ -250,7 +250,7 @@ def assemble_and_debug(fj_file_paths: List[Path], warning_as_errors: bool = True, show_statistics: bool = False, print_time: bool = True, - max_recursion_depth: Optional[int] = DEFAULT_MAX_MACRO_RECURSION_DEPTH, + max_recursion_depth: int = DEFAULT_MAX_MACRO_RECURSION_DEPTH, breakpoints_addresses: Optional[Set[int]] = None, breakpoints: Optional[Set[str]] = None, @@ -310,7 +310,7 @@ def assemble_and_run_test_output(fj_file_paths: List[Path], warning_as_errors: bool = True, show_statistics: bool = False, print_time: bool = True, - max_recursion_depth: Optional[int] = DEFAULT_MAX_MACRO_RECURSION_DEPTH, + max_recursion_depth: int = DEFAULT_MAX_MACRO_RECURSION_DEPTH, expected_termination_cause: TerminationCause = TerminationCause.Looping, should_raise_assertion_error: bool = False, diff --git a/flipjump/interpretter/debugging/breakpoints.py b/flipjump/interpretter/debugging/breakpoints.py index c1c58f5..dde4736 100644 --- a/flipjump/interpretter/debugging/breakpoints.py +++ b/flipjump/interpretter/debugging/breakpoints.py @@ -16,7 +16,7 @@ class BreakpointHandlerUnnecessary(Exception): pass -def calculate_variable_value(variable_prefix: Optional[Tuple[str, int, int]], address: int, mem: fjm_reader.Reader): +def calculate_variable_value(variable_prefix: Tuple[str, int, int], address: int, mem: fjm_reader.Reader): """ Read the variable related memory words (using 'mem'), and return the value of the word created by this bit/hex/Byte vector. @@ -96,7 +96,7 @@ def show_memory_address(variable_prefix: Optional[Tuple[str, int, int]], user_qu try: variable_prefix, address, label_name = handle_read_f_j(variable_prefix, address, label_name, w) - if not variable_prefix: + if variable_prefix is None: memory_word_value = mem.get_word(address) display_message_box( body_message=f'Reading {user_query}:\n' @@ -134,7 +134,8 @@ class BreakpointHandler: Handle breakpoints (know when breakpoints happen, query user for action). """ - def __init__(self, breakpoints: Dict[int, str], address_to_label: Dict[int, str], label_to_address: Dict[str, int]): + def __init__(self, breakpoints: Dict[int, Optional[str]], address_to_label: Dict[int, str], + label_to_address: Dict[str, int]): self.breakpoints = breakpoints self.address_to_label = address_to_label self.label_to_address = label_to_address @@ -142,7 +143,7 @@ def __init__(self, breakpoints: Dict[int, str], address_to_label: Dict[int, str] if self.address_to_label and 0 not in self.address_to_label: self.address_to_label[0] = ':memory-start:' - self.next_break = None # will break(point) when the number of executed ops reaches this number. + self.next_break: Optional[int] = None # will break(point) when the number of executed ops reaches this number. def should_break(self, ip: int, op_counter: int) -> bool: return self.next_break == op_counter or ip in self.breakpoints @@ -155,7 +156,7 @@ def get_address_str(self, address: int) -> str: If cant be found, just return the address. All labels returned are more pretty-formatted. """ if address in self.breakpoints and self.breakpoints[address] is not None: - label_repr = get_nice_label_repr(self.breakpoints[address], pad=4) + label_repr = get_nice_label_repr(self.breakpoints[address], pad=4) # type: ignore[arg-type] return f'{hex(address)}:\n{label_repr}' elif address in self.address_to_label: label_repr = get_nice_label_repr(self.address_to_label[address], pad=4) @@ -272,7 +273,7 @@ def apply_debug_action(self, action: str, op_counter: int) -> None: def handle_breakpoint(breakpoint_handler: BreakpointHandler, ip: int, mem: fjm_reader.Reader, - statistics: RunStatistics) -> BreakpointHandler: + statistics: RunStatistics) -> Optional[BreakpointHandler]: """ show debug message, query user for action, apply its action. @param breakpoint_handler: the breakpoint handler @@ -288,21 +289,20 @@ def handle_breakpoint(breakpoint_handler: BreakpointHandler, ip: int, mem: fjm_r try: breakpoint_handler.apply_debug_action(action, statistics.op_counter) + return breakpoint_handler except BreakpointHandlerUnnecessary: - breakpoint_handler = None - - return breakpoint_handler + return None def get_breakpoints(breakpoint_addresses: Optional[Set[int]], breakpoint_labels: Optional[Set[str]], breakpoint_contains_labels: Optional[Set[str]], label_to_address: Dict[str, int]) \ - -> Dict[int, str]: + -> Dict[int, Optional[str]]: """ generate the breakpoints' dictionary """ - breakpoints = {} + breakpoints: Dict[int, Optional[str]] = {} update_breakpoints_from_addresses_set(breakpoint_addresses, breakpoints) update_breakpoints_from_breakpoint_contains_set(breakpoint_contains_labels, breakpoints, label_to_address) @@ -373,8 +373,9 @@ def load_labels_dictionary(debugging_file: Optional[Path], labels_file_needed: b return load_debugging_labels(debugging_file) -def get_breakpoint_handler(debugging_file: Optional[Path], breakpoint_addresses: Set[int], breakpoint_labels: Set[str], - breakpoint_contains_labels: Set[str]) -> BreakpointHandler: +def get_breakpoint_handler(debugging_file: Optional[Path], breakpoint_addresses: Optional[Set[int]], + breakpoint_labels: Optional[Set[str]], + breakpoint_contains_labels: Optional[Set[str]]) -> BreakpointHandler: """ generate the breakpoint handler from the debugging file and the breakpoint sets. @param debugging_file: the debug file path (created at assemble time) @@ -386,7 +387,7 @@ def get_breakpoint_handler(debugging_file: Optional[Path], breakpoint_addresses: labels_file_needed = any((breakpoint_addresses, breakpoint_contains_labels)) label_to_address = load_labels_dictionary(debugging_file, labels_file_needed) - address_to_label = {} + address_to_label: Dict[int, str] = {} for label, address in label_to_address.items(): if address in address_to_label: if len(label) >= len(address_to_label[address]): diff --git a/flipjump/interpretter/debugging/macro_usage_graph.py b/flipjump/interpretter/debugging/macro_usage_graph.py index 64c028a..91549f0 100644 --- a/flipjump/interpretter/debugging/macro_usage_graph.py +++ b/flipjump/interpretter/debugging/macro_usage_graph.py @@ -9,7 +9,7 @@ def _prepare_first_and_second_level_significant_macros( main_thresh: float, secondary_thresh: float)\ -> Tuple[Dict[str, int], Dict[str, Dict[str, int]]]: first_level = {} - second_level = collections.defaultdict(lambda: dict()) + second_level: Dict[str, Dict[str, int]] = collections.defaultdict(lambda: dict()) for k, v in macro_code_size.items(): if MACRO_SEPARATOR_STRING not in k: if v < main_thresh: diff --git a/flipjump/interpretter/debugging/message_boxes.py b/flipjump/interpretter/debugging/message_boxes.py index a02e8d4..52c579c 100644 --- a/flipjump/interpretter/debugging/message_boxes.py +++ b/flipjump/interpretter/debugging/message_boxes.py @@ -19,7 +19,7 @@ def display_message_box_with_choices_and_get_answer(body_message: str, title_mes except ImportError: raise FlipJumpMissingImportException(EASYGUI_NOT_INSTALLED_MESSAGE) - answer = easygui.buttonbox(body_message, title_message, choices) + answer: Optional[str] = easygui.buttonbox(body_message, title_message, choices) if answer is None: return default_cancel_answer return answer @@ -50,4 +50,5 @@ def display_message_box_and_get_text_answer(body_message: str, title_message: st except ImportError: raise FlipJumpMissingImportException(EASYGUI_NOT_INSTALLED_MESSAGE) - return easygui.enterbox(msg=body_message, title=title_message) + answer: Optional[str] = easygui.enterbox(msg=body_message, title=title_message) + return answer diff --git a/flipjump/interpretter/fjm_run.py b/flipjump/interpretter/fjm_run.py index 52f887c..43441fb 100644 --- a/flipjump/interpretter/fjm_run.py +++ b/flipjump/interpretter/fjm_run.py @@ -21,7 +21,7 @@ def __init__(self, run_statistics: RunStatistics, termination_cause: Termination self.op_counter = run_statistics.op_counter self.flip_counter = run_statistics.flip_counter self.jump_counter = run_statistics.jump_counter - self.last_ops_addresses: Deque[int] = run_statistics.last_ops_addresses + self.last_ops_addresses: Optional[Deque[int]] = run_statistics.last_ops_addresses self.termination_cause = termination_cause @@ -56,7 +56,7 @@ def print(self, *, + '\n '.join([self.beautify_address(address, labels_handler) for address in self.last_ops_addresses][::-1]) if output_to_print is not None: - output_str = f"Program's output before it was terminated: {output_to_print}\n\n" + output_str = f"Program's output before it was terminated: {output_to_print!r}\n\n" print(f'\n' f'{output_str}' diff --git a/flipjump/interpretter/io_devices/BrokenIO.py b/flipjump/interpretter/io_devices/BrokenIO.py index 5c0c3c5..4f850ee 100644 --- a/flipjump/interpretter/io_devices/BrokenIO.py +++ b/flipjump/interpretter/io_devices/BrokenIO.py @@ -12,4 +12,7 @@ def read_bit(self) -> bool: def write_bit(self, bit: bool) -> None: raise BrokenIOUsed(f"program tried to write a bit ({int(bit)}) to the BrokenIO device") + def get_output(self, *, allow_incomplete_output=False) -> bytes: + raise BrokenIOUsed(f"program tried to get current output from a BrokenIO device") + # default __del__ diff --git a/flipjump/interpretter/io_devices/IODevice.py b/flipjump/interpretter/io_devices/IODevice.py index eaff5f8..0475e14 100644 --- a/flipjump/interpretter/io_devices/IODevice.py +++ b/flipjump/interpretter/io_devices/IODevice.py @@ -13,4 +13,8 @@ def read_bit(self) -> bool: def write_bit(self, bit: bool) -> None: pass + @abstractmethod + def get_output(self, *, allow_incomplete_output=False) -> bytes: + pass + # Also, each class should implement a "__del__" to flush last changes before it gets deleted. diff --git a/flipjump/py.typed b/flipjump/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/flipjump/utils/classes.py b/flipjump/utils/classes.py index ed9d68e..ee82aec 100644 --- a/flipjump/utils/classes.py +++ b/flipjump/utils/classes.py @@ -80,7 +80,7 @@ def __init__(self, memory_width: int, last_ops_debugging_list_length: Optional[i self.pause_timer = self.PauseTimer() def get_run_time(self) -> float: - return time() - self._start_time - self.pause_timer.paused_time + return time() - self._start_time - float(self.pause_timer.paused_time) def register_op_address(self, ip: int): if self.last_ops_addresses is not None: diff --git a/flipjump/utils/functions.py b/flipjump/utils/functions.py index 95668cd..ca9806b 100644 --- a/flipjump/utils/functions.py +++ b/flipjump/utils/functions.py @@ -4,7 +4,7 @@ import lzma import os from pathlib import Path -from typing import List, Dict, Tuple, Union +from typing import List, Dict, Tuple, Union, Optional from flipjump.utils.constants import DEBUG_JSON_ENCODING, DEBUG_JSON_LZMA_FORMAT, DEBUG_JSON_LZMA_FILTERS, STL_PATH @@ -18,7 +18,7 @@ def get_stl_paths() -> List[Path]: return [STL_PATH / f'{lib}.fj' for lib in stl_options['all']] -def save_debugging_labels(debugging_file_path: Path, labels: Dict[str, int]) -> None: +def save_debugging_labels(debugging_file_path: Optional[Path], labels: Dict[str, int]) -> None: """ save the labels' dictionary to the debugging-file as lzma2-compressed json @param debugging_file_path: the file's path @@ -37,11 +37,12 @@ def load_debugging_labels(debugging_file_path: Path) -> Dict[str, int]: @param debugging_file_path: the file's path @return: the labels' dictionary """ - if debugging_file_path: - with open(debugging_file_path, 'rb') as f: - compressed_data = f.read() - data = lzma.decompress(compressed_data, format=DEBUG_JSON_LZMA_FORMAT, filters=DEBUG_JSON_LZMA_FILTERS) - return json.loads(data.decode(DEBUG_JSON_ENCODING)) + with open(debugging_file_path, 'rb') as f: + compressed_data = f.read() + + data = lzma.decompress(compressed_data, format=DEBUG_JSON_LZMA_FORMAT, filters=DEBUG_JSON_LZMA_FILTERS) + debugging_labels: Dict[str, int] = json.loads(data.decode(DEBUG_JSON_ENCODING)) + return debugging_labels def get_file_tuples(files: List[str], *, no_stl: bool = False) -> List[Tuple[str, Path]]: @@ -63,10 +64,10 @@ def get_file_tuples(files: List[str], *, no_stl: bool = False) -> List[Tuple[str return file_tuples -def get_temp_directory_suffix(files: List[Union[Path, str]]) -> str: +def get_temp_directory_suffix(files: Union[List[Path], List[str]]) -> str: """ create a suffix for the temp directory name, using args. @param files: the list of fj-code files. @return: the suffix """ - return f'__{"_".join(map(os.path.basename, files))}__temp_directory' + return f'__{"_".join(os.path.basename(str(file)) for file in files)}__temp_directory' diff --git a/pyproject.toml b/pyproject.toml index 2e9216c..6128d8b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,3 +67,13 @@ stats = ["plotly"] [tool.poetry.scripts] fj = 'flipjump.flipjump_cli:main' + + +[tool.mypy] +warn_return_any = true +warn_unused_configs = true +disable_error_code = ["import-untyped"] + +[[tool.mypy.overrides]] +module = "flipjump.assembler.fj_parser" +disable_error_code = ["name-defined", "no-redef", "index", "no-any-return"] diff --git a/tests/conftest.py b/tests/conftest.py index f32a81e..fd186dc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -14,7 +14,7 @@ build_tests_lock = Lock() -build_tests_queue = Queue() +build_tests_queue: Queue[Tuple[List, List]] = Queue() COMPILE_ARGUMENTS_FIXTURE = "compile_args" diff --git a/tests/test_fj.py b/tests/test_fj.py index f3f6df0..9fa4b25 100644 --- a/tests/test_fj.py +++ b/tests/test_fj.py @@ -1,4 +1,5 @@ from pathlib import Path +from typing import Optional from flipjump import run_test_output from flipjump.assembler import assembler @@ -112,15 +113,8 @@ def __init__(self, save_debug_file: bool, debug_info_length: int, test_name: str if save_debug_file: self.debugging_file_path = Path(f'{self.fjm_path.absolute()}{DEBUGGING_FILE_SUFFIX}') - if in_file_path: - self.in_file_path = ROOT_PATH / in_file_path - else: - self.in_file_path = None - - if out_file_path: - self.out_file_path = ROOT_PATH / out_file_path - else: - self.out_file_path = None + self.in_file_path: Optional[Path] = ROOT_PATH / in_file_path if in_file_path else None + self.out_file_path: Optional[Path] = ROOT_PATH / out_file_path if out_file_path else None def get_defined_input(self) -> bytes: """ From a828555a1282b34344a23909afeb7efbc5665e7a Mon Sep 17 00:00:00 2001 From: Tom Herman Date: Mon, 11 Dec 2023 01:30:38 +0200 Subject: [PATCH 3/6] mypy --strict success: modified project for mypy --strict to pass - tests + manual tests are passing --- flipjump/__init__.py | 10 +++-- flipjump/assembler/assembler.py | 6 +-- flipjump/assembler/fj_parser.py | 2 +- flipjump/assembler/inner_classes/expr.py | 4 +- flipjump/assembler/inner_classes/ops.py | 26 +++++------ flipjump/assembler/preprocessor.py | 9 ++-- flipjump/fjm/fjm_reader.py | 4 +- flipjump/flipjump_cli.py | 4 +- .../interpretter/debugging/breakpoints.py | 6 ++- flipjump/interpretter/io_devices/BrokenIO.py | 4 +- flipjump/interpretter/io_devices/FixedIO.py | 2 +- flipjump/interpretter/io_devices/IODevice.py | 2 +- .../interpretter/io_devices/StandardIO.py | 2 +- flipjump/utils/classes.py | 12 ++--- pyproject.toml | 5 +-- tests/conftest.py | 44 +++++++++++-------- 16 files changed, 78 insertions(+), 64 deletions(-) diff --git a/flipjump/__init__.py b/flipjump/__init__.py index fccd306..87c0f49 100644 --- a/flipjump/__init__.py +++ b/flipjump/__init__.py @@ -1,9 +1,13 @@ from flipjump.flipjump_cli import assemble_run_according_to_cmd_line_args from flipjump.flipjump_quickstart import (assemble, run, debug, run_test_output, - assemble_and_run, assemble_and_debug, assemble_and_run_test_output, - FJMVersion, TerminationCause, TerminationStatistics, - FixedIO, IODevice, StandardIO) + assemble_and_run, assemble_and_debug, assemble_and_run_test_output) +from flipjump.fjm.fjm_consts import FJMVersion +from flipjump.interpretter.fjm_run import TerminationStatistics +from flipjump.utils.classes import TerminationCause from flipjump.utils.exceptions import IODeviceException, FlipJumpException +from flipjump.interpretter.io_devices.IODevice import IODevice +from flipjump.interpretter.io_devices.FixedIO import FixedIO +from flipjump.interpretter.io_devices.StandardIO import StandardIO from flipjump.interpretter.io_devices.BrokenIO import BrokenIO diff --git a/flipjump/assembler/assembler.py b/flipjump/assembler/assembler.py index 4b13d2b..cb08f59 100644 --- a/flipjump/assembler/assembler.py +++ b/flipjump/assembler/assembler.py @@ -13,12 +13,12 @@ from flipjump.assembler.preprocessor import resolve_macros -def assert_address_in_memory(memory_width: int, address: int): +def assert_address_in_memory(memory_width: int, address: int) -> None: if address < 0 or address >= (1 << memory_width): raise FlipJumpAssemblerException(f"Not enough space with the {memory_width}-bits memory-width.") -def validate_addresses(memory_width, first_address, last_address): +def validate_addresses(memory_width: int, first_address: int, last_address: int) -> None: if first_address % memory_width != 0 or last_address % memory_width != 0: raise FlipJumpAssemblerException(f'segment boundaries are unaligned: ' f'[{hex(first_address)}, {hex(last_address - 1)}].') @@ -101,7 +101,7 @@ def close_and_add_segment(self, fjm_writer: Writer) -> None: self.first_address, self.next_wflip_address, self.fj_words, self.wflip_words) - def _insert_wflip_label(self, address: int): + def _insert_wflip_label(self, address: int) -> None: self.labels[f'{WFLIP_LABEL_PREFIX}{self.wflips_so_far}'] = address self.wflips_so_far += 1 diff --git a/flipjump/assembler/fj_parser.py b/flipjump/assembler/fj_parser.py index 8b73e9a..4d9c5f0 100644 --- a/flipjump/assembler/fj_parser.py +++ b/flipjump/assembler/fj_parser.py @@ -26,7 +26,7 @@ def get_position(lineno: int) -> CodePosition: return CodePosition(str(curr_file), curr_file_short_name, lineno) -def syntax_error(lineno: int, msg='') -> None: +def syntax_error(lineno: int, msg: str = '') -> None: global error_occurred, all_errors error_occurred = True curr_position = get_position(lineno) diff --git a/flipjump/assembler/inner_classes/expr.py b/flipjump/assembler/inner_classes/expr.py index e41ed31..12cae60 100644 --- a/flipjump/assembler/inner_classes/expr.py +++ b/flipjump/assembler/inner_classes/expr.py @@ -35,9 +35,9 @@ def __init__(self, expr: Union[int, str, Tuple[str, Tuple[Expr, ...]]]): def is_int(self) -> bool: return isinstance(self.value, int) - def __int__(self): + def __int__(self) -> int: if self.is_int(): - return self.value + return self.value # type: ignore[return-value] raise FlipJumpExprException(f"Can't resolve labels: {', '.join(self.all_unknown_labels())}") def all_unknown_labels(self) -> Set[str]: diff --git a/flipjump/assembler/inner_classes/ops.py b/flipjump/assembler/inner_classes/ops.py index e9b6627..bb295ee 100644 --- a/flipjump/assembler/inner_classes/ops.py +++ b/flipjump/assembler/inner_classes/ops.py @@ -3,7 +3,7 @@ import dataclasses import os from dataclasses import dataclass -from typing import Union, Dict, Set, List, Tuple +from typing import Union, Dict, Set, List, Tuple, Any from flipjump.utils.exceptions import FlipJumpExprException from flipjump.assembler.inner_classes.expr import Expr @@ -41,16 +41,16 @@ def __str__(self) -> str: return self.name return f"{self.name}({self.parameter_num})" - def to_tuple(self): + def to_tuple(self) -> Tuple[str, int]: return self.name, self.parameter_num - def __hash__(self): + def __hash__(self) -> int: return hash(self.to_tuple()) - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: return isinstance(other, MacroName) and self.to_tuple() == other.to_tuple() - def __repr__(self): + def __repr__(self) -> str: return str(self) @@ -69,7 +69,7 @@ def __init__(self, flip: Expr, jump: Expr, code_position: CodePosition): self.jump = jump self.code_position = code_position - def __str__(self): + def __str__(self) -> str: return f"Flip: {self.flip}, Jump: {self.jump}, at {self.code_position}" def eval_new(self, labels_dict: Dict[str, Expr]) -> FlipJump: @@ -97,7 +97,7 @@ def __init__(self, word_address: Expr, flip_value: Expr, return_address: Expr, c self.return_address = return_address self.code_position = code_position - def __str__(self): + def __str__(self) -> str: return f"Flip Word {self.word_address} by {self.flip_value}, and return to {self.return_address}. " \ f"at {self.code_position}" @@ -128,7 +128,7 @@ def __init__(self, ops_alignment: Expr, code_position: CodePosition): self.ops_alignment = ops_alignment self.code_position = code_position - def __str__(self): + def __str__(self) -> str: return f"Pad {self.ops_alignment} ops, at {self.code_position}" def eval_new(self, labels_dict: Dict[str, Expr]) -> Pad: @@ -152,7 +152,7 @@ def __init__(self, start_address: Expr, code_position: CodePosition): self.start_address = start_address self.code_position = code_position - def __str__(self): + def __str__(self) -> str: return f"Segment {self.start_address}, at {self.code_position}" def eval_new(self, labels_dict: Dict[str, Expr]) -> Segment: @@ -176,7 +176,7 @@ def __init__(self, reserved_bit_size: Expr, code_position: CodePosition): self.reserved_bit_size = reserved_bit_size self.code_position = code_position - def __str__(self): + def __str__(self) -> str: return f"Reserve {self.reserved_bit_size}, at {self.code_position}" def eval_new(self, labels_dict: Dict[str, Expr]) -> Reserve: @@ -201,7 +201,7 @@ def __init__(self, macro_name: str, arguments: List[Expr], code_position: CodePo self.arguments = arguments self.code_position = code_position - def __str__(self): + def __str__(self) -> str: return f"macro call. {self.macro_name.name} {', '.join(map(str, self.arguments))}. at {self.code_position}" def eval_new(self, labels_dict: Dict[str, Expr]) -> MacroCall: @@ -228,7 +228,7 @@ def __init__(self, repeat_times: Expr, iterator_name: str, macro_name: str, argu self.arguments = arguments self.code_position = code_position - def __str__(self): + def __str__(self) -> str: return f"rep call. rep({self.repeat_times}, {self.iterator_name}) {self.macro_name.name} " \ f"{', '.join(map(str, self.arguments))}. at {self.code_position}" @@ -279,7 +279,7 @@ def __init__(self, name: str, code_position: CodePosition): self.name = name self.code_position = code_position - def __str__(self): + def __str__(self) -> str: return f'Label "{self.name}:", at {self.code_position}' def eval_name(self, labels_dict: Dict[str, Expr]) -> str: diff --git a/flipjump/assembler/preprocessor.py b/flipjump/assembler/preprocessor.py index 6ec5114..e0b34ec 100644 --- a/flipjump/assembler/preprocessor.py +++ b/flipjump/assembler/preprocessor.py @@ -20,7 +20,8 @@ wflip_start_label = '_.wflip_area_start_' -def macro_resolve_error(curr_tree: CurrTree, msg='', *, orig_exception: Optional[BaseException] = None) -> NoReturn: +def macro_resolve_error(curr_tree: CurrTree, msg: str = '', *, + orig_exception: Optional[BaseException] = None) -> NoReturn: """ raise a descriptive error (with the macro-expansion trace). @param curr_tree: the ops in the macro-calling path to arrive in this macro @@ -61,7 +62,7 @@ def __init__(self, self.macros = macros self.max_recursion_depth = max_recursion_depth - def __enter__(self): + def __enter__(self) -> None: macro_name = self.calling_op.macro_name if macro_name not in self.macros: macro_resolve_error(self.curr_tree, f"macro {macro_name} is used but isn't defined. " @@ -71,7 +72,7 @@ def __enter__(self): macro_resolve_error(self.curr_tree, "The maximal macro-expansion recursive depth was reached. " "change the max_recursion_depth variable.") - def __exit__(self, exc_type, exc_val, exc_tb): + def __exit__(self, exc_type, exc_val, exc_tb): # type: ignore[no-untyped-def] self.curr_tree.pop() def __init__(self, @@ -158,7 +159,7 @@ def insert_macro_start_label(self, label: str, code_position: CodePosition) -> N """ self.macro_start_labels.append((self.curr_address, label, code_position)) - def insert_macro_start_labels_if_their_address_not_used(self): + def insert_macro_start_labels_if_their_address_not_used(self) -> None: for address, label, code_position in self.macro_start_labels[::-1]: if address not in self.addresses_with_labels: self.insert_label(label, code_position, address=address) diff --git a/flipjump/fjm/fjm_reader.py b/flipjump/fjm/fjm_reader.py index 70e4e8f..5206bde 100644 --- a/flipjump/fjm/fjm_reader.py +++ b/flipjump/fjm/fjm_reader.py @@ -63,7 +63,7 @@ def _init_header_fields(self, fjm_file: BinaryIO) -> None: else: self.flags, self.reserved = unpack(_header_extension_format, fjm_file.read(_header_extension_size)) - def _init_segments(self, fjm_file: BinaryIO) -> List[Tuple]: + def _init_segments(self, fjm_file: BinaryIO) -> List[Tuple[int, int, int, int]]: return [unpack(_segment_format, fjm_file.read(_segment_size)) for _ in range(self.segment_num)] def _validate_header(self) -> None: @@ -98,7 +98,7 @@ def _read_decompressed_data(self, fjm_file: BinaryIO) -> List[int]: for i in range(0, len(file_data), word_bytes_size)] return data - def _init_memory(self, segments: List[Tuple], data: List[int]) -> None: + def _init_memory(self, segments: List[Tuple[int, int, int, int]], data: List[int]) -> None: self.memory = {} self.zeros_boundaries = [] diff --git a/flipjump/flipjump_cli.py b/flipjump/flipjump_cli.py index 2b33d06..3ec4471 100644 --- a/flipjump/flipjump_cli.py +++ b/flipjump/flipjump_cli.py @@ -3,7 +3,7 @@ import os from pathlib import Path from tempfile import TemporaryDirectory -from typing import Tuple, List, Callable, Optional, Union +from typing import Tuple, List, Callable, Optional from flipjump import flipjump_quickstart from flipjump.assembler import assembler @@ -182,7 +182,7 @@ def add_run_only_arguments(parser: argparse.ArgumentParser) -> None: add the arguments that are usable in run time. @param parser: the parser """ - def _check_int_positive(value: str): + def _check_int_positive(value: str) -> int: int_value = int(value) if int_value <= 0: raise argparse.ArgumentTypeError(f"{value} is an invalid positive int value") diff --git a/flipjump/interpretter/debugging/breakpoints.py b/flipjump/interpretter/debugging/breakpoints.py index dde4736..7636ff4 100644 --- a/flipjump/interpretter/debugging/breakpoints.py +++ b/flipjump/interpretter/debugging/breakpoints.py @@ -16,7 +16,8 @@ class BreakpointHandlerUnnecessary(Exception): pass -def calculate_variable_value(variable_prefix: Tuple[str, int, int], address: int, mem: fjm_reader.Reader): +def calculate_variable_value(variable_prefix: Tuple[str, int, int], address: int, mem: fjm_reader.Reader)\ + -> Tuple[int, int, int]: """ Read the variable related memory words (using 'mem'), and return the value of the word created by this bit/hex/Byte vector. @@ -40,7 +41,8 @@ def calculate_variable_value(variable_prefix: Tuple[str, int, int], address: int return value, first_address, last_address -def handle_read_f_j(variable_prefix: Optional[Tuple[str, int, int]], address: int, label_name: Optional[str], w: int): +def handle_read_f_j(variable_prefix: Optional[Tuple[str, int, int]], address: int, label_name: Optional[str], w: int)\ + -> Tuple[Optional[Tuple[str, int, int]], int, str]: """ If variable_type is f/j, modify the address accordingly, them set variable_prefix = None. Anyway, also create a label_name string and return the new one. diff --git a/flipjump/interpretter/io_devices/BrokenIO.py b/flipjump/interpretter/io_devices/BrokenIO.py index 4f850ee..ea5f233 100644 --- a/flipjump/interpretter/io_devices/BrokenIO.py +++ b/flipjump/interpretter/io_devices/BrokenIO.py @@ -12,7 +12,7 @@ def read_bit(self) -> bool: def write_bit(self, bit: bool) -> None: raise BrokenIOUsed(f"program tried to write a bit ({int(bit)}) to the BrokenIO device") - def get_output(self, *, allow_incomplete_output=False) -> bytes: - raise BrokenIOUsed(f"program tried to get current output from a BrokenIO device") + def get_output(self, *, allow_incomplete_output: bool = False) -> bytes: + raise BrokenIOUsed("program tried to get current output from a BrokenIO device") # default __del__ diff --git a/flipjump/interpretter/io_devices/FixedIO.py b/flipjump/interpretter/io_devices/FixedIO.py index f6ecfc1..45ed14a 100644 --- a/flipjump/interpretter/io_devices/FixedIO.py +++ b/flipjump/interpretter/io_devices/FixedIO.py @@ -39,7 +39,7 @@ def write_bit(self, bit: bool) -> None: self.current_output_byte = 0 self.bits_to_write_in_output_byte = 0 - def get_output(self, *, allow_incomplete_output=False) -> bytes: + def get_output(self, *, allow_incomplete_output: bool = False) -> bytes: """ @raise IncompleteOutput when the number of outputted bits can't be divided by 8 @return: full output until now diff --git a/flipjump/interpretter/io_devices/IODevice.py b/flipjump/interpretter/io_devices/IODevice.py index 0475e14..772e549 100644 --- a/flipjump/interpretter/io_devices/IODevice.py +++ b/flipjump/interpretter/io_devices/IODevice.py @@ -14,7 +14,7 @@ def write_bit(self, bit: bool) -> None: pass @abstractmethod - def get_output(self, *, allow_incomplete_output=False) -> bytes: + def get_output(self, *, allow_incomplete_output: bool = False) -> bytes: pass # Also, each class should implement a "__del__" to flush last changes before it gets deleted. diff --git a/flipjump/interpretter/io_devices/StandardIO.py b/flipjump/interpretter/io_devices/StandardIO.py index db11ba0..90ce636 100644 --- a/flipjump/interpretter/io_devices/StandardIO.py +++ b/flipjump/interpretter/io_devices/StandardIO.py @@ -49,7 +49,7 @@ def write_bit(self, bit: bool) -> None: self.current_output_byte = 0 self.bits_to_write_in_output_byte = 0 - def get_output(self, *, allow_incomplete_output=False) -> bytes: + def get_output(self, *, allow_incomplete_output: bool = False) -> bytes: if not allow_incomplete_output and 0 != self.bits_to_write_in_output_byte: raise IncompleteOutput("tries to get output when an unaligned number of bits was outputted " "(doesn't divide 8)") diff --git a/flipjump/utils/classes.py b/flipjump/utils/classes.py index ee82aec..1b4917c 100644 --- a/flipjump/utils/classes.py +++ b/flipjump/utils/classes.py @@ -40,7 +40,7 @@ def __enter__(self) -> None: self.start_time = time() print(self.init_message, end='', flush=True) - def __exit__(self, exc_type, exc_val, exc_tb) -> None: + def __exit__(self, exc_type, exc_val, exc_tb) -> None: # type: ignore[no-untyped-def] if self.print_time: print(f'{time() - self.start_time:.3f}s') @@ -50,13 +50,13 @@ class RunStatistics: maintains times and counters of the current run. """ class PauseTimer: - def __init__(self): - self.paused_time = 0 + def __init__(self) -> None: + self.paused_time: float = 0.0 - def __enter__(self): + def __enter__(self) -> None: self.pause_start_time = time() - def __exit__(self, exc_type, exc_val, exc_tb): + def __exit__(self, exc_type, exc_val, exc_tb): # type: ignore[no-untyped-def] self.paused_time += time() - self.pause_start_time def __init__(self, memory_width: int, last_ops_debugging_list_length: Optional[int]): @@ -82,7 +82,7 @@ def __init__(self, memory_width: int, last_ops_debugging_list_length: Optional[i def get_run_time(self) -> float: return time() - self._start_time - float(self.pause_timer.paused_time) - def register_op_address(self, ip: int): + def register_op_address(self, ip: int) -> None: if self.last_ops_addresses is not None: self.last_ops_addresses.append(ip) diff --git a/pyproject.toml b/pyproject.toml index 6128d8b..32d5443 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,10 +70,9 @@ fj = 'flipjump.flipjump_cli:main' [tool.mypy] -warn_return_any = true -warn_unused_configs = true +strict = true disable_error_code = ["import-untyped"] [[tool.mypy.overrides]] module = "flipjump.assembler.fj_parser" -disable_error_code = ["name-defined", "no-redef", "index", "no-any-return"] +disable_error_code = ["name-defined", "no-redef", "index", "no-any-return", "misc"] diff --git a/tests/conftest.py b/tests/conftest.py index fd186dc..4b64c99 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,16 +5,22 @@ from queue import Queue from threading import Lock from pathlib import Path -from typing import List, Iterable, Callable, Tuple, Optional, Any +from typing import List, Iterable, Callable, Tuple, Optional, Any, Union import pytest +from _pytest.config import Config +from _pytest.mark import ParameterSet +from _pytest.nodes import Item, Collector +from _pytest.python import Metafunc +from _pytest.reports import CollectReport from flipjump.utils.constants import LAST_OPS_DEBUGGING_LIST_DEFAULT_LENGTH from tests.test_fj import CompileTestArgs, RunTestArgs +TestsType = Tuple[List[ParameterSet], List[ParameterSet]] build_tests_lock = Lock() -build_tests_queue: Queue[Tuple[List, List]] = Queue() +build_tests_queue: Queue[TestsType] = Queue() COMPILE_ARGUMENTS_FIXTURE = "compile_args" @@ -80,7 +86,8 @@ def argument_line_iterator(csv_file_path: Path, num_of_args: int) -> Iterable[Li yield list(map(str.strip, line)) -def get_compile_tests_params_from_csv(csv_file_path: Path, xfail_list: List[str], save_debug_file: bool) -> List: +def get_compile_tests_params_from_csv(csv_file_path: Path, xfail_list: List[str], save_debug_file: bool) \ + -> List[ParameterSet]: """ read the compile-tests from the csv @param csv_file_path: read tests from this csv @@ -102,7 +109,7 @@ def get_compile_tests_params_from_csv(csv_file_path: Path, xfail_list: List[str] def get_run_tests_params_from_csv(csv_file_path: Path, xfail_list: List[str], - save_debug_file: bool, debug_info_length: int) -> List: + save_debug_file: bool, debug_info_length: int) -> List[ParameterSet]: """ read the run-tests from the csv @param csv_file_path: read tests from this csv @@ -131,7 +138,7 @@ def pytest_addoption(parser: pytest.Parser) -> None: colliding_keywords = set(TEST_TYPES) & SAVED_KEYWORDS assert not colliding_keywords - def _check_int_positive_with_true(value): + def _check_int_positive_with_true(value: str) -> Tuple[bool, int]: int_value = int(value) if int_value <= 0: raise argparse.ArgumentTypeError(f"{value} is an invalid positive int value") @@ -242,7 +249,8 @@ def is_test_name_ok(name: str, exact: Optional[List[str]], contains: Optional[Li return False -def filter_by_test_name(tests_args: List, get_option: Callable[[str], Optional[List[str]]]) -> List: +def filter_by_test_name(tests_args: List[ParameterSet], + get_option: Callable[[str], Optional[List[str]]]) -> List[ParameterSet]: """ filter the test list by the test names (and the namings flags) @param tests_args: the tests @@ -257,16 +265,16 @@ def filter_by_test_name(tests_args: List, get_option: Callable[[str], Optional[L if all(filter_list is None for filter_list in (exact, contains, startswith, endswith)): return tests_args - return [args for args in tests_args if - is_test_name_ok(args.values[0].test_name, exact, contains, startswith, endswith)] + return [args for args in tests_args if is_test_name_ok( + args.values[0].test_name, exact, contains, startswith, endswith)] # type: ignore[union-attr] -def pytest_generate_tests(metafunc) -> None: +def pytest_generate_tests(metafunc: Metafunc) -> None: """ gather the tests from the csvs, and parametrize the compile-tests and run-tests fixtures with it. @param metafunc: enables to get-flags, parametrize-fixtures """ - def get_option(opt): + def get_option(opt: str) -> Any: return metafunc.config.getoption(opt) compile_tests__heavy_first, run_tests__heavy_first = get_tests_from_csvs__heavy_first__execute_once(get_option) @@ -278,7 +286,7 @@ def get_option(opt): metafunc.parametrize(RUN_ARGUMENTS_FIXTURE, run_tests__heavy_first, ids=repr) -def is_not_skipped(test) -> bool: +def is_not_skipped(test: Union[Item, Collector]) -> bool: if hasattr(test, 'callspec') and hasattr(test.callspec, 'params'): params = test.callspec.params for fixture_name, fixture_type in fixtures_name_to_type.items(): @@ -288,19 +296,19 @@ def is_not_skipped(test) -> bool: @pytest.hookimpl(hookwrapper=True) -def pytest_collectreport(report): - report.result = filter(is_not_skipped, report.result) +def pytest_collectreport(report: CollectReport) -> Iterable[None]: + report.result = list(filter(is_not_skipped, report.result)) yield # noinspection PyUnusedLocal @pytest.hookimpl(hookwrapper=True) -def pytest_collection_modifyitems(config, items): +def pytest_collection_modifyitems(config: Config, items: List[Item]) -> Iterable[None]: yield items[:] = filter(is_not_skipped, items) -def get_tests_from_csvs__heavy_first__execute_once(get_option: Callable[[str], Any]) -> Tuple[List, List]: +def get_tests_from_csvs__heavy_first__execute_once(get_option: Callable[[str], Any]) -> TestsType: """ get the tests from the csv. heavy first. if more than 1 worker - only one worker will do the work, and distribute the result to the other workers. @@ -319,7 +327,7 @@ def get_tests_from_csvs__heavy_first__execute_once(get_option: Callable[[str], A return tests -def get_tests_from_csvs(get_option: Callable[[str], Any]) -> Tuple[List, List]: +def get_tests_from_csvs(get_option: Callable[[str], Any]) -> TestsType: """ get the tests from the csv. heavy first. @param get_option: function that returns the flags values @@ -336,7 +344,7 @@ def get_tests_from_csvs(get_option: Callable[[str], Any]) -> Tuple[List, List]: if is_parallel_active(): save_debug_file = False - compile_tests = [] + compile_tests: List[ParameterSet] = [] if check_compile_tests: compiles_csvs = {test_type: TESTS_PATH / f"test_compile_{test_type}.csv" for test_type in types_to_run__heavy_first} @@ -345,7 +353,7 @@ def get_tests_from_csvs(get_option: Callable[[str], Any]) -> Tuple[List, List]: save_debug_file)) compile_tests = filter_by_test_name(compile_tests, get_option) - run_tests = [] + run_tests: List[ParameterSet] = [] if check_run_tests: run_csvs = {test_type: TESTS_PATH / f"test_run_{test_type}.csv" for test_type in types_to_run__heavy_first} From 6f9157537d32288447a7180f68e14f8f302fbc73 Mon Sep 17 00:00:00 2001 From: Tom Herman Date: Mon, 11 Dec 2023 11:20:38 +0200 Subject: [PATCH 4/6] add tox support (py3.8-12, full-tests, mypy, flake8), bump version 1.2.2 --- .flake8 | 16 ------- .../debugging/macro_usage_graph.py | 2 +- pyproject.toml | 7 ++-- tests/conftest.py | 2 +- tox.ini | 42 +++++++++++++++++++ 5 files changed, 47 insertions(+), 22 deletions(-) delete mode 100644 .flake8 create mode 100644 tox.ini diff --git a/.flake8 b/.flake8 deleted file mode 100644 index c799a5a..0000000 --- a/.flake8 +++ /dev/null @@ -1,16 +0,0 @@ -[flake8] -exclude = - .git, - __pycache__, - .tox, - tests/inout - tests/compiled - venv - -max-line-length=120 - -per-file-ignores = - # imported but unused - flipjump/__init__.py: F401 - # redefinition of unused / undefined name - flipjump/assembler/fj_parser.py: F811, F821 diff --git a/flipjump/interpretter/debugging/macro_usage_graph.py b/flipjump/interpretter/debugging/macro_usage_graph.py index 91549f0..77097d7 100644 --- a/flipjump/interpretter/debugging/macro_usage_graph.py +++ b/flipjump/interpretter/debugging/macro_usage_graph.py @@ -55,7 +55,7 @@ def _choose_most_significant_macros(first_level: Dict[str, int], second_level: D def _show_macro_usage_graph(chosen_macros: List[Tuple[str, int]]) -> None: try: - import plotly.graph_objects as go + import plotly.graph_objects as go # type: ignore[import-not-found,unused-ignore] except ImportError: ordered_chosen_macros = sorted(chosen_macros, key=lambda name_count: name_count[1], reverse=True) total_ops = sum([count for name, count in chosen_macros]) diff --git a/pyproject.toml b/pyproject.toml index 32d5443..da4dd18 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "flipjump" -version = "1.2.1" +version = "1.2.2" description = "The single instruction language - Flip a bit, then Jump" authors = ["Tom Herman "] license = "BSD-2-Clause-Simplified" @@ -28,7 +28,6 @@ classifiers = [ "Topic :: Software Development :: Interpreters", "Topic :: Software Development :: Libraries", "Operating System :: OS Independent", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", @@ -42,7 +41,7 @@ classifiers = [ [tool.poetry.dependencies] -python = "^3.7" +python = "^3.8.1" sly = "^0.4" easygui = "^0.98.3" @@ -60,7 +59,7 @@ tox = { version = "^4.11.4", optional = true } [tool.poetry.extras] tests = [ "pytest", "pytest-ordering", "pytest-xdist", "pytest-cov", - "mypy", "flake8", "tox", + "mypy", "flake8", "tox", "python381", ] stats = ["plotly"] #docs = [sphinx, sphinx_rtd_theme] diff --git a/tests/conftest.py b/tests/conftest.py index 4b64c99..1319502 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -20,7 +20,7 @@ TestsType = Tuple[List[ParameterSet], List[ParameterSet]] build_tests_lock = Lock() -build_tests_queue: Queue[TestsType] = Queue() +build_tests_queue: "Queue[TestsType]" = Queue() COMPILE_ARGUMENTS_FIXTURE = "compile_args" diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..a7ac73b --- /dev/null +++ b/tox.ini @@ -0,0 +1,42 @@ +[tox] +min_version = 4.0 +env_list = py38, py39, py310, py311, py312, + full_tests, mypy, flake8 +isolated_build = true + +[testenv] +setenv = PYTHONPATH = {toxinidir} +deps = .[tests] +commands = pytest --fast --medium + +[testenv:mypy] +python = py312 +commands = mypy flipjump + +[testenv:full_tests] +python = py312 +commands = + pytest --compile -n auto --all + pytest --run -n auto --all + +[testenv:flake8] +python = py312 +commands = flake8 + + +[flake8] +exclude = + .git, + __pycache__, + .tox, + tests/inout + tests/compiled + venv + +max-line-length=120 + +per-file-ignores = + # imported but unused + flipjump/__init__.py: F401 + # redefinition of unused / undefined name + flipjump/assembler/fj_parser.py: F811, F821 From 7573541028e9eab9807ed1ae6d769fddf0e424f8 Mon Sep 17 00:00:00 2001 From: Tom Herman Date: Mon, 11 Dec 2023 12:10:24 +0200 Subject: [PATCH 5/6] add tox-gh-actions tests.yml workflow --- .github/workflows/tests.yml | 24 ++++++++++++++++++++++++ tox.ini | 11 ++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..0fd6d7c --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,24 @@ +name: Tests + +on: pull_request + +jobs: + test_all_versions: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [windows-latest, ubuntu-latest, macos-latest] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install tox tox-gh-actions + - name: Test with tox + run: tox diff --git a/tox.ini b/tox.ini index a7ac73b..11fbfe6 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,17 @@ [tox] min_version = 4.0 env_list = py38, py39, py310, py311, py312, - full_tests, mypy, flake8 + mypy, flake8, full_tests isolated_build = true +[gh-actions] +python = + 3.8: py38 + 3.9: py39 + 3.10: py310 + 3.11: py311 + 3.12: py312 mypy flake8 full_tests + [testenv] setenv = PYTHONPATH = {toxinidir} deps = .[tests] @@ -15,6 +23,7 @@ commands = mypy flipjump [testenv:full_tests] python = py312 +platform = windows-latest: windows commands = pytest --compile -n auto --all pytest --run -n auto --all From b79b7cdf10ce77e4816a69399d9e8207535c6f9a Mon Sep 17 00:00:00 2001 From: Tom Herman Date: Mon, 11 Dec 2023 12:15:44 +0200 Subject: [PATCH 6/6] run test_full and show results, separately from tox - also small fixes --- .github/workflows/tests.yml | 31 ++++++++++++++++++++++++++++--- pyproject.toml | 5 ++--- tox.ini | 10 +++++----- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0fd6d7c..0285e74 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,8 +2,11 @@ name: Tests on: pull_request +env: + COLUMNS: 120 + jobs: - test_all_versions: + test_py_os_variations: runs-on: ${{ matrix.os }} strategy: matrix: @@ -20,5 +23,27 @@ jobs: run: | python -m pip install --upgrade pip pip install tox tox-gh-actions - - name: Test with tox - run: tox + - name: Test with tox py${{ matrix.python-version }}-${{ matrix.os }} + run: tox -v + + test_full: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: 3.12 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install .[tests] + - name: Test pytest + run: | + pytest --compile -n auto --all --junitxml=./compile-test-results.xml + pytest --run -n auto --all --junitxml=./run-test-results.xml + - name: Show tests results + if: always() + uses: pmeier/pytest-results-action@main + with: + path: ./*-test-results.xml diff --git a/pyproject.toml b/pyproject.toml index da4dd18..e43bde2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,15 +51,14 @@ plotly = { version = "^5.16.1", optional = true } pytest = { version = "^7.4.0", optional = true } pytest-ordering = { version = "^0.6", optional = true } pytest-xdist = { version = "^3.3.1", optional = true } -pytest-cov = { version = "^4.1.0", optional = true } mypy = { version = "^1.7.1", optional = true } flake8 = { version = "^6.1.0", optional = true } tox = { version = "^4.11.4", optional = true } [tool.poetry.extras] tests = [ - "pytest", "pytest-ordering", "pytest-xdist", "pytest-cov", - "mypy", "flake8", "tox", "python381", + "pytest", "pytest-ordering", "pytest-xdist", + "mypy", "flake8", "tox" ] stats = ["plotly"] #docs = [sphinx, sphinx_rtd_theme] diff --git a/tox.ini b/tox.ini index 11fbfe6..65a341e 100644 --- a/tox.ini +++ b/tox.ini @@ -5,12 +5,13 @@ env_list = py38, py39, py310, py311, py312, isolated_build = true [gh-actions] +;Notice that in gh-actions we don't run the full_tests via tox. python = 3.8: py38 3.9: py39 3.10: py310 3.11: py311 - 3.12: py312 mypy flake8 full_tests + 3.12: py312, mypy, flake8 [testenv] setenv = PYTHONPATH = {toxinidir} @@ -18,18 +19,17 @@ deps = .[tests] commands = pytest --fast --medium [testenv:mypy] -python = py312 +basepython = python3.12 commands = mypy flipjump [testenv:full_tests] -python = py312 -platform = windows-latest: windows +basepython = python3.12 commands = pytest --compile -n auto --all pytest --run -n auto --all [testenv:flake8] -python = py312 +basepython = python3.12 commands = flake8