From 24a54a7085f9a1e0f430712b7bc81b9b9dd0a0f1 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Mon, 4 Dec 2023 11:22:55 -0500 Subject: [PATCH] Add `TimeseriesNotFound` for fqme lookup failures A common usage error is to run `piker anal mnq.cme.ib` where the CLI passed fqme is not actually fully-qualified (in this case missing an expiry token) and we get an underlying `FileNotFoundError` from the `StorageClient.read_ohlcv()` call. In such key misses, scan the existing `StorageClient._index` for possible matches and report in a `raise from` the new error. CHERRY into #486 --- piker/storage/cli.py | 9 ++++++++- piker/storage/nativedb.py | 27 ++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/piker/storage/cli.py b/piker/storage/cli.py index 9835f32b..972396c0 100644 --- a/piker/storage/cli.py +++ b/piker/storage/cli.py @@ -142,14 +142,21 @@ def anal( period: int = 60, ) -> np.ndarray: + ''' + Anal-ysis is when you take the data do stuff to it, i think. + ''' async def main(): async with ( open_piker_runtime( + # are you a bear or boi? 'tsdb_polars_anal', debug_mode=True, ), - open_storage_client() as (mod, client), + open_storage_client() as ( + mod, + client, + ), ): syms: list[str] = await client.list_keys() print(f'{len(syms)} FOUND for {mod.name}') diff --git a/piker/storage/nativedb.py b/piker/storage/nativedb.py index 1f7da9f7..7ec91fc7 100644 --- a/piker/storage/nativedb.py +++ b/piker/storage/nativedb.py @@ -72,6 +72,12 @@ from piker.log import get_logger log = get_logger('storage.nativedb') +class TimeseriesNotFound(Exception): + ''' + No timeseries entry can be found for this backend. + + ''' + # NOTE: thanks to this SO answer for the below conversion routines # to go from numpy struct-arrays to polars dataframes and back: # https://stackoverflow.com/a/72054819 @@ -228,8 +234,21 @@ class NativeStorageClient: fqme, timeframe, ) - except FileNotFoundError: - return None + except FileNotFoundError as fnfe: + + bs_fqme, _, *_ = fqme.rpartition('.') + + possible_matches: list[str] = [] + for tskey in self._index: + if bs_fqme in tskey: + possible_matches.append(tskey) + + match_str: str = '\n'.join(possible_matches) + raise TimeseriesNotFound( + f'No entry for `{fqme}`?\n' + f'Maybe you need a more specific fqme-key like:\n\n' + f'{match_str}' + ) from fnfe times = array['time'] return ( @@ -376,6 +395,8 @@ class NativeStorageClient: # ... +# TODO: does this need to be async on average? +# I guess for any IPC connected backend yes? @acm async def get_client( @@ -393,7 +414,7 @@ async def get_client( ''' datadir: Path = config.get_conf_dir() / 'nativedb' if not datadir.is_dir(): - log.info(f'Creating `nativedb` director: {datadir}') + log.info(f'Creating `nativedb` dir: {datadir}') datadir.mkdir() client = NativeStorageClient(datadir)