From 7d63a9aaeb5ea645bce5e1f1836349edee891ac1 Mon Sep 17 00:00:00 2001 From: Aristid Breitkreuz Date: Thu, 19 Nov 2009 17:39:34 +0100 Subject: Some improvements to README.textile --- README.textile | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/README.textile b/README.textile index 50e15244..29f34d40 100644 --- a/README.textile +++ b/README.textile @@ -96,19 +96,32 @@ the following packages (current as of Ubuntu Hardy):
 sudo apt-get install build-essential libtool autoconf automake   \
      texinfo python-dev zlib1g-dev libbz2-dev stow libgmp3-dev   \
-     bjam libboost1.35-dev libboost-regex1.35-dev                \
-     libboost-date-time1.35-dev libboost-filesystem1.35-dev
+     bjam gettext cvs libboost1.35-dev libboost-regex1.35-dev    \
+     libboost-date-time1.35-dev libboost-filesystem1.35-dev      \
+     libmpfr-dev
 
-h2. Preparing the Build +Or for Ubuntu Karmic: + +
+sudo apt-get install build-essential libtool autoconf automake   \
+     texinfo python-dev zlib1g-dev libbz2-dev stow libgmp3-dev   \
+     bjam gettext cvs libboost1.40-dev libboost-regex1.40-dev    \
+     libboost-date-time1.40-dev libboost-filesystem1.40-dev      \
+     libmpfr-dev
+
+ +h2. Building The next step is preparing your environment for building. While you can use -@autogen.sh@, I've prepared a script that does a lot more of the footwork for -you: +@autogen.sh@, ./configure and make, I've prepared a script that does a lot more +of the footwork for you:
-./acprep pull       # Make sure everything is pulled that needs to be
-./acprep
+./acprep update
+# or, if you want to use the Boost libraries with suffix -mt, install in
+# $HOME/local and build with 2 processes in parallel
+./acprep update --boost=-mt -- --prefix=$HOME/local -j2
 
Please read the contents of @config.log@ if the configure step fails. Also, @@ -116,10 +129,7 @@ see the @help@ command to @acprep@, which explains some of its many options. It's pretty much the only command I run for configuring, building and testing Ledger. -h2. Building - -Once you have the dependencies installed and the source prepared for building, -run @make check@ to get things started and confirm the result. +You can run @make check@ to confirm the result, and @make install@ to install. If you have extra CPU cycles to burn, try @./acprep proof@, which provides the most thorough shakedown of a healthy source tree. -- cgit v1.2.3 From 6e31328bd62d15e8da4238394abd8de65539cfa2 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 19 Nov 2009 16:47:46 -0500 Subject: Updated system dependencies lists --- README.textile | 45 +++++++++++++++--------- acprep | 109 ++++++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 112 insertions(+), 42 deletions(-) diff --git a/README.textile b/README.textile index 29f34d40..2a730746 100644 --- a/README.textile +++ b/README.textile @@ -45,17 +45,24 @@ much further with those. h2. Dependencies -If you wish to proceed in this venture, you'll need a few dependencies: +If you wish to proceed in this venture, you'll need a few dependencies. The +easiest way to get them for your platform is to run: + +
+./acprep dependencies
+
+ +If that doesn't completely work, read on. h3. For building the current master branch |_.Library|_.Min.Ver.|_.When needed| -|Boost|1.35 or higher|| +|Boost|1.35|| |GMP|4.2.2|| |MPFR|2.4.0|| |gettext|0.17|_optional_| |libedit|20090111-3.0|_optional_| -|Python|2.4 or higher|_optional_| +|Python|2.4|_optional_| |cppunit|1.12.1|_optional_, for @make check@| |doxygen|1.5.7.1|_optional_, for @make docs@| |graphviz|2.20.3|_optional_, for @make docs@| @@ -63,7 +70,7 @@ h3. For building the current master branch |lcov|1.6|_optional_, for @make report@, used with @./acprep gcov@| |sloccount|2.26|_optional_, for @make sloc@| -h3. For building the beta or release branches +h3. For building the current @maint@ branch |_.Library|_.Min.Ver.|_.When needed| |GMP|4.2.2|| @@ -74,12 +81,16 @@ h3. For building the beta or release branches h3. MacPorts -If you build stuff using MacPorts, as I do, here is what you would run: +If you build stuff using MacPorts on OS X, as I do, here is what you would +run:
-sudo port install boost +python25+st gmp mpfr gettext libedit \
-     cppunit texlive doxygen graphviz texinfo lcov            \
-     sloccount pcre libofx expat
+sudo port install -f automake autoconf libtool python26
+    libiconv +universal zlib +universal gmp +universal
+    mpfr +universal ncurses +universal ncursesw +universal
+    gettext +universal libedit +universal boost-jam
+    boost +st+python26+icu cppunit texlive doxygen graphviz
+    texinfo lcov sloccount
 
You can even just install the current Ledger *RELEASE* directly: @@ -90,22 +101,22 @@ sudo port install ledger h3. Ubuntu -If you're going to be build on Ubuntu, @sudo apt-get install ...@ -the following packages (current as of Ubuntu Hardy): +If you're going to be build on Ubuntu, @sudo apt-get install ...@ the +following packages (current as of Ubuntu Hardy):
-sudo apt-get install build-essential libtool autoconf automake   \
-     texinfo python-dev zlib1g-dev libbz2-dev stow libgmp3-dev   \
-     bjam gettext cvs libboost1.35-dev libboost-regex1.35-dev    \
-     libboost-date-time1.35-dev libboost-filesystem1.35-dev      \
-     libmpfr-dev
+sudo apt-get install build-essential libtool autoconf automake	   \
+     zlib1g-dev libbz2-dev python-dev bjam cvs gettext libgmp3-dev \
+     libmpfr-dev libboost1.35-dev libboost-regex1.35-dev	   \
+     libboost-date-time1.35-dev libboost-filesystem1.35-dev	   \
+     libboost-python1.35-dev texinfo lcov sloccount
 
-Or for Ubuntu Karmic: +Or, for Ubuntu Karmic:
 sudo apt-get install build-essential libtool autoconf automake   \
-     texinfo python-dev zlib1g-dev libbz2-dev stow libgmp3-dev   \
+     texinfo python-dev zlib1g-dev libbz2-dev libgmp3-dev        \
      bjam gettext cvs libboost1.40-dev libboost-regex1.40-dev    \
      libboost-date-time1.40-dev libboost-filesystem1.40-dev      \
      libmpfr-dev
diff --git a/acprep b/acprep
index bf582cf5..f188a4a8 100755
--- a/acprep
+++ b/acprep
@@ -551,14 +551,27 @@ class PrepareBuild(CommandLineApp):
                 self.log.info('Looks like you are using MacPorts on OS X')
                 packages = [
                     'sudo', 'port', 'install', '-f',
-                    'automake', 'autoconf', 'libtool', 'python26',
-                    'libiconv', '+universal', 'zlib', '+universal',
-                    'gmp' ,'+universal', 'mpfr', '+universal',
-                    'ncurses', '+universal', 'ncursesw', '+universal',
-                    'gettext' ,'+universal', 'libedit' ,'+universal',
-                     'boost-jam', 'boost', '+st+python26+icu', 'cppunit',
-                    'texlive', 'doxygen', 'graphviz', 'texinfo',
-                    'lcov', 'sloccount'
+                    'automake',
+                    'autoconf',
+                    'libtool',
+                    'python26',
+                    'libiconv', '+universal',
+                    'zlib', '+universal',
+                    'gmp' ,'+universal',
+                    'mpfr', '+universal',
+                    'ncurses', '+universal',
+                    'ncursesw', '+universal',
+                    'gettext' ,'+universal',
+                    'libedit' ,'+universal',
+                     'boost-jam',
+                    'boost', '+st+python26+icu',
+                    'cppunit',
+                    #'texlive',
+                    #'doxygen',
+                    #'graphviz',
+                    'texinfo',
+                    'lcov',
+                    'sloccount'
                 ]
                 self.log.info('Executing: ' + string.join(packages, ' '))
                 self.execute(*packages)
@@ -570,23 +583,69 @@ class PrepareBuild(CommandLineApp):
             if exists('/etc/issue'):
                 issue = open('/etc/issue')
                 if issue.readline().startswith('Ubuntu'):
-                    self.log.info('Looks like you are using APT on Ubuntu')
-                    packages = [
-                        'sudo', 'apt-get', 'install',
-                         'build-essential',
-                         'libtool', 'autoconf', 'automake',
-                         'zlib1g-dev', 'libbz2-dev', 'python-dev',
-                         'libboost1.35-dev',
-                         'libboost-python1.35-dev',
-                         'libboost-regex1.35-dev',
-                         'libboost-date-time1.35-dev',
-                         'libboost-filesystem1.35-dev'
-                         'libgmp3-dev', 'libmpfr-dev', 'gettext',
-                         'libedit-dev', 'libcppunit-dev',
-                         #'texlive-full',
-                         #'doxygen', 'graphviz', 'texinfo',
-                         'lcov', 'sloccount'
-                    ]
+                    release = open('/etc/lsb-release')
+                    info = release.read()
+                    release.close()
+                    if re.search('karmic', info):
+                        self.log.info('Looks like you are using APT on Ubuntu Karmic')
+                        packages = [
+                            'sudo', 'apt-get', 'install',
+                            'build-essential',
+                            'libtool',
+                            'autoconf',
+                            'automake',
+                            'zlib1g-dev',
+                            'libbz2-dev',
+                            'python-dev',
+                            'libgmp3-dev',
+                            'libmpfr-dev',
+                            'bjam',
+                            'gettext',
+                            'cvs',
+                            'libboost1.40-dev',
+                            'libboost-regex1.40-dev',
+                            'libboost-date-time1.40-dev',
+                            'libboost-filesystem1.40-dev'
+                            'libboost-python1.40-dev',
+                            'libedit-dev',
+                            'libcppunit-dev',
+                            #'texlive-full',
+                            #'doxygen',
+                            #'graphviz',
+                            'texinfo',
+                            'lcov',
+                            'sloccount'
+                        ]
+                    else:
+                        self.log.info('Looks like you are using APT on Ubuntu Hardy')
+                        packages = [
+                            'sudo', 'apt-get', 'install',
+                            'build-essential',
+                            'libtool',
+                            'autoconf',
+                            'automake',
+                            'zlib1g-dev',
+                            'libbz2-dev',
+                            'python-dev',
+                            'bjam',
+                            'cvs',
+                            'gettext',
+                            'libgmp3-dev',
+                            'libmpfr-dev',
+                            'libboost1.35-dev',
+                            'libboost-python1.35-dev',
+                            'libboost-regex1.35-dev',
+                            'libboost-date-time1.35-dev',
+                            'libboost-filesystem1.35-dev'
+                            'libedit-dev',
+                            'libcppunit-dev',
+                            #'texlive-full',
+                            #'doxygen',
+                            #'graphviz',
+                            'texinfo',
+                            'lcov',
+                            'sloccount'
+                        ]
                     self.log.info('Executing: ' + string.join(packages, ' '))
                     self.execute(*packages)
 
-- 
cgit v1.2.3


From 4e30fcdf4094a0c450cbe1918c2e12dd19eb58f2 Mon Sep 17 00:00:00 2001
From: John Wiegley 
Date: Thu, 19 Nov 2009 21:53:02 -0500
Subject: Many improvements to Ledger's Python bindings

