diff --git a/beets/importer.py b/beets/importer.py index 2ee143e4c..deb13fd24 100644 --- a/beets/importer.py +++ b/beets/importer.py @@ -26,6 +26,7 @@ from tempfile import mkdtemp from bisect import insort, bisect_left from contextlib import contextmanager import shutil +import time from beets import logging from beets import autotag @@ -174,14 +175,13 @@ class ImportSession(object): """Controls an import action. Subclasses should implement methods to communicate with the user or otherwise make decisions. """ - def __init__(self, lib, logfile, paths, query): - """Create a session. `lib` is a Library object. `logfile` is a - file-like object open for writing or None if no logging is to be - performed. Either `paths` or `query` is non-null and indicates + def __init__(self, lib, loghandler, paths, query): + """Create a session. `lib` is a Library object. `loghandler` is a + logging.Handler. Either `paths` or `query` is non-null and indicates the source of files to be imported. """ self.lib = lib - self.logfile = logfile + self.logger = self._setup_logging(loghandler) self.paths = paths self.query = query self.seen_idents = set() @@ -191,6 +191,14 @@ class ImportSession(object): if self.paths: self.paths = map(normpath, self.paths) + def _setup_logging(self, loghandler): + logger = logging.getLogger(__name__) + logger.propagate = False + if not loghandler: + loghandler = logging.NullHandler() + logger.handlers = [loghandler] + return logger + def set_config(self, config): """Set `config` property from global import config and make implied changes. @@ -225,13 +233,10 @@ class ImportSession(object): self.want_resume = config['resume'].as_choice([True, False, 'ask']) def tag_log(self, status, paths): - """Log a message about a given album to logfile. The status should - reflect the reason the album couldn't be tagged. + """Log a message about a given album to the importer log. The status + should reflect the reason the album couldn't be tagged. """ - if self.logfile: - print(u'{0} {1}'.format(status, displayable_path(paths)), - file=self.logfile) - self.logfile.flush() + self.logger.info(u'{0} {1}', status, displayable_path(paths)) def log_choice(self, task, duplicate=False): """Logs the task's current choice if it should be logged. If @@ -269,6 +274,7 @@ class ImportSession(object): def run(self): """Run the import task. """ + self.logger.info(u'import started {0}', time.asctime()) self.set_config(config['import']) # Set up the pipeline. diff --git a/beets/logging.py b/beets/logging.py index dd1a28cf2..933fdb167 100644 --- a/beets/logging.py +++ b/beets/logging.py @@ -92,3 +92,16 @@ if PY26: return logger my_manager.getLogger = new_getLogger + + +# Offer NullHandler in Python 2.6 to reduce the difference with never versions +if PY26: + class NullHandler(Handler): + def handle(self, record): + pass + + def emit(self, record): + pass + + def createLock(self): + self.lock = None diff --git a/beets/ui/commands.py b/beets/ui/commands.py index da96b1898..2c0863b60 100644 --- a/beets/ui/commands.py +++ b/beets/ui/commands.py @@ -18,8 +18,6 @@ interface. from __future__ import print_function import os -import time -import codecs import platform import re import shlex @@ -825,29 +823,22 @@ def import_files(lib, paths, query): # Open the log. if config['import']['log'].get() is not None: - logpath = config['import']['log'].as_filename() + logpath = syspath(config['import']['log'].as_filename()) try: - logfile = codecs.open(syspath(logpath), 'a', 'utf8') + loghandler = logging.FileHandler(logpath) except IOError: - raise ui.UserError(u"could not open log file for writing: %s" % - displayable_path(logpath)) - print(u'import started', time.asctime(), file=logfile) + raise ui.UserError(u"could not open log file for writing: " + u"{0}".format(displayable_path(loghandler))) else: - logfile = None + loghandler = None # Never ask for input in quiet mode. if config['import']['resume'].get() == 'ask' and \ config['import']['quiet']: config['import']['resume'] = False - session = TerminalImportSession(lib, logfile, paths, query) - try: - session.run() - finally: - # If we were logging, close the file. - if logfile: - print(u'', file=logfile) - logfile.close() + session = TerminalImportSession(lib, loghandler, paths, query) + session.run() # Emit event. plugins.send('import', lib=lib, paths=paths) diff --git a/test/_common.py b/test/_common.py index 3852ba2f0..6107601c8 100644 --- a/test/_common.py +++ b/test/_common.py @@ -115,9 +115,9 @@ def album(lib=None): # Dummy import session. -def import_session(lib=None, logfile=None, paths=[], query=[], cli=False): +def import_session(lib=None, loghandler=None, paths=[], query=[], cli=False): cls = commands.TerminalImportSession if cli else importer.ImportSession - return cls(lib, logfile, paths, query) + return cls(lib, loghandler, paths, query) # A test harness for all beets tests. diff --git a/test/helper.py b/test/helper.py index afa5d29d1..af63c18a4 100644 --- a/test/helper.py +++ b/test/helper.py @@ -255,7 +255,7 @@ class TestHelper(object): config['import']['autotag'] = False config['import']['resume'] = False - return TestImportSession(self.lib, logfile=None, query=None, + return TestImportSession(self.lib, loghandler=None, query=None, paths=[import_dir]) # Library fixtures methods diff --git a/test/test_importer.py b/test/test_importer.py index 9a555c665..7eab84e3e 100644 --- a/test/test_importer.py +++ b/test/test_importer.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2015, Adrian Sampson. # @@ -33,6 +34,7 @@ from beets.mediafile import MediaFile from beets import autotag from beets.autotag import AlbumInfo, TrackInfo, AlbumMatch from beets import config +from beets import logging class AutotagStub(object): @@ -209,7 +211,7 @@ class ImportHelper(TestHelper): config['import']['link'] = link self.importer = TestImportSession( - self.lib, logfile=None, query=None, + self.lib, loghandler=None, query=None, paths=[import_dir or self.import_dir] ) @@ -1219,15 +1221,17 @@ class ImportDuplicateSingletonTest(unittest.TestCase, TestHelper): class TagLogTest(_common.TestCase): def test_tag_log_line(self): sio = StringIO.StringIO() - session = _common.import_session(logfile=sio) + handler = logging.StreamHandler(sio) + session = _common.import_session(loghandler=handler) session.tag_log('status', 'path') - assert 'status path' in sio.getvalue() + self.assertIn('status path', sio.getvalue()) def test_tag_log_unicode(self): sio = StringIO.StringIO() - session = _common.import_session(logfile=sio) - session.tag_log('status', 'caf\xc3\xa9') - assert 'status caf' in sio.getvalue() + handler = logging.StreamHandler(sio) + session = _common.import_session(loghandler=handler) + session.tag_log('status', u'café') # send unicode + self.assertIn(u'status café', sio.getvalue()) class ResumeImportTest(unittest.TestCase, TestHelper): diff --git a/test/test_ui_importer.py b/test/test_ui_importer.py index 8006e4215..0e3599301 100644 --- a/test/test_ui_importer.py +++ b/test/test_ui_importer.py @@ -91,7 +91,7 @@ class TerminalImportSessionSetup(object): self.io = DummyIO() self.io.install() self.importer = TestTerminalImportSession( - self.lib, logfile=None, query=None, io=self.io, + self.lib, loghandler=None, query=None, io=self.io, paths=[import_dir or self.import_dir], )