esgpull 0.6.4__py3-none-any.whl → 0.7.1__py3-none-any.whl
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/cli/__init__.py +2 -0
- esgpull/cli/add.py +10 -3
- esgpull/cli/datasets.py +78 -0
- esgpull/cli/decorators.py +11 -0
- esgpull/cli/search.py +14 -7
- esgpull/cli/update.py +19 -16
- esgpull/cli/utils.py +5 -1
- esgpull/config.py +25 -8
- esgpull/constants.py +1 -0
- esgpull/database.py +24 -5
- esgpull/esgpull.py +46 -2
- esgpull/exceptions.py +5 -2
- esgpull/install_config.py +10 -2
- esgpull/migrations/env.py +3 -0
- esgpull/migrations/versions/0.6.5_update_tables.py +25 -0
- esgpull/migrations/versions/0.7.0_update_tables.py +38 -0
- esgpull/migrations/versions/0.7.1_update_tables.py +28 -0
- esgpull/models/query.py +1 -1
- esgpull/models/selection.py +6 -1
- esgpull/models/sql.py +17 -3
- esgpull/tui.py +3 -4
- esgpull/version.py +3 -1
- {esgpull-0.6.4.dist-info → esgpull-0.7.1.dist-info}/METADATA +22 -43
- {esgpull-0.6.4.dist-info → esgpull-0.7.1.dist-info}/RECORD +35 -31
- {esgpull-0.6.4.dist-info → esgpull-0.7.1.dist-info}/WHEEL +1 -1
- {esgpull-0.6.4.dist-info → esgpull-0.7.1.dist-info}/entry_points.txt +0 -1
- {esgpull-0.6.4.dist-info → esgpull-0.7.1.dist-info}/licenses/LICENSE +0 -0
esgpull/cli/__init__.py
CHANGED
|
@@ -7,6 +7,7 @@ from esgpull import __version__
|
|
|
7
7
|
from esgpull.cli.add import add
|
|
8
8
|
from esgpull.cli.config import config
|
|
9
9
|
from esgpull.cli.convert import convert
|
|
10
|
+
from esgpull.cli.datasets import datasets
|
|
10
11
|
from esgpull.cli.download import download
|
|
11
12
|
from esgpull.cli.login import login
|
|
12
13
|
from esgpull.cli.remove import remove
|
|
@@ -34,6 +35,7 @@ SUBCOMMANDS: list[click.Command] = [
|
|
|
34
35
|
# autoremove,
|
|
35
36
|
config,
|
|
36
37
|
convert,
|
|
38
|
+
datasets,
|
|
37
39
|
download,
|
|
38
40
|
# facet,
|
|
39
41
|
# get,
|
esgpull/cli/add.py
CHANGED
|
@@ -21,20 +21,22 @@ from esgpull.tui import Verbosity
|
|
|
21
21
|
@groups.query_def
|
|
22
22
|
@opts.query_file
|
|
23
23
|
@opts.track
|
|
24
|
+
@opts.no_default_query
|
|
24
25
|
@opts.record
|
|
25
26
|
@opts.verbosity
|
|
26
27
|
def add(
|
|
27
28
|
facets: list[str],
|
|
28
|
-
|
|
29
|
+
## query_def
|
|
29
30
|
tags: list[str],
|
|
30
31
|
require: str | None,
|
|
31
32
|
distrib: str | None,
|
|
32
33
|
latest: str | None,
|
|
33
34
|
replica: str | None,
|
|
34
35
|
retracted: str | None,
|
|
35
|
-
|
|
36
|
+
## ungrouped
|
|
36
37
|
query_file: Path | None,
|
|
37
38
|
track: bool,
|
|
39
|
+
no_default_query: bool,
|
|
38
40
|
record: bool,
|
|
39
41
|
verbosity: Verbosity,
|
|
40
42
|
) -> None:
|
|
@@ -55,7 +57,11 @@ def add(
|
|
|
55
57
|
|
|
56
58
|
To fetch files from ESGF and link them to a query, see the `track` and `update` commands.
|
|
57
59
|
"""
|
|
58
|
-
esg = init_esgpull(
|
|
60
|
+
esg = init_esgpull(
|
|
61
|
+
verbosity,
|
|
62
|
+
record=record,
|
|
63
|
+
no_default_query=no_default_query,
|
|
64
|
+
)
|
|
59
65
|
with esg.ui.logging("add", onraise=Abort):
|
|
60
66
|
if query_file is not None:
|
|
61
67
|
queries = serialize_queries_from_file(query_file)
|
|
@@ -77,6 +83,7 @@ def add(
|
|
|
77
83
|
expanded = query
|
|
78
84
|
query.track(expanded.options)
|
|
79
85
|
queries = [query]
|
|
86
|
+
queries = esg.insert_default_query(*queries)
|
|
80
87
|
subgraph = Graph(None)
|
|
81
88
|
subgraph.add(*queries)
|
|
82
89
|
esg.ui.print(subgraph)
|
esgpull/cli/datasets.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
from click.exceptions import Abort, Exit
|
|
6
|
+
from rich.box import MINIMAL_DOUBLE_HEAD
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
|
|
9
|
+
from esgpull.cli.decorators import args, groups, opts
|
|
10
|
+
from esgpull.cli.utils import init_esgpull, valid_name_tag
|
|
11
|
+
from esgpull.models import FileStatus
|
|
12
|
+
from esgpull.tui import Verbosity
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class DatasetCounter:
|
|
17
|
+
done: int = 0
|
|
18
|
+
total: int = 0
|
|
19
|
+
|
|
20
|
+
def is_complete(self) -> int:
|
|
21
|
+
return self.done == self.total
|
|
22
|
+
|
|
23
|
+
def asdict(self) -> dict:
|
|
24
|
+
return {
|
|
25
|
+
"done": self.done,
|
|
26
|
+
"total": self.total,
|
|
27
|
+
"complete": self.is_complete(),
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@click.command()
|
|
32
|
+
@args.query_id_required
|
|
33
|
+
@groups.json_yaml
|
|
34
|
+
@opts.verbosity
|
|
35
|
+
def datasets(
|
|
36
|
+
query_id: str,
|
|
37
|
+
json: bool,
|
|
38
|
+
yaml: bool,
|
|
39
|
+
verbosity: Verbosity,
|
|
40
|
+
):
|
|
41
|
+
"""
|
|
42
|
+
View datasets completeness per query.
|
|
43
|
+
"""
|
|
44
|
+
esg = init_esgpull(verbosity)
|
|
45
|
+
with esg.ui.logging("datasets", onraise=Abort):
|
|
46
|
+
if not valid_name_tag(esg.graph, esg.ui, query_id, None):
|
|
47
|
+
raise Exit(1)
|
|
48
|
+
query = esg.graph.get(query_id)
|
|
49
|
+
datasets: defaultdict[str, DatasetCounter] = defaultdict(
|
|
50
|
+
DatasetCounter
|
|
51
|
+
)
|
|
52
|
+
for file in query.files:
|
|
53
|
+
datasets[file.dataset_id].total += 1
|
|
54
|
+
if file.status == FileStatus.Done:
|
|
55
|
+
datasets[file.dataset_id].done += 1
|
|
56
|
+
if json or yaml:
|
|
57
|
+
datasets_dict = {
|
|
58
|
+
dataset_id: counts.asdict()
|
|
59
|
+
for dataset_id, counts in datasets.items()
|
|
60
|
+
}
|
|
61
|
+
if json:
|
|
62
|
+
esg.ui.print(datasets_dict, json=True)
|
|
63
|
+
elif yaml:
|
|
64
|
+
esg.ui.print(datasets_dict, yaml=True)
|
|
65
|
+
else:
|
|
66
|
+
table = Table(box=MINIMAL_DOUBLE_HEAD, show_edge=False)
|
|
67
|
+
table.add_column("dataset_id", justify="right", style="bold blue")
|
|
68
|
+
table.add_column("done", justify="center")
|
|
69
|
+
table.add_column("total", justify="center")
|
|
70
|
+
table.add_column("complete", justify="center")
|
|
71
|
+
for dataset_id, counts in datasets.items():
|
|
72
|
+
table.add_row(
|
|
73
|
+
dataset_id,
|
|
74
|
+
str(counts.done),
|
|
75
|
+
str(counts.total),
|
|
76
|
+
str(counts.is_complete()),
|
|
77
|
+
)
|
|
78
|
+
esg.ui.print(table)
|
esgpull/cli/decorators.py
CHANGED
|
@@ -59,6 +59,12 @@ class args:
|
|
|
59
59
|
nargs=1,
|
|
60
60
|
required=False,
|
|
61
61
|
)
|
|
62
|
+
query_id_required: Dec = click.argument(
|
|
63
|
+
"query_id",
|
|
64
|
+
type=str,
|
|
65
|
+
nargs=1,
|
|
66
|
+
required=True,
|
|
67
|
+
)
|
|
62
68
|
query_ids: Dec = click.argument(
|
|
63
69
|
"query_ids",
|
|
64
70
|
type=str,
|
|
@@ -143,6 +149,11 @@ class opts:
|
|
|
143
149
|
type=str,
|
|
144
150
|
default=None,
|
|
145
151
|
)
|
|
152
|
+
no_default_query: Dec = click.option(
|
|
153
|
+
"--no-default-query",
|
|
154
|
+
is_flag=True,
|
|
155
|
+
default=False,
|
|
156
|
+
)
|
|
146
157
|
query_file: Dec = click.option(
|
|
147
158
|
"--query-file",
|
|
148
159
|
"-q",
|
esgpull/cli/search.py
CHANGED
|
@@ -18,17 +18,17 @@ from esgpull.tui import Verbosity
|
|
|
18
18
|
@groups.query_def
|
|
19
19
|
@groups.query_date
|
|
20
20
|
@groups.display
|
|
21
|
+
@groups.json_yaml
|
|
22
|
+
@opts.detail
|
|
23
|
+
@opts.no_default_query
|
|
24
|
+
@opts.show
|
|
21
25
|
@opts.dry_run
|
|
22
26
|
@opts.file
|
|
23
27
|
@opts.facets_hints
|
|
24
28
|
@opts.hints
|
|
25
|
-
@groups.json_yaml
|
|
26
|
-
@opts.detail
|
|
27
|
-
@opts.show
|
|
28
29
|
@opts.yes
|
|
29
30
|
@opts.record
|
|
30
31
|
@opts.verbosity
|
|
31
|
-
# @opts.selection_file
|
|
32
32
|
def search(
|
|
33
33
|
facets: list[str],
|
|
34
34
|
## query_def
|
|
@@ -38,6 +38,7 @@ def search(
|
|
|
38
38
|
latest: str | None,
|
|
39
39
|
replica: str | None,
|
|
40
40
|
retracted: str | None,
|
|
41
|
+
## query_date
|
|
41
42
|
date_from: datetime | None,
|
|
42
43
|
date_to: datetime | None,
|
|
43
44
|
## display
|
|
@@ -48,14 +49,14 @@ def search(
|
|
|
48
49
|
json: bool,
|
|
49
50
|
yaml: bool,
|
|
50
51
|
## ungrouped
|
|
51
|
-
show: bool,
|
|
52
52
|
detail: int | None,
|
|
53
|
+
no_default_query: bool,
|
|
54
|
+
show: bool,
|
|
53
55
|
dry_run: bool,
|
|
54
56
|
file: bool,
|
|
55
57
|
facets_hints: bool,
|
|
56
58
|
hints: list[str] | None,
|
|
57
59
|
yes: bool,
|
|
58
|
-
# selection_file: str | None,
|
|
59
60
|
record: bool,
|
|
60
61
|
verbosity: Verbosity,
|
|
61
62
|
) -> None:
|
|
@@ -64,7 +65,12 @@ def search(
|
|
|
64
65
|
|
|
65
66
|
More info
|
|
66
67
|
"""
|
|
67
|
-
esg = init_esgpull(
|
|
68
|
+
esg = init_esgpull(
|
|
69
|
+
verbosity,
|
|
70
|
+
safe=False,
|
|
71
|
+
record=record,
|
|
72
|
+
no_default_query=no_default_query,
|
|
73
|
+
)
|
|
68
74
|
with esg.ui.logging("search", onraise=Abort):
|
|
69
75
|
query = parse_query(
|
|
70
76
|
facets=facets,
|
|
@@ -77,6 +83,7 @@ def search(
|
|
|
77
83
|
)
|
|
78
84
|
query.compute_sha()
|
|
79
85
|
esg.graph.resolve_require(query)
|
|
86
|
+
query = esg.insert_default_query(query)[0]
|
|
80
87
|
if show:
|
|
81
88
|
if json:
|
|
82
89
|
esg.ui.print(query.asdict(), json=True)
|
esgpull/cli/update.py
CHANGED
|
@@ -165,20 +165,23 @@ def update(
|
|
|
165
165
|
if choice == "y":
|
|
166
166
|
legacy = esg.legacy_query
|
|
167
167
|
has_legacy = legacy.state.persistent
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
168
|
+
with esg.db.commit_context():
|
|
169
|
+
for file in esg.ui.track(
|
|
170
|
+
new_files,
|
|
171
|
+
description=qf.query.rich_name,
|
|
172
|
+
):
|
|
173
|
+
file_db = esg.db.get(File, file.sha)
|
|
174
|
+
if file_db is None:
|
|
175
|
+
if esg.db.has_file_id(file):
|
|
176
|
+
logger.error(
|
|
177
|
+
"File id already exists in database, "
|
|
178
|
+
"there might be an error with its checksum"
|
|
179
|
+
f"\n{file}"
|
|
180
|
+
)
|
|
181
|
+
continue
|
|
182
|
+
file.status = FileStatus.Queued
|
|
183
|
+
esg.db.session.add(file)
|
|
184
|
+
elif has_legacy and legacy in file_db.queries:
|
|
185
|
+
esg.db.unlink(query=legacy, file=file_db)
|
|
186
|
+
esg.db.link(query=qf.query, file=file)
|
|
184
187
|
esg.ui.raise_maybe_record(Exit(0))
|
esgpull/cli/utils.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
from collections import OrderedDict
|
|
3
|
+
from collections.abc import MutableMapping, Sequence
|
|
3
4
|
from enum import Enum
|
|
4
5
|
from pathlib import Path
|
|
5
|
-
from typing import Any, Literal
|
|
6
|
+
from typing import Any, Literal
|
|
6
7
|
|
|
7
8
|
import click
|
|
8
9
|
import yaml
|
|
@@ -29,6 +30,7 @@ def init_esgpull(
|
|
|
29
30
|
safe: bool = True,
|
|
30
31
|
record: bool = False,
|
|
31
32
|
load_db: bool = True,
|
|
33
|
+
no_default_query: bool = False,
|
|
32
34
|
) -> Esgpull:
|
|
33
35
|
TempUI.verbosity = Verbosity.Errors
|
|
34
36
|
with TempUI.logging():
|
|
@@ -38,6 +40,8 @@ def init_esgpull(
|
|
|
38
40
|
record=record,
|
|
39
41
|
load_db=load_db,
|
|
40
42
|
)
|
|
43
|
+
if no_default_query:
|
|
44
|
+
esg.config.api.default_query_id = ""
|
|
41
45
|
if record:
|
|
42
46
|
esg.ui.print(get_command())
|
|
43
47
|
return esg
|
esgpull/config.py
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from collections.abc import
|
|
4
|
+
from collections.abc import Iterator, Mapping
|
|
5
5
|
from enum import Enum, auto
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import Any, cast
|
|
8
8
|
|
|
9
9
|
import tomlkit
|
|
10
|
-
from attrs import Factory, define, field
|
|
10
|
+
from attrs import Factory, define, field, fields
|
|
11
|
+
from attrs import has as attrs_has
|
|
11
12
|
from cattrs import Converter
|
|
12
13
|
from cattrs.gen import make_dict_unstructure_fn, override
|
|
13
14
|
from tomlkit import TOMLDocument
|
|
@@ -98,6 +99,7 @@ class Download:
|
|
|
98
99
|
max_concurrent: int = 5
|
|
99
100
|
disable_ssl: bool = False
|
|
100
101
|
disable_checksum: bool = False
|
|
102
|
+
show_filename: bool = False
|
|
101
103
|
|
|
102
104
|
|
|
103
105
|
@define
|
|
@@ -123,6 +125,7 @@ class API:
|
|
|
123
125
|
max_concurrent: int = 5
|
|
124
126
|
page_limit: int = 50
|
|
125
127
|
default_options: DefaultOptions = Factory(DefaultOptions)
|
|
128
|
+
default_query_id: str = ""
|
|
126
129
|
|
|
127
130
|
|
|
128
131
|
def fix_rename_search_api(doc: TOMLDocument) -> TOMLDocument:
|
|
@@ -298,15 +301,29 @@ class Config:
|
|
|
298
301
|
doc.setdefault(part, {})
|
|
299
302
|
doc = doc[part]
|
|
300
303
|
obj = getattr(obj, part)
|
|
304
|
+
value_type = getattr(fields(type(obj)), last).type
|
|
301
305
|
old_value = getattr(obj, last)
|
|
302
|
-
if
|
|
303
|
-
...
|
|
304
|
-
elif isinstance(old_value, Container):
|
|
306
|
+
if attrs_has(value_type):
|
|
305
307
|
raise KeyError(key)
|
|
306
|
-
|
|
307
|
-
value = int(value)
|
|
308
|
-
except ValueError:
|
|
308
|
+
elif value_type is str:
|
|
309
309
|
...
|
|
310
|
+
elif value_type is int:
|
|
311
|
+
try:
|
|
312
|
+
value = value_type(value)
|
|
313
|
+
except Exception:
|
|
314
|
+
...
|
|
315
|
+
elif value_type is bool:
|
|
316
|
+
if isinstance(value, bool):
|
|
317
|
+
...
|
|
318
|
+
elif isinstance(value, str):
|
|
319
|
+
if value.lower() in ["on", "true"]:
|
|
320
|
+
value = True
|
|
321
|
+
elif value.lower() in ["off", "false"]:
|
|
322
|
+
value = False
|
|
323
|
+
else:
|
|
324
|
+
raise ValueError(value)
|
|
325
|
+
else:
|
|
326
|
+
raise TypeError(value)
|
|
310
327
|
setattr(obj, last, value)
|
|
311
328
|
doc[last] = value
|
|
312
329
|
return old_value
|
esgpull/constants.py
CHANGED
esgpull/database.py
CHANGED
|
@@ -16,11 +16,10 @@ from sqlalchemy.orm import Session, joinedload, make_transient
|
|
|
16
16
|
|
|
17
17
|
from esgpull import __file__
|
|
18
18
|
from esgpull.config import Config
|
|
19
|
-
from esgpull.models import File, Table, sql
|
|
19
|
+
from esgpull.models import File, Query, Table, sql
|
|
20
20
|
from esgpull.version import __version__
|
|
21
21
|
|
|
22
22
|
# from esgpull.exceptions import NoClauseError
|
|
23
|
-
# from esgpull.models import Query
|
|
24
23
|
|
|
25
24
|
T = TypeVar("T")
|
|
26
25
|
|
|
@@ -42,8 +41,16 @@ class Database:
|
|
|
42
41
|
url = f"sqlite:///{config.paths.db / config.db.filename}"
|
|
43
42
|
return Database(url, run_migrations=run_migrations)
|
|
44
43
|
|
|
44
|
+
def _setup_sqlite(self, conn, record):
|
|
45
|
+
cursor = conn.cursor()
|
|
46
|
+
cursor.execute("PRAGMA journal_mode = WAL;")
|
|
47
|
+
cursor.execute("PRAGMA synchronous = NORMAL;")
|
|
48
|
+
cursor.execute("PRAGMA cache_size = 20000;")
|
|
49
|
+
cursor.close()
|
|
50
|
+
|
|
45
51
|
def __post_init__(self, run_migrations: bool) -> None:
|
|
46
52
|
self._engine = sa.create_engine(self.url)
|
|
53
|
+
sa.event.listen(self._engine, "connect", self._setup_sqlite)
|
|
47
54
|
self.session = Session(self._engine)
|
|
48
55
|
if run_migrations:
|
|
49
56
|
self._update()
|
|
@@ -59,10 +66,10 @@ class Database:
|
|
|
59
66
|
opts = {"version_table": "version"}
|
|
60
67
|
ctx = MigrationContext.configure(conn, opts=opts)
|
|
61
68
|
self.version = ctx.get_current_revision()
|
|
62
|
-
if self.version != head:
|
|
63
|
-
alembic.command.upgrade(alembic_config,
|
|
69
|
+
if head is not None and self.version != head:
|
|
70
|
+
alembic.command.upgrade(alembic_config, head)
|
|
64
71
|
self.version = head
|
|
65
|
-
if self.version != __version__:
|
|
72
|
+
if "+dev" not in __version__ and self.version != __version__:
|
|
66
73
|
alembic.command.revision(
|
|
67
74
|
alembic_config,
|
|
68
75
|
message="update tables",
|
|
@@ -80,6 +87,12 @@ class Database:
|
|
|
80
87
|
self.session.rollback()
|
|
81
88
|
raise
|
|
82
89
|
|
|
90
|
+
@contextmanager
|
|
91
|
+
def commit_context(self) -> Iterator[None]:
|
|
92
|
+
with self.safe:
|
|
93
|
+
yield
|
|
94
|
+
self.session.commit()
|
|
95
|
+
|
|
83
96
|
def get(
|
|
84
97
|
self,
|
|
85
98
|
table: type[Table],
|
|
@@ -132,6 +145,12 @@ class Database:
|
|
|
132
145
|
for item in items:
|
|
133
146
|
make_transient(item)
|
|
134
147
|
|
|
148
|
+
def link(self, query: Query, file: File):
|
|
149
|
+
self.session.execute(sql.query_file.link(query, file))
|
|
150
|
+
|
|
151
|
+
def unlink(self, query: Query, file: File):
|
|
152
|
+
self.session.execute(sql.query_file.unlink(query, file))
|
|
153
|
+
|
|
135
154
|
def __contains__(self, item: Table) -> bool:
|
|
136
155
|
return self.scalars(sql.count(item))[0] > 0
|
|
137
156
|
|
esgpull/esgpull.py
CHANGED
|
@@ -13,6 +13,7 @@ from rich.progress import (
|
|
|
13
13
|
DownloadColumn,
|
|
14
14
|
MofNCompleteColumn,
|
|
15
15
|
Progress,
|
|
16
|
+
ProgressColumn,
|
|
16
17
|
SpinnerColumn,
|
|
17
18
|
TaskID,
|
|
18
19
|
TextColumn,
|
|
@@ -28,6 +29,7 @@ from esgpull.exceptions import (
|
|
|
28
29
|
DownloadCancelled,
|
|
29
30
|
InvalidInstallPath,
|
|
30
31
|
NoInstallPath,
|
|
32
|
+
UnknownDefaultQueryID,
|
|
31
33
|
)
|
|
32
34
|
from esgpull.fs import Filesystem
|
|
33
35
|
from esgpull.graph import Graph
|
|
@@ -337,7 +339,10 @@ class Esgpull:
|
|
|
337
339
|
data_node = (
|
|
338
340
|
f"[blue]{task.fields['data_node']}[/]"
|
|
339
341
|
)
|
|
340
|
-
|
|
342
|
+
parts = [sha, size, speed, data_node]
|
|
343
|
+
if self.config.download.show_filename:
|
|
344
|
+
parts.append(task.fields["filename"])
|
|
345
|
+
msg = " · ".join(parts)
|
|
341
346
|
logger.info(msg)
|
|
342
347
|
live.console.print(msg)
|
|
343
348
|
yield result
|
|
@@ -366,7 +371,7 @@ class Esgpull:
|
|
|
366
371
|
MofNCompleteColumn(),
|
|
367
372
|
TimeRemainingColumn(compact=True, elapsed_when_finished=True),
|
|
368
373
|
)
|
|
369
|
-
|
|
374
|
+
file_columns: list[str | ProgressColumn] = [
|
|
370
375
|
TextColumn("[cyan][{task.id}] [b blue]{task.fields[sha]}"),
|
|
371
376
|
"[progress.percentage]{task.percentage:>3.0f}%",
|
|
372
377
|
BarColumn(),
|
|
@@ -376,6 +381,16 @@ class Esgpull:
|
|
|
376
381
|
TransferSpeedColumn(),
|
|
377
382
|
"·",
|
|
378
383
|
TextColumn("[blue]{task.fields[data_node]}"),
|
|
384
|
+
]
|
|
385
|
+
if self.config.download.show_filename:
|
|
386
|
+
file_columns.extend(
|
|
387
|
+
[
|
|
388
|
+
"·",
|
|
389
|
+
TextColumn("{task.fields[filename]}"),
|
|
390
|
+
]
|
|
391
|
+
)
|
|
392
|
+
file_progress = self.ui.make_progress(
|
|
393
|
+
*file_columns,
|
|
379
394
|
transient=True,
|
|
380
395
|
)
|
|
381
396
|
file_task_shas = {}
|
|
@@ -387,6 +402,7 @@ class Esgpull:
|
|
|
387
402
|
visible=False,
|
|
388
403
|
start=False,
|
|
389
404
|
sha=short_sha(file.sha),
|
|
405
|
+
filename=file.filename,
|
|
390
406
|
data_node=file.data_node,
|
|
391
407
|
)
|
|
392
408
|
callback = partial(file_progress.start_task, task_id)
|
|
@@ -445,3 +461,31 @@ class Esgpull:
|
|
|
445
461
|
if use_db:
|
|
446
462
|
self.db.add(*cancelled)
|
|
447
463
|
return files, errors
|
|
464
|
+
|
|
465
|
+
def replace_queries(
|
|
466
|
+
self,
|
|
467
|
+
graph: Graph,
|
|
468
|
+
mapping: tuple[str | None, str],
|
|
469
|
+
) -> None:
|
|
470
|
+
to_replace = [
|
|
471
|
+
q for q in graph.queries.values() if q.require == mapping[0]
|
|
472
|
+
]
|
|
473
|
+
for query in to_replace:
|
|
474
|
+
new_query = query.clone(compute_sha=False)
|
|
475
|
+
new_query.require = mapping[1]
|
|
476
|
+
new_query.compute_sha()
|
|
477
|
+
graph.replace(query, new_query)
|
|
478
|
+
self.replace_queries(graph, (query.sha, new_query.sha))
|
|
479
|
+
|
|
480
|
+
def insert_default_query(self, *queries: Query) -> list[Query]:
|
|
481
|
+
if self.config.api.default_query_id == "":
|
|
482
|
+
return list(queries)
|
|
483
|
+
default_query_id = self.config.api.default_query_id
|
|
484
|
+
try:
|
|
485
|
+
default_query = self.graph.get(default_query_id)
|
|
486
|
+
except KeyError:
|
|
487
|
+
raise UnknownDefaultQueryID(default_query_id)
|
|
488
|
+
graph = Graph(None)
|
|
489
|
+
graph.add(*queries)
|
|
490
|
+
self.replace_queries(graph, (None, default_query.sha))
|
|
491
|
+
return list(graph.queries.values())
|
esgpull/exceptions.py
CHANGED
|
@@ -109,8 +109,11 @@ class VirtualConfigError(EsgpullException):
|
|
|
109
109
|
"""
|
|
110
110
|
|
|
111
111
|
|
|
112
|
-
class InstallException(EsgpullException):
|
|
113
|
-
|
|
112
|
+
class InstallException(EsgpullException): ...
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class UnknownDefaultQueryID(EsgpullException):
|
|
116
|
+
msg = "{}"
|
|
114
117
|
|
|
115
118
|
|
|
116
119
|
class UntrackableQuery(EsgpullException):
|
esgpull/install_config.py
CHANGED
|
@@ -8,7 +8,7 @@ from pathlib import Path
|
|
|
8
8
|
import platformdirs
|
|
9
9
|
from typing_extensions import NotRequired, TypedDict
|
|
10
10
|
|
|
11
|
-
from esgpull.constants import ROOT_ENV
|
|
11
|
+
from esgpull.constants import INSTALLS_PATH_ENV, ROOT_ENV
|
|
12
12
|
from esgpull.exceptions import AlreadyInstalledName, AlreadyInstalledPath
|
|
13
13
|
|
|
14
14
|
|
|
@@ -45,7 +45,15 @@ class _InstallConfig:
|
|
|
45
45
|
installs: list[Install]
|
|
46
46
|
|
|
47
47
|
def __init__(self) -> None:
|
|
48
|
-
|
|
48
|
+
self.setup()
|
|
49
|
+
|
|
50
|
+
def setup(self, install_path: Path | None = None):
|
|
51
|
+
if install_path is not None:
|
|
52
|
+
user_config_dir = install_path
|
|
53
|
+
elif (env := os.environ.get(INSTALLS_PATH_ENV)) is not None:
|
|
54
|
+
user_config_dir = Path(env)
|
|
55
|
+
else:
|
|
56
|
+
user_config_dir = platformdirs.user_config_path("esgpull")
|
|
49
57
|
self.path = user_config_dir / "installs.json"
|
|
50
58
|
if self.path.is_file():
|
|
51
59
|
with self.path.open() as f:
|
esgpull/migrations/env.py
CHANGED
|
@@ -43,8 +43,10 @@ def run_migrations_offline() -> None:
|
|
|
43
43
|
url=url,
|
|
44
44
|
target_metadata=target_metadata,
|
|
45
45
|
literal_binds=True,
|
|
46
|
+
dialect_name="sqlite",
|
|
46
47
|
dialect_opts={"paramstyle": "named"},
|
|
47
48
|
version_table="version",
|
|
49
|
+
render_as_batch=True,
|
|
48
50
|
)
|
|
49
51
|
|
|
50
52
|
with context.begin_transaction():
|
|
@@ -70,6 +72,7 @@ def run_migrations_online() -> None:
|
|
|
70
72
|
connection=connection,
|
|
71
73
|
target_metadata=target_metadata,
|
|
72
74
|
version_table="version",
|
|
75
|
+
render_as_batch=True,
|
|
73
76
|
)
|
|
74
77
|
|
|
75
78
|
with context.begin_transaction():
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""update tables
|
|
2
|
+
|
|
3
|
+
Revision ID: 0.6.5
|
|
4
|
+
Revises: 0.6.4
|
|
5
|
+
Create Date: 2024-07-09 16:29:09.389585
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# revision identifiers, used by Alembic.
|
|
10
|
+
revision = "0.6.5"
|
|
11
|
+
down_revision = "0.6.4"
|
|
12
|
+
branch_labels = None
|
|
13
|
+
depends_on = None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def upgrade() -> None:
|
|
17
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
18
|
+
pass
|
|
19
|
+
# ### end Alembic commands ###
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def downgrade() -> None:
|
|
23
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
24
|
+
pass
|
|
25
|
+
# ### end Alembic commands ###
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""update tables
|
|
2
|
+
|
|
3
|
+
Revision ID: 0.7.0
|
|
4
|
+
Revises: 0.6.5
|
|
5
|
+
Create Date: 2024-09-12 16:32:48.745570
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from alembic import op
|
|
9
|
+
import sqlalchemy as sa
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# revision identifiers, used by Alembic.
|
|
13
|
+
revision = '0.7.0'
|
|
14
|
+
down_revision = '0.6.5'
|
|
15
|
+
branch_labels = None
|
|
16
|
+
depends_on = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade() -> None:
|
|
20
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
21
|
+
with op.batch_alter_table('file', schema=None) as batch_op:
|
|
22
|
+
batch_op.alter_column('size',
|
|
23
|
+
existing_type=sa.INTEGER(),
|
|
24
|
+
type_=sa.BigInteger(),
|
|
25
|
+
existing_nullable=False)
|
|
26
|
+
|
|
27
|
+
# ### end Alembic commands ###
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def downgrade() -> None:
|
|
31
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
32
|
+
with op.batch_alter_table('file', schema=None) as batch_op:
|
|
33
|
+
batch_op.alter_column('size',
|
|
34
|
+
existing_type=sa.BigInteger(),
|
|
35
|
+
type_=sa.INTEGER(),
|
|
36
|
+
existing_nullable=False)
|
|
37
|
+
|
|
38
|
+
# ### end Alembic commands ###
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""update tables
|
|
2
|
+
|
|
3
|
+
Revision ID: 0.7.1
|
|
4
|
+
Revises: 0.7.0
|
|
5
|
+
Create Date: 2024-09-12 17:00:21.157499
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from alembic import op
|
|
9
|
+
import sqlalchemy as sa
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# revision identifiers, used by Alembic.
|
|
13
|
+
revision = '0.7.1'
|
|
14
|
+
down_revision = '0.7.0'
|
|
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 ###
|
esgpull/models/query.py
CHANGED
|
@@ -235,7 +235,7 @@ class Query(Base):
|
|
|
235
235
|
return nb_files is not None and nb_files > 0
|
|
236
236
|
|
|
237
237
|
def files_count_size(self, *status: FileStatus) -> tuple[int, int]:
|
|
238
|
-
stmt: sa.Select[tuple[int, int
|
|
238
|
+
stmt: sa.Select[tuple[int, int]] = (
|
|
239
239
|
sa.select(sa.func.count("*"), sa.func.sum(File.size))
|
|
240
240
|
.join_from(query_file_proxy, File)
|
|
241
241
|
.where(query_file_proxy.c.query_sha == self.sha)
|
esgpull/models/selection.py
CHANGED
|
@@ -71,6 +71,10 @@ class Selection(Base):
|
|
|
71
71
|
|
|
72
72
|
setattr(cls, name, property(getter, setter))
|
|
73
73
|
|
|
74
|
+
@classmethod
|
|
75
|
+
def reset(cls) -> None:
|
|
76
|
+
cls.configure(*DefaultFacets, *BaseFacets, replace=True)
|
|
77
|
+
|
|
74
78
|
@classmethod
|
|
75
79
|
def configure(cls, *names: str, replace: bool = True) -> None:
|
|
76
80
|
nameset = set(names) | {f"!{name}" for name in names}
|
|
@@ -195,7 +199,8 @@ BaseFacets = [
|
|
|
195
199
|
"member_id",
|
|
196
200
|
"cmor_table",
|
|
197
201
|
"grid_label",
|
|
202
|
+
"nominal_resolution",
|
|
198
203
|
]
|
|
199
204
|
|
|
200
205
|
|
|
201
|
-
Selection.
|
|
206
|
+
Selection.reset()
|
esgpull/models/sql.py
CHANGED
|
@@ -217,9 +217,7 @@ class selection:
|
|
|
217
217
|
@functools.cache
|
|
218
218
|
def orphans() -> sa.Select[tuple[Selection]]:
|
|
219
219
|
return (
|
|
220
|
-
sa.select(Selection)
|
|
221
|
-
.outerjoin(Query)
|
|
222
|
-
.where(Query.sha == None) # noqa
|
|
220
|
+
sa.select(Selection).outerjoin(Query).where(Query.sha == None) # noqa
|
|
223
221
|
)
|
|
224
222
|
|
|
225
223
|
|
|
@@ -256,3 +254,19 @@ class synda_file:
|
|
|
256
254
|
@staticmethod
|
|
257
255
|
def with_ids(*ids: int) -> sa.Select[tuple[SyndaFile]]:
|
|
258
256
|
return sa.select(SyndaFile).where(SyndaFile.file_id.in_(ids))
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
class query_file:
|
|
260
|
+
@staticmethod
|
|
261
|
+
def link(query: Query, file: File) -> sa.Insert:
|
|
262
|
+
return sa.insert(query_file_proxy).values(
|
|
263
|
+
query_sha=query.sha, file_sha=file.sha
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
@staticmethod
|
|
267
|
+
def unlink(query: Query, file: File) -> sa.Delete:
|
|
268
|
+
return (
|
|
269
|
+
sa.delete(query_file_proxy)
|
|
270
|
+
.where(query_file_proxy.c.query_sha == query.sha)
|
|
271
|
+
.where(query_file_proxy.c.file_sha == file.sha)
|
|
272
|
+
)
|
esgpull/tui.py
CHANGED
|
@@ -71,8 +71,7 @@ class DummyLive:
|
|
|
71
71
|
def __enter__(self) -> DummyLive:
|
|
72
72
|
return self
|
|
73
73
|
|
|
74
|
-
def __exit__(self, *args):
|
|
75
|
-
...
|
|
74
|
+
def __exit__(self, *args): ...
|
|
76
75
|
|
|
77
76
|
@property
|
|
78
77
|
def console(self) -> DummyConsole:
|
|
@@ -259,9 +258,9 @@ class UI:
|
|
|
259
258
|
# use _console to avoid recording the progress bar
|
|
260
259
|
return Live(renderables, console=_console)
|
|
261
260
|
|
|
262
|
-
def track(self, iterable: Iterable[T]) -> Iterable[T]:
|
|
261
|
+
def track(self, iterable: Iterable[T], **kwargs) -> Iterable[T]:
|
|
263
262
|
# use _console to avoid recording the progress bar
|
|
264
|
-
return track(iterable, console=_console)
|
|
263
|
+
return track(iterable, console=_console, **kwargs)
|
|
265
264
|
|
|
266
265
|
def make_progress(
|
|
267
266
|
self,
|
esgpull/version.py
CHANGED
|
@@ -1,63 +1,42 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: esgpull
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.1
|
|
4
4
|
Summary: ESGF data discovery, download, replication tool
|
|
5
|
-
Author-Email: Sven Rodriguez <srodriguez@ipsl.fr>
|
|
6
|
-
License: BSD 3-Clause License
|
|
7
|
-
|
|
8
|
-
Copyright (c) 2023, Institut Pierre-Simon Laplace (IPSL) and contributors
|
|
9
|
-
|
|
10
|
-
Redistribution and use in source and binary forms, with or without
|
|
11
|
-
modification, are permitted provided that the following conditions are met:
|
|
12
|
-
|
|
13
|
-
1. Redistributions of source code must retain the above copyright notice, this
|
|
14
|
-
list of conditions and the following disclaimer.
|
|
15
|
-
|
|
16
|
-
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
17
|
-
this list of conditions and the following disclaimer in the documentation
|
|
18
|
-
and/or other materials provided with the distribution.
|
|
19
|
-
|
|
20
|
-
3. Neither the name of the copyright holder nor the names of its
|
|
21
|
-
contributors may be used to endorse or promote products derived from
|
|
22
|
-
this software without specific prior written permission.
|
|
23
|
-
|
|
24
|
-
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
25
|
-
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
26
|
-
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
27
|
-
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
28
|
-
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
29
|
-
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
30
|
-
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
31
|
-
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
32
|
-
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
33
|
-
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
34
5
|
Project-URL: Repository, https://github.com/ESGF/esgf-download
|
|
35
6
|
Project-URL: Documentation, https://esgf.github.io/esgf-download/
|
|
7
|
+
Author-email: Sven Rodriguez <srodriguez@ipsl.fr>
|
|
8
|
+
License: BSD-3-Clause
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
36
15
|
Requires-Python: >=3.10
|
|
37
|
-
Requires-Dist: MyProxyClient>=2.1.0
|
|
38
16
|
Requires-Dist: aiofiles>=22.1.0
|
|
17
|
+
Requires-Dist: aiostream>=0.4.5
|
|
39
18
|
Requires-Dist: alembic>=1.8.1
|
|
40
|
-
Requires-Dist:
|
|
19
|
+
Requires-Dist: attrs>=22.1.0
|
|
20
|
+
Requires-Dist: cattrs>=22.2.0
|
|
41
21
|
Requires-Dist: click-params>=0.4.0
|
|
22
|
+
Requires-Dist: click>=8.1.3
|
|
42
23
|
Requires-Dist: httpx>=0.23.0
|
|
24
|
+
Requires-Dist: myproxyclient>=2.1.0
|
|
43
25
|
Requires-Dist: nest-asyncio>=1.5.6
|
|
44
|
-
Requires-Dist:
|
|
26
|
+
Requires-Dist: platformdirs>=2.6.2
|
|
27
|
+
Requires-Dist: pyopenssl>=22.1.0
|
|
28
|
+
Requires-Dist: pyparsing>=3.0.9
|
|
45
29
|
Requires-Dist: pyyaml>=6.0
|
|
46
|
-
Requires-Dist: tomlkit>=0.11.5
|
|
47
30
|
Requires-Dist: rich>=12.6.0
|
|
48
|
-
Requires-Dist: sqlalchemy>=2.0.0b2
|
|
49
31
|
Requires-Dist: setuptools>=65.4.1
|
|
50
|
-
Requires-Dist:
|
|
51
|
-
Requires-Dist:
|
|
52
|
-
Requires-Dist: cattrs>=22.2.0
|
|
53
|
-
Requires-Dist: platformdirs>=2.6.2
|
|
54
|
-
Requires-Dist: pyparsing>=3.0.9
|
|
32
|
+
Requires-Dist: sqlalchemy>=2.0.0b2
|
|
33
|
+
Requires-Dist: tomlkit>=0.11.5
|
|
55
34
|
Description-Content-Type: text/markdown
|
|
56
35
|
|
|
57
|
-
[](https://pdm.fming.dev)
|
|
58
|
-
|
|
59
36
|
# esgpull - ESGF data management utility
|
|
60
37
|
|
|
38
|
+
[](https://rye.astral.sh)
|
|
39
|
+
|
|
61
40
|
`esgpull` is a tool that simplifies usage of the [ESGF Search API](https://esgf.github.io/esg-search/ESGF_Search_RESTful_API.html) for data discovery, and manages procedures related to downloading and storing files from ESGF.
|
|
62
41
|
|
|
63
42
|
```py
|
|
@@ -1,15 +1,28 @@
|
|
|
1
|
-
esgpull-0.6.4.dist-info/METADATA,sha256=SsHJeynCInwtASL4sPMRytgrzjd6ZKkMj_ibmMDBudY,4473
|
|
2
|
-
esgpull-0.6.4.dist-info/WHEEL,sha256=mbxFTmdEUhG7evcdMkR3aBt9SWcoFBJ4CDwnfguNegA,90
|
|
3
|
-
esgpull-0.6.4.dist-info/entry_points.txt,sha256=nfKsESeZyCiVD6dDCTCturf-vRnI-6GCuwqv9jxXCq8,46
|
|
4
|
-
esgpull-0.6.4.dist-info/licenses/LICENSE,sha256=lUqGPGWDHHxjkUDuYgjLLY2XQXXn_EHU7fnrQWHGugc,1540
|
|
5
1
|
esgpull/__init__.py,sha256=XItFDIMNmFUNNcKtUgXdfmGwUIWt4AAv0a4mZkfj5P8,240
|
|
6
2
|
esgpull/auth.py,sha256=QZ-l1ySLMP0fvuwYHRLv9FZYp1gqfju_eGaTMDByUxw,5205
|
|
7
|
-
esgpull/
|
|
8
|
-
esgpull/
|
|
3
|
+
esgpull/config.py,sha256=3E3jRGem09M_Hm1_uqWHu4dy7gzgShiUecZp-6XC9E4,12149
|
|
4
|
+
esgpull/constants.py,sha256=fQE6vE2EU8V5m2lRNYBeMkL3u1_Em343Zs8u7i6ZRlo,1118
|
|
5
|
+
esgpull/context.py,sha256=oSymljW0hZLGhCJxo7lh96Eq3SI_KOT_jFMPHckIrm0,23203
|
|
6
|
+
esgpull/database.py,sha256=TDo5xIXWyMEPFpaeygVhLZkeDAwYlkBG9UWhmL6ntfo,6144
|
|
7
|
+
esgpull/download.py,sha256=3YcGLrffzAmuiV49tYsW-7PuVYcSFtbsZnH2Q26khm0,5491
|
|
8
|
+
esgpull/esgpull.py,sha256=R7lnlyOo92FB_Qem5ST4hNA5Iwn9PHYsvCpKxqRX-qM,17717
|
|
9
|
+
esgpull/exceptions.py,sha256=wgLyhyIITdusNucPjnnURJX1Jxv1VVIr9PzJV_77qhg,3275
|
|
10
|
+
esgpull/fs.py,sha256=O52QD7DVJImYYf9wWECMNWYrGt8EcHQ3z9TzroK6VAY,7623
|
|
11
|
+
esgpull/graph.py,sha256=CeaRcIAWMpVAJ-o6v6b3ffzy-ter-8HZ3tmxoN-Z-FM,15925
|
|
12
|
+
esgpull/install_config.py,sha256=hzYpcHMtPMOK9fYcvVH-Hn_8zYsbs3yXlYgMumXo1zE,5598
|
|
13
|
+
esgpull/processor.py,sha256=noenWExukON2P4klMN8Vt1ALzApmvc5gpsFqd7J5g78,5446
|
|
14
|
+
esgpull/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
esgpull/result.py,sha256=f64l9gPFpFWgctmHVYrNWJvuXXB1sxpxXzJEIssLXxc,1020
|
|
16
|
+
esgpull/tui.py,sha256=GTk86277Vv4msP2YJGpFWMQqxFhW_WSvxViU9nDFeAU,10407
|
|
17
|
+
esgpull/utils.py,sha256=eKipKCqj15dzCAqs1o7x5O1XClWPmDEM-qSvNwGZmXY,1233
|
|
18
|
+
esgpull/version.py,sha256=IHT4mKrIr8eV-C3HtmIVD85iGVH25n2ohoff31kaJ1A,93
|
|
19
|
+
esgpull/cli/__init__.py,sha256=mvxiV7bRJlJ3uAfxr6hHDcuQDvy70TGkKyY5W4wE4SY,1686
|
|
20
|
+
esgpull/cli/add.py,sha256=PhTe-jY7CNWYVtD1qYptGgIwYat2RYUfV-RHG_-SWbU,3270
|
|
9
21
|
esgpull/cli/autoremove.py,sha256=g76_qnc3q84zSO7W0JsbWtGN4AfWBXTkQJO6gPCs2Pw,1336
|
|
10
22
|
esgpull/cli/config.py,sha256=JbSa2NvXxccbOpEyvJd4vSlBnwb_NClx5sCJp_WY7-k,4048
|
|
11
23
|
esgpull/cli/convert.py,sha256=Nonu49aijY3OFq2MNCqs7EQ08NXFiT0ehvNxoGp4NCE,9847
|
|
12
|
-
esgpull/cli/
|
|
24
|
+
esgpull/cli/datasets.py,sha256=wO_gZUhvWy027q2jX2hAW25wJkIGrYHHCYuGAbcTmG8,2374
|
|
25
|
+
esgpull/cli/decorators.py,sha256=X5Ja6HlB_AgpTohpbF2EjtT5sA0IdPxX20g-j1K7raM,6727
|
|
13
26
|
esgpull/cli/download.py,sha256=3_Fm8JJYBWKs63oQ1OLaHJCv9ccbeY2gIW8SDasaYNE,2356
|
|
14
27
|
esgpull/cli/facet.py,sha256=V1u-DxNkhswwSt0qpXvuHrCI_tE8jAJGEe6_fMhYbaM,584
|
|
15
28
|
esgpull/cli/get.py,sha256=2WXL01Ri0P_2Rf1xrp9bnsrrxir6whxkAC0SnohjFpg,678
|
|
@@ -17,25 +30,15 @@ esgpull/cli/install.py,sha256=fd8nKwIFvOivgn_gOGn7XIk1BB9LXnhQB47KuIIy5AU,2880
|
|
|
17
30
|
esgpull/cli/login.py,sha256=FZ63SsB4fCDixwf7gMUR697Dk89W1OvpgeadKE4IqEU,2153
|
|
18
31
|
esgpull/cli/remove.py,sha256=p26hPhsgAgmAF4IsvjAI3jtzlIUq3k8Rxb0GTKYoEQM,2517
|
|
19
32
|
esgpull/cli/retry.py,sha256=UVpAjW_N7l6RTJ-T5qXojwcXPzzjT7sDKb_wBdvavrg,1310
|
|
20
|
-
esgpull/cli/search.py,sha256=
|
|
33
|
+
esgpull/cli/search.py,sha256=c2OI0Xvw6h2zBnC8msjxidKJZ0KKUuWJK9FuAmQ15ZU,6369
|
|
21
34
|
esgpull/cli/self.py,sha256=7nEEsK5W_Pth8IOSmvJHRlfwPPgXldhHAQK9yqf01S8,7932
|
|
22
35
|
esgpull/cli/show.py,sha256=4O1TvH31x9Fg-SI0a8dtblMZuJHJ30eBXOV9bwEl9Lk,1897
|
|
23
36
|
esgpull/cli/status.py,sha256=jgGBbmvcCjw21QTDNXdKAUo664p6junRg5kMwQ7My2I,2470
|
|
24
37
|
esgpull/cli/track.py,sha256=Q9ZvvV5FFGzp6wQZflAd_OFmqhAWgl1JFBad2dCbEF0,3089
|
|
25
|
-
esgpull/cli/update.py,sha256=
|
|
26
|
-
esgpull/cli/utils.py,sha256=
|
|
27
|
-
esgpull/config.py,sha256=WelBOrkaTPldk4UasFJvku5DRh7062kO6CPHNWSV3Wc,11529
|
|
28
|
-
esgpull/constants.py,sha256=3eq5Yow519p3YLAKjNdwOt1Ooei62FE9HykqCiyhZYE,1074
|
|
29
|
-
esgpull/context.py,sha256=oSymljW0hZLGhCJxo7lh96Eq3SI_KOT_jFMPHckIrm0,23203
|
|
30
|
-
esgpull/database.py,sha256=4JONNJpt7JzeohgBtRO2hpQUUFtV_wTncU20HjWPGVA,5431
|
|
31
|
-
esgpull/download.py,sha256=3YcGLrffzAmuiV49tYsW-7PuVYcSFtbsZnH2Q26khm0,5491
|
|
32
|
-
esgpull/esgpull.py,sha256=nmRZA72erSav2EfJJUNljkZzVKogKPRWuYHyNiCB7NI,16098
|
|
33
|
-
esgpull/exceptions.py,sha256=uK5ApBZJPwLlR7hYpvwQdApVtugn7_-N-MMmR8HpEgE,3215
|
|
34
|
-
esgpull/fs.py,sha256=O52QD7DVJImYYf9wWECMNWYrGt8EcHQ3z9TzroK6VAY,7623
|
|
35
|
-
esgpull/graph.py,sha256=CeaRcIAWMpVAJ-o6v6b3ffzy-ter-8HZ3tmxoN-Z-FM,15925
|
|
36
|
-
esgpull/install_config.py,sha256=mGZLaYEDaXlmX9obl_9d7jTyweT8UcP3XSo5RZRkkpk,5295
|
|
38
|
+
esgpull/cli/update.py,sha256=x_R4x7hdUp5qowcFUibIMozwWTlJybuZMY_EXWhj_kI,7024
|
|
39
|
+
esgpull/cli/utils.py,sha256=mMCVUuHEG1g3KPGq_Jx1p1AfJCVFwxR23GsQRtV88xA,7262
|
|
37
40
|
esgpull/migrations/README,sha256=heMzebYwlGhnE8_4CWJ4LS74WoEZjBy-S-mIJRxAEKI,39
|
|
38
|
-
esgpull/migrations/env.py,sha256=
|
|
41
|
+
esgpull/migrations/env.py,sha256=am2HhFrlIZNlXCaA5Ye7yKbIJ2MRSO5UFmUwB8l9fyE,2285
|
|
39
42
|
esgpull/migrations/script.py.mako,sha256=HNlf26BI1xvQKjiUojnj15BPrVUfVVr81IOgliJf83c,510
|
|
40
43
|
esgpull/migrations/versions/0.3.0_update_tables.py,sha256=r0rGX_cQSNX74JM5tZ8iqgstsAdfJH5rO3gGPkOxpok,5540
|
|
41
44
|
esgpull/migrations/versions/0.3.1_update_tables.py,sha256=Fm7o-ZVvuyFcyReXXLhXQJQ-feCrbY2c0wnKwYnj_5k,493
|
|
@@ -58,22 +61,23 @@ esgpull/migrations/versions/0.6.1_update_tables.py,sha256=L4mkKlZUz4ftINwNeVNsPh
|
|
|
58
61
|
esgpull/migrations/versions/0.6.2_update_tables.py,sha256=LFUVgDhxSll9FpvNFm-idD5t-SI3R30IO7cTzvShLVI,493
|
|
59
62
|
esgpull/migrations/versions/0.6.3_update_tables.py,sha256=ut4M0b90OWVB9pKSGTJ4Bl5cucXFJzRD4-WA_ego0WY,493
|
|
60
63
|
esgpull/migrations/versions/0.6.4_update_tables.py,sha256=PFALlSAjCFOqqoQgjq2TF0HjGc22fVdStMK2NNNa3Yk,493
|
|
64
|
+
esgpull/migrations/versions/0.6.5_update_tables.py,sha256=NYO8vnS4h_g4Co4M1CJibB2WYLqmVAy6ZaApFk-do3c,493
|
|
65
|
+
esgpull/migrations/versions/0.7.0_update_tables.py,sha256=aCmR7q-1V49JIfvFR-1iVskTwR3J8O1iKysgF0oJZ4k,971
|
|
66
|
+
esgpull/migrations/versions/0.7.1_update_tables.py,sha256=f_PakdA0ZKmekcWWDC86u5PlUrF_Kl2xzkNV3Am9sd0,541
|
|
61
67
|
esgpull/models/__init__.py,sha256=rfa1yGLVDahFrnhq_8TPGzr7_oeBOFKNVD9EF0slUtY,725
|
|
62
68
|
esgpull/models/base.py,sha256=3nbR2lYMHWfovWz4EiAJ2bIvKpMadRvYZDdMRQDvN7M,1237
|
|
63
69
|
esgpull/models/dataset.py,sha256=1fOIVYIWKK5BivqvBpjfxrNpy9VfUHZng9Yc6ipPK1Q,905
|
|
64
70
|
esgpull/models/facet.py,sha256=COMgFjsxQcgb4uGMLy5JDRFWeMSHO-QDdG-cWpwvYqQ,459
|
|
65
71
|
esgpull/models/file.py,sha256=-8PPYtq7BWp-O_QtCDbkLdhTGTPhI1F1nodQacMnYGA,1517
|
|
66
72
|
esgpull/models/options.py,sha256=yzADDeDgtMymFdKG31JMl8sds-LnWDmjtaCpQ6EUKQ0,4745
|
|
67
|
-
esgpull/models/query.py,sha256=
|
|
68
|
-
esgpull/models/selection.py,sha256=
|
|
69
|
-
esgpull/models/sql.py,sha256=
|
|
73
|
+
esgpull/models/query.py,sha256=sMtwdnTRcPzK_KCIXXN9YEQ8TdaGW2z3m6RDeyQJU94,16712
|
|
74
|
+
esgpull/models/selection.py,sha256=QCX_2eoCJcYB1ULll-J7UV5lCpkweis92FANQY8pH0o,5895
|
|
75
|
+
esgpull/models/sql.py,sha256=Wlw9cGJ_buv51qC0grV8HvprEwoG92e2zagxkQXKosk,7481
|
|
70
76
|
esgpull/models/synda_file.py,sha256=6o5unPhzVJGnbpA2MxcS0r-hrBwocHYVnLrqjSGtmuk,2387
|
|
71
77
|
esgpull/models/tag.py,sha256=5CQDB9rAeCqog63ec9LPFN46HOFNkHPy-maY4gkBQ3E,461
|
|
72
78
|
esgpull/models/utils.py,sha256=exwlIlIKYjhhfUE82w1kU_HeSQOSY97PTvpkhW0udMA,1631
|
|
73
|
-
esgpull/
|
|
74
|
-
esgpull/
|
|
75
|
-
esgpull/
|
|
76
|
-
esgpull/
|
|
77
|
-
esgpull
|
|
78
|
-
esgpull/version.py,sha256=WMmvm2Keb76yMz8OL_h4fKT34Xpi-1BVfCiTn2QGzz4,22
|
|
79
|
-
esgpull-0.6.4.dist-info/RECORD,,
|
|
79
|
+
esgpull-0.7.1.dist-info/METADATA,sha256=1igfjfbOvh_U8yLbJbv027Q2t4Ft5NteUGnOSleOMPA,3052
|
|
80
|
+
esgpull-0.7.1.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
81
|
+
esgpull-0.7.1.dist-info/entry_points.txt,sha256=vyh7HvFrCp4iyMrTkDoSF3weaYrlNj2OJe0Fq5q4QB4,45
|
|
82
|
+
esgpull-0.7.1.dist-info/licenses/LICENSE,sha256=lUqGPGWDHHxjkUDuYgjLLY2XQXXn_EHU7fnrQWHGugc,1540
|
|
83
|
+
esgpull-0.7.1.dist-info/RECORD,,
|
|
File without changes
|