Skip to content

Commit 3aada4a

Browse files
authored
Send selection to REPL should read from stdin (#14548)
* Read from sys.stdin * Update test * Clarify comment * News file * Remove extra newline * Send selection as JSON * Send the normalized code back in a JSON object. * move sending JSON back out of the normalize func * Fix tests * Update comment * I should have run these tests locally * Use correct stream in normalizeForInterpreter * lol forgot something * encode using utf-8 * Explicitly use \n and not os.EOL in tests * Decode in utf-8 * Close early * Move output json parsing to normalizeLines
1 parent f8636cd commit 3aada4a

8 files changed

Lines changed: 132 additions & 138 deletions

File tree

news/2 Fixes/14471.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
When sending code to the REPL, read input from `sys.stdin` instead of passing it as an argument.

pythonFiles/normalizeForInterpreter.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import ast
55
import io
6+
import json
67
import operator
78
import os
89
import sys
@@ -137,18 +138,21 @@ def normalize_lines(source):
137138
for line_number in filter(lambda x: x > 1, start_positions):
138139
lines.insert(line_number - 1, "")
139140

140-
sys.stdout.write("\n".join(lines) + trailing_newline)
141-
sys.stdout.flush()
141+
return "\n".join(lines) + trailing_newline
142142

143143

144144
if __name__ == "__main__":
145-
contents = sys.argv[1]
146-
try:
147-
default_encoding = sys.getdefaultencoding()
148-
encoded_contents = contents.encode(default_encoding, "surrogateescape")
149-
contents = encoded_contents.decode(default_encoding, "replace")
150-
except (UnicodeError, LookupError):
151-
pass
152-
if isinstance(contents, bytes):
153-
contents = contents.decode("utf8")
154-
normalize_lines(contents)
145+
# Content is being sent from the extension as a JSON object.
146+
# Decode the data from the raw bytes.
147+
stdin = sys.stdin if sys.version_info < (3,) else sys.stdin.buffer
148+
raw = stdin.read()
149+
contents = json.loads(raw.decode("utf-8"))
150+
151+
normalized = normalize_lines(contents["code"])
152+
153+
# Send the normalized code back in a JSON object.
154+
data = json.dumps({"normalized": normalized})
155+
156+
stdout = sys.stdout if sys.version_info < (3,) else sys.stdout.buffer
157+
stdout.write(data.encode("utf-8"))
158+
stdout.close()

pythonFiles/normalizeSelection.py

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
# Licensed under the MIT License.
33

44
import ast
5-
import textwrap
5+
import json
66
import re
77
import sys
8+
import textwrap
89

910

1011
def split_lines(source):
@@ -82,14 +83,14 @@ def _get_statements(selection):
8283

8384
def normalize_lines(selection):
8485
"""
85-
Normalize the text selection received from the extension and send it to the REPL.
86+
Normalize the text selection received from the extension.
8687
87-
If it is a single line selection, dedent it, append a newline and send it to the REPL.
88-
Otherwise, sanitize the multiline selection before sending it to the REPL:
88+
If it is a single line selection, dedent it and append a newline and
89+
send it back to the extension.
90+
Otherwise, sanitize the multiline selection before returning it:
8991
split it in a list of top-level statements
90-
and add newlines between each of them to tell the REPL where each block ends.
92+
and add newlines between each of them so the REPL knows where each block ends.
9193
"""
92-
9394
try:
9495
# Parse the selection into a list of top-level blocks.
9596
# We don't differentiate between single and multiline statements
@@ -104,25 +105,21 @@ def normalize_lines(selection):
104105
# append a blank line to end the block and send it as-is.
105106
source = selection + "\n\n"
106107

107-
# `source` is a unicode instance at this point on Python 2,
108-
# so if we used `sys.stdout.write` to send it to the REPL,
109-
# Python will implicitly encode it using sys.getdefaultencoding(),
110-
# which we don't want.
111-
stdout = sys.stdout if sys.version_info < (3,) else sys.stdout.buffer
112-
stdout.write(source.encode("utf-8"))
113-
stdout.flush()
108+
return source
114109

115110

116111
if __name__ == "__main__":
117-
# This will fail on a large file.
118-
# See https://github.com/microsoft/vscode-python/issues/14471
119-
contents = sys.argv[1]
120-
try:
121-
default_encoding = sys.getdefaultencoding()
122-
encoded_contents = contents.encode(default_encoding, "surrogateescape")
123-
contents = encoded_contents.decode(default_encoding, "replace")
124-
except (UnicodeError, LookupError):
125-
pass
126-
if isinstance(contents, bytes):
127-
contents = contents.decode("utf8")
128-
normalize_lines(contents)
112+
# Content is being sent from the extension as a JSON object.
113+
# Decode the data from the raw bytes.
114+
stdin = sys.stdin if sys.version_info < (3,) else sys.stdin.buffer
115+
raw = stdin.read()
116+
contents = json.loads(raw.decode("utf-8"))
117+
118+
normalized = normalize_lines(contents["code"])
119+
120+
# Send the normalized code back to the extension in a JSON object.
121+
data = json.dumps({"normalized": normalized})
122+
123+
stdout = sys.stdout if sys.version_info < (3,) else sys.stdout.buffer
124+
stdout.write(data.encode("utf-8"))
125+
stdout.close()

pythonFiles/tests/test_normalize_for_interpreter.py

Lines changed: 19 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,12 @@
1111
class TestNormalizationScript(object):
1212
"""Basic unit tests for the normalization script."""
1313

14-
@pytest.mark.skipif(
15-
sys.version_info.major == 2,
16-
reason="normalizeForInterpreter not working for 2.7, see GH #4805",
17-
)
18-
def test_basicNormalization(self, capsys):
14+
def test_basicNormalization(self):
1915
src = 'print("this is a test")'
20-
normalizeForInterpreter.normalize_lines(src)
21-
captured = capsys.readouterr()
22-
assert captured.out == src
23-
24-
@pytest.mark.skipif(
25-
sys.version_info.major == 2,
26-
reason="normalizeForInterpreter not working for 2.7, see GH #4805",
27-
)
28-
def test_moreThanOneLine(self, capsys):
16+
result = normalizeForInterpreter.normalize_lines(src)
17+
assert result == src
18+
19+
def test_moreThanOneLine(self):
2920
src = textwrap.dedent(
3021
"""\
3122
# Some rando comment
@@ -34,15 +25,10 @@ def show_something():
3425
print("Something")
3526
"""
3627
)
37-
normalizeForInterpreter.normalize_lines(src)
38-
captured = capsys.readouterr()
39-
assert captured.out == src
40-
41-
@pytest.mark.skipif(
42-
sys.version_info.major == 2,
43-
reason="normalizeForInterpreter not working for 2.7, see GH #4805",
44-
)
45-
def test_withHangingIndent(self, capsys):
28+
result = normalizeForInterpreter.normalize_lines(src)
29+
assert result == src
30+
31+
def test_withHangingIndent(self):
4632
src = textwrap.dedent(
4733
"""\
4834
x = 22
@@ -54,15 +40,10 @@ def test_withHangingIndent(self, capsys):
5440
print("The answer to life, the universe, and everything")
5541
"""
5642
)
57-
normalizeForInterpreter.normalize_lines(src)
58-
captured = capsys.readouterr()
59-
assert captured.out == src
60-
61-
@pytest.mark.skipif(
62-
sys.version_info.major == 2,
63-
reason="normalizeForInterpreter not working for 2.7, see GH #4805",
64-
)
65-
def test_clearOutExtraneousNewlines(self, capsys):
43+
result = normalizeForInterpreter.normalize_lines(src)
44+
assert result == src
45+
46+
def test_clearOutExtraneousNewlines(self):
6647
src = textwrap.dedent(
6748
"""\
6849
value_x = 22
@@ -84,15 +65,10 @@ def test_clearOutExtraneousNewlines(self, capsys):
8465
8566
"""
8667
)
87-
normalizeForInterpreter.normalize_lines(src)
88-
result = capsys.readouterr()
89-
assert result.out == expectedResult
90-
91-
@pytest.mark.skipif(
92-
sys.version_info.major == 2,
93-
reason="normalizeForInterpreter not working for 2.7, see GH #4805",
94-
)
95-
def test_clearOutExtraLinesAndWhitespace(self, capsys):
68+
result = normalizeForInterpreter.normalize_lines(src)
69+
assert result == expectedResult
70+
71+
def test_clearOutExtraLinesAndWhitespace(self):
9672
src = textwrap.dedent(
9773
"""\
9874
if True:
@@ -117,6 +93,5 @@ def test_clearOutExtraLinesAndWhitespace(self, capsys):
11793
11894
"""
11995
)
120-
normalizeForInterpreter.normalize_lines(src)
121-
result = capsys.readouterr()
122-
assert result.out == expectedResult
96+
result = normalizeForInterpreter.normalize_lines(src)
97+
assert result == expectedResult

pythonFiles/tests/test_normalize_selection.py

Lines changed: 33 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,13 @@
99
class TestNormalizationScript(object):
1010
"""Unit tests for the normalization script."""
1111

12-
def test_basicNormalization(self, capsys):
12+
def test_basicNormalization(self):
1313
src = 'print("this is a test")'
1414
expected = src + "\n"
15-
normalizeSelection.normalize_lines(src)
16-
captured = capsys.readouterr()
17-
assert captured.out == expected
15+
result = normalizeSelection.normalize_lines(src)
16+
assert result == expected
1817

19-
def test_moreThanOneLine(self, capsys):
18+
def test_moreThanOneLine(self):
2019
src = textwrap.dedent(
2120
"""\
2221
# Some rando comment
@@ -32,11 +31,10 @@ def show_something():
3231
3332
"""
3433
)
35-
normalizeSelection.normalize_lines(src)
36-
captured = capsys.readouterr()
37-
assert captured.out == expected
34+
result = normalizeSelection.normalize_lines(src)
35+
assert result == expected
3836

39-
def test_withHangingIndent(self, capsys):
37+
def test_withHangingIndent(self):
4038
src = textwrap.dedent(
4139
"""\
4240
x = 22
@@ -59,11 +57,10 @@ def test_withHangingIndent(self, capsys):
5957
6058
"""
6159
)
62-
normalizeSelection.normalize_lines(src)
63-
captured = capsys.readouterr()
64-
assert captured.out == expected
60+
result = normalizeSelection.normalize_lines(src)
61+
assert result == expected
6562

66-
def test_clearOutExtraneousNewlines(self, capsys):
63+
def test_clearOutExtraneousNewlines(self):
6764
src = textwrap.dedent(
6865
"""\
6966
value_x = 22
@@ -76,19 +73,18 @@ def test_clearOutExtraneousNewlines(self, capsys):
7673
7774
"""
7875
)
79-
expectedResult = textwrap.dedent(
76+
expected = textwrap.dedent(
8077
"""\
8178
value_x = 22
8279
value_y = 30
8380
value_z = -10
8481
print(value_x + value_y + value_z)
8582
"""
8683
)
87-
normalizeSelection.normalize_lines(src)
88-
result = capsys.readouterr()
89-
assert result.out == expectedResult
84+
result = normalizeSelection.normalize_lines(src)
85+
assert result == expected
9086

91-
def test_clearOutExtraLinesAndWhitespace(self, capsys):
87+
def test_clearOutExtraLinesAndWhitespace(self):
9288
src = textwrap.dedent(
9389
"""\
9490
if True:
@@ -102,7 +98,7 @@ def test_clearOutExtraLinesAndWhitespace(self, capsys):
10298
10399
"""
104100
)
105-
expectedResult = textwrap.dedent(
101+
expected = textwrap.dedent(
106102
"""\
107103
if True:
108104
x = 22
@@ -112,18 +108,16 @@ def test_clearOutExtraLinesAndWhitespace(self, capsys):
112108
print(x + y + z)
113109
"""
114110
)
115-
normalizeSelection.normalize_lines(src)
116-
result = capsys.readouterr()
117-
assert result.out == expectedResult
111+
result = normalizeSelection.normalize_lines(src)
112+
assert result == expected
118113

119-
def test_partialSingleLine(self, capsys):
114+
def test_partialSingleLine(self):
120115
src = " print('foo')"
121116
expected = textwrap.dedent(src) + "\n"
122-
normalizeSelection.normalize_lines(src)
123-
result = capsys.readouterr()
124-
assert result.out == expected
117+
result = normalizeSelection.normalize_lines(src)
118+
assert result == expected
125119

126-
def test_multiLineWithIndent(self, capsys):
120+
def test_multiLineWithIndent(self):
127121
src = """\
128122
129123
if (x > 0
@@ -134,7 +128,7 @@ def test_multiLineWithIndent(self, capsys):
134128
print('bar')
135129
"""
136130

137-
expectedResult = textwrap.dedent(
131+
expected = textwrap.dedent(
138132
"""\
139133
if (x > 0
140134
and condition == True):
@@ -145,11 +139,10 @@ def test_multiLineWithIndent(self, capsys):
145139
"""
146140
)
147141

148-
normalizeSelection.normalize_lines(src)
149-
result = capsys.readouterr()
150-
assert result.out == expectedResult
142+
result = normalizeSelection.normalize_lines(src)
143+
assert result == expected
151144

152-
def test_multiLineWithComment(self, capsys):
145+
def test_multiLineWithComment(self):
153146
src = textwrap.dedent(
154147
"""\
155148
@@ -166,18 +159,16 @@ def show_something():
166159
167160
"""
168161
)
169-
normalizeSelection.normalize_lines(src)
170-
captured = capsys.readouterr()
171-
assert captured.out == expected
162+
result = normalizeSelection.normalize_lines(src)
163+
assert result == expected
172164

173-
def test_exception(self, capsys):
165+
def test_exception(self):
174166
src = " if True:"
175167
expected = src + "\n\n"
176-
normalizeSelection.normalize_lines(src)
177-
captured = capsys.readouterr()
178-
assert captured.out == expected
168+
result = normalizeSelection.normalize_lines(src)
169+
assert result == expected
179170

180-
def test_multilineException(self, capsys):
171+
def test_multilineException(self):
181172
src = textwrap.dedent(
182173
"""\
183174
@@ -186,6 +177,5 @@ def show_something():
186177
"""
187178
)
188179
expected = src + "\n\n"
189-
normalizeSelection.normalize_lines(src)
190-
captured = capsys.readouterr()
191-
assert captured.out == expected
180+
result = normalizeSelection.normalize_lines(src)
181+
assert result == expected

src/client/common/process/internal/scripts/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,9 @@ export function refactor(root: string): [string[], (out: string) => object[]] {
215215
//============================
216216
// normalizeForInterpreter.py
217217

218-
export function normalizeForInterpreter(code: string): [string[], (out: string) => string] {
218+
export function normalizeForInterpreter(): [string[], (out: string) => string] {
219219
const script = path.join(SCRIPTS_DIR, 'normalizeForInterpreter.py');
220-
const args = [ISOLATED, script, code];
220+
const args = [ISOLATED, script];
221221

222222
function parse(out: string) {
223223
// The text will be used as-is.

0 commit comments

Comments
 (0)