Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 32fc48e

Browse files
author
Kartik Raj
committed
Revert "Simplify buffer decoder. (#19836)"
This reverts commit 8e91332.
1 parent 90725dd commit 32fc48e

17 files changed

+106
-45
lines changed

src/client/common/process/decoder.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@
22
// Licensed under the MIT License.
33

44
import * as iconv from 'iconv-lite';
5+
import { injectable } from 'inversify';
56
import { DEFAULT_ENCODING } from './constants';
7+
import { IBufferDecoder } from './types';
68

7-
export function decodeBuffer(buffers: Buffer[], encoding: string = DEFAULT_ENCODING): string {
8-
encoding = iconv.encodingExists(encoding) ? encoding : DEFAULT_ENCODING;
9-
return iconv.decode(Buffer.concat(buffers), encoding);
9+
@injectable()
10+
export class BufferDecoder implements IBufferDecoder {
11+
public decode(buffers: Buffer[], encoding: string = DEFAULT_ENCODING): string {
12+
encoding = iconv.encodingExists(encoding) ? encoding : DEFAULT_ENCODING;
13+
return iconv.decode(Buffer.concat(buffers), encoding);
14+
}
1015
}

src/client/common/process/proc.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,19 @@ import { traceError } from '../../logging';
66
import { IDisposable } from '../types';
77
import { EnvironmentVariables } from '../variables/types';
88
import { execObservable, killPid, plainExec, shellExec } from './rawProcessApis';
9-
import { ExecutionResult, IProcessService, ObservableExecutionResult, ShellOptions, SpawnOptions } from './types';
9+
import {
10+
ExecutionResult,
11+
IBufferDecoder,
12+
IProcessService,
13+
ObservableExecutionResult,
14+
ShellOptions,
15+
SpawnOptions,
16+
} from './types';
1017

1118
export class ProcessService extends EventEmitter implements IProcessService {
1219
private processesToKill = new Set<IDisposable>();
1320

14-
constructor(private readonly env?: EnvironmentVariables) {
21+
constructor(private readonly decoder: IBufferDecoder, private readonly env?: EnvironmentVariables) {
1522
super();
1623
}
1724

@@ -40,13 +47,13 @@ export class ProcessService extends EventEmitter implements IProcessService {
4047
}
4148

4249
public execObservable(file: string, args: string[], options: SpawnOptions = {}): ObservableExecutionResult<string> {
43-
const result = execObservable(file, args, options, this.env, this.processesToKill);
50+
const result = execObservable(file, args, options, this.decoder, this.env, this.processesToKill);
4451
this.emit('exec', file, args, options);
4552
return result;
4653
}
4754

4855
public exec(file: string, args: string[], options: SpawnOptions = {}): Promise<ExecutionResult<string>> {
49-
const promise = plainExec(file, args, options, this.env, this.processesToKill);
56+
const promise = plainExec(file, args, options, this.decoder, this.env, this.processesToKill);
5057
this.emit('exec', file, args, options);
5158
return promise;
5259
}

src/client/common/process/processFactory.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,19 @@ import { Uri } from 'vscode';
88
import { IDisposableRegistry } from '../types';
99
import { IEnvironmentVariablesProvider } from '../variables/types';
1010
import { ProcessService } from './proc';
11-
import { IProcessLogger, IProcessService, IProcessServiceFactory } from './types';
11+
import { IBufferDecoder, IProcessLogger, IProcessService, IProcessServiceFactory } from './types';
1212

1313
@injectable()
1414
export class ProcessServiceFactory implements IProcessServiceFactory {
1515
constructor(
1616
@inject(IEnvironmentVariablesProvider) private readonly envVarsService: IEnvironmentVariablesProvider,
1717
@inject(IProcessLogger) private readonly processLogger: IProcessLogger,
18+
@inject(IBufferDecoder) private readonly decoder: IBufferDecoder,
1819
@inject(IDisposableRegistry) private readonly disposableRegistry: IDisposableRegistry,
1920
) {}
2021
public async create(resource?: Uri): Promise<IProcessService> {
2122
const customEnvVars = await this.envVarsService.getEnvironmentVariables(resource);
22-
const proc: IProcessService = new ProcessService(customEnvVars);
23+
const proc: IProcessService = new ProcessService(this.decoder, customEnvVars);
2324
this.disposableRegistry.push(proc);
2425
return proc.on('exec', this.processLogger.logProcess.bind(this.processLogger));
2526
}

src/client/common/process/pythonExecutionFactory.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { createPythonProcessService } from './pythonProcess';
1515
import {
1616
ExecutionFactoryCreateWithEnvironmentOptions,
1717
ExecutionFactoryCreationOptions,
18+
IBufferDecoder,
1819
IProcessLogger,
1920
IProcessService,
2021
IProcessServiceFactory,
@@ -39,6 +40,7 @@ export class PythonExecutionFactory implements IPythonExecutionFactory {
3940
@inject(IEnvironmentActivationService) private readonly activationHelper: IEnvironmentActivationService,
4041
@inject(IProcessServiceFactory) private readonly processServiceFactory: IProcessServiceFactory,
4142
@inject(IConfigurationService) private readonly configService: IConfigurationService,
43+
@inject(IBufferDecoder) private readonly decoder: IBufferDecoder,
4244
@inject(IComponentAdapter) private readonly pyenvs: IComponentAdapter,
4345
@inject(IInterpreterAutoSelectionService) private readonly autoSelection: IInterpreterAutoSelectionService,
4446
@inject(IInterpreterPathService) private readonly interpreterPathExpHelper: IInterpreterPathService,
@@ -108,7 +110,7 @@ export class PythonExecutionFactory implements IPythonExecutionFactory {
108110
const pythonPath = options.interpreter
109111
? options.interpreter.path
110112
: this.configService.getSettings(options.resource).pythonPath;
111-
const processService: IProcessService = new ProcessService({ ...envVars });
113+
const processService: IProcessService = new ProcessService(this.decoder, { ...envVars });
112114
processService.on('exec', this.logger.logProcess.bind(this.logger));
113115
this.disposables.push(processService);
114116

src/client/common/process/rawProcessApis.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,16 @@ import { IDisposable } from '../types';
88
import { createDeferred } from '../utils/async';
99
import { EnvironmentVariables } from '../variables/types';
1010
import { DEFAULT_ENCODING } from './constants';
11-
import { ExecutionResult, ObservableExecutionResult, Output, ShellOptions, SpawnOptions, StdErrError } from './types';
11+
import {
12+
ExecutionResult,
13+
IBufferDecoder,
14+
ObservableExecutionResult,
15+
Output,
16+
ShellOptions,
17+
SpawnOptions,
18+
StdErrError,
19+
} from './types';
1220
import { noop } from '../utils/misc';
13-
import { decodeBuffer } from './decoder';
1421

1522
function getDefaultOptions<T extends ShellOptions | SpawnOptions>(options: T, defaultEnv?: EnvironmentVariables): T {
1623
const defaultOptions = { ...options };
@@ -83,6 +90,7 @@ export function plainExec(
8390
file: string,
8491
args: string[],
8592
options: SpawnOptions = {},
93+
decoder?: IBufferDecoder,
8694
defaultEnv?: EnvironmentVariables,
8795
disposables?: Set<IDisposable>,
8896
): Promise<ExecutionResult<string>> {
@@ -133,11 +141,11 @@ export function plainExec(
133141
return;
134142
}
135143
const stderr: string | undefined =
136-
stderrBuffers.length === 0 ? undefined : decodeBuffer(stderrBuffers, encoding);
144+
stderrBuffers.length === 0 ? undefined : decoder?.decode(stderrBuffers, encoding);
137145
if (stderr && stderr.length > 0 && options.throwOnStdErr) {
138146
deferred.reject(new StdErrError(stderr));
139147
} else {
140-
let stdout = decodeBuffer(stdoutBuffers, encoding);
148+
let stdout = decoder ? decoder.decode(stdoutBuffers, encoding) : '';
141149
stdout = filterOutputUsingCondaRunMarkers(stdout);
142150
deferred.resolve({ stdout, stderr });
143151
}
@@ -169,6 +177,7 @@ export function execObservable(
169177
file: string,
170178
args: string[],
171179
options: SpawnOptions = {},
180+
decoder?: IBufferDecoder,
172181
defaultEnv?: EnvironmentVariables,
173182
disposables?: Set<IDisposable>,
174183
): ObservableExecutionResult<string> {
@@ -211,7 +220,7 @@ export function execObservable(
211220
}
212221

213222
const sendOutput = (source: 'stdout' | 'stderr', data: Buffer) => {
214-
let out = decodeBuffer([data], encoding);
223+
let out = decoder ? decoder.decode([data], encoding) : '';
215224
if (source === 'stderr' && options.throwOnStdErr) {
216225
subscriber.error(new StdErrError(out));
217226
} else {

src/client/common/process/serviceRegistry.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
// Licensed under the MIT License.
33

44
import { IServiceManager } from '../../ioc/types';
5+
import { BufferDecoder } from './decoder';
56
import { ProcessServiceFactory } from './processFactory';
67
import { PythonExecutionFactory } from './pythonExecutionFactory';
78
import { PythonToolExecutionService } from './pythonToolService';
8-
import { IProcessServiceFactory, IPythonExecutionFactory, IPythonToolExecutionService } from './types';
9+
import { IBufferDecoder, IProcessServiceFactory, IPythonExecutionFactory, IPythonToolExecutionService } from './types';
910

1011
export function registerTypes(serviceManager: IServiceManager) {
12+
serviceManager.addSingleton<IBufferDecoder>(IBufferDecoder, BufferDecoder);
1113
serviceManager.addSingleton<IProcessServiceFactory>(IProcessServiceFactory, ProcessServiceFactory);
1214
serviceManager.addSingleton<IPythonExecutionFactory>(IPythonExecutionFactory, PythonExecutionFactory);
1315
serviceManager.addSingleton<IPythonToolExecutionService>(IPythonToolExecutionService, PythonToolExecutionService);

src/client/common/process/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ import { PythonExecInfo } from '../../pythonEnvironments/exec';
88
import { InterpreterInformation, PythonEnvironment } from '../../pythonEnvironments/info';
99
import { ExecutionInfo, IDisposable } from '../types';
1010

11+
export const IBufferDecoder = Symbol('IBufferDecoder');
12+
export interface IBufferDecoder {
13+
decode(buffers: Buffer[], encoding: string): string;
14+
}
15+
1116
export type Output<T extends string | Buffer> = {
1217
source: 'stdout' | 'stderr';
1318
out: T;

src/test/common.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,9 +303,10 @@ export function correctPathForOsType(pathToCorrect: string, os?: OSType): string
303303
* @return `SemVer` version of the Python interpreter, or `undefined` if an error occurs.
304304
*/
305305
export async function getPythonSemVer(procService?: IProcessService): Promise<SemVer | undefined> {
306+
const decoder = await import('../client/common/process/decoder');
306307
const proc = await import('../client/common/process/proc');
307308

308-
const pythonProcRunner = procService ? procService : new proc.ProcessService();
309+
const pythonProcRunner = procService ? procService : new proc.ProcessService(new decoder.BufferDecoder());
309310
const pyVerArgs = ['-c', 'import sys;print("{0}.{1}.{2}".format(*sys.version_info[:3]))'];
310311

311312
return pythonProcRunner

src/test/common/process/decoder.test.ts

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

44
import { expect } from 'chai';
55
import { encode, encodingExists } from 'iconv-lite';
6-
import { decodeBuffer } from '../../../client/common/process/decoder';
6+
import { BufferDecoder } from '../../../client/common/process/decoder';
77
import { initialize } from './../../initialize';
88

99
suite('Decoder', () => {
@@ -13,7 +13,8 @@ suite('Decoder', () => {
1313
test('Test decoding utf8 strings', () => {
1414
const value = 'Sample input string Сделать это';
1515
const buffer = encode(value, 'utf8');
16-
const decodedValue = decodeBuffer([buffer]);
16+
const decoder = new BufferDecoder();
17+
const decodedValue = decoder.decode([buffer]);
1718
expect(decodedValue).equal(value, 'Decoded string is incorrect');
1819
});
1920

@@ -23,10 +24,11 @@ suite('Decoder', () => {
2324
}
2425
const value = 'Sample input string Сделать это';
2526
const buffer = encode(value, 'cp866');
26-
let decodedValue = decodeBuffer([buffer]);
27+
const decoder = new BufferDecoder();
28+
let decodedValue = decoder.decode([buffer]);
2729
expect(decodedValue).not.equal(value, 'Decoded string is the same');
2830

29-
decodedValue = decodeBuffer([buffer], 'cp866');
31+
decodedValue = decoder.decode([buffer], 'cp866');
3032
expect(decodedValue).equal(value, 'Decoded string is incorrect');
3133
});
3234
});

src/test/common/process/proc.exec.test.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { expect, use } from 'chai';
77
import * as chaiAsPromised from 'chai-as-promised';
88
import { CancellationTokenSource } from 'vscode';
9+
import { BufferDecoder } from '../../../client/common/process/decoder';
910
import { ProcessService } from '../../../client/common/process/proc';
1011
import { StdErrError } from '../../../client/common/process/types';
1112
import { OSType } from '../../../client/common/utils/platform';
@@ -25,7 +26,7 @@ suite('ProcessService Observable', () => {
2526
teardown(initialize);
2627

2728
test('exec should output print statements', async () => {
28-
const procService = new ProcessService();
29+
const procService = new ProcessService(new BufferDecoder());
2930
const printOutput = '1234';
3031
const result = await procService.exec(pythonPath, ['-c', `print("${printOutput}")`]);
3132

@@ -41,7 +42,7 @@ suite('ProcessService Observable', () => {
4142
return this.skip();
4243
}
4344

44-
const procService = new ProcessService();
45+
const procService = new ProcessService(new BufferDecoder());
4546
const printOutput = 'öä';
4647
const result = await procService.exec(pythonPath, ['-c', `print("${printOutput}")`]);
4748

@@ -52,7 +53,7 @@ suite('ProcessService Observable', () => {
5253

5354
test('exec should wait for completion of program with new lines', async function () {
5455
this.timeout(5000);
55-
const procService = new ProcessService();
56+
const procService = new ProcessService(new BufferDecoder());
5657
const pythonCode = [
5758
'import sys',
5859
'import time',
@@ -78,7 +79,7 @@ suite('ProcessService Observable', () => {
7879

7980
test('exec should wait for completion of program without new lines', async function () {
8081
this.timeout(5000);
81-
const procService = new ProcessService();
82+
const procService = new ProcessService(new BufferDecoder());
8283
const pythonCode = [
8384
'import sys',
8485
'import time',
@@ -104,7 +105,7 @@ suite('ProcessService Observable', () => {
104105

105106
test('exec should end when cancellationToken is cancelled', async function () {
106107
this.timeout(15000);
107-
const procService = new ProcessService();
108+
const procService = new ProcessService(new BufferDecoder());
108109
const pythonCode = [
109110
'import sys',
110111
'import time',
@@ -132,7 +133,7 @@ suite('ProcessService Observable', () => {
132133

133134
test('exec should stream stdout and stderr separately and filter output using conda related markers', async function () {
134135
this.timeout(7000);
135-
const procService = new ProcessService();
136+
const procService = new ProcessService(new BufferDecoder());
136137
const pythonCode = [
137138
'print(">>>PYTHON-EXEC-OUTPUT")',
138139
'import sys',
@@ -175,7 +176,7 @@ suite('ProcessService Observable', () => {
175176

176177
test('exec should merge stdout and stderr streams', async function () {
177178
this.timeout(7000);
178-
const procService = new ProcessService();
179+
const procService = new ProcessService(new BufferDecoder());
179180
const pythonCode = [
180181
'import sys',
181182
'import time',
@@ -209,29 +210,29 @@ suite('ProcessService Observable', () => {
209210
});
210211

211212
test('exec should throw an error with stderr output', async () => {
212-
const procService = new ProcessService();
213+
const procService = new ProcessService(new BufferDecoder());
213214
const pythonCode = ['import sys', 'sys.stderr.write("a")', 'sys.stderr.flush()'];
214215
const result = procService.exec(pythonPath, ['-c', pythonCode.join(';')], { throwOnStdErr: true });
215216

216217
await expect(result).to.eventually.be.rejectedWith(StdErrError, 'a', 'Expected error to be thrown');
217218
});
218219

219220
test('exec should throw an error when spawn file not found', async () => {
220-
const procService = new ProcessService();
221+
const procService = new ProcessService(new BufferDecoder());
221222
const result = procService.exec(Date.now().toString(), []);
222223

223224
await expect(result).to.eventually.be.rejected.and.to.have.property('code', 'ENOENT', 'Invalid error code');
224225
});
225226

226227
test('exec should exit without no output', async () => {
227-
const procService = new ProcessService();
228+
const procService = new ProcessService(new BufferDecoder());
228229
const result = await procService.exec(pythonPath, ['-c', 'import sys', 'sys.exit()']);
229230

230231
expect(result.stdout).equals('', 'stdout is invalid');
231232
expect(result.stderr).equals(undefined, 'stderr is invalid');
232233
});
233234
test('shellExec should be able to run python and filter output using conda related markers', async () => {
234-
const procService = new ProcessService();
235+
const procService = new ProcessService(new BufferDecoder());
235236
const printOutput = '1234';
236237
const result = await procService.shellExec(
237238
`"${pythonPath}" -c "print('>>>PYTHON-EXEC-OUTPUT');print('${printOutput}');print('<<<PYTHON-EXEC-OUTPUT')"`,
@@ -242,12 +243,12 @@ suite('ProcessService Observable', () => {
242243
expect(result.stdout.trim()).to.be.equal(printOutput, 'Invalid output');
243244
});
244245
test('shellExec should fail on invalid command', async () => {
245-
const procService = new ProcessService();
246+
const procService = new ProcessService(new BufferDecoder());
246247
const result = procService.shellExec('invalid command');
247248
await expect(result).to.eventually.be.rejectedWith(Error, 'a', 'Expected error to be thrown');
248249
});
249250
test('variables can be changed after the fact', async () => {
250-
const procService = new ProcessService(process.env);
251+
const procService = new ProcessService(new BufferDecoder(), process.env);
251252
let result = await procService.exec(pythonPath, ['-c', `import os;print(os.environ.get("MY_TEST_VARIABLE"))`], {
252253
extraVariables: { MY_TEST_VARIABLE: 'foo' },
253254
});

0 commit comments

Comments
 (0)