-
-
Notifications
You must be signed in to change notification settings - Fork 508
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
Missing Doc Strings for Classes and Modules #340
Comments
definitely a bug, it seems to work in some cases, though? |
Sometimes - e.g. with xmlrpclib some of the classes in that module have jedi docs. I have yet to see a module have docs. |
The obvious thing I was missing it that it is straightforward enough to use follow_definition to retrieve the doc from a class or module (see code below). At least for a module, that makes some sense because the completion for say "jed" refers to to an import statement ("import jedi") which then refers to the module ("jedi"). I guess this has the benefit of jedi not needing to actually load any modules until the user tries to retrieve module members (e.g. "jedi."). But it is slightly problematic for showing doc strings in an IDE's completion popup because it could take a long time to load the module just to get a doc. In the case of a class, it looks like jedi tries to return the docstring for the init statement, which is an empty string if there isn't one. Maybe classes and functions should have import jedi
s = jedi.Script('import jedi\njed',2,3)
c = s.completions()[0]
print c.type
print c.follow_definition()[0].raw_doc
print jedi.Script.__doc__ == ''
s = jedi.Script('import jedi\njedi.Scr',2,8)
c = s.completions()[0]
print c.type
print c.follow_definition()[0].raw_doc
print jedi.Script.__doc__ == ''
s = jedi.Script('import xmlrpclib\nxmlrpclib.Marshall',2,18)
c = s.completions()[0]
print c
print c.type
print c.doc
print c.follow_definition()[0].raw_doc
print jedi.Script.__doc__ == '' |
One thing I find confusing: s = jedi.Script('import jedi\njedi.Scr',2,8)
c = s.completions()[0]
print c.type, c
s = jedi.Script('import xmlrpclib\nxmlrpclib.Marshall',2,18)
c = s.completions()[0]
print c.type, c outputs
Why a |
Well - that looks like another bug. I plan to add a new api command called "documentation" anyway. This call would return a documentation object with different docstrings that describe the class - We could then also integrate that into the One of the problems is that we even have an "import" type. But I'm just not sure if we can even get rid of it. If we cannot resolve the import we will still have to write something and that's obviously Therefore I would be very careful to just call |
Agree, and if someone can get |
This doesn't actually happen. In the above case |
I am using |
Hmmm. Thank you for the feedback. I also doubt that it's a problem, normally. It might lead to problems if you're using a lot of imports, like |
I've just tested that (following all completion objects for
|
Hmm could you post the exact call to Jedi? Because |
I am following the definition of every import completion |
Here is the code: import jedi
script = jedi.Script("import ")
for c in script.completions():
type = c.type
# follow import to get the real type
if c.type == 'import':
try:
definition = c.follow_definition()[0]
type = definition.type
except IndexError:
# no definition
pass
print(type) # I use `type` to create the associated icon in pyqode which outputs:
|
Arghh... This is painful. Just calling e.g. displaying So we just follow import types... Well... This also kind of sucks, since an import can be a statement again, which makes our API behave inconsistently. However, this happens rarely (most of the time explicit imports are really classes or functions), we should really care about the most typical use cases for Python. It's not like we will ever have perfect autocompletion anyway. The big problem is that in your above example (though buggy ATM, but let's suppose we've already fixed it) Jedi would import every single f***ing module in the Uffff... If only there was a consistent solution?! Maybe we should only follow imports if we're not doing autocompletion on imports? Or maybe we should just follow second level imports? Or a combination of those two? Any ideas @tkf @dbrgn @asmeurer @ColinDuquesnoy? I just know that this issue is really blocking me right now, as I'm trying to refactor the API to use |
This is not required for just listing modules, right? You can just search through |
The only place where we should follow definition of imports is in the form |
@ColinDuquesnoy Well following it is not just important for the |
But you don't need to parse the entire module just for the docstring, right? |
Well but we need to at least open the module, parse a part and so on. If you happen to have a few hundred modules installed, that's not good. It's even worse if some of those modules have been compiled - you would import a lot of modules. Plus at the moment the procedure for getting a docstring is by parsing the whole file, because we probably need it anyway. |
If I understand correctly |
The issue is that Jedi sometimes reports imported classes and functions as imports. At the moment the only way to find out if an import is a class, a function or a module/package is to use Maybe we should forget about this solution and fix it internally so that user will have the correct completion type directly from calling That being said, I don't know enough of the Jedi internals so I might be completely off. |
|
Well I think that's their problem ;) We have nice "lazy" attribute
Really? I've heard VIM got some async libs too. At the bottom line, you can combine Jedi with multiprocessing to do the job in background and wait for some time to finish. If it is not done until timeout, you just ignore it. This way, you can use Jedi without blocking even if editor does not have async API. I really don't think it is a good idea to "take care" of IDE developers problem at Jedi side. If you have to do some hard computation (or IO access) to get some value, let's just do it. User may request it and OK to wait for a while. In that case, truncating the search does not help. But in the end, I think you are the one to program all of this if you want, so I don't want to say too much. I just want to note this point, since nobody mentioning it. |
BTW, it is nice if something like |
Good points.
I plan to add some sort of an asynchronous interface to do things like that, but I guess to do that for all methods would be kind of stupid. (Plus I'm really sad that I'm using properties all over, it would have been easy to just say
Haha, that's not exactly true - you're alive and well again, now :-) And BTW: VIMs async support is horrible. In general VIM script is the spawn of satan. :-) |
I don't think adding timeout option makes it async. Async interface would be something like
You got me... well, not in this case though. I don't think we should add complicated way to truncate evaluation in Jedi (except for the explicit timeout stuff) so I don't want to help in this very case ;-) |
A few thoughts:
|
@spillz I think caching top-level modules and its docstring is a good idea! |
@ColinDuquesnoy I fixed the problem you had, above. Used way more time than I thought, but it should be working now. |
Ok thanks, I will give it a try this weekend and will let you know how it worked. |
@davidhalter I confirm the problem is fixed. Now the results are as you expected, it takes more than 25s to follow every import (when the cache has been cleaned) and the memory used by Jedi rise up to 200MiB on my system. However following definition of imports when the user typed at least one letter of the module/package is pretty fast (which will happen 99% of the time), e.g. this code will work fast and will collect the correct type for any completion but I feel like I am hacking jedi to get the results I want :/ import jedi
code = "import "
line = 1
script = jedi.Script(code, line)
completions = script.completions()
current_line = code.splitlines()[line-1].strip()
# HERE IS THE IMPORTANT PART
follow_definitions = current_line != 'import' and current_line != 'from'
for c in completions:
type = c.type
# follow import to get the real type
if c.type == 'import' and follow_definitions:
try:
definition = c.follow_definition()[0]
type = definition.type
except IndexError:
# no definition
pass
print(type, c.name) # I use `type` to create the associated icon in pyqode |
I finally changed the behavior of For
Having changed to this behavior, we would just be left with first class Does this make sense? The idea is to really create an easy and understandable API. |
+1 but maybe it's a good idea to discuss API in a dedicated issue. |
That looks good to me. I really like the
So you would just mark |
@tkf I will probably still start a detailed discussion about
@ColinDuquesnoy They will disappear, but we haven't yet removed anything. We still have to come up with a good deprecation process. Currently they are just deprecated. |
@davidhalter Well I don't mind how you organize issue list. Just suggesting. If adding new API does not increase functionality at this point, maybe it is better to stick with what we have now. We may have another set of functionality which does not fit with Another possible API is to add optional arguments to
Brainstorming other possible functionality to consider: fetching and mixing documentation from parent classes, "additional strings" (PEP 257), fetching parameter document and type from |
True. Thank you for your thoughts. |
I think I'm going directly for the My only remaining question is: How should we call the documentation method of
|
…are in a from clause or if its a longer imnport
You can do timeouts in Python using signal handlers http://docs.python.org/2/library/signal.html#signal.setitimer. The bad news is that it doesn't work on Windows. Also, I think it might not work against an atomic (i.e., written in C) operation (I could be wrong about that). That's usually not an issue unless you are using built in Python functions against some large data. The other gotcha with it is that you have to use one of the standard signals. You can't just invent your own. So e.g., if you set up a handler for sigint, nothing else can use that now. I think Jedi typically runs in its own process so this might not be an issue. That being said, I've used this in a few applications before, and it seems to work pretty well. I've never used it with subprocesses or async, though. It may actually be easier to have a timeout in that case, as you can work around the atomic operation issue. OTOH, you could leave it to the plugin authors, as in most cases the editors themselves should have better timeout mechanisms, I'd imagine, since they were designed with interactivity in mind (and Python wasn't so much). |
@asmeurer I didn't know that. That's good to know. I think you are right about letting plugin handle timeout. @davidhalter Isn't |
I'm going to start a separate discussion, soon. But to address your concerns right now: |
If we are sure that [1] BTW, I missed this point. Using |
I think |
I'm closing this one. |
Won't |
Oh I guess not. It works if it's a subclass of str, but raises TypeError if it isn't. |
Using v0.7.0
Maybe I'm missing something obvious, but
returns
Is there some way to enable these doc strings?
I want to be able to display doc popups next to completion popup items as per the screenshot below (which currently only works with functions because of this limitation):
The text was updated successfully, but these errors were encountered: