justkitten 0.8.0__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 (47) hide show
  1. justkitten-0.8.0/.github/workflows/integration.yml +40 -0
  2. justkitten-0.8.0/.github/workflows/release.yml +40 -0
  3. justkitten-0.8.0/.github/workflows/tests.yml +13 -0
  4. justkitten-0.8.0/.gitignore +142 -0
  5. justkitten-0.8.0/CHANGELOG.md +408 -0
  6. justkitten-0.8.0/CLAUDE.md +41 -0
  7. justkitten-0.8.0/PKG-INFO +145 -0
  8. justkitten-0.8.0/README.md +97 -0
  9. justkitten-0.8.0/pyproject.toml +124 -0
  10. justkitten-0.8.0/src/justkitten/__init__.py +0 -0
  11. justkitten-0.8.0/src/justkitten/cd.py +19 -0
  12. justkitten-0.8.0/src/justkitten/cli.py +162 -0
  13. justkitten-0.8.0/src/justkitten/data.py +134 -0
  14. justkitten-0.8.0/src/justkitten/dictable.py +237 -0
  15. justkitten-0.8.0/src/justkitten/exif.py +124 -0
  16. justkitten-0.8.0/src/justkitten/filesystem.py +648 -0
  17. justkitten-0.8.0/src/justkitten/joins.py +62 -0
  18. justkitten-0.8.0/src/justkitten/json_migration.py +60 -0
  19. justkitten-0.8.0/src/justkitten/parts.py +216 -0
  20. justkitten-0.8.0/src/justkitten/py.typed +0 -0
  21. justkitten-0.8.0/src/justkitten/pylinq.py +182 -0
  22. justkitten-0.8.0/src/justkitten/singleton.py +22 -0
  23. justkitten-0.8.0/src/justkitten/sources.py +157 -0
  24. justkitten-0.8.0/src/justkitten/subfolder.py +25 -0
  25. justkitten-0.8.0/src/justkitten/testing.py +27 -0
  26. justkitten-0.8.0/src/justkitten/time_formatter.py +24 -0
  27. justkitten-0.8.0/src/justkitten/transfer.py +67 -0
  28. justkitten-0.8.0/src/justkitten/util.py +326 -0
  29. justkitten-0.8.0/tests/__init__.py +0 -0
  30. justkitten-0.8.0/tests/conftest.py +10 -0
  31. justkitten-0.8.0/tests/test_cd.py +44 -0
  32. justkitten-0.8.0/tests/test_cli.py +114 -0
  33. justkitten-0.8.0/tests/test_data.py +118 -0
  34. justkitten-0.8.0/tests/test_dictable.py +691 -0
  35. justkitten-0.8.0/tests/test_exif.py +171 -0
  36. justkitten-0.8.0/tests/test_filesystem.py +68 -0
  37. justkitten-0.8.0/tests/test_joins.py +48 -0
  38. justkitten-0.8.0/tests/test_json_migration.py +76 -0
  39. justkitten-0.8.0/tests/test_parts.py +184 -0
  40. justkitten-0.8.0/tests/test_pylinq.py +244 -0
  41. justkitten-0.8.0/tests/test_singleton.py +31 -0
  42. justkitten-0.8.0/tests/test_sources.py +151 -0
  43. justkitten-0.8.0/tests/test_subfolder.py +50 -0
  44. justkitten-0.8.0/tests/test_testing.py +69 -0
  45. justkitten-0.8.0/tests/test_time_formatter.py +33 -0
  46. justkitten-0.8.0/tests/test_transfer.py +30 -0
  47. justkitten-0.8.0/tests/test_util.py +36 -0
