Skip to main content

Declare a runtime data structure like a config block; swap, compose, and let a cost engine pick the backend. Zero dependencies.

Project description

fluxds

tests License: MIT Python 3.9+

Declare a runtime data structure like a config block — then swap it, compose it, and let a cost engine pick the backend. Zero dependencies. Its own model base (no Pydantic).

from fluxds import FluxModel, Order

class Task(FluxModel):
    id: int
    priority: int
    class flux:
        order_by = "priority"     # extract by this field
        order    = Order.MIN      # lowest first
        index    = ("id",)        # O(1) lookup by id

q = Task.collection()
q.push(Task(id=1, priority=5))
q.push(Task(id=2, priority=2))

q.pop()             # -> Task(id=2, priority=2)   lowest priority, no negation tricks
q[1]                # -> Task(id=1, priority=5)   O(1) lookup
q.swap(Order.MAX)   # O(1) — flip extraction order, never rebuilds

Why

You fetched some data (hundreds to a few million rows — a working set that fits in RAM). Now you need to keep it ordered, findable, capped, and fresh — and you'll want to change those rules later without rewriting your code. Today you hand-roll a heap plus a dict plus manual eviction, and rewrite it in every project. fluxds is that plumbing, declared once.

It is not a database and not a big-data tool. It's the in-memory working-set layer — the same role Pydantic plays for validation, fluxds plays for runtime data structures.

Install

pip install fluxds                                    # once published to PyPI
pip install git+https://github.com/Inlionden/fluxds.git   # straight from GitHub
# or, from a clone:
pip install -e .

Requires Python 3.9+. No runtime dependencies.

The idea in one table

A data structure is two cost vectors: time (Big-O + a kind: worst / amortized / expected) and space. Using it = calling operations. So a swap is never free — it's a trade, a SWOT. fluxds makes that trade explicit and even picks the structure for you.

Pillar What it's for
Swap Change your mind cheaply — flip min↔max in O(1), or migrate to a new backend with the cost reported first.
Compose Features are keywords, not classes — stack lookup, membership, TTL, and size-cap onto one collection.
Cost engine recommend(workload) picks the cheapest backend for your op-mix and refuses one that can't serve a required op.
Customizable Nothing fits? Write ~25 lines (Backend + @register_backend) and it inherits indexing, swap, compose, and cost analysis.

What ships in v0.1

Piece API
Declarative model FluxModel + a flux: block (order_by, order, index, structure, bounded)
Collection push · pop · peek · swap · delete · update · [key] · in · by · rank · top · around · ordered · swap_structure
Backends HeapBackend (O(1) order swap), SortedBackend (skip list: O(log n) rank/top/around)
Compose compose(backend, HashIndex, BloomFilter, ExpiryTTL, BoundedSize, key=...)
Cost engine swot(a, b), recommend(workload), register_profile()
Rate limiting SlidingWindow, TieredLimiter
Your own DS Backend + @register_backend(name, costs=...), provides={...}

Examples

Leaderboard (rank / top-k / around)

class Entry(FluxModel):
    player: str
    score: int
    class flux:
        order_by = "score"; order = Order.MAX
        index = ("player",); structure = "sorted"

board = Entry.collection()
board.push(Entry(player="alice", score=1200))
board.update("alice", score=1450)   # re-ranks automatically
board.top(10)                        # the podium, non-destructive
board.rank("alice")                  # 1-based position
board.around("alice", 2)             # neighbours by rank

A cache from composed layers

from fluxds import compose, HeapBackend, HashIndex, BloomFilter, ExpiryTTL, BoundedSize

cache = compose(
    HeapBackend(order="min"),                          # ordering (oldest first)
    HashIndex("key"),                                  # O(1) lookup
    BloomFilter(expected_items=1000, error_rate=0.01),  # O(1) membership
    ExpiryTTL(ttl_seconds=300, time_field="ts"),        # auto-expiry
    BoundedSize(1000),                                 # size cap
    key="ts",
)

Let the analysis choose

from fluxds import recommend
recommend({"n": 100_000, "insert": 100_000, "rank_query": 1_000_000})
# ranks skiplist / fenwick / hash_index ... and refuses a heap (no rank_query)

Your own structure, adopted as family

from fluxds import Backend, register_backend, FluxModel
from collections import OrderedDict
import itertools

@register_backend("lru", costs={"insert": "O(1)", "lookup_key": "O(1)", "delete_key": "O(1)"})
class LRUBackend(Backend):
    caps = frozenset({"extract", "peek", "remove"})
    def __init__(self):
        self.d = OrderedDict(); self._uid = itertools.count()
    def insert(self, key, item):
        uid = next(self._uid); self.d[uid] = item; return uid
    def pop_best(self):        return self.d.popitem(last=False)[1] if self.d else None
    def pop_worst(self):       return self.pop_best()
    def peek_best(self):       return next(iter(self.d.values())) if self.d else None
    def remove(self, h):       return self.d.pop(h, None) is not None
    def __len__(self):         return len(self.d)

class CacheEntry(FluxModel):
    key: str
    value: int
    class flux:
        index = ("key",); bounded = 1000; structure = "lru"

More runnable examples in examples/: showcase.py, demo.py, compose_demo.py, lld_custom_demo.py.

Run the tests

python -m unittest discover -s tests     # 27 tests, all green
# or, with pytest installed:
pytest

Where it fits (system design & ML)

  • LLD / system design — LRU cache, leaderboard, rate limiter, job scheduler, delayed-payment queue, hit counter. See docs/LLD_ANALYSIS.md.
  • PyTorch / ML loops — top-k checkpoint keeper, curriculum / hard-example pools (strategy switch is an O(1) swap), prioritized replay buffers. fluxds lives in the Python layer around the training loop, never in the GPU math.

Roadmap

Planned next (see docs/ROADMAP_USAGE.md, which is written usage-first — the dream API is the spec):

  • flux(rows, by=..., index=...) — a no-class front door that works on plain dicts.
  • Compose as keywords: unique=, ttl=, limit= directly on flux().
  • flux.cache(limit, ttl, evict="lru") preset.
  • search(start, goal, expand, strategy="bfs"|"dijkstra"|"astar") — one loop, many algorithms.
  • DB source/sink adapters.

License

MIT © 2026 Chukkala Nikhilesh Krishna

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fluxds-0.1.0.tar.gz (24.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fluxds-0.1.0-py3-none-any.whl (20.3 kB view details)

Uploaded Python 3

File details

Details for the file fluxds-0.1.0.tar.gz.

File metadata

  • Download URL: fluxds-0.1.0.tar.gz
  • Upload date:
  • Size: 24.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.11

File hashes

Hashes for fluxds-0.1.0.tar.gz
Algorithm Hash digest
SHA256 f07e076b5e567a004ba84da76bb16b05ecade9a4b93c7a8b57800b4ce704d350
MD5 6e4d8954be7a79c3bf281e4c3700cb78
BLAKE2b-256 029a0f3d630fb652429d83fd56a36875a3d9c62e6786b906a01a7e2bb9d076d0

See more details on using hashes here.

File details

Details for the file fluxds-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: fluxds-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 20.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.11

File hashes

Hashes for fluxds-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b41f620eb4987f4a257f9f6a957aeac6065a440093c653460c2568b3fdeeaa28
MD5 566fd8a21242f2d0788c434524d467e2
BLAKE2b-256 4758fc21093c5e213a71bb516dcd4f9a7f95e7966c1160183eb0525650ac8da5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page