We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
hook: Interpolate paths (and other bytestrings) correctly into commands
### Problem

I have the following configuration for the Hook plugin in my config.yaml:

```
hook:
    hooks:
        - event: album_imported
          command: /usr/bin/ls -l "{album.path}"
```

This is just a test to see how beets presents the path values.  It appears that the paths are returned as bytes objects rather than strings.  This is problematic when using the path values as arguments for external shell scripts.  As can be seen below, the shell is unable to use the value provided by {album.path}.


```sh
$ beet -vv import /tmp/music/new/Al\ Di\ Meola\ -\ Elegant\ Gypsy/
```

Led to this problem:

```
hook: running command "/usr/bin/ls -l b'/tmp/music/FLAC/Al Di Meola/Elegant Gypsy'" for event album_imported
/usr/bin/ls: cannot access "b'/tmp/music/FLAC/Al Di Meola/Elegant Gypsy'": No such file or directory

```

The path "/tmp/music/FLAC/Al Di Meola/Elegant Gypsy" does exist on the filesystem after the import is complete.



### Setup

* OS: Arch Linux
* Python version: 3.4.5
* beets version: 1.4.7
* Turning off plugins made problem go away (yes/no): This issue is related to a plugin, so I didn't turn them off

My configuration (output of `beet config`) is:

```yaml
plugins: inline convert badfiles info missing lastgenre fetchart mbsync scrub smartplaylist hook
directory: /tmp/music/FLAC
library: ~/.config/beets/library.db

import:
    copy: yes
    write: yes
    log: ~/.config/beets/import.log
    languages: en
per_disc_numbering: yes

paths:
    default: $albumartist/$album%aunique{}/$disc_and_track - $title
    comp: $albumartist/$album%aunique{}/$disc_and_track - $title
item_fields:
    disc_and_track: u'%01i-%02i' % (disc, track) if disctotal > 1 else u'%02i' % (track)

ui:
    color: yes

match:
    ignored: missing_tracks unmatched_tracks
convert:
    copy_album_art: yes
    dest: /tmp/music/ogg
    embed: yes
    never_convert_lossy_files: yes
    format: ogg
    formats:
        ogg:
            command: oggenc -Q -q 4 -o $dest $source
            extension: ogg
        aac:
            command: ffmpeg -i $source -y -vn -acodec aac -aq 1 $dest
            extension: m4a
        alac:
            command: ffmpeg -i $source -y -vn -acodec alac $dest
            extension: m4a
        flac: ffmpeg -i $source -y -vn -acodec flac $dest
        mp3: ffmpeg -i $source -y -vn -aq 2 $dest
        opus: ffmpeg -i $source -y -vn -acodec libopus -ab 96k $dest
        wma: ffmpeg -i $source -y -vn -acodec wmav2 -vn $dest
    pretend: no
    threads: 4
    max_bitrate: 500
    auto: no
    tmpdir:
    quiet: no

    paths: {}
    no_convert: ''
    album_art_maxwidth: 0
lastgenre:
    force: yes
    prefer_specific: no
    min_weight: 20
    count: 4
    separator: '; '
    whitelist: yes
    fallback:
    canonical: no
    source: album
    auto: yes
fetchart:
    sources: filesystem coverart amazon albumart
    auto: yes
    minwidth: 0
    maxwidth: 0
    enforce_ratio: no
    cautious: no
    cover_names:
    - cover
    - front
    - art
    - album
    - folder
    google_key: REDACTED
    google_engine: 001442825323518660753:hrh5ch1gjzm
    fanarttv_key: REDACTED
    store_source: no
hook:
    hooks: [{event: album_imported, command: '/usr/bin/ls -l "{album.path}"'}]
pathfields: {}
album_fields: {}
scrub:
    auto: yes
missing:
    count: no
    total: no
    album: no
smartplaylist:
    relative_to:
    playlist_dir: .
    auto: yes
    playlists: []
```

I created a Python 2 virtual environment, installed beets and any dependencies in to that virtualenv, cleaned my test library, and imported the same files using the same config.yaml.  This time the shell  was able to use the path value returned by the hook configuration:

```
hook: running command "/usr/bin/ls -l /tmp/music/FLAC/Al Di Meola/Elegant Gypsy" for event album_imported
total 254944
-rw-r--r-- 1 mike mike 50409756 Jun 24 13:46 01 - Flight Over Rio.flac
-rw-r--r-- 1 mike mike 43352354 Jun 24 13:46 02 - Midnight Tango.flac
-rw-r--r-- 1 mike mike  7726389 Jun 24 13:46 03 - Percussion Intro.flac
-rw-r--r-- 1 mike mike 32184646 Jun 24 13:46 04 - Mediterranean Sundance.flac
-rw-r--r-- 1 mike mike 45770796 Jun 24 13:46 05 - Race With Devil on Spanish Highway.flac
-rw-r--r-- 1 mike mike 10421006 Jun 24 13:46 06 - Lady of Rome, Sister of Brazil.flac
-rw-r--r-- 1 mike mike 65807504 Jun 24 13:46 07 - Elegant Gypsy Suite.flac
-rw-r--r-- 1 mike mike  5366515 Jun 24 13:46 cover.jpg
```
I'm guessing this is due to a data type difference between Python 2 and Python 3.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `beetsplug/hook.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 # This file is part of beets.
3 # Copyright 2015, Adrian Sampson.
4 #
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
12 #
13 # The above copyright notice and this permission notice shall be
14 # included in all copies or substantial portions of the Software.
15 
16 """Allows custom commands to be run when an event is emitted by beets"""
17 from __future__ import division, absolute_import, print_function
18 
19 import string
20 import subprocess
21 import six
22 
23 from beets.plugins import BeetsPlugin
24 from beets.util import shlex_split, arg_encoding
25 
26 
27 class CodingFormatter(string.Formatter):
28     """A variant of `string.Formatter` that converts everything to `unicode`
29     strings.
30 
31     This is necessary on Python 2, where formatting otherwise occurs on
32     bytestrings. It intercepts two points in the formatting process to decode
33     the format string and all fields using the specified encoding. If decoding
34     fails, the values are used as-is.
35     """
36 
37     def __init__(self, coding):
38         """Creates a new coding formatter with the provided coding."""
39         self._coding = coding
40 
41     def format(self, format_string, *args, **kwargs):
42         """Formats the provided string using the provided arguments and keyword
43         arguments.
44 
45         This method decodes the format string using the formatter's coding.
46 
47         See str.format and string.Formatter.format.
48         """
49         try:
50             format_string = format_string.decode(self._coding)
51         except UnicodeEncodeError:
52             pass
53 
54         return super(CodingFormatter, self).format(format_string, *args,
55                                                    **kwargs)
56 
57     def convert_field(self, value, conversion):
58         """Converts the provided value given a conversion type.
59 
60         This method decodes the converted value using the formatter's coding.
61 
62         See string.Formatter.convert_field.
63         """
64         converted = super(CodingFormatter, self).convert_field(value,
65                                                                conversion)
66 
67         if isinstance(converted, bytes):
68             return converted.decode(self._coding)
69 
70         return converted
71 
72 
73 class HookPlugin(BeetsPlugin):
74     """Allows custom commands to be run when an event is emitted by beets"""
75     def __init__(self):
76         super(HookPlugin, self).__init__()
77 
78         self.config.add({
79             'hooks': []
80         })
81 
82         hooks = self.config['hooks'].get(list)
83 
84         for hook_index in range(len(hooks)):
85             hook = self.config['hooks'][hook_index]
86 
87             hook_event = hook['event'].as_str()
88             hook_command = hook['command'].as_str()
89 
90             self.create_and_register_hook(hook_event, hook_command)
91 
92     def create_and_register_hook(self, event, command):
93         def hook_function(**kwargs):
94             if command is None or len(command) == 0:
95                 self._log.error('invalid command "{0}"', command)
96                 return
97 
98             # Use a string formatter that works on Unicode strings.
99             if six.PY2:
100                 formatter = CodingFormatter(arg_encoding())
101             else:
102                 formatter = string.Formatter()
103 
104             command_pieces = shlex_split(command)
105 
106             for i, piece in enumerate(command_pieces):
107                 command_pieces[i] = formatter.format(piece, event=event,
108                                                      **kwargs)
109 
110             self._log.debug(u'running command "{0}" for event {1}',
111                             u' '.join(command_pieces), event)
112 
113             try:
114                 subprocess.Popen(command_pieces).wait()
115             except OSError as exc:
116                 self._log.error(u'hook for {0} failed: {1}', event, exc)
117 
118         self.register_listener(event, hook_function)
119
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/beetsplug/hook.py b/beetsplug/hook.py
--- a/beetsplug/hook.py
+++ b/beetsplug/hook.py
@@ -18,7 +18,6 @@
 
 import string
 import subprocess
