From 58bc1c7b1dbbf9ba03ad8230cb84f416dc742639 Mon Sep 17 00:00:00 2001 From: Levi Cameron Date: Thu, 7 Sep 2017 18:10:24 +1000 Subject: [PATCH 1/3] Pull int_types and legal_arg_types up to the module level so that they are only initialized once. --- unipath/abstractpath.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/unipath/abstractpath.py b/unipath/abstractpath.py index eecb114..2757b36 100644 --- a/unipath/abstractpath.py +++ b/unipath/abstractpath.py @@ -15,6 +15,16 @@ except NameError: pass +try: + int_types = (int, long) +except NameError: # We are in Python 3 + int_types = int + +try: + legal_arg_types = (basestring, list, int, long) +except NameError: # Python 3 doesn't have basestring nor long + legal_arg_types = (str, list, int) + class AbstractPath(_base): """An object-oriented approach to os.path functions.""" pathlib = os.path @@ -55,19 +65,11 @@ def _new_helper(class_, args): if len(args) == 1 and isinstance(args[0], class_) and \ args[0].pathlib == pathlib: return args[0] - try: - legal_arg_types = (class_, basestring, list, int, long) - except NameError: # Python 3 doesn't have basestring nor long - legal_arg_types = (class_, str, list, int) args = list(args) for i, arg in enumerate(args): - if not isinstance(arg, legal_arg_types): + if not isinstance(arg, (AbstractPath,) + legal_arg_types): m = "arguments must be str, unicode, list, int, long, or %s" raise TypeError(m % class_.__name__) - try: - int_types = (int, long) - except NameError: # We are in Python 3 - int_types = int if isinstance(arg, int_types): args[i] = str(arg) elif isinstance(arg, class_) and arg.pathlib != pathlib: From 23374080777f05a21c773a9b12264a348f57b4bd Mon Sep 17 00:00:00 2001 From: Levi Cameron Date: Thu, 7 Sep 2017 18:10:51 +1000 Subject: [PATCH 2/3] Add support for PEP519 --- README.rst | 2 +- test.py | 26 +++++++++++++++++++++++++- tox.ini | 2 +- unipath/abstractpath.py | 9 +++++++++ 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index dca4cc6..c51d9c2 100644 --- a/README.rst +++ b/README.rst @@ -122,7 +122,7 @@ Constructor ``Path`` (and ``AbstractPath``) objects can be created from a string path, or from several string arguments which are joined together a la ``os.path.join``. Each argument can be a string, an ``(Abstract)Path`` instance, an int or long, -or a list/tuple of strings to be joined:: +a list/tuple of strings to be joined, or (Python 3.6+) a ``PathLike`` object:: p = Path("foo/bar.py") # A relative path p = Path("foo", "bar.py") # Same as previous diff --git a/test.py b/test.py index 89039c0..d8480b0 100755 --- a/test.py +++ b/test.py @@ -622,5 +622,29 @@ def test_read_file(self): # .write_file and .rmtree tested in .setUp. +try: + from os import PathLike as _ + pep519_present = True +except ImportError: + pep519_present = False - + +@pytest.mark.skipif(not pep519_present, reason='PEP519 support not found') +class TestPEP519(FilesystemTest): + # If PEP519 support is not present then interactions with other path-like objects are left undefined (will probably fail) + + def test_pathlib(self): + import pathlib + pure_path = Path('a', 'b', Path('c'), Path('d')) + mixed_path = Path('a', pathlib.Path('b'), 'c', pathlib.Path('d')) + assert pure_path == mixed_path + + def test_DirEntry(self): + d = self.d + with os.scandir(self.d) as it: + for entry in it: + pure_path = Path(self.d, entry.name, '..').resolve() + mixed_path = Path(entry, '..').resolve() + assert pure_path == mixed_path + return + assert False, "scandir() yielded an empty directory" diff --git a/tox.ini b/tox.ini index be2fa36..606216e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27, py34 +envlist = py27, py34, py36 [testenv] deps=pytest diff --git a/unipath/abstractpath.py b/unipath/abstractpath.py index 2757b36..6cda811 100644 --- a/unipath/abstractpath.py +++ b/unipath/abstractpath.py @@ -25,6 +25,12 @@ except NameError: # Python 3 doesn't have basestring nor long legal_arg_types = (str, list, int) +try: + from os import PathLike + legal_arg_types += (PathLike,) +except ImportError: + pass + class AbstractPath(_base): """An object-oriented approach to os.path functions.""" pathlib = os.path @@ -54,6 +60,9 @@ def __add__(self, more): if resultStr is NotImplemented: return resultStr return self.__class__(resultStr) + + def __fspath__(self): + return _base(self) @classmethod def _new_helper(class_, args): From a3f3e847ce018481eda658ade195b3dc0da315aa Mon Sep 17 00:00:00 2001 From: Levi Cameron Date: Fri, 8 Sep 2017 11:04:16 +1000 Subject: [PATCH 3/3] Make exception message more accurate --- unipath/abstractpath.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unipath/abstractpath.py b/unipath/abstractpath.py index 6cda811..3c8ebd7 100644 --- a/unipath/abstractpath.py +++ b/unipath/abstractpath.py @@ -77,7 +77,7 @@ def _new_helper(class_, args): args = list(args) for i, arg in enumerate(args): if not isinstance(arg, (AbstractPath,) + legal_arg_types): - m = "arguments must be str, unicode, list, int, long, or %s" + m = "arguments must be str, unicode, list, int, long, os.PathLike, or %s" raise TypeError(m % class_.__name__) if isinstance(arg, int_types): args[i] = str(arg)