Ejemplo n.º 1
0
 def test_parse_string_None(self):
     input_string = report_filename = None
     with self.assertRaises(TypeError):
         entries, errors, _ = parser.parse_string(input_string)
     with self.assertRaises(TypeError):
         entries, errors, _ = parser.parse_string("something", None,
                                                  report_filename)
Ejemplo n.º 2
0
    def test_run_transformations(self):
        # Test success case.
        entries, errors, options_map = parser.parse_string(TEST_INPUT)
        trans_entries, trans_errors = loader.run_transformations(
            entries, errors, options_map, None)
        self.assertEqual(0, len(trans_errors))

        # Test an invalid plugin name.
        entries, errors, options_map = parser.parse_string(
            'plugin "invalid.module.name"\n\n' + TEST_INPUT)
        trans_entries, trans_errors = loader.run_transformations(
            entries, errors, options_map, None)
        self.assertEqual(1, len(trans_errors))
Ejemplo n.º 3
0
    def test_print_extracted_entries(self):
        entries, error, options = parser.parse_string(
            textwrap.dedent('''
            1970-01-01 * "Test"
              Assets:Tests  10.00 USD'''))

        extracted = [
            ('/path/to/test.csv', entries),
            ('/path/to/empty.pdf', []),
        ]

        output = io.StringIO()
        extract.print_extracted_entries(extracted, output)

        self.assertEqual(
            output.getvalue(),
            textwrap.dedent('''\
            ;; -*- mode: beancount -*-

            **** /path/to/test.csv

            1970-01-01 * "Test"
              Assets:Tests  10.00 USD


            **** /path/to/empty.pdf


            '''))
Ejemplo n.º 4
0
 def test_bytes_encoded_invalid(self):
     latin1_bytes = self.test_latin1_string.encode('latin1')
     entries, errors, _ = parser.parse_string(latin1_bytes,
                                              encoding='garbage')
     self.assertEqual(1, len(errors))
     self.assertRegex(errors[0].message, "unknown encoding")
     self.assertFalse(entries)
Ejemplo n.º 5
0
def read_string_or_entries(entries_or_str):
    """Read a string of entries or just entries.

    Args:
      entries_or_str: Either a list of directives, or a string containing directives.
    Returns:
      A list of directives.
    """
    if isinstance(entries_or_str, str):
        entries, parse_errors, options_map = parser.parse_string(
            textwrap.dedent(entries_or_str))

        # Don't accept incomplete entries either.
        if parser.has_auto_postings(entries):
            raise TestError("Entries in assertions may not use interpolation.")

        entries, booking_errors = booking.book(entries, options_map)
        errors = parse_errors + booking_errors

        # Don't tolerate errors.
        if errors:
            oss = io.StringIO()
            printer.print_errors(errors, file=oss)
            raise TestError("Unexpected errors in expected: {}".format(oss.getvalue()))

    else:
        assert isinstance(entries_or_str, list), "Expecting list: {}".format(entries_or_str)
        entries = entries_or_str

    return entries
Ejemplo n.º 6
0
 def test_bytes_encoded_utf8(self):
     utf8_bytes = self.test_utf8_string.encode('utf8')
     entries, errors, _ = parser.parse_string(utf8_bytes)
     self.assertEqual(1, len(entries))
     self.assertFalse(errors)
     # Check that the lexer correctly parsed the UTF8 string.
     self.assertEqual(self.expected_utf8_string, entries[0].comment)
Ejemplo n.º 7
0
 def test_bytes_encoded_latin1(self):
     latin1_bytes = self.test_latin1_string.encode('latin1')
     entries, errors, _ = parser.parse_string(latin1_bytes, encoding='latin1')
     self.assertEqual(1, len(entries))
     self.assertFalse(errors)
     # Check that the lexer correctly parsed the latin1 string.
     self.assertEqual(self.expected_latin1_string, entries[0].comment)
Ejemplo n.º 8
0
    def test_print_extracted_entries(self, mock_extract_from_file):
        entries, _, __ = parser.parse_string("""

          2016-02-01 * "A"
            Assets:Account1    11.11 USD
            Assets:Account2   -11.11 USD

          2016-02-01 * "B"
            Assets:Account1    22.22 USD
            Assets:Account3   -22.22 USD

        """)
        mock_extract_from_file.return_value = entries

        entries[-2].meta[extract.DUPLICATE_META] = True

        oss = io.StringIO()
        extract.print_extracted_entries(entries, oss)

        self.assertEqual(textwrap.dedent("""\

        ; 2016-02-01 * "A"
        ;   Assets:Account1   11.11 USD
        ;   Assets:Account2  -11.11 USD

        2016-02-01 * "B"
          Assets:Account1   22.22 USD
          Assets:Account3  -22.22 USD

        """).strip(), oss.getvalue().strip())
Ejemplo n.º 9
0
    def test_print_extracted_entries_duplictes(self):
        entries, error, options = parser.parse_string(
            textwrap.dedent('''
            1970-01-01 * "Test"
              Assets:Tests  10.00 USD

            1970-01-01 * "Test"
              Assets:Tests  10.00 USD '''))

        # Mark the second entry as duplicate
        entries[1].meta[extract.DUPLICATE] = True

        extracted = [
            ('/path/to/test.csv', entries),
        ]

        output = io.StringIO()
        extract.print_extracted_entries(extracted, output)

        self.assertEqual(
            output.getvalue(),
            textwrap.dedent('''\
            ;; -*- mode: beancount -*-

            **** /path/to/test.csv

            1970-01-01 * "Test"
              Assets:Tests  10.00 USD

            ; 1970-01-01 * "Test"
            ;   Assets:Tests  10.00 USD


            '''))
Ejemplo n.º 10
0
 def test_bytes_encoded_incorrect(self):
     latin1_bytes = self.test_utf8_string.encode('latin1')
     entries, errors, _ = parser.parse_string(latin1_bytes)
     # Check that the lexer failed to convert the string and reported an error.
     self.assertEqual(1, len(errors))
     self.assertRegex(errors[0].message,
                      "^UnicodeDecodeError: 'utf-8' codec ")
     self.assertFalse(entries)
Ejemplo n.º 11
0
 def test_bytes_encoded_incorrect(self):
     latin1_bytes = self.test_utf8_string.encode('latin1')
     entries, errors, _ = parser.parse_string(latin1_bytes)
     self.assertEqual(1, len(entries))
     self.assertFalse(errors)
     # Check that the lexer failed to convert the string but did not cause
     # other errors.
     self.assertNotEqual(self.expected_utf8_string, entries[0].comment)
Ejemplo n.º 12
0
 def test_import_exception(self):
     # Test an invalid plugin name.
     entries, errors, options_map = parser.parse_string(
         'plugin "invalid.module.name"\n\n' + TEST_INPUT)
     trans_entries, trans_errors = loader.run_transformations(
         entries, errors, options_map, None)
     self.assertEqual(1, len(trans_errors))
     self.assertRegex(trans_errors[0].message, "ModuleNotFoundError")
Ejemplo n.º 13
0
 def test_parse_None(self):
     # None is treated as the empty string...
     entries, errors, _ = parser.parse_string(None)
     self.assertEqual(0, len(entries))
     self.assertEqual(0, len(errors))
     # ...however None in not a valid file like object
     with self.assertRaises(TypeError):
         entries, errors, _ = parser.parse_file(None)
Ejemplo n.º 14
0
 def test_extract_with_balance_declared(self):
     soup, exp_entries = self._extract_with_balance()
     entries = ofximp.extract(soup, 'test.ofx', '379700001111222',
                              'Liabilities:CreditCard', '*',
                              ofximp.BalanceType.DECLARED)
     balance_entries, _, __ = parser.parse_string("""
       2014-01-13 balance Liabilities:CreditCard            -2356.38 USD
     """)
     self.assertEqualEntries(exp_entries + balance_entries, entries)
Ejemplo n.º 15
0
 def test_two_distinct_balances(self):
     ofx_contents = clean_xml("""
       <OFX>
        <SIGNONMSGSRSV1>
        </SIGNONMSGSRSV1>
        <CREDITCARDMSGSRSV1>
         <STMTTRNRS>
          <TRNUID>0
           <STMTRS>
            <CURDEF>USD
             <ACCTFROM>
              <ACCTID>379700001111222
              </ACCTID>
             </ACCTFROM>
             <BANKTRANLIST>
             </BANKTRANLIST>
             <LEDGERBAL>
              <BALAMT>100.00
               <DTASOF>20140101000000.000[-7:MST]</DTASOF>
              </BALAMT>
             </LEDGERBAL>
            </CURDEF>
           </STMTRS>
          </TRNUID>
         </STMTTRNRS>
         <CCSTMTTRNRS>
          <TRNUID>0
           <CCSTMTRS>
            <CURDEF>USD
             <CCACCTFROM>
              <ACCTID>379700001111222
              </ACCTID>
             </CCACCTFROM>
             <BANKTRANLIST>
             </BANKTRANLIST>
             <LEDGERBAL>
              <BALAMT>200.00
               <DTASOF>20140102000000.000[-7:MST]</DTASOF>
              </BALAMT>
             </LEDGERBAL>
            </CURDEF>
           </CCSTMTRS>
          </TRNUID>
         </CCSTMTTRNRS>
        </CREDITCARDMSGSRSV1>
       </OFX>
     """)
     soup = bs4.BeautifulSoup(ofx_contents, 'lxml')
     entries = ofx.extract(soup, 'test.ofx', '379700001111222',
                           'Liabilities:CreditCard', '*',
                           ofx.BalanceType.DECLARED)
     balance_entries, _, __ = parser.parse_string("""
       2014-01-02 balance Liabilities:CreditCard   100.00 USD
       2014-01-03 balance Liabilities:CreditCard   200.00 USD
     """,
                                                  dedent=True)
     self.assertEqualEntries(balance_entries, entries)
Ejemplo n.º 16
0
 def test_extract_with_balance_last(self):
     soup, exp_entries = self._extract_with_balance()
     entries = ofx.extract(soup, 'test.ofx', '379700001111222',
                           'Liabilities:CreditCard', '*',
                           ofx.BalanceType.LAST)
     balance_entries, _, __ = parser.parse_string("""
       2013-11-27 balance Liabilities:CreditCard            -2356.38 USD
     """)
     self.assertEqualEntries(exp_entries + balance_entries, entries)
Ejemplo n.º 17
0
 def test_run_transformation_exception(self):
     # Test another exception occurring during import.
     entries, errors, options_map = parser.parse_string(
         'plugin "failing"\n\n' + TEST_INPUT)
     loader.run_transformations(entries, errors, options_map, None)
     trans_entries, trans_errors = loader.run_transformations(
         entries, errors, options_map, None)
     self.assertEqual(1, len(trans_errors))
     self.assertRegex(trans_errors[0].message, "ValueError")
Ejemplo n.º 18
0
def deserialise_posting(posting):
    """Parse JSON to a Beancount Posting."""
    amount = posting.get("amount", "")
    entries, errors, _ = parse_string(
        '2000-01-01 * "" ""\n Assets:Account {}'.format(amount))
    if errors:
        raise FavaAPIException("Invalid amount: {}".format(amount))
    pos = entries[0].postings[0]
    return pos._replace(account=posting["account"], meta=None)
