Skip to content

Commit

Permalink
dictionary: improve loading manager
Browse files Browse the repository at this point in the history
- add support for unloading outdated dictionaries
- make the loading manager work more dict-like
  • Loading branch information
benoit-pierre committed May 6, 2017
1 parent 0bc192c commit e9c0550
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 17 deletions.
14 changes: 14 additions & 0 deletions plover/dictionary/loading_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ class DictionaryLoadingManager(object):
def __init__(self):
self.dictionaries = {}

def __len__(self):
return len(self.dictionaries)

def __getitem__(self, filename):
return self.dictionaries[filename].get()

def __contains__(self, filename):
return filename in self.dictionaries

def start_loading(self, filename):
op = self.dictionaries.get(filename)
if op is not None and not op.needs_reloading():
Expand All @@ -26,6 +35,11 @@ def start_loading(self, filename):
self.dictionaries[filename] = op
return op

def unload_outdated(self):
for filename, op in list(self.dictionaries.items()):
if op.needs_reloading():
del self.dictionaries[filename]

def load(self, filenames):
start_time = time.time()
self.dictionaries = {f: self.start_loading(f) for f in filenames}
Expand Down
42 changes: 25 additions & 17 deletions test/test_loading_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ def __init__(self, name, contents):
self.name = name
self.contents = contents
self.tf = tempfile.NamedTemporaryFile()
self.timestamp = os.path.getmtime(self.tf.name)

def __repr__(self):
return 'FakeDictionaryInfo(%r, %r)' % (self.name, self.contents)
Expand All @@ -53,7 +52,8 @@ def __call__(self, filename):
d = self.files[filename]
if isinstance(d.contents, Exception):
raise d.contents
return FakeDictionaryContents(d.contents, d.timestamp)
timestamp = os.path.getmtime(filename)
return FakeDictionaryContents(d.contents, timestamp)

dictionaries = {}
for i in range(8):
Expand All @@ -73,13 +73,21 @@ def df(name):
loader = MockLoader(dictionaries)
with patch('plover.dictionary.loading_manager.load_dictionary', loader):
manager = loading_manager.DictionaryLoadingManager()
manager.start_loading(df('a'))
manager.start_loading(df('b'))
manager.start_loading(df('a')).get()
manager.start_loading(df('b')).get()
results = manager.load([df('c'), df('b')])
# Returns the right values in the right order.
self.assertEqual(results, ['ccccc', 'bbbbb'])
# Dropped superfluous files.
assertCountEqual(self, [df('b'), df('c')], manager.dictionaries.keys())
# Check dict like interface.
self.assertEqual(len(manager), 2)
self.assertFalse(df('a') in manager)
with self.assertRaises(KeyError):
manager[df('a')]
self.assertTrue(df('b') in manager)
self.assertTrue(df('c') in manager)
self.assertEqual(results, [manager[df('c')], manager[df('b')]])
# Return a DictionaryLoaderException for load errors.
results = manager.load([df('c'), df('e'), df('b'), df('f')])
self.assertEqual(len(results), 4)
Expand All @@ -93,22 +101,22 @@ def df(name):
self.assertIsInstance(results[3].exception, Exception)
# Only loaded the files once.
self.assertTrue(all(x == 1 for x in loader.load_counts.values()))
# No reload if timestamp is unchanged, or more recent.
# (use case: dictionary edited with Plover and saved back)
# No reload if file timestamp is unchanged, or the dictionary
# timestamp is more recent. (use case: dictionary edited with
# Plover and saved back)
file_timestamp = results[0].timestamp
results[0].timestamp = file_timestamp + 1
dictionaries['c'].contents = 'CCCCC'
dictionaries['c'].timestamp += 1
results = manager.load([df('c')])
self.assertEqual(results, ['ccccc'])
self.assertEqual(loader.load_counts[df('c')], 1)
# Check files are reloaded when modified.
# Note: do not try to "touch" the file using os.utime
# as it's not reliable without sleeping to account
# for operating system resolution.
mtime = os.path.getmtime(df('c'))
def getmtime(path):
assert path == df('c')
return mtime + 1
with patch('os.path.getmtime', getmtime):
results = manager.load([df('c')])
# Check outdated dictionaries are reloaded.
results[0].timestamp = file_timestamp - 1
results = manager.load([df('c')])
self.assertEqual(results, ['CCCCC'])
self.assertEqual(loader.load_counts[df('c')], 2)
# Check trimming of outdated dictionaries.
results[0].timestamp = file_timestamp - 1
manager.unload_outdated()
self.assertEqual(len(manager), 0)
self.assertFalse(df('c') in manager)

0 comments on commit e9c0550

Please sign in to comment.