path encoding now works with slashes better

--HG--
extra : convert_revision : svn%3A41726ec3-264d-0410-9c23-a9f1637257cc/trunk%40199
This commit is contained in:
adrian.sampson 2009-04-11 02:51:26 +00:00
parent 597694b58b
commit 0b309edd27
2 changed files with 97 additions and 24 deletions

View file

@ -140,35 +140,27 @@ def seq_to_path(seq):
"""
out = []
for s in seq:
out.append(s.replace('\\', '\\\\').replace('/', '\\/'))
out.append(s.replace('\\', '\\\\') # preserve backslashes
.replace('_', '\\_') # preserve _s
.replace('/', '_') # hide /s as _s
)
return '/'.join(out)
path_to_list_pattern = re.compile(r'(?:\\/|[^/])*')
def path_to_list(path):
"""Takes a path-like string (probably encoded by seq_to_path) and
returns the list of strings it represents.
"""
# To simplify parsing, ensure that everything is terminated by a
# slash. Note that seq_to_path never added a trailing slash, so
# they are "disallowed" by this encoding.
path += '/'
out = []
while path:
# Search for one path component.
m = path_to_list_pattern.match(path)
if not m: break # Should never happen.
component = m.group(0)
# Chop off this component and the / that follows it.
path = path[len(component):]
if len(path) >= 1 and path[0] == '/': # Should always be true.
path = path[1:]
out.append(component.replace('\\/', '/').replace('\\\\', '\\'))
return out
def repl(m):
# This function maps "escaped" characters to original
# characters. Because the regex is in the right order, the
# sequences are replaced top-to-bottom.
return {'\\\\': '\\',
'\\_': '_',
'_': '/',
}[m.group(0)]
return [re.sub(r'\\\\|\\_|_', repl, component)
for component in path.split('/')]
# Generic server infrastructure, implementing the basic protocol.
@ -792,7 +784,7 @@ class Server(BaseServer):
if not artist: # List all artists.
for artist in self.lib.artists():
conn.send('directory: ' + artist)
conn.send('directory: ' + seq_to_path((artist,)))
elif not album: # List all albums for an artist.
for album in self.lib.albums(artist):
conn.send('directory: ' + seq_to_path(album))

81
test/test_player.py Normal file
View file

@ -0,0 +1,81 @@
# This file is part of beets.
# Copyright 2009, Adrian Sampson.
#
# Beets is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Beets is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with beets. If not, see <http://www.gnu.org/licenses/>.
"""Tests for BPD and music playing.
"""
import unittest
import sys
sys.path.append('..')
from beets.player import bpd
class FauxPathTest(unittest.TestCase):
# The current encoding actually cannot distinguish between ['']
# and []. This doesn't cause a bug because we never use empty
# sequences, but it might be nice to fix someday.
#def test_empty_seq_preserved(self):
# seq = []
# self.assertEqual(bpd.path_to_list(bpd.seq_to_path(seq)), seq)
def test_single_element_preserved(self):
seq = ['hello']
self.assertEqual(bpd.path_to_list(bpd.seq_to_path(seq)), seq)
def test_multiple_elements_preserved(self):
seq = ['hello', 'there', 'how', 'are', 'you']
self.assertEqual(bpd.path_to_list(bpd.seq_to_path(seq)), seq)
def test_spaces_preserved(self):
seq = ['hel lo', 'what', 'is up']
self.assertEqual(bpd.path_to_list(bpd.seq_to_path(seq)), seq)
def test_empty_string_preserved_in_middle(self):
seq = ['hello', '', 'sup']
self.assertEqual(bpd.path_to_list(bpd.seq_to_path(seq)), seq)
def test_empty_strings_preserved_on_ends(self):
seq = ['', 'whatever', '']
self.assertEqual(bpd.path_to_list(bpd.seq_to_path(seq)), seq)
def test_empty_strings_only(self):
seq = ['', '', '']
self.assertEqual(bpd.path_to_list(bpd.seq_to_path(seq)), seq)
def test_slashes_preserved(self):
seq = ['hel/lo', 'what', 'is', 'up']
self.assertEqual(bpd.path_to_list(bpd.seq_to_path(seq)), seq)
def test_backslashes_preserved(self):
seq = ['hel\\lo', 'what', 'is', 'up']
self.assertEqual(bpd.path_to_list(bpd.seq_to_path(seq)), seq)
def test_unicode_preserved(self):
seq = [u'hello', u'what \x99 is', u'up']
self.assertEqual(bpd.path_to_list(bpd.seq_to_path(seq)), seq)
def test_slashes_not_encoded_as_slashes(self):
no_slashes = bpd.seq_to_path(['goodday', 'sir'])
with_slashes = bpd.seq_to_path(['good/day', 'sir'])
self.assertEqual(no_slashes.count('/'), with_slashes.count('/'))
def suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == '__main__':
unittest.main(defaultTest='suite')