From c0a7582e3d4cc8bec3b7f5a6c52b36880dcb57d7 Mon Sep 17 00:00:00 2001 From: Allan Simon Date: Fri, 8 May 2020 15:46:07 +0200 Subject: [PATCH 1/1] permits black to run in AWS Lambda: (#1141) AWS Lambda and some other virtualized environment may not permit access to /dev/shm on Linux and as such, trying to use ProcessPoolExecutor will fail. As using parallelism is only a 'nice to have' feature of black, if it fails we gracefully fallback to a monoprocess implementation, which permits black to finish normally. Co-authored-by: Allan Simon --- black.py | 14 +++++++++++--- tests/test_black.py | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/black.py b/black.py index 2a913fc..fc1597a 100644 --- a/black.py +++ b/black.py @@ -618,7 +618,14 @@ def reformat_many( if sys.platform == "win32": # Work around https://bugs.python.org/issue26903 worker_count = min(worker_count, 61) - executor = ProcessPoolExecutor(max_workers=worker_count) + try: + executor = ProcessPoolExecutor(max_workers=worker_count) + except OSError: + # we arrive here if the underlying system does not support multi-processing + # like in AWS Lambda, in which case we gracefully fallback to the default + # mono-process Executor by using None + executor = None + try: loop.run_until_complete( schedule_formatting( @@ -633,7 +640,8 @@ def reformat_many( ) finally: shutdown(loop) - executor.shutdown() + if executor is not None: + executor.shutdown() async def schedule_formatting( @@ -643,7 +651,7 @@ async def schedule_formatting( mode: Mode, report: "Report", loop: asyncio.AbstractEventLoop, - executor: Executor, + executor: Optional[Executor], ) -> None: """Run formatting of `sources` in parallel using the provided `executor`. diff --git a/tests/test_black.py b/tests/test_black.py index 11bba1f..a0e57dc 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -1273,6 +1273,27 @@ class BlackTestCase(unittest.TestCase): self.assertIn(one, cache) self.assertIn(two, cache) + @patch("black.ProcessPoolExecutor", autospec=True) + def test_works_in_mono_process_only_environment(self, mock_executor) -> None: + mock_executor.side_effect = OSError() + mode = black.FileMode() + with cache_dir() as workspace: + one = (workspace / "one.py").resolve() + with one.open("w") as fobj: + fobj.write("print('hello')") + two = (workspace / "two.py").resolve() + with two.open("w") as fobj: + fobj.write("print('hello')") + black.write_cache({}, [one], mode) + self.invokeBlack([str(workspace)]) + with one.open("r") as fobj: + self.assertEqual(fobj.read(), "print('hello')") + with two.open("r") as fobj: + self.assertEqual(fobj.read(), 'print("hello")\n') + cache = black.read_cache(mode) + self.assertIn(one, cache) + self.assertIn(two, cache) + def test_no_cache_when_writeback_diff(self) -> None: mode = black.FileMode() with cache_dir() as workspace: -- 2.39.5