Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.7] bpo-32964: Reuse a testing implementation of the path protocol in tests. (GH-5930) #5957

Merged
merged 1 commit into from
Mar 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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