Ejemplo n.º 19
0
 def test_find_duplicate_entries(self):
     entries, error, options = parser.parse_string(
         textwrap.dedent('''
         1970-01-01 * "Test"
           Assets:Tests  10.00 USD'''))
     extracted = [
         ('/path/to/test.csv', entries),
     ]
     marked = extract.find_duplicate_entries(extracted, entries)
     self.assertTrue(marked[0][1][0].meta[extract.DUPLICATE])
Ejemplo n.º 20
0
    def test_has_auto_postings(self):
        entries, _, __ = parser.parse_string("""

          2014-01-27 * "UNION MARKET"
            Liabilities:US:Amex:BlueCash    -22.02 USD
            Expenses:Food:Grocery            22.02 USD

        """,
                                             dedent=True)
        self.assertFalse(parser.has_auto_postings(entries))

        entries, _, __ = parser.parse_string("""

          2014-01-27 * "UNION MARKET"
            Liabilities:US:Amex:BlueCash    -22.02 USD
            Expenses:Food:Grocery

        """,
                                             dedent=True)
        self.assertTrue(parser.has_auto_postings(entries))
Ejemplo n.º 21
0
def deserialise_posting(posting: Any) -> Posting:
    """Parse JSON to a Beancount Posting."""
    amount = posting.get("amount", "")
    entries, errors, _ = parse_string(
        f'2000-01-01 * "" ""\n Assets:Account {amount}')
    if errors:
        raise FavaAPIException(f"Invalid amount: {amount}")
    txn = entries[0]
    assert isinstance(txn, Transaction)
    pos = txn.postings[0]
    return pos._replace(account=posting["account"], meta=None)
Ejemplo n.º 22
0
    def test_extract_from_file_ensure_sanity(self):
        entries, errors, options = parser.parse_string('''
            1970-01-01 * "Test"
              Assets:Tests  1.00 USD
            ''')

        # Break something.
        entries[-1] = entries[-1]._replace(narration=42)
        importer = mock.MagicMock(wraps=self.importer)
        importer.extract.return_value = entries
        with self.assertRaises(AssertionError):
            extract.extract_from_file(importer, path.abspath('test.csv'), [])
Ejemplo n.º 23
0
def _load_testset(testset):
    path = os.path.join(os.path.dirname(__file__), "data",
                        testset + ".beancount")
    with open(path, "r") as test_file:
        _, *sections = re.split(r"# [A-Z]+\n", test_file.read())
    parsed_sections = []
    for section in sections:
        entries, errors, __ = parser.parse_string(section)
        assert not errors
        parsed_sections.append(entries)
    assert len(parsed_sections) == 3
    return parsed_sections
Ejemplo n.º 24
0
    def get_incomplete_entry(self, string):
        """Parse an incomplete entry and convert its LotSpec representation to a Lot.

        Args:
          string: The input string to parse.
        Returns:
          A pair of (entry, list of errors).
        """
        entries, _, options_map = parser.parse_string(string, dedent=True)
        entries_with_lots, errors = booking.convert_lot_specs_to_lots(entries, options_map)
        entry = entries_with_lots[0]
        errors = interpolate.balance_incomplete_postings(entry, options_map)
        return entry, errors
Ejemplo n.º 25
0
    def test_simple_interpolation(self):
        entries, _, options_map = parser.parse_string("""
          2013-05-01 open Assets:Bank:Investing
          2013-05-01 open Equity:Opening-Balances

          2013-05-02 *
            Assets:Bank:Investing                 5 HOOL {501 USD}
            Equity:Opening-Balances
        """)
        interpolated_entries, errors = booking.simple_interpolation(
            entries, options_map)
        self.assertFalse(errors)
        self.assertEqual(D('-2505'),
                         interpolated_entries[-1].postings[-1].position.number)
