diff --git a/maint-helper.py b/maint-helper.py
index 8c64ca2..5ffd7e0 100755
a
|
b
|
import re |
22 | 22 | import datetime |
23 | 23 | import subprocess |
24 | 24 | |
25 | | source_exts = [ '.py', '.c', '.h', '.cpp' ] |
| 25 | |
| 26 | SOURCE_EXTS = ['.py', '.c', '.h', '.cpp'] |
| 27 | COPYRIGHT = 'Copyright (C) ' |
| 28 | |
26 | 29 | |
27 | 30 | def is_source(path): |
28 | | for ext in source_exts: |
| 31 | for ext in SOURCE_EXTS: |
29 | 32 | if path.endswith(ext): |
30 | 33 | return True |
31 | 34 | |
| 35 | |
32 | 36 | def get_name_and_version(): |
33 | 37 | f = open('configure.ac', 'r') |
34 | 38 | config = f.read() |
… |
… |
def get_name_and_version(): |
40 | 44 | print 'Cannot find the package name and version.' |
41 | 45 | sys.exit(0) |
42 | 46 | |
43 | | return [ match.group(2), match.group(1) ] |
| 47 | return (match.group(2), match.group(1)) |
| 48 | |
44 | 49 | |
45 | 50 | def cmd_help(): |
46 | 51 | print 'Usage: \n\ |
… |
… |
maint-helper.py build-snapshot - build a source snapshot \n\ |
48 | 53 | maint-helper.py fix-copyright [path] - fix the copyright year \n\ |
49 | 54 | maint-helper.py check-licenses - check licenses in the source' |
50 | 55 | |
| 56 | |
51 | 57 | def cmd_build_snapshot(): |
52 | | [ name, version ] = get_name_and_version() |
| 58 | name, version = get_name_and_version() |
53 | 59 | |
54 | 60 | print 'Update git...' |
55 | 61 | |
… |
… |
def cmd_build_snapshot(): |
70 | 76 | |
71 | 77 | print 'Update NEWS.sugar...' |
72 | 78 | |
73 | | if os.environ.has_key('SUGAR_NEWS'): |
| 79 | if 'SUGAR_NEWS' in os.environ: |
74 | 80 | sugar_news_path = os.environ['SUGAR_NEWS'] |
75 | 81 | if os.path.isfile(sugar_news_path): |
76 | 82 | f = open(sugar_news_path, 'r') |
… |
… |
def cmd_build_snapshot(): |
79 | 85 | else: |
80 | 86 | sugar_news = '' |
81 | 87 | |
82 | | [ name, version ] = get_name_and_version() |
| 88 | name, version = get_name_and_version() |
83 | 89 | sugar_news += '%s - %s - %s\n\n' % (name, version, alphatag) |
84 | 90 | |
85 | 91 | f = open('NEWS', 'r') |
… |
… |
def cmd_build_snapshot(): |
119 | 125 | |
120 | 126 | print 'Done.' |
121 | 127 | |
| 128 | |
122 | 129 | def check_licenses(path, license, missing): |
123 | | matchers = { 'LGPL' : 'GNU Lesser General Public', |
124 | | 'GPL' : 'GNU General Public License' } |
| 130 | matchers = {'LGPL': 'GNU Lesser General Public', |
| 131 | 'GPL': 'GNU General Public License'} |
125 | 132 | |
126 | 133 | license_file = os.path.join(path, '.license') |
127 | 134 | if os.path.isfile(license_file): |
… |
… |
def check_licenses(path, license, missing): |
156 | 163 | miss_license = False |
157 | 164 | |
158 | 165 | if miss_license: |
159 | | if not missing.has_key(license): |
| 166 | if license not in missing: |
160 | 167 | missing[license] = [] |
161 | 168 | missing[license].append(full_path) |
162 | 169 | |
| 170 | |
163 | 171 | def cmd_check_licenses(): |
164 | 172 | missing = {} |
165 | 173 | check_licenses(os.getcwd(), 'GPL', missing) |
… |
… |
def cmd_check_licenses(): |
170 | 178 | print path |
171 | 179 | print '\n' |
172 | 180 | |
173 | | COPYRIGHT = 'Copyright (C) ' |
174 | 181 | |
175 | 182 | def fix_copyright(path): |
176 | 183 | for item in os.listdir(path): |
… |
… |
def fix_copyright(path): |
208 | 215 | f.write(result) |
209 | 216 | f.close() |
210 | 217 | |
| 218 | |
211 | 219 | def cmd_fix_copyright(path): |
212 | 220 | fix_copyright(path) |
213 | 221 | |
| 222 | |
214 | 223 | if len(sys.argv) < 2: |
215 | 224 | cmd_help() |
216 | 225 | elif sys.argv[1] == 'build-snapshot': |
diff --git a/src/carquinyol/__init__.py b/src/carquinyol/__init__.py
index 8b13789..e69de29 100644
diff --git a/src/carquinyol/datastore.py b/src/carquinyol/datastore.py
index ff3875d..74c75e9 100644
a
|
b
|
DS_OBJECT_PATH = "/org/laptop/sugar/DataStore" |
43 | 43 | |
44 | 44 | logger = logging.getLogger(DS_LOG_CHANNEL) |
45 | 45 | |
| 46 | |
46 | 47 | class DataStore(dbus.service.Object): |
47 | 48 | """D-Bus API and logic for connecting all the other components. |
48 | | """ |
| 49 | """ |
| 50 | |
49 | 51 | def __init__(self, **options): |
50 | 52 | bus_name = dbus.service.BusName(DS_SERVICE, |
51 | 53 | bus=dbus.SessionBus(), |
… |
… |
class DataStore(dbus.service.Object): |
214 | 216 | for uid in uids: |
215 | 217 | entry_path = layoutmanager.get_instance().get_entry_path(uid) |
216 | 218 | if not os.path.exists(entry_path): |
217 | | logging.warning('Inconsistency detected, returning all entries') |
| 219 | logging.warning( |
| 220 | 'Inconsistency detected, returning all entries') |
218 | 221 | |
219 | 222 | layoutmanager.get_instance().index_updated = False |
220 | 223 | self._index_store.close_index() |
… |
… |
class DataStore(dbus.service.Object): |
293 | 296 | self._index_store.delete(uid) |
294 | 297 | self._file_store.delete(uid) |
295 | 298 | self._metadata_store.delete(uid) |
296 | | |
| 299 | |
297 | 300 | entry_path = layoutmanager.get_instance().get_entry_path(uid) |
298 | 301 | os.removedirs(entry_path) |
299 | 302 | |
… |
… |
class DataStore(dbus.service.Object): |
338 | 341 | @dbus.service.signal(DS_DBUS_INTERFACE, signature="a{sv}") |
339 | 342 | def Unmounted(self, descriptor): |
340 | 343 | pass |
341 | | |
diff --git a/src/carquinyol/filestore.py b/src/carquinyol/filestore.py
index f88c531..b96c323 100644
a
|
b
|
import gobject |
23 | 23 | |
24 | 24 | from carquinyol import layoutmanager |
25 | 25 | |
| 26 | |
26 | 27 | class FileStore(object): |
27 | 28 | """Handle the storage of one file per entry. |
28 | 29 | """ |
| 30 | |
29 | 31 | # TODO: add protection against store and retrieve operations on entries |
30 | 32 | # that are being processed async. |
31 | 33 | |
32 | 34 | def store(self, uid, file_path, transfer_ownership, completion_cb): |
33 | 35 | """Store a file for a given entry. |
34 | | |
| 36 | |
35 | 37 | """ |
36 | 38 | dir_path = layoutmanager.get_instance().get_entry_path(uid) |
37 | 39 | if not os.path.exists(dir_path): |
… |
… |
class FileStore(object): |
68 | 70 | |
69 | 71 | def _async_copy(self, file_path, destination_path, completion_cb): |
70 | 72 | """Start copying a file asynchronously. |
71 | | |
| 73 | |
72 | 74 | """ |
73 | 75 | logging.debug('FileStore copying from %r to %r' % \ |
74 | 76 | (file_path, destination_path)) |
… |
… |
class FileStore(object): |
76 | 78 | async_copy.start() |
77 | 79 | |
78 | 80 | def retrieve(self, uid, user_id, extension): |
79 | | """Place the file associated to a given entry into a directory where the |
80 | | user can read it. The caller is reponsible for deleting this file. |
81 | | |
| 81 | """Place the file associated to a given entry into a directory |
| 82 | where the user can read it. The caller is reponsible for |
| 83 | deleting this file. |
| 84 | |
82 | 85 | """ |
83 | 86 | dir_path = layoutmanager.get_instance().get_entry_path(uid) |
84 | 87 | file_path = os.path.join(dir_path, 'data') |
… |
… |
class FileStore(object): |
91 | 94 | if use_instance_dir: |
92 | 95 | if not user_id: |
93 | 96 | raise ValueError('Couldnt determine the current user uid.') |
94 | | destination_dir = os.path.join(os.environ['HOME'], 'isolation', '1', |
95 | | 'uid_to_instance_dir', str(user_id)) |
| 97 | destination_dir = os.path.join(os.environ['HOME'], 'isolation', |
| 98 | '1', 'uid_to_instance_dir', str(user_id)) |
96 | 99 | else: |
97 | 100 | profile = os.environ.get('SUGAR_PROFILE', 'default') |
98 | 101 | destination_dir = os.path.join(os.path.expanduser('~'), '.sugar', |
… |
… |
class FileStore(object): |
147 | 150 | |
148 | 151 | def delete(self, uid): |
149 | 152 | """Remove the file associated to a given entry. |
150 | | |
| 153 | |
151 | 154 | """ |
152 | 155 | dir_path = layoutmanager.get_instance().get_entry_path(uid) |
153 | 156 | file_path = os.path.join(dir_path, 'data') |
… |
… |
class FileStore(object): |
168 | 171 | logging.debug('hard linking %r -> %r' % (new_file, existing_file)) |
169 | 172 | os.link(existing_file, new_file) |
170 | 173 | |
| 174 | |
171 | 175 | class AsyncCopy(object): |
172 | 176 | """Copy a file in chunks in the idle loop. |
173 | | |
| 177 | |
174 | 178 | """ |
175 | 179 | CHUNK_SIZE = 65536 |
176 | 180 | |
… |
… |
class AsyncCopy(object): |
227 | 231 | self.size = stat[6] |
228 | 232 | |
229 | 233 | gobject.idle_add(self._copy_block) |
230 | | |
diff --git a/src/carquinyol/indexstore.py b/src/carquinyol/indexstore.py
index 0f2b982..42c3132 100644
a
|
b
|
_PROPERTIES_NOT_TO_INDEX = ['timestamp', 'activity_id', 'keep', 'preview'] |
44 | 44 | |
45 | 45 | _MAX_RESULTS = int(2 ** 31 - 1) |
46 | 46 | |
| 47 | |
47 | 48 | class IndexStore(object): |
48 | 49 | """Index metadata and provide rich query facilities on it. |
49 | | """ |
| 50 | """ |
| 51 | |
50 | 52 | def __init__(self): |
51 | 53 | self._database = None |
52 | 54 | self._flush_timeout = None |
… |
… |
class IndexStore(object): |
221 | 223 | |
222 | 224 | if query_dict: |
223 | 225 | logging.warning('Unknown term(s): %r' % query_dict) |
224 | | |
| 226 | |
225 | 227 | return Query(Query.OP_AND, queries) |
226 | 228 | |
227 | 229 | def delete(self, uid): |
… |
… |
class IndexStore(object): |
239 | 241 | |
240 | 242 | def _flush(self, force=False): |
241 | 243 | """Called after any database mutation""" |
242 | | logging.debug('IndexStore.flush: %r %r' % (force, self._pending_writes)) |
| 244 | logging.debug('IndexStore.flush: %r %r' % |
| 245 | (force, self._pending_writes)) |
243 | 246 | |
244 | 247 | if self._flush_timeout is not None: |
245 | 248 | gobject.source_remove(self._flush_timeout) |
… |
… |
class IndexStore(object): |
252 | 255 | else: |
253 | 256 | self._flush_timeout = gobject.timeout_add(_FLUSH_TIMEOUT * 1000, |
254 | 257 | self._flush_timeout_cb) |
255 | | |
diff --git a/src/carquinyol/layoutmanager.py b/src/carquinyol/layoutmanager.py
index dc3fde6..a017029 100644
a
|
b
|
import os |
18 | 18 | |
19 | 19 | MAX_QUERY_LIMIT = 40960 |
20 | 20 | |
| 21 | |
21 | 22 | class LayoutManager(object): |
22 | 23 | """Provide the logic about how entries are stored inside the datastore |
23 | 24 | directory |
24 | | """ |
| 25 | """ |
| 26 | |
25 | 27 | def __init__(self): |
26 | 28 | profile = os.environ.get('SUGAR_PROFILE', 'default') |
27 | 29 | base_dir = os.path.join(os.path.expanduser('~'), '.sugar', profile) |
… |
… |
class LayoutManager(object): |
65 | 67 | |
66 | 68 | def get_checksums_dir(self): |
67 | 69 | return os.path.join(self._root_path, 'checksums') |
68 | | |
| 70 | |
69 | 71 | def get_queue_path(self): |
70 | 72 | return os.path.join(self.get_checksums_dir(), 'queue') |
71 | 73 | |
… |
… |
class LayoutManager(object): |
93 | 95 | uids.append(g) |
94 | 96 | return uids |
95 | 97 | |
| 98 | |
96 | 99 | _instance = None |
97 | 100 | def get_instance(): |
98 | 101 | global _instance |
99 | 102 | if _instance is None: |
100 | 103 | _instance = LayoutManager() |
101 | 104 | return _instance |
102 | | |
diff --git a/src/carquinyol/metadatastore.py b/src/carquinyol/metadatastore.py
index 50981f3..8461ef7 100644
a
|
b
|
from carquinyol import metadatareader |
5 | 5 | |
6 | 6 | MAX_SIZE = 256 |
7 | 7 | |
| 8 | |
8 | 9 | class MetadataStore(object): |
| 10 | |
9 | 11 | def store(self, uid, metadata): |
10 | 12 | dir_path = layoutmanager.get_instance().get_entry_path(uid) |
11 | 13 | if not os.path.exists(dir_path): |
… |
… |
class MetadataStore(object): |
21 | 23 | metadata['uid'] = uid |
22 | 24 | for key, value in metadata.items(): |
23 | 25 | |
24 | | # Hack to support activities that still pass properties named as for |
25 | | # example title:text. |
| 26 | # Hack to support activities that still pass properties named as |
| 27 | # for example title:text. |
26 | 28 | if ':' in key: |
27 | 29 | key = key.split(':', 1)[0] |
28 | 30 | |
… |
… |
class MetadataStore(object): |
61 | 63 | metadata_path = os.path.join(dir_path, 'metadata') |
62 | 64 | property_path = os.path.join(metadata_path, key) |
63 | 65 | open(property_path, 'w').write(value) |
64 | | |
diff --git a/src/carquinyol/migration.py b/src/carquinyol/migration.py
index 228db2a..02f8e68 100644
a
|
b
|
|
15 | 15 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
16 | 16 | |
17 | 17 | """Transform one DataStore directory in a newer format. |
18 | | """ |
| 18 | """ |
19 | 19 | |
20 | 20 | import os |
21 | 21 | import logging |
… |
… |
from carquinyol import layoutmanager |
29 | 29 | |
30 | 30 | DATE_FORMAT = '%Y-%m-%dT%H:%M:%S' |
31 | 31 | |
| 32 | |
32 | 33 | def migrate_from_0(): |
33 | 34 | logging.info('Migrating datastore from version 0 to version 1') |
34 | 35 | |
… |
… |
def migrate_from_0(): |
57 | 58 | |
58 | 59 | logging.info('Migration finished') |
59 | 60 | |
| 61 | |
60 | 62 | def _migrate_metadata(root_path, old_root_path, uid): |
61 | 63 | dir_path = layoutmanager.get_instance().get_entry_path(uid) |
62 | 64 | metadata_path = os.path.join(dir_path, 'metadata') |
… |
… |
def _migrate_metadata(root_path, old_root_path, uid): |
88 | 90 | 'Error while migrating property %s of entry %s: %s\n' % \ |
89 | 91 | (key, uid, traceback.format_exc())) |
90 | 92 | |
| 93 | |
91 | 94 | def _migrate_file(root_path, old_root_path, uid): |
92 | 95 | if os.path.exists(os.path.join(old_root_path, uid)): |
93 | 96 | dir_path = layoutmanager.get_instance().get_entry_path(uid) |
94 | 97 | os.rename(os.path.join(old_root_path, uid), |
95 | 98 | os.path.join(dir_path, 'data')) |
96 | 99 | |
| 100 | |
97 | 101 | def _migrate_preview(root_path, old_root_path, uid): |
98 | 102 | dir_path = layoutmanager.get_instance().get_entry_path(uid) |
99 | 103 | metadata_path = os.path.join(dir_path, 'metadata') |
100 | 104 | os.rename(os.path.join(old_root_path, 'preview', uid), |
101 | 105 | os.path.join(metadata_path, 'preview')) |
102 | | |
diff --git a/src/carquinyol/optimizer.py b/src/carquinyol/optimizer.py
index f8a2e3e..6cb1374 100644
a
|
b
|
import gobject |
23 | 23 | |
24 | 24 | from carquinyol import layoutmanager |
25 | 25 | |
| 26 | |
26 | 27 | class Optimizer(object): |
27 | 28 | """Optimizes disk space usage by detecting duplicates and sharing storage. |
28 | 29 | """ |
| 30 | |
29 | 31 | def __init__(self, file_store, metadata_store): |
30 | 32 | self._file_store = file_store |
31 | 33 | self._metadata_store = metadata_store |
… |
… |
class Optimizer(object): |
90 | 92 | |
91 | 93 | def _create_checksum_dir(self, checksum): |
92 | 94 | """Create directory that tracks files with this same checksum. |
93 | | |
| 95 | |
94 | 96 | """ |
95 | 97 | checksums_dir = layoutmanager.get_instance().get_checksums_dir() |
96 | 98 | checksum_path = os.path.join(checksums_dir, checksum) |
… |
… |
class Optimizer(object): |
110 | 112 | def _already_linked(self, uid, checksum): |
111 | 113 | """Check if this entry's file is already a hard link to the checksums |
112 | 114 | dir. |
113 | | |
| 115 | |
114 | 116 | """ |
115 | 117 | checksums_dir = layoutmanager.get_instance().get_checksums_dir() |
116 | 118 | checksum_path = os.path.join(checksums_dir, checksum) |
… |
… |
class Optimizer(object): |
120 | 122 | """Process one item in the checksums queue by calculating its checksum, |
121 | 123 | checking if there exist already an identical file, and in that case |
122 | 124 | substituting its file with a hard link to that pre-existing file. |
123 | | |
| 125 | |
124 | 126 | """ |
125 | 127 | queue_path = layoutmanager.get_instance().get_queue_path() |
126 | 128 | queue = os.listdir(queue_path) |
… |
… |
class Optimizer(object): |
158 | 160 | |
159 | 161 | def _calculate_md5sum(self, path): |
160 | 162 | """Calculate the md5 checksum of a given file. |
161 | | |
| 163 | |
162 | 164 | """ |
163 | 165 | popen = subprocess.Popen(['md5sum', path], stdout=subprocess.PIPE) |
164 | 166 | stdout, stderr_ = popen.communicate() |
165 | 167 | return stdout.split(' ', 1)[0] |
166 | | |