---
 python/demo.py             | 120 +++++++++++++++++++++++++++
 src/amount.cc              |  54 ++++++------
 src/amount.h               |   6 +-
 src/annotate.cc            |   4 +-
 src/balance.cc             |   6 +-
 src/commodity.cc           |  10 +--
 src/commodity.h            |   2 +-
 src/journal.cc             |  16 ----
 src/journal.h              |   3 -
 src/op.cc                  |   2 +-
 src/pool.cc                |   5 +-
 src/pool.h                 |   5 +-
 src/py_amount.cc           |  76 ++++++++---------
 src/py_balance.cc          |  10 ++-
 src/py_commodity.cc        | 199 ++++++++++++++++++++++++++++++++++++---------
 src/py_journal.cc          |   8 --
 src/py_value.cc            |  39 ++++-----
 src/pyutils.h              |  20 +++++
 src/quotes.cc              |   8 +-
 src/report.cc              |  17 ++--
 src/session.cc             |   4 +-
 src/textual.cc             |   6 +-
 src/value.cc               |   7 +-
 src/value.h                |   2 +-
 src/xact.cc                |  13 +--
 test/convert.py            |   2 +
 test/regress/25A099C9.test |  12 +--
 27 files changed, 449 insertions(+), 207 deletions(-)
 create mode 100644 python/demo.py

diff --git a/python/demo.py b/python/demo.py
new file mode 100644
index 00000000..788610d7
--- /dev/null
+++ b/python/demo.py
@@ -0,0 +1,120 @@
+import sys
+
+import ledger
+
+print "Welcome to the Ledger.Python demo!"
+
+def assertEqual(pat, candidate):
+    if pat != candidate:
+        print "FAILED: %s != %s" % (pat, candidate)
+        sys.exit(1)
+
+# COMMODITIES
+
+pool = ledger.commodity_pool
+
+usd = pool.find_or_create('$')
+eur = pool.find_or_create('EUR')
+xcd = pool.find_or_create('XCD')
+
+assertEqual('$', usd.symbol)
+assertEqual('$', pool['$'].symbol)
+
+assert not pool.find('CAD')
+assert not pool.has_key('CAD')
+assert not 'CAD' in pool
+
+# There are a few built-in commodities: null, %, h, m and s
+assertEqual([u'', u'$', u'%', u'EUR', u'XCD',
+             u'h', u'm', u's'], sorted(pool.keys()))
+
+for symbol in pool.iterkeys(): pass
+for commodity in pool.itervalues(): pass
+
+# jww (2009-11-19): Not working: missing conversion from std::pair
+#for symbol, commodity in pool.iteritems(): pass
+#for symbol, commodity in pool: pass
+
+# This creates a price exchange entry, trading EUR for $0.77 each at the
+# current time.
+pool.exchange(eur, ledger.Amount('$0.77'))
+
+# AMOUNTS & BALANCES
+
+# When two amounts are multipied or divided, the result carries the commodity
+# of the first term.  So, 1 EUR / $0.77 == roughly 1.2987 EUR
+amt    = ledger.Amount('$100.12')
+market = ((ledger.Amount('1 EUR') / ledger.Amount('$0.77')) * amt)
+
+# An amount's "precision" is a notional thing only.  Since Ledger uses
+# rational numbers throughout, and only renders to decimal form for printing
+# to the user, the meaning of amt.precision should not be relied on as
+# meaningful.  It only controls how much precision unrounded numbers (those
+# for which keep_precision is True, and thus that ignore display_precision)
+# are rendered into strings.  This is the case, btw, for all uncommoditized
+# amounts.
+assert not amt.keep_precision
+assertEqual(2, amt.precision)
+assertEqual(2, amt.display_precision)
+
+assertEqual('$-100.12', str(amt.negated()))  # negate the amount
+assertEqual('$0.01', str(amt.inverted()))    # reverse NUM/DEM
+assertEqual('$100.12', str(amt.rounded()))   # round it to display precision
+assertEqual('$100.12', str(amt.truncated())) # truncate to display precision
+assertEqual('$100.00', str(amt.floored()))   # floor it to nearest integral
+assertEqual(market, amt.value(eur))          # find present market value
+assertEqual('$100.12', str(abs(amt)))        # absolute value
+assertEqual('$100.12', str(amt))             # render to a string
+assertEqual('100.12', amt.quantity_string()) # render quantity to a string
+assertEqual('100.12', str(amt.number()))     # strip away commodity
+assertEqual(1, amt.sign())                   # -1, 0 or 1
+assert amt.is_nonzero()                      # True if display amount nonzero
+assert not amt.is_zero()                     # True if display amount is zero
+assert not amt.is_realzero()                 # True only if value is 0/0
+assert not amt.is_null()                     # True if uninitialized
+
+assertEqual(100.12, amt.to_double())
+assert amt.fits_in_long()
+assertEqual(100, amt.to_long())
+
+amt2 = ledger.Amount('$100.12 {140 EUR}')
+
+assert amt2.has_annotation()
+assertEqual(amt, amt2.strip_annotations(ledger.KeepDetails()))
+
+# jww (2009-11-19): Not working: missing conversion from optional
+#assertEqual(ledger.Amount('20 EUR'), amt.annotation.price)
+
+# VALUES
+
+val = ledger.Value('$100.00')
+
+assert val.is_amount()
+assertEqual('$', val.to_amount().commodity.symbol)
+
+# JOURNALS
+
+#journal.find_account('')
+#journal.find_or_create_account('')
+
+# ACCOUNTS
+
+#account.name
+#account.fullname()
+#account.amount
+#account.total
+
+# TRANSACTIONS
+
+#txn.payee
+
+# POSTINGS
+
+#post.account
+
+# REPORTING
+
+#journal.collect('-M food')
+#journal.collect_accounts('^assets ^liab ^equity')
+
+print 'Demo completed successfully.'
diff --git a/src/amount.cc b/src/amount.cc
index 82b93931..eddbca18 100644
--- a/src/amount.cc
+++ b/src/amount.cc
@@ -106,8 +106,6 @@ private:
 #endif // HAVE_BOOST_SERIALIZATION
 };
 
-shared_ptr amount_t::current_pool;
-
 bool amount_t::is_initialized = false;
 
 namespace {
@@ -203,7 +201,7 @@ namespace {
   }
 }
 
-void amount_t::initialize(shared_ptr pool)
+void amount_t::initialize()
 {
   if (! is_initialized) {
     mpz_init(temp);
@@ -211,26 +209,35 @@ void amount_t::initialize(shared_ptr pool)
     mpfr_init(tempf);
     mpfr_init(tempfb);
 
+    commodity_pool_t::current_pool.reset(new commodity_pool_t);
+
+    // Add time commodity conversions, so that timelog's may be parsed
+    // in terms of seconds, but reported as minutes or hours.
+    if (commodity_t * commodity = commodity_pool_t::current_pool->create("s"))
+      commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET);
+    else
+      assert(false);
+
+    // Add a "percentile" commodity
+    if (commodity_t * commodity = commodity_pool_t::current_pool->create("%"))
+      commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET);
+    else
+      assert(false);
+
     is_initialized = true;
   }
-  current_pool = pool;
-}
-
-void amount_t::initialize()
-{
-  initialize(shared_ptr(new commodity_pool_t));
 }
 
 void amount_t::shutdown()
 {
-  current_pool.reset();
-
   if (is_initialized) {
     mpz_clear(temp);
     mpq_clear(tempq);
     mpfr_clear(tempf);
     mpfr_clear(tempfb);
 
+    commodity_pool_t::current_pool.reset();
+
     is_initialized = false;
   }
 }
