From 13b8e2cbfc7b29ff5fe68b848706580c776c55aa Mon Sep 17 00:00:00 2001 From: saygox Date: Fri, 14 Aug 2020 08:52:47 +0900 Subject: [PATCH 01/17] feat(commands/commit): apply prepare-commit-msg hook add --commit-msg-file argument --- .pre-commit-hooks.yaml | 9 ++++++ commitizen/cli.py | 8 +++++ commitizen/commands/commit.py | 42 ++++++++++++++++++++++++++- docs/getting_started.md | 5 +++- tests/commands/test_commit_command.py | 38 ++++++++++++++++++++++++ 5 files changed, 100 insertions(+), 2 deletions(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 2a3a08848..708942b15 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -25,3 +25,12 @@ language: python language_version: python3 minimum_pre_commit_version: "1.4.3" + +- id: commitizen-prepare-commit-msg + name: commitizen prepare commit msg + description: "prepare commit message" + entry: cz commit --commit-msg-file + language: python + language_version: python3 + require_serial: true + minimum_pre_commit_version: "1.4.3" diff --git a/commitizen/cli.py b/commitizen/cli.py index 0b411cba6..b5425f5c6 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -174,6 +174,14 @@ def __call__( "dest": "double_dash", "help": "Positional arguments separator (recommended)", }, + { + "name": "--commit-msg-file", + "help": ( + "ask for the name of the temporal file that contains " + "the commit message. " + "Using it in a git hook script: MSG_FILE=$1" + ), + }, ], }, { diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index abecb3b3c..611804a7e 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -4,6 +4,7 @@ import os import shutil import subprocess +import sys import tempfile import questionary @@ -26,6 +27,21 @@ from commitizen.git import smart_open +class WrapStdin: + def __init__(self): + fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) + tty = open(fd, "wb+", buffering=0) + self.tty = tty + + def __getattr__(self, key): + if key == "encoding": + return "UTF-8" + return getattr(self.tty, key) + + def __del__(self): + self.tty.close() + + class Commit: """Show prompt for the user to create a guided commit.""" @@ -105,6 +121,15 @@ def __call__(self): if is_all: c = git.add("-u") + commit_msg_file: str = self.arguments.get("commit_msg_file") + if commit_msg_file: + old_stdin = sys.stdin + old_stdout = sys.stdout + old_stderr = sys.stderr + sys.stdin = WrapStdin() + sys.stdout = open("/dev/tty", "w") + sys.stderr = open("/dev/tty", "w") + if git.is_staging_clean() and not (dry_run or allow_empty): raise NothingToCommitError("No files added to staging!") @@ -138,9 +163,24 @@ def __call__(self): if dry_run: raise DryRunExit() + if commit_msg_file: + sys.stdin.close() + sys.stdout.close() + sys.stderr.close() + sys.stdin = old_stdin + sys.stdout = old_stdout + sys.stderr = old_stderr + defaultmesaage = "" + with open(commit_msg_file) as f: + defaultmesaage = f.read() + with open(commit_msg_file, "w") as f: + f.write(m) + f.write(defaultmesaage) + out.success("Commit message is successful!") + return + always_signoff: bool = self.config.settings["always_signoff"] signoff: bool = self.arguments.get("signoff") - if signoff: out.warn( "signoff mechanic is deprecated, please use `cz commit -- -s` instead." diff --git a/docs/getting_started.md b/docs/getting_started.md index 378b81919..594216c3d 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -95,12 +95,14 @@ repos: - id: commitizen - id: commitizen-branch stages: [push] + - id: commitizen-prepare-commit-msg + stages: [prepare-commit-msg] ``` After the configuration is added, you'll need to run: ```sh -pre-commit install --hook-type commit-msg --hook-type pre-push +pre-commit install --hook-type commit-msg --hook-type pre-push --hook-type prepare-commit-msg ``` If you aren't using both hooks, you needn't install both stages. @@ -109,6 +111,7 @@ If you aren't using both hooks, you needn't install both stages. | ----------------- | ----------------- | | commitizen | commit-msg | | commitizen-branch | pre-push | +| commitizen-prepare-commit-msg | prepare-commit-msg | Note that pre-commit discourages using `master` as a revision, and the above command will print a warning. You should replace the `master` revision with the [latest tag](https://github.com/commitizen-tools/commitizen/tags). This can be done automatically with: diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py index 55751f690..7292e1b56 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -525,3 +525,41 @@ def test_commit_command_shows_description_when_use_help_option( out, _ = capsys.readouterr() file_regression.check(out, extension=".txt") + + +def test_commit_from_pre_commit_msg_hook(config, mocker, capsys): + testargs = ["cz", "commit", "--commit-msg-file", "some_file"] + mocker.patch.object(sys, "argv", testargs) + + prompt_mock = mocker.patch("questionary.prompt") + prompt_mock.return_value = { + "prefix": "feat", + "subject": "user created", + "scope": "", + "is_breaking_change": False, + "body": "", + "footer": "", + } + + commit_mock = mocker.patch("commitizen.git.commit") + mocker.patch("commitizen.commands.commit.WrapStdin") + mocker.patch("os.open") + reader_mock = mocker.mock_open(read_data="\n\n#test\n") + mocker.patch("builtins.open", reader_mock, create=True) + + cli.main() + + out, _ = capsys.readouterr() + assert "Commit message is successful!" in out + commit_mock.assert_not_called() + + +def test_WrapStdin(mocker): + mocker.patch("os.open") + reader_mock = mocker.mock_open(read_data="data") + mocker.patch("builtins.open", reader_mock, create=True) + + wrap_stdin = commands.commit.WrapStdin() + + assert wrap_stdin.encoding == "UTF-8" + assert wrap_stdin.read() == "data" From fadfb7a9c6d5e8fcb36fae08355bf1edc305cbe7 Mon Sep 17 00:00:00 2001 From: saygox Date: Sat, 6 Nov 2021 22:40:30 +0900 Subject: [PATCH 02/17] feat(commands/commit): apply prepare-commit-msg hook for Mac --- commitizen/commands/commit.py | 54 +++++++++++++++++++-------- tests/commands/test_commit_command.py | 34 +++++++++++++++-- 2 files changed, 69 insertions(+), 19 deletions(-) diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index 611804a7e..b110bd999 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -2,10 +2,13 @@ import contextlib import os +import selectors import shutil import subprocess import sys import tempfile +from asyncio import DefaultEventLoopPolicy, get_event_loop_policy, set_event_loop_policy +from io import IOBase import questionary @@ -27,14 +30,31 @@ from commitizen.git import smart_open -class WrapStdin: - def __init__(self): - fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) - tty = open(fd, "wb+", buffering=0) +class CZEventLoopPolicy(DefaultEventLoopPolicy): + def get_event_loop(self): + self.set_event_loop(self._loop_factory(selectors.SelectSelector())) + return self._local._loop + + +class WrapStdx: + def __init__(self, stdx: IOBase): + self._fileno = stdx.fileno() + if sys.platform == "linux": + if self._fileno == 0: + fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) + tty = open(fd, "wb+", buffering=0) + else: + tty = open("/dev/tty", "w") + else: + fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) + if self._fileno == 0: + tty = open(fd, "wb+", buffering=0) + else: + tty = open(fd, "rb+", buffering=0) self.tty = tty def __getattr__(self, key): - if key == "encoding": + if key == "encoding" and (sys.platform != "linux" or self._fileno == 0): return "UTF-8" return getattr(self.tty, key) @@ -126,9 +146,11 @@ def __call__(self): old_stdin = sys.stdin old_stdout = sys.stdout old_stderr = sys.stderr - sys.stdin = WrapStdin() - sys.stdout = open("/dev/tty", "w") - sys.stderr = open("/dev/tty", "w") + old_event_loop_policy = get_event_loop_policy() + set_event_loop_policy(CZEventLoopPolicy()) + sys.stdin = WrapStdx(sys.stdin) + sys.stdout = WrapStdx(sys.stdout) + sys.stderr = WrapStdx(sys.stderr) if git.is_staging_clean() and not (dry_run or allow_empty): raise NothingToCommitError("No files added to staging!") @@ -151,9 +173,17 @@ def __call__(self): else: m = self.prompt_commit_questions() + if commit_msg_file: + sys.stdin.close() + sys.stdout.close() + sys.stderr.close() + set_event_loop_policy(old_event_loop_policy) + sys.stdin = old_stdin + sys.stdout = old_stdout + sys.stderr = old_stderr + if manual_edit: m = self.manual_edit(m) - out.info(f"\n{m}\n") if write_message_to_file: @@ -164,12 +194,6 @@ def __call__(self): raise DryRunExit() if commit_msg_file: - sys.stdin.close() - sys.stdout.close() - sys.stderr.close() - sys.stdin = old_stdin - sys.stdout = old_stdout - sys.stderr = old_stderr defaultmesaage = "" with open(commit_msg_file) as f: defaultmesaage = f.read() diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py index 7292e1b56..67712b1d0 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -542,7 +542,8 @@ def test_commit_from_pre_commit_msg_hook(config, mocker, capsys): } commit_mock = mocker.patch("commitizen.git.commit") - mocker.patch("commitizen.commands.commit.WrapStdin") + commit_mock.return_value = cmd.Command("success", "", "", "", 0) + mocker.patch("commitizen.commands.commit.WrapStdx") mocker.patch("os.open") reader_mock = mocker.mock_open(read_data="\n\n#test\n") mocker.patch("builtins.open", reader_mock, create=True) @@ -553,13 +554,38 @@ def test_commit_from_pre_commit_msg_hook(config, mocker, capsys): assert "Commit message is successful!" in out commit_mock.assert_not_called() - -def test_WrapStdin(mocker): +def test_WrapStdx(mocker): mocker.patch("os.open") reader_mock = mocker.mock_open(read_data="data") mocker.patch("builtins.open", reader_mock, create=True) - wrap_stdin = commands.commit.WrapStdin() + stdin_mock_fileno=mocker.patch.object(sys.stdin, 'fileno') + stdin_mock_fileno.return_value = 0 + wrap_stdin = commands.commit.WrapStdx(sys.stdin) assert wrap_stdin.encoding == "UTF-8" assert wrap_stdin.read() == "data" + + + writer_mock = mocker.mock_open(read_data="data") + mocker.patch("builtins.open", writer_mock, create=True) + stdout_mock_fileno=mocker.patch.object(sys.stdout, 'fileno') + stdout_mock_fileno.return_value = 1 + wrap_stout = commands.commit.WrapStdx(sys.stdout) + wrap_stout.write("data") + + writer_mock.assert_called_once_with("/dev/tty", 'w') + writer_mock().write.assert_called_once_with("data") + + + writer_mock = mocker.mock_open(read_data="data") + mocker.patch("builtins.open", writer_mock, create=True) + stderr_mock_fileno=mocker.patch.object(sys.stdout, 'fileno') + stderr_mock_fileno.return_value = 2 + wrap_sterr = commands.commit.WrapStdx(sys.stderr) + + + wrap_sterr.write("data") + + writer_mock.assert_called_once_with("/dev/tty", 'w') + writer_mock().write.assert_called_once_with("data") From 581233b70ee1515e6e71aef989e94dad5350348f Mon Sep 17 00:00:00 2001 From: saygox Date: Sun, 7 Nov 2021 16:08:21 +0900 Subject: [PATCH 03/17] test(commands/commit): change prepare-commit-msg hook for Mac --- tests/commands/test_commit_command.py | 10 ++++++++-- ..._command_shows_description_when_use_help_option.txt | 5 +++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py index 67712b1d0..7c5cb3189 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -574,7 +574,10 @@ def test_WrapStdx(mocker): wrap_stout = commands.commit.WrapStdx(sys.stdout) wrap_stout.write("data") - writer_mock.assert_called_once_with("/dev/tty", 'w') + if sys.platform == 'linux': + writer_mock.assert_called_once_with("/dev/tty", 'w') + else: + pass writer_mock().write.assert_called_once_with("data") @@ -587,5 +590,8 @@ def test_WrapStdx(mocker): wrap_sterr.write("data") - writer_mock.assert_called_once_with("/dev/tty", 'w') + if sys.platform == 'linux': + writer_mock.assert_called_once_with("/dev/tty", 'w') + else: + pass writer_mock().write.assert_called_once_with("data") diff --git a/tests/commands/test_commit_command/test_commit_command_shows_description_when_use_help_option.txt b/tests/commands/test_commit_command/test_commit_command_shows_description_when_use_help_option.txt index dd1f53f3d..3f61c697f 100644 --- a/tests/commands/test_commit_command/test_commit_command_shows_description_when_use_help_option.txt +++ b/tests/commands/test_commit_command/test_commit_command_shows_description_when_use_help_option.txt @@ -1,6 +1,7 @@ usage: cz commit [-h] [--retry] [--no-retry] [--dry-run] [--write-message-to-file FILE_PATH] [-s] [-a] [-e] [-l MESSAGE_LENGTH_LIMIT] [--] + [--commit-msg-file COMMIT_MSG_FILE] create new commit @@ -20,3 +21,7 @@ options: -l, --message-length-limit MESSAGE_LENGTH_LIMIT length limit of the commit message; 0 for no limit -- Positional arguments separator (recommended) + --commit-msg-file COMMIT_MSG_FILE + ask for the name of the temporal file that contains + the commit message. Using it in a git hook script: + MSG_FILE=$1 From 8e2abe972232f297e65af99586a4630a5f18e27e Mon Sep 17 00:00:00 2001 From: saygox Date: Tue, 9 Nov 2021 00:37:29 +0900 Subject: [PATCH 04/17] refactor(commands/commit): change valiable by comment and use black --- commitizen/commands/commit.py | 6 +++--- tests/commands/test_commit_command.py | 18 ++++++++---------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index b110bd999..4a553db52 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -194,12 +194,12 @@ def __call__(self): raise DryRunExit() if commit_msg_file: - defaultmesaage = "" + default_message = "" with open(commit_msg_file) as f: - defaultmesaage = f.read() + default_message = f.read() with open(commit_msg_file, "w") as f: f.write(m) - f.write(defaultmesaage) + f.write(default_message) out.success("Commit message is successful!") return diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py index 7c5cb3189..3c08606ea 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -554,44 +554,42 @@ def test_commit_from_pre_commit_msg_hook(config, mocker, capsys): assert "Commit message is successful!" in out commit_mock.assert_not_called() + def test_WrapStdx(mocker): mocker.patch("os.open") reader_mock = mocker.mock_open(read_data="data") mocker.patch("builtins.open", reader_mock, create=True) - stdin_mock_fileno=mocker.patch.object(sys.stdin, 'fileno') + stdin_mock_fileno = mocker.patch.object(sys.stdin, "fileno") stdin_mock_fileno.return_value = 0 wrap_stdin = commands.commit.WrapStdx(sys.stdin) assert wrap_stdin.encoding == "UTF-8" assert wrap_stdin.read() == "data" - writer_mock = mocker.mock_open(read_data="data") mocker.patch("builtins.open", writer_mock, create=True) - stdout_mock_fileno=mocker.patch.object(sys.stdout, 'fileno') + stdout_mock_fileno = mocker.patch.object(sys.stdout, "fileno") stdout_mock_fileno.return_value = 1 wrap_stout = commands.commit.WrapStdx(sys.stdout) wrap_stout.write("data") - if sys.platform == 'linux': - writer_mock.assert_called_once_with("/dev/tty", 'w') + if sys.platform == "linux": + writer_mock.assert_called_once_with("/dev/tty", "w") else: pass writer_mock().write.assert_called_once_with("data") - writer_mock = mocker.mock_open(read_data="data") mocker.patch("builtins.open", writer_mock, create=True) - stderr_mock_fileno=mocker.patch.object(sys.stdout, 'fileno') + stderr_mock_fileno = mocker.patch.object(sys.stdout, "fileno") stderr_mock_fileno.return_value = 2 wrap_sterr = commands.commit.WrapStdx(sys.stderr) - wrap_sterr.write("data") - if sys.platform == 'linux': - writer_mock.assert_called_once_with("/dev/tty", 'w') + if sys.platform == "linux": + writer_mock.assert_called_once_with("/dev/tty", "w") else: pass writer_mock().write.assert_called_once_with("data") From 404eb95205de9721db22bd85dd0c52fc3f18bbfd Mon Sep 17 00:00:00 2001 From: saygox Date: Tue, 16 Nov 2021 21:23:41 +0900 Subject: [PATCH 05/17] refactor(commands/commit): use direct async class and open tty as binmod --- commitizen/commands/commit.py | 7 ++++--- tests/commands/test_commit_command.py | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index 4a553db52..420091b34 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -7,7 +7,8 @@ import subprocess import sys import tempfile -from asyncio import DefaultEventLoopPolicy, get_event_loop_policy, set_event_loop_policy +from asyncio import get_event_loop_policy, set_event_loop_policy +from asyncio.unix_events import _UnixDefaultEventLoopPolicy from io import IOBase import questionary @@ -30,7 +31,7 @@ from commitizen.git import smart_open -class CZEventLoopPolicy(DefaultEventLoopPolicy): +class CZEventLoopPolicy(_UnixDefaultEventLoopPolicy): def get_event_loop(self): self.set_event_loop(self._loop_factory(selectors.SelectSelector())) return self._local._loop @@ -44,7 +45,7 @@ def __init__(self, stdx: IOBase): fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) tty = open(fd, "wb+", buffering=0) else: - tty = open("/dev/tty", "w") + tty = open("/dev/tty", "wb") else: fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) if self._fileno == 0: diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py index 3c08606ea..db9db6c16 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -575,7 +575,7 @@ def test_WrapStdx(mocker): wrap_stout.write("data") if sys.platform == "linux": - writer_mock.assert_called_once_with("/dev/tty", "w") + writer_mock.assert_called_once_with("/dev/tty", "wb") else: pass writer_mock().write.assert_called_once_with("data") @@ -589,7 +589,7 @@ def test_WrapStdx(mocker): wrap_sterr.write("data") if sys.platform == "linux": - writer_mock.assert_called_once_with("/dev/tty", "w") + writer_mock.assert_called_once_with("/dev/tty", "wb") else: pass writer_mock().write.assert_called_once_with("data") From e224d8ee7f70dc40b6a6d0fb7e79b0c72541bd62 Mon Sep 17 00:00:00 2001 From: saygox Date: Tue, 16 Nov 2021 23:19:22 +0900 Subject: [PATCH 06/17] refactor(commands/commit): ignore type worrn --- commitizen/commands/commit.py | 7 +++---- tests/commands/test_commit_command.py | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index 420091b34..b458a8718 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -7,8 +7,7 @@ import subprocess import sys import tempfile -from asyncio import get_event_loop_policy, set_event_loop_policy -from asyncio.unix_events import _UnixDefaultEventLoopPolicy +from asyncio import DefaultEventLoopPolicy, get_event_loop_policy, set_event_loop_policy from io import IOBase import questionary @@ -31,7 +30,7 @@ from commitizen.git import smart_open -class CZEventLoopPolicy(_UnixDefaultEventLoopPolicy): +class CZEventLoopPolicy(DefaultEventLoopPolicy): # type: ignore def get_event_loop(self): self.set_event_loop(self._loop_factory(selectors.SelectSelector())) return self._local._loop @@ -45,7 +44,7 @@ def __init__(self, stdx: IOBase): fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) tty = open(fd, "wb+", buffering=0) else: - tty = open("/dev/tty", "wb") + tty = open("/dev/tty", "w") # type: ignore else: fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) if self._fileno == 0: diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py index db9db6c16..3c08606ea 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -575,7 +575,7 @@ def test_WrapStdx(mocker): wrap_stout.write("data") if sys.platform == "linux": - writer_mock.assert_called_once_with("/dev/tty", "wb") + writer_mock.assert_called_once_with("/dev/tty", "w") else: pass writer_mock().write.assert_called_once_with("data") @@ -589,7 +589,7 @@ def test_WrapStdx(mocker): wrap_sterr.write("data") if sys.platform == "linux": - writer_mock.assert_called_once_with("/dev/tty", "wb") + writer_mock.assert_called_once_with("/dev/tty", "w") else: pass writer_mock().write.assert_called_once_with("data") From 1fda8308a367be1dfbcd23a87e1592093d40fda6 Mon Sep 17 00:00:00 2001 From: saygox Date: Wed, 17 Nov 2021 18:52:16 +0900 Subject: [PATCH 07/17] feat(wrap_stdio): separate wrap stdio module --- commitizen/commands/commit.py | 54 ++------------------ commitizen/wrap_stdio.py | 18 +++++++ commitizen/wrap_stdio_linux.py | 55 +++++++++++++++++++++ commitizen/wrap_stdio_unix.py | 71 +++++++++++++++++++++++++++ commitizen/wrap_stdio_windows.py | 6 +++ tests/commands/test_commit_command.py | 45 ++--------------- 6 files changed, 156 insertions(+), 93 deletions(-) create mode 100644 commitizen/wrap_stdio.py create mode 100644 commitizen/wrap_stdio_linux.py create mode 100644 commitizen/wrap_stdio_unix.py create mode 100644 commitizen/wrap_stdio_windows.py diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index b458a8718..646420730 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -2,13 +2,9 @@ import contextlib import os -import selectors import shutil import subprocess -import sys import tempfile -from asyncio import DefaultEventLoopPolicy, get_event_loop_policy, set_event_loop_policy -from io import IOBase import questionary @@ -28,38 +24,7 @@ NothingToCommitError, ) from commitizen.git import smart_open - - -class CZEventLoopPolicy(DefaultEventLoopPolicy): # type: ignore - def get_event_loop(self): - self.set_event_loop(self._loop_factory(selectors.SelectSelector())) - return self._local._loop - - -class WrapStdx: - def __init__(self, stdx: IOBase): - self._fileno = stdx.fileno() - if sys.platform == "linux": - if self._fileno == 0: - fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) - tty = open(fd, "wb+", buffering=0) - else: - tty = open("/dev/tty", "w") # type: ignore - else: - fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) - if self._fileno == 0: - tty = open(fd, "wb+", buffering=0) - else: - tty = open(fd, "rb+", buffering=0) - self.tty = tty - - def __getattr__(self, key): - if key == "encoding" and (sys.platform != "linux" or self._fileno == 0): - return "UTF-8" - return getattr(self.tty, key) - - def __del__(self): - self.tty.close() +from commitizen.wrap_stdio import unwrap_stdio, wrap_stdio class Commit: @@ -143,14 +108,7 @@ def __call__(self): commit_msg_file: str = self.arguments.get("commit_msg_file") if commit_msg_file: - old_stdin = sys.stdin - old_stdout = sys.stdout - old_stderr = sys.stderr - old_event_loop_policy = get_event_loop_policy() - set_event_loop_policy(CZEventLoopPolicy()) - sys.stdin = WrapStdx(sys.stdin) - sys.stdout = WrapStdx(sys.stdout) - sys.stderr = WrapStdx(sys.stderr) + wrap_stdio() if git.is_staging_clean() and not (dry_run or allow_empty): raise NothingToCommitError("No files added to staging!") @@ -174,13 +132,7 @@ def __call__(self): m = self.prompt_commit_questions() if commit_msg_file: - sys.stdin.close() - sys.stdout.close() - sys.stderr.close() - set_event_loop_policy(old_event_loop_policy) - sys.stdin = old_stdin - sys.stdout = old_stdout - sys.stderr = old_stderr + unwrap_stdio() if manual_edit: m = self.manual_edit(m) diff --git a/commitizen/wrap_stdio.py b/commitizen/wrap_stdio.py new file mode 100644 index 000000000..db9c200b4 --- /dev/null +++ b/commitizen/wrap_stdio.py @@ -0,0 +1,18 @@ +import sys + +if sys.platform == "win32": # pragma: no cover + from .wrap_stdio_windows import _unwrap_stdio, _wrap_stdio +elif sys.platform == "linux": + from .wrap_stdio_linux import _unwrap_stdio, _wrap_stdio # pragma: no cover +else: + from .wrap_stdio_unix import _unwrap_stdio, _wrap_stdio # pragma: no cover + + +def wrap_stdio(): + _wrap_stdio() + return None + + +def unwrap_stdio(): + _unwrap_stdio() + return None diff --git a/commitizen/wrap_stdio_linux.py b/commitizen/wrap_stdio_linux.py new file mode 100644 index 000000000..1ec0315b1 --- /dev/null +++ b/commitizen/wrap_stdio_linux.py @@ -0,0 +1,55 @@ +import os +import sys +from io import IOBase + + +class WrapStdioLinux: + def __init__(self, stdx: IOBase): + self._fileno = stdx.fileno() + if self._fileno == 0: + fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) + tty = open(fd, "wb+", buffering=0) + else: + tty = open("/dev/tty", "w") # type: ignore + self.tty = tty + + def __getattr__(self, key): + if key == "encoding" and self._fileno == 0: + return "UTF-8" + return getattr(self.tty, key) + + def __del__(self): + self.tty.close() + + +backup_stdin = None +backup_stdout = None +backup_stderr = None + + +def _wrap_stdio(): + global backup_stdin + backup_stdin = sys.stdin + sys.stdin = WrapStdioLinux(sys.stdin) + + global backup_stdout + backup_stdout = sys.stdout + sys.stdout = WrapStdioLinux(sys.stdout) + + global backup_stderr + backup_stderr = sys.stderr + sys.stderr = WrapStdioLinux(sys.stderr) + + +def _unwrap_stdio(): + global backup_stdin + sys.stdin.close() + sys.stdin = backup_stdin + + global backup_stdout + sys.stdout.close() + sys.stdout = backup_stdout + + global backup_stderr + sys.stderr.close() + sys.stderr = backup_stderr diff --git a/commitizen/wrap_stdio_unix.py b/commitizen/wrap_stdio_unix.py new file mode 100644 index 000000000..21fc52dea --- /dev/null +++ b/commitizen/wrap_stdio_unix.py @@ -0,0 +1,71 @@ +import os +import selectors +import sys +from asyncio import DefaultEventLoopPolicy, get_event_loop_policy, set_event_loop_policy +from io import IOBase + + +class CZEventLoopPolicy(DefaultEventLoopPolicy): # type: ignore + def get_event_loop(self): + self.set_event_loop(self._loop_factory(selectors.SelectSelector())) + return self._local._loop + + +class WrapStdioLinux: + def __init__(self, stdx: IOBase): + self._fileno = stdx.fileno() + fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) + if self._fileno == 0: + tty = open(fd, "wb+", buffering=0) + else: + tty = open(fd, "rb+", buffering=0) + self.tty = tty + + def __getattr__(self, key): + if key == "encoding": + return "UTF-8" + return getattr(self.tty, key) + + def __del__(self): + self.tty.close() + + +backup_event_loop_policy = None +backup_stdin = None +backup_stdout = None +backup_stderr = None + + +def _wrap_stdio(): + global backup_event_loop_policy + backup_event_loop_policy = get_event_loop_policy() + set_event_loop_policy(CZEventLoopPolicy()) + + global backup_stdin + backup_stdin = sys.stdin + sys.stdin = WrapStdioLinux(sys.stdin) + + global backup_stdout + backup_stdout = sys.stdout + sys.stdout = WrapStdioLinux(sys.stdout) + + global backup_stderr + backup_stdout = sys.stderr + sys.stderr = WrapStdioLinux(sys.stderr) + + +def _unwrap_stdio(): + global backup_event_loop_policy + set_event_loop_policy(backup_event_loop_policy) + + global backup_stdin + sys.stdin.close() + sys.stdin = backup_stdin + + global backup_stdout + sys.stdout.close() + sys.stdout = backup_stdout + + global backup_stderr + sys.stderr.close() + sys.stderr = backup_stderr diff --git a/commitizen/wrap_stdio_windows.py b/commitizen/wrap_stdio_windows.py new file mode 100644 index 000000000..a3eb1826c --- /dev/null +++ b/commitizen/wrap_stdio_windows.py @@ -0,0 +1,6 @@ +def _wrap_stdio(): + pass + + +def _unwrap_stdio(): + pass diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py index 3c08606ea..f70231741 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -543,8 +543,9 @@ def test_commit_from_pre_commit_msg_hook(config, mocker, capsys): commit_mock = mocker.patch("commitizen.git.commit") commit_mock.return_value = cmd.Command("success", "", "", "", 0) - mocker.patch("commitizen.commands.commit.WrapStdx") - mocker.patch("os.open") + + mocker.patch("commitizen.commands.commit.wrap_stdio") + mocker.patch("commitizen.commands.commit.unwrap_stdio") reader_mock = mocker.mock_open(read_data="\n\n#test\n") mocker.patch("builtins.open", reader_mock, create=True) @@ -553,43 +554,3 @@ def test_commit_from_pre_commit_msg_hook(config, mocker, capsys): out, _ = capsys.readouterr() assert "Commit message is successful!" in out commit_mock.assert_not_called() - - -def test_WrapStdx(mocker): - mocker.patch("os.open") - reader_mock = mocker.mock_open(read_data="data") - mocker.patch("builtins.open", reader_mock, create=True) - - stdin_mock_fileno = mocker.patch.object(sys.stdin, "fileno") - stdin_mock_fileno.return_value = 0 - wrap_stdin = commands.commit.WrapStdx(sys.stdin) - - assert wrap_stdin.encoding == "UTF-8" - assert wrap_stdin.read() == "data" - - writer_mock = mocker.mock_open(read_data="data") - mocker.patch("builtins.open", writer_mock, create=True) - stdout_mock_fileno = mocker.patch.object(sys.stdout, "fileno") - stdout_mock_fileno.return_value = 1 - wrap_stout = commands.commit.WrapStdx(sys.stdout) - wrap_stout.write("data") - - if sys.platform == "linux": - writer_mock.assert_called_once_with("/dev/tty", "w") - else: - pass - writer_mock().write.assert_called_once_with("data") - - writer_mock = mocker.mock_open(read_data="data") - mocker.patch("builtins.open", writer_mock, create=True) - stderr_mock_fileno = mocker.patch.object(sys.stdout, "fileno") - stderr_mock_fileno.return_value = 2 - wrap_sterr = commands.commit.WrapStdx(sys.stderr) - - wrap_sterr.write("data") - - if sys.platform == "linux": - writer_mock.assert_called_once_with("/dev/tty", "w") - else: - pass - writer_mock().write.assert_called_once_with("data") From a5f7d99700d9d538db01ac95e9e5a53055d6c56f Mon Sep 17 00:00:00 2001 From: saygox Date: Thu, 18 Nov 2021 00:51:03 +0900 Subject: [PATCH 08/17] feat(commands/commit): apply prepare-commit-msg hook for Windows --- commitizen/wrap_stdio_unix.py | 8 ++--- commitizen/wrap_stdio_windows.py | 60 +++++++++++++++++++++++++++++--- 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/commitizen/wrap_stdio_unix.py b/commitizen/wrap_stdio_unix.py index 21fc52dea..939858b28 100644 --- a/commitizen/wrap_stdio_unix.py +++ b/commitizen/wrap_stdio_unix.py @@ -11,7 +11,7 @@ def get_event_loop(self): return self._local._loop -class WrapStdioLinux: +class WrapStdioUnix: def __init__(self, stdx: IOBase): self._fileno = stdx.fileno() fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) @@ -43,15 +43,15 @@ def _wrap_stdio(): global backup_stdin backup_stdin = sys.stdin - sys.stdin = WrapStdioLinux(sys.stdin) + sys.stdin = WrapStdioUnix(sys.stdin) global backup_stdout backup_stdout = sys.stdout - sys.stdout = WrapStdioLinux(sys.stdout) + sys.stdout = WrapStdioUnix(sys.stdout) global backup_stderr backup_stdout = sys.stderr - sys.stderr = WrapStdioLinux(sys.stderr) + sys.stderr = WrapStdioUnix(sys.stderr) def _unwrap_stdio(): diff --git a/commitizen/wrap_stdio_windows.py b/commitizen/wrap_stdio_windows.py index a3eb1826c..9428f287f 100644 --- a/commitizen/wrap_stdio_windows.py +++ b/commitizen/wrap_stdio_windows.py @@ -1,6 +1,58 @@ -def _wrap_stdio(): - pass +import sys +if sys.platform == "win32": # pragma: no cover + import msvcrt + import os + from ctypes import c_ulong, windll # noqa + from ctypes.wintypes import HANDLE + from io import IOBase -def _unwrap_stdio(): - pass + STD_INPUT_HANDLE = c_ulong(-10) + STD_OUTPUT_HANDLE = c_ulong(-11) + + class WrapStdioWindows: + def __init__(self, stdx: IOBase): + self._fileno = stdx.fileno() + if self._fileno == 0: + fd = os.open("CONIN$", os.O_RDWR | os.O_BINARY) + tty = open(fd, "r") + handle = HANDLE(msvcrt.get_osfhandle(fd)) # noqa + windll.kernel32.SetStdHandle(STD_INPUT_HANDLE, handle) + elif self._fileno == 1: + fd = os.open("CONOUT$", os.O_RDWR | os.O_BINARY) + tty = open(fd, "w") + handle = HANDLE(msvcrt.get_osfhandle(fd)) # noqa + windll.kernel32.SetStdHandle(STD_OUTPUT_HANDLE, handle) + else: + raise Exception("not defined type") + self._tty = tty + + def __getattr__(self, key): + if key == "encoding" and self._fileno == 0: + return "UTF-8" + return getattr(self._tty, key) + + def __del__(self): + if "_tty" in self.__dict__: + self._tty.close() + + backup_stdin = None + backup_stdout = None + + def _wrap_stdio(): + global backup_stdin + backup_stdin = sys.stdin + sys.stdin = WrapStdioWindows(sys.stdin) + + global backup_stdout + backup_stdout = sys.stdout + sys.stdout = WrapStdioWindows(sys.stdout) + + def _unwrap_stdio(): + global backup_stdin + sys.stdin.close() + sys.stdin = backup_stdin + + global backup_stdout + sys.stdout.close() + sys.stdout = backup_stdout From 94ce1d72992eedb9b60188242519cee2440a782d Mon Sep 17 00:00:00 2001 From: saygox Date: Fri, 19 Nov 2021 23:11:18 +0900 Subject: [PATCH 09/17] test(wrap_stdio): does not load other platform --- commitizen/wrap_stdio_linux.py | 104 ++++++++++++------------ commitizen/wrap_stdio_unix.py | 139 +++++++++++++++++---------------- 2 files changed, 121 insertions(+), 122 deletions(-) diff --git a/commitizen/wrap_stdio_linux.py b/commitizen/wrap_stdio_linux.py index 1ec0315b1..33d462e93 100644 --- a/commitizen/wrap_stdio_linux.py +++ b/commitizen/wrap_stdio_linux.py @@ -1,55 +1,53 @@ -import os import sys -from io import IOBase - -class WrapStdioLinux: - def __init__(self, stdx: IOBase): - self._fileno = stdx.fileno() - if self._fileno == 0: - fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) - tty = open(fd, "wb+", buffering=0) - else: - tty = open("/dev/tty", "w") # type: ignore - self.tty = tty - - def __getattr__(self, key): - if key == "encoding" and self._fileno == 0: - return "UTF-8" - return getattr(self.tty, key) - - def __del__(self): - self.tty.close() - - -backup_stdin = None -backup_stdout = None -backup_stderr = None - - -def _wrap_stdio(): - global backup_stdin - backup_stdin = sys.stdin - sys.stdin = WrapStdioLinux(sys.stdin) - - global backup_stdout - backup_stdout = sys.stdout - sys.stdout = WrapStdioLinux(sys.stdout) - - global backup_stderr - backup_stderr = sys.stderr - sys.stderr = WrapStdioLinux(sys.stderr) - - -def _unwrap_stdio(): - global backup_stdin - sys.stdin.close() - sys.stdin = backup_stdin - - global backup_stdout - sys.stdout.close() - sys.stdout = backup_stdout - - global backup_stderr - sys.stderr.close() - sys.stderr = backup_stderr +if sys.platform == "linux": # pragma: no cover + import os + from io import IOBase + + class WrapStdioLinux: + def __init__(self, stdx: IOBase): + self._fileno = stdx.fileno() + if self._fileno == 0: + fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) + tty = open(fd, "wb+", buffering=0) + else: + tty = open("/dev/tty", "w") # type: ignore + self.tty = tty + + def __getattr__(self, key): + if key == "encoding" and self._fileno == 0: + return "UTF-8" + return getattr(self.tty, key) + + def __del__(self): + self.tty.close() + + backup_stdin = None + backup_stdout = None + backup_stderr = None + + def _wrap_stdio(): + global backup_stdin + backup_stdin = sys.stdin + sys.stdin = WrapStdioLinux(sys.stdin) + + global backup_stdout + backup_stdout = sys.stdout + sys.stdout = WrapStdioLinux(sys.stdout) + + global backup_stderr + backup_stderr = sys.stderr + sys.stderr = WrapStdioLinux(sys.stderr) + + def _unwrap_stdio(): + global backup_stdin + sys.stdin.close() + sys.stdin = backup_stdin + + global backup_stdout + sys.stdout.close() + sys.stdout = backup_stdout + + global backup_stderr + sys.stderr.close() + sys.stderr = backup_stderr diff --git a/commitizen/wrap_stdio_unix.py b/commitizen/wrap_stdio_unix.py index 939858b28..2f2f34663 100644 --- a/commitizen/wrap_stdio_unix.py +++ b/commitizen/wrap_stdio_unix.py @@ -1,71 +1,72 @@ -import os -import selectors import sys -from asyncio import DefaultEventLoopPolicy, get_event_loop_policy, set_event_loop_policy -from io import IOBase - -class CZEventLoopPolicy(DefaultEventLoopPolicy): # type: ignore - def get_event_loop(self): - self.set_event_loop(self._loop_factory(selectors.SelectSelector())) - return self._local._loop - - -class WrapStdioUnix: - def __init__(self, stdx: IOBase): - self._fileno = stdx.fileno() - fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) - if self._fileno == 0: - tty = open(fd, "wb+", buffering=0) - else: - tty = open(fd, "rb+", buffering=0) - self.tty = tty - - def __getattr__(self, key): - if key == "encoding": - return "UTF-8" - return getattr(self.tty, key) - - def __del__(self): - self.tty.close() - - -backup_event_loop_policy = None -backup_stdin = None -backup_stdout = None -backup_stderr = None - - -def _wrap_stdio(): - global backup_event_loop_policy - backup_event_loop_policy = get_event_loop_policy() - set_event_loop_policy(CZEventLoopPolicy()) - - global backup_stdin - backup_stdin = sys.stdin - sys.stdin = WrapStdioUnix(sys.stdin) - - global backup_stdout - backup_stdout = sys.stdout - sys.stdout = WrapStdioUnix(sys.stdout) - - global backup_stderr - backup_stdout = sys.stderr - sys.stderr = WrapStdioUnix(sys.stderr) - - -def _unwrap_stdio(): - global backup_event_loop_policy - set_event_loop_policy(backup_event_loop_policy) - - global backup_stdin - sys.stdin.close() - sys.stdin = backup_stdin - - global backup_stdout - sys.stdout.close() - sys.stdout = backup_stdout - - global backup_stderr - sys.stderr.close() - sys.stderr = backup_stderr +if sys.platform != "win32" and sys.platform != "linux": # pragma: no cover + import os + import selectors + from asyncio import ( + DefaultEventLoopPolicy, + get_event_loop_policy, + set_event_loop_policy, + ) + from io import IOBase + + class CZEventLoopPolicy(DefaultEventLoopPolicy): # pragma: no cover + def get_event_loop(self): + self.set_event_loop(self._loop_factory(selectors.SelectSelector())) + return self._local._loop + + class WrapStdioUnix: + def __init__(self, stdx: IOBase): + self._fileno = stdx.fileno() + fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) + if self._fileno == 0: + tty = open(fd, "wb+", buffering=0) + else: + tty = open(fd, "rb+", buffering=0) + self.tty = tty + + def __getattr__(self, key): + if key == "encoding": + return "UTF-8" + return getattr(self.tty, key) + + def __del__(self): + self.tty.close() + + backup_event_loop_policy = None + backup_stdin = None + backup_stdout = None + backup_stderr = None + + def _wrap_stdio(): + global backup_event_loop_policy + backup_event_loop_policy = get_event_loop_policy() + set_event_loop_policy(CZEventLoopPolicy()) + + global backup_stdin + backup_stdin = sys.stdin + sys.stdin = WrapStdioUnix(sys.stdin) + + global backup_stdout + backup_stdout = sys.stdout + sys.stdout = WrapStdioUnix(sys.stdout) + + global backup_stderr + backup_stdout = sys.stderr + sys.stderr = WrapStdioUnix(sys.stderr) + + def _unwrap_stdio(): + global backup_event_loop_policy + set_event_loop_policy(backup_event_loop_policy) + + global backup_stdin + sys.stdin.close() + sys.stdin = backup_stdin + + global backup_stdout + sys.stdout.close() + sys.stdout = backup_stdout + + global backup_stderr + sys.stderr.close() + sys.stderr = backup_stderr From 8627369d5d19840f021e64121e57487aff0f9f86 Mon Sep 17 00:00:00 2001 From: saygox Date: Sat, 20 Nov 2021 01:31:28 +0900 Subject: [PATCH 10/17] test(test_wrap_stdio): add wrap_stdio tests --- commitizen/wrap_stdio.py | 2 -- tests/test_wrap_stdio.py | 56 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 tests/test_wrap_stdio.py diff --git a/commitizen/wrap_stdio.py b/commitizen/wrap_stdio.py index db9c200b4..4bbfa0541 100644 --- a/commitizen/wrap_stdio.py +++ b/commitizen/wrap_stdio.py @@ -10,9 +10,7 @@ def wrap_stdio(): _wrap_stdio() - return None def unwrap_stdio(): _unwrap_stdio() - return None diff --git a/tests/test_wrap_stdio.py b/tests/test_wrap_stdio.py new file mode 100644 index 000000000..ed76ddc71 --- /dev/null +++ b/tests/test_wrap_stdio.py @@ -0,0 +1,56 @@ +import sys + +from commitizen import wrap_stdio, wrap_stdio_linux, wrap_stdio_unix, wrap_stdio_windows + + +def test_warp_stdio_exists(): + assert hasattr(wrap_stdio_windows, "sys") + assert hasattr(wrap_stdio_linux, "sys") + assert hasattr(wrap_stdio_unix, "sys") + + +if sys.platform == "win32": # pragma: no cover + pass +elif sys.platform == "linux": + from commitizen.wrap_stdio_linux import WrapStdioLinux + + def test_wrap_stdio_linux(mocker): + + tmp_stdin = sys.stdin + tmp_stdout = sys.stdout + tmp_stderr = sys.stderr + + mocker.patch("os.open") + readerwriter_mock = mocker.mock_open(read_data="data") + mocker.patch("builtins.open", readerwriter_mock, create=True) + + mocker.patch.object(sys.stdin, "fileno", return_value=0) + mocker.patch.object(sys.stdout, "fileno", return_value=1) + mocker.patch.object(sys.stdout, "fileno", return_value=2) + + wrap_stdio.wrap_stdio() + + assert sys.stdin != tmp_stdin + assert isinstance(sys.stdin, WrapStdioLinux) + assert sys.stdin.encoding == "UTF-8" + assert sys.stdin.read() == "data" + + assert sys.stdout != tmp_stdout + assert isinstance(sys.stdout, WrapStdioLinux) + sys.stdout.write("stdout") + readerwriter_mock().write.assert_called_with("stdout") + + assert sys.stderr != tmp_stderr + assert isinstance(sys.stderr, WrapStdioLinux) + sys.stdout.write("stderr") + readerwriter_mock().write.assert_called_with("stderr") + + wrap_stdio.unwrap_stdio() + + assert sys.stdin == tmp_stdin + assert sys.stdout == tmp_stdout + assert sys.stderr == tmp_stderr + + +else: + pass From c1018e7cfcf52df31160a63c2226ec47a00b97e7 Mon Sep 17 00:00:00 2001 From: saygox Date: Mon, 22 Nov 2021 23:13:24 +0900 Subject: [PATCH 11/17] refactor(wrap_stdio): remake classes --- commitizen/wrap_stdio_linux.py | 36 +++++++++++++++++++++------------- commitizen/wrap_stdio_unix.py | 12 ++++++------ tests/test_wrap_stdio.py | 30 ++++++++++++++++++---------- 3 files changed, 48 insertions(+), 30 deletions(-) diff --git a/commitizen/wrap_stdio_linux.py b/commitizen/wrap_stdio_linux.py index 33d462e93..e349b1fbc 100644 --- a/commitizen/wrap_stdio_linux.py +++ b/commitizen/wrap_stdio_linux.py @@ -2,26 +2,34 @@ if sys.platform == "linux": # pragma: no cover import os - from io import IOBase - - class WrapStdioLinux: - def __init__(self, stdx: IOBase): - self._fileno = stdx.fileno() - if self._fileno == 0: - fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) - tty = open(fd, "wb+", buffering=0) - else: - tty = open("/dev/tty", "w") # type: ignore + + # from io import IOBase + + class WrapStdinLinux: + def __init__(self): + fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) + tty = open(fd, "wb+", buffering=0) self.tty = tty def __getattr__(self, key): - if key == "encoding" and self._fileno == 0: + if key == "encoding": return "UTF-8" return getattr(self.tty, key) def __del__(self): self.tty.close() + class WrapStdoutLinux: + def __init__(self): + tty = open("/dev/tty", "w") + self.tty = tty + + def __getattr__(self, key): + return getattr(self.tty, key) + + def __del__(self): + self.tty.close() + backup_stdin = None backup_stdout = None backup_stderr = None @@ -29,15 +37,15 @@ def __del__(self): def _wrap_stdio(): global backup_stdin backup_stdin = sys.stdin - sys.stdin = WrapStdioLinux(sys.stdin) + sys.stdin = WrapStdinLinux() global backup_stdout backup_stdout = sys.stdout - sys.stdout = WrapStdioLinux(sys.stdout) + sys.stdout = WrapStdoutLinux() global backup_stderr backup_stderr = sys.stderr - sys.stderr = WrapStdioLinux(sys.stderr) + sys.stderr = WrapStdoutLinux() def _unwrap_stdio(): global backup_stdin diff --git a/commitizen/wrap_stdio_unix.py b/commitizen/wrap_stdio_unix.py index 2f2f34663..8b9952eab 100644 --- a/commitizen/wrap_stdio_unix.py +++ b/commitizen/wrap_stdio_unix.py @@ -5,16 +5,12 @@ import selectors from asyncio import ( DefaultEventLoopPolicy, + SelectorEventLoop, get_event_loop_policy, set_event_loop_policy, ) from io import IOBase - class CZEventLoopPolicy(DefaultEventLoopPolicy): # pragma: no cover - def get_event_loop(self): - self.set_event_loop(self._loop_factory(selectors.SelectSelector())) - return self._local._loop - class WrapStdioUnix: def __init__(self, stdx: IOBase): self._fileno = stdx.fileno() @@ -33,6 +29,7 @@ def __getattr__(self, key): def __del__(self): self.tty.close() + # backup_event_loop = None backup_event_loop_policy = None backup_stdin = None backup_stdout = None @@ -41,7 +38,10 @@ def __del__(self): def _wrap_stdio(): global backup_event_loop_policy backup_event_loop_policy = get_event_loop_policy() - set_event_loop_policy(CZEventLoopPolicy()) + + event_loop = DefaultEventLoopPolicy() + event_loop.set_event_loop(SelectorEventLoop(selectors.SelectSelector())) + set_event_loop_policy(event_loop) global backup_stdin backup_stdin = sys.stdin diff --git a/tests/test_wrap_stdio.py b/tests/test_wrap_stdio.py index ed76ddc71..bd32a8182 100644 --- a/tests/test_wrap_stdio.py +++ b/tests/test_wrap_stdio.py @@ -12,42 +12,52 @@ def test_warp_stdio_exists(): if sys.platform == "win32": # pragma: no cover pass elif sys.platform == "linux": - from commitizen.wrap_stdio_linux import WrapStdioLinux + from commitizen.wrap_stdio_linux import WrapStdinLinux, WrapStdoutLinux - def test_wrap_stdio_linux(mocker): + def test_wrap_stdin_linux(mocker): tmp_stdin = sys.stdin - tmp_stdout = sys.stdout - tmp_stderr = sys.stderr mocker.patch("os.open") readerwriter_mock = mocker.mock_open(read_data="data") mocker.patch("builtins.open", readerwriter_mock, create=True) mocker.patch.object(sys.stdin, "fileno", return_value=0) - mocker.patch.object(sys.stdout, "fileno", return_value=1) - mocker.patch.object(sys.stdout, "fileno", return_value=2) wrap_stdio.wrap_stdio() assert sys.stdin != tmp_stdin - assert isinstance(sys.stdin, WrapStdioLinux) + assert isinstance(sys.stdin, WrapStdinLinux) assert sys.stdin.encoding == "UTF-8" assert sys.stdin.read() == "data" + wrap_stdio.unwrap_stdio() + + assert sys.stdin == tmp_stdin + + def test_wrap_stdout_linux(mocker): + + tmp_stdout = sys.stdout + tmp_stderr = sys.stderr + + mocker.patch("os.open") + readerwriter_mock = mocker.mock_open(read_data="data") + mocker.patch("builtins.open", readerwriter_mock, create=True) + + wrap_stdio.wrap_stdio() + assert sys.stdout != tmp_stdout - assert isinstance(sys.stdout, WrapStdioLinux) + assert isinstance(sys.stdout, WrapStdoutLinux) sys.stdout.write("stdout") readerwriter_mock().write.assert_called_with("stdout") assert sys.stderr != tmp_stderr - assert isinstance(sys.stderr, WrapStdioLinux) + assert isinstance(sys.stderr, WrapStdoutLinux) sys.stdout.write("stderr") readerwriter_mock().write.assert_called_with("stderr") wrap_stdio.unwrap_stdio() - assert sys.stdin == tmp_stdin assert sys.stdout == tmp_stdout assert sys.stderr == tmp_stderr From 82cd4d7a7f909ae24322cce31b788f7e7576dba8 Mon Sep 17 00:00:00 2001 From: saygox Date: Mon, 6 Dec 2021 23:53:00 +0900 Subject: [PATCH 12/17] fix(commands/commit): check cz commit msg --- commitizen/commands/commit.py | 14 ++++++++++++++ commitizen/wrap_stdio.py | 16 ---------------- commitizen/wrap_stdio/__init__.py | 16 ++++++++++++++++ .../{wrap_stdio_linux.py => wrap_stdio/linux.py} | 0 .../{wrap_stdio_unix.py => wrap_stdio/unix.py} | 0 .../windows.py} | 2 +- tests/{ => wrap_stdio}/test_wrap_stdio.py | 15 ++++++--------- 7 files changed, 37 insertions(+), 26 deletions(-) delete mode 100644 commitizen/wrap_stdio.py create mode 100644 commitizen/wrap_stdio/__init__.py rename commitizen/{wrap_stdio_linux.py => wrap_stdio/linux.py} (100%) rename commitizen/{wrap_stdio_unix.py => wrap_stdio/unix.py} (100%) rename commitizen/{wrap_stdio_windows.py => wrap_stdio/windows.py} (98%) rename tests/{ => wrap_stdio}/test_wrap_stdio.py (82%) diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index 646420730..10cd6effe 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -5,6 +5,7 @@ import shutil import subprocess import tempfile +from os.path import exists import questionary @@ -93,6 +94,17 @@ def manual_edit(self, message: str) -> str: file.unlink() return message + def is_blank_commit_file(self, filename) -> bool: + if not exists(filename): + return True + with open(filename, "tr") as f: + for x in f: + if len(x) == 0 or x[0] == "#": + continue + elif x[0] != "\r" and x[0] != "\n": + return False + return True + def __call__(self): extra_args: str = self.arguments.get("extra_cli_args", "") @@ -108,6 +120,8 @@ def __call__(self): commit_msg_file: str = self.arguments.get("commit_msg_file") if commit_msg_file: + if not self.is_blank_commit_file(commit_msg_file): + return wrap_stdio() if git.is_staging_clean() and not (dry_run or allow_empty): diff --git a/commitizen/wrap_stdio.py b/commitizen/wrap_stdio.py deleted file mode 100644 index 4bbfa0541..000000000 --- a/commitizen/wrap_stdio.py +++ /dev/null @@ -1,16 +0,0 @@ -import sys - -if sys.platform == "win32": # pragma: no cover - from .wrap_stdio_windows import _unwrap_stdio, _wrap_stdio -elif sys.platform == "linux": - from .wrap_stdio_linux import _unwrap_stdio, _wrap_stdio # pragma: no cover -else: - from .wrap_stdio_unix import _unwrap_stdio, _wrap_stdio # pragma: no cover - - -def wrap_stdio(): - _wrap_stdio() - - -def unwrap_stdio(): - _unwrap_stdio() diff --git a/commitizen/wrap_stdio/__init__.py b/commitizen/wrap_stdio/__init__.py new file mode 100644 index 000000000..f5be85ec9 --- /dev/null +++ b/commitizen/wrap_stdio/__init__.py @@ -0,0 +1,16 @@ +import sys + +if sys.platform == "win32": # pragma: no cover + from .windows import _unwrap_stdio, _wrap_stdio +elif sys.platform == "linux": + from .linux import _unwrap_stdio, _wrap_stdio # pragma: no cover +else: + from .unix import _unwrap_stdio, _wrap_stdio # pragma: no cover + + +def wrap_stdio(): + _wrap_stdio() + + +def unwrap_stdio(): + _unwrap_stdio() diff --git a/commitizen/wrap_stdio_linux.py b/commitizen/wrap_stdio/linux.py similarity index 100% rename from commitizen/wrap_stdio_linux.py rename to commitizen/wrap_stdio/linux.py diff --git a/commitizen/wrap_stdio_unix.py b/commitizen/wrap_stdio/unix.py similarity index 100% rename from commitizen/wrap_stdio_unix.py rename to commitizen/wrap_stdio/unix.py diff --git a/commitizen/wrap_stdio_windows.py b/commitizen/wrap_stdio/windows.py similarity index 98% rename from commitizen/wrap_stdio_windows.py rename to commitizen/wrap_stdio/windows.py index 9428f287f..2c0856273 100644 --- a/commitizen/wrap_stdio_windows.py +++ b/commitizen/wrap_stdio/windows.py @@ -15,7 +15,7 @@ def __init__(self, stdx: IOBase): self._fileno = stdx.fileno() if self._fileno == 0: fd = os.open("CONIN$", os.O_RDWR | os.O_BINARY) - tty = open(fd, "r") + tty = open(fd) handle = HANDLE(msvcrt.get_osfhandle(fd)) # noqa windll.kernel32.SetStdHandle(STD_INPUT_HANDLE, handle) elif self._fileno == 1: diff --git a/tests/test_wrap_stdio.py b/tests/wrap_stdio/test_wrap_stdio.py similarity index 82% rename from tests/test_wrap_stdio.py rename to tests/wrap_stdio/test_wrap_stdio.py index bd32a8182..741cb2360 100644 --- a/tests/test_wrap_stdio.py +++ b/tests/wrap_stdio/test_wrap_stdio.py @@ -1,21 +1,19 @@ import sys -from commitizen import wrap_stdio, wrap_stdio_linux, wrap_stdio_unix, wrap_stdio_windows +from commitizen import wrap_stdio - -def test_warp_stdio_exists(): - assert hasattr(wrap_stdio_windows, "sys") - assert hasattr(wrap_stdio_linux, "sys") - assert hasattr(wrap_stdio_unix, "sys") +# def test_warp_stdio_exists(): +# assert hasattr(wrap_stdio_windows, "sys") +# assert hasattr(wrap_stdio_linux, "sys") +# assert hasattr(wrap_stdio_unix, "sys") if sys.platform == "win32": # pragma: no cover pass elif sys.platform == "linux": - from commitizen.wrap_stdio_linux import WrapStdinLinux, WrapStdoutLinux + from commitizen.wrap_stdio.linux import WrapStdinLinux, WrapStdoutLinux def test_wrap_stdin_linux(mocker): - tmp_stdin = sys.stdin mocker.patch("os.open") @@ -36,7 +34,6 @@ def test_wrap_stdin_linux(mocker): assert sys.stdin == tmp_stdin def test_wrap_stdout_linux(mocker): - tmp_stdout = sys.stdout tmp_stderr = sys.stderr From a30c24d855e2e66a64cfc07ce2bf966044779831 Mon Sep 17 00:00:00 2001 From: saygox Date: Tue, 7 Dec 2021 10:30:37 +0900 Subject: [PATCH 13/17] refactor: wrap_stdio --- commitizen/wrap_stdio/linux.py | 91 ++++++++++----------- commitizen/wrap_stdio/unix.py | 141 +++++++++++++++++---------------- 2 files changed, 117 insertions(+), 115 deletions(-) diff --git a/commitizen/wrap_stdio/linux.py b/commitizen/wrap_stdio/linux.py index e349b1fbc..e9aa7d98a 100644 --- a/commitizen/wrap_stdio/linux.py +++ b/commitizen/wrap_stdio/linux.py @@ -1,61 +1,62 @@ +import os import sys -if sys.platform == "linux": # pragma: no cover - import os - # from io import IOBase +class WrapStdinLinux: + def __init__(self): + fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) + tty = open(fd, "wb+", buffering=0) + self.tty = tty - class WrapStdinLinux: - def __init__(self): - fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) - tty = open(fd, "wb+", buffering=0) - self.tty = tty + def __getattr__(self, key): + if key == "encoding": + return "UTF-8" + return getattr(self.tty, key) - def __getattr__(self, key): - if key == "encoding": - return "UTF-8" - return getattr(self.tty, key) + def __del__(self): + self.tty.close() - def __del__(self): - self.tty.close() - class WrapStdoutLinux: - def __init__(self): - tty = open("/dev/tty", "w") - self.tty = tty +class WrapStdoutLinux: + def __init__(self): + tty = open("/dev/tty", "w") + self.tty = tty - def __getattr__(self, key): - return getattr(self.tty, key) + def __getattr__(self, key): + return getattr(self.tty, key) - def __del__(self): - self.tty.close() + def __del__(self): + self.tty.close() - backup_stdin = None - backup_stdout = None - backup_stderr = None - def _wrap_stdio(): - global backup_stdin - backup_stdin = sys.stdin - sys.stdin = WrapStdinLinux() +backup_stdin = None +backup_stdout = None +backup_stderr = None - global backup_stdout - backup_stdout = sys.stdout - sys.stdout = WrapStdoutLinux() - global backup_stderr - backup_stderr = sys.stderr - sys.stderr = WrapStdoutLinux() +def _wrap_stdio(): + global backup_stdin + backup_stdin = sys.stdin + sys.stdin = WrapStdinLinux() - def _unwrap_stdio(): - global backup_stdin - sys.stdin.close() - sys.stdin = backup_stdin + global backup_stdout + backup_stdout = sys.stdout + sys.stdout = WrapStdoutLinux() - global backup_stdout - sys.stdout.close() - sys.stdout = backup_stdout + global backup_stderr + backup_stderr = sys.stderr + sys.stderr = WrapStdoutLinux() - global backup_stderr - sys.stderr.close() - sys.stderr = backup_stderr + +def _unwrap_stdio(): + global backup_stdin + sys.stdin.close() + sys.stdin = backup_stdin + + global backup_stdout + sys.stdout.close() + sys.stdout = backup_stdout + + global backup_stderr + sys.stderr.close() + sys.stderr = backup_stderr diff --git a/commitizen/wrap_stdio/unix.py b/commitizen/wrap_stdio/unix.py index 8b9952eab..229ffee5c 100644 --- a/commitizen/wrap_stdio/unix.py +++ b/commitizen/wrap_stdio/unix.py @@ -1,72 +1,73 @@ +import os +import selectors import sys +from asyncio import ( + DefaultEventLoopPolicy, + SelectorEventLoop, + get_event_loop_policy, + set_event_loop_policy, +) +from io import IOBase -if sys.platform != "win32" and sys.platform != "linux": # pragma: no cover - import os - import selectors - from asyncio import ( - DefaultEventLoopPolicy, - SelectorEventLoop, - get_event_loop_policy, - set_event_loop_policy, - ) - from io import IOBase - - class WrapStdioUnix: - def __init__(self, stdx: IOBase): - self._fileno = stdx.fileno() - fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) - if self._fileno == 0: - tty = open(fd, "wb+", buffering=0) - else: - tty = open(fd, "rb+", buffering=0) - self.tty = tty - - def __getattr__(self, key): - if key == "encoding": - return "UTF-8" - return getattr(self.tty, key) - - def __del__(self): - self.tty.close() - - # backup_event_loop = None - backup_event_loop_policy = None - backup_stdin = None - backup_stdout = None - backup_stderr = None - - def _wrap_stdio(): - global backup_event_loop_policy - backup_event_loop_policy = get_event_loop_policy() - - event_loop = DefaultEventLoopPolicy() - event_loop.set_event_loop(SelectorEventLoop(selectors.SelectSelector())) - set_event_loop_policy(event_loop) - - global backup_stdin - backup_stdin = sys.stdin - sys.stdin = WrapStdioUnix(sys.stdin) - - global backup_stdout - backup_stdout = sys.stdout - sys.stdout = WrapStdioUnix(sys.stdout) - - global backup_stderr - backup_stdout = sys.stderr - sys.stderr = WrapStdioUnix(sys.stderr) - - def _unwrap_stdio(): - global backup_event_loop_policy - set_event_loop_policy(backup_event_loop_policy) - - global backup_stdin - sys.stdin.close() - sys.stdin = backup_stdin - - global backup_stdout - sys.stdout.close() - sys.stdout = backup_stdout - - global backup_stderr - sys.stderr.close() - sys.stderr = backup_stderr + +class WrapStdioUnix: + def __init__(self, stdx: IOBase): + self._fileno = stdx.fileno() + fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) + if self._fileno == 0: + tty = open(fd, "wb+", buffering=0) + else: + tty = open(fd, "rb+", buffering=0) + self.tty = tty + + def __getattr__(self, key): + if key == "encoding": + return "UTF-8" + return getattr(self.tty, key) + + def __del__(self): + self.tty.close() + + +backup_event_loop_policy = None +backup_stdin = None +backup_stdout = None +backup_stderr = None + + +def _wrap_stdio(): + global backup_event_loop_policy + backup_event_loop_policy = get_event_loop_policy() + + event_loop = DefaultEventLoopPolicy() + event_loop.set_event_loop(SelectorEventLoop(selectors.SelectSelector())) + set_event_loop_policy(event_loop) + + global backup_stdin + backup_stdin = sys.stdin + sys.stdin = WrapStdioUnix(sys.stdin) + + global backup_stdout + backup_stdout = sys.stdout + sys.stdout = WrapStdioUnix(sys.stdout) + + global backup_stderr + backup_stdout = sys.stderr + sys.stderr = WrapStdioUnix(sys.stderr) + + +def _unwrap_stdio(): + global backup_event_loop_policy + set_event_loop_policy(backup_event_loop_policy) + + global backup_stdin + sys.stdin.close() + sys.stdin = backup_stdin + + global backup_stdout + sys.stdout.close() + sys.stdout = backup_stdout + + global backup_stderr + sys.stderr.close() + sys.stderr = backup_stderr From 13bfed73a3a790f6dc584db0bc53e38ee8f4cfb8 Mon Sep 17 00:00:00 2001 From: saygox Date: Tue, 7 Dec 2021 12:11:00 +0900 Subject: [PATCH 14/17] Revert "refactor: wrap_stdio" This reverts commit f7f80bbe0c8693a2fff2f3dfc5b2510f0cda00af. --- commitizen/wrap_stdio/linux.py | 91 +++++++++++---------- commitizen/wrap_stdio/unix.py | 141 ++++++++++++++++----------------- 2 files changed, 115 insertions(+), 117 deletions(-) diff --git a/commitizen/wrap_stdio/linux.py b/commitizen/wrap_stdio/linux.py index e9aa7d98a..e349b1fbc 100644 --- a/commitizen/wrap_stdio/linux.py +++ b/commitizen/wrap_stdio/linux.py @@ -1,62 +1,61 @@ -import os import sys +if sys.platform == "linux": # pragma: no cover + import os -class WrapStdinLinux: - def __init__(self): - fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) - tty = open(fd, "wb+", buffering=0) - self.tty = tty + # from io import IOBase - def __getattr__(self, key): - if key == "encoding": - return "UTF-8" - return getattr(self.tty, key) + class WrapStdinLinux: + def __init__(self): + fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) + tty = open(fd, "wb+", buffering=0) + self.tty = tty - def __del__(self): - self.tty.close() + def __getattr__(self, key): + if key == "encoding": + return "UTF-8" + return getattr(self.tty, key) + def __del__(self): + self.tty.close() -class WrapStdoutLinux: - def __init__(self): - tty = open("/dev/tty", "w") - self.tty = tty + class WrapStdoutLinux: + def __init__(self): + tty = open("/dev/tty", "w") + self.tty = tty - def __getattr__(self, key): - return getattr(self.tty, key) + def __getattr__(self, key): + return getattr(self.tty, key) - def __del__(self): - self.tty.close() + def __del__(self): + self.tty.close() + backup_stdin = None + backup_stdout = None + backup_stderr = None -backup_stdin = None -backup_stdout = None -backup_stderr = None + def _wrap_stdio(): + global backup_stdin + backup_stdin = sys.stdin + sys.stdin = WrapStdinLinux() + global backup_stdout + backup_stdout = sys.stdout + sys.stdout = WrapStdoutLinux() -def _wrap_stdio(): - global backup_stdin - backup_stdin = sys.stdin - sys.stdin = WrapStdinLinux() + global backup_stderr + backup_stderr = sys.stderr + sys.stderr = WrapStdoutLinux() - global backup_stdout - backup_stdout = sys.stdout - sys.stdout = WrapStdoutLinux() + def _unwrap_stdio(): + global backup_stdin + sys.stdin.close() + sys.stdin = backup_stdin - global backup_stderr - backup_stderr = sys.stderr - sys.stderr = WrapStdoutLinux() + global backup_stdout + sys.stdout.close() + sys.stdout = backup_stdout - -def _unwrap_stdio(): - global backup_stdin - sys.stdin.close() - sys.stdin = backup_stdin - - global backup_stdout - sys.stdout.close() - sys.stdout = backup_stdout - - global backup_stderr - sys.stderr.close() - sys.stderr = backup_stderr + global backup_stderr + sys.stderr.close() + sys.stderr = backup_stderr diff --git a/commitizen/wrap_stdio/unix.py b/commitizen/wrap_stdio/unix.py index 229ffee5c..8b9952eab 100644 --- a/commitizen/wrap_stdio/unix.py +++ b/commitizen/wrap_stdio/unix.py @@ -1,73 +1,72 @@ -import os -import selectors import sys -from asyncio import ( - DefaultEventLoopPolicy, - SelectorEventLoop, - get_event_loop_policy, - set_event_loop_policy, -) -from io import IOBase - -class WrapStdioUnix: - def __init__(self, stdx: IOBase): - self._fileno = stdx.fileno() - fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) - if self._fileno == 0: - tty = open(fd, "wb+", buffering=0) - else: - tty = open(fd, "rb+", buffering=0) - self.tty = tty - - def __getattr__(self, key): - if key == "encoding": - return "UTF-8" - return getattr(self.tty, key) - - def __del__(self): - self.tty.close() - - -backup_event_loop_policy = None -backup_stdin = None -backup_stdout = None -backup_stderr = None - - -def _wrap_stdio(): - global backup_event_loop_policy - backup_event_loop_policy = get_event_loop_policy() - - event_loop = DefaultEventLoopPolicy() - event_loop.set_event_loop(SelectorEventLoop(selectors.SelectSelector())) - set_event_loop_policy(event_loop) - - global backup_stdin - backup_stdin = sys.stdin - sys.stdin = WrapStdioUnix(sys.stdin) - - global backup_stdout - backup_stdout = sys.stdout - sys.stdout = WrapStdioUnix(sys.stdout) - - global backup_stderr - backup_stdout = sys.stderr - sys.stderr = WrapStdioUnix(sys.stderr) - - -def _unwrap_stdio(): - global backup_event_loop_policy - set_event_loop_policy(backup_event_loop_policy) - - global backup_stdin - sys.stdin.close() - sys.stdin = backup_stdin - - global backup_stdout - sys.stdout.close() - sys.stdout = backup_stdout - - global backup_stderr - sys.stderr.close() - sys.stderr = backup_stderr +if sys.platform != "win32" and sys.platform != "linux": # pragma: no cover + import os + import selectors + from asyncio import ( + DefaultEventLoopPolicy, + SelectorEventLoop, + get_event_loop_policy, + set_event_loop_policy, + ) + from io import IOBase + + class WrapStdioUnix: + def __init__(self, stdx: IOBase): + self._fileno = stdx.fileno() + fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) + if self._fileno == 0: + tty = open(fd, "wb+", buffering=0) + else: + tty = open(fd, "rb+", buffering=0) + self.tty = tty + + def __getattr__(self, key): + if key == "encoding": + return "UTF-8" + return getattr(self.tty, key) + + def __del__(self): + self.tty.close() + + # backup_event_loop = None + backup_event_loop_policy = None + backup_stdin = None + backup_stdout = None + backup_stderr = None + + def _wrap_stdio(): + global backup_event_loop_policy + backup_event_loop_policy = get_event_loop_policy() + + event_loop = DefaultEventLoopPolicy() + event_loop.set_event_loop(SelectorEventLoop(selectors.SelectSelector())) + set_event_loop_policy(event_loop) + + global backup_stdin + backup_stdin = sys.stdin + sys.stdin = WrapStdioUnix(sys.stdin) + + global backup_stdout + backup_stdout = sys.stdout + sys.stdout = WrapStdioUnix(sys.stdout) + + global backup_stderr + backup_stdout = sys.stderr + sys.stderr = WrapStdioUnix(sys.stderr) + + def _unwrap_stdio(): + global backup_event_loop_policy + set_event_loop_policy(backup_event_loop_policy) + + global backup_stdin + sys.stdin.close() + sys.stdin = backup_stdin + + global backup_stdout + sys.stdout.close() + sys.stdout = backup_stdout + + global backup_stderr + sys.stderr.close() + sys.stderr = backup_stderr From 235de4c217e5298af45ded631220f793b52aa721 Mon Sep 17 00:00:00 2001 From: saygox Date: Tue, 7 Dec 2021 12:33:51 +0900 Subject: [PATCH 15/17] refactor(wrap_stdout): remove unused lines --- commitizen/wrap_stdio/linux.py | 2 -- commitizen/wrap_stdio/unix.py | 4 ---- tests/wrap_stdio/test_wrap_stdio.py | 7 ------- 3 files changed, 13 deletions(-) diff --git a/commitizen/wrap_stdio/linux.py b/commitizen/wrap_stdio/linux.py index e349b1fbc..b0f39778a 100644 --- a/commitizen/wrap_stdio/linux.py +++ b/commitizen/wrap_stdio/linux.py @@ -3,8 +3,6 @@ if sys.platform == "linux": # pragma: no cover import os - # from io import IOBase - class WrapStdinLinux: def __init__(self): fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) diff --git a/commitizen/wrap_stdio/unix.py b/commitizen/wrap_stdio/unix.py index 8b9952eab..e864b4d4d 100644 --- a/commitizen/wrap_stdio/unix.py +++ b/commitizen/wrap_stdio/unix.py @@ -26,10 +26,6 @@ def __getattr__(self, key): return "UTF-8" return getattr(self.tty, key) - def __del__(self): - self.tty.close() - - # backup_event_loop = None backup_event_loop_policy = None backup_stdin = None backup_stdout = None diff --git a/tests/wrap_stdio/test_wrap_stdio.py b/tests/wrap_stdio/test_wrap_stdio.py index 741cb2360..a5bd47272 100644 --- a/tests/wrap_stdio/test_wrap_stdio.py +++ b/tests/wrap_stdio/test_wrap_stdio.py @@ -2,12 +2,6 @@ from commitizen import wrap_stdio -# def test_warp_stdio_exists(): -# assert hasattr(wrap_stdio_windows, "sys") -# assert hasattr(wrap_stdio_linux, "sys") -# assert hasattr(wrap_stdio_unix, "sys") - - if sys.platform == "win32": # pragma: no cover pass elif sys.platform == "linux": @@ -54,7 +48,6 @@ def test_wrap_stdout_linux(mocker): readerwriter_mock().write.assert_called_with("stderr") wrap_stdio.unwrap_stdio() - assert sys.stdout == tmp_stdout assert sys.stderr == tmp_stderr From e13b4f9f61bd6124f13afec04574665af3ca8a13 Mon Sep 17 00:00:00 2001 From: saygox Date: Sat, 11 Dec 2021 14:50:31 +0900 Subject: [PATCH 16/17] style(test/wrap_stdout): format by black --- tests/wrap_stdio/test_wrap_stdio.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/wrap_stdio/test_wrap_stdio.py b/tests/wrap_stdio/test_wrap_stdio.py index a5bd47272..d1545a895 100644 --- a/tests/wrap_stdio/test_wrap_stdio.py +++ b/tests/wrap_stdio/test_wrap_stdio.py @@ -51,6 +51,5 @@ def test_wrap_stdout_linux(mocker): assert sys.stdout == tmp_stdout assert sys.stderr == tmp_stderr - else: pass From a7b3228d0c3f0f456cf552ce3c2d09f8446439eb Mon Sep 17 00:00:00 2001 From: saygox Date: Mon, 13 Dec 2021 22:19:53 +0900 Subject: [PATCH 17/17] test(commands/commit): add a test for is_blank_commit_file --- commitizen/commands/commit.py | 4 +- tests/commands/test_commit_command.py | 54 +++++++++++++++++++++++++++ tests/wrap_stdio/test_wrap_stdio.py | 7 ++++ 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index 10cd6effe..d2fc8fc2b 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -97,8 +97,8 @@ def manual_edit(self, message: str) -> str: def is_blank_commit_file(self, filename) -> bool: if not exists(filename): return True - with open(filename, "tr") as f: - for x in f: + with open(filename) as f: + for x in f.readlines(): if len(x) == 0 or x[0] == "#": continue elif x[0] != "\r" and x[0] != "\n": diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py index f70231741..eabe8a223 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -541,6 +541,11 @@ def test_commit_from_pre_commit_msg_hook(config, mocker, capsys): "footer": "", } + is_blank_commit_file_mock = mocker.patch( + "commitizen.commands.commit.Commit.is_blank_commit_file" + ) + is_blank_commit_file_mock.return_value = True + commit_mock = mocker.patch("commitizen.git.commit") commit_mock.return_value = cmd.Command("success", "", "", "", 0) @@ -554,3 +559,52 @@ def test_commit_from_pre_commit_msg_hook(config, mocker, capsys): out, _ = capsys.readouterr() assert "Commit message is successful!" in out commit_mock.assert_not_called() + + +def test_commit_with_msg_from_pre_commit_msg_hook(config, mocker, capsys): + testargs = ["cz", "commit", "--commit-msg-file", "some_file"] + mocker.patch.object(sys, "argv", testargs) + + prompt_mock = mocker.patch("questionary.prompt") + prompt_mock.return_value = { + "prefix": "feat", + "subject": "user created", + "scope": "", + "is_breaking_change": False, + "body": "", + "footer": "", + } + + is_blank_commit_file_mock = mocker.patch( + "commitizen.commands.commit.Commit.is_blank_commit_file" + ) + is_blank_commit_file_mock.return_value = False + + commit_mock = mocker.patch("commitizen.git.commit") + commit_mock.return_value = cmd.Command("success", "", "", "", 0) + + cli.main() + + prompt_mock.assert_not_called() + commit_mock.assert_not_called() + + +@pytest.mark.parametrize( + "isexist, commitmsg, returnvalue", + [ + [False, "", True], + [True, "\n#test", True], + [True, "test: test\n#test", False], + [True, "#test: test\n#test", True], + ], +) +def test_is_blank_commit_file(config, mocker, isexist, commitmsg, returnvalue): + exists_mock = mocker.patch("commitizen.commands.commit.exists") + exists_mock.return_value = isexist + + reader_mock = mocker.mock_open(read_data=commitmsg) + mocker.patch("builtins.open", reader_mock) + + commit_cmd = commands.Commit(config, {}) + ret = commit_cmd.is_blank_commit_file("test") + assert ret == returnvalue diff --git a/tests/wrap_stdio/test_wrap_stdio.py b/tests/wrap_stdio/test_wrap_stdio.py index d1545a895..152506956 100644 --- a/tests/wrap_stdio/test_wrap_stdio.py +++ b/tests/wrap_stdio/test_wrap_stdio.py @@ -2,6 +2,13 @@ from commitizen import wrap_stdio + +def test_import_sub_files(): + import commitizen.wrap_stdio.linux # noqa: F401 + import commitizen.wrap_stdio.unix # noqa: F401 + import commitizen.wrap_stdio.windows # noqa: F401 + + if sys.platform == "win32": # pragma: no cover pass elif sys.platform == "linux":