Skip to content

Commit 676c1a3

Browse files
Merge pull request #70 from RRosio/file_transfer
Update file upload/download handlers
2 parents f00208d + 067f6f3 commit 676c1a3

File tree

4 files changed

+220
-67
lines changed

4 files changed

+220
-67
lines changed

Diff for: conftest.py

+33-19
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,37 @@
1212
]
1313

1414

15+
@pytest.fixture(scope="function")
16+
def setup_tmp_local(tmp_path: Path):
17+
local_root = tmp_path / "test"
18+
local_root.mkdir()
19+
local_file = local_root / "file_loc.txt"
20+
local_file.touch()
21+
local_empty_root = tmp_path / "empty"
22+
local_empty_root.mkdir()
23+
24+
return [local_root, local_empty_root]
25+
26+
1527
@pytest.fixture(scope="function", autouse=True)
16-
def setup_config_file_fs(tmp_path: Path):
28+
def setup_config_file_fs(tmp_path: Path, setup_tmp_local):
29+
tmp_local = setup_tmp_local[0]
30+
empty_tmp_local = setup_tmp_local[1]
1731
config_dir = tmp_path / "config"
1832
config_dir.mkdir(exist_ok=True)
1933

20-
yaml_content = """sources:
34+
yaml_content = f"""sources:
2135
- name: "TestSourceAWS"
2236
path: "s3://my-test-bucket/"
2337
additional_options:
2438
anon: false
2539
key: "my-access-key"
2640
secret: "my-secret-key"
2741
- name: "TestDir"
28-
path: "/Users/someuser/Desktop/test_fsspec"
42+
path: "{tmp_local}"
2943
protocol: "local"
3044
- name: "TestEmptyLocalDir"
31-
path: "/Users/someuser/Desktop/notebooks/sample/nothinghere"
45+
path: "{empty_tmp_local}"
3246
protocol: "local"
3347
- name: "TestMem Source"
3448
path: "/my_mem_dir"
@@ -50,31 +64,31 @@ def setup_config_file_fs(tmp_path: Path):
5064
def fs_manager_instance(setup_config_file_fs):
5165
fs_manager = setup_config_file_fs
5266
fs_info = fs_manager.get_filesystem_by_protocol("memory")
53-
fs = fs_info["info"]["instance"]
67+
mem_fs = fs_info["info"]["instance"]
5468
mem_root_path = fs_info["info"]["path"]
5569

56-
if fs:
57-
if fs.exists(f"{mem_root_path}/test_dir"):
58-
fs.rm(f"{mem_root_path}/test_dir", recursive=True)
59-
if fs.exists(f"{mem_root_path}/second_dir"):
60-
fs.rm(f"{mem_root_path}/second_dir", recursive=True)
70+
if mem_fs:
71+
if mem_fs.exists(f"{mem_root_path}/test_dir"):
72+
mem_fs.rm(f"{mem_root_path}/test_dir", recursive=True)
73+
if mem_fs.exists(f"{mem_root_path}/second_dir"):
74+
mem_fs.rm(f"{mem_root_path}/second_dir", recursive=True)
6175

62-
fs.touch(f"{mem_root_path}/file_in_root.txt")
63-
with fs.open(f"{mem_root_path}/file_in_root.txt", "wb") as f:
76+
mem_fs.touch(f"{mem_root_path}/file_in_root.txt")
77+
with mem_fs.open(f"{mem_root_path}/file_in_root.txt", "wb") as f:
6478
f.write("Root file content".encode())
6579

66-
fs.mkdir(f"{mem_root_path}/test_dir", exist_ok=True)
67-
fs.mkdir(f"{mem_root_path}/second_dir", exist_ok=True)
68-
# fs.mkdir(f'{mem_root_path}/second_dir/subdir', exist_ok=True)
69-
fs.touch(f"{mem_root_path}/test_dir/file1.txt")
70-
with fs.open(f"{mem_root_path}/test_dir/file1.txt", "wb") as f:
80+
mem_fs.mkdir(f"{mem_root_path}/test_dir", exist_ok=True)
81+
mem_fs.mkdir(f"{mem_root_path}/second_dir", exist_ok=True)
82+
# mem_fs.mkdir(f'{mem_root_path}/second_dir/subdir', exist_ok=True)
83+
mem_fs.touch(f"{mem_root_path}/test_dir/file1.txt")
84+
with mem_fs.open(f"{mem_root_path}/test_dir/file1.txt", "wb") as f:
7185
f.write("Test content".encode())
7286
f.close()
7387
else:
7488
print("In memory filesystem NOT FOUND")
7589

