Feature/ add a tag(--body-length-limit) for command commit#1849
Feature/ add a tag(--body-length-limit) for command commit#1849yjaw wants to merge 3 commits intocommitizen-tools:masterfrom
Conversation
|
| ) | ||
|
|
||
| # Execute with body_length_limit | ||
| commands.Commit(config, {"body_length_limit": 72})() |
There was a problem hiding this comment.
The number 72 should be extracted.
| assert len(line) <= 72, ( | ||
| f"Line exceeds 72 chars: '{line}' ({len(line)} chars)" | ||
| ) | ||
|
|
There was a problem hiding this comment.
Add a file regression check here and remove assert line[0] == ... and assert line[1] == ...
| commit_mock = mocker.patch( | ||
| "commitizen.git.commit", return_value=cmd.Command("success", "", b"", b"", 0) | ||
| ) |
There was a problem hiding this comment.
Please reuse commit_mock fixture. Same for other new tests.
| assert lines[1] == "" | ||
| body_lines = lines[2:] | ||
| for line in body_lines: | ||
| if line.strip(): |
There was a problem hiding this comment.
This if can be removed?
There was a problem hiding this comment.
I believe so. This line was intended to skip empty lines, which are no longer necessary.
| # All lines should be <= 45 chars | ||
| for line in body_lines: | ||
| if line.strip(): | ||
| assert len(line) == 45, ( |
There was a problem hiding this comment.
In this test case, every line is exactly 45 characters long, so I check if it truly wraps to 45-character lines. However, there’s a case where it wraps to a line shorter than 45 characters which is not the intended behavior but test still can pass.
- I forgot to modify the comment.
There was a problem hiding this comment.
That is strange. Wrapping should not guarantee exactly N chars long.
|
|
||
|
|
||
| @pytest.mark.usefixtures("staging_is_clean") | ||
| def test_commit_command_with_body_length_limit_wrapping( |
There was a problem hiding this comment.
I think all the new tests can be merged into one and use parameterize to shorten the tests
Basically we can do like following:
fixtures
parameterize
def test_...(...):
mocker.patch(
"questionary.prompt",
return_value={
"prefix": "feat",
"subject": "add feature",
"scope": "",
"is_breaking_change": False,
"body": body, # parameterized, can be a long text with / without line breaks or short strings or an empty string
"footer": "",
},
)
# commit with parameterized config
# check each line in line[2:] does not exceed the line length limit if it is not 0
# file regression checkThere was a problem hiding this comment.
I understand! This will make the test more organized.
| if ( | ||
| body_length_limit is None or body_length_limit <= 0 | ||
| ): # do nothing for no limit |
There was a problem hiding this comment.
| if ( | |
| body_length_limit is None or body_length_limit <= 0 | |
| ): # do nothing for no limit | |
| if ( | |
| body_length_limit <= 0 | |
| ): # do nothing for no limit |
There was a problem hiding this comment.
Quick question: I understand that the value is guaranteed, but according to defensive programming, isn’t it better to handle the null value?
There was a problem hiding this comment.
I am not sure whether body_length_limit is guaranteed to be an int. If it is None under some code paths, then something might be wrong.
| # First line is subject, second is blank line, rest is body | ||
| subject = message_parts[0] | ||
| blank_line = message_parts[1] | ||
| body = message_parts[2].strip() | ||
| body_lines = body.split("\n") | ||
| wrapped_body_lines = [] | ||
| for line in body_lines: | ||
| wrapped_body_lines.append(textwrap.fill(line, width=body_length_limit)) | ||
| wrapped_body = "\n".join(wrapped_body_lines) | ||
| return f"{subject}\n{blank_line}\n{wrapped_body}" |
There was a problem hiding this comment.
| # First line is subject, second is blank line, rest is body | |
| subject = message_parts[0] | |
| blank_line = message_parts[1] | |
| body = message_parts[2].strip() | |
| body_lines = body.split("\n") | |
| wrapped_body_lines = [] | |
| for line in body_lines: | |
| wrapped_body_lines.append(textwrap.fill(line, width=body_length_limit)) | |
| wrapped_body = "\n".join(wrapped_body_lines) | |
| return f"{subject}\n{blank_line}\n{wrapped_body}" | |
| # First line is subject, second is blank line, rest is body | |
| wrapped_body_lines = [textwrap.fill(line, width=body_length_limit) for line in lines[2:]] | |
| return "\n".join(chain(lines[:2], wrapped_body_lines)) |
There was a problem hiding this comment.
Wow, this code is so clean! I Learned a lot.
Co-authored-by: Tim Hsiung <bear890707@gmail.com>
| if ( | ||
| body_length_limit is None or body_length_limit <= 0 | ||
| ): # do nothing for no limit |
There was a problem hiding this comment.
I am not sure whether body_length_limit is guaranteed to be an int. If it is None under some code paths, then something might be wrong.
| body_lines = body.split("\n") | ||
| wrapped_body_lines = [] | ||
| for line in body_lines: | ||
| wrapped_body_lines.append(textwrap.fill(line, width=body_length_limit)) |
There was a problem hiding this comment.
we should use wrap instead of fill
Reference: https://docs.python.org/3/library/textwrap.html#textwrap.wrap
| # All lines should be <= 45 chars | ||
| for line in body_lines: | ||
| if line.strip(): | ||
| assert len(line) == 45, ( |
There was a problem hiding this comment.
That is strange. Wrapping should not guarantee exactly N chars long.
| f"Length of commit message exceeds limit ({len(subject)}/{message_length_limit}), subject: '{subject}'" | ||
| ) | ||
|
|
||
| def _rewrap_body(self, message: str) -> str: |
There was a problem hiding this comment.
why _rewrap? I think _wrap_body is better
Description
I added a tag (—body-length-limit) for command commit. This tag utilizes Python’s built-in library, textwrap, to rewrap the body. It also respects the user’s |(\n) signal. This tag will affect the footer as well.
The flow is as follows:
\ncharacter.textwrapto rewrap that line.Checklist
Was generative AI tooling used to co-author this PR?
Generated-by: [Gemini] following the guidelines
Code Changes
uv run poe alllocally to ensure this change passes linter check and testsDocumentation Changes
uv run poe doclocally to ensure the documentation pages renders correctlyExpected Behavior
Steps to Test This Pull Request
Additional Context
close #1597