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

Add STDIN test to primer #2315

Merged
merged 4 commits into from Jun 11, 2021
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
6 changes: 6 additions & 0 deletions CHANGES.md
@@ -1,5 +1,11 @@
# Change Log

## Unreleased

### _Black_

- Add primer support and test for code piped into black via STDIN (#2315)

## 21.6b0

### _Black_
Expand Down
45 changes: 34 additions & 11 deletions src/black_primer/lib.py
Expand Up @@ -42,16 +42,18 @@ async def _gen_check_output(
timeout: float = 600,
env: Optional[Dict[str, str]] = None,
cwd: Optional[Path] = None,
stdin: Optional[bytes] = None,
) -> Tuple[bytes, bytes]:
process = await asyncio.create_subprocess_exec(
*cmd,
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.STDOUT,
env=env,
cwd=cwd,
)
try:
(stdout, stderr) = await asyncio.wait_for(process.communicate(), timeout)
(stdout, stderr) = await asyncio.wait_for(process.communicate(stdin), timeout)
except asyncio.TimeoutError:
process.kill()
await process.wait()
Expand Down Expand Up @@ -112,20 +114,35 @@ def analyze_results(project_count: int, results: Results) -> int:


async def black_run(
repo_path: Path,
project_name: str,
repo_path: Optional[Path],
project_config: Dict[str, Any],
results: Results,
no_diff: bool = False,
) -> None:
"""Run Black and record failures"""
if not repo_path:
results.stats["failed"] += 1
results.failed_projects[project_name] = CalledProcessError(
69, [], f"{project_name} has no repo_path: {repo_path}".encode(), b""
)
return

stdin_test = project_name.upper() == "STDIN"
cmd = [str(which(BLACK_BINARY))]
if "cli_arguments" in project_config and project_config["cli_arguments"]:
cmd.extend(project_config["cli_arguments"])
cmd.append("--check")
if no_diff:
cmd.append(".")
if not no_diff:
cmd.append("--diff")

# Workout if we should read in a python file or search from cwd
stdin = None
if stdin_test:
cmd.append("-")
stdin = repo_path.read_bytes()
else:
cmd.extend(["--diff", "."])
cmd.append(".")

with TemporaryDirectory() as tmp_path:
# Prevent reading top-level user configs by manipulating environment variables
Expand All @@ -135,8 +152,11 @@ async def black_run(
"USERPROFILE": tmp_path, # Windows (changes `Path.home()` output)
}

cwd_path = repo_path.parent if stdin_test else repo_path
try:
_stdout, _stderr = await _gen_check_output(cmd, cwd=repo_path, env=env)
_stdout, _stderr = await _gen_check_output(
cmd, cwd=cwd_path, env=env, stdin=stdin
)
except asyncio.TimeoutError:
results.stats["failed"] += 1
LOG.error(f"Running black for {repo_path} timed out ({cmd})")
Expand Down Expand Up @@ -289,12 +309,15 @@ async def project_runner(
LOG.debug(f"Skipping {project_name} as it's configured as a long checkout")
continue

repo_path = await git_checkout_or_rebase(work_path, project_config, rebase)
if not repo_path:
continue
await black_run(repo_path, project_config, results, no_diff)
repo_path: Optional[Path] = Path(__file__)
stdin_project = project_name.upper() == "STDIN"
if not stdin_project:
repo_path = await git_checkout_or_rebase(work_path, project_config, rebase)
if not repo_path:
continue
await black_run(project_name, repo_path, project_config, results, no_diff)

if not keep:
if not keep and not stdin_project:
LOG.debug(f"Removing {repo_path}")
rmtree_partial = partial(
rmtree, path=repo_path, onerror=handle_PermissionError
Expand Down
7 changes: 7 additions & 0 deletions src/black_primer/primer.json
@@ -1,6 +1,13 @@
{
"configuration_format_version": 20200509,
"projects": {
"STDIN": {
"cli_arguments": ["--experimental-string-processing"],
"expect_formatting_changes": false,
"git_clone_url": "",
"long_checkout": false,
"py_versions": ["all"]
},
"aioexabgp": {
"cli_arguments": ["--experimental-string-processing"],
"expect_formatting_changes": false,
Expand Down
17 changes: 13 additions & 4 deletions tests/test_primer.py
Expand Up @@ -106,35 +106,44 @@ def test_analyze_results(self) -> None:
def test_black_run(self) -> None:
"""Pretend to run Black to ensure we cater for all scenarios"""
loop = asyncio.get_event_loop()
project_name = "unittest"
repo_path = Path(gettempdir())
project_config = deepcopy(FAKE_PROJECT_CONFIG)
results = lib.Results({"failed": 0, "success": 0}, {})

# Test a successful Black run
with patch("black_primer.lib._gen_check_output", return_subproccess_output):
loop.run_until_complete(lib.black_run(repo_path, project_config, results))
loop.run_until_complete(
lib.black_run(project_name, repo_path, project_config, results)
)
self.assertEqual(1, results.stats["success"])
self.assertFalse(results.failed_projects)

# Test a fail based on expecting formatting changes but not getting any
project_config["expect_formatting_changes"] = True
results = lib.Results({"failed": 0, "success": 0}, {})
with patch("black_primer.lib._gen_check_output", return_subproccess_output):
loop.run_until_complete(lib.black_run(repo_path, project_config, results))
loop.run_until_complete(
lib.black_run(project_name, repo_path, project_config, results)
)
self.assertEqual(1, results.stats["failed"])
self.assertTrue(results.failed_projects)

# Test a fail based on returning 1 and not expecting formatting changes
project_config["expect_formatting_changes"] = False
results = lib.Results({"failed": 0, "success": 0}, {})
with patch("black_primer.lib._gen_check_output", raise_subprocess_error_1):
loop.run_until_complete(lib.black_run(repo_path, project_config, results))
loop.run_until_complete(
lib.black_run(project_name, repo_path, project_config, results)
)
self.assertEqual(1, results.stats["failed"])
self.assertTrue(results.failed_projects)

# Test a formatting error based on returning 123
with patch("black_primer.lib._gen_check_output", raise_subprocess_error_123):
loop.run_until_complete(lib.black_run(repo_path, project_config, results))
loop.run_until_complete(
lib.black_run(project_name, repo_path, project_config, results)
)
self.assertEqual(2, results.stats["failed"])

@event_loop()
Expand Down