Skip to content

Commit 1b04ed7

Browse files
authored
Add conda support to nightly flake test (#10523)
* Add conda environments * Reenable multiple interpreters * Change path for conda environments * Add pip requirements * Get multiple interpreters test to work * Partially working conda search * Get conda working locally * Make windows supported environments * Fix hygiene * Fix memory leak for daemons * Fix hang on conda * Actually retry * Two missing setting change updates * Rework the failure capture * Get rid of memory leak when session dies * Make tests leak less memory * Hygiene problem * More potential cleanup * More changes to clean up memory as soon as possible * Dont auto start jupyter * More leaks found and fixes for breaking tests * Fix missing args when test is shutting down * Put back some of the functional requirements * Update tests to not use static * Fix linter tests * More output for linter tests * Linter only fails with other tests * Install linters in conda too * Make base also have dependencies as it's being picked up anyway * Add news entry * Review feedback * Fix unit tests and one of the functional tests * Functional tests and workspace tests at the same time fail
1 parent 66a9333 commit 1b04ed7

File tree

54 files changed

+1029
-703
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1029
-703
lines changed

.vscode/launch.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,9 @@
240240
"env": {
241241
// Remove `X` prefix to test with real python (for DS functional tests).
242242
"XVSCODE_PYTHON_ROLLING": "1",
243+
// Remove 'X' to turn on all logging in the debug output
244+
"XVSC_PYTHON_FORCE_LOGGING": "1",
243245
// Remove `X` prefix and update path to test with real python interpreter (for DS functional tests).
244-
// Do not use a conda environment (as it needs to be activated and the like).
245246
"XCI_PYTHON_PATH": "<Python Path>"
246247
},
247248
"outFiles": [

build/ci/conda_base.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pandas
2+
jupyter
3+
numpy
4+
matplotlib

build/ci/conda_env_1.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
name: conda_env_1
2+
dependencies:
3+
- python=3.7
4+
- pandas
5+
- jupyter
6+
- numpy
7+
- matplotlib

build/ci/conda_env_2.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
name: conda_env_2
2+
dependencies:
3+
- python=3.8
4+
- pandas
5+
- jupyter
6+
- numpy
7+
- matplotlib

build/ci/templates/test_phases.yml

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -97,16 +97,7 @@ steps:
9797
python -m pip --disable-pip-version-check install -t ./pythonFiles/lib/python/debugpy/no_wheels --no-cache-dir --implementation py --no-deps --upgrade --pre debugpy
9898
displayName: 'pip install system test requirements'
9999
condition: and(succeeded(), eq(variables['NeedsPythonTestReqs'], 'true'))
100-
101-
# Install the additional sqlite requirements
102-
#
103-
# This task will only run if variable `NeedsPythonFunctionalReqs` is true.
104-
- bash: |
105-
sudo apt-get install libsqlite3-dev
106-
python -m pip install pysqlite
107-
displayName: 'Setup python to run with sqlite on 2.7'
108-
condition: and(succeeded(), eq(variables['NeedsPythonFunctionalReqs'], 'true'), eq(variables['Agent.Os'], 'Linux'), eq(variables['PythonVersion'], '2.7'))
109-
100+
110101
# Install the requirements for functional tests.
111102
#
112103
# This task will only run if variable `NeedsPythonFunctionalReqs` is true.
@@ -118,9 +109,69 @@ steps:
118109
- bash: |
119110
python -m pip install numpy
120111
python -m pip install --upgrade -r ./build/functional-test-requirements.txt
112+
python -c "import sys;print(sys.executable)"
121113
displayName: 'pip install functional requirements'
114+
condition: and(succeeded(), eq(variables['NeedsPythonFunctionalReqs'], 'true'))
115+
116+
# Add CONDA to the path so anaconda works
117+
#
118+
# This task will only run if variable `NeedsPythonFunctionalReqs` is true.
119+
- bash: |
120+
echo "##vso[task.prependpath]$CONDA/bin"
121+
displayName: 'Add conda to the path'
122+
condition: and(succeeded(), eq(variables['NeedsPythonFunctionalReqs'], 'true'), ne(variables['Agent.Os'], 'Windows_NT'))
123+
124+
# Add CONDA to the path so anaconda works (windows)
125+
#
126+
# This task will only run if variable `NeedsPythonFunctionalReqs` is true.
127+
- powershell: |
128+
Write-Host "##vso[task.prependpath]$env:CONDA\Scripts"
129+
displayName: 'Add conda to the path'
130+
condition: and(succeeded(), eq(variables['NeedsPythonFunctionalReqs'], 'true'), eq(variables['Agent.Os'], 'Windows_NT'))
131+
132+
# On MAC let CONDA update install paths
133+
#
134+
# This task will only run if variable `NeedsPythonFunctionalReqs` is true.
135+
- bash: |
136+
sudo chown -R $USER $CONDA
137+
displayName: 'Give CONDA permission to its own files'
138+
condition: and(succeeded(), eq(variables['NeedsPythonFunctionalReqs'], 'true'), eq(variables['Agent.Os'], 'Darwin'))
139+
140+
# Create the two anaconda environments
141+
#
142+
# This task will only run if variable `NeedsPythonFunctionalReqs` is true.
143+
#
144+
- script: |
145+
conda env create --quiet --force --file build/ci/conda_env_1.yml
146+
conda env create --quiet --force --file build/ci/conda_env_2.yml
147+
displayName: 'Create CONDA Environments'
122148
condition: and(succeeded(), eq(variables['NeedsPythonFunctionalReqs'], 'true'))
123149
150+
# Run the pip installs in the 3 environments (darwin linux)
151+
- bash: |
152+
source activate base
153+
conda install --quiet -y --file build/ci/conda_base.yml
154+
python -m pip install --upgrade -r build/conda-functional-requirements.txt
155+
source activate conda_env_1
156+
python -m pip install --upgrade -r build/conda-functional-requirements.txt
157+
source activate conda_env_2
158+
python -m pip install --upgrade -r build/conda-functional-requirements.txt
159+
conda deactivate
160+
displayName: 'Install Pip requirements for CONDA envs'
161+
condition: and(succeeded(), eq(variables['NeedsPythonFunctionalReqs'], 'true'), ne(variables['Agent.Os'], 'Windows_NT'))
162+
163+
# Run the pip installs in the 3 environments (windows)
164+
- script: |
165+
call activate base
166+
conda install --quiet -y --file build/ci/conda_base.yml
167+
python -m pip install --upgrade -r build/conda-functional-requirements.txt
168+
call activate conda_env_1
169+
python -m pip install --upgrade -r build/conda-functional-requirements.txt
170+
call activate conda_env_2
171+
python -m pip install --upgrade -r build/conda-functional-requirements.txt
172+
displayName: 'Install Pip requirements for CONDA envs'
173+
condition: and(succeeded(), eq(variables['NeedsPythonFunctionalReqs'], 'true'), eq(variables['Agent.Os'], 'Windows_NT'))
174+
124175
# Downgrade pywin32 on Windows due to bug https://github.com/jupyter/notebook/issues/4909
125176
#
126177
# This task will only run if variable `NeedsPythonFunctionalReqs` is true.

build/ci/vscode-python-nightly-flake-ci.yaml

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -50,22 +50,6 @@ stages:
5050
steps:
5151
- template: templates/test_phases.yml
5252

53-
- job: 'Py36'
54-
dependsOn: []
55-
timeoutInMinutes: 120
56-
strategy:
57-
matrix:
58-
'Functional':
59-
PythonVersion: '3.6'
60-
TestsToRun: 'testfunctional'
61-
NeedsPythonTestReqs: true
62-
NeedsPythonFunctionalReqs: true
63-
VSCODE_PYTHON_ROLLING: true
64-
pool:
65-
vmImage: 'ubuntu-16.04'
66-
steps:
67-
- template: templates/test_phases.yml
68-
6953
- stage: Mac
7054
dependsOn:
7155
- Build
@@ -85,22 +69,6 @@ stages:
8569
steps:
8670
- template: templates/test_phases.yml
8771

88-
- job: 'Py36'
89-
dependsOn: []
90-
timeoutInMinutes: 120
91-
strategy:
92-
matrix:
93-
'Functional':
94-
PythonVersion: '3.6'
95-
TestsToRun: 'testfunctional'
96-
NeedsPythonTestReqs: true
97-
NeedsPythonFunctionalReqs: true
98-
VSCODE_PYTHON_ROLLING: true
99-
pool:
100-
vmImage: '$(vmImageMacOS)'
101-
steps:
102-
- template: templates/test_phases.yml
103-
10472
- stage: Windows
10573
dependsOn:
10674
- Build
@@ -119,19 +87,3 @@ stages:
11987
vmImage: 'vs2017-win2016'
12088
steps:
12189
- template: templates/test_phases.yml
122-
123-
- job: 'Py36'
124-
dependsOn: []
125-
timeoutInMinutes: 120
126-
strategy:
127-
matrix:
128-
'Functional':
129-
PythonVersion: '3.6'
130-
TestsToRun: 'testfunctional'
131-
NeedsPythonTestReqs: true
132-
NeedsPythonFunctionalReqs: true
133-
VSCODE_PYTHON_ROLLING: true
134-
pool:
135-
vmImage: 'vs2017-win2016'
136-
steps:
137-
- template: templates/test_phases.yml
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# List of requirements for conda environments that cannot be installed using conda
2+
livelossplot
3+
versioneer
4+
flake8
5+
autopep8
6+
bandit
7+
black ; python_version>='3.6'
8+
yapf
9+
pylint
10+
pycodestyle
11+
prospector
12+
pydocstyle
13+
nose
14+
pytest==4.6.9 # Last version of pytest with Python 2.7 support
15+
rope
16+
flask
17+
django
18+
isort
19+
pathlib2>=2.2.0 ; python_version<'3.6' # Python 2.7 compatibility (pytest)
Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,2 @@
1-
# List of requirements for functional tests
2-
versioneer
3-
jupyter
4-
numpy
5-
matplotlib
6-
pandas
7-
livelossplot
1+
# List of requirements for functional tests
2+
versioneer

news/3 Code Health/10134.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add conda environments to nightly test runs

src/client/common/asyncDisposableRegistry.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export class AsyncDisposableRegistry implements IAsyncDisposableRegistry {
1212
public async dispose(): Promise<void> {
1313
const promises = this._list.map(l => l.dispose());
1414
await Promise.all(promises);
15+
this._list = [];
1516
}
1617

1718
public push(disposable?: IDisposable | IAsyncDisposable) {

src/client/common/process/proc.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ export class ProcessService extends EventEmitter implements IProcessService {
6262
const proc = spawn(file, args, spawnOptions);
6363
let procExited = false;
6464
const disposable: IDisposable = {
65-
dispose: () => {
65+
// tslint:disable-next-line: no-function-expression
66+
dispose: function() {
6667
if (proc && !proc.killed && !procExited) {
6768
ProcessService.kill(proc.pid);
6869
}

src/client/common/process/pythonDaemon.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export class PythonDaemonExecutionService implements IPythonDaemonExecutionServi
6666
try {
6767
// The daemon should die as a result of this.
6868
this.connection.sendNotification(new NotificationType('exit'));
69+
this.proc.kill();
6970
} catch {
7071
noop();
7172
}

src/client/common/process/pythonDaemonPool.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export class PythonDaemonExecutionServicePool implements IPythonDaemonExecutionS
3838
private readonly observableDaemons: IPythonDaemonExecutionService[] = [];
3939
private readonly envVariables: NodeJS.ProcessEnv;
4040
private readonly pythonPath: string;
41+
private _disposed = false;
4142
constructor(
4243
private readonly logger: IProcessLogger,
4344
private readonly disposables: IDisposableRegistry,
@@ -67,6 +68,7 @@ export class PythonDaemonExecutionServicePool implements IPythonDaemonExecutionS
6768

6869
// Always ignore warnings as the user should never see the output of the daemon running
6970
this.envVariables[PYTHON_WARNINGS] = 'ignore';
71+
this.disposables.push(this);
7072
}
7173
public async initialize() {
7274
const promises = Promise.all(
@@ -85,7 +87,7 @@ export class PythonDaemonExecutionServicePool implements IPythonDaemonExecutionS
8587
await Promise.all([promises, promises2]);
8688
}
8789
public dispose() {
88-
noop();
90+
this._disposed = true;
8991
}
9092
public async getInterpreterInformation(): Promise<InterpreterInfomation | undefined> {
9193
const msg = { args: ['GetPythonVersion'] };
@@ -235,7 +237,7 @@ export class PythonDaemonExecutionServicePool implements IPythonDaemonExecutionS
235237
completed = true;
236238
if (!daemonProc || (!daemonProc.killed && ProcessService.isAlive(daemonProc.pid))) {
237239
this.pushDaemonIntoPool('ObservableDaemon', execService);
238-
} else {
240+
} else if (!this._disposed) {
239241
// Possible daemon is dead (explicitly killed or died due to some error).
240242
this.addDaemonService('ObservableDaemon').ignoreErrors();
241243
}

src/client/common/terminal/helper.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export class TerminalHelper implements ITerminalHelper {
2828
@inject(IPlatformService) private readonly platform: IPlatformService,
2929
@inject(ITerminalManager) private readonly terminalManager: ITerminalManager,
3030
@inject(ICondaService) private readonly condaService: ICondaService,
31-
@inject(IInterpreterService) private readonly interpreterService: IInterpreterService,
31+
@inject(IInterpreterService) readonly interpreterService: IInterpreterService,
3232
@inject(IConfigurationService) private readonly configurationService: IConfigurationService,
3333
@inject(ITerminalActivationCommandProvider)
3434
@named(TerminalActivationProviders.conda)
@@ -71,9 +71,9 @@ export class TerminalHelper implements ITerminalHelper {
7171
const providers = [this.pipenv, this.pyenv, this.bashCShellFish, this.commandPromptAndPowerShell];
7272
const promise = this.getActivationCommands(resource || undefined, interpreter, terminalShellType, providers);
7373
this.sendTelemetry(
74-
resource,
7574
terminalShellType,
7675
EventName.PYTHON_INTERPRETER_ACTIVATION_FOR_TERMINAL,
76+
interpreter,
7777
promise
7878
).ignoreErrors();
7979
return promise;
@@ -89,22 +89,21 @@ export class TerminalHelper implements ITerminalHelper {
8989
const providers = [this.bashCShellFish, this.commandPromptAndPowerShell];
9090
const promise = this.getActivationCommands(resource, interpreter, shell, providers);
9191
this.sendTelemetry(
92-
resource,
9392
shell,
9493
EventName.PYTHON_INTERPRETER_ACTIVATION_FOR_RUNNING_CODE,
94+
interpreter,
9595
promise
9696
).ignoreErrors();
9797
return promise;
9898
}
9999
@traceDecorators.error('Failed to capture telemetry')
100100
protected async sendTelemetry(
101-
resource: Resource,
102101
terminalShellType: TerminalShellType,
103102
eventName: EventName,
103+
interpreter: PythonInterpreter | undefined,
104104
promise: Promise<string[] | undefined>
105105
): Promise<void> {
106106
let hasCommands = false;
107-
const interpreter = await this.interpreterService.getActiveInterpreter(resource);
108107
let failed = false;
109108
try {
110109
const cmds = await promise;

src/client/common/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export interface IOutputChannel extends OutputChannel {}
2727
export const IDocumentSymbolProvider = Symbol('IDocumentSymbolProvider');
2828
export interface IDocumentSymbolProvider extends DocumentSymbolProvider {}
2929
export const IsWindows = Symbol('IS_WINDOWS');
30-
export const IDisposableRegistry = Symbol('IDiposableRegistry');
30+
export const IDisposableRegistry = Symbol('IDisposableRegistry');
3131
export type IDisposableRegistry = { push(disposable: Disposable): void };
3232
export const IMemento = Symbol('IGlobalMemento');
3333
export const GLOBAL_MEMENTO = Symbol('IGlobalMemento');

src/client/datascience/interactive-common/interactiveBase.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,13 @@ export abstract class InteractiveBase extends WebViewHost<IInteractiveWindowMapp
326326
super.dispose();
327327
this.listeners.forEach(l => l.dispose());
328328
this.updateContexts(undefined);
329+
330+
// When closing an editor, dispose of the notebook associated with it.
331+
// This won't work when we have multiple views of the notebook though. Notebook ownership
332+
// should probably move to whatever owns the backing model.
333+
return this.notebook?.dispose().then(() => {
334+
this._notebook = undefined;
335+
});
329336
}
330337

331338
public startProgress() {
@@ -1412,7 +1419,12 @@ export abstract class InteractiveBase extends WebViewHost<IInteractiveWindowMapp
14121419
// Request our new list of variables
14131420
const response: IJupyterVariablesResponse = this._notebook
14141421
? await this.jupyterVariables.getVariables(this._notebook, args)
1415-
: { totalCount: 0, pageResponse: [], pageStartIndex: args.startIndex, executionCount: args.executionCount };
1422+
: {
1423+
totalCount: 0,
1424+
pageResponse: [],
1425+
pageStartIndex: args?.startIndex,
1426+
executionCount: args?.executionCount
1427+
};
14161428

14171429
this.postMessage(InteractiveWindowMessages.GetVariablesResponse, response).ignoreErrors();
14181430
sendTelemetryEvent(Telemetry.VariableExplorerVariableCount, undefined, { variableCount: response.totalCount });

src/client/datascience/interactive-ipynb/nativeEditor.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -512,18 +512,6 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
512512
protected async close(): Promise<void> {
513513
// Fire our event
514514
this.closedEvent.fire(this);
515-
516-
// Restart our kernel so that execution counts are reset
517-
let oldAsk: boolean | undefined = false;
518-
const settings = this.configuration.getSettings(await this.getOwningResource());
519-
if (settings && settings.datascience) {
520-
oldAsk = settings.datascience.askForKernelRestart;
521-
settings.datascience.askForKernelRestart = false;
522-
}
523-
await this.restartKernel(true);
524-
if (oldAsk && settings && settings.datascience) {
525-
settings.datascience.askForKernelRestart = true;
526-
}
527515
}
528516

529517
protected saveAll() {

0 commit comments

Comments
 (0)