Repository: beetbox/beets

Issue:
On Python 3, rarfile<3.0 does not support bytes filename
### Problem

Importing  rar files does not work anymore. It used to work with beets `1.4.2` and below.

The underlying problem is that `is_rarfile` does no accept bytes (and `toppath` variable is byte). See this commit https://github.com/markokr/rarfile/commit/7d7a2cbf04cf4572c26e38ba18b5fe0b049cd850 where future versions (once or twice a year) of `rarfile` will gladly accepts bytes.

Running this command in verbose (`-vv`) mode:

```sh
beet -vv import /mnt/E/Album\ -\ group.rar
```

Led to this problem:

```
user configuration: /home/lompik/.config/beets/config.yaml
data directory: /home/lompik/.config/beets
plugin paths: 
Sending event: pluginload
library database: /home/lompik/.data/musiklibrary.blb
library directory: /mnt/music
Sending event: library_opened
Sending event: import_begin
Extracting archive: /mnt/E/Album - group.rar
extraction failed: Invalid object passed as file
No files imported from /mnt/E/Album - group.rar
Sending event: import
Sending event: cli_exit
```



### Setup

* OS: Linux
* Python version: 3.6
* beets version: 1.4.3  / rafile version: 3.0
* Turning off plugins made problem go away (yes/no): no


EDIT: change filename


Discussion:
In most cases we do pass string file names to rarfile, but we must have missed one :(
Thanks for pointing that out! It looks like we'll need to either (a) hope for a speedy release of the new version of rarfile, or (b) add an adapter to `ArchiveImportTask` that optimistically decodes the path using the filesystem encoding (and quietly fails, just logging an error, if it can't be decoded).
@sampsyo shouldn't `util.py3_path` work ?
Hmm, you're right that it should… sorry I didn't think of that!

But we already pass the archive path through `py3_path`, so I'm a little bit mystified: https://github.com/beetbox/beets/blob/8ccdb1c77e2becc851e2996b182eecedf40c16ba/beets/importer.py#L990
I wonder which line `extraction failed: Invalid object passed as file` is actually coming from.
@jrobeson it is coming from here: https://github.com/beetbox/beets/blob/8ccdb1c77e2becc851e2996b182eecedf40c16ba/beets/importer.py#L1158. The `extract` method raises an exeption when the `is_rarfile` method is called with a `bytes` argument. Maybe we could use `py3_path` before calling this method ?

Actually, I misrepresented the file name in my example command. Sorry, I will edit the original ! What I ran beets on was : 

```sh
beet -vv import /mnt/E/group\ -\ album.rar
```
Here's the thing, though, @Lompik: that `extract` method already uses `py3_path` (see the link in my previous comment). So it seems like we're already passing a Unicode path on Python 3. Some assumption must be wrong here…
@sampsyo The exeption is raised at line https://github.com/beetbox/beets/blob/8ccdb1c77e2becc851e2996b182eecedf40c16ba/beets/importer.py#L985 which is executed before the line you pointed (990). Indeed, in case of a rar file , `path_test`=`is_rarfile`. I guess `py3_path` should be used on `self.toppath` before calling `path_test`. I can create a pull request if it's not clear.
That would it explain it; thanks!

Would you mind opening a PR if it's easy? That will run the CI on the change to make sure it doesn't break the other archiving backends.

Resolving patch:
diff --git a/beets/importer.py b/beets/importer.py
--- a/beets/importer.py
+++ b/beets/importer.py
@@ -988,7 +988,7 @@ def extract(self):
         `toppath` to that directory.
         """
         for path_test, handler_class in self.handlers():
-            if path_test(self.toppath):
+            if path_test(util.py3_path(self.toppath)):
                 break
 
         try:


Test patch: