Repository: jmcgeheeiv/pyfakefs
Pull request #422 (2018-08-07 08:26:40)

Add support for pathlib2 (#408)

matthew16550: Yep that fixed `TestCopyOrAddRealFile`thanks.

I've made all the changes you suggested so the only thing left is a final decision on `all_tests_without_extra_packages.py`.
mrbean-bremen: Looks fine to me - thanks a lot! @jmcgeheeiv will have a seconf review and handle the merge.

Patch:
diff --git a/.travis/install.sh b/.travis/install.sh
index 0afbce0..e62c01e 100755
--- a/.travis/install.sh
+++ b/.travis/install.sh
@@ -45,4 +45,5 @@ if [[ $TRAVIS_OS_NAME == 'osx' ]]; then
 fi
 
 pip install -r requirements.txt
+pip install -r extra_requirements.txt
 pip install .
diff --git a/appveyor.yml b/appveyor.yml
index 4ca57fe..c790992 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -8,12 +8,14 @@ environment:
 
 install:
   - "%PYTHON%\\python.exe -m pip install -r requirements.txt"
+  - "%PYTHON%\\python.exe -m pip install -r extra_requirements.txt"
   - "%PYTHON%\\python.exe -m pip install ."
 
 build: off
 
 test_script:
   - "%PYTHON%\\python.exe -m pyfakefs.tests.all_tests"
+  - "%PYTHON%\\python.exe -m pyfakefs.tests.all_tests_without_extra_packages"
   - "%PYTHON%\\python.exe -m pytest pyfakefs\\tests\\pytest\\pytest_plugin_test.py"
   - IF "C:\\Python36-x64"=="%PYTHON%" "%PYTHON%\\python.exe -m pytest pyfakefs\\tests\\pytest\\pytest_fixture_test.py"
   - "%PYTHON%\\python.exe -m pytest pyfakefs\\tests\\pytest\\pytest_plugin_failing_test.py > testresult.txt | echo."
diff --git a/extra_requirements.txt b/extra_requirements.txt
new file mode 100644
index 0000000..9352552
--- /dev/null
+++ b/extra_requirements.txt
@@ -0,0 +1,13 @@
+# "pathlib2" and "scandir" are backports of new standard modules,  pyfakefs will
+# use them if available when running on older Python versions.
+#
+# They are dependencies of pytest when Python < 3.6 so we sometimes get them via
+# requirements.txt, this file makes them explicit dependencies for testing &
+# development.
+#
+# Older versions might work ok, the versions chosen here are just the latest
+# available at the time of writing.
+
+pathlib2>=2.3.2
+
+scandir>=1.8
diff --git a/pyfakefs/extra_packages.py b/pyfakefs/extra_packages.py
new file mode 100644
index 0000000..c926109
--- /dev/null
+++ b/pyfakefs/extra_packages.py
@@ -0,0 +1,24 @@
+try:
+    import pathlib
+    pathlib2 = None
+except ImportError:
+    try:
+        import pathlib2
+        pathlib = pathlib2
+    except ImportError:
+        pathlib = None
+        pathlib2 = None
+
+
+try:
+    from os import scandir
+    use_scandir = True
+    use_scandir_package = False
+except ImportError:
+    try:
+        import scandir
+        use_scandir = True
+        use_scandir_package = True
+    except ImportError:
+        use_scandir = False
+        use_scandir_package = False
diff --git a/pyfakefs/fake_filesystem.py b/pyfakefs/fake_filesystem.py
index 4eebbbd..55c1375 100644
--- a/pyfakefs/fake_filesystem.py
+++ b/pyfakefs/fake_filesystem.py
@@ -107,9 +107,10 @@ from stat import S_IFREG, S_IFDIR, S_ISLNK, S_IFMT, S_ISDIR, S_IFLNK, S_ISREG
 
 from pyfakefs.deprecator import Deprecator
 from pyfakefs.fake_scandir import scandir, walk
+from pyfakefs.extra_packages import use_scandir
 from pyfakefs.helpers import FakeStatResult, FileBufferIO, IS_PY2, NullFileBufferIO
 from pyfakefs.helpers import is_int_type, is_byte_string, is_unicode_string
-from pyfakefs.helpers import make_string_path
+from pyfakefs.helpers import make_string_path, text_type
 
 __pychecker__ = 'no-reimportself'
 
@@ -122,11 +123,6 @@ PERM_DEF = 0o777  # Default permission bits.
 PERM_DEF_FILE = 0o666  # Default permission bits (regular file)
 PERM_ALL = 0o7777  # All permission bits.
 
-try:
-    text_type = unicode  # Python 2
-except NameError:
-    text_type = str      # Python 3
-
 _OpenModes = namedtuple(
     'open_modes',
     'must_exist can_read can_write truncate append must_not_exist'
@@ -3693,7 +3689,7 @@ class FakeOsModule(object):
             used by shutil."""
             return []
 
-    if sys.version_info >= (3, 5):
+    if use_scandir:
         def scandir(self, path=''):
             """Return an iterator of DirEntry objects corresponding to the
             entries in the directory given by path.
diff --git a/pyfakefs/fake_pathlib.py b/pyfakefs/fake_pathlib.py
index b82861c..d3631e3 100644
--- a/pyfakefs/fake_pathlib.py
+++ b/pyfakefs/fake_pathlib.py
@@ -30,8 +30,11 @@ get the properties of the underlying fake filesystem.
 """
 
 import os
-import pathlib
-from urllib.parse import quote_from_bytes as urlquote_from_bytes
+
+try:
+    from urllib.parse import quote_from_bytes as urlquote_from_bytes
+except ImportError:
+    from urllib import quote as urlquote_from_bytes
 
 import sys
 
@@ -40,6 +43,8 @@ import functools
 import errno
 
 from pyfakefs import fake_scandir
+from pyfakefs.extra_packages import use_scandir, pathlib, pathlib2
+from pyfakefs.helpers import text_type
 from pyfakefs.fake_filesystem import FakeFileOpen, FakeFilesystem
 
 
@@ -91,7 +96,7 @@ class _FakeAccessor(pathlib._Accessor):  # pylint: disable=protected-access
 
     chmod = _wrap_strfunc(FakeFilesystem.chmod)
 
-    if sys.version_info >= (3, 6):
+    if use_scandir:
         scandir = _wrap_strfunc(fake_scandir.scandir)
 
     if hasattr(os, "lchmod"):
@@ -280,13 +285,13 @@ class _FakeFlavour(pathlib._Flavour):
         previous_s = None
         if strict:
             if not self.filesystem.exists(path):
-                raise FileNotFoundError(path)
+                self.filesystem.raise_os_error(errno.ENOENT, path)
             return self.filesystem.resolve_path(path)
         else:
             while True:
                 try:
                     path = self.filesystem.resolve_path(path)
-                except FileNotFoundError:
+                except IOError:
                     previous_s = path
                     path = self.filesystem.splitpath(path)[0]
                 else:
@@ -472,11 +477,14 @@ class FakePath(pathlib.Path):
         Raises:
             IOError: if the path doesn't exist (strict=True or Python < 3.6)
         """
-        if strict is None:
-            strict = sys.version_info < (3, 6)
-        elif sys.version_info < (3, 6):
-            raise TypeError(
-                "resolve() got an unexpected keyword argument 'strict'")
+        if sys.version_info >= (3, 6) or pathlib2:
+            if strict is None:
+                strict = False
+        else:
+            if strict is not None:
+                raise TypeError(
+                    "resolve() got an unexpected keyword argument 'strict'")
+            strict = True
         if self._closed:
             self._raise_closed()
         path = self._flavour.resolve(self, strict=strict)
@@ -496,10 +504,10 @@ class FakePath(pathlib.Path):
         """
         if self._closed:
             self._raise_closed()
-        return FakeFileOpen(self.filesystem)(
+        return FakeFileOpen(self.filesystem, use_io=True)(
             self._path(), mode, buffering, encoding, errors, newline)
 
-    if sys.version_info >= (3, 5):
+    if sys.version_info >= (3, 5) or pathlib2:
         def read_bytes(self):
             """Open the fake file in bytes mode, read it, and close the file.
 
@@ -514,7 +522,7 @@ class FakePath(pathlib.Path):
             """
             Open the fake file in text mode, read it, and close the file.
             """
-            with FakeFileOpen(self.filesystem)(self._path(), mode='r',
+            with FakeFileOpen(self.filesystem, use_io=True)(self._path(), mode='r',
                                                encoding=encoding,
                                                errors=errors) as f:
                 return f.read()
@@ -542,14 +550,15 @@ class FakePath(pathlib.Path):
                     default locale encoding is used
                 errors: ignored
             Raises:
-                TypeError: if data is not of type 'str'
+                TypeError: if data is not of type 'str' (Python 3) or 'unicode'
+                    (Python 2)
                 IOError: if the target object is a directory, the path is
                     invalid or permission is denied.
             """
-            if not isinstance(data, str):
+            if not isinstance(data, text_type):
                 raise TypeError('data must be str, not %s' %
                                 data.__class__.__name__)
-            with FakeFileOpen(self.filesystem)(self._path(), mode='w',
+            with FakeFileOpen(self.filesystem, use_io=True)(self._path(), mode='w',
                                                encoding=encoding,
                                                errors=errors) as f:
                 return f.write(data)
@@ -599,7 +608,9 @@ class FakePath(pathlib.Path):
                 happens, otherwise FileExistError is raised
 
         Raises:
-            FileExistsError if the file exists and exits_ok is False.
+            OSError: (Python 2 only) if the file exists and exits_ok is False.
+            FileExistsError: (Python 3 only) if the file exists and exits_ok is
+                False.
         """
         if self._closed:
             self._raise_closed()
@@ -607,7 +618,7 @@ class FakePath(pathlib.Path):
             if exist_ok:
                 self.filesystem.utime(self._path(), None)
             else:
-                raise FileExistsError
+                self.filesystem.raise_os_error(errno.EEXIST, self._path())
         else:
             fake_file = self.open('w')
             fake_file.close()
diff --git a/pyfakefs/fake_scandir.py b/pyfakefs/fake_scandir.py
index afb1624..f00463d 100644
--- a/pyfakefs/fake_scandir.py
+++ b/pyfakefs/fake_scandir.py
@@ -18,6 +18,8 @@ package.
 """
 import sys
 
+from pyfakefs.extra_packages import use_scandir_package
+
 
 class DirEntry(object):
     """Emulates os.DirEntry. Note that we did not enforce keyword only
@@ -107,7 +109,8 @@ class ScanDirIter:
     def __init__(self, filesystem, path):
         self.filesystem = filesystem
         if isinstance(path, int):
-            if sys.version_info < (3, 7) or self.filesystem.is_windows_fs:
+            if not use_scandir_package and (
+                    sys.version_info < (3, 7) or self.filesystem.is_windows_fs):
                 raise NotImplementedError(
                     'scandir does not support file descriptor '
                     'path argument')
diff --git a/pyfakefs/helpers.py b/pyfakefs/helpers.py
index 1228daf..dd490a6 100644
--- a/pyfakefs/helpers.py
+++ b/pyfakefs/helpers.py
@@ -22,6 +22,12 @@ import os
 IS_PY2 = sys.version_info[0] < 3
 
 
+try:
+    text_type = unicode  # Python 2
+except NameError:
+    text_type = str      # Python 3
+
+
 def is_int_type(val):
     """Return True if `val` is of integer type."""
     try:               # Python 2
@@ -35,7 +41,7 @@ def is_byte_string(val):
     string."""
     if not IS_PY2:
         return not hasattr(val, 'encode')
-    return isinstance(val, str)
+    return isinstance(val, (memoryview, str))
 
 
 def is_unicode_string(val):
diff --git a/tox.ini b/tox.ini
index b1a3f0a..0859109 100644
--- a/tox.ini
+++ b/tox.ini
@@ -2,8 +2,11 @@
 envlist=py27,py33,py34,py35,py36,py37,pypy
 
 [testenv]
-deps = -rrequirements.txt
+deps =
+    -rrequirements.txt
+    -rextra_requirements.txt
 passenv = HOME USERPROFILE
 commands=
     python -m pyfakefs.tests.all_tests
-    python -m pytest pyfakefs/tests/pytest_plugin_test.py
+    python -m pyfakefs.tests.all_tests_without_extra_packages
+    python -m pytest pyfakefs/tests/pytest/pytest_plugin_test.py


Test patch:
diff --git a/.travis/run_tests.sh b/.travis/run_tests.sh
index 07b4328..ebb23dc 100755
--- a/.travis/run_tests.sh
+++ b/.travis/run_tests.sh
@@ -18,3 +18,4 @@ flake8 . $EXCLUDES --count --select=E901,E999,F821,F822,F823 --show-source --sta
 # exit-zero treats all errors as warnings
 flake8 . $EXCLUDES --count --exit-zero --max-complexity=12 --statistics
 python -m pyfakefs.tests.all_tests
+python -m pyfakefs.tests.all_tests_without_extra_packages
diff --git a/pyfakefs/fake_filesystem_unittest.py b/pyfakefs/fake_filesystem_unittest.py
index 6b3d958..0fe032c 100644
--- a/pyfakefs/fake_filesystem_unittest.py
+++ b/pyfakefs/fake_filesystem_unittest.py
@@ -63,17 +63,13 @@ except ImportError:
 from pyfakefs import fake_filesystem
 from pyfakefs import fake_filesystem_shutil
 from pyfakefs import mox3_stubout
+from pyfakefs.extra_packages import pathlib, use_scandir
 
-if sys.version_info >= (3, 4):
+if pathlib:
     from pyfakefs import fake_pathlib
 
-
-try:
-    import scandir  # noqa: F401 import used to set has_scandir
-    import fake_scandir
-    has_scandir = True
-except ImportError:
-    has_scandir = False
+if use_scandir:
+    from pyfakefs import fake_scandir
 
 if sys.version_info < (3, ):
     import __builtin__ as builtins  # pylint: disable=import-error
@@ -284,11 +280,10 @@ class Patcher(object):
     assert None in SKIPMODULES, ("sys.modules contains 'None' values;"
                                  " must skip them.")
 
-    HAS_PATHLIB = sys.version_info >= (3, 4)
     IS_WINDOWS = sys.platform in ('win32', 'cygwin')
 
     SKIPNAMES = {'os', 'path', 'io', 'genericpath'}
-    if HAS_PATHLIB:
+    if pathlib:
         SKIPNAMES.add('pathlib')
 
     def __init__(self, additional_skip_names=None, patch_path=True,
@@ -321,10 +316,10 @@ class Patcher(object):
             'shutil': fake_filesystem_shutil.FakeShutilModule,
             'io': fake_filesystem.FakeIoModule,
         }
-        if self.HAS_PATHLIB:
+        if pathlib:
             self._fake_module_classes[
                 'pathlib'] = fake_pathlib.FakePathlibModule
-        if has_scandir:
+        if use_scandir:
             self._fake_module_classes[
                 'scandir'] = fake_scandir.FakeScanDirModule
 
diff --git a/pyfakefs/tests/all_tests.py b/pyfakefs/tests/all_tests.py
index 8d9b2e7..11a8665 100644
--- a/pyfakefs/tests/all_tests.py
+++ b/pyfakefs/tests/all_tests.py
@@ -19,6 +19,7 @@
 import sys
 import unittest
 
+from pyfakefs.extra_packages import pathlib
 from pyfakefs.tests import dynamic_patch_test
 from pyfakefs.tests import fake_open_test
 from pyfakefs.tests import fake_os_test
@@ -31,7 +32,7 @@ from pyfakefs.tests import fake_tempfile_test
 from pyfakefs.tests import fake_filesystem_vs_real_test
 from pyfakefs.tests import mox3_stubout_test
 
-if sys.version_info >= (3, 4):
+if pathlib:
     from pyfakefs.tests import fake_pathlib_test
 
 
@@ -53,7 +54,7 @@ class AllTests(unittest.TestSuite):
             loader.loadTestsFromModule(mox3_stubout_test),
             loader.loadTestsFromModule(dynamic_patch_test),
         ])
-        if sys.version_info >= (3, 4):
+        if pathlib:
             self.addTests([
                 loader.loadTestsFromModule(fake_pathlib_test)
             ])
diff --git a/pyfakefs/tests/all_tests_without_extra_packages.py b/pyfakefs/tests/all_tests_without_extra_packages.py
new file mode 100644
index 0000000..4c14084
--- /dev/null
+++ b/pyfakefs/tests/all_tests_without_extra_packages.py
@@ -0,0 +1,19 @@
+import sys
+import unittest
+
+from pyfakefs import extra_packages
+
+if extra_packages.pathlib2:
+    extra_packages.pathlib = None
+    extra_packages.pathlib2 = None
+
+if extra_packages.use_scandir_package:
+    extra_packages.use_scandir = False
+    extra_packages.use_scandir_package = False
+
+from pyfakefs.tests.all_tests import AllTests
+
+
+if __name__ == '__main__':
+    result = unittest.TextTestRunner(verbosity=2).run(AllTests().suite())
+    sys.exit(int(not result.wasSuccessful()))
diff --git a/pyfakefs/tests/dynamic_patch_test.py b/pyfakefs/tests/dynamic_patch_test.py
index 2897253..a81d27a 100644
--- a/pyfakefs/tests/dynamic_patch_test.py
+++ b/pyfakefs/tests/dynamic_patch_test.py
@@ -17,6 +17,7 @@ import sys
 import unittest
 
 from pyfakefs import fake_filesystem_unittest
+from pyfakefs.extra_packages import pathlib
 
 
 class TestPyfakefsUnittestBase(fake_filesystem_unittest.TestCase):
@@ -60,10 +61,8 @@ class DynamicImportPatchTest(TestPyfakefsUnittestBase):
         self.fs.set_disk_usage(100)
         self.assertEqual(100, shutil.disk_usage('/').total)
 
-    @unittest.skipIf(sys.version_info < (3, 4), 'pathlib new in Python 3.4')
+    @unittest.skipIf(not pathlib, 'only run if pathlib is available')
     def test_pathlib_patch(self):
-        import pathlib
-
         file_path = 'test.txt'
         path = pathlib.Path(file_path)
         with path.open('w') as f:
@@ -73,12 +72,10 @@ class DynamicImportPatchTest(TestPyfakefsUnittestBase):
         file_object = self.fs.get_object(file_path)
         self.assertEqual('test', file_object.contents)
 
-    @unittest.skipIf(sys.version_info < (3, 4), 'pathlib new in Python 3.4')
+    @unittest.skipIf(not pathlib, 'only run if pathlib is available')
     def test_pathlib_path_patch(self):
-        from pathlib import Path
-
         file_path = 'test.txt'
-        path = Path(file_path)
+        path = pathlib.Path(file_path)
         with path.open('w') as f:
             f.write('test')
 
diff --git a/pyfakefs/tests/example.py b/pyfakefs/tests/example.py
index 962c815..7391acf 100644
--- a/pyfakefs/tests/example.py
+++ b/pyfakefs/tests/example.py
@@ -39,6 +39,7 @@ was eliminated in Python 3.
 import os
 import glob
 import shutil
+import sys
 
 try:
     import scandir
@@ -144,8 +145,10 @@ def scan_dir(path):
     """Return a list of directory entries for the given path."""
     if has_scandir:
         return list(scandir.scandir(path))
-    else:
+    elif sys.version_info >= (3, 5):
         return list(os.scandir(path))
+    else:
+        raise NotImplementedError
 
 
 def file_contents(path):
diff --git a/pyfakefs/tests/example_test.py b/pyfakefs/tests/example_test.py
index 24048bc..e15b844 100644
--- a/pyfakefs/tests/example_test.py
+++ b/pyfakefs/tests/example_test.py
@@ -34,7 +34,7 @@ import sys
 import unittest
 
 from pyfakefs import fake_filesystem_unittest
-from pyfakefs.fake_filesystem_unittest import has_scandir
+from pyfakefs.extra_packages import use_scandir_package
 from pyfakefs.tests import example  # The module under test
 
 
@@ -152,7 +152,7 @@ class TestExample(fake_filesystem_unittest.TestCase):  # pylint: disable=R0904
         self.assertTrue(entries[1].is_symlink())
         self.assertTrue(entries[2].is_file())
 
-    @unittest.skipIf(not has_scandir,
+    @unittest.skipIf(not use_scandir_package,
                      'Testing only if scandir module is installed')
     def test_scandir_scandir(self):
         """Test example.scandir() which uses `scandir.scandir()`.
diff --git a/pyfakefs/tests/fake_filesystem_unittest_test.py b/pyfakefs/tests/fake_filesystem_unittest_test.py
index ac727e2..7bf9589 100644
--- a/pyfakefs/tests/fake_filesystem_unittest_test.py
+++ b/pyfakefs/tests/fake_filesystem_unittest_test.py
@@ -29,13 +29,11 @@ import unittest
 from unittest import TestCase
 
 from pyfakefs import fake_filesystem_unittest, fake_filesystem
+from pyfakefs.extra_packages import pathlib
 from pyfakefs.fake_filesystem_unittest import Patcher
 import pyfakefs.tests.import_as_example
 from pyfakefs.tests.fixtures import module_with_attributes
 
-if sys.version_info >= (3, 4):
-    import pathlib
-
 
 class TestPatcher(TestCase):
     def test_context_manager(self):
@@ -127,7 +125,7 @@ class TestPyfakefsUnittest(TestPyfakefsUnittestBase):  # pylint: disable=R0904
         shutil.rmtree('/test/dir1')
         self.assertFalse(self.fs.exists('/test/dir1'))
 
-    @unittest.skipIf(sys.version_info < (3, 4), "pathlib new in Python 3.4")
+    @unittest.skipIf(not pathlib, "only run if pathlib is available")
     def test_fakepathlib(self):
         with pathlib.Path('/fake_file.txt') as p:
             with p.open('w') as f:
@@ -206,7 +204,7 @@ class TestPatchPathUnittestPassing(TestPyfakefsUnittestBase):
         self.assertEqual(2, path.floor(2.5))
 
 
-if sys.version_info >= (3, 4):
+if pathlib:
     class FakePathlibPathModule(object):
         """Patches `pathlib.Path` by passing all calls to FakePathlibModule."""
         fake_pathlib = None
@@ -262,7 +260,10 @@ class TestCopyOrAddRealFile(TestPyfakefsUnittestBase):
 
     @classmethod
     def setUpClass(cls):
-        cls.filepath = os.path.abspath(__file__)
+        filename = __file__
+        if filename.endswith('.pyc'):  # happens on windows / py27
+            filename = filename[:-1]
+        cls.filepath = os.path.abspath(filename)
         with open(cls.filepath) as f:
             cls.real_string_contents = f.read()
         with open(cls.filepath, 'rb') as f:
diff --git a/pyfakefs/tests/fake_os_test.py b/pyfakefs/tests/fake_os_test.py
index a1f40db..f18910e 100644
--- a/pyfakefs/tests/fake_os_test.py
+++ b/pyfakefs/tests/fake_os_test.py
@@ -26,7 +26,7 @@ import unittest
 
 from pyfakefs import fake_filesystem
 from pyfakefs.fake_filesystem import FakeFileOpen
-from pyfakefs.fake_filesystem_unittest import has_scandir
+from pyfakefs.extra_packages import use_scandir, use_scandir_package
 
 from pyfakefs.tests.test_utils import DummyTime, TestCase, RealFsTestCase
 
@@ -4602,8 +4602,7 @@ class StatPropagationTest(TestCase):
         fh.close()
 
 
-@unittest.skipIf(sys.version_info < (3, 5) and not has_scandir,
-                 'os.scandir was introduced in Python 3.5')
+@unittest.skipIf(not use_scandir, 'only run if scandir is available')
 class FakeScandirTest(FakeOsModuleTestBase):
     def setUp(self):
         super(FakeScandirTest, self).setUp()
@@ -4611,7 +4610,7 @@ class FakeScandirTest(FakeOsModuleTestBase):
                                   not self.use_real_fs() and
                                   not self.is_python2)
 
-        if has_scandir:
+        if use_scandir_package:
             if self.use_real_fs():
                 from scandir import scandir
             else:
@@ -4693,10 +4692,10 @@ class FakeScandirTest(FakeOsModuleTestBase):
         link_path = self.make_path('A', 'C')
         self.os.symlink(dir_path, link_path)
         self.assertEqual([self.os.path.join(link_path, 'D')],
-                         [f.path for f in self.os.scandir(link_path)])
+                         [f.path for f in self.scandir(link_path)])
 
     def test_inode(self):
-        if has_scandir and self.is_windows and self.use_real_fs():
+        if use_scandir and self.is_windows and self.use_real_fs():
             self.skipTest(
                 'inode seems not to work in scandir module under Windows')
         self.assertEqual(self.os.stat(self.dir_path).st_ino,
diff --git a/pyfakefs/tests/fake_pathlib_test.py b/pyfakefs/tests/fake_pathlib_test.py
index 1943906..206298a 100644
--- a/pyfakefs/tests/fake_pathlib_test.py
+++ b/pyfakefs/tests/fake_pathlib_test.py
@@ -21,18 +21,35 @@ Note that many of the tests are directly taken from examples in the
 python docs.
 """
 
+import errno
 import os
-import pathlib
 import stat
 import sys
 import unittest
 
 from pyfakefs import fake_pathlib, fake_filesystem
-from pyfakefs.tests.test_utils import RealFsTestCase
+from pyfakefs.extra_packages import pathlib, pathlib2
+from pyfakefs.helpers import text_type
+from pyfakefs.tests.test_utils import RealFsTestCase, TestCase
 
 is_windows = sys.platform == 'win32'
 
 
+def skip_if_pathlib_35_not_available():
+    if sys.version_info < (3, 5) and not pathlib2:
+        raise unittest.SkipTest('New in version 3.5')
+
+
+def skip_if_pathlib_36_not_available():
+    if sys.version_info < (3, 6) and not pathlib2:
+        raise unittest.SkipTest('Changed behavior in Python 3.6')
+
+
+def skip_if_pathlib_36_is_available():
+    if sys.version_info >= (3, 6) or pathlib2:
+        raise unittest.SkipTest('Changed behavior in Python 3.6')
+
+
 class RealPathlibTestCase(RealFsTestCase):
     def __init__(self, methodName='runTest'):
         super(RealPathlibTestCase, self).__init__(methodName)
@@ -382,35 +399,35 @@ class FakePathlibFileObjectPropertyTest(RealPathlibTestCase):
             self.path(
                 self.os.path.realpath(self.make_path('antoine', 'setup.py'))))
 
-    @unittest.skipIf(sys.version_info >= (3, 6),
-                     'Changed behavior in Python 3.6')
     def test_resolve_nonexisting_file(self):
+        skip_if_pathlib_36_is_available()
         path = self.path('/foo/bar')
-        self.assertRaises(FileNotFoundError, path.resolve)
+        self.assert_raises_os_error(errno.ENOENT, path.resolve)
 
-    @unittest.skipIf(not is_windows or sys.version_info >= (3, 6),
-                     'Changed behavior in Python 3.6')
+    @unittest.skipIf(not is_windows, 'Windows specific behavior')
     def test_resolve_file_as_parent_windows(self):
+        skip_if_pathlib_36_is_available()
         self.check_windows_only()
         self.create_file(self.make_path('a_file'))
         path = self.path(self.make_path('a_file', 'this can not exist'))
-        self.assertRaises(FileNotFoundError, path.resolve)
+        self.assert_raises_os_error(errno.ENOENT, path.resolve)
 
-    @unittest.skipIf(is_windows or sys.version_info >= (3, 6),
-                     'Changed behavior in Python 3.6')
+    @unittest.skipIf(is_windows, 'POSIX specific behavior')
     def test_resolve_file_as_parent_posix(self):
+        skip_if_pathlib_36_is_available()
         self.check_posix_only()
         self.create_file(self.make_path('a_file'))
         path = self.path(self.make_path('', 'a_file', 'this can not exist'))
         self.assertRaises(NotADirectoryError, path.resolve)
 
-    @unittest.skipIf(sys.version_info < (3, 6),
-                     'Changed behavior in Python 3.6')
     def test_resolve_nonexisting_file_after_36(self):
+        skip_if_pathlib_36_not_available()
         path = self.path(
             self.make_path('/path', 'to', 'file', 'this can not exist'))
-        self.assertEqual(path, path.resolve())
-        self.assertRaises(FileNotFoundError, path.resolve, strict=True)
+        # kludge to avoid https://github.com/mcmtroffaes/pathlib2/issues/45
+        if not (pathlib2 and TestCase.is_windows and sys.version_info < (3, 2)):
+            self.assertEqual(path, path.resolve())
+        self.assert_raises_os_error(errno.ENOENT, path.resolve, strict=True)
 
     def test_cwd(self):
         dir_path = self.make_path('jane')
@@ -419,8 +436,8 @@ class FakePathlibFileObjectPropertyTest(RealPathlibTestCase):
         self.assertEqual(self.path.cwd(),
                          self.path(self.os.path.realpath(dir_path)))
 
-    @unittest.skipIf(sys.version_info < (3, 5), 'New in version 3.5')
     def test_expanduser(self):
+        skip_if_pathlib_35_not_available()
         if is_windows:
             self.assertEqual(self.path('~').expanduser(),
                              self.path(
@@ -429,8 +446,8 @@ class FakePathlibFileObjectPropertyTest(RealPathlibTestCase):
             self.assertEqual(self.path('~').expanduser(),
                              self.path(os.environ['HOME']))
 
-    @unittest.skipIf(sys.version_info < (3, 5), 'New in version 3.5')
     def test_home(self):
+        skip_if_pathlib_35_not_available()
         if is_windows:
             self.assertEqual(self.path.home(),
                              self.path(
@@ -467,35 +484,53 @@ class FakePathlibPathFileOperationTest(RealPathlibTestCase):
 
     def test_open(self):
         self.create_dir(self.make_path('foo'))
-        self.assertRaises(OSError,
+        self.assertRaises(IOError,
                           self.path(self.make_path('foo', 'bar.txt')).open)
         self.path(self.make_path('foo', 'bar.txt')).open('w').close()
         self.assertTrue(self.os.path.exists(self.make_path('foo', 'bar.txt')))
 
-    @unittest.skipIf(sys.version_info < (3, 5), 'New in version 3.5')
     def test_read_text(self):
+        skip_if_pathlib_35_not_available()
+        self.create_file(self.make_path('text_file'), contents='foo')
+        file_path = self.path(self.make_path('text_file'))
+        self.assertEqual(file_path.read_text(), 'foo')
+
+    @unittest.skipIf(sys.version_info < (3, 0),
+                     'Python 3 specific string handling')
+    def test_read_text_with_encoding(self):
+        skip_if_pathlib_35_not_available()
         self.create_file(self.make_path('text_file'),
                          contents='ерунда', encoding='cyrillic')
         file_path = self.path(self.make_path('text_file'))
         self.assertEqual(file_path.read_text(encoding='cyrillic'), 'ерунда')
 
-    @unittest.skipIf(sys.version_info < (3, 5), 'New in version 3.5')
     def test_write_text(self):
+        skip_if_pathlib_35_not_available()
+        path_name = self.make_path('text_file')
+        file_path = self.path(path_name)
+        file_path.write_text(text_type('foo'))
+        self.assertTrue(self.os.path.exists(path_name))
+        self.check_contents(path_name, 'foo')
+
+    @unittest.skipIf(sys.version_info < (3, 0),
+                     'Python 3 specific string handling')
+    def test_write_text_with_encoding(self):
+        skip_if_pathlib_35_not_available()
         path_name = self.make_path('text_file')
         file_path = self.path(path_name)
         file_path.write_text('ανοησίες', encoding='greek')
         self.assertTrue(self.os.path.exists(path_name))
         self.check_contents(path_name, 'ανοησίες'.encode('greek'))
 
-    @unittest.skipIf(sys.version_info < (3, 5), 'New in version 3.5')
     def test_read_bytes(self):
+        skip_if_pathlib_35_not_available()
         path_name = self.make_path('binary_file')
         self.create_file(path_name, contents=b'Binary file contents')
         file_path = self.path(path_name)
         self.assertEqual(file_path.read_bytes(), b'Binary file contents')
 
-    @unittest.skipIf(sys.version_info < (3, 5), 'New in version 3.5')
     def test_write_bytes(self):
+        skip_if_pathlib_35_not_available()
         path_name = self.make_path('binary_file')
         file_path = self.path(path_name)
         file_path.write_bytes(b'Binary file contents')
@@ -510,6 +545,8 @@ class FakePathlibPathFileOperationTest(RealPathlibTestCase):
         self.assertFalse(self.os.path.exists(file_name))
         self.check_contents(new_file_name, 'test')
 
+    @unittest.skipIf(sys.version_info < (3, 3),
+                     'Path.replace() available since Python 3.3')
     def test_replace(self):
         self.create_file(self.make_path('foo', 'bar.txt'), contents='test')
         self.create_file(self.make_path('bar', 'old.txt'), contents='replaced')
@@ -536,12 +573,12 @@ class FakePathlibPathFileOperationTest(RealPathlibTestCase):
         file_name = self.make_path('foo', 'bar.txt')
         self.create_file(file_name, contents='test')
         file_path = self.path(file_name)
-        self.assertRaises(FileExistsError, file_path.touch, exist_ok=False)
+        self.assert_raises_os_error(errno.EEXIST, file_path.touch, exist_ok=False)
         file_path.touch()
         self.check_contents(file_name, 'test')
 
-    @unittest.skipIf(sys.version_info < (3, 5), 'New in version 3.5')
     def test_samefile(self):
+        skip_if_pathlib_35_not_available()
         file_name = self.make_path('foo', 'bar.txt')
         self.create_file(file_name)
         file_name2 = self.make_path('foo', 'baz.txt')
@@ -576,21 +613,21 @@ class FakePathlibPathFileOperationTest(RealPathlibTestCase):
 
     def test_mkdir(self):
         dir_name = self.make_path('foo', 'bar')
-        self.assertRaises(FileNotFoundError, self.path(dir_name).mkdir)
+        self.assert_raises_os_error(errno.ENOENT, self.path(dir_name).mkdir)
         self.path(dir_name).mkdir(parents=True)
         self.assertTrue(self.os.path.exists(dir_name))
-        self.assertRaises(FileExistsError, self.path(dir_name).mkdir)
+        self.assert_raises_os_error(errno.EEXIST, self.path(dir_name).mkdir)
 
-    @unittest.skipIf(sys.version_info < (3, 5),
-                     'exist_ok argument new in Python 3.5')
     def test_mkdir_exist_ok(self):
+        skip_if_pathlib_35_not_available()
         dir_name = self.make_path('foo', 'bar')
         self.create_dir(dir_name)
         self.path(dir_name).mkdir(exist_ok=True)
         file_name = self.os.path.join(dir_name, 'baz')
         self.create_file(file_name)
-        self.assertRaises(FileExistsError, self.path(file_name).mkdir,
-                          exist_ok=True)
+        self.assert_raises_os_error(errno.EEXIST,
+                                         self.path(file_name).mkdir,
+                                         exist_ok=True)
 
     def test_rmdir(self):
         dir_name = self.make_path('foo', 'bar')
diff --git a/pyfakefs/tests/import_as_example.py b/pyfakefs/tests/import_as_example.py
index 96fe229..86fb2e6 100644
--- a/pyfakefs/tests/import_as_example.py
+++ b/pyfakefs/tests/import_as_example.py
@@ -17,15 +17,18 @@ to be patched under another name.
 import os as my_os
 
 try:
-    from pathlib import Path
+    import pathlib
 except ImportError:
-    Path = None
+    try:
+        import pathlib2 as pathlib
+    except ImportError:
+        pathlib = None
 
 
 def check_if_exists(filepath):
     return my_os.path.exists(filepath)
 
 
-if Path is not None:
+if pathlib:
     def check_if_path_exists(filepath):
-        return Path(filepath).exists()
+        return pathlib.Path(filepath).exists()