littlefs for Python¶
littlefs-python provides a thin wrapper around littlefs, a filesystem targeted for small embedded systems. The wrapper provides a pythonic interface to the filesystem and allows the creation, inspection and modification of the filesystem or individual files. Even if this package uses Cython, the goal is not to provide a high performance implementation. Cython was chosen as an easy method is offered to generate the binding and the littlefs library in one step.
Quick Examples¶
Let’s create a image ready to transfer to a flash memory using the pythonic interface:
from littlefs import LittleFS
# Initialize the File System according to your specifications
fs = LittleFS(block_size=512, block_count=256)
# Open a file and write some content
with fs.open('first-file.txt', 'w') as fh:
fh.write(b'Some text to begin with\n')
# Dump the filesystem content to a file
with open('FlashMemory.bin', 'wb') as fh:
fh.write(fs.context.buffer)
The same can be done by using the more verbose C-Style API, which closely resembels the steps which must be performed in C:
from littlefs import lfs
cfg = lfs.LFSConfig(block_size=512, block_count=256)
fs = lfs.LFSFilesystem()
# Format and mount the filesystem
lfs.format(fs, cfg)
lfs.mount(fs, cfg)
# Open a file and write some content
fh = lfs.file_open(fs, 'first-file.txt', 'w') as fh:
lfs.file_write(fs, fh, b'Some text to begin with\n')
lfs.file_close(fs, fh)
# Dump the filesystem content to a file
with open('FlashMemory.bin', 'wb') as fh:
fh.write(cfg.user_context.buffer)
Installation¶
This is as simple as it can be:
pip install littlefs-python
At the moment wheels (which require no build) are provided for the following platforms, on other platforms the source package is provided:
- Linux: Python 3.5, 3.6, 3.7 & 3.8 / 32- & 64-bit
- Windows: Python 3.5, 3.6 & 3.7 / 32- & 64-bit
Development Setup¶
Start by checking out the source repository of littlefs-python:
git clone https://github.com/jrast/littlefs-python.git
The source code for littlefs is included as a submodule which must be checked out after the clone:
cd <littlefs-python>
git submodule update --init
this ensures that the correct version of littlefs is cloned into the littlefs folder. As a next step install the dependencies and install the package:
pip install -r requirements.txt
pip install -e .
Note
It’s highly recommended to install the package in a virtual environment!
Contents¶
Usage¶
littlefs-python offers two interfaces to the underlying littlefs library:
- A C-Style API which exposes all functions from the library using a minimal wrapper, written in Cython, to access the functions.
- A pythonic high-level API which offers convenient functions similiar to
the ones known from the
os
standard library module.
Both API’s can be mixed and matched if required.
C-Style API¶
The C-Style API tries to map functions from the C library to python with as little
intermediate logic as possible. The possibility to provide customized read()
,
prog()
, erase()
and sync()
functions to littlefs was a main goal
for the api.
All methods and relevant classes for this API are available in the littlefs.lfs
module. The methods where named the same as in the littlfs library, leaving out the lfs_
prefix. Because direct access to C structs is not possible from python, wrapper classes
are provided for the commonly used structs:
LFSFilesystem
is a wrapper around thelfs_t
struct.LFSFile
is a wrapper around thelfs_file_t
struct.LFSDirectory
is a wrapper around thelfs_dir_t
struct.LFSConfig
is a wrapper around thelfs_config_t
struct.
All these wrappers have a _impl
attribute which contains the actual data. Note that
this attribute is not accessible from python.
The LFSConfig
class exposes most of the internal fields from the
_impl
as properties to provide read access to the configuration.
Examples¶
Preparing a filesystem on the PC¶
In the following example shows how to prepare an image of a Flash / EEPROM memory. The generated image can then be written to the memory by other tools.
Start by creating a new filesystem:
>>> from littlefs import LittleFS
>>> fs = LittleFS(block_size=256, block_count=64)
It’s important to set the correct block_size
and block_count
during the
instantiation of the filesystem. The values you set here must match the settings which are
later used on the embedded system. The filesystem is automatically formatted and mounted [1]
during instantiation. For example, if we look at the first few bytes of the underlying buffer,
we can see that the filesystem header was written:
>>> fs.context.buffer[:20]
bytearray(b'\x01\x00\x00\x00\xf0\x0f\xff\xf7littlefs/\xe0\x00\x10')
We can start right away by creating some files. Lets create a simple file containing some Information about the hardware [2]:
>>> with fs.open('hardware.txt', 'w') as fh:
... fh.write(b'BoardVersion:1234\n')
... fh.write(b'BoardSerial:001122\n')
18
19
Note that all files are opened in binary mode, therefore you must pass bytes
to
the write function. File- and foldernames are encoded as ASCII.
File handles of littlefs can be used as normal file handles, using a context manager
ensures that the file is closed as soon as the with
block is left.
Let’s create some more files in a configuration folder:
>>> fs.mkdir('/config')
0
>>> with fs.open('config/sensor', 'w') as fh:
... fh.write(bytearray([0x01, 0x02, 0x05]))
3
>>> with fs.open('config/actor', 'w') as fh:
... fh.write(bytearray([0xAA, 0xBB] * 100))
200
As we wan’t to place the files in a folder, the folder first needs to be created.
The filesystem does not know the concept of a working directory. The working directory
is allways assumed to be the root directory, therefore ./config
, /config
and
config
have all the same meaning, use whatever you like the best.
A final check to see if all required files are on the filesystem before we dump the data to a file:
>>> fs.listdir('/')
['config', 'hardware.txt']
>>> fs.listdir('/config')
['actor', 'sensor']
Everything ok? Ok, lets go and dump the filesystem to a binary file. This file can be written/downloaded to the actual storage.
>>> with open('fs.bin', 'wb') as fh:
... fh.write(fs.context.buffer)
16384
Inspecting a filesystem image¶
Sometimes it’s necesary to inspect a filesystem which was previously in use on a embedded system. Once the filesystem is available as an binary image, it’s easy to inspect the content using littlefs-python.
In this example we will inspect the image created in the last example. We check if
the actor file is still the same as when the image was written.
We start again by creating a LittleFS
instance. However, this
time we do not want to mount the filesystem immediateley because we need to load
the actual data into the buffer first.
After the buffer is initialized with the correct data, we can mount the filesystem.
>>> fs = LittleFS(block_size=256, block_count=64, mount=False)
>>> with open('fs.bin', 'rb') as fh:
... fs.context.buffer = bytearray(fh.read())
>>> fs.mount()
0
Let’s see whats on the filesystem:
>>> fs.listdir('/config')
['actor', 'sensor']
Ok, this seems to be fine. Let’s check if the actor file was modified:
>>> with fs.open('/config/actor', 'r') as fh:
... data = fh.read(200)
>>> assert data == bytearray([0xAA, 0xBB] * 100)
Great, our memory contains the correct data!
Now it’s up to you! Play around with the data, try writing and reading other files,
create directories or play around with differnt block_size
and block_count
arguments.
[1] | See littlefs.lfs.format() and littlefs.lfs.mount() for further details. |
[2] | Ignore the output of the examples. These are the return values in which we are not interested in almost all cases. |
API Documentation¶
littlefs module¶
-
class
littlefs.
LittleFS
(context=None, **kwargs)¶ Bases:
object
Littlefs file system
-
context
¶
-
format
()¶ Format the underlying buffer
-
listdir
(path='.')¶ List directory content
-
makedirs
(name, exist_ok=False)¶
-
mkdir
(path)¶ Create a new directory
-
mount
()¶ Mount the underlying buffer
-
open
(fname, mode='r')¶ Open a file
-
remove
(path)¶ Remove a file or directory
If the path to remove is a directory, the directory must be empty.
Parameters: path (str) – The path to the file or directory to remove.
-
removedirs
(name)¶ Remove directories recursively
This works like
remove()
but if the leaf directory is empty after the successfull removal ofname
, the function tries to recursively remove all parent directories which are also empty.
-
rename
(src, dst)¶
-
replace
(src, dst)¶
-
rmdir
(path)¶
-
scandir
(path)¶
-
stat
(path)¶
-
sync
()¶
-
truncate
(path, length)¶
-
walk
(top)¶
-
littlefs.context module¶
littlefs.lfs module¶
-
class
littlefs.lfs.
LFSConfig
(context=None, **kwargs)¶ -
block_count
¶
-
block_size
¶
-
cache_size
¶
-
lookahead_size
¶
-
prog_size
¶
-
read_size
¶
-
-
class
littlefs.lfs.
LFSDirectory
¶
-
class
littlefs.lfs.
LFSFile
¶
-
class
littlefs.lfs.
LFSFilesystem
¶
-
littlefs.lfs.
dir_close
(LFSFilesystem fs, LFSDirectory dh)¶
-
littlefs.lfs.
dir_open
(LFSFilesystem fs, path)¶
-
littlefs.lfs.
dir_read
(LFSFilesystem fs, LFSDirectory dh)¶
-
littlefs.lfs.
dir_rewind
(LFSFilesystem fs, LFSDirectory dh)¶
-
littlefs.lfs.
dir_tell
(LFSFilesystem fs, LFSDirectory dh)¶
-
littlefs.lfs.
file_close
(LFSFilesystem fs, LFSFile fh)¶
-
littlefs.lfs.
file_open
(LFSFilesystem fs, path, flags)¶
-
littlefs.lfs.
file_open_cfg
(self, path, flags, config)¶
-
littlefs.lfs.
file_read
(LFSFilesystem fs, LFSFile fh, size)¶
-
littlefs.lfs.
file_rewind
(LFSFilesystem fs, LFSFile fh)¶
-
littlefs.lfs.
file_seek
(LFSFilesystem fs, LFSFile fh, off, whence)¶
-
littlefs.lfs.
file_size
(LFSFilesystem fs, LFSFile fh)¶
-
littlefs.lfs.
file_sync
(LFSFilesystem fs, LFSFile fh)¶
-
littlefs.lfs.
file_tell
(LFSFilesystem fs, LFSFile fh)¶
-
littlefs.lfs.
file_truncate
(LFSFilesystem fs, LFSFile fh, size)¶
-
littlefs.lfs.
file_write
(LFSFilesystem fs, LFSFile fh, data)¶
-
littlefs.lfs.
format
(LFSFilesystem fs, LFSConfig cfg)¶ Format the filesystem
-
littlefs.lfs.
fs_size
(LFSFilesystem fs)¶
-
littlefs.lfs.
getattr
(LFSFilesystem fs, path, type, buffer, size)¶
-
littlefs.lfs.
mkdir
(LFSFilesystem fs, path)¶
-
littlefs.lfs.
mount
(LFSFilesystem fs, LFSConfig cfg)¶ Mount the filesystem
-
littlefs.lfs.
remove
(LFSFilesystem fs, path)¶ Remove a file or directory
If removing a direcotry, the directory must be empty.
-
littlefs.lfs.
removeattr
(LFSFilesystem fs, path, type)¶
-
littlefs.lfs.
rename
(LFSFilesystem fs, oldpath, newpath)¶ Rename or move a file or directory
If the destination exists, it must match the source in type. If the destination is a directory, the directory must be empty.
-
littlefs.lfs.
setattr
(LFSFilesystem fs, path, type, buffer, size)¶
-
littlefs.lfs.
stat
(LFSFilesystem fs, path)¶ Find info about a file or directory
-
littlefs.lfs.
unmount
(LFSFilesystem fs)¶ Unmount the filesystem
This does nothing beside releasing any allocated resources