76-
if fs.exists(f"{mem_root_path}/test_dir/file1.txt"):
77-
file_info = fs.info(f"{mem_root_path}/test_dir/file1.txt")
90+
if mem_fs.exists(f"{mem_root_path}/test_dir/file1.txt"):
91+
file_info = mem_fs.info(f"{mem_root_path}/test_dir/file1.txt")
7892
print(f"File exists. size: {file_info}")
7993
else:
8094
print("File does not exist!")

Diff for: jupyter_fsspec/handlers.py

+9-4
Original file line numberDiff line numberDiff line change
@@ -183,16 +183,21 @@ def post(self):
183183
try:
184184
if action == "upload":
185185
# upload fs_instance.put(local_path, remote_path)
186-
fs_instance.put(item_path, destination)
187-
response["description"] = f"Uploaded {item_path} to {destination}."
186+
local_path = item_path
187+
remote_path = destination
188+
fs_instance.put(local_path, remote_path, recursive=True)
189+
response["description"] = f"Uploaded {local_path} to {remote_path}."
188190
else: # download
189191
# download fs_instance.get(remote_path, local_path)
190-
fs_instance.get(destination, item_path)
191-
response["description"] = f"Downloaded {destination} to {item_path}."
192+
local_path = destination
193+
remote_path = item_path
194+
fs_instance.get(remote_path, local_path, recursive=True)
195+
response["description"] = f"Downloaded {remote_path} to {local_path}."
192196

193197
response["status"] = "success"
194198
self.set_status(200)
195199
except Exception as e:
200+
print(f"Error uploading/downloading file: {e}")
196201
self.set_status(500)
197202
response["status"] = "failed"
198203
response["description"] = str(e)

Diff for: jupyter_fsspec/tests/test_api.py

+122-17
Original file line numberDiff line numberDiff line change
@@ -478,25 +478,130 @@ async def xtest___async_s3_file_operations(mock_s3_fs):
478478
print(f"contents: {contents}")
479479

480480

481-
# TODO: Test transfer endpoint
482-
async def xtest_file_transfer(fs_manager_instance_parameterized, jp_fetch):
483-
fs_manager = fs_manager_instance_parameterized
484-
fs_info = fs_manager.get_filesystem_by_protocol("memory")
485-
fs = fs_info["info"]["instance"]
486-
assert fs is not None
481+
# TODO: Update file paths from memory to remote; Update remote_key usage
482+
async def xtest_file_transfer(fs_manager_instance, jp_fetch):
483+
fs_manager = fs_manager_instance
484+
remote_fs_info = fs_manager.get_filesystem_by_protocol("s3")
485+
remote_key = remote_fs_info["key"]
486+
remote_fs = remote_fs_info["info"]["instance"]
487+
remote_item_path = remote_fs_info["info"]["path"]
488+
assert remote_fs is not None
489+
assert remote_fs.exists(remote_item_path)
490+
491+
local_fs_info = fs_manager.get_filesystem_by_protocol("local")
492+
local_key = local_fs_info["key"]
493+
local_fs = local_fs_info["info"]["instance"]
494+
local_item_path = local_fs_info["info"]["path"]
495+
assert local_fs is not None
496+
497+
# upload file [local to remote]
498+
upload_filepath = f"{local_item_path}/file_loc.txt"
499+
assert local_fs.exists(upload_filepath)
500+
upload_file_payload = {
501+
"item_path": upload_filepath,
502+
"content": remote_item_path,
503+
"destination_key": remote_key,
504+
"action": "upload",
505+
}
506+
upload_file_res = await jp_fetch(
507+
"jupyter_fsspec",
508+
"files",
509+
"transfer",
510+
method="POST",
511+
params={"key": local_key},
512+
body=json.dumps(upload_file_payload),
513+
)
514+
515+
upfile_body_json = upload_file_res.body.decode("utf-8")
516+
upfile_body = json.loads(upfile_body_json)
517+
518+
assert upfile_body["status"] == "success"
519+
assert upfile_body["description"] == f"Uploaded {upload_filepath} to /my_mem_dir."
520+
521+
uploaded_filepath = remote_item_path + "/file_loc.txt"
522+
assert remote_fs.exists(uploaded_filepath)
523+
524+
# upload dir [local to remote]
525+
upload_dirpath = local_item_path
526+
upload_dir_payload = {
527+
"item_path": upload_dirpath,
528+
"content": remote_item_path,
529+
"destination_key": remote_key,
530+
"action": "upload",
531+
}
532+
upload_dir_res = await jp_fetch(
533+
"jupyter_fsspec",
534+
"files",
535+
"transfer",
536+
method="POST",
537+
params={"key": remote_key},
538+
body=json.dumps(upload_dir_payload),
539+
)
540+
541+
updir_body_json = upload_dir_res.body.decode("utf-8")
542+
updir_body = json.loads(updir_body_json)
543+
assert updir_body["status"] == "success"
544+
assert updir_body["description"] == f"Uploaded {upload_dirpath} to /my_mem_dir."
545+
546+
# download file [other to remote]
547+
download_filepath = "/my_mem_dir/test_dir/file1.txt"
548+
download_file_payload = {
549+
"item_path": download_filepath,
550+
"content": local_item_path,
551+
"destination_key": local_key,
552+
"action": "download",
553+
}
554+
download_file_res = await jp_fetch(
555+
"jupyter_fsspec",
556+
"files",
557+
"transfer",
558+
method="POST",
559+
params={"key": remote_key},
560+
body=json.dumps(download_file_payload),
561+
)
487562

