template: Improve error detection and reporting in amount expressions.
This commit is contained in:
		
							parent
							
								
									e3335aa6ec
								
							
						
					
					
						commit
						a1f815c60b
					
				
					 2 changed files with 37 additions and 4 deletions
				
			
		| 
						 | 
				
			
			@ -52,7 +52,15 @@ class TokenTransformer:
 | 
			
		|||
class AmountTokenTransformer(TokenTransformer):
 | 
			
		||||
    SUPPORTED_OPS = frozenset('+-*/()')
 | 
			
		||||
 | 
			
		||||
    transform_NAME = TokenTransformer._noop_transformer
 | 
			
		||||
    def __iter__(self):
 | 
			
		||||
        tokens = super().__iter__()
 | 
			
		||||
        for token in tokens:
 | 
			
		||||
            yield token
 | 
			
		||||
            if token[0] == tokenize.NAME:
 | 
			
		||||
                break
 | 
			
		||||
        else:
 | 
			
		||||
            raise ValueError("no amount in expression")
 | 
			
		||||
        yield from tokens
 | 
			
		||||
 | 
			
		||||
    def transform_NUMBER(self, ttype, tvalue):
 | 
			
		||||
        yield (tokenize.NAME, 'Decimal')
 | 
			
		||||
| 
						 | 
				
			
			@ -66,6 +74,7 @@ class AmountTokenTransformer(TokenTransformer):
 | 
			
		|||
                name_type, name_value, _, _, _ = next(self.in_tokens)
 | 
			
		||||
                close_type, close_value, _, _, _ = next(self.in_tokens)
 | 
			
		||||
                if (name_type != tokenize.NAME
 | 
			
		||||
                    or name_value != name_value.lower()
 | 
			
		||||
                    or close_type != tokenize.OP
 | 
			
		||||
                    or close_value != '}'):
 | 
			
		||||
                    raise ValueError()
 | 
			
		||||
| 
						 | 
				
			
			@ -91,9 +100,11 @@ class AccountSplitter:
 | 
			
		|||
    def add(self, account, amount_expr):
 | 
			
		||||
        try:
 | 
			
		||||
            clean_expr = AmountTokenTransformer.from_str(amount_expr).transform()
 | 
			
		||||
        except ValueError as error:
 | 
			
		||||
            compiled_expr = compile(clean_expr, self.template_name, 'eval')
 | 
			
		||||
        except (SyntaxError, tokenize.TokenError, ValueError) as error:
 | 
			
		||||
            raise errors.UserInputConfigurationError(error.args[0], amount_expr)
 | 
			
		||||
        self.splits[account] = compile(clean_expr, self.template_name, 'eval')
 | 
			
		||||
        else:
 | 
			
		||||
            self.splits[account] = compiled_expr
 | 
			
		||||
 | 
			
		||||
    def _currency_decimal(self, amount, currency):
 | 
			
		||||
        return decimal.Decimal(babel.numbers.format_currency(amount, currency, '###0.###'))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ import decimal
 | 
			
		|||
import pathlib
 | 
			
		||||
 | 
			
		||||
import pytest
 | 
			
		||||
from import2ledger import template
 | 
			
		||||
from import2ledger import errors, template
 | 
			
		||||
 | 
			
		||||
from . import DATA_DIR, normalize_whitespace
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -87,3 +87,25 @@ def test_multivalue():
 | 
			
		|||
        "  Income:RBI  -15.00 USD",
 | 
			
		||||
        "  Income:Donations  -135.00 USD",
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('amount_expr', [
 | 
			
		||||
    '',
 | 
			
		||||
    'name',
 | 
			
		||||
    '-',
 | 
			
		||||
    '()',
 | 
			
		||||
    '+()',
 | 
			
		||||
    '{}',
 | 
			
		||||
    '{{}}',
 | 
			
		||||
    '{()}',
 | 
			
		||||
    '{name',
 | 
			
		||||
    'name}',
 | 
			
		||||
    '{42}',
 | 
			
		||||
    '(5).real',
 | 
			
		||||
    '{amount.real}',
 | 
			
		||||
    '{amount.is_nan()}',
 | 
			
		||||
    '{Decimal}',
 | 
			
		||||
    '{FOO}',
 | 
			
		||||
])
 | 
			
		||||
def test_bad_amount_expression(amount_expr):
 | 
			
		||||
    with pytest.raises(errors.UserInputError):
 | 
			
		||||
        template.Template(" Income  " + amount_expr)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue