Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/build_deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Build and Release

on:
push:
tags:
- "v*"


# Builds on tag
jobs:
build-and-upload:
# https://docs.pypi.org/trusted-publishers/using-a-publisher/
name: Build-Test-Release on Tag
runs-on: ubuntu-latest
environment: release
permissions:
id-token: write
contents: read
steps:
- name: Check out code
uses: actions/checkout@v4
with:
fetch-depth: 0 # needed for tags for dunamai
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
- uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install dependencies
run: uv sync
- name: Build package
run: |
uv build
- name: Run tests
run: |
uv run pytest --ignore=tests/extras
- name: Upload package to PyPI
if: startsWith(github.ref, 'refs/tags/v')
uses: pypa/gh-action-pypi-publish@release/v1
2 changes: 1 addition & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

version: 2
updates:
- package-ecosystem: "pip" # See documentation for possible values
- package-ecosystem: "uv" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
64 changes: 64 additions & 0 deletions .github/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Test and Coverage

on:
workflow_dispatch:
push:
branches:
- main
pull_request:
branches:
- main

concurrency:
# cancels superceded runs
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
LATEST_PY_VERSION: &latest '3.13'

jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
include:
# test oldest & latest versions against ubuntu
# and one version against windows and macos
- { os: ubuntu-latest, python-version: "3.10" }
- { os: ubuntu-latest, python-version: *latest }
#- { os: ubuntu-latest, python-version: "3.14t" }
#- { os: ubuntu-latest, python-version: "pypy3.11" }
- { os: windows-latest, python-version: *latest }
- { os: macos-latest, python-version: *latest }
environment: test
permissions:
contents: write
steps:
- name: Check out code
uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: uv sync
- name: Run tests with coverage and Build
env:
COVERAGE_PROCESS_START: ${{ github.workspace }}/.coveragerc
run: |
uv run pytest --verbose --cov --cov-report=term-missing --ignore=tests/extras
uv build
- name: Python Coverage Comment if main
if: ${{ github.ref == 'refs/heads/main' && matrix.python-version == env.LATEST_PY_VERSION && matrix.os == 'ubuntu-latest' }}
uses: py-cov-action/[email protected]
with:
GITHUB_TOKEN: ${{ github.token }}
71 changes: 45 additions & 26 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,37 +1,27 @@
default_language_version:
python: python3
exclude: "^$"

repos:
- repo: https://github.com/ambv/black
rev: 23.3.0
hooks:
- id: black
- repo: https://github.com/kynan/nbstripout
rev: 0.6.1
rev: 0.8.1
hooks:
- id: nbstripout
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.0.0-alpha.6
hooks:
- id: prettier
exclude: "packages/app/public/.*|packages/jupyter_app_base/dist/.*|packages/app/build/.*|pyrightconfig.json"
pass_filenames: true
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.261
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: ruff
- repo: "local"
- id: check-added-large-files
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.12.11
hooks:
- entry: tsc --noEmit
id: "tsc"
name: "tsc"
language: node
pass_filenames: false
types_or: [ts, tsx]
args: []
require_serial: false
additional_dependencies: ["typescript@~4.1.3"]
minimum_pre_commit_version: "0"
exclude: "packages/app/public/.*|packages/app/build/.*"
# Run the linter.
- id: ruff-check
types_or: [ python, pyi, jupyter ]
args: [ --fix ]
# Run the formatter.
- id: ruff-format
types_or: [ python, pyi, jupyter ]
- repo: local
hooks:
- entry: pyright
Expand All @@ -41,3 +31,32 @@ repos:
language: node
pass_filenames: true
additional_dependencies: ["pyright"]
# - repo: https://github.com/cidrblock/pylint-ruff-sync
# rev: v25.07.18.000003
# hooks:
# - id: pylint-ruff-sync
- repo: local
hooks:
- id: pylint
name: pylint
entry: pylint
language: system
types: [python]
require_serial: true
always_run: false
pass_filenames: true
args:
[
"-rn",
"-sn",
]
- repo: local
hooks:
- id: pytest
name: pytest
entry: pytest --cov -n 6 --cov-report=term-missing
language: system
types: [python]
pass_filenames: false
always_run: false
files: '.*\.(py|ipynb)$'
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.10
9 changes: 6 additions & 3 deletions fabduckdb/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from fabduckdb.fab_execute import registerfab
from fabduckdb.decorator import wrap_execute
from fabduckdb.fab_functions import register_function
# from fabduckdb.fab_execute import registerfab
# from fabduckdb.decorator import wrap_execute
# from .fab_functions import register_function

from .fab_execute import registerfab
from .table_functions.fab_functions import register_function

registerfab()
12 changes: 5 additions & 7 deletions fabduckdb/_version.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# This file is entirely overwritten at build time
# Module dunamai is used to create the dev and release __version__'s
# from the most recent git tag
# See git action yaml for details
import importlib.metadata