-import six
 
 from beets.plugins import BeetsPlugin
 from beets.util import shlex_split, arg_encoding
@@ -46,10 +45,8 @@
 
         See str.format and string.Formatter.format.
         """
-        try:
+        if isinstance(format_string, bytes):
             format_string = format_string.decode(self._coding)
-        except UnicodeEncodeError:
-            pass
 
         return super(CodingFormatter, self).format(format_string, *args,
                                                    **kwargs)
@@ -96,10 +93,7 @@
                 return
 
             # Use a string formatter that works on Unicode strings.
-            if six.PY2:
-                formatter = CodingFormatter(arg_encoding())
-            else:
-                formatter = string.Formatter()
+            formatter = CodingFormatter(arg_encoding())
 
             command_pieces = shlex_split(command)

{"golden_diff": "diff --git a/beetsplug/hook.py b/beetsplug/hook.py\n--- a/beetsplug/hook.py\n+++ b/beetsplug/hook.py\n@@ -18,7 +18,6 @@\n \n import string\n import subprocess\n-import six\n \n from beets.plugins import BeetsPlugin\n from beets.util import shlex_split, arg_encoding\n@@ -46,10 +45,8 @@\n \n         See str.format and string.Formatter.format.\n         \"\"\"\n-        try:\n+        if isinstance(format_string, bytes):\n             format_string = format_string.decode(self._coding)\n-        except UnicodeEncodeError:\n-            pass\n \n         return super(CodingFormatter, self).format(format_string, *args,\n                                                    **kwargs)\n@@ -96,10 +93,7 @@\n                 return\n \n             # Use a string formatter that works on Unicode strings.\n-            if six.PY2:\n-                formatter = CodingFormatter(arg_encoding())\n-            else:\n-                formatter = string.Formatter()\n+            formatter = CodingFormatter(arg_encoding())\n \n             command_pieces = shlex_split(command)\n", "issue": "hook: Interpolate paths (and other bytestrings) correctly into commands\n### Problem\r\n\r\nI have the following configuration for the Hook plugin in my config.yaml:\r\n\r\n```\r\nhook:\r\n    hooks:\r\n        - event: album_imported\r\n          command: /usr/bin/ls -l \"{album.path}\"\r\n```\r\n\r\nThis is just a test to see how beets presents the path values.  It appears that the paths are returned as bytes objects rather than strings.  This is problematic when using the path values as arguments for external shell scripts.  As can be seen below, the shell is unable to use the value provided by {album.path}.\r\n\r\n\r\n```sh\r\n$ beet -vv import /tmp/music/new/Al\\ Di\\ Meola\\ -\\ Elegant\\ Gypsy/\r\n```\r\n\r\nLed to this problem:\r\n\r\n```\r\nhook: running command \"/usr/bin/ls -l b'/tmp/music/FLAC/Al Di Meola/Elegant Gypsy'\" for event album_imported\r\n/usr/bin/ls: cannot access \"b'/tmp/music/FLAC/Al Di Meola/Elegant Gypsy'\": No such file or directory\r\n\r\n```\r\n\r\nThe path \"/tmp/music/FLAC/Al Di Meola/Elegant Gypsy\" does exist on the filesystem after the import is complete.\r\n\r\n\r\n\r\n### Setup\r\n\r\n* OS: Arch Linux\r\n* Python version: 3.4.5\r\n* beets version: 1.4.7\r\n* Turning off plugins made problem go away (yes/no): This issue is related to a plugin, so I didn't turn them off\r\n\r\nMy configuration (output of `beet config`) is:\r\n\r\n```yaml\r\nplugins: inline convert badfiles info missing lastgenre fetchart mbsync scrub smartplaylist hook\r\ndirectory: /tmp/music/FLAC\r\nlibrary: ~/.config/beets/library.db\r\n\r\nimport:\r\n    copy: yes\r\n    write: yes\r\n    log: ~/.config/beets/import.log\r\n    languages: en\r\nper_disc_numbering: yes\r\n\r\npaths:\r\n    default: $albumartist/$album%aunique{}/$disc_and_track - $title\r\n    comp: $albumartist/$album%aunique{}/$disc_and_track - $title\r\nitem_fields:\r\n    disc_and_track: u'%01i-%02i' % (disc, track) if disctotal > 1 else u'%02i' % (track)\r\n\r\nui:\r\n    color: yes\r\n\r\nmatch:\r\n    ignored: missing_tracks unmatched_tracks\r\nconvert:\r\n    copy_album_art: yes\r\n    dest: /tmp/music/ogg\r\n    embed: yes\r\n    never_convert_lossy_files: yes\r\n    format: ogg\r\n    formats:\r\n        ogg:\r\n            command: oggenc -Q -q 4 -o $dest $source\r\n            extension: ogg\r\n        aac:\r\n            command: ffmpeg -i $source -y -vn -acodec aac -aq 1 $dest\r\n            extension: m4a\r\n        alac:\r\n            command: ffmpeg -i $source -y -vn -acodec alac $dest\r\n            extension: m4a\r\n        flac: ffmpeg -i $source -y -vn -acodec flac $dest\r\n        mp3: ffmpeg -i $source -y -vn -aq 2 $dest\r\n        opus: ffmpeg -i $source -y -vn -acodec libopus -ab 96k $dest\r\n        wma: ffmpeg -i $source -y -vn -acodec wmav2 -vn $dest\r\n    pretend: no\r\n    threads: 4\r\n    max_bitrate: 500\r\n    auto: no\r\n    tmpdir:\r\n    quiet: no\r\n\r\n    paths: {}\r\n    no_convert: ''\r\n    album_art_maxwidth: 0\r\nlastgenre:\r\n    force: yes\r\n    prefer_specific: no\r\n    min_weight: 20\r\n    count: 4\r\n    separator: '; '\r\n    whitelist: yes\r\n    fallback:\r\n    canonical: no\r\n    source: album\r\n    auto: yes\r\nfetchart:\r\n    sources: filesystem coverart amazon albumart\r\n    auto: yes\r\n    minwidth: 0\r\n    maxwidth: 0\r\n    enforce_ratio: no\r\n    cautious: no\r\n    cover_names:\r\n    - cover\r\n    - front\r\n    - art\r\n    - album\r\n    - folder\r\n    google_key: REDACTED\r\n    google_engine: 001442825323518660753:hrh5ch1gjzm\r\n    fanarttv_key: REDACTED\r\n    store_source: no\r\nhook:\r\n    hooks: [{event: album_imported, command: '/usr/bin/ls -l \"{album.path}\"'}]\r\npathfields: {}\r\nalbum_fields: {}\r\nscrub:\r\n    auto: yes\r\nmissing:\r\n    count: no\r\n    total: no\r\n    album: no\r\nsmartplaylist:\r\n    relative_to:\r\n    playlist_dir: .\r\n    auto: yes\r\n    playlists: []\r\n```\r\n\r\nI created a Python 2 virtual environment, installed beets and any dependencies in to that virtualenv, cleaned my test library, and imported the same files using the same config.yaml.  This time the shell  was able to use the path value returned by the hook configuration:\r\n\r\n```\r\nhook: running command \"/usr/bin/ls -l /tmp/music/FLAC/Al Di Meola/Elegant Gypsy\" for event album_imported\r\ntotal 254944\r\n-rw-r--r-- 1 mike mike 50409756 Jun 24 13:46 01 - Flight Over Rio.flac\r\n-rw-r--r-- 1 mike mike 43352354 Jun 24 13:46 02 - Midnight Tango.flac\r\n-rw-r--r-- 1 mike mike  7726389 Jun 24 13:46 03 - Percussion Intro.flac\r\n-rw-r--r-- 1 mike mike 32184646 Jun 24 13:46 04 - Mediterranean Sundance.flac\r\n-rw-r--r-- 1 mike mike 45770796 Jun 24 13:46 05 - Race With Devil on Spanish Highway.flac\r\n-rw-r--r-- 1 mike mike 10421006 Jun 24 13:46 06 - Lady of Rome, Sister of Brazil.flac\r\n-rw-r--r-- 1 mike mike 65807504 Jun 24 13:46 07 - Elegant Gypsy Suite.flac\r\n-rw-r--r-- 1 mike mike  5366515 Jun 24 13:46 cover.jpg\r\n```\r\nI'm guessing this is due to a data type difference between Python 2 and Python 3.\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n# This file is part of beets.\n# Copyright 2015, Adrian Sampson.\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n\n\"\"\"Allows custom commands to be run when an event is emitted by beets\"\"\"\nfrom __future__ import division, absolute_import, print_function\n\nimport string\nimport subprocess\nimport six\n\nfrom beets.plugins import BeetsPlugin\nfrom beets.util import shlex_split, arg_encoding\n\n\nclass CodingFormatter(string.Formatter):\n    \"\"\"A variant of `string.Formatter` that converts everything to `unicode`\n    strings.\n\n    This is necessary on Python 2, where formatting otherwise occurs on\n    bytestrings. It intercepts two points in the formatting process to decode\n    the format string and all fields using the specified encoding. If decoding\n    fails, the values are used as-is.\n    \"\"\"\n\n    def __init__(self, coding):\n        \"\"\"Creates a new coding formatter with the provided coding.\"\"\"\n        self._coding = coding\n\n    def format(self, format_string, *args, **kwargs):\n        \"\"\"Formats the provided string using the provided arguments and keyword\n        arguments.\n\n        This method decodes the format string using the formatter's coding.\n\n        See str.format and string.Formatter.format.\n        \"\"\"\n        try:\n            format_string = format_string.decode(self._coding)\n        except UnicodeEncodeError:\n            pass\n\n        return super(CodingFormatter, self).format(format_string, *args,\n                                                   **kwargs)\n\n    def convert_field(self, value, conversion):\n        \"\"\"Converts the provided value given a conversion type.\n\n        This method decodes the converted value using the formatter's coding.\n\n        See string.Formatter.convert_field.\n        \"\"\"\n        converted = super(CodingFormatter, self).convert_field(value,\n                                                               conversion)\n\n        if isinstance(converted, bytes):\n            return converted.decode(self._coding)\n\n        return converted\n\n\nclass HookPlugin(BeetsPlugin):\n    \"\"\"Allows custom commands to be run when an event is emitted by beets\"\"\"\n    def __init__(self):\n        super(HookPlugin, self).__init__()\n\n        self.config.add({\n            'hooks': []\n        })\n\n        hooks = self.config['hooks'].get(list)\n\n        for hook_index in range(len(hooks)):\n            hook = self.config['hooks'][hook_index]\n\n            hook_event = hook['event'].as_str()\n            hook_command = hook['command'].as_str()\n\n            self.create_and_register_hook(hook_event, hook_command)\n\n    def create_and_register_hook(self, event, command):\n        def hook_function(**kwargs):\n            if command is None or len(command) == 0:\n                self._log.error('invalid command \"{0}\"', command)\n                return\n\n            # Use a string formatter that works on Unicode strings.\n            if six.PY2:\n                formatter = CodingFormatter(arg_encoding())\n            else:\n                formatter = string.Formatter()\n\n            command_pieces = shlex_split(command)\n\n            for i, piece in enumerate(command_pieces):\n                command_pieces[i] = formatter.format(piece, event=event,\n                                                     **kwargs)\n\n            self._log.debug(u'running command \"{0}\" for event {1}',\n                            u' '.join(command_pieces), event)\n\n            try:\n                subprocess.Popen(command_pieces).wait()\n            except OSError as exc:\n                self._log.error(u'hook for {0} failed: {1}', event, exc)\n\n        self.register_listener(event, hook_function)\n", "path": "beetsplug/hook.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n# This file is part of beets.\n# Copyright 2015, Adrian Sampson.\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n\n\"\"\"Allows custom commands to be run when an event is emitted by beets\"\"\"\nfrom __future__ import division, absolute_import, print_function\n\nimport string\nimport subprocess\n\nfrom beets.plugins import BeetsPlugin\nfrom beets.util import shlex_split, arg_encoding\n\n\nclass CodingFormatter(string.Formatter):\n    \"\"\"A variant of `string.Formatter` that converts everything to `unicode`\n    strings.\n\n    This is necessary on Python 2, where formatting otherwise occurs on\n    bytestrings. It intercepts two points in the formatting process to decode\n    the format string and all fields using the specified encoding. If decoding\n    fails, the values are used as-is.\n    \"\"\"\n\n    def __init__(self, coding):\n        \"\"\"Creates a new coding formatter with the provided coding.\"\"\"\n        self._coding = coding\n\n    def format(self, format_string, *args, **kwargs):\n        \"\"\"Formats the provided string using the provided arguments and keyword\n        arguments.\n\n        This method decodes the format string using the formatter's coding.\n\n        See str.format and string.Formatter.format.\n        \"\"\"\n        if isinstance(format_string, bytes):\n            format_string = format_string.decode(self._coding)\n\n        return super(CodingFormatter, self).format(format_string, *args,\n                                                   **kwargs)\n\n    def convert_field(self, value, conversion):\n        \"\"\"Converts the provided value given a conversion type.\n\n        This method decodes the converted value using the formatter's coding.\n\n        See string.Formatter.convert_field.\n        \"\"\"\n        converted = super(CodingFormatter, self).convert_field(value,\n                                                               conversion)\n\n        if isinstance(converted, bytes):\n            return converted.decode(self._coding)\n\n        return converted\n\n\nclass HookPlugin(BeetsPlugin):\n    \"\"\"Allows custom commands to be run when an event is emitted by beets\"\"\"\n    def __init__(self):\n        super(HookPlugin, self).__init__()\n\n        self.config.add({\n            'hooks': []\n        })\n\n        hooks = self.config['hooks'].get(list)\n\n        for hook_index in range(len(hooks)):\n            hook = self.config['hooks'][hook_index]\n\n            hook_event = hook['event'].as_str()\n            hook_command = hook['command'].as_str()\n\n            self.create_and_register_hook(hook_event, hook_command)\n\n    def create_and_register_hook(self, event, command):\n        def hook_function(**kwargs):\n            if command is None or len(command) == 0:\n                self._log.error('invalid command \"{0}\"', command)\n                return\n\n            # Use a string formatter that works on Unicode strings.\n            formatter = CodingFormatter(arg_encoding())\n\n            command_pieces = shlex_split(command)\n\n            for i, piece in enumerate(command_pieces):\n                command_pieces[i] = formatter.format(piece, event=event,\n                                                     **kwargs)\n\n            self._log.debug(u'running command \"{0}\" for event {1}',\n                            u' '.join(command_pieces), event)\n\n            try:\n                subprocess.Popen(command_pieces).wait()\n            except OSError as exc:\n                self._log.error(u'hook for {0} failed: {1}', event, exc)\n\n        self.register_listener(event, hook_function)\n", "path": "beetsplug/hook.py"}]}