Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
124 changes: 123 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,128 @@
# Changelog

## 2020.12.0-rc (10 December 2020)
## 2020.12.2 (15 December 2020)

### Fixes

1. Only activate discovery component when in experiment.
([#14977](https://github.com/Microsoft/vscode-python/issues/14977))

### Thanks

Thanks to the following projects which we fully rely on to provide some of
our features:

- [debugpy](https://pypi.org/project/debugpy/)
- [isort](https://pypi.org/project/isort/)
- [jedi](https://pypi.org/project/jedi/)
and [parso](https://pypi.org/project/parso/)
- [Microsoft Python Language Server](https://github.com/microsoft/python-language-server)
- [Pylance](https://github.com/microsoft/pylance-release)
- [exuberant ctags](http://ctags.sourceforge.net/) (user-installed)
- [rope](https://pypi.org/project/rope/) (user-installed)

Also thanks to the various projects we provide integrations with which help
make this extension useful:

- Debugging support:
[Django](https://pypi.org/project/Django/),
[Flask](https://pypi.org/project/Flask/),
[gevent](https://pypi.org/project/gevent/),
[Jinja](https://pypi.org/project/Jinja/),
[Pyramid](https://pypi.org/project/pyramid/),
[PySpark](https://pypi.org/project/pyspark/),
[Scrapy](https://pypi.org/project/Scrapy/),
[Watson](https://pypi.org/project/Watson/)
- Formatting:
[autopep8](https://pypi.org/project/autopep8/),
[black](https://pypi.org/project/black/),
[yapf](https://pypi.org/project/yapf/)
- Interpreter support:
[conda](https://conda.io/),
[direnv](https://direnv.net/),
[pipenv](https://pypi.org/project/pipenv/),
[pyenv](https://github.com/pyenv/pyenv),
[venv](https://docs.python.org/3/library/venv.html#module-venv),
[virtualenv](https://pypi.org/project/virtualenv/)
- Linting:
[bandit](https://pypi.org/project/bandit/),
[flake8](https://pypi.org/project/flake8/),
[mypy](https://pypi.org/project/mypy/),
[prospector](https://pypi.org/project/prospector/),
[pylint](https://pypi.org/project/pylint/),
[pydocstyle](https://pypi.org/project/pydocstyle/),
[pylama](https://pypi.org/project/pylama/)
- Testing:
[nose](https://pypi.org/project/nose/),
[pytest](https://pypi.org/project/pytest/),
[unittest](https://docs.python.org/3/library/unittest.html#module-unittest)

And finally thanks to the [Python](https://www.python.org/) development team and
community for creating a fantastic programming language and community to be a
part of!

## 2020.12.1 (15 December 2020)

### Fixes

1. Fix for extension loading issue in the latest release.
([#14977](https://github.com/Microsoft/vscode-python/issues/14977))

### Thanks

Thanks to the following projects which we fully rely on to provide some of
our features:

- [debugpy](https://pypi.org/project/debugpy/)
- [isort](https://pypi.org/project/isort/)
- [jedi](https://pypi.org/project/jedi/)
and [parso](https://pypi.org/project/parso/)
- [Microsoft Python Language Server](https://github.com/microsoft/python-language-server)
- [Pylance](https://github.com/microsoft/pylance-release)
- [exuberant ctags](http://ctags.sourceforge.net/) (user-installed)
- [rope](https://pypi.org/project/rope/) (user-installed)

Also thanks to the various projects we provide integrations with which help
make this extension useful:

- Debugging support:
[Django](https://pypi.org/project/Django/),
[Flask](https://pypi.org/project/Flask/),
[gevent](https://pypi.org/project/gevent/),
[Jinja](https://pypi.org/project/Jinja/),
[Pyramid](https://pypi.org/project/pyramid/),
[PySpark](https://pypi.org/project/pyspark/),
[Scrapy](https://pypi.org/project/Scrapy/),
[Watson](https://pypi.org/project/Watson/)
- Formatting:
[autopep8](https://pypi.org/project/autopep8/),
[black](https://pypi.org/project/black/),
[yapf](https://pypi.org/project/yapf/)
- Interpreter support:
[conda](https://conda.io/),
[direnv](https://direnv.net/),
[pipenv](https://pypi.org/project/pipenv/),
[pyenv](https://github.com/pyenv/pyenv),
[venv](https://docs.python.org/3/library/venv.html#module-venv),
[virtualenv](https://pypi.org/project/virtualenv/)
- Linting:
[bandit](https://pypi.org/project/bandit/),
[flake8](https://pypi.org/project/flake8/),
[mypy](https://pypi.org/project/mypy/),
[prospector](https://pypi.org/project/prospector/),
[pylint](https://pypi.org/project/pylint/),
[pydocstyle](https://pypi.org/project/pydocstyle/),
[pylama](https://pypi.org/project/pylama/)
- Testing:
[nose](https://pypi.org/project/nose/),
[pytest](https://pypi.org/project/pytest/),
[unittest](https://docs.python.org/3/library/unittest.html#module-unittest)

And finally thanks to the [Python](https://www.python.org/) development team and
community for creating a fantastic programming language and community to be a
part of!

## 2020.12.0 (14 December 2020)

### Enhancements

Expand Down
33 changes: 0 additions & 33 deletions src/client/common/utils/exec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,45 +44,12 @@ function parseSearchPathEntries(envVarValue: string): string[] {
* If the file does not exist or has any other problem when accessed
* then `false` is returned. The caller is responsible to determine
* whether or not the file exists.
*
* If it could not be determined if the file is executable (e.g. on
* Windows) then `undefined` is returned. This allows the caller
* to decide what to do.
*/
export async function isValidAndExecutable(filename: string): Promise<boolean | undefined> {
// There are three options when it comes to checking if a file
// is executable: `fs.stat()`, `fs.access()`, and
// `child_process.exec()`. `stat()` requires non-trivial logic
// to deal with user/group/everyone permissions. `exec()` requires
// that we make an attempt to actually execute the file, which is
// beyond the scope of this function (due to potential security
// risks). That leaves `access()`, which is what we use.
try {
// We do not need to check if the file exists. `fs.access()`
// takes care of that for us.
await fsapi.promises.access(filename, fsapi.constants.X_OK);
} catch (err) {
return false;
}
if (getOSType() === OSType.Windows) {
// On Windows a file is determined to be executable through
// its ACLs. However, the FS-related functionality provided
// by node does not check them (currently). This includes both
// `fs.stat()` and `fs.access()` (which we used above). One
// option is to use the "acl" NPM package (or something similar)
// to make the relevant checks. However, we want to avoid
// adding a dependency needlessly. Another option is to fall
// back to checking the filename's suffix (e.g. ".exe"). The
// problem there is that such a check is a bit *too* naive.
// Finally, we could still go the `exec()` route. We'd
// rather not given the concern identified above. Instead,
// it is good enough to return `undefined` and let the
// caller decide what to do about it. That is better
// than returning `true` when we aren't sure.
//
// Note that we still call `fs.access()` on Windows first,
// in case node makes it smarter later.
return undefined;
}
return true;
}
135 changes: 0 additions & 135 deletions src/client/common/utils/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,138 +111,3 @@ export function parsePosition(raw: string | number): Position {
}
return new Position(line, col);
}

/**
* Return the indentation part of the given line.
*/
export function getIndent(line: string): string {
const found = line.match(/^ */);
return found![0];
}

/**
* Return the dedented lines in the given text.
*
* This is used to represent text concisely and readably, which is
* particularly useful for declarative definitions (e.g. in tests).
*
* (inspired by Python's `textwrap.dedent()`)
*/
export function getDedentedLines(text: string): string[] {
const linesep = text.includes('\r') ? '\r\n' : '\n';
const lines = text.split(linesep);
if (!lines) {
return [text];
}

if (lines[0] !== '') {
throw Error('expected actual first line to be blank');
}
lines.shift();

if (lines[0] === '') {
throw Error('expected "first" line to not be blank');
}
const leading = getIndent(lines[0]).length;

for (let i = 0; i < lines.length; i += 1) {
const line = lines[i];
if (getIndent(line).length < leading) {
throw Error(`line ${i} has less indent than the "first" line`);
}
lines[i] = line.substring(leading);
}

return lines;
}

/**
* Extract a tree based on the given text.
*
* The tree is derived from the indent level of each line. The caller
* is responsible for applying any meaning to the text of each node
* in the tree.
*
* Blank lines and comments (with a leading `#`) are ignored. Also,
* the full text is automatically dedented until at least one line
* has no indent (i.e. is treated as a root).
*
* @returns - the list of nodes in the tree (pairs of text & parent index)
* (note that the parent index of roots is `-1`)
*
* Example:
*
* parseTree(`
* # This comment and the following blank line are ignored.
*
* this is a root
* the first branch
* a sub-branch # This comment is ignored.
* this is the first leaf node!
* another leaf node...
* middle
*
* the second main branch
* # indents do not have to be consistent across the full text.
* # ...and the indent of comments is not relevant.
* node 1
* node 2
*
* the last leaf node!
*
* another root
* nothing to see here!
*
* # this comment is ignored
* `.trim())
*
* would produce the following:
*
* [
* ['this is a root', -1],
* ['the first branch', 0],
* ['a sub-branch', 1],
* ['this is the first leaf node!', 2],
* ['another leaf node...', 1],
* ['middle', 1],
* ['the second main branch', 0],
* ['node 1', 6],
* ['node 2', 6],
* ['the last leaf node!', 0],
* ['another root', -1],
* ['nothing to see here!', 10],
* ]
*/
export function parseTree(text: string): [string, number][] {
const parsed: [string, number][] = [];
const parents: [string, number][] = [];

const lines = getDedentedLines(text)
.map((l) => l.split(' #')[0].split(' //')[0].trimEnd())
.filter((l) => l.trim() !== '');
lines.forEach((line) => {
const indent = getIndent(line);
const entry = line.trim();

let parentIndex: number;
if (indent === '') {
parentIndex = -1;
parents.push([indent, parsed.length]);
} else if (parsed.length === 0) {
throw Error(`expected non-indented line, got ${line}`);
} else {
let parentIndent: string;
[parentIndent, parentIndex] = parents[parents.length - 1];
while (indent.length <= parentIndent.length) {
parents.pop();
[parentIndent, parentIndex] = parents[parents.length - 1];
}
if (parentIndent.length < indent.length) {
parents.push([indent, parsed.length]);
}
}
parsed.push([entry, parentIndex!]);
});

return parsed;
}
12 changes: 9 additions & 3 deletions src/client/extensionActivation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,15 @@ import { ITestCodeNavigatorCommandHandler, ITestExplorerCommandHandler } from '.
import { registerTypes as unitTestsRegisterTypes } from './testing/serviceRegistry';

// components
import * as pythonEnvironments from './pythonEnvironments';
// import * as pythonEnvironments from './pythonEnvironments';

import { ActivationResult, ExtensionState } from './components';
import { Components } from './extensionInit';

export async function activateComponents(
// `ext` is passed to any extra activation funcs.
ext: ExtensionState,
components: Components
_components: Components
): Promise<ActivationResult[]> {
// Note that each activation returns a promise that resolves
// when that activation completes. However, it might have started
Expand All @@ -89,7 +89,13 @@ export async function activateComponents(
// activation resolves `ActivationResult`, which can safely wrap
// the "inner" promise.
const promises: Promise<ActivationResult>[] = [
pythonEnvironments.activate(components.pythonEnvs),
// tslint:disable-next-line:no-suspicious-comment
// TODO: For now the extension should only interact with the component via the component adapter,
// which takes care of putting the component behind the experiment flag. It already activates the
// component among other things, hence the following is not needed.
// pythonEnvironments.activate(components.pythonEnvs),
// If we need to activate, we need to use the adapter:
// https://github.com/microsoft/vscode-python/issues/14984
// These will go away eventually.
activateLegacy(ext)
];
Expand Down
3 changes: 1 addition & 2 deletions src/client/pythonEnvironments/base/envsCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@ export class PythonEnvsCache {
if (this.byExecutable === undefined) {
this.byExecutable = {};
for (const env of this.envs) {
const key = getEnvExecutable(env.executable.filename);
this.byExecutable[key] = env;
this.byExecutable[env.executable.filename] = env;
}
}
return this.byExecutable[executable];
Expand Down
Loading