__version__ = "0.0.0"
__commit__ = "1234567890"
__commit_short__ = "1234"
try:
__version__ = importlib.metadata.version(__package__ or __name__)
except importlib.metadata.PackageNotFoundError: # pragma: no cover
__version__ = "0.0.0"
2 changes: 1 addition & 1 deletion fabduckdb/decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ def _wrap(funcname, newfunc, append: bool = False):
setattr(DuckDBPyConnection, funcname, replacement_func)


def wrap_execute(func, append: bool = False):
def wrap_execute(func: callable, append: bool = False):
_wrap(funcname="execute", newfunc=func, append=append)
35 changes: 16 additions & 19 deletions fabduckdb/fab_execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

from duckdb import DuckDBPyConnection
import duckdb
from fabduckdb.fab_functions import ContextObject, _consume_functions
from fabduckdb.fab_statements import _consume_statements
from fabduckdb.splitter import split_into_statements
from .table_functions.fab_functions import ContextObject, _consume_functions

# from fabduckdb.fab_statements import _consume_statements
from .splitter import split_into_statements

PATCHED = False
DONTPATCH = False
Expand All @@ -24,7 +25,7 @@ def registerfab():
return
else:
if not hasattr(DuckDBPyConnection, "execute_decorated"):
logger.info("Patching")
logger.debug("Patching")

DuckDBPyConnection._execute_orig_ = DuckDBPyConnection.execute # type: ignore
DuckDBPyConnection.execute_decorated = DuckDBPyConnection.execute # type: ignore
Expand All @@ -44,15 +45,9 @@ def registerfab():
def _consume(
query: str, con, params
) -> Tuple[Optional[List[str]], Dict[str, ContextObject]]:
statements, context = _consume_statements(query, con, params)

if statements is not None:
return statements, context
else:
# After doing any statement rewriting, see if there's any functions to replace
statements, context = _consume_functions(query, con, params)
statements, context = _consume_functions(query, con, params)

return statements, context
return statements, context


def process_top_level_statement(con, statement: str, params: object) -> object:
Expand All @@ -64,7 +59,7 @@ def process_top_level_statement(con, statement: str, params: object) -> object:

while statementQueue:
statement = statementQueue.popleft()
logger.info(f"Processing: {statement}")
logger.debug(f"Processing: {statement}")

(new_statements, new_context) = _consume(statement, con=con, params=params)

Expand All @@ -74,7 +69,7 @@ def process_top_level_statement(con, statement: str, params: object) -> object:
if not v.is_file:
if v.data is None:
raise ValueError(f"{k} is None, cannot process")
logger.info(f"Registering {k}")
logger.debug(f"Registering {k}")
con.register(k, v.data)
DuckDBPyConnection._fabitems_to_unregister[con].append(k) # type: ignore
# TODO: Delete any files
Expand All @@ -85,11 +80,11 @@ def process_top_level_statement(con, statement: str, params: object) -> object:
params = {k: v for k, v in params.items() if str(k) in statement}
if len(params) == 0:
params = None
res = con.execute_decorated(query=statement, parameters=params, multiple_parameter_sets=False) # type: ignore
res = con.execute_decorated(query=statement, parameters=params) # type: ignore
# can't unregister them until the next statement: the data must be consumed first

else:
logger.info(f"Rewrote: {statement} => {new_statements}")
logger.debug(f"Rewrote: {statement} => {new_statements}")

list(map(statementQueue.appendleft, new_statements))

Expand All @@ -103,15 +98,17 @@ def convert_questionmark_parameters(statement):
return statement


def fab_execute(self, query: str, parameters: object = None, multiple_parameter_sets: bool = False) -> DuckDBPyConnection: # type: ignore
def fab_execute(
self, query: str, parameters: object = None, multiple_parameter_sets: bool = False
) -> DuckDBPyConnection: # type: ignore
"""I am your captain now."""

registerfab()

# TODO: Don't split on semicolon
statements = split_into_statements(query)

logger.info(f"# Statements: {len(statements)}, {statements}")
logger.debug(f"# Statements: {len(statements)}, {statements}")
res = self

if self not in DuckDBPyConnection._fabitems_to_unregister: # type: ignore
Expand All @@ -122,7 +119,7 @@ def fab_execute(self, query: str, parameters: object = None, multiple_parameter_

if len(DuckDBPyConnection._fabitems_to_unregister[self]) > 0: # type: ignore
for i in DuckDBPyConnection._fabitems_to_unregister[self]: # type: ignore
logger.info(f"Unregistering {i}")
logger.debug(f"Unregistering {i}")

self.unregister(i)
DuckDBPyConnection._fabitems_to_unregister[self] = [] # type: ignore
Expand Down
Loading