2349 Commits

Author SHA1 Message Date
Dave Halter
66557903ae \\\r\n is as possible as \\\n. 2015-04-28 18:53:14 +02:00
Dave Halter
712ae01ac0 Classes should always evaluate to true when asked for py__bool__() 2015-04-28 18:32:19 +02:00
Dave Halter
607f43290f The backwards tokenizer sometimes parsed not only string literals but also normal names. 2015-04-28 18:10:08 +02:00
Dave Halter
c2a287c25a Usages on syntax should not return anything. 2015-04-28 17:35:26 +02:00
Dave Halter
126f490f1e Modules have now the name __main__ if they contain dots. 2015-04-28 17:29:42 +02:00
Dave Halter
bb02f99de3 Dynamically created trailers need a parent, otherwise it can lead to crashes. 2015-04-28 16:40:58 +02:00
Dave Halter
b59fc04432 Remove crate.io badges. Thy are not working anymore, see crateio/crate.io#18 2015-04-28 12:38:53 +02:00
Dave Halter
cbd3a8a59a Restructured loading of compiled __init__ files. 2015-04-28 02:30:32 +02:00
Dave Halter
836fcd6ea0 Small api.Script.goto cleanup. 2015-04-28 02:07:53 +02:00
Dave Halter
b6f635b88b Python 2.7 io.StringIO always needs unicode input. 2015-04-28 02:05:38 +02:00
Dave Halter
657920baf5 Finally able to ditch the old namespace_packages implementation. 2015-04-28 02:03:17 +02:00
Dave Halter
0d406d27fd Different __init__ file searching. 2015-04-28 01:58:49 +02:00
Dave Halter
b8bb258677 Get rid of get_importer and clean up imports in general. 2015-04-28 01:41:01 +02:00
Dave Halter
ef4b424cda Replace pr with tree, #566. 2015-04-28 01:34:31 +02:00
Dave Halter
71547641ae The recursion detector doesn't need to separate params and normal statements anymore, because now they are two completely different things. 2015-04-28 01:26:48 +02:00
Dave Halter
265e6b2c35 Change parser and api to use tree instead of pr. 2015-04-27 23:38:48 +02:00
Dave Halter
b6ebb2f8bf Fixed issues with last positions in the tokenizer, which was messed up a little bit a few commits ago. 2015-04-27 21:42:40 +02:00
Dave Halter
0a96083fde Fix ur'' literals. 2015-04-27 19:21:41 +02:00
Dave Halter
902482568e The tokenize endmarker should really be the maximum position possible. Caused matplotlib to fail. Fixes davidhalter/jedi-vim#377. 2015-04-27 19:01:45 +02:00
Dave Halter
47d468a9bc forgot to include test_evaluate/not_in_sys_path files. 2015-04-27 17:16:43 +02:00
Dave Halter
84b774d9e1 Small refactorings. 2015-04-27 17:07:38 +02:00
Dave Halter
d7417391a7 Skip star import cache tests. 2015-04-27 14:15:39 +02:00
Dave Halter
0203461980 Disable the star import cache. 2015-04-26 00:02:47 +02:00
Dave Halter
06d134a7c1 Finished changing the import logic. The sys.path calculations within Jedi are clearer now. 2015-04-25 22:45:08 +02:00
Dave Halter
d038fba9df er.wrap -> Evaluator.wrap 2015-04-23 13:51:42 +02:00
Dave Halter
ed74dde45c forgot to check in invisible_pkg 2015-04-23 13:40:05 +02:00
Dave Halter
d16da33b9b Small test fix. 2015-04-23 04:11:28 +02:00
Dave Halter
fbb960423e Remove legacy importer code. 2015-04-23 03:42:29 +02:00
Dave Halter
a7c4b5800b Namespace packages work again. This time the same way as Python does it. 2015-04-23 03:36:46 +02:00
Dave Halter
039579b391 Improved static analysis for imports. 2015-04-23 02:43:49 +02:00
Dave Halter
f4f30841ec change the return of _Importer.follow_file_system 2015-04-23 02:39:44 +02:00
Dave Halter
d04241b482 Goto should not include imports that cannot be followed. 2015-04-23 02:37:22 +02:00
Dave Halter
691e5a8969 Fix flask tests. 2015-04-22 03:58:44 +02:00
Dave Halter
29bd59a355 Following os.path should be possible again. 2015-04-22 03:35:18 +02:00
Dave Halter
dd3edd15f9 Remove legacy code from imports. 2015-04-22 03:22:54 +02:00
Dave Halter
7af5c23874 Cache bug fixes. 2015-04-22 03:01:32 +02:00
Dave Halter
05554a1c89 Fix some issues with import path errors. 2015-04-21 18:45:12 +02:00
Dave Halter
13267adfc2 Move the level calculation into the Importer. 2015-04-21 17:57:06 +02:00
Dave Halter
9b9049e574 Some small import changes that fix a few of the broken test cases. 2015-04-21 17:31:43 +02:00
Dave Halter
18c4b5f7dc Add py__package__ to the ModuleWrapper, which makes relative imports easy to implement and fixed a lot of other things. 2015-04-21 16:12:24 +02:00
Dave Halter
5c65e9cdaa py__name__ now returns the value found in the modules cache. 2015-04-20 16:40:10 +02:00
Dave Halter
77a37be83a Add a py__path__ method to the ModuleWrapper, that behaves very similar to a package's __path__ attribute. 2015-04-20 16:21:00 +02:00
Dave Halter
df9452f210 Trying to change the import logic completely. We now have a sys.modules like cache. 2015-04-20 14:47:33 +02:00
Dave Halter
8fca3f78a1 Add a py__name__ call to modules. This makes listing the qualified names of modules possible (in combination with the module_name_cache). Fixes #519. 2015-04-14 17:36:20 +02:00
Dave Halter
2f64a83e3c Rename test_api_classes -> test_classes. 2015-04-13 15:17:44 +02:00
Dave Halter
fbe26ab64a Importlib might raise a ValueError. Fix #491. 2015-04-13 15:12:46 +02:00
Dave Halter
bc765979ca Import priorities are wrong (__dict__ > files). Test for #536. 2015-04-13 15:04:49 +02:00
Dave Halter
e2455eb670 Call signatures should work better for builtin classes (ducktyping). Fixes #515. 2015-04-10 13:45:23 +02:00
Dave Halter
74779f1a5d Test and preparations for better call signatures with builtins, see #515. 2015-04-10 03:05:38 +02:00
Dave Halter
1e623509cd Fix README glitches. 2015-04-10 02:40:16 +02:00
Dave Halter
47bf1c5daf Issue with numbers after names in call signatures. It would cause Jedi to stop analysing call signatures. Fixes #510 2015-04-10 02:17:12 +02:00
Dave Halter
7a22d374ca Merge branch 'dev' of github.com:davidhalter/jedi into dev 2015-04-09 16:17:30 +02:00
Dave Halter
a9d3df9b5e Replace the threading.Thread tests in docstrings with random.Random tests, because that might work smother in the travis tests. Don't know why it broke there. 2015-04-09 16:17:16 +02:00
Dave Halter
fab6567485 Merge pull request #567 from mfussenegger/buildout
run buildout detection only once per buildout script
2015-04-09 13:07:58 +02:00
Mathias Fussenegger
67d9fbca81 run buildout detection only once per buildout script
in order to avoid running into the max recursion limit.
2015-04-09 08:51:25 +02:00
Dave Halter
1195ed64ea Fix a small issue in the import logic that caused tests to fail. 2015-04-09 01:43:50 +02:00
Dave Halter
79caa2186e list(open().read()) should work now, fixes #412. 2015-04-09 00:46:31 +02:00
Dave Halter
408d182c41 Changelog for 0.9.0. 2015-04-08 13:20:15 +02:00
Dave Halter
f122c9b5b3 Document the new features better in the next release. 2015-04-08 13:15:21 +02:00
Dave Halter
b106dc25bd Update the README to tell more about Python features. 2015-04-08 12:16:13 +02:00
Dave Halter
98cf9f0c1a Jedi description update. 2015-04-08 11:47:58 +02:00
Dave Halter
7773859305 Write the tests for init extension modules (#472). 2015-04-08 02:54:35 +02:00
Dave Halter
474d390220 Use imp.get_suffixes to deal with __init__ files that are not .py files but .so etc. fixes #472 2015-04-08 02:41:59 +02:00
Dave Halter
9149c5adc2 Python 3.2 tests didn't work because a u string literal was used. 2015-03-31 14:42:26 +02:00
Dave Halter
ef855a5316 Param descriptions should not end with a comma. 2015-03-31 14:38:03 +02:00
Dave Halter
72fd190149 unicode strings should not raise an error if used in repr.
Python 2 doesn't allow unicode objects in __repr__ methods. Therefore we need to encode them as utf-8 bytes.
2015-03-25 23:42:52 +01:00
Dave Halter
4bb41b6096 A property can raise an Exception, therefore the interpreter completion should check for those exceptions, fixes #538. 2015-03-24 15:26:00 +01:00
Dave Halter
54d8cd0a9b Small bug in parameter creation. 2015-03-24 15:06:11 +01:00
Dave Halter
0de5a0f412 Python 2 allows tuple unpacking in parameter definitions. Jedi just ignores such constructs, since they are really rare and not the future. 2015-03-24 15:02:07 +01:00
Dave Halter
61683cb83e Remove some unnecessary comment parts in the Python 2.7 grammar. 2015-03-08 22:40:22 +01:00
Dave Halter
e296b00201 Change the tests of @hamatov a small bit. They are now working with the new parser. 2015-03-06 13:10:59 +01:00
Dave Halter
2cddfd656b Merge branch 'unicode_tokenize_fix2' of https://github.com/hatamov/jedi into dev 2015-03-06 11:44:03 +01:00
Dave Halter
8b1c033fc4 Remove old commented code. 2015-03-06 11:22:38 +01:00
Dave Halter
eb146adcc1 Modules that are not importable shouldn't cause Jedi to stop working (just issue a warning). Fixes #468, #71. 2015-03-06 11:13:04 +01:00
farhad
32081bd156 Merge branch 'dev' into unicode_tokenize_fix2
Conflicts:
	AUTHORS.txt
2015-03-06 12:14:38 +04:00
farhad
f9c104348e added myself to AUTHORS.txt 2015-03-06 11:55:16 +04:00
farhad
80719fc821 added test for quoted strings parsing 2015-03-06 11:54:01 +04:00
farhad
3747b009bf fix tokenization of code containing unicode strings 2015-03-06 09:11:35 +04:00
Dave Halter
910f2e6486 Use textwrap.dedent for better readability of the testing code. 2015-03-06 01:49:57 +01:00
Dave Halter
fd1be02f1e Test for unicode tokens in Python 2.7. 2015-03-06 01:47:37 +01:00
Dave Halter
a6c5d9f0a6 Merge branch 'add-egg-links-to-syspath-on-parser' of https://github.com/blueyed/jedi into dev 2015-03-06 01:06:17 +01:00
Dave Halter
0b531d2b17 print in Python 2 shouldn't be a function, it's a keyword (without the future import). 2015-03-06 01:01:20 +01:00
Dave Halter
b036c88b73 True in Python 2 is still not a keyword, but a name. 2015-03-06 00:42:57 +01:00
Dave Halter
a0f8b58e71 Fix a Python 2.7 compatibility issue. 2015-03-06 00:37:41 +01:00
Dave Halter
468ff59c1c Remove hasattr/next from _compatibility (not used anymore), thanks @dongweiming for noticing. 2015-03-06 00:25:42 +01:00
Dave Halter
10df0f933f Remove the strange check in the parser to always create expr_stmt and file_input. 2015-03-05 15:30:07 +01:00
Dave Halter
8f58258f4d Writing a different Name.get_definition() implementation, returns the node, if there's no expr_stmt parent. 2015-03-05 15:17:08 +01:00
Dave Halter
0ceadf69a3 Fake objects don't need an ExprStmt for the docstring anymore. 2015-03-05 14:24:19 +01:00
Dave Halter
76588aa040 Static analysis issues resolved (that were cause by the removal of using ExprStmt for every node). 2015-03-05 14:18:10 +01:00
Dave Halter
e698e6aeeb Rework some of the analysis statement gathering. 2015-03-05 13:36:41 +01:00
Dave Halter
b489019f5b Most integration tests (except 2) pass if we don't always make the use of an ExprStmt. 2015-03-05 01:55:25 +01:00
Dave Halter
5d54922c4b Docstring change, to make non ExprStmt statements possible. 2015-03-05 01:37:47 +01:00
Dave Halter
ec7a609e44 Remove some unnecessary code in dynamic.py 2015-03-05 01:13:43 +01:00
Dave Halter
f273e314b6 Preparing for an eventual replacement of using expr_stmt for all nodes. 2015-03-05 00:07:50 +01:00
Dave Halter
aea38ca9aa Remove the classify function in the parser. This could make Jedi a tiny bit faster. 2015-03-04 17:15:33 +01:00
Dave Halter
9c2e73d460 Add syntax errors to the parser. 2015-03-04 17:12:51 +01:00
Dave Halter
a3c2108ecf Fix and test CallSignature.bracket_start. 2015-03-04 12:15:43 +01:00
Dave Halter
1ce96f2581 More fixes for ExprStmt docstrings. 2015-03-03 18:08:24 +01:00
Dave Halter
40e61fc96d Fix ExprStmt docstring bugs. 2015-03-03 17:42:49 +01:00
Dave Halter
ff0c7e27d3 Comment for two commits earlier. 2015-03-03 13:00:32 +01:00
Dave Halter
5cc5505185 Moved comprehension tests out of basic tests into its own file. 2015-03-03 12:58:52 +01:00
Dave Halter
96add84459 Fix a very complicated issue with comprehensions. 2015-03-03 12:56:48 +01:00
Dave Halter
1520ebf557 Fixed an issue with ArrayInstances that were using name lookups, which it doesn't have. 2015-03-03 02:39:02 +01:00
Dave Halter
5322c4a965 decorator dotted_names goto lookups. 2015-03-02 14:31:12 +01:00
Dave Halter
5a845e4dea Fix a decorator goto issue. 2015-03-02 13:23:26 +01:00
Dave Halter
6d3bb5c4b1 Fix generator comprehensions issue when used as an argument. 2015-03-02 03:06:58 +01:00
Dave Halter
2b1ddb19c9 Need py__bool__ on generators as well as any other object. 2015-02-27 12:36:03 +01:00
Dave Halter
23fe08363d Simplify cache_call_signatures. 2015-02-27 12:20:55 +01:00
Dave Halter
ea8209d45e Call signatures should not fail when used on if(. 2015-02-27 12:17:44 +01:00
Dave Halter
53490991d7 Goto_definitions bug fix -> imports stuff. 2015-02-27 11:56:36 +01:00
Dave Halter
1bc9ac1c00 Goto bug fix. 2015-02-27 11:37:49 +01:00
Dave Halter
610068dde4 Fix merged array values. 2015-02-27 11:23:53 +01:00
Dave Halter
a5728f8767 list comprehensions should be completeable. 2015-02-27 11:14:08 +01:00
Dave Halter
f5dad437dd Get rid of the None default for memoize_default. It shouldn't have a default if not given. This also uncovered a bug in for/else loops, that wasn't teste before. 2015-02-27 01:42:14 +01:00
Dave Halter
a998c36fa3 Fix an attribute error in static analysis code. 2015-02-26 14:40:33 +01:00
Dave Halter
9b4385fb24 Merge branch 'dev' of github.com:davidhalter/jedi into dev 2015-02-26 13:59:30 +01:00
Dave Halter
b8a8c4d402 Fix an array lookup issue. list.pop calls work now pretty well and return the right type. 2015-02-26 13:57:54 +01:00
Dave Halter
d318d3c855 Fix a potential issue in sys path searching. However not tested. This is something that raised an error with sith that was not reproducible. 2015-02-26 13:56:28 +01:00
Dave Halter
d7b69ab92c Fix a small bug in the logic of finding self variables. 2015-02-25 13:54:13 +01:00
Dave Halter
30efdc5e4e Because we replaced and simplified strings in the last commits (including string ERRORTOKENs), we are now able to remove an error recovery in the backwards tokenizer. 2015-02-25 13:34:12 +01:00
Dave Halter
8c08a4e574 Call signatures again: function definitions and other things that cannot be a part of call signatures stop the process of scanning for them. Also strings get replaced and simplified. 2015-02-25 13:33:09 +01:00
Dave Halter
48392a7dac Fix some issues in call signatures. 2015-02-24 16:55:33 +01:00
Dave Halter
b8386d29d5 Whitespace before brackets should still show call signatures. 2015-02-24 01:48:25 +01:00
Dave Halter
0ae74a7666 Replace a __bases__ call with an __mro__ call, because the latter is closer to how Python actually works. __bases__ is never used. 2015-02-23 19:07:23 +01:00
Dave Halter
4f2d4992da Fix an mro resolution issue. 2015-02-23 19:04:35 +01:00
Dave Halter
a91e240c8b ALWAYS_BREAK_TOKEN -> ALWAYS_BREAK_TOKENS 2015-02-23 14:10:29 +01:00
Dave Halter
aebeafccc4 Rewrite last newlines in the fast parser to get correct get_code outputs even with the fast parser. 2015-02-23 13:36:43 +01:00
Dave Halter
489ea8fc83 Replace set_parser with direct ParserNode instance calls. 2015-02-23 13:10:40 +01:00
Dave Halter
2fcb1b9b65 Fast parser fix. 2015-02-23 01:00:17 +01:00
Dave Halter
69412224eb Merge pull request #550 from IanLee1521/issue-525
Point docs at readthedocs.org rather than jedidjah.ch
2015-02-23 00:17:46 +01:00
Ian Lee
49150d760e Fixed #525 - Point to readthedocs.org rather than jedidjah.ch 2015-02-22 14:29:31 -08:00
Dave Halter
3a5b2d396e Failed statements should not lead to parser fails. 2015-02-22 20:29:22 +01:00
Dave Halter
3ec96b25cc Issue with backslashes again in the fast parser. 2015-02-21 18:07:21 +01:00
Dave Halter
3347718808 Merge pull request #549 from IanLee1521/readme-update
Readme update
2015-02-21 10:37:26 +01:00
Ian Lee
5625e1cb62 Add self to authors list 2015-02-20 17:17:34 -08:00
Ian Lee
2b193cb1f0 Update list of supported cPython versions in readme 2015-02-20 17:14:00 -08:00
Dave Halter
0b5a509e83 Small correction: mixed up a re.match and re.search. 2015-02-20 00:48:05 +01:00
Dave Halter
ce96af5e04 Fix an issue with open parentheses and function definitions right after. The fast parser should behave like the normal one and just ignore the open brackets. 2015-02-19 11:02:11 +01:00
Dave Halter
9d048623dd Delete the old and unused MultiLevelStopIteration exception. 2015-02-19 01:43:43 +01:00
Dave Halter
0e73bf7d80 Account for code parts that were not parsed in the fast parser. 2015-02-19 01:42:13 +01:00
Dave Halter
39bf9f426b Handle backslash escaping. 2015-02-18 17:32:34 +01:00
Dave Halter
595da50ab8 The fast parser splitting now also checks for parentheses levels, because without that, sometimes we split in very strange positions, while ignoring others. 2015-02-18 13:49:03 +01:00
Dave Halter
38e26892f2 The fast parser doesn't work with open parentheses properly, document that. 2015-02-18 12:50:26 +01:00
Dave Halter
cefd76e5d1 Testing open parentheses in the fast parser. 2015-02-17 17:26:00 +01:00
Dave Halter
506d602795 Fix multi line param issues in the fast parser. 2015-02-17 15:24:49 +01:00
Dave Halter
7663703989 Fix issues with multi line for loops in the fast parser. 2015-02-17 14:57:00 +01:00
Dave Halter
4d9608ea6f Check more precisely for flow keywords. 2015-02-16 16:04:48 +01:00
Dave Halter
e1c28d2c3f variables starting with 'class' and 'def' should not slow down the parser, changed the check to 'class ' and 'def '. 2015-02-16 10:08:22 +01:00
Dave Halter
3680784234 Add another for in one line test for the fast parser. 2015-02-15 20:28:59 +01:00
Dave Halter
db31e0e37d The fast parser works now faster in case of for flows with a simple_stmt after. 2015-02-14 18:57:04 +01:00
Dave Halter
a3b32729a7 Test for an issue with for loops and a statement on the same line. (fast parser) 2015-02-14 16:27:04 +01:00
Dave Halter
4613a810a5 Some small refactorings to the names_dict/deep_ast_copy logic. 2015-02-12 13:24:08 +01:00
Dave Halter
774b3d5ce8 Python 2 compatibility. 2015-02-12 11:36:36 +01:00
Dave Halter
a8d3a9ab42 Remove old deep_ast_copy code. 2015-02-12 11:24:17 +01:00
Dave Halter
bcf6be0636 Radically rewrote deep_ast_copy. 2015-02-12 02:25:54 +01:00
Dave Halter
a12f259a0f Actually remove check_first from deep_ast_copy. 2015-02-11 14:47:28 +01:00
Dave Halter
315c687048 Remove the need for the check_first param in deep_ast_copy. 2015-02-11 14:46:51 +01:00
Dave Halter
bc722a70f2 Simplify deep_ast_copy. 2015-02-11 02:16:57 +01:00
Dave Halter
6e5ba3de87 Fix remaining issue siwh the Param refactoring. 2015-02-11 01:40:18 +01:00
Dave Halter
cdbe26786a Trying to get ird of the weird param generation in the parser tree. 2015-02-10 15:49:26 +01:00
Dave Halter
8775d90173 Merge the master branch into the dev branch. 2015-02-09 14:41:41 +01:00
Dave Halter
07156b427c Fix some compatibilty issues in the test suite for Python 2.7. 2015-02-09 14:15:25 +01:00
Dave Halter
28d3ba6c04 Fix a test about regex goto, don't know how that one even worked in the first place. 2015-02-09 12:35:20 +01:00
Dave Halter
a095f8d9e0 Replace some isinstance checks in the parser tree with .type checks. 2015-02-09 12:27:29 +01:00
Dave Halter
a9a3387cb0 Refactor user scope search. 2015-02-05 21:52:57 +01:00
Dave Halter
8125d5f562 Remove asserts and calculate them dynamically. 2015-02-05 20:16:55 +01:00
Dave Halter
0a3797cf6e Small refactorings. 2015-02-05 19:47:26 +01:00
Dave Halter
2dd08594fc Simplify the indent calculation in the fast parser. 2015-02-05 14:37:24 +01:00
Dave Halter
abe6c8934c Update the parser pickling protocol version. 2015-02-05 14:19:22 +01:00
Dave Halter
d0f1fd5267 Rename Simple -> BaseNode. 2015-02-05 14:18:30 +01:00
Dave Halter
0c1bbf78e2 Rename SubModule to Module, because that's a more fitting description. There were reasons for the name before the new fast parser, but those don't exist anymore. 2015-02-05 14:16:43 +01:00
Dave Halter
c689573b0b Removed the line_offset from tokenize, we have better ways to modify positions, now. 2015-02-05 14:00:58 +01:00
Dave Halter
59cf1bce5d Delete legacy code from the fast parser. 2015-02-05 13:47:35 +01:00
Dave Halter
4ace58e29e Make get_statement_for_position faster. 2015-02-05 13:35:43 +01:00
Dave Halter
a77ecdbed6 Remove param from get_statement_for_position. 2015-02-05 12:28:55 +01:00
Dave Halter
2d9c644ab6 Fixed some minor mocking differences in Python 2 and 3. 2015-02-05 01:25:53 +01:00
Dave Halter
109fdc53e0 Fix the remaining fast parser issues. 2015-02-05 01:13:00 +01:00
Dave Halter
b57ee880af Remove assertEqual from tokenize tests, we can do it with just assert, py.test converts all of that automatically. 2015-02-05 00:48:40 +01:00
Dave Halter
fdfe17ada5 Import the token IDs directly, this way we minimize lookups. 2015-02-05 00:44:01 +01:00
Dave Halter
c6b818c504 Changed a tokenize test to match the current intended behavior of the tokenizer. 2015-02-05 00:43:25 +01:00
Dave Halter
3a4235eb33 The interpreter is not using the fast parser anymore. 2015-02-05 00:28:54 +01:00
Dave Halter
dce952aec6 Fix an issue with omited dedents in the parser. 2015-02-05 00:11:12 +01:00
Dave Halter
e1c623d3f3 Python 2 compatibility. 2015-02-04 17:09:18 +01:00
Dave Halter
e23e354fe8 Simplified the line splitting and with that a few other things in the fast parser. 2015-02-03 22:22:57 +01:00
Dave Halter
66dfa59286 Fix some endmarker prefix issues in the fast parser. 2015-02-03 22:09:55 +01:00
Dave Halter
6cdfecb541 Fix a number of issues in the fast parser around functions with only one statement (no suite) and wrong indentations). 2015-02-02 15:03:57 +01:00
Dave Halter
f9fe6b47eb Fix error statement stacks positions. 2015-02-02 10:43:47 +01:00
Dave Halter
a4bd412801 Fix an issue with the positions of InstanceNames that used the original position_modifier. 2015-02-02 02:29:39 +01:00
Dave Halter
c58cdbbf9b Fix an issue that comes from a combination of property/__slots__/pickle 2015-02-02 00:45:17 +01:00
Dave Halter
e913872192 Merged the tokenize is_identifier changes. 2015-02-01 20:32:01 +01:00
Dave Halter
9a0f1363e3 Start removing the print statements that were used for debugging. 2015-02-01 02:32:52 +01:00
Dave Halter
bc118e8047 Simplify the fast parser tokenizer more. Now it is more readable and less buggy (+bugfixes). 2015-01-31 20:09:44 +01:00
Dave Halter
1826f432c8 Fix an issue in the fast parser splitting. 2015-01-30 15:17:38 +01:00
Dave Halter
413da3b790 Remove the line_offset calculation. We can now also remove it from tokenize. With the position_modifier we have enough tools to change a position, we don't need to do that in tokenize.py. 2015-01-29 17:57:01 +01:00
Dave Halter
a3cdec819e Fix the prefix in tokenize, which was the wrong way around. 2015-01-29 17:10:00 +01:00
Dave Halter
cf1b2ff54b Function tests now pass with the fast parser. 2015-01-29 15:47:38 +01:00
Dave Halter
a221eee02c Fix more issues in the fast parser. 2015-01-29 15:38:38 +01:00
Dave Halter
0a537c05c4 Fix an issue with Function/Flow combination in the fast parser. 2015-01-29 02:24:11 +01:00
Dave Halter
dde0e9c7c6 Fix for loop issues in the fast parser. 2015-01-29 01:36:16 +01:00
Dave Halter
e412694fa2 Fix issues with flows in the fast parser. 2015-01-28 17:06:18 +01:00
Dave Halter
b8c63f366c FastModule seems to be compatible now with the normal Module, because it inherits from it and makes some minor modifications in some cases. 2015-01-28 15:11:53 +01:00
Dave Halter
c7563470b1 We don't need set_global_names, just set the attribute directly. 2015-01-28 15:00:17 +01:00
Dave Halter
d0589430bb FastModule should inherit from SubModule, because it has almost all the same properties. 2015-01-28 14:59:00 +01:00
Dave Halter
6ec89e6785 Fix issues with flows. 2015-01-28 13:03:57 +01:00
Dave Halter
5e8f8f7a8d Fix issues with error correction / newline correction. 2015-01-27 12:24:54 +01:00
Dave Halter
62e45aa42b Fix issues with the new newline end_pos positions. 2015-01-27 02:21:05 +01:00
Dave Halter
4a07f97f10 Reenable a few get_code tests. 2015-01-27 01:19:09 +01:00
Dave Halter
88a3e25814 Fix newline stuff for empty parsers. 2015-01-27 01:15:39 +01:00
Dave Halter
39e869d146 Test added newline module end_pos as well. 2015-01-26 22:02:11 +01:00
Dave Halter
cdae250b36 code -> source and also care for added newlines in the fast parser. 2015-01-26 22:01:39 +01:00
Dave Halter
07c60d7ff6 Fix DEDENT issues in _remove_newline. 2015-01-26 21:17:50 +01:00
Dave Halter
61e2bba380 Tests and implementation to remove the last newline again in the parser tree, to be able to exactly reproduce the parser input. 2015-01-26 21:07:14 +01:00
Dave Halter
e5d265e845 Add a method Leaf.get_previous, to get previous leafs. 2015-01-26 21:02:56 +01:00
Daniel Hahler
8621aae73c Add any .egg-link paths from VIRTUAL_ENV to sys.path
Adding test_get_sys_path required factoring out
`_get_venv_sitepackages`, because `sys.version_info` cannot be mocked
apparently.
2015-01-25 21:35:09 +01:00
Dave Halter
a8943b8a80 Get the position modifiers right. 2015-01-24 20:42:28 +01:00
Dave Halter
446f5b9018 Fix issues with the right count of parsers used. 2015-01-24 20:19:03 +01:00
Dave Halter
4d6afd3c99 Fix fast parser tests. 2015-01-24 00:06:16 +01:00
Dave Halter
8569651bf4 Fast parser simplifications and bug fixes. 2015-01-21 18:34:22 +01:00
Dave Halter
91ab1d0ecd Fix an issue in the fast parser that caused stuff to be parsed always. 2015-01-21 02:03:06 +01:00
Dave Halter
7188105dc7 The fast parser is now in a more readable shape. 2015-01-19 16:21:25 +01:00
Dave Halter
ce793b1066 Trying to restructure the fast parser. 2015-01-19 14:49:44 +01:00
Dave Halter
d6b3b76d26 First fast parser version that actually let a test pass. 2015-01-19 00:39:51 +01:00
Dave Halter
add0cafbf1 Merge pull request #530 from felipeacsi/arch-installation
Updated Arch Linux installation
2015-01-17 13:50:49 +01:00
felipeacsi
f348aaeab6 Updated Arch Linux installation 2015-01-16 14:38:39 -03:00
Dave Halter
01c209dc00 MergedNamesDicts for the parser. 2015-01-16 15:25:58 +01:00
Dave Halter
e477fab856 Playing with the fast parser implementation. 2015-01-16 15:23:49 +01:00
Dave Halter
86391268a7 Merge pull request #528 from KenetJervet/parser
Fixed issue #526.
2015-01-16 13:03:56 +01:00
Savor d'Isavano
c3c07c4ec2 Fixed issue #526. 2015-01-16 18:45:34 +08:00
Dave Halter
cc7483498c Start using the position modifier. 2015-01-15 14:18:22 +01:00
Dave Halter
cf223a71f5 Add a position modifier for the fast parser. Not yet in use though. 2015-01-15 13:57:56 +01:00
Dave Halter
c963706418 Delete legacy logic. 2015-01-15 02:19:48 +01:00
Dave Halter
e82d51e161 Correct a path in memory check. 2015-01-15 02:03:26 +01:00
Dave Halter
95b518e9fc Use the Python 3.4 parser for docstring types.
We had to switch, because Ellipsis was otherwise not parseable.
2015-01-13 13:17:21 +01:00
Dave Halter
e6b9111749 Python 2.7 compatibility. 2015-01-13 02:12:49 +01:00
Dave Halter
cc64265187 Grammar modifications so that the Python2.7 grammar looks more like the Python 3.4 grammar. 2015-01-13 01:05:13 +01:00
Dave Halter
09da6ec0d3 Function annotations don't need to be tested in Python 2.7. 2015-01-13 01:00:08 +01:00
Dave Halter
f59e05f8e7 Switch grammars depending on Python version. 2015-01-12 13:33:44 +01:00
Dave Halter
582b9b01af Get invalid INDENTs working.
The following DEDENT's are removed.
2015-01-12 12:22:57 +01:00
Dave Halter
ef72f4fb6c Test the new error correction feature. 2015-01-12 01:27:25 +01:00
Dave Halter
5c98f6cf04 Suites don't have to contain statements anymore, this makes autocompletion better in certain cases. 2015-01-12 01:11:46 +01:00
Dave Halter
f8570b1f03 Test for error recovery with try statements. 2015-01-09 18:02:15 +01:00
Dave Halter
5334f8dbad Implemented the in operator in a very simple fashion: It returns nothing. 2015-01-09 16:05:09 +01:00
Dave Halter
53b456dff2 Cleaning up. 2015-01-09 01:55:23 +01:00
Dave Halter
e8ef3b8ad4 Remove legacy code. 2015-01-09 01:45:09 +01:00
Dave Halter
d78a89df51 Move filter_after_position. 2015-01-09 01:37:42 +01:00
Dave Halter
26ecb16e5f CompiledObject.type resembles now the Node.type values. 2015-01-09 01:33:59 +01:00
Dave Halter
b75ba1e16c interpreter documentation. 2015-01-08 18:34:55 +01:00
Dave Halter
81c4792349 Simplify the interpreter completion. 2015-01-08 18:30:49 +01:00
Dave Halter
ed7500bfaa Delete deprecations from 0.6.0 and 0.5.0. 2015-01-08 18:22:38 +01:00
Dave Halter
7c6a6006fd Delete commented code. 2015-01-08 18:19:54 +01:00
Dave Halter
301b4ca649 Deprecate NotFoundError, because it wasn't used anymore. 2015-01-08 18:17:37 +01:00
Dave Halter
8ec8a74a3f Removed base in completions 2015-01-08 18:02:55 +01:00
Dave Halter
108cab21f4 Added a closure test that would have failed before the names_dict refactoring. 2015-01-08 17:58:24 +01:00
Dave Halter
144c20579b Get rid of get_defined_names in compiled modules. 2015-01-08 17:53:20 +01:00
Dave Halter
bd304d33c7 Get rid of Function's get_magic_function_X, they are not used anymore. 2015-01-08 14:17:33 +01:00
Dave Halter
47fc3cbdfe Functions are not exceptions anymore in the name finder. 2015-01-08 14:14:01 +01:00
Dave Halter
0dc61292b9 Remove get_defined_names methods from evaluate representation objects. 2015-01-08 13:42:52 +01:00
Dave Halter
6d58fed0e8 Remove get_defined_names in favor of names_dict in the parser tree. 2015-01-08 13:38:03 +01:00
Dave Halter
a20fd12de9 Remove all scope_names_generator usages. 2015-01-08 13:24:01 +01:00
Dave Halter
af20eff943 Get completely rid of get_names_of_scope. 2015-01-08 13:19:42 +01:00
Dave Halter
705b569e32 Get rid of all get_names_of_scope calls. 2015-01-08 12:48:57 +01:00
Dave Halter
05a9f19429 Delete more legacy code. 2015-01-08 02:43:13 +01:00
Dave Halter
7891cdfd48 Start deleting legacy code. 2015-01-08 02:33:35 +01:00
Dave Halter
82d8e45a1c Fix descriptors. 2015-01-08 02:29:33 +01:00
Dave Halter
83a94c12c9 Correct global name issues. 2015-01-08 01:20:53 +01:00
Dave Halter
f5e687bc22 Use names_dicts now for all completions. 2015-01-07 23:49:13 +01:00
Dave Halter
dd40991669 filtering private variables is now also possible for CompiledObject (important for fake/builtins.pym). 2015-01-07 15:09:03 +01:00
Dave Halter
c451c0b29e Private variable filtering improved. 2015-01-07 14:44:19 +01:00
Dave Halter
987121ae5c Filter names in a separate function so that it can be used for both completion and name lookups. 2015-01-07 13:56:35 +01:00
Dave Halter
ec76d57679 Start using names_dicts for completion as well. 2015-01-07 01:49:38 +01:00
Dave Halter
494a3e3307 Fix usages. 2015-01-06 16:54:01 +01:00
Dave Halter
9178d314b0 Add search_global to names_dicts calls. 2015-01-06 15:30:59 +01:00
Dave Halter
b982b746e7 Fix problems with += stmts. 2015-01-06 11:24:13 +01:00
Dave Halter
8bad12522a Fix issues with module attributes 2015-01-06 01:12:55 +01:00
Dave Halter
7abdbb563c Fix list comprehensions 2015-01-06 00:24:11 +01:00
Dave Halter
54fcf7af9d Fix goto. 2015-01-05 23:55:38 +01:00
Dave Halter
65b33013e5 Few small issues. 2015-01-05 23:31:32 +01:00
Dave Halter
9cd8fabf2c Fix issues with generators. 2015-01-05 19:11:09 +01:00
Dave Halter
91710e0310 Versions should be PEP440 compatible, fixes #521. 2015-01-05 13:15:34 +01:00
Dave Halter
1d2704fb68 Descriptors work with names_dicts now. 2015-01-03 11:07:38 +01:00
Dave Halter
177dcf0c0d Merge pull request #522 from msabramo/add_python_3.4_classifier
setup.py: Add python3.4 classifier
2015-01-02 22:38:33 +01:00
Marc Abramowitz
672982a2f5 setup.py: Add python3.4 classifier 2015-01-02 11:07:01 -08:00
Dave Halter
36819b3241 Filtering private variables seems to be working now at least in the evaluation engine. 2015-01-02 01:50:14 +01:00
Dave Halter
8157dd2da8 Fix most instance related issues. 2015-01-02 01:12:14 +01:00
Dave Halter
0478ff907f names_dicts for instances. 2015-01-02 00:19:07 +01:00
Dave Halter
9de4a5479c Start using names_dicts instead of scope_names_generator. 2015-01-01 23:27:03 +01:00
Dave Halter
ed3cf5577e Compiled objects should also have a names_dict. 2014-12-26 12:49:40 +01:00
Dave Halter
bfaef9815c Remove _gen_param_name_copy, because it's not useful anymore. 2014-12-19 12:51:56 +01:00
Dave Halter
e22aed9ef4 Restructure ExecutedParam so that it works better with generated instances. 2014-12-19 12:42:09 +01:00
Dave Halter
4a08335fd8 Simplify ExecutedParam. 2014-12-19 01:21:00 +01:00
Dave Halter
b802e97c18 Delete legacy code from params. 2014-12-19 01:11:14 +01:00
Dave Halter
da582117ac Array.type docstring. 2014-12-19 01:07:51 +01:00
Dave Halter
47615ae786 Remove pr.Array.type identifiers. 2014-12-19 01:05:52 +01:00
Dave Halter
98eb4a71a1 Clean up the parser tree. 2014-12-18 03:38:24 +01:00
Dave Halter
ab9571bccd Remove FakeStatement 2014-12-18 03:24:12 +01:00
Dave Halter
64ebfb0644 Usages/imports cleanup. 2014-12-18 03:22:46 +01:00
Dave Halter
1fb13837c4 Fix import completion issues. 2014-12-18 02:55:03 +01:00
Dave Halter
f8cd3c661a Fix slots in the parser tree. 2014-12-17 20:36:17 +01:00
Dave Halter
b2e54ca1eb The tokenizer now includes all newlines and comments in its prefix. 2014-12-17 20:11:42 +01:00
Dave Halter
9cdf6de206 More positioning for backwards tokenizer. 2014-12-17 17:51:12 +01:00
Dave Halter
d918f8be73 Give the backwards tokenizer a better structure and comments. 2014-12-17 17:30:00 +01:00
Dave Halter
f164dd8892 Fix some newline issues in the backwards tokenizer. 2014-12-17 14:56:46 +01:00
Dave Halter
6eb2af301d Simplifying reversed line generation of user_context. 2014-12-17 14:04:54 +01:00
Dave Halter
62609cb6f1 Resolve tox issues. 2014-12-17 14:01:00 +01:00
Dave Halter
c6315e0b45 todo updates. 2014-12-17 01:48:32 +01:00
Dave Halter
0147a7f68d usages cleanup 2014-12-17 01:44:06 +01:00
Dave Halter
4897791901 Remove old precedence stuff. 2014-12-16 18:28:45 +01:00
Dave Halter
7f95a9806a api cleanup. 2014-12-16 18:13:49 +01:00
Dave Halter
5730e5add0 parser tree docstring updates. 2014-12-16 18:10:28 +01:00
Dave Halter
f702a91813 iterable cleanup. 2014-12-16 17:45:01 +01:00
Dave Halter
580dcb06ff Clean up the dynamic module. 2014-12-16 17:39:50 +01:00
Dave Halter
576a1182af Remove legacy code from param. However, this there's still work needed on params. 2014-12-16 17:37:20 +01:00
Dave Halter
3d080afd71 Cleanup finder. 2014-12-16 17:31:28 +01:00
Dave Halter
869b0b4189 Cleaning up api classes. 2014-12-16 17:23:59 +01:00
Dave Halter
237f0e526c Cleaning up evaluate.helpers. 2014-12-16 17:19:14 +01:00
Dave Halter
6821ccba91 Add the pgen2 packages and grammar files to be able to deploy Jedi. 2014-12-16 15:23:49 +01:00
Dave Halter
e53e211325 Python 2 compatibility in fake module. 2014-12-16 02:07:20 +01:00
Dave Halter
d5e3a09c44 Python 2 compatibility with the new tokens. 2014-12-16 02:03:05 +01:00
Dave Halter
fd1cb86765 Now able to remove both tokenize and token from pgen2. 2014-12-16 02:00:33 +01:00
Dave Halter
d9d3740c92 Trying to replace the old pgen2 token module with a token module more tightly coupled to the standard library. 2014-12-16 01:52:15 +01:00
Dave Halter
eaace104dd Replace the tokenizer's output with a tuple (switching back from a Token class). 2014-12-16 00:10:07 +01:00
Dave Halter
680fdd574b Remove some old unused tokenize stuff. 2014-12-15 17:44:40 +01:00
Dave Halter
955f125c0d Trying to remove token from pgen2. 2014-12-15 17:36:15 +01:00
Dave Halter
491b4ad76d Pgen2 license amendments. 2014-12-15 17:29:32 +01:00
Dave Halter
b911a39fb4 The driver file is now empty. 2014-12-15 17:27:27 +01:00
Dave Halter
55a6dbc8a2 Remove the old driver code of pgen2. 2014-12-15 17:18:01 +01:00
Dave Halter
4e0172a915 Partial parser.__init__' cleanup. 2014-12-15 16:21:35 +01:00
Dave Halter
af303e10c8 Statement -> ExprStmt. 2014-12-15 16:18:09 +01:00
Dave Halter
9431d89797 Imports cleanup. 2014-12-15 16:07:43 +01:00
Dave Halter
4af51a9516 Removing legacy code from evaluate/representation. 2014-12-15 16:02:19 +01:00
Dave Halter
b03330c5d7 Updating the docs of evaluate/__init__. 2014-12-15 16:00:16 +01:00
Dave Halter
5f892d62a6 Delete legacy code from evaluate. 2014-12-15 15:34:15 +01:00
Dave Halter
f2d35c3ff1 Reenable star import caching. 2014-12-15 15:19:22 +01:00
Dave Halter
24cfa62c8a documentation 2014-12-15 15:10:44 +01:00
Dave Halter
f0c6e5709c Some temporary args/kwargs related changes to static analysis. 2014-12-15 14:58:16 +01:00
Dave Halter
4a8bbd9583 Restructure dynamic param search, so that it can be cached better. 2014-12-15 13:39:53 +01:00
Dave Halter
70e80a5d1c star argument bug fixes. 2014-12-13 08:37:20 +01:00
Dave Halter
7d9f85c762 invalid star star arguments. 2014-12-13 08:34:03 +01:00
Dave Halter
ddd4d675f6 star args improvements 2014-12-13 08:17:38 +01:00
Dave Halter
1b48f6fbce Fix static analysis' argument tests. 2014-12-13 07:33:03 +01:00
Dave Halter
a4c454c103 Fix for unwanted NameError exception in static analysis with named params. 2014-12-12 14:52:34 +01:00
Dave Halter
a762e0bcec Fix a potential issue with star args. 2014-12-12 14:30:42 +01:00
Dave Halter
e8cc8f0a83 Get hasattr checks completely working 2014-12-12 02:34:25 +01:00
Dave Halter
8eaa008b5f Fix try/except checks in static analysis. 2014-12-12 02:26:16 +01:00
Dave Halter
c3106c10ef Fix flow's AttributeError detection. 2014-12-11 19:26:49 +01:00
Dave Halter
d11ea73ef4 Re-enable AttributeError/NameError detection for more complicated occurances than just statements. 2014-12-11 19:18:00 +01:00
Dave Halter
77fdbac234 static analysis: Import tests working again. 2014-12-11 16:25:18 +01:00
Dave Halter
6818d3affa Implement Import.is_nested method. 2014-12-11 16:17:07 +01:00
Dave Halter
6406bfb3c2 First static analysis test working. 2014-12-11 15:42:16 +01:00
Dave Halter
6afc5ccca5 Few docstring fixes. 2014-12-11 15:32:45 +01:00
Dave Halter
003d1249c5 empty import statement completion. 2014-12-11 15:24:19 +01:00
Dave Halter
bf8645d615 namedtuple fix 2014-12-11 13:08:09 +01:00
Dave Halter
d6b2a64343 Some small import completion fixes. 2014-12-11 13:00:57 +01:00
Dave Halter
c4c3ef5a21 goto_definition on a name definition (statement) should land on the statement. 2014-12-11 12:48:23 +01:00
Dave Halter
d8067a7286 Small test corrections. 2014-12-11 04:44:27 +01:00
Dave Halter
2dd8ed2270 Fix interpreter stuff, fix slicing with CompiledObject and a few other things. 2014-12-11 04:24:50 +01:00
Dave Halter
4aac363413 Some changes to the interpreter completions. 2014-12-11 03:49:05 +01:00
Dave Halter
220610bbf4 Importer now handles follow rest as well. 2014-12-11 02:28:55 +01:00
Dave Halter
48d2e99e55 os.path handling. 2014-12-11 01:49:59 +01:00
Dave Halter
ef0958a43c Temporarily change the behavior of a defined names test. Hard to say how we really want it to behave. 2014-12-11 00:44:31 +01:00
Dave Halter
d0ade9b2e9 Set a new version number: 0.9.0-alpha0. 2014-12-11 00:42:34 +01:00
Dave Halter
bb7bbf51ec Deprecate jedi.defined_names in favor of jedi.names. 2014-12-11 00:41:36 +01:00
Dave Halter
243fb8ef34 Small import fix. 2014-12-11 00:14:03 +01:00
Dave Halter
23417f0288 Fix docstrings in fake/skeleton objects. 2014-12-11 00:05:49 +01:00
Dave Halter
95620accdb Fix tests for namespace packages. 2014-12-10 19:19:13 +01:00
Dave Halter
897c4cded6 Fix issues with sys.path modifications that directly assign the sys.path or use the slicing notation. 2014-12-10 19:18:53 +01:00
Dave Halter
5af665abd8 Dynamic array checking in combination with FakeSequences might have caused an exception. 2014-12-10 11:42:02 +01:00
Dave Halter
4bef8895a0 Fix dynamic arrays: They work in instances, now. 2014-12-10 11:34:11 +01:00
Dave Halter
d4dfcfe321 NameFinder refactoring to make it possible to cache names_to_types. 2014-12-10 11:23:12 +01:00
Dave Halter
2536dede28 Check for recursions in dynamic arrays. 2014-12-10 02:02:55 +01:00
Dave Halter
e429144979 Fix some stuff list.append stuff combined with functions executions. 2014-12-10 01:58:04 +01:00
Dave Halter
5ed914ea21 dynamic array improvements. 2014-12-08 20:18:33 +01:00
Dave Halter
1c44336d60 First array addition working. 2014-12-08 18:25:38 +01:00
Dave Halter
4c3584ed3c Removed the dynamic_arrays_for_instances setting, because it's a subset of dynamic_array_additions, which is more concise. 2014-12-08 16:36:37 +01:00
Dave Halter
936a3c9dfe Small cleanup: Removed a few print statements. 2014-12-08 16:03:23 +01:00
Dave Halter
51d309b0a8 Moved keyword completion around to get it working in all cases. 2014-12-08 15:52:05 +01:00
Dave Halter
94ea2c1096 Issues with argument clinic parser. 2014-12-08 15:45:40 +01:00
Dave Halter
01b9361b33 Reenable keyword completion. 2014-12-08 15:14:27 +01:00
Dave Halter
5cc9dd57a6 Finally fixed the last on_import issue, which was that goto was not working on incomplete import statements. Still a bit messy, though. 2014-12-08 15:02:35 +01:00
Dave Halter
034d782e65 Last few on_import fixes. 2014-12-08 14:15:21 +01:00
Dave Halter
6cc4d71822 Import completion improvements. 2014-12-08 13:47:23 +01:00
Dave Halter
7cc2a07cd3 Small full_name improvements. 2014-12-08 12:38:59 +01:00
Dave Halter
8868b87d42 Make imports stuff in API classes work. Now goto on imports follows even aliases. 2014-12-08 12:04:09 +01:00
Dave Halter
0ad6aeba6b Fix some API classes issues. Among them call signature generation and Definition.parent() issues. 2014-12-08 02:32:43 +01:00
Dave Halter
0f01242954 named param goto. 2014-12-08 01:52:32 +01:00
Dave Halter
6ad9c5ee76 Small fixes to the parser tests. 2014-12-08 00:58:28 +01:00
Dave Halter
dffce937f2 With the old parser we did more complicated checking for invalid statements, now the new parser does it by itself. Therefore we can stop doing crazy regex stuff in the API. 2014-12-08 00:52:40 +01:00
Dave Halter
0c77e9960a NotFoundError doesn't really exist anymore. We're deprecating it, so change the corresponding tests. 2014-12-08 00:48:06 +01:00
Dave Halter
d6595ad020 Fixed more parser tests. 2014-12-08 00:36:09 +01:00
Dave Halter
fe8a99dfd5 Fix the parser tests. 2014-12-08 00:32:38 +01:00
Dave Halter
34c9422749 The parser tests should also give the parser a grammar. 2014-12-08 00:22:33 +01:00
Dave Halter
f0c430e20c On import problem with name completion of modules. 2014-12-08 00:16:01 +01:00
Dave Halter
b24bf29fc2 Fixed named argument call signature stuff and issues with classes and call signature params. 2014-12-07 23:55:44 +01:00
Dave Halter
bb747a83e8 Small fix with big impact for the previously done simple_stmt error recovery. Now it actually works. 2014-12-07 19:45:19 +01:00
Dave Halter
2b7434342e Fix absolute imports. 2014-12-07 18:51:14 +01:00
Dave Halter
eead122636 Use grammar in test scripts. 2014-12-07 18:22:11 +01:00
Dave Halter
6058855dd3 test_helpers doesn't make sense anymore, because those the only test it consisted of, was a test with StatementElement, which does not exist anymore in the new parser. 2014-12-07 18:15:18 +01:00
Dave Halter
e3ab56504e Fixed and simplified flask imports. 2014-12-07 18:11:05 +01:00
Dave Halter
db636c35ae Error recovery should not delete parts of simple_stmt. 2014-12-07 18:04:55 +01:00
Dave Halter
33b39c2b5d Don't use the old setup_function/teardown_function pytest stuff. It's very implicit and hard to understand. 2014-12-07 17:21:52 +01:00
Dave Halter
49b34b4d01 Stuff mostly related to namespace packages. 2014-12-07 16:51:54 +01:00
Dave Halter
528b325c39 Remove precedence tests. They are not needed anymore, since precedence is now handled by the parser itself. 2014-12-07 14:41:57 +01:00
Dave Halter
b94a09f360 Fix end_pos of Literals and Whitespace leafs. 2014-12-07 14:28:40 +01:00
Dave Halter
fe1d7b7030 Replace the old tokenizer tests with the refactored attributes. 2014-12-07 14:19:21 +01:00
Dave Halter
ea4f7053d6 Fix completion/definition.py tests. 2014-12-07 14:13:59 +01:00
Dave Halter
e1e5c3a6c7 Progress with call signatures. 2014-12-07 13:56:40 +01:00
Dave Halter
24903739f2 A first implementation of call signatures. 2014-12-05 16:05:54 +01:00
Dave Halter
ab254bbcba Call signature search progress. 2014-12-05 00:23:59 +01:00
Dave Halter
24c7142810 Fix issues with scope ordering in classes/functions. 2014-12-04 18:49:09 +01:00
Dave Halter
774ade955d Fixing for loop additions. 2014-12-04 17:58:01 +01:00
Dave Halter
a96d1b8d0f fix something with not/- prefixes. 2014-12-04 17:51:14 +01:00
Dave Halter
478acf8ccf partial is working partially now with the new parser, because invalid statements are not possible anymore (two times **kwargs) 2014-12-04 14:29:37 +01:00
Dave Halter
8f1002218d Very temporary solution for doing deep_ast_copy. 2014-12-04 11:19:33 +01:00
Dave Halter
aa9057be38 Small fix for builtins. 2014-12-04 02:01:30 +01:00
Dave Halter
1725abb1fd Fix issues with docstrings. 2014-12-03 20:30:03 +01:00
Dave Halter
f1431cef40 Decorator fixes. 2014-12-03 17:09:30 +01:00
Dave Halter
09ad3411da Goto fixes. 2014-12-03 17:01:29 +01:00
Dave Halter
6314b80abd Some goto refactorings. 2014-12-03 16:52:05 +01:00
Dave Halter
b2267d3878 Fix usages. 2014-12-03 16:34:31 +01:00
Dave Halter
6bf154de5e Better goto for imports, which helps usages. 2014-12-03 16:15:31 +01:00
Dave Halter
536c188192 Change get_self_vars. Now using py__mro__ to avoid recursions. 2014-12-03 13:04:53 +01:00
Dave Halter
b9e7a2eb95 Fix assert issues in combination with comprehensions. 2014-12-02 17:55:42 +01:00
Dave Halter
5f89ceb385 Add the type attribute to all classes in the tree. Because nodes have them as well. 2014-12-02 17:50:55 +01:00
Dave Halter
425741e285 Fix assertion/isinstance type information. 2014-12-02 17:45:19 +01:00
Dave Halter
cf0407e164 Add 'if isinstance' type information. 2014-12-02 17:34:36 +01:00
Dave Halter
99febfe6c2 Fixed a very nasty bug in deep_ast_copy. 2014-12-02 04:19:22 +01:00
Dave Halter
235672efc1 Fix an issue for stdlib regex completion. deep_ast_copy had a bug and also changed the way how decorators work. 2014-12-01 18:09:21 +01:00
Dave Halter
2515d283be __getitem__ in instances. 2014-12-01 15:41:13 +01:00
Dave Halter
0ab9d331f8 Issues with dictionary/list/tuple literal methods. 2014-12-01 15:36:36 +01:00
Dave Halter
e51a393e4c Fix reversed. 2014-12-01 12:41:47 +01:00
Dave Halter
3cc4da28ed Fix lambda_nocond. 2014-12-01 11:56:28 +01:00
Dave Halter
bcd998ae02 Lambdas are own namespaces and deserve their own used_names dictionary in the parser. 2014-12-01 11:49:52 +01:00
Dave Halter
7c9de1fbeb Some class lambdas tests too assure that they are working well with instances. 2014-12-01 11:36:19 +01:00
Dave Halter
50752df6dd Fix an issue with combinations of InstanceElement and Lambdas. 2014-12-01 11:26:35 +01:00
Dave Halter
88853c78f4 Get lambdas mostly working. 2014-12-01 02:47:48 +01:00
Dave Halter
4ee5ad4ce3 iterating list comprehensions should be possible. 2014-12-01 01:08:50 +01:00
Dave Halter
ed1915eea0 Fixes for goto on list comprehensions. 2014-12-01 01:02:41 +01:00
Dave Halter
68bd9160e2 Fixed list comprehension name lookups. 2014-12-01 00:08:27 +01:00
Dave Halter
3928f466cf Fix positioning of the user statements. 2014-11-29 16:20:12 +01:00
Dave Halter
cd7044cae3 Don't use NotFoundError anymore, since it's very ambiguous what that would imply. 2014-11-29 15:57:18 +01:00
Dave Halter
0184e80120 dynamic_params correction. 2014-11-29 13:49:50 +01:00
Dave Halter
417db4e83f suites without indent can also be deleted. 2014-11-29 13:30:21 +01:00
Dave Halter
a7560069b0 Fixes for issues with empty compound_stmt. We always remove a whole stmt and just a funcdef as an error correction. 2014-11-29 13:25:31 +01:00
Dave Halter
3fb1934462 Fix invalid test issues. 2014-11-29 01:35:26 +01:00
Dave Halter
2b912cb75a The func/class dictionaries must be changed if some scopes are removed by the parser's error recovery. 2014-11-29 01:29:21 +01:00
Dave Halter
43c01afcfc invalid.py test changes. Error recovery will be different from the old one. 2014-11-28 21:58:44 +01:00
Dave Halter
2c684906e3 Working with dedents in error recovery. 2014-11-28 21:33:40 +01:00
Dave Halter
31600b9552 classes and functions are new statements and should never get removed by the error recovery. 2014-11-28 02:44:34 +01:00
Dave Halter
128dbd34b6 Check parentheses level in tokenizer. 2014-11-28 02:14:38 +01:00
Dave Halter
e1d6511f2f Trying to move the indent/dedent logic back into the tokenizer. 2014-11-28 02:04:04 +01:00
Dave Halter
97516eb26b The new tokenizer is more or less working now. Indents are calculated as they should 2014-11-27 16:03:58 +01:00
Dave Halter
c0df7003a5 Allow both the old tokenizer and the new one (able to toggle). 2014-11-27 01:12:49 +01:00
Dave Halter
c7862925f5 Small tokenizer changes & tokens now have a prefix attribute instead of preceeding_whitespace. 2014-11-27 01:10:45 +01:00
Dave Halter
02cb1fef95 Rename test_tokenizer to test_tokenize. 2014-11-26 16:16:58 +01:00
Dave Halter
cc1098b93c Fix a few tokenize tests and merge them back together. 2014-11-26 16:09:28 +01:00
Dave Halter
f43c371467 Merge @joel-wright's whitespace tokenizer branch. Thanks! 2014-11-26 15:56:11 +01:00
Dave Halter
427056a22d Change the pgen2 parser and its driver so that it can be accessed easily from the outside. This is a minor change and will allow Jedis tokenizer to work with pgen2. 2014-11-26 15:38:53 +01:00
Dave Halter
cd1e07a532 The now passing on_import tests should not worsen the performance of the other tests. 2014-11-26 03:11:22 +01:00
Dave Halter
f24a3bf997 Fix on_import tests. 2014-11-26 03:07:41 +01:00
Dave Halter
1326a2137d Change the backwards tokenizer that keywords always stop. 2014-11-26 02:32:13 +01:00
Dave Halter
a940c31a86 Improvments to on import completion. 2014-11-26 02:13:24 +01:00
Dave Halter
149b4d8ad5 Import completion on syntactically correct imports. 2014-11-26 01:15:40 +01:00
Dave Halter
499c62df43 Fixes for os.path import 2014-11-25 19:39:14 +01:00
Dave Halter
5d82b11f59 First implementation to be ready to complete corrupt imports. Working ok. 2014-11-25 19:35:27 +01:00
Dave Halter
e72eaf7a59 on import completion preparations. 2014-11-25 15:10:36 +01:00
Dave Halter
52d4aaebbe small fix for docstring parsing. 2014-11-25 15:10:19 +01:00
Dave Halter
5de84afff4 Fix __getitem__ 2014-11-24 02:10:02 +01:00
Dave Halter
fae0a7b0c4 Small fixes for past mistakes. 2014-11-24 01:56:54 +01:00
Dave Halter
db76bbccc5 Trying to change the symbols in node. They are now strings.
With this change we are finally able to get rid of parser/pytree.py
2014-11-24 01:52:41 +01:00
Dave Halter
9f45f18ad1 Added a grammar param to the parser. 2014-11-24 01:10:39 +01:00
Dave Halter
c152a1c58b Actually replace tree with representation (in all the imports). 2014-11-23 19:46:52 +01:00
Dave Halter
1fbc4c9196 Change parser.representation to parser.tree. It's shorter and says more. 2014-11-23 19:35:46 +01:00
Dave Halter
ac41c31015 Removed more of the old parser representation code. 2014-11-23 19:33:18 +01:00
Dave Halter
9b54541cae Remove quite a bit of the old parser representation logic. 2014-11-23 19:26:30 +01:00
Dave Halter
0f21d38e2c Remove the old parser. 2014-11-23 19:17:50 +01:00
Dave Halter
267016f533 Function for evaluating functions with already executed arguments. 2014-11-23 19:12:25 +01:00
Dave Halter
8adfc47297 Fix some issues with params. 2014-11-23 12:22:03 +01:00
Dave Halter
c10ec4f876 star arg iteration improved. 2014-11-23 12:05:19 +01:00
Dave Halter
f1cbd45575 Usages are pretty solid now except for parser issues. 2014-11-22 15:43:23 +01:00
Dave Halter
b82e1e28e5 Get at least some usages stuff right. 2014-11-22 02:05:36 +01:00
Dave Halter
22fbcf6c77 More goto improvements. 2014-11-21 15:45:17 +01:00
Dave Halter
eb0bfb4381 get_code in Definition.description should not return first prefix. 2014-11-21 15:33:38 +01:00
Dave Halter
f604066288 First small implementation of goto. 2014-11-21 14:21:00 +01:00
Dave Halter
fd16dfe2c7 Fix the first part of sys path checks. 2014-11-21 01:46:18 +01:00
Dave Halter
11fa71bac8 Fixes for nested star imports. 2014-11-20 14:56:47 +01:00
Dave Halter
3b7454e294 copy fixes. 2014-11-20 14:51:01 +01:00
Dave Halter
83b09f6c1e Small issue with is_definition and params. Found by looking at stdlib/random.choice tests. 2014-11-20 14:48:34 +01:00
Dave Halter
cc465364d3 Fixes towards better MergedArray and partial functions. 2014-11-20 13:33:05 +01:00
Dave Halter
a6e1348757 type implementation. 2014-11-20 12:31:11 +01:00
Dave Halter
f2e3a3d090 Just rebuilt reversed. 2014-11-20 11:56:58 +01:00
Dave Halter
53c2a1679c Fix types tests. 2014-11-20 11:16:52 +01:00
Dave Halter
2c3a7b6d6c Imports are not Statements. 2014-11-20 02:29:24 +01:00
Dave Halter
164518b993 Get docstrings working. 2014-11-20 02:19:01 +01:00
Dave Halter
ce5d428d22 At least functions generate docstrings again. 2014-11-20 01:37:18 +01:00
Dave Halter
22b288fc73 Change docstring test function names, so that dynamic param completion doesn't interfere (especially because some of those files still generate parser errors). 2014-11-20 00:35:48 +01:00
Dave Halter
b5418d9d73 Small issue with dynamic params. 2014-11-20 00:25:03 +01:00
Dave Halter
e6364fdd8b Fix os.path issues. 2014-11-19 18:40:28 +01:00
Dave Halter
ba0e61d99f Star imports are now part of the ModuleWrapper. 2014-11-19 18:09:49 +01:00
Dave Halter
08bdcfb8ca Small issue with relative imports that don't contain a path after from. 2014-11-19 15:22:18 +01:00
Dave Halter
aeaf073ca2 Move some tests that targeted completion on import statements into a separate file. 2014-11-19 14:14:27 +01:00
Dave Halter
bb9d6b4832 Temporarily disable on import completion. Not sure if we're going to do it with the normal parser. 2014-11-19 13:24:45 +01:00
Dave Halter
c71646a9a0 Fixed relative imports. 2014-11-19 13:13:06 +01:00
Dave Halter
6c5f3419ff 'as' test and implementation for ImportName. Working pretty well. 2014-11-19 13:07:08 +01:00
Dave Halter
e630eeb397 Care for import aliases better. 2014-11-19 12:45:39 +01:00
Dave Halter
1c240e75d3 Replace get_all_import_names with a leaf search method in Simple. 2014-11-19 01:31:08 +01:00
Dave Halter
bab6788b42 split and improved Import*._paths 2014-11-19 00:45:20 +01:00
Dave Halter
3c6d5dafb1 Split Import, now there is ImportFrom and ImportName as it exists in the python grammar. 2014-11-19 00:40:16 +01:00
Dave Halter
535a69e499 Small improvments to from imports 2014-11-18 18:43:16 +01:00
Dave Halter
9d5f3162d7 More import stuff. Fake imports work a little bit better. 2014-11-18 18:22:26 +01:00
Dave Halter
a4a767f8bb Import improvements. 2014-11-18 17:19:15 +01:00
Dave Halter
b0109343e4 Jedi didn't care for decorator 'dotted_name' nodes and therefore descriptor tests failed. 2014-11-18 15:44:40 +01:00
Dave Halter
90ce1ac47f Simplify decorators, make them easier to read in debugging mode. 2014-11-18 15:04:20 +01:00
Dave Halter
6d866eb915 Small cleanup: Dynamic params comparison works now with the evaluator functions. 2014-11-18 13:28:24 +01:00
Dave Halter
78b7b8ffaf add a test that wasnt working with the old dynamic param lookup. 2014-11-18 13:08:27 +01:00
Dave Halter
93ffc799f5 Because of the change in dynamic params, we can now remove the decorator hack in the name finder. 2014-11-18 13:06:20 +01:00
Dave Halter
f9276a8bd2 Fix issues with decorators and dynamic params combined. 2014-11-18 13:04:05 +01:00
Dave Halter
4fa78e3482 Fix the last remaining issue with decorators. 2014-11-17 23:59:38 +01:00
Dave Halter
fd8752f285 Now most decorator tests pass. 2014-11-17 23:46:05 +01:00
Dave Halter
f62f181066 First decorator implementations. 2014-11-17 22:24:54 +01:00
Dave Halter
df5df1ccf5 FakeArray recursion issues. 2014-11-17 20:41:32 +01:00
Dave Halter
d49a8fc073 The test runner should only start tests if the name starts with the same letters. 2014-11-17 17:56:43 +01:00
Dave Halter
9ac66261c9 Fi dynamic param stuff combined with list comprehensions. 2014-11-17 17:54:12 +01:00
Dave Halter
ae3ff35674 Fix issues with buggy trailers in dynamic params. 2014-11-17 17:49:34 +01:00
Dave Halter
b57e4c4e7c dynamic test descriptions. 2014-11-17 17:18:03 +01:00
Dave Halter
9054a3f674 Split dynamic tests into dynamic_params and dynamic_arrays. 2014-11-17 17:11:58 +01:00
Dave Halter
da5273ce20 Fix issues with dynamic class parameter completion. 2014-11-17 17:07:32 +01:00
Dave Halter
259aa6bd5f First dynamic params working. 2014-11-17 16:23:18 +01:00
Dave Halter
22f20ec715 Treat executed params different from normal ones. 2014-11-17 13:35:16 +01:00
Dave Halter
2dfbc2a0fd Start to rework dynamic params. However goto is now needed first. 2014-11-17 12:34:32 +01:00
Dave Halter
0567a886c4 Fixed an issue with set literals. (The Array type was wrong before.) 2014-11-14 16:54:54 +01:00
Dave Halter
fce715b867 By disabling dynamic arrays completely, arrays are now almost fully passing. 2014-11-14 16:43:53 +01:00
Dave Halter
7049ad58db Small fix for dict lookups. 2014-11-14 16:03:09 +01:00
Dave Halter
01178d30ea Disable dynamic array instances and fix an issue with missing the arguments class. 2014-11-14 15:55:02 +01:00
Dave Halter
e64c78503e Fix some issues with array arguments. 2014-11-14 15:19:47 +01:00
Dave Halter
278bc9d705 Fix generators. 2014-11-14 02:05:25 +01:00
Dave Halter
13a128b160 global working. 2014-11-13 18:13:56 +01:00
Dave Halter
f3c2b4fc33 Get with statements working. 2014-11-13 12:51:49 +01:00
Dave Halter
541b8872d0 Changed is_node so it can actually deal with InstanceElements. 2014-11-13 01:15:44 +01:00
Dave Halter
1ab67ebbba Fix except statements names. 2014-11-13 01:02:56 +01:00
Dave Halter
2fc67b97e5 Disable analysis for now. 2014-11-13 00:28:42 +01:00
Dave Halter
f0a3c37fa0 __file__ should be listed as a module attribute. 2014-11-13 00:24:40 +01:00
Dave Halter
408eee50dd Start fixing issues with for loops and += operations. 2014-11-12 14:54:52 +01:00
Dave Halter
13c2279fea Make Generator tuple assignments work. 2014-11-12 13:48:08 +01:00
Dave Halter
65c18f143c Separate some Generator stuff. 2014-11-12 13:42:24 +01:00
Dave Halter
65f182ff0d Separate class for Generator and List Comprehensions. 2014-11-12 13:28:31 +01:00
Dave Halter
f760a7755d Nested list comprehensions seem to be working pretty well. 2014-11-12 12:30:59 +01:00
Dave Halter
c326562c27 Implemented x if foo else y case. 2014-11-12 11:49:27 +01:00
Dave Halter
54c5591ccb Progress with list comprehensions. There is now a separate class. 2014-11-12 11:42:31 +01:00
Dave Halter
cc661473bc Trying to change used_names, so that they don't contain name definitions from CompFor. 2014-11-11 17:13:27 +01:00
Dave Halter
460d959988 Implicit tuples as a separate class. 2014-11-06 18:24:55 +01:00
Dave Halter
8200b68549 Fix for loops and for loops tuple assignments. 2014-11-06 17:21:00 +01:00
Dave Halter
3a9e9e29e1 Small pytree changes. 2014-11-06 14:17:34 +01:00
Dave Halter
00454daf57 change the new parser tests 2014-11-06 14:16:17 +01:00
Dave Halter
aa0c73c9ab Fixed a few small things in the parser. Flow analysis is working again. Completely. 2014-11-06 04:41:23 +01:00
Dave Halter
56102e408e Small glitch with the change of return statements to loops in precedences. 2014-11-06 04:06:40 +01:00
Dave Halter
d58046f38f Changed normal precedence calculation. 2014-11-06 03:47:42 +01:00
Dave Halter
fae798adfe Simplify precedence.factor_calculate. 2014-11-05 23:21:32 +01:00
Dave Halter
ca70d32f23 isinstance fixes 2014-11-05 22:14:38 +01:00
Dave Halter
fa0f4b1e00 Further small flow_analysis corrections. Keywords are only equal to other keywords if they are the same. Not in case of the same value anymore. 2014-11-05 21:29:54 +01:00
Dave Halter
186ce2b70a Improve flow analysis a bit. 2014-11-05 19:18:45 +01:00
Dave Halter
9549c2b389 Last few small changes to the parser. Now beginning to work on the tests again. 2014-11-05 17:39:56 +01:00
Dave Halter
70bc6642d8 symbols on the stack are now defined with a smaller tuple. 2014-11-05 10:27:24 +01:00
Dave Halter
da5dd0efa1 simplify the parser stack. 2014-11-05 00:35:58 +01:00
Dave Halter
61df804e6e context is not needed for nodes. 2014-11-04 19:49:39 +01:00
Dave Halter
3b4a8dcd7e convert: run away from tuples. 2014-11-04 19:26:33 +01:00
Dave Halter
73bd576bb2 Merged some convert stuff. 2014-11-04 19:12:02 +01:00
Dave Halter
3518123afa Moved the convert function away from pytree 2014-11-04 19:09:58 +01:00
Dave Halter
f3e4bf9ed1 Splitting up the convert function into leaves and nodes. 2014-11-04 18:54:25 +01:00
Dave Halter
d483d50284 Small optimizations to make parsing faster. 2014-11-04 17:23:16 +01:00
Dave Halter
c6c2768dda Parser error recovery simplified. Just fall back to scopes, if somethings wrong. 2014-11-04 09:40:32 +01:00
Dave Halter
8c775e0a18 Resolved if/else issues in instances with get_defined_names.
This also means that class tests are now passing, except for private variables, which are not that important.
2014-11-04 00:24:01 +01:00
Dave Halter
1d2980cd2d Remove a flow information thing for now. 2014-11-03 18:27:31 +01:00
Dave Halter
55db65434c Add the parsers flow information as classes to parser.representation. 2014-11-03 18:25:10 +01:00
Dave Halter
e25684d470 Start to check for name positions with names_dict name finder. 2014-11-03 15:58:56 +01:00
Dave Halter
4676998fb5 Playing with params/names_dict 2014-11-03 13:38:57 +01:00
Dave Halter
f4d7020ebf Improve Name.is_definition 2014-11-02 17:53:04 +01:00
Dave Halter
81174741a4 After error recovery, names need to be removed from the used_names list, because they are not actually defined in the current AST. 2014-11-02 17:40:45 +01:00
Dave Halter
1ff4713848 Move error recovery function. 2014-11-02 17:24:06 +01:00
Dave Halter
0c3cba166e Make names_dict available in modules. 2014-11-02 14:22:00 +01:00
Dave Halter
afca0ef047 The user context parser should ignore keywords if they are not standing alone. 2014-10-30 01:56:08 +01:00
Dave Halter
1bb0eccc86 Simple error recovery in the parser: For now just discard everything that is not a suite or file_input, if we detect an error. 2014-10-30 01:40:22 +01:00
Dave Halter
f09ff04fcc Yield expressions are now separate form ReturnStmt. 2014-10-29 18:54:05 +01:00
Dave Halter
71c3d34965 Increase ParserPickling.version. 2014-10-29 01:46:54 +01:00
Dave Halter
1c09a90ac1 Disable usage of filter_private_variable for now. 2014-10-28 17:00:12 +01:00
Dave Halter
4f2223ae7b Super is now working. Still has the same flaws like the old implementation, but the tests are passing. 2014-10-28 16:22:59 +01:00
Dave Halter
500ac9b384 A irst super() implementation in stdlib. 2014-10-28 15:50:05 +01:00
Dave Halter
d27df89681 A temporary argument clinic implementation for the stdlib. 2014-10-28 14:56:22 +01:00
Dave Halter
f8bb369467 __call__ issues 2014-10-28 13:36:13 +01:00
Dave Halter
1cc1d4480b Fix issues with copying children in combination with InstanceElement. 2014-10-28 13:33:40 +01:00
Dave Halter
b550f67bce Small instance refactoring, now adding is_generated as a param. 2014-10-28 11:33:28 +01:00
Dave Halter
19e083cbfb Make Leaf public 2014-10-28 11:13:33 +01:00
Dave Halter
d667f19c57 Remove the old is_generated. 2014-10-28 02:11:50 +01:00
Dave Halter
b3d87302f9 Small changes to adapt oto the new param structure. 2014-10-28 02:11:13 +01:00
Dave Halter
fe7c750c2c Param is now the parent of its names and not just a helper class. 2014-10-28 02:05:44 +01:00
Dave Halter
68378a1468 Merge pull request #502 from abingham/patch-1
Added link to emacs-ycmd.
2014-10-27 16:17:43 +01:00
Dave Halter
1a6ff3e8e6 Small parser fix. 2014-10-27 16:07:24 +01:00
Dave Halter
8911ecb6a5 A last change for defaults. Params are pretty usable and work smooth now. 2014-10-27 15:36:29 +01:00
abingham
df4845790e Added link to emacs-ycmd.
This is another option for using jedi in emacs via ycmd.
2014-10-27 16:12:10 +02:00
Dave Halter
14ec210891 param default values. 2014-10-27 12:03:09 +01:00
Dave Halter
db2d380441 Issues with errors *args resolution. 2014-10-27 01:29:49 +01:00
Dave Halter
c0768924f6 Managed to get dict inputs working into kwargs. This was wrong in the old version of the parser. 2014-10-27 01:07:15 +01:00
Dave Halter
8df8749f22 Dict key that are not in dict should return all value types. 2014-10-27 00:19:31 +01:00
Dave Halter
e4124fcf9a More dynamic *args 2014-10-25 15:58:09 +02:00
Dave Halter
2315d51e68 direct param evaluation 2014-10-25 14:37:01 +02:00
Dave Halter
afbdf1a7ea Fix for default arguments in combination with named arguments. 2014-10-25 13:14:01 +02:00
Dave Halter
7532f52cdd Understanding implicit tuple returns (testlist) 2014-10-25 12:50:51 +02:00
Dave Halter
97a102bd24 Get param parsing right. 2014-10-25 12:02:54 +02:00
Dave Halter
22cb3ca5f0 subscriptable errors. 2014-10-25 11:34:16 +02:00
Dave Halter
995f0700c9 Fix params, so that quite a few functions can now pass. 2014-10-25 02:35:04 +02:00
Dave Halter
4384e938e9 Get a few more things right with params. 2014-10-25 02:25:09 +02:00
Dave Halter
9f1336095b unpacking arguments. 2014-10-24 21:46:48 +02:00
Dave Halter
c58975807c Small function/param corrections. 2014-10-24 01:58:56 +02:00
Dave Halter
93c97a78a3 Fix an issue with classes and decorators combined. 2014-10-24 00:41:26 +02:00
Dave Halter
3bdd32ad87 Always create a module. 2014-10-23 16:27:16 +02:00
Dave Halter
de4db11d25 Reading dicts works now. 2014-10-23 16:21:23 +02:00
Dave Halter
51ffc54471 Temporary params of class solution. 2014-10-23 14:41:01 +02:00
Dave Halter
387fc3b038 Adding prev_sibling, getting self attributes. 2014-10-23 14:03:52 +02:00
Dave Halter
88dcbe1f48 Name.is_definition implementation. 2014-10-23 13:37:35 +02:00
Dave Halter
971f1db823 Create a next_sibling method on _Leaf, which is then used to check for self attributes. 2014-10-23 01:36:24 +02:00
Dave Halter
abb8d0e26c get_names_dict removed and use instead the names_dict attribute. 2014-10-23 01:06:50 +02:00
Dave Halter
3bbce49fd3 Scope.names_dict implementation. 2014-10-23 00:51:02 +02:00
Dave Halter
4f4aef7ac8 Param helper class in the tree. 2014-10-22 20:07:42 +02:00
Dave Halter
e9f4c60e49 Use used_names not in pgen2, but only in our parser. 2014-10-22 15:50:02 +02:00
Dave Halter
e2a07752fd '.NAME' lookups. 2014-10-22 02:33:35 +02:00
Dave Halter
34f3ea6973 More and probably the last tuple assignment stuff. 2014-10-22 02:29:47 +02:00
Dave Halter
297bcf6e19 Parentheses without commas are no tuples. 2014-10-22 02:10:48 +02:00
Dave Halter
6a8b840b29 Be able to differentiate tuple/list/dict. 2014-10-22 01:42:21 +02:00
Dave Halter
14113a1bff Fix a lot more of the tuple assignments. 2014-10-22 01:27:12 +02:00
Dave Halter
5b29e2c54d Add a method 'Name.assignment_indexes', to process tuple assignments. 2014-10-21 15:45:29 +02:00
Dave Halter
c1807e5f33 Reworked ExprStmt.get_definition 2014-10-21 14:21:59 +02:00
Dave Halter
1c27759c4f Few fixes. 2014-10-21 13:54:03 +02:00
Dave Halter
d119902496 Slices 2014-10-21 13:36:56 +02:00
Dave Halter
ab53942e55 Start working with arithmetics. 2014-10-21 12:18:03 +02:00
Dave Halter
2eed6b7b5f Unaccessible array indexes should still produce results. 2014-10-21 12:03:01 +02:00
Dave Halter
8f3b7f9d44 A first array test passing. 2014-10-21 11:58:53 +02:00
Dave Halter
fb2ef5a7a0 Start using arrays. 2014-10-21 11:05:12 +02:00
Dave Halter
718f43431c Introduce error recovery for the parser: At the moment just recover from broken statements. 2014-10-21 09:57:22 +02:00
Dave Halter
c821b30017 Fix a first test: complex.py 2014-10-20 17:06:18 +02:00
Dave Halter
43e3452474 Fix more argument related stuff. 2014-10-20 16:34:17 +02:00
Dave Halter
1a639bd118 Arguments move to params. 2014-10-20 15:43:56 +02:00
Dave Halter
b2c95cb02f Generating return statements. 2014-10-18 12:40:36 +02:00
Dave Halter
74d4fcf4e7 globals are more or less ready. 2014-10-17 18:48:07 +02:00
Dave Halter
f08811fba7 Start implementing GlobalStmt. 2014-10-17 15:13:45 +02:00
Dave Halter
6abafc40fa ExprStmt now doesn't contain imports anymore. 2014-10-17 14:56:12 +02:00
Dave Halter
d7face17f6 Using expr_stmt now instead of simple_stmt as ExprStmt, because that resembles the official grammar better. 2014-10-17 12:03:41 +02:00
Dave Halter
19acdd32b7 Fixed issues with the Python3.4 grammar file.
The order of symbols matters. 'file_input' needs to be the first symbol.
2014-10-17 01:34:47 +02:00
Dave Halter
ae8969a0d1 New kind of KeywordStatement 2014-10-17 01:26:34 +02:00
Dave Halter
aefc5ec15f add the 3.4 grammar. 2014-10-16 14:47:14 +02:00
Dave Halter
01ce93cb5c Preparation for a parser refactoring. 2014-10-16 11:45:25 +02:00
Dave Halter
887949e23f Start making executions work. 2014-10-16 10:58:27 +02:00
Dave Halter
7b91050c85 introduce something that resembles argument clinic in stdlib. 2014-10-16 10:58:11 +02:00
Dave Halter
631aa0ea61 Processing atom and power nodes. 2014-10-15 13:40:56 +02:00
Dave Halter
485b8ae3da Move Node away from pytree into the parser representation. 2014-10-13 13:47:32 +02:00
Dave Halter
6458047bac Name in statement definition implementation. 2014-10-13 13:33:49 +02:00
Dave Halter
0def3afaaa A move function for Nodes. 2014-10-12 23:37:46 +02:00
Dave Halter
660124aca1 Get an example running: 'import json; json.dump'. 2014-10-12 22:37:23 +02:00
Dave Halter
e2b7e74aef Import improvements. 2014-10-10 17:52:36 +02:00
Dave Halter
a192f4f347 Add keywords, improve import support. 2014-10-10 17:48:40 +02:00
Dave Halter
54c91b1509 Get a first local test passing. 2014-10-10 12:07:08 +02:00
Dave Halter
3bf1fec568 Start implementing an algorithm for actually evaluating the parser tree. 2014-10-10 11:29:22 +02:00
Dave Halter
66840a742c Implement the new parser in jedi.parser.Parser 2014-10-10 00:06:28 +02:00
Dave Halter
05fd7f992e Don't use the fast parser at the moment. It's more important for now to even get a parser working. 2014-10-09 14:36:52 +02:00
Dave Halter
aa75140f96 Remove old base class for Nodes. 2014-10-09 13:33:42 +02:00
Dave Halter
92ee2a912e Actually get the first few written tests passing. 2014-10-09 13:21:30 +02:00
Dave Halter
68d23840bb Start actual testing of the new parser. 2014-10-09 13:16:28 +02:00
Dave Halter
daee273a08 Remove all the old get_code references. 2014-10-09 11:38:57 +02:00
Dave Halter
843efb43e9 statement refactoring. 2014-10-09 11:36:27 +02:00
Dave Halter
a0092c2653 remove old statement parser code. 2014-10-09 11:20:49 +02:00
Dave Halter
140320143a Remove the old Leave class. 2014-10-09 11:02:03 +02:00
Dave Halter
c7c222daab Implement WhiteSpace as well and merge with pytree. 2014-10-09 10:55:03 +02:00
Dave Halter
8236ce18a2 Refactor Literal. 2014-10-09 03:23:52 +02:00
Dave Halter
eb384fc2d1 Name refactoring. 2014-10-09 03:21:18 +02:00
Dave Halter
432ec8f186 Operator refactoring. 2014-10-09 03:15:50 +02:00
Dave Halter
6bb88ddd85 Function is starting to work. 2014-10-08 17:58:02 +02:00
Dave Halter
585e92ac9f Temporarily disable evaluate/compiled stuff, because it interferes with the current changes of the parser. This will be undone. 2014-10-08 17:45:49 +02:00
Dave Halter
308c971ad7 Change pr.Simple. 2014-10-08 17:02:37 +02:00
Dave Halter
d9aa84f971 simplify the use of 'context' 2014-10-08 16:33:35 +02:00
Dave Halter
e54dac3777 Start setting up the parser.representation merge 2014-10-08 16:15:27 +02:00
Dave Halter
220ddc8a74 Probably the last big removals, there's not a lot left. 2014-10-08 15:35:45 +02:00
Dave Halter
a23ad3df10 Remove more and more from pytree. 2014-10-08 15:30:15 +02:00
Dave Halter
2781a4ac98 Remove comparison methods. 2014-10-08 15:22:44 +02:00
Dave Halter
1fb6e15750 Fix prefixes of Leafs. 2014-10-08 15:19:36 +02:00
Dave Halter
36368cd606 More removals and cleanups. 2014-10-08 15:13:43 +02:00
Dave Halter
07d9111c77 Remove some more pytree methods. 2014-10-08 15:05:29 +02:00
Dave Halter
a7fff54d7b Remove some fixer related stuff (lib2to3 stuff). 2014-10-08 15:04:01 +02:00
Dave Halter
834172a3e9 Add a new parser, check it pgen2 would work. (already modified outside this repository) 2014-10-08 14:26:52 +02:00
Dave Halter
09a7317bc9 Merge branch 'dev' of github.com:davidhalter/jedi into dev 2014-10-06 17:55:46 +02:00
Dave Halter
e9a3a44780 Remove some other _star_import_cache stuff, and with this, #489 should be fixed. 2014-10-06 17:55:28 +02:00
Dave Halter
3638d5149d Change time_cache, to also host the star_import_cache. 2014-10-06 17:37:34 +02:00
Dave Halter
bbdb4703ec change cache_call_signatures, so that it has a well defined input. 2014-10-06 16:07:33 +02:00
Dave Halter
87574e9d2e star_import_cache refactorings: Make it more readable. 2014-10-04 12:43:08 +02:00
Dave Halter
a1b55a9df7 clear_caches -> clear_time_caches 2014-10-03 14:23:46 +02:00
Dave Halter
116e9e72fc is_definition/Import issue 2014-10-02 11:27:01 +02:00
Dave Halter
8ca48f03db Tests for imports and is_definition. 2014-10-02 11:14:03 +02:00
Dave Halter
90d159eadd Merge pull request #483 from ColinDuquesnoy/fix_runtime_error
Fix RuntimeError: the PyQt5.QtCore and PyQt4.QtCore modules both wrap the QObject class
2014-09-29 16:07:01 +04:30
ColinDuquesnoy
d7836c1034 Add a comment and link to issue #483 2014-09-29 11:57:38 +02:00
ColinDuquesnoy
42596dba15 Merge remote-tracking branch 'upstream/dev' into fix_runtime_error
Conflicts:
	jedi/evaluate/imports.py
2014-09-29 11:53:35 +02:00
ColinDuquesnoy
d1ae447362 Simplify code 2014-09-29 09:50:49 +02:00
Dave Halter
27444ed64d Remove Import.alias_name_part, it was simply an alias for another lookup. 2014-09-26 16:32:36 +02:00
Dave Halter
03e01631cc Remove NamePart from existance and rename it to Name. 2014-09-26 16:29:53 +02:00
Dave Halter
522c9eda90 Remove pr.Name completely. 2014-09-26 16:18:10 +02:00
Dave Halter
4d7db35340 Fix a few last tests, now Jedi's working again, tests are passing. 2014-09-26 16:02:03 +02:00
Dave Halter
6f29e802c2 Fix an issue with as_names. 2014-09-26 15:48:49 +02:00
Dave Halter
7fea6437d9 Fix issues with Definition.full_name 2014-09-26 13:07:21 +02:00
Dave Halter
4f4ac505a3 Fix isses with interpreter completions. 2014-09-26 13:07:08 +02:00
Dave Halter
3add6e4289 Fix various bugs. 2014-09-26 12:22:56 +02:00
Dave Halter
ce3ec6b534 Finally remove ArrayMethod and use an InstanceElement instead (which it basically is). 2014-09-26 12:08:04 +02:00
Dave Halter
90842ce62d Fixed global variables. 2014-09-26 11:58:11 +02:00
Dave Halter
4eaee09d6e Fix named param issues. 2014-09-26 11:52:26 +02:00
Dave Halter
47c4369d28 Fix remaining issues with sys path checks. 2014-09-25 18:28:03 +02:00
Dave Halter
f4c99259b5 Fix an issue with sys.path. Also moved the names closure for isinstance checks away (used for sys.path stuff) and use a get_code check instead, which is more flexible. 2014-09-25 12:35:53 +02:00
Dave Halter
c2d645b7c1 Fix one of the really hard issues: deep_ast_copy didn't copy the newly created _names_dict. 2014-09-25 12:15:15 +02:00
Dave Halter
16f244a1b2 Fix isinstance issues. 2014-09-25 00:36:53 +02:00
Dave Halter
59225ceaa3 usages issues. 2014-09-25 00:14:43 +02:00
Dave Halter
9ecf3774a0 Import issues again. 2014-09-24 21:59:08 +02:00
Dave Halter
c43afae24a Issues with imports. 2014-09-24 21:12:38 +02:00
Dave Halter
ff61c1d81c Fix an extremely annoying bug that made pickling impossible. 2014-09-24 20:58:29 +02:00
Dave Halter
56243e10c6 The get_code generation of imports was buggy. 2014-09-24 17:48:11 +02:00
Dave Halter
db31536d78 Fix issue with descriptors. 2014-09-24 16:52:44 +02:00
Dave Halter
19b32a3657 And by changing small things about NamePart/InstanceElement usage, we're finally able to pass the class tests again. 2014-09-24 15:42:44 +02:00
Dave Halter
f300e63dae A couple of changes:
- parser.representation now uses ArrayStmt and ExprStatement to be able to differentiate easily with isinstance.
- NameParts are temporarily allowed again as InstanceElements.
2014-09-24 15:21:56 +02:00
Dave Halter
0a65eea2cf Start to change the logic for self.foo variables. 2014-09-24 14:33:15 +02:00
Dave Halter
12e391c97a Add a LazyDict to be able to use it within FunctionExecution for get_names_dict 2014-09-24 12:57:26 +02:00
Dave Halter
d5fbc006e2 Add a names_dict to scopes. This is good for the future parser and now useful to process self.foo and other stuff. 2014-09-24 12:44:24 +02:00
Dave Halter
c61f79314b Introduce get_names_dict to statements to actually fetch the calls out of a statement.
This is going to be the new default method to do dynamic stuff as well as self.foo resolutions for instances.
2014-09-24 12:06:41 +02:00
Dave Halter
5efa467449 Few import issues. 2014-09-22 23:45:48 +02:00
Dave Halter
1d71b25109 Previously forgot to add the NameParts to used_names. (which had worked before that) 2014-09-22 23:24:29 +02:00
Dave Halter
6819deb404 Resolve some **kwargs issues. 2014-09-22 23:06:22 +02:00
Dave Halter
dae1a48d70 Remove a lot of the old Name.names usages in favor of a direct NamePart usage. 2014-09-22 22:34:33 +02:00
Dave Halter
04cf742973 Temporary parser implementation. Now we're pretty much done with pr.Name. 2014-09-22 17:05:23 +02:00
Dave Halter
6bd7ef56f1 Now most tests pass and we're able to continue getting rid of parsing.representation.Name. 2014-09-22 15:41:27 +02:00
Dave Halter
8f3301f281 Passing Function tests now. 2014-09-22 14:06:38 +02:00
Dave Halter
c4e45916c6 Modules also use a NamePart as a name, now. 2014-09-22 12:52:48 +02:00
Dave Halter
779618c08b First changes to eventually replace Name by NamePart. 2014-09-22 11:57:19 +02:00
Dave Halter
b26f51ded2 Fix obvious UnboundLocalError. 2014-09-19 18:08:30 +02:00
Dave Halter
78bd775889 Only real modules should be added in get_modules_containing_name. 2014-09-19 18:05:57 +02:00
Dave Halter
e0f84ccb86 Tests for issues with default args in dynamic param contexts. 2014-09-19 16:56:26 +02:00
Dave Halter
d4503c77a5 get_parent_until should always have the same signature.
Fix it for ArrayMethod.get_parent_until.
2014-09-19 16:17:05 +02:00
Dave Halter
99d35e57b6 Fix alias usages in goto_assignments. 2014-09-19 13:42:47 +02:00
Dave Halter
ed56f73836 Care for nested imports in goto_assignments. 2014-09-19 12:14:29 +02:00
Dave Halter
fc5f73861c Fix issues with the os module.
Using a try/finally assures that the recursion checkers work the right way.
2014-09-19 10:59:24 +02:00
Dave Halter
b2342c76be Refactoring: Make Import.get_all_import_names return NameParts. 2014-09-19 01:40:09 +02:00
Dave Halter
83d2af5138 First imports are working with goto. 2014-09-19 01:21:17 +02:00
Dave Halter
610b2fc832 tests for goto on imports. 2014-09-19 00:49:22 +02:00
Dave Halter
7b0bb83d16 Change the behavior of eval_statement_element and follow_call_path. Arrays should only be looked at in the latter. 2014-09-18 23:44:11 +02:00
Dave Halter
69e6139527 Goto on named params in class calls is now working. 2014-09-18 20:11:58 +02:00
Dave Halter
ba80e35204 Test for an issue with named params in class calls (instead of functions). 2014-09-18 13:30:52 +02:00
Dave Halter
9fa6a86a19 Tests for Definition.is_definition(). 2014-09-17 18:17:22 +02:00
ColinDuquesnoy
fb86388890 Fix RuntimeError: the PyQt5.QtCore and PyQt4.QtCore modules both wrap the QObject class 2014-09-13 12:18:34 +02:00
Dave Halter
9983898162 Temporarily disable a test for goto on nested imports. The positions are currently wrong. But this is a known issue. 2014-09-11 02:27:53 +02:00
Dave Halter
085c8034b3 Apply evaluate.representation wrappers already before they go out into the goto world. 2014-09-11 02:20:54 +02:00
Dave Halter
1624fa0872 Replace BaseDefinition._name.get_definition() calls with BaseDefinition._definition. 2014-09-11 01:36:21 +02:00
Dave Halter
71efb51f2a Remove BaseDefinition._start_pos. 2014-09-11 01:21:08 +02:00
Dave Halter
283afa78f1 Remove code that is not needed anymore, because the Definition/Completion import is now standardized (to NamePart). 2014-09-11 01:15:00 +02:00
Dave Halter
9f16555f47 Big refactoring: BaseDefinition._definnition changes to BaseDefinition._name, because it's a NamePart now.
This also includes changes to tests and some simplifications like deleting the old name logic of Definition.
2014-09-11 01:03:30 +02:00
Dave Halter
58526e2302 Completion now also uses only NameParts as its _definition attribute. 2014-09-10 20:12:19 +02:00
Dave Halter
8f892e3922 Use FakeName instead of a custom KeywordName. 2014-09-10 20:07:13 +02:00
Dave Halter
1fb9b4bc6b Completion now always takes a NamePart as input. 2014-09-10 18:59:08 +02:00
Dave Halter
0eea30f227 NamePart migration of Definition is complete. Now Completion. 2014-09-10 18:29:10 +02:00
Dave Halter
2aa538999e Removed an old test from the days where it was allowed to add Keywords to Definitions. 2014-09-10 18:05:04 +02:00
Dave Halter
46b49af5d9 Even params should be NameParts as a Definition input. 2014-09-10 17:41:06 +02:00
Dave Halter
5e28d69437 Fix remaining usage issues. 2014-09-10 17:15:58 +02:00
Dave Halter
4060c4dc55 Fix some goto issues. 2014-09-10 16:39:09 +02:00
Dave Halter
a93a389d5c Fix all the normal issues with the NameFinder change. Now goto... 2014-09-10 16:30:22 +02:00
Dave Halter
657a2c7d4f Trying to get the NameFinder to use only NameParts. 2014-09-10 16:20:30 +02:00
Dave Halter
43cf1d451f Python 2/3 compatibility issues that were not resolved in the latest commit. 2014-09-09 17:51:39 +02:00
Dave Halter
fdc637c5c4 Add a forgotten test module, test_sys_path.py and fix Python2/3 compatibility issues. 2014-09-09 17:08:22 +02:00
Dave Halter
38f7296f39 Typo. 2014-09-09 16:51:46 +02:00
Dave Halter
87aa76678a Goto should work on named params, too. 2014-09-09 16:48:53 +02:00
Dave Halter
110f130741 Make it possible to get previous statements of Calls. 2014-09-09 16:44:56 +02:00
Dave Halter
b68a59daef Fix the last remaining issues of the first part of the NamePart switch. 2014-09-09 15:58:20 +02:00
Dave Halter
45e033c50e Quite a few fixes to be eventually able to use NameParts as Definition inputs. 2014-09-09 15:21:27 +02:00
Dave Halter
1199defabb Start to use NameParts only in Definition contexts. 2014-09-09 14:13:10 +02:00
Dave Halter
ff7680c15f Generate the expression_list of a statement in any case.
This is a consequence of being able to have Calls as parents of Names. Otherwise we would have changing Name.parent values.
2014-09-09 12:54:46 +02:00
Dave Halter
740fd0657f Add a goto_assignments test for named params 2014-09-09 00:06:24 +02:00
Dave Halter
0dcb91d236 Add a Definition.is_definition function to be able to check if a name is a definition or not. 2014-09-08 23:44:35 +02:00
Dave Halter
851717a968 Publicize jedi.names and add a first test. 2014-09-08 22:39:47 +02:00
Dave Halter
be85391321 Create a 'jedi.names' function with the proper docstring.
Modelled according the discussion in #477.
2014-09-08 21:43:16 +02:00
Dave Halter
ca536baf9b Last fixes, because of the Name.get_definition change. The recent parser.representation changes are now fully working and we're ready to improve Evaluator.goto again. 2014-09-06 13:23:00 +02:00
Dave Halter
ece9fdf4ae Fixing most of the issues that existed, because of the recent Name.get_definition/Call.name.parent change. 2014-09-06 13:02:52 +02:00
Dave Halter
2e7e2f0a29 Name parents are now Calls (once their statements have generated the Calls).
This makes the goto function more powerful. Also fixes an issue with the deep_ast_copy, that I tried to fix previously, but failed, because I hadn't tested it.
2014-09-06 12:19:07 +02:00
Dave Halter
5a3ee02399 Use ExprStmt pretty much everywhere where it should be used.
ExprStmt is now really a normal statement. All the other statements are from now on considered legacy code. As a side effect this increases the parser pickling version.
2014-09-06 11:13:58 +02:00
Dave Halter
cb84bc0829 Start using ExprStmt. 2014-09-06 10:46:59 +02:00
Dave Halter
f57d9ef675 Rename Name.get_parent_stmt to Name.get_definition, because it's not always a statement. Also start using it in the NameFinder. 2014-09-06 10:43:26 +02:00
Dave Halter
99116cdcb7 Add a Name.get_parent_stmt() function. 2014-09-05 22:26:55 +02:00
Dave Halter
6c07c7acfe Create an ExprStatement class to replace the Statement class in the future and separate array parts of actual statements 2014-09-05 22:21:26 +02:00
Dave Halter
12154fdecf Refactor a few name finder things. 2014-09-05 12:12:29 +02:00
Dave Halter
ba805879b4 Updated helpers.deep_ast_copy. Now the function copies statements in a better way.
Previously statement attributes where scanned like every other objects. Now two statements get priority: _assignment_details and _expression_list. This is necessary if we want to switch towards proper name parents (A Call would be the parent of a name, not a Statement)
2014-09-05 11:49:45 +02:00
Dave Halter
42d6b57599 precedence._is_string -> precedence.is_string 2014-09-04 14:13:26 +02:00
Dave Halter
7b2e11d71b Rewrote sys_path._paths_from_assignment. 2014-09-04 14:12:10 +02:00
Dave Halter
4180005893 Forgot to add the the params in the case of a class in the previous commit. 2014-09-04 12:53:42 +02:00
Dave Halter
06699993f1 Class inheritance definitions shouldn't be params. It's just statements. 2014-09-04 12:28:50 +02:00
Dave Halter
1df025c39d Definitions should not be followed in Evaluator.goto. 2014-09-04 11:55:42 +02:00
Dave Halter
e872d9e073 Script.goto_assignments now always needs a call_path. Otherwise it raises a NotFoundError.
This change makes Jedi's behavior more consistent.
2014-09-04 00:56:58 +02:00
Dave Halter
fb10199f37 Remove search_name and search_name_part from goto returns.
The search_name can be retrieved by checking definitions for it. Definitions should always be names or even better name_parts in case of goto. Therefore we can just get it there.
2014-09-03 23:28:19 +02:00
Dave Halter
f7a1c110ba Fix remaining issues with CompiledName.name change. 2014-09-03 19:47:37 +02:00
Dave Halter
bb5ffe9343 CompiledObject.name returns a Name now, not a string. This is more consistent with the Jedi design and doesn't lead to bugs while ducktyping. 2014-09-03 19:43:21 +02:00
Dave Halter
18204c4c19 By trying to get rid of search_name in usages, we had to fix an issue with imports:
If used like 'follow(is_goto)', it could return a ModuleWrapper instead of a Name, which is what we actually want.
2014-09-03 19:30:00 +02:00
Dave Halter
59578966cf Remove code that is not used anymore from Script._goto 2014-09-03 18:02:20 +02:00
Dave Halter
e2ca11435c Script._goto improvements and documentation. 2014-09-03 17:27:26 +02:00
Dave Halter
95852f5e7f Clarify inner workings of Evaluator.goto 2014-09-03 14:58:24 +02:00
Dave Halter
bcc84820fe Fix issues with unreachable flows.
This benefits static analysis as well as autocompletion: Unreachable code (things like code within 'if 0:') should still be resolveable.
2014-09-03 00:05:37 +02:00
Dave Halter
ea5b98905e Make statement_elements_in_statement work with ListComprehensions, Lambdas and 'except foo as' expressions 2014-09-02 14:52:04 +02:00
Dave Halter
38c71fce3f Added tests for statement_elements_in_statement 2014-09-02 12:10:16 +02:00
Dave Halter
f785aa26dd Additional helper methods, to find all the statement elements that are needed. 2014-09-02 03:26:17 +02:00
Dave Halter
be9e77d7d3 Add a temporary api._names, to make it possible to annotate a full script with types. 2014-09-01 18:10:40 +02:00
Dave Halter
bbf1070ad9 Add a helper function to list all name parts of a given module. 2014-09-01 13:20:01 +02:00
Dave Halter
6b88da4d2d Merge pull request #467 from phillipberndt/master
Alter jedi.Interpreter() namespace argument default from [] to [{}]
2014-08-23 16:05:12 +04:30
Dave Halter
76d91ba72a Rename fast_parent_copy to deep_ast_copy. 2014-08-22 00:59:46 +02:00
Dave Halter
ed3b507ab7 cleanup 2014-08-22 00:47:08 +02:00
Dave Halter
6ba0b7b81e lower than/greater than operators evaluate to a boolean now. This is not a 100% correct, because it doesn't evaluate __gt__, etc. But that's ok for now. 2014-08-22 00:26:55 +02:00
Dave Halter
039a5ecaf9 Fix issues caused by KeywordStatement, which needs to be copied as well. 2014-08-21 16:51:00 +02:00
Dave Halter
0ef030848d refactor fast_parent_copy, use new_elements_default to hand in a dictionary, that contains all the generated duplicates. 2014-08-21 13:17:33 +02:00
Dave Halter
3cf8bfa8e1 Fix a few tests by either fixing the test cases or adding py__bool__ functions to objects that should have such a method. 2014-08-20 17:28:54 +02:00
Dave Halter
f911050300 Rewrote the isistance implementation, so that it works properly with tuples as well as normal classes. 2014-08-20 16:58:19 +02:00
Dave Halter
2a964d4e48 Also implement the or operator. 2014-08-20 16:28:04 +02:00
Dave Halter
148d17b3be Implementation of the and operation. 2014-08-20 16:21:33 +02:00
Dave Halter
d6dd7cd55e Move process_precedence_element from the Evaluator to the precedence module. 2014-08-20 15:59:37 +02:00
Dave Halter
9abc8a19e7 By adding a py__class__ method to CompiledObject and Class, we Jedi is able to understand isinstance checks, now.
This also includes a CheckAttribute class in evaluatue.compiled, because it's way easier to generalize the AttributeErrors there.
2014-08-20 14:46:18 +02:00
Dave Halter
0e66aef511 Use IterableWrapper in the iterable module to be able to add methods like is_class quickly. 2014-08-20 14:01:41 +02:00
Dave Halter
442a1a1d08 wrap some more values with er.wrap 2014-08-20 13:52:49 +02:00
Dave Halter
c9542cbc04 Start implementing an is_class function that will determine if an object is a class or not in the future. 2014-08-20 11:43:25 +02:00
Dave Halter
7f874620db Start documenting all the py__foo__ methods 2014-08-20 11:31:23 +02:00
Dave Halter
2e949b43bb Ignore FunctionExecutions in old style isinstance checks for now, because it collides with new style isinstance checks. 2014-08-20 11:31:11 +02:00
Dave Halter
09ca47fa93 Introduce a dedicated isinstance function implementation. 2014-08-19 23:57:59 +02:00
Phillip Berndt
3189ba7662 Add type check to jedi.Interpreter() namespace argument and remove default value 2014-08-19 09:41:17 +02:00
Dave Halter
49163e135c flow_analysis test for isinstance as well as and/or operations. 2014-08-19 01:03:14 +02:00
Dave Halter
77673ba986 Add an optional param 'parent' to parser.representation.Simple, which simplifies some calls to that superclass. 2014-08-19 00:30:17 +02:00
Dave Halter
8bde89cc58 Fix the remaining issues with the StatementElement.next refactoring. 2014-08-19 00:12:14 +02:00
Dave Halter
8006d6f190 Change implementation of StatementElement.
Instead of having both next and execution as attributes, we now only have next, because it's an execution if there's an array.
2014-08-18 22:25:55 +02:00
Dave Halter
7619bf27d1 Simplify goto_definition in case it done on a function. 2014-08-18 15:00:14 +02:00
Dave Halter
00d15da143 refactor search_call_signatures. Now we don't need to set Call.next.parent in a strange way anymore and the whole thing seems to be more logical. 2014-08-18 14:51:38 +02:00
Dave Halter
542648f5a0 first step in refactoring call_signature_array_for_pos, use original_call as a param. 2014-08-18 13:39:01 +02:00
Dave Halter
9f38f10366 fix tests. Operators should not equal to other operators with a different position. or even parent. 2014-08-18 13:13:07 +02:00
Dave Halter
1d812c2414 Use the "wrong" parents again for next/execution in StatementElement. This is important for call_signature lookups. We might still be able to change this somewhere in time. 2014-08-18 11:22:38 +02:00
Dave Halter
fd90dfc4f5 Use a LazyName for module attributes, they should only be generated if needed. 2014-08-15 15:20:40 +02:00
Dave Halter
868dab4f51 small debug change 2014-08-15 02:26:13 +02:00
Dave Halter
89ab0ba137 Fix fast_parent_copy. The caching is now more solid than before (and doesn't produce weird side effects. This also solves an issue with Lambdas. However, by fixing all of this we have broken some other things. 2014-08-15 01:55:43 +02:00
Dave Halter
1965469050 fast_parent_copy should also change the parent of NameParts. 2014-08-14 23:48:27 +02:00
Dave Halter
1f9e7ddff8 Remove code in the parser that didn't make sense. 2014-08-14 13:24:26 +02:00
Dave Halter
425290aa8f Fix an issue with partial keyword inputs. 2014-08-14 12:25:00 +02:00
Dave Halter
1540ac89f8 Implement the descriptor protocoll properly for instances. 2014-08-14 12:15:48 +02:00
Dave Halter
f743619fb8 Tests for conditions in descriptors. 2014-08-13 14:49:42 +02:00
Dave Halter
ec7b3bf433 refactor py_base to py__bases__, because that's the general naming schema 2014-08-13 14:34:37 +02:00
Dave Halter
cd433adf84 Speedup object lookup even further in classes. 2014-08-13 14:17:57 +02:00
Dave Halter
9702c4cdc6 Restructure the way we get self arguments (probably reduces executions of object). 2014-08-13 14:07:09 +02:00
Dave Halter
cf32e15f65 Remove the old 'is not' logic to detect if not instances and use them to do branch predictions. This is not necessary anymore, since we now support that in a more general way (flow_analysis). 2014-08-12 18:14:03 +02:00
Dave Halter
eeac77d360 Also support the not operator. 2014-08-12 18:09:59 +02:00
Dave Halter
8ed89e8245 implement !=, ==, is, is not operators to work in if statements (they also work in in non if contexts and return a bool value.), includes tests. 2014-08-12 17:59:19 +02:00
Dave Halter
6f018e4884 introduce maybe_docstr in parse_statement, which should have been introduced way earlier. 2014-08-12 17:13:14 +02:00
Dave Halter
fb1dba269a re-enable the interpretation of the None keyword 2014-08-12 14:38:56 +02:00
Dave Halter
469988be9c actually add tests for the flow analysis of variables. 2014-08-12 09:57:00 +02:00
Dave Halter
968bc45314 even tests should not suffer too much from side effects. 2014-08-12 01:46:07 +02:00
Dave Halter
6b7ce590fa Simplify get_parent_scope 2014-08-12 01:37:58 +02:00
Dave Halter
33e5a3280a Remove IsScope in favor of an is_scope function.
This function was partially implemented anway. Now we've also added a function called 'get_parent_scope', to make it easy to get a scope of a Call, Statement, whatever.
2014-08-12 01:19:19 +02:00
Dave Halter
1865284fa9 fix the interpreter (previously broken by flow analysis) 2014-08-12 00:19:20 +02:00
Dave Halter
242072976a use py__mro__ in a classes scope_names_generator 2014-08-11 23:53:45 +02:00
Dave Halter
526af7ccbe settings should not be affected by exceptions. 2014-08-11 17:27:40 +02:00
Dave Halter
f1711f8f9c possible direction of branch checks for name resolution. 2014-08-10 13:17:37 +02:00
Dave Halter
483f5c14ee Listeneres should be removed even in exception cases. Do a 'finally' cleanup. 2014-08-07 16:27:57 +02:00
Dave Halter
01bdd1e4fa Test fixes and for loops need to be handled a awell in flow_analysis. 2014-08-07 15:51:41 +02:00
Dave Halter
0ae9e520c1 flow analysis working for elif statements (even in combination with else) 2014-08-07 15:18:30 +02:00
Dave Halter
743d064e6d exception while using else as a scope 2014-08-07 12:10:31 +02:00
Dave Halter
ee65764c3a more complicated logic working with else 2014-08-07 12:02:08 +02:00
Dave Halter
d94a70b524 fix a logic issue in the flow_analysis.Status.__and__ 2014-08-07 03:02:40 +02:00
Dave Halter
b7151c1ef9 add a first flow analysis test 2014-08-06 23:35:30 +02:00
Dave Halter
138fa1b4de deletion of returns from SCOPE_CONTENTS was wrong. 2014-08-06 22:42:38 +02:00
Dave Halter
e7e7bd29e8 fix generator tests (multiple yields must be called with an if random. 2014-08-06 12:45:38 +02:00
Dave Halter
23c39eff9a fix lambda issues 2014-08-06 12:40:08 +02:00
Dave Halter
e3bb0ccc2e fix a keyword statement issue 2014-08-06 12:10:36 +02:00
Dave Halter
15ec0a77fe a first very simple implementation of reachable/unreachable return statements. 2014-08-05 17:02:16 +02:00
Dave Halter
f5e49e3218 flow analysis preparation 2014-08-05 12:06:58 +02:00
Dave Halter
c44168f7ad add a Flow.previous attribute to be able to access the if flow from an else clause. 2014-08-05 11:17:18 +02:00
Dave Halter
54dce0e3b2 fix strange issues of Python's std lib tokenizer, might be in there as well (not sure, cause I modified a lot). fixes #449 2014-08-04 16:47:36 +02:00
Dave Halter
b2b4827ce3 moved test_token to test_tokenize 2014-08-04 16:25:33 +02:00
Dave Halter
cba100a801 test for #414 which doesn't seem to be failing anymore. 2014-08-04 16:08:47 +02:00
Dave Halter
625e88e851 isinstance checks now also give you type hints in class contexts, fixes #241. 2014-08-04 02:11:30 +02:00
Dave Halter
0a0673e87c refactoring in dynamic param searching 2014-08-04 01:39:05 +02:00
Dave Halter
7bba12e8c5 comments 2014-08-03 23:00:32 +02:00
Dave Halter
6e5d80a6b2 builtins shouldn't be unique if called by compiled.create 2014-08-01 15:51:59 +02:00
Dave Halter
68cecad996 tests for py__mro__ 2014-08-01 15:50:18 +02:00
Dave Halter
2c0a46fafe Fix an issue with CallSignatures:
If used in a longer statement, it could happen that parts of the statement was still evaluated, but the call signature is only valid at the cursor.
2014-07-31 17:47:56 +02:00
Dave Halter
7b4a188948 fix a few small issues that remained in the tests 2014-07-31 17:34:35 +02:00
Dave Halter
59b8c6b015 CompiledObjects should execute everything when reading the return information from docstring (because it's always types, not values) 2014-07-31 17:16:24 +02:00
Dave Halter
332a16a27e elements in tuples/lists in docstrings were not executed. 2014-07-31 17:13:56 +02:00
Dave Halter
d09279e0ad change tests that provided wrong instance information 2014-07-31 15:16:24 +02:00
Dave Halter
50fa3a732d actually start checking if the integration tests are instances on both sides of the comparison. This wasnt necessary for just autocompletion, but it's way more important now. 2014-07-31 14:58:32 +02:00
Dave Halter
d899f69686 simplify a lot of the current InstanceElement behavior, because we know now, that there's either a Statement or a Function inside (or maybe some other parser objects like an Array. 2014-07-31 13:41:10 +02:00
Dave Halter
0fbd5efefd wrap instance element creation so that it only contains functions and statements, not CompiledObject or Instance. 2014-07-31 13:16:11 +02:00
Dave Halter
870abe73d4 Calling an InstanceElement of an Instance of CompiledObject doesn't raise an error anymore. Yes, it's really that complicated. 2014-07-30 19:49:41 +02:00
Dave Halter
0851e7667e A module shouldn't be callable. 2014-07-30 17:07:57 +02:00
Dave Halter
723d1e4631 Nicer usage of py_call within InstanceElement and Python 2.7 compatibility 2014-07-30 17:00:16 +02:00
Dave Halter
7cc35fe0b8 remove a very old function call in FunctionExecution that had no effect 2014-07-30 16:41:02 +02:00
Dave Halter
cf63d20988 get rid of the evaluate_generator param 2014-07-30 16:36:27 +02:00
Dave Halter
565cfce2fe Executable -> Executed 2014-07-30 16:00:38 +02:00
Dave Halter
7bd76022bf get rid of the whole is_callable stuff, because now we can just check for hasattr(obj, 'py__call__') 2014-07-30 15:50:47 +02:00
Dave Halter
e58dc0a3d9 simplify evaluator.execute, because now everything is using py__call__ 2014-07-30 15:40:10 +02:00
Dave Halter
373ff2c45a fix most issues related to the py__call__ stuff and generalize it. 2014-07-30 15:23:41 +02:00
Dave Halter
1e6a950aec further progress in changing to py__call__ 2014-07-30 14:40:56 +02:00
Dave Halter
ccd304bcb7 start switching to a more python similar approach of naming, start by naming execution stuff py__call__ 2014-07-30 14:06:32 +02:00
Joel Wright
07d0a43f7e Add preceding whitespace collection to tokenizer 2014-07-30 11:59:20 +01:00
Dave Halter
196afaacbf always operate on class in super and not on an instance. that's the proper way. 2014-07-30 11:34:27 +02:00
Dave Halter
e81749bbe1 Merge branch 'dev' of github.com:davidhalter/jedi into dev 2014-07-30 11:27:46 +02:00
Dave Halter
3c92d175da using super() in actual executed classes wasn't possible. fixes #421 2014-07-30 11:27:27 +02:00
Dave Halter
53671bca84 replace get_super_classes with py_bases 2014-07-30 10:54:39 +02:00
Dave Halter
a6855029d2 added a few EuroPython sprint guys to AUTHORS.txt 2014-07-30 09:15:17 +02:00
Dave Halter
ddd4e92e84 temporary SuperInstance class to eventually handle super. But need something like mro() resolution first. 2014-07-29 23:57:29 +02:00
Dave Halter
cfe54e83ff incomplete functions shouldn't cause any trouble. fixes #429. 2014-07-28 17:42:20 +02:00
Dave Halter
a86cfa2dd7 Merge pull request #453 from alga/dev
Europython2014 davidhalter/jedi#361attempt
2014-07-27 20:58:03 +04:30
Albertas Agejevas
ecb2085174 Add flask.ext to the test fixture so tests pass even without flask installed. 2014-07-27 17:59:09 +02:00
Albertas Agejevas
25978cf591 Mentioned framework support in the docs. 2014-07-27 17:35:50 +02:00
Albertas Agejevas
ab486ba84f List old-style flask extensions, too. 2014-07-27 17:00:17 +02:00
Albertas Agejevas
733eee94b6 Fix breaking tests. 2014-07-27 16:18:24 +02:00
Albertas Agejevas
7f45bfe689 More on #361: enumerate new-style flask extensions. 2014-07-27 16:08:26 +02:00
Danilo Bargen
605b0c5881 Added note about numpydoc package
Refs #450.
2014-07-27 16:01:11 +02:00
Dave Halter
440b9b072e Merge pull request #441 from davidhalter/dynamic_inheritance
Dynamic superclasses
2014-07-27 18:26:48 +04:30
Dave Halter
4e04770a75 Merge pull request #451 from davidhalter/issue436
Issue 436: Operator statement wrapper was missing
2014-07-27 18:22:43 +04:30
Albertas Agejevas
5edd2274b2 Fix an exception in the flask ext code. 2014-07-27 15:04:55 +02:00
Albertas Agejevas
a18f8a7cbb Make tests terser. pytest rules! 2014-07-27 15:04:46 +02:00
Albertas Agejevas
13c1f79d5c A stab at davidhalter/jedi#361 (Flask extension imports)
Both new-style and old-style extensions work, but only when imported
with a 'from'.  There are two skipped tests of the full dotted name
imports.

Also, our fixture has a normal flaskext package, whereas in practice
the flaskext module is injected from a pth file and does not have
__init__.py, we need to figure out to handle that.
2014-07-27 15:04:31 +02:00
Danilo Bargen
e8f479172a Implemented dynamic superclasses 2014-07-27 14:11:48 +02:00
Danilo Bargen
73637d7e3f Small doc fix 2014-07-27 13:12:26 +02:00
Danilo Bargen
176da139d8 Added docs for numpydoc docstrings (fixes #450) 2014-07-27 13:10:14 +02:00
Danilo Bargen
c97e1732ee Operator statement wrapper was missing (fixes #436) 2014-07-27 12:53:18 +02:00
Dave Halter
6d99e639cd Merge branch 'add-numpydoc-support' of git://github.com/immerrr/jedi into dev 2014-07-27 11:23:39 +02:00
immerrr
194d87bbad Add basic numpydoc support 2014-07-27 12:51:31 +04:00
Dave Halter
e2cdbf61de Merge remote-tracking branch 'origin/travis_py34' into dev 2014-07-27 09:52:54 +02:00
Dave Halter
9028641ca7 Merge remote-tracking branch 'origin/namedtuple' into dev 2014-07-27 09:51:50 +02:00
Dave Halter
97a204a985 Merge branch 'dev' of github.com:davidhalter/jedi into dev 2014-07-27 09:44:25 +02:00
Dave Halter
606b6851ff remove the scope_names_generator stuff again. We should enable it somewhere in time, but for now it just breaks tests. 2014-07-27 09:43:22 +02:00
Dave Halter
cd648e933b Merge pull request #448 from ppalucki/master
Sphinx oneline param type declaration feature
2014-07-27 12:05:32 +04:30
Pawel Palucki
d359f5d043 Sphinx oneline param type declaration feature
allows for type definition in ":param keyword"
2014-07-26 22:15:56 +02:00
Danilo Bargen
d3620fd84f Implemented support for namedtuples (fixes #107)
Note that namedtuples are only supported for Python >2.6.
2014-07-26 17:51:38 +02:00
Danilo Bargen
81e066097d Added pytest-cache to tox.ini
This allows you to only run the last failed tests using `py.test --lf`
or `tox -- --lf`.
2014-07-26 17:48:46 +02:00
Danilo Bargen
49089c06ff Travis now provides Python 3.4 2014-07-26 17:41:32 +02:00
Danilo Bargen
efebb2d6d0 Added tests for random.choice 2014-07-26 17:40:08 +02:00
Danilo Bargen
2a1c108bbf Fixed whitespace problems in completion tests 2014-07-26 17:39:05 +02:00
Dave Halter
c85bdb8ff1 little edge case of modules that don't have a scope_names_generator, which is unfortunately missing, but not really used in Jedi.
At europython's hackathon we played with it and @scoder added a small script to cython/Cython/Compiler/JediTyper.py, which makes it possible to add Cython types to a Python script.
2014-07-26 13:18:04 +02:00
Dave Halter
4f1d39d3df set changelog date for v0.8.1 correct 2014-07-25 00:45:39 +02:00
Dave Halter
293fa5a14f Merge pull request #437 from blueyed/doc-changelog-minor
Minor fixes to the changelog
2014-07-25 03:14:06 +04:30
Daniel Hahler
fd2f56f3b6 minor: changelog: formatting 2014-07-25 00:31:17 +02:00
Daniel Hahler
93f6d45e11 minor: changelog: fix typo 2014-07-25 00:30:53 +02:00
Dave Halter
a01e4c6b37 Merge remote-tracking branch 'origin/master' into dev 2014-07-23 00:47:30 +02:00
Dave Halter
5e9d9573d5 Don't warn on addition of an int literal plus an unknown int number. 2014-07-22 16:44:10 +02:00
Dave Halter
f7c8c43fbc check not only - but also + for numbers. 2014-07-22 16:18:57 +02:00
Dave Halter
96ca596cc2 fix the operation test where static analysis reports additions of ints with non-ints. 2014-07-22 16:05:46 +02:00
Dave Halter
852cdad754 Operator fixes. Subclass of Simple, now. 2014-07-22 16:02:34 +02:00
Dave Halter
15f42d93d7 it should be possible to find the origin of an operation, if it's a faulty one (static analysis) 2014-07-22 14:52:58 +02:00
Dave Halter
50ceef6e09 1 + '1' TypeErrors are now detected, but not shown in the right place 2014-07-22 10:44:56 +02:00
Dave Halter
52bbedd4a8 renamed company-jedi to anaconda-mode, cc @proofit404 2014-07-22 10:33:49 +02:00
Dave Halter
037d5fa02a static analysis tests for the 1 + '1' test 2014-07-22 01:40:36 +02:00
Dave Halter
d350c1fa30 Merge branch 'linter' of github.com:davidhalter/jedi into linter 2014-07-21 17:40:25 +02:00
Dave Halter
b0d5d96b20 Merge pull request #435 from hattya/windows
fixes for Windows
2014-07-21 16:38:53 +02:00
Dave Halter
ec690b9ec5 change the default linter command from 'force' to 'linter' 2014-07-21 16:15:42 +02:00
Akinori Hattori
712e5653d8 use _ctypes for extension tests 2014-07-19 14:39:14 +09:00
Akinori Hattori
10b7ed967d organize imports 2014-07-19 10:41:19 +09:00
Akinori Hattori
00b8263859 fix buildout test for Windows 2014-07-19 10:39:45 +09:00
Akinori Hattori
11bc105207 skip readline test on Windows 2014-07-19 10:37:46 +09:00
Akinori Hattori
68150f2814 fix module name of integration tests for Windows 2014-07-19 10:36:31 +09:00
Akinori Hattori
5b15c0ba84 fix package detection for Windows 2014-07-19 10:34:15 +09:00
Akinori Hattori
2696d95d70 fix dotted_from_fs_path for Windows 2014-07-19 10:33:08 +09:00
Dave Halter
2616143d10 unicode issues with docstrings should be gone, fixes #420 2014-07-18 17:43:25 +02:00
Dave Halter
e07f51387f added a test for hex value issues in docstrings, see #427 2014-07-18 17:09:44 +02:00
Dave Halter
ee1c5041ed use the new implementation of splitlines all over the code, fixes #424 2014-07-18 16:59:22 +02:00
Dave Halter
ffaacbefbc new splitlines implementation to get rid of the ugly splitlines we now have as well as (partially) the issue with form feeds 2014-07-18 16:52:55 +02:00
Dave Halter
0f665bf436 test for #424, issues with form feeds 2014-07-18 16:52:25 +02:00
Dave Halter
81f3b940e8 dicts should not be used to check against in get_defined_names, because they cannot contain a defined name (lists and tuples can) so just ignore them., fixes #417 2014-07-18 15:37:10 +02:00
Dave Halter
4626a8b6df test for #417 2014-07-18 15:23:47 +02:00
Dave Halter
21341283ca another parser issue, very much related to the last one. sometimes a None element was used as a token.fixes #418 2014-07-18 15:10:10 +02:00
Dave Halter
0dea47b260 def/class keywords after an opening parentheses led to empty arrays without closing brackets, tests & fixes #416 2014-07-18 12:53:06 +02:00
Dave Halter
3be5220bf1 parentheses checks in the fast parser (tokenizer) have been improved to really cover all cases. real fix for davidhalter/jedi-vim#288 2014-07-18 10:59:28 +02:00
Dave Halter
03226783dd parentheses should be ignored when calculating the indent of a new block in the fast parser, fixes davidhalter/jedi-vim#288 2014-07-18 09:11:06 +02:00
Dave Halter
08f7a439c0 merged dev and linter 2014-07-17 23:17:49 +02:00
Dave Halter
a1bc644bfb fix a small issue in the alternative test runner 2014-07-17 22:47:54 +02:00
Dave Halter
9eec2b2794 Merge pull request #434 from hattya/last-part
keep newline at end of code
2014-07-17 20:21:21 +04:30
Akinori Hattori
e14b144199 keep newline at end of code 2014-07-17 20:37:25 +09:00
Dave Halter
4852c7840f Merge pull request #433 from hattya/pytest
update pytest to latest stable
2014-07-17 14:28:31 +04:30
Dave Halter
ef58f0e8e0 Merge pull request #430 from hattya/dev
improve fast parser for incremental parsing
2014-07-17 14:24:17 +04:30
Akinori Hattori
1eeb5677fa update pytest to latest stable 2014-07-17 18:35:31 +09:00
Akinori Hattori
3f75ea5cc7 skip newline at end of code 2014-07-17 18:29:00 +09:00
Dave Halter
53a32d8304 test for parser issues with parentheses indentation, see davidhalter/jedi-vim#288 2014-07-15 17:05:16 +02:00
Akinori Hattori
feae67484c CRLF should be also treated as blank line 2014-07-08 20:21:45 +09:00
Akinori Hattori
da89b66594 use generator to reduce memory usage 2014-07-06 11:53:22 +09:00
Akinori Hattori
1650f65507 reduce loops for finding sub parser 2014-07-06 11:29:24 +09:00
Akinori Hattori
aab4891c4e reduce regex searches and compile pattern 2014-07-06 11:11:23 +09:00
Akinori Hattori
0610ef16ae use del instead of assigning empty iterator 2014-07-06 11:07:19 +09:00
Dave Halter
22e5574a91 Remove some get_defined_names methods, that are not needed anymore. 2014-07-03 12:12:50 +02:00
Dave Halter
1fd7acef7a finally able to remove _get_defined_names_for_position 2014-07-03 11:53:51 +02:00
Dave Halter
c1ed3bf38a cleanup NameFinder.scopes, fully use scope_names_generator. 2014-07-03 11:48:26 +02:00
Dave Halter
ea370a083d more consequent usage of scope_names_generator 2014-07-03 11:41:33 +02:00
Dave Halter
a7e4d81692 also add scope_names_generator to the iterable module classes, as well as cleaning up some old scope_names_generator stuff 2014-07-02 18:58:31 +02:00
Dave Halter
3264a1815e Merge pull request #407 from hattya/dev
fix virtualenv support
2014-07-02 21:16:51 +04:30
Dave Halter
13ada3154b replace get_defined_names with scope_names_generator in a lot of places (the cleanup still needs to be done, though). 2014-07-02 16:12:49 +02:00
Dave Halter
749d0121fc change test results, because of inserts in another test file 2014-07-02 13:15:36 +02:00
Dave Halter
23008d8a19 use scope_names_generator in completion api as well. hopefully the last __file__ related issue :) 2014-07-02 13:12:37 +02:00
Dave Halter
ea72b46fe8 Also update the changelog temporarily. 2014-07-02 12:49:36 +02:00
Dave Halter
bb50c285f1 Bump release to 0.8.1-final0, because we're doing a release soon. 2014-07-02 12:47:44 +02:00
Dave Halter
7d8c1e8810 pre-alpha notice in static analysis API 2014-07-02 12:46:57 +02:00
Dave Halter
8d395a2ef1 scope_names_generator now works with modules perfectly well. 2014-07-02 12:41:16 +02:00
Dave Halter
789d48b7e3 fix issues introduced with defined_names. 2014-07-02 11:01:42 +02:00
Dave Halter
3865c1a844 Fixed __file__ issues by always applying a ModuleWrapper in the global scope lookup. 2014-07-01 15:35:21 +02:00
Dave Halter
8d63e6f6e7 somehow temporary solution to enable completion of __file__ 2014-07-01 15:10:32 +02:00
Dave Halter
f7a384bf18 fix a multi line issue of var_args with an error token or newline in the beginning. 2014-07-01 12:45:34 +02:00
Dave Halter
4ba1c95317 multiple files should be scannable in the main analysis API. 2014-07-01 02:17:28 +02:00
Dave Halter
ede685c717 string prefixes are now recognized by the backwards tokenizer 2014-07-01 01:19:07 +02:00
Dave Halter
5099c44593 exceptions were ignored in jedis static analysis. They shouldn't be. 2014-06-30 15:57:24 +02:00
Dave Halter
58d7dac92f fix dict issue with **kwargs use 2014-06-30 15:54:49 +02:00
Dave Halter
0b99473886 ExecutedParams should never be additionally faked, even if they are the first params. They have been legitimately created by a caller. 2014-06-30 15:22:53 +02:00
Dave Halter
0d3ea4dfb4 fix a ModuleWrapper with StarImports. Now all modules are wrapped. 2014-06-29 21:04:52 +02:00
Dave Halter
5b7c869323 types also add to completions, also for compiled objects. removed a few lines of code that complicated the process as well. 2014-06-28 12:09:43 +02:00
Dave Halter
7d73e571bb json.load[s] shouldn't return any results. fixed by overwriting the method 2014-06-27 11:56:40 +02:00
Dave Halter
cf1fd691da custom copy.copy and copy.deepcopy implementations to not confuse autocompletion (just return the first param) 2014-06-27 11:49:26 +02:00
Dave Halter
8cd7f9a288 std -> stdlib 2014-06-27 11:24:23 +02:00
Dave Halter
aba4a16ae3 fix indexing issues for multiple index options 2014-06-27 11:23:46 +02:00
Dave Halter
4c849f5969 make it possible to access pdb with 'python -m jedi force --pdb' 2014-06-26 16:14:39 +02:00
Dave Halter
d444ef9e15 setdefault fix 2014-06-26 15:23:20 +02:00
Dave Halter
1c9058ce6b Also issue warnings if setattr is used in a class instead of an error 2014-06-26 13:40:15 +02:00
Dave Halter
4238538df4 Add __getattr__ checks with proper inheritance. 2014-06-26 12:56:01 +02:00
Dave Halter
a936cea987 jedi issues now warnings instead of errors for AttributeErrors that happen in instances with __getattr__/__getattribute__ methods 2014-06-26 12:21:19 +02:00
Dave Halter
7e0edc4776 preparation for warnings in static analysis 2014-06-26 11:57:44 +02:00
Dave Halter
bdcbac160b fix string/array (sequence) multiplications with integer. 2014-06-26 00:49:56 +02:00
Dave Halter
47205dd7f3 change the implementation of compiled.load_module and always use the sys.modules cache after an import, because it's easier. Doesn't require any logic. 2014-06-25 18:57:07 +02:00
Dave Halter
e5efd6e5c8 add a setting auto_import_modules to fix autocompletion for modules that use setattr and companions a lot. fixes #151 2014-06-25 17:14:31 +02:00
Dave Halter
01869e4100 make a translation from file system paths to dotted paths possible 2014-06-25 16:33:25 +02:00
Dave Halter
718df569ea memoize sys_path modifications 2014-06-25 15:04:48 +02:00
Dave Halter
6e82fa31e1 submodules need relative imports not absolute 2014-06-25 11:08:29 +02:00
Dave Halter
44238a9f92 submodules are automatically indexed without actually importing them. fixes #413.
However, this is not a 100% correct Python behavior. Python behavior would be to follow ALL imports in all modules (recursively) and check if the module was imported. However, that's a lot of work, that would slow down autocompletion. For now it's better to have no false positives in flaking and to ignore a few attribute errors.
2014-06-25 01:39:43 +02:00
Dave Halter
034a818863 repr improvements 2014-06-24 13:42:40 +02:00
Dave Halter
393833059a fixed inheritance for exception checks 2014-06-23 13:01:12 +02:00
Dave Halter
c1181a0459 fix except: usage in analysis 2014-06-23 12:13:10 +02:00
Dave Halter
9348d4bb6c jedi should be able to detect exceptions even in except X: statements 2014-06-23 12:07:51 +02:00
Dave Halter
e106e4ffc8 fixed for loop in exception issue combined with usage of an exception variable 2014-06-22 23:32:07 +02:00
Dave Halter
401914e91c exception elements are always instances 2014-06-22 16:25:42 +02:00
Dave Halter
3e0f719915 changed the way how the dynamic param function searches it's parent scope, to enable smooth list comprehension following 2014-06-22 13:48:30 +02:00
Dave Halter
b7bf8d515c support for completions on return statements. 2014-06-22 12:05:22 +02:00
Dave Halter
d752907290 temporary solution for completions in asserts 2014-06-22 11:27:28 +02:00
Dave Halter
ddca14980e introduce an is_scope function to make it easier to work with scopes 2014-06-20 17:47:42 +02:00
Dave Halter
3ee3a04bcb fix list comprehension issues in nested parentheses. 2014-06-20 17:29:30 +02:00
Dave Halter
2fc404f99d fix issue with list comprehensions in function calls 2014-06-20 16:28:31 +02:00
Dave Halter
8c924afdb8 manifest was missing the fake paths, which makes the last release a bad one. This means basically that in the last (few?) release(s) the stdlib was not correctly supported. 2014-06-20 16:25:05 +02:00
Dave Halter
ea271c8047 replace _sre fake module literals with undefined types. 2014-06-20 11:18:54 +02:00
Dave Halter
be3ac0b1c0 fix issue with reordering var_args 2014-06-20 00:35:25 +02:00
Dave Halter
bbc5ad748d Merge pull request #423 from syohex/fix-package_data
Fix package_data '*.pym' paths
2014-06-19 15:07:08 +04:30
Dave Halter
36fbb6cd3e reorder var_args if named arguments are in front of *args. 2014-06-19 12:18:24 +02:00
Syohei YOSHIDA
605ab9c6f5 Fix package_data '*.pym' paths 2014-06-19 19:10:58 +09:00
Dave Halter
6edff1d952 Merge pull request #403 from mfussenegger/dev_buildout
detect buildout and add buildout eggs to sys.path
2014-06-19 02:05:31 +04:30
Mathias Fussenegger
bf43fcf1c6 detect buildout and add buildout eggs to sys.path 2014-06-18 18:30:11 +02:00
Dave Halter
a373818965 fix function execution mutable list issue 2014-06-16 17:12:27 +02:00
Dave Halter
eb1f299444 function repr should only include the decorated function if it actually is one. 2014-06-16 13:06:53 +02:00
Dave Halter
8aeac478a5 message improvement for param failure 2014-06-13 12:20:08 +02:00
Dave Halter
acfa40afa7 *args without self but still an implicit self from a method decorator 2014-06-12 22:42:15 +02:00
Dave Halter
371ec888e9 further test for list comprehensions 2014-06-12 11:20:46 +02:00
Dave Halter
d5758adb2b fix list comprehensions. they were not implemented in a good way 2014-06-12 11:10:10 +02:00
Dave Halter
f5f8d99233 Merge pull request #422 from fbergroth/fix-completion-params
Fix completion params
2014-06-12 13:10:40 +04:30
Dave Halter
f8b79b3dd0 work in progress refactoring to make ListComprehension a sublass of ForFlow 2014-06-12 10:13:49 +02:00
Fredrik Bergroth
d3ac1e902e Always cast pr to er in _follow_statements_imports 2014-06-11 23:49:36 +02:00
Dave Halter
43e54b6173 list comprehensions should be able to serve as an input for dynamic params as well. 2014-06-11 21:54:18 +02:00
Dave Halter
63868feb5d hasattr checks working now, #408 2014-06-10 16:08:53 +02:00
Dave Halter
bba120d906 hasattr test for static analysis, it's a common idiom. 2014-06-10 11:15:59 +02:00
Dave Halter
c6aea92753 fix issues with tokens in expression list 2014-06-10 11:03:36 +02:00
Dave Halter
e6331f8ac8 fix issues with the previous commits (broken tests) 2014-06-10 01:13:37 +02:00
Dave Halter
081fa79d9b fix issues with generator comprehensions used directly with a send() call or something similar 2014-06-10 00:56:51 +02:00
Dave Halter
9cffbef608 tests for generator to tuple assignment as well as generator comprehensions 2014-06-10 00:40:38 +02:00
Dave Halter
af801ef9b4 make generator comprehensions work 2014-06-10 00:36:36 +02:00
Dave Halter
cd5b8aebfd fix issues with equal names before and after listcomprehension 2014-06-09 20:28:24 +02:00
Dave Halter
0b926ca454 get rid of is_list_comp boolean in favor of a direct check of ListComprehensionFlow 2014-06-09 20:19:31 +02:00
Dave Halter
dd8e4341db create a ListComprehensionFlow, to make the distinction between ForFlow and list comprehensions clearer 2014-06-09 20:09:53 +02:00
Dave Halter
c48146093e test: list comprehension name resolve should not include its own definitions 2014-06-09 12:53:17 +02:00
Dave Halter
496671966b instance issue with param static analysis 2014-06-09 01:59:54 +02:00
Dave Halter
920eb3b06a fix a default argument issue 2014-06-08 14:19:22 +02:00
Dave Halter
c8b7d79b54 erroneus star arguments warning 2014-06-07 13:10:19 +02:00
Dave Halter
62db176e5e cleanup 2014-06-07 12:36:16 +02:00
Dave Halter
f061de0f74 Wrong var_args with a star star function. 2014-06-06 16:49:53 +02:00
Dave Halter
cb430c4c36 add a merged array class to account for array additions 2014-06-06 02:51:48 +02:00
Dave Halter
8798f5b1d7 add comments to the *args/**kwargs merge code 2014-06-05 12:08:08 +02:00
Dave Halter
cf7b5b6b2b disable two failing multiple value tests. These are things jedi is not able to detect at the moment. It's not a huge problem, but it would be very nice if we could detect these as well. But there would be a need of restructuring var_args unpacking. 2014-06-05 11:54:46 +02:00
Dave Halter
6f83eb65ce raise multiple key errors also if they are an input to kwargs 2014-06-05 10:35:44 +02:00
Dave Halter
acb4959a6a temporary very unfinished solution for the *args/**kwargs combination problem, if they are used in common with dynamic params. This doesn't solve the issue entirely, but it's at least a start and will probably solve all autocompletion issues. However, static analysis needs a little bit more than that. 2014-06-04 17:18:09 +02:00
Dave Halter
945888a535 fix for kwargs params 2014-06-01 13:52:21 +02:00
Dave Halter
933e231d74 small update on multiple value named argument type error 2014-06-01 11:34:20 +02:00
Dave Halter
b8525c7e1e get dicts partially working 2014-06-01 11:24:24 +02:00
Dave Halter
248cca2e5e fix issues with empty *args as inputs 2014-05-31 11:03:37 +02:00
Dave Halter
f4a508ac53 handle *args arguments the right way. 2014-05-29 20:53:51 +02:00
Dave Halter
b24178b275 multiple values refactoring in params 2014-05-29 16:59:56 +02:00
Dave Halter
1899f16a4a if there's a func listener, stop the execution of a function. This solves the issue of nested *args that were reported as having too many params in static analysis. 2014-05-29 12:15:07 +02:00
Dave Halter
4f66591227 nested functions with *args should only raise an error if there's well defined input. 2014-05-28 14:35:48 +02:00
Dave Halter
a695166585 add a new static_analysis file to test star arguments separately 2014-05-28 11:08:44 +02:00
Dave Halter
11e867d2c1 fix calling_var_args with tuples 2014-05-28 11:00:24 +02:00
Dave Halter
23edfd27ad detect origin of a call in case of missing params. This is important, because the user doesn't care about decorators in between. 2014-05-28 02:30:35 +02:00
Dave Halter
b7aaec50e3 add ExecutedParam instead of using copy.copy 2014-05-28 00:50:14 +02:00
Dave Halter
40c2d64bac use the internal api to get a param name instead of doing crazy stuff 2014-05-27 15:04:22 +02:00
Dave Halter
f3e986a285 add multiple values for keyword type error detection 2014-05-26 18:40:02 +02:00
Dave Halter
720907531b small corrections in too few argument errors 2014-05-26 17:38:14 +02:00
Dave Halter
425f7a8b64 better error reporting for static analysis 2014-05-26 17:31:50 +02:00
Dave Halter
3a946ab549 fix for the newly created keyword/default tests 2014-05-26 12:37:47 +02:00
Dave Halter
f71e2d5b8f keyword/default param tests 2014-05-26 11:21:16 +02:00
Dave Halter
e46979c354 improved static analysis found mistakes in its own test suite 2014-05-26 10:51:19 +02:00
Dave Halter
ad120f529d param.py doesn't seem to be needing some default value checks 2014-05-26 10:50:41 +02:00
Dave Halter
0d0d123393 Merge pull request #415 from pombredanne/patch-1
Fixed comment typo
2014-05-25 18:29:42 +04:30
Philippe Ombredanne
6f69d7d17f Fixed comment typo 2014-05-25 15:38:57 +02:00
Dave Halter
a621662440 some param refactorings. 2014-05-23 19:54:27 +02:00
Dave Halter
d9f17beea5 unexpected keyword arguments detection 2014-05-23 15:34:16 +02:00
Dave Halter
acd836f30d new named arguments tests for static analysis 2014-05-23 15:18:55 +02:00
Dave Halter
9214e0b358 move a closure out of get_params 2014-05-22 10:58:38 +02:00
Dave Halter
9d4dc546ca first version of too few params detection 2014-05-21 13:01:12 +02:00
Dave Halter
4ecc150d85 first version of too many arguments detection 2014-05-21 12:30:51 +02:00
Dave Halter
a252d825f2 remove an unimportant test case that showed strange unreproducible behavior on travis. 2014-05-20 16:47:23 +02:00
Dave Halter
f57b53bbe2 check for 'if foo is not None' checks in the NameFinder. Solves the issues with the subprocess library. 2014-05-20 16:23:46 +02:00
Dave Halter
79556a7935 finder docstring and naming improvements 2014-05-20 15:17:07 +02:00
Dave Halter
ad762f674e renaming of an unclear variable 2014-05-20 14:01:43 +02:00
Dave Halter
dd50001ed1 update pickling version, because we have changed some things about the KeywordStatement a while ago 2014-05-19 13:49:52 +02:00
Dave Halter
77baabb93b % operation returned both left and right side, but only the left side is really important. 2014-05-19 13:26:12 +02:00
Dave Halter
a717981679 more extensive __file__ tests, #408 2014-05-19 01:03:40 +02:00
Dave Halter
08b48807e9 a (temporary) solution for the __file__ access issues in imported modules, see #408 2014-05-19 01:01:56 +02:00
Dave Halter
709c53a679 empty reversed issue, fixes traceback of #408 2014-05-17 12:32:18 +02:00
Dave Halter
41f32f21ea Merge pull request #411 from jorgenschaefer/fix-deprecation-docstring-typo
Fix deprecation docstring typo
2014-05-17 13:04:45 +04:30
Jorgen Schaefer
1cbbc00089 Fix names in docstrings and DeprecationWarnings.
Update a number of docstrings and DeprecationWarnings to refer to
the correct methods or attributes.
2014-05-17 10:21:44 +02:00
Dave Halter
06bae0e835 fix test issue with python 2.7 2014-05-16 18:03:36 +02:00
Dave Halter
ecf9043d97 Improved error messages for AttributeErrors, however not a final version #408 2014-05-16 18:02:33 +02:00
Dave Halter
ca2cc65686 improved import positioning errors in static analysis 2014-05-16 17:20:45 +02:00
Dave Halter
9bf50e6022 better knowledge in the importer where the import names originate from 2014-05-16 17:05:43 +02:00
Dave Halter
87704ec16a custom message capability for analysis 2014-05-16 15:46:08 +02:00
Dave Halter
11b7f9f7f6 decorators should also be included in the static analysis 2014-05-16 15:33:21 +02:00
Dave Halter
0f7a17090c static analysis start positions are now tested 2014-05-16 15:03:59 +02:00
Dave Halter
552502a2e9 list comprehension static analysis test 2014-05-16 14:31:53 +02:00
Dave Halter
4e596060b9 test for is_nested failure 2014-05-16 13:00:13 +02:00
Dave Halter
8e27ed556e fix returns/flow command edge cases 2014-05-16 12:33:00 +02:00
Dave Halter
d59e21f43c new way of gathering statements to evaluate for static analysis 2014-05-16 12:23:09 +02:00
Dave Halter
857a9b7621 don't raise attribute NameErrors in all for loop name definitions. 2014-05-15 12:39:00 +02:00
Dave Halter
bcab821df9 linter output changes - #408 2014-05-15 00:45:50 +02:00
Dave Halter
b54d46374c recursive file paths for the temporary linter api - #408 2014-05-15 00:39:42 +02:00
Dave Halter
5e2bb0ef9b Using python -m jedi force <path> you can use the linter for now. 2014-05-13 16:44:46 +02:00
Dave Halter
99340dd2a1 few NameError tests 2014-05-13 16:17:25 +02:00
Dave Halter
9bcc4f8fd2 static analysis is now able to tell the difference between NameError/AttributeError 2014-05-13 16:14:32 +02:00
Dave Halter
7632a7d120 sorting the statements for analysis. we need to be able to reproduce results. 2014-05-13 15:56:41 +02:00
Dave Halter
876942d2b8 Small adjustment in Interpreter completion to be compatible with static analysis 2014-05-13 01:39:33 +02:00
Dave Halter
a2b483b4f5 None issue fix for static analysis 2014-05-13 01:21:32 +02:00
Dave Halter
00e43d4585 except can also catch multiple exceptions in one statement 2014-05-12 18:46:17 +02:00
Dave Halter
7096a570bf try/except test for static analysis (duck typing should not cause jedi to report mistakes) 2014-05-12 18:34:38 +02:00
Dave Halter
96386b4578 if something catches an exception, ignore that report 2014-05-12 18:10:17 +02:00
Dave Halter
64af9524b7 simple generator static analysis subscript check 2014-05-12 16:06:28 +02:00
Dave Halter
b6ec589997 refactor general array lookup method get_index_types 2014-05-12 15:23:48 +02:00
Dave Halter
70d85d1b3a strip_imports -> follow_imports 2014-05-12 11:18:47 +02:00
Dave Halter
e5fe726862 imports cleanup & documentation 2014-05-12 11:15:17 +02:00
Dave Halter
02d1e1aa42 fix static analysis for nested imports 2014-05-12 11:03:27 +02:00
Dave Halter
13949ec145 move is_nested check from evaluate.imports to the parser 2014-05-12 11:02:57 +02:00
Dave Halter
04855e9452 pytest -> ignore static_analysis folder 2014-05-12 01:59:00 +02:00
Dave Halter
63155808df interpreter cleanup, use proper parents 2014-05-12 01:55:48 +02:00
Dave Halter
3f2e737702 statical analysis shouldn't report the exact same error twice 2014-05-11 16:45:22 +02:00
Dave Halter
7abdc375c2 add tests for attribute errors 2014-05-11 16:44:58 +02:00
Dave Halter
284a64a79a more import-error detection tests 2014-05-11 15:33:53 +02:00
Dave Halter
7b525285bd static analysis import tests 2014-05-11 15:18:48 +02:00
Dave Halter
c92113a7b1 improved static analysis test base 2014-05-11 13:30:29 +02:00
Dave Halter
40a54961cd testing structure for static analysis. 2014-05-11 12:09:42 +02:00
Dave Halter
c59a8dce28 ImportError detection 2014-05-11 01:54:25 +02:00
Dave Halter
586ac9d013 removed a few debug things. 2014-05-10 20:31:33 +02:00
Dave Halter
11a445ab42 Merge branch 'dev' into linter 2014-05-10 16:53:41 +02:00
Dave Halter
60971245d6 star imports now have their own class, which will be important for AttributeError detection 2014-05-10 16:53:27 +02:00
Dave Halter
358472b21f improved star import support preparation 2014-05-10 14:12:36 +02:00
Dave Halter
ecfb3a0423 Merge branch 'dev' into linter 2014-05-09 11:52:39 +02:00
Dave Halter
d0b2a2ce4f fixed NestedImportModule 2014-05-09 11:52:10 +02:00
Dave Halter
2d48c72340 introduce a NestedImportModule class 2014-05-08 13:22:41 +02:00
Dave Halter
6098ba5e84 small fixes to the analysis.Error class. 2014-05-08 12:04:40 +02:00
Akinori Hattori
1ecb173b33 update AUTHORS 2014-05-08 18:45:51 +09:00
Dave Halter
7c965e544f dive further into following imports, etc 2014-05-08 11:26:08 +02:00
Akinori Hattori
9f3542903d fix virtualenv support 2014-05-08 18:09:05 +09:00
Dave Halter
9eb75f9c61 static analysis prototype decription 2014-05-07 12:39:18 +02:00
Dave Halter
c0064e17bc Merge pull request #406 from davidhalter/repl_info
Print the Jedi version when REPL completion is used
2014-05-06 12:58:33 +04:30
Danilo Bargen
ae8b0d5eab Print the Jedi version when REPL completion is used
This also makes debugging easier, because people see which completion
they're actually using.
2014-05-06 08:17:09 +02:00
Danilo Bargen
e66f2d8f4b Docs / changelog formatting 2014-05-06 07:47:59 +02:00
Danilo Bargen
db9ee1f5a0 Updated AUTHORS 2014-05-06 07:39:31 +02:00
Dave Halter
895db8d6ff changelog mistake again, thanks for noticing it @aebersold 2014-05-05 23:50:45 +02:00
Dave Halter
c587d12510 removed call signature caching unintentionally (a few commmits ago). 2014-05-05 12:45:19 +02:00
Dave Halter
7db1eb2f62 change small changelog mistake 2014-05-05 00:41:34 +02:00
Dave Halter
7b402d7bbe change release to 0.8.0-final0, #395 2014-05-05 00:23:07 +02:00
Dave Halter
b5b79fc818 partial support for *args/**kwargs in CallSignature.index, fixes #395 2014-05-05 00:12:43 +02:00
Dave Halter
f2af053a4a fix the CallSignature.index attribute 2014-05-04 23:36:24 +02:00
Dave Halter
a4b9ccc2e7 the fast parser was able to return wrong sub parsers, because the sometimes hashes were not updated, fixes #396. 2014-05-04 16:36:41 +02:00
Dave Halter
16fd7f5424 create a more specific test for #396, which is again a fast parser issue 2014-05-04 15:53:53 +02:00
Dave Halter
672594514e call_signature/completion interference issues 2014-05-04 13:12:49 +02:00
Dave Halter
fcd8b25d3d the parser in general now cares for carriage return/new line combinations 2014-05-04 12:32:02 +02:00
Dave Halter
f64b309ff0 carriage return test for statements that continue, even though they shouldn't 2014-05-04 12:31:21 +02:00
Dave Halter
ef62904af3 python 2 compatibility (also with the new travis build) 2014-05-04 11:50:13 +02:00
Dave Halter
02b98ad4e4 small carriage return fix, so that Definition.description doesn't return crazy carriage returns without a newline 2014-05-04 02:39:57 +02:00
Dave Halter
e0d0572d7f fix for an issue with commas in statements 2014-05-04 02:20:46 +02:00
Dave Halter
8404107397 carriage return issues fixed in the fast parser -> fixes #402 2014-05-04 02:14:34 +02:00
Dave Halter
091518d924 test for carriage return issues. fast parser seems to be counting characters wrong when carriage returns are involved. see #402 2014-05-04 01:56:53 +02:00
Dave Halter
065e9a001d call signature test change to avoid version changes of python 3.2.5 (int docstrings have changed, travis has updated since) 2014-05-03 20:12:39 +02:00
Dave Halter
c44b88adc3 Error token handling with dots in front. 2014-05-03 12:39:20 +02:00
Dave Halter
e5326acf8f ImportPath -> ImportWrapper 2014-04-30 17:15:59 +02:00
Dave Halter
0322869202 catch ModuleNotFound for a special imports usage 2014-04-30 17:09:46 +02:00
Dave Halter
2205117f92 test for #397, something raises ModuleNotFound if we use Definition.type 2014-04-30 16:58:27 +02:00
Dave Halter
5ed887c170 precautionary fix without test (sith.py didn't reproduce it), in execution detection. 2014-04-30 12:21:18 +02:00
Dave Halter
44d8abb198 fix for the recursive ArrayInstance issues. 2014-04-30 12:20:28 +02:00
Dave Halter
5a6eabb364 array recursion test, took a long time to find that annoying bug. 2014-04-30 11:50:21 +02:00
Dave Halter
8edaea31e2 simplify some usage stuff 2014-04-29 14:01:24 +02:00
Dave Halter
bc7896f93d unicode issues in usage matching 2014-04-29 12:20:25 +02:00
Dave Halter
5740c45791 again tokenize simplifications 2014-04-28 19:31:41 +02:00
Dave Halter
18dc92f85f removed a few old/unnecessary tokenize definitions 2014-04-28 18:33:40 +02:00
Dave Halter
23b4a89d1d slices should be ignored in __getitem__ settings (for now) 2014-04-28 18:15:25 +02:00
Dave Halter
d106b2ce2b __getattr__ arbitrary returns fix 2014-04-27 23:15:32 +02:00
Dave Halter
45d3bbff0d sometimes the speed test fails, because there's not enough margin on travis 2014-04-25 15:12:56 +02:00
Dave Halter
289dbc8629 Merge branch 'dev' of github.com:davidhalter/jedi into dev 2014-04-25 15:03:36 +02:00
Dave Halter
a6f962f1c2 clean up some precedence stuff 2014-04-25 14:39:59 +02:00
Dave Halter
6bec7ce847 speed up Builtin lookups 2014-04-25 14:12:28 +02:00
Dave Halter
c790f88678 speed up precedences. 2014-04-25 13:28:01 +02:00
Dave Halter
10809e836d speed test for slow precedence issues 2014-04-24 16:33:17 +02:00
Dave Halter
ecd9470803 Merge pull request #399 from jorgenschaefer/dev
Always load source files in binary mode.
2014-04-24 14:09:01 +04:30
Jorgen Schaefer
9ed0dc4861 Always load source files in binary mode.
Source files can be in any coding system, provided Python can
read a coding: line at the beginning of the file. So source files
should be loaded in binary format and decoded according to that
line, not assumed to be in the default coding system.

Fixes #398.
2014-04-24 11:10:59 +02:00
Dave Halter
4ae99256be py3.0, py3.1 and py32 don't support unicode literals. Support those. 2014-04-22 16:00:13 +02:00
Dave Halter
a49c624154 tokenize corrections, add unicode literals, because they had been removed from Python 3.2 (reintroduced in 3.3) 2014-04-22 15:17:48 +02:00
Dave Halter
b685101efb Nothing + string literal should not result in string literal but in a string type (because we don't know how the string literal could have looked like 2014-04-22 15:16:48 +02:00
Dave Halter
77d505e251 eval_statement input shouldn't be a NamePart, but a string 2014-04-22 11:33:46 +02:00
Dave Halter
8dc7f6a771 small NO_DEFAULT fix for a memoize issue with iterable.Array 2014-04-22 10:29:06 +02:00
Dave Halter
70efa159f2 fix for slices which have been screwed up in a few previous commits... again... 2014-04-22 10:21:15 +02:00
Dave Halter
2a0423302c small evaluate.iterable.Array optimization 2014-04-22 10:13:23 +02:00
Dave Halter
eaf54942fc fix a slice issue with precedences. 2014-04-21 02:30:17 +02:00
Dave Halter
e320c3a8a0 Merge branch 'dev' of github.com:davidhalter/jedi into dev 2014-04-20 22:17:39 +02:00
Dave Halter
fe5a03badd Increase maximum recursion depth. Makes sense for very complicated code (completions within Jedi), since Jedi itself uses so many recursions. 2014-04-20 14:59:42 +02:00
Dave Halter
67f49da15c Increase maximum recursion depth. Makes sense for very complicated code (completions within Jedi), since Jedi itself uses so many recursions. 2014-04-20 14:55:05 +02:00
Dave Halter
81cc50e8b5 well yeah give NamePart a get_code method after all, makes sense, because all the other parser representation objects have it. 2014-04-20 14:32:48 +02:00
Dave Halter
50c4b7bfd9 memoize problems with defaults in combination with raised exceptions 2014-04-20 14:14:30 +02:00
Dave Halter
27f01ca1f6 use Param.stars to avoid certain syntax error params to cause an exception 2014-04-20 13:38:10 +02:00
Dave Halter
ab154d46d5 fixed a very nasty problem with exponential growth in precedence for loops 2014-04-20 12:42:53 +02:00
Dave Halter
6f17000fa8 test/completion/operators.py -> precedence.py 2014-04-20 12:14:07 +02:00
Dave Halter
7682f204fb make it possible to show locations with sith.py before something is executed. This makes never ending completions traceable. 2014-04-20 01:54:16 +02:00
Dave Halter
b1a8a15486 removed a lot of old sith.py code, that is not needed anymore, because almost everything is now a Definition (except Completions). 2014-04-20 01:40:48 +02:00
Dave Halter
4f3a15e235 solve random stdlib issue 2014-04-20 01:25:02 +02:00
Dave Halter
404baf5020 use Param.stars more generally 2014-04-20 00:12:52 +02:00
Dave Halter
2b091076c1 fix the named param issue in the autocompletion 2014-04-19 22:57:41 +02:00
Dave Halter
34488d1bb6 named param tests for *args/**kwargs 2014-04-19 22:26:24 +02:00
Dave Halter
10e2dc2f6c remove unused imports from helpers 2014-04-19 16:28:31 +02:00
Dave Halter
0b5ed6a5fe array_for_pos -> call_signature_array_for_pos 2014-04-19 16:27:41 +02:00
Dave Halter
1f7fd6f439 search_call_signature refactoring 2014-04-19 16:26:15 +02:00
Dave Halter
bb6874bc7c fix for problems with incomplete one liner string literals, after a start of an incomplete string literal the whole line should be seen as an error token 2014-04-19 13:56:29 +02:00
Dave Halter
54568c1868 disable refactoring tests for now. 2014-04-18 17:29:01 +02:00
Dave Halter
33fa2b0cb4 call_signatures should work on more nested edge cases 2014-04-18 17:17:10 +02:00
Dave Halter
ef23f7401e specify a deprecation process, see also #395 2014-04-18 15:24:36 +02:00
Dave Halter
99b1ad18b4 test for davidhalter/jedi-vim#268, Definition objects should be unique per position. This was fixed a few commits ago by the whole usage fixes. 2014-04-18 15:10:57 +02:00
Dave Halter
efc24a9ecc forgot __ne__ function for python 2 compatibility in Definition 2014-04-18 14:53:43 +02:00
Dave Halter
0301606d18 Now remove Usage completely. 2014-04-18 14:45:03 +02:00
Dave Halter
f0e7b5583d update parser pickling version again, because we've changed a lot in the last few commits 2014-04-18 14:41:29 +02:00
Dave Halter
547ec56bd3 remove NamePart.string, can be done by casting it to unicode 2014-04-18 14:40:28 +02:00
Dave Halter
240b0c9581 NamePart doesn't have an __eq__ method anymore 2014-04-18 14:36:10 +02:00
Dave Halter
c2bdda339b again Definition/Usage merging 2014-04-18 01:51:09 +02:00
Dave Halter
b643325889 type of NamePart in definition should work. 2014-04-18 01:31:07 +02:00
Dave Halter
fe98940624 test for problem with builtins in usages 2014-04-17 14:41:56 +02:00
Dave Halter
5f4c4de229 Usage is now a subclass of Definition, #395 2014-04-17 14:39:22 +02:00
Dave Halter
a92c9dd81b added a docstring to _Help 2014-04-17 14:21:20 +02:00
Dave Halter
084a4a5a43 Help -> _Help for now 2014-04-17 14:14:30 +02:00
Dave Halter
0582979db5 documentation to docstring, #395 2014-04-17 14:13:47 +02:00
Dave Halter
2384556861 use Help instead of Documentation. see #392 for a discussion about that. 2014-04-16 11:18:22 +02:00
Dave Halter
ddc1cb0e6a same problem with KeywordStatement 2014-04-16 10:28:12 +02:00
Dave Halter
15fdecdb61 for flows triggered an exception if goto_assignments was used on the keyword. found with the help of sith.py 2014-04-16 09:57:01 +02:00
Dave Halter
a341791fda found list indexing issue with sith and fixed it. 2014-04-16 01:31:49 +02:00
Dave Halter
62bd8bd8ef make the parametrizing of tests nicer for integration tests 2014-04-14 17:07:34 +02:00
Dave Halter
b81eb9f8b3 NameFinder.filter_name is simpler now. 2014-04-14 13:45:31 +02:00
Dave Halter
6a40c9b671 remove get_set_vars completely from existance 2014-04-14 12:40:59 +02:00
Dave Halter
237af765b7 start to get rid of the get_set_vars/get_defined_names distinction 2014-04-14 12:28:24 +02:00
Dave Halter
4c53a64ca0 increase speed again for compiled objects by not using sorted + lambda on its list of defined names 2014-04-14 10:12:46 +02:00
Dave Halter
ff810d9ece cache compiled.CompiledObject.defined_names attribute, improves test suite performance by 20%, numpy by more than 50%. Awesome! 2014-04-14 02:20:22 +02:00
Dave Halter
3fced34544 the last commit featured an incomplete caching for compiled objects. The current one should improve this. 2014-04-13 21:55:29 +02:00
Dave Halter
4bc55be103 significant speedup due to compiled caching 2014-04-13 16:31:38 +02:00
Dave Halter
edeebd0bb9 Name initialization should always take the same param types as input 2014-04-13 15:00:49 +02:00
Dave Halter
058e123879 fix issues with a copy.copy usage for Name 2014-04-13 14:55:07 +02:00
Dave Halter
3d9d0bfd03 Name.get_code caching to make lookups faster 2014-04-12 15:28:19 +02:00
Dave Halter
7f288eb0b0 Add a nice and small profile script for Jedi. 2014-04-11 16:01:26 +02:00
Dave Halter
27645af6d2 make a slow inspect.getdoc() call lazy, which improves performance for numpy completions by 30% 2014-04-11 11:53:11 +02:00
Dave Halter
a6bfb1b3ad fix issues with os.path completions 2014-04-11 10:33:32 +02:00
Dave Halter
840a806246 generalize import optimizations and make them behave more like sys.modules 2014-04-10 15:21:23 +02:00
Dave Halter
4bc89d638c start caching some parts of the imports to significantly speed up numpy completions. 2014-04-10 14:55:49 +02:00
Dave Halter
14bf618af0 put some list casts into a different place. 2014-04-10 12:24:30 +02:00
Dave Halter
6de46fe373 realizing that #241 is more of a feature than a bug, I disabled the corresponding test. This will be changed once the whole implementation of isinstance becomes more flexible and also allows checks on subclasses, so that no information is lost. 2014-04-09 16:27:42 +02:00
Dave Halter
d8d6b20a17 fix line split issues in cache 2014-04-09 13:16:28 +02:00
Dave Halter
46277eb9c9 call_signatures caching should be much more precise, now. fixes #390 2014-04-09 12:27:23 +02:00
Dave Halter
d2dc39e0c2 also allow unicode, bytes, bytearray, dict -> fixes #297 2014-04-07 16:16:31 +02:00
Dave Halter
193e04ae8e get rid of side effects in the interpreter if trying to use the index on an iterable (using __getitem__) 2014-04-07 16:11:23 +02:00
Dave Halter
17345b6e78 reduce/remove getitem side effects, tests for #297 2014-04-07 16:06:07 +02:00
Dave Halter
ea62ad6a50 backwards tokenizer can now handle 10e-5 and so on literals 2014-04-07 15:51:24 +02:00
Dave Halter
0dd3936c5c small clean up in the user context backwards tokenizer 2014-04-07 15:39:15 +02:00
Dave Halter
1704185ed1 fix Completion.parent() issues with builtins 2014-04-07 15:21:17 +02:00
Dave Halter
0d9f9f0e4a parent on completions tests 2014-04-07 14:54:56 +02:00
Dave Halter
120099ac5e removed the complex number tests somewhere along the line -> reintroduced. 2014-04-07 14:46:26 +02:00
Dave Halter
484ace2cfd fix last problems with #327 2014-04-07 14:43:46 +02:00
Dave Halter
b48d0bf622 passing hex/oct/bin tests for #327 2014-04-07 14:12:12 +02:00
Dave Halter
d15203162a more tests for #327, for complex numbersand the power notation. 2014-04-07 13:50:07 +02:00
Dave Halter
99beac1c2b fix completion on int literals, fixes #327 2014-04-07 13:04:45 +02:00
Dave Halter
6ebc40792a completions should also have the parent attribute -> move parent to BaseDefinition 2014-04-07 00:53:34 +02:00
Dave Halter
79e2ec85cc implement slicing for __getitem__ with interpreter 2014-04-04 15:09:25 +02:00
Dave Halter
09854ae6ca negative factors like -1 are now evaluated 2014-04-04 15:07:45 +02:00
Dave Halter
a6fbcde184 more detailed tests and implementation of array indexing in the interpreter 2014-04-04 13:22:12 +02:00
Dave Halter
50ef3c7fa3 implement __getitem__ access for CompiledObject 2014-04-04 12:59:16 +02:00
Dave Halter
f7e236971b moved the input of get_index_types to a more typical type system 2014-04-04 12:09:46 +02:00
Dave Halter
84fde13b84 implemented a first prototype of a slicing and index creation function 2014-04-03 21:10:42 +02:00
Dave Halter
b8987fe451 CompiledObject.get_index_types implementation example 2014-04-03 19:05:29 +02:00
Dave Halter
1facdb2961 test for 3 part slice precedences 2014-04-03 18:59:16 +02:00
Dave Halter
9959929220 test lists in Interpreter, #297 2014-04-03 11:42:22 +02:00
Dave Halter
9854a42922 add elpy to the list of supported emacs implementations 2014-04-03 11:08:11 +02:00
Dave Halter
5784e23b4e tuples are also a possiblity in docstring type annotations 2014-04-02 21:17:57 +02:00
Dave Halter
bb72ecfa8a docstring annotation types should be instantiated -> execute them, fixes #178 2014-04-02 20:42:18 +02:00
Dave Halter
e681ed9fda test for #178, docstring type annotations should be executed 2014-04-02 20:41:25 +02:00
Dave Halter
28fbdbc0f6 docs were not correct about epydoc docstrings. fixes davidhalter/jedi#245 2014-04-02 16:43:59 +02:00
Dave Halter
69364c598f fix abroken test 2014-04-02 16:01:39 +02:00
Dave Halter
a66f8e5a0b more Definition.parent tests. 2014-04-02 15:44:55 +02:00
Dave Halter
d8c433cb20 Definition.parent is working. fixes #325 2014-04-02 15:37:41 +02:00
Dave Halter
99f292fbb5 tests for Definition.parent(), #325 2014-04-02 15:25:57 +02:00
Dave Halter
cc5957d56c reverse backwords incompatibility of #393 and therefore deprecate CallSignature.module 2014-04-02 13:59:05 +02:00
Dave Halter
edb0bbd183 use defined_names also for modules, fixes symbol browsing issues - fixes #344 2014-04-02 13:48:27 +02:00
Dave Halter
b823a196d6 tests for support for symbol browsing with imports and 'defined_names', see #344 2014-04-02 13:17:55 +02:00
Dave Halter
347ae636e4 cache Definition objects 2014-04-02 12:05:02 +02:00
Dave Halter
d6ffc80f0e cast pr.Function to er.Function even in the api. 2014-04-02 11:40:50 +02:00
Dave Halter
dfd2b202ff use _follow_statements_imports also in the BaseDefinition.params 2014-04-02 11:05:32 +02:00
Dave Halter
7763192850 use the generalized _follow_statements_imports internally 2014-04-02 10:46:57 +02:00
Dave Halter
c26b57bef6 generalize Completion.follow_definition to Definition._follow_statements_imports 2014-04-02 10:44:14 +02:00
Dave Halter
283a5086f9 use memoize_default instead of strange underscore_decorators in api classes 2014-04-02 10:34:43 +02:00
Dave Halter
8ac9e16f3f clean up the is_callable stuff 2014-04-02 09:47:27 +02:00
Dave Halter
305b593f3b create an is_callable method for all representation objects, to determine if something is callable or not 2014-04-02 09:42:15 +02:00
Dave Halter
d1a6dd1098 tests for call signatures that are not actually callable. 2014-04-01 15:22:43 +02:00
Dave Halter
72aa7f918f the type of a compiled object is also function, not def 2014-04-01 14:45:05 +02:00
Dave Halter
ee18e19711 moved params to BaseDefinition. This enables completions and gotos to check for call signatures as well, fixes #238 2014-04-01 14:34:10 +02:00
Dave Halter
03b02f9830 temporary support for unicode in Documentation objects. We still need to discuss the exact details of that class #392 2014-03-31 12:44:47 +02:00
Dave Halter
9b3b28f185 use defaultdict instead of OrderedDict for testing purposes, since OrderedDict is not supported in Python 2.6 2014-03-31 12:18:53 +02:00
Dave Halter
dc953d3c54 fix broken goto_definition command for variables in the same statement 2014-03-29 16:52:54 +01:00
Dave Halter
ced5b6ca82 goto_assignments and usages both working now with issues like #315 2014-03-29 16:37:39 +01:00
Dave Halter
29435852e6 trying to clean up the goto mess 2014-03-28 13:46:36 +01:00
Dave Halter
54d9d325da temporary fix for #315. in the future we should be removing that whole indent change crap 2014-03-28 02:08:22 +01:00
Dave Halter
5b7843747c test for recurring use of variable name with gotos, #315 2014-03-28 02:01:57 +01:00
Dave Halter
16e3e327d3 docstring returns now also strip rst roles and work with more complicated patterns 2014-03-28 01:41:24 +01:00
Dave Halter
1a1d4f5576 test: docstring sphinx type returns don't work like the docstring param stuff 2014-03-27 21:02:07 +01:00
Dave Halter
fb34864ace fix docstrings that didn't work with multiple return classes of other modules (or in tuples) 2014-03-27 20:23:41 +01:00
Dave Halter
2f71bd4e63 test for multiple docstring imports with complicated lists and tuples 2014-03-27 20:05:39 +01:00
Dave Halter
eea2d0c8c5 param docstrings cleaned up - #370 2014-03-27 19:51:35 +01:00
Dave Halter
b680246195 docstring working for local definitions as well 2014-03-27 12:34:53 +01:00
Dave Halter
196fcdb4df docstring param hints not working properly - test for #370 2014-03-26 16:47:54 +01:00
Dave Halter
2dbfe46a5a fix a minor issue with __repr__ for decorators 2014-03-26 16:33:31 +01:00
Dave Halter
4a9b9388df missing docstrings for imports in completions should be there now, fixes #340 2014-03-25 11:57:34 +01:00
Dave Halter
33d59d8055 tests for documentation Completion.documentation - check if Jedi is following imports correctly 2014-03-25 11:20:48 +01:00
Dave Halter
3f3788e800 add a Documentation() class to the API, which will be used in the future for all kind of docstrings. Also add a documentation method on BaseDefinition that returns a Documentation object. Deprecate at the same time its doc and raw_doc functions 2014-03-25 02:14:34 +01:00
Dave Halter
fa664534e4 make param backwards compatibility clearer 2014-03-25 01:08:29 +01:00
Dave Halter
1c1b51f116 more type tests 2014-03-24 01:31:14 +01:00
Dave Halter
18ca96803f implement a Completion.type version for #340, follow imports if they are in a from clause or if its a longer imnport 2014-03-23 17:51:03 +01:00
Dave Halter
45fecabbf1 definition type shouldn't return import, we'd rather want the implementation name -> #340 2014-03-23 13:30:10 +01:00
Dave Halter
c8b3658d63 forgot to deprecate source_encoding properly -> #389 2014-03-23 12:54:39 +01:00
Dave Halter
5aa6c770b3 refactor follow_definition tests 2014-03-23 12:40:33 +01:00
Dave Halter
67202db305 change a test (other library) so that python 3.4 also works 2014-03-21 15:36:13 +01:00
Dave Halter
37ff4cfbd9 Merge pull request #387 from ColinDuquesnoy/py34
UnicodeError with Python 3.4
2014-03-21 18:42:21 +04:30
Dave Halter
78837ce539 cleanup 2014-03-21 14:31:15 +01:00
Dave Halter
83b7190a0a a follow function for the Importer which solves the issues with follow_definitions 2014-03-21 14:09:12 +01:00
Dave Halter
fa0502d762 again testing improvements. follow_definitions imports are now pretty much covered 2014-03-21 13:50:29 +01:00
Dave Halter
eeeeee49f5 new tests for that whole import mess 2014-03-21 11:12:40 +01:00
Dave Halter
a06310db4a again a temporary follow_definition for imports 2014-03-21 09:27:52 +01:00
Dave Halter
1703f7cca6 kind of separated Importer from ImportPath 2014-03-20 18:21:17 +01:00
Dave Halter
58026c6542 temporary conversion of the imports module to separate import processing from actually importing things 2014-03-19 16:40:38 +01:00
ColinDuquesnoy
b18b3e3edd install python 3.4 only if toxenv is py34 2014-03-18 21:47:24 +01:00
ColinDuquesnoy
666d037022 Travis: add apt-get update 2014-03-18 20:55:39 +01:00
ColinDuquesnoy
d810c13a58 Install python 3.4 from ppa 2014-03-18 20:51:49 +01:00
ColinDuquesnoy
9470bc6c63 Add TOXENV=py34 to .travis.yml 2014-03-18 20:39:02 +01:00
ColinDuquesnoy
d6187a4e23 Fix UnicodeDecodeError
Open binary file in binary mode. This happens in py34 because they
fixed the missing ``get_filename`` attribute of ExtensionLoader
2014-03-18 19:19:35 +01:00
ColinDuquesnoy
dceec59877 Add py34 to tox's envlist 2014-03-18 19:16:49 +01:00
Dave Halter
9f12352ec1 tests for import completions and a following follow_definition call 2014-03-17 11:58:29 +01:00
Dave Halter
253e1c5717 generalize Definition.name even more 2014-03-17 10:46:32 +01:00
Dave Halter
aca7668da6 small internal change for Script, _source_path to _orig_path 2014-03-17 08:58:03 +01:00
Dave Halter
1d95a987c4 move test_pyc to test_evaluate 2014-03-17 08:54:29 +01:00
Dave Halter
4c6c9c4fb5 py.test should be able to call the tests from different directories: fix pyc tests 2014-03-17 08:48:48 +01:00
Dave Halter
5e6616a451 add a command line option to produce errors from warnings 2014-03-15 14:37:40 +01:00
Dave Halter
ce7cf53628 the temporary pyc package should always be removed 2014-03-15 14:31:37 +01:00
Dave Halter
266330c815 removed some deprecated calls 2014-03-15 14:21:23 +01:00
Dave Halter
c7fc13743f add proper deprecation warnings and warnings become error messages in Jedi; also deprecate CallSignature.call_name 2014-03-14 14:49:13 +01:00
Dave Halter
064f161acc signature is now a subclass of definitions 2014-03-14 13:32:03 +01:00
Dave Halter
e13f0a60d2 signature should be defined as a definitions: tests 2014-03-14 13:20:00 +01:00
Dave Halter
9f200b43ce sith readability 2014-03-14 13:10:25 +01:00
Dave Halter
6a6927e9a7 sith refactoring 2014-03-14 12:50:57 +01:00
Dave Halter
c1dec3e87a remove CallSignature.module, because that's something for internal use 2014-03-14 01:34:04 +01:00
Dave Halter
56206a1ad8 fix test case for python3.3 2014-03-14 01:12:37 +01:00
Dave Halter
88af0ad7d7 deprecate Param.get_code 2014-03-13 23:11:20 +01:00
Dave Halter
c5833003dc remove strange Definition.description length limitation 2014-03-13 23:09:40 +01:00
Dave Halter
2bb40fcee8 CallDef -> CallSignature 2014-03-13 22:55:16 +01:00
Dave Halter
4b5ac063d3 Param docstrings 2014-03-13 22:53:42 +01:00
Dave Halter
2ae5dad78b fix Definition.name API for params 2014-03-13 22:48:02 +01:00
Dave Halter
eaa56c47cc compiled params didn't have a param 2014-03-13 12:49:37 +01:00
Dave Halter
92da4ca99f tests for call signature param names 2014-03-13 12:49:08 +01:00
Dave Halter
f132dd852d fix Param calling in API 2014-03-13 11:27:53 +01:00
Dave Halter
a835fcd0ce start using a specialised param class for CallDef 2014-03-13 11:16:00 +01:00
Dave Halter
c71247e9c9 syntastic sugar 2014-03-13 10:39:18 +01:00
Dave Halter
8b176e34f2 clean up decorator mess 2014-03-13 01:51:02 +01:00
Dave Halter
ec6d7df950 fix problems with decorators with call signatures, fixes #319 2014-03-13 01:29:32 +01:00
Dave Halter
28d5da2f73 call signatures: decorator in class, test for #319 2014-03-13 00:05:23 +01:00
Dave Halter
e0f5fe4f1d update changelog with precedence stuff 2014-03-12 16:06:46 +01:00
Dave Halter
60aff6fa9d A kate plugin uses Jedi, fixes #322 2014-03-12 10:39:25 +01:00
Dave Halter
394e2e77de refactored the development docs, which was badly needed especially since evaluate/dynamic.py doesn't contain a lot of functionality anymore. 2014-03-11 17:26:38 +01:00
Dave Halter
18e5a3ad4f document a goto part of the API 2014-03-11 16:08:20 +01:00
Dave Halter
5764e760d2 improve internal module links for sphinx documentation 2014-03-11 15:46:08 +01:00
Dave Halter
cffdcd2571 correct developer documentation a little bit 2014-03-11 15:38:46 +01:00
Dave Halter
5abe4e2d57 my stupid english sometimes :-) 2014-03-11 14:32:40 +01:00
Dave Halter
9da09c9058 improve the testing documentation. Add a section about the alternate test runner ./run.py 2014-03-11 14:27:11 +01:00
Dave Halter
90338aa828 fix sphinx documentation issues (call it through the right namespace) 2014-03-11 14:17:21 +01:00
Dave Halter
8aa71d7cd6 fix version string in documentation 2014-03-11 14:07:41 +01:00
Dave Halter
b7a4b543fd exclude deprecated members from documentation (there are just too many), this can be undone once we've removed the old API (also cleaned up docs/conf.py a little bit). fixes #317 2014-03-11 14:01:09 +01:00
Dave Halter
17ecd73df9 fix @Alexey-T's concern of deprecated documentation, see #317 2014-03-11 13:23:25 +01:00
Dave Halter
f191917555 fixed compild classes inheritance issue, fixes #380 2014-03-11 12:45:29 +01:00
Dave Halter
b6dbbd2c5d tests for compiled super classes - #380 2014-03-11 12:42:40 +01:00
Dave Halter
2457da0e7d moved extension tests into test_evaluate folder 2014-03-11 12:37:02 +01:00
Dave Halter
cd7774f25f lambda can be used as a default param in function, which means there have been slight changes to the parser to allow that (comma in a function definitions doesn't always mean new param), fixes #379 2014-03-11 12:24:36 +01:00
Dave Halter
18a012509f tests for lambda params - #379 2014-03-11 12:03:19 +01:00
Dave Halter
937ab602ae add a lambda integration testing file 2014-03-11 11:59:17 +01:00
Dave Halter
5eef23046b clean up 2014-03-11 11:38:08 +01:00
Dave Halter
db1b73d423 lo and behold - reversed is implemented - the force is strong with this one. fixes #24 2014-03-11 11:04:00 +01:00
Dave Halter
6c5e91da69 some code - just written for @dbrgn 2014-03-10 23:08:09 +01:00
Dave Halter
f8336d7176 fix a unicode issue (test input was not unicode) 2014-03-10 17:15:41 +01:00
Dave Halter
af7814c6d4 enable assignment operators and add substraction support for numbers 2014-03-10 16:51:47 +01:00
Dave Halter
52348e0d1b delete unnecessary checks from filter 2014-03-10 16:12:17 +01:00
Dave Halter
62a14f7558 restructure NameFinder.filter_name a little bit 2014-03-10 15:57:01 +01:00
Dave Halter
4e314409c1 remove position argument of follow_path 2014-03-10 00:44:15 +01:00
Dave Halter
cc667e76dc start_pos is usually not needed to search in modules, but if we generalize that, things get a lot slower - tests still pass - but the slowness is probably due to some weird recursion catching that now happens and that Jedi runs into, e.g. check ./run.py std 27 --debug 2014-03-10 00:40:57 +01:00
Dave Halter
a073b902c1 assignment operators test 2014-03-09 13:15:30 +01:00
Dave Halter
7f0e31798a fix an old test case that was always wrong anyway 2014-03-09 12:40:38 +01:00
Dave Halter
b22c9c96f2 string additions also work now - be prepared #24! 2014-03-09 12:36:17 +01:00
Dave Halter
70e1970f40 plus on numbers is now basically working 2014-03-09 12:27:03 +01:00
Dave Halter
3b372e2fe2 add some operator tests for precedences (array indexing) 2014-03-09 12:00:28 +01:00
Dave Halter
60ff2bc088 fix an invalid statement that has changed because of changes to the parser. 2014-03-09 11:56:29 +01:00
Dave Halter
35548cbf3c clean up the new _eval_statement_element method 2014-03-09 02:44:23 +01:00
Dave Halter
f26e3770dd ternary operator implementation, precedences complete and working, now 2014-03-09 02:41:07 +01:00
Dave Halter
e2832e1172 fix multiplication of arrays with precedences 2014-03-09 02:23:38 +01:00
Dave Halter
61fa9b7fb4 start using precedences, working good with only a few test fails 2014-03-09 02:10:59 +01:00
Dave Halter
48dd8f9cb0 slice precedences should be working as well. 2014-03-09 01:05:46 +01:00
Dave Halter
29cfdfeab1 precedence integration preparation 2014-03-08 23:43:29 +01:00
Dave Halter
11346d0901 get the 'not' priorities right for precedences 2014-03-08 03:14:05 +01:00
Dave Halter
3559dba7ea to the power of precedences 2014-03-08 03:05:38 +01:00
Dave Halter
0a253b4651 MultiPart string precedences seem to be working now 2014-03-08 02:52:26 +01:00
Dave Halter
0dcc924cf8 precedence working for some simple cases (and invalid statements) 2014-03-08 02:24:25 +01:00
Dave Halter
f2e2a684d5 precedence tests and a parse_tree method for Precedence objects 2014-03-07 15:05:28 +01:00
Dave Halter
77bfb0fb7b basic precedence algorithm - not tested yet 2014-03-07 14:40:57 +01:00
Dave Halter
ca460ac34f fixed a problem with docstrings that were empty (None), docstrings in Jedi are always strings. 2014-03-06 12:20:11 +01:00
Dave Halter
d12e030677 use tuples instead of lists if the don't change (immutable is faster and more memory efficient) 2014-03-06 00:26:23 +01:00
Dave Halter
a97c91002f replace name and statement end_pos (this way we can get rid of all that end_pos crap soon) 2014-03-05 23:46:39 +01:00
Dave Halter
66a488b911 clean up parse_dot_name and other small things 2014-03-05 22:46:11 +01:00
Dave Halter
1f7e4ca637 dict literals are now working (at least the parser) 2014-03-05 18:46:43 +01:00
Dave Halter
387319fde5 simplify parse_array again (closure in statement parser) 2014-03-05 16:54:21 +01:00
Dave Halter
7222d70ecb restructure token breaks in statement parser 2014-03-05 14:39:21 +01:00
Dave Halter
7d7a86239c tests for list comprehensions with included dict literals 2014-03-05 14:23:53 +01:00
Dave Halter
14aca9708e forgot to change to docs - only changed the readme 2014-03-05 12:36:54 +01:00
Dave Halter
eb1c6f813a add a TextMate plugin link, by @lawrenceakka 2014-03-05 12:32:36 +01:00
Dave Halter
5f8f1e170b moved global variables handling 2014-03-05 01:19:18 +01:00
Dave Halter
2322edff8f keyword statement not includes globals 2014-03-05 00:44:19 +01:00
Dave Halter
f4b235a35d Merge branch 'dev' of github.com:davidhalter/jedi into dev 2014-03-04 17:36:01 +01:00
Dave Halter
8bd8ba1df8 don't need to generate 'pass' anymore if a class is empty in get_code 2014-03-04 17:27:26 +01:00
Dave Halter
f54344fd9e KeywordStatements are working except for some of the old ones (global, assert, return, yield) 2014-03-04 17:20:29 +01:00
Dave Halter
53fd1f925a create KeywordStatement to fit assert, del, global, etc into a more generalized schema, which can also improve the get_code method 2014-03-04 15:35:24 +01:00
Dave Halter
65ce609a3c protect token_list -> _token_list 2014-03-04 12:43:37 +01:00
Dave Halter
7de4b14461 remove crazy PushBackIterator from statement parser 2014-02-27 17:40:01 +01:00
Dave Halter
85b5fdf85f again... statement parser 2014-02-27 17:12:16 +01:00
Dave Halter
1eba63760e more change to simplify the statement parser 2014-02-27 16:58:08 +01:00
Dave Halter
8688def619 more statement parser deletions 2014-02-27 11:42:16 +01:00
Dave Halter
38d940cc2b remove start_pos definition from statement parser 2014-02-27 11:36:58 +01:00
Dave Halter
2061fbcacc small parse_stmt refactoring 2014-02-27 00:43:27 +01:00
Dave Halter
6577fa4336 just check for unicode, the parser doesn't know str anymore :) 2014-02-27 00:31:41 +01:00
Dave Halter
3332eba3f7 lambda problem with new operators 2014-02-27 00:17:52 +01:00
Dave Halter
fdabca20e9 fix get_code method for Operator (still ugly, though) 2014-02-26 22:23:21 +01:00
Dave Halter
3330e29748 more problems fixed that relate to Operator 2014-02-26 22:08:51 +01:00
Dave Halter
45517a9b8f Merge pull request #378 from ColinDuquesnoy/issue373
CompiledObject params, fixes #373
2014-02-26 19:47:30 +04:30
Dave Halter
2e12eb7861 start with the integration of an Operator class to make way for precedences 2014-02-26 14:44:51 +01:00
ColinDuquesnoy
2c49a968a9 Fix test (bad assert) 2014-02-26 14:29:27 +01:00
ColinDuquesnoy
b56c1cb118 Add test with standard lib
math.cos( should return <Param: x @0,0>
2014-02-26 14:21:52 +01:00
ColinDuquesnoy
d5ba683756 Fix api.classes.CallDef.params
We need to check for compiled objects
2014-02-26 13:53:35 +01:00
ColinDuquesnoy
725e55485a Add params property to compiled object 2014-02-26 13:53:09 +01:00
ColinDuquesnoy
da27ce4d7c Execute test only on 64bit platforms 2014-02-26 13:52:46 +01:00
ColinDuquesnoy
2ee60675f1 Add a test call_signatures for compiled modules
Should fail
2014-02-26 13:51:40 +01:00
Dave Halter
e152939791 remove encoding stuff from tokenizer - encoding is always unicode 2014-02-26 12:55:32 +01:00
Dave Halter
1a3cca6edb fixed a problem with the fast parser and its strange end positions 2014-02-26 11:23:45 +01:00
Dave Halter
8d1a9f2d46 fix long standing issue with import end_pos 2014-02-26 11:03:19 +01:00
Dave Halter
b28f8fc7b4 test_parsing -> test_parser 2014-02-26 10:45:28 +01:00
Dave Halter
acec5fe76f fake docstrs fixed 2014-02-26 02:38:28 +01:00
Dave Halter
44e16c11e5 fixed docstr problems with unified interfaces 2014-02-26 02:33:18 +01:00
Dave Halter
5e5bb618ea remove token.py, docstrings are now just normal tokens until used 2014-02-26 01:13:38 +01:00
Dave Halter
40be00826e clean up tokenize 2014-02-25 17:17:33 +01:00
Dave Halter
761c28ef00 remove __getitem__ from Token 2014-02-25 17:03:56 +01:00
Dave Halter
66aca8eba1 user_context now doesn't work with the token tuple anymore 2014-02-25 16:49:44 +01:00
Dave Halter
18e985a961 TokenInfo -> Token 2014-02-25 16:44:48 +01:00
Dave Halter
6439d6c848 removed the now redundant token.Token version in favor of tokenize.TokenInfo 2014-02-25 16:38:19 +01:00
Dave Halter
2db26abf72 start and end don't exst anymore in parser.token.Token, it's now start_pos/end_pos as everywhere else 2014-02-25 16:34:27 +01:00
Dave Halter
ee7108cc11 successfully removed __str__ and __unicode__ methods from token.Token 2014-02-25 16:21:53 +01:00
Dave Halter
aea2c4620f more unicode switches in the parser 2014-02-25 14:27:50 +01:00
Dave Halter
f4f79317fe start uniting tokenize.TokenInfo and token.Token 2014-02-25 13:54:18 +01:00
Dave Halter
5b84f0b27f remove end_pos stuff from tokenizer, the tokens can do that themselves 2014-02-25 13:29:27 +01:00
Dave Halter
2252271bf5 fix unicode issues with python2.7 2014-02-25 12:54:06 +01:00
Dave Halter
3a23c80ae5 prepare for eventual? tokenizer end_pos replacement. 2014-02-25 11:59:10 +01:00
Dave Halter
246118f851 start using @ganwell's new token class (modified in some ways) as the main token class - hope to gain a little bit of memory/cpu/pickling performance 2014-02-25 02:06:26 +01:00
Dave Halter
e2a6d1dd43 remove offset param for Parser 2014-02-25 01:31:24 +01:00
Dave Halter
44d560c53a finally removed self-contained iterator from Parser - after knowing this since almost the beginning. 2014-02-25 01:26:19 +01:00
Dave Halter
a7a8a73a2c removed Parser._start_pos as well 2014-02-25 01:20:55 +01:00
Dave Halter
add8259d7e successfully removed end_pos from parser 2014-02-25 01:05:00 +01:00
Dave Halter
936c7dfde4 protect start_pos/end_pos in parser to hopefully remove them soon 2014-02-25 00:27:39 +01:00
Dave Halter
ef8d3633dd use the module end_pos in fast parser 2014-02-25 00:18:24 +01:00
Dave Halter
a5a6e9ac12 parser: remove current 2014-02-24 20:43:00 +01:00
Dave Halter
50f8b8bf0c start using tokens all the way through in the parser 2014-02-24 20:35:36 +01:00
Dave Halter
6058e8b9c3 remove unnecessary checks 2014-02-24 11:40:05 +01:00
Dave Halter
9943bb6205 remove some old parameters from Parser and FastTokenizer 2014-02-24 11:24:54 +01:00
Dave Halter
7db090a48a moved NoErrorTokenizer to fast.FastTokenizer 2014-02-24 11:05:31 +01:00
Dave Halter
9257062910 remove NoErrorTokenizer dependency for all but the fast parsers 2014-02-24 10:31:15 +01:00
Dave Halter
553ff66c8b remove last_previous from NoErrorTokenizer 2014-02-23 12:51:05 +01:00
Dave Halter
8e847f4982 fix python 2.7 issues. the parser now only takes unicode inputs 2014-02-23 11:44:32 +01:00
Dave Halter
c5fcebde82 changed _compatibility.utf8 -> 'u' and removed a lot of the issues with the now enforced unicode source input of the parser 2014-02-23 11:29:00 +01:00
Dave Halter
5478e50f8b Merge branch 'dev' of github.com:davidhalter/jedi into dev 2014-02-21 17:52:44 +01:00
Dave Halter
9c2fcfffd7 StringIo is now always used as part of the io library 2014-02-21 17:52:09 +01:00
Dave Halter
d9bf9be35e small refactorings of the tokenizer 2014-02-21 17:45:56 +01:00
Dave Halter
efba0cd80c deleted a big part of the generate_tokens code that doesn't seem to be needed anymore 2014-02-21 16:38:15 +01:00
Dave Halter
73057d4176 removed the INDENT usages that were left in the parser code 2014-02-21 15:39:14 +01:00
Dave Halter
fe02088dd7 restructure user_context reverse tokenizer to remove INDENT tokens in the future 2014-02-21 15:30:46 +01:00
Dave Halter
9c3b7b9c46 get rid of more variables 2014-02-21 10:44:33 +01:00
Dave Halter
184e90a3a0 removed some line checks from tokenizer 2014-02-21 00:13:23 +01:00
Dave Halter
a69eee5876 error tokens only exist in the end in a tokenizer 2014-02-21 00:09:49 +01:00
Dave Halter
3232ae5b0c removed parentheses counting from generate_tokens 2014-02-20 18:45:22 +01:00
Dave Halter
7e651684ff fix problems with debug mode if not correctly initialized. 2014-02-20 14:17:05 +01:00
Dave Halter
d7033726fd tokenize removed NL/NEWLINE ambiguity 2014-02-20 01:52:30 +01:00
Dave Halter
c26ae3c00d ignore dedents in tokenizer 2014-02-20 01:21:20 +01:00
Dave Halter
3e9b72b636 created a PushBackTokenizer specifically for the parser 2014-02-20 01:17:19 +01:00
Dave Halter
c8d6fbb0a1 temporary changes for the tokenizer issues 2014-02-20 00:43:42 +01:00
Dave Halter
000e929e3c Merge pull request #375 from ColinDuquesnoy/dev
Fix import of compiled module with python3.
2014-02-19 22:11:27 +04:30
ColinDuquesnoy
9d7ecae27c Add precompiled extension modules 2014-02-19 08:14:51 +01:00
ColinDuquesnoy
2c97d01bd0 Move extensions dir into test 2014-02-19 08:10:43 +01:00
ColinDuquesnoy
f808dbbd28 Change sys.path for the test to succeed.
Tested locally with a python3 extension module (in
/extensions/compiled33).

Also tested that reverting a75773cf9f make
the test fail.
2014-02-18 22:14:43 +01:00
ColinDuquesnoy
7977d57169 Add test_compiled
Should pass on travis since there is no precompiled modules yet
2014-02-18 20:31:06 +01:00
ColinDuquesnoy
7aed62cb99 Add compiled modules sources + readme 2014-02-18 20:12:30 +01:00
ColinDuquesnoy
5a706265bc Move dummy pyc to dummy package 2014-02-18 19:18:17 +01:00
ColinDuquesnoy
18a31dcbf1 Fix read mode for pyc files
Now all test should pass on travis.
2014-02-18 19:00:55 +01:00
ColinDuquesnoy
ea43efc9d8 Fix pyc test for python3
To import pyc modules, we must move them out of the __pycache__
directory and rename them to remove ".cpython-%s%d".

This should still faild with python3 (UnicodeDecodeError)
2014-02-18 19:00:55 +01:00
ColinDuquesnoy
d80caa7108 Add test pyc
Should succeed for python2 and faild for python3
2014-02-18 17:36:01 +01:00
Dave Halter
1b6df4602d replace parser tokenizer offset with line offset 2014-02-17 23:02:04 +01:00
Dave Halter
6952596117 implement an offset in generate_tokens 2014-02-17 10:08:32 +01:00
ColinDuquesnoy
a75773cf9f Fix import of compiled module with python3.
This at least fix #331
2014-02-17 09:08:30 +01:00
Dave Halter
bb111daf91 removed line from tokenizer 2014-02-16 15:28:18 +01:00
Dave Halter
22928dbcd0 simplified some crazy iterator stuff within parser 2014-02-16 15:04:57 +01:00
Dave Halter
82f27569b2 little changes to clean up the code (flake8) 2014-02-14 12:53:22 +01:00
Dave Halter
d704743422 more consistent __slots__ usage in the parser -> 20% memory decrease for wx 2014-02-14 00:56:42 +01:00
Dave Halter
bb4d77f2a8 minor changes to the NamePart representation 2014-02-13 19:53:41 +01:00
Dave Halter
600371632f NamePart is now no str subclass anymore. They are separated, which makes us save a lot of dicts 2014-02-13 19:22:36 +01:00
Dave Halter
660a29ef93 multiple passes for wx script 2014-02-13 17:47:52 +01:00
Dave Halter
5be996baa8 more detailed wx._core module inspection as a script. This makes it possible to compare different Jedi commits in speed and memory efficiency 2014-02-13 16:51:04 +01:00
Dave Halter
040ea2b735 fix a few annoyances to be quicker to develop now (disabled a few tests for now) 2014-02-13 15:40:51 +01:00
Dave Halter
6939e3e18f moved test_get_code to test_parser 2014-02-12 11:33:46 +01:00
Dave Halter
2f3e4152b4 Merge branch 'get_code_fidelity' of git://github.com/ganwell/jedi into ganwell 2014-02-12 11:09:08 +01:00
Dave Halter
32ccec8447 precedence stuff is not working yet, but need to merge first because of potential parser changes 2014-02-12 11:08:48 +01:00
Dave Halter
cd1660dc53 first succeeding precedence tests 2014-01-31 11:12:28 +01:00
Dave Halter
62a74a6d2f tests for understanding operations 2014-01-30 11:27:03 +01:00
Dave Halter
e904031400 start correcting the documentation 2014-01-29 21:34:57 +01:00
Dave Halter
8660555d7b moved api, parser and evaluate test directories to test_api, test_parser... 2014-01-29 21:16:18 +01:00
Dave Halter
021aae365d move more test files to specific directories 2014-01-29 20:50:09 +01:00
Dave Halter
78114b12e9 move some tests into specific directories 2014-01-29 20:43:42 +01:00
Dave Halter
36de8c427f moved more functions from api to helpers 2014-01-29 00:28:31 +01:00
Dave Halter
7aef1f934d simplified _prepare_goto 2014-01-29 00:03:03 +01:00
Dave Halter
13696018a2 removed some functions from classes 2014-01-28 23:51:34 +01:00
Dave Halter
9cfa8fead0 create a helpers module to push some api functions into it (make the api code easier to read. 2014-01-28 23:26:50 +01:00
Dave Halter
2175416684 removed keyword docstring functionality for goto_definitions - will be reintroduced with a Script.documentation function 2014-01-28 22:27:26 +01:00
Jean-Louis Fuchs
34e89fa1c5 Merge branch 'dev' into get_code_fidelity
Conflicts:
	jedi/evaluate/docstrings.py
	jedi/parser/representation.py

Resolving merge problems:
* Introducing docstring for compiled.fake
* Partly fixing poor decision in TokenDocstring __init__
2014-01-28 00:37:06 +01:00
Dave Halter
18f225200a test for dynamic params/usages case 2014-01-26 23:13:46 +01:00
Dave Halter
f6b1e5635e move usages to its own api module usages 2014-01-26 23:04:38 +01:00
Dave Halter
8193f0c2b6 fix an issue with invalid syntax 2014-01-26 20:22:51 +01:00
Dave Halter
ba6a65c477 user_stmt refactoring 2014-01-26 19:30:05 +01:00
Dave Halter
6a4f33f373 call signatures with whitespace seem to be working 2014-01-26 19:13:06 +01:00
Dave Halter
441c001bf9 test for not working call signatures with whitespace issues 2014-01-24 01:34:13 +01:00
Dave Halter
3d7522dff6 fixed None type appearances in CompiledObject 2014-01-24 00:57:53 +01:00
Dave Halter
e587b876b6 tests and improvements for __next__ and send generator methods 2014-01-23 20:02:36 +01:00
Dave Halter
162d794081 underscore_decorators now automatically convert generators to lists 2014-01-23 15:21:52 +01:00
Dave Halter
6f2c1397b0 autocompletion diggs now pretty deep for generator objects 2014-01-23 14:39:00 +01:00
Dave Halter
a1b68945ed fix some generator parents 2014-01-23 14:27:20 +01:00
Dave Halter
c6b315aa2e failing test for fucked up generator parents 2014-01-23 14:26:04 +01:00
Dave Halter
1884087e71 start writing the changelog for 0.8.0. Hopefully to be realeased soon 2014-01-22 18:24:15 +01:00
Dave Halter
c8fffbd7b6 fix python2.6 issues with completing colorama. this happened because of a missing object parent class 2014-01-22 17:14:28 +01:00
Dave Halter
c7cae7900b remove old interpret.py 2014-01-22 16:34:05 +01:00
Dave Halter
7b1c35c3ed fix python 3 issues with the new interpreter implementation 2014-01-22 16:33:00 +01:00
Dave Halter
e4aac3eb54 add colorama to tox config, because that improves the color output 2014-01-22 16:15:56 +01:00
Dave Halter
4a71f4beeb fixes for the interpreter stuff, py27 works now fine 2014-01-22 15:42:11 +01:00
Dave Halter
7c105d27e0 some tests survive the new interpreter module already 2014-01-22 15:17:50 +01:00
Dave Halter
9e063b1248 started writing a new interpreter module that is heavily simplified and fits the current Jedi architecture way better. 2014-01-21 23:35:21 +01:00
Dave Halter
a29026c212 added Jedi debugging to pytest. Awesome. 2014-01-20 01:35:35 +01:00
Dave Halter
97ad1c6a29 moved get_names_for_scope to finder 2014-01-17 11:33:46 +01:00
Dave Halter
de6a6b5813 avoid more import recursion - moved assign_tuples and find_assignments to finder module 2014-01-17 11:12:41 +01:00
Dave Halter
a6abab4ef0 that test again... 2014-01-17 03:07:04 +01:00
Dave Halter
cd40e213ce Remove UserContextParser again from docstring stuff, not really needed there. use a simpler solution 2014-01-17 03:06:07 +01:00
Dave Halter
aa59aee3dc user_position removed from Parser. yikes! 2014-01-17 02:58:03 +01:00
Dave Halter
6063093151 test fixing 2014-01-17 02:53:30 +01:00
Dave Halter
19b0e1d5b6 reenable check_user_statement in parser, that did the whole user names calculation 2014-01-17 02:48:00 +01:00
Dave Halter
b30a186f8f remove user_stmt and user_scope stuff - yes! 2014-01-17 02:34:09 +01:00
Dave Halter
fc1899ecd4 implement user_scope search separately in UserContextParser 2014-01-17 02:08:37 +01:00
Dave Halter
33b7c341ab minor changes 2014-01-17 01:40:30 +01:00
Dave Halter
8b34e120e0 for loops are now parsed even if they are really faulty and don't end. 2014-01-17 01:39:23 +01:00
Dave Halter
92eba44d07 allow for flows to also be very faulty 2014-01-17 01:26:34 +01:00
Dave Halter
e5d40c3685 flow syntax errors should still make it possible to add them to the parser (otherwise parser doesn't include all the code 2014-01-17 01:15:36 +01:00
Dave Halter
3337d638d1 fix some colorama/pytest combo crazyness 2014-01-17 01:02:50 +01:00
Dave Halter
588fbea4f9 start to remove user_stmt parsing 2014-01-17 00:25:30 +01:00
Dave Halter
6ef75256e0 fix end_pos stuff for fast parser 2014-01-17 00:24:45 +01:00
Dave Halter
4bf72eeaed create a customized UserContextParser for user_stmt and user_scope 2014-01-16 12:12:21 +01:00
Dave Halter
269e84da85 changed version stuff in setup.py, fixes #369 2014-01-16 00:36:14 +01:00
Dave Halter
134cd234de moved user_stmt up in api 2014-01-15 17:35:40 +01:00
Dave Halter
059b1e1353 underscore_memoization is now even easier in fast parser 2014-01-15 15:57:43 +01:00
Dave Halter
d71cdded6e some easier memoization for fast parser 2014-01-15 15:48:16 +01:00
Dave Halter
d5aa36cc69 replace propery with safe_property in some evaluate cases. fixes #249 2014-01-15 15:07:06 +01:00
Dave Halter
99882724da is_py3k -> is_py3 2014-01-14 11:43:56 +01:00
Dave Halter
a6e49f2680 compatibility improvements -> use reduce from functools 2014-01-14 11:31:01 +01:00
Dave Halter
8bf8985247 move replstartup to the api 2014-01-14 00:08:25 +01:00
Dave Halter
a44ce6b7df beautify __main__ 2014-01-14 00:06:01 +01:00
Dave Halter
a9efa3db33 make utils feel more pythonic 2014-01-14 00:02:09 +01:00
Dave Halter
b2507ad94a increase the pickling version. 2014-01-13 23:00:01 +01:00
Dave Halter
b70ea1b9f6 fix a version_info testcase - testcase was broken 2014-01-13 22:17:12 +01:00
Dave Halter
6051b76304 added a note on how to ask questions on stackoverflow to the readme, fixes #334 2014-01-13 22:14:34 +01:00
Dave Halter
0c4a86acfd version bump to 0.8.0, we should make a release again 2014-01-13 21:49:00 +01:00
Dave Halter
7dff41c6b7 added a utils.version_info function to make it easy to check Jedi's version. #350 2014-01-13 21:40:16 +01:00
Dave Halter
bf427fb312 use a string instead of a tuple for __version__, this behaviour is defined in a pep somewhere (maybe pep8). fixes #350 2014-01-13 21:34:30 +01:00
Dave Halter
cdd356ff9b removed pr.String and pr.Number in favor of the more general pr.Literal 2014-01-13 16:47:01 +01:00
Dave Halter
717c4315df Remove Todos that didn't make sense. 2014-01-13 16:29:30 +01:00
Dave Halter
682e1c2708 debug.dbg and debug.warning now take a string and format args parameters to make debugging a little bit cleaner 2014-01-13 16:16:07 +01:00
Dave Halter
157f76a55d keywords should be part of the api package 2014-01-13 14:24:34 +01:00
Dave Halter
cf0a2e8c2e use FakeStatement instead of strange Statement constructions 2014-01-13 14:14:04 +01:00
Dave Halter
83b490dd6d PyObject -> CompiledObject, PyName -> CompiledName 2014-01-13 14:09:03 +01:00
Dave Halter
0c98c05cd3 use __import__ instead of exec_function (should have done that a long time ago) 2014-01-13 14:01:03 +01:00
Dave Halter
1c1349162c memory_check.py should be an executable script 2014-01-13 13:55:29 +01:00
Dave Halter
292d33e2d1 Merge pull request #363 from davidhalter/builtin
Fixing Memory Issues in Compiled Modules
2014-01-13 03:20:00 -08:00
Dave Halter
6b3ebe50d8 scripts folder doesn't need testing - just ignore it 2014-01-13 03:18:17 +01:00
Dave Halter
fd8c4bcf67 Merge pull request #367 from blink1073/memory_check
Add a module to test the memory usage with large libaries
2014-01-12 18:11:49 -08:00
Dave Halter
e56a0cf544 docstring was sometimes empty for faked modules 2014-01-13 02:58:10 +01:00
blink1073
9478908346 Add a module to test the memory usage with large libaries 2014-01-12 19:57:02 -06:00
Dave Halter
a96a2baf5b fix an issue with missing '__class__' methods e.g. in numpy 2014-01-13 02:30:10 +01:00
Dave Halter
c602dc1c40 modules like PyQt4.QtGui are now importable, because the import works again. used a code snippet from the old builtin plugin 2014-01-13 01:57:26 +01:00
Dave Halter
b2d99be3ee delete old builtin code 2014-01-12 19:23:37 +01:00
Dave Halter
4b319ad817 fix a docstr issue 2014-01-12 19:22:31 +01:00
Dave Halter
dfb494b9c4 finally able to delete the old builtin stuff 2014-01-12 18:22:33 +01:00
Dave Halter
860aa50192 renamed fake/_io.pym to io.pym and fixed some other 'fake' issues 2014-01-12 18:17:00 +01:00
Dave Halter
2bde6cde08 changing completion of python file objects 2014-01-12 18:07:58 +01:00
Dave Halter
99fe204496 Merge remote-tracking branch 'origin/dev' into builtin 2014-01-12 17:04:48 +01:00
Dave Halter
c75cef0882 fix some python 3 compatibility things (which involves some real bugs, but py2 was passing) 2014-01-12 17:02:26 +01:00
Dave Halter
4e18fe1e11 Merge pull request #365 from blink1073/dev
Allow Jedi to be imported from a REPL on Windows
2014-01-12 06:08:11 -08:00
blink1073
f47d529f76 Allow creation of the Fore class on Windows 2014-01-12 07:53:06 -06:00
blink1073
4045e6f239 Fix troublesome import of colorama on Windows 2014-01-12 07:50:46 -06:00
Dave Halter
e4f3f5bea2 fix recursion issue with compiled classes 2014-01-12 14:28:42 +01:00
Dave Halter
bd239446f5 fix call signatures 2014-01-12 02:42:00 +01:00
Dave Halter
7a0dc41b62 fixed doctest issues 2014-01-12 02:23:35 +01:00
Dave Halter
0bff729294 lots of small bugfixes 2014-01-12 02:15:59 +01:00
Dave Halter
b93c761db6 after fixing private variable filtering it looks much better 2014-01-12 01:11:59 +01:00
Dave Halter
4006b231d3 fix a few last standing issues with integration tests. ok after running tests with tox i see that they are not the last issues... 2014-01-12 00:34:56 +01:00
Dave Halter
1765fadf73 fix problems with self attributes - from fake modules 2014-01-11 18:05:44 +01:00
Dave Halter
d430ef53a7 fix a few more minor issues 2014-01-11 16:14:58 +01:00
Dave Halter
c6a14a348e parent was wrongly used in _create_from_name 2014-01-11 16:04:05 +01:00
Dave Halter
bfe0c62e7f filter None (is more a keyword than a builtin object) 2014-01-11 15:58:14 +01:00
Dave Halter
fc35e69a16 adding helpers.FakeName and other fakes to make it easier to fake parser names and statements 2014-01-11 13:41:03 +01:00
Dave Halter
6f9d834a93 now able to execute instance subscopes on compiled 2014-01-11 13:01:09 +01:00
Dave Halter
28ab937eca in the process... 2014-01-11 02:55:50 +01:00
Dave Halter
e7c7bbca79 simple debugging improvement - make it more readable by giving it an indent 2014-01-11 01:58:31 +01:00
Dave Halter
19fa320c88 a hopefully simple integration of PyObject into Instance 2014-01-11 01:48:59 +01:00
Dave Halter
8337f77886 a few other small changes before changing compiled Instance execution to the representation 2014-01-11 01:19:09 +01:00
Dave Halter
32e39ef4ca fixing parents in compiled 2014-01-11 00:26:53 +01:00
Dave Halter
01c03966a7 make first faked compiled modules work 2014-01-10 23:35:58 +01:00
Dave Halter
78cc015b9d start introducing the compiled.fake module that fakes builtin code 2014-01-10 22:58:49 +01:00
Dave Halter
400b0a4aa7 move mixin to compiled/fake to structure it better 2014-01-10 16:38:13 +01:00
Dave Halter
8854206f2a created a module for compiled 2014-01-10 16:37:28 +01:00
Dave Halter
14c9ed88ca fixed the getattr/__getattr__ stuff 2014-01-10 15:14:55 +01:00
Dave Halter
f868668f0e trying to fix the getattr mess with compiled 2014-01-10 13:36:29 +01:00
Dave Halter
b1409c8f74 fix a name glitch in keywords 2014-01-10 02:03:02 +01:00
Dave Halter
59b379ccc5 fixed most function issues 2014-01-10 00:52:39 +01:00
Dave Halter
9056dc1b9b fixed some array indexing 2014-01-09 19:55:00 +01:00
Dave Halter
0234c1429b fix a few more tracebacks 2014-01-09 17:54:40 +01:00
Dave Halter
dfd9a779c3 fix magic_function issues with compiled module 2014-01-09 17:09:31 +01:00
Dave Halter
f755e615c9 magic_method -> magic_function 2014-01-09 17:06:02 +01:00
Dave Halter
db149a84a8 fixed a lot of import related problems 2014-01-09 16:50:31 +01:00
Dave Halter
7965cae373 also changed the imports stuff to partially support compiled 2014-01-09 14:38:15 +01:00
Dave Halter
11e2446438 replaced builtin with compiled in all modules except imports 2014-01-09 11:05:04 +01:00
Dave Halter
d2358c60b7 more builtin replacements with compiled 2014-01-09 02:02:33 +01:00
Dave Halter
df6317f8b0 already a lot of tests pass - time to kick it up a notch 2014-01-09 01:56:27 +01:00
Dave Halter
e7e802408b fix some api stuff for PyObject 2014-01-09 01:52:10 +01:00
Dave Halter
0cb23dcfa2 tried to start introducing the compiled module to the library 2014-01-09 01:30:29 +01:00
Dave Halter
3017e72b86 first executions with compiled seem to be working 2014-01-09 00:53:50 +01:00
Dave Halter
7af9e6dfd7 pushed the _parse_function_doc to compiled 2014-01-08 22:01:26 +01:00
Dave Halter
d71fe3061b better environment for compiled executions 2014-01-08 02:20:41 +01:00
Dave Halter
f257e279c2 pass the first test 2014-01-07 18:48:54 +01:00
Dave Halter
70413768ef add a 'compiled' module, to finally solve #102 and #335 2014-01-07 18:45:02 +01:00
Dave Halter
f3768f818b Merge pull request #362 from davidhalter/refactoring
Refactoring Jedi for code readability
2014-01-07 09:35:44 -08:00
Dave Halter
1a3541e3aa removed from 'from __future__ import with_statement' imports, they are not needed anymore (python 2.5 is no longer supported) 2014-01-07 17:46:22 +01:00
Dave Halter
73aeee6919 make defined_names public in api.classes, because it is 2014-01-07 15:47:00 +01:00
Dave Halter
6deac1dc41 api is now a separate package, to structure the whole thing better. 2014-01-07 15:33:24 +01:00
Dave Halter
3126031ff2 move module checking again - probably the last time (resolves an import issue) 2014-01-07 15:20:00 +01:00
Dave Halter
a74b7299e2 scan_statement -> scan_statement_for_calls 2014-01-07 15:10:19 +01:00
Dave Halter
359f3ed4a9 also moved scan_statement away from dynamic 2014-01-07 15:06:11 +01:00
Dave Halter
a5fa739960 moved another method away from dynamic 2014-01-07 15:01:59 +01:00
Dave Halter
8a9453872f cleaned up evaluate.param 2014-01-07 14:44:39 +01:00
Dave Halter
1881a24e73 moved the param generation into another file 2014-01-07 14:33:27 +01:00
Dave Halter
69afc2482a nicer _remove_statements 2014-01-07 13:25:30 +01:00
Dave Halter
a3e4b209c7 _remove_statements beautified 2014-01-07 12:19:05 +01:00
Dave Halter
35640abd82 split param stuff from remove_statements - which is now finally a simple method 2014-01-07 12:02:33 +01:00
Dave Halter
66ec389f5c make _remove_statements smaller 2014-01-07 11:32:20 +01:00
Dave Halter
821d2b9220 remove some recursion 2014-01-07 11:28:01 +01:00
Dave Halter
546a7bbad9 merged _some_method and _remove_statements 2014-01-07 10:53:53 +01:00
Dave Halter
d1a4eccf13 prepare merging of some_method and remove_statement 2014-01-07 00:54:45 +01:00
Dave Halter
bbc15d4349 moved some NameFinder methods around 2014-01-07 00:38:43 +01:00
Dave Halter
a66589161d remove is_goto parameters from NameFinder - yay, finally reached a first longtime goal 2014-01-07 00:25:02 +01:00
Dave Halter
d38c4f7482 split is_array_assignment and no_break_scope calculations 2014-01-07 00:11:25 +01:00
Dave Halter
41eb305d41 removed another whole lot of code previously added as a hack and try/error 2014-01-06 23:54:11 +01:00
Dave Halter
453421395f reduced the first process method 2014-01-06 23:46:23 +01:00
Dave Halter
090536d03c fix by disabling test - usages are tainted crap anyway :) 2014-01-06 23:41:40 +01:00
Dave Halter
292fb010ca improve the hack (still not passing tests) 2014-01-06 23:16:15 +01:00
Dave Halter
51abedcae1 trying to get some NameFinder things refactored, by adding huge hacks (I hope to remove them later). 2014-01-06 23:11:35 +01:00
Dave Halter
abe8de679b update ignored debug modules 2014-01-06 21:56:16 +01:00
Dave Halter
ce207e6dbb goto shouldn't call names_to_types 2014-01-06 21:20:38 +01:00
Dave Halter
8e982bf25c move __getattr__ checks 2014-01-06 20:43:24 +01:00
Dave Halter
17c18aba98 lambdas should only exist if not named 2014-01-06 20:31:49 +01:00
Dave Halter
03ed2c8969 added names_to_types 2014-01-06 18:00:06 +01:00
Dave Halter
a10f34ab1c move dynamic flow stuff to finder 2014-01-06 16:38:21 +01:00
Dave Halter
117a9d8cf2 simplified flowscope stuff again 2014-01-06 16:35:15 +01:00
Dave Halter
3f80de34e3 move flowscope stuff to the right place 2014-01-06 16:27:58 +01:00
Dave Halter
0e69ad478b moved a few closures around in NameFinder 2014-01-06 14:36:24 +01:00
Dave Halter
53dbec52ab use find_types instead of find_names 2014-01-06 14:29:23 +01:00
Dave Halter
0a87f8b02f fix last few glitches in NameFinder 2014-01-06 13:26:00 +01:00
Dave Halter
7e874f8c9f basic implementation of the new evaluate.finder module, moved the whole find_name procedure there 2014-01-06 11:43:05 +01:00
Dave Halter
887418bbfd move helpers to evaluate 2014-01-06 00:09:29 +01:00
Dave Halter
1e3b936052 move FakeStatement 2014-01-06 00:01:25 +01:00
Dave Halter
c9efc15ea0 delete unused check_arr_index function 2014-01-05 23:54:38 +01:00
Dave Halter
39c16237da remove a circular dependency for docstrings. 2014-01-05 14:45:01 +01:00
Dave Halter
fbfab9eefb moved docstrings module to evaluate 2014-01-05 14:40:21 +01:00
Dave Halter
5857b4dbc9 moved modules.py -> parser/user_context.py 2014-01-05 14:15:11 +01:00
Dave Halter
3afda3cb3e more UserContext improvements 2014-01-05 14:10:55 +01:00
Dave Halter
d4701d7be8 refactor ModuleWithCursor to UserContext 2014-01-05 13:57:19 +01:00
Dave Halter
6df69478dc move load_module to imports 2014-01-05 13:51:22 +01:00
Dave Halter
2e65fbb00f rename parser.tokenizer -> parser.tokenize 2014-01-05 13:38:14 +01:00
Dave Halter
261f49d3e2 move NoErrorTokenizer to the tokenizer module, where it more or less belongs. 2014-01-05 13:34:29 +01:00
Dave Halter
e115689b7f move NoErrorTokenizer to the parser, where it more or less belongs. 2014-01-05 13:25:11 +01:00
Dave Halter
fce36ebea4 move source_to_unicode to common 2014-01-05 13:17:04 +01:00
Dave Halter
9523e70a71 sys_path stuff has its own module, now. 2014-01-05 13:07:37 +01:00
Dave Halter
40c7949d20 remove parser from modules.py 2014-01-05 12:55:28 +01:00
Dave Halter
78ac8b2fd6 use it for the parser representation as well 2014-01-05 11:29:12 +01:00
Dave Halter
cc950c5ddb also apply to api_classes 2014-01-05 10:44:25 +01:00
Dave Halter
d0a1f66777 apply underscore_memoization to builtin 2014-01-05 10:41:41 +01:00
Dave Halter
471cf742dc add an cache.underscore_memoization decorator to make some recurring patterns easier to read 2014-01-05 10:37:28 +01:00
Dave Halter
4fdfbcd7e4 make the invalidate_star_import stuff easier 2014-01-05 10:18:04 +01:00
Dave Halter
1b40414d90 skip the strange add additional_modules test for now 2014-01-05 01:53:51 +01:00
Dave Halter
4d7349411e probably finished the load_module method migration 2014-01-05 01:37:54 +01:00
Dave Halter
29e661ea74 remove a lot of modules crap and replace it with a simple method called load_module 2014-01-04 14:35:11 +01:00
Dave Halter
962a678417 minor api refactorings 2014-01-04 13:33:09 +01:00
Dave Halter
036f119e68 protect mor variables 2013-12-30 02:24:32 +01:00
Dave Halter
f1862120e2 protect the assignments stuff 2013-12-30 02:23:15 +01:00
Dave Halter
0c62d7d0d9 evaluate.iterable also owns get_iterator_types now 2013-12-30 01:56:40 +01:00
Dave Halter
6ece1de22d move some imports around 2013-12-30 01:49:01 +01:00
Dave Halter
7b936cf6ec move dynamic array stuff to evaluate.iterable 2013-12-30 01:38:15 +01:00
Dave Halter
e4692381cb created evaluate.iterable to push arrays and generators into a seperate file 2013-12-30 01:02:18 +01:00
Dave Halter
8561217333 evaluator executions instead of direct instances 2013-12-29 19:40:45 +01:00
Dave Halter
0f6b5b222b add a stdlib module to make it easy to write functions in pure python (instead of evaluating them) 2013-12-29 03:05:05 +01:00
Dave Halter
4215e7934e basic refactoring, of function executions super() is not working yet 2013-12-29 02:05:37 +01:00
Dave Halter
870f5da354 use execute method instead of Execution creators 2013-12-28 21:21:15 +01:00
Dave Halter
37e157d441 improve follow_path params 2013-12-28 20:56:09 +01:00
Dave Halter
75a1b6f8cb follow_paths -> follow_path, follow_path -> _follow_path 2013-12-28 15:00:04 +01:00
Dave Halter
46dd0a9abe follow_call_path -> eval_call_path 2013-12-27 14:40:23 +01:00
Dave Halter
91f377eeb6 pep8 stylings 2013-12-27 14:35:30 +01:00
Dave Halter
7347c46502 expression_list instead of commands in more places 2013-12-27 14:32:15 +01:00
Dave Halter
4af92b166a use expression_list instead of commands or call_list 2013-12-27 14:24:14 +01:00
Dave Halter
76eec7bfc5 follow_call -> eval_call 2013-12-27 12:02:49 +01:00
Dave Halter
eb30c3e6cf follow_statement -> eval_statement 2013-12-27 11:55:35 +01:00
Dave Halter
b7958b32a3 Merge pull request #360 from davidhalter/evaluator
Refactoring: Use an Evaluator class.
2013-12-26 17:49:00 -08:00
Dave Halter
d40030c14e disable a test for py26. 2013-12-27 02:40:52 +01:00
Dave Halter
08fba1e191 fix last remaining issues with tests 2013-12-27 02:28:01 +01:00
Dave Halter
4ec64a9763 move mixin to evaluate directory 2013-12-27 01:36:05 +01:00
Dave Halter
c290afbb1a increase pickling version and change mixin path for builtins 2013-12-27 01:34:23 +01:00
Dave Halter
40bd118acb fix sys path tests 2013-12-27 01:30:22 +01:00
Dave Halter
c862afb967 fix a few nasty bugs 2013-12-27 01:27:07 +01:00
Dave Halter
ff983f70bc fix execution recursion decorators 2013-12-26 18:01:54 +01:00
Dave Halter
dd7d7ceb7e make some parser caches private 2013-12-26 12:20:29 +01:00
Dave Halter
84c2be9f58 remove old way of using a separate decorator for search param memoization, use the default one 2013-12-26 02:32:32 +01:00
Dave Halter
8f564a301f clean up caches clearing 2013-12-26 02:10:30 +01:00
Dave Halter
947e616da0 hand api classes the evaluator 2013-12-26 01:00:37 +01:00
Dave Halter
4aa8be7829 include jedi.evaluate as a package in setup.py 2013-12-25 19:08:28 +01:00
Dave Halter
05f7e6f4b5 import changes so that all local imports are 'from jedi.* import *' 2013-12-25 18:48:12 +01:00
Dave Halter
002b7001ca pass more than half of the integration tests, more bugfixes 2013-12-25 18:38:23 +01:00
Dave Halter
f039bc3fc9 fix docstrings with evaluator stuff 2013-12-25 18:31:42 +01:00
Dave Halter
d86f180d2e bug fixes for dynamic/iterator stuff 2013-12-25 18:28:56 +01:00
Dave Halter
5d486f0e3c fix a lot of the dynamic issues 2013-12-25 18:16:40 +01:00
Dave Halter
b768e214eb fix cache memoization 2013-12-25 15:19:12 +01:00
Dave Halter
4cf5d56b64 fix more issues and renamings 2013-12-25 03:24:12 +01:00
Dave Halter
ef764d39d8 evaluator stuff for dynamic 2013-12-25 01:54:51 +01:00
Dave Halter
dd804dc4cb move the usages stuff away from dynamic 2013-12-24 22:54:03 +01:00
Dave Halter
3c28b1907c dynamic belongs also into the evaluate folder 2013-12-24 22:49:57 +01:00
Dave Halter
8326658134 more improvements, so that almost all import tests pass again 2013-12-24 19:55:11 +01:00
Dave Halter
9e52d51a6b use filter_private_variable correctly in api 2013-12-24 19:50:53 +01:00
Dave Halter
39ed0d0f8a most evaluate representation classes probably receive the evaluator instance now 2013-12-24 19:47:52 +01:00
Dave Halter
85a12e8866 RecursionDecorator now also works with Evaluator caches 2013-12-24 19:14:14 +01:00
Dave Halter
118ed2e0a3 RecursionNode should be public 2013-12-24 18:49:15 +01:00
Dave Halter
ab2c6bf45d integrate memoize caches into evaluator 2013-12-24 18:46:51 +01:00
Dave Halter
066056fb2b memoize_default should always take a first argument 2013-12-24 18:06:15 +01:00
Dave Halter
56457a9c8c ImportPath has now also an _evaluator 2013-12-24 18:00:00 +01:00
Dave Halter
d25cc6e1fe it should be _evaluator not evaluator 2013-12-24 17:52:01 +01:00
Dave Halter
eedadbfe95 temporary interfaces subclass. let's see if we need to keep it 2013-12-24 17:27:06 +01:00
Dave Halter
cd8880d8ef move recursion.py to evaluate folder 2013-12-24 17:19:07 +01:00
Dave Halter
cf4b2c4557 the whole import thing seems to be working 2013-12-24 16:28:28 +01:00
Dave Halter
7bf6ff768c first steps to world domination -> removal of the hackish sys.path modifications 2013-12-24 16:23:13 +01:00
Dave Halter
4e68856cc4 move builtin and imports into the evaluate directory 2013-12-24 16:13:26 +01:00
Dave Halter
b9719b8c13 first tests running again with the new structure 2013-12-24 16:05:43 +01:00
Dave Halter
28771b8f15 remove import problems - evaluate calls from a lot of places 2013-12-24 15:50:15 +01:00
Dave Halter
ae17c66148 use evaluator everywhere in representation 2013-12-24 15:38:40 +01:00
Dave Halter
65013d1019 add an execute method to the evaluator 2013-12-24 15:24:37 +01:00
Dave Halter
5f4bcd330c evaluator class introduction 2013-12-24 15:21:09 +01:00
Dave Halter
cbe93298ce move evaluate and evaluate_representation to evaluate folder 2013-12-24 15:14:06 +01:00
Dave Halter
e4013cee77 pep cleanups 2013-12-24 11:47:28 +01:00
David Halter
5b33de991b protect more Execution methods 2013-12-23 22:58:02 +01:00
David Halter
8d2b3a0f7c Executable.decorated should be protected 2013-12-23 22:52:17 +01:00
Dave Halter
a885d85fda Merge pull request #358 from ColinDuquesnoy/dev
Fix for pyqt errors with python 2.7
2013-12-22 15:19:36 -08:00
Colin
d96bc94b0b Remove test pyqt 2013-12-21 14:24:12 +01:00
Colin
478140caab Add a comment 2013-12-21 14:23:41 +01:00
ColinDuquesnoy
c7d4c0453a Add a regression test 2013-12-20 13:48:58 +01:00
ColinDuquesnoy
3da4ff1f89 Fix for #357/#331 2013-12-20 13:48:49 +01:00
David Halter
3a8692e730 merge authors.txt 2013-12-16 20:40:24 +01:00
Jean-Louis Fuchs
cc1a89b637 * simplified statement 2013-12-13 01:24:25 +01:00
Jean-Louis Fuchs
d687fa4df6 * replaced docstr-string with TokenDocstring object 2013-12-13 01:22:56 +01:00
Jean-Louis Fuchs
53e4962711 * started create paralell get_code 2013-12-12 22:11:15 +01:00
Jean-Louis Fuchs
0e00aa103f * created basic test 2013-12-12 19:13:12 +01:00
David Halter
e34874543e Merge pull request #353 from ganwell/tokens
Token objects
2013-12-08 04:40:32 -08:00
Jean-Louis Fuchs
3204a39f6c * made Token readonly like a tuple by using @property
* end_pos calculation didn't respect multiline tokens
* replaced all index access to Token
* wrapped all code that injects token tuples with
  Token.from_tuple()
* repr of Token is still its tuple form!?
* PEP8 where I read or wrote code
2013-12-08 01:32:58 +01:00
Jean-Louis Fuchs
13680945d6 * imported utf8 for python 3.2 2013-12-07 23:00:47 +01:00
Jean-Louis Fuchs
e204c27ecc * imported u for python 3.2 2013-12-07 22:53:01 +01:00
Jean-Louis Fuchs
5c543ac364 * unicode compatibility 2013-12-07 22:50:20 +01:00
Jean-Louis Fuchs
f1c49db6b6 * Added doctests to Token class
* Added __setattr__ for symmetry
* Code cleanup
2013-12-06 02:51:28 +01:00
Jean-Louis Fuchs
ced926b7f0 * cleanup 2013-12-06 00:11:38 +01:00
Jean-Louis Fuchs
099a6c2697 * added token.py 2013-12-05 23:59:48 +01:00
Jean-Louis Fuchs
989e12e8a7 * Replaced token tuple with token class
* Fixed PEP8 where I read code
2013-12-05 23:34:20 +01:00
David Halter
4ff8b921a3 editor list always with proper urls at the end (easier to copy) 2013-11-23 14:31:59 +01:00
David Halter
a80a1fc19b add company jedi by @proofit404 to emacs jedi plugins 2013-11-23 14:29:21 +01:00
David Halter
35c05f3162 Merge pull request #338 from ColinDuquesnoy/master
Doc: add warning about thread safety
2013-10-31 13:42:10 -07:00
ColinDuquesnoy
ebbe0eeb18 Add name to authors 2013-10-31 17:18:59 +01:00
ColinDuquesnoy
1fccefab5a Documentation: add thread safety warning in API 2013-10-31 16:26:49 +01:00
David Halter
099fe4eeb3 test a little bit more with the not in sys path packages 2013-10-11 23:41:38 +04:30
David Halter
873fc16247 py33 fix for an undefined name 2013-10-11 23:37:31 +04:30
David Halter
ec5ad4ac9c Merge branch 'dev' of github.com:davidhalter/jedi into dev 2013-10-11 23:25:19 +04:30
David Halter
fb5b39a7bd remove a strange imports.py code line 2013-10-11 23:21:13 +04:30
David Halter
f8f2194db3 imports that are not in sys.path - as a second option to those in sys.path 2013-10-11 23:18:29 +04:30
David Halter
ee181ea098 use encoding instead of source_encoding as a param in the API. I really hope this doesn't break anything, but I doubt it. 2013-10-07 23:52:51 +04:30
David Halter
4ea5f370eb use GlobalNamespace differently 2013-10-07 23:50:27 +04:30
David Halter
ea25b1489a GlobalNamespace doesn't use a lot of attributes - remove them 2013-10-07 23:47:06 +04:30
David Halter
c4896f767c remove unused ModuleNotFound exception raise 2013-10-07 23:41:20 +04:30
David Halter
b357c099bc test for imports that are not in the sys.path. 2013-10-07 21:43:23 +04:30
David Halter
7e6ee728ea Merge pull request #323 from blink1073/gc-disable
Disabling gc while loading pickles for a 20% speed boost on larger libs
2013-10-06 08:55:38 -07:00
Steven Silvester
9cac894273 Disabling gc while loading pickles for a 20% speed boost on larger libs 2013-10-06 06:26:36 -05:00
David Halter
b6c30b542a merge fix for docs 2013-09-14 23:27:36 +04:30
David Halter
566d1023ee don't load builtin modules, if not necessary in python3.3 2013-09-14 23:15:41 +04:30
David Halter
92237b5598 test fix in python 3.2 2013-09-14 23:13:35 +04:30
David Halter
a2ceaf1987 fixed import problems in python3.3 2013-09-14 23:12:09 +04:30
David Halter
0025a8c61e added failing test for import problems in py33 2013-09-14 23:11:47 +04:30
David Halter
5ad12bc8aa fix mro usage and all the type stuff (see also my blog post about why dir is wrong). fixes #314, fixes #86 2013-09-13 22:47:00 +04:30
David Halter
b17de836c3 Added SynJedi also to sphinx docs 2013-09-12 13:25:21 +04:30
David Halter
cedae32605 Add SynWrite to supported editors. 2013-09-11 22:38:42 +04:30
David Halter
70123a6499 awesome badge power again :-) 2013-09-09 20:09:14 +04:30
David Halter
ae365eb930 Merge branch 'dev' of github.com:davidhalter/jedi into dev 2013-09-08 22:28:26 +04:30
David Halter
353bc54642 fix reversed builtin in 2.7 2013-09-07 22:56:01 +04:30
David Halter
06cdc3753a Merge pull request #316 from asmeurer/sith_run
Try to make it clearer that the first output of sith.py run is just the input
2013-09-06 21:41:45 -07:00
Aaron Meurer
fc4e5408ba Try to make it clearer that the first output of sith.py run is just the input 2013-09-06 20:39:26 -06:00
David Halter
9feb76ed53 add _io mixin because that's what open in python3 returns 2013-09-06 16:10:00 +04:30
David Halter
39a5c8501b fixed file in python 2.7 with test, fixes #309 2013-09-06 16:00:12 +04:30
David Halter
9bbd73bf86 fix problems with set_vars that were no set_vars 2013-09-06 15:24:14 +04:30
David Halter
6f16aaaa19 corrected an old comment 2013-09-06 15:12:54 +04:30
David Halter
d216ab331a also add a test for class execution, which still fails due to another bug, that has been (almost) fixed lately, fixes #305 2013-09-06 15:09:12 +04:30
David Halter
fcc6c4d811 goto before assignments should still work, 305 2013-09-06 14:56:35 +04:30
David Halter
95bac43840 before assignment goto test, #305 2013-09-06 14:35:56 +04:30
David Halter
648095ad52 sorted builtin should now be working, fixes #312 2013-09-06 14:08:54 +04:30
David Halter
b18cafa8f8 test for #312, sorted/reversed builtins 2013-09-06 13:27:15 +04:30
David Halter
2deccb6d66 correct docstring, fixes #310 2013-09-06 13:03:30 +04:30
David Halter
c74aa71549 update development docs with parser changes 2013-09-06 01:49:46 +04:30
David Halter
92a3491b3d removed an unused tokenizer import 2013-09-06 01:45:29 +04:30
David Halter
9eca33f55c final import renamings for the new parser package 2013-09-06 01:27:07 +04:30
David Halter
3e217a8270 setup.py add new parser package 2013-09-06 01:21:18 +04:30
David Halter
13f0c2b91f all the import changes 2013-09-06 01:19:19 +04:30
David Halter
390442dc3b move all the parser stuff into a seperate package 2013-09-06 00:58:57 +04:30
David Halter
dd4d0bc619 remove unused ParserError 2013-09-06 00:49:21 +04:30
David Halter
33711ba966 fix problems with literals, finally 2013-09-06 00:40:08 +04:30
David Halter
c3ba7d2ae8 remove except only clauses 2013-09-06 00:02:32 +04:30
David Halter
78f3199b03 fix some tests, that have been broken by literals 2013-09-05 23:51:25 +04:30
David Halter
1b5f4f5e0b literal classes mostly working 2013-09-05 23:45:56 +04:30
David Halter
fc5fdf929a actually use the new types in the parser 2013-09-05 22:29:24 +04:30
David Halter
7383e5dc53 add String and Number instead of abusing Call for it 2013-09-05 22:19:05 +04:30
David Halter
9e54abaf22 StatementElement instead of Call and Call now inherits from that 2013-09-05 21:50:05 +04:30
David Halter
458497747b add pypy to failing travis tests 2013-09-04 21:35:54 +04:30
David Halter
1c75ced693 fix tox problem with sith tests 2013-09-04 21:31:48 +04:30
David Halter
d483143d47 fix a very annoying caching problem 2013-09-03 22:41:56 +04:30
David Halter
4c6a58644e removed used_vars and set_vars parameters, because they are unused 2013-09-03 01:17:35 +04:30
David Halter
ba228d2ca0 remove used_vars completely 2013-09-03 01:05:58 +04:30
David Halter
31d992207c add a names_are_set_vars variable to statement 2013-09-03 01:01:46 +04:30
David Halter
1fa5d34878 move some things from Flow to ForFlow 2013-09-03 00:22:09 +04:30
David Halter
15c9ed573d it seems like all relevant used_vars usages have been removed 2013-09-02 23:43:51 +04:30
David Halter
a355c8c54c remove more used_vars 2013-09-02 23:36:26 +04:30
David Halter
a2acc6a8a9 remove last_token 2013-09-02 23:25:50 +04:30
David Halter
47d250494e simplified and united docstring version 2013-09-02 23:21:54 +04:30
David Halter
2b88640c3a statements should care for its own docstrings, not the parser 2013-09-02 23:12:10 +04:30
David Halter
6ac3cfdece remove one more used_vars usage 2013-09-02 23:04:52 +04:30
David Halter
afc388e2d7 replace used_vars for params 2013-09-02 22:03:47 +04:30
David Halter
e442dbbc4f move also used_vars to a property, to show that it's not being written to 2013-09-02 21:52:42 +04:30
David Halter
37ecf943af finally able to remove stmt.set_vars property 2013-09-02 21:43:23 +04:30
David Halter
9565aa1431 version should be the next one (with alpha) and not a past one, thx @ramiro 2013-09-02 21:23:40 +04:30
David Halter
9367b54a3d get rid of set_vars class var in statement and define a wrapper for it, because it's still used 2013-09-02 17:23:08 +04:30
David Halter
a8510e51f1 use as_names as a way to inject 'set_vars' to statements 2013-09-02 16:23:13 +04:30
David Halter
ad16f34cda generate set_vars now 2013-09-02 16:02:18 +04:30
David Halter
a253f07827 get rid of set_vars parent setting (use token_list instead). 2013-09-02 15:21:28 +04:30
David Halter
05b37e61cb preparation to get rid of set_vars 2013-09-02 15:14:15 +04:30
David Halter
9c6dae1df8 temporarily disable old tests to refactor set_vars 2013-09-01 23:54:15 +04:30
David Halter
2e97986545 assignment details description 2013-09-01 22:22:20 +04:30
David Halter
58d3ba37e9 Jedi doesn't support del yet 2013-09-01 20:06:40 +04:30
David Halter
3d3157eff8 remove unused set_vars, but there are still set_vars that shouldn't be in defined_names, #205 2013-08-31 23:52:12 +04:30
David Halter
2ee7ee4473 regression test for #205 2013-08-31 19:59:39 +04:30
David Halter
4c3ac7fe2e s/readthedocs.org/jedidjah.ch/g 2013-08-31 19:03:02 +04:30
David Halter
7b1290a182 last documentation fixes 2013-08-31 18:59:29 +04:30
David Halter
0fd0b0efc6 split api classes from api documentation, fixes #212 2013-08-31 18:45:55 +04:30
David Halter
ed554411b8 remove deleted docs entries from index 2013-08-31 18:39:37 +04:30
David Halter
3baf4a1711 add repl stuff to usage docs 2013-08-31 18:38:28 +04:30
David Halter
d9b4218ca6 Jedi usage chapter as a first chapter for Jedis documentation. 2013-08-31 18:27:16 +04:30
David Halter
39e766c2eb add recipes also to features & caveats 2013-08-31 18:15:21 +04:30
David Halter
7d97d70dc1 move history to features and caveats 2013-08-31 18:12:56 +04:30
David Halter
7f439b67cb use a sphinx toctree of depth 2 2013-08-31 18:10:50 +04:30
David Halter
9023d01dcb jedi settings should have its own documentation page, #212 2013-08-31 18:04:07 +04:30
David Halter
4e9bb0c062 add module_path documentation, fixes #306 2013-08-31 17:53:38 +04:30
David Halter
1ebf68999a make source parameter in Script optional 2013-08-31 14:41:37 +04:30
David Halter
b8a909d352 fix a wrong usage of column in absolute import tests 2013-08-31 13:39:08 +04:30
David Halter
0d221ffebc line/column ValueError are now being raised if they are not valid in Script 2013-08-31 11:51:52 +04:30
David Halter
23ece5e4c8 Script needs valid ranges, otherwise ValueError should be raised (test) 2013-08-31 11:47:10 +04:30
David Halter
5267e13e0a fixed a problem with classes in docstrings 2013-08-31 11:32:35 +04:30
David Halter
b68e3cc8de problems with class in docstring 2013-08-31 00:03:02 +04:30
David Halter
8518311a22 clean up _parse_function_doc a little bit, #298 2013-08-26 20:31:50 +04:30
David Halter
345ba0d3c2 docstrings of builtins can be non-standard, fixes #298 2013-08-26 20:21:03 +04:30
David Halter
8cefc8d49b provide test for #298 2013-08-26 20:16:55 +04:30
David Halter
951dc52c03 Merge branch 'dev' of github.com:davidhalter/jedi into dev 2013-08-24 01:39:57 +04:30
Danilo Bargen
e83b3edcac Fixed search URL for debian package 2013-08-23 22:59:45 +02:00
Danilo Bargen
be97009fb4 Updated debian package URL 2013-08-23 10:07:00 +02:00
David Halter
01cd75f388 small readme correction 2013-08-22 22:17:21 +04:30
David Halter
cce425cb6d move BaseDefinition.path to _path 2013-08-22 12:35:08 +04:30
David Halter
2f3304b9f1 test for multiple call_signatures 2013-08-21 15:47:25 +04:30
David Halter
eca786bf0e fix a problem with empty scripts 2013-08-21 13:42:18 +04:30
David Halter
2e8432de21 don't use function_definition as a name anymore, also not in helper functions 2013-08-21 13:38:13 +04:30
David Halter
80ec8da513 version bump to 0.7.0.'alpha1', fixes #226 2013-08-19 14:19:40 +04:30
David Halter
432818e4cd add docs to pypi release, #256 2013-08-19 13:38:27 +04:30
David Halter
645a197d5a Merge branch 'dev' 2013-08-19 12:11:32 +04:30
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
35fe689b2b Merge pull request #281 from davidhalter/dev
New release: 0.7.0
2013-08-17 11:45:38 -07:00
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
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
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
220 changed files with 19721 additions and 9985 deletions

2
.gitignore vendored
View File

@@ -10,3 +10,5 @@
/docs/_build/
/dist/
jedi.egg-info/
record.json
/.cache/

View File

@@ -4,10 +4,15 @@ env:
- TOXENV=py27
- TOXENV=py32
- TOXENV=py33
- TOXENV=py34
- TOXENV=pypy
- TOXENV=cov
- TOXENV=sith
matrix:
allow_failures:
- env: TOXENV=cov
- env: TOXENV=sith
- env: TOXENV=pypy
install:
- pip install --quiet --use-mirrors tox
script:

View File

@@ -1,14 +1,37 @@
Main Authors
============
David Halter (@davidhalter)
Takafumi Arakaki (@tkf)
David Halter (@davidhalter) <davidhalter88@gmail.com>
Takafumi Arakaki (@tkf) <aka.tkf@gmail.com>
Code Contributors
=================
Danilo Bargen (@dbrgn)
Danilo Bargen (@dbrgn) <mail@dbrgn.ch>
Laurens Van Houtven (@lvh) <_@lvh.cc>
Aldo Stracquadanio (@Astrac) <aldo.strac@gmail.com>
Jean-Louis Fuchs (@ganwell) <ganwell@fangorn.ch>
tek (@tek)
Yasha Borevich (@jjay)
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) <hattya@gmail.com>
srusskih (@srusskih)
Steven Silvester (@blink1073)
Colin Duquesnoy (@ColinDuquesnoy) <colin.duquesnoy@gmail.com>
Jorgen Schaefer (@jorgenschaefer) <contact@jorgenschaefer.de>
Fredrik Bergroth (@fbergroth)
Mathias Fußenegger (@mfussenegger)
Syohei Yoshida (@syohex) <syohex@gmail.com>
ppalucky (@ppalucky)
immerrr (@immerrr) immerrr@gmail.com
Albertas Agejevas (@alga)
Savor d'Isavano (@KenetJervet) <newelevenken@163.com>
Phillip Berndt (@phillipberndt) <phillip.berndt@gmail.com>
Ian Lee (@IanLee1521) <IanLee1521@gmail.com>
Farkhad Khatamov (@hatamov) <comsgn@gmail.com>
Note: (@user) means a github user name.

View File

@@ -3,13 +3,54 @@
Changelog
---------
0.9.0 (2015-04-10)
++++++++++++++++++
- Integrated the parser of 2to3. This will make refactoring possible. It will
also be possible to check for error messages (like compiling an AST would give)
in the future.
- With the new parser, the evaluation also completely changed. It's now simpler
and more readable.
- Completely rewritten REPL completion.
- Added ``jedi.names``, a command to do static analysis. Thanks to that
sourcegraph guys for sponsoring this!
- Alpha version of the linter.
0.8.1 (2014-07-23)
+++++++++++++++++++
- Bugfix release, the last release forgot to include files that improve
autocompletion for builtin libraries. Fixed.
0.8.0 (2014-05-05)
+++++++++++++++++++
- Memory Consumption for compiled modules (e.g. builtins, sys) has been reduced
drastically. Loading times are down as well (it takes basically as long as an
import).
- REPL completion is starting to become usable.
- Various small API changes. Generally this release focuses on stability and
refactoring of internal APIs.
- Introducing operator precedence, which makes calculating correct Array
indices and ``__getattr__`` strings possible.
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
- Much faster parser with builtin part caching.
- A test suite, thanks @tkf.
0.5 versions (2012)
+++++++++++++++++++
* Initial development
- Initial development.

View File

@@ -10,4 +10,19 @@ 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.
Please use Pep8 to style your code.
**Try to use the PEP8 style guide.**
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

@@ -2,5 +2,13 @@ include README.rst
include CHANGELOG.rst
include LICENSE.txt
include AUTHORS.txt
include jedi/mixin/*.pym
include .coveragerc
include sith.py
include conftest.py
include pytest.ini
include tox.ini
include jedi/evaluate/compiled/fake/*.pym
include jedi/parser/grammar*.txt
recursive-include test *
recursive-include docs *
recursive-exclude * *.pyc

View File

@@ -1,6 +1,6 @@
###################################################
Jedi - an awesome autocompletion library for Python
###################################################
###################################################################
Jedi - an awesome autocompletion/static analysis library for Python
###################################################################
.. image:: https://secure.travis-ci.org/davidhalter/jedi.png?branch=master
:target: http://travis-ci.org/davidhalter/jedi
@@ -11,28 +11,41 @@ Jedi - an awesome autocompletion library for Python
:alt: Coverage Status
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.
*If you have specific questions, please add an issue or ask on* `stackoverflow
<https://stackoverflow.com>`_ *with the label* ``python-jedi``.
Additionaly, Jedi suports two different goto functions and has support for
renaming as well as Pydoc support and some other IDE features.
Jedi is a static analysis tool for Python that can be used in IDEs/editors. Its
historic focus is autocompletion, but does static analysis for now as well.
Jedi is fast and is very well tested. It understands Python on a deeper level
than all other static analysis frameworks for Python.
Jedi has support for two different goto functions. It's possible to search for
related names and to list all names in a Python file and infer them. Jedi
understands docstrings and you can use Jedi autocompletion in your REPL as
well.
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.
which uses Jedi's autocompletion. We encourage you to use Jedi in your IDEs.
It's really easy.
Jedi can be used with the following plugins/software:
Jedi can currently be used with the following editors:
- `VIM-Plugin <https://github.com/davidhalter/jedi-vim>`_
- `Emacs-Plugin <https://github.com/tkf/emacs-jedi>`_
- `Sublime-Plugin <https://github.com/svaiter/SublimeJEDI>`_
- `wdb (web debugger) <https://github.com/Kozea/wdb>`_
- Vim (jedi-vim_, YouCompleteMe_)
- Emacs (Jedi.el_, elpy_, anaconda-mode_, ycmd_)
- Sublime Text (SublimeJEDI_ [ST2 + ST3], anaconda_ [only ST3])
- SynWrite_
- TextMate_ (Not sure if it's actually working)
- Kate_ version 4.13+ supports it natively, you have to enable it, though. [`proof
<https://projects.kde.org/projects/kde/applications/kate/repository/show?rev=KDE%2F4.13>`_]
And it powers the following projects:
- wdb_ - Web Debugger
Here are some pictures:
Here are some pictures taken from jedi-vim_:
.. image:: https://github.com/davidhalter/jedi/raw/master/docs/_screenshots/screenshot_complete.png
@@ -44,15 +57,15 @@ Display of function/class bodies, docstrings.
.. image:: https://github.com/davidhalter/jedi/raw/master/docs/_screenshots/screenshot_pydoc.png
Pydoc support (with highlighting, Shift+k).
Pydoc support (Shift+k).
There is also support for goto and renaming.
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
Docs are available at `https://jedi.readthedocs.org/en/latest/
<https://jedi.readthedocs.org/en/latest/>`_. Pull requests with documentation
enhancements and/or fixes are awesome and most welcome. Jedi uses `semantic
versioning <http://semver.org/>`_.
@@ -74,23 +87,61 @@ Feature Support and Caveats
===========================
Jedi really understands your Python code. For a comprehensive list what Jedi
can do, see: `Features
understands, 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
You can run Jedi on cPython 2.6, 2.7, 3.2, 3.3 or 3.4, 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
---
API for IDEs
============
You can find the documentation for the `API here <https://jedi.readthedocs.org/en/latest/docs/plugin-api.html>`_.
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.
Autocompletion / Goto / Pydoc
-----------------------------
Please check the API for a good explanation. There are the following commands:
- ``jedi.Script.goto_assignments``
- ``jedi.Script.completions``
- ``jedi.Script.usages``
The returned objects are very powerful and really all you might need.
Autocompletion in your REPL (IPython, etc.)
-------------------------------------------
It's possible to have Jedi autocompletion in REPL modes - `example video <https://vimeo.com/122332037>`_.
This means that IPython and others are `supported
<https://jedi.readthedocs.org/en/latest/docs/usage.html#tab-completion-in-the-python-shell>`_.
Static Analysis / Linter
------------------------
To do all forms of static analysis, please try to use ``jedi.names``. It will
return a list of names that you can use to infer types and so on.
Linting is another thing that is going to be part of Jedi. For now you can try
an alpha version ``python -m jedi linter``. The API might change though and
it's still buggy. It's Jedi's goal to be smarter than classic linter and
understand ``AttributeError`` and other code issues.
Refactoring
-----------
Jedi would in theory support refactoring, but we have never publicized it,
because it's not production ready. If you're interested in helping out here,
let me know. With the latest parser changes, it should be very easy to actually
make it work.
Development
@@ -121,3 +172,17 @@ Tests are also run automatically on `Travis CI
For more detailed information visit the `testing documentation
<https://jedi.readthedocs.org/en/latest/docs/testing.html>`_
.. _jedi-vim: https://github.com/davidhalter/jedi-vim
.. _youcompleteme: http://valloric.github.io/YouCompleteMe/
.. _Jedi.el: https://github.com/tkf/emacs-jedi
.. _elpy: https://github.com/jorgenschaefer/elpy
.. _anaconda-mode: https://github.com/proofit404/anaconda-mode
.. _ycmd: https://github.com/abingham/emacs-ycmd
.. _sublimejedi: https://github.com/srusskih/SublimeJEDI
.. _anaconda: https://github.com/DamnWidget/anaconda
.. _SynWrite: http://uvviewsoft.com/synjedi/
.. _wdb: https://github.com/Kozea/wdb
.. _TextMate: https://github.com/lawrenceakka/python-jedi.tmbundle
.. _Kate: http://kate-editor.org

View File

@@ -21,12 +21,27 @@ jedi_cache_directory_orig = None
jedi_cache_directory_temp = None
def pytest_addoption(parser):
parser.addoption("--jedi-debug", "-D", action='store_true',
help="Enables Jedi's debug output.")
parser.addoption("--warning-is-error", action='store_true',
help="Warnings are treated as errors.")
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
if config.option.jedi_debug:
jedi.set_debug_function()
if config.option.warning_is_error:
import warnings
warnings.simplefilter("error")
def pytest_unconfigure(config):
global jedi_cache_directory_orig, jedi_cache_directory_temp

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -11,13 +11,14 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os, datetime
import sys
import os
import 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 -----------------------------------------------------
@@ -28,7 +29,7 @@ sys.path.append(os.path.abspath('_themes'))
# 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.inheritance_diagram']
'sphinx.ext.intersphinx', 'sphinx.ext.inheritance_diagram']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -47,18 +48,16 @@ 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__]
from jedi.utils import version_info
# 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])
version = '.'.join(str(x) for x in version_info()[:2])
# The full version, including alpha/beta/rc tags.
release = '.'.join(_version_strs)
release = jedi.__version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -141,12 +140,12 @@ html_static_path = ['_static']
# Custom sidebar templates, maps document names to template names.
html_sidebars = {
'**': [
# 'sidebarlogo.html',
'sidebarlogo.html',
'localtoc.html',
# 'relations.html',
#'relations.html',
'ghbuttons.html',
# 'sourcelink.html',
# 'searchbox.html'
#'sourcelink.html',
#'searchbox.html'
]
}
@@ -189,21 +188,21 @@ htmlhelp_basename = 'Jedidoc'
# -- Options for LaTeX output --------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
# 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'),
('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
@@ -246,9 +245,9 @@ man_pages = [
# (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'),
('index', 'Jedi', u'Jedi Documentation',
u'Jedi contributors', 'Jedi', 'Awesome Python autocompletion library.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
@@ -266,6 +265,27 @@ 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,
}
def skip_deprecated(app, what, name, obj, skip, options):
"""
All attributes containing a deprecated note shouldn't be documented
anymore. This makes it even clearer that they are not supported anymore.
"""
doc = obj.__doc__
return skip or doc and '.. deprecated::' in doc
def setup(app):
app.connect('autodoc-skip-member', skip_deprecated)

View File

@@ -44,25 +44,27 @@ The Jedi Core
The core of Jedi consists of three parts:
- :ref:`Parser <parsing>`
- :ref:`Parser <parser>`
- :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.
<parser>` first, because :mod:`jedi.evaluate` uses it extensively.
.. _parsing:
.. _parser:
Parser (parsing.py)
~~~~~~~~~~~~~~~~~~~
Parser (parser/__init__.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: parsing
.. automodule:: jedi.parser
Parser Representation (parser_representation.py)
Parser Representation (parser/representation.py)
++++++++++++++++++++++++++++++++++++++++++++++++
.. automodule:: parsing_representation
.. automodule:: jedi.parser.representation
Class inheritance diagram:
.. inheritance-diagram::
SubModule
@@ -82,15 +84,15 @@ Parser Representation (parser_representation.py)
.. _evaluate:
Evaluation of python code (evaluate.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Evaluation of python code (evaluate/__init__.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: evaluate
.. automodule:: jedi.evaluate
Evaluation Representation (evaluate_representation.py)
Evaluation Representation (evaluate/representation.py)
++++++++++++++++++++++++++++++++++++++++++++++++++++++
.. automodule:: evaluate_representation
.. automodule:: jedi.evaluate.representation
.. inheritance-diagram::
Executable
@@ -98,11 +100,18 @@ Evaluation Representation (evaluate_representation.py)
InstanceElement
Class
Function
Execution
Generator
Array
FunctionExecution
:parts: 1
.. _name_resolution:
Name resolution (evaluate/finder.py)
++++++++++++++++++++++++++++++++++++
.. automodule:: jedi.evaluate.finder
.. _dev-api:
API (api.py and api_classes.py)
@@ -115,7 +124,6 @@ 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
@@ -123,7 +131,8 @@ Core Extensions
Core Extensions is a summary of the following topics:
- :ref:`Dynamic Arrays & Function Parameters <dynamic>`
- :ref:`Iterables & Dynamic Arrays <iterables>`
- :ref:`Dynamic Parameters <dynamic>`
- :ref:`Fast Parser <fast_parser>`
- :ref:`Docstrings <docstrings>`
- :ref:`Refactoring <refactoring>`
@@ -132,35 +141,45 @@ 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:
.. _iterables:
Dynamic Arrays & Function Parameters (dynamic.py)
Iterables & Dynamic Arrays (evaluate/iterable.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: dynamic
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:
.. automodule:: jedi.evaluate.iterable
.. _dynamic:
Parameter completion (evaluate/dynamic.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: jedi.evaluate.dynamic
.. _fast_parser:
Fast Parser (fast_parser.py)
Fast Parser (parser/fast.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: fast_parser
.. automodule:: jedi.parser.fast
.. _docstrings:
Docstrings (docstrings.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~
Docstrings (evaluate/docstrings.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: docstrings
.. automodule:: jedi.evaluate.docstrings
.. _refactoring:
Refactoring (refactoring.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: refactoring
Refactoring (evaluate/refactoring.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: jedi.refactoring
.. _imports-modules:
@@ -173,33 +192,27 @@ Imports & Modules
- :ref:`Builtin Modules <builtin>`
- :ref:`Imports <imports>`
.. _modules:
Modules (modules.py)
~~~~~~~~~~~~~~~~~~~~
.. automodule:: modules
.. _builtin:
Builtin Modules (builtin.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Compiled Modules (evaluate/compiled.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: jedi.evaluate.compiled
.. automodule:: builtin
.. _imports:
Imports (imports.py)
~~~~~~~~~~~~~~~~~~~~
.. automodule:: imports
Imports (evaluate/imports.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: jedi.evaluate.imports
.. _caching-recursions:
Caching & Recursions
----------------------
--------------------
- :ref:`Caching <cache>`
@@ -210,15 +223,14 @@ Caching & Recursions
Caching (cache.py)
~~~~~~~~~~~~~~~~~~
.. automodule:: cache
.. automodule:: jedi.cache
.. _recursion:
Recursions (recursion.py)
~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: recursion
.. automodule:: jedi.evaluate.recursion
.. _dev-helpers:
@@ -226,12 +238,11 @@ Recursions (recursion.py)
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.
Most other modules are not really central to how Jedi works. They all contain
relevant code, but you if you understand the modules above, you pretty much
understand Jedi.
Python 2/3 compatibility (_compatibility.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: _compatibility
.. automodule:: jedi._compatibility

View File

@@ -3,7 +3,18 @@
Features and Caveats
====================
|jedi| supports many of the widely used Python features:
Jedi obviously supports autocompletion. It's also possible to get it working in
(:ref:`your REPL (IPython, etc.) <repl-completion>`).
Static analysis is also possible by using the command ``jedi.names``.
The Jedi Linter is currently in an alpha version and can be tested by calling
``python -m jedi linter``.
Jedi would in theory support refactoring, but we have never publicized it,
because it's not production ready. If you're interested in helping out here,
let me know. With the latest parser changes, it should be very easy to actually
make it work.
General Features
@@ -13,13 +24,15 @@ General Features
- 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>`)
- can infer function arguments from sphinx, epydoc and basic numpydoc docstrings
(:ref:`type hinting <type-hinting>`)
Supported Python Features
-------------------------
|jedi| supports many of the widely used Python features:
- builtins
- multiple returns or yields
- tuple assignments / array indexing / dictionary indexing
@@ -40,6 +53,8 @@ Supported Python Features
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)
- Django / Flask / Buildout support
Unsupported Features
@@ -55,7 +70,7 @@ 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``
- evaluating ``if`` / ``while`` / ``del``
Caveats
@@ -64,8 +79,8 @@ 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|.
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**
@@ -75,23 +90,131 @@ 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.
- Exceptions are only looked at in the form of ``Exception as e``, no comma!
**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.
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.
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.
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
one of the following docstring syntax styles:
**Sphinx style**
http://sphinx-doc.org/domains.html#info-field-lists
::
def myfunction(node, foo):
"""Do something with a ``node``.
:type node: ProgramNode
:param str foo: foo parameter description
"""
node.| # complete here
**Epydoc**
http://epydoc.sourceforge.net/manual-fields.html
::
def myfunction(node):
"""Do something with a ``node``.
@type node: ProgramNode
"""
node.| # complete here
**Numpydoc**
https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
In order to support the numpydoc format, you need to install the `numpydoc
<https://pypi.python.org/pypi/numpydoc>`__ package.
::
def foo(var1, var2, long_var_name='hi'):
r"""A one-line summary that does not use variable names or the
function name.
...
Parameters
----------
var1 : array_like
Array_like means all those objects -- lists, nested lists,
etc. -- that can be converted to an array. We can also
refer to variables like `var1`.
var2 : int
The type above can either refer to an actual Python type
(e.g. ``int``), or describe the type of the variable in more
detail, e.g. ``(N,) ndarray`` or ``array_like``.
long_variable_name : {'hi', 'ho'}, optional
Choices in brackets, default first when optional.
...
"""
var2.| # complete here
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

@@ -1,31 +0,0 @@
.. 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

@@ -30,22 +30,31 @@ System-wide installation via a package manager
Arch Linux
~~~~~~~~~~
You can install jedi directly from AUR: `python-jedi at AUR
<https://aur.archlinux.org/packages/python-jedi/>`__.
You can install |jedi| directly from official Arch Linux packages:
(There is also a packaged version of the vim plugin available: `vim-jedi at AUR
<https://aur.archlinux.org/packages/vim-jedi/>`__.)
- `python-jedi <https://www.archlinux.org/packages/community/any/python-jedi/>`__
(Python 3)
- `python2-jedi <https://www.archlinux.org/packages/community/any/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
Arch Linux<https://www.archlinux.org/packages/community/any/vim-jedi/>`__.)
Debian
~~~~~~
Debian packages are available as `experimental packages
<http://packages.debian.org/experimental/python-jedi>`__.
Debian packages are available in the `unstable repository
<http://packages.debian.org/search?keywords=python%20jedi>`__.
Others
~~~~~~
We are in the discussion of adding Jedi to the Fedora repositories.
We are in the discussion of adding |jedi| to the Fedora repositories.
Manual installation from a downloaded package
@@ -53,7 +62,7 @@ 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.
|jedi| and install it manually.
To install it, navigate to the directory containing `setup.py` on your console
and type::

View File

@@ -0,0 +1,10 @@
.. include:: ../global.rst
.. _plugin-api-classes:
API Return Classes
------------------
.. automodule:: jedi.api.classes
:members:
:undoc-members:

View File

@@ -8,8 +8,20 @@ The Plugin API
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`.
If you want to use |jedi|, you first need to ``import jedi``. You then have
direct access to the :class:`.Script`. You can then call the functions
documented here. These functions return :ref:`API classes
<plugin-api-classes>`.
Deprecations
------------
The deprecation process is as follows:
1. A deprecation is announced in the next major/minor release.
2. We wait either at least a year & at least two minor releases until we remove
the deprecated functionality.
API documentation
@@ -18,24 +30,11 @@ API documentation
API Interface
~~~~~~~~~~~~~
.. automodule:: api
.. automodule:: jedi.api
:members:
:undoc-members:
API Return Classes
~~~~~~~~~~~~~~~~~~
.. automodule:: api_classes
:members:
:undoc-members:
Settings Module
~~~~~~~~~~~~~~~
.. automodule:: settings
Examples
--------
@@ -73,10 +72,10 @@ Definitions / Goto:
... inception()'''
>>> script = jedi.Script(source, 8, 1, '')
>>>
>>> script.goto()
>>> script.goto_assignments()
[<Definition inception=my_list[2]>]
>>>
>>> script.get_definition()
>>> script.goto_definitions()
[<Definition def my_func>]
Related names:

View File

@@ -1,44 +0,0 @@
.. 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

6
docs/docs/settings.rst Normal file
View File

@@ -0,0 +1,6 @@
.. include:: ../global.rst
Settings
========
.. automodule:: jedi.settings

View File

@@ -23,6 +23,9 @@ 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.
For specific API testing we're using simple unit tests, with a focus on a
simple and readable testing structure.
.. _blackbox:
Blackbox Tests (run.py)
@@ -30,12 +33,8 @@ Blackbox Tests (run.py)
.. automodule:: test.run
Regression Tests (test_regression.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: test.test_regression
Refactoring Tests (refactor.py)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: test.refactor

88
docs/docs/usage.rst Normal file
View File

@@ -0,0 +1,88 @@
.. include:: ../global.rst
End User Usage
==============
If you are a not an IDE Developer, the odds are that you just want to use
|jedi| as a browser plugin or in the shell. Yes that's :ref:`also possible
<repl-completion>`!
|jedi| is relatively young and can be used in a variety of Plugins and
Software. If your Editor/IDE is not among them, recommend |jedi| to your IDE
developers.
.. _editor-plugins:
Editor Plugins
--------------
Vim:
- jedi-vim_
- YouCompleteMe_
Emacs:
- Jedi.el_
- elpy_
- anaconda-mode_
Sublime Text 2/3:
- SublimeJEDI_ (ST2 & ST3)
- anaconda_ (only ST3)
SynWrite:
- SynJedi_
TextMate:
- Textmate_ (Not sure if it's actually working)
Kate:
- Kate_ version 4.13+ `supports it natively
<https://projects.kde.org/projects/kde/applications/kate/repository/entry/addons/kate/pate/src/plugins/python_autocomplete_jedi.py?rev=KDE%2F4.13>`__,
you have to enable it, though.
.. _other-software:
Other Software Using Jedi
-------------------------
- wdb_ - Web Debugger
.. _repl-completion:
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
.. _jedi-vim: https://github.com/davidhalter/jedi-vim
.. _youcompleteme: http://valloric.github.io/YouCompleteMe/
.. _Jedi.el: https://github.com/tkf/emacs-jedi
.. _elpy: https://github.com/jorgenschaefer/elpy
.. _anaconda-mode: https://github.com/proofit404/anaconda-mode
.. _sublimejedi: https://github.com/srusskih/SublimeJEDI
.. _anaconda: https://github.com/DamnWidget/anaconda
.. _SynJedi: http://uvviewsoft.com/synjedi/
.. _wdb: https://github.com/Kozea/wdb
.. _TextMate: https://github.com/lawrenceakka/python-jedi.tmbundle
.. _kate: http://kate-editor.org/

View File

@@ -1,7 +1,7 @@
.. include global.rst
Jedi - an awesome autocompletion library for Python
===================================================
Jedi - an awesome autocompletion/static analysis library for Python
===================================================================
Release v\ |release|. (:doc:`Installation <docs/installation>`)
@@ -18,13 +18,14 @@ Docs
----
.. toctree::
:maxdepth: 1
:maxdepth: 2
docs/usage
docs/installation
docs/features
docs/recipes
docs/plugin-api
docs/history
docs/plugin-api-classes
docs/settings
docs/development
docs/testing
@@ -37,21 +38,3 @@ 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 <http://github.com/davidhalter/jedi-vim>`_
- `Emacs <https://github.com/tkf/emacs-jedi>`_
- `Sublime Text 2 <https://github.com/svaiter/SublimeJEDI>`_
.. _other-software:
Other Software Using Jedi
-------------------------
- `wdb <https://github.com/Kozea/wdb>`_

View File

@@ -1,16 +1,18 @@
"""
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 is a static analysis tool for Python that can be used in IDEs/editors. Its
historic focus is autocompletion, but does static analysis for now as well.
Jedi is fast and is very well tested. It understands Python on a deeper level
than all other static analysis frameworks for Python.
Additionaly, Jedi suports two different goto functions and has support for
renaming as well as Pydoc support and some other IDE features.
Jedi has support for two different goto functions. It's possible to search for
related names and to list all names in a Python file and infer them. Jedi
understands docstrings and you can use Jedi autocompletion in your REPL as
well.
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.
implementation as a `VIM-Plugin <https://github.com/davidhalter/jedi-vim>`_,
which uses Jedi's autocompletion. We encourage you to use Jedi in your IDEs.
It's really easy.
To give you a simple example how you can use the Jedi library, here is an
example for the autocompletion feature:
@@ -34,16 +36,8 @@ 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, 6, 0
__version__ = '0.9.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, _quick_complete, \
preload_module
from . import settings
sys.path.pop(0)
from jedi.api import Script, Interpreter, NotFoundError, set_debug_function
from jedi.api import preload_module, defined_names, names
from jedi import settings

43
jedi/__main__.py Normal file
View File

@@ -0,0 +1,43 @@
from sys import argv
from os.path import join, dirname, abspath, isdir
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.
print(join(dirname(abspath(__file__)), 'api', 'replstartup.py'))
elif len(argv) > 1 and argv[1] == 'linter':
"""
This is a pre-alpha API. You're not supposed to use it at all, except for
testing. It will very likely change.
"""
import jedi
import sys
if '--debug' in sys.argv:
jedi.set_debug_function()
for path in sys.argv[2:]:
if path.startswith('--'):
continue
if isdir(path):
import fnmatch
import os
paths = []
for root, dirnames, filenames in os.walk(path):
for filename in fnmatch.filter(filenames, '*.py'):
paths.append(os.path.join(root, filename))
else:
paths = [path]
try:
for path in paths:
for error in jedi.Script(path=path)._analysis():
print(error)
except Exception:
if '--pdb' in sys.argv:
import pdb
pdb.post_mortem()
else:
raise

View File

@@ -1,91 +1,75 @@
"""
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.
created. Clearly there is huge need to use conforming syntax.
"""
import sys
import imp
import os
import re
try:
import importlib
except:
except ImportError:
pass
is_py3k = sys.hexversion >= 0x03000000
is_py33 = sys.hexversion >= 0x03030000
is_py3 = sys.version_info[0] >= 3
is_py33 = is_py3 and sys.version_info.minor >= 3
is_py26 = not is_py3 and sys.version_info[1] < 7
def find_module_py33(string, path=None):
mod_info = (None, None, None)
loader = None
if path is not None:
# Check for the module in the specidied path
loader = importlib.machinery.PathFinder.find_module(string, path)
else:
# Check for the module in sys.path
loader = importlib.machinery.PathFinder.find_module(string, sys.path)
if loader is None:
# Fallback to find builtins
loader = importlib.machinery.PathFinder.find_module(string, path)
if loader is None and path is None: # Fallback to find builtins
try:
loader = importlib.find_loader(string)
except ValueError as e:
# See #491. Importlib might raise a ValueError, to avoid this, we
# just raise an ImportError to fix the issue.
raise ImportError("Originally ValueError: " + e.message)
if loader is None:
raise ImportError
raise ImportError("Couldn't find a loader for {0}".format(string))
try:
if (loader.is_package(string)):
mod_info = (None, os.path.dirname(loader.path), True)
is_package = loader.is_package(string)
if is_package:
module_path = os.path.dirname(loader.path)
module_file = None
else:
filename = loader.get_filename(string)
if filename and os.path.exists(filename):
mod_info = (open(filename, 'U'), filename, False)
else:
mod_info = (None, filename, False)
module_path = loader.get_filename(string)
module_file = open(module_path, 'rb')
except AttributeError:
mod_info = (None, loader.load_module(string).__name__, False)
# ExtensionLoader has not attribute get_filename, instead it has a
# path attribute that we can use to retrieve the module path
try:
module_path = loader.path
module_file = open(loader.path, 'rb')
except AttributeError:
module_path = string
module_file = None
finally:
is_package = False
return mod_info
return module_file, module_path, is_package
def find_module_pre_py33(string, path=None):
mod_info = None
if path is None:
mod_info = imp.find_module(string)
else:
mod_info = imp.find_module(string, path)
return (mod_info[0], mod_info[1], mod_info[2][2] == imp.PKG_DIRECTORY)
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
def find_module(string, path=None):
"""Provides information about a module.
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."""
if is_py33:
return find_module_py33(string, path)
else:
return find_module_pre_py33(string, path)
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
try:
next = next
except NameError:
_raiseStopIteration = object()
def next(iterator, default=_raiseStopIteration):
if not hasattr(iterator, 'next'):
raise TypeError("not an iterator")
try:
return iterator.next()
except StopIteration:
if default is _raiseStopIteration:
raise
else:
return default
# unicode function
try:
@@ -93,17 +77,17 @@ try:
except NameError:
unicode = str
if is_py3k:
utf8 = lambda s: s
if is_py3:
u = lambda s: s
else:
utf8 = lambda s: s.decode('utf-8')
u = lambda s: s.decode('utf-8')
utf8.__doc__ = """
u.__doc__ = """
Decode a raw string into unicode object. Do nothing in Python 3.
"""
# exec function
if is_py3k:
if is_py3:
def exec_function(source, global_map):
exec(source, global_map)
else:
@@ -111,7 +95,7 @@ else:
exec source in global_map """, 'blub', 'exec'))
# re-raise function
if is_py3k:
if is_py3:
def reraise(exception, traceback):
raise exception.with_traceback(traceback)
else:
@@ -129,24 +113,6 @@ Usage::
"""
# StringIO (Python 2.5 has no io module), so use io only for py3k
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
# hasattr function used because python
if is_py3k:
hasattr = hasattr
else:
def hasattr(obj, name):
try:
getattr(obj, name)
return True
except AttributeError:
return False
class Python3Method(object):
def __init__(self, func):
self.func = func
@@ -157,12 +123,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. """
@@ -170,10 +130,6 @@ def use_metaclass(meta, *bases):
bases = (object,)
return meta("HackClass", bases, {})
try:
from functools import reduce # Python 3
except ImportError:
reduce = reduce
try:
encoding = sys.stdout.encoding
@@ -181,3 +137,69 @@ try:
encoding = 'utf-8'
except AttributeError:
encoding = 'ascii'
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_py3:
return str(string)
elif not isinstance(string, unicode):
return unicode(str(string), 'UTF-8')
return string
try:
import builtins # module name in python 3
except ImportError:
import __builtin__ as builtins
import ast
def literal_eval(string):
# py3.0, py3.1 and py32 don't support unicode literals. Support those, I
# don't want to write two versions of the tokenizer.
if is_py3 and sys.version_info.minor < 3:
if re.match('[uU][\'"]', string):
string = string[1:]
return ast.literal_eval(string)
try:
from itertools import zip_longest
except ImportError:
from itertools import izip_longest as zip_longest # Python 2
def no_unicode_pprint(dct):
"""
Python 2/3 dict __repr__ may be different, because of unicode differens
(with or without a `u` prefix). Normally in doctests we could use `pprint`
to sort dicts and check for equality, but here we have to write a separate
function to do that.
"""
import pprint
s = pprint.pformat(dct)
print(re.sub("u'", "'", s))
def utf8_repr(func):
"""
``__repr__`` methods in Python 2 don't allow unicode objects to be
returned. Therefore cast them to utf-8 bytes in this decorator.
"""
def wrapper(self):
result = func(self)
if isinstance(result, unicode):
return result.encode('utf-8')
else:
return result
if is_py3:
return func
else:
return wrapper

View File

@@ -1,569 +0,0 @@
"""
The API basically only provides one class. You can create a :class:`Script` and
use its methods.
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
import re
import os
import warnings
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._compatibility import next, unicode
import evaluate
import keywords
import api_classes
import evaluate_representation as er
import dynamic
import imports
import builtin
class NotFoundError(Exception):
"""A custom error to avoid catching the wrong exceptions."""
pass
class Script(object):
"""
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, separated by newlines.
:type source: str
:param line: The line to perform actions on (starting with 1).
:type line: int
:param col: The column of the cursor (starting with 0).
:type col: int
:param source_path: The path of the file in the file system, or ``''`` if
it hasn't been saved yet.
:type source_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'):
api_classes._clear_caches()
debug.reset_time()
self.source = modules.source_to_unicode(source, source_encoding)
self.pos = line, column
self._module = modules.ModuleWithCursor(source_path,
source=self.source, position=self.pos)
self._source_path = source_path
self.source_path = None if source_path is None \
else os.path.abspath(source_path)
debug.speed('init')
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`
"""
debug.speed('completions start')
path = self._module.get_path_until_cursor()
if re.search('^\.|\.\.$', path):
return []
path, dot, like = self._get_completion_parts(path)
completion_line = self._module.get_line(self.pos[0])[:self.pos[1]]
try:
scopes = list(self._prepare_goto(path, True))
except NotFoundError:
scopes = []
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:
completions.append((c, scope))
else:
completions = []
debug.dbg('possible scopes', scopes)
for s in scopes:
if s.isinstance(er.Function):
names = s.get_magic_method_names()
else:
if isinstance(s, imports.ImportPath):
if like == 'import':
if not completion_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))
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))
# Do the completion if there is no path before and no import stmt.
u = self._parser.user_stmt
bs = builtin.Builtin.scope
if isinstance(u, pr.Import):
if (u.relative_count > 0 or u.from_ns) and not re.search(
r'(,|from)\s*$|import\s+$', completion_line):
completions += ((k, bs) for k
in keywords.get_keywords('import'))
if not path and not isinstance(u, pr.Import):
# add keywords
completions += ((k, bs) for k in keywords.get_keywords(
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,
self._parser.user_stmt, 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 _prepare_goto(self, goto_path, is_like_search=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))
user_stmt = self._parser.user_stmt
debug.speed('parsed')
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, pr.Import):
scopes = [self._get_on_import_stmt(is_like_search)[0]]
else:
# just parse one statement, take it and evaluate it
stmt = self._get_under_cursor_stmt(goto_path)
scopes = evaluate.follow_statement(stmt)
return scopes
def _get_under_cursor_stmt(self, cursor_txt):
offset = self.pos[0] - 1, self.pos[1]
r = parsing.Parser(cursor_txt, no_docstr=True, offset=offset)
try:
stmt = r.module.statements[0]
except IndexError:
raise NotFoundError()
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):
"""
.. deprecated:: 0.5.0
Use :attr:`.goto_definitions` instead.
.. todo:: Remove!
"""
warnings.warn("Use goto_definitions instead.", DeprecationWarning)
return self.goto_definitions()
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():
if isinstance(s, imports.ImportPath):
scopes.remove(s)
scopes.update(resolve_import_paths(set(s.follow())))
return scopes
goto_path = self._module.get_path_under_cursor()
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])
elif not 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
self.pos = (row, max(col - 1, 0))
self._module = modules.ModuleWithCursor(
self._source_path,
source=self.source,
position=self.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)
d = set([api_classes.Definition(s) for s in scopes
if not isinstance(s, imports.ImportPath._GlobalNamespace)])
return self._sorted_defs(d)
@api_classes._clear_caches_after_call
def goto_assignments(self):
"""
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.
:rtype: list of :class:`api_classes.Definition`
"""
d = [api_classes.Definition(d) for d in set(self._goto()[0])]
return self._sorted_defs(d)
def _goto(self, add_import_name=False):
"""
Used for goto_assignments and usages.
:param add_import_name: TODO add description
"""
def follow_inexistent_imports(defs):
""" Imports can be generated, e.g. following
`multiprocessing.dummy` generates an import dummy in the
multiprocessing module. The Import doesn't exist -> follow.
"""
definitions = set(defs)
for d in defs:
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()
user_stmt = self._parser.user_stmt
if next(context) in ('class', 'def'):
user_scope = self._parser.user_scope
definitions = set([user_scope.name])
search_name = unicode(user_scope.name)
elif isinstance(user_stmt, pr.Import):
s, name_part = self._get_on_import_stmt()
try:
definitions = [s.follow(is_goto=True)[0]]
except IndexError:
definitions = []
search_name = unicode(name_part)
if add_import_name:
import_name = user_stmt.get_defined_names()
# imports have only one name
if 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):
if user_stmt.get_commands()[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
@api_classes._clear_caches_after_call
def usages(self, additional_module_paths=()):
"""
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
:rtype: list of :class:`api_classes.Usage`
"""
user_stmt = self._parser.user_stmt
definitions, search_name = self._goto(add_import_name=True)
if isinstance(user_stmt, pr.Statement) \
and self.pos < user_stmt.get_commands()[0].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.usages_add_import_modules(definitions,
search_name)
module = set([d.get_parent_until() for d in definitions])
module.add(self._parser.module)
names = dynamic.usages(definitions, search_name, module)
for d in set(definitions):
if isinstance(d, pr.Module):
names.append(api_classes.Usage(d, d))
else:
names.append(api_classes.Usage(d.names[-1], d))
return self._sorted_defs(set(names))
@api_classes._clear_caches_after_call
def call_signatures(self):
"""
Return the function object of the call you're currently in.
E.g. if the cursor is here::
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`
"""
call, index = self._func_call_and_param_index()
if call is None:
return []
user_stmt = self._parser.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')
return [api_classes.CallDef(o, index, call) for o in origins]
def _func_call_and_param_index(self):
debug.speed('func_call start')
call, index = None, 0
if call is None:
user_stmt = self._parser.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):
""" 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
for i in import_names:
if user_stmt.alias == i:
continue
for name_part in i.names:
if name_part.end_pos >= self.pos:
if not cur_name_part:
cur_name_part = name_part
kill_count += 1
i = imports.ImportPath(user_stmt, is_like_search,
kill_count=kill_count, direct_resolve=True)
return i, cur_name_part
def _get_completion_parts(self, path):
"""
Returns the parts for the completion
:return: tuple - (path, dot, like)
"""
match = re.match(r'^(.*?)(\.|)(\w?[\w\d]*)$', path, flags=re.S)
return match.groups()
@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.start_pos))
def defined_names(source, 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=source_path,
)
return api_classes._defined_names(parser.scope)
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).complete()
def set_debug_function(func_cb=debug.print_to_stdout, warnings=True,
notices=True, speed=True):
"""
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
debug.enable_warning = warnings
debug.enable_notice = notices
debug.enable_speed = speed
def _quick_complete(source):
"""
Convenience function to complete a source string at the end.
Example:
>>> _quick_complete('''
... import datetime
... datetime.da''') #doctest: +ELLIPSIS
[<Completion: date>, <Completion: datetime>, ...]
:param source: The source code to be completed.
:type source: string
:return: Completion objects as returned by :meth:`complete`.
:rtype: list of :class:`api_classes.Completion`
"""
lines = re.sub(r'[\n\r\s]*$', '', source).splitlines()
pos = len(lines), len(lines[-1])
script = Script(source, pos[0], pos[1], '')
return script.completions()

709
jedi/api/__init__.py Normal file
View File

@@ -0,0 +1,709 @@
"""
The API basically only provides one class. You can create a :class:`Script` and
use its methods.
Additionally you can add a debug function with :func:`set_debug_function`.
.. warning:: Please, note that Jedi is **not thread safe**.
"""
import re
import os
import warnings
import sys
from itertools import chain
from jedi._compatibility import unicode, builtins
from jedi.parser import Parser, load_grammar
from jedi.parser.tokenize import source_tokens
from jedi.parser import tree
from jedi.parser.user_context import UserContext, UserContextParser
from jedi import debug
from jedi import settings
from jedi import common
from jedi import cache
from jedi.api import keywords
from jedi.api import classes
from jedi.api import interpreter
from jedi.api import usages
from jedi.api import helpers
from jedi.evaluate import Evaluator
from jedi.evaluate import representation as er
from jedi.evaluate import compiled
from jedi.evaluate import imports
from jedi.evaluate.cache import memoize_default
from jedi.evaluate.helpers import FakeName, get_module_names
from jedi.evaluate.finder import global_names_dict_generator, filter_definition_names
from jedi.evaluate import analysis
# Jedi uses lots and lots of recursion. By setting this a little bit higher, we
# can remove some "maximum recursion depth" errors.
sys.setrecursionlimit(2000)
class NotFoundError(Exception):
"""A custom error to avoid catching the wrong exceptions.
.. deprecated:: 0.9.0
Not in use anymore, Jedi just returns no goto result if you're not on a
valid name.
.. todo:: Remove!
"""
class Script(object):
"""
A Script is the base for completions, goto or whatever you want to do with
|jedi|.
You can either use the ``source`` parameter or ``path`` to read a file.
Usually you're going to want to use both of them (in an editor).
: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 of the cursor (starting with 0).
:type col: int
: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 encoding: The encoding of ``source``, if it is not a
``unicode`` object (default ``'utf-8'``).
:type encoding: str
:param source_encoding: The encoding of ``source``, if it is not a
``unicode`` object (default ``'utf-8'``).
:type encoding: str
"""
def __init__(self, source=None, line=None, column=None, path=None,
encoding='utf-8', source_path=None, source_encoding=None):
if source_path is not None:
warnings.warn("Use path instead of source_path.", DeprecationWarning)
path = source_path
if source_encoding is not None:
warnings.warn("Use encoding instead of source_encoding.", DeprecationWarning)
encoding = source_encoding
self._orig_path = path
self.path = None if path is None else os.path.abspath(path)
if source is None:
with open(path) as f:
source = f.read()
self.source = common.source_to_unicode(source, encoding)
lines = common.splitlines(self.source)
line = max(len(lines), 1) if line is None else line
if not (0 < line <= len(lines)):
raise ValueError('`line` parameter is not in a valid range.')
line_len = len(lines[line - 1])
column = line_len if column is None else column
if not (0 <= column <= line_len):
raise ValueError('`column` parameter is not in a valid range.')
self._pos = line, column
cache.clear_time_caches()
debug.reset_time()
self._grammar = load_grammar('grammar%s.%s' % sys.version_info[:2])
self._user_context = UserContext(self.source, self._pos)
self._parser = UserContextParser(self._grammar, self.source, path,
self._pos, self._user_context,
self._parsed_callback)
self._evaluator = Evaluator(self._grammar)
debug.speed('init')
def _parsed_callback(self, parser):
module = self._evaluator.wrap(parser.module)
imports.add_module(self._evaluator, unicode(module.name), module)
@property
def source_path(self):
"""
.. deprecated:: 0.7.0
Use :attr:`.path` instead.
.. todo:: Remove!
"""
warnings.warn("Use path instead of source_path.", DeprecationWarning)
return self.path
def __repr__(self):
return '<%s: %s>' % (self.__class__.__name__, repr(self._orig_path))
def completions(self):
"""
Return :class:`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:`classes.Completion`
"""
def get_completions(user_stmt, bs):
# TODO this closure is ugly. it also doesn't work with
# simple_complete (used for Interpreter), somehow redo.
module = self._evaluator.wrap(self._parser.module())
names, level, only_modules, unfinished_dotted = \
helpers.check_error_statements(module, self._pos)
completion_names = []
if names is not None:
imp_names = tuple(str(n) for n in names if n.end_pos < self._pos)
i = imports.Importer(self._evaluator, imp_names, module, level)
completion_names = i.completion_names(self._evaluator, only_modules)
# TODO this paragraph is necessary, but not sure it works.
context = self._user_context.get_context()
if not next(context).startswith('.'): # skip the path
if next(context) == 'from':
# completion is just "import" if before stands from ..
if unfinished_dotted:
return completion_names
else:
return keywords.keyword_names('import')
if isinstance(user_stmt, tree.Import):
module = self._parser.module()
completion_names += imports.completion_names(self._evaluator,
user_stmt, self._pos)
return completion_names
if names is None and not isinstance(user_stmt, tree.Import):
if not path and not dot:
# add keywords
completion_names += keywords.keyword_names(all=True)
# TODO delete? We should search for valid parser
# transformations.
completion_names += self._simple_complete(path, dot, like)
return completion_names
debug.speed('completions start')
path = self._user_context.get_path_until_cursor()
# Dots following an int are not the start of a completion but a float
# literal.
if re.search(r'^\d\.$', path):
return []
path, dot, like = helpers.completion_parts(path)
user_stmt = self._parser.user_stmt_with_whitespace()
b = compiled.builtin
completion_names = get_completions(user_stmt, b)
if not dot:
# add named params
for call_sig in self.call_signatures():
# Allow protected access, because it's a public API.
module = call_sig._name.get_parent_until()
# Compiled modules typically don't allow keyword arguments.
if not isinstance(module, compiled.CompiledObject):
for p in call_sig.params:
# Allow access on _definition here, because it's a
# public API and we don't want to make the internal
# Name object public.
if p._definition.stars == 0: # no *args/**kwargs
completion_names.append(p._name)
needs_dot = not dot and path
comps = []
comp_dct = {}
for c in set(completion_names):
n = str(c)
if settings.case_insensitive_completion \
and n.lower().startswith(like.lower()) \
or n.startswith(like):
if isinstance(c.parent, (tree.Function, tree.Class)):
# TODO I think this is a hack. It should be an
# er.Function/er.Class before that.
c = self._evaluator.wrap(c.parent).name
new = classes.Completion(self._evaluator, c, needs_dot, len(like))
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, dot, like):
if not path and not dot:
scope = self._parser.user_scope()
if not scope.is_scope(): # Might be a flow (if/while/etc).
scope = scope.get_parent_scope()
names_dicts = global_names_dict_generator(
self._evaluator,
self._evaluator.wrap(scope),
self._pos
)
completion_names = []
for names_dict, pos in names_dicts:
names = list(chain.from_iterable(names_dict.values()))
if not names:
continue
completion_names += filter_definition_names(names, self._parser.user_stmt(), pos)
elif self._get_under_cursor_stmt(path) is None:
return []
else:
scopes = list(self._prepare_goto(path, True))
completion_names = []
debug.dbg('possible completion scopes: %s', scopes)
for s in scopes:
names = []
for names_dict in s.names_dicts(search_global=False):
names += chain.from_iterable(names_dict.values())
completion_names += filter_definition_names(names, self._parser.user_stmt())
return completion_names
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())
user_stmt = self._parser.user_stmt_with_whitespace()
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, tree.Import):
i, _ = helpers.get_on_import_stmt(self._evaluator, self._user_context,
user_stmt, is_completion)
if i is None:
return []
scopes = [i]
else:
# just parse one statement, take it and evaluate it
eval_stmt = self._get_under_cursor_stmt(goto_path)
if eval_stmt is None:
return []
module = self._evaluator.wrap(self._parser.module())
names, level, _, _ = helpers.check_error_statements(module, self._pos)
if names:
names = [str(n) for n in names]
i = imports.Importer(self._evaluator, names, module, level)
return i.follow()
scopes = self._evaluator.eval_element(eval_stmt)
return scopes
@memoize_default()
def _get_under_cursor_stmt(self, cursor_txt, start_pos=None):
tokenizer = source_tokens(cursor_txt)
r = Parser(self._grammar, cursor_txt, tokenizer=tokenizer)
try:
# Take the last statement available that is not an endmarker.
# And because it's a simple_stmt, we need to get the first child.
stmt = r.module.children[-2].children[0]
except (AttributeError, IndexError):
return None
user_stmt = self._parser.user_stmt()
if user_stmt is None:
# Set the start_pos to a pseudo position, that doesn't exist but
# works perfectly well (for both completions in docstrings and
# statements).
pos = start_pos or self._pos
else:
pos = user_stmt.start_pos
stmt.move(pos[0] - 1, pos[1]) # Moving the offset.
stmt.parent = self._parser.user_scope()
return stmt
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:`classes.Definition`
"""
def resolve_import_paths(scopes):
for s in scopes.copy():
if isinstance(s, imports.ImportWrapper):
scopes.remove(s)
scopes.update(resolve_import_paths(set(s.follow())))
return scopes
goto_path = self._user_context.get_path_under_cursor()
context = self._user_context.get_context()
definitions = set()
if next(context) in ('class', 'def'):
definitions = set([self._evaluator.wrap(self._parser.user_scope())])
else:
# Fetch definition of callee, if there's no path otherwise.
if not goto_path:
definitions = set(signature._definition
for signature in self.call_signatures())
if re.match('\w[\w\d_]*$', goto_path) and not definitions:
user_stmt = self._parser.user_stmt()
if user_stmt is not None and user_stmt.type == 'expr_stmt':
for name in user_stmt.get_defined_names():
if name.start_pos <= self._pos <= name.end_pos:
# TODO scaning for a name and then using it should be
# the default.
definitions = set(self._evaluator.goto_definition(name))
if not definitions and goto_path:
definitions = set(self._prepare_goto(goto_path))
definitions = resolve_import_paths(definitions)
names = [s.name for s in definitions]
defs = [classes.Definition(self._evaluator, name) for name in names]
return helpers.sorted_definitions(set(defs))
def goto_assignments(self):
"""
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.
:rtype: list of :class:`classes.Definition`
"""
results = self._goto()
d = [classes.Definition(self._evaluator, d) for d in set(results)]
return helpers.sorted_definitions(d)
def _goto(self, add_import_name=False):
"""
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
`multiprocessing.dummy` generates an import dummy in the
multiprocessing module. The Import doesn't exist -> follow.
"""
definitions = set(defs)
for d in defs:
if isinstance(d.parent, tree.Import) \
and d.start_pos == (0, 0):
i = imports.ImportWrapper(self._evaluator, d.parent).follow(is_goto=True)
definitions.remove(d)
definitions |= follow_inexistent_imports(i)
return definitions
goto_path = self._user_context.get_path_under_cursor()
context = self._user_context.get_context()
user_stmt = self._parser.user_stmt()
user_scope = self._parser.user_scope()
stmt = self._get_under_cursor_stmt(goto_path)
if stmt is None:
return []
if user_scope is None:
last_name = None
else:
# Try to use the parser if possible.
last_name = user_scope.name_for_position(self._pos)
if last_name is None:
last_name = stmt
while not isinstance(last_name, tree.Name):
try:
last_name = last_name.children[-1]
except AttributeError:
# Doesn't have a name in it.
return []
if next(context) in ('class', 'def'):
# The cursor is on a class/function name.
user_scope = self._parser.user_scope()
definitions = set([user_scope.name])
elif isinstance(user_stmt, tree.Import):
s, name = helpers.get_on_import_stmt(self._evaluator,
self._user_context, user_stmt)
definitions = self._evaluator.goto(name)
else:
# The Evaluator.goto function checks for definitions, but since we
# use a reverse tokenizer, we have new name_part objects, so we
# have to check the user_stmt here for positions.
if isinstance(user_stmt, tree.ExprStmt) \
and isinstance(last_name.parent, tree.ExprStmt):
for name in user_stmt.get_defined_names():
if name.start_pos <= self._pos <= name.end_pos:
return [name]
defs = self._evaluator.goto(last_name)
definitions = follow_inexistent_imports(defs)
return definitions
def usages(self, additional_module_paths=()):
"""
Return :class:`classes.Definition` 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
:rtype: list of :class:`classes.Definition`
"""
temp, settings.dynamic_flow_information = \
settings.dynamic_flow_information, False
try:
user_stmt = self._parser.user_stmt()
definitions = self._goto(add_import_name=True)
if not definitions and isinstance(user_stmt, tree.Import):
# For not defined imports (goto doesn't find something, we take
# the name as a definition. This is enough, because every name
# points to it.
name = user_stmt.name_for_position(self._pos)
if name is None:
# Must be syntax
return []
definitions = [name]
if not definitions:
# Without a definition for a name we cannot find references.
return []
if not isinstance(user_stmt, tree.Import):
# import case is looked at with add_import_name option
definitions = usages.usages_add_import_modules(self._evaluator,
definitions)
module = set([d.get_parent_until() for d in definitions])
module.add(self._parser.module())
names = usages.usages(self._evaluator, definitions, module)
for d in set(definitions):
names.append(classes.Definition(self._evaluator, d))
finally:
settings.dynamic_flow_information = temp
return helpers.sorted_definitions(set(names))
def call_signatures(self):
"""
Return the function object of the call you're currently in.
E.g. if the cursor is here::
abs(# <-- cursor is here
This would return the ``abs`` function. On the other hand::
abs()# <-- cursor is here
This would return ``None``.
:rtype: list of :class:`classes.CallSignature`
"""
call_txt, call_index, key_name, start_pos = self._user_context.call_signature()
if call_txt is None:
return []
stmt = self._get_under_cursor_stmt(call_txt, start_pos)
if stmt is None:
return []
with common.scale_speed_settings(settings.scale_call_signatures):
origins = cache.cache_call_signatures(self._evaluator, stmt,
self.source, self._pos)
debug.speed('func_call followed')
return [classes.CallSignature(self._evaluator, o.name, stmt, call_index, key_name)
for o in origins if hasattr(o, 'py__call__')]
def _analysis(self):
def check_types(types):
for typ in types:
try:
f = typ.iter_content
except AttributeError:
pass
else:
check_types(f())
#statements = set(chain(*self._parser.module().used_names.values()))
nodes, imp_names, decorated_funcs = \
analysis.get_module_statements(self._parser.module())
# Sort the statements so that the results are reproducible.
for n in imp_names:
imports.ImportWrapper(self._evaluator, n).follow()
for node in sorted(nodes, key=lambda obj: obj.start_pos):
check_types(self._evaluator.eval_element(node))
for dec_func in decorated_funcs:
er.Function(self._evaluator, dec_func).get_decorated_func()
ana = [a for a in self._evaluator.analysis if self.path == a.path]
return sorted(set(ana), key=lambda x: x.line)
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`.
"""
if type(namespaces) is not list or len(namespaces) == 0 or \
any([type(x) is not dict for x in namespaces]):
raise TypeError("namespaces must be a non-empty list of dict")
super(Interpreter, self).__init__(source, **kwds)
self.namespaces = namespaces
# Don't use the fast parser, because it does crazy stuff that we don't
# need in our very simple and small code here (that is always
# changing).
self._parser = UserContextParser(self._grammar, self.source,
self._orig_path, self._pos,
self._user_context, self._parsed_callback,
use_fast_parser=False)
interpreter.add_namespaces_to_parser(self._evaluator, namespaces,
self._parser.module())
def _simple_complete(self, path, dot, like):
user_stmt = self._parser.user_stmt_with_whitespace()
is_simple_path = not path or re.search('^[\w][\w\d.]*$', path)
if isinstance(user_stmt, tree.Import) or not is_simple_path:
return super(Interpreter, self)._simple_complete(path, dot, like)
else:
class NamespaceModule(object):
def __getattr__(_, name):
for n in self.namespaces:
try:
return n[name]
except KeyError:
pass
raise AttributeError()
def __dir__(_):
gen = (n.keys() for n in self.namespaces)
return list(set(chain.from_iterable(gen)))
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 Exception:
pass
completion_names = []
for namespace in namespaces:
for name in dir(namespace):
if name.lower().startswith(like.lower()):
scope = self._parser.module()
n = FakeName(name, scope)
completion_names.append(n)
return completion_names
def defined_names(source, path=None, 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 classes.Definition
.. deprecated:: 0.9.0
Use :func:`names` instead.
.. todo:: Remove!
"""
warnings.warn("Use call_signatures instead.", DeprecationWarning)
return names(source, path, encoding)
def names(source=None, path=None, encoding='utf-8', all_scopes=False,
definitions=True, references=False):
"""
Returns a list of `Definition` objects, containing name parts.
This means you can call ``Definition.goto_assignments()`` and get the
reference of a name.
The parameters are the same as in :py:class:`Script`, except or the
following ones:
:param all_scopes: If True lists the names of all scopes instead of only
the module namespace.
:param definitions: If True lists the names that have been defined by a
class, function or a statement (``a = b`` returns ``a``).
:param references: If True lists all the names that are not listed by
``definitions=True``. E.g. ``a = b`` returns ``b``.
"""
def def_ref_filter(_def):
is_def = _def.is_definition()
return definitions and is_def or references and not is_def
# Set line/column to a random position, because they don't matter.
script = Script(source, line=1, column=0, path=path, encoding=encoding)
defs = [classes.Definition(script._evaluator, name_part)
for name_part in get_module_names(script._parser.module(), all_scopes)]
return sorted(filter(def_ref_filter, defs), key=lambda x: (x.line, x.column))
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):
"""
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
debug.enable_warning = warnings
debug.enable_notice = notices
debug.enable_speed = speed

735
jedi/api/classes.py Normal file
View File

@@ -0,0 +1,735 @@
"""
The :mod:`jedi.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.
"""
import warnings
from itertools import chain
import re
from jedi._compatibility import unicode, use_metaclass
from jedi import settings
from jedi import common
from jedi.parser import tree
from jedi.evaluate.cache import memoize_default, CachedMetaClass
from jedi.evaluate import representation as er
from jedi.evaluate import iterable
from jedi.evaluate import imports
from jedi.evaluate import compiled
from jedi.api import keywords
from jedi.evaluate.finder import filter_definition_names
def defined_names(evaluator, scope):
"""
List sub-definitions (e.g., methods in class).
:type scope: Scope
:rtype: list of Definition
"""
dct = scope.names_dict
names = list(chain.from_iterable(dct.values()))
names = filter_definition_names(names, scope)
return [Definition(evaluator, d) for d in sorted(names, key=lambda s: s.start_pos)]
class BaseDefinition(object):
_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',
'_sre.SRE_Match': 're.MatchObject',
'_sre.SRE_Pattern': 're.RegexObject',
}.items())
def __init__(self, evaluator, name):
self._evaluator = evaluator
self._name = name
"""
An instance of :class:`jedi.parser.reprsentation.Name` subclass.
"""
self._definition = evaluator.wrap(self._name.get_definition())
self.is_keyword = isinstance(self._definition, keywords.Keyword)
# generate a path to the definition
self._module = name.get_parent_until()
if self.in_builtin_module():
self.module_path = None
else:
self.module_path = self._module.path
"""Shows the file path of a module. e.g. ``/usr/lib/python2.7/os.py``"""
@property
def name(self):
"""
Name of variable/function/class/module.
For example, for ``x = None`` it returns ``'x'``.
:rtype: str or None
"""
return unicode(self._name)
@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._name.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:`jedi.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
...
... for variable in [keyword, f, C, x]:
... variable'''
>>> script = Script(source)
>>> 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'
"""
stripped = self._definition
if isinstance(stripped, er.InstanceElement):
stripped = stripped.var
if isinstance(stripped, compiled.CompiledObject):
return stripped.api_type()
elif isinstance(stripped, iterable.Array):
return 'instance'
elif isinstance(stripped, tree.Import):
return 'import'
string = type(stripped).__name__.lower().replace('wrapper', '')
if string == 'exprstmt':
return 'statement'
else:
return string
def _path(self):
"""The path to a module/class/function definition."""
path = []
par = self._definition
while par is not None:
if isinstance(par, tree.Import):
path += imports.ImportWrapper(self._evaluator, self._name).import_path
break
try:
name = par.name
except AttributeError:
pass
else:
if isinstance(par, er.ModuleWrapper):
# TODO just make the path dotted from the beginning, we
# shouldn't really split here.
path[0:0] = par.py__name__().split('.')
break
else:
path.insert(0, unicode(name))
par = par.parent
return path
@property
def module_name(self):
"""
The module name.
>>> from jedi import Script
>>> source = 'import json'
>>> script = Script(source, path='example.py')
>>> d = script.goto_definitions()[0]
>>> print(d.module_name) # doctest: +ELLIPSIS
json
"""
return str(self._module.name)
def in_builtin_module(self):
"""Whether this is a builtin module."""
return isinstance(self._module, compiled.CompiledObject)
@property
def line(self):
"""The line where the definition occurs (starting with 1)."""
if self.in_builtin_module():
return None
return self._name.start_pos[0]
@property
def column(self):
"""The column where the definition occurs (starting with 0)."""
if self.in_builtin_module():
return None
return self._name.start_pos[1]
def docstring(self, raw=False):
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')
>>> doc = script.goto_definitions()[0].docstring()
>>> print(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 ``raw=True`` instead.
>>> print(script.goto_definitions()[0].docstring(raw=True))
Document for function f.
"""
if raw:
return _Help(self._definition).raw()
else:
return _Help(self._definition).full()
@property
def doc(self):
"""
.. deprecated:: 0.8.0
Use :meth:`.docstring` instead.
.. todo:: Remove!
"""
warnings.warn("Use docstring() instead.", DeprecationWarning)
return self.docstring()
@property
def raw_doc(self):
"""
.. deprecated:: 0.8.0
Use :meth:`.docstring` instead.
.. todo:: Remove!
"""
warnings.warn("Use docstring() instead.", DeprecationWarning)
return self.docstring(raw=True)
@property
def description(self):
"""A textual description of the object."""
return unicode(self._name)
@property
def full_name(self):
"""
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 = [unicode(p) for p in self._path()]
# TODO add further checks, the mapping should only occur on stdlib.
if not path:
return None # for keywords the path is empty
with common.ignored(KeyError):
path[0] = self._mapping[path[0]]
for key, repl in self._tuple_mapping.items():
if tuple(path[:len(key)]) == key:
path = [repl] + path[len(key):]
return '.'.join(path if path[0] else path[1:])
def goto_assignments(self):
defs = self._evaluator.goto(self._name)
return [Definition(self._evaluator, d) for d in defs]
@memoize_default()
def _follow_statements_imports(self):
"""
Follow both statements and imports, as far as possible.
"""
if self._definition.isinstance(tree.ExprStmt):
return self._evaluator.eval_statement(self._definition)
elif self._definition.isinstance(tree.Import):
return imports.ImportWrapper(self._evaluator, self._name).follow()
else:
return [self._definition]
@property
@memoize_default()
def params(self):
"""
Raises an ``AttributeError``if the definition is not callable.
Otherwise returns a list of `Definition` that represents the params.
"""
followed = self._follow_statements_imports()
if not followed or not hasattr(followed[0], 'py__call__'):
raise AttributeError()
followed = followed[0] # only check the first one.
if followed.type == 'funcdef':
if isinstance(followed, er.InstanceElement):
params = followed.params[1:]
else:
params = followed.params
elif followed.isinstance(er.compiled.CompiledObject):
params = followed.params
else:
try:
sub = followed.get_subscope_by_name('__init__')
params = sub.params[1:] # ignore self
except KeyError:
return []
return [_Param(self._evaluator, p.name) for p in params]
def parent(self):
scope = self._definition.get_parent_scope()
scope = self._evaluator.wrap(scope)
return Definition(self._evaluator, scope.name)
def __repr__(self):
return "<%s %s>" % (type(self).__name__, self.description)
class Completion(BaseDefinition):
"""
`Completion` objects are returned from :meth:`api.Script.completions`. They
provide additional information about a completion.
"""
def __init__(self, evaluator, name, needs_dot, like_name_length):
super(Completion, self).__init__(evaluator, name)
self._needs_dot = needs_dot
self._like_name_length = like_name_length
# Completion objects with the same Completion name (which means
# duplicate items in the completion)
self._same_name_completions = []
def _complete(self, like_name):
dot = '.' if self._needs_dot else ''
append = ''
if settings.add_bracket_after_function \
and self.type == 'Function':
append = '('
if settings.add_dot_after_module:
if isinstance(self._definition, tree.Module):
append += '.'
if isinstance(self._definition, tree.Param):
append += '='
name = str(self._name)
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_with_symbols(self):
"""
Similar to :attr:`name`, but like :attr:`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 description(self):
"""Provide a description of the completion object."""
if self._definition is None:
return ''
t = self.type
if t == 'statement' or t == 'import':
desc = self._definition.get_code()
else:
desc = '.'.join(unicode(p) for p in self._path())
line = '' if self.in_builtin_module else '@%s' % self.line
return '%s: %s%s' % (t, desc, line)
def __repr__(self):
return '<%s: %s>' % (type(self).__name__, self._name)
def docstring(self, raw=False, fast=True):
"""
:param fast: Don't follow imports that are only one level deep like
``import foo``, but follow ``from foo import bar``. This makes
sense for speed reasons. Completing `import a` is slow if you use
the ``foo.docstring(fast=False)`` on every object, because it
parses all libraries starting with ``a``.
"""
definition = self._definition
if isinstance(definition, tree.Import):
i = imports.ImportWrapper(self._evaluator, self._name)
if len(i.import_path) > 1 or not fast:
followed = self._follow_statements_imports()
if followed:
# TODO: Use all of the followed objects as input to Documentation.
definition = followed[0]
if raw:
return _Help(definition).raw()
else:
return _Help(definition).full()
@property
def type(self):
"""
The type of the completion objects. Follows imports. For a further
description, look at :attr:`jedi.api.classes.BaseDefinition.type`.
"""
if isinstance(self._definition, tree.Import):
i = imports.ImportWrapper(self._evaluator, self._name)
if len(i.import_path) <= 1:
return 'module'
followed = self.follow_definition()
if followed:
# Caveat: Only follows the first one, ignore the other ones.
# This is ok, since people are almost never interested in
# variations.
return followed[0].type
return super(Completion, self).type
@memoize_default()
def _follow_statements_imports(self):
# imports completion is very complicated and needs to be treated
# separately in Completion.
definition = self._definition
if definition.isinstance(tree.Import):
i = imports.ImportWrapper(self._evaluator, self._name)
return i.follow()
return super(Completion, self)._follow_statements_imports()
@memoize_default()
def follow_definition(self):
"""
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.
"""
defs = self._follow_statements_imports()
return [Definition(self._evaluator, d.name) for d in defs]
class Definition(use_metaclass(CachedMetaClass, BaseDefinition)):
"""
*Definition* objects are returned from :meth:`api.Script.goto_assignments`
or :meth:`api.Script.goto_definitions`.
"""
def __init__(self, evaluator, definition):
super(Definition, self).__init__(evaluator, definition)
@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 if random.choice([0,1]) else 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, compiled.CompiledObject):
typ = d.api_type()
if typ == 'instance':
typ = 'class' # The description should be similar to Py objects.
d = typ + ' ' + d.name.get_code()
elif isinstance(d, iterable.Array):
d = 'class ' + d.type
elif isinstance(d, (tree.Class, er.Class, er.Instance)):
d = 'class ' + unicode(d.name)
elif isinstance(d, (er.Function, tree.Function)):
d = 'def ' + unicode(d.name)
elif isinstance(d, tree.Module):
# only show module name
d = 'module %s' % self.module_name
elif isinstance(d, tree.Param):
d = d.get_code().strip()
if d.endswith(','):
d = d[:-1] # Remove the comma.
else: # ExprStmt
try:
first_leaf = d.first_leaf()
except AttributeError:
# `d` is already a Leaf (Name).
first_leaf = d
# Remove the prefix, because that's not what we want for get_code
# here.
old, first_leaf.prefix = first_leaf.prefix, ''
try:
d = d.get_code()
finally:
first_leaf.prefix = old
# Delete comments:
d = re.sub('#[^\n]+\n', ' ', d)
# Delete multi spaces/newlines
return re.sub('\s+', ' ', d).strip()
@property
def desc_with_module(self):
"""
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.
"""
position = '' if self.in_builtin_module else '@%s' % (self.line)
return "%s:%s%s" % (self.module_name, self.description, position)
@memoize_default()
def defined_names(self):
"""
List sub-definitions (e.g., methods in class).
:rtype: list of Definition
"""
defs = self._follow_statements_imports()
# For now we don't want base classes or evaluate decorators.
defs = [d.base if isinstance(d, (er.Class, er.Function)) else d for d in defs]
iterable = (defined_names(self._evaluator, d) for d in defs)
iterable = list(iterable)
return list(chain.from_iterable(iterable))
def is_definition(self):
"""
Returns True, if defined as a name in a statement, function or class.
Returns False, if it's a reference to such a definition.
"""
return self._name.is_definition()
def __eq__(self, other):
return self._name.start_pos == other._name.start_pos \
and self.module_path == other.module_path \
and self.name == other.name \
and self._evaluator == other._evaluator
def __ne__(self, other):
return not self.__eq__(other)
def __hash__(self):
return hash((self._name.start_pos, self.module_path, self.name, self._evaluator))
class CallSignature(Definition):
"""
`CallSignature` 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.
"""
def __init__(self, evaluator, executable_name, call_stmt, index, key_name):
super(CallSignature, self).__init__(evaluator, executable_name)
self._index = index
self._key_name = key_name
self._call_stmt = call_stmt
@property
def index(self):
"""
The Param index of the current call.
Returns None if the index cannot be found in the curent call.
"""
if self._key_name is not None:
for i, param in enumerate(self.params):
if self._key_name == param.name:
return i
if self.params and self.params[-1]._name.get_definition().stars == 2:
return i
else:
return None
if self._index >= len(self.params):
for i, param in enumerate(self.params):
# *args case
if param._name.get_definition().stars == 1:
return i
return None
return self._index
@property
def bracket_start(self):
"""
The indent of the bracket that is responsible for the last function
call.
"""
return self._call_stmt.end_pos
@property
def call_name(self):
"""
.. deprecated:: 0.8.0
Use :attr:`.name` instead.
.. todo:: Remove!
The name (e.g. 'isinstance') as a string.
"""
warnings.warn("Use name instead.", DeprecationWarning)
return unicode(self.name)
@property
def module(self):
"""
.. deprecated:: 0.8.0
Use :attr:`.module_name` for the module name.
.. todo:: Remove!
"""
return self._executable.get_parent_until()
def __repr__(self):
return '<%s: %s index %s>' % (type(self).__name__, self._name,
self.index)
class _Param(Definition):
"""
Just here for backwards compatibility.
"""
def get_code(self):
"""
.. deprecated:: 0.8.0
Use :attr:`.description` and :attr:`.name` instead.
.. todo:: Remove!
A function to get the whole code of the param.
"""
warnings.warn("Use description instead.", DeprecationWarning)
return self.description
class _Help(object):
"""
Temporary implementation, will be used as `Script.help() or something in
the future.
"""
def __init__(self, definition):
self._name = definition
def full(self):
try:
return self._name.doc
except AttributeError:
return self.raw()
def raw(self):
"""
The raw docstring ``__doc__`` for any object.
See :attr:`doc` for example.
"""
try:
return self._name.raw_doc
except AttributeError:
return ''

78
jedi/api/helpers.py Normal file
View File

@@ -0,0 +1,78 @@
"""
Helpers for the API
"""
import re
from jedi.parser import tree as pt
from jedi.evaluate import imports
def completion_parts(path_until_cursor):
"""
Returns the parts for the completion
:return: tuple - (path, dot, like)
"""
match = re.match(r'^(.*?)(\.|)(\w?[\w\d]*)$', path_until_cursor, flags=re.S)
return match.groups()
def sorted_definitions(defs):
# Note: `or ''` below is required because `module_path` could be
return sorted(defs, key=lambda x: (x.module_path or '', x.line or 0, x.column or 0))
def get_on_import_stmt(evaluator, user_context, user_stmt, is_like_search=False):
"""
Resolve the user statement, if it is an import. Only resolve the
parts until the user position.
"""
name = user_stmt.name_for_position(user_context.position)
if name is None:
return None, None
i = imports.ImportWrapper(evaluator, name)
return i, name
def check_error_statements(module, pos):
for error_statement in module.error_statement_stacks:
if error_statement.first_type in ('import_from', 'import_name') \
and error_statement.first_pos < pos <= error_statement.next_start_pos:
return importer_from_error_statement(error_statement, pos)
return None, 0, False, False
def importer_from_error_statement(error_statement, pos):
def check_dotted(children):
for name in children[::2]:
if name.start_pos <= pos:
yield name
names = []
level = 0
only_modules = True
unfinished_dotted = False
for typ, nodes in error_statement.stack:
if typ == 'dotted_name':
names += check_dotted(nodes)
if nodes[-1] == '.':
# An unfinished dotted_name
unfinished_dotted = True
elif typ == 'import_name':
if nodes[0].start_pos <= pos <= nodes[0].end_pos:
# We are on the import.
return None, 0, False, False
elif typ == 'import_from':
for node in nodes:
if node.start_pos >= pos:
break
elif isinstance(node, pt.Node) and node.type == 'dotted_name':
names += check_dotted(node.children)
elif node in ('.', '...'):
level += len(node.value)
elif isinstance(node, pt.Name):
names.append(node)
elif node == 'import':
only_modules = False
return names, level, only_modules, unfinished_dotted

109
jedi/api/interpreter.py Normal file
View File

@@ -0,0 +1,109 @@
"""
TODO Some parts of this module are still not well documented.
"""
import inspect
import re
from jedi._compatibility import builtins
from jedi import debug
from jedi.common import source_to_unicode
from jedi.cache import underscore_memoization
from jedi.evaluate import compiled
from jedi.evaluate.compiled.fake import get_module
from jedi.parser import tree as pt
from jedi.parser import load_grammar
from jedi.parser.fast import FastParser
from jedi.evaluate import helpers
from jedi.evaluate import iterable
from jedi.evaluate import representation as er
def add_namespaces_to_parser(evaluator, namespaces, parser_module):
for namespace in namespaces:
for key, value in namespace.items():
# Name lookups in an ast tree work by checking names_dict.
# Therefore we just add fake names to that and we're done.
arr = parser_module.names_dict.setdefault(key, [])
arr.append(LazyName(evaluator, parser_module, key, value))
class LazyName(helpers.FakeName):
def __init__(self, evaluator, module, name, value):
super(LazyName, self).__init__(name)
self._module = module
self._evaluator = evaluator
self._value = value
self._name = name
def is_definition(self):
return True
@property
@underscore_memoization
def parent(self):
"""
Creating fake statements for the interpreter.
"""
obj = self._value
parser_path = []
if inspect.ismodule(obj):
module = obj
else:
names = []
try:
o = obj.__objclass__
names.append(obj.__name__)
obj = o
except AttributeError:
pass
try:
module_name = obj.__module__
names.insert(0, obj.__name__)
except AttributeError:
# Unfortunately in some cases like `int` there's no __module__
module = builtins
else:
# TODO this import is wrong. Yields x for x.y.z instead of z
module = __import__(module_name)
parser_path = names
raw_module = get_module(self._value)
found = []
try:
path = module.__file__
except AttributeError:
pass
else:
path = re.sub('c$', '', path)
if path.endswith('.py'):
# cut the `c` from `.pyc`
with open(path) as f:
source = source_to_unicode(f.read())
mod = FastParser(load_grammar(), source, path[:-1]).module
if parser_path:
assert len(parser_path) == 1
found = self._evaluator.find_types(mod, parser_path[0], search_global=True)
else:
found = [self._evaluator.wrap(mod)]
if not found:
debug.warning('Possibly an interpreter lookup for Python code failed %s',
parser_path)
if not found:
evaluated = compiled.CompiledObject(obj)
if evaluated == builtins:
# The builtins module is special and always cached.
evaluated = compiled.builtin
found = [evaluated]
content = iterable.AlreadyEvaluated(found)
stmt = pt.ExprStmt([self, pt.Operator(pt.zero_position_modifier,
'=', (0, 0), ''), content])
stmt.parent = self._module
return stmt
@parent.setter
def parent(self, value):
"""Needed because the super class tries to set parent."""

View File

@@ -1,11 +1,10 @@
from __future__ import with_statement
import pydoc
import keyword
from jedi._compatibility import is_py3k
from jedi._compatibility import is_py3
from jedi import common
import builtin
from jedi.evaluate import compiled
from jedi.evaluate.helpers import FakeName
try:
from pydoc_data import topics as pydoc_topics
@@ -13,13 +12,13 @@ except ImportError:
# Python 2.6
import pydoc_topics
if is_py3k:
if is_py3:
keys = keyword.kwlist
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:
@@ -27,15 +26,19 @@ def get_keywords(string='', pos=(0, 0), all=False):
return set()
def keyword_names(*args, **kwargs):
return [k.name for k in keywords(*args, **kwargs)]
def get_operator(string, pos):
return Keyword(string, pos)
class Keyword(object):
def __init__(self, name, pos):
self.name = name
self.name = FakeName(name, self, pos)
self.start_pos = pos
self.parent = builtin.Builtin.scope
self.parent = compiled.builtin
def get_parent_until(self):
return self.parent

27
jedi/api/replstartup.py Normal file
View File

@@ -0,0 +1,27 @@
"""
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
from jedi import __version__ as __jedi_version__
print('REPL completion using Jedi %s' % __jedi_version__)
jedi.utils.setup_readline()
del jedi
# Note: try not to do many things here, as it will contaminate global
# namespace of the interpreter.

49
jedi/api/usages.py Normal file
View File

@@ -0,0 +1,49 @@
from jedi._compatibility import unicode
from jedi.api import classes
from jedi.parser import tree
from jedi.evaluate import imports
def usages(evaluator, definition_names, mods):
"""
:param definitions: list of Name
"""
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
search_name = unicode(list(definition_names)[0])
compare_definitions = compare_array(definition_names)
mods |= set([d.get_parent_until() for d in definition_names])
definitions = []
for m in imports.get_modules_containing_name(evaluator, mods, search_name):
try:
check_names = m.used_names[search_name]
except KeyError:
continue
for name in check_names:
result = evaluator.goto(name)
if [c for c in compare_array(result) if c in compare_definitions]:
definitions.append(classes.Definition(evaluator, name))
# Previous definitions might be imports, so include them
# (because goto might return that import name).
compare_definitions += compare_array([name])
return definitions
def usages_add_import_modules(evaluator, definitions):
""" Adds the modules of the imports """
new = set()
for d in definitions:
imp_or_stmt = d.get_definition()
if isinstance(imp_or_stmt, tree.Import):
s = imports.ImportWrapper(evaluator, d)
new |= set(s.follow(is_goto=True))
return set(definitions) | new

View File

@@ -1,594 +0,0 @@
"""
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 warnings
import functools
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 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',
'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',
'_sre.SRE_Match': 're.MatchObject',
'_sre.SRE_Pattern': 're.RegexObject',
}.items())
def __init__(self, definition, start_pos):
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 = definition.get_parent_until()
self.module_path = self._module.path
@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.definition` should return a list of
definition for ``sys``, ``f``, ``C`` and ``x``.
>>> from jedi import Script
>>> source = '''
... import sys
...
... class C:
... pass
...
... class D:
... pass
...
... x = D()
...
... def f():
... pass
...
... variable = sys or f or C or x'''
>>> script = Script(source, len(source.splitlines()), 3, 'example.py')
>>> defs = script.definition()
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 sys>, <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, 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
while par is not None:
with common.ignored(AttributeError):
path.insert(0, par.name)
par = par.parent
return path
@property
def module_name(self):
"""
The module name.
>>> from jedi import Script
>>> source = 'import datetime'
>>> script = Script(source, 1, len(source), 'example.py')
>>> d = script.definition()[0]
>>> print(d.module_name) # doctest: +ELLIPSIS
datetime
"""
return str(self._module.name)
def in_builtin_module(self):
"""Whether this is a builtin module."""
return not (self.module_path is None or
self.module_path.endswith('.py'))
@property
def line_nr(self):
"""
.. 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)."""
return self.start_pos[0]
@property
def column(self):
"""The column where the definition occurs (starting with 0)."""
return self.start_pos[1]
@property
def doc(self):
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.definition()[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
except AttributeError:
return self.raw_doc
@property
def raw_doc(self):
"""
The raw docstring ``__doc__`` for any object.
See :attr:`doc` for example.
"""
try:
return unicode(self._definition.docstr)
except AttributeError:
return ''
@property
def description(self):
"""
A textual description of the object.
Example:
>>> from jedi import Script
>>> source = '''
... def f():
... pass
...
... class C:
... pass
...
... variable = f or C'''
>>> script = Script(source, len(source.splitlines()), 3, 'example.py')
>>> defs = script.definition() # doctest: +SKIP
>>> defs = sorted(defs, key=lambda d: d.line) # doctest: +SKIP
>>> defs # doctest: +SKIP
[<Definition def f>, <Definition class C>]
>>> defs[0].description # doctest: +SKIP
'def f'
>>> defs[1].description # doctest: +SKIP
'class C'
"""
return unicode(self._definition)
@property
def full_name(self):
"""
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.definition()[0].full_name)
os.path.join
Notice that it correctly returns ``'os.path.join'`` instead of
(for example) ``'posixpath.join'``.
"""
path = [unicode(p) for p in self.path]
# TODO add further checks, the mapping should only occur on stdlib.
if not path:
return None # for keywords the path is empty
with common.ignored(KeyError):
path[0] = self._mapping[path[0]]
for key, repl in self._tuple_mapping.items():
if tuple(path[:len(key)]) == key:
path = [repl] + path[len(key):]
return '.'.join(path if path[0] else path[1:])
def __repr__(self):
return "<%s %s>" % (type(self).__name__, self.description)
class Completion(BaseDefinition):
"""
`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
# 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):
"""
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`.
"""
dot = '.' if self._needs_dot else ''
append = ''
if settings.add_bracket_after_function \
and self.type == 'Function':
append = '('
if settings.add_dot_after_module:
if isinstance(self._base, pr.Module):
append += '.'
if isinstance(self._base, pr.Param):
append += '='
return dot + self._name.names[-1][self._like_name_length:] + append
@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 word(self):
"""
.. deprecated:: 0.6.0
Use :attr:`.name` instead.
.. todo:: Remove!
"""
warnings.warn("Use name instead.", DeprecationWarning)
return self.name
@property
def description(self):
"""
Provide a description of the completion object.
.. todo:: return value is just __repr__ of some objects, improve!
"""
parent = self._name.parent
if parent is None:
return ''
t = self.type
if t == 'Statement' or t == 'Import':
desc = self._definition.get_code(False)
else:
desc = '.'.join(unicode(p) for p in self.path)
line = '' if self.in_builtin_module else '@%s' % self.line
return '%s: %s%s' % (t, desc, line)
def follow_definition(self):
"""
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(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]
_clear_caches()
return self._followed_definitions
def __repr__(self):
return '<%s: %s>' % (type(self).__name__, self._name)
class Definition(BaseDefinition):
"""
*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 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, 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``.
"""
d = self._definition
if isinstance(d, er.InstanceElement):
d = d.var
if isinstance(d, pr.Name):
d = d.parent
if isinstance(d, er.Array):
d = 'class ' + d.type
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', '')
return d
@property
def desc_with_module(self):
"""
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, 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).
: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(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])
def __eq__(self, other):
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))
class CallDef(object):
"""
`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.
"""
def __init__(self, executable, index, call):
self._executable = executable
self.index = index
self._call = call
@property
def params(self):
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__')
return sub.params[1:] # ignore self
except KeyError:
return []
@property
def bracket_start(self):
""" The indent of the bracket that is responsible for the last function
call. """
c = self._call
while c.next is not None:
c = c.next
return c.name.end_pos
@property
def call_name(self):
""" The name (e.g. 'isinstance') as a string. """
return unicode(self._executable.name)
@property
def module(self):
return self._executable.get_parent_until()
def __repr__(self):
return '<%s: %s index %s>' % (type(self).__name__, self._executable,
self.index)

View File

@@ -1,449 +0,0 @@
"""
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 jedi._compatibility import exec_function, is_py3k
import re
import sys
import os
if is_py3k:
import io
import types
import inspect
from jedi import common
from jedi import debug
from jedi import parsing
from jedi import modules
import evaluate
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.
It can be instantiated with either a path or a name of the module. The path
is important for third party modules.
:param name: The name of the module.
:param path: The path of the module.
:param sys_path: The sys.path, which is can be customizable.
"""
map_types = {
'floating point number': '0.0',
'string': '""',
'str': '""',
'character': '"a"',
'integer': '0',
'int': '0',
'dictionary': '{}',
'list': '[]',
'file object': 'file("")',
# TODO things like dbg: ('not working', 'tuple of integers')
}
if is_py3k:
map_types['file object'] = 'import io; return io.TextIOWrapper()'
def __init__(self, path=None, name=None, sys_path=None):
if sys_path is None:
sys_path = modules.get_sys_path()
if not name:
name = os.path.basename(path)
name = name.rpartition('.')[0] # cut file type (normally .so)
super(BuiltinModule, self).__init__(path=path, name=name)
self.sys_path = list(sys_path)
self._module = None
@property
def module(self):
def load_module(name, path):
if path:
self.sys_path.insert(0, path)
temp, sys.path = sys.path, self.sys_path
content = {}
try:
exec_function('import %s as module' % name, content)
self._module = content['module']
except AttributeError:
# use sys.modules, because you cannot access some modules
# directly. -> #59
self._module = sys.modules[name]
sys.path = temp
if path:
self.sys_path.pop(0)
# module might already be defined
if not self._module:
path = self.path
name = self.name
if self.path:
dot_path = []
p = self.path
# search for the builtin with the correct path
while p and p not in sys.path:
p, sep, mod = p.rpartition(os.path.sep)
dot_path.append(mod.partition('.')[0])
if p:
name = ".".join(reversed(dot_path))
path = p
else:
path = os.path.dirname(self.path)
load_module(name, path)
return self._module
def _get_source(self):
""" Override this abstract method """
return _generate_code(self.module, self._load_mixins())
def _load_mixins(self):
"""
Load functions that are mixed in to the standard library.
E.g. builtins are written in C (binaries), but my autocompletion only
understands Python code. By mixing in Python code, the autocompletion
should work much better for builtins.
"""
regex = r'^(def|class)\s+([\w\d]+)'
def process_code(code, depth=0):
funcs = {}
matches = list(re.finditer(regex, code, re.MULTILINE))
positions = [m.start() for m in matches]
for i, pos in enumerate(positions):
try:
code_block = code[pos:positions[i + 1]]
except IndexError:
code_block = code[pos:len(code)]
structure_name = matches[i].group(1)
name = matches[i].group(2)
if structure_name == 'def':
funcs[name] = code_block
elif structure_name == 'class':
if depth > 0:
raise NotImplementedError()
# remove class line
c = re.sub(r'^[^\n]+', '', code_block)
# remove whitespace
c = re.compile(r'^[ ]{4}', re.MULTILINE).sub('', c)
funcs[name] = process_code(c)
else:
raise NotImplementedError()
return funcs
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]) + '.pym') as f:
s = f.read()
except IOError:
return {}
else:
mixin_dct = process_code(s)
if is_py3k and self.name == Builtin.name:
# in the case of Py3k xrange is now range
mixin_dct['range'] = mixin_dct['xrange']
return mixin_dct
def _generate_code(scope, mixin_funcs={}, depth=0):
"""
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 = common.indent_block(doc)
return doc
return ''
def is_in_base_classes(cls, name, comparison):
""" Base classes may contain the exact same object """
if name in mixin_funcs:
return False
try:
mro = cls.mro()
except TypeError:
# this happens, if cls == type
return False
for base in mro[1:]:
try:
attr = getattr(base, name)
except AttributeError:
continue
if attr == comparison:
return True
return False
def get_scope_objects(names):
"""
Looks for the names defined with dir() in an objects and divides
them into different object types.
"""
classes = {}
funcs = {}
stmts = {}
members = {}
for n in names:
try:
# this has a builtin_function_or_method
exe = getattr(scope, n)
except AttributeError:
# happens e.g. in properties of
# PyQt4.QtGui.QStyleOptionComboBox.currentText
# -> just set it to None
members[n] = None
else:
if inspect.isclass(scope):
if is_in_base_classes(scope, n, exe):
continue
if inspect.isbuiltin(exe) or inspect.ismethod(exe) \
or inspect.ismethoddescriptor(exe):
funcs[n] = exe
elif inspect.isclass(exe) or inspect.ismodule(exe):
classes[n] = exe
elif inspect.ismemberdescriptor(exe):
members[n] = exe
else:
stmts[n] = exe
return classes, funcs, stmts, members
code = ''
if inspect.ismodule(scope): # generate comment where the code's from.
try:
path = scope.__file__
except AttributeError:
path = '?'
code += '# Generated module %s from %s\n' % (scope.__name__, path)
code += get_doc(scope)
names = set(dir(scope)) - set(['__file__', '__name__', '__doc__',
'__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__) if inspect.isclass(cl) \
else []
code += 'class %s(%s):\n' % (name, ','.join(bases))
if depth == 0:
try:
mixin = mixin_funcs[name]
except KeyError:
mixin = {}
cl_code = _generate_code(cl, mixin, depth + 1)
code += common.indent_block(cl_code)
code += '\n'
# functions
for name, func in funcs.items():
params, ret = _parse_function_doc(func)
if depth > 0:
params = 'self, ' + params
doc_str = get_doc(func, indent=True)
try:
mixin = mixin_funcs[name]
except KeyError:
# normal code generation
code += 'def %s(%s):\n' % (name, params)
code += doc_str
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
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:]
# class members (functions) properties?
for name, func in members.items():
# recursion problem in properties TODO remove
if name in ['fget', 'fset', 'fdel']:
continue
ret = 'pass'
code += '@property\ndef %s(self):\n' % (name)
code += common.indent_block(get_doc(func) + '%s\n\n' % ret)
# variables
for name, value in stmts.items():
if is_py3k:
file_type = io.TextIOWrapper
else:
file_type = types.FileType
if type(value) == file_type:
value = 'open()'
elif name == 'None':
value = ''
elif type(value).__name__ in ['int', 'bool', 'float',
'dict', 'list', 'tuple']:
value = repr(value)
else:
# get the type, if the type is not simple.
mod = type(value).__module__
value = type(value).__name__ + '()'
if mod != '__builtin__':
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):
"""
Takes a function and returns the params and return value as a tuple.
This is nothing more than a docstring parser.
"""
# TODO: things like utime(path, (atime, mtime)) and a(b [, b]) -> None
doc = inspect.getdoc(func)
# get full string, parse round parentheses: def func(a, (b,c))
try:
count = 0
debug.dbg(func, func.__name__, doc)
start = doc.index('(')
for i, s in enumerate(doc[start:]):
if s == '(':
count += 1
elif s == ')':
count -= 1
if count == 0:
end = start + i
break
param_str = doc[start + 1:end]
# remove square brackets, that show an optional param ( = None)
def change_options(m):
args = m.group(1).split(',')
for i, a in enumerate(args):
if a and '=' not in a:
args[i] += '=None'
return ','.join(args)
while True:
param_str, changes = re.subn(r' ?\[([^\[\]]+)\]',
change_options, param_str)
if changes == 0:
break
except (ValueError, AttributeError):
debug.dbg('no brackets found - no param')
end = 0
param_str = ''
param_str = param_str.replace('-', '_') # see: isinstance.__doc__
if doc is not None:
r = re.search('-[>-]* ', doc[end:end + 7])
if doc is None or r is None:
ret = 'pass'
else:
index = end + r.end()
# get result type, which can contain newlines
pattern = re.compile(r'(,\n|[^\n-])+')
ret_str = pattern.match(doc, index).group(0).strip()
# New object -> object()
ret_str = re.sub(r'[nN]ew (.*)', r'\1()', 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':
ret = ('return ' if 'return' not in ret else '') + ret
return param_str, ret
class Builtin(object):
""" The builtin scope / module """
# Python 3 compatibility
if is_py3k:
name = 'builtins'
else:
name = '__builtin__'
_builtin = None
@property
def builtin(self):
if self._builtin is None:
self._builtin = BuiltinModule(name=self.name)
return self._builtin
@property
def scope(self):
return self.builtin.parser.module
@property
def magic_function_scope(self):
try:
return self._magic_function_scope
except AttributeError:
# depth = 1 because this is not a module
class Container(object):
FunctionType = types.FunctionType
source = _generate_code(Container, depth=0)
parser = parsing.Parser(source, None)
module = parser.module
module.parent = self.scope
typ = evaluate.follow_path(iter(['FunctionType']), module, module)
s = self._magic_function_scope = typ.pop()
return s
Builtin = Builtin()

View File

@@ -3,11 +3,8 @@ 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
- module caching (`load_parser` and `save_parser`), 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.
@@ -16,29 +13,25 @@ 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
import gc
import inspect
import shutil
import re
try:
import cPickle as pickle
except:
except ImportError:
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 = {}
_time_caches = {}
# for fast_parser, should not be deleted
parser_cache = {}
@@ -52,27 +45,22 @@ class ParserCacheItem(object):
self.change_time = change_time
def clear_caches(delete_all=False):
def clear_time_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()
global _time_caches
if delete_all:
time_caches = []
star_import_cache.clear()
for cache in _time_caches.values():
cache.clear()
parser_cache.clear()
else:
# normally just kill the expired entries, not all
for tc in time_caches:
for tc in _time_caches.values():
# check time_cache for expired entries
for key, (t, value) in list(tc.items()):
if t < time.time():
@@ -80,59 +68,29 @@ def clear_caches(delete_all=False):
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
"""
s
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)
_time_caches[time_add_setting] = dct
def wrapper(optional_callable, *args, **kwargs):
key = key_func(*args, **kwargs)
value = None
if key in dct:
def wrapper(*args, **kwargs):
generator = key_func(*args, **kwargs)
key = next(generator)
try:
expiry, value = dct[key]
if expiry > time.time():
return value
value = optional_callable()
except KeyError:
pass
value = next(generator)
time_add = getattr(settings, time_add_setting)
if key is not None:
dct[key] = time.time() + time_add, value
@@ -141,85 +99,133 @@ def time_cache(time_add_setting):
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)
@time_cache("call_signatures_validity")
def cache_call_signatures(evaluator, call, source, user_pos):
"""This function calculates the cache key."""
index = user_pos[0] - 1
lines = common.splitlines(source)
before_cursor = lines[index][:user_pos[1]]
other_lines = lines[call.start_pos[0]:index]
whole = '\n'.join(other_lines + [before_cursor])
before_bracket = re.match(r'.*\(', whole, re.DOTALL)
module_path = call.get_parent_until().path
yield None if module_path is None else (module_path, before_bracket, call.start_pos)
yield evaluator.eval_element(call)
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
def underscore_memoization(func):
"""
Decorator for methods::
class A(object):
def x(self):
if self._x:
self._x = 10
return self._x
Becomes::
class A(object):
@underscore_memoization
def x(self):
return 10
A now has an attribute ``_x`` written by this decorator.
"""
name = '_' + func.__name__
def wrapper(self):
try:
return getattr(self, name)
except AttributeError:
result = func(self)
if inspect.isgenerator(result):
result = list(result)
setattr(self, name, result)
return result
return mods
return wrapper
def invalidate_star_import_cache(module, only_main=False):
def memoize_method(method):
"""A normal memoize function."""
def wrapper(self, *args, **kwargs):
dct = self.__dict__.setdefault('_memoize_method_dct', {})
key = (args, frozenset(kwargs.items()))
try:
return dct[key]
except KeyError:
result = method(self, *args, **kwargs)
dct[key] = result
return result
return wrapper
def cache_star_import(func):
@time_cache("star_import_cache_validity")
def wrapper(self):
yield self.base # The cache key
yield func(self)
return wrapper
def _invalidate_star_import_cache_module(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)
try:
t, modules = _time_caches['star_import_cache_validity'][module]
except KeyError:
pass
else:
del _time_caches['star_import_cache_validity'][module]
def load_module(path, name):
def invalidate_star_import_cache(path):
"""On success returns True."""
try:
parser_cache_item = parser_cache[path]
except KeyError:
pass
else:
_invalidate_star_import_cache_module(parser_cache_item.parser.module)
def load_parser(path):
"""
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
p_time = os.path.getmtime(path) if path else None
try:
parser_cache_item = parser_cache[n]
if not path or tim <= parser_cache_item.change_time:
parser_cache_item = parser_cache[path]
if not path or p_time <= 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)
_invalidate_star_import_cache_module(parser_cache_item.parser.module)
except KeyError:
if settings.use_filesystem_cache:
return ModulePickling.load_module(n, tim)
return ParserPickling.load_parser(path, p_time)
def save_module(path, name, parser, pickling=True):
def save_parser(path, parser, pickling=True):
try:
p_time = None if not path else os.path.getmtime(path)
p_time = None if path is None 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
parser_cache[path] = item
if settings.use_filesystem_cache and pickling:
ModulePickling.save_module(n, item)
ParserPickling.save_parser(path, item)
class _ModulePickling(object):
class ParserPickling(object):
version = 3
version = 24
"""
Version number (integer) for file system cache.
@@ -245,7 +251,7 @@ class _ModulePickling(object):
.. todo:: Detect interpreter (e.g., PyPy).
"""
def load_module(self, path, original_changed_time):
def load_parser(self, path, original_changed_time):
try:
pickle_changed_time = self._index[path]
except KeyError:
@@ -256,13 +262,17 @@ class _ModulePickling(object):
return None
with open(self._get_hashed_path(path), 'rb') as f:
parser_cache_item = pickle.load(f)
try:
gc.disable()
parser_cache_item = pickle.load(f)
finally:
gc.enable()
debug.dbg('pickle loaded', path)
debug.dbg('pickle loaded: %s', path)
parser_cache[path] = parser_cache_item
return parser_cache_item.parser
def save_module(self, path, parser_cache_item):
def save_parser(self, path, parser_cache_item):
self.__index = None
try:
files = self._index
@@ -282,12 +292,12 @@ class _ModulePickling(object):
try:
with open(self._get_path('index.json')) as f:
data = json.load(f)
except IOError:
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.clear_cache()
self.__index = {}
else:
self.__index = data['index']
@@ -306,7 +316,7 @@ class _ModulePickling(object):
json.dump(data, f)
self.__index = None
def delete_cache(self):
def clear_cache(self):
shutil.rmtree(self._cache_directory())
def _get_hashed_path(self, path):
@@ -323,4 +333,4 @@ class _ModulePickling(object):
# is a singleton
ModulePickling = _ModulePickling()
ParserPickling = ParserPickling()

View File

@@ -2,20 +2,12 @@
import sys
import contextlib
import functools
import tokenizer as tokenize
import re
from ast import literal_eval
from jedi._compatibility import next, reraise
from jedi._compatibility import unicode, 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):
"""
@@ -30,7 +22,11 @@ class UncaughtAttributeError(Exception):
"""
def rethrow_uncaught(func):
def safe_property(func):
return property(reraise_uncaught(func))
def reraise_uncaught(func):
"""
Re-throw uncaught `AttributeError`.
@@ -84,115 +80,21 @@ class PushBackIterator(object):
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
try:
yield
finally:
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 """
"""This function indents a text block with a default of four spaces."""
temp = ''
while text and text[-1] == '\n':
temp += text[-1]
@@ -203,9 +105,50 @@ def indent_block(text, indention=' '):
@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."""
"""
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
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')
def splitlines(string):
"""
A splitlines for Python code. In contrast to Python's ``str.splitlines``,
looks at form feeds and other special characters as normal text. Just
splits ``\n`` and ``\r\n``.
Also different: Returns ``['']`` for an empty string input.
"""
return re.split('\n|\r\n', string)

View File

@@ -1,10 +1,20 @@
from jedi._compatibility import encoding, is_py3, u
import inspect
import os
import time
try:
# Use colorama for nicer console output.
from colorama import Fore, init
init()
if os.name == 'nt':
# does not work on Windows, as pyreadline and colorama interfere
raise ImportError
else:
# Use colorama for nicer console output.
from colorama import Fore, init
from colorama import initialise
# pytest resets the stream at the end - causes troubles. Since after
# every output the stream is reset automatically we don't need this.
initialise.atexit_done = True
init()
except ImportError:
class Fore(object):
RED = ''
@@ -22,33 +32,51 @@ enable_notice = False
# callback, interface: level, str
debug_function = None
ignored_modules = ['parsing', 'builtin', 'jedi.builtin', 'jedi.parsing']
ignored_modules = ['jedi.evaluate.builtin', 'jedi.parser']
_debug_indent = -1
_start_time = time.time()
def reset_time():
global start_time
start_time = time.time()
global _start_time, _debug_indent
_start_time = time.time()
_debug_indent = -1
def dbg(*args):
def increase_indent(func):
"""Decorator for makin """
def wrapper(*args, **kwargs):
global _debug_indent
_debug_indent += 1
try:
result = func(*args, **kwargs)
finally:
_debug_indent -= 1
return result
return wrapper
def dbg(message, *args):
""" Looks at the stack, to see if a debug message should be printed. """
if debug_function and enable_notice:
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))
i = ' ' * _debug_indent
debug_function(NOTICE, i + 'dbg: ' + message % tuple(u(repr(a)) for a in args))
def warning(*args):
def warning(message, *args):
if debug_function and enable_warning:
debug_function(WARNING, 'warning: ' + ', '.join(str(a) for a in args))
i = ' ' * _debug_indent
debug_function(WARNING, i + 'warning: ' + message % tuple(u(repr(a)) for a in args))
def speed(name):
if debug_function and enable_speed:
global start_time
now = time.time()
debug_function(SPEED, 'speed: ' + '%s %s' % (name, now - start_time))
i = ' ' * _debug_indent
debug_function(SPEED, i + 'speed: ' + '%s %s' % (name, now - _start_time))
def print_to_stdout(level, str_out):
@@ -59,7 +87,9 @@ def print_to_stdout(level, str_out):
col = Fore.RED
else:
col = Fore.YELLOW
if not is_py3:
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,127 +0,0 @@
"""
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':[^`]+:`([^`]+)`')
@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()))
user_position = (1, 0)
if param_str is not None:
# 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)
return evaluate.follow_statement(p.user_stmt)
return []
def _search_param_in_docstr(docstr, param_str):
"""
Search `docstr` for a type of `param_str`.
>>> _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))
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)
p.user_stmt.parent = func
return list(evaluate.follow_statement(p.user_stmt))

View File

@@ -1,551 +0,0 @@
"""
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:
- 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
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.
search_param_modules = ['.']
search_param_cache = {}
def get_directory_modules_for_name(mods, name):
"""
Search a name in the directories of modules.
"""
def check_python_file(path):
try:
return cache.parser_cache[path].parser.module
except KeyError:
try:
return check_fs(path)
except IOError:
return None
def check_fs(path):
with open(path) as f:
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 is None or m.path.endswith('.py'))
mod_paths = set()
for m in mods:
mod_paths.add(m.path)
yield m
if settings.dynamic_params_for_other_modules:
paths = set(settings.additional_dynamic_modules)
for p in mod_paths:
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:
c = check_python_file(p)
if c is not None and c not in mods:
yield c
def search_param_memoize(func):
"""
Is only good for search params memoize, respectively the closure,
because it just caches the input, not the func, like normal memoize does.
"""
def wrapper(*args, **kwargs):
key = (args, frozenset(kwargs.items()))
if key in search_param_cache:
return search_param_cache[key]
else:
rv = func(*args, **kwargs)
search_param_cache[key] = rv
return rv
return wrapper
class ParamListener(object):
"""
This listener is used to get the params for a function.
"""
def __init__(self):
self.param_possibilities = []
def execute(self, params):
self.param_possibilities.append(params)
@cache.memoize_default([])
def search_params(param):
"""
This is a dynamic search for params. If you try to complete a type:
>>> def func(foo):
>>> # here is the completion
>>> foo
>>> func(1)
>>> func("")
It is not known what the type is, because it cannot be guessed with
recursive madness. Therefore one has to analyse the statements that are
calling the function, as well as analyzing the incoming params.
"""
if not settings.dynamic_params:
return []
def get_params_for_module(module):
"""
Returns the values of a param, or an empty array.
"""
@search_param_memoize
def get_posibilities(module, func_name):
try:
possible_stmts = module.used_names[func_name]
except KeyError:
return []
for stmt in possible_stmts:
if not isinstance(stmt, pr.Import):
calls = _scan_statement(stmt, 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)
return listener.param_possibilities
result = []
for params in get_posibilities(module, func_name):
for p in params:
if str(p) == param_name:
result += evaluate.follow_statement(p.parent)
return result
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, pr.Class):
func_name = str(func.parent.name)
# get the param name
if param.assignment_details:
# first assignment details, others would be a syntax error
commands, op = param.assignment_details[0]
else:
commands = param.get_commands()
offset = 1 if commands[0] in ['*', '**'] else 0
param_name = str(commands[offset].name)
# add the listener
listener = ParamListener()
func.listeners.add(listener)
result = []
# This is like backtracking: Get the first possible result.
for mod in get_directory_modules_for_name([current_module], func_name):
result = get_params_for_module(mod)
if result:
break
# cleanup: remove the listener; important: should not stick.
func.listeners.remove(listener)
return result
def check_array_additions(array):
""" Just a mapper function for the internal _check_array_additions """
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.get_parent_until()
res = _check_array_additions(array, current_module, is_list)
return res
def _scan_statement(stmt, search_name, assignment_details=False):
""" Returns the function Call that match search_name in an Array. """
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
return result
@cache.memoize_default([])
def _check_array_additions(compare_array, module, is_list):
"""
Checks if a `pr.Array` has "add" statements:
>>> a = [""]
>>> a.append(1)
"""
if not settings.dynamic_array_additions or module.is_builtin():
return []
def check_calls(calls, add_name):
"""
Calls are processed here. The part before the call is searched and
compared with the original Array.
"""
result = []
for c in calls:
call_path = list(c.generate_call_path())
separate_index = call_path.index(add_name)
if add_name == call_path[-1] or separate_index == 0:
# this means that there is no execution -> [].append
# or the keyword is at the start -> append()
continue
backtrack_path = iter(call_path[:separate_index])
position = c.start_pos
scope = c.get_parent_until(pr.IsScope)
found = evaluate.follow_call_path(backtrack_path, scope, position)
if not compare_array in found:
continue
params = call_path[separate_index + 1]
if not params.values:
continue # no params: just ignore it
if add_name in ['append', 'add']:
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_statement(second_param)
elif add_name in ['extend', 'update']:
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, er.Array):
stmt = element._array.parent
else:
# 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, er.Execution)
possible_stmts = []
res = []
for n in search_names:
try:
possible_stmts += module.used_names[n]
except KeyError:
continue
for stmt in possible_stmts:
# Check if the original scope is an execution. If it is, one
# 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, er.Execution):
stmt = comp_arr_parent. \
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, er.InstanceElement):
stmt = er.InstanceElement(comp_arr_parent.instance, stmt)
if evaluate.follow_statement.push_stmt(stmt):
# check recursion
continue
res += check_calls(_scan_statement(stmt, n), n)
evaluate.follow_statement.pop_stmt()
# reset settings
settings.dynamic_params_for_other_modules = temp_param_add
return res
def check_array_instances(instance):
"""Used for set() and list() instances."""
if not settings.dynamic_arrays_instances:
return instance.var_args
ai = ArrayInstance(instance)
return [ai]
class ArrayInstance(pr.Base):
"""
Used for the usage of set() and list().
This is definitely a hack, but a good one :-)
It makes it possible to use set/list conversions.
"""
def __init__(self, instance):
self.instance = instance
self.var_args = instance.var_args
def iter_content(self):
"""
The index is here just ignored, because of all the appends, etc.
lists/sets are too complicated too handle that.
"""
items = []
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])
# 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.get_parent_until()
is_list = str(self.instance.name) == 'list'
items += _check_array_additions(self.instance, module, is_list)
return items
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 `pr.NamePart`.
if name == search_name:
follow.append(call_path[:i + 1])
for f in follow:
follow_res, search = evaluate.goto(call.parent, f)
follow_res = usages_add_import_modules(follow_res, search)
compare_follow_res = compare_array(follow_res)
# compare to see if they match
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()
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):
try:
stmts = m.used_names[search_name]
except KeyError:
continue
for stmt in stmts:
if isinstance(stmt, pr.Import):
count = 0
imps = []
for i in stmt.get_all_import_names():
for name_part in i.names:
count += 1
if name_part == search_name:
imps.append((count, name_part))
for used_count, name_part in imps:
i = imports.ImportPath(stmt, kill_count=count - used_count,
direct_resolve=True)
f = i.follow(is_goto=True)
if set(f) & set(definitions):
names.append(api_classes.Usage(name_part, stmt))
else:
for call in _scan_statement(stmt, search_name,
assignment_details=True):
names += check_call(call)
return names
def usages_add_import_modules(definitions, search_name):
""" Adds the modules of the imports """
new = set()
for d in definitions:
if isinstance(d.parent, pr.Import):
s = imports.ImportPath(d.parent, direct_resolve=True)
with common.ignored(IndexError):
new.add(s.follow(is_goto=True)[0])
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
ensures that `k` is a string.
"""
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)
if result:
break
if isinstance(flow, pr.Flow) and not result:
if flow.command in ['if', 'while'] and len(flow.inputs) == 1:
result = check_statement_information(flow.inputs[0], search_name)
return result
def check_statement_information(stmt, search_name):
try:
commands = stmt.get_commands()
# this might be removed if we analyze and, etc
assert len(commands) == 1
call = commands[0]
assert type(call) == pr.Call and str(call.name) == 'isinstance'
assert bool(call.execution)
# isinstance check
isinst = call.execution.values
assert len(isinst) == 2 # has two params
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(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

View File

@@ -1,796 +0,0 @@
"""
Evaluation of Python code in |jedi| is based on three assumptions:
* Code is recursive (to weaken this assumption, the :mod:`dynamic` module
exists).
* No magic is being used:
- metaclasses
- ``setattr()`` / ``__import__()``
- writing to ``globals()``, ``locals()``, ``object.__dict__``
* The programmer is not a total dick, e.g. like `this
<https://github.com/davidhalter/jedi/issues/24>`_ :-)
That said, there's mainly one entry point in this script: ``follow_statement``.
This is where autocompletion starts. Everything you want to complete is either
a ``Statement`` or some special name like ``class``, which is easy to complete.
Therefore you need to understand what follows after ``follow_statement``. Let's
make an example::
import datetime
datetime.date.toda# <-- cursor here
First of all, this module doesn't care about completion. It really just cares
about ``datetime.date``. At the end of the procedure ``follow_statement`` will
return the ``datetime`` class.
To *visualize* this (simplified):
- ``follow_statement`` - ``<Statement: datetime.date>``
- Unpacking of the statement into ``[[<Call: datetime.date>]]``
- ``follow_call_list``, calls ``follow_call`` with ``<Call: datetime.date>``
- ``follow_call`` - searches the ``datetime`` name within the module.
This is exactly where it starts to get complicated. Now recursions start to
kick in. The statement has not been resolved fully, but now we need to resolve
the datetime import. So it continues
- follow import, which happens in the :mod:`imports` module.
- now the same ``follow_call`` as above calls ``follow_paths`` to follow the
second part of the statement ``date``.
- After ``follow_paths`` returns with the desired ``datetime.date`` class, the
result is being returned and the recursion finishes.
Now what would happen if we wanted ``datetime.date.foo.bar``? Just two more
calls to ``follow_paths`` (which calls itself with a recursion). What if the
import would contain another Statement like this::
from foo import bar
Date = bar.baz
Well... You get it. Just another ``follow_statement`` recursion. It's really
easy. Just that Python is not that easy sometimes. To understand tuple
assignments and different class scopes, a lot more code had to be written. Yet
we're still not talking about Descriptors and Nested List Comprehensions, just
the simple stuff.
So if you want to change something, write a test and then just change what you
want. This module has been tested by about 600 tests. Don't be afraid to break
something. The tests are good enough.
I need to mention now that this recursive approach is really good because it
only *evaluates* what needs to be *evaluated*. All the statements and modules
that are not used are just being ignored. It's a little bit similar to the
backtracking algorithm.
.. todo:: nonlocal statement, needed or can be ignored? (py3k)
"""
from __future__ import with_statement
import sys
import itertools
from jedi._compatibility import next, hasattr, is_py3k, unicode, reraise
from jedi import common
from jedi import cache
from jedi import parsing_representation as pr
from jedi import debug
import evaluate_representation as er
import recursion
import docstrings
import builtin
import imports
import dynamic
def get_defined_names_for_position(scope, position=None, start_scope=None):
"""
Return filtered version of ``scope.get_defined_names()``.
This function basically does what :meth:`scope.get_defined_names
<parsing_representation.Scope.get_defined_names>` does.
- If `position` is given, delete all names defined after `position`.
- For special objects like instances, `position` is ignored and all
names are returned.
:type scope: :class:`parsing_representation.IsScope`
:param scope: Scope in which names are searched.
:param position: the position as a line/column tuple, default is infinity.
"""
names = scope.get_defined_names()
# Instances have special rules, always return all the possible completions,
# because class variables are always valid and the `self.` variables, too.
if (not position or isinstance(scope, (er.Array, er.Instance))
or start_scope != scope
and isinstance(start_scope, (pr.Function, er.Execution))):
return names
names_new = []
for n in names:
if n.start_pos[0] is not None and n.start_pos < position:
names_new.append(n)
return names_new
def get_names_of_scope(scope, position=None, star_search=True,
include_builtin=True):
"""
Get all completions (names) possible for the current scope.
The star search option is only here to provide an optimization. Otherwise
the whole thing would probably start a little recursive madness.
This function is used to include names from outer scopes. For example,
when the current scope is function:
>>> from jedi.parsing import Parser
>>> parser = Parser('''
... x = ['a', 'b', 'c']
... def func():
... y = None
... ''')
>>> scope = parser.module.subscopes[0]
>>> scope
<Function: func@3-5>
`get_names_of_scope` is a generator. First it yields names from
most inner scope.
>>> pairs = list(get_names_of_scope(scope))
>>> pairs[0]
(<Function: func@3-5>, [<Name: y@4,4>])
Then it yield the names from one level outer scope. For this
example, this is the most outer scope.
>>> pairs[1]
(<SubModule: None@1-5>, [<Name: x@2,0>, <Name: func@3,4>])
Finally, it yields names from builtin, if `include_builtin` is
true (default).
>>> pairs[2] #doctest: +ELLIPSIS
(<Module: ...builtin...>, [<Name: ...>, ...])
:rtype: [(pr.Scope, [pr.Name])]
:return: Return an generator that yields a pair of scope and names.
"""
in_func_scope = scope
non_flow = scope.get_parent_until(pr.Flow, reverse=True)
while scope:
if isinstance(scope, pr.SubModule) and scope.parent:
# we don't want submodules to report if we have modules.
scope = scope.parent
continue
# `pr.Class` is used, because the parent is never `Class`.
# Ignore the Flows, because the classes and functions care for that.
# InstanceElement of Class is ignored, if it is not the start scope.
if not (scope != non_flow and scope.isinstance(pr.Class)
or scope.isinstance(pr.Flow)
or scope.isinstance(er.Instance)
and non_flow.isinstance(er.Function)
):
try:
if isinstance(scope, er.Instance):
for g in scope.scope_generator():
yield g
else:
yield scope, get_defined_names_for_position(scope,
position, in_func_scope)
except StopIteration:
reraise(common.MultiLevelStopIteration, sys.exc_info()[2])
if scope.isinstance(pr.ForFlow) and scope.is_list_comp:
# is a list comprehension
yield scope, scope.get_set_vars(is_internal_call=True)
scope = scope.parent
# This is used, because subscopes (Flow scopes) would distort the
# results.
if scope and scope.isinstance(er.Function, pr.Function, er.Execution):
in_func_scope = scope
# Add star imports.
if star_search:
for s in imports.remove_star_imports(non_flow.get_parent_until()):
for g in get_names_of_scope(s, star_search=False):
yield g
# Add builtins to the global scope.
if include_builtin:
builtin_scope = builtin.Builtin.scope
yield builtin_scope, builtin_scope.get_defined_names()
def find_name(scope, name_str, position=None, search_global=False,
is_goto=False):
"""
This is the search function. The most important part to debug.
`remove_statements` and `filter_statements` really are the core part of
this completion.
:param position: Position of the last statement -> tuple of line, column
:return: List of Names. Their parents are the scopes, they are defined in.
:rtype: list
"""
def remove_statements(result):
"""
This is the part where statements are being stripped.
Due to lazy evaluation, statements like a = func; b = a; b() have to be
evaluated.
"""
res_new = []
for r in result:
add = []
if r.isinstance(pr.Statement):
check_instance = None
if isinstance(r, er.InstanceElement) and r.is_class_var:
check_instance = r.instance
r = r.var
# Global variables handling.
if r.is_global():
for token_name in r.token_list[1:]:
if isinstance(token_name, pr.Name):
add = find_name(r.parent, str(token_name))
else:
# generated objects are used within executions, but these
# objects are in functions, and we have to dynamically
# execute first.
if isinstance(r, pr.Param):
func = r.parent
# Instances are typically faked, if the instance is not
# called from outside. Here we check it for __init__
# functions and return.
if isinstance(func, er.InstanceElement) \
and func.instance.is_generated \
and hasattr(func, 'name') \
and str(func.name) == '__init__' \
and r.position_nr > 0: # 0 would be self
r = func.var.params[r.position_nr]
# add docstring knowledge
doc_params = docstrings.follow_param(r)
if doc_params:
res_new += doc_params
continue
if not r.is_generated:
res_new += dynamic.search_params(r)
if not r.assignment_details:
# this means that there are no default params,
# so just ignore it.
continue
if r.docstr:
res_new.append(r)
scopes = follow_statement(r, seek_name=name_str)
add += remove_statements(scopes)
if check_instance is not None:
# class renames
add = [er.InstanceElement(check_instance, a, True)
if isinstance(a, (er.Function, pr.Function))
else a for a in add]
res_new += add
else:
if isinstance(r, pr.Class):
r = er.Class(r)
elif isinstance(r, pr.Function):
r = er.Function(r)
if r.isinstance(er.Function):
try:
r = r.get_decorated_func()
except er.DecoratorNotFound:
continue
res_new.append(r)
debug.dbg('sfn remove, new: %s, old: %s' % (res_new, result))
return res_new
def filter_name(scope_generator):
"""
Filters all variables of a scope (which are defined in the
`scope_generator`), until the name fits.
"""
def handle_for_loops(loop):
# Take the first statement (for has always only
# one, remember `in`). And follow it.
if not loop.inputs:
return []
result = get_iterator_types(follow_statement(loop.inputs[0]))
if len(loop.set_vars) > 1:
commands = loop.set_stmt.get_commands()
# loops with loop.set_vars > 0 only have one command
result = assign_tuples(commands[0], result, name_str)
return result
def process(name):
"""
Returns the parent of a name, which means the element which stands
behind a name.
"""
result = []
no_break_scope = False
par = name.parent
exc = pr.Class, pr.Function
until = lambda: par.parent.parent.get_parent_until(exc)
if par.isinstance(pr.Flow):
if par.command == 'for':
result += handle_for_loops(par)
else:
debug.warning('Flow: Why are you here? %s' % par.command)
elif par.isinstance(pr.Param) \
and par.parent is not None \
and isinstance(until(), pr.Class) \
and par.position_nr == 0:
# This is where self gets added - this happens at another
# place, if the var_args are clear. But sometimes the class is
# not known. Therefore add a new instance for self. Otherwise
# take the existing.
if isinstance(scope, er.InstanceElement):
inst = scope.instance
else:
inst = er.Instance(er.Class(until()))
inst.is_generated = True
result.append(inst)
elif par.isinstance(pr.Statement):
def is_execution(calls):
for c in calls:
if c.isinstance(pr.Array):
if is_execution(c):
return True
elif c.isinstance(pr.Call):
# Compare start_pos, because names may be different
# because of executions.
if c.name.start_pos == name.start_pos \
and c.execution:
return True
return False
is_exe = False
for assignee, op in par.assignment_details:
is_exe |= is_execution(assignee)
if is_exe:
# filter array[3] = ...
# TODO check executions for dict contents
pass
else:
details = par.assignment_details
if details and details[0][1] != '=':
no_break_scope = True
# TODO this makes self variables non-breakable. wanted?
if isinstance(name, er.InstanceElement) \
and not name.is_class_var:
no_break_scope = True
result.append(par)
else:
result.append(par)
return result, no_break_scope
flow_scope = scope
result = []
# compare func uses the tuple of line/indent = line/column
comparison_func = lambda name: (name.start_pos)
for nscope, name_list in scope_generator:
break_scopes = []
# here is the position stuff happening (sorting of variables)
for name in sorted(name_list, key=comparison_func, reverse=True):
p = name.parent.parent if name.parent else None
if isinstance(p, er.InstanceElement) \
and isinstance(p.var, pr.Class):
p = p.var
if name_str == name.get_code() and p not in break_scopes:
r, no_break_scope = process(name)
if is_goto:
if r:
# Directly assign the name, but there has to be a
# result.
result.append(name)
else:
result += r
# for comparison we need the raw class
s = nscope.base if isinstance(nscope, er.Class) else nscope
# this means that a definition was found and is not e.g.
# in if/else.
if result and not no_break_scope:
if not name.parent or p == s:
break
break_scopes.append(p)
while flow_scope:
# TODO check if result is in scope -> no evaluation necessary
n = dynamic.check_flow_information(flow_scope, name_str,
position)
if n:
result = n
break
if result:
break
if flow_scope == nscope:
break
flow_scope = flow_scope.parent
flow_scope = nscope
if result:
break
if not result and isinstance(nscope, er.Instance):
# __getattr__ / __getattribute__
result += check_getattr(nscope, name_str)
debug.dbg('sfn filter "%s" in (%s-%s): %s@%s' % (name_str, scope,
nscope, result, position))
return result
def descriptor_check(result):
"""Processes descriptors"""
res_new = []
for r in result:
if isinstance(scope, (er.Instance, er.Class)) \
and hasattr(r, 'get_descriptor_return'):
# handle descriptors
with common.ignored(KeyError):
res_new += r.get_descriptor_return(scope)
continue
res_new.append(r)
return res_new
if search_global:
scope_generator = get_names_of_scope(scope, position=position)
else:
if isinstance(scope, er.Instance):
scope_generator = scope.scope_generator()
else:
if isinstance(scope, (er.Class, pr.Module)):
# classes are only available directly via chaining?
# strange stuff...
names = scope.get_defined_names()
else:
names = get_defined_names_for_position(scope, position)
scope_generator = iter([(scope, names)])
if is_goto:
return filter_name(scope_generator)
return descriptor_check(remove_statements(filter_name(scope_generator)))
def check_getattr(inst, name_str):
"""Checks for both __getattr__ and __getattribute__ methods"""
result = []
# str is important to lose the NamePart!
module = builtin.Builtin.scope
name = pr.Call(module, str(name_str), pr.Call.STRING, (0, 0), inst)
with common.ignored(KeyError):
result = inst.execute_subscope_by_name('__getattr__', [name])
if not result:
# this is a little bit special. `__getattribute__` is executed
# before anything else. But: I know no use case, where this
# could be practical and the jedi would return wrong types. If
# you ever have something, let me know!
with common.ignored(KeyError):
result = inst.execute_subscope_by_name('__getattribute__', [name])
return result
def get_iterator_types(inputs):
"""Returns the types of any iterator (arrays, yields, __iter__, etc)."""
iterators = []
# Take the first statement (for has always only
# one, remember `in`). And follow it.
for it in inputs:
if isinstance(it, (er.Generator, er.Array, dynamic.ArrayInstance)):
iterators.append(it)
else:
if not hasattr(it, 'execute_subscope_by_name'):
debug.warning('iterator/for loop input wrong', it)
continue
try:
iterators += it.execute_subscope_by_name('__iter__')
except KeyError:
debug.warning('iterators: No __iter__ method found.')
result = []
for gen in iterators:
if isinstance(gen, er.Array):
# Array is a little bit special, since this is an internal
# array, but there's also the list builtin, which is
# another thing.
result += gen.get_index_types()
elif isinstance(gen, er.Instance):
# __iter__ returned an instance.
name = '__next__' if is_py3k else 'next'
try:
result += gen.execute_subscope_by_name(name)
except KeyError:
debug.warning('Instance has no __next__ function', gen)
else:
# is a generator
result += gen.iter_content()
return result
def assign_tuples(tup, results, seek_name):
"""
This is a normal assignment checker. In python functions and other things
can return tuples:
>>> a, b = 1, ""
>>> a, (b, c) = 1, ("", 1.0)
Here, if `seek_name` is "a", the number type will be returned.
The first part (before `=`) is the param tuples, the second one result.
:type tup: pr.Array
"""
def eval_results(index):
types = []
for r in results:
try:
func = r.get_exact_index_types
except AttributeError:
debug.warning("invalid tuple lookup %s of result %s in %s"
% (tup, results, seek_name))
else:
with common.ignored(IndexError):
types += func(index)
return types
result = []
for i, stmt in enumerate(tup):
# Used in assignments. There is just one call and no other things,
# therefore we can just assume, that the first part is important.
command = stmt.get_commands()[0]
if tup.type == pr.Array.NOARRAY:
# unnessecary braces -> just remove.
r = results
else:
r = eval_results(i)
# are there still tuples or is it just a Call.
if isinstance(command, pr.Array):
# These are "sub"-tuples.
result += assign_tuples(command, r, seek_name)
else:
if command.name.names[-1] == seek_name:
result += r
return result
@recursion.RecursionDecorator
@cache.memoize_default(default=())
def follow_statement(stmt, seek_name=None):
"""
The starting point of the completion. A statement always owns a call list,
which are the calls, that a statement does.
In case multiple names are defined in the statement, `seek_name` returns
the result for this name.
:param stmt: A `pr.Statement`.
:param seek_name: A string.
"""
debug.dbg('follow_stmt %s (%s)' % (stmt, seek_name))
commands = stmt.get_commands()
debug.dbg('calls: %s' % commands)
result = follow_call_list(commands)
# Assignment checking is only important if the statement defines multiple
# variables.
if len(stmt.get_set_vars()) > 1 and seek_name and stmt.assignment_details:
new_result = []
for ass_commands, op in stmt.assignment_details:
new_result += assign_tuples(ass_commands[0], result, seek_name)
result = new_result
return set(result)
@common.rethrow_uncaught
def follow_call_list(call_list, follow_array=False):
"""
`call_list` can be either `pr.Array` or `list of list`.
It is used to evaluate a two dimensional object, that has calls, arrays and
operators in it.
"""
def evaluate_list_comprehension(lc, parent=None):
input = lc.input
nested_lc = lc.input.token_list[0]
if isinstance(nested_lc, pr.ListComprehension):
# is nested LC
input = nested_lc.stmt
module = input.get_parent_until()
# create a for loop, which does the same as list comprehensions
loop = pr.ForFlow(module, [input], lc.stmt.start_pos, lc.middle, True)
loop.parent = parent or lc.get_parent_until(pr.IsScope)
if isinstance(nested_lc, pr.ListComprehension):
loop = evaluate_list_comprehension(nested_lc, loop)
return loop
result = []
calls_iterator = iter(call_list)
for call in calls_iterator:
if pr.Array.is_type(call, pr.Array.NOARRAY):
r = list(itertools.chain.from_iterable(follow_statement(s)
for s in call))
call_path = call.generate_call_path()
next(call_path, None) # the first one has been used already
result += follow_paths(call_path, r, call.parent,
position=call.start_pos)
elif isinstance(call, pr.ListComprehension):
loop = evaluate_list_comprehension(call)
# Caveat: parents are being changed, but this doesn't matter,
# because nothing else uses it.
call.stmt.parent = loop
result += follow_statement(call.stmt)
else:
if isinstance(call, pr.Lambda):
result.append(er.Function(call))
# With things like params, these can also be functions...
elif isinstance(call, (er.Function, er.Class, er.Instance,
dynamic.ArrayInstance)):
result.append(call)
# The string tokens are just operations (+, -, etc.)
elif not isinstance(call, (str, unicode)):
if str(call.name) == 'if':
# Ternary operators.
while True:
try:
call = next(calls_iterator)
except StopIteration:
break
with common.ignored(AttributeError):
if str(call.name) == 'else':
break
continue
result += follow_call(call)
elif call == '*':
if [r for r in result if isinstance(r, er.Array)
or isinstance(r, er.Instance)
and str(r.name) == 'str']:
# if it is an iterable, ignore * operations
next(calls_iterator)
return set(result)
def follow_call(call):
"""Follow a call is following a function, variable, string, etc."""
path = call.generate_call_path()
# find the statement of the Scope
s = call
while not s.parent.isinstance(pr.IsScope):
s = s.parent
return follow_call_path(path, s.parent, s.start_pos)
def follow_call_path(path, scope, position):
"""Follows a path generated by `pr.Call.generate_call_path()`"""
current = next(path)
if isinstance(current, pr.Array):
result = [er.Array(current)]
else:
if isinstance(current, pr.NamePart):
# This is the first global lookup.
scopes = find_name(scope, current, position=position,
search_global=True)
else:
if current.type in (pr.Call.STRING, pr.Call.NUMBER):
t = type(current.name).__name__
scopes = find_name(builtin.Builtin.scope, t)
else:
debug.warning('unknown type:', current.type, current)
scopes = []
# Make instances of those number/string objects.
scopes = [er.Instance(s, (current.name,)) for s in scopes]
result = imports.strip_imports(scopes)
return follow_paths(path, result, scope, position=position)
def follow_paths(path, results, call_scope, position=None):
"""
In each result, `path` must be followed. Copies the path iterator.
"""
results_new = []
if results:
if len(results) > 1:
iter_paths = itertools.tee(path, len(results))
else:
iter_paths = [path]
for i, r in enumerate(results):
fp = follow_path(iter_paths[i], r, call_scope, position=position)
if fp is not None:
results_new += fp
else:
# This means stop iteration.
return results
return results_new
def follow_path(path, scope, call_scope, position=None):
"""
Uses a generator and tries to complete the path, e.g.::
foo.bar.baz
`follow_path` is only responsible for completing `.bar.baz`, the rest is
done in the `follow_call` function.
"""
# current is either an Array or a Scope.
try:
current = next(path)
except StopIteration:
return None
debug.dbg('follow %s in scope %s' % (current, scope))
result = []
if isinstance(current, pr.Array):
# This must be an execution, either () or [].
if current.type == pr.Array.LIST:
if hasattr(scope, 'get_index_types'):
result = scope.get_index_types(current)
elif current.type not in [pr.Array.DICT]:
# Scope must be a class or func - make an instance or execution.
debug.dbg('exe', scope)
result = er.Execution(scope, current).get_return_types()
else:
# Curly braces are not allowed, because they make no sense.
debug.warning('strange function call with {}', current, scope)
else:
# The function must not be decorated with something else.
if scope.isinstance(er.Function):
scope = scope.get_magic_method_scope()
else:
# This is the typical lookup while chaining things.
if filter_private_variable(scope, call_scope, current):
return []
result = imports.strip_imports(find_name(scope, current,
position=position))
return follow_paths(path, set(result), call_scope, position=position)
def filter_private_variable(scope, call_scope, var_name):
"""private variables begin with a double underline `__`"""
if isinstance(var_name, (str, unicode)) \
and var_name.startswith('__') and isinstance(scope, er.Instance):
s = call_scope.get_parent_until((pr.Class, er.Instance))
if s != scope and s != scope.base.base:
return True
return False
def goto(stmt, call_path=None):
if call_path is None:
commands = stmt.get_commands()
assert len(commands) == 1
call = commands[0]
call_path = list(call.generate_call_path())
scope = stmt.get_parent_until(pr.IsScope)
pos = stmt.start_pos
call_path, search = call_path[:-1], call_path[-1]
pos = pos[0], pos[1] + 1
if call_path:
scopes = follow_call_path(iter(call_path), scope, pos)
search_global = False
pos = None
else:
scopes = [scope]
search_global = True
follow_res = []
for s in scopes:
follow_res += find_name(s, search, pos,
search_global=search_global, is_goto=True)
return follow_res, search

379
jedi/evaluate/__init__.py Normal file
View File

@@ -0,0 +1,379 @@
"""
Evaluation of Python code in |jedi| is based on three assumptions:
* The code uses as least side effects as possible. Jedi understands certain
list/tuple/set modifications, but there's no guarantee that Jedi detects
everything (list.append in different modules for example).
* No magic is being used:
- metaclasses
- ``setattr()`` / ``__import__()``
- writing to ``globals()``, ``locals()``, ``object.__dict__``
* The programmer is not a total dick, e.g. like `this
<https://github.com/davidhalter/jedi/issues/24>`_ :-)
The actual algorithm is based on a principle called lazy evaluation. If you
don't know about it, google it. That said, the typical entry point for static
analysis is calling ``eval_statement``. There's separate logic for
autocompletion in the API, the evaluator is all about evaluating an expression.
Now you need to understand what follows after ``eval_statement``. Let's
make an example::
import datetime
datetime.date.toda# <-- cursor here
First of all, this module doesn't care about completion. It really just cares
about ``datetime.date``. At the end of the procedure ``eval_statement`` will
return the ``date`` class.
To *visualize* this (simplified):
- ``Evaluator.eval_statement`` doesn't do much, because there's no assignment.
- ``Evaluator.eval_element`` cares for resolving the dotted path
- ``Evaluator.find_types`` searches for global definitions of datetime, which
it finds in the definition of an import, by scanning the syntax tree.
- Using the import logic, the datetime module is found.
- Now ``find_types`` is called again by ``eval_element`` to find ``date``
inside the datetime module.
Now what would happen if we wanted ``datetime.date.foo.bar``? Two more
calls to ``find_types``. However the second call would be ignored, because the
first one would return nothing (there's no foo attribute in ``date``).
What if the import would contain another ``ExprStmt`` like this::
from foo import bar
Date = bar.baz
Well... You get it. Just another ``eval_statement`` recursion. It's really
easy. Python can obviously get way more complicated then this. To understand
tuple assignments, list comprehensions and everything else, a lot more code had
to be written.
Jedi has been tested very well, so you can just start modifying code. It's best
to write your own test first for your "new" feature. Don't be scared of
breaking stuff. As long as the tests pass, you're most likely to be fine.
I need to mention now that lazy evaluation is really good because it
only *evaluates* what needs to be *evaluated*. All the statements and modules
that are not used are just being ignored.
"""
import copy
from itertools import chain
from jedi.parser import tree
from jedi import debug
from jedi.evaluate import representation as er
from jedi.evaluate import imports
from jedi.evaluate import recursion
from jedi.evaluate import iterable
from jedi.evaluate.cache import memoize_default
from jedi.evaluate import stdlib
from jedi.evaluate import finder
from jedi.evaluate import compiled
from jedi.evaluate import precedence
from jedi.evaluate import param
from jedi.evaluate import helpers
class Evaluator(object):
def __init__(self, grammar):
self.grammar = grammar
self.memoize_cache = {} # for memoize decorators
# To memorize modules -> equals `sys.modules`.
self.modules = {} # like `sys.modules`.
self.compiled_cache = {} # see `compiled.create()`
self.recursion_detector = recursion.RecursionDetector()
self.execution_recursion_detector = recursion.ExecutionRecursionDetector()
self.analysis = []
def wrap(self, element):
if isinstance(element, tree.Class):
return er.Class(self, element)
elif isinstance(element, tree.Function):
if isinstance(element, tree.Lambda):
return er.LambdaWrapper(self, element)
else:
return er.Function(self, element)
elif isinstance(element, (tree.Module)) \
and not isinstance(element, er.ModuleWrapper):
return er.ModuleWrapper(self, element)
else:
return element
def find_types(self, scope, name_str, position=None, search_global=False,
is_goto=False):
"""
This is the search function. The most important part to debug.
`remove_statements` and `filter_statements` really are the core part of
this completion.
:param position: Position of the last statement -> tuple of line, column
:return: List of Names. Their parents are the types.
"""
f = finder.NameFinder(self, scope, name_str, position)
scopes = f.scopes(search_global)
if is_goto:
return f.filter_name(scopes)
return f.find(scopes, search_global)
@memoize_default(default=[], evaluator_is_first_arg=True)
@recursion.recursion_decorator
@debug.increase_indent
def eval_statement(self, stmt, seek_name=None):
"""
The starting point of the completion. A statement always owns a call
list, which are the calls, that a statement does. In case multiple
names are defined in the statement, `seek_name` returns the result for
this name.
:param stmt: A `tree.ExprStmt`.
"""
debug.dbg('eval_statement %s (%s)', stmt, seek_name)
types = self.eval_element(stmt.get_rhs())
if seek_name:
types = finder.check_tuple_assignments(types, seek_name)
first_operation = stmt.first_operation()
if first_operation not in ('=', None) and not isinstance(stmt, er.InstanceElement): # TODO don't check for this.
# `=` is always the last character in aug assignments -> -1
operator = copy.copy(first_operation)
operator.value = operator.value[:-1]
name = str(stmt.get_defined_names()[0])
parent = self.wrap(stmt.get_parent_scope())
left = self.find_types(parent, name, stmt.start_pos, search_global=True)
if isinstance(stmt.get_parent_until(tree.ForStmt), tree.ForStmt):
# Iterate through result and add the values, that's possible
# only in for loops without clutter, because they are
# predictable.
for r in types:
left = precedence.calculate(self, left, operator, [r])
types = left
else:
types = precedence.calculate(self, left, operator, types)
debug.dbg('eval_statement result %s', types)
return types
@memoize_default(evaluator_is_first_arg=True)
def eval_element(self, element):
if isinstance(element, iterable.AlreadyEvaluated):
return list(element)
elif isinstance(element, iterable.MergedNodes):
return iterable.unite(self.eval_element(e) for e in element)
debug.dbg('eval_element %s@%s', element, element.start_pos)
if isinstance(element, (tree.Name, tree.Literal)) or tree.is_node(element, 'atom'):
return self._eval_atom(element)
elif isinstance(element, tree.Keyword):
# For False/True/None
if element.value in ('False', 'True', 'None'):
return [compiled.builtin.get_by_name(element.value)]
else:
return []
elif element.isinstance(tree.Lambda):
return [er.LambdaWrapper(self, element)]
elif element.isinstance(er.LambdaWrapper):
return [element] # TODO this is no real evaluation.
elif element.type == 'expr_stmt':
return self.eval_statement(element)
elif element.type == 'power':
types = self._eval_atom(element.children[0])
for trailer in element.children[1:]:
if trailer == '**': # has a power operation.
raise NotImplementedError
types = self.eval_trailer(types, trailer)
return types
elif element.type in ('testlist_star_expr', 'testlist',):
# The implicit tuple in statements.
return [iterable.ImplicitTuple(self, element)]
elif element.type in ('not_test', 'factor'):
types = self.eval_element(element.children[-1])
for operator in element.children[:-1]:
types = list(precedence.factor_calculate(self, types, operator))
return types
elif element.type == 'test':
# `x if foo else y` case.
return (self.eval_element(element.children[0]) +
self.eval_element(element.children[-1]))
elif element.type == 'operator':
# Must be an ellipsis, other operators are not evaluated.
return [] # Ignore for now.
elif element.type == 'dotted_name':
types = self._eval_atom(element.children[0])
for next_name in element.children[2::2]:
types = list(chain.from_iterable(self.find_types(typ, next_name)
for typ in types))
return types
else:
return precedence.calculate_children(self, element.children)
def _eval_atom(self, atom):
"""
Basically to process ``atom`` nodes. The parser sometimes doesn't
generate the node (because it has just one child). In that case an atom
might be a name or a literal as well.
"""
if isinstance(atom, tree.Name):
# This is the first global lookup.
stmt = atom.get_definition()
scope = stmt.get_parent_until(tree.IsScope, include_current=True)
if isinstance(stmt, tree.CompFor):
stmt = stmt.get_parent_until((tree.ClassOrFunc, tree.ExprStmt))
if stmt.type != 'expr_stmt':
# We only need to adjust the start_pos for statements, because
# there the name cannot be used.
stmt = atom
return self.find_types(scope, atom, stmt.start_pos, search_global=True)
elif isinstance(atom, tree.Literal):
return [compiled.create(self, atom.eval())]
else:
c = atom.children
# Parentheses without commas are not tuples.
if c[0] == '(' and not len(c) == 2 \
and not(tree.is_node(c[1], 'testlist_comp')
and len(c[1].children) > 1):
return self.eval_element(c[1])
try:
comp_for = c[1].children[1]
except (IndexError, AttributeError):
pass
else:
if isinstance(comp_for, tree.CompFor):
return [iterable.Comprehension.from_atom(self, atom)]
return [iterable.Array(self, atom)]
def eval_trailer(self, types, trailer):
trailer_op, node = trailer.children[:2]
if node == ')': # `arglist` is optional.
node = ()
new_types = []
for typ in types:
debug.dbg('eval_trailer: %s in scope %s', trailer, typ)
if trailer_op == '.':
new_types += self.find_types(typ, node)
elif trailer_op == '(':
new_types += self.execute(typ, node, trailer)
elif trailer_op == '[':
try:
get = typ.get_index_types
except AttributeError:
debug.warning("TypeError: '%s' object is not subscriptable"
% typ)
else:
new_types += get(self, node)
return new_types
def execute_evaluated(self, obj, *args):
"""
Execute a function with already executed arguments.
"""
args = [iterable.AlreadyEvaluated([arg]) for arg in args]
return self.execute(obj, args)
@debug.increase_indent
def execute(self, obj, arguments=(), trailer=None):
if not isinstance(arguments, param.Arguments):
arguments = param.Arguments(self, arguments, trailer)
if obj.isinstance(er.Function):
obj = obj.get_decorated_func()
debug.dbg('execute: %s %s', obj, arguments)
try:
# Some stdlib functions like super(), namedtuple(), etc. have been
# hard-coded in Jedi to support them.
return stdlib.execute(self, obj, arguments)
except stdlib.NotInStdLib:
pass
try:
func = obj.py__call__
except AttributeError:
debug.warning("no execution possible %s", obj)
return []
else:
types = func(self, arguments)
debug.dbg('execute result: %s in %s', types, obj)
return types
def goto_definition(self, name):
def_ = name.get_definition()
if def_.type == 'expr_stmt' and name in def_.get_defined_names():
return self.eval_statement(def_, name)
call = helpers.call_of_name(name)
return self.eval_element(call)
def goto(self, name):
def resolve_implicit_imports(names):
for name in names:
if isinstance(name.parent, helpers.FakeImport):
# Those are implicit imports.
s = imports.ImportWrapper(self, name)
for n in s.follow(is_goto=True):
yield n
else:
yield name
stmt = name.get_definition()
par = name.parent
if par.type == 'argument' and par.children[1] == '=' and par.children[0] == name:
# Named param goto.
trailer = par.parent
if trailer.type == 'arglist':
trailer = trailer.parent
if trailer.type != 'classdef':
if trailer.type == 'decorator':
types = self.eval_element(trailer.children[1])
else:
i = trailer.parent.children.index(trailer)
to_evaluate = trailer.parent.children[:i]
types = self.eval_element(to_evaluate[0])
for trailer in to_evaluate[1:]:
types = self.eval_trailer(types, trailer)
param_names = []
for typ in types:
try:
params = typ.params
except AttributeError:
pass
else:
param_names += [param.name for param in params
if param.name.value == name.value]
return param_names
elif isinstance(par, tree.ExprStmt) and name in par.get_defined_names():
# Only take the parent, because if it's more complicated than just
# a name it's something you can "goto" again.
return [name]
elif isinstance(par, (tree.Param, tree.Function, tree.Class)) and par.name is name:
return [name]
elif isinstance(stmt, tree.Import):
modules = imports.ImportWrapper(self, name).follow(is_goto=True)
return list(resolve_implicit_imports(modules))
elif par.type == 'dotted_name': # Is a decorator.
index = par.children.index(name)
if index > 0:
new_dotted = helpers.deep_ast_copy(par)
new_dotted.children[index - 1:] = []
types = self.eval_element(new_dotted)
return resolve_implicit_imports(iterable.unite(
self.find_types(typ, name, is_goto=True) for typ in types
))
scope = name.get_parent_scope()
if tree.is_node(name.parent, 'trailer'):
call = helpers.call_of_name(name, cut_own_trailer=True)
types = self.eval_element(call)
return resolve_implicit_imports(iterable.unite(
self.find_types(typ, name, is_goto=True) for typ in types
))
else:
if stmt.type != 'expr_stmt':
# We only need to adjust the start_pos for statements, because
# there the name cannot be used.
stmt = name
return self.find_types(scope, name, stmt.start_pos,
search_global=True, is_goto=True)

302
jedi/evaluate/analysis.py Normal file
View File

@@ -0,0 +1,302 @@
"""
Module for statical analysis.
"""
from jedi import debug
from jedi.parser import tree
from jedi.evaluate.compiled import CompiledObject
CODES = {
'attribute-error': (1, AttributeError, 'Potential AttributeError.'),
'name-error': (2, NameError, 'Potential NameError.'),
'import-error': (3, ImportError, 'Potential ImportError.'),
'type-error-generator': (4, TypeError, "TypeError: 'generator' object is not subscriptable."),
'type-error-too-many-arguments': (5, TypeError, None),
'type-error-too-few-arguments': (6, TypeError, None),
'type-error-keyword-argument': (7, TypeError, None),
'type-error-multiple-values': (8, TypeError, None),
'type-error-star-star': (9, TypeError, None),
'type-error-star': (10, TypeError, None),
'type-error-operation': (11, TypeError, None),
}
class Error(object):
def __init__(self, name, module_path, start_pos, message=None):
self.path = module_path
self._start_pos = start_pos
self.name = name
if message is None:
message = CODES[self.name][2]
self.message = message
@property
def line(self):
return self._start_pos[0]
@property
def column(self):
return self._start_pos[1]
@property
def code(self):
# The class name start
first = self.__class__.__name__[0]
return first + str(CODES[self.name][0])
def __unicode__(self):
return '%s:%s:%s: %s %s' % (self.path, self.line, self.column,
self.code, self.message)
def __str__(self):
return self.__unicode__()
def __eq__(self, other):
return (self.path == other.path and self.name == other.name
and self._start_pos == other._start_pos)
def __ne__(self, other):
return not self.__eq__(other)
def __hash__(self):
return hash((self.path, self._start_pos, self.name))
def __repr__(self):
return '<%s %s: %s@%s,%s>' % (self.__class__.__name__,
self.name, self.path,
self._start_pos[0], self._start_pos[1])
class Warning(Error):
pass
def add(evaluator, name, jedi_obj, message=None, typ=Error, payload=None):
from jedi.evaluate.iterable import MergedNodes
while isinstance(jedi_obj, MergedNodes):
if len(jedi_obj) != 1:
# TODO is this kosher?
return
jedi_obj = list(jedi_obj)[0]
exception = CODES[name][1]
if _check_for_exception_catch(evaluator, jedi_obj, exception, payload):
return
module_path = jedi_obj.get_parent_until().path
instance = typ(name, module_path, jedi_obj.start_pos, message)
debug.warning(str(instance))
evaluator.analysis.append(instance)
def _check_for_setattr(instance):
"""
Check if there's any setattr method inside an instance. If so, return True.
"""
module = instance.get_parent_until()
try:
stmts = module.used_names['setattr']
except KeyError:
return False
return any(instance.start_pos < stmt.start_pos < instance.end_pos
for stmt in stmts)
def add_attribute_error(evaluator, scope, name):
message = ('AttributeError: %s has no attribute %s.' % (scope, name))
from jedi.evaluate.representation import Instance
# Check for __getattr__/__getattribute__ existance and issue a warning
# instead of an error, if that happens.
if isinstance(scope, Instance):
typ = Warning
try:
scope.get_subscope_by_name('__getattr__')
except KeyError:
try:
scope.get_subscope_by_name('__getattribute__')
except KeyError:
if not _check_for_setattr(scope):
typ = Error
else:
typ = Error
payload = scope, name
add(evaluator, 'attribute-error', name, message, typ, payload)
def _check_for_exception_catch(evaluator, jedi_obj, exception, payload=None):
"""
Checks if a jedi object (e.g. `Statement`) sits inside a try/catch and
doesn't count as an error (if equal to `exception`).
Also checks `hasattr` for AttributeErrors and uses the `payload` to compare
it.
Returns True if the exception was catched.
"""
def check_match(cls, exception):
try:
return isinstance(cls, CompiledObject) and issubclass(exception, cls.obj)
except TypeError:
return False
def check_try_for_except(obj, exception):
# Only nodes in try
iterator = iter(obj.children)
for branch_type in iterator:
colon = next(iterator)
suite = next(iterator)
if branch_type == 'try' \
and not (branch_type.start_pos < jedi_obj.start_pos <= suite.end_pos):
return False
for node in obj.except_clauses():
if node is None:
return True # An exception block that catches everything.
else:
except_classes = evaluator.eval_element(node)
for cls in except_classes:
from jedi.evaluate import iterable
if isinstance(cls, iterable.Array) and cls.type == 'tuple':
# multiple exceptions
for c in cls.values():
if check_match(c, exception):
return True
else:
if check_match(cls, exception):
return True
def check_hasattr(node, suite):
try:
assert suite.start_pos <= jedi_obj.start_pos < suite.end_pos
assert node.type == 'power'
base = node.children[0]
assert base.type == 'name' and base.value == 'hasattr'
trailer = node.children[1]
assert trailer.type == 'trailer'
arglist = trailer.children[1]
assert arglist.type == 'arglist'
from jedi.evaluate.param import Arguments
args = list(Arguments(evaluator, arglist).unpack())
# Arguments should be very simple
assert len(args) == 2
# Check name
key, values = args[1]
assert len(values) == 1
names = evaluator.eval_element(values[0])
assert len(names) == 1 and isinstance(names[0], CompiledObject)
assert names[0].obj == str(payload[1])
# Check objects
key, values = args[0]
assert len(values) == 1
objects = evaluator.eval_element(values[0])
return payload[0] in objects
except AssertionError:
return False
obj = jedi_obj
while obj is not None and not obj.isinstance(tree.Function, tree.Class):
if obj.isinstance(tree.Flow):
# try/except catch check
if obj.isinstance(tree.TryStmt) and check_try_for_except(obj, exception):
return True
# hasattr check
if exception == AttributeError and obj.isinstance(tree.IfStmt, tree.WhileStmt):
if check_hasattr(obj.children[1], obj.children[3]):
return True
obj = obj.parent
return False
def get_module_statements(module):
"""
Returns the statements used in a module. All these statements should be
evaluated to check for potential exceptions.
"""
def check_children(node):
try:
children = node.children
except AttributeError:
return []
else:
nodes = []
for child in children:
nodes += check_children(child)
if child.type == 'trailer':
c = child.children
if c[0] == '(' and c[1] != ')':
if c[1].type != 'arglist':
if c[1].type == 'argument':
nodes.append(c[1].children[-1])
else:
nodes.append(c[1])
else:
for argument in c[1].children:
if argument.type == 'argument':
nodes.append(argument.children[-1])
elif argument.type != 'operator':
nodes.append(argument)
return nodes
def add_nodes(nodes):
new = set()
for node in nodes:
if isinstance(node, tree.Flow):
children = node.children
if node.type == 'for_stmt':
children = children[2:] # Don't want to include the names.
# Pick the suite/simple_stmt.
new |= add_nodes(children)
elif node.type in ('simple_stmt', 'suite'):
new |= add_nodes(node.children)
elif node.type in ('return_stmt', 'yield_expr'):
try:
new.add(node.children[1])
except IndexError:
pass
elif node.type not in ('whitespace', 'operator', 'keyword',
'parameters', 'decorated', 'except_clause') \
and not isinstance(node, (tree.ClassOrFunc, tree.Import)):
new.add(node)
try:
children = node.children
except AttributeError:
pass
else:
for next_node in children:
new.update(check_children(node))
if next_node.type != 'keyword' and node.type != 'expr_stmt':
new.add(node)
return new
nodes = set()
import_names = set()
decorated_funcs = []
for scope in module.walk():
for imp in set(scope.imports):
import_names |= set(imp.get_defined_names())
if imp.is_nested():
import_names |= set(path[-1] for path in imp.paths())
children = scope.children
if isinstance(scope, tree.ClassOrFunc):
children = children[2:] # We don't want to include the class name.
nodes |= add_nodes(children)
for flow in scope.flows:
if flow.type == 'for_stmt':
nodes.add(flow.children[3])
elif flow.type == 'try_stmt':
nodes.update(e for e in flow.except_clauses() if e is not None)
try:
decorators = scope.get_decorators()
except AttributeError:
pass
else:
if decorators:
decorated_funcs.append(scope)
return nodes, import_names, decorated_funcs

58
jedi/evaluate/cache.py Normal file
View File

@@ -0,0 +1,58 @@
"""
- the popular ``memoize_default`` works like a typical memoize and returns the
default otherwise.
- ``CachedMetaClass`` uses ``memoize_default`` to do the same with classes.
"""
import inspect
NO_DEFAULT = object()
def memoize_default(default=NO_DEFAULT, evaluator_is_first_arg=False, second_arg_is_evaluator=False):
""" 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):
def wrapper(obj, *args, **kwargs):
if evaluator_is_first_arg:
cache = obj.memoize_cache
elif second_arg_is_evaluator: # needed for meta classes
cache = args[0].memoize_cache
else:
cache = obj._evaluator.memoize_cache
try:
memo = cache[function]
except KeyError:
memo = {}
cache[function] = memo
key = (obj, args, frozenset(kwargs.items()))
if key in memo:
return memo[key]
else:
if default is not NO_DEFAULT:
memo[key] = default
rv = function(obj, *args, **kwargs)
if inspect.isgenerator(rv):
rv = list(rv)
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. Either you do it this way or with decorators, but
with decorators you lose class access (isinstance, etc).
"""
@memoize_default(None, second_arg_is_evaluator=True)
def __call__(self, *args, **kwargs):
return super(CachedMetaClass, self).__call__(*args, **kwargs)

View File

@@ -0,0 +1,531 @@
"""
Imitate the parser representation.
"""
import inspect
import re
import sys
import os
from functools import partial
from jedi._compatibility import builtins as _builtins, unicode
from jedi import debug
from jedi.cache import underscore_memoization, memoize_method
from jedi.evaluate.sys_path import get_sys_path
from jedi.parser.tree import Param, Base, Operator, zero_position_modifier
from jedi.evaluate.helpers import FakeName
from . import fake
_sep = os.path.sep
if os.path.altsep is not None:
_sep += os.path.altsep
_path_re = re.compile('(?:\.[^{0}]+|[{0}]__init__\.py)$'.format(re.escape(_sep)))
del _sep
class CheckAttribute(object):
"""Raises an AttributeError if the attribute X isn't available."""
def __init__(self, func):
self.func = func
# Remove the py in front of e.g. py__call__.
self.check_name = func.__name__[2:]
def __get__(self, instance, owner):
# This might raise an AttributeError. That's wanted.
getattr(instance.obj, self.check_name)
return partial(self.func, instance)
class CompiledObject(Base):
# comply with the parser
start_pos = 0, 0
path = None # modules have this attribute - set it to None.
used_names = {} # To be consistent with modules.
def __init__(self, obj, parent=None):
self.obj = obj
self.parent = parent
@property
def py__call__(self):
def actual(evaluator, params):
if inspect.isclass(self.obj):
from jedi.evaluate.representation import Instance
return [Instance(evaluator, self, params)]
else:
return list(self._execute_function(evaluator, params))
# Might raise an AttributeError, which is intentional.
self.obj.__call__
return actual
@CheckAttribute
def py__class__(self, evaluator):
return CompiledObject(self.obj.__class__, parent=self.parent)
@CheckAttribute
def py__mro__(self, evaluator):
return tuple(create(evaluator, cls, self.parent) for cls in self.obj.__mro__)
@CheckAttribute
def py__bases__(self, evaluator):
return tuple(create(evaluator, cls) for cls in self.obj.__bases__)
def py__bool__(self):
return bool(self.obj)
def py__file__(self):
return self.obj.__file__
def is_class(self):
return inspect.isclass(self.obj)
@property
def doc(self):
return inspect.getdoc(self.obj) or ''
@property
def params(self):
params_str, ret = self._parse_function_doc()
tokens = params_str.split(',')
if inspect.ismethoddescriptor(self._cls().obj):
tokens.insert(0, 'self')
params = []
for p in tokens:
parts = [FakeName(part) for part in p.strip().split('=')]
if len(parts) > 1:
parts.insert(1, Operator(zero_position_modifier, '=', (0, 0)))
params.append(Param(parts, self))
return params
def __repr__(self):
return '<%s: %s>' % (type(self).__name__, repr(self.obj))
@underscore_memoization
def _parse_function_doc(self):
if self.doc is None:
return '', ''
return _parse_function_doc(self.doc)
def api_type(self):
if fake.is_class_instance(self.obj):
return 'instance'
cls = self._cls().obj
if inspect.isclass(cls):
return 'class'
elif inspect.ismodule(cls):
return 'module'
elif inspect.isbuiltin(cls) or inspect.ismethod(cls) \
or inspect.ismethoddescriptor(cls):
return 'function'
@property
def type(self):
"""Imitate the tree.Node.type values."""
cls = self._cls().obj
if inspect.isclass(cls):
return 'classdef'
elif inspect.ismodule(cls):
return 'file_input'
elif inspect.isbuiltin(cls) or inspect.ismethod(cls) \
or inspect.ismethoddescriptor(cls):
return 'funcdef'
@underscore_memoization
def _cls(self):
# Ensures that a CompiledObject is returned that is not an instance (like list)
if fake.is_class_instance(self.obj):
try:
c = self.obj.__class__
except AttributeError:
# happens with numpy.core.umath._UFUNC_API (you get it
# automatically by doing `import numpy`.
c = type(None)
return CompiledObject(c, self.parent)
return self
@property
def names_dict(self):
# For compatibility with `representation.Class`.
return self.names_dicts(False)[0]
def names_dicts(self, search_global, is_instance=False):
return self._names_dict_ensure_one_dict(is_instance)
@memoize_method
def _names_dict_ensure_one_dict(self, is_instance):
"""
search_global shouldn't change the fact that there's one dict, this way
there's only one `object`.
"""
return [LazyNamesDict(self._cls(), is_instance)]
def get_subscope_by_name(self, name):
if name in dir(self._cls().obj):
return CompiledName(self._cls(), name).parent
else:
raise KeyError("CompiledObject doesn't have an attribute '%s'." % name)
def get_index_types(self, evaluator, index_array=()):
# If the object doesn't have `__getitem__`, just raise the
# AttributeError.
if not hasattr(self.obj, '__getitem__'):
debug.warning('Tried to call __getitem__ on non-iterable.')
return []
if type(self.obj) not in (str, list, tuple, unicode, bytes, bytearray, dict):
# Get rid of side effects, we won't call custom `__getitem__`s.
return []
result = []
from jedi.evaluate.iterable import create_indexes_or_slices
for typ in create_indexes_or_slices(evaluator, index_array):
index = None
try:
index = typ.obj
new = self.obj[index]
except (KeyError, IndexError, TypeError, AttributeError):
# Just try, we don't care if it fails, except for slices.
if isinstance(index, slice):
result.append(self)
else:
result.append(CompiledObject(new))
if not result:
try:
for obj in self.obj:
result.append(CompiledObject(obj))
except TypeError:
pass # self.obj maynot have an __iter__ method.
return result
@property
def name(self):
# might not exist sometimes (raises AttributeError)
return FakeName(self._cls().obj.__name__, self)
def _execute_function(self, evaluator, params):
if self.type != 'funcdef':
return
for name in self._parse_function_doc()[1].split():
try:
bltn_obj = _create_from_name(builtin, builtin, name)
except AttributeError:
continue
else:
if isinstance(bltn_obj, CompiledObject) and bltn_obj.obj is None:
# We want everything except None.
continue
for result in evaluator.execute(bltn_obj, params):
yield result
@property
@underscore_memoization
def subscopes(self):
"""
Returns only the faked scopes - the other ones are not important for
internal analysis.
"""
module = self.get_parent_until()
faked_subscopes = []
for name in dir(self._cls().obj):
f = fake.get_faked(module.obj, self.obj, name)
if f:
f.parent = self
faked_subscopes.append(f)
return faked_subscopes
def is_scope(self):
return True
def get_self_attributes(self):
return [] # Instance compatibility
def get_imports(self):
return [] # Builtins don't have imports
class LazyNamesDict(object):
"""
A names_dict instance for compiled objects, resembles the parser.tree.
"""
def __init__(self, compiled_obj, is_instance):
self._compiled_obj = compiled_obj
self._is_instance = is_instance
def __iter__(self):
return (v[0].value for v in self.values())
@memoize_method
def __getitem__(self, name):
try:
getattr(self._compiled_obj.obj, name)
except AttributeError:
raise KeyError('%s in %s not found.' % (name, self._compiled_obj))
return [CompiledName(self._compiled_obj, name)]
def values(self):
obj = self._compiled_obj.obj
values = []
for name in dir(obj):
try:
values.append(self[name])
except KeyError:
# The dir function can be wrong.
pass
# dir doesn't include the type names.
if not inspect.ismodule(obj) and obj != type and not self._is_instance:
values += _type_names_dict.values()
return values
class CompiledName(FakeName):
def __init__(self, obj, name):
super(CompiledName, self).__init__(name)
self._obj = obj
self.name = name
def __repr__(self):
try:
name = self._obj.name # __name__ is not defined all the time
except AttributeError:
name = None
return '<%s: (%s).%s>' % (type(self).__name__, name, self.name)
def is_definition(self):
return True
@property
@underscore_memoization
def parent(self):
module = self._obj.get_parent_until()
return _create_from_name(module, self._obj, self.name)
@parent.setter
def parent(self, value):
pass # Just ignore this, FakeName tries to overwrite the parent attribute.
def dotted_from_fs_path(fs_path, sys_path=None):
"""
Changes `/usr/lib/python3.4/email/utils.py` to `email.utils`. I.e.
compares the path with sys.path and then returns the dotted_path. If the
path is not in the sys.path, just returns None.
"""
if sys_path is None:
sys_path = get_sys_path()
if os.path.basename(fs_path).startswith('__init__.'):
# We are calculating the path. __init__ files are not interesting.
fs_path = os.path.dirname(fs_path)
# prefer
# - UNIX
# /path/to/pythonX.Y/lib-dynload
# /path/to/pythonX.Y/site-packages
# - Windows
# C:\path\to\DLLs
# C:\path\to\Lib\site-packages
# over
# - UNIX
# /path/to/pythonX.Y
# - Windows
# C:\path\to\Lib
path = ''
for s in sys_path:
if (fs_path.startswith(s) and len(path) < len(s)):
path = s
return _path_re.sub('', fs_path[len(path):].lstrip(os.path.sep)).replace(os.path.sep, '.')
def load_module(path=None, name=None):
if path is not None:
dotted_path = dotted_from_fs_path(path)
else:
dotted_path = name
sys_path = get_sys_path()
if dotted_path is None:
p, _, dotted_path = path.partition(os.path.sep)
sys_path.insert(0, p)
temp, sys.path = sys.path, sys_path
try:
__import__(dotted_path)
except RuntimeError:
if 'PySide' in dotted_path or 'PyQt' in dotted_path:
# RuntimeError: the PyQt4.QtCore and PyQt5.QtCore modules both wrap
# the QObject class.
# See https://github.com/davidhalter/jedi/pull/483
return None
raise
except ImportError:
# If a module is "corrupt" or not really a Python module or whatever.
debug.warning('Module %s not importable.', path)
return None
finally:
sys.path = temp
# Just access the cache after import, because of #59 as well as the very
# complicated import structure of Python.
module = sys.modules[dotted_path]
return CompiledObject(module)
docstr_defaults = {
'floating point number': 'float',
'character': 'str',
'integer': 'int',
'dictionary': 'dict',
'string': 'str',
}
def _parse_function_doc(doc):
"""
Takes a function and returns the params and return value as a tuple.
This is nothing more than a docstring parser.
TODO docstrings like utime(path, (atime, mtime)) and a(b [, b]) -> None
TODO docstrings like 'tuple of integers'
"""
# parse round parentheses: def func(a, (b,c))
try:
count = 0
start = doc.index('(')
for i, s in enumerate(doc[start:]):
if s == '(':
count += 1
elif s == ')':
count -= 1
if count == 0:
end = start + i
break
param_str = doc[start + 1:end]
except (ValueError, UnboundLocalError):
# ValueError for doc.index
# UnboundLocalError for undefined end in last line
debug.dbg('no brackets found - no param')
end = 0
param_str = ''
else:
# remove square brackets, that show an optional param ( = None)
def change_options(m):
args = m.group(1).split(',')
for i, a in enumerate(args):
if a and '=' not in a:
args[i] += '=None'
return ','.join(args)
while True:
param_str, changes = re.subn(r' ?\[([^\[\]]+)\]',
change_options, param_str)
if changes == 0:
break
param_str = param_str.replace('-', '_') # see: isinstance.__doc__
# parse return value
r = re.search('-[>-]* ', doc[end:end + 7])
if r is None:
ret = ''
else:
index = end + r.end()
# get result type, which can contain newlines
pattern = re.compile(r'(,\n|[^\n-])+')
ret_str = pattern.match(doc, index).group(0).strip()
# New object -> object()
ret_str = re.sub(r'[nN]ew (.*)', r'\1()', ret_str)
ret = docstr_defaults.get(ret_str, ret_str)
return param_str, ret
class Builtin(CompiledObject):
@memoize_method
def get_by_name(self, name):
return self.names_dict[name][0].parent
def _a_generator(foo):
"""Used to have an object to return for generators."""
yield 42
yield foo
def _create_from_name(module, parent, name):
faked = fake.get_faked(module.obj, parent.obj, name)
# only functions are necessary.
if faked is not None:
faked.parent = parent
return faked
try:
obj = getattr(parent.obj, name)
except AttributeError:
# happens e.g. in properties of
# PyQt4.QtGui.QStyleOptionComboBox.currentText
# -> just set it to None
obj = None
return CompiledObject(obj, parent)
builtin = Builtin(_builtins)
magic_function_class = CompiledObject(type(load_module), parent=builtin)
generator_obj = CompiledObject(_a_generator(1.0))
_type_names_dict = builtin.get_by_name('type').names_dict
none_obj = builtin.get_by_name('None')
false_obj = builtin.get_by_name('False')
true_obj = builtin.get_by_name('True')
object_obj = builtin.get_by_name('object')
def keyword_from_value(obj):
if obj is None:
return none_obj
elif obj is False:
return false_obj
elif obj is True:
return true_obj
else:
raise NotImplementedError
def compiled_objects_cache(func):
def wrapper(evaluator, obj, parent=builtin, module=None):
# Do a very cheap form of caching here.
key = id(obj), id(parent), id(module)
try:
return evaluator.compiled_cache[key][0]
except KeyError:
result = func(evaluator, obj, parent, module)
# Need to cache all of them, otherwise the id could be overwritten.
evaluator.compiled_cache[key] = result, obj, parent, module
return result
return wrapper
@compiled_objects_cache
def create(evaluator, obj, parent=builtin, module=None):
"""
A very weird interface class to this module. The more options provided the
more acurate loading compiled objects is.
"""
if not inspect.ismodule(obj):
faked = fake.get_faked(module and module.obj, obj)
if faked is not None:
faked.parent = parent
return faked
try:
if parent == builtin and obj.__module__ in ('builtins', '__builtin__'):
return builtin.get_by_name(obj.__name__)
except AttributeError:
pass
return CompiledObject(obj, parent)

View File

@@ -0,0 +1,123 @@
"""
Loads functions that are mixed in to the standard library. E.g. builtins are
written in C (binaries), but my autocompletion only understands Python code. By
mixing in Python code, the autocompletion should work much better for builtins.
"""
import os
import inspect
from jedi._compatibility import is_py3, builtins, unicode
from jedi.parser import Parser, load_grammar
from jedi.parser import tree as pt
from jedi.evaluate.helpers import FakeName
modules = {}
def _load_faked_module(module):
module_name = module.__name__
if module_name == '__builtin__' and not is_py3:
module_name = 'builtins'
try:
return modules[module_name]
except KeyError:
path = os.path.dirname(os.path.abspath(__file__))
try:
with open(os.path.join(path, 'fake', module_name) + '.pym') as f:
source = f.read()
except IOError:
modules[module_name] = None
return
grammar = load_grammar('grammar3.4')
module = Parser(grammar, unicode(source), module_name).module
modules[module_name] = module
if module_name == 'builtins' and not is_py3:
# There are two implementations of `open` for either python 2/3.
# -> Rename the python2 version (`look at fake/builtins.pym`).
open_func = search_scope(module, 'open')
open_func.children[1] = FakeName('open_python3')
open_func = search_scope(module, 'open_python2')
open_func.children[1] = FakeName('open')
return module
def search_scope(scope, obj_name):
for s in scope.subscopes:
if str(s.name) == obj_name:
return s
def get_module(obj):
if inspect.ismodule(obj):
return obj
try:
obj = obj.__objclass__
except AttributeError:
pass
try:
imp_plz = obj.__module__
except AttributeError:
# Unfortunately in some cases like `int` there's no __module__
return builtins
else:
return __import__(imp_plz)
def _faked(module, obj, name):
# Crazy underscore actions to try to escape all the internal madness.
if module is None:
module = get_module(obj)
faked_mod = _load_faked_module(module)
if faked_mod is None:
return
# Having the module as a `parser.representation.module`, we need to scan
# for methods.
if name is None:
if inspect.isbuiltin(obj):
return search_scope(faked_mod, obj.__name__)
elif not inspect.isclass(obj):
# object is a method or descriptor
cls = search_scope(faked_mod, obj.__objclass__.__name__)
if cls is None:
return
return search_scope(cls, obj.__name__)
else:
if obj == module:
return search_scope(faked_mod, name)
else:
cls = search_scope(faked_mod, obj.__name__)
if cls is None:
return
return search_scope(cls, name)
def get_faked(module, obj, name=None):
obj = obj.__class__ if is_class_instance(obj) else obj
result = _faked(module, obj, name)
if result is None or isinstance(result, pt.Class):
# We're not interested in classes. What we want is functions.
return None
else:
# Set the docstr which was previously not set (faked modules don't
# contain it).
doc = '"""%s"""' % obj.__doc__ # TODO need escapes.
suite = result.children[-1]
string = pt.String(pt.zero_position_modifier, doc, (0, 0), '')
new_line = pt.Whitespace('\n', (0, 0), '')
docstr_node = pt.Node('simple_stmt', [string, new_line])
suite.children.insert(2, docstr_node)
return result
def is_class_instance(obj):
"""Like inspect.* methods."""
return not (inspect.isclass(obj) or inspect.ismodule(obj)
or inspect.isbuiltin(obj) or inspect.ismethod(obj)
or inspect.ismethoddescriptor(obj) or inspect.iscode(obj)
or inspect.isgenerator(obj))

View File

@@ -5,5 +5,5 @@ class partial():
self.__keywords = keywords
def __call__(self, *args, **kwargs):
# I know this doesn't work in Python, but Jedi can this ;-)
return self.__func(*self.__args, *args, **self.keywords, **kwargs)
# TODO should be **dict(self.__keywords, **kwargs)
return self.__func(*(self.__args + args), **self.__keywords)

View File

@@ -1,48 +1,48 @@
def compile():
class SRE_Match():
endpos = 1
lastgroup = 0
lastindex = 1
pos = 0
string = 'a'
regs = ((0, 1),)
endpos = int()
lastgroup = int()
lastindex = int()
pos = int()
string = str()
regs = ((int(), int()),)
def __init__(self, pattern):
self.re = pattern
def start(self):
return 0
return int()
def end(self):
return 1
return int()
def span(self):
return 0, 1
return int(), int()
def expand(self):
return ''
return str()
def group(self, nr):
return ''
return str()
def groupdict(self):
return {'a', 'a'}
return {str(): str()}
def groups(self):
return ('a',)
return (str(),)
class SRE_Pattern():
flags = 0
flags = int()
groupindex = {}
groups = 0
pattern = 'a'
groups = int()
pattern = str()
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']
return [str()]
def finditer(self, string, pos=None, endpos=None):
"""
@@ -77,7 +77,7 @@ def compile():
split(string[, maxsplit = 0]) --> list.
Split string by the occurrences of pattern.
"""
return ['a']
return [str()]
def sub(self, repl, string, count=0):
"""
@@ -85,7 +85,7 @@ def compile():
Return the string obtained by replacing the leftmost non-overlapping
occurrences of pattern in string by the replacement repl.
"""
return ''
return str()
def subn(self, repl, string, count=0):
"""
@@ -94,6 +94,6 @@ def compile():
the leftmost non-overlapping occurrences of pattern with the
replacement repl.
"""
return ('', 1)
return (str(), int())
return SRE_Pattern()

View File

@@ -1,7 +1,7 @@
def proxy(object, callback=None):
return object
class ref():
class weakref():
def __init__(self, object, callback=None):
self.__object = object
def __call__(self):

View File

@@ -7,11 +7,14 @@ possible for the auto completion.
def next(iterator, default=None):
if hasattr("next"):
return iterator.next()
if random.choice([0, 1]):
if hasattr("next"):
return iterator.next()
else:
return iterator.__next__()
else:
return iterator.__next__()
return default
if default is not None:
return default
def iter(collection, sentinel=None):
@@ -26,6 +29,13 @@ def range(start, stop=None, step=1):
return [0]
class file():
def __iter__(self):
yield ''
def next(self):
return ''
class xrange():
# Attention: this function doesn't exist in Py3k (there it is range).
def __iter__(self):
@@ -38,6 +48,15 @@ class xrange():
return 1
def open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True):
import io
return io.TextIOWrapper(file, mode, buffering, encoding, errors, newline, closefd)
def open_python2(name, mode=None, buffering=None):
return file(name, mode, buffering)
#--------------------------------------------------------
# descriptors
#--------------------------------------------------------
@@ -137,7 +156,7 @@ class set():
yield i
def pop(self):
return self.__iterable.pop()
return list(self.__iterable)[-1]
def copy(self):
return self
@@ -183,6 +202,10 @@ class dict():
except KeyError:
return d
def setdefault(self, k, d):
# TODO maybe also return the content
return d
class reversed():
def __init__(self, sequence):
@@ -196,7 +219,11 @@ class reversed():
return next(self.__iter__())
def next(self):
return self.__next__()
return next(self.__iter__())
def sorted(iterable, cmp=None, key=None, reverse=False):
return iterable
#--------------------------------------------------------
@@ -211,8 +238,7 @@ class str():
def __init__(self, obj):
pass
class object():
class type():
def mro():
""" mro() -> list
return a type's method resolution order """
return [object]

View File

@@ -0,0 +1,6 @@
class TextIOWrapper():
def __next__(self):
return str()
def __iter__(self):
yield str()

195
jedi/evaluate/docstrings.py Normal file
View File

@@ -0,0 +1,195 @@
"""
Docstrings are another source of information for functions and classes.
:mod:`jedi.evaluate.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.
"""
from ast import literal_eval
import re
from itertools import chain
from textwrap import dedent
from jedi.evaluate.cache import memoize_default
from jedi.parser import Parser, load_grammar
from jedi.common import indent_block
from jedi.evaluate.iterable import Array, FakeSequence, AlreadyEvaluated
DOCSTRING_PARAM_PATTERNS = [
r'\s*:type\s+%s:\s*([^\n]+)', # Sphinx
r'\s*:param\s+(\w+)\s+%s:[^\n]+', # Sphinx param with type
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':[^`]+:`([^`]+)`')
try:
from numpydoc.docscrape import NumpyDocString
except ImportError:
def _search_param_in_numpydocstr(docstr, param_str):
return []
else:
def _search_param_in_numpydocstr(docstr, param_str):
"""Search `docstr` (in numpydoc format) for type(-s) of `param_str`."""
params = NumpyDocString(docstr)._parsed_data['Parameters']
for p_name, p_type, p_descr in params:
if p_name == param_str:
m = re.match('([^,]+(,[^,]+)*?)(,[ ]*optional)?$', p_type)
if m:
p_type = m.group(1)
if p_type.startswith('{'):
types = set(type(x).__name__ for x in literal_eval(p_type))
return list(types)
else:
return [p_type]
return []
def _search_param_in_docstr(docstr, param_str):
"""
Search `docstr` for type(-s) of `param_str`.
>>> _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']
>>> bool(_search_param_in_docstr('no document', 'param'))
False
>>> _search_param_in_docstr(':param int param: some description', 'param')
['int']
"""
# 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_rst_role(match.group(1))]
return (_search_param_in_numpydocstr(docstr, param_str) or
[])
def _strip_rst_role(type_str):
"""
Strip off the part looks like a ReST role in `type_str`.
>>> _strip_rst_role(':class:`ClassName`') # strip off :class:
'ClassName'
>>> _strip_rst_role(':py:obj:`module.Object`') # works with domain
'module.Object'
>>> _strip_rst_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 _evaluate_for_statement_string(evaluator, string, module):
code = dedent("""
def pseudo_docstring_stuff():
# Create a pseudo function for docstring statements.
%s
""")
if string is None:
return []
for element in re.findall('((?:\w+\.)*\w+)\.', string):
# Try to import module part in dotted name.
# (e.g., 'threading' in 'threading.Thread').
string = 'import %s\n' % element + string
# Take the default grammar here, if we load the Python 2.7 grammar here, it
# will be impossible to use `...` (Ellipsis) as a token. Docstring types
# don't need to conform with the current grammar.
p = Parser(load_grammar(), code % indent_block(string))
try:
pseudo_cls = p.module.subscopes[0]
# First pick suite, then simple_stmt (-2 for DEDENT) and then the node,
# which is also not the last item, because there's a newline.
stmt = pseudo_cls.children[-1].children[-2].children[-2]
except (AttributeError, IndexError):
return []
# Use the module of the param.
# TODO this module is not the module of the param in case of a function
# call. In that case it's the module of the function call.
# stuffed with content from a function call.
pseudo_cls.parent = module
return list(_execute_types_in_stmt(evaluator, stmt))
def _execute_types_in_stmt(evaluator, stmt):
"""
Executing all types or general elements that we find in a statement. This
doesn't include tuple, list and dict literals, because the stuff they
contain is executed. (Used as type information).
"""
definitions = evaluator.eval_element(stmt)
return chain.from_iterable(_execute_array_values(evaluator, d) for d in definitions)
def _execute_array_values(evaluator, array):
"""
Tuples indicate that there's not just one return value, but the listed
ones. `(str, int)` means that it returns a tuple with both types.
"""
if isinstance(array, Array):
values = []
for typ in array.values():
objects = _execute_array_values(evaluator, typ)
values.append(AlreadyEvaluated(objects))
return [FakeSequence(evaluator, values, array.type)]
else:
return evaluator.execute(array)
@memoize_default(None, evaluator_is_first_arg=True)
def follow_param(evaluator, param):
func = param.parent_function
return [p
for param_str in _search_param_in_docstr(func.raw_doc,
str(param.name))
for p in _evaluate_for_statement_string(evaluator, param_str,
param.get_parent_until())]
@memoize_default(None, evaluator_is_first_arg=True)
def find_return_types(evaluator, func):
def search_return_in_docstr(code):
for p in DOCSTRING_RETURN_PATTERNS:
match = p.search(code)
if match:
return _strip_rst_role(match.group(1))
type_str = search_return_in_docstr(func.raw_doc)
return _evaluate_for_statement_string(evaluator, type_str, func.get_parent_until())

146
jedi/evaluate/dynamic.py Normal file
View File

@@ -0,0 +1,146 @@
"""
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:
- |Jedi| sees a param
- search for function calls named ``foo``
- execute these calls and check the input. This work with a ``ParamListener``.
"""
from itertools import chain
from jedi._compatibility import unicode
from jedi.parser import tree
from jedi import settings
from jedi import debug
from jedi.evaluate.cache import memoize_default
from jedi.evaluate import imports
class ParamListener(object):
"""
This listener is used to get the params for a function.
"""
def __init__(self):
self.param_possibilities = []
def execute(self, params):
self.param_possibilities += params
@debug.increase_indent
def search_params(evaluator, param):
"""
A dynamic search for param values. If you try to complete a type:
>>> def func(foo):
... foo
>>> func(1)
>>> func("")
It is not known what the type ``foo`` without analysing the whole code. You
have to look for all calls to ``func`` to find out what ``foo`` possibly
is.
"""
if not settings.dynamic_params:
return []
func = param.get_parent_until(tree.Function)
debug.dbg('Dynamic param search for %s in %s.', param, str(func.name))
# Compare the param names.
names = [n for n in search_function_call(evaluator, func)
if n.value == param.name.value]
# Evaluate the ExecutedParams to types.
result = list(chain.from_iterable(n.parent.eval(evaluator) for n in names))
debug.dbg('Dynamic param result %s', result)
return result
@memoize_default([], evaluator_is_first_arg=True)
def search_function_call(evaluator, func):
"""
Returns a list of param names.
"""
from jedi.evaluate import representation as er
def get_params_for_module(module):
"""
Returns the values of a param, or an empty array.
"""
@memoize_default([], evaluator_is_first_arg=True)
def get_posibilities(evaluator, module, func_name):
try:
names = module.used_names[func_name]
except KeyError:
return []
for name in names:
parent = name.parent
if tree.is_node(parent, 'trailer'):
parent = parent.parent
trailer = None
if tree.is_node(parent, 'power'):
for t in parent.children[1:]:
if t == '**':
break
if t.start_pos > name.start_pos and t.children[0] == '(':
trailer = t
break
if trailer is not None:
types = evaluator.goto_definition(name)
# We have to remove decorators, because they are not the
# "original" functions, this way we can easily compare.
# At the same time we also have to remove InstanceElements.
undec = []
for escope in types:
if escope.isinstance(er.Function, er.Instance) \
and escope.decorates is not None:
undec.append(escope.decorates)
elif isinstance(escope, er.InstanceElement):
undec.append(escope.var)
else:
undec.append(escope)
if evaluator.wrap(compare) in undec:
# Only if we have the correct function we execute
# it, otherwise just ignore it.
evaluator.eval_trailer(types, trailer)
return listener.param_possibilities
return get_posibilities(evaluator, module, func_name)
current_module = func.get_parent_until()
func_name = unicode(func.name)
compare = func
if func_name == '__init__':
cls = func.get_parent_scope()
if isinstance(cls, tree.Class):
func_name = unicode(cls.name)
compare = cls
# add the listener
listener = ParamListener()
func.listeners.add(listener)
try:
result = []
# This is like backtracking: Get the first possible result.
for mod in imports.get_modules_containing_name(evaluator, [current_module], func_name):
result = get_params_for_module(mod)
if result:
break
finally:
# cleanup: remove the listener; important: should not stick.
func.listeners.remove(listener)
return result

547
jedi/evaluate/finder.py Normal file
View File

@@ -0,0 +1,547 @@
"""
Searching for names with given scope and name. This is very central in Jedi and
Python. The name resolution is quite complicated with descripter,
``__getattribute__``, ``__getattr__``, ``global``, etc.
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 itertools import chain
from jedi._compatibility import unicode, u
from jedi.parser import tree
from jedi import debug
from jedi import common
from jedi import settings
from jedi.evaluate import representation as er
from jedi.evaluate import dynamic
from jedi.evaluate import compiled
from jedi.evaluate import docstrings
from jedi.evaluate import iterable
from jedi.evaluate import imports
from jedi.evaluate import analysis
from jedi.evaluate import flow_analysis
from jedi.evaluate import param
from jedi.evaluate import helpers
from jedi.evaluate.cache import memoize_default
def filter_after_position(names, position):
"""
Removes all names after a certain position. If position is None, just
returns the names list.
"""
if position is None:
return names
names_new = []
for n in names:
# Filter positions and also allow list comprehensions and lambdas.
if n.start_pos[0] is not None and n.start_pos < position \
or isinstance(n.get_definition(), (tree.CompFor, tree.Lambda)):
names_new.append(n)
return names_new
def filter_definition_names(names, origin, position=None):
"""
Filter names that are actual definitions in a scope. Names that are just
used will be ignored.
"""
# Just calculate the scope from the first
stmt = names[0].get_definition()
scope = stmt.get_parent_scope()
if not (isinstance(scope, er.FunctionExecution)
and isinstance(scope.base, er.LambdaWrapper)):
names = filter_after_position(names, position)
names = [name for name in names if name.is_definition()]
# Private name mangling (compile.c) disallows access on names
# preceeded by two underscores `__` if used outside of the class. Names
# that also end with two underscores (e.g. __id__) are not affected.
for name in list(names):
if name.value.startswith('__') and not name.value.endswith('__'):
if filter_private_variable(scope, origin):
names.remove(name)
return names
class NameFinder(object):
def __init__(self, evaluator, scope, name_str, position=None):
self._evaluator = evaluator
# Make sure that it's not just a syntax tree node.
self.scope = evaluator.wrap(scope)
self.name_str = name_str
self.position = position
@debug.increase_indent
def find(self, scopes, search_global=False):
# TODO rename scopes to names_dicts
names = self.filter_name(scopes)
types = self._names_to_types(names, search_global)
if not names and not types \
and not (isinstance(self.name_str, tree.Name)
and isinstance(self.name_str.parent.parent, tree.Param)):
if not isinstance(self.name_str, (str, unicode)): # TODO Remove?
if search_global:
message = ("NameError: name '%s' is not defined."
% self.name_str)
analysis.add(self._evaluator, 'name-error', self.name_str,
message)
else:
analysis.add_attribute_error(self._evaluator,
self.scope, self.name_str)
debug.dbg('finder._names_to_types: %s -> %s', names, types)
return types
def scopes(self, search_global=False):
if search_global:
return global_names_dict_generator(self._evaluator, self.scope, self.position)
else:
return ((n, None) for n in self.scope.names_dicts(search_global))
def names_dict_lookup(self, names_dict, position):
def get_param(scope, el):
if isinstance(el.get_parent_until(tree.Param), tree.Param):
return scope.param_by_name(str(el))
return el
search_str = str(self.name_str)
try:
names = names_dict[search_str]
if not names: # We want names, otherwise stop.
return []
except KeyError:
return []
names = filter_definition_names(names, self.name_str, position)
name_scope = None
# Only the names defined in the last position are valid definitions.
last_names = []
for name in reversed(sorted(names, key=lambda name: name.start_pos)):
stmt = name.get_definition()
name_scope = self._evaluator.wrap(stmt.get_parent_scope())
if isinstance(self.scope, er.Instance) and not isinstance(name_scope, er.Instance):
# Instances should not be checked for positioning, because we
# don't know in which order the functions are called.
last_names.append(name)
continue
if isinstance(name_scope, compiled.CompiledObject):
# Let's test this. TODO need comment. shouldn't this be
# filtered before?
last_names.append(name)
continue
if isinstance(name, compiled.CompiledName) \
or isinstance(name, er.InstanceName) and isinstance(name._origin_name, compiled.CompiledName):
last_names.append(name)
continue
if isinstance(self.name_str, tree.Name):
origin_scope = self.name_str.get_parent_until(tree.Scope, reverse=True)
else:
origin_scope = None
if isinstance(stmt.parent, compiled.CompiledObject):
# TODO seriously? this is stupid.
continue
check = flow_analysis.break_check(self._evaluator, name_scope,
stmt, origin_scope)
if check is not flow_analysis.UNREACHABLE:
last_names.append(name)
if check is flow_analysis.REACHABLE:
break
if isinstance(name_scope, er.FunctionExecution):
# Replace params
return [get_param(name_scope, n) for n in last_names]
return last_names
def filter_name(self, names_dicts):
"""
Searches names that are defined in a scope (the different
`names_dicts`), until a name fits.
"""
names = []
for names_dict, position in names_dicts:
names = self.names_dict_lookup(names_dict, position)
if names:
break
debug.dbg('finder.filter_name "%s" in (%s): %s@%s', self.name_str,
self.scope, u(names), self.position)
return list(self._clean_names(names))
def _clean_names(self, names):
"""
``NameFinder.filter_name`` should only output names with correct
wrapper parents. We don't want to see AST classes out in the
evaluation, so remove them already here!
"""
for n in names:
definition = n.parent
if isinstance(definition, (tree.Function, tree.Class, tree.Module)):
yield self._evaluator.wrap(definition).name
else:
yield n
def _check_getattr(self, inst):
"""Checks for both __getattr__ and __getattribute__ methods"""
result = []
# str is important, because it shouldn't be `Name`!
name = compiled.create(self._evaluator, str(self.name_str))
with common.ignored(KeyError):
result = inst.execute_subscope_by_name('__getattr__', name)
if not result:
# this is a little bit special. `__getattribute__` is executed
# before anything else. But: I know no use case, where this
# could be practical and the jedi would return wrong types. If
# you ever have something, let me know!
with common.ignored(KeyError):
result = inst.execute_subscope_by_name('__getattribute__', name)
return result
def _names_to_types(self, names, search_global):
types = []
# Add isinstance and other if/assert knowledge.
if isinstance(self.name_str, tree.Name):
# Ignore FunctionExecution parents for now.
flow_scope = self.name_str
until = flow_scope.get_parent_until(er.FunctionExecution)
while not isinstance(until, er.FunctionExecution):
flow_scope = flow_scope.get_parent_scope(include_flows=True)
if flow_scope is None:
break
# TODO check if result is in scope -> no evaluation necessary
n = check_flow_information(self._evaluator, flow_scope,
self.name_str, self.position)
if n:
return n
for name in names:
new_types = _name_to_types(self._evaluator, name, self.scope)
if isinstance(self.scope, (er.Class, er.Instance)) and not search_global:
types += self._resolve_descriptors(name, new_types)
else:
types += new_types
if not names and isinstance(self.scope, er.Instance):
# handling __getattr__ / __getattribute__
types = self._check_getattr(self.scope)
return types
def _resolve_descriptors(self, name, types):
# The name must not be in the dictionary, but part of the class
# definition. __get__ is only called if the descriptor is defined in
# the class dictionary.
name_scope = name.get_definition().get_parent_scope()
if not isinstance(name_scope, (er.Instance, tree.Class)):
return types
result = []
for r in types:
try:
desc_return = r.get_descriptor_returns
except AttributeError:
result.append(r)
else:
result += desc_return(self.scope)
return result
@memoize_default([], evaluator_is_first_arg=True)
def _name_to_types(evaluator, name, scope):
types = []
typ = name.get_definition()
if typ.isinstance(tree.ForStmt):
for_types = evaluator.eval_element(typ.children[3])
for_types = iterable.get_iterator_types(for_types)
types += check_tuple_assignments(for_types, name)
elif typ.isinstance(tree.CompFor):
for_types = evaluator.eval_element(typ.children[3])
for_types = iterable.get_iterator_types(for_types)
types += check_tuple_assignments(for_types, name)
elif isinstance(typ, tree.Param):
types += _eval_param(evaluator, typ, scope)
elif typ.isinstance(tree.ExprStmt):
types += _remove_statements(evaluator, typ, name)
elif typ.isinstance(tree.WithStmt):
types += evaluator.eval_element(typ.node_from_name(name))
elif isinstance(typ, tree.Import):
types += imports.ImportWrapper(evaluator, name).follow()
elif isinstance(typ, tree.GlobalStmt):
# TODO theoretically we shouldn't be using search_global here, it
# doesn't make sense, because it's a local search (for that name)!
# However, globals are not that important and resolving them doesn't
# guarantee correctness in any way, because we don't check for when
# something is executed.
types += evaluator.find_types(typ.get_parent_scope(), str(name),
search_global=True)
elif isinstance(typ, tree.TryStmt):
# TODO an exception can also be a tuple. Check for those.
# TODO check for types that are not classes and add it to
# the static analysis report.
exceptions = evaluator.eval_element(name.prev_sibling().prev_sibling())
types = list(chain.from_iterable(
evaluator.execute(t) for t in exceptions))
else:
if typ.isinstance(er.Function):
typ = typ.get_decorated_func()
types.append(typ)
return types
def _remove_statements(evaluator, stmt, name):
"""
This is the part where statements are being stripped.
Due to lazy evaluation, statements like a = func; b = a; b() have to be
evaluated.
"""
types = []
# Remove the statement docstr stuff for now, that has to be
# implemented with the evaluator class.
#if stmt.docstr:
#res_new.append(stmt)
check_instance = None
if isinstance(stmt, er.InstanceElement) and stmt.is_class_var:
check_instance = stmt.instance
stmt = stmt.var
types += evaluator.eval_statement(stmt, seek_name=name)
if check_instance is not None:
# class renames
types = [er.get_instance_el(evaluator, check_instance, a, True)
if isinstance(a, (er.Function, tree.Function))
else a for a in types]
return types
def _eval_param(evaluator, param, scope):
res_new = []
func = param.get_parent_scope()
cls = func.parent.get_parent_until((tree.Class, tree.Function))
from jedi.evaluate.param import ExecutedParam, Arguments
if isinstance(cls, tree.Class) and param.position_nr == 0 \
and not isinstance(param, ExecutedParam):
# This is where we add self - if it has never been
# instantiated.
if isinstance(scope, er.InstanceElement):
res_new.append(scope.instance)
else:
inst = er.Instance(evaluator, evaluator.wrap(cls),
Arguments(evaluator, ()), is_generated=True)
res_new.append(inst)
return res_new
# Instances are typically faked, if the instance is not called from
# outside. Here we check it for __init__ functions and return.
if isinstance(func, er.InstanceElement) \
and func.instance.is_generated and str(func.name) == '__init__':
param = func.var.params[param.position_nr]
# Add docstring knowledge.
doc_params = docstrings.follow_param(evaluator, param)
if doc_params:
return doc_params
if isinstance(param, ExecutedParam):
return res_new + param.eval(evaluator)
else:
# Param owns no information itself.
res_new += dynamic.search_params(evaluator, param)
if not res_new:
if param.stars:
t = 'tuple' if param.stars == 1 else 'dict'
typ = evaluator.find_types(compiled.builtin, t)[0]
res_new = evaluator.execute(typ)
if param.default:
res_new += evaluator.eval_element(param.default)
return res_new
def check_flow_information(evaluator, 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
ensures that `k` is a string.
"""
if not settings.dynamic_flow_information:
return None
result = []
if flow.is_scope():
# Check for asserts.
try:
names = reversed(flow.names_dict[search_name.value])
except (KeyError, AttributeError):
names = []
for name in names:
ass = name.get_parent_until(tree.AssertStmt)
if isinstance(ass, tree.AssertStmt) and pos is not None and ass.start_pos < pos:
result = _check_isinstance_type(evaluator, ass.assertion(), search_name)
if result:
break
if isinstance(flow, (tree.IfStmt, tree.WhileStmt)):
element = flow.children[1]
result = _check_isinstance_type(evaluator, element, search_name)
return result
def _check_isinstance_type(evaluator, element, search_name):
try:
assert element.type == 'power'
# this might be removed if we analyze and, etc
assert len(element.children) == 2
first, trailer = element.children
assert isinstance(first, tree.Name) and first.value == 'isinstance'
assert trailer.type == 'trailer' and trailer.children[0] == '('
assert len(trailer.children) == 3
# arglist stuff
arglist = trailer.children[1]
args = param.Arguments(evaluator, arglist, trailer)
lst = list(args.unpack())
# Disallow keyword arguments
assert len(lst) == 2 and lst[0][0] is None and lst[1][0] is None
name = lst[0][1][0] # first argument, values, first value
# Do a simple get_code comparison. They should just have the same code,
# and everything will be all right.
classes = lst[1][1][0]
call = helpers.call_of_name(search_name)
assert name.get_code() == call.get_code()
except AssertionError:
return []
result = []
for typ in evaluator.eval_element(classes):
for typ in (typ.values() if isinstance(typ, iterable.Array) else [typ]):
result += evaluator.execute(typ)
return result
def global_names_dict_generator(evaluator, scope, position):
"""
For global name lookups. Yields tuples of (names_dict, position). If the
position is None, the position does not matter anymore in that scope.
This function is used to include names from outer scopes. For example, when
the current scope is function:
>>> from jedi._compatibility import u, no_unicode_pprint
>>> from jedi.parser import Parser, load_grammar
>>> parser = Parser(load_grammar(), u('''
... x = ['a', 'b', 'c']
... def func():
... y = None
... '''))
>>> scope = parser.module.subscopes[0]
>>> scope
<Function: func@3-5>
`global_names_dict_generator` is a generator. First it yields names from
most inner scope.
>>> from jedi.evaluate import Evaluator
>>> evaluator = Evaluator(load_grammar())
>>> scope = evaluator.wrap(scope)
>>> pairs = list(global_names_dict_generator(evaluator, scope, (4, 0)))
>>> no_unicode_pprint(pairs[0])
({'func': [], 'y': [<Name: y@4,4>]}, (4, 0))
Then it yields the names from one level "lower". In this example, this
is the most outer scope. As you can see, the position in the tuple is now
None, because typically the whole module is loaded before the function is
called.
>>> no_unicode_pprint(pairs[1])
({'func': [<Name: func@3,4>], 'x': [<Name: x@2,0>]}, None)
After that we have a few underscore names that are part of the module.
>>> sorted(pairs[2][0].keys())
['__doc__', '__file__', '__name__', '__package__']
>>> pairs[3] # global names -> there are none in our example.
({}, None)
>>> pairs[4] # package modules -> Also none.
({}, None)
Finally, it yields names from builtin, if `include_builtin` is
true (default).
>>> pairs[5][0].values() #doctest: +ELLIPSIS
[[<CompiledName: ...>], ...]
"""
in_func = False
while scope is not None:
if not (scope.type == 'classdef' and in_func):
# Names in methods cannot be resolved within the class.
for names_dict in scope.names_dicts(True):
yield names_dict, position
if scope.type == 'funcdef':
# The position should be reset if the current scope is a function.
in_func = True
position = None
scope = evaluator.wrap(scope.get_parent_scope())
# Add builtins to the global scope.
for names_dict in compiled.builtin.names_dicts(True):
yield names_dict, None
def check_tuple_assignments(types, name):
"""
Checks if tuples are assigned.
"""
for index in name.assignment_indexes():
new_types = []
for r in types:
try:
func = r.get_exact_index_types
except AttributeError:
debug.warning("Invalid tuple lookup #%s of result %s in %s",
index, types, name)
else:
try:
new_types += func(index)
except IndexError:
pass
types = new_types
return types
def filter_private_variable(scope, origin_node):
"""Check if a variable is defined inside the same class or outside."""
instance = scope.get_parent_scope()
coming_from = origin_node
while coming_from is not None \
and not isinstance(coming_from, (tree.Class, compiled.CompiledObject)):
coming_from = coming_from.get_parent_scope()
# CompiledObjects don't have double underscore attributes, but Jedi abuses
# those for fakes (builtins.pym -> list).
if isinstance(instance, compiled.CompiledObject):
return instance != coming_from
else:
return isinstance(instance, er.Instance) and instance.base.base != coming_from

View File

@@ -0,0 +1,84 @@
from jedi.parser import tree
class Status(object):
lookup_table = {}
def __init__(self, value, name):
self._value = value
self._name = name
Status.lookup_table[value] = self
def invert(self):
if self is REACHABLE:
return UNREACHABLE
elif self is UNREACHABLE:
return REACHABLE
else:
return UNSURE
def __and__(self, other):
if UNSURE in (self, other):
return UNSURE
else:
return REACHABLE if self._value and other._value else UNREACHABLE
def __repr__(self):
return '<%s: %s>' % (type(self).__name__, self._name)
REACHABLE = Status(True, 'reachable')
UNREACHABLE = Status(False, 'unreachable')
UNSURE = Status(None, 'unsure')
def break_check(evaluator, base_scope, stmt, origin_scope=None):
element_scope = evaluator.wrap(stmt.get_parent_scope(include_flows=True))
# Direct parents get resolved, we filter scopes that are separate branches.
# This makes sense for autocompletion and static analysis. For actual
# Python it doesn't matter, because we're talking about potentially
# unreachable code.
# e.g. `if 0:` would cause all name lookup within the flow make
# unaccessible. This is not a "problem" in Python, because the code is
# never called. In Jedi though, we still want to infer types.
while origin_scope is not None:
if element_scope == origin_scope:
return REACHABLE
origin_scope = origin_scope.parent
return _break_check(evaluator, stmt, base_scope, element_scope)
def _break_check(evaluator, stmt, base_scope, element_scope):
element_scope = evaluator.wrap(element_scope)
base_scope = evaluator.wrap(base_scope)
reachable = REACHABLE
if isinstance(element_scope, tree.IfStmt):
if element_scope.node_after_else(stmt):
for check_node in element_scope.check_nodes():
reachable = _check_if(evaluator, check_node)
if reachable in (REACHABLE, UNSURE):
break
reachable = reachable.invert()
else:
node = element_scope.node_in_which_check_node(stmt)
reachable = _check_if(evaluator, node)
elif isinstance(element_scope, (tree.TryStmt, tree.WhileStmt)):
return UNSURE
# Only reachable branches need to be examined further.
if reachable in (UNREACHABLE, UNSURE):
return reachable
if base_scope != element_scope and base_scope != element_scope.parent:
return reachable & _break_check(evaluator, stmt, base_scope, element_scope.parent)
return reachable
def _check_if(evaluator, node):
types = evaluator.eval_element(node)
values = set(x.py__bool__() for x in types)
if len(values) == 1:
return Status.lookup_table[values.pop()]
else:
return UNSURE

179
jedi/evaluate/helpers.py Normal file
View File

@@ -0,0 +1,179 @@
import copy
from itertools import chain
from jedi.parser import tree
def deep_ast_copy(obj, parent=None, new_elements=None):
"""
Much, much faster than copy.deepcopy, but just for Parser elements (Doesn't
copy parents).
"""
if new_elements is None:
new_elements = {}
def copy_node(obj):
# If it's already in the cache, just return it.
try:
return new_elements[obj]
except KeyError:
# Actually copy and set attributes.
new_obj = copy.copy(obj)
new_elements[obj] = new_obj
# Copy children
new_children = []
for child in obj.children:
typ = child.type
if typ in ('whitespace', 'operator', 'keyword', 'number', 'string'):
# At the moment we're not actually copying those primitive
# elements, because there's really no need to. The parents are
# obviously wrong, but that's not an issue.
new_child = child
elif typ == 'name':
new_elements[child] = new_child = copy.copy(child)
new_child.parent = new_obj
else: # Is a BaseNode.
new_child = copy_node(child)
new_child.parent = new_obj
new_children.append(new_child)
new_obj.children = new_children
# Copy the names_dict (if there is one).
try:
names_dict = obj.names_dict
except AttributeError:
pass
else:
try:
new_obj.names_dict = new_names_dict = {}
except AttributeError: # Impossible to set CompFor.names_dict
pass
else:
for string, names in names_dict.items():
new_names_dict[string] = [new_elements[n] for n in names]
return new_obj
if obj.type == 'name':
# Special case of a Name object.
new_elements[obj] = new_obj = copy.copy(obj)
if parent is not None:
new_obj.parent = parent
elif isinstance(obj, tree.BaseNode):
new_obj = copy_node(obj)
if parent is not None:
for child in new_obj.children:
if isinstance(child, (tree.Name, tree.BaseNode)):
child.parent = parent
else: # String literals and so on.
new_obj = obj # Good enough, don't need to copy anything.
return new_obj
def call_of_name(name, cut_own_trailer=False):
"""
Creates a "call" node that consist of all ``trailer`` and ``power``
objects. E.g. if you call it with ``append``::
list([]).append(3) or None
You would get a node with the content ``list([]).append`` back.
This generates a copy of the original ast node.
"""
par = name
if tree.is_node(par.parent, 'trailer'):
par = par.parent
power = par.parent
if tree.is_node(power, 'power') and power.children[0] != name \
and not (power.children[-2] == '**' and
name.start_pos > power.children[-1].start_pos):
par = power
# Now the name must be part of a trailer
index = par.children.index(name.parent)
if index != len(par.children) - 1 or cut_own_trailer:
# Now we have to cut the other trailers away.
par = deep_ast_copy(par)
if not cut_own_trailer:
# Normally we would remove just the stuff after the index, but
# if the option is set remove the index as well. (for goto)
index = index + 1
par.children[index:] = []
return par
def get_module_names(module, all_scopes):
"""
Returns a dictionary with name parts as keys and their call paths as
values.
"""
if all_scopes:
dct = module.used_names
else:
dct = module.names_dict
return chain.from_iterable(dct.values())
class FakeImport(tree.ImportName):
def __init__(self, name, parent, level=0):
super(FakeImport, self).__init__([])
self.parent = parent
self._level = level
self.name = name
def get_defined_names(self):
return [self.name]
def aliases(self):
return {}
@property
def level(self):
return self._level
@property
def start_pos(self):
return 0, 0
def paths(self):
return [[self.name]]
def is_definition(self):
return True
class FakeName(tree.Name):
def __init__(self, name_str, parent=None, start_pos=(0, 0), is_definition=None):
"""
In case is_definition is defined (not None), that bool value will be
returned.
"""
super(FakeName, self).__init__(tree.zero_position_modifier, name_str, start_pos)
self.parent = parent
self._is_definition = is_definition
def get_definition(self):
return self.parent
def is_definition(self):
if self._is_definition is None:
return super(FakeName, self).is_definition()
else:
return self._is_definition
class LazyName(FakeName):
def __init__(self, name, parent_callback, is_definition=None):
super(LazyName, self).__init__(name, is_definition=is_definition)
self._parent_callback = parent_callback
@property
def parent(self):
return self._parent_callback()
@parent.setter
def parent(self, value):
pass # Do nothing, super classes can try to set the parent.

507
jedi/evaluate/imports.py Normal file
View File

@@ -0,0 +1,507 @@
"""
:mod:`jedi.evaluate.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``).
"""
import imp
import os
import pkgutil
import sys
from itertools import chain
from jedi._compatibility import find_module, unicode
from jedi import common
from jedi import debug
from jedi import cache
from jedi.parser import fast
from jedi.parser import tree
from jedi.evaluate import sys_path
from jedi.evaluate import helpers
from jedi import settings
from jedi.common import source_to_unicode
from jedi.evaluate import compiled
from jedi.evaluate import analysis
from jedi.evaluate.cache import memoize_default, NO_DEFAULT
def completion_names(evaluator, imp, pos):
name = imp.name_for_position(pos)
module = evaluator.wrap(imp.get_parent_until())
if name is None:
level = 0
for node in imp.children:
if node.end_pos <= pos:
if node in ('.', '...'):
level += len(node.value)
import_path = []
else:
# Completion on an existing name.
# The import path needs to be reduced by one, because we're completing.
import_path = imp.path_for_name(name)[:-1]
level = imp.level
importer = Importer(evaluator, tuple(import_path), module, level)
if isinstance(imp, tree.ImportFrom):
c = imp.children
only_modules = c[c.index('import')].start_pos >= pos
else:
only_modules = True
return importer.completion_names(evaluator, only_modules)
class ImportWrapper(tree.Base):
def __init__(self, evaluator, name):
self._evaluator = evaluator
self._name = name
self._import = name.get_parent_until(tree.Import)
self.import_path = self._import.path_for_name(name)
@memoize_default()
def follow(self, is_goto=False):
if self._evaluator.recursion_detector.push_stmt(self._import):
# check recursion
return []
try:
module = self._evaluator.wrap(self._import.get_parent_until())
import_path = self._import.path_for_name(self._name)
from_import_name = None
try:
from_names = self._import.get_from_names()
except AttributeError:
# Is an import_name
pass
else:
if len(from_names) + 1 == len(import_path):
# We have to fetch the from_names part first and then check
# if from_names exists in the modules.
from_import_name = import_path[-1]
import_path = from_names
importer = Importer(self._evaluator, tuple(import_path),
module, self._import.level)
types = importer.follow()
#if self._import.is_nested() and not self.nested_resolve:
# scopes = [NestedImportModule(module, self._import)]
if from_import_name is not None:
types = list(chain.from_iterable(
self._evaluator.find_types(t, unicode(from_import_name),
is_goto=is_goto)
for t in types))
if not types:
path = import_path + [from_import_name]
importer = Importer(self._evaluator, tuple(path),
module, self._import.level)
types = importer.follow()
# goto only accepts `Name`
if is_goto:
types = [s.name for s in types]
else:
# goto only accepts `Name`
if is_goto:
types = [s.name for s in types]
debug.dbg('after import: %s', types)
finally:
self._evaluator.recursion_detector.pop_stmt()
return types
class NestedImportModule(tree.Module):
"""
TODO while there's no use case for nested import module right now, we might
be able to use them for static analysis checks later on.
"""
def __init__(self, module, nested_import):
self._module = module
self._nested_import = nested_import
def _get_nested_import_name(self):
"""
Generates an Import statement, that can be used to fake nested imports.
"""
i = self._nested_import
# This is not an existing Import statement. Therefore, set position to
# 0 (0 is not a valid line number).
zero = (0, 0)
names = [unicode(name) for name in i.namespace_names[1:]]
name = helpers.FakeName(names, self._nested_import)
new = tree.Import(i._sub_module, zero, zero, name)
new.parent = self._module
debug.dbg('Generated a nested import: %s', new)
return helpers.FakeName(str(i.namespace_names[1]), new)
def __getattr__(self, name):
return getattr(self._module, name)
def __repr__(self):
return "<%s: %s of %s>" % (self.__class__.__name__, self._module,
self._nested_import)
def _add_error(evaluator, name, message=None):
if hasattr(name, 'parent'):
# Should be a name, not a string!
analysis.add(evaluator, 'import-error', name, message)
def get_init_path(directory_path):
"""
The __init__ file can be searched in a directory. If found return it, else
None.
"""
for suffix, _, _ in imp.get_suffixes():
path = os.path.join(directory_path, '__init__' + suffix)
if os.path.exists(path):
return path
return None
class Importer(object):
def __init__(self, evaluator, import_path, module, level=0):
"""
An implementation similar to ``__import__``. Use `follow`
to actually follow the imports.
*level* specifies whether to use absolute or relative imports. 0 (the
default) means only perform absolute imports. Positive values for level
indicate the number of parent directories to search relative to the
directory of the module calling ``__import__()`` (see PEP 328 for the
details).
:param import_path: List of namespaces (strings or Names).
"""
debug.speed('import %s' % (import_path,))
self._evaluator = evaluator
self.level = level
self.module = module
try:
self.file_path = module.py__file__()
except AttributeError:
# Can be None for certain compiled modules like 'builtins'.
self.file_path = None
if level:
base = module.py__package__().split('.')
if base == ['']:
base = []
if level > len(base):
path = module.py__file__()
import_path = list(import_path)
for i in range(level):
path = os.path.dirname(path)
dir_name = os.path.basename(path)
# This is not the proper way to do relative imports. However, since
# Jedi cannot be sure about the entry point, we just calculate an
# absolute path here.
if dir_name:
import_path.insert(0, dir_name)
else:
_add_error(self._evaluator, import_path[-1])
import_path = []
# TODO add import error.
debug.warning('Attempted relative import beyond top-level package.')
else:
# Here we basically rewrite the level to 0.
import_path = tuple(base) + import_path
self.import_path = import_path
@property
def str_import_path(self):
"""Returns the import path as pure strings instead of `Name`."""
return tuple(str(name) for name in self.import_path)
@memoize_default()
def sys_path_with_modifications(self):
in_path = []
sys_path_mod = list(sys_path.sys_path_with_modifications(self._evaluator, self.module))
if self.file_path is not None:
# 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.
if self.import_path: # TODO is this check really needed?
for path in sys_path.traverse_parents(self.file_path):
if os.path.basename(path) == self.str_import_path[0]:
in_path.append(os.path.dirname(path))
# Since we know nothing about the call location of the sys.path,
# it's a possibility that the current directory is the origin of
# the Python execution.
sys_path_mod.insert(0, os.path.dirname(self.file_path))
return in_path + sys_path_mod
@memoize_default(NO_DEFAULT)
def follow(self):
if not self.import_path:
return []
return self._do_import(self.import_path, self.sys_path_with_modifications())
def _do_import(self, import_path, sys_path):
"""
This method is very similar to importlib's `_gcd_import`.
"""
import_parts = [str(i) for i in import_path]
# Handle "magic" Flask extension imports:
# ``flask.ext.foo`` is really ``flask_foo`` or ``flaskext.foo``.
if len(import_path) > 2 and import_parts[:2] == ['flask', 'ext']:
# New style.
ipath = ('flask_' + str(import_parts[2]),) + import_path[3:]
modules = self._do_import(ipath, sys_path)
if modules:
return modules
else:
# Old style
return self._do_import(('flaskext',) + import_path[2:], sys_path)
module_name = '.'.join(import_parts)
try:
return [self._evaluator.modules[module_name]]
except KeyError:
pass
if len(import_path) > 1:
# This is a recursive way of importing that works great with
# the module cache.
bases = self._do_import(import_path[:-1], sys_path)
if not bases:
return []
# We can take the first element, because only the os special
# case yields multiple modules, which is not important for
# further imports.
base = bases[0]
# 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``.
if [str(i) for i in import_path] == ['os', 'path']:
return self._evaluator.find_types(base, 'path')
try:
# It's possible that by giving it always the sys path (and not
# the __path__ attribute of the parent, we get wrong results
# and nested namespace packages don't work. But I'm not sure.
paths = base.py__path__(sys_path)
except AttributeError:
# The module is not a package.
_add_error(self._evaluator, import_path[-1])
return []
else:
debug.dbg('search_module %s in paths %s', module_name, paths)
for path in paths:
# At the moment we are only using one path. So this is
# not important to be correct.
try:
module_file, module_path, is_pkg = \
find_module(import_parts[-1], [path])
break
except ImportError:
module_path = None
if module_path is None:
_add_error(self._evaluator, import_path[-1])
return []
else:
try:
debug.dbg('search_module %s in %s', import_parts[-1], 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, sys.path
try:
module_file, module_path, is_pkg = \
find_module(import_parts[-1])
finally:
sys.path = temp
except ImportError:
# The module is not a package.
_add_error(self._evaluator, import_path[-1])
return []
source = None
if is_pkg:
# In this case, we don't have a file yet. Search for the
# __init__ file.
module_path = get_init_path(module_path)
elif module_file:
source = module_file.read()
module_file.close()
if module_file is None and not module_path.endswith('.py'):
module = compiled.load_module(module_path)
else:
module = _load_module(self._evaluator, module_path, source, sys_path)
self._evaluator.modules[module_name] = module
return [module]
def _generate_name(self, name):
return helpers.FakeName(name, parent=self.module)
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.
"""
names = []
# add builtin module names
if search_path is None:
names += [self._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):
names.append(self._generate_name(name))
return names
def completion_names(self, evaluator, only_modules=False):
"""
:param only_modules: Indicates wheter it's possible to import a
definition that is not defined in a module.
"""
from jedi.evaluate import finder
names = []
if self.import_path:
# flask
if self.str_import_path == ('flask', 'ext'):
# List Flask extensions like ``flask_foo``
for mod in self._get_module_names():
modname = str(mod)
if modname.startswith('flask_'):
extname = modname[len('flask_'):]
names.append(self._generate_name(extname))
# Now the old style: ``flaskext.foo``
for dir in self.sys_path_with_modifications():
flaskext = os.path.join(dir, 'flaskext')
if os.path.isdir(flaskext):
names += self._get_module_names([flaskext])
for scope in self.follow():
# Non-modules are not completable.
if not scope.type == 'file_input': # not a module
continue
# namespace packages
if isinstance(scope, tree.Module) and scope.path.endswith('__init__.py'):
paths = scope.py__path__(self.sys_path_with_modifications())
names += self._get_module_names(paths)
if only_modules:
# In the case of an import like `from x.` we don't need to
# add all the variables.
if ('os',) == self.str_import_path and not self.level:
# os.path is a hardcoded exception, because it's a
# ``sys.modules`` modification.
names.append(self._generate_name('path'))
continue
for names_dict in scope.names_dicts(search_global=False):
_names = list(chain.from_iterable(names_dict.values()))
if not _names:
continue
_names = finder.filter_definition_names(_names, scope)
names += _names
else:
# Empty import path=completion after import
if not self.level:
names += self._get_module_names()
if self.file_path is not None:
path = os.path.abspath(self.file_path)
for i in range(self.level - 1):
path = os.path.dirname(path)
names += self._get_module_names([path])
return names
def _load_module(evaluator, path=None, source=None, sys_path=None):
def load(source):
dotted_path = path and compiled.dotted_from_fs_path(path, sys_path)
if path is not None and path.endswith('.py') \
and not dotted_path in settings.auto_import_modules:
if source is None:
with open(path, 'rb') as f:
source = f.read()
else:
return compiled.load_module(path)
p = path
p = fast.FastParser(evaluator.grammar, common.source_to_unicode(source), p)
cache.save_parser(path, p)
return p.module
cached = cache.load_parser(path)
module = load(source) if cached is None else cached.module
module = evaluator.wrap(module)
return module
def add_module(evaluator, module_name, module):
if '.' not in module_name:
# We cannot add paths with dots, because that would collide with
# the sepatator dots for nested packages. Therefore we return
# `__main__` in ModuleWrapper.py__name__(), which is similar to
# Python behavior.
evaluator.modules[module_name] = module
def get_modules_containing_name(evaluator, mods, name):
"""
Search a name in the directories of modules.
"""
def check_python_file(path):
try:
return cache.parser_cache[path].parser.module
except KeyError:
try:
return check_fs(path)
except IOError:
return None
def check_fs(path):
with open(path, 'rb') as f:
source = source_to_unicode(f.read())
if name in source:
module_name = os.path.basename(path)[:-3] # Remove `.py`.
module = _load_module(evaluator, path, source)
add_module(evaluator, module_name, module)
return module
# skip non python modules
mods = set(m for m in mods if not isinstance(m, compiled.CompiledObject))
mod_paths = set()
for m in mods:
mod_paths.add(m.path)
yield m
if settings.dynamic_params_for_other_modules:
paths = set(settings.additional_dynamic_modules)
for p in mod_paths:
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 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 and not isinstance(c, compiled.CompiledObject):
yield c

631
jedi/evaluate/iterable.py Normal file
View File

@@ -0,0 +1,631 @@
"""
Contains all classes and functions to deal with lists, dicts, generators and
iterators in general.
Array modifications
*******************
If the content of an array (``set``/``list``) is requested 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`` and check wheter it's the right array. 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. Jedi only checks Array additions; ``list.pop``, etc are ignored.
"""
from itertools import chain
from jedi import common
from jedi import debug
from jedi import settings
from jedi._compatibility import use_metaclass, is_py3, unicode
from jedi.parser import tree
from jedi.evaluate import compiled
from jedi.evaluate import helpers
from jedi.evaluate.cache import CachedMetaClass, memoize_default
from jedi.evaluate import analysis
def unite(iterable):
"""Turns a two dimensional array into a one dimensional."""
return list(chain.from_iterable(iterable))
class IterableWrapper(tree.Base):
def is_class(self):
return False
class GeneratorMixin(object):
@memoize_default()
def names_dicts(self, search_global=False): # is always False
dct = {}
executes_generator = '__next__', 'send', 'next'
for names in compiled.generator_obj.names_dict.values():
for name in names:
if name.value in executes_generator:
parent = GeneratorMethod(self, name.parent)
dct[name.value] = [helpers.FakeName(name.name, parent, is_definition=True)]
else:
dct[name.value] = [name]
yield dct
def get_index_types(self, evaluator, index_array):
#debug.warning('Tried to get array access on a generator: %s', self)
analysis.add(self._evaluator, 'type-error-generator', index_array)
return []
def get_exact_index_types(self, index):
"""
Exact lookups are used for tuple lookups, which are perfectly fine if
used with generators.
"""
return [self.iter_content()[index]]
def py__bool__(self):
return True
class Generator(use_metaclass(CachedMetaClass, IterableWrapper, GeneratorMixin)):
"""Handling of `yield` functions."""
def __init__(self, evaluator, func, var_args):
super(Generator, self).__init__()
self._evaluator = evaluator
self.func = func
self.var_args = var_args
def iter_content(self):
""" returns the content of __iter__ """
# Directly execute it, because with a normal call to py__call__ a
# Generator will be returned.
from jedi.evaluate.representation import FunctionExecution
f = FunctionExecution(self._evaluator, self.func, self.var_args)
return f.get_return_types(check_yields=True)
def __getattr__(self, name):
if name not in ['start_pos', 'end_pos', 'parent', 'get_imports',
'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 GeneratorMethod(IterableWrapper):
"""``__next__`` and ``send`` methods."""
def __init__(self, generator, builtin_func):
self._builtin_func = builtin_func
self._generator = generator
def py__call__(self, evaluator, params):
# TODO add TypeError if params are given.
return self._generator.iter_content()
def __getattr__(self, name):
return getattr(self._builtin_func, name)
class Comprehension(IterableWrapper):
@staticmethod
def from_atom(evaluator, atom):
mapping = {
'(': GeneratorComprehension,
'[': ListComprehension
}
return mapping[atom.children[0]](evaluator, atom)
def __init__(self, evaluator, atom):
self._evaluator = evaluator
self._atom = atom
@memoize_default()
def eval_node(self):
"""
The first part `x + 1` of the list comprehension:
[x + 1 for x in foo]
"""
comprehension = self._atom.children[1]
# For nested comprehensions we need to search the last one.
last = comprehension.children[-1]
last_comp = comprehension.children[1]
while True:
if isinstance(last, tree.CompFor):
last_comp = last
elif not tree.is_node(last, 'comp_if'):
break
last = last.children[-1]
return helpers.deep_ast_copy(comprehension.children[0], parent=last_comp)
def get_exact_index_types(self, index):
return [self._evaluator.eval_element(self.eval_node())[index]]
def __repr__(self):
return "<e%s of %s>" % (type(self).__name__, self._atom)
class ArrayMixin(object):
@memoize_default()
def names_dicts(self, search_global=False): # Always False.
# `array.type` is a string with the type, e.g. 'list'.
scope = self._evaluator.find_types(compiled.builtin, self.type)[0]
# builtins only have one class -> [0]
scope = self._evaluator.execute(scope, (AlreadyEvaluated((self,)),))[0]
return scope.names_dicts(search_global)
def py__bool__(self):
return None # We don't know the length, because of appends.
class ListComprehension(Comprehension, ArrayMixin):
type = 'list'
def get_index_types(self, evaluator, index):
return self.iter_content()
def iter_content(self):
return self._evaluator.eval_element(self.eval_node())
@property
def name(self):
return FakeSequence(self._evaluator, [], 'list').name
class GeneratorComprehension(Comprehension, GeneratorMixin):
def iter_content(self):
return self._evaluator.eval_element(self.eval_node())
class Array(IterableWrapper, ArrayMixin):
mapping = {'(': 'tuple',
'[': 'list',
'{': 'dict'}
def __init__(self, evaluator, atom):
self._evaluator = evaluator
self.atom = atom
self.type = Array.mapping[atom.children[0]]
"""The builtin name of the array (list, set, tuple or dict)."""
c = self.atom.children
array_node = c[1]
if self.type == 'dict' and array_node != '}' \
and (not hasattr(array_node, 'children')
or ':' not in array_node.children):
self.type = 'set'
@property
def name(self):
return helpers.FakeName(self.type, parent=self)
@memoize_default()
def get_index_types(self, evaluator, index=()):
"""
Get the types of a specific index or all, if not given.
:param index: A subscriptlist node (or subnode).
"""
indexes = create_indexes_or_slices(evaluator, index)
lookup_done = False
types = []
for index in indexes:
if isinstance(index, Slice):
types += [self]
lookup_done = True
elif isinstance(index, compiled.CompiledObject) \
and isinstance(index.obj, (int, str, unicode)):
with common.ignored(KeyError, IndexError, TypeError):
types += self.get_exact_index_types(index.obj)
lookup_done = True
return types if lookup_done else self.values()
@memoize_default()
def values(self):
result = unite(self._evaluator.eval_element(v) for v in self._values())
result += check_array_additions(self._evaluator, self)
return result
def get_exact_index_types(self, mixed_index):
""" Here the index is an int/str. Raises IndexError/KeyError """
if self.type == 'dict':
for key, values in self._items():
# Because we only want the key to be a string.
keys = self._evaluator.eval_element(key)
for k in keys:
if isinstance(k, compiled.CompiledObject) \
and mixed_index == k.obj:
for value in values:
return self._evaluator.eval_element(value)
raise KeyError('No key found in dictionary %s.' % self)
# Can raise an IndexError
return self._evaluator.eval_element(self._items()[mixed_index])
def iter_content(self):
return self.values()
@common.safe_property
def parent(self):
return compiled.builtin
def get_parent_until(self):
return compiled.builtin
def __getattr__(self, name):
if name not in ['start_pos', 'get_only_subelement', 'parent',
'get_parent_until', 'items']:
raise AttributeError('Strange access on %s: %s.' % (self, name))
return getattr(self.atom, name)
def _values(self):
"""Returns a list of a list of node."""
if self.type == 'dict':
return list(chain.from_iterable(v for k, v in self._items()))
else:
return self._items()
def _items(self):
c = self.atom.children
array_node = c[1]
if array_node in (']', '}', ')'):
return [] # Direct closing bracket, doesn't contain items.
if tree.is_node(array_node, 'testlist_comp'):
return array_node.children[::2]
elif tree.is_node(array_node, 'dictorsetmaker'):
kv = []
iterator = iter(array_node.children)
for key in iterator:
op = next(iterator, None)
if op is None or op == ',':
kv.append(key) # A set.
elif op == ':': # A dict.
kv.append((key, [next(iterator)]))
next(iterator, None) # Possible comma.
else:
raise NotImplementedError('dict/set comprehensions')
return kv
else:
return [array_node]
def __iter__(self):
return iter(self._items())
def __repr__(self):
return "<%s of %s>" % (type(self).__name__, self.atom)
class _FakeArray(Array):
def __init__(self, evaluator, container, type):
self.type = type
self._evaluator = evaluator
self.atom = container
class ImplicitTuple(_FakeArray):
def __init__(self, evaluator, testlist):
super(ImplicitTuple, self).__init__(evaluator, testlist, 'tuple')
self._testlist = testlist
def _items(self):
return self._testlist.children[::2]
class FakeSequence(_FakeArray):
def __init__(self, evaluator, sequence_values, type):
super(FakeSequence, self).__init__(evaluator, sequence_values, type)
self._sequence_values = sequence_values
def _items(self):
return self._sequence_values
def get_exact_index_types(self, index):
value = self._sequence_values[index]
return self._evaluator.eval_element(value)
class AlreadyEvaluated(frozenset):
"""A simple container to add already evaluated objects to an array."""
def get_code(self):
# For debugging purposes.
return str(self)
class MergedNodes(frozenset):
pass
class FakeDict(_FakeArray):
def __init__(self, evaluator, dct):
super(FakeDict, self).__init__(evaluator, dct, 'dict')
self._dct = dct
def get_exact_index_types(self, index):
return list(chain.from_iterable(self._evaluator.eval_element(v)
for v in self._dct[index]))
def _items(self):
return self._dct.items()
class MergedArray(_FakeArray):
def __init__(self, evaluator, arrays):
super(MergedArray, self).__init__(evaluator, arrays, arrays[-1].type)
self._arrays = arrays
def get_exact_index_types(self, mixed_index):
raise IndexError
def values(self):
return list(chain(*(a.values() for a in self._arrays)))
def __iter__(self):
for array in self._arrays:
for a in array:
yield a
def __len__(self):
return sum(len(a) for a in self._arrays)
def get_iterator_types(inputs):
"""Returns the types of any iterator (arrays, yields, __iter__, etc)."""
iterators = []
# Take the first statement (for has always only
# one, remember `in`). And follow it.
for it in inputs:
if isinstance(it, (Generator, Array, ArrayInstance, Comprehension)):
iterators.append(it)
else:
if not hasattr(it, 'execute_subscope_by_name'):
debug.warning('iterator/for loop input wrong: %s', it)
continue
try:
iterators += it.execute_subscope_by_name('__iter__')
except KeyError:
debug.warning('iterators: No __iter__ method found.')
result = []
from jedi.evaluate.representation import Instance
for it in iterators:
if isinstance(it, Array):
# Array is a little bit special, since this is an internal array,
# but there's also the list builtin, which is another thing.
result += it.values()
elif isinstance(it, Instance):
# __iter__ returned an instance.
name = '__next__' if is_py3 else 'next'
try:
result += it.execute_subscope_by_name(name)
except KeyError:
debug.warning('Instance has no __next__ function in %s.', it)
else:
# TODO this is not correct, __iter__ can return arbitrary input!
# Is a generator.
result += it.iter_content()
return result
def check_array_additions(evaluator, array):
""" Just a mapper function for the internal _check_array_additions """
if array.type not in ('list', 'set'):
# TODO also check for dict updates
return []
is_list = array.type == 'list'
try:
current_module = array.atom.get_parent_until()
except AttributeError:
# If there's no get_parent_until, it's a FakeSequence or another Fake
# type. Those fake types are used inside Jedi's engine. No values may
# be added to those after their creation.
return []
return _check_array_additions(evaluator, array, current_module, is_list)
@memoize_default([], evaluator_is_first_arg=True)
def _check_array_additions(evaluator, compare_array, module, is_list):
"""
Checks if a `Array` has "add" (append, insert, extend) statements:
>>> a = [""]
>>> a.append(1)
"""
if not settings.dynamic_array_additions or isinstance(module, compiled.CompiledObject):
return []
def check_additions(arglist, add_name):
params = list(param.Arguments(evaluator, arglist).unpack())
result = []
if add_name in ['insert']:
params = params[1:]
if add_name in ['append', 'add', 'insert']:
for key, nodes in params:
result += unite(evaluator.eval_element(node) for node in nodes)
elif add_name in ['extend', 'update']:
for key, nodes in params:
iterators = unite(evaluator.eval_element(node) for node in nodes)
result += get_iterator_types(iterators)
return result
from jedi.evaluate import representation as er, param
def get_execution_parent(element):
""" Used to get an Instance/FunctionExecution parent """
if isinstance(element, Array):
node = element.atom
else:
# Is an Instance with an
# Arguments([AlreadyEvaluated([ArrayInstance])]) inside
# Yeah... I know... It's complicated ;-)
node = list(element.var_args.argument_node[0])[0].var_args.trailer
if isinstance(node, er.InstanceElement):
return node
return node.get_parent_until(er.FunctionExecution)
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)
added_types = []
for add_name in search_names:
try:
possible_names = module.used_names[add_name]
except KeyError:
continue
else:
for name in possible_names:
# Check if the original scope is an execution. If it is, one
# 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, er.FunctionExecution):
if comp_arr_parent.start_pos < name.start_pos < comp_arr_parent.end_pos:
name = comp_arr_parent.name_for_position(name.start_pos)
else:
# Don't check definitions that are not defined in the
# same function. This is not "proper" anyway. It also
# improves Jedi's speed for array lookups, since we
# don't have to check the whole source tree anymore.
continue
trailer = name.parent
power = trailer.parent
trailer_pos = power.children.index(trailer)
try:
execution_trailer = power.children[trailer_pos + 1]
except IndexError:
continue
else:
if execution_trailer.type != 'trailer' \
or execution_trailer.children[0] != '(' \
or execution_trailer.children[1] == ')':
continue
power = helpers.call_of_name(name, cut_own_trailer=True)
# InstanceElements are special, because they don't get copied,
# but have this wrapper around them.
if isinstance(comp_arr_parent, er.InstanceElement):
power = er.get_instance_el(evaluator, comp_arr_parent.instance, power)
if evaluator.recursion_detector.push_stmt(power):
# Check for recursion. Possible by using 'extend' in
# combination with function calls.
continue
if compare_array in evaluator.eval_element(power):
# The arrays match. Now add the results
added_types += check_additions(execution_trailer.children[1], add_name)
evaluator.recursion_detector.pop_stmt()
# reset settings
settings.dynamic_params_for_other_modules = temp_param_add
return added_types
def check_array_instances(evaluator, instance):
"""Used for set() and list() instances."""
if not settings.dynamic_array_additions:
return instance.var_args
ai = ArrayInstance(evaluator, instance)
from jedi.evaluate import param
return param.Arguments(evaluator, [AlreadyEvaluated([ai])])
class ArrayInstance(IterableWrapper):
"""
Used for the usage of set() and list().
This is definitely a hack, but a good one :-)
It makes it possible to use set/list conversions.
In contrast to Array, ListComprehension and all other iterable types, this
is something that is only used inside `evaluate/compiled/fake/builtins.py`
and therefore doesn't need `names_dicts`, `py__bool__` and so on, because
we don't use these operations in `builtins.py`.
"""
def __init__(self, evaluator, instance):
self._evaluator = evaluator
self.instance = instance
self.var_args = instance.var_args
def iter_content(self):
"""
The index is here just ignored, because of all the appends, etc.
lists/sets are too complicated too handle that.
"""
items = []
for key, nodes in self.var_args.unpack():
for node in nodes:
for typ in self._evaluator.eval_element(node):
items += get_iterator_types([typ])
module = self.var_args.get_parent_until()
is_list = str(self.instance.name) == 'list'
items += _check_array_additions(self._evaluator, self.instance, module, is_list)
return items
class Slice(object):
def __init__(self, evaluator, start, stop, step):
self._evaluator = evaluator
# all of them are either a Precedence or None.
self._start = start
self._stop = stop
self._step = step
@property
def obj(self):
"""
Imitate CompiledObject.obj behavior and return a ``builtin.slice()``
object.
"""
def get(element):
if element is None:
return None
result = self._evaluator.eval_element(element)
if len(result) != 1:
# We want slices to be clear defined with just one type.
# Otherwise we will return an empty slice object.
raise IndexError
try:
return result[0].obj
except AttributeError:
return None
try:
return slice(get(self._start), get(self._stop), get(self._step))
except IndexError:
return slice(None, None, None)
def create_indexes_or_slices(evaluator, index):
if tree.is_node(index, 'subscript'): # subscript is a slice operation.
start, stop, step = None, None, None
result = []
for el in index.children:
if el == ':':
if not result:
result.append(None)
elif tree.is_node(el, 'sliceop'):
if len(el.children) == 2:
result.append(el.children[1])
else:
result.append(el)
result += [None] * (3 - len(result))
return (Slice(evaluator, *result),)
return evaluator.eval_element(index)

403
jedi/evaluate/param.py Normal file
View File

@@ -0,0 +1,403 @@
from collections import defaultdict
from itertools import chain
from jedi._compatibility import unicode, zip_longest
from jedi import debug
from jedi import common
from jedi.parser import tree
from jedi.evaluate import iterable
from jedi.evaluate import analysis
from jedi.evaluate import precedence
from jedi.evaluate.helpers import FakeName
from jedi.cache import underscore_memoization
class Arguments(tree.Base):
def __init__(self, evaluator, argument_node, trailer=None):
"""
The argument_node is either a parser node or a list of evaluated
objects. Those evaluated objects may be lists of evaluated objects
themselves (one list for the first argument, one for the second, etc).
:param argument_node: May be an argument_node or a list of nodes.
"""
self.argument_node = argument_node
self._evaluator = evaluator
self.trailer = trailer # Can be None, e.g. in a class definition.
def _split(self):
if isinstance(self.argument_node, (tuple, list)):
for el in self.argument_node:
yield 0, el
else:
if not tree.is_node(self.argument_node, 'arglist'):
yield 0, self.argument_node
return
iterator = iter(self.argument_node.children)
for child in iterator:
if child == ',':
continue
elif child in ('*', '**'):
yield len(child.value), next(iterator)
else:
yield 0, child
def get_parent_until(self, *args, **kwargs):
if self.trailer is None:
try:
element = self.argument_node[0]
from jedi.evaluate.iterable import AlreadyEvaluated
if isinstance(element, AlreadyEvaluated):
element = self._evaluator.eval_element(element)[0]
except IndexError:
return None
else:
return element.get_parent_until(*args, **kwargs)
else:
return self.trailer.get_parent_until(*args, **kwargs)
def as_tuple(self):
for stars, argument in self._split():
if tree.is_node(argument, 'argument'):
argument, default = argument.children[::2]
else:
default = None
yield argument, default, stars
def unpack(self, func=None):
named_args = []
for stars, el in self._split():
if stars == 1:
arrays = self._evaluator.eval_element(el)
iterators = [_iterate_star_args(self._evaluator, a, el, func)
for a in arrays]
iterators = list(iterators)
for values in list(zip_longest(*iterators)):
yield None, [v for v in values if v is not None]
elif stars == 2:
arrays = self._evaluator.eval_element(el)
dicts = [_star_star_dict(self._evaluator, a, el, func)
for a in arrays]
for dct in dicts:
for key, values in dct.items():
yield key, values
else:
if tree.is_node(el, 'argument'):
c = el.children
if len(c) == 3: # Keyword argument.
named_args.append((c[0].value, (c[2],)))
else: # Generator comprehension.
# Include the brackets with the parent.
comp = iterable.GeneratorComprehension(
self._evaluator, self.argument_node.parent)
yield None, (iterable.AlreadyEvaluated([comp]),)
elif isinstance(el, (list, tuple)):
yield None, el
else:
yield None, (el,)
# Reordering var_args is necessary, because star args sometimes appear
# after named argument, but in the actual order it's prepended.
for key_arg in named_args:
yield key_arg
def _reorder_var_args(var_args):
named_index = None
new_args = []
for i, stmt in enumerate(var_args):
if isinstance(stmt, tree.ExprStmt):
if named_index is None and stmt.assignment_details:
named_index = i
if named_index is not None:
expression_list = stmt.expression_list()
if expression_list and expression_list[0] == '*':
new_args.insert(named_index, stmt)
named_index += 1
continue
new_args.append(stmt)
return new_args
def eval_argument_clinic(self, arguments):
"""Uses a list with argument clinic information (see PEP 436)."""
iterator = self.unpack()
for i, (name, optional, allow_kwargs) in enumerate(arguments):
key, va_values = next(iterator, (None, []))
if key is not None:
raise NotImplementedError
if not va_values and not optional:
debug.warning('TypeError: %s expected at least %s arguments, got %s',
name, len(arguments), i)
raise ValueError
values = list(chain.from_iterable(self._evaluator.eval_element(el)
for el in va_values))
if not values and not optional:
# For the stdlib we always want values. If we don't get them,
# that's ok, maybe something is too hard to resolve, however,
# we will not proceed with the evaluation of that function.
debug.warning('argument_clinic "%s" not resolvable.', name)
raise ValueError
yield values
def scope(self):
# Returns the scope in which the arguments are used.
return (self.trailer or self.argument_node).get_parent_until(tree.IsScope)
def eval_args(self):
# TODO this method doesn't work with named args and a lot of other
# things. Use unpack.
return [self._evaluator.eval_element(el) for stars, el in self._split()]
def __repr__(self):
return '<%s: %s>' % (type(self).__name__, self.argument_node)
def get_calling_var_args(self):
if tree.is_node(self.argument_node, 'arglist', 'argument') \
or self.argument_node == () and self.trailer is not None:
return _get_calling_var_args(self._evaluator, self)
else:
return None
class ExecutedParam(tree.Param):
"""Fake a param and give it values."""
def __init__(self, original_param, var_args, values):
self._original_param = original_param
self.var_args = var_args
self._values = values
def eval(self, evaluator):
types = []
for v in self._values:
types += evaluator.eval_element(v)
return types
@property
def position_nr(self):
# Need to use the original logic here, because it uses the parent.
return self._original_param.position_nr
@property
@underscore_memoization
def name(self):
return FakeName(str(self._original_param.name), self, self.start_pos)
def __getattr__(self, name):
return getattr(self._original_param, name)
def _get_calling_var_args(evaluator, var_args):
old_var_args = None
while var_args != old_var_args:
old_var_args = var_args
for name, default, stars in reversed(list(var_args.as_tuple())):
if not stars or not isinstance(name, tree.Name):
continue
names = evaluator.goto(name)
if len(names) != 1:
break
param = names[0].get_definition()
if not isinstance(param, ExecutedParam):
if isinstance(param, tree.Param):
# There is no calling var_args in this case - there's just
# a param without any input.
return None
break
# We never want var_args to be a tuple. This should be enough for
# now, we can change it later, if we need to.
if isinstance(param.var_args, Arguments):
var_args = param.var_args
return var_args.argument_node or var_args.trailer
def get_params(evaluator, func, var_args):
param_names = []
param_dict = {}
for param in func.params:
param_dict[str(param.name)] = param
unpacked_va = list(var_args.unpack(func))
from jedi.evaluate.representation import InstanceElement
if isinstance(func, InstanceElement):
# Include self at this place.
unpacked_va.insert(0, (None, [iterable.AlreadyEvaluated([func.instance])]))
var_arg_iterator = common.PushBackIterator(iter(unpacked_va))
non_matching_keys = defaultdict(lambda: [])
keys_used = {}
keys_only = False
had_multiple_value_error = False
for param in func.params:
# 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.
default = [] if param.default is None else [param.default]
key, va_values = next(var_arg_iterator, (None, default))
while key is not None:
keys_only = True
k = unicode(key)
try:
key_param = param_dict[unicode(key)]
except KeyError:
non_matching_keys[key] += va_values
else:
param_names.append(ExecutedParam(key_param, var_args, va_values).name)
if k in keys_used:
had_multiple_value_error = True
m = ("TypeError: %s() got multiple values for keyword argument '%s'."
% (func.name, k))
calling_va = _get_calling_var_args(evaluator, var_args)
if calling_va is not None:
analysis.add(evaluator, 'type-error-multiple-values',
calling_va, message=m)
else:
try:
keys_used[k] = param_names[-1]
except IndexError:
# TODO this is wrong stupid and whatever.
pass
key, va_values = next(var_arg_iterator, (None, ()))
values = []
if param.stars == 1:
# *args param
lst_values = [iterable.MergedNodes(va_values)] if va_values else []
for key, va_values in var_arg_iterator:
# Iterate until a key argument is found.
if key:
var_arg_iterator.push_back((key, va_values))
break
if va_values:
lst_values.append(iterable.MergedNodes(va_values))
seq = iterable.FakeSequence(evaluator, lst_values, 'tuple')
values = [iterable.AlreadyEvaluated([seq])]
elif param.stars == 2:
# **kwargs param
dct = iterable.FakeDict(evaluator, dict(non_matching_keys))
values = [iterable.AlreadyEvaluated([dct])]
non_matching_keys = {}
else:
# normal param
if va_values:
values = va_values
else:
# No value: Return an empty container
values = []
if not keys_only:
calling_va = var_args.get_calling_var_args()
if calling_va is not None:
m = _error_argument_count(func, len(unpacked_va))
analysis.add(evaluator, 'type-error-too-few-arguments',
calling_va, message=m)
# Now add to result if it's not one of the previously covered cases.
if (not keys_only or param.stars == 2):
param_names.append(ExecutedParam(param, var_args, values).name)
keys_used[unicode(param.name)] = param_names[-1]
if keys_only:
# All arguments should be handed over to the next function. It's not
# about the values inside, it's about the names. Jedi needs to now that
# there's nothing to find for certain names.
for k in set(param_dict) - set(keys_used):
param = param_dict[k]
values = [] if param.default is None else [param.default]
param_names.append(ExecutedParam(param, var_args, values).name)
if not (non_matching_keys or had_multiple_value_error
or param.stars or param.default):
# add a warning only if there's not another one.
calling_va = _get_calling_var_args(evaluator, var_args)
if calling_va is not None:
m = _error_argument_count(func, len(unpacked_va))
analysis.add(evaluator, 'type-error-too-few-arguments',
calling_va, message=m)
for key, va_values in non_matching_keys.items():
m = "TypeError: %s() got an unexpected keyword argument '%s'." \
% (func.name, key)
for value in va_values:
analysis.add(evaluator, 'type-error-keyword-argument', value.parent, message=m)
remaining_params = list(var_arg_iterator)
if remaining_params:
m = _error_argument_count(func, len(unpacked_va))
# Just report an error for the first param that is not needed (like
# cPython).
first_key, first_values = remaining_params[0]
for v in first_values:
if first_key is not None:
# Is a keyword argument, return the whole thing instead of just
# the value node.
v = v.parent
try:
non_kw_param = keys_used[first_key]
except KeyError:
pass
else:
origin_args = non_kw_param.parent.var_args.argument_node
# TODO calculate the var_args tree and check if it's in
# the tree (if not continue).
# print('\t\tnonkw', non_kw_param.parent.var_args.argument_node, )
if origin_args not in [f.parent.parent for f in first_values]:
continue
analysis.add(evaluator, 'type-error-too-many-arguments',
v, message=m)
return param_names
def _iterate_star_args(evaluator, array, input_node, func=None):
from jedi.evaluate.representation import Instance
if isinstance(array, iterable.Array):
for field_stmt in array: # yield from plz!
yield field_stmt
elif isinstance(array, iterable.Generator):
for field_stmt in array.iter_content():
yield iterable.AlreadyEvaluated([field_stmt])
elif isinstance(array, Instance) and array.name.get_code() == 'tuple':
debug.warning('Ignored a tuple *args input %s' % array)
else:
if func is not None:
m = "TypeError: %s() argument after * must be a sequence, not %s" \
% (func.name.value, array)
analysis.add(evaluator, 'type-error-star', input_node, message=m)
def _star_star_dict(evaluator, array, input_node, func):
dct = defaultdict(lambda: [])
from jedi.evaluate.representation import Instance
if isinstance(array, Instance) and array.name.get_code() == 'dict':
# For now ignore this case. In the future add proper iterators and just
# make one call without crazy isinstance checks.
return {}
if isinstance(array, iterable.FakeDict):
return array._dct
elif isinstance(array, iterable.Array) and array.type == 'dict':
# TODO bad call to non-public API
for key_node, values in array._items():
for key in evaluator.eval_element(key_node):
if precedence.is_string(key):
dct[key.obj] += values
else:
if func is not None:
m = "TypeError: %s argument after ** must be a mapping, not %s" \
% (func.name.value, array)
analysis.add(evaluator, 'type-error-star-star', input_node, message=m)
return dict(dct)
def _error_argument_count(func, actual_count):
default_arguments = sum(1 for p in func.params if p.default or p.stars)
if default_arguments == 0:
before = 'exactly '
else:
before = 'from %s to ' % (len(func.params) - default_arguments)
return ('TypeError: %s() takes %s%s arguments (%s given).'
% (func.name, before, len(func.params), actual_count))

174
jedi/evaluate/precedence.py Normal file
View File

@@ -0,0 +1,174 @@
"""
Handles operator precedence.
"""
import operator
from jedi._compatibility import unicode
from jedi.parser import tree
from jedi import debug
from jedi.evaluate.compiled import (CompiledObject, create, builtin,
keyword_from_value, true_obj, false_obj)
from jedi.evaluate import analysis
# Maps Python syntax to the operator module.
COMPARISON_OPERATORS = {
'==': operator.eq,
'!=': operator.ne,
'is': operator.is_,
'is not': operator.is_not,
'<': operator.lt,
'<=': operator.le,
'>': operator.gt,
'>=': operator.ge,
}
def _literals_to_types(evaluator, result):
# Changes literals ('a', 1, 1.0, etc) to its type instances (str(),
# int(), float(), etc).
for i, r in enumerate(result):
if is_literal(r):
# Literals are only valid as long as the operations are
# correct. Otherwise add a value-free instance.
cls = builtin.get_by_name(r.name.get_code())
result[i] = evaluator.execute(cls)[0]
return list(set(result))
def calculate_children(evaluator, children):
"""
Calculate a list of children with operators.
"""
iterator = iter(children)
types = evaluator.eval_element(next(iterator))
for operator in iterator:
right = next(iterator)
if tree.is_node(operator, 'comp_op'): # not in / is not
operator = ' '.join(str(c.value) for c in operator.children)
# handle lazy evaluation of and/or here.
if operator in ('and', 'or'):
left_bools = set([left.py__bool__() for left in types])
if left_bools == set([True]):
if operator == 'and':
types = evaluator.eval_element(right)
elif left_bools == set([False]):
if operator != 'and':
types = evaluator.eval_element(right)
# Otherwise continue, because of uncertainty.
else:
types = calculate(evaluator, types, operator,
evaluator.eval_element(right))
debug.dbg('calculate_children types %s', types)
return types
def calculate(evaluator, left_result, operator, right_result):
result = []
if not left_result or not right_result:
# illegal slices e.g. cause left/right_result to be None
result = (left_result or []) + (right_result or [])
result = _literals_to_types(evaluator, result)
else:
# I don't think there's a reasonable chance that a string
# operation is still correct, once we pass something like six
# objects.
if len(left_result) * len(right_result) > 6:
result = _literals_to_types(evaluator, left_result + right_result)
else:
for left in left_result:
for right in right_result:
result += _element_calculate(evaluator, left, operator, right)
return result
def factor_calculate(evaluator, types, operator):
"""
Calculates `+`, `-`, `~` and `not` prefixes.
"""
for typ in types:
if operator == '-':
if _is_number(typ):
yield create(evaluator, -typ.obj)
elif operator == 'not':
value = typ.py__bool__()
if value is None: # Uncertainty.
return
yield keyword_from_value(not value)
else:
yield typ
def _is_number(obj):
return isinstance(obj, CompiledObject) \
and isinstance(obj.obj, (int, float))
def is_string(obj):
return isinstance(obj, CompiledObject) \
and isinstance(obj.obj, (str, unicode))
def is_literal(obj):
return _is_number(obj) or is_string(obj)
def _is_tuple(obj):
from jedi.evaluate import iterable
return isinstance(obj, iterable.Array) and obj.type == 'tuple'
def _is_list(obj):
from jedi.evaluate import iterable
return isinstance(obj, iterable.Array) and obj.type == 'list'
def _element_calculate(evaluator, left, operator, right):
from jedi.evaluate import iterable, representation as er
l_is_num = _is_number(left)
r_is_num = _is_number(right)
if operator == '*':
# for iterables, ignore * operations
if isinstance(left, iterable.Array) or is_string(left):
return [left]
elif isinstance(right, iterable.Array) or is_string(right):
return [right]
elif operator == '+':
if l_is_num and r_is_num or is_string(left) and is_string(right):
return [create(evaluator, left.obj + right.obj)]
elif _is_tuple(left) and _is_tuple(right) or _is_list(left) and _is_list(right):
return [iterable.MergedArray(evaluator, (left, right))]
elif operator == '-':
if l_is_num and r_is_num:
return [create(evaluator, left.obj - right.obj)]
elif operator == '%':
# With strings and numbers the left type typically remains. Except for
# `int() % float()`.
return [left]
elif operator in COMPARISON_OPERATORS:
operation = COMPARISON_OPERATORS[operator]
if isinstance(left, CompiledObject) and isinstance(right, CompiledObject):
# Possible, because the return is not an option. Just compare.
left = left.obj
right = right.obj
try:
return [keyword_from_value(operation(left, right))]
except TypeError:
# Could be True or False.
return [true_obj, false_obj]
elif operator == 'in':
return []
def check(obj):
"""Checks if a Jedi object is either a float or an int."""
return isinstance(obj, er.Instance) and obj.name.get_code() in ('int', 'float')
# Static analysis, one is a number, the other one is not.
if operator in ('+', '-') and l_is_num != r_is_num \
and not (check(left) or check(right)):
message = "TypeError: unsupported operand type(s) for +: %s and %s"
analysis.add(evaluator, 'type-error-operation', operator,
message % (left, right))
return [left, right]

View File

@@ -3,41 +3,44 @@ 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.
Next to :mod:`jedi.evaluate.cache` this module also makes |jedi| not
thread-safe. Why? ``execution_recursion_decorator`` 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
from jedi.evaluate import compiled
from jedi.evaluate import iterable
class RecursionDecorator(object):
def recursion_decorator(func):
def run(evaluator, stmt, *args, **kwargs):
rec_detect = evaluator.recursion_detector
# print stmt, len(self.node_statements())
if rec_detect.push_stmt(stmt):
return []
else:
result = func(evaluator, stmt, *args, **kwargs)
rec_detect.pop_stmt()
return result
return run
class RecursionDetector(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 __init__(self):
self.top = None
self.current = None
def push_stmt(self, stmt):
self.current = RecursionNode(stmt, self.current)
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))
if check:
debug.warning('catched stmt recursion: %s against %s @%s', stmt,
check.stmt, stmt.start_pos)
self.pop_stmt()
return True
return False
@@ -57,10 +60,6 @@ class RecursionDecorator(object):
if not test:
return False
def reset(self):
self.top = None
self.current = None
def node_statements(self):
result = []
n = self.current
@@ -70,7 +69,7 @@ class RecursionDecorator(object):
return result
class RecursionNode(object):
class _RecursionNode(object):
""" A node of the RecursionDecorator. """
def __init__(self, stmt, parent):
self.script = stmt.get_parent_until()
@@ -81,47 +80,56 @@ class RecursionNode(object):
# 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)
self.is_ignored = self.script == compiled.builtin
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
and self.position == other.position \
and not self.is_ignored and not other.is_ignored
class ExecutionRecursionDecorator(object):
def execution_recursion_decorator(func):
def run(execution, **kwargs):
detector = execution._evaluator.execution_recursion_detector
if detector.push_execution(execution):
result = []
else:
result = func(execution, **kwargs)
detector.pop_execution()
return result
return run
class ExecutionRecursionDetector(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 __init__(self):
self.recursion_level = 0
self.parent_execution_funcs = []
self.execution_funcs = set()
self.execution_count = 0
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):
def __call__(self, execution):
debug.dbg('Execution recursions: %s', execution, self.recursion_level,
self.execution_count, len(self.execution_funcs))
if self.check_recursion(execution):
result = []
else:
result = self.func(execution, evaluate_generator)
self.cleanup()
result = self.func(execution)
self.pop_execution()
return result
@classmethod
def cleanup(cls):
def pop_execution(cls):
cls.parent_execution_funcs.pop()
cls.recursion_level -= 1
@classmethod
def check_recursion(cls, execution, evaluate_generator):
def push_execution(cls, execution):
in_par_execution_funcs = execution.base in cls.parent_execution_funcs
in_execution_funcs = execution.base in cls.execution_funcs
cls.recursion_level += 1
@@ -132,10 +140,10 @@ class ExecutionRecursionDecorator(object):
if cls.execution_count > settings.max_executions:
return True
if isinstance(execution.base, (er.Generator, er.Array)):
if isinstance(execution.base, (iterable.Array, iterable.Generator)):
return False
module = execution.get_parent_until()
if evaluate_generator or module == builtin.Builtin.scope:
if module == compiled.builtin:
return False
if in_par_execution_funcs:
@@ -147,10 +155,3 @@ class ExecutionRecursionDecorator(object):
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

View File

@@ -0,0 +1,857 @@
"""
Like described in the :mod:`jedi.parser.tree` 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.
Representation modules also define "magic methods". Those methods look like
``py__foo__`` and are typically mappable to the Python equivalents ``__call__``
and others. Here's a list:
====================================== ========================================
**Method** **Description**
-------------------------------------- ----------------------------------------
py__call__(evaluator, params: Array) On callable objects, returns types.
py__bool__() Returns True/False/None; None means that
there's no certainty.
py__bases__(evaluator) Returns a list of base classes.
py__mro__(evaluator) Returns a list of classes (the mro).
py__getattribute__(evaluator, name) Returns a list of attribute values. The
name can be str or Name.
====================================== ========================================
__
"""
import os
import pkgutil
import imp
import re
from itertools import chain
from jedi._compatibility import use_metaclass, unicode, Python3Method
from jedi.parser import tree
from jedi import debug
from jedi import common
from jedi.cache import underscore_memoization, cache_star_import
from jedi.evaluate.cache import memoize_default, CachedMetaClass, NO_DEFAULT
from jedi.evaluate import compiled
from jedi.evaluate import recursion
from jedi.evaluate import iterable
from jedi.evaluate import docstrings
from jedi.evaluate import helpers
from jedi.evaluate import param
from jedi.evaluate import flow_analysis
from jedi.evaluate import imports
class Executed(tree.Base):
"""
An instance is also an executable - because __init__ is called
:param var_args: The param input array, consist of a parser node or a list.
"""
def __init__(self, evaluator, base, var_args=()):
self._evaluator = evaluator
self.base = base
self.var_args = var_args
def is_scope(self):
return True
def get_parent_until(self, *args, **kwargs):
return tree.Base.get_parent_until(self, *args, **kwargs)
@common.safe_property
def parent(self):
return self.base.parent
class Instance(use_metaclass(CachedMetaClass, Executed)):
"""
This class is used to evaluate instances.
"""
def __init__(self, evaluator, base, var_args, is_generated=False):
super(Instance, self).__init__(evaluator, base, var_args)
self.decorates = None
# Generated instances are classes that are just generated by self
# (No var_args) used.
self.is_generated = is_generated
if base.name.get_code() in ['list', 'set'] \
and compiled.builtin == base.get_parent_until():
# compare the module path with the builtin name.
self.var_args = iterable.check_array_instances(evaluator, self)
elif not is_generated:
# Need to execute the __init__ function, because the dynamic param
# searching needs it.
try:
method = self.get_subscope_by_name('__init__')
except KeyError:
pass
else:
evaluator.execute(method, self.var_args)
@property
def py__call__(self):
def actual(evaluator, params):
return evaluator.execute(method, params)
try:
method = self.get_subscope_by_name('__call__')
except KeyError:
# Means the Instance is not callable.
raise AttributeError
return actual
def py__class__(self, evaluator):
return self.base
def py__bool__(self):
# Signalize that we don't know about the bool type.
return None
@memoize_default()
def _get_method_execution(self, func):
func = get_instance_el(self._evaluator, self, func, True)
return FunctionExecution(self._evaluator, 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].name)
except IndexError:
return None
def _self_names_dict(self, add_mro=True):
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, tree.Class):
continue
# Get the self name, if there's one.
self_name = self._get_func_self_name(sub)
if self_name is None:
continue
if sub.name.value == '__init__' and not self.is_generated:
# ``__init__`` is special because the params need are injected
# this way. Therefore an execution is necessary.
if not sub.get_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 name_list in sub.names_dict.values():
for name in name_list:
if name.value == self_name and name.prev_sibling() is None:
trailer = name.next_sibling()
if tree.is_node(trailer, 'trailer') \
and len(trailer.children) == 2 \
and trailer.children[0] == '.':
name = trailer.children[1] # After dot.
if name.is_definition():
arr = names.setdefault(name.value, [])
arr.append(get_instance_el(self._evaluator, self, name))
return names
def get_subscope_by_name(self, name):
sub = self.base.get_subscope_by_name(name)
return get_instance_el(self._evaluator, self, sub, True)
def execute_subscope_by_name(self, name, *args):
method = self.get_subscope_by_name(name)
return self._evaluator.execute_evaluated(method, *args)
def get_descriptor_returns(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 [compiled.none_obj, obj]
try:
return self.execute_subscope_by_name('__get__', *args)
except KeyError:
return [self]
@memoize_default()
def names_dicts(self, search_global):
yield self._self_names_dict()
for s in self.base.py__mro__(self._evaluator)[1:]:
if not isinstance(s, compiled.CompiledObject):
# Compiled objects don't have `self.` names.
for inst in self._evaluator.execute(s):
yield inst._self_names_dict(add_mro=False)
for names_dict in self.base.names_dicts(search_global=False, is_instance=True):
yield LazyInstanceDict(self._evaluator, self, names_dict)
def get_index_types(self, evaluator, index_array):
indexes = iterable.create_indexes_or_slices(self._evaluator, index_array)
if any([isinstance(i, iterable.Slice) for i in indexes]):
# Slice support in Jedi is very marginal, at the moment, so just
# ignore them in case of __getitem__.
# TODO support slices in a more general way.
indexes = []
try:
method = self.get_subscope_by_name('__getitem__')
except KeyError:
debug.warning('No __getitem__, cannot access the array.')
return []
else:
return self._evaluator.execute(method, [iterable.AlreadyEvaluated(indexes)])
@property
@underscore_memoization
def name(self):
name = self.base.name
return helpers.FakeName(unicode(name), self, name.start_pos)
def __getattr__(self, name):
if name not in ['start_pos', 'end_pos', 'get_imports', 'type',
'doc', 'raw_doc']:
raise AttributeError("Instance %s: Don't touch this (%s)!"
% (self, name))
return getattr(self.base, name)
def __repr__(self):
dec = ''
if self.decorates is not None:
dec = " decorates " + repr(self.decorates)
return "<e%s of %s(%s)%s>" % (type(self).__name__, self.base,
self.var_args, dec)
class LazyInstanceDict(object):
def __init__(self, evaluator, instance, dct):
self._evaluator = evaluator
self._instance = instance
self._dct = dct
def __getitem__(self, name):
return [get_instance_el(self._evaluator, self._instance, var, True)
for var in self._dct[name]]
def values(self):
return [self[key] for key in self._dct]
class InstanceName(tree.Name):
def __init__(self, origin_name, parent):
super(InstanceName, self).__init__(tree.zero_position_modifier,
origin_name.value,
origin_name.start_pos)
self._origin_name = origin_name
self.parent = parent
def is_definition(self):
return self._origin_name.is_definition()
def get_instance_el(evaluator, instance, var, is_class_var=False):
"""
Returns an InstanceElement if it makes sense, otherwise leaves the object
untouched.
Basically having an InstanceElement is context information. That is needed
in quite a lot of cases, which includes Nodes like ``power``, that need to
know where a self name comes from for example.
"""
if isinstance(var, tree.Name):
parent = get_instance_el(evaluator, instance, var.parent, is_class_var)
return InstanceName(var, parent)
elif var.type != 'funcdef' \
and isinstance(var, (Instance, compiled.CompiledObject, tree.Leaf,
tree.Module, FunctionExecution)):
return var
var = evaluator.wrap(var)
return InstanceElement(evaluator, instance, var, is_class_var)
class InstanceElement(use_metaclass(CachedMetaClass, tree.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, evaluator, instance, var, is_class_var):
self._evaluator = evaluator
self.instance = instance
self.var = var
self.is_class_var = is_class_var
@common.safe_property
@memoize_default()
def parent(self):
par = self.var.parent
if isinstance(par, Class) and par == self.instance.base \
or isinstance(par, tree.Class) \
and par == self.instance.base.base:
par = self.instance
else:
par = get_instance_el(self._evaluator, self.instance, par,
self.is_class_var)
return par
def get_parent_until(self, *args, **kwargs):
return tree.BaseNode.get_parent_until(self, *args, **kwargs)
def get_definition(self):
return self.get_parent_until((tree.ExprStmt, tree.IsScope, tree.Import))
def get_decorated_func(self):
""" Needed because the InstanceElement should not be stripped """
func = self.var.get_decorated_func()
func = get_instance_el(self._evaluator, self.instance, func)
return func
def get_rhs(self):
return get_instance_el(self._evaluator, self.instance,
self.var.get_rhs(), self.is_class_var)
def is_definition(self):
return self.var.is_definition()
@property
def children(self):
# Copy and modify the array.
return [get_instance_el(self._evaluator, self.instance, command, self.is_class_var)
for command in self.var.children]
@property
@memoize_default()
def name(self):
name = self.var.name
return helpers.FakeName(unicode(name), self, name.start_pos)
def __iter__(self):
for el in self.var.__iter__():
yield get_instance_el(self._evaluator, self.instance, el,
self.is_class_var)
def __getitem__(self, index):
return get_instance_el(self._evaluator, self.instance, self.var[index],
self.is_class_var)
def __getattr__(self, name):
return getattr(self.var, name)
def isinstance(self, *cls):
return isinstance(self.var, cls)
def is_scope(self):
"""
Since we inherit from Base, it would overwrite the action we want here.
"""
return self.var.is_scope()
def py__call__(self, evaluator, params):
if isinstance(self.var, compiled.CompiledObject):
# This check is a bit strange, but CompiledObject itself is a bit
# more complicated than we would it actually like to be.
return self.var.py__call__(evaluator, params)
else:
return Function.py__call__(self, evaluator, params)
def __repr__(self):
return "<%s of %s>" % (type(self).__name__, self.var)
class Wrapper(tree.Base):
def is_scope(self):
return True
def is_class(self):
return False
def py__bool__(self):
"""
Since Wrapper is a super class for classes, functions and modules,
the return value will always be true.
"""
return True
@property
@underscore_memoization
def name(self):
name = self.base.name
return helpers.FakeName(unicode(name), self, name.start_pos)
class Class(use_metaclass(CachedMetaClass, Wrapper)):
"""
This class is not only important to extend `tree.Class`, it is also a
important for descriptors (if the descriptor methods are evaluated or not).
"""
def __init__(self, evaluator, base):
self._evaluator = evaluator
self.base = base
@memoize_default(default=())
def py__mro__(self, evaluator):
def add(cls):
if cls not in mro:
mro.append(cls)
mro = [self]
# TODO Do a proper mro resolution. Currently we are just listing
# classes. However, it's a complicated algorithm.
for cls in self.py__bases__(self._evaluator):
# TODO detect for TypeError: duplicate base class str,
# e.g. `class X(str, str): pass`
try:
mro_method = cls.py__mro__
except AttributeError:
# TODO add a TypeError like:
"""
>>> class Y(lambda: test): pass
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: function() argument 1 must be code, not str
>>> class Y(1): pass
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: int() takes at most 2 arguments (3 given)
"""
pass
else:
add(cls)
for cls_new in mro_method(evaluator):
add(cls_new)
return tuple(mro)
@memoize_default(default=())
def py__bases__(self, evaluator):
arglist = self.base.get_super_arglist()
if arglist:
args = param.Arguments(self._evaluator, arglist)
return list(chain.from_iterable(args.eval_args()))
else:
return [compiled.object_obj]
def py__call__(self, evaluator, params):
return [Instance(evaluator, self, params)]
def py__getattribute__(self, name):
return self._evaluator.find_types(self, name)
@property
def params(self):
return self.get_subscope_by_name('__init__').params
def names_dicts(self, search_global, is_instance=False):
if search_global:
yield self.names_dict
else:
for scope in self.py__mro__(self._evaluator):
if isinstance(scope, compiled.CompiledObject):
yield scope.names_dicts(False, is_instance)[0]
else:
yield scope.names_dict
def is_class(self):
return True
def get_subscope_by_name(self, name):
for s in self.py__mro__(self._evaluator):
for sub in reversed(s.subscopes):
if sub.name.value == name:
return sub
raise KeyError("Couldn't find subscope.")
def __getattr__(self, name):
if name not in ['start_pos', 'end_pos', 'parent', 'raw_doc',
'doc', 'get_imports', 'get_parent_until', 'get_code',
'subscopes', 'names_dict', 'type']:
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(CachedMetaClass, Wrapper)):
"""
Needed because of decorators. Decorators are evaluated here.
"""
def __init__(self, evaluator, func, is_decorated=False):
""" This should not be called directly """
self._evaluator = evaluator
self.base = self.base_func = func
self.is_decorated = is_decorated
# A property that is set by the decorator resolution.
self.decorates = None
@memoize_default()
def get_decorated_func(self):
"""
Returns the function, that should to be executed in the end.
This is also the places where the decorators are processed.
"""
f = self.base_func
decorators = self.base_func.get_decorators()
if not decorators or self.is_decorated:
return self
# Only enter it, if has not already been processed.
if not self.is_decorated:
for dec in reversed(decorators):
debug.dbg('decorator: %s %s', dec, f)
dec_results = self._evaluator.eval_element(dec.children[1])
trailer = dec.children[2:-1]
if trailer:
# Create a trailer and evaluate it.
trailer = tree.Node('trailer', trailer)
trailer.parent = dec
dec_results = self._evaluator.eval_trailer(dec_results, trailer)
if not len(dec_results):
debug.warning('decorator not found: %s on %s', dec, self.base_func)
return self
decorator = dec_results.pop()
if dec_results:
debug.warning('multiple decorators found %s %s',
self.base_func, dec_results)
# Create param array.
if isinstance(f, Function):
old_func = f # TODO this is just hacky. change.
else:
old_func = Function(self._evaluator, f, is_decorated=True)
wrappers = self._evaluator.execute_evaluated(decorator, old_func)
if not len(wrappers):
debug.warning('no wrappers found %s', self.base_func)
return self
if len(wrappers) > 1:
# TODO resolve issue with multiple wrappers -> multiple types
debug.warning('multiple wrappers found %s %s',
self.base_func, wrappers)
f = wrappers[0]
if isinstance(f, (Instance, Function)):
f.decorates = self
debug.dbg('decorator end %s', f)
return f
def names_dicts(self, search_global):
if search_global:
yield self.names_dict
else:
for names_dict in compiled.magic_function_class.names_dicts(False):
yield names_dict
@Python3Method
def py__call__(self, evaluator, params):
if self.base.is_generator():
return [iterable.Generator(evaluator, self, params)]
else:
return FunctionExecution(evaluator, self, params).get_return_types()
def __getattr__(self, name):
return getattr(self.base_func, name)
def __repr__(self):
dec = ''
if self.decorates is not None:
dec = " decorates " + repr(self.decorates)
return "<e%s of %s%s>" % (type(self).__name__, self.base_func, dec)
class LambdaWrapper(Function):
def get_decorated_func(self):
return self
class FunctionExecution(Executed):
"""
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.
"""
type = 'funcdef'
def __init__(self, evaluator, base, *args, **kwargs):
super(FunctionExecution, self).__init__(evaluator, base, *args, **kwargs)
self._copy_dict = {}
new_func = helpers.deep_ast_copy(base.base_func, self, self._copy_dict)
self.children = new_func.children
self.names_dict = new_func.names_dict
@memoize_default(default=())
@recursion.execution_recursion_decorator
def get_return_types(self, check_yields=False):
func = self.base
if func.isinstance(LambdaWrapper):
return self._evaluator.eval_element(self.children[-1])
if func.listeners:
# Feed the listeners, with the params.
for listener in func.listeners:
listener.execute(self._get_params())
# If we do have listeners, that means that there's not a regular
# execution ongoing. In this case Jedi is interested in the
# inserted params, not in the actual execution of the function.
return []
if check_yields:
types = []
returns = self.yields
else:
returns = self.returns
types = list(docstrings.find_return_types(self._evaluator, func))
for r in returns:
check = flow_analysis.break_check(self._evaluator, self, r)
if check is flow_analysis.UNREACHABLE:
debug.dbg('Return unreachable: %s', r)
else:
types += self._evaluator.eval_element(r.children[1])
if check is flow_analysis.REACHABLE:
debug.dbg('Return reachable: %s', r)
break
return types
def names_dicts(self, search_global):
yield self.names_dict
@memoize_default(default=NO_DEFAULT)
def _get_params(self):
"""
This returns the params for an TODO and is injected as a
'hack' into the tree.Function class.
This needs to be here, because Instance can have __init__ functions,
which act the same way as normal functions.
"""
return param.get_params(self._evaluator, self.base, self.var_args)
def param_by_name(self, name):
return [n for n in self._get_params() if str(n) == name][0]
def name_for_position(self, position):
return tree.Function.name_for_position(self, position)
def _copy_list(self, lst):
"""
Copies a list attribute of a parser 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.
"""
objects = []
for element in lst:
self._scope_copy(element.parent)
copied = helpers.deep_ast_copy(element, self._copy_dict)
objects.append(copied)
return objects
def __getattr__(self, name):
if name not in ['start_pos', 'end_pos', 'imports', 'name', 'type']:
raise AttributeError('Tried to access %s: %s. Why?' % (name, self))
return getattr(self.base, name)
def _scope_copy(self, scope):
raise NotImplementedError
""" Copies a scope (e.g. `if foo:`) in an execution """
if scope != self.base.base_func:
# Just make sure the parents been copied.
self._scope_copy(scope.parent)
helpers.deep_ast_copy(scope, self._copy_dict)
@common.safe_property
@memoize_default([])
def returns(self):
return tree.Scope._search_in_scope(self, tree.ReturnStmt)
@common.safe_property
@memoize_default([])
def yields(self):
return tree.Scope._search_in_scope(self, tree.YieldExpr)
@common.safe_property
@memoize_default([])
def statements(self):
return tree.Scope._search_in_scope(self, tree.ExprStmt)
@common.safe_property
@memoize_default([])
def subscopes(self):
return tree.Scope._search_in_scope(self, tree.Scope)
def __repr__(self):
return "<%s of %s>" % (type(self).__name__, self.base)
class GlobalName(helpers.FakeName):
def __init__(self, name):
"""
We need to mark global names somehow. Otherwise they are just normal
names that are not definitions.
"""
super(GlobalName, self).__init__(name.value, name.parent,
name.start_pos, is_definition=True)
class ModuleWrapper(use_metaclass(CachedMetaClass, tree.Module, Wrapper)):
def __init__(self, evaluator, module):
self._evaluator = evaluator
self.base = self._module = module
def names_dicts(self, search_global):
yield self.base.names_dict
yield self._module_attributes_dict()
for star_module in self.star_imports():
yield star_module.names_dict
yield dict((str(n), [GlobalName(n)]) for n in self.base.global_names)
yield self._sub_modules_dict()
# I'm not sure if the star import cache is really that effective anymore
# with all the other really fast import caches. Recheck. Also we would need
# to push the star imports into Evaluator.modules, if we reenable this.
#@cache_star_import
@memoize_default([])
def star_imports(self):
modules = []
for i in self.base.imports:
if i.is_star_import():
name = i.star_import_name()
new = imports.ImportWrapper(self._evaluator, name).follow()
for module in new:
if isinstance(module, tree.Module):
modules += module.star_imports()
modules += new
return modules
@memoize_default()
def _module_attributes_dict(self):
def parent_callback():
return self._evaluator.execute(compiled.create(self._evaluator, str))[0]
names = ['__file__', '__package__', '__doc__', '__name__']
# All the additional module attributes are strings.
return dict((n, [helpers.LazyName(n, parent_callback, is_definition=True)])
for n in names)
@property
@memoize_default()
def name(self):
return helpers.FakeName(unicode(self.base.name), self, (1, 0))
def _get_init_directory(self):
for suffix, _, _ in imp.get_suffixes():
ending = '__init__' + suffix
if self.py__file__().endswith(ending):
# Remove the ending, including the separator.
return self.py__file__()[:-len(ending) - 1]
return None
def py__name__(self):
for name, module in self._evaluator.modules.items():
if module == self:
return name
return '__main__'
def py__file__(self):
"""
In contrast to Python's __file__ can be None.
"""
if self._module.path is None:
return None
return os.path.abspath(self._module.path)
def py__package__(self):
if self._get_init_directory() is None:
return re.sub(r'\.?[^\.]+$', '', self.py__name__())
else:
return self.py__name__()
@property
def py__path__(self):
"""
Not seen here, since it's a property. The callback actually uses a
variable, so use it like::
foo.py__path__(sys_path)
In case of a package, this returns Python's __path__ attribute, which
is a list of paths (strings).
Raises an AttributeError if the module is not a package.
"""
def return_value(search_path):
init_path = self.py__file__()
if os.path.basename(init_path) == '__init__.py':
with open(init_path, 'rb') as f:
content = common.source_to_unicode(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 on sys_path or whatever the search_path is.
paths = set()
for s in search_path:
other = os.path.join(s, unicode(self.name))
if os.path.isdir(other):
paths.add(other)
return list(paths)
# Default to this.
return [path]
path = self._get_init_directory()
if path is None:
raise AttributeError('Only packages have __path__ attributes.')
else:
return return_value
@memoize_default()
def _sub_modules_dict(self):
"""
Lists modules in the directory of this module (if this module is a
package).
"""
path = self._module.path
names = {}
if path is not None and path.endswith(os.path.sep + '__init__.py'):
mods = pkgutil.iter_modules([os.path.dirname(path)])
for module_loader, name, is_pkg in mods:
fake_n = helpers.FakeName(name)
# It's obviously a relative import to the current module.
imp = helpers.FakeImport(fake_n, self, level=1)
fake_n.parent = imp
names[name] = [fake_n]
# TODO add something like this in the future, its cleaner than the
# import hacks.
# ``os.path`` is a hardcoded exception, because it's a
# ``sys.modules`` modification.
#if str(self.name) == 'os':
# names.append(helpers.FakeName('path', parent=self))
return names
def __getattr__(self, name):
return getattr(self._module, name)
def __repr__(self):
return "<%s: %s>" % (type(self).__name__, self._module)

256
jedi/evaluate/stdlib.py Normal file
View File

@@ -0,0 +1,256 @@
"""
Implementations of standard library functions, because it's not possible to
understand them with Jedi.
To add a new implementation, create a function and add it to the
``_implemented`` dict at the bottom of this module.
"""
import collections
import re
from jedi._compatibility import unicode
from jedi.evaluate import compiled
from jedi.evaluate import representation as er
from jedi.evaluate import iterable
from jedi.parser import Parser
from jedi.parser import tree
from jedi import debug
from jedi.evaluate import precedence
from jedi.evaluate import param
class NotInStdLib(LookupError):
pass
def execute(evaluator, obj, params):
try:
obj_name = str(obj.name)
except AttributeError:
pass
else:
if obj.parent == compiled.builtin:
module_name = 'builtins'
elif isinstance(obj.parent, tree.Module):
module_name = str(obj.parent.name)
else:
module_name = ''
# for now we just support builtin functions.
try:
return _implemented[module_name][obj_name](evaluator, obj, params)
except KeyError:
pass
raise NotInStdLib()
def _follow_param(evaluator, params, index):
try:
key, values = list(params.unpack())[index]
except IndexError:
return []
else:
return iterable.unite(evaluator.eval_element(v) for v in values)
def argument_clinic(string, want_obj=False, want_scope=False):
"""
Works like Argument Clinic (PEP 436), to validate function params.
"""
clinic_args = []
allow_kwargs = False
optional = False
while string:
# Optional arguments have to begin with a bracket. And should always be
# at the end of the arguments. This is therefore not a proper argument
# clinic implementation. `range()` for exmple allows an optional start
# value at the beginning.
match = re.match('(?:(?:(\[),? ?|, ?|)(\w+)|, ?/)\]*', string)
string = string[len(match.group(0)):]
if not match.group(2): # A slash -> allow named arguments
allow_kwargs = True
continue
optional = optional or bool(match.group(1))
word = match.group(2)
clinic_args.append((word, optional, allow_kwargs))
def f(func):
def wrapper(evaluator, obj, arguments):
try:
lst = list(arguments.eval_argument_clinic(clinic_args))
except ValueError:
return []
else:
kwargs = {}
if want_scope:
kwargs['scope'] = arguments.scope()
if want_obj:
kwargs['obj'] = obj
return func(evaluator, *lst, **kwargs)
return wrapper
return f
@argument_clinic('object, name[, default], /')
def builtins_getattr(evaluator, objects, names, defaults=None):
types = []
# follow the first param
for obj in objects:
if not isinstance(obj, (er.Instance, er.Class, tree.Module, compiled.CompiledObject)):
debug.warning('getattr called without instance')
continue
for name in names:
if precedence.is_string(name):
return evaluator.find_types(obj, name.obj)
else:
debug.warning('getattr called without str')
continue
return types
@argument_clinic('object[, bases, dict], /')
def builtins_type(evaluator, objects, bases, dicts):
if bases or dicts:
# metaclass... maybe someday...
return []
else:
return [o.base for o in objects if isinstance(o, er.Instance)]
class SuperInstance(er.Instance):
"""To be used like the object ``super`` returns."""
def __init__(self, evaluator, cls):
su = cls.py_mro()[1]
super().__init__(evaluator, su and su[0] or self)
@argument_clinic('[type[, obj]], /', want_scope=True)
def builtins_super(evaluator, types, objects, scope):
# TODO make this able to detect multiple inheritance super
accept = (tree.Function, er.FunctionExecution)
if scope.isinstance(*accept):
wanted = (tree.Class, er.Instance)
cls = scope.get_parent_until(accept + wanted,
include_current=False)
if isinstance(cls, wanted):
if isinstance(cls, tree.Class):
cls = er.Class(evaluator, cls)
elif isinstance(cls, er.Instance):
cls = cls.base
su = cls.py__bases__(evaluator)
if su:
return evaluator.execute(su[0])
return []
@argument_clinic('sequence, /', want_obj=True)
def builtins_reversed(evaluator, sequences, obj):
# Unpack the iterator values
objects = tuple(iterable.get_iterator_types(sequences))
rev = [iterable.AlreadyEvaluated([o]) for o in reversed(objects)]
# Repack iterator values and then run it the normal way. This is
# necessary, because `reversed` is a function and autocompletion
# would fail in certain cases like `reversed(x).__iter__` if we
# just returned the result directly.
rev = iterable.AlreadyEvaluated(
[iterable.FakeSequence(evaluator, rev, 'list')]
)
return [er.Instance(evaluator, obj, param.Arguments(evaluator, [rev]))]
@argument_clinic('obj, type, /')
def builtins_isinstance(evaluator, objects, types):
bool_results = set([])
for o in objects:
try:
mro_func = o.py__class__(evaluator).py__mro__
except AttributeError:
# This is temporary. Everything should have a class attribute in
# Python?! Maybe we'll leave it here, because some numpy objects or
# whatever might not.
return [compiled.true_obj, compiled.false_obj]
mro = mro_func(evaluator)
for cls_or_tup in types:
if cls_or_tup.is_class():
bool_results.add(cls_or_tup in mro)
else:
# Check for tuples.
classes = iterable.get_iterator_types([cls_or_tup])
bool_results.add(any(cls in mro for cls in classes))
return [compiled.keyword_from_value(x) for x in bool_results]
def collections_namedtuple(evaluator, obj, params):
"""
Implementation of the namedtuple function.
This has to be done by processing the namedtuple class template and
evaluating the result.
.. note:: |jedi| only supports namedtuples on Python >2.6.
"""
# Namedtuples are not supported on Python 2.6
if not hasattr(collections, '_class_template'):
return []
# Process arguments
name = _follow_param(evaluator, params, 0)[0].obj
_fields = _follow_param(evaluator, params, 1)[0]
if isinstance(_fields, compiled.CompiledObject):
fields = _fields.obj.replace(',', ' ').split()
elif isinstance(_fields, iterable.Array):
try:
fields = [v.obj for v in _fields.values()]
except AttributeError:
return []
else:
return []
# Build source
source = collections._class_template.format(
typename=name,
field_names=fields,
num_fields=len(fields),
arg_list=', '.join(fields),
repr_fmt=', '.join(collections._repr_template.format(name=name) for name in fields),
field_defs='\n'.join(collections._field_template.format(index=index, name=name)
for index, name in enumerate(fields))
)
# Parse source
generated_class = Parser(evaluator.grammar, unicode(source)).module.subscopes[0]
return [er.Class(evaluator, generated_class)]
@argument_clinic('first, /')
def _return_first_param(evaluator, firsts):
return firsts
_implemented = {
'builtins': {
'getattr': builtins_getattr,
'type': builtins_type,
'super': builtins_super,
'reversed': builtins_reversed,
'isinstance': builtins_isinstance,
},
'copy': {
'copy': _return_first_param,
'deepcopy': _return_first_param,
},
'json': {
'load': lambda *args: [],
'loads': lambda *args: [],
},
'collections': {
'namedtuple': collections_namedtuple,
},
}

247
jedi/evaluate/sys_path.py Normal file
View File

@@ -0,0 +1,247 @@
import glob
import os
import sys
from jedi._compatibility import exec_function, unicode
from jedi.parser import tree
from jedi.parser import Parser
from jedi.evaluate.cache import memoize_default
from jedi import debug
from jedi import common
from jedi import cache
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 = _get_venv_sitepackages(venv)
if p not in sys_path:
sys_path.insert(0, p)
# Add all egg-links from the virtualenv.
for egg_link in glob.glob(os.path.join(p, '*.egg-link')):
with open(egg_link) as fd:
sys_path.insert(0, fd.readline().rstrip())
check_virtual_env(sys.path)
return [p for p in sys.path if p != ""]
def _get_venv_sitepackages(venv):
if os.name == 'nt':
p = os.path.join(venv, 'lib', 'site-packages')
else:
p = os.path.join(venv, 'lib', 'python%d.%d' % sys.version_info[:2],
'site-packages')
return p
def _execute_code(module_path, code):
c = "import os; from os.path import *; result=%s"
variables = {'__file__': module_path}
try:
exec_function(c % code, variables)
except Exception:
debug.warning('sys.path manipulation detected, but failed to evaluate.')
else:
try:
res = variables['result']
if isinstance(res, str):
return [os.path.abspath(res)]
except KeyError:
pass
return []
def _paths_from_assignment(evaluator, expr_stmt):
"""
Extracts the assigned strings from an assignment that looks as follows::
>>> sys.path[0:0] = ['module/path', 'another/module/path']
This function is in general pretty tolerant (and therefore 'buggy').
However, it's not a big issue usually to add more paths to Jedi's sys_path,
because it will only affect Jedi in very random situations and by adding
more paths than necessary, it usually benefits the general user.
"""
for assignee, operator in zip(expr_stmt.children[::2], expr_stmt.children[1::2]):
try:
assert operator in ['=', '+=']
assert tree.is_node(assignee, 'power') and len(assignee.children) > 1
c = assignee.children
assert c[0].type == 'name' and c[0].value == 'sys'
trailer = c[1]
assert trailer.children[0] == '.' and trailer.children[1].value == 'path'
# TODO Essentially we're not checking details on sys.path
# manipulation. Both assigment of the sys.path and changing/adding
# parts of the sys.path are the same: They get added to the current
# sys.path.
"""
execution = c[2]
assert execution.children[0] == '['
subscript = execution.children[1]
assert subscript.type == 'subscript'
assert ':' in subscript.children
"""
except AssertionError:
continue
from jedi.evaluate.iterable import get_iterator_types
from jedi.evaluate.precedence import is_string
for val in get_iterator_types(evaluator.eval_statement(expr_stmt)):
if is_string(val):
yield val.obj
def _paths_from_list_modifications(module_path, trailer1, trailer2):
""" extract the path from either "sys.path.append" or "sys.path.insert" """
# Guarantee that both are trailers, the first one a name and the second one
# a function execution with at least one param.
if not (tree.is_node(trailer1, 'trailer') and trailer1.children[0] == '.'
and tree.is_node(trailer2, 'trailer') and trailer2.children[0] == '('
and len(trailer2.children) == 3):
return []
name = trailer1.children[1].value
if name not in ['insert', 'append']:
return []
arg = trailer2.children[1]
if name == 'insert' and len(arg.children) in (3, 4): # Possible trailing comma.
arg = arg.children[2]
return _execute_code(module_path, arg.get_code())
def _check_module(evaluator, module):
def get_sys_path_powers(names):
for name in names:
power = name.parent.parent
if tree.is_node(power, 'power'):
c = power.children
if isinstance(c[0], tree.Name) and c[0].value == 'sys' \
and tree.is_node(c[1], 'trailer'):
n = c[1].children[1]
if isinstance(n, tree.Name) and n.value == 'path':
yield name, power
sys_path = list(get_sys_path()) # copy
try:
possible_names = module.used_names['path']
except KeyError:
pass
else:
for name, power in get_sys_path_powers(possible_names):
stmt = name.get_definition()
if len(power.children) >= 4:
sys_path.extend(_paths_from_list_modifications(module.path, *power.children[2:4]))
elif name.get_definition().type == 'expr_stmt':
sys_path.extend(_paths_from_assignment(evaluator, stmt))
return sys_path
@memoize_default(evaluator_is_first_arg=True, default=[])
def sys_path_with_modifications(evaluator, module):
if module.path is None:
# Support for modules without a path is bad, therefore return the
# normal path.
return list(get_sys_path())
curdir = os.path.abspath(os.curdir)
with common.ignored(OSError):
os.chdir(os.path.dirname(module.path))
buildout_script_paths = set()
result = _check_module(evaluator, module)
result += _detect_django_path(module.path)
for buildout_script in _get_buildout_scripts(module.path):
for path in _get_paths_from_buildout_script(evaluator, buildout_script):
buildout_script_paths.add(path)
# cleanup, back to old directory
os.chdir(curdir)
return list(result) + list(buildout_script_paths)
def _get_paths_from_buildout_script(evaluator, buildout_script):
def load(buildout_script):
try:
with open(buildout_script, 'rb') as f:
source = common.source_to_unicode(f.read())
except IOError:
debug.dbg('Error trying to read buildout_script: %s', buildout_script)
return
p = Parser(evaluator.grammar, source, buildout_script)
cache.save_parser(buildout_script, p)
return p.module
cached = cache.load_parser(buildout_script)
module = cached and cached.module or load(buildout_script)
if not module:
return
for path in _check_module(evaluator, module):
yield path
def traverse_parents(path):
while True:
new = os.path.dirname(path)
if new == path:
return
path = new
yield path
def _get_parent_dir_with_file(path, filename):
for parent in traverse_parents(path):
if os.path.isfile(os.path.join(parent, filename)):
return parent
return None
def _detect_django_path(module_path):
""" Detects the path of the very well known Django library (if used) """
result = []
for parent in traverse_parents(module_path):
with common.ignored(IOError):
with open(parent + os.path.sep + 'manage.py'):
debug.dbg('Found django path: %s', module_path)
result.append(parent)
return result
def _get_buildout_scripts(module_path):
"""
if there is a 'buildout.cfg' file in one of the parent directories of the
given module it will return a list of all files in the buildout bin
directory that look like python files.
:param module_path: absolute path to the module.
:type module_path: str
"""
project_root = _get_parent_dir_with_file(module_path, 'buildout.cfg')
if not project_root:
return []
bin_path = os.path.join(project_root, 'bin')
if not os.path.exists(bin_path):
return []
extra_module_paths = []
for filename in os.listdir(bin_path):
try:
filepath = os.path.join(bin_path, filename)
with open(filepath, 'r') as f:
firstline = f.readline()
if firstline.startswith('#!') and 'python' in firstline:
extra_module_paths.append(filepath)
except IOError as e:
# either permission error or race cond. because file got deleted
# ignore
debug.warning(unicode(e))
continue
return extra_module_paths

View File

@@ -1,901 +0,0 @@
"""
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
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 DecoratorNotFound(LookupError):
"""
Decorators are sometimes not found, if that happens, that error is raised.
"""
pass
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.base.get_parent_until(*args, **kwargs)
@property
def parent(self):
return self.base.parent
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_init_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 func.params[0].used_vars[0].names[0]
except IndexError:
return None
def get_self_properties(self):
def add_self_dot_name(name):
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 self_name:
# Check the __init__ function.
if sub.name.get_code() == '__init__':
sub = self.get_init_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():
if s == self.base:
# I don't know how this could happen... But saw it once.
continue
names += Instance(s).get_self_properties()
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_properties()
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_properties()
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)):
"""
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()
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)
for command in self.var.get_commands()]
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
@property
@cache.memoize_default()
def _decorated_func(self):
"""
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 = evaluate.follow_statement(dec)
if not len(dec_results):
debug.warning('decorator func not found: %s in stmt %s' %
(self.base_func, dec))
return None
if len(dec_results) > 1:
debug.warning('multiple decorators found', self.base_func,
dec_results)
decorator = dec_results.pop()
# Create param array.
old_func = Function(f, is_decorated=True)
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):
if self._decorated_func is None:
raise DecoratorNotFound()
if self._decorated_func == self.base_func:
return self
return self._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
@cache.memoize_default(default=())
@recursion.ExecutionRecursionDecorator
def get_return_types(self, evaluate_generator=False):
""" Get the return types of a function. """
stmts = []
if self.base.parent == builtin.Builtin.scope \
and not isinstance(self.base, (Generator, Array)):
func_name = str(self.base.name)
# some implementations of builtins:
if func_name == 'getattr':
# follow the first param
try:
objects = self.follow_var_arg(0)
names = self.follow_var_arg(1)
except IndexError:
debug.warning('getattr() called with to few args.')
return []
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 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,
self.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 self.base.isinstance(Class):
# There maybe executions of executions.
stmts = [Instance(self.base, self.var_args)]
elif isinstance(self.base, Generator):
return self.base.iter_content()
else:
# Don't do this with exceptions, as usual, because some deeper
# exceptions could be catched - and I wouldn't know what happened.
try:
self.base.returns
except (AttributeError, DecoratorNotFound):
if hasattr(self.base, 'execute_subscope_by_name'):
try:
stmts = self.base.execute_subscope_by_name('__call__',
self.var_args)
except KeyError:
debug.warning("no __call__ func available", self.base)
else:
debug.warning("no execution possible", self.base)
else:
stmts = self._get_function_returns(evaluate_generator)
debug.dbg('exec result: %s in %s' % (stmts, self))
return imports.strip_imports(stmts)
def _get_function_returns(self, evaluate_generator):
""" A normal Function execution """
# Feed the listeners, with the params.
for listener in self.base.listeners:
listener.execute(self.get_params())
func = self.base.get_decorated_func()
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.base
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.base, InstanceElement):
# Care for self -> just exclude it and add the instance
start_offset = 1
self_name = copy.copy(self.base.params[0].get_name())
self_name.parent = self.base.instance
result.append(self_name)
param_dict = {}
for param in self.base.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.base.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
if stmt.get_commands()[0] == '*':
arrays = evaluate.follow_call_list(stmt.get_commands()[1:])
# *args must be some sort of an array, otherwise -> ignore
for array in arrays:
for field_stmt in array: # yield from plz!
yield None, field_stmt
# **kwargs
elif stmt.get_commands()[0] == '**':
arrays = evaluate.follow_call_list(stmt.get_commands()[1:])
for array in arrays:
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) == 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_set_vars(self):
return self.get_defined_names()
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)
@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.base, 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.base, 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.base)
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
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:
with common.ignored(KeyError, IndexError):
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)

View File

@@ -1,424 +0,0 @@
"""
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()
self._parse(code)
@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()
self._parse(code)
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,127 +0,0 @@
from __future__ import with_statement
import copy
from jedi import common
from jedi import parsing_representation as pr
def fast_parent_copy(obj):
"""
Much, much faster than copy.deepcopy, but just for certain elements.
"""
new_elements = {}
def recursion(obj):
new_obj = copy.copy(obj)
new_elements[obj] = new_obj
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'] and value is not None:
if key == 'parent' and '_parent' in items:
# parent can be a property
continue
with common.ignored(KeyError):
setattr(new_obj, key, new_elements[value])
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, (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, (pr.Simple, pr.Call)):
copied_list[i] = recursion(el)
elif isinstance(el, list):
copied_list[i] = list_rec(el)
return copied_list
return recursion(obj)
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:
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 search_function_definition(stmt, pos):
"""
Returns the function Call that matches the position before.
"""
# 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, index, False
return None, 0, False

View File

@@ -1,339 +0,0 @@
"""
: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 sys
import itertools
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
class ModuleNotFound(Exception):
pass
class ImportPath(pr.Base):
"""
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 []
def get_parent_until(self):
return None
GlobalNamespace = _GlobalNamespace()
def __init__(self, import_stmt, is_like_search=False, kill_count=0,
direct_resolve=False):
self.import_stmt = import_stmt
self.is_like_search = is_like_search
self.direct_resolve = direct_resolve
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
# rest is import_path resolution
self.import_path = []
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:
self.import_path.append(import_stmt.namespace.names[0])
else:
self.import_path += import_stmt.namespace.names
for i in range(kill_count + int(is_like_search)):
self.import_path.pop()
def __repr__(self):
return '<%s: %s>' % (type(self).__name__, self.import_stmt)
def is_nested_import(self):
"""
This checks for the special case of nested imports, without aliases and
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
def get_nested_import(self, parent):
"""
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)
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
debug.dbg('Generated a nested import: %s' % new)
return new
def get_defined_names(self, on_import_stmt=False):
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.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])
if self.import_stmt.relative_count:
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, 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_of_scope(scope,
include_builtin=False):
for n in scope_names:
if self.import_stmt.from_ns is None \
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
# automatically implies that there must not be
# any non-module scope.
continue
names.append(n)
return names
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()
names = []
for module_loader, name, is_pkg in pkgutil.iter_modules(search_path):
inf_pos = (float('inf'), float('inf'))
names.append(pr.Name(self.GlobalNamespace, [(name, inf_pos)],
inf_pos, inf_pos, self.import_stmt))
return names
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.
parts = self.file_path.split(os.path.sep)
in_path = []
if self.import_path:
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 in_path + modules.sys_path_with_modifications(module)
def follow(self, is_goto=False):
"""
Returns the imported modules.
"""
if evaluate.follow_statement.push_stmt(self.import_stmt):
# check recursion
return []
if self.import_path:
try:
scope, rest = self._follow_file_system()
except ModuleNotFound:
debug.warning('Module not found: ' + str(self.import_stmt))
evaluate.follow_statement.pop_stmt()
return []
scopes = [scope]
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 = []
elif rest:
if is_goto:
scopes = itertools.chain.from_iterable(
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)
scopes = list(scopes)
if self.is_nested_import():
scopes.append(self.get_nested_import(scope))
else:
scopes = [ImportPath.GlobalNamespace]
debug.dbg('after import', scopes)
evaluate.follow_statement.pop_stmt()
return scopes
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 _follow_file_system(self):
"""
Find a module with a path (of the module, like usb.backend.libusb10).
"""
def follow_str(ns_path, string):
debug.dbg('follow_module', ns_path, string)
path = None
if ns_path:
path = ns_path
elif self.import_stmt.relative_count:
path = self.get_relative_path()
global imports_processed
imports_processed += 1
importing = None
if path is not None:
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:
importing = find_module(string)
except ImportError:
sys.path = temp
raise
sys.path = temp
return importing
if self.file_path:
sys_path_mod = list(self.sys_path_with_modifications())
sys_path_mod.insert(0, self.file_path)
else:
sys_path_mod = list(modules.get_sys_path())
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[1], s)
except ImportError:
if self.import_stmt.relative_count \
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__')
if current_namespace[1]:
rest = self.import_path[i:]
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]
f = None
if is_package_directory or current_namespace[0]:
# is a directory module
if is_package_directory:
path += '/__init__.py'
with open(path) as f:
source = f.read()
else:
source = current_namespace[0].read()
current_namespace[0].close()
if path.endswith('.py'):
f = modules.Module(path, source)
else:
f = builtin.BuiltinModule(path=path)
else:
f = builtin.BuiltinModule(name=path)
return f.parser.module, rest
def strip_imports(scopes):
"""
Here we strip the imports - they don't get resolved necessarily.
Really used anymore? Merge with remove_star_imports?
"""
result = []
for s in scopes:
if isinstance(s, pr.Import):
result += ImportPath(s).follow()
else:
result.append(s)
return result
@cache.cache_star_import
def remove_star_imports(scope, ignored_modules=()):
"""
Check a module for star imports:
>>> from module import *
and follow these modules.
"""
modules = strip_imports(i for i in scope.get_imports() if i.star)
new = []
for m in modules:
if m not in ignored_modules:
new += remove_star_imports(m, modules)
modules += new
# Filter duplicate modules.
return set(modules)

View File

@@ -1,388 +0,0 @@
"""
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
import re
import tokenizer as tokenize
import sys
import os
from ast import literal_eval
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 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=None):
super(Module, self).__init__(path=path)
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):
""" Just one time """
s = self.source
del self.source # memory efficiency
return s
class ModuleWithCursor(Module):
"""
Manages all files, that are parsed and caches them.
Important are the params source and path, one of them has to
be there.
:param source: The source code of the file.
:param path: The module path of the file or None.
:param position: The position, the user is currently in. Only important \
for the main file.
"""
def __init__(self, path, source, position):
super(ModuleWithCursor, self).__init__(path, source)
self.position = position
# this two are only used, because there is no nonlocal in Python 2
self._line_temp = None
self._relevant_temp = None
self.source = source
@property
def parser(self):
""" get the parser lazy """
if not self._parser:
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 = 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
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]
else:
self._line_length = len(line)
line = line + '\n'
# add lines with a backslash at the end
while 1:
self._line_temp -= 1
last_line = self.get_line(self._line_temp)
if last_line and last_line[-1] == '\\':
line = last_line[:-1] + ' ' + 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
open_brackets = ['(', '[', '{']
close_brackets = [')', ']', '}']
gen = tokenize.generate_tokens(fetch_line)
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
if last_type == token_type == tokenize.NAME:
string += ' '
if level > 0:
if tok in close_brackets:
level += 1
if tok in open_brackets:
level -= 1
elif tok == '.':
force_point = False
elif force_point:
# it is reversed, therefore a number is getting recognized
# as a floating point number
if token_type == tokenize.NUMBER and tok[0] == '.':
force_point = False
else:
break
elif tok in close_brackets:
level += 1
elif token_type in [tokenize.NAME, tokenize.STRING]:
force_point = True
elif token_type == tokenize.NUMBER:
pass
else:
break
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)
# string can still contain spaces at the end
return string[::-1].strip()
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.
"""
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
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 '')
def get_context(self):
pos = self._start_cursor_pos
while pos > (1, 0):
# 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
try:
yield self._get_path_until_cursor(start_pos=pos)
except StopIteration:
yield ''
pos = self._line_temp, self._column_temp
while True:
yield ''
def get_line(self, line_nr):
if not self._line_cache:
self._line_cache = self.source.splitlines()
if not self.source: # ''.splitlines() == []
self._line_cache = [self.source]
if line_nr == 0:
# This is a fix for the zeroth line. We need a newline there, for
# the backwards parser.
return ''
if line_nr < 0:
raise StopIteration()
try:
return self._line_cache[line_nr - 1]
except IndexError:
raise StopIteration()
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"
variables = {'__file__': module.path}
try:
exec_function(c % code, variables)
except Exception:
debug.warning('sys path detected, but failed to evaluate')
return None
try:
res = variables['result']
if isinstance(res, str):
return os.path.abspath(res)
else:
return None
except KeyError:
return None
def check_module(module):
try:
possible_stmts = module.used_names['path']
except KeyError:
return get_sys_path()
sys_path = list(get_sys_path()) # copy
for p in possible_stmts:
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, pr.Name) or len(n.names) != 3:
continue
if n.names[:2] != ('sys', 'path'):
continue
array_cmd = n.names[2]
if call.execution is None:
continue
exe = call.execution
if not (array_cmd == 'insert' and len(exe) == 2
or array_cmd == 'append' and len(exe) == 1):
continue
if array_cmd == 'insert':
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:
sys_path.insert(0, res)
debug.dbg('sys path inserted: %s' % res)
exe.type = exe_type
exe.values.insert(0, exe_pop)
elif array_cmd == 'append':
res = execute_code(exe.get_code())
if res is not None:
sys_path.append(res)
debug.dbg('sys path added: %s' % res)
return sys_path
if module.path is None:
return [] # support for modules without a path is intentionally bad.
curdir = os.path.abspath(os.curdir)
with common.ignored(OSError):
os.chdir(os.path.dirname(module.path))
result = check_module(module)
result += detect_django_path(module.path)
# cleanup, back to old directory
os.chdir(curdir)
return result
def detect_django_path(module_path):
""" Detects the path of the very well known Django library (if used) """
result = []
while True:
new = os.path.dirname(module_path)
# If the module_path doesn't change anymore, we're finished -> /
if new == module_path:
break
else:
module_path = new
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)
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')

393
jedi/parser/__init__.py Normal file
View File

@@ -0,0 +1,393 @@
"""
The ``Parser`` tries to convert the available Python code in an easy to read
format, something like an abstract syntax tree. The classes who represent this
tree, are sitting in the :mod:`jedi.parser.tree` module.
The Python module ``tokenize`` is a very important part in the ``Parser``,
because it splits the code into different words (tokens). Sometimes it looks a
bit messy. Sorry for that! You might ask now: "Why didn't you use the ``ast``
module for this? Well, ``ast`` does a very good job understanding proper Python
code, but fails to work as soon as there's a single line of broken code.
There's one important optimization that needs to be known: Statements are not
being parsed completely. ``Statement`` is just a representation of the tokens
within the statement. This lowers memory usage and cpu time and reduces the
complexity of the ``Parser`` (there's another parser sitting inside
``Statement``, which produces ``Array`` and ``Call``).
"""
import os
import re
from jedi.parser import tree as pt
from jedi.parser import tokenize
from jedi.parser import token
from jedi.parser.token import (DEDENT, INDENT, ENDMARKER, NEWLINE, NUMBER,
STRING, OP, ERRORTOKEN)
from jedi.parser.pgen2.pgen import generate_grammar
from jedi.parser.pgen2.parse import PgenParser
OPERATOR_KEYWORDS = 'and', 'for', 'if', 'else', 'in', 'is', 'lambda', 'not', 'or'
# Not used yet. In the future I intend to add something like KeywordStatement
STATEMENT_KEYWORDS = 'assert', 'del', 'global', 'nonlocal', 'raise', \
'return', 'yield', 'pass', 'continue', 'break'
_loaded_grammars = {}
def load_grammar(file='grammar3.4'):
# For now we only support two different Python syntax versions: The latest
# Python 3 and Python 2. This may change.
if file.startswith('grammar3'):
file = 'grammar3.4'
else:
file = 'grammar2.7'
global _loaded_grammars
path = os.path.join(os.path.dirname(__file__), file) + '.txt'
try:
return _loaded_grammars[path]
except KeyError:
return _loaded_grammars.setdefault(path, generate_grammar(path))
class ErrorStatement(object):
def __init__(self, stack, next_token, position_modifier, next_start_pos):
self.stack = stack
self._position_modifier = position_modifier
self.next_token = next_token
self._next_start_pos = next_start_pos
@property
def next_start_pos(self):
s = self._next_start_pos
return s[0] + self._position_modifier.line, s[1]
@property
def first_pos(self):
first_type, nodes = self.stack[0]
return nodes[0].start_pos
@property
def first_type(self):
first_type, nodes = self.stack[0]
return first_type
class ParserSyntaxError(object):
def __init__(self, message, position):
self.message = message
self.position = position
class Parser(object):
"""
This class is used to parse a Python file, it then divides them into a
class structure of different scopes.
:param grammar: The grammar object of pgen2. Loaded by load_grammar.
:param source: The codebase for the parser. Must be unicode.
:param module_path: The path of the module in the file system, may be None.
:type module_path: str
:param top_module: Use this module as a parent instead of `self.module`.
"""
def __init__(self, grammar, source, module_path=None, tokenizer=None):
self._ast_mapping = {
'expr_stmt': pt.ExprStmt,
'classdef': pt.Class,
'funcdef': pt.Function,
'file_input': pt.Module,
'import_name': pt.ImportName,
'import_from': pt.ImportFrom,
'break_stmt': pt.KeywordStatement,
'continue_stmt': pt.KeywordStatement,
'return_stmt': pt.ReturnStmt,
'raise_stmt': pt.KeywordStatement,
'yield_expr': pt.YieldExpr,
'del_stmt': pt.KeywordStatement,
'pass_stmt': pt.KeywordStatement,
'global_stmt': pt.GlobalStmt,
'nonlocal_stmt': pt.KeywordStatement,
'assert_stmt': pt.AssertStmt,
'if_stmt': pt.IfStmt,
'with_stmt': pt.WithStmt,
'for_stmt': pt.ForStmt,
'while_stmt': pt.WhileStmt,
'try_stmt': pt.TryStmt,
'comp_for': pt.CompFor,
'decorator': pt.Decorator,
'lambdef': pt.Lambda,
'old_lambdef': pt.Lambda,
'lambdef_nocond': pt.Lambda,
}
self.syntax_errors = []
self._global_names = []
self._omit_dedent_list = []
self._indent_counter = 0
self._last_failed_start_pos = (0, 0)
# TODO do print absolute import detection here.
#try:
# del python_grammar_no_print_statement.keywords["print"]
#except KeyError:
# pass # Doesn't exist in the Python 3 grammar.
#if self.options["print_function"]:
# python_grammar = pygram.python_grammar_no_print_statement
#else:
self._used_names = {}
self._scope_names_stack = [{}]
self._error_statement_stacks = []
added_newline = False
# The Python grammar needs a newline at the end of each statement.
if not source.endswith('\n'):
source += '\n'
added_newline = True
# For the fast parser.
self.position_modifier = pt.PositionModifier()
p = PgenParser(grammar, self.convert_node, self.convert_leaf,
self.error_recovery)
tokenizer = tokenizer or tokenize.source_tokens(source)
self.module = p.parse(self._tokenize(tokenizer))
if self.module.type != 'file_input':
# If there's only one statement, we get back a non-module. That's
# not what we want, we want a module, so we add it here:
self.module = self.convert_node(grammar,
grammar.symbol2number['file_input'],
[self.module])
if added_newline:
self.remove_last_newline()
self.module.used_names = self._used_names
self.module.path = module_path
self.module.global_names = self._global_names
self.module.error_statement_stacks = self._error_statement_stacks
def convert_node(self, grammar, type, children):
"""
Convert raw node information to a Node instance.
This is passed to the parser driver which calls it whenever a reduction of a
grammar rule produces a new complete node, so that the tree is build
strictly bottom-up.
"""
symbol = grammar.number2symbol[type]
try:
new_node = self._ast_mapping[symbol](children)
except KeyError:
new_node = pt.Node(symbol, children)
# We need to check raw_node always, because the same node can be
# returned by convert multiple times.
if symbol == 'global_stmt':
self._global_names += new_node.get_global_names()
elif isinstance(new_node, pt.Lambda):
new_node.names_dict = self._scope_names_stack.pop()
elif isinstance(new_node, (pt.ClassOrFunc, pt.Module)) \
and symbol in ('funcdef', 'classdef', 'file_input'):
# scope_name_stack handling
scope_names = self._scope_names_stack.pop()
if isinstance(new_node, pt.ClassOrFunc):
n = new_node.name
scope_names[n.value].remove(n)
# Set the func name of the current node
arr = self._scope_names_stack[-1].setdefault(n.value, [])
arr.append(n)
new_node.names_dict = scope_names
elif isinstance(new_node, pt.CompFor):
# The name definitions of comprehenions shouldn't be part of the
# current scope. They are part of the comprehension scope.
for n in new_node.get_defined_names():
self._scope_names_stack[-1][n.value].remove(n)
return new_node
def convert_leaf(self, grammar, type, value, prefix, start_pos):
#print('leaf', value, pytree.type_repr(type))
if type == tokenize.NAME:
if value in grammar.keywords:
if value in ('def', 'class', 'lambda'):
self._scope_names_stack.append({})
return pt.Keyword(self.position_modifier, value, start_pos, prefix)
else:
name = pt.Name(self.position_modifier, value, start_pos, prefix)
# Keep a listing of all used names
arr = self._used_names.setdefault(name.value, [])
arr.append(name)
arr = self._scope_names_stack[-1].setdefault(name.value, [])
arr.append(name)
return name
elif type == STRING:
return pt.String(self.position_modifier, value, start_pos, prefix)
elif type == NUMBER:
return pt.Number(self.position_modifier, value, start_pos, prefix)
elif type in (NEWLINE, ENDMARKER):
return pt.Whitespace(self.position_modifier, value, start_pos, prefix)
else:
return pt.Operator(self.position_modifier, value, start_pos, prefix)
def error_recovery(self, grammar, stack, typ, value, start_pos, prefix,
add_token_callback):
"""
This parser is written in a dynamic way, meaning that this parser
allows using different grammars (even non-Python). However, error
recovery is purely written for Python.
"""
def current_suite(stack):
# For now just discard everything that is not a suite or
# file_input, if we detect an error.
for index, (dfa, state, (typ, nodes)) in reversed(list(enumerate(stack))):
# `suite` can sometimes be only simple_stmt, not stmt.
symbol = grammar.number2symbol[typ]
if symbol == 'file_input':
break
elif symbol == 'suite' and len(nodes) > 1:
# suites without an indent in them get discarded.
break
elif symbol == 'simple_stmt' and len(nodes) > 1:
# simple_stmt can just be turned into a Node, if there are
# enough statements. Ignore the rest after that.
break
return index, symbol, nodes
index, symbol, nodes = current_suite(stack)
if symbol == 'simple_stmt':
index -= 2
(_, _, (typ, suite_nodes)) = stack[index]
symbol = grammar.number2symbol[typ]
suite_nodes.append(pt.Node(symbol, list(nodes)))
# Remove
nodes[:] = []
nodes = suite_nodes
stack[index]
#print('err', token.tok_name[typ], repr(value), start_pos, len(stack), index)
self._stack_removal(grammar, stack, index + 1, value, start_pos)
if typ == INDENT:
# For every deleted INDENT we have to delete a DEDENT as well.
# Otherwise the parser will get into trouble and DEDENT too early.
self._omit_dedent_list.append(self._indent_counter)
if value in ('import', 'from', 'class', 'def', 'try', 'while', 'return'):
# Those can always be new statements.
add_token_callback(typ, value, prefix, start_pos)
elif typ == DEDENT and symbol == 'suite':
# Close the current suite, with DEDENT.
# Note that this may cause some suites to not contain any
# statements at all. This is contrary to valid Python syntax. We
# keep incomplete suites in Jedi to be able to complete param names
# or `with ... as foo` names. If we want to use this parser for
# syntax checks, we have to check in a separate turn if suites
# contain statements or not. However, a second check is necessary
# anyway (compile.c does that for Python), because Python's grammar
# doesn't stop you from defining `continue` in a module, etc.
add_token_callback(typ, value, prefix, start_pos)
def _stack_removal(self, grammar, stack, start_index, value, start_pos):
def clear_names(children):
for c in children:
try:
clear_names(c.children)
except AttributeError:
if isinstance(c, pt.Name):
try:
self._scope_names_stack[-1][c.value].remove(c)
self._used_names[c.value].remove(c)
except ValueError:
pass # This may happen with CompFor.
for dfa, state, node in stack[start_index:]:
clear_names(children=node[1])
failed_stack = []
found = False
for dfa, state, (typ, nodes) in stack[start_index:]:
if nodes:
found = True
if found:
symbol = grammar.number2symbol[typ]
failed_stack.append((symbol, nodes))
if nodes and nodes[0] in ('def', 'class', 'lambda'):
self._scope_names_stack.pop()
if failed_stack:
err = ErrorStatement(failed_stack, value, self.position_modifier, start_pos)
self._error_statement_stacks.append(err)
self._last_failed_start_pos = start_pos
stack[start_index:] = []
def _tokenize(self, tokenizer):
for typ, value, start_pos, prefix in tokenizer:
#print(tokenize.tok_name[typ], repr(value), start_pos, repr(prefix))
if typ == DEDENT:
# We need to count indents, because if we just omit any DEDENT,
# we might omit them in the wrong place.
o = self._omit_dedent_list
if o and o[-1] == self._indent_counter:
o.pop()
continue
self._indent_counter -= 1
elif typ == INDENT:
self._indent_counter += 1
elif typ == ERRORTOKEN:
self._add_syntax_error('Strange token', start_pos)
continue
if typ == OP:
typ = token.opmap[value]
yield typ, value, prefix, start_pos
def _add_syntax_error(self, message, position):
self.syntax_errors.append(ParserSyntaxError(message, position))
def __repr__(self):
return "<%s: %s>" % (type(self).__name__, self.module)
def remove_last_newline(self):
"""
In all of this we need to work with _start_pos, because if we worked
with start_pos, we would need to check the position_modifier as well
(which is accounted for in the start_pos property).
"""
endmarker = self.module.children[-1]
# The newline is either in the endmarker as a prefix or the previous
# leaf as a newline token.
if endmarker.prefix.endswith('\n'):
endmarker.prefix = endmarker.prefix[:-1]
last_line = re.sub('.*\n', '', endmarker.prefix)
endmarker._start_pos = endmarker._start_pos[0] - 1, len(last_line)
else:
try:
newline = endmarker.get_previous()
except IndexError:
return # This means that the parser is empty.
while True:
if newline.value == '':
# Must be a DEDENT, just continue.
try:
newline = newline.get_previous()
except IndexError:
# If there's a statement that fails to be parsed, there
# will be no previous leaf. So just ignore it.
break
elif newline.value != '\n':
# This may happen if error correction strikes and removes
# a whole statement including '\n'.
break
else:
newline.value = ''
if self._last_failed_start_pos > newline._start_pos:
# It may be the case that there was a syntax error in a
# function. In that case error correction removes the
# right newline. So we use the previously assigned
# _last_failed_start_pos variable to account for that.
endmarker._start_pos = self._last_failed_start_pos
else:
endmarker._start_pos = newline._start_pos
break

580
jedi/parser/fast.py Normal file
View File

@@ -0,0 +1,580 @@
"""
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 itertools import chain
from jedi._compatibility import use_metaclass
from jedi import settings
from jedi.parser import Parser
from jedi.parser import tree
from jedi import cache
from jedi import debug
from jedi.parser.tokenize import (source_tokens, NEWLINE,
ENDMARKER, INDENT, DEDENT)
FLOWS = 'if', 'else', 'elif', 'while', 'with', 'try', 'except', 'finally', 'for'
class FastModule(tree.Module):
type = 'file_input'
def __init__(self, module_path):
super(FastModule, self).__init__([])
self.modules = []
self.reset_caches()
self.names_dict = {}
self.path = module_path
def reset_caches(self):
self.modules = []
try:
del self._used_names # Remove the used names cache.
except AttributeError:
pass # It was never used.
@property
@cache.underscore_memoization
def used_names(self):
return MergedNamesDict([m.used_names for m in self.modules])
@property
def global_names(self):
return [name for m in self.modules for name in m.global_names]
@property
def error_statement_stacks(self):
return [e for m in self.modules for e in m.error_statement_stacks]
def __repr__(self):
return "<fast.%s: %s@%s-%s>" % (type(self).__name__, self.name,
self.start_pos[0], self.end_pos[0])
# To avoid issues with with the `parser.Parser`, we need setters that do
# nothing, because if pickle comes along and sets those values.
@global_names.setter
def global_names(self, value):
pass
@error_statement_stacks.setter
def error_statement_stacks(self, value):
pass
@used_names.setter
def used_names(self, value):
pass
class MergedNamesDict(object):
def __init__(self, dicts):
self.dicts = dicts
def __iter__(self):
return iter(set(key for dct in self.dicts for key in dct))
def __getitem__(self, value):
return list(chain.from_iterable(dct.get(value, []) for dct in self.dicts))
def items(self):
dct = {}
for d in self.dicts:
for key, values in d.items():
try:
dct_values = dct[key]
dct_values += values
except KeyError:
dct[key] = list(values)
return dct.items()
def values(self):
lst = []
for dct in self.dicts:
lst += dct.values()
return lst
class CachedFastParser(type):
""" This is a metaclass for caching `FastParser`. """
def __call__(self, grammar, source, module_path=None):
if not settings.fast_parser:
return Parser(grammar, source, module_path)
pi = cache.parser_cache.get(module_path, None)
if pi is None or isinstance(pi.parser, Parser):
p = super(CachedFastParser, self).__call__(grammar, source, module_path)
else:
p = pi.parser # pi is a `cache.ParserCacheItem`
p.update(source)
return p
class ParserNode(object):
def __init__(self, fast_module, parser, source):
self._fast_module = fast_module
self.parent = None
self._node_children = []
self.source = source
self.hash = hash(source)
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
else:
self._rewrite_last_newline()
# We need to be able to reset the original children of a parser.
self._old_children = list(self._content_scope.children)
def _rewrite_last_newline(self):
"""
The ENDMARKER can contain a newline in the prefix. However this prefix
really belongs to the function - respectively to the next function or
parser node. If we don't rewrite that newline, we end up with a newline
in the wrong position, i.d. at the end of the file instead of in the
middle.
"""
c = self._content_scope.children
if tree.is_node(c[-1], 'suite'): # In a simple_stmt there's no DEDENT.
end_marker = self.parser.module.children[-1]
# Set the DEDENT prefix instead of the ENDMARKER.
c[-1].children[-1].prefix = end_marker.prefix
end_marker.prefix = ''
def __repr__(self):
module = self.parser.module
try:
return '<%s: %s-%s>' % (type(self).__name__, module.start_pos, module.end_pos)
except IndexError:
# There's no module yet.
return '<%s: empty>' % type(self).__name__
def reset_node(self):
"""
Removes changes that were applied in this class.
"""
self._node_children = []
scope = self._content_scope
scope.children = list(self._old_children)
try:
# This works if it's a MergedNamesDict.
# We are correcting it, because the MergedNamesDicts are artificial
# and can change after closing a node.
scope.names_dict = scope.names_dict.dicts[0]
except AttributeError:
pass
def close(self):
"""
Closes the current parser node. This means that after this no further
nodes should be added anymore.
"""
# We only need to replace the dict if multiple dictionaries are used:
if self._node_children:
dcts = [n.parser.module.names_dict for n in self._node_children]
# Need to insert the own node as well.
dcts.insert(0, self._content_scope.names_dict)
self._content_scope.names_dict = MergedNamesDict(dcts)
def parent_until_indent(self, indent=None):
if (indent is None or self._indent >= indent) and self.parent is not None:
self.close()
return self.parent.parent_until_indent(indent)
return self
@property
def _indent(self):
if not self.parent:
return 0
return self.parser.module.children[0].start_pos[1]
def add_node(self, node, line_offset):
"""Adding a node means adding a node that was already added earlier"""
# Changing the line offsets is very important, because if they don't
# fit, all the start_pos values will be wrong.
m = node.parser.module
node.parser.position_modifier.line = line_offset
self._fast_module.modules.append(m)
node.parent = self
self._node_children.append(node)
# Insert parser objects into current structure. We only need to set the
# parents and children in a good way.
scope = self._content_scope
for child in m.children:
child.parent = scope
scope.children.append(child)
return node
def all_sub_nodes(self):
"""
Returns all nodes including nested ones.
"""
for n in self._node_children:
yield n
for y in n.all_sub_nodes():
yield y
@cache.underscore_memoization # Should only happen once!
def remove_last_newline(self):
self.parser.remove_last_newline()
class FastParser(use_metaclass(CachedFastParser)):
_FLOWS_NEED_SPACE = 'if', 'elif', 'while', 'with', 'except', 'for'
_FLOWS_NEED_COLON = 'else', 'try', 'except', 'finally'
_keyword_re = re.compile('^[ \t]*(def |class |@|(?:%s)|(?:%s)\s*:)'
% ('|'.join(_FLOWS_NEED_SPACE),
'|'.join(_FLOWS_NEED_COLON)))
def __init__(self, grammar, source, module_path=None):
# set values like `tree.Module`.
self._grammar = grammar
self.module_path = module_path
self._reset_caches()
self.update(source)
def _reset_caches(self):
self.module = FastModule(self.module_path)
self.current_node = ParserNode(self.module, self, '')
def update(self, source):
# For testing purposes: It is important that the number of parsers used
# can be minimized. With these variables we can test against that.
self.number_parsers_used = 0
self.number_of_splits = 0
self.number_of_misses = 0
self.module.reset_caches()
try:
self._parse(source)
except:
# FastParser is cached, be careful with exceptions.
self._reset_caches()
raise
def _split_parts(self, source):
"""
Split the source code into different parts. This makes it possible to
parse each part seperately and therefore cache parts of the file and
not everything.
"""
def gen_part():
text = ''.join(current_lines)
del current_lines[:]
self.number_of_splits += 1
return text
def just_newlines(current_lines):
for line in current_lines:
line = line.lstrip('\t \n\r')
if line and line[0] != '#':
return False
return True
# Split only new lines. Distinction between \r\n is the tokenizer's
# job.
# It seems like there's no problem with form feed characters here,
# because we're not counting lines.
self._lines = source.splitlines(True)
current_lines = []
is_decorator = False
# Use -1, because that indent is always smaller than any other.
indent_list = [-1, 0]
new_indent = False
parentheses_level = 0
flow_indent = None
previous_line = None
# All things within flows are simply being ignored.
for i, l in enumerate(self._lines):
# Handle backslash newline escaping.
if l.endswith('\\\n') or l.endswith('\\\r\n'):
if previous_line is not None:
previous_line += l
else:
previous_line = l
continue
if previous_line is not None:
l = previous_line + l
previous_line = None
# check for dedents
s = l.lstrip('\t \n\r')
indent = len(l) - len(s)
if not s or s[0] == '#':
current_lines.append(l) # Just ignore comments and blank lines
continue
if new_indent:
if indent > indent_list[-2]:
# Set the actual indent, not just the random old indent + 1.
indent_list[-1] = indent
new_indent = False
while indent <= indent_list[-2]: # -> dedent
indent_list.pop()
# This automatically resets the flow_indent if there was a
# dedent or a flow just on one line (with one simple_stmt).
new_indent = False
if flow_indent is None and current_lines and not parentheses_level:
yield gen_part()
flow_indent = None
# Check lines for functions/classes and split the code there.
if flow_indent is None:
m = self._keyword_re.match(l)
if m:
# Strip whitespace and colon from flows as a check.
if m.group(1).strip(' \t\r\n:') in FLOWS:
if not parentheses_level:
flow_indent = indent
else:
if not is_decorator and not just_newlines(current_lines):
yield gen_part()
is_decorator = '@' == m.group(1)
if not is_decorator:
parentheses_level = 0
# The new indent needs to be higher
indent_list.append(indent + 1)
new_indent = True
elif is_decorator:
is_decorator = False
parentheses_level = \
max(0, (l.count('(') + l.count('[') + l.count('{')
- l.count(')') - l.count(']') - l.count('}')))
current_lines.append(l)
if current_lines:
yield gen_part()
def _parse(self, source):
""" :type source: str """
added_newline = False
if not source or source[-1] != '\n':
# To be compatible with Pythons grammar, we need a newline at the
# end. The parser would handle it, but since the fast parser abuses
# the normal parser in various ways, we need to care for this
# ourselves.
source += '\n'
added_newline = True
next_line_offset = line_offset = 0
start = 0
nodes = list(self.current_node.all_sub_nodes())
# Now we can reset the node, because we have all the old nodes.
self.current_node.reset_node()
last_end_line = 1
for code_part in self._split_parts(source):
next_line_offset += code_part.count('\n')
# If the last code part parsed isn't equal to the current end_pos,
# we know that the parser went further (`def` start in a
# docstring). So just parse the next part.
if line_offset + 1 == last_end_line:
self.current_node = self._get_node(code_part, source[start:],
line_offset, nodes)
else:
# Means that some lines where not fully parsed. Parse it now.
# This is a very rare case. Should only happens with very
# strange code bits.
self.number_of_misses += 1
while last_end_line < next_line_offset + 1:
line_offset = last_end_line - 1
# We could calculate the src in a more complicated way to
# make caching here possible as well. However, this is
# complicated and error-prone. Since this is not very often
# called - just ignore it.
src = ''.join(self._lines[line_offset:])
self.current_node = self._get_node(code_part, src,
line_offset, nodes)
last_end_line = self.current_node.parser.module.end_pos[0]
debug.dbg('While parsing %s, line %s slowed down the fast parser.',
self.module_path, line_offset + 1)
line_offset = next_line_offset
start += len(code_part)
last_end_line = self.current_node.parser.module.end_pos[0]
if added_newline:
self.current_node.remove_last_newline()
# Now that the for loop is finished, we still want to close all nodes.
self.current_node = self.current_node.parent_until_indent()
self.current_node.close()
debug.dbg('Parsed %s, with %s parsers in %s splits.'
% (self.module_path, self.number_parsers_used,
self.number_of_splits))
def _get_node(self, source, parser_code, line_offset, nodes):
"""
Side effect: Alters the list of nodes.
"""
indent = len(source) - len(source.lstrip('\t '))
self.current_node = self.current_node.parent_until_indent(indent)
h = hash(source)
for index, node in enumerate(nodes):
if node.hash == h and node.source == source:
node.reset_node()
nodes.remove(node)
break
else:
tokenizer = FastTokenizer(parser_code)
self.number_parsers_used += 1
p = Parser(self._grammar, parser_code, self.module_path, tokenizer=tokenizer)
end = line_offset + p.module.end_pos[0]
used_lines = self._lines[line_offset:end - 1]
code_part_actually_used = ''.join(used_lines)
node = ParserNode(self.module, p, code_part_actually_used)
self.current_node.add_node(node, line_offset)
return node
class FastTokenizer(object):
"""
Breaks when certain conditions are met, i.e. a new function or class opens.
"""
def __init__(self, source):
self.source = source
self._gen = source_tokens(source)
self._closed = False
# fast parser options
self.current = self.previous = NEWLINE, '', (0, 0)
self._in_flow = False
self._is_decorator = False
self._first_stmt = True
self._parentheses_level = 0
self._indent_counter = 0
self._flow_indent_counter = 0
self._returned_endmarker = False
self._expect_indent = False
def __iter__(self):
return self
def next(self):
""" Python 2 Compatibility """
return self.__next__()
def __next__(self):
if self._closed:
return self._finish_dedents()
typ, value, start_pos, prefix = current = next(self._gen)
if typ == ENDMARKER:
self._closed = True
self._returned_endmarker = True
return current
self.previous = self.current
self.current = current
if typ == INDENT:
self._indent_counter += 1
if not self._expect_indent and not self._first_stmt and not self._in_flow:
# This does not mean that there is an actual flow, it means
# that the INDENT is syntactically wrong.
self._flow_indent_counter = self._indent_counter - 1
self._in_flow = True
self._expect_indent = False
elif typ == DEDENT:
self._indent_counter -= 1
if self._in_flow:
if self._indent_counter == self._flow_indent_counter:
self._in_flow = False
else:
self._closed = True
return current
if value in ('def', 'class') and self._parentheses_level \
and re.search(r'\n[ \t]*\Z', prefix):
# Account for the fact that an open parentheses before a function
# will reset the parentheses counter, but new lines before will
# still be ignored. So check the prefix.
# TODO what about flow parentheses counter resets in the tokenizer?
self._parentheses_level = 0
return self._close()
# Parentheses ignore the indentation rules. The other three stand for
# new lines.
if self.previous[0] in (NEWLINE, INDENT, DEDENT) \
and not self._parentheses_level and typ not in (INDENT, DEDENT):
if not self._in_flow:
if value in FLOWS:
self._flow_indent_counter = self._indent_counter
self._first_stmt = False
elif value in ('def', 'class', '@'):
# The values here are exactly the same check as in
# _split_parts, but this time with tokenize and therefore
# precise.
if not self._first_stmt and not self._is_decorator:
return self._close()
self._is_decorator = '@' == value
if not self._is_decorator:
self._first_stmt = False
self._expect_indent = True
elif self._expect_indent:
return self._close()
else:
self._first_stmt = False
if value in '([{' and value:
self._parentheses_level += 1
elif value in ')]}' and value:
# Ignore closing parentheses, because they are all
# irrelevant for the indentation.
self._parentheses_level = max(self._parentheses_level - 1, 0)
return current
def _close(self):
if self._first_stmt:
# Continue like nothing has happened, because we want to enter
# the first class/function.
if self.current[1] != '@':
self._first_stmt = False
return self.current
else:
self._closed = True
return self._finish_dedents()
def _finish_dedents(self):
if self._indent_counter:
self._indent_counter -= 1
return DEDENT, '', self.current[2], ''
elif not self._returned_endmarker:
self._returned_endmarker = True
return ENDMARKER, '', self.current[2], self._get_prefix()
else:
raise StopIteration
def _get_prefix(self):
"""
We're using the current prefix for the endmarker to not loose any
information. However we care about "lost" lines. The prefix of the
current line (indent) will always be included in the current line.
"""
cur = self.current
while cur[0] == DEDENT:
cur = next(self._gen)
prefix = cur[3]
# \Z for the end of the string. $ is bugged, because it has the
# same behavior with or without re.MULTILINE.
return re.sub(r'[^\n]+\Z', '', prefix)

152
jedi/parser/grammar2.7.txt Normal file
View File

@@ -0,0 +1,152 @@
# Grammar for 2to3. This grammar supports Python 2.x and 3.x.
# Note: Changing the grammar specified in this file will most likely
# require corresponding changes in the parser module
# (../Modules/parsermodule.c). If you can't make the changes to
# that module yourself, please co-ordinate the required changes
# with someone who can; ask around on python-dev for help. Fred
# Drake <fdrake@acm.org> will probably be listening there.
# NOTE WELL: You should also follow all the steps listed in PEP 306,
# "How to Change Python's Grammar"
# Start symbols for the grammar:
# file_input is a module or sequence of commands read from an input file;
# single_input is a single interactive statement;
# eval_input is the input for the eval() and input() functions.
# NB: compound_stmt in single_input is followed by extra NEWLINE!
file_input: (NEWLINE | stmt)* ENDMARKER
single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE
eval_input: testlist NEWLINE* ENDMARKER
decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
decorators: decorator+
decorated: decorators (classdef | funcdef)
funcdef: 'def' NAME parameters ['->' test] ':' suite
parameters: '(' [typedargslist] ')'
typedargslist: ((tfpdef ['=' test] ',')*
('*' [tname] (',' tname ['=' test])* [',' '**' tname] | '**' tname)
| tfpdef ['=' test] (',' tfpdef ['=' test])* [','])
tname: NAME [':' test]
tfpdef: tname | '(' tfplist ')'
tfplist: tfpdef (',' tfpdef)* [',']
varargslist: ((vfpdef ['=' test] ',')*
('*' [vname] (',' vname ['=' test])* [',' '**' vname] | '**' vname)
| vfpdef ['=' test] (',' vfpdef ['=' test])* [','])
vname: NAME
vfpdef: vname | '(' vfplist ')'
vfplist: vfpdef (',' vfpdef)* [',']
stmt: simple_stmt | compound_stmt
simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
small_stmt: (expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt |
import_stmt | global_stmt | exec_stmt | assert_stmt)
expr_stmt: testlist_star_expr (augassign (yield_expr|testlist) |
('=' (yield_expr|testlist_star_expr))*)
testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [',']
augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
'<<=' | '>>=' | '**=' | '//=')
# For normal assignments, additional restrictions enforced by the interpreter
print_stmt: 'print' ( [ test (',' test)* [','] ] |
'>>' test [ (',' test)+ [','] ] )
del_stmt: 'del' exprlist
pass_stmt: 'pass'
flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt
break_stmt: 'break'
continue_stmt: 'continue'
return_stmt: 'return' [testlist]
yield_stmt: yield_expr
raise_stmt: 'raise' [test ['from' test | ',' test [',' test]]]
import_stmt: import_name | import_from
import_name: 'import' dotted_as_names
# note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS
import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+)
'import' ('*' | '(' import_as_names ')' | import_as_names))
import_as_name: NAME ['as' NAME]
dotted_as_name: dotted_name ['as' NAME]
import_as_names: import_as_name (',' import_as_name)* [',']
dotted_as_names: dotted_as_name (',' dotted_as_name)*
dotted_name: NAME ('.' NAME)*
global_stmt: ('global' | 'nonlocal') NAME (',' NAME)*
exec_stmt: 'exec' expr ['in' test [',' test]]
assert_stmt: 'assert' test [',' test]
compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
while_stmt: 'while' test ':' suite ['else' ':' suite]
for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
try_stmt: ('try' ':' suite
((except_clause ':' suite)+
['else' ':' suite]
['finally' ':' suite] |
'finally' ':' suite))
with_stmt: 'with' with_item (',' with_item)* ':' suite
with_item: test ['as' expr]
with_var: 'as' expr
# NB compile.c makes sure that the default except clause is last
except_clause: 'except' [test [(',' | 'as') test]]
# Edit by David Halter: The stmt is now optional. This reflects how Jedi allows
# classes and functions to be empty, which is beneficial for autocompletion.
suite: simple_stmt | NEWLINE INDENT stmt* DEDENT
# Backward compatibility cruft to support:
# [ x for x in lambda: True, lambda: False if x() ]
# even while also allowing:
# lambda x: 5 if x else 2
# (But not a mix of the two)
testlist_safe: old_test [(',' old_test)+ [',']]
old_test: or_test | old_lambdef
old_lambdef: 'lambda' [varargslist] ':' old_test
test: or_test ['if' or_test 'else' test] | lambdef
or_test: and_test ('or' and_test)*
and_test: not_test ('and' not_test)*
not_test: 'not' not_test | comparison
comparison: expr (comp_op expr)*
comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
star_expr: '*' expr
expr: xor_expr ('|' xor_expr)*
xor_expr: and_expr ('^' and_expr)*
and_expr: shift_expr ('&' shift_expr)*
shift_expr: arith_expr (('<<'|'>>') arith_expr)*
arith_expr: term (('+'|'-') term)*
term: factor (('*'|'/'|'%'|'//') factor)*
factor: ('+'|'-'|'~') factor | power
power: atom trailer* ['**' factor]
atom: ('(' [yield_expr|testlist_comp] ')' |
'[' [testlist_comp] ']' |
'{' [dictorsetmaker] '}' |
'`' testlist1 '`' |
NAME | NUMBER | STRING+ | '.' '.' '.')
# Modification by David Halter, remove `testlist_gexp` and `listmaker`
testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
lambdef: 'lambda' [varargslist] ':' test
trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
subscriptlist: subscript (',' subscript)* [',']
subscript: test | [test] ':' [test] [sliceop]
sliceop: ':' [test]
exprlist: (expr|star_expr) (',' (expr|star_expr))* [',']
testlist: test (',' test)* [',']
# Modification by David Halter, dictsetmaker -> dictorsetmaker (so that it's
# the same as in the 3.4 grammar).
dictorsetmaker: ( (test ':' test (comp_for | (',' test ':' test)* [','])) |
(test (comp_for | (',' test)* [','])) )
classdef: 'class' NAME ['(' [arglist] ')'] ':' suite
arglist: (argument ',')* (argument [',']
|'*' test (',' argument)* [',' '**' test]
|'**' test)
argument: test [comp_for] | test '=' test # Really [keyword '='] test
comp_iter: comp_for | comp_if
comp_for: 'for' exprlist 'in' testlist_safe [comp_iter]
comp_if: 'if' old_test [comp_iter]
testlist1: test (',' test)*
# not used in grammar, but may appear in "node" passed from Parser to Compiler
encoding_decl: NAME
yield_expr: 'yield' [testlist]

135
jedi/parser/grammar3.4.txt Normal file
View File

@@ -0,0 +1,135 @@
# Grammar for Python
# Note: Changing the grammar specified in this file will most likely
# require corresponding changes in the parser module
# (../Modules/parsermodule.c). If you can't make the changes to
# that module yourself, please co-ordinate the required changes
# with someone who can; ask around on python-dev for help. Fred
# Drake <fdrake@acm.org> will probably be listening there.
# NOTE WELL: You should also follow all the steps listed in PEP 306,
# "How to Change Python's Grammar"
# Start symbols for the grammar:
# single_input is a single interactive statement;
# file_input is a module or sequence of commands read from an input file;
# eval_input is the input for the eval() functions.
# NB: compound_stmt in single_input is followed by extra NEWLINE!
file_input: (NEWLINE | stmt)* ENDMARKER
single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE
eval_input: testlist NEWLINE* ENDMARKER
decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
decorators: decorator+
decorated: decorators (classdef | funcdef)
funcdef: 'def' NAME parameters ['->' test] ':' suite
parameters: '(' [typedargslist] ')'
typedargslist: (tfpdef ['=' test] (',' tfpdef ['=' test])* [','
['*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] | '**' tfpdef]]
| '*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] | '**' tfpdef)
tfpdef: NAME [':' test]
varargslist: (vfpdef ['=' test] (',' vfpdef ['=' test])* [','
['*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef]]
| '*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef)
vfpdef: NAME
stmt: simple_stmt | compound_stmt
simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt |
import_stmt | global_stmt | nonlocal_stmt | assert_stmt)
expr_stmt: testlist_star_expr (augassign (yield_expr|testlist) |
('=' (yield_expr|testlist_star_expr))*)
testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [',']
augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
'<<=' | '>>=' | '**=' | '//=')
# For normal assignments, additional restrictions enforced by the interpreter
del_stmt: 'del' exprlist
pass_stmt: 'pass'
flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt
break_stmt: 'break'
continue_stmt: 'continue'
return_stmt: 'return' [testlist]
yield_stmt: yield_expr
raise_stmt: 'raise' [test ['from' test]]
import_stmt: import_name | import_from
import_name: 'import' dotted_as_names
# note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS
import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+)
'import' ('*' | '(' import_as_names ')' | import_as_names))
import_as_name: NAME ['as' NAME]
dotted_as_name: dotted_name ['as' NAME]
import_as_names: import_as_name (',' import_as_name)* [',']
dotted_as_names: dotted_as_name (',' dotted_as_name)*
dotted_name: NAME ('.' NAME)*
global_stmt: 'global' NAME (',' NAME)*
nonlocal_stmt: 'nonlocal' NAME (',' NAME)*
assert_stmt: 'assert' test [',' test]
compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
while_stmt: 'while' test ':' suite ['else' ':' suite]
for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
try_stmt: ('try' ':' suite
((except_clause ':' suite)+
['else' ':' suite]
['finally' ':' suite] |
'finally' ':' suite))
with_stmt: 'with' with_item (',' with_item)* ':' suite
with_item: test ['as' expr]
# NB compile.c makes sure that the default except clause is last
except_clause: 'except' [test ['as' NAME]]
# Edit by David Halter: The stmt is now optional. This reflects how Jedi allows
# classes and functions to be empty, which is beneficial for autocompletion.
suite: simple_stmt | NEWLINE INDENT stmt* DEDENT
test: or_test ['if' or_test 'else' test] | lambdef
test_nocond: or_test | lambdef_nocond
lambdef: 'lambda' [varargslist] ':' test
lambdef_nocond: 'lambda' [varargslist] ':' test_nocond
or_test: and_test ('or' and_test)*
and_test: not_test ('and' not_test)*
not_test: 'not' not_test | comparison
comparison: expr (comp_op expr)*
# <> isn't actually a valid comparison operator in Python. It's here for the
# sake of a __future__ import described in PEP 401
comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
star_expr: '*' expr
expr: xor_expr ('|' xor_expr)*
xor_expr: and_expr ('^' and_expr)*
and_expr: shift_expr ('&' shift_expr)*
shift_expr: arith_expr (('<<'|'>>') arith_expr)*
arith_expr: term (('+'|'-') term)*
term: factor (('*'|'/'|'%'|'//') factor)*
factor: ('+'|'-'|'~') factor | power
power: atom trailer* ['**' factor]
atom: ('(' [yield_expr|testlist_comp] ')' |
'[' [testlist_comp] ']' |
'{' [dictorsetmaker] '}' |
NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False')
testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
subscriptlist: subscript (',' subscript)* [',']
subscript: test | [test] ':' [test] [sliceop]
sliceop: ':' [test]
exprlist: (expr|star_expr) (',' (expr|star_expr))* [',']
testlist: test (',' test)* [',']
dictorsetmaker: ( (test ':' test (comp_for | (',' test ':' test)* [','])) |
(test (comp_for | (',' test)* [','])) )
classdef: 'class' NAME ['(' [arglist] ')'] ':' suite
arglist: (argument ',')* (argument [',']
|'*' test (',' argument)* [',' '**' test]
|'**' test)
# The reason that keywords are test nodes instead of NAME is that using NAME
# results in an ambiguity. ast.c makes sure it's a NAME.
argument: test [comp_for] | test '=' test # Really [keyword '='] test
comp_iter: comp_for | comp_if
comp_for: 'for' exprlist 'in' or_test [comp_iter]
comp_if: 'if' test_nocond [comp_iter]
# not used in grammar, but may appear in "node" passed from Parser to Compiler
encoding_decl: NAME
yield_expr: 'yield' [yield_arg]
yield_arg: 'from' test | testlist

View File

@@ -0,0 +1,8 @@
# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved.
# Licensed to PSF under a Contributor Agreement.
# Modifications:
# Copyright 2006 Google, Inc. All Rights Reserved.
# Licensed to PSF under a Contributor Agreement.
# Copyright 2014 David Halter. Integration into Jedi.
# Modifications are dual-licensed: MIT and PSF.

View File

@@ -0,0 +1,125 @@
# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved.
# Licensed to PSF under a Contributor Agreement.
# Modifications:
# Copyright 2014 David Halter. Integration into Jedi.
# Modifications are dual-licensed: MIT and PSF.
"""This module defines the data structures used to represent a grammar.
These are a bit arcane because they are derived from the data
structures used by Python's 'pgen' parser generator.
There's also a table here mapping operators to their names in the
token module; the Python tokenize module reports all operators as the
fallback token code OP, but the parser needs the actual token code.
"""
# Python imports
import pickle
class Grammar(object):
"""Pgen parsing tables conversion class.
Once initialized, this class supplies the grammar tables for the
parsing engine implemented by parse.py. The parsing engine
accesses the instance variables directly. The class here does not
provide initialization of the tables; several subclasses exist to
do this (see the conv and pgen modules).
The load() method reads the tables from a pickle file, which is
much faster than the other ways offered by subclasses. The pickle
file is written by calling dump() (after loading the grammar
tables using a subclass). The report() method prints a readable
representation of the tables to stdout, for debugging.
The instance variables are as follows:
symbol2number -- a dict mapping symbol names to numbers. Symbol
numbers are always 256 or higher, to distinguish
them from token numbers, which are between 0 and
255 (inclusive).
number2symbol -- a dict mapping numbers to symbol names;
these two are each other's inverse.
states -- a list of DFAs, where each DFA is a list of
states, each state is a list of arcs, and each
arc is a (i, j) pair where i is a label and j is
a state number. The DFA number is the index into
this list. (This name is slightly confusing.)
Final states are represented by a special arc of
the form (0, j) where j is its own state number.
dfas -- a dict mapping symbol numbers to (DFA, first)
pairs, where DFA is an item from the states list
above, and first is a set of tokens that can
begin this grammar rule (represented by a dict
whose values are always 1).
labels -- a list of (x, y) pairs where x is either a token
number or a symbol number, and y is either None
or a string; the strings are keywords. The label
number is the index in this list; label numbers
are used to mark state transitions (arcs) in the
DFAs.
start -- the number of the grammar's start symbol.
keywords -- a dict mapping keyword strings to arc labels.
tokens -- a dict mapping token numbers to arc labels.
"""
def __init__(self):
self.symbol2number = {}
self.number2symbol = {}
self.states = []
self.dfas = {}
self.labels = [(0, "EMPTY")]
self.keywords = {}
self.tokens = {}
self.symbol2label = {}
self.start = 256
def dump(self, filename):
"""Dump the grammar tables to a pickle file."""
with open(filename, "wb") as f:
pickle.dump(self.__dict__, f, 2)
def load(self, filename):
"""Load the grammar tables from a pickle file."""
with open(filename, "rb") as f:
d = pickle.load(f)
self.__dict__.update(d)
def copy(self):
"""
Copy the grammar.
"""
new = self.__class__()
for dict_attr in ("symbol2number", "number2symbol", "dfas", "keywords",
"tokens", "symbol2label"):
setattr(new, dict_attr, getattr(self, dict_attr).copy())
new.labels = self.labels[:]
new.states = self.states[:]
new.start = self.start
return new
def report(self):
"""Dump the grammar tables to standard output, for debugging."""
from pprint import pprint
print("s2n")
pprint(self.symbol2number)
print("n2s")
pprint(self.number2symbol)
print("states")
pprint(self.states)
print("dfas")
pprint(self.dfas)
print("labels")
pprint(self.labels)
print("start", self.start)

205
jedi/parser/pgen2/parse.py Normal file
View File

@@ -0,0 +1,205 @@
# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved.
# Licensed to PSF under a Contributor Agreement.
# Modifications:
# Copyright 2014 David Halter. Integration into Jedi.
# Modifications are dual-licensed: MIT and PSF.
"""
Parser engine for the grammar tables generated by pgen.
The grammar table must be loaded first.
See Parser/parser.c in the Python distribution for additional info on
how this parsing engine works.
"""
# Local imports
from jedi.parser import tokenize
class ParseError(Exception):
"""Exception to signal the parser is stuck."""
def __init__(self, msg, type, value, start_pos):
Exception.__init__(self, "%s: type=%r, value=%r, start_pos=%r" %
(msg, tokenize.tok_name[type], value, start_pos))
self.msg = msg
self.type = type
self.value = value
self.start_pos = start_pos
class PgenParser(object):
"""Parser engine.
The proper usage sequence is:
p = Parser(grammar, [converter]) # create instance
p.setup([start]) # prepare for parsing
<for each input token>:
if p.addtoken(...): # parse a token; may raise ParseError
break
root = p.rootnode # root of abstract syntax tree
A Parser instance may be reused by calling setup() repeatedly.
A Parser instance contains state pertaining to the current token
sequence, and should not be used concurrently by different threads
to parse separate token sequences.
See driver.py for how to get input tokens by tokenizing a file or
string.
Parsing is complete when addtoken() returns True; the root of the
abstract syntax tree can then be retrieved from the rootnode
instance variable. When a syntax error occurs, addtoken() raises
the ParseError exception. There is no error recovery; the parser
cannot be used after a syntax error was reported (but it can be
reinitialized by calling setup()).
"""
def __init__(self, grammar, convert_node, convert_leaf, error_recovery):
"""Constructor.
The grammar argument is a grammar.Grammar instance; see the
grammar module for more information.
The parser is not ready yet for parsing; you must call the
setup() method to get it started.
The optional convert argument is a function mapping concrete
syntax tree nodes to abstract syntax tree nodes. If not
given, no conversion is done and the syntax tree produced is
the concrete syntax tree. If given, it must be a function of
two arguments, the first being the grammar (a grammar.Grammar
instance), and the second being the concrete syntax tree node
to be converted. The syntax tree is converted from the bottom
up.
A concrete syntax tree node is a (type, nodes) tuple, where
type is the node type (a token or symbol number) and nodes
is a list of children for symbols, and None for tokens.
An abstract syntax tree node may be anything; this is entirely
up to the converter function.
"""
self.grammar = grammar
self.convert_node = convert_node
self.convert_leaf = convert_leaf
# Prepare for parsing.
start = self.grammar.start
# Each stack entry is a tuple: (dfa, state, node).
# A node is a tuple: (type, children),
# where children is a list of nodes or None
newnode = (start, [])
stackentry = (self.grammar.dfas[start], 0, newnode)
self.stack = [stackentry]
self.rootnode = None
self.error_recovery = error_recovery
def parse(self, tokenizer):
for type, value, prefix, start_pos in tokenizer:
if self.addtoken(type, value, prefix, start_pos):
break
else:
# We never broke out -- EOF is too soon -- Unfinished statement.
self.error_recovery(self.grammar, self.stack, type, value,
start_pos, prefix, self.addtoken)
# Add the ENDMARKER again.
if not self.addtoken(type, value, prefix, start_pos):
raise ParseError("incomplete input", type, value, start_pos)
return self.rootnode
def addtoken(self, type, value, prefix, start_pos):
"""Add a token; return True if this is the end of the program."""
# Map from token to label
if type == tokenize.NAME:
# Check for reserved words (keywords)
try:
ilabel = self.grammar.keywords[value]
except KeyError:
ilabel = self.grammar.tokens[type]
else:
ilabel = self.grammar.tokens[type]
# Loop until the token is shifted; may raise exceptions
while True:
dfa, state, node = self.stack[-1]
states, first = dfa
arcs = states[state]
# Look for a state with this label
for i, newstate in arcs:
t, v = self.grammar.labels[i]
if ilabel == i:
# Look it up in the list of labels
assert t < 256
# Shift a token; we're done with it
self.shift(type, value, newstate, prefix, start_pos)
# Pop while we are in an accept-only state
state = newstate
while states[state] == [(0, state)]:
self.pop()
if not self.stack:
# Done parsing!
return True
dfa, state, node = self.stack[-1]
states, first = dfa
# Done with this token
return False
elif t >= 256:
# See if it's a symbol and if we're in its first set
itsdfa = self.grammar.dfas[t]
itsstates, itsfirst = itsdfa
if ilabel in itsfirst:
# Push a symbol
self.push(t, itsdfa, newstate)
break # To continue the outer while loop
else:
if (0, state) in arcs:
# An accepting state, pop it and try something else
self.pop()
if not self.stack:
# Done parsing, but another token is input
raise ParseError("too much input", type, value, start_pos)
else:
self.error_recovery(self.grammar, self.stack, type,
value, start_pos, prefix, self.addtoken)
break
def shift(self, type, value, newstate, prefix, start_pos):
"""Shift a token. (Internal)"""
dfa, state, node = self.stack[-1]
newnode = self.convert_leaf(self.grammar, type, value, prefix, start_pos)
node[-1].append(newnode)
self.stack[-1] = (dfa, newstate, node)
def push(self, type, newdfa, newstate):
"""Push a nonterminal. (Internal)"""
dfa, state, node = self.stack[-1]
newnode = (type, [])
self.stack[-1] = (dfa, newstate, node)
self.stack.append((newdfa, 0, newnode))
def pop(self):
"""Pop a nonterminal. (Internal)"""
popdfa, popstate, (type, children) = self.stack.pop()
# If there's exactly one child, return that child instead of creating a
# new node. We still create expr_stmt and file_input though, because a
# lot of Jedi depends on its logic.
if len(children) == 1:
newnode = children[0]
else:
newnode = self.convert_node(self.grammar, type, children)
try:
# Equal to:
# dfa, state, node = self.stack[-1]
# symbol, children = node
self.stack[-1][2][1].append(newnode)
except IndexError:
# Stack is empty, set the rootnode.
self.rootnode = newnode

394
jedi/parser/pgen2/pgen.py Normal file
View File

@@ -0,0 +1,394 @@
# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved.
# Licensed to PSF under a Contributor Agreement.
# Modifications:
# Copyright 2014 David Halter. Integration into Jedi.
# Modifications are dual-licensed: MIT and PSF.
# Pgen imports
from . import grammar
from jedi.parser import token
from jedi.parser import tokenize
class ParserGenerator(object):
def __init__(self, filename, stream=None):
close_stream = None
if stream is None:
stream = open(filename)
close_stream = stream.close
self.filename = filename
self.stream = stream
self.generator = tokenize.generate_tokens(stream.readline)
self.gettoken() # Initialize lookahead
self.dfas, self.startsymbol = self.parse()
if close_stream is not None:
close_stream()
self.first = {} # map from symbol name to set of tokens
self.addfirstsets()
def make_grammar(self):
c = grammar.Grammar()
names = list(self.dfas.keys())
names.sort()
names.remove(self.startsymbol)
names.insert(0, self.startsymbol)
for name in names:
i = 256 + len(c.symbol2number)
c.symbol2number[name] = i
c.number2symbol[i] = name
for name in names:
dfa = self.dfas[name]
states = []
for state in dfa:
arcs = []
for label, next in state.arcs.items():
arcs.append((self.make_label(c, label), dfa.index(next)))
if state.isfinal:
arcs.append((0, dfa.index(state)))
states.append(arcs)
c.states.append(states)
c.dfas[c.symbol2number[name]] = (states, self.make_first(c, name))
c.start = c.symbol2number[self.startsymbol]
return c
def make_first(self, c, name):
rawfirst = self.first[name]
first = {}
for label in rawfirst:
ilabel = self.make_label(c, label)
##assert ilabel not in first # XXX failed on <> ... !=
first[ilabel] = 1
return first
def make_label(self, c, label):
# XXX Maybe this should be a method on a subclass of converter?
ilabel = len(c.labels)
if label[0].isalpha():
# Either a symbol name or a named token
if label in c.symbol2number:
# A symbol name (a non-terminal)
if label in c.symbol2label:
return c.symbol2label[label]
else:
c.labels.append((c.symbol2number[label], None))
c.symbol2label[label] = ilabel
return ilabel
else:
# A named token (NAME, NUMBER, STRING)
itoken = getattr(token, label, None)
assert isinstance(itoken, int), label
assert itoken in token.tok_name, label
if itoken in c.tokens:
return c.tokens[itoken]
else:
c.labels.append((itoken, None))
c.tokens[itoken] = ilabel
return ilabel
else:
# Either a keyword or an operator
assert label[0] in ('"', "'"), label
value = eval(label)
if value[0].isalpha():
# A keyword
if value in c.keywords:
return c.keywords[value]
else:
c.labels.append((token.NAME, value))
c.keywords[value] = ilabel
return ilabel
else:
# An operator (any non-numeric token)
itoken = token.opmap[value] # Fails if unknown token
if itoken in c.tokens:
return c.tokens[itoken]
else:
c.labels.append((itoken, None))
c.tokens[itoken] = ilabel
return ilabel
def addfirstsets(self):
names = list(self.dfas.keys())
names.sort()
for name in names:
if name not in self.first:
self.calcfirst(name)
#print name, self.first[name].keys()
def calcfirst(self, name):
dfa = self.dfas[name]
self.first[name] = None # dummy to detect left recursion
state = dfa[0]
totalset = {}
overlapcheck = {}
for label, next in state.arcs.items():
if label in self.dfas:
if label in self.first:
fset = self.first[label]
if fset is None:
raise ValueError("recursion for rule %r" % name)
else:
self.calcfirst(label)
fset = self.first[label]
totalset.update(fset)
overlapcheck[label] = fset
else:
totalset[label] = 1
overlapcheck[label] = {label: 1}
inverse = {}
for label, itsfirst in overlapcheck.items():
for symbol in itsfirst:
if symbol in inverse:
raise ValueError("rule %s is ambiguous; %s is in the"
" first sets of %s as well as %s" %
(name, symbol, label, inverse[symbol]))
inverse[symbol] = label
self.first[name] = totalset
def parse(self):
dfas = {}
startsymbol = None
# MSTART: (NEWLINE | RULE)* ENDMARKER
while self.type != token.ENDMARKER:
while self.type == token.NEWLINE:
self.gettoken()
# RULE: NAME ':' RHS NEWLINE
name = self.expect(token.NAME)
self.expect(token.OP, ":")
a, z = self.parse_rhs()
self.expect(token.NEWLINE)
#self.dump_nfa(name, a, z)
dfa = self.make_dfa(a, z)
#self.dump_dfa(name, dfa)
# oldlen = len(dfa)
self.simplify_dfa(dfa)
# newlen = len(dfa)
dfas[name] = dfa
#print name, oldlen, newlen
if startsymbol is None:
startsymbol = name
return dfas, startsymbol
def make_dfa(self, start, finish):
# To turn an NFA into a DFA, we define the states of the DFA
# to correspond to *sets* of states of the NFA. Then do some
# state reduction. Let's represent sets as dicts with 1 for
# values.
assert isinstance(start, NFAState)
assert isinstance(finish, NFAState)
def closure(state):
base = {}
addclosure(state, base)
return base
def addclosure(state, base):
assert isinstance(state, NFAState)
if state in base:
return
base[state] = 1
for label, next in state.arcs:
if label is None:
addclosure(next, base)
states = [DFAState(closure(start), finish)]
for state in states: # NB states grows while we're iterating
arcs = {}
for nfastate in state.nfaset:
for label, next in nfastate.arcs:
if label is not None:
addclosure(next, arcs.setdefault(label, {}))
for label, nfaset in arcs.items():
for st in states:
if st.nfaset == nfaset:
break
else:
st = DFAState(nfaset, finish)
states.append(st)
state.addarc(st, label)
return states # List of DFAState instances; first one is start
def dump_nfa(self, name, start, finish):
print("Dump of NFA for", name)
todo = [start]
for i, state in enumerate(todo):
print(" State", i, state is finish and "(final)" or "")
for label, next in state.arcs:
if next in todo:
j = todo.index(next)
else:
j = len(todo)
todo.append(next)
if label is None:
print(" -> %d" % j)
else:
print(" %s -> %d" % (label, j))
def dump_dfa(self, name, dfa):
print("Dump of DFA for", name)
for i, state in enumerate(dfa):
print(" State", i, state.isfinal and "(final)" or "")
for label, next in state.arcs.items():
print(" %s -> %d" % (label, dfa.index(next)))
def simplify_dfa(self, dfa):
# This is not theoretically optimal, but works well enough.
# Algorithm: repeatedly look for two states that have the same
# set of arcs (same labels pointing to the same nodes) and
# unify them, until things stop changing.
# dfa is a list of DFAState instances
changes = True
while changes:
changes = False
for i, state_i in enumerate(dfa):
for j in range(i + 1, len(dfa)):
state_j = dfa[j]
if state_i == state_j:
#print " unify", i, j
del dfa[j]
for state in dfa:
state.unifystate(state_j, state_i)
changes = True
break
def parse_rhs(self):
# RHS: ALT ('|' ALT)*
a, z = self.parse_alt()
if self.value != "|":
return a, z
else:
aa = NFAState()
zz = NFAState()
aa.addarc(a)
z.addarc(zz)
while self.value == "|":
self.gettoken()
a, z = self.parse_alt()
aa.addarc(a)
z.addarc(zz)
return aa, zz
def parse_alt(self):
# ALT: ITEM+
a, b = self.parse_item()
while (self.value in ("(", "[") or
self.type in (token.NAME, token.STRING)):
c, d = self.parse_item()
b.addarc(c)
b = d
return a, b
def parse_item(self):
# ITEM: '[' RHS ']' | ATOM ['+' | '*']
if self.value == "[":
self.gettoken()
a, z = self.parse_rhs()
self.expect(token.OP, "]")
a.addarc(z)
return a, z
else:
a, z = self.parse_atom()
value = self.value
if value not in ("+", "*"):
return a, z
self.gettoken()
z.addarc(a)
if value == "+":
return a, z
else:
return a, a
def parse_atom(self):
# ATOM: '(' RHS ')' | NAME | STRING
if self.value == "(":
self.gettoken()
a, z = self.parse_rhs()
self.expect(token.OP, ")")
return a, z
elif self.type in (token.NAME, token.STRING):
a = NFAState()
z = NFAState()
a.addarc(z, self.value)
self.gettoken()
return a, z
else:
self.raise_error("expected (...) or NAME or STRING, got %s/%s",
self.type, self.value)
def expect(self, type, value=None):
if self.type != type or (value is not None and self.value != value):
self.raise_error("expected %s/%s, got %s/%s",
type, value, self.type, self.value)
value = self.value
self.gettoken()
return value
def gettoken(self):
tup = next(self.generator)
while tup[0] in (token.COMMENT, token.NL):
tup = next(self.generator)
self.type, self.value, self.begin, prefix = tup
#print tokenize.tok_name[self.type], repr(self.value)
def raise_error(self, msg, *args):
if args:
try:
msg = msg % args
except:
msg = " ".join([msg] + list(map(str, args)))
line = open(self.filename).readlines()[self.begin[0]]
raise SyntaxError(msg, (self.filename, self.begin[0],
self.begin[1], line))
class NFAState(object):
def __init__(self):
self.arcs = [] # list of (label, NFAState) pairs
def addarc(self, next, label=None):
assert label is None or isinstance(label, str)
assert isinstance(next, NFAState)
self.arcs.append((label, next))
class DFAState(object):
def __init__(self, nfaset, final):
assert isinstance(nfaset, dict)
assert isinstance(next(iter(nfaset)), NFAState)
assert isinstance(final, NFAState)
self.nfaset = nfaset
self.isfinal = final in nfaset
self.arcs = {} # map from label to DFAState
def addarc(self, next, label):
assert isinstance(label, str)
assert label not in self.arcs
assert isinstance(next, DFAState)
self.arcs[label] = next
def unifystate(self, old, new):
for label, next in self.arcs.items():
if next is old:
self.arcs[label] = new
def __eq__(self, other):
# Equality test -- ignore the nfaset instance variable
assert isinstance(other, DFAState)
if self.isfinal != other.isfinal:
return False
# Can't just return self.arcs == other.arcs, because that
# would invoke this method recursively, with cycles...
if len(self.arcs) != len(other.arcs):
return False
for label, next in self.arcs.items():
if next is not other.arcs.get(label):
return False
return True
__hash__ = None # For Py3 compatibility.
def generate_grammar(filename="Grammar.txt"):
p = ParserGenerator(filename)
return p.make_grammar()

84
jedi/parser/token.py Normal file
View File

@@ -0,0 +1,84 @@
from __future__ import absolute_import
from jedi._compatibility import is_py3
from token import *
COMMENT = N_TOKENS
tok_name[COMMENT] = 'COMMENT'
N_TOKENS += 1
NL = N_TOKENS
tok_name[NL] = 'NL'
N_TOKENS += 1
if is_py3:
BACKQUOTE = N_TOKENS
tok_name[BACKQUOTE] = 'BACKQUOTE'
N_TOKENS += 1
else:
RARROW = N_TOKENS
tok_name[RARROW] = 'RARROW'
N_TOKENS += 1
ELLIPSIS = N_TOKENS
tok_name[ELLIPSIS] = 'ELLIPSIS'
N_TOKENS += 1
# Map from operator to number (since tokenize doesn't do this)
opmap_raw = """\
( LPAR
) RPAR
[ LSQB
] RSQB
: COLON
, COMMA
; SEMI
+ PLUS
- MINUS
* STAR
/ SLASH
| VBAR
& AMPER
< LESS
> GREATER
= EQUAL
. DOT
% PERCENT
` BACKQUOTE
{ LBRACE
} RBRACE
@ AT
== EQEQUAL
!= NOTEQUAL
<> NOTEQUAL
<= LESSEQUAL
>= GREATEREQUAL
~ TILDE
^ CIRCUMFLEX
<< LEFTSHIFT
>> RIGHTSHIFT
** DOUBLESTAR
+= PLUSEQUAL
-= MINEQUAL
*= STAREQUAL
/= SLASHEQUAL
%= PERCENTEQUAL
&= AMPEREQUAL
|= VBAREQUAL
^= CIRCUMFLEXEQUAL
<<= LEFTSHIFTEQUAL
>>= RIGHTSHIFTEQUAL
**= DOUBLESTAREQUAL
// DOUBLESLASH
//= DOUBLESLASHEQUAL
-> RARROW
... ELLIPSIS
"""
opmap = {}
for line in opmap_raw.splitlines():
op, name = line.split()
opmap[op] = globals()[name]

290
jedi/parser/tokenize.py Normal file
View File

@@ -0,0 +1,290 @@
# -*- coding: utf-8 -*-
"""
This tokenizer has been copied from the ``tokenize.py`` standard library
tokenizer. The reason was simple: The standard 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. Additionally we included some speed and
memory optimizations here.
"""
from __future__ import absolute_import
import string
import re
from io import StringIO
from jedi.parser.token import (tok_name, N_TOKENS, ENDMARKER, STRING, NUMBER,
NAME, OP, ERRORTOKEN, NEWLINE, INDENT, DEDENT)
from jedi._compatibility import is_py3
cookie_re = re.compile("coding[:=]\s*([-\w.]+)")
if is_py3:
# Python 3 has str.isidentifier() to check if a char is a valid identifier
is_identifier = str.isidentifier
else:
namechars = string.ascii_letters + '_'
is_identifier = lambda s: s in namechars
COMMENT = N_TOKENS
tok_name[COMMENT] = 'COMMENT'
def group(*choices):
return '(' + '|'.join(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]*'
name = r'\w+'
hex_number = r'0[xX][0-9a-fA-F]+'
bin_number = r'0[bB][01]+'
oct_number = r'0[oO][0-7]+'
dec_number = r'(?:0+|[1-9][0-9]*)'
int_number = group(hex_number, bin_number, oct_number, dec_number)
exponent = r'[eE][-+]?[0-9]+'
point_float = group(r'[0-9]+\.[0-9]*', r'\.[0-9]+') + maybe(exponent)
Expfloat = r'[0-9]+' + exponent
float_number = group(point_float, Expfloat)
imag_number = group(r'[0-9]+[jJ]', float_number + r'[jJ]')
number = group(imag_number, float_number, int_number)
# 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("[uUbB]?[rR]?'''", '[uUbB]?[rR]?"""')
# Single-line ' or " string.
# 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)
# First (or only) line of ' or " string.
cont_str = group(r"[bBuU]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*" +
group("'", r'\\\r?\n'),
r'[bBuU]?[rR]?"[^\n"\\]*(?:\\.[^\n"\\]*)*' +
group('"', r'\\\r?\n'))
pseudo_extras = group(r'\\\r?\n', comment, triple)
pseudo_token = group(whitespace) + \
group(pseudo_extras, number, funny, cont_str, name)
def _compile(expr):
return re.compile(expr, re.UNICODE)
pseudoprog, single3prog, double3prog = map(
_compile, (pseudo_token, single3, double3))
endprogs = {"'": _compile(single), '"': _compile(double),
"'''": single3prog, '"""': double3prog,
"r'''": single3prog, 'r"""': double3prog,
"b'''": single3prog, 'b"""': double3prog,
"u'''": single3prog, 'u"""': double3prog,
"R'''": single3prog, 'R"""': double3prog,
"B'''": single3prog, 'B"""': double3prog,
"U'''": single3prog, 'U"""': double3prog,
"br'''": single3prog, 'br"""': double3prog,
"bR'''": single3prog, 'bR"""': double3prog,
"Br'''": single3prog, 'Br"""': double3prog,
"BR'''": single3prog, 'BR"""': double3prog,
"ur'''": single3prog, 'ur"""': double3prog,
"uR'''": single3prog, 'uR"""': double3prog,
"Ur'''": single3prog, 'Ur"""': double3prog,
"UR'''": single3prog, 'UR"""': double3prog,
'r': None, 'R': None, 'b': None, 'B': None}
triple_quoted = {}
for t in ("'''", '"""',
"r'''", 'r"""', "R'''", 'R"""',
"b'''", 'b"""', "B'''", 'B"""',
"u'''", 'u"""', "U'''", 'U"""',
"br'''", 'br"""', "Br'''", 'Br"""',
"bR'''", 'bR"""', "BR'''", 'BR"""',
"ur'''", 'ur"""', "Ur'''", 'Ur"""',
"uR'''", 'uR"""', "UR'''", 'UR"""'):
triple_quoted[t] = t
single_quoted = {}
for t in ("'", '"',
"r'", 'r"', "R'", 'R"',
"b'", 'b"', "B'", 'B"',
"u'", 'u"', "U'", 'U"',
"br'", 'br"', "Br'", 'Br"',
"bR'", 'bR"', "BR'", 'BR"',
"ur'", 'ur"', "Ur'", 'Ur"',
"uR'", 'uR"', "UR'", 'UR"'):
single_quoted[t] = t
del _compile
tabsize = 8
ALWAYS_BREAK_TOKENS = (';', 'import', 'from', 'class', 'def', 'try', 'except',
'finally', 'while', 'return')
def source_tokens(source):
"""Generate tokens from a the source code (string)."""
source = source + '\n' # end with \n, because the parser needs it
readline = StringIO(source).readline
return generate_tokens(readline)
def generate_tokens(readline):
"""
A heavily modified Python standard library tokenizer.
Additionally to the default information, yields also the prefix of each
token. This idea comes from lib2to3. The prefix contains all information
that is irrelevant for the parser like newlines in parentheses or comments.
"""
paren_level = 0 # count parentheses
indents = [0]
lnum = 0
numchars = '0123456789'
contstr = ''
contline = None
# We start with a newline. This makes indent at the first position
# possible. It's not valid Python, but still better than an INDENT in the
# second line (and not in the first). This makes quite a few things in
# Jedi's fast parser possible.
new_line = True
prefix = '' # Should never be required, but here for safety
additional_prefix = ''
while True: # loop over lines in stream
line = readline() # readline returns empty when finished. See StringIO
if not line:
if contstr:
yield ERRORTOKEN, contstr, contstr_start, prefix
break
lnum += 1
pos, max = 0, len(line)
if contstr: # continued string
endmatch = endprog.match(line)
if endmatch:
pos = endmatch.end(0)
yield STRING, contstr + line[:pos], contstr_start, prefix
contstr = ''
contline = None
else:
contstr = contstr + line
contline = contline + line
continue
while pos < max:
pseudomatch = pseudoprog.match(line, pos)
if not pseudomatch: # scan for tokens
txt = line[pos]
if line[pos] in '"\'':
# If a literal starts but doesn't end the whole rest of the
# line is an error token.
txt = line[pos:]
yield ERRORTOKEN, txt, (lnum, pos), prefix
pos += 1
continue
prefix = additional_prefix + pseudomatch.group(1)
additional_prefix = ''
start, pos = pseudomatch.span(2)
spos = (lnum, start)
token, initial = line[start:pos], line[start]
if new_line and initial not in '\r\n#':
new_line = False
if paren_level == 0:
if start > indents[-1]:
yield INDENT, '', spos, ''
indents.append(start)
while start < indents[-1]:
yield DEDENT, '', spos, ''
indents.pop()
if (initial in numchars or # ordinary number
(initial == '.' and token != '.' and token != '...')):
yield NUMBER, token, spos, prefix
elif initial in '\r\n':
if not new_line and paren_level == 0:
yield NEWLINE, token, spos, prefix
else:
additional_prefix = prefix + token
new_line = True
elif initial == '#': # Comments
assert not token.endswith("\n")
additional_prefix = prefix + token
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 STRING, token, spos, prefix
else:
contstr_start = (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
contstr_start = lnum, start
endprog = (endprogs.get(initial) or endprogs.get(token[1])
or endprogs.get(token[2]))
contstr = line[start:]
contline = line
break
else: # ordinary string
yield STRING, token, spos, prefix
elif is_identifier(initial): # ordinary name
if token in ALWAYS_BREAK_TOKENS:
paren_level = 0
while True:
indent = indents.pop()
if indent > start:
yield DEDENT, '', spos, ''
else:
indents.append(indent)
break
yield NAME, token, spos, prefix
elif initial == '\\' and line[start:] in ('\\\n', '\\\r\n'): # continued stmt
additional_prefix += prefix + line[start:]
break
else:
if token in '([{':
paren_level += 1
elif token in ')]}':
paren_level -= 1
yield OP, token, spos, prefix
end_pos = (lnum, max - 1)
# As the last position we just take the maximally possible position. We
# remove -1 for the last new line.
for indent in indents[1:]:
yield DEDENT, '', end_pos, ''
yield ENDMARKER, '', end_pos, prefix

1222
jedi/parser/tree.py Normal file

File diff suppressed because it is too large Load Diff

339
jedi/parser/user_context.py Normal file
View File

@@ -0,0 +1,339 @@
import re
import os
import keyword
from jedi import cache
from jedi import common
from jedi.parser import tokenize, Parser
from jedi._compatibility import u
from jedi.parser.fast import FastParser
from jedi.parser import tree
from jedi import debug
from jedi.common import PushBackIterator
REPLACE_STR = r"[bBuU]?[rR]?" + (r"(?:(')[^\n'\\]*(?:\\.[^\n'\\]*)*(?:'|$)" +
'|' +
r'(")[^\n"\\]*(?:\\.[^\n"\\]*)*(?:"|$))')
REPLACE_STR = re.compile(REPLACE_STR)
class UserContext(object):
"""
:param source: The source code of the file.
:param position: The position, the user is currently in. Only important \
for the main file.
"""
def __init__(self, source, position):
self.source = source
self.position = position
self._line_cache = None
self._relevant_temp = None
@cache.underscore_memoization
def get_path_until_cursor(self):
""" Get the path under the cursor. """
path, self._start_cursor_pos = self._calc_path_until_cursor(self.position)
return path
def _backwards_line_generator(self, start_pos):
self._line_temp, self._column_temp = start_pos
first_line = self.get_line(start_pos[0])[:self._column_temp]
self._line_length = self._column_temp
yield first_line[::-1] + '\n'
while True:
self._line_temp -= 1
line = self.get_line(self._line_temp)
self._line_length = len(line)
yield line[::-1] + '\n'
def _get_backwards_tokenizer(self, start_pos, line_gen=None):
if line_gen is None:
line_gen = self._backwards_line_generator(start_pos)
token_gen = tokenize.generate_tokens(lambda: next(line_gen))
for typ, tok_str, tok_start_pos, prefix in token_gen:
line = self.get_line(self._line_temp)
# Calculate the real start_pos of the token.
if tok_start_pos[0] == 1:
# We are in the first checked line
column = start_pos[1] - tok_start_pos[1]
else:
column = len(line) - tok_start_pos[1]
# Multi-line docstrings must be accounted for.
first_line = common.splitlines(tok_str)[0]
column -= len(first_line)
# Reverse the token again, so that it is in normal order again.
yield typ, tok_str[::-1], (self._line_temp, column), prefix[::-1]
def _calc_path_until_cursor(self, start_pos):
"""
Something like a reverse tokenizer that tokenizes the reversed strings.
"""
open_brackets = ['(', '[', '{']
close_brackets = [')', ']', '}']
start_cursor = start_pos
gen = PushBackIterator(self._get_backwards_tokenizer(start_pos))
string = u('')
level = 0
force_point = False
last_type = None
is_first = True
for tok_type, tok_str, tok_start_pos, prefix in gen:
if is_first:
if prefix: # whitespace is not a path
return u(''), start_cursor
is_first = False
if last_type == tok_type == tokenize.NAME:
string = ' ' + string
if level:
if tok_str in close_brackets:
level += 1
elif tok_str in open_brackets:
level -= 1
elif tok_str == '.':
force_point = False
elif force_point:
# Reversed tokenizing, therefore a number is recognized as a
# floating point number.
# The same is true for string prefixes -> represented as a
# combination of string and name.
if tok_type == tokenize.NUMBER and tok_str[-1] == '.' \
or tok_type == tokenize.NAME and last_type == tokenize.STRING \
and tok_str.lower() in ('b', 'u', 'r', 'br', 'ur'):
force_point = False
else:
break
elif tok_str in close_brackets:
level += 1
elif tok_type in [tokenize.NAME, tokenize.STRING]:
if keyword.iskeyword(tok_str) and string:
# If there's already something in the string, a keyword
# never adds any meaning to the current statement.
break
force_point = True
elif tok_type == tokenize.NUMBER:
pass
else:
if tok_str == '-':
next_tok = next(gen)
if next_tok[1] == 'e':
gen.push_back(next_tok)
else:
break
else:
break
start_cursor = tok_start_pos
string = tok_str + prefix + string
last_type = tok_type
# Don't need whitespace around a statement.
return string.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])
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 '')
def call_signature(self):
"""
:return: Tuple of string of the call and the index of the cursor.
"""
def get_line(pos):
def simplify_str(match):
"""
To avoid having strings without end marks (error tokens) and
strings that just screw up all the call signatures, just
simplify everything.
"""
mark = match.group(1) or match.group(2)
return mark + ' ' * (len(match.group(0)) - 2) + mark
line_gen = self._backwards_line_generator(pos)
for line in line_gen:
# We have to switch the already backwards lines twice, because
# we scan them from start.
line = line[::-1]
modified = re.sub(REPLACE_STR, simplify_str, line)
yield modified[::-1]
index = 0
level = 0
next_must_be_name = False
next_is_key = False
key_name = None
generator = self._get_backwards_tokenizer(self.position, get_line(self.position))
for tok_type, tok_str, start_pos, prefix in generator:
if tok_str in tokenize.ALWAYS_BREAK_TOKENS:
break
elif next_must_be_name:
if tok_type == tokenize.NUMBER:
# If there's a number at the end of the string, it will be
# tokenized as a number. So add it to the name.
tok_type, t, _, _ = next(generator)
if tok_type == tokenize.NAME:
end_pos = start_pos[0], start_pos[1] + len(tok_str)
call, start_pos = self._calc_path_until_cursor(start_pos=end_pos)
return call, index, key_name, start_pos
index = 0
next_must_be_name = False
elif next_is_key:
if tok_type == tokenize.NAME:
key_name = tok_str
next_is_key = False
if tok_str == '(':
level += 1
if level == 1:
next_must_be_name = True
level = 0
elif tok_str == ')':
level -= 1
elif tok_str == ',':
index += 1
elif tok_str == '=':
next_is_key = True
return None, 0, None, (0, 0)
def get_context(self, yield_positions=False):
self.get_path_until_cursor() # In case _start_cursor_pos is undefined.
pos = self._start_cursor_pos
while True:
# remove non important white space
line = self.get_line(pos[0])
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:
result, pos = self._calc_path_until_cursor(start_pos=pos)
if yield_positions:
yield pos
else:
yield result
except StopIteration:
if yield_positions:
yield None
else:
yield ''
def get_line(self, line_nr):
if not self._line_cache:
self._line_cache = common.splitlines(self.source)
if line_nr == 0:
# This is a fix for the zeroth line. We need a newline there, for
# the backwards parser.
return u('')
if line_nr < 0:
raise StopIteration()
try:
return self._line_cache[line_nr - 1]
except IndexError:
raise StopIteration()
def get_position_line(self):
return self.get_line(self.position[0])[:self.position[1]]
class UserContextParser(object):
def __init__(self, grammar, source, path, position, user_context,
parser_done_callback, use_fast_parser=True):
self._grammar = grammar
self._source = source
self._path = path and os.path.abspath(path)
self._position = position
self._user_context = user_context
self._use_fast_parser = use_fast_parser
self._parser_done_callback = parser_done_callback
@cache.underscore_memoization
def _parser(self):
cache.invalidate_star_import_cache(self._path)
if self._use_fast_parser:
parser = FastParser(self._grammar, self._source, self._path)
# Don't pickle that module, because the main module is changing quickly
cache.save_parser(self._path, parser, pickling=False)
else:
parser = Parser(self._grammar, self._source, self._path)
self._parser_done_callback(parser)
return parser
@cache.underscore_memoization
def user_stmt(self):
module = self.module()
debug.speed('parsed')
return module.get_statement_for_position(self._position)
@cache.underscore_memoization
def user_stmt_with_whitespace(self):
"""
Returns the statement under the cursor even if the statement lies
before the cursor.
"""
user_stmt = self.user_stmt()
if not user_stmt:
# for statements like `from x import ` (cursor not in statement)
# or `abs( ` where the cursor is out in the whitespace.
if self._user_context.get_path_under_cursor():
# We really should have a user_stmt, but the parser couldn't
# process it - probably a Syntax Error (or in a comment).
debug.warning('No statement under the cursor.')
return
pos = next(self._user_context.get_context(yield_positions=True))
user_stmt = self.module().get_statement_for_position(pos)
return user_stmt
@cache.underscore_memoization
def user_scope(self):
"""
Returns the scope in which the user resides. This includes flows.
"""
user_stmt = self.user_stmt()
if user_stmt is None:
def scan(scope):
for s in scope.children:
if s.start_pos <= self._position <= s.end_pos:
if isinstance(s, (tree.Scope, tree.Flow)):
if isinstance(s, tree.Flow):
return s
return scan(s) or s
elif s.type in ('suite', 'decorated'):
return scan(s)
return scan(self.module()) or self.module()
else:
return user_stmt.get_parent_scope(include_flows=True)
def module(self):
return self._parser().module

View File

@@ -1,681 +0,0 @@
"""
The ``Parser`` tries to convert the available Python code in an easy to read
format, something like an abstract syntax tree. The classes who represent this
tree, are sitting in the :mod:`parsing_representation` module.
The Python module ``tokenize`` is a very important part in the ``Parser``,
because it splits the code into different words (tokens). Sometimes it looks a
bit messy. Sorry for that! You might ask now: "Why didn't you use the ``ast``
module for this? Well, ``ast`` does a very good job understanding proper Python
code, but fails to work as soon as there's a single line of broken code.
There's one important optimization that needs to be known: Statements are not
being parsed completely. ``Statement`` is just a representation of the tokens
within the statement. This lowers memory usage and cpu time and reduces the
complexity of the ``Parser`` (there's another parser sitting inside
``Statement``, which produces ``Array`` and ``Call``).
"""
from __future__ import with_statement
import tokenizer as tokenize
import keyword
from jedi._compatibility import next, StringIO
from jedi import debug
from jedi import common
from jedi import parsing_representation as pr
class ParserError(Exception):
pass
class Parser(object):
"""
This class is used to parse a Python file, it then divides them into a
class structure of different scopes.
:param source: The codebase for the parser.
:type source: str
:param module_path: The path of the module in the file system, may be None.
:type module_path: str
:param user_position: The line/column, the user is currently on.
:type user_position: tuple(int, int)
:param no_docstr: If True, a string at the beginning is not a docstr.
:param is_fast_parser: -> for fast_parser
:param top_module: Use this module as a parent instead of `self.module`.
"""
def __init__(self, source, module_path=None, user_position=None,
no_docstr=False, offset=(0, 0), is_fast_parser=None,
top_module=None):
self.user_position = user_position
self.user_scope = None
self.user_stmt = None
self.no_docstr = no_docstr
self.start_pos = self.end_pos = 1 + offset[0], offset[1]
# initialize global Scope
self.module = pr.SubModule(module_path, self.start_pos, top_module)
self.scope = self.module
self.current = (None, None)
source = source + '\n' # end with \n, because the parser needs it
buf = StringIO(source)
self._gen = common.NoErrorTokenizer(buf.readline, offset,
is_fast_parser)
self.top_module = top_module or self.module
try:
self._parse()
except (common.MultiLevelStopIteration, StopIteration):
# StopIteration needs to be added as well, because python 2 has a
# strange way of handling StopIterations.
# sometimes StopIteration isn't catched. Just ignore it.
pass
# clean up unused decorators
for d in self._decorators:
# set a parent for unused decorators, avoid NullPointerException
# because of `self.module.used_names`.
d.parent = self.module
if self.current[0] in (tokenize.NL, tokenize.NEWLINE):
# we added a newline before, so we need to "remove" it again.
self.end_pos = self._gen.previous[2]
if self.current[0] == tokenize.INDENT:
self.end_pos = self._gen.last_previous[2]
self.start_pos = self.module.start_pos
self.module.end_pos = self.end_pos
del self._gen
def __repr__(self):
return "<%s: %s>" % (type(self).__name__, self.module)
def _check_user_stmt(self, simple):
# this is not user checking, just update the used_names
for tok_name in self.module.temp_used_names:
try:
self.module.used_names[tok_name].add(simple)
except KeyError:
self.module.used_names[tok_name] = set([simple])
self.module.temp_used_names = []
if not self.user_position:
return
# the position is right
if simple.start_pos <= self.user_position <= simple.end_pos:
if self.user_stmt is not None:
# if there is already a user position (another import, because
# imports are splitted) the names are checked.
for n in simple.get_set_vars():
if n.start_pos < self.user_position <= n.end_pos:
self.user_stmt = simple
else:
self.user_stmt = simple
def _parse_dot_name(self, pre_used_token=None):
"""
The dot name parser parses a name, variable or function and returns
their names.
:return: Tuple of Name, token_type, nexttoken.
:rtype: tuple(Name, int, str)
"""
def append(el):
names.append(el)
self.module.temp_used_names.append(el[0])
names = []
if pre_used_token is None:
token_type, tok = self.next()
if token_type != tokenize.NAME and tok != '*':
return [], token_type, tok
else:
token_type, tok = pre_used_token
if token_type != tokenize.NAME and tok != '*':
# token maybe a name or star
return None, token_type, tok
append((tok, self.start_pos))
first_pos = self.start_pos
while True:
token_type, tok = self.next()
if tok != '.':
break
token_type, tok = self.next()
if token_type != tokenize.NAME:
break
append((tok, self.start_pos))
n = pr.Name(self.module, names, first_pos, self.end_pos) if names \
else None
return n, token_type, tok
def _parse_import_list(self):
"""
The parser for the imports. Unlike the class and function parse
function, this returns no Import class, but rather an import list,
which is then added later on.
The reason, why this is not done in the same class lies in the nature
of imports. There are two ways to write them:
- from ... import ...
- import ...
To distinguish, this has to be processed after the parser.
:return: List of imports.
:rtype: list
"""
imports = []
brackets = False
continue_kw = [",", ";", "\n", ')'] \
+ list(set(keyword.kwlist) - set(['as']))
while True:
defunct = False
token_type, tok = self.next()
if brackets and tok == '\n':
self.next()
if tok == '(': # python allows only one `(` in the statement.
brackets = True
self.next()
i, token_type, tok = self._parse_dot_name(self.current)
if not i:
defunct = True
name2 = None
if tok == 'as':
name2, token_type, tok = self._parse_dot_name()
imports.append((i, name2, defunct))
while tok not in continue_kw:
token_type, tok = self.next()
if not (tok == "," or brackets and tok == '\n'):
break
return imports
def _parse_parentheses(self):
"""
Functions and Classes have params (which means for classes
super-classes). They are parsed here and returned as Statements.
:return: List of Statements
:rtype: list
"""
names = []
tok = None
pos = 0
breaks = [',', ':']
while tok not in [')', ':']:
param, tok = self._parse_statement(added_breaks=breaks,
stmt_class=pr.Param)
if param and tok == ':':
# parse annotations
annotation, tok = self._parse_statement(added_breaks=breaks)
if annotation:
param.add_annotation(annotation)
# params without vars are usually syntax errors.
if param and (param.set_vars or param.used_vars):
param.position_nr = pos
names.append(param)
pos += 1
return names
def _parse_function(self):
"""
The parser for a text functions. Process the tokens, which follow a
function definition.
:return: Return a Scope representation of the tokens.
:rtype: Function
"""
first_pos = self.start_pos
token_type, fname = self.next()
if token_type != tokenize.NAME:
return None
fname = pr.Name(self.module, [(fname, self.start_pos)], self.start_pos,
self.end_pos)
token_type, open = self.next()
if open != '(':
return None
params = self._parse_parentheses()
token_type, colon = self.next()
annotation = None
if colon in ['-', '->']:
# parse annotations
if colon == '-':
# The Python 2 tokenizer doesn't understand this
token_type, colon = self.next()
if colon != '>':
return None
annotation, colon = self._parse_statement(added_breaks=[':'])
if colon != ':':
return None
# because of 2 line func param definitions
scope = pr.Function(self.module, fname, params, first_pos, annotation)
if self.user_scope and scope != self.user_scope \
and self.user_position > first_pos:
self.user_scope = scope
return scope
def _parse_class(self):
"""
The parser for a text class. Process the tokens, which follow a
class definition.
:return: Return a Scope representation of the tokens.
:rtype: Class
"""
first_pos = self.start_pos
token_type, cname = self.next()
if token_type != tokenize.NAME:
debug.warning("class: syntax err, token is not a name@%s (%s: %s)"
% (self.start_pos[0], tokenize.tok_name[token_type], cname))
return None
cname = pr.Name(self.module, [(cname, self.start_pos)], self.start_pos,
self.end_pos)
super = []
token_type, _next = self.next()
if _next == '(':
super = self._parse_parentheses()
token_type, _next = self.next()
if _next != ':':
debug.warning("class syntax: %s@%s" % (cname, self.start_pos[0]))
return None
# because of 2 line class initializations
scope = pr.Class(self.module, cname, super, first_pos)
if self.user_scope and scope != self.user_scope \
and self.user_position > first_pos:
self.user_scope = scope
return scope
def _parse_statement(self, pre_used_token=None, added_breaks=None,
stmt_class=pr.Statement):
"""
Parses statements like::
a = test(b)
a += 3 - 2 or b
and so on. One line at a time.
:param pre_used_token: The pre parsed token.
:type pre_used_token: set
:return: Statement + last parsed token.
:rtype: (Statement, str)
"""
set_vars = []
used_vars = []
level = 0 # The level of parentheses
if pre_used_token:
token_type, tok = pre_used_token
else:
token_type, tok = self.next()
while token_type == tokenize.COMMENT:
# remove newline and comment
self.next()
token_type, tok = self.next()
first_pos = self.start_pos
opening_brackets = ['{', '(', '[']
closing_brackets = ['}', ')', ']']
# the difference between "break" and "always break" is that the latter
# will even break in parentheses. This is true for typical flow
# commands like def and class and the imports, which will never be used
# in a statement.
breaks = set(['\n', ':', ')'])
always_break = [';', 'import', 'from', 'class', 'def', 'try', 'except',
'finally', 'while', 'return', 'yield']
not_first_break = ['del', 'raise']
if added_breaks:
breaks |= set(added_breaks)
tok_list = []
while not (tok in always_break
or tok in not_first_break and not tok_list
or tok in breaks and level <= 0):
try:
#print 'parse_stmt', tok, tokenize.tok_name[token_type]
tok_list.append(self.current + (self.start_pos,))
if tok == 'as':
token_type, tok = self.next()
if token_type == tokenize.NAME:
n, token_type, tok = self._parse_dot_name(self.current)
if n:
set_vars.append(n)
tok_list.append(n)
continue
elif tok in ['lambda', 'for', 'in']:
# don't parse these keywords, parse later in stmt.
if tok == 'lambda':
breaks.discard(':')
elif token_type == tokenize.NAME:
n, token_type, tok = self._parse_dot_name(self.current)
# removed last entry, because we add Name
tok_list.pop()
if n:
tok_list.append(n)
used_vars.append(n)
continue
elif tok.endswith('=') and tok not in ['>=', '<=', '==', '!=']:
# there has been an assignement -> change vars
if level == 0:
set_vars += used_vars
used_vars = []
elif tok in opening_brackets:
level += 1
elif tok in closing_brackets:
level -= 1
token_type, tok = self.next()
except (StopIteration, common.MultiLevelStopIteration):
# comes from tokenizer
break
if not tok_list:
return None, tok
#print 'new_stat', set_vars, used_vars
if self.freshscope and not self.no_docstr and len(tok_list) == 1 \
and self.last_token[0] == tokenize.STRING:
self.scope.add_docstr(self.last_token[1])
return None, tok
else:
stmt = stmt_class(self.module, set_vars, used_vars, tok_list,
first_pos, self.end_pos)
stmt.parent = self.top_module
self._check_user_stmt(stmt)
# Attribute docstring (PEP 224) support (sphinx uses it, e.g.)
with common.ignored(IndexError, AttributeError):
# If string literal is being parsed
first_tok = stmt.token_list[0]
if (not stmt.set_vars
and not stmt.used_vars
and len(stmt.token_list) == 1
and first_tok[0] == tokenize.STRING):
# ... then set it as a docstring
self.scope.statements[-1].add_docstr(first_tok[1])
if tok in always_break + not_first_break:
self._gen.push_last_back()
return stmt, tok
def next(self):
return self.__next__()
def __iter__(self):
return self
def __next__(self):
""" Generate the next tokenize pattern. """
try:
typ, tok, start_pos, end_pos, self.parserline = next(self._gen)
# dedents shouldn't change positions
if typ != tokenize.DEDENT:
self.start_pos, self.end_pos = start_pos, end_pos
except (StopIteration, common.MultiLevelStopIteration):
# on finish, set end_pos correctly
s = self.scope
while s is not None:
if isinstance(s, pr.Module) \
and not isinstance(s, pr.SubModule):
self.module.end_pos = self.end_pos
break
s.end_pos = self.end_pos
s = s.parent
raise
if self.user_position and (self.start_pos[0] == self.user_position[0]
or self.user_scope is None
and self.start_pos[0] >= self.user_position[0]):
debug.dbg('user scope found [%s] = %s' %
(self.parserline.replace('\n', ''), repr(self.scope)))
self.user_scope = self.scope
self.last_token = self.current
self.current = (typ, tok)
return self.current
def _parse(self):
"""
The main part of the program. It analyzes the given code-text and
returns a tree-like scope. For a more detailed description, see the
class description.
:param text: The code which should be parsed.
:param type: str
:raises: IndentationError
"""
extended_flow = ['else', 'elif', 'except', 'finally']
statement_toks = ['{', '[', '(', '`']
self._decorators = []
self.freshscope = True
self.iterator = iter(self)
# This iterator stuff is not intentional. It grew historically.
for token_type, tok in self.iterator:
self.module.temp_used_names = []
#debug.dbg('main: tok=[%s] type=[%s] indent=[%s]'\
# % (tok, tokenize.tok_name[token_type], start_position[0]))
while token_type == tokenize.DEDENT and self.scope != self.module:
token_type, tok = self.next()
if self.start_pos[1] <= self.scope.start_pos[1]:
self.scope.end_pos = self.start_pos
self.scope = self.scope.parent
if isinstance(self.scope, pr.Module) \
and not isinstance(self.scope, pr.SubModule):
self.scope = self.module
# check again for unindented stuff. this is true for syntax
# errors. only check for names, because thats relevant here. If
# some docstrings are not indented, I don't care.
while self.start_pos[1] <= self.scope.start_pos[1] \
and (token_type == tokenize.NAME or tok in ['(', '['])\
and self.scope != self.module:
self.scope.end_pos = self.start_pos
self.scope = self.scope.parent
if isinstance(self.scope, pr.Module) \
and not isinstance(self.scope, pr.SubModule):
self.scope = self.module
use_as_parent_scope = self.top_module if isinstance(self.scope,
pr.SubModule) else self.scope
first_pos = self.start_pos
if tok == 'def':
func = self._parse_function()
if func is None:
debug.warning("function: syntax error@%s" %
self.start_pos[0])
continue
self.freshscope = True
self.scope = self.scope.add_scope(func, self._decorators)
self._decorators = []
elif tok == 'class':
cls = self._parse_class()
if cls is None:
debug.warning("class: syntax error@%s" % self.start_pos[0])
continue
self.freshscope = True
self.scope = self.scope.add_scope(cls, self._decorators)
self._decorators = []
# import stuff
elif tok == 'import':
imports = self._parse_import_list()
for count, (m, alias, defunct) in enumerate(imports):
e = (alias or m or self).end_pos
end_pos = self.end_pos if count + 1 == len(imports) else e
i = pr.Import(self.module, first_pos, end_pos, m,
alias, defunct=defunct)
self._check_user_stmt(i)
self.scope.add_import(i)
if not imports:
i = pr.Import(self.module, first_pos, self.end_pos, None,
defunct=True)
self._check_user_stmt(i)
self.freshscope = False
elif tok == 'from':
defunct = False
# take care for relative imports
relative_count = 0
while 1:
token_type, tok = self.next()
if tok != '.':
break
relative_count += 1
# the from import
mod, token_type, tok = self._parse_dot_name(self.current)
if str(mod) == 'import' and relative_count:
self._gen.push_last_back()
tok = 'import'
mod = None
if not mod and not relative_count or tok != "import":
debug.warning("from: syntax error@%s" % self.start_pos[0])
defunct = True
if tok != 'import':
self._gen.push_last_back()
names = self._parse_import_list()
for count, (name, alias, defunct2) in enumerate(names):
star = name is not None and name.names[0] == '*'
if star:
name = None
e = (alias or name or self).end_pos
end_pos = self.end_pos if count + 1 == len(names) else e
i = pr.Import(self.module, first_pos, end_pos, name,
alias, mod, star, relative_count,
defunct=defunct or defunct2)
self._check_user_stmt(i)
self.scope.add_import(i)
self.freshscope = False
#loops
elif tok == 'for':
set_stmt, tok = self._parse_statement(added_breaks=['in'])
if tok == 'in':
statement, tok = self._parse_statement()
if tok == ':':
s = [] if statement is None else [statement]
f = pr.ForFlow(self.module, s, first_pos, set_stmt)
self.scope = self.scope.add_statement(f)
else:
debug.warning('syntax err, for flow started @%s',
self.start_pos[0])
if statement is not None:
statement.parent = use_as_parent_scope
if set_stmt is not None:
set_stmt.parent = use_as_parent_scope
else:
debug.warning('syntax err, for flow incomplete @%s',
self.start_pos[0])
if set_stmt is not None:
set_stmt.parent = use_as_parent_scope
elif tok in ['if', 'while', 'try', 'with'] + extended_flow:
added_breaks = []
command = tok
if command in ['except', 'with']:
added_breaks.append(',')
# multiple inputs because of with
inputs = []
first = True
while first or command == 'with' \
and tok not in [':', '\n']:
statement, tok = \
self._parse_statement(added_breaks=added_breaks)
if command == 'except' and tok in added_breaks:
# the except statement defines a var
# this is only true for python 2
n, token_type, tok = self._parse_dot_name()
if n:
statement.set_vars.append(n)
if statement:
inputs.append(statement)
first = False
if tok == ':':
f = pr.Flow(self.module, command, inputs, first_pos)
if command in extended_flow:
# the last statement has to be another part of
# the flow statement, because a dedent releases the
# main scope, so just take the last statement.
try:
s = self.scope.statements[-1].set_next(f)
except (AttributeError, IndexError):
# If set_next doesn't exist, just add it.
s = self.scope.add_statement(f)
else:
s = self.scope.add_statement(f)
self.scope = s
else:
for i in inputs:
i.parent = use_as_parent_scope
debug.warning('syntax err, flow started @%s',
self.start_pos[0])
# returns
elif tok in ['return', 'yield']:
s = self.start_pos
self.freshscope = False
# add returns to the scope
func = self.scope.get_parent_until(pr.Function)
if tok == 'yield':
func.is_generator = True
stmt, tok = self._parse_statement()
if stmt is not None:
stmt.parent = use_as_parent_scope
try:
func.returns.append(stmt)
# start_pos is the one of the return statement
stmt.start_pos = s
except AttributeError:
debug.warning('return in non-function')
# globals
elif tok == 'global':
stmt, tok = self._parse_statement(self.current)
if stmt:
self.scope.add_statement(stmt)
for name in stmt.used_vars:
# add the global to the top, because there it is
# important.
self.module.add_global(name)
# decorator
elif tok == '@':
stmt, tok = self._parse_statement()
self._decorators.append(stmt)
elif tok == 'pass':
continue
elif tok == 'assert':
stmt, tok = self._parse_statement()
stmt.parent = use_as_parent_scope
self.scope.asserts.append(stmt)
# default
elif token_type in [tokenize.NAME, tokenize.STRING,
tokenize.NUMBER] \
or tok in statement_toks:
# this is the main part - a name can be a function or a
# normal var, which can follow anything. but this is done
# by the statement parser.
stmt, tok = self._parse_statement(self.current)
if stmt:
self.scope.add_statement(stmt)
self.freshscope = False
else:
if token_type not in [tokenize.COMMENT, tokenize.INDENT,
tokenize.NEWLINE, tokenize.NL]:
debug.warning('token not classified', tok, token_type,
self.start_pos[0])
continue
self.no_docstr = False

File diff suppressed because it is too large Load Diff

View File

@@ -12,14 +12,11 @@ following functions (sometimes bug-prone):
- 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
from jedi.evaluate import helpers
from jedi.parser import tree as pt
class Refactoring(object):
@@ -64,8 +61,8 @@ def rename(script, new_name):
def _rename(names, replace_str):
""" For both rename and inline. """
order = sorted(names, key=lambda x: (x.module_path, x.start_pos),
reverse=True)
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
@@ -86,13 +83,13 @@ def _rename(names, replace_str):
with open(current_path) as f:
source = f.read()
new_lines = modules.source_to_unicode(source).splitlines()
new_lines = common.splitlines(common.source_to_unicode(source))
old_lines = new_lines[:]
nr, indent = name.start_pos
nr, indent = name.line, name.column
line = new_lines[nr - 1]
new_lines[nr - 1] = line[:indent] + replace_str + \
line[indent + len(name.text):]
line[indent + len(name.name):]
process(current_path, old_lines, new_lines)
return dct
@@ -104,15 +101,15 @@ def extract(script, new_name):
:type source: str
:return: list of changed lines/changed files
"""
new_lines = modules.source_to_unicode(script.source).splitlines()
new_lines = common.splitlines(common.source_to_unicode(script.source))
old_lines = new_lines[:]
user_stmt = script._parser.user_stmt
user_stmt = script._parser.user_stmt()
# TODO care for multiline extracts
dct = {}
if user_stmt:
pos = script.pos
pos = script._pos
line_index = pos[0] - 1
arr, index = helpers.array_for_pos(user_stmt, pos)
if arr is not None:
@@ -148,14 +145,14 @@ def extract(script, new_name):
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])]):
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.source_path] = script.source_path, old_lines, new_lines
dct[script.path] = script.path, old_lines, new_lines
return Refactoring(dct)
@@ -163,40 +160,40 @@ def inline(script):
"""
:type script: api.Script
"""
new_lines = modules.source_to_unicode(script.source).splitlines()
new_lines = common.splitlines(common.source_to_unicode(script.source))
dct = {}
definitions = script.goto()
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.start_pos <= stmt.end_pos]
inlines = sorted(inlines, key=lambda x: (x.module_path, x.start_pos),
reverse=True)
commands = stmt.get_commands()
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)
expression_list = stmt.expression_list()
# 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 = line[expression_list[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 expression_list and isinstance(expression_list[0], pr.Array):
arr = expression_list[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:
if len(stmt.get_defined_names()) == 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.source_path][2]
new_lines = dct[script.path][2]
if line.strip():
new_lines[index] = line
else:

View File

@@ -1,7 +1,6 @@
"""
This module contains variables with global |jedi| setting. To change the
behavior of |jedi|, change the variables defined in
:mod:`jedi.settings`.
This module contains variables with global |jedi| settings. 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.
@@ -38,11 +37,11 @@ Parser
Dynamic stuff
~~~~~~~~~~~~~
.. autodata:: dynamic_arrays_instances
.. autodata:: dynamic_array_additions
.. autodata:: dynamic_params
.. autodata:: dynamic_params_for_other_modules
.. autodata:: additional_dynamic_modules
.. autodata:: auto_import_modules
.. _settings-recursion:
@@ -64,14 +63,14 @@ definitely worse in some cases. But a completion should also be fast.
.. autodata:: max_function_recursion_level
.. autodata:: max_executions_without_builtins
.. autodata:: max_executions
.. autodata:: scale_function_definition
.. autodata:: scale_call_signatures
Caching
~~~~~~~
.. autodata:: star_import_cache_validity
.. autodata:: function_definition_validity
.. autodata:: call_signatures_validity
"""
@@ -127,8 +126,10 @@ 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 ``~/.jedi/`` and on
Windows to ``%APPDATA%\\Jedi\\Jedi\\``.
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.
"""
# ----------------
@@ -146,14 +147,9 @@ function is being reparsed.
# dynamic stuff
# ----------------
dynamic_arrays_instances = True
"""
Check for `append`, etc. on array instances like list()
"""
dynamic_array_additions = True
"""
check for `append`, etc. on arrays: [], {}, ()
check for `append`, etc. on arrays: [], {}, () as well as list/set calls.
"""
dynamic_params = True
@@ -173,6 +169,20 @@ 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.
"""
auto_import_modules = [
'hashlib', # setattr
]
"""
Modules that are not analyzed but imported, although they contain Python code.
This improves autocompletion for libraries that use ``setattr`` or
``globals()`` modifications a lot.
"""
# ----------------
# recursions
# ----------------
@@ -204,9 +214,9 @@ max_executions = 250
A maximum amount of time, the completion may use.
"""
scale_function_definition = 0.1
scale_call_signatures = 0.1
"""
Because function_definition is normally used on every single key hit, it has
Because call_signatures 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`:
"""
@@ -222,7 +232,7 @@ might be slow, therefore we do a star import caching, that lasts a certain
time span (in seconds).
"""
function_definition_validity = 3.0
call_signatures_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.

View File

@@ -1,285 +0,0 @@
"""
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), '')

114
jedi/utils.py Normal file
View File

@@ -0,0 +1,114 @@
"""
Utilities for end-users.
"""
from __future__ import absolute_import
import __main__
from collections import namedtuple
import re
import os
import sys
from jedi import Interpreter
from jedi.api.helpers import completion_parts
from jedi.parser.user_context import UserContext
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>
will complete to ``range``
with both Jedi and readline, but::
range(10).cou<TAB>
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(object):
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:
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 = UserContext(text, (1, len(text))).get_path_until_cursor()
path, dot, like = completion_parts(path)
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('')
def version_info():
"""
Returns a namedtuple of Jedi's version, similar to Python's
``sys.version_info``.
"""
Version = namedtuple('Version', 'major, minor, micro')
from jedi import __version__
tupl = re.findall('[a-z]+|\d+', __version__)
return Version(*[x if i == 3 else int(x) for i, x in enumerate(tupl)])

View File

@@ -2,7 +2,7 @@
addopts = --doctest-modules
# Ignore broken files in blackbox test directories
norecursedirs = .* docs completion refactor
norecursedirs = .* docs completion refactor absolute_import namespace_package scripts extensions speed static_analysis not_in_sys_path buildout_project egg-link init_extension_module
# 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

58
scripts/memory_check.py Executable file
View File

@@ -0,0 +1,58 @@
#! /usr/bin/env python
"""
This is a convenience script to test the speed and memory usage of Jedi with
large libraries.
Each library is preloaded by jedi, recording the time and memory consumed by
each operation.
You can provide additional libraries via command line arguments.
Note: This requires the psutil library, available on PyPI.
"""
import time
import sys
import os
import psutil
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__) + '/..'))
import jedi
def used_memory():
"""Return the total MB of System Memory in use."""
return psutil.virtual_memory().used / 2 ** 20
def profile_preload(mod):
"""Preload a module into Jedi, recording time and memory used."""
base = used_memory()
t0 = time.time()
jedi.preload_module(mod)
elapsed = time.time() - t0
used = used_memory() - base
return elapsed, used
def main(mods):
"""Preload the modules, and print the time and memory used."""
t0 = time.time()
baseline = used_memory()
print('Time (s) | Mem (MB) | Package')
print('------------------------------')
for mod in mods:
elapsed, used = profile_preload(mod)
if used > 0:
print('%8.2f | %8d | %s' % (elapsed, used, mod))
print('------------------------------')
elapsed = time.time() - t0
used = used_memory() - baseline
print('%8.2f | %8d | %s' % (elapsed, used, 'Total'))
if __name__ == '__main__':
if sys.argv[1:]:
mods = sys.argv[1:]
else:
mods = ['re', 'numpy', 'scipy', 'scipy.sparse', 'scipy.stats',
'wx', 'decimal', 'PyQt4.QtGui', 'PySide.QtGui', 'Tkinter']
main(mods)

49
scripts/profile.py Executable file
View File

@@ -0,0 +1,49 @@
#!/usr/bin/env python
"""
Profile a piece of Python code with ``cProfile``. Tries a completion on a
certain piece of code.
Usage:
profile.py [<code>] [-n <number>] [-d] [-o] [-s <sort>]
profile.py -h | --help
Options:
-h --help Show this screen.
-n <number> Number of passes before profiling [default: 1].
-d --debug Enable Jedi internal debugging.
-o --omit Omit profiler, just do a normal run.
-s <sort> Sort the profile results, e.g. cum, name [default: time].
"""
import time
import cProfile
from docopt import docopt
import jedi
def run(code, index):
start = time.time()
result = jedi.Script(code).completions()
print('Used %ss for the %sth run.' % (time.time() - start, index + 1))
return result
def main(args):
code = args['<code>']
n = int(args['-n'])
for i in range(n):
run(code, i)
jedi.set_debug_function(notices=args['--debug'])
if args['--omit']:
run(code, n)
else:
cProfile.runctx('run(code, n)', globals(), locals(), sort=args['-s'])
if __name__ == '__main__':
args = docopt(__doc__)
if args['<code>'] is None:
args['<code>'] = 'import numpy; numpy.array([0])'
main(args)

61
scripts/wx_check.py Executable file
View File

@@ -0,0 +1,61 @@
#! /usr/bin/env python
"""
Depends: ``objgraph`` (third party Python library)
``wx._core`` is a very nice module to test Jedi's speed and memory performance
on big Python modules. Its size is ~16kLOC (one file). It also seems to look
like a typical big Python modules. A mix between a lot of different Python
things.
You can view a markup version of it here:
http://svn.wxwidgets.org/viewvc/wx/wxPython/trunk/src/gtk/_core.py?view=markup
"""
import resource
import time
import sys
try:
import urllib.request as urllib2
except ImportError:
import urllib2
import gc
from os.path import abspath, dirname
import objgraph
sys.path.insert(0, dirname(dirname(abspath(__file__))))
import jedi
def process_memory():
"""
In kB according to
http://stackoverflow.com/questions/938733/total-memory-used-by-python-process
"""
return resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
uri = 'http://svn.wxwidgets.org/viewvc/wx/wxPython/trunk/src/gtk/_core.py?revision=74740&content-type=text%2Fplain&view=co'
wx_core = urllib2.urlopen(uri).read()
def run():
start = time.time()
print('Process Memory before: %skB' % process_memory())
# After this the module should be cached.
# Need to invent a path so that it's really cached.
jedi.Script(wx_core, path='foobar.py').completions()
gc.collect() # make sure that it's all fair and the gc did its job.
print('Process Memory after: %skB' % process_memory())
print(objgraph.most_common_types(limit=50))
print('\nIt took %s seconds to parse the file.' % (time.time() - start))
print('First pass')
run()
print('\nSecond pass')
run()
print('\nThird pass')
run()

View File

@@ -1,35 +1,40 @@
#!/usr/bin/env python
from __future__ import with_statement
from setuptools import setup
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() + '\n\n' + open('CHANGELOG.rst').read()
packages = ['jedi', 'jedi.parser', 'jedi.parser.pgen2',
'jedi.evaluate', 'jedi.evaluate.compiled', 'jedi.api']
import jedi
VERSION = '.'.join(str(x) for x in jedi.__version__)
setup(name='jedi',
version=VERSION,
version=jedi.__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'],
package_data={'jedi': ['mixin/*.pym']},
packages=packages,
package_data={'jedi': ['evaluate/compiled/fake/*.pym', 'parser/grammar*.txt']},
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 :: 2',
'Programming Language :: Python :: 2.6',
@@ -37,8 +42,9 @@ setup(name='jedi',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Text Editors :: Integrated Development Environments (IDE)',
'Topic :: Utilities',
],
)
)

216
sith.py Executable file
View File

@@ -0,0 +1,216 @@
#!/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 [-s] [<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.
-s Shows the path/line numbers of every completion before it starts.
--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.objects = getattr(self.script, self.operation)()
if print_result:
print("{path}: Line {line} column {column}".format(**self.__dict__))
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
prefix = ' |'
for i, line in enumerate(self.script.source.split('\n')[lower:lineno]):
print(prefix, lower + i + 1, line)
print(prefix, ' ', ' ' * (column + len(str(lineno))), '^')
def show_operation(self):
print("%s:\n" % self.operation.capitalize())
if self.operation == 'completions':
self.show_completions()
else:
self.show_definitions()
def show_completions(self):
for completion in self.objects:
print(completion.name)
def show_definitions(self):
for completion in self.objects:
print(completion.desc_with_module)
if completion.module_path is None:
continue
if os.path.abspath(completion.module_path) == os.path.abspath(self.path):
self.show_location(completion.line, completion.column)
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 '.')
if arguments['-s']:
print('%s %s %s %s ' % (t.operation, t.path, t.line, t.column))
sys.stdout.flush()
else:
print('.', end='')
t.run(debugger, record)
sys.stdout.flush()
print()
if __name__ == '__main__':
arguments = docopt(__doc__)
main(arguments)

View File

@@ -11,6 +11,8 @@
[1,""][2]
#? int() str()
[1,""][20]
#? int() str()
[1,""][str(hello)]
a = list()
#? list()
@@ -28,6 +30,9 @@ b = [6,7]
#? int()
b[8-7]
# -----------------
# Slices
# -----------------
#? list()
b[8:]
@@ -35,6 +40,15 @@ b[8:]
b[int():]
class _StrangeSlice():
def __getitem__(self, slice):
return slice
# Should not result in an error, just because the slice itself is returned.
#? []
_StrangeSlice()[1:2]
# -----------------
# iterable multiplication
# -----------------
@@ -42,14 +56,6 @@ a = ['']*2
#? list()
a
a = 2*2
#? int()
a
a = "a"*3
#? str()
a
# -----------------
# tuple assignments
# -----------------
@@ -94,6 +100,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
# -----------------
@@ -128,6 +156,12 @@ def a(): return ''
#? int()
(tuple)().index()
class C():
def __init__(self):
self.a = (str()).upper()
#? str()
C().a
# -----------------
# imbalanced sides
@@ -186,6 +220,18 @@ f()
#? 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
# -----------------
@@ -230,6 +276,17 @@ class GetItem():
#? str()
GetItem("")[1]
class GetItemWithList():
def __getitem__(self, index):
return [1, 1.0, 's'][index]
#? float()
GetItemWithList()[1]
for i in 0, 2:
#? int() str()
GetItemWithList()[i]
# -----------------
# conversions
# -----------------
@@ -240,7 +297,7 @@ list(a)[1]
#? int() str()
list(a)[0]
#?
#?
set(a)[0]
#? int() str()
@@ -280,3 +337,31 @@ tuple((1,))[0]
# implementation detail for lists, should not be visible
#? []
list().__iterable
# With a list comprehension.
for i in set(a for a in [1]):
#? int()
i
# -----------------
# Recursions
# -----------------
def to_list(iterable):
return list(set(iterable))
def recursion1(foo):
return to_list(to_list(foo)) + recursion1(foo)
#? int()
recursion1([1,2])[0]
# -----------------
# Merged Arrays
# -----------------
for x in [1] + ['']:
#? int() str()
x

View File

@@ -23,18 +23,18 @@ a(0):.
# if/else/elif
# -----------------
if 1:
if (random.choice([0, 1])):
1
elif(3):
elif(random.choice([0, 1])):
a = 3
else:
a = ''
#? int() str()
a
def func():
if 1:
if random.choice([0, 1]):
1
elif(3):
elif(random.choice([0, 1])):
a = 3
else:
a = ''
@@ -43,6 +43,18 @@ def func():
#? int() str()
func()
# -----------------
# keywords
# -----------------
#? list()
assert []
def focus_return():
#? list()
return []
# -----------------
# for loops
# -----------------
@@ -93,6 +105,13 @@ for i in b:
#? float() str()
a[0]
for i in [1,2,3]:
#? int()
i
else:
i
# -----------------
# range()
# -----------------
@@ -100,61 +119,6 @@ for i in range(10):
#? int()
i
# -----------------
# list comprehensions
# -----------------
# basics:
a = ['' for a in [1]]
#? str()
a[0]
a = [a for a in [1]]
#? int()
a[0]
a = [a for a in 1,2]
#? int()
a[0]
a = [a for a,b in [(1,'')]]
#? int()
a[0]
arr = [1,'']
a = [a for a in arr]
#? int() str()
a[0]
a = [a if 1.0 else '' for a in [1] if [1.0]]
#? int() str()
a[0]
# -----------------
# nested list comprehensions
# -----------------
b = [a for arr in [[1]] for a in arr]
#? int()
b[0]
b = [a for arr in [[1]] if '' for a in arr if '']
#? int()
b[0]
b = [b for arr in [[[1.0]]] for a in arr for b in a]
#? float()
b[0]
# jedi issue #26
#? list()
a = [[int(v) for v in line.strip().split() if v] for line in ["123", "123", "123"] if line]
#? list()
a[0]
#? int()
a[0][0]
# -----------------
# ternary operator
# -----------------
@@ -179,6 +143,9 @@ ret()[0]
with open('') as f:
#? ['closed']
f.closed
for line in f:
#? str()
line
with open('') as f1, open('') as f2:
#? ['closed']
@@ -209,6 +176,9 @@ def a():
"""
pass
#?
# str literals in comment """ upper
# -----------------
# magic methods
# -----------------
@@ -220,3 +190,80 @@ 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:
# TODO check this only in Python2
##? ['i_b']
i_b
##? ImportError()
i_b
class MyException(Exception):
def __init__(self, my_attr):
self.my_attr = my_attr
try:
raise MyException(1)
except MyException as e:
#? ['my_attr']
e.my_attr
#? 22 ['my_attr']
for x in e.my_attr:
pass
# -----------------
# continuations
# -----------------
foo = \
1
#? int()
foo
# -----------------
# module attributes
# -----------------
# Don't move this to imports.py, because there's a star import.
#? str()
__file__
#? ['__file__']
__file__

View File

@@ -119,6 +119,23 @@ 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
# should also work before `=`
#? 8 int()
A().addition = None
#? 8 int()
A(1).addition = None
a = A()
#? 8 int()
a.addition = None
# -----------------
# inheritance
# -----------------
@@ -162,6 +179,34 @@ SubClass.var
#? ['class_sub', 'class_super']
SubClass.class_
# -----------------
# inheritance of builtins
# -----------------
class Base(str):
pass
#? ['upper']
Base.upper
#? ['upper']
Base().upper
# -----------------
# dynamic inheritance
# -----------------
class Angry(object):
def shout(self):
return 'THIS IS MALARKEY!'
def classgetter():
return Angry
class Dude(classgetter()):
def react(self):
#? ['shout']
self.s
# -----------------
# __call__
# -----------------
@@ -241,192 +286,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
# -----------------
@@ -435,10 +294,10 @@ def Recursion():
self.a = self.a
self.b = self.b.recurse()
#?
#?
Recursion().a
#?
#?
Recursion().b
# -----------------
@@ -480,14 +339,16 @@ getattr(str(), 'upper')
getattr(str, 'upper')
# some strange getattr calls
#?
#?
getattr(str, 1)
#?
#?
getattr()
#?
#?
getattr(str)
#?
#?
getattr(getattr, 1)
#?
getattr(str, [])
class Base():
@@ -511,6 +372,14 @@ Wrapper(Base()).ret(3)
#? int()
Wrapper2(Base()).ret(3)
class GetattrArray():
def __getattr__(self, name):
return [1]
#? int()
GetattrArray().something[0]
# -----------------
# private vars
# -----------------
@@ -519,9 +388,11 @@ class PrivateVar():
self.__var = 1
#? int()
self.__var
#? ['__var']
self.__var
#? []
PrivateVar().__var
#?
#?
PrivateVar().__var
# -----------------
@@ -529,9 +400,11 @@ PrivateVar().__var
# -----------------
class Super(object):
a = 3
def return_sup(self):
return 1
class TestSuper(Super):
#?
#?
super()
def test(self):
#? Super()
@@ -542,9 +415,16 @@ class TestSuper(Super):
#? Super()
super()
def a():
#?
#?
super()
def return_sup(self):
#? int()
return super().return_sup()
#? int()
TestSuper().return_sup()
# -----------------
# if flow at class level
@@ -564,3 +444,32 @@ class TestX(object):
var = self.conditional_method()
#? int()
var
# -----------------
# mro method
# -----------------
class A(object):
a = 3
#? ['mro']
A.mro
#? []
A().mro
# -----------------
# mro resolution
# -----------------
class B(A()):
b = 3
#?
B.a
#?
B().a
#? int()
B.b
#? int()
B().b

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