Skip to content

Commit

Permalink
feat(tbl): add tbl function for pandas and sqla
Browse files Browse the repository at this point in the history
  • Loading branch information
machow committed Sep 26, 2022
1 parent fc462c8 commit ea14a32
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 1 deletion.
28 changes: 27 additions & 1 deletion siuba/dply/verbs.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
"join", "inner_join", "full_join", "left_join", "right_join", "semi_join", "anti_join",
# TODO: move to vectors
"if_else", "case_when",
"collect", "show_query"
"collect", "show_query",
"tbl",
)

__all__ = [*DPLY_FUNCTIONS, "Pipeable", "pipe"]
Expand Down Expand Up @@ -2291,6 +2292,31 @@ def _extract_gdf(__data, *args, **kwargs):
return out.groupby(groupings)


# tbl ----
from siuba.siu._databackend import SqlaEngine

@singledispatch2((pd.DataFrame, DataFrameGroupBy))
def tbl(src, *args, **kwargs):
return src


@tbl.register
def _tbl_sqla(src: SqlaEngine, table_name, columns=None):
from siuba.sql import LazyTbl

if src.dialect.name == "duckdb" and isinstance(columns, pd.DataFrame):
src.execute("register", (table_name, columns))

return LazyTbl(src, table_name)


tbl.register(object)
def _tbl(__data, *args, **kwargs):
raise NotImplementedError(
f"Unsupported type {type(__data)}. "
"Note that tbl currently cannot be used in a pipe."
)

# Install Siu =================================================================

install_pd_siu()
52 changes: 52 additions & 0 deletions siuba/siu/_databackend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""
This module allows you to check types (e.g. using isinstance) without importing them.
Note that this is copied from https://github.com/machow/databackend
"""

import sys
import importlib

from abc import ABCMeta


def _load_class(mod_name: str, cls_name: str):
mod = importlib.import_module(mod_name)
return getattr(mod, cls_name)


class _AbstractBackendMeta(ABCMeta):
def __new__(mcls, clsname, bases, attrs):
cls = super().__new__(mcls, clsname, bases, attrs)
if not hasattr(cls, "_backends"):
cls._backends = []
return cls

def register_backend(cls, mod_name: str, cls_name: str):
cls._backends.append((mod_name, cls_name))
cls._abc_caches_clear()


class AbstractBackend(metaclass=_AbstractBackendMeta):
@classmethod
def __subclasshook__(cls, subclass):
for mod_name, cls_name in cls._backends:
if mod_name not in sys.modules:
# module isn't loaded, so it can't be the subclass
# we don't want to import the module to explicitly run the check
# so skip here.
continue
else:
target_cls = _load_class(mod_name, cls_name)
if issubclass(target_cls, subclass):
return True

return NotImplemented


# Implementations -------------------------------------------------------------

class SqlaEngine(AbstractBackend): pass

SqlaEngine.register_backend("sqlalchemy.engine", "Engine")

0 comments on commit ea14a32

Please sign in to comment.