| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| Expand Up | @@ -4,7 +4,7 @@ | |
| # | ||
| # This module is part of GitPython and is released under | ||
| # the BSD License: https://opensource.org/license/bsd-3-clause/ | ||
| import contextlib | ||
| import inspect | ||
| import logging | ||
| import os | ||
| import os.path as osp | ||
| Expand Down Expand Up | @@ -40,6 +40,13 @@ def tearDown(self): | |
|
|
||
| gc.collect() | ||
|
|
||
| def _assert_logged_for_popen(self, log_watcher, name, value): | ||
| re_name = re.escape(name) | ||
| re_value = re.escape(str(value)) | ||
| re_line = re.compile(fr"DEBUG:git.cmd:Popen\(.*\b{re_name}={re_value}[,)]") | ||
| match_attempts = [re_line.match(message) for message in log_watcher.output] | ||
| self.assertTrue(any(match_attempts), repr(log_watcher.output)) | ||
|
|
||
| @mock.patch.object(Git, "execute") | ||
| def test_call_process_calls_execute(self, git): | ||
| git.return_value = "" | ||
| Expand Down Expand Up | @@ -96,10 +103,8 @@ def _do_shell_combo(self, value_in_call, value_from_class): | |
| # git.cmd gets Popen via a "from" import, so patch it there. | ||
| with mock.patch.object(cmd, "Popen", wraps=cmd.Popen) as mock_popen: | ||
| # Use a command with no arguments (besides the program name), so it runs | ||
| # with or without a shell, on all OSes, with the same effect. Since git | ||
| # errors out when run with no arguments, we swallow that error. | ||
| with contextlib.suppress(GitCommandError): | ||
| self.git.execute(["git"], shell=value_in_call) | ||
| # with or without a shell, on all OSes, with the same effect. | ||
| self.git.execute(["git"], with_exceptions=False, shell=value_in_call) | ||
|
|
||
| return mock_popen | ||
|
|
||
| Expand All | @@ -115,14 +120,19 @@ def test_it_uses_shell_or_not_as_specified(self, case): | |
| def test_it_logs_if_it_uses_a_shell(self, case): | ||
| """``shell=`` in the log message agrees with what is passed to `Popen`.""" | ||
| value_in_call, value_from_class = case | ||
|
|
||
| with self.assertLogs(cmd.log, level=logging.DEBUG) as log_watcher: | ||
| mock_popen = self._do_shell_combo(value_in_call, value_from_class) | ||
| self._assert_logged_for_popen(log_watcher, "shell", mock_popen.call_args.kwargs["shell"]) | ||
|
|
||
| popen_shell_arg = mock_popen.call_args.kwargs["shell"] | ||
| expected_message = re.compile(rf"DEBUG:git.cmd:Popen\(.*\bshell={popen_shell_arg}\b.*\)") | ||
| match_attempts = [expected_message.fullmatch(message) for message in log_watcher.output] | ||
| self.assertTrue(any(match_attempts), repr(log_watcher.output)) | ||
| @ddt.data( | ||
| ("None", None), | ||
| ("<valid stream>", subprocess.PIPE), | ||
| ) | ||
| def test_it_logs_istream_summary_for_stdin(self, case): | ||
| expected_summary, istream_argument = case | ||
| with self.assertLogs(cmd.log, level=logging.DEBUG) as log_watcher: | ||
| self.git.execute(["git", "version"], istream=istream_argument) | ||
| self._assert_logged_for_popen(log_watcher, "stdin", expected_summary) | ||
|
|
||
| def test_it_executes_git_and_returns_result(self): | ||
| self.assertRegex(self.git.execute(["git", "version"]), r"^git version [\d\.]{2}.*$") | ||
| Expand Down Expand Up | @@ -364,3 +374,11 @@ def counter_stderr(line): | |
|
|
||
| self.assertEqual(count[1], line_count) | ||
| self.assertEqual(count[2], line_count) | ||
|
|
||
| def test_execute_kwargs_set_agrees_with_method(self): | ||
| parameter_names = inspect.signature(cmd.Git.execute).parameters.keys() | ||
| self_param, command_param, *most_params, extra_kwargs_param = parameter_names | ||
| self.assertEqual(self_param, "self") | ||
| self.assertEqual(command_param, "command") | ||
| self.assertEqual(set(most_params), cmd.execute_kwargs) # Most important. | ||
| self.assertEqual(extra_kwargs_param, "subprocess_kwargs") | ||
|
Comment thread
Comment on lines
+378
to
+384
Copy link
Copy Markdown
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityOr git.cmd.execute_kwargs could be generated in a manner similar to this, and this test, the need to manually update it, and the note in the Git.execute docstring about that need, could all be done away with. Maybe something like this: execute_kwargs = inspect.signature(Git.execute).parameters.keys() - {"self", "command", "subprocess_kwargs"}Right now it is defined very high up in the module, even higher than __all__, and this suggests that it may be intended to be available for static inspection. But I don't think any tools will statically inspect a set of strings like that (plus, static analysis tools can examine the parameters of Git.execute... though the incomplete lists of parameters in the @overload-decorated stubs that precede it confuse the situation somewhat). git.cmd.__all__ contains only "Git" and I hope that means code that uses GitPython should not be using git.cmd.execute_kwargs or relying on its presence. If possible, perhaps it could be made more explicitly private (_execute_kwargs, or if it needs to remain statically defined, maybe _EXECUTE_KWARGS) or, even better, removed altogether if the Git.execute method's arguments can be inspected efficiently enough without it where execute_kwargs is currently used. On the other hand, people may have come to depend on it even though the presence of __all__ that omits it means no one should have depended on it. Anyway, I do not think any of the changes I suggest in this comment need to be made in this pull request. But I wanted to mention the issue just in case.
Sorry, something went wrong.
All reactions
Copy link
Copy Markdown
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityRemoving execute_kwargs seems like a reduction of complexity, which would always be a valuable reduction of maintenance costs should changes need to be made. As execute_kwargs was never advertised in __all__ I'd think that it's fair to say that those who depend on it nonetheless new the risk. I think the same argument is valid knowing that nothing is ever truly private, everything can be introspected if one truly wants to, yet it's something one simply has to ignore in order to be able to make any changes to python software once released.
Sorry, something went wrong.
EliahKagan reacted with thumbs up emoji
All reactions
|
||
| Back | FazBrowse Home | New Git URL |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityThis changes only the order in which they are given, not the contents, and the set is equal to the same value as before.
However, I am wondering if command should actually be added here. Although the @overload-decorated stubs for Git.execute define some parameters as keyword-only, in the actual definition no parameter is keyword-only and there is definitional symmetry between command and the others. Should I worry about what happens if someone has a git-command script that they can usually run as git command and tries to run g.command() where g is a git.cmd.Git instance? Or related situations?
Another ramification of the parameters not being keyword-only is that, because they can be passed positionally, it is a breaking change to add a new one to Git.execute elsewhere than the end. Still, regarding #1686 (comment), if you think a stdin parameter should be added as a synonym of istream, this could still be done even with stdin at the end, with the docstring items that document the parameters referring to each other to overcome any confusion. I am inclined to say the added complexity is not worthwhile (for example, the function would have to handle the case where they are both passed and with inconsistent values). But a possible argument against this is that the text synonym of universal_newlines could also be added.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityIn these situations it would be great to know why command was put there in the first place, and I for one do not remember. I know there never was an issue related to command specifically, which seems to indicate it's a limitation worth ignoring or fixing in a non-breaking fashion (which seems possible).
Regarding istream, I'd think maintaining stability would let me sleep better at night especially since you seem to agree that it's used consistently here and in gitdb. Probably along with the documentation improvements, all is well and better than it was before, which even in the past didn't seem to have caused enough confusion to cause an issue to be opened.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityDo you mean why it was omitted from the execute_kwargs set? If so, my guess is that it was intended to be used positionally while the others were intended to be keyword-only. This is judging by how they are given as keyword-only arguments in the @overloads, where they are preceded by a *, item in the list of formal parameters, which is absent in the actual function definition (thus causing them to accept both positional and keyword arguments).
But it may be that I am misunderstanding what you mean here. It is also possible that their keyword-only status in the @overloads was intentionally different from the ultimate definition and intended to provide guidance. (I'm not sure. The @overloads are missing most of the arguments, which confuses tooling and seems unintentional.)
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityI am happy to adopt your conclusion in that this is not intentional and since there are negative side-effects, like tooling not working correctly, it's certainly something that could be improved.
If against all odds such an action does create downstream breakage, it could also be undone with a patch release.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityI don't think we should make the parameters in the real definition actually keyword-only, since that is a breaking change. Or, at least, I would not rush to that. It seems likely to me that, at least for the first few, people are passing them positionally.
I believe it is merely the absence of many parameters from the @overload-decorated stubs (which precede the real definition) that is causing VS Code not to show or autocomplete some arguments. Adding the missing parameters to the @overload-decorated stubs should be sufficient to fix that, and it shouldn't be a breaking change because (a) they are just type stubs, so it doesn't break runtime behavior, (b) the actual change seems compatible even relative to what the type stubs said before, and (c) GitPython doesn't have static typing stability currently anyway, because although py.typed is included in the package, mypy checks don't pass (nor, per #1659, do pyright checks pass).
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.