Skip to content

Added Windows store file system watcher #14577

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

Merged
merged 12 commits into from
Nov 3, 2020

Conversation

karrtikr
Copy link

No description provided.

@karrtikr karrtikr added the no-changelog No news entry required label Oct 29, 2020
@codecov-io
Copy link

codecov-io commented Oct 29, 2020

Codecov Report

Merging #14577 into main will decrease coverage by 0.02%.
The diff coverage is 59.09%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main   #14577      +/-   ##
==========================================
- Coverage   64.91%   64.88%   -0.03%     
==========================================
  Files         541      541              
  Lines       25131    25156      +25     
  Branches     3544     3545       +1     
==========================================
+ Hits        16313    16323      +10     
- Misses       8155     8171      +16     
+ Partials      663      662       -1     
Impacted Files Coverage Δ
...discovery/locators/services/windowsStoreLocator.ts 86.79% <58.82%> (-13.21%) ⬇️
...pythonEnvironments/common/pythonBinariesWatcher.ts 61.53% <60.00%> (+1.53%) ⬆️
src/client/common/utils/platform.ts 68.00% <0.00%> (-4.00%) ⬇️
...rc/client/common/process/internal/scripts/index.ts 65.85% <0.00%> (-3.89%) ⬇️
src/client/common/experiments/groups.ts 100.00% <0.00%> (ø)
...c/client/application/misc/joinMailingListPrompt.ts 97.56% <0.00%> (ø)
...ocators/services/globalVirtualEnvronmentLocator.ts 96.42% <0.00%> (+0.04%) ⬆️
src/client/terminals/codeExecution/helper.ts 14.58% <0.00%> (+1.53%) ⬆️
.../pythonEnvironments/common/externalDependencies.ts 67.56% <0.00%> (+2.70%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update ed0dad5...a1dcd0a. Read the comment docs.

* python2.7.exe
* python3.exe
* python38.exe
* Note the below is constructed in a way that it can be used as a glob pattern, or as a regex.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...

Copy link

@ericsnowcurrently ericsnowcurrently left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The major comments:

  • why are we using globs instead of regexes?
  • do not start the watcher in the constructor

@karrtikr
Copy link
Author

karrtikr commented Nov 3, 2020

@ericsnowcurrently The glob module that we use is built on top of minimatch, so that one is a dependency freebie. We also use that in the extension. Hence directly used that in favor of picomatch to avoid the disclaimer comments, converting regex to glob and then vice versa without any additional cost. Will add the disposable and other base class changes in a separate PR.

Copy link

@ericsnowcurrently ericsnowcurrently left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this is looking good. Thanks for the fixes. I've left a few minor comments in the non-test files. Otherwise all the rest of the comments are in the test files.

): void {
const patterns = [executableGlob, `*/${executableGlob}`, `*/${binName}/${executableGlob}`];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be helpful to callers to throw an error here if executableGlob has path.separator in it. I still think a similar check for wildcards (*) would be likewise helpful.

Copy link
Author

@karrtikr karrtikr Nov 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with your initial suggestion to expand the glob pattern to support more than base patterns. #14577 (comment)

So we don't have to put a restriction on the wildcards or path separators. I can rename it to executableSuffixGlob if that would help.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with your initial suggestion to expand the glob pattern to support more than base patterns.

I don't see that. What I see is:

const pythonExeGlob = 'python3\.[0-9]*\.exe';

A more-than-base pattern would be:

const pythonExeGlob = '**/python3\.[0-9]*\.exe';

or:

const pythonExeGlob = path.join('**', 'python3\.[0-9]*\.exe');

Copy link
Author

@karrtikr karrtikr Nov 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm talking about the API, not this particular usecase. The API supports more than base patterns, i.e user can pass in a path.join('**', 'python3\.[0-9]*\.exe') and things should work. Just needed to do this here.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

path.join('**', executablesGlob) is exactly what I would expect this function to do for the caller.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're assuming executablesGlob to be the base executable glob, it's not. It refers to the suffix of the full executable path we intend to watch. For eg. it can be path.join('**', 'python3\.[0-9]*\.exe') itself, so the function need not do anything for the caller.

Hence why I suggested to rename it to executablesSuffixGlob.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The patterns on this line suggest otherwise:

  • executableGlob - look for the file in the current directory
  • */${executableGlob} - look for the file one level down
  • */${binName}/${executableGlob} - look for the file in the "bin" directory one level down

What you seen to be saying is that a caller may want to search at some arbitrary depth relative to a given root:

  • **/python3.exe
  • */**/python3.exe
  • */${binName}/**/python3.exe

However, those don't seem right to me. Perhaps you meant a user might want to do something like the following?

  • **/python3.exe
  • **/*/python3.exe
  • **/*/${binName}/python3.exe

The problem is that kind of defeats the structure-oriented design of this function. So I don't see that to be right either.

All this is why I would expect executableGlob to always be a base name.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What you seen to be saying is that a caller may want to search at some arbitrary depth relative to a given root:

  • **/python3.exe
  • */**/python3.exe
  • */${binName}/**/python3.exe
    However, those don't seem right to me.

I realize it doesn't seem right. But if you look closely, the first pattern is enough to do what the user asks (watch for suffixes ending with python3.exe). And the second and third pattern are just subsets of the first. So yes, they do what the user intended to.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is only true if the glob is for the basename. If they put **/... then it won't be what they expect. If it shouldn't matter then this function should not be auto-watching */${executableGlob} and */${binName}/${executableGlob}. The two things just don't make sense together.

Copy link

@ericsnowcurrently ericsnowcurrently left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only have a few more minor comments. So...

LGTM once all the open comments have been addressed.

Comment on lines +110 to 112
export function isWindowsStorePythonExe(interpreterPath: string): boolean {
return minimatch(path.basename(interpreterPath), pythonExeGlob, { nocase: true });
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why aren't we just using isWindowsPythonExe()?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of the reason mentioned in the sprint demo today: We're matching only python3.*.exes so that we do collect only unique executables. isWindowsPythonExe() also matches with python.exe which is not what we want.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc comment explains this.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then I'd expect the following to take care of it:

Suggested change
export function isWindowsStorePythonExe(interpreterPath: string): boolean {
return minimatch(path.basename(interpreterPath), pythonExeGlob, { nocase: true });
}
export function isWindowsStorePythonExe(interpreterPath: string): boolean {
if (!isWindowsPythonExe(interpreterPath)) {
return false;
}
return path.basename(interpreterPath) !== 'python.exe';
}

That way we don't accept executables we shouldn't, like "python3-whoa!!!.exe".

Copy link
Author

@karrtikr karrtikr Nov 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, there's also python3.exe, and python2.exe that we would need to ignore. I can either do this or correct the glob pattern to not accept the whoaaa string. I'll have to correct the glob either way so we don't watch for such executables.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, so we're okay with "extended glob". What you have looks fine, but I'd change the * to ?.

const windowsAppsRoot = getWindowsStoreAppsRoot();
watchLocationForPythonBinaries(windowsAppsRoot, (type: FileChangeType) => {
this.emitter.fire({ type, kind: this.kind });
}, pythonExeGlob);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this is really the only place that we need pythonExeGlob. So it may make sense to move it inline here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need it in isWindowsStorePythonExe as well.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use isWindowsPythonExe() there then only need it here.

Copy link
Author

@karrtikr karrtikr Nov 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But then we would have two places where we capture what a windows store executable is? The glob pattern itself and isWindowsStorePythonExe.

If we only use the glob, that way we have the logic in one central place.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Nov 3, 2020

Kudos, SonarCloud Quality Gate passed!

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities (and Security Hotspot 0 Security Hotspots to review)
Code Smell A 0 Code Smells

No Coverage information No Coverage information
0.0% 0.0% Duplication

@karrtikr
Copy link
Author

karrtikr commented Nov 3, 2020

@ericsnowcurrently I believe I have addressed all the comments. I generally like to keep comments open for the reviewers to resolve them themselves. Hope I addressed most of your concerns.

@karrtikr karrtikr merged commit f752706 into microsoft:main Nov 3, 2020
@karrtikr karrtikr deleted the windowsappswatcher branch November 3, 2020 23:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
no-changelog No news entry required
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants