+ def test_symlink_out_of_root_directory(self) -> None:
+ path = MagicMock()
+ root = THIS_DIR
+ child = MagicMock()
+ include = re.compile(black.DEFAULT_INCLUDES)
+ exclude = re.compile(black.DEFAULT_EXCLUDES)
+ report = black.Report()
+ # `child` should behave like a symlink which resolved path is clearly
+ # outside of the `root` directory.
+ path.iterdir.return_value = [child]
+ child.resolve.return_value = Path("/a/b/c")
+ child.is_symlink.return_value = True
+ try:
+ list(black.gen_python_files_in_dir(path, root, include, exclude, report))
+ except ValueError as ve:
+ self.fail(f"`get_python_files_in_dir()` failed: {ve}")
+ path.iterdir.assert_called_once()
+ child.resolve.assert_called_once()
+ child.is_symlink.assert_called_once()
+ # `child` should behave like a strange file which resolved path is clearly
+ # outside of the `root` directory.
+ child.is_symlink.return_value = False
+ with self.assertRaises(ValueError):
+ list(black.gen_python_files_in_dir(path, root, include, exclude, report))
+ path.iterdir.assert_called()
+ self.assertEqual(path.iterdir.call_count, 2)
+ child.resolve.assert_called()
+ self.assertEqual(child.resolve.call_count, 2)
+ child.is_symlink.assert_called()
+ self.assertEqual(child.is_symlink.call_count, 2)
+
+ def test_shhh_click(self) -> None:
+ try:
+ from click import _unicodefun # type: ignore
+ except ModuleNotFoundError:
+ self.skipTest("Incompatible Click version")
+ if not hasattr(_unicodefun, "_verify_python3_env"):
+ self.skipTest("Incompatible Click version")
+ # First, let's see if Click is crashing with a preferred ASCII charset.
+ with patch("locale.getpreferredencoding") as gpe:
+ gpe.return_value = "ASCII"
+ with self.assertRaises(RuntimeError):
+ _unicodefun._verify_python3_env()
+ # Now, let's silence Click...
+ black.patch_click()
+ # ...and confirm it's silent.
+ with patch("locale.getpreferredencoding") as gpe:
+ gpe.return_value = "ASCII"
+ try:
+ _unicodefun._verify_python3_env()
+ except RuntimeError as re:
+ self.fail(f"`patch_click()` failed, exception still raised: {re}")
+
+ def test_root_logger_not_used_directly(self) -> None:
+ def fail(*args: Any, **kwargs: Any) -> None:
+ self.fail("Record created with root logger")
+
+ with patch.multiple(
+ logging.root,
+ debug=fail,
+ info=fail,
+ warning=fail,
+ error=fail,
+ critical=fail,
+ log=fail,
+ ):
+ ff(THIS_FILE)
+
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ def test_blackd_main(self) -> None:
+ with patch("blackd.web.run_app"):
+ result = CliRunner().invoke(blackd.main, [])
+ if result.exception is not None:
+ raise result.exception
+ self.assertEqual(result.exit_code, 0)
+
+
+class BlackDTestCase(AioHTTPTestCase):
+ async def get_application(self) -> web.Application:
+ return blackd.make_app()
+
+ # TODO: remove these decorators once the below is released
+ # https://github.com/aio-libs/aiohttp/pull/3727
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_request_needs_formatting(self) -> None:
+ response = await self.client.post("/", data=b"print('hello world')")
+ self.assertEqual(response.status, 200)
+ self.assertEqual(response.charset, "utf8")
+ self.assertEqual(await response.read(), b'print("hello world")\n')
+
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_request_no_change(self) -> None:
+ response = await self.client.post("/", data=b'print("hello world")\n')
+ self.assertEqual(response.status, 204)
+ self.assertEqual(await response.read(), b"")
+
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_request_syntax_error(self) -> None:
+ response = await self.client.post("/", data=b"what even ( is")
+ self.assertEqual(response.status, 400)
+ content = await response.text()
+ self.assertTrue(
+ content.startswith("Cannot parse"),
+ msg=f"Expected error to start with 'Cannot parse', got {repr(content)}",
+ )
+
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_unsupported_version(self) -> None:
+ response = await self.client.post(
+ "/", data=b"what", headers={blackd.VERSION_HEADER: "2"}
+ )
+ self.assertEqual(response.status, 501)
+
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_supported_version(self) -> None:
+ response = await self.client.post(
+ "/", data=b"what", headers={blackd.VERSION_HEADER: "1"}
+ )
+ self.assertEqual(response.status, 200)
+
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_invalid_python_variant(self) -> None:
+ async def check(header_value: str, expected_status: int = 400) -> None:
+ response = await self.client.post(
+ "/", data=b"what", headers={blackd.PYTHON_VARIANT_HEADER: header_value}
+ )
+ self.assertEqual(response.status, expected_status)
+
+ await check("lol")
+ await check("ruby3.5")
+ await check("pyi3.6")
+ await check("py1.5")
+ await check("2.8")
+ await check("py2.8")
+ await check("3.0")
+ await check("pypy3.0")
+ await check("jython3.4")
+
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_pyi(self) -> None:
+ source, expected = read_data("stub.pyi")
+ response = await self.client.post(
+ "/", data=source, headers={blackd.PYTHON_VARIANT_HEADER: "pyi"}
+ )
+ self.assertEqual(response.status, 200)
+ self.assertEqual(await response.text(), expected)
+
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_python_variant(self) -> None:
+ code = (
+ "def f(\n"
+ " and_has_a_bunch_of,\n"
+ " very_long_arguments_too,\n"
+ " and_lots_of_them_as_well_lol,\n"
+ " **and_very_long_keyword_arguments\n"
+ "):\n"
+ " pass\n"
+ )
+
+ async def check(header_value: str, expected_status: int) -> None:
+ response = await self.client.post(
+ "/", data=code, headers={blackd.PYTHON_VARIANT_HEADER: header_value}
+ )
+ self.assertEqual(
+ response.status, expected_status, msg=await response.text()
+ )
+
+ await check("3.6", 200)
+ await check("py3.6", 200)
+ await check("3.6,3.7", 200)
+ await check("3.6,py3.7", 200)
+ await check("py36,py37", 200)
+ await check("36", 200)
+ await check("3.6.4", 200)
+
+ await check("2", 204)
+ await check("2.7", 204)
+ await check("py2.7", 204)
+ await check("3.4", 204)
+ await check("py3.4", 204)
+ await check("py34,py36", 204)
+ await check("34", 204)
+
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_line_length(self) -> None:
+ response = await self.client.post(
+ "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "7"}
+ )
+ self.assertEqual(response.status, 200)
+
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_invalid_line_length(self) -> None:
+ response = await self.client.post(
+ "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "NaN"}
+ )
+ self.assertEqual(response.status, 400)
+