-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Add the base Python environments "watchers". #13735
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1b222af
4a9c7a6
7f3c22e
0a10ea5
b5d60b7
69d7fe6
df16bf1
077c6e0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| // tslint:disable:max-classes-per-file | ||
|
|
||
| import { Event, EventEmitter, Uri } from 'vscode'; | ||
| import { PythonEnvKind } from './info'; | ||
|
|
||
| /** | ||
| * The most basic info for a Python environments event. | ||
| * | ||
| * @prop kind - the env kind, if any, affected by the event | ||
| */ | ||
| export type BasicPythonEnvsChangedEvent = { | ||
| kind?: PythonEnvKind; | ||
| }; | ||
|
|
||
| /** | ||
| * The full set of possible info for a Python environments event. | ||
| * | ||
| * @prop searchLocation - the location, if any, affected by the event | ||
| */ | ||
| export type PythonEnvsChangedEvent = BasicPythonEnvsChangedEvent & { | ||
| searchLocation?: Uri; | ||
| }; | ||
|
|
||
| /** | ||
| * A "watcher" for events related to changes to Python environemts. | ||
| * | ||
| * The watcher will notify listeners (callbacks registered through | ||
| * `onChanged`) of events at undetermined times. The actual emitted | ||
| * events, their source, and the timing is entirely up to the watcher | ||
| * implementation. | ||
| */ | ||
| export interface IPythonEnvsWatcher<E extends BasicPythonEnvsChangedEvent = PythonEnvsChangedEvent> { | ||
| /** | ||
| * The hook for registering event listeners (callbacks). | ||
| */ | ||
| readonly onChanged: Event<E>; | ||
| } | ||
|
|
||
| /** | ||
| * This provides the fundamental functionality of a watcher for any event type. | ||
| * | ||
| * Consumers register listeners (callbacks) using `onChanged`. Each | ||
| * listener is invoked when `fire()` is called. | ||
| * | ||
| * Note that in most cases classes will not inherit from this classes, | ||
| * but instead keep a private watcher property. The rule of thumb | ||
| * is to follow whether or not consumers of *that* class should be able | ||
| * to trigger events (via `fire()`). | ||
| */ | ||
| class WatcherBase<T> implements IPythonEnvsWatcher<T> { | ||
| /** | ||
| * The hook for registering event listeners (callbacks). | ||
| */ | ||
| public readonly onChanged: Event<T>; | ||
| private readonly didChange = new EventEmitter<T>(); | ||
|
|
||
| constructor() { | ||
| this.onChanged = this.didChange.event; | ||
| } | ||
|
|
||
| /** | ||
| * Send the event to all registered listeners. | ||
| */ | ||
| public fire(event: T) { | ||
| this.didChange.fire(event); | ||
| } | ||
| } | ||
|
|
||
| // The use cases for BasicPythonEnvsWatcher are currently hypothetical. | ||
| // However, there's a real chance they may prove useful for the concrete | ||
| // locators. Adding BasicPythonEnvsWatcher later will be much harder | ||
| // than removing it later, so we're leaving it for now. | ||
|
|
||
| /** | ||
| * A watcher for the basic Python environments events. | ||
| * | ||
| * This should be used only in low-level cases, with the most | ||
| * rudimentary watchers. Most of the time `PythonEnvsWatcher` | ||
| * should be used instead. | ||
| * | ||
| * Note that in most cases classes will not inherit from this classes, | ||
| * but instead keep a private watcher property. The rule of thumb | ||
| * is to follow whether or not consumers of *that* class should be able | ||
| * to trigger events (via `fire()`). | ||
| */ | ||
| export class BasicPythonEnvsWatcher extends WatcherBase<BasicPythonEnvsChangedEvent> { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In which use cases would we use a
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's for the case that a watcher does not possibly provide non-basic info (currently only There isn't much cost to leaving |
||
| /** | ||
| * Fire an event based on the given info. | ||
| */ | ||
| public trigger(kind?: PythonEnvKind) { | ||
| this.fire({ kind }); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * A general-use watcher for Python environments events. | ||
| * | ||
| * In most cases this is the class you will want to use or subclass. | ||
| * Only in low-level cases should you consider using `BasicPythonEnvsWatcher`. | ||
| * | ||
| * Note that in most cases classes will not inherit from this classes, | ||
| * but instead keep a private watcher property. The rule of thumb | ||
| * is to follow whether or not consumers of *that* class should be able | ||
| * to trigger events (via `fire()`). | ||
| */ | ||
| export class PythonEnvsWatcher extends WatcherBase<PythonEnvsChangedEvent> { | ||
| /** | ||
| * Fire an event based on the given info. | ||
| */ | ||
| public trigger(kind?: PythonEnvKind, searchLocation?: Uri) { | ||
| this.fire({ kind, searchLocation }); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| import { Disposable, Event } from 'vscode'; | ||
| import { IPythonEnvsWatcher, PythonEnvsChangedEvent, PythonEnvsWatcher } from './watcher'; | ||
|
|
||
| /** | ||
| * A wrapper around a set of watchers, exposing them as a single watcher. | ||
| * | ||
| * If any of the wrapped watchers emits an event then this wrapper | ||
| * emits that event. | ||
| */ | ||
| export class PythonEnvsWatchers implements IPythonEnvsWatcher { | ||
| public readonly onChanged: Event<PythonEnvsChangedEvent>; | ||
| private watcher = new PythonEnvsWatcher(); | ||
|
|
||
| constructor(watchers: ReadonlyArray<IPythonEnvsWatcher>) { | ||
| this.onChanged = this.watcher.onChanged; | ||
| watchers.forEach((w) => { | ||
| w.onChanged((e) => this.watcher.fire(e)); | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| // This matches the `vscode.Event` arg. | ||
| type EnvsEventListener = (e: PythonEnvsChangedEvent) => unknown; | ||
|
|
||
| /** | ||
| * A watcher wrapper that can be disabled. | ||
| * | ||
| * If disabled, events emitted by the wrapped watcher are discarded. | ||
| */ | ||
| export class DisableableEnvsWatcher implements IPythonEnvsWatcher { | ||
| private enabled = true; | ||
| constructor( | ||
| // To wrap more than one use `PythonEnvWatchers`. | ||
| private readonly wrapped: IPythonEnvsWatcher | ||
|
ericsnowcurrently marked this conversation as resolved.
|
||
| ) {} | ||
|
|
||
| /** | ||
| * Ensure that the watcher is enabled. | ||
| */ | ||
| public enable() { | ||
| this.enabled = true; | ||
| } | ||
|
|
||
| /** | ||
| * Ensure that the watcher is disabled. | ||
| */ | ||
| public disable() { | ||
| this.enabled = false; | ||
| } | ||
|
|
||
| // This matches the signature of `vscode.Event`. | ||
| public onChanged(listener: EnvsEventListener, thisArgs?: unknown, disposables?: Disposable[]): Disposable { | ||
| return this.wrapped.onChanged( | ||
| (e: PythonEnvsChangedEvent) => { | ||
| if (this.enabled) { | ||
| listener(e); | ||
| } | ||
| }, | ||
| thisArgs, | ||
| disposables | ||
| ); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,163 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| import * as assert from 'assert'; | ||
| import { Uri } from 'vscode'; | ||
| import { PythonEnvKind } from '../../../client/pythonEnvironments/base/info'; | ||
| import { | ||
| BasicPythonEnvsChangedEvent, | ||
| BasicPythonEnvsWatcher, | ||
| PythonEnvsChangedEvent, | ||
| PythonEnvsWatcher | ||
| } from '../../../client/pythonEnvironments/base/watcher'; | ||
|
|
||
| const KINDS_TO_TEST = [ | ||
| PythonEnvKind.Unknown, | ||
| PythonEnvKind.System, | ||
| PythonEnvKind.Custom, | ||
| PythonEnvKind.OtherGlobal, | ||
| PythonEnvKind.Venv, | ||
| PythonEnvKind.Conda, | ||
| PythonEnvKind.OtherVirtual | ||
| ]; | ||
|
|
||
| suite('pyenvs watcher - BasicPythonEnvsWatcher', () => { | ||
| suite('fire()', () => { | ||
| test('empty event', () => { | ||
| const expected: BasicPythonEnvsChangedEvent = {}; | ||
| const watcher = new BasicPythonEnvsWatcher(); | ||
| let event: BasicPythonEnvsChangedEvent | undefined; | ||
| watcher.onChanged((e) => { | ||
| event = e; | ||
| }); | ||
|
|
||
| watcher.fire(expected); | ||
|
|
||
| assert.equal(event, expected); | ||
| }); | ||
|
|
||
| KINDS_TO_TEST.forEach((kind) => { | ||
| test(`non-empty event ("${kind}")`, () => { | ||
| const expected: BasicPythonEnvsChangedEvent = { | ||
| kind: kind | ||
| }; | ||
| const watcher = new BasicPythonEnvsWatcher(); | ||
| let event: BasicPythonEnvsChangedEvent | undefined; | ||
| watcher.onChanged((e) => { | ||
| event = e; | ||
| }); | ||
|
|
||
| watcher.fire(expected); | ||
|
|
||
| assert.equal(event, expected); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| suite('trigger()', () => { | ||
| test('empty event', () => { | ||
| const expected: BasicPythonEnvsChangedEvent = { | ||
| kind: undefined | ||
| }; | ||
| const watcher = new BasicPythonEnvsWatcher(); | ||
| let event: BasicPythonEnvsChangedEvent | undefined; | ||
| watcher.onChanged((e) => { | ||
| event = e; | ||
| }); | ||
|
|
||
| watcher.trigger(); | ||
|
|
||
| assert.deepEqual(event, expected); | ||
| }); | ||
|
|
||
| KINDS_TO_TEST.forEach((kind) => { | ||
| test(`non-empty event ("${kind}")`, () => { | ||
| const expected: BasicPythonEnvsChangedEvent = { | ||
| kind: kind | ||
| }; | ||
| const watcher = new BasicPythonEnvsWatcher(); | ||
| let event: BasicPythonEnvsChangedEvent | undefined; | ||
| watcher.onChanged((e) => { | ||
| event = e; | ||
| }); | ||
|
|
||
| watcher.trigger(kind); | ||
|
|
||
| assert.deepEqual(event, expected); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| suite('pyenvs watcher - PythonEnvsWatcher', () => { | ||
| const location = Uri.file('some-dir'); | ||
|
|
||
| suite('fire()', () => { | ||
| test('empty event', () => { | ||
| const expected: PythonEnvsChangedEvent = {}; | ||
| const watcher = new PythonEnvsWatcher(); | ||
| let event: PythonEnvsChangedEvent | undefined; | ||
| watcher.onChanged((e) => { | ||
| event = e; | ||
| }); | ||
|
|
||
| watcher.fire(expected); | ||
|
|
||
| assert.equal(event, expected); | ||
| }); | ||
|
|
||
| KINDS_TO_TEST.forEach((kind) => { | ||
| test(`non-empty event ("${kind}")`, () => { | ||
| const expected: PythonEnvsChangedEvent = { | ||
| kind: kind, | ||
| searchLocation: location | ||
| }; | ||
| const watcher = new PythonEnvsWatcher(); | ||
| let event: PythonEnvsChangedEvent | undefined; | ||
| watcher.onChanged((e) => { | ||
| event = e; | ||
| }); | ||
|
|
||
| watcher.fire(expected); | ||
|
|
||
| assert.equal(event, expected); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| suite('trigger()', () => { | ||
| test('empty event', () => { | ||
| const expected: PythonEnvsChangedEvent = { | ||
| kind: undefined, | ||
| searchLocation: undefined | ||
| }; | ||
| const watcher = new PythonEnvsWatcher(); | ||
| let event: PythonEnvsChangedEvent | undefined; | ||
| watcher.onChanged((e) => { | ||
| event = e; | ||
| }); | ||
|
|
||
| watcher.trigger(); | ||
|
|
||
| assert.deepEqual(event, expected); | ||
| }); | ||
|
|
||
| KINDS_TO_TEST.forEach((kind) => { | ||
| test(`non-empty event ("${kind}")`, () => { | ||
| const expected: PythonEnvsChangedEvent = { | ||
| kind: kind, | ||
| searchLocation: location | ||
| }; | ||
| const watcher = new PythonEnvsWatcher(); | ||
| let event: PythonEnvsChangedEvent | undefined; | ||
| watcher.onChanged((e) => { | ||
| event = e; | ||
| }); | ||
|
|
||
| watcher.trigger(kind, location); | ||
|
|
||
| assert.deepEqual(event, expected); | ||
| }); | ||
| }); | ||
| }); | ||
| }); |
Uh oh!
There was an error while loading. Please reload this page.