Generated python code initializes static variables in the wrong order
Environment macos monterey 12.1 haxe 4.2.4 utest 1.13.2 haxe-concurrent 3.0.2
The issue can be reproduced by creating a test case like the following one (even if the issue is triggered by the interaction between the libraries utest and haxe-concurrent, I don’t think that it is caused by them as I’m explaining below)
package foo;
import utest.Assert;
class TestCase extends utest.Test {
static var ex = hx.concurrent.executor.Executor.create();
function testFoo() {
Assert.pass();
}
}
The reported error is
Traceback (most recent call last):
File "/Users/foo/temp/bin/python/main.py", line 6819, in <module>
foo_TestCase.ex = hx_concurrent_executor_Executor.create()
File "/Users/foo/temp/bin/python/main.py", line 745, in create
return hx_concurrent_executor_ThreadPoolExecutor(maxConcurrent,autostart)
File "/Users/foo/temp/bin/python/main.py", line 819, in __init__
super().__init__()
File "/Users/foo/temp/bin/python/main.py", line 730, in __init__
super().__init__()
File "/Users/foo/temp/bin/python/main.py", line 622, in __init__
haxe_Log.trace((("[" + Std.string(self)) + "] instantiated."),_hx_AnonObject({'fileName': "hx/concurrent/Service.hx", 'lineNumber': 57, 'className': "hx.concurrent.ServiceBase", 'methodName': "new"}))
File "/Users/foo/temp/bin/python/main.py", line 1978, in trace
python_Lib.printString((("" + ("null" if str1 is None else str1)) + HxOverrides.stringOrNull(python_Lib.lineEnd)))
AttributeError: type object 'python_Lib' has no attribute 'lineEnd'
Error: Command failed with error 1
The last part of the generated python file is this
foo_TestCase.ex = hx_concurrent_executor_Executor.create()
hx_concurrent_ConcurrentException.INDENT = " "
hx_concurrent_executor_ScheduleTools.HOUR_IN_MS = 3600000
hx_concurrent_executor_ScheduleTools.DAY_IN_MS = 86400000
hx_concurrent_executor_ScheduleTools.WEEK_IN_MS = 604800000
hx_concurrent_lock_RLock.isSupported = True
python_Lib.lineEnd = ("\r\n" if ((Sys.systemName() == "Windows")) else "\n")
utest_TestHandler.POLLING_TIME = 10
utest_AccessoryName.SETUP_NAME = "setup"
utest_AccessoryName.SETUP_CLASS_NAME = "setupClass"
utest_AccessoryName.TEARDOWN_NAME = "teardown"
utest_AccessoryName.TEARDOWN_CLASS_NAME = "teardownClass"
foo_TestAll.main()
sys_thread__Thread_Thread_Impl_.processEvents()
As you can see the problem is that the executor hx_concurrent_executor_Executor, which internally uses python_Lib.lineEnd for logging, is created before the variable lineEnd is assigned.
That might be undefined behavior depending on what that Executor.create function does exactly.
But Executor.create prints only some logging messages. In general term, I think that if a class A depends on a class B, haxe should emit code where the static fields of B are initialized before the static fields on A.
Could you link that function or paste its implementation?
Sure.
Executor.create calls the constructor of hx.concurrent.ServiceBase, that in python is implemented in the following way
def __init__(self):
self._stateLock = hx_concurrent_lock_RLock()
self.state = hx_concurrent_ServiceState.STOPPED
self.id = hx_concurrent_ServiceBase._ids.incrementAndGet()
haxe_Log.trace((("[" + Std.string(self)) + "] instantiated."),_hx_AnonObject({'fileName': "hx/concurrent/Service.hx", 'lineNumber': 57, 'className': "hx.concurrent.ServiceBase", 'methodName': "new"}))
The implementation of haxe.Log.trace is
@staticmethod
def trace(v,infos = None):
_hx_str = haxe_Log.formatOutput(v,infos)
str1 = Std.string(_hx_str)
python_Lib.printString((("" + ("null" if str1 is None else str1)) + HxOverrides.stringOrNull(python_Lib.lineEnd)))
and the implementation of lineEnd is
python_Lib.lineEnd = ("\r\n" if ((Sys.systemName() == "Windows")) else "\n")
But as you can see in the fragment of the global python file in the first post, the statement foo_TestCase.ex = hx_concurrent_executor_Executor.create() is called before the statement python_Lib.lineEnd = ("\r\n" if ((Sys.systemName() == "Windows")) else "\n") causing the exception AttributeError: type object 'python_Lib' has no attribute 'lineEnd'.
Ah yeah, trace is quite special. Not sure what we can do here without some sort of hack. But I agree that this is pretty unfortunate.
I think #5592 is a very similar issue (the static field keywords being initialized too late and causing a crash).
A simple workaround is to write getters for those static fields and give them values on the spot if they're null.
Same error after transpile to Python when using trace. haxe 4.2.5 utest 1.13.2
@ilevd @acarioni @Simn a temporary workaround is to add a command to your .hxml to include the missing lines in classes after building.
Using SQlite/record-macros triggers this error reliably, and using sed to add the static declarations inside the class make the generated Python code work.
--cmd sed -i '/_hx_class_name = "python.Boot"/c\ _hx_class_name = "python.Boot"\n keywords = set(["and", "del", "from", "not", "with", "as", "elif", "global", "or", "yield", "assert", "else", "if", "pass", "None", "break", "except", "import", "raise", "True", "class", "exec", "in", "return", "False", "continue", "finally", "is", "try", "def", "for", "lambda", "while"])\n prefixLength = len("_hx_")' main.py
(To anyone reading this, replace main.py with your python target file name)