+ @patch("black.find_project_root", lambda *args: THIS_DIR.resolve())
+ def test_exclude_for_issue_1572(self) -> None:
+ # Exclude shouldn't touch files that were explicitly given to Black through the
+ # CLI. Exclude is supposed to only apply to the recursive discovery of files.
+ # https://github.com/psf/black/issues/1572
+ path = THIS_DIR / "data" / "include_exclude_tests"
+ include = ""
+ exclude = r"/exclude/|a\.py"
+ src = str(path / "b/exclude/a.py")
+ report = black.Report()
+ expected = [Path(path / "b/exclude/a.py")]
+ sources = list(
+ black.get_sources(
+ ctx=FakeContext(),
+ src=(src,),
+ quiet=True,
+ verbose=False,
+ include=re.compile(include),
+ exclude=re.compile(exclude),
+ extend_exclude=None,
+ force_exclude=None,
+ report=report,
+ stdin_filename=None,
+ )
+ )
+ self.assertEqual(sorted(expected), sorted(sources))
+
+ @patch("black.find_project_root", lambda *args: THIS_DIR.resolve())
+ def test_get_sources_with_stdin(self) -> None:
+ include = ""
+ exclude = r"/exclude/|a\.py"
+ src = "-"
+ report = black.Report()
+ expected = [Path("-")]
+ sources = list(
+ black.get_sources(
+ ctx=FakeContext(),
+ src=(src,),
+ quiet=True,
+ verbose=False,
+ include=re.compile(include),
+ exclude=re.compile(exclude),
+ extend_exclude=None,
+ force_exclude=None,
+ report=report,
+ stdin_filename=None,
+ )
+ )
+ self.assertEqual(sorted(expected), sorted(sources))
+
+ @patch("black.find_project_root", lambda *args: THIS_DIR.resolve())
+ def test_get_sources_with_stdin_filename(self) -> None:
+ include = ""
+ exclude = r"/exclude/|a\.py"
+ src = "-"
+ report = black.Report()
+ stdin_filename = str(THIS_DIR / "data/collections.py")
+ expected = [Path(f"__BLACK_STDIN_FILENAME__{stdin_filename}")]
+ sources = list(
+ black.get_sources(
+ ctx=FakeContext(),
+ src=(src,),
+ quiet=True,
+ verbose=False,
+ include=re.compile(include),
+ exclude=re.compile(exclude),
+ extend_exclude=None,
+ force_exclude=None,
+ report=report,
+ stdin_filename=stdin_filename,
+ )
+ )
+ self.assertEqual(sorted(expected), sorted(sources))
+
+ @patch("black.find_project_root", lambda *args: THIS_DIR.resolve())
+ def test_get_sources_with_stdin_filename_and_exclude(self) -> None:
+ # Exclude shouldn't exclude stdin_filename since it is mimicking the
+ # file being passed directly. This is the same as
+ # test_exclude_for_issue_1572
+ path = THIS_DIR / "data" / "include_exclude_tests"
+ include = ""
+ exclude = r"/exclude/|a\.py"
+ src = "-"
+ report = black.Report()
+ stdin_filename = str(path / "b/exclude/a.py")
+ expected = [Path(f"__BLACK_STDIN_FILENAME__{stdin_filename}")]
+ sources = list(
+ black.get_sources(
+ ctx=FakeContext(),
+ src=(src,),
+ quiet=True,
+ verbose=False,
+ include=re.compile(include),
+ exclude=re.compile(exclude),
+ extend_exclude=None,
+ force_exclude=None,
+ report=report,
+ stdin_filename=stdin_filename,
+ )
+ )
+ self.assertEqual(sorted(expected), sorted(sources))
+
+ @patch("black.find_project_root", lambda *args: THIS_DIR.resolve())
+ def test_get_sources_with_stdin_filename_and_extend_exclude(self) -> None:
+ # Extend exclude shouldn't exclude stdin_filename since it is mimicking the
+ # file being passed directly. This is the same as
+ # test_exclude_for_issue_1572
+ path = THIS_DIR / "data" / "include_exclude_tests"
+ include = ""
+ extend_exclude = r"/exclude/|a\.py"
+ src = "-"
+ report = black.Report()
+ stdin_filename = str(path / "b/exclude/a.py")
+ expected = [Path(f"__BLACK_STDIN_FILENAME__{stdin_filename}")]
+ sources = list(
+ black.get_sources(
+ ctx=FakeContext(),
+ src=(src,),
+ quiet=True,
+ verbose=False,
+ include=re.compile(include),
+ exclude=re.compile(""),
+ extend_exclude=re.compile(extend_exclude),
+ force_exclude=None,
+ report=report,
+ stdin_filename=stdin_filename,
+ )
+ )
+ self.assertEqual(sorted(expected), sorted(sources))
+
+ @patch("black.find_project_root", lambda *args: THIS_DIR.resolve())
+ def test_get_sources_with_stdin_filename_and_force_exclude(self) -> None:
+ # Force exclude should exclude the file when passing it through
+ # stdin_filename
+ path = THIS_DIR / "data" / "include_exclude_tests"
+ include = ""
+ force_exclude = r"/exclude/|a\.py"
+ src = "-"
+ report = black.Report()
+ stdin_filename = str(path / "b/exclude/a.py")
+ sources = list(
+ black.get_sources(
+ ctx=FakeContext(),
+ src=(src,),
+ quiet=True,
+ verbose=False,
+ include=re.compile(include),
+ exclude=re.compile(""),
+ extend_exclude=None,
+ force_exclude=re.compile(force_exclude),
+ report=report,
+ stdin_filename=stdin_filename,
+ )
+ )
+ self.assertEqual([], sorted(sources))
+
+ def test_reformat_one_with_stdin(self) -> None:
+ with patch(
+ "black.format_stdin_to_stdout",
+ return_value=lambda *args, **kwargs: black.Changed.YES,
+ ) as fsts:
+ report = MagicMock()
+ path = Path("-")
+ black.reformat_one(
+ path,
+ fast=True,
+ write_back=black.WriteBack.YES,
+ mode=DEFAULT_MODE,
+ report=report,
+ )
+ fsts.assert_called_once()
+ report.done.assert_called_with(path, black.Changed.YES)
+
+ def test_reformat_one_with_stdin_filename(self) -> None:
+ with patch(
+ "black.format_stdin_to_stdout",
+ return_value=lambda *args, **kwargs: black.Changed.YES,
+ ) as fsts:
+ report = MagicMock()
+ p = "foo.py"
+ path = Path(f"__BLACK_STDIN_FILENAME__{p}")
+ expected = Path(p)
+ black.reformat_one(
+ path,
+ fast=True,
+ write_back=black.WriteBack.YES,
+ mode=DEFAULT_MODE,
+ report=report,
+ )
+ fsts.assert_called_once_with(
+ fast=True, write_back=black.WriteBack.YES, mode=DEFAULT_MODE
+ )
+ # __BLACK_STDIN_FILENAME__ should have been stripped
+ report.done.assert_called_with(expected, black.Changed.YES)
+
+ def test_reformat_one_with_stdin_filename_pyi(self) -> None:
+ with patch(
+ "black.format_stdin_to_stdout",
+ return_value=lambda *args, **kwargs: black.Changed.YES,
+ ) as fsts:
+ report = MagicMock()
+ p = "foo.pyi"
+ path = Path(f"__BLACK_STDIN_FILENAME__{p}")
+ expected = Path(p)
+ black.reformat_one(
+ path,
+ fast=True,
+ write_back=black.WriteBack.YES,
+ mode=DEFAULT_MODE,
+ report=report,
+ )
+ fsts.assert_called_once_with(
+ fast=True,
+ write_back=black.WriteBack.YES,
+ mode=replace(DEFAULT_MODE, is_pyi=True),
+ )
+ # __BLACK_STDIN_FILENAME__ should have been stripped
+ report.done.assert_called_with(expected, black.Changed.YES)
+
+ def test_reformat_one_with_stdin_filename_ipynb(self) -> None:
+ with patch(
+ "black.format_stdin_to_stdout",
+ return_value=lambda *args, **kwargs: black.Changed.YES,
+ ) as fsts:
+ report = MagicMock()
+ p = "foo.ipynb"
+ path = Path(f"__BLACK_STDIN_FILENAME__{p}")
+ expected = Path(p)
+ black.reformat_one(
+ path,
+ fast=True,
+ write_back=black.WriteBack.YES,
+ mode=DEFAULT_MODE,
+ report=report,
+ )
+ fsts.assert_called_once_with(
+ fast=True,
+ write_back=black.WriteBack.YES,
+ mode=replace(DEFAULT_MODE, is_ipynb=True),
+ )
+ # __BLACK_STDIN_FILENAME__ should have been stripped
+ report.done.assert_called_with(expected, black.Changed.YES)
+
+ def test_reformat_one_with_stdin_and_existing_path(self) -> None:
+ with patch(
+ "black.format_stdin_to_stdout",
+ return_value=lambda *args, **kwargs: black.Changed.YES,
+ ) as fsts:
+ report = MagicMock()
+ # Even with an existing file, since we are forcing stdin, black
+ # should output to stdout and not modify the file inplace
+ p = Path(str(THIS_DIR / "data/collections.py"))
+ # Make sure is_file actually returns True
+ self.assertTrue(p.is_file())
+ path = Path(f"__BLACK_STDIN_FILENAME__{p}")
+ expected = Path(p)
+ black.reformat_one(
+ path,
+ fast=True,
+ write_back=black.WriteBack.YES,
+ mode=DEFAULT_MODE,
+ report=report,
+ )
+ fsts.assert_called_once()
+ # __BLACK_STDIN_FILENAME__ should have been stripped
+ report.done.assert_called_with(expected, black.Changed.YES)
+
+ def test_reformat_one_with_stdin_empty(self) -> None:
+ output = io.StringIO()
+ with patch("io.TextIOWrapper", lambda *args, **kwargs: output):
+ try:
+ black.format_stdin_to_stdout(
+ fast=True,
+ content="",
+ write_back=black.WriteBack.YES,
+ mode=DEFAULT_MODE,
+ )
+ except io.UnsupportedOperation:
+ pass # StringIO does not support detach
+ assert output.getvalue() == ""
+
+ def test_gitignore_exclude(self) -> None:
+ path = THIS_DIR / "data" / "include_exclude_tests"
+ include = re.compile(r"\.pyi?$")
+ exclude = re.compile(r"")
+ report = black.Report()
+ gitignore = PathSpec.from_lines(
+ "gitwildmatch", ["exclude/", ".definitely_exclude"]
+ )
+ sources: List[Path] = []
+ expected = [
+ Path(path / "b/dont_exclude/a.py"),
+ Path(path / "b/dont_exclude/a.pyi"),
+ ]
+ this_abs = THIS_DIR.resolve()
+ sources.extend(
+ black.gen_python_files(
+ path.iterdir(),
+ this_abs,
+ include,
+ exclude,
+ None,
+ None,
+ report,
+ gitignore,
+ verbose=False,
+ quiet=False,
+ )
+ )
+ self.assertEqual(sorted(expected), sorted(sources))
+
+ def test_nested_gitignore(self) -> None:
+ path = Path(THIS_DIR / "data" / "nested_gitignore_tests")
+ include = re.compile(r"\.pyi?$")
+ exclude = re.compile(r"")
+ root_gitignore = black.files.get_gitignore(path)
+ report = black.Report()
+ expected: List[Path] = [
+ Path(path / "x.py"),
+ Path(path / "root/b.py"),
+ Path(path / "root/c.py"),
+ Path(path / "root/child/c.py"),
+ ]
+ this_abs = THIS_DIR.resolve()
+ sources = list(
+ black.gen_python_files(
+ path.iterdir(),
+ this_abs,
+ include,
+ exclude,
+ None,
+ None,
+ report,
+ root_gitignore,
+ verbose=False,
+ quiet=False,
+ )
+ )
+ self.assertEqual(sorted(expected), sorted(sources))
+
+ def test_invalid_gitignore(self) -> None:
+ path = THIS_DIR / "data" / "invalid_gitignore_tests"
+ empty_config = path / "pyproject.toml"
+ result = BlackRunner().invoke(
+ black.main, ["--verbose", "--config", str(empty_config), str(path)]
+ )
+ assert result.exit_code == 1
+ assert result.stderr_bytes is not None
+
+ gitignore = path / ".gitignore"
+ assert f"Could not parse {gitignore}" in result.stderr_bytes.decode()
+
+ def test_invalid_nested_gitignore(self) -> None:
+ path = THIS_DIR / "data" / "invalid_nested_gitignore_tests"
+ empty_config = path / "pyproject.toml"
+ result = BlackRunner().invoke(
+ black.main, ["--verbose", "--config", str(empty_config), str(path)]
+ )
+ assert result.exit_code == 1
+ assert result.stderr_bytes is not None
+
+ gitignore = path / "a" / ".gitignore"
+ assert f"Could not parse {gitignore}" in result.stderr_bytes.decode()
+