Skip to content

Commit

Permalink
Add type annotations for public interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
ptmcg committed Dec 19, 2022
1 parent 57356fa commit adc1e4d
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 25 deletions.
5 changes: 5 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ plusminus Change Log

0.7.0 -

- Added type annotations on public methods, to aid in type checking and
autocomplete.

- Added sin², sin³, sin⁻¹, etc. methods for common mathematical notation
such as sin²(x) for sin(x)**2, and sin⁻¹(x) for asin(x).

Expand All @@ -23,6 +26,8 @@ plusminus Change Log

- Added better docstring to enumerate all supported parser options.

- Added scan_string method, to replace deprecated scanString method.

(Many features added by TheOneMusic, thanks!)

0.6.0 -
Expand Down
59 changes: 34 additions & 25 deletions plusminus/plusminus.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,16 @@
from abc import ABC
from collections import namedtuple, deque
from contextlib import contextmanager
from functools import partial, total_ordering, lru_cache
from functools import partial, total_ordering
from itertools import groupby
import math
import operator
import random
import pyparsing as pp
import string
import sys
from typing import Dict, Callable, List, Any, Union, Tuple

import pyparsing as pp

# monkeypatch explain into an instance method
from pyparsing import ParseBaseException, ParseException
Expand Down Expand Up @@ -423,7 +425,7 @@ def __repr__(self):


class TernaryNode(ArithNode):
opns_map = {}
opns_map: Dict[Tuple[str, str], Callable] = {}

def left_associative_evaluate(self, oper_fn_map):
operands = self.tokens
Expand Down Expand Up @@ -462,15 +464,15 @@ def evaluate(self):


class ArithmeticUnaryPostOp(UnaryNode):
opns_map = {}
opns_map: Dict[str, Callable] = {}

def evaluate(self):
with _trimming_exception_traceback():
return self.left_associative_evaluate(self.opns_map)


class ArithmeticBinaryOp(BinaryNode):
opns_map = {
opns_map: Dict[str, Callable] = {
"+": operator.add,
"-": operator.sub,
"−": operator.sub,
Expand Down Expand Up @@ -499,7 +501,7 @@ def evaluate(self):


class ArithmeticFunction(ArithNode):
fn_map = None
fn_map: Dict[str, FunctionSpec] = None

def evaluate(self):
with _trimming_exception_traceback():
Expand Down Expand Up @@ -663,7 +665,7 @@ def evaluate(self):
return ret


DEFAULT_BASE_FUNCTION_MAP = {
DEFAULT_BASE_FUNCTION_MAP: Dict[str, FunctionSpec] = {
"abs": FunctionSpec("abs", abs, 1),
"round": FunctionSpec("round", round, (1, 2)),
"trunc": FunctionSpec("trunc", math.trunc, 1),
Expand Down Expand Up @@ -704,7 +706,7 @@ class BaseArithmeticParser:
LEFT = pp.opAssoc.LEFT
RIGHT = pp.opAssoc.RIGHT

def usage(self):
def usage(self) -> str:
import textwrap

msg = textwrap.dedent(
Expand Down Expand Up @@ -754,7 +756,7 @@ def usage(self):
)

class IdentifierNode(ArithNode):
_assigned_vars = {}
_assigned_vars: Dict[str, Any] = {}

@property
def name(self):
Expand Down Expand Up @@ -811,12 +813,12 @@ def __init__(self, **options):
"allow_user_functions", self.user_variables_supported
)

self._added_operator_specs = []
self._added_function_specs = {}
self._base_operators = (
self._added_operator_specs: List[OperatorSpec] = []
self._added_function_specs: Dict[str, FunctionSpec] = {}
self._base_operators: List[str] = (
"** * // / mod × ÷ + - < > <= >= == != ≠ ≤ ≥ ∈ ∉ ∩ ∪ & | in not and ∧ or ∨ ?:"
).split()
self._base_function_map = options.get("base_function_map", DEFAULT_BASE_FUNCTION_MAP)
self._base_function_map: Dict[str, FunctionSpec] = options.get("base_function_map", DEFAULT_BASE_FUNCTION_MAP)

# epsilon for computing "close" floating point values - can be updated in customize
self.epsilon = options.get("epsilon", 1e-15)
Expand All @@ -843,20 +845,23 @@ def __init__(self, **options):
self.parse = self._parse

@property
def base_function_map(self):
def base_function_map(self) -> Dict[str, FunctionSpec]:
return {**self._base_function_map}

@property
def added_function_specs(self):
def added_function_specs(self) -> Dict[str, FunctionSpec]:
return {**self._added_function_specs}

@property
def added_operator_specs(self):
def added_operator_specs(self) -> List[OperatorSpec]:
return self._added_operator_specs[:]

def scanString(self, *args):
def scan_string(self, *args):
yield from self.get_parser().scanString(*args)

def scanString(self, *args):
yield from self.scan_string(*args)

def _parse(self, *args, **kwargs):
"""Parses an expression."""
if _get_expression_depth(args[0]) > self.maximum_expression_depth:
Expand Down Expand Up @@ -904,12 +909,16 @@ def __delitem__(self, key):
def __setitem__(self, name, value):
self._variable_map[name] = LiteralNode([value])

def customize(self):
def customize(self) -> None:
"""
Entry point to define operators, functions and variables.
"""

def add_operator(self, operator_expr, arity, assoc, parse_action):
def add_operator(self,
operator_expr: Union[str, pp.ParserElement],
arity: int,
assoc: object,
parse_action: Callable) -> None:
"""
Adds an operator.
Expand Down Expand Up @@ -942,10 +951,10 @@ def add_operator(self, operator_expr, arity, assoc, parse_action):
if isinstance(operator_expr, str) and operator_expr.isalpha():
operator_expr = pp.Keyword(operator_expr, identChars=string.ascii_letters)
self._added_operator_specs.insert(
0, (operator_expr, arity, assoc, operator_node_class)
0, OperatorSpec(operator_expr, arity, assoc, operator_node_class)
)

def initialize_variable(self, vname, vvalue, as_formula=False):
def initialize_variable(self, vname: str, vvalue: Any, as_formula: bool = False) -> None:
"""
Adds a variable to the parser.
Expand All @@ -957,7 +966,7 @@ def initialize_variable(self, vname, vvalue, as_formula=False):
"""
self._initial_variables[vname] = (vvalue, as_formula)

def add_function(self, fn_name, fn_arity, fn_method):
def add_function(self, fn_name: str, fn_arity: Union[int, Tuple[int, int]], fn_method: Callable):
"""
Adds a function to the parser.
Expand All @@ -968,15 +977,15 @@ def add_function(self, fn_name, fn_arity, fn_method):
"""
self._added_function_specs[fn_name] = FunctionSpec(fn_name, fn_method, fn_arity)

def get_parser(self):
def get_parser(self) -> pp.ParserElement:
"""
Retrieves parser or make a new one if None was found.
"""
if self._parser is None:
self._parser = self.make_parser()
return self._parser

def make_parser(self):
def make_parser(self) -> pp.ParserElement:
"""
Creates a new parser.
"""
Expand Down Expand Up @@ -1302,7 +1311,7 @@ def vars(self):
return ret


def log(x, y=math.e):
def log(x: Union[int, float], y: Union[int, float] = math.e) -> float:
"""
Similar to `math.log`, with is_close for bases 2 and 10.
"""
Expand Down

0 comments on commit adc1e4d

Please sign in to comment.