Skip to content

Commit

Permalink
chore: remove unused Dispatched utility
Browse files Browse the repository at this point in the history
  • Loading branch information
jcrist committed Sep 16, 2024
1 parent 499ccc6 commit 4eb766d
Show file tree
Hide file tree
Showing 2 changed files with 1 addition and 203 deletions.
87 changes: 0 additions & 87 deletions ibis/common/dispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import abc
import functools
import inspect
import re
from collections import defaultdict
from types import UnionType
from typing import Union
Expand All @@ -16,24 +14,6 @@
from ibis.util import import_object, unalias_package


def normalize(r: str | re.Pattern):
"""Normalize a expression by wrapping it with `'^'` and `'$'`.
Parameters
----------
r
The pattern to normalize.
Returns
-------
Pattern
The compiled regex.
"""
r = getattr(r, "pattern", r)
return re.compile("^" + r.lstrip("^").rstrip("$") + "$")


class SingleDispatch:
def __init__(self, func, typ=None):
self.lookup = {}
Expand Down Expand Up @@ -142,70 +122,3 @@ def call(arg, *args, **kwargs):
call.dispatch = dispatcher.dispatch
call.register = dispatcher.register
return call


class _MultiDict(dict):
"""A dictionary that allows multiple values for a single key."""

def __setitem__(self, key, value):
if key in self:
self[key].append(value)
else:
super().__setitem__(key, [value])


class DispatchedMeta(type):
"""Metaclass that allows multiple implementations of a method to be defined."""

def __new__(cls, name, bases, dct):
namespace = {}
for key, value in dct.items():
if len(value) == 1:
# there is just a single attribute so pick that
namespace[key] = value[0]
elif all(inspect.isfunction(v) for v in value):
# multiple functions are defined with the same name, so create
# a dispatcher function
first, *rest = value
func = SingleDispatch(first)
for impl in rest:
func.add(impl)
namespace[key] = func
elif all(isinstance(v, classmethod) for v in value):
first, *rest = value
func = SingleDispatch(first.__func__)
for impl in rest:
func.add(impl.__func__)
namespace[key] = classmethod(func)
elif all(isinstance(v, staticmethod) for v in value):
first, *rest = value
func = SingleDispatch(first.__func__)
for impl in rest:
func.add(impl.__func__)
namespace[key] = staticmethod(func)
else:
raise TypeError(f"Multiple attributes are defined with name {key}")

return type.__new__(cls, name, bases, namespace)

@classmethod
def __prepare__(cls, name, bases):
return _MultiDict()


class Dispatched(metaclass=DispatchedMeta):
"""Base class supporting multiple implementations of a method.
Methods with the same name can be defined multiple times. The first method
defined is the default implementation, and subsequent methods are registered
as implementations for specific types of the first argument.
The constructed methods are equivalent as if they were defined with
`functools.singledispatchmethod` but without the need to use the decorator
syntax. The recommended application of this class is to implement visitor
patterns.
Besides ordinary methods, classmethods and staticmethods are also supported.
The implementation can be extended to overload multiple arguments by using
`multimethod` instead of `singledispatchmethod` as the dispatcher.
"""
117 changes: 1 addition & 116 deletions ibis/common/tests/test_dispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,8 @@

import collections
import decimal
from typing import TYPE_CHECKING, Union

import pytest

from ibis.common.dispatch import Dispatched, lazy_singledispatch

# ruff: noqa: F811
if TYPE_CHECKING:
import pandas as pd
import pyarrow as pa
from ibis.common.dispatch import lazy_singledispatch


def test_lazy_singledispatch():
Expand Down Expand Up @@ -126,110 +118,3 @@ def _(a):
assert foo({}) == "mapping"
assert foo(mydict()) == "mydict" # concrete takes precedence
assert foo(sum) == "callable"


class A:
pass


class B:
pass


class Visitor(Dispatched):
def a(self):
return "a"

def b(self, x: int):
return "b_int"

def b(self, x: str):
return "b_str"

def b(self, x: Union[A, B]):
return "b_union"

@classmethod
def c(cls, x: int, **kwargs):
return "c_int"

@classmethod
def c(cls, x: str, a=0, b=1):
return "c_str"

def d(self, x: int):
return "d_int"

def d(self, x: str):
return "d_str"

@staticmethod
def e(x: int):
return "e_int"

@staticmethod
def e(x: str):
return "e_str"

def f(self, df: dict):
return "f_dict"

def f(self, df: pd.DataFrame):
return "f_pandas"

def f(self, df: pa.Table):
return "f_pyarrow"


class Subvisitor(Visitor):
def b(self, x):
return super().b(x)

def b(self, x: float):
return "b_float"

@classmethod
def c(cls, x):
return super().c(x)

@classmethod
def c(cls, s: float):
return "c_float"


def test_dispatched():
v = Visitor()
assert v.a() == "a"
assert v.b(1) == "b_int"
assert v.b("1") == "b_str"
assert v.b(A()) == "b_union"
assert v.b(B()) == "b_union"
assert v.d(1) == "d_int"
assert v.d("1") == "d_str"

w = Subvisitor()
assert w.b(1) == "b_int"
assert w.b(1.1) == "b_float"

assert Visitor.c(1, a=0, b=0) == "c_int"
assert Visitor.c("1") == "c_str"

assert Visitor.e("1") == "e_str"
assert Visitor.e(1) == "e_int"

assert Subvisitor.c(1) == "c_int"
assert Subvisitor.c(1.1) == "c_float"

assert Subvisitor.e(1) == "e_int"


def test_dispatched_lazy():
pa = pytest.importorskip("pyarrow")

empty_pyarrow_table = pa.Table.from_arrays([])
empty_pandas_table = empty_pyarrow_table.to_pandas()

v = Visitor()
assert v.f({}) == "f_dict"
assert v.f(empty_pyarrow_table) == "f_pyarrow"
assert v.f(empty_pandas_table) == "f_pandas"

0 comments on commit 4eb766d

Please sign in to comment.