littlefs-python 0.16.0__tar.gz → 0.17.1__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.
Files changed (138) hide show
  1. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/.github/workflows/deploy.yml +11 -10
  2. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/.github/workflows/run-tests.yml +2 -2
  3. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/PKG-INFO +1 -1
  4. littlefs_python-0.17.1/requirements.txt +3 -0
  5. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/src/littlefs/__main__.py +63 -61
  6. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/src/littlefs/context.py +12 -4
  7. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/src/littlefs/lfs.c +1667 -1441
  8. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/src/littlefs/lfs.pxd +1 -0
  9. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/src/littlefs/lfs.pyi +5 -0
  10. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/src/littlefs/lfs.pyx +8 -0
  11. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/src/littlefs/repl.py +5 -1
  12. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/src/littlefs_python.egg-info/PKG-INFO +1 -1
  13. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/src/littlefs_python.egg-info/SOURCES.txt +4 -0
  14. littlefs_python-0.17.1/test/cli/test_create_and_extract.py +101 -0
  15. littlefs_python-0.17.1/test/cli/test_create_and_repl.py +99 -0
  16. littlefs_python-0.17.1/test/cli/test_walk_all.py +60 -0
  17. littlefs_python-0.17.1/test/test_windisk_context.py +60 -0
  18. littlefs_python-0.16.0/requirements.txt +0 -2
  19. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/.gitattributes +0 -0
  20. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/.github/dependabot.yml +0 -0
  21. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/.gitignore +0 -0
  22. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/.gitmodules +0 -0
  23. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/.pre-commit-config.yaml +0 -0
  24. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/.readthedocs.yml +0 -0
  25. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/LICENSE +0 -0
  26. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/MANIFEST.in +0 -0
  27. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/README.rst +0 -0
  28. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/ci/build-wheels.sh +0 -0
  29. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/ci/download_release_files.py +0 -0
  30. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/docs/Makefile +0 -0
  31. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/docs/api/index.rst +0 -0
  32. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/docs/cli.rst +0 -0
  33. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/docs/conf.py +0 -0
  34. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/docs/doc8.ini +0 -0
  35. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/docs/examples/index.rst +0 -0
  36. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/docs/index.rst +0 -0
  37. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/docs/make.bat +0 -0
  38. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/docs/requirements.txt +0 -0
  39. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/docs/usage.rst +0 -0
  40. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/examples/mkfsimg.py +0 -0
  41. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/examples/walk.py +0 -0
  42. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/.git +0 -0
  43. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/.gitattributes +0 -0
  44. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/.github/workflows/post-release.yml +0 -0
  45. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/.github/workflows/release.yml +0 -0
  46. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/.github/workflows/status.yml +0 -0
  47. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/.github/workflows/test.yml +0 -0
  48. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/.gitignore +0 -0
  49. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/DESIGN.md +0 -0
  50. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/LICENSE.md +0 -0
  51. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/Makefile +0 -0
  52. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/README.md +0 -0
  53. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/SPEC.md +0 -0
  54. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/bd/lfs_emubd.c +0 -0
  55. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/bd/lfs_emubd.h +0 -0
  56. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/bd/lfs_filebd.c +0 -0
  57. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/bd/lfs_filebd.h +0 -0
  58. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/bd/lfs_rambd.c +0 -0
  59. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/bd/lfs_rambd.h +0 -0
  60. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/benches/bench_dir.toml +0 -0
  61. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/benches/bench_file.toml +0 -0
  62. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/benches/bench_superblock.toml +0 -0
  63. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/lfs.c +0 -0
  64. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/lfs.h +0 -0
  65. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/lfs_util.c +0 -0
  66. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/lfs_util.h +0 -0
  67. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/runners/bench_runner.c +0 -0
  68. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/runners/bench_runner.h +0 -0
  69. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/runners/test_runner.c +0 -0
  70. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/runners/test_runner.h +0 -0
  71. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/scripts/bench.py +0 -0
  72. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/scripts/changeprefix.py +0 -0
  73. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/scripts/code.py +0 -0
  74. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/scripts/cov.py +0 -0
  75. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/scripts/data.py +0 -0
  76. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/scripts/perf.py +0 -0
  77. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/scripts/perfbd.py +0 -0
  78. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/scripts/plot.py +0 -0
  79. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/scripts/plotmpl.py +0 -0
  80. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/scripts/prettyasserts.py +0 -0
  81. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/scripts/readblock.py +0 -0
  82. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/scripts/readmdir.py +0 -0
  83. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/scripts/readtree.py +0 -0
  84. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/scripts/stack.py +0 -0
  85. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/scripts/structs.py +0 -0
  86. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/scripts/summary.py +0 -0
  87. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/scripts/tailpipe.py +0 -0
  88. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/scripts/teepipe.py +0 -0
  89. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/scripts/test.py +0 -0
  90. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/scripts/tracebd.py +0 -0
  91. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/scripts/watch.py +0 -0
  92. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/tests/test_alloc.toml +0 -0
  93. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/tests/test_attrs.toml +0 -0
  94. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/tests/test_badblocks.toml +0 -0
  95. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/tests/test_bd.toml +0 -0
  96. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/tests/test_compat.toml +0 -0
  97. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/tests/test_dirs.toml +0 -0
  98. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/tests/test_entries.toml +0 -0
  99. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/tests/test_evil.toml +0 -0
  100. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/tests/test_exhaustion.toml +0 -0
  101. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/tests/test_files.toml +0 -0
  102. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/tests/test_interspersed.toml +0 -0
  103. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/tests/test_move.toml +0 -0
  104. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/tests/test_orphans.toml +0 -0
  105. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/tests/test_paths.toml +0 -0
  106. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/tests/test_powerloss.toml +0 -0
  107. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/tests/test_relocations.toml +0 -0
  108. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/tests/test_seek.toml +0 -0
  109. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/tests/test_shrink.toml +0 -0
  110. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/tests/test_superblocks.toml +0 -0
  111. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/littlefs/tests/test_truncate.toml +0 -0
  112. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/mypy.ini +0 -0
  113. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/pyproject.toml +0 -0
  114. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/setup.cfg +0 -0
  115. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/setup.py +0 -0
  116. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/src/littlefs/__init__.py +0 -0
  117. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/src/littlefs/errors.py +0 -0
  118. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/src/littlefs/py.typed +0 -0
  119. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/src/littlefs_python.egg-info/dependency_links.txt +0 -0
  120. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/src/littlefs_python.egg-info/entry_points.txt +0 -0
  121. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/src/littlefs_python.egg-info/not-zip-safe +0 -0
  122. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/src/littlefs_python.egg-info/requires.txt +0 -0
  123. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/src/littlefs_python.egg-info/top_level.txt +0 -0
  124. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/test/lfs/conftest.py +0 -0
  125. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/test/lfs/test_dir_functions.py +0 -0
  126. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/test/lfs/test_file_functions.py +0 -0
  127. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/test/lfs/test_fs_functions.py +0 -0
  128. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/test/test_attr.py +0 -0
  129. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/test/test_block_count.py +0 -0
  130. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/test/test_context.py +0 -0
  131. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/test/test_directories.py +0 -0
  132. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/test/test_files.py +0 -0
  133. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/test/test_grow.py +0 -0
  134. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/test/test_multiversion.py +0 -0
  135. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/test/test_name_max.py +0 -0
  136. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/test/test_remove_rename.py +0 -0
  137. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/test/test_version.py +0 -0
  138. {littlefs_python-0.16.0 → littlefs_python-0.17.1}/test/test_walk.py +0 -0
