Skip to content

feat(client)!: list and search every source - #673

Merged
karhu merged 3 commits into
tnet-157/19-inspectionfrom
tnet-157/19b-list-search
Nov 10, 2026
Merged

karhu merged 3 commits into
tnet-157/19-inspectionfrom
tnet-157/19b-list-search

Conversation

@karhu

@karhu karhu commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

The Stack

This stack covers refactoring the internal logic of how releases are resolved during read, download & build, in particular also in the context of parent releases / parent datasets. On top of that it refactors the public TimeNet APIs and CLI interfaces around these actions, to streamline the behavior across them.

This PR

TimeNet.list() and TimeNet.search() ask every source of the list and merge the rows, so they also show the store and the installed connectors.

Details

list() and search() asked one registry of the list: the first registry that is not the store. So they missed the releases in the store and the datasets that only an installed connector provides, while open() reads all of them.

  • Both methods ask each source of the list, one after another, in order. A registry answers from its catalog. InstalledConnectors() gets list_datasets() and search() like a registry, so the client treats every source the same. Its rows are the cards of the installed connectors, sorted by dataset ID. If two packages claim an ID, the first entry point wins, as it does for a build.
  • The rows merge by dataset ID at the highest version. If two sources list the same version, the earlier source wins.
  • search() asks each source without a limit. A DatasetFilter in timenet.types holds the filters of one search. Its matches_card() filters the cards, and BaseRegistry.search(filter) takes it too. limit applies after the merge, and a negative limit raises TimeFValidationError.
  • A card never matches task or time_series_spec, because both filters read the schema of a manifest. So with one of these filters, search() does not read the cards.
  • A registry without a catalog, such as an S3 registry, adds nothing and gets a CatalogWarning. If no source has a catalog, list() and search() raise TimeNetNoCatalogError. A card that cannot be read is left out with a ConnectorCardWarning. An error of a source, for example a registry that cannot be reached, stops the call.
  • _catalog() is gone. timenet list and timenet search keep their flags. Their help, and the line of timenet list for no rows, name the sources.
  • Size: about 590 changed lines.

Design decisions

  • The call stops on an error of a source and does not skip it, because a partial list looks complete. A broken card is only a warning, because one bad connector must not hide the other connectors.
  • search() asks registries without a limit, because the limit must apply to the merged rows and not to each source. With a task or time_series_spec filter, a registry search can therefore cost more.
  • The earlier source wins on equal versions, so the order of the list stays the one rule for every command.
  • The card filters live in one function that both searches use, so a registry and a connector card cannot match in different ways.

Breaking changes

  • TimeNet.list() and TimeNet.search() merge every source of the list, connector cards included, and keep one row per dataset at the highest version.
  • BaseRegistry.search() takes a DatasetFilter instead of filter keywords.
  • If no source has a catalog, list() and search() raise TimeNetNoCatalogError, not NotImplementedError.

Stack

Changes later in the stack:

Merge message

feat(client)!: list and search every source

TimeNet.list() and TimeNet.search() ask every source of the list and
merge the rows. So they also show the local registry and the datasets
that only an installed connector provides, as open() reads them.

- InstalledConnectors() answers list_datasets() and search() like a
  registry, with the cards of the installed connectors.
- The rows merge by dataset ID at the highest version. For the same
  version, the earlier source wins.
- A DatasetFilter holds the filters of one search, for the client and
  for each registry. The limit applies after the merge.
- A source without a catalog adds nothing and gets a CatalogWarning. Any
  other error of a source stops the call.

BREAKING CHANGE: TimeNet.list() and TimeNet.search() merge every source
of the list and keep one row for each dataset, at its highest version.
If no source has a catalog, they raise TimeNetNoCatalogError.

BREAKING CHANGE: BaseRegistry.search() takes a DatasetFilter instead of
filter keywords. A negative limit raises TimeFValidationError instead of
ValueError.

@karhu
karhu added this pull request to stack #650 November 4, 2026 20:04
This was referenced Nov 4, 2026
@karhu
karhu force-pushed the tnet-157/19b-list-search branch 2 times, most recently from 72fff69 to fb6f19c Compare November 5, 2026 07:27
@karhu
karhu force-pushed the tnet-157/19b-list-search branch from fb6f19c to e2233ca Compare November 5, 2026 08:16
@karhu
karhu force-pushed the tnet-157/19b-list-search branch 2 times, most recently from 1e539c9 to f72407c Compare November 5, 2026 14:05
@karhu
karhu force-pushed the tnet-157/19b-list-search branch from 8f27f7d to 011c1cc Compare November 7, 2026 22:40

@TimoStoff TimoStoff left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

OK so accepting to unblock, but I have a design suggestion here I would like you to consider: https://github.com/OpenTSLM/TimeNet/pull/673/changes#r4511197286

Will send in chat too

Comment thread packages/timenet/src/timenet/client.py Resolved
Comment thread packages/timenet/src/timenet/client.py Resolved
Comment thread packages/timenet/src/timenet/client.py Resolved
Comment thread packages/timenet/src/timenet/client.py Resolved
Comment thread packages/timenet/src/timenet/sources.py Resolved
Comment thread packages/timenet/tests/test_client.py Resolved
Comment thread packages/timenet/tests/test_client.py Resolved
Comment thread packages/timenet/tests/test_sources.py Resolved
Comment thread packages/timenet/tests/test_cli.py Resolved
@karhu
karhu force-pushed the tnet-157/19b-list-search branch 2 times, most recently from 2a8c66c to 9825a06 Compare November 9, 2026 11:11
@karhu
karhu force-pushed the tnet-157/19b-list-search branch 2 times, most recently from 2025149 to 6499bc5 Compare November 9, 2026 13:56
@karhu
karhu force-pushed the tnet-157/19b-list-search branch 2 times, most recently from e423711 to df3c843 Compare November 9, 2026 16:18
@karhu
karhu force-pushed the tnet-157/19b-list-search branch from df3c843 to 054db0a Compare November 10, 2026 14:39

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Findings marked 🟡 are optional suggestions and need no follow-up push.

Comment thread packages/timenet/src/timenet/sources.py Resolved
Comment thread packages/timenet/src/timenet/client.py Resolved
@karhu
karhu force-pushed the tnet-157/19b-list-search branch from 054db0a to c3741e3 Compare November 10, 2026 15:50
@karhu
karhu removed this pull request from stack #699 November 10, 2026 15:58

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code review found no issues

No high-confidence issues detected in this change.

@karhu
karhu force-pushed the tnet-157/19b-list-search branch from c3741e3 to 2c32cd8 Compare November 10, 2026 16:24
@karhu
karhu added this pull request to stack #772 November 10, 2026 16:24

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code review completed

Nothing new to post: everything this review found is already covered by existing comments on this pull request or didn't merit a separate one.

list() and search() asked one registry of the list: the first registry
that is not the local registry. So they missed the releases in the local
registry and the datasets that only an installed connector provides,
while open() reads all of them.

Both methods now ask each source of the list, one after another, in
order. A registry answers from its catalog. InstalledConnectors()
answers with its new list_datasets(): the card of each installed
connector, sorted by dataset ID. If two packages claim an ID, the first
entry point wins, as it does for a build. The new builders.claims()
applies this rule once, for the build lookup and for the listing. The
rows merge by dataset ID at the highest version.
If two sources list the same version, the earlier source wins.

search() builds one DatasetFilter from its keywords. DatasetFilter is
new in timenet.types. It holds each filter as a tuple, and it refuses a
negative limit with TimeFValidationError. It matches a card, and for the
task and time_series_spec filters it matches a schema. Its select()
goes through a list of ListedRelease rows, also new: a card and the
registry that lists it. select() checks the card first. Only for a task
or time_series_spec filter, and only if the card matches, it reads the
schema from the manifest in that registry. It stops at limit matches.
search() gives select() the merged rows of list(), so it filters the
highest version of each dataset. A task search reads manifests only
until it has limit matches over all sources. A connector card has no
registry, so it never matches a task or time_series_spec filter.
BaseRegistry.search() takes a DatasetFilter in place of the filter
keywords. It sorts its catalog by dataset ID and calls select() too.

A source tells with has_catalog if it can list. An S3 registry has no
catalog, so it sets has_catalog to False. list() and search() leave
such a source out with a CatalogWarning that names it. If no source of
the list has a catalog, they raise TimeNetNoCatalogError, a new
subclass of TimeNetRegistryError. A card that cannot be read is left
out with a ConnectorCardWarning. In the SDK, both warnings point at the
first caller outside the timenet package. An error of a source stops
the call, for example a registry that cannot be reached or an error of
dataset_ids() of a backend. _catalog() is gone.
timenet list and timenet search keep their flags. Their help, and the
line of timenet list for no rows, name the sources. They show each of
the two warnings as a warning line on stderr, without a file and a
line number.

BREAKING CHANGE: TimeNet.list() and TimeNet.search() merge every
source of the list, connector cards included, and keep one row per
dataset at the highest version.

BREAKING CHANGE: BaseRegistry.search() takes a DatasetFilter in place
of the filter keywords. A negative limit raises TimeFValidationError
in place of ValueError.

Part of TNET-157.
Shorter docstrings for the code that this change adds or changes.

Part of TNET-157.
Drop tests that cannot fail or repeat other tests, and sharpen the rest.

Part of TNET-157.
@karhu
karhu force-pushed the tnet-157/19b-list-search branch from 2c32cd8 to c2dcb44 Compare November 10, 2026 17:41
@karhu
karhu merged commit e54dc4d into main Nov 10, 2026
4 of 8 checks passed
@karhu
karhu deleted the tnet-157/19b-list-search branch November 10, 2026 18:24
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.

2 participants