1
0
forked from VimPlug/jedi

Compare commits

...

1225 Commits

Author SHA1 Message Date
David Halter
78f1ae5e71 change strange usage of resetting positions in Script 2013-08-18 19:30:11 +04:30
David Halter
ef2d99358f move Script.pos to Script._pos 2013-08-18 19:28:34 +04:30
David Halter
5cec226d7e remove unused Statement.get_command function 2013-08-18 00:07:46 +04:30
David Halter
6d9d07f873 remove useless is_empty function 2013-08-18 00:06:54 +04:30
David Halter
a6b6b6cbe8 parsing representation get_code tests 2013-08-18 00:06:08 +04:30
David Halter
f0b25d5451 simple debug test, if they work 2013-08-17 23:40:00 +04:30
David Halter
230bf724a2 empty asserts should not lead to broken parsers 2013-08-17 23:04:01 +04:30
David Halter
af7089b8ca use cwd_at to avoid test failures if called from different places 2013-08-17 23:03:32 +04:30
David Halter
a1e232bcbf fix local imports in repl completion 2013-08-17 22:43:20 +04:30
David Halter
a8e259763e fix problem with variables in from clauses, #268 2013-08-17 22:04:55 +04:30
David Halter
a1d61e4855 after 'from namespace ', only import completion is allowed, as well as a lot of related fixes to context/user_stmt stuff, fixes #291 2013-08-17 21:28:53 +04:30
David Halter
878f88b4b9 don't repeat all the things written in the readline all the time 2013-08-17 19:48:32 +04:30
David Halter
2097eca646 hit tab once in readline instead of twice 2013-08-17 19:25:54 +04:30
David Halter
1ce4babe36 fix a problem in readline completion with upper/lower cases 2013-08-17 19:19:47 +04:30
David Halter
e46d33be90 move completion to interpreter, which improves shell completion 2013-08-17 10:49:23 +04:30
David Halter
2612963f58 fix many position problems 2013-08-17 00:34:20 +04:30
David Halter
8beeb71f84 parsing import names position tests 2013-08-16 22:51:35 +04:30
David Halter
7485802149 tests for class/func name positions 2013-08-16 22:38:56 +04:30
David Halter
d4f605d213 fix name end_pos problems 2013-08-16 22:31:51 +04:30
David Halter
098397aa79 tests for module name/position 2013-08-16 22:25:35 +04:30
David Halter
51468b1e09 module name should have 0, 0 start/end_pos 2013-08-16 22:21:52 +04:30
David Halter
d4ff98aee2 Call should always have an end_pos 2013-08-16 22:15:02 +04:30
David Halter
3d698ffad6 call type tests 2013-08-16 22:00:27 +04:30
David Halter
cb21de45f1 add parsing tests for name/call positions 2013-08-16 21:34:58 +04:30
David Halter
0268109d1d fast parser test improvements 2013-08-16 21:33:43 +04:30
David Halter
b32065052a test that proves fast_parser problems in certain cases 2013-08-16 16:03:56 +04:30
David Halter
f12e7f66c4 remove unused argument from calls to set_debug_function 2013-08-16 14:06:53 +04:30
David Halter
8996bd6ad1 modifications to use a differnt kind of completion for the interpreter 2013-08-16 13:38:50 +04:30
David Halter
ae6e997098 do some sort of error handling in fast_parser, because if fast_parser cracks down, cache might be corrupted 2013-08-16 11:12:35 +04:30
David Halter
f76ec55786 fix problems with empty decorators - just '@' and nothing else 2013-08-16 11:04:06 +04:30
David Halter
e63783f122 test for empty decorators 2013-08-16 11:02:22 +04:30
David Halter
cfe21c74e8 tab completion instead of jedi autocompletion in docs 2013-08-16 10:55:12 +04:30
David Halter
873c609fca forgot to write a test for #293 2013-08-16 10:05:25 +04:30
David Halter
4b0465a2d1 REPL completion deletes the line sometimes, fixes #293 2013-08-16 10:00:52 +04:30
David Halter
8b5e130e55 fix a problem with setup_readline, using __dict__ instead of a simple dir(), #280 2013-08-16 01:38:58 +04:30
David Halter
fbcecaf1ef deprecate 'source_path' as a Script parameter in favour of 'path' 2013-08-15 19:25:19 +04:30
David Halter
98fc11ecfd change a lot of ImportPath functions to protected methods 2013-08-15 15:28:48 +04:30
David Halter
19f904f999 fix a problem with testing setup_readline, when running all tests, this should also mean that #280 is now really finished 2013-08-15 15:14:59 +04:30
David Halter
68d595fe70 imports without path did have problems, because of an incorrect sys.path 2013-08-15 14:54:07 +04:30
David Halter
17d7870b3b first import tests for repl completion, #280 2013-08-15 14:25:28 +04:30
David Halter
6718020fac failing import tests for #280 2013-08-15 14:10:41 +04:30
David Halter
c4e07cae11 rename name_with_signs to name_with_symbols 2013-08-15 14:04:08 +04:30
David Halter
d6e9732064 added a method 'api_classes.Completion.name_with_signs', because this is important for #280 2013-08-15 13:56:43 +04:30
David Halter
5c0dec6106 test of repl autocompletion, #280 2013-08-15 13:36:12 +04:30
David Halter
9a3ea38b1c use builtin repl completion after all, but written by us not the std lib module, which doesn't seem to work really, #280 2013-08-15 12:47:10 +04:30
David Halter
306dbf12e4 goto now also returns results for loops without input, fixes #283 2013-08-15 11:34:46 +04:30
David Halter
4a6f421b80 test for goto in loops that use e.g. empty arrays as input, #283 2013-08-15 11:33:32 +04:30
David Halter
0db6be0e99 add namespace packages to changelog 2013-08-14 08:24:50 +04:30
David Halter
d817a36e80 fix a syntax problem of python2.6 (in tests) 2013-08-14 01:10:35 +04:30
David Halter
ffa6daa3d1 add namespaces to feature list 2013-08-14 01:04:43 +04:30
David Halter
e0b781987a fix two failed tests due to previous changes 2013-08-14 00:59:33 +04:30
David Halter
77181e3f4e more namespace package tests, #122 2013-08-14 00:56:22 +04:30
David Halter
ed36486966 fixed a bug in api_classes comparison 2013-08-14 00:52:34 +04:30
David Halter
34f05cdad5 minor bug/testing fixes for #122 2013-08-14 00:26:06 +04:30
David Halter
f5f1fcb8c3 also enable autocompletion on namespace packages, fixes #122 2013-08-14 00:17:40 +04:30
David Halter
2e0863e76a namespace packages first part, fix goto problems 2013-08-13 23:55:22 +04:30
David Halter
4e087f6c09 namespace package tests, #122 2013-08-13 23:04:34 +04:30
David Halter
19dec3d0ed fix api_classes definition description on flows 2013-08-13 22:38:02 +04:30
David Halter
51428fa38e first functioning for loop tests, see #283 2013-08-13 22:36:54 +04:30
David Halter
cacc94acf3 clean up some docstrings in api_classes and put them in the right order 2013-08-13 22:23:24 +04:30
David Halter
93e993549a don't skip doctests in api_classes 2013-08-13 22:19:30 +04:30
David Halter
a895561f1a fixed a problem that combined __getattr__ and usages 2013-08-13 15:39:26 +04:30
David Halter
2359ccbeb1 rename completion/renaming.py to usages.py 2013-08-13 15:13:18 +04:30
David Halter
faf7e0c422 fix a problem found by sith with dynamic params (includes tests) 2013-08-12 02:23:44 +04:30
David Halter
27854a3948 rewriting dynamic search_param stuff, should be faster now (fewer executions) and i hope that this fixes #220 2013-08-12 01:54:19 +04:30
David Halter
e07625017d remove all the deprecation warnings in jedi itself 2013-08-11 23:00:27 +04:30
David Halter
0ab4119447 previous fix had still bugs, now finally fixes #221 2013-08-11 22:28:41 +04:30
David Halter
e53c6d10d6 deprecate start_pos as discussed in #221 2013-08-11 21:24:48 +04:30
David Halter
dee105119b tests for #221 2013-08-11 21:20:54 +04:30
David Halter
ba9ba7c1fe line and column return None in case of builtin module, fixes #221 2013-08-11 21:15:48 +04:30
David Halter
cf4325cef1 fix an output proplem with debugging via stdout (encoding in python 2) 2013-08-11 21:05:29 +04:30
David Halter
6179389df8 make it easier to debug bugs like #220, in dynamic param searching 2013-08-11 20:56:01 +04:30
David Halter
f93226420e fix a few debugging problems 2013-08-11 20:10:52 +04:30
David Halter
a72601a9d8 use docopt for run.py executions, much more readable 2013-08-11 19:53:14 +04:30
David Halter
604fe5e3f7 minor refactoring, use finally instead of complicated except 2013-08-11 18:15:24 +04:30
David Halter
1121588678 use ImportPath.is_relative_import instead of complicated lookups all the time 2013-08-11 18:14:58 +04:30
David Halter
81e625862e os.path is now accessible, but nested imports in general are not. fixes #213, #230 2013-08-11 18:10:21 +04:30
David Halter
710dac797b fix a nested import problem with not found imports 2013-08-11 17:59:36 +04:30
David Halter
0fef34c26e finding out that fake imports arent't allowed in python and os.path is a sys.modules modification, fix tests #213 2013-08-11 17:30:50 +04:30
David Halter
9d1e527b7c merge issue-213-from-import-definition of @tkf into dev 2013-08-11 01:20:45 +04:30
David Halter
86394a7ed5 fix problems with imports as user_statement and brackets, fixes #285 2013-08-11 01:14:13 +04:30
David Halter
ec347dd975 user_statement problems with imports and brackets, test for #285 2013-08-11 01:12:08 +04:30
David Halter
66fa0b0575 move current to _current in parsing 2013-08-11 01:03:20 +04:30
David Halter
649135ea7b goto_assignments more readable 2013-08-11 00:06:04 +04:30
David Halter
832f05b4a1 Merge branch 'dev' of github.com:davidhalter/jedi into dev 2013-08-10 22:58:08 +04:30
David Halter
959519560d init decorators should not execute __init__ in case of self variable lookups, fixes #247 2013-08-10 22:55:58 +04:30
David Halter
6421c95df1 tests for init decorator problem, see #247 2013-08-10 22:53:26 +04:30
David Halter
97edfb13f7 some method decorator tests were wrong (incomplete) 2013-08-10 22:26:24 +04:30
David Halter
ff4f396957 more documentation on Instance 2013-08-10 22:18:21 +04:30
David Halter
ee1df18694 instance readability improvements 2013-08-10 22:11:31 +04:30
David Halter
c093e50537 Merge pull request #289 from asmeurer/pythonrc
Some fixes to the .pythonrc.py code
2013-08-10 10:37:32 -07:00
Aaron Meurer
5e81fc22e1 Skip doctests that are just completion examples 2013-08-10 11:20:31 -06:00
David Halter
ce8a8cfa3a Merge branch 'dev' of github.com:davidhalter/jedi into dev 2013-08-10 21:42:03 +04:30
David Halter
085bddd6a1 change Execution usage to always use the decorator function 2013-08-10 21:40:36 +04:30
David Halter
72313e2774 Merge pull request #290 from asmeurer/sith_file
Make sith.py random work with files
2013-08-10 10:08:00 -07:00
Aaron Meurer
b6b510693b Make sith.py random work with files 2013-08-10 10:39:19 -06:00
Aaron Meurer
754835bec5 Add some print statements to the .pythonrc.py example 2013-08-10 10:34:30 -06:00
Aaron Meurer
1ac6d779a1 Cleanup to the Jedi .pythonrc.py example 2013-08-10 10:28:09 -06:00
Aaron Meurer
8b3a62a76f Indent code blocks in the docstring 2013-08-10 00:50:26 -06:00
Aaron Meurer
7c53988bae Note that you have to add PYTHONSTARTUP to your profile 2013-08-10 00:44:30 -06:00
Aaron Meurer
6f9da26593 Show how to fallback to regular readline completion 2013-08-10 00:43:37 -06:00
Aaron Meurer
c60fd21805 Don't make it sound like Jedi has to be installed with pip 2013-08-10 00:42:34 -06:00
Aaron Meurer
396e19c2fd Don't format the code to go in .pythonrc.py in a doctest
This is way too confusing. You don't put doctests in files, you put code in
files.
2013-08-10 00:42:00 -06:00
David Halter
c35da04820 illegal decorators tests 2013-08-09 18:12:51 +04:30
David Halter
dce6f4a232 fix unicode problems in sith.py 2013-08-09 17:15:57 +04:30
David Halter
f2ffa037da Merge pull request #287 from davidhalter/sith_check_isfile
Sith check isfile
2013-08-09 05:43:24 -07:00
David Halter
9c9a90707e Merge pull request #284 from davidhalter/sith_recording_bug
Sith recording bug
2013-08-09 05:41:27 -07:00
Danilo Bargen
7806119911 Verify that path in "random" operation is a directory 2013-08-09 13:32:43 +02:00
Danilo Bargen
1762cb2ef8 Added record.json to .gitignore 2013-08-09 13:28:01 +02:00
Danilo Bargen
2d31f33038 Move call_args definition to recording 2013-08-09 13:26:11 +02:00
Danilo Bargen
a8bdee0051 Fixed bug in sith.py related to recording / serialization 2013-08-09 11:34:19 +02:00
Danilo Bargen
b791817c66 Sith PEP8 2013-08-09 11:29:24 +02:00
David Halter
2dd9ad7864 fix goto_definition on lambda problem, fixes #274 2013-08-09 13:51:45 +04:30
David Halter
22b11e0706 ignore decorators if cannot be found (also get rid of DecoratorNotFound exception), fixes #272 2013-08-09 13:01:21 +04:30
David Halter
a5fa0708ee tests for #272, not existing method decorators in combination with super on __init__ 2013-08-09 12:36:53 +04:30
David Halter
59f0b523bd move descriptors out of classes into their own file 2013-08-09 11:38:30 +04:30
David Halter
7548ec7280 fix problems with class method decorators 2013-08-09 11:32:44 +04:30
David Halter
c222cc5a32 method decorator as class tests 2013-08-09 10:58:24 +04:30
David Halter
5cf39565ee fix decorator problems in class methods (at least partially, there are problems with @staticmethod and @classmethod) 2013-08-09 02:11:12 +04:30
David Halter
a6b1a247c2 method decorator tests 2013-08-08 15:14:10 +04:30
David Halter
1fb1244b68 remove unlogical operation catching 2013-08-08 12:36:26 +04:30
David Halter
7f53bd71fe fix encoding problems in python2 for travis 2013-08-08 00:26:47 +04:30
David Halter
7ab00242a8 finally change name of test.base module to helpers.py, fixes #181 2013-08-07 18:35:47 +04:30
David Halter
45432c6cb0 documentation and clean up, #181 2013-08-07 18:27:25 +04:30
David Halter
04e454269c add test_integration_keyword.py to test keywords (moved out of test_regression), #181 2013-08-07 18:12:37 +04:30
David Halter
ee3f8d04f1 remove test.base.TestBase finally, #181 2013-08-07 18:06:52 +04:30
David Halter
e114c662f9 use jedi.Script.method instead of stupid self calls in tests 2013-08-07 18:00:45 +04:30
David Halter
74edb9e08e test_regression should not be executable 2013-08-07 17:49:52 +04:30
David Halter
297ad635b6 moved more methods out of regression tests, created test_unicode.py, #181 2013-08-07 17:48:57 +04:30
David Halter
4e75cda7f6 moved more methods out of regression tests, created integration_import and jedi_system, #181 2013-08-07 17:41:22 +04:30
David Halter
5b4ee16317 move some docstring tests, #181 2013-08-07 17:37:14 +04:30
David Halter
686e29c2f2 move cache test to the right file, #181 2013-08-07 17:25:47 +04:30
David Halter
0b3a623c8d add a separate jedi system testing file, which helps e.g. if imports are correctly used, #181 2013-08-07 17:20:02 +04:30
David Halter
a7584cfa04 add a separate api testing file, #181 2013-08-07 17:15:36 +04:30
David Halter
5c39b4596c add a separate docstring testing file, #181 2013-08-07 17:10:41 +04:30
David Halter
31e0b89791 add a separate speed testing file, #181 2013-08-07 17:06:24 +04:30
David Halter
6ed33d7c8d remove interpreter test from test_regression 2013-08-07 17:00:46 +04:30
David Halter
ed53ce65ed removed old unused code 2013-08-07 16:42:50 +04:30
David Halter
8c5cf0ace6 further call_signatures test refactoring 2013-08-07 16:40:37 +04:30
David Halter
085a076764 finished with a huge refactoring of call_signatures testing 2013-08-07 16:33:51 +04:30
David Halter
d1383965aa refactor call signature tests, use own file (incomplete) 2013-08-07 14:54:42 +04:30
David Halter
2ea2a8c6cd filter all non executables when used with call_signatures, fixes #240 2013-08-07 14:36:35 +04:30
David Halter
a79148925d another improvement to test/base.py 2013-08-07 14:16:50 +04:30
David Halter
03c75babdd Allow access to all the Jedi methods with a simplified interface in regression tests 2013-08-07 12:54:43 +04:30
David Halter
c6e08221ce fixed more import problems (also with the import usage), definitely fixes davidhalter/jedi-vim#152 2013-08-07 10:24:28 +04:30
David Halter
f72816a702 fix builtin module imports, fixes davidhalter/jedi-vim#152 2013-08-06 18:44:34 +04:30
David Halter
5ec5e2b139 Merge branch 'dev' of github.com:davidhalter/jedi into dev 2013-08-06 18:03:47 +04:30
David Halter
50438218ef *args/**kwargs without call, fixes #235 2013-08-06 16:10:02 +04:30
David Halter
0dc3d5e195 *args/**kwargs tests for #235 (failing tests) 2013-08-06 15:34:12 +04:30
David Halter
22edd6a149 *args/**kwargs tests for #235 (those are working) 2013-08-06 15:21:40 +04:30
David Halter
289967303d added anaconda, another sublime/jedi plugin, cc @srusskih and @DamnWidget 2013-08-06 14:50:37 +04:30
David Halter
1a8227aeec changed no duplicate modules test to respect attributes with the same name as their module 2013-08-06 13:52:25 +04:30
Danilo Bargen
8837414e31 sith.py help formatting 2013-08-06 11:14:47 +02:00
David Halter
9f843730ed Merge branch 'repl' into dev 2013-08-06 10:57:24 +04:30
David Halter
0dc3106569 add keyword_names method to keyword module (includes test), fixes #248 2013-08-06 10:55:05 +04:30
David Halter
4d910491bc Merge pull request #280 from davidhalter/repl
Repl completion changes
2013-08-05 23:04:30 -07:00
David Halter
66a984b8ef move issues to pull request in contributing 2013-08-05 16:36:41 +04:30
David Halter
9c258ca897 remove a sys.path hack that is not needed 2013-08-05 16:26:50 +04:30
David Halter
06f150028a Merge pull request #278 from asmeurer/sith_print
Working on improving sith.py run
2013-08-05 04:52:32 -07:00
David Halter
77d771b10e Merge branch 'dev' into repl 2013-08-05 15:35:59 +04:30
David Halter
57ab3cf92c fix doctest problem 2013-08-05 15:35:46 +04:30
David Halter
8f7de8f663 Merge branch 'dev' into repl 2013-08-05 15:20:10 +04:30
David Halter
cea1d265a6 repl documentation, adds @asmeurer suggestions 2013-08-05 15:18:24 +04:30
David Halter
e287bf9bca changelog for version 0.7.0 2013-08-02 15:45:47 +02:00
David Halter
a118b001cc version bump to 0.7 2013-08-02 15:38:39 +02:00
David Halter
997e506038 fix a magic method problem 2013-08-02 15:11:01 +02:00
David Halter
68d02df017 fix a parent problem with generators 2013-08-01 15:49:52 +02:00
David Halter
cef58f89a8 generator problem test 2013-08-01 10:23:22 +02:00
David Halter
6c3d7431e7 fix a typo 2013-07-31 23:03:50 +02:00
David Halter
c615e70889 only use repl keyword for now in __main__.py. 2013-07-31 17:32:47 +02:00
David Halter
ae6dc782da simplify repl completion support, only use the jedi one, because the builtin completion (rlcompleter) is not context sensitive and makes the whole thing really complicated. 2013-07-31 17:16:12 +02:00
David Halter
ea2f9ebd4c from LPGL to MIT license (got the permission from all contributors), fixes #5 2013-07-30 03:11:34 +02:00
Aaron Meurer
2b5f53d1d4 Minor fixes 2013-07-29 11:55:09 -05:00
David Halter
8276848bfb merged master with dev 2013-07-29 15:12:21 +02:00
David Halter
2b6cc01d39 forgot @hattya and @srusskih in the contributors list 2013-07-29 14:05:54 +02:00
David Halter
16e96afcc0 add emails to authors/contributors 2013-07-28 23:22:38 +02:00
David Halter
cdba4ef3e1 fixed multi level import issue, includes parts of a patch by Lubos Trilety <ltrilety@redhat.com> 2013-07-28 23:15:45 +02:00
Aaron Meurer
fe63e3bc97 Add printing for all the different kinds of completions in sith.py run
This kind of printing should probably go in the objects themselves, but I was
too disgusted by the completion APIs to even begin to start extending the
code for that (it needs to be cleaned up first).
2013-07-28 15:13:09 -05:00
Aaron Meurer
47bed4a30d Add type checking for sith.py run 2013-07-28 15:12:51 -05:00
Aaron Meurer
ae09a2d0a4 Define the operations in the class, not in the function
I was originally going to try to automatically include these in the docstring
using format, but they would have to be defined before the docstring for that
to work. But I think it's still useful to have these defined in the class.
2013-07-28 14:31:47 -05:00
Aaron Meurer
78f7ff6760 Document sith.py run 2013-07-28 14:31:40 -05:00
Aaron Meurer
40dec0c2c6 Fix a typo in the sith.py usage 2013-07-28 14:31:21 -05:00
David Halter
b681c5c90d Merge branch 'dev' of github.com:davidhalter/jedi into dev 2013-07-28 21:25:33 +02:00
David Halter
1ed94060df fix another position problem with the reverse tokenizer. includes tests. 2013-07-28 21:19:17 +02:00
Aaron Meurer
8af28bef2d Extra line for readability 2013-07-28 14:14:46 -05:00
Aaron Meurer
607f66c974 Print the location and the completions for sith.py run 2013-07-28 11:43:19 -05:00
David Halter
1f54e71a0d Merge pull request #276 from asmeurer/nosetuptools
Nosetuptools
2013-07-28 05:45:33 -07:00
David Halter
448e9e90be Merge pull request #273 from asmeurer/pudb
Pudb
2013-07-28 05:44:57 -07:00
Aaron Meurer
9c4c36ce6a Add myself to AUTHORS 2013-07-26 09:34:33 -05:00
Danilo Bargen
acf068fb21 hub is no requirement 2013-07-26 16:09:52 +02:00
Danilo Bargen
6e649c1a67 Added hub pull-request info to CONTRIBUTING.md 2013-07-26 16:07:16 +02:00
Aaron Meurer
42e1737be3 Don't require distribute in setup.py
It isn't actually required to install (it's only needed if you want to
setup.py develop).

Fixes #269.
2013-07-25 21:58:54 -05:00
Aaron Meurer
783f71501e Give all the necessary traceback info when using PuDB 2013-07-25 19:00:02 -05:00
Aaron Meurer
a0d8b4b508 PuDB's post_mortem is now consistant with pdb (https://github.com/inducer/pudb/pull/71) 2013-07-25 18:41:53 -05:00
David Halter
8598fe7327 get the reverse tokenizer positions right even with strange docstring situations 2013-07-26 01:24:44 +02:00
Danilo Bargen
d057f5e587 Change SublimeJEDI url in docs too 2013-07-25 22:27:11 +02:00
Danilo Bargen
76ff9e3f20 s/svaiter/srusskih/g 2013-07-25 22:25:22 +02:00
Danilo Bargen
bdbe765b22 Merge pull request #270 from srusskih/patch-1
change SublimeJEDI url
2013-07-25 13:21:19 -07:00
srusskih
33397314fe change SublimeJEDI url 2013-07-25 21:30:42 +03:00
David Halter
284f2f1671 add __iter__method to InstanceElement, because it's needed for arrays 2013-07-24 17:21:49 +02:00
David Halter
c8648d9ca5 test for unnecessary parentheses inside instances 2013-07-24 16:57:46 +02:00
David Halter
f4fdf904ee fix lambdas in instances 2013-07-24 16:30:38 +02:00
David Halter
3c96ef8905 test for lambdas in InstanceElements 2013-07-24 16:16:20 +02:00
David Halter
18e561f332 fix problem that strings are no function calls 2013-07-24 16:11:51 +02:00
David Halter
972d4e9a08 fix a problem with dynamic flow information and usages 2013-07-24 01:28:15 +02:00
David Halter
991b138ff3 star imports and usages 2013-07-23 17:21:12 +02:00
David Halter
dbd04da26c set exit code in sith 2013-07-23 17:03:13 +02:00
David Halter
fd2e158cf6 fix usage problem on **kwargs params 2013-07-23 16:33:59 +02:00
David Halter
68635fc80c fix empty return statement docstrings 2013-07-23 15:36:53 +02:00
David Halter
9c53de5034 add empty return type test 2013-07-23 15:35:37 +02:00
David Halter
434de60350 fixed docstring params 2013-07-23 15:26:29 +02:00
David Halter
90202b2ad6 tests for failing docstrings 2013-07-23 15:11:14 +02:00
David Halter
000eb20cce delete old IndexError catches, didn't make sense 2013-07-23 14:39:43 +02:00
David Halter
d78b72b046 fixed getattr with array call 2013-07-23 14:30:18 +02:00
David Halter
db17c27bb8 removed old very strange string casts, that didn't really make sense and even though it mentioned a test, it wouldn't break after deleting it 2013-07-23 13:33:00 +02:00
David Halter
bb8e864f8e fix UnboundLocalError and TypeError within Array lookups, not a very good solution, but for now it doesn't puke errors 2013-07-23 02:33:07 +02:00
David Halter
52c8340d72 invalid goto syntax 2013-07-23 00:19:58 +02:00
David Halter
471f492f12 generators in *args and illegal *args like *1 2013-07-23 00:07:17 +02:00
David Halter
6a2afa7e94 failing test for illegal unpacking with * 2013-07-22 14:42:39 +02:00
David Halter
a6e621e39c add @lvh, @Astrac, @ganwell, @floppym to authors 2013-07-20 10:19:57 +02:00
David Halter
2cd7e66016 also add sith.py, refs #264 2013-07-20 10:09:17 +02:00
David Halter
6ab65999b0 Merge branch 'master' of https://github.com/floppym/jedi into dev 2013-07-20 10:06:44 +02:00
David Halter
b3c07b9a25 Merge remote-tracking branch 'origin/master' into dev 2013-07-20 10:03:49 +02:00
Mike Gilbert
7312999c8f Add a few more test support files 2013-07-19 18:12:44 -04:00
David Halter
6017daded5 removed statement docstr stuff for now. it will be reintroduced when using the evaluator class 2013-07-19 13:17:27 +02:00
David Halter
ebc8d4c04c docstring test with statement docstrings (failed until now, worked only for specific cases) 2013-07-19 12:47:38 +02:00
David Halter
ed99e380d7 catch ValueError when the cache json.load method fails, fixes #245 2013-07-19 10:55:07 +02:00
David Halter
4e34fb9494 fixed illegal params problem 2013-07-19 10:32:58 +02:00
David Halter
0dfe2f44a5 test for dots in params (happens sometimes by accident when generating builtins) 2013-07-19 02:40:17 +02:00
David Halter
6525f9169c reformat feature documentation 2013-07-19 01:29:58 +02:00
David Halter
9b73f3ca3c exceptions are now accepted in the form of 'except Exception, e' 2013-07-19 01:29:16 +02:00
David Halter
f754d1565f fixed error with 'comma' separated exceptions 2013-07-19 01:27:40 +02:00
David Halter
b84315b829 tests for import problems 2013-07-19 00:56:56 +02:00
David Halter
a7cfb89c2a fix a sith problem with empty files 2013-07-19 00:15:09 +02:00
David Halter
29f89cc75a finally fixed a unicode problem that existed for a longer time 2013-07-18 16:55:19 +02:00
David Halter
7684135b99 finally found a test for unicode problems 2013-07-18 16:45:27 +02:00
David Halter
3777afb2cb add record file in sith.py to other commands 2013-07-18 14:57:22 +02:00
David Halter
dd6967de7c fixed a unicode test problem 2013-07-18 14:54:40 +02:00
David Halter
681b9d6371 small sith improvements 2013-07-18 12:35:18 +02:00
David Halter
9348feb8bf operations (+, *, etc) shouldn't be InstanceElements, fixes #246 2013-07-18 12:31:48 +02:00
David Halter
075c60de29 fix a problem related to #246: comments were used as normal strings 2013-07-18 12:18:07 +02:00
David Halter
b6e9f16a01 fix a completion problem on docstrings 2013-07-17 16:48:50 +02:00
David Halter
8490b1d0ff forgot to cast to int with the sith run command 2013-07-17 16:38:35 +02:00
David Halter
45c8cc739b fixed the dict problem, but dictionary completions are still very bad 2013-07-17 16:14:06 +02:00
David Halter
0dbfc409a7 iteration problem test 2013-07-17 15:56:21 +02:00
David Halter
8db85dbe15 add docopt to tox 2013-07-17 15:21:56 +02:00
David Halter
c0d51e300b fixed problem with empty statements in function calls 2013-07-17 15:05:06 +02:00
David Halter
2ad3121aa3 test for empty statement problem in function call 2013-07-17 15:02:56 +02:00
David Halter
9ee0d34bc7 corrected docopt typo 2013-07-17 14:13:52 +02:00
David Halter
fcf4b04145 Merge branch 'dev' of github.com:davidhalter/jedi into dev 2013-07-17 14:01:13 +02:00
David Halter
8d8b645f11 completely rewrote sith.py with docopt - also added a run command and debug option 2013-07-17 14:00:28 +02:00
Mike Gilbert
ebd4e228d3 Include everying in the test directory in source tarballs.
Resolves #256.
2013-07-15 23:55:33 -04:00
David Halter
57196b0278 Merge pull request #260 from lvh/cleanup
Many minor PEP8 and related cleanups
2013-07-12 02:01:39 -07:00
Laurens Van Houtven
627266b38d Move isinstance checks back to unary type checks 2013-07-11 20:36:00 +02:00
Laurens Van Houtven
73662fe893 More PEP8 indentation fixes 2013-07-11 15:26:07 +02:00
Laurens Van Houtven
8c314c2732 while 1 -> while True 2013-07-11 15:25:05 +02:00
Laurens Van Houtven
5b2028c9fc PEP8 indentation 2013-07-11 15:24:15 +02:00
Laurens Van Houtven
75f848d8a9 PEP8 indentation 2013-07-11 15:22:09 +02:00
Laurens Van Houtven
6157be1a19 PEP8 indentation of multiline expressions 2013-07-11 15:21:10 +02:00
Laurens Van Houtven
1391a5a914 PEP8 newlines between class members 2013-07-11 15:19:54 +02:00
Laurens Van Houtven
4763846b1b PEP8 indentation 2013-07-11 15:19:31 +02:00
Laurens Van Houtven
f353d5f6af PEP8 imports 2013-07-11 15:18:30 +02:00
Laurens Van Houtven
bb0a4e0d0a Fix indentation according to PEP8 in api.py 2013-07-11 14:55:36 +02:00
Laurens Van Houtven
3817110717 Remove unneccessary pass statement 2013-07-11 14:55:16 +02:00
Laurens Van Houtven
afcaa89cf0 Use isinstance instead of unary type 2013-07-10 19:00:11 +02:00
Laurens Van Houtven
18c105b055 Remove stale debug code 2013-07-10 18:57:05 +02:00
Laurens Van Houtven
ad932815b9 PEP8 whitespace fixes 2013-07-10 18:55:48 +02:00
Laurens Van Houtven
cdc41128b4 (Mostly) whitespace fixes 2013-07-10 14:45:47 +02:00
David Halter
1ce44c483f Merge pull request #258 from lvh/master
absolute_import test comment still insinuates jedi gets it wrong
2013-07-10 05:15:15 -07:00
Laurens Van Houtven
8dae2049c9 Fix inaccuracy in test comment, since jedi now does the right thing 2013-07-10 14:06:33 +02:00
David Halter
2fffaee2c3 Merge pull request #252 from lvh/master
Handle explicit absolute imports correctly
2013-07-10 05:02:14 -07:00
Laurens Van Houtven
96477cbb13 Inline absolute import detection function 2013-07-10 12:24:19 +02:00
Laurens Van Houtven
e42d0d226f Remove stale pytest import 2013-07-10 12:24:02 +02:00
Laurens Van Houtven
402bc092e3 Remove has_explicit_absolute_import caching 2013-07-10 12:18:41 +02:00
Laurens Van Houtven
4068f5f058 Move has_absolute_import to SubModule class 2013-07-10 12:17:48 +02:00
Laurens Van Houtven
b5ee375fe1 Rename explicit_absolute_import to has_explicit_absolute_import 2013-07-10 12:12:33 +02:00
Laurens Van Houtven
1682f24703 Merge branch 'dev' of git://github.com/davidhalter/jedi 2013-07-10 12:06:35 +02:00
Laurens Van Houtven
42fc44b478 Remove unused py.test test skipping decorators 2013-07-10 12:06:13 +02:00
David Halter
57c030c160 remove untested legacy code 2013-07-10 10:25:13 +02:00
David Halter
2ce0dfbeb7 Merge branch 'dev' of github.com:davidhalter/jedi into dev 2013-07-10 10:24:22 +02:00
David Halter
6cb021f4ae add memoize decorator to an Instance method, fixes RecursionError of #239 2013-07-10 10:23:05 +02:00
Jean-Louis Fuchs
462e450de8 * removing claim from jedi-logo 2013-07-05 14:42:08 +02:00
David Halter
2dc3d9e135 added new logo, designed by @ganwell, refs #98 2013-07-05 14:42:08 +02:00
Danilo Bargen
777e7e57d1 Merge pull request #255 from ganwell/change_logo
Removing claim from jedi-logo
2013-07-05 05:35:45 -07:00
Jean-Louis Fuchs
afb4c8cee5 * removing claim from jedi-logo 2013-07-05 14:15:55 +02:00
Danilo Bargen
18b5b3072d SublimeJEDI should also work with ST3 2013-07-02 12:15:26 +02:00
Laurens Van Houtven
3d4a71f6bb Use Parser.module instead of Parser.scope 2013-06-23 22:50:38 +02:00
Laurens Van Houtven
e39f8a246e Merge branch 'dev' of git://github.com/davidhalter/jedi 2013-06-23 22:50:00 +02:00
Laurens Van Houtven
60ee6c607a Don't throw away path optimization for modules with explicit absolute import 2013-06-23 22:43:21 +02:00
Laurens Van Houtven
368c7fd5b5 Only deal with explicit absolute_import 2013-06-23 22:30:53 +02:00
Laurens Van Houtven
58b165e4b6 Don't assume either namespace is not-None 2013-06-23 22:02:37 +02:00
Laurens Van Houtven
7eff1bbf06 Don't mess with the path if the import is absolute 2013-06-23 22:00:23 +02:00
Laurens Van Houtven
e82e3eaa0d Don't assume every module has a namespace 2013-06-23 21:59:58 +02:00
Laurens Van Houtven
adec666994 py.test skipping evaluation rules are kinda silly, and in this case completely useless 2013-06-23 21:56:00 +02:00
Laurens Van Houtven
cb4acccfe5 More PEP8, minor cleanup 2013-06-22 09:54:18 +02:00
Laurens Van Houtven
ae94a2cda1 Remove stale import 2013-06-22 08:57:02 +02:00
Laurens Van Houtven
397b881c96 PEP8 whitespace 2013-06-22 08:56:32 +02:00
Laurens Van Houtven
0b3955a257 Remove minimal_demo.py demo script 2013-06-22 08:53:51 +02:00
Laurens Van Houtven
38daa3cc34 Add failing test case 2013-06-21 19:49:05 +02:00
Laurens Van Houtven
d32045303f Add absolute_import failing SSCCE, and get pytest to ignore it 2013-06-21 19:25:18 +02:00
Laurens Van Houtven
5701ac1a10 Add absolute import detection 2013-06-21 17:02:17 +02:00
David Halter
248931d9f3 Merge pull request #250 from lvh/master
Cleanup in _compatibility
2013-06-20 14:37:48 -07:00
Laurens Van Houtven
fd87e8af2a Refactor the way module_path, module_file and is_package are computed to be a bit more legible 2013-06-20 22:36:16 +02:00
Laurens Van Houtven
1b0822743c Clean up find_module_py33 loader finding 2013-06-20 22:05:08 +02:00
Laurens Van Houtven
f171617766 Raise a nicer exception instance instead of raising an exception class 2013-06-20 21:40:37 +02:00
Laurens Van Houtven
44b1390d80 Clean up find_module_pre_py33 2013-06-20 21:35:56 +02:00
Laurens Van Houtven
68f9cdd884 Pick find_module implementation statically 2013-06-20 21:08:33 +02:00
Laurens Van Houtven
e201f89a77 Remove double reduce import 2013-06-20 21:08:33 +02:00
David Halter
7509c239ec Merge branch 'dev' of github.com:davidhalter/jedi into dev 2013-06-20 11:27:08 +02:00
David Halter
aa5bb539c1 added new logo, designed by @ganwell, refs #98 2013-06-20 11:26:06 +02:00
Takafumi Arakaki
5de63873df Fix and improve settings.cache_directory document
fixes #244
2013-06-19 15:38:24 +02:00
Danilo Bargen
c5a2ba3d35 s/emacs-jedi/Jedi.el/g 2013-05-27 10:40:43 +02:00
Danilo Bargen
97ef7b00db Added YCM link to docs too 2013-05-27 10:38:45 +02:00
Danilo Bargen
06cd9752bd Added YCM to plugin list 2013-05-27 10:33:20 +02:00
Takafumi Arakaki
868a4b5dd8 Fix sith.py for default --record 2013-05-24 23:19:45 +02:00
Takafumi Arakaki
4f66173603 Merge branch 'backslash-continuation' into dev 2013-05-24 23:11:02 +02:00
Takafumi Arakaki
96ca86e9f7 Run random smoke test at Travis 2013-05-24 23:06:51 +02:00
Takafumi Arakaki
a94642b9c0 Add testenv:sith in tox.ini 2013-05-24 23:06:50 +02:00
Takafumi Arakaki
1f3c4700c9 Fix get_path_until_cursor for empty path + continuation 2013-05-24 22:48:40 +02:00
Takafumi Arakaki
71455f6b31 Add another failing case 2013-05-24 22:05:12 +02:00
Takafumi Arakaki
05564c23d5 Fix the error 2013-05-24 21:06:48 +02:00
Takafumi Arakaki
105bb2b1ca ModuleWithCursor.get_path_until_cursor cannot handle "\"
It raises: IndexError: string index out of range
2013-05-24 19:43:23 +02:00
Takafumi Arakaki
788eeb9bd5 Simplify how post_mortem is launched 2013-05-24 19:10:19 +02:00
Takafumi Arakaki
91c605b7f0 Add --pudb option to sith.py 2013-05-24 18:58:34 +02:00
David Halter
b0116b0d7c Merge pull request #232 from tkf/unicode-has-no-generate_call_path
Fix: 'unicode' object has no attribute 'generate_call_path'
2013-05-23 22:33:02 -07:00
David Halter
4e3fe88141 Merge pull request #234 from tkf/None.isinstance
Fix: 'NoneType' object has no attribute 'isinstance'
2013-05-23 21:15:19 -07:00
Takafumi Arakaki
b9c71b5bb1 Merge branch 'multi-assignments' into dev 2013-05-23 23:43:51 +02:00
Takafumi Arakaki
45bddec83a Add more complex assignment tests 2013-05-23 23:41:41 +02:00
Takafumi Arakaki
b896f352ad Support "a = b = 1"-style assignment 2013-05-23 23:41:26 +02:00
Takafumi Arakaki
ced1a83f5b Add a failing test: "a = b = 1"-style assignment 2013-05-23 23:28:30 +02:00
Takafumi Arakaki
636b10ab8e Fix: 'NoneType' object has no attribute 'isinstance' 2013-05-23 22:41:36 +02:00
Takafumi Arakaki
3620633b2a Turn off FS cache in sith.py by default 2013-05-23 21:56:43 +02:00
Takafumi Arakaki
346cb87830 Print traceback in redo 2013-05-23 21:42:06 +02:00
Danilo Bargen
d9b2788ef8 Fixed py26 ImportError in sith.py 2013-05-23 16:34:14 +02:00
Danilo Bargen
021940e46b Fixed typo in sith.py classname 2013-05-23 16:30:28 +02:00
Danilo Bargen
dc5190abe2 Removed unused import from sith.py 2013-05-23 16:27:25 +02:00
Danilo Bargen
84ba8bbca1 Small doc improvement in sith.py 2013-05-23 16:24:39 +02:00
Danilo Bargen
336ab32345 Moved argparse import in sith.py to top (refs #231) 2013-05-23 16:23:08 +02:00
Danilo Bargen
4d210af6bf Fixed typo in sith docstring 2013-05-23 15:49:01 +02:00
Takafumi Arakaki
a4922774c0 Fix the previous error 2013-05-23 15:32:32 +02:00
Takafumi Arakaki
ce92fd946b Add failing test: test_goto_assignments_keyword 2013-05-23 15:32:32 +02:00
Takafumi Arakaki
58031d34fe Merge pull request #231 from tkf/sith
Add sith.py, a script for random smoke test
2013-05-23 04:22:25 -07:00
Takafumi Arakaki
286279f14a Show default value for --record 2013-05-23 00:53:45 +02:00
Takafumi Arakaki
d246192df0 Add short options 2013-05-23 00:49:20 +02:00
Takafumi Arakaki
fd0ec772fb Fix: record was not saved 2013-05-23 00:20:11 +02:00
Takafumi Arakaki
cee0d4cf2f Generate better help / add more help 2013-05-23 00:00:25 +02:00
Takafumi Arakaki
8bf5f9d539 Report attacking by "." 2013-05-22 23:33:01 +02:00
Takafumi Arakaki
2dee71ff4b Ignore jedi.NotFoundError 2013-05-22 23:14:32 +02:00
Takafumi Arakaki
6d026ec1af Add --(i)pdb option 2013-05-22 23:11:50 +02:00
Takafumi Arakaki
1e209aed37 Add 'show' command 2013-05-22 23:00:19 +02:00
Takafumi Arakaki
311025258e Print exception 2013-05-22 22:51:34 +02:00
Takafumi Arakaki
0fecb0a780 Add sith.py, a script for random smoke test 2013-05-22 22:33:58 +02:00
Takafumi Arakaki
0ee2c16551 Add TestDefinedNames.test_nested_class 2013-05-21 20:16:44 +02:00
David Halter
fbf17e3e5b Merge pull request #228 from tkf/fix-full_name
Fix full_name for import statements
2013-05-21 09:11:15 -07:00
Takafumi Arakaki
46ac76e8be Reproduce the previous test failure w/o stdlib
It turned out that the failure occurs when you define "fake module path"
by importing some module from others.  Indeed, this is what happens
in os.py.
2013-05-21 17:40:20 +02:00
Takafumi Arakaki
1a5942364a Add failing tests for first part of #213 2013-05-21 17:16:40 +02:00
Takafumi Arakaki
91f3d524de Document test_full_name.py 2013-05-21 16:57:40 +02:00
Takafumi Arakaki
851238386f Fix tests for defined_names + full_name 2013-05-21 14:23:59 +02:00
Takafumi Arakaki
5d6719ed8c Add tests for defined_names + full_name 2013-05-21 14:23:58 +02:00
Takafumi Arakaki
339ebbbf4e Fix TestFullNameWithCompletions.test_from_import 2013-05-21 12:30:21 +02:00
Takafumi Arakaki
f4982606d8 Refactor tests for full_name 2013-05-21 12:10:03 +02:00
David Halter
dcf3f0dcdc downloads badge, yay!!! 2013-05-21 11:00:58 +04:30
David Halter
5567a42334 fix another keyword problem 2013-05-21 10:59:28 +04:30
David Halter
a9bf06987a completion on empty import problem 2013-05-19 21:13:32 +04:30
David Halter
384334ae06 goto on import statement (only import) raised an error 2013-05-19 20:40:54 +04:30
David Halter
c5169b2d66 parsing.Parser.scope -> parsing.Parser._scope, fixes #224 2013-05-19 10:25:00 +04:30
David Halter
ec061ea612 Merge pull request #222 from tkf/replstartup
Improve $PYTHONSTARTUP support
2013-05-18 21:51:26 -07:00
David Halter
93f4cb5b6b Merge pull request #223 from tkf/fix-defined_names
Fix defined_names
2013-05-18 21:49:04 -07:00
Takafumi Arakaki
6acf34efd3 Fix: api.defined_names was run against wrong scope
when there is only one class is defined in the module.
2013-05-19 05:08:18 +02:00
Takafumi Arakaki
e011683ea4 dedent source before passing it to defined_names
test_nested_definitions fails now
2013-05-19 05:08:18 +02:00
Takafumi Arakaki
6fadfb573c Refactor TestDefinedNames 2013-05-19 05:08:18 +02:00
Takafumi Arakaki
29f74c245d Move tests for defined_names to test_defined_names.py 2013-05-19 05:08:18 +02:00
Takafumi Arakaki
200d29713e Make defined_names importable at top-level 2013-05-19 05:08:17 +02:00
Takafumi Arakaki
4bd03d0c74 Document JediRLCompleter 2013-05-19 00:52:41 +02:00
Takafumi Arakaki
3b3310ee3e Determine readline delims programmatically 2013-05-19 00:42:00 +02:00
Takafumi Arakaki
6a8a06fd0b Improve JediRLCompleter for minor cases 2013-05-19 00:42:00 +02:00
Takafumi Arakaki
0c85642e6b Add utils.JediRLCompleter 2013-05-19 00:41:58 +02:00
Takafumi Arakaki
27f8b342e1 Document utils.setup_readline 2013-05-19 00:41:48 +02:00
Takafumi Arakaki
134dab174d Setup intersphinx module for linking Python doc
For example, :mod:`readline` links to the corresponding page.
2013-05-18 23:26:13 +02:00
Takafumi Arakaki
7a8ea56b05 Document jedi.replstartup usage 2013-05-18 23:26:13 +02:00
Takafumi Arakaki
ee5f96f119 Fix test failure
The bug was introduced when I merged branch 'interpreter-api' at
5f2477d5bf.
This patch redo (part of) 2846fe980b.
2013-05-18 23:25:54 +02:00
Takafumi Arakaki
5f2477d5bf Merge branch 'interpreter-api' into dev
Conflicts:
	jedi/__init__.py
	test/test_regression.py

See: #145
2013-05-18 22:28:56 +02:00
David Halter
700f685f59 Merge branch 'dev' of github.com:davidhalter/jedi into dev 2013-05-18 23:57:31 +04:30
David Halter
7e2eb587e1 is_keyword test 2013-05-18 23:56:12 +04:30
Danilo Bargen
e1afe2e80d Small docs changes 2013-05-15 23:33:14 +02:00
Danilo Bargen
6ed0e1580b Added remark about Python versions to installation docs 2013-05-15 23:31:46 +02:00
Danilo Bargen
16de02023f Use |jedi| macro in docs more consistently 2013-05-15 23:29:13 +02:00
Danilo Bargen
5c5f93d69e Typo in docs 2013-05-15 23:28:20 +02:00
Danilo Bargen
6d1a33a3c7 Added python2 AUR package 2013-05-15 23:27:16 +02:00
David Halter
d5d12716b1 update debian installation notice, cc @p1otr 2013-05-14 10:02:50 +04:30
David Halter
f3788c9955 Merge pull request #215 from davidhalter/cov
Use coverage differently
2013-05-13 06:38:42 -07:00
David Halter
b2b8bfc8e3 don't erase coverage data 2013-05-13 18:01:59 +04:30
David Halter
bb27afa19c forgot the coverage dependency when removing the pytest-coverage 2013-05-13 16:47:46 +04:30
David Halter
6b57f7ce48 remove pytest-cov from tox.ini requirements 2013-05-13 12:35:09 +04:30
David Halter
05eb62eea5 use coverage instead of pytest-cov 2013-05-13 12:29:56 +04:30
David Halter
fbde16450b authors change 2013-05-13 09:20:08 +04:30
David Halter
15885e8685 preload_module function for IDEs, to control which modules to load on startup, refs #102 2013-05-13 09:13:59 +04:30
Takafumi Arakaki
3d6ef88795 Add simple PYTHONSTARTUP file 2013-05-12 08:21:55 +02:00
Takafumi Arakaki
52b3a326c4 Revert changes in jedi/modules.py in this branch 2013-05-11 22:01:48 +02:00
Takafumi Arakaki
19b3fef0c5 Remove fast option from api.Script 2013-05-11 21:59:39 +02:00
Takafumi Arakaki
a870fece0f Use fast parser in api.Interpreter 2013-05-11 21:47:55 +02:00
Takafumi Arakaki
2846fe980b Remove api._quick_complete 2013-05-11 21:40:00 +02:00
Takafumi Arakaki
08f8dcfae4 Do not repeat defaults in Interpreter.__init__ 2013-05-11 21:37:53 +02:00
Takafumi Arakaki
6af2a0677d Compute default line/column/source_path in api.Script 2013-05-11 21:36:43 +02:00
Takafumi Arakaki
633f582184 Add jedi.interpret.ObjectImporter
and move scope manipulation functions in api.Interpreter to there.
2013-05-11 21:33:57 +02:00
David Halter
b5b625ea14 forgot to include the changelog in the manifest 2013-05-11 17:07:55 +04:30
David Halter
977f4871d6 added changelog 2013-05-11 16:55:02 +04:30
David Halter
7d4fe30c63 change autocomplete screenshot, which contained wrong information for a long time now. 2013-05-11 14:30:07 +04:30
David Halter
29575b2562 add some testing notes of the readme to the documentation 2013-05-11 14:00:15 +04:30
David Halter
3681adef78 improve README 2013-05-11 13:53:00 +04:30
David Halter
b2f2b7a779 update installation docs 2013-05-11 11:29:40 +04:30
David Halter
f30b3416e8 remove VERSION file in favor of a jedi.__version__ 2013-05-11 11:10:01 +04:30
David Halter
ad56b6cc89 fix title length (docstr stuff) 2013-05-11 11:02:13 +04:30
David Halter
3a7c8af87f also normal imports should return the right user_stmt, similar case than #137 2013-05-09 12:16:54 +04:30
David Halter
f3a4439285 import end_pos calculated differently, fixes #137 2013-05-09 11:54:08 +04:30
David Halter
4b4681d285 re-enable test, probably related to #131 2013-05-08 00:01:22 +04:30
David Halter
f4e8972157 end_pos issues, fixes #150 2013-05-07 23:55:58 +04:30
David Halter
0621a276b9 modules in modules (only possible with c_builtins) are now completable. however it's kind of hackish, I'm using a class instead of a real module, because this is the easiest way for jedi, fixes #126 2013-05-07 22:57:23 +04:30
David Halter
6cb0b1ed16 bump version to 0.6 2013-05-07 21:03:04 +04:30
Takafumi Arakaki
17f5b9a79d Fix test failure in Python 3.3
There is a new method introduced in Python 3.3:
http://docs.python.org/3/library/datetime.html#datetime.datetime.timestamp
2013-05-04 18:10:20 +02:00
Takafumi Arakaki
89edb73978 Fix failure due to rebase 2013-05-04 17:58:02 +02:00
Takafumi Arakaki
3d0458bca9 Export Interpreter class to top-level namespace 2013-05-04 17:50:42 +02:00
Takafumi Arakaki
e8914e7856 Document api.Interpreter 2013-05-04 17:45:34 +02:00
Takafumi Arakaki
8fc396371c Respect settings.fast_parser 2013-05-04 17:44:59 +02:00
Takafumi Arakaki
c2e9ccda2b Fix failing test: args for ModuleWithCursor 2013-05-04 17:44:59 +02:00
Takafumi Arakaki
dc47d15de0 Use locals() for tests in TestInterpreterAPI
It is better to check that Interpreter does not fail with the
real name space.  Previously the doctest using locals() failed
because of the bug in _import_raw_namespace.
2013-05-04 17:44:59 +02:00
Takafumi Arakaki
a93016db1b Python 2.5 compatibility fix 2013-05-04 17:44:59 +02:00
Takafumi Arakaki
af10891096 Document api.Interpreter._genname 2013-05-04 17:44:59 +02:00
Takafumi Arakaki
fc79f0e258 Support case like from os.path import join as pjoin 2013-05-04 17:44:59 +02:00
Takafumi Arakaki
6fb42e5f4b Document _import_raw_namespace more 2013-05-04 17:44:59 +02:00
Takafumi Arakaki
12ac71b1fd Document api.Interpreter 2013-05-04 17:44:59 +02:00
Takafumi Arakaki
b4e3d1d65d Fix test_complete_raw_instance example
'dt.strftime("%Y").up' does not work even if with `api.Script`.
Change it to the one that can be handled by Jedi.
2013-05-04 17:44:59 +02:00
Takafumi Arakaki
45cad4c83b Fake imports should locate before fake statements 2013-05-04 17:44:59 +02:00
Takafumi Arakaki
06c03cafa2 Implement the case where raw object is an instance
There is still a bug in this implementation.
The corresponding test fails.
2013-05-04 17:44:58 +02:00
Takafumi Arakaki
11abd92d2b Support alias in _make_fakeimport 2013-05-04 17:44:58 +02:00
Takafumi Arakaki
cf48d1272d Use proper interface to add fake Import object 2013-05-04 17:44:58 +02:00
Takafumi Arakaki
17770356e2 Implement the case where raw object is a module
Previously failing test_complete_raw_module is passed.
2013-05-04 17:44:58 +02:00
Takafumi Arakaki
fb197c42ab Add tests for api.Interpreter (2/3 fail) 2013-05-04 17:44:58 +02:00
Takafumi Arakaki
3266aa2d00 Improve api.Interpreter docstring 2013-05-04 17:43:37 +02:00
Takafumi Arakaki
842fbfce8a Add api.Interpreter: Jedi API for Python REPLs 2013-05-04 17:43:37 +02:00
David Halter
ae0dd1d72c fix docstr issue in the wrong place, fixes #210 2013-05-04 19:51:42 +04:30
David Halter
2e0d0bb4f5 test for #210 2013-05-04 13:44:15 +04:30
David Halter
698ee7af28 removed an old part_parser test 2013-05-03 21:35:28 +04:30
David Halter
4d0417f591 change some definition usages to goto_definitions in tests and docs 2013-05-03 21:35:01 +04:30
David Halter
7c8fee1257 removed a lot of the goto usages, used goto_assignments instead. Did the same also for a lot of definition uses 2013-05-03 21:26:29 +04:30
David Halter
edd0a08351 api_classes.RelatedName -> Usage 2013-05-03 21:06:56 +04:30
David Halter
efeeee9706 removed related_names occurences in favor of usages 2013-05-03 21:04:36 +04:30
David Halter
860cc5de04 make api_classes.BaseDefinition._definition private 2013-05-03 20:56:07 +04:30
David Halter
5d472e99ac more variables private in the same file 2013-05-03 20:51:24 +04:30
David Halter
150b7fc1d5 make some variables of the Completion class private 2013-05-03 20:41:18 +04:30
David Halter
49e51f5a1a deprecated api_classes.Completion.word in favor of name 2013-05-03 20:38:37 +04:30
David Halter
beae920177 Script.complete has been deprecated, therefore changed all usages / documatation to Script.completions 2013-05-03 20:28:11 +04:30
David Halter
c912428f78 deprecation process for a nice api design (as discussed in #124)
deprecate:

- definition
- goto
- complete
- related_names
- function_definition

in favor of:

- goto_definitions
- goto_assignments
- completions
- usages
- call_signatures

Thank you @dbrgn and @tkf for the fruitful discussion!
2013-05-03 20:15:36 +04:30
David Halter
c0bb6ff04b even more deletions (clean up for last commit) 2013-05-03 19:35:51 +04:30
David Halter
88e60b85e0 best patch in a long time, deleted all the part_parser stuff which was necessary for fast function_definitions, but with the new parser Jedi's fast enough -> fixes #136 2013-05-03 19:33:24 +04:30
David Halter
a2da599d6e fix a completion in dict problem 2013-05-03 18:31:05 +04:30
David Halter
55d2b19cc0 get rid of UncaughtAttributError, fixes #191 2013-05-03 17:22:48 +04:30
David Halter
4865505215 getattr is now also allowed on modules, fixes #116 2013-05-03 17:00:07 +04:30
David Halter
1d45105461 flows in classes shouldn't be a problem anymore, fixes #183 2013-05-03 16:29:34 +04:30
David Halter
fd4eb5f0a6 add test of #183 2013-05-03 15:16:09 +04:30
David Halter
c37515f938 included test from #162 (seems to be working), fixes #162 2013-05-03 15:01:30 +04:30
David Halter
300d43de19 PEP 3148 module formatting, fixes #207 2013-05-03 14:17:48 +04:30
David Halter
cc520530c0 changed module name checking (should only be in one place) 2013-05-03 14:09:46 +04:30
David Halter
da067c5c38 add _module to BaseDefinition api 2013-05-03 14:04:40 +04:30
David Halter
902e197c4e bump _ModulePickling number 2013-05-03 13:26:25 +04:30
David Halter
88f39a01cb Merge pull request #208 from davidhalter/fast
New improved Fast Parser
2013-05-02 14:11:05 -07:00
David Halter
6204cb740b merge with dev branch 2013-05-03 01:28:50 +04:30
David Halter
302d06de85 fix end_pos stuff in parser (no (None, None) end_pos anymore 2013-05-03 01:14:41 +04:30
David Halter
4bb4176296 fixed a position problem 2013-05-02 18:56:22 +04:30
David Halter
b820d1ee27 set user_scope right in fast_parser 2013-05-02 17:58:06 +04:30
David Halter
aebfb3d162 fix pass indentation in functions 2013-05-02 11:33:14 +04:30
David Halter
01be114386 renaming tests are now possible outside of renaming.py 2013-05-01 21:07:37 +04:30
David Halter
f5aaeaa428 fast parser splitting is now working better 2013-05-01 14:31:41 +04:30
David Halter
fa32c0a765 fix a set_parent problem 2013-04-30 22:06:46 +04:30
David Halter
6bf64b2ed7 fixed speed problems 2013-04-30 21:41:46 +04:30
David Halter
f535267a9b fixed a memory leak 2013-04-30 21:24:44 +04:30
David Halter
0f3454f897 fix test problems, thx @tkf 2013-04-29 15:14:17 +04:30
David Halter
841f4d8423 skip one regression test 2013-04-28 22:24:19 +04:30
David Halter
3eeecff1a1 fixed some position issues 2013-04-28 22:12:32 +04:30
David Halter
56ca0cbfaa skip unit test until this will be fixed 2013-04-28 21:30:26 +04:30
David Halter
7ad156bde7 fixed a problem with 'None' returns 2013-04-28 21:19:05 +04:30
David Halter
4b36fb2f6f corrected some doctest problems 2013-04-28 21:10:29 +04:30
David Halter
9988c49097 fix pushback problem with tokenizer 2013-04-28 00:24:52 +04:30
David Halter
c675706877 clean up a little bit 2013-04-27 23:47:18 +04:30
David Halter
3feac63468 fix global_vars 2013-04-27 23:45:48 +04:30
David Halter
aab3b1c627 also remove old start_pos stuff 2013-04-27 22:49:45 +04:30
David Halter
26ce32ec6b fix some end_pos problems 2013-04-27 22:47:26 +04:30
David Halter
792808d835 fix another IndexError, due to empty files (e.g. __init__.py) 2013-04-27 21:47:54 +04:30
David Halter
3627263cbe fixed an IndexError 2013-04-27 21:36:57 +04:30
David Halter
4839f3d80e fix a scope problem with the new fast parser 2013-04-27 21:25:42 +04:30
David Halter
e99f7a25fe exceptions should produce errors even in alternate run display 2013-04-27 20:55:51 +04:30
David Halter
199d80fa02 fix most rename tests 2013-04-27 20:22:28 +04:30
Danilo Bargen
b3d9b6ce69 Removed py25 related code (fixes #206) 2013-04-27 16:47:40 +02:00
Danilo Bargen
b06e654b92 Removed py25 from tox.ini (refs #206) 2013-04-27 15:58:19 +02:00
Danilo Bargen
df468d3f38 Use dev version of pytest.
This should fix some issues with testing under py32, see
https://bitbucket.org/hpk42/pytest/issue/274/internalerror-when-running-doctest
2013-04-27 15:40:58 +02:00
Danilo Bargen
86d775324b Fix most flake8 issues (fixes #205) 2013-04-27 15:40:36 +02:00
David Halter
45f29bfa31 fixed a problem with non used decorators when splitting parts 2013-04-24 12:52:45 +04:30
David Halter
ddc5b248f1 use correct parents 2013-04-21 23:32:31 +04:30
David Halter
80cbed2f9f fixed some decorator stuff. 2013-04-21 23:06:39 +04:30
David Halter
43306429f9 fixed some little testing problems 2013-04-21 22:55:12 +04:30
David Halter
e494be2537 Merge branch 'dev' into fast 2013-04-21 00:20:25 +04:30
David Halter
406e70d51a Merge pull request #204 from davidhalter/testing
Make run.py testing reusable
2013-04-20 12:49:30 -07:00
David Halter
07075a7264 running tests again from run.py works. 2013-04-21 00:04:51 +04:30
David Halter
d16f5f6a4c refactored test_integration, so that those tests can be called from multiple places. 2013-04-20 21:55:08 +04:30
David Halter
2b1ea33fe8 reintroduce old testing mode for run.py 2013-04-20 21:22:08 +04:30
David Halter
cf49bdc9ab merge of tokenizer/dev branch 2013-04-20 19:23:15 +04:30
David Halter
336d35eb71 Merge pull request #203 from davidhalter/tokenizer
Tokenizer
2013-04-20 03:14:19 -07:00
David Halter
d27dc0d58e remove py2.5 from travis - this also removes python 2.5 support 2013-04-20 14:37:18 +04:30
David Halter
8c56fba1e9 fix a problem with statements without parents 2013-04-20 14:12:15 +04:30
David Halter
a7308fa51d removed more unused code 2013-04-20 14:11:46 +04:30
David Halter
d4428c497a use tokenizer instead of tokenize in all modules 2013-04-20 09:52:54 +04:30
David Halter
e2a6562384 tokenizer: removed raised errors 2013-04-20 09:51:52 +04:30
David Halter
0d385563a5 tokenizer: removed encoding 2013-04-20 09:28:32 +04:30
David Halter
6b2619a844 tokenizer removed unused functions 2013-04-20 09:23:27 +04:30
David Halter
06da7ddeec tokenizer py27 support 2013-04-20 09:21:22 +04:30
David Halter
fa53579950 Merge branch 'dev' into tokenizer 2013-04-20 08:55:02 +04:30
David Halter
3b0f4b87cf removed unnecessary stuff for the jedi tokenizer 2013-04-19 23:59:33 +04:30
David Halter
a28bc7195f added tokenizer of the standard library to jedi, because in the future it shall replace tokenize 2013-04-19 23:50:28 +04:30
Danilo Bargen
36614fdb27 Merge pull request #202 from davidhalter/ignored_contextmanager
New `with ignored(*exceptions)` context manager
2013-04-17 14:45:08 -07:00
Danilo Bargen
f98b0d7b6f New "with ignored(*exceptions)" context manager 2013-04-17 23:28:37 +02:00
David Halter
4e27e7d335 fixed a positioning bug in the own tokenizer 2013-04-17 23:37:21 +04:30
David Halter
d82f315ea6 don't use SubModule's in the scope list if it has a parent 2013-04-17 13:39:02 +04:30
David Halter
9181410c47 fixed a problem with functions in the beginning of a module 2013-04-17 10:07:15 +04:30
Danilo Bargen
24573c5ef2 Extended testing information in README 2013-04-16 22:51:41 +02:00
David Halter
188ed33c4f fixed parser change problems 2013-04-15 11:50:05 +04:30
David Halter
b08390c136 some parser end positions changed 2013-04-15 10:22:27 +04:30
David Halter
bafb17001b fix encoding problems with terminal 2013-04-13 15:57:28 +04:30
David Halter
eeb8e0e21d first test working again with new fast_parser 2013-04-13 13:50:50 +04:30
Danilo Bargen
fb96f9bf3d Missing semicolon in .travis.yml 2013-04-10 23:52:57 +02:00
Danilo Bargen
1a41f92a0d Added .coveragerc (refs #196) 2013-04-10 23:41:25 +02:00
Danilo Bargen
26cabd581c Added coverage report to README 2013-04-10 22:20:37 +02:00
Danilo Bargen
0b4e8e09a3 Merge pull request #198 from tkf/coveralls
Run coveralls task separately on travis-ci (fixes #196, refs #197)
2013-04-10 13:12:36 -07:00
David Halter
967d01d490 fast parser produces now trees that are correct 2013-04-10 23:50:39 +04:30
David Halter
5dd05eff1a a basic approach to the new fast parser 2013-04-10 22:33:49 +04:30
Takafumi Arakaki
944b3200a0 Fix allow_failures syntax
allow_failures.env requires a string when matrix/global is not used?
Also, indent is changed to fit with other entries.
2013-04-10 17:28:15 +02:00
Takafumi Arakaki
658bcb6421 Install coveralls only when needed 2013-04-10 17:23:50 +02:00
Takafumi Arakaki
8e4a76ca1b Allow TOXENV=cov to fail in travis-ci 2013-04-10 17:09:36 +02:00
Takafumi Arakaki
af22409059 Separate tox env for coverage check 2013-04-10 17:08:20 +02:00
Danilo Bargen
798136787f Configuration to run coveralls on py33 2013-04-10 16:30:32 +02:00
Danilo Bargen
be7cecef00 Typos in docs 2013-04-10 16:04:56 +02:00
Danilo Bargen
327a1ded17 Note about testing in README 2013-04-10 15:47:44 +02:00
Danilo Bargen
492a6c5704 Added specific "Programming Language" trove classifiers (fixes #194) 2013-04-05 11:18:53 +02:00
David Halter
3c73a744c3 Merge pull request #187 from Astrac/python-3.3
Python 3.3
2013-03-27 10:21:15 -07:00
Aldo Stracquadanio
124595de6e Making it nicer
Fixed typo in docstring and added some comments in find_module_py33

Removed a test that is not compatible with python 3.3

Better variable names in find_module implementation(s)

Removed variable assignation in favor of direct return statement
2013-03-27 10:49:43 +00:00
Aldo Stracquadanio
0b67a08e48 Using PathFinder rather than find_loader to correctly handle paths
Using PathFinder rather than find_loader to correctly handle from ... import ...

Moved away from find_loader in favour of PathFinder also when using sys.path
2013-03-27 10:49:21 +00:00
Aldo Stracquadanio
07ec134bc9 Adding python 3.3 to test environment, mani fixes
Added python 3.3 to test-suite

Removed unused import

Removed unused import

Migrated to EAFP for attribute checking

Bumped version of ModulePickling for migration to hashlib

Added py33 environment to tox

Fixed issue with package importing on python 3.3
2013-03-27 10:48:56 +00:00
Aldo Stracquadanio
3ef564847d Simplified code for readability
Splitted import compatibility function definition for better readability

Simplified code for python 3.3 load_module implementation
2013-03-27 10:48:26 +00:00
Aldo Stracquadanio
be8ef33b69 Fixed caching issues
Fixed exception raised during cache reading

Switched cache hashing to hashlib

In python 3.3 the hash function is returning different hashes during
different executions of the application.
2013-03-27 10:48:13 +00:00
Aldo Stracquadanio
de849fbe8a Fixed follow_definition test 2013-03-27 10:48:12 +00:00
Aldo Stracquadanio
d481a7aae4 Created find_module helper to handle compatibility with python 3.3
Moved package checking logic in follow_str function

Created find_module compatibility helper method

Conditional implementation of load_module for python 3.3
2013-03-27 10:47:29 +00:00
David Halter
a99d9541bd on the way to a better fast_parser - improved a lot of the positioning stuff 2013-03-24 22:51:17 +04:30
David Halter
6c998067e8 Merge pull request #177 from tkf/Generator-object-has-no-attribute-start_pos
Generator object has no attribute start_pos (added a failing test)
2013-03-21 11:06:21 -07:00
David Halter
6c65eaf14d Merge pull request #186 from tkf/versioned-cache
Remove old version of FS cache automatically
2013-03-20 09:05:37 -07:00
Takafumi Arakaki
aca1cbfa4b Use "jedi/cpython-27" instead of "jedi/2.7" for FS cache 2013-03-20 16:46:55 +01:00
Takafumi Arakaki
27d007adba Compare version using "!="
Cache is not forward compatible.
2013-03-20 12:21:22 +01:00
Takafumi Arakaki
942b66ba5b Separate directory for each Python version 2013-03-20 12:21:22 +01:00
Takafumi Arakaki
f8d3339f2f Remove old version of FS cache automatically 2013-03-20 12:21:22 +01:00
David Halter
960dd38e2c Merge pull request #185 from tkf/cPickle
Use cPickle if available
2013-03-19 21:32:22 -07:00
Takafumi Arakaki
8734e0efe2 Use cPickle if available 2013-03-19 20:33:15 +01:00
David Halter
09f39d891a remove __all__ from api, because not needed, fixes #180 2013-03-18 08:25:14 +04:30
Takafumi Arakaki
d1150cfdae List allowed attributes in Generator.__getattr__ 2013-03-17 13:56:54 +01:00
David Halter
8c1ccd2800 Merge pull request #182 from tkf/Fix-Completion.follow_definition
Fix Completion.follow_definition
2013-03-17 03:48:22 -07:00
David Halter
e8d34c574f Merge pull request #178 from tkf/no-duplicate-module
Add a test to make sure that the import hack works
2013-03-17 03:39:45 -07:00
David Halter
66fb7915c2 Merge pull request #179 from tkf/remove-known-failures-tag
Remove tags for known failures due to the import hack
2013-03-17 03:35:55 -07:00
Takafumi Arakaki
2cc6edfa7c Fix Completion.follow_definition 2013-03-16 22:57:31 +01:00
Takafumi Arakaki
6a2e535bcb Add er.Generator.__getattr__ 2013-03-16 21:50:05 +01:00
Takafumi Arakaki
c25ee531a2 AttributeError: 'Generator' object has no attribute 'start_pos'
Running py.test raises this error:
```tb
test/test_api_classes.py:50: in <module>
>   @pytest.mark.parametrize('definition', make_definitions())
test/test_api_classes.py:38: in make_definitions
>       definitions += script.definition()
jedi/api_classes.py:44: in wrapper
>       result = func(*args, **kwds)
jedi/api.py:274: in definition
>                   if not isinstance(s, imports.ImportPath._GlobalNamespace)])
jedi/api_classes.py:418: in __init__
>       super(Definition, self).__init__(definition, definition.start_pos)
E       AttributeError: 'Generator' object has no attribute 'start_pos'
```
2013-03-16 21:50:05 +01:00
Takafumi Arakaki
febc65be6f Skip test_no_duplicate_modules for jedi.__init__ 2013-03-16 21:28:27 +01:00
Takafumi Arakaki
4aac06eb86 Remove tags for known failures due to the import hack 2013-03-16 21:18:37 +01:00
Takafumi Arakaki
f5fee5f0df Fix wrong imports
- "from _compatibility import ..." (not in circular imports)
- "from jedi import builtin" (one of circular imports)
- "api_classes = api.api_classes"
  ("from jedi import api_classes" is not supported)
2013-03-16 21:03:34 +01:00
Takafumi Arakaki
07ba6658dd Add test_no_duplicate_modules 2013-03-16 20:44:00 +01:00
David Halter
df058b93c2 improve get_code of parsing_representation scopes 2013-03-16 23:26:20 +04:30
David Halter
841b0be19a improved parents in fast parser 2013-03-15 23:03:59 +04:30
David Halter
cd513590ab Merge pull request #176 from tkf/basedefinition-type
More detailed BaseDefinition.type (fixes #169)
2013-03-15 10:36:53 -07:00
David Halter
89b6741539 Merge pull request #175 from davidhalter/absolute-import
Use Absolute Imports
2013-03-15 10:35:28 -07:00
David Halter
8be6b64bc7 add @tkf to main authors, concrats man - you earned it - more than enough. 2013-03-15 21:01:44 +04:30
Takafumi Arakaki
df08122639 Fix a failure when run with Python 3 2013-03-15 14:51:59 +01:00
Takafumi Arakaki
be33f0ad98 Fix previously failing test_follow_definition 2013-03-15 14:39:51 +01:00
Takafumi Arakaki
c8c1113c55 Test generator and lambda 2013-03-15 14:26:58 +01:00
Takafumi Arakaki
a0c796087a Generate "param" definition in make_definitions 2013-03-15 14:24:42 +01:00
Takafumi Arakaki
956ad50276 Make more examples in make_definitions 2013-03-15 14:13:55 +01:00
Takafumi Arakaki
f791e96d9d Fix failing test_basedefinition_type 2013-03-15 13:54:19 +01:00
Takafumi Arakaki
3caebcb5aa Add test_basedefinition_type 2013-03-15 13:54:17 +01:00
Takafumi Arakaki
f70e425c4a Lowercase the str returned by BaseDefinition.type 2013-03-15 13:08:55 +01:00
David Halter
8c51d1e53e increase speed test limit a little bit, because it fails sometimes on travis 2013-03-15 16:31:35 +04:30
David Halter
7b6a4c9a2b some corrections of imports 2013-03-15 16:01:23 +04:30
David Halter
9ad9c806bb rename non-cycling imports to 'from jedi import ..' 2013-03-15 15:02:33 +04:30
David Halter
b426835d18 parser preparation for the new fast_parser 2013-03-15 13:57:23 +04:30
David Halter
2f809d8066 Merge pull request #172 from tkf/test_modulepickling_change_cache_dir
ModulePickling should not save old cache when cache_directory is changed
2013-03-15 02:25:10 -07:00
David Halter
26eb7add23 Merge pull request #173 from tkf/gc-and-cache
Do not clear cache in __del__
2013-03-14 21:11:39 -07:00
David Halter
952b510c24 Merge pull request #171 from tkf/norecursedirs-fix
Use pytest_(un)configure to setup cache_directory
2013-03-14 21:01:36 -07:00
Takafumi Arakaki
6659a532ac Remove --assert=plain 2013-03-15 00:25:10 +01:00
Takafumi Arakaki
7cf70a3f0a Do not clear cache in __del__
Prior to this change, running
`py.test --assert=rewrite test/test_integration.py` fails with errors
due to:

```py
cls = <class 'jedi.recursion.ExecutionRecursionDecorator'>

    @classmethod
    def cleanup(cls):
>       cls.parent_execution_funcs.pop()
E       IndexError: pop from empty list

recursion.py:127: IndexError
```

Similar errors occurred in travis occasionally:
- https://travis-ci.org/davidhalter/jedi/jobs/5449831
- https://travis-ci.org/davidhalter/jedi/jobs/5512538

I think this is because GC calls __del__ at random point during
actual execution of ExecutionRecursionDecorator.__call__.
As --assert=rewrite works by AST dynamically, I guess that it
is harder for Python's GC to clean up code and therefore GC
happens sometime later.  Although this is just a random guess,
as `tox -- --assert=rewrite` works with this patch now, I think
this is a good change.
2013-03-15 00:21:46 +01:00
David Halter
b5ad56d116 new part spliting of strings 2013-03-15 02:15:45 +04:30
Takafumi Arakaki
841d46684f Skip test in Python 3. It is a known bug.
See: #161
2013-03-14 22:21:58 +01:00
Takafumi Arakaki
edc7148320 Fix test_modulepickling_change_cache_dir failure 2013-03-14 22:15:34 +01:00
Takafumi Arakaki
860cf7b974 Add test_modulepickling_change_cache_dir 2013-03-14 22:15:07 +01:00
Takafumi Arakaki
ee03f3ae0d Use pytest_(un)configure to setup cache_directory 2013-03-14 20:43:10 +01:00
David Halter
22fa66b6e1 Merge pull request #168 from tkf/norecursedirs
Run doctests in the "main" py.test run
2013-03-14 12:09:57 -07:00
Takafumi Arakaki
95aed3c54e Exclude docs directory from test search 2013-03-14 19:20:20 +01:00
David Halter
fbbdc50bb0 Merge pull request #164 from tkf/doc-api-classes
Document api_classes
2013-03-14 09:20:38 -07:00
David Halter
6d0c5bbb80 Merge pull request #167 from tkf/simplify-travis-yml
Simplify .travis.yml
2013-03-14 08:02:03 -07:00
Takafumi Arakaki
3881f8d6ed Do not collect setup.py as a test 2013-03-14 14:31:32 +01:00
Takafumi Arakaki
fbccdd3f70 Run doctests in the main py.test run 2013-03-14 14:15:00 +01:00
Takafumi Arakaki
3d647d3f3d Use --quiet when installing tox 2013-03-14 10:35:20 +01:00
Takafumi Arakaki
191e8f690b Simplify .travis.yml 2013-03-14 10:33:28 +01:00
David Halter
25d55d4124 Merge pull request #165 from tkf/hide-api_classes-defined_names
Hide api_classes.defined_names in document
2013-03-13 21:44:54 -07:00
David Halter
5f9aa5506f Merge pull request #166 from tkf/rethrow_uncaught
Better way of re-raising AttributeError
2013-03-13 21:43:14 -07:00
Takafumi Arakaki
7a9e374a65 Use original exception value when reraise 2013-03-13 23:54:19 +01:00
Takafumi Arakaki
93bd00bba4 Document rethrow_uncaught/reraise 2013-03-13 23:50:40 +01:00
Takafumi Arakaki
2b89dda5a6 Use reraise when re-raising MultiLevelStopIteration 2013-03-13 23:28:53 +01:00
Takafumi Arakaki
3b78b52204 Rename MultiLevelAttributeErro to UncaughtAttributeError 2013-03-13 23:24:26 +01:00
Takafumi Arakaki
eca0f01cfb Use @common.rethrow_uncaught and stop manual re-raise 2013-03-13 23:21:49 +01:00
Takafumi Arakaki
e42ff9e762 Add common.rethrow_uncaught 2013-03-13 23:21:15 +01:00
Takafumi Arakaki
dccda224ab Hide api_classes.defined_names 2013-03-13 22:18:50 +01:00
Takafumi Arakaki
8d54ebea7b Fix BaseDefinition.module_name 2013-03-13 22:05:59 +01:00
Takafumi Arakaki
125a3b1a6b Document BaseDefinition.module_name 2013-03-13 22:05:31 +01:00
Takafumi Arakaki
2f7797f867 Ignore known failure in BaseDefinition.description doctest
See: #162
2013-03-13 21:44:55 +01:00
Takafumi Arakaki
a449428391 Document BaseDefinition.full_name 2013-03-13 20:30:36 +01:00
Takafumi Arakaki
9e3e6a2eea Document BaseDefinition.description 2013-03-13 20:29:02 +01:00
Takafumi Arakaki
41b33fd460 Document BaseDefinition.doc 2013-03-13 20:11:14 +01:00
Takafumi Arakaki
f9ed3d42f7 Document BaseDefinition.type 2013-03-13 20:01:43 +01:00
David Halter
74a9501bd7 Merge pull request #159 from tkf/fix-doc-import-error
Fix ImportError when building Sphinx
2013-03-12 11:50:49 -07:00
Takafumi Arakaki
c8c26f52b9 Fix ImportError when building Sphinx 2013-03-12 19:35:20 +01:00
David Halter
43ff06d7ea Merge pull request #157 from tkf/thirdparty-test-import-check
Automatically skip thirdparty test if not importable
2013-03-12 07:23:21 -07:00
David Halter
a8218dee54 Merge pull request #156 from tkf/fix-get_sys_path
Make VIRTUAL_ENV actually importable
2013-03-12 07:21:50 -07:00
Takafumi Arakaki
ab5266b840 Automatically skip thirdparty test if not importable 2013-03-12 13:51:33 +01:00
Takafumi Arakaki
4866b38bda Make VIRTUAL_ENV actually importable 2013-03-12 13:08:47 +01:00
David Halter
6c71fad802 Merge pull request #155 from tkf/migrate-to-pytest
Migrate to pytest
2013-03-12 03:59:16 -07:00
Takafumi Arakaki
e5b660b0a1 Use PIP_INSECURE=t only in Python 2.5 test 2013-03-12 11:33:01 +01:00
Takafumi Arakaki
de7092d56b Fix: --thirdparty was not considered 2013-03-12 11:26:58 +01:00
Takafumi Arakaki
c87d3dad52 Remove unused imports 2013-03-12 11:04:15 +01:00
Takafumi Arakaki
c387bf06bc No need to setup import path in test.base
py.test does that for us.
2013-03-12 11:03:12 +01:00
Takafumi Arakaki
446c7cf694 Document how to run test 2013-03-12 10:36:42 +01:00
Takafumi Arakaki
1fffbf13ca Fix test failures because imports.py uses base.py 2013-03-12 10:18:18 +01:00
Takafumi Arakaki
b5764c1446 Remove old utility functions in test.base 2013-03-12 10:11:26 +01:00
Takafumi Arakaki
ff80988a75 Remove old test code 2013-03-12 10:02:19 +01:00
Takafumi Arakaki
49f635dca3 Add a failing test due to import hack 2013-03-12 10:01:57 +01:00
Takafumi Arakaki
7c289ce6be Workaround test failure due to cache in Python 3.2 2013-03-12 09:59:30 +01:00
Takafumi Arakaki
cee167e3d2 Run py.test in clean cache directory
And finally remove XDG_CACHE_HOME=... in tox.ini.
2013-03-12 09:30:13 +01:00
Takafumi Arakaki
5c3252908f Use PIP_INSECURE=t 2013-03-12 09:29:00 +01:00
Takafumi Arakaki
ab33400f76 Install libbluetooth-dev for Python 2.5 2013-03-12 09:21:30 +01:00
Takafumi Arakaki
135dd56e61 Install libssl-dev for Python 2.5 2013-03-12 09:15:30 +01:00
Takafumi Arakaki
9e600ed0b1 Use --insecure when running pip with Python 2.5 2013-03-12 09:04:56 +01:00
Takafumi Arakaki
e7b352b826 Install ssl in .travis.yml 2013-03-12 09:01:56 +01:00
Takafumi Arakaki
674743b7bb Avoid NoSSLError in Python 2.5 2013-03-12 08:49:24 +01:00
Takafumi Arakaki
88adcbcf8a Use tox in .travis.yml 2013-03-12 08:03:55 +01:00
Takafumi Arakaki
13b48632e5 Better assertion message formatter 2013-03-12 07:58:40 +01:00
Takafumi Arakaki
71bb93224d Ignore first N failures in Python 2.5 2013-03-12 07:48:20 +01:00
Takafumi Arakaki
180d0a8764 Rename regression.py to test_regression.py
in order to let py.test collect the tests.
2013-03-12 07:12:44 +01:00
Takafumi Arakaki
0f9761aac7 Fix tests for Python 3.2 (use relative import) 2013-03-12 07:11:37 +01:00
Takafumi Arakaki
51a094be02 Run py.test in tox.ini 2013-03-12 07:08:52 +01:00
Takafumi Arakaki
a993dd0da4 Fix test_refactor
It was not run because test cases were collected from test/complete
instead of test/refactor.
2013-03-12 07:00:44 +01:00
Takafumi Arakaki
a31ba8737a Run refactoring test using py.test
refactor.collect_file_tests is fixed;  it uses global variable
refactoring_test_dir which is not defined when refactor is used
as a module.
2013-03-12 06:50:03 +01:00
David Halter
fb0b8b0fc1 Merge pull request #149 from tkf/definition-in-call
Fallback to callee definition when definition not found (fixes #131)
2013-03-10 21:14:15 -07:00
David Halter
124fadae28 Merge pull request #154 from tkf/refactor-refactoring-test
Refactor test/refactor.py
2013-03-10 21:07:01 -07:00
David Halter
d08b135363 Merge pull request #153 from tkf/pytest
Use py.test
2013-03-10 21:04:10 -07:00
Takafumi Arakaki
9791f04555 Reduce args for RefactoringCase 2013-03-10 23:06:42 +01:00
Takafumi Arakaki
2362e290d8 Separate methods in RefactoringCase 2013-03-10 23:06:41 +01:00
Takafumi Arakaki
0a13b8f114 Separate test collection and run in refactoring.py 2013-03-10 23:06:39 +01:00
Takafumi Arakaki
50e1fad8f6 Remove tests from regression.py 2013-03-10 21:29:09 +01:00
Takafumi Arakaki
fa231b58a8 Add more test cases in completion/definition.py 2013-03-10 21:29:09 +01:00
Takafumi Arakaki
5f49fb8c7c Add blackbox tests using column number 2013-03-10 21:29:09 +01:00
Takafumi Arakaki
31df9a92fe Add test/completion/definition.py 2013-03-10 21:29:09 +01:00
Takafumi Arakaki
c6c17d7ed5 Add known failing test for function_definition 2013-03-10 21:29:09 +01:00
Takafumi Arakaki
ab79689cee Use unittest2 for Python < 2.7 2013-03-10 21:28:27 +01:00
Takafumi Arakaki
3542b7f80a Refactor tests for function_definition 2013-03-10 21:27:34 +01:00
Takafumi Arakaki
0e99c33c67 Don't use NotFoundError in _func_call_and_param_index
This makes it simpler.
2013-03-10 21:27:34 +01:00
Takafumi Arakaki
7ce0e1c178 Rename it to _func_call_and_param_index 2013-03-10 21:27:34 +01:00
Takafumi Arakaki
d9b3bb4016 Refactor _get_function_call_and_param_index_at_point 2013-03-10 21:27:34 +01:00
Takafumi Arakaki
4d6abfb8de Fix a failing test in Python 3 2013-03-10 21:27:34 +01:00
Takafumi Arakaki
1eae32c182 Skip test case for finding definition at f( |) 2013-03-10 21:27:34 +01:00
Takafumi Arakaki
c546323bca Tests for f(| ) and f( |) (the latter fails) 2013-03-10 21:27:34 +01:00
Takafumi Arakaki
9490730d59 Treat case like f() 2013-03-10 21:27:34 +01:00
Takafumi Arakaki
8f3db6976b Fix test for issue #94 2013-03-10 21:27:34 +01:00
Takafumi Arakaki
b8e3e0c157 Add tests for Script.definition when in function call 2013-03-10 21:27:34 +01:00
Takafumi Arakaki
a25aa884d6 Make Script.definition work inside function call 2013-03-10 21:27:34 +01:00
Takafumi Arakaki
0a09d8a813 Refactor function_definition 2013-03-10 21:27:34 +01:00
David Halter
d063aa3d66 Merge pull request #141 from tkf/attribute-docstring
Attribute docstring support
2013-03-10 13:23:47 -07:00
David Halter
91ee75d5a2 Merge pull request #152 from tkf/refactor-integration-test
Refactor integration test runner
2013-03-10 13:07:34 -07:00
David Halter
6ae2d8cfeb Merge pull request #130 from tkf/list_definitions
Add new API: jedi.api.get_definitions
2013-03-10 12:56:08 -07:00
Takafumi Arakaki
871ce5ad33 Compatibility fix for Python 2.5 2013-03-10 20:44:15 +01:00
Takafumi Arakaki
a02940f3c1 Rename api.get_definitions to defined_names
Do the same for Definition.get_definitions also.
2013-03-10 20:40:52 +01:00
Takafumi Arakaki
36f03f4b0d Use the first item returned by get_names_of_scope 2013-03-10 20:40:52 +01:00
Takafumi Arakaki
06de4d66e7 Revert "Add get_names_of_scope option to evaluate.get_names_of_scope"
This reverts commit 154662ea273a17f00186546bdf9d75833553d307.
2013-03-10 20:40:52 +01:00
Takafumi Arakaki
d4bf14dd66 Use list comprehension instead of map 2013-03-10 20:40:52 +01:00
Takafumi Arakaki
bc32844bd7 Add TestGetDefinitions.test_dotted_assignment 2013-03-10 20:40:52 +01:00
Takafumi Arakaki
5a29fecfb2 Fix Python 3 syntax error 2013-03-10 20:40:52 +01:00
Takafumi Arakaki
71d5b0ac67 Document return type of get_names_of_scope 2013-03-10 20:40:52 +01:00
Takafumi Arakaki
87714460a2 Add get_names_of_scope option to evaluate.get_names_of_scope
Previously failing test passes now.
2013-03-10 20:40:14 +01:00
Takafumi Arakaki
3fbe7e9ff4 Rewrite get_definitions using evaluate.get_names_of_scope
- Definition.names is changed to Definition.name.
- TestGetDefinitions.test_nested_definitions fails.
2013-03-10 20:40:14 +01:00
Takafumi Arakaki
1eccf2f765 Make sure get_definitions returns list in Python 3 2013-03-10 20:40:14 +01:00
Takafumi Arakaki
89bfdd2b2d Test that full_name works with get_definitions 2013-03-10 20:40:14 +01:00
Takafumi Arakaki
08f45d1f95 Fix Definition.get_definitions 2013-03-10 20:40:14 +01:00
Takafumi Arakaki
0f67b3e0c2 More robust Definition.names 2013-03-10 20:40:14 +01:00
Takafumi Arakaki
d0ad14adf4 Fix Definition.names for star import 2013-03-10 20:40:14 +01:00
Takafumi Arakaki
ba7d29f542 Add a test for multiple assignment 2013-03-10 20:40:14 +01:00
Takafumi Arakaki
cbcaa85108 Add :attr:Definition.names 2013-03-10 20:40:14 +01:00
Takafumi Arakaki
39feecee04 Add new API: jedi.api.get_definitions 2013-03-10 20:40:14 +01:00
Takafumi Arakaki
c3aaf7b4ba Show line_nr of test comment, not the line after 2013-03-10 20:26:09 +01:00
Takafumi Arakaki
59030daa60 Add --test-files option to py.test
At this point, py.test should be equivalent to test/run.py
2013-03-10 19:00:46 +01:00
Takafumi Arakaki
9018cea22e Rewrite integration test using py.test 2013-03-10 19:00:44 +01:00
Takafumi Arakaki
3b41a47c08 Re-add sys.path setup in test/base.py 2013-03-10 18:58:32 +01:00
Takafumi Arakaki
20c9709aef Do not change cwd at import time 2013-03-10 17:28:48 +01:00
Takafumi Arakaki
00912e69fe Completely separate test collection and run 2013-03-10 16:34:54 +01:00
Takafumi Arakaki
6a10f79551 Do not destruct test case in run_test 2013-03-10 14:24:05 +01:00
Takafumi Arakaki
418bce909a run_{test_type} functions takes same args now 2013-03-10 13:16:05 +01:00
Takafumi Arakaki
932ce397d6 Separate test collection and run 2013-03-10 12:21:12 +01:00
David Halter
dac25ea342 Merge pull request #148 from tkf/docstring-improvements
Improve docstrings
2013-03-03 20:18:58 -08:00
Takafumi Arakaki
8d486ff9aa Make doctest Python 3.x compatible 2013-03-04 02:07:47 +01:00
Takafumi Arakaki
0c6f7f66e9 Document get_names_of_scope with examples 2013-03-03 15:49:49 +01:00
Takafumi Arakaki
11707ffbbb Document evaluate.get_defined_names_for_position 2013-03-03 15:29:26 +01:00
Takafumi Arakaki
8e85adb718 Document Scope.get_defined_names 2013-03-03 15:19:34 +01:00
Takafumi Arakaki
d256c5470c Add a very short tutorial on parsing_representation 2013-03-03 14:55:24 +01:00
Takafumi Arakaki
99ab2dec15 Document pr.SubModule 2013-03-03 14:45:07 +01:00
Takafumi Arakaki
77e8b2aecd Remove a philosophical (and confusing) statement 2013-03-03 14:18:58 +01:00
Takafumi Arakaki
ac159bac07 Document pr.Simple.__init__ 2013-03-03 14:17:16 +01:00
Takafumi Arakaki
dc9a269d35 Use literal block instead of >>> for code example 2013-03-03 14:07:48 +01:00
Takafumi Arakaki
700493cac8 Fix and improve Statement.__doc__
- The type of `set_vars` and `used_vars` were wrong.
- ":param ...:" was used instead of ":type ...:".
- The parameter table is aligned to make it easier to read.
2013-03-03 14:07:07 +01:00
David Halter
90c800b257 remove repl style comments and use indented blocks instead, #146 2013-03-01 19:52:36 +04:30
David Halter
c98d099001 typos 2013-03-01 00:50:20 +04:30
David Halter
ee6a261b42 Merge branch 'dev' of github.com:davidhalter/jedi into dev 2013-03-01 00:35:18 +04:30
David Halter
e3b33fc009 fixed a unicode problem caused by a wrongly placed docstring 2013-03-01 00:32:54 +04:30
David Halter
68a9cecb27 improved run test documentation 2013-03-01 00:31:51 +04:30
David Halter
c808bcef70 little description of regression and refactor 2013-03-01 00:23:13 +04:30
David Halter
669dfda419 run documentation improved 2013-03-01 00:19:05 +04:30
David Halter
855d683d9a switched to __name__ == '__main__' checks for tests 2013-03-01 00:07:36 +04:30
David Halter
1834ef51b1 basic testing docstring for run.py 2013-02-28 23:57:33 +04:30
David Halter
76bd1a2d71 add testing docs to sphinx 2013-02-28 23:19:53 +04:30
David Halter
ffbc7df68d Merge pull request #146 from tkf/doctest
Add doctest
2013-02-26 03:46:08 -08:00
Takafumi Arakaki
9ba9e2c3a6 Make doctest work on Python 2.5 2013-02-26 10:58:24 +01:00
Takafumi Arakaki
6d2f7558fb Run doctests on Travis CI 2013-02-26 10:45:58 +01:00
Takafumi Arakaki
b56c212a2c Fix renamed functions in docstring 2013-02-26 10:36:29 +01:00
Takafumi Arakaki
95a98c9033 Do not use >>> for non-doctest code example
Use literal block instead.
2013-02-26 10:36:08 +01:00
Takafumi Arakaki
dad9cf0518 Print str to avoid u prefix 2013-02-26 10:28:25 +01:00
Takafumi Arakaki
668be37089 Use nose to run doctests 2013-02-26 10:25:16 +01:00
David Halter
20e5a6adea Merge pull request #144 from tkf/class-call-signature
Include __init__ call signature in Class.doc
2013-02-25 05:41:54 -08:00
David Halter
69cc5020be Merge pull request #143 from tkf/call-signature-test
Add a test for docstring in call signature
2013-02-24 20:38:37 -08:00
Takafumi Arakaki
38fc49022f Include __init__ call signature in Class.doc 2013-02-25 01:50:31 +01:00
Takafumi Arakaki
4ba9fd2b68 Fix Statement.get_code
Parsed Statement.assignment_details must be used.  Otherwise,
incorrect code is returned at the first time Statement.get_code
is called.
2013-02-25 01:43:22 +01:00
Takafumi Arakaki
e1e2ed8fcc Add a test for docstring in call signature 2013-02-24 22:45:10 +01:00
Danilo Bargen
3dcad981a5 Merge pull request #142 from tkf/doc-inheritance-diagram
Add inheritance-diagram to Sphinx documentation
2013-02-24 13:44:13 -08:00
Takafumi Arakaki
c911255d6e Add inheritance-diagram to Sphinx documentation 2013-02-24 21:20:00 +01:00
Takafumi Arakaki
7f00b9414e Check the case where multiple docstrings are defined 2013-02-24 20:58:43 +01:00
Takafumi Arakaki
aad9c34db6 Include statements to Script.definition if it has docstring 2013-02-24 20:56:09 +01:00
Takafumi Arakaki
e458b35f1c Unconditionally set attribute docstrings
As Statement.assignment_details triggers parsing inside
statement let's don't check assignment_details when setting
attribute docstrings.
2013-02-24 19:45:47 +01:00
Takafumi Arakaki
540eff3d42 Add test_attribute_docstring 2013-02-24 19:39:59 +01:00
Takafumi Arakaki
b6839fb264 Define Name.docstr as a proxy for Statement.docstr 2013-02-24 19:39:46 +01:00
Takafumi Arakaki
740f27c8b8 Add attribute docstrings (PEP 257) support 2013-02-24 19:39:22 +01:00
Takafumi Arakaki
19b41c1cb4 Make pr.Statement "document-able" 2013-02-24 19:36:39 +01:00
Takafumi Arakaki
524feca0de Add TestDocstring 2013-02-24 17:40:18 +01:00
David Halter
d9ac630633 Merge pull request #140 from tkf/tox-setenv
Set XDG_CACHE_HOME in tox to avoid contaminating ~/.cache
2013-02-24 06:35:13 -08:00
David Halter
bbb1c463b4 Merge pull request #139 from tkf/tox-25
Fix setup.py and make 2.5 test runnable with tox
2013-02-24 06:33:46 -08:00
Takafumi Arakaki
679cd05acf Set XDG_CACHE_HOME in tox to avoid contaminating ~/.cache 2013-02-24 15:01:39 +01:00
Takafumi Arakaki
d9ca833814 Make 2.5 test runnable with tox 2013-02-24 14:56:15 +01:00
David Halter
68ace0d05b dynamic docstring improvements 2013-02-23 22:21:34 +04:30
David Halter
e8feb0b7d2 dynamic docstring 2013-02-23 22:15:17 +04:30
David Halter
4ee4e225a6 don't encode strings in python3, affects davidhalter/jedi-vim#94 and @dbrgn 2013-02-23 21:10:22 +04:30
David Halter
6ea64a28bf dev/refactor merge 2013-02-23 20:53:57 +04:30
David Halter
adc7a559cf python3.2 compatibility 2013-02-23 08:58:15 +04:30
David Halter
6ab10b6fa1 pep8 2013-02-23 01:16:02 +04:30
David Halter
4d7aad4ce8 deleted old crap code 2013-02-23 01:13:21 +04:30
David Halter
6912764923 fix position problems which came with lambdas 2013-02-23 00:40:34 +04:30
David Halter
7f051087e5 basic lambda implementation 2013-02-22 23:38:06 +04:30
David Halter
c1e805d7b0 fixed invalid list comprehension tests 2013-02-22 23:06:59 +04:30
David Halter
2fda713118 basic listcomprehension/lambda move 2013-02-22 23:02:44 +04:30
David Halter
b0c3fd5439 api.Script.get_definition -> definition, to be consistent in the api naming. deprecated api.Script.get_definition 2013-02-21 22:12:51 +04:30
David Halter
f8e0c78f77 Script.get_in_function_call -> Script.function_definition 2013-02-21 22:05:39 +04:30
David Halter
923e59b9c2 fixed position problems (with tokenizer offsets) 2013-02-21 21:45:39 +04:30
David Halter
80423ddb57 todo remove 2013-02-21 21:38:31 +04:30
David Halter
9690cf3eaf removed merged Statement.used_funcs and used_vars (made no sense) 2013-02-21 21:30:57 +04:30
David Halter
774c191d7e removed code part of Statement 2013-02-21 21:26:22 +04:30
David Halter
0084b9f04d little cleanup, removed old unused code 2013-02-21 20:57:05 +04:30
David Halter
2b174ecf2c simplify and fix refactoring: extract 2013-02-21 20:42:06 +04:30
David Halter
98ee2d9675 fixed refactoring: inline 2013-02-21 20:23:19 +04:30
David Halter
78fd8372a5 refactoring exception fixes 2013-02-21 20:02:25 +04:30
David Halter
d05018757d completely rewrote helpers.search_function_definition 2013-02-21 19:56:05 +04:30
David Halter
9fa0b9f924 fix rest of rename tests 2013-02-21 01:45:25 +04:30
David Halter
6b5295bc40 also fixed nested list comprehensions 2013-02-21 01:39:49 +04:30
David Halter
4d3267b24f fix list comprehension problems for non-nested 2013-02-21 01:13:23 +04:30
David Halter
9cda8c2a52 fix list comprehensions recursion problems 2013-02-20 23:45:40 +04:30
David Halter
fad4cd6c13 fix some other array parts 2013-02-20 23:38:33 +04:30
David Halter
f2cdf03e6e fixed dict problems 2013-02-20 22:44:02 +04:30
David Halter
06a54f30a8 some renaming fixes 2013-02-19 00:32:47 +04:30
David Halter
1b7fc1ee50 fixes for goto 2013-02-19 00:21:04 +04:30
David Halter
926ab81bf2 some import fixes 2013-02-19 00:11:15 +04:30
David Halter
3ddd371310 fixed isinstance checks 2013-02-19 00:02:05 +04:30
David Halter
b1825621ff fixed the rest of the dynamic problems 2013-02-18 23:49:28 +04:30
David Halter
c3fe5d04dd line_offset to offset in Parser, which is important for exact positioning 2013-02-18 23:17:41 +04:30
David Halter
b2aa8b8024 get_parent_until for ListComprehension 2013-02-18 20:52:07 +04:30
David Halter
30fa504457 fix some more dynamic problems 2013-02-18 20:41:27 +04:30
David Halter
4e102e25e6 fix some of the dynamic bugs 2013-02-17 23:23:57 +04:30
David Halter
108f395670 fixed ordering.py tests 2013-02-17 22:58:13 +04:30
David Halter
c67b9986a6 some minor fixes / inits to inputs refactoring 2013-02-17 22:48:41 +04:30
David Halter
779ce6509d first dynamic array work 2013-02-17 22:15:11 +04:30
David Halter
79216f189f moved parsing.Simple.module to _sub_module 2013-02-17 00:30:44 +04:30
David Halter
1366f5fa61 made ListComprehensions behave more llike typical 'pr' objects 2013-02-17 00:23:43 +04:30
David Halter
89a2cb15d0 fix problems with positions in definitions with the same name on the same line 2013-02-17 00:14:30 +04:30
David Halter
8fd3910958 fix a get_code problem for instances/statements, which is not needed there, because get_code should only be called on pure parsing_representation objects 2013-02-16 23:38:07 +04:30
Danilo Bargen
e6b050c681 Updated README 2013-02-16 14:22:09 +01:00
Danilo Bargen
bfa1aaf183 Added @andviro to AUTHORS.txt 2013-02-16 14:18:17 +01:00
Danilo Bargen
aedd680e9d Typo in docstring 2013-02-16 14:06:23 +01:00
Danilo Bargen
d3947a6dc7 Added a type hinting recipe to docs (refs #133) 2013-02-16 14:04:04 +01:00
David Halter
860d5a061f Merge pull request #134 from tkf/docfix
Fix typo in doc and add some links
2013-02-15 10:00:37 -08:00
Takafumi Arakaki
d1bcbd1559 Fix typo in development.rst 2013-02-15 18:34:25 +01:00
Takafumi Arakaki
98a6febb50 Fix typo s/Epidoc/Epydoc/g and add links for markups 2013-02-15 18:31:05 +01:00
Danilo Bargen
ea9a667425 Fixed issue with repr encoding (fixes davidhalter/jedi-vim#94) 2013-02-14 21:10:06 +01:00
Danilo Bargen
3ab2b0a244 Added (failing) regression test for davidhalter/jedi-vim#94 2013-02-14 20:26:31 +01:00
David Halter
fb0ea354ac fix a dynamic search param problem 2013-02-14 20:58:06 +04:00
David Halter
cda84dc92a add an empty IsScope class to match all scopes 2013-02-14 20:54:26 +04:00
David Halter
52b32a01c1 move default arguments [] to (), because mutable may be dangerous (especially in a recursive environment 2013-02-11 01:21:51 +01:00
David Halter
f423de1956 fix some descriptor methods 2013-02-11 01:08:45 +01:00
David Halter
cae38ed3d7 fix getattr/__getattr*__ 2013-02-11 00:50:32 +01:00
David Halter
f2a7788d66 fix InstanceElement get_commands (doesn't raise an error anymore, but I don't know if it works) 2013-02-11 00:21:05 +01:00
David Halter
de5de41627 fix last failing function tests 2013-02-11 00:06:31 +01:00
David Halter
da4ad7fd3f fix Name (instead of Param) as param key 2013-02-10 23:57:20 +01:00
David Halter
5722cd2382 fix parts of helpers.search_function_definition 2013-02-10 22:40:28 +01:00
David Halter
01c48593bf er.Array is now also iterable 2013-02-10 21:15:04 +01:00
David Halter
df17c98061 fixed the whole var args iterator stuff 2013-02-10 16:59:51 +01:00
David Halter
a091ee92a4 fix dictionary index problems 2013-02-10 16:12:30 +01:00
David Halter
fb6b3ce342 reintroduce parent and get_parent_until for er.Array, because an array is a builtin. always. 2013-02-10 15:58:02 +01:00
David Halter
7dce3cb964 param keys of dicts are statements now 2013-02-10 15:50:44 +01:00
David Halter
cd49126733 improved param generator 2013-02-10 02:01:27 +01:00
David Halter
0008531a30 pr.Array takes no values param anymore 2013-02-09 03:55:56 +01:00
David Halter
22a1b2397d simple function tests pass now 2013-02-09 03:55:30 +01:00
David Halter
a1e366f791 different little beauty improvements 2013-02-09 03:16:10 +01:00
David Halter
9540025a02 fixed some more array tests 2013-02-09 02:17:29 +01:00
David Halter
25b2239f6a repr corrections for Array/Statement/Call 2013-02-09 01:54:48 +01:00
David Halter
1735bdb3c3 Merge pull request #125 from tkf/more-mapping
More mapping
2013-02-08 16:35:50 -08:00
Takafumi Arakaki
7f9eb272c4 Add '_sqlite3' to BaseDefinition._mapping 2013-02-09 01:32:53 +01:00
Takafumi Arakaki
44097bac22 Add 'posix' to BaseDefinition._mapping
Needed for "os.fsync", for example.
2013-02-09 01:32:43 +01:00
Takafumi Arakaki
ef30642f25 Add '_functools' to BaseDefinition._mapping
Needed for "functools.partial"
2013-02-09 01:30:30 +01:00
David Halter
ffaaa68f56 fix unnessecary bracket stuff 2013-02-09 00:00:26 +01:00
David Halter
07f4c08069 some param fixes 2013-02-08 23:16:04 +01:00
David Halter
2fef25b1b1 fixed some dict issues 2013-02-08 21:18:58 +01:00
David Halter
a5e9977e94 fix first tuple assignments 2013-02-08 18:46:56 +01:00
David Halter
c831bfcca1 exchanged assignment_details order (another refactoring) 2013-02-08 18:06:11 +01:00
David Halter
bd393883b6 fixed assignment_detail parsing if tuples were used before = 2013-02-08 17:58:27 +01:00
David Halter
70f07320aa Statement.get_assignment_details -> get_commands (makes more sense) 2013-02-08 16:19:05 +01:00
David Halter
d6257fffc8 make array slicing work again 2013-02-08 15:50:35 +01:00
David Halter
942c620cde make the second test pass 2013-02-08 15:24:49 +01:00
David Halter
6c0a0e889c fix start_pos of multiple elements 2013-02-08 15:14:43 +01:00
David Halter
4f060ddcd9 refactor a lot of er.Array 2013-02-08 13:09:08 +01:00
David Halter
39f43c52fe little fixes for the dynamic module 2013-02-08 12:53:14 +01:00
David Halter
94126edda8 get rid of generate_param_array 2013-02-08 12:32:19 +01:00
David Halter
d82b8d5e19 remove close_brackets - not needed anymore 2013-02-08 02:43:05 +01:00
David Halter
5737e3ad1d fix __repr__ functions 2013-02-08 02:22:02 +01:00
David Halter
80d4c6e884 many refactoring fixes 2013-02-08 01:36:58 +01:00
David Halter
bf298f0ef4 start to get rid of parent_stmt 2013-02-07 18:34:18 +01:00
David Halter
6d2d23cd78 docstring update for parsing_representation 2013-02-07 18:19:35 +01:00
David Halter
0eeb1036ac removed more unused methods from Array 2013-02-07 18:07:20 +01:00
David Halter
f2451102c1 end_pos crap of Array 2013-02-07 17:58:44 +01:00
David Halter
d6a5a3e0e7 further removals of unused code, also changes evaluate, now. 2013-02-07 17:39:54 +01:00
David Halter
d2ab0fe862 basic concept for get_assignment_calls 2013-02-07 16:28:38 +01:00
David Halter
edb401eb48 basic implementation of recursive array parser in statement 2013-02-07 14:06:14 +01:00
David Halter
5be5d08441 heapy debugging in run.py 2013-02-07 10:18:19 +01:00
David Halter
0e41001dd6 er.ArrayElement -> ArrayMethod 2013-02-06 18:37:56 +01:00
David Halter
f3635ae75c imports docstring 2013-02-06 18:20:57 +01:00
David Halter
3530ccf69e builtin docstring 2013-02-06 16:06:59 +01:00
David Halter
fa5ce843cd builtin.parse_function_doc -> _parse_function_doc 2013-02-06 15:45:37 +01:00
David Halter
59de98af67 builtin.Parser -> builtin.BuiltinModule 2013-02-06 15:42:27 +01:00
David Halter
acf81225b7 modules docstring 2013-02-06 15:38:05 +01:00
David Halter
eda6335cc0 refactor some parts of docstrings.py 2013-02-06 13:14:36 +01:00
David Halter
48d8b6fa2d docstrings docstr update 2013-02-06 13:06:59 +01:00
David Halter
69137a48f0 pep8 2013-02-06 13:04:27 +01:00
David Halter
8cf783f2c3 get recursion links right in sphinx dev 2013-02-06 00:20:07 +01:00
David Halter
27b162346a _compatibility docstring 2013-02-06 00:14:29 +01:00
David Halter
c7627b10be recursion docstring 2013-02-06 00:08:34 +01:00
David Halter
7cb5411414 cache docstring 2013-02-05 23:55:42 +01:00
David Halter
bd993af30d added recursion documentation in sphinx dev 2013-02-05 23:43:40 +01:00
David Halter
56d900b7e2 moved recursion parts from helper to recursion.py 2013-02-05 23:41:54 +01:00
David Halter
18d424f86c move refactoring warning 2013-02-05 23:34:43 +01:00
David Halter
23bdb880e4 dynamic docstring 2013-02-05 23:32:28 +01:00
David Halter
836a3ae13b refactoring warning 2013-02-05 23:27:26 +01:00
David Halter
12d9d5b9fd refactoring docstring 2013-02-05 23:25:39 +01:00
David Halter
cd931c5112 docstrings.py docstring 2013-02-05 23:25:12 +01:00
David Halter
0adb0ac818 fast_parser docstring 2013-02-05 23:11:39 +01:00
David Halter
6e1d693c46 evaluate_representation docstring 2013-02-05 18:45:16 +01:00
David Halter
9be695ae8f remove 'messy' warning of evaluate docstring, because it's actually much better, now. 2013-02-05 18:45:06 +01:00
David Halter
2b64171dd7 evaluate.get_scopes_for_name -> evaluate.find_name 2013-02-05 18:27:01 +01:00
David Halter
650b96dbda improve docstrings of evaluate 2013-02-05 18:21:38 +01:00
David Halter
a14471ad48 split also the parser / parser_representation docstring 2013-02-05 18:13:44 +01:00
David Halter
4020c505e3 python 2.5 compatibility 2013-02-05 17:37:16 +01:00
David Halter
fccc4ccbe3 parser / evaluation representation added to sphinx dev 2013-02-05 17:34:01 +01:00
David Halter
3c1ebc02cc fix last remaining bugs of the big evaluate_representation refactoring 2013-02-05 17:21:23 +01:00
David Halter
e6352ae0a2 move CachedModule and get_sys_path from builtin.py to modules.py 2013-02-05 17:17:10 +01:00
David Halter
1474dcb91c other modules also adapt to the new evaluate_representation now 2013-02-05 17:09:57 +01:00
David Halter
783ed0b2a0 cleanup - pep8 2013-02-05 17:00:17 +01:00
David Halter
9d60d7b9ea change evaluate module to cope with the new evaluate_representation 2013-02-05 16:59:02 +01:00
David Halter
9a977c46f4 split evaluate into evaluate and evaluate_representation 2013-02-05 16:52:56 +01:00
David Halter
b3a0249475 fix the remaining refactoring problems 2013-02-05 16:38:39 +01:00
David Halter
bc08ea9630 renamed all occurences of parsing 2013-02-05 16:29:39 +01:00
David Halter
42450f48dc change an indentifier (avoid naming collisions) 2013-02-05 16:16:58 +01:00
David Halter
d372e86317 splitted parsing into parsing and parsing_representation 2013-02-05 16:15:21 +01:00
David Halter
c8a483eb9c Merge pull request #120 from dbrgn/dev
Development doc fixes
2013-02-05 02:38:15 -08:00
Danilo Bargen
a0f07c90e8 Development doc fixes 2013-02-05 11:31:05 +01:00
David Halter
4e6eff5688 little evaluate docstring fix 2013-02-05 01:48:22 +01:00
David Halter
24f2ff83a5 new evaluate docstring 2013-02-05 01:43:30 +01:00
David Halter
6942cca302 fixed some sphinx references 2013-02-05 00:40:23 +01:00
David Halter
ee9676a5d7 improved evaluate docstring 2013-02-05 00:39:48 +01:00
David Halter
4fc350f637 sphinx dev: make API documentation clearer 2013-02-05 00:06:44 +01:00
David Halter
3678f235c0 new parsing docstring 2013-02-05 00:02:29 +01:00
David Halter
057ed6bda2 better parsing docstring 2013-02-04 17:55:38 +01:00
David Halter
4bf6332a83 rename some parser functions (add underlines) 2013-02-04 16:55:35 +01:00
David Halter
c5f48873e8 sphinx dev: add fast_parser 2013-02-04 16:33:11 +01:00
David Halter
95c5b9a5e3 parsing.PyFuzzyParser -> Parser 2013-02-04 16:18:24 +01:00
David Halter
e1ab3855fa sphinx dev: move api further down 2013-02-04 16:11:49 +01:00
David Halter
fbcd58f488 sphinx dev: little text about api and about me 2013-02-04 16:10:43 +01:00
David Halter
27636dbb7c sphinx dev: remove dev- tags 2013-02-04 15:26:38 +01:00
David Halter
3af25edcf9 sphinx dev: more toc's 2013-02-04 15:20:57 +01:00
David Halter
117063da85 sphinx dev: core extensions description 2013-02-04 15:14:26 +01:00
David Halter
8aebf1c9f9 move core extensions up 2013-02-04 14:52:24 +01:00
David Halter
9eec549a39 make docstrings ready for sphinx 2013-02-04 14:23:50 +01:00
David Halter
3faf50049b fix sphinx warnings/errors 2013-02-04 11:09:07 +01:00
David Halter
ed61f28549 sphinx: remove sidebar stuff: relations, sourcelink and searchbox 2013-02-04 01:00:05 +01:00
David Halter
bbc1a6f425 sphinx dev: overview 2013-02-04 00:54:17 +01:00
David Halter
38db3af324 sphinx dev: polishes 2013-02-04 00:02:49 +01:00
David Halter
0ce1bfda72 sphinx automodule doesn't document members by default anymore 2013-02-03 23:59:03 +01:00
David Halter
f0108d494f dev docs: introduction 2013-02-03 23:56:37 +01:00
David Halter
bded969e02 dev docs: no-members for automodule 2013-02-03 23:55:37 +01:00
David Halter
fc2188ecff dev docs: core overview 2013-02-03 23:32:31 +01:00
David Halter
48c2979539 sphinx development docs beginning 2013-02-03 23:02:10 +01:00
David Halter
18dbbada88 removed the plugin list in the wrong place 2013-02-03 21:32:04 +01:00
David Halter
f59a8d9955 remove redundant sphinx documentation 2013-02-03 20:52:09 +01:00
David Halter
5fc4657f35 improve feature readme note 2013-02-03 20:46:52 +01:00
David Halter
19cb4cb1e6 move history from readme to sphinx docs 2013-02-03 20:44:53 +01:00
David Halter
e775c3ce13 semantic versioning notice now has no starting number anymore 2013-02-03 20:32:02 +01:00
David Halter
046b11d7aa docs title 2013-02-03 20:28:33 +01:00
David Halter
8f6b7289f9 readme title 2013-02-03 20:27:35 +01:00
David Halter
b6ecd71f16 use first lines of readme as jedi docstring 2013-02-03 20:14:21 +01:00
David Halter
0adfb30084 readme typo 2013-02-03 16:40:57 +01:00
David Halter
1cc67b271d improved jedi docstring, better basic description 2013-02-03 15:51:21 +01:00
David Halter
1f26aded67 bad sentence in readme 2013-02-02 18:28:13 +01:00
David Halter
15570a14eb removed the under 'construction notice' for SublimeJEDI, I think the plugin is good enough to be used now. I hope this is ok with you, @srusskih 2013-02-02 18:23:18 +01:00
David Halter
4eced17e99 better API description README text 2013-02-02 18:21:09 +01:00
David Halter
73ffec9a1b basic description 2013-02-02 18:14:58 +01:00
David Halter
be2d935703 Merge pull request #118 from tkf/dotted-docstr-type
Import module part of "dotted" type in docstring
2013-02-02 06:12:11 -08:00
Takafumi Arakaki
2917da8025 Make :ROLE:TYPE handling more general 2013-02-01 17:57:24 +01:00
David Halter
c62a435aee Merge pull request #119 from tkf/tox
Add tox setup to run tests
2013-01-30 13:08:01 -08:00
Takafumi Arakaki
26e27aecec Add tox setup to run tests 2013-01-30 22:02:46 +01:00
Takafumi Arakaki
0cbbb17a82 Add tests in completion/docstring.py 2013-01-30 21:23:04 +01:00
Takafumi Arakaki
b8f139d574 Merge branch 'sphinx-class-type' into dotted-docstr-type 2013-01-30 21:10:30 +01:00
Takafumi Arakaki
a7131fa949 Make the failing doctest pass 2013-01-30 21:09:41 +01:00
Takafumi Arakaki
ef1123a513 Add a failing doctest for :class:ClassName 2013-01-30 21:07:30 +01:00
Takafumi Arakaki
9a35301a29 Add doctests for search_param_in_docstr 2013-01-30 21:03:48 +01:00
Takafumi Arakaki
691177c20c Fix Python 2.5 failure 2013-01-30 17:05:43 +01:00
Takafumi Arakaki
81f59f9a6e Make the failing test pass; dotted type works now 2013-01-30 01:13:50 +01:00
Takafumi Arakaki
9f416c9dae Implement import for dotted types in dcostring 2013-01-30 01:12:45 +01:00
Takafumi Arakaki
e12212942f Add a failing test for type inference from docstring 2013-01-30 01:01:25 +01:00
David Halter
0e3cec5b17 Merge pull request #115 from andviro/master
more robust source encoding detection
2013-01-27 06:31:41 -08:00
Andrew Rodionoff
048608f4b7 compatibility with python2.5 2013-01-26 13:08:24 +04:00
Andrew Rodionoff
0315da9699 compatibility with python3 fix 2013-01-26 02:10:26 +04:00
Andrew Rodionoff
9c152119ad more robust source encoding detection 2013-01-25 22:57:23 +04:00
Danilo Bargen
b906fe4209 Added Arch Linux instructions to docs (fixes #114) 2013-01-17 14:31:32 +01:00
Danilo Bargen
9cdf745c6b Merge commit '46d49fd66c6e2ca61153cebab3f3bf7366dd0879' into dev.
Dangling commit by force-push.
2013-01-15 00:33:47 +01:00
David Halter
6d733f124d remove old unused function 2013-01-15 00:18:59 +01:00
Danilo Bargen
46d49fd66c Cross platform cachedir, again.
Another attempt at getting a cross-platform and (on Linux) XDG compliant cache dir.

Squashed commit of the following:

- Correct usage of os.getenv (thx @tkf)
- Use `~/.cache/jedi` as default fallback, not `~/.jedi`
- Updated cache location for OS X
- Added expanduser to cachedir
- Cross platform cachedir, again.
2013-01-15 00:17:24 +01:00
David Halter
f3f7eacae3 fix a little problem with import keyword autocompletion 2013-01-14 22:35:32 +01:00
David Halter
35e55fab25 improved import statement keyword autocompletion, #111 2013-01-14 22:32:34 +01:00
David Halter
ac2b0016d5 fix import problems with negative kill_counts, fixes #111 2013-01-14 21:25:48 +01:00
David Halter
d08ee04fbb regression tests for #111 2013-01-14 18:41:09 +01:00
David Halter
ce1653df46 python 2.5 with_statement 2013-01-12 15:18:30 +01:00
David Halter
96e4f301e3 changed pickling files to byte for py3k 2013-01-12 15:03:15 +01:00
David Halter
7b150630f0 stopiteration works in strange ways in the parser in py2, fixes #70, affects probably davidhalter/jedi-vim#68 2013-01-12 12:15:32 +01:00
David Halter
dd58afe8b2 fix cache problem with unsaved files 2013-01-12 11:57:25 +01:00
David Halter
d89d80733f travis: install simplejson for python 2.5 2013-01-12 01:20:51 +01:00
David Halter
9d9e40a66f fix path problems for path='' 2013-01-12 01:13:30 +01:00
David Halter
c16c66d895 improve debugging speed messages 2013-01-12 00:49:54 +01:00
David Halter
0219c8fd61 remove wrong caching of the parser 2013-01-12 00:14:30 +01:00
David Halter
00f7088c05 test for davidhalter/jedi-vim#70 2013-01-12 00:11:38 +01:00
David Halter
2efb93273a merged module_cache into parser_cache 2013-01-11 23:19:22 +01:00
David Halter
21ae8e4266 don't pickle cursor modules 2013-01-11 22:49:32 +01:00
David Halter
af92f7f9f2 pickling works now (had problems deriving str, see http://stackoverflow.com/questions/14248633/python-pickle-derived-str-object), #102 2013-01-11 22:26:30 +01:00
David Halter
1017db903c basic pickle implementation #102 2013-01-11 22:00:03 +01:00
David Halter
c923c93ccc default directory for parser caches: HOME, #102 2013-01-10 17:32:02 +01:00
David Halter
7c02632fdc simplify parser_cache checks 2013-01-10 12:10:35 +01:00
David Halter
9031e425be renamed code -> source 2013-01-10 11:41:11 +01:00
David Halter
133fbcd57a move module caching from builtin to cache 2013-01-10 11:04:26 +01:00
David Halter
cc3168ee0a added filesystem cache options 2013-01-10 10:32:47 +01:00
David Halter
cf22a08d0a care for python 3 dict.items iterator return (in python2 list) 2013-01-10 10:02:21 +01:00
David Halter
21ee6f400d improvement for pickling 2013-01-10 09:33:15 +01:00
David Halter
bc6005d833 add a newline to tests 2013-01-09 22:49:25 +01:00
David Halter
7b73072045 cleanup / pep8 2013-01-09 22:47:21 +01:00
David Halter
e9aadce5ae __slots__ usage works much better now -> scan for all slots and not only for current class 2013-01-09 21:44:59 +01:00
David Halter
f32c2e6dc4 added a common.MultiLevelAttributeError to copying, because there were some problems 2013-01-09 21:26:50 +01:00
David Halter
7aa527f12e use_as_parent again and fast_parent_copy deals now with __slots__ 2013-01-09 15:23:33 +01:00
David Halter
b85c54932b parsing.set_parent -> use_as_parent 2013-01-09 15:21:04 +01:00
David Halter
232aafeb6a improved slot statements 2013-01-09 15:18:28 +01:00
David Halter
1557ea792f use __slots__ to reduce memory footprint of parser, #102 2013-01-08 14:54:56 +01:00
David Halter
4a6aabaaba avoid syntaxwarning 2013-01-08 13:02:06 +01:00
David Halter
b991190f50 python 3 compatibility, which is nicer anyway 2013-01-08 12:58:13 +01:00
David Halter
8987ecf3a8 forgot some fragments of CachedModule.cache 2013-01-08 12:53:02 +01:00
David Halter
67f7e66cc6 move module cache to cache.py - centralize all caches there 2013-01-08 12:47:44 +01:00
David Halter
be3d7f35cd move parser cache to cache.py - centralize all caches there 2013-01-08 12:44:32 +01:00
David Halter
da34fc2358 add delete option to cache deleter 2013-01-08 12:41:08 +01:00
David Halter
869572fe64 further improvements of the jedi import in tests 2013-01-08 12:29:17 +01:00
David Halter
3c4e079ecb import jedi in tests and not api 2013-01-08 12:15:50 +01:00
David Halter
35186629f0 delete token generator after parsing, lowers the memory footprint for 10k files with a factor of 5, #102 2013-01-08 11:57:10 +01:00
David Halter
de45c277c1 broke the api again (unintentionally) 2013-01-07 20:55:05 +01:00
David Halter
3c15471182 fix problems with unbounderrors in assignment calls 2013-01-07 20:52:40 +01:00
David Halter
b748003b54 inline refactorings support tuples now, #103 2013-01-07 20:30:17 +01:00
David Halter
53a5bf6be8 inline tests 2013-01-07 20:20:02 +01:00
David Halter
8fbe1c47a5 working inline refactoring 2013-01-07 20:19:42 +01:00
David Halter
07cb30d91b set end_pos Arrays better 2013-01-07 20:08:54 +01:00
David Halter
f3c2c2aab4 add rename tests# 2013-01-07 14:29:48 +01:00
David Halter
f192259383 use absolute paths as the default for 'Script' 2013-01-07 14:29:34 +01:00
David Halter
082db3fdbd scan_array_for_pos -> search_function_call, which makes much more sense 2013-01-07 14:11:45 +01:00
David Halter
d35cb1898d change helpers.scan_array_for_pos to helpers.array_for_pos for refactorings 2013-01-07 14:01:14 +01:00
David Halter
5fc1d1130d more extract tests 2013-01-07 14:00:28 +01:00
David Halter
fbd2ed02e5 again py2.5... i should test in the future before pushing to dev... 2013-01-07 01:55:33 +01:00
David Halter
ab554a7529 py2.5 with statement again 2013-01-07 01:52:51 +01:00
David Halter
d72d8ad756 py2.5 compatibility 2013-01-07 01:51:12 +01:00
David Halter
8ed550254f add refactoring tests to travis tests 2013-01-07 01:48:48 +01:00
David Halter
50e64cf930 corrected indent of some extract functions 2013-01-07 01:47:33 +01:00
David Halter
fc58d1b8e7 working extract refactorings, affects #103 2013-01-07 01:32:20 +01:00
David Halter
54ac9b3292 add __repr__ to api.Script 2013-01-07 01:07:49 +01:00
David Halter
5796496baf corrected problem with line numbers 2013-01-07 00:36:37 +01:00
David Halter
00f78bc50d use lines_to_execute also in refactoring.py 2013-01-06 17:42:07 +01:00
David Halter
37bbf1af1a finished testing script for refactoring 2013-01-06 17:35:55 +01:00
David Halter
48c04b2fcd move script in refactorings to be the default first parameter 2013-01-06 01:57:02 +01:00
David Halter
4700656c71 added refactoring tests - non functional 2013-01-06 01:49:41 +01:00
David Halter
795d25d9a7 improved some run.py stuff again 2013-01-06 01:48:48 +01:00
David Halter
ce51021c1e move more testing functionality out of run.py 2013-01-05 22:42:16 +01:00
David Halter
31e8a8c5a2 remove sys path stuff in run.py (it's in base) 2013-01-05 20:19:48 +01:00
David Halter
15bd963f59 move test bases to test/base.py 2013-01-05 18:59:56 +01:00
David Halter
79e2bf80d0 don't use json (doesn't work in py2.5), even if it's not relevant. fixes #94 again :-) 2013-01-05 18:28:09 +01:00
David Halter
6c2f8a759d fix problems of the last 10 commits 2013-01-05 18:24:07 +01:00
David Halter
4b98321796 Merge branch 'dev' of github.com:davidhalter/jedi into dev 2013-01-05 14:33:06 +01:00
David Halter
a720178b72 Merge pull request #106 from dbrgn/quick_completion
Added quick_complete convenience function
2013-01-05 05:29:57 -08:00
David Halter
f3e71a220f basic exctract refactoring (not working yet) 2013-01-05 14:22:24 +01:00
Danilo Bargen
c14feb3f86 Renamed quick_complete to _quick_complete 2013-01-05 01:48:39 +01:00
David Halter
5c8d65c57c remove unnecessary assert 2013-01-04 22:54:38 +01:00
David Halter
cd9bdb9e1c renaming should work now. 2013-01-04 21:21:34 +01:00
David Halter
9f9bbaae56 no str cast on None for module paths 2013-01-04 20:16:13 +01:00
David Halter
3f5e9d6bca some docstring improvements 2013-01-04 20:07:20 +01:00
David Halter
89bd32e0d7 use str.splitlines() instead of split('\n'), which is better for non unix systems 2013-01-04 14:55:31 +01:00
David Halter
903626f4f9 change default sorting of related names to a more natural 2013-01-04 14:46:42 +01:00
David Halter
e50053aa46 enable None file_paths for related_name 2013-01-04 14:37:17 +01:00
David Halter
ae9d88c3f9 remove generalized refactoring method for now 2013-01-04 11:18:53 +01:00
Danilo Bargen
12b726c5a5 Added quick_complete convenience function 2013-01-03 23:10:35 +01:00
Danilo Bargen
23f36c86d7 Merge pull request #104 from dbrgn/newdocs
New Sphinx Docs
2013-01-02 11:18:15 -08:00
Danilo Bargen
96e961a609 Disabled sidebar image for now... 2013-01-02 20:10:59 +01:00
Danilo Bargen
a4e1fe47fd Improved plugin api documentation 2013-01-02 20:10:59 +01:00
Danilo Bargen
cd4727ea9c Added global.rst with jedi macro 2013-01-02 20:10:25 +01:00
Danilo Bargen
31492a721c Simplified README 2013-01-02 20:10:25 +01:00
Danilo Bargen
e9ca801f0f Added features to documentation 2013-01-02 20:10:25 +01:00
Danilo Bargen
e01a6cd0f8 Changed directory structure 2013-01-02 20:10:25 +01:00
Danilo Bargen
a215171750 Improved docs 2013-01-02 20:10:25 +01:00
Danilo Bargen
beba40e920 Dynamically update copyright date range 2013-01-02 20:09:46 +01:00
Danilo Bargen
bed4d6fa06 Added github star button 2013-01-02 20:09:46 +01:00
Danilo Bargen
aef84d71e1 Added "fork me on github" ribbon 2013-01-02 20:09:46 +01:00
Danilo Bargen
d3e4ea4d2a Added logo to docs 2013-01-02 20:09:46 +01:00
Danilo Bargen
7210751584 Switched to flask theme 2013-01-02 20:09:46 +01:00
Takafumi Arakaki
d89f469e12 Add intro part in settings.py 2013-01-02 20:09:46 +01:00
Takafumi Arakaki
51b22e0cb5 Add docstrings to all variables in settings.py 2013-01-02 20:09:46 +01:00
Takafumi Arakaki
c11a4fc050 Add top level docstring to settings.py 2013-01-02 20:09:46 +01:00
Takafumi Arakaki
0747469ffc Use autodoc_member_order = 'bysource'
This is for making the variables appear in settings.py
to appear in the document in the same order.
2013-01-02 20:09:46 +01:00
Takafumi Arakaki
9e17f552f1 Move comments in settings.py to docstring 2013-01-02 20:09:46 +01:00
David Halter
72869310c1 refactoring basics, #103 2012-12-31 14:47:57 +01:00
David Halter
17bec929bb fix a little import problem in regression tests 2012-12-31 12:36:33 +01:00
David Halter
806426898d add @jjay to authors 2012-12-30 14:10:41 +01:00
David Halter
71b50bdbd7 return None on keywords for BaseDefinition.full_name, fixes #94 2012-12-30 10:49:18 +01:00
David Halter
548fb64fd7 test for #94 2012-12-30 10:39:08 +01:00
David Halter
64465dae8d fix problem with empty import_paths 2012-12-30 10:32:11 +01:00
David Halter
41362370ed follow imports if they are in the file path, fixes davidhalter/jedi-vim#56 2012-12-30 10:29:35 +01:00
David Halter
886d43fd4c fix problems with points in completion 2012-12-29 23:47:20 +01:00
David Halter
07dbebe277 test for problem with points in completion 2012-12-29 23:38:47 +01:00
David Halter
01220e047e sphinx api_classes description 2012-12-29 20:44:07 +01:00
David Halter
19084616ea sphinx: update api return types 2012-12-29 20:39:55 +01:00
David Halter
1700b37f64 same_name_completions -> _same_name_completions, #97 2012-12-29 20:20:26 +01:00
David Halter
86f37261a2 the bool variable of #97 was defined wrong, fixes #97 2012-12-29 20:07:45 +01:00
David Halter
968f9bdf60 fix completion duplication problem with upper/lower case 2012-12-29 02:18:05 +01:00
David Halter
406233b62d ignore duplicates of completion outputs, #97 2012-12-29 01:59:34 +01:00
David Halter
b04e74ca7d Merge pull request #96 from jjay/improvedocparse
Improve docstring parsing for param types.
2012-12-28 05:29:22 -08:00
Yakov Borevich
3b412c72e9 Remove google docstrings support. Updates #40 2012-12-28 10:09:03 +04:00
Yakov Borevich
c7cf7b6dc7 Fix complex param parsing in docstrings. 2012-12-28 10:07:51 +04:00
David Halter
79efd20d63 move screenshots so that sphinx can access it. 2012-12-27 20:02:00 +01:00
David Halter
6002b019c3 sphinx change again 2012-12-27 19:54:09 +01:00
David Halter
596d0fdf77 resolved merge conflict 2012-12-27 19:49:30 +01:00
David Halter
3f90055c69 improve docs again 2012-12-27 19:48:19 +01:00
David Halter
bf16a3a77a sphinx: added ressources/plugins 2012-12-27 19:32:36 +01:00
David Halter
d7e06b1d69 add an api docstring again 2012-12-27 19:18:38 +01:00
David Halter
464685f433 move docsring of api.py to __init__.py 2012-12-27 19:07:49 +01:00
David Halter
4d5645504d sphinx: for now use automodule instead of documenting every class 2012-12-27 19:06:22 +01:00
David Halter
fc1d075a7c Merge pull request #95 from dbrgn/docs
Proper Sphinx deprecation warnings and cross references
2012-12-27 10:01:28 -08:00
Danilo Bargen
59ff88ab57 Proper sphinx deprecation warning 2012-12-27 18:52:56 +01:00
Danilo Bargen
9e4a1bce5f Fixed cross reference to exception 2012-12-27 18:52:46 +01:00
David Halter
16236857bc Merge pull request #93 from tkf/warnings.warn
Use warnings.warn instead of raise
2012-12-27 09:38:02 -08:00
Takafumi Arakaki
7cbb541b78 Use warnings.warn instead of raise 2012-12-27 18:32:03 +01:00
David Halter
5e03147f43 tried to link the DeprecationWarning in Sphinx 2012-12-27 18:16:37 +01:00
David Halter
f8efc7c950 changed warnings to sphinx doc style 2012-12-27 18:15:11 +01:00
David Halter
22fcfe5e19 deprecated BaseDefinition.line_nr in favor of 'line'. thx @dbrgn for the hint. 2012-12-27 18:12:18 +01:00
David Halter
db4b09e05e Merge pull request #91 from jjay/docrtypes
Return type detection based on docstrings.
2012-12-27 08:41:32 -08:00
David Halter
422eb6cbbf version can now be changed in one file (for sphinx and pypi) 2012-12-27 17:25:31 +01:00
Yakov Borevich
5036960bf1 Merge branch 'dev' of github.com:davidhalter/jedi into docrtypes 2012-12-27 19:31:36 +04:00
Yakov Borevich
502c643c73 Return types for docstrings 2012-12-27 19:30:40 +04:00
David Halter
4c2c86ae72 ignore completion in import aliases 2012-12-27 16:22:25 +01:00
David Halter
50d6f8e916 fix completions on 'from . import variable' 2012-12-27 16:13:48 +01:00
David Halter
fc9e78c31e new relative import tests 2012-12-27 15:40:46 +01:00
David Halter
21bd50c608 follow 'from . import variable', fixes parts of davidhalter/jedi-vim#56 2012-12-27 15:30:43 +01:00
David Halter
31b335dc08 fix for tar_import stuff, that was caused by http://bugs.python.org/issue16791, may affect davidhalter/jedi-vim#56 2012-12-27 13:32:39 +01:00
David Halter
f0dcbd8290 Merge branch 'dev' of github.com:davidhalter/jedi into dev 2012-12-27 12:11:14 +01:00
David Halter
7fe2423c53 Merge pull request #90 from jjay/master
Epydoc dosctrings support
2012-12-27 03:10:55 -08:00
Yakov Borevich
a7f7d8ff9f Add tests for epydoc formated dosctring 2012-12-27 14:59:42 +04:00
Yakov Borevich
46a3ecc7d8 Support for Epydoc format in docstrings 2012-12-27 14:59:42 +04:00
Danilo Bargen
a8dfcb35a3 Fixed RTD theme now. Really. (Hopefully.) 2012-12-27 02:27:27 +01:00
Danilo Bargen
e12c8371dd Hopefully fixed issue with RTD html building 2012-12-27 02:21:25 +01:00
David Halter
e94376095f note that readme/doc changes are ok in master branch to contributing.md 2012-12-27 02:09:20 +01:00
David Halter
9b0e60afa2 Merge remote-tracking branch 'origin/master' into dev 2012-12-27 02:07:32 +01:00
David Halter
3d22c2480e Merge branch 'dev' of github.com:davidhalter/jedi into dev 2012-12-27 02:07:12 +01:00
David Halter
9337aa3875 Merge pull request #89 from dbrgn/docs
Sphinx docs cleanup
2012-12-26 17:06:59 -08:00
David Halter
b5cfd158f5 Merge pull request #88 from dbrgn/readme_changes
Some README changes
2012-12-26 17:06:50 -08:00
Danilo Bargen
f648357600 Force usage of default Sphinx template on RTD 2012-12-27 01:59:09 +01:00
Danilo Bargen
c4b35bb8b1 Removed "indices and tables" from index page 2012-12-27 01:56:22 +01:00
David Halter
5119090395 removed two notices in readme 2012-12-27 01:54:58 +01:00
Danilo Bargen
a0813fd126 Some README changes 2012-12-27 01:50:54 +01:00
David Halter
b9cc4dc378 forgot a point :-) 2012-12-27 01:39:28 +01:00
David Halter
232e7e4826 jedi is using semantic versioning 2012-12-27 01:36:49 +01:00
David Halter
d1f23444b1 add property tests for renaming and removed some limitations of renaming, fixes davidhalter/jedi-vim#57 2012-12-27 00:03:02 +01:00
David Halter
a7deb4766f test for davidhalter/jedi-vim#57 2012-12-26 23:28:24 +01:00
David Halter
4ff3a0ba26 variable set in the wrong place 2012-12-26 23:10:03 +01:00
David Halter
e057387910 added docstring tests (forgot to add them a while ago), important for #87 2012-12-26 23:08:34 +01:00
David Halter
95a435f946 use get_in_function_call_cache again, because parsing the code is sometimes to slow 2012-12-26 23:05:31 +01:00
David Halter
45c9fe16b8 many tests and fixes for instance element renamings 2012-12-26 22:56:56 +01:00
David Halter
2534269cce many new renaming tests (and bug fixes) 2012-12-26 21:34:21 +01:00
David Halter
3bf0ec70fc improved goto on assignees (just return the assignee). 2012-12-26 20:53:15 +01:00
David Halter
e020c6b792 api.Script.parser -> api.Script._parser (and the same with module). important for #86, sphinx docs 2012-12-26 18:42:54 +01:00
David Halter
993f7bd8d7 Merge branch 'dev' of github.com:davidhalter/jedi into dev 2012-12-26 18:36:24 +01:00
David Halter
b7888c3d7c Merge pull request #86 from dbrgn/docs
Sphinx Docs
2012-12-26 08:35:38 -08:00
Danilo Bargen
6b31199371 Set author to "Jedi contributors" 2012-12-26 16:42:58 +01:00
Danilo Bargen
5170919458 Extended documentation of BaseDefinition properties. 2012-12-26 16:41:47 +01:00
Danilo Bargen
33cec59e94 Show inheritance and base class 2012-12-26 16:38:11 +01:00
Danilo Bargen
495ff87081 Added examples to API docs 2012-12-26 16:31:49 +01:00
Danilo Bargen
9f3bf0114e Improved API documentation 2012-12-26 14:25:59 +01:00
Danilo Bargen
e0d07aff79 Started creating full documentation 2012-12-26 01:34:36 +01:00
Danilo Bargen
4217632ec8 Added Sphinx docs 2012-12-26 00:29:18 +01:00
David Halter
92cd50b2b7 added goto tests for params 2012-12-25 02:26:36 +01:00
David Halter
70b949b6e5 test for class inheritance renaming 2012-12-24 17:22:45 +01:00
David Halter
1a4b556ddb removed whitespace in renaming tests 2012-12-24 17:15:21 +01:00
David Halter
6322df11f3 renaming tests refactored. It's now much easier and understandable to create rename tests 2012-12-24 17:12:46 +01:00
David Halter
bd93f8fd2d move some parser details to protected class members 2012-12-24 12:53:38 +01:00
David Halter
40898450af support for super() 2012-12-24 12:27:43 +01:00
David Halter
6ec41909fe add lambdas to README 2012-12-24 10:58:56 +01:00
David Halter
0cca730b6e increase python2.5 test fails again, I'm sure @dbrgn will like this. 2012-12-24 10:53:17 +01:00
David Halter
e647e09f0c fix problems with unnecessary brackets and following executions, which fixes also the last remaining lambda problem 2012-12-24 10:47:11 +01:00
David Halter
3f95d33f4d added tests for executions/follows of unnecessary brackets 2012-12-24 02:25:26 +01:00
David Halter
6ab9390bce lambdas: fix remaining problems with tests 2012-12-24 01:28:03 +01:00
David Halter
f05b960386 lambdas: fix problems with reverse tokenizer 2012-12-24 01:26:35 +01:00
David Halter
716d2362fd fixed mostly wrong lambda tests (and a few lambda improvements as well) 2012-12-23 15:52:49 +01:00
David Halter
c7fd196850 get_code is now able to process returns again 2012-12-22 20:28:34 +01:00
David Halter
e701a4ef92 lambdas: add correct parents and change evaluate 2012-12-22 19:43:30 +01:00
David Halter
77b2ea2dce basic lambda parsing 2012-12-22 19:27:44 +01:00
David Halter
ef1f637e71 Lambda class 2012-12-22 19:08:37 +01:00
David Halter
3fbf66f42c 14 lambda tests 2012-12-22 18:59:55 +01:00
David Halter
da6186618e changed re.SREPattern mixins (forgot the params everywhere) 2012-12-22 18:33:16 +01:00
David Halter
60c65596d4 changed re.SREMatch.group 2012-12-22 18:29:01 +01:00
David Halter
55c7d414b0 and again something forgotten... sooo tired!!! 2012-12-22 01:55:34 +01:00
David Halter
3d06f7a257 and again some forgotten imports in the tests 2012-12-22 01:52:40 +01:00
David Halter
5fa88af041 forgot an import... it's late, I should go to sleep... 2012-12-22 01:48:36 +01:00
David Halter
a2e05c215a forgot to move two function calls in the last commit 2012-12-22 01:43:29 +01:00
David Halter
0d6d84402d move star_import_cache to cache.py 2012-12-22 01:39:22 +01:00
David Halter
ed8845c343 delete caches also at the beginning of an interaction 2012-12-22 01:37:31 +01:00
David Halter
2610c180c0 check for expired time caches in every interaction 2012-12-22 01:36:03 +01:00
David Halter
0617909e84 add get_in_function_call caching (3s for now) 2012-12-22 01:28:24 +01:00
David Halter
f4b585a5e7 added an option to disable get_in_function_call caching 2012-12-21 18:00:21 +01:00
David Halter
b3c89effe7 add wdb to implementations, #85 2012-12-19 23:50:49 +01:00
David Halter
99f1c805ef reversed builtin for pypy (is not a class), fixes #85 2012-12-19 23:47:54 +01:00
David Halter
ce0aadc9a5 fix problems with certain builtins (names were just stupid, like '_sqlite3.cpython-32mu' / python 2.5 may have an error more #84 2012-12-19 23:12:15 +01:00
David Halter
2ee09980eb improve sqlite3 support -> added mixins -> fixes #84 2012-12-19 22:26:53 +01:00
David Halter
acc8a4bc66 improve docstring hinting, also matters for sqlite3 2012-12-19 22:26:12 +01:00
David Halter
dfc82742ec fix for missing unicode in py3k 2012-12-19 21:30:27 +01:00
David Halter
e789e41fde remove old return statements 2012-12-19 21:27:59 +01:00
David Halter
ca21f76128 no confusions in executions for dict assignments, fixes #83 2012-12-19 21:23:50 +01:00
David Halter
bb31d3de3f pep8 2012-12-19 20:58:52 +01:00
David Halter
5e2e0a8f45 added source_to_unicode method to deal with different encodings, davidhalter/jedi-vim#48 2012-12-19 20:45:49 +01:00
David Halter
15510a4c3b more unicode casts instead of str casts 2012-12-19 18:46:32 +01:00
David Halter
8351cd65e8 refactored unicode tests 2012-12-19 12:07:43 +01:00
David Halter
571a96c02b test for #83 2012-12-19 02:01:57 +01:00
David Halter
7d3fd315d3 dots without any other actual relevant code shouldn't just complete, fixes #46 2012-12-19 01:55:32 +01:00
David Halter
df95416160 refactored return/yield parsing, fixes davidhalter/jedi-vim#49 2012-12-18 15:35:59 +01:00
David Halter
e09b578608 test for davidhalter/jedi#49 2012-12-18 12:09:41 +01:00
David Halter
17c349400c accidentally removed the fast_parser=True from settings 2012-12-18 01:51:36 +01:00
David Halter
74a51c87cb moved using setting.fast_parser to fast_parser 2012-12-18 01:50:10 +01:00
David Halter
2a8660b989 update parser settings 2012-12-18 01:09:21 +01:00
David Halter
92feb2f1d6 cleanup / py2.5 and py2.6 compatibility 2012-12-18 00:56:16 +01:00
David Halter
ffeecc1674 improved speed for tests again, reparsing is now only done if something changed (removed reparsing of the user_scope in every completion). this is not going to have a deep impact on users, but it speeds up tests. 2012-12-18 00:51:25 +01:00
David Halter
7e7006e142 fix problems (end_pos of positions was wrong) with user_scopes 2012-12-17 23:19:44 +01:00
David Halter
8bbd2108bd basic caching of fast_parser modules. 2012-12-17 01:27:15 +01:00
David Halter
c1d442fa17 added a line_offset attribute - enables fast_parser to change positions 2012-12-16 17:23:15 +01:00
David Halter
099e2410ad added test count to test result 2012-12-16 04:09:45 +01:00
David Halter
5f76ffdfe8 py3k/py2.5 compatibility, and a bugfix 2012-12-16 04:03:46 +01:00
David Halter
36b75c3e15 fixed the last remaining problems with fast_parser (not cached yet) 2012-12-16 03:50:50 +01:00
David Halter
bcdf273c21 fix comment problem (code shouldn't be parsed multiple times) 2012-12-16 02:41:04 +01:00
David Halter
21b49a17ff fix last problems with fast_parser 2012-12-15 23:17:37 +01:00
David Halter
3aae532374 fix fast_parser problems with asserts 2012-12-15 21:27:09 +01:00
David Halter
2b579eb09b many bugfixes related to fast_parser 2012-12-15 16:38:21 +01:00
David Halter
b8b4a02398 fixed some bugs within fast_parser and added an option 'settings.fast_parser' to turn on fast parsing. 2012-12-14 18:16:54 +01:00
David Halter
6963a6ac4a other random bugfixes for positions and the use of fast_parser 2012-12-14 15:22:28 +01:00
David Halter
05885a8b06 parsing change of positions for modules (wasn't correct before) 2012-12-14 15:20:51 +01:00
David Halter
5b79e7026e python 2.5 compatibility 2012-12-14 12:25:27 +01:00
David Halter
fe05474459 added a first version of fast_parser 2012-12-14 12:19:07 +01:00
David Halter
2ed8bad8af move MultiLevel exceptions to common 2012-12-14 12:04:51 +01:00
David Halter
fa831b4f4a Module -> SubModule because of part_parser 2012-12-14 11:47:47 +01:00
David Halter
f3273e1cda refactor use of NoErrorTokenizer 2012-12-14 10:07:39 +01:00
David Halter
b0386d9c74 start/end_pos refactored in parsing 2012-12-13 19:59:29 +01:00
David Halter
a51a1f7aa0 remove PyFuzzyParser attributes in favor of local vars 2012-12-13 18:16:06 +01:00
David Halter
3aecb3ff90 add NoErrorTokenizer, for more readability 2012-12-13 18:08:01 +01:00
David Halter
cab74d5f51 move parsing.PushBackIterator and parsing.indent_block to common 2012-12-13 16:38:15 +01:00
David Halter
2d8d180354 preparation for a more general purpose tokenizer 2012-12-13 16:25:39 +01:00
David Halter
4814c13e29 PyFuzzyParser should not be called with no_docstr=True and source_path 2012-12-13 12:42:04 +01:00
David Halter
303672f037 memoize_default is now able to use different caches. 2012-12-13 12:39:51 +01:00
David Halter
30fef3ffde added time used by test script 2012-12-12 21:38:22 +01:00
David Halter
7969e19a5b cleanup/pep8 2012-12-12 21:34:04 +01:00
David Halter
9b45ddcb7c removed faked_scopes, was only necessary because of weakrefs. 2012-12-12 21:31:41 +01:00
David Halter
e8ddc35a1b caches have their own module now. 2012-12-12 21:28:19 +01:00
David Halter
169b35b5a9 setup.py should be executable 2012-12-12 18:11:39 +01:00
David Halter
5a7a420026 merge and a few changes for the SyntaxError problem with mixin in (pip installation). -> fixes #79, also important for #82, #81, davidhalter/jedi-vim/issues/41, solution from #65 2012-12-12 18:10:42 +01:00
David Halter
181d700e50 change the way mixins are being added by setup.py, may fix #79 2012-12-12 11:32:53 +01:00
David Halter
2d75f509b7 typo for symmetric_difference, davidhalter/jedi-vim#45 2012-12-11 14:03:24 +01:00
David Halter
cef7da7035 method signatures and return types of some 'set' methods, fixes davidhalter/jedi-vim#45 2012-12-11 14:01:18 +01:00
David Halter
b8a92ecbe3 speed debugging for completions 2012-12-11 13:59:59 +01:00
David Halter
6217e1e85a private members are now being put at the end of a completion list, #78 2012-12-11 09:37:52 +01:00
David Halter
a3647a4c03 version update 2012-12-11 09:36:14 +01:00
Danilo Bargen
b5be075b56 Fixed including of mixin files in MANIFEST.in 2012-11-20 11:25:34 +01:00
Danilo Bargen
9f20243dd3 Renamed mixin .py files to .pym (fixes davidhalter/jedi-vim#41) 2012-11-20 11:21:11 +01:00
137 changed files with 12935 additions and 5212 deletions

18
.coveragerc Normal file
View File

@@ -0,0 +1,18 @@
[run]
omit =
jedi/_compatibility.py
[report]
# Regexes for lines to exclude from consideration
exclude_lines =
# Don't complain about missing debug-only code:
def __repr__
if self\.debug
# Don't complain if tests don't hit defensive assertion code:
raise AssertionError
raise NotImplementedError
# Don't complain if non-runnable code isn't run:
if 0:
if __name__ == .__main__.:

8
.gitignore vendored
View File

@@ -1,7 +1,13 @@
*~
*.swp
*.swo
.ropeproject
*.pyc
.ropeproject
.tox
.coveralls.yml
.coverage
/build/
/docs/_build/
/dist/
jedi.egg-info/
record.json

View File

@@ -1,10 +1,21 @@
language: python
python:
- 2.5
- 2.6
- 2.7
- 3.2
install: echo "No external requirements"
env:
- TOXENV=py26
- TOXENV=py27
- TOXENV=py32
- TOXENV=py33
- TOXENV=cov
- TOXENV=sith
matrix:
allow_failures:
- env: TOXENV=cov
- env: TOXENV=sith
install:
- pip install --quiet --use-mirrors tox
script:
- cd test
- ./test.sh
- tox
after_script:
- if [ $TOXENV == "cov" ]; then
pip install --quiet --use-mirrors coveralls;
coveralls;
fi

View File

@@ -1,13 +1,25 @@
Main Authors
============
David Halter (@davidhalter)
David Halter (@davidhalter) <davidhalter88@gmail.com>
Takafumi Arakaki (@tkf) <aka.tkf@gmail.com>
Code Contributors
=================
Contributors
============
Danilo Bargen (@dbrgn)
Danilo Bargen (@dbrgn) <gezuru@gmail.com>
Laurens Van Houtven (@lvh) <_@lvh.cc>
Aldo Stracquadanio (@Astrac) <aldo.strac@gmail.com>
Jean-Louis Fuchs (@ganwell) <ganwell@fangorn.ch>
tek (@tek)
Takafumi Arakaki (@tkf)
Aaron Griffin
Yasha Borevich (@jjay) <j.borevich@gmail.com>
Aaron Griffin <aaronmgriffin@gmail.com>
andviro (@andviro)
Mike Gilbert (@floppym) <floppym@gentoo.org>
Aaron Meurer (@asmeurer) <asmeurer@gmail.com>
Lubos Trilety <ltrilety@redhat.com>
Akinori Hattori (@hattya)
srusskih (@srusskih)
Note: (@user) means a github user name.

22
CHANGELOG.rst Normal file
View File

@@ -0,0 +1,22 @@
.. :changelog:
Changelog
---------
0.7.0 (2013-08-09)
++++++++++++++++++
* switched from LGPL to MIT license
* added an Interpreter class to the API to make autocompletion in REPL possible.
* added autocompletion support for namespace packages
* add sith.py, a new random testing method
0.6.0 (2013-05-14)
++++++++++++++++++
* much faster parser with builtin part caching
* a test suite, thanks @tkf
0.5 versions (2012)
+++++++++++++++++++
* Initial development

View File

@@ -1,11 +1,28 @@
Pull Requests are great (on the **dev** branch)!
Pull Requests are great (on the **dev** branch)! Readme/Documentation changes
are ok in the master branch.
1. Fork the Repo on github.
2. If you are adding functionality or fixing a bug, please add a test!
3. Push to your fork and submit a **pull request to the dev branch**.
3. Add your name to AUTHORS.txt
4. Push to your fork and submit a **pull request to the dev branch**.
My **master** branch is a 100% stable (should be). I only push to it after I am
certain that things are working out. Many people are using Jedi directly from
the github master branch.
the github master branch.
Please use Pep8 to style your code.
**Please use PEP8 to style your code.**
Changing Issues to Pull Requests (Github)
-----------------------------------------
If you have have previously filed a GitHub issue and want to contribute code
that addresses that issue, we prefer it if you use
[hub](https://github.com/github/hub) to convert your existing issue to a pull
request. To do that, first push the changes to a separate branch in your fork
and then issue the following command:
hub pull-request -b davidhalter:dev -i <issue-number> -h <your-github-username>:<your-branch-name>
It's no strict requirement though, if you don't have hub installed or prefer to
use the web interface, then feel free to post a traditional pull request.

View File

@@ -1,170 +1,21 @@
Licensed under the GNU LGPL v3 or later.
Copyright (C) 2012 David Halter <davidhalter88@gmail.com>.
The MIT License (MIT)
===============================================================================
Copyright (c) <2013> <David Halter and others, see AUTHORS.txt>
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -1,5 +1,12 @@
include README.rst
include CHANGELOG.rst
include LICENSE.txt
include AUTHORS.txt
include jedi/mixin/*.py
include .coveragerc
include sith.py
include conftest.py
include pytest.ini
include tox.ini
include jedi/mixin/*.pym
recursive-include test *
recursive-exclude * *.pyc

View File

@@ -1,262 +1,136 @@
########################################
Jedi - an awesome Python auto-completion
########################################
###################################################
Jedi - an awesome autocompletion library for Python
###################################################
.. image:: https://secure.travis-ci.org/davidhalter/jedi.png?branch=master
:target: http://travis-ci.org/davidhalter/jedi
:alt: Travis-CI build status
**beta testing**
.. image:: https://coveralls.io/repos/davidhalter/jedi/badge.png?branch=master
:target: https://coveralls.io/r/davidhalter/jedi
:alt: Coverage Status
*If you have any comments or feature requests, please tell me! I really want to
know, what you think about Jedi.*
.. image:: https://pypip.in/d/jedi/badge.png
:target: https://crate.io/packages/jedi/
Jedi is an autocompletion tool for Python. It works. With and without syntax
errors. Sometimes it sucks, but that's normal in dynamic languages. But it
sucks less than other tools. It understands almost all of the basic Python
syntax elements including many builtins.
Jedi is an autocompletion tool for Python that can be used in IDEs/editors.
Jedi works. Jedi is fast. It understands all of the basic Python syntax
elements including many builtin functions.
Jedi suports two different goto functions and has support for renaming.
Probably it will also have some support for refactoring in the future.
Additionaly, Jedi suports two different goto functions and has support for
renaming as well as Pydoc support and some other IDE features.
Jedi uses a very simple interface to connect with IDE's. As an reference, there
is a VIM implementation, which uses Jedi's autocompletion. However, I encourage
you to use Jedi in your IDEs. Start writing plugins! If there are problems with
licensing, just contact me.
Jedi uses a very simple API to connect with IDE's. There's a reference
implementation as a `VIM-Plugin <https://github.com/davidhalter/jedi-vim>`_,
which uses Jedi's autocompletion. I encourage you to use Jedi in your IDEs.
It's really easy. If there are any problems (also with licensing), just contact
me.
At the moment Jedi can be used as a
`VIM-Plugin <http://github.com/davidhalter/jedi-vim>`_. So, if you want to test
Jedi for now, you'll have to use VIM. But there are new plugins emerging:
Jedi can be used with the following editors:
- Vim (jedi-vim_, YouCompleteMe_)
- Emacs (Jedi.el_)
- Sublime Text (SublimeJEDI_ [ST2 + ST3], anaconda_ [only ST3])
And it powers the following projects:
- wdb_
- `Emacs-Plugin <https://github.com/tkf/emacs-jedi>`_
- `Sublime-Plugin <https://github.com/svaiter/SublimeJEDI>`_ **Under construction**
Here are some pictures:
.. image:: https://github.com/davidhalter/jedi/raw/master/screenshot_complete.png
.. image:: https://github.com/davidhalter/jedi/raw/master/docs/_screenshots/screenshot_complete.png
Completion for almost anything (Ctrl+Space).
.. image:: https://github.com/davidhalter/jedi/raw/master/screenshot_function.png
.. image:: https://github.com/davidhalter/jedi/raw/master/docs/_screenshots/screenshot_function.png
Display of function/class bodies, docstrings.
.. image:: https://github.com/davidhalter/jedi/raw/master/screenshot_pydoc.png
.. image:: https://github.com/davidhalter/jedi/raw/master/docs/_screenshots/screenshot_pydoc.png
Pydoc support (with highlighting, Shift+k).
There is also support for goto and renaming.
Get the latest from `github <http://github.com/davidhalter/jedi>`_.
Get the latest version from `github <https://github.com/davidhalter/jedi>`_
(master branch should always be kind of stable/working).
Docs are available at `https://jedi.readthedocs.org/
<https://jedi.readthedocs.org/>`_. Pull requests with documentation
enhancements and/or fixes are awesome and most welcome. Jedi uses `semantic
versioning <http://semver.org/>`_.
Installation
============
You can either include Jedi as a submodule in your text editor plugin (like
jedi-vim_ does it by default), or you
can install Jedi systemwide.
The preferred way to install the Jedi library into your system is by using
pip_::
sudo pip install jedi
If you want to install the current development version::
sudo pip install -e git://github.com/davidhalter/jedi.git#egg=jedi
pip install jedi
Note: This just installs the Jedi library, not the editor plugins. For
information about how to make it work with your editor, refer to the
corresponding documentation.
You don't want to use ``pip``? Please refer to the `manual
<https://jedi.readthedocs.org/en/latest/docs/installation.html>`_.
Support
Feature Support and Caveats
===========================
Jedi really understands your Python code. For a comprehensive list what Jedi
can do, see: `Features
<https://jedi.readthedocs.org/en/latest/docs/features.html>`_. A list of
caveats can be found on the same page.
You can run Jedi on cPython 2.6, 2.7, 3.2 or 3.3, but it should also
understand/parse code older than those versions.
Tips on how to use Jedi efficiently can be found `here
<https://jedi.readthedocs.org/en/latest/docs/recipes.html>`_.
API for IDEs
============
It's very easy to create an editor plugin that uses Jedi. See `Plugin API
<https://jedi.readthedocs.org/en/latest/docs/plugin-api.html>`_ for more
information.
Development
===========
There's a pretty good and extensive `development documentation
<https://jedi.readthedocs.org/en/latest/docs/development.html>`_.
Testing
=======
Jedi supports Python 2.5 up to 3.x. There is just one code base, for both
Python 2 and 3.
Jedi supports many of the widely used Python features:
The test suite depends on ``tox`` and ``pytest``::
- builtin functions/classes support
- complex module / function / class structures
- ignores syntax and indentation errors
- multiple returns / yields
- tuple assignments / array indexing / dictionary indexing
- exceptions / with-statement
- \*args / \*\*kwargs
- decorators
- descriptors -> property / staticmethod / classmethod
- closures
- generators (yield statement) / iterators
- support for some magic methods: ``__call__``, ``__iter__``, ``__next__``,
``__get__``, ``__getitem__``, ``__init__``
- support for list.append, set.add, list.extend, etc.
- (nested) list comprehensions / ternary expressions
- relative imports
- ``getattr()`` / ``__getattr__`` / ``__getattribute__``
- function annotations (py3k feature, are ignored right now, but being parsed.
I don't know what to do with them.)
- class decorators (py3k feature, are being ignored too, until I find a use
case, that doesn't work with Jedi)
- simple/usual ``sys.path`` modifications
- ``isinstance`` checks for if/while/assert
- virtualenv support
- infer function arguments with sphinx (and other) docstrings
pip install tox pytest
However, it does not yet support (and probably will in future versions, because
they are on my todo list):
To run the tests for all supported Python versions::
- manipulations of instances outside the instance variables, without using
functions
tox
It does not support (and most probably will not in future versions):
If you want to test only a specific Python version (e.g. Python 2.7), it's as
easy as ::
- metaclasses (how could an auto-completion ever support this)
- ``setattr()``, ``__import__()``
- Writing to some dicts: ``globals()``, ``locals()``, ``object.__dict__``
- evaluate ``if`` / ``while``
tox -e py27
Tests are also run automatically on `Travis CI
<https://travis-ci.org/davidhalter/jedi/>`_.
For more detailed information visit the `testing documentation
<https://jedi.readthedocs.org/en/latest/docs/testing.html>`_
Caveats
=======
This framework should work for both Python 2/3. However, some things were just
not as *pythonic* in Python 2 as things should be. To keep things simple, some
things have been held back:
- Classes: Always Python 3 like, therefore all classes inherit from ``object``.
- Generators: No ``next`` method. The ``__next__`` method is used instead.
- Exceptions are only looked at in the form of ``Exception as e``, no comma!
Syntax errors and other strange stuff, that is defined differently in the
Python language, may lead to undefined behaviour of the completion. Jedi is
**NOT** a Python compiler, that tries to correct you. It is a tool that wants
to help you. But **YOU** have to know Python, not Jedi.
Importing ``numpy`` can be quite slow sometimes, as well as loading the builtins
the first time. If you want to speed it up, you could write import hooks in
jedi, which preloads this stuff. However, once loaded, this is not a problem
anymore. The same is true for huge modules like ``PySide``, ``wx``, etc.
Security is an important issue for Jedi. Therefore no Python code is executed.
As long as you write pure python, everything is evaluated statically. But: If
you use builtin modules (`c_builtin`) there is no other option than to execute
those modules. However: Execute isn't that critical (as e.g. in pythoncomplete,
which used to execute *every* import!), because it means one import and no
more. So basically the only dangerous thing is using the import itself. If your
`c_builtin` uses some strange initializations, it might be dangerous. But if it
does you're screwed anyways, because eventualy you're going to execute your
code, which executes the import.
A little history
================
The Star Wars Jedi are awesome. My Jedi software tries to imitate a little bit
of the precognition the Jedi have. There is even an awesome `scene
<http://www.youtube.com/watch?v=5BDO3pyavOY>`_ of Monty Python Jedi's :-).
But actually the name hasn't so much to do with Star Wars. It's part of my
second name.
After I explained Guido van Rossum, how some parts of my auto-completion work,
he said (we drank a beer or two):
*Oh, that worries me*
When it's finished, I hope he'll like it :-)
I actually started Jedi, because there were no good solutions available for
VIM. Most auto-completions just didn't work well. The only good solution was
PyCharm. I just like my good old VIM. Rope was never really intended to be an
auto-completion (and also I really hate project folders for my Python scripts).
It's more of a refactoring suite. So I decided to do my own version of a
completion, which would execute non-dangerous code. But I soon realized, that
this wouldn't work. So I built an extremely recursive thing which understands
many of Python's key features.
By the way, I really tried to program it as understandable as possible. But I
think understanding it might need quite some time, because of its recursive
nature.
API-Design for IDEs
===================
If you want to set up an IDE with Jedi, you need to ``import jedi``. You should
have the following objects available:
::
Script(source, line, column, source_path)
``source`` would be the source of your python file/script, separated by new
lines. ``line`` is the current line you want to perform actions on (starting
with line #1 as the first line). ``column`` represents the current
column/indent of the cursor (starting with zero). ``source_path`` should be the
path of your file in the file system.
It returns a script object that contains the relevant information for the other
functions to work without params.
::
Script().complete
Returns ``api.Completion`` objects. Those objects have got
informations about the completions. More than just names.
::
Script().goto
Similar to complete. The returned ``api.Definition`` objects contain
information about the definitions found.
::
Script().get_definition
Mostly used for tests. Like goto, but follows statements and imports and
doesn't break there. You probably don't want to use this function. It's
mostly for testing.
::
Script().related_names
Returns all names that point to the definition of the name under the
cursor. This is also very useful for refactoring (renaming).
::
Script().get_in_function_call
Get the ``Function`` object of the call you're currently in, e.g.: ``abs(``
with the cursor at the end would return the builtin ``abs`` function.
::
NotFoundError
If you use the goto function and no valid identifier (name) is at the
place of the cursor (position). It will raise this exception.
::
set_debug_function
Sets a callback function for ``debug.py``. This function is called with
multiple text objects, in python 3 you could insert ``print``.
::
settings
Access to the ``settings.py`` module. The settings are described there.
.. _jedi-vim: http://github.com/davidhalter/jedi-vim
.. _pip: http://www.pip-installer.org/
.. _jedi-vim: https://github.com/davidhalter/jedi-vim
.. _youcompleteme: http://valloric.github.io/YouCompleteMe/
.. _Jedi.el: https://github.com/tkf/emacs-jedi
.. _sublimejedi: https://github.com/srusskih/SublimeJEDI
.. _anaconda: https://github.com/DamnWidget/anaconda
.. _wdb: https://github.com/Kozea/wdb

34
conftest.py Normal file
View File

@@ -0,0 +1,34 @@
import tempfile
import shutil
import jedi
collect_ignore = ["setup.py"]
# The following hooks (pytest_configure, pytest_unconfigure) are used
# to modify `jedi.settings.cache_directory` because `clean_jedi_cache`
# has no effect during doctests. Without these hooks, doctests uses
# user's cache (e.g., ~/.cache/jedi/). We should remove this
# workaround once the problem is fixed in py.test.
#
# See:
# - https://github.com/davidhalter/jedi/pull/168
# - https://bitbucket.org/hpk42/pytest/issue/275/
jedi_cache_directory_orig = None
jedi_cache_directory_temp = None
def pytest_configure(config):
global jedi_cache_directory_orig, jedi_cache_directory_temp
jedi_cache_directory_orig = jedi.settings.cache_directory
jedi_cache_directory_temp = tempfile.mkdtemp(prefix='jedi-test-')
jedi.settings.cache_directory = jedi_cache_directory_temp
def pytest_unconfigure(config):
global jedi_cache_directory_orig, jedi_cache_directory_temp
jedi.settings.cache_directory = jedi_cache_directory_orig
shutil.rmtree(jedi_cache_directory_temp)

153
docs/Makefile Normal file
View File

@@ -0,0 +1,153 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Jedi.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Jedi.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/Jedi"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Jedi"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

3
docs/_static/logo-src.txt vendored Normal file
View File

@@ -0,0 +1,3 @@
The source of the logo is a photoshop file hosted here:
https://dl.dropboxusercontent.com/u/170011615/Jedi12_Logo.psd.xz

BIN
docs/_static/logo.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

4
docs/_templates/ghbuttons.html vendored Normal file
View File

@@ -0,0 +1,4 @@
<h3>Github</h3>
<iframe src="http://ghbtns.com/github-btn.html?user=davidhalter&repo=jedi&type=watch&count=true&size=large"
frameborder="0" scrolling="0" width="170" height="30" allowtransparency="true"></iframe>
<br><br>

3
docs/_templates/sidebarlogo.html vendored Normal file
View File

@@ -0,0 +1,3 @@
<p class="logo"><a href="{{ pathto(master_doc) }}">
<img class="logo" src="{{ pathto('_static/logo.png', 1) }}" alt="Logo"/>
</a></p>

37
docs/_themes/flask/LICENSE vendored Normal file
View File

@@ -0,0 +1,37 @@
Copyright (c) 2010 by Armin Ronacher.
Some rights reserved.
Redistribution and use in source and binary forms of the theme, with or
without modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* The names of the contributors may not be used to endorse or
promote products derived from this software without specific
prior written permission.
We kindly ask you to only use these themes in an unmodified manner just
for Flask and Flask-related products, not for unrelated projects. If you
like the visual style and want to use it for your own projects, please
consider making some larger changes to the themes (such as changing
font faces, sizes, colors or margins).
THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

28
docs/_themes/flask/layout.html vendored Normal file
View File

@@ -0,0 +1,28 @@
{%- extends "basic/layout.html" %}
{%- block extrahead %}
{{ super() }}
{% if theme_touch_icon %}
<link rel="apple-touch-icon" href="{{ pathto('_static/' ~ theme_touch_icon, 1) }}" />
{% endif %}
<link media="only screen and (max-device-width: 480px)" href="{{
pathto('_static/small_flask.css', 1) }}" type= "text/css" rel="stylesheet" />
<a href="https://github.com/davidhalter/jedi">
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub">
</a>
{% endblock %}
{%- block relbar2 %}{% endblock %}
{% block header %}
{{ super() }}
{% if pagename == 'index' %}
<div class=indexwrapper>
{% endif %}
{% endblock %}
{%- block footer %}
<div class="footer">
&copy; Copyright {{ copyright }}.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
</div>
{% if pagename == 'index' %}
</div>
{% endif %}
{%- endblock %}

19
docs/_themes/flask/relations.html vendored Normal file
View File

@@ -0,0 +1,19 @@
<h3>Related Topics</h3>
<ul>
<li><a href="{{ pathto(master_doc) }}">Documentation overview</a><ul>
{%- for parent in parents %}
<li><a href="{{ parent.link|e }}">{{ parent.title }}</a><ul>
{%- endfor %}
{%- if prev %}
<li>Previous: <a href="{{ prev.link|e }}" title="{{ _('previous chapter')
}}">{{ prev.title }}</a></li>
{%- endif %}
{%- if next %}
<li>Next: <a href="{{ next.link|e }}" title="{{ _('next chapter')
}}">{{ next.title }}</a></li>
{%- endif %}
{%- for parent in parents %}
</ul></li>
{%- endfor %}
</ul></li>
</ul>

394
docs/_themes/flask/static/flasky.css_t vendored Normal file
View File

@@ -0,0 +1,394 @@
/*
* flasky.css_t
* ~~~~~~~~~~~~
*
* :copyright: Copyright 2010 by Armin Ronacher.
* :license: Flask Design License, see LICENSE for details.
*/
{% set page_width = '940px' %}
{% set sidebar_width = '220px' %}
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: 'Georgia', serif;
font-size: 17px;
background-color: white;
color: #000;
margin: 0;
padding: 0;
}
div.document {
width: {{ page_width }};
margin: 30px auto 0 auto;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 {{ sidebar_width }};
}
div.sphinxsidebar {
width: {{ sidebar_width }};
}
hr {
border: 1px solid #B1B4B6;
}
div.body {
background-color: #ffffff;
color: #3E4349;
padding: 0 30px 0 30px;
}
img.floatingflask {
padding: 0 0 10px 10px;
float: right;
}
div.footer {
width: {{ page_width }};
margin: 20px auto 30px auto;
font-size: 14px;
color: #888;
text-align: right;
}
div.footer a {
color: #888;
}
div.related {
display: none;
}
div.sphinxsidebar a {
color: #444;
text-decoration: none;
border-bottom: 1px dotted #999;
}
div.sphinxsidebar a:hover {
border-bottom: 1px solid #999;
}
div.sphinxsidebar {
font-size: 14px;
line-height: 1.5;
}
div.sphinxsidebarwrapper {
padding: 18px 10px;
}
div.sphinxsidebarwrapper p.logo {
padding: 0 0 20px 0;
margin: 0;
text-align: center;
}
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: 'Garamond', 'Georgia', serif;
color: #444;
font-size: 24px;
font-weight: normal;
margin: 0 0 5px 0;
padding: 0;
}
div.sphinxsidebar h4 {
font-size: 20px;
}
div.sphinxsidebar h3 a {
color: #444;
}
div.sphinxsidebar p.logo a,
div.sphinxsidebar h3 a,
div.sphinxsidebar p.logo a:hover,
div.sphinxsidebar h3 a:hover {
border: none;
}
div.sphinxsidebar p {
color: #555;
margin: 10px 0;
}
div.sphinxsidebar ul {
margin: 10px 0;
padding: 0;
color: #000;
}
div.sphinxsidebar input {
border: 1px solid #ccc;
font-family: 'Georgia', serif;
font-size: 1em;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #004B6B;
text-decoration: underline;
}
a:hover {
color: #6D4100;
text-decoration: underline;
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
margin: 30px 0px 10px 0px;
padding: 0;
}
{% if theme_index_logo %}
div.indexwrapper h1 {
text-indent: -999999px;
background: url({{ theme_index_logo }}) no-repeat center center;
height: {{ theme_index_logo_height }};
}
{% endif %}
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: #ddd;
padding: 0 4px;
text-decoration: none;
}
a.headerlink:hover {
color: #444;
}
div.body p, div.body dd, div.body li {
line-height: 1.4em;
}
div.admonition {
background: #fafafa;
margin: 20px -30px;
padding: 10px 30px;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
div.admonition tt.xref, div.admonition a tt {
border-bottom: 1px solid #fafafa;
}
dd div.admonition {
margin-left: -60px;
padding-left: 60px;
}
div.admonition p.admonition-title {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
font-size: 24px;
margin: 0 0 10px 0;
padding: 0;
line-height: 1;
}
div.admonition p.last {
margin-bottom: 0;
}
div.highlight {
background-color: white;
}
dt:target, .highlight {
background: #FAF3E8;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre, tt {
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
font-size: 0.9em;
}
img.screenshot {
}
tt.descname, tt.descclassname {
font-size: 0.95em;
}
tt.descname {
padding-right: 0.08em;
}
img.screenshot {
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils {
border: 1px solid #888;
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils td, table.docutils th {
border: 1px solid #888;
padding: 0.25em 0.7em;
}
table.field-list, table.footnote {
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
table.footnote {
margin: 15px 0;
width: 100%;
border: 1px solid #eee;
background: #fdfdfd;
font-size: 0.9em;
}
table.footnote + table.footnote {
margin-top: -15px;
border-top: none;
}
table.field-list th {
padding: 0 0.8em 0 0;
}
table.field-list td {
padding: 0;
}
table.footnote td.label {
width: 0px;
padding: 0.3em 0 0.3em 0.5em;
}
table.footnote td {
padding: 0.3em 0.5em;
}
dl {
margin: 0;
padding: 0;
}
dl dd {
margin-left: 30px;
}
blockquote {
margin: 0 0 0 30px;
padding: 0;
}
ul, ol {
margin: 10px 0 10px 30px;
padding: 0;
}
pre {
background: #eee;
padding: 7px 30px;
margin: 15px -30px;
line-height: 1.3em;
}
dl pre, blockquote pre, li pre {
margin-left: -60px;
padding-left: 60px;
}
dl dl pre {
margin-left: -90px;
padding-left: 90px;
}
tt {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
}
tt.xref, a tt {
background-color: #FBFBFB;
border-bottom: 1px solid white;
}
a.reference {
text-decoration: none;
border-bottom: 1px dotted #004B6B;
}
a.reference:hover {
border-bottom: 1px solid #6D4100;
}
a.footnote-reference {
text-decoration: none;
font-size: 0.7em;
vertical-align: top;
border-bottom: 1px dotted #004B6B;
}
a.footnote-reference:hover {
border-bottom: 1px solid #6D4100;
}
a:hover tt {
background: #EEE;
}

View File

@@ -0,0 +1,70 @@
/*
* small_flask.css_t
* ~~~~~~~~~~~~~~~~~
*
* :copyright: Copyright 2010 by Armin Ronacher.
* :license: Flask Design License, see LICENSE for details.
*/
body {
margin: 0;
padding: 20px 30px;
}
div.documentwrapper {
float: none;
background: white;
}
div.sphinxsidebar {
display: block;
float: none;
width: 102.5%;
margin: 50px -30px -20px -30px;
padding: 10px 20px;
background: #333;
color: white;
}
div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
div.sphinxsidebar h3 a {
color: white;
}
div.sphinxsidebar a {
color: #aaa;
}
div.sphinxsidebar p.logo {
display: none;
}
div.document {
width: 100%;
margin: 0;
}
div.related {
display: block;
margin: 0;
padding: 10px 0 20px 0;
}
div.related ul,
div.related ul li {
margin: 0;
padding: 0;
}
div.footer {
display: none;
}
div.bodywrapper {
margin: 0;
}
div.body {
min-height: 0;
padding: 0;
}

9
docs/_themes/flask/theme.conf vendored Normal file
View File

@@ -0,0 +1,9 @@
[theme]
inherit = basic
stylesheet = flasky.css
pygments_style = flask_theme_support.FlaskyStyle
[options]
index_logo =
index_logo_height = 120px
touch_icon =

125
docs/_themes/flask_theme_support.py vendored Normal file
View File

@@ -0,0 +1,125 @@
"""
Copyright (c) 2010 by Armin Ronacher.
Some rights reserved.
Redistribution and use in source and binary forms of the theme, with or
without modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* The names of the contributors may not be used to endorse or
promote products derived from this software without specific
prior written permission.
We kindly ask you to only use these themes in an unmodified manner just
for Flask and Flask-related products, not for unrelated projects. If you
like the visual style and want to use it for your own projects, please
consider making some larger changes to the themes (such as changing
font faces, sizes, colors or margins).
THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
"""
# flasky extensions. flasky pygments style based on tango style
from pygments.style import Style
from pygments.token import Keyword, Name, Comment, String, Error, \
Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
class FlaskyStyle(Style):
background_color = "#f8f8f8"
default_style = ""
styles = {
# No corresponding class for the following:
#Text: "", # class: ''
Whitespace: "underline #f8f8f8", # class: 'w'
Error: "#a40000 border:#ef2929", # class: 'err'
Other: "#000000", # class 'x'
Comment: "italic #8f5902", # class: 'c'
Comment.Preproc: "noitalic", # class: 'cp'
Keyword: "bold #004461", # class: 'k'
Keyword.Constant: "bold #004461", # class: 'kc'
Keyword.Declaration: "bold #004461", # class: 'kd'
Keyword.Namespace: "bold #004461", # class: 'kn'
Keyword.Pseudo: "bold #004461", # class: 'kp'
Keyword.Reserved: "bold #004461", # class: 'kr'
Keyword.Type: "bold #004461", # class: 'kt'
Operator: "#582800", # class: 'o'
Operator.Word: "bold #004461", # class: 'ow' - like keywords
Punctuation: "bold #000000", # class: 'p'
# because special names such as Name.Class, Name.Function, etc.
# are not recognized as such later in the parsing, we choose them
# to look the same as ordinary variables.
Name: "#000000", # class: 'n'
Name.Attribute: "#c4a000", # class: 'na' - to be revised
Name.Builtin: "#004461", # class: 'nb'
Name.Builtin.Pseudo: "#3465a4", # class: 'bp'
Name.Class: "#000000", # class: 'nc' - to be revised
Name.Constant: "#000000", # class: 'no' - to be revised
Name.Decorator: "#888", # class: 'nd' - to be revised
Name.Entity: "#ce5c00", # class: 'ni'
Name.Exception: "bold #cc0000", # class: 'ne'
Name.Function: "#000000", # class: 'nf'
Name.Property: "#000000", # class: 'py'
Name.Label: "#f57900", # class: 'nl'
Name.Namespace: "#000000", # class: 'nn' - to be revised
Name.Other: "#000000", # class: 'nx'
Name.Tag: "bold #004461", # class: 'nt' - like a keyword
Name.Variable: "#000000", # class: 'nv' - to be revised
Name.Variable.Class: "#000000", # class: 'vc' - to be revised
Name.Variable.Global: "#000000", # class: 'vg' - to be revised
Name.Variable.Instance: "#000000", # class: 'vi' - to be revised
Number: "#990000", # class: 'm'
Literal: "#000000", # class: 'l'
Literal.Date: "#000000", # class: 'ld'
String: "#4e9a06", # class: 's'
String.Backtick: "#4e9a06", # class: 'sb'
String.Char: "#4e9a06", # class: 'sc'
String.Doc: "italic #8f5902", # class: 'sd' - like a comment
String.Double: "#4e9a06", # class: 's2'
String.Escape: "#4e9a06", # class: 'se'
String.Heredoc: "#4e9a06", # class: 'sh'
String.Interpol: "#4e9a06", # class: 'si'
String.Other: "#4e9a06", # class: 'sx'
String.Regex: "#4e9a06", # class: 'sr'
String.Single: "#4e9a06", # class: 's1'
String.Symbol: "#4e9a06", # class: 'ss'
Generic: "#000000", # class: 'g'
Generic.Deleted: "#a40000", # class: 'gd'
Generic.Emph: "italic #000000", # class: 'ge'
Generic.Error: "#ef2929", # class: 'gr'
Generic.Heading: "bold #000080", # class: 'gh'
Generic.Inserted: "#00A000", # class: 'gi'
Generic.Output: "#888", # class: 'go'
Generic.Prompt: "#745334", # class: 'gp'
Generic.Strong: "bold #000000", # class: 'gs'
Generic.Subheading: "bold #800080", # class: 'gu'
Generic.Traceback: "bold #a40000", # class: 'gt'
}

279
docs/conf.py Normal file
View File

@@ -0,0 +1,279 @@
# -*- coding: utf-8 -*-
#
# Jedi documentation build configuration file, created by
# sphinx-quickstart on Wed Dec 26 00:11:34 2012.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os, datetime
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('..'))
sys.path.insert(0, os.path.abspath('../jedi'))
sys.path.append(os.path.abspath('_themes'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.todo',
'sphinx.ext.intersphinx', 'sphinx.ext.inheritance_diagram']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
source_encoding = 'utf-8'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'Jedi'
copyright = u'2012 - {today.year}, Jedi contributors'.format(today=datetime.date.today())
import jedi
_path = os.path.dirname(os.path.abspath(__file__))
_version_strs = [str(x) for x in jedi.__version__]
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '.'.join(_version_strs[:2])
# The full version, including alpha/beta/rc tags.
release = '.'.join(_version_strs)
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = []
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'flask'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = ['_themes']
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
html_sidebars = {
'**': [
'sidebarlogo.html',
'localtoc.html',
# 'relations.html',
'ghbuttons.html',
# 'sourcelink.html',
# 'searchbox.html'
]
}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'Jedidoc'
#html_style = 'default.css' # Force usage of default template on RTD
# -- Options for LaTeX output --------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'Jedi.tex', u'Jedi Documentation',
u'Jedi contributors', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'jedi', u'Jedi Documentation',
[u'Jedi contributors'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output ------------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'Jedi', u'Jedi Documentation',
u'Jedi contributors', 'Jedi', 'Awesome Python autocompletion library.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# -- Options for todo module ---------------------------------------------------
todo_include_todos = False
# -- Options for autodoc module ------------------------------------------------
autoclass_content = 'both'
autodoc_member_order = 'bysource'
autodoc_default_flags = []
#autodoc_default_flags = ['members', 'undoc-members']
# -- Options for intersphinx module --------------------------------------------
intersphinx_mapping = {
'http://docs.python.org/': None,
}

239
docs/docs/development.rst Normal file
View File

@@ -0,0 +1,239 @@
.. include:: ../global.rst
Jedi Development
================
.. currentmodule:: jedi
.. note:: This documentation is for Jedi developers who want to improve Jedi
itself, but have no idea how Jedi works. If you want to use Jedi for
your IDE, look at the `plugin api <plugin-api.html>`_.
Introduction
------------
This page tries to address the fundamental demand for documentation of the
|jedi| interals. Understanding a dynamic language is a complex task. Especially
because type inference in Python can be a very recursive task. Therefore |jedi|
couldn't get rid of complexity. I know that **simple is better than complex**,
but unfortunately it sometimes requires complex solutions to understand complex
systems.
Since most of the Jedi internals have been written by me (David Halter), this
introduction will be written mostly by me, because no one else understands to
the same level how Jedi works. Actually this is also the reason for exactly this
part of the documentation. To make multiple people able to edit the Jedi core.
In five chapters I'm trying to describe the internals of |jedi|:
- :ref:`The Jedi Core <core>`
- :ref:`Core Extensions <core-extensions>`
- :ref:`Imports & Modules <imports-modules>`
- :ref:`Caching & Recursions <caching-recursions>`
- :ref:`Helper modules <dev-helpers>`
.. note:: Testing is not documented here, you'll find that
`right here <testing.html>`_.
.. _core:
The Jedi Core
-------------
The core of Jedi consists of three parts:
- :ref:`Parser <parsing>`
- :ref:`Python code evaluation <evaluate>`
- :ref:`API <dev-api>`
Most people are probably interested in :ref:`code evaluation <evaluate>`,
because that's where all the magic happens. I need to introduce the :ref:`parser
<parsing>` first, because :mod:`evaluate` uses it extensively.
.. _parsing:
Parser (parsing.py)
~~~~~~~~~~~~~~~~~~~
.. automodule:: parsing
Parser Representation (parser_representation.py)
++++++++++++++++++++++++++++++++++++++++++++++++
.. automodule:: parsing_representation
Class inheritance diagram:
.. inheritance-diagram::
SubModule
Class
Function
Lambda
Flow
ForFlow
Import
Statement
Param
Call
Array
Name
ListComprehension
:parts: 1
.. _evaluate:
Evaluation of python code (evaluate.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: evaluate
Evaluation Representation (evaluate_representation.py)
++++++++++++++++++++++++++++++++++++++++++++++++++++++
.. automodule:: evaluate_representation
.. inheritance-diagram::
Executable
Instance
InstanceElement
Class
Function
Execution
Generator
Array
:parts: 1
.. _dev-api:
API (api.py and api_classes.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The API has been designed to be as easy to use as possible. The API
documentation can be found `here <plugin-api.html>`_. The API itself contains
little code that needs to be mentioned here. Generally I'm trying to be
conservative with the API. I'd rather not add new API features if they are not
necessary, because it's much harder to deprecate stuff than to add it later.
.. _core-extensions:
Core Extensions
---------------
Core Extensions is a summary of the following topics:
- :ref:`Dynamic Arrays & Function Parameters <dynamic>`
- :ref:`Fast Parser <fast_parser>`
- :ref:`Docstrings <docstrings>`
- :ref:`Refactoring <refactoring>`
These topics are very important to understand what Jedi additionally does, but
they could be removed from Jedi and Jedi would still work. But slower and
without some features.
.. _dynamic:
Dynamic Arrays & Function Parameters (dynamic.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: dynamic
.. _fast_parser:
Fast Parser (fast_parser.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: fast_parser
.. _docstrings:
Docstrings (docstrings.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: docstrings
.. _refactoring:
Refactoring (refactoring.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: refactoring
.. _imports-modules:
Imports & Modules
-------------------
- :ref:`Modules <modules>`
- :ref:`Builtin Modules <builtin>`
- :ref:`Imports <imports>`
.. _modules:
Modules (modules.py)
~~~~~~~~~~~~~~~~~~~~
.. automodule:: modules
.. _builtin:
Builtin Modules (builtin.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: builtin
.. _imports:
Imports (imports.py)
~~~~~~~~~~~~~~~~~~~~
.. automodule:: imports
.. _caching-recursions:
Caching & Recursions
----------------------
- :ref:`Caching <cache>`
- :ref:`Recursions <recursion>`
.. _cache:
Caching (cache.py)
~~~~~~~~~~~~~~~~~~
.. automodule:: cache
.. _recursion:
Recursions (recursion.py)
~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: recursion
.. _dev-helpers:
Helper Modules
---------------
There are some helper modules: `common.py`, `helpers.py`, `debug.py`,
`keywords.py`, which I won't describe further. Some functions are inheritely
important and central for Jedi to work, but not important to understand how
Jedi works.
Python 2/3 compatibility (_compatibility.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: _compatibility

98
docs/docs/features.rst Normal file
View File

@@ -0,0 +1,98 @@
.. include:: ../global.rst
Features and Caveats
====================
|jedi| supports many of the widely used Python features:
General Features
----------------
- python 2.6+ and 3.2+ support
- ignores syntax errors and wrong indentation
- can deal with complex module / function / class structures
- virtualenv support
- can infer function arguments from sphinx and epydoc docstrings (:ref:`type
hinting <type-hinting>`)
Supported Python Features
-------------------------
- builtins
- multiple returns or yields
- tuple assignments / array indexing / dictionary indexing
- with-statement / exception handling
- ``*args`` / ``**kwargs``
- decorators / lambdas / closures
- generators / iterators
- some descriptors: property / staticmethod / classmethod
- some magic methods: ``__call__``, ``__iter__``, ``__next__``, ``__get__``,
``__getitem__``, ``__init__``
- ``list.append()``, ``set.add()``, ``list.extend()``, etc.
- (nested) list comprehensions / ternary expressions
- relative imports
- ``getattr()`` / ``__getattr__`` / ``__getattribute__``
- function annotations (py3k feature, are ignored right now, but being parsed.
I don't know what to do with them.)
- class decorators (py3k feature, are being ignored too, until I find a use
case, that doesn't work with |jedi|)
- simple/usual ``sys.path`` modifications
- ``isinstance`` checks for if/while/assert
- namespace packages (includes ``pkgutil`` and ``pkg_resources`` namespaces)
Unsupported Features
--------------------
Not yet implemented:
- manipulations of instances outside the instance variables without using
methods
Will probably never be implemented:
- metaclasses (how could an auto-completion ever support this)
- ``setattr()``, ``__import__()``
- writing to some dicts: ``globals()``, ``locals()``, ``object.__dict__``
- evaluating ``if`` / ``while``
Caveats
-------
**Malformed Syntax**
Syntax errors and other strange stuff may lead to undefined behaviour of the
completion. |jedi| is **NOT** a Python compiler, that tries to correct you. It
is a tool that wants to help you. But **YOU** have to know Python, not |jedi|.
**Legacy Python 2 Features**
This framework should work for both Python 2/3. However, some things were just
not as *pythonic* in Python 2 as things should be. To keep things simple, some
older Python 2 features have been left out:
- Classes: Always Python 3 like, therefore all classes inherit from ``object``.
- Generators: No ``next()`` method. The ``__next__()`` method is used instead.
**Slow Performance**
Importing ``numpy`` can be quite slow sometimes, as well as loading the
builtins the first time. If you want to speed things up, you could write import
hooks in |jedi|, which preload stuff. However, once loaded, this is not a
problem anymore. The same is true for huge modules like ``PySide``, ``wx``,
etc.
**Security**
Security is an important issue for |jedi|. Therefore no Python code is
executed. As long as you write pure python, everything is evaluated
statically. But: If you use builtin modules (``c_builtin``) there is no other
option than to execute those modules. However: Execute isn't that critical (as
e.g. in pythoncomplete, which used to execute *every* import!), because it
means one import and no more. So basically the only dangerous thing is using
the import itself. If your ``c_builtin`` uses some strange initializations, it
might be dangerous. But if it does you're screwed anyways, because eventualy
you're going to execute your code, which executes the import.

31
docs/docs/history.rst Normal file
View File

@@ -0,0 +1,31 @@
.. include:: ../global.rst
A little history
================
The Star Wars Jedi are awesome. My Jedi software tries to imitate a little bit
of the precognition the Jedi have. There's even an awesome `scene
<http://www.youtube.com/watch?v=5BDO3pyavOY>`_ of Monty Python Jedis :-).
But actually the name hasn't so much to do with Star Wars. It's part of my
second name.
After I explained Guido van Rossum, how some parts of my auto-completion work,
he said (we drank a beer or two):
*"Oh, that worries me..."*
When it's finished, I hope he'll like it :-)
I actually started Jedi, because there were no good solutions available for VIM.
Most auto-completions just didn't work well. The only good solution was PyCharm.
But I like my good old VIM. Rope was never really intended to be an
auto-completion (and also I really hate project folders for my Python scripts).
It's more of a refactoring suite. So I decided to do my own version of a
completion, which would execute non-dangerous code. But I soon realized, that
this wouldn't work. So I built an extremely recursive thing which understands
many of Python's key features.
By the way, I really tried to program it as understandable as possible. But I
think understanding it might need quite some time, because of its recursive
nature.

View File

@@ -0,0 +1,81 @@
.. include:: ../global.rst
Installation and Configuration
==============================
You can either include |jedi| as a submodule in your text editor plugin (like
jedi-vim_ does by default), or you can install it systemwide.
.. note:: This just installs the |jedi| library, not the :ref:`editor plugins
<editor-plugins>`. For information about how to make it work with your
editor, refer to the corresponding documentation.
The preferred way
-----------------
On any system you can install |jedi| directly from the Python package index
using pip::
sudo pip install jedi
If you want to install the current development version (master branch)::
sudo pip install -e git://github.com/davidhalter/jedi.git#egg=jedi
System-wide installation via a package manager
----------------------------------------------
Arch Linux
~~~~~~~~~~
You can install |jedi| directly from official AUR packages:
- `python-jedi <https://aur.archlinux.org/packages/python-jedi/>`__ (Python 3)
- `python2-jedi <https://aur.archlinux.org/packages/python2-jedi/>`__ (Python 2)
The specified Python version just refers to the *runtime environment* for
|jedi|. Use the Python 2 version if you're running vim (or whatever editor you
use) under Python 2. Otherwise, use the Python 3 version. But whatever version
you choose, both are able to complete both Python 2 and 3 *code*.
(There is also a packaged version of the vim plugin available: `vim-jedi at AUR
<https://aur.archlinux.org/packages/vim-jedi/>`__.)
Debian
~~~~~~
Debian packages are available as `experimental packages
<http://packages.debian.org/experimental/python-jedi>`__.
Others
~~~~~~
We are in the discussion of adding |jedi| to the Fedora repositories.
Manual installation from a downloaded package
---------------------------------------------
If you prefer not to use an automated package installer, you can `download
<https://github.com/davidhalter/jedi/archive/master.zip>`__ a current copy of
|jedi| and install it manually.
To install it, navigate to the directory containing `setup.py` on your console
and type::
sudo python setup.py install
Inclusion as a submodule
------------------------
If you use an editor plugin like jedi-vim_, you can simply include |jedi| as a
git submodule of the plugin directory. Vim plugin managers like Vundle_ or
Pathogen_ make it very easy to keep submodules up to date.
.. _jedi-vim: https://github.com/davidhalter/jedi-vim
.. _vundle: https://github.com/gmarik/vundle
.. _pathogen: https://github.com/tpope/vim-pathogen

101
docs/docs/plugin-api.rst Normal file
View File

@@ -0,0 +1,101 @@
.. include:: ../global.rst
The Plugin API
==============
.. currentmodule:: jedi
Note: This documentation is for Plugin developers, who want to improve their
editors/IDE autocompletion
If you want to use |jedi|, you first need to
``import jedi``. You then have direct access to the :class:`.Script`.
API documentation
-----------------
API Interface
~~~~~~~~~~~~~
.. automodule:: api
:members:
:undoc-members:
API Return Classes
~~~~~~~~~~~~~~~~~~
.. automodule:: api_classes
:members:
:undoc-members:
Settings Module
~~~~~~~~~~~~~~~
.. automodule:: settings
Examples
--------
Completions:
.. sourcecode:: python
>>> import jedi
>>> source = '''import json; json.l'''
>>> script = jedi.Script(source, 1, 19, '')
>>> script
<jedi.api.Script object at 0x2121b10>
>>> completions = script.complete()
>>> completions
[<Completion: load>, <Completion: loads>]
>>> completions[1]
<Completion: loads>
>>> completions[1].complete
'oads'
>>> completions[1].word
'loads'
Definitions / Goto:
.. sourcecode:: python
>>> import jedi
>>> source = '''def my_func():
... print 'called'
...
... alias = my_func
... my_list = [1, None, alias]
... inception = my_list[2]
...
... inception()'''
>>> script = jedi.Script(source, 8, 1, '')
>>>
>>> script.goto()
[<Definition inception=my_list[2]>]
>>>
>>> script.get_definition()
[<Definition def my_func>]
Related names:
.. sourcecode:: python
>>> import jedi
>>> source = '''x = 3
... if 1 == 2:
... x = 4
... else:
... del x'''
>>> script = jedi.Script(source, 5, 8, '')
>>> rns = script.related_names()
>>> rns
[<RelatedName x@3,4>, <RelatedName x@1,0>]
>>> rns[0].start_pos
(3, 4)
>>> rns[0].is_keyword
False
>>> rns[0].text
'x'

44
docs/docs/recipes.rst Normal file
View File

@@ -0,0 +1,44 @@
.. include:: ../global.rst
Recipes
=======
Here are some tips on how to use |jedi| efficiently.
.. _type-hinting:
Type Hinting
------------
If |jedi| cannot detect the type of a function argument correctly (due to the
dynamic nature of Python), you can help it by hinting the type using
Sphinx-style info field lists or Epydoc docstrings.
**Sphinx style**
http://sphinx-doc.org/domains.html#info-field-lists
::
def myfunction(node):
"""Do something with a ``node``.
:type node: ProgramNode
"""
node.| # complete here
**Epydoc**
http://epydoc.sourceforge.net/manual-fields.html
::
def myfunction(node):
"""Do something with a ``node``.
@param node: ProgramNode
"""
node.| # complete here

18
docs/docs/repl.rst Normal file
View File

@@ -0,0 +1,18 @@
.. include:: ../global.rst
Tab completion in the Python Shell
==================================
There are two different options how you can use Jedi autocompletion in
your Python interpreter. One with your custom ``$HOME/.pythonrc.py`` file
and one that uses ``PYTHONSTARTUP``.
Using ``PYTHONSTARTUP``
-----------------------
.. automodule:: jedi.replstartup
Using a custom ``$HOME/.pythonrc.py``
-------------------------------------
.. autofunction:: jedi.utils.setup_readline

41
docs/docs/testing.rst Normal file
View File

@@ -0,0 +1,41 @@
.. include:: ../global.rst
Jedi Testing
============
The test suite depends on ``tox`` and ``pytest``::
pip install tox pytest
To run the tests for all supported Python versions::
tox
If you want to test only a specific Python version (e.g. Python 2.7), it's as
easy as::
tox -e py27
Tests are also run automatically on `Travis CI
<https://travis-ci.org/davidhalter/jedi/>`_.
You want to add a test for |jedi|? Great! We love that. Normally you should
write your tests as :ref:`Blackbox Tests <blackbox>`. Most tests would
fit right in there.
.. _blackbox:
Blackbox Tests (run.py)
~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: test.run
Regression Tests (test_regression.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: test.test_regression
Refactoring Tests (refactor.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: test.refactor

3
docs/global.rst Normal file
View File

@@ -0,0 +1,3 @@
:orphan:
.. |jedi| replace:: *Jedi*

68
docs/index.rst Normal file
View File

@@ -0,0 +1,68 @@
.. include global.rst
Jedi - an awesome autocompletion library for Python
===================================================
Release v\ |release|. (:doc:`Installation <docs/installation>`)
.. automodule:: jedi
Autocompletion can look like this (e.g. VIM plugin):
.. figure:: _screenshots/screenshot_complete.png
.. _toc:
Docs
----
.. toctree::
:maxdepth: 1
docs/installation
docs/features
docs/repl
docs/recipes
docs/plugin-api
docs/history
docs/development
docs/testing
.. _resources:
Resources
---------
- `Source Code on Github <https://github.com/davidhalter/jedi>`_
- `Travis Testing <https://travis-ci.org/davidhalter/jedi>`_
- `Python Package Index <http://pypi.python.org/pypi/jedi/>`_
.. _editor-plugins:
Editor Plugins
--------------
Vim:
- `jedi-vim <http://github.com/davidhalter/jedi-vim>`_
- `YouCompleteMe <http://valloric.github.io/YouCompleteMe/>`_
Emacs:
- `Jedi.el <https://github.com/tkf/emacs-jedi>`_
Sublime Text 2/3:
- `SublimeJEDI <https://github.com/srusskih/SublimeJEDI>`_ (ST2 & ST3)
- `anaconda <https://github.com/DamnWidget/anaconda>`_ (only ST3)
.. _other-software:
Other Software Using Jedi
-------------------------
- `wdb <https://github.com/Kozea/wdb>`_

View File

@@ -1,16 +1,49 @@
"""
Jedi is an autocompletion tool for Python that can be used in IDEs/editors.
Jedi works. Jedi is fast. It understands all of the basic Python syntax
elements including many builtin functions.
Additionaly, Jedi suports two different goto functions and has support for
renaming as well as Pydoc support and some other IDE features.
Jedi uses a very simple API to connect with IDE's. There's a reference
implementation as a `VIM-Plugin <http://github.com/davidhalter/jedi-vim>`_,
which uses Jedi's autocompletion. I encourage you to use Jedi in your IDEs.
It's really easy. If there are any problems (also with licensing), just contact
me.
To give you a simple example how you can use the Jedi library, here is an
example for the autocompletion feature:
>>> import jedi
>>> source = '''
... import datetime
... datetime.da'''
>>> script = jedi.Script(source, 3, len('datetime.da'), 'example.py')
>>> script
<Script: 'example.py'>
>>> completions = script.completions()
>>> completions #doctest: +ELLIPSIS
[<Completion: date>, <Completion: datetime>, ...]
>>> print(completions[0].complete)
te
>>> print(completions[0].name)
date
As you see Jedi is pretty simple and allows you to concentrate on writing a
good text editor, while still having very good IDE features for Python.
"""
__version__ = 0, 7, 0
import sys
# python imports are hell sometimes. Especially the combination of relative
# imports and circular imports... Just avoid it:
sys.path.insert(0, __path__[0])
from .api import Script, NotFoundError, set_debug_function
from .api import Script, Interpreter, NotFoundError, set_debug_function
from .api import preload_module, defined_names
from . import settings
from . import api
__doc__ = api.__doc__
del api
sys.path.pop(0)

7
jedi/__main__.py Normal file
View File

@@ -0,0 +1,7 @@
from sys import argv
if len(argv) == 2 and argv[1] == 'repl':
# don't want to use __main__ only for repl yet, maybe we want to use it for
# something else. So just use the keyword ``repl`` for now.
from os import path
print(path.join(path.dirname(path.abspath(__file__)), 'replstartup.py'))

View File

@@ -1,12 +1,60 @@
"""
This is a compatibility module, to make it possible to use jedi also with older
python versions.
To ensure compatibility from Python ``2.6`` - ``3.3``, a module has been
created. Clearly there is huge need to use conforming syntax. But many changes
(e.g. ``property``, ``hasattr`` in ``2.5``) can be rewritten in pure python.
"""
import sys
import imp
import os
try:
import importlib
except:
pass
is_py3k = sys.hexversion >= 0x03000000
is_py33 = sys.hexversion >= 0x03030000
is_py25 = sys.hexversion < 0x02060000
def find_module_py33(string, path=None):
loader = importlib.machinery.PathFinder.find_module(string, path)
if loader is None and path is None: # Fallback to find builtins
loader = importlib.find_loader(string)
if loader is None:
raise ImportError("Couldn't find a loader for {0}".format(string))
try:
is_package = loader.is_package(string)
if is_package:
module_path = os.path.dirname(loader.path)
module_file = None
else:
module_path = loader.get_filename(string)
module_file = open(module_path)
except AttributeError:
module_path = loader.load_module(string).__name__
module_file = None
return module_file, module_path, is_package
def find_module_pre_py33(string, path=None):
module_file, module_path, description = imp.find_module(string, path)
module_type = description[2]
return module_file, module_path, module_type is imp.PKG_DIRECTORY
find_module = find_module_py33 if is_py33 else find_module_pre_py33
find_module.__doc__ = """
Provides information about a module.
This function isolates the differences in importing libraries introduced with
python 3.3 on; it gets a module name and optionally a path. It will return a
tuple containin an open file for the module (if not builtin), the filename
or the name of the module if it is a builtin one and a boolean indicating
if the module is contained in a package.
"""
# next was defined in python 2.6, in python 3 obj.next won't be possible
# anymore
@@ -26,34 +74,6 @@ except NameError:
else:
return default
# ast module was defined in python 2.6
try:
from ast import literal_eval
except ImportError:
literal_eval = eval
# properties in 2.5
try:
property.setter
except AttributeError:
class property(property):
def __init__(self, fget, *args, **kwargs):
self.__doc__ = fget.__doc__
super(property, self).__init__(fget, *args, **kwargs)
def setter(self, fset):
cls_ns = sys._getframe(1).f_locals
for k, v in cls_ns.iteritems():
if v == self:
propname = k
break
cls_ns[propname] = property(self.fget, fset,
self.fdel, self.__doc__)
return cls_ns[propname]
else:
property = property
# unicode function
try:
unicode = unicode
@@ -77,6 +97,25 @@ else:
eval(compile("""def exec_function(source, global_map):
exec source in global_map """, 'blub', 'exec'))
# re-raise function
if is_py3k:
def reraise(exception, traceback):
raise exception.with_traceback(traceback)
else:
eval(compile("""
def reraise(exception, traceback):
raise exception, None, traceback
""", 'blub', 'exec'))
reraise.__doc__ = """
Re-raise `exception` with a `traceback` object.
Usage::
reraise(Exception, sys.exc_info()[2])
"""
# StringIO (Python 2.5 has no io module), so use io only for py3k
try:
from StringIO import StringIO
@@ -105,13 +144,6 @@ class Python3Method(object):
else:
return lambda *args, **kwargs: self.func(obj, *args, **kwargs)
try:
# the python3 way
from functools import reduce
except ImportError:
reduce = reduce
def use_metaclass(meta, *bases):
""" Create a class with a metaclass. """
if not bases:
@@ -119,51 +151,30 @@ def use_metaclass(meta, *bases):
return meta("HackClass", bases, {})
try:
from inspect import cleandoc
from functools import reduce # Python 3
except ImportError:
# python 2.5 doesn't have this method
import string
reduce = reduce
def cleandoc(doc):
"""Clean up indentation from docstrings.
try:
encoding = sys.stdout.encoding
if encoding is None:
encoding = 'utf-8'
except AttributeError:
encoding = 'ascii'
Any whitespace that can be uniformly removed from the second line
onwards is removed."""
try:
lines = string.split(string.expandtabs(doc), '\n')
except UnicodeError:
return None
else:
# Find minimum indentation of any non-blank lines after first line.
margin = sys.maxint
for line in lines[1:]:
content = len(string.lstrip(line))
if content:
indent = len(line) - content
margin = min(margin, indent)
# Remove indentation.
if lines:
lines[0] = lines[0].lstrip()
if margin < sys.maxint:
for i in range(1, len(lines)):
lines[i] = lines[i][margin:]
# Remove any trailing or leading blank lines.
while lines and not lines[-1]:
lines.pop()
while lines and not lines[0]:
lines.pop(0)
return string.join(lines, '\n')
def u(string):
"""Cast to unicode DAMMIT!
Written because Python2 repr always implicitly casts to a string, so we
have to cast back to a unicode (and we now that we always deal with valid
unicode, because we check that in the beginning).
"""
if is_py3k:
return str(string)
elif not isinstance(string, unicode):
return unicode(str(string), 'UTF-8')
return string
if is_py25:
# adds the `itertools.chain.from_iterable` constructor
import itertools
class chain(itertools.chain):
@staticmethod
def from_iterable(iterables):
# chain.from_iterable(['ABC', 'DEF']) --> A B C D E F
for it in iterables:
for element in it:
yield element
itertools.chain = chain
del chain
try:
import builtins # module name in python 3
except ImportError:
import __builtin__ as builtins

View File

@@ -1,119 +1,171 @@
"""
Jedi is an autocompletion library for Python. It offers additonal
services such as goto / get_definition / pydoc support /
get_in_function_call / related names.
The API basically only provides one class. You can create a :class:`Script` and
use its methods.
To give you a simple exmple how you can use the jedi library,
here is an exmple for the autocompletion feature:
>>> import jedi
>>> source = '''import json; json.l'''
>>> script = jedi.Script(source, 1, 19, '')
>>> script
<jedi.api.Script at 0x7f6d40f3db90>
>>> completions = script.complete()
>>> completions
[<Completion: load>, <Completion: loads>]
>>> completions[0].complete
'oad'
>>> completions[0].word
'load'
As you see Jedi is pretty simple and allows you to concentrate
writing a good text editor, while still having very good IDE features
for Python.
Additionally you can add a debug function with :func:`set_debug_function` and
catch :exc:`NotFoundError` which is being raised if your completion is not
possible.
"""
from __future__ import with_statement
__all__ = ['Script', 'NotFoundError', 'set_debug_function']
import re
import os
import warnings
from itertools import chain
import parsing
from jedi import parsing
from jedi import parsing_representation as pr
from jedi import debug
from jedi import settings
from jedi import helpers
from jedi import common
from jedi import cache
from jedi import modules
from jedi import interpret
from jedi._compatibility import next, unicode, builtins
import keywords
import evaluate
import api_classes
import evaluate_representation as er
import dynamic
import imports
import evaluate
import modules
import debug
import settings
import keywords
import helpers
import builtin
import api_classes
from _compatibility import next, unicode
class NotFoundError(Exception):
""" A custom error to avoid catching the wrong exceptions """
pass
"""A custom error to avoid catching the wrong exceptions."""
class Script(object):
"""
A Script is the base for a completion, goto or whatever call.
A Script is the base for completions, goto or whatever you want to do with
|jedi|.
:param source: The source code of the current file
:type source: string
:param line: The line to complete in.
:param source: The source code of the current file, separated by newlines.
:type source: str
:param line: The line to perform actions on (starting with 1).
:type line: int
:param col: The column to complete in.
:param col: The column of the cursor (starting with 0).
:type col: int
:param source_path: The path in the os, the current module is in.
:type source_path: string or None
:param source_encoding: encoding for decoding `source`, when it
is not a `unicode` object.
:type source_encoding: string
:param path: The path of the file in the file system, or ``''`` if
it hasn't been saved yet.
:type path: str or None
:param source_encoding: The encoding of ``source``, if it is not a
``unicode`` object (default ``'utf-8'``).
:type source_encoding: str
"""
def __init__(self, source, line, column, source_path,
source_encoding='utf-8'):
def __init__(self, source, line=None, column=None, path=None,
source_encoding='utf-8', source_path=None):
if source_path is not None:
warnings.warn("Use path instead of source_path.", DeprecationWarning)
path = source_path
lines = source.splitlines()
if source and source[-1] == '\n':
lines.append('')
self._line = max(len(lines), 1) if line is None else line
self._column = len(lines[-1]) if column is None else column
api_classes._clear_caches()
debug.reset_time()
try:
source = unicode(source, source_encoding, 'replace')
# Use 'replace' over 'ignore' to hold code structure.
except TypeError: # `source` is already a unicode object
pass
self.pos = line, column
self.module = modules.ModuleWithCursor(source_path, source=source,
position=self.pos)
self.source_path = source_path
self.source = modules.source_to_unicode(source, source_encoding)
self._pos = self._line, self._column
self._module = modules.ModuleWithCursor(
path, source=self.source, position=self._pos)
self._source_path = path
self.path = None if path is None else os.path.abspath(path)
debug.speed('init')
@property
def parser(self):
""" The lazy parser """
return self.module.parser
def complete(self):
def source_path(self):
"""
An auto completer for python files.
:return: list of Completion objects, sorted by name and __ comes last.
:rtype: list
.. deprecated:: 0.7.0
Use :attr:`.path` instead.
.. todo:: Remove!
"""
def follow_imports_if_possible(name):
# TODO remove this, or move to another place (not used)
par = name.parent
if isinstance(par, parsing.Import) and not \
isinstance(self.parser.user_stmt, parsing.Import):
new = imports.ImportPath(par).follow(is_goto=True)
# Only remove the old entry if a new one has been found.
#print par, new, par.parent
if new:
try:
return new
except AttributeError: # .name undefined
pass
return [name]
warnings.warn("Use path instead of source_path.", DeprecationWarning)
return self.path
path = self.module.get_path_until_cursor()
path, dot, like = self._get_completion_parts(path)
def __repr__(self):
return '<%s: %s>' % (self.__class__.__name__, repr(self._source_path))
@property
def _parser(self):
""" lazy parser."""
return self._module.parser
@api_classes._clear_caches_after_call
def completions(self):
"""
Return :class:`api_classes.Completion` objects. Those objects contain
information about the completions, more than just names.
:return: Completion objects, sorted by name and __ comes last.
:rtype: list of :class:`api_classes.Completion`
"""
def get_completions(user_stmt, bs):
if isinstance(user_stmt, pr.Import):
context = self._module.get_context()
next(context) # skip the path
if next(context) == 'from':
# completion is just "import" if before stands from ..
return ((k, bs) for k in keywords.keyword_names('import'))
return self._simple_complete(path, like)
debug.speed('completions start')
path = self._module.get_path_until_cursor()
if re.search('^\.|\.\.$', path):
return []
path, dot, like = self._get_completion_parts()
user_stmt = self._user_stmt(True)
bs = builtin.Builtin.scope
completions = get_completions(user_stmt, bs)
if not dot: # named params have no dots
for call_def in self.call_signatures():
if not call_def.module.is_builtin():
for p in call_def.params:
completions.append((p.get_name(), p))
if not path and not isinstance(user_stmt, pr.Import):
# add keywords
completions += ((k, bs) for k in keywords.keyword_names(
all=True))
needs_dot = not dot and path
comps = []
comp_dct = {}
for c, s in set(completions):
n = c.names[-1]
if settings.case_insensitive_completion \
and n.lower().startswith(like.lower()) \
or n.startswith(like):
if not evaluate.filter_private_variable(s,
user_stmt or self._parser.user_scope, n):
new = api_classes.Completion(c, needs_dot, len(like), s)
k = (new.name, new.complete) # key
if k in comp_dct and settings.no_completion_duplicates:
comp_dct[k]._same_name_completions.append(new)
else:
comp_dct[k] = new
comps.append(new)
debug.speed('completions end')
return sorted(comps, key=lambda x: (x.name.startswith('__'),
x.name.startswith('_'),
x.name.lower()))
def _simple_complete(self, path, like):
try:
scopes = list(self._prepare_goto(path, True))
except NotFoundError:
scopes = []
scope_generator = evaluate.get_names_for_scope(
self.parser.user_scope, self.pos)
scope_generator = evaluate.get_names_of_scope(
self._parser.user_scope, self._pos)
completions = []
for scope, name_list in scope_generator:
for c in name_list:
@@ -122,68 +174,55 @@ class Script(object):
completions = []
debug.dbg('possible scopes', scopes)
for s in scopes:
if s.isinstance(evaluate.Function):
if s.isinstance(er.Function):
names = s.get_magic_method_names()
else:
if isinstance(s, imports.ImportPath):
if like == 'import':
l = self.module.get_line(self.pos[0])[:self.pos[1]]
if not l.endswith('import import'):
under = like + self._module.get_path_after_cursor()
if under == 'import':
current_line = self._module.get_position_line()
if not current_line.endswith('import import'):
continue
a = s.import_stmt.alias
if a and a.start_pos <= self._pos <= a.end_pos:
continue
names = s.get_defined_names(on_import_stmt=True)
else:
names = s.get_defined_names()
for c in names:
completions.append((c, s))
return completions
if not dot: # named_params have no dots
call_def = self.get_in_function_call()
if call_def:
if not call_def.module.is_builtin():
for p in call_def.params:
completions.append((p.get_name(), p))
def _user_stmt(self, is_completion=False):
user_stmt = self._parser.user_stmt
debug.speed('parsed')
# Do the completion if there is no path before and no import stmt.
if (not scopes or not isinstance(scopes[0], imports.ImportPath)) \
and not path:
# add keywords
bs = builtin.Builtin.scope
completions += ((k, bs) for k in keywords.get_keywords(
all=True))
if is_completion and not user_stmt:
# for statements like `from x import ` (cursor not in statement)
pos = next(self._module.get_context(yield_positions=True))
last_stmt = pos and self._parser.module.get_statement_for_position(
pos, include_imports=True)
if isinstance(last_stmt, pr.Import):
user_stmt = last_stmt
return user_stmt
needs_dot = not dot and path
def _prepare_goto(self, goto_path, is_completion=False):
"""
Base for completions/goto. Basically it returns the resolved scopes
under cursor.
"""
debug.dbg('start: %s in %s' % (goto_path, self._parser.user_scope))
comps = []
for c, s in set(completions):
n = c.names[-1]
if settings.case_insensitive_completion \
and n.lower().startswith(like.lower()) \
or n.startswith(like):
if not evaluate.filter_private_variable(s,
self.parser.user_stmt, n):
new = api_classes.Completion(c, needs_dot,
len(like), s)
comps.append(new)
return sorted(comps, key=lambda x: (x.word.startswith('__'),
x.word.lower()))
def _prepare_goto(self, goto_path, is_like_search=False):
""" Base for complete, goto and get_definition. Basically it returns
the resolved scopes under cursor. """
debug.dbg('start: %s in %s' % (goto_path, self.parser.scope))
user_stmt = self.parser.user_stmt
user_stmt = self._user_stmt(is_completion)
if not user_stmt and len(goto_path.split('\n')) > 1:
# If the user_stmt is not defined and the goto_path is multi line,
# something's strange. Most probably the backwards tokenizer
# matched to much.
return []
if isinstance(user_stmt, parsing.Import):
scopes = [self._get_on_import_stmt(is_like_search)[0]]
if isinstance(user_stmt, pr.Import):
scopes = [self._get_on_import_stmt(user_stmt, is_completion)[0]]
else:
# just parse one statement, take it and evaluate it
stmt = self._get_under_cursor_stmt(goto_path)
@@ -191,28 +230,90 @@ class Script(object):
return scopes
def _get_under_cursor_stmt(self, cursor_txt):
r = parsing.PyFuzzyParser(cursor_txt, self.source_path, no_docstr=True)
offset = self._line - 1, self._column
r = parsing.Parser(cursor_txt, no_docstr=True, offset=offset)
try:
stmt = r.module.statements[0]
except IndexError:
raise NotFoundError()
stmt.start_pos = self.pos
stmt.parent = self.parser.user_scope
stmt.parent = self._parser.user_scope
return stmt
def complete(self):
"""
.. deprecated:: 0.6.0
Use :attr:`.completions` instead.
.. todo:: Remove!
"""
warnings.warn("Use completions instead.", DeprecationWarning)
return self.completions()
def goto(self):
"""
.. deprecated:: 0.6.0
Use :attr:`.goto_assignments` instead.
.. todo:: Remove!
"""
warnings.warn("Use goto_assignments instead.", DeprecationWarning)
return self.goto_assignments()
def definition(self):
"""
.. deprecated:: 0.6.0
Use :attr:`.goto_definitions` instead.
.. todo:: Remove!
"""
warnings.warn("Use goto_definitions instead.", DeprecationWarning)
return self.goto_definitions()
def get_definition(self):
"""
Returns the definitions of a the path under the cursor. This is
not a goto function! This follows complicated paths and returns the
end, not the first definition.
The big difference of goto and get_definition is that goto doesn't
follow imports and statements.
Multiple objects may be returned, because Python itself is a dynamic
language, which means depending on an option you can have two different
versions of a function.
.. deprecated:: 0.5.0
Use :attr:`.goto_definitions` instead.
.. todo:: Remove!
"""
warnings.warn("Use goto_definitions instead.", DeprecationWarning)
return self.goto_definitions()
:return: list of Definition objects, which are basically scopes.
:rtype: list
def related_names(self):
"""
.. deprecated:: 0.6.0
Use :attr:`.usages` instead.
.. todo:: Remove!
"""
warnings.warn("Use usages instead.", DeprecationWarning)
return self.usages()
def get_in_function_call(self):
"""
.. deprecated:: 0.6.0
Use :attr:`.call_signatures` instead.
.. todo:: Remove!
"""
return self.function_definition()
def function_definition(self):
"""
.. deprecated:: 0.6.0
Use :attr:`.call_signatures` instead.
.. todo:: Remove!
"""
warnings.warn("Use line instead.", DeprecationWarning)
sig = self.call_signatures()
return sig[0] if sig else None
@api_classes._clear_caches_after_call
def goto_definitions(self):
"""
Return the definitions of a the path under the cursor. goto function!
This follows complicated paths and returns the end, not the first
definition. The big difference between :meth:`goto_assignments` and
:meth:`goto_definitions` is that :meth:`goto_assignments` doesn't
follow imports and statements. Multiple objects may be returned,
because Python itself is a dynamic language, which means depending on
an option you can have two different versions of a function.
:rtype: list of :class:`api_classes.Definition`
"""
def resolve_import_paths(scopes):
for s in scopes.copy():
@@ -221,43 +322,70 @@ class Script(object):
scopes.update(resolve_import_paths(set(s.follow())))
return scopes
goto_path = self.module.get_path_under_cursor()
goto_path = self._module.get_path_under_cursor()
context = self.module.get_context()
context = self._module.get_context()
scopes = set()
lower_priority_operators = ('()', '(', ',')
"""Operators that could hide callee."""
if next(context) in ('class', 'def'):
scopes = set([self.module.parser.user_scope])
scopes = set([self._module.parser.user_scope])
elif not goto_path:
op = self.module.get_operator_under_cursor()
scopes = set([keywords.get_operator(op, self.pos)] if op else [])
else:
scopes = set(self._prepare_goto(goto_path))
op = self._module.get_operator_under_cursor()
if op and op not in lower_priority_operators:
scopes = set([keywords.get_operator(op, self._pos)])
# Fetch definition of callee
if not goto_path:
(call, _) = self._func_call_and_param_index()
if call is not None:
while call.next is not None:
call = call.next
# reset cursor position:
(row, col) = call.name.end_pos
_pos = (row, max(col - 1, 0))
self._module = modules.ModuleWithCursor(
self._source_path,
source=self.source,
position=_pos)
# then try to find the path again
goto_path = self._module.get_path_under_cursor()
if not scopes:
if goto_path:
scopes = set(self._prepare_goto(goto_path))
elif op in lower_priority_operators:
scopes = set([keywords.get_operator(op, self._pos)])
scopes = resolve_import_paths(scopes)
# add keywords
scopes |= keywords.get_keywords(string=goto_path, pos=self.pos)
scopes |= keywords.keywords(string=goto_path, pos=self._pos)
d = set([api_classes.Definition(s) for s in scopes
if not isinstance(s, imports.ImportPath._GlobalNamespace)])
return sorted(d, key=lambda x: (x.module_path, x.start_pos))
if not isinstance(s, imports.ImportPath._GlobalNamespace)])
return self._sorted_defs(d)
def goto(self):
@api_classes._clear_caches_after_call
def goto_assignments(self):
"""
Returns the first definition found by goto. This means: It doesn't
follow imports and statements.
Multiple objects may be returned, because Python itself is a dynamic
language, which means depending on an option you can have two different
versions of a function.
Return the first definition found. Imports and statements aren't
followed. Multiple objects may be returned, because Python itself is a
dynamic language, which means depending on an option you can have two
different versions of a function.
:return: list of Definition objects, which are basically scopes.
:rtype: list of :class:`api_classes.Definition`
"""
d = [api_classes.Definition(d) for d in set(self._goto()[0])]
return sorted(d, key=lambda x: (x.module_path, x.start_pos))
results, _ = self._goto()
d = [api_classes.Definition(d) for d in set(results)
if not isinstance(d, imports.ImportPath._GlobalNamespace)]
return self._sorted_defs(d)
def _goto(self, add_import_name=False):
"""
Used for goto and related_names.
:param add_import_name: TODO add description
Used for goto_assignments and usages.
:param add_import_name: Add the the name (if import) to the result.
"""
def follow_inexistent_imports(defs):
""" Imports can be generated, e.g. following
@@ -266,152 +394,136 @@ class Script(object):
"""
definitions = set(defs)
for d in defs:
if isinstance(d.parent, parsing.Import) \
and d.start_pos == (0, 0):
if isinstance(d.parent, pr.Import) \
and d.start_pos == (0, 0):
i = imports.ImportPath(d.parent).follow(is_goto=True)
definitions.remove(d)
definitions |= follow_inexistent_imports(i)
return definitions
goto_path = self.module.get_path_under_cursor()
context = self.module.get_context()
goto_path = self._module.get_path_under_cursor()
context = self._module.get_context()
user_stmt = self._user_stmt()
if next(context) in ('class', 'def'):
user_scope = self.parser.user_scope
user_scope = self._parser.user_scope
definitions = set([user_scope.name])
search_name = str(user_scope.name)
elif isinstance(self.parser.user_stmt, parsing.Import):
s, name_part = self._get_on_import_stmt()
search_name = unicode(user_scope.name)
elif isinstance(user_stmt, pr.Import):
s, name_part = self._get_on_import_stmt(user_stmt)
try:
definitions = [s.follow(is_goto=True)[0]]
except IndexError:
definitions = []
search_name = str(name_part)
search_name = unicode(name_part)
if add_import_name:
import_name = self.parser.user_stmt.get_defined_names()
import_name = user_stmt.get_defined_names()
# imports have only one name
if name_part == import_name[0].names[-1]:
if not user_stmt.star \
and name_part == import_name[0].names[-1]:
definitions.append(import_name[0])
else:
stmt = self._get_under_cursor_stmt(goto_path)
defs, search_name = evaluate.goto(stmt)
definitions = follow_inexistent_imports(defs)
if isinstance(user_stmt, pr.Statement):
c = user_stmt.get_commands()
if c and not isinstance(c[0], (str, unicode)) and \
c[0].start_pos > self._pos:
# The cursor must be after the start, otherwise the
# statement is just an assignee.
definitions = [user_stmt]
return definitions, search_name
def related_names(self, additional_module_paths=[]):
@api_classes._clear_caches_after_call
def usages(self, additional_module_paths=()):
"""
Returns `dynamic.RelatedName` objects, which contain all names, that
are defined by the same variable, function, class or import.
This function can be used either to show all the usages of a variable
or for renaming purposes.
Return :class:`api_classes.Usage` objects, which contain all
names that point to the definition of the name under the cursor. This
is very useful for refactoring (renaming), or to show all usages of a
variable.
TODO implement additional_module_paths
.. todo:: Implement additional_module_paths
:rtype: list of :class:`api_classes.Usage`
"""
user_stmt = self.parser.user_stmt
temp, settings.dynamic_flow_information = \
settings.dynamic_flow_information, False
user_stmt = self._user_stmt()
definitions, search_name = self._goto(add_import_name=True)
if isinstance(user_stmt, parsing.Statement) \
and self.pos < user_stmt.get_assignment_calls().start_pos:
# the search_name might be before `=`
definitions = [v for v in user_stmt.set_vars
if str(v) == search_name]
if not isinstance(user_stmt, parsing.Import):
if isinstance(user_stmt, pr.Statement):
c = user_stmt.get_commands()[0]
if not isinstance(c, unicode) and self._pos < c.start_pos:
# the search_name might be before `=`
definitions = [v for v in user_stmt.set_vars
if unicode(v.names[-1]) == search_name]
if not isinstance(user_stmt, pr.Import):
# import case is looked at with add_import_name option
definitions = dynamic.related_name_add_import_modules(definitions,
search_name)
definitions = dynamic.usages_add_import_modules(definitions,
search_name)
module = set([d.get_parent_until() for d in definitions])
module.add(self.parser.module)
names = dynamic.related_names(definitions, search_name, module)
module.add(self._parser.module)
names = dynamic.usages(definitions, search_name, module)
for d in set(definitions):
if isinstance(d, parsing.Module):
names.append(api_classes.RelatedName(d, d))
if isinstance(d, pr.Module):
names.append(api_classes.Usage(d, d))
elif isinstance(d, er.Instance):
# Instances can be ignored, because they are being created by
# ``__getattr__``.
pass
else:
names.append(api_classes.RelatedName(d.names[0], d))
names.append(api_classes.Usage(d.names[-1], d))
return sorted(set(names), key=lambda x: (x.module_path, x.start_pos),
reverse=True)
settings.dynamic_flow_information = temp
return self._sorted_defs(set(names))
def get_in_function_call(self):
@api_classes._clear_caches_after_call
def call_signatures(self):
"""
Return the function, that the cursor is in, e.g.:
>>> isinstance(| # | <-- cursor is here
Return the function object of the call you're currently in.
This would return the `isinstance` function. In contrary:
>>> isinstance()| # | <-- cursor is here
E.g. if the cursor is here::
This would return `None`.
abs(# <-- cursor is here
This would return the ``abs`` function. On the other hand::
abs()# <-- cursor is here
This would return ``None``.
:rtype: :class:`api_classes.CallDef`
"""
def check_user_stmt(user_stmt):
if user_stmt is None \
or not isinstance(user_stmt, parsing.Statement):
return None, 0
ass = helpers.fast_parent_copy(user_stmt.get_assignment_calls())
call, index, stop = helpers.scan_array_for_pos(ass, self.pos)
return call, index
def check_cache():
""" Do the parsing with a part parser, therefore reduce ressource
costs.
TODO this is not working with multi-line docstrings, improve.
"""
if self.source_path is None:
return None, 0
try:
timestamp, parser = builtin.CachedModule.cache[
self.source_path]
except KeyError:
return None, 0
part_parser = self.module.get_part_parser()
user_stmt = part_parser.user_stmt
call, index = check_user_stmt(user_stmt)
if call:
old_stmt = parser.module.get_statement_for_position(self.pos)
if old_stmt is None:
return None, 0
old_call, old_index = check_user_stmt(old_stmt)
if old_call:
# compare repr because that should definitely be the same.
# Otherwise the whole thing is out of sync.
if repr(old_call) == repr(call):
# return the index of the part_parser
return old_call, index
return None, 0
else:
raise NotFoundError()
debug.speed('func_call start')
try:
call, index = check_cache()
except NotFoundError:
return None
debug.speed('func_call parsed')
call, index = self._func_call_and_param_index()
if call is None:
# This is a backup, if the above is not successful.
user_stmt = self.parser.user_stmt
call, index = check_user_stmt(user_stmt)
if call is None:
return None
return []
debug.speed('func_call user_stmt')
with helpers.scale_speed_settings(settings.scale_get_in_function_call):
origins = evaluate.follow_call(call)
user_stmt = self._user_stmt()
with common.scale_speed_settings(settings.scale_function_definition):
_callable = lambda: evaluate.follow_call(call)
origins = cache.cache_function_definition(_callable, user_stmt)
debug.speed('func_call followed')
if len(origins) == 0:
return None
# just take entry zero, because we need just one.
executable = origins[0]
return [api_classes.CallDef(o, index, call) for o in origins
if o.isinstance(er.Function, er.Instance, er.Class)]
return api_classes.CallDef(executable, index, call)
def _func_call_and_param_index(self):
debug.speed('func_call start')
call, index = None, 0
if call is None:
user_stmt = self._user_stmt()
if user_stmt is not None and isinstance(user_stmt, pr.Statement):
call, index, _ = helpers.search_function_definition(
user_stmt, self._pos)
debug.speed('func_call parsed')
return call, index
def _get_on_import_stmt(self, is_like_search=False):
def _get_on_import_stmt(self, user_stmt, is_like_search=False):
""" Resolve the user statement, if it is an import. Only resolve the
parts until the user position. """
user_stmt = self.parser.user_stmt
import_names = user_stmt.get_all_import_names()
kill_count = -1
cur_name_part = None
@@ -419,31 +531,155 @@ class Script(object):
if user_stmt.alias == i:
continue
for name_part in i.names:
if name_part.end_pos >= self.pos:
if name_part.end_pos >= self._pos:
if not cur_name_part:
cur_name_part = name_part
kill_count += 1
context = self._module.get_context()
just_from = next(context) == 'from'
i = imports.ImportPath(user_stmt, is_like_search,
kill_count=kill_count, direct_resolve=True)
kill_count=kill_count, direct_resolve=True,
is_just_from=just_from)
return i, cur_name_part
def _get_completion_parts(self, path):
def _get_completion_parts(self):
"""
Returns the parts for the completion
:return: tuple - (path, dot, like)
"""
path = self._module.get_path_until_cursor()
match = re.match(r'^(.*?)(\.|)(\w?[\w\d]*)$', path, flags=re.S)
return match.groups()
def __del__(self):
evaluate.clear_caches()
@staticmethod
def _sorted_defs(d):
# Note: `or ''` below is required because `module_path` could be
# None and you can't compare None and str in Python 3.
return sorted(d, key=lambda x: (x.module_path or '', x.line, x.column))
class Interpreter(Script):
"""
Jedi API for Python REPLs.
In addition to completion of simple attribute access, Jedi
supports code completion based on static code analysis.
Jedi can complete attributes of object which is not initialized
yet.
>>> from os.path import join
>>> namespace = locals()
>>> script = Interpreter('join().up', [namespace])
>>> print(script.completions()[0].name)
upper
"""
def __init__(self, source, namespaces=[], **kwds):
"""
Parse `source` and mixin interpreted Python objects from `namespaces`.
:type source: str
:arg source: Code to parse.
:type namespaces: list of dict
:arg namespaces: a list of namespace dictionaries such as the one
returned by :func:`locals`.
Other optional arguments are same as the ones for :class:`Script`.
If `line` and `column` are None, they are assumed be at the end of
`source`.
"""
super(Interpreter, self).__init__(source, **kwds)
self.namespaces = namespaces
# Here we add the namespaces to the current parser.
importer = interpret.ObjectImporter(self._parser.user_scope)
for ns in namespaces:
importer.import_raw_namespace(ns)
def _simple_complete(self, path, like):
user_stmt = self._user_stmt(True)
is_simple_path = not path or re.search('^[\w][\w\d.]*$', path)
if isinstance(user_stmt, pr.Import) or not is_simple_path:
return super(type(self), self)._simple_complete(path, like)
else:
class NamespaceModule:
def __getattr__(_, name):
for n in self.namespaces:
try:
return n[name]
except KeyError:
pass
raise AttributeError()
def __dir__(_):
return list(set(chain.from_iterable(n.keys()
for n in self.namespaces)))
paths = path.split('.') if path else []
namespaces = (NamespaceModule(), builtins)
for p in paths:
old, namespaces = namespaces, []
for n in old:
try:
namespaces.append(getattr(n, p))
except AttributeError:
pass
completions = []
for n in namespaces:
for name in dir(n):
if name.lower().startswith(like.lower()):
scope = self._parser.module
n = pr.Name(self._parser.module, [(name, (0, 0))],
(0, 0), (0, 0), scope)
completions.append((n, scope))
return completions
def defined_names(source, path=None, source_encoding='utf-8'):
"""
Get all definitions in `source` sorted by its position.
This functions can be used for listing functions, classes and
data defined in a file. This can be useful if you want to list
them in "sidebar". Each element in the returned list also has
`defined_names` method which can be used to get sub-definitions
(e.g., methods in class).
:rtype: list of api_classes.Definition
"""
parser = parsing.Parser(
modules.source_to_unicode(source, source_encoding),
module_path=path,
)
return api_classes._defined_names(parser.module)
def preload_module(*modules):
"""
Preloading modules tells Jedi to load a module now, instead of lazy parsing
of modules. Usful for IDEs, to control which modules to load on startup.
:param modules: different module names, list of string.
"""
for m in modules:
s = "import %s as x; x." % m
Script(s, 1, len(s), None).completions()
def set_debug_function(func_cb=debug.print_to_stdout, warnings=True,
notices=True, speed=True):
notices=True, speed=True):
"""
You can define a callback debug function to get all the debug messages.
Define a callback debug function to get all the debug messages.
:param func_cb: The callback function for debug messages, with n params.
"""
debug.debug_function = func_cb

View File

@@ -1,26 +1,67 @@
""" The classes returned by the api """
"""
The :mod:`api_classes` module contains the return classes of the API. These
classes are the much bigger part of the whole API, because they contain the
interesting information about completion and goto operations.
"""
from __future__ import with_statement
import re
import os
import warnings
import functools
import settings
from jedi._compatibility import unicode, next
from jedi import settings
from jedi import common
from jedi import parsing_representation as pr
from jedi import cache
import keywords
import recursion
import dynamic
import evaluate
import imports
import parsing
import keywords
import evaluate_representation as er
def _clear_caches():
"""
Clear all caches of this and related modules. The only cache that will not
be deleted is the module cache.
"""
cache.clear_caches()
dynamic.search_param_cache.clear()
recursion.ExecutionRecursionDecorator.reset()
evaluate.follow_statement.reset()
imports.imports_processed = 0
def _clear_caches_after_call(func):
"""
Clear caches just before returning a value.
"""
@functools.wraps(func)
def wrapper(*args, **kwds):
result = func(*args, **kwds)
_clear_caches()
return result
return wrapper
class BaseDefinition(object):
_mapping = {'posixpath': 'os.path',
'riscospath': 'os.path',
'ntpath': 'os.path',
'os2emxpath': 'os.path',
'macpath': 'os.path',
'genericpath': 'os.path',
'_io': 'io',
'__builtin__': '',
'builtins': '',
}
_mapping = {
'posixpath': 'os.path',
'riscospath': 'os.path',
'ntpath': 'os.path',
'os2emxpath': 'os.path',
'macpath': 'os.path',
'genericpath': 'os.path',
'posix': 'os',
'_io': 'io',
'_functools': 'functools',
'_sqlite3': 'sqlite3',
'__builtin__': '',
'builtins': '',
}
_tuple_mapping = dict((tuple(k.split('.')), v) for (k, v) in {
'argparse._ActionsContainer': 'argparse.ArgumentParser',
@@ -29,83 +70,228 @@ class BaseDefinition(object):
}.items())
def __init__(self, definition, start_pos):
self.start_pos = start_pos
self.definition = definition
self._start_pos = start_pos
self._definition = definition
"""
An instance of :class:`jedi.parsing_representation.Base` subclass.
"""
self.is_keyword = isinstance(definition, keywords.Keyword)
# generate a path to the definition
self.module_path = str(definition.get_parent_until().path)
self._module = definition.get_parent_until()
self.module_path = self._module.path
@property
def start_pos(self):
"""
.. deprecated:: 0.7.0
Use :attr:`.line` and :attr:`.column` instead.
.. todo:: Remove!
"""
warnings.warn("Use line/column instead.", DeprecationWarning)
return self._start_pos
@property
def type(self):
"""
The type of the definition.
Here is an example of the value of this attribute. Let's consider
the following source. As what is in ``variable`` is unambiguous
to Jedi, :meth:`api.Script.goto_definitions` should return a list of
definition for ``sys``, ``f``, ``C`` and ``x``.
>>> from jedi import Script
>>> source = '''
... import keyword
...
... class C:
... pass
...
... class D:
... pass
...
... x = D()
...
... def f():
... pass
...
... variable = keyword or f or C or x'''
>>> script = Script(source, len(source.splitlines()), 3, 'example.py')
>>> defs = script.goto_definitions()
Before showing what is in ``defs``, let's sort it by :attr:`line`
so that it is easy to relate the result to the source code.
>>> defs = sorted(defs, key=lambda d: d.line)
>>> defs # doctest: +NORMALIZE_WHITESPACE
[<Definition module keyword>, <Definition class C>,
<Definition class D>, <Definition def f>]
Finally, here is what you can get from :attr:`type`:
>>> defs[0].type
'module'
>>> defs[1].type
'class'
>>> defs[2].type
'instance'
>>> defs[3].type
'function'
"""
# generate the type
stripped = self.definition
if isinstance(self.definition, evaluate.InstanceElement):
stripped = self.definition.var
return type(stripped).__name__
stripped = self._definition
if isinstance(self._definition, er.InstanceElement):
stripped = self._definition.var
if isinstance(stripped, pr.Name):
stripped = stripped.parent
return type(stripped).__name__.lower()
@property
def path(self):
"""The module path."""
path = []
if not isinstance(self.definition, keywords.Keyword):
par = self.definition
def insert_nonnone(x):
if x:
path.insert(0, x)
if not isinstance(self._definition, keywords.Keyword):
par = self._definition
while par is not None:
try:
if isinstance(par, pr.Import):
insert_nonnone(par.namespace)
insert_nonnone(par.from_ns)
if par.relative_count == 0:
break
with common.ignored(AttributeError):
path.insert(0, par.name)
except AttributeError:
pass
par = par.parent
return path
@property
def module_name(self):
path = self.module_path
sep = os.path.sep
p = re.sub(r'^.*?([\w\d]+)(%s__init__)?.py$' % sep, r'\1', path)
return p
"""
The module name.
>>> from jedi import Script
>>> source = 'import datetime'
>>> script = Script(source, 1, len(source), 'example.py')
>>> d = script.goto_definitions()[0]
>>> print(d.module_name) # doctest: +ELLIPSIS
datetime
"""
return str(self._module.name)
def in_builtin_module(self):
return not self.module_path.endswith('.py')
"""Whether this is a builtin module."""
return not (self.module_path is None or
self.module_path.endswith('.py'))
@property
def line_nr(self):
return self.start_pos[0]
"""
.. deprecated:: 0.5.0
Use :attr:`.line` instead.
.. todo:: Remove!
"""
warnings.warn("Use line instead.", DeprecationWarning)
return self.line
@property
def line(self):
"""The line where the definition occurs (starting with 1)."""
if self.in_builtin_module():
return None
return self._start_pos[0]
@property
def column(self):
return self.start_pos[1]
"""The column where the definition occurs (starting with 0)."""
if self.in_builtin_module():
return None
return self._start_pos[1]
@property
def doc(self):
""" Return a document string for this completion object. """
r"""
Return a document string for this completion object.
Example:
>>> from jedi import Script
>>> source = '''\
... def f(a, b=1):
... "Document for function f."
... '''
>>> script = Script(source, 1, len('def f'), 'example.py')
>>> d = script.goto_definitions()[0]
>>> print(d.doc)
f(a, b = 1)
<BLANKLINE>
Document for function f.
Notice that useful extra information is added to the actual
docstring. For function, it is call signature. If you need
actual docstring, use :attr:`raw_doc` instead.
>>> print(d.raw_doc)
Document for function f.
"""
try:
return self.definition.doc
return self._definition.doc
except AttributeError:
return self.raw_doc
@property
def raw_doc(self):
""" Returns the raw docstring `__doc__` for any object """
"""
The raw docstring ``__doc__`` for any object.
See :attr:`doc` for example.
"""
try:
return str(self.definition.docstr)
return unicode(self._definition.docstr)
except AttributeError:
return ''
@property
def description(self):
return str(self.definition)
"""A textual description of the object."""
return unicode(self._definition)
@property
def full_name(self):
"""
Returns the path to a certain class/function, see #61.
Dot-separated path of this object.
It is in the form of ``<module>[.<submodule>[...]][.<object>]``.
It is useful when you want to look up Python manual of the
object at hand.
Example:
>>> from jedi import Script
>>> source = '''
... import os
... os.path.join'''
>>> script = Script(source, 3, len('os.path.join'), 'example.py')
>>> print(script.goto_definitions()[0].full_name)
os.path.join
Notice that it correctly returns ``'os.path.join'`` instead of
(for example) ``'posixpath.join'``.
"""
path = [str(p) for p in self.path]
path = [unicode(p) for p in self.path]
# TODO add further checks, the mapping should only occur on stdlib.
try:
if not path:
return None # for keywords the path is empty
with common.ignored(KeyError):
path[0] = self._mapping[path[0]]
except KeyError:
pass
for key, repl in self._tuple_mapping.items():
if tuple(path[:len(key)]) == key:
path = [repl] + path[len(key):]
@@ -117,173 +303,308 @@ class BaseDefinition(object):
class Completion(BaseDefinition):
""" `Completion` objects are returned from `Script.complete`. Providing
some useful functions for IDE's. """
"""
`Completion` objects are returned from :meth:`api.Script.completions`. They
provide additional information about a completion.
"""
def __init__(self, name, needs_dot, like_name_length, base):
super(Completion, self).__init__(name.parent, name.start_pos)
self.name = name
self.needs_dot = needs_dot
self.like_name_length = like_name_length
self.base = base
self._name = name
self._needs_dot = needs_dot
self._like_name_length = like_name_length
self._base = base
# Completion objects with the same Completion name (which means
# duplicate items in the completion)
self._same_name_completions = []
self._followed_definitions = None
@property
def complete(self):
""" Delievers the rest of the word, e.g. completing `isinstance`
>>> isinstan
would return the string 'ce'. It also adds additional stuff, depending
on your `settings.py`
"""
dot = '.' if self.needs_dot else ''
def _complete(self, like_name):
dot = '.' if self._needs_dot else ''
append = ''
if settings.add_bracket_after_function \
and self.type == 'Function':
and self.type == 'Function':
append = '('
if settings.add_dot_after_module:
if isinstance(self.base, parsing.Module):
if isinstance(self._base, pr.Module):
append += '.'
if isinstance(self.base, parsing.Param):
if isinstance(self._base, pr.Param):
append += '='
return dot + self.name.names[-1][self.like_name_length:] + append
name = self._name.names[-1]
if like_name:
name = name[self._like_name_length:]
return dot + name + append
@property
def complete(self):
"""
Return the rest of the word, e.g. completing ``isinstance``::
isinstan# <-- Cursor is here
would return the string 'ce'. It also adds additional stuff, depending
on your `settings.py`.
"""
return self._complete(True)
@property
def name(self):
"""
Similar to :meth:`Completion.complete`, but return the whole word, for
example::
isinstan
would return `isinstance`.
"""
return unicode(self._name.names[-1])
@property
def name_with_symbols(self):
"""
Similar to :meth:`Completion.name`, but like :meth:`Completion.name`
returns also the symbols, for example::
list()
would return ``.append`` and others (which means it adds a dot).
"""
return self._complete(False)
@property
def word(self):
""" In contrary to `complete` returns the whole word, e.g.
>>> isinstan
would return 'isinstance'.
"""
return str(self.name.names[-1])
.. deprecated:: 0.6.0
Use :attr:`.name` instead.
.. todo:: Remove!
"""
warnings.warn("Use name instead.", DeprecationWarning)
return self.name
@property
def description(self):
""" Provides a description of the completion object
TODO return value is just __repr__ of some objects, improve! """
parent = self.name.parent
"""Provide a description of the completion object."""
parent = self._name.parent
if parent is None:
return ''
t = self.type
if t == 'Statement' or t == 'Import':
desc = self.definition.get_code(False)
if t == 'statement' or t == 'import':
desc = self._definition.get_code(False)
else:
desc = '.'.join(str(p) for p in self.path)
desc = '.'.join(unicode(p) for p in self.path)
line_nr = '' if self.in_builtin_module else '@%s' % self.line_nr
return '%s: %s%s' % (t, desc, line_nr)
line = '' if self.in_builtin_module else '@%s' % self.line
return '%s: %s%s' % (t, desc, line)
def follow_definition(self):
""" Returns you the original definitions. I strongly recommend not
using it for your completions, because it might slow down Jedi. If you
want to read only a few objects (<=20). I think it might be useful,
especially to get the original docstrings.
The basic problem of this function is that it follows all results. This
means with 1000 completions (e.g. numpy), it's just PITA slow.
"""
Return the original definitions. I strongly recommend not using it for
your completions, because it might slow down |jedi|. If you want to
read only a few objects (<=20), it might be useful, especially to get
the original docstrings. The basic problem of this function is that it
follows all results. This means with 1000 completions (e.g. numpy),
it's just PITA-slow.
"""
if self._followed_definitions is None:
if self.definition.isinstance(parsing.Statement):
defs = evaluate.follow_statement(self.definition)
elif self.definition.isinstance(parsing.Import):
defs = imports.strip_imports([self.definition])
if self._definition.isinstance(pr.Statement):
defs = evaluate.follow_statement(self._definition)
elif self._definition.isinstance(pr.Import):
defs = imports.strip_imports([self._definition])
else:
return [self]
self._followed_definitions = \
[BaseDefinition(d, d.start_pos) for d in defs]
evaluate.clear_caches()
[BaseDefinition(d, d.start_pos) for d in defs]
_clear_caches()
return self._followed_definitions
def __repr__(self):
return '<%s: %s>' % (type(self).__name__, self.name)
return '<%s: %s>' % (type(self).__name__, self._name)
class Definition(BaseDefinition):
""" These are the objects returned by either `Script.goto` or
`Script.get_definition`. """
"""
*Definition* objects are returned from :meth:`api.Script.goto_assignments`
or :meth:`api.Script.goto_definitions`.
"""
def __init__(self, definition):
super(Definition, self).__init__(definition, definition.start_pos)
@property
def description(self):
""" A description of the Definition object, which is heavily used in
testing. e.g. for `isinstance` it returns 'def isinstance' """
d = self.definition
if isinstance(d, evaluate.InstanceElement):
def name(self):
"""
Name of variable/function/class/module.
For example, for ``x = None`` it returns ``'x'``.
:rtype: str or None
"""
d = self._definition
if isinstance(d, er.InstanceElement):
d = d.var
if isinstance(d, evaluate.parsing.Name):
if isinstance(d, pr.Name):
return d.names[-1] if d.names else None
elif isinstance(d, er.Array):
return unicode(d.type)
elif isinstance(d, (pr.Class, er.Class, er.Instance,
er.Function, pr.Function)):
return unicode(d.name)
elif isinstance(d, pr.Module):
return self.module_name
elif isinstance(d, pr.Import):
try:
return d.get_defined_names()[0].names[-1]
except (AttributeError, IndexError):
return None
elif isinstance(d, pr.Statement):
try:
return d.assignment_details[0][1].values[0][0].name.names[-1]
except IndexError:
return None
return None
@property
def description(self):
"""
A description of the :class:`.Definition` object, which is heavily used
in testing. e.g. for ``isinstance`` it returns ``def isinstance``.
Example:
>>> from jedi import Script
>>> source = '''
... def f():
... pass
...
... class C:
... pass
...
... variable = f or C'''
>>> script = Script(source, column=3) # line is maximum by default
>>> defs = script.goto_definitions()
>>> defs = sorted(defs, key=lambda d: d.line)
>>> defs
[<Definition def f>, <Definition class C>]
>>> str(defs[0].description) # strip literals in python2
'def f'
>>> str(defs[1].description)
'class C'
"""
d = self._definition
if isinstance(d, er.InstanceElement):
d = d.var
if isinstance(d, pr.Name):
d = d.parent
if isinstance(d, evaluate.Array):
if isinstance(d, er.Array):
d = 'class ' + d.type
elif isinstance(d, (parsing.Class, evaluate.Class, evaluate.Instance)):
d = 'class ' + str(d.name)
elif isinstance(d, (evaluate.Function, evaluate.parsing.Function)):
d = 'def ' + str(d.name)
elif isinstance(d, evaluate.parsing.Module):
elif isinstance(d, (pr.Class, er.Class, er.Instance)):
d = 'class ' + unicode(d.name)
elif isinstance(d, (er.Function, pr.Function)):
d = 'def ' + unicode(d.name)
elif isinstance(d, pr.Module):
# only show module name
d = 'module %s' % self.module_name
elif self.is_keyword:
d = 'keyword %s' % d.name
else:
d = d.get_code().replace('\n', '')
code = d.get_code().replace('\n', '')
max_len = 20
d = (code[:max_len] + '...') if len(code) > max_len + 3 else code
return d
@property
def desc_with_module(self):
""" In addition to the Definition, it also returns the module. Don't
use it yet, its behaviour may change. If you really need it, talk to me
TODO add full path. This function is should return a
module.class.function path. """
"""
In addition to the definition, also return the module.
.. warning:: Don't use this function yet, its behaviour may change. If
you really need it, talk to me.
.. todo:: Add full path. This function is should return a
`module.class.function` path.
"""
if self.module_path.endswith('.py') \
and not isinstance(self.definition, parsing.Module):
position = '@%s' % (self.line_nr)
and not isinstance(self._definition, pr.Module):
position = '@%s' % (self.line)
else:
# is a builtin or module
position = ''
return "%s:%s%s" % (self.module_name, self.description, position)
def defined_names(self):
"""
List sub-definitions (e.g., methods in class).
class RelatedName(BaseDefinition):
:rtype: list of Definition
"""
d = self._definition
if isinstance(d, er.InstanceElement):
d = d.var
if isinstance(d, pr.Name):
d = d.parent
return _defined_names(d)
def _defined_names(scope):
"""
List sub-definitions (e.g., methods in class).
:type scope: Scope
:rtype: list of Definition
"""
pair = next(evaluate.get_names_of_scope(
scope, star_search=False, include_builtin=False), None)
names = pair[1] if pair else []
return [Definition(d) for d in sorted(names, key=lambda s: s.start_pos)]
class Usage(BaseDefinition):
"""TODO: document this"""
def __init__(self, name_part, scope):
super(RelatedName, self).__init__(scope, name_part.start_pos)
self.name_part = name_part
self.text = str(name_part)
super(Usage, self).__init__(scope, name_part.start_pos)
self.text = unicode(name_part)
self.end_pos = name_part.end_pos
@property
def description(self):
return "%s@%s,%s" % (self.text, self.start_pos[0], self.start_pos[1])
return "%s@%s,%s" % (self.text, self.line, self.column)
def __eq__(self, other):
return self.start_pos == other.start_pos \
return self._start_pos == other._start_pos \
and self.module_path == other.module_path
def __hash__(self):
return hash((self.start_pos, self.module_path))
return hash((self._start_pos, self.module_path))
class CallDef(object):
""" `CallDef` objects is the return value of `Script.get_in_function_call`.
"""
`CallDef` objects is the return value of `Script.function_definition`.
It knows what functions you are currently in. e.g. `isinstance(` would
return the `isinstance` function. without `(` it would return nothing."""
return the `isinstance` function. without `(` it would return nothing.
"""
def __init__(self, executable, index, call):
self.executable = executable
self._executable = executable
self.index = index
self.call = call
self._call = call
@property
def params(self):
if self.executable.isinstance(evaluate.Function):
if isinstance(self.executable, evaluate.InstanceElement):
return self.executable.params[1:]
return self.executable.params
if self._executable.isinstance(er.Function):
if isinstance(self._executable, er.InstanceElement):
return self._executable.params[1:]
return self._executable.params
else:
try:
sub = self.executable.get_subscope_by_name('__init__')
sub = self._executable.get_subscope_by_name('__init__')
return sub.params[1:] # ignore self
except KeyError:
return []
@@ -292,7 +613,7 @@ class CallDef(object):
def bracket_start(self):
""" The indent of the bracket that is responsible for the last function
call. """
c = self.call
c = self._call
while c.next is not None:
c = c.next
return c.name.end_pos
@@ -300,12 +621,12 @@ class CallDef(object):
@property
def call_name(self):
""" The name (e.g. 'isinstance') as a string. """
return str(self.executable.name)
return unicode(self._executable.name)
@property
def module(self):
return self.executable.get_parent_until()
return self._executable.get_parent_until()
def __repr__(self):
return '<%s: %s index %s>' % (type(self).__name__, self.executable,
self.index)
return '<%s: %s index %s>' % (type(self).__name__, self._executable,
self.index)

View File

@@ -1,5 +1,28 @@
"""
A big part of the Python standard libraries are unfortunately not only written
in Python. The process works like this:
- ``BuiltinModule`` imports the builtin module (e.g. ``sys``)
- then ``BuiltinModule`` generates code with the docstrings of builtin
functions.
- The :mod:`parsing` parser processes the generated code.
This is possible, because many builtin functions supply docstrings, for example
the method ``list.index`` has the following attribute ``__doc__``:
L.index(value, [start, [stop]]) -> integer -- return first index of value.
Raises ValueError if the value is not present.
`PEP 257 <http://www.python.org/dev/peps/pep-0257/#one-line-docstrings>`_
teaches how docstrings should look like for C functions.
Additionally there's a ``Builtin`` instance in this module, to make it
possible to access functions like ``list`` and ``int`` directly, the same way
|jedi| access other functions.
"""
from __future__ import with_statement
from _compatibility import exec_function, is_py3k
from jedi._compatibility import exec_function, is_py3k
import re
import sys
@@ -9,71 +32,14 @@ if is_py3k:
import types
import inspect
import debug
import parsing
import imports
from jedi import common
from jedi import debug
from jedi import parsing
from jedi import modules
import evaluate
def get_sys_path():
def check_virtual_env(sys_path):
""" Add virtualenv's site-packages to the `sys.path`."""
venv = os.getenv('VIRTUAL_ENV')
if not venv:
return
venv = os.path.abspath(venv)
p = os.path.join(
venv, 'lib', 'python%d.%d' % sys.version_info[:2], 'site-packages')
sys_path.insert(0, p)
p = sys.path[1:]
check_virtual_env(p)
return p
class CachedModule(object):
"""
The base type for all modules, which is not to be confused with
`parsing.Module`. Caching happens here.
"""
cache = {}
def __init__(self, path=None, name=None):
self.path = path and os.path.abspath(path)
self.name = name
self._parser = None
@property
def parser(self):
""" get the parser lazy """
if not self._parser:
try:
timestamp, parser = self.cache[self.path or self.name]
if not self.path or os.path.getmtime(self.path) <= timestamp:
self._parser = parser
else:
# In case there is already a module cached and this module
# has to be reparsed, we also need to invalidate the import
# caches.
imports.invalidate_star_import_cache(parser.module)
raise KeyError()
except KeyError:
self._load_module()
return self._parser
def _get_source(self):
raise NotImplementedError()
def _load_module(self):
source = self._get_source()
self._parser = parsing.PyFuzzyParser(source, self.path or self.name)
p_time = None if not self.path else os.path.getmtime(self.path)
if self.path or self.name:
self.cache[self.path or self.name] = p_time, self._parser
class Parser(CachedModule):
class BuiltinModule(modules.CachedModule):
"""
This module is a parser for all builtin modules, which are programmed in
C/C++. It should also work on third party modules.
@@ -101,15 +67,13 @@ class Parser(CachedModule):
if is_py3k:
map_types['file object'] = 'import io; return io.TextIOWrapper()'
module_cache = {}
def __init__(self, path=None, name=None, sys_path=None):
if sys_path is None:
sys_path = get_sys_path()
sys_path = modules.get_sys_path()
if not name:
name = os.path.basename(path)
name = name.rpartition('.')[0] # cut file type (normally .so)
super(Parser, self).__init__(path=path, name=name)
super(BuiltinModule, self).__init__(path=path, name=name)
self.sys_path = list(sys_path)
self._module = None
@@ -197,10 +161,13 @@ class Parser(CachedModule):
try:
name = self.name
# sometimes there are stupid endings like `_sqlite3.cpython-32mu`
name = re.sub(r'\..*', '', name)
if name == '__builtin__' and not is_py3k:
name = 'builtins'
path = os.path.dirname(os.path.abspath(__file__))
with open(os.path.sep.join([path, 'mixin', name]) + '.py') as f:
with open(os.path.sep.join([path, 'mixin', name]) + '.pym') as f:
s = f.read()
except IOError:
return {}
@@ -214,15 +181,14 @@ class Parser(CachedModule):
def _generate_code(scope, mixin_funcs={}, depth=0):
"""
Generate a string, which uses python syntax as an input to the
PyFuzzyParser.
Generate a string, which uses python syntax as an input to the Parser.
"""
def get_doc(obj, indent=False):
doc = inspect.getdoc(obj)
if doc:
doc = ('r"""\n%s\n"""\n' % doc)
if indent:
doc = parsing.indent_block(doc)
doc = common.indent_block(doc)
return doc
return ''
@@ -267,9 +233,9 @@ def _generate_code(scope, mixin_funcs={}, depth=0):
if is_in_base_classes(scope, n, exe):
continue
if inspect.isbuiltin(exe) or inspect.ismethod(exe) \
or inspect.ismethoddescriptor(exe):
or inspect.ismethoddescriptor(exe):
funcs[n] = exe
elif inspect.isclass(exe):
elif inspect.isclass(exe) or inspect.ismodule(exe):
classes[n] = exe
elif inspect.ismemberdescriptor(exe):
members[n] = exe
@@ -288,14 +254,15 @@ def _generate_code(scope, mixin_funcs={}, depth=0):
code += get_doc(scope)
names = set(dir(scope)) - set(['__file__', '__name__', '__doc__',
'__path__', '__package__']) \
| set(['mro'])
'__path__', '__package__']) \
| set(['mro'])
classes, funcs, stmts, members = get_scope_objects(names)
# classes
for name, cl in classes.items():
bases = (c.__name__ for c in cl.__bases__)
bases = (c.__name__ for c in cl.__bases__) if inspect.isclass(cl) \
else []
code += 'class %s(%s):\n' % (name, ','.join(bases))
if depth == 0:
try:
@@ -303,12 +270,12 @@ def _generate_code(scope, mixin_funcs={}, depth=0):
except KeyError:
mixin = {}
cl_code = _generate_code(cl, mixin, depth + 1)
code += parsing.indent_block(cl_code)
code += common.indent_block(cl_code)
code += '\n'
# functions
for name, func in funcs.items():
params, ret = parse_function_doc(func)
params, ret = _parse_function_doc(func)
if depth > 0:
params = 'self, ' + params
doc_str = get_doc(func, indent=True)
@@ -318,13 +285,23 @@ def _generate_code(scope, mixin_funcs={}, depth=0):
# normal code generation
code += 'def %s(%s):\n' % (name, params)
code += doc_str
code += parsing.indent_block('%s\n\n' % ret)
code += common.indent_block('%s\n\n' % ret)
else:
# generation of code with mixins
# the parser only supports basic functions with a newline after
# the double dots
# find doc_str place
pos = re.search(r'\):\s*\n', mixin).end()
try:
pos = re.search(r'\):\s*\n', mixin).end()
except TypeError:
# pypy uses a different reversed builtin
if name == 'reversed':
mixin = 'def reversed(sequence):\n' \
' for i in self.__sequence: yield i'
pos = 24
else:
debug.warning('mixin trouble in pypy: %s', name)
raise
if pos is None:
raise Exception("Builtin function not parsed correctly")
code += mixin[:pos] + doc_str + mixin[pos:]
@@ -336,7 +313,7 @@ def _generate_code(scope, mixin_funcs={}, depth=0):
continue
ret = 'pass'
code += '@property\ndef %s(self):\n' % (name)
code += parsing.indent_block(get_doc(func) + '%s\n\n' % ret)
code += common.indent_block(get_doc(func) + '%s\n\n' % ret)
# variables
for name, value in stmts.items():
@@ -344,7 +321,7 @@ def _generate_code(scope, mixin_funcs={}, depth=0):
file_type = io.TextIOWrapper
else:
file_type = types.FileType
if type(value) == file_type:
if isinstance(value, file_type):
value = 'open()'
elif name == 'None':
value = ''
@@ -359,17 +336,10 @@ def _generate_code(scope, mixin_funcs={}, depth=0):
value = '%s.%s' % (mod, value)
code += '%s = %s\n' % (name, value)
if depth == 0:
#with open('writeout.py', 'w') as f:
# f.write(code)
#import sys
#sys.stdout.write(code)
#exit()
pass
return code
def parse_function_doc(func):
def _parse_function_doc(func):
"""
Takes a function and returns the params and return value as a tuple.
This is nothing more than a docstring parser.
@@ -401,7 +371,7 @@ def parse_function_doc(func):
return ','.join(args)
while True:
param_str, changes = re.subn(r' ?\[([^\[\]]+)\]',
change_options, param_str)
change_options, param_str)
if changes == 0:
break
except (ValueError, AttributeError):
@@ -423,7 +393,7 @@ def parse_function_doc(func):
# New object -> object()
ret_str = re.sub(r'[nN]ew (.*)', r'\1()', ret_str)
ret = Parser.map_types.get(ret_str, ret_str)
ret = BuiltinModule.map_types.get(ret_str, ret_str)
if ret == ret_str and ret not in ['None', 'object', 'tuple', 'set']:
debug.dbg('not working', ret_str)
if ret != 'pass':
@@ -444,7 +414,7 @@ class Builtin(object):
@property
def builtin(self):
if self._builtin is None:
self._builtin = Parser(name=self.name)
self._builtin = BuiltinModule(name=self.name)
return self._builtin
@property
@@ -460,7 +430,7 @@ class Builtin(object):
class Container(object):
FunctionType = types.FunctionType
source = _generate_code(Container, depth=0)
parser = parsing.PyFuzzyParser(source, None)
parser = parsing.Parser(source, None)
module = parser.module
module.parent = self.scope
typ = evaluate.follow_path(iter(['FunctionType']), module, module)

326
jedi/cache.py Normal file
View File

@@ -0,0 +1,326 @@
"""
This caching is very important for speed and memory optimizations. There's
nothing really spectacular, just some decorators. The following cache types are
available:
- module caching (`load_module` and `save_module`), which uses pickle and is
really important to assure low load times of modules like ``numpy``.
- the popular ``memoize_default`` works like a typical memoize and returns the
default otherwise.
- ``CachedMetaClass`` uses ``memoize_default`` to do the same with classes.
- ``time_cache`` can be used to cache something for just a limited time span,
which can be useful if there's user interaction and the user cannot react
faster than a certain time.
This module is one of the reasons why |jedi| is not thread-safe. As you can see
there are global variables, which are holding the cache information. Some of
these variables are being cleaned after every API usage.
"""
from __future__ import with_statement
import time
import os
import sys
import json
import hashlib
try:
import cPickle as pickle
except:
import pickle
import shutil
from jedi import settings
from jedi import common
from jedi import debug
# memoize caches will be deleted after every action
memoize_caches = []
time_caches = []
star_import_cache = {}
# for fast_parser, should not be deleted
parser_cache = {}
class ParserCacheItem(object):
def __init__(self, parser, change_time=None):
self.parser = parser
if change_time is None:
change_time = time.time()
self.change_time = change_time
def clear_caches(delete_all=False):
""" Jedi caches many things, that should be completed after each completion
finishes.
:param delete_all: Deletes also the cache that is normally not deleted,
like parser cache, which is important for faster parsing.
"""
global memoize_caches, time_caches
# memorize_caches must never be deleted, because the dicts will get lost in
# the wrappers.
for m in memoize_caches:
m.clear()
if delete_all:
time_caches = []
star_import_cache.clear()
parser_cache.clear()
else:
# normally just kill the expired entries, not all
for tc in time_caches:
# check time_cache for expired entries
for key, (t, value) in list(tc.items()):
if t < time.time():
# delete expired entries
del tc[key]
def memoize_default(default=None, cache=memoize_caches):
""" This is a typical memoization decorator, BUT there is one difference:
To prevent recursion it sets defaults.
Preventing recursion is in this case the much bigger use than speed. I
don't think, that there is a big speed difference, but there are many cases
where recursion could happen (think about a = b; b = a).
"""
def func(function):
memo = {}
cache.append(memo)
def wrapper(*args, **kwargs):
key = (args, frozenset(kwargs.items()))
if key in memo:
return memo[key]
else:
memo[key] = default
rv = function(*args, **kwargs)
memo[key] = rv
return rv
return wrapper
return func
class CachedMetaClass(type):
""" This is basically almost the same than the decorator above, it just
caches class initializations. I haven't found any other way, so I do it
with meta classes.
"""
@memoize_default()
def __call__(self, *args, **kwargs):
return super(CachedMetaClass, self).__call__(*args, **kwargs)
def time_cache(time_add_setting):
""" This decorator works as follows: Call it with a setting and after that
use the function with a callable that returns the key.
But: This function is only called if the key is not available. After a
certain amount of time (`time_add_setting`) the cache is invalid.
"""
def _temp(key_func):
dct = {}
time_caches.append(dct)
def wrapper(optional_callable, *args, **kwargs):
key = key_func(*args, **kwargs)
value = None
if key in dct:
expiry, value = dct[key]
if expiry > time.time():
return value
value = optional_callable()
time_add = getattr(settings, time_add_setting)
if key is not None:
dct[key] = time.time() + time_add, value
return value
return wrapper
return _temp
@time_cache("function_definition_validity")
def cache_function_definition(stmt):
module_path = stmt.get_parent_until().path
return None if module_path is None else (module_path, stmt.start_pos)
def cache_star_import(func):
def wrapper(scope, *args, **kwargs):
with common.ignored(KeyError):
mods = star_import_cache[scope]
if mods[0] + settings.star_import_cache_validity > time.time():
return mods[1]
# cache is too old and therefore invalid or not available
invalidate_star_import_cache(scope)
mods = func(scope, *args, **kwargs)
star_import_cache[scope] = time.time(), mods
return mods
return wrapper
def invalidate_star_import_cache(module, only_main=False):
""" Important if some new modules are being reparsed """
with common.ignored(KeyError):
t, mods = star_import_cache[module]
del star_import_cache[module]
for m in mods:
invalidate_star_import_cache(m, only_main=True)
if not only_main:
# We need a list here because otherwise the list is being changed
# during the iteration in py3k: iteritems -> items.
for key, (t, mods) in list(star_import_cache.items()):
if module in mods:
invalidate_star_import_cache(key)
def load_module(path, name):
"""
Returns the module or None, if it fails.
"""
if path is None and name is None:
return None
tim = os.path.getmtime(path) if path else None
n = name if path is None else path
try:
parser_cache_item = parser_cache[n]
if not path or tim <= parser_cache_item.change_time:
return parser_cache_item.parser
else:
# In case there is already a module cached and this module
# has to be reparsed, we also need to invalidate the import
# caches.
invalidate_star_import_cache(parser_cache_item.parser.module)
except KeyError:
if settings.use_filesystem_cache:
return ModulePickling.load_module(n, tim)
def save_module(path, name, parser, pickling=True):
try:
p_time = None if not path else os.path.getmtime(path)
except OSError:
p_time = None
pickling = False
n = name if path is None else path
item = ParserCacheItem(parser, p_time)
parser_cache[n] = item
if settings.use_filesystem_cache and pickling:
ModulePickling.save_module(n, item)
class _ModulePickling(object):
version = 3
"""
Version number (integer) for file system cache.
Increment this number when there are any incompatible changes in
parser representation classes. For example, the following changes
are regarded as incompatible.
- Class name is changed.
- Class is moved to another module.
- Defined slot of the class is changed.
"""
def __init__(self):
self.__index = None
self.py_tag = 'cpython-%s%s' % sys.version_info[:2]
"""
Short name for distinguish Python implementations and versions.
It's like `sys.implementation.cache_tag` but for Python < 3.3
we generate something similar. See:
http://docs.python.org/3/library/sys.html#sys.implementation
.. todo:: Detect interpreter (e.g., PyPy).
"""
def load_module(self, path, original_changed_time):
try:
pickle_changed_time = self._index[path]
except KeyError:
return None
if original_changed_time is not None \
and pickle_changed_time < original_changed_time:
# the pickle file is outdated
return None
with open(self._get_hashed_path(path), 'rb') as f:
parser_cache_item = pickle.load(f)
debug.dbg('pickle loaded', path)
parser_cache[path] = parser_cache_item
return parser_cache_item.parser
def save_module(self, path, parser_cache_item):
self.__index = None
try:
files = self._index
except KeyError:
files = {}
self._index = files
with open(self._get_hashed_path(path), 'wb') as f:
pickle.dump(parser_cache_item, f, pickle.HIGHEST_PROTOCOL)
files[path] = parser_cache_item.change_time
self._flush_index()
@property
def _index(self):
if self.__index is None:
try:
with open(self._get_path('index.json')) as f:
data = json.load(f)
except (IOError, ValueError):
self.__index = {}
else:
# 0 means version is not defined (= always delete cache):
if data.get('version', 0) != self.version:
self.delete_cache()
self.__index = {}
else:
self.__index = data['index']
return self.__index
def _remove_old_modules(self):
# TODO use
change = False
if change:
self._flush_index(self)
self._index # reload index
def _flush_index(self):
data = {'version': self.version, 'index': self._index}
with open(self._get_path('index.json'), 'w') as f:
json.dump(data, f)
self.__index = None
def delete_cache(self):
shutil.rmtree(self._cache_directory())
def _get_hashed_path(self, path):
return self._get_path('%s.pkl' % hashlib.md5(path.encode("utf-8")).hexdigest())
def _get_path(self, file):
dir = self._cache_directory()
if not os.path.exists(dir):
os.makedirs(dir)
return os.path.join(dir, file)
def _cache_directory(self):
return os.path.join(settings.cache_directory, self.py_tag)
# is a singleton
ModulePickling = _ModulePickling()

211
jedi/common.py Normal file
View File

@@ -0,0 +1,211 @@
""" A universal module with functions / classes without dependencies. """
import sys
import contextlib
import functools
import tokenizer as tokenize
from jedi._compatibility import next, reraise
from jedi import settings
FLOWS = ['if', 'else', 'elif', 'while', 'with', 'try', 'except', 'finally']
class MultiLevelStopIteration(Exception):
"""
StopIteration's get catched pretty easy by for loops, let errors propagate.
"""
pass
class UncaughtAttributeError(Exception):
"""
Important, because `__getattr__` and `hasattr` catch AttributeErrors
implicitly. This is really evil (mainly because of `__getattr__`).
`hasattr` in Python 2 is even more evil, because it catches ALL exceptions.
Therefore this class originally had to be derived from `BaseException`
instead of `Exception`. But because I removed relevant `hasattr` from
the code base, we can now switch back to `Exception`.
:param base: return values of sys.exc_info().
"""
def rethrow_uncaught(func):
"""
Re-throw uncaught `AttributeError`.
Usage: Put ``@rethrow_uncaught`` in front of the function
which does **not** suppose to raise `AttributeError`.
AttributeError is easily get caught by `hasattr` and another
``except AttributeError`` clause. This becomes problem when you use
a lot of "dynamic" attributes (e.g., using ``@property``) because you
can't distinguish if the property does not exist for real or some code
inside of the "dynamic" attribute through that error. In a well
written code, such error should not exist but getting there is very
difficult. This decorator is to help us getting there by changing
`AttributeError` to `UncaughtAttributeError` to avoid unexpected catch.
This helps us noticing bugs earlier and facilitates debugging.
.. note:: Treating StopIteration here is easy.
Add that feature when needed.
"""
@functools.wraps(func)
def wrapper(*args, **kwds):
try:
return func(*args, **kwds)
except AttributeError:
exc_info = sys.exc_info()
reraise(UncaughtAttributeError(exc_info[1]), exc_info[2])
return wrapper
class PushBackIterator(object):
def __init__(self, iterator):
self.pushes = []
self.iterator = iterator
self.current = None
def push_back(self, value):
self.pushes.append(value)
def __iter__(self):
return self
def next(self):
""" Python 2 Compatibility """
return self.__next__()
def __next__(self):
if self.pushes:
self.current = self.pushes.pop()
else:
self.current = next(self.iterator)
return self.current
class NoErrorTokenizer(object):
def __init__(self, readline, offset=(0, 0), is_fast_parser=False):
self.readline = readline
self.gen = tokenize.generate_tokens(readline)
self.offset = offset
self.closed = False
self.is_first = True
self.push_backs = []
# fast parser options
self.is_fast_parser = is_fast_parser
self.current = self.previous = [None, None, (0, 0), (0, 0), '']
self.in_flow = False
self.new_indent = False
self.parser_indent = self.old_parser_indent = 0
self.is_decorator = False
self.first_stmt = True
def push_last_back(self):
self.push_backs.append(self.current)
def next(self):
""" Python 2 Compatibility """
return self.__next__()
def __next__(self):
if self.closed:
raise MultiLevelStopIteration()
if self.push_backs:
return self.push_backs.pop(0)
self.last_previous = self.previous
self.previous = self.current
self.current = next(self.gen)
c = list(self.current)
if c[0] == tokenize.ENDMARKER:
self.current = self.previous
self.previous = self.last_previous
raise MultiLevelStopIteration()
# this is exactly the same check as in fast_parser, but this time with
# tokenize and therefore precise.
breaks = ['def', 'class', '@']
if self.is_first:
c[2] = self.offset[0] + c[2][0], self.offset[1] + c[2][1]
c[3] = self.offset[0] + c[3][0], self.offset[1] + c[3][1]
self.is_first = False
else:
c[2] = self.offset[0] + c[2][0], c[2][1]
c[3] = self.offset[0] + c[3][0], c[3][1]
self.current = c
def close():
if not self.first_stmt:
self.closed = True
raise MultiLevelStopIteration()
# ignore indents/comments
if self.is_fast_parser \
and self.previous[0] in (tokenize.INDENT, tokenize.NL, None,
tokenize.NEWLINE, tokenize.DEDENT) \
and c[0] not in (tokenize.COMMENT, tokenize.INDENT,
tokenize.NL, tokenize.NEWLINE, tokenize.DEDENT):
# print c, tokenize.tok_name[c[0]]
tok = c[1]
indent = c[2][1]
if indent < self.parser_indent: # -> dedent
self.parser_indent = indent
self.new_indent = False
if not self.in_flow or indent < self.old_parser_indent:
close()
self.in_flow = False
elif self.new_indent:
self.parser_indent = indent
self.new_indent = False
if not self.in_flow:
if tok in FLOWS or tok in breaks:
self.in_flow = tok in FLOWS
if not self.is_decorator and not self.in_flow:
close()
self.is_decorator = '@' == tok
if not self.is_decorator:
self.old_parser_indent = self.parser_indent
self.parser_indent += 1 # new scope: must be higher
self.new_indent = True
if tok != '@':
if self.first_stmt and not self.new_indent:
self.parser_indent = indent
self.first_stmt = False
return c
@contextlib.contextmanager
def scale_speed_settings(factor):
a = settings.max_executions
b = settings.max_until_execution_unique
settings.max_executions *= factor
settings.max_until_execution_unique *= factor
yield
settings.max_executions = a
settings.max_until_execution_unique = b
def indent_block(text, indention=' '):
""" This function indents a text block with a default of four spaces """
temp = ''
while text and text[-1] == '\n':
temp += text[-1]
text = text[:-1]
lines = text.split('\n')
return '\n'.join(map(lambda s: indention + s, lines)) + temp
@contextlib.contextmanager
def ignored(*exceptions):
"""Context manager that ignores all of the specified exceptions. This will
be in the standard library starting with Python 3.4."""
try:
yield
except exceptions:
pass

View File

@@ -1,3 +1,4 @@
from _compatibility import u, encoding, is_py3k
import inspect
import time
@@ -36,12 +37,12 @@ def dbg(*args):
frm = inspect.stack()[1]
mod = inspect.getmodule(frm[0])
if not (mod.__name__ in ignored_modules):
debug_function(NOTICE, 'dbg: ' + ', '.join(str(a) for a in args))
debug_function(NOTICE, 'dbg: ' + ', '.join(u(a) for a in args))
def warning(*args):
if debug_function and enable_warning:
debug_function(WARNING, 'warning: ' + ', '.join(str(a) for a in args))
debug_function(WARNING, 'warning: ' + ', '.join(u(a) for a in args))
def speed(name):
@@ -58,7 +59,9 @@ def print_to_stdout(level, str_out):
col = Fore.RED
else:
col = Fore.YELLOW
if not is_py3k:
str_out = str_out.encode(encoding, 'replace')
print(col + str_out + Fore.RESET)
#debug_function = print_to_stdout
# debug_function = print_to_stdout

View File

@@ -1,34 +1,131 @@
""" Processing of docstrings, which means parsing for types. """
"""
Docstrings are another source of information for functions and classes.
:mod:`dynamic` tries to find all executions of functions, while the docstring
parsing is much easier. There are two different types of docstrings that |jedi|
understands:
- `Sphinx <http://sphinx-doc.org/markup/desc.html#info-field-lists>`_
- `Epydoc <http://epydoc.sourceforge.net/manual-fields.html>`_
For example, the sphinx annotation ``:type foo: str`` clearly states that the
type of ``foo`` is ``str``.
As an addition to parameter searching, this module also provides return
annotations.
"""
import re
from jedi import cache
from jedi import parsing
import evaluate
import evaluate_representation as er
DOCSTRING_PARAM_PATTERNS = [
r'\s*:type\s+%s:\s*([^\n]+)', # Sphinx
r'\s*@type\s+%s:\s*([^\n]+)', # Epydoc
]
DOCSTRING_RETURN_PATTERNS = [
re.compile(r'\s*:rtype:\s*([^\n]+)', re.M), # Sphinx
re.compile(r'\s*@rtype:\s*([^\n]+)', re.M), # Epydoc
]
REST_ROLE_PATTERN = re.compile(r':[^`]+:`([^`]+)`')
#@evaluate.memoize_default() # TODO add
@cache.memoize_default()
def follow_param(param):
func = param.parent_function
#print func, param, param.parent_function
param_str = search_param_in_docstr(func.docstr, str(param.get_name()))
# print func, param, param.parent_function
param_str = _search_param_in_docstr(func.docstr, str(param.get_name()))
user_position = (1, 0)
if param_str is not None:
scope = func.get_parent_until()
return evaluate.get_scopes_for_name(scope, param_str,
search_global=True)
# Try to import module part in dotted name.
# (e.g., 'threading' in 'threading.Thread').
if '.' in param_str:
param_str = 'import %s\n%s' % (
param_str.rsplit('.', 1)[0],
param_str)
user_position = (2, 0)
p = parsing.Parser(param_str, None, user_position,
no_docstr=True)
if p.user_stmt is None:
return []
return evaluate.follow_statement(p.user_stmt)
return []
def search_param_in_docstr(docstr, param_str):
lines = docstr.split('\n')
def _search_param_in_docstr(docstr, param_str):
"""
Search `docstr` for a type of `param_str`.
# look at #40 to see definitions of those params
sphinx_comp = ':type %s:' % param_str
googley_comp = re.compile('\s*%s\s+\(([^()]+)\)' % re.escape(param_str))
for l in lines:
if l.startswith(sphinx_comp):
return l.replace(sphinx_comp, '', 1).strip()
>>> _search_param_in_docstr(':type param: int', 'param')
'int'
>>> _search_param_in_docstr('@type param: int', 'param')
'int'
>>> _search_param_in_docstr(
... ':type param: :class:`threading.Thread`', 'param')
'threading.Thread'
>>> _search_param_in_docstr('no document', 'param') is None
True
"""
# look at #40 to see definitions of those params
patterns = [re.compile(p % re.escape(param_str))
for p in DOCSTRING_PARAM_PATTERNS]
for pattern in patterns:
match = pattern.search(docstr)
if match:
return _strip_rest_role(match.group(1))
r = re.match(googley_comp, l)
if r is not None:
return r.group(1)
return None
def _strip_rest_role(type_str):
"""
Strip off the part looks like a ReST role in `type_str`.
>>> _strip_rest_role(':class:`ClassName`') # strip off :class:
'ClassName'
>>> _strip_rest_role(':py:obj:`module.Object`') # works with domain
'module.Object'
>>> _strip_rest_role('ClassName') # do nothing when not ReST role
'ClassName'
See also:
http://sphinx-doc.org/domains.html#cross-referencing-python-objects
"""
match = REST_ROLE_PATTERN.match(type_str)
if match:
return match.group(1)
else:
return type_str
def find_return_types(func):
def search_return_in_docstr(code):
for p in DOCSTRING_RETURN_PATTERNS:
match = p.search(code)
if match:
return match.group(1)
if isinstance(func, er.InstanceElement):
func = func.var
if isinstance(func, er.Function):
func = func.base_func
type_str = search_return_in_docstr(func.docstr)
if not type_str:
return []
p = parsing.Parser(type_str, None, (1, 0), no_docstr=True)
if p.user_stmt is None:
return []
p.user_stmt.parent = func
return list(evaluate.follow_statement(p.user_stmt))

View File

@@ -1,23 +1,71 @@
"""
For dynamic completion.
To understand Python on a deeper level, |jedi| needs to understand some of the
dynamic features of Python, however this probably the most complicated part:
Sorry to everyone who is reading this code. Especially the array parts are
really cryptic and not understandable. It's just a hack, that turned out to be
working quite good.
- Array modifications (e.g. ``list.append``)
- Parameter completion in functions
- Flow checks (e.g. ``if isinstance(a, str)`` -> a is a str)
Array modifications
*******************
If the content of an array (``set``/``list``) is wanted somewhere, the current
module will be checked for appearances of ``arr.append``, ``arr.insert``, etc.
If the ``arr`` name points to an actual array, the content will be added
This can be really cpu intensive, as you can imagine. Because |jedi| has to
follow **every** ``append``. However this works pretty good, because in *slow*
cases, the recursion detector and other settings will stop this process.
It is important to note that:
1. Array modfications work only in the current module
2. Only Array additions are being checked, ``list.pop``, etc. is being ignored.
Parameter completion
********************
One of the really important features of |jedi| is to have an option to
understand code like this::
def foo(bar):
bar. # completion here
foo(1)
There's no doubt wheter bar is an ``int`` or not, but if there's also a call
like ``foo('str')``, what would happen? Well, we'll just show both. Because
that's what a human would expect.
It works as follows:
- A param is being encountered
- search for function calls named ``foo``
- execute these calls and check the injected params. This work with a
``ParamListener``.
Flow checks
***********
Flow checks are not really mature. There's only a check for ``isinstance``. It
would check whether a flow has the form of ``if isinstance(a, type_or_tuple)``.
Unfortunately every other thing is being ignored (e.g. a == '' would be easy to
check for -> a is a string). There's big potential in these checks.
"""
from __future__ import with_statement
import os
import parsing
import modules
import evaluate
import helpers
import settings
import debug
import builtin
import imports
from jedi import cache
from jedi import parsing_representation as pr
from jedi import modules
from jedi import settings
from jedi import common
from jedi import debug
from jedi import fast_parser
import api_classes
import evaluate
import imports
import evaluate_representation as er
# This is something like the sys.path, but only for searching params. It means
# that this is the order in which Jedi searches params.
@@ -31,7 +79,7 @@ def get_directory_modules_for_name(mods, name):
"""
def check_python_file(path):
try:
return builtin.CachedModule.cache[path][1].module
return cache.parser_cache[path].parser.module
except KeyError:
try:
return check_fs(path)
@@ -40,12 +88,12 @@ def get_directory_modules_for_name(mods, name):
def check_fs(path):
with open(path) as f:
source = f.read()
source = modules.source_to_unicode(f.read())
if name in source:
return modules.Module(path, source).parser.module
# skip non python modules
mods = set(m for m in mods if m.path.endswith('.py'))
mods = set(m for m in mods if m.path is None or m.path.endswith('.py'))
mod_paths = set()
for m in mods:
mod_paths.add(m.path)
@@ -54,13 +102,15 @@ def get_directory_modules_for_name(mods, name):
if settings.dynamic_params_for_other_modules:
paths = set(settings.additional_dynamic_modules)
for p in mod_paths:
d = os.path.dirname(p)
for entry in os.listdir(d):
if entry not in mod_paths:
if entry.endswith('.py'):
paths.add(d + os.path.sep + entry)
if p is not None:
d = os.path.dirname(p)
for entry in os.listdir(d):
if entry not in mod_paths:
if entry.endswith('.py'):
paths.add(d + os.path.sep + entry)
for p in paths:
for p in sorted(paths):
# make testing easier, sort it - same results on every interpreter
c = check_python_file(p)
if c is not None and c not in mods:
yield c
@@ -93,7 +143,7 @@ class ParamListener(object):
self.param_possibilities.append(params)
@evaluate.memoize_default([])
@cache.memoize_default([])
def search_params(param):
"""
This is a dynamic search for params. If you try to complete a type:
@@ -122,14 +172,46 @@ def search_params(param):
return []
for stmt in possible_stmts:
if not isinstance(stmt, parsing.Import):
calls = _scan_array(stmt.get_assignment_calls(), func_name)
for c in calls:
# no execution means that params cannot be set
call_path = c.generate_call_path()
pos = c.start_pos
scope = stmt.parent
evaluate.follow_call_path(call_path, scope, pos)
if isinstance(stmt, pr.Import):
continue
calls = _scan_statement(stmt, func_name)
for c in calls:
# no execution means that params cannot be set
call_path = list(c.generate_call_path())
pos = c.start_pos
scope = stmt.parent
# this whole stuff is just to not execute certain parts
# (speed improvement), basically we could just call
# ``follow_call_path`` on the call_path and it would
# also work.
def listRightIndex(lst, value):
return len(lst) - lst[-1::-1].index(value) -1
# Need to take right index, because there could be a
# func usage before.
i = listRightIndex(call_path, func_name)
first, last = call_path[:i], call_path[i+1:]
if not last and not call_path.index(func_name) != i:
continue
scopes = [scope]
if first:
scopes = evaluate.follow_call_path(iter(first), scope, pos)
pos = None
for scope in scopes:
s = evaluate.find_name(scope, func_name, position=pos,
search_global=not first,
resolve_decorator=False)
c = [getattr(escope, 'base_func', None) or escope.base
for escope in s
if escope.isinstance(er.Function, er.Class)
]
if compare in c:
# only if we have the correct function we execute
# it, otherwise just ignore it.
evaluate.follow_paths(iter(last), s, scope)
return listener.param_possibilities
result = []
@@ -139,19 +221,22 @@ def search_params(param):
result += evaluate.follow_statement(p.parent)
return result
func = param.get_parent_until(parsing.Function)
func = param.get_parent_until(pr.Function)
current_module = param.get_parent_until()
func_name = str(func.name)
if func_name == '__init__' and isinstance(func.parent, parsing.Class):
compare = func
if func_name == '__init__' and isinstance(func.parent, pr.Class):
func_name = str(func.parent.name)
compare = func.parent
# get the param name
if param.assignment_details:
arr = param.assignment_details[0][1]
# first assignment details, others would be a syntax error
commands, op = param.assignment_details[0]
else:
arr = param.get_assignment_calls()
offset = 1 if arr[0][0] in ['*', '**'] else 0
param_name = str(arr[0][offset].name)
commands = param.get_commands()
offset = 1 if commands[0] in ['*', '**'] else 0
param_name = str(commands[offset].name)
# add the listener
listener = ParamListener()
@@ -172,61 +257,56 @@ def search_params(param):
def check_array_additions(array):
""" Just a mapper function for the internal _check_array_additions """
if array._array.type not in ['list', 'set']:
if not pr.Array.is_type(array._array, pr.Array.LIST, pr.Array.SET):
# TODO also check for dict updates
return []
is_list = array._array.type == 'list'
current_module = array._array.parent_stmt.get_parent_until()
current_module = array._array.get_parent_until()
res = _check_array_additions(array, current_module, is_list)
return res
def _scan_array(arr, search_name):
def _scan_statement(stmt, search_name, assignment_details=False):
""" Returns the function Call that match search_name in an Array. """
result = []
for sub in arr:
for s in sub:
if isinstance(s, parsing.Array):
result += _scan_array(s, search_name)
elif isinstance(s, parsing.Call):
s_new = s
while s_new is not None:
n = s_new.name
if isinstance(n, parsing.Name) and search_name in n.names:
result.append(s)
def scan_array(arr, search_name):
result = []
if arr.type == pr.Array.DICT:
for key_stmt, value_stmt in arr.items():
result += _scan_statement(key_stmt, search_name)
result += _scan_statement(value_stmt, search_name)
else:
for stmt in arr:
result += _scan_statement(stmt, search_name)
return result
check = list(stmt.get_commands())
if assignment_details:
for commands, op in stmt.assignment_details:
check += commands
result = []
for c in check:
if isinstance(c, pr.Array):
result += scan_array(c, search_name)
elif isinstance(c, pr.Call):
s_new = c
while s_new is not None:
n = s_new.name
if isinstance(n, pr.Name) and search_name in n.names:
result.append(c)
if s_new.execution is not None:
result += scan_array(s_new.execution, search_name)
s_new = s_new.next
if s_new.execution is not None:
result += _scan_array(s_new.execution, search_name)
s_new = s_new.next
return result
counter = 0
def dec(func):
""" TODO delete this """
def wrapper(*args, **kwargs):
global counter
element = args[0]
if isinstance(element, evaluate.Array):
stmt = element._array.parent_stmt
else:
# must be instance
stmt = element.var_args.parent_stmt
print(' ' * counter + 'recursion,', stmt)
counter += 1
res = func(*args, **kwargs)
counter -= 1
#print ' '*counter + 'end,'
return res
return wrapper
#@dec
@evaluate.memoize_default([])
@cache.memoize_default([])
def _check_array_additions(compare_array, module, is_list):
"""
Checks if a `parsing.Array` has "add" statements:
Checks if a `pr.Array` has "add" statements:
>>> a = [""]
>>> a.append(1)
"""
@@ -249,7 +329,7 @@ def _check_array_additions(compare_array, module, is_list):
backtrack_path = iter(call_path[:separate_index])
position = c.start_pos
scope = c.parent_stmt.parent
scope = c.get_parent_until(pr.IsScope)
found = evaluate.follow_call_path(backtrack_path, scope, position)
if not compare_array in found:
@@ -259,36 +339,39 @@ def _check_array_additions(compare_array, module, is_list):
if not params.values:
continue # no params: just ignore it
if add_name in ['append', 'add']:
result += evaluate.follow_call_list(params)
for param in params:
result += evaluate.follow_statement(param)
elif add_name in ['insert']:
try:
second_param = params[1]
except IndexError:
continue
else:
result += evaluate.follow_call_list([second_param])
result += evaluate.follow_statement(second_param)
elif add_name in ['extend', 'update']:
iterators = evaluate.follow_call_list(params)
for param in params:
iterators = evaluate.follow_statement(param)
result += evaluate.get_iterator_types(iterators)
return result
def get_execution_parent(element, *stop_classes):
""" Used to get an Instance/Execution parent """
if isinstance(element, evaluate.Array):
stmt = element._array.parent_stmt
if isinstance(element, er.Array):
stmt = element._array.parent
else:
# must be instance
stmt = element.var_args.parent_stmt
if isinstance(stmt, evaluate.InstanceElement):
stop_classes = list(stop_classes) + [evaluate.Function]
# is an Instance with an ArrayInstance inside
stmt = element.var_args[0].var_args.parent
if isinstance(stmt, er.InstanceElement):
stop_classes = list(stop_classes) + [er.Function]
return stmt.get_parent_until(stop_classes)
temp_param_add = settings.dynamic_params_for_other_modules
settings.dynamic_params_for_other_modules = False
search_names = ['append', 'extend', 'insert'] if is_list else \
['add', 'update']
comp_arr_parent = get_execution_parent(compare_array, evaluate.Execution)
['add', 'update']
comp_arr_parent = get_execution_parent(compare_array, er.Execution)
possible_stmts = []
res = []
for n in search_names:
@@ -301,20 +384,20 @@ def _check_array_additions(compare_array, module, is_list):
# can search for the same statement, that is in the module
# dict. Executions are somewhat special in jedi, since they
# literally copy the contents of a function.
if isinstance(comp_arr_parent, evaluate.Execution):
if isinstance(comp_arr_parent, er.Execution):
stmt = comp_arr_parent. \
get_statement_for_position(stmt.start_pos)
get_statement_for_position(stmt.start_pos)
if stmt is None:
continue
# InstanceElements are special, because they don't get copied,
# but have this wrapper around them.
if isinstance(comp_arr_parent, evaluate.InstanceElement):
stmt = evaluate.InstanceElement(comp_arr_parent.instance, stmt)
if isinstance(comp_arr_parent, er.InstanceElement):
stmt = er.InstanceElement(comp_arr_parent.instance, stmt)
if evaluate.follow_statement.push_stmt(stmt):
# check recursion
continue
res += check_calls(_scan_array(stmt.get_assignment_calls(), n), n)
res += check_calls(_scan_statement(stmt, n), n)
evaluate.follow_statement.pop_stmt()
# reset settings
settings.dynamic_params_for_other_modules = temp_param_add
@@ -322,14 +405,14 @@ def _check_array_additions(compare_array, module, is_list):
def check_array_instances(instance):
""" Used for set() and list() instances. """
"""Used for set() and list() instances."""
if not settings.dynamic_arrays_instances:
return instance.var_args
ai = ArrayInstance(instance)
return helpers.generate_param_array([ai], instance.var_args.parent_stmt)
return [ai]
class ArrayInstance(parsing.Base):
class ArrayInstance(pr.Base):
"""
Used for the usage of set() and list().
This is definitely a hack, but a good one :-)
@@ -345,66 +428,68 @@ class ArrayInstance(parsing.Base):
lists/sets are too complicated too handle that.
"""
items = []
for array in evaluate.follow_call_list(self.var_args):
if isinstance(array, evaluate.Instance) and len(array.var_args):
temp = array.var_args[0][0]
if isinstance(temp, ArrayInstance):
# prevent recursions
# TODO compare Modules
if self.var_args.start_pos != temp.var_args.start_pos:
items += temp.iter_content()
else:
debug.warning('ArrayInstance recursion', self.var_args)
continue
items += evaluate.get_iterator_types([array])
for stmt in self.var_args:
for typ in evaluate.follow_statement(stmt):
if isinstance(typ, er.Instance) and len(typ.var_args):
array = typ.var_args[0]
if isinstance(array, ArrayInstance):
# prevent recursions
# TODO compare Modules
if self.var_args.start_pos != array.var_args.start_pos:
items += array.iter_content()
else:
debug.warning(
'ArrayInstance recursion',
self.var_args)
continue
items += evaluate.get_iterator_types([typ])
if self.var_args.parent_stmt is None:
# TODO check if exclusion of tuple is a problem here.
if isinstance(self.var_args, tuple) or self.var_args.parent is None:
return [] # generated var_args should not be checked for arrays
module = self.var_args.parent_stmt.get_parent_until()
module = self.var_args.get_parent_until()
is_list = str(self.instance.name) == 'list'
items += _check_array_additions(self.instance, module, is_list)
return items
def related_names(definitions, search_name, mods):
def usages(definitions, search_name, mods):
def compare_array(definitions):
""" `definitions` are being compared by module/start_pos, because
sometimes the id's of the objects change (e.g. executions).
"""
result = []
for d in definitions:
module = d.get_parent_until()
result.append((module, d.start_pos))
return result
def check_call(call):
result = []
follow = [] # There might be multiple search_name's in one call_path
call_path = list(call.generate_call_path())
for i, name in enumerate(call_path):
# name is `parsing.NamePart`.
# name is `pr.NamePart`.
if name == search_name:
follow.append(call_path[:i + 1])
for f in follow:
follow_res, search = evaluate.goto(call.parent_stmt, f)
follow_res = related_name_add_import_modules(follow_res, search)
follow_res, search = evaluate.goto(call.parent, f)
follow_res = usages_add_import_modules(follow_res, search)
#print follow_res, [d.parent for d in follow_res]
compare_follow_res = compare_array(follow_res)
# compare to see if they match
if any(r in definitions for r in follow_res):
scope = call.parent_stmt
result.append(api_classes.RelatedName(search, scope))
if any(r in compare_definitions for r in compare_follow_res):
scope = call.parent
result.append(api_classes.Usage(search, scope))
return result
if not definitions:
return set()
def is_definition(arr):
try:
for a in arr:
assert len(a) == 1
a = a[0]
if a.isinstance(parsing.Array):
assert is_definition(a)
elif a.isinstance(parsing.Call):
assert a.execution is None
return True
except AssertionError:
return False
compare_definitions = compare_array(definitions)
mods |= set([d.get_parent_until() for d in definitions])
names = []
for m in get_directory_modules_for_name(mods, search_name):
@@ -413,7 +498,7 @@ def related_names(definitions, search_name, mods):
except KeyError:
continue
for stmt in stmts:
if isinstance(stmt, parsing.Import):
if isinstance(stmt, pr.Import):
count = 0
imps = []
for i in stmt.get_all_import_names():
@@ -424,84 +509,82 @@ def related_names(definitions, search_name, mods):
for used_count, name_part in imps:
i = imports.ImportPath(stmt, kill_count=count - used_count,
direct_resolve=True)
direct_resolve=True)
f = i.follow(is_goto=True)
if set(f) & set(definitions):
names.append(api_classes.RelatedName(name_part, stmt))
names.append(api_classes.Usage(name_part, stmt))
else:
calls = _scan_array(stmt.get_assignment_calls(), search_name)
for d in stmt.assignment_details:
if not is_definition(d[1]):
calls += _scan_array(d[1], search_name)
for call in calls:
for call in _scan_statement(stmt, search_name,
assignment_details=True):
names += check_call(call)
return names
def related_name_add_import_modules(definitions, search_name):
def usages_add_import_modules(definitions, search_name):
""" Adds the modules of the imports """
new = set()
for d in definitions:
if isinstance(d.parent, parsing.Import):
if isinstance(d.parent, pr.Import):
s = imports.ImportPath(d.parent, direct_resolve=True)
try:
with common.ignored(IndexError):
new.add(s.follow(is_goto=True)[0])
except IndexError:
pass
return set(definitions) | new
def check_flow_information(flow, search_name, pos):
""" Try to find out the type of a variable just with the information that
is given by the flows: e.g. It is also responsible for assert checks.
>>> if isinstance(k, str):
>>> k. # <- completion here
is given by the flows: e.g. It is also responsible for assert checks.::
if isinstance(k, str):
k. # <- completion here
ensures that `k` is a string.
"""
if not settings.dynamic_flow_information:
return None
result = []
if isinstance(flow, parsing.Scope) and not result:
if isinstance(flow, (pr.Scope, fast_parser.Module)) and not result:
for ass in reversed(flow.asserts):
if pos is None or ass.start_pos > pos:
continue
result = check_statement_information(ass, search_name)
result = _check_isinstance_type(ass, search_name)
if result:
break
if isinstance(flow, parsing.Flow) and not result:
if flow.command in ['if', 'while'] and len(flow.inits) == 1:
result = check_statement_information(flow.inits[0], search_name)
if isinstance(flow, pr.Flow) and not result:
if flow.command in ['if', 'while'] and len(flow.inputs) == 1:
result = _check_isinstance_type(flow.inputs[0], search_name)
return result
def check_statement_information(stmt, search_name):
def _check_isinstance_type(stmt, search_name):
try:
ass = stmt.get_assignment_calls()
try:
call = ass.get_only_subelement()
except AttributeError:
assert False
assert type(call) == parsing.Call and str(call.name) == 'isinstance'
commands = stmt.get_commands()
# this might be removed if we analyze and, etc
assert len(commands) == 1
call = commands[0]
assert type(call) is pr.Call and str(call.name) == 'isinstance'
assert bool(call.execution)
# isinstance check
isinst = call.execution.values
assert len(isinst) == 2 # has two params
assert len(isinst[0]) == 1
assert len(isinst[1]) == 1
assert isinstance(isinst[0][0], parsing.Call)
obj, classes = [statement.get_commands() for statement in isinst]
assert len(obj) == 1
assert len(classes) == 1
assert isinstance(obj[0], pr.Call)
# names fit?
assert str(isinst[0][0].name) == search_name
classes_call = isinst[1][0] # class_or_type_or_tuple
assert isinstance(classes_call, parsing.Call)
result = []
for c in evaluate.follow_call(classes_call):
if isinstance(c, evaluate.Array):
result += c.get_index_types()
else:
result.append(c)
for i, c in enumerate(result):
result[i] = evaluate.Instance(c)
return result
assert str(obj[0].name) == search_name
assert isinstance(classes[0], pr.Call) # can be type or tuple
except AssertionError:
return []
result = []
for c in evaluate.follow_call(classes[0]):
if isinstance(c, er.Array):
result += c.get_index_types()
else:
result.append(c)
for i, c in enumerate(result):
result[i] = er.Instance(c)
return result

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,940 @@
"""
Like described in the :mod:`parsing_representation` module, there's a need for
an ast like module to represent the states of parsed modules.
But now there are also structures in Python that need a little bit more than
that. An ``Instance`` for example is only a ``Class`` before it is
instantiated. This class represents these cases.
So, why is there also a ``Class`` class here? Well, there are decorators and
they change classes in Python 3.
"""
from __future__ import with_statement
import copy
import itertools
from jedi._compatibility import use_metaclass, next, hasattr, unicode
from jedi import parsing_representation as pr
from jedi import cache
from jedi import helpers
from jedi import debug
from jedi import common
import recursion
import docstrings
import imports
import evaluate
import builtin
import dynamic
class Executable(pr.IsScope):
"""
An instance is also an executable - because __init__ is called
:param var_args: The param input array, consist of `pr.Array` or list.
"""
def __init__(self, base, var_args=()):
self.base = base
self.var_args = var_args
def get_parent_until(self, *args, **kwargs):
return self.decorated.get_parent_until(*args, **kwargs)
@property
def parent(self):
return self.decorated.parent
@property
def decorated(self):
"""
Instance doesn't care about decorators and Execution overrides this
"""
return self.base
class Instance(use_metaclass(cache.CachedMetaClass, Executable)):
"""
This class is used to evaluate instances.
"""
def __init__(self, base, var_args=()):
super(Instance, self).__init__(base, var_args)
if str(base.name) in ['list', 'set'] \
and builtin.Builtin.scope == base.get_parent_until():
# compare the module path with the builtin name.
self.var_args = dynamic.check_array_instances(self)
else:
# need to execute the __init__ function, because the dynamic param
# searching needs it.
with common.ignored(KeyError):
self.execute_subscope_by_name('__init__', self.var_args)
# Generated instances are classes that are just generated by self
# (No var_args) used.
self.is_generated = False
@cache.memoize_default()
def _get_method_execution(self, func):
func = InstanceElement(self, func, True)
return Execution(func, self.var_args)
def _get_func_self_name(self, func):
"""
Returns the name of the first param in a class method (which is
normally self.
"""
try:
return str(func.params[0].used_vars[0])
except IndexError:
return None
@cache.memoize_default([])
def _get_self_attributes(self):
def add_self_dot_name(name):
"""
Need to copy and rewrite the name, because names are now
``instance_usage.variable`` instead of ``self.variable``.
"""
n = copy.copy(name)
n.names = n.names[1:]
names.append(InstanceElement(self, n))
names = []
# This loop adds the names of the self object, copies them and removes
# the self.
for sub in self.base.subscopes:
if isinstance(sub, pr.Class):
continue
# Get the self name, if there's one.
self_name = self._get_func_self_name(sub)
if not self_name:
continue
if sub.name.get_code() == '__init__':
# ``__init__`` is special because the params need are injected
# this way. Therefore an execution is necessary.
if not sub.decorators:
# __init__ decorators should generally just be ignored,
# because to follow them and their self variables is too
# complicated.
sub = self._get_method_execution(sub)
for n in sub.get_set_vars():
# Only names with the selfname are being added.
# It is also important, that they have a len() of 2,
# because otherwise, they are just something else
if n.names[0] == self_name and len(n.names) == 2:
add_self_dot_name(n)
for s in self.base.get_super_classes():
names += Instance(s)._get_self_attributes()
return names
def get_subscope_by_name(self, name):
sub = self.base.get_subscope_by_name(name)
return InstanceElement(self, sub, True)
def execute_subscope_by_name(self, name, args=()):
method = self.get_subscope_by_name(name)
return Execution(method, args).get_return_types()
def get_descriptor_return(self, obj):
""" Throws a KeyError if there's no method. """
# Arguments in __get__ descriptors are obj, class.
# `method` is the new parent of the array, don't know if that's good.
args = [obj, obj.base] if isinstance(obj, Instance) else [None, obj]
return self.execute_subscope_by_name('__get__', args)
@cache.memoize_default([])
def get_defined_names(self):
"""
Get the instance vars of a class. This includes the vars of all
classes
"""
names = self._get_self_attributes()
class_names = self.base.get_defined_names()
for var in class_names:
names.append(InstanceElement(self, var, True))
return names
def scope_generator(self):
"""
An Instance has two scopes: The scope with self names and the class
scope. Instance variables have priority over the class scope.
"""
yield self, self._get_self_attributes()
names = []
class_names = self.base.get_defined_names()
for var in class_names:
names.append(InstanceElement(self, var, True))
yield self, names
def get_index_types(self, index=None):
args = [] if index is None else [index]
try:
return self.execute_subscope_by_name('__getitem__', args)
except KeyError:
debug.warning('No __getitem__, cannot access the array.')
return []
def __getattr__(self, name):
if name not in ['start_pos', 'end_pos', 'name', 'get_imports',
'doc', 'docstr', 'asserts']:
raise AttributeError("Instance %s: Don't touch this (%s)!"
% (self, name))
return getattr(self.base, name)
def __repr__(self):
return "<e%s of %s (var_args: %s)>" % \
(type(self).__name__, self.base, len(self.var_args or []))
class InstanceElement(use_metaclass(cache.CachedMetaClass, pr.Base)):
"""
InstanceElement is a wrapper for any object, that is used as an instance
variable (e.g. self.variable or class methods).
"""
def __init__(self, instance, var, is_class_var=False):
if isinstance(var, pr.Function):
var = Function(var)
elif isinstance(var, pr.Class):
var = Class(var)
self.instance = instance
self.var = var
self.is_class_var = is_class_var
@property
@cache.memoize_default()
def parent(self):
par = self.var.parent
if isinstance(par, Class) and par == self.instance.base \
or isinstance(par, pr.Class) \
and par == self.instance.base.base:
par = self.instance
elif not isinstance(par, pr.Module):
par = InstanceElement(self.instance, par, self.is_class_var)
return par
def get_parent_until(self, *args, **kwargs):
return pr.Simple.get_parent_until(self, *args, **kwargs)
def get_decorated_func(self):
""" Needed because the InstanceElement should not be stripped """
func = self.var.get_decorated_func(self.instance)
if func == self.var:
return self
return func
def get_commands(self):
# Copy and modify the array.
return [InstanceElement(self.instance, command, self.is_class_var)
if not isinstance(command, unicode) else command
for command in self.var.get_commands()]
def __iter__(self):
for el in self.var.__iter__():
yield InstanceElement(self.instance, el, self.is_class_var)
def __getattr__(self, name):
return getattr(self.var, name)
def isinstance(self, *cls):
return isinstance(self.var, cls)
def __repr__(self):
return "<%s of %s>" % (type(self).__name__, self.var)
class Class(use_metaclass(cache.CachedMetaClass, pr.IsScope)):
"""
This class is not only important to extend `pr.Class`, it is also a
important for descriptors (if the descriptor methods are evaluated or not).
"""
def __init__(self, base):
self.base = base
@cache.memoize_default(default=())
def get_super_classes(self):
supers = []
# TODO care for mro stuff (multiple super classes).
for s in self.base.supers:
# Super classes are statements.
for cls in evaluate.follow_statement(s):
if not isinstance(cls, Class):
debug.warning('Received non class, as a super class')
continue # Just ignore other stuff (user input error).
supers.append(cls)
if not supers and self.base.parent != builtin.Builtin.scope:
# add `object` to classes
supers += evaluate.find_name(builtin.Builtin.scope, 'object')
return supers
@cache.memoize_default(default=())
def get_defined_names(self):
def in_iterable(name, iterable):
""" checks if the name is in the variable 'iterable'. """
for i in iterable:
# Only the last name is important, because these names have a
# maximal length of 2, with the first one being `self`.
if i.names[-1] == name.names[-1]:
return True
return False
result = self.base.get_defined_names()
super_result = []
# TODO mro!
for cls in self.get_super_classes():
# Get the inherited names.
for i in cls.get_defined_names():
if not in_iterable(i, result):
super_result.append(i)
result += super_result
return result
def get_subscope_by_name(self, name):
for sub in reversed(self.subscopes):
if sub.name.get_code() == name:
return sub
raise KeyError("Couldn't find subscope.")
@property
def name(self):
return self.base.name
def __getattr__(self, name):
if name not in ['start_pos', 'end_pos', 'parent', 'asserts', 'docstr',
'doc', 'get_imports', 'get_parent_until', 'get_code',
'subscopes']:
raise AttributeError("Don't touch this: %s of %s !" % (name, self))
return getattr(self.base, name)
def __repr__(self):
return "<e%s of %s>" % (type(self).__name__, self.base)
class Function(use_metaclass(cache.CachedMetaClass, pr.IsScope)):
"""
Needed because of decorators. Decorators are evaluated here.
"""
def __init__(self, func, is_decorated=False):
""" This should not be called directly """
self.base_func = func
self.is_decorated = is_decorated
@cache.memoize_default()
def _decorated_func(self, instance=None):
"""
Returns the function, that is to be executed in the end.
This is also the places where the decorators are processed.
"""
f = self.base_func
# Only enter it, if has not already been processed.
if not self.is_decorated:
for dec in reversed(self.base_func.decorators):
debug.dbg('decorator:', dec, f)
dec_results = set(evaluate.follow_statement(dec))
if not len(dec_results):
debug.warning('decorator not found: %s on %s' %
(dec, self.base_func))
return None
decorator = dec_results.pop()
if dec_results:
debug.warning('multiple decorators found', self.base_func,
dec_results)
# Create param array.
old_func = Function(f, is_decorated=True)
if instance is not None and decorator.isinstance(Function):
old_func = InstanceElement(instance, old_func)
instance = None
wrappers = Execution(decorator, (old_func,)).get_return_types()
if not len(wrappers):
debug.warning('no wrappers found', self.base_func)
return None
if len(wrappers) > 1:
debug.warning('multiple wrappers found', self.base_func,
wrappers)
# This is here, that the wrapper gets executed.
f = wrappers[0]
debug.dbg('decorator end', f)
if f != self.base_func and isinstance(f, pr.Function):
f = Function(f)
return f
def get_decorated_func(self, instance=None):
decorated_func = self._decorated_func(instance)
if decorated_func == self.base_func:
return self
if decorated_func is None:
# If the decorator func is not found, just ignore the decorator
# function, because sometimes decorators are just really
# complicated.
return Function(self.base_func, True)
return decorated_func
def get_magic_method_names(self):
return builtin.Builtin.magic_function_scope.get_defined_names()
def get_magic_method_scope(self):
return builtin.Builtin.magic_function_scope
def __getattr__(self, name):
return getattr(self.base_func, name)
def __repr__(self):
dec = ''
if self._decorated_func() != self.base_func:
dec = " is " + repr(self._decorated_func())
return "<e%s of %s%s>" % (type(self).__name__, self.base_func, dec)
class Execution(Executable):
"""
This class is used to evaluate functions and their returns.
This is the most complicated class, because it contains the logic to
transfer parameters. It is even more complicated, because there may be
multiple calls to functions and recursion has to be avoided. But this is
responsibility of the decorators.
"""
def follow_var_arg(self, index):
try:
stmt = self.var_args[index]
except IndexError:
return []
else:
if isinstance(stmt, pr.Statement):
return evaluate.follow_statement(stmt)
else:
return [stmt] # just some arbitrary object
@property
@cache.memoize_default()
def decorated(self):
"""Get the decorated version of the input"""
base = self.base
if self.base.isinstance(Function):
base = base.get_decorated_func()
return base
@cache.memoize_default(default=())
@recursion.ExecutionRecursionDecorator
def get_return_types(self, evaluate_generator=False):
""" Get the return types of a function. """
base = self.decorated
stmts = []
if base.parent == builtin.Builtin.scope \
and not isinstance(base, (Generator, Array)):
func_name = str(base.name)
# some implementations of builtins:
if func_name == 'getattr':
# follow the first param
objects = self.follow_var_arg(0)
names = self.follow_var_arg(1)
for obj in objects:
if not isinstance(obj, (Instance, Class, pr.Module)):
debug.warning('getattr called without instance')
continue
for arr_name in names:
if not isinstance(arr_name, Instance):
debug.warning('getattr called without str')
continue
if len(arr_name.var_args) != 1:
debug.warning('jedi getattr is too simple')
key = arr_name.var_args[0]
stmts += evaluate.follow_path(iter([key]), obj, base)
return stmts
elif func_name == 'type':
# otherwise it would be a metaclass
if len(self.var_args) == 1:
objects = self.follow_var_arg(0)
return [o.base for o in objects if isinstance(o, Instance)]
elif func_name == 'super':
# TODO make this able to detect multiple inheritance supers
accept = (pr.Function,)
func = self.var_args.get_parent_until(accept)
if func.isinstance(*accept):
cls = func.get_parent_until(accept + (pr.Class,),
include_current=False)
if isinstance(cls, pr.Class):
cls = Class(cls)
su = cls.get_super_classes()
if su:
return [Instance(su[0])]
return []
if base.isinstance(Class):
# There maybe executions of executions.
stmts = [Instance(base, self.var_args)]
elif isinstance(base, Generator):
return base.iter_content()
else:
try:
base.returns # Test if it is a function
except AttributeError:
if hasattr(base, 'execute_subscope_by_name'):
try:
stmts = base.execute_subscope_by_name('__call__',
self.var_args)
except KeyError:
debug.warning("no __call__ func available", base)
else:
debug.warning("no execution possible", base)
else:
stmts = self._get_function_returns(base, evaluate_generator)
debug.dbg('exec result: %s in %s' % (stmts, self))
return imports.strip_imports(stmts)
def _get_function_returns(self, func, evaluate_generator):
""" A normal Function execution """
# Feed the listeners, with the params.
for listener in func.listeners:
listener.execute(self.get_params())
if func.is_generator and not evaluate_generator:
return [Generator(func, self.var_args)]
else:
stmts = docstrings.find_return_types(func)
for r in self.returns:
if r is not None:
stmts += evaluate.follow_statement(r)
return stmts
@cache.memoize_default(default=())
def get_params(self):
"""
This returns the params for an Execution/Instance and is injected as a
'hack' into the pr.Function class.
This needs to be here, because Instance can have __init__ functions,
which act the same way as normal functions.
"""
def gen_param_name_copy(param, keys=(), values=(), array_type=None):
"""
Create a param with the original scope (of varargs) as parent.
"""
if isinstance(self.var_args, pr.Array):
parent = self.var_args.parent
start_pos = self.var_args.start_pos
else:
parent = self.decorated
start_pos = 0, 0
new_param = copy.copy(param)
new_param.is_generated = True
if parent is not None:
new_param.parent = parent
# create an Array (-> needed for *args/**kwargs tuples/dicts)
arr = pr.Array(self._sub_module, start_pos, array_type, parent)
arr.values = values
key_stmts = []
for key in keys:
stmt = pr.Statement(self._sub_module, [], [], [],
start_pos, None)
stmt._commands = [key]
key_stmts.append(stmt)
arr.keys = key_stmts
arr.type = array_type
new_param._commands = [arr]
name = copy.copy(param.get_name())
name.parent = new_param
return name
result = []
start_offset = 0
if isinstance(self.decorated, InstanceElement):
# Care for self -> just exclude it and add the instance
start_offset = 1
self_name = copy.copy(self.decorated.params[0].get_name())
self_name.parent = self.decorated.instance
result.append(self_name)
param_dict = {}
for param in self.decorated.params:
param_dict[str(param.get_name())] = param
# There may be calls, which don't fit all the params, this just ignores
# it.
var_arg_iterator = self.get_var_args_iterator()
non_matching_keys = []
keys_used = set()
keys_only = False
for param in self.decorated.params[start_offset:]:
# The value and key can both be null. There, the defaults apply.
# args / kwargs will just be empty arrays / dicts, respectively.
# Wrong value count is just ignored. If you try to test cases that
# are not allowed in Python, Jedi will maybe not show any
# completions.
key, value = next(var_arg_iterator, (None, None))
while key:
keys_only = True
try:
key_param = param_dict[str(key)]
except KeyError:
non_matching_keys.append((key, value))
else:
keys_used.add(str(key))
result.append(gen_param_name_copy(key_param,
values=[value]))
key, value = next(var_arg_iterator, (None, None))
commands = param.get_commands()
keys = []
values = []
array_type = None
ignore_creation = False
if commands[0] == '*':
# *args param
array_type = pr.Array.TUPLE
if value:
values.append(value)
for key, value in var_arg_iterator:
# Iterate until a key argument is found.
if key:
var_arg_iterator.push_back((key, value))
break
values.append(value)
elif commands[0] == '**':
# **kwargs param
array_type = pr.Array.DICT
if non_matching_keys:
keys, values = zip(*non_matching_keys)
elif not keys_only:
# normal param
if value is not None:
values = [value]
else:
if param.assignment_details:
# No value: return the default values.
ignore_creation = True
result.append(param.get_name())
param.is_generated = True
else:
# If there is no assignment detail, that means there is
# no assignment, just the result. Therefore nothing has
# to be returned.
values = []
# Just ignore all the params that are without a key, after one
# keyword argument was set.
if not ignore_creation and (not keys_only or commands[0] == '**'):
keys_used.add(str(key))
result.append(gen_param_name_copy(param, keys=keys,
values=values, array_type=array_type))
if keys_only:
# sometimes param arguments are not completely written (which would
# create an Exception, but we have to handle that).
for k in set(param_dict) - keys_used:
result.append(gen_param_name_copy(param_dict[k]))
return result
def get_var_args_iterator(self):
"""
Yields a key/value pair, the key is None, if its not a named arg.
"""
def iterate():
# `var_args` is typically an Array, and not a list.
for stmt in self.var_args:
if not isinstance(stmt, pr.Statement):
if stmt is None:
yield None, None
continue
old = stmt
# generate a statement if it's not already one.
module = builtin.Builtin.scope
stmt = pr.Statement(module, [], [], [], (0, 0), None)
stmt._commands = [old]
# *args
commands = stmt.get_commands()
if not len(commands):
continue
if commands[0] == '*':
arrays = evaluate.follow_call_list(commands[1:])
# *args must be some sort of an array, otherwise -> ignore
for array in arrays:
if isinstance(array, Array):
for field_stmt in array: # yield from plz!
yield None, field_stmt
elif isinstance(array, Generator):
for field_stmt in array.iter_content():
yield None, helpers.FakeStatement(field_stmt)
# **kwargs
elif commands[0] == '**':
arrays = evaluate.follow_call_list(commands[1:])
for array in arrays:
if isinstance(array, Array):
for key_stmt, value_stmt in array.items():
# first index, is the key if syntactically correct
call = key_stmt.get_commands()[0]
if isinstance(call, pr.Name):
yield call, value_stmt
elif type(call) is pr.Call:
yield call.name, value_stmt
# Normal arguments (including key arguments).
else:
if stmt.assignment_details:
key_arr, op = stmt.assignment_details[0]
# named parameter
if key_arr and isinstance(key_arr[0], pr.Call):
yield key_arr[0].name, stmt
else:
yield None, stmt
return iter(common.PushBackIterator(iterate()))
def get_defined_names(self):
"""
Call the default method with the own instance (self implements all
the necessary functions). Add also the params.
"""
return self.get_params() + pr.Scope.get_set_vars(self)
get_set_vars = get_defined_names
@common.rethrow_uncaught
def copy_properties(self, prop):
"""
Literally copies a property of a Function. Copying is very expensive,
because it is something like `copy.deepcopy`. However, these copied
objects can be used for the executions, as if they were in the
execution.
"""
# Copy all these lists into this local function.
attr = getattr(self.decorated, prop)
objects = []
for element in attr:
if element is None:
copied = element
else:
copied = helpers.fast_parent_copy(element)
copied.parent = self._scope_copy(copied.parent)
if isinstance(copied, pr.Function):
copied = Function(copied)
objects.append(copied)
return objects
def __getattr__(self, name):
if name not in ['start_pos', 'end_pos', 'imports', '_sub_module']:
raise AttributeError('Tried to access %s: %s. Why?' % (name, self))
return getattr(self.decorated, name)
@cache.memoize_default()
@common.rethrow_uncaught
def _scope_copy(self, scope):
""" Copies a scope (e.g. if) in an execution """
# TODO method uses different scopes than the subscopes property.
# just check the start_pos, sometimes it's difficult with closures
# to compare the scopes directly.
if scope.start_pos == self.start_pos:
return self
else:
copied = helpers.fast_parent_copy(scope)
copied.parent = self._scope_copy(copied.parent)
return copied
@property
@cache.memoize_default()
def returns(self):
return self.copy_properties('returns')
@property
@cache.memoize_default()
def asserts(self):
return self.copy_properties('asserts')
@property
@cache.memoize_default()
def statements(self):
return self.copy_properties('statements')
@property
@cache.memoize_default()
def subscopes(self):
return self.copy_properties('subscopes')
def get_statement_for_position(self, pos):
return pr.Scope.get_statement_for_position(self, pos)
def __repr__(self):
return "<%s of %s>" % \
(type(self).__name__, self.decorated)
class Generator(use_metaclass(cache.CachedMetaClass, pr.Base)):
""" Cares for `yield` statements. """
def __init__(self, func, var_args):
super(Generator, self).__init__()
self.func = func
self.var_args = var_args
def get_defined_names(self):
"""
Returns a list of names that define a generator, which can return the
content of a generator.
"""
names = []
none_pos = (0, 0)
executes_generator = ('__next__', 'send')
for n in ('close', 'throw') + executes_generator:
name = pr.Name(builtin.Builtin.scope, [(n, none_pos)],
none_pos, none_pos)
if n in executes_generator:
name.parent = self
else:
name.parent = builtin.Builtin.scope
names.append(name)
debug.dbg('generator names', names)
return names
def iter_content(self):
""" returns the content of __iter__ """
return Execution(self.func, self.var_args).get_return_types(True)
def get_index_types(self, index=None):
debug.warning('Tried to get array access on a generator', self)
return []
def __getattr__(self, name):
if name not in ['start_pos', 'end_pos', 'parent', 'get_imports',
'asserts', 'doc', 'docstr', 'get_parent_until', 'get_code',
'subscopes']:
raise AttributeError("Accessing %s of %s is not allowed."
% (self, name))
return getattr(self.func, name)
def __repr__(self):
return "<%s of %s>" % (type(self).__name__, self.func)
class Array(use_metaclass(cache.CachedMetaClass, pr.Base)):
"""
Used as a mirror to pr.Array, if needed. It defines some getter
methods which are important in this module.
"""
def __init__(self, array):
self._array = array
def get_index_types(self, index_arr=None):
""" Get the types of a specific index or all, if not given """
if index_arr is not None:
if index_arr and [x for x in index_arr if ':' in x.get_commands()]:
# array slicing
return [self]
index_possibilities = self._follow_values(index_arr)
if len(index_possibilities) == 1:
# This is indexing only one element, with a fixed index number,
# otherwise it just ignores the index (e.g. [1+1]).
index = index_possibilities[0]
if isinstance(index, Instance) \
and str(index.name) in ['int', 'str'] \
and len(index.var_args) == 1:
# TODO this is just very hackish and a lot of use cases are
# being ignored
with common.ignored(KeyError, IndexError,
UnboundLocalError, TypeError):
return self.get_exact_index_types(index.var_args[0])
result = list(self._follow_values(self._array.values))
result += dynamic.check_array_additions(self)
return set(result)
def get_exact_index_types(self, mixed_index):
""" Here the index is an int/str. Raises IndexError/KeyError """
index = mixed_index
if self.type == pr.Array.DICT:
index = None
for i, key_statement in enumerate(self._array.keys):
# Because we only want the key to be a string.
key_commands = key_statement.get_commands()
if len(key_commands) != 1: # cannot deal with complex strings
continue
key = key_commands[0]
if isinstance(key, pr.Call) and key.type == pr.Call.STRING:
str_key = key.name
elif isinstance(key, pr.Name):
str_key = str(key)
if mixed_index == str_key:
index = i
break
if index is None:
raise KeyError('No key found in dictionary')
# Can raise an IndexError
values = [self._array.values[index]]
return self._follow_values(values)
def _follow_values(self, values):
""" helper function for the index getters """
return list(itertools.chain.from_iterable(evaluate.follow_statement(v)
for v in values))
def get_defined_names(self):
"""
This method generates all `ArrayMethod` for one pr.Array.
It returns e.g. for a list: append, pop, ...
"""
# `array.type` is a string with the type, e.g. 'list'.
scope = evaluate.find_name(builtin.Builtin.scope, self._array.type)[0]
scope = Instance(scope)
names = scope.get_defined_names()
return [ArrayMethod(n) for n in names]
@property
def parent(self):
return builtin.Builtin.scope
def get_parent_until(self):
return builtin.Builtin.scope
def __getattr__(self, name):
if name not in ['type', 'start_pos', 'get_only_subelement', 'parent',
'get_parent_until', 'items']:
raise AttributeError('Strange access on %s: %s.' % (self, name))
return getattr(self._array, name)
def __getitem__(self):
return self._array.__getitem__()
def __iter__(self):
return self._array.__iter__()
def __len__(self):
return self._array.__len__()
def __repr__(self):
return "<e%s of %s>" % (type(self).__name__, self._array)
class ArrayMethod(object):
"""
A name, e.g. `list.append`, it is used to access the original array
methods.
"""
def __init__(self, name):
super(ArrayMethod, self).__init__()
self.name = name
def __getattr__(self, name):
# Set access privileges:
if name not in ['parent', 'names', 'start_pos', 'end_pos', 'get_code']:
raise AttributeError('Strange accesson %s: %s.' % (self, name))
return getattr(self.name, name)
def get_parent_until(self):
return builtin.Builtin.scope
def __repr__(self):
return "<%s of %s>" % (type(self).__name__, self.name)

435
jedi/fast_parser.py Normal file
View File

@@ -0,0 +1,435 @@
"""
Basically a parser that is faster, because it tries to parse only parts and if
anything changes, it only reparses the changed parts. But because it's not
finished (and still not working as I want), I won't document it any further.
"""
import re
from jedi._compatibility import use_metaclass
from jedi import settings
from jedi import parsing
from jedi import parsing_representation as pr
from jedi import cache
from jedi import common
SCOPE_CONTENTS = ['asserts', 'subscopes', 'imports', 'statements', 'returns']
class Module(pr.Simple, pr.Module):
def __init__(self, parsers):
super(Module, self).__init__(self, (1, 0))
self.parsers = parsers
self.reset_caches()
self.start_pos = 1, 0
self.end_pos = None, None
def reset_caches(self):
""" This module does a whole lot of caching, because it uses different
parsers. """
self._used_names = None
for p in self.parsers:
p.user_scope = None
p.user_stmt = None
def __getattr__(self, name):
if name.startswith('__'):
raise AttributeError('Not available!')
else:
return getattr(self.parsers[0].module, name)
@property
def used_names(self):
if self._used_names is None:
dct = {}
for p in self.parsers:
for k, statement_set in p.module.used_names.items():
if k in dct:
dct[k] |= statement_set
else:
dct[k] = set(statement_set)
self._used_names = dct
return self._used_names
def __repr__(self):
return "<%s: %s@%s-%s>" % (type(self).__name__, self.name,
self.start_pos[0], self.end_pos[0])
class CachedFastParser(type):
""" This is a metaclass for caching `FastParser`. """
def __call__(self, source, module_path=None, user_position=None):
if not settings.fast_parser:
return parsing.Parser(source, module_path, user_position)
pi = cache.parser_cache.get(module_path, None)
if pi is None or isinstance(pi.parser, parsing.Parser):
p = super(CachedFastParser, self).__call__(source, module_path,
user_position)
else:
p = pi.parser # pi is a `cache.ParserCacheItem`
p.update(source, user_position)
return p
class ParserNode(object):
def __init__(self, parser, code, parent=None):
self.parent = parent
self.code = code
self.hash = hash(code)
self.children = []
# must be created before new things are added to it.
self.save_contents(parser)
def save_contents(self, parser):
self.parser = parser
try:
# with fast_parser we have either 1 subscope or only statements.
self.content_scope = parser.module.subscopes[0]
except IndexError:
self.content_scope = parser.module
scope = self.content_scope
self._contents = {}
for c in SCOPE_CONTENTS:
self._contents[c] = list(getattr(scope, c))
self._is_generator = scope.is_generator
self.old_children = self.children
self.children = []
def reset_contents(self):
scope = self.content_scope
for key, c in self._contents.items():
setattr(scope, key, list(c))
scope.is_generator = self._is_generator
self.parser.user_scope = self.parser.module
if self.parent is None:
# Global vars of the first one can be deleted, in the global scope
# they make no sense.
self.parser.module.global_vars = []
for c in self.children:
c.reset_contents()
def parent_until_indent(self, indent=None):
if indent is None or self.indent >= indent and self.parent:
self.old_children = []
if self.parent is not None:
return self.parent.parent_until_indent(indent)
return self
@property
def indent(self):
if not self.parent:
return 0
module = self.parser.module
try:
el = module.subscopes[0]
except IndexError:
try:
el = module.statements[0]
except IndexError:
try:
el = module.imports[0]
except IndexError:
try:
el = [r for r in module.returns if r is not None][0]
except IndexError:
return self.parent.indent + 1
return el.start_pos[1]
def _set_items(self, parser, set_parent=False):
# insert parser objects into current structure
scope = self.content_scope
for c in SCOPE_CONTENTS:
content = getattr(scope, c)
items = getattr(parser.module, c)
if set_parent:
for i in items:
if i is None:
continue # happens with empty returns
i.parent = scope.use_as_parent
if isinstance(i, (pr.Function, pr.Class)):
for d in i.decorators:
d.parent = scope.use_as_parent
content += items
# global_vars
cur = self
while cur.parent is not None:
cur = cur.parent
cur.parser.module.global_vars += parser.module.global_vars
scope.is_generator |= parser.module.is_generator
def add_node(self, node, set_parent=False):
"""Adding a node means adding a node that was already added earlier"""
self.children.append(node)
self._set_items(node.parser, set_parent=set_parent)
node.old_children = node.children
node.children = []
return node
def add_parser(self, parser, code):
return self.add_node(ParserNode(parser, code, self), True)
class FastParser(use_metaclass(CachedFastParser)):
def __init__(self, code, module_path=None, user_position=None):
# set values like `pr.Module`.
self.module_path = module_path
self.user_position = user_position
self._user_scope = None
self.current_node = None
self.parsers = []
self.module = Module(self.parsers)
self.reset_caches()
try:
self._parse(code)
except:
# FastParser is cached, be careful with exceptions
self.parsers[:] = []
raise
@property
def user_scope(self):
if self._user_scope is None:
for p in self.parsers:
if p.user_scope:
if isinstance(p.user_scope, pr.SubModule):
continue
self._user_scope = p.user_scope
if isinstance(self._user_scope, pr.SubModule) \
or self._user_scope is None:
self._user_scope = self.module
return self._user_scope
@property
def user_stmt(self):
if self._user_stmt is None:
for p in self.parsers:
if p.user_stmt:
self._user_stmt = p.user_stmt
break
return self._user_stmt
def update(self, code, user_position=None):
self.user_position = user_position
self.reset_caches()
try:
self._parse(code)
except:
# FastParser is cached, be careful with exceptions
self.parsers[:] = []
raise
def _scan_user_scope(self, sub_module):
""" Scan with self.user_position. """
for scope in sub_module.statements + sub_module.subscopes:
if isinstance(scope, pr.Scope):
if scope.start_pos <= self.user_position <= scope.end_pos:
return self._scan_user_scope(scope) or scope
return None
def _split_parts(self, code):
"""
Split the code into different parts. This makes it possible to parse
each part seperately and therefore cache parts of the file and not
everything.
"""
def add_part():
txt = '\n'.join(current_lines)
if txt:
if add_to_last and parts:
parts[-1] += '\n' + txt
else:
parts.append(txt)
current_lines[:] = []
r_keyword = '^[ \t]*(def|class|@|%s)' % '|'.join(common.FLOWS)
lines = code.splitlines()
current_lines = []
parts = []
is_decorator = False
current_indent = 0
old_indent = 0
new_indent = False
in_flow = False
add_to_last = False
# All things within flows are simply being ignored.
for i, l in enumerate(lines):
# check for dedents
m = re.match('^([\t ]*)(.?)', l)
indent = len(m.group(1))
if m.group(2) in ['', '#']:
current_lines.append(l) # just ignore comments and blank lines
continue
if indent < current_indent: # -> dedent
current_indent = indent
new_indent = False
if not in_flow or indent < old_indent:
add_part()
add_to_last = False
in_flow = False
elif new_indent:
current_indent = indent
new_indent = False
# Check lines for functions/classes and split the code there.
if not in_flow:
m = re.match(r_keyword, l)
if m:
in_flow = m.group(1) in common.FLOWS
if not is_decorator and not in_flow:
add_part()
add_to_last = False
is_decorator = '@' == m.group(1)
if not is_decorator:
old_indent = current_indent
current_indent += 1 # it must be higher
new_indent = True
elif is_decorator:
is_decorator = False
add_to_last = True
current_lines.append(l)
add_part()
return parts
def _parse(self, code):
""" :type code: str """
def empty_parser():
new, temp = self._get_parser('', '', 0, [], False)
return new
parts = self._split_parts(code)
self.parsers[:] = []
line_offset = 0
start = 0
p = None
is_first = True
for code_part in parts:
lines = code_part.count('\n') + 1
if is_first or line_offset >= p.end_pos[0]:
indent = len(re.match(r'[ \t]*', code_part).group(0))
if is_first and self.current_node is not None:
nodes = [self.current_node]
else:
nodes = []
if self.current_node is not None:
self.current_node = \
self.current_node.parent_until_indent(indent)
nodes += self.current_node.old_children
# check if code_part has already been parsed
# print '#'*45,line_offset, p and p.end_pos, '\n', code_part
p, node = self._get_parser(code_part, code[start:],
line_offset, nodes, not is_first)
if is_first and p.module.subscopes:
# special case, we cannot use a function subscope as a
# base scope, subscopes would save all the other contents
new = empty_parser()
if self.current_node is None:
self.current_node = ParserNode(new, '')
else:
self.current_node.save_contents(new)
self.parsers.append(new)
is_first = False
if is_first:
if self.current_node is None:
self.current_node = ParserNode(p, code_part)
else:
self.current_node.save_contents(p)
else:
if node is None:
self.current_node = \
self.current_node.add_parser(p, code_part)
else:
self.current_node = self.current_node.add_node(node)
if self.current_node.parent and (isinstance(p.user_scope,
pr.SubModule) or p.user_scope is None) \
and self.user_position \
and p.start_pos <= self.user_position < p.end_pos:
p.user_scope = self.current_node.parent.content_scope
self.parsers.append(p)
is_first = False
else:
# print '#'*45, line_offset, p.end_pos, 'theheck\n', code_part
pass
line_offset += lines
start += len(code_part) + 1 # +1 for newline
if self.parsers:
self.current_node = self.current_node.parent_until_indent()
else:
self.parsers.append(empty_parser())
self.module.end_pos = self.parsers[-1].end_pos
# print(self.parsers[0].module.get_code())
del code
def _get_parser(self, code, parser_code, line_offset, nodes, no_docstr):
h = hash(code)
hashes = [n.hash for n in nodes]
node = None
try:
index = hashes.index(h)
if nodes[index].code != code:
raise ValueError()
except ValueError:
p = parsing.Parser(parser_code, self.module_path,
self.user_position, offset=(line_offset, 0),
is_fast_parser=True, top_module=self.module,
no_docstr=no_docstr)
p.module.parent = self.module
else:
if nodes[index] != self.current_node:
offset = int(nodes[0] == self.current_node)
self.current_node.old_children.pop(index - offset)
node = nodes.pop(index)
p = node.parser
m = p.module
m.line_offset += line_offset + 1 - m.start_pos[0]
if self.user_position is not None and \
m.start_pos[0] <= self.user_position[0] <= m.end_pos[0]:
# It's important to take care of the whole user
# positioning stuff, if no reparsing is being done.
p.user_stmt = m.get_statement_for_position(
self.user_position, include_imports=True)
if p.user_stmt:
p.user_scope = p.user_stmt.parent
else:
p.user_scope = self._scan_user_scope(m) or m
return p, node
def reset_caches(self):
self._user_scope = None
self._user_stmt = None
self.module.reset_caches()
if self.current_node is not None:
self.current_node.reset_contents()

View File

@@ -1,147 +1,9 @@
from __future__ import with_statement
import copy
import contextlib
import parsing
import evaluate
import debug
import builtin
import settings
class RecursionDecorator(object):
"""
A decorator to detect recursions in statements. In a recursion a statement
at the same place, in the same module may not be executed two times.
"""
def __init__(self, func):
self.func = func
self.reset()
def __call__(self, stmt, *args, **kwargs):
#print stmt, len(self.node_statements())
if self.push_stmt(stmt):
return []
else:
result = self.func(stmt, *args, **kwargs)
self.pop_stmt()
return result
def push_stmt(self, stmt):
self.current = RecursionNode(stmt, self.current)
if self._check_recursion():
debug.warning('catched recursion', stmt)
self.pop_stmt()
return True
return False
def pop_stmt(self):
if self.current is not None:
# I don't know how current can be None, but sometimes it happens
# with Python3.
self.current = self.current.parent
def _check_recursion(self):
test = self.current
while True:
test = test.parent
if self.current == test:
return True
if not test:
return False
def reset(self):
self.top = None
self.current = None
def node_statements(self):
result = []
n = self.current
while n:
result.insert(0, n.stmt)
n = n.parent
return result
class RecursionNode(object):
""" A node of the RecursionDecorator. """
def __init__(self, stmt, parent):
self.script = stmt.get_parent_until()
self.position = stmt.start_pos
self.parent = parent
self.stmt = stmt
# Don't check param instances, they are not causing recursions
# The same's true for the builtins, because the builtins are really
# simple.
self.is_ignored = isinstance(stmt, parsing.Param) \
or (self.script == builtin.Builtin.scope)
def __eq__(self, other):
if not other:
return None
return self.script == other.script \
and self.position == other.position \
and not self.is_ignored and not other.is_ignored
class ExecutionRecursionDecorator(object):
"""
Catches recursions of executions.
It is designed like a Singelton. Only one instance should exist.
"""
def __init__(self, func):
self.func = func
self.reset()
def __call__(self, execution, evaluate_generator=False):
debug.dbg('Execution recursions: %s' % execution, self.recursion_level,
self.execution_count, len(self.execution_funcs))
if self.check_recursion(execution, evaluate_generator):
result = []
else:
result = self.func(execution, evaluate_generator)
self.cleanup()
return result
@classmethod
def cleanup(cls):
cls.parent_execution_funcs.pop()
cls.recursion_level -= 1
@classmethod
def check_recursion(cls, execution, evaluate_generator):
in_par_execution_funcs = execution.base in cls.parent_execution_funcs
in_execution_funcs = execution.base in cls.execution_funcs
cls.recursion_level += 1
cls.execution_count += 1
cls.execution_funcs.add(execution.base)
cls.parent_execution_funcs.append(execution.base)
if cls.execution_count > settings.max_executions:
return True
if isinstance(execution.base, (evaluate.Generator, evaluate.Array)):
return False
module = execution.get_parent_until()
if evaluate_generator or module == builtin.Builtin.scope:
return False
if in_par_execution_funcs:
if cls.recursion_level > settings.max_function_recursion_level:
return True
if in_execution_funcs and \
len(cls.execution_funcs) > settings.max_until_execution_unique:
return True
if cls.execution_count > settings.max_executions_without_builtins:
return True
return False
@classmethod
def reset(cls):
cls.recursion_level = 0
cls.parent_execution_funcs = []
cls.execution_funcs = set()
cls.execution_count = 0
from jedi import common
from jedi import parsing_representation as pr
def fast_parent_copy(obj):
@@ -154,30 +16,40 @@ def fast_parent_copy(obj):
new_obj = copy.copy(obj)
new_elements[obj] = new_obj
items = new_obj.__dict__.items()
try:
items = list(new_obj.__dict__.items())
except AttributeError:
# __dict__ not available, because of __slots__
items = []
before = ()
for cls in new_obj.__class__.__mro__:
with common.ignored(AttributeError):
if before == cls.__slots__:
continue
before = cls.__slots__
items += [(n, getattr(new_obj, n)) for n in before]
for key, value in items:
# replace parent (first try _parent and then parent)
if key in ['parent', '_parent', '_parent_stmt'] \
and value is not None:
if key in ['parent', '_parent'] and value is not None:
if key == 'parent' and '_parent' in items:
# parent can be a property
continue
try:
with common.ignored(KeyError):
setattr(new_obj, key, new_elements[value])
except KeyError:
pass
elif key in ['parent_stmt', 'parent_function']:
elif key in ['parent_function', 'use_as_parent', '_sub_module']:
continue
elif isinstance(value, list):
setattr(new_obj, key, list_rec(value))
elif isinstance(value, (parsing.Simple, parsing.Call)):
elif isinstance(value, (pr.Simple, pr.Call)):
setattr(new_obj, key, recursion(value))
return new_obj
def list_rec(list_obj):
copied_list = list_obj[:] # lists, tuples, strings, unicode
for i, el in enumerate(copied_list):
if isinstance(el, (parsing.Simple, parsing.Call)):
if isinstance(el, (pr.Simple, pr.Call)):
copied_list[i] = recursion(el)
elif isinstance(el, list):
copied_list[i] = list_rec(el)
@@ -185,82 +57,81 @@ def fast_parent_copy(obj):
return recursion(obj)
def generate_param_array(args_tuple, parent_stmt=None):
""" This generates an array, that can be used as a param. """
values = []
for arg in args_tuple:
if arg is None:
values.append([])
def check_arr_index(arr, pos):
positions = arr.arr_el_pos
for index, comma_pos in enumerate(positions):
if pos < comma_pos:
return index
return len(positions)
def array_for_pos(stmt, pos, array_types=None):
"""Searches for the array and position of a tuple"""
def search_array(arr, pos):
if arr.type == 'dict':
for stmt in arr.values + arr.keys:
new_arr, index = array_for_pos(stmt, pos, array_types)
if new_arr is not None:
return new_arr, index
else:
values.append([arg])
pos = None
arr = parsing.Array(pos, parsing.Array.TUPLE, parent_stmt, values=values)
evaluate.faked_scopes.append(arr)
return arr
for i, stmt in enumerate(arr):
new_arr, index = array_for_pos(stmt, pos, array_types)
if new_arr is not None:
return new_arr, index
if arr.start_pos < pos <= stmt.end_pos:
if not array_types or arr.type in array_types:
return arr, i
if len(arr) == 0 and arr.start_pos < pos < arr.end_pos:
if not array_types or arr.type in array_types:
return arr, 0
return None, 0
def search_call(call, pos):
arr, index = None, 0
if call.next is not None:
if isinstance(call.next, pr.Array):
arr, index = search_array(call.next, pos)
else:
arr, index = search_call(call.next, pos)
if not arr and call.execution is not None:
arr, index = search_array(call.execution, pos)
return arr, index
if stmt.start_pos >= pos >= stmt.end_pos:
return None, 0
for command in stmt.get_commands():
arr = None
if isinstance(command, pr.Array):
arr, index = search_array(command, pos)
elif isinstance(command, pr.Call):
arr, index = search_call(command, pos)
if arr is not None:
return arr, index
return None, 0
def scan_array_for_pos(arr, pos):
def search_function_definition(stmt, pos):
"""
Returns the function Call that match search_name in an Array.
Makes changes to arr!
Returns the function Call that matches the position before.
"""
def check_arr_index():
positions = arr.arr_el_pos
for index, comma_pos in enumerate(positions):
if pos < comma_pos:
return index
return len(positions)
call = None
stop = False
for sub in arr.values:
call = None
for s in sub:
if isinstance(s, parsing.Array):
new = scan_array_for_pos(s, pos)
if new[0] is not None:
call, index, stop = new
if stop:
return call, index, stop
elif isinstance(s, parsing.Call):
start_s = s
# check parts of calls
while s is not None:
if s.start_pos >= pos:
return call, check_arr_index(), stop
elif s.execution is not None:
end = s.execution.end_pos
if s.execution.start_pos < pos and \
(end is None or pos < end):
c, index, stop = scan_array_for_pos(
s.execution, pos)
if stop:
return c, index, stop
# call should return without execution and
# next
reset = c or s
if reset.execution.type not in \
[parsing.Array.TUPLE,
parsing.Array.NOARRAY]:
return start_s, index, False
reset.execution = None
reset.next = None
return c or start_s, index, True
s = s.next
# The third return is just necessary for recursion inside, because
# it needs to know when to stop iterating.
return call, check_arr_index(), stop
# some parts will of the statement will be removed
stmt = fast_parent_copy(stmt)
arr, index = array_for_pos(stmt, pos, [pr.Array.TUPLE, pr.Array.NOARRAY])
if arr is not None and isinstance(arr.parent, pr.Call):
call = arr.parent
while isinstance(call.parent, pr.Call):
call = call.parent
arr.parent.execution = None
return call if call.type == pr.Call.NAME else None, index, False
return None, 0, False
@contextlib.contextmanager
def scale_speed_settings(factor):
a = settings.max_executions
b = settings.max_until_execution_unique
settings.max_executions *= factor
settings.max_until_execution_unique *= factor
yield
settings.max_executions = a
settings.max_until_execution_unique = b
class FakeStatement(pr.Statement):
class SubModule():
line_offset = 0
def __init__(self, content):
cls = type(self)
p = 0, 0
super(cls, self).__init__(cls.SubModule, [], [], [content], p, p)

View File

@@ -1,55 +1,68 @@
"""
:mod:`imports` is here to resolve import statements and return the
modules/classes/functions/whatever, which they stand for. However there's not
any actual importing done. This module is about finding modules in the
filesystem. This can be quite tricky sometimes, because Python imports are not
always that simple.
This module uses imp for python up to 3.2 and importlib for python 3.3 on; the
correct implementation is delegated to _compatibility.
This module also supports import autocompletion, which means to complete
statements like ``from datetim`` (curser at the end would return ``datetime``).
"""
from __future__ import with_statement
import os
import pkgutil
import imp
import sys
import time
import builtin
import modules
import debug
import parsing
import evaluate
import itertools
import settings
from jedi._compatibility import find_module
from jedi import modules
from jedi import common
from jedi import debug
from jedi import parsing_representation as pr
from jedi import cache
import builtin
import evaluate
# for debugging purposes only
imports_processed = 0
star_import_cache = {}
class ModuleNotFound(Exception):
pass
class ImportPath(parsing.Base):
class ImportPath(pr.Base):
"""
An ImportPath is the path of a `parsing.Import` object.
An ImportPath is the path of a `pr.Import` object.
"""
class _GlobalNamespace(object):
def __init__(self):
self.start_pos = 0, 0
self.line_offset = 0
def get_defined_names(self):
return []
def get_imports(self):
return []
@property
def start_pos(self):
return (0, 0)
def get_parent_until(self):
return None
GlobalNamespace = _GlobalNamespace()
def __init__(self, import_stmt, is_like_search=False, kill_count=0,
direct_resolve=False):
direct_resolve=False, is_just_from=False):
self.import_stmt = import_stmt
self.is_like_search = is_like_search
self.direct_resolve = direct_resolve
self.is_partial_import = bool(kill_count)
self.is_just_from = is_just_from
self.is_partial_import = bool(max(0, kill_count))
path = import_stmt.get_parent_until().path
self.file_path = os.path.dirname(path) if path is not None else None
@@ -58,7 +71,7 @@ class ImportPath(parsing.Base):
if import_stmt.from_ns:
self.import_path += import_stmt.from_ns.names
if import_stmt.namespace:
if self.is_nested_import() and not direct_resolve:
if self._is_nested_import() and not direct_resolve:
self.import_path.append(import_stmt.namespace.names[0])
else:
self.import_path += import_stmt.namespace.names
@@ -69,29 +82,30 @@ class ImportPath(parsing.Base):
def __repr__(self):
return '<%s: %s>' % (type(self).__name__, self.import_stmt)
def is_nested_import(self):
def _is_nested_import(self):
"""
This checks for the special case of nested imports, without aliases and
from statement:
>>> import foo.bar
from statement::
import foo.bar
"""
return not self.import_stmt.alias and not self.import_stmt.from_ns \
and len(self.import_stmt.namespace.names) > 1 \
and not self.direct_resolve
and len(self.import_stmt.namespace.names) > 1 \
and not self.direct_resolve
def get_nested_import(self, parent):
def _get_nested_import(self, parent):
"""
See documentation of `self.is_nested_import`.
See documentation of `self._is_nested_import`.
Generates an Import statement, that can be used to fake nested imports.
"""
i = self.import_stmt
# This is not an existing Import statement. Therefore, set position to
# 0 (0 is not a valid line number).
zero = (0, 0)
n = parsing.Name(i.namespace.names[1:], zero, zero, self.import_stmt)
new = parsing.Import(zero, zero, n)
names = i.namespace.names[1:]
n = pr.Name(i._sub_module, names, zero, zero, self.import_stmt)
new = pr.Import(i._sub_module, zero, zero, n)
new.parent = parent
evaluate.faked_scopes.append(new)
debug.dbg('Generated a nested import: %s' % new)
return new
@@ -99,24 +113,41 @@ class ImportPath(parsing.Base):
names = []
for scope in self.follow():
if scope is ImportPath.GlobalNamespace:
if self.import_stmt.relative_count == 0:
names += self.get_module_names()
if self._is_relative_import() == 0:
names += self._get_module_names()
if self.file_path is not None:
path = os.path.abspath(self.file_path)
for i in range(self.import_stmt.relative_count - 1):
path = os.path.dirname(path)
names += self.get_module_names([path])
names += self._get_module_names([path])
if self._is_relative_import():
rel_path = self._get_relative_path() + '/__init__.py'
with common.ignored(IOError):
m = modules.Module(rel_path)
names += m.parser.module.get_defined_names()
else:
if on_import_stmt and isinstance(scope, parsing.Module) \
and scope.path.endswith('__init__.py'):
if on_import_stmt and isinstance(scope, pr.Module) \
and scope.path.endswith('__init__.py'):
pkg_path = os.path.dirname(scope.path)
names += self.get_module_names([pkg_path])
for s, scope_names in evaluate.get_names_for_scope(scope,
include_builtin=False):
paths = self._namespace_packages(pkg_path, self.import_path)
names += self._get_module_names([pkg_path] + paths)
if self.is_just_from:
# In the case of an import like `from x.` we don't need to
# add all the variables.
if ['os'] == self.import_path and not self._is_relative_import():
# os.path is a hardcoded exception, because it's a
# ``sys.modules`` modification.
p = (0, 0)
names.append(pr.Name(self.GlobalNamespace, [('path', p)],
p, p, self.import_stmt))
continue
for s, scope_names in evaluate.get_names_of_scope(scope,
include_builtin=False):
for n in scope_names:
if self.import_stmt.from_ns is None \
or self.is_partial_import:
or self.is_partial_import:
# from_ns must be defined to access module
# values plus a partial import means that there
# is something after the import, which
@@ -126,23 +157,41 @@ class ImportPath(parsing.Base):
names.append(n)
return names
def get_module_names(self, search_path=None):
def _get_module_names(self, search_path=None):
"""
Get the names of all modules in the search_path. This means file names
and not names defined in the files.
"""
if not search_path:
search_path = self.sys_path_with_modifications()
def generate_name(name):
return pr.Name(self.GlobalNamespace, [(name, inf_pos)],
inf_pos, inf_pos, self.import_stmt)
names = []
inf_pos = float('inf'), float('inf')
# add builtin module names
if search_path is None:
names += [generate_name(name) for name in sys.builtin_module_names]
if search_path is None:
search_path = self._sys_path_with_modifications()
for module_loader, name, is_pkg in pkgutil.iter_modules(search_path):
inf_pos = (float('inf'), float('inf'))
names.append(parsing.Name([(name, inf_pos)], inf_pos, inf_pos,
self.import_stmt))
names.append(generate_name(name))
return names
def sys_path_with_modifications(self):
def _sys_path_with_modifications(self):
# If you edit e.g. gunicorn, there will be imports like this:
# `from gunicorn import something`. But gunicorn is not in the
# sys.path. Therefore look if gunicorn is a parent directory, #56.
in_path = []
if self.import_path:
parts = self.file_path.split(os.path.sep)
for i, p in enumerate(parts):
if p == self.import_path[0]:
new = os.path.sep.join(parts[:i])
in_path.append(new)
module = self.import_stmt.get_parent_until()
return modules.sys_path_with_modifications(module)
return in_path + modules.sys_path_with_modifications(module)
def follow(self, is_goto=False):
"""
@@ -161,25 +210,31 @@ class ImportPath(parsing.Base):
return []
scopes = [scope]
scopes += itertools.chain.from_iterable(
remove_star_imports(s) for s in scopes)
scopes += remove_star_imports(scope)
# follow the rest of the import (not FS -> classes, functions)
if len(rest) > 1 or rest and self.is_like_search:
scopes = []
if ['os', 'path'] == self.import_path[:2] \
and not self._is_relative_import():
# This is a huge exception, we follow a nested import
# ``os.path``, because it's a very important one in Python
# that is being achieved by messing with ``sys.modules`` in
# ``os``.
scopes = evaluate.follow_path(iter(rest), scope, scope)
elif rest:
if is_goto:
scopes = itertools.chain.from_iterable(
evaluate.get_scopes_for_name(s, rest[0], is_goto=True)
for s in scopes)
evaluate.find_name(s, rest[0], is_goto=True)
for s in scopes)
else:
scopes = itertools.chain.from_iterable(
evaluate.follow_path(iter(rest), s, s)
for s in scopes)
evaluate.follow_path(iter(rest), s, s)
for s in scopes)
scopes = list(scopes)
if self.is_nested_import():
scopes.append(self.get_nested_import(scope))
if self._is_nested_import():
scopes.append(self._get_nested_import(scope))
else:
scopes = [ImportPath.GlobalNamespace]
debug.dbg('after import', scopes)
@@ -187,60 +242,122 @@ class ImportPath(parsing.Base):
evaluate.follow_statement.pop_stmt()
return scopes
def _is_relative_import(self):
return bool(self.import_stmt.relative_count)
def _get_relative_path(self):
path = self.file_path
for i in range(self.import_stmt.relative_count - 1):
path = os.path.dirname(path)
return path
def _namespace_packages(self, found_path, import_path):
"""
Returns a list of paths of possible ``pkgutil``/``pkg_resources``
namespaces. If the package is no "namespace package", an empty list is
returned.
"""
def follow_path(directories, paths):
try:
directory = next(directories)
except StopIteration:
return paths
else:
deeper_paths = []
for p in paths:
new = os.path.join(p, directory)
if os.path.isdir(new) and new != found_path:
deeper_paths.append(new)
return follow_path(directories, deeper_paths)
with open(os.path.join(found_path, '__init__.py')) as f:
content = f.read()
# these are strings that need to be used for namespace packages,
# the first one is ``pkgutil``, the second ``pkg_resources``.
options = 'declare_namespace(__name__)', 'extend_path(__path__'
if options[0] in content or options[1] in content:
# It is a namespace, now try to find the rest of the modules.
return follow_path(iter(import_path), sys.path)
return []
def _follow_file_system(self):
"""
Find a module with a path (of the module, like usb.backend.libusb10).
"""
def follow_str(ns, string):
debug.dbg('follow_module', ns, string)
def follow_str(ns_path, string):
debug.dbg('follow_module', ns_path, string)
path = None
if ns:
path = ns[1]
elif self.import_stmt.relative_count:
module = self.import_stmt.get_parent_until()
path = os.path.abspath(module.path)
for i in range(self.import_stmt.relative_count):
path = os.path.dirname(path)
if ns_path:
path = ns_path
elif self._is_relative_import():
path = self._get_relative_path()
global imports_processed
imports_processed += 1
if path is not None:
return imp.find_module(string, [path])
importing = find_module(string, [path])
else:
debug.dbg('search_module', string, self.file_path)
# Override the sys.path. It works only good that way.
# Injecting the path directly into `find_module` did not work.
sys.path, temp = sys_path_mod, sys.path
try:
i = imp.find_module(string)
except ImportError:
importing = find_module(string)
finally:
sys.path = temp
raise
sys.path = temp
return i
return importing
if self.file_path:
sys_path_mod = list(self.sys_path_with_modifications())
sys_path_mod.insert(0, self.file_path)
sys_path_mod = list(self._sys_path_with_modifications())
module = self.import_stmt.get_parent_until()
if not module.has_explicit_absolute_import:
# If the module explicitly asks for absolute imports,
# there's probably a bogus local one.
sys_path_mod.insert(0, self.file_path)
else:
sys_path_mod = list(builtin.get_sys_path())
sys_path_mod = list(modules.get_sys_path())
current_namespace = None
def module_not_found():
raise ModuleNotFound('The module you searched has not been found')
current_namespace = (None, None, None)
# now execute those paths
rest = []
for i, s in enumerate(self.import_path):
try:
current_namespace = follow_str(current_namespace, s)
current_namespace = follow_str(current_namespace[1], s)
except ImportError:
if current_namespace:
rest = self.import_path[i:]
else:
raise ModuleNotFound(
'The module you searched has not been found')
_continue = False
if self._is_relative_import() and len(self.import_path) == 1:
# follow `from . import some_variable`
rel_path = self._get_relative_path()
with common.ignored(ImportError):
current_namespace = follow_str(rel_path, '__init__')
elif current_namespace[2]: # is a package
for n in self._namespace_packages(current_namespace[1],
self.import_path[:i]):
try:
current_namespace = follow_str(n, s)
if current_namespace[1]:
_continue = True
break
except ImportError:
pass
if not _continue:
if current_namespace[1]:
rest = self.import_path[i:]
break
else:
module_not_found()
if current_namespace == (None, None, False):
module_not_found()
sys_path_mod.pop(0) # TODO why is this here?
path = current_namespace[1]
is_package_directory = current_namespace[2][2] == imp.PKG_DIRECTORY
is_package_directory = current_namespace[2]
f = None
if is_package_directory or current_namespace[0]:
@@ -255,9 +372,9 @@ class ImportPath(parsing.Base):
if path.endswith('.py'):
f = modules.Module(path, source)
else:
f = builtin.Parser(path=path)
f = builtin.BuiltinModule(path=path)
else:
f = builtin.Parser(name=path)
f = builtin.BuiltinModule(name=path)
return f.parser.module, rest
@@ -269,52 +386,15 @@ def strip_imports(scopes):
"""
result = []
for s in scopes:
if isinstance(s, parsing.Import):
if isinstance(s, pr.Import):
result += ImportPath(s).follow()
else:
result.append(s)
return result
def cache_star_import(func):
def wrapper(scope, *args, **kwargs):
try:
mods = star_import_cache[scope]
if mods[0] + settings.star_import_cache_validity > time.time():
return mods[1]
except KeyError:
pass
# cache is too old and therefore invalid or not available
invalidate_star_import_cache(scope)
mods = func(scope, *args, **kwargs)
star_import_cache[scope] = time.time(), mods
return mods
return wrapper
def invalidate_star_import_cache(module, only_main=False):
""" Important if some new modules are being reparsed """
try:
t, mods = star_import_cache[module]
del star_import_cache[module]
for m in mods:
invalidate_star_import_cache(m, only_main=True)
except KeyError:
pass
if not only_main:
# We need a list here because otherwise the list is being changed
# during the iteration in py3k: iteritems -> items.
for key, (t, mods) in list(star_import_cache.items()):
if module in mods:
invalidate_star_import_cache(key)
@cache_star_import
def remove_star_imports(scope, ignored_modules=[]):
@cache.cache_star_import
def remove_star_imports(scope, ignored_modules=()):
"""
Check a module for star imports:
>>> from module import *

171
jedi/interpret.py Normal file
View File

@@ -0,0 +1,171 @@
"""
Module to handle interpreted Python objects.
"""
import itertools
import tokenize
from jedi import parsing_representation as pr
class ObjectImporter(object):
"""
Import objects in "raw" namespace such as :func:`locals`.
"""
def __init__(self, scope):
self.scope = scope
count = itertools.count()
self._genname = lambda: '*jedi-%s*' % next(count)
"""
Generate unique variable names to avoid name collision.
To avoid name collision to already defined names, generated
names are invalid as Python identifier.
"""
def import_raw_namespace(self, raw_namespace):
"""
Import interpreted Python objects in a namespace.
Three kinds of objects are treated here.
1. Functions and classes. The objects imported like this::
from os.path import join
2. Modules. The objects imported like this::
import os
3. Instances. The objects created like this::
from datetime import datetime
dt = datetime(2013, 1, 1)
:type raw_namespace: dict
:arg raw_namespace: e.g., the dict given by `locals`
"""
scope = self.scope
for (variable, obj) in raw_namespace.items():
objname = getattr(obj, '__name__', None)
# Import functions and classes
module = getattr(obj, '__module__', None)
if module and objname:
fakeimport = self.make_fakeimport(module, objname, variable)
scope.add_import(fakeimport)
continue
# Import modules
if getattr(obj, '__file__', None) and objname:
fakeimport = self.make_fakeimport(objname)
scope.add_import(fakeimport)
continue
# Import instances
objclass = getattr(obj, '__class__', None)
module = getattr(objclass, '__module__', None)
if objclass and module:
alias = self._genname()
fakeimport = self.make_fakeimport(module, objclass.__name__,
alias)
fakestmt = self.make_fakestatement(variable, alias, call=True)
scope.add_import(fakeimport)
scope.add_statement(fakestmt)
continue
def make_fakeimport(self, module, variable=None, alias=None):
"""
Make a fake import object.
The following statements are created depending on what parameters
are given:
- only `module`: ``import <module>``
- `module` and `variable`: ``from <module> import <variable>``
- all: ``from <module> import <variable> as <alias>``
:type module: str
:arg module: ``<module>`` part in ``from <module> import ...``
:type variable: str
:arg variable: ``<variable>`` part in ``from ... import <variable>``
:type alias: str
:arg alias: ``<alias>`` part in ``... import ... as <alias>``.
:rtype: :class:`parsing_representation.Import`
"""
submodule = self.scope._sub_module
if variable:
varname = pr.Name(
module=submodule,
names=[(variable, (-1, 0))],
start_pos=(-1, 0),
end_pos=(None, None))
else:
varname = None
modname = pr.Name(
module=submodule,
names=[(module, (-1, 0))],
start_pos=(-1, 0),
end_pos=(None, None))
if alias:
aliasname = pr.Name(
module=submodule,
names=[(alias, (-1, 0))],
start_pos=(-1, 0),
end_pos=(None, None))
else:
aliasname = None
if varname:
fakeimport = pr.Import(
module=submodule,
namespace=varname,
from_ns=modname,
alias=aliasname,
start_pos=(-1, 0),
end_pos=(None, None))
else:
fakeimport = pr.Import(
module=submodule,
namespace=modname,
alias=aliasname,
start_pos=(-1, 0),
end_pos=(None, None))
return fakeimport
def make_fakestatement(self, lhs, rhs, call=False):
"""
Make a fake statement object that represents ``lhs = rhs``.
:type call: bool
:arg call: When `call` is true, make a fake statement that represents
``lhs = rhs()``.
:rtype: :class:`parsing_representation.Statement`
"""
submodule = self.scope._sub_module
lhsname = pr.Name(
module=submodule,
names=[(lhs, (0, 0))],
start_pos=(0, 0),
end_pos=(None, None))
rhsname = pr.Name(
module=submodule,
names=[(rhs, (0, 0))],
start_pos=(0, 0),
end_pos=(None, None))
token_list = [lhsname, (tokenize.OP, '=', (0, 0)), rhsname]
if call:
token_list.extend([
(tokenize.OP, '(', (0, 0)),
(tokenize.OP, ')', (0, 0)),
])
return pr.Statement(
module=submodule,
set_vars=[lhsname],
used_vars=[rhsname],
token_list=token_list,
start_pos=(0, 0),
end_pos=(None, None))

View File

@@ -1,18 +1,18 @@
import keyword
from _compatibility import is_py3k
import builtin
from __future__ import with_statement
import pydoc
import keyword
from jedi._compatibility import is_py3k
from jedi import parsing_representation as pr
from jedi import common
import builtin
try:
from pydoc_data import topics as pydoc_topics
except ImportError:
# Python 2.6
try:
import pydoc_topics
except ImportError:
# Python 2.5
pydoc_topics = None
import pydoc_topics
if is_py3k:
keys = keyword.kwlist
@@ -20,7 +20,7 @@ else:
keys = keyword.kwlist + ['None', 'False', 'True']
def get_keywords(string='', pos=(0, 0), all=False):
def keywords(string='', pos=(0, 0), all=False):
if all:
return set([Keyword(k, pos) for k in keys])
if string in keys:
@@ -28,6 +28,15 @@ def get_keywords(string='', pos=(0, 0), all=False):
return set()
def keyword_names(*args, **kwargs):
kwds = []
for k in keywords(*args, **kwargs):
start = k.start_pos
end = start[0], start[1] + len(k.name)
kwds.append(pr.Name(k.parent, [(k.name, start)], start, end, k))
return kwds
def get_operator(string, pos):
return Keyword(string, pos)
@@ -63,12 +72,10 @@ def imitate_pydoc(string):
# with unicode strings)
string = str(string)
h = pydoc.help
try:
with common.ignored(KeyError):
# try to access symbols
string = h.symbols[string]
string, _, related = string.partition(' ')
except KeyError:
pass
get_target = lambda s: h.topics.get(s, h.keywords.get(s))
while isinstance(string, str):

26
jedi/mixin/_sqlite3.pym Normal file
View File

@@ -0,0 +1,26 @@
def connect(database, timeout=None, isolation_level=None, detect_types=None, factory=None):
return Connection()
class Connection():
def cursor(self):
return Cursor()
class Cursor():
def cursor(self):
return Cursor()
def fetchone(self):
return Row()
def fetchmany(self, size=cursor.arraysize):
return [self.fetchone()]
def fetchall(self):
return [self.fetchone()]
class Row():
def keys(self):
return ['']

View File

@@ -22,7 +22,7 @@ def compile():
def expand(self):
return ''
def group(self):
def group(self, nr):
return ''
def groupdict(self):
@@ -37,14 +37,14 @@ def compile():
groups = 0
pattern = 'a'
def findall(self):
def findall(self, string, pos=None, endpos=None):
"""
findall(string[, pos[, endpos]]) --> list.
Return a list of all non-overlapping matches of pattern in string.
"""
return ['a']
def finditer(self):
def finditer(self, string, pos=None, endpos=None):
"""
finditer(string[, pos[, endpos]]) --> iterator.
Return an iterator over all non-overlapping matches for the
@@ -53,7 +53,7 @@ def compile():
"""
yield SRE_Match(self)
def match(self):
def match(self, string, pos=None, endpos=None):
"""
match(string[, pos[, endpos]]) --> match object or None.
Matches zero or more characters at the beginning of the string
@@ -61,10 +61,10 @@ def compile():
"""
return SRE_Match(self)
def scanner(self):
def scanner(self, string, pos=None, endpos=None):
pass
def search(self):
def search(self, string, pos=None, endpos=None):
"""
search(string[, pos[, endpos]]) --> match object or None.
Scan through string looking for a match, and return a corresponding
@@ -72,14 +72,14 @@ def compile():
"""
return SRE_Match(self)
def split(self):
def split(self, string, maxsplit=0]):
"""
split(string[, maxsplit = 0]) --> list.
Split string by the occurrences of pattern.
"""
return ['a']
def sub(self):
def sub(self, repl, string, count=0):
"""
sub(repl, string[, count = 0]) --> newstring
Return the string obtained by replacing the leftmost non-overlapping
@@ -87,7 +87,7 @@ def compile():
"""
return ''
def subn(self):
def subn(self, repl, string, count=0):
"""
subn(repl, string[, count = 0]) --> (newstring, number of subs)
Return the tuple (new_string, number_of_subs_made) found by replacing

View File

@@ -142,6 +142,18 @@ class set():
def copy(self):
return self
def difference(self, other):
return self - other
def intersection(self, other):
return self & other
def symmetric_difference(self, other):
return self ^ other
def union(self, other):
return self | other
class frozenset():
def __init__(self, iterable=[]):

View File

@@ -1,31 +1,77 @@
"""
Don't confuse these classes with :mod:`parsing_representation` modules, the
modules here can access these representation with ``module.parser.module``.
``Module`` exists mainly for caching purposes.
Basically :mod:`modules` offers the classes:
- ``CachedModule``, a base class for Cachedmodule.
- ``Module`` the class for all normal Python modules (not builtins, they are at
home at :mod:`builtin`).
- ``ModuleWithCursor``, holds the module information for :class:`api.Script`.
Apart from those classes there's a ``sys.path`` fetching function, as well as
`Virtual Env` and `Django` detection.
"""
from __future__ import with_statement
from _compatibility import exec_function
import re
import tokenize
import tokenizer as tokenize
import sys
import os
import time
from ast import literal_eval
import parsing
import builtin
import debug
import evaluate
import settings
import imports
from jedi._compatibility import exec_function, unicode
from jedi import cache
from jedi import parsing_representation as pr
from jedi import fast_parser
from jedi import debug
from jedi import common
class Module(builtin.CachedModule):
class CachedModule(object):
"""
The base type for all modules, which is not to be confused with
`parsing_representation.Module`. Caching happens here.
"""
def __init__(self, path=None, name=None):
self.path = path and os.path.abspath(path)
self.name = name
self._parser = None
@property
def parser(self):
""" get the parser lazy """
if self._parser is None:
self._parser = cache.load_module(self.path, self.name) \
or self._load_module()
return self._parser
def _get_source(self):
raise NotImplementedError()
def _load_module(self):
source = self._get_source()
p = self.path or self.name
p = fast_parser.FastParser(source, p)
cache.save_module(self.path, self.name, p)
return p
class Module(CachedModule):
"""
Manages all files, that are parsed and caches them.
:param path: The module path of the file.
:param source: The source code of the file.
"""
def __init__(self, path, source):
def __init__(self, path, source=None):
super(Module, self).__init__(path=path)
self.source = source
if source is None:
with open(path) as f:
source = f.read()
self.source = source_to_unicode(source)
self._line_cache = None
def _get_source(self):
@@ -49,67 +95,62 @@ class ModuleWithCursor(Module):
def __init__(self, path, source, position):
super(ModuleWithCursor, self).__init__(path, source)
self.position = position
self.source = source
self._path_until_cursor = None
# this two are only used, because there is no nonlocal in Python 2
self._line_temp = None
self._relevant_temp = None
self.source = source
self._part_parser = None
@property
def parser(self):
""" get the parser lazy """
if not self._parser:
try:
ts, parser = builtin.CachedModule.cache[self.path]
imports.invalidate_star_import_cache(parser.module)
del builtin.CachedModule.cache[self.path]
except KeyError:
pass
with common.ignored(KeyError):
parser = cache.parser_cache[self.path].parser
cache.invalidate_star_import_cache(parser.module)
# Call the parser already here, because it will be used anyways.
# Also, the position is here important (which will not be used by
# default), therefore fill the cache here.
self._parser = parsing.PyFuzzyParser(self.source, self.path,
self.position)
if self.path is not None:
builtin.CachedModule.cache[self.path] = time.time(), \
self._parser
self._parser = fast_parser.FastParser(self.source, self.path,
self.position)
# don't pickle that module, because it's changing fast
cache.save_module(self.path, self.name, self._parser,
pickling=False)
return self._parser
def get_path_until_cursor(self):
""" Get the path under the cursor. """
result = self._get_path_until_cursor()
self._start_cursor_pos = self._line_temp + 1, self._column_temp
return result
if self._path_until_cursor is None: # small caching
self._path_until_cursor, self._start_cursor_pos = \
self._get_path_until_cursor(self.position)
return self._path_until_cursor
def _get_path_until_cursor(self, start_pos=None):
def fetch_line():
line = self.get_line(self._line_temp)
if self._is_first:
self._is_first = False
self._line_length = self._column_temp
line = line[:self._column_temp]
line = self._first_line
else:
line = self.get_line(self._line_temp)
self._line_length = len(line)
line = line + '\n'
# add lines with a backslash at the end
while 1:
while True:
self._line_temp -= 1
last_line = self.get_line(self._line_temp)
#print self._line_temp, repr(last_line)
if last_line and last_line[-1] == '\\':
line = last_line[:-1] + ' ' + line
self._line_length = len(last_line)
else:
break
return line[::-1]
self._is_first = True
if start_pos is None:
self._line_temp = self.position[0]
self._column_temp = self.position[1]
else:
self._line_temp, self._column_temp = start_pos
self._line_temp, self._column_temp = start_cursor = start_pos
self._first_line = self.get_line(self._line_temp)[:self._column_temp]
open_brackets = ['(', '[', '{']
close_brackets = [')', ']', '}']
@@ -118,9 +159,13 @@ class ModuleWithCursor(Module):
string = ''
level = 0
force_point = False
last_type = None
try:
for token_type, tok, start, end, line in gen:
#print 'tok', token_type, tok, force_point
# print 'tok', token_type, tok, force_point
if last_type == token_type == tokenize.NAME:
string += ' '
if level > 0:
if tok in close_brackets:
level += 1
@@ -142,51 +187,79 @@ class ModuleWithCursor(Module):
elif token_type == tokenize.NUMBER:
pass
else:
self._column_temp = self._line_length - end[1]
break
x = start_pos[0] - end[0] + 1
l = self.get_line(x)
l = self._first_line if x == start_pos[0] else l
start_cursor = x, len(l) - end[1]
self._column_temp = self._line_length - end[1]
string += tok
last_type = token_type
except tokenize.TokenError:
debug.warning("Tokenize couldn't finish", sys.exc_info)
return string[::-1]
# string can still contain spaces at the end
return string[::-1].strip(), start_cursor
def get_path_under_cursor(self):
"""
Return the path under the cursor. If there is a rest of the path left,
it will be added to the stuff before it.
"""
return self.get_path_until_cursor() + self.get_path_after_cursor()
def get_path_after_cursor(self):
line = self.get_line(self.position[0])
after = re.search("[\w\d]*", line[self.position[1]:]).group(0)
return self.get_path_until_cursor() + after
return re.search("[\w\d]*", line[self.position[1]:]).group(0)
def get_operator_under_cursor(self):
line = self.get_line(self.position[0])
after = re.match("[^\w\s]+", line[self.position[1]:])
before = re.match("[^\w\s]+", line[:self.position[1]][::-1])
return (before.group(0) if before is not None else '') \
+ (after.group(0) if after is not None else '')
+ (after.group(0) if after is not None else '')
def get_context(self):
def get_context(self, yield_positions=False):
pos = self._start_cursor_pos
while pos > (1, 0):
while True:
# remove non important white space
line = self.get_line(pos[0])
while pos[1] > 0 and line[pos[1] - 1].isspace():
pos = pos[0], pos[1] - 1
while True:
if pos[1] == 0:
line = self.get_line(pos[0] - 1)
if line and line[-1] == '\\':
pos = pos[0] - 1, len(line) - 1
continue
else:
break
if line[pos[1] - 1].isspace():
pos = pos[0], pos[1] - 1
else:
break
try:
yield self._get_path_until_cursor(start_pos=pos)
result, pos = self._get_path_until_cursor(start_pos=pos)
if yield_positions:
yield pos
else:
yield result
except StopIteration:
yield ''
pos = self._line_temp, self._column_temp
while True:
yield ''
if yield_positions:
yield None
else:
yield ''
def get_line(self, line_nr):
if not self._line_cache:
self._line_cache = self.source.split('\n')
self._line_cache = self.source.splitlines()
if self.source:
if self.source[-1] == '\n':
self._line_cache.append('')
else: # ''.splitlines() == []
self._line_cache = ['']
if line_nr == 0:
# This is a fix for the zeroth line. We need a newline there, for
@@ -199,23 +272,26 @@ class ModuleWithCursor(Module):
except IndexError:
raise StopIteration()
def get_part_parser(self):
""" Returns a parser that contains only part of the source code. This
exists only because of performance reasons.
"""
if self._part_parser:
return self._part_parser
# TODO check for docstrings
length = settings.part_line_length
offset = max(self.position[0] - length, 0)
s = '\n'.join(self.source.split('\n')[offset:offset + length])
self._part_parser = parsing.PyFuzzyParser(s, self.path, self.position,
line_offset=offset)
return self._part_parser
def get_position_line(self):
return self.get_line(self.position[0])[:self.position[1]]
@evaluate.memoize_default([])
def get_sys_path():
def check_virtual_env(sys_path):
""" Add virtualenv's site-packages to the `sys.path`."""
venv = os.getenv('VIRTUAL_ENV')
if not venv:
return
venv = os.path.abspath(venv)
p = os.path.join(
venv, 'lib', 'python%d.%d' % sys.version_info[:2], 'site-packages')
sys_path.insert(0, p)
check_virtual_env(sys.path)
return [p for p in sys.path if p != ""]
@cache.memoize_default([])
def sys_path_with_modifications(module):
def execute_code(code):
c = "import os; from os.path import *; result=%s"
@@ -238,16 +314,18 @@ def sys_path_with_modifications(module):
try:
possible_stmts = module.used_names['path']
except KeyError:
return builtin.get_sys_path()
return get_sys_path()
sys_path = list(builtin.get_sys_path()) # copy
sys_path = list(get_sys_path()) # copy
for p in possible_stmts:
try:
call = p.get_assignment_calls().get_only_subelement()
except AttributeError:
if not isinstance(p, pr.Statement):
continue
commands = p.get_commands()
if len(commands) != 1: # sys.path command is just one thing.
continue
call = commands[0]
n = call.name
if not isinstance(n, parsing.Name) or len(n.names) != 3:
if not isinstance(n, pr.Name) or len(n.names) != 3:
continue
if n.names[:2] != ('sys', 'path'):
continue
@@ -260,7 +338,7 @@ def sys_path_with_modifications(module):
continue
if array_cmd == 'insert':
exe_type, exe.type = exe.type, parsing.Array.NOARRAY
exe_type, exe.type = exe.type, pr.Array.NOARRAY
exe_pop = exe.values.pop(0)
res = execute_code(exe.get_code())
if res is not None:
@@ -276,13 +354,13 @@ def sys_path_with_modifications(module):
return sys_path
if module.path is None:
return [] # support for modules without a path is intentionally bad.
# Support for modules without a path is bad, therefore return the
# normal path.
return list(get_sys_path())
curdir = os.path.abspath(os.curdir)
try:
with common.ignored(OSError):
os.chdir(os.path.dirname(module.path))
except OSError:
pass
result = check_module(module)
result += detect_django_path(module.path)
@@ -303,10 +381,37 @@ def detect_django_path(module_path):
else:
module_path = new
try:
with common.ignored(IOError):
with open(module_path + os.path.sep + 'manage.py'):
debug.dbg('Found django path: %s' % module_path)
result.append(module_path)
except IOError:
pass
return result
def source_to_unicode(source, encoding=None):
def detect_encoding():
""" For the implementation of encoding definitions in Python, look at:
http://www.python.org/dev/peps/pep-0263/
http://docs.python.org/2/reference/lexical_analysis.html#encoding-\
declarations
"""
byte_mark = literal_eval(r"b'\xef\xbb\xbf'")
if source.startswith(byte_mark):
# UTF-8 byte-order mark
return 'utf-8'
first_two_lines = re.match(r'(?:[^\n]*\n){0,2}', str(source)).group(0)
possible_encoding = re.search(r"coding[=:]\s*([-\w.]+)",
first_two_lines)
if possible_encoding:
return possible_encoding.group(1)
else:
# the default if nothing else has been set -> PEP 263
return encoding if encoding is not None else 'iso-8859-1'
if isinstance(source, unicode):
# only cast str/bytes
return source
# cast to unicode by default
return unicode(source, detect_encoding(), 'replace')

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

156
jedi/recursion.py Normal file
View File

@@ -0,0 +1,156 @@
"""
Recursions are the recipe of |jedi| to conquer Python code. However, someone
must stop recursions going mad. Some settings are here to make |jedi| stop at
the right time. You can read more about them :ref:`here <settings-recursion>`.
Next to :mod:`cache` this module also makes |jedi| not thread-safe. Why?
``ExecutionRecursionDecorator`` uses class variables to count the function
calls.
"""
from jedi import parsing_representation as pr
from jedi import debug
from jedi import settings
import evaluate_representation as er
import builtin
class RecursionDecorator(object):
"""
A decorator to detect recursions in statements. In a recursion a statement
at the same place, in the same module may not be executed two times.
"""
def __init__(self, func):
self.func = func
self.reset()
def __call__(self, stmt, *args, **kwargs):
# print stmt, len(self.node_statements())
if self.push_stmt(stmt):
return []
else:
result = self.func(stmt, *args, **kwargs)
self.pop_stmt()
return result
def push_stmt(self, stmt):
self.current = RecursionNode(stmt, self.current)
check = self._check_recursion()
if check: # TODO remove False!!!!
debug.warning('catched stmt recursion: %s against %s @%s'
% (stmt, check.stmt, stmt.start_pos))
self.pop_stmt()
return True
return False
def pop_stmt(self):
if self.current is not None:
# I don't know how current can be None, but sometimes it happens
# with Python3.
self.current = self.current.parent
def _check_recursion(self):
test = self.current
while True:
test = test.parent
if self.current == test:
return test
if not test:
return False
def reset(self):
self.top = None
self.current = None
def node_statements(self):
result = []
n = self.current
while n:
result.insert(0, n.stmt)
n = n.parent
return result
class RecursionNode(object):
""" A node of the RecursionDecorator. """
def __init__(self, stmt, parent):
self.script = stmt.get_parent_until()
self.position = stmt.start_pos
self.parent = parent
self.stmt = stmt
# Don't check param instances, they are not causing recursions
# The same's true for the builtins, because the builtins are really
# simple.
self.is_ignored = isinstance(stmt, pr.Param) \
or (self.script == builtin.Builtin.scope)
def __eq__(self, other):
if not other:
return None
is_list_comp = lambda x: isinstance(x, pr.ForFlow) and x.is_list_comp
return self.script == other.script \
and self.position == other.position \
and not is_list_comp(self.stmt.parent) \
and not is_list_comp(other.parent) \
and not self.is_ignored and not other.is_ignored
class ExecutionRecursionDecorator(object):
"""
Catches recursions of executions.
It is designed like a Singelton. Only one instance should exist.
"""
def __init__(self, func):
self.func = func
self.reset()
def __call__(self, execution, evaluate_generator=False):
debug.dbg('Execution recursions: %s' % execution, self.recursion_level,
self.execution_count, len(self.execution_funcs))
if self.check_recursion(execution, evaluate_generator):
result = []
else:
result = self.func(execution, evaluate_generator)
self.cleanup()
return result
@classmethod
def cleanup(cls):
cls.parent_execution_funcs.pop()
cls.recursion_level -= 1
@classmethod
def check_recursion(cls, execution, evaluate_generator):
in_par_execution_funcs = execution.base in cls.parent_execution_funcs
in_execution_funcs = execution.base in cls.execution_funcs
cls.recursion_level += 1
cls.execution_count += 1
cls.execution_funcs.add(execution.base)
cls.parent_execution_funcs.append(execution.base)
if cls.execution_count > settings.max_executions:
return True
if isinstance(execution.base, (er.Generator, er.Array)):
return False
module = execution.get_parent_until()
if evaluate_generator or module == builtin.Builtin.scope:
return False
if in_par_execution_funcs:
if cls.recursion_level > settings.max_function_recursion_level:
return True
if in_execution_funcs and \
len(cls.execution_funcs) > settings.max_until_execution_unique:
return True
if cls.execution_count > settings.max_executions_without_builtins:
return True
return False
@classmethod
def reset(cls):
cls.recursion_level = 0
cls.parent_execution_funcs = []
cls.execution_funcs = set()
cls.execution_count = 0

205
jedi/refactoring.py Normal file
View File

@@ -0,0 +1,205 @@
"""
Introduce some basic refactoring functions to |jedi|. This module is still in a
very early development stage and needs much testing and improvement.
.. warning:: I won't do too much here, but if anyone wants to step in, please
do. Refactoring is none of my priorities
It uses the |jedi| `API <plugin-api.html>`_ and supports currently the
following functions (sometimes bug-prone):
- rename
- extract variable
- inline variable
"""
from __future__ import with_statement
import difflib
from jedi import common
from jedi import modules
from jedi import helpers
from jedi import parsing_representation as pr
class Refactoring(object):
def __init__(self, change_dct):
"""
:param change_dct: dict(old_path=(new_path, old_lines, new_lines))
"""
self.change_dct = change_dct
def old_files(self):
dct = {}
for old_path, (new_path, old_l, new_l) in self.change_dct.items():
dct[new_path] = '\n'.join(new_l)
return dct
def new_files(self):
dct = {}
for old_path, (new_path, old_l, new_l) in self.change_dct.items():
dct[new_path] = '\n'.join(new_l)
return dct
def diff(self):
texts = []
for old_path, (new_path, old_l, new_l) in self.change_dct.items():
if old_path:
udiff = difflib.unified_diff(old_l, new_l)
else:
udiff = difflib.unified_diff(old_l, new_l, old_path, new_path)
texts.append('\n'.join(udiff))
return '\n'.join(texts)
def rename(script, new_name):
""" The `args` / `kwargs` params are the same as in `api.Script`.
:param operation: The refactoring operation to execute.
:type operation: str
:type source: str
:return: list of changed lines/changed files
"""
return Refactoring(_rename(script.usages(), new_name))
def _rename(names, replace_str):
""" For both rename and inline. """
order = sorted(names, key=lambda x: (x.module_path, x.line, x.column),
reverse=True)
def process(path, old_lines, new_lines):
if new_lines is not None: # goto next file, save last
dct[path] = path, old_lines, new_lines
dct = {}
current_path = object()
new_lines = old_lines = None
for name in order:
if name.in_builtin_module():
continue
if current_path != name.module_path:
current_path = name.module_path
process(current_path, old_lines, new_lines)
if current_path is not None:
# None means take the source that is a normal param.
with open(current_path) as f:
source = f.read()
new_lines = modules.source_to_unicode(source).splitlines()
old_lines = new_lines[:]
nr, indent = name.line, name.column
line = new_lines[nr - 1]
new_lines[nr - 1] = line[:indent] + replace_str + \
line[indent + len(name.text):]
process(current_path, old_lines, new_lines)
return dct
def extract(script, new_name):
""" The `args` / `kwargs` params are the same as in `api.Script`.
:param operation: The refactoring operation to execute.
:type operation: str
:type source: str
:return: list of changed lines/changed files
"""
new_lines = modules.source_to_unicode(script.source).splitlines()
old_lines = new_lines[:]
user_stmt = script._parser.user_stmt
# TODO care for multiline extracts
dct = {}
if user_stmt:
pos = script._pos
line_index = pos[0] - 1
arr, index = helpers.array_for_pos(user_stmt, pos)
if arr is not None:
start_pos = arr[index].start_pos
end_pos = arr[index].end_pos
# take full line if the start line is different from end line
e = end_pos[1] if end_pos[0] == start_pos[0] else None
start_line = new_lines[start_pos[0] - 1]
text = start_line[start_pos[1]:e]
for l in range(start_pos[0], end_pos[0] - 1):
text += '\n' + l
if e is None:
end_line = new_lines[end_pos[0] - 1]
text += '\n' + end_line[:end_pos[1]]
# remove code from new lines
t = text.lstrip()
del_start = start_pos[1] + len(text) - len(t)
text = t.rstrip()
del_end = len(t) - len(text)
if e is None:
new_lines[end_pos[0] - 1] = end_line[end_pos[1] - del_end:]
e = len(start_line)
else:
e = e - del_end
start_line = start_line[:del_start] + new_name + start_line[e:]
new_lines[start_pos[0] - 1] = start_line
new_lines[start_pos[0]:end_pos[0] - 1] = []
# add parentheses in multiline case
open_brackets = ['(', '[', '{']
close_brackets = [')', ']', '}']
if '\n' in text and not (text[0] in open_brackets and text[-1] ==
close_brackets[open_brackets.index(text[0])]):
text = '(%s)' % text
# add new line before statement
indent = user_stmt.start_pos[1]
new = "%s%s = %s" % (' ' * indent, new_name, text)
new_lines.insert(line_index, new)
dct[script.path] = script.path, old_lines, new_lines
return Refactoring(dct)
def inline(script):
"""
:type script: api.Script
"""
new_lines = modules.source_to_unicode(script.source).splitlines()
dct = {}
definitions = script.goto_assignments()
with common.ignored(AssertionError):
assert len(definitions) == 1
stmt = definitions[0]._definition
usages = script.usages()
inlines = [r for r in usages
if not stmt.start_pos <= (r.line, r.column) <= stmt.end_pos]
inlines = sorted(inlines, key=lambda x: (x.module_path, x.line, x.column),
reverse=True)
commands = stmt.get_commands()
# don't allow multiline refactorings for now.
assert stmt.start_pos[0] == stmt.end_pos[0]
index = stmt.start_pos[0] - 1
line = new_lines[index]
replace_str = line[commands[0].start_pos[1]:stmt.end_pos[1] + 1]
replace_str = replace_str.strip()
# tuples need parentheses
if commands and isinstance(commands[0], pr.Array):
arr = commands[0]
if replace_str[0] not in ['(', '[', '{'] and len(arr) > 1:
replace_str = '(%s)' % replace_str
# if it's the only assignment, remove the statement
if len(stmt.set_vars) == 1:
line = line[:stmt.start_pos[1]] + line[stmt.end_pos[1]:]
dct = _rename(inlines, replace_str)
# remove the empty line
new_lines = dct[script.path][2]
if line.strip():
new_lines[index] = line
else:
new_lines.pop(index)
return Refactoring(dct)

23
jedi/replstartup.py Normal file
View File

@@ -0,0 +1,23 @@
"""
To use Jedi completion in Python interpreter, add the following in your shell
setup (e.g., ``.bashrc``)::
export PYTHONSTARTUP="$(python -m jedi repl)"
Then you will be able to use Jedi completer in your Python interpreter::
$ python
Python 2.7.2+ (default, Jul 20 2012, 22:15:08)
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.path.join().split().in<TAB> # doctest: +SKIP
os.path.join().split().index os.path.join().split().insert
"""
import jedi.utils
jedi.utils.setup_readline()
del jedi
# Note: try not to do many things here, as it will contaminate global
# namespace of the interpreter.

View File

@@ -1,87 +1,236 @@
"""
This module contains variables with global |jedi| setting. To change the
behavior of |jedi|, change the variables defined in
:mod:`jedi.settings`.
Plugins should expose an interface so that the user can adjust the
configuration.
Example usage::
from jedi import settings
settings.case_insensitive_completion = True
Completion output
~~~~~~~~~~~~~~~~~
.. autodata:: case_insensitive_completion
.. autodata:: add_dot_after_module
.. autodata:: add_bracket_after_function
.. autodata:: no_completion_duplicates
Filesystem cache
~~~~~~~~~~~~~~~~
.. autodata:: cache_directory
.. autodata:: use_filesystem_cache
Parser
~~~~~~
.. autodata:: fast_parser
Dynamic stuff
~~~~~~~~~~~~~
.. autodata:: dynamic_arrays_instances
.. autodata:: dynamic_array_additions
.. autodata:: dynamic_params
.. autodata:: dynamic_params_for_other_modules
.. autodata:: additional_dynamic_modules
.. _settings-recursion:
Recursions
~~~~~~~~~~
Recursion settings are important if you don't want extremly
recursive python code to go absolutely crazy. First of there is a
global limit :data:`max_executions`. This limit is important, to set
a maximum amount of time, the completion may use.
The default values are based on experiments while completing the |jedi| library
itself (inception!). But I don't think there's any other Python library that
uses recursion in a similarly extreme way. These settings make the completion
definitely worse in some cases. But a completion should also be fast.
.. autodata:: max_until_execution_unique
.. autodata:: max_function_recursion_level
.. autodata:: max_executions_without_builtins
.. autodata:: max_executions
.. autodata:: scale_function_definition
Caching
~~~~~~~
.. autodata:: star_import_cache_validity
.. autodata:: function_definition_validity
"""
import os
import platform
# ----------------
# completion output settings
# ----------------
# The completion is by default case insensitive.
case_insensitive_completion = True
"""
The completion is by default case insensitive.
"""
# Adds a dot after a module, because a module that is not accessed this way is
# definitely not the normal case. However, in VIM this doesn't work, that's why
# it isn't used at the moment.
add_dot_after_module = False
"""
Adds a dot after a module, because a module that is not accessed this way is
definitely not the normal case. However, in VIM this doesn't work, that's why
it isn't used at the moment.
"""
# Adds an opening bracket after a function, because that's normal behaviour.
# Removed it again, because in VIM that is not very practical.
add_bracket_after_function = False
"""
Adds an opening bracket after a function, because that's normal behaviour.
Removed it again, because in VIM that is not very practical.
"""
no_completion_duplicates = True
"""
If set, completions with the same name don't appear in the output anymore,
but are in the `same_name_completions` attribute.
"""
# ----------------
# Filesystem cache
# ----------------
use_filesystem_cache = True
"""
Use filesystem cache to save once parsed files with pickle.
"""
if platform.system().lower() == 'windows':
_cache_directory = os.path.join(os.getenv('APPDATA') or '~', 'Jedi',
'Jedi')
elif platform.system().lower() == 'darwin':
_cache_directory = os.path.join('~', 'Library', 'Caches', 'Jedi')
else:
_cache_directory = os.path.join(os.getenv('XDG_CACHE_HOME') or '~/.cache',
'jedi')
cache_directory = os.path.expanduser(_cache_directory)
"""
The path where all the caches can be found.
On Linux, this defaults to ``~/.cache/jedi/``, on OS X to
``~/Library/Caches/Jedi/`` and on Windows to ``%APPDATA%\\Jedi\\Jedi\\``.
On Linux, if environment variable ``$XDG_CACHE_HOME`` is set,
``$XDG_CACHE_HOME/jedi`` is used instead of the default one.
"""
# ----------------
# parser
# ----------------
fast_parser = True
"""
Use the fast parser. This means that reparsing is only being done if
something has been changed e.g. to a function. If this happens, only the
function is being reparsed.
"""
# ----------------
# dynamic stuff
# ----------------
# check for `append`, etc. on array instances like list()
dynamic_arrays_instances = True
# check for `append`, etc. on arrays: [], {}, ()
"""
Check for `append`, etc. on array instances like list()
"""
dynamic_array_additions = True
"""
check for `append`, etc. on arrays: [], {}, ()
"""
# A dynamic param completion, finds the callees of the function, which define
# the params of a function.
dynamic_params = True
# Do the same for other modules.
dynamic_params_for_other_modules = True
"""
A dynamic param completion, finds the callees of the function, which define
the params of a function.
"""
dynamic_params_for_other_modules = True
"""
Do the same for other modules.
"""
# Additional modules in which Jedi checks if statements are to be found. This
# is practical for IDE's, that want to administrate their modules themselves.
additional_dynamic_modules = []
"""
Additional modules in which |jedi| checks if statements are to be found. This
is practical for IDEs, that want to administrate their modules themselves.
"""
dynamic_flow_information = True
"""
Check for `isinstance` and other information to infer a type.
"""
# ----------------
# recursions
# ----------------
# Recursion settings are important if you don't want extremly recursive python
# code to go absolutely crazy. First of there is a global limit
# `max_executions`. This limit is important, to set a maximum amount of time,
# the completion may use.
#
# The `max_until_execution_unique` limit is probably the most important one,
# because if that limit is passed, functions can only be one time executed. So
# new functions will be executed, complex recursions with the same functions
# again and again, are ignored.
#
# `max_function_recursion_level` is more about whether the recursions are
# stopped in deepth or in width. The ratio beetween this and
# `max_until_execution_unique` is important here. It stops a recursion (after
# the number of function calls in the recursion), if it was already used
# earlier.
#
# The values are based on my experimental tries, used on the jedi library. But
# I don't think there's any other Python library, that uses recursion in a
# similar (extreme) way. This makes the completion definitely worse in some
# cases. But a completion should also be fast.
max_until_execution_unique = 50
"""
This limit is probably the most important one, because if this limit is
exceeded, functions can only be one time executed. So new functions will be
executed, complex recursions with the same functions again and again, are
ignored.
"""
max_function_recursion_level = 5
max_until_execution_unique = 50
"""
`max_function_recursion_level` is more about whether the recursions are
stopped in deepth or in width. The ratio beetween this and
`max_until_execution_unique` is important here. It stops a recursion (after
the number of function calls in the recursion), if it was already used
earlier.
"""
max_executions_without_builtins = 200
"""
.. todo:: Document this.
"""
max_executions = 250
"""
A maximum amount of time, the completion may use.
"""
# Because get_in_function_call is normally used on every single key hit, it has
# to be faster than a normal completion. This is the factor that is used to
# scale `max_executions` and `max_until_execution_unique`:
scale_get_in_function_call = 0.1
scale_function_definition = 0.1
"""
Because function_definition is normally used on every single key hit, it has
to be faster than a normal completion. This is the factor that is used to
scale `max_executions` and `max_until_execution_unique`:
"""
# ----------------
# various
# caching validity (time)
# ----------------
# Size of the current code part, which is used to speed up parsing.
part_line_length = 20
# ----------------
# star import caching
# ----------------
# In huge packages like numpy, checking all star imports on every completion
# might be slow, therefore we do a star import caching, that lasts a certain
# time span (in seconds).
star_import_cache_validity = 60.0
"""
In huge packages like numpy, checking all star imports on every completion
might be slow, therefore we do a star import caching, that lasts a certain
time span (in seconds).
"""
function_definition_validity = 3.0
"""
Finding function calls might be slow (0.1-0.5s). This is not acceptible for
normal writing. Therefore cache it for a short time.
"""

286
jedi/tokenizer.py Normal file
View File

@@ -0,0 +1,286 @@
"""
This tokenizer has been copied from the ``tokenize.py`` standard library
tokenizer. The reason was simple: The standanrd library tokenizer fails
if the indentation is not right. The fast parser of jedi however requires
"wrong" indentation.
Basically this is a stripped down version of the standard library module, so
you can read the documentation there.
"""
import string
import re
from token import *
from codecs import lookup, BOM_UTF8
import collections
cookie_re = re.compile("coding[:=]\s*([-\w.]+)")
namechars = string.ascii_letters + '_'
COMMENT = N_TOKENS
tok_name[COMMENT] = 'COMMENT'
NL = N_TOKENS + 1
tok_name[NL] = 'NL'
ENCODING = N_TOKENS + 2
tok_name[ENCODING] = 'ENCODING'
N_TOKENS += 3
class TokenInfo(collections.namedtuple('TokenInfo', 'type string start end line')):
def __repr__(self):
annotated_type = '%d (%s)' % (self.type, tok_name[self.type])
return ('TokenInfo(type=%s, string=%r, start=%r, end=%r, line=%r)' %
self._replace(type=annotated_type))
def group(*choices):
return '(' + '|'.join(choices) + ')'
def any(*choices):
return group(*choices) + '*'
def maybe(*choices):
return group(*choices) + '?'
# Note: we use unicode matching for names ("\w") but ascii matching for
# number literals.
Whitespace = r'[ \f\t]*'
Comment = r'#[^\r\n]*'
Ignore = Whitespace + any(r'\\\r?\n' + Whitespace) + maybe(Comment)
Name = r'\w+'
Hexnumber = r'0[xX][0-9a-fA-F]+'
Binnumber = r'0[bB][01]+'
Octnumber = r'0[oO][0-7]+'
Decnumber = r'(?:0+|[1-9][0-9]*)'
Intnumber = group(Hexnumber, Binnumber, Octnumber, Decnumber)
Exponent = r'[eE][-+]?[0-9]+'
Pointfloat = group(r'[0-9]+\.[0-9]*', r'\.[0-9]+') + maybe(Exponent)
Expfloat = r'[0-9]+' + Exponent
Floatnumber = group(Pointfloat, Expfloat)
Imagnumber = group(r'[0-9]+[jJ]', Floatnumber + r'[jJ]')
Number = group(Imagnumber, Floatnumber, Intnumber)
# Tail end of ' string.
Single = r"[^'\\]*(?:\\.[^'\\]*)*'"
# Tail end of " string.
Double = r'[^"\\]*(?:\\.[^"\\]*)*"'
# Tail end of ''' string.
Single3 = r"[^'\\]*(?:(?:\\.|'(?!''))[^'\\]*)*'''"
# Tail end of """ string.
Double3 = r'[^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*"""'
Triple = group("[bB]?[rR]?'''", '[bB]?[rR]?"""')
# Single-line ' or " string.
String = group(r"[bB]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*'",
r'[bB]?[rR]?"[^\n"\\]*(?:\\.[^\n"\\]*)*"')
# Because of leftmost-then-longest match semantics, be sure to put the
# longest operators first (e.g., if = came before ==, == would get
# recognized as two instances of =).
Operator = group(r"\*\*=?", r">>=?", r"<<=?", r"!=",
r"//=?", r"->",
r"[+\-*/%&|^=<>]=?",
r"~")
Bracket = '[][(){}]'
Special = group(r'\r?\n', r'\.\.\.', r'[:;.,@]')
Funny = group(Operator, Bracket, Special)
PlainToken = group(Number, Funny, String, Name)
Token = Ignore + PlainToken
# First (or only) line of ' or " string.
ContStr = group(r"[bB]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*" +
group("'", r'\\\r?\n'),
r'[bB]?[rR]?"[^\n"\\]*(?:\\.[^\n"\\]*)*' +
group('"', r'\\\r?\n'))
PseudoExtras = group(r'\\\r?\n', Comment, Triple)
PseudoToken = Whitespace + group(PseudoExtras, Number, Funny, ContStr, Name)
def _compile(expr):
return re.compile(expr, re.UNICODE)
tokenprog, pseudoprog, single3prog, double3prog = map(
_compile, (Token, PseudoToken, Single3, Double3))
endprogs = {"'": _compile(Single), '"': _compile(Double),
"'''": single3prog, '"""': double3prog,
"r'''": single3prog, 'r"""': double3prog,
"b'''": single3prog, 'b"""': double3prog,
"br'''": single3prog, 'br"""': double3prog,
"R'''": single3prog, 'R"""': double3prog,
"B'''": single3prog, 'B"""': double3prog,
"bR'''": single3prog, 'bR"""': double3prog,
"Br'''": single3prog, 'Br"""': double3prog,
"BR'''": single3prog, 'BR"""': double3prog,
'r': None, 'R': None, 'b': None, 'B': None}
triple_quoted = {}
for t in ("'''", '"""',
"r'''", 'r"""', "R'''", 'R"""',
"b'''", 'b"""', "B'''", 'B"""',
"br'''", 'br"""', "Br'''", 'Br"""',
"bR'''", 'bR"""', "BR'''", 'BR"""'):
triple_quoted[t] = t
single_quoted = {}
for t in ("'", '"',
"r'", 'r"', "R'", 'R"',
"b'", 'b"', "B'", 'B"',
"br'", 'br"', "Br'", 'Br"',
"bR'", 'bR"', "BR'", 'BR"'):
single_quoted[t] = t
del _compile
tabsize = 8
class TokenError(Exception):
pass
def generate_tokens(readline):
lnum = parenlev = continued = 0
numchars = '0123456789'
contstr, needcont = '', 0
contline = None
indents = [0]
while True: # loop over lines in stream
try:
line = readline()
except StopIteration:
line = b''
lnum += 1
pos, max = 0, len(line)
if contstr: # continued string
if not line:
# multiline string has not been finished
break
endmatch = endprog.match(line)
if endmatch:
pos = end = endmatch.end(0)
yield TokenInfo(STRING, contstr + line[:end],
strstart, (lnum, end), contline + line)
contstr, needcont = '', 0
contline = None
elif needcont and line[-2:] != '\\\n' and line[-3:] != '\\\r\n':
yield TokenInfo(ERRORTOKEN, contstr + line,
strstart, (lnum, len(line)), contline)
contstr = ''
contline = None
continue
else:
contstr = contstr + line
contline = contline + line
continue
elif parenlev == 0 and not continued: # new statement
if not line:
break
column = 0
while pos < max: # measure leading whitespace
if line[pos] == ' ':
column += 1
elif line[pos] == '\t':
column = (column // tabsize + 1) * tabsize
elif line[pos] == '\f':
column = 0
else:
break
pos += 1
if pos == max:
break
if line[pos] in '#\r\n': # skip comments or blank lines
if line[pos] == '#':
comment_token = line[pos:].rstrip('\r\n')
nl_pos = pos + len(comment_token)
yield TokenInfo(COMMENT, comment_token,
(lnum, pos), (lnum, pos + len(comment_token)), line)
yield TokenInfo(NL, line[nl_pos:],
(lnum, nl_pos), (lnum, len(line)), line)
else:
yield TokenInfo(
(NL, COMMENT)[line[pos] == '#'], line[pos:],
(lnum, pos), (lnum, len(line)), line)
continue
if column > indents[-1]: # count indents or dedents
indents.append(column)
yield TokenInfo(INDENT, line[:pos], (lnum, 0), (lnum, pos), line)
while column < indents[-1]:
indents = indents[:-1]
yield TokenInfo(DEDENT, '', (lnum, pos), (lnum, pos), line)
else: # continued statement
if not line:
# basically a statement has not been finished here.
break
continued = 0
while pos < max:
pseudomatch = pseudoprog.match(line, pos)
if pseudomatch: # scan for tokens
start, end = pseudomatch.span(1)
spos, epos, pos = (lnum, start), (lnum, end), end
token, initial = line[start:end], line[start]
if (initial in numchars or # ordinary number
(initial == '.' and token != '.' and token != '...')):
yield TokenInfo(NUMBER, token, spos, epos, line)
elif initial in '\r\n':
yield TokenInfo(NL if parenlev > 0 else NEWLINE,
token, spos, epos, line)
elif initial == '#':
assert not token.endswith("\n")
yield TokenInfo(COMMENT, token, spos, epos, line)
elif token in triple_quoted:
endprog = endprogs[token]
endmatch = endprog.match(line, pos)
if endmatch: # all on one line
pos = endmatch.end(0)
token = line[start:pos]
yield TokenInfo(STRING, token, spos, (lnum, pos), line)
else:
strstart = (lnum, start) # multiple lines
contstr = line[start:]
contline = line
break
elif initial in single_quoted or \
token[:2] in single_quoted or \
token[:3] in single_quoted:
if token[-1] == '\n': # continued string
strstart = (lnum, start)
endprog = (endprogs[initial] or endprogs[token[1]] or
endprogs[token[2]])
contstr, needcont = line[start:], 1
contline = line
break
else: # ordinary string
yield TokenInfo(STRING, token, spos, epos, line)
elif initial in namechars: # ordinary name
yield TokenInfo(NAME, token, spos, epos, line)
elif initial == '\\': # continued stmt
continued = 1
else:
if initial in '([{':
parenlev += 1
elif initial in ')]}':
parenlev -= 1
yield TokenInfo(OP, token, spos, epos, line)
else:
yield TokenInfo(ERRORTOKEN, line[pos],
(lnum, pos), (lnum, pos + 1), line)
pos += 1
for indent in indents[1:]: # pop remaining indent levels
yield TokenInfo(DEDENT, '', (lnum, 0), (lnum, 0), '')
yield TokenInfo(ENDMARKER, '', (lnum, 0), (lnum, 0), '')

97
jedi/utils.py Normal file
View File

@@ -0,0 +1,97 @@
"""
Utilities for end-users.
"""
from __future__ import absolute_import
import __main__
from jedi import Interpreter
def setup_readline(namespace_module=__main__):
"""
Install Jedi completer to :mod:`readline`.
This function setups :mod:`readline` to use Jedi in Python interactive
shell. If you want to use a custom ``PYTHONSTARTUP`` file (typically
``$HOME/.pythonrc.py``), you can add this piece of code::
try:
from jedi.utils import setup_readline
setup_readline()
except ImportError:
# Fallback to the stdlib readline completer if it is installed.
# Taken from http://docs.python.org/2/library/rlcompleter.html
print("Jedi is not installed, falling back to readline")
try:
import readline
import rlcompleter
readline.parse_and_bind("tab: complete")
except ImportError:
print("Readline is not installed either. No tab completion is enabled.")
This will fallback to the readline completer if Jedi is not installed.
The readline completer will only complete names in the global namespace,
so for example,
>>> ran<TAB> # doctest: +SKIP
will complete to ``range``
with both Jedi and readline, but
>>> range(10).cou<TAB> # doctest: +SKIP
will show complete to ``range(10).count`` only with Jedi.
You'll also need to add ``export PYTHONSTARTUP=$HOME/.pythonrc.py`` to
your shell profile (usually ``.bash_profile`` or ``.profile`` if you use
bash).
"""
class JediRL():
def complete(self, text, state):
"""
This complete stuff is pretty weird, a generator would make
a lot more sense, but probably due to backwards compatibility
this is still the way how it works.
The only important part is stuff in the ``state == 0`` flow,
everything else has been copied from the ``rlcompleter`` std.
library module.
"""
if state == 0:
import os, sys
sys.path.insert(0, os.getcwd())
# Calling python doesn't have a path, so add to sys.path.
try:
interpreter = Interpreter(text, [namespace_module.__dict__])
path, dot, like = interpreter._get_completion_parts()
before = text[:len(text) - len(like)]
completions = interpreter.completions()
finally:
sys.path.pop(0)
self.matches = [before + c.name_with_symbols for c in completions]
try:
return self.matches[state]
except IndexError:
return None
try:
import readline
except ImportError:
print("Module readline not available.")
else:
readline.set_completer(JediRL().complete)
readline.parse_and_bind("tab: complete")
# jedi itself does the case matching
readline.parse_and_bind("set completion-ignore-case on")
# because it's easier to hit the tab just once
readline.parse_and_bind("set show-all-if-unmodified")
readline.parse_and_bind("set show-all-if-ambiguous on")
# don't repeat all the things written in the readline all the time
readline.parse_and_bind("set completion-prefix-display-length 2")
# No delimiters, Jedi handles that.
readline.set_completer_delims('')

10
pytest.ini Normal file
View File

@@ -0,0 +1,10 @@
[pytest]
addopts = --doctest-modules
# Ignore broken files in blackbox test directories
norecursedirs = .* docs completion refactor absolute_import namespace_package
# Activate `clean_jedi_cache` fixture for all tests. This should be
# fine as long as we are using `clean_jedi_cache` as a session scoped
# fixture.
usefixtures = clean_jedi_cache

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

27
setup.py Normal file → Executable file
View File

@@ -1,33 +1,46 @@
#!/usr/bin/env python
from setuptools import setup
from __future__ import with_statement
try:
from setuptools import setup
except ImportError:
# Distribute is not actually required to install
from distutils.core import setup
__AUTHOR__ = 'David Halter'
__AUTHOR_EMAIL__ = 'davidhalter88@gmail.com'
readme = open('README.rst').read()
readme = open('README.rst').read() + '\n\n' + open('CHANGELOG.rst').read()
import jedi
VERSION = '.'.join(str(x) for x in jedi.__version__)
setup(name='jedi',
version='0.5b3',
version=VERSION,
description='An autocompletion tool for Python that can be used for text editors.',
author=__AUTHOR__,
author_email=__AUTHOR_EMAIL__,
maintainer=__AUTHOR__,
maintainer_email=__AUTHOR_EMAIL__,
url='https://github.com/davidhalter/jedi',
license='LGPLv3',
license='MIT',
keywords='python completion refactoring vim',
long_description=readme,
packages=['jedi', 'jedi.mixin'],
packages=['jedi'],
package_data={'jedi': ['mixin/*.pym']},
platforms=['any'],
classifiers=[
'Development Status :: 4 - Beta',
'Environment :: Plugins',
'Intended Audience :: Developers',
'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Text Editors :: Integrated Development Environments (IDE)',
'Topic :: Utilities',

221
sith.py Executable file
View File

@@ -0,0 +1,221 @@
#!/usr/bin/env python
"""
Sith attacks (and helps debugging) Jedi.
Randomly search Python files and run Jedi on it. Exception and used
arguments are recorded to ``./record.json`` (specified by --record)::
./sith.py random /path/to/sourcecode
Redo recorded exception::
./sith.py redo
Show recorded exception::
./sith.py show
Run a specific operation
./sith.py run <operation> </path/to/source/file.py> <line> <col>
Where operation is one of completions, goto_assignments, goto_definitions,
usages, or call_signatures.
Note: Line numbers start at 1; columns start at 0 (this is consistent with
many text editors, including Emacs).
Usage:
sith.py [--pdb|--ipdb|--pudb] [-d] [-n=<nr>] [-f] [--record=<file>] random [<path>]
sith.py [--pdb|--ipdb|--pudb] [-d] [-f] [--record=<file>] redo
sith.py [--pdb|--ipdb|--pudb] [-d] [-f] run <operation> <path> <line> <column>
sith.py show [--record=<file>]
sith.py -h | --help
Options:
-h --help Show this screen.
--record=<file> Exceptions are recorded in here [default: record.json].
-f, --fs-cache By default, file system cache is off for reproducibility.
-n, --maxtries=<nr> Maximum of random tries [default: 100]
-d, --debug Jedi print debugging when an error is raised.
--pdb Launch pdb when error is raised.
--ipdb Launch ipdb when error is raised.
--pudb Launch pudb when error is raised.
"""
from __future__ import print_function, division, unicode_literals
from docopt import docopt
import json
import os
import random
import sys
import traceback
import jedi
class SourceFinder(object):
_files = None
@staticmethod
def fetch(file_path):
if not os.path.isdir(file_path):
yield file_path
return
for root, dirnames, filenames in os.walk(file_path):
for name in filenames:
if name.endswith('.py'):
yield os.path.join(root, name)
@classmethod
def files(cls, file_path):
if cls._files is None:
cls._files = list(cls.fetch(file_path))
return cls._files
class TestCase(object):
def __init__(self, operation, path, line, column, traceback=None):
if operation not in self.operations:
raise ValueError("%s is not a valid operation" % operation)
# Set other attributes
self.operation = operation
self.path = path
self.line = line
self.column = column
self.traceback = traceback
@classmethod
def from_cache(cls, record):
with open(record) as f:
args = json.load(f)
return cls(*args)
operations = [
'completions', 'goto_assignments', 'goto_definitions', 'usages',
'call_signatures']
@classmethod
def generate(cls, file_path):
operation = random.choice(cls.operations)
path = random.choice(SourceFinder.files(file_path))
with open(path) as f:
source = f.read()
lines = source.splitlines()
if not lines:
lines = ['']
line = random.randint(1, len(lines))
column = random.randint(0, len(lines[line - 1]))
return cls(operation, path, line, column)
def run(self, debugger, record=None, print_result=False):
try:
with open(self.path) as f:
self.script = jedi.Script(f.read(), self.line, self.column, self.path)
self.completions = getattr(self.script, self.operation)()
if print_result:
self.show_location(self.line, self.column)
self.show_operation()
except jedi.NotFoundError:
pass
except Exception:
self.traceback = traceback.format_exc()
if record is not None:
call_args = (self.operation, self.path, self.line, self.column, self.traceback)
with open(record, 'w') as f:
json.dump(call_args, f)
self.show_errors()
if debugger:
einfo = sys.exc_info()
pdb = __import__(debugger)
if debugger == 'pudb':
pdb.post_mortem(einfo[2], einfo[0], einfo[1])
else:
pdb.post_mortem(einfo[2])
exit(1)
def show_location(self, lineno, column, show=3):
# Three lines ought to be enough
lower = lineno - show if lineno - show > 0 else 0
for i, line in enumerate(self.script.source.split('\n')[lower:lineno]):
print(lower + i + 1, line)
print(' ' * (column + len(str(lineno))), '^')
def show_operation(self):
print("%s:\n" % self.operation.capitalize())
getattr(self, 'show_' + self.operation)()
def show_completions(self):
for completion in self.completions:
print(completion.name)
# TODO: Support showing the location in other files
# TODO: Move this printing to the completion objects themselves
def show_usages(self):
for completion in self.completions:
print(completion.description)
if os.path.abspath(completion.module_path) == os.path.abspath(self.path):
self.show_location(completion.line, completion.column)
def show_call_signatures(self):
for completion in self.completions:
# This is too complicated to print. It really should be
# implemented in str() anyway.
print(completion)
# Can't print the location here because we don't have the module path
def show_goto_definitions(self):
for completion in self.completions:
print(completion.desc_with_module)
if os.path.abspath(completion.module_path) == os.path.abspath(self.path):
self.show_location(completion.line, completion.column)
show_goto_assignments = show_goto_definitions
def show_errors(self):
print(self.traceback)
print(("Error with running Script(...).{operation}() with\n"
"\tpath: {path}\n"
"\tline: {line}\n"
"\tcolumn: {column}").format(**self.__dict__))
def main(arguments):
debugger = 'pdb' if arguments['--pdb'] else \
'ipdb' if arguments['--ipdb'] else \
'pudb' if arguments['--pudb'] else None
record = arguments['--record']
jedi.settings.use_filesystem_cache = arguments['--fs-cache']
if arguments['--debug']:
jedi.set_debug_function()
if arguments['redo'] or arguments['show']:
t = TestCase.from_cache(record)
if arguments['show']:
t.show_errors()
else:
t.run(debugger)
elif arguments['run']:
TestCase(
arguments['<operation>'], arguments['<path>'],
int(arguments['<line>']), int(arguments['<column>'])
).run(debugger, print_result=True)
else:
for _ in range(int(arguments['--maxtries'])):
t = TestCase.generate(arguments['<path>'] or '.')
t.run(debugger, record)
print('.', end='')
sys.stdout.flush()
print()
if __name__ == '__main__':
arguments = docopt(__doc__)
main(arguments)

View File

@@ -0,0 +1,14 @@
"""
This is a module that imports the *standard library* unittest,
despite there being a local "unittest" module. It specifies that it
wants the stdlib one with the ``absolute_import`` __future__ import.
The twisted equivalent of this module is ``twisted.trial._synctest``.
"""
from __future__ import absolute_import
import unittest
class Assertions(unittest.TestCase):
pass

View File

@@ -0,0 +1,14 @@
"""
This is a module that shadows a builtin (intentionally).
It imports a local module, which in turn imports stdlib unittest (the
name shadowed by this module). If that is properly resolved, there's
no problem. However, if jedi doesn't understand absolute_imports, it
will get this module again, causing infinite recursion.
"""
from local_module import Assertions
class TestCase(Assertions):
def test(self):
self.assertT

View File

@@ -0,0 +1,3 @@
""" needed for some modules to test against packages. """
some_variable = 1

View File

@@ -2,6 +2,7 @@
# basic array lookups
# -----------------
#? int()
[1,""][0]
#? str()
@@ -10,6 +11,8 @@
[1,""][2]
#? int() str()
[1,""][20]
#? int() str()
[1,""][str(hello)]
a = list()
#? list()
@@ -93,6 +96,28 @@ a4
b4
# -----------------
# multiple assignments
# -----------------
a = b = 1
#? int()
a
#? int()
b
(a, b) = (c, (e, f)) = ('2', (3, 4))
#? str()
a
#? tuple()
b
#? str()
c
#? int()
e
#? int()
f
# -----------------
# unnessecary braces
# -----------------
@@ -117,6 +142,23 @@ u1
#? int()
a
def a(): return ''
#? str()
(a)()
#? str()
(a)().replace()
#? int()
(tuple).index()
#? int()
(tuple)().index()
class C():
def __init__(self):
self.a = (str()).upper()
#? str()
C().a
# -----------------
# imbalanced sides
# -----------------
@@ -161,6 +203,31 @@ dic2[r'asdf']
#? int() str()
dic2['just_something']
def f():
""" github #83 """
r = {}
r['status'] = (200, 'ok')
return r
#? dict()
f()
# completion within dicts
#? 9 ['str']
{str: str}
# iteration problem (detected with sith)
d = dict({'a':''})
def y(a):
return a
#?
y(**d)
# problem with more complicated casts
dic = {str(key): ''}
#? str()
dic['']
# -----------------
# with variable as index
# -----------------

View File

@@ -9,6 +9,16 @@ int()
int(str)
# -----------------
# should not complete
# -----------------
#? []
.
#? []
str..
#? []
a(0):.
# -----------------
# if/else/elif
# -----------------
@@ -72,6 +82,9 @@ for i in list([1,'']):
#? int() str()
i
#? int() str()
for x in [1,'']: x
a = []
b = [1.0,'']
for i in b:
@@ -196,6 +209,9 @@ def a():
"""
pass
#?
# str literals in comment """ upper
# -----------------
# magic methods
# -----------------
@@ -207,3 +223,45 @@ class B(): pass
A.__init__
#? ['__init__']
B.__init__
#? ['__init__']
int().__init__
# -----------------
# comments
# -----------------
class A():
def __init__(self):
self.hello = {} # comment shouldn't be a string
#? dict()
A().hello
# -----------------
# unicode
# -----------------
a = 'smörbröd'
#? str()
a
xyz = 'smörbröd.py'
if 1:
#? str()
xyz
# -----------------
# exceptions
# -----------------
try:
import math
except ImportError as i_a:
#? ['i_a']
i_a
#? ImportError()
i_a
try:
import math
except ImportError, i_b:
#? ['i_b']
i_b
#? ImportError()
i_b

View File

@@ -119,6 +119,13 @@ strs.second
#? ['var_class']
TestClass.var_class.var_class.var_class.var_class
# operations (+, *, etc) shouldn't be InstanceElements - #246
class A():
def __init__(self):
self.addition = 1 + 2
#? int()
A().addition
# -----------------
# inheritance
# -----------------
@@ -241,192 +248,6 @@ class A():
#? list()
A().b()
# -----------------
# descriptors
# -----------------
class RevealAccess(object):
"""
A data descriptor that sets and returns values
normally and prints a message logging their access.
"""
def __init__(self, initval=None, name='var'):
self.val = initval
self.name = name
def __get__(self, obj, objtype):
print('Retrieving', self.name)
return self.val
def __set__(self, obj, val):
print('Updating', self.name)
self.val = val
def just_a_method(self):
pass
class C(object):
x = RevealAccess(10, 'var "x"')
#? RevealAccess()
x
#? ['just_a_method']
x.just_a_method
y = 5.0
def __init__(self):
#? int()
self.x
#? []
self.just_a_method
#? []
C.just_a_method
m = C()
#? int()
m.x
#? float()
m.y
#? int()
C.x
#? []
m.just_a_method
#? []
C.just_a_method
# -----------------
# properties
# -----------------
class B():
@property
def r(self):
return 1
@r.setter
def r(self, value):
return ''
def t(self):
return ''
p = property(t)
#? []
B().r()
#? int()
B().r
#? str()
B().p
#? []
B().p()
class PropClass():
def __init__(self, a):
self.a = a
@property
def ret(self):
return self.a
@ret.setter
def ret(self, value):
return 1.0
def ret2(self):
return self.a
ret2 = property(ret2)
@property
def nested(self):
""" causes recusions in properties, should work """
return self.ret
@property
def nested2(self):
""" causes recusions in properties, should not work """
return self.nested2
@property
def join1(self):
""" mutual recusion """
return self.join2
@property
def join2(self):
""" mutual recusion """
return self.join1
#? str()
PropClass("").ret
#? []
PropClass().ret.
#? str()
PropClass("").ret2
#?
PropClass().ret2
#? int()
PropClass(1).nested
#? []
PropClass().nested.
#?
PropClass(1).nested2
#? []
PropClass().nested2.
#?
PropClass(1).join1
# -----------------
# staticmethod/classmethod
# -----------------
class E(object):
a = ''
def __init__(self, a):
self.a = a
def f(x):
return x
f = staticmethod(f)
@staticmethod
def g(x):
return x
def s(cls, x):
return x
s = classmethod(s)
@classmethod
def t(cls, x):
return x
@classmethod
def u(cls, x):
return cls.a
e = E(1)
#? int()
e.f(1)
#? int()
E.f(1)
#? int()
e.g(1)
#? int()
E.g(1)
#? int()
e.s(1)
#? int()
E.s(1)
#? int()
e.t(1)
#? int()
E.t(1)
#? str()
e.u(1)
#? str()
E.u(1)
# -----------------
# recursions
# -----------------
@@ -488,6 +309,8 @@ getattr()
getattr(str)
#?
getattr(getattr, 1)
#?
getattr(str, [])
class Base():
@@ -523,3 +346,44 @@ class PrivateVar():
PrivateVar().__var
#?
PrivateVar().__var
# -----------------
# super
# -----------------
class Super(object):
a = 3
class TestSuper(Super):
#?
super()
def test(self):
#? Super()
super()
#? ['a']
super().a
if 1:
#? Super()
super()
def a():
#?
super()
# -----------------
# if flow at class level
# -----------------
class TestX(object):
def normal_method(self):
return 1
if True:
def conditional_method(self):
var = self.normal_method()
#? int()
var
return 2
def other_method(self):
var = self.conditional_method()
#? int()
var

View File

@@ -76,7 +76,7 @@ exe[4]['d']
# -----------------
# class decorators
# Decorator is a class
# -----------------
class Decorator(object):
def __init__(self, func):
@@ -94,18 +94,65 @@ nothing("")[0]
#? str()
nothing("")[1]
@Decorator
def nothing(a,b,c):
return a,b,c
class MethodDecoratorAsClass():
class_var = 3
@Decorator
def func_without_self(arg, arg2):
return arg, arg2
@Decorator
def func_with_self(self, arg):
return self.class_var
#? int()
MethodDecoratorAsClass().func_without_self('')[0]
#? str()
MethodDecoratorAsClass().func_without_self('')[1]
#?
MethodDecoratorAsClass().func_with_self(1)
class SelfVars():
"""Init decorator problem as an instance, #247"""
@Decorator
def __init__(self):
"""
init decorators should be ignored when looking up variables in the
class.
"""
self.c = list
@Decorator
def shouldnt_expose_var(not_self):
"""
Even though in real Python this shouldn't expose the variable, in this
case Jedi exposes the variable, because these kind of decorators are
normally descriptors, which SHOULD be exposed (at least 90%).
"""
not_self.b = 1.0
def other_method(self):
#? float()
self.b
#? list
self.c
# -----------------
# not found decorators
# not found decorators (are just ignored)
# -----------------
@not_found_decorator
def just_a_func():
return 1
#? []
#? int()
just_a_func()
#? []
just_a_func.
#? ['__closure__']
just_a_func.__closure__
class JustAClass:
@@ -113,14 +160,97 @@ class JustAClass:
def a(self):
return 1
#? []
JustAClass().a.
#? []
JustAClass().a()
#? []
JustAClass.a.
#? []
#? ['__closure__']
JustAClass().a.__closure__
#? int()
JustAClass().a()
#? ['__closure__']
JustAClass.a.__closure__
#? int()
JustAClass.a()
# -----------------
# illegal decorators
# -----------------
class DecoratorWithoutCall():
def __init__(self, func):
self.func = func
@DecoratorWithoutCall
def f():
return 1
# cannot be resolved - should be ignored
@DecoratorWithoutCall(None)
def g():
return 1
#?
f()
#? int()
g()
# -----------------
# method decorators
# -----------------
def dec(f):
def wrapper(s):
return f(s)
return wrapper
class MethodDecorators():
_class_var = 1
def __init__(self):
self._method_var = ''
@dec
def constant(self):
return 1.0
@dec
def class_var(self):
return self._class_var
@dec
def method_var(self):
return self._method_var
#? float()
MethodDecorators().constant()
#? int()
MethodDecorators().class_var()
#? str()
MethodDecorators().method_var()
class Base():
@not_existing
def __init__(self):
pass
@not_existing
def b(self):
return ''
@dec
def c(self):
return 1
class MethodDecoratorDoesntExist(Base):
"""#272 github: combination of method decorators and super()"""
def a(self):
#?
super().__init__()
#? str()
super().b()
#? int()
super().c()
#? float()
self.d()
@doesnt_exist
def d(self):
return 1.0
# -----------------
# others
@@ -148,5 +278,9 @@ follow_statement(1)
# class decorators should just be ignored
@should_ignore
class A(): pass
class A():
def ret(self):
return 1
#? int()
A().ret()

View File

@@ -0,0 +1,65 @@
"""
Fallback to callee definition when definition not found.
- https://github.com/davidhalter/jedi/issues/131
- https://github.com/davidhalter/jedi/pull/149
"""
"""Parenthesis closed at next line."""
#? isinstance
isinstance(
)
#? isinstance
isinstance(
)
#? isinstance
isinstance(None,
)
#? isinstance
isinstance(None,
)
"""Parenthesis closed at same line."""
# Note: len('isinstance(') == 11
#? 11 isinstance
isinstance()
# Note: len('isinstance(None,') == 16
##? 16 isinstance
isinstance(None,)
# Note: len('isinstance(None,') == 16
##? 16 isinstance
isinstance(None, )
# Note: len('isinstance(None, ') == 17
##? 17 isinstance
isinstance(None, )
# Note: len('isinstance( ') == 12
##? 12 isinstance
isinstance( )
"""Unclosed parenthesis."""
#? isinstance
isinstance(
def x(): pass # acts like EOF
##? isinstance
isinstance(
def x(): pass # acts like EOF
#? isinstance
isinstance(None,
def x(): pass # acts like EOF
##? isinstance
isinstance(None,

View File

@@ -0,0 +1,182 @@
class RevealAccess(object):
"""
A data descriptor that sets and returns values
normally and prints a message logging their access.
"""
def __init__(self, initval=None, name='var'):
self.val = initval
self.name = name
def __get__(self, obj, objtype):
print('Retrieving', self.name)
return self.val
def __set__(self, obj, val):
print('Updating', self.name)
self.val = val
def just_a_method(self):
pass
class C(object):
x = RevealAccess(10, 'var "x"')
#? RevealAccess()
x
#? ['just_a_method']
x.just_a_method
y = 5.0
def __init__(self):
#? int()
self.x
#? []
self.just_a_method
#? []
C.just_a_method
m = C()
#? int()
m.x
#? float()
m.y
#? int()
C.x
#? []
m.just_a_method
#? []
C.just_a_method
# -----------------
# properties
# -----------------
class B():
@property
def r(self):
return 1
@r.setter
def r(self, value):
return ''
def t(self):
return ''
p = property(t)
#? []
B().r()
#? int()
B().r
#? str()
B().p
#? []
B().p()
class PropClass():
def __init__(self, a):
self.a = a
@property
def ret(self):
return self.a
@ret.setter
def ret(self, value):
return 1.0
def ret2(self):
return self.a
ret2 = property(ret2)
@property
def nested(self):
""" causes recusions in properties, should work """
return self.ret
@property
def nested2(self):
""" causes recusions in properties, should not work """
return self.nested2
@property
def join1(self):
""" mutual recusion """
return self.join2
@property
def join2(self):
""" mutual recusion """
return self.join1
#? str()
PropClass("").ret
#? []
PropClass().ret.
#? str()
PropClass("").ret2
#?
PropClass().ret2
#? int()
PropClass(1).nested
#? []
PropClass().nested.
#?
PropClass(1).nested2
#? []
PropClass().nested2.
#?
PropClass(1).join1
# -----------------
# staticmethod/classmethod
# -----------------
class E(object):
a = ''
def __init__(self, a):
self.a = a
def f(x):
return x
f = staticmethod(f)
@staticmethod
def g(x):
return x
def s(cls, x):
return x
s = classmethod(s)
@classmethod
def t(cls, x):
return x
@classmethod
def u(cls, x):
return cls.a
e = E(1)
#? int()
e.f(1)
#? int()
E.f(1)
#? int()
e.g(1)
#? int()
E.g(1)
#? int()
e.s(1)
#? int()
E.s(1)
#? int()
e.t(1)
#? int()
E.t(1)
#? str()
e.u(1)
#? str()
E.u(1)

View File

@@ -0,0 +1,121 @@
""" Test docstrings in functions and classes, which are used to infer types """
# -----------------
# sphinx style
# -----------------
def f(a, b, c, d):
""" asdfasdf
:param a: blablabla
:type a: str
:type b: (str, int)
:type c: threading.Thread
:type d: :class:`threading.Thread`
:rtype: dict
"""
#? str()
a
#? str()
b[0]
#? int()
b[1]
#? ['join']
c.join
#? ['join']
d.join
#? dict()
f()
# wrong declarations
def f(a, b):
"""
:param a: Forgot type declaration
:type a:
:param b: Just something
:type b: ``
:rtype:
"""
#?
a
#?
b
#?
f()
# -----------------
# epydoc style
# -----------------
def e(a, b):
""" asdfasdf
@type a: str
@param a: blablabla
@type b: (str, int)
@param b: blablah
@rtype: list
"""
#? str()
a
#? str()
b[0]
#? int()
b[1]
#? list()
e()
# Returns with param type only
def rparam(a,b):
"""
@type a: str
"""
return a
#? str()
rparam()
# Composite types
def composite():
"""
@rtype: (str, int, dict)
"""
x, y, z = composite()
#? str()
x
#? int()
y
#? dict()
z
# Both docstring and calculated return type
def both():
"""
@rtype: str
"""
return 23
#? str(), int()
both()
class Test(object):
def __init__(self):
self.teststr = ""
"""
# jedi issue #210
"""
def test(self):
#? ['teststr']
self.teststr
# -----------------
# statement docstrings
# -----------------
d = ''
""" bsdf """
#? str()
d.upper()

View File

@@ -63,6 +63,11 @@ def func(c=1):
func(1.0)
# Needs to be here, because in this case func is an import -> shouldn't lead to
# exceptions.
import sys as func
func.sys
# -----------------
# classes
# -----------------

View File

@@ -46,6 +46,15 @@ def multi_line_func(a, # comment blabla
#? str()
multi_line_func(1,'')
# nothing after comma
def asdf(a):
return a
x = asdf(a=1,
)
#? int()
x
# -----------------
# double execution
# -----------------
@@ -109,10 +118,10 @@ def func(a=1, b=''):
return a, b
exe = func(b=list, a=tuple)
#? tuple()
#? tuple
exe[0]
#? list()
#? list
exe[1]
# -----------------
@@ -146,15 +155,29 @@ def a():
# -----------------
def args_func(*args):
#? tuple()
return args
exe = args_func(1, "")
#? int()
exe[0]
#? str()
exe[1]
# illegal args (TypeError)
#?
args_func(*1)[0]
# iterator
#? int()
args_func(*iter([1]))[0]
# different types
e = args_func(*[1+"", {}])
#? int() str()
e[0]
#? dict()
e[1]
_list = [1,""]
exe2 = args_func(_list)[0]
@@ -181,6 +204,9 @@ exe[1][1]
# ** kwargs
# -----------------
def kwargs_func(**kwargs):
#? ['keys']
kwargs.keys
#? dict()
return kwargs
exe = kwargs_func(a=3,b=4.0)
@@ -202,6 +228,12 @@ exe2['a']
# *args / ** kwargs
# -----------------
def func_without_call(*args, **kwargs):
#? tuple()
args
#? dict()
kwargs
def fu(a=1, b="", *args, **kwargs):
return a, b, args, kwargs
@@ -354,3 +386,66 @@ annot_ret('')
def a(): pass
#? ['__closure__']
a.__closure__
# -----------------
# lambdas
# -----------------
a = lambda: 3
#? int()
a()
x = []
a = lambda x: x
#? int()
a(0)
#? float()
(lambda x: x)(3.0)
arg_l = lambda x, y: y, x
#? float()
arg_l[0]('', 1.0)
#? list()
arg_l[1]
arg_l = lambda x, y: (y, x)
args = 1,""
result = arg_l(*args)
#? tuple()
result
#? str()
result[0]
#? int()
result[1]
def with_lambda(callable_lambda, *args, **kwargs):
return callable_lambda(1, *args, **kwargs)
#? int()
with_lambda(arg_l, 1.0)[1]
#? float()
with_lambda(arg_l, 1.0)[0]
#? float()
with_lambda(arg_l, y=1.0)[0]
#? int()
with_lambda(lambda x: x)
#? float()
with_lambda(lambda x, y: y, y=1.0)
arg_func = lambda *args, **kwargs: (args[0], kwargs['a'])
#? int()
arg_func(1, 2, a='', b=10)[0]
#? list()
arg_func(1, 2, a=[], b=10)[1]
# magic method
a = lambda: 3
#? ['__closure__']
a.__closure__
class C():
def __init__(self):
self.a = lambda: 1
#? int()
C().a()

View File

@@ -1,19 +1,25 @@
# goto command test are a different in syntax
# goto_assignments command tests are different in syntax
definition = 3
##! 0 ['a=definition']
#! 0 ['a = definition']
a = definition
#! []
b
#! ['a=definition']
#! ['a = definition']
a
b = a
c = b
#! ['c=b']
#! ['c = b']
c
cd = 1
#! 1 ['cd = c']
cd = c
#! 0 ['cd = e']
cd = e
#! ['module math']
import math
#! ['import math']
@@ -21,12 +27,12 @@ math
#! ['import math']
b = math
#! ['b=math']
#! ['b = math']
b
class C(object):
def b(self):
#! ['b=math']
#! ['b = math']
b
#! ['def b']
self.b
@@ -39,7 +45,7 @@ class C(object):
#! ['def b']
b
#! ['b=math']
#! ['b = math']
b
#! ['def b']
@@ -57,9 +63,9 @@ D.b
#! ['def b']
D().b
#! 0 ['D=C']
#! 0 ['D = C']
D().b
#! 0 ['D=C']
#! 0 ['D = C']
D().b
def c():
@@ -76,39 +82,45 @@ c()
#! ['module import_tree']
import import_tree
#! ['a=""']
#! ["a = ''"]
import_tree.a
#! ['module mod1']
import import_tree.mod1
#! ['a=1']
#! ['a = 1']
import_tree.mod1.a
#! ['module pkg']
import import_tree.pkg
#! ['a=list']
#! ['a = list']
import_tree.pkg.a
#! ['module mod1']
import import_tree.pkg.mod1
#! ['a=1.0']
#! ['a = 1.0']
import_tree.pkg.mod1.a
#! ['a=""']
#! ["a = ''"]
import_tree.a
#! ['module mod1']
from import_tree.pkg import mod1
#! ['a=1.0']
#! ['a = 1.0']
mod1.a
#! ['module mod1']
from import_tree import mod1
#! ['a=1']
#! ['a = 1']
mod1.a
#! ['a=1.0']
#! ['a = 1.0']
from import_tree.pkg.mod1 import a
#! ['import os']
from .imports import os
#! ['some_variable = 1']
from . import some_variable
# -----------------
# anonymous classes
# -----------------
@@ -121,11 +133,41 @@ def func():
#! 8 ['def b']
func().b()
# -----------------
# -----------------
# on itself
# -----------------
# -----------------
#! 7 ['class ClassDef']
class ClassDef():
""" abc """
pass
# -----------------
# params
# -----------------
param = ClassDef
#! 8 ['param']
def ab1(param): pass
#! 9 ['param']
def ab2(param): pass
#! 11 ['param = ClassDef']
def ab3(a=param): pass
ab1(ClassDef);ab2(ClassDef);ab3(ClassDef)
# -----------------
# for loops
# -----------------
for i in range(1):
#! ['for i in range(1): i']
i
for key, value in [(1,2)]:
#! ['for key,value in [(1...']
key
for i in []:
#! ['for i in []: i']
i

View File

@@ -1 +1,2 @@
a = 1
from import_tree.random import a as c

View File

@@ -0,0 +1 @@
from . import mod1 as fake

View File

@@ -0,0 +1,5 @@
import recurse_class2
class C(recurse_class2.C):
def a(self):
pass

View File

@@ -0,0 +1,4 @@
import recurse_class1
class C(recurse_class1.C):
pass

View File

@@ -56,6 +56,53 @@ def scope_nested():
#? set
import_tree.random.a
def scope_nested2():
"""Multiple modules should be indexable, if imported"""
import import_tree.mod1
import import_tree.pkg
#? ['mod1']
import_tree.mod1
#? ['pkg']
import_tree.pkg
#? []
import_tree.rename1
def from_names():
#? ['mod1']
from import_tree.pkg.
#? ['path']
from os.
def builtin_test():
#? ['math']
import math
def scope_from_import_variable():
"""
All of them shouldn't work, because "fake" imports don't work in python
without the use of ``sys.modules`` modifications (e.g. ``os.path`` see also
github issue #213 for clarification.
"""
#?
from import_tree.mod2.fake import a
#?
from import_tree.mod2.fake import c
#?
a
#?
c
def scope_from_import_variable_with_parenthesis():
from import_tree.mod2.fake import (
a, c
)
#?
a
#?
c
# -----------------
# std lib modules
# -----------------
@@ -72,6 +119,16 @@ import os
#? ['dirname']
os.path.dirname
#? os.path.join
from os.path import join
from os.path import (
expanduser
)
#? os.path.expanduser
expanduser
from itertools import (tee,
islice)
#? ['islice']
@@ -152,11 +209,11 @@ from .......import_tree import mod1
#?
mod1.a
from .. import run
from .. import helpers
#? int()
run.tests_fail
helpers.sample_int
from ..run import tests_fail as f
from ..helpers import sample_int as f
#? int()
f
@@ -175,6 +232,13 @@ mod1.
#? str()
imp_tree.a
#? ['some_variable']
from . import some_variable
#? ['arrays']
from . import arrays
#? []
from . import import_tree as ren
# -----------------
# special positions -> edge cases
@@ -189,7 +253,9 @@ import datetime.
#? []
import datetime.date
#? 18 ['mod1', 'random', 'pkg', 'rename1', 'rename2']
#? 18 ['import']
from import_tree. import pkg
#? 17 ['mod1', 'mod2', 'random', 'pkg', 'rename1', 'rename2', 'recurse_class1', 'recurse_class2']
from import_tree. import pkg
#? 18 ['pkg']
@@ -208,3 +274,32 @@ from not_a_module import
# self import
# this can cause recursions
from imports import *
#137
import json
#? 23 json.dump
from json import load, dump
#? 17 json.load
from json import load, dump
# without the from clause:
import json, datetime
#? 7 json
import json, datetime
#? 13 datetime
import json, datetime
# -----------------
# packages
# -----------------
from import_tree.mod1 import c
#? set
c
from import_tree import recurse_class1
#? ['a']
recurse_class1.C.a
# github #239 RecursionError
#? ['a']
recurse_class1.C().a

View File

@@ -6,9 +6,22 @@ Basically this file could change depending on the current implementation. But
there should never be any errors.
"""
# wait until keywords are out of definitions (pydoc function).
##? 5
's'()
#? ['upper']
str()).upper
# -----------------
# funcs
# -----------------
def asdf(a or b): # multiple param names
return a
#? int()
asdf(2)
from a import (b
def blub():
return 0
@@ -42,6 +55,22 @@ def normalfunc():
#? int()
normalfunc()
# dots in param
def f(seq1...=None):
return seq1
#? int()
f(1)
@
def test_empty_decorator():
return 1
#? int()
test_empty_decorator()
# -----------------
# flows
# -----------------
# first part not complete (raised errors)
if a
@@ -77,6 +106,10 @@ for_local
for_local
# -----------------
# list comprehensions
# -----------------
a2 = [for a2 in [0]]
#?
a2[0]
@@ -101,16 +134,23 @@ a[0]
a = [a for a in [1,2]
def break(): pass
#? list()
#? int()
a[0]
#? []
int()).
def asdf(a or b): # multiple param names
return a
# -----------------
# keywords
# -----------------
#? int()
asdf(2)
#! []
as
def empty_assert():
x = 3
assert
#? int()
x
import datetime as

View File

@@ -1,5 +1,6 @@
""" named params:
>>> def a(abc): pass
...
>>> a(abc=3) # <- this stuff
"""

View File

@@ -1,99 +0,0 @@
"""
Renaming tests. This means search for related names.
I always leave a little bit of space to add room for additions, because the
results always contain position informations.
"""
#< 4 (7,4), (10,0), (12,0)
def abc(): pass
#< 0 (7,4), (10,0), (12,0)
abc.d.a.bsaasd.abc.d
abc
abc =
#< (20,0), (23,0)
abc
Abc = 3
#< 6 (30,6), (32,4), (35,8), (47,0)
class Abc():
#< (30,6), (32,4), (35,8), (47,0)
Abc
def Abc(self):
Abc; self.c = 3
#< 17 (38,16), (40,8)
def a(self, Abc):
#< 10 (38,16), (40,8)
Abc
#< 19 (43,18), (45,8)
def self_test(self):
#< 12 (43,18), (45,8)
self.b
Abc.d.Abc
#< 4 (55,4), (59,1)
def blub():
#< (55,4), (59,1)
@blub
def a(): pass
#< (65,7), (68,0)
import module_not_exists
#< (65,7), (68,0)
module_not_exists
#< (1,0), (75,24), (78,0), (81,17), (4,5), (85,17), (88,17)
from import_tree import rename1
#< (78,8), (3,0), (4,20), (6,0), (81,32), (85,32), (82,0)
rename1.abc
#< (78,8), (3,0), (4,20), (6,0), (81,32), (85,32), (82,0)
from import_tree.rename1 import abc
abc
#< 20 (1,0), (4,5), (75,24), (78,0), (81,17), (85,17), (88,17)
from import_tree.rename1 import abc
#< (88, 32),
from import_tree.rename1 import not_existing
response = 5
#< 0 (95, 0), (96, 0), (97, 0), (99, 0)
response = HttpResponse(mimetype='application/pdf')
response['Content-Disposition'] = 'attachment; filename=%s.pdf' % id
response.write(pdf)
#< (95, 0), (96, 0), (97, 0), (99, 0)
response

View File

@@ -67,11 +67,25 @@ def example(a):
#? str()
example('')
# -----------------
# various regression tests
# sqlite3 (#84)
# -----------------
#62
import threading
#? ['_Verbose', '_VERBOSE']
threading._Verbose
import sqlite3
#? sqlite3.Connection()
con = sqlite3.connect()
#? sqlite3.Cursor()
c = con.cursor()
#? sqlite3.Row()
row = c.fetchall()[0]
#? str()
row.keys()[0]
def huhu(db):
"""
:type db: sqlite3.Connection
:param db: the db connection
"""
#? sqlite3.Connection()
db

View File

@@ -12,7 +12,7 @@ sys.path.append(sys.path[1] + '/thirdparty')
# syntax err
sys.path.append('a' +* '/thirdparty')
#? ['evaluate']
#? ['evaluate', 'evaluate_representation']
import evaluate
#? ['goto']

View File

@@ -1,7 +1,7 @@
from jedi import functions, evaluate, parsing
el = functions.complete()[0]
el = functions.completions()[0]
#? ['description']
el.description
@@ -10,8 +10,7 @@ el.description
scopes, path, dot, like = \
functions.prepare_goto(source, row, column,
source_path, True)
api._prepare_goto(source, row, column, path, True)
# has problems with that (sometimes) very deep nesting.
#? set()

233
test/completion/usages.py Normal file
View File

@@ -0,0 +1,233 @@
"""
Renaming tests. This means search for usages.
I always leave a little bit of space to add room for additions, because the
results always contain position informations.
"""
#< 4 (0,4), (3,0), (5,0)
def abc(): pass
#< 0 (-3,4), (0,0), (2,0)
abc.d.a.bsaasd.abc.d
abc
abc =
#< (-3,0), (0,0)
abc
Abc = 3
#< 6 (0,6), (2,4), (5,8), (17,0)
class Abc():
#< (-2,6), (0,4), (3,8), (15,0)
Abc
def Abc(self):
Abc; self.c = 3
#< 17 (0,16), (2,8)
def a(self, Abc):
#< 10 (-2,16), (0,8)
Abc
#< 19 (0,18), (2,8)
def self_test(self):
#< 12 (-2,18), (0,8)
self.b
Abc.d.Abc
#< 4 (0,4), (4,1)
def blub():
#< (-4,4), (0,1)
@blub
def a(): pass
#< 0 (0,0), (1,0)
set_object_var = object()
set_object_var.var = 1
response = 5
#< 0 (0,0), (1,0), (2,0), (4,0)
response = HttpResponse(mimetype='application/pdf')
response['Content-Disposition'] = 'attachment; filename=%s.pdf' % id
response.write(pdf)
#< (-4,0), (-3,0), (-2,0), (0,0)
response
# -----------------
# imports
# -----------------
#< (0,7), (3,0)
import module_not_exists
#< (-3,7), (0,0)
module_not_exists
#< ('rename1', 1,0), (0,24), (3,0), (6,17), ('rename2', 4,5), (10,17), (13,17)
from import_tree import rename1
#< (0,8), ('rename1',3,0), ('rename2',4,20), ('rename2',6,0), (3,32), (7,32), (4,0)
rename1.abc
#< (-3,8), ('rename1', 3,0), ('rename2', 4,20), ('rename2', 6,0), (0,32), (4,32), (1,0)
from import_tree.rename1 import abc
abc
#< 20 ('rename1', 1,0), ('rename2', 4,5), (-10,24), (-7,0), (-4,17), (0,17), (3,17)
from import_tree.rename1 import abc
#< (0, 32),
from import_tree.rename1 import not_existing
# shouldn't work
#<
from not_existing import *
# -----------------
# classes
# -----------------
class TestMethods(object):
#< 8 (0,8), (2,13)
def a_method(self):
#< 13 (-2,8), (0,13)
self.a_method()
#< 13 (2,8), (0,13), (3,13)
self.b_method()
def b_method(self):
self.b_method
class TestClassVar(object):
#< 4 (0,4), (5,13), (7,21)
class_v = 1
def a(self):
class_v = 1
#< (-5,4), (0,13), (2,21)
self.class_v
#< (-7,4), (-2,13), (0,21)
TestClassVar.class_v
#< (0,8), (-7, 8)
class_v
class TestInstanceVar():
def a(self):
#< 13 (4,13), (0,13)
self._instance_var = 3
def b(self):
#< (-4,13), (0,13)
self._instance_var
class NestedClass():
def __getattr__(self, name):
return self
# Shouldn't find a definition, because there's no name defined (used ``getattr``).
#< (0, 14),
NestedClass().instance
# -----------------
# inheritance
# -----------------
class Super(object):
#< 4 (0,4), (23,18), (25,13)
base_class = 1
#< 4 (0,4),
class_var = 1
#< 8 (0,8),
def base_method(self):
#< 13 (0,13), (20,13)
self.base_var = 1
#< 13 (0,13), (24,13), (29,13)
self.instance_var = 1
#< 8 (0,8),
def just_a_method(self): pass
#< 20 (0,16), (-18,6)
class TestClass(Super):
#< 4 (0,4),
class_var = 1
def x_method(self):
#< (0,18), (2,13), (-23,4)
TestClass.base_class
#< (-2,18), (0,13), (-25,4)
self.base_class
#< (-20,13), (0,13)
self.base_var
#<
TestClass.base_var
#< 13 (5,13), (0,13)
self.instance_var = 3
#< 9 (0,8),
def just_a_method(self):
#< (-5,13), (0,13), (-29,13)
self.instance_var
# -----------------
# properties
# -----------------
class TestProperty:
@property
#< 10 (0,8), (5,13)
def prop(self):
return 1
def a(self):
#< 13 (-5,8), (0,13)
self.prop
@property
#< 13 (0,8), (4,5)
def rw_prop(self):
return self._rw_prop
#< 8 (-4,8), (0,5)
@rw_prop.setter
#< 8 (0,8), (5,13)
def rw_prop(self, value):
self._rw_prop = value
def b(self):
#< 13 (-5,8), (0,13)
self.rw_prop
# -----------------
# *args, **kwargs
# -----------------
#< 11 (1,11), (0,8)
def f(**kwargs):
return kwargs
# -----------------
# No result
# -----------------
if isinstance(j, int):
#<
j

99
test/conftest.py Normal file
View File

@@ -0,0 +1,99 @@
import os
import shutil
import tempfile
import pytest
from . import helpers
from . import run
from . import refactor
def pytest_addoption(parser):
parser.addoption(
"--integration-case-dir",
default=os.path.join(helpers.test_dir, 'completion'),
help="Directory in which integration test case files locate.")
parser.addoption(
"--refactor-case-dir",
default=os.path.join(helpers.test_dir, 'refactor'),
help="Directory in which refactoring test case files locate.")
parser.addoption(
"--test-files", "-T", default=[], action='append',
help=(
"Specify test files using FILE_NAME[:LINE[,LINE[,...]]]. "
"For example: -T generators.py:10,13,19. "
"Note that you can use -m to specify the test case by id."))
parser.addoption(
"--thirdparty", action='store_true',
help="Include integration tests that requires third party modules.")
def parse_test_files_option(opt):
"""
Parse option passed to --test-files into a key-value pair.
>>> parse_test_files_option('generators.py:10,13,19')
('generators.py', [10, 13, 19])
"""
opt = str(opt)
if ':' in opt:
(f_name, rest) = opt.split(':', 1)
return (f_name, list(map(int, rest.split(','))))
else:
return (opt, [])
def pytest_generate_tests(metafunc):
"""
:type metafunc: _pytest.python.Metafunc
"""
test_files = dict(map(parse_test_files_option,
metafunc.config.option.test_files))
if 'case' in metafunc.fixturenames:
base_dir = metafunc.config.option.integration_case_dir
thirdparty = metafunc.config.option.thirdparty
cases = list(run.collect_dir_tests(base_dir, test_files))
if thirdparty:
cases.extend(run.collect_dir_tests(
os.path.join(base_dir, 'thirdparty'), test_files, True))
metafunc.parametrize('case', cases)
if 'refactor_case' in metafunc.fixturenames:
base_dir = metafunc.config.option.refactor_case_dir
metafunc.parametrize(
'refactor_case',
refactor.collect_dir_tests(base_dir, test_files))
@pytest.fixture()
def isolated_jedi_cache(monkeypatch, tmpdir):
"""
Set `jedi.settings.cache_directory` to a temporary directory during test.
Same as `clean_jedi_cache`, but create the temporary directory for
each test case (scope='function').
"""
from jedi import settings
monkeypatch.setattr(settings, 'cache_directory', str(tmpdir))
@pytest.fixture(scope='session')
def clean_jedi_cache(request):
"""
Set `jedi.settings.cache_directory` to a temporary directory during test.
Note that you can't use built-in `tmpdir` and `monkeypatch`
fixture here because their scope is 'function', which is not used
in 'session' scope fixture.
This fixture is activated in ../pytest.ini.
"""
from jedi import settings
old = settings.cache_directory
tmp = tempfile.mkdtemp(prefix='jedi-test-')
settings.cache_directory = tmp
@request.addfinalizer
def restore():
settings.cache_directory = old
shutil.rmtree(tmp)

Some files were not shown because too many files have changed in this diff Show More