mirror of
https://github.com/Gnucash/gnucash.git
synced 2026-10-12 17:32:00 -05:00
[bindings/python] Deprecate set_source_string; remove it from the Guile bindings
gnc_price_set_source_string maps a source string to the PriceSource enum and silently leaves the source unchanged for anything it doesn't recognize -- a silent no-op a naive Python or Guile script can hit. Steer callers to set_source() with a PriceSource member (added in #2317) instead. Python bindings: - Deprecate GncPrice.set_source_string(): it now emits a DeprecationWarning pointing at set_source(PriceSource.<...>). It still works during the deprecation period and still guards -- if the string is not recognized (the round-tripped source no longer equals the input) it raises ValueError rather than silently doing nothing. - Fix the deprecated() decorator in deprecation.py: it referenced the undefined DeprecationWarnig and inspect.current_frame(), so it raised instead of warning (the one existing @deprecated use was latently broken). Guile bindings: - mark gnc-price-set-source-string deprecated in engine.i. (Python and Guile use separate SWIG interface files, so Python is handled separately). The only in-tree Guile caller is the QIF importer, updated to gnc-price-set-source with PRICE-SOURCE-USER-PRICE; the redundant set-source-string in test-engine-extras.scm's fixture helper is dropped. Tests (test_price_and_wrapping.py): - TestSetSourceStringGuard covers the guard (still raises on an unrecognized string) and the new DeprecationWarning. Removing set_source_string from the Python bindings is a follow-up.
This commit is contained in:
1 parent
973a64ceda
commit
0dc41c1212
6 files changed
+87
-11
No files matched your search
@@ -56,6 +56,7 @@
|
||||
|
||||
#include <numeric>
|
||||
#include <unordered_set>
|
||||
#include <libguile/deprecation.h>
|
||||
%}
|
||||
#if defined(SWIGGUILE) //Always C++
|
||||
%{
|
||||
@@ -359,6 +360,14 @@ functions. */
|
||||
%include <policy.h>
|
||||
%include <gnc-pricedb.h>
|
||||
|
||||
%feature("shadow") gnc_price_set_source_string %{
|
||||
(define (gnc_price_set_source_string price str)
|
||||
(issue-deprecation_warning
|
||||
"gnc-price-set-source-string is deprecated and will be removed in GnuCash 6."
|
||||
"Use gnc-price-set-source and the appropriate enum value instead.")
|
||||
($action price str))
|
||||
%}
|
||||
|
||||
QofSession * qof_session_new (QofBook* book);
|
||||
QofBook * qof_session_get_book (QofSession *session);
|
||||
// TODO: Unroll/remove
|
||||
|
||||
@@ -151,7 +151,6 @@
|
||||
(gnc-price-set-currency price currency)
|
||||
(gnc-price-set-time64 price time64)
|
||||
(gnc-price-set-source price PRICE-SOURCE-XFER-DLG-VAL)
|
||||
(gnc-price-set-source-string price "test-price")
|
||||
(gnc-price-set-typestr price "test")
|
||||
(gnc-price-set-value price value)
|
||||
(gnc-price-commit-edit price)
|
||||
|
||||
@@ -24,10 +24,10 @@ def deprecated(message):
|
||||
def wrapper(*args, **kwargs):
|
||||
warning_msg = 'Call to deprecated function {}. {}'.format(
|
||||
func.__name__, message)
|
||||
frame = inspect.current_frame().f_back
|
||||
frame = inspect.currentframe().f_back
|
||||
|
||||
warn_explicit(message,
|
||||
category=DeprecationWarnig,
|
||||
warn_explicit(warning_msg,
|
||||
category=DeprecationWarning,
|
||||
filename=inspect.getfile(frame.f_code),
|
||||
lineno=frame.f_lineno)
|
||||
return func(*args, **kwargs)
|
||||
|
||||
@@ -744,12 +744,9 @@ class GncPrice(GnuCashCoreClass):
|
||||
* currency: the denomination of the value of the item being priced.
|
||||
* value: the value of the item being priced.
|
||||
* time: the time the price was valid.
|
||||
* source: a string describing the source of the quote. These strings will be something like this:
|
||||
"Finance::Quote", "user:misc", "user:foo", etc. If the quote came from a user, as a matter of policy,
|
||||
you *must* prefix the string you give with "user:". For now, the only other reserved values are
|
||||
"Finance::Quote" and "old-file-import". Any string used must be added to the source_list array in
|
||||
dialog-price-edit-db.c so that it can be properly translated. (There are unfortunately many strings
|
||||
in users' databases, so this string must be translated on output instead of always being used in untranslated form).
|
||||
* source: describes how the price was created. The string form used by
|
||||
set_source_string() / get_source_string() must be one of the recognized
|
||||
PriceSource values; set_source_string() raises ValueError otherwise.
|
||||
* type: the type of quote - types possible right now are bid, ask, last, nav, and
|
||||
unknown.Each price in the database represents an "instantaneous" quote for a given
|
||||
commodity with respect to another commodity.
|
||||
@@ -760,6 +757,30 @@ class GncPrice(GnuCashCoreClass):
|
||||
_new_instance = 'gnc_price_create'
|
||||
GncPrice.add_methods_with_prefix('gnc_price_')
|
||||
|
||||
# Deprecate set_source_string() in favour of set_source() with a PriceSource
|
||||
# member. While it exists, guard it: the C setter maps a fixed set of canonical
|
||||
# strings to the PriceSource enum and silently leaves the source unchanged for
|
||||
# anything else, so a typo or obsolete string would change nothing -- detect
|
||||
# that (the round-tripped source no longer equals the input) and raise instead.
|
||||
_gnc_price_set_source_string = GncPrice.set_source_string
|
||||
def _deprecated_set_source_string(self, source):
|
||||
"""Deprecated: use set_source() with a PriceSource member.
|
||||
|
||||
Still raises ValueError if `source` is not a recognized PriceSource string
|
||||
(the underlying C setter would otherwise leave the source unchanged)."""
|
||||
_gnc_price_set_source_string(self, source)
|
||||
if self.get_source_string() != source:
|
||||
raise ValueError(
|
||||
"%r is not a recognized price source string, so set_source_string() "
|
||||
"left the source unchanged. Use set_source() with a PriceSource "
|
||||
"member, e.g. set_source(PriceSource.FINANCE_QUOTE)." % (source,))
|
||||
# Report the deprecation under the public method name, not the wrapper's.
|
||||
_deprecated_set_source_string.__name__ = 'set_source_string'
|
||||
_deprecated_set_source_string.__qualname__ = 'GncPrice.set_source_string'
|
||||
GncPrice.set_source_string = deprecated(
|
||||
"use set_source() with a PriceSource member, e.g. "
|
||||
"price.set_source(PriceSource.FINANCE_QUOTE)")(_deprecated_set_source_string)
|
||||
|
||||
|
||||
class GncPriceDB(GnuCashCoreClass):
|
||||
'''
|
||||
|
||||
@@ -488,5 +488,52 @@ class TestPriceSourceEnum(TestCase):
|
||||
ses.end()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test: GncPrice.set_source_string -- deprecated, but still guards (raises on
|
||||
# an unrecognized source) during the deprecation period
|
||||
# ---------------------------------------------------------------------------
|
||||
class TestSetSourceStringGuard(PriceSession):
|
||||
"""set_source_string() is deprecated in favour of set_source(PriceSource.*),
|
||||
but while it exists it must fail loud on an unrecognized source string
|
||||
instead of silently leaving the source unchanged (the C setter no-ops).
|
||||
These tests ignore the DeprecationWarning and check the guard behaviour;
|
||||
the warning itself is covered by test_emits_deprecation_warning."""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
# These tests deliberately call the deprecated method.
|
||||
self._warn_ctx = warnings.catch_warnings()
|
||||
self._warn_ctx.__enter__()
|
||||
warnings.simplefilter("ignore", DeprecationWarning)
|
||||
|
||||
def tearDown(self):
|
||||
self._warn_ctx.__exit__(None, None, None)
|
||||
super().tearDown()
|
||||
|
||||
def test_canonical_source_is_accepted(self):
|
||||
self.price1.set_source_string("Finance::Quote")
|
||||
self.assertEqual(self.price1.get_source_string(), "Finance::Quote")
|
||||
|
||||
def test_unknown_source_on_fresh_price_raises(self):
|
||||
price = GncPrice(self.book)
|
||||
with self.assertRaises(ValueError):
|
||||
price.set_source_string("not-a-real-source")
|
||||
|
||||
def test_unknown_source_does_not_clobber_existing(self):
|
||||
# The C setter no-ops on an unknown string; on a price that already has
|
||||
# a valid source that leaves the old value in place (so a bare "is the
|
||||
# source INVALID now?" check would miss it). The guard must still raise.
|
||||
self.price1.set_source_string("Finance::Quote")
|
||||
with self.assertRaises(ValueError):
|
||||
self.price1.set_source_string("totally-bogus")
|
||||
self.assertEqual(self.price1.get_source_string(), "Finance::Quote")
|
||||
|
||||
def test_emits_deprecation_warning(self):
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("error", DeprecationWarning)
|
||||
with self.assertRaises(DeprecationWarning):
|
||||
self.price1.set_source_string("Finance::Quote")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1438,7 +1438,7 @@
|
||||
(gnc-price-begin-edit gnc-price)
|
||||
(gnc-price-set-commodity gnc-price commodity)
|
||||
(gnc-price-set-currency gnc-price default-currency)
|
||||
(gnc-price-set-source-string gnc-price "user:price")
|
||||
(gnc-price-set-source gnc-price PRICE-SOURCE-USER-PRICE)
|
||||
|
||||
;; other options for type are "last" or "nav" which are
|
||||
;; the last known price for a stock or the net asset value
|
||||
|
||||
Reference in new issue
Block a user