fastapi-file-validator 0.0.0__tar.gz → 0.0.2__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.
- fastapi_file_validator-0.0.2/PKG-INFO +103 -0
- fastapi_file_validator-0.0.2/README.md +88 -0
- fastapi_file_validator-0.0.2/fastapi_file_validator.egg-info/PKG-INFO +103 -0
- {fastapi_file_validator-0.0.0 → fastapi_file_validator-0.0.2}/fastapi_file_validator.egg-info/SOURCES.txt +4 -1
- fastapi_file_validator-0.0.2/file_validator/file_extension_validator.py +47 -0
- fastapi_file_validator-0.0.2/file_validator/file_size_validator.py +41 -0
- {fastapi_file_validator-0.0.0 → fastapi_file_validator-0.0.2}/pyproject.toml +1 -1
- fastapi_file_validator-0.0.2/tests/test_file_extension_validator.py +71 -0
- fastapi_file_validator-0.0.2/tests/test_file_size_validator.py +85 -0
- fastapi_file_validator-0.0.0/PKG-INFO +0 -14
- fastapi_file_validator-0.0.0/fastapi_file_validator.egg-info/PKG-INFO +0 -14
- fastapi_file_validator-0.0.0/file_validator/file_extension_validator.py +0 -42
- fastapi_file_validator-0.0.0/file_validator/file_size_validator.py +0 -34
- {fastapi_file_validator-0.0.0 → fastapi_file_validator-0.0.2}/fastapi_file_validator.egg-info/dependency_links.txt +0 -0
- {fastapi_file_validator-0.0.0 → fastapi_file_validator-0.0.2}/fastapi_file_validator.egg-info/requires.txt +0 -0
- {fastapi_file_validator-0.0.0 → fastapi_file_validator-0.0.2}/fastapi_file_validator.egg-info/top_level.txt +0 -0
- {fastapi_file_validator-0.0.0 → fastapi_file_validator-0.0.2}/file_validator/__init__.py +0 -0
- {fastapi_file_validator-0.0.0 → fastapi_file_validator-0.0.2}/setup.cfg +0 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fastapi-file-validator
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: A lightweight, flexible file extension validator and MIME type checker for fastapi
|
|
5
|
+
Author-email: Seungkyu-Han <trust1204@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Seungkyu-Han/fastapi-file-validator
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/Seungkyu-Han/fastapi-file-validator
|
|
9
|
+
Project-URL: Documentation, https://github.com/Seungkyu-Han/fastapi-file-validator
|
|
10
|
+
Project-URL: Source, https://github.com/Seungkyu-Han/fastapi-file-validator
|
|
11
|
+
Keywords: validator
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: fastapi>=0.110.1
|
|
15
|
+
|
|
16
|
+
# FastAPI File Validators
|
|
17
|
+
|
|
18
|
+
A simple, lightweight, and flexible decorator-based file validation library for FastAPI. Easily validate uploaded file extensions and file sizes before hitting your endpoint logic.
|
|
19
|
+
|
|
20
|
+
[](https://pypi.org/project/fastapi-file-validator/)
|
|
21
|
+
[](https://codecov.io/gh/Seungkyu-Han/fastapi-file-validator)
|
|
22
|
+
[](https://pypi.org/project/fastapi-file-validator/)
|
|
23
|
+
[](https://pypi.org/project/fastapi-file-validator/)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 🚀 Installation
|
|
30
|
+
|
|
31
|
+
Install via pip:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install fastapi-file-validators
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Or install locally for development:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install -e .
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 📦 Usage
|
|
48
|
+
|
|
49
|
+
### 1. Validate File Extension
|
|
50
|
+
|
|
51
|
+
Ensure that the uploaded file matches specific allowed extensions. If the extension is invalid or missing, it automatically raises an `HTTP 415 Unsupported Media Type` or `HTTP 400 Bad Request`.
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from fastapi import FastAPI, UploadFile, File
|
|
55
|
+
from file_validator import file_extension_validator
|
|
56
|
+
|
|
57
|
+
app = FastAPI()
|
|
58
|
+
|
|
59
|
+
@app.post("/upload/image")
|
|
60
|
+
@file_extension_validator(file_arg_name="file", file_extensions={"jpg", "jpeg", "png"})
|
|
61
|
+
async def upload_image(file: UploadFile = File(...)):
|
|
62
|
+
return {"filename": file.filename, "message": "Extension validated successfully!"}
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
### 2. Validate File Size
|
|
69
|
+
|
|
70
|
+
Restrict the maximum size of an uploaded file in Megabytes (MB). If the file exceeds the limit, it automatically raises an `HTTP 413 Content Too Large`.
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from fastapi import FastAPI, UploadFile, File
|
|
74
|
+
from file_validator import file_size_validator
|
|
75
|
+
|
|
76
|
+
app = FastAPI()
|
|
77
|
+
|
|
78
|
+
@app.post("/upload/large-file")
|
|
79
|
+
@file_size_validator(file_arg_name="file", max_size_mb=10) # Limit to 10MB
|
|
80
|
+
async def upload_large_file(file: UploadFile = File(...)):
|
|
81
|
+
return {"size": file.size, "message": "Size validated successfully!"}
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## ⚙️ Configuration
|
|
88
|
+
|
|
89
|
+
### Exception Handling
|
|
90
|
+
|
|
91
|
+
The decorators raise standard FastAPI `HTTPException`s. You can expect the following responses when validation fails:
|
|
92
|
+
|
|
93
|
+
| Validator | Scenario | HTTP Status | Detail Message Example |
|
|
94
|
+
| --- | --- | --- | --- |
|
|
95
|
+
| `file_extension_validator` | Filename is empty | `400 Bad Request` | `"filename is empty"` |
|
|
96
|
+
| `file_extension_validator` | Invalid extension | `415 Unsupported Media Type` | `"exe type is unavailable"` |
|
|
97
|
+
| `file_size_validator` | File exceeds max size | `413 Content Too Large` | `"file size is bigger than 5MB"` |
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 📄 License
|
|
102
|
+
|
|
103
|
+
MIT License
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# FastAPI File Validators
|
|
2
|
+
|
|
3
|
+
A simple, lightweight, and flexible decorator-based file validation library for FastAPI. Easily validate uploaded file extensions and file sizes before hitting your endpoint logic.
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/fastapi-file-validator/)
|
|
6
|
+
[](https://codecov.io/gh/Seungkyu-Han/fastapi-file-validator)
|
|
7
|
+
[](https://pypi.org/project/fastapi-file-validator/)
|
|
8
|
+
[](https://pypi.org/project/fastapi-file-validator/)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 🚀 Installation
|
|
15
|
+
|
|
16
|
+
Install via pip:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install fastapi-file-validators
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Or install locally for development:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install -e .
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 📦 Usage
|
|
33
|
+
|
|
34
|
+
### 1. Validate File Extension
|
|
35
|
+
|
|
36
|
+
Ensure that the uploaded file matches specific allowed extensions. If the extension is invalid or missing, it automatically raises an `HTTP 415 Unsupported Media Type` or `HTTP 400 Bad Request`.
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from fastapi import FastAPI, UploadFile, File
|
|
40
|
+
from file_validator import file_extension_validator
|
|
41
|
+
|
|
42
|
+
app = FastAPI()
|
|
43
|
+
|
|
44
|
+
@app.post("/upload/image")
|
|
45
|
+
@file_extension_validator(file_arg_name="file", file_extensions={"jpg", "jpeg", "png"})
|
|
46
|
+
async def upload_image(file: UploadFile = File(...)):
|
|
47
|
+
return {"filename": file.filename, "message": "Extension validated successfully!"}
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
### 2. Validate File Size
|
|
54
|
+
|
|
55
|
+
Restrict the maximum size of an uploaded file in Megabytes (MB). If the file exceeds the limit, it automatically raises an `HTTP 413 Content Too Large`.
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from fastapi import FastAPI, UploadFile, File
|
|
59
|
+
from file_validator import file_size_validator
|
|
60
|
+
|
|
61
|
+
app = FastAPI()
|
|
62
|
+
|
|
63
|
+
@app.post("/upload/large-file")
|
|
64
|
+
@file_size_validator(file_arg_name="file", max_size_mb=10) # Limit to 10MB
|
|
65
|
+
async def upload_large_file(file: UploadFile = File(...)):
|
|
66
|
+
return {"size": file.size, "message": "Size validated successfully!"}
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## ⚙️ Configuration
|
|
73
|
+
|
|
74
|
+
### Exception Handling
|
|
75
|
+
|
|
76
|
+
The decorators raise standard FastAPI `HTTPException`s. You can expect the following responses when validation fails:
|
|
77
|
+
|
|
78
|
+
| Validator | Scenario | HTTP Status | Detail Message Example |
|
|
79
|
+
| --- | --- | --- | --- |
|
|
80
|
+
| `file_extension_validator` | Filename is empty | `400 Bad Request` | `"filename is empty"` |
|
|
81
|
+
| `file_extension_validator` | Invalid extension | `415 Unsupported Media Type` | `"exe type is unavailable"` |
|
|
82
|
+
| `file_size_validator` | File exceeds max size | `413 Content Too Large` | `"file size is bigger than 5MB"` |
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 📄 License
|
|
87
|
+
|
|
88
|
+
MIT License
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fastapi-file-validator
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: A lightweight, flexible file extension validator and MIME type checker for fastapi
|
|
5
|
+
Author-email: Seungkyu-Han <trust1204@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Seungkyu-Han/fastapi-file-validator
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/Seungkyu-Han/fastapi-file-validator
|
|
9
|
+
Project-URL: Documentation, https://github.com/Seungkyu-Han/fastapi-file-validator
|
|
10
|
+
Project-URL: Source, https://github.com/Seungkyu-Han/fastapi-file-validator
|
|
11
|
+
Keywords: validator
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: fastapi>=0.110.1
|
|
15
|
+
|
|
16
|
+
# FastAPI File Validators
|
|
17
|
+
|
|
18
|
+
A simple, lightweight, and flexible decorator-based file validation library for FastAPI. Easily validate uploaded file extensions and file sizes before hitting your endpoint logic.
|
|
19
|
+
|
|
20
|
+
[](https://pypi.org/project/fastapi-file-validator/)
|
|
21
|
+
[](https://codecov.io/gh/Seungkyu-Han/fastapi-file-validator)
|
|
22
|
+
[](https://pypi.org/project/fastapi-file-validator/)
|
|
23
|
+
[](https://pypi.org/project/fastapi-file-validator/)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 🚀 Installation
|
|
30
|
+
|
|
31
|
+
Install via pip:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install fastapi-file-validators
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Or install locally for development:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install -e .
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 📦 Usage
|
|
48
|
+
|
|
49
|
+
### 1. Validate File Extension
|
|
50
|
+
|
|
51
|
+
Ensure that the uploaded file matches specific allowed extensions. If the extension is invalid or missing, it automatically raises an `HTTP 415 Unsupported Media Type` or `HTTP 400 Bad Request`.
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from fastapi import FastAPI, UploadFile, File
|
|
55
|
+
from file_validator import file_extension_validator
|
|
56
|
+
|
|
57
|
+
app = FastAPI()
|
|
58
|
+
|
|
59
|
+
@app.post("/upload/image")
|
|
60
|
+
@file_extension_validator(file_arg_name="file", file_extensions={"jpg", "jpeg", "png"})
|
|
61
|
+
async def upload_image(file: UploadFile = File(...)):
|
|
62
|
+
return {"filename": file.filename, "message": "Extension validated successfully!"}
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
### 2. Validate File Size
|
|
69
|
+
|
|
70
|
+
Restrict the maximum size of an uploaded file in Megabytes (MB). If the file exceeds the limit, it automatically raises an `HTTP 413 Content Too Large`.
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from fastapi import FastAPI, UploadFile, File
|
|
74
|
+
from file_validator import file_size_validator
|
|
75
|
+
|
|
76
|
+
app = FastAPI()
|
|
77
|
+
|
|
78
|
+
@app.post("/upload/large-file")
|
|
79
|
+
@file_size_validator(file_arg_name="file", max_size_mb=10) # Limit to 10MB
|
|
80
|
+
async def upload_large_file(file: UploadFile = File(...)):
|
|
81
|
+
return {"size": file.size, "message": "Size validated successfully!"}
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## ⚙️ Configuration
|
|
88
|
+
|
|
89
|
+
### Exception Handling
|
|
90
|
+
|
|
91
|
+
The decorators raise standard FastAPI `HTTPException`s. You can expect the following responses when validation fails:
|
|
92
|
+
|
|
93
|
+
| Validator | Scenario | HTTP Status | Detail Message Example |
|
|
94
|
+
| --- | --- | --- | --- |
|
|
95
|
+
| `file_extension_validator` | Filename is empty | `400 Bad Request` | `"filename is empty"` |
|
|
96
|
+
| `file_extension_validator` | Invalid extension | `415 Unsupported Media Type` | `"exe type is unavailable"` |
|
|
97
|
+
| `file_size_validator` | File exceeds max size | `413 Content Too Large` | `"file size is bigger than 5MB"` |
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 📄 License
|
|
102
|
+
|
|
103
|
+
MIT License
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
README.md
|
|
1
2
|
pyproject.toml
|
|
2
3
|
fastapi_file_validator.egg-info/PKG-INFO
|
|
3
4
|
fastapi_file_validator.egg-info/SOURCES.txt
|
|
@@ -6,4 +7,6 @@ fastapi_file_validator.egg-info/requires.txt
|
|
|
6
7
|
fastapi_file_validator.egg-info/top_level.txt
|
|
7
8
|
file_validator/__init__.py
|
|
8
9
|
file_validator/file_extension_validator.py
|
|
9
|
-
file_validator/file_size_validator.py
|
|
10
|
+
file_validator/file_size_validator.py
|
|
11
|
+
tests/test_file_extension_validator.py
|
|
12
|
+
tests/test_file_size_validator.py
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from functools import wraps
|
|
3
|
+
|
|
4
|
+
from fastapi import HTTPException, status
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def file_extension_validator(file_arg_name: str, file_extensions: set[str]):
|
|
8
|
+
"""
|
|
9
|
+
A decorator that validates the file extension of an uploaded file. Ensures that the provided file has an allowed
|
|
10
|
+
extension before executing the decorated function. Raises an HTTPException if the validation fails.
|
|
11
|
+
|
|
12
|
+
:param file_arg_name: The name of the argument in the decorated function's signature that is expected to
|
|
13
|
+
contain the file object.
|
|
14
|
+
:param file_extensions: A set of allowed file extensions (case-insensitive) for the uploaded file.
|
|
15
|
+
:return: The decorated function with added file extension validation logic.
|
|
16
|
+
:rtype: Callable
|
|
17
|
+
"""
|
|
18
|
+
def decorator(func):
|
|
19
|
+
@wraps(func)
|
|
20
|
+
async def wrapper(*args, **kwargs):
|
|
21
|
+
if file_arg_name in kwargs:
|
|
22
|
+
file = kwargs.get(file_arg_name)
|
|
23
|
+
if file and (hasattr(file, "filename")):
|
|
24
|
+
|
|
25
|
+
filename: str | None = file.filename
|
|
26
|
+
|
|
27
|
+
if not filename:
|
|
28
|
+
raise HTTPException(
|
|
29
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
|
30
|
+
detail=f"filename is empty",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
_, ext = os.path.splitext(filename)
|
|
34
|
+
|
|
35
|
+
file_extension = ext[1:].lower()
|
|
36
|
+
|
|
37
|
+
if file_extension not in file_extensions:
|
|
38
|
+
raise HTTPException(
|
|
39
|
+
status_code=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE,
|
|
40
|
+
detail=f"{file_extension} type is unavailable",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
return await func(*args, **kwargs)
|
|
44
|
+
|
|
45
|
+
return wrapper
|
|
46
|
+
|
|
47
|
+
return decorator
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
|
|
3
|
+
from fastapi import HTTPException, status
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def file_size_validator(file_arg_name: str, max_size_mb: int):
|
|
7
|
+
"""
|
|
8
|
+
Decorator to validate the size of a file passed as a keyword argument to an asynchronous function. The decorator
|
|
9
|
+
checks if the file's size exceeds the specified maximum size in megabytes and raises an HTTPException if the
|
|
10
|
+
limit is exceeded.
|
|
11
|
+
|
|
12
|
+
:param file_arg_name: The name of the keyword argument representing the file in the decorated function.
|
|
13
|
+
:param max_size_mb: The maximum file size allowed in megabytes.
|
|
14
|
+
:return: A decorator function that validates the file's size against the specified limit.
|
|
15
|
+
:rtype: Callable
|
|
16
|
+
"""
|
|
17
|
+
max_size_bytes = max_size_mb * 1024 * 1024
|
|
18
|
+
|
|
19
|
+
def decorator(func):
|
|
20
|
+
@wraps(func)
|
|
21
|
+
async def wrapper(*args, **kwargs):
|
|
22
|
+
if file_arg_name in kwargs:
|
|
23
|
+
file = kwargs.get(file_arg_name)
|
|
24
|
+
|
|
25
|
+
if file and (hasattr(file, "size")):
|
|
26
|
+
file_size: int | None = file.size
|
|
27
|
+
|
|
28
|
+
if not file_size:
|
|
29
|
+
return await func(*args, **kwargs)
|
|
30
|
+
|
|
31
|
+
if file_size > max_size_bytes:
|
|
32
|
+
raise HTTPException(
|
|
33
|
+
status_code=status.HTTP_413_CONTENT_TOO_LARGE,
|
|
34
|
+
detail=f"file size is bigger than {max_size_mb}MB",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
return await func(*args, **kwargs)
|
|
38
|
+
|
|
39
|
+
return wrapper
|
|
40
|
+
|
|
41
|
+
return decorator
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "fastapi-file-validator"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.2"
|
|
8
8
|
description = "A lightweight, flexible file extension validator and MIME type checker for fastapi"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from fastapi import HTTPException, status
|
|
5
|
+
from unittest.mock import Mock
|
|
6
|
+
|
|
7
|
+
from file_validator import file_extension_validator
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@file_extension_validator(
|
|
11
|
+
file_arg_name="file",
|
|
12
|
+
file_extensions={"jpg", "png"},
|
|
13
|
+
)
|
|
14
|
+
async def dummy_endpoint(file: Any):
|
|
15
|
+
return "success"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.mark.asyncio
|
|
19
|
+
async def test_file_extension_validator_success():
|
|
20
|
+
# given
|
|
21
|
+
mocked_file = Mock()
|
|
22
|
+
mocked_file.filename = "image.png"
|
|
23
|
+
|
|
24
|
+
# when
|
|
25
|
+
result = await dummy_endpoint(file=mocked_file)
|
|
26
|
+
|
|
27
|
+
# then
|
|
28
|
+
assert result == "success"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.mark.asyncio
|
|
32
|
+
async def test_file_extension_validator_case_insensitive():
|
|
33
|
+
# given
|
|
34
|
+
mocked_file = Mock()
|
|
35
|
+
mocked_file.filename = "IMAGE.JPG"
|
|
36
|
+
|
|
37
|
+
# when
|
|
38
|
+
result = await dummy_endpoint(file=mocked_file)
|
|
39
|
+
|
|
40
|
+
# then
|
|
41
|
+
assert result == "success"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@pytest.mark.asyncio
|
|
45
|
+
async def test_file_extension_validator_empty_filename():
|
|
46
|
+
# given
|
|
47
|
+
mocked_file = Mock()
|
|
48
|
+
mocked_file.filename = ""
|
|
49
|
+
|
|
50
|
+
# when
|
|
51
|
+
with pytest.raises(HTTPException) as exc_info:
|
|
52
|
+
await dummy_endpoint(file=mocked_file)
|
|
53
|
+
|
|
54
|
+
# then
|
|
55
|
+
assert exc_info.value.status_code == status.HTTP_400_BAD_REQUEST
|
|
56
|
+
assert exc_info.value.detail == "filename is empty"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@pytest.mark.asyncio
|
|
60
|
+
async def test_file_extension_validator_unsupported_type():
|
|
61
|
+
# given
|
|
62
|
+
mocked_file = Mock()
|
|
63
|
+
mocked_file.filename = "document.pdf"
|
|
64
|
+
|
|
65
|
+
# when
|
|
66
|
+
with pytest.raises(HTTPException) as exc_info:
|
|
67
|
+
await dummy_endpoint(file=mocked_file)
|
|
68
|
+
|
|
69
|
+
# then
|
|
70
|
+
assert exc_info.value.status_code == status.HTTP_415_UNSUPPORTED_MEDIA_TYPE
|
|
71
|
+
assert exc_info.value.detail == "pdf type is unavailable"
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from fastapi import HTTPException, status
|
|
5
|
+
from unittest.mock import Mock
|
|
6
|
+
|
|
7
|
+
from file_validator import file_size_validator
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@file_size_validator(
|
|
11
|
+
file_arg_name="file",
|
|
12
|
+
max_size_mb=2,
|
|
13
|
+
)
|
|
14
|
+
async def dummy_endpoint(file: Any):
|
|
15
|
+
return "success"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.mark.asyncio
|
|
19
|
+
async def test_file_size_validator_success():
|
|
20
|
+
# given
|
|
21
|
+
mocked_file = Mock()
|
|
22
|
+
mocked_file.size = 1 * 1024 * 1024
|
|
23
|
+
|
|
24
|
+
# when
|
|
25
|
+
result = await dummy_endpoint(file=mocked_file)
|
|
26
|
+
|
|
27
|
+
# then
|
|
28
|
+
assert result == "success"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.mark.asyncio
|
|
32
|
+
async def test_file_size_validator_exactly_max_size():
|
|
33
|
+
# given
|
|
34
|
+
mocked_file = Mock()
|
|
35
|
+
mocked_file.size = 2 * 1024 * 1024
|
|
36
|
+
|
|
37
|
+
# when
|
|
38
|
+
result = await dummy_endpoint(file=mocked_file)
|
|
39
|
+
|
|
40
|
+
# then
|
|
41
|
+
assert result == "success"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@pytest.mark.asyncio
|
|
45
|
+
async def test_file_size_validator_too_large():
|
|
46
|
+
# given
|
|
47
|
+
mocked_file = Mock()
|
|
48
|
+
mocked_file.size = int(2.1 * 1024 * 1024)
|
|
49
|
+
|
|
50
|
+
# when
|
|
51
|
+
with pytest.raises(HTTPException) as exc_info:
|
|
52
|
+
await dummy_endpoint(file=mocked_file)
|
|
53
|
+
|
|
54
|
+
# then
|
|
55
|
+
assert exc_info.value.status_code == status.HTTP_413_CONTENT_TOO_LARGE
|
|
56
|
+
assert exc_info.value.detail == "file size is bigger than 2MB"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@pytest.mark.asyncio
|
|
60
|
+
async def test_file_size_validator_zero_or_none_size():
|
|
61
|
+
# given
|
|
62
|
+
mocked_file = Mock()
|
|
63
|
+
mocked_file.size = 0
|
|
64
|
+
|
|
65
|
+
mock_file_none = Mock()
|
|
66
|
+
mock_file_none.size = None
|
|
67
|
+
|
|
68
|
+
# when
|
|
69
|
+
result_zero = await dummy_endpoint(file=mocked_file)
|
|
70
|
+
result_none = await dummy_endpoint(file=mock_file_none)
|
|
71
|
+
|
|
72
|
+
# then
|
|
73
|
+
assert result_none == "success"
|
|
74
|
+
assert result_zero == "success"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@pytest.mark.asyncio
|
|
78
|
+
async def test_file_size_validator_missing_size_attribute():
|
|
79
|
+
# given
|
|
80
|
+
mocked_file = Mock(spec=[])
|
|
81
|
+
|
|
82
|
+
# when
|
|
83
|
+
result = await dummy_endpoint(file=mocked_file)
|
|
84
|
+
|
|
85
|
+
assert result == "success"
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: fastapi-file-validator
|
|
3
|
-
Version: 0.0.0
|
|
4
|
-
Summary: A lightweight, flexible file extension validator and MIME type checker for fastapi
|
|
5
|
-
Author-email: Seungkyu-Han <trust1204@gmail.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/Seungkyu-Han/fastapi-file-validator
|
|
8
|
-
Project-URL: Bug Tracker, https://github.com/Seungkyu-Han/fastapi-file-validator
|
|
9
|
-
Project-URL: Documentation, https://github.com/Seungkyu-Han/fastapi-file-validator
|
|
10
|
-
Project-URL: Source, https://github.com/Seungkyu-Han/fastapi-file-validator
|
|
11
|
-
Keywords: validator
|
|
12
|
-
Requires-Python: >=3.10
|
|
13
|
-
Description-Content-Type: text/markdown
|
|
14
|
-
Requires-Dist: fastapi>=0.110.1
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: fastapi-file-validator
|
|
3
|
-
Version: 0.0.0
|
|
4
|
-
Summary: A lightweight, flexible file extension validator and MIME type checker for fastapi
|
|
5
|
-
Author-email: Seungkyu-Han <trust1204@gmail.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/Seungkyu-Han/fastapi-file-validator
|
|
8
|
-
Project-URL: Bug Tracker, https://github.com/Seungkyu-Han/fastapi-file-validator
|
|
9
|
-
Project-URL: Documentation, https://github.com/Seungkyu-Han/fastapi-file-validator
|
|
10
|
-
Project-URL: Source, https://github.com/Seungkyu-Han/fastapi-file-validator
|
|
11
|
-
Keywords: validator
|
|
12
|
-
Requires-Python: >=3.10
|
|
13
|
-
Description-Content-Type: text/markdown
|
|
14
|
-
Requires-Dist: fastapi>=0.110.1
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
from fastapi import UploadFile, HTTPException, status
|
|
4
|
-
from functools import wraps
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def file_extension_validator(file_arg_name: str, file_extensions: set[str]):
|
|
8
|
-
def decorator(func):
|
|
9
|
-
@wraps(func)
|
|
10
|
-
async def wrapper(*args, **kwargs):
|
|
11
|
-
if file_arg_name in kwargs:
|
|
12
|
-
|
|
13
|
-
file_arg = kwargs.get(file_arg_name)
|
|
14
|
-
|
|
15
|
-
if not isinstance(file_arg, UploadFile):
|
|
16
|
-
return await func(*args, **kwargs)
|
|
17
|
-
|
|
18
|
-
file: UploadFile = file_arg
|
|
19
|
-
|
|
20
|
-
filename: str | None = file.filename
|
|
21
|
-
|
|
22
|
-
if not filename:
|
|
23
|
-
raise HTTPException(
|
|
24
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
|
25
|
-
detail=f"filename is empty",
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
_, ext = os.path.splitext(filename)
|
|
29
|
-
|
|
30
|
-
file_extension = ext[1:].lower()
|
|
31
|
-
|
|
32
|
-
if file_extension not in file_extensions:
|
|
33
|
-
raise HTTPException(
|
|
34
|
-
status_code=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE,
|
|
35
|
-
detail=f"{file_extension} type is unavailable",
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
return await func(*args, **kwargs)
|
|
39
|
-
|
|
40
|
-
return wrapper
|
|
41
|
-
|
|
42
|
-
return decorator
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
from fastapi import UploadFile, HTTPException, status
|
|
2
|
-
from functools import wraps
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def file_size_validator(file_arg_name: str, max_size_mb: int):
|
|
6
|
-
max_size_bytes = max_size_mb * 1024 * 1024
|
|
7
|
-
|
|
8
|
-
def decorator(func):
|
|
9
|
-
@wraps(func)
|
|
10
|
-
async def wrapper(*args, **kwargs):
|
|
11
|
-
if file_arg_name in kwargs:
|
|
12
|
-
file_arg = kwargs.get(file_arg_name)
|
|
13
|
-
|
|
14
|
-
if not isinstance(file_arg, UploadFile):
|
|
15
|
-
return await func(*args, **kwargs)
|
|
16
|
-
|
|
17
|
-
file: UploadFile = file_arg
|
|
18
|
-
|
|
19
|
-
file_size: int | None = file.size
|
|
20
|
-
|
|
21
|
-
if not file_size:
|
|
22
|
-
return await func(*args, **kwargs)
|
|
23
|
-
|
|
24
|
-
if file_size > max_size_bytes:
|
|
25
|
-
raise HTTPException(
|
|
26
|
-
status_code=status.HTTP_413_CONTENT_TOO_LARGE,
|
|
27
|
-
detail=f"file size is bigger than {max_size_mb}MB",
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
return await func(*args, **kwargs)
|
|
31
|
-
|
|
32
|
-
return wrapper
|
|
33
|
-
|
|
34
|
-
return decorator
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|