@@ -670,7 +677,7 @@ amount_t::value(const bool		      primary_only,
       if (in_terms_of && commodity() == *in_terms_of) {
 	return *this;
       }
-      else if (is_annotated() && annotation().price &&
+      else if (has_annotation() && annotation().price &&
 	       annotation().has_flags(ANNOTATION_PRICE_FIXATED)) {
 	return (*annotation().price * number()).rounded();
       }
@@ -696,7 +703,7 @@ amount_t::value(const bool		      primary_only,
 
 amount_t amount_t::price() const
 {
-  if (is_annotated() && annotation().price) {
+  if (has_annotation() && annotation().price) {
     amount_t temp(*annotation().price);
     temp *= *this;
     DEBUG("amount.price", "Returning price of " << *this << " = " << temp);
@@ -776,7 +783,8 @@ bool amount_t::fits_in_long() const
 
 commodity_t& amount_t::commodity() const
 {
-  return has_commodity() ? *commodity_ : *current_pool->null_commodity;
+  return (has_commodity() ?
+	  *commodity_ : *commodity_pool_t::current_pool->null_commodity);
 }
 
 bool amount_t::has_commodity() const
@@ -794,7 +802,7 @@ void amount_t::annotate(const annotation_t& details)
   else if (! has_commodity())
     return;			// ignore attempt to annotate a "bare commodity
 
-  if (commodity().is_annotated()) {
+  if (commodity().has_annotation()) {
     this_ann  = &as_annotated_commodity(commodity());
     this_base = &this_ann->referent();
   } else {
@@ -816,15 +824,15 @@ void amount_t::annotate(const annotation_t& details)
   DEBUG("amounts.commodities", "Annotated amount is " << *this);
 }
 
-bool amount_t::is_annotated() const
+bool amount_t::has_annotation() const
 {
   if (! quantity)
     throw_(amount_error,
 	   _("Cannot determine if an uninitialized amount's commodity is annotated"));
 
-  assert(! has_commodity() || ! commodity().is_annotated() ||
+  assert(! has_commodity() || ! commodity().has_annotation() ||
 	 as_annotated_commodity(commodity()).details);
-  return has_commodity() && commodity().is_annotated();
+  return has_commodity() && commodity().has_annotation();
 }
 
 annotation_t& amount_t::annotation()
@@ -833,7 +841,7 @@ annotation_t& amount_t::annotation()
     throw_(amount_error,
 	   _("Cannot return commodity annotation details of an uninitialized amount"));
 
-  if (! commodity().is_annotated())
+  if (! commodity().has_annotation())
     throw_(amount_error,
 	   _("Request for annotation details from an unannotated amount"));
 
@@ -963,15 +971,16 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags)
   if (symbol.empty()) {
     commodity_ = NULL;
   } else {
-    commodity_ = current_pool->find(symbol);
+    commodity_ = commodity_pool_t::current_pool->find(symbol);
     if (! commodity_) {
-      commodity_ = current_pool->create(symbol);
+      commodity_ = commodity_pool_t::current_pool->create(symbol);
       newly_created = true;
     }
     assert(commodity_);
 
     if (details)
-      commodity_ = current_pool->find_or_create(*commodity_, details);
+      commodity_ =
+	commodity_pool_t::current_pool->find_or_create(*commodity_, details);
   }
 
   // Quickly scan through and verify the correctness of the amount's use of
@@ -1206,7 +1215,6 @@ void to_xml(std::ostream& out, const amount_t& amt, bool commodity_details)
 template
 void amount_t::serialize(Archive& ar, const unsigned int /* version */)
 {
-  ar & current_pool;
   ar & is_initialized;
   ar & quantity;
   ar & commodity_;
diff --git a/src/amount.h b/src/amount.h
index c75370e3..a8c08905 100644
--- a/src/amount.h
+++ b/src/amount.h
@@ -97,12 +97,8 @@ class amount_t
 	   ordered_field_operators > > >
 {
 public:
-  /** Indicates which commodity pool should be used. */
-  static shared_ptr current_pool;
-
   /** Ready the amount subsystem for use.
       @note Normally called by session_t::initialize(). */
-  static void initialize(shared_ptr pool);
   static void initialize();
   /** Shutdown the amount subsystem and free all resources.
       @note Normally called by session_t::shutdown(). */
@@ -577,7 +573,7 @@ public:
       been stripped.
   */
   void          annotate(const annotation_t& details);
-  bool          is_annotated() const;
+  bool          has_annotation() const;
 
   annotation_t& annotation();
   const annotation_t& annotation() const {
diff --git a/src/annotate.cc b/src/annotate.cc
index bd5a8ef8..146a7afd 100644
--- a/src/annotate.cc
+++ b/src/annotate.cc
@@ -135,13 +135,13 @@ void annotation_t::print(std::ostream& out, bool keep_base) const
 
 bool keep_details_t::keep_all(const commodity_t& comm) const
 {
-  return (! comm.is_annotated() ||
+  return (! comm.has_annotation() ||
 	  (keep_price && keep_date && keep_tag && ! only_actuals));
 }
 
 bool keep_details_t::keep_any(const commodity_t& comm) const
 {
-  return comm.is_annotated() && (keep_price || keep_date || keep_tag);
+  return comm.has_annotation() && (keep_price || keep_date || keep_tag);
 }
 
 bool annotated_commodity_t::operator==(const commodity_t& comm) const
diff --git a/src/balance.cc b/src/balance.cc
index 59eb4d92..4ff51ffc 100644
--- a/src/balance.cc
+++ b/src/balance.cc
@@ -43,21 +43,21 @@ balance_t::balance_t(const double val)
 {
   TRACE_CTOR(balance_t, "const double");
   amounts.insert
-    (amounts_map::value_type(amount_t::current_pool->null_commodity, val));
+    (amounts_map::value_type(commodity_pool_t::current_pool->null_commodity, val));
 }
 
 balance_t::balance_t(const unsigned long val)
 {
   TRACE_CTOR(balance_t, "const unsigned long");
   amounts.insert
-    (amounts_map::value_type(amount_t::current_pool->null_commodity, val));
+    (amounts_map::value_type(commodity_pool_t::current_pool->null_commodity, val));
 }
 
 balance_t::balance_t(const long val)
 {
   TRACE_CTOR(balance_t, "const long");
   amounts.insert
-    (amounts_map::value_type(amount_t::current_pool->null_commodity, val));
+    (amounts_map::value_type(commodity_pool_t::current_pool->null_commodity, val));
 }
 
 balance_t& balance_t::operator+=(const balance_t& bal)
diff --git a/src/commodity.cc b/src/commodity.cc
index b76c7896..79ed77fe 100644
--- a/src/commodity.cc
+++ b/src/commodity.cc
@@ -600,11 +600,11 @@ bool compare_amount_commodities::operator()(const amount_t * left,
   if (cmp != 0)
     return cmp < 0;
 
-  if (! leftcomm.is_annotated()) {
-    return rightcomm.is_annotated();
+  if (! leftcomm.has_annotation()) {
+    return rightcomm.has_annotation();
   }
-  else if (! rightcomm.is_annotated()) {
-    return ! leftcomm.is_annotated();
+  else if (! rightcomm.has_annotation()) {
+    return ! leftcomm.has_annotation();
   }
   else {
     annotated_commodity_t& aleftcomm(static_cast(leftcomm));
@@ -675,7 +675,7 @@ void to_xml(std::ostream& out, const commodity_t& comm,
   }
 
   if (commodity_details) {
-    if (comm.is_annotated())
+    if (comm.has_annotation())
       to_xml(out, as_annotated_commodity(comm).details);
 
     if (comm.varied_history()) {
diff --git a/src/commodity.h b/src/commodity.h
index 42cc6d8f..d5f18844 100644
--- a/src/commodity.h
+++ b/src/commodity.h
@@ -251,7 +251,7 @@ public:
     return *this;
   }
 
-  bool is_annotated() const {
+  bool has_annotation() const {
     return annotated;
   }
 
diff --git a/src/journal.cc b/src/journal.cc
index 2366ce30..6ebccd66 100644
--- a/src/journal.cc
+++ b/src/journal.cc
@@ -77,7 +77,6 @@ journal_t::~journal_t()
     checked_delete(xact);
   
   checked_delete(master);
-  commodity_pool.reset();
 }
 
 void journal_t::initialize()
@@ -85,21 +84,6 @@ void journal_t::initialize()
   master     = new account_t;
   bucket     = NULL;
   was_loaded = false;
-
-  commodity_pool.reset(new commodity_pool_t);
-
-  // Add time commodity conversions, so that timelog's may be parsed
-  // in terms of seconds, but reported as minutes or hours.
-  if (commodity_t * commodity = commodity_pool->create("s"))
-    commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET);
-  else
-    assert(false);
-
-  // Add a "percentile" commodity
-  if (commodity_t * commodity = commodity_pool->create("%"))
-    commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET);
-  else
-    assert(false);
 }
 
 void journal_t::add_account(account_t * acct)
diff --git a/src/journal.h b/src/journal.h
index f7124736..8d59e3b4 100644
--- a/src/journal.h
+++ b/src/journal.h
@@ -47,7 +47,6 @@
 
 namespace ledger {
 
-class commodity_pool_t;
 class xact_base_t;
 class xact_t;
 class auto_xact_t;
@@ -112,8 +111,6 @@ public:
   std::list sources;
   bool                  was_loaded;
 
-  shared_ptr commodity_pool;
-
   journal_t();
   journal_t(const path& pathname);
   journal_t(const string& str);
diff --git a/src/op.cc b/src/op.cc
index 2815ac1a..f38fc86b 100644
--- a/src/op.cc
+++ b/src/op.cc
@@ -624,7 +624,7 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const
   }
 
   if (! symbol.empty()) {
-    if (amount_t::current_pool->find(symbol))
+    if (commodity_pool_t::current_pool->find(symbol))
       out << '@';
     out << symbol;
   }
diff --git a/src/pool.cc b/src/pool.cc
index b08c8fad..9e06613f 100644
--- a/src/pool.cc
+++ b/src/pool.cc
@@ -39,6 +39,8 @@
 
 namespace ledger {
 
+shared_ptr commodity_pool_t::current_pool;
+
 commodity_pool_t::commodity_pool_t()
   : default_commodity(NULL), keep_base(false),
     quote_leeway(86400), get_quotes(false),
@@ -318,8 +320,7 @@ optional commodity_pool_t::parse_price_directive(char * line)
   VERIFY(point.price.valid());
 
   DEBUG("commodity.download", "Looking up symbol: " << symbol);
-  if (commodity_t * commodity =
-      amount_t::current_pool->find_or_create(symbol)) {
+  if (commodity_t * commodity = find_or_create(symbol)) {
     DEBUG("commodity.download", "Adding price for " << symbol << ": "
 	  << point.when << " " << point.price);
     commodity->add_price(point.when, point.price, true);
diff --git a/src/pool.h b/src/pool.h
index 7328df9d..c3d701b9 100644
--- a/src/pool.h
+++ b/src/pool.h
@@ -57,6 +57,7 @@ struct cost_breakdown_t
 
 class commodity_pool_t : public noncopyable
 {
+public:
   /**
    * The commodities collection in commodity_pool_t maintains pointers to all
    * the commodities which have ever been created by the user, whether
@@ -65,7 +66,6 @@ class commodity_pool_t : public noncopyable
    */
   typedef std::map commodities_map;
 
-public:
   commodities_map commodities;
   commodity_t *	  null_commodity;
   commodity_t *	  default_commodity;
@@ -76,6 +76,8 @@ public:
   long		  quote_leeway;     // --leeway=
   bool		  get_quotes;       // --download
 
+  static shared_ptr current_pool;
+
   function
 	   (commodity_t& commodity, const optional& in_terms_of)>
       get_commodity_quote;
@@ -136,6 +138,7 @@ private:
 
   template
   void serialize(Archive& ar, const unsigned int /* version */) {
+    ar & current_pool;
     ar & commodities;
     ar & null_commodity;
     ar & default_commodity;
diff --git a/src/py_amount.cc b/src/py_amount.cc
index b44f3716..2307c454 100644
--- a/src/py_amount.cc
+++ b/src/py_amount.cc
@@ -44,27 +44,14 @@ using namespace boost::python;
 
 namespace {
 
-  boost::optional py_value_0(const amount_t& amount) {
-    return amount.value();
-  }
-  boost::optional py_value_1(const amount_t& amount,
-				       const bool primary_only) {
-    return amount.value(primary_only);
-  }
-
   boost::optional
-  py_value_2(const amount_t& amount,
-	     const bool primary_only,
-	     const boost::optional& moment) {
-    return amount.value(primary_only, moment);
+  py_value_1(const amount_t& amount, commodity_t& in_terms_of) {
+    return amount.value(false, CURRENT_TIME(), in_terms_of);
   }
-
   boost::optional
-  py_value_3(const amount_t& amount,
-	     const bool primary_only,
-	     const boost::optional& moment,
-	     const boost::optional& in_terms_of) {
-    return amount.value(primary_only, moment, in_terms_of);
+  py_value_2(const amount_t& amount, commodity_t& in_terms_of,
+	     datetime_t& moment) {
+    return amount.value(false, moment, in_terms_of);
   }
 
   void py_parse_2(amount_t& amount, object in, unsigned char flags) {
@@ -97,8 +84,15 @@ namespace {
     }
   }
 
-  void py_amount_initialize() {
-    amount_t::initialize();
+  annotation_t& py_amount_annotation(amount_t& amount) {
+    return amount.annotation();
+  }
+
+  amount_t py_strip_annotations_0(amount_t& amount) {
+    return amount.strip_annotations(keep_details_t());
+  }
+  amount_t py_strip_annotations_1(amount_t& amount, const keep_details_t& keep) {
+    return amount.strip_annotations(keep);
   }
 
 } // unnamed namespace
@@ -113,11 +107,7 @@ EXC_TRANSLATOR(amount_error)
 void export_amount()
 {
   class_< amount_t > ("Amount")
-    .add_static_property("current_pool",
-			 make_getter(&amount_t::current_pool,
-				     return_internal_reference<>()))
-
-    .def("initialize", py_amount_initialize) // only for the PyUnitTests
+    .def("initialize", &amount_t::initialize) // only for the PyUnitTests
     .staticmethod("initialize")
     .def("shutdown", &amount_t::shutdown)
     .staticmethod("shutdown")
@@ -196,11 +186,11 @@ internal precision."))
     .def(self	  /  long())
     .def(long()	  / self)
 
-    .def("precision", &amount_t::precision)
-    .def("keep_precision", &amount_t::keep_precision)
-    .def("set_keep_precision", &amount_t::set_keep_precision, args("keep"),
-	 _("Set whether an amount should always display at full precision."))
-    .def("display_precision", &amount_t::display_precision)
+    .add_property("precision", &amount_t::precision)
+    .add_property("display_precision", &amount_t::display_precision)
+    .add_property("keep_precision",
+		  &amount_t::keep_precision,
+		  &amount_t::set_keep_precision)
 
     .def("negated", &amount_t::negated)
     .def("in_place_negate", &amount_t::in_place_negate,
@@ -236,10 +226,8 @@ internal precision."))
     .def("in_place_unreduce", &amount_t::in_place_unreduce,
 	 return_internal_reference<>())
 
-    .def("value", py_value_0)
-    .def("value", py_value_1, args("primary_only"))
-    .def("value", py_value_2, args("primary_only", "moment"))
-    .def("value", py_value_3, args("primary_only", "moment", "in_terms_of"))
+    .def("value", py_value_1, args("moment"))
+    .def("value", py_value_2, args("in_terms_of", "moment"))
 
     .def("price", &amount_t::price)
 
@@ -262,21 +250,23 @@ internal precision."))
     .def("__repr__", &amount_t::to_fullstring)
     .def("quantity_string", &amount_t::quantity_string)
 
-    .def("commodity", &amount_t::commodity,
-	 return_internal_reference<>())
+    .add_property("commodity",
+		  make_function(&amount_t::commodity,
+				return_value_policy()),
+		  make_function(&amount_t::set_commodity,
+				with_custodian_and_ward<1, 2>()))
     .def("has_commodity", &amount_t::has_commodity)
-    .def("set_commodity", &amount_t::set_commodity,
-	 with_custodian_and_ward<1, 2>())
     .def("clear_commodity", &amount_t::clear_commodity)
 
     .def("number", &amount_t::number)
 
     .def("annotate", &amount_t::annotate)
-    .def("is_annotated", &amount_t::is_annotated)
-#if 0
-    .def("annotation", &amount_t::annotation)
-#endif
-    .def("strip_annotations", &amount_t::strip_annotations)
+    .def("has_annotation", &amount_t::has_annotation)
+    .add_property("annotation",
+		  make_function(py_amount_annotation,
+				return_internal_reference<>()))
+    .def("strip_annotations", py_strip_annotations_0)
+    .def("strip_annotations", py_strip_annotations_1)
 
     .def("parse", py_parse_1)
     .def("parse", py_parse_2)
diff --git a/src/py_balance.cc b/src/py_balance.cc
index 23a2ff73..5aed2b43 100644
--- a/src/py_balance.cc
+++ b/src/py_balance.cc
@@ -108,6 +108,13 @@ namespace {
     return (*elem).second;
   }
 
+  balance_t py_strip_annotations_0(balance_t& balance) {
+    return balance.strip_annotations(keep_details_t());
+  }
+  balance_t py_strip_annotations_1(balance_t& balance, const keep_details_t& keep) {
+    return balance.strip_annotations(keep);
+  }
+
 } // unnamed namespace
 
 #define EXC_TRANSLATOR(type)				\
@@ -215,7 +222,8 @@ void export_balance()
 
     .def("number", &balance_t::number)
 
-    .def("strip_annotations", &balance_t::strip_annotations)
+    .def("strip_annotations", py_strip_annotations_0)
+    .def("strip_annotations", py_strip_annotations_1)
 
     .def("valid",  &balance_t::valid)
     ;
diff --git a/src/py_commodity.cc b/src/py_commodity.cc
index 08af8f62..dfaf7f5b 100644
--- a/src/py_commodity.cc
+++ b/src/py_commodity.cc
@@ -32,6 +32,7 @@
 #include 
 
 #include "pyinterp.h"
+#include "pyutils.h"
 #include "commodity.h"
 #include "annotate.h"
 #include "pool.h"
@@ -81,6 +82,12 @@ namespace {
 
   // Exchange one commodity for another, while recording the factored price.
 
+  void py_exchange_2(commodity_pool_t& pool,
+		     commodity_t&      commodity,
+		     const amount_t&   per_unit_cost)
+  {
+    pool.exchange(commodity, per_unit_cost, CURRENT_TIME());
+  }
   void py_exchange_3(commodity_pool_t& pool,
 		     commodity_t&      commodity,
 		     const amount_t&   per_unit_cost,
@@ -99,6 +106,77 @@ namespace {
     return pool.exchange(amount, cost, is_per_unit, moment, tag);
   }
 
+  commodity_t * py_pool_getitem(commodity_pool_t& pool, const string& symbol)
+  {
+    commodity_pool_t::commodities_map::iterator i =
+      pool.commodities.find(symbol);
+    if (i == pool.commodities.end()) {
+      PyErr_SetString(PyExc_ValueError,
+		      (string("Could not find commodity ") + symbol).c_str());
+      throw boost::python::error_already_set();
+    }
+    return (*i).second;
+  }
+
+  python::list py_pool_keys(commodity_pool_t& pool) {
+    python::list keys;
+    BOOST_REVERSE_FOREACH
+      (const commodity_pool_t::commodities_map::value_type& pair,
+       pool.commodities) {
+      keys.insert(0, pair.first);
+    }
+    return keys;
+  }
+
+  bool py_pool_contains(commodity_pool_t& pool, const string& symbol) {
+    return pool.commodities.find(symbol) != pool.commodities.end();
+  }
+
+  commodity_pool_t::commodities_map::iterator
+  py_pool_commodities_begin(commodity_pool_t& pool) {
+    return pool.commodities.begin();
+  }
+  commodity_pool_t::commodities_map::iterator
+  py_pool_commodities_end(commodity_pool_t& pool) {
+    return pool.commodities.end();
+  }
+
+  typedef transform_iterator
+      ,
+       commodity_pool_t::commodities_map::iterator>
+    commodities_map_firsts_iterator;
+  commodities_map_firsts_iterator
+
+  py_pool_commodities_keys_begin(commodity_pool_t& pool) {
+    return make_transform_iterator
+      (pool.commodities.begin(),
+       bind(&commodity_pool_t::commodities_map::value_type::first, _1));
+  }
+  commodities_map_firsts_iterator
+  py_pool_commodities_keys_end(commodity_pool_t& pool) {
+    return make_transform_iterator
+      (pool.commodities.end(),
+       bind(&commodity_pool_t::commodities_map::value_type::first, _1));
+  }
+
+  typedef transform_iterator
+      ,
+       commodity_pool_t::commodities_map::iterator>
+    commodities_map_seconds_iterator;
+
+  commodities_map_seconds_iterator
+  py_pool_commodities_values_begin(commodity_pool_t& pool) {
+    return make_transform_iterator
+      (pool.commodities.begin(),
+       bind(&commodity_pool_t::commodities_map::value_type::second, _1));
+  }
+  commodities_map_seconds_iterator
+  py_pool_commodities_values_end(commodity_pool_t& pool) {
+    return make_transform_iterator
+      (pool.commodities.end(),
+       bind(&commodity_pool_t::commodities_map::value_type::second, _1));
+  }
+
   void py_add_price_2(commodity_t& commodity,
 		      const datetime_t& date, const amount_t& price) {
     commodity.add_price(date, price);
@@ -123,16 +201,38 @@ namespace {
     return details.keep_any(comm);
   }
 
+  commodity_t& py_commodity_referent(commodity_t& comm) {
+    return comm.referent();
+  }
+  commodity_t& py_annotated_commodity_referent(annotated_commodity_t& comm) {
+    return comm.referent();
+  }
+
+  commodity_t& py_strip_annotations_0(commodity_t& comm) {
+    return comm.strip_annotations(keep_details_t());
+  }
+  commodity_t& py_strip_annotations_1(commodity_t& comm,
+				      const keep_details_t& keep) {
+    return comm.strip_annotations(keep);
+  }
+
+  commodity_t& py_strip_ann_annotations_0(annotated_commodity_t& comm) {
+    return comm.strip_annotations(keep_details_t());
+  }
+  commodity_t& py_strip_ann_annotations_1(annotated_commodity_t& comm,
+					  const keep_details_t& keep) {
+    return comm.strip_annotations(keep);
+  }
+
 } // unnamed namespace
 
 void export_commodity()
 {
-  class_< commodity_pool_t, boost::noncopyable > ("CommodityPool", no_init)
+  class_< commodity_pool_t, shared_ptr,
+          boost::noncopyable > ("CommodityPool", no_init)
     .add_property("null_commodity",
 		  make_getter(&commodity_pool_t::null_commodity,
-			      return_internal_reference<>()),
-		  make_setter(&commodity_pool_t::null_commodity,
-			      with_custodian_and_ward<1, 2>()))
+			      return_internal_reference<>()))
     .add_property("default_commodity",
 		  make_getter(&commodity_pool_t::default_commodity,
 			      return_internal_reference<>()),
@@ -157,25 +257,46 @@ void export_commodity()
 
     .def("make_qualified_name", &commodity_pool_t::make_qualified_name)
 
-    .def("create", py_create_1, return_internal_reference<>())
-    .def("create", py_create_2, return_internal_reference<>())
+    .def("create", py_create_1,
+	 return_value_policy())
+    .def("create", py_create_2,
+	 return_value_policy())
 
     .def("find_or_create", py_find_or_create_1,
-	 return_internal_reference<>())
+	 return_value_policy())
     .def("find_or_create", py_find_or_create_2,
-	 return_internal_reference<>())
+	 return_value_policy())
 
-    .def("find", py_find_1, return_internal_reference<>())
-    .def("find", py_find_2, return_internal_reference<>())
+    .def("find", py_find_1, return_value_policy())
+    .def("find", py_find_2, return_value_policy())
 
+    .def("exchange", py_exchange_2, with_custodian_and_ward<1, 2>())
     .def("exchange", py_exchange_3, with_custodian_and_ward<1, 2>())
     .def("exchange", py_exchange_5)
 
     .def("parse_price_directive", &commodity_pool_t::parse_price_directive)
     .def("parse_price_expression", &commodity_pool_t::parse_price_expression,
-	 return_internal_reference<>())
+	 return_value_policy())
+
+    .def("__getitem__", py_pool_getitem,
+	 return_value_policy())
+    .def("keys", py_pool_keys)
+    .def("has_key", py_pool_contains)
+    .def("__contains__", py_pool_contains)
+    .def("__iter__", range >
+	 (py_pool_commodities_begin, py_pool_commodities_end))
+    .def("iteritems", range >
+	 (py_pool_commodities_begin, py_pool_commodities_end))
+    .def("iterkeys", range<>(py_pool_commodities_keys_begin,
+			     py_pool_commodities_keys_end))
+    .def("itervalues", range >
+	 (py_pool_commodities_values_begin, py_pool_commodities_values_end))
     ;
 
+  map_value_type_converter();
+
+  scope().attr("commodity_pool") = commodity_pool_t::current_pool;
+
   scope().attr("COMMODITY_STYLE_DEFAULTS")  = COMMODITY_STYLE_DEFAULTS;
   scope().attr("COMMODITY_STYLE_SUFFIXED")  = COMMODITY_STYLE_SUFFIXED;
   scope().attr("COMMODITY_STYLE_SEPARATED") = COMMODITY_STYLE_SEPARATED;
@@ -209,33 +330,30 @@ void export_commodity()
     .def("symbol_needs_quotes", &commodity_t::symbol_needs_quotes)
     .staticmethod("symbol_needs_quotes")
 
-#if 0
-    .def("referent", &commodity_t::referent,
-	 return_internal_reference<>())
-#endif
+    .add_property("referent",
+		  make_function(py_commodity_referent,
+				return_value_policy()))
 
-    .def("is_annotated", &commodity_t::is_annotated)
-    .def("strip_annotations", &commodity_t::strip_annotations,
-	 return_internal_reference<>())
+    .def("has_annotation", &commodity_t::has_annotation)
+    .def("strip_annotations", py_strip_annotations_0,
+	 return_value_policy())
+    .def("strip_annotations", py_strip_annotations_1,
+	 return_value_policy())
     .def("write_annotations", &commodity_t::write_annotations)
 
     .def("pool", &commodity_t::pool,
-	 return_internal_reference<>())
-
-    .def("base_symbol", &commodity_t::base_symbol)
-    .def("symbol", &commodity_t::symbol)
-    .def("mapping_key", &commodity_t::mapping_key)
-
-    .def("name", &commodity_t::name)
-    .def("set_name", &commodity_t::set_name)
-    .def("note", &commodity_t::note)
-    .def("set_note", &commodity_t::set_note)
-    .def("precision", &commodity_t::precision)
-    .def("set_precision", &commodity_t::set_precision)
-    .def("smaller", &commodity_t::smaller)
-    .def("set_smaller", &commodity_t::set_smaller)
-    .def("larger", &commodity_t::larger)
-    .def("set_larger", &commodity_t::set_larger)
+	 return_value_policy())
+
+    .add_property("base_symbol", &commodity_t::base_symbol)
+    .add_property("symbol", &commodity_t::symbol)
+    .add_property("mapping_key", &commodity_t::mapping_key)
+
+    .add_property("name", &commodity_t::name, &commodity_t::set_name)
+    .add_property("note", &commodity_t::note, &commodity_t::set_note)
+    .add_property("precision", &commodity_t::precision,
+		  &commodity_t::set_precision)
+    .add_property("smaller", &commodity_t::smaller, &commodity_t::set_smaller)
+    .add_property("larger", &commodity_t::larger, &commodity_t::set_larger)
 
     .def("add_price", py_add_price_2)
     .def("add_price", py_add_price_3)
@@ -306,13 +424,14 @@ void export_commodity()
     .def(self == self)
     .def(self == other())
 
-#if 0
-    .def("referent", &annotated_commodity_t::referent,
-	 return_internal_reference<>())
-#endif
+    .add_property("referent",
+		  make_function(py_annotated_commodity_referent,
+				return_value_policy()))
 
-    .def("strip_annotations", &annotated_commodity_t::strip_annotations,
-	 return_internal_reference<>())
+    .def("strip_annotations", py_strip_ann_annotations_0,
+	 return_value_policy())
+    .def("strip_annotations", py_strip_ann_annotations_1,
+	 return_value_policy())
     .def("write_annotations", &annotated_commodity_t::write_annotations)
     ;
 }
diff --git a/src/py_journal.cc b/src/py_journal.cc
index 7e9f8a1b..5be9cbe1 100644
--- a/src/py_journal.cc
+++ b/src/py_journal.cc
@@ -52,10 +52,6 @@ namespace {
     return *journal.master;
   }
 
-  commodity_pool_t& py_commodity_pool(journal_t& journal) {
-    return *journal.commodity_pool;
-  }
-
   long xacts_len(journal_t& journal)
   {
     return journal.xacts.size();
@@ -276,10 +272,6 @@ void export_journal()
 			          with_custodian_and_ward_postcall<1, 0> >()),
 		  make_setter(&journal_t::bucket))
     .add_property("was_loaded", make_getter(&journal_t::was_loaded))
-    .add_property("commodity_pool",
-		  make_getter(&journal_t::commodity_pool,
-			      return_internal_reference<1,
-			          with_custodian_and_ward_postcall<1, 0> >()))
 
     .def("add_account", &journal_t::add_account)
     .def("remove_account", &journal_t::remove_account)
diff --git a/src/py_value.cc b/src/py_value.cc
index ee039519..98f3f079 100644
--- a/src/py_value.cc
+++ b/src/py_value.cc
@@ -47,7 +47,8 @@ BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(set_string_overloads, set_string, 0, 2)
 
 namespace {
 
-  PyObject * py_base_type(value_t& value) {
+  PyObject * py_base_type(value_t& value)
+  {
     if (value.is_boolean()) {
       return (PyObject *)&PyBool_Type;
     }
@@ -63,16 +64,6 @@ namespace {
     }
   }
 
-  expr_t py_value_getattr(const value_t& value, const string& name)
-  {
-    if (value.is_scope()) {
-      if (scope_t * scope = value.as_scope())
-	return expr_t(scope->lookup(symbol_t::FUNCTION, name), scope);
-    }
-    throw_(value_error, _("Cannot lookup attributes in %1") << value.label());
-    return expr_t();
-  }
-
   string py_dump(const value_t& value) {
     std::ostringstream buf;
     value.dump(buf);
@@ -85,10 +76,20 @@ namespace {
     return buf.str();
   }
 
-  void py_set_string(value_t& amount, const string& str) {
-    return amount.set_string(str);
+  void py_set_string(value_t& value, const string& str) {
+    return value.set_string(str);
   }
 
+  annotation_t& py_value_annotation(value_t& value) {
+    return value.annotation();
+  }
+
+  value_t py_strip_annotations_0(value_t& value) {
+    return value.strip_annotations(keep_details_t());
+  }
+  value_t py_strip_annotations_1(value_t& value, const keep_details_t& keep) {
+    return value.strip_annotations(keep);
+  }
 } // unnamed namespace
 
 #define EXC_TRANSLATOR(type)				\
@@ -306,16 +307,16 @@ void export_value()
     .def("number", &value_t::number)
 
     .def("annotate", &value_t::annotate)
-    .def("is_annotated", &value_t::is_annotated)
-#if 0
-    .def("annotation", &value_t::annotation)
-#endif
-    .def("strip_annotations", &value_t::strip_annotations)
+    .def("has_annotation", &value_t::has_annotation)
+    .add_property("annotation",
+		  make_function(py_value_annotation,
+				return_internal_reference<>()))
+    .def("strip_annotations", py_strip_annotations_0)
+    .def("strip_annotations", py_strip_annotations_1)
 
 #if 0
     .def("__getitem__", &value_t::operator[])
 #endif
-    .def("__getattr__", py_value_getattr)
     .def("push_back", &value_t::push_back)
     .def("pop_back", &value_t::pop_back)
     .def("size", &value_t::size)
diff --git a/src/pyutils.h b/src/pyutils.h
index a9e968e0..d8a46527 100644
--- a/src/pyutils.h
+++ b/src/pyutils.h
@@ -106,6 +106,26 @@ struct register_optional_to_python : public boost::noncopyable
   }
 };
 
+template 
+struct PairToTupleConverter
+{
+  static PyObject * convert(const std::pair& pair) {
+    return boost::python::incref
+      (boost::python::make_tuple(pair.first, pair.second).ptr());
+  }
+};
+
+template 
+struct map_value_type_converter
+{
+  map_value_type_converter() {
+    boost::python::to_python_converter
+       >();
+  }
+};
+
 namespace boost { namespace python {
 
 // Use expr to create the PyObject corresponding to x
diff --git a/src/quotes.cc b/src/quotes.cc
index 7f41e4ff..ffe2142a 100644
--- a/src/quotes.cc
+++ b/src/quotes.cc
@@ -76,13 +76,13 @@ commodity_quote_from_script(commodity_t& commodity,
     DEBUG("commodity.download", "downloaded quote: " << buf);
 
     if (optional point =
-	amount_t::current_pool->parse_price_directive(buf)) {
-      if (amount_t::current_pool->price_db) {
+	commodity_pool_t::current_pool->parse_price_directive(buf)) {
+      if (commodity_pool_t::current_pool->price_db) {
 #if defined(__GNUG__) && __GNUG__ < 3
-	ofstream database(*amount_t::current_pool->price_db,
+	ofstream database(*commodity_pool_t::current_pool->price_db,
 			  ios::out | ios::app);
 #else
-	ofstream database(*amount_t::current_pool->price_db,
+	ofstream database(*commodity_pool_t::current_pool->price_db,
 			  std::ios_base::out | std::ios_base::app);
 #endif
 	database << "P "
diff --git a/src/report.cc b/src/report.cc
index 7da44f8c..4c157312 100644
--- a/src/report.cc
+++ b/src/report.cc
@@ -69,18 +69,17 @@ void report_t::normalize_options(const string& verb)
   item_t::use_effective_date = (HANDLED(effective) &&
 				! HANDLED(actual_dates));
 
-  session.journal->commodity_pool->keep_base  = HANDLED(base);
-  session.journal->commodity_pool->get_quotes = session.HANDLED(download);
+  commodity_pool_t::current_pool->keep_base  = HANDLED(base);
+  commodity_pool_t::current_pool->get_quotes = session.HANDLED(download);
 
   if (session.HANDLED(price_exp_))
-    session.journal->commodity_pool->quote_leeway =
+    commodity_pool_t::current_pool->quote_leeway =
       session.HANDLER(price_exp_).value.as_long();
 
   if (session.HANDLED(price_db_))
-    session.journal->commodity_pool->price_db =
-      session.HANDLER(price_db_).str();
+    commodity_pool_t::current_pool->price_db = session.HANDLER(price_db_).str();
   else
-    session.journal->commodity_pool->price_db = none;
+    commodity_pool_t::current_pool->price_db = none;
 
   if (HANDLED(date_format_))
     set_date_format(HANDLER(date_format_).str().c_str());
@@ -522,7 +521,7 @@ value_t report_t::fn_price(call_scope_t& scope)
 value_t report_t::fn_lot_date(call_scope_t& scope)
 {
   interactive_t args(scope, "v");
-  if (args.value_at(0).is_annotated()) {
+  if (args.value_at(0).has_annotation()) {
     const annotation_t& details(args.value_at(0).annotation());
     if (details.date)
       return *details.date;
@@ -533,7 +532,7 @@ value_t report_t::fn_lot_date(call_scope_t& scope)
 value_t report_t::fn_lot_price(call_scope_t& scope)
 {
   interactive_t args(scope, "v");
-  if (args.value_at(0).is_annotated()) {
+  if (args.value_at(0).has_annotation()) {
     const annotation_t& details(args.value_at(0).annotation());
     if (details.price)
       return *details.price;
@@ -544,7 +543,7 @@ value_t report_t::fn_lot_price(call_scope_t& scope)
 value_t report_t::fn_lot_tag(call_scope_t& scope)
 {
   interactive_t args(scope, "v");
-  if (args.value_at(0).is_annotated()) {
+  if (args.value_at(0).has_annotation()) {
     const annotation_t& details(args.value_at(0).annotation());
     if (details.tag)
       return string_value(*details.tag);
diff --git a/src/session.cc b/src/session.cc
index 0d6a6829..1882e554 100644
--- a/src/session.cc
+++ b/src/session.cc
@@ -45,7 +45,7 @@ void set_session_context(session_t * session)
 {
   if (session) {
     times_initialize();
-    amount_t::initialize(session->journal->commodity_pool);
+    amount_t::initialize();
 
     amount_t::parse_conversion("1.0m", "60s");
     amount_t::parse_conversion("1.0h", "60m");
@@ -179,7 +179,7 @@ void session_t::close_journal_files()
   amount_t::shutdown();
   
   journal.reset(new journal_t);
-  amount_t::initialize(journal->commodity_pool);
+  amount_t::initialize();
 }
 
 option_t * session_t::lookup_option(const char * p)
diff --git a/src/textual.cc b/src/textual.cc
index aec7dbda..071e111d 100644
--- a/src/textual.cc
+++ b/src/textual.cc
@@ -490,7 +490,7 @@ void instance_t::default_commodity_directive(char * line)
 {
   amount_t amt(skip_ws(line + 1));
   VERIFY(amt.valid());
-  amount_t::current_pool->default_commodity = &amt.commodity();
+  commodity_pool_t::current_pool->default_commodity = &amt.commodity();
   amt.commodity().add_flags(COMMODITY_KNOWN);
 }
 
@@ -511,7 +511,7 @@ void instance_t::price_conversion_directive(char * line)
 void instance_t::price_xact_directive(char * line)
 {
   optional point =
-    amount_t::current_pool->parse_price_directive(skip_ws(line + 1));
+    commodity_pool_t::current_pool->parse_price_directive(skip_ws(line + 1));
   if (! point)
     throw parse_error(_("Pricing entry failed to parse"));
 }
@@ -523,7 +523,7 @@ void instance_t::nomarket_directive(char * line)
   commodity_t::parse_symbol(p, symbol);
 
   if (commodity_t * commodity =
-      amount_t::current_pool->find_or_create(symbol))
+      commodity_pool_t::current_pool->find_or_create(symbol))
     commodity->add_flags(COMMODITY_NOMARKET | COMMODITY_KNOWN);
 }
 
diff --git a/src/value.cc b/src/value.cc
index f4df3329..e2e748f4 100644
--- a/src/value.cc
+++ b/src/value.cc
@@ -1353,7 +1353,8 @@ value_t value_t::exchange_commodities(const std::string&	  commodities,
        p;
        p = std::strtok(NULL, ",")) {
     if (commodity_t * commodity =
-	amount_t::current_pool->parse_price_expression(p, add_prices, moment)) {
+	commodity_pool_t::current_pool->parse_price_expression(p, add_prices,
+							       moment)) {
       value_t result = value(false, moment, *commodity);
       if (! result.is_null())
 	return result;
@@ -1523,10 +1524,10 @@ void value_t::annotate(const annotation_t& details)
     throw_(value_error, _("Cannot annotate %1") << label());
 }
 
-bool value_t::is_annotated() const
+bool value_t::has_annotation() const
 {
   if (is_amount())
-    return as_amount().is_annotated();
+    return as_amount().has_annotation();
   else
     throw_(value_error,
 	   _("Cannot determine whether %1 is annotated") << label());
diff --git a/src/value.h b/src/value.h
index 2a420cd3..ffbb89e8 100644
--- a/src/value.h
+++ b/src/value.h
@@ -774,7 +774,7 @@ public:
    * Annotated commodity methods.
    */
   void          annotate(const annotation_t& details);
-  bool          is_annotated() const;
+  bool          has_annotation() const;
 
   annotation_t& annotation();
   const annotation_t& annotation() const {
diff --git a/src/xact.cc b/src/xact.cc
index f2694976..623c5772 100644
--- a/src/xact.cc
+++ b/src/xact.cc
@@ -122,7 +122,7 @@ bool xact_base_t::finalize()
     amount_t& p(post->cost ? *post->cost : post->amount);
     if (! p.is_null()) {
       DEBUG("xact.finalize", "post must balance = " << p.reduced());
-      if (! post->cost && post->amount.is_annotated() &&
+      if (! post->cost && post->amount.has_annotation() &&
 	  post->amount.annotation().price) {
 	// If the amount has no cost, but is annotated with a per-unit
 	// price, use the price times the amount as the cost
@@ -221,7 +221,7 @@ bool xact_base_t::finalize()
 
     foreach (post_t * post, posts) {
       if (! post->amount.is_null()) {
-	if (post->amount.is_annotated())
+	if (post->amount.has_annotation())
 	  top_post = post;
 	else if (! top_post)
 	  top_post = post;
@@ -260,7 +260,7 @@ bool xact_base_t::finalize()
 	foreach (post_t * post, posts) {
 	  if (post != top_post && post->must_balance() &&
 	      ! post->amount.is_null() &&
-	      post->amount.is_annotated() &&
+	      post->amount.has_annotation() &&
 	      post->amount.annotation().price) {
 	    amount_t temp = *post->amount.annotation().price * post->amount;
 	    if (total_cost.is_null()) {
@@ -309,10 +309,11 @@ bool xact_base_t::finalize()
 	     _("A posting's cost must be of a different commodity than its amount"));
 
     cost_breakdown_t breakdown =
-      amount_t::current_pool->exchange(post->amount, *post->cost, false,
-				       datetime_t(date(), time_duration(0, 0, 0, 0)));
+      commodity_pool_t::current_pool->exchange
+        (post->amount, *post->cost, false,
+	 datetime_t(date(), time_duration(0, 0, 0, 0)));
 
-    if (post->amount.is_annotated() &&
+    if (post->amount.has_annotation() &&
 	breakdown.basis_cost.commodity() ==
 	breakdown.final_cost.commodity()) {
       if (amount_t gain_loss = (breakdown.basis_cost -
diff --git a/test/convert.py b/test/convert.py
index 0c64fde4..05a62b7e 100755
--- a/test/convert.py
+++ b/test/convert.py
@@ -158,6 +158,8 @@ for line in fd.readlines():
     line = re.sub('false', 'False', line)
     line = re.sub('CURRENT_TIME\(\)', 'datetime.now()', line)
     line = re.sub('CURRENT_DATE\(\)', 'date.today()', line)
+    line = re.sub('commodity\(\)', 'commodity', line)
+    line = re.sub('precision\(\)', 'precision', line)
     line = re.sub('([0-9]+)[FL]', '\\1', line)
     line = re.sub('([0-9]+)UL', '\\1L', line)
     line = re.sub(';', '', line)
diff --git a/test/regress/25A099C9.test b/test/regress/25A099C9.test
index 604939d8..dec51008 100644
--- a/test/regress/25A099C9.test
+++ b/test/regress/25A099C9.test
@@ -4,16 +4,16 @@
 >>>2
 While parsing file "$sourcepath/src/amount.h", line 67: 
 Error: No quantity specified for amount
-While parsing file "$sourcepath/src/amount.h", line 721: 
+While parsing file "$sourcepath/src/amount.h", line 717: 
 Error: Invalid date/time: line amount_t amoun
-While parsing file "$sourcepath/src/amount.h", line 727: 
+While parsing file "$sourcepath/src/amount.h", line 723: 
 Error: Invalid date/time: line string amount_
-While parsing file "$sourcepath/src/amount.h", line 733: 
+While parsing file "$sourcepath/src/amount.h", line 729: 
 Error: Invalid date/time: line string amount_
-While parsing file "$sourcepath/src/amount.h", line 739: 
+While parsing file "$sourcepath/src/amount.h", line 735: 
 Error: Invalid date/time: line string amount_
-While parsing file "$sourcepath/src/amount.h", line 745: 
+While parsing file "$sourcepath/src/amount.h", line 741: 
 Error: Invalid date/time: line std::ostream& 
-While parsing file "$sourcepath/src/amount.h", line 752: 
+While parsing file "$sourcepath/src/amount.h", line 748: 
 Error: Invalid date/time: line std::istream& 
 === 7
-- 
cgit v1.2.3


From 73c3aa324b5d7ed850ded57a83a6a1e2aa33c185 Mon Sep 17 00:00:00 2001
From: John Wiegley 
Date: Thu, 19 Nov 2009 21:53:23 -0500
Subject: Python is enabled by default if building via acprep

---
 acprep | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/acprep b/acprep
index f188a4a8..bfb3173a 100755
--- a/acprep
+++ b/acprep
@@ -183,7 +183,7 @@ class PrepareBuild(CommandLineApp):
         self.current_flavor    = 'debug'
         self.products_dir      = None
         self.build_dir         = self.source_dir
-        self.configure_args    = ['--with-included-gettext']
+        self.configure_args    = ['--with-included-gettext', '--enable-python']
         self.sys_include_dirs  = []
         self.sys_library_dirs  = []
 
-- 
cgit v1.2.3


From b00e7ac19a096a7b736863dced616d552843ed6e Mon Sep 17 00:00:00 2001
From: John Wiegley 
Date: Fri, 20 Nov 2009 05:56:24 -0500
Subject: Added more documentation to python/demo.py

---
 python/demo.py      | 285 +++++++++++++++++++++++++++++++++++++++++-----------
 src/py_commodity.cc |  14 ++-
 2 files changed, 239 insertions(+), 60 deletions(-)
 mode change 100644 => 100755 python/demo.py

diff --git a/python/demo.py b/python/demo.py
old mode 100644
new mode 100755
index 788610d7..7b4003f3
--- a/python/demo.py
+++ b/python/demo.py
@@ -1,91 +1,264 @@
+#!/usr/bin/env python
+
 import sys
+from datetime import datetime
+
+# The following literate program will demonstrate, by example, how to use the
+# Ledger Python module to access your data and build custom reports using the
+# magic of Python.
 
 import ledger
 
 print "Welcome to the Ledger.Python demo!"
 
+# Some quick helper functions to help us assert various types of truth
+# throughout the script.
+
 def assertEqual(pat, candidate):
     if pat != candidate:
-        print "FAILED: %s != %s" % (pat, candidate)
+        raise Exception("FAILED: %s != %s" % (pat, candidate))
         sys.exit(1)
 
+###############################################################################
+#
 # COMMODITIES
-
-pool = ledger.commodity_pool
-
-usd = pool.find_or_create('$')
-eur = pool.find_or_create('EUR')
-xcd = pool.find_or_create('XCD')
+#
+# Every amount in Ledger has a commodity, even if it is the "null commodity".
+# What's special about commodities are not just their symbol, but how they
+# alter the way amounts are displayed.
+#
+# For example, internally Ledger uses infinite precision rational numbers,
+# which have no decimal point.  So how does it know that $1.00 / $0.75 should
+# be displayed as $1.33, and not with an infinitely repeating decimal?  It
+# does it by consulting the commodity.
+#
+# Whenever an amount is encountered in your data file, Ledger observes how you
+# specified it:
+#   - How many digits of precision did you use?
+#   - Was the commodity name before or after the amount?
+#   - Was the commodity separated from the amount by a space?
+#   - Did you use thousands markers (1,000)?
+#   - Did you use European-style numbers (1.000,00)?
+#
+# By tracking this information for each commodity, Ledger knows how you want
+# to see the amount in your reports.  This way, dollars can be output as
+# $123.56, while stock options could be output as 10.113 AAPL.
+#
+# Your program can access the known set of commodities using the global
+# `ledger.commodities'.  This object behaves like a dict, and support all of
+# the non-modifying dict protocol methods.  If you wish to create a new
+# commodity without parsing an amount, you can use the method
+# `find_or_create':
+
+comms = ledger.commodities
+
+usd = comms.find_or_create('$')
+eur = comms.find_or_create('EUR')
+xcd = comms.find_or_create('XCD')
+
+assert not comms.find('CAD')
+assert not comms.has_key('CAD')
+assert not 'CAD' in comms
+
+# The above mentioned commodity display attributes can be set using commodity
+# display flags.  This is not something you will usually be doing, however, as
+# these flags can be inferred correctly from a large enough set of sample
+# amounts, such as those found in your data file.  If you live in Europe and
+# want all amounts to default to the European-style, set the static variable
+# `european_by_default'.
+
+eur.add_flags(ledger.COMMODITY_STYLE_EUROPEAN)
+assert eur.has_flags(ledger.COMMODITY_STYLE_EUROPEAN)
+assert not eur.has_flags(ledger.COMMODITY_STYLE_THOUSANDS)
+
+comms.european_by_default = True
+
+# There are a few built-in commodities: null, %, h, m and s.  Normally you
+# don't need to worry about them, but they'll show up if you examine all the
+# keys in the commodities dict.
+
+assertEqual([u'', u'$', u'%', u'EUR', u'XCD', u'h', u'm', u's'],
+            sorted(comms.keys()))
+
+# All the styles of dict iteration are supported:
+
+for symbol in comms.iterkeys():
+    pass
+for commodity in comms.itervalues():
+    pass
+#for symbol, commodity in comms.iteritems():
+#    pass
+#for symbol, commodity in comms:
+#    pass
+
+# Another important thing about commodities is that they remember if they've
+# been exchanged for another commodity, and what the conversion rate was on
+# that date.  You can record specific conversion rates for any date using the
+# `exchange' method.
+
+comms.exchange(eur, ledger.Amount('$0.77')) # Trade 1 EUR for $0.77
+comms.exchange(eur, ledger.Amount('$0.66'), datetime.now())
+
+# For the most part, however, you won't be interacting with commodities
+# directly, except maybe to look at their `symbol'.
 
 assertEqual('$', usd.symbol)
-assertEqual('$', pool['$'].symbol)
+assertEqual('$', comms['$'].symbol)
 
-assert not pool.find('CAD')
-assert not pool.has_key('CAD')
-assert not 'CAD' in pool
+###############################################################################
+#
+# AMOUNTS & BALANCES
+#
+# Ledger deals with two basic numerical values: Amount and Balance objects.
+# An Amount is an infinite-precision rational with an associated commodity
+# (even if it is the null commodity, which is called an "uncommoditized
+# amount").  A Balance is a collection of Amounts of differing commodities.
+#
+# Amounts support all the math operations you might expect of an integer,
+# except it carries a commodity.  Let's take dollars for example:
+
+zero  = ledger.Amount("$0")
+one   = ledger.Amount("$1")
+oneb  = ledger.Amount("$1")
+two   = ledger.Amount("$2")
+three = ledger.Amount("3")      # uncommoditized
+
+assert one == oneb              # numeric equality, not identity
+assert one != two
+assert not zero                 # tests if it would *display* as a zero
+assert one < two
+assert one > zero
+
+# For addition and subtraction, only amounts of the same commodity may be
+# used, unless one of the amounts has no commodity at all -- in which case the
+# result uses the commodity of the other value.  Adding $10 to 10 EUR, for
+# example, causes an ArithmeticError exception, but adding 10 to $10 gives
+# $20.
+
+four = ledger.Amount(two)       # make a copy
+four += two
+assertEqual(four, two + two)
+assertEqual(zero, one - one)
+
+try:
+    two += ledger.Amount("20 EUR")
+    assert False
+except ArithmeticError:
+    pass
+
+# Use `number' to get the uncommoditized version of an Amount
+
+assertEqual(three, (two + one).number())
+
+# Multiplication and division does supports Amounts of different commodities,
+# however:
+#   - If either amount is uncommoditized, the result carries the commodity of
+#     the other amount.
+#   - Otherwise, the result always carries the commodity of the first amount.
+
+five = ledger.Amount("5 CAD")
+
+assertEqual(one, two / two)
+assertEqual(five, (five * ledger.Amount("$2")) - ledger.Amount("5"))
+
+# An amount's commodity determines the decimal precision it's displayed with.
+# However, this "precision" is a notional thing only.  You can tell an amount
+# to ignore its display precision by setting `keep_precision' to True.
+# (Uncommoditized amounts ignore the value of `keep_precision', and assume it
+# is always True).  In this case, Ledger does its best to maintain maximal
+# precision by watching how the Amount is used.  That is, 1.01 * 1.01 yields a
+# precision of 4.  This tracking is just a best estimate, however, since
+# internally Ledger never uses floating-point values.
+
+amt  = ledger.Amount('$100.12')
+mini = ledger.Amount('0.00045')
 
-# There are a few built-in commodities: null, %, h, m and s
-assertEqual([u'', u'$', u'%', u'EUR', u'XCD',
-             u'h', u'm', u's'], sorted(pool.keys()))
+assert not amt.keep_precision
 
-for symbol in pool.iterkeys(): pass
-for commodity in pool.itervalues(): pass
+assertEqual(5, mini.precision)
+assertEqual(5, mini.display_precision) # display_precision == precision
+assertEqual(2, amt.precision)
+assertEqual(2, amt.display_precision)
 
-# jww (2009-11-19): Not working: missing conversion from std::pair
-#for symbol, commodity in pool.iteritems(): pass
-#for symbol, commodity in pool: pass
+mini *= mini
+amt  *= amt
 
-# This creates a price exchange entry, trading EUR for $0.77 each at the
-# current time.
-pool.exchange(eur, ledger.Amount('$0.77'))
+assertEqual(10, mini.precision)
+assertEqual(10, mini.display_precision)
+assertEqual(4, amt.precision)
+assertEqual(2, amt.display_precision)
 
-# AMOUNTS & BALANCES
+# There are several other supported math operations:
 
-# When two amounts are multipied or divided, the result carries the commodity
-# of the first term.  So, 1 EUR / $0.77 == roughly 1.2987 EUR
 amt    = ledger.Amount('$100.12')
 market = ((ledger.Amount('1 EUR') / ledger.Amount('$0.77')) * amt)
 
-# An amount's "precision" is a notional thing only.  Since Ledger uses
-# rational numbers throughout, and only renders to decimal form for printing
-# to the user, the meaning of amt.precision should not be relied on as
-# meaningful.  It only controls how much precision unrounded numbers (those
-# for which keep_precision is True, and thus that ignore display_precision)
-# are rendered into strings.  This is the case, btw, for all uncommoditized
-# amounts.
-assert not amt.keep_precision
-assertEqual(2, amt.precision)
-assertEqual(2, amt.display_precision)
-
-assertEqual('$-100.12', str(amt.negated()))  # negate the amount
-assertEqual('$0.01', str(amt.inverted()))    # reverse NUM/DEM
-assertEqual('$100.12', str(amt.rounded()))   # round it to display precision
-assertEqual('$100.12', str(amt.truncated())) # truncate to display precision
-assertEqual('$100.00', str(amt.floored()))   # floor it to nearest integral
-assertEqual(market, amt.value(eur))          # find present market value
-assertEqual('$100.12', str(abs(amt)))        # absolute value
-assertEqual('$100.12', str(amt))             # render to a string
-assertEqual('100.12', amt.quantity_string()) # render quantity to a string
-assertEqual('100.12', str(amt.number()))     # strip away commodity
-assertEqual(1, amt.sign())                   # -1, 0 or 1
-assert amt.is_nonzero()                      # True if display amount nonzero
-assert not amt.is_zero()                     # True if display amount is zero
-assert not amt.is_realzero()                 # True only if value is 0/0
-assert not amt.is_null()                     # True if uninitialized
+assertEqual(market, amt.value(eur))            # find present market value
+
+assertEqual('$-100.12', str(amt.negated()))    # negate the amount
+assertEqual('$-100.12', str(- amt))            # negate it more simply
+assertEqual('$0.01',    str(amt.inverted()))   # reverse NUM/DEM
+assertEqual('$100.12',  str(amt.rounded()))    # round it to display precision
+assertEqual('$100.12',  str(amt.truncated()))  # truncate to display precision
+assertEqual('$100.00',  str(amt.floored()))    # floor it to nearest integral
+assertEqual('$100.12',  str(abs(amt)))         # absolute value
+assertEqual('$100.12',  str(amt))              # render to a string
+assertEqual('100.12',   amt.quantity_string()) # render quantity to a string
+assertEqual('100.12',   str(amt.number()))     # strip away commodity
+assertEqual(1,          amt.sign())            # -1, 0 or 1
+assert amt.is_nonzero()                        # True if display amount nonzero
+assert not amt.is_zero()                       # True if display amount is zero
+assert not amt.is_realzero()                   # True only if value is 0/0
+assert not amt.is_null()                       # True if uninitialized
+
+# Amounts can also be converted the standard floats and integers, although
+# this is not recommend since it can lose precision.
 
 assertEqual(100.12, amt.to_double())
-assert amt.fits_in_long()
+assert amt.fits_in_long()       # there is no `fits_in_double'
 assertEqual(100, amt.to_long())
 
-amt2 = ledger.Amount('$100.12 {140 EUR}')
+# Finally, amounts can be annotated to provide additional information about
+# "lots" of a given commodity.  This example shows $100.12 that was purchased
+# on 2009/10/01 for 140 EUR.  Lot information can be accessed through via the
+# Amount's `annotation' property.  You can also strip away lot details to get
+# the underlying amount.  If you want the total price of any Amount, by
+# multiplying by its per-unit lot price, call the `Amount.price' method
+# instead of the `Annotation.price' property.
+
+amt2 = ledger.Amount('$100.12 {140 EUR} [2009/10/01]')
 
 assert amt2.has_annotation()
-assertEqual(amt, amt2.strip_annotations(ledger.KeepDetails()))
+assertEqual(amt, amt2.strip_annotations())
 
-# jww (2009-11-19): Not working: missing conversion from optional
-#assertEqual(ledger.Amount('20 EUR'), amt.annotation.price)
+assertEqual(ledger.Amount('140 EUR'), amt2.annotation.price)
+assertEqual(ledger.Amount('14016,8 EUR'), amt2.price()) # european amount!
 
+###############################################################################
+#
 # VALUES
+#
+# As common as Amounts and Balances are, there is a more prevalent numeric
+# type you will encounter when generating reports: Value objects.  A Value is
+# a variadic type that can be any of the following types:
+#   - Amount
+#   - Balance
+#   - boolean
+#   - integer
+#   - datetime
+#   - date
+#   - string
+#   - regex
+#   - sequence
+#
+# The reason for the variadic type is that it supports dynamic self-promotion.
+# For example, it is illegal to add two Amounts of different commodities, but
+# it is not illegal to add two Value amounts of different commodities.  In the
+# former case an exception in raised, but in the latter the Value simply
+# promotes itself to a Balance object to make the addition valid.
+#
+# Values are not used by any of Ledger's data objects (Journal, Transaction,
+# Posting or Account), but they are used extensively by value expressions.
 
 val = ledger.Value('$100.00')
 
diff --git a/src/py_commodity.cc b/src/py_commodity.cc
index dfaf7f5b..c201d370 100644
--- a/src/py_commodity.cc
+++ b/src/py_commodity.cc
@@ -224,6 +224,14 @@ namespace {
     return comm.strip_annotations(keep);
   }
 
+  boost::optional py_price(annotation_t& ann) {
+    return ann.price;
+  }
+  boost::optional py_set_price(annotation_t& ann,
+					 const boost::optional& price) {
+    return ann.price = price;
+  }
+
 } // unnamed namespace
 
 void export_commodity()
@@ -295,7 +303,7 @@ void export_commodity()
 
   map_value_type_converter();
 
-  scope().attr("commodity_pool") = commodity_pool_t::current_pool;
+  scope().attr("commodities") = commodity_pool_t::current_pool;
 
   scope().attr("COMMODITY_STYLE_DEFAULTS")  = COMMODITY_STYLE_DEFAULTS;
   scope().attr("COMMODITY_STYLE_SUFFIXED")  = COMMODITY_STYLE_SUFFIXED;
@@ -375,9 +383,7 @@ void export_commodity()
     .def("drop_flags", &supports_flags<>::drop_flags)
 #endif
 
-    .add_property("price",
-		  make_getter(&annotation_t::price),
-		  make_setter(&annotation_t::price))
+    .add_property("price", py_price, py_set_price)
     .add_property("date",
 		  make_getter(&annotation_t::date),
 		  make_setter(&annotation_t::date))
-- 
cgit v1.2.3


From aa086686ea2eb4efcb4684a48de8c15a97004a80 Mon Sep 17 00:00:00 2001
From: John Wiegley 
Date: Fri, 20 Nov 2009 17:40:15 -0500
Subject: Normalized the value() method for Python numerics

---
 src/py_amount.cc  | 16 ++++++++++------
 src/py_balance.cc | 31 ++++++++++---------------------
 src/py_value.cc   | 17 +++++++++++++++++
 3 files changed, 37 insertions(+), 27 deletions(-)

diff --git a/src/py_amount.cc b/src/py_amount.cc
index 2307c454..09d3294e 100644
--- a/src/py_amount.cc
+++ b/src/py_amount.cc
@@ -44,13 +44,16 @@ using namespace boost::python;
 
 namespace {
 
-  boost::optional
-  py_value_1(const amount_t& amount, commodity_t& in_terms_of) {
+  boost::optional py_value_0(const amount_t& amount) {
+    return amount.value(false, CURRENT_TIME());
+  }
+  boost::optional py_value_1(const amount_t& amount,
+				       commodity_t& in_terms_of) {
     return amount.value(false, CURRENT_TIME(), in_terms_of);
   }
-  boost::optional
-  py_value_2(const amount_t& amount, commodity_t& in_terms_of,
-	     datetime_t& moment) {
+  boost::optional py_value_2(const amount_t& amount,
+				       commodity_t& in_terms_of,
+				       datetime_t& moment) {
     return amount.value(false, moment, in_terms_of);
   }
 
@@ -226,7 +229,8 @@ internal precision."))
     .def("in_place_unreduce", &amount_t::in_place_unreduce,
 	 return_internal_reference<>())
 
-    .def("value", py_value_1, args("moment"))
+    .def("value", py_value_0)
+    .def("value", py_value_1, args("in_terms_of"))
     .def("value", py_value_2, args("in_terms_of", "moment"))
 
     .def("price", &amount_t::price)
diff --git a/src/py_balance.cc b/src/py_balance.cc
index 5aed2b43..760730a7 100644
--- a/src/py_balance.cc
+++ b/src/py_balance.cc
@@ -45,28 +45,18 @@ using namespace boost::python;
 namespace {
 
   boost::optional py_value_0(const balance_t& balance) {
-    return balance.value();
+    return balance.value(false, CURRENT_TIME());
   }
   boost::optional py_value_1(const balance_t& balance,
-				       const bool primary_only) {
-    return balance.value(primary_only);
+					commodity_t& in_terms_of) {
+    return balance.value(false, CURRENT_TIME(), in_terms_of);
   }
-
-  boost::optional
-  py_value_2(const balance_t& balance,
-	     const bool primary_only,
-	     const boost::optional& moment) {
-    return balance.value(primary_only, moment);
-  }
-
-  boost::optional
-  py_value_3(const balance_t& balance,
-	     const bool primary_only,
-	     const boost::optional& moment,
-	     const boost::optional& in_terms_of) {
-    return balance.value(primary_only, moment, in_terms_of);
+  boost::optional py_value_2(const balance_t& balance,
+					commodity_t& in_terms_of,
+					datetime_t& moment) {
+    return balance.value(false, moment, in_terms_of);
   }
-
+  
   boost::optional
   py_commodity_amount_0(const balance_t& balance) {
     return balance.commodity_amount();
@@ -200,9 +190,8 @@ void export_balance()
 	 return_internal_reference<>())
 
     .def("value", py_value_0)
-    .def("value", py_value_1, args("primary_only"))
-    .def("value", py_value_2, args("primary_only", "moment"))
-    .def("value", py_value_3, args("primary_only", "moment", "in_terms_of"))
+    .def("value", py_value_1, args("in_terms_of"))
+    .def("value", py_value_2, args("in_terms_of", "moment"))
 
     .def("price", &balance_t::price)
 
diff --git a/src/py_value.cc b/src/py_value.cc
index 98f3f079..1a77da72 100644
--- a/src/py_value.cc
+++ b/src/py_value.cc
@@ -47,6 +47,19 @@ BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(set_string_overloads, set_string, 0, 2)
 
 namespace {
 
+  boost::optional py_value_0(const value_t& value) {
+    return value.value(false, CURRENT_TIME());
+  }
+  boost::optional py_value_1(const value_t& value,
+				      commodity_t& in_terms_of) {
+    return value.value(false, CURRENT_TIME(), in_terms_of);
+  }
+  boost::optional py_value_2(const value_t& value,
+				      commodity_t& in_terms_of,
+				      datetime_t& moment) {
+    return value.value(false, moment, in_terms_of);
+  }
+
   PyObject * py_base_type(value_t& value)
   {
     if (value.is_boolean()) {
@@ -244,6 +257,10 @@ void export_value()
     .def("unreduced", &value_t::unreduced)
     .def("in_place_unreduce", &value_t::in_place_unreduce)
 
+    .def("value", py_value_0)
+    .def("value", py_value_1, args("in_terms_of"))
+    .def("value", py_value_2, args("in_terms_of", "moment"))
+
     .def("value", &value_t::value, value_overloads())
     .def("price", &value_t::price)
     .def("exchange_commodities", &value_t::exchange_commodities,
-- 
cgit v1.2.3


From 8c18f66decdc1ab8b95a5057c2c8e752f009a631 Mon Sep 17 00:00:00 2001
From: John Wiegley 
Date: Fri, 20 Nov 2009 18:03:35 -0500
Subject: Don't rely on the integral value of a boolean

---
 src/times.cc | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/times.cc b/src/times.cc
index e3ccaff8..d4317509 100644
--- a/src/times.cc
+++ b/src/times.cc
@@ -1321,10 +1321,13 @@ date_parser_t::lexer_t::token_t date_parser_t::lexer_t::next_token()
     catch (...) {}
   }
 
+  start = begin;
+
   string term;
   bool alnum = std::isalnum(*begin);
-  for (start = begin; (begin != end && ! std::isspace(*begin) &&
-		       alnum == std::isalnum(*begin)); begin++)
+  for (; (begin != end && ! std::isspace(*begin) &&
+	  ((alnum && static_cast(std::isalnum(*begin))) ||
+	   (! alnum && ! static_cast(std::isalnum(*begin))))); begin++)
     term.push_back(*begin);
 
   if (! term.empty()) {
-- 
cgit v1.2.3


From 87a1b1a6c3d698ba391b11d56ae2c896d4f9bb64 Mon Sep 17 00:00:00 2001
From: Aristid Breitkreuz 
Date: Fri, 20 Nov 2009 23:21:04 +0100
Subject: Several changes to the sample.dat file

---
 doc/sample.dat | 30 ++++++++++++++++++------------
 1 file changed, 18 insertions(+), 12 deletions(-)

diff --git a/doc/sample.dat b/doc/sample.dat
index 10a970db..12ac4cb4 100644
--- a/doc/sample.dat
+++ b/doc/sample.dat
@@ -1,38 +1,44 @@
+; -*- ledger -*-
+
 N $
 
-= account =~ /^Expenses:Books/
-    (Liabilities:Taxes)             -0.10
+= /^Expenses:Books/
+    (Liabilities:Taxes)                                -0.10
 
 ~ Monthly
-    Assets:Bank:Checking          $500.00
+    Assets:Bank:Checking                             $500.00
     Income:Salary
 
+~ Yearly
+    Expenses:Donations                               $100.00
+    Assets:Bank:Checking
+
 2004/05/01 * Checking balance
-    Assets:Bank:Checking        $1,000.00
+    Assets:Bank:Checking                           $1,000.00
     Equity:Opening Balances
 
 2004/05/03=2004/05/01 * Investment balance
-    Assets:Brokerage              50 AAPL @ $30.00
+    Assets:Brokerage                                 50 AAPL @ $30.00
     Equity:Opening Balances
 
 2004/05/14 * Páy dày
-    Assets:Bank:Checking          500.00€
+    Assets:Bank:Checking                             500.00€
     Income:Salary
 
 2004/05/14 * Another dày in which there is Páying
-    Asséts:Bánk:Chécking:Asséts:Bánk:Chécking          $500.00
+    Asséts:Bánk:Chécking:Asséts:Bánk:Chécking        $500.00
     Income:Salary
 
 2004/05/14 * Another dày in which there is Páying
-    Русский язык:Активы:Русский язык:Русский язык    $1000.00
+    Русский язык:Активы:Русский язык:Русский язык         $1000.00
     Income:Salary
 
 tag foo
 
 2004/05/27 Book Store
-    Expenses:Books                 $20.00
-    Expenses:Cards                 $40.00
-    Expenses:Docs                  $30.00
+    Expenses:Books                                    $20.00
+    Expenses:Cards                                    $40.00
+    Expenses:Docs                                     $30.00
     Liabilities:MasterCard
 
 end tag
@@ -40,7 +46,7 @@ end tag
 2004/05/27 (100) Credit card company
     ; This is an xact note!
     ; Sample: Value
-    Liabilities:MasterCard         $20.00
+    Liabilities:MasterCard                            $20.00
     ; This is a posting note!
     ; Sample: Another Value
     ; :MyTag:
-- 
cgit v1.2.3


From ef5c0b37d5be4e9d3b66607d8c8f5e2d2873d5be Mon Sep 17 00:00:00 2001
From: John Wiegley 
Date: Fri, 20 Nov 2009 18:24:22 -0500
Subject: Added baseline test for "sample.dat" contents

---
 test/baseline/test-sample.dat | 91 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 91 insertions(+)
 create mode 100644 test/baseline/test-sample.dat

diff --git a/test/baseline/test-sample.dat b/test/baseline/test-sample.dat
new file mode 100644
index 00000000..4bfbe1e2
--- /dev/null
+++ b/test/baseline/test-sample.dat
@@ -0,0 +1,91 @@
+reg
+<<<
+; -*- ledger -*-
+
+N $
+
+= /^Expenses:Books/
+    (Liabilities:Taxes)                                -0.10
+
+~ Monthly
+    Assets:Bank:Checking                             $500.00
+    Income:Salary
+
+~ Yearly
+    Expenses:Donations                               $100.00
+    Assets:Bank:Checking
+
+2004/05/01 * Checking balance
+    Assets:Bank:Checking                           $1,000.00
+    Equity:Opening Balances
+
+2004/05/03=2004/05/01 * Investment balance
+    Assets:Brokerage                                 50 AAPL @ $30.00
+    Equity:Opening Balances
+
+2004/05/14 * Páy dày
+    Assets:Bank:Checking                             500.00€
+    Income:Salary
+
+2004/05/14 * Another dày in which there is Páying
+    Asséts:Bánk:Chécking:Asséts:Bánk:Chécking        $500.00
+    Income:Salary
+
+2004/05/14 * Another dày in which there is Páying
+    Русский язык:Активы:Русский язык:Русский язык         $1000.00
+    Income:Salary
+
+tag foo
+
+2004/05/27 Book Store
+    Expenses:Books                                    $20.00
+    Expenses:Cards                                    $40.00
+    Expenses:Docs                                     $30.00
+    Liabilities:MasterCard
+
+end tag
+
+2004/05/27 (100) Credit card company
+    ; This is an xact note!
+    ; Sample: Value
+    Liabilities:MasterCard                            $20.00
+    ; This is a posting note!
+    ; Sample: Another Value
+    ; :MyTag:
+    Assets:Bank:Checking
+    ; :AnotherTag:
+>>>1
+04-May-01 Checking balance      Assets:Bank:Checking      $1,000.00    $1,000.00
+                                Eq:Opening Balances      $-1,000.00            0
+04-May-03 Investment balance    Assets:Brokerage            50 AAPL      50 AAPL
+                                Eq:Opening Balances      $-1,500.00   $-1,500.00
+                                                                         50 AAPL
+04-May-14 Páy dày               Assets:Bank:Checking        500.00€   $-1,500.00
+                                                                         50 AAPL
+                                                                         500.00€
+                                Income:Salary              -500.00€   $-1,500.00
+                                                                         50 AAPL
+04-May-14 Another dày in whic.. ..Bá:Ch:As:Bá:Chécking      $500.00   $-1,000.00
+                                                                         50 AAPL
+                                Income:Salary              $-500.00   $-1,500.00
+                                                                         50 AAPL
+04-May-14 Another dày in whic.. Ру:Ак:Ру:Русский язык     $1,000.00     $-500.00
+                                                                         50 AAPL
+                                Income:Salary            $-1,000.00   $-1,500.00
+                                                                         50 AAPL
+04-May-27 Book Store            Expenses:Books               $20.00   $-1,480.00
+                                                                         50 AAPL
+                                Expenses:Cards               $40.00   $-1,440.00
+                                                                         50 AAPL
+                                Expenses:Docs                $30.00   $-1,410.00
+                                                                         50 AAPL
+                                Liabilities:MasterCard      $-90.00   $-1,500.00
+                                                                         50 AAPL
+                                (Liabilities:Taxes)          $-2.00   $-1,502.00
+                                                                         50 AAPL
+04-May-27 Credit card company   Liabilities:MasterCard       $20.00   $-1,482.00
+                                                                         50 AAPL
+                                Assets:Bank:Checking        $-20.00   $-1,502.00
+                                                                         50 AAPL
+>>>2
+=== 0
-- 
cgit v1.2.3


From 4e6ec09e4d2a69dcb06627e44512980b09561448 Mon Sep 17 00:00:00 2001
From: John Wiegley 
Date: Fri, 20 Nov 2009 18:29:49 -0500
Subject: Updated test/input/sample.dat

---
 test/input/sample.dat | 34 ++++++++++++++++++++++------------
 1 file changed, 22 insertions(+), 12 deletions(-)

diff --git a/test/input/sample.dat b/test/input/sample.dat
index 002d20ee..12ac4cb4 100644
--- a/test/input/sample.dat
+++ b/test/input/sample.dat
@@ -1,42 +1,52 @@
+; -*- ledger -*-
+
 N $
 
-= account =~ /^Expenses:Books/
-    (Liabilities:Taxes)             -0.10
+= /^Expenses:Books/
+    (Liabilities:Taxes)                                -0.10
 
 ~ Monthly
-    Assets:Bank:Checking          $500.00
+    Assets:Bank:Checking                             $500.00
     Income:Salary
 
+~ Yearly
+    Expenses:Donations                               $100.00
+    Assets:Bank:Checking
+
 2004/05/01 * Checking balance
-    Assets:Bank:Checking        $1,000.00
+    Assets:Bank:Checking                           $1,000.00
     Equity:Opening Balances
 
 2004/05/03=2004/05/01 * Investment balance
-    Assets:Brokerage              50 AAPL @ $30.00
+    Assets:Brokerage                                 50 AAPL @ $30.00
     Equity:Opening Balances
 
 2004/05/14 * Páy dày
-    Assets:Bank:Checking          500.00€
+    Assets:Bank:Checking                             500.00€
     Income:Salary
 
 2004/05/14 * Another dày in which there is Páying
-    Asséts:Bánk:Chécking:Asséts:Bánk:Chécking          $500.00
+    Asséts:Bánk:Chécking:Asséts:Bánk:Chécking        $500.00
     Income:Salary
 
 2004/05/14 * Another dày in which there is Páying
-    Русский язык:Русский язык:Русский язык:Русский язык  $1000.00
+    Русский язык:Активы:Русский язык:Русский язык         $1000.00
     Income:Salary
 
+tag foo
+
 2004/05/27 Book Store
-    Expenses:Books                 $20.00
-    Expenses:Cards                 $40.00
-    Expenses:Docs                  $30.00
+    Expenses:Books                                    $20.00
+    Expenses:Cards                                    $40.00
+    Expenses:Docs                                     $30.00
     Liabilities:MasterCard
 
+end tag
+
 2004/05/27 (100) Credit card company
     ; This is an xact note!
     ; Sample: Value
-    Liabilities:MasterCard         $20.00
+    Liabilities:MasterCard                            $20.00
     ; This is a posting note!
     ; Sample: Another Value
     ; :MyTag:
-- 
cgit v1.2.3