From 0c61eb11f5627a22011c49838bbd1fa7d92c7809 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 6 Aug 2014 12:46:26 +0200 Subject: [PATCH] Dropping trailing whitespaces --- headphones/albumswitcher.py | 18 +- headphones/api.py | 176 +++--- headphones/db.py | 34 +- headphones/getXldProfile.py | 4 +- headphones/importer.py | 20 +- headphones/librarysync.py | 80 +-- headphones/lyrics.py | 16 +- headphones/mb.py | 148 +++--- headphones/notifiers.py | 18 +- headphones/postprocessor.py | 306 +++++------ headphones/searcher.py | 4 +- headphones/searcher_rutracker.py | 80 +-- headphones/updater.py | 6 +- headphones/utorrent.py | 2 +- headphones/webserve.py | 10 +- headphones/webstart.py | 20 +- lib/apscheduler/jobstores/ram_store.py | 2 +- lib/beets/autotag/__init__.py | 2 +- lib/beets/ui/migrate.py | 6 +- lib/beets/util/__init__.py | 2 +- lib/bs4/dammit.py | 2 +- lib/bs4/diagnose.py | 4 +- lib/cherrypy/__init__.py | 140 ++--- lib/cherrypy/_cpchecker.py | 84 +-- lib/cherrypy/_cpconfig.py | 14 +- lib/cherrypy/_cpdispatch.py | 134 ++--- lib/cherrypy/_cperror.py | 122 ++--- lib/cherrypy/_cplogging.py | 98 ++-- lib/cherrypy/_cpmodpy.py | 56 +- lib/cherrypy/_cpnative_server.py | 36 +- lib/cherrypy/_cpreqbody.py | 234 ++++---- lib/cherrypy/_cprequest.py | 292 +++++----- lib/cherrypy/_cpserver.py | 66 +-- lib/cherrypy/_cptools.py | 148 +++--- lib/cherrypy/_cptree.py | 110 ++-- lib/cherrypy/_cpwsgi.py | 98 ++-- lib/cherrypy/_cpwsgi_server.py | 10 +- lib/cherrypy/lib/__init__.py | 6 +- lib/cherrypy/lib/auth.py | 32 +- lib/cherrypy/lib/auth_basic.py | 8 +- lib/cherrypy/lib/auth_digest.py | 28 +- lib/cherrypy/lib/caching.py | 118 ++-- lib/cherrypy/lib/covercp.py | 50 +- lib/cherrypy/lib/cpstats.py | 70 +-- lib/cherrypy/lib/cptools.py | 128 ++--- lib/cherrypy/lib/encoding.py | 78 +-- lib/cherrypy/lib/gctools.py | 14 +- lib/cherrypy/lib/httpauth.py | 48 +- lib/cherrypy/lib/httputil.py | 138 ++--- lib/cherrypy/lib/jsontools.py | 14 +- lib/cherrypy/lib/profiler.py | 40 +- lib/cherrypy/lib/reprconf.py | 130 ++--- lib/cherrypy/lib/sessions.py | 238 ++++----- lib/cherrypy/lib/static.py | 76 +-- lib/cherrypy/process/plugins.py | 178 +++---- lib/cherrypy/process/servers.py | 72 +-- lib/cherrypy/process/win32.py | 46 +- lib/cherrypy/process/wspbus.py | 86 +-- lib/cherrypy/scaffold/__init__.py | 10 +- lib/cherrypy/wsgiserver/ssl_builtin.py | 14 +- lib/cherrypy/wsgiserver/ssl_pyopenssl.py | 62 +-- lib/cherrypy/wsgiserver/wsgiserver2.py | 502 +++++++++--------- lib/cherrypy/wsgiserver/wsgiserver3.py | 498 ++++++++--------- lib/configobj.py | 388 +++++++------- lib/feedparser.py | 210 ++++---- lib/gntp/core.py | 2 +- lib/httplib2/__init__.py | 154 +++--- lib/httplib2/iri2uri.py | 18 +- lib/mako/ext/autohandler.py | 4 +- lib/mako/ext/preprocessors.py | 6 +- lib/musicbrainzngs/musicbrainz.py | 8 +- lib/mutagen/easymp4.py | 2 +- lib/oauth2/__init__.py | 120 ++--- lib/pyItunes/Library.py | 2 +- lib/pyItunes/Song.py | 2 +- lib/pyItunes/XMLLibraryParser.py | 2 +- lib/pygazelle/api.py | 2 +- lib/pygazelle/inbox.py | 4 +- lib/pynma/__init__.py | 2 +- lib/pynma/pynma.py | 8 +- lib/requests/cookies.py | 2 +- .../packages/chardet/charsetgroupprober.py | 8 +- lib/requests/packages/chardet/constants.py | 4 +- lib/requests/packages/chardet/euckrfreq.py | 10 +- lib/requests/packages/chardet/euctwprober.py | 4 +- lib/requests/packages/chardet/gb2312prober.py | 4 +- lib/unidecode/__init__.py | 2 +- lib/yaml/constructor.py | 2 +- lib/yaml/emitter.py | 2 +- lib/yaml/parser.py | 2 +- lib/yaml/scanner.py | 18 +- 91 files changed, 3139 insertions(+), 3139 deletions(-) diff --git a/headphones/albumswitcher.py b/headphones/albumswitcher.py index e4a442d8..34ab7b99 100644 --- a/headphones/albumswitcher.py +++ b/headphones/albumswitcher.py @@ -39,11 +39,11 @@ def switch(AlbumID, ReleaseID): "ReleaseCountry": newalbumdata['ReleaseCountry'], "ReleaseFormat": newalbumdata['ReleaseFormat'] } - + myDB.upsert("albums", newValueDict, controlValueDict) - + for track in newtrackdata: - + controlValueDict = {"TrackID": track['TrackID'], "AlbumID": AlbumID} @@ -60,23 +60,23 @@ def switch(AlbumID, ReleaseID): "Format": track['Format'], "BitRate": track['BitRate'] } - + myDB.upsert("tracks", newValueDict, controlValueDict) - + # Mark albums as downloaded if they have at least 80% (by default, configurable) of the album total_track_count = len(newtrackdata) have_track_count = len(myDB.select('SELECT * from tracks WHERE AlbumID=? AND Location IS NOT NULL', [AlbumID])) - + if oldalbumdata['Status'] == 'Skipped' and ((have_track_count/float(total_track_count)) >= (headphones.ALBUM_COMPLETION_PCT/100.0)): myDB.action('UPDATE albums SET Status=? WHERE AlbumID=?', ['Downloaded', AlbumID]) - + # Update have track counts on index totaltracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=? AND AlbumID IN (SELECT AlbumID FROM albums WHERE Status != "Ignored")', [newalbumdata['ArtistID']])) havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=? AND Location IS NOT NULL', [newalbumdata['ArtistID']])) controlValueDict = {"ArtistID": newalbumdata['ArtistID']} - + newValueDict = { "TotalTracks": totaltracks, "HaveTracks": havetracks} - + myDB.upsert("artists", newValueDict, controlValueDict) diff --git a/headphones/api.py b/headphones/api.py index 03888a26..87f20ae0 100644 --- a/headphones/api.py +++ b/headphones/api.py @@ -21,29 +21,29 @@ import lib.simplejson as simplejson from xml.dom.minidom import Document import copy -cmd_list = [ 'getIndex', 'getArtist', 'getAlbum', 'getUpcoming', 'getWanted', 'getSimilar', 'getHistory', 'getLogs', +cmd_list = [ 'getIndex', 'getArtist', 'getAlbum', 'getUpcoming', 'getWanted', 'getSimilar', 'getHistory', 'getLogs', 'findArtist', 'findAlbum', 'addArtist', 'delArtist', 'pauseArtist', 'resumeArtist', 'refreshArtist', - 'addAlbum', 'queueAlbum', 'unqueueAlbum', 'forceSearch', 'forceProcess', 'getVersion', 'checkGithub', - 'shutdown', 'restart', 'update', 'getArtistArt', 'getAlbumArt', 'getArtistInfo', 'getAlbumInfo', + 'addAlbum', 'queueAlbum', 'unqueueAlbum', 'forceSearch', 'forceProcess', 'getVersion', 'checkGithub', + 'shutdown', 'restart', 'update', 'getArtistArt', 'getAlbumArt', 'getArtistInfo', 'getAlbumInfo', 'getArtistThumb', 'getAlbumThumb', 'choose_specific_download', 'download_specific_release'] class Api(object): def __init__(self): - + self.apikey = None self.cmd = None self.id = None - + self.kwargs = None self.data = None self.callback = None - + def checkParams(self,*args,**kwargs): - + if not headphones.API_ENABLED: self.data = 'API not enabled' return @@ -53,32 +53,32 @@ class Api(object): if len(headphones.API_KEY) != 32: self.data = 'API key not generated correctly' return - + if 'apikey' not in kwargs: self.data = 'Missing api key' return - + if kwargs['apikey'] != headphones.API_KEY: self.data = 'Incorrect API key' return else: self.apikey = kwargs.pop('apikey') - + if 'cmd' not in kwargs: self.data = 'Missing parameter: cmd' return - + if kwargs['cmd'] not in cmd_list: self.data = 'Unknown command: %s' % kwargs['cmd'] return else: self.cmd = kwargs.pop('cmd') - + self.kwargs = kwargs self.data = 'OK' def fetchData(self): - + if self.data == 'OK': logger.info('Recieved API command: %s', self.cmd) methodToCall = getattr(self, "_" + self.cmd) @@ -95,74 +95,74 @@ class Api(object): return self.data else: return self.data - + def _dic_from_query(self,query): - + myDB = db.DBConnection() rows = myDB.select(query) - + rows_as_dic = [] - + for row in rows: row_as_dic = dict(zip(row.keys(), row)) rows_as_dic.append(row_as_dic) - + return rows_as_dic - + def _getIndex(self, **kwargs): - + self.data = self._dic_from_query('SELECT * from artists order by ArtistSortName COLLATE NOCASE') - return - + return + def _getArtist(self, **kwargs): - + if 'id' not in kwargs: self.data = 'Missing parameter: id' return else: self.id = kwargs['id'] - + artist = self._dic_from_query('SELECT * from artists WHERE ArtistID="' + self.id + '"') albums = self._dic_from_query('SELECT * from albums WHERE ArtistID="' + self.id + '" order by ReleaseDate DESC') description = self._dic_from_query('SELECT * from descriptions WHERE ArtistID="' + self.id + '"') - + self.data = { 'artist': artist, 'albums': albums, 'description' : description } return - + def _getAlbum(self, **kwargs): - + if 'id' not in kwargs: self.data = 'Missing parameter: id' return else: self.id = kwargs['id'] - + album = self._dic_from_query('SELECT * from albums WHERE AlbumID="' + self.id + '"') tracks = self._dic_from_query('SELECT * from tracks WHERE AlbumID="' + self.id + '"') description = self._dic_from_query('SELECT * from descriptions WHERE ReleaseGroupID="' + self.id + '"') - + self.data = { 'album' : album, 'tracks' : tracks, 'description' : description } return - + def _getHistory(self, **kwargs): self.data = self._dic_from_query('SELECT * from snatched order by DateAdded DESC') return - + def _getUpcoming(self, **kwargs): self.data = self._dic_from_query("SELECT * from albums WHERE ReleaseDate > date('now') order by ReleaseDate DESC") return - + def _getWanted(self, **kwargs): self.data = self._dic_from_query("SELECT * from albums WHERE Status='Wanted'") return - + def _getSimilar(self, **kwargs): self.data = self._dic_from_query('SELECT * from lastfmcloud') return - + def _getLogs(self, **kwargs): pass - + def _findArtist(self, **kwargs): if 'name' not in kwargs: self.data = 'Missing parameter: name' @@ -171,7 +171,7 @@ class Api(object): limit = kwargs['limit'] else: limit=50 - + self.data = mb.findArtist(kwargs['name'], limit) def _findAlbum(self, **kwargs): @@ -182,216 +182,216 @@ class Api(object): limit = kwargs['limit'] else: limit=50 - + self.data = mb.findRelease(kwargs['name'], limit) - + def _addArtist(self, **kwargs): if 'id' not in kwargs: self.data = 'Missing parameter: id' return else: self.id = kwargs['id'] - + try: importer.addArtisttoDB(self.id) except Exception, e: self.data = e - + return - + def _delArtist(self, **kwargs): if 'id' not in kwargs: self.data = 'Missing parameter: id' return else: self.id = kwargs['id'] - + myDB = db.DBConnection() myDB.action('DELETE from artists WHERE ArtistID="' + self.id + '"') myDB.action('DELETE from albums WHERE ArtistID="' + self.id + '"') myDB.action('DELETE from tracks WHERE ArtistID="' + self.id + '"') - + def _pauseArtist(self, **kwargs): if 'id' not in kwargs: self.data = 'Missing parameter: id' return else: self.id = kwargs['id'] - + myDB = db.DBConnection() controlValueDict = {'ArtistID': self.id} newValueDict = {'Status': 'Paused'} myDB.upsert("artists", newValueDict, controlValueDict) - + def _resumeArtist(self, **kwargs): if 'id' not in kwargs: self.data = 'Missing parameter: id' return else: self.id = kwargs['id'] - + myDB = db.DBConnection() controlValueDict = {'ArtistID': self.id} newValueDict = {'Status': 'Active'} myDB.upsert("artists", newValueDict, controlValueDict) - + def _refreshArtist(self, **kwargs): if 'id' not in kwargs: self.data = 'Missing parameter: id' return else: self.id = kwargs['id'] - + try: importer.addArtisttoDB(self.id) except Exception, e: self.data = e - + return - + def _addAlbum(self, **kwargs): if 'id' not in kwargs: self.data = 'Missing parameter: id' return else: self.id = kwargs['id'] - + try: importer.addReleaseById(self.id) except Exception, e: self.data = e - + return - + def _queueAlbum(self, **kwargs): - + if 'id' not in kwargs: self.data = 'Missing parameter: id' return else: self.id = kwargs['id'] - + if 'new' in kwargs: new = kwargs['new'] else: new = False - + if 'lossless' in kwargs: lossless = kwargs['lossless'] else: lossless = False - + myDB = db.DBConnection() controlValueDict = {'AlbumID': self.id} if lossless: newValueDict = {'Status': 'Wanted Lossless'} - else: + else: newValueDict = {'Status': 'Wanted'} myDB.upsert("albums", newValueDict, controlValueDict) - searcher.searchforalbum(self.id, new) - + searcher.searchforalbum(self.id, new) + def _unqueueAlbum(self, **kwargs): - + if 'id' not in kwargs: self.data = 'Missing parameter: id' return else: self.id = kwargs['id'] - + myDB = db.DBConnection() controlValueDict = {'AlbumID': self.id} newValueDict = {'Status': 'Skipped'} myDB.upsert("albums", newValueDict, controlValueDict) - + def _forceSearch(self, **kwargs): searcher.searchforalbum() - + def _forceProcess(self, **kwargs): self.dir = None if 'dir' in kwargs: self.dir = kwargs['dir'] postprocessor.forcePostProcess(self.dir) - + def _getVersion(self, **kwargs): - self.data = { + self.data = { 'git_path' : headphones.GIT_PATH, 'install_type' : headphones.INSTALL_TYPE, 'current_version' : headphones.CURRENT_VERSION, 'latest_version' : headphones.LATEST_VERSION, 'commits_behind' : headphones.COMMITS_BEHIND, } - + def _checkGithub(self, **kwargs): versioncheck.checkGithub() self._getVersion() - + def _shutdown(self, **kwargs): headphones.SIGNAL = 'shutdown' - + def _restart(self, **kwargs): headphones.SIGNAL = 'restart' - + def _update(self, **kwargs): headphones.SIGNAL = 'update' - + def _getArtistArt(self, **kwargs): - + if 'id' not in kwargs: self.data = 'Missing parameter: id' return else: self.id = kwargs['id'] - + self.data = cache.getArtwork(ArtistID=self.id) def _getAlbumArt(self, **kwargs): - + if 'id' not in kwargs: self.data = 'Missing parameter: id' return else: self.id = kwargs['id'] - + self.data = cache.getArtwork(AlbumID=self.id) - + def _getArtistInfo(self, **kwargs): - + if 'id' not in kwargs: self.data = 'Missing parameter: id' return else: self.id = kwargs['id'] - + self.data = cache.getInfo(ArtistID=self.id) - + def _getAlbumInfo(self, **kwargs): - + if 'id' not in kwargs: self.data = 'Missing parameter: id' return else: self.id = kwargs['id'] - + self.data = cache.getInfo(AlbumID=self.id) - + def _getArtistThumb(self, **kwargs): - + if 'id' not in kwargs: self.data = 'Missing parameter: id' return else: self.id = kwargs['id'] - + self.data = cache.getThumb(ArtistID=self.id) def _getAlbumThumb(self, **kwargs): - + if 'id' not in kwargs: self.data = 'Missing parameter: id' return else: self.id = kwargs['id'] - + self.data = cache.getThumb(AlbumID=self.id) def _choose_specific_download(self, **kwargs): @@ -403,9 +403,9 @@ class Api(object): self.id = kwargs['id'] results = searcher.searchforalbum(self.id, choose_specific_download=True) - + results_as_dicts = [] - + for result in results: result_dict = { diff --git a/headphones/db.py b/headphones/db.py index 7068ba0e..576d2533 100644 --- a/headphones/db.py +++ b/headphones/db.py @@ -31,7 +31,7 @@ from headphones import logger def dbFilename(filename="headphones.db"): return os.path.join(headphones.DATA_DIR, filename) - + def getCacheSize(): #this will protect against typecasting problems produced by empty string and None settings if not headphones.CACHE_SIZEMB: @@ -42,25 +42,25 @@ def getCacheSize(): class DBConnection: def __init__(self, filename="headphones.db"): - + self.filename = filename self.connection = sqlite3.connect(dbFilename(filename), timeout=20) #don't wait for the disk to finish writing self.connection.execute("PRAGMA synchronous = OFF") #journal disabled since we never do rollbacks - self.connection.execute("PRAGMA journal_mode = %s" % headphones.JOURNAL_MODE) + self.connection.execute("PRAGMA journal_mode = %s" % headphones.JOURNAL_MODE) #64mb of cache memory,probably need to make it user configurable self.connection.execute("PRAGMA cache_size=-%s" % (getCacheSize()*1024)) self.connection.row_factory = sqlite3.Row - + def action(self, query, args=None): if query == None: return - + sqlResult = None attempt = 0 - + while attempt < 5: try: if args == None: @@ -82,28 +82,28 @@ class DBConnection: except sqlite3.DatabaseError, e: logger.error('Fatal Error executing %s :: %s', query, e) raise - + return sqlResult - + def select(self, query, args=None): - + sqlResults = self.action(query, args).fetchall() - + if sqlResults == None: return [] - + return sqlResults - + def upsert(self, tableName, valueDict, keyDict): - + changesBefore = self.connection.total_changes - + genParams = lambda myDict : [x + " = ?" for x in myDict.keys()] - + query = "UPDATE "+tableName+" SET " + ", ".join(genParams(valueDict)) + " WHERE " + " AND ".join(genParams(keyDict)) - + self.action(query, valueDict.values() + keyDict.values()) - + if self.connection.total_changes == changesBefore: query = "INSERT INTO "+tableName+" (" + ", ".join(valueDict.keys() + keyDict.keys()) + ")" + \ " VALUES (" + ", ".join(["?"] * len(valueDict.keys() + keyDict.keys())) + ")" diff --git a/headphones/getXldProfile.py b/headphones/getXldProfile.py index 6c744a87..e083d1b6 100755 --- a/headphones/getXldProfile.py +++ b/headphones/getXldProfile.py @@ -8,7 +8,7 @@ from headphones import logger def getXldProfile(xldProfile): xldProfileNotFound = xldProfile expandedPath = os.path.expanduser('~/Library/Preferences/jp.tmkk.XLD.plist') - try: + try: preferences = plistlib.Plist.fromFile(expandedPath) except (expat.ExpatError): os.system("/usr/bin/plutil -convert xml1 %s" % expandedPath ) @@ -61,7 +61,7 @@ def getXldProfile(xldProfile): elif 'TVBR' in ShortDesc: XLDAacOutput2_VBRQuality = int(profile.get('XLDAacOutput2_VBRQuality')) if XLDAacOutput2_VBRQuality > 122: - xldBitrate = 320 + xldBitrate = 320 elif XLDAacOutput2_VBRQuality > 113 and XLDAacOutput2_VBRQuality <= 122: xldBitrate = 285 elif XLDAacOutput2_VBRQuality > 104 and XLDAacOutput2_VBRQuality <= 113: diff --git a/headphones/importer.py b/headphones/importer.py index 36eecaf5..a94bf35c 100644 --- a/headphones/importer.py +++ b/headphones/importer.py @@ -226,27 +226,27 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): skip_log = 0 #Make a user configurable variable to skip update of albums with release dates older than this date (in days) pause_delta = headphones.MB_IGNORE_AGE - + rg_exists = myDB.action("SELECT * from albums WHERE AlbumID=?", [rg['id']]).fetchone() if not forcefull: - + new_release_group = False - + try: check_release_date = rg_exists['ReleaseDate'] except TypeError: check_release_date = None new_release_group = True - - + + if new_release_group: - + logger.info("[%s] Now adding: %s (New Release Group)" % (artist['artist_name'], rg['title'])) new_releases = mb.get_new_releases(rgid,includeExtras) - + else: - + if check_release_date is None or check_release_date == u"None": logger.info("[%s] Now updating: %s (No Release Date)" % (artist['artist_name'], rg['title'])) new_releases = mb.get_new_releases(rgid,includeExtras,True) @@ -384,7 +384,7 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): # If there's no release in the main albums tables, add the default (hybrid) # If there is a release, check the ReleaseID against the AlbumID to see if they differ (user updated) # check if the album already exists - + if not rg_exists: releaseid = rg['id'] else: @@ -410,7 +410,7 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): if rg_exists: newValueDict['DateAdded'] = rg_exists['DateAdded'] newValueDict['Status'] = rg_exists['Status'] - + else: today = helpers.today() diff --git a/headphones/librarysync.py b/headphones/librarysync.py index 6fe13e17..416023fc 100644 --- a/headphones/librarysync.py +++ b/headphones/librarysync.py @@ -27,18 +27,18 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal if cron and not headphones.LIBRARYSCAN: return - + if not dir: if not headphones.MUSIC_DIR: return else: dir = headphones.MUSIC_DIR - + # If we're appending a dir, it's coming from the post processor which is # already bytestring if not append: dir = dir.encode(headphones.SYS_ENCODING) - + if not os.path.isdir(dir): logger.warn('Cannot find directory: %s. Not scanning' % dir.decode(headphones.SYS_ENCODING, 'replace')) return @@ -47,7 +47,7 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal new_artists = [] logger.info('Scanning music directory: %s' % dir.decode(headphones.SYS_ENCODING, 'replace')) - + if not append: # Clean up bad filepaths tracks = myDB.select('SELECT Location, TrackID from alltracks WHERE Location IS NOT NULL') @@ -57,7 +57,7 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal if not os.path.isfile(encoded_track_string): myDB.action('UPDATE tracks SET Location=?, BitRate=?, Format=? WHERE Location=?', [None, None, None, track['Location']]) myDB.action('UPDATE alltracks SET Location=?, BitRate=?, Format=? WHERE Location=?', [None, None, None, track['Location']]) - + del_have_tracks = myDB.select('SELECT Location, Matched, ArtistName from have') for track in del_have_tracks: @@ -71,13 +71,13 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal ###############myDB.action('DELETE from have') bitrates = [] - + song_list = [] new_song_count = 0 file_count = 0 latest_subdirectory = [] - + for r,d,f in os.walk(dir): #need to abuse slicing to get a copy of the list, doing it directly will skip the element after a deleted one #using a list comprehension will not work correctly for nested subdirectories (os.walk keeps its original list) @@ -108,11 +108,11 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal except: logger.error('Cannot read file: ' + unicode_song_path) continue - + # Grab the bitrates for the auto detect bit rate option if f.bitrate: bitrates.append(f.bitrate) - + # Use the album artist over the artist if available if f.albumartist: f_artist = f.albumartist @@ -120,8 +120,8 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal f_artist = f.artist else: f_artist = None - - # Add the song to our song list - + + # Add the song to our song list - # TODO: skip adding songs without the minimum requisite information (just a matter of putting together the right if statements) if f_artist and f.album and f.title: @@ -144,7 +144,7 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal 'Format' : f.format, 'CleanName' : CleanName } - + #song_list.append(song_dict) check_exist_song = myDB.action("SELECT * FROM have WHERE Location=?", [unicode_song_path]).fetchone() #Only attempt to match songs that are new, haven't yet been matched, or metadata has changed. @@ -182,17 +182,17 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal song_list = myDB.action("SELECT * FROM have WHERE Matched IS NULL AND LOCATION LIKE ?", [dir.decode(headphones.SYS_ENCODING, 'replace')+"%"]) total_number_of_songs = myDB.action("SELECT COUNT(*) FROM have WHERE Matched IS NULL AND LOCATION LIKE ?", [dir.decode(headphones.SYS_ENCODING, 'replace')+"%"]).fetchone()[0] logger.info("Found " + str(total_number_of_songs) + " new/modified tracks in: '" + dir.decode(headphones.SYS_ENCODING, 'replace') + "'. Matching tracks to the appropriate releases....") - + # Sort the song_list by most vague (e.g. no trackid or releaseid) to most specific (both trackid & releaseid) # When we insert into the database, the tracks with the most specific information will overwrite the more general matches - + ##############song_list = helpers.multikeysort(song_list, ['ReleaseID', 'TrackID']) song_list = helpers.multikeysort(song_list, ['ArtistName', 'AlbumTitle']) - + # We'll use this to give a % completion, just because the track matching might take a while song_count = 0 latest_artist = [] - + for song in song_list: latest_artist.append(song['ArtistName']) @@ -200,26 +200,26 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal logger.info("Now matching songs by %s" % song['ArtistName']) elif latest_artist[song_count] != latest_artist[song_count-1] and song_count !=0: logger.info("Now matching songs by %s" % song['ArtistName']) - + #print song['ArtistName']+' - '+song['AlbumTitle']+' - '+song['TrackTitle'] song_count += 1 completion_percentage = float(song_count)/total_number_of_songs * 100 - + if completion_percentage%10 == 0: logger.info("Track matching is " + str(completion_percentage) + "% complete") - + #THE "MORE-SPECIFIC" CLAUSES HERE HAVE ALL BEEN REMOVED. WHEN RUNNING A LIBRARY SCAN, THE ONLY CLAUSES THAT #EVER GOT HIT WERE [ARTIST/ALBUM/TRACK] OR CLEANNAME. ARTISTID & RELEASEID ARE NEVER PASSED TO THIS FUNCTION, #ARE NEVER FOUND, AND THE OTHER CLAUSES WERE NEVER HIT. FURTHERMORE, OTHER MATCHING FUNCTIONS IN THIS PROGRAM #(IMPORTER.PY, MB.PY) SIMPLY DO A [ARTIST/ALBUM/TRACK] OR CLEANNAME MATCH, SO IT'S ALL CONSISTENT. if song['ArtistName'] and song['AlbumTitle'] and song['TrackTitle']: - + track = myDB.action('SELECT ArtistName, AlbumTitle, TrackTitle, AlbumID from tracks WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [song['ArtistName'], song['AlbumTitle'], song['TrackTitle']]).fetchone() if track: controlValueDict = { 'ArtistName' : track['ArtistName'], 'AlbumTitle' : track['AlbumTitle'], - 'TrackTitle' : track['TrackTitle'] } + 'TrackTitle' : track['TrackTitle'] } newValueDict = { 'Location' : song['Location'], 'BitRate' : song['BitRate'], 'Format' : song['Format'] } @@ -231,10 +231,10 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal else: track = myDB.action('SELECT CleanName, AlbumID from tracks WHERE CleanName LIKE ?', [song['CleanName']]).fetchone() if track: - controlValueDict = { 'CleanName' : track['CleanName']} + controlValueDict = { 'CleanName' : track['CleanName']} newValueDict = { 'Location' : song['Location'], 'BitRate' : song['BitRate'], - 'Format' : song['Format'] } + 'Format' : song['Format'] } myDB.upsert("tracks", newValueDict, controlValueDict) controlValueDict2 = { 'Location' : song['Location']} @@ -244,9 +244,9 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal controlValueDict2 = { 'Location' : song['Location']} newValueDict2 = { 'Matched' : "Failed"} myDB.upsert("have", newValueDict2, controlValueDict2) - - alltrack = myDB.action('SELECT ArtistName, AlbumTitle, TrackTitle, AlbumID from alltracks WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [song['ArtistName'], song['AlbumTitle'], song['TrackTitle']]).fetchone() + + alltrack = myDB.action('SELECT ArtistName, AlbumTitle, TrackTitle, AlbumID from alltracks WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [song['ArtistName'], song['AlbumTitle'], song['TrackTitle']]).fetchone() if alltrack: controlValueDict = { 'ArtistName' : alltrack['ArtistName'], 'AlbumTitle' : alltrack['AlbumTitle'], @@ -262,10 +262,10 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal else: alltrack = myDB.action('SELECT CleanName, AlbumID from alltracks WHERE CleanName LIKE ?', [song['CleanName']]).fetchone() if alltrack: - controlValueDict = { 'CleanName' : alltrack['CleanName']} + controlValueDict = { 'CleanName' : alltrack['CleanName']} newValueDict = { 'Location' : song['Location'], 'BitRate' : song['BitRate'], - 'Format' : song['Format'] } + 'Format' : song['Format'] } myDB.upsert("alltracks", newValueDict, controlValueDict) controlValueDict2 = { 'Location' : song['Location']} @@ -279,35 +279,35 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal controlValueDict2 = { 'Location' : song['Location']} newValueDict2 = { 'Matched' : "Failed"} myDB.upsert("have", newValueDict2, controlValueDict2) - + #######myDB.action('INSERT INTO have (ArtistName, AlbumTitle, TrackNumber, TrackTitle, TrackLength, BitRate, Genre, Date, TrackID, Location, CleanName, Format) VALUES( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [song['ArtistName'], song['AlbumTitle'], song['TrackNumber'], song['TrackTitle'], song['TrackLength'], song['BitRate'], song['Genre'], song['Date'], song['TrackID'], song['Location'], CleanName, song['Format']]) logger.info('Completed matching tracks from directory: %s' % dir.decode(headphones.SYS_ENCODING, 'replace')) - + if not append: logger.info('Updating scanned artist track counts') - + # Clean up the new artist list unique_artists = {}.fromkeys(new_artists).keys() current_artists = myDB.select('SELECT ArtistName, ArtistID from artists') - #There was a bug where artists with special characters (-,') would show up in new artists. + #There was a bug where artists with special characters (-,') would show up in new artists. artist_list = [f for f in unique_artists if helpers.cleanName(f).lower() not in [helpers.cleanName(x[0]).lower() for x in current_artists]] artists_checked = [f for f in unique_artists if helpers.cleanName(f).lower() in [helpers.cleanName(x[0]).lower() for x in current_artists]] # Update track counts - + for artist in artists_checked: # Have tracks are selected from tracks table and not all tracks because of duplicates # We update the track count upon an album switch to compliment this havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistName like ? AND Location IS NOT NULL', [artist])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ? AND Matched = "Failed"', [artist])) - #Note, some people complain about having "artist have tracks" > # of tracks total in artist official releases + #Note, some people complain about having "artist have tracks" > # of tracks total in artist official releases # (can fix by getting rid of second len statement) myDB.action('UPDATE artists SET HaveTracks=? WHERE ArtistName=?', [havetracks, artist]) - + logger.info('Found %i new artists' % len(artist_list)) - + if len(artist_list): if headphones.ADD_ARTISTS: logger.info('Importing %i new artists' % len(artist_list)) @@ -317,14 +317,14 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal #myDB.action('DELETE from newartists') for artist in artist_list: myDB.action('INSERT OR IGNORE INTO newartists VALUES (?)', [artist]) - + if headphones.DETECT_BITRATE: headphones.PREFERRED_BITRATE = sum(bitrates)/len(bitrates)/1000 - + else: # If we're appending a new album to the database, update the artists total track counts logger.info('Updating artist track counts') - + havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=? AND Location IS NOT NULL', [ArtistID])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ? AND Matched = "Failed"', [ArtistName])) myDB.action('UPDATE artists SET HaveTracks=? WHERE ArtistID=?', [havetracks, ArtistID]) @@ -357,7 +357,7 @@ def update_album_status(AlbumID=None): if album_completion >= headphones.ALBUM_COMPLETION_PCT and album['Status'] == 'Skipped': new_album_status = "Downloaded" - + # I don't think we want to change Downloaded->Skipped..... # I think we can only automatically change Skipped->Downloaded when updating # There was a bug report where this was causing infinite downloads if the album was @@ -369,7 +369,7 @@ def update_album_status(AlbumID=None): # new_album_status = album['Status'] else: new_album_status = album['Status'] - + myDB.upsert("albums", {'Status' : new_album_status}, {'AlbumID' : album['AlbumID']}) if new_album_status != album['Status']: logger.info('Album %s changed to %s' % (album['AlbumTitle'], new_album_status)) diff --git a/headphones/lyrics.py b/headphones/lyrics.py index aa3c952a..ea8458ee 100644 --- a/headphones/lyrics.py +++ b/headphones/lyrics.py @@ -27,26 +27,26 @@ def getLyrics(artist, song): url = 'http://lyrics.wikia.com/api.php' data = request.request_minidom(url, params=params) - + if not data: return - + url = data.getElementsByTagName("url") - + if url: lyricsurl = url[0].firstChild.nodeValue else: logger.info('No lyrics found for %s - %s' % (artist, song)) return - + lyricspage = request.request_content(lyricsurl) - + if not lyricspage: logger.warn('Error fetching lyrics from: %s' % lyricsurl) return m = re.compile('''
.*?
(.*?) # Reconstruct the original comment. self.pieces.append('' % locals()) - + def handle_pi(self, text): # called for each processing instruction, e.g. # Reconstruct original processing instruction. @@ -1942,7 +1942,7 @@ class _BaseHTMLProcessor(sgmllib.SGMLParser): # "http://www.w3.org/TR/html4/loose.dtd"> # Reconstruct original DOCTYPE self.pieces.append('' % locals()) - + _new_declname_match = re.compile(r'[a-zA-Z][-_.a-zA-Z0-9:]*\s*').match def _scan_name(self, i, declstartpos): rawdata = self.rawdata @@ -2006,7 +2006,7 @@ class _LooseFeedParser(_FeedParserMixin, _BaseHTMLProcessor): data = data.replace('"', '"') data = data.replace(''', "'") return data - + def strattrs(self, attrs): return ''.join([' %s="%s"' % (n,v.replace('"','"')) for n,v in attrs]) @@ -2030,12 +2030,12 @@ class _MicroformatsParser: self.enclosures = [] self.xfn = [] self.vcard = None - + def vcardEscape(self, s): if type(s) in (type(''), type(u'')): s = s.replace(',', '\\,').replace(';', '\\;').replace('\n', '\\n') return s - + def vcardFold(self, s): s = re.sub(';+$', '', s) sFolded = '' @@ -2051,14 +2051,14 @@ class _MicroformatsParser: def normalize(self, s): return re.sub(r'\s+', ' ', s).strip() - + def unique(self, aList): results = [] for element in aList: if element not in results: results.append(element) return results - + def toISO8601(self, dt): return time.strftime('%Y-%m-%dT%H:%M:%SZ', dt) @@ -2148,21 +2148,21 @@ class _MicroformatsParser: def findVCards(self, elmRoot, bAgentParsing=0): sVCards = '' - + if not bAgentParsing: arCards = self.getPropertyValue(elmRoot, 'vcard', bAllowMultiple=1) else: arCards = [elmRoot] - + for elmCard in arCards: arLines = [] - + def processSingleString(sProperty): sValue = self.getPropertyValue(elmCard, sProperty, self.STRING, bAutoEscape=1).decode(self.encoding) if sValue: arLines.append(self.vcardFold(sProperty.upper() + ':' + sValue)) return sValue or u'' - + def processSingleURI(sProperty): sValue = self.getPropertyValue(elmCard, sProperty, self.URI) if sValue: @@ -2185,7 +2185,7 @@ class _MicroformatsParser: if sContentType: sContentType = ';TYPE=' + sContentType.upper() arLines.append(self.vcardFold(sProperty.upper() + sEncoding + sContentType + sValueKey + ':' + sValue)) - + def processTypeValue(sProperty, arDefaultType, arForceType=None): arResults = self.getPropertyValue(elmCard, sProperty, bAllowMultiple=1) for elmResult in arResults: @@ -2197,7 +2197,7 @@ class _MicroformatsParser: sValue = self.getPropertyValue(elmResult, 'value', self.EMAIL, 0) if sValue: arLines.append(self.vcardFold(sProperty.upper() + ';TYPE=' + ','.join(arType) + ':' + sValue)) - + # AGENT # must do this before all other properties because it is destructive # (removes nested class="vcard" nodes so they don't interfere with @@ -2216,10 +2216,10 @@ class _MicroformatsParser: sAgentValue = self.getPropertyValue(elmAgent, 'value', self.URI, bAutoEscape=1); if sAgentValue: arLines.append(self.vcardFold('AGENT;VALUE=uri:' + sAgentValue)) - + # FN (full name) sFN = processSingleString('fn') - + # N (name) elmName = self.getPropertyValue(elmCard, 'n') if elmName: @@ -2228,7 +2228,7 @@ class _MicroformatsParser: arAdditionalNames = self.getPropertyValue(elmName, 'additional-name', self.STRING, 1, 1) + self.getPropertyValue(elmName, 'additional-names', self.STRING, 1, 1) arHonorificPrefixes = self.getPropertyValue(elmName, 'honorific-prefix', self.STRING, 1, 1) + self.getPropertyValue(elmName, 'honorific-prefixes', self.STRING, 1, 1) arHonorificSuffixes = self.getPropertyValue(elmName, 'honorific-suffix', self.STRING, 1, 1) + self.getPropertyValue(elmName, 'honorific-suffixes', self.STRING, 1, 1) - arLines.append(self.vcardFold('N:' + sFamilyName + ';' + + arLines.append(self.vcardFold('N:' + sFamilyName + ';' + sGivenName + ';' + ','.join(arAdditionalNames) + ';' + ','.join(arHonorificPrefixes) + ';' + @@ -2245,25 +2245,25 @@ class _MicroformatsParser: arLines.append(self.vcardFold('N:' + arNames[0] + ';' + arNames[1])) else: arLines.append(self.vcardFold('N:' + arNames[1] + ';' + arNames[0])) - + # SORT-STRING sSortString = self.getPropertyValue(elmCard, 'sort-string', self.STRING, bAutoEscape=1) if sSortString: arLines.append(self.vcardFold('SORT-STRING:' + sSortString)) - + # NICKNAME arNickname = self.getPropertyValue(elmCard, 'nickname', self.STRING, 1, 1) if arNickname: arLines.append(self.vcardFold('NICKNAME:' + ','.join(arNickname))) - + # PHOTO processSingleURI('photo') - + # BDAY dtBday = self.getPropertyValue(elmCard, 'bday', self.DATE) if dtBday: arLines.append(self.vcardFold('BDAY:' + self.toISO8601(dtBday))) - + # ADR (address) arAdr = self.getPropertyValue(elmCard, 'adr', bAllowMultiple=1) for elmAdr in arAdr: @@ -2285,38 +2285,38 @@ class _MicroformatsParser: sRegion + ';' + sPostalCode + ';' + sCountryName)) - + # LABEL processTypeValue('label', ['intl','postal','parcel','work']) - + # TEL (phone number) processTypeValue('tel', ['voice']) - + # EMAIL processTypeValue('email', ['internet'], ['internet']) - + # MAILER processSingleString('mailer') - + # TZ (timezone) processSingleString('tz') - + # GEO (geographical information) elmGeo = self.getPropertyValue(elmCard, 'geo') if elmGeo: sLatitude = self.getPropertyValue(elmGeo, 'latitude', self.STRING, 0, 1) sLongitude = self.getPropertyValue(elmGeo, 'longitude', self.STRING, 0, 1) arLines.append(self.vcardFold('GEO:' + sLatitude + ';' + sLongitude)) - + # TITLE processSingleString('title') - + # ROLE processSingleString('role') # LOGO processSingleURI('logo') - + # ORG (organization) elmOrg = self.getPropertyValue(elmCard, 'org') if elmOrg: @@ -2330,39 +2330,39 @@ class _MicroformatsParser: else: arOrganizationUnit = self.getPropertyValue(elmOrg, 'organization-unit', self.STRING, 1, 1) arLines.append(self.vcardFold('ORG:' + sOrganizationName + ';' + ';'.join(arOrganizationUnit))) - + # CATEGORY arCategory = self.getPropertyValue(elmCard, 'category', self.STRING, 1, 1) + self.getPropertyValue(elmCard, 'categories', self.STRING, 1, 1) if arCategory: arLines.append(self.vcardFold('CATEGORIES:' + ','.join(arCategory))) - + # NOTE processSingleString('note') - + # REV processSingleString('rev') - + # SOUND processSingleURI('sound') - + # UID processSingleString('uid') - + # URL processSingleURI('url') - + # CLASS processSingleString('class') - + # KEY processSingleURI('key') - + if arLines: arLines = [u'BEGIN:vCard',u'VERSION:3.0'] + arLines + [u'END:vCard'] sVCards += u'\n'.join(arLines) + u'\n' - + return sVCards.strip() - + def isProbablyDownloadable(self, elm): attrsD = elm.attrMap if not attrsD.has_key('href'): return 0 @@ -2461,7 +2461,7 @@ class _RelativeURIResolver(_BaseHTMLProcessor): def resolveURI(self, uri): return _makeSafeAbsoluteURI(_urljoin(self.baseuri, uri.strip())) - + def unknown_starttag(self, tag, attrs): if _debug: sys.stderr.write('tag: [%s] with attributes: [%s]\n' % (tag, str(attrs))) @@ -2575,7 +2575,7 @@ class _HTMLSanitizer(_BaseHTMLProcessor): # svgtiny - foreignObject + linearGradient + radialGradient + stop svg_elements = ['a', 'animate', 'animateColor', 'animateMotion', 'animateTransform', 'circle', 'defs', 'desc', 'ellipse', 'foreignObject', - 'font-face', 'font-face-name', 'font-face-src', 'g', 'glyph', 'hkern', + 'font-face', 'font-face-name', 'font-face-src', 'g', 'glyph', 'hkern', 'linearGradient', 'line', 'marker', 'metadata', 'missing-glyph', 'mpath', 'path', 'polygon', 'polyline', 'radialGradient', 'rect', 'set', 'stop', 'svg', 'switch', 'text', 'title', 'tspan', 'use'] @@ -2621,7 +2621,7 @@ class _HTMLSanitizer(_BaseHTMLProcessor): self.unacceptablestack = 0 self.mathmlOK = 0 self.svgOK = 0 - + def unknown_starttag(self, tag, attrs): acceptable_attributes = self.acceptable_attributes keymap = {} @@ -2683,7 +2683,7 @@ class _HTMLSanitizer(_BaseHTMLProcessor): clean_value = self.sanitize_style(value) if clean_value: clean_attrs.append((key,clean_value)) _BaseHTMLProcessor.unknown_starttag(self, tag, clean_attrs) - + def unknown_endtag(self, tag): if not tag in self.acceptable_elements: if tag in self.unacceptable_elements_with_end_tag: @@ -2815,7 +2815,7 @@ class _FeedURLHandler(urllib2.HTTPDigestAuthHandler, urllib2.HTTPRedirectHandler http_error_300 = http_error_302 http_error_303 = http_error_302 http_error_307 = http_error_302 - + def http_error_401(self, req, fp, code, msg, headers): # Check if # - server requires digest auth, AND @@ -2914,7 +2914,7 @@ def _open_resource(url_file_stream_or_string, etag, modified, agent, referrer, h return opener.open(request, timeout=15) finally: opener.close() # JohnD - + # try to open with native open function (if url_file_stream_or_string is a filename) try: return open(url_file_stream_or_string, 'rb') @@ -2966,7 +2966,7 @@ _date_handlers = [] def registerDateHandler(func): '''Register a date handler function (takes string, returns 9-tuple date in GMT)''' _date_handlers.insert(0, func) - + # ISO-8601 date parsing routines written by Fazal Majid. # The ISO 8601 standard is very convoluted and irregular - a full ISO 8601 # parser is beyond the scope of feedparser and would be a worthwhile addition @@ -2977,7 +2977,7 @@ def registerDateHandler(func): # Please note the order in templates is significant because we need a # greedy match. _iso8601_tmpl = ['YYYY-?MM-?DD', 'YYYY-0MM?-?DD', 'YYYY-MM', 'YYYY-?OOO', - 'YY-?MM-?DD', 'YY-?OOO', 'YYYY', + 'YY-?MM-?DD', 'YY-?OOO', 'YYYY', '-YY-?MM', '-OOO', '-YY', '--MM-?DD', '--MM', '---DD', @@ -3079,7 +3079,7 @@ def _parse_date_iso8601(dateString): # Many implementations have bugs, but we'll pretend they don't. return time.localtime(time.mktime(tuple(tm))) registerDateHandler(_parse_date_iso8601) - + # 8-bit date handling routines written by ytrewq1. _korean_year = u'\ub144' # b3e2 in euc-kr _korean_month = u'\uc6d4' # bff9 in euc-kr @@ -3170,7 +3170,7 @@ _greek_wdays = \ u'\u03a4\u03b5\u03c4': u'Wed', # d4e5f4 in iso-8859-7 u'\u03a0\u03b5\u03bc': u'Thu', # d0e5ec in iso-8859-7 u'\u03a0\u03b1\u03c1': u'Fri', # d0e1f1 in iso-8859-7 - u'\u03a3\u03b1\u03b2': u'Sat', # d3e1e2 in iso-8859-7 + u'\u03a3\u03b1\u03b2': u'Sat', # d3e1e2 in iso-8859-7 } _greek_date_format_re = \ @@ -3360,7 +3360,7 @@ def _parse_date_rfc822(dateString): # 'ET' is equivalent to 'EST', etc. _additional_timezones = {'AT': -400, 'ET': -500, 'CT': -600, 'MT': -700, 'PT': -800} rfc822._timezones.update(_additional_timezones) -registerDateHandler(_parse_date_rfc822) +registerDateHandler(_parse_date_rfc822) def _parse_date_perforce(aDateString): """parse a date in yyyy/mm/dd hh:mm:ss TTT format""" @@ -3398,7 +3398,7 @@ def _getCharacterEncoding(http_headers, xml_data): http_headers is a dictionary xml_data is a raw string (not Unicode) - + This is so much trickier than it sounds, it's not even funny. According to RFC 3023 ('XML Media Types'), if the HTTP Content-Type is application/xml, application/*+xml, @@ -3417,12 +3417,12 @@ def _getCharacterEncoding(http_headers, xml_data): served with a Content-Type of text/* and no charset parameter must be treated as us-ascii. (We now do this.) And also that it must always be flagged as non-well-formed. (We now do this too.) - + If Content-Type is unspecified (input was local file or non-HTTP source) or unrecognized (server just got it totally wrong), then go by the encoding given in the XML prefix of the document and default to 'iso-8859-1' as per the HTTP specification (RFC 2616). - + Then, assuming we didn't find a character encoding in the HTTP headers (and the HTTP Content-type allowed us to look in the body), we need to sniff the first few bytes of the XML data and try to determine @@ -3532,7 +3532,7 @@ def _getCharacterEncoding(http_headers, xml_data): if true_encoding.lower() == 'gb2312': true_encoding = 'gb18030' return true_encoding, http_encoding, xml_encoding, sniffed_xml_encoding, acceptable_content_type - + def _toUTF8(data, encoding): '''Changes an XML data stream on the fly to specify a new encoding @@ -3595,7 +3595,7 @@ def _stripDoctype(data): start = re.search(_s2bytes('<\w'), data) start = start and start.start() or -1 head,data = data[:start+1], data[start+1:] - + entity_pattern = re.compile(_s2bytes(r'^\s*]*?)>'), re.MULTILINE) entity_results=entity_pattern.findall(head) head = entity_pattern.sub(_s2bytes(''), head) @@ -3617,10 +3617,10 @@ def _stripDoctype(data): data = doctype_pattern.sub(replacement, head) + data return version, data, dict(replacement and [(k.decode('utf-8'), v.decode('utf-8')) for k, v in safe_pattern.findall(replacement)]) - + def parse(url_file_stream_or_string, etag=None, modified=None, agent=None, referrer=None, handlers=[], request_headers={}, response_headers={}): '''Parse a feed from a URL, file, stream, or string. - + request_headers, if given, is a dict from http header name to value to add to the request; this overrides internally generated values. ''' @@ -3861,7 +3861,7 @@ class TextSerializer(Serializer): stream.write('\n') except: pass - + class PprintSerializer(Serializer): def write(self, stream=sys.stdout): if self.results.has_key('href'): @@ -3869,7 +3869,7 @@ class PprintSerializer(Serializer): from pprint import pprint pprint(self.results, stream) stream.write('\n') - + if __name__ == '__main__': try: from optparse import OptionParser diff --git a/lib/gntp/core.py b/lib/gntp/core.py index ee544d3d..957b5270 100644 --- a/lib/gntp/core.py +++ b/lib/gntp/core.py @@ -70,7 +70,7 @@ class _GNTPBase(object): 'SHA1': hashlib.sha1, 'SHA256': hashlib.sha256, 'SHA512': hashlib.sha512, - } + } self.headers = {} self.resources = {} diff --git a/lib/httplib2/__init__.py b/lib/httplib2/__init__.py index 01151f7f..58b0de02 100755 --- a/lib/httplib2/__init__.py +++ b/lib/httplib2/__init__.py @@ -3,7 +3,7 @@ from __future__ import generators httplib2 A caching http interface that supports ETags and gzip -to conserve bandwidth. +to conserve bandwidth. Requires Python 2.3 or later @@ -24,8 +24,8 @@ __contributors__ = ["Thomas Broyer (t.broyer@ltgt.net)", __license__ = "MIT" __version__ = "$Rev$" -import re -import sys +import re +import sys import email import email.Utils import email.Message @@ -85,7 +85,7 @@ def has_timeout(timeout): # python 2.6 return (timeout is not None) __all__ = ['Http', 'Response', 'ProxyInfo', 'HttpLib2Error', - 'RedirectMissingLocation', 'RedirectLimit', 'FailedToDecompressContent', + 'RedirectMissingLocation', 'RedirectLimit', 'FailedToDecompressContent', 'UnimplementedDigestAuthOptionError', 'UnimplementedHmacDigestAuthOptionError', 'debuglevel'] @@ -113,8 +113,8 @@ if not hasattr(httplib.HTTPResponse, 'getheaders'): # All exceptions raised here derive from HttpLib2Error class HttpLib2Error(Exception): pass -# Some exceptions can be caught and optionally -# be turned back into responses. +# Some exceptions can be caught and optionally +# be turned back into responses. class HttpLib2ErrorWithResponse(HttpLib2Error): def __init__(self, desc, response, content): self.response = response @@ -176,7 +176,7 @@ def urlnorm(uri): raise RelativeURIError("Only absolute URIs are allowed. uri = %s" % uri) authority = authority.lower() scheme = scheme.lower() - if not path: + if not path: path = "/" # Could do syntax based normalization of the URI before # computing the digest. See Section 6.2.2 of Std 66. @@ -228,7 +228,7 @@ def _parse_cache_control(headers): parts_with_args = [tuple([x.strip().lower() for x in part.split("=", 1)]) for part in parts if -1 != part.find("=")] parts_wo_args = [(name.strip().lower(), 1) for name in parts if -1 == name.find("=")] retval = dict(parts_with_args + parts_wo_args) - return retval + return retval # Whether to use a strict mode to parse WWW-Authenticate headers # Might lead to bad results in case of ill-formed header value, @@ -254,10 +254,10 @@ def _parse_www_authenticate(headers, headername='www-authenticate'): while authenticate: # Break off the scheme at the beginning of the line if headername == 'authentication-info': - (auth_scheme, the_rest) = ('digest', authenticate) + (auth_scheme, the_rest) = ('digest', authenticate) else: (auth_scheme, the_rest) = authenticate.split(" ", 1) - # Now loop over all the key value pairs that come after the scheme, + # Now loop over all the key value pairs that come after the scheme, # being careful not to roll into the next scheme match = www_auth.search(the_rest) auth_params = {} @@ -279,17 +279,17 @@ def _entry_disposition(response_headers, request_headers): 1. Cache-Control: max-stale 2. Age: headers are not used in the calculations. - Not that this algorithm is simpler than you might think + Not that this algorithm is simpler than you might think because we are operating as a private (non-shared) cache. This lets us ignore 's-maxage'. We can also ignore 'proxy-invalidate' since we aren't a proxy. - We will never return a stale document as - fresh as a design decision, and thus the non-implementation - of 'max-stale'. This also lets us safely ignore 'must-revalidate' + We will never return a stale document as + fresh as a design decision, and thus the non-implementation + of 'max-stale'. This also lets us safely ignore 'must-revalidate' since we operate as if every server has sent 'must-revalidate'. Since we are private we get to ignore both 'public' and 'private' parameters. We also ignore 'no-transform' since - we don't do any transformations. + we don't do any transformations. The 'no-store' parameter is handled at a higher level. So the only Cache-Control parameters we look at are: @@ -298,7 +298,7 @@ def _entry_disposition(response_headers, request_headers): max-age min-fresh """ - + retval = "STALE" cc = _parse_cache_control(request_headers) cc_response = _parse_cache_control(response_headers) @@ -340,10 +340,10 @@ def _entry_disposition(response_headers, request_headers): min_fresh = int(cc['min-fresh']) except ValueError: min_fresh = 0 - current_age += min_fresh + current_age += min_fresh if freshness_lifetime > current_age: retval = "FRESH" - return retval + return retval def _decompressContent(response, new_content): content = new_content @@ -408,10 +408,10 @@ def _wsse_username_token(cnonce, iso_now, password): return base64.b64encode(_sha("%s%s%s" % (cnonce, iso_now, password)).digest()).strip() -# For credentials we need two things, first +# For credentials we need two things, first # a pool of credential to try (not necesarily tied to BAsic, Digest, etc.) # Then we also need a list of URIs that have already demanded authentication -# That list is tricky since sub-URIs can take the same auth, or the +# That list is tricky since sub-URIs can take the same auth, or the # auth scheme may change as you descend the tree. # So we also need each Auth instance to be able to tell us # how close to the 'top' it is. @@ -443,7 +443,7 @@ class Authentication(object): or such returned from the last authorized response. Over-rise this in sub-classes if necessary. - Return TRUE is the request is to be retried, for + Return TRUE is the request is to be retried, for example Digest may return stale=true. """ return False @@ -461,7 +461,7 @@ class BasicAuthentication(Authentication): class DigestAuthentication(Authentication): - """Only do qop='auth' and MD5, since that + """Only do qop='auth' and MD5, since that is all Apache currently implements""" def __init__(self, credentials, host, request_uri, headers, response, content, http): Authentication.__init__(self, credentials, host, request_uri, headers, response, content, http) @@ -474,7 +474,7 @@ class DigestAuthentication(Authentication): self.challenge['algorithm'] = self.challenge.get('algorithm', 'MD5').upper() if self.challenge['algorithm'] != 'MD5': raise UnimplementedDigestAuthOptionError( _("Unsupported value for algorithm: %s." % self.challenge['algorithm'])) - self.A1 = "".join([self.credentials[0], ":", self.challenge['realm'], ":", self.credentials[1]]) + self.A1 = "".join([self.credentials[0], ":", self.challenge['realm'], ":", self.credentials[1]]) self.challenge['nc'] = 1 def request(self, method, request_uri, headers, content, cnonce = None): @@ -482,17 +482,17 @@ class DigestAuthentication(Authentication): H = lambda x: _md5(x).hexdigest() KD = lambda s, d: H("%s:%s" % (s, d)) A2 = "".join([method, ":", request_uri]) - self.challenge['cnonce'] = cnonce or _cnonce() - request_digest = '"%s"' % KD(H(self.A1), "%s:%s:%s:%s:%s" % (self.challenge['nonce'], - '%08x' % self.challenge['nc'], - self.challenge['cnonce'], + self.challenge['cnonce'] = cnonce or _cnonce() + request_digest = '"%s"' % KD(H(self.A1), "%s:%s:%s:%s:%s" % (self.challenge['nonce'], + '%08x' % self.challenge['nc'], + self.challenge['cnonce'], self.challenge['qop'], H(A2) - )) + )) headers['Authorization'] = 'Digest username="%s", realm="%s", nonce="%s", uri="%s", algorithm=%s, response=%s, qop=%s, nc=%08x, cnonce="%s"' % ( - self.credentials[0], + self.credentials[0], self.challenge['realm'], self.challenge['nonce'], - request_uri, + request_uri, self.challenge['algorithm'], request_digest, self.challenge['qop'], @@ -506,14 +506,14 @@ class DigestAuthentication(Authentication): challenge = _parse_www_authenticate(response, 'www-authenticate').get('digest', {}) if 'true' == challenge.get('stale'): self.challenge['nonce'] = challenge['nonce'] - self.challenge['nc'] = 1 + self.challenge['nc'] = 1 return True else: updated_challenge = _parse_www_authenticate(response, 'authentication-info').get('digest', {}) if updated_challenge.has_key('nextnonce'): self.challenge['nonce'] = updated_challenge['nextnonce'] - self.challenge['nc'] = 1 + self.challenge['nc'] = 1 return False @@ -562,11 +562,11 @@ class HmacDigestAuthentication(Authentication): request_digest = "%s:%s:%s:%s:%s" % (method, request_uri, cnonce, self.challenge['snonce'], headers_val) request_digest = hmac.new(self.key, request_digest, self.hashmod).hexdigest().lower() headers['Authorization'] = 'HMACDigest username="%s", realm="%s", snonce="%s", cnonce="%s", uri="%s", created="%s", response="%s", headers="%s"' % ( - self.credentials[0], + self.credentials[0], self.challenge['realm'], self.challenge['snonce'], cnonce, - request_uri, + request_uri, created, request_digest, keylist, @@ -583,7 +583,7 @@ class WsseAuthentication(Authentication): """This is thinly tested and should not be relied upon. At this time there isn't any third party server to test against. Blogger and TypePad implemented this algorithm at one point - but Blogger has since switched to Basic over HTTPS and + but Blogger has since switched to Basic over HTTPS and TypePad has implemented it wrong, by never issuing a 401 challenge but instead requiring your client to telepathically know that their endpoint is expecting WSSE profile="UsernameToken".""" @@ -629,7 +629,7 @@ class GoogleLoginAuthentication(Authentication): def request(self, method, request_uri, headers, content): """Modify the request headers to add the appropriate Authorization header.""" - headers['authorization'] = 'GoogleLogin Auth=' + self.Auth + headers['authorization'] = 'GoogleLogin Auth=' + self.Auth AUTH_SCHEME_CLASSES = { @@ -644,13 +644,13 @@ AUTH_SCHEME_ORDER = ["hmacdigest", "googlelogin", "digest", "wsse", "basic"] class FileCache(object): """Uses a local directory as a store for cached files. - Not really safe to use if multiple threads or processes are going to + Not really safe to use if multiple threads or processes are going to be running on the same cache. """ def __init__(self, cache, safe=safename): # use safe=lambda x: md5.new(x).hexdigest() for the old behavior self.cache = cache self.safe = safe - if not os.path.exists(cache): + if not os.path.exists(cache): os.makedirs(self.cache) def get(self, key): @@ -688,7 +688,7 @@ class Credentials(object): def iter(self, domain): for (cdomain, name, password) in self.credentials: if cdomain == "" or domain == cdomain: - yield (name, password) + yield (name, password) class KeyCerts(Credentials): """Identical to Credentials except that @@ -772,7 +772,7 @@ class HTTPSConnectionWithTimeout(httplib.HTTPSConnection): sock.setproxy(*self.proxy_info.astuple()) else: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - + if has_timeout(self.timeout): sock.settimeout(self.timeout) sock.connect((self.host, self.port)) @@ -820,7 +820,7 @@ the same interface as FileCache.""" # If set to False then no redirects are followed, even safe ones. self.follow_redirects = True - + # Which HTTP methods do we apply optimistic concurrency to, i.e. # which methods get an "if-match:" etag header added to them. self.optimistic_concurrency_methods = ["PUT"] @@ -831,7 +831,7 @@ the same interface as FileCache.""" self.ignore_etag = False - self.force_exception_to_status_code = False + self.force_exception_to_status_code = False self.timeout = timeout @@ -908,12 +908,12 @@ the same interface as FileCache.""" auths = [(auth.depth(request_uri), auth) for auth in self.authorizations if auth.inscope(host, request_uri)] auth = auths and sorted(auths)[0][1] or None - if auth: + if auth: auth.request(method, request_uri, headers, body) (response, content) = self._conn_request(conn, request_uri, method, body, headers) - if auth: + if auth: if auth.response(response, body): auth.request(method, request_uri, headers, body) (response, content) = self._conn_request(conn, request_uri, method, body, headers ) @@ -921,7 +921,7 @@ the same interface as FileCache.""" if response.status == 401: for authorization in self._auth_from_challenge(host, request_uri, headers, response, content): - authorization.request(method, request_uri, headers, body) + authorization.request(method, request_uri, headers, body) (response, content) = self._conn_request(conn, request_uri, method, body, headers, ) if response.status != 401: self.authorizations.append(authorization) @@ -944,7 +944,7 @@ the same interface as FileCache.""" if response.status == 301 and method in ["GET", "HEAD"]: response['-x-permanent-redirect-url'] = response['location'] if not response.has_key('content-location'): - response['content-location'] = absolute_uri + response['content-location'] = absolute_uri _updateCache(headers, response, content, self.cache, cachekey) if headers.has_key('if-none-match'): del headers['if-none-match'] @@ -954,7 +954,7 @@ the same interface as FileCache.""" location = response['location'] old_response = copy.deepcopy(response) if not old_response.has_key('content-location'): - old_response['content-location'] = absolute_uri + old_response['content-location'] = absolute_uri redirect_method = ((response.status == 303) and (method not in ["GET", "HEAD"])) and "GET" or method (response, content) = self.request(location, redirect_method, body=body, headers = headers, redirections = redirections - 1) response.previous = old_response @@ -963,7 +963,7 @@ the same interface as FileCache.""" elif response.status in [200, 203] and method == "GET": # Don't cache 206's since we aren't going to handle byte range requests if not response.has_key('content-location'): - response['content-location'] = absolute_uri + response['content-location'] = absolute_uri _updateCache(headers, response, content, self.cache, cachekey) return (response, content) @@ -978,10 +978,10 @@ the same interface as FileCache.""" def request(self, uri, method="GET", body=None, headers=None, redirections=DEFAULT_MAX_REDIRECTS, connection_type=None): """ Performs a single HTTP request. -The 'uri' is the URI of the HTTP resource and can begin +The 'uri' is the URI of the HTTP resource and can begin with either 'http' or 'https'. The value of 'uri' must be an absolute URI. -The 'method' is the HTTP method to perform, such as GET, POST, DELETE, etc. +The 'method' is the HTTP method to perform, such as GET, POST, DELETE, etc. There is no restriction on the methods allowed. The 'body' is the entity body to be sent with the request. It is a string @@ -990,11 +990,11 @@ object. Any extra headers that are to be sent with the request should be provided in the 'headers' dictionary. -The maximum number of redirect to follow before raising an +The maximum number of redirect to follow before raising an exception is 'redirections. The default is 5. -The return value is a tuple of (response, content), the first -being and instance of the 'Response' class, the second being +The return value is a tuple of (response, content), the first +being and instance of the 'Response' class, the second being a string that contains the response entity body. """ try: @@ -1085,13 +1085,13 @@ a string that contains the response entity body. # Determine our course of action: # Is the cached entry fresh or stale? # Has the client requested a non-cached response? - # - # There seems to be three possible answers: + # + # There seems to be three possible answers: # 1. [FRESH] Return the cache entry w/o doing a GET # 2. [STALE] Do the GET (but add in cache validators if available) # 3. [TRANSPARENT] Do a GET w/o any cache validators (Cache-Control: no-cache) on the request - entry_disposition = _entry_disposition(info, headers) - + entry_disposition = _entry_disposition(info, headers) + if entry_disposition == "FRESH": if not cached_value: info['status'] = '504' @@ -1113,7 +1113,7 @@ a string that contains the response entity body. if response.status == 304 and method == "GET": # Rewrite the cache entry with the new end-to-end headers - # Take all headers that are in response + # Take all headers that are in response # and overwrite their values in info. # unless they are hop-by-hop, or are listed in the connection header. @@ -1125,14 +1125,14 @@ a string that contains the response entity body. _updateCache(headers, merged_response, content, self.cache, cachekey) response = merged_response response.status = 200 - response.fromcache = True + response.fromcache = True elif response.status == 200: content = new_content else: self.cache.delete(cachekey) - content = new_content - else: + content = new_content + else: cc = _parse_cache_control(headers) if cc.has_key('only-if-cached'): info['status'] = '504' @@ -1146,7 +1146,7 @@ a string that contains the response entity body. response = e.response content = e.content response.status = 500 - response.reason = str(e) + response.reason = str(e) elif isinstance(e, socket.timeout) or (isinstance(e, socket.error) and 'timed out' in str(e)): content = "Request Timeout" response = Response( { @@ -1156,24 +1156,24 @@ a string that contains the response entity body. }) response.reason = "Request Timeout" else: - content = str(e) + content = str(e) response = Response( { "content-type": "text/plain", "status": "400", "content-length": len(content) }) - response.reason = "Bad Request" + response.reason = "Bad Request" else: raise - + return (response, content) - + class Response(dict): """An object more like email.Message than httplib.HTTPResponse.""" - + """Is this response from our local cache""" fromcache = False @@ -1189,27 +1189,27 @@ class Response(dict): previous = None def __init__(self, info): - # info is either an email.Message or + # info is either an email.Message or # an httplib.HTTPResponse object. if isinstance(info, httplib.HTTPResponse): - for key, value in info.getheaders(): - self[key.lower()] = value + for key, value in info.getheaders(): + self[key.lower()] = value self.status = info.status self['status'] = str(self.status) self.reason = info.reason self.version = info.version elif isinstance(info, email.Message.Message): - for key, value in info.items(): - self[key] = value + for key, value in info.items(): + self[key] = value self.status = int(self['status']) else: - for key, value in info.iteritems(): - self[key] = value + for key, value in info.iteritems(): + self[key] = value self.status = int(self.get('status', self.status)) def __getattr__(self, name): if name == 'dict': - return self - else: - raise AttributeError, name + return self + else: + raise AttributeError, name diff --git a/lib/httplib2/iri2uri.py b/lib/httplib2/iri2uri.py index 70667edf..e4cda1d7 100755 --- a/lib/httplib2/iri2uri.py +++ b/lib/httplib2/iri2uri.py @@ -16,7 +16,7 @@ import urlparse # Convert an IRI to a URI following the rules in RFC 3987 -# +# # The characters we need to enocde and escape are defined in the spec: # # iprivate = %xE000-F8FF / %xF0000-FFFFD / %x100000-10FFFD @@ -49,7 +49,7 @@ escape_range = [ (0xF0000, 0xFFFFD ), (0x100000, 0x10FFFD) ] - + def encode(c): retval = c i = ord(c) @@ -63,19 +63,19 @@ def encode(c): def iri2uri(uri): - """Convert an IRI to a URI. Note that IRIs must be + """Convert an IRI to a URI. Note that IRIs must be passed in a unicode strings. That is, do not utf-8 encode - the IRI before passing it into the function.""" + the IRI before passing it into the function.""" if isinstance(uri ,unicode): (scheme, authority, path, query, fragment) = urlparse.urlsplit(uri) authority = authority.encode('idna') # For each character in 'ucschar' or 'iprivate' # 1. encode as utf-8 - # 2. then %-encode each octet of that utf-8 + # 2. then %-encode each octet of that utf-8 uri = urlparse.urlunsplit((scheme, authority, path, query, fragment)) uri = "".join([encode(c) for c in uri]) return uri - + if __name__ == "__main__": import unittest @@ -83,7 +83,7 @@ if __name__ == "__main__": def test_uris(self): """Test that URIs are invariant under the transformation.""" - invariant = [ + invariant = [ u"ftp://ftp.is.co.za/rfc/rfc1808.txt", u"http://www.ietf.org/rfc/rfc2396.txt", u"ldap://[2001:db8::7]/c=GB?objectClass?one", @@ -94,7 +94,7 @@ if __name__ == "__main__": u"urn:oasis:names:specification:docbook:dtd:xml:4.1.2" ] for uri in invariant: self.assertEqual(uri, iri2uri(uri)) - + def test_iri(self): """ Test that the right type of escaping is done for each part of the URI.""" self.assertEqual("http://xn--o3h.com/%E2%98%84", iri2uri(u"http://\N{COMET}.com/\N{COMET}")) @@ -107,4 +107,4 @@ if __name__ == "__main__": unittest.main() - + diff --git a/lib/mako/ext/autohandler.py b/lib/mako/ext/autohandler.py index d56cbc1e..7bd1c71f 100644 --- a/lib/mako/ext/autohandler.py +++ b/lib/mako/ext/autohandler.py @@ -48,7 +48,7 @@ def autohandler(template, context, name='autohandler'): if len(tokens) == 1: break tokens[-2:] = [name] - + if not lookup.filesystem_checks: return lookup._uri_cache.setdefault( (autohandler, _template_uri, name), None) @@ -62,4 +62,4 @@ def _file_exists(lookup, path): return True else: return False - + diff --git a/lib/mako/ext/preprocessors.py b/lib/mako/ext/preprocessors.py index 5a15ff35..acd32e55 100644 --- a/lib/mako/ext/preprocessors.py +++ b/lib/mako/ext/preprocessors.py @@ -4,16 +4,16 @@ # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php -"""preprocessing functions, used with the 'preprocessor' +"""preprocessing functions, used with the 'preprocessor' argument on Template, TemplateLookup""" import re def convert_comments(text): """preprocess old style comments. - + example: - + from mako.ext.preprocessors import convert_comments t = Template(..., preprocessor=preprocess_comments)""" return re.sub(r'(?<=\n)\s*#[^#]', "##", text) diff --git a/lib/musicbrainzngs/musicbrainz.py b/lib/musicbrainzngs/musicbrainz.py index 65454834..16492087 100644 --- a/lib/musicbrainzngs/musicbrainz.py +++ b/lib/musicbrainzngs/musicbrainz.py @@ -279,7 +279,7 @@ def auth(u, p): global user, password user = u password = p - + def hpauth(u, p): """Set the username and password to be used in subsequent queries to the MusicBrainz XML API that require authentication. @@ -574,7 +574,7 @@ def _mb_request(path, method='GET', auth_required=False, client_required=False, whether exceptions should be raised if the client and username/password are left unspecified, respectively. """ - global parser_fun + global parser_fun if args is None: args = {} @@ -638,7 +638,7 @@ def _mb_request(path, method='GET', auth_required=False, client_required=False, if hostname == '144.76.94.239:8181': base64string = base64.encodestring('%s:%s' % (hpuser, hppassword)).replace('\n', '') req.add_header("Authorization", "Basic %s" % base64string) - + _log.debug("requesting with UA %s" % _useragent) if body: req.add_header('Content-Type', 'application/xml; charset=UTF-8') @@ -908,7 +908,7 @@ def get_releases_by_discid(id, includes=[], toc=None, cdstubs=True): The `toc` should have to same format as :attr:`discid.Disc.toc_string`. If no toc matches in musicbrainz but a :musicbrainz:`CD Stub` does, - the CD Stub will be returned. Prevent this from happening by + the CD Stub will be returned. Prevent this from happening by passing `cdstubs=False`. The result is a dict with either a 'disc' , a 'cdstub' key diff --git a/lib/mutagen/easymp4.py b/lib/mutagen/easymp4.py index 3abacccc..65e78b74 100644 --- a/lib/mutagen/easymp4.py +++ b/lib/mutagen/easymp4.py @@ -22,7 +22,7 @@ class EasyMP4Tags(DictMixin, Metadata): strings, and values are a list of Unicode strings (and these lists are always of length 0 or 1). - If you need access to the full MP4 metadata feature set, you should use + If you need access to the full MP4 metadata feature set, you should use MP4, not EasyMP4. """ diff --git a/lib/oauth2/__init__.py b/lib/oauth2/__init__.py index 2b938909..cf77705d 100644 --- a/lib/oauth2/__init__.py +++ b/lib/oauth2/__init__.py @@ -85,11 +85,11 @@ def generate_verifier(length=8): class Consumer(object): """A consumer of OAuth-protected services. - + The OAuth consumer is a "third-party" service that wants to access protected resources from an OAuth service provider on behalf of an end user. It's kind of the OAuth client. - + Usually a consumer must be registered with the service provider by the developer of the consumer software. As part of that process, the service provider gives the consumer a *key* and a *secret* with which the consumer @@ -97,7 +97,7 @@ class Consumer(object): key in each request to identify itself, but will use its secret only when signing requests, to prove that the request is from that particular registered consumer. - + Once registered, the consumer can then use its consumer credentials to ask the service provider for a request token, kicking off the OAuth authorization process. @@ -125,12 +125,12 @@ class Consumer(object): class Token(object): """An OAuth credential used to request authorization or a protected resource. - + Tokens in OAuth comprise a *key* and a *secret*. The key is included in requests to identify the token being used, but the secret is used only in the signature, to prove that the requester is who the server gave the token to. - + When first negotiating the authorization, the consumer asks for a *request token* that the live user authorizes with the service provider. The consumer then exchanges the request token for an *access token* that can @@ -175,7 +175,7 @@ class Token(object): def to_string(self): """Returns this token as a plain string, suitable for storage. - + The resulting string includes the token's secret, so you should never send or store this string where a third party can read it. """ @@ -188,7 +188,7 @@ class Token(object): if self.callback_confirmed is not None: data['oauth_callback_confirmed'] = self.callback_confirmed return urllib.urlencode(data) - + @staticmethod def from_string(s): """Deserializes a token from a string like one returned by @@ -209,7 +209,7 @@ class Token(object): try: secret = params['oauth_token_secret'][0] except Exception: - raise ValueError("'oauth_token_secret' not found in " + raise ValueError("'oauth_token_secret' not found in " "OAuth request.") token = Token(key, secret) @@ -225,45 +225,45 @@ class Token(object): def setter(attr): name = attr.__name__ - + def getter(self): try: return self.__dict__[name] except KeyError: raise AttributeError(name) - + def deleter(self): del self.__dict__[name] - + return property(getter, attr, deleter) class Request(dict): - + """The parameters and information for an HTTP request, suitable for authorizing with OAuth credentials. - + When a consumer wants to access a service's protected resources, it does so using a signed HTTP request identifying itself (the consumer) with its key, and providing an access token authorized by the end user to access those resources. - + """ - + http_method = HTTP_METHOD http_url = None version = VERSION - + def __init__(self, method=HTTP_METHOD, url=None, parameters=None): if method is not None: self.method = method - + if url is not None: self.url = url - + if parameters is not None: self.update(parameters) - + @setter def url(self, value): parts = urlparse.urlparse(value) @@ -280,33 +280,33 @@ class Request(dict): value = '%s://%s%s' % (scheme, netloc, path) self.__dict__['url'] = value - + @setter def method(self, value): self.__dict__['method'] = value.upper() - + def _get_timestamp_nonce(self): return self['oauth_timestamp'], self['oauth_nonce'] - + def get_nonoauth_parameters(self): """Get any non-OAuth parameters.""" - return dict([(k, v) for k, v in self.iteritems() + return dict([(k, v) for k, v in self.iteritems() if not k.startswith('oauth_')]) - + def to_header(self, realm=''): """Serialize as a header for an HTTPAuth request.""" - oauth_params = ((k, v) for k, v in self.items() + oauth_params = ((k, v) for k, v in self.items() if k.startswith('oauth_')) stringy_params = ((k, escape(str(v))) for k, v in oauth_params) header_params = ('%s="%s"' % (k, v) for k, v in stringy_params) params_header = ', '.join(header_params) - + auth_header = 'OAuth realm="%s"' % realm if params_header: auth_header = "%s, %s" % (auth_header, params_header) - + return {'Authorization': auth_header} - + def to_postdata(self): """Serialize as post data for a POST request.""" return self.encode_postdata(self) @@ -327,7 +327,7 @@ class Request(dict): raise Error('Parameter not found: %s' % parameter) return ret - + def get_normalized_parameters(self): """Return a string that contains the parameters that must be signed.""" items = [(k, v) for k, v in self.items() if k != 'oauth_signature'] @@ -337,7 +337,7 @@ class Request(dict): # (http://tools.ietf.org/html/draft-hammer-oauth-07#section-3.6) # Spaces must be encoded with "%20" instead of "+" return encoded_str.replace('+', '%20') - + def sign_request(self, signature_method, consumer, token): """Set the signature parameter to the result of sign.""" @@ -349,24 +349,24 @@ class Request(dict): self['oauth_signature_method'] = signature_method.name self['oauth_signature'] = signature_method.sign(self, consumer, token) - + @classmethod def make_timestamp(cls): """Get seconds since epoch (UTC).""" return str(int(time.time())) - + @classmethod def make_nonce(cls): """Generate pseudorandom number.""" return str(random.randint(0, 100000000)) - + @classmethod def from_request(cls, http_method, http_url, headers=None, parameters=None, query_string=None): """Combines multiple parameter sources.""" if parameters is None: parameters = {} - + # Headers if headers and 'Authorization' in headers: auth_header = headers['Authorization'] @@ -380,57 +380,57 @@ class Request(dict): except: raise Error('Unable to parse OAuth parameters from ' 'Authorization header.') - + # GET or POST query string. if query_string: query_params = cls._split_url_string(query_string) parameters.update(query_params) - + # URL parameters. param_str = urlparse.urlparse(http_url)[4] # query url_params = cls._split_url_string(param_str) parameters.update(url_params) - + if parameters: return cls(http_method, http_url, parameters) - + return None - + @classmethod def from_consumer_and_token(cls, consumer, token=None, http_method=HTTP_METHOD, http_url=None, parameters=None): if not parameters: parameters = {} - + defaults = { 'oauth_consumer_key': consumer.key, 'oauth_timestamp': cls.make_timestamp(), 'oauth_nonce': cls.make_nonce(), 'oauth_version': cls.version, } - + defaults.update(parameters) parameters = defaults - + if token: parameters['oauth_token'] = token.key - + return Request(http_method, http_url, parameters) - + @classmethod - def from_token_and_callback(cls, token, callback=None, + def from_token_and_callback(cls, token, callback=None, http_method=HTTP_METHOD, http_url=None, parameters=None): if not parameters: parameters = {} - + parameters['oauth_token'] = token.key - + if callback: parameters['oauth_callback'] = callback - + return cls(http_method, http_url, parameters) - + @staticmethod def _split_header(header): """Turn Authorization: header into parameters.""" @@ -447,7 +447,7 @@ class Request(dict): # Remove quotes and unescape the value. params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"')) return params - + @staticmethod def _split_url_string(param_str): """Turn URL string into parameters.""" @@ -460,7 +460,7 @@ class Request(dict): class Server(object): """A skeletal implementation of a service provider, providing protected resources to requests from authorized consumers. - + This class implements the logic to check requests for authorization. You can use it with your web server or web framework to protect certain resources with OAuth. @@ -536,7 +536,7 @@ class Server(object): if not valid: key, base = signature_method.signing_base(request, consumer, token) - raise Error('Invalid signature. Expected signature base ' + raise Error('Invalid signature. Expected signature base ' 'string: %s' % base) built = signature_method.sign(request, consumer, token) @@ -567,7 +567,7 @@ class Client(httplib2.Http): self.token = token self.method = SignatureMethod_HMAC_SHA1() - httplib2.Http.__init__(self, cache=cache, timeout=timeout, + httplib2.Http.__init__(self, cache=cache, timeout=timeout, proxy_info=proxy_info) def set_signature_method(self, method): @@ -576,10 +576,10 @@ class Client(httplib2.Http): self.method = method - def request(self, uri, method="GET", body=None, headers=None, + def request(self, uri, method="GET", body=None, headers=None, redirections=httplib2.DEFAULT_MAX_REDIRECTS, connection_type=None, force_auth_header=False): - + if not isinstance(headers, dict): headers = {} @@ -587,7 +587,7 @@ class Client(httplib2.Http): parameters = dict(parse_qsl(body)) elif method == "GET": parsed = urlparse.urlparse(uri) - parameters = parse_qs(parsed.query) + parameters = parse_qs(parsed.query) else: parameters = None @@ -614,14 +614,14 @@ class Client(httplib2.Http): # don't call update twice. headers.update(req.to_header()) - return httplib2.Http.request(self, uri, method=method, body=body, - headers=headers, redirections=redirections, + return httplib2.Http.request(self, uri, method=method, body=body, + headers=headers, redirections=redirections, connection_type=connection_type) class SignatureMethod(object): """A way of signing requests. - + The OAuth protocol lets consumers and service providers pick a way to sign requests. This interface shows the methods expected by the other `oauth` modules for signing requests. Subclass it and implement its methods to @@ -657,7 +657,7 @@ class SignatureMethod(object): class SignatureMethod_HMAC_SHA1(SignatureMethod): name = 'HMAC-SHA1' - + def signing_base(self, request, consumer, token): sig = ( escape(request.method), diff --git a/lib/pyItunes/Library.py b/lib/pyItunes/Library.py index 2400c969..3118fb7b 100644 --- a/lib/pyItunes/Library.py +++ b/lib/pyItunes/Library.py @@ -36,6 +36,6 @@ class Library: if attributes.get('Play Count'): s.play_count = int(attributes.get('Play Count')) if attributes.get('Location'): - s.location = attributes.get('Location') + s.location = attributes.get('Location') songs.append(s) return songs \ No newline at end of file diff --git a/lib/pyItunes/Song.py b/lib/pyItunes/Song.py index 27d44d79..c59759e6 100644 --- a/lib/pyItunes/Song.py +++ b/lib/pyItunes/Song.py @@ -42,5 +42,5 @@ class Song: album_rating = None play_count = None location = None - + #title = property(getTitle,setTitle) \ No newline at end of file diff --git a/lib/pyItunes/XMLLibraryParser.py b/lib/pyItunes/XMLLibraryParser.py index 7e4b239a..6824aee7 100644 --- a/lib/pyItunes/XMLLibraryParser.py +++ b/lib/pyItunes/XMLLibraryParser.py @@ -5,7 +5,7 @@ class XMLLibraryParser: s = f.read() lines = s.split("\n") self.dictionary = self.parser(lines) - + def getValue(self,restOfLine): value = re.sub("<.*?>","",restOfLine) u = unicode(value,"utf-8") diff --git a/lib/pygazelle/api.py b/lib/pygazelle/api.py index 3bdd7c2e..91df1ad2 100644 --- a/lib/pygazelle/api.py +++ b/lib/pygazelle/api.py @@ -201,7 +201,7 @@ class GazelleAPI(object): Returns the inbox Mailbox for the logged in user """ return Mailbox(self, 'inbox', page, sort) - + def get_sentbox(self, page='1', sort='unread'): """ Returns the sentbox Mailbox for the logged in user diff --git a/lib/pygazelle/inbox.py b/lib/pygazelle/inbox.py index e5016286..a26c9a26 100644 --- a/lib/pygazelle/inbox.py +++ b/lib/pygazelle/inbox.py @@ -58,9 +58,9 @@ class Mailbox(object): """ This class represents the logged in user's inbox/sentbox """ - def __init__(self, parent_api, boxtype='inbox', page='1', sort='unread'): + def __init__(self, parent_api, boxtype='inbox', page='1', sort='unread'): self.parent_api = parent_api - self.boxtype = boxtype + self.boxtype = boxtype self.current_page = page self.total_pages = None self.sort = sort diff --git a/lib/pynma/__init__.py b/lib/pynma/__init__.py index f90424eb..cbd82cfd 100644 --- a/lib/pynma/__init__.py +++ b/lib/pynma/__init__.py @@ -1,4 +1,4 @@ #!/usr/bin/python -from pynma import PyNMA +from pynma import PyNMA diff --git a/lib/pynma/pynma.py b/lib/pynma/pynma.py index fc7d8de2..037145c8 100644 --- a/lib/pynma/pynma.py +++ b/lib/pynma/pynma.py @@ -99,7 +99,7 @@ class PyNMA(object): res = self.callapi('POST', ADD_PATH, datas) results[datas['apikey']] = res return results - + def callapi(self, method, path, args): headers = { 'User-Agent': USER_AGENT } if method == "POST": @@ -116,7 +116,7 @@ class PyNMA(object): 'message': str(e) } pass - + return res def _parse_reponse(self, response): @@ -133,5 +133,5 @@ class PyNMA(object): res['message'] = elem.firstChild.nodeValue res['type'] = elem.tagName return res - - + + diff --git a/lib/requests/cookies.py b/lib/requests/cookies.py index 831c49c6..3456a9fe 100644 --- a/lib/requests/cookies.py +++ b/lib/requests/cookies.py @@ -440,7 +440,7 @@ def merge_cookies(cookiejar, cookies): """ if not isinstance(cookiejar, cookielib.CookieJar): raise ValueError('You can only merge into CookieJar') - + if isinstance(cookies, dict): cookiejar = cookiejar_from_dict( cookies, cookiejar=cookiejar, overwrite=False) diff --git a/lib/requests/packages/chardet/charsetgroupprober.py b/lib/requests/packages/chardet/charsetgroupprober.py index 85e7a1c6..ddbeeea2 100644 --- a/lib/requests/packages/chardet/charsetgroupprober.py +++ b/lib/requests/packages/chardet/charsetgroupprober.py @@ -1,11 +1,11 @@ ######################## BEGIN LICENSE BLOCK ######################## # The Original Code is Mozilla Communicator client code. -# +# # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. -# +# # Contributor(s): # Mark Pilgrim - port to Python # @@ -13,12 +13,12 @@ # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. -# +# # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. -# +# # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA diff --git a/lib/requests/packages/chardet/constants.py b/lib/requests/packages/chardet/constants.py index e4d148b3..8895c94e 100644 --- a/lib/requests/packages/chardet/constants.py +++ b/lib/requests/packages/chardet/constants.py @@ -14,12 +14,12 @@ # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. -# +# # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. -# +# # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA diff --git a/lib/requests/packages/chardet/euckrfreq.py b/lib/requests/packages/chardet/euckrfreq.py index a179e4c2..1fa75883 100644 --- a/lib/requests/packages/chardet/euckrfreq.py +++ b/lib/requests/packages/chardet/euckrfreq.py @@ -13,12 +13,12 @@ # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. -# +# # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. -# +# # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA @@ -35,14 +35,14 @@ # # Idea Distribution Ratio = 0.98653 / (1-0.98653) = 73.24 # Random Distribution Ration = 512 / (2350-512) = 0.279. -# -# Typical Distribution Ratio +# +# Typical Distribution Ratio EUCKR_TYPICAL_DISTRIBUTION_RATIO = 6.0 EUCKR_TABLE_SIZE = 2352 -# Char to FreqOrder table , +# Char to FreqOrder table , EUCKRCharToFreqOrder = ( \ 13, 130, 120,1396, 481,1719,1720, 328, 609, 212,1721, 707, 400, 299,1722, 87, 1397,1723, 104, 536,1117,1203,1724,1267, 685,1268, 508,1725,1726,1727,1728,1398, diff --git a/lib/requests/packages/chardet/euctwprober.py b/lib/requests/packages/chardet/euctwprober.py index fe652fe3..178a3042 100644 --- a/lib/requests/packages/chardet/euctwprober.py +++ b/lib/requests/packages/chardet/euctwprober.py @@ -13,12 +13,12 @@ # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. -# +# # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. -# +# # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA diff --git a/lib/requests/packages/chardet/gb2312prober.py b/lib/requests/packages/chardet/gb2312prober.py index 0325a2d8..b7a181a2 100644 --- a/lib/requests/packages/chardet/gb2312prober.py +++ b/lib/requests/packages/chardet/gb2312prober.py @@ -13,12 +13,12 @@ # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. -# +# # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. -# +# # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA diff --git a/lib/unidecode/__init__.py b/lib/unidecode/__init__.py index 82eb5a3f..dd3d2310 100644 --- a/lib/unidecode/__init__.py +++ b/lib/unidecode/__init__.py @@ -39,7 +39,7 @@ def unidecode(string): if codepoint < 0x80: # Basic ASCII retval.append(str(char)) continue - + if codepoint > 0xeffff: continue # Characters in Private Use Area and above are ignored diff --git a/lib/yaml/constructor.py b/lib/yaml/constructor.py index 635faac3..abbff97b 100644 --- a/lib/yaml/constructor.py +++ b/lib/yaml/constructor.py @@ -287,7 +287,7 @@ class SafeConstructor(BaseConstructor): return str(value).decode('base64') except (binascii.Error, UnicodeEncodeError), exc: raise ConstructorError(None, None, - "failed to decode base64 data: %s" % exc, node.start_mark) + "failed to decode base64 data: %s" % exc, node.start_mark) timestamp_regexp = re.compile( ur'''^(?P[0-9][0-9][0-9][0-9]) diff --git a/lib/yaml/emitter.py b/lib/yaml/emitter.py index e5bcdccc..488337f2 100644 --- a/lib/yaml/emitter.py +++ b/lib/yaml/emitter.py @@ -674,7 +674,7 @@ class Emitter(object): # Check for indicators. if index == 0: # Leading indicators are special characters. - if ch in u'#,[]{}&*!|>\'\"%@`': + if ch in u'#,[]{}&*!|>\'\"%@`': flow_indicators = True block_indicators = True if ch in u'?:': diff --git a/lib/yaml/parser.py b/lib/yaml/parser.py index f9e3057f..ffec9552 100644 --- a/lib/yaml/parser.py +++ b/lib/yaml/parser.py @@ -482,7 +482,7 @@ class Parser(object): token = self.peek_token() raise ParserError("while parsing a flow sequence", self.marks[-1], "expected ',' or ']', but got %r" % token.id, token.start_mark) - + if self.check_token(KeyToken): token = self.peek_token() event = MappingStartEvent(None, None, True, diff --git a/lib/yaml/scanner.py b/lib/yaml/scanner.py index 5228fad6..4e578581 100644 --- a/lib/yaml/scanner.py +++ b/lib/yaml/scanner.py @@ -314,7 +314,7 @@ class Scanner(object): # Remove the saved possible key position at the current flow level. if self.flow_level in self.possible_simple_keys: key = self.possible_simple_keys[self.flow_level] - + if key.required: raise ScannerError("while scanning a simple key", key.mark, "could not found expected ':'", self.get_mark()) @@ -363,11 +363,11 @@ class Scanner(object): # Read the token. mark = self.get_mark() - + # Add STREAM-START. self.tokens.append(StreamStartToken(mark, mark, encoding=self.encoding)) - + def fetch_stream_end(self): @@ -381,7 +381,7 @@ class Scanner(object): # Read the token. mark = self.get_mark() - + # Add STREAM-END. self.tokens.append(StreamEndToken(mark, mark)) @@ -389,7 +389,7 @@ class Scanner(object): self.done = True def fetch_directive(self): - + # Set the current intendation to -1. self.unwind_indent(-1) @@ -516,7 +516,7 @@ class Scanner(object): self.tokens.append(BlockEntryToken(start_mark, end_mark)) def fetch_key(self): - + # Block context needs additional checks. if not self.flow_level: @@ -566,7 +566,7 @@ class Scanner(object): # It must be a part of a complex key. else: - + # Block context needs additional checks. # (Do we really need them? They will be catched by the parser # anyway.) @@ -1024,14 +1024,14 @@ class Scanner(object): # Unfortunately, folding rules are ambiguous. # # This is the folding according to the specification: - + if folded and line_break == u'\n' \ and leading_non_space and self.peek() not in u' \t': if not breaks: chunks.append(u' ') else: chunks.append(line_break) - + # This is Clark Evans's interpretation (also in the spec # examples): #