Travis Shirk avatar Travis Shirk committed 6f67a9f

FrameSet.getAllFrames() sorts the returned list. Many pep8/lint fixes.

Comments (0)

Files changed (2)

src/eyed3/id3/frames.py

 import re
 from cStringIO import StringIO
 from collections import namedtuple
+import logging
 
 from .. import core
 from ..utils import requireUnicode
 from ..utils.binfuncs import *
+from .. import Exception as BaseException
 from . import ID3_V2, ID3_V2_3, ID3_V2_4
 from . import (LATIN1_ENCODING, UTF_8_ENCODING, UTF_16BE_ENCODING,
                UTF_16_ENCODING, DEFAULT_LANG)
 from .headers import FrameHeader
 
-import logging
+
 log = logging.getLogger(__name__)
 
-from .. import Exception as BaseException
+
 class FrameException(BaseException):
     pass
 
         # Format flags in the frame header may add extra data to the
         # beginning of this data.
         if header.minor_version <= 3:
-            # 2.3:  compression(4), encryption(1), group(1) 
+            # 2.3:  compression(4), encryption(1), group(1)
             if header.compressed:
                 self.decompressed_size = bin2dec(bytes2bin(data[:4]))
                 data = data[4:]
                 data = data[4:]
                 log.debug("Data Length: %d" % self.data_len)
                 if header.compressed:
-                   self.decompressed_size = self.data_len
-                   log.debug("Decompressed Size: %d" % self.decompressed_size)
+                    self.decompressed_size = self.data_len
+                    log.debug("Decompressed Size: %d" % self.decompressed_size)
 
         if header.minor_version == 4 and header.unsync:
             data = deunsyncData(data)
     # Process a 3 byte language code (ISO 639-2).
     # This code must match the [A-Z][A-Z][A-Z]
     # (although case is ignored) and be ascii to be considered valid. When
-    # deemed invalid warnings are logged and the value is changed to 
+    # deemed invalid warnings are logged and the value is changed to
     # \c DEFAULT_LANG.
     #
     # \param lang The code.
         try:
             # Test ascii encoding, it MUST be
             lang = lang.encode("ascii")
-        except (UnicodeEncodeError, UnicodeDecodeError) as ex:
+        except (UnicodeEncodeError, UnicodeDecodeError):
             log.warning("Fixing invalid lyrics language code: %s" % lang)
             lang = DEFAULT_LANG
 
         self.data = self.url
         return super(UrlFrame, self).render()
 
-##
-# Data string format:
-# encoding (one byte) + description + "\x00" + url (ascii)
+
 class UserUrlFrame(UrlFrame):
+    '''
+    Data string format:
+    encoding (one byte) + description + "\x00" + url (ascii)
+    '''
     @requireUnicode("description")
     def __init__(self, id=USERURL_FID, description=u"", url=""):
         UrlFrame.__init__(self, id, url=url)
                                            "type"))
         if (self.mime_type != self.URL_MIME_TYPE and
                 self.mime_type.find("/") == -1):
-           self.mime_type = "image/" + self.mime_type
+            self.mime_type = "image/" + self.mime_type
 
         pt = ord(input.read(1))
         log.debug("Initial APIC picture type: %d" % pt)
         log.debug("description len: %d" % len(desc))
         log.debug("image len: %d" % len(img))
         self.description = decodeUnicode(desc, encoding)
-        log.debug("APIC description: %s" % self.description);
+        log.debug("APIC description: %s" % self.description)
 
         if self.mime_type.find(self.URL_MIME_TYPE) != -1:
             self.image_data = None
         self.data = data
         return super(ImageFrame, self).render()
 
-
     @staticmethod
     def picTypeToString(t):
         if t == ImageFrame.OTHER:
         elif s == "PUBLISHER_LOGO":
             return ImageFrame.PUBLISHER_LOGO
         else:
-          raise ValueError("Invalid APIC picture type: %s" % s)
+            raise ValueError("Invalid APIC picture type: %s" % s)
 
 
 class ObjectFrame(Frame):
             self.mime_type = input.read(3)
         log.debug("GEOB mime type: %s" % self.mime_type)
         if not self.mime_type:
