esgpull 0.9.1__tar.gz → 0.9.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {esgpull-0.9.1 → esgpull-0.9.2}/PKG-INFO +1 -1
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/cli/plugins.py +1 -1
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/cli/search.py +21 -8
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/context.py +38 -9
- esgpull-0.9.2/esgpull/migrations/versions/0.9.2_update_tables.py +28 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/plugin.py +22 -19
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/utils.py +0 -17
- {esgpull-0.9.1 → esgpull-0.9.2}/pyproject.toml +1 -1
- {esgpull-0.9.1 → esgpull-0.9.2}/requirements-dev.lock +23 -22
- {esgpull-0.9.1 → esgpull-0.9.2}/requirements.lock +8 -8
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/test_context.py +43 -2
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/test_utils.py +1 -9
- {esgpull-0.9.1 → esgpull-0.9.2}/uv.lock +1 -1
- {esgpull-0.9.1 → esgpull-0.9.2}/.github/workflows/ci.yml +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/.github/workflows/doc.yml +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/.github/workflows/pypi-publish.yml +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/.gitignore +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/.pre-commit-config.yaml +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/CITATION.cff +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/LICENSE +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/README.md +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/alembic.ini +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/configuration.md +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/download.md +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/glossary.md +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/images/download_1.svg +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/images/download_2.svg +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/images/download_3.svg +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/images/download_4.svg +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/images/download_5.svg +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/images/download_6.svg +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/images/intro_1.svg +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/images/intro_2.svg +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/images/intro_3.svg +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/images/intro_4.svg +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/images/intro_5.svg +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/images/intro_6.svg +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/images/quickstart_1.svg +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/images/search_1.svg +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/images/search_2.svg +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/images/search_3.svg +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/images/search_4.svg +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/images/search_5.svg +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/images/search_6.svg +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/images/search_7.svg +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/images/search_ignore.svg +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/index.md +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/installation.md +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/plugins.md +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/queries.md +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/quickstart.md +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/search.md +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/docs/stylesheets/extra.css +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/includes/abbreviations.md +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/docs/mkdocs.yml +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/__init__.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/auth.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/cli/__init__.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/cli/add.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/cli/autoremove.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/cli/config.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/cli/convert.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/cli/decorators.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/cli/download.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/cli/facet.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/cli/get.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/cli/install.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/cli/login.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/cli/remove.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/cli/retry.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/cli/self.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/cli/show.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/cli/status.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/cli/track.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/cli/update.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/cli/utils.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/config.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/constants.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/database.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/download.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/esgpull.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/exceptions.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/fs.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/graph.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/install_config.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/README +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/env.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/script.py.mako +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.3.0_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.3.1_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.3.2_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.3.3_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.3.4_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.3.5_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.3.6_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.3.7_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.3.8_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.4.0_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.5.0_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.5.1_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.5.2_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.5.3_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.5.4_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.5.5_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.6.0_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.6.1_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.6.2_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.6.3_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.6.4_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.6.5_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.7.0_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.7.1_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.7.2_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.7.3_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.8.0_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.9.0_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/0.9.1_update_tables.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/14c72daea083_query_add_column_updated_at.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/c7c8541fa741_query_add_column_added_at.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/d14f179e553c_file_add_composite_index_dataset_id_.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/migrations/versions/e7edab5d4e4b_add_dataset_tracking.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/models/__init__.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/models/base.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/models/dataset.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/models/facet.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/models/file.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/models/options.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/models/query.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/models/selection.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/models/sql.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/models/synda_file.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/models/tag.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/models/utils.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/processor.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/py.typed +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/result.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/tui.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/esgpull/version.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/pdm.lock +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/__init__.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/assets/error_plugin.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/assets/incompatible_plugin.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/assets/priority_test_plugin.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/assets/sample_plugin.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/cli/__init__.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/cli/test_dataset.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/cli/test_parse.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/cli/test_plugin_cli.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/cli/test_remove.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/cli/test_show_dates.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/cli/test_update.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/conftest.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/test_auth.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/test_config.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/test_dataset.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/test_db.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/test_esgpull.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/test_fs.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/test_graph.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/test_plugin.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/test_processor.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/test_query.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/test_selection.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/test_synda.py +0 -0
- {esgpull-0.9.1 → esgpull-0.9.2}/tests/utils.py +0 -0
|
@@ -337,7 +337,7 @@ from datetime import datetime
|
|
|
337
337
|
from pathlib import Path
|
|
338
338
|
from logging import Logger
|
|
339
339
|
|
|
340
|
-
from esgpull.models import File, Query
|
|
340
|
+
from esgpull.models import Dataset, File, Query
|
|
341
341
|
from esgpull.plugin import Event, on
|
|
342
342
|
|
|
343
343
|
# Specify version compatibility (optional)
|
|
@@ -7,6 +7,7 @@ from click.exceptions import Abort, Exit
|
|
|
7
7
|
|
|
8
8
|
from esgpull.cli.decorators import args, groups, opts
|
|
9
9
|
from esgpull.cli.utils import filter_keys, init_esgpull, parse_query, totable
|
|
10
|
+
from esgpull.context import IndexNode
|
|
10
11
|
from esgpull.exceptions import PageIndexError
|
|
11
12
|
from esgpull.graph import Graph
|
|
12
13
|
from esgpull.models import Query
|
|
@@ -135,14 +136,26 @@ def search(
|
|
|
135
136
|
esg.ui.raise_maybe_record(Exit(0))
|
|
136
137
|
if facets_hints:
|
|
137
138
|
not_distrib_query = query << Query(options=dict(distrib=False))
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
139
|
+
index = IndexNode(esg.config.api.index_node)
|
|
140
|
+
if index.is_bridge():
|
|
141
|
+
first_file_result = esg.context.search_as_queries(
|
|
142
|
+
not_distrib_query,
|
|
143
|
+
file=True,
|
|
144
|
+
max_hits=1,
|
|
145
|
+
date_from=date_from,
|
|
146
|
+
date_to=date_to,
|
|
147
|
+
)
|
|
148
|
+
first_file = first_file_result[0].selection.asdict()
|
|
149
|
+
esg.ui.print(list(first_file), json=True)
|
|
150
|
+
else:
|
|
151
|
+
facet_counts = esg.context.hints(
|
|
152
|
+
not_distrib_query,
|
|
153
|
+
file=file,
|
|
154
|
+
facets=["*"],
|
|
155
|
+
date_from=date_from,
|
|
156
|
+
date_to=date_to,
|
|
157
|
+
)
|
|
158
|
+
esg.ui.print(list(facet_counts[0]), json=True)
|
|
146
159
|
esg.ui.raise_maybe_record(Exit(0))
|
|
147
160
|
if hints is not None:
|
|
148
161
|
facet_counts = esg.context.hints(
|
|
@@ -7,6 +7,7 @@ from collections.abc import AsyncIterator, Callable, Coroutine, Sequence
|
|
|
7
7
|
from dataclasses import dataclass, field
|
|
8
8
|
from datetime import datetime
|
|
9
9
|
from typing import Any, TypeAlias, TypeVar
|
|
10
|
+
from urllib.parse import urlparse
|
|
10
11
|
|
|
11
12
|
if sys.version_info < (3, 11):
|
|
12
13
|
from exceptiongroup import BaseExceptionGroup
|
|
@@ -18,7 +19,7 @@ from esgpull.config import Config
|
|
|
18
19
|
from esgpull.exceptions import SolrUnstableQueryError
|
|
19
20
|
from esgpull.models import DatasetRecord, File, Query
|
|
20
21
|
from esgpull.tui import logger
|
|
21
|
-
from esgpull.utils import format_date_iso,
|
|
22
|
+
from esgpull.utils import format_date_iso, sync
|
|
22
23
|
|
|
23
24
|
# workaround for notebooks with running event loop
|
|
24
25
|
if asyncio.get_event_loop().is_running():
|
|
@@ -39,6 +40,29 @@ DangerousFacets = {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
|
|
43
|
+
@dataclass
|
|
44
|
+
class IndexNode:
|
|
45
|
+
value: str
|
|
46
|
+
|
|
47
|
+
def is_bridge(self) -> bool:
|
|
48
|
+
return "esgf-1-5-bridge" in self.value
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def url(self) -> str:
|
|
52
|
+
parsed = urlparse(self.value)
|
|
53
|
+
result: str
|
|
54
|
+
match (parsed.scheme, parsed.netloc, parsed.path, self.is_bridge()):
|
|
55
|
+
case ("", "", path, True):
|
|
56
|
+
result = "https://" + parsed.path
|
|
57
|
+
case ("", "", path, False):
|
|
58
|
+
result = "https://" + parsed.path + "/esg-search/search"
|
|
59
|
+
case _:
|
|
60
|
+
result = self.value
|
|
61
|
+
if "." not in result:
|
|
62
|
+
raise ValueError(self.value)
|
|
63
|
+
return result
|
|
64
|
+
|
|
65
|
+
|
|
42
66
|
@dataclass
|
|
43
67
|
class Result:
|
|
44
68
|
query: Query
|
|
@@ -70,12 +94,12 @@ class Result:
|
|
|
70
94
|
"format": "application/solr+json",
|
|
71
95
|
# "from": self.since,
|
|
72
96
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
97
|
+
index = IndexNode(value=index_url or index_node)
|
|
98
|
+
if not index.is_bridge():
|
|
99
|
+
if fields_param is not None:
|
|
100
|
+
params["fields"] = ",".join(fields_param)
|
|
101
|
+
else:
|
|
102
|
+
params["fields"] = "instance_id"
|
|
79
103
|
if date_from is not None:
|
|
80
104
|
params["from"] = format_date_iso(date_from)
|
|
81
105
|
if date_to is not None:
|
|
@@ -101,15 +125,20 @@ class Result:
|
|
|
101
125
|
else:
|
|
102
126
|
if len(values) > 1:
|
|
103
127
|
value_term = f"({value_term})"
|
|
104
|
-
|
|
128
|
+
if name.startswith("!"):
|
|
129
|
+
solr_terms.append(f"(NOT {name[1:]}:{value_term})")
|
|
130
|
+
else:
|
|
131
|
+
solr_terms.append(f"{name}:{value_term}")
|
|
105
132
|
if solr_terms:
|
|
106
133
|
params["query"] = " AND ".join(solr_terms)
|
|
107
134
|
for name, option in self.query.options.items(use_default=True):
|
|
108
135
|
if option.is_bool():
|
|
109
136
|
params[name] = option.name
|
|
137
|
+
if index.is_bridge():
|
|
138
|
+
_ = params.pop("retracted", None) # not supported in bridge API
|
|
110
139
|
if params.get("distrib") == "true" and facets_star:
|
|
111
140
|
raise SolrUnstableQueryError(pretty_repr(self.query))
|
|
112
|
-
self.request = Request("GET",
|
|
141
|
+
self.request = Request("GET", index.url, params=params)
|
|
113
142
|
|
|
114
143
|
def to(self, subtype: type[RT]) -> RT:
|
|
115
144
|
result: RT = subtype(self.query, self.file)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""update tables
|
|
2
|
+
|
|
3
|
+
Revision ID: 0.9.2
|
|
4
|
+
Revises: 0.9.1
|
|
5
|
+
Create Date: 2025-09-04 16:56:29.263007
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from alembic import op
|
|
9
|
+
import sqlalchemy as sa
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# revision identifiers, used by Alembic.
|
|
13
|
+
revision = '0.9.2'
|
|
14
|
+
down_revision = '0.9.1'
|
|
15
|
+
branch_labels = None
|
|
16
|
+
depends_on = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade() -> None:
|
|
20
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
21
|
+
pass
|
|
22
|
+
# ### end Alembic commands ###
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def downgrade() -> None:
|
|
26
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
27
|
+
pass
|
|
28
|
+
# ### end Alembic commands ###
|
|
@@ -151,7 +151,11 @@ class PluginConfig:
|
|
|
151
151
|
enabled: set[str] = field(default_factory=set)
|
|
152
152
|
disabled: set[str] = field(default_factory=set)
|
|
153
153
|
plugins: dict[str, dict[str, Any]] = field(default_factory=dict)
|
|
154
|
-
_raw:
|
|
154
|
+
_raw: tomlkit.TOMLDocument = field(default_factory=tomlkit.TOMLDocument)
|
|
155
|
+
|
|
156
|
+
def __post_init__(self):
|
|
157
|
+
if "plugins" not in self._raw:
|
|
158
|
+
self._raw["plugins"] = {}
|
|
155
159
|
|
|
156
160
|
|
|
157
161
|
class PluginManager:
|
|
@@ -199,12 +203,13 @@ class PluginManager:
|
|
|
199
203
|
|
|
200
204
|
try:
|
|
201
205
|
with open(self.config_path, "r") as f:
|
|
202
|
-
raw = tomlkit.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
raw = tomlkit.load(f)
|
|
207
|
+
unwrap = raw.unwrap()
|
|
208
|
+
self.config.enabled = set(unwrap.get("enabled", []))
|
|
209
|
+
self.config.disabled = set(unwrap.get("disabled", []))
|
|
210
|
+
self.config.plugins = unwrap.get("plugins", {})
|
|
211
|
+
# Store the raw plugin configuration to preserve what's on disk
|
|
212
|
+
self.config._raw = raw
|
|
208
213
|
except Exception as e:
|
|
209
214
|
logger.error(f"Failed to load plugin config: {e}")
|
|
210
215
|
|
|
@@ -219,18 +224,16 @@ class PluginManager:
|
|
|
219
224
|
return
|
|
220
225
|
|
|
221
226
|
try:
|
|
222
|
-
doc =
|
|
227
|
+
doc = self.config._raw
|
|
223
228
|
doc["enabled"] = list(self.config.enabled)
|
|
224
229
|
doc["disabled"] = list(self.config.disabled)
|
|
225
230
|
|
|
226
231
|
# For plugins section, handle differently based on generate_full_config flag
|
|
227
232
|
if generate_full_config:
|
|
228
233
|
doc["plugins"] = self.config.plugins
|
|
229
|
-
else:
|
|
230
|
-
doc["plugins"] = self.config._raw
|
|
231
234
|
|
|
232
235
|
with open(self.config_path, "w") as f:
|
|
233
|
-
|
|
236
|
+
tomlkit.dump(doc, f)
|
|
234
237
|
except Exception as e:
|
|
235
238
|
logger.error(f"Failed to save plugin config: {e}")
|
|
236
239
|
|
|
@@ -464,8 +467,8 @@ class PluginManager:
|
|
|
464
467
|
# Make sure plugin exists in both plugins and _raw dicts
|
|
465
468
|
if plugin_name not in self.config.plugins:
|
|
466
469
|
self.config.plugins[plugin_name] = {}
|
|
467
|
-
if plugin_name not in self.config._raw:
|
|
468
|
-
self.config._raw[plugin_name] = {}
|
|
470
|
+
if plugin_name not in self.config._raw["plugins"]:
|
|
471
|
+
self.config._raw["plugins"][plugin_name] = {}
|
|
469
472
|
|
|
470
473
|
# Update the value in both places
|
|
471
474
|
if key in self.config.plugins[plugin_name]:
|
|
@@ -473,7 +476,7 @@ class PluginManager:
|
|
|
473
476
|
new_value = cast_value(old_value, value, key)
|
|
474
477
|
self.config.plugins[plugin_name][key] = new_value
|
|
475
478
|
# Also update in _raw to keep in sync
|
|
476
|
-
self.config._raw[plugin_name][key] = new_value
|
|
479
|
+
self.config._raw["plugins"][plugin_name][key] = new_value
|
|
477
480
|
else:
|
|
478
481
|
raise KeyError(key, self.config.plugins[plugin_name])
|
|
479
482
|
|
|
@@ -497,8 +500,8 @@ class PluginManager:
|
|
|
497
500
|
# Make sure the plugin exists in both configs
|
|
498
501
|
if plugin_name not in self.config.plugins:
|
|
499
502
|
self.config.plugins[plugin_name] = {}
|
|
500
|
-
if plugin_name not in self.config._raw:
|
|
501
|
-
self.config._raw[plugin_name] = {}
|
|
503
|
+
if plugin_name not in self.config._raw["plugins"]:
|
|
504
|
+
self.config._raw["plugins"][plugin_name] = {}
|
|
502
505
|
|
|
503
506
|
# Remove the key from both configs
|
|
504
507
|
if key in self.config.plugins[plugin_name]:
|
|
@@ -508,10 +511,10 @@ class PluginManager:
|
|
|
508
511
|
|
|
509
512
|
# Also remove from _raw if it exists
|
|
510
513
|
if (
|
|
511
|
-
plugin_name in self.config._raw
|
|
512
|
-
and key in self.config._raw[plugin_name]
|
|
514
|
+
plugin_name in self.config._raw["plugins"]
|
|
515
|
+
and key in self.config._raw["plugins"][plugin_name]
|
|
513
516
|
):
|
|
514
|
-
self.config._raw[plugin_name].pop(key)
|
|
517
|
+
self.config._raw["plugins"][plugin_name].pop(key)
|
|
515
518
|
|
|
516
519
|
self.write_config()
|
|
517
520
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import datetime
|
|
3
3
|
from typing import Callable, Coroutine, TypeVar
|
|
4
|
-
from urllib.parse import urlparse
|
|
5
4
|
|
|
6
5
|
from rich.filesize import _to_str
|
|
7
6
|
|
|
@@ -52,19 +51,3 @@ def format_date_iso(
|
|
|
52
51
|
date: str | datetime.datetime, fmt: str = "%Y-%m-%d"
|
|
53
52
|
) -> str:
|
|
54
53
|
return parse_date(date, fmt).replace(microsecond=0).isoformat() + "Z"
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def url2index(url: str) -> str:
|
|
58
|
-
parsed = urlparse(url)
|
|
59
|
-
if parsed.netloc == "":
|
|
60
|
-
return parsed.path
|
|
61
|
-
else:
|
|
62
|
-
return parsed.netloc
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def index2url(index: str) -> str:
|
|
66
|
-
url = "https://" + url2index(index)
|
|
67
|
-
if "esgf-1-5-bridge" in index:
|
|
68
|
-
return url
|
|
69
|
-
else:
|
|
70
|
-
return url + "/esg-search/search"
|
|
@@ -14,7 +14,7 @@ aiofiles==24.1.0
|
|
|
14
14
|
# via esgpull
|
|
15
15
|
aiostream==0.7.0
|
|
16
16
|
# via esgpull
|
|
17
|
-
alembic==1.16.
|
|
17
|
+
alembic==1.16.5
|
|
18
18
|
# via esgpull
|
|
19
19
|
anyio==4.10.0
|
|
20
20
|
# via httpx
|
|
@@ -27,7 +27,7 @@ babel==2.17.0
|
|
|
27
27
|
# via mkdocs-material
|
|
28
28
|
backrefs==5.9
|
|
29
29
|
# via mkdocs-material
|
|
30
|
-
cattrs==25.
|
|
30
|
+
cattrs==25.2.0
|
|
31
31
|
# via esgpull
|
|
32
32
|
certifi==2025.8.3
|
|
33
33
|
# via httpcore
|
|
@@ -35,21 +35,22 @@ certifi==2025.8.3
|
|
|
35
35
|
# via requests
|
|
36
36
|
cffi==1.17.1
|
|
37
37
|
# via cryptography
|
|
38
|
-
charset-normalizer==3.4.
|
|
38
|
+
charset-normalizer==3.4.3
|
|
39
39
|
# via requests
|
|
40
40
|
click==8.2.1
|
|
41
41
|
# via click-params
|
|
42
42
|
# via esgpull
|
|
43
43
|
# via mkdocs
|
|
44
|
+
# via mkdocs-material
|
|
44
45
|
click-params==0.5.0
|
|
45
46
|
# via esgpull
|
|
46
47
|
colorama==0.4.6
|
|
47
48
|
# via mkdocs-material
|
|
48
49
|
comm==0.2.3
|
|
49
50
|
# via ipykernel
|
|
50
|
-
coverage==7.10.
|
|
51
|
+
coverage==7.10.6
|
|
51
52
|
# via pytest-cov
|
|
52
|
-
cryptography==45.0.
|
|
53
|
+
cryptography==45.0.7
|
|
53
54
|
# via pyopenssl
|
|
54
55
|
debugpy==1.8.16
|
|
55
56
|
# via ipykernel
|
|
@@ -65,9 +66,9 @@ exceptiongroup==1.3.0
|
|
|
65
66
|
# via pytest
|
|
66
67
|
execnet==2.1.1
|
|
67
68
|
# via pytest-xdist
|
|
68
|
-
executing==2.2.
|
|
69
|
+
executing==2.2.1
|
|
69
70
|
# via stack-data
|
|
70
|
-
filelock==3.
|
|
71
|
+
filelock==3.19.1
|
|
71
72
|
# via pytest-mypy
|
|
72
73
|
ghp-import==2.1.0
|
|
73
74
|
# via mkdocs
|
|
@@ -111,7 +112,7 @@ markdown==3.8.2
|
|
|
111
112
|
# via mkdocs
|
|
112
113
|
# via mkdocs-material
|
|
113
114
|
# via pymdown-extensions
|
|
114
|
-
markdown-it-py==
|
|
115
|
+
markdown-it-py==4.0.0
|
|
115
116
|
# via rich
|
|
116
117
|
markupsafe==3.0.2
|
|
117
118
|
# via jinja2
|
|
@@ -129,7 +130,7 @@ mkdocs==1.6.1
|
|
|
129
130
|
# via mkdocs-material
|
|
130
131
|
mkdocs-get-deps==0.2.0
|
|
131
132
|
# via mkdocs
|
|
132
|
-
mkdocs-material==9.6.
|
|
133
|
+
mkdocs-material==9.6.18
|
|
133
134
|
mkdocs-material-extensions==1.3.1
|
|
134
135
|
# via mkdocs-material
|
|
135
136
|
myproxyclient==2.1.1
|
|
@@ -141,7 +142,7 @@ mypy-extensions==1.1.0
|
|
|
141
142
|
nest-asyncio==1.6.0
|
|
142
143
|
# via esgpull
|
|
143
144
|
# via ipykernel
|
|
144
|
-
orjson==3.11.
|
|
145
|
+
orjson==3.11.3
|
|
145
146
|
packaging==25.0
|
|
146
147
|
# via esgpull
|
|
147
148
|
# via ipykernel
|
|
@@ -149,21 +150,21 @@ packaging==25.0
|
|
|
149
150
|
# via pytest
|
|
150
151
|
paginate==0.5.7
|
|
151
152
|
# via mkdocs-material
|
|
152
|
-
parso==0.8.
|
|
153
|
+
parso==0.8.5
|
|
153
154
|
# via jedi
|
|
154
155
|
pathspec==0.12.1
|
|
155
156
|
# via mkdocs
|
|
156
157
|
# via mypy
|
|
157
158
|
pexpect==4.9.0
|
|
158
159
|
# via ipython
|
|
159
|
-
platformdirs==4.
|
|
160
|
+
platformdirs==4.4.0
|
|
160
161
|
# via esgpull
|
|
161
162
|
# via jupyter-core
|
|
162
163
|
# via mkdocs-get-deps
|
|
163
164
|
pluggy==1.6.0
|
|
164
165
|
# via pytest
|
|
165
166
|
# via pytest-cov
|
|
166
|
-
prompt-toolkit==3.0.
|
|
167
|
+
prompt-toolkit==3.0.52
|
|
167
168
|
# via ipython
|
|
168
169
|
# via jupyter-console
|
|
169
170
|
psutil==7.0.0
|
|
@@ -187,7 +188,7 @@ pyopenssl==25.1.0
|
|
|
187
188
|
# via myproxyclient
|
|
188
189
|
pyparsing==3.2.3
|
|
189
190
|
# via esgpull
|
|
190
|
-
pytest==8.4.
|
|
191
|
+
pytest==8.4.2
|
|
191
192
|
# via pytest-cov
|
|
192
193
|
# via pytest-mypy
|
|
193
194
|
# via pytest-xdist
|
|
@@ -205,11 +206,11 @@ pyyaml==6.0.2
|
|
|
205
206
|
# via pyyaml-env-tag
|
|
206
207
|
pyyaml-env-tag==1.1
|
|
207
208
|
# via mkdocs
|
|
208
|
-
pyzmq==27.0.
|
|
209
|
+
pyzmq==27.0.2
|
|
209
210
|
# via ipykernel
|
|
210
211
|
# via jupyter-client
|
|
211
212
|
# via jupyter-console
|
|
212
|
-
requests==2.32.
|
|
213
|
+
requests==2.32.5
|
|
213
214
|
# via mkdocs-material
|
|
214
215
|
rich==14.1.0
|
|
215
216
|
# via esgpull
|
|
@@ -220,7 +221,7 @@ six==1.17.0
|
|
|
220
221
|
# via python-dateutil
|
|
221
222
|
sniffio==1.3.1
|
|
222
223
|
# via anyio
|
|
223
|
-
sqlalchemy==2.0.
|
|
224
|
+
sqlalchemy==2.0.43
|
|
224
225
|
# via alembic
|
|
225
226
|
# via esgpull
|
|
226
227
|
stack-data==0.6.3
|
|
@@ -233,7 +234,7 @@ tomli==2.2.1
|
|
|
233
234
|
# via pytest
|
|
234
235
|
tomlkit==0.13.3
|
|
235
236
|
# via esgpull
|
|
236
|
-
tornado==6.5.
|
|
237
|
+
tornado==6.5.2
|
|
237
238
|
# via ipykernel
|
|
238
239
|
# via jupyter-client
|
|
239
240
|
traitlets==5.14.3
|
|
@@ -243,9 +244,9 @@ traitlets==5.14.3
|
|
|
243
244
|
# via jupyter-console
|
|
244
245
|
# via jupyter-core
|
|
245
246
|
# via matplotlib-inline
|
|
246
|
-
types-aiofiles==24.1.0.
|
|
247
|
-
types-pyyaml==6.0.12.
|
|
248
|
-
typing-extensions==4.
|
|
247
|
+
types-aiofiles==24.1.0.20250822
|
|
248
|
+
types-pyyaml==6.0.12.20250822
|
|
249
|
+
typing-extensions==4.15.0
|
|
249
250
|
# via aiostream
|
|
250
251
|
# via alembic
|
|
251
252
|
# via anyio
|
|
@@ -263,5 +264,5 @@ watchdog==6.0.0
|
|
|
263
264
|
# via mkdocs
|
|
264
265
|
wcwidth==0.2.13
|
|
265
266
|
# via prompt-toolkit
|
|
266
|
-
wrapt==1.17.
|
|
267
|
+
wrapt==1.17.3
|
|
267
268
|
# via deprecated
|
|
@@ -14,14 +14,14 @@ aiofiles==24.1.0
|
|
|
14
14
|
# via esgpull
|
|
15
15
|
aiostream==0.7.0
|
|
16
16
|
# via esgpull
|
|
17
|
-
alembic==1.16.
|
|
17
|
+
alembic==1.16.5
|
|
18
18
|
# via esgpull
|
|
19
19
|
anyio==4.10.0
|
|
20
20
|
# via httpx
|
|
21
21
|
attrs==25.3.0
|
|
22
22
|
# via cattrs
|
|
23
23
|
# via esgpull
|
|
24
|
-
cattrs==25.
|
|
24
|
+
cattrs==25.2.0
|
|
25
25
|
# via esgpull
|
|
26
26
|
certifi==2025.8.3
|
|
27
27
|
# via httpcore
|
|
@@ -33,7 +33,7 @@ click==8.2.1
|
|
|
33
33
|
# via esgpull
|
|
34
34
|
click-params==0.5.0
|
|
35
35
|
# via esgpull
|
|
36
|
-
cryptography==45.0.
|
|
36
|
+
cryptography==45.0.7
|
|
37
37
|
# via pyopenssl
|
|
38
38
|
deprecated==1.2.18
|
|
39
39
|
# via click-params
|
|
@@ -53,7 +53,7 @@ idna==3.10
|
|
|
53
53
|
# via httpx
|
|
54
54
|
mako==1.3.10
|
|
55
55
|
# via alembic
|
|
56
|
-
markdown-it-py==
|
|
56
|
+
markdown-it-py==4.0.0
|
|
57
57
|
# via rich
|
|
58
58
|
markupsafe==3.0.2
|
|
59
59
|
# via mako
|
|
@@ -65,7 +65,7 @@ nest-asyncio==1.6.0
|
|
|
65
65
|
# via esgpull
|
|
66
66
|
packaging==25.0
|
|
67
67
|
# via esgpull
|
|
68
|
-
platformdirs==4.
|
|
68
|
+
platformdirs==4.4.0
|
|
69
69
|
# via esgpull
|
|
70
70
|
pycparser==2.22
|
|
71
71
|
# via cffi
|
|
@@ -86,14 +86,14 @@ six==1.17.0
|
|
|
86
86
|
# via myproxyclient
|
|
87
87
|
sniffio==1.3.1
|
|
88
88
|
# via anyio
|
|
89
|
-
sqlalchemy==2.0.
|
|
89
|
+
sqlalchemy==2.0.43
|
|
90
90
|
# via alembic
|
|
91
91
|
# via esgpull
|
|
92
92
|
tomli==2.2.1
|
|
93
93
|
# via alembic
|
|
94
94
|
tomlkit==0.13.3
|
|
95
95
|
# via esgpull
|
|
96
|
-
typing-extensions==4.
|
|
96
|
+
typing-extensions==4.15.0
|
|
97
97
|
# via aiostream
|
|
98
98
|
# via alembic
|
|
99
99
|
# via anyio
|
|
@@ -103,5 +103,5 @@ typing-extensions==4.14.1
|
|
|
103
103
|
# via sqlalchemy
|
|
104
104
|
validators==0.22.0
|
|
105
105
|
# via click-params
|
|
106
|
-
wrapt==1.17.
|
|
106
|
+
wrapt==1.17.3
|
|
107
107
|
# via deprecated
|
|
@@ -3,7 +3,7 @@ from time import perf_counter
|
|
|
3
3
|
|
|
4
4
|
import pytest
|
|
5
5
|
|
|
6
|
-
from esgpull.context import Context
|
|
6
|
+
from esgpull.context import Context, IndexNode
|
|
7
7
|
from esgpull.models import Query
|
|
8
8
|
|
|
9
9
|
|
|
@@ -104,6 +104,10 @@ class Timer:
|
|
|
104
104
|
|
|
105
105
|
|
|
106
106
|
@pytest.mark.slow
|
|
107
|
+
@pytest.mark.xfail(
|
|
108
|
+
raises=ValueError,
|
|
109
|
+
reason="ESGF bridge API gives index_node values that are not valid URLs",
|
|
110
|
+
)
|
|
107
111
|
def test_search_distributed(ctx):
|
|
108
112
|
query = Query()
|
|
109
113
|
# ctx.config.api.http_timeout = 60
|
|
@@ -138,7 +142,12 @@ def test_search_distributed(ctx):
|
|
|
138
142
|
|
|
139
143
|
|
|
140
144
|
def test_ipsl_hits_between_1_and_2_million(ctx, cmip6_ipsl):
|
|
141
|
-
|
|
145
|
+
hits = ctx.hits(
|
|
146
|
+
cmip6_ipsl,
|
|
147
|
+
file=False,
|
|
148
|
+
index_node="esgf-node.ipsl.upmc.fr",
|
|
149
|
+
)
|
|
150
|
+
assert 1_000_000 < hits[0] < 2_000_000
|
|
142
151
|
|
|
143
152
|
|
|
144
153
|
def test_more_files_than_datasets(ctx, query):
|
|
@@ -168,3 +177,35 @@ def test_ignore_facet_hits(ctx):
|
|
|
168
177
|
hits_not_ipsl = ctx.hits(query_not_ipsl, file=False)[0]
|
|
169
178
|
assert all(hits > 0 for hits in [hits_all, hits_ipsl, hits_not_ipsl])
|
|
170
179
|
assert hits_all == hits_ipsl + hits_not_ipsl
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@pytest.mark.parametrize(
|
|
183
|
+
"index,url,is_bridge",
|
|
184
|
+
[
|
|
185
|
+
(
|
|
186
|
+
"esgf-node.ipsl.upmc.fr",
|
|
187
|
+
"https://esgf-node.ipsl.upmc.fr/esg-search/search",
|
|
188
|
+
False,
|
|
189
|
+
),
|
|
190
|
+
(
|
|
191
|
+
"https://esgf-node.ipsl.upmc.fr/esg-search/search",
|
|
192
|
+
"https://esgf-node.ipsl.upmc.fr/esg-search/search",
|
|
193
|
+
False,
|
|
194
|
+
),
|
|
195
|
+
(
|
|
196
|
+
"esgf-node.ornl.gov/esgf-1-5-bridge",
|
|
197
|
+
"https://esgf-node.ornl.gov/esgf-1-5-bridge",
|
|
198
|
+
True,
|
|
199
|
+
),
|
|
200
|
+
(
|
|
201
|
+
"https://esgf-node.ornl.gov/esgf-1-5-bridge",
|
|
202
|
+
"https://esgf-node.ornl.gov/esgf-1-5-bridge",
|
|
203
|
+
True,
|
|
204
|
+
),
|
|
205
|
+
],
|
|
206
|
+
)
|
|
207
|
+
def test_index2url(index: str, url: str, is_bridge: bool):
|
|
208
|
+
for value in (index, url):
|
|
209
|
+
index_node = IndexNode(value=value)
|
|
210
|
+
assert index_node.url == url
|
|
211
|
+
assert index_node.is_bridge() == is_bridge
|
|
@@ -2,10 +2,7 @@ from datetime import datetime
|
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
4
|
|
|
5
|
-
from esgpull.utils import format_date, format_date_iso,
|
|
6
|
-
|
|
7
|
-
ESGF_INDEX = "esgf-node.ipsl.upmc.fr"
|
|
8
|
-
ESGF_URL = "https://esgf-node.ipsl.upmc.fr/esg-search/search"
|
|
5
|
+
from esgpull.utils import format_date, format_date_iso, parse_date
|
|
9
6
|
|
|
10
7
|
|
|
11
8
|
def test_parse_date():
|
|
@@ -40,8 +37,3 @@ def test_format_date_iso():
|
|
|
40
37
|
format_date_iso("20220101")
|
|
41
38
|
with pytest.raises(ValueError):
|
|
42
39
|
format_date_iso(20220101)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def test_index2url():
|
|
46
|
-
assert index2url(ESGF_INDEX) == ESGF_URL
|
|
47
|
-
assert index2url(ESGF_URL) == ESGF_URL
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|