1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011 MediaGoblin contributors. See AUTHORS.
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Affero General Public License for more details.
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 Make it so that ``import cloudfiles`` does not pick THIS file, but the
19 python-cloudfiles one.
21 http://docs.python.org/whatsnew/2.5.html#pep-328-absolute-and-relative-imports
23 from __future__
import absolute_import
25 from mediagoblin
.storage
import StorageInterface
, clean_listy_filepath
31 class CloudFilesStorage(StorageInterface
):
33 OpenStack/Rackspace Cloud's Swift/CloudFiles support
38 def __init__(self
, **kwargs
):
39 self
.param_container
= kwargs
.get('cloudfiles_container')
40 self
.param_user
= kwargs
.get('cloudfiles_user')
41 self
.param_api_key
= kwargs
.get('cloudfiles_api_key')
42 self
.param_host
= kwargs
.get('cloudfiles_host')
43 self
.param_use_servicenet
= kwargs
.get('cloudfiles_use_servicenet')
45 if not self
.param_host
:
46 print('No CloudFiles host URL specified, '
47 'defaulting to Rackspace US')
49 self
.connection
= cloudfiles
.get_connection(
50 username
=self
.param_user
,
51 api_key
=self
.param_api_key
,
52 servicenet
=True if self
.param_use_servicenet
== 'true' or \
53 self
.param_use_servicenet
== True else False)
55 if not self
.param_container
== \
56 self
.connection
.get_container(self
.param_container
):
57 self
.container
= self
.connection
.create_container(
59 self
.container
.make_public(
62 self
.container
= self
.connection
.get_container(
65 self
.container_uri
= self
.container
.public_uri()
67 def _resolve_filepath(self
, filepath
):
69 clean_listy_filepath(filepath
))
71 def file_exists(self
, filepath
):
73 object = self
.container
.get_object(
74 self
._resolve
_filepath
(filepath
))
76 except cloudfiles
.errors
.NoSuchObject
:
79 def get_file(self
, filepath
, *args
, **kwargs
):
81 - Doesn't care about the "mode" argument
84 obj
= self
.container
.get_object(
85 self
._resolve
_filepath
(filepath
))
86 except cloudfiles
.errors
.NoSuchObject
:
87 obj
= self
.container
.create_object(
88 self
._resolve
_filepath
(filepath
))
90 mimetype
= mimetypes
.guess_type(
94 obj
.content_type
= mimetype
[0]
96 return CloudFilesStorageObjectWrapper(obj
, *args
, **kwargs
)
98 def delete_file(self
, filepath
):
99 # TODO: Also delete unused directories if empty (safely, with
100 # checks to avoid race conditions).
101 self
.container
.delete_object(
102 self
._resolve
_filepath
(filepath
))
104 def file_url(self
, filepath
):
107 self
._resolve
_filepath
(filepath
)])
110 class CloudFilesStorageObjectWrapper():
112 Wrapper for python-cloudfiles's cloudfiles.storage_object.Object
113 used to circumvent the mystic `medium.jpg` corruption issue, where
114 we had both python-cloudfiles and PIL doing buffering on both
115 ends and that breaking things.
117 This wrapper currently meets mediagoblin's needs for a public_store
120 def __init__(self
, storage_object
, *args
, **kwargs
):
121 self
.storage_object
= storage_object
123 def read(self
, *args
, **kwargs
):
124 return self
.storage_object
.read(*args
, **kwargs
)
126 def write(self
, data
, *args
, **kwargs
):
128 write data to the cloudfiles storage object
130 The original motivation for this wrapper is to ensure
131 that buffered writing to a cloudfiles storage object does not overwrite
132 any preexisting data.
134 Currently this method does not support any write modes except "append".
135 However if we should need it it would be easy implement.
137 if self
.storage_object
.size
and type(data
) == str:
138 data
= self
.read() + data
140 self
.storage_object
.write(data
, *args
, **kwargs
)
147 Context Manager API implementation
148 http://docs.python.org/library/stdtypes.html#context-manager-types
152 def __exit__(self
, *exc_info
):
154 Context Manger API implementation