Fixing strange behavior of user property generation with Python

Problem was that integers are converted to "long long"
in Python, which creates Variants rendering "#l1" as
parsable strings. These are marked as invalid by the
Syntax highlighter of the user properties editor.

This patch fixes the user properties editor syntax
highlighter to cover all Variant variants and
uses "long" type for Variant if possible.
This commit is contained in:
Matthias Koefferlein 2026-08-10 00:09:42 +02:00
parent 877e1856b0
commit f8b83357e1
6 changed files with 57 additions and 4 deletions

View File

@ -1278,7 +1278,9 @@ static gsi::Class<A> decl_a ("", "A",
gsi::method ("ft_str", &A::ft_str) +
gsi::method ("ft_cv", &A::ft_cv) +
gsi::method ("ft_cptr", &A::ft_cptr) +
gsi::method ("ft_var", &A::ft_var)
gsi::method ("ft_var", &A::ft_var) +
gsi::method ("var2s", &A::var2s) +
gsi::method ("ll_size", &A::ll_size)
);
static gsi::Class<A_NC> decl_a_nc (decl_a, "", "A_NC");

View File

@ -419,6 +419,11 @@ struct A
static int sp_i_get ();
static void sp_i_set (int v);
// Variant to parsable string for Variant generation tests
static std::string var2s (const tl::Variant &v) { return v.to_parsable_string (); }
// platform specifics: length of "long long"
static size_t ll_size () { return sizeof (long long); }
// feed-through values for full cycle tests
// (mainly for string encoding and binary strings)
static std::string ft_str (const std::string &v) { return v; }

View File

@ -13,7 +13,8 @@
<DetectChar attribute="Raw String" char="'" context="Apostrophed Key String"/>
<RegExpr attribute="Float" String="##\s*\-?[0-9]([0-9]|_[0-9])*(\.[0-9]([0-9]|_[0-9])*)?([eE]\-?[1-9]([0-9]|_[0-9])*(\.[0-9]*)?)?" context="After Key"/>
<RegExpr attribute="Dec" String="#\s*\-?[1-9]([0-9]|_[0-9])*" context="After Key"/>
<RegExpr attribute="Dec" String="#\s*((l|u|lu)\s*)?\-?[1-9]([0-9]|_[0-9])*" context="After Key"/>
<RegExpr attribute="Special" String="(false|true|nil)" context="After Key"/>
<RegExpr attribute="Symbol" String="[_A-Za-z0-9]+\b" context="After Key"/>
<RegExpr attribute="Error" String="[^\s]" context="Error"/>
@ -31,7 +32,8 @@
<DetectChar attribute="Raw String" char="'" context="Apostrophed Value String"/>
<RegExpr attribute="Float" String="##\s*\-?[0-9]([0-9]|_[0-9])*(\.[0-9]([0-9]|_[0-9])*)?([eE]\-?[1-9]([0-9]|_[0-9])*(\.[0-9]*)?)?" context="After Value"/>
<RegExpr attribute="Dec" String="#\s*\-?[1-9]([0-9]|_[0-9])*" context="After Value"/>
<RegExpr attribute="Dec" String="#\s*((l|u|lu)\s*)?\-?[1-9]([0-9]|_[0-9])*" context="After Value"/>
<RegExpr attribute="Special" String="(false|true|nil)" context="After Value"/>
<RegExpr attribute="Symbol" String="[_A-Za-z0-9]+\b" context="After Value"/>
<RegExpr attribute="Normal" String="\s+" context="Value"/>
@ -77,6 +79,7 @@
<itemData name="Dec" defStyleNum="dsDecVal"/>
<itemData name="Float" defStyleNum="dsFloat"/>
<itemData name="Symbol" defStyleNum="dsString" color="#D40000"/>
<itemData name="Special" defStyleNum="dsString" color="#808080"/>
<itemData name="String" defStyleNum="dsString"/>
<itemData name="Raw String" defStyleNum="dsString" color="#DD4A4A" selColor="#DD4A4A"/>

View File

@ -270,7 +270,12 @@ tl::Variant python2c_func<tl::Variant>::operator() (PyObject *rval)
} else if (PyBool_Check (rval)) {
return tl::Variant (python2c<bool> (rval));
} else if (PyLong_Check (rval)) {
return tl::Variant (python2c<long long> (rval));
long long ll = python2c<long long> (rval);
if (ll >= (long long) std::numeric_limits<long>::min () && ll <= (long long) std::numeric_limits<long>::max ()) {
return tl::Variant ((long) ll);
} else {
return tl::Variant (ll);
}
#if PY_MAJOR_VERSION < 3
} else if (PyInt_Check (rval)) {
return tl::Variant (python2c<int> (rval));

View File

@ -3209,6 +3209,24 @@ class BasicTest(unittest.TestCase):
self.assertEqual(pya.A.ba_to_ia(b'\x00\x01\x02'), [ 0, 1, 2 ])
def test_variant_formation(self):
self.assertEqual(pya.A.var2s(1.5), "##1.5")
self.assertEqual(pya.A.var2s(-17), "#-17")
self.assertEqual(pya.A.var2s("abc"), "'abc'")
self.assertEqual(pya.A.var2s(None), "nil")
self.assertEqual(pya.A.var2s(True), "true")
self.assertEqual(pya.A.var2s(False), "false")
self.assertEqual(pya.A.var2s(pya.DBox(0, 0, 10, 20)), "[dbox:(0,0;10,20)]")
self.assertEqual(pya.A.var2s([ 0.5, "hello" ]), "(##0.5,'hello')")
self.assertEqual(pya.A.var2s(( 0.5, "hello" )), "(##0.5,'hello')")
self.assertEqual(pya.A.var2s([ 0.5, [ 1, 2 ] ]), "(##0.5,(#1,#2))")
self.assertEqual(pya.A.var2s({ 1: 'one', 'two': 17 }), "{#1=>'one','two'=>#17}")
if pya.A.ll_size() == 4:
self.assertEqual(pya.A.var2s(100000000000), "#l100000000000")
else:
self.assertEqual(pya.A.var2s(100000000000), "#100000000000")
# Tests multi-base mixins (only constants and enums available)
def test_multiBaseMixins(self):

View File

@ -3165,6 +3165,26 @@ class Basic_TestClass < TestBase
end
def test_variant_formation
assert_equal(RBA::A::var2s(1.5), "##1.5")
assert_equal(RBA::A::var2s(-17), "#-17")
assert_equal(RBA::A::var2s("abc"), "'abc'")
assert_equal(RBA::A::var2s(nil), "nil")
assert_equal(RBA::A::var2s(true), "true")
assert_equal(RBA::A::var2s(false), "false")
assert_equal(RBA::A::var2s(RBA::DBox::new(0, 0, 10, 20)), "[dbox:(0,0;10,20)]")
assert_equal(RBA::A::var2s([ 0.5, "hello" ]), "(##0.5,'hello')")
assert_equal(RBA::A::var2s([ 0.5, [ 1, 2 ] ]), "(##0.5,(#1,#2))")
assert_equal(RBA::A::var2s({ 1 => 'one', 'two' => 17 }), "{#1=>'one','two'=>#17}")
if RBA::A::ll_size == 4
assert_equal(RBA::A::var2s(100000000000), "#l100000000000")
else
assert_equal(RBA::A::var2s(100000000000), "#100000000000")
end
end
def test_optional
if RBA::B.respond_to?(:int_to_optional)