488-
# # copy file
489-
# copy_filepath = f'{fs_root_path}/test_dir/file1.txt'
490-
# copy_file_payload = {"item_path": copy_filepath, "content": "/my_local_dir/", "destination_key": "", "action": "copy"}
491-
# copy_file_res = await jp_fetch("jupyter_fsspec", "files", "transfer", method="POST", params={"key": mem_key}, body=json.dumps(copy_file_payload))
563+
download_file_body_json = download_file_res.body.decode("utf-8")
564+
download_file_body = json.loads(download_file_body_json)
565+
assert download_file_body["status"] == "success"
566+
assert (
567+
download_file_body["description"]
568+
== f"Downloaded {download_filepath} to {local_item_path}."
569+
)
492570

493-
# cfile_body_json = copy_file_res.body.decode('utf-8')
494-
# cfile_body = json.loads(cfile_body_json)
495-
# assert cfile_body["status"] == 'success'
496-
# assert cfile_body['description'] == f'Copied {fs_root_path}/test_dir/file1.txt to /my_local_dir/file1.txt'
571+
downloaded_filepath = local_item_path + "/file1.txt"
572+
print("downloaded_filepath: ", downloaded_filepath)
573+
assert local_fs.exists(downloaded_filepath)
574+
575+
# download dir [other to local]
576+
download_dirpath = "/my_mem_dir/test_dir"
577+
download_dir_payload = {
578+
"item_path": download_dirpath,
579+
"content": local_item_path,
580+
"destination_key": local_key,
581+
"action": "download",
582+
}
583+
download_dir_res = await jp_fetch(
584+
"jupyter_fsspec",
585+
"files",
586+
"transfer",
587+
method="POST",
588+
params={"key": remote_key},
589+
body=json.dumps(download_dir_payload),
590+
)
497591

498-
# copy dir
592+
download_dir_body_json = download_dir_res.body.decode("utf-8")
593+
download_dir_body = json.loads(download_dir_body_json)
594+
assert download_dir_body["status"] == "success"
595+
assert (
596+
download_dir_body["description"]
597+
== f"Downloaded {download_dirpath} to {local_item_path}."
598+
)
499599

500-
# move file
600+
downloaded_dirpath = local_item_path + "/test_dir/"
601+
print("downloaded_dirpath: ", downloaded_dirpath)
602+
print("ls of downloaded_dirpath: ", local_fs.ls(local_item_path))
603+
assert local_fs.exists(downloaded_dirpath)
501604

