Skip to content

Commit

Permalink
bpo-32964: Reuse a testing implementation of the path protocol in tes…
Browse files Browse the repository at this point in the history
…ts. (python#5930)
  • Loading branch information
serhiy-storchaka committed Mar 2, 2018
1 parent bf63e8d commit b21d155
Show file tree
Hide file tree
Showing 11 changed files with 70 additions and 103 deletions.
7 changes: 7 additions & 0 deletions Doc/library/test.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1293,6 +1293,13 @@ The :mod:`test.support` module defines the following classes:
Class for logging support.


.. class:: FakePath(path)

Simple :term:`path-like object`. It implements the :meth:`__fspath__`
method which just returns the *path* argument. If *path* is an exception,
it will be raised in :meth:`!__fspath__`.


:mod:`test.support.script_helper` --- Utilities for the Python execution tests
==============================================================================

Expand Down
18 changes: 18 additions & 0 deletions Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2840,3 +2840,21 @@ def restore(self):
def with_pymalloc():
import _testcapi
return _testcapi.WITH_PYMALLOC


class FakePath:
"""Simple implementing of the path protocol.
"""
def __init__(self, path):
self.path = path

def __repr__(self):
return f'<FakePath {self.path!r}>'

def __fspath__(self):
if (isinstance(self.path, BaseException) or
isinstance(self.path, type) and
issubclass(self.path, BaseException)):
raise self.path
else:
return self.path
10 changes: 2 additions & 8 deletions Lib/test/test_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import tempfile
import types
from test import support
from test.support import script_helper
from test.support import script_helper, FakePath

class TestSpecifics(unittest.TestCase):

Expand Down Expand Up @@ -663,13 +663,7 @@ def check_different_constants(const1, const2):

def test_path_like_objects(self):
# An implicit test for PyUnicode_FSDecoder().
class PathLike:
def __init__(self, path):
self._path = path
def __fspath__(self):
return self._path

compile("42", PathLike("test_compile_pathlike"), "single")
compile("42", FakePath("test_compile_pathlike"), "single")

def test_stack_overflow(self):
# bpo-31113: Stack overflow when compile a long sequence of
Expand Down
12 changes: 2 additions & 10 deletions Lib/test/test_genericpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import warnings
from test import support
from test.support.script_helper import assert_python_ok
from test.support import FakePath


def create_file(filename, data=b'foo'):
Expand Down Expand Up @@ -493,18 +494,9 @@ def test_import(self):

class PathLikeTests(unittest.TestCase):

class PathLike:
def __init__(self, path=''):
self.path = path
def __fspath__(self):
if isinstance(self.path, BaseException):
raise self.path
else:
return self.path

def setUp(self):
self.file_name = support.TESTFN.lower()
self.file_path = self.PathLike(support.TESTFN)
self.file_path = FakePath(support.TESTFN)
self.addCleanup(support.unlink, self.file_name)
create_file(self.file_name, b"test_genericpath.PathLikeTests")

Expand Down
25 changes: 14 additions & 11 deletions Lib/test/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from itertools import cycle, count
from test import support
from test.support.script_helper import assert_python_ok, run_python_until_end
from test.support import FakePath

import codecs
import io # C implementation of io
Expand Down Expand Up @@ -891,30 +892,32 @@ def read(self, size):
self.assertEqual(bytes(buffer), b"12345")

def test_fspath_support(self):
class PathLike:
def __init__(self, path):
self.path = path

def __fspath__(self):
return self.path

def check_path_succeeds(path):
with self.open(path, "w") as f:
f.write("egg\n")

with self.open(path, "r") as f:
self.assertEqual(f.read(), "egg\n")

check_path_succeeds(PathLike(support.TESTFN))
check_path_succeeds(PathLike(support.TESTFN.encode('utf-8')))
check_path_succeeds(FakePath(support.TESTFN))
check_path_succeeds(FakePath(support.TESTFN.encode('utf-8')))

with self.open(support.TESTFN, "w") as f:
bad_path = FakePath(f.fileno())
with self.assertRaises(TypeError):
self.open(bad_path, 'w')

bad_path = PathLike(TypeError)
bad_path = FakePath(None)
with self.assertRaises(TypeError):
self.open(bad_path, 'w')

bad_path = FakePath(FloatingPointError)
with self.assertRaises(FloatingPointError):
self.open(bad_path, 'w')

# ensure that refcounting is correct with some error conditions
with self.assertRaisesRegex(ValueError, 'read/write/append mode'):
self.open(PathLike(support.TESTFN), 'rwxa')
self.open(FakePath(support.TESTFN), 'rwxa')

def test_RawIOBase_readall(self):
# Exercise the default unlimited RawIOBase.read() and readall()
Expand Down
15 changes: 3 additions & 12 deletions Lib/test/test_ntpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import sys
import unittest
import warnings
from test.support import TestFailed
from test.support import TestFailed, FakePath
from test import support, test_genericpath
from tempfile import TemporaryFile

Expand Down Expand Up @@ -459,18 +459,9 @@ class PathLikeTests(unittest.TestCase):

path = ntpath

class PathLike:
def __init__(self, path=''):
self.path = path
def __fspath__(self):
if isinstance(self.path, BaseException):
raise self.path
else:
return self.path

def setUp(self):
self.file_name = support.TESTFN.lower()
self.file_path = self.PathLike(support.TESTFN)
self.file_path = FakePath(support.TESTFN)
self.addCleanup(support.unlink, self.file_name)
with open(self.file_name, 'xb', 0) as file:
file.write(b"test_ntpath.PathLikeTests")
Expand All @@ -485,7 +476,7 @@ def test_path_isabs(self):
self.assertPathEqual(self.path.isabs)

def test_path_join(self):
self.assertEqual(self.path.join('a', self.PathLike('b'), 'c'),
self.assertEqual(self.path.join('a', FakePath('b'), 'c'),
self.path.join('a', 'b', 'c'))

def test_path_split(self):
Expand Down
44 changes: 14 additions & 30 deletions Lib/test/test_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
INT_MAX = PY_SSIZE_T_MAX = sys.maxsize

from test.support.script_helper import assert_python_ok
from test.support import unix_shell
from test.support import unix_shell, FakePath


root_in_posix = False
Expand All @@ -85,21 +85,6 @@ def requires_os_func(name):
return unittest.skipUnless(hasattr(os, name), 'requires os.%s' % name)


class _PathLike(os.PathLike):

def __init__(self, path=""):
self.path = path

def __str__(self):
return str(self.path)

def __fspath__(self):
if isinstance(self.path, BaseException):
raise self.path
else:
return self.path


def create_file(filename, content=b'content'):
with open(filename, "xb", 0) as fp:
fp.write(content)
Expand Down Expand Up @@ -942,15 +927,14 @@ def test_walk_prune(self, walk_path=None):
dirs.remove('SUB1')

self.assertEqual(len(all), 2)
self.assertEqual(all[0],
(str(walk_path), ["SUB2"], ["tmp1"]))
self.assertEqual(all[0], (self.walk_path, ["SUB2"], ["tmp1"]))

all[1][-1].sort()
all[1][1].sort()
self.assertEqual(all[1], self.sub2_tree)

def test_file_like_path(self):
self.test_walk_prune(_PathLike(self.walk_path))
self.test_walk_prune(FakePath(self.walk_path))

def test_walk_bottom_up(self):
# Walk bottom-up.
Expand Down Expand Up @@ -2288,7 +2272,7 @@ def test_getppid(self):
def test_waitpid(self):
args = [sys.executable, '-c', 'pass']
# Add an implicit test for PyUnicode_FSConverter().
pid = os.spawnv(os.P_NOWAIT, _PathLike(args[0]), args)
pid = os.spawnv(os.P_NOWAIT, FakePath(args[0]), args)
status = os.waitpid(pid, 0)
self.assertEqual(status, (pid, 0))

Expand Down Expand Up @@ -3129,13 +3113,13 @@ def test_path_t_converter(self):
bytes_fspath = bytes_filename = None
else:
bytes_filename = support.TESTFN.encode('ascii')
bytes_fspath = _PathLike(bytes_filename)
fd = os.open(_PathLike(str_filename), os.O_WRONLY|os.O_CREAT)
bytes_fspath = FakePath(bytes_filename)
fd = os.open(FakePath(str_filename), os.O_WRONLY|os.O_CREAT)
self.addCleanup(support.unlink, support.TESTFN)
self.addCleanup(os.close, fd)

int_fspath = _PathLike(fd)
str_fspath = _PathLike(str_filename)
int_fspath = FakePath(fd)
str_fspath = FakePath(str_filename)

for name, allow_fd, extra_args, cleanup_fn in self.functions:
with self.subTest(name=name):
Expand Down Expand Up @@ -3540,16 +3524,16 @@ def test_return_string(self):

def test_fsencode_fsdecode(self):
for p in "path/like/object", b"path/like/object":
pathlike = _PathLike(p)
pathlike = FakePath(p)

self.assertEqual(p, self.fspath(pathlike))
self.assertEqual(b"path/like/object", os.fsencode(pathlike))
self.assertEqual("path/like/object", os.fsdecode(pathlike))

def test_pathlike(self):
self.assertEqual('#feelthegil', self.fspath(_PathLike('#feelthegil')))
self.assertTrue(issubclass(_PathLike, os.PathLike))
self.assertTrue(isinstance(_PathLike(), os.PathLike))
self.assertEqual('#feelthegil', self.fspath(FakePath('#feelthegil')))
self.assertTrue(issubclass(FakePath, os.PathLike))
self.assertTrue(isinstance(FakePath('x'), os.PathLike))

def test_garbage_in_exception_out(self):
vapor = type('blah', (), {})
Expand All @@ -3561,14 +3545,14 @@ def test_argument_required(self):

def test_bad_pathlike(self):
# __fspath__ returns a value other than str or bytes.
self.assertRaises(TypeError, self.fspath, _PathLike(42))
self.assertRaises(TypeError, self.fspath, FakePath(42))
# __fspath__ attribute that is not callable.
c = type('foo', (), {})
c.__fspath__ = 1
self.assertRaises(TypeError, self.fspath, c())
# __fspath__ raises an exception.
self.assertRaises(ZeroDivisionError, self.fspath,
_PathLike(ZeroDivisionError()))
FakePath(ZeroDivisionError()))


class TimesTests(unittest.TestCase):
Expand Down
9 changes: 3 additions & 6 deletions Lib/test/test_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from unittest import mock

from test import support
TESTFN = support.TESTFN
from test.support import TESTFN, FakePath

try:
import grp, pwd
Expand Down Expand Up @@ -191,18 +191,15 @@ def test_constructor_common(self):
P = self.cls
p = P('a')
self.assertIsInstance(p, P)
class PathLike:
def __fspath__(self):
return "a/b/c"
P('a', 'b', 'c')
P('/a', 'b', 'c')
P('a/b/c')
P('/a/b/c')
P(PathLike())
P(FakePath("a/b/c"))
self.assertEqual(P(P('a')), P('a'))
self.assertEqual(P(P('a'), 'b'), P('a/b'))
self.assertEqual(P(P('a'), P('b')), P('a/b'))
self.assertEqual(P(P('a'), P('b'), P('c')), P(PathLike()))
self.assertEqual(P(P('a'), P('b'), P('c')), P(FakePath("a/b/c")))

def _check_str_subclass(self, *args):
# Issue #21127: it should be possible to construct a PurePath object
Expand Down
14 changes: 3 additions & 11 deletions Lib/test/test_posixpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import warnings
from posixpath import realpath, abspath, dirname, basename
from test import support, test_genericpath
from test.support import FakePath

try:
import posix
Expand Down Expand Up @@ -600,18 +601,9 @@ class PathLikeTests(unittest.TestCase):

path = posixpath

class PathLike:
def __init__(self, path=''):
self.path = path
def __fspath__(self):
if isinstance(self.path, BaseException):
raise self.path
else:
return self.path

def setUp(self):
self.file_name = support.TESTFN.lower()
self.file_path = self.PathLike(support.TESTFN)
self.file_path = FakePath(support.TESTFN)
self.addCleanup(support.unlink, self.file_name)
with open(self.file_name, 'xb', 0) as file:
file.write(b"test_posixpath.PathLikeTests")
Expand All @@ -626,7 +618,7 @@ def test_path_isabs(self):
self.assertPathEqual(self.path.isabs)

def test_path_join(self):
self.assertEqual(self.path.join('a', self.PathLike('b'), 'c'),
self.assertEqual(self.path.join('a', FakePath('b'), 'c'),
self.path.join('a', 'b', 'c'))

def test_path_split(self):
Expand Down
11 changes: 2 additions & 9 deletions Lib/test/test_shutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import zipfile

from test import support
from test.support import TESTFN
from test.support import TESTFN, FakePath

TESTFN2 = TESTFN + "2"

Expand Down Expand Up @@ -1232,14 +1232,7 @@ def test_register_archive_format(self):
def check_unpack_archive(self, format):
self.check_unpack_archive_with_converter(format, lambda path: path)
self.check_unpack_archive_with_converter(format, pathlib.Path)

class MyPath:
def __init__(self, path):
self.path = path
def __fspath__(self):
return self.path

self.check_unpack_archive_with_converter(format, MyPath)
self.check_unpack_archive_with_converter(format, FakePath)

def check_unpack_archive_with_converter(self, format, converter):
root_dir, base_dir = self._create_files()
Expand Down

0 comments on commit b21d155

Please sign in to comment.