curds: Move the ThreadPoolExecutor into the track code

I think it makes sense to do this transparently through the Track code
so higher layers don't have to do as much work.  We'll just return a
Future-object corresponding to the track that will be added.

It makes sense to only have one thread in this case, since __add() needs
exclusive access to the track_list for its entire execution.  Anything
more would just create conflicts and actually increase execution time.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2019-02-15 15:54:00 -05:00
parent 2c022163ad
commit 1eac742bc4
2 changed files with 46 additions and 34 deletions

View File

@ -1,15 +1,17 @@
# Copyright 2019 (c) Anna Schumaker. # Copyright 2019 (c) Anna Schumaker.
import asyncio
import concurrent.futures import concurrent.futures
import mutagen import mutagen
import unittest
import os import os
import track import track
import unittest
test_tracks = os.path.abspath("./trier/Test Album") test_tracks = os.path.abspath("./trier/Test Album")
test_library = os.path.abspath("./trier/Test Library") test_library = os.path.abspath("./trier/Test Library")
class TestTrackClass(unittest.TestCase): class TestTrackClass(unittest.TestCase):
def setUp(self):
track.track_list.clear()
def test_init_basic(self): def test_init_basic(self):
p = [ test_tracks, "01 - Test Track.ogg" ] p = [ test_tracks, "01 - Test Track.ogg" ]
t = track.Track(os.path.join(*p)) t = track.Track(os.path.join(*p))
@ -23,7 +25,7 @@ class TestTrackClass(unittest.TestCase):
self.assertEqual(t.album.date, 2019) self.assertEqual(t.album.date, 2019)
self.assertEqual(t.album.genre, "Test") self.assertEqual(t.album.genre, "Test")
self.assertEqual(t.album.albumartist, "Test Artist") self.assertEqual(t.album.albumartist, "Test Artist")
self.assertEqual(len(track.tracklist), 0) self.assertEqual(len(track.track_list), 0)
def test_init_empty(self): def test_init_empty(self):
p = [ test_tracks, "00 - Empty Track.ogg" ] p = [ test_tracks, "00 - Empty Track.ogg" ]
@ -38,7 +40,7 @@ class TestTrackClass(unittest.TestCase):
self.assertEqual(t.album.date, 0) self.assertEqual(t.album.date, 0)
self.assertEqual(t.album.genre, "Unknown") self.assertEqual(t.album.genre, "Unknown")
self.assertEqual(t.album.albumartist, "Unknown Artist") self.assertEqual(t.album.albumartist, "Unknown Artist")
self.assertEqual(len(track.tracklist), 0) self.assertEqual(len(track.track_list), 0)
def test_init_discno_detect(self): def test_init_discno_detect(self):
join = os.path.join join = os.path.join
@ -53,35 +55,40 @@ class TestTrackClass(unittest.TestCase):
self.assertEqual(track.Track(join(test_tracks, "10 - Test {Disc 20}.ogg")).discnumber, 20) self.assertEqual(track.Track(join(test_tracks, "10 - Test {Disc 20}.ogg")).discnumber, 20)
def test_track_add(self): def test_track_add(self):
track.tracklist.clear() fs = track.add(os.path.join(test_tracks, "01 - Test Track.ogg"))
t = track.add(os.path.join(test_tracks, "01 - Test Track.ogg")) self.assertEqual(len(track.track_list), 0)
t = fs.result()
self.assertIsNotNone(t) self.assertIsNotNone(t)
self.assertEqual(len(track.tracklist), 1) self.assertEqual(len(track.track_list), 1)
self.assertIn(t, track.tracklist) self.assertIn(t, track.track_list)
self.assertIsNone(track.add(os.path.join(test_tracks, "01 - Test Track.ogg"))) self.assertIsNone(track.add(os.path.join(test_tracks, "01 - Test Track.ogg")).result())
self.assertEqual(len(track.tracklist), 1) self.assertEqual(len(track.track_list), 1)
self.assertIsNone(track.add("99 - No Such Track.ogg")) self.assertIsNone(track.add("99 - No Such Track.ogg").result())
self.assertEqual(len(track.tracklist), 1) self.assertEqual(len(track.track_list), 1)
u = track.add(os.path.join(test_tracks, "02 - Test {Disc 2}.ogg")) fs = track.add(os.path.join(test_tracks, "02 - Test {Disc 2}.ogg"))
u = fs.result()
self.assertIsNotNone(u) self.assertIsNotNone(u)
self.assertEqual(len(track.tracklist), 2) self.assertEqual(len(track.track_list), 2)
self.assertIn(u, track.tracklist) self.assertIn(u, track.track_list)
def test_parallel_add(self): def test_parallel_add(self):
track.tracklist.clear() count = 0
count = 0 futures = [ ]
tracks = [ ] done = [ ]
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as pool: none = [ ]
for dirname, subdirs, files in os.walk(test_library): for dirname, subdirs, files in os.walk(test_library):
for f in files: for f in files:
tracks.append(pool.submit(track.add, os.path.join(dirname, f))) futures.append(track.add(os.path.join(dirname, f)))
count += 1 futures.append(track.add(os.path.join(dirname, f)))
self.assertEqual(len(track.tracklist), count) count += 1
self.assertEqual(len(tracks), count) self.assertLess(len(track.track_list), count)
for t in tracks: for fs in concurrent.futures.as_completed(futures):
self.assertIsNotNone(t) if fs.result() is not None:
done.append(fs.result())
if __name__ == '__main--': else:
unittest.main() none.append(fs.result())
self.assertEqual(len(done), count)
self.assertEqual(len(none), count)
self.assertEqual(len(track.track_list), count)

View File

@ -1,11 +1,13 @@
# Copyright 2019 (c) Anna Schumaker. # Copyright 2019 (c) Anna Schumaker.
import album import album
import concurrent.futures
import mutagen import mutagen
import os import os
import re import re
discno_map = { "one" : "1", "two" : "2", "three" : "3", "four" : "4", "five" : "5" } discno_map = { "one" : "1", "two" : "2", "three" : "3", "four" : "4", "five" : "5" }
tracklist = [ ] track_list = [ ]
track_pool = concurrent.futures.ThreadPoolExecutor(max_workers = 1)
class Track: class Track:
def __init__(self, path): def __init__(self, path):
@ -26,11 +28,14 @@ class Track:
self.discnumber = int(discno_map.get(match.group(), match.group())) self.discnumber = int(discno_map.get(match.group(), match.group()))
def add(path): def __add(path):
try: try:
if not any(t.path == path for t in tracklist): if not any(t.path == path for t in track_list):
track = Track(path) track = Track(path)
tracklist.append(track) track_list.append(track)
return track return track
except mutagen.MutagenError: except mutagen.MutagenError:
return None return None
def add(path):
return track_pool.submit(__add, path)