@@ -0,0 +1,40 @@
1
+ name: Integration
2
+
3
+ on:
4
+ pull_request:
5
+ branches:
6
+ - master
7
+
8
+ jobs:
9
+ integration:
10
+ uses: djachenko/repokit/.github/workflows/python-integration.yml@0.9
11
+ secrets: inherit
12
+
13
+ publish:
14
+ needs: integration
15
+ runs-on: ubuntu-latest
16
+ permissions:
17
+ id-token: write
18
+
19
+ steps:
20
+ - uses: actions/download-artifact@v8
21
+ with:
22
+ name: dist
23
+ path: dist/
24
+
25
+ - name: Publish to TestPyPI
26
+ uses: pypa/gh-action-pypi-publish@release/v1
27
+ with:
28
+ repository-url: https://test.pypi.org/legacy/
29
+ skip-existing: true
30
+
31
+ - name: Smoke test
32
+ run: |
33
+ PACKAGE=$(ls dist/*.whl | head -1 | xargs basename | sed 's/-[0-9].*//')
34
+ VERSION=$(ls dist/*.tar.gz | sed 's/.*-\(.*\)\.tar\.gz/\1/')
35
+ for i in {1..5}; do
36
+ pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ $PACKAGE==$VERSION && break
37
+ echo "Attempt $i failed, retrying in 10s..."
38
+ sleep 10
39
+ done
40
+ python -c "import $PACKAGE"
@@ -0,0 +1,40 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+
8
+ jobs:
9
+ release:
10
+ uses: djachenko/repokit/.github/workflows/python-release.yml@0.9
11
+ secrets: inherit
12
+ permissions:
13
+ contents: write
14
+
15
+ publish:
16
+ needs: release
17
+ if: needs.release.outputs.released == 'true'
18
+ runs-on: ubuntu-latest
19
+ permissions:
20
+ id-token: write
21
+
22
+ steps:
23
+ - uses: actions/download-artifact@v8
24
+ with:
25
+ name: dist
26
+ path: dist/
27
+
28
+ - name: Publish to PyPI
29
+ uses: pypa/gh-action-pypi-publish@release/v1
30
+
31
+ - name: Smoke test
32
+ run: |
33
+ PACKAGE=$(ls dist/*.whl | head -1 | xargs basename | sed 's/-[0-9].*//')
34
+ VERSION=$(ls dist/*.tar.gz | sed 's/.*-\(.*\)\.tar\.gz/\1/')
35
+ for i in {1..5}; do
36
+ pip install $PACKAGE==$VERSION && break
37
+ echo "Attempt $i failed, retrying in 10s..."
38
+ sleep 10
39
+ done
40
+ python -c "import $PACKAGE"
@@ -0,0 +1,13 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - '**'
7
+ tags-ignore:
8
+ - '**'
9
+
10
+ jobs:
11
+ tests:
12
+ uses: djachenko/repokit/.github/workflows/python-tests.yml@0.9
13
+ secrets: inherit
@@ -0,0 +1,142 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ pip-wheel-metadata/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a python script from a template
32
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .tox/
43
+ .nox/
44
+ .coverage
45
+ .coverage.*
46
+ .cache
47
+ nosetests.xml
48
+ coverage.xml
49
+ *.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+
53
+ # Translations
54
+ *.mo
55
+ *.pot
56
+
57
+ # Django stuff:
58
+ *.log
59
+ local_settings.py
60
+ db.sqlite3
61
+ db.sqlite3-journal
62
+
63
+ # Flask stuff:
64
+ instance/
65
+ .webassets-cache
66
+
67
+ # Scrapy stuff:
68
+ .scrapy
69
+
70
+ # Sphinx documentation
71
+ docs/_build/
72
+
73
+ # PyBuilder
74
+ target/
75
+
76
+ # Jupyter Notebook
77
+ .ipynb_checkpoints
78
+
79
+ # IPython
80
+ profile_default/
81
+ ipython_config.py
82
+
83
+ # pyenv
84
+ .python-version
85
+
86
+ # pipenv
87
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
88
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
89
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
90
+ # install all needed dependencies.
91
+ #Pipfile.lock
92
+
93
+ # celery beat schedule file
94
+ celerybeat-schedule
95
+
96
+ # SageMath parsed files
97
+ *.sage.py
98
+
99
+ # Environments
100
+ .env
101
+ .venv
102
+ env/
103
+ venv/
104
+ ENV/
105
+ env.bak/
106
+ venv.bak/
107
+
108
+ # Spyder project settings
109
+ .spyderproject
110
+ .spyproject
111
+
112
+ # Rope project settings
113
+ .ropeproject
114
+
115
+ # mkdocs documentation
116
+ /site
117
+
118
+ # mypy
119
+ .mypy_cache/
120
+ .dmypy.json
121
+ dmypy.json
122
+
123
+ # Pyre type checker
124
+ .pyre/
125
+
126
+ # Worktrees
127
+ _worktrees/
128
+
129
+ # Session memory
130
+ memory/
131
+
132
+ # IDE
133
+ .idea/
134
+
135
+ # Claude Code
136
+ .claude/
137
+ _claude
138
+
139
+ # Repo dump
140
+ justin_utils.txt
141
+ .repokit
142
+ CLAUDE.local.md
@@ -0,0 +1,408 @@
1
+ # CHANGELOG
2
+
3
+ <!-- version list -->
4
+
5
+ ## v0.8.0 (2026-10-20)
6
+
7
+ ### Refactoring
8
+
9
+ - Rename justin_utils to justkitten
10
+ ([`a8e47d6`](https://github.com/djachenko/justkitten-py/commit/a8e47d62781bf00794c6f9472ec8a7fca5c0cc47))
11
+
12
+
13
+ ## v0.7.0 (2026-10-15)
14
+
15
+ ### Features
16
+
17
+ - Follow Codable semantics in dictable
18
+ ([`fb7df96`](https://github.com/djachenko/justin_utils/commit/fb7df9675ca34652b2a5dd3f2d1e8c9ec629ce8a))
19
+
20
+ ### Testing
21
+
22
+ - Pin dictable behaviors uncovered by mutation sweep
23
+ ([`68824cc`](https://github.com/djachenko/justin_utils/commit/68824ccd325f68ebd0fd5f52ea0e860c6ddd5cf5))
24
+
25
+ ### Breaking Changes
26
+
27
+ - Dictable.rules() is removed; pass rules to fromdict or from_dict instead. from_dict takes a rules
28
+ argument: from_dict(cls, json_object, rules=None).
29
+
30
+
31
+ ## v0.6.2 (2026-10-14)
32
+
33
+ ### Refactoring
34
+
35
+ - Name fixture files once per test via local file_name
36
+ ([`b887d65`](https://github.com/djachenko/justin_utils/commit/b887d65d0acd6dffe2976319b528fecd80b7e237))
37
+
38
+
39
+ ## v0.6.1 (2026-10-14)
40
+
41
+ ### Bug Fixes
42
+
43
+ - Parse nested plain dataclasses recursively in fromdict
44
+ ([`76e0038`](https://github.com/djachenko/justin_utils/commit/76e00387fbbc373914a0a260227596128a98f4b1))
45
+
46
+ ### Chores
47
+
48
+ - Mark json_migration classes as deprecated
49
+ ([`9534664`](https://github.com/djachenko/justin_utils/commit/95346643d98e3daa82ea6f6336a870dbec506568))
50
+
51
+ ### Refactoring
52
+
53
+ - Fix mypy errors in json_migration
54
+ ([`1b0fd30`](https://github.com/djachenko/justin_utils/commit/1b0fd30e0f3197d017e23278754a75a2bc958124))
55
+
56
+ - Group type-based branches in _convert under one isinstance check
57
+ ([`751c8a7`](https://github.com/djachenko/justin_utils/commit/751c8a702eb5164c22103f1677019f0b1124de52))
58
+
59
+ ### Testing
60
+
61
+ - Nested dataclass keeps its own from_dict
62
+ ([`14b22ac`](https://github.com/djachenko/justin_utils/commit/14b22acc6e27701e93bc8ba567491bfe82b24866))
63
+
64
+ - Remove duplicate test_scalar_coercion
65
+ ([`2546206`](https://github.com/djachenko/justin_utils/commit/2546206ffa0c3c5dea96e2b811edbeb5a007e145))
66
+
67
+
68
+ ## v0.6.0 (2026-10-08)
69
+
70
+ ### Features
71
+
72
+ - Add create_files pytest fixture as a shareable plugin
73
+ ([`917fbff`](https://github.com/djachenko/justin_utils/commit/917fbff7ee02ca2e452ea695bee2d8fceccde7d2))
74
+
75
+ ### Refactoring
76
+
77
+ - Load create_files from justin_utils.testing in own tests
78
+ ([`60bb8fc`](https://github.com/djachenko/justin_utils/commit/60bb8fc7645fd45a86b2f33d58029b7703fd2b97))
79
+
80
+ - Return Self from pylinq methods and drop quoted annotations
81
+ ([`895c260`](https://github.com/djachenko/justin_utils/commit/895c260af22471cfedbfc86f61060f75cf71adbd))
82
+
83
+ - Return Self from the constructing classmethods
84
+ ([`228d5c6`](https://github.com/djachenko/justin_utils/commit/228d5c6a183707aeb0fb5f5b6547d747ecc9e94d))
85
+
86
+ - Type pylinq and drop its mypy exemption
87
+ ([`6676c35`](https://github.com/djachenko/justin_utils/commit/6676c35320a31fdd2e89126f3d7f0f7c6c45f125))
88
+
89
+
90
+ ## v0.5.0 (2026-09-05)
91
+
92
+ ### Build System
93
+
94
+ - Add py.typed marker
95
+ ([`9fdef21`](https://github.com/djachenko/justin_utils/commit/9fdef214f4291865eaa40800ffa2889fa4941cc8))
96
+
97
+ ### Chores
98
+
99
+ - Ignore the _claude symlink
100
+ ([`5eac279`](https://github.com/djachenko/justin_utils/commit/5eac2798c7399501165d805bf2ee9e74bc02ef9e))
101
+
102
+ ### Code Style
103
+
104
+ - Collapse the parametrize decorator
105
+ ([`1ba68c3`](https://github.com/djachenko/justin_utils/commit/1ba68c3aa1d9d3175528b13f1c981884b1525afc))
106
+
107
+ ### Features
108
+
109
+ - Report deserialization failures with DictableError
110
+ ([`e128ff4`](https://github.com/djachenko/justin_utils/commit/e128ff40e00ee732799d423c5fd4a3cc7d240265))
111
+
112
+ ### Refactoring
113
+
114
+ - Replace lambda defaults with named callables in pylinq
115
+ ([`cd2bc3f`](https://github.com/djachenko/justin_utils/commit/cd2bc3f40c0313dc84ac9d4487049afe29aae898))
116
+
117
+ - Stop assuming every Dictable is a dataclass
118
+ ([`2e6366e`](https://github.com/djachenko/justin_utils/commit/2e6366ee6cb05502eaff1950f70330551dd284f4))
119
+
120
+ ### Testing
121
+
122
+ - Accept the windows error for reading a directory
123
+ ([`ef4bcf3`](https://github.com/djachenko/justin_utils/commit/ef4bcf3b3333e15d7f883022526f435b031eff1a))
124
+
125
+ - Support binary files in the conftest file tree
126
+ ([`9d2f49f`](https://github.com/djachenko/justin_utils/commit/9d2f49f6a50ceec9db1805ab9a5444ef2c6ca989))
127
+
128
+
129
+ ## v0.4.1 (2026-09-05)
130
+
131
+ ### Bug Fixes
132
+
133
+ - Raise AssertionError instead of assert False
134
+ ([`6e0dc14`](https://github.com/djachenko/justin_utils/commit/6e0dc148b1bab63baa1266f1ea7dccdc2208275b))
135
+
136
+ - Raise meaningful exceptions instead of AssertionError
137
+ ([`c6a6cd8`](https://github.com/djachenko/justin_utils/commit/c6a6cd893b6406cfd9e00d894d688b5df42065c4))
138
+
139
+ - Return None for a RAF with a truncated header
140
+ ([`d347f81`](https://github.com/djachenko/justin_utils/commit/d347f814259b4fef9834a2ddc25e6c2e249d20c4))
141
+
142
+ ### Chores
143
+
144
+ - Configure ruff lint rules and line length
145
+ ([`5bde5ef`](https://github.com/djachenko/justin_utils/commit/5bde5ef93df21a3f0289c7f5a8596b0dd9fdbc0e))
146
+
147
+ ### Code Style
148
+
149
+ - Apply ruff format
150
+ ([`80c5a39`](https://github.com/djachenko/justin_utils/commit/80c5a39f2058757fa1670606f0d32af1885d8876))
151
+
152
+ - Drop the formatter, keep the linter
153
+ ([`642e030`](https://github.com/djachenko/justin_utils/commit/642e0301081a95b09219ef63df93c858cdafbd6b))
154
+
155
+ - Preserve author quote style and keep exploded call sites
156
+ ([`0d0de99`](https://github.com/djachenko/justin_utils/commit/0d0de99e8aafb218386c49f2b55b0242e58fe2e5))
157
+
158
+ ### Refactoring
159
+
160
+ - Call the classmethod on type(self), not the instance
161
+ ([`21cce05`](https://github.com/djachenko/justin_utils/commit/21cce05fff1e29e2e32a2152aaae5a60bf7fcfd4))
162
+
163
+ - Drop the decorative ABC from Singleton
164
+ ([`70d5acc`](https://github.com/djachenko/justin_utils/commit/70d5accb93c80771da6a8af97fcb9e6df34290de))
165
+
166
+ - Drop two noqa directives by fixing their cause
167
+ ([`84f88ca`](https://github.com/djachenko/justin_utils/commit/84f88cade2c8210fb056b27d8bc111347d73bc33))
168
+
169
+ - Fix ruff lint findings
170
+ ([`517c6a7`](https://github.com/djachenko/justin_utils/commit/517c6a7e7acf1c868190d6d8cb9d162038458afc))
171
+
172
+ - Remove the unused TypeVar from singleton
173
+ ([`07cb39c`](https://github.com/djachenko/justin_utils/commit/07cb39ce74255f0911e19e87df30460143e030e6))
174
+
175
+ - Return Self from constructing classmethods
176
+ ([`9b19301`](https://github.com/djachenko/justin_utils/commit/9b193013045dc74c78a6d8e21870ab29a3792969))
177
+
178
+
179
+ ## v0.4.0 (2026-08-19)
180
+
181
+ ### Features
182
+
183
+ - Rewrite exif parsing with container abstraction and RAF support
184
+ ([`556ac3c`](https://github.com/djachenko/justin_utils/commit/556ac3c390d745ff238df2211777ba99f5ae153e))
185
+
186
+ ### Refactoring
187
+
188
+ - Make RAF magic and header offset class constants
189
+ ([`32edf21`](https://github.com/djachenko/justin_utils/commit/32edf2113f37308412d36e11aaf1d2b1941f4e68))
190
+
191
+ - Narrow ImageSource to Path | IO[bytes], drop private PIL import
192
+ ([`ac12695`](https://github.com/djachenko/justin_utils/commit/ac12695b2c6d24d44be9d4f296e11dae980fd667))
193
+
194
+ - Pass path to containers instead of a shared file handle
195
+ ([`5dbf072`](https://github.com/djachenko/justin_utils/commit/5dbf072c1919bdf7ba0a6b4fbdf7285dc1131eae))
196
+
197
+ ### Testing
198
+
199
+ - Extract raf container builder to drop duplicated header assembly
200
+ ([`623c954`](https://github.com/djachenko/justin_utils/commit/623c954d3187fa76358eff43ab4c55c61b3163f9))
201
+
202
+
203
+ ## v0.3.0 (2026-07-29)
204
+
205
+ ### Chores
206
+
207
+ - Ignore CLAUDE.local.md
208
+ ([`c24bde6`](https://github.com/djachenko/justin_utils/commit/c24bde6b075f3094e703a006bde09d26dc2ed3d7))
209
+
210
+ ### Documentation
211
+
212
+ - Remove stale status and backlog from CLAUDE.md
213
+ ([`ce127dd`](https://github.com/djachenko/justin_utils/commit/ce127dd96b96c900182605d1ba01e33d8bcbad40))
214
+
215
+ - Reorder modules section alphabetically, promote CLI tools
216
+ ([`de7f0fc`](https://github.com/djachenko/justin_utils/commit/de7f0fcd68bf7968caed2d21c5c03d6a04b62e64))
217
+
218
+ ### Features
219
+
220
+ - Add dictable module
221
+ ([`ae0ee7c`](https://github.com/djachenko/justin_utils/commit/ae0ee7c0ef960e819d92f5722afc7ab7de1ea00c))
222
+
223
+ ### Refactoring
224
+
225
+ - Remove frozendict dependency from dictable
226
+ ([`7d8fb50`](https://github.com/djachenko/justin_utils/commit/7d8fb50bbc40892a2e707918f37806af0f2821f5))
227
+
228
+
229
+ ## v0.2.3 (2026-07-29)
230
+
231
+ ### Bug Fixes
232
+
233
+ - Add return type annotations for mypy --strict compliance
234
+ ([`88af0ca`](https://github.com/djachenko/justin_utils/commit/88af0ca2b38363ca06e65797d5ae90926b7cbe66))
235
+
236
+ - Resolve ruff 0.16 violations (RUF012/013, C41x, UP028, B008, BLE001, PLR1722, DTZ, noqa)
237
+ ([`0f2d878`](https://github.com/djachenko/justin_utils/commit/0f2d878cc08ea1fcf88d8d30aa56d75d54f02d46))
238
+
239
+ ### Chores
240
+
241
+ - Pin ruff to >= 0.16, < 0.17
242
+ ([`04157f0`](https://github.com/djachenko/justin_utils/commit/04157f0ea62cc2d790c319a041c4efe343540014))
243
+
244
+ ### Code Style
245
+
246
+ - Apply ruff 0.16 auto-fixes (UP006/UP035, I001, C41x, B009)
247
+ ([`91bf2d1`](https://github.com/djachenko/justin_utils/commit/91bf2d1af497b1b251a176cc339fc1bb3bcb6e4a))
248
+
249
+
250
+ ## v0.2.2 (2026-06-30)
251
+
252
+ ### Bug Fixes
253
+
254
+ - Allow None for metadata in ExternalMetadataSource type hint
255
+ ([`eac8d77`](https://github.com/djachenko/justin_utils/commit/eac8d77efc69eefe7ebede1df76ebfb1aaca7b52))
256
+
257
+ ### Chores
258
+
259
+ - Mark cli.py public classes as deprecated
260
+ ([`00d95a7`](https://github.com/djachenko/justin_utils/commit/00d95a70ff0e3c0c9d97e11784992b63de8cbb49))
261
+
262
+
263
+ ## v0.2.1 (2026-06-30)
264
+
265
+ ### Bug Fixes
266
+
267
+ - DataSize.Unit.for_value off-by-one at exact unit boundaries
268
+ ([`051f730`](https://github.com/djachenko/justin_utils/commit/051f730e969643beb539e7c9f87497794658a629))
269
+
270
+ - Remove now-empty source folder after renumber single-part flatten
271
+ ([`04ffb38`](https://github.com/djachenko/justin_utils/commit/04ffb38514a55b1db8bf1ec1f71c93abc601d8b0))
272
+
273
+ ### Code Style
274
+
275
+ - Add type hints to _run() in test_subfolder.py
276
+ ([`9710541`](https://github.com/djachenko/justin_utils/commit/97105418b511bfce410e14f31db77e39c4cfd3b2))
277
+
278
+ - Blank line after first statement in _RecordingAction.__init__
279
+ ([`27a4a5d`](https://github.com/djachenko/justin_utils/commit/27a4a5d645e522d2dba5f75200faf7d79b31cd6e))
280
+
281
+ - Type the recursive file-tree structure instead of bare dict
282
+ ([`54f2d83`](https://github.com/djachenko/justin_utils/commit/54f2d83b0d9d9f5785c5d957d53b645b1ed2035f))
283
+
284
+ - Unify exception-test naming to _raises suffix
285
+ ([`39f0553`](https://github.com/djachenko/justin_utils/commit/39f055376574c436135e531dde26127a85a5a275))
286
+
287
+ - Use collections.abc.Iterable and lowercase list[] in test_cli.py
288
+ ([`1a83dbe`](https://github.com/djachenko/justin_utils/commit/1a83dbed078ee8ead8d93735cfb7cb5fc4bea0e1))
289
+
290
+ ### Testing
291
+
292
+ - Add coverage for subfolder.py, fix mypy typing in test_cli.py
293
+ ([`0c59307`](https://github.com/djachenko/justin_utils/commit/0c5930766742d08bccf3f7332f7a309ad1af7f56))
294
+
295
+ - Assert correct behavior for unit boundaries and renumber cleanup (red)
296
+ ([`a6d4d14`](https://github.com/djachenko/justin_utils/commit/a6d4d14c95f9119023a67da0ac44ab957d3b8cfa))
297
+
298
+ - Cover case-insensitive extension matching in parse_sources
299
+ ([`2ec5538`](https://github.com/djachenko/justin_utils/commit/2ec55380dd862f8e10f07bc3f9076568fd2f47d5))
300
+
301
+ - Deduplicate repeated string literals into single source of truth
302
+ ([`2de5092`](https://github.com/djachenko/justin_utils/commit/2de5092688b1343ef1131061b1dfea84617a6921))
303
+
304
+ - Fix DataSpeed.formatted boundary test missed in previous audit
305
+ ([`271cbd1`](https://github.com/djachenko/justin_utils/commit/271cbd1b8517e3b938903d00d2aaa2cdb0bdc11e))
306
+
307
+ - Fold case-insensitivity into existing parse_sources parametrize instead of new tests
308
+ ([`d2d5eef`](https://github.com/djachenko/justin_utils/commit/d2d5eef388b36cb5aaabf0e877d5253f5cbfacf2))
309
+
310
+ - Fold duplicate-shaped tests into parametrize across the suite
311
+ ([`fb7fcf6`](https://github.com/djachenko/justin_utils/commit/fb7fcf6f3405e9184655909d3d1e52d6bb528bed))
312
+
313
+ - Refactor test_parts.py to class+parametrize style
314
+ ([`a94bde9`](https://github.com/djachenko/justin_utils/commit/a94bde9543ffcd34ad916a3043e646939ac5cd17))
315
+
316
+ - Use cross-product parametrize for extension-case combinations
317
+ ([`bf6139d`](https://github.com/djachenko/justin_utils/commit/bf6139d5680712d61eac1b3042e47c46a53b892e))
318
+
319
+
320
+ ## v0.2.0 (2026-06-30)
321
+
322
+ ### Bug Fixes
323
+
324
+ - Clean up leftover temp folder in dump_in_temp
325
+ ([`4565292`](https://github.com/djachenko/justin_utils/commit/4565292cebbe4a227d464031fabeb22b6b8bd5cc))
326
+
327
+ ### Features
328
+
329
+ - Migrate parts CLI from argparse to Typer
330
+ ([`2a5cf62`](https://github.com/djachenko/justin_utils/commit/2a5cf626f902f38274f077fa8048ba54210cb8b5))
331
+
332
+ ### Testing
333
+
334
+ - Add tests for parts.py Typer CLI
335
+ ([`cf850f8`](https://github.com/djachenko/justin_utils/commit/cf850f8be5c41bca85ec564423df89d0405b8e9c))
336
+
337
+
338
+ ## v0.1.2 (2026-06-30)
339
+
340
+ ### Bug Fixes
341
+
342
+ - Add path setter on FolderBased and drop unused pytest imports
343
+ ([`983609c`](https://github.com/djachenko/justin_utils/commit/983609cda8deff699e9300003f41359835a26ac2))
344
+
345
+ - Folder.merge_into path update and __get_by_path single-component lookup
346
+ ([`8ea2d62`](https://github.com/djachenko/justin_utils/commit/8ea2d62127c728e945071d657b9a5ceea68d09a5))
347
+
348
+ - Support Linux in __get_mount
349
+ ([`e9ad807`](https://github.com/djachenko/justin_utils/commit/e9ad807f3b6b2a09742b32c3bf4a819bbcc7e2e3))
350
+
351
+ ### Chores
352
+
353
+ - Add _worktrees/ to gitignore
354
+ ([`8abfe6e`](https://github.com/djachenko/justin_utils/commit/8abfe6eeed64e90c16c7df042a51a46f8d45a5a5))
355
+
356
+ - Mark FolderBased as deprecated
357
+ ([`6ddb233`](https://github.com/djachenko/justin_utils/commit/6ddb2332e9564eb7dd7a1aadf6aafc727138c871))
358
+
359
+ ### Testing
360
+
361
+ - Add pytest suite for backlog bugs
362
+ ([`3255843`](https://github.com/djachenko/justin_utils/commit/325584380b18522e9c02b4bd72a9620f2781d2eb))
363
+
364
+
365
+ ## v0.1.1 (2026-06-27)
366
+
367
+ ### Bug Fixes
368
+
369
+ - Add assertions for mypy in TransferSpeedMeter
370
+ ([`9abdfe2`](https://github.com/djachenko/justin_utils/commit/9abdfe22253af2e2ee4ae0d30cfd8db0d414c0f5))
371
+
372
+ - Copy() no longer removes source tree on cross-drive copy
373
+ ([`92e2d89`](https://github.com/djachenko/justin_utils/commit/92e2d893814a4c7d3c2ab7605330a034322e8620))
374
+
375
+ - Correct null checks in joins
376
+ ([`d7ab2f5`](https://github.com/djachenko/justin_utils/commit/d7ab2f52fb64f7f1462940003b8f45d31f7be74d))
377
+
378
+ - Correct parse_date year logic for 4-digit years
379
+ ([`cf7e703`](https://github.com/djachenko/justin_utils/commit/cf7e703c376cd7233c0f729961cde2cc50d07086))
380
+
381
+ - Make Exif.from_path abstract, Action inherit ABC
382
+ ([`34d8cdc`](https://github.com/djachenko/justin_utils/commit/34d8cdcf2b4ca665474f27d69fc887d832e3cfec))
383
+
384
+ - Replace @property @cache with @cached_property
385
+ ([`b07b020`](https://github.com/djachenko/justin_utils/commit/b07b020a4e49033a7f314a3b4b9acb1038a3bcb9))
386
+
387
+ ### Chores
388
+
389
+ - [repokit] update ci workflows
390
+ ([`0f1b1e5`](https://github.com/djachenko/justin_utils/commit/0f1b1e5ffde4b03d140b724d5b5eae327a2208f2))
391
+
392
+ - Bump minimum Python to 3.11, drop typing_extensions
393
+ ([`284ff9c`](https://github.com/djachenko/justin_utils/commit/284ff9c0a682d835df67d64ba4c38283ea015fa9))
394
+
395
+ ### Documentation
396
+
397
+ - Rewrite README
398
+ ([`d898e99`](https://github.com/djachenko/justin_utils/commit/d898e999d1f9b5dab6015e3c24fa907903e09df7))
399
+
400
+ ### Refactoring
401
+
402
+ - Expand ternary operators
403
+ ([`37dede3`](https://github.com/djachenko/justin_utils/commit/37dede3c36407b2602971198255eda117b5236c3))
404
+
405
+
406
+ ## v0.1.0 (2026-06-25)
407
+
408
+ - Initial Release
@@ -0,0 +1,41 @@
1
+ # justkitten — Project Guide
2
+
3
+ ## Что это
4
+
5
+ Общие утилиты Python-экосистемы. Библиотека без зависимостей, которую можно установить отдельно и использовать в любом проекте экосистемы.
6
+
7
+ ---
8
+
9
+ ## Место в экосистеме
10
+
11
+ ```
12
+ justin ──→ justkitten
13
+ pyvko ──→ justkitten (планируется)
14
+ ```
15
+
16
+ Цель: вобрать весь переиспользуемый код из `justin` и `pyvko`, чтобы шарить между проектами без лишних зависимостей.
17
+
18
+ ---
19
+
20
+ ## Текущие модули
21
+
22
+ | Модуль | Содержимое |
23
+ |--------|------------|
24
+ | `filesystem.py` | `Folder`, `File` — обёртки над `Path` с удобным доступом к подпапкам |
25
+ | `parts.py` | CLI-утилита `parts` |
26
+ | `subfolder.py` | CLI-утилита `sf` |
27
+ | `transfer.py` | `TransferSpeedMeter`, `TransferTimeEstimator` |
28
+ | `time_formatter.py` | Форматирование времени |
29
+ | `data.py` | `DataSize` |
30
+ | `dictable.py` | `Dictable`, `DictableDataclass`, `fromdict` — десериализация JSON в датаклассы |
31
+ | `exif.py` | EXIF-утилиты |
32
+ | `singleton.py` | Singleton паттерн |
33
+ | `joins.py`, `pylinq.py` | LINQ-подобные операции над коллекциями |
34
+ | `json_migration.py` | Утилиты для JSON-миграций |
35
+ | `testing.py` | pytest-плагин с фикстурой `create_files`; подключается через `pytest_plugins`, extra `testing` |
36
+
37
+ ---
38
+
39
+ ## Git
40
+
41
+ Semantic commits: `feat:`, `fix:`, `refactor:`, `chore:`