-           core.parseError(FrameException("GEOB frame does not contain a mime "
-                                          "type"))
+            core.parseError(FrameException("GEOB frame does not contain a "
+                                           "mime type"))
         if self.mime_type.find("/") == -1:
-           core.parseError(FrameException("GEOB frame does not contain a valid "
-                                          "mime type"))
+            core.parseError(FrameException("GEOB frame does not contain a "
+                                           "valid mime type"))
 
         self.filename = u""
         self.description = u""
     @property
     def rating(self):
         return self._rating
+
     @rating.setter
     def rating(self, rating):
         if rating < 0 or rating > 255:
     @property
     def email(self):
         return self._email
+
     @email.setter
     def email(self, email):
         self._email = email.encode("ascii")
     @property
     def count(self):
         return self._count
+
     @count.setter
     def count(self, count):
         if count < 0:
         self.data = data
         return super(PopularityFrame, self).render()
 
+
 class UniqueFileIDFrame(Frame):
     def __init__(self, id=UNIQUE_FILE_ID_FID, owner_id=None, uniq_id=None):
         super(UniqueFileIDFrame, self).__init__(id)
         self.data = self.owner_id + "\x00" + self.uniq_id
         return super(UniqueFileIDFrame, self).render()
 
+
 class DescriptionLangTextFrame(Frame):
 
     @requireUnicode(2, 4)
         super(CommentFrame, self).__init__(id, description, lang, text)
         assert(self.id == COMMENT_FID)
 
+
 class LyricsFrame(DescriptionLangTextFrame):
     def __init__(self, id=LYRICS_FID, description=u"", lang=DEFAULT_LANG,
                  text=u""):
         super(LyricsFrame, self).__init__(id, description, lang, text)
         assert(self.id == LYRICS_FID)
 
+
 class TermsOfUseFrame(Frame):
     @requireUnicode("text")
     def __init__(self, id="USER", text=u"", lang=DEFAULT_LANG):
                      self.text.encode(id3EncodingToString(self.encoding)))
         return super(TermsOfUseFrame, self).render()
 
+
 class TocFrame(Frame):
     '''Table of content frame. There may be more than one, but only one may
     have the top-level flag set.
 
 StartEndTuple = namedtuple("StartEndTuple", ["start", "end"])
 
+
 class ChapterFrame(Frame):
     '''Frame type for chapter/section of the audio file.
     <ID3v2.3 or ID3v2.4 frame header, ID: "CHAP">           (10 bytes)
         '''Read frames starting from the current read position of the file
         object. Returns the amount of padding which occurs after the tag, but
         before the audio content.  A return valule of 0 does not mean error.'''
-        from .headers import FrameHeader
-
         self.clear()
 
         padding_size = 0
             log.debug("De-unsynch'd %d bytes at once (<= 2.3 tag) to %d bytes" %
                       (og_size, size_left))
 
-        # Adding bytes to simulate the tag header(s) in the buffer.  This keeps 
+        # Adding bytes to simulate the tag header(s) in the buffer.  This keeps
         # f.tell() values matching the file offsets for logging.
         prepadding = '\x00' * 10  # Tag header
         prepadding += '\x00' * extended_header.size
             dict.__setitem__(self, fid, [frame])
 
     def getAllFrames(self):
+        '''Return all the frames in the set as a list. The list is sorted
+        in an arbitrary but consistent order.'''
         frames = []
         for flist in list(self.values()):
             frames += flist
+        frames.sort()
         return frames
 
     @requireUnicode(2)
         assert(fid[0] == "T" and fid in list(ID3_FRAMES.keys()))
 
         if fid in self:
-            curr = self[fid][0].text = text
+            self[fid][0].text = text
         else:
             if fid in DATE_FIDS:
                 self[fid] = DateFrame(fid, date=text)
             else:
                 self[fid] = TextFrame(fid, text=text)
 
+
 def deunsyncData(data):
     output = []
     safe = True
         if encoding == LATIN1_ENCODING or encoding == UTF_8_ENCODING:
             (d, t) = data.split("\x00", 1)
         elif encoding == UTF_16_ENCODING or encoding == UTF_16BE_ENCODING:
-            # Two null bytes split, but since each utf16 char is also two 
+            # Two null bytes split, but since each utf16 char is also two
             # bytes we need to ensure we found a proper boundary.
             (d, t) = data.split("\x00\x00", 1)
             if (len(d) % 2) != 0:
     else:
         raise ValueError("Encoding unknown: %s" % encoding)
 
+
 def stringToEncoding(s):
     s = s.replace('-', '_')
     if s in ("latin_1", "latin1"):
     "TCP" : "TCP ", # iTunes "extension" for compilation marking
     "CM1" : "CM1 ", # Seems to be some script kiddie tagging the tag.
                     # For example, [rH] join #rH on efnet [rH]
-    "PCS" : "PCST", # iTunes extension for podcast marking. 
+    "PCS" : "PCST", # iTunes extension for podcast marking.
 }
 
 import apple
 NONSTANDARD_ID3_FRAMES = {
-        "NCON": ("Undefined MusicMatch extension", ID3_V2, Frame),
-        "TCMP": ("iTunes complilation flag extension", ID3_V2, TextFrame),
-        "XSOA": ("Album sort-order string extension for v2.3",
-                 ID3_V2_3, TextFrame),
-        "XSOP": ("Performer sort-order string extension for v2.3",
-                 ID3_V2_3, TextFrame),
-        "XSOT": ("Title sort-order string extension for v2.3",
-                 ID3_V2_3, TextFrame),
-        "XDOR": ("MusicBrainz release date (full) extension for v2.3",
-                 ID3_V2_3, TextFrame),
+    "NCON": ("Undefined MusicMatch extension", ID3_V2, Frame),
+    "TCMP": ("iTunes complilation flag extension", ID3_V2, TextFrame),
+    "XSOA": ("Album sort-order string extension for v2.3",
+             ID3_V2_3, TextFrame),
+    "XSOP": ("Performer sort-order string extension for v2.3",
+             ID3_V2_3, TextFrame),
+    "XSOT": ("Title sort-order string extension for v2.3",
+             ID3_V2_3, TextFrame),
+    "XDOR": ("MusicBrainz release date (full) extension for v2.3",
+             ID3_V2_3, TextFrame),
 
-        "PCST": ("iTunes extension; marks the file as a podcast",
-                 ID3_V2, apple.PCST),
-        "TKWD": ("iTunes extension; podcast keywords?",
-                 ID3_V2, apple.TKWD),
-        "TDES": ("iTunes extension; podcast description?",
-                 ID3_V2, apple.TDES),
-        "TGID": ("iTunes extension; podcast ?????",
-                 ID3_V2, apple.TGID),
-        "WFED": ("iTunes extension; podcast feed URL?",
-                 ID3_V2, apple.WFED),
+    "PCST": ("iTunes extension; marks the file as a podcast",
+             ID3_V2, apple.PCST),
+    "TKWD": ("iTunes extension; podcast keywords?",
+             ID3_V2, apple.TKWD),
+    "TDES": ("iTunes extension; podcast description?",
+             ID3_V2, apple.TDES),
+    "TGID": ("iTunes extension; podcast ?????",
+             ID3_V2, apple.TGID),
+    "WFED": ("iTunes extension; podcast feed URL?",
+             ID3_V2, apple.WFED),
 }
 

src/eyed3/id3/tag.py

 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 #
 ################################################################################
-import types, sys, string, re, os, shutil, types, tempfile
+import os
+import types
+import string
+import shutil
+import tempfile
 
 from ..utils import requireUnicode, chunkCopy
 from .. import core
+from .. import Exception as BaseException
 from . import (ID3_ANY_VERSION, ID3_V1, ID3_V1_0, ID3_V1_1,
                ID3_V2, ID3_V2_2, ID3_V2_3, ID3_V2_4, versionToString)
 from . import DEFAULT_LANG
 from . import frames
 from .headers import TagHeader, ExtendedTagHeader
 
-
 import logging
 log = logging.getLogger(__name__)
 
-from .. import Exception as BaseException
+
 class TagException(BaseException):
-   pass
+    pass
+
 
 ID3_V1_COMMENT_DESC = u"ID3v1.x Comment"
 DEFAULT_PADDING = 1024
 
+
 class Tag(core.Tag):
     def __init__(self):
         core.Tag.__init__(self)
         ## Optional extended header in v2 tags.
         self.extended_header = ExtendedTagHeader()
         ## Contains the tag's frames. ID3v1 fields are read and converted
-        #  the the corresponding v2 frame.  
+        #  the the corresponding v2 frame.
         self.frame_set = frames.FrameSet()
         self._comments = CommentsAccessor(self.frame_set)
         self._images = ImagesAccessor(self.frame_set)
         version = version or ID3_ANY_VERSION
 
         close_file = False
-        if type(fileobj) is types.FileType:
+        if isinstance(fileobj, types.FileType):
             filename = fileobj.name
         elif type(fileobj) in types.StringTypes:
             filename = fileobj
         if artist:
             self.artist = unicode(artist, v1_enc)
 
-
         album = tag_data[63:93].strip(STRIP_CHARS)
         log.debug("Album: %s" % album)
         if album:
     def version(self, v):
         self.header.version = v
 
-    ## Test ID3 major version for v1.x
     def isV1(self):
+        '''Test ID3 major version for v1.x'''
         return self.header.major_version == 1
-    ## Test ID3 major version for v2.x
+
     def isV2(self):
+        '''Test ID3 major version for v2.x'''
         return self.header.major_version == 2
 
     @requireUnicode(2)
     @requireUnicode(1)
     def _setArtist(self, val):
         self.setTextFrame(frames.ARTIST_FID, val)
+
     def _getArtist(self):
         return self.getTextFrame(frames.ARTIST_FID)
 
     @requireUnicode(1)
     def _setAlbum(self, val):
         self.setTextFrame(frames.ALBUM_FID, val)
+
     def _getAlbum(self):
         return self.getTextFrame(frames.ALBUM_FID)
 
     @requireUnicode(1)
     def _setTitle(self, val):
         self.setTextFrame(frames.TITLE_FID, val)
+
     def _getTitle(self):
         return self.getTextFrame(frames.TITLE_FID)
 
 
         n = (tn, tt)
 
-        if n[0] == None and n[1] == None:
+        if n[0] is None and n[1] is None:
             if self.frame_set[fid]:
                 del self.frame_set[fid]
             return
 
         total_str = ""
-        if n[1] != None:
+        if n[1] is not None:
             if n[1] >= 0 and n[1] <= 9:
                 total_str = "0" + str(n[1])
             else:
         return bpm
 
     def _setBpm(self, bpm):
-       assert(bpm >= 0)
-       self.setTextFrame(frames.BPM_FID, unicode(str(bpm)))
+        assert(bpm >= 0)
+        self.setTextFrame(frames.BPM_FID, unicode(str(bpm)))
 
     bpm = property(_getBpm, _setBpm)
 
             pc.count = count
         else:
             self.frame_set[frames.PLAYCOUNT_FID] = \
-                    frames.PlayCountFrame(count=count)
+                frames.PlayCountFrame(count=count)
 
     def _getPublisher(self):
         if frames.PUBLISHER_FID in self.frame_set:
             cdid.toc = str(toc)
         else:
             self.frame_set[frames.CDID_FID] = \
-                    frames.MusicCDIdFrame(toc=toc)
+                frames.MusicCDIdFrame(toc=toc)
 
     @property
     def images(self):
 
     def _getEncodingDate(self):
         return self._getDate("TDEN")
+
     def _setEncodingDate(self, date):
         self._setDate("TDEN", date)
     encoding_date = property(_getEncodingDate, _setEncodingDate)
                 self.recording_date)
 
     def _getReleaseDate(self):
-        return self._getDate("TDRL") if self.version == ID3_V2_4\
+        return self._getDate("TDRL") if self.version == ID3_V2_4 \
                                      else self._getV23OrignalReleaseDate()
+
     def _setReleaseDate(self, date):
         self._setDate("TDRL" if self.version == ID3_V2_4 else "TORY", date)
 
 
     def _getOrigReleaseDate(self):
         return self._getDate("TDOR") or self._getV23OrignalReleaseDate()
+
     def _setOrigReleaseDate(self, date):
         self._setDate("TDOR", date)
 
 
     def _getRecordingDate(self):
         return self._getDate("TDRC") or self._getV23RecordingDate()
+
     def _setRecordingDate(self, date):
         if self.version == ID3_V2_4:
             self._setDate("TDRC", date)
 
     def _getTaggingDate(self):
         return self._getDate("TDTG")
+
     def _setTaggingDate(self, date):
         self._setDate("TDTG", date)
     tagging_date = property(_getTaggingDate, _setTaggingDate)
 
     def _setDate(self, fid, date):
         assert(fid in frames.DATE_FIDS or
-                fid in frames.DEPRECATED_DATE_FIDS)
+               fid in frames.DEPRECATED_DATE_FIDS)
 
         if date is None:
             try:
     @property
     def disc_num(self):
         return self._splitNum(frames.DISCNUM_FID)
+
     @disc_num.setter
     def disc_num(self, val):
         self._setNum(frames.DISCNUM_FID, val)
             return Genre.parse(f[0].text)
         else:
             return None
+
     def _setGenre(self, g):
         '''
         Set the genre. Four types are accepted for the ``g`` argument.
     @property
     def commercial_url(self):
         return self._getUrlFrame(frames.URL_COMMERCIAL_FID)
+
     @commercial_url.setter
     def commercial_url(self, url):
         self._setUrlFrame(frames.URL_COMMERCIAL_FID, url)
     @property
     def copyright_url(self):
         return self._getUrlFrame(frames.URL_COPYRIGHT_FID)
+
     @copyright_url.setter
     def copyright_url(self, url):
         self._setUrlFrame(frames.URL_COPYRIGHT_FID, url)
     @property
     def audio_file_url(self):
         return self._getUrlFrame(frames.URL_AUDIOFILE_FID)
+
     @audio_file_url.setter
     def audio_file_url(self, url):
         self._setUrlFrame(frames.URL_AUDIOFILE_FID, url)
     @property
     def audio_source_url(self):
         return self._getUrlFrame(frames.URL_AUDIOSRC_FID)
+
     @audio_source_url.setter
     def audio_source_url(self, url):
         self._setUrlFrame(frames.URL_AUDIOSRC_FID, url)
     @property
     def artist_url(self):
         return self._getUrlFrame(frames.URL_ARTIST_FID)
+
     @artist_url.setter
     def artist_url(self, url):
         self._setUrlFrame(frames.URL_ARTIST_FID, url)
     @property
     def internet_radio_url(self):
         return self._getUrlFrame(frames.URL_INET_RADIO_FID)
+
     @internet_radio_url.setter
     def internet_radio_url(self, url):
         self._setUrlFrame(frames.URL_INET_RADIO_FID, url)
     @property
     def payment_url(self):
         return self._getUrlFrame(frames.URL_PAYMENT_FID)
+
     @payment_url.setter
     def payment_url(self, url):
         self._setUrlFrame(frames.URL_PAYMENT_FID, url)
     @property
     def publisher_url(self):
         return self._getUrlFrame(frames.URL_PUBLISHER_FID)
+
     @publisher_url.setter
     def publisher_url(self, url):
         self._setUrlFrame(frames.URL_PUBLISHER_FID, url)
             shutil.copyfile(self.file_info.name, backup_name)
 
         if version[0] == 1:
-            self.__saveV1Tag(version)
+            self._saveV1Tag(version)
         elif version[0] == 2:
-            self.__saveV2Tag(version, encoding)
+            self._saveV2Tag(version, encoding)
         else:
             assert(not "Version bug: %s" % str(version))
 
-    def __saveV1Tag(self, version):
+    def _saveV1Tag(self, version):
         assert(version[0] == 1)
 
         def pack(s, n):
 
         cmt = ""
         for c in self.comments:
-           if c.description == ID3_V1_COMMENT_DESC:
-              cmt = c.text
-              # We prefer this one over ""
-              break
-           elif c.description == "":
-              cmt = c.text
-              # Keep searching in case we find the description eyeD3 uses.
+            if c.description == ID3_V1_COMMENT_DESC:
+                cmt = c.text
+                # We prefer this one over ""
+                break
+            elif c.description == "":
+                cmt = c.text
+                # Keep searching in case we find the description eyeD3 uses.
         cmt = pack(cmt.encode("latin_1"), 30)
 
         if version != ID3_V1_0:
-           track = self.track_num[0]
-           if track != None:
-              cmt = cmt[0:28] + "\x00" + chr(int(track) & 0xff)
+            track = self.track_num[0]
+            if track is not None:
+                cmt = cmt[0:28] + "\x00" + chr(int(track) & 0xff)
         tag += cmt
 
         if not self.genre or self.genre.id is None:
-           genre = 0
+            genre = 0
         else:
-           genre = self.genre.id
+            genre = self.genre.id
         tag += chr(genre & 0xff)
 
         assert(len(tag) == 128)
                 else:
                     tag_file.seek(0, 2)
             except IOError:
-               # File is smaller than 128 bytes.
-               tag_file.seek(0, 2)
+                # File is smaller than 128 bytes.
+                tag_file.seek(0, 2)
 
             tag_file.write(tag)
             tag_file.flush()
                    {"tag_header": header_data,
                     "ext_header": ext_header_data,
                     "frames": frame_data,
-                   }
+                    }
         assert(len(tag_data) == (total_size - padding_size))
         return (rewrite_required, tag_data, "\x00" * padding_size)
 
-    def __saveV2Tag(self, version, encoding):
+    def _saveV2Tag(self, version, encoding):
         assert(version[0] == 2 and version[1] != 2)
         log.debug("Rendering tag version: %s" % versionToString(version))
 
 
 
 ##
-# This class is for storing information about a parsed file. It containts info 
+# This class is for storing information about a parsed file. It containts info
 # such as the filename, original tag size, and amount of padding all of which
 # can make rewriting faster.
 class FileInfo:
         self.tag_size = 0  # This includes the padding byte count.
         self.tag_padding_size = 0
 
+
 class AccessorBase(object):
     def __init__(self, fid, fs, match_func=None):
         self._fid = fid
     def get(self, description, lang=DEFAULT_LANG):
         return super(DltAccessor, self).get(description, lang=lang)
 
+
 class CommentsAccessor(DltAccessor):
     def __init__(self, fs):
         super(CommentsAccessor, self).__init__(frames.CommentFrame,
                                                frames.COMMENT_FID, fs)
 
+
 class LyricsAccessor(DltAccessor):
     def __init__(self, fs):
         super(LyricsAccessor, self).__init__(frames.LyricsFrame,
                                              frames.LYRICS_FID, fs)
 
+
 class ImagesAccessor(AccessorBase):
     def __init__(self, fs):
         def match_func(frame, description):
     def get(self, description):
         return super(ImagesAccessor, self).get(description)
 
+
 class ObjectsAccessor(AccessorBase):
     def __init__(self, fs):
 
     def get(self, description):
         return super(ObjectsAccessor, self).get(description)
 
+
 class PrivatesAccessor(AccessorBase):
     def __init__(self, fs):
 
     def get(self, owner_id):
         return super(PrivatesAccessor, self).get(owner_id)
 
+
 class UserTextsAccessor(AccessorBase):
     def __init__(self, fs):
         def match_func(frame, description):
     def get(self, description):
         return super(UserTextsAccessor, self).get(description)
 
+
 class UniqueFileIdAccessor(AccessorBase):
     def __init__(self, fs):
         def match_func(frame, owner_id):
     def get(self, description):
         return super(UserUrlsAccessor, self).get(description)
 
+
 class PopularitiesAccessor(AccessorBase):
     def __init__(self, fs):
         def match_func(frame, email):
             return frame.element_id == element_id
         super(ChaptersAccessor, self).__init__(frames.CHAPTER_FID, fs,
                                                match_func)
+
     def set(self, element_id, times, offsets=(None, None), sub_frames=None):
         flist = self._fs[frames.CHAPTER_FID] or []
         for chap in flist:
         raise IndexError("toc '%s' not found" % elem_id)
 
 
-
-import string
 class TagTemplate(string.Template):
     idpattern = r'[_a-z][_a-z0-9:]*'
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.