Skip to content

singledispatchmethod should expose registry of all known overloads #87109

Open

Description

@Kentzo
mannequin
BPO 42943
Nosy @ncoghlan, @methane, @ambv, @Kentzo, @hongweipeng, @AlexWaygood
PRs
  • gh-87109: singledispatchmethod exposes registry and dispatch #30007
  • 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 2021-01-16.21:46:19.079>
    labels = ['type-feature', 'library', '3.11']
    title = 'singledispatchmethod should expose registry of all known overloads'
    updated_at = <Date 2021-12-09.15:14:16.576>
    user = 'https://github.com/Kentzo'

    bugs.python.org fields:

    activity = <Date 2021-12-09.15:14:16.576>
    actor = 'hongweipeng'
    assignee = 'none'
    closed = False
    closed_date = None
    closer = None
    components = ['Library (Lib)']
    creation = <Date 2021-01-16.21:46:19.079>
    creator = 'Ilya.Kulakov'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 42943
    keywords = ['patch']
    message_count = 1.0
    messages = ['385151']
    nosy_count = 6.0
    nosy_names = ['ncoghlan', 'methane', 'lukasz.langa', 'Ilya.Kulakov', 'hongweipeng', 'AlexWaygood']
    pr_nums = ['30007']
    priority = 'normal'
    resolution = None
    stage = 'patch review'
    status = 'open'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue42943'
    versions = ['Python 3.11']

    Activity

    1. Kentzo commented on Jan 16, 2021

      Kentzomannequin
      MannequinAuthor

      The wrapper created by singledispatchmethod does not (trivially) expose registry of all known overloads.
      Consider the following example:

          @singledispatchmethod
          def on_message(message):
              raise NotImplementedError
      
          @on_message.register
          def _(message: MessageA): ...
      
          @on_message.register
          def _(message: MessageB): ...
      loop = ...
      condition = ...
      messages = []
      
          def receive_message_on_thread(message):
              if ...:  # only signal to asyncio if message can be handled
                  messages.append(message)
                  loop.call_soon_threadsafe(condition.notify_all)
              else:
                  ...
      
          async def process_messages_on_asyncio():
              while True:
                  await condition.wait_for(lambda: len(messages) > 0)
      
                  while len(messages):
                      m = messages.pop(0)
                      ...

      Here messages are delivered outside of asyncio in a separate thread and thus they need to be forwarded into asyncio for further processing in the app. If the flow of messages is high it is desirable to filter inside the thread handler before signaling asyncio.

      There are multiple approaches to describe which messages can be handled by the asyncio processor, but singledispatchmethod is the most elegant as it allows to keep everything in one place without if/else or code duplication.

      Having a registry (trivially) exposed would allow to write the condition as isinstance(message, tuple(on_message.registry.keys())).

    2. added
      stdlibStandard Library Python modules in the Lib/ directory
      type-featureA feature request or enhancement
      on Jan 16, 2021
    3. added
      3.11only security fixes
      and removed on Nov 4, 2021
    4. transferred this issue fromon Apr 10, 2022
    5. serhiy-storchaka commented on Feb 24, 2024

      @serhiy-storchaka
      Member

      I do not think that isinstance(message, tuple(on_message.registry.keys())) is the most elegant way. Dispatching logic is more complex.

      You need to separate method dispatching step from method execution step. Since you process messages in asynchronous code, I think that most simple way is to make all message handlers asynchronous functions. Except the default handler.

          @singledispatchmethod
          def on_message(self, message):
              raise NotImplementedError # or return None, or whatever
      
          @on_message.register
          async def _(self, message: MessageA): ...
      
          @on_message.register
          async def _(self, message: MessageB): ...
          def receive_message_on_thread(self, message):
              try:
                  handler = self.on_message(message)
              except NotImplementedError:
                  ...
              else:
                  messages_to_handle.append(handler)
                  loop.call_soon_threadsafe(condition.notify_all)
      
          async def process_messages_on_asyncio():
              while True:
                  await condition.wait_for(lambda: len(messages_to_handle) > 0)
      
                  while len(messages_to_handle):
                      handler = messages_to_handle.pop(0)
                      await handler
    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-featureA feature request or enhancement

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions