~ahayzen/cappella/cappella10_glade_handlers

« back to all changes in this revision

Viewing changes to cappella/engine/htd32/_workers/file_fix/file.py

* Pull of HTD3.2 Alpha 2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python3
 
2
'''
 
3
This file is part of HTD (HayzenTech Database)
 
4
 
 
5
Copyright (C) 2011 - 2013 Andrew Hayzen
 
6
 
 
7
HTD is free software: you can redistribute it and/or modify
 
8
it under the terms of the GNU Lesser General Public License as published by
 
9
the Free Software Foundation, either version 3 of the License, or
 
10
(at your option) any later version.
 
11
 
 
12
HTD is distributed in the hope that it will be useful,
 
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 
15
GNU Lesser General Public License for more details.
 
16
 
 
17
You should have received a copy of the GNU Lesser General Public License
 
18
along with HTD. If not, see <http://www.gnu.org/licenses/>
 
19
 
 
20
This document conforms to the PEP 8 Style Guide
 
21
'''
 
22
 
 
23
from ..._base.file import location_is_file, FileTools, LockFile
 
24
import os
 
25
from os import name, rename, unlink
 
26
from os.path import exists
 
27
from struct import pack, unpack
 
28
from sys import platform
 
29
 
 
30
# Load fsync
 
31
if platform == 'linux2':
 
32
    fsync = os.fdatasync
 
33
else:
 
34
    fsync = os.fsync
 
35
 
 
36
 
 
37
class FileWriter(LockFile):
 
38
    def __init__(self, location, header, new):
 
39
        LockFile.__init__(self)
 
40
 
 
41
        self.location = location
 
42
        self.offset = 0  # Offset until data
 
43
        self.width = 0  # Width of a record
 
44
        self.update_file_settings(header)
 
45
 
 
46
    def __delitem__(self, index):
 
47
        """ Remove the data or mark as deleted """
 
48
 
 
49
        self.delete_region(index, index + 1)
 
50
 
 
51
    def __getitem__(self, index):
 
52
        """ Get the data for the index """
 
53
 
 
54
        self.file.seek((index * self.width) + self.offset + 8)
 
55
        return self.file.read(self.width - 8)
 
56
 
 
57
    def __setitem__(self, index, data):
 
58
        """ Set the data for the index """
 
59
 
 
60
        self.file.seek((index * self.width) + self.offset + 8)
 
61
        self.file.write(data)
 
62
 
 
63
    def __len__(self):
 
64
        """ Number of items in the file """
 
65
 
 
66
        self.file.seek(0, 2)
 
67
        return (self.file.tell() - self.offset) // self.width
 
68
 
 
69
    def close(self, delete_files):
 
70
        if exists(self.location):
 
71
            self.flush()
 
72
 
 
73
        self.close_lock_file(delete_files)  # Deletes this file too
 
74
 
 
75
    def copy(self, location, replace):
 
76
        if replace is False and exists(location):
 
77
            raise IOError("Path already exists")
 
78
 
 
79
        if not location_is_file(location):
 
80
            raise ValueError("Location is not valid for this file format")
 
81
 
 
82
        with open(location, "wb") as f:
 
83
            self.file.seek(0)
 
84
            f.write(self.file.read())
 
85
 
 
86
    def delete_region(self, start, stop):
 
87
        """ Delete a region of data """
 
88
 
 
89
        self.file.seek(0, 2)
 
90
 
 
91
        FileTools.delete_region(self.file, (start * self.width) + self.offset,
 
92
                                (stop * self.width) + self.offset)
 
93
 
 
94
    def flush(self, fsync=False):
 
95
        if self.file is not None:
 
96
            self.file.flush()
 
97
 
 
98
            if fsync:
 
99
                fsync(self.file)
 
100
 
 
101
    def get_version(self, index):
 
102
        """ Get the version of the index """
 
103
 
 
104
        self.file.seek((index * self.width) + self.offset)
 
105
 
 
106
        return unpack("Q", self.file.read(8))[0]
 
107
 
 
108
    def insert(self, index, data):
 
109
        """ Insert the item into an index """
 
110
 
 
111
        position = (index * self.width) + self.offset
 
112
 
 
113
        data = b"".join([b"\x00" * 8, data])  # Insert blank version
 
114
 
 
115
        FileTools.insert_region(self.file, position, data)
 
116
 
 
117
    def iterate(self, i, stop):
 
118
        """ Iterate over a region in the file """
 
119
 
 
120
        reading = True
 
121
 
 
122
        while reading and i < stop:
 
123
            self.file.seek((i * self.width) + self.offset)
 
124
            data = self.file.read(self.width)
 
125
            reading = data != b""
 
126
            i += 1
 
127
 
 
128
            yield unpack("Q", data[:8])[0], data[8:]
 
129
 
 
130
        raise StopIteration
 
131
 
 
132
    def left(self, index, binary_search=False):
 
133
        """
 
134
        Get the index to the left of the index
 
135
        binary_search=True get the real left
 
136
        binary_search=False get the data left
 
137
        """
 
138
 
 
139
        if index - 1 > 0:
 
140
            return index - 1
 
141
        else:
 
142
            raise IndexError()
 
143
 
 
144
    def rename(self, location, replace):
 
145
        if replace is False and exists(location):
 
146
            raise IOError("Cannot rename because path already exists")
 
147
 
 
148
        if replace and name == "nt":  # Windows only rename if dst !exist
 
149
            unlink(location)
 
150
 
 
151
        rename(self.location, location)  # Rename the file
 
152
 
 
153
    def right(self, index, binary_search=False):
 
154
        """
 
155
        Get the index to the right of the index
 
156
        binary_search=True get the real right
 
157
        binary_search=False get the data right
 
158
        """
 
159
 
 
160
        if index + 1 < len(self):
 
161
            return index + 1
 
162
        else:
 
163
            raise IndexError()
 
164
 
 
165
    def set_left(self, index):
 
166
        """ Set the index to the left of the item """
 
167
 
 
168
        return True  # File is ordered so no need for setting positioning
 
169
 
 
170
    def set_right(self, index):
 
171
        """ Set the index to the right of the item """
 
172
 
 
173
        return True  # File is ordered so no need for setting positioning
 
174
 
 
175
    def set_version(self, index, version):
 
176
        """ Set the version of an index """
 
177
 
 
178
        self.file.seek((index * self.width) + self.offset)
 
179
        self.file.write(pack("Q", version or 0))
 
180
 
 
181
        return True
 
182
 
 
183
    def update_file_settings(self, header):
 
184
        """ Update the file settings from the header info """
 
185
 
 
186
        self.offset = header["SIZ"]
 
187
        self.width = sum([header["ATT"][i].size + 1
 
188
                          for i in header["ATT"]]) + 8