Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
bc9cd8b
Add an optional onUpdated event to the iterator returned by ILocator.…
Sep 16, 2020
f687921
fully bump raw kernel (#13941)
IanMatthewHuff Sep 16, 2020
64c8526
Virtualenvwrapper locator (#13895)
kimadeline Sep 16, 2020
a9b5603
Clean up eslint errors (#13951)
karthiknadig Sep 16, 2020
016cce8
Add a legacy DI adapter for the Python envs component. (#13858)
Sep 16, 2020
7b3b541
Extension api for DataScience (#13923)
DonJayamanne Sep 17, 2020
a7c3b7f
Fix installing ipykernel into interpreters for raw kernel (#13959)
IanMatthewHuff Sep 17, 2020
89b47a0
Fix kernel and server name missing in certain situations (#13974)
rchiodo Sep 17, 2020
7a86cf0
Extension API to return path to debugpy for DS (#13973)
DonJayamanne Sep 17, 2020
6b94c8b
Use the component adapter in the extension. (#13869)
Sep 17, 2020
10b8965
Disable split views of custom editors (#13985)
DonJayamanne Sep 17, 2020
245f393
Fix backup storage by looking at the options correctly (#13983)
rchiodo Sep 18, 2020
8ec292e
update test plan for ipykernel (python repo) (#13993)
IanMatthewHuff Sep 18, 2020
da38997
Find interpreter based on hash in kernelspec of nb metadata (#13856)
DonJayamanne Sep 21, 2020
2cc2c93
Pyenv locator (#13996)
kimadeline Sep 21, 2020
ee0cad9
merge
gramster Sep 21, 2020
889375b
Revert "merge"
rchiodo Sep 22, 2020
8c0d3aa
Remove cwd while running using isolate script (#14014)
karthiknadig Sep 22, 2020
a7176fc
Upgrade isort to 5.5.3 (#14035)
Sep 22, 2020
ec568ca
Modify environment info worker to support new type and to work with r…
Sep 22, 2020
68e127a
Remove cross process lock (#14036)
joyceerhl Sep 22, 2020
352d9a5
Add API to get language server from external extensions (#14021)
rchiodo Sep 22, 2020
a531cba
Fix failing virtualenvwrapper Windows unit tests (#14012)
kimadeline Sep 22, 2020
221c0d5
make sure that we run our show code when a custom editor is shown (#1…
IanMatthewHuff Sep 22, 2020
367e002
Rely on AppInsights API for exceptions (#13878)
Sep 22, 2020
ed0e2a7
Fix HTML escaping to match what Jupyter does (#14038)
rchiodo Sep 22, 2020
f60eeaf
Have dependabot ignore pytest and py (#14039)
brettcannon Sep 23, 2020
5f09a26
Added environments reducer (#13953)
Sep 23, 2020
fd5abc1
Merge release back to master (#14059)
Sep 23, 2020
e31046b
Fix object sort order in tools tests (#14050)
karthiknadig Sep 23, 2020
66a4cb1
Update ubuntu images for GPU issue fix (#14064)
karthiknadig Sep 23, 2020
1a0c790
Added environments resolver (#14019)
Sep 25, 2020
6abde6b
Add Windows unit tests to the PR pipeline (#14106)
kimadeline Sep 25, 2020
d498e85
Do not opt users out of the insiders program if they have a stable ve…
Sep 25, 2020
12de21d
Do not quote isolated in exec module (#14108)
karthiknadig Sep 25, 2020
94da334
Update cell output and metadata using Edit API (#13737)
DonJayamanne Sep 25, 2020
57fedbd
Fix unit test broken by recent revert (#14122)
karthiknadig Sep 28, 2020
8b87f08
Merge remote-tracking branch 'origin/main' into rchiodo/merge_main_nods
rchiodo Sep 28, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add an optional onUpdated event to the iterator returned by ILocator.…
…iterEnvs(). (#13950)

In order to ensure that ILocator.iterEnvs() can finish as fast as possible, we add a side-channel event for each iteration. This event fires any time an already-yielded env info object is updated (e.g. reduced/merged or resolved/completed). The update only relates to operations triggered by that particular iteration. ILocator.onChanged remains separate and only relates to when the locators finds a new env, notices one was removed, or that one was otherwise fundamentally changed.

There are two similar approaches we could take for this update event. Either we added the event as a property of IPythonEnvsIterator or we change the return value of ILocator.iterEnvs() to be a 2-tuple ([PythonEnvsIterator, Event<PythonEnvUpdatedEvent>]). We took the property approach since most of the time callers won't need to worry about the update events.
  • Loading branch information
Eric Snow
Eric Snow authored Sep 16, 2020
commit bc9cd8bc8982981342740631c57fd222c2a62890
72 changes: 65 additions & 7 deletions src/client/pythonEnvironments/base/locator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,68 @@
import { Event, Uri } from 'vscode';
import { iterEmpty } from '../../common/utils/async';
import { PythonEnvInfo, PythonEnvKind } from './info';
import { BasicPythonEnvsChangedEvent, IPythonEnvsWatcher, PythonEnvsChangedEvent, PythonEnvsWatcher } from './watcher';
import {
BasicPythonEnvsChangedEvent,
IPythonEnvsWatcher,
PythonEnvsChangedEvent,
PythonEnvsWatcher
} from './watcher';

/**
* An async iterator of `PythonEnvInfo`.
* A single update to a previously provided Python env object.
*/
export type PythonEnvsIterator = AsyncIterator<PythonEnvInfo, void>;
export type PythonEnvUpdatedEvent = {
/**
* The env info that was previously provided.
*
* If the event comes from `IPythonEnvsIterator.onUpdated` then
* `old` was previously yielded during iteration.
*/
old: PythonEnvInfo;
/**
* The env info that replaces the old info.
*/
new: PythonEnvInfo;
};

/**
* A fast async iterator of Python envs, which may have incomplete info.
*
* Each object yielded by the iterator represents a unique Python
* environment.
*
* The iterator is not required to have provide all info about
* an environment. However, each yielded item will at least
* include all the `PythonEnvBaseInfo` data.
*
* During iteration the information for an already
* yielded object may be updated. Rather than updating the yielded
* object or yielding it again with updated info, the update is
* emitted by the iterator's `onUpdated` (event) property. Once there are no more updates, the event emits
* `null`.
*
* If the iterator does not have `onUpdated` then it means the
* provider does not support updates.
*
* Callers can usually ignore the update event entirely and rely on
* the locator to provide sufficiently complete information.
*/
export interface IPythonEnvsIterator extends AsyncIterator<PythonEnvInfo, void> {
/**
* Provides possible updates for already-iterated envs.
*
* Once there are no more updates, `null` is emitted.
*
* If this property is not provided then it means the iterator does
* not support updates.
*/
onUpdated?: Event<PythonEnvUpdatedEvent | null>;
}

/**
* An empty Python envs iterator.
*/
export const NOOP_ITERATOR: PythonEnvsIterator = iterEmpty<PythonEnvInfo>();
export const NOOP_ITERATOR: IPythonEnvsIterator = iterEmpty<PythonEnvInfo>();

/**
* The most basic info to send to a locator when requesting environments.
Expand Down Expand Up @@ -64,11 +115,18 @@ export interface ILocator<E extends BasicPythonEnvsChangedEvent = PythonEnvsChan
*
* Locators are not required to have provide all info about
* an environment. However, each yielded item will at least
* include all the `PythonEnvBaseInfo` data.
* include all the `PythonEnvBaseInfo` data. To ensure all
* possible information is filled in, call `ILocator.resolveEnv()`.
*
* Updates to yielded objects may be provided via the optional
* `onUpdated` property of the iterator. However, callers can
* usually ignore the update event entirely and rely on the
* locator to provide sufficiently complete information.
*
* @param query - if provided, the locator will limit results to match
* @returns - the fast async iterator of Python envs, which may have incomplete info
*/
iterEnvs(query?: QueryForEvent<E>): PythonEnvsIterator;
iterEnvs(query?: QueryForEvent<E>): IPythonEnvsIterator;

/**
* Find the given Python environment and fill in as much missing info as possible.
Expand Down Expand Up @@ -111,7 +169,7 @@ export abstract class LocatorBase<E extends BasicPythonEnvsChangedEvent = Python
this.onChanged = watcher.onChanged;
}

public abstract iterEnvs(query?: QueryForEvent<E>): PythonEnvsIterator;
public abstract iterEnvs(query?: QueryForEvent<E>): IPythonEnvsIterator;

public async resolveEnv(_env: string | PythonEnvInfo): Promise<PythonEnvInfo | undefined> {
return undefined;
Expand Down
45 changes: 41 additions & 4 deletions src/client/pythonEnvironments/base/locators.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,48 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import { EventEmitter } from 'vscode';
import { chain } from '../../common/utils/async';
import { PythonEnvInfo } from './info';
import { ILocator, NOOP_ITERATOR, PythonEnvsIterator, PythonLocatorQuery } from './locator';
import {
ILocator,
IPythonEnvsIterator,
NOOP_ITERATOR,
PythonEnvUpdatedEvent,
PythonLocatorQuery
} from './locator';
import { DisableableEnvsWatcher, PythonEnvsWatchers } from './watchers';

/**
* Combine the `onUpdated` event of the given iterators into a single event.
*/
export function combineIterators(iterators: IPythonEnvsIterator[]): IPythonEnvsIterator {
const result: IPythonEnvsIterator = chain(iterators);
const events = iterators.map((it) => it.onUpdated).filter((v) => v);
if (!events || events.length === 0) {
// There are no sub-events, so we leave `onUpdated` undefined.
return result;
}

const emitter = new EventEmitter<PythonEnvUpdatedEvent | null>();
let numActive = events.length;
events.forEach((event) => {
event!((e: PythonEnvUpdatedEvent | null) => {
if (e === null) {
numActive -= 1;
if (numActive === 0) {
// All the sub-events are done so we're done.
emitter.fire(null);
}
} else {
emitter.fire(e);
}
});
});
result.onUpdated = emitter.event;
return result;
}

/**
* A wrapper around a set of locators, exposing them as a single locator.
*
Expand All @@ -19,9 +56,9 @@ export class Locators extends PythonEnvsWatchers implements ILocator {
super(locators);
}

public iterEnvs(query?: PythonLocatorQuery): PythonEnvsIterator {
public iterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator {
const iterators = this.locators.map((loc) => loc.iterEnvs(query));
return chain(iterators);
return combineIterators(iterators);
}

public async resolveEnv(env: string | PythonEnvInfo): Promise<PythonEnvInfo | undefined> {
Expand Down Expand Up @@ -50,7 +87,7 @@ export class DisableableLocator extends DisableableEnvsWatcher implements ILocat
super(locator);
}

public iterEnvs(query?: PythonLocatorQuery): PythonEnvsIterator {
public iterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator {
if (!this.enabled) {
return NOOP_ITERATOR;
}
Expand Down
20 changes: 15 additions & 5 deletions src/client/pythonEnvironments/discovery/locators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
import { traceDecorators } from '../../../common/logger';
import { IPlatformService } from '../../../common/platform/types';
import { IDisposableRegistry } from '../../../common/types';
import { chain, createDeferred, Deferred } from '../../../common/utils/async';
import { createDeferred, Deferred } from '../../../common/utils/async';
import { OSType } from '../../../common/utils/platform';
import {
CONDA_ENV_FILE_SERVICE,
Expand All @@ -21,8 +21,18 @@ import {
} from '../../../interpreter/contracts';
import { IServiceContainer } from '../../../ioc/types';
import { PythonEnvInfo } from '../../base/info';
import { ILocator, Locator, NOOP_ITERATOR, PythonEnvsIterator, PythonLocatorQuery } from '../../base/locator';
import { DisableableLocator, Locators } from '../../base/locators';
import {
ILocator,
IPythonEnvsIterator,
Locator,
NOOP_ITERATOR,
PythonLocatorQuery,
} from '../../base/locator';
import {
combineIterators,
DisableableLocator,
Locators,
} from '../../base/locators';
import { PythonEnvironment } from '../../info';
import { isHiddenInterpreter } from './services/interpreterFilter';
import { GetInterpreterLocatorOptions } from './types';
Expand Down Expand Up @@ -83,7 +93,7 @@ export class WorkspaceLocators extends Locator {
folders.onRemoved((root: Uri) => this.removeRoot(root));
}

public iterEnvs(query?: PythonLocatorQuery): PythonEnvsIterator {
public iterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator {
const iterators = Object.keys(this.locators).map((key) => {
if (query?.searchLocations) {
const root = this.roots[key];
Expand All @@ -95,7 +105,7 @@ export class WorkspaceLocators extends Locator {
const locator = this.locators[key];
return locator.iterEnvs(query);
});
return chain(iterators);
return combineIterators(iterators);
}

public async resolveEnv(env: string | PythonEnvInfo): Promise<PythonEnvInfo | undefined> {
Expand Down
4 changes: 2 additions & 2 deletions src/client/pythonEnvironments/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import * as vscode from 'vscode';
import { IServiceContainer, IServiceManager } from '../ioc/types';
import { PythonEnvInfo } from './base/info';
import { ILocator, PythonEnvsIterator, PythonLocatorQuery } from './base/locator';
import { ILocator, IPythonEnvsIterator, PythonLocatorQuery } from './base/locator';
import { PythonEnvsChangedEvent } from './base/watcher';
import { ExtensionLocators, WorkspaceLocators } from './discovery/locators';
import { registerForIOC } from './legacyIOC';
Expand Down Expand Up @@ -33,7 +33,7 @@ export class PythonEnvironments implements ILocator {
return this.locators.onChanged;
}

public iterEnvs(query?: PythonLocatorQuery): PythonEnvsIterator {
public iterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator {
return this.locators.iterEnvs(query);
}

Expand Down
6 changes: 3 additions & 3 deletions src/test/pythonEnvironments/base/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
PythonReleaseLevel,
PythonVersion
} from '../../../client/pythonEnvironments/base/info';
import { Locator, PythonEnvsIterator, PythonLocatorQuery } from '../../../client/pythonEnvironments/base/locator';
import { IPythonEnvsIterator, Locator, PythonLocatorQuery } from '../../../client/pythonEnvironments/base/locator';
import { PythonEnvsChangedEvent } from '../../../client/pythonEnvironments/base/watcher';

export function createEnv(
Expand Down Expand Up @@ -111,7 +111,7 @@ export class SimpleLocator extends Locator {
public fire(event: PythonEnvsChangedEvent) {
this.emitter.fire(event);
}
public iterEnvs(query?: PythonLocatorQuery): PythonEnvsIterator {
public iterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator {
const deferred = this.deferred;
const callbacks = this.callbacks;
let envs = this.envs;
Expand Down Expand Up @@ -161,6 +161,6 @@ export class SimpleLocator extends Locator {
}
}

export async function getEnvs(iterator: PythonEnvsIterator): Promise<PythonEnvInfo[]> {
export async function getEnvs(iterator: IPythonEnvsIterator): Promise<PythonEnvInfo[]> {
return flattenIterator(iterator);
}