[Python-modules-commits] [mutagen] 02/07: Import mutagen_1.35.1.orig.tar.gz

Tristan Seligmann mithrandi at moszumanska.debian.org
Fri Dec 16 09:33:40 UTC 2016


This is an automated email from the git hooks/post-receive script.

mithrandi pushed a commit to branch master
in repository mutagen.

commit c4d908388c34fb66492a01316b4933c29776c5cb
Author: Tristan Seligmann <mithrandi at debian.org>
Date:   Fri Dec 16 11:14:43 2016 +0200

    Import mutagen_1.35.1.orig.tar.gz
---
 MANIFEST.in                                        |   2 +
 NEWS                                               |  22 +
 PKG-INFO                                           |  25 +-
 README.rst                                         |  15 +-
 docs/conf.py                                       |   4 +-
 docs/id3_frames_gen.py                             |  10 +-
 docs/index.rst                                     |  15 +-
 mutagen/__init__.py                                |   2 +-
 mutagen/_senf/README.rst                           |   1 +
 mutagen/_senf/__init__.py                          |  82 ++++
 mutagen/_senf/_argv.py                             |  46 ++
 mutagen/_senf/_compat.py                           |  52 +++
 mutagen/_senf/_environ.py                          | 238 +++++++++++
 mutagen/_senf/_fsnative.py                         | 420 +++++++++++++++++++
 mutagen/_senf/_print.py                            | 352 ++++++++++++++++
 mutagen/_senf/_stdlib.py                           | 146 +++++++
 mutagen/_senf/_temp.py                             |  90 ++++
 mutagen/_senf/_winansi.py                          | 311 ++++++++++++++
 mutagen/_senf/_winapi.py                           | 183 ++++++++
 mutagen/_tools/__init__.py                         |   6 +
 mutagen/_tools/_util.py                            |  94 +++++
 tools/mid3cp => mutagen/_tools/mid3cp.py           |  17 +-
 tools/mid3iconv => mutagen/_tools/mid3iconv.py     |  19 +-
 tools/mid3v2 => mutagen/_tools/mid3v2.py           | 100 +++--
 tools/moggsplit => mutagen/_tools/moggsplit.py     |  16 +-
 .../_tools/mutagen_inspect.py                      |  20 +-
 .../mutagen-pony => mutagen/_tools/mutagen_pony.py |  13 +-
 mutagen/_toolsutil.py                              | 231 ----------
 mutagen/_util.py                                   |   2 +-
 mutagen/asf/__init__.py                            |   5 +-
 mutagen/asf/_util.py                               |   2 +-
 mutagen/id3/_frames.py                             |  66 +--
 mutagen/id3/_specs.py                              |  37 +-
 mutagen/id3/_tags.py                               |  61 ++-
 mutagen/mp3/__init__.py                            |  10 +-
 mutagen/mp4/__init__.py                            |   1 -
 setup.py                                           |  55 ++-
 tests/__init__.py                                  | 130 +-----
 tests/quality/test_pep8.py                         |  46 +-
 tests/quality/test_pyflakes.py                     |  18 +-
 tests/test___init__.py                             |  14 +-
 tests/test__id3frames.py                           |  40 +-
 tests/test_asf.py                                  |  10 +-
 tests/test_encoding.py                             |   2 +-
 tests/test_id3.py                                  |  43 +-
 tests/test_mp4.py                                  |   2 +-
 tests/test_tools.py                                |  22 +-
 tests/test_tools_mid3cp.py                         |   8 +-
 tests/test_tools_mid3iconv.py                      |   2 +-
 tests/test_tools_mid3v2.py                         |   4 +-
 tests/test_tools_moggsplit.py                      |   2 +-
 tests/test_tools_mutagen_inspect.py                |   2 +-
 tests/test_tools_mutagen_pony.py                   |   4 +-
 tests/{test__toolsutil.py => test_tools_util.py}   |  12 +-
 tools/mid3cp                                       | 116 +-----
 tools/mid3iconv                                    | 159 +------
 tools/mid3v2                                       | 463 +--------------------
 tools/moggsplit                                    |  66 +--
 tools/mutagen-inspect                              |  36 +-
 tools/mutagen-pony                                 | 107 +----
 60 files changed, 2494 insertions(+), 1585 deletions(-)