Ejemplo n.º 26
0
    def test_is_entry_incomplete(self):
        entries, _, __ = parser.parse_string("""

          2014-01-27 * "UNION MARKET"
            Liabilities:US:Amex:BlueCash    -22.02 USD
            Expenses:Food:Grocery            22.02 USD

          2014-01-27 * "UNION MARKET"
            Liabilities:US:Amex:BlueCash    -22.02 USD
            Expenses:Food:Grocery

        """, dedent=True)
        self.assertFalse(parser.is_entry_incomplete(entries[0]))
        self.assertTrue(parser.is_entry_incomplete(entries[1]))
Ejemplo n.º 27
0
    def get_incomplete_entry(self, string):
        """Parse a single incomplete entry and convert its CostSpec to a Cost.

        Args:
          string: The input string to parse.
        Returns:
          A pair of (entry, list of errors).
        """
        entries, errors, options_map = parser.parse_string(string, dedent=True)
        self.assertFalse(errors)
        self.assertEqual(1, len(entries))
        (entries_with_lots,
         errors) = booking_simple.convert_lot_specs_to_lots(entries)
        self.assertEqual(1, len(entries))
        entry = entries_with_lots[0]
        errors = booking_simple.balance_incomplete_postings(entry, options_map)
        return entry, errors
Ejemplo n.º 28
0
    def test_extract_from_file(self):
        entries, errors, options = parser.parse_string(
            textwrap.dedent('''
            1970-01-03 * "Test"
              Assets:Tests  1.00 USD

            1970-01-01 * "Test"
              Assets:Tests  1.00 USD

            1970-01-02 * "Test"
              Assets:Tests  1.00 USD
            '''))

        importer = mock.MagicMock(wraps=self.importer)
        importer.extract.return_value = entries
        entries = extract.extract_from_file(importer, path.abspath('test.csv'),
                                            [])
        dates = [entry.date for entry in entries]
        self.assertSequenceEqual(dates, sorted(dates))
Ejemplo n.º 29
0
 def test_validate__use_legacy_fixed_tolerances(self):
     for input_value, expected_value in [
         ('TRUE', True),
         ('True', True),
         ('true', True),
         ('1', True),
         ('42', False),
         ('FALSE', False),
         ('False', False),
         ('false', False),
         ('0', False),
         ('something', False),
         ('other', False),
     ]:
         input_str = """
           option "use_legacy_fixed_tolerances" "{}"
         """.format(input_value)
         _, errors, options_map = parser.parse_string(input_str)
         self.assertFalse(errors)
         self.assertEqual(expected_value,
                          options_map['use_legacy_fixed_tolerances'])
Ejemplo n.º 30
0
def read_string_or_entries(entries_or_str, allow_incomplete=False):
    """Read a string of entries or just entries.

    Args:
      entries_or_str: Either a list of directives, or a string containing directives.
      allow_incomplete: A boolean, true if we allow incomplete inputs and perform
        light-weight booking.
    Returns:
      A list of directives.
    """
    if isinstance(entries_or_str, str):
        entries, errors, options_map = parser.parse_string(
            textwrap.dedent(entries_or_str))

        if allow_incomplete:
            # Do a simplistic local conversion in order to call the comparison.
            entries = [_local_booking(entry) for entry in entries]
        else:
            # Don't accept incomplete entries either.
            if any(parser.is_entry_incomplete(entry) for entry in entries):
                raise TestError(
                    "Entries in assertions may not use interpolation.")

            entries, booking_errors = booking.book(entries, options_map)
            errors = errors + booking_errors

        # Don't tolerate errors.
        if errors:
            oss = io.StringIO()
            printer.print_errors(errors, file=oss)
            raise TestError("Unexpected errors in expected: {}".format(
                oss.getvalue()))

    else:
        assert isinstance(entries_or_str,
                          list), "Expecting list: {}".format(entries_or_str)
        entries = entries_or_str

    return entries
Ejemplo n.º 31
0
def get_budgets(beancount_string):
    entries, errors, options_map = parser.parse_string(beancount_string,
                                                       dedent=True)
    return Budgets(entries)