@@ -15,7 +15,7 @@ jobs:
15
15
  runs-on: ${{ matrix.os }}
16
16
  strategy:
17
17
  matrix:
18
- os: [ubuntu-22.04, macos-13, windows-latest]
18
+ os: [ubuntu-22.04, macos-15-intel, windows-latest]
19
19
 
20
20
  env:
21
21
  CIBW_BUILD: "cp38-* cp39-* cp310-* cp311-* cp312-* cp313-* cp314-*"
@@ -23,7 +23,7 @@ jobs:
23
23
  CIBW_ARCHS_LINUX: "x86_64 i686 aarch64"
24
24
 
25
25
  steps:
26
- - uses: actions/checkout@v5
26
+ - uses: actions/checkout@v6
27
27
  with:
28
28
  submodules: "recursive"
29
29
  fetch-depth: 0
@@ -35,9 +35,9 @@ jobs:
35
35
  platforms: all
36
36
 
37
37
  - name: Build wheels
38
- uses: pypa/cibuildwheel@v3.2.1
38
+ uses: pypa/cibuildwheel@v3.3.1
39
39
 
40
- - uses: actions/upload-artifact@v5
40
+ - uses: actions/upload-artifact@v6
41
41
  with:
42
42
  name: wheels-${{ matrix.os }}
