gnucash-devel Digest, Vol 20, Issue 7
array_hourly_0u at icloud.com
array_hourly_0u at icloud.com
Fri Mar 6 16:12:31 EST 2026
Hi All,
"PR1" in the below plan is addressed with:
https://github.com/Gnucash/gnucash/pull/2186
And I decided to do both "PR2" and "PR3" as:
https://github.com/Gnucash/gnucash/pull/2187
I look forward to your thoughts.
Cheers,
Noah
> On Feb 9, 2026, at 11:22 AM, array_hourly_0u at icloud.com wrote:
>
> John,
> Thanks for the guidance on SWIG typemaps -- while new to me, after
> some learning, I think that's the right mechanism and I appreciate you
> pointing me in that direction. The Python bindings barely use input
> typemaps today, so there's a real opportunity to improve the
> infrastructure here.
> After studying the existing typemap patterns in the codebase (the
> GncOwner input/output typemaps in gnucash_core.i, the time64
> multi-type acceptance in time64.i, and the GList output typemap in
> base-typemaps.i), I believe we can do this with no breaking changes
> and no need for deprecation. The approach is a %typemap(in) for each
> pointer type that tries the normal SWIG pointer conversion first, and
> only if that fails, checks for a .instance attribute (which is how the
> Python wrapper classes store their underlying SWIG pointer). The
> normal code path has zero overhead -- the fallback only triggers when
> someone passes a wrapper object to a gnucash_core_c function (and we
> could introduce a deprecation warning in this pathway).
> I'm estimating three PRs:
> PR 1 -- SWIG typemap compatibility layer. A %define macro
> (GNC_ACCEPT_WRAPPER) in gnucash_core.i that generates input typemaps
> for each core pointer type (GNCPrice *, gnc_commodity *, Account *,
> Split *, etc.). This is pure infrastructure -- no behavior changes, no
> risk. Includes tests verifying that wrapper objects are accepted by
> gnucash_core_c functions.
> PR 2 -- Fix the return-type wrapping. Add the missing
> methods_return_instance dict entries for GncPrice, GncPriceDB,
> GncCommodity, Account, and GncLot. With the typemaps from PR 1 in
> place, both old-style (gnucash_core_c direct calls) and new-style
> (wrapper methods) code works. Includes tests for each newly-wrapped
> method.
> PR 3 -- Clean up example scripts. Remove the type(x).__name__ ==
> 'SwigPyObject' workarounds from the shipped examples
> (gnc_convenience.py, gncinvoicefkt.py, str_methods.py).
> PRs 1 and 2 could both target 5.15 (except I have no idea the timeline
> for that). I'll start with PR 1.
> 5.16 or later: Optionally remove the fallback to pointer, or just
> leave it forever since it costs nothing
> Cheers,
> Noah
> ----------------------------------------------------------------------
>
> Message: 1
> Date: Sat, 7 Feb 2026 13:56:12 -0800
> From: array_hourly_0u at icloud.com
> To: gnucash-devel at gnucash.org
> Subject: Python bindings -- many methods return raw SWIG pointers
> instead of wrapped Python objects
> Message-ID: <D4307C66-C28B-46F0-939B-42856175604F at icloud.com>
> Content-Type: text/plain; charset="UTF-8"
>
> Hi All,
> I've been a happy GnuCash user for 13 years and am recently finding
> more time and interest to take on some more advanced use cases and
> also contribute to the GnuCash project. (and more bandwidth thanks to
> AI assisted coding)
>
> My projects all involve use of the official API python bindings. So
> you'll see some bug reports, PR, and other chatter from me about that.
>
> Here's a matter that's within my skillet to fix, but the best approach
> is debatable given one route involves breaking changes to the existing
> python bindings. So I wanted to seek other's opinions.
>
> Background
> -----------------------------
>
> The Python bindings use a two-layer architecture: SWIG auto-generates
> a low-level C API (gnucash_core_c), and gnucash_core.py wraps selected
> methods so they return proper Python objects (GncPrice, GncCommodity,
> etc.) instead of raw SWIG pointers. This wrapping is done via
> methods_return_instance() dicts that map method names to their return
> types.
>
> The problem is that these dicts are incomplete. Many methods that
> return pointers to GnuCash objects are exposed on the Python classes
> (via add_methods_with_prefix) but never registered for return-type
> wrapping. They silently return raw SwigPyObject pointers that have no
> Python methods and can only be used via gnucash_core_c C-level
> function calls.
>
> This is confusing because the methods *appear* to work -- they're
> callable and return non-None values -- but the return values are
> unusable through the documented Python API. There's no error, no
> warning, and no documentation indicating which methods are wrapped and
> which aren't. The only way to discover the problem is to inspect
> type() on the return value.
>
> I've done a systematic audit of all classes in gnucash_core.py and
> gnucash_business.py, cross-referencing the C functions exposed by SWIG
> against the methods_return_instance dicts, and empirically verified
> each finding. Below are the results.
>
>
> Affected Classes and Methods
> -----------------------------
>
> 1. GncPrice -- no wrapping dict exists at all
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
> GncPrice is the worst case. bindings/python/gnucash_core.py line 741
> calls add_methods_with_prefix('gnc_price_'), which exposes all
> gnc_price_* functions as methods. But there is no
> methods_return_instance call for GncPrice anywhere in that file -- not
> a single method has its return type wrapped.
>
> Method Currently returns Should return
> ---------------- -------------------------------- ----------------
> get_commodity() SwigPyObject (raw gnc_commodity *) GncCommodity
> get_currency() SwigPyObject (raw gnc_commodity *) GncCommodity
> clone(book) SwigPyObject (raw GNCPrice *) GncPrice
> get_value() _gnc_numeric (SWIG proxy) GncNumeric
>
> get_value() is a partial case -- the _gnc_numeric SWIG proxy is usable
> via GncNumeric(instance=val), but this is inconsistent with Split and
> Transaction where equivalent methods (GetAmount, GetValue, GetBalance,
> etc.) are all wrapped to return GncNumeric directly.
>
> Suggested fix:
>
> GncPrice.add_methods_with_prefix('gnc_price_')
>
> gnc_price_dict = {
> 'get_commodity': GncCommodity,
> 'get_currency': GncCommodity,
> 'clone': GncPrice,
> 'get_value': GncNumeric,
> }
> methods_return_instance(GncPrice, gnc_price_dict)
>
>
> 2. GncPriceDB -- PriceDB_dict incomplete
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
> The existing PriceDB_dict wraps lookup_latest,
> lookup_nearest_in_time64, lookup_nearest_before_t64, and some
> convert_balance methods. But several methods that return the same
> types are missing.
>
> Missing single-value wrappers (should return GncPrice):
>
> Method Currently returns
> Should return
> ---------------------------------------- ------------------------
> -----------
> nth_price(commodity, n) SwigPyObject (GNCPrice *)
> GncPrice
> lookup_day_t64(commodity, currency, date) SwigPyObject (GNCPrice *)
> GncPrice
>
> Missing single-value wrapper (should return GncNumeric):
>
> Method Currently returns
> Should return
> ----------------------------------------------- --------------------
> -----------
> convert_balance_nearest_before_price_t64(...) _gnc_numeric (raw)
> GncNumeric
>
> Its siblings convert_balance_latest_price and
> convert_balance_nearest_price_t64 are correctly wrapped.
>
> Missing list wrappers (should return list of GncPrice):
>
> Method Currently returns
> Should return
> --------------------------------------------------
> --------------------- ----------------
> lookup_latest_any_currency(commodity)
> list[SwigPyObject] list[GncPrice]
> lookup_nearest_before_any_currency_t64(comm, date)
> list[SwigPyObject] list[GncPrice]
> lookup_nearest_in_time_any_currency_t64(comm, date)
> list[SwigPyObject] list[GncPrice]
>
> Note: get_latest_price, get_nearest_price, and get_nearest_before_price
> are NOT bugs -- their C functions return gnc_numeric directly (the
> price value, not a GNCPrice *), so the raw _gnc_numeric return is the
> correct C type. They should arguably be wrapped to GncNumeric for
> consistency, but that's a lower priority.
>
> Suggested fix:
>
> PriceDB_dict = {
> 'lookup_latest': GncPrice,
> 'lookup_nearest_in_time64': GncPrice,
> 'lookup_nearest_before_t64': GncPrice,
> 'nth_price': GncPrice,
> 'lookup_day_t64': GncPrice,
> 'convert_balance_latest_price': GncNumeric,
> 'convert_balance_nearest_price_t64': GncNumeric,
> 'convert_balance_nearest_before_price_t64': GncNumeric,
> 'get_latest_price': GncNumeric,
> 'get_nearest_price': GncNumeric,
> 'get_nearest_before_price': GncNumeric,
> }
> methods_return_instance(GncPriceDB, PriceDB_dict)
>
> methods_return_instance_lists(GncPriceDB, {
> 'get_prices': GncPrice, # already done
> 'lookup_latest_any_currency': GncPrice, # new
> 'lookup_nearest_before_any_currency_t64': GncPrice, # new
> 'lookup_nearest_in_time_any_currency_t64': GncPrice, # new
> })
>
>
> 3. GncCommodity -- two missing wrappers
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
> GncCommodity.clone is correctly wrapped (gnucash_core.py line 979),
> but two other methods that return object pointers are not.
>
> Method Currently returns Should
> return
> ------------------ -----------------------------------------
> ----------------------
> obtain_twin(book) SwigPyObject (raw gnc_commodity *)
> GncCommodity
> get_namespace_ds() SwigPyObject (raw gnc_commodity_namespace *)
> GncCommodityNamespace
>
> Additionally, get_quote_source() and get_default_quote_source() return
> raw gnc_quote_source * pointers. However, gnc_quote_source has no
> Python wrapper class, so these are a deeper design gap rather than a
> simple omission -- there's nothing to wrap *to*. Currently the only
> way to use them is via
> gnucash_core_c.gnc_quote_source_get_internal_name(ptr)
> etc. A proper fix would require creating a new GncQuoteSource wrapper
> class.
>
>
> 4. Account -- one missing wrapper
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
> Method Currently returns Should
> return
> ------------------------ -------------------------------- ------------
> get_currency_or_parent() SwigPyObject (raw gnc_commodity *) GncCommodity
>
> The existing account_dict wraps GetCommodity -> GncCommodity but
> misses get_currency_or_parent, which returns the same C type.
>
>
> 5. GncLot -- two missing wrappers
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
> Method Currently returns Should return
> ---------------------- --------------------- ---------------
> get_balance_before(sp) raw _gnc_numeric GncNumeric
> get_split_list() list[SwigPyObject] list[Split]
>
> The existing gnclot_dict wraps get_balance -> GncNumeric but misses
> get_balance_before. And get_split_list needs
> method_function_returns_instance_list like Account.GetSplitList and
> Transaction.GetSplitList already have.
>
>
> The Breaking Change Problem
> ---------------------------
>
> Every fix listed above changes a method's return type from a raw SWIG
> pointer to a wrapped Python object. These are breaking changes: any
> existing code that passes these return values to gnucash_core_c
> C-level functions will break, because those functions expect the raw
> SWIG pointer, not a wrapper.
>
> For example, current workaround code looks like:
>
> from gnucash import gnucash_core_c as gc
>
> raw_price = pricedb.nth_price(commodity, 0)
> gc.gnc_price_get_currency(raw_price) # works today
> gc.gnc_price_get_time64(raw_price)
>
> After wrapping nth_price -> GncPrice:
>
> price = pricedb.nth_price(commodity, 0) # now returns GncPrice
> gc.gnc_price_get_currency(price) # BREAKS
> price.get_currency() # new correct usage
>
> The workaround-after-the-fix is to use .instance to extract the raw
> pointer:
>
> gc.gnc_price_get_currency(price.instance) # works
>
> How many users are affected?
>
> Anyone using these methods today has already discovered the raw-pointer
> problem through trial and error and written gnucash_core_c workarounds.
> These workarounds are undocumented and fragile. The "break" moves users
> from an undocumented workaround to the intended API.
>
> That said, the Python bindings have been in this state for years, and
> scripts using the C-level workarounds do exist in the wild (Stack
> Overflow answers, wiki examples, personal scripts).
>
>
> Possible Approaches
> -------------------
>
> I'd like the developers' input on how to handle this. Some options:
>
> Option A: Fix everything, accept the break
>
> Add all missing entries to the methods_return_instance dicts.
> Document the change in release notes. This is the cleanest long-term
> outcome but breaks existing workaround code silently (no error --
> just wrong types passed to C functions, likely causing segfaults or
> TypeError).
>
> Option B: Fix everything, add a compatibility shim
>
> Modify method_function_returns_instance (in function_class.py) so
> that wrapped objects are transparently accepted by gnucash_core_c
> functions. This could be done by making the wrapper classes implement
> __swig_convert__ or by patching process_list_convert_to_instance to
> unwrap at call boundaries. This would make both old and new code
> work, but adds complexity to the wrapping layer.
>
> Option C: Fix only the most impactful methods, leave the rest
>
> Prioritize the methods most likely to be encountered by users:
> - GncPrice.get_commodity(), .get_currency()
> - GncPriceDB.nth_price()
> - Account.get_currency_or_parent()
>
> Leave edge cases like GncLot.get_balance_before() and
> GncCommodity.obtain_twin() for later.
>
> Option D: Deprecation warnings first, fix later
>
> Add runtime deprecation warnings when unwrapped methods are called,
> pointing users to the upcoming change. Fix the return types in the
> next major release.
>
> ---
>
> My preference is Option A with clear release notes because the
> compatibility shim may be too complex. The current state is a usability
> trap -- methods look like they work but return unusable objects -- and
> fixing it benefits all future users even if it inconveniences the few
> who have written workarounds.
>
> I'm happy to submit patches for whichever approach the project prefers.
>
>
> Appendix: Methodology
> ---------------------
>
> All findings were verified empirically on GnuCash 5.14 built from
> source (Python 3.11, Debian Bookworm, -DWITH_PYTHON=ON
> -DWITH_GNUCASH=OFF). Each method was called against a test GnuCash
> SQLite file containing accounts, commodities, and prices. Return types
> were checked with type() and compared against the C function signatures
> in gnc-pricedb.h, gnc-commodity.h, Account.h, and gnc-lot.h.
>
> The full C API surface was enumerated via dir(gnucash_core_c) and
> cross-referenced against the methods_return_instance dicts in
> gnucash_core.py (lines 769-776, 960-974, 984-992, 1011-1020,
> 1029-1044, 1056-1074, 1085-1114) and gnucash_business.py.
>
>
> Sincerely,
>
> Noah Reddell
>
>
> ------------------------------
>
> Message: 2
> Date: Sat, 7 Feb 2026 15:40:07 -0800
> From: John Ralls <jralls at ceridwen.us>
> To: array_hourly_0u at icloud.com
> Cc: gnucash-devel at gnucash.org
> Subject: Re: Python bindings -- many methods return raw SWIG pointers
> instead of wrapped Python objects
> Message-ID: <7597CAE6-455F-4A25-81E0-14B993E148A1 at ceridwen.us>
> Content-Type: text/plain; charset=utf-8
>
>
>
> > On Feb 7, 2026, at 13:56, array_hourly_0u at icloud.com wrote:
> >
> > Hi All,
> > I've been a happy GnuCash user for 13 years and am recently finding
> > more time and interest to take on some more advanced use cases and
> > also contribute to the GnuCash project. (and more bandwidth thanks to
> > AI assisted coding)
> >
> > My projects all involve use of the official API python bindings. So
> > you'll see some bug reports, PR, and other chatter from me about that.
> >
> > Here's a matter that's within my skillet to fix, but the best approach
> > is debatable given one route involves breaking changes to the existing
> > python bindings. So I wanted to seek other's opinions.
> >
> > Background
> > -----------------------------
> >
> > The Python bindings use a two-layer architecture: SWIG auto-generates
> > a low-level C API (gnucash_core_c), and gnucash_core.py wraps selected
> > methods so they return proper Python objects (GncPrice, GncCommodity,
> > etc.) instead of raw SWIG pointers. This wrapping is done via
> > methods_return_instance() dicts that map method names to their return
> > types.
> >
> > The problem is that these dicts are incomplete. Many methods that
> > return pointers to GnuCash objects are exposed on the Python classes
> > (via add_methods_with_prefix) but never registered for return-type
> > wrapping. They silently return raw SwigPyObject pointers that have no
> > Python methods and can only be used via gnucash_core_c C-level
> > function calls.
> >
> > This is confusing because the methods *appear* to work -- they're
> > callable and return non-None values -- but the return values are
> > unusable through the documented Python API. There's no error, no
> > warning, and no documentation indicating which methods are wrapped and
> > which aren't. The only way to discover the problem is to inspect
> > type() on the return value.
> >
> > I've done a systematic audit of all classes in gnucash_core.py and
> > gnucash_business.py, cross-referencing the C functions exposed by SWIG
> > against the methods_return_instance dicts, and empirically verified
> > each finding. Below are the results.
> >
> >
> > Affected Classes and Methods
> > -----------------------------
> >
> > 1. GncPrice -- no wrapping dict exists at all
> > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> >
> > GncPrice is the worst case. bindings/python/gnucash_core.py line 741
> > calls add_methods_with_prefix('gnc_price_'), which exposes all
> > gnc_price_* functions as methods. But there is no
> > methods_return_instance call for GncPrice anywhere in that file -- not
> > a single method has its return type wrapped.
> >
> > Method Currently returns Should return
> > ---------------- -------------------------------- ----------------
> > get_commodity() SwigPyObject (raw gnc_commodity *) GncCommodity
> > get_currency() SwigPyObject (raw gnc_commodity *) GncCommodity
> > clone(book) SwigPyObject (raw GNCPrice *) GncPrice
> > get_value() _gnc_numeric (SWIG proxy) GncNumeric
> >
> > get_value() is a partial case -- the _gnc_numeric SWIG proxy is usable
> > via GncNumeric(instance=val), but this is inconsistent with Split and
> > Transaction where equivalent methods (GetAmount, GetValue, GetBalance,
> > etc.) are all wrapped to return GncNumeric directly.
> >
> > Suggested fix:
> >
> > GncPrice.add_methods_with_prefix('gnc_price_')
> >
> > gnc_price_dict = {
> > 'get_commodity': GncCommodity,
> > 'get_currency': GncCommodity,
> > 'clone': GncPrice,
> > 'get_value': GncNumeric,
> > }
> > methods_return_instance(GncPrice, gnc_price_dict)
> >
> >
> > 2. GncPriceDB -- PriceDB_dict incomplete
> > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> >
> > The existing PriceDB_dict wraps lookup_latest,
> > lookup_nearest_in_time64, lookup_nearest_before_t64, and some
> > convert_balance methods. But several methods that return the same
> > types are missing.
> >
> > Missing single-value wrappers (should return GncPrice):
> >
> > Method Currently returns
> > Should return
> > ---------------------------------------- ------------------------
> > -----------
> > nth_price(commodity, n) SwigPyObject (GNCPrice *)
> GncPrice
> > lookup_day_t64(commodity, currency, date) SwigPyObject (GNCPrice *)
> GncPrice
> >
> > Missing single-value wrapper (should return GncNumeric):
> >
> > Method Currently returns
> > Should return
> > ----------------------------------------------- --------------------
> > -----------
> > convert_balance_nearest_before_price_t64(...) _gnc_numeric (raw)
> > GncNumeric
> >
> > Its siblings convert_balance_latest_price and
> > convert_balance_nearest_price_t64 are correctly wrapped.
> >
> > Missing list wrappers (should return list of GncPrice):
> >
> > Method Currently returns
> > Should return
> > --------------------------------------------------
> > --------------------- ----------------
> > lookup_latest_any_currency(commodity)
> > list[SwigPyObject] list[GncPrice]
> > lookup_nearest_before_any_currency_t64(comm, date)
> > list[SwigPyObject] list[GncPrice]
> > lookup_nearest_in_time_any_currency_t64(comm, date)
> > list[SwigPyObject] list[GncPrice]
> >
> > Note: get_latest_price, get_nearest_price, and get_nearest_before_price
> > are NOT bugs -- their C functions return gnc_numeric directly (the
> > price value, not a GNCPrice *), so the raw _gnc_numeric return is the
> > correct C type. They should arguably be wrapped to GncNumeric for
> > consistency, but that's a lower priority.
> >
> > Suggested fix:
> >
> > PriceDB_dict = {
> > 'lookup_latest': GncPrice,
> > 'lookup_nearest_in_time64': GncPrice,
> > 'lookup_nearest_before_t64': GncPrice,
> > 'nth_price': GncPrice,
> > 'lookup_day_t64': GncPrice,
> > 'convert_balance_latest_price': GncNumeric,
> > 'convert_balance_nearest_price_t64': GncNumeric,
> > 'convert_balance_nearest_before_price_t64': GncNumeric,
> > 'get_latest_price': GncNumeric,
> > 'get_nearest_price': GncNumeric,
> > 'get_nearest_before_price': GncNumeric,
> > }
> > methods_return_instance(GncPriceDB, PriceDB_dict)
> >
> > methods_return_instance_lists(GncPriceDB, {
> > 'get_prices': GncPrice, # already
> done
> > 'lookup_latest_any_currency': GncPrice, # new
> > 'lookup_nearest_before_any_currency_t64': GncPrice, # new
> > 'lookup_nearest_in_time_any_currency_t64': GncPrice, # new
> > })
> >
> >
> > 3. GncCommodity -- two missing wrappers
> > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> >
> > GncCommodity.clone is correctly wrapped (gnucash_core.py line 979),
> > but two other methods that return object pointers are not.
> >
> > Method Currently returns Should
> return
> > ------------------ -----------------------------------------
> > ----------------------
> > obtain_twin(book) SwigPyObject (raw gnc_commodity *)
> GncCommodity
> > get_namespace_ds() SwigPyObject (raw gnc_commodity_namespace *)
> > GncCommodityNamespace
> >
> > Additionally, get_quote_source() and get_default_quote_source() return
> > raw gnc_quote_source * pointers. However, gnc_quote_source has no
> > Python wrapper class, so these are a deeper design gap rather than a
> > simple omission -- there's nothing to wrap *to*. Currently the only
> > way to use them is via
> gnucash_core_c.gnc_quote_source_get_internal_name(ptr)
> > etc. A proper fix would require creating a new GncQuoteSource wrapper
> > class.
> >
> >
> > 4. Account -- one missing wrapper
> > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> >
> > Method Currently returns Should
> return
> > ------------------------ --------------------------------
> ------------
> > get_currency_or_parent() SwigPyObject (raw gnc_commodity *)
> GncCommodity
> >
> > The existing account_dict wraps GetCommodity -> GncCommodity but
> > misses get_currency_or_parent, which returns the same C type.
> >
> >
> > 5. GncLot -- two missing wrappers
> > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> >
> > Method Currently returns Should return
> > ---------------------- --------------------- ---------------
> > get_balance_before(sp) raw _gnc_numeric GncNumeric
> > get_split_list() list[SwigPyObject] list[Split]
> >
> > The existing gnclot_dict wraps get_balance -> GncNumeric but misses
> > get_balance_before. And get_split_list needs
> > method_function_returns_instance_list like Account.GetSplitList and
> > Transaction.GetSplitList already have.
> >
> >
> > The Breaking Change Problem
> > ---------------------------
> >
> > Every fix listed above changes a method's return type from a raw SWIG
> > pointer to a wrapped Python object. These are breaking changes: any
> > existing code that passes these return values to gnucash_core_c
> > C-level functions will break, because those functions expect the raw
> > SWIG pointer, not a wrapper.
> >
> > For example, current workaround code looks like:
> >
> > from gnucash import gnucash_core_c as gc
> >
> > raw_price = pricedb.nth_price(commodity, 0)
> > gc.gnc_price_get_currency(raw_price) # works today
> > gc.gnc_price_get_time64(raw_price)
> >
> > After wrapping nth_price -> GncPrice:
> >
> > price = pricedb.nth_price(commodity, 0) # now returns GncPrice
> > gc.gnc_price_get_currency(price) # BREAKS
> > price.get_currency() # new correct usage
> >
> > The workaround-after-the-fix is to use .instance to extract the raw
> > pointer:
> >
> > gc.gnc_price_get_currency(price.instance) # works
> >
> > How many users are affected?
> >
> > Anyone using these methods today has already discovered the raw-pointer
> > problem through trial and error and written gnucash_core_c workarounds.
> > These workarounds are undocumented and fragile. The "break" moves users
> > from an undocumented workaround to the intended API.
> >
> > That said, the Python bindings have been in this state for years, and
> > scripts using the C-level workarounds do exist in the wild (Stack
> > Overflow answers, wiki examples, personal scripts).
> >
> >
> > Possible Approaches
> > -------------------
> >
> > I'd like the developers' input on how to handle this. Some options:
> >
> > Option A: Fix everything, accept the break
> >
> > Add all missing entries to the methods_return_instance dicts.
> > Document the change in release notes. This is the cleanest long-term
> > outcome but breaks existing workaround code silently (no error --
> > just wrong types passed to C functions, likely causing segfaults or
> > TypeError).
> >
> > Option B: Fix everything, add a compatibility shim
> >
> > Modify method_function_returns_instance (in function_class.py) so
> > that wrapped objects are transparently accepted by gnucash_core_c
> > functions. This could be done by making the wrapper classes implement
> > __swig_convert__ or by patching process_list_convert_to_instance to
> > unwrap at call boundaries. This would make both old and new code
> > work, but adds complexity to the wrapping layer.
> >
> > Option C: Fix only the most impactful methods, leave the rest
> >
> > Prioritize the methods most likely to be encountered by users:
> > - GncPrice.get_commodity(), .get_currency()
> > - GncPriceDB.nth_price()
> > - Account.get_currency_or_parent()
> >
> > Leave edge cases like GncLot.get_balance_before() and
> > GncCommodity.obtain_twin() for later.
> >
> > Option D: Deprecation warnings first, fix later
> >
> > Add runtime deprecation warnings when unwrapped methods are called,
> > pointing users to the upcoming change. Fix the return types in the
> > next major release.
> >
> > ---
> >
> > My preference is Option A with clear release notes because the
> > compatibility shim may be too complex. The current state is a usability
> > trap -- methods look like they work but return unusable objects -- and
> > fixing it benefits all future users even if it inconveniences the few
> > who have written workarounds.
> >
> > I'm happy to submit patches for whichever approach the project prefers.
> >
> >
> > Appendix: Methodology
> > ---------------------
> >
> > All findings were verified empirically on GnuCash 5.14 built from
> > source (Python 3.11, Debian Bookworm, -DWITH_PYTHON=ON
> > -DWITH_GNUCASH=OFF). Each method was called against a test GnuCash
> > SQLite file containing accounts, commodities, and prices. Return types
> > were checked with type() and compared against the C function signatures
> > in gnc-pricedb.h, gnc-commodity.h, Account.h, and gnc-lot.h.
> >
> > The full C API surface was enumerated via dir(gnucash_core_c) and
> > cross-referenced against the methods_return_instance dicts in
> > gnucash_core.py (lines 769-776, 960-974, 984-992, 1011-1020,
> > 1029-1044, 1056-1074, 1085-1114) and gnucash_business.py.
> >
>
>
> Thanks for being willing to take this on.
>
> Unfortunately we have no idea how many python bindings users there are nor
> how sophisticated any of them are about working around the bindings?
> limitations. Our general policy is that we wouldn?t remove API without at
> least a release or two worth of notice via deprecation warnings, so if we
> got deprecations in the upcoming 5.15 release we?d wait until the end of
> the year to remove the API. It?s also not ideal to deprecate API before the
> replacement is available making your option B the best choice.
>
> The breakage problem seems to me to be what SWIG typemaps are for, and it
> looks to me like the python bindings don?t make much use of typemaps. Did
> you consider that and if not can you?
>
> Regards,
> John Ralls
>
>
>
> ------------------------------
>
> Subject: Digest Footer
>
> _______________________________________________
> gnucash-devel mailing list
> gnucash-devel at gnucash.org
> https://lists.gnucash.org/mailman/listinfo/gnucash-devel
>
>
> ------------------------------
>
> End of gnucash-devel Digest, Vol 20, Issue 7
> ********************************************
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.gnucash.org/pipermail/gnucash-devel/attachments/20260306/425b24d5/attachment-0001.htm>
More information about the gnucash-devel
mailing list