diff --git a/MANIFEST.in b/MANIFEST.in
index e48dc4c..e466433 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,10 +1,12 @@
 include COPYING
 include NEWS
 include README.rst
+include setup.cfg
 include MANIFEST.in
 include tests/data/*
 include tests/quality/*
 include tests/*.py
 include man/*.1
+recursive-include mutagen README.rst
 recursive-include docs *.py Makefile *.rst *.png *.svg *.ico
 prune docs/_build
diff --git a/NEWS b/NEWS
index 6f1c44b..0d629b8 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,25 @@
+1.35.1 - 2016.11.09
+-------------------
+
+* Revert back to distutils :bug:`273`
+
+
+1.35 - 2016.11.02
+-----------------
+
+* Tests: Require pytest
+* Tools: Install .exe launchers on Windows
+* setup.py: Require setuptools
+* ID3:
+
+  * Fix loading files with CRM frames :bug:`239`
+  * Fix loading AENC, LINK, GRID frames with no payload
+  * Merge duplicate text frames with same key on load :bug:`172`
+  * Allow parsing of duplicate APIC frames :bug:`172`
+  * Parse utf-16 text fields with missing BOM :bug:`267`
+  * Increase max resyncs for the mpeg frame search :bug:`268`
+
+
 1.34.1 - 2016.08.13
 -------------------
 
diff --git a/PKG-INFO b/PKG-INFO
index fb7bc61..96d39af 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,19 +1,26 @@
 Metadata-Version: 1.1
 Name: mutagen
-Version: 1.34.1
+Version: 1.35.1
 Summary: read and write audio tags for many formats
 Home-page: https://github.com/quodlibet/mutagen
 Author: Michael Urman
 Author-email: quod-libet-development at groups.google.com
 License: GNU GPL v2
-Description: Mutagen is a Python module to handle audio metadata. It supports ASF,
-        FLAC, M4A, Monkey's Audio, MP3, Musepack, Ogg FLAC, Ogg Speex, Ogg
-        Theora, Ogg Vorbis, True Audio, WavPack and OptimFROG audio files. All
-        versions of ID3v2 are supported, and all standard ID3v2.4 frames are
-        parsed. It can read Xing headers to accurately calculate the bitrate
-        and length of MP3s. ID3 and APEv2 tags can be edited regardless of
-        audio format. It can also manipulate Ogg streams on an individual
-        packet/page level.
+Description: Mutagen is a Python module to handle audio metadata. It supports ASF, FLAC,
+        MP4, Monkey's Audio, MP3, Musepack, Ogg Opus, Ogg FLAC, Ogg Speex, Ogg Theora,
+        Ogg Vorbis, True Audio, WavPack, OptimFROG, and AIFF audio files. All
+        versions of ID3v2 are supported, and all standard ID3v2.4 frames are parsed.
+        It can read Xing headers to accurately calculate the bitrate and length of
+        MP3s. ID3 and APEv2 tags can be edited regardless of audio format. It can also
+        manipulate Ogg streams on an individual packet/page level.
+        
+        Mutagen works with Python 2.7, 3.3+ (CPython and PyPy) on Linux, Windows and
+        macOS, and has no dependencies outside the Python standard library.
+        
+        For more information visit https://mutagen.readthedocs.org
+        
+        .. image:: https://travis-ci.org/quodlibet/mutagen.svg?branch=master
+            :target: https://travis-ci.org/quodlibet/mutagen
         
 Platform: UNKNOWN
 Classifier: Operating System :: OS Independent
diff --git a/README.rst b/README.rst
index 1affc6a..7293508 100644
--- a/README.rst
+++ b/README.rst
@@ -1,8 +1,15 @@
-Mutagen
-=======
+Mutagen is a Python module to handle audio metadata. It supports ASF, FLAC,
+MP4, Monkey's Audio, MP3, Musepack, Ogg Opus, Ogg FLAC, Ogg Speex, Ogg Theora,
+Ogg Vorbis, True Audio, WavPack, OptimFROG, and AIFF audio files. All
+versions of ID3v2 are supported, and all standard ID3v2.4 frames are parsed.
+It can read Xing headers to accurately calculate the bitrate and length of
+MP3s. ID3 and APEv2 tags can be edited regardless of audio format. It can also
+manipulate Ogg streams on an individual packet/page level.
 
-Mutagen is a Python module to handle audio metadata. For more information
-visit http://mutagen.readthedocs.org
+Mutagen works with Python 2.7, 3.3+ (CPython and PyPy) on Linux, Windows and
+macOS, and has no dependencies outside the Python standard library.
+
+For more information visit https://mutagen.readthedocs.org
 
 .. image:: https://travis-ci.org/quodlibet/mutagen.svg?branch=master
     :target: https://travis-ci.org/quodlibet/mutagen
diff --git a/docs/conf.py b/docs/conf.py
index f2fc667..40faa34 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -6,7 +6,6 @@ import sys
 dir_ = os.path.dirname(os.path.realpath(__file__))
 sys.path.insert(0, dir_)
 sys.path.insert(0, os.path.abspath(os.path.join(dir_, "..")))
-import mutagen
 
 needs_sphinx = "1.3"
 
@@ -25,8 +24,7 @@ master_doc = 'index'
 project = 'mutagen'
 copyright = u'2016, Joe Wreschnig, Michael Urman, Lukáš Lalinský, ' \
             u'Christoph Reiter, Ben Ockmore & others'
-version = mutagen.version_string
-release = mutagen.version_string
+html_title = project
 exclude_patterns = ['_build']
 bug_url_template = "https://github.com/quodlibet/mutagen/issues/%s"
 pr_url_template = "https://github.com/quodlibet/mutagen/pull/%s"
diff --git a/docs/id3_frames_gen.py b/docs/id3_frames_gen.py
index 68848ce..162085f 100755
--- a/docs/id3_frames_gen.py
+++ b/docs/id3_frames_gen.py
@@ -26,9 +26,9 @@ BaseFrames = dict([(k, v) for (k, v) in vars(mutagen.id3).items()
 
 
 def print_header(header, type_="-"):
-    print header
-    print type_ * len(header)
-    print
+    print(header)
+    print(type_ * len(header))
+    print("")
 
 
 def print_frames(frames, sort_mro=False):
@@ -39,11 +39,11 @@ def print_frames(frames, sort_mro=False):
         sort_func = lambda x: x
 
     for name, cls in sorted(frames.items(), key=sort_func):
-        print """
+        print("""
 .. autoclass:: mutagen.id3.%s
     :show-inheritance:
     :members:
-""" % repr(cls())
+""" % repr(cls()))
 
 
 if __name__ == "__main__":
diff --git a/docs/index.rst b/docs/index.rst
index d14c9fc..4956d1d 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -13,16 +13,11 @@
     man/index
     contact
 
-Mutagen is a Python module to handle audio metadata. It supports ASF, FLAC,
-MP4, Monkey's Audio, MP3, Musepack, Ogg Opus, Ogg FLAC, Ogg Speex, Ogg Theora,
-Ogg Vorbis, True Audio, WavPack, OptimFROG, and AIFF audio files. All
-versions of ID3v2 are supported, and all standard ID3v2.4 frames are parsed.
-It can read Xing headers to accurately calculate the bitrate and length of
-MP3s. ID3 and APEv2 tags can be edited regardless of audio format. It can also
-manipulate Ogg streams on an individual packet/page level.
-
-Mutagen works with Python 2.7, 3.3+ (CPython and PyPy) on Linux, Windows and
-macOS, and has no dependencies outside the Python standard library.
+.. title:: Overview
+
+.. include:: ../README.rst
+
+----
 
 There is a :doc:`brief tutorial with several API examples. 
 <user/index>`
diff --git a/mutagen/__init__.py b/mutagen/__init__.py
index 824952e..c996b77 100644
--- a/mutagen/__init__.py
+++ b/mutagen/__init__.py
@@ -24,7 +24,7 @@ from mutagen._util import MutagenError
 from mutagen._file import FileType, StreamInfo, File
 from mutagen._tags import Tags, Metadata, PaddingInfo
 
-version = (1, 34, 1)
+version = (1, 35, 1)
 """Version tuple."""
 
 version_string = ".".join(map(str, version))
diff --git a/mutagen/_senf/README.rst b/mutagen/_senf/README.rst
new file mode 100644
index 0000000..49ff928
--- /dev/null
+++ b/mutagen/_senf/README.rst
@@ -0,0 +1 @@
+Don't change things here, this is a copy of https://github.com/lazka/senf
diff --git a/mutagen/_senf/__init__.py b/mutagen/_senf/__init__.py
new file mode 100644
index 0000000..f2fcb4f
--- /dev/null
+++ b/mutagen/_senf/__init__.py
@@ -0,0 +1,82 @@
+# -*- coding: utf-8 -*-
+# Copyright 2016 Christoph Reiter
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+import os
+
+if os.name != "nt":
+    # make imports work
+    _winapi = object()
+
+from ._fsnative import fsnative, path2fsn, fsn2text, fsn2bytes, \
+    bytes2fsn, uri2fsn, fsn2uri, text2fsn
+from ._print import print_, input_
+from ._stdlib import sep, pathsep, curdir, pardir, altsep, extsep, devnull, \
+    defpath, getcwd, expanduser, expandvars
+from ._argv import argv
+from ._environ import environ, getenv, unsetenv, putenv
+from ._temp import mkstemp, gettempdir, gettempprefix, mkdtemp
+
+
+fsnative, print_, getcwd, getenv, unsetenv, putenv, environ, expandvars, \
+    path2fsn, fsn2text, fsn2bytes, bytes2fsn, uri2fsn, fsn2uri, mkstemp, \
+    gettempdir, gettempprefix, mkdtemp, input_, expanduser, text2fsn
+
+
+version = (1, 0, 1)
+"""Tuple[`int`, `int`, `int`]: The version tuple (major, minor, micro)"""
+
+
+version_string = ".".join(map(str, version))
+"""`str`: A version string"""
+
+
+argv = argv
+"""List[`fsnative`]: Like `sys.argv` but contains unicode under
+Windows + Python 2
+"""
+
+
+sep = sep
+"""`fsnative`: Like `os.sep` but a `fsnative`"""
+
+
+pathsep = pathsep
+"""`fsnative`: Like `os.pathsep` but a `fsnative`"""
+
+
+curdir = curdir
+"""`fsnative`: Like `os.curdir` but a `fsnative`"""
+
+
+pardir = pardir
+"""`fsnative`: Like `os.pardir` but a fsnative"""
+
+
+altsep = altsep
+"""`fsnative` or `None`: Like `os.altsep` but a `fsnative` or `None`"""
+
+
+extsep = extsep
+"""`fsnative`: Like `os.extsep` but a `fsnative`"""
+
+
+devnull = devnull
+"""`fsnative`: Like `os.devnull` but a `fsnative`"""
+
+
+defpath = defpath
+"""`fsnative`: Like `os.defpath` but a `fsnative`"""
+
+
+__all__ = []
diff --git a/mutagen/_senf/_argv.py b/mutagen/_senf/_argv.py
new file mode 100644
index 0000000..14df5c6
--- /dev/null
+++ b/mutagen/_senf/_argv.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+# Copyright 2016 Christoph Reiter
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+import sys
+import ctypes
+
+from ._compat import PY2
+from ._fsnative import is_unix
+from . import _winapi as winapi
+
+
+def create_argv():
+    """Returns a unicode argv under Windows and standard sys.argv otherwise"""
+
+    if is_unix or not PY2:
+        return sys.argv
+
+    argc = ctypes.c_int()
+    try:
+        argv = winapi.CommandLineToArgvW(
+            winapi.GetCommandLineW(), ctypes.byref(argc))
+    except WindowsError:
+        return []
+
+    if not argv:
+        return []
+
+    res = argv[max(0, argc.value - len(sys.argv)):argc.value]
+
+    winapi.LocalFree(argv)
+
+    return res
+
+
+argv = create_argv()
diff --git a/mutagen/_senf/_compat.py b/mutagen/_senf/_compat.py
new file mode 100644
index 0000000..6a1c9cc
--- /dev/null
+++ b/mutagen/_senf/_compat.py
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+# Copyright 2016 Christoph Reiter
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+import sys
+
+
+PY2 = sys.version_info[0] == 2
+PY3 = not PY2
+
+
+if PY2:
+    from urlparse import urlparse
+    urlparse
+    from urllib import pathname2url, url2pathname, quote, unquote
+    pathname2url, url2pathname, quote, unquote
+
+    from StringIO import StringIO
+    BytesIO = StringIO
+    from io import StringIO as TextIO
+    TextIO
+
+    string_types = (str, unicode)
+    text_type = unicode
+
+    iteritems = lambda d: d.iteritems()
+elif PY3:
+    from urllib.parse import urlparse, quote, unquote
+    urlparse, quote, unquote
+    from urllib.request import pathname2url, url2pathname
+    pathname2url, url2pathname
+
+    from io import StringIO
+    StringIO = StringIO
+    TextIO = StringIO
+    from io import BytesIO
+    BytesIO = BytesIO
+
+    string_types = (str,)
+    text_type = str
+
+    iteritems = lambda d: iter(d.items())
diff --git a/mutagen/_senf/_environ.py b/mutagen/_senf/_environ.py
new file mode 100644
index 0000000..5903783
--- /dev/null
+++ b/mutagen/_senf/_environ.py
@@ -0,0 +1,238 @@
+# -*- coding: utf-8 -*-
+# Copyright 2016 Christoph Reiter
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+import os
+import ctypes
+import collections
+
+from ._compat import text_type, PY2
+from ._fsnative import path2fsn, is_win
+from . import _winapi as winapi
+
+
+def get_windows_env_var(key):
+    """Get an env var.
+
+    Raises:
+        WindowsError
+    """
+
+    if not isinstance(key, text_type):
+        raise TypeError("%r not of type %r" % (key, text_type))
+
+    buf = ctypes.create_unicode_buffer(32767)
+
+    stored = winapi.GetEnvironmentVariableW(key, buf, 32767)
+    if stored == 0:
+        raise ctypes.WinError()
+    return buf[:stored]
+
+
+def set_windows_env_var(key, value):
+    """Set an env var.
+
+    Raises:
+        WindowsError
+    """
+
+    if not isinstance(key, text_type):
+        raise TypeError("%r not of type %r" % (key, text_type))
+
+    if not isinstance(value, text_type):
+        raise TypeError("%r not of type %r" % (value, text_type))
+
+    status = winapi.SetEnvironmentVariableW(key, value)
+    if status == 0:
+        raise ctypes.WinError()
+
+
+def del_windows_env_var(key):
+    """Delete an env var.
+
+    Raises:
+        WindowsError
+    """
+
+    if not isinstance(key, text_type):
+        raise TypeError("%r not of type %r" % (key, text_type))
+
+    status = winapi.SetEnvironmentVariableW(key, None)
+    if status == 0:
+        raise ctypes.WinError()
+
+
+def read_windows_environ():
+    """Returns a unicode dict of the Windows environment.
+
+    Raises:
+        WindowsEnvironError
+    """
+
+    res = winapi.GetEnvironmentStringsW()
+    if not res:
+        raise ctypes.WinError()
+
+    res = ctypes.cast(res, ctypes.POINTER(ctypes.c_wchar))
+
+    done = []
+    current = u""
+    i = 0
+    while 1:
+        c = res[i]
+        i += 1
+        if c == u"\x00":
+            if not current:
+                break
+            done.append(current)
+            current = u""
+            continue
+        current += c
+
+    dict_ = {}
+    for entry in done:
+        try:
+            key, value = entry.split(u"=", 1)
+        except ValueError:
+            continue
+        dict_[key] = value
+
+    status = winapi.FreeEnvironmentStringsW(res)
+    if status == 0:
+        raise ctypes.WinError()
+
+    return dict_
+
+
+class Environ(collections.MutableMapping):
+    """Dict[`fsnative`, `fsnative`]: Like `os.environ` but contains unicode
+    keys and values under Windows + Python 2
+    """
+
+    def __init__(self):
+        if is_win and PY2:
+            try:
+                env = read_windows_environ()
+            except WindowsError:
+                env = {}
+        else:
+            env = os.environ
+        self._env = env
+
+    def __getitem__(self, key):
+        key = path2fsn(key)
+        return self._env[key]
+
+    def __setitem__(self, key, value):
+        key = path2fsn(key)
+        value = path2fsn(value)
+
+        if is_win and PY2:
+            try:
+                set_windows_env_var(key, value)
+            except WindowsError:
+                # py3+win fails for invalid keys. try to do the same
+                raise ValueError
+        try:
+            self._env[key] = value
+        except OSError:
+            raise ValueError
+
+    def __delitem__(self, key):
+        key = path2fsn(key)
+
+        if is_win and PY2:
+            try:
+                del_windows_env_var(key)
+            except WindowsError:
+                pass
+
+        del self._env[key]
+
+    def __iter__(self):
+        return iter(self._env)
+
+    def __len__(self):
+        return len(self._env)
+
+    def __repr__(self):
+        return repr(self._env)
+
+    def copy(self):
+        return self._env.copy()
+
+
+environ = Environ()
+
+
+def getenv(key, value=None):
+    """Like `os.getenv` but returns unicode under Windows + Python 2
+
+    Args:
+        key (pathlike): The env var to get
+        value (object): The value to return if the env var does not exist
+    Returns:
+        `fsnative` or `object`:
+            The env var or the passed value if it doesn't exist
+    """
+
+    key = path2fsn(key)
+    if is_win and PY2:
+        return environ.get(key, value)
+    return os.getenv(key, value)
+
+
+def unsetenv(key):
+    """Like `os.unsetenv` but takes unicode under Windows + Python 2
+
+    Args:
+        key (pathlike): The env var to unset
+    """
+
+    key = path2fsn(key)
+    if is_win:
+        # python 3 has no unsetenv under Windows -> use our ctypes one as well
+        try:
+            del_windows_env_var(key)
+        except WindowsError:
+            pass
+    else:
+        os.unsetenv(key)
+
+
+def putenv(key, value):
+    """Like `os.putenv` but takes unicode under Windows + Python 2
+
+    Args:
+        key (pathlike): The env var to get
+        value (pathlike): The value to set
+    Raises:
+        ValueError
+    """
+
+    key = path2fsn(key)
+    value = path2fsn(value)
+
+    if is_win and PY2:
+        try:
+            set_windows_env_var(key, value)
+        except WindowsError:
+            # py3 + win fails here
+            raise ValueError
+    else:
+        try:
+            os.putenv(key, value)
+        except OSError:
+            # win + py3 raise here for invalid keys which is probably a bug.
+            # ValueError seems better
+            raise ValueError
diff --git a/mutagen/_senf/_fsnative.py b/mutagen/_senf/_fsnative.py
new file mode 100644
index 0000000..7b62bc6
--- /dev/null
+++ b/mutagen/_senf/_fsnative.py
@@ -0,0 +1,420 @@
+# -*- coding: utf-8 -*-
+# Copyright 2016 Christoph Reiter
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+import os
+import sys
+import ctypes
+import codecs
+
+from . import _winapi as winapi
+from ._compat import text_type, PY3, PY2, url2pathname, urlparse, quote, \
+    unquote
+
+
+is_win = os.name == "nt"
+is_unix = not is_win
+is_darwin = sys.platform == "darwin"
+
+_surrogatepass = "strict" if PY2 else "surrogatepass"
+
+
+def _decode_surrogatepass(data, codec):
+    """Like data.decode(codec, 'surrogatepass') but makes utf-16-le work
+    on Python < 3.4 + Windows
+
+    https://bugs.python.org/issue27971
+
+    Raises UnicodeDecodeError, LookupError
+    """
+
+    try:
+        return data.decode(codec, _surrogatepass)
+    except UnicodeDecodeError:
+        if os.name == "nt" and sys.version_info[:2] < (3, 4) and \
+                codecs.lookup(codec).name == "utf-16-le":
+            buffer_ = ctypes.create_string_buffer(data + b"\x00\x00")
+            value = ctypes.wstring_at(buffer_, len(data) // 2)
+            if value.encode("utf-16-le", _surrogatepass) != data:
+                raise
+            return value
+        else:
+            raise
+
+
+def _fsnative(text):
+    if not isinstance(text, text_type):
+        raise TypeError("%r needs to be a text type (%r)" % (text, text_type))
+
+    if is_unix:
+        # First we go to bytes so we can be sure we have a valid source.
+        # Theoretically we should fail here in case we have a non-unicode
+        # encoding. But this would make everything complicated and there is
+        # no good way to handle a failure from the user side. Instead
+        # fall back to utf-8 which is the most likely the right choice in
+        # a mis-configured environment
+        encoding = _encoding
+        try:
+            path = text.encode(encoding, _surrogatepass)
+        except UnicodeEncodeError:
+            path = text.encode("utf-8", _surrogatepass)
+        if PY3:
+            return path.decode(_encoding, "surrogateescape")
+        return path
+    else:
+        return text
+
+
+def _create_fsnative(type_):
+    # a bit of magic to make fsnative(u"foo") and isinstance(path, fsnative)
+    # work
+
+    class meta(type):
+
+        def __instancecheck__(self, instance):
+            # XXX: invalid str on Unix + Py3 still returns True here, but
+            # might fail when passed to fsnative API. We could be more strict
+            # here and call _validate_fsnative(), but then we could
+            # have a value not being an instance of fsnative, while its type
+            # is still a subclass of fsnative.. and this is enough magic
+            # already.
+            return isinstance(instance, type_)
+
+        def __subclasscheck__(self, subclass):
+            return issubclass(subclass, type_)
+
+    class impl(object):
+        """fsnative(text=u"")
+
+        Args:
+            text (text): The text to convert to a path
+        Returns:
+            fsnative: The new path.
+        Raises:
+            TypeError: In case something other then `text` has been passed
+
+        This type is a virtual base class for the real path type.
+        Instantiating it returns an instance of the real path type and it
+        overrides instance and subclass checks so that `isinstance` and
+        `issubclass` checks work:
+
+        ::
+
+            isinstance(fsnative(u"foo"), fsnative) == True
+            issubclass(type(fsnative(u"foo")), fsnative) == True
+
+        The real returned type is:
+
+        - Python 2 + Windows: :obj:`python:unicode` with ``surrogates``
+        - Python 2 + Unix: :obj:`python:str`
+        - Python 3 + Windows: :obj:`python3:str` with ``surrogates``
+        - Python 3 + Unix: :obj:`python3:str` with ``surrogates`` (only
+          containing code points which can be encoded with the locale encoding)
+
+        Constructing a `fsnative` can't fail.
+        """
+
+        def __new__(cls, text=u""):
+            return _fsnative(text)
+
+    new_type = meta("fsnative", (object,), dict(impl.__dict__))
+    new_type.__module__ = "senf"
+    return new_type
+
+
+fsnative_type = text_type if is_win or PY3 else bytes
+fsnative = _create_fsnative(fsnative_type)
+
+
+def _validate_fsnative(path):
+    """
+    Args:
+        path (fsnative)
+    Returns:
+        `text` on Windows, `bytes` on Unix
+    Raises:
+        TypeError: in case the type is wrong or the ´str` on Py3 + Unix
+            can't be converted to `bytes`
+
+    This helper allows to validate the type and content of a path.
+    To reduce overhead the encoded value for Py3 + Unix is returned so
+    it can be reused.
+    """
+
+    if not isinstance(path, fsnative_type):
+        raise TypeError("path needs to be %s, not %s" % (
+            fsnative_type.__name__, type(path).__name__))
+
+    if PY3 and is_unix:
+        try:
+            return path.encode(_encoding, "surrogateescape")
+        except UnicodeEncodeError:
+            # This look more like ValueError, but raising only one error
+            # makes things simpler... also one could say str + surrogates
+            # is its own type
+            raise TypeError("path contained Unicode code points not valid in"
+                            "the current path encoding. To create a valid "
+                            "path from Unicode use text2fsn()")
+
+    return path
+
+
+def _get_encoding():
+    """The encoding used for paths, argv, environ, stdout and stdin"""
+
+    encoding = sys.getfilesystemencoding()
+    if encoding is None:
+        if is_darwin:
+            return "utf-8"
+        elif is_win:
+            return "mbcs"
+        else:
+            return "ascii"
+    return encoding
+
+_encoding = _get_encoding()
+
+
+def path2fsn(path):
+    """
+    Args:
+        path (pathlike): The path to convert
+    Returns:
+        `fsnative`
+    Raises:
+        TypeError: In case the type can't be converted to a `fsnative`
+        ValueError: In case conversion fails
+
+    Returns a `fsnative` path for a `pathlike`.
+    """
+
+    # allow mbcs str on py2+win and bytes on py3
+    if PY2:
+        if is_win:
+            if isinstance(path, str):
+                path = path.decode(_encoding)
+        else:
+            if isinstance(path, unicode):
+                path = path.encode(_encoding)
+    else:
+        path = getattr(os, "fspath", lambda x: x)(path)
+        if isinstance(path, bytes):
+            path = path.decode(_encoding, "surrogateescape")
+        elif is_unix and isinstance(path, str):
+            # make sure we can encode it and this is not just some random
+            # unicode string
+            path.encode(_encoding, "surrogateescape")
+
+    if not isinstance(path, fsnative_type):
+        raise TypeError("path needs to be %s", fsnative_type.__name__)
+
+    return path
+
+
+def fsn2text(path):
+    """
+    Args:
+        path (fsnative): The path to convert
+    Returns:
+        `text`
+    Raises:
+        TypeError: In case no `fsnative` has been passed
+
+    Converts a `fsnative` path to `text`.
+
+    This process is not reversible and should only be used for display
+    purposes.
+    Encoding with a Unicode encoding will always succeed with the result.
+    """
+
+    path = _validate_fsnative(path)
+
+    if is_win:
+        return path.encode("utf-16-le", _surrogatepass).decode("utf-16-le",
+                                                               "replace")
+    else:
+        return path.decode(_encoding, "replace")
+
+
+def text2fsn(text):
+    """
+    Args:
+        text (text): The text to convert
+    Returns:
+        `fsnative`
+    Raises:
+        TypeError: In case no `text` has been passed
+
+    Takes `text` and converts it to a `fsnative`.
+
+    This operation is not reversible and can't fail.
+    """
+
+    return fsnative(text)
+
+
+def fsn2bytes(path, encoding):
+    """
+    Args:
+        path (fsnative): The path to convert
+        encoding (`str` or `None`): `None` if you don't care about Windows
+    Returns:
+        `bytes`
+    Raises:
+        TypeError: If no `fsnative` path is passed
... 4450 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/mutagen.git



More information about the Python-modules-commits mailing list