43
43
  path: ./wheelhouse/*.whl
@@ -51,13 +51,13 @@ jobs:
51
51
  cibw_archs: ["arm64"]
52
52
 
53
53
  steps:
54
- - uses: actions/checkout@v5
54
+ - uses: actions/checkout@v6
55
55
  with:
56
56
  submodules: "recursive"
57
57
  fetch-depth: 0
58
58
 
59
59
  - name: Build wheels
60
- uses: pypa/cibuildwheel@v3.2.1
60
+ uses: pypa/cibuildwheel@v3.3.1
61
61
  env:
62
62
  CIBW_BUILD: ${{ matrix.cibw_build }}
63
63
  CIBW_SKIP: "pp*"
@@ -84,7 +84,7 @@ jobs:
84
84
  delocate-listdeps --all {dest_dir}/$WHEEL_SIMPLE_FILENAME
85
85
 
86
86
  echo "DONE."
87
- - uses: actions/upload-artifact@v5
87
+ - uses: actions/upload-artifact@v6
88
88
  with:
89
89
  name: wheels-macos-arm64
90
90
  path: ./wheelhouse/*.whl
@@ -93,7 +93,7 @@ jobs:
93
93
  name: Build source distribution
94
94
  runs-on: ubuntu-22.04
95
95
  steps:
96
- - uses: actions/checkout@v5
96
+ - uses: actions/checkout@v6
97
97
  with:
98
98
  submodules: "recursive"
99
99
  fetch-depth: 0
@@ -103,7 +103,7 @@ jobs:
103
103
  pip install build
104
104
  python -m build . --sdist
105
105
 
106
- - uses: actions/upload-artifact@v5
106
+ - uses: actions/upload-artifact@v6
107
107
  with:
108
108
  name: wheels-sdist
109
109
  path: dist/*.tar.gz
@@ -113,7 +113,7 @@ jobs:
113
113
  runs-on: ubuntu-22.04
114
114
  if: ${{ inputs.upload_to_pypi }}
115
115
  steps:
116
- - uses: actions/download-artifact@v6
116
+ - uses: actions/download-artifact@v7
117
117
  with:
118
118
  path: dist
119
119
  pattern: wheels-*
@@ -123,3 +123,4 @@ jobs:
123
123
  with:
124
124
  user: __token__
125
125
  password: ${{ secrets.pypi_api_token }}
126
+ skip-existing: true
@@ -14,11 +14,11 @@ jobs:
14
14
  runs-on: ${{ matrix.os }}
15
15
  strategy:
16
16
  matrix:
17
- os: [ubuntu-22.04, macos-13, windows-latest]
17
+ os: [ubuntu-22.04, macos-15, windows-latest]
18
18
  python-version: [3.8, 3.9, "3.10", "3.11", "3.12", "3.13", "3.14"]
19
19
 
20
20
  steps:
21
- - uses: actions/checkout@v5
21
+ - uses: actions/checkout@v6
22
22
  with:
23
23
  submodules: "recursive"
24
24
  - name: Set up Python ${{ matrix.python-version }}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: littlefs-python
3
- Version: 0.16.0
3
+ Version: 0.17.1
4
4
  Summary: A python wrapper for littlefs
5
5
  Home-page: https://github.com/jrast/littlefs-python
6
6
  Author: Jürg Rast
@@ -0,0 +1,3 @@
1
+ pytest>=4.0.0
2
+ tox>=3.14.0
3
+ pywin32; sys_platform == "win32"
@@ -1,5 +1,6 @@
1
1
  import argparse
2
2
  from contextlib import suppress
3
+ import os
3
4
  from pathlib import Path
4
5
  import sys
5
6
  import textwrap
@@ -7,7 +8,7 @@ import textwrap
7
8
  from littlefs import LittleFS, __version__
8
9
  from littlefs.errors import LittleFSError
9
10
  from littlefs.repl import LittleFSRepl
10
- from littlefs.context import UserContextFile
11
+ from littlefs.context import UserContextFile, UserContext
11
12
 
12
13
  # Dictionary mapping suffixes to their size in bytes
13
14
  _suffix_map = {
@@ -17,10 +18,12 @@ _suffix_map = {
17
18
  }
18
19
 
19
20
 
20
- def _fs_from_args(args: argparse.Namespace, mount=True) -> LittleFS:
21
+ def _fs_from_args(args: argparse.Namespace, block_count=None, mount=True, context: UserContext = None) -> LittleFS:
22
+ block_count = block_count if block_count is not None else getattr(args, "block_count", 0)
21
23
  return LittleFS(
24
+ context=context,
22
25
  block_size=args.block_size,
23
- block_count=getattr(args, "block_count", 0),
26
+ block_count=block_count,
24
27
  name_max=args.name_max,
25
28
  mount=mount,
26
29
  )
@@ -51,6 +54,16 @@ def size_parser(size_str):
51
54
  return int(size_str, base)
52
55
 
53
56
 
57
+ def _walk_all(root):
58
+ """Recursively yield all paths under root, following symlinks."""
59
+ for dirpath, dirnames, filenames in os.walk(root, followlinks=True):
60
+ dirpath = Path(dirpath)
61
+ for dirname in dirnames:
62
+ yield dirpath / dirname
63
+ for filename in filenames:
64
+ yield dirpath / filename
65
+
66
+
54
67
  def create(parser: argparse.ArgumentParser, args: argparse.Namespace) -> int:
55
68
  """Create LittleFS image from file/directory contents."""
56
69
  # fs_size OR block_count may be populated; make them consistent.
@@ -72,7 +85,7 @@ def create(parser: argparse.ArgumentParser, args: argparse.Namespace) -> int:
72
85
 
73
86
  source = Path(args.source).absolute()
74
87
  if source.is_dir():
75
- sources = source.rglob("*")
88
+ sources = list(_walk_all(source))
76
89
  root = source
77
90
  else:
78
91
  sources = [source]
@@ -94,21 +107,14 @@ def create(parser: argparse.ArgumentParser, args: argparse.Namespace) -> int:
94
107
  if args.compact:
95
108
  if args.verbose:
96
109
  print(f"Compacting... {fs.used_block_count} / {args.block_count}")
97
- compact_fs = LittleFS(
98
- block_size=args.block_size,
99
- block_count=fs.used_block_count,
100
- name_max=args.name_max,
101
- )
102
- for root, dirs, files in fs.walk("/"):
103
- if not root.endswith("/"):
104
- root += "/"
105
- for _dir in dirs:
106
- compact_fs.makedirs(root + _dir, exist_ok=True)
107
- for file in files:
108
- path = root + file
109
- print(path)
110
- with fs.open(path, "rb") as src, compact_fs.open(path, "wb") as dest:
111
- dest.write(src.read())
110
+ compact_fs = _fs_from_args(args, block_count=fs.used_block_count)
111
+ for path in sources:
112
+ rel_path = path.relative_to(root)
113
+ if path.is_dir():
114
+ compact_fs.mkdir(rel_path.as_posix())
115
+ else:
116
+ with compact_fs.open(rel_path.as_posix(), "wb") as dest:
117
+ dest.write(path.read_bytes())
112
118
  compact_fs.fs_grow(args.block_count)
113
119
  data = compact_fs.context.buffer
114
120
  if not args.no_pad:
@@ -121,21 +127,37 @@ def create(parser: argparse.ArgumentParser, args: argparse.Namespace) -> int:
121
127
  return 0
122
128
 
123
129
 
124
- def _list(parser: argparse.ArgumentParser, args: argparse.Namespace) -> int:
125
- """List LittleFS image contents."""
126
- fs = _fs_from_args(args, mount=False)
127
- fs.context.buffer = bytearray(args.source.read_bytes())
130
+ def _mount_from_context(parser: argparse.ArgumentParser, args: argparse.Namespace, context: UserContext) -> LittleFS:
131
+ # Block count is 0 because we don't know the size of the real image yet, the source file may be compacted (with the create --compact option).
132
+ fs = _fs_from_args(args, block_count=0, mount=False, context=context)
128
133
  fs.mount()
129
134
 
130
135
  if args.verbose:
131
- fs_size = len(fs.context.buffer)
136
+ input_image_size = context.in_size
137
+ actual_image_size = fs.block_count * args.block_size
132
138
  print("LittleFS Configuration:")
133
139
  print(f" Block Size: {args.block_size:9d} / 0x{args.block_size:X}")
134
- print(f" Image Size: {fs_size:9d} / 0x{fs_size:X}")
140
+ if input_image_size != actual_image_size:
141
+ print(f" Image Size: {actual_image_size:9d} / 0x{actual_image_size:X}")
142
+ print(f" Input Image Size (compacted): {input_image_size:9d} / 0x{input_image_size:X}")
143
+ else:
144
+ print(f" Image Size: {input_image_size:9d} / 0x{input_image_size:X}")
135
145
  print(f" Block Count: {fs.block_count:9d}")
136
146
  print(f" Name Max: {args.name_max:9d}")
137
147
  print(f" Image: {args.source}")
138
148
 
149
+ return fs
150
+
151
+
152
+ def _list(parser: argparse.ArgumentParser, args: argparse.Namespace) -> int:
153
+ """List LittleFS image contents."""
154
+ source: Path = args.source
155
+ if not source.is_file():
156
+ parser.error(f"Source image '{source}' does not exist.")
157
+ context = UserContext(buffer=bytearray(source.read_bytes()))
158
+
159
+ fs = _mount_from_context(parser, args, context)
160
+
139
161
  for root, dirs, files in fs.walk("/"):
140
162
  if not root.endswith("/"):
141
163
  root += "/"
@@ -148,18 +170,12 @@ def _list(parser: argparse.ArgumentParser, args: argparse.Namespace) -> int:
148
170
 
149
171
  def extract(parser: argparse.ArgumentParser, args: argparse.Namespace) -> int:
150
172
  """Extract LittleFS image contents to a directory."""
151
- fs = _fs_from_args(args, mount=False)
152
- fs.context.buffer = bytearray(args.source.read_bytes())
153
- fs.mount()
173
+ source: Path = args.source
174
+ if not source.is_file():
175
+ parser.error(f"Source image '{source}' does not exist.")
176
+ context = UserContext(buffer=bytearray(source.read_bytes()))
154
177
 
155
- if args.verbose:
156
- fs_size = len(fs.context.buffer)
157
- print("LittleFS Configuration:")
158
- print(f" Block Size: {args.block_size:9d} / 0x{args.block_size:X}")
159
- print(f" Image Size: {fs_size:9d} / 0x{fs_size:X}")
160
- print(f" Block Count: {fs.block_count:9d}")
161
- print(f" Name Max: {args.name_max:9d}")
162
- print(f" Image: {args.source}")
178
+ fs = _mount_from_context(parser, args, context)
163
179
 
164
180
  root_dest = args.destination.absolute()
165
181
  if not root_dest.exists():
@@ -192,36 +208,19 @@ def extract(parser: argparse.ArgumentParser, args: argparse.Namespace) -> int:
192
208
 
193
209
  def repl(parser: argparse.ArgumentParser, args: argparse.Namespace) -> int:
194
210
  """Inspect an existing LittleFS image through an interactive shell."""
195
-
196
211
  source: Path = args.source
197
212
  if not source.is_file():
198
213
  parser.error(f"Source image '{source}' does not exist.")
214
+ context = UserContextFile(str(source)) # In repl we want context to be the file itself, so commands will change it
199
215
 
200
- image_size = source.stat().st_size
201
- if not image_size or image_size % args.block_size:
202
- parser.error(
203
- f"Image size ({image_size} bytes) is not a multiple of the supplied block size ({args.block_size})."
204
- )
205
-
206
- block_count = image_size // args.block_size
207
- if block_count == 0:
208
- parser.error("Image is smaller than a single block; cannot mount.")
209
-
210
- context = UserContextFile(str(source))
211
- fs = LittleFS(
212
- context=context,
213
- block_size=args.block_size,
214
- block_count=block_count,
215
- name_max=args.name_max,
216
- mount=False,
217
- )
218
-
219
- shell = LittleFSRepl(fs)
220
216
  try:
221
217
  try:
222
- shell.do_mount()
218
+ fs = _mount_from_context(parser, args, context)
223
219
  except LittleFSError as exc:
224
220
  parser.error(f"Failed to mount '{source}': {exc}")
221
+
222
+ shell = LittleFSRepl(fs)
223
+
225
224
  shell.cmdloop()
226
225
  finally:
227
226
  if shell._mounted:
@@ -358,10 +357,13 @@ def get_parser():
358
357
  return parser
359
358
 
360
359
 
361
- def main():
360
+ # Getting argv optionally from the caller to enable call from python (generally for testing, but could be used for other purposes)
361
+ def main(argv=None):
362
+ if argv is None:
363
+ argv = sys.argv
362
364
  parser = get_parser()
363
- parser.parse_known_args(sys.argv[1:]) # Allows for ``littlefs-python --version``
364
- args = parser.parse_args(sys.argv[1:])
365
+ parser.parse_known_args(argv[1:]) # Allows for ``littlefs-python --version``
366
+ args = parser.parse_args(argv[1:])
365
367
  return args.func(parser, args)
366
368
 
367
369
 
@@ -10,8 +10,14 @@ if typing.TYPE_CHECKING:
10
10
  class UserContext:
11
11
  """Basic User Context Implementation"""
12
12
 
13
- def __init__(self, buffsize: int) -> None:
14
- self.buffer = bytearray([0xFF] * buffsize)
13
+ def __init__(self, buffsize: int = None, buffer: bytearray = None) -> None:
14
+ if buffer is not None:
15
+ self.buffer = buffer
16
+ elif buffsize is not None:
17
+ self.buffer = bytearray([0xFF] * buffsize)
18
+ else:
19
+ raise ValueError("Either buffsize or buffer must be provided")
20
+ self.in_size = len(self.buffer)
15
21
 
16
22
  def read(self, cfg: "LFSConfig", block: int, off: int, size: int) -> bytearray:
17
23
  """read data
@@ -91,6 +97,7 @@ class UserContextFile(UserContext):
91
97
 
92
98
  self._path = file_path
93
99
  self._fh = open(file_path, mode)
100
+ self.in_size = os.path.getsize(file_path)
94
101
 
95
102
  def read(self, cfg: "LFSConfig", block: int, off: int, size: int) -> bytearray:
96
103
  logging.getLogger(__name__).debug("LFS Read : Block: %d, Offset: %d, Size=%d" % (block, off, size))
@@ -150,10 +157,11 @@ class UserContextWinDisk(UserContext):
150
157
  "Unable to import 'win32file'. This module is required for Windows-specific functionality. Please ensure you are running on a Windows platform or install 'pywin32' using: 'pip install pywin32'."
151
158
  )
152
159
  self.device = win32file.CreateFile(
153
- disk_path, win32file.GENERIC_READ, win32file.FILE_SHARE_READ, None, win32file.OPEN_EXISTING, 0, None
160
+ disk_path, win32file.GENERIC_READ | win32file.GENERIC_WRITE, win32file.FILE_SHARE_READ, None, win32file.OPEN_EXISTING, 0, None
154
161
  )
155
162
  if self.device == win32file.INVALID_HANDLE_VALUE:
156
163
  raise IOError("Could not open disk %s" % disk_path)
164
+ self.in_size = win32file.GetFileSize(self.device)
157
165
 
158
166
  def read(self, cfg: "LFSConfig", block: int, off: int, size: int) -> bytearray:
159
167
  """read data
@@ -214,7 +222,7 @@ class UserContextWinDisk(UserContext):
214
222
  start = block * cfg.block_size
215
223
 
216
224
  win32file.SetFilePointer(self.device, start, win32file.FILE_BEGIN)
217
- win32file.WriteFile(self.device, [0xFF] * cfg.block_size)
225
+ win32file.WriteFile(self.device, b'\xff' * cfg.block_size)
218
226
  return 0
219
227
 
220
228
  def sync(self, cfg: "LFSConfig") -> int: