/usr/lib/python3/dist-packages/defcon/objects/dataSet.py is in python3-defcon 0.3.5-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | from __future__ import absolute_import
import os
import weakref
from ufoLib import UFOReader, UFOLibError
from defcon.objects.base import BaseObject
class DataSet(BaseObject):
"""
This object manages all contents of the data directory in the font.
**This object posts the following notifications:**
===============
Name
===============
DataSet.Changed
===============
"""
changeNotificationName = "DataSet.Changed"
representationFactories = {}
def __init__(self, font=None):
self._font = None
if font is not None:
self._font = weakref.ref(font)
super(DataSet, self).__init__()
self.beginSelfNotificationObservation()
self._data = {}
self._scheduledForDeletion = {}
# --------------
# Parent Objects
# --------------
def getParent(self):
return self.font
def _get_font(self):
if self._font is not None:
return self._font()
return None
font = property(_get_font, doc="The :class:`Font` that this object belongs to.")
# ----------
# File Names
# ----------
def _get_fileNames(self):
return list(self._data.keys())
def _set_fileNames(self, fileNames):
assert not self._data
for fileName in fileNames:
self._data[fileName] = _dataDict()
fileNames = property(_get_fileNames, _set_fileNames, doc="A list of all file names. This should not be set externally.")
# -------------
# Dict Behavior
# -------------
def __getitem__(self, fileName):
if self._data[fileName]["data"] is None:
path = self.font.path
reader = UFOReader(path)
path = os.path.join("data", fileName)
data = reader.readBytesFromPath(path)
onDiskModTime = reader.getFileModificationTime(path)
self._data[fileName] = _dataDict(data=data, onDisk=True, onDiskModTime=onDiskModTime)
return self._data[fileName]["data"]
def __setitem__(self, fileName, data):
assert data is not None
onDisk = False
onDiskModTime = None
if fileName in self._scheduledForDeletion:
assert fileName not in self._data
self._data[fileName] = self._scheduledForDeletion.pop(fileName)
if fileName in self._data:
n = self[fileName] # force it to load so that the stamping is correct
onDisk = self._data[fileName]["onDisk"]
onDiskModTime = self._data[fileName]["onDiskModTime"]
del self._data[fileName] # now remove it
self._data[fileName] = _dataDict(data=data, dirty=True, onDisk=onDisk, onDiskModTime=onDiskModTime)
self.dirty = True
def __delitem__(self, fileName):
n = self[fileName] # force it to load so that the stamping is correct]
self._scheduledForDeletion[fileName] = dict(self._data.pop(fileName))
self.dirty = True
# ----
# Save
# ----
def getSaveProgressBarTickCount(self, formatVersion):
"""
Get the number of ticks that will be used by a progress bar
in the save method. This method should not be called externally.
Subclasses may override this method to implement custom saving behavior.
"""
return 0
def save(self, writer, saveAs=False, progressBar=None):
"""
Save data. This method should not be called externally.
Subclasses may override this method to implement custom saving behavior.
"""
if saveAs:
font = self.font
if font is not None and font.path is not None and os.path.exists(font.path):
reader = UFOReader(font.path)
readerDataDirectoryListing = reader.getDataDirectoryListing()
for fileName, data in self._data.items():
path = os.path.join("data", fileName)
if data["data"] is not None or fileName not in readerDataDirectoryListing:
continue
writer.copyFromReader(reader, path, path)
for fileName in self._scheduledForDeletion:
try:
path = os.path.join("data", fileName)
writer.removeFileForPath(path)
except UFOLibError:
# this will be raised if the file doesn't exist.
# instead of trying to maintain a list of in UFO
# vs. in memory, simply fail and move on when
# something can't be deleted because it isn't
# in the UFO.
pass
self._scheduledForDeletion.clear()
reader = UFOReader(writer.path)
for fileName, data in self._data.items():
if not data["dirty"]:
continue
path = os.path.join("data", fileName)
writer.writeBytesToPath(path, data["data"])
data["dirty"] = False
data["onDisk"] = True
data["onDiskModTime"] = reader.getFileModificationTime(os.path.join("data", fileName))
self.dirty = False
# ---------------------
# External Edit Support
# ---------------------
def testForExternalChanges(self, reader):
"""
Test for external changes. This should not be called externally.
"""
filesOnDisk = reader.getDataDirectoryListing()
modified = []
added = []
deleted = []
for fileName in set(filesOnDisk) - set(self.fileNames):
if fileName not in self._scheduledForDeletion:
added.append(fileName)
elif not self._scheduledForDeletion[fileName]["onDisk"]:
added.append(fileName)
elif self._scheduledForDeletion[fileName]["onDiskModTime"] != reader.getFileModificationTime(os.path.join("data", fileName)):
added.append(fileName)
for fileName, data in self._data.items():
# file on disk and has been loaded
if fileName in filesOnDisk and data["data"] is not None:
path = os.path.join("data", fileName)
newModTime = reader.getFileModificationTime(path)
if newModTime != data["onDiskModTime"]:
newData = reader.readBytesFromPath(path)
if newData != data["data"]:
modified.append(fileName)
continue
# file removed
if fileName not in filesOnDisk and data["onDisk"]:
deleted.append(fileName)
continue
return modified, added, deleted
def reloadData(self, fileNames):
"""
Reload specified data. This should not be called externally.
"""
for fileName in fileNames:
self._data[fileName] = _dataDict()
data = self[fileName]
# ------------------------
# Notification Observation
# ------------------------
def endSelfNotificationObservation(self):
super(DataSet, self).endSelfNotificationObservation()
self._font = None
# -----------------------------
# Serialization/Deserialization
# -----------------------------
def getDataForSerialization(self, **kwargs):
simple_get = lambda key: self[key]
getters = []
for k in self.fileNames:
getters.append((k, simple_get))
return self._serialize(getters, **kwargs)
def setDataFromSerialization(self, data):
self._data = {}
self._scheduledForDeletion = {}
for k in data:
self[k] = data[k]
def _dataDict(data=None, dirty=False, onDisk=True, onDiskModTime=None):
return dict(data=data, dirty=dirty, onDisk=onDisk, onDiskModTime=onDiskModTime)
if __name__ == "__main__":
import doctest
doctest.testmod()
|