Skip to content

unpickling an xmlrpc.client.Fault raises TypeError #71468

Open

Description

@UriOkrent
mannequin
BPO 27281
Nosy @loewis, @berkerpeksag, @serhiy-storchaka, @iritkatriel
Files
  • 0001-xmlrpc.client-make-Fault-pickleable.patch: pass Fault's init() args to the base class init()
  • 0001-xmlrpc.client-make-Errors-pickleable.patch
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = None
    created_at = <Date 2016-06-09.15:09:16.615>
    labels = ['type-bug', 'library', '3.11']
    title = 'unpickling an xmlrpc.client.Fault raises TypeError'
    updated_at = <Date 2021-11-26.18:53:47.180>
    user = 'https://bugs.python.org/UriOkrent'

    bugs.python.org fields:

    activity = <Date 2021-11-26.18:53:47.180>
    actor = 'iritkatriel'
    assignee = 'none'
    closed = False
    closed_date = None
    closer = None
    components = ['Library (Lib)']
    creation = <Date 2016-06-09.15:09:16.615>
    creator = 'Uri Okrent'
    dependencies = []
    files = ['43321', '43322']
    hgrepos = []
    issue_num = 27281
    keywords = ['patch']
    message_count = 8.0
    messages = ['268028', '268039', '268048', '268050', '268052', '268056', '268058', '407084']
    nosy_count = 5.0
    nosy_names = ['loewis', 'berker.peksag', 'serhiy.storchaka', 'Uri Okrent', 'iritkatriel']
    pr_nums = []
    priority = 'normal'
    resolution = None
    stage = 'patch review'
    status = 'open'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue27281'
    versions = ['Python 3.11']

    Linked PRs

    Activity

    1. UriOkrent commented on Jun 9, 2016

      UriOkrentmannequin
      MannequinAuthor

      Attempting to unpickle an xmlrpc.client.Fault will raise a TypeError:

      >>> import xmlrpc.client as xmlrpclib
      >>> f = xmlrpclib.Fault(42, 'Test Fault')
      >>> import pickle
      >>> s = pickle.dumps(f)
      >>> pickle.loads(s)
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      TypeError: __init__() missing 2 required positional arguments: 'faultCode' and 'faultString'

      Apparently unpickle relies on BaseException's args attribute when reconstructing an Exception class that inherits from BaseException (Fault inherits from Exception). Unfortunately, Fault implements __init__() and does call the base class constructor, but does not pass its arguments, so Fault.args is always an empty tuple.

      Simply passing Fault's arguments to the base class constructor allows it to be unpickled.

      I've included a patch for 3.x but the issue is present in 2.x as well (the code and fix are exactly the same except in xmlrpclib.py).

    2. added
      stdlibStandard Library Python modules in the Lib/ directory
      type-bugAn unexpected behavior, bug, or error
      on Jun 9, 2016
    3. berkerpeksag commented on Jun 9, 2016

      @berkerpeksag
      Member

      Looks good to me, thanks.

    4. serhiy-storchaka commented on Jun 9, 2016

      @serhiy-storchaka
      Member

      Why you need to pickle Fault?

    5. UriOkrent commented on Jun 9, 2016

      UriOkrentmannequin
      MannequinAuthor

      I'm not pickling/unpickling it directly, I'm using multiprocessing to handle queries to my server in worker processes which is using pickle to propagate exceptions raised in the worker to the parent.

      I could instead raise a different exception and wrap it in a Fault later (which is what I ended up doing to avoid the issue), but it seems like a case of "to-may-to" vs. "to-mah-to". Either way an Exception should be able to be propagated up to the parent in multiprocessing, whether it's my own or Fault.

    6. serhiy-storchaka commented on Jun 9, 2016

      @serhiy-storchaka
      Member

      This looks reasonable.

      But since Fault never was unpickleable, this issue is in gray zone between bug fixing and adding new feature. The latter can be done only in new Python version. I hesitate with classification of this issue.

      There are a lot of other exceptions that can't be unpickled (including xmlrpc.client.ProtocolError). This may be a part of larger issue.

      As for the patch itself, it solves the issue. But faultCode and faultString are saved twice (this is not a large problem for multiprocessing). Test should test pickling with all possible protocols (see how other pickle tests are written).

    7. UriOkrent commented on Jun 9, 2016

      UriOkrentmannequin
      MannequinAuthor

      My reading of the docs leads me to lean towards bug since this seems to break the contract of BaseException API in a standard lib module:

      https://docs.python.org/3/library/exceptions.html#BaseException says BaseExceptions have args and with_traceback so those probably should be preserved in all standard lib inheritors.

      This excerpt from the tutorial also seems to imply the same:
      https://docs.python.org/3/tutorial/errors.html#user-defined-exceptions

      "In this example, the default __init__() of Exception has been overridden. The new behavior simply creates the value attribute. This replaces the default behavior of creating the args attribute."

      The tutorial goes out of its way to note that the default behavior of creating args has been replaced in this simple example -- i.e., one would expect args to be present.

      As for your other comments, should I make a similar change to other inheritors of xmlrpc.client that inherit from xmlrpc.client.Error? Should I instead completely remove Fault's constructor and just make faultCode and faultString properties so the attributes aren't duplicated (that seems like it may be dangerous)?

      I will update the test for pickling with the other protocols.

    8. UriOkrent commented on Jun 9, 2016

      UriOkrentmannequin
      MannequinAuthor

      I updated the patch to improve the tests and made the same change to xmlrpc.client.ProtocolError (which was the only other place in xmlrpc.client). I'll let you decide if this patch is better despite the fact that it suffers from the same data duplication as Fault.

      If not, we can go back to just fixing Fault, or come up with a fancier solution entirely that doesn't duplicate data. Though, I hesitate to start getting too fancy for two reasons: 1) this module is old and stable, and 2) these are exception objects, so while the data duplication is ugly, the instances themselves should be short-lived.

    9. iritkatriel commented on Nov 26, 2021

      @iritkatriel
      Member

      Reproduced in 3.11.

    10. transferred this issue fromon Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    No one assigned

      Labels

      3.11only security fixesstdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

      Projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions