-
Notifications
You must be signed in to change notification settings - Fork 676
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
Fixes #3437 Typeshed integration #4264
Conversation
Enables value merging from typeshed Improves some handling of type annotations in AST imported files
@MikhailArkhipov In case you want to watch my work in progress on this. The basics are in there, just need to beef up the tests and the handling of the typing module when doing AST imports. |
/// This should be used in place of: | ||
/// <c>Path.GetDirectoryName(CommonUtils.TrimEndSeparator(path)) + Path.DirectorySeparatorChar</c> | ||
/// </remarks> | ||
public static string GetParent(string path) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can technically do Path.GetFullPath(Path.Combine(path, ".."))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's much slower and far more pedantic about paths (it will actually validate the paths...)
if (typeShedPaths != null) { | ||
return typeShedPaths; | ||
} | ||
lock (_searchPathsLock) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like duplicate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a double check after taking the lock, which we then release immediately to do the asynchronous collection. But I'll move the re-lock earlier so we are less likely to search for the paths multiple times.
Fixes PythonCompiledRegex Removes USE_TYPESHED variable
@MikhailArkhipov @huguesv This is ready for review |
@@ -0,0 +1,55 @@ | |||
using System; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing header
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh, this whole file is unused and not meant to be checked in yet... whoops
@@ -289,15 +289,15 @@ internal bool RemoveVariable(string name, out VariableDef value) | |||
} | |||
|
|||
internal virtual void ClearNodeValues() { | |||
_nodeValues.Clear(); | |||
_nodeValues = new AnalysisDictionary<Node, NodeValue>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
References to these dictionaries are publicly available through properties. Is it safe to change the instance instead of calling Clear()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. We only change them on the thread that accesses it, and we replace it specifically so that references obtained through the properties continue to point at the old dictionary.
DeclaringModule = original.DeclaringModule; | ||
DeclaringType = original.DeclaringType; | ||
Name = original.Name; | ||
_doc = (original as AstPythonFunction)?._doc ?? original.Documentation; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AstPythonFunction.Documentation
first checks for _doc
as well, no need to check for it separately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant to copy the null
from _doc
, but this doesn't do that either :) So a slightly different change coming
IsClassMethod = original.IsClassMethod; | ||
IsStatic = original.IsStatic; | ||
_overloads = original.Overloads.ToList(); | ||
Locations = (original as AstPythonFunction)?.Locations ?? (original as ILocatedMember)?.Locations ?? Array.Empty<LocationInfo>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AstPythonFunction
implements ILocatedMember
, so no need to check AstPythonFunction
by itself
|
||
private IReadOnlyList<string> GetTypeShedPaths(int timeout) { | ||
// First check | ||
var typeShedPaths = _typeShedPaths; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_typeShedPaths
is accessed outside of the lock
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Deliberately, so we can avoid locking for the search if the search has already been done (it'll only ever be done once - we never change _typeShedPaths
after initialization - but it depends on context too much to make it Lazy<T>
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since _searchPathsLock
is used only for locking, it will be a thin lock and the overhead of using it is close to nothing:
https://blogs.msdn.microsoft.com/seteplia/2017/09/06/managed-object-internals-part-2-object-header-layout-and-the-cost-of-locking/
(removed second article cause it is using custom testing technique instead of reliable benchmark)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not using it has no overhead, and it's only for initialization.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's also read outside the async
to avoid any overhead there.
PythonPaths.Versions.LastOrDefault(v => Directory.Exists(Path.Combine(v.PrefixPath, "Lib", "site-packages", "typeshed"))); | ||
|
||
[TestMethod, Priority(0)] | ||
public void TypeShedElementTree() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hacked this test to run under a version of python that doesn't have typeshed installed, and it still passes. I'm not sure what to make of that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:( It means our analysis is so good that typeshed doesn't actually help here. Which version did you run it on? The one I tried seemed to need it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
3.6
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, an easy way to disable type shed is to go into AstPythonInterpreter, search for IncludeTypeShed
and set it to false (which I'm currently turning into one of our secret registry key settings)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the latest code, you can disable it by setting: HKEY_CURRENT_USER\Software\Microsoft\PythonTools\15.0\Analysis\Project@UseTypeStubPackages
to 0
(defaults to 1
). You can also set UseTypeStubPackagesExclusively
to 1
to not merge with the original results.
return mmy; | ||
} else if (mmx != null && mmy != null) { | ||
mmx.AddModules(mmy._modules); | ||
return mmx; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it matter anywhere that we return mmx
instead of mmy
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. This should only be being used while we're constructing objects and before the analyzer ever gets a hold of it (great question though)
@@ -39,6 +39,13 @@ class AstPythonMultipleMembers : IPythonMultipleMembers, ILocatedMember { | |||
_checkForLazy = true; | |||
} | |||
|
|||
public IMember Trim() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if _checkForLazy is true? Will the caller know/expect to see a ILazyMember? If not you can return Members[0]
which will do the lazy get.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, calling Members
might resize _members
potentially going from size 1 to 0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point though, should really check that here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, decided not to change it. If we trim down to a single lazy member, we should probably leave it lazy even if it will eventually (depending on context) resolve down to nothing.
About to push a fix for the module members issue, and it seems we're currently not resolving parameter annotations into displayable types, so there's a bit more work to do there. The default values of |
Fixes module members not combining shared names. Fixes crash when parsing tuple assignment.
// Assume caller has already done the quick checks | ||
|
||
IReadOnlyList<string> typeShedPaths; | ||
var typeshed = await FindModuleInSearchPathAsync("typeshed"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it ok that FindModuleInSearchPathAsync
can be called several times concurrently before _typeShedPaths
is finally assigned?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep. We could call it every time if we wanted, it doesn't change any state. But since its results (rarely) change, better to cache it.
Still need to fix that test before merging, but that's all I'm doing |
} | ||
|
||
public IList<IPythonType> ReturnTypes => _returnTypes; | ||
public IList<IPythonType> ReturnTypes => _overload.ReturnTypes; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
according to the constructor, _overload
can be null
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's used in a few other places without checks, so I suppose ctor should throw if null.
Just that one comment. |
Fixes mishandling of linked variables that was breaking tests
Fixes #3437 Typeshed integration
Enables value merging from typeshed
Improves some handling of type annotations in AST imported files