1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
|
import os
from typing import Any, Optional
from banal import decode_path
from mimetypes import guess_extension
from normality import slugify, safe_filename
from pantomime.mime import normalize_mimetype
from pantomime.types import DEFAULT
def normalize_extension(extension: Optional[str]) -> Optional[str]:
"""Normalise a file name extension."""
extension = decode_path(extension)
if extension is None:
return None
if extension.startswith("."):
extension = extension[1:]
if "." in extension:
_, extension = os.path.splitext(extension)
extension = slugify(extension, sep="")
if extension is None or not len(extension):
return None
return extension
def mimetype_extension(mime_type: Optional[str]) -> Optional[str]:
"""Infer a possible extension from a MIME type."""
mime_type = normalize_mimetype(mime_type)
if mime_type == DEFAULT:
return None
extension = guess_extension(mime_type)
return normalize_extension(extension)
class FileName(object):
FALLBACK = "data"
def __init__(self, file_name: Optional[str]):
self.file_name = file_name
self.base: Optional[str] = None
self.extension: Optional[str] = None
if file_name is not None:
self.base, ext = os.path.splitext(file_name)
self.extension = normalize_extension(ext)
self.has_extension = self.extension is not None
def safe(self, extension: Optional[str] = None) -> Optional[str]:
ext = extension or self.extension
default = "data.%s" % ext if ext else self.FALLBACK
return safe_filename(self.file_name, default=default, extension=ext)
def __str__(self) -> str:
return self.file_name or self.FALLBACK
def __repr__(self) -> str:
return "<FileName(%r)" % self.safe()
|