feat(phase1): add image storage utilities
- Create backend/services/image_storage.py with 4 core functions:
- sanitize_filename(): remove unsafe chars, limit to 255 chars, convert to lowercase
- get_unique_filename(): handle collisions with UUID suffix (format: {name}_{uuid8}_{variant}.jpg)
- ensure_image_directories(): create /images/ root and category subdirs on startup
- save_image(): save bytes to /images/{category}/{filename}, returns relative path
- Create comprehensive test suite (22 tests) covering all functionality
- Integrate ensure_image_directories() into FastAPI startup event
- Directory structure: /images/{category}/{filename}
- Collision handling: auto-suffix with UUID if filename exists
- All tests passing, pathlib.Path for safe operations
This commit is contained in:
@@ -0,0 +1 @@
|
||||
pip
|
||||
@@ -0,0 +1,148 @@
|
||||
Metadata-Version: 2.4
|
||||
Name: jiter
|
||||
Version: 0.14.0
|
||||
Classifier: Development Status :: 4 - Beta
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3 :: Only
|
||||
Classifier: Programming Language :: Python :: 3.9
|
||||
Classifier: Programming Language :: Python :: 3.10
|
||||
Classifier: Programming Language :: Python :: 3.11
|
||||
Classifier: Programming Language :: Python :: 3.12
|
||||
Classifier: Programming Language :: Python :: 3.13
|
||||
Classifier: Programming Language :: Python :: 3.14
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||
Classifier: Programming Language :: Python :: Implementation :: GraalPy
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: Intended Audience :: Information Technology
|
||||
Classifier: Intended Audience :: System Administrators
|
||||
Classifier: Operating System :: Unix
|
||||
Classifier: Operating System :: POSIX :: Linux
|
||||
Classifier: Environment :: Console
|
||||
Classifier: Environment :: MacOS X
|
||||
Classifier: Topic :: File Formats :: JSON
|
||||
Classifier: Framework :: Pydantic :: 2
|
||||
License-File: LICENSE
|
||||
Summary: Fast iterable JSON parser.
|
||||
Home-Page: https://github.com/pydantic/jiter/
|
||||
Author-email: Samuel Colvin <s@muelcolvin.com>
|
||||
License-Expression: MIT
|
||||
Requires-Python: >=3.9
|
||||
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
||||
|
||||
# jiter
|
||||
|
||||
[](https://github.com/pydantic/jiter/actions?query=event%3Apush+branch%3Amain+workflow%3ACI)
|
||||
[](https://pypi.python.org/pypi/jiter)
|
||||
[](https://github.com/pydantic/jiter)
|
||||
[](https://github.com/pydantic/jiter/blob/main/LICENSE)
|
||||
|
||||
This is a standalone version of the JSON parser used in `pydantic-core`. The recommendation is to only use this package directly if you do not use `pydantic`.
|
||||
|
||||
The API is extremely minimal:
|
||||
|
||||
```python
|
||||
def from_json(
|
||||
json_data: bytes,
|
||||
/,
|
||||
*,
|
||||
allow_inf_nan: bool = True,
|
||||
cache_mode: Literal[True, False, 'all', 'keys', 'none'] = 'all',
|
||||
partial_mode: Literal[True, False, 'off', 'on', 'trailing-strings'] = False,
|
||||
catch_duplicate_keys: bool = False,
|
||||
float_mode: Literal['float', 'decimal', 'lossless-float'] = 'float',
|
||||
) -> Any:
|
||||
"""
|
||||
Parse input bytes into a JSON object.
|
||||
|
||||
Arguments:
|
||||
json_data: The JSON data to parse
|
||||
allow_inf_nan: Whether to allow infinity (`Infinity` an `-Infinity`) and `NaN` values to float fields.
|
||||
Defaults to True.
|
||||
cache_mode: cache Python strings to improve performance at the cost of some memory usage
|
||||
- True / 'all' - cache all strings
|
||||
- 'keys' - cache only object keys
|
||||
- False / 'none' - cache nothing
|
||||
partial_mode: How to handle incomplete strings:
|
||||
- False / 'off' - raise an exception if the input is incomplete
|
||||
- True / 'on' - allow incomplete JSON but discard the last string if it is incomplete
|
||||
- 'trailing-strings' - allow incomplete JSON, and include the last incomplete string in the output
|
||||
catch_duplicate_keys: if True, raise an exception if objects contain the same key multiple times
|
||||
float_mode: How to return floats: as a `float`, `Decimal` or `LosslessFloat`
|
||||
|
||||
Returns:
|
||||
Python object built from the JSON input.
|
||||
"""
|
||||
|
||||
|
||||
def cache_clear() -> None:
|
||||
"""
|
||||
Reset the string cache.
|
||||
"""
|
||||
|
||||
|
||||
def cache_usage() -> int:
|
||||
"""
|
||||
get the size of the string cache.
|
||||
|
||||
Returns:
|
||||
Size of the string cache in bytes.
|
||||
"""
|
||||
|
||||
```
|
||||
## Examples
|
||||
|
||||
The main function provided by Jiter is `from_json()`, which accepts a bytes object containing JSON and returns a Python dictionary, list or other value.
|
||||
|
||||
```python
|
||||
import jiter
|
||||
|
||||
json_data = b'{"name": "John", "age": 30}'
|
||||
parsed_data = jiter.from_json(json_data)
|
||||
print(parsed_data) # Output: {'name': 'John', 'age': 30}
|
||||
```
|
||||
|
||||
### Handling Partial JSON
|
||||
|
||||
Incomplete JSON objects can be parsed using the `partial_mode=` parameter.
|
||||
|
||||
```python
|
||||
import jiter
|
||||
|
||||
partial_json = b'{"name": "John", "age": 30, "city": "New Yor'
|
||||
|
||||
# Raise error on incomplete JSON
|
||||
try:
|
||||
jiter.from_json(partial_json, partial_mode=False)
|
||||
except ValueError as e:
|
||||
print(f'Error: {e}')
|
||||
|
||||
# Parse incomplete JSON, discarding incomplete last field
|
||||
result = jiter.from_json(partial_json, partial_mode=True)
|
||||
print(result) # Output: {'name': 'John', 'age': 30}
|
||||
|
||||
# Parse incomplete JSON, including incomplete last field
|
||||
result = jiter.from_json(partial_json, partial_mode='trailing-strings')
|
||||
print(result) # Output: {'name': 'John', 'age': 30, 'city': 'New Yor'}
|
||||
```
|
||||
|
||||
### Catching Duplicate Keys
|
||||
|
||||
The `catch_duplicate_keys=True` option can be used to raise a `ValueError` if an object contains duplicate keys.
|
||||
|
||||
```python
|
||||
import jiter
|
||||
|
||||
json_with_dupes = b'{"foo": 1, "foo": 2}'
|
||||
|
||||
# Default behavior (last value wins)
|
||||
result = jiter.from_json(json_with_dupes)
|
||||
print(result) # Output: {'foo': 2}
|
||||
|
||||
# Catch duplicate keys
|
||||
try:
|
||||
jiter.from_json(json_with_dupes, catch_duplicate_keys=True)
|
||||
except ValueError as e:
|
||||
print(f'Error: {e}')
|
||||
```
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
jiter-0.14.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
jiter-0.14.0.dist-info/METADATA,sha256=0lyKL1x9Gu78v5TYL6MguN-TXK0fdIzKLSC8mmIQvZI,5212
|
||||
jiter-0.14.0.dist-info/RECORD,,
|
||||
jiter-0.14.0.dist-info/WHEEL,sha256=F6wS_EyyTApAlVDO0HV37reS5C0BQB3Ecd85S9pKlHg,147
|
||||
jiter-0.14.0.dist-info/licenses/LICENSE,sha256=fHE0ufe5eMA_yodVF885jbkfGbu4EJtmhedCqj9XRo4,1091
|
||||
jiter-0.14.0.dist-info/sboms/jiter-python.cyclonedx.json,sha256=5v3DyzZZFCeM9MBgsso1__bJxSIge_ZkB6V4bZIBxSU,83375
|
||||
jiter/__init__.py,sha256=Fp9HkOixiYYDSiC_80vmiJ_sCoCGT8OAh48yltm0lP0,103
|
||||
jiter/__init__.pyi,sha256=UJVBfIIdO79ycPzZZ7wqL6SLBgFmzCMOZv0J6kybmpM,2378
|
||||
jiter/__pycache__/__init__.cpython-312.pyc,,
|
||||
jiter/jiter.cpython-312-x86_64-linux-gnu.so,sha256=03oVGh4i1xUB3K4K996I9XTOtBSVOAiFu1hoz91Z_js,826152
|
||||
jiter/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
@@ -0,0 +1,5 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: maturin (1.13.1)
|
||||
Root-Is-Purelib: false
|
||||
Tag: cp312-cp312-manylinux_2_17_x86_64
|
||||
Tag: cp312-cp312-manylinux2014_x86_64
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2022 to present Samuel Colvin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user