502-
# move dir
605+
downloaded_dir_asset_path = local_item_path + "/test_dir/file1.txt"
606+
print("downloaded_dir_asset_path: ", downloaded_dir_asset_path)
607+
assert local_fs.exists(downloaded_dir_asset_path)

Diff for: src/handler/fileOperations.ts

+56-27
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,9 @@ export class FsspecModel {
8989
async refreshConfig() {
9090
// TODO fix/refactor
9191
this.userFilesystems = {};
92-
Logger.debug('aaa');
9392
Logger.debug('[FSSpec] Refresh config requested');
9493
try {
95-
Logger.debug('bbb');
9694
for (let i = 0; i < this.retry; i++) {
97-
Logger.debug('ccc');
9895
Logger.info('[FSSpec] Attempting to read config file...');
9996
const result = await this.getStoredFilesystems(); // This is a result dict, not a response
10097
if (result?.status === 'success') {
@@ -107,16 +104,13 @@ export class FsspecModel {
107104
// Set active filesystem to first
108105
if (Object.keys(result).length > 0) {
109106
this.activeFilesystem = Object.keys(this.userFilesystems)[0];
110-
Logger.debug('ddd');
111107
}
112108
break;
113109
} else {
114-
Logger.debug('eee');
115110
// TODO handle no config file
116111
Logger.error('[FSSpec] Error fetching filesystems from user config');
117112
if (i + 1 < this.retry) {
118113
Logger.info('[FSSpec] retrying...');
119-
Logger.debug('fffr');
120114
}
121115
}
122116
}
@@ -125,7 +119,6 @@ export class FsspecModel {
125119
`[FSSpec] Error: Unknown error initializing fsspec model:\n${error}`
126120
);
127121
}
128-
Logger.debug('zzz');
129122
}
130123

131124
async getStoredFilesystems(): Promise<any> {
@@ -224,26 +217,6 @@ export class FsspecModel {
224217
}
225218

226219
async delete(key: string, item_path: string): Promise<any> {
227-
try {
228-
const reqBody = JSON.stringify({
229-
key,
230-
item_path
231-
});
232-
const response = await requestAPI<any>('fsspec', {
233-
method: 'DELETE',
234-
body: reqBody,
235-
headers: {
236-
'Content-Type': 'application/json'
237-
}
238-
});
239-
console.log('response is: ', response);
240-
} catch (error) {
241-
console.error('Failed to delete: ', error);
242-
return null;
243-
}
244-
}
245-
246-
async delete_refactored(key: string, item_path: string): Promise<any> {
247220
try {
248221
const query = new URLSearchParams({
249222
key,
@@ -337,6 +310,62 @@ export class FsspecModel {
337310
}
338311
}
339312

313+
async upload(
314+
key: string,
315+
local_path: string,
316+
remote_path: string,
317+
action: 'upload'
318+
): Promise<any> {
319+
try {
320+
const query = new URLSearchParams({ action: action });
321+
const reqBody = JSON.stringify({
322+
key: key,
323+
local_path,
324+
remote_path
325+
});
326+
327+
const response = await requestAPI<any>(
328+
`files/transfer?${query.toString()}`,
329+
{
330+
method: 'POST',
331+
body: reqBody,
332+
headers: {
333+
'Content-Type': 'application/json'
334+
}
335+
}
336+
);
337+
console.log('response: ', response);
338+
} catch (error) {
339+
console.error('Failed to upload: ', error);
340+
return null;
341+
}
342+
}
343+
344+
async download(
345+
key: string,
346+
remote_path: string,
347+
local_path: string,
348+
action: 'download'
349+
): Promise<any> {
350+
try {
351+
const query = new URLSearchParams({ action: action });
352+
const reqBody = JSON.stringify({
353+
key: key,
354+
remote_path,
355+
local_path
356+
});
357+
await requestAPI<any>(`files/transfer?${query.toString()}`, {
358+
method: 'POST',
359+
body: reqBody,
360+
headers: {
361+
'Content-Type': 'application/json'
362+
}
363+
});
364+
} catch (error) {
365+
console.error('Failed to download: ', error);
366+
}
367+
}
368+
340369
// async update(
341370
// key: any = 'local%7CSourceDisk%7C.',
342371
// item_path = '',

0 commit comments

Comments
 (0)