Skip to content

Commit f831ff7

Browse files
lesnik512Artur Shiriev
andauthored
migrate to python-magic (#23)
Co-authored-by: Artur Shiriev <me@shiriev.ru>
1 parent 226d4ec commit f831ff7

File tree

11 files changed

+155
-34
lines changed

11 files changed

+155
-34
lines changed

.dockerignore

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
.env
2+
.coverage
3+
.gitignore
4+
.idea
5+
.mypy_cache
6+
.ruff_cache
7+
.vscode
8+
.git
9+
.pytest_cache
10+
.DS_Store
11+
*.yml
12+
Dockerfile
13+
**/__pycache__
14+
.hypothesis
15+
.venv

.github/workflows/ci.yml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: main
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request: {}
8+
9+
concurrency:
10+
group: ${{ github.head_ref || github.run_id }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
lint:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v4
18+
- uses: extractions/setup-just@v2
19+
- uses: astral-sh/setup-uv@v3
20+
with:
21+
enable-cache: true
22+
cache-dependency-glob: "**/pyproject.toml"
23+
- run: uv python install 3.10
24+
- run: just install lint-ci
25+
26+
pytest:
27+
runs-on: ubuntu-latest
28+
strategy:
29+
fail-fast: false
30+
matrix:
31+
python-version:
32+
- "3.10"
33+
- "3.11"
34+
- "3.12"
35+
- "3.13"
36+
steps:
37+
- name: install libmagic
38+
run: sudo apt install -y libmagic-dev
39+
- uses: actions/checkout@v4
40+
- uses: extractions/setup-just@v2
41+
- uses: astral-sh/setup-uv@v3
42+
with:
43+
enable-cache: true
44+
cache-dependency-glob: "**/pyproject.toml"
45+
- run: uv python install ${{ matrix.python-version }}
46+
- run: just install
47+
- run: just test-ci . --cov=. --cov-report xml
48+
- uses: codecov/codecov-action@v4.0.1
49+
env:
50+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
51+
with:
52+
files: ./coverage.xml
53+
flags: unittests
54+
name: codecov-${{ matrix.python-version }}

.github/workflows/publish.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: Publish Package
2+
3+
on:
4+
release:
5+
types:
6+
- published
7+
8+
jobs:
9+
publish:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
- uses: extractions/setup-just@v2
14+
- uses: astral-sh/setup-uv@v3
15+
- run: just publish
16+
env:
17+
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}

.github/workflows/workflow.yml

Lines changed: 0 additions & 20 deletions
This file was deleted.

Dockerfile

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
FROM python:3.13-slim
2+
3+
# required for python-magic
4+
RUN apt update \
5+
&& apt install -y --no-install-recommends \
6+
libmagic-dev \
7+
&& apt clean \
8+
&& rm -rf /var/lib/apt/lists/*
9+
10+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
11+
RUN useradd --no-create-home --gid root runner
12+
13+
ENV UV_PYTHON_PREFERENCE=only-system
14+
ENV UV_NO_CACHE=true
15+
16+
WORKDIR /code
17+
18+
COPY pyproject.toml .
19+
COPY uv.lock .
20+
21+
RUN uv sync --all-extras --frozen --no-install-project
22+
23+
COPY . .
24+
25+
RUN chown -R runner:root /code && chmod -R g=u /code
26+
27+
USER runner

Justfile

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
default: install lint test
22

3+
down:
4+
docker compose down --remove-orphans
5+
6+
sh:
7+
docker compose run --service-ports application bash
8+
9+
test *args: down && down
10+
docker compose run application uv run --no-sync pytest {{ args }}
11+
12+
build:
13+
docker compose build application
14+
315
install:
416
uv lock --upgrade
517
uv sync --frozen --all-groups
@@ -16,7 +28,7 @@ lint-ci:
1628
uv run --group lint ruff check --no-fix
1729
uv run --group lint mypy .
1830

19-
test *args:
31+
test-ci *args:
2032
uv run pytest {{ args }}
2133

2234
publish:

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# safe-s3-storage
22

3-
S3 tools for uploading files to S3 safely (antivirus check, etc) as well as downloading and deleting files.
3+
S3 tools for uploading files to S3 safely (antivirus check, etc.) as well as downloading and deleting files.
44

55
## How To Use
66

docker-compose.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
services:
2+
application:
3+
build:
4+
context: .
5+
dockerfile: ./Dockerfile
6+
restart: always
7+
volumes:
8+
- .:/code
9+
- /code/.venv
10+
stdin_open: true
11+
tty: true

pyproject.toml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ keywords = ["s3", "kaspersky", "antivirus", "upload"]
66
classifiers = [
77
"Natural Language :: English",
88
"Programming Language :: Python :: 3.10",
9+
"Programming Language :: Python :: 3.11",
910
"Programming Language :: Python :: 3.12",
1011
"Programming Language :: Python :: 3.13",
1112
"Topic :: Software Development :: Libraries",
@@ -22,12 +23,22 @@ dependencies = [
2223
"pydantic",
2324
"pyvips",
2425
"pyvips-binary",
25-
"magika",
26+
"python-magic",
2627
]
2728

2829
[dependency-groups]
29-
dev = ["anyio", "faker", "pytest", "pytest-cov"]
30-
lint = [{ include-group = "dev" }, "auto-typing-final", "mypy", "ruff"]
30+
dev = [
31+
"anyio",
32+
"faker",
33+
"pytest",
34+
"pytest-cov",
35+
]
36+
lint = [
37+
{ include-group = "dev" },
38+
"auto-typing-final",
39+
"mypy",
40+
"ruff",
41+
]
3142

3243
[build-system]
3344
requires = ["uv_build"]

safe_s3_storage/file_validator.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
import enum
33
import typing
44

5+
import magic
56
import pyvips # type: ignore[import-untyped]
6-
from magika import Magika
77

88
from safe_s3_storage import exceptions
99
from safe_s3_storage.kaspersky_scan_engine import KasperskyScanEngineClient
@@ -49,13 +49,7 @@ class FileValidator:
4949
excluded_conversion_formats: list[str] | None = None
5050

5151
def _validate_mime_type(self, *, file_name: str, file_content: bytes) -> str:
52-
mime_type_prediction: typing.Final = Magika().identify_bytes(file_content)
53-
if mime_type_prediction.output.is_text and file_name.endswith(".txt"):
54-
mime_type = "text/plain"
55-
elif mime_type_prediction.dl.extensions:
56-
mime_type = mime_type_prediction.dl.mime_type
57-
else:
58-
mime_type = mime_type_prediction.output.mime_type
52+
mime_type: typing.Final = magic.from_buffer(file_content, mime=True)
5953
if self.allowed_mime_types is None or mime_type in self.allowed_mime_types:
6054
return mime_type
6155

0 commit comments

Comments
 (0)