Repository navigation
fix(contrib/sge): fix bytes pickle serialization in _dump and guard fstatvfs - #3477
reginaldalfret wants to merge 2 commits into
Conversation
…statvfs - In Python 3, pickle.dumps produces bytes. When running under __main__, rewriting module name with str raised TypeError, as did writing bytes to a file opened in text mode. Use protocol 0 with byte replacement and open with 'wb'. - Guard os.fstatvfs call against missing attribute or OSError on platforms such as Windows. Signed-off-by: reginaldalfret <reginaldalfret@gmail.com>
Signed-off-by: reginaldalfret <reginaldalfret@gmail.com>
Manishram-ai
left a comment
There was a problem hiding this comment.
Thanks! The bytes handling in _dump looks right. protocol=0 matters here, since protocol 2 writes c__main__ without the ( and protocol 4 and up don't write it at all, so a short comment saying that would help the next person.
A few things:
-
The other end still reads the pickle in text mode.
sge_runner.py:53opensjob-instance.picklewith"r", andpickle.loadon that raisesTypeError: a bytes-like object is required, not 'str', so the job still fails on the compute node. Changing it to"rb"would finish the fix. The older #2480 had that change too. -
The new test doesn't reach the rename.
task.__module__ = "__main__"only sets it on the instance, so the class still pickles assge_testand thereplacedoesn't change anything. A class defined in__main__does hit it:main_mod = sys.modules["__main__"] cls = type("MainModuleJobTask", (TestJobTask,), {"__module__": "__main__"}) setattr(main_mod, "MainModuleJobTask", cls) try: task = cls(i="2", n_cpu=1) with tempfile.TemporaryDirectory() as tmp_dir, patch.object(sys, "argv", ["my_pipeline.py"]): task._dump(tmp_dir) with open(task.job_file, "rb") as f: content = f.read() self.assertNotIn(b"(c__main__", content) self.assertIn(b"(cmy_pipeline\nMainModuleJobTask", content) finally: delattr(main_mod, "MainModuleJobTask")
That fails on master and passes here.
-
About
fstatvfs:os.fstatvfs(0)looks at stdin's filesystem, notshared_tmp_dir, and with stdin closed it raisesEBADF, so catchingOSErrormakes sense.f_namemaxis also the limit for one path component, but the code cuts the whole path to that length.os.statvfs(base_tmp_dir)and trimming onlyfolder_name(likelsf.pytrims only the task name) would fix both. That could be a follow-up too.
FYI #3469 changes _dump the same way but without protocol=0, so its rename doesn't do anything.
Description
This PR fixes two compatibility issues in
luigi.contrib.sge:Python 3 Bytes Serialization Error in
_dump():When
self.__module__ == "__main__",pickle.dumps(self)returnsbytes. The code previously attempted string replacementd.replace("(c__main__", "(c" + module_name)and wrote to a file opened with mode"w". Both operations raiseTypeErrorin Python 3 (a bytes-like object is required, not 'str'andwrite() argument must be str, not bytes).We now explicitly specify protocol 0, perform byte replacement, and open the job file in
"wb"mode.AttributeError / OSError with
os.fstatvfs:In
_init_local(),os.fstatvfs(0)is called to queryf_namemax. On Windows or platforms/environments wherefstatvfsis not supported, this raisesAttributeError: module 'os' has no attribute 'fstatvfs'.We now defensively catch
AttributeErrorandOSErrorso_init_local()proceeds cleanly.Testing
test_dump_main_moduleintest/contrib/sge_test.py.test/contrib/sge_test.pypass.