From b7f0d36fe6ffcad6d7dadcfe4053d600213cb7b7 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sat, 23 Nov 2024 00:13:34 +0100 Subject: [PATCH 1/2] refact: rename decorator to be more PEP8 --- src/api/decorator.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/api/decorator.py b/src/api/decorator.py index fa62bdf7f..a9bc6bb83 100644 --- a/src/api/decorator.py +++ b/src/api/decorator.py @@ -1,7 +1,7 @@ from collections.abc import Callable -class classproperty: +class ClassProperty: """Decorator for class properties. Use @classproperty instead of @property to add properties to the class object. @@ -12,3 +12,8 @@ def __init__(self, fget: Callable[[type], Callable]) -> None: def __get__(self, owner_self, owner_cls: type): return self.fget(owner_cls) + + +def classproperty(fget: Callable[[type], Callable]) -> ClassProperty: + """Use this function as the decorator in lowercase to follow Python conventions.""" + return ClassProperty(fget) From a76a88fdd11d883c0456623498398cc32ede70a7 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sat, 23 Nov 2024 00:44:15 +0100 Subject: [PATCH 2/2] refact: improve typing --- src/api/constants.py | 54 ++++++++++++++++----------- src/symbols/type_.py | 45 +++++++++++----------- tests/runtime/check_test.py | 2 +- tests/runtime/update_test.py | 3 +- tests/symbols/test_symbolBASICTYPE.py | 3 +- 5 files changed, 60 insertions(+), 47 deletions(-) diff --git a/src/api/constants.py b/src/api/constants.py index 67ff04a91..489b496ac 100644 --- a/src/api/constants.py +++ b/src/api/constants.py @@ -9,7 +9,8 @@ import enum import os -from typing import Optional, Union +from enum import StrEnum +from typing import Final, Optional, Union from .decorator import classproperty @@ -29,7 +30,7 @@ @enum.unique -class CLASS(str, enum.Enum): +class CLASS(StrEnum): """Enums class constants""" unknown = "unknown" # 0 @@ -80,10 +81,12 @@ class TYPE(enum.IntEnum): fixed = 7 float = 8 string = 9 + boolean = 10 @classmethod - def type_size(cls, type_: "TYPE"): + def type_size(cls, type_: "TYPE") -> int: type_sizes = { + cls.boolean: 1, cls.byte: 1, cls.ubyte: 1, cls.integer: 2, @@ -98,59 +101,66 @@ def type_size(cls, type_: "TYPE"): return type_sizes[type_] @classproperty - def types(cls): + def types(cls) -> set["TYPE"]: return set(TYPE) @classmethod - def size(cls, type_: "TYPE"): + def size(cls, type_: "TYPE") -> int: return cls.type_size(type_) @classproperty - def integral(cls): - return {cls.byte, cls.ubyte, cls.integer, cls.uinteger, cls.long, cls.ulong} + def integral(cls) -> set["TYPE"]: + return {cls.boolean, cls.byte, cls.ubyte, cls.integer, cls.uinteger, cls.long, cls.ulong} @classproperty - def signed(cls): + def signed(cls) -> set["TYPE"]: return {cls.byte, cls.integer, cls.long, cls.fixed, cls.float} @classproperty - def unsigned(cls): - return {cls.ubyte, cls.uinteger, cls.ulong} + def unsigned(cls) -> set["TYPE"]: + return {cls.boolean, cls.ubyte, cls.uinteger, cls.ulong} @classproperty - def decimals(cls): + def decimals(cls) -> set["TYPE"]: return {cls.fixed, cls.float} @classproperty - def numbers(cls): - return set(cls.integral) | set(cls.decimals) + def numbers(cls) -> set["TYPE"]: + return cls.integral | cls.decimals @classmethod - def is_valid(cls, type_: "TYPE"): + def is_valid(cls, type_: "TYPE") -> bool: """Whether the given type is valid or not. """ return type_ in cls.types @classmethod - def is_signed(cls, type_: "TYPE"): + def is_signed(cls, type_: "TYPE") -> bool: return type_ in cls.signed @classmethod - def is_unsigned(cls, type_: "TYPE"): + def is_unsigned(cls, type_: "TYPE") -> bool: return type_ in cls.unsigned @classmethod - def to_signed(cls, type_: "TYPE"): + def to_signed(cls, type_: "TYPE") -> "TYPE": """Return signed type or equivalent""" if type_ in cls.unsigned: - return {TYPE.ubyte: TYPE.byte, TYPE.uinteger: TYPE.integer, TYPE.ulong: TYPE.long}[type_] + return { + TYPE.boolean: TYPE.boolean, + TYPE.ubyte: TYPE.byte, + TYPE.uinteger: TYPE.integer, + TYPE.ulong: TYPE.long, + }[type_] + if type_ in cls.decimals or type_ in cls.signed: return type_ + return cls.unknown @staticmethod - def to_string(type_: "TYPE"): + def to_string(type_: "TYPE") -> str: """Return ID representation (string) of a type""" return type_.name @@ -173,11 +183,11 @@ class SCOPE(str, enum.Enum): parameter = "parameter" @staticmethod - def is_valid(scope: Union[str, "SCOPE"]): + def is_valid(scope: Union[str, "SCOPE"]) -> bool: return scope in set(SCOPE) @staticmethod - def to_string(scope: "SCOPE"): + def to_string(scope: "SCOPE") -> str: assert SCOPE.is_valid(scope) return scope.value @@ -208,7 +218,7 @@ class LoopType(str, enum.Enum): # ---------------------------------------------------------------------- # Deprecated suffixes for variable names, such as "a$" # ---------------------------------------------------------------------- -DEPRECATED_SUFFIXES = ("$", "%", "&") +DEPRECATED_SUFFIXES: Final[frozenset[str]] = frozenset(("$", "%", "&")) # ---------------------------------------------------------------------- # Identifier type diff --git a/src/symbols/type_.py b/src/symbols/type_.py index 5d48b0c95..2f94682c2 100644 --- a/src/symbols/type_.py +++ b/src/symbols/type_.py @@ -108,8 +108,8 @@ def __bool__(self): class SymbolBASICTYPE(SymbolTYPE): - """Defines a basic type (Ubyte, Byte, etc..) - Basic (default) types are defined upon start and are case insensitive. + """Defines a basic type (Ubyte, Byte, etc.) + Basic (default) types are defined upon start and are case-insensitive. If name is None or '', default typename from TYPES.to_string will be used. """ @@ -237,73 +237,74 @@ class Type: fixed = SymbolBASICTYPE(TYPE.fixed) float_ = SymbolBASICTYPE(TYPE.float) string = SymbolBASICTYPE(TYPE.string) + boolean = SymbolBASICTYPE(TYPE.boolean) - types = [unknown, ubyte, byte_, uinteger, integer, ulong, long_, fixed, float_, string] + types = unknown, ubyte, byte_, uinteger, integer, ulong, long_, fixed, float_, string, boolean _by_name = {x.name: x for x in types} @staticmethod - def size(t: SymbolTYPE): + def size(t: SymbolTYPE) -> int: assert isinstance(t, SymbolTYPE) return t.size @staticmethod - def to_string(t: SymbolTYPE): + def to_string(t: SymbolTYPE) -> str: assert isinstance(t, SymbolTYPE) return t.name @classmethod - def by_name(cls, typename): + def by_name(cls, typename) -> SymbolBASICTYPE | None: """Converts a given typename to Type""" return cls._by_name.get(typename, None) @classproperty - def integrals(cls): - return (cls.byte_, cls.ubyte, cls.integer, cls.uinteger, cls.long_, cls.ulong) + def integrals(cls) -> set[SymbolBASICTYPE]: + return {cls.boolean, cls.byte_, cls.ubyte, cls.integer, cls.uinteger, cls.long_, cls.ulong} @classproperty - def signed(cls): - return cls.byte_, cls.integer, cls.long_, cls.fixed, cls.float_ + def signed(cls) -> set[SymbolBASICTYPE]: + return {cls.byte_, cls.integer, cls.long_, cls.fixed, cls.float_} @classproperty - def unsigned(cls): - return cls.ubyte, cls.uinteger, cls.ulong + def unsigned(cls) -> set[SymbolBASICTYPE]: + return {cls.boolean, cls.ubyte, cls.uinteger, cls.ulong} @classproperty - def decimals(cls): - return cls.fixed, cls.float_ + def decimals(cls) -> set[SymbolBASICTYPE]: + return {cls.fixed, cls.float_} @classproperty - def numbers(cls): - return tuple(list(cls.integrals) + list(cls.decimals)) + def numbers(cls) -> set[SymbolBASICTYPE]: + return cls.integrals | cls.decimals @classmethod - def is_numeric(cls, t: SymbolTYPE): + def is_numeric(cls, t: SymbolTYPE) -> bool: assert isinstance(t, SymbolTYPE) return t.final in cls.numbers @classmethod - def is_signed(cls, t: SymbolTYPE): + def is_signed(cls, t: SymbolTYPE) -> bool: assert isinstance(t, SymbolTYPE) return t.final in cls.signed @classmethod - def is_unsigned(cls, t: SymbolTYPE): + def is_unsigned(cls, t: SymbolTYPE) -> bool: assert isinstance(t, SymbolTYPE) return t.final in cls.unsigned @classmethod - def is_integral(cls, t: SymbolTYPE): + def is_integral(cls, t: SymbolTYPE) -> bool: assert isinstance(t, SymbolTYPE) return t.final in cls.integrals @classmethod - def is_decimal(cls, t: SymbolTYPE): + def is_decimal(cls, t: SymbolTYPE) -> bool: assert isinstance(t, SymbolTYPE) return t.final in cls.decimals @classmethod - def is_string(cls, t: SymbolTYPE): + def is_string(cls, t: SymbolTYPE) -> bool: assert isinstance(t, SymbolTYPE) return t.final == cls.string diff --git a/tests/runtime/check_test.py b/tests/runtime/check_test.py index 77bc9ae92..0c4a92402 100755 --- a/tests/runtime/check_test.py +++ b/tests/runtime/check_test.py @@ -3,7 +3,7 @@ import signal import sys -import zx +import zx # type: ignore[import-untyped] def signal_handler(sig, frame): diff --git a/tests/runtime/update_test.py b/tests/runtime/update_test.py index 2a9957f85..d05f01252 100755 --- a/tests/runtime/update_test.py +++ b/tests/runtime/update_test.py @@ -5,7 +5,7 @@ import sys -import zx +import zx # type: ignore[import-untyped] class Stop(Exception): @@ -46,6 +46,7 @@ def run_test(self, filename): def main(): with TakeSnapshot() as t: t.run_test(sys.argv[1]) + print("OK") diff --git a/tests/symbols/test_symbolBASICTYPE.py b/tests/symbols/test_symbolBASICTYPE.py index 8554d13ac..827868262 100644 --- a/tests/symbols/test_symbolBASICTYPE.py +++ b/tests/symbols/test_symbolBASICTYPE.py @@ -45,8 +45,9 @@ def test__ne__(self): def test_to_signed(self): for type_ in TYPE.types: - if type_ is TYPE.unknown or type_ == TYPE.string: + if type_ in {TYPE.unknown, TYPE.string, TYPE.boolean}: continue + t = SymbolBASICTYPE(type_) q = t.to_signed() self.assertTrue(q.is_signed)