Skip to content

Commit 00122bd

Browse files
committed
Fixes bug due to incorrect behavior in KeyboardInterrupt
1 parent e7aed98 commit 00122bd

File tree

2 files changed

+38
-12
lines changed

2 files changed

+38
-12
lines changed

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,12 @@ The "primitives" for the package are defined in `pick_git/core.py` and are liste
103103
The public methods whose names are passed to __pick-git__ in the `--function` argument, for example `branch` or `commit`, are defined in a mixin class in `pick_git/helpers.py`. I'm sure there are use cases I haven't thought of that deserve public functions, and I would be grateful for contributors.
104104

105105

106+
### Development
107+
See `main.py` in the root of the repo? This script is there for making it easy to test the package. It ensures the __pick-git__ executable can be invoked from the command line, without going through the shim created by `setuptools` when the package is installed.
108+
109+
For example, from the root of the repo, just try running `python main.py --function commit`.
110+
111+
106112
## Tests
107113
The package currently has no tests. This is because __pick-git__ requires user keystrokes to return a value. Sending keystrokes isn't part of Python's standard library, and anyway Python doesn't seem like the best way to do this, although [this seems promising](http://stackoverflow.com/questions/12755968/sending-arrow-keys-to-popen).
108114

pick_git/core.py

+32-12
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ def add_new_line(b):
4040
def exit_on_keyboard_interrupt(f):
4141
"""Decorator that allows user to exit script by sending a keyboard interrupt
4242
(ctrl + c) without raising an exception.
43+
44+
WARNING: there is no guarantee KeyboardInterrupt will be raised if `ctrl+c` is
45+
pressed while the subprocess is running, which means we also need to guard
46+
against empty results from `pick`!
47+
48+
See here: https://stackoverflow.com/questions/39499959/terminating-a-subprocess-with-keyboardinterrupt/39503654
4349
"""
4450
@wraps(f)
4551
def wrapper(*args, **kwargs):
@@ -57,17 +63,23 @@ def wrapper(*args, **kwargs):
5763
def pick_branch(*args):
5864
"""Pick a branch, local or remote.
5965
"""
60-
branches = subprocess.Popen(('git', 'branch', '-a') + args, stdout=PIPE)
61-
branch = subprocess.check_output(['pick'], stdin=branches.stdout)
66+
branches = subprocess.check_output(('git', 'branch', '-a') + args)
67+
p = subprocess.Popen(['pick'], stdin=PIPE, stdout=PIPE, stderr=STDOUT)
68+
branch = p.communicate(input=branches)[0]
69+
if not branch:
70+
raise KeyboardInterrupt
6271
return branch.split()[-1].decode('utf-8')
6372

6473

6574
@exit_on_keyboard_interrupt
6675
def pick_tag(*args):
6776
"""Pick a local tag.
6877
"""
69-
branches = subprocess.Popen(('git', 'tag', '-l') + args, stdout=PIPE)
70-
branch = subprocess.check_output(['pick'], stdin=branches.stdout)
78+
branches = subprocess.check_output(('git', 'tag', '-l') + args)
79+
p = subprocess.Popen(['pick'], stdin=PIPE, stdout=PIPE, stderr=STDOUT)
80+
branch = p.communicate(input=branches)[0]
81+
if not branch:
82+
raise KeyboardInterrupt
7183
return branch.split()[-1].decode('utf-8')
7284

7385

@@ -76,9 +88,10 @@ def pick_commit(*args):
7688
"""Pick a commit hash.
7789
"""
7890
commits = subprocess.check_output(('git', 'log', "--pretty=format:%h %ad | %s%d [%an]", '--date=short') + args)
79-
commits = add_new_line(commits)
8091
p = subprocess.Popen(['pick'], stdin=PIPE, stdout=PIPE, stderr=STDOUT)
81-
commit = p.communicate(input=commits)[0]
92+
commit = p.communicate(input=add_new_line(commits))[0]
93+
if not commit:
94+
raise KeyboardInterrupt
8295
return commit.split()[0].decode('utf-8')
8396

8497

@@ -87,9 +100,10 @@ def pick_commit_reflog(*args):
87100
"""Pick a commit hash from the reflog.
88101
"""
89102
commits = subprocess.check_output(('git', 'reflog', '--date=short') + args)
90-
commits = add_new_line(commits)
91103
p = subprocess.Popen(['pick'], stdin=PIPE, stdout=PIPE, stderr=STDOUT)
92-
commit = p.communicate(input=commits)[0]
104+
commit = p.communicate(input=add_new_line(commits))[0]
105+
if not commit:
106+
raise KeyboardInterrupt
93107
return commit.split()[0].decode('utf-8')
94108

95109

@@ -98,8 +112,11 @@ def pick_modified_file(*args):
98112
"""Pick a file whose state differs between branches or commits, which are
99113
passed in `args`. `args` can contain between 0 and 2 elements.
100114
"""
101-
files = subprocess.Popen(('git', 'diff', '--name-only') + args, stdout=PIPE)
102-
file = subprocess.check_output(['pick'], stdin=files.stdout)
115+
files = subprocess.check_output(('git', 'diff', '--name-only') + args)
116+
p = subprocess.Popen(['pick'], stdin=PIPE, stdout=PIPE, stderr=STDOUT)
117+
file = p.communicate(input=files)[0]
118+
if not file:
119+
raise KeyboardInterrupt
103120
return file.strip().decode('utf-8')
104121

105122

@@ -108,6 +125,9 @@ def pick_file(*args):
108125
"""Pick a file from the index.
109126
"""
110127
branch = current_branch()
111-
files = subprocess.Popen(('git', 'ls-tree', '-r', branch, '--name-only') + args, stdout=PIPE)
112-
file = subprocess.check_output(['pick'], stdin=files.stdout)
128+
files = subprocess.check_output(('git', 'ls-tree', '-r', branch, '--name-only') + args)
129+
p = subprocess.Popen(['pick'], stdin=PIPE, stdout=PIPE, stderr=STDOUT)
130+
file = p.communicate(input=files)[0]
131+
if not file:
132+
raise KeyboardInterrupt
113133
return file.strip().decode('utf-8')

0 commit comments

Comments
 (0)