Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

An os.sched_param object can be dumped with pickle, but loading raises TypeError #126303

Closed
Willy19921992 opened this issue Nov 1, 2024 · 3 comments
Labels
topic-multiprocessing type-bug An unexpected behavior, bug, or error

Comments

@Willy19921992
Copy link

Willy19921992 commented Nov 1, 2024

Bug report

Bug description:

While working on an experiment with different scheduler priorities and the multiprocessing.Pool, I found out that using an os.sched_param object as one of the initargs parameters raised the following exception:

Traceback (most recent call last):
  File "/usr/lib/python3.12/multiprocessing/forkserver.py", line 274, in main
    code = _serve_one(child_r, fds,
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/multiprocessing/forkserver.py", line 313, in _serve_one
    code = spawn._main(child_r, parent_sentinel)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/multiprocessing/spawn.py", line 132, in _main
    self = reduction.pickle.load(from_parent)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: sched_param() takes at most 1 argument (2 given)

With a bit of investigation using the REPL, I quickly found out a simple way to reproduce the problem:

Python 3.12.7 (main, Oct  1 2024, 11:15:50) [GCC 14.2.1 20240910] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> import pickle
>>> with open('schedparam', 'wb') as f:
...     pickle.dump(os.sched_param(99), f)
... 
>>> with open('schedparam', 'rb') as f:
...     pickle.load(f)
... 
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: sched_param() takes at most 1 argument (2 given)

The exact value used in the os.sched_param constructor doesn't affect the result. My specific use case is in Linux, where I'm trying to create a pool of processes where each one uses os.sched_setscheduler as an initializer to change their own scheduling policy to something different from the parent process and see the resulting behavior.

Im using Arch Linux with the package python3 3.12.7-1, which is the latest version as the time of writting. I have also found that this issue is also present in Pypy, if it is of any use.

CPython versions tested on:

3.12

Operating systems tested on:

Linux

Linked PRs

@Willy19921992 Willy19921992 added the type-bug An unexpected behavior, bug, or error label Nov 1, 2024
@xooxo
Copy link

xooxo commented Nov 2, 2024

I took a look into this with pickletools (on Debian 12 Python 3.11.2). It seems that the problem is pickle creates tuple with 99 and an empty dictionary for keyword arguments.

>>> import pickletools
>>> with open("schedparam", "rb") as f:
...     pickletools.dis(f)
... 
    0: \x80 PROTO      4
    2: \x95 FRAME      35
   11: \x8c SHORT_BINUNICODE 'posix'
   18: \x94 MEMOIZE    (as 0)
   19: \x8c SHORT_BINUNICODE 'sched_param'
   32: \x94 MEMOIZE    (as 1)
   33: \x93 STACK_GLOBAL
   34: \x94 MEMOIZE    (as 2)
   35: K    BININT1    99
   37: \x85 TUPLE1
   38: \x94 MEMOIZE    (as 3)
   39: }    EMPTY_DICT
   40: \x94 MEMOIZE    (as 4)
   41: \x86 TUPLE2
   42: \x94 MEMOIZE    (as 5)
   43: R    REDUCE
   44: \x94 MEMOIZE    (as 6)
   45: .    STOP

@picnixz
Copy link
Contributor

picnixz commented Nov 2, 2024

cc @serhiy-storchaka as the pickle expert

@serhiy-storchaka
Copy link
Member

os.sched_param is a type created by PyStructSequence_NewType(). But it has special constructor, different from common PyStructSequence constructors. The __reduce__ method returns value that works with standard PyStructSequence constructor.

There are two ways to fix this issue:

  • Change the custom constructor to accept also PyStructSequence-compatible signature.
  • Override the __reduce__ method.

The former is more complex and error-prone. The latter needs adding internal C API for overriding methods specified by PyMethodDef. I hope this is enough.

miss-islington pushed a commit to miss-islington/cpython that referenced this issue Nov 5, 2024
…ythonGH-126336)

(cherry picked from commit d384050)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
serhiy-storchaka added a commit to serhiy-storchaka/cpython that referenced this issue Nov 5, 2024
…jects (pythonGH-126336)

(cherry picked from commit d384050)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
serhiy-storchaka added a commit that referenced this issue Nov 5, 2024
…H-126336) (GH-126423)

(cherry picked from commit d384050)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic-multiprocessing type-bug An unexpected behavior, bug, or error
Projects
Status: Done
Development

No branches or pull requests

4 participants