argparse & logging (#92)
* introduce logcommand.Lager, Whipper(); use argparse for whipper image commands, stub logging * update Lager docstring to mention config.Config() * make incorrect subcommand and --version work on toplevel command * migrate accurip show, expand Lager, do not attempt to return from Lager.__init__. * migrate offset find, add Lager.error * correct offset find drive symlink handling * migrate drive * change Lager.__init__(prog) to arg from kwarg * but actually * remove Whipper.usage * add and use Lager.device_option() context manager * help I married an axe murderer * use unified options namespace for entire command tree * migrate whipper cd without comprehensive config loading * switch to logging module - use logging instead of flog for non-extern modules - use WHIPPER_DEBUG and WHIPPER_LOGFILE env variables * convert self.log calls to logger.debug * convert self.error calls to logger.error * remove log.Loggable, use logger not logging * Logging conversion continues - Convert log.* calls to logger.* - Remove morituri.common.log imports * remove morituri.common.log from tests * remove extern/flog, bare minimum Debug conversion * update README for logging changes * update soxi to use logging * refactor Lager for more declarative subcommands * Refactor Lager.device_option: - inline into __init__ - throw IOError instead of Exception for missing drives - remove CommandError checking in rip/main * rename rip to whipper in rip.main * convert rip.debug commands * Rename logcommand.Lager to command.BaseCommand - remove command.CommandError occurrences - remove python-command external module * remove submodules from README, update rclog formatter * update minor ambiguity in readme for command invocation * update version number to match setup.py * remove gitmodules * update version number in tests as well (boo) * convert logger.error to logger.critical * Change morituri.rip to morituri.command - mv common.command to command.basecommand - move TEMPLATES used only by rip.cd out of rip.common - update entry point for command to command.main * update basecommand documentation * go pyflaking: import fixing * replace self.stdout with sys.stdout * remove BaseCommand.config, alphabetise imports * convert self.stdXXX leftovers * convert last getRootCommand to config.Config * convert last getExceptionMessage's to str * change musicbrainz useragent to whipper
This commit is contained in:
committed by
JoeLametta
parent
8f4607de4c
commit
d1ed80d62a
@@ -1,6 +1,8 @@
|
||||
import logging
|
||||
from subprocess import Popen, PIPE
|
||||
from os.path import exists
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
ARB = 'accuraterip-checksum'
|
||||
FLAC = 'flac'
|
||||
@@ -33,18 +35,18 @@ def accuraterip_checksum(f, track, tracks, wave=False, v2=False):
|
||||
arc_rc = arc.returncode
|
||||
|
||||
if not wave and flac_rc != 0:
|
||||
logging.warning('ARC calculation failed: flac return code is non zero')
|
||||
logger.warning('ARC calculation failed: flac return code is non zero')
|
||||
return None
|
||||
|
||||
if arc_rc != 0:
|
||||
logging.warning('ARC calculation failed: arc return code is non zero')
|
||||
logger.warning('ARC calculation failed: arc return code is non zero')
|
||||
return None
|
||||
|
||||
out = out.strip()
|
||||
try:
|
||||
outh = int('0x%s' % out, base=16)
|
||||
except ValueError:
|
||||
logging.warning('ARC output is not usable')
|
||||
logger.warning('ARC output is not usable')
|
||||
return None
|
||||
|
||||
return outh
|
||||
|
||||
@@ -20,21 +20,23 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with morituri. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import errno
|
||||
import time
|
||||
import os
|
||||
import re
|
||||
import stat
|
||||
import shutil
|
||||
import stat
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
from morituri.common import log, common
|
||||
from morituri.common import common
|
||||
from morituri.common import task as ctask
|
||||
|
||||
from morituri.extern import asyncsub
|
||||
from morituri.extern.task import task
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FileSizeError(Exception):
|
||||
|
||||
@@ -78,7 +80,7 @@ _ERROR_RE = re.compile("^scsi_read error:")
|
||||
# number of single-channel samples, ie. 2 bytes (word) per unit, and absolute
|
||||
|
||||
|
||||
class ProgressParser(log.Loggable):
|
||||
class ProgressParser:
|
||||
read = 0 # last [read] frame
|
||||
wrote = 0 # last [wrote] frame
|
||||
errors = 0 # count of number of scsi errors
|
||||
@@ -130,12 +132,12 @@ class ProgressParser(log.Loggable):
|
||||
# set nframes if not yet set
|
||||
if self._nframes is None and self.read != 0:
|
||||
self._nframes = frameOffset - self.read
|
||||
self.debug('set nframes to %r', self._nframes)
|
||||
logger.debug('set nframes to %r', self._nframes)
|
||||
|
||||
# set firstFrames if not yet set
|
||||
if self._firstFrames is None:
|
||||
self._firstFrames = frameOffset - self.start
|
||||
self.debug('set firstFrames to %r', self._firstFrames)
|
||||
logger.debug('set firstFrames to %r', self._firstFrames)
|
||||
|
||||
markStart = None
|
||||
markEnd = None # the next unread frame (half-inclusive)
|
||||
@@ -192,7 +194,7 @@ class ProgressParser(log.Loggable):
|
||||
"""
|
||||
frames = self.stop - self.start + 1 # + 1 since stop is inclusive
|
||||
reads = self.reads
|
||||
self.debug('getTrackQuality: frames %d, reads %d' % (frames, reads))
|
||||
logger.debug('getTrackQuality: frames %d, reads %d' % (frames, reads))
|
||||
|
||||
# don't go over a 100%; we know cdparanoia reads each frame at least
|
||||
# twice
|
||||
@@ -202,7 +204,7 @@ class ProgressParser(log.Loggable):
|
||||
# FIXME: handle errors
|
||||
|
||||
|
||||
class ReadTrackTask(log.Loggable, task.Task):
|
||||
class ReadTrackTask(task.Task):
|
||||
"""
|
||||
I am a task that reads a track using cdparanoia.
|
||||
|
||||
@@ -271,11 +273,11 @@ class ReadTrackTask(log.Loggable, task.Task):
|
||||
stopTrack = i + 1
|
||||
stopOffset = self._stop - self._table.getTrackStart(i + 1)
|
||||
|
||||
self.debug('Ripping from %d to %d (inclusive)',
|
||||
logger.debug('Ripping from %d to %d (inclusive)',
|
||||
self._start, self._stop)
|
||||
self.debug('Starting at track %d, offset %d',
|
||||
logger.debug('Starting at track %d, offset %d',
|
||||
startTrack, startOffset)
|
||||
self.debug('Stopping at track %d, offset %d',
|
||||
logger.debug('Stopping at track %d, offset %d',
|
||||
stopTrack, stopOffset)
|
||||
|
||||
bufsize = 1024
|
||||
@@ -291,7 +293,7 @@ class ReadTrackTask(log.Loggable, task.Task):
|
||||
startTrack, common.framesToHMSF(startOffset),
|
||||
stopTrack, common.framesToHMSF(stopOffset)),
|
||||
self.path])
|
||||
self.debug('Running %s' % (" ".join(argv), ))
|
||||
logger.debug('Running %s' % (" ".join(argv), ))
|
||||
try:
|
||||
self._popen = asyncsub.Popen(argv,
|
||||
bufsize=bufsize,
|
||||
@@ -333,7 +335,7 @@ class ReadTrackTask(log.Loggable, task.Task):
|
||||
|
||||
# fail if too many errors
|
||||
if self._parser.errors > self._MAXERROR:
|
||||
self.debug('%d errors, terminating', self._parser.errors)
|
||||
logger.debug('%d errors, terminating', self._parser.errors)
|
||||
self._popen.terminate()
|
||||
|
||||
num = self._parser.wrote - self._start + 1
|
||||
@@ -366,13 +368,13 @@ class ReadTrackTask(log.Loggable, task.Task):
|
||||
expected = offsetLength * common.BYTES_PER_FRAME + 44
|
||||
if size != expected:
|
||||
# FIXME: handle errors better
|
||||
self.warning('file size %d did not match expected size %d',
|
||||
logger.warning('file size %d did not match expected size %d',
|
||||
size, expected)
|
||||
if (size - expected) % common.BYTES_PER_FRAME == 0:
|
||||
self.warning('%d frames difference' % (
|
||||
logger.warning('%d frames difference' % (
|
||||
(size - expected) / common.BYTES_PER_FRAME))
|
||||
else:
|
||||
self.warning('non-integral amount of frames difference')
|
||||
logger.warning('non-integral amount of frames difference')
|
||||
|
||||
self.setAndRaiseException(FileSizeError(self.path,
|
||||
"File size %d did not match expected size %d" % (
|
||||
@@ -382,7 +384,7 @@ class ReadTrackTask(log.Loggable, task.Task):
|
||||
if self._errors:
|
||||
print "\n".join(self._errors)
|
||||
else:
|
||||
self.warning('exit code %r', self._popen.returncode)
|
||||
logger.warning('exit code %r', self._popen.returncode)
|
||||
self.exception = ReturnCodeError(self._popen.returncode)
|
||||
|
||||
self.quality = self._parser.getTrackQuality()
|
||||
@@ -393,7 +395,7 @@ class ReadTrackTask(log.Loggable, task.Task):
|
||||
return
|
||||
|
||||
|
||||
class ReadVerifyTrackTask(log.Loggable, task.MultiSeparateTask):
|
||||
class ReadVerifyTrackTask(task.MultiSeparateTask):
|
||||
"""
|
||||
I am a task that reads and verifies a track using cdparanoia.
|
||||
I also encode the track.
|
||||
@@ -449,10 +451,10 @@ class ReadVerifyTrackTask(log.Loggable, task.MultiSeparateTask):
|
||||
"""
|
||||
task.MultiSeparateTask.__init__(self)
|
||||
|
||||
self.debug('Creating read and verify task on %r', path)
|
||||
logger.debug('Creating read and verify task on %r', path)
|
||||
|
||||
if taglist:
|
||||
self.debug('read and verify with taglist %r', taglist)
|
||||
logger.debug('read and verify with taglist %r', taglist)
|
||||
# FIXME: choose a dir on the same disk/dir as the final path
|
||||
fd, tmppath = tempfile.mkstemp(suffix='.morituri.wav')
|
||||
tmppath = unicode(tmppath)
|
||||
@@ -504,7 +506,7 @@ class ReadVerifyTrackTask(log.Loggable, task.MultiSeparateTask):
|
||||
self.quality = max(self.tasks[0].quality,
|
||||
self.tasks[2].quality)
|
||||
self.peak = self.tasks[6].peak
|
||||
self.debug('peak: %r', self.peak)
|
||||
logger.debug('peak: %r', self.peak)
|
||||
self.testspeed = self.tasks[0].speed
|
||||
self.copyspeed = self.tasks[2].speed
|
||||
self.testduration = self.tasks[0].duration
|
||||
@@ -513,11 +515,11 @@ class ReadVerifyTrackTask(log.Loggable, task.MultiSeparateTask):
|
||||
self.testchecksum = c1 = self.tasks[1].checksum
|
||||
self.copychecksum = c2 = self.tasks[3].checksum
|
||||
if c1 == c2:
|
||||
self.info('Checksums match, %08x' % c1)
|
||||
logger.info('Checksums match, %08x' % c1)
|
||||
self.checksum = self.testchecksum
|
||||
else:
|
||||
# FIXME: detect this before encoding
|
||||
self.info('Checksums do not match, %08x %08x' % (
|
||||
logger.info('Checksums do not match, %08x %08x' % (
|
||||
c1, c2))
|
||||
self.exception = ChecksumException(
|
||||
'read and verify failed: test checksum')
|
||||
@@ -531,17 +533,17 @@ class ReadVerifyTrackTask(log.Loggable, task.MultiSeparateTask):
|
||||
|
||||
if not self.exception:
|
||||
try:
|
||||
self.debug('Moving to final path %r', self.path)
|
||||
logger.debug('Moving to final path %r', self.path)
|
||||
os.rename(self._tmppath, self.path)
|
||||
except Exception, e:
|
||||
self.debug('Exception while moving to final path %r: '
|
||||
logger.debug('Exception while moving to final path %r: '
|
||||
'%r',
|
||||
self.path, log.getExceptionMessage(e))
|
||||
self.path, str(e))
|
||||
self.exception = e
|
||||
else:
|
||||
os.unlink(self._tmppath)
|
||||
else:
|
||||
self.debug('stop: exception %r', self.exception)
|
||||
logger.debug('stop: exception %r', self.exception)
|
||||
except Exception, e:
|
||||
print 'WARNING: unhandled exception %r' % (e, )
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
@@ -6,6 +5,9 @@ from subprocess import check_call, Popen, PIPE, CalledProcessError
|
||||
|
||||
from morituri.image.toc import TocFile
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
CDRDAO = 'cdrdao'
|
||||
|
||||
def read_toc(device, fast_toc=False):
|
||||
@@ -28,7 +30,7 @@ def read_toc(device, fast_toc=False):
|
||||
try:
|
||||
check_call(cmd, stdout=PIPE, stderr=PIPE)
|
||||
except CalledProcessError, e:
|
||||
logging.warning('cdrdao read-toc failed: return code is non-zero: ' +
|
||||
logger.warning('cdrdao read-toc failed: return code is non-zero: ' +
|
||||
str(e.returncode))
|
||||
raise e
|
||||
toc = TocFile(tocfile)
|
||||
@@ -43,14 +45,14 @@ def version():
|
||||
cdrdao = Popen(CDRDAO, stderr=PIPE)
|
||||
out, err = cdrdao.communicate()
|
||||
if cdrdao.returncode != 1:
|
||||
logging.warning("cdrdao version detection failed: "
|
||||
"return code is " + str(cdrdao.returncode))
|
||||
logger.warning("cdrdao version detection failed: "
|
||||
"return code is " + str(cdrdao.returncode))
|
||||
return None
|
||||
m = re.compile(r'^Cdrdao version (?P<version>.*) - \(C\)').search(
|
||||
err.decode('utf-8'))
|
||||
if not m:
|
||||
logging.warning("cdrdao version detection failed: "
|
||||
+ "could not find version")
|
||||
logger.warning("cdrdao version detection failed: "
|
||||
"could not find version")
|
||||
return None
|
||||
return m.group('version')
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import logging
|
||||
import os
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
SOX = 'sox'
|
||||
|
||||
def peak_level(track_path):
|
||||
@@ -12,12 +14,12 @@ def peak_level(track_path):
|
||||
Returns None on error.
|
||||
"""
|
||||
if not os.path.exists(track_path):
|
||||
logging.warning("SoX peak detection failed: file not found")
|
||||
logger.warning("SoX peak detection failed: file not found")
|
||||
return None
|
||||
sox = Popen([SOX, track_path, "-n", "stat"], stderr=PIPE)
|
||||
out, err = sox.communicate()
|
||||
if sox.returncode:
|
||||
logging.warning("SoX peak detection failed: " + str(sox.returncode))
|
||||
logger.warning("SoX peak detection failed: " + str(sox.returncode))
|
||||
return None
|
||||
# relevant captured line looks like:
|
||||
# Maximum amplitude: 0.123456
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import os
|
||||
|
||||
from morituri.common import log, common
|
||||
from morituri.common import common
|
||||
from morituri.common import task as ctask
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
SOXI = 'soxi'
|
||||
|
||||
class AudioLengthTask(ctask.PopenTask, log.Loggable):
|
||||
class AudioLengthTask(ctask.PopenTask):
|
||||
"""
|
||||
I calculate the length of a track in audio samples.
|
||||
|
||||
@@ -42,5 +45,5 @@ class AudioLengthTask(ctask.PopenTask, log.Loggable):
|
||||
|
||||
def done(self):
|
||||
if self._error:
|
||||
self.warning("soxi reported on stderr: %s", "".join(self._error))
|
||||
logger.warning("soxi reported on stderr: %s", "".join(self._error))
|
||||
self.length = int("".join(self._output))
|
||||
|
||||
Reference in New Issue
Block a user