Python - str() outputs non unicode
Question: I'm using the [py](https://pypi.python.org/pypi/py) library to rename .flac
files with information from its metadata. I'm having a problem with getting a
`LocalPath` object to a normal utf-8 string to run it into `subprocess`.

Here's the bit of code that goes wrong:

    
    
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    
    # This program takes the information from FLAC metadata to rename the files
    # according to various naming paterns.
    
    import subprocess
    import sys
    from py.path import local
    
    
    # Defining the function that fetches metadata and formats it
    def metadata(filename):
        filename = str(filename).decode("utf-8")
    
        pipe = subprocess.Popen(
            ["metaflac", "--show-tag=tracknumber", filename],
            stdout=subprocess.PIPE)
        tracknumber, error = pipe.communicate()
        tracknumber = tracknumber.decode("utf-8")
        tracknumber = tracknumber.replace("tracknumber=", "")
        tracknumber = tracknumber.replace("TRACKNUMBER=", "")
        tracknumber = tracknumber.rstrip() # Remove whitespaces
        if int(tracknumber) < 10:
            if "0" in tracknumber:
                pass
            else: 
                tracknumber = "0" + tracknumber
        else:
            pass
    
        pipe = subprocess.Popen(
            ["metaflac", "--show-tag=title", filename],
            stdout=subprocess.PIPE)
        title, error = pipe.communicate()
        title = title.decode("utf-8")
        title = title.replace("TITLE=", "")
        title = title.replace("title=", "")
        title = title.rstrip()
    
        pipe = subprocess.Popen(
            ["metaflac", "--show-tag=artist", filename],
            stdout=subprocess.PIPE)
        artist, error = pipe.communicate()
        artist = artist.decode("utf-8")
        artist = artist.replace("ARTIST=", "")
        artist = artist.replace("artist=", "")
        artist = artist.rstrip()
    
        return tracknumber, title, artist
    
    
    # Defining function that renames the files
    def rename(root):
        if output == filename:
            print "%s is already named correctly\n" % (title)
        else:
            filename.rename(filename.new(basename=output))
    
    # Importing command line arguments
    try:
        root = local(sys.argv[2])
        option = sys.argv[1]
        print option, root
    except ValueError:
        print "WTF!!!"
    
    # Running
    if option == "--s":
        for filename in root.visit(fil="*.flac", rec=True):
            tracknumber, title, artist = metadata(filename)
            output = "%s - %s" % (tracknumber, filename)
            rename(root)
    print "Files renamed"
    

I'm using `filename = str(filename)` because `filename` is not a string, but a
`LocalPath` and `subprocess` crashes when not given string.

The problem is that with utf-8 file name, `str(filename)` gives back something
like this: `/media/Main/Musique/2 Bal 2 Neg'/3X plus efficace/09 -
Po\xc3\xa8tes de la mort.flac`

Adding `filename = filename.decode("utf-8")` does not seem to change anything

I'm still very new to python :D


Answer: The `UnboundLocalError` you are getting at the end is because in this line:

    
    
    filename = str(filename)
    

you are trying to treat `filename` as both a local and a global variable. You
are (hopefully) not wanting to reassign the global (which would happen if you
did `global filename` before this line). But when you assign it as a local,
Python will not let you look up the global version of it anywhere in the
function - if you do this before it is assigned, you get the error you are
seeing. Choose a different variable name, or pass `filename` in as an argument
- eg,

    
    
    def metadata(filename):
        filename = str(filename)
        ...
        return tracknumber, title, artist
    
    tracknumber, title, artist = metadata(filename)
    

will work.

To fix your unicode errors, you are mostly on the right track. You need to
maintain the distiction at all times between bytes and (unicode) characters -
in Python 2, use `str` for the former and `unicode` for the latter. If you
upgrade to Python 3, you would use `bytes` for the former and `str` for the
latter (and the distiction would be easier to maintain in a lot of cases). Use
the `decode` method to go from bytes to unicode, and the `encode` method to go
the other way.

You generally want to use bytes only when you are directly doing IO, and
unicode everywhere else. The `subprocess` module accepts _either_ , but for
filenames it is usually easier to use unicode.

The `Py` library helpfully doesn't maintain, or acknowledge, this distinction
at all. It just uses `str` internally, regardless of which Python version it
is running on (it uses a single codebase for both Python 2.x and Python 3.x).
This means in Python 2, you _do_ want to decode it to `unicode`:

    
    
    def metadata(filename):
       filename = str(filename).decode('utf-8')
    

but in Python 3, you want to leave it as a `str`:

    
    
    def metadata(filename):
       filename = str(filename)
    

in both cases, you then want to only use `filename` in all your calls to
`subprocess.Popen`, and _not_ `str(filename)`. So,

    
    
    pipe = subprocess.Popen(
        ["metaflac", "--show-tag=title", str(filename)],
        stdout=subprocess.PIPE)
    

should be:

    
    
    pipe = subprocess.Popen(
        ["metaflac", "--show-tag=title", filename],
        stdout=subprocess.PIPE)
    

You want to do something similar with the metadata you do get back.
`communicate` counts as direct IO, and so it returns bytes, not characters.
You already try to convert two of your three bits of metadata like this:

    
    
    title.decode("utf-8")
    

but that needs to be:

    
    
    title = title.decode("utf-8")
    

you need to do the same for `artist`, and add the equivalent line for
`tracknumber`.

