TL;DR: Not many user-visible changes (well, arithmetic operations support),
but lots of internal changes to prepare to implement fancy inventory
booking methods.
2015-08-30
- Fixed a very minor bug in split_expenses plugin whereby the generated
postings did not contain the __automatic__ metadata field, and in some
particular situations, their automatically calculated values would end
up
being used for inferring the tolerances.
2015-08-15
- Changed the semantics of the parsing stage, in a fairly profound way.
This should have no visible changes to users, but people writing scripts
should revise their code if they were using
beancount.parser.parser.parse_*() functions.
Just to be clear: beancount.loader.load_*() has not changed. If you
just use
the loader, there are no changes. Changes are only at the parser level.
Here's what's going on and why: The parser used to carry out
interpolation
of missing numbers on postings for each transaction locally, while
parsing
the transactions. This was done by calling by calling
beancount.core.interpolate.balance_incomplete_postings(), here:
https://bitbucket.org/blais/beancount/src/ee2073aae080aaa8e260abe8a501abf872948f0e/src/python/beancount/parser/grammar.py?at=default#grammar.py-803
Loading a list of entries was carried out in two steps:
,-----------------------load----------------------.
(recursively)
,---------------------. ,---------. ,------------.
(input)--->| parse + interpolate |-->| plugins |-->| validation |-->
entries
`---------------------' `---------' `------------'
First, the parser would run on the input and process all the input files
recursively (processing includes). "Interpolation", the process of
filling
in missing numbers, was carried out at that stage, during parsing, and
only
locally, that is, for each transaction in isolation. "Booking" of lots,
that
is, selecting which of an account's inventory lots to match and reduce,
was
explicit. This booking could not take advantage of the accumulated
inventories in order to vary its behavior. You had to specify the
entire lot
information unambiguously.
After this, in a second stage, the plugins were run on the entries and a
final validation step was run at the end.
To implement the booking proposal
(http://furius.ca/beancount/doc/proposal-booking), we want for the user
to
be able to provide a partial specification of lots to be matched
against an
account's accumulated inventory at the date the transaction is to be
applied. The idea is that if there is no ambiguity, the user should be
able
to specify very little information about a lot (for example if there is
a
single lot in the account when processing the transaction an empty spec
of
"{}" should be sufficient). Moreover, where the specification is
ambiguous,
we also want to support automatic selection of lots according a method
specified by the user, e.g., FIFO booking, LIFO booking, etc.
For this to work, we need to have parsed all the inputs to some sort of
incomplete specification, a representation of inputs that hasn't yet
been
resolved to specific lots, in order to carry out booking. The parser has
been modified to output such incomplete postings:
,-------------------------load-------------------------.
(recursively)
,-------. ,-------------. ,---------. ,------------.
(input)--->| parse |-->| booking |-->| plugins |-->| validation
|--> entries
`-------' | + | `---------' `------------'
| interpolate |
`-------------'
incomplete
entries
Because "interpolation" runs on the result of specific lot values,
"booking"
must run before it, and so they are inter-related. Thus, booking and
inteprolation has been moved to a dedicated step that runs on the list
of
incomplete entries and resolves them to the regular entries to be
processed
further by plugins and validation.
This also has a nice side-effect: the booking step is where all the
complexity is, and it is now isolated and I will be able to test and
experiment on it in isolation. This is where all the fun will be.
A description of the incomplete specifications output by the parser can
be
found here in the parser.py file, this is the description of the
intermediate state of postings whose lots haven't yet been matched and
resolved to specific inventory lots:
https://bitbucket.org/blais/beancount/src/18282452e265959b69d3d10c6d9cf32e5815c522/src/python/beancount/parser/parser.py?at=booking
Essentially, a posting's 'lot' attribute contains a "LotSpec" tuple
instead
of a "Lot" tuple, and several numbers may be left unfilled (for
interpolate
values). I don't imagine anyone will ever have to manipulate such
intermediate entries, only the booking code.
The previous booking algorithm has been moved to the booking stage and
the
semantics should be identical to what they used to be. This is still the
default algorithm--it just runs in its own dedicated stage, still
operating
locally on each transation. You should observe no difference in
behaviour.
I've merged these changes now in order to minimize the differences
between
the booking and default branches and because I was able to do it without
changing any of the semantics (despite the large number of lines and
tests
modified). This was a necessary refactoring. Because that code is now
isolated to its own stage I should be able to begin implementing the
more
complex state-dependent booking algorithms in the 'booking' branch. (I'm
excited about this.) I could even implement different booking
heuristics and
switch between them.
Because the parser used to spit out regular, complete entries,
parser.parsedoc() was used in much of the tests instead of
loader.loaddoc(),
so that many of those tests would not have to concern themselves with
making
sure the input passed the validation stage run by the loader. For
example,
creating Open directives just to create some Transaction test object
wasn't
necessary in the tests. All of those tests had to be revised and I made
them
all depend on beancount.loader.load_*() instead of the now weakened
parser.parse_*() functions which output incomplete, unbooked entries.
This
cleans up the dependencies a bit as well. If you wrote your own unit
tests
and were using parser.parsedoc(), you should convert them to use
loader.loaddoc(). In order for this change not to go unnoticed (and for
naming consistency with parse_string() and parse_file()) I'll probably
rename parser.parsedoc() to parser.parse_doc() and ditto with the
loader.
(Note: In some cases I've had to specifically setup the input of some
tests
in "raw" plugin processing mode to avoid triggering some unwanted and
unrelated errors. I'm tempted to remove even more from the default
plugins.
This may happen in a future CL.)
- Renamed beancount.parser.parser.parsedoc() to parse_doc() and
beancount.loader.loaddoc() to load_doc(), for consistency with the other
parse_*() and load_*() functions. Kept a stub that will issue a warning
if
you use it.
2015-07-23
- In the conversion to the new booking syntax, I had inadventently removed
support for the total cost "{{ ... }}" syntax. I brought this back next
to
the new booking syntax, and uncommented tests that had been made to
skip.
2015-07-21
- Merged ongoing work from the 'booking' branch that will eventually
change
the semantics of inventory booking; in the interest of a smooth
transition,
and for me to be able to use either branch interchangeably, I
introduced a
few changes that should have no user-visible effect on 'default':
* The parser.parse_string() and parser.parse_file() routines don't
interpolate anymore. For this reason, the tests all had to be
adjusted not
to include interpolation. The interpolation of incomplete postings
(grammar.interpolation()) has moved to a separate phase and is now
run by
loader._load() *AFTER* parsing. This is key to implementing fuzzy
matching
semantics for matching reducing lots: we need to have all the
incomplete
transactions parsed and sorted in order to select matching lots in
date
order.
This only affects you if you wrote scripts against the parser
interface directly (this is highly unlikely).
* For writing unit tests, parser.parsedoc() and loader.loaddoc() are now
decorator factories. This allowed me to add options to
parser.parsedoc()
to perform interpolation, and when not specified, to check that
entries
with interpolation are not present in the tests. Also, I coudl merge
the
functionality of parser.parsedoc_noerrors() and
loader.loaddoc_noerrors()
in their respective equivalents. The docstring tests now validate that
there are no errors in the docstrings by default. This makes the tests
tighter (a few bugs in the tests themselves were found and fixed).
* The new syntax for cost specification that will be in effect for the
inventory booking proposal is now supported. The syntax is
backward-compatible with the previous one. Previously, the following
syntaxes were supported for specifying the cost and optionally a lot
acquisition date:
Assets:Investments 1 HOOL {123.00 USD}
Assets:Investments 1 HOOL {123.00 USD / 2013-07-20}
Instead of these options, the new syntax supports a comma-separated
list
of cost-specifiers which can be one of
<cost> -> e.g. 123.00 USD
<lot-date> -> e.g. 2015-07-20
<label> -> e.g. "first-lot"
<merge-cost> -> e.g. *
In order to keep current input working, either a comma (,) or a slash
(/)
is supported to separate the components. This is valid:
Assets:Investments 1 HOOL {2013-07-20 / 123.00 USD / "first-lot"}
Those can be provided in any order. For example, these are all also
valid
syntaxes for cost:
Assets:Investments 1 HOOL {}
Assets:Investments 1 HOOL {"first-lot"}
Assets:Investments 1 HOOL {2013-07-20, 123.00 USD}
Assets:Investments 1 HOOL {2013-07-20, "first-lot"}
Assets:Investments 1 HOOL {*}
Assets:Investments 1 HOOL {*, 123.00 USD}
Moreover, the cost amount now supports a compound amount that is
expressed
not in terms of each unit of the currency, but in terms of the total
amount over all units, for example, this is how you could fold in the
cost
of a commission:
Assets:Investments 1 HOOL {123.00 # 9.95 USD}
The syntax for a compound amount follows this pattern:
[<per-unit-cost>] # [<total-cost>] <currency>
The numbers are both optional. If no '#' separator is present, the
total
cost component is assumed to be zero. This will eventually subsume the
{{...}} total cost syntax, by specifying only the total cost portion
of
the compound amount:
Assets:Investments 100 HOOL {# 12300.00 USD}
Of course, this combines with the other spec formats, so this is
valid:
Assets:Investments 1 HOOL {123.00 # 9.95 USD, 2015-07-22}
Finally, while the new syntax is supported in the parser, the old
semantics for inventory lot specification is still in order. If you
provide an unsupported combination of lot specifiers (e.g., you use a
label, a compound amount, or a merge-cost marker), an error will be
issued
accordingly.
So just use the cost and date as previously; I will bring in new
semantics
incrementally, semantics that will take advantage of this new syntax.
I
will try to do so in a way that minimizes changes.
- I removed forwarded symbols from beancount.parser. Generally, in order
to be
able to mock functions, you should always import packages, not symbols.
2015-07-20
- Numbers rendered from bean-query are now rendered using the display
context
inferred from the input file. This means numbers are rounded nicely,
using
the most common precision seen in the input file.
- I fixed a bug on writing UTF-8 output to the console on Mac OS X and
how it
interacts with the 'less' pager.
2015-07-12
- Implemented support for arithmetic operations: +, -, *, / and
parenthesis
groupings are now supported, anywhere that a number can be seen in the
input
file, including postings, costs & prices, and balance numbers.
--
---
You received this message because you are subscribed to the Google Groups
"Ledger" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.