Skip to content

Commit 783ff78

Browse files
committed
feat(backend): use Path object from pathlib instead of os.path library
1 parent 898a55c commit 783ff78

File tree

7 files changed

+80
-87
lines changed

7 files changed

+80
-87
lines changed

config.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# Licensed under MIT (https://github.com/kolomenkin/limbo/blob/master/LICENSE)
44
#
55
import os
6+
from pathlib import Path
67

78

89
def read_env(name: str, default_value: str) -> str:
@@ -16,7 +17,7 @@ def read_env(name: str, default_value: str) -> str:
1617
LISTEN_HOST = read_env('LIMBO_LISTEN_HOST', '127.0.0.1')
1718
LISTEN_PORT = int(read_env('LIMBO_LISTEN_PORT', '8080'))
1819

19-
STORAGE_DIRECTORY = read_env('LIMBO_STORAGE_DIRECTORY', './storage')
20+
STORAGE_DIRECTORY = Path(read_env('LIMBO_STORAGE_DIRECTORY', './storage'))
2021

2122
# STORAGE_WEB_URL_BASE allows to specify alternative web url to
2223
# read files stored in STORAGE_DIRECTORY through HTTP/HTTPS.

lib_common.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
# Copyright 2018-2022 Sergey Kolomenkin
33
# Licensed under MIT (https://github.com/kolomenkin/limbo/blob/master/LICENSE)
44
#
5-
import os
6-
import stat
5+
from pathlib import Path
76

87

9-
def get_file_modified_unixtime(pathname: str) -> float:
10-
return os.stat(pathname)[stat.ST_MTIME]
8+
def get_file_modified_unixtime(pathname: Path) -> float:
9+
return pathname.stat().st_mtime

lib_file_storage.py

Lines changed: 54 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@
22
# Copyright 2018-2022 Sergey Kolomenkin
33
# Licensed under MIT (https://github.com/kolomenkin/limbo/blob/master/LICENSE)
44
#
5-
import io
65
import logging
7-
import os
86
import re
97
import threading
108
from dataclasses import dataclass
9+
from pathlib import Path
1110
from time import sleep, time
1211
from typing import Any, List, Optional, Sequence
1312
from uuid import uuid4
@@ -44,19 +43,19 @@ def clean_filename(filename: str) -> str:
4443

4544

4645
class AtomicFile:
47-
def __init__(self, temp_filename: str, final_filename: str):
48-
if os.path.isfile(final_filename):
46+
def __init__(self, temp_filename: Path, final_filename: Path):
47+
if final_filename.is_file():
4948
raise Exception('Destination file already exists')
50-
self._temp_filename = temp_filename
51-
self._final_filename = final_filename
52-
self._fd = io.open(self._temp_filename, 'wb') # pylint: disable=consider-using-with
49+
self._temp_filename: Path = temp_filename
50+
self._final_filename: Path = final_filename
51+
self._fd = self._temp_filename.open('wb') # pylint: disable=consider-using-with
5352

5453
def write(self, data: bytes) -> None:
5554
self._fd.write(data)
5655

5756
def close(self) -> None:
5857
self._fd.close()
59-
os.rename(self._temp_filename, self._final_filename)
58+
self._temp_filename.rename(self._final_filename)
6059

6160
def __enter__(self) -> 'AtomicFile':
6261
return self
@@ -65,28 +64,28 @@ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
6564
self._fd.close()
6665
if exc_tb is None:
6766
# No exception, so rename
68-
os.rename(self._temp_filename, self._final_filename)
67+
self._temp_filename.rename(self._final_filename)
6968

7069

7170
@dataclass
7271
class DisplayFileItem:
73-
full_disk_filename: str
72+
full_disk_filename: Path
7473
url_filename: str
7574
display_filename: str
7675

7776

7877
@dataclass
7978
class StorageFileItem:
80-
storage_directory: str
79+
storage_directory: Path
8180
disk_filename: str
8281
display_filename: str
8382

8483

8584
class FileStorage:
86-
def __init__(self, storage_directory: str, max_store_time_seconds: int):
85+
def __init__(self, storage_directory: Path, max_store_time_seconds: int):
8786
LOGGER.info('FileStorage: create("%s", max %d sec)', storage_directory, max_store_time_seconds)
88-
self._storage_directory = os.path.abspath(storage_directory)
89-
self._temp_directory = os.path.join(self._storage_directory, 'incomplete')
87+
self._storage_directory: Path = storage_directory.absolute()
88+
self._temp_directory: Path = self._storage_directory / 'incomplete'
9089
self._max_store_time_seconds = max_store_time_seconds
9190
self._retention_thread: Optional[threading.Thread] = None
9291
self._protect_stop = threading.Lock()
@@ -109,16 +108,15 @@ def stop(self) -> None:
109108
self._retention_thread.join()
110109

111110
def enumerate_files(self) -> Sequence[DisplayFileItem]:
112-
if not os.path.isdir(self._storage_directory):
111+
if not self._storage_directory.is_dir():
113112
return []
114113
files: List[DisplayFileItem] = []
115-
for disk_filename in os.listdir(self._storage_directory):
116-
fullname = os.path.join(self._storage_directory, disk_filename)
117-
if os.path.isfile(fullname):
118-
url_filename = self._fname_disk_to_url(disk_filename)
119-
display_filename = self._fname_disk_to_display(disk_filename)
114+
for disk_filename in self._storage_directory.iterdir():
115+
if disk_filename.is_file():
116+
url_filename = self._fname_disk_to_url(disk_filename.name)
117+
display_filename = self._fname_disk_to_display(disk_filename.name)
120118
files.append(DisplayFileItem(
121-
full_disk_filename=fullname,
119+
full_disk_filename=disk_filename,
122120
url_filename=url_filename,
123121
display_filename=display_filename,
124122
))
@@ -128,8 +126,8 @@ def open_file_writer(self, original_filename: str) -> AtomicFile:
128126
self._create_dirs()
129127
disk_filename = self._fname_original_to_disk(original_filename)
130128
temp_disk_filename = f'{uuid4().hex}.{disk_filename}'
131-
temp_fullname = os.path.join(self._temp_directory, temp_disk_filename)
132-
fullname = os.path.join(self._storage_directory, disk_filename)
129+
temp_fullname = self._temp_directory / temp_disk_filename
130+
fullname = self._storage_directory / disk_filename
133131
LOGGER.info('FileStorage: Upload file: %s', disk_filename)
134132
return AtomicFile(temp_fullname, fullname)
135133

@@ -144,28 +142,26 @@ def get_file_info_to_read(self, url_filename: str) -> StorageFileItem:
144142

145143
def remove_file(self, url_filename: str) -> None:
146144
disk_filename = self._fname_url_to_disk(url_filename)
147-
fullname = os.path.join(self._storage_directory, disk_filename)
148-
file_size = os.path.getsize(fullname)
145+
fullname = self._storage_directory / disk_filename
146+
file_size = fullname.stat().st_size
149147
LOGGER.info('FileStorage: Remove file: "%s"; size: %d', disk_filename, file_size)
150-
os.remove(fullname)
148+
fullname.unlink()
151149

152150
def remove_all_files(self) -> None:
153-
if not os.path.isdir(self._storage_directory):
151+
if not self._storage_directory.is_dir():
154152
return
155-
for disk_filename in os.listdir(self._storage_directory):
156-
fullname = os.path.join(self._storage_directory, disk_filename)
157-
if os.path.isfile(fullname):
158-
file_size = os.path.getsize(fullname)
159-
LOGGER.info('FileStorage: Remove file: "%s"; size: %d', disk_filename, file_size)
160-
os.remove(fullname)
161-
if not os.path.isdir(self._temp_directory):
153+
for disk_filename in self._storage_directory.iterdir():
154+
if disk_filename.is_file():
155+
file_size = disk_filename.stat().st_size
156+
LOGGER.info('FileStorage: Remove file: "%s"; size: %d', disk_filename.name, file_size)
157+
disk_filename.unlink()
158+
if not self._temp_directory.is_dir():
162159
return
163-
for disk_filename in os.listdir(self._temp_directory):
164-
fullname = os.path.join(self._temp_directory, disk_filename)
165-
if os.path.isfile(fullname):
166-
file_size = os.path.getsize(fullname)
167-
LOGGER.info('FileStorage: Remove temp file: "%s"; size: %d', disk_filename, file_size)
168-
os.remove(fullname)
160+
for disk_filename in self._temp_directory.iterdir():
161+
if disk_filename.is_file():
162+
file_size = disk_filename.stat().st_size
163+
LOGGER.info('FileStorage: Remove temp file: "%s"; size: %d', disk_filename.name, file_size)
164+
disk_filename.unlink()
169165

170166
@classmethod
171167
def _fname_original_to_disk(cls, original_filename: str) -> str:
@@ -191,11 +187,11 @@ def _canonize_file(filename: str) -> str:
191187
return canonized
192188

193189
def _create_dirs(self) -> None:
194-
if not os.path.isdir(self._storage_directory):
195-
os.makedirs(self._storage_directory, 0o755)
190+
if not self._storage_directory.is_dir():
191+
self._storage_directory.mkdir(mode=0o755)
196192

197-
if not os.path.isdir(self._temp_directory):
198-
os.makedirs(self._temp_directory, 0o755)
193+
if not self._temp_directory.is_dir():
194+
self._temp_directory.mkdir(mode=0o755)
199195

200196
def _retention_thread_procedure(self) -> None:
201197
LOGGER.info('FileStorage: Retention thread started')
@@ -222,24 +218,22 @@ def _retention_thread_procedure(self) -> None:
222218

223219
def _check_retention(self) -> None:
224220
now = time()
225-
if not os.path.isdir(self._storage_directory):
221+
if not self._storage_directory.is_dir():
226222
return
227-
for file in os.listdir(self._storage_directory):
228-
fullname = os.path.join(self._storage_directory, file)
229-
if os.path.isfile(fullname):
230-
modified_unixtime = get_file_modified_unixtime(fullname)
223+
for file in self._storage_directory.iterdir():
224+
if file.is_file():
225+
modified_unixtime = get_file_modified_unixtime(file)
231226
if now - modified_unixtime > self._max_store_time_seconds:
232-
file_size = os.path.getsize(fullname)
233-
LOGGER.info('FileStorage: Remove outdated file: "%s"; size: %d', fullname, file_size)
234-
os.remove(fullname)
227+
file_size = file.stat().st_size
228+
LOGGER.info('FileStorage: Remove outdated file: "%s"; size: %d', file, file_size)
229+
file.unlink()
235230

236-
if not os.path.isdir(self._temp_directory):
231+
if not self._temp_directory.is_dir():
237232
return
238-
for file in os.listdir(self._temp_directory):
239-
fullname = os.path.join(self._temp_directory, file)
240-
if os.path.isfile(fullname):
241-
modified_unixtime = get_file_modified_unixtime(fullname)
233+
for file in self._temp_directory.iterdir():
234+
if file.is_file():
235+
modified_unixtime = get_file_modified_unixtime(file)
242236
if now - modified_unixtime > 15 * 60: # every 15 minutes
243-
file_size = os.path.getsize(fullname)
244-
LOGGER.info('FileStorage: Remove outdated temp file: "%s"; size: %d', fullname, file_size)
245-
os.remove(fullname)
237+
file_size = file.stat().st_size
238+
LOGGER.info('FileStorage: Remove outdated temp file: "%s"; size: %d', file, file_size)
239+
file.unlink()

server.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import sys
1313
import threading
1414
import urllib.parse
15+
from pathlib import Path
1516
from time import time
1617
from typing import Any, Dict, List, Mapping, Optional, Union
1718

@@ -93,7 +94,7 @@ def root_page() -> ViewResponse:
9394
'display_filename': file.display_filename,
9495
'url': URLPREFIX + urllib.parse.quote(file.url_filename),
9596
'url_filename': file.url_filename,
96-
'size': format_size(os.path.getsize(file.full_disk_filename)),
97+
'size': format_size(file.full_disk_filename.stat().st_size),
9798
'age': format_age(int(now - modified_unixtime)),
9899
'sortBy': now - modified_unixtime,
99100
}
@@ -120,7 +121,7 @@ def cgi_enumerate() -> MethodResponse:
120121
'display_filename': file.display_filename,
121122
'url': URLPREFIX + urllib.parse.quote(file.url_filename),
122123
'url_filename': file.url_filename,
123-
'size': os.path.getsize(file.full_disk_filename),
124+
'size': file.full_disk_filename.stat().st_size,
124125
'modified': modified_unixtime,
125126
}
126127
)
@@ -232,8 +233,8 @@ def cgi_remove_all() -> MethodResponse:
232233
@bottle_route('/static/<urlpath:path>')
233234
def server_static(urlpath: str) -> RouteResponse:
234235
LOGGER.debug('Static file requested: %s', urlpath)
235-
root_folder = os.path.abspath(os.path.dirname(__file__))
236-
response = bottle.static_file(urlpath, root=os.path.join(root_folder, 'static'))
236+
root_folder = Path(__file__).parent.absolute()
237+
response = bottle.static_file(urlpath, root=root_folder / 'static')
237238
response.set_header('Cache-Control', 'public, max-age=604800')
238239
return response
239240

@@ -348,7 +349,7 @@ def main() -> None:
348349

349350
LOGGER.info('Start server...')
350351

351-
bottle.TEMPLATE_PATH = [os.path.join(os.path.dirname(__file__), 'static', 'templates')]
352+
bottle.TEMPLATE_PATH = [Path(__file__).parent / 'static' / 'templates']
352353

353354
server_thread = threading.Thread(target=run_bottle, daemon=True, name='bottle')
354355
server_thread.start()

tests/test_file_storage.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
from base64 import b64decode
22
from dataclasses import dataclass
3-
from os import path as os_path
3+
from pathlib import Path
44
from tempfile import TemporaryDirectory
55
from typing import Sequence
66
from unittest import TestCase
77

88
from numpy import random
99

10-
# add parent dir to search for imported modules
11-
# import os, sys
12-
# script_dir = os.path.dirname(os.path.abspath(__file__))
13-
# sys.path.insert(0, script_dir + '/../')
1410
from lib_file_storage import DisplayFileItem, FileStorage, StorageFileItem
1511

1612

@@ -30,7 +26,7 @@ class TempStorage:
3026
def get_temp_file_storage() -> TempStorage:
3127
temp_directory = TemporaryDirectory() # pylint: disable=consider-using-with
3228
print('created temporary directory: ' + temp_directory.name)
33-
storage = FileStorage(temp_directory.name, 24 * 3600)
29+
storage = FileStorage(Path(temp_directory.name), 24 * 3600)
3430
return TempStorage(temp_directory=temp_directory, storage=storage)
3531

3632

@@ -53,14 +49,14 @@ def process_single_file(self, original_filename: str, original_filedata: bytes)
5349
url_filename = item.url_filename
5450
display_filename = item.display_filename
5551
self.assertEqual(display_filename, original_filename)
56-
size = os_path.getsize(item.full_disk_filename)
52+
size = item.full_disk_filename.stat().st_size
5753
self.assertEqual(size, len(original_filedata))
5854

5955
info: StorageFileItem = storage.get_file_info_to_read(url_filename)
60-
self.assertEqual(info.storage_directory, temp_storage.temp_directory.name)
56+
self.assertEqual(info.storage_directory, Path(temp_storage.temp_directory.name))
6157
self.assertEqual(info.display_filename, display_filename)
6258

63-
fullpath = os_path.join(info.storage_directory, info.disk_filename)
59+
fullpath = info.storage_directory / info.disk_filename
6460
with open(fullpath, 'rb') as file:
6561
filedata = file.read()
6662
self.assertEqual(filedata, original_filedata)
@@ -122,13 +118,13 @@ def test_multiple_files(self) -> None:
122118
item = files[0]
123119
url_filename1 = item.url_filename
124120
self.assertEqual(item.display_filename, original_filename1)
125-
size = os_path.getsize(item.full_disk_filename)
121+
size = item.full_disk_filename.stat().st_size
126122
self.assertEqual(size, len(original_filedata1))
127123

128124
item = files[1]
129125
url_filename2 = item.url_filename
130126
self.assertEqual(item.display_filename, original_filename2)
131-
size = os_path.getsize(item.full_disk_filename)
127+
size = item.full_disk_filename.stat().st_size
132128
self.assertEqual(size, len(original_filedata2))
133129

134130
storage.remove_file(url_filename1)

tests/test_server.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from base64 import b64decode
55
from dataclasses import dataclass
66
from datetime import datetime
7+
from pathlib import Path
78
from tempfile import TemporaryDirectory
89
from typing import Any, List, Optional, Sequence
910
from unittest import TestCase
@@ -51,9 +52,9 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
5152

5253
@staticmethod
5354
def run_child_server(server_name: str, host: str, port: int) -> RunningServer:
54-
script_dir = os.path.dirname(os.path.abspath(__file__))
55-
root_dir = os.path.join(script_dir, '..')
56-
server_py = os.path.join(root_dir, 'server.py')
55+
script_dir = Path(__file__).parent.absolute()
56+
root_dir = script_dir.parent
57+
server_py = root_dir / 'server.py'
5758

5859
temp_directory = TemporaryDirectory() # pylint: disable=consider-using-with
5960
log('created temporary directory: ' + temp_directory.name)

utils/speedtest.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import sys
55
from dataclasses import dataclass
66
from datetime import datetime
7+
from pathlib import Path
78
from tempfile import TemporaryDirectory
89
from typing import List, Optional, Sequence
910

@@ -43,9 +44,9 @@ def __init__(self) -> None:
4344

4445
@staticmethod
4546
def run_child_server(server_name: str, port: int) -> RunningServer:
46-
script_dir = os.path.dirname(os.path.abspath(__file__))
47-
root_dir = os.path.join(script_dir, '..')
48-
server_py = os.path.join(root_dir, 'server.py')
47+
script_dir = Path(__file__).parent.absolute()
48+
root_dir = script_dir.parent
49+
server_py = root_dir / 'server.py'
4950

5051
temp_directory = TemporaryDirectory() # pylint: disable=consider-using-with
5152
LOGGER.info('created temporary directory: %s', temp_directory.name)

0 commit comments

Comments
 (0)