As Elrond points out, we might as well .pop() default_handler
[mediagoblin.git] / mediagoblin / tools / workbench.py
CommitLineData
68784b9e 1# GNU MediaGoblin -- federated, autonomous media hosting
cf29e8a8 2# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
68784b9e
CAW
3#
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.
8#
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.
13#
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/>.
16
678d1a20
CAW
17import os
18import shutil
68784b9e
CAW
19import tempfile
20
21
678d1a20
CAW
22# Actual workbench stuff
23# ----------------------
68784b9e 24
52426ae0 25class Workbench(object):
68784b9e 26 """
52426ae0 27 Represent the directory for the workbench
b67a983a
E
28
29 WARNING: DO NOT create Workbench objects on your own,
30 let the WorkbenchManager do that for you!
68784b9e 31 """
52426ae0 32 def __init__(self, dir):
b67a983a
E
33 """
34 WARNING: DO NOT create Workbench objects on your own,
35 let the WorkbenchManager do that for you!
36 """
52426ae0 37 self.dir = dir
68784b9e 38
52426ae0
E
39 def __unicode__(self):
40 return unicode(self.dir)
243c3843 41
52426ae0
E
42 def __str__(self):
43 return str(self.dir)
243c3843 44
52426ae0 45 def __repr__(self):
26729e02
JW
46 try:
47 return str(self)
48 except AttributeError:
49 return 'None'
68784b9e 50
52426ae0
E
51 def joinpath(self, *args):
52 return os.path.join(self.dir, *args)
68784b9e 53
52426ae0 54 def localized_file(self, storage, filepath,
68ffb136
CAW
55 filename_if_copying=None,
56 keep_extension_if_copying=True):
68784b9e
CAW
57 """
58 Possibly localize the file from this storage system (for read-only
59 purposes, modifications should be written to a new file.).
60
61 If the file is already local, just return the absolute filename of that
62 local file. Otherwise, copy the file locally to the workbench, and
63 return the absolute path of the new file.
64
678d1a20
CAW
65 If it is copying locally, we might want to require a filename like
66 "source.jpg" to ensure that we won't conflict with other filenames in
67 our workbench... if that's the case, make sure filename_if_copying is
68 set to something like 'source.jpg'. Relatedly, if you set
69 keep_extension_if_copying, you don't have to set an extension on
70 filename_if_copying yourself, it'll be set for you (assuming such an
71 extension can be extacted from the filename in the filepath).
72
68784b9e 73 Returns:
fdc50039 74 localized_filename
678d1a20
CAW
75
76 Examples:
68ffb136 77 >>> wb_manager.localized_file(
678d1a20
CAW
78 ... '/our/workbench/subdir', local_storage,
79 ... ['path', 'to', 'foobar.jpg'])
fdc50039 80 u'/local/storage/path/to/foobar.jpg'
678d1a20 81
68ffb136 82 >>> wb_manager.localized_file(
678d1a20
CAW
83 ... '/our/workbench/subdir', remote_storage,
84 ... ['path', 'to', 'foobar.jpg'])
fdc50039 85 '/our/workbench/subdir/foobar.jpg'
678d1a20 86
68ffb136 87 >>> wb_manager.localized_file(
678d1a20
CAW
88 ... '/our/workbench/subdir', remote_storage,
89 ... ['path', 'to', 'foobar.jpg'], 'source.jpeg', False)
fdc50039 90 '/our/workbench/subdir/foobar.jpeg'
678d1a20 91
68ffb136 92 >>> wb_manager.localized_file(
678d1a20
CAW
93 ... '/our/workbench/subdir', remote_storage,
94 ... ['path', 'to', 'foobar.jpg'], 'source', True)
fdc50039 95 '/our/workbench/subdir/foobar.jpg'
68784b9e 96 """
678d1a20 97 if storage.local_storage:
fdc50039 98 return storage.get_local_path(filepath)
678d1a20
CAW
99 else:
100 if filename_if_copying is None:
101 dest_filename = filepath[-1]
102 else:
103 orig_filename, orig_ext = os.path.splitext(filepath[-1])
104 if keep_extension_if_copying and orig_ext:
f2b96ff0 105 dest_filename = filename_if_copying + orig_ext
678d1a20
CAW
106 else:
107 dest_filename = filename_if_copying
108
109 full_dest_filename = os.path.join(
52426ae0 110 self.dir, dest_filename)
678d1a20
CAW
111
112 # copy it over
113 storage.copy_locally(
114 filepath, full_dest_filename)
115
fdc50039 116 return full_dest_filename
52426ae0 117
bd6fe977 118 def destroy(self):
b67a983a
E
119 """
120 Destroy this workbench! Deletes the directory and all its contents!
121
122 WARNING: Does no checks for a sane value in self.dir!
123 """
124 # just in case
125 workbench = os.path.abspath(self.dir)
b67a983a 126 shutil.rmtree(workbench)
b67a983a
E
127 del self.dir
128
c11c1994
SS
129 def __enter__(self):
130 """Make Workbench a context manager so we can use `with Workbench() as bench:`"""
131 return self
132
133 def __exit__(self, *args):
134 """Clean up context manager, aka ourselves, deleting the workbench"""
bd6fe977 135 self.destroy()
c11c1994 136
52426ae0
E
137
138class WorkbenchManager(object):
139 """
140 A system for generating and destroying workbenches.
141
c11c1994
SS
142 Workbenches are actually just subdirectories of a (local) temporary
143 storage space for during the processing stage. The preferred way to
144 create them is to use:
145
bd6fe977 146 with workbenchmger.create() as workbench:
c11c1994
SS
147 do stuff...
148
149 This will automatically clean up all temporary directories even in
150 case of an exceptions. Also check the
151 @mediagoblin.decorators.get_workbench decorator for a convenient
152 wrapper.
52426ae0
E
153 """
154
155 def __init__(self, base_workbench_dir):
156 self.base_workbench_dir = os.path.abspath(base_workbench_dir)
157 if not os.path.exists(self.base_workbench_dir):
158 os.makedirs(self.base_workbench_dir)
243c3843 159
bd6fe977 160 def create(self):
52426ae0
E
161 """
162 Create and return the path to a new workbench (directory).
163 """
164 return Workbench(tempfile.mkdtemp(dir=self.base_workbench_dir))