~mterry/duplicity/gdrive

« back to all changes in this revision

Viewing changes to duplicity/misc.py

  • Committer: bescoto
  • Date: 2002-10-29 01:49:46 UTC
  • Revision ID: vcs-imports@canonical.com-20021029014946-3m4rmm5plom7pl6q
Initial checkin

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2002 Ben Escoto
 
2
#
 
3
# This file is part of duplicity.
 
4
#
 
5
# duplicity is free software; you can redistribute it and/or modify it
 
6
# under the terms of the GNU General Public License as published by
 
7
# the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA
 
8
# 02139, USA; either version 2 of the License, or (at your option) any
 
9
# later version; incorporated herein by reference.
 
10
 
 
11
"""Miscellaneous classes and methods"""
 
12
 
 
13
import os
 
14
import log
 
15
 
 
16
class MiscError(Exception):
 
17
        """Signifies a miscellaneous error..."""
 
18
        pass
 
19
 
 
20
 
 
21
class FileVolumeWriter:
 
22
        """Split up an incoming fileobj into multiple volumes on disk
 
23
 
 
24
        This class can also be used as an iterator.  It returns the
 
25
        filenames of the files it writes.
 
26
 
 
27
        """
 
28
        volume_size = 50 * 1024 * 1024
 
29
        blocksize = 64 * 1024
 
30
        def __init__(self, infp, file_prefix):
 
31
                """FileVolumeWriter initializer
 
32
 
 
33
                infp is a file object opened for reading.  It will be closed
 
34
                at end.  file_prefix is the full path of the volumes that will
 
35
                be written.  If more than one is required, it will be appended
 
36
                with .1, .2, etc.
 
37
 
 
38
                """
 
39
                self.infp = infp
 
40
                self.prefix = file_prefix
 
41
                self.current_index = 1
 
42
                self.finished = None # set to true when completely done
 
43
                self.buffer = "" # holds data that belongs in next volume
 
44
 
 
45
        def get_initial_buf(self):
 
46
                """Get first value of buffer, from self.buffer or infp"""
 
47
                if self.buffer:
 
48
                        buf = self.buffer
 
49
                        self.buffer = ""
 
50
                        return buf
 
51
                else: return self.infp.read(self.blocksize)
 
52
 
 
53
        def write_volume(self, outfp):
 
54
                """Write self.volume_size bytes from self.infp to outfp
 
55
 
 
56
                Return None if we have reached end of infp without reaching
 
57
                volume size, and false otherwise.
 
58
 
 
59
                """
 
60
                bytes_written, buf = 0, self.get_initial_buf()
 
61
                while len(buf) + bytes_written <= self.volume_size:
 
62
                        if not buf: # reached end of input
 
63
                                outfp.close()
 
64
                                return None
 
65
                        if len(buf) + bytes_written > self.volume_size: break
 
66
                        outfp.write(buf)
 
67
                        bytes_written += len(buf)
 
68
                        buf = self.infp.read(self.blocksize)
 
69
 
 
70
                remainder = self.volume_size - bytes_written
 
71
                assert remainder < len(buf)
 
72
                outfp.write(buf[:remainder])
 
73
                outfp.close()
 
74
                self.buffer = buf[remainder:]
 
75
                return 1
 
76
 
 
77
        def next(self):
 
78
                """Write next file, return filename"""
 
79
                if self.finished: raise StopIteration
 
80
 
 
81
                filename = "%s.%d" % (self.prefix, self.current_index)
 
82
                log.Log("Starting to write %s" % filename, 5)
 
83
                outfp = open(filename, "wb")
 
84
 
 
85
                if not self.write_volume(outfp): # end of input
 
86
                        self.finished = 1
 
87
                        if self.current_index == 1: # special case first index
 
88
                                log.Log("One only volume required.\n"
 
89
                                                "Renaming %s to %s" % (filename, self.prefix), 4)
 
90
                                os.rename(filename, self.prefix)
 
91
                                return self.prefix
 
92
                else: self.current_index += 1
 
93
                return filename
 
94
 
 
95
        def __iter__(self): return self
 
96
 
 
97
 
 
98
class BufferedFile:
 
99
        """Buffer file open for reading, so reads will happen in fixed sizes
 
100
 
 
101
        This is currently used to buffer a GzipFile, because that class
 
102
        apparently doesn't respond well to arbitrary read sizes.
 
103
 
 
104
        """
 
105
        def __init__(self, fileobj, blocksize = 32 * 1024):
 
106
                self.fileobj = fileobj
 
107
                self.buffer = ""
 
108
                self.blocksize = blocksize
 
109
 
 
110
        def read(self, length = -1):
 
111
                """Return length bytes, or all if length < 0"""
 
112
                if length < 0:
 
113
                        while 1:
 
114
                                buf = self.fileobj.read(self.blocksize)
 
115
                                if not buf: break
 
116
                                self.buffer += buf
 
117
                        real_length = len(self.buffer)
 
118
                else:
 
119
                        while len(self.buffer) < length:
 
120
                                buf = self.fileobj.read(self.blocksize)
 
121
                                if not buf: break
 
122
                                self.buffer += buf
 
123
                        real_length = min(length, len(self.buffer))
 
124
                result = self.buffer[:real_length]
 
125
                self.buffer = self.buffer[real_length:]
 
126
                return result
 
127
 
 
128
        def close(self): self.fileobj.close()
 
129
                
 
130
        
 
131
def copyfileobj(infp, outfp, byte_count = -1):
 
132
        """Copy byte_count bytes from infp to outfp, or all if byte_count < 0
 
133
 
 
134
        Returns the number of bytes actually written (may be less than
 
135
        byte_count if find eof.  Does not close either fileobj.
 
136
 
 
137
        """
 
138
        blocksize = 32 * 1024
 
139
        bytes_written = 0
 
140
        if byte_count < 0:
 
141
                while 1:
 
142
                        buf = infp.read(blocksize)
 
143
                        if not buf: break
 
144
                        bytes_written += len(buf)
 
145
                        outfp.write(buf)
 
146
        else:
 
147
                while bytes_written + blocksize <= byte_count:
 
148
                        buf = infp.read(blocksize)
 
149
                        if not buf: break
 
150
                        bytes_written += len(buf)
 
151
                        outfp.write(buf)
 
152
                buf = infp.read(byte_count - bytes_written)
 
153
                bytes_written += len(buf)
 
154
                outfp.write(buf)
 
155
        return bytes_written
 
156
 
 
157
def copyfileobj_close(infp, outfp):
 
158
        """Copy infp to outfp, closing afterwards"""
 
159
        copyfileobj(infp, outfp)
 
160
        if infp.close(): raise MiscError("Error closing input file")
 
161
        if outfp.close(): raise MiscError("Error closing output file")
 
162
 
 
163