def test_any_normalize(path): fsn = path2fsn(path) assert path2fsn(path) == fsn2norm(fsn) assert bytes2fsn(fsn2bytes(fsn, "utf-8"), "utf-8") == fsn2norm(fsn) if isinstance(path, text_type): assert text2fsn(path) == fsn2norm(text2fsn(path))
def test_surrogates(): if os.name == "nt": assert fsn2bytes(u"\ud83d", "utf-16-le") == b"=\xd8" assert bytes2fsn(b"\xd8=", "utf-16-be") == u"\ud83d" with pytest.raises(ValueError): bytes2fsn(b"\xd8=a", "utf-16-be") with pytest.raises(ValueError): bytes2fsn(b"=\xd8a", "utf-16-le") # for utf-16-le we have a workaround assert bytes2fsn(b"=\xd8", "utf-16-le") == u"\ud83d" assert bytes2fsn(b"=\xd8=\xd8", "utf-16-le") == u"\ud83d\ud83d" with pytest.raises(ValueError): bytes2fsn(b"=\xd8\x00\x00", "utf-16-le") # 4 byte code point assert fsn2bytes(u"\U0001f600", "utf-16-le") == b"=\xd8\x00\xde" assert bytes2fsn(b"=\xd8\x00\xde", "utf-16-le") == u"\U0001f600" # 4 byte codepoint + lone surrogate assert bytes2fsn(b"=\xd8\x00\xde=\xd8", "utf-16-le") == \ u"\U0001f600\ud83d" with pytest.raises(UnicodeDecodeError): bytes2fsn(b"a", "utf-16-le") assert fsn2bytes(u"\ud83d", "utf-8") == b"\xed\xa0\xbd" assert bytes2fsn(b"\xed\xa0\xbd", "utf-8") == u"\ud83d" assert fsnative(u"\ud83d") == u"\ud83d" assert fsn2text(u"\ud83d") == u"\ufffd" # at least don't fail... assert fsn2uri(u"C:\\\ud83d") == u"file:///C:/%ED%A0%BD" else: # this shouldn't fail and produce the same result on py2/3 at least. assert fsn2bytes(fsnative(u"\ud83d"), None) == b"\xed\xa0\xbd" text2fsn(fsn2text(fsnative(u"\ud83d"))) if PY2 and isunicodeencoding(): # under Python 2 we get surrogates, but we can't do anything about # it since most codecs don't treat that as an error assert fsn2text(fsnative(u"\ud83d")) == u"\ud83d" else: # under Python 3 the decoder don't allow surrogates assert fsn2text(fsnative(u"\ud83d")) == u"\ufffd\ufffd\ufffd"
def init(language=None): """Call this sometime at start before any register_translation() and before any gettext using libraries are loaded. Args: language (text_type or None): Either a language to use or None for the system derived default. """ global _initialized set_i18n_envvars() fixup_i18n_envvars() print_d("LANGUAGE: %r" % environ.get("LANGUAGE")) print_d("LANG: %r" % environ.get("LANG")) try: locale.setlocale(locale.LC_ALL, '') except locale.Error: pass # XXX: these are our most user facing APIs, make sre they are not loaded # before we set the language. For GLib this is too late.. assert "gi.repository.Gtk" not in sys.modules assert "gi.repository.Gst" not in sys.modules if language is not None: environ["LANGUAGE"] = text2fsn(language) print_d("LANGUAGE: %r" % environ.get("LANGUAGE")) _initialized = True
def test_any_filenames(path): if isinstance(path, fsnative): assert path2fsn(path) == fsn2norm(path) fsn = path2fsn(path) assert path2fsn(fsn) == fsn assert isinstance(fsn, fsnative) try: # never raises ValueError/TypError with open(fsn): pass except EnvironmentError: pass fsn2text(fsn).encode("utf-8") try: t = fsn2text(fsn, strict=True) except ValueError: pass else: assert fsn2norm(text2fsn(t)) == fsn2norm(fsn) data = fsn2bytes(fsn, "utf-8") assert fsn2bytes(bytes2fsn(data, "utf-8"), "utf-8") == data
def test_fsn2text_strict(): if os.name != "nt": path = bytes2fsn(b"\xff", None) else: path = u"\ud83d" if text2fsn(fsn2text(path)) != path: with pytest.raises(ValueError): fsn2text(path, strict=True)
def _dir_for(filelike): try: if isinstance(filelike, addinfourl): # if the "filelike" was created via urlopen # it is wrapped in an addinfourl object return os.path.dirname(path2fsn(filelike.fp.name)) else: return os.path.dirname(path2fsn(filelike.name)) except AttributeError: # Probably a URL return text2fsn(u'')
def ParsePLS(file): data = {} lines = file.read().decode('utf-8', 'replace').splitlines() if not lines or "[playlist]" not in lines.pop(0): return [] for line in lines: try: head, val = line.strip().split("=", 1) except (TypeError, ValueError): continue else: head = head.lower() if head.startswith("length") and val == "-1": continue else: data[head] = val count = 1 files = [] warnings = [] while True: if "file%d" % count in data: filename = text2fsn(data["file%d" % count]) if filename.lower()[-4:] in [".pls", ".m3u"]: warnings.append(filename) else: irf = IRFile(filename) for key in ["title", "genre", "artist"]: try: irf[key] = data["%s%d" % (key, count)] except KeyError: pass try: irf["~#length"] = int(data["length%d" % count]) except (KeyError, TypeError, ValueError): pass files.append(irf) else: break count += 1 if warnings: WarningMessage( None, _("Unsupported file type"), _("Station lists can only contain locations of stations, " "not other station lists or playlists. The following locations " "cannot be loaded:\n%s") % "\n ".join(map(util.escape, warnings)) ).run() return files
def __rename(self, library): model = self.view.get_model() win = WritingWindow(self, len(model)) win.show() was_changed = set() skip_all = False self.view.freeze_child_notify() for entry in model.itervalues(): song = entry.song new_name = entry.new_name old_name = entry.name if new_name is None: continue try: library.rename(song, text2fsn(new_name), changed=was_changed) except Exception: util.print_exc() if skip_all: continue RESPONSE_SKIP_ALL = 1 msg = qltk.Message( Gtk.MessageType.ERROR, win, _("Unable to rename file"), _("Renaming <b>%(old-name)s</b> to <b>%(new-name)s</b> " "failed. Possibly the target file already exists, " "or you do not have permission to make the " "new file or remove the old one.") % { "old-name": util.escape(old_name), "new-name": util.escape(new_name), }, buttons=Gtk.ButtonsType.NONE) msg.add_button(_("Ignore _All Errors"), RESPONSE_SKIP_ALL) msg.add_icon_button(_("_Stop"), Icons.PROCESS_STOP, Gtk.ResponseType.CANCEL) msg.add_button(_("_Continue"), Gtk.ResponseType.OK) msg.set_default_response(Gtk.ResponseType.OK) resp = msg.run() skip_all |= (resp == RESPONSE_SKIP_ALL) # Preserve old behavior: shift-click is Ignore All mods = Gdk.Display.get_default().get_pointer()[3] skip_all |= mods & Gdk.ModifierType.SHIFT_MASK library.reload(song, changed=was_changed) if resp != Gtk.ResponseType.OK and resp != RESPONSE_SKIP_ALL: break if win.step(): break self.view.thaw_child_notify() win.destroy() library.changed(was_changed) self.save.set_sensitive(False)
def __rename(self, library): model = self.view.get_model() win = WritingWindow(self, len(model)) win.show() was_changed = set() skip_all = False self.view.freeze_child_notify() for entry in itervalues(model): song = entry.song new_name = entry.new_name old_name = entry.name if new_name is None: continue try: library.rename(song, text2fsn(new_name), changed=was_changed) except Exception: util.print_exc() if skip_all: continue RESPONSE_SKIP_ALL = 1 msg = qltk.Message( Gtk.MessageType.ERROR, win, _("Unable to rename file"), _("Renaming <b>%(old-name)s</b> to <b>%(new-name)s</b> " "failed. Possibly the target file already exists, " "or you do not have permission to make the " "new file or remove the old one.") % { "old-name": util.escape(old_name), "new-name": util.escape(new_name), }, buttons=Gtk.ButtonsType.NONE) msg.add_button(_("Ignore _All Errors"), RESPONSE_SKIP_ALL) msg.add_icon_button(_("_Stop"), Icons.PROCESS_STOP, Gtk.ResponseType.CANCEL) msg.add_button(_("_Continue"), Gtk.ResponseType.OK) msg.set_default_response(Gtk.ResponseType.OK) resp = msg.run() skip_all |= (resp == RESPONSE_SKIP_ALL) # Preserve old behavior: shift-click is Ignore All mods = Gdk.Display.get_default().get_pointer()[3] skip_all |= mods & Gdk.ModifierType.SHIFT_MASK library.reload(song, changed=was_changed) if resp != Gtk.ResponseType.OK and resp != RESPONSE_SKIP_ALL: break if win.step(): break self.view.thaw_child_notify() win.destroy() library.changed(was_changed) self.save.set_sensitive(False)
def _print_playing(app, fstring=None): from quodlibet.formats import AudioFile from quodlibet.pattern import Pattern if fstring is None: fstring = u"<artist~album~tracknumber~title>" else: fstring = arg2text(fstring) song = app.player.info if song is None: song = AudioFile({"~filename": fsnative(u"/")}) song.sanitize() return text2fsn(Pattern(fstring).format(song) + u"\n")
def ParseM3U(fileobj): files = [] pending_title = None lines = fileobj.read().decode('utf-8', 'replace').splitlines() for line in lines: line = line.strip() if line.startswith("#EXTINF:"): try: pending_title = line.split(",", 1)[1] except IndexError: pending_title = None elif line.startswith("http"): irf = IRFile(text2fsn(line)) if pending_title: irf["title"] = pending_title pending_title = None files.append(irf) return files
def test_import(self): pl_name = u"_€3 œufs à Noël" pl = FileBackedPlaylist(_TEMP_DIR, pl_name, None) pl.extend(SONGS) pl.write() new_fn = os.path.splitext(text2fsn(pl.name))[0] + '.m3u' new_path = os.path.join(pl.dir, new_fn) os.rename(pl.filename, new_path) added = self.bar._import_playlists([new_path], self.lib) self.failUnlessEqual(added, 1, msg="Failed to add '%s'" % new_path) os.unlink(new_path) pls = self.bar.playlists() self.failUnlessEqual(len(pls), 3) # Leading underscore makes it always the last entry imported = pls[-1] self.failUnlessEqual(imported.name, pl_name) def fns(songs): return [song('~filename') for song in songs] self.failUnlessEqual(fns(imported.songs), fns(pl.songs))
def _status(app): player = app.player if player.paused: strings = ["paused"] else: strings = ["playing"] strings.append(type(app.browser).__name__) po = app.player_options strings.append("%0.3f" % player.volume) strings.append("shuffle" if po.shuffle else "inorder") strings.append("on" if po.repeat else "off") progress = 0 if player.info: length = player.info.get("~#length", 0) if length: progress = player.get_position() / (length * 1000.0) strings.append("%0.3f" % progress) status = u" ".join(strings) + u"\n" return text2fsn(status)
def _dump_browsers(app): response = u"" for i, b in enumerate(browsers.browsers): response += u"%d. %s\n" % (i, browsers.name(b)) return text2fsn(response)
# the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. import os import re from typing import Iterable, Generator, Optional import quodlibet from quodlibet import print_d, print_w from quodlibet.formats import AudioFile from quodlibet.library.base import Library from quodlibet.util.collection import (Playlist, XSPFBackedPlaylist, FileBackedPlaylist) from senf import text2fsn, _fsnative, fsn2text _DEFAULT_PLAYLIST_DIR = text2fsn( os.path.join(quodlibet.get_user_dir(), "playlists")) """Directory for playlist files""" HIDDEN_RE = re.compile(r'^\.\w[^.]*') """Hidden-like files, to ignored""" class PlaylistLibrary(Library[str, Playlist]): """A PlaylistLibrary listens to a SongLibrary, and keeps tracks of playlists of these songs. The library behaves like a dictionary: the keys are playlist names, the values are Playlist objects. """ def __init__(self, library: Library,
def _rename(self, library): model = self.view.get_model() win = WritingWindow(self, len(model)) win.show() was_changed = set() skip_all = self.__skip_interactive self.view.freeze_child_notify() should_move_art = config.getboolean("rename", "move_art") moveart_sets = {} remove_empty_dirs = config.getboolean("rename", "remove_empty_dirs") for entry in itervalues(model): if entry.new_name is None: continue song = entry.song old_name = entry.name old_pathfile = song['~filename'] new_name = entry.new_name new_pathfile = "" # ensure target is a full path if os.path.abspath(new_name) != \ os.path.abspath(os.path.join(os.getcwd(), new_name)): new_pathfile = new_name else: # must be a relative pattern, so prefix the path new_pathfile = \ os.path.join(os.path.dirname(old_pathfile), new_name) try: library.rename(song, text2fsn(new_name), changed=was_changed) except Exception: util.print_exc() if skip_all: continue RESPONSE_SKIP_ALL = 1 msg = qltk.Message( Gtk.MessageType.ERROR, win, _("Unable to rename file"), _("Renaming <b>%(old-name)s</b> to <b>%(new-name)s</b> " "failed. Possibly the target file already exists, " "or you do not have permission to make the " "new file or remove the old one.") % { "old-name": util.escape(old_name), "new-name": util.escape(new_name), }, buttons=Gtk.ButtonsType.NONE) msg.add_button(_("Ignore _All Errors"), RESPONSE_SKIP_ALL) msg.add_icon_button(_("_Stop"), Icons.PROCESS_STOP, Gtk.ResponseType.CANCEL) msg.add_button(_("_Continue"), Gtk.ResponseType.OK) msg.set_default_response(Gtk.ResponseType.OK) resp = msg.run() skip_all |= (resp == RESPONSE_SKIP_ALL) # Preserve old behavior: shift-click is Ignore All mods = Gdk.Display.get_default().get_pointer()[3] skip_all |= mods & Gdk.ModifierType.SHIFT_MASK library.reload(song, changed=was_changed) if resp != Gtk.ResponseType.OK and resp != RESPONSE_SKIP_ALL: break if should_move_art: self._moveart(moveart_sets, old_pathfile, new_pathfile, song) if remove_empty_dirs: path_old = os.path.dirname(old_pathfile) if not os.listdir(path_old): try: os.rmdir(path_old) print_d("Removed empty directory: %r" % path_old, self) except Exception: util.print_exc() if win.step(): break self.view.thaw_child_notify() win.destroy() library.changed(was_changed) self.save.set_sensitive(False)
def _dir_for(filelike): try: return os.path.dirname(path2fsn(filelike.name)) except AttributeError: # Probably a URL return text2fsn(u'')
def test_text2fsn(text): assert isinstance(text2fsn(text), fsnative)
def _print_query_text(app): if app.browser.can_filter_text(): return text2fsn(text_type(app.browser.get_filter_text()) + u"\n")
def fsencode(s): """Takes a `text_type` and returns a fsnative path""" return text2fsn(s)
def path_activate(entry, *args): path = text2fsn(entry.get_text()) if BansheeImport.USR_PATH != path: BansheeImport.USR_PATH = path
def test_text2fsn(): with pytest.raises(TypeError): text2fsn(b"foo") assert text2fsn(u"foo") == fsnative(u"foo")
def _dump_queue(app): window = app.window uris = [] for song in window.playlist.q.get(): uris.append(song("~uri")) return text2fsn(u"\n".join(uris) + u"\n")
def Library(self): lib = SongFileLibrary(watch_dirs=[text2fsn(str(self.temp_path))]) # Setup needs copools run_gtk_loop() return lib
def test_text_fsn_roudntrip(text): if u"\x00" in text: return assert isinstance(fsn2text(text2fsn(text)), text_type)