forked from pylover/sqlalchemy-media
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
26 changed files
with
479 additions
and
107 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
-r requirements-optional.txt | ||
nose | ||
coverage | ||
sphinx | ||
coveralls |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
python-magic >= 0.4.12 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
|
||
|
||
from sqlalchemy_media.attachments.file import File | ||
from sqlalchemy_media.constants import MB, KB | ||
|
||
|
||
class Image(File): | ||
__directory__ = 'images' | ||
__prefix__ = 'image' | ||
|
||
max_length = 2*MB | ||
min_length = 4*KB |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .attachable import AttachableDescriptor |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
|
||
import cgi | ||
|
||
from sqlalchemy_media.typing import Attachable | ||
from sqlalchemy_media.helpers import is_uri | ||
from sqlalchemy_media.descriptors.base import BaseDescriptor | ||
from sqlalchemy_media.descriptors.localfs import LocalFileSystemDescriptor | ||
from sqlalchemy_media.descriptors.cgi_fieldstorage import CgiFieldStorageDescriptor | ||
from sqlalchemy_media.descriptors.url import UrlDescriptor | ||
from sqlalchemy_media.descriptors.stream import StreamDescriptor | ||
|
||
|
||
# noinspection PyAbstractClass | ||
class AttachableDescriptor(BaseDescriptor): | ||
|
||
# noinspection PyInitNewSignature | ||
def __new__(cls, attachable: Attachable, *args, **kwargs): | ||
""" | ||
Should determine the appropriate descriptor and return an instance of it. | ||
:param attachable: | ||
:param args: | ||
:param kwargs: | ||
:return: | ||
""" | ||
|
||
if isinstance(attachable, cgi.FieldStorage): | ||
return_type = CgiFieldStorageDescriptor | ||
elif isinstance(attachable, str): | ||
return_type = UrlDescriptor if is_uri(attachable) else LocalFileSystemDescriptor | ||
else: | ||
return_type = StreamDescriptor | ||
|
||
return return_type(attachable, *args, **kwargs) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
|
||
import io | ||
import mimetypes | ||
|
||
from sqlalchemy_media.exceptions import MaximumLengthIsReachedError | ||
|
||
|
||
class BaseDescriptor(object): | ||
__header_buffer_size__ = 1024 | ||
header = None | ||
original_filename = None | ||
extension = None | ||
|
||
def __init__(self, max_length: int=None, content_type: str=None, content_length: int=None, extension: str=None, | ||
**kwargs): | ||
self.max_length = max_length | ||
self.content_type = content_type | ||
self.content_length = content_length | ||
self.header = io.BytesIO(self._read_source(self.__header_buffer_size__)) | ||
|
||
for k, v in kwargs.items(): | ||
setattr(self, k, v) | ||
|
||
if extension: | ||
self.extension = extension | ||
elif self.original_filename: | ||
self.extension = mimetypes.guess_extension(self.original_filename) | ||
|
||
def __enter__(self): | ||
return self | ||
|
||
def __exit__(self, exc_type, exc_val, exc_tb): | ||
self.close() | ||
|
||
def read(self, size): | ||
if not self.header: | ||
return self._read_source(size) | ||
|
||
current_cursor = self.header.tell() | ||
cursor_after_read = current_cursor + size | ||
source_cursor = self._tell_source() | ||
|
||
if self.max_length is not None and source_cursor > self.max_length: | ||
raise MaximumLengthIsReachedError(self.max_length) | ||
|
||
if source_cursor > self.__header_buffer_size__ or current_cursor == self.__header_buffer_size__: | ||
return self._read_source(size) | ||
|
||
if cursor_after_read > self.__header_buffer_size__: | ||
# split the read, half from header & half from source | ||
part1 = self.header.read() | ||
part2 = self._read_source(size - len(part1)) | ||
return part1 + part2 | ||
return self.header.read(size) | ||
|
||
def tell(self): | ||
source_cursor = self._tell_source() | ||
if not self.header: | ||
return source_cursor | ||
|
||
if source_cursor > self.header.tell(): | ||
return self.header.tell() | ||
return source_cursor | ||
|
||
def _tell_source(self): | ||
raise NotImplementedError() | ||
|
||
def _read_source(self, size): | ||
raise NotImplementedError() | ||
|
||
def seek(self, position): | ||
raise NotImplementedError('Seek operation is not supported by this object: %r' % self) | ||
|
||
def close(self): | ||
raise NotImplementedError() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
|
||
from cgi import FieldStorage | ||
|
||
from sqlalchemy_media.descriptors.stream import CloserStreamDescriptor | ||
|
||
|
||
class CgiFieldStorageDescriptor(CloserStreamDescriptor): | ||
|
||
def __init__(self, storage: FieldStorage, content_type: str=None, **kwargs): | ||
self.original_filename = storage.filename | ||
if content_type is None: | ||
content_type = storage.headers['Content-Type'] | ||
|
||
super().__init__(storage.file, content_type=content_type, **kwargs) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
|
||
import mimetypes | ||
|
||
from sqlalchemy_media.descriptors.stream import CloserStreamDescriptor | ||
|
||
|
||
class LocalFileSystemDescriptor(CloserStreamDescriptor): | ||
|
||
def __init__(self, filename: str, content_type: str=None, **kwargs): | ||
self.original_filename = filename | ||
if content_type is None: | ||
content_type = mimetypes.guess_type(filename)[0] | ||
|
||
super().__init__(open(filename, 'rb'), content_type=content_type, **kwargs) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
|
||
|
||
from sqlalchemy_media.typing import Stream | ||
from sqlalchemy_media.descriptors.base import BaseDescriptor | ||
|
||
|
||
class StreamDescriptor(BaseDescriptor): | ||
|
||
def __init__(self, stream: Stream, **kwargs): | ||
self._file = stream | ||
super().__init__(**kwargs) | ||
|
||
def _tell_source(self) -> int: | ||
return self._file.tell() | ||
|
||
def _read_source(self, size: int) -> bytes: | ||
return self._file.read(size) | ||
|
||
def seek(self, position: int): | ||
self._file.seek(position) | ||
|
||
def close(self) -> None: | ||
""" | ||
Do not closing the stream here, because we'r not upened it. | ||
:return: | ||
""" | ||
pass | ||
|
||
|
||
class CloserStreamDescriptor(StreamDescriptor): | ||
|
||
def close(self) -> None: | ||
self._file.close() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
|
||
from urllib.request import urlopen | ||
|
||
from sqlalchemy_media.descriptors.stream import CloserStreamDescriptor | ||
|
||
|
||
class UrlDescriptor(CloserStreamDescriptor): | ||
def __init__(self, uri: str, content_type: str=None, **kwargs): | ||
self.original_filename = uri | ||
response = urlopen(uri) | ||
|
||
if content_type is None and 'Content-Type' in response.headers: | ||
content_type = response.headers.get('Content-Type') | ||
|
||
if 'Content-Length' in response.headers: | ||
kwargs['content_length'] = int(response.headers.get('Content-Length')) | ||
|
||
super().__init__(response, content_type = content_type, ** kwargs) | ||
|
Oops, something went wrong.