Repository: beetbox/mediafile

Issue:
Support filelike objects, not only pathes
Hi!
I'm trying to use this library with remote files defined as file-likes in python.
`mutagen` supports file-like interface, why tho mediafile doesn't?
Docs say it needs local file path as first argument.

Discussion:
sampsyo: Thanks for getting this started! If you're interested in changing `path` to `filename`, perhaps we should do that in a separate PR so it's easier to review the core of the "file-like object" feature separately? (In general, for me, I find it's easier to fully understand one change at a time.)
JuniorJPDJ: It's connected as in case of FileThing it doesn't have to be `path` already but it will always be some sort of file name.
I can split it to separate commits for you if it's gonna make review easier ;D
sampsyo: That would be awesome, if you don't mind!
JuniorJPDJ: Done!
If possible - I'd like to get your review and merge on #42 first tho.

Patch:
diff --git a/docs/index.rst b/docs/index.rst
index 2ea6655..1023abd 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -95,7 +95,7 @@ Internals
 .. autoclass:: StorageStyle
     :members:
 
-    
+
 Examples
 --------
 
@@ -112,7 +112,7 @@ To add cover art to a MediaFile:
     f = MediaFile("file.mp3)
     f.images = [cover]
     f.save()
-    
+
 
 To copy tags from one MediaFile to another:
 
@@ -130,7 +130,7 @@ To copy tags from one MediaFile to another:
             pass
 
     g.save()
-    
+
 
 Changelog
 ---------
@@ -139,6 +139,8 @@ v0.7.0
 ''''''
 
 - Mutagen 1.45.0 or later is now required.
+- MediaFile can now use file-like objects (instead of just the filesystem, via
+  filenames).
 
 v0.6.0
 ''''''
diff --git a/mediafile.py b/mediafile.py
index a9e5e8e..a91ec83 100644
--- a/mediafile.py
+++ b/mediafile.py
@@ -40,20 +40,22 @@ import mutagen.id3
 import mutagen.mp4
 import mutagen.flac
 import mutagen.asf
+import mutagen._util
 
-import codecs
-import datetime
-import re
 import base64
 import binascii
-import math
-import struct
-import imghdr
-import os
-import traceback
+import codecs
+import datetime
 import enum
+import functools
+import imghdr
 import logging
+import math
+import os
+import re
 import six
+import struct
+import traceback
 
 
 __version__ = '0.7.0'
@@ -85,8 +87,8 @@ PREFERRED_IMAGE_EXTENSIONS = {'jpeg': 'jpg'}
 class UnreadableFileError(Exception):
     """Mutagen is not able to extract information from the file.
     """
-    def __init__(self, path, msg):
-        Exception.__init__(self, msg if msg else repr(path))
+    def __init__(self, filename, msg):
+        Exception.__init__(self, msg if msg else repr(filename))
 
 
 class FileTypeError(UnreadableFileError):
@@ -95,29 +97,32 @@ class FileTypeError(UnreadableFileError):
     If passed the `mutagen_type` argument this indicates that the
     mutagen type is not supported by `Mediafile`.
     """
-    def __init__(self, path, mutagen_type=None):
+    def __init__(self, filename, mutagen_type=None):
         if mutagen_type is None:
-            msg = u'{0!r}: not in a recognized format'.format(path)
+            msg = u'{0!r}: not in a recognized format'.format(filename)
         else:
-            msg = u'{0}: of mutagen type {1}'.format(repr(path), mutagen_type)
+            msg = u'{0}: of mutagen type {1}'.format(
+                repr(filename), mutagen_type
+            )
         Exception.__init__(self, msg)
 
 
 class MutagenError(UnreadableFileError):
     """Raised when Mutagen fails unexpectedly---probably due to a bug.
     """
-    def __init__(self, path, mutagen_exc):
-        msg = u'{0}: {1}'.format(repr(path), mutagen_exc)
+    def __init__(self, filename, mutagen_exc):
+        msg = u'{0}: {1}'.format(repr(filename), mutagen_exc)
         Exception.__init__(self, msg)
 
 
 # Interacting with Mutagen.
 
-def mutagen_call(action, path, func, *args, **kwargs):
+
+def mutagen_call(action, filename, func, *args, **kwargs):
     """Call a Mutagen function with appropriate error handling.
 
     `action` is a string describing what the function is trying to do,
-    and `path` is the relevant filename. The rest of the arguments
+    and `filename` is the relevant filename. The rest of the arguments
     describe the callable to invoke.
 
     We require at least Mutagen 1.33, where `IOError` is *never* used,
@@ -130,16 +135,52 @@ def mutagen_call(action, path, func, *args, **kwargs):
         return func(*args, **kwargs)
     except mutagen.MutagenError as exc:
         log.debug(u'%s failed: %s', action, six.text_type(exc))
-        raise UnreadableFileError(path, six.text_type(exc))
+        raise UnreadableFileError(filename, six.text_type(exc))
+    except UnreadableFileError:
+        # Reraise our errors without changes.
+        # Used in case of decorating functions (e.g. by `loadfile`).
+        raise
     except Exception as exc:
         # Isolate bugs in Mutagen.
         log.debug(u'%s', traceback.format_exc())
         log.error(u'uncaught Mutagen exception in %s: %s', action, exc)
-        raise MutagenError(path, exc)
+        raise MutagenError(filename, exc)
+
+
+def loadfile(method=True, writable=False, create=False):
+    """A decorator that works like `mutagen._util.loadfile` but with
+    additional error handling.
+
+    Opens a file and passes a `mutagen._utils.FileThing` to the
+    decorated function. Should be used as a decorator for functions
+    using a `filething` parameter.
+    """
+    def decorator(func):
+        f = mutagen._util.loadfile(method, writable, create)(func)
+
+        @functools.wraps(func)
+        def wrapper(*args, **kwargs):
+            return mutagen_call('loadfile', '', f, *args, **kwargs)
+        return wrapper
+    return decorator
 
 
 # Utility.
 
+def _update_filething(filething):
+    """Reopen a `filething` if it's a local file.
+
+    A filething that is *not* an actual file is left unchanged; a
+    filething with a filename is reopened and a new object is returned.
+    """
+    if filething.filename:
+        return mutagen._util.FileThing(
+            None, filething.filename, filething.name
+        )
+    else:
+        return filething
+
+
 def _safe_cast(out_type, val):
     """Try to covert val to out_type but never raise an exception.
 
@@ -1503,20 +1544,27 @@ class MediaFile(object):
     """Represents a multimedia file on disk and provides access to its
     metadata.
     """
-    def __init__(self, path, id3v23=False):
-        """Constructs a new `MediaFile` reflecting the file at path. May
-        throw `UnreadableFileError`.
+    @loadfile()
+    def __init__(self, filething, id3v23=False):
+        """Constructs a new `MediaFile` reflecting the provided file.
+
+        `filething` can be a path to a file (i.e., a string) or a
+        file-like object.
+
+        May throw `UnreadableFileError`.
 
         By default, MP3 files are saved with ID3v2.4 tags. You can use
         the older ID3v2.3 standard by specifying the `id3v23` option.
         """
-        self.path = path
+        self.filething = filething
 
-        self.mgfile = mutagen_call('open', path, mutagen.File, path)
+        self.mgfile = mutagen_call(
+            'open', self.filename, mutagen.File, filething
+        )
 
         if self.mgfile is None:
             # Mutagen couldn't guess the type
-            raise FileTypeError(path)
+            raise FileTypeError(self.filename)
         elif type(self.mgfile).__name__ in ['M4A', 'MP4']:
             info = self.mgfile.info
             if info.codec and info.codec.startswith('alac'):
@@ -1544,7 +1592,7 @@ class MediaFile(object):
         elif type(self.mgfile).__name__ == 'DSF':
             self.type = 'dsf'
         else:
-            raise FileTypeError(path, type(self.mgfile).__name__)
+            raise FileTypeError(self.filename, type(self.mgfile).__name__)
 
         # Add a set of tags if it's missing.
         if self.mgfile.tags is None:
@@ -1553,6 +1601,45 @@ class MediaFile(object):
         # Set the ID3v2.3 flag only for MP3s.
         self.id3v23 = id3v23 and self.type == 'mp3'
 
+    @property
+    def filename(self):
+        """The name of the file.
+
+        This is the path if this object was opened from the filesystem,
+        or the name of the file-like object.
+        """
+        return self.filething.name
+
+    @filename.setter
+    def filename(self, val):
+        """Silently skips setting filename.
+        Workaround for `mutagen._util._openfile` setting instance's filename.
+        """
+        pass
+
+    @property
+    def path(self):
+        """The path to the file.
+
+        This is `None` if the data comes from a file-like object instead
+        of a filesystem path.
+        """
+        return self.filething.filename
+
+    @property
+    def filesize(self):
+        """The size (in bytes) of the underlying file.
+        """
+        if self.filething.filename:
+            return os.path.getsize(self.filething.filename)
+        if hasattr(self.filething.fileobj, '__len__'):
+            return len(self.filething.fileobj)
+        else:
+            tell = self.filething.fileobj.tell()
+            filesize = self.filething.fileobj.seek(0, 2)
+            self.filething.fileobj.seek(tell)
+            return filesize
+
     def save(self, **kwargs):
         """Write the object's tags back to the file.
 
@@ -1568,13 +1655,15 @@ class MediaFile(object):
             id3.update_to_v23()
             kwargs['v2_version'] = 3
 
-        mutagen_call('save', self.path, self.mgfile.save, **kwargs)
+        mutagen_call('save', self.filename, self.mgfile.save,
+                     _update_filething(self.filething), **kwargs)
 
     def delete(self):
         """Remove the current metadata tag from the file. May
         throw `UnreadableFileError`.
         """
-        mutagen_call('delete', self.path, self.mgfile.delete)
+        mutagen_call('delete', self.filename, self.mgfile.delete,
+                     _update_filething(self.filething))
 
     # Convenient access to the set of available fields.
 
@@ -1664,6 +1753,13 @@ class MediaFile(object):
                 else:
                     setattr(self, field, dict[field])
 
+    def as_dict(self):
+        """Get a dictionary with all writable properties that reflect
+        metadata tags (i.e., those that are instances of
+        :class:`MediaField`).
+        """
+        return dict((x, getattr(self, x)) for x in self.fields())
+
     # Field definitions.
 
     title = MediaField(
@@ -2216,8 +2312,7 @@ class MediaFile(object):
             if not self.length:
                 # Avoid division by zero if length is not available.
                 return 0
-            size = os.path.getsize(self.path)
-            return int(size * 8 / self.length)
+            return int(self.filesize * 8 / self.length)
 
     @property
     def format(self):


Test patch:
diff --git a/test/test_mediafile.py b/test/test_mediafile.py
index 269cdd2..32daf7d 100644
--- a/test/test_mediafile.py
+++ b/test/test_mediafile.py
@@ -67,7 +67,7 @@ class ArtTestMixin(object):
         mediafile.art = self.png_data
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertEqual(mediafile.art, self.png_data)
 
     def test_set_jpg_art(self):
@@ -75,7 +75,7 @@ class ArtTestMixin(object):
         mediafile.art = self.jpg_data
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertEqual(mediafile.art, self.jpg_data)
 
     def test_delete_art(self):
@@ -83,13 +83,13 @@ class ArtTestMixin(object):
         mediafile.art = self.jpg_data
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertIsNotNone(mediafile.art)
 
         del mediafile.art
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertIsNone(mediafile.art)
 
 
@@ -125,7 +125,7 @@ class ImageStructureTestMixin(ArtTestMixin):
         mediafile.images = [image]
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertEqual(len(mediafile.images), 1)
 
         image = mediafile.images[0]
@@ -143,7 +143,7 @@ class ImageStructureTestMixin(ArtTestMixin):
         mediafile.images += [image]
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertEqual(len(mediafile.images), 3)
 
         images = (i for i in mediafile.images if i.desc == u'the composer')
@@ -159,7 +159,7 @@ class ImageStructureTestMixin(ArtTestMixin):
         del mediafile.images
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertEqual(len(mediafile.images), 0)
 
     def test_guess_cover(self):
@@ -195,7 +195,7 @@ class ExtendedImageStructureTestMixin(ImageStructureTestMixin):
         mediafile.images += [image]
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertEqual(len(mediafile.images), 3)
 
         # WMA does not preserve the order, so we have to work around this
@@ -212,50 +212,50 @@ class LazySaveTestMixin(object):
     @unittest.skip(u'not yet implemented')
     def test_unmodified(self):
         mediafile = self._mediafile_fixture('full')
-        mtime = self._set_past_mtime(mediafile.path)
-        self.assertEqual(os.stat(mediafile.path).st_mtime, mtime)
+        mtime = self._set_past_mtime(mediafile.filename)
+        self.assertEqual(os.stat(mediafile.filename).st_mtime, mtime)
 
         mediafile.save()
-        self.assertEqual(os.stat(mediafile.path).st_mtime, mtime)
+        self.assertEqual(os.stat(mediafile.filename).st_mtime, mtime)
 
     @unittest.skip(u'not yet implemented')
     def test_same_tag_value(self):
         mediafile = self._mediafile_fixture('full')
-        mtime = self._set_past_mtime(mediafile.path)
-        self.assertEqual(os.stat(mediafile.path).st_mtime, mtime)
+        mtime = self._set_past_mtime(mediafile.filename)
+        self.assertEqual(os.stat(mediafile.filename).st_mtime, mtime)
 
         mediafile.title = mediafile.title
         mediafile.save()
-        self.assertEqual(os.stat(mediafile.path).st_mtime, mtime)
+        self.assertEqual(os.stat(mediafile.filename).st_mtime, mtime)
 
     def test_update_same_tag_value(self):
         mediafile = self._mediafile_fixture('full')
-        mtime = self._set_past_mtime(mediafile.path)
-        self.assertEqual(os.stat(mediafile.path).st_mtime, mtime)
+        mtime = self._set_past_mtime(mediafile.filename)
+        self.assertEqual(os.stat(mediafile.filename).st_mtime, mtime)
 
         mediafile.update({'title': mediafile.title})
         mediafile.save()
-        self.assertEqual(os.stat(mediafile.path).st_mtime, mtime)
+        self.assertEqual(os.stat(mediafile.filename).st_mtime, mtime)
 
     @unittest.skip(u'not yet implemented')
     def test_tag_value_change(self):
         mediafile = self._mediafile_fixture('full')
-        mtime = self._set_past_mtime(mediafile.path)
-        self.assertEqual(os.stat(mediafile.path).st_mtime, mtime)
+        mtime = self._set_past_mtime(mediafile.filename)
+        self.assertEqual(os.stat(mediafile.filename).st_mtime, mtime)
 
         mediafile.title = mediafile.title
         mediafile.album = u'another'
         mediafile.save()
-        self.assertNotEqual(os.stat(mediafile.path).st_mtime, mtime)
+        self.assertNotEqual(os.stat(mediafile.filename).st_mtime, mtime)
 
     def test_update_changed_tag_value(self):
         mediafile = self._mediafile_fixture('full')
-        mtime = self._set_past_mtime(mediafile.path)
-        self.assertEqual(os.stat(mediafile.path).st_mtime, mtime)
+        mtime = self._set_past_mtime(mediafile.filename)
+        self.assertEqual(os.stat(mediafile.filename).st_mtime, mtime)
 
         mediafile.update({'title': mediafile.title, 'album': u'another'})
         mediafile.save()
-        self.assertNotEqual(os.stat(mediafile.path).st_mtime, mtime)
+        self.assertNotEqual(os.stat(mediafile.filename).st_mtime, mtime)
 
     def _set_past_mtime(self, path):
         mtime = round(time.time() - 10000)
@@ -276,7 +276,7 @@ class GenreListTestMixin(object):
         mediafile.genres = [u'one', u'two']
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         assertCountEqual(self, mediafile.genres, [u'one', u'two'])
 
     def test_write_genre_list_get_first(self):
@@ -284,7 +284,7 @@ class GenreListTestMixin(object):
         mediafile.genres = [u'one', u'two']
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertEqual(mediafile.genre, u'one')
 
     def test_append_genre_list(self):
@@ -293,7 +293,7 @@ class GenreListTestMixin(object):
         mediafile.genres += [u'another']
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         assertCountEqual(self, mediafile.genres, [u'the genre', u'another'])
 
 
@@ -416,12 +416,12 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
 
     def test_read_nonexisting(self):
         mediafile = self._mediafile_fixture('full')
-        os.remove(mediafile.path)
-        self.assertRaises(UnreadableFileError, MediaFile, mediafile.path)
+        os.remove(mediafile.filename)
+        self.assertRaises(UnreadableFileError, MediaFile, mediafile.filename)
 
     def test_save_nonexisting(self):
         mediafile = self._mediafile_fixture('full')
-        os.remove(mediafile.path)
+        os.remove(mediafile.filename)
         try:
             mediafile.save()
         except UnreadableFileError:
@@ -429,7 +429,7 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
 
     def test_delete_nonexisting(self):
         mediafile = self._mediafile_fixture('full')
-        os.remove(mediafile.path)
+        os.remove(mediafile.filename)
         try:
             mediafile.delete()
         except UnreadableFileError:
@@ -465,7 +465,7 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
             setattr(mediafile, key, value)
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertTags(mediafile, tags)
 
     def test_update_empty(self):
@@ -475,7 +475,7 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
         mediafile.update(tags)
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertTags(mediafile, tags)
 
     def test_overwrite_full(self):
@@ -491,7 +491,7 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
             setattr(mediafile, key, value)
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertTags(mediafile, tags)
 
     def test_update_full(self):
@@ -504,7 +504,7 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
         mediafile.update(tags)
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertTags(mediafile, tags)
 
     def test_write_date_components(self):
@@ -517,7 +517,7 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
         mediafile.original_day = 30
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertEqual(mediafile.year, 2001)
         self.assertEqual(mediafile.month, 1)
         self.assertEqual(mediafile.day, 2)
@@ -534,7 +534,7 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
         mediafile.day = 2
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertEqual(mediafile.year, 2001)
         self.assertIsNone(mediafile.month)
         self.assertIsNone(mediafile.day)
@@ -546,7 +546,7 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
         mediafile.original_date = datetime.date(1999, 12, 30)
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertEqual(mediafile.year, 2001)
         self.assertEqual(mediafile.month, 1)
         self.assertEqual(mediafile.day, 2)
@@ -580,7 +580,7 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
         mediafile.track = 1
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertEqual(mediafile.track, 1)
         self.assertEqual(mediafile.tracktotal, 2)
 
@@ -597,7 +597,7 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
         delattr(mediafile, 'disctotal')
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertEqual(mediafile.track, 10)
         self.assertEqual(mediafile.tracktotal, None)
         self.assertEqual(mediafile.disc, 10)
@@ -623,7 +623,7 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
         for key in keys:
             delattr(mediafile, key)
         mediafile.save()
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
 
         for key in keys:
             value = getattr(mediafile, key)
@@ -639,7 +639,7 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
         delattr(mediafile, 'disctotal')
 
         mediafile.save()
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertEqual(mediafile.track, self.full_initial_tags['track'])
         self.assertEqual(mediafile.disc, self.full_initial_tags['disc'])
 
@@ -648,7 +648,7 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
 
         mediafile.date = datetime.date(2001, 12, 3)
         mediafile.save()
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertIsNotNone(mediafile.date)
         self.assertIsNotNone(mediafile.year)
         self.assertIsNotNone(mediafile.month)
@@ -656,7 +656,7 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
 
         delattr(mediafile, 'month')
         mediafile.save()
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertIsNotNone(mediafile.date)
         self.assertIsNotNone(mediafile.year)
         self.assertIsNone(mediafile.month)
@@ -670,7 +670,7 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
 
         delattr(mediafile, 'year')
         mediafile.save()
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertIsNone(mediafile.date)
         self.assertIsNone(mediafile.year)
 
@@ -836,7 +836,7 @@ class WMATest(ReadWriteTestBase, ExtendedImageStructureTestMixin,
         mediafile.genres = [u'one', u'two']
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertIn(mediafile.genre, [u'one', u'two'])
 
     def test_read_pure_tags(self):
@@ -868,7 +868,7 @@ class OggTest(ReadWriteTestBase, ExtendedImageStructureTestMixin,
         mediafile.year = 2000
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertEqual(mediafile.mgfile['YEAR'], [u'2000'])
 
     def test_legacy_coverart_tag(self):
@@ -879,7 +879,7 @@ class OggTest(ReadWriteTestBase, ExtendedImageStructureTestMixin,
         mediafile.art = self.png_data
         mediafile.save()
 
-        mediafile = MediaFile(mediafile.path)
+        mediafile = MediaFile(mediafile.filename)
         self.assertFalse('coverart' in mediafile.mgfile)
 
     def test_date_tag_with_slashes(self):
diff --git a/test/test_mediafile_edge.py b/test/test_mediafile_edge.py
index 219dbaf..3119861 100644
--- a/test/test_mediafile_edge.py
+++ b/test/test_mediafile_edge.py
@@ -436,7 +436,6 @@ class ReadOnlyTagTest(unittest.TestCase, _common.TempDirMixin):
         shutil.copy(src, path)
         mf = mediafile.MediaFile(path)
         mf.read_only_field = "something terrible"
-        mf.path = os.path.join(self.temp_dir, b'test.flac')
         mf.save()
         self.assertNotIn(self.key, mf.mgfile.tags)