littlefs-python 0.15.0__cp314-cp314-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- littlefs/__init__.py +494 -0
- littlefs/__main__.py +313 -0
- littlefs/context.py +171 -0
- littlefs/errors.py +37 -0
- littlefs/lfs.cp314-win_amd64.pyd +0 -0
- littlefs/lfs.pyi +114 -0
- littlefs/py.typed +0 -0
- littlefs_python-0.15.0.dist-info/METADATA +226 -0
- littlefs_python-0.15.0.dist-info/RECORD +13 -0
- littlefs_python-0.15.0.dist-info/WHEEL +5 -0
- littlefs_python-0.15.0.dist-info/entry_points.txt +2 -0
- littlefs_python-0.15.0.dist-info/licenses/LICENSE +29 -0
- littlefs_python-0.15.0.dist-info/top_level.txt +1 -0
littlefs/__init__.py
ADDED
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import warnings
|
|
3
|
+
from typing import TYPE_CHECKING, List, Tuple, Iterator, IO, Union, Optional
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
from importlib_metadata import version, PackageNotFoundError
|
|
7
|
+
except ImportError:
|
|
8
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
from . import errors, lfs
|
|
12
|
+
from .lfs import (
|
|
13
|
+
__LFS_DISK_VERSION__,
|
|
14
|
+
__LFS_VERSION__,
|
|
15
|
+
LFSConfig,
|
|
16
|
+
LFSFilesystem,
|
|
17
|
+
LFSFile,
|
|
18
|
+
LFSDirectory,
|
|
19
|
+
LFSFileFlag,
|
|
20
|
+
LFSStat,
|
|
21
|
+
LFSFSStat,
|
|
22
|
+
)
|
|
23
|
+
from .errors import LittleFSError
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"FileHandle",
|
|
27
|
+
"LFSConfig",
|
|
28
|
+
"LFSDirectory",
|
|
29
|
+
"LFSFSStat",
|
|
30
|
+
"LFSFile",
|
|
31
|
+
"LFSFileFlag",
|
|
32
|
+
"LFSFilesystem",
|
|
33
|
+
"LFSStat",
|
|
34
|
+
"LittleFS",
|
|
35
|
+
"LittleFSError",
|
|
36
|
+
"UserContext",
|
|
37
|
+
"UserContextWinDisk",
|
|
38
|
+
"__LFS_DISK_VERSION__",
|
|
39
|
+
"__LFS_VERSION__",
|
|
40
|
+
"errors",
|
|
41
|
+
"lfs",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
__version__ = version("littlefs-python")
|
|
47
|
+
except PackageNotFoundError:
|
|
48
|
+
# Package not installed
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
from .context import UserContext, UserContextWinDisk
|
|
52
|
+
|
|
53
|
+
if TYPE_CHECKING:
|
|
54
|
+
from .lfs import LFSStat
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class LittleFS:
|
|
58
|
+
"""Littlefs file system"""
|
|
59
|
+
|
|
60
|
+
def __init__(self, context: Optional["UserContext"] = None, mount=True, **kwargs) -> None:
|
|
61
|
+
self.cfg = lfs.LFSConfig(context=context, **kwargs)
|
|
62
|
+
self.fs = lfs.LFSFilesystem()
|
|
63
|
+
|
|
64
|
+
if mount:
|
|
65
|
+
try:
|
|
66
|
+
self.mount()
|
|
67
|
+
except errors.LittleFSError:
|
|
68
|
+
self.format()
|
|
69
|
+
self.mount()
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def block_count(self) -> int:
|
|
73
|
+
return self.fs.block_count
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def used_block_count(self) -> int:
|
|
77
|
+
return lfs.fs_size(self.fs)
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def context(self) -> "UserContext":
|
|
81
|
+
"""User context of the file system"""
|
|
82
|
+
return self.cfg.user_context
|
|
83
|
+
|
|
84
|
+
def format(self) -> int:
|
|
85
|
+
"""Format the underlying buffer"""
|
|
86
|
+
if self.cfg.block_count == 0:
|
|
87
|
+
# ``lfs.format`` looks at cfg's block_count.
|
|
88
|
+
# Cannot autodetect size when formatting.
|
|
89
|
+
raise LittleFSError(LittleFSError.Error.LFS_ERR_INVAL)
|
|
90
|
+
return lfs.format(self.fs, self.cfg)
|
|
91
|
+
|
|
92
|
+
def mount(self) -> int:
|
|
93
|
+
"""Mount the underlying buffer"""
|
|
94
|
+
return lfs.mount(self.fs, self.cfg)
|
|
95
|
+
|
|
96
|
+
def unmount(self) -> int:
|
|
97
|
+
"""Unmount the underlying buffer"""
|
|
98
|
+
return lfs.unmount(self.fs)
|
|
99
|
+
|
|
100
|
+
def fs_mkconsistent(self) -> int:
|
|
101
|
+
"""Attempt to make the filesystem consistent and ready for writing"""
|
|
102
|
+
return lfs.fs_mkconsistent(self.fs)
|
|
103
|
+
|
|
104
|
+
def fs_grow(self, block_count: int) -> int:
|
|
105
|
+
"""WARNING: does not modify underlying ``self.context``.
|
|
106
|
+
|
|
107
|
+
Must be done externally.
|
|
108
|
+
"""
|
|
109
|
+
if block_count < self.block_count:
|
|
110
|
+
raise ValueError(
|
|
111
|
+
f"Supplied block_count='{block_count}' cannot be smaller than current block_count {self.block_count}"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
return lfs.fs_grow(self.fs, block_count)
|
|
115
|
+
|
|
116
|
+
def fs_stat(self) -> "LFSFSStat":
|
|
117
|
+
"""Get the status of the filesystem"""
|
|
118
|
+
return lfs.fs_stat(self.fs)
|
|
119
|
+
|
|
120
|
+
def fs_gc(self):
|
|
121
|
+
return lfs.fs_gc(self.fs)
|
|
122
|
+
|
|
123
|
+
def open(
|
|
124
|
+
self, fname: str, mode="r", buffering: int = -1, encoding: str = None, errors: str = None, newline: str = None
|
|
125
|
+
) -> IO:
|
|
126
|
+
"""Open a file.
|
|
127
|
+
|
|
128
|
+
:attr:`mode` is an optional string that specifies the mode in which
|
|
129
|
+
the file is opened and is analogous to the built-in :func:`io.open`
|
|
130
|
+
function. Files opened in text mode (default) will take and return
|
|
131
|
+
`str` objects. Files opened in binary mode will take and return
|
|
132
|
+
byte-like objects.
|
|
133
|
+
|
|
134
|
+
Parameters
|
|
135
|
+
----------
|
|
136
|
+
fname : str
|
|
137
|
+
The path to the file to open.
|
|
138
|
+
mode : str
|
|
139
|
+
Specifies the mode in which the file is opened.
|
|
140
|
+
buffering : int
|
|
141
|
+
Specifies the buffering policy. Pass `0` to disable buffering in
|
|
142
|
+
binary mode.
|
|
143
|
+
encoding : str
|
|
144
|
+
Text encoding to use. (text mode only)
|
|
145
|
+
errors : str
|
|
146
|
+
Specifies how encoding and decoding errors are to be handled. (text mode only)
|
|
147
|
+
newline : str
|
|
148
|
+
Controls how universal newlines mode works. (text mode only)
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
# Parse mode
|
|
152
|
+
creating = False
|
|
153
|
+
reading = False
|
|
154
|
+
writing = False
|
|
155
|
+
appending = False
|
|
156
|
+
updating = False
|
|
157
|
+
|
|
158
|
+
binary = False
|
|
159
|
+
text = False
|
|
160
|
+
|
|
161
|
+
for ch in mode:
|
|
162
|
+
if ch == "x":
|
|
163
|
+
creating = True
|
|
164
|
+
elif ch == "r":
|
|
165
|
+
reading = True
|
|
166
|
+
elif ch == "w":
|
|
167
|
+
writing = True
|
|
168
|
+
elif ch == "a":
|
|
169
|
+
appending = True
|
|
170
|
+
elif ch == "+":
|
|
171
|
+
updating = True
|
|
172
|
+
elif ch == "b":
|
|
173
|
+
binary = True
|
|
174
|
+
elif ch == "t":
|
|
175
|
+
text = True
|
|
176
|
+
else:
|
|
177
|
+
raise ValueError(f"invalid mode: '{mode}'")
|
|
178
|
+
|
|
179
|
+
if text and binary:
|
|
180
|
+
raise ValueError("can't have text and binary mode at once")
|
|
181
|
+
|
|
182
|
+
exclusive_modes = (creating, reading, writing, appending)
|
|
183
|
+
|
|
184
|
+
if sum(int(m) for m in exclusive_modes) > 1:
|
|
185
|
+
raise ValueError("must have exactly one of create/read/write/append mode")
|
|
186
|
+
|
|
187
|
+
if binary:
|
|
188
|
+
if encoding is not None:
|
|
189
|
+
raise ValueError("binary mode doesn't take an encoding argument")
|
|
190
|
+
|
|
191
|
+
if errors is not None:
|
|
192
|
+
raise ValueError("binary mode doesn't take an errors argument")
|
|
193
|
+
|
|
194
|
+
if newline is not None:
|
|
195
|
+
raise ValueError("binary mode doesn't take a newline argument")
|
|
196
|
+
|
|
197
|
+
if buffering == 1:
|
|
198
|
+
msg = (
|
|
199
|
+
"line buffering (buffering=1) isn't supported in "
|
|
200
|
+
"binary mode, the default buffer size will be used"
|
|
201
|
+
)
|
|
202
|
+
warnings.warn(msg, RuntimeWarning)
|
|
203
|
+
buffering = -1
|
|
204
|
+
|
|
205
|
+
try:
|
|
206
|
+
fh = lfs.file_open(self.fs, fname, mode)
|
|
207
|
+
except LittleFSError as e:
|
|
208
|
+
# Try to map to standard Python exceptions
|
|
209
|
+
if e.code == LittleFSError.Error.LFS_ERR_NOENT:
|
|
210
|
+
raise FileNotFoundError from e
|
|
211
|
+
elif e.code == LittleFSError.Error.LFS_ERR_ISDIR:
|
|
212
|
+
raise IsADirectoryError from e
|
|
213
|
+
elif e.code == LittleFSError.Error.LFS_ERR_EXIST:
|
|
214
|
+
raise FileExistsError from e
|
|
215
|
+
else:
|
|
216
|
+
raise e
|
|
217
|
+
|
|
218
|
+
raw = FileHandle(self.fs, fh)
|
|
219
|
+
|
|
220
|
+
line_buffering = False
|
|
221
|
+
|
|
222
|
+
if buffering == 1:
|
|
223
|
+
buffering = -1
|
|
224
|
+
line_buffering = True
|
|
225
|
+
|
|
226
|
+
if buffering < 0:
|
|
227
|
+
buffering = self.cfg.cache_size
|
|
228
|
+
|
|
229
|
+
if buffering == 0:
|
|
230
|
+
if not binary:
|
|
231
|
+
raise ValueError("can't have unbuffered text I/O")
|
|
232
|
+
|
|
233
|
+
return raw
|
|
234
|
+
|
|
235
|
+
if updating:
|
|
236
|
+
buffered = io.BufferedRandom(raw, buffering)
|
|
237
|
+
elif creating or writing or appending:
|
|
238
|
+
buffered = io.BufferedWriter(raw, buffering)
|
|
239
|
+
elif reading:
|
|
240
|
+
buffered = io.BufferedReader(raw, buffering)
|
|
241
|
+
else:
|
|
242
|
+
raise ValueError(f"Unknown mode: '{mode}'")
|
|
243
|
+
|
|
244
|
+
if binary:
|
|
245
|
+
return buffered
|
|
246
|
+
|
|
247
|
+
wrapped = io.TextIOWrapper(buffered, encoding, errors, newline, line_buffering)
|
|
248
|
+
|
|
249
|
+
return wrapped
|
|
250
|
+
|
|
251
|
+
def getattr(self, path: str, typ: Union[str, bytes, int]) -> bytes:
|
|
252
|
+
typ = _typ_to_uint8(typ)
|
|
253
|
+
return lfs.getattr(self.fs, path, typ)
|
|
254
|
+
|
|
255
|
+
def setattr(self, path: str, typ: Union[str, bytes, int], data: bytes) -> None:
|
|
256
|
+
typ = _typ_to_uint8(typ)
|
|
257
|
+
lfs.setattr(self.fs, path, typ, data)
|
|
258
|
+
|
|
259
|
+
def removeattr(self, path: str, typ: Union[str, bytes, int]) -> None:
|
|
260
|
+
typ = _typ_to_uint8(typ)
|
|
261
|
+
lfs.removeattr(self.fs, path, typ)
|
|
262
|
+
|
|
263
|
+
def listdir(self, path=".") -> List[str]:
|
|
264
|
+
"""List directory content
|
|
265
|
+
|
|
266
|
+
List the content of a directory. This function uses :meth:`scandir`
|
|
267
|
+
internally. Using :meth:`scandir` might be better if you are
|
|
268
|
+
searching for a specific file or need access to the :class:`littlefs.lfs.LFSStat`
|
|
269
|
+
of the files.
|
|
270
|
+
"""
|
|
271
|
+
return [st.name for st in self.scandir(path)]
|
|
272
|
+
|
|
273
|
+
def mkdir(self, path: str) -> int:
|
|
274
|
+
"""Create a new directory"""
|
|
275
|
+
try:
|
|
276
|
+
return lfs.mkdir(self.fs, path)
|
|
277
|
+
except errors.LittleFSError as e:
|
|
278
|
+
if e.code == LittleFSError.Error.LFS_ERR_EXIST:
|
|
279
|
+
msg = "[LittleFSError {:d}] Cannot create a file when that file already exists: '{:s}'.".format(
|
|
280
|
+
e.code, path
|
|
281
|
+
)
|
|
282
|
+
raise FileExistsError(msg) from e
|
|
283
|
+
raise
|
|
284
|
+
|
|
285
|
+
def makedirs(self, name: str, exist_ok=False):
|
|
286
|
+
"""Recursive directory creation function."""
|
|
287
|
+
parts = [p for p in name.split("/") if p]
|
|
288
|
+
current_name = ""
|
|
289
|
+
for nr, part in enumerate(parts):
|
|
290
|
+
current_name += "/%s" % part
|
|
291
|
+
try:
|
|
292
|
+
self.mkdir(current_name)
|
|
293
|
+
except FileExistsError as e:
|
|
294
|
+
is_last = nr == len(parts) - 1
|
|
295
|
+
if (not is_last) or (is_last and exist_ok):
|
|
296
|
+
continue
|
|
297
|
+
raise e
|
|
298
|
+
|
|
299
|
+
def remove(self, path: str, recursive: bool = False) -> None:
|
|
300
|
+
"""Remove a file or directory
|
|
301
|
+
|
|
302
|
+
If the path to remove is a directory, the directory must be empty.
|
|
303
|
+
|
|
304
|
+
Parameters
|
|
305
|
+
----------
|
|
306
|
+
path : str
|
|
307
|
+
The path to the file or directory to remove.
|
|
308
|
+
recursive: bool
|
|
309
|
+
If ``true`` and ``path`` is a directory, recursively remove all children files/folders.
|
|
310
|
+
"""
|
|
311
|
+
try:
|
|
312
|
+
lfs.remove(self.fs, path)
|
|
313
|
+
return
|
|
314
|
+
except errors.LittleFSError as e:
|
|
315
|
+
if e.code == LittleFSError.Error.LFS_ERR_NOENT:
|
|
316
|
+
msg = "[LittleFSError {:d}] No such file or directory: '{:s}'.".format(e.code, path)
|
|
317
|
+
raise FileNotFoundError(msg) from e
|
|
318
|
+
elif e.code == LittleFSError.Error.LFS_ERR_NOTEMPTY and recursive:
|
|
319
|
+
# We want to recursively delete the ``path`` directory.
|
|
320
|
+
# Handled below to reduce amount of logic in ``except`` handler.
|
|
321
|
+
pass
|
|
322
|
+
else:
|
|
323
|
+
raise e
|
|
324
|
+
|
|
325
|
+
# Recursively delete the ``path`` directory
|
|
326
|
+
for elem in self.scandir(path):
|
|
327
|
+
self.remove(path + "/" + elem.name, recursive=True)
|
|
328
|
+
lfs.remove(self.fs, path)
|
|
329
|
+
|
|
330
|
+
def removedirs(self, name):
|
|
331
|
+
"""Remove directories recursively
|
|
332
|
+
|
|
333
|
+
This works like :func:`remove` but if the leaf directory
|
|
334
|
+
is empty after the successful removal of :attr:`name`, the
|
|
335
|
+
function tries to recursively remove all parent directories
|
|
336
|
+
which are also empty.
|
|
337
|
+
"""
|
|
338
|
+
parts = name.split("/")
|
|
339
|
+
while parts:
|
|
340
|
+
try:
|
|
341
|
+
name = "/".join(parts)
|
|
342
|
+
if not name:
|
|
343
|
+
break
|
|
344
|
+
self.remove("/".join(parts))
|
|
345
|
+
except errors.LittleFSError as e:
|
|
346
|
+
if e.code == LittleFSError.Error.LFS_ERR_NOTEMPTY:
|
|
347
|
+
break
|
|
348
|
+
raise e
|
|
349
|
+
parts.pop()
|
|
350
|
+
|
|
351
|
+
def rename(self, src: str, dst: str) -> int:
|
|
352
|
+
"""Rename a file or directory"""
|
|
353
|
+
return lfs.rename(self.fs, src, dst)
|
|
354
|
+
|
|
355
|
+
def rmdir(self, path: str) -> int:
|
|
356
|
+
"""Remove a directory
|
|
357
|
+
|
|
358
|
+
This function is an alias for :func:`remove`
|
|
359
|
+
"""
|
|
360
|
+
return self.remove(path)
|
|
361
|
+
|
|
362
|
+
def scandir(self, path=".") -> Iterator["LFSStat"]:
|
|
363
|
+
"""List directory content"""
|
|
364
|
+
dh = lfs.dir_open(self.fs, path)
|
|
365
|
+
info = lfs.dir_read(self.fs, dh)
|
|
366
|
+
while info:
|
|
367
|
+
if info.name not in [".", ".."]:
|
|
368
|
+
yield info
|
|
369
|
+
info = lfs.dir_read(self.fs, dh)
|
|
370
|
+
lfs.dir_close(self.fs, dh)
|
|
371
|
+
|
|
372
|
+
def stat(self, path: str) -> "LFSStat":
|
|
373
|
+
"""Get the status of a file or directory"""
|
|
374
|
+
return lfs.stat(self.fs, path)
|
|
375
|
+
|
|
376
|
+
def unlink(self, path: str) -> int:
|
|
377
|
+
"""Remove a file or directory
|
|
378
|
+
|
|
379
|
+
This function is an alias for :func:`remove`.
|
|
380
|
+
"""
|
|
381
|
+
return self.remove(path)
|
|
382
|
+
|
|
383
|
+
def walk(self, top: str) -> Iterator[Tuple[str, List[str], List[str]]]:
|
|
384
|
+
"""Generate the file names in a directory tree
|
|
385
|
+
|
|
386
|
+
Generate the file and directory names in a directory tree by
|
|
387
|
+
walking the tree top-down. This functions closely resembels the
|
|
388
|
+
behaviour of :func:`os.walk`.
|
|
389
|
+
|
|
390
|
+
Each iteration yields a tuple containing three elements:
|
|
391
|
+
|
|
392
|
+
- The root of the currently processed element
|
|
393
|
+
- A list of directories located in the root
|
|
394
|
+
- A list of filenames located in the root
|
|
395
|
+
"""
|
|
396
|
+
files, dirs = [], []
|
|
397
|
+
for elem in self.scandir(top):
|
|
398
|
+
if elem.type == 1:
|
|
399
|
+
files.append(elem.name)
|
|
400
|
+
elif elem.type == 2:
|
|
401
|
+
dirs.append(elem.name)
|
|
402
|
+
|
|
403
|
+
yield top, dirs, files
|
|
404
|
+
for dirname in dirs:
|
|
405
|
+
newtop = "/".join((top, dirname)).replace("//", "/")
|
|
406
|
+
yield from self.walk(newtop)
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
class FileHandle(io.RawIOBase):
|
|
410
|
+
def __init__(self, fs, fh):
|
|
411
|
+
super().__init__()
|
|
412
|
+
|
|
413
|
+
self.fs = fs
|
|
414
|
+
self.fh = fh
|
|
415
|
+
|
|
416
|
+
def close(self):
|
|
417
|
+
# Base implementation is not used to avoid extra call to flush().
|
|
418
|
+
# LittleFS already flushes the file on close.
|
|
419
|
+
|
|
420
|
+
if not self.closed:
|
|
421
|
+
lfs.file_close(self.fs, self.fh)
|
|
422
|
+
setattr(self, "__IOBase_closed", True)
|
|
423
|
+
|
|
424
|
+
def readable(self):
|
|
425
|
+
return lfs.LFSFileFlag.rdonly in self.fh.flags and not self.closed
|
|
426
|
+
|
|
427
|
+
def writable(self):
|
|
428
|
+
return lfs.LFSFileFlag.wronly in self.fh.flags and not self.closed
|
|
429
|
+
|
|
430
|
+
def seekable(self):
|
|
431
|
+
self._checkClosed()
|
|
432
|
+
return True
|
|
433
|
+
|
|
434
|
+
def seek(self, offset, whence=io.SEEK_SET):
|
|
435
|
+
# Whence constants are reused from the io module. The constants have
|
|
436
|
+
# the same as LFS_SEEK_SET / LFS_SEEK_CUR / LFS_SEEK_END.
|
|
437
|
+
self._checkClosed()
|
|
438
|
+
return lfs.file_seek(self.fs, self.fh, offset, whence)
|
|
439
|
+
|
|
440
|
+
def tell(self):
|
|
441
|
+
self._checkClosed()
|
|
442
|
+
return lfs.file_tell(self.fs, self.fh)
|
|
443
|
+
|
|
444
|
+
def truncate(self, size=None) -> int:
|
|
445
|
+
self._checkClosed()
|
|
446
|
+
|
|
447
|
+
pos = self.tell()
|
|
448
|
+
ret = lfs.file_truncate(self.fs, self.fh, pos)
|
|
449
|
+
|
|
450
|
+
return ret
|
|
451
|
+
|
|
452
|
+
def write(self, data):
|
|
453
|
+
self._checkClosed()
|
|
454
|
+
self._checkWritable()
|
|
455
|
+
|
|
456
|
+
return lfs.file_write(self.fs, self.fh, bytes(data))
|
|
457
|
+
|
|
458
|
+
def readinto(self, b):
|
|
459
|
+
self._checkClosed()
|
|
460
|
+
self._checkReadable()
|
|
461
|
+
|
|
462
|
+
req_len = len(b)
|
|
463
|
+
result = lfs.file_read(self.fs, self.fh, req_len)
|
|
464
|
+
res_len = len(result)
|
|
465
|
+
|
|
466
|
+
b[0:res_len] = result
|
|
467
|
+
|
|
468
|
+
return res_len
|
|
469
|
+
|
|
470
|
+
def readall(self):
|
|
471
|
+
self._checkClosed()
|
|
472
|
+
self._checkReadable()
|
|
473
|
+
|
|
474
|
+
file_size = lfs.file_size(self.fs, self.fh)
|
|
475
|
+
file_pos = self.tell()
|
|
476
|
+
size = file_size - file_pos
|
|
477
|
+
|
|
478
|
+
return lfs.file_read(self.fs, self.fh, size)
|
|
479
|
+
|
|
480
|
+
def flush(self):
|
|
481
|
+
super().flush()
|
|
482
|
+
lfs.file_sync(self.fs, self.fh)
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def _typ_to_uint8(typ):
|
|
486
|
+
try:
|
|
487
|
+
out = ord(typ)
|
|
488
|
+
except TypeError:
|
|
489
|
+
out = int(typ)
|
|
490
|
+
|
|
491
|
+
if not (0 <= out <= 255):
|
|
492
|
+
raise ValueError(f"type must be in range [0, 255]")
|
|
493
|
+
|
|
494
|
+
return out
|