]> git.decadent.org.uk Git - dak.git/blob - daklib/filewriter.py
daklib/filewriter.py: indices do not need to be group-writable
[dak.git] / daklib / filewriter.py
1 #!/usr/bin/env python
2 """
3 Helper code for file writing with optional compression.
4
5 @contact: Debian FTPMaster <ftpmaster@debian.org>
6 @copyright: 2011 Torsten Werner <twerner@debian.org>
7 @license: GNU General Public License version 2 or later
8 """
9
10 ################################################################################
11
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 2 of the License, or
15 # (at your option) any later version.
16
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 # GNU General Public License for more details.
21
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
26 ################################################################################
27
28 from daklib.config import Config
29
30 from daklib.daksubprocess import check_call
31
32 import os, os.path
33
34 class BaseFileWriter(object):
35     '''
36     Base class for compressed and uncompressed file writing.
37     '''
38     def __init__(self, template, **keywords):
39         '''
40         The template argument is a string template like
41         "dists/%(suite)s/%(component)s/Contents-%(architecture)s.gz" that
42         should be relative to the archive's root directory. The keywords
43         include strings for suite, component, architecture and booleans
44         uncompressed, gzip, bzip2.
45         '''
46         compression = keywords.get('compression', ['none'])
47         self.uncompressed = 'none' in compression
48         self.gzip = 'gzip' in compression
49         self.bzip2 = 'bzip2' in compression
50         self.xz = 'xz' in compression
51         self.path = template % keywords
52
53     def open(self):
54         '''
55         Returns a file object for writing.
56         '''
57         # create missing directories
58         try:
59             os.makedirs(os.path.dirname(self.path))
60         except:
61             pass
62         self.file = open(self.path + '.new', 'w')
63         return self.file
64
65     # internal helper function
66     def rename(self, filename):
67         tempfilename = filename + '.new'
68         os.chmod(tempfilename, 0o644)
69         os.rename(tempfilename, filename)
70
71     # internal helper function to compress output
72     def compress(self, cmd, suffix, path):
73         in_filename = "{0}.new".format(path)
74         out_filename = "{0}.{1}.new".format(path, suffix)
75         with open(in_filename, 'r') as in_fh, open(out_filename, 'w') as out_fh:
76             check_call(cmd, stdin=in_fh, stdout=out_fh)
77         self.rename("{0}.{1}".format(path, suffix))
78
79     def close(self):
80         '''
81         Closes the file object and does the compression and rename work.
82         '''
83         self.file.close()
84         if self.gzip:
85             self.compress(['gzip', '-9cn', '--rsyncable'], 'gz', self.path)
86         if self.bzip2:
87             self.compress(['bzip2', '-9'], 'bz2', self.path)
88         if self.xz:
89             self.compress(['xz', '-c'], 'xz', self.path)
90         if self.uncompressed:
91             self.rename(self.path)
92         else:
93             os.unlink(self.path + '.new')
94
95 class BinaryContentsFileWriter(BaseFileWriter):
96     def __init__(self, **keywords):
97         '''
98         The value of the keywords suite, component, and architecture are
99         strings. The value of component may be omitted if not applicable.
100         Output files are gzip compressed only.
101         '''
102         flags = {
103             'compression': ['gzip'],
104         }
105         flags.update(keywords)
106         if flags['debtype'] == 'deb':
107             template = "%(archive)s/dists/%(suite)s/%(component)s/Contents-%(architecture)s"
108         else: # udeb
109             template = "%(archive)s/dists/%(suite)s/%(component)s/Contents-udeb-%(architecture)s"
110         BaseFileWriter.__init__(self, template, **flags)
111
112 class SourceContentsFileWriter(BaseFileWriter):
113     def __init__(self, **keywords):
114         '''
115         The value of the keywords suite and component are strings.
116         Output files are gzip compressed only.
117         '''
118         flags = {
119             'compression': ['gzip'],
120         }
121         flags.update(keywords)
122         template = "%(archive)s/dists/%(suite)s/%(component)s/Contents-source"
123         BaseFileWriter.__init__(self, template, **flags)
124
125 class PackagesFileWriter(BaseFileWriter):
126     def __init__(self, **keywords):
127         '''
128         The value of the keywords suite, component, debtype and architecture
129         are strings.  Output files are gzip compressed only.
130         '''
131         flags = {
132             'compression': ['gzip', 'xz'],
133         }
134         flags.update(keywords)
135         if flags['debtype'] == 'deb':
136             template = "%(archive)s/dists/%(suite)s/%(component)s/binary-%(architecture)s/Packages"
137         else: # udeb
138             template = "%(archive)s/dists/%(suite)s/%(component)s/debian-installer/binary-%(architecture)s/Packages"
139         BaseFileWriter.__init__(self, template, **flags)
140
141 class SourcesFileWriter(BaseFileWriter):
142     def __init__(self, **keywords):
143         '''
144         The value of the keywords suite and component are strings. Output
145         files are gzip compressed only.
146         '''
147         flags = {
148             'compression': ['gzip', 'xz'],
149         }
150         flags.update(keywords)
151         template = "%(archive)s/dists/%(suite)s/%(component)s/source/Sources"
152         BaseFileWriter.__init__(self, template, **flags)
153
154 class TranslationFileWriter(BaseFileWriter):
155     def __init__(self, **keywords):
156         '''
157         The value of the keywords suite, component and language are strings.
158         Output files are bzip2 compressed only.
159         '''
160         flags = {
161             'compression': ['bzip2'],
162             'language':     'en',
163         }
164         flags.update(keywords)
165         template = "%(archive)s/dists/%(suite)s/%(component)s/i18n/Translation-%(language)s"
166         super(TranslationFileWriter, self).__init__(template, **flags)