Skip to content

CI workflow + @materialize decorator - #1

Merged
eddietejeda merged 2 commits into
mainfrom
feat/ci-and-materialize-decorator
Jul 22, 2026
Merged

CI workflow + @materialize decorator#1
eddietejeda merged 2 commits into
mainfrom
feat/ci-and-materialize-decorator

Conversation

@eddietejeda

Copy link
Copy Markdown
Contributor

What

Two additions:

  1. CI (.github/workflows/ci.yml): flake8 + mypy + pytest on Python 3.11/3.12. Verified locally that the suite passes against the published hotdata 0.8.0 from PyPI.
  2. The @materialize decorator and MaterializedFrame (step 2 first cut):
    • Hit: returns a frame backed by Hotdata without calling the function; reads are lazy and Arrow-native.
    • Miss: runs the function, returns the frame over the computed result immediately, persists write-behind.
    • frame.sql("... FROM this ...") runs server-side against the entry's database.
    • Fail-open: a failing hit-check or persist degrades to running the function uncached.

Tests

67 passing (9 new decorator tests: miss/hit, argument and version busting, TTL expiry, fail-open, server-side SQL, capture normalization). mypy and flake8 clean under the SDK-matching config.

- @materialize(ttl=, version=, key=, key_fn=, background=): hit returns a
  frame without calling the function; miss runs it, serves the result
  immediately, persists write-behind
- MaterializedFrame: .arrow()/.df()/.to_pylist()/len(), .sql() runs
  server-side against the entry ("this" names the cached data)
- Fail-open: registry/persist failures degrade to running uncached
- EntryStore.query_table() extracted for frame SQL; README leads with the
  decorator
def sql(self, sql: str) -> pa.Table:
"""Run SQL server-side against this entry's database; the identifier
`this` names the cached data (e.g. "SELECT x FROM this LIMIT 5")."""
return self._store.query_table(self.entry, _THIS.sub(DATA_TABLE, sql))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: _THIS.sub(...) rewrites every standalone this token in the SQL string, including ones inside string literals and quoted identifiers. frame.sql("SELECT channel FROM this WHERE label = 'this'") silently becomes ... WHERE label = 'data', returning wrong rows with no error. The documented FROM this path is fine, but the collision produces silent incorrect results. Consider a less collision-prone sentinel (e.g. a token unlikely to appear in real SQL) or scoping the substitution to table-reference positions. (not blocking)

exc_info=True,
)
pending = RegistryEntry(fingerprint=fingerprint, key=label)
return MaterializedFrame(store, pending, table=table, cached=False)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super nit: on a miss in the default (background) mode, pending has database_id=None, so calling .sql() on the returned frame raises StoreError("... no database yet") until the write-behind persist finishes. .arrow()/.to_pylist()/len() all work on a miss frame (they use the local table), so .sql() being the one method that doesn't is an easy surprise. Worth a note in the README/docstring that server-side .sql() requires a persisted entry (a hit, or background=False). (not blocking)

@eddietejeda
eddietejeda merged commit 12bb8c4 into main Jul 22, 2026
3 checks passed
eddietejeda added a commit that referenced this pull request Jul 22, 2026
Decorator-first docs and demo; fix review nits from #1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant