From b407fbd478a84dcd6de31460a707dea30d21915f Mon Sep 17 00:00:00 2001 From: Jonathan Reichelt Gjertsen Date: Fri, 23 Jul 2021 06:29:32 +0200 Subject: [PATCH] Support passing in a pathlib.Path by converting to str (V2) (#74) * Support passing in a pathlib.Path by converting to str * Run all tests with pathlib.Path inputs as well as strings * Ensure input is str in _playsoundAnotherPython * Do not attempt conversion to str on Python 2, in which str is a byte string Co-authored-by: TaylorSMarks --- playsound.py | 22 +++++++++++++++++++++- test.py | 22 ++++++++++++++++++---- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/playsound.py b/playsound.py index eacebfb..84a7f7a 100644 --- a/playsound.py +++ b/playsound.py @@ -4,6 +4,18 @@ class PlaysoundException(Exception): pass +def _canonicalizePath(path): + """ + Support passing in a pathlib.Path-like object by converting to str. + """ + import sys + if sys.version_info[0] >= 3: + return str(path) + else: + # On earlier Python versions, str is a byte string, so attempting to + # convert a unicode string to str will fail. Leave it alone in this case. + return path + def _playsoundWin(sound, block = True): ''' Utilizes windll.winmm. Tested and known to work with MP3 and WAVE on @@ -16,6 +28,8 @@ def _playsoundWin(sound, block = True): I never would have tried using windll.winmm without seeing his code. ''' + sound = _canonicalizePath(sound) + if any((c in sound for c in ' "\'()')): from os import close, remove from os.path import splitext @@ -67,6 +81,8 @@ def winCommand(*command): pass def _handlePathOSX(sound): + sound = _canonicalizePath(sound) + if '://' not in sound: if not sound.startswith('/'): from os import getcwd @@ -133,6 +149,8 @@ def _playsoundNix(sound, block = True): Inspired by this: https://gstreamer.freedesktop.org/documentation/tutorials/playback/playbin-usage.html """ + sound = _canonicalizePath(sound) + # pathname2url escapes non-URL-safe characters from os.path import abspath, exists try: @@ -184,6 +202,8 @@ def _playsoundAnotherPython(otherPython, sound, block = True, macOS = False): from subprocess import check_call from threading import Thread + sound = _canonicalizePath(sound) + class PropogatingThread(Thread): def run(self): self.exc = None @@ -238,4 +258,4 @@ def join(self, timeout = None): if __name__ == '__main__': # block is always True if you choose to run this from the command line. from sys import argv - playsound(argv[1]) \ No newline at end of file + playsound(argv[1]) diff --git a/test.py b/test.py index 65b571a..f840ea0 100644 --- a/test.py +++ b/test.py @@ -60,10 +60,14 @@ def mockMciSendStringW(command, buf, bufLen, bufStart): return 0 class PlaysoundTests(unittest.TestCase): - def helper(self, file, approximateDuration, block = True): - startTime = time() + def get_full_path(self, file): path = join('test_media', file) print(path.encode('utf-8')) + return path + + def helper(self, file, approximateDuration, block = True): + startTime = time() + path = self.get_full_path(file) if isTravis and system == 'Windows': with patch('ctypes.windll.winmm.mciSendStringW', side_effect = mockMciSendStringW): @@ -86,16 +90,26 @@ def helper(self, file, approximateDuration, block = True): def testMissing(self): with self.assertRaises(PlaysoundException) as context: - playsound('fakefile.wav') + playsound(self.get_full_path('fakefile.wav')) message = str(context.exception).lower() for sub in ['not', 'fakefile.wav']: self.assertIn(sub, message, '"{}" was expected in the exception message, but instead got: "{}"'.format(sub, message)) +# Run the same tests as above, but pass pathlib.Path objects to playsound instead of strings. +try: + from pathlib import Path +except ImportError: + pass +else: + class PlaysoundTestsWithPathlib(PlaysoundTests): + def get_full_path(self, file): + return Path('test_media') / file + if __name__ == '__main__': print(version) import sys print(sys.executable) print(sys.path) - unittest.main() \ No newline at end of file + unittest.main()