materials-commons-cli 2.2.1__tar.gz → 2.2.3__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.2.1 → materials_commons_cli-2.2.3}/PKG-INFO +1 -1
- materials_commons_cli-2.2.3/materials_commons/cli/__init__.py +1 -0
- materials_commons_cli-2.2.3/materials_commons/cli/filedb.py +110 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/functions.py +13 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/parser.py +10 -4
- {materials_commons_cli-2.2.1/materials_commons/cli/desktop → materials_commons_cli-2.2.3/materials_commons/cli/server}/command_handlers.py +5 -5
- {materials_commons_cli-2.2.1/materials_commons/cli/desktop → materials_commons_cli-2.2.3/materials_commons/cli/server}/downloader/file_download_manager.py +1 -3
- {materials_commons_cli-2.2.1/materials_commons/cli/desktop → materials_commons_cli-2.2.3/materials_commons/cli/server}/downloader/file_downloader.py +2 -2
- materials_commons_cli-2.2.3/materials_commons/cli/server/indexer/file_index_manager.py +84 -0
- {materials_commons_cli-2.2.1/materials_commons/cli/desktop → materials_commons_cli-2.2.3/materials_commons/cli/server}/local_rest_server.py +3 -3
- {materials_commons_cli-2.2.1/materials_commons/cli/desktop → materials_commons_cli-2.2.3/materials_commons/cli/server}/uploader/api.py +2 -2
- {materials_commons_cli-2.2.1/materials_commons/cli/desktop → materials_commons_cli-2.2.3/materials_commons/cli/server}/uploader/file_transfer_manager.py +1 -1
- {materials_commons_cli-2.2.1/materials_commons/cli/desktop → materials_commons_cli-2.2.3/materials_commons/cli/server}/uploader/file_uploader.py +17 -4
- {materials_commons_cli-2.2.1/materials_commons/cli/desktop → materials_commons_cli-2.2.3/materials_commons/cli/server}/websocket_server.py +4 -4
- materials_commons_cli-2.2.3/materials_commons/cli/subcommands/__init__.py +0 -0
- materials_commons_cli-2.2.3/materials_commons/cli/subcommands/scan.py +83 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/subcommands/server.py +3 -3
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/subcommands/up.py +16 -3
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons_cli.egg-info/PKG-INFO +1 -1
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons_cli.egg-info/SOURCES.txt +16 -12
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons_cli.egg-info/requires.txt +2 -1
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/setup.py +2 -1
- materials_commons_cli-2.2.1/materials_commons/cli/__init__.py +0 -1
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/LICENSE +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/README.md +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/LICENSE.txt +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/cloned_project.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/exceptions.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/file_functions.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/globus.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/list_objects.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/print_formatter.py +0 -0
- {materials_commons_cli-2.2.1/materials_commons/cli/desktop → materials_commons_cli-2.2.3/materials_commons/cli/server}/__init__.py +0 -0
- {materials_commons_cli-2.2.1/materials_commons/cli/desktop → materials_commons_cli-2.2.3/materials_commons/cli/server}/downloader/__init__.py +0 -0
- {materials_commons_cli-2.2.1/materials_commons/cli/desktop/uploader → materials_commons_cli-2.2.3/materials_commons/cli/server/indexer}/__init__.py +0 -0
- {materials_commons_cli-2.2.1/materials_commons/cli/desktop → materials_commons_cli-2.2.3/materials_commons/cli/server}/projects.py +0 -0
- {materials_commons_cli-2.2.1/materials_commons/cli/subcommands → materials_commons_cli-2.2.3/materials_commons/cli/server/uploader}/__init__.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/sqltable.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/subcommands/clone.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/subcommands/config.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/subcommands/dataset.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/subcommands/down.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/subcommands/expt.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/subcommands/fetch.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/subcommands/globus.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/subcommands/init.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/subcommands/ls.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/subcommands/mkdir.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/subcommands/mv.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/subcommands/proj.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/subcommands/remote.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/subcommands/rm.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/subcommands/versions.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/tmp_functions.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/tree_functions.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/treedb.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/user_config.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons_cli.egg-info/dependency_links.txt +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons_cli.egg-info/entry_points.txt +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons_cli.egg-info/not-zip-safe +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons_cli.egg-info/top_level.txt +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/setup.cfg +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/test/test_api.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/test/test_functions.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/test/test_mc_dataset.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/test/test_mc_down.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/test/test_mc_mkdir.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/test/test_mc_mv.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/test/test_mc_proj.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/test/test_mc_rm.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/test/test_mc_up.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/test/test_treecompare_no_cache.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/test/test_treecompare_with_cache.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/test/test_treedb.py +0 -0
- {materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/test/test_user_config.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '2.2.3'
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import os
|
|
3
|
+
import sqlite3
|
|
4
|
+
import time
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Iterable, Optional, Tuple
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class FileRecord:
|
|
11
|
+
path: str
|
|
12
|
+
size: int
|
|
13
|
+
mtime_ns: int
|
|
14
|
+
ctime_ns: int
|
|
15
|
+
last_seen_ts: int
|
|
16
|
+
checksum: Optional[str] = None
|
|
17
|
+
status: Optional[str] = None
|
|
18
|
+
remote_file_id: Optional[int] = None
|
|
19
|
+
transfer_id: Optional[str] = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class FileIndexDB:
|
|
23
|
+
def __init__(self, db_path: Path):
|
|
24
|
+
self.db_path = db_path
|
|
25
|
+
self._init_db()
|
|
26
|
+
|
|
27
|
+
def _init_db(self):
|
|
28
|
+
with self._connect() as conn:
|
|
29
|
+
conn.execute(
|
|
30
|
+
"""
|
|
31
|
+
CREATE TABLE IF NOT EXISTS files (
|
|
32
|
+
path TEXT PRIMARY KEY,
|
|
33
|
+
size INTEGER NOT NULL,
|
|
34
|
+
mtime_ns INTEGER NOT NULL,
|
|
35
|
+
ctime_ns INTEGER NOT NULL,
|
|
36
|
+
last_seen_ts INTEGER NOT NULL,
|
|
37
|
+
checksum TEXT,
|
|
38
|
+
status TEXT,
|
|
39
|
+
remote_file_id INTEGER,
|
|
40
|
+
transfer_id TEXT
|
|
41
|
+
) STRICT;
|
|
42
|
+
"""
|
|
43
|
+
)
|
|
44
|
+
conn.execute("CREATE INDEX IF NOT EXISTS idx_files_status ON files(status);")
|
|
45
|
+
conn.execute("CREATE INDEX IF NOT EXISTS idx_files_last_seen ON files(last_seen_ts);")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _connect(self):
|
|
49
|
+
conn = sqlite3.connect(self.db_path)
|
|
50
|
+
conn.row_factory = sqlite3.Row
|
|
51
|
+
conn.execute("PRAGMA journal_mode=WAL;")
|
|
52
|
+
conn.execute("PRAGMA busy_timeout=5000;")
|
|
53
|
+
conn.execute("PRAGMA synchronous=NORMAL;")
|
|
54
|
+
conn.execute("PRAGMA cache_size=100000;")
|
|
55
|
+
conn.execute("PRAGMA temp_store=MEMORY;")
|
|
56
|
+
conn.execute("PRAGMA locking_mode=EXCLUSIVE;")
|
|
57
|
+
conn.execute("PRAGMA foreign_keys=ON;")
|
|
58
|
+
return conn
|
|
59
|
+
|
|
60
|
+
def upsert(self, record: FileRecord):
|
|
61
|
+
with self._connect() as conn:
|
|
62
|
+
conn.execute(
|
|
63
|
+
"""
|
|
64
|
+
INSERT INTO files(path, size, mtime_ns, ctime_ns, last_seen_ts, checksum, status, remote_file_id, transfer_id)
|
|
65
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
66
|
+
ON CONFLICT(path) DO UPDATE SET
|
|
67
|
+
size = excluded.size,
|
|
68
|
+
mtime_ns = excluded.mtime_ns,
|
|
69
|
+
ctime_ns = excluded.ctime_ns,
|
|
70
|
+
last_seen_ts = excluded.last_seen_ts,
|
|
71
|
+
checksum = COALESCE(excluded.checksum, files.checksum),
|
|
72
|
+
status = COALESCE(excluded.status, files.status),
|
|
73
|
+
remote_file_id = COALESCE(excluded.remote_file_id, files.remote_file_id),
|
|
74
|
+
transfer_id = COALESCE(excluded.transfer_id, files.transfer_id)
|
|
75
|
+
""",
|
|
76
|
+
(
|
|
77
|
+
record.path,
|
|
78
|
+
record.size,
|
|
79
|
+
record.mtime_ns,
|
|
80
|
+
record.ctime_ns,
|
|
81
|
+
record.last_seen_ts,
|
|
82
|
+
record.checksum,
|
|
83
|
+
record.status,
|
|
84
|
+
record.remote_file_id,
|
|
85
|
+
record.transfer_id,
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def get_file_by_path(self, path: str) -> Optional[FileRecord]:
|
|
90
|
+
with self._connect() as conn:
|
|
91
|
+
cursor = conn.execute("SELECT * FROM files WHERE path=?", (path,))
|
|
92
|
+
row = cursor.fetchone()
|
|
93
|
+
if row is None:
|
|
94
|
+
return None
|
|
95
|
+
return FileRecord(**row)
|
|
96
|
+
|
|
97
|
+
def walk(root: Path) -> Iterable[Path]:
|
|
98
|
+
for dirpath, dirnames, filenames in os.walk(root, followlinks=False):
|
|
99
|
+
base = Path(dirpath)
|
|
100
|
+
for name in filenames:
|
|
101
|
+
yield base / name
|
|
102
|
+
|
|
103
|
+
def build_file_db_from_scan(db: FileIndexDB, root: Path, compute_checksums: bool = True):
|
|
104
|
+
scan_started = int(time.time())
|
|
105
|
+
count = 0
|
|
106
|
+
for p in walk(root):
|
|
107
|
+
if not p.is_file():
|
|
108
|
+
continue
|
|
109
|
+
|
|
110
|
+
|
{materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/functions.py
RENAMED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import datetime
|
|
2
|
+
|
|
3
|
+
import aiofiles
|
|
2
4
|
import dateutil
|
|
3
5
|
import hashlib
|
|
4
6
|
import json
|
|
@@ -153,6 +155,17 @@ def checksum(path, chunk_size=8192):
|
|
|
153
155
|
md5.update(chunk)
|
|
154
156
|
return md5.hexdigest()
|
|
155
157
|
|
|
158
|
+
async def checksum_async(path, chunk_size=8192):
|
|
159
|
+
"""Generate MD5 checksum for the file at "path"""
|
|
160
|
+
md5 = hashlib.md5()
|
|
161
|
+
async with aiofiles.open(path, 'rb') as f:
|
|
162
|
+
while True:
|
|
163
|
+
chunk = await f.read(chunk_size)
|
|
164
|
+
if not chunk:
|
|
165
|
+
break
|
|
166
|
+
md5.update(chunk)
|
|
167
|
+
return md5.hexdigest()
|
|
168
|
+
|
|
156
169
|
def random_name(n=3, max_letters=6, sep='-'):
|
|
157
170
|
"""Generates a random name for "n" words of max "max_letters" length, joined by "sep" """
|
|
158
171
|
import random
|
|
@@ -6,7 +6,7 @@ from datetime import datetime, timedelta
|
|
|
6
6
|
from io import StringIO
|
|
7
7
|
|
|
8
8
|
import materials_commons.api as mcapi
|
|
9
|
-
import
|
|
9
|
+
from importlib.metadata import PackageNotFoundError, version as pkg_version
|
|
10
10
|
import requests
|
|
11
11
|
|
|
12
12
|
import materials_commons.cli.functions as clifuncs
|
|
@@ -30,6 +30,7 @@ from materials_commons.cli.subcommands.remote import remote_subcommand
|
|
|
30
30
|
from materials_commons.cli.subcommands.rm import rm_subcommand
|
|
31
31
|
from materials_commons.cli.subcommands.up import up_subcommand
|
|
32
32
|
from materials_commons.cli.subcommands.versions import versions_subcommand
|
|
33
|
+
from materials_commons.cli.subcommands.scan import scan_subcommand
|
|
33
34
|
from materials_commons.cli.user_config import Config
|
|
34
35
|
from materials_commons.cli.subcommands.server import server_subcommand
|
|
35
36
|
|
|
@@ -47,6 +48,7 @@ standard_usage = [
|
|
|
47
48
|
{'name': 'fetch', 'desc': 'Remote data fetching and configuration', 'subcommand': fetch_subcommand},
|
|
48
49
|
{'name': 'up', 'desc': 'Upload files', 'subcommand': up_subcommand},
|
|
49
50
|
{'name': 'down', 'desc': 'Download files', 'subcommand': down_subcommand},
|
|
51
|
+
# {'name': 'scan', 'desc': 'Scan local directory for files to upload', 'subcommand': scan_subcommand},
|
|
50
52
|
{'name': 'globus', 'desc': 'Manage Globus uploads and downloads', 'subcommand': globus_subcommand},
|
|
51
53
|
{'name': 'versions', 'desc': 'List file versions', 'subcommand': versions_subcommand},
|
|
52
54
|
# {'name': 'proc', 'desc': 'List processes', 'subcommand': ProcSubcommand()},
|
|
@@ -74,8 +76,12 @@ def make_parser():
|
|
|
74
76
|
description='Materials Commons command line interface',
|
|
75
77
|
usage=usage_help.getvalue())
|
|
76
78
|
parser.add_argument('command', help='Subcommand to run')
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
package_name = 'materials-commons-cli'
|
|
80
|
+
try:
|
|
81
|
+
installed_version = pkg_version(package_name)
|
|
82
|
+
except PackageNotFoundError:
|
|
83
|
+
installed_version = "unknown"
|
|
84
|
+
parser.add_argument('--version', '-v', action='version', version=installed_version)
|
|
79
85
|
|
|
80
86
|
return parser
|
|
81
87
|
|
|
@@ -113,7 +119,7 @@ def check_package_version():
|
|
|
113
119
|
|
|
114
120
|
package_name = 'materials-commons-cli'
|
|
115
121
|
try:
|
|
116
|
-
current_version =
|
|
122
|
+
current_version = pkg_version(package_name)
|
|
117
123
|
response = requests.get(f"https://pypi.org/pypi/{package_name}/json")
|
|
118
124
|
latest_version = response.json()["info"]["version"]
|
|
119
125
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
from typing import Dict, Any, Callable, Awaitable
|
|
3
|
-
from materials_commons.cli import
|
|
3
|
+
from materials_commons.cli import server
|
|
4
4
|
import os
|
|
5
5
|
import signal
|
|
6
6
|
import logging
|
|
@@ -74,7 +74,7 @@ async def handle_list_directory(queue: asyncio.Queue, cmd: Dict[str, Any]) -> No
|
|
|
74
74
|
async def handle_list_projects(queue: asyncio.Queue, cmd: Dict[str, Any]) -> None:
|
|
75
75
|
print(f"[handler] list_projects -> {cmd}")
|
|
76
76
|
payload = cmd.get("payload") or {}
|
|
77
|
-
projects =
|
|
77
|
+
projects = server.list_local_projects()
|
|
78
78
|
response_payload = {
|
|
79
79
|
"projects": projects,
|
|
80
80
|
"request_id": payload.get("request_id"),
|
|
@@ -121,7 +121,7 @@ async def handle_upload_file(queue: asyncio.Queue, cmd: Dict[str, Any]) -> None:
|
|
|
121
121
|
# To do this, we get the local path for the project, then join project_path and the local project's path
|
|
122
122
|
# to get the full local path. Note that project_path may start with a /, so we strip it off before joining.
|
|
123
123
|
if not file_path:
|
|
124
|
-
p =
|
|
124
|
+
p = server.get_local_project_by_id(project_id)
|
|
125
125
|
if not p:
|
|
126
126
|
logger.error(f"Project {project_id} not found in local projects")
|
|
127
127
|
return
|
|
@@ -178,7 +178,7 @@ async def handle_upload_directory(queue: asyncio.Queue, cmd: Dict[str, Any]):
|
|
|
178
178
|
logger.error("Missing required field mc_project_path in UPLOAD_DIRECTORY command")
|
|
179
179
|
return
|
|
180
180
|
|
|
181
|
-
p =
|
|
181
|
+
p = server.get_local_project_by_id(project_id)
|
|
182
182
|
if not p:
|
|
183
183
|
logger.error(f"Project {project_id} not found in local projects")
|
|
184
184
|
return
|
|
@@ -341,7 +341,7 @@ async def handle_download_file(queue: asyncio.Queue, cmd: Dict[str, Any]) -> Non
|
|
|
341
341
|
if project_path:
|
|
342
342
|
# If we have a project path, then we need to translate it to a
|
|
343
343
|
# local file path
|
|
344
|
-
p =
|
|
344
|
+
p = server.get_local_project_by_id(project_id)
|
|
345
345
|
if not p:
|
|
346
346
|
logger.error(f"Project {project_id} not found in local projects")
|
|
347
347
|
return
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import os
|
|
3
|
-
from pathlib import Path
|
|
4
2
|
from typing import Dict, Any, Optional, Callable
|
|
5
3
|
|
|
6
|
-
from materials_commons.cli.
|
|
4
|
+
from materials_commons.cli.server.downloader.file_downloader import FileDownloader, logger
|
|
7
5
|
|
|
8
6
|
|
|
9
7
|
class FileDownloadManager:
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# materials_commons/cli/desktop/downloader/file_downloader.py
|
|
2
1
|
import asyncio
|
|
3
2
|
import hashlib
|
|
4
3
|
import json
|
|
@@ -6,7 +5,8 @@ import logging
|
|
|
6
5
|
import uuid
|
|
7
6
|
from datetime import datetime, timezone
|
|
8
7
|
from pathlib import Path
|
|
9
|
-
from typing import Optional, Callable
|
|
8
|
+
from typing import Optional, Callable
|
|
9
|
+
|
|
10
10
|
import requests
|
|
11
11
|
|
|
12
12
|
logger = logging.getLogger(__name__)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from materials_commons.cli.filedb import FileIndexDB, FileRecord
|
|
6
|
+
from materials_commons.cli.functions import checksum
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FileIndexManager:
|
|
10
|
+
"""Manages multiple concurrent file indexers"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, db: FileIndexDB, queue: asyncio.Queue, max_concurrent: int = 3):
|
|
13
|
+
self.db = db
|
|
14
|
+
self._index_queue = queue
|
|
15
|
+
self._workers_running = False
|
|
16
|
+
self._max_concurrent = max_concurrent
|
|
17
|
+
self.db_write_queue = asyncio.Queue()
|
|
18
|
+
|
|
19
|
+
async def start_workers(self):
|
|
20
|
+
"""Start background workers to process the index queue"""
|
|
21
|
+
self._workers_running = True
|
|
22
|
+
workers = [
|
|
23
|
+
asyncio.create_task(self._index_file_worker())
|
|
24
|
+
for i in range(self._max_concurrent)
|
|
25
|
+
]
|
|
26
|
+
db_queue_worker = asyncio.create_task(self._db_queue_worker())
|
|
27
|
+
workers.append(db_queue_worker)
|
|
28
|
+
return workers
|
|
29
|
+
|
|
30
|
+
async def _index_file_worker(self):
|
|
31
|
+
"""Worker that processes index requests from queue"""
|
|
32
|
+
print("Index worker started")
|
|
33
|
+
while self._workers_running:
|
|
34
|
+
try:
|
|
35
|
+
(file_path, project_path) = await asyncio.wait_for(self._index_queue.get(), timeout=1.0)
|
|
36
|
+
except asyncio.TimeoutError:
|
|
37
|
+
continue
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
await self._index(file_path, project_path)
|
|
41
|
+
except Exception as e:
|
|
42
|
+
print(f"Error indexing {file_path}: {e}")
|
|
43
|
+
finally:
|
|
44
|
+
self._index_queue.task_done()
|
|
45
|
+
|
|
46
|
+
async def _index(self, file_path: str, project_path: str):
|
|
47
|
+
"""Index a file"""
|
|
48
|
+
print(f"Indexing {file_path} in {project_path}")
|
|
49
|
+
hash = await asyncio.to_thread(checksum, file_path)
|
|
50
|
+
finfo = os.stat(file_path)
|
|
51
|
+
file_record = FileRecord(
|
|
52
|
+
path = file_path,
|
|
53
|
+
size = finfo.st_size,
|
|
54
|
+
mtime_ns = finfo.st_mtime_ns,
|
|
55
|
+
ctime_ns = finfo.st_ctime_ns,
|
|
56
|
+
last_seen_ts = int(datetime.now().timestamp()),
|
|
57
|
+
checksum = hash,
|
|
58
|
+
status = "indexed",
|
|
59
|
+
)
|
|
60
|
+
await self.db_write_queue.put(file_record)
|
|
61
|
+
print(f"Done indexing {file_path} in {project_path} got Hash: {hash}")
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
async def _db_queue_worker(self):
|
|
65
|
+
"""Worker that processes db writes from queue"""
|
|
66
|
+
while self._workers_running:
|
|
67
|
+
try:
|
|
68
|
+
db_entry = await asyncio.wait_for(self.db_write_queue.get(), timeout=1.0)
|
|
69
|
+
print(f"Writing to db: {db_entry}")
|
|
70
|
+
except asyncio.TimeoutError:
|
|
71
|
+
continue
|
|
72
|
+
except asyncio.CancelledError:
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
self.db.upsert(db_entry)
|
|
77
|
+
except Exception as e:
|
|
78
|
+
pass
|
|
79
|
+
finally:
|
|
80
|
+
self.db_write_queue.task_done()
|
|
81
|
+
|
|
82
|
+
async def stop_workers(self):
|
|
83
|
+
"""Stop background workers"""
|
|
84
|
+
self._workers_running = False
|
|
@@ -5,7 +5,7 @@ import asyncio
|
|
|
5
5
|
import threading
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
|
-
from materials_commons.cli import
|
|
8
|
+
from materials_commons.cli import server
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class LocalRestRequestHandler(BaseHTTPRequestHandler):
|
|
@@ -27,7 +27,7 @@ class LocalRestRequestHandler(BaseHTTPRequestHandler):
|
|
|
27
27
|
|
|
28
28
|
def _refresh_projects(self):
|
|
29
29
|
try:
|
|
30
|
-
projects =
|
|
30
|
+
projects = server.list_local_projects()
|
|
31
31
|
project_ids = [str(p["project_id"]) for p in projects]
|
|
32
32
|
payload = {"type": "refresh-projects", "project_ids": project_ids}
|
|
33
33
|
|
|
@@ -54,7 +54,7 @@ class LocalRestRequestHandler(BaseHTTPRequestHandler):
|
|
|
54
54
|
|
|
55
55
|
class LocalRestServer:
|
|
56
56
|
"""
|
|
57
|
-
Local REST server for handling requests from the
|
|
57
|
+
Local REST server for handling requests from the server client. Writes to the server.json state file so
|
|
58
58
|
the CLI can find the port.
|
|
59
59
|
"""
|
|
60
60
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import logging
|
|
3
3
|
from typing import List, Optional
|
|
4
|
-
from materials_commons.cli.
|
|
5
|
-
from materials_commons.cli.
|
|
4
|
+
from materials_commons.cli.server.websocket_server import WebSocketCommandListener
|
|
5
|
+
from materials_commons.cli.server.command_handlers import register_handlers
|
|
6
6
|
from materials_commons.cli.user_config import Config
|
|
7
7
|
import ssl
|
|
8
8
|
import websockets
|
|
@@ -5,7 +5,7 @@ from datetime import datetime, timezone
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from typing import Dict, Any, Optional, Callable
|
|
7
7
|
|
|
8
|
-
from materials_commons.cli.
|
|
8
|
+
from materials_commons.cli.server.uploader.file_uploader import FileUploader, logger
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class FileTransferManager:
|
|
@@ -6,6 +6,8 @@ from datetime import datetime, timezone
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import Optional, Callable, Dict, Any
|
|
8
8
|
|
|
9
|
+
import aiofiles
|
|
10
|
+
|
|
9
11
|
logger = logging.getLogger(__name__)
|
|
10
12
|
|
|
11
13
|
|
|
@@ -43,6 +45,7 @@ class FileUploader:
|
|
|
43
45
|
self.in_flight_chunks = 0
|
|
44
46
|
self.last_acked_chunk = -1
|
|
45
47
|
self._state_lock = asyncio.Lock()
|
|
48
|
+
self._already_uploaded = False
|
|
46
49
|
|
|
47
50
|
# Response handling
|
|
48
51
|
self.response_queue: asyncio.Queue = asyncio.Queue()
|
|
@@ -59,6 +62,10 @@ class FileUploader:
|
|
|
59
62
|
|
|
60
63
|
# Step 2: Wait for acceptance
|
|
61
64
|
if not await self._wait_for_acceptance():
|
|
65
|
+
if self._already_uploaded:
|
|
66
|
+
logger.info(f"File {self.file_path} already uploaded")
|
|
67
|
+
print(f"File {self.file_path} already uploaded...")
|
|
68
|
+
return True
|
|
62
69
|
return False
|
|
63
70
|
|
|
64
71
|
# # Step 3: Send file chunks
|
|
@@ -89,10 +96,12 @@ class FileUploader:
|
|
|
89
96
|
return False
|
|
90
97
|
|
|
91
98
|
logger.info(f"File upload complete for {self.file_path}")
|
|
99
|
+
print(f"Uploaded {self.file_path}...")
|
|
92
100
|
return True
|
|
93
101
|
|
|
94
102
|
except Exception as e:
|
|
95
103
|
logger.error(f"Error uploading file {self.file_path}: {e}")
|
|
104
|
+
print(f"Error uploading file {self.file_path}: {e}")
|
|
96
105
|
return False
|
|
97
106
|
|
|
98
107
|
async def resume(self, resume_from_byte: int, resume_from_chunk: int) -> bool:
|
|
@@ -161,7 +170,11 @@ class FileUploader:
|
|
|
161
170
|
|
|
162
171
|
elif msg["command"] == "TRANSFER_REJECT":
|
|
163
172
|
reason = msg["payload"].get("reason", "unknown")
|
|
164
|
-
|
|
173
|
+
if reason == "file already uploaded":
|
|
174
|
+
# TODO: This is a hack to prevent uploading the same file twice. We need a better solution.
|
|
175
|
+
self._already_uploaded = True
|
|
176
|
+
else:
|
|
177
|
+
logger.error(f"Transfer rejected: {reason}")
|
|
165
178
|
return False
|
|
166
179
|
|
|
167
180
|
logger.error(f"Unexpected response: {msg['command']}")
|
|
@@ -189,10 +202,10 @@ class FileUploader:
|
|
|
189
202
|
"""Send file chunks using sliding window (don't wait for each ACK)"""
|
|
190
203
|
total_chunks = (self.file_size + self.chunk_size - 1) // self.chunk_size
|
|
191
204
|
|
|
192
|
-
with open(self.file_path, 'rb') as f:
|
|
205
|
+
async with aiofiles.open(self.file_path, 'rb') as f:
|
|
193
206
|
# Single seek to start position (for resume)
|
|
194
207
|
if start_chunk > 0:
|
|
195
|
-
f.seek(start_chunk * self.chunk_size)
|
|
208
|
+
await f.seek(start_chunk * self.chunk_size)
|
|
196
209
|
|
|
197
210
|
async with self._state_lock:
|
|
198
211
|
self.next_chunk_to_send = start_chunk
|
|
@@ -227,7 +240,7 @@ class FileUploader:
|
|
|
227
240
|
continue
|
|
228
241
|
|
|
229
242
|
# Read the next chunk (outside lock - file I/O can be slow)
|
|
230
|
-
chunk = f.read(self.chunk_size)
|
|
243
|
+
chunk = await f.read(self.chunk_size)
|
|
231
244
|
if not chunk:
|
|
232
245
|
break
|
|
233
246
|
|
|
@@ -9,9 +9,9 @@ from typing import Dict, Any, Optional, Callable, Awaitable
|
|
|
9
9
|
|
|
10
10
|
import websockets
|
|
11
11
|
from websockets.exceptions import ConnectionClosedError, ConnectionClosedOK, InvalidStatus
|
|
12
|
-
from materials_commons.cli import
|
|
13
|
-
from materials_commons.cli.
|
|
14
|
-
from materials_commons.cli.
|
|
12
|
+
from materials_commons.cli import server
|
|
13
|
+
from materials_commons.cli.server.uploader.file_transfer_manager import FileTransferManager
|
|
14
|
+
from materials_commons.cli.server.downloader.file_download_manager import FileDownloadManager
|
|
15
15
|
from materials_commons.cli.user_config import Config
|
|
16
16
|
|
|
17
17
|
# Type alias for handler functions
|
|
@@ -233,7 +233,7 @@ class WebSocketCommandListener:
|
|
|
233
233
|
headers["MC-Connection-Type"] = "cli"
|
|
234
234
|
|
|
235
235
|
try:
|
|
236
|
-
projects =
|
|
236
|
+
projects = server.list_local_projects()
|
|
237
237
|
headers["MC-Client-Projects"] = ",".join([str(p["project_id"]) for p in projects])
|
|
238
238
|
except Exception:
|
|
239
239
|
# Fallback if listing projects fails
|
|
File without changes
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import asyncio
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
import materials_commons.cli.functions as clifuncs
|
|
6
|
+
import materials_commons.cli.tree_functions as treefuncs
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import igittigitt
|
|
9
|
+
from materials_commons.cli.filedb import FileIndexDB
|
|
10
|
+
from materials_commons.cli.server.indexer.file_index_manager import FileIndexManager
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def make_parser():
|
|
14
|
+
mc_scan_usage = 'mc scan'
|
|
15
|
+
parser = argparse.ArgumentParser(
|
|
16
|
+
description='Scan a project and build a database of current files and directories. This speeds uploads by storing state on the file.',
|
|
17
|
+
usage=mc_scan_usage,
|
|
18
|
+
prog='mc scan')
|
|
19
|
+
|
|
20
|
+
return parser
|
|
21
|
+
|
|
22
|
+
async def scan_files_async(local_path, mc_path, file_index_queue, ignore_parser):
|
|
23
|
+
"""Scan files and directories in a project asynchronously"""
|
|
24
|
+
|
|
25
|
+
loop = asyncio.get_event_loop()
|
|
26
|
+
|
|
27
|
+
def walk_and_enqueue():
|
|
28
|
+
|
|
29
|
+
for root, dirs, files in os.walk(local_path):
|
|
30
|
+
current_dir = os.path.basename(root)
|
|
31
|
+
if current_dir == ".mc":
|
|
32
|
+
continue
|
|
33
|
+
for filename in files:
|
|
34
|
+
if filename == ".DS_Store":
|
|
35
|
+
continue
|
|
36
|
+
if ignore_parser.match(filename):
|
|
37
|
+
continue
|
|
38
|
+
file_path = os.path.join(root, filename)
|
|
39
|
+
# Calculate relative path and construct mc_path
|
|
40
|
+
relative = Path(file_path).relative_to(local_path)
|
|
41
|
+
file_mc_path = str(Path(mc_path) / relative)
|
|
42
|
+
# print(f"Indexing {file_mc_path}, {file_path}")
|
|
43
|
+
asyncio.run_coroutine_threadsafe(file_index_queue.put((file_path, file_mc_path)), loop).result()
|
|
44
|
+
|
|
45
|
+
await asyncio.to_thread(walk_and_enqueue)
|
|
46
|
+
|
|
47
|
+
async def scan_subcommand_async(argv, working_dir):
|
|
48
|
+
"""Builds and populates the file index database for project scan; skips ignored directories and files"""
|
|
49
|
+
parser = make_parser()
|
|
50
|
+
parser.parse_args(argv)
|
|
51
|
+
|
|
52
|
+
proj = clifuncs.make_local_project(working_dir)
|
|
53
|
+
|
|
54
|
+
filedb = FileIndexDB(db_path=Path(proj.local_path) / ".mc" / "mc2.sqlite")
|
|
55
|
+
|
|
56
|
+
ignore_parser = igittigitt.IgnoreParser()
|
|
57
|
+
ignore_parser.parse_rule_files(base_dir=proj.local_path, filename=".mcignore",
|
|
58
|
+
add_default_patterns=False)
|
|
59
|
+
|
|
60
|
+
file_index_queue = asyncio.Queue()
|
|
61
|
+
file_index_manager = FileIndexManager(filedb, file_index_queue)
|
|
62
|
+
|
|
63
|
+
workers = await file_index_manager.start_workers()
|
|
64
|
+
|
|
65
|
+
local_abspaths = treefuncs.clipaths_to_local_abspaths(proj.local_path, [proj.local_path], proj.local_path)
|
|
66
|
+
local_abspaths = treefuncs.filter_local_abspaths(proj.local_path, local_abspaths, working_dir)
|
|
67
|
+
mcpaths = treefuncs.clipaths_to_mcpaths(proj.local_path, local_abspaths, working_dir)
|
|
68
|
+
for local_path, mc_path in zip(local_abspaths, mcpaths):
|
|
69
|
+
await scan_files_async(local_path, mc_path, file_index_queue, ignore_parser)
|
|
70
|
+
|
|
71
|
+
# Wait for all tasks to complete
|
|
72
|
+
await file_index_queue.join()
|
|
73
|
+
await file_index_manager.stop_workers()
|
|
74
|
+
print("called stop_workers")
|
|
75
|
+
for worker in workers:
|
|
76
|
+
try:
|
|
77
|
+
await worker
|
|
78
|
+
except asyncio.CancelledError:
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
def scan_subcommand(argv, working_dir):
|
|
82
|
+
"""Runs the scan subcommand asynchronously"""
|
|
83
|
+
asyncio.run(scan_subcommand_async(argv, working_dir))
|
|
@@ -4,10 +4,10 @@ import signal
|
|
|
4
4
|
import uuid
|
|
5
5
|
from typing import Awaitable, Callable, Dict, Any
|
|
6
6
|
|
|
7
|
-
from materials_commons.cli.
|
|
8
|
-
from materials_commons.cli.
|
|
7
|
+
from materials_commons.cli.server.command_handlers import register_handlers
|
|
8
|
+
from materials_commons.cli.server.websocket_server import WebSocketCommandListener
|
|
9
9
|
from materials_commons.cli.user_config import Config
|
|
10
|
-
from materials_commons.cli.
|
|
10
|
+
from materials_commons.cli.server.local_rest_server import LocalRestServer
|
|
11
11
|
|
|
12
12
|
CommandHandler = Callable[[Dict[str, Any]], Awaitable[None]]
|
|
13
13
|
|
{materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/subcommands/up.py
RENAMED
|
@@ -2,13 +2,15 @@ import argparse
|
|
|
2
2
|
import logging
|
|
3
3
|
import sys
|
|
4
4
|
|
|
5
|
+
import igittigitt
|
|
6
|
+
|
|
5
7
|
import materials_commons.cli.exceptions as cliexcept
|
|
6
8
|
import materials_commons.cli.functions as clifuncs
|
|
7
9
|
import materials_commons.cli.globus as cliglobus
|
|
8
10
|
import materials_commons.cli.tree_functions as treefuncs
|
|
9
11
|
from materials_commons.cli.treedb import LocalTree, RemoteTree
|
|
10
12
|
from materials_commons.cli.subcommands.server import DEFAULT_WS_URL
|
|
11
|
-
from materials_commons.cli.
|
|
13
|
+
from materials_commons.cli.server.uploader.api import ws_upload_synchronous
|
|
12
14
|
import os
|
|
13
15
|
from pathlib import Path
|
|
14
16
|
|
|
@@ -106,11 +108,14 @@ def up_subcommand(argv, working_dir):
|
|
|
106
108
|
proj.local_path, args.paths, working_dir)
|
|
107
109
|
|
|
108
110
|
# filter, skipping .mc, those specified by .mcignore
|
|
109
|
-
local_abspaths = treefuncs.filter_local_abspaths(
|
|
110
|
-
proj.local_path, local_abspaths, working_dir)
|
|
111
|
+
local_abspaths = treefuncs.filter_local_abspaths(proj.local_path, local_abspaths, working_dir)
|
|
111
112
|
|
|
112
113
|
mcpaths = treefuncs.clipaths_to_mcpaths(proj.local_path, local_abspaths, working_dir)
|
|
113
114
|
|
|
115
|
+
ignore_parser = igittigitt.IgnoreParser()
|
|
116
|
+
ignore_parser.parse_rule_files(base_dir=proj.local_path, filename=".mcignore",
|
|
117
|
+
add_default_patterns=False)
|
|
118
|
+
|
|
114
119
|
file_paths = []
|
|
115
120
|
project_paths = []
|
|
116
121
|
|
|
@@ -118,7 +123,14 @@ def up_subcommand(argv, working_dir):
|
|
|
118
123
|
if treefuncs.os.path.isdir(local_path):
|
|
119
124
|
if args.recursive:
|
|
120
125
|
for root, dirs, files in os.walk(local_path):
|
|
126
|
+
current_dir = os.path.basename(root)
|
|
127
|
+
if current_dir == ".mc":
|
|
128
|
+
continue
|
|
121
129
|
for filename in files:
|
|
130
|
+
if filename == ".DS_Store":
|
|
131
|
+
continue
|
|
132
|
+
# if ignore_parser.match(filename):
|
|
133
|
+
# continue
|
|
122
134
|
file_path = os.path.join(root, filename)
|
|
123
135
|
|
|
124
136
|
# Calculate relative path and construct mc_path
|
|
@@ -126,6 +138,7 @@ def up_subcommand(argv, working_dir):
|
|
|
126
138
|
file_mc_path = str(Path(mc_path) / relative)
|
|
127
139
|
file_paths.append(file_path)
|
|
128
140
|
project_paths.append(file_mc_path)
|
|
141
|
+
# print(f"Indexing {file_mc_path}, {file_path}")
|
|
129
142
|
else:
|
|
130
143
|
print("Skipping directory because --recursive option not specified: " + local_path)
|
|
131
144
|
else:
|
|
@@ -7,6 +7,7 @@ materials_commons/cli/__init__.py
|
|
|
7
7
|
materials_commons/cli/cloned_project.py
|
|
8
8
|
materials_commons/cli/exceptions.py
|
|
9
9
|
materials_commons/cli/file_functions.py
|
|
10
|
+
materials_commons/cli/filedb.py
|
|
10
11
|
materials_commons/cli/functions.py
|
|
11
12
|
materials_commons/cli/globus.py
|
|
12
13
|
materials_commons/cli/list_objects.py
|
|
@@ -17,18 +18,20 @@ materials_commons/cli/tmp_functions.py
|
|
|
17
18
|
materials_commons/cli/tree_functions.py
|
|
18
19
|
materials_commons/cli/treedb.py
|
|
19
20
|
materials_commons/cli/user_config.py
|
|
20
|
-
materials_commons/cli/
|
|
21
|
-
materials_commons/cli/
|
|
22
|
-
materials_commons/cli/
|
|
23
|
-
materials_commons/cli/
|
|
24
|
-
materials_commons/cli/
|
|
25
|
-
materials_commons/cli/
|
|
26
|
-
materials_commons/cli/
|
|
27
|
-
materials_commons/cli/
|
|
28
|
-
materials_commons/cli/
|
|
29
|
-
materials_commons/cli/
|
|
30
|
-
materials_commons/cli/
|
|
31
|
-
materials_commons/cli/
|
|
21
|
+
materials_commons/cli/server/__init__.py
|
|
22
|
+
materials_commons/cli/server/command_handlers.py
|
|
23
|
+
materials_commons/cli/server/local_rest_server.py
|
|
24
|
+
materials_commons/cli/server/projects.py
|
|
25
|
+
materials_commons/cli/server/websocket_server.py
|
|
26
|
+
materials_commons/cli/server/downloader/__init__.py
|
|
27
|
+
materials_commons/cli/server/downloader/file_download_manager.py
|
|
28
|
+
materials_commons/cli/server/downloader/file_downloader.py
|
|
29
|
+
materials_commons/cli/server/indexer/__init__.py
|
|
30
|
+
materials_commons/cli/server/indexer/file_index_manager.py
|
|
31
|
+
materials_commons/cli/server/uploader/__init__.py
|
|
32
|
+
materials_commons/cli/server/uploader/api.py
|
|
33
|
+
materials_commons/cli/server/uploader/file_transfer_manager.py
|
|
34
|
+
materials_commons/cli/server/uploader/file_uploader.py
|
|
32
35
|
materials_commons/cli/subcommands/__init__.py
|
|
33
36
|
materials_commons/cli/subcommands/clone.py
|
|
34
37
|
materials_commons/cli/subcommands/config.py
|
|
@@ -44,6 +47,7 @@ materials_commons/cli/subcommands/mv.py
|
|
|
44
47
|
materials_commons/cli/subcommands/proj.py
|
|
45
48
|
materials_commons/cli/subcommands/remote.py
|
|
46
49
|
materials_commons/cli/subcommands/rm.py
|
|
50
|
+
materials_commons/cli/subcommands/scan.py
|
|
47
51
|
materials_commons/cli/subcommands/server.py
|
|
48
52
|
materials_commons/cli/subcommands/up.py
|
|
49
53
|
materials_commons/cli/subcommands/versions.py
|
|
@@ -21,7 +21,7 @@ setup(
|
|
|
21
21
|
"": ["*.txt"]
|
|
22
22
|
},
|
|
23
23
|
packages=find_namespace_packages(
|
|
24
|
-
include=['materials_commons.cli', 'materials_commons.cli.subcommands', 'materials_commons.cli.
|
|
24
|
+
include=['materials_commons.cli', 'materials_commons.cli.subcommands', 'materials_commons.cli.server', 'materials_commons.cli.server.uploader', 'materials_commons.cli.server.downloader', 'materials_commons.cli.server.indexer']
|
|
25
25
|
),
|
|
26
26
|
entry_points={
|
|
27
27
|
'console_scripts': ['mc=materials_commons.cli.parser:main']
|
|
@@ -50,6 +50,7 @@ setup(
|
|
|
50
50
|
keywords='materials science mc materials-commons prisms',
|
|
51
51
|
install_requires=[
|
|
52
52
|
"websockets",
|
|
53
|
+
"aiofiles",
|
|
53
54
|
"globus-cli",
|
|
54
55
|
"globus-sdk",
|
|
55
56
|
"igittigitt==2.1.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '2.2.1'
|
|
File without changes
|
|
File without changes
|
{materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/LICENSE.txt
RENAMED
|
File without changes
|
{materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/cloned_project.py
RENAMED
|
File without changes
|
{materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/exceptions.py
RENAMED
|
File without changes
|
{materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/file_functions.py
RENAMED
|
File without changes
|
|
File without changes
|
{materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/list_objects.py
RENAMED
|
File without changes
|
{materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/print_formatter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/sqltable.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
|
{materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/subcommands/ls.py
RENAMED
|
File without changes
|
|
File without changes
|
{materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/subcommands/mv.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/subcommands/rm.py
RENAMED
|
File without changes
|
|
File without changes
|
{materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/tmp_functions.py
RENAMED
|
File without changes
|
{materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/tree_functions.py
RENAMED
|
File without changes
|
|
File without changes
|
{materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/materials_commons/cli/user_config.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
|
{materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/test/test_treecompare_no_cache.py
RENAMED
|
File without changes
|
{materials_commons_cli-2.2.1 → materials_commons_cli-2.2.3}/test/test_treecompare_with_cache.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|