materials-commons-cli 2.3.0b6__tar.gz → 2.3.0b8__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.
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/PKG-INFO +1 -1
- materials_commons_cli-2.3.0b8/materials_commons/cli/__init__.py +1 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/async_reconciler.py +26 -21
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/downloader.py +11 -7
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/parser.py +3 -1
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/downloader/file_download_manager.py +3 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/downloader/file_downloader.py +5 -1
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/projects.py +10 -7
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/uploader/file_uploader.py +1 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/ls2.py +22 -17
- materials_commons_cli-2.3.0b8/materials_commons/cli/subcommands/mcp.py +86 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/up.py +2 -1
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/walk.py +17 -13
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons_cli.egg-info/PKG-INFO +1 -1
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons_cli.egg-info/SOURCES.txt +1 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons_cli.egg-info/requires.txt +3 -2
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/setup.py +1 -0
- materials_commons_cli-2.3.0b6/materials_commons/cli/__init__.py +0 -1
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/LICENSE +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/README.md +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/LICENSE.txt +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/config.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/filedb.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/local_project.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/models.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/old/__init__.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/old/cloned_project.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/old/exceptions.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/old/file_functions.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/old/functions.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/old/globus.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/old/list_objects.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/old/print_formatter.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/old/sqltable.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/old/tmp_functions.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/old/tree_functions.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/old/treedb.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/old/user_config.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/reconcile2.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/reconcile3.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/requests.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/run.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/__init__.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/command_handlers/__init__.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/command_handlers/admin_handler_lookup.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/command_handlers/download_handler_lookup.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/command_handlers/list_handler_lookup.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/command_handlers/multi_handler_lookup.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/command_handlers/protocol.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/command_handlers/search_find_handler_lookup.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/command_handlers/upload_handler_lookup.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/db/__init__.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/db/db_manager.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/downloader/__init__.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/indexer/__init__.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/indexer/file_index_manager.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/local_rest_server.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/ocommand_handlers.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/project_filedbs.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/service_container.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/service_runtime.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/uploader/__init__.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/uploader/client_api.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/uploader/file_upload_manager.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/server/websocket_server.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/__init__.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/clone.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/config.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/dataset.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/down.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/down2.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/expt.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/fetch.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/globus.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/init.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/ls.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/mkdir.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/mv.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/proj.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/remote.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/rm.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/runners/__init__.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/runners/download_globus_runner.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/runners/download_v1_runner.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/scan.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/server.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/subcommands/versions.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons_cli.egg-info/dependency_links.txt +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons_cli.egg-info/entry_points.txt +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons_cli.egg-info/not-zip-safe +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons_cli.egg-info/top_level.txt +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/setup.cfg +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/test/test_api.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/test/test_functions.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/test/test_mc_dataset.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/test/test_mc_down.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/test/test_mc_mkdir.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/test/test_mc_mv.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/test/test_mc_proj.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/test/test_mc_rm.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/test/test_mc_up.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/test/test_treecompare_no_cache.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/test/test_treecompare_with_cache.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/test/test_treedb.py +0 -0
- {materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/test/test_user_config.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '2.3.0b8'
|
|
@@ -17,12 +17,14 @@ class AsyncReconciler:
|
|
|
17
17
|
db: FileIndexDB,
|
|
18
18
|
reconcile_mode: ReconcileMode,
|
|
19
19
|
# recompute_checksum: bool = True,
|
|
20
|
-
max_concurrent: int = 10
|
|
20
|
+
max_concurrent: int = 10,
|
|
21
|
+
batch_size: Optional[int] = None):
|
|
21
22
|
self.proj = proj
|
|
22
23
|
self.db = db
|
|
23
24
|
# self.recompute_checksum = recompute_checksum
|
|
24
25
|
self.max_concurrent = max_concurrent
|
|
25
26
|
self.reconcile_mode = reconcile_mode
|
|
27
|
+
self.batch_size = batch_size or max_concurrent
|
|
26
28
|
self.reconciler = SingleFileReconciler(proj=self.proj, mode=reconcile_mode)
|
|
27
29
|
|
|
28
30
|
async def walk(self, path: str | Path, listdir_fn: ListDirFunc, recursive: bool = False,
|
|
@@ -35,36 +37,39 @@ class AsyncReconciler:
|
|
|
35
37
|
|
|
36
38
|
async for current_path, observations in async_walk(path, recursive=recursive, listdir_fn=listdir_fn,
|
|
37
39
|
ignore_fn=ignore_fn):
|
|
38
|
-
path_states: dict[str, FileState] = {}
|
|
39
40
|
remote_dir = projects.local_to_remote_project_path(Path(self.proj.local_path), Path(current_path))
|
|
40
41
|
|
|
41
42
|
# Create a map of file_records to name
|
|
42
43
|
file_records = await self.db.get_files_by_dir(remote_dir.as_posix())
|
|
43
44
|
file_records_map = {file_record.name: file_record for file_record in file_records}
|
|
44
45
|
|
|
45
|
-
#
|
|
46
|
-
for
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
path_states[obs.name] = FileState(observation=obs)
|
|
46
|
+
# Handle a batch of records rather than the entire directory
|
|
47
|
+
for start in range(0, len(observations), self.batch_size):
|
|
48
|
+
batch_observations = observations[start:start + self.batch_size]
|
|
49
|
+
path_states: dict[str, FileState] = {}
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
# Go through all the observations and match to the file records
|
|
52
|
+
for obs in batch_observations:
|
|
53
|
+
obs.file_record = file_records_map.get(obs.name, None)
|
|
54
|
+
obs.remote_path = remote_dir / obs.name
|
|
55
|
+
path_states[obs.name] = FileState(observation=obs)
|
|
54
56
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
# 3. Only local entry exists
|
|
59
|
-
# 4. Lookup failed, and we got an exception
|
|
57
|
+
results = await asyncio.gather(
|
|
58
|
+
*[single_entry_reconcile(state.observation) for state in path_states.values()],
|
|
59
|
+
return_exceptions=True)
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
# At this point path_entries contains entries in one of 4 states:
|
|
62
|
+
# 1. Both remote and local entries exist
|
|
63
|
+
# 2. Only remote entry exists
|
|
64
|
+
# 3. Only local entry exists
|
|
65
|
+
# 4. Lookup failed, and we got an exception
|
|
66
|
+
for state, result in zip(path_states.values(), results):
|
|
67
|
+
if isinstance(result, Exception):
|
|
68
|
+
state.exception = result
|
|
69
|
+
else:
|
|
70
|
+
state.file_decision = result
|
|
66
71
|
|
|
67
|
-
|
|
72
|
+
yield current_path, path_states
|
|
68
73
|
|
|
69
74
|
async def reconcile_file(self, path: str | Path) -> FileState:
|
|
70
75
|
remote_project_path = projects.local_to_remote_project_path(Path(self.proj.local_path), Path(path))
|
{materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/downloader.py
RENAMED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import traceback
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
|
|
4
5
|
from igittigitt import IgnoreParser, igittigitt
|
|
@@ -45,13 +46,7 @@ class Downloader:
|
|
|
45
46
|
try:
|
|
46
47
|
for path in paths:
|
|
47
48
|
p = Path(path)
|
|
48
|
-
if p.
|
|
49
|
-
# Walk and download local dir
|
|
50
|
-
await self._download_dir(p, recursive=recursive)
|
|
51
|
-
elif p.is_file():
|
|
52
|
-
# Download local file
|
|
53
|
-
await self._download_file(p)
|
|
54
|
-
else:
|
|
49
|
+
if not p.exists():
|
|
55
50
|
# If we are here, then the path entry is a remote path. We look up the path to
|
|
56
51
|
# determine the type of download to perform.
|
|
57
52
|
remote_path = self.proj.to_remote_path(p)
|
|
@@ -71,9 +66,16 @@ class Downloader:
|
|
|
71
66
|
else:
|
|
72
67
|
# Remote entry is a file
|
|
73
68
|
await self._download_file(p, force=force)
|
|
69
|
+
elif p.is_dir():
|
|
70
|
+
# Walk and download local dir
|
|
71
|
+
await self._download_dir(p, recursive=recursive)
|
|
72
|
+
elif p.is_file():
|
|
73
|
+
# Download local file
|
|
74
|
+
await self._download_file(p)
|
|
74
75
|
|
|
75
76
|
except Exception as e:
|
|
76
77
|
logger.error(f"Unexpected error during download: {e}")
|
|
78
|
+
traceback.print_exc()
|
|
77
79
|
return False
|
|
78
80
|
|
|
79
81
|
finally:
|
|
@@ -114,3 +116,5 @@ class Downloader:
|
|
|
114
116
|
print(f"Downloading {entry_name} to {current_path / entry_name}...")
|
|
115
117
|
transfer_id = await self.container.file_download_manager.download_file(download_request)
|
|
116
118
|
self.transfer_ids.append(transfer_id)
|
|
119
|
+
else:
|
|
120
|
+
print(f"Skipping download of {entry_name} because {file_state.file_decision.reason}")
|
{materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/parser.py
RENAMED
|
@@ -25,6 +25,7 @@ from materials_commons.cli.subcommands.globus import globus_subcommand
|
|
|
25
25
|
from materials_commons.cli.subcommands.init import init_subcommand
|
|
26
26
|
from materials_commons.cli.subcommands.ls import ls_subcommand
|
|
27
27
|
from materials_commons.cli.subcommands.ls2 import ls2_subcommand
|
|
28
|
+
from materials_commons.cli.subcommands.mcp import mcp_subcommand
|
|
28
29
|
from materials_commons.cli.subcommands.mkdir import mkdir_subcommand
|
|
29
30
|
from materials_commons.cli.subcommands.mv import mv_subcommand
|
|
30
31
|
# from materials_commons.cli.subcommands.proc import ProcSubcommand
|
|
@@ -58,7 +59,8 @@ standard_usage = [
|
|
|
58
59
|
# {'name': 'ls2', 'desc': 'List directory contents (experimental)', 'subcommand': ls2_subcommand},
|
|
59
60
|
# {'name': 'down2', 'desc': 'Donwload files (experimental)', 'subcommand': down2_subcommand},
|
|
60
61
|
{'name': 'config', 'desc': 'Configure `mc`', 'subcommand': config_subcommand},
|
|
61
|
-
{'name': 'server', 'desc': 'Run MC Server', 'subcommand': server_subcommand}
|
|
62
|
+
{'name': 'server', 'desc': 'Run MC Server', 'subcommand': server_subcommand},
|
|
63
|
+
{'name': 'mcp', 'desc': 'Run MCP Server', 'subcommand': mcp_subcommand},
|
|
62
64
|
]
|
|
63
65
|
standard_interfaces = {d['name']: d for d in standard_usage}
|
|
64
66
|
|
|
@@ -25,6 +25,9 @@ class FileDownloadManager:
|
|
|
25
25
|
|
|
26
26
|
async def start_workers(self) -> None:
|
|
27
27
|
"""Start background workers to process download queue"""
|
|
28
|
+
if self._workers_running:
|
|
29
|
+
logger.info("Download workers already running")
|
|
30
|
+
return
|
|
28
31
|
self._workers_running = True
|
|
29
32
|
self._worker_tasks = [
|
|
30
33
|
asyncio.create_task(self._download_worker(i))
|
|
@@ -12,6 +12,7 @@ import requests
|
|
|
12
12
|
|
|
13
13
|
from materials_commons.cli.old.functions import checksum
|
|
14
14
|
from materials_commons.cli.requests import DownloadRequest, DBWriteRequest
|
|
15
|
+
from materials_commons.cli.server import projects
|
|
15
16
|
|
|
16
17
|
logger = logging.getLogger(__name__)
|
|
17
18
|
|
|
@@ -32,7 +33,10 @@ class FileDownloader:
|
|
|
32
33
|
self.db_write_queue = db_write_queue
|
|
33
34
|
self.download_request = download_request
|
|
34
35
|
self.file_id = download_request.observation.remote_entry.remote_file_id
|
|
35
|
-
|
|
36
|
+
if download_request.observation.local_path is None:
|
|
37
|
+
self.file_path = projects.remote_to_local_project_path(download_request.project.local_path, download_request.observation.remote_path)
|
|
38
|
+
else:
|
|
39
|
+
self.file_path = download_request.observation.local_path
|
|
36
40
|
self.base_url = download_request.project.remote.base_url
|
|
37
41
|
self.apitoken = download_request.project.remote.apikey
|
|
38
42
|
self.project_id = download_request.project.id
|
|
@@ -133,13 +133,16 @@ def local_to_remote_project_path(proj_base: Path, full_path: Path) -> Path:
|
|
|
133
133
|
Returns:
|
|
134
134
|
Path: The remote path corresponding to the local path.
|
|
135
135
|
"""
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
136
|
+
try:
|
|
137
|
+
full_path_resolved = full_path.resolve()
|
|
138
|
+
# remote_path will be the relative path from proj_base to full_path, i.e., it
|
|
139
|
+
# won't start with a slash, so we need to add one to get the correct path.
|
|
140
|
+
if full_path_resolved.as_posix() == proj_base.as_posix():
|
|
141
|
+
return Path("/")
|
|
142
|
+
remote_path = full_path_resolved.relative_to(proj_base)
|
|
143
|
+
return Path("/" + remote_path.as_posix())
|
|
144
|
+
except ValueError:
|
|
145
|
+
return full_path
|
|
143
146
|
|
|
144
147
|
|
|
145
148
|
def remote_to_local_project_path(proj_base: Path, remote_path: Path) -> Path:
|
|
@@ -90,21 +90,26 @@ async def ls2_subcommand_async(args, working_dir):
|
|
|
90
90
|
async_reconciler = AsyncReconciler(db=db, proj=proj, reconcile_mode="status")
|
|
91
91
|
which_class = LSAction if args.action else LSEntry
|
|
92
92
|
listdir_fn = make_merged_listdir_func(proj)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
p = Path(path)
|
|
96
|
-
if p.is_dir():
|
|
97
|
-
async for current_path, path_entries in async_reconciler.walk(path=path, listdir_fn=listdir_fn,
|
|
98
|
-
recursive=False, ignore_fn=None):
|
|
99
|
-
for entry_name in sorted(path_entries):
|
|
100
|
-
entry = path_entries[entry_name]
|
|
101
|
-
if entry.exception:
|
|
102
|
-
print(f"Error reconciling file: {entry.exception}")
|
|
103
|
-
continue
|
|
104
|
-
lstable.add_row(which_class.from_file_state(entry))
|
|
105
|
-
lstable.print_table()
|
|
106
|
-
elif p.is_file():
|
|
107
|
-
state = await async_reconciler.reconcile_file(p)
|
|
93
|
+
try:
|
|
94
|
+
for path in args.paths:
|
|
108
95
|
lstable = LSTable()
|
|
109
|
-
|
|
110
|
-
|
|
96
|
+
p = Path(path)
|
|
97
|
+
if p.is_dir():
|
|
98
|
+
async for current_path, path_entries in async_reconciler.walk(path=path, listdir_fn=listdir_fn,
|
|
99
|
+
recursive=False, ignore_fn=None):
|
|
100
|
+
for entry_name in sorted(path_entries):
|
|
101
|
+
entry = path_entries[entry_name]
|
|
102
|
+
if entry.exception:
|
|
103
|
+
print(f"Error reconciling file: {entry.exception}")
|
|
104
|
+
continue
|
|
105
|
+
lstable.add_row(which_class.from_file_state(entry))
|
|
106
|
+
lstable.print_table()
|
|
107
|
+
elif p.is_file():
|
|
108
|
+
state = await async_reconciler.reconcile_file(p)
|
|
109
|
+
lstable = LSTable()
|
|
110
|
+
lstable.add_row(which_class.from_file_state(state))
|
|
111
|
+
lstable.print_table()
|
|
112
|
+
except Exception as e:
|
|
113
|
+
pass
|
|
114
|
+
finally:
|
|
115
|
+
await db.close()
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import functools
|
|
3
|
+
import inspect
|
|
4
|
+
|
|
5
|
+
from mcp.server.fastmcp import FastMCP
|
|
6
|
+
from materials_commons.cli.config import Config
|
|
7
|
+
from materials_commons.api.client import Client
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
mcp = FastMCP(
|
|
11
|
+
name="mcmcp",
|
|
12
|
+
instructions=""
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def make_parser():
|
|
17
|
+
parser = argparse.ArgumentParser(
|
|
18
|
+
description="MCP Server for Materials Commons",
|
|
19
|
+
usage="mc mcp",
|
|
20
|
+
)
|
|
21
|
+
parser.add_argument("--port", type=int, default=8000)
|
|
22
|
+
parser.add_argument("--mc-url", type=str, default="https://materialscommons.org/api")
|
|
23
|
+
return parser
|
|
24
|
+
|
|
25
|
+
def mcp_subcommand(argv, working_dir=None):
|
|
26
|
+
parser = make_parser()
|
|
27
|
+
args = parser.parse_args(argv)
|
|
28
|
+
config = Config.load()
|
|
29
|
+
client = config.default_remote.make_client()
|
|
30
|
+
register_mcapi_tools(client)
|
|
31
|
+
mcp.run()
|
|
32
|
+
return args
|
|
33
|
+
|
|
34
|
+
def register_mcapi_tools(client: Client):
|
|
35
|
+
for method_name, method in inspect.getmembers(client, predicate=callable):
|
|
36
|
+
if method_name.startswith("_"):
|
|
37
|
+
continue
|
|
38
|
+
if not is_get_or_list_method(method_name):
|
|
39
|
+
continue
|
|
40
|
+
|
|
41
|
+
doc = inspect.getdoc(method)
|
|
42
|
+
if doc is None:
|
|
43
|
+
continue
|
|
44
|
+
|
|
45
|
+
signature = inspect.signature(method)
|
|
46
|
+
|
|
47
|
+
@functools.wraps(method)
|
|
48
|
+
def tool_wrapper(*args, __method=method, **kwargs):
|
|
49
|
+
result = __method(*args, **kwargs)
|
|
50
|
+
return to_jsonable(result)
|
|
51
|
+
|
|
52
|
+
tool_wrapper.__name__ = method_name
|
|
53
|
+
tool_wrapper.__doc__ = doc
|
|
54
|
+
tool_wrapper.__signature__ = signature
|
|
55
|
+
|
|
56
|
+
mcp.tool(name=method_name)(tool_wrapper)
|
|
57
|
+
|
|
58
|
+
def is_get_or_list_method(method_name: str):
|
|
59
|
+
return method_name.startswith("get_") or method_name.startswith("list_")
|
|
60
|
+
|
|
61
|
+
def to_jsonable(value):
|
|
62
|
+
if value is None:
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
if isinstance(value, (str, int, float, bool)):
|
|
66
|
+
return value
|
|
67
|
+
|
|
68
|
+
if isinstance(value, list):
|
|
69
|
+
return [to_jsonable(item) for item in value]
|
|
70
|
+
|
|
71
|
+
if isinstance(value, tuple):
|
|
72
|
+
return [to_jsonable(item) for item in value]
|
|
73
|
+
|
|
74
|
+
if isinstance(value, dict):
|
|
75
|
+
return {
|
|
76
|
+
str(key): to_jsonable(item)
|
|
77
|
+
for key, item in value.items()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if hasattr(value, "to_dict"):
|
|
81
|
+
return to_jsonable(value.to_dict())
|
|
82
|
+
|
|
83
|
+
if hasattr(value, "__dict__"):
|
|
84
|
+
return to_jsonable(value.__dict__)
|
|
85
|
+
|
|
86
|
+
return str(value)
|
|
@@ -208,6 +208,7 @@ async def ws_upload(args, working_dir):
|
|
|
208
208
|
file_state = path_entries[entry_name]
|
|
209
209
|
if file_state.exception:
|
|
210
210
|
logger.error(f"Error encountered while processing {entry_name}: {file_state.exception}")
|
|
211
|
+
# print(f"Error encountered while processing {entry_name}: {file_state.exception}")
|
|
211
212
|
continue
|
|
212
213
|
if file_state.file_decision.action == "upload":
|
|
213
214
|
upload_request = UploadRequest(observation=file_state.observation,
|
|
@@ -219,7 +220,7 @@ async def ws_upload(args, working_dir):
|
|
|
219
220
|
file_state = await async_reconciler.reconcile_file(p)
|
|
220
221
|
if file_state.exception:
|
|
221
222
|
logger.error(f"Error encountered while processing {p}: {file_state.exception}")
|
|
222
|
-
print(f"Error encountered while processing {p}: {file_state.exception}")
|
|
223
|
+
# print(f"Error encountered while processing {p}: {file_state.exception}")
|
|
223
224
|
continue
|
|
224
225
|
if file_state.file_decision.action == "upload":
|
|
225
226
|
upload_request = UploadRequest(observation=file_state.observation,
|
{materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/walk.py
RENAMED
|
@@ -118,20 +118,23 @@ async def local_listdir(path: str | Path) -> list[Observation]:
|
|
|
118
118
|
def _scan() -> list[Observation]:
|
|
119
119
|
items: list[Observation] = []
|
|
120
120
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
121
|
+
try:
|
|
122
|
+
for entry in root.iterdir():
|
|
123
|
+
local = path_to_local_file_entry(entry)
|
|
124
|
+
|
|
125
|
+
items.append(
|
|
126
|
+
Observation(
|
|
127
|
+
local_path=entry,
|
|
128
|
+
remote_path=None,
|
|
129
|
+
local_entry=local,
|
|
130
|
+
file_record=None,
|
|
131
|
+
remote_entry=None,
|
|
132
|
+
)
|
|
131
133
|
)
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
134
|
+
except:
|
|
135
|
+
pass
|
|
136
|
+
finally:
|
|
137
|
+
return items
|
|
135
138
|
|
|
136
139
|
return await asyncio.to_thread(_scan)
|
|
137
140
|
|
|
@@ -202,6 +205,7 @@ async def merged_local_remote_listdir(
|
|
|
202
205
|
Merge local and remote directory listings, prioritizing local entries.
|
|
203
206
|
"""
|
|
204
207
|
local_entries = await local_listdir_fn(path)
|
|
208
|
+
|
|
205
209
|
project_path = projects.local_to_remote_project_path(Path(proj.local_path), path)
|
|
206
210
|
remote_entries = await remote_listdir_fn(project_path)
|
|
207
211
|
|
|
@@ -68,6 +68,7 @@ materials_commons/cli/subcommands/globus.py
|
|
|
68
68
|
materials_commons/cli/subcommands/init.py
|
|
69
69
|
materials_commons/cli/subcommands/ls.py
|
|
70
70
|
materials_commons/cli/subcommands/ls2.py
|
|
71
|
+
materials_commons/cli/subcommands/mcp.py
|
|
71
72
|
materials_commons/cli/subcommands/mkdir.py
|
|
72
73
|
materials_commons/cli/subcommands/mv.py
|
|
73
74
|
materials_commons/cli/subcommands/proj.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '2.3.0b6'
|
|
File without changes
|
|
File without changes
|
{materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/LICENSE.txt
RENAMED
|
File without changes
|
{materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/config.py
RENAMED
|
File without changes
|
{materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/filedb.py
RENAMED
|
File without changes
|
|
File without changes
|
{materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/models.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/old/globus.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/old/treedb.py
RENAMED
|
File without changes
|
|
File without changes
|
{materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/reconcile2.py
RENAMED
|
File without changes
|
{materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/reconcile3.py
RENAMED
|
File without changes
|
{materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/requests.py
RENAMED
|
File without changes
|
{materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/materials_commons/cli/run.py
RENAMED
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/test/test_treecompare_no_cache.py
RENAMED
|
File without changes
|
{materials_commons_cli-2.3.0b6 → materials_commons_cli-2.3.0b8}/test/test_treecompare_with_cache.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|