├── .gitignore ├── .travis.yml ├── ISSUE_TEMPLATE.md ├── LICENSE ├── README.md ├── keymaps └── autocomplete-python-jedi.cson ├── lib ├── completion.py ├── definitions-view.coffee ├── hyperclick-provider.coffee ├── interpreters-lookup.coffee ├── jedi │ ├── __init__.py │ ├── __main__.py │ ├── _compatibility.py │ ├── api │ │ ├── __init__.py │ │ ├── classes.py │ │ ├── completion.py │ │ ├── helpers.py │ │ ├── interpreter.py │ │ ├── keywords.py │ │ ├── replstartup.py │ │ └── usages.py │ ├── cache.py │ ├── common.py │ ├── debug.py │ ├── evaluate │ │ ├── __init__.py │ │ ├── analysis.py │ │ ├── cache.py │ │ ├── compiled │ │ │ ├── __init__.py │ │ │ ├── fake.py │ │ │ ├── fake │ │ │ │ ├── _functools.pym │ │ │ │ ├── _sqlite3.pym │ │ │ │ ├── _sre.pym │ │ │ │ ├── _weakref.pym │ │ │ │ ├── builtins.pym │ │ │ │ ├── datetime.pym │ │ │ │ ├── io.pym │ │ │ │ └── posix.pym │ │ │ └── mixed.py │ │ ├── docstrings.py │ │ ├── dynamic.py │ │ ├── finder.py │ │ ├── flow_analysis.py │ │ ├── helpers.py │ │ ├── imports.py │ │ ├── iterable.py │ │ ├── jedi_typing.py │ │ ├── param.py │ │ ├── pep0484.py │ │ ├── precedence.py │ │ ├── recursion.py │ │ ├── representation.py │ │ ├── site.py │ │ ├── stdlib.py │ │ └── sys_path.py │ ├── parser │ │ ├── __init__.py │ │ ├── fast.py │ │ ├── grammar2.7.txt │ │ ├── grammar3.4.txt │ │ ├── grammar3.5.txt │ │ ├── grammar3.6.txt │ │ ├── pgen2 │ │ │ ├── __init__.py │ │ │ ├── grammar.py │ │ │ ├── parse.py │ │ │ └── pgen.py │ │ ├── token.py │ │ ├── tokenize.py │ │ ├── tree.py │ │ └── utils.py │ ├── refactoring.py │ ├── settings.py │ └── utils.py ├── log.coffee ├── main.coffee ├── override-view.coffee ├── provider.coffee ├── rename-view.coffee ├── scope-helpers.coffee ├── tooltips.coffee ├── touchbar.coffee └── usages-view.coffee ├── package.json ├── spec └── provider-spec.coffee └── styles └── autocomplete-python-jedi.less /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | 29 | *.pyc 30 | 31 | *.DS_Store 32 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | 3 | notifications: 4 | email: 5 | on_success: change 6 | on_failure: always 7 | 8 | script: 'curl -s https://raw.githubusercontent.com/atom/ci/master/build-package.sh | sh' 9 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Prerequisites 2 | 3 | * [ ] Please make sure to properly [configure](https://github.com/brennv/autocomplete-python-jedi#configuration) plugin first if you simply cannot see any completions. 4 | * [ ] It's always helpful if you include debug logs with your issue: 5 | * Go to package settings to enable them 6 | ![image](https://cloud.githubusercontent.com/assets/193864/19852051/01e4e45c-9f9c-11e6-9480-dd22147c82b3.png) 7 | * Open `dev tools` using command palette and you should see some `autocomplete-python-jedi` records in console tab. This will include parts of your code – make sure to remove everything you don't want to share. 8 | 9 | ### Steps to Reproduce 10 | 11 | 1. [First Step] 12 | 2. [Second Step] 13 | 3. [and so on...] 14 | 15 | **Expected behavior:** [What you expected to happen] 16 | 17 | **Actual behavior:** [What actually happened] 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python Autocomplete Jedi Package [![Build Status](https://travis-ci.org/brennv/autocomplete-python-jedi.svg?branch=master)](https://travis-ci.org/brennv/autocomplete-python-jedi) 2 | 3 | Python packages, variables, methods and functions with their arguments autocompletion in [Atom](http://atom.io) powered by [Jedi](https://jedi.readthedocs.io/en/latest/). 4 | 5 | **autocomplete-python-jedi** is a streamlined fork of **autocomplete-python** with good intentions to resolve these [issues](https://github.com/autocomplete-python/autocomplete-python/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aopen%20refactor%20OR%20owner%20OR%20telemetry%20). 6 | 7 | See [releases](https://github.com/brennv/autocomplete-python-jedi/releases) for release notes. 8 | 9 | ![Demo](https://cloud.githubusercontent.com/assets/193864/12288427/61fe2114-ba0f-11e5-9832-98869180d87f.gif) 10 | 11 | # Features 12 | 13 | * Works with :apple: Mac OSX, :penguin: Linux and :checkered_flag: Windows. 14 | * Works with both :snake: Python 2 and 3. 15 | * Automatic lookup of virtual environments inside of your projects. 16 | * Configurable additional packages to include for completions. 17 | * Prints first N characters of statement value while completing variables. 18 | * Prints function arguments while completing functions. 19 | * Go-to-definition functionality, by default on `Alt+Cmd+G`/`Ctrl+Alt+G`. Thanks to [@patrys](https://github.com/patrys) for idea and implementation. 20 | * Method override functionality. Available as `override-method` command. Thanks to [@pchomik](https://github.com/pchomik) for idea and help. 21 | * If you have [Hyperclick](https://atom.io/packages/hyperclick) installed – you can click on anything to go-to-definition 22 | ![sample](https://cloud.githubusercontent.com/assets/193864/10814177/17fb8bce-7e5f-11e5-8285-6b0100b3a0f8.gif) 23 | 24 | * Show usages of selected object 25 | ![sample](https://cloud.githubusercontent.com/assets/193864/12263525/aff07ad4-b96a-11e5-949e-598e943b0190.gif) 26 | 27 | * Rename across multiple files. It will not touch files outside of your project, but it will change VCS ignored files. I'm not responsible for any broken projects without VCS because of this. 28 | ![sample](https://cloud.githubusercontent.com/assets/193864/12288191/f448b55a-ba0c-11e5-81d7-31289ef5dbba.gif) 29 | 30 | # Configuration 31 | 32 | * If using a [virtualenv](https://virtualenv.pypa.io/en/latest/) with third-party packages, everything should "just work", but if it's not – use the `Python Executable Paths` and/or `Extra Paths For Packages` configuration options to specify the virtualenv's site-packages. Or launch Atom from the [activated virtualenv](https://virtualenv.pypa.io/en/latest/userguide.html#activate-script) to get completion for your third-party packages 33 | * Be sure to check package settings and adjust them. Please read them carefully before creating any new issues 34 | * Set path to python executable if package cannot find it automatically 35 | * Set extra path if package cannot autocomplete external python libraries 36 | * Select one of autocomplete function parameters if you want function arguments to be completed 37 | 38 | ![image](https://cloud.githubusercontent.com/assets/193864/11631369/aafb34b4-9d3c-11e5-9a06-e8712a21474e.png) 39 | 40 | 41 | # Common problems 42 | 43 | * "Error: spawn UNKNOWN" on Windows 44 | * Solution: Find your python executable and uncheck the "Run this program as an administrator". 45 | * You have a separated folder for virtualenvs (e.g. by using `virtualenvwrapper`) and all your virtualenvs are stored in e.g. `~/.virtualenvs/` 46 | * Create symlink to venv from your project root 47 | * OR 48 | * Add virtualenv folder as additional project root 49 | * OR 50 | * Use a virtualenv with the same name as the folder name of your project and use $PROJECT_NAME variable to set path to python executable. 51 | You can use same variable to set extra paths as well. For example: 52 | ``` 53 | /Users/name/.virtualenvs/$PROJECT_NAME/bin/python3.4 54 | ``` 55 | * No argument completion after I type left parenthesis character 56 | * Likely this is because you have non standard keyboard layout. 57 | Try to install the keyboard-localization package from: https://atom.io/packages/keyboard-localization 58 | and use keymap generator to check what unicode character being generated after you type `(`. 59 | Currently we trigger argument completion only on `U+0028`, `U+0038` and `U+0039`. 60 | -------------------------------------------------------------------------------- /keymaps/autocomplete-python-jedi.cson: -------------------------------------------------------------------------------- 1 | '.platform-darwin atom-text-editor[data-grammar~=python]:not(.mini)': 2 | 'alt-cmd-g': 'autocomplete-python-jedi:go-to-definition' 3 | 4 | '.platform-linux atom-text-editor[data-grammar~=python]:not(.mini)': 5 | 'ctrl-alt-g': 'autocomplete-python-jedi:go-to-definition' 6 | 7 | '.platform-win32 atom-text-editor[data-grammar~=python]:not(.mini)': 8 | 'ctrl-alt-g': 'autocomplete-python-jedi:go-to-definition' 9 | -------------------------------------------------------------------------------- /lib/definitions-view.coffee: -------------------------------------------------------------------------------- 1 | {$$, SelectListView} = require 'atom-space-pen-views' 2 | path = require 'path' 3 | 4 | module.exports = 5 | class DefinitionsView extends SelectListView 6 | initialize: (matches) -> 7 | super 8 | @storeFocusedElement() 9 | @addClass('symbols-view') 10 | @panel ?= atom.workspace.addModalPanel(item: this) 11 | @panel.show() 12 | @setLoading('Looking for definitions') 13 | @focusFilterEditor() 14 | 15 | destroy: -> 16 | @cancel() 17 | @panel.destroy() 18 | 19 | viewForItem: ({text, fileName, line, column, type}) -> 20 | [_, relativePath] = atom.project.relativizePath(fileName) 21 | return $$ -> 22 | @li class: 'two-lines', => 23 | @div "#{type} #{text}", class: 'primary-line' 24 | @div "#{relativePath}, line #{line + 1}", class: 'secondary-line' 25 | 26 | getFilterKey: -> 'fileName' 27 | 28 | getEmptyMessage: (itemCount) -> 29 | if itemCount is 0 30 | 'No definition found' 31 | else 32 | super 33 | 34 | confirmed: ({fileName, line, column}) -> 35 | @cancelPosition = null 36 | @cancel() 37 | promise = atom.workspace.open(fileName) 38 | promise.then (editor) -> 39 | editor.setCursorBufferPosition([line, column]) 40 | editor.scrollToCursorPosition() 41 | 42 | cancelled: -> 43 | @panel?.hide() 44 | -------------------------------------------------------------------------------- /lib/hyperclick-provider.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | priority: 1 3 | providerName: 'autocomplete-python-jedi' 4 | disableForSelector: '.source.python .comment, .source.python .string, .source.python .numeric, .source.python .integer, .source.python .decimal, .source.python .punctuation, .source.python .keyword, .source.python .storage, .source.python .variable.parameter, .source.python .entity.name' 5 | constructed: false 6 | 7 | constructor: -> 8 | @provider = require './provider' 9 | @log = require './log' 10 | {@selectorsMatchScopeChain} = require './scope-helpers' 11 | {@Selector} = require 'selector-kit' 12 | @constructed = true 13 | @log.debug 'Loading python hyper-click provider...' 14 | 15 | _getScopes: (editor, range) -> 16 | return editor.scopeDescriptorForBufferPosition(range).scopes 17 | 18 | getSuggestionForWord: (editor, text, range) -> 19 | if not @constructed 20 | @constructor() 21 | if text in ['.', ':'] 22 | return 23 | if editor.getGrammar().scopeName.indexOf('source.python') > -1 24 | bufferPosition = range.start 25 | scopeDescriptor = editor.scopeDescriptorForBufferPosition( 26 | bufferPosition) 27 | scopeChain = scopeDescriptor.getScopeChain() 28 | disableForSelector = @Selector.create(@disableForSelector) 29 | if @selectorsMatchScopeChain(disableForSelector, scopeChain) 30 | return 31 | 32 | if atom.config.get('autocomplete-python-jedi.outputDebug') 33 | @log.debug range.start, @_getScopes(editor, range.start) 34 | @log.debug range.end, @_getScopes(editor, range.end) 35 | callback = => 36 | @provider.load().goToDefinition(editor, bufferPosition) 37 | return {range, callback} 38 | -------------------------------------------------------------------------------- /lib/interpreters-lookup.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | os = require 'os' 3 | path = require 'path' 4 | log = require './log' 5 | 6 | module.exports = 7 | pythonExecutableRe: -> 8 | if /^win/.test process.platform 9 | return /^python(\d+(.\d+)?)?\.exe$/ 10 | else 11 | return /^python(\d+(.\d+)?)?$/ 12 | 13 | possibleGlobalPythonPaths: -> 14 | if /^win/.test process.platform 15 | return [ 16 | 'C:\\Python2.7' 17 | 'C:\\Python3.4' 18 | 'C:\\Python3.5' 19 | 'C:\\Program Files (x86)\\Python 2.7' 20 | 'C:\\Program Files (x86)\\Python 3.4' 21 | 'C:\\Program Files (x86)\\Python 3.5' 22 | 'C:\\Program Files (x64)\\Python 2.7' 23 | 'C:\\Program Files (x64)\\Python 3.4' 24 | 'C:\\Program Files (x64)\\Python 3.5' 25 | 'C:\\Program Files\\Python 2.7' 26 | 'C:\\Program Files\\Python 3.4' 27 | 'C:\\Program Files\\Python 3.5' 28 | "#{os.homedir()}\\AppData\\Local\\Programs\\Python\\Python35-32" 29 | ] 30 | else 31 | return ['/usr/local/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin'] 32 | 33 | readDir: (dirPath) -> 34 | try 35 | return fs.readdirSync dirPath 36 | catch 37 | return [] 38 | 39 | isBinary: (filePath) -> 40 | try 41 | fs.accessSync filePath, fs.X_OK 42 | return true 43 | catch 44 | return false 45 | 46 | lookupInterpreters: (dirPath) -> 47 | interpreters = new Set() 48 | files = @readDir(dirPath) 49 | matches = (f for f in files when @pythonExecutableRe().test(f)) 50 | for fileName in matches 51 | potentialInterpreter = path.join(dirPath, fileName) 52 | if @isBinary(potentialInterpreter) 53 | interpreters.add(potentialInterpreter) 54 | return interpreters 55 | 56 | applySubstitutions: (paths) -> 57 | modPaths = [] 58 | for p in paths 59 | if /\$PROJECT/.test p 60 | for project in atom.project.getPaths() 61 | [..., projectName] = project.split(path.sep) 62 | p = p.replace(/\$PROJECT_NAME/i, projectName) 63 | p = p.replace(/\$PROJECT/i, project) 64 | if p not in modPaths 65 | modPaths.push p 66 | else 67 | modPaths.push p 68 | return modPaths 69 | 70 | getInterpreter: -> 71 | userDefinedPythonPaths = @applySubstitutions( 72 | atom.config.get('autocomplete-python-jedi.pythonPaths').split(';')) 73 | interpreters = new Set(p for p in userDefinedPythonPaths when @isBinary(p)) 74 | if interpreters.size > 0 75 | log.debug 'User defined interpreters found', interpreters 76 | return interpreters.keys().next().value 77 | 78 | log.debug 'No user defined interpreter found, trying automatic lookup' 79 | interpreters = new Set() 80 | 81 | for project in atom.project.getPaths() 82 | for f in @readDir(project) 83 | @lookupInterpreters(path.join(project, f, 'bin')).forEach (i) -> 84 | interpreters.add(i) 85 | log.debug 'Project level interpreters found', interpreters 86 | envPath = (process.env.PATH or '').split path.delimiter 87 | envPath = new Set(envPath.concat(@possibleGlobalPythonPaths())) 88 | envPath.forEach (potentialPath) => 89 | @lookupInterpreters(potentialPath).forEach (i) -> 90 | interpreters.add(i) 91 | log.debug 'Total automatically found interpreters', interpreters 92 | 93 | if interpreters.size > 0 94 | return interpreters.keys().next().value 95 | -------------------------------------------------------------------------------- /lib/jedi/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Jedi is a static analysis tool for Python that can be used in IDEs/editors. Its 3 | historic focus is autocompletion, but does static analysis for now as well. 4 | Jedi is fast and is very well tested. It understands Python on a deeper level 5 | than all other static analysis frameworks for Python. 6 | 7 | Jedi has support for two different goto functions. It's possible to search for 8 | related names and to list all names in a Python file and infer them. Jedi 9 | understands docstrings and you can use Jedi autocompletion in your REPL as 10 | well. 11 | 12 | Jedi uses a very simple API to connect with IDE's. There's a reference 13 | implementation as a `VIM-Plugin `_, 14 | which uses Jedi's autocompletion. We encourage you to use Jedi in your IDEs. 15 | It's really easy. 16 | 17 | To give you a simple example how you can use the Jedi library, here is an 18 | example for the autocompletion feature: 19 | 20 | >>> import jedi 21 | >>> source = ''' 22 | ... import datetime 23 | ... datetime.da''' 24 | >>> script = jedi.Script(source, 3, len('datetime.da'), 'example.py') 25 | >>> script 26 | 27 | >>> completions = script.completions() 28 | >>> completions #doctest: +ELLIPSIS 29 | [, , ...] 30 | >>> print(completions[0].complete) 31 | te 32 | >>> print(completions[0].name) 33 | date 34 | 35 | As you see Jedi is pretty simple and allows you to concentrate on writing a 36 | good text editor, while still having very good IDE features for Python. 37 | """ 38 | 39 | __version__ = '0.10.0' 40 | 41 | from jedi.api import Script, Interpreter, NotFoundError, set_debug_function 42 | from jedi.api import preload_module, defined_names, names 43 | from jedi import settings 44 | -------------------------------------------------------------------------------- /lib/jedi/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from os.path import join, dirname, abspath, isdir 3 | 4 | 5 | def _start_linter(): 6 | """ 7 | This is a pre-alpha API. You're not supposed to use it at all, except for 8 | testing. It will very likely change. 9 | """ 10 | import jedi 11 | 12 | if '--debug' in sys.argv: 13 | jedi.set_debug_function() 14 | 15 | for path in sys.argv[2:]: 16 | if path.startswith('--'): 17 | continue 18 | if isdir(path): 19 | import fnmatch 20 | import os 21 | 22 | paths = [] 23 | for root, dirnames, filenames in os.walk(path): 24 | for filename in fnmatch.filter(filenames, '*.py'): 25 | paths.append(os.path.join(root, filename)) 26 | else: 27 | paths = [path] 28 | 29 | try: 30 | for path in paths: 31 | for error in jedi.Script(path=path)._analysis(): 32 | print(error) 33 | except Exception: 34 | if '--pdb' in sys.argv: 35 | import traceback 36 | traceback.print_exc() 37 | import pdb 38 | pdb.post_mortem() 39 | else: 40 | raise 41 | 42 | 43 | if len(sys.argv) == 2 and sys.argv[1] == 'repl': 44 | # don't want to use __main__ only for repl yet, maybe we want to use it for 45 | # something else. So just use the keyword ``repl`` for now. 46 | print(join(dirname(abspath(__file__)), 'api', 'replstartup.py')) 47 | elif len(sys.argv) > 1 and sys.argv[1] == 'linter': 48 | _start_linter() 49 | -------------------------------------------------------------------------------- /lib/jedi/_compatibility.py: -------------------------------------------------------------------------------- 1 | """ 2 | To ensure compatibility from Python ``2.6`` - ``3.3``, a module has been 3 | created. Clearly there is huge need to use conforming syntax. 4 | """ 5 | import sys 6 | import imp 7 | import os 8 | import re 9 | import pkgutil 10 | try: 11 | import importlib 12 | except ImportError: 13 | pass 14 | 15 | is_py3 = sys.version_info[0] >= 3 16 | is_py33 = is_py3 and sys.version_info.minor >= 3 17 | is_py34 = is_py3 and sys.version_info.minor >= 4 18 | is_py35 = is_py3 and sys.version_info.minor >= 5 19 | is_py26 = not is_py3 and sys.version_info[1] < 7 20 | 21 | 22 | class DummyFile(object): 23 | def __init__(self, loader, string): 24 | self.loader = loader 25 | self.string = string 26 | 27 | def read(self): 28 | return self.loader.get_source(self.string) 29 | 30 | def close(self): 31 | del self.loader 32 | 33 | 34 | def find_module_py33(string, path=None): 35 | loader = importlib.machinery.PathFinder.find_module(string, path) 36 | 37 | if loader is None and path is None: # Fallback to find builtins 38 | try: 39 | loader = importlib.find_loader(string) 40 | except ValueError as e: 41 | # See #491. Importlib might raise a ValueError, to avoid this, we 42 | # just raise an ImportError to fix the issue. 43 | raise ImportError("Originally " + repr(e)) 44 | 45 | if loader is None: 46 | raise ImportError("Couldn't find a loader for {0}".format(string)) 47 | 48 | try: 49 | is_package = loader.is_package(string) 50 | if is_package: 51 | if hasattr(loader, 'path'): 52 | module_path = os.path.dirname(loader.path) 53 | else: 54 | # At least zipimporter does not have path attribute 55 | module_path = os.path.dirname(loader.get_filename(string)) 56 | if hasattr(loader, 'archive'): 57 | module_file = DummyFile(loader, string) 58 | else: 59 | module_file = None 60 | else: 61 | module_path = loader.get_filename(string) 62 | module_file = DummyFile(loader, string) 63 | except AttributeError: 64 | # ExtensionLoader has not attribute get_filename, instead it has a 65 | # path attribute that we can use to retrieve the module path 66 | try: 67 | module_path = loader.path 68 | module_file = DummyFile(loader, string) 69 | except AttributeError: 70 | module_path = string 71 | module_file = None 72 | finally: 73 | is_package = False 74 | 75 | if hasattr(loader, 'archive'): 76 | module_path = loader.archive 77 | 78 | return module_file, module_path, is_package 79 | 80 | 81 | def find_module_pre_py33(string, path=None): 82 | try: 83 | module_file, module_path, description = imp.find_module(string, path) 84 | module_type = description[2] 85 | return module_file, module_path, module_type is imp.PKG_DIRECTORY 86 | except ImportError: 87 | pass 88 | 89 | if path is None: 90 | path = sys.path 91 | for item in path: 92 | loader = pkgutil.get_importer(item) 93 | if loader: 94 | try: 95 | loader = loader.find_module(string) 96 | if loader: 97 | is_package = loader.is_package(string) 98 | is_archive = hasattr(loader, 'archive') 99 | try: 100 | module_path = loader.get_filename(string) 101 | except AttributeError: 102 | # fallback for py26 103 | try: 104 | module_path = loader._get_filename(string) 105 | except AttributeError: 106 | continue 107 | if is_package: 108 | module_path = os.path.dirname(module_path) 109 | if is_archive: 110 | module_path = loader.archive 111 | file = None 112 | if not is_package or is_archive: 113 | file = DummyFile(loader, string) 114 | return (file, module_path, is_package) 115 | except ImportError: 116 | pass 117 | raise ImportError("No module named {0}".format(string)) 118 | 119 | 120 | find_module = find_module_py33 if is_py33 else find_module_pre_py33 121 | find_module.__doc__ = """ 122 | Provides information about a module. 123 | 124 | This function isolates the differences in importing libraries introduced with 125 | python 3.3 on; it gets a module name and optionally a path. It will return a 126 | tuple containin an open file for the module (if not builtin), the filename 127 | or the name of the module if it is a builtin one and a boolean indicating 128 | if the module is contained in a package. 129 | """ 130 | 131 | 132 | # unicode function 133 | try: 134 | unicode = unicode 135 | except NameError: 136 | unicode = str 137 | 138 | if is_py3: 139 | u = lambda s: s 140 | else: 141 | u = lambda s: s.decode('utf-8') 142 | 143 | u.__doc__ = """ 144 | Decode a raw string into unicode object. Do nothing in Python 3. 145 | """ 146 | 147 | # exec function 148 | if is_py3: 149 | def exec_function(source, global_map): 150 | exec(source, global_map) 151 | else: 152 | eval(compile("""def exec_function(source, global_map): 153 | exec source in global_map """, 'blub', 'exec')) 154 | 155 | # re-raise function 156 | if is_py3: 157 | def reraise(exception, traceback): 158 | raise exception.with_traceback(traceback) 159 | else: 160 | eval(compile(""" 161 | def reraise(exception, traceback): 162 | raise exception, None, traceback 163 | """, 'blub', 'exec')) 164 | 165 | reraise.__doc__ = """ 166 | Re-raise `exception` with a `traceback` object. 167 | 168 | Usage:: 169 | 170 | reraise(Exception, sys.exc_info()[2]) 171 | 172 | """ 173 | 174 | class Python3Method(object): 175 | def __init__(self, func): 176 | self.func = func 177 | 178 | def __get__(self, obj, objtype): 179 | if obj is None: 180 | return lambda *args, **kwargs: self.func(*args, **kwargs) 181 | else: 182 | return lambda *args, **kwargs: self.func(obj, *args, **kwargs) 183 | 184 | 185 | def use_metaclass(meta, *bases): 186 | """ Create a class with a metaclass. """ 187 | if not bases: 188 | bases = (object,) 189 | return meta("HackClass", bases, {}) 190 | 191 | 192 | try: 193 | encoding = sys.stdout.encoding 194 | if encoding is None: 195 | encoding = 'utf-8' 196 | except AttributeError: 197 | encoding = 'ascii' 198 | 199 | 200 | def u(string): 201 | """Cast to unicode DAMMIT! 202 | Written because Python2 repr always implicitly casts to a string, so we 203 | have to cast back to a unicode (and we now that we always deal with valid 204 | unicode, because we check that in the beginning). 205 | """ 206 | if is_py3: 207 | return str(string) 208 | elif not isinstance(string, unicode): 209 | return unicode(str(string), 'UTF-8') 210 | return string 211 | 212 | try: 213 | import builtins # module name in python 3 214 | except ImportError: 215 | import __builtin__ as builtins 216 | 217 | 218 | import ast 219 | 220 | 221 | def literal_eval(string): 222 | # py3.0, py3.1 and py32 don't support unicode literals. Support those, I 223 | # don't want to write two versions of the tokenizer. 224 | if is_py3 and sys.version_info.minor < 3: 225 | if re.match('[uU][\'"]', string): 226 | string = string[1:] 227 | return ast.literal_eval(string) 228 | 229 | 230 | try: 231 | from itertools import zip_longest 232 | except ImportError: 233 | from itertools import izip_longest as zip_longest # Python 2 234 | 235 | 236 | def no_unicode_pprint(dct): 237 | """ 238 | Python 2/3 dict __repr__ may be different, because of unicode differens 239 | (with or without a `u` prefix). Normally in doctests we could use `pprint` 240 | to sort dicts and check for equality, but here we have to write a separate 241 | function to do that. 242 | """ 243 | import pprint 244 | s = pprint.pformat(dct) 245 | print(re.sub("u'", "'", s)) 246 | 247 | 248 | def utf8_repr(func): 249 | """ 250 | ``__repr__`` methods in Python 2 don't allow unicode objects to be 251 | returned. Therefore cast them to utf-8 bytes in this decorator. 252 | """ 253 | def wrapper(self): 254 | result = func(self) 255 | if isinstance(result, unicode): 256 | return result.encode('utf-8') 257 | else: 258 | return result 259 | 260 | if is_py3: 261 | return func 262 | else: 263 | return wrapper 264 | -------------------------------------------------------------------------------- /lib/jedi/api/interpreter.py: -------------------------------------------------------------------------------- 1 | """ 2 | TODO Some parts of this module are still not well documented. 3 | """ 4 | import copy 5 | 6 | from jedi.cache import underscore_memoization 7 | from jedi.evaluate import helpers 8 | from jedi.evaluate.representation import ModuleWrapper 9 | from jedi.evaluate.compiled import mixed 10 | 11 | 12 | class MixedModule(object): 13 | resets_positions = True 14 | type = 'mixed_module' 15 | 16 | def __init__(self, evaluator, parser_module, namespaces): 17 | self._evaluator = evaluator 18 | self._namespaces = namespaces 19 | 20 | self._namespace_objects = [type('jedi_namespace', (), n) for n in namespaces] 21 | self._wrapped_module = ModuleWrapper(evaluator, parser_module) 22 | # Usually we are dealing with very small code sizes when it comes to 23 | # interpreter modules. In this case we just copy the whole syntax tree 24 | # to be able to modify it. 25 | self._parser_module = copy.deepcopy(parser_module) 26 | 27 | for child in self._parser_module.children: 28 | child.parent = self 29 | 30 | def names_dicts(self, search_global): 31 | for names_dict in self._wrapped_module.names_dicts(search_global): 32 | yield names_dict 33 | 34 | for namespace_obj in self._namespace_objects: 35 | m = mixed.MixedObject(self._evaluator, namespace_obj, self._parser_module.name) 36 | for names_dict in m.names_dicts(False): 37 | yield names_dict 38 | 39 | def __getattr__(self, name): 40 | return getattr(self._parser_module, name) 41 | 42 | 43 | class LazyName(helpers.FakeName): 44 | def __init__(self, evaluator, module, name, value): 45 | super(LazyName, self).__init__(name) 46 | self._module = module 47 | self._evaluator = evaluator 48 | self._value = value 49 | self._name = name 50 | 51 | def is_definition(self): 52 | return True 53 | 54 | @property 55 | @underscore_memoization 56 | def parent(self): 57 | """ 58 | Creating fake statements for the interpreter. 59 | 60 | Here we are trying to link back to Python code, if possible. This means 61 | we try to find the python module for a name (not the builtin). 62 | """ 63 | return mixed.create(self._evaluator, self._value) 64 | 65 | @parent.setter 66 | def parent(self, value): 67 | """Needed because the super class tries to set parent.""" 68 | -------------------------------------------------------------------------------- /lib/jedi/api/keywords.py: -------------------------------------------------------------------------------- 1 | import pydoc 2 | import keyword 3 | 4 | from jedi._compatibility import is_py3, is_py35 5 | from jedi import common 6 | from jedi.evaluate.helpers import FakeName 7 | from jedi.parser.tree import Leaf 8 | try: 9 | from pydoc_data import topics as pydoc_topics 10 | except ImportError: 11 | # Python 2 12 | try: 13 | import pydoc_topics 14 | except ImportError: 15 | # This is for Python 3 embeddable version, which dont have 16 | # pydoc_data module in its file python3x.zip. 17 | pydoc_topics = None 18 | 19 | if is_py3: 20 | if is_py35: 21 | # in python 3.5 async and await are not proper keywords, but for 22 | # completion pursposes should as as though they are 23 | keys = keyword.kwlist + ["async", "await"] 24 | else: 25 | keys = keyword.kwlist 26 | else: 27 | keys = keyword.kwlist + ['None', 'False', 'True'] 28 | 29 | 30 | def has_inappropriate_leaf_keyword(pos, module): 31 | relevant_errors = filter( 32 | lambda error: error.first_pos[0] == pos[0], 33 | module.error_statement_stacks) 34 | 35 | for error in relevant_errors: 36 | if error.next_token in keys: 37 | return True 38 | 39 | return False 40 | 41 | 42 | def completion_names(evaluator, stmt, pos, module): 43 | keyword_list = all_keywords(evaluator) 44 | 45 | if not isinstance(stmt, Leaf) or has_inappropriate_leaf_keyword(pos, module): 46 | keyword_list = filter( 47 | lambda keyword: not keyword.only_valid_as_leaf, 48 | keyword_list 49 | ) 50 | return [keyword.name for keyword in keyword_list] 51 | 52 | 53 | def all_keywords(evaluator, pos=(0, 0)): 54 | return set([Keyword(evaluator, k, pos) for k in keys]) 55 | 56 | 57 | def keyword(evaluator, string, pos=(0, 0)): 58 | if string in keys: 59 | return Keyword(evaluator, string, pos) 60 | else: 61 | return None 62 | 63 | 64 | def get_operator(evaluator, string, pos): 65 | return Keyword(evaluator, string, pos) 66 | 67 | 68 | keywords_only_valid_as_leaf = ( 69 | 'continue', 70 | 'break', 71 | ) 72 | 73 | 74 | class Keyword(object): 75 | type = 'completion_keyword' 76 | 77 | def __init__(self, evaluator, name, pos): 78 | self.name = FakeName(name, self, pos) 79 | self.start_pos = pos 80 | self.parent = evaluator.BUILTINS 81 | 82 | def get_parent_until(self): 83 | return self.parent 84 | 85 | @property 86 | def only_valid_as_leaf(self): 87 | return self.name.value in keywords_only_valid_as_leaf 88 | 89 | @property 90 | def names(self): 91 | """ For a `parsing.Name` like comparision """ 92 | return [self.name] 93 | 94 | @property 95 | def docstr(self): 96 | return imitate_pydoc(self.name) 97 | 98 | def __repr__(self): 99 | return '<%s: %s>' % (type(self).__name__, self.name) 100 | 101 | 102 | def imitate_pydoc(string): 103 | """ 104 | It's not possible to get the pydoc's without starting the annoying pager 105 | stuff. 106 | """ 107 | if pydoc_topics is None: 108 | return '' 109 | 110 | # str needed because of possible unicode stuff in py2k (pydoc doesn't work 111 | # with unicode strings) 112 | string = str(string) 113 | h = pydoc.help 114 | with common.ignored(KeyError): 115 | # try to access symbols 116 | string = h.symbols[string] 117 | string, _, related = string.partition(' ') 118 | 119 | get_target = lambda s: h.topics.get(s, h.keywords.get(s)) 120 | while isinstance(string, str): 121 | string = get_target(string) 122 | 123 | try: 124 | # is a tuple now 125 | label, related = string 126 | except TypeError: 127 | return '' 128 | 129 | try: 130 | return pydoc_topics.topics[label] if pydoc_topics else '' 131 | except KeyError: 132 | return '' 133 | -------------------------------------------------------------------------------- /lib/jedi/api/replstartup.py: -------------------------------------------------------------------------------- 1 | """ 2 | To use Jedi completion in Python interpreter, add the following in your shell 3 | setup (e.g., ``.bashrc``):: 4 | 5 | export PYTHONSTARTUP="$(python -m jedi repl)" 6 | 7 | Then you will be able to use Jedi completer in your Python interpreter:: 8 | 9 | $ python 10 | Python 2.7.2+ (default, Jul 20 2012, 22:15:08) 11 | [GCC 4.6.1] on linux2 12 | Type "help", "copyright", "credits" or "license" for more information. 13 | >>> import os 14 | >>> os.path.join().split().in # doctest: +SKIP 15 | os.path.join().split().index os.path.join().split().insert 16 | 17 | """ 18 | import jedi.utils 19 | from jedi import __version__ as __jedi_version__ 20 | 21 | print('REPL completion using Jedi %s' % __jedi_version__) 22 | jedi.utils.setup_readline() 23 | 24 | del jedi 25 | 26 | # Note: try not to do many things here, as it will contaminate global 27 | # namespace of the interpreter. 28 | -------------------------------------------------------------------------------- /lib/jedi/api/usages.py: -------------------------------------------------------------------------------- 1 | from jedi._compatibility import unicode 2 | from jedi.api import classes 3 | from jedi.parser import tree 4 | from jedi.evaluate import imports 5 | 6 | 7 | def usages(evaluator, definition_names, mods): 8 | """ 9 | :param definitions: list of Name 10 | """ 11 | def compare_array(definitions): 12 | """ `definitions` are being compared by module/start_pos, because 13 | sometimes the id's of the objects change (e.g. executions). 14 | """ 15 | result = [] 16 | for d in definitions: 17 | module = d.get_parent_until() 18 | result.append((module, d.start_pos)) 19 | return result 20 | 21 | search_name = unicode(list(definition_names)[0]) 22 | compare_definitions = compare_array(definition_names) 23 | mods |= set([d.get_parent_until() for d in definition_names]) 24 | definitions = [] 25 | for m in imports.get_modules_containing_name(evaluator, mods, search_name): 26 | try: 27 | check_names = m.used_names[search_name] 28 | except KeyError: 29 | continue 30 | for name in check_names: 31 | 32 | result = evaluator.goto(name) 33 | if [c for c in compare_array(result) if c in compare_definitions]: 34 | definitions.append(classes.Definition(evaluator, name)) 35 | # Previous definitions might be imports, so include them 36 | # (because goto might return that import name). 37 | compare_definitions += compare_array([name]) 38 | return definitions 39 | 40 | 41 | def usages_add_import_modules(evaluator, definitions): 42 | """ Adds the modules of the imports """ 43 | new = set() 44 | for d in definitions: 45 | imp_or_stmt = d.get_definition() 46 | if isinstance(imp_or_stmt, tree.Import): 47 | s = imports.ImportWrapper(evaluator, d) 48 | new |= set(s.follow(is_goto=True)) 49 | return set(definitions) | new 50 | -------------------------------------------------------------------------------- /lib/jedi/cache.py: -------------------------------------------------------------------------------- 1 | """ 2 | This caching is very important for speed and memory optimizations. There's 3 | nothing really spectacular, just some decorators. The following cache types are 4 | available: 5 | 6 | - module caching (`load_parser` and `save_parser`), which uses pickle and is 7 | really important to assure low load times of modules like ``numpy``. 8 | - ``time_cache`` can be used to cache something for just a limited time span, 9 | which can be useful if there's user interaction and the user cannot react 10 | faster than a certain time. 11 | 12 | This module is one of the reasons why |jedi| is not thread-safe. As you can see 13 | there are global variables, which are holding the cache information. Some of 14 | these variables are being cleaned after every API usage. 15 | """ 16 | import time 17 | 18 | from jedi import settings 19 | from jedi.parser.utils import parser_cache 20 | from jedi.parser.utils import underscore_memoization 21 | 22 | _time_caches = {} 23 | 24 | 25 | def clear_time_caches(delete_all=False): 26 | """ Jedi caches many things, that should be completed after each completion 27 | finishes. 28 | 29 | :param delete_all: Deletes also the cache that is normally not deleted, 30 | like parser cache, which is important for faster parsing. 31 | """ 32 | global _time_caches 33 | 34 | if delete_all: 35 | for cache in _time_caches.values(): 36 | cache.clear() 37 | parser_cache.clear() 38 | else: 39 | # normally just kill the expired entries, not all 40 | for tc in _time_caches.values(): 41 | # check time_cache for expired entries 42 | for key, (t, value) in list(tc.items()): 43 | if t < time.time(): 44 | # delete expired entries 45 | del tc[key] 46 | 47 | 48 | def time_cache(time_add_setting): 49 | """ 50 | This decorator works as follows: Call it with a setting and after that 51 | use the function with a callable that returns the key. 52 | But: This function is only called if the key is not available. After a 53 | certain amount of time (`time_add_setting`) the cache is invalid. 54 | 55 | If the given key is None, the function will not be cached. 56 | """ 57 | def _temp(key_func): 58 | dct = {} 59 | _time_caches[time_add_setting] = dct 60 | 61 | def wrapper(*args, **kwargs): 62 | generator = key_func(*args, **kwargs) 63 | key = next(generator) 64 | try: 65 | expiry, value = dct[key] 66 | if expiry > time.time(): 67 | return value 68 | except KeyError: 69 | pass 70 | 71 | value = next(generator) 72 | time_add = getattr(settings, time_add_setting) 73 | if key is not None: 74 | dct[key] = time.time() + time_add, value 75 | return value 76 | return wrapper 77 | return _temp 78 | 79 | 80 | def memoize_method(method): 81 | """A normal memoize function.""" 82 | def wrapper(self, *args, **kwargs): 83 | dct = self.__dict__.setdefault('_memoize_method_dct', {}) 84 | key = (args, frozenset(kwargs.items())) 85 | try: 86 | return dct[key] 87 | except KeyError: 88 | result = method(self, *args, **kwargs) 89 | dct[key] = result 90 | return result 91 | return wrapper 92 | 93 | 94 | def cache_star_import(func): 95 | @time_cache("star_import_cache_validity") 96 | def wrapper(self): 97 | yield self.base # The cache key 98 | yield func(self) 99 | return wrapper 100 | 101 | 102 | def _invalidate_star_import_cache_module(module, only_main=False): 103 | """ Important if some new modules are being reparsed """ 104 | try: 105 | t, modules = _time_caches['star_import_cache_validity'][module] 106 | except KeyError: 107 | pass 108 | else: 109 | del _time_caches['star_import_cache_validity'][module] 110 | 111 | # This stuff was part of load_parser. However since we're most likely 112 | # not going to use star import caching anymore, just ignore it. 113 | #else: 114 | # In case there is already a module cached and this module 115 | # has to be reparsed, we also need to invalidate the import 116 | # caches. 117 | # _invalidate_star_import_cache_module(parser_cache_item.parser.module) 118 | 119 | 120 | def invalidate_star_import_cache(path): 121 | """On success returns True.""" 122 | try: 123 | parser_cache_item = parser_cache[path] 124 | except KeyError: 125 | pass 126 | else: 127 | _invalidate_star_import_cache_module(parser_cache_item.parser.module) 128 | -------------------------------------------------------------------------------- /lib/jedi/common.py: -------------------------------------------------------------------------------- 1 | """ A universal module with functions / classes without dependencies. """ 2 | import sys 3 | import contextlib 4 | import functools 5 | import re 6 | from itertools import chain 7 | from ast import literal_eval 8 | 9 | from jedi._compatibility import unicode, reraise 10 | from jedi import settings 11 | 12 | 13 | class UncaughtAttributeError(Exception): 14 | """ 15 | Important, because `__getattr__` and `hasattr` catch AttributeErrors 16 | implicitly. This is really evil (mainly because of `__getattr__`). 17 | `hasattr` in Python 2 is even more evil, because it catches ALL exceptions. 18 | Therefore this class originally had to be derived from `BaseException` 19 | instead of `Exception`. But because I removed relevant `hasattr` from 20 | the code base, we can now switch back to `Exception`. 21 | 22 | :param base: return values of sys.exc_info(). 23 | """ 24 | 25 | 26 | def safe_property(func): 27 | return property(reraise_uncaught(func)) 28 | 29 | 30 | def reraise_uncaught(func): 31 | """ 32 | Re-throw uncaught `AttributeError`. 33 | 34 | Usage: Put ``@rethrow_uncaught`` in front of the function 35 | which does **not** suppose to raise `AttributeError`. 36 | 37 | AttributeError is easily get caught by `hasattr` and another 38 | ``except AttributeError`` clause. This becomes problem when you use 39 | a lot of "dynamic" attributes (e.g., using ``@property``) because you 40 | can't distinguish if the property does not exist for real or some code 41 | inside of the "dynamic" attribute through that error. In a well 42 | written code, such error should not exist but getting there is very 43 | difficult. This decorator is to help us getting there by changing 44 | `AttributeError` to `UncaughtAttributeError` to avoid unexpected catch. 45 | This helps us noticing bugs earlier and facilitates debugging. 46 | 47 | .. note:: Treating StopIteration here is easy. 48 | Add that feature when needed. 49 | """ 50 | @functools.wraps(func) 51 | def wrapper(*args, **kwds): 52 | try: 53 | return func(*args, **kwds) 54 | except AttributeError: 55 | exc_info = sys.exc_info() 56 | reraise(UncaughtAttributeError(exc_info[1]), exc_info[2]) 57 | return wrapper 58 | 59 | 60 | class PushBackIterator(object): 61 | def __init__(self, iterator): 62 | self.pushes = [] 63 | self.iterator = iterator 64 | self.current = None 65 | 66 | def push_back(self, value): 67 | self.pushes.append(value) 68 | 69 | def __iter__(self): 70 | return self 71 | 72 | def next(self): 73 | """ Python 2 Compatibility """ 74 | return self.__next__() 75 | 76 | def __next__(self): 77 | if self.pushes: 78 | self.current = self.pushes.pop() 79 | else: 80 | self.current = next(self.iterator) 81 | return self.current 82 | 83 | 84 | @contextlib.contextmanager 85 | def scale_speed_settings(factor): 86 | a = settings.max_executions 87 | b = settings.max_until_execution_unique 88 | settings.max_executions *= factor 89 | settings.max_until_execution_unique *= factor 90 | try: 91 | yield 92 | finally: 93 | settings.max_executions = a 94 | settings.max_until_execution_unique = b 95 | 96 | 97 | def indent_block(text, indention=' '): 98 | """This function indents a text block with a default of four spaces.""" 99 | temp = '' 100 | while text and text[-1] == '\n': 101 | temp += text[-1] 102 | text = text[:-1] 103 | lines = text.split('\n') 104 | return '\n'.join(map(lambda s: indention + s, lines)) + temp 105 | 106 | 107 | @contextlib.contextmanager 108 | def ignored(*exceptions): 109 | """ 110 | Context manager that ignores all of the specified exceptions. This will 111 | be in the standard library starting with Python 3.4. 112 | """ 113 | try: 114 | yield 115 | except exceptions: 116 | pass 117 | 118 | 119 | def source_to_unicode(source, encoding=None): 120 | def detect_encoding(): 121 | """ 122 | For the implementation of encoding definitions in Python, look at: 123 | - http://www.python.org/dev/peps/pep-0263/ 124 | - http://docs.python.org/2/reference/lexical_analysis.html#encoding-declarations 125 | """ 126 | byte_mark = literal_eval(r"b'\xef\xbb\xbf'") 127 | if source.startswith(byte_mark): 128 | # UTF-8 byte-order mark 129 | return 'utf-8' 130 | 131 | first_two_lines = re.match(br'(?:[^\n]*\n){0,2}', source).group(0) 132 | possible_encoding = re.search(br"coding[=:]\s*([-\w.]+)", 133 | first_two_lines) 134 | if possible_encoding: 135 | return possible_encoding.group(1) 136 | else: 137 | # the default if nothing else has been set -> PEP 263 138 | return encoding if encoding is not None else 'utf-8' 139 | 140 | if isinstance(source, unicode): 141 | # only cast str/bytes 142 | return source 143 | 144 | encoding = detect_encoding() 145 | if not isinstance(encoding, unicode): 146 | encoding = unicode(encoding, 'utf-8', 'replace') 147 | # cast to unicode by default 148 | return unicode(source, encoding, 'replace') 149 | 150 | 151 | def splitlines(string): 152 | """ 153 | A splitlines for Python code. In contrast to Python's ``str.splitlines``, 154 | looks at form feeds and other special characters as normal text. Just 155 | splits ``\n`` and ``\r\n``. 156 | Also different: Returns ``['']`` for an empty string input. 157 | """ 158 | return re.split('\n|\r\n', string) 159 | 160 | 161 | def unite(iterable): 162 | """Turns a two dimensional array into a one dimensional.""" 163 | return set(chain.from_iterable(iterable)) 164 | 165 | 166 | def to_list(func): 167 | def wrapper(*args, **kwargs): 168 | return list(func(*args, **kwargs)) 169 | return wrapper 170 | -------------------------------------------------------------------------------- /lib/jedi/debug.py: -------------------------------------------------------------------------------- 1 | from jedi._compatibility import encoding, is_py3, u 2 | import inspect 3 | import os 4 | import time 5 | 6 | def _lazy_colorama_init(): 7 | """ 8 | Lazily init colorama if necessary, not to screw up stdout is debug not 9 | enabled. 10 | 11 | This version of the function does nothing. 12 | """ 13 | pass 14 | 15 | _inited=False 16 | 17 | try: 18 | if os.name == 'nt': 19 | # Does not work on Windows, as pyreadline and colorama interfere 20 | raise ImportError 21 | else: 22 | # Use colorama for nicer console output. 23 | from colorama import Fore, init 24 | from colorama import initialise 25 | def _lazy_colorama_init(): 26 | """ 27 | Lazily init colorama if necessary, not to screw up stdout is 28 | debug not enabled. 29 | 30 | This version of the function does init colorama. 31 | """ 32 | global _inited 33 | if not _inited: 34 | # pytest resets the stream at the end - causes troubles. Since 35 | # after every output the stream is reset automatically we don't 36 | # need this. 37 | initialise.atexit_done = True 38 | try: 39 | init() 40 | except Exception: 41 | # Colorama fails with initializing under vim and is buggy in 42 | # version 0.3.6. 43 | pass 44 | _inited = True 45 | 46 | except ImportError: 47 | class Fore(object): 48 | RED = '' 49 | GREEN = '' 50 | YELLOW = '' 51 | MAGENTA = '' 52 | RESET = '' 53 | 54 | NOTICE = object() 55 | WARNING = object() 56 | SPEED = object() 57 | 58 | enable_speed = False 59 | enable_warning = False 60 | enable_notice = False 61 | 62 | # callback, interface: level, str 63 | debug_function = None 64 | ignored_modules = ['jedi.parser'] 65 | _debug_indent = 0 66 | _start_time = time.time() 67 | 68 | 69 | def reset_time(): 70 | global _start_time, _debug_indent 71 | _start_time = time.time() 72 | _debug_indent = 0 73 | 74 | 75 | def increase_indent(func): 76 | """Decorator for makin """ 77 | def wrapper(*args, **kwargs): 78 | global _debug_indent 79 | _debug_indent += 1 80 | try: 81 | return func(*args, **kwargs) 82 | finally: 83 | _debug_indent -= 1 84 | return wrapper 85 | 86 | 87 | def dbg(message, *args, **kwargs): 88 | """ Looks at the stack, to see if a debug message should be printed. """ 89 | # Python 2 compatibility, because it doesn't understand default args 90 | color = kwargs.pop('color', 'GREEN') 91 | assert color 92 | 93 | if debug_function and enable_notice: 94 | frm = inspect.stack()[1] 95 | mod = inspect.getmodule(frm[0]) 96 | if not (mod.__name__ in ignored_modules): 97 | i = ' ' * _debug_indent 98 | _lazy_colorama_init() 99 | debug_function(color, i + 'dbg: ' + message % tuple(u(repr(a)) for a in args)) 100 | 101 | 102 | def warning(message, *args, **kwargs): 103 | format = kwargs.pop('format', True) 104 | assert not kwargs 105 | 106 | if debug_function and enable_warning: 107 | i = ' ' * _debug_indent 108 | if format: 109 | message = message % tuple(u(repr(a)) for a in args) 110 | debug_function('RED', i + 'warning: ' + message) 111 | 112 | 113 | def speed(name): 114 | if debug_function and enable_speed: 115 | now = time.time() 116 | i = ' ' * _debug_indent 117 | debug_function('YELLOW', i + 'speed: ' + '%s %s' % (name, now - _start_time)) 118 | 119 | 120 | def print_to_stdout(color, str_out): 121 | """ 122 | The default debug function that prints to standard out. 123 | 124 | :param str color: A string that is an attribute of ``colorama.Fore``. 125 | """ 126 | col = getattr(Fore, color) 127 | _lazy_colorama_init() 128 | if not is_py3: 129 | str_out = str_out.encode(encoding, 'replace') 130 | print(col + str_out + Fore.RESET) 131 | 132 | 133 | # debug_function = print_to_stdout 134 | -------------------------------------------------------------------------------- /lib/jedi/evaluate/analysis.py: -------------------------------------------------------------------------------- 1 | """ 2 | Module for statical analysis. 3 | """ 4 | from jedi import debug 5 | from jedi.parser import tree 6 | from jedi.evaluate.compiled import CompiledObject 7 | 8 | from jedi.common import unite 9 | 10 | 11 | CODES = { 12 | 'attribute-error': (1, AttributeError, 'Potential AttributeError.'), 13 | 'name-error': (2, NameError, 'Potential NameError.'), 14 | 'import-error': (3, ImportError, 'Potential ImportError.'), 15 | 'type-error-too-many-arguments': (4, TypeError, None), 16 | 'type-error-too-few-arguments': (5, TypeError, None), 17 | 'type-error-keyword-argument': (6, TypeError, None), 18 | 'type-error-multiple-values': (7, TypeError, None), 19 | 'type-error-star-star': (8, TypeError, None), 20 | 'type-error-star': (9, TypeError, None), 21 | 'type-error-operation': (10, TypeError, None), 22 | 'type-error-not-iterable': (11, TypeError, None), 23 | 'type-error-isinstance': (12, TypeError, None), 24 | 'type-error-not-subscriptable': (13, TypeError, None), 25 | 'value-error-too-many-values': (14, ValueError, None), 26 | 'value-error-too-few-values': (15, ValueError, None), 27 | } 28 | 29 | 30 | class Error(object): 31 | def __init__(self, name, module_path, start_pos, message=None): 32 | self.path = module_path 33 | self._start_pos = start_pos 34 | self.name = name 35 | if message is None: 36 | message = CODES[self.name][2] 37 | self.message = message 38 | 39 | @property 40 | def line(self): 41 | return self._start_pos[0] 42 | 43 | @property 44 | def column(self): 45 | return self._start_pos[1] 46 | 47 | @property 48 | def code(self): 49 | # The class name start 50 | first = self.__class__.__name__[0] 51 | return first + str(CODES[self.name][0]) 52 | 53 | def __unicode__(self): 54 | return '%s:%s:%s: %s %s' % (self.path, self.line, self.column, 55 | self.code, self.message) 56 | 57 | def __str__(self): 58 | return self.__unicode__() 59 | 60 | def __eq__(self, other): 61 | return (self.path == other.path and self.name == other.name 62 | and self._start_pos == other._start_pos) 63 | 64 | def __ne__(self, other): 65 | return not self.__eq__(other) 66 | 67 | def __hash__(self): 68 | return hash((self.path, self._start_pos, self.name)) 69 | 70 | def __repr__(self): 71 | return '<%s %s: %s@%s,%s>' % (self.__class__.__name__, 72 | self.name, self.path, 73 | self._start_pos[0], self._start_pos[1]) 74 | 75 | 76 | class Warning(Error): 77 | pass 78 | 79 | 80 | def add(evaluator, name, jedi_obj, message=None, typ=Error, payload=None): 81 | from jedi.evaluate.iterable import MergedNodes 82 | while isinstance(jedi_obj, MergedNodes): 83 | if len(jedi_obj) != 1: 84 | # TODO is this kosher? 85 | return 86 | jedi_obj = list(jedi_obj)[0] 87 | 88 | exception = CODES[name][1] 89 | if _check_for_exception_catch(evaluator, jedi_obj, exception, payload): 90 | return 91 | 92 | module_path = jedi_obj.get_parent_until().path 93 | instance = typ(name, module_path, jedi_obj.start_pos, message) 94 | debug.warning(str(instance), format=False) 95 | evaluator.analysis.append(instance) 96 | 97 | 98 | def _check_for_setattr(instance): 99 | """ 100 | Check if there's any setattr method inside an instance. If so, return True. 101 | """ 102 | module = instance.get_parent_until() 103 | try: 104 | stmts = module.used_names['setattr'] 105 | except KeyError: 106 | return False 107 | 108 | return any(instance.start_pos < stmt.start_pos < instance.end_pos 109 | for stmt in stmts) 110 | 111 | 112 | def add_attribute_error(evaluator, scope, name): 113 | message = ('AttributeError: %s has no attribute %s.' % (scope, name)) 114 | from jedi.evaluate.representation import Instance 115 | # Check for __getattr__/__getattribute__ existance and issue a warning 116 | # instead of an error, if that happens. 117 | if isinstance(scope, Instance): 118 | typ = Warning 119 | try: 120 | scope.get_subscope_by_name('__getattr__') 121 | except KeyError: 122 | try: 123 | scope.get_subscope_by_name('__getattribute__') 124 | except KeyError: 125 | if not _check_for_setattr(scope): 126 | typ = Error 127 | else: 128 | typ = Error 129 | 130 | payload = scope, name 131 | add(evaluator, 'attribute-error', name, message, typ, payload) 132 | 133 | 134 | def _check_for_exception_catch(evaluator, jedi_obj, exception, payload=None): 135 | """ 136 | Checks if a jedi object (e.g. `Statement`) sits inside a try/catch and 137 | doesn't count as an error (if equal to `exception`). 138 | Also checks `hasattr` for AttributeErrors and uses the `payload` to compare 139 | it. 140 | Returns True if the exception was catched. 141 | """ 142 | def check_match(cls, exception): 143 | try: 144 | return isinstance(cls, CompiledObject) and issubclass(exception, cls.obj) 145 | except TypeError: 146 | return False 147 | 148 | def check_try_for_except(obj, exception): 149 | # Only nodes in try 150 | iterator = iter(obj.children) 151 | for branch_type in iterator: 152 | colon = next(iterator) 153 | suite = next(iterator) 154 | if branch_type == 'try' \ 155 | and not (branch_type.start_pos < jedi_obj.start_pos <= suite.end_pos): 156 | return False 157 | 158 | for node in obj.except_clauses(): 159 | if node is None: 160 | return True # An exception block that catches everything. 161 | else: 162 | except_classes = evaluator.eval_element(node) 163 | for cls in except_classes: 164 | from jedi.evaluate import iterable 165 | if isinstance(cls, iterable.Array) and cls.type == 'tuple': 166 | # multiple exceptions 167 | for typ in unite(cls.py__iter__()): 168 | if check_match(typ, exception): 169 | return True 170 | else: 171 | if check_match(cls, exception): 172 | return True 173 | 174 | def check_hasattr(node, suite): 175 | try: 176 | assert suite.start_pos <= jedi_obj.start_pos < suite.end_pos 177 | assert node.type in ('power', 'atom_expr') 178 | base = node.children[0] 179 | assert base.type == 'name' and base.value == 'hasattr' 180 | trailer = node.children[1] 181 | assert trailer.type == 'trailer' 182 | arglist = trailer.children[1] 183 | assert arglist.type == 'arglist' 184 | from jedi.evaluate.param import Arguments 185 | args = list(Arguments(evaluator, arglist).unpack()) 186 | # Arguments should be very simple 187 | assert len(args) == 2 188 | 189 | # Check name 190 | key, values = args[1] 191 | assert len(values) == 1 192 | names = list(evaluator.eval_element(values[0])) 193 | assert len(names) == 1 and isinstance(names[0], CompiledObject) 194 | assert names[0].obj == str(payload[1]) 195 | 196 | # Check objects 197 | key, values = args[0] 198 | assert len(values) == 1 199 | objects = evaluator.eval_element(values[0]) 200 | return payload[0] in objects 201 | except AssertionError: 202 | return False 203 | 204 | obj = jedi_obj 205 | while obj is not None and not obj.isinstance(tree.Function, tree.Class): 206 | if obj.isinstance(tree.Flow): 207 | # try/except catch check 208 | if obj.isinstance(tree.TryStmt) and check_try_for_except(obj, exception): 209 | return True 210 | # hasattr check 211 | if exception == AttributeError and obj.isinstance(tree.IfStmt, tree.WhileStmt): 212 | if check_hasattr(obj.children[1], obj.children[3]): 213 | return True 214 | obj = obj.parent 215 | 216 | return False 217 | -------------------------------------------------------------------------------- /lib/jedi/evaluate/cache.py: -------------------------------------------------------------------------------- 1 | """ 2 | - the popular ``memoize_default`` works like a typical memoize and returns the 3 | default otherwise. 4 | - ``CachedMetaClass`` uses ``memoize_default`` to do the same with classes. 5 | """ 6 | 7 | import inspect 8 | 9 | NO_DEFAULT = object() 10 | 11 | 12 | def memoize_default(default=NO_DEFAULT, evaluator_is_first_arg=False, second_arg_is_evaluator=False): 13 | """ This is a typical memoization decorator, BUT there is one difference: 14 | To prevent recursion it sets defaults. 15 | 16 | Preventing recursion is in this case the much bigger use than speed. I 17 | don't think, that there is a big speed difference, but there are many cases 18 | where recursion could happen (think about a = b; b = a). 19 | """ 20 | def func(function): 21 | def wrapper(obj, *args, **kwargs): 22 | if evaluator_is_first_arg: 23 | cache = obj.memoize_cache 24 | elif second_arg_is_evaluator: # needed for meta classes 25 | cache = args[0].memoize_cache 26 | else: 27 | cache = obj._evaluator.memoize_cache 28 | 29 | try: 30 | memo = cache[function] 31 | except KeyError: 32 | memo = {} 33 | cache[function] = memo 34 | 35 | key = (obj, args, frozenset(kwargs.items())) 36 | if key in memo: 37 | return memo[key] 38 | else: 39 | if default is not NO_DEFAULT: 40 | memo[key] = default 41 | rv = function(obj, *args, **kwargs) 42 | if inspect.isgenerator(rv): 43 | rv = list(rv) 44 | memo[key] = rv 45 | return rv 46 | return wrapper 47 | return func 48 | 49 | 50 | class CachedMetaClass(type): 51 | """ 52 | This is basically almost the same than the decorator above, it just caches 53 | class initializations. Either you do it this way or with decorators, but 54 | with decorators you lose class access (isinstance, etc). 55 | """ 56 | @memoize_default(None, second_arg_is_evaluator=True) 57 | def __call__(self, *args, **kwargs): 58 | return super(CachedMetaClass, self).__call__(*args, **kwargs) 59 | -------------------------------------------------------------------------------- /lib/jedi/evaluate/compiled/fake.py: -------------------------------------------------------------------------------- 1 | """ 2 | Loads functions that are mixed in to the standard library. E.g. builtins are 3 | written in C (binaries), but my autocompletion only understands Python code. By 4 | mixing in Python code, the autocompletion should work much better for builtins. 5 | """ 6 | 7 | import os 8 | import inspect 9 | import types 10 | 11 | from jedi._compatibility import is_py3, builtins, unicode, is_py34 12 | from jedi.parser import ParserWithRecovery, load_grammar 13 | from jedi.parser import tree as pt 14 | from jedi.evaluate.helpers import FakeName 15 | 16 | modules = {} 17 | 18 | 19 | MethodDescriptorType = type(str.replace) 20 | # These are not considered classes and access is granted even though they have 21 | # a __class__ attribute. 22 | NOT_CLASS_TYPES = ( 23 | types.BuiltinFunctionType, 24 | types.CodeType, 25 | types.FrameType, 26 | types.FunctionType, 27 | types.GeneratorType, 28 | types.GetSetDescriptorType, 29 | types.LambdaType, 30 | types.MemberDescriptorType, 31 | types.MethodType, 32 | types.ModuleType, 33 | types.TracebackType, 34 | MethodDescriptorType 35 | ) 36 | 37 | if is_py3: 38 | NOT_CLASS_TYPES += ( 39 | types.MappingProxyType, 40 | types.SimpleNamespace 41 | ) 42 | if is_py34: 43 | NOT_CLASS_TYPES += (types.DynamicClassAttribute,) 44 | 45 | 46 | class FakeDoesNotExist(Exception): 47 | pass 48 | 49 | 50 | def _load_faked_module(module): 51 | module_name = module.__name__ 52 | if module_name == '__builtin__' and not is_py3: 53 | module_name = 'builtins' 54 | 55 | try: 56 | return modules[module_name] 57 | except KeyError: 58 | path = os.path.dirname(os.path.abspath(__file__)) 59 | try: 60 | with open(os.path.join(path, 'fake', module_name) + '.pym') as f: 61 | source = f.read() 62 | except IOError: 63 | modules[module_name] = None 64 | return 65 | grammar = load_grammar(version='3.4') 66 | module = ParserWithRecovery(grammar, unicode(source), module_name).module 67 | modules[module_name] = module 68 | 69 | if module_name == 'builtins' and not is_py3: 70 | # There are two implementations of `open` for either python 2/3. 71 | # -> Rename the python2 version (`look at fake/builtins.pym`). 72 | open_func = search_scope(module, 'open') 73 | open_func.children[1] = FakeName('open_python3') 74 | open_func = search_scope(module, 'open_python2') 75 | open_func.children[1] = FakeName('open') 76 | return module 77 | 78 | 79 | def search_scope(scope, obj_name): 80 | for s in scope.subscopes: 81 | if str(s.name) == obj_name: 82 | return s 83 | 84 | 85 | def get_module(obj): 86 | if inspect.ismodule(obj): 87 | return obj 88 | try: 89 | obj = obj.__objclass__ 90 | except AttributeError: 91 | pass 92 | 93 | try: 94 | imp_plz = obj.__module__ 95 | except AttributeError: 96 | # Unfortunately in some cases like `int` there's no __module__ 97 | return builtins 98 | else: 99 | if imp_plz is None: 100 | # Happens for example in `(_ for _ in []).send.__module__`. 101 | return builtins 102 | else: 103 | try: 104 | return __import__(imp_plz) 105 | except ImportError: 106 | # __module__ can be something arbitrary that doesn't exist. 107 | return builtins 108 | 109 | 110 | def _faked(module, obj, name): 111 | # Crazy underscore actions to try to escape all the internal madness. 112 | if module is None: 113 | module = get_module(obj) 114 | 115 | faked_mod = _load_faked_module(module) 116 | if faked_mod is None: 117 | return None 118 | 119 | # Having the module as a `parser.representation.module`, we need to scan 120 | # for methods. 121 | if name is None: 122 | if inspect.isbuiltin(obj): 123 | return search_scope(faked_mod, obj.__name__) 124 | elif not inspect.isclass(obj): 125 | # object is a method or descriptor 126 | try: 127 | objclass = obj.__objclass__ 128 | except AttributeError: 129 | return None 130 | else: 131 | cls = search_scope(faked_mod, objclass.__name__) 132 | if cls is None: 133 | return None 134 | return search_scope(cls, obj.__name__) 135 | else: 136 | if obj == module: 137 | return search_scope(faked_mod, name) 138 | else: 139 | try: 140 | cls_name = obj.__name__ 141 | except AttributeError: 142 | return None 143 | cls = search_scope(faked_mod, cls_name) 144 | if cls is None: 145 | return None 146 | return search_scope(cls, name) 147 | 148 | 149 | def memoize_faked(obj): 150 | """ 151 | A typical memoize function that ignores issues with non hashable results. 152 | """ 153 | cache = obj.cache = {} 154 | 155 | def memoizer(*args, **kwargs): 156 | key = (obj, args, frozenset(kwargs.items())) 157 | try: 158 | result = cache[key] 159 | except TypeError: 160 | return obj(*args, **kwargs) 161 | except KeyError: 162 | result = obj(*args, **kwargs) 163 | if result is not None: 164 | cache[key] = obj(*args, **kwargs) 165 | return result 166 | else: 167 | return result 168 | return memoizer 169 | 170 | 171 | @memoize_faked 172 | def _get_faked(module, obj, name=None): 173 | obj = type(obj) if is_class_instance(obj) else obj 174 | result = _faked(module, obj, name) 175 | if result is None or isinstance(result, pt.Class): 176 | # We're not interested in classes. What we want is functions. 177 | raise FakeDoesNotExist 178 | else: 179 | # Set the docstr which was previously not set (faked modules don't 180 | # contain it). 181 | doc = '"""%s"""' % obj.__doc__ # TODO need escapes. 182 | suite = result.children[-1] 183 | string = pt.String(pt.zero_position_modifier, doc, (0, 0), '') 184 | new_line = pt.Newline('\n', (0, 0), '') 185 | docstr_node = pt.Node('simple_stmt', [string, new_line]) 186 | suite.children.insert(2, docstr_node) 187 | return result 188 | 189 | 190 | def get_faked(module, obj, name=None, parent=None): 191 | faked = _get_faked(module, obj, name) 192 | faked.parent = parent 193 | return faked 194 | 195 | 196 | def is_class_instance(obj): 197 | """Like inspect.* methods.""" 198 | try: 199 | cls = obj.__class__ 200 | except AttributeError: 201 | return False 202 | else: 203 | return cls != type and not issubclass(cls, NOT_CLASS_TYPES) 204 | -------------------------------------------------------------------------------- /lib/jedi/evaluate/compiled/fake/_functools.pym: -------------------------------------------------------------------------------- 1 | class partial(): 2 | def __init__(self, func, *args, **keywords): 3 | self.__func = func 4 | self.__args = args 5 | self.__keywords = keywords 6 | 7 | def __call__(self, *args, **kwargs): 8 | # TODO should be **dict(self.__keywords, **kwargs) 9 | return self.__func(*(self.__args + args), **self.__keywords) 10 | -------------------------------------------------------------------------------- /lib/jedi/evaluate/compiled/fake/_sqlite3.pym: -------------------------------------------------------------------------------- 1 | def connect(database, timeout=None, isolation_level=None, detect_types=None, factory=None): 2 | return Connection() 3 | 4 | 5 | class Connection(): 6 | def cursor(self): 7 | return Cursor() 8 | 9 | 10 | class Cursor(): 11 | def cursor(self): 12 | return Cursor() 13 | 14 | def fetchone(self): 15 | return Row() 16 | 17 | def fetchmany(self, size=cursor.arraysize): 18 | return [self.fetchone()] 19 | 20 | def fetchall(self): 21 | return [self.fetchone()] 22 | 23 | 24 | class Row(): 25 | def keys(self): 26 | return [''] 27 | -------------------------------------------------------------------------------- /lib/jedi/evaluate/compiled/fake/_sre.pym: -------------------------------------------------------------------------------- 1 | def compile(): 2 | class SRE_Match(): 3 | endpos = int() 4 | lastgroup = int() 5 | lastindex = int() 6 | pos = int() 7 | string = str() 8 | regs = ((int(), int()),) 9 | 10 | def __init__(self, pattern): 11 | self.re = pattern 12 | 13 | def start(self): 14 | return int() 15 | 16 | def end(self): 17 | return int() 18 | 19 | def span(self): 20 | return int(), int() 21 | 22 | def expand(self): 23 | return str() 24 | 25 | def group(self, nr): 26 | return str() 27 | 28 | def groupdict(self): 29 | return {str(): str()} 30 | 31 | def groups(self): 32 | return (str(),) 33 | 34 | class SRE_Pattern(): 35 | flags = int() 36 | groupindex = {} 37 | groups = int() 38 | pattern = str() 39 | 40 | def findall(self, string, pos=None, endpos=None): 41 | """ 42 | findall(string[, pos[, endpos]]) --> list. 43 | Return a list of all non-overlapping matches of pattern in string. 44 | """ 45 | return [str()] 46 | 47 | def finditer(self, string, pos=None, endpos=None): 48 | """ 49 | finditer(string[, pos[, endpos]]) --> iterator. 50 | Return an iterator over all non-overlapping matches for the 51 | RE pattern in string. For each match, the iterator returns a 52 | match object. 53 | """ 54 | yield SRE_Match(self) 55 | 56 | def match(self, string, pos=None, endpos=None): 57 | """ 58 | match(string[, pos[, endpos]]) --> match object or None. 59 | Matches zero or more characters at the beginning of the string 60 | pattern 61 | """ 62 | return SRE_Match(self) 63 | 64 | def scanner(self, string, pos=None, endpos=None): 65 | pass 66 | 67 | def search(self, string, pos=None, endpos=None): 68 | """ 69 | search(string[, pos[, endpos]]) --> match object or None. 70 | Scan through string looking for a match, and return a corresponding 71 | MatchObject instance. Return None if no position in the string matches. 72 | """ 73 | return SRE_Match(self) 74 | 75 | def split(self, string, maxsplit=0]): 76 | """ 77 | split(string[, maxsplit = 0]) --> list. 78 | Split string by the occurrences of pattern. 79 | """ 80 | return [str()] 81 | 82 | def sub(self, repl, string, count=0): 83 | """ 84 | sub(repl, string[, count = 0]) --> newstring 85 | Return the string obtained by replacing the leftmost non-overlapping 86 | occurrences of pattern in string by the replacement repl. 87 | """ 88 | return str() 89 | 90 | def subn(self, repl, string, count=0): 91 | """ 92 | subn(repl, string[, count = 0]) --> (newstring, number of subs) 93 | Return the tuple (new_string, number_of_subs_made) found by replacing 94 | the leftmost non-overlapping occurrences of pattern with the 95 | replacement repl. 96 | """ 97 | return (str(), int()) 98 | 99 | return SRE_Pattern() 100 | -------------------------------------------------------------------------------- /lib/jedi/evaluate/compiled/fake/_weakref.pym: -------------------------------------------------------------------------------- 1 | def proxy(object, callback=None): 2 | return object 3 | 4 | class weakref(): 5 | def __init__(self, object, callback=None): 6 | self.__object = object 7 | def __call__(self): 8 | return self.__object 9 | -------------------------------------------------------------------------------- /lib/jedi/evaluate/compiled/fake/builtins.pym: -------------------------------------------------------------------------------- 1 | """ 2 | Pure Python implementation of some builtins. 3 | This code is not going to be executed anywhere. 4 | These implementations are not always correct, but should work as good as 5 | possible for the auto completion. 6 | """ 7 | 8 | 9 | def next(iterator, default=None): 10 | if random.choice([0, 1]): 11 | if hasattr("next"): 12 | return iterator.next() 13 | else: 14 | return iterator.__next__() 15 | else: 16 | if default is not None: 17 | return default 18 | 19 | 20 | def iter(collection, sentinel=None): 21 | if sentinel: 22 | yield collection() 23 | else: 24 | for c in collection: 25 | yield c 26 | 27 | 28 | def range(start, stop=None, step=1): 29 | return [0] 30 | 31 | 32 | class file(): 33 | def __iter__(self): 34 | yield '' 35 | def next(self): 36 | return '' 37 | 38 | 39 | class xrange(): 40 | # Attention: this function doesn't exist in Py3k (there it is range). 41 | def __iter__(self): 42 | yield 1 43 | 44 | def count(self): 45 | return 1 46 | 47 | def index(self): 48 | return 1 49 | 50 | 51 | def open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True): 52 | import io 53 | return io.TextIOWrapper(file, mode, buffering, encoding, errors, newline, closefd) 54 | 55 | 56 | def open_python2(name, mode=None, buffering=None): 57 | return file(name, mode, buffering) 58 | 59 | 60 | #-------------------------------------------------------- 61 | # descriptors 62 | #-------------------------------------------------------- 63 | class property(): 64 | def __init__(self, fget, fset=None, fdel=None, doc=None): 65 | self.fget = fget 66 | self.fset = fset 67 | self.fdel = fdel 68 | self.__doc__ = doc 69 | 70 | def __get__(self, obj, cls): 71 | return self.fget(obj) 72 | 73 | def __set__(self, obj, value): 74 | self.fset(obj, value) 75 | 76 | def __delete__(self, obj): 77 | self.fdel(obj) 78 | 79 | def setter(self, func): 80 | self.fset = func 81 | return self 82 | 83 | def getter(self, func): 84 | self.fget = func 85 | return self 86 | 87 | def deleter(self, func): 88 | self.fdel = func 89 | return self 90 | 91 | 92 | class staticmethod(): 93 | def __init__(self, func): 94 | self.__func = func 95 | 96 | def __get__(self, obj, cls): 97 | return self.__func 98 | 99 | 100 | class classmethod(): 101 | def __init__(self, func): 102 | self.__func = func 103 | 104 | def __get__(self, obj, cls): 105 | def _method(*args, **kwargs): 106 | return self.__func(cls, *args, **kwargs) 107 | return _method 108 | 109 | 110 | #-------------------------------------------------------- 111 | # array stuff 112 | #-------------------------------------------------------- 113 | class list(): 114 | def __init__(self, iterable=[]): 115 | self.__iterable = [] 116 | for i in iterable: 117 | self.__iterable += [i] 118 | 119 | def __iter__(self): 120 | for i in self.__iterable: 121 | yield i 122 | 123 | def __getitem__(self, y): 124 | return self.__iterable[y] 125 | 126 | def pop(self): 127 | return self.__iterable[int()] 128 | 129 | 130 | class tuple(): 131 | def __init__(self, iterable=[]): 132 | self.__iterable = [] 133 | for i in iterable: 134 | self.__iterable += [i] 135 | 136 | def __iter__(self): 137 | for i in self.__iterable: 138 | yield i 139 | 140 | def __getitem__(self, y): 141 | return self.__iterable[y] 142 | 143 | def index(self): 144 | return 1 145 | 146 | def count(self): 147 | return 1 148 | 149 | 150 | class set(): 151 | def __init__(self, iterable=[]): 152 | self.__iterable = iterable 153 | 154 | def __iter__(self): 155 | for i in self.__iterable: 156 | yield i 157 | 158 | def pop(self): 159 | return list(self.__iterable)[-1] 160 | 161 | def copy(self): 162 | return self 163 | 164 | def difference(self, other): 165 | return self - other 166 | 167 | def intersection(self, other): 168 | return self & other 169 | 170 | def symmetric_difference(self, other): 171 | return self ^ other 172 | 173 | def union(self, other): 174 | return self | other 175 | 176 | 177 | class frozenset(): 178 | def __init__(self, iterable=[]): 179 | self.__iterable = iterable 180 | 181 | def __iter__(self): 182 | for i in self.__iterable: 183 | yield i 184 | 185 | def copy(self): 186 | return self 187 | 188 | 189 | class dict(): 190 | def __init__(self, **elements): 191 | self.__elements = elements 192 | 193 | def clear(self): 194 | # has a strange docstr 195 | pass 196 | 197 | def get(self, k, d=None): 198 | # TODO implement 199 | try: 200 | #return self.__elements[k] 201 | pass 202 | except KeyError: 203 | return d 204 | 205 | def values(self): 206 | return self.__elements.values() 207 | 208 | def setdefault(self, k, d): 209 | # TODO maybe also return the content 210 | return d 211 | 212 | 213 | class enumerate(): 214 | def __init__(self, sequence, start=0): 215 | self.__sequence = sequence 216 | 217 | def __iter__(self): 218 | for i in self.__sequence: 219 | yield 1, i 220 | 221 | def __next__(self): 222 | return next(self.__iter__()) 223 | 224 | def next(self): 225 | return next(self.__iter__()) 226 | 227 | 228 | class reversed(): 229 | def __init__(self, sequence): 230 | self.__sequence = sequence 231 | 232 | def __iter__(self): 233 | for i in self.__sequence: 234 | yield i 235 | 236 | def __next__(self): 237 | return next(self.__iter__()) 238 | 239 | def next(self): 240 | return next(self.__iter__()) 241 | 242 | 243 | def sorted(iterable, cmp=None, key=None, reverse=False): 244 | return iterable 245 | 246 | 247 | #-------------------------------------------------------- 248 | # basic types 249 | #-------------------------------------------------------- 250 | class int(): 251 | def __init__(self, x, base=None): 252 | pass 253 | 254 | 255 | class str(): 256 | def __init__(self, obj): 257 | pass 258 | 259 | 260 | class type(): 261 | def mro(): 262 | return [object] 263 | -------------------------------------------------------------------------------- /lib/jedi/evaluate/compiled/fake/datetime.pym: -------------------------------------------------------------------------------- 1 | class datetime(): 2 | @staticmethod 3 | def now(): 4 | return datetime() 5 | -------------------------------------------------------------------------------- /lib/jedi/evaluate/compiled/fake/io.pym: -------------------------------------------------------------------------------- 1 | class TextIOWrapper(): 2 | def __next__(self): 3 | return str() 4 | 5 | def __iter__(self): 6 | yield str() 7 | -------------------------------------------------------------------------------- /lib/jedi/evaluate/compiled/fake/posix.pym: -------------------------------------------------------------------------------- 1 | def getcwd(): 2 | return '' 3 | 4 | def getcwdu(): 5 | return '' 6 | -------------------------------------------------------------------------------- /lib/jedi/evaluate/compiled/mixed.py: -------------------------------------------------------------------------------- 1 | """ 2 | Used only for REPL Completion. 3 | """ 4 | 5 | import inspect 6 | import os 7 | 8 | from jedi import common 9 | from jedi.parser.fast import FastParser 10 | from jedi.evaluate import compiled 11 | from jedi.cache import underscore_memoization 12 | 13 | 14 | class MixedObject(object): 15 | """ 16 | A ``MixedObject`` is used in two ways: 17 | 18 | 1. It uses the default logic of ``parser.tree`` objects, 19 | 2. except for getattr calls. The names dicts are generated in a fashion 20 | like ``CompiledObject``. 21 | 22 | This combined logic makes it possible to provide more powerful REPL 23 | completion. It allows side effects that are not noticable with the default 24 | parser structure to still be completeable. 25 | 26 | The biggest difference from CompiledObject to MixedObject is that we are 27 | generally dealing with Python code and not with C code. This will generate 28 | fewer special cases, because we in Python you don't have the same freedoms 29 | to modify the runtime. 30 | """ 31 | def __init__(self, evaluator, obj, node_name): 32 | self._evaluator = evaluator 33 | self.obj = obj 34 | self.node_name = node_name 35 | self.definition = node_name.get_definition() 36 | 37 | @property 38 | def names_dict(self): 39 | return LazyMixedNamesDict(self._evaluator, self) 40 | 41 | def names_dicts(self, search_global): 42 | # TODO is this needed? 43 | assert search_global is False 44 | return [self.names_dict] 45 | 46 | def api_type(self): 47 | mappings = { 48 | 'expr_stmt': 'statement', 49 | 'classdef': 'class', 50 | 'funcdef': 'function', 51 | 'file_input': 'module', 52 | } 53 | return mappings[self.definition.type] 54 | 55 | def __repr__(self): 56 | return '<%s: %s>' % (type(self).__name__, repr(self.obj)) 57 | 58 | def __getattr__(self, name): 59 | return getattr(self.definition, name) 60 | 61 | 62 | class MixedName(compiled.CompiledName): 63 | """ 64 | The ``CompiledName._compiled_object`` is our MixedObject. 65 | """ 66 | @property 67 | @underscore_memoization 68 | def parent(self): 69 | return create(self._evaluator, getattr(self._compiled_obj.obj, self.name)) 70 | 71 | @parent.setter 72 | def parent(self, value): 73 | pass # Just ignore this, Name tries to overwrite the parent attribute. 74 | 75 | @property 76 | def start_pos(self): 77 | if isinstance(self.parent, MixedObject): 78 | return self.parent.node_name.start_pos 79 | 80 | # This means a start_pos that doesn't exist (compiled objects). 81 | return (0, 0) 82 | 83 | 84 | class LazyMixedNamesDict(compiled.LazyNamesDict): 85 | name_class = MixedName 86 | 87 | 88 | def parse(grammar, path): 89 | with open(path) as f: 90 | source = f.read() 91 | source = common.source_to_unicode(source) 92 | return FastParser(grammar, source, path) 93 | 94 | 95 | def _load_module(evaluator, path, python_object): 96 | module = parse(evaluator.grammar, path).module 97 | python_module = inspect.getmodule(python_object) 98 | 99 | evaluator.modules[python_module.__name__] = module 100 | return module 101 | 102 | 103 | def find_syntax_node_name(evaluator, python_object): 104 | try: 105 | path = inspect.getsourcefile(python_object) 106 | except TypeError: 107 | # The type might not be known (e.g. class_with_dict.__weakref__) 108 | return None 109 | if path is None or not os.path.exists(path): 110 | # The path might not exist or be e.g. . 111 | return None 112 | 113 | module = _load_module(evaluator, path, python_object) 114 | 115 | if inspect.ismodule(python_object): 116 | # We don't need to check names for modules, because there's not really 117 | # a way to write a module in a module in Python (and also __name__ can 118 | # be something like ``email.utils``). 119 | return module 120 | 121 | name_str = python_object.__name__ 122 | if name_str == '': 123 | return None # It's too hard to find lambdas. 124 | 125 | names = module.used_names[name_str] 126 | names = [n for n in names if n.is_definition()] 127 | 128 | try: 129 | code = python_object.__code__ 130 | # By using the line number of a code object we make the lookup in a 131 | # file pretty easy. There's still a possibility of people defining 132 | # stuff like ``a = 3; foo(a); a = 4`` on the same line, but if people 133 | # do so we just don't care. 134 | line_nr = code.co_firstlineno 135 | except AttributeError: 136 | pass 137 | else: 138 | line_names = [name for name in names if name.start_pos[0] == line_nr] 139 | # There's a chance that the object is not available anymore, because 140 | # the code has changed in the background. 141 | if line_names: 142 | return line_names[-1] 143 | 144 | # It's really hard to actually get the right definition, here as a last 145 | # resort we just return the last one. This chance might lead to odd 146 | # completions at some points but will lead to mostly correct type 147 | # inference, because people tend to define a public name in a module only 148 | # once. 149 | return names[-1] 150 | 151 | 152 | @compiled.compiled_objects_cache('mixed_cache') 153 | def create(evaluator, obj): 154 | name = find_syntax_node_name(evaluator, obj) 155 | if name is None: 156 | return compiled.create(evaluator, obj) 157 | else: 158 | return MixedObject(evaluator, obj, name) 159 | -------------------------------------------------------------------------------- /lib/jedi/evaluate/docstrings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Docstrings are another source of information for functions and classes. 3 | :mod:`jedi.evaluate.dynamic` tries to find all executions of functions, while 4 | the docstring parsing is much easier. There are two different types of 5 | docstrings that |jedi| understands: 6 | 7 | - `Sphinx `_ 8 | - `Epydoc `_ 9 | 10 | For example, the sphinx annotation ``:type foo: str`` clearly states that the 11 | type of ``foo`` is ``str``. 12 | 13 | As an addition to parameter searching, this module also provides return 14 | annotations. 15 | """ 16 | 17 | from ast import literal_eval 18 | import re 19 | from itertools import chain 20 | from textwrap import dedent 21 | 22 | from jedi.evaluate.cache import memoize_default 23 | from jedi.parser import ParserWithRecovery, load_grammar 24 | from jedi.parser.tree import Class 25 | from jedi.common import indent_block 26 | from jedi.evaluate.iterable import Array, FakeSequence, AlreadyEvaluated 27 | 28 | 29 | DOCSTRING_PARAM_PATTERNS = [ 30 | r'\s*:type\s+%s:\s*([^\n]+)', # Sphinx 31 | r'\s*:param\s+(\w+)\s+%s:[^\n]+', # Sphinx param with type 32 | r'\s*@type\s+%s:\s*([^\n]+)', # Epydoc 33 | ] 34 | 35 | DOCSTRING_RETURN_PATTERNS = [ 36 | re.compile(r'\s*:rtype:\s*([^\n]+)', re.M), # Sphinx 37 | re.compile(r'\s*@rtype:\s*([^\n]+)', re.M), # Epydoc 38 | ] 39 | 40 | REST_ROLE_PATTERN = re.compile(r':[^`]+:`([^`]+)`') 41 | 42 | 43 | try: 44 | from numpydoc.docscrape import NumpyDocString 45 | except ImportError: 46 | def _search_param_in_numpydocstr(docstr, param_str): 47 | return [] 48 | else: 49 | def _search_param_in_numpydocstr(docstr, param_str): 50 | """Search `docstr` (in numpydoc format) for type(-s) of `param_str`.""" 51 | params = NumpyDocString(docstr)._parsed_data['Parameters'] 52 | for p_name, p_type, p_descr in params: 53 | if p_name == param_str: 54 | m = re.match('([^,]+(,[^,]+)*?)(,[ ]*optional)?$', p_type) 55 | if m: 56 | p_type = m.group(1) 57 | 58 | if p_type.startswith('{'): 59 | types = set(type(x).__name__ for x in literal_eval(p_type)) 60 | return list(types) 61 | else: 62 | return [p_type] 63 | return [] 64 | 65 | 66 | def _search_param_in_docstr(docstr, param_str): 67 | """ 68 | Search `docstr` for type(-s) of `param_str`. 69 | 70 | >>> _search_param_in_docstr(':type param: int', 'param') 71 | ['int'] 72 | >>> _search_param_in_docstr('@type param: int', 'param') 73 | ['int'] 74 | >>> _search_param_in_docstr( 75 | ... ':type param: :class:`threading.Thread`', 'param') 76 | ['threading.Thread'] 77 | >>> bool(_search_param_in_docstr('no document', 'param')) 78 | False 79 | >>> _search_param_in_docstr(':param int param: some description', 'param') 80 | ['int'] 81 | 82 | """ 83 | # look at #40 to see definitions of those params 84 | patterns = [re.compile(p % re.escape(param_str)) 85 | for p in DOCSTRING_PARAM_PATTERNS] 86 | for pattern in patterns: 87 | match = pattern.search(docstr) 88 | if match: 89 | return [_strip_rst_role(match.group(1))] 90 | 91 | return (_search_param_in_numpydocstr(docstr, param_str) or 92 | []) 93 | 94 | 95 | def _strip_rst_role(type_str): 96 | """ 97 | Strip off the part looks like a ReST role in `type_str`. 98 | 99 | >>> _strip_rst_role(':class:`ClassName`') # strip off :class: 100 | 'ClassName' 101 | >>> _strip_rst_role(':py:obj:`module.Object`') # works with domain 102 | 'module.Object' 103 | >>> _strip_rst_role('ClassName') # do nothing when not ReST role 104 | 'ClassName' 105 | 106 | See also: 107 | http://sphinx-doc.org/domains.html#cross-referencing-python-objects 108 | 109 | """ 110 | match = REST_ROLE_PATTERN.match(type_str) 111 | if match: 112 | return match.group(1) 113 | else: 114 | return type_str 115 | 116 | 117 | def _evaluate_for_statement_string(evaluator, string, module): 118 | code = dedent(""" 119 | def pseudo_docstring_stuff(): 120 | # Create a pseudo function for docstring statements. 121 | %s 122 | """) 123 | if string is None: 124 | return [] 125 | 126 | for element in re.findall('((?:\w+\.)*\w+)\.', string): 127 | # Try to import module part in dotted name. 128 | # (e.g., 'threading' in 'threading.Thread'). 129 | string = 'import %s\n' % element + string 130 | 131 | # Take the default grammar here, if we load the Python 2.7 grammar here, it 132 | # will be impossible to use `...` (Ellipsis) as a token. Docstring types 133 | # don't need to conform with the current grammar. 134 | p = ParserWithRecovery(load_grammar(), code % indent_block(string)) 135 | try: 136 | pseudo_cls = p.module.subscopes[0] 137 | # First pick suite, then simple_stmt (-2 for DEDENT) and then the node, 138 | # which is also not the last item, because there's a newline. 139 | stmt = pseudo_cls.children[-1].children[-2].children[-2] 140 | except (AttributeError, IndexError): 141 | return [] 142 | 143 | # Use the module of the param. 144 | # TODO this module is not the module of the param in case of a function 145 | # call. In that case it's the module of the function call. 146 | # stuffed with content from a function call. 147 | pseudo_cls.parent = module 148 | return list(_execute_types_in_stmt(evaluator, stmt)) 149 | 150 | 151 | def _execute_types_in_stmt(evaluator, stmt): 152 | """ 153 | Executing all types or general elements that we find in a statement. This 154 | doesn't include tuple, list and dict literals, because the stuff they 155 | contain is executed. (Used as type information). 156 | """ 157 | definitions = evaluator.eval_element(stmt) 158 | return chain.from_iterable(_execute_array_values(evaluator, d) for d in definitions) 159 | 160 | 161 | def _execute_array_values(evaluator, array): 162 | """ 163 | Tuples indicate that there's not just one return value, but the listed 164 | ones. `(str, int)` means that it returns a tuple with both types. 165 | """ 166 | if isinstance(array, Array): 167 | values = [] 168 | for types in array.py__iter__(): 169 | objects = set(chain.from_iterable(_execute_array_values(evaluator, typ) for typ in types)) 170 | values.append(AlreadyEvaluated(objects)) 171 | return [FakeSequence(evaluator, values, array.type)] 172 | else: 173 | return evaluator.execute(array) 174 | 175 | 176 | @memoize_default(None, evaluator_is_first_arg=True) 177 | def follow_param(evaluator, param): 178 | def eval_docstring(docstring): 179 | return set( 180 | [p for param_str in _search_param_in_docstr(docstring, str(param.name)) 181 | for p in _evaluate_for_statement_string(evaluator, param_str, module)] 182 | ) 183 | func = param.parent_function 184 | module = param.get_parent_until() 185 | 186 | types = eval_docstring(func.raw_doc) 187 | if func.name.value == '__init__': 188 | cls = func.get_parent_until(Class) 189 | if cls.type == 'classdef': 190 | types |= eval_docstring(cls.raw_doc) 191 | 192 | return types 193 | 194 | 195 | @memoize_default(None, evaluator_is_first_arg=True) 196 | def find_return_types(evaluator, func): 197 | def search_return_in_docstr(code): 198 | for p in DOCSTRING_RETURN_PATTERNS: 199 | match = p.search(code) 200 | if match: 201 | return _strip_rst_role(match.group(1)) 202 | 203 | type_str = search_return_in_docstr(func.raw_doc) 204 | return _evaluate_for_statement_string(evaluator, type_str, func.get_parent_until()) 205 | -------------------------------------------------------------------------------- /lib/jedi/evaluate/dynamic.py: -------------------------------------------------------------------------------- 1 | """ 2 | One of the really important features of |jedi| is to have an option to 3 | understand code like this:: 4 | 5 | def foo(bar): 6 | bar. # completion here 7 | foo(1) 8 | 9 | There's no doubt wheter bar is an ``int`` or not, but if there's also a call 10 | like ``foo('str')``, what would happen? Well, we'll just show both. Because 11 | that's what a human would expect. 12 | 13 | It works as follows: 14 | 15 | - |Jedi| sees a param 16 | - search for function calls named ``foo`` 17 | - execute these calls and check the input. This work with a ``ParamListener``. 18 | """ 19 | from itertools import chain 20 | 21 | from jedi._compatibility import unicode 22 | from jedi.parser import tree 23 | from jedi import settings 24 | from jedi import debug 25 | from jedi.evaluate.cache import memoize_default 26 | from jedi.evaluate import imports 27 | 28 | 29 | MAX_PARAM_SEARCHES = 20 30 | 31 | 32 | class ParamListener(object): 33 | """ 34 | This listener is used to get the params for a function. 35 | """ 36 | def __init__(self): 37 | self.param_possibilities = [] 38 | 39 | def execute(self, params): 40 | self.param_possibilities += params 41 | 42 | 43 | @debug.increase_indent 44 | def search_params(evaluator, param): 45 | """ 46 | A dynamic search for param values. If you try to complete a type: 47 | 48 | >>> def func(foo): 49 | ... foo 50 | >>> func(1) 51 | >>> func("") 52 | 53 | It is not known what the type ``foo`` without analysing the whole code. You 54 | have to look for all calls to ``func`` to find out what ``foo`` possibly 55 | is. 56 | """ 57 | if not settings.dynamic_params: 58 | return set() 59 | 60 | evaluator.dynamic_params_depth += 1 61 | try: 62 | func = param.get_parent_until(tree.Function) 63 | debug.dbg('Dynamic param search for %s in %s.', param, str(func.name), color='MAGENTA') 64 | # Compare the param names. 65 | names = [n for n in search_function_call(evaluator, func) 66 | if n.value == param.name.value] 67 | # Evaluate the ExecutedParams to types. 68 | result = set(chain.from_iterable(n.parent.eval(evaluator) for n in names)) 69 | debug.dbg('Dynamic param result %s', result, color='MAGENTA') 70 | return result 71 | finally: 72 | evaluator.dynamic_params_depth -= 1 73 | 74 | 75 | @memoize_default([], evaluator_is_first_arg=True) 76 | def search_function_call(evaluator, func): 77 | """ 78 | Returns a list of param names. 79 | """ 80 | from jedi.evaluate import representation as er 81 | 82 | def get_possible_nodes(module, func_name): 83 | try: 84 | names = module.used_names[func_name] 85 | except KeyError: 86 | return 87 | 88 | for name in names: 89 | bracket = name.get_next_leaf() 90 | trailer = bracket.parent 91 | if trailer.type == 'trailer' and bracket == '(': 92 | yield name, trailer 93 | 94 | def undecorate(typ): 95 | # We have to remove decorators, because they are not the 96 | # "original" functions, this way we can easily compare. 97 | # At the same time we also have to remove InstanceElements. 98 | if typ.isinstance(er.Function, er.Instance) \ 99 | and typ.decorates is not None: 100 | return typ.decorates 101 | elif isinstance(typ, er.InstanceElement): 102 | return typ.var 103 | else: 104 | return typ 105 | 106 | current_module = func.get_parent_until() 107 | func_name = unicode(func.name) 108 | compare = func 109 | if func_name == '__init__': 110 | cls = func.get_parent_scope() 111 | if isinstance(cls, tree.Class): 112 | func_name = unicode(cls.name) 113 | compare = cls 114 | 115 | # add the listener 116 | listener = ParamListener() 117 | func.listeners.add(listener) 118 | 119 | try: 120 | result = [] 121 | i = 0 122 | for mod in imports.get_modules_containing_name(evaluator, [current_module], func_name): 123 | for name, trailer in get_possible_nodes(mod, func_name): 124 | i += 1 125 | 126 | # This is a simple way to stop Jedi's dynamic param recursion 127 | # from going wild: The deeper Jedi's in the recursin, the less 128 | # code should be evaluated. 129 | if i * evaluator.dynamic_params_depth > MAX_PARAM_SEARCHES: 130 | return listener.param_possibilities 131 | 132 | for typ in evaluator.goto_definitions(name): 133 | undecorated = undecorate(typ) 134 | if evaluator.wrap(compare) == undecorated: 135 | # Only if we have the correct function we execute 136 | # it, otherwise just ignore it. 137 | evaluator.eval_trailer([typ], trailer) 138 | 139 | result = listener.param_possibilities 140 | 141 | # If there are results after processing a module, we're probably 142 | # good to process. 143 | if result: 144 | return result 145 | finally: 146 | # cleanup: remove the listener; important: should not stick. 147 | func.listeners.remove(listener) 148 | 149 | return set() 150 | -------------------------------------------------------------------------------- /lib/jedi/evaluate/flow_analysis.py: -------------------------------------------------------------------------------- 1 | from jedi.parser import tree 2 | 3 | 4 | class Status(object): 5 | lookup_table = {} 6 | 7 | def __init__(self, value, name): 8 | self._value = value 9 | self._name = name 10 | Status.lookup_table[value] = self 11 | 12 | def invert(self): 13 | if self is REACHABLE: 14 | return UNREACHABLE 15 | elif self is UNREACHABLE: 16 | return REACHABLE 17 | else: 18 | return UNSURE 19 | 20 | def __and__(self, other): 21 | if UNSURE in (self, other): 22 | return UNSURE 23 | else: 24 | return REACHABLE if self._value and other._value else UNREACHABLE 25 | 26 | def __repr__(self): 27 | return '<%s: %s>' % (type(self).__name__, self._name) 28 | 29 | 30 | REACHABLE = Status(True, 'reachable') 31 | UNREACHABLE = Status(False, 'unreachable') 32 | UNSURE = Status(None, 'unsure') 33 | 34 | 35 | def break_check(evaluator, base_scope, stmt, origin_scope=None): 36 | element_scope = evaluator.wrap(stmt.get_parent_scope(include_flows=True)) 37 | # Direct parents get resolved, we filter scopes that are separate branches. 38 | # This makes sense for autocompletion and static analysis. For actual 39 | # Python it doesn't matter, because we're talking about potentially 40 | # unreachable code. 41 | # e.g. `if 0:` would cause all name lookup within the flow make 42 | # unaccessible. This is not a "problem" in Python, because the code is 43 | # never called. In Jedi though, we still want to infer types. 44 | while origin_scope is not None: 45 | if element_scope == origin_scope: 46 | return REACHABLE 47 | origin_scope = origin_scope.parent 48 | x = _break_check(evaluator, stmt, base_scope, element_scope) 49 | return x 50 | 51 | 52 | def _break_check(evaluator, stmt, base_scope, element_scope): 53 | element_scope = evaluator.wrap(element_scope) 54 | base_scope = evaluator.wrap(base_scope) 55 | 56 | reachable = REACHABLE 57 | if isinstance(element_scope, tree.IfStmt): 58 | if element_scope.node_after_else(stmt): 59 | for check_node in element_scope.check_nodes(): 60 | reachable = _check_if(evaluator, check_node) 61 | if reachable in (REACHABLE, UNSURE): 62 | break 63 | reachable = reachable.invert() 64 | else: 65 | node = element_scope.node_in_which_check_node(stmt) 66 | if node is not None: 67 | reachable = _check_if(evaluator, node) 68 | elif isinstance(element_scope, (tree.TryStmt, tree.WhileStmt)): 69 | return UNSURE 70 | 71 | # Only reachable branches need to be examined further. 72 | if reachable in (UNREACHABLE, UNSURE): 73 | return reachable 74 | 75 | if element_scope.type == 'file_input': 76 | # The definition is in another module and therefore just return what we 77 | # have generated. 78 | return reachable 79 | if base_scope != element_scope and base_scope != element_scope.parent: 80 | return reachable & _break_check(evaluator, stmt, base_scope, element_scope.parent) 81 | else: 82 | return reachable 83 | 84 | 85 | def _check_if(evaluator, node): 86 | types = evaluator.eval_element(node) 87 | values = set(x.py__bool__() for x in types) 88 | if len(values) == 1: 89 | return Status.lookup_table[values.pop()] 90 | else: 91 | return UNSURE 92 | -------------------------------------------------------------------------------- /lib/jedi/evaluate/helpers.py: -------------------------------------------------------------------------------- 1 | import copy 2 | from itertools import chain 3 | 4 | from jedi.parser import tree 5 | 6 | 7 | def deep_ast_copy(obj, parent=None, new_elements=None): 8 | """ 9 | Much, much faster than copy.deepcopy, but just for Parser elements (Doesn't 10 | copy parents). 11 | """ 12 | 13 | if new_elements is None: 14 | new_elements = {} 15 | 16 | def copy_node(obj): 17 | # If it's already in the cache, just return it. 18 | try: 19 | return new_elements[obj] 20 | except KeyError: 21 | # Actually copy and set attributes. 22 | new_obj = copy.copy(obj) 23 | new_elements[obj] = new_obj 24 | 25 | # Copy children 26 | new_children = [] 27 | for child in obj.children: 28 | typ = child.type 29 | if typ in ('newline', 'operator', 'keyword', 'number', 'string', 30 | 'indent', 'dedent', 'endmarker', 'error_leaf'): 31 | # At the moment we're not actually copying those primitive 32 | # elements, because there's really no need to. The parents are 33 | # obviously wrong, but that's not an issue. 34 | new_child = child 35 | elif typ == 'name': 36 | new_elements[child] = new_child = copy.copy(child) 37 | new_child.parent = new_obj 38 | else: # Is a BaseNode. 39 | new_child = copy_node(child) 40 | new_child.parent = new_obj 41 | new_children.append(new_child) 42 | new_obj.children = new_children 43 | 44 | # Copy the names_dict (if there is one). 45 | try: 46 | names_dict = obj.names_dict 47 | except AttributeError: 48 | pass 49 | else: 50 | try: 51 | new_obj.names_dict = new_names_dict = {} 52 | except AttributeError: # Impossible to set CompFor.names_dict 53 | pass 54 | else: 55 | for string, names in names_dict.items(): 56 | new_names_dict[string] = [new_elements[n] for n in names] 57 | return new_obj 58 | 59 | if isinstance(obj, tree.BaseNode): 60 | new_obj = copy_node(obj) 61 | else: 62 | # Special case of a Name object. 63 | new_elements[obj] = new_obj = copy.copy(obj) 64 | 65 | if parent is not None: 66 | new_obj.parent = parent 67 | return new_obj 68 | 69 | 70 | def call_of_leaf(leaf, cut_own_trailer=False): 71 | """ 72 | Creates a "call" node that consist of all ``trailer`` and ``power`` 73 | objects. E.g. if you call it with ``append``:: 74 | 75 | list([]).append(3) or None 76 | 77 | You would get a node with the content ``list([]).append`` back. 78 | 79 | This generates a copy of the original ast node. 80 | 81 | If you're using the leaf, e.g. the bracket `)` it will return ``list([])``. 82 | 83 | # TODO remove cut_own_trailer option, since its always used with it. Just 84 | # ignore it, It's not what we want anyway. Or document it better? 85 | """ 86 | trailer = leaf.parent 87 | # The leaf may not be the last or first child, because there exist three 88 | # different trailers: `( x )`, `[ x ]` and `.x`. In the first two examples 89 | # we should not match anything more than x. 90 | if trailer.type != 'trailer' or leaf not in (trailer.children[0], trailer.children[-1]): 91 | if trailer.type == 'atom': 92 | return trailer 93 | return leaf 94 | 95 | power = trailer.parent 96 | index = power.children.index(trailer) 97 | power = deep_ast_copy(power) 98 | if cut_own_trailer: 99 | cut = index 100 | else: 101 | cut = index + 1 102 | power.children[cut:] = [] 103 | 104 | if power.type == 'error_node': 105 | start = index 106 | while True: 107 | start -= 1 108 | if power.children[start].type != 'trailer': 109 | break 110 | transformed = tree.Node('power', power.children[start:]) 111 | transformed.parent = power.parent 112 | return transformed 113 | 114 | return power 115 | 116 | 117 | def get_names_of_node(node): 118 | try: 119 | children = node.children 120 | except AttributeError: 121 | if node.type == 'name': 122 | return [node] 123 | else: 124 | return [] 125 | else: 126 | return list(chain.from_iterable(get_names_of_node(c) for c in children)) 127 | 128 | 129 | def get_module_names(module, all_scopes): 130 | """ 131 | Returns a dictionary with name parts as keys and their call paths as 132 | values. 133 | """ 134 | if all_scopes: 135 | dct = module.used_names 136 | else: 137 | dct = module.names_dict 138 | return chain.from_iterable(dct.values()) 139 | 140 | 141 | class FakeImport(tree.ImportName): 142 | def __init__(self, name, parent, level=0): 143 | super(FakeImport, self).__init__([]) 144 | self.parent = parent 145 | self._level = level 146 | self.name = name 147 | 148 | def get_defined_names(self): 149 | return [self.name] 150 | 151 | def aliases(self): 152 | return {} 153 | 154 | @property 155 | def level(self): 156 | return self._level 157 | 158 | @property 159 | def start_pos(self): 160 | return 0, 0 161 | 162 | def paths(self): 163 | return [[self.name]] 164 | 165 | def is_definition(self): 166 | return True 167 | 168 | 169 | class FakeName(tree.Name): 170 | def __init__(self, name_str, parent=None, start_pos=(0, 0), is_definition=None): 171 | """ 172 | In case is_definition is defined (not None), that bool value will be 173 | returned. 174 | """ 175 | super(FakeName, self).__init__(tree.zero_position_modifier, name_str, start_pos) 176 | self.parent = parent 177 | self._is_definition = is_definition 178 | 179 | def get_definition(self): 180 | return self.parent 181 | 182 | def is_definition(self): 183 | if self._is_definition is None: 184 | return super(FakeName, self).is_definition() 185 | else: 186 | return self._is_definition 187 | 188 | 189 | class LazyName(FakeName): 190 | def __init__(self, name, parent_callback, is_definition=None): 191 | super(LazyName, self).__init__(name, is_definition=is_definition) 192 | self._parent_callback = parent_callback 193 | 194 | @property 195 | def parent(self): 196 | return self._parent_callback() 197 | 198 | @parent.setter 199 | def parent(self, value): 200 | pass # Do nothing, super classes can try to set the parent. 201 | -------------------------------------------------------------------------------- /lib/jedi/evaluate/jedi_typing.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module is not intended to be used in jedi, rather it will be fed to the 3 | jedi-parser to replace classes in the typing module 4 | """ 5 | 6 | try: 7 | from collections import abc 8 | except ImportError: 9 | # python 2 10 | import collections as abc 11 | 12 | 13 | def factory(typing_name, indextypes): 14 | class Iterable(abc.Iterable): 15 | def __iter__(self): 16 | while True: 17 | yield indextypes[0]() 18 | 19 | class Iterator(Iterable, abc.Iterator): 20 | def next(self): 21 | """ needed for python 2 """ 22 | return self.__next__() 23 | 24 | def __next__(self): 25 | return indextypes[0]() 26 | 27 | class Sequence(abc.Sequence): 28 | def __getitem__(self, index): 29 | return indextypes[0]() 30 | 31 | class MutableSequence(Sequence, abc.MutableSequence): 32 | pass 33 | 34 | class List(MutableSequence, list): 35 | pass 36 | 37 | class Tuple(Sequence, tuple): 38 | def __getitem__(self, index): 39 | if indextypes[1] == Ellipsis: 40 | # https://www.python.org/dev/peps/pep-0484/#the-typing-module 41 | # Tuple[int, ...] means a tuple of ints of indetermined length 42 | return indextypes[0]() 43 | else: 44 | return indextypes[index]() 45 | 46 | class AbstractSet(Iterable, abc.Set): 47 | pass 48 | 49 | class MutableSet(AbstractSet, abc.MutableSet): 50 | pass 51 | 52 | class KeysView(Iterable, abc.KeysView): 53 | pass 54 | 55 | class ValuesView(abc.ValuesView): 56 | def __iter__(self): 57 | while True: 58 | yield indextypes[1]() 59 | 60 | class ItemsView(abc.ItemsView): 61 | def __iter__(self): 62 | while True: 63 | yield (indextypes[0](), indextypes[1]()) 64 | 65 | class Mapping(Iterable, abc.Mapping): 66 | def __getitem__(self, item): 67 | return indextypes[1]() 68 | 69 | def keys(self): 70 | return KeysView() 71 | 72 | def values(self): 73 | return ValuesView() 74 | 75 | def items(self): 76 | return ItemsView() 77 | 78 | class MutableMapping(Mapping, abc.MutableMapping): 79 | pass 80 | 81 | class Dict(MutableMapping, dict): 82 | pass 83 | 84 | dct = { 85 | "Sequence": Sequence, 86 | "MutableSequence": MutableSequence, 87 | "List": List, 88 | "Iterable": Iterable, 89 | "Iterator": Iterator, 90 | "AbstractSet": AbstractSet, 91 | "MutableSet": MutableSet, 92 | "Mapping": Mapping, 93 | "MutableMapping": MutableMapping, 94 | "Tuple": Tuple, 95 | "KeysView": KeysView, 96 | "ItemsView": ItemsView, 97 | "ValuesView": ValuesView, 98 | "Dict": Dict, 99 | } 100 | return dct[typing_name] 101 | -------------------------------------------------------------------------------- /lib/jedi/evaluate/pep0484.py: -------------------------------------------------------------------------------- 1 | """ 2 | PEP 0484 ( https://www.python.org/dev/peps/pep-0484/ ) describes type hints 3 | through function annotations. There is a strong suggestion in this document 4 | that only the type of type hinting defined in PEP0484 should be allowed 5 | as annotations in future python versions. 6 | 7 | The (initial / probably incomplete) implementation todo list for pep-0484: 8 | v Function parameter annotations with builtin/custom type classes 9 | v Function returntype annotations with builtin/custom type classes 10 | v Function parameter annotations with strings (forward reference) 11 | v Function return type annotations with strings (forward reference) 12 | v Local variable type hints 13 | v Assigned types: `Url = str\ndef get(url:Url) -> str:` 14 | v Type hints in `with` statements 15 | x Stub files support 16 | x support `@no_type_check` and `@no_type_check_decorator` 17 | x support for typing.cast() operator 18 | x support for type hint comments for functions, `# type: (int, str) -> int`. 19 | See comment from Guido https://github.com/davidhalter/jedi/issues/662 20 | """ 21 | 22 | import itertools 23 | 24 | import os 25 | from jedi.parser import \ 26 | Parser, load_grammar, ParseError, ParserWithRecovery, tree 27 | from jedi.evaluate.cache import memoize_default 28 | from jedi.common import unite 29 | from jedi.evaluate import compiled 30 | from jedi import debug 31 | from jedi import _compatibility 32 | import re 33 | 34 | 35 | def _evaluate_for_annotation(evaluator, annotation, index=None): 36 | """ 37 | Evaluates a string-node, looking for an annotation 38 | If index is not None, the annotation is expected to be a tuple 39 | and we're interested in that index 40 | """ 41 | if annotation is not None: 42 | definitions = evaluator.eval_element( 43 | _fix_forward_reference(evaluator, annotation)) 44 | if index is not None: 45 | definitions = list(itertools.chain.from_iterable( 46 | definition.py__getitem__(index) for definition in definitions 47 | if definition.type == 'tuple' and 48 | len(list(definition.py__iter__())) >= index)) 49 | return list(itertools.chain.from_iterable( 50 | evaluator.execute(d) for d in definitions)) 51 | else: 52 | return [] 53 | 54 | 55 | def _fix_forward_reference(evaluator, node): 56 | evaled_nodes = evaluator.eval_element(node) 57 | if len(evaled_nodes) != 1: 58 | debug.warning("Eval'ed typing index %s should lead to 1 object, " 59 | " not %s" % (node, evaled_nodes)) 60 | return node 61 | evaled_node = list(evaled_nodes)[0] 62 | if isinstance(evaled_node, compiled.CompiledObject) and \ 63 | isinstance(evaled_node.obj, str): 64 | try: 65 | p = Parser(load_grammar(), _compatibility.unicode(evaled_node.obj), 66 | start_symbol='eval_input') 67 | newnode = p.get_parsed_node() 68 | except ParseError: 69 | debug.warning('Annotation not parsed: %s' % evaled_node.obj) 70 | return node 71 | else: 72 | module = node.get_parent_until() 73 | p.position_modifier.line = module.end_pos[0] 74 | newnode.parent = module 75 | return newnode 76 | else: 77 | return node 78 | 79 | 80 | @memoize_default(None, evaluator_is_first_arg=True) 81 | def follow_param(evaluator, param): 82 | annotation = param.annotation() 83 | return _evaluate_for_annotation(evaluator, annotation) 84 | 85 | 86 | @memoize_default(None, evaluator_is_first_arg=True) 87 | def find_return_types(evaluator, func): 88 | annotation = func.py__annotations__().get("return", None) 89 | return _evaluate_for_annotation(evaluator, annotation) 90 | 91 | 92 | _typing_module = None 93 | 94 | 95 | def _get_typing_replacement_module(): 96 | """ 97 | The idea is to return our jedi replacement for the PEP-0484 typing module 98 | as discussed at https://github.com/davidhalter/jedi/issues/663 99 | """ 100 | global _typing_module 101 | if _typing_module is None: 102 | typing_path = \ 103 | os.path.abspath(os.path.join(__file__, "../jedi_typing.py")) 104 | with open(typing_path) as f: 105 | code = _compatibility.unicode(f.read()) 106 | p = ParserWithRecovery(load_grammar(), code) 107 | _typing_module = p.module 108 | return _typing_module 109 | 110 | 111 | def get_types_for_typing_module(evaluator, typ, node): 112 | from jedi.evaluate.iterable import FakeSequence 113 | if not typ.base.get_parent_until().name.value == "typing": 114 | return None 115 | # we assume that any class using [] in a module called 116 | # "typing" with a name for which we have a replacement 117 | # should be replaced by that class. This is not 100% 118 | # airtight but I don't have a better idea to check that it's 119 | # actually the PEP-0484 typing module and not some other 120 | if tree.is_node(node, "subscriptlist"): 121 | nodes = node.children[::2] # skip the commas 122 | else: 123 | nodes = [node] 124 | del node 125 | 126 | nodes = [_fix_forward_reference(evaluator, node) for node in nodes] 127 | 128 | # hacked in Union and Optional, since it's hard to do nicely in parsed code 129 | if typ.name.value == "Union": 130 | return unite(evaluator.eval_element(node) for node in nodes) 131 | if typ.name.value == "Optional": 132 | return evaluator.eval_element(nodes[0]) 133 | 134 | typing = _get_typing_replacement_module() 135 | factories = evaluator.find_types(typing, "factory") 136 | assert len(factories) == 1 137 | factory = list(factories)[0] 138 | assert factory 139 | function_body_nodes = factory.children[4].children 140 | valid_classnames = set(child.name.value 141 | for child in function_body_nodes 142 | if isinstance(child, tree.Class)) 143 | if typ.name.value not in valid_classnames: 144 | return None 145 | compiled_classname = compiled.create(evaluator, typ.name.value) 146 | 147 | args = FakeSequence(evaluator, nodes, "tuple") 148 | 149 | result = evaluator.execute_evaluated(factory, compiled_classname, args) 150 | return result 151 | 152 | 153 | def find_type_from_comment_hint_for(evaluator, node, name): 154 | return \ 155 | _find_type_from_comment_hint(evaluator, node, node.children[1], name) 156 | 157 | 158 | def find_type_from_comment_hint_with(evaluator, node, name): 159 | assert len(node.children[1].children) == 3, \ 160 | "Can only be here when children[1] is 'foo() as f'" 161 | return _find_type_from_comment_hint( 162 | evaluator, node, node.children[1].children[2], name) 163 | 164 | 165 | def find_type_from_comment_hint_assign(evaluator, node, name): 166 | return \ 167 | _find_type_from_comment_hint(evaluator, node, node.children[0], name) 168 | 169 | 170 | def _find_type_from_comment_hint(evaluator, node, varlist, name): 171 | index = None 172 | if varlist.type in ("testlist_star_expr", "exprlist"): 173 | # something like "a, b = 1, 2" 174 | index = 0 175 | for child in varlist.children: 176 | if child == name: 177 | break 178 | if child.type == "operator": 179 | continue 180 | index += 1 181 | else: 182 | return [] 183 | 184 | comment = node.get_following_comment_same_line() 185 | if comment is None: 186 | return [] 187 | match = re.match(r"^#\s*type:\s*([^#]*)", comment) 188 | if not match: 189 | return [] 190 | annotation = tree.String( 191 | tree.zero_position_modifier, 192 | repr(str(match.group(1).strip())), 193 | node.start_pos) 194 | annotation.parent = node.parent 195 | return _evaluate_for_annotation(evaluator, annotation, index) 196 | -------------------------------------------------------------------------------- /lib/jedi/evaluate/precedence.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles operator precedence. 3 | """ 4 | import operator 5 | 6 | from jedi._compatibility import unicode 7 | from jedi.parser import tree 8 | from jedi import debug 9 | from jedi.evaluate.compiled import CompiledObject, create, builtin_from_name 10 | from jedi.evaluate import analysis 11 | 12 | # Maps Python syntax to the operator module. 13 | COMPARISON_OPERATORS = { 14 | '==': operator.eq, 15 | '!=': operator.ne, 16 | 'is': operator.is_, 17 | 'is not': operator.is_not, 18 | '<': operator.lt, 19 | '<=': operator.le, 20 | '>': operator.gt, 21 | '>=': operator.ge, 22 | } 23 | 24 | 25 | def literals_to_types(evaluator, result): 26 | # Changes literals ('a', 1, 1.0, etc) to its type instances (str(), 27 | # int(), float(), etc). 28 | new_result = set() 29 | for typ in result: 30 | if is_literal(typ): 31 | # Literals are only valid as long as the operations are 32 | # correct. Otherwise add a value-free instance. 33 | cls = builtin_from_name(evaluator, typ.name.value) 34 | new_result |= evaluator.execute(cls) 35 | else: 36 | new_result.add(typ) 37 | return new_result 38 | 39 | 40 | def calculate_children(evaluator, children): 41 | """ 42 | Calculate a list of children with operators. 43 | """ 44 | iterator = iter(children) 45 | types = evaluator.eval_element(next(iterator)) 46 | for operator in iterator: 47 | right = next(iterator) 48 | if tree.is_node(operator, 'comp_op'): # not in / is not 49 | operator = ' '.join(str(c.value) for c in operator.children) 50 | 51 | # handle lazy evaluation of and/or here. 52 | if operator in ('and', 'or'): 53 | left_bools = set([left.py__bool__() for left in types]) 54 | if left_bools == set([True]): 55 | if operator == 'and': 56 | types = evaluator.eval_element(right) 57 | elif left_bools == set([False]): 58 | if operator != 'and': 59 | types = evaluator.eval_element(right) 60 | # Otherwise continue, because of uncertainty. 61 | else: 62 | types = calculate(evaluator, types, operator, 63 | evaluator.eval_element(right)) 64 | debug.dbg('calculate_children types %s', types) 65 | return types 66 | 67 | 68 | def calculate(evaluator, left_result, operator, right_result): 69 | result = set() 70 | if not left_result or not right_result: 71 | # illegal slices e.g. cause left/right_result to be None 72 | result = (left_result or set()) | (right_result or set()) 73 | result = literals_to_types(evaluator, result) 74 | else: 75 | # I don't think there's a reasonable chance that a string 76 | # operation is still correct, once we pass something like six 77 | # objects. 78 | if len(left_result) * len(right_result) > 6: 79 | result = literals_to_types(evaluator, left_result | right_result) 80 | else: 81 | for left in left_result: 82 | for right in right_result: 83 | result |= _element_calculate(evaluator, left, operator, right) 84 | return result 85 | 86 | 87 | def factor_calculate(evaluator, types, operator): 88 | """ 89 | Calculates `+`, `-`, `~` and `not` prefixes. 90 | """ 91 | for typ in types: 92 | if operator == '-': 93 | if _is_number(typ): 94 | yield create(evaluator, -typ.obj) 95 | elif operator == 'not': 96 | value = typ.py__bool__() 97 | if value is None: # Uncertainty. 98 | return 99 | yield create(evaluator, not value) 100 | else: 101 | yield typ 102 | 103 | 104 | def _is_number(obj): 105 | return isinstance(obj, CompiledObject) \ 106 | and isinstance(obj.obj, (int, float)) 107 | 108 | 109 | def is_string(obj): 110 | return isinstance(obj, CompiledObject) \ 111 | and isinstance(obj.obj, (str, unicode)) 112 | 113 | 114 | def is_literal(obj): 115 | return _is_number(obj) or is_string(obj) 116 | 117 | 118 | def _is_tuple(obj): 119 | from jedi.evaluate import iterable 120 | return isinstance(obj, iterable.Array) and obj.type == 'tuple' 121 | 122 | 123 | def _is_list(obj): 124 | from jedi.evaluate import iterable 125 | return isinstance(obj, iterable.Array) and obj.type == 'list' 126 | 127 | 128 | def _element_calculate(evaluator, left, operator, right): 129 | from jedi.evaluate import iterable, representation as er 130 | l_is_num = _is_number(left) 131 | r_is_num = _is_number(right) 132 | if operator == '*': 133 | # for iterables, ignore * operations 134 | if isinstance(left, iterable.Array) or is_string(left): 135 | return set([left]) 136 | elif isinstance(right, iterable.Array) or is_string(right): 137 | return set([right]) 138 | elif operator == '+': 139 | if l_is_num and r_is_num or is_string(left) and is_string(right): 140 | return set([create(evaluator, left.obj + right.obj)]) 141 | elif _is_tuple(left) and _is_tuple(right) or _is_list(left) and _is_list(right): 142 | return set([iterable.MergedArray(evaluator, (left, right))]) 143 | elif operator == '-': 144 | if l_is_num and r_is_num: 145 | return set([create(evaluator, left.obj - right.obj)]) 146 | elif operator == '%': 147 | # With strings and numbers the left type typically remains. Except for 148 | # `int() % float()`. 149 | return set([left]) 150 | elif operator in COMPARISON_OPERATORS: 151 | operation = COMPARISON_OPERATORS[operator] 152 | if isinstance(left, CompiledObject) and isinstance(right, CompiledObject): 153 | # Possible, because the return is not an option. Just compare. 154 | left = left.obj 155 | right = right.obj 156 | 157 | try: 158 | result = operation(left, right) 159 | except TypeError: 160 | # Could be True or False. 161 | return set([create(evaluator, True), create(evaluator, False)]) 162 | else: 163 | return set([create(evaluator, result)]) 164 | elif operator == 'in': 165 | return set() 166 | 167 | def check(obj): 168 | """Checks if a Jedi object is either a float or an int.""" 169 | return isinstance(obj, er.Instance) and obj.name.get_code() in ('int', 'float') 170 | 171 | # Static analysis, one is a number, the other one is not. 172 | if operator in ('+', '-') and l_is_num != r_is_num \ 173 | and not (check(left) or check(right)): 174 | message = "TypeError: unsupported operand type(s) for +: %s and %s" 175 | analysis.add(evaluator, 'type-error-operation', operator, 176 | message % (left, right)) 177 | 178 | return set([left, right]) 179 | -------------------------------------------------------------------------------- /lib/jedi/evaluate/recursion.py: -------------------------------------------------------------------------------- 1 | """ 2 | Recursions are the recipe of |jedi| to conquer Python code. However, someone 3 | must stop recursions going mad. Some settings are here to make |jedi| stop at 4 | the right time. You can read more about them :ref:`here `. 5 | 6 | Next to :mod:`jedi.evaluate.cache` this module also makes |jedi| not 7 | thread-safe. Why? ``execution_recursion_decorator`` uses class variables to 8 | count the function calls. 9 | """ 10 | from jedi import debug 11 | from jedi import settings 12 | from jedi.evaluate import iterable 13 | 14 | 15 | def recursion_decorator(func): 16 | def run(evaluator, stmt, *args, **kwargs): 17 | rec_detect = evaluator.recursion_detector 18 | if rec_detect.push_stmt(stmt): 19 | return set() 20 | else: 21 | result = func(evaluator, stmt, *args, **kwargs) 22 | rec_detect.pop_stmt() 23 | return result 24 | return run 25 | 26 | 27 | class RecursionDetector(object): 28 | """ 29 | A decorator to detect recursions in statements. In a recursion a statement 30 | at the same place, in the same module may not be executed two times. 31 | """ 32 | def __init__(self, evaluator): 33 | self.top = None 34 | self.current = None 35 | self._evaluator = evaluator 36 | 37 | def push_stmt(self, stmt): 38 | self.current = _RecursionNode(self._evaluator, stmt, self.current) 39 | check = self._check_recursion() 40 | if check: 41 | debug.warning('catched stmt recursion: %s against %s @%s', stmt, 42 | check.stmt, stmt.start_pos) 43 | self.pop_stmt() 44 | return True 45 | return False 46 | 47 | def pop_stmt(self): 48 | if self.current is not None: 49 | # I don't know how current can be None, but sometimes it happens 50 | # with Python3. 51 | self.current = self.current.parent 52 | 53 | def _check_recursion(self): 54 | test = self.current 55 | while True: 56 | test = test.parent 57 | if self.current == test: 58 | return test 59 | if not test: 60 | return False 61 | 62 | def node_statements(self): 63 | result = [] 64 | n = self.current 65 | while n: 66 | result.insert(0, n.stmt) 67 | n = n.parent 68 | return result 69 | 70 | 71 | class _RecursionNode(object): 72 | """ A node of the RecursionDecorator. """ 73 | def __init__(self, evaluator, stmt, parent): 74 | self._evaluator = evaluator 75 | self.script = stmt.get_parent_until() 76 | self.position = stmt.start_pos 77 | self.parent = parent 78 | self.stmt = stmt 79 | 80 | # Don't check param instances, they are not causing recursions 81 | # The same's true for the builtins, because the builtins are really 82 | # simple. 83 | self.is_ignored = self.script == self._evaluator.BUILTINS 84 | 85 | def __eq__(self, other): 86 | if not other: 87 | return None 88 | 89 | return self.script == other.script \ 90 | and self.position == other.position \ 91 | and not self.is_ignored and not other.is_ignored 92 | 93 | 94 | def execution_recursion_decorator(func): 95 | def run(execution, **kwargs): 96 | detector = execution._evaluator.execution_recursion_detector 97 | if detector.push_execution(execution): 98 | result = set() 99 | else: 100 | result = func(execution, **kwargs) 101 | detector.pop_execution() 102 | return result 103 | 104 | return run 105 | 106 | 107 | class ExecutionRecursionDetector(object): 108 | """ 109 | Catches recursions of executions. 110 | """ 111 | def __init__(self, evaluator): 112 | self.recursion_level = 0 113 | self.parent_execution_funcs = [] 114 | self.execution_funcs = set() 115 | self.execution_count = 0 116 | self._evaluator = evaluator 117 | 118 | def __call__(self, execution): 119 | debug.dbg('Execution recursions: %s', execution, self.recursion_level, 120 | self.execution_count, len(self.execution_funcs)) 121 | if self.check_recursion(execution): 122 | result = set() 123 | else: 124 | result = self.func(execution) 125 | self.pop_execution() 126 | return result 127 | 128 | def pop_execution(self): 129 | self.parent_execution_funcs.pop() 130 | self.recursion_level -= 1 131 | 132 | def push_execution(self, execution): 133 | in_par_execution_funcs = execution.base in self.parent_execution_funcs 134 | in_execution_funcs = execution.base in self.execution_funcs 135 | self.recursion_level += 1 136 | self.execution_count += 1 137 | self.execution_funcs.add(execution.base) 138 | self.parent_execution_funcs.append(execution.base) 139 | 140 | if self.execution_count > settings.max_executions: 141 | return True 142 | 143 | if isinstance(execution.base, (iterable.Array, iterable.Generator)): 144 | return False 145 | module = execution.get_parent_until() 146 | if module == self._evaluator.BUILTINS: 147 | return False 148 | 149 | if in_par_execution_funcs: 150 | if self.recursion_level > settings.max_function_recursion_level: 151 | return True 152 | if in_execution_funcs and \ 153 | len(self.execution_funcs) > settings.max_until_execution_unique: 154 | return True 155 | if self.execution_count > settings.max_executions_without_builtins: 156 | return True 157 | return False 158 | -------------------------------------------------------------------------------- /lib/jedi/evaluate/site.py: -------------------------------------------------------------------------------- 1 | """An adapted copy of relevant site-packages functionality from Python stdlib. 2 | 3 | This file contains some functions related to handling site-packages in Python 4 | with jedi-specific modifications: 5 | 6 | - the functions operate on sys_path argument rather than global sys.path 7 | 8 | - in .pth files "import ..." lines that allow execution of arbitrary code are 9 | skipped to prevent code injection into jedi interpreter 10 | 11 | """ 12 | 13 | # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 14 | # 2011, 2012, 2013, 2014, 2015 Python Software Foundation; All Rights Reserved 15 | 16 | from __future__ import print_function 17 | 18 | import sys 19 | import os 20 | 21 | 22 | def makepath(*paths): 23 | dir = os.path.join(*paths) 24 | try: 25 | dir = os.path.abspath(dir) 26 | except OSError: 27 | pass 28 | return dir, os.path.normcase(dir) 29 | 30 | 31 | def _init_pathinfo(sys_path): 32 | """Return a set containing all existing directory entries from sys_path""" 33 | d = set() 34 | for dir in sys_path: 35 | try: 36 | if os.path.isdir(dir): 37 | dir, dircase = makepath(dir) 38 | d.add(dircase) 39 | except TypeError: 40 | continue 41 | return d 42 | 43 | 44 | def addpackage(sys_path, sitedir, name, known_paths): 45 | """Process a .pth file within the site-packages directory: 46 | For each line in the file, either combine it with sitedir to a path 47 | and add that to known_paths, or execute it if it starts with 'import '. 48 | """ 49 | if known_paths is None: 50 | known_paths = _init_pathinfo(sys_path) 51 | reset = 1 52 | else: 53 | reset = 0 54 | fullname = os.path.join(sitedir, name) 55 | try: 56 | f = open(fullname, "r") 57 | except OSError: 58 | return 59 | with f: 60 | for n, line in enumerate(f): 61 | if line.startswith("#"): 62 | continue 63 | try: 64 | if line.startswith(("import ", "import\t")): 65 | # Change by immerrr: don't evaluate import lines to prevent 66 | # code injection into jedi through pth files. 67 | # 68 | # exec(line) 69 | continue 70 | line = line.rstrip() 71 | dir, dircase = makepath(sitedir, line) 72 | if not dircase in known_paths and os.path.exists(dir): 73 | sys_path.append(dir) 74 | known_paths.add(dircase) 75 | except Exception: 76 | print("Error processing line {:d} of {}:\n".format(n+1, fullname), 77 | file=sys.stderr) 78 | import traceback 79 | for record in traceback.format_exception(*sys.exc_info()): 80 | for line in record.splitlines(): 81 | print(' '+line, file=sys.stderr) 82 | print("\nRemainder of file ignored", file=sys.stderr) 83 | break 84 | if reset: 85 | known_paths = None 86 | return known_paths 87 | 88 | 89 | def addsitedir(sys_path, sitedir, known_paths=None): 90 | """Add 'sitedir' argument to sys_path if missing and handle .pth files in 91 | 'sitedir'""" 92 | if known_paths is None: 93 | known_paths = _init_pathinfo(sys_path) 94 | reset = 1 95 | else: 96 | reset = 0 97 | sitedir, sitedircase = makepath(sitedir) 98 | if not sitedircase in known_paths: 99 | sys_path.append(sitedir) # Add path component 100 | known_paths.add(sitedircase) 101 | try: 102 | names = os.listdir(sitedir) 103 | except OSError: 104 | return 105 | names = [name for name in names if name.endswith(".pth")] 106 | for name in sorted(names): 107 | addpackage(sys_path, sitedir, name, known_paths) 108 | if reset: 109 | known_paths = None 110 | return known_paths 111 | -------------------------------------------------------------------------------- /lib/jedi/parser/grammar2.7.txt: -------------------------------------------------------------------------------- 1 | # Grammar for 2to3. This grammar supports Python 2.x and 3.x. 2 | 3 | # Note: Changing the grammar specified in this file will most likely 4 | # require corresponding changes in the parser module 5 | # (../Modules/parsermodule.c). If you can't make the changes to 6 | # that module yourself, please co-ordinate the required changes 7 | # with someone who can; ask around on python-dev for help. Fred 8 | # Drake will probably be listening there. 9 | 10 | # NOTE WELL: You should also follow all the steps listed in PEP 306, 11 | # "How to Change Python's Grammar" 12 | 13 | 14 | # Start symbols for the grammar: 15 | # file_input is a module or sequence of commands read from an input file; 16 | # single_input is a single interactive statement; 17 | # eval_input is the input for the eval() and input() functions. 18 | # NB: compound_stmt in single_input is followed by extra NEWLINE! 19 | file_input: (NEWLINE | stmt)* ENDMARKER 20 | single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE 21 | eval_input: testlist NEWLINE* ENDMARKER 22 | 23 | decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE 24 | decorators: decorator+ 25 | decorated: decorators (classdef | funcdef) 26 | funcdef: 'def' NAME parameters ['->' test] ':' suite 27 | parameters: '(' [typedargslist] ')' 28 | typedargslist: ((tfpdef ['=' test] ',')* 29 | ('*' [tname] (',' tname ['=' test])* [',' '**' tname] | '**' tname) 30 | | tfpdef ['=' test] (',' tfpdef ['=' test])* [',']) 31 | tname: NAME [':' test] 32 | tfpdef: tname | '(' tfplist ')' 33 | tfplist: tfpdef (',' tfpdef)* [','] 34 | varargslist: ((vfpdef ['=' test] ',')* 35 | ('*' [vname] (',' vname ['=' test])* [',' '**' vname] | '**' vname) 36 | | vfpdef ['=' test] (',' vfpdef ['=' test])* [',']) 37 | vname: NAME 38 | vfpdef: vname | '(' vfplist ')' 39 | vfplist: vfpdef (',' vfpdef)* [','] 40 | 41 | stmt: simple_stmt | compound_stmt 42 | simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE 43 | small_stmt: (expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt | 44 | import_stmt | global_stmt | exec_stmt | assert_stmt) 45 | expr_stmt: testlist_star_expr (augassign (yield_expr|testlist) | 46 | ('=' (yield_expr|testlist_star_expr))*) 47 | testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [','] 48 | augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | 49 | '<<=' | '>>=' | '**=' | '//=') 50 | # For normal assignments, additional restrictions enforced by the interpreter 51 | print_stmt: 'print' ( [ test (',' test)* [','] ] | 52 | '>>' test [ (',' test)+ [','] ] ) 53 | del_stmt: 'del' exprlist 54 | pass_stmt: 'pass' 55 | flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt 56 | break_stmt: 'break' 57 | continue_stmt: 'continue' 58 | return_stmt: 'return' [testlist] 59 | yield_stmt: yield_expr 60 | raise_stmt: 'raise' [test [',' test [',' test]]] 61 | import_stmt: import_name | import_from 62 | import_name: 'import' dotted_as_names 63 | # note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS 64 | import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+) 65 | 'import' ('*' | '(' import_as_names ')' | import_as_names)) 66 | import_as_name: NAME ['as' NAME] 67 | dotted_as_name: dotted_name ['as' NAME] 68 | import_as_names: import_as_name (',' import_as_name)* [','] 69 | dotted_as_names: dotted_as_name (',' dotted_as_name)* 70 | dotted_name: NAME ('.' NAME)* 71 | global_stmt: 'global' NAME (',' NAME)* 72 | exec_stmt: 'exec' expr ['in' test [',' test]] 73 | assert_stmt: 'assert' test [',' test] 74 | 75 | compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated 76 | if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] 77 | while_stmt: 'while' test ':' suite ['else' ':' suite] 78 | for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] 79 | try_stmt: ('try' ':' suite 80 | ((except_clause ':' suite)+ 81 | ['else' ':' suite] 82 | ['finally' ':' suite] | 83 | 'finally' ':' suite)) 84 | with_stmt: 'with' with_item (',' with_item)* ':' suite 85 | with_item: test ['as' expr] 86 | with_var: 'as' expr 87 | # NB compile.c makes sure that the default except clause is last 88 | except_clause: 'except' [test [(',' | 'as') test]] 89 | # Edit by David Halter: The stmt is now optional. This reflects how Jedi allows 90 | # classes and functions to be empty, which is beneficial for autocompletion. 91 | suite: simple_stmt | NEWLINE INDENT stmt* DEDENT 92 | 93 | # Backward compatibility cruft to support: 94 | # [ x for x in lambda: True, lambda: False if x() ] 95 | # even while also allowing: 96 | # lambda x: 5 if x else 2 97 | # (But not a mix of the two) 98 | testlist_safe: old_test [(',' old_test)+ [',']] 99 | old_test: or_test | old_lambdef 100 | old_lambdef: 'lambda' [varargslist] ':' old_test 101 | 102 | test: or_test ['if' or_test 'else' test] | lambdef 103 | or_test: and_test ('or' and_test)* 104 | and_test: not_test ('and' not_test)* 105 | not_test: 'not' not_test | comparison 106 | comparison: expr (comp_op expr)* 107 | comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' 108 | star_expr: '*' expr 109 | expr: xor_expr ('|' xor_expr)* 110 | xor_expr: and_expr ('^' and_expr)* 111 | and_expr: shift_expr ('&' shift_expr)* 112 | shift_expr: arith_expr (('<<'|'>>') arith_expr)* 113 | arith_expr: term (('+'|'-') term)* 114 | term: factor (('*'|'/'|'%'|'//') factor)* 115 | factor: ('+'|'-'|'~') factor | power 116 | power: atom trailer* ['**' factor] 117 | atom: ('(' [yield_expr|testlist_comp] ')' | 118 | '[' [testlist_comp] ']' | 119 | '{' [dictorsetmaker] '}' | 120 | '`' testlist1 '`' | 121 | NAME | NUMBER | STRING+ | '.' '.' '.') 122 | # Modification by David Halter, remove `testlist_gexp` and `listmaker` 123 | testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] ) 124 | lambdef: 'lambda' [varargslist] ':' test 125 | trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME 126 | subscriptlist: subscript (',' subscript)* [','] 127 | subscript: test | [test] ':' [test] [sliceop] 128 | sliceop: ':' [test] 129 | exprlist: (expr|star_expr) (',' (expr|star_expr))* [','] 130 | testlist: test (',' test)* [','] 131 | # Modification by David Halter, dictsetmaker -> dictorsetmaker (so that it's 132 | # the same as in the 3.4 grammar). 133 | dictorsetmaker: ( (test ':' test (comp_for | (',' test ':' test)* [','])) | 134 | (test (comp_for | (',' test)* [','])) ) 135 | 136 | classdef: 'class' NAME ['(' [arglist] ')'] ':' suite 137 | 138 | arglist: (argument ',')* (argument [','] 139 | |'*' test (',' argument)* [',' '**' test] 140 | |'**' test) 141 | argument: test [comp_for] | test '=' test # Really [keyword '='] test 142 | 143 | comp_iter: comp_for | comp_if 144 | comp_for: 'for' exprlist 'in' testlist_safe [comp_iter] 145 | comp_if: 'if' old_test [comp_iter] 146 | 147 | testlist1: test (',' test)* 148 | 149 | # not used in grammar, but may appear in "node" passed from Parser to Compiler 150 | encoding_decl: NAME 151 | 152 | yield_expr: 'yield' [testlist] 153 | -------------------------------------------------------------------------------- /lib/jedi/parser/grammar3.4.txt: -------------------------------------------------------------------------------- 1 | # Grammar for Python 2 | 3 | # Note: Changing the grammar specified in this file will most likely 4 | # require corresponding changes in the parser module 5 | # (../Modules/parsermodule.c). If you can't make the changes to 6 | # that module yourself, please co-ordinate the required changes 7 | # with someone who can; ask around on python-dev for help. Fred 8 | # Drake will probably be listening there. 9 | 10 | # NOTE WELL: You should also follow all the steps listed in PEP 306, 11 | # "How to Change Python's Grammar" 12 | 13 | # Start symbols for the grammar: 14 | # single_input is a single interactive statement; 15 | # file_input is a module or sequence of commands read from an input file; 16 | # eval_input is the input for the eval() functions. 17 | # NB: compound_stmt in single_input is followed by extra NEWLINE! 18 | file_input: (NEWLINE | stmt)* ENDMARKER 19 | single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE 20 | eval_input: testlist NEWLINE* ENDMARKER 21 | 22 | decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE 23 | decorators: decorator+ 24 | decorated: decorators (classdef | funcdef) 25 | funcdef: 'def' NAME parameters ['->' test] ':' suite 26 | parameters: '(' [typedargslist] ')' 27 | typedargslist: (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' 28 | ['*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] | '**' tfpdef]] 29 | | '*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] | '**' tfpdef) 30 | tfpdef: NAME [':' test] 31 | varargslist: (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' 32 | ['*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef]] 33 | | '*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef) 34 | vfpdef: NAME 35 | 36 | stmt: simple_stmt | compound_stmt 37 | simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE 38 | small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt | 39 | import_stmt | global_stmt | nonlocal_stmt | assert_stmt) 40 | expr_stmt: testlist_star_expr (augassign (yield_expr|testlist) | 41 | ('=' (yield_expr|testlist_star_expr))*) 42 | testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [','] 43 | augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | 44 | '<<=' | '>>=' | '**=' | '//=') 45 | # For normal assignments, additional restrictions enforced by the interpreter 46 | del_stmt: 'del' exprlist 47 | pass_stmt: 'pass' 48 | flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt 49 | break_stmt: 'break' 50 | continue_stmt: 'continue' 51 | return_stmt: 'return' [testlist] 52 | yield_stmt: yield_expr 53 | raise_stmt: 'raise' [test ['from' test]] 54 | import_stmt: import_name | import_from 55 | import_name: 'import' dotted_as_names 56 | # note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS 57 | import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+) 58 | 'import' ('*' | '(' import_as_names ')' | import_as_names)) 59 | import_as_name: NAME ['as' NAME] 60 | dotted_as_name: dotted_name ['as' NAME] 61 | import_as_names: import_as_name (',' import_as_name)* [','] 62 | dotted_as_names: dotted_as_name (',' dotted_as_name)* 63 | dotted_name: NAME ('.' NAME)* 64 | global_stmt: 'global' NAME (',' NAME)* 65 | nonlocal_stmt: 'nonlocal' NAME (',' NAME)* 66 | assert_stmt: 'assert' test [',' test] 67 | 68 | compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated 69 | if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] 70 | while_stmt: 'while' test ':' suite ['else' ':' suite] 71 | for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] 72 | try_stmt: ('try' ':' suite 73 | ((except_clause ':' suite)+ 74 | ['else' ':' suite] 75 | ['finally' ':' suite] | 76 | 'finally' ':' suite)) 77 | with_stmt: 'with' with_item (',' with_item)* ':' suite 78 | with_item: test ['as' expr] 79 | # NB compile.c makes sure that the default except clause is last 80 | except_clause: 'except' [test ['as' NAME]] 81 | # Edit by David Halter: The stmt is now optional. This reflects how Jedi allows 82 | # classes and functions to be empty, which is beneficial for autocompletion. 83 | suite: simple_stmt | NEWLINE INDENT stmt* DEDENT 84 | 85 | test: or_test ['if' or_test 'else' test] | lambdef 86 | test_nocond: or_test | lambdef_nocond 87 | lambdef: 'lambda' [varargslist] ':' test 88 | lambdef_nocond: 'lambda' [varargslist] ':' test_nocond 89 | or_test: and_test ('or' and_test)* 90 | and_test: not_test ('and' not_test)* 91 | not_test: 'not' not_test | comparison 92 | comparison: expr (comp_op expr)* 93 | # <> isn't actually a valid comparison operator in Python. It's here for the 94 | # sake of a __future__ import described in PEP 401 95 | comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' 96 | star_expr: '*' expr 97 | expr: xor_expr ('|' xor_expr)* 98 | xor_expr: and_expr ('^' and_expr)* 99 | and_expr: shift_expr ('&' shift_expr)* 100 | shift_expr: arith_expr (('<<'|'>>') arith_expr)* 101 | arith_expr: term (('+'|'-') term)* 102 | term: factor (('*'|'/'|'%'|'//') factor)* 103 | factor: ('+'|'-'|'~') factor | power 104 | power: atom trailer* ['**' factor] 105 | atom: ('(' [yield_expr|testlist_comp] ')' | 106 | '[' [testlist_comp] ']' | 107 | '{' [dictorsetmaker] '}' | 108 | NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False') 109 | testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] ) 110 | trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME 111 | subscriptlist: subscript (',' subscript)* [','] 112 | subscript: test | [test] ':' [test] [sliceop] 113 | sliceop: ':' [test] 114 | exprlist: (expr|star_expr) (',' (expr|star_expr))* [','] 115 | testlist: test (',' test)* [','] 116 | dictorsetmaker: ( (test ':' test (comp_for | (',' test ':' test)* [','])) | 117 | (test (comp_for | (',' test)* [','])) ) 118 | 119 | classdef: 'class' NAME ['(' [arglist] ')'] ':' suite 120 | 121 | arglist: (argument ',')* (argument [','] 122 | |'*' test (',' argument)* [',' '**' test] 123 | |'**' test) 124 | # The reason that keywords are test nodes instead of NAME is that using NAME 125 | # results in an ambiguity. ast.c makes sure it's a NAME. 126 | argument: test [comp_for] | test '=' test # Really [keyword '='] test 127 | comp_iter: comp_for | comp_if 128 | comp_for: 'for' exprlist 'in' or_test [comp_iter] 129 | comp_if: 'if' test_nocond [comp_iter] 130 | 131 | # not used in grammar, but may appear in "node" passed from Parser to Compiler 132 | encoding_decl: NAME 133 | 134 | yield_expr: 'yield' [yield_arg] 135 | yield_arg: 'from' test | testlist 136 | -------------------------------------------------------------------------------- /lib/jedi/parser/grammar3.5.txt: -------------------------------------------------------------------------------- 1 | # Grammar for Python 2 | 3 | # Note: Changing the grammar specified in this file will most likely 4 | # require corresponding changes in the parser module 5 | # (../Modules/parsermodule.c). If you can't make the changes to 6 | # that module yourself, please co-ordinate the required changes 7 | # with someone who can; ask around on python-dev for help. Fred 8 | # Drake will probably be listening there. 9 | 10 | # NOTE WELL: You should also follow all the steps listed at 11 | # https://docs.python.org/devguide/grammar.html 12 | 13 | # Start symbols for the grammar: 14 | # single_input is a single interactive statement; 15 | # file_input is a module or sequence of commands read from an input file; 16 | # eval_input is the input for the eval() functions. 17 | # NB: compound_stmt in single_input is followed by extra NEWLINE! 18 | file_input: (NEWLINE | stmt)* ENDMARKER 19 | single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE 20 | eval_input: testlist NEWLINE* ENDMARKER 21 | 22 | decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE 23 | decorators: decorator+ 24 | decorated: decorators (classdef | funcdef | async_funcdef) 25 | 26 | # NOTE: Reinoud Elhorst, using ASYNC/AWAIT keywords instead of tokens 27 | # skipping python3.5 compatibility, in favour of 3.7 solution 28 | async_funcdef: 'async' funcdef 29 | funcdef: 'def' NAME parameters ['->' test] ':' suite 30 | 31 | parameters: '(' [typedargslist] ')' 32 | typedargslist: (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' 33 | ['*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] | '**' tfpdef]] 34 | | '*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] | '**' tfpdef) 35 | tfpdef: NAME [':' test] 36 | varargslist: (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' 37 | ['*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef]] 38 | | '*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef) 39 | vfpdef: NAME 40 | 41 | stmt: simple_stmt | compound_stmt 42 | simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE 43 | small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt | 44 | import_stmt | global_stmt | nonlocal_stmt | assert_stmt) 45 | expr_stmt: testlist_star_expr (augassign (yield_expr|testlist) | 46 | ('=' (yield_expr|testlist_star_expr))*) 47 | testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [','] 48 | augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | 49 | '<<=' | '>>=' | '**=' | '//=') 50 | # For normal assignments, additional restrictions enforced by the interpreter 51 | del_stmt: 'del' exprlist 52 | pass_stmt: 'pass' 53 | flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt 54 | break_stmt: 'break' 55 | continue_stmt: 'continue' 56 | return_stmt: 'return' [testlist] 57 | yield_stmt: yield_expr 58 | raise_stmt: 'raise' [test ['from' test]] 59 | import_stmt: import_name | import_from 60 | import_name: 'import' dotted_as_names 61 | # note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS 62 | import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+) 63 | 'import' ('*' | '(' import_as_names ')' | import_as_names)) 64 | import_as_name: NAME ['as' NAME] 65 | dotted_as_name: dotted_name ['as' NAME] 66 | import_as_names: import_as_name (',' import_as_name)* [','] 67 | dotted_as_names: dotted_as_name (',' dotted_as_name)* 68 | dotted_name: NAME ('.' NAME)* 69 | global_stmt: 'global' NAME (',' NAME)* 70 | nonlocal_stmt: 'nonlocal' NAME (',' NAME)* 71 | assert_stmt: 'assert' test [',' test] 72 | 73 | compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt 74 | async_stmt: 'async' (funcdef | with_stmt | for_stmt) 75 | if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] 76 | while_stmt: 'while' test ':' suite ['else' ':' suite] 77 | for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] 78 | try_stmt: ('try' ':' suite 79 | ((except_clause ':' suite)+ 80 | ['else' ':' suite] 81 | ['finally' ':' suite] | 82 | 'finally' ':' suite)) 83 | with_stmt: 'with' with_item (',' with_item)* ':' suite 84 | with_item: test ['as' expr] 85 | # NB compile.c makes sure that the default except clause is last 86 | except_clause: 'except' [test ['as' NAME]] 87 | # Edit by David Halter: The stmt is now optional. This reflects how Jedi allows 88 | # classes and functions to be empty, which is beneficial for autocompletion. 89 | suite: simple_stmt | NEWLINE INDENT stmt* DEDENT 90 | 91 | test: or_test ['if' or_test 'else' test] | lambdef 92 | test_nocond: or_test | lambdef_nocond 93 | lambdef: 'lambda' [varargslist] ':' test 94 | lambdef_nocond: 'lambda' [varargslist] ':' test_nocond 95 | or_test: and_test ('or' and_test)* 96 | and_test: not_test ('and' not_test)* 97 | not_test: 'not' not_test | comparison 98 | comparison: expr (comp_op expr)* 99 | # <> isn't actually a valid comparison operator in Python. It's here for the 100 | # sake of a __future__ import described in PEP 401 (which really works :-) 101 | comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' 102 | star_expr: '*' expr 103 | expr: xor_expr ('|' xor_expr)* 104 | xor_expr: and_expr ('^' and_expr)* 105 | and_expr: shift_expr ('&' shift_expr)* 106 | shift_expr: arith_expr (('<<'|'>>') arith_expr)* 107 | arith_expr: term (('+'|'-') term)* 108 | term: factor (('*'|'@'|'/'|'%'|'//') factor)* 109 | factor: ('+'|'-'|'~') factor | power 110 | power: atom_expr ['**' factor] 111 | atom_expr: ['await'] atom trailer* 112 | atom: ('(' [yield_expr|testlist_comp] ')' | 113 | '[' [testlist_comp] ']' | 114 | '{' [dictorsetmaker] '}' | 115 | NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False') 116 | testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] ) 117 | trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME 118 | subscriptlist: subscript (',' subscript)* [','] 119 | subscript: test | [test] ':' [test] [sliceop] 120 | sliceop: ':' [test] 121 | exprlist: (expr|star_expr) (',' (expr|star_expr))* [','] 122 | testlist: test (',' test)* [','] 123 | dictorsetmaker: ( ((test ':' test | '**' expr) 124 | (comp_for | (',' (test ':' test | '**' expr))* [','])) | 125 | ((test | star_expr) 126 | (comp_for | (',' (test | star_expr))* [','])) ) 127 | 128 | classdef: 'class' NAME ['(' [arglist] ')'] ':' suite 129 | 130 | arglist: argument (',' argument)* [','] 131 | 132 | # The reason that keywords are test nodes instead of NAME is that using NAME 133 | # results in an ambiguity. ast.c makes sure it's a NAME. 134 | # "test '=' test" is really "keyword '=' test", but we have no such token. 135 | # These need to be in a single rule to avoid grammar that is ambiguous 136 | # to our LL(1) parser. Even though 'test' includes '*expr' in star_expr, 137 | # we explicitly match '*' here, too, to give it proper precedence. 138 | # Illegal combinations and orderings are blocked in ast.c: 139 | # multiple (test comp_for) arguements are blocked; keyword unpackings 140 | # that precede iterable unpackings are blocked; etc. 141 | argument: ( test [comp_for] | 142 | test '=' test | 143 | '**' test | 144 | '*' test ) 145 | 146 | comp_iter: comp_for | comp_if 147 | comp_for: 'for' exprlist 'in' or_test [comp_iter] 148 | comp_if: 'if' test_nocond [comp_iter] 149 | 150 | # not used in grammar, but may appear in "node" passed from Parser to Compiler 151 | encoding_decl: NAME 152 | 153 | yield_expr: 'yield' [yield_arg] 154 | yield_arg: 'from' test | testlist 155 | -------------------------------------------------------------------------------- /lib/jedi/parser/grammar3.6.txt: -------------------------------------------------------------------------------- 1 | # Grammar for Python 2 | 3 | # Note: Changing the grammar specified in this file will most likely 4 | # require corresponding changes in the parser module 5 | # (../Modules/parsermodule.c). If you can't make the changes to 6 | # that module yourself, please co-ordinate the required changes 7 | # with someone who can; ask around on python-dev for help. Fred 8 | # Drake will probably be listening there. 9 | 10 | # NOTE WELL: You should also follow all the steps listed at 11 | # https://docs.python.org/devguide/grammar.html 12 | 13 | # Start symbols for the grammar: 14 | # file_input is a module or sequence of commands read from an input file; 15 | # single_input is a single interactive statement; 16 | # eval_input is the input for the eval() functions. 17 | # NB: compound_stmt in single_input is followed by extra NEWLINE! 18 | file_input: (NEWLINE | stmt)* ENDMARKER 19 | single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE 20 | eval_input: testlist NEWLINE* ENDMARKER 21 | 22 | decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE 23 | decorators: decorator+ 24 | decorated: decorators (classdef | funcdef | async_funcdef) 25 | 26 | # NOTE: Francisco Souza/Reinoud Elhorst, using ASYNC/'await' keywords instead of 27 | # skipping python3.5+ compatibility, in favour of 3.7 solution 28 | async_funcdef: 'async' funcdef 29 | funcdef: 'def' NAME parameters ['->' test] ':' suite 30 | 31 | parameters: '(' [typedargslist] ')' 32 | typedargslist: (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [ 33 | '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] 34 | | '**' tfpdef [',']]] 35 | | '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] 36 | | '**' tfpdef [',']) 37 | tfpdef: NAME [':' test] 38 | varargslist: (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [ 39 | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] 40 | | '**' vfpdef [',']]] 41 | | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] 42 | | '**' vfpdef [','] 43 | ) 44 | vfpdef: NAME 45 | 46 | stmt: simple_stmt | compound_stmt 47 | simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE 48 | small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt | 49 | import_stmt | global_stmt | nonlocal_stmt | assert_stmt) 50 | expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | 51 | ('=' (yield_expr|testlist_star_expr))*) 52 | annassign: ':' test ['=' test] 53 | testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [','] 54 | augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | 55 | '<<=' | '>>=' | '**=' | '//=') 56 | # For normal and annotated assignments, additional restrictions enforced by the interpreter 57 | del_stmt: 'del' exprlist 58 | pass_stmt: 'pass' 59 | flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt 60 | break_stmt: 'break' 61 | continue_stmt: 'continue' 62 | return_stmt: 'return' [testlist] 63 | yield_stmt: yield_expr 64 | raise_stmt: 'raise' [test ['from' test]] 65 | import_stmt: import_name | import_from 66 | import_name: 'import' dotted_as_names 67 | # note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS 68 | import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+) 69 | 'import' ('*' | '(' import_as_names ')' | import_as_names)) 70 | import_as_name: NAME ['as' NAME] 71 | dotted_as_name: dotted_name ['as' NAME] 72 | import_as_names: import_as_name (',' import_as_name)* [','] 73 | dotted_as_names: dotted_as_name (',' dotted_as_name)* 74 | dotted_name: NAME ('.' NAME)* 75 | global_stmt: 'global' NAME (',' NAME)* 76 | nonlocal_stmt: 'nonlocal' NAME (',' NAME)* 77 | assert_stmt: 'assert' test [',' test] 78 | 79 | compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt 80 | async_stmt: 'async' (funcdef | with_stmt | for_stmt) 81 | if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] 82 | while_stmt: 'while' test ':' suite ['else' ':' suite] 83 | for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] 84 | try_stmt: ('try' ':' suite 85 | ((except_clause ':' suite)+ 86 | ['else' ':' suite] 87 | ['finally' ':' suite] | 88 | 'finally' ':' suite)) 89 | with_stmt: 'with' with_item (',' with_item)* ':' suite 90 | with_item: test ['as' expr] 91 | # NB compile.c makes sure that the default except clause is last 92 | except_clause: 'except' [test ['as' NAME]] 93 | # Edit by Francisco Souza/David Halter: The stmt is now optional. This reflects 94 | # how Jedi allows classes and functions to be empty, which is beneficial for 95 | # autocompletion. 96 | suite: simple_stmt | NEWLINE INDENT stmt* DEDENT 97 | 98 | test: or_test ['if' or_test 'else' test] | lambdef 99 | test_nocond: or_test | lambdef_nocond 100 | lambdef: 'lambda' [varargslist] ':' test 101 | lambdef_nocond: 'lambda' [varargslist] ':' test_nocond 102 | or_test: and_test ('or' and_test)* 103 | and_test: not_test ('and' not_test)* 104 | not_test: 'not' not_test | comparison 105 | comparison: expr (comp_op expr)* 106 | # <> isn't actually a valid comparison operator in Python. It's here for the 107 | # sake of a __future__ import described in PEP 401 (which really works :-) 108 | comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' 109 | star_expr: '*' expr 110 | expr: xor_expr ('|' xor_expr)* 111 | xor_expr: and_expr ('^' and_expr)* 112 | and_expr: shift_expr ('&' shift_expr)* 113 | shift_expr: arith_expr (('<<'|'>>') arith_expr)* 114 | arith_expr: term (('+'|'-') term)* 115 | term: factor (('*'|'@'|'/'|'%'|'//') factor)* 116 | factor: ('+'|'-'|'~') factor | power 117 | power: atom_expr ['**' factor] 118 | atom_expr: ['await'] atom trailer* 119 | atom: ('(' [yield_expr|testlist_comp] ')' | 120 | '[' [testlist_comp] ']' | 121 | '{' [dictorsetmaker] '}' | 122 | NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False') 123 | testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] ) 124 | trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME 125 | subscriptlist: subscript (',' subscript)* [','] 126 | subscript: test | [test] ':' [test] [sliceop] 127 | sliceop: ':' [test] 128 | exprlist: (expr|star_expr) (',' (expr|star_expr))* [','] 129 | testlist: test (',' test)* [','] 130 | dictorsetmaker: ( ((test ':' test | '**' expr) 131 | (comp_for | (',' (test ':' test | '**' expr))* [','])) | 132 | ((test | star_expr) 133 | (comp_for | (',' (test | star_expr))* [','])) ) 134 | 135 | classdef: 'class' NAME ['(' [arglist] ')'] ':' suite 136 | 137 | arglist: argument (',' argument)* [','] 138 | 139 | # The reason that keywords are test nodes instead of NAME is that using NAME 140 | # results in an ambiguity. ast.c makes sure it's a NAME. 141 | # "test '=' test" is really "keyword '=' test", but we have no such token. 142 | # These need to be in a single rule to avoid grammar that is ambiguous 143 | # to our LL(1) parser. Even though 'test' includes '*expr' in star_expr, 144 | # we explicitly match '*' here, too, to give it proper precedence. 145 | # Illegal combinations and orderings are blocked in ast.c: 146 | # multiple (test comp_for) arguments are blocked; keyword unpackings 147 | # that precede iterable unpackings are blocked; etc. 148 | argument: ( test [comp_for] | 149 | test '=' test | 150 | '**' test | 151 | '*' test ) 152 | 153 | comp_iter: comp_for | comp_if 154 | comp_for: ['async'] 'for' exprlist 'in' or_test [comp_iter] 155 | comp_if: 'if' test_nocond [comp_iter] 156 | 157 | # not used in grammar, but may appear in "node" passed from Parser to Compiler 158 | encoding_decl: NAME 159 | 160 | yield_expr: 'yield' [yield_arg] 161 | yield_arg: 'from' test | testlist 162 | -------------------------------------------------------------------------------- /lib/jedi/parser/pgen2/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved. 2 | # Licensed to PSF under a Contributor Agreement. 3 | 4 | # Modifications: 5 | # Copyright 2006 Google, Inc. All Rights Reserved. 6 | # Licensed to PSF under a Contributor Agreement. 7 | # Copyright 2014 David Halter. Integration into Jedi. 8 | # Modifications are dual-licensed: MIT and PSF. 9 | -------------------------------------------------------------------------------- /lib/jedi/parser/pgen2/grammar.py: -------------------------------------------------------------------------------- 1 | # Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved. 2 | # Licensed to PSF under a Contributor Agreement. 3 | 4 | # Modifications: 5 | # Copyright 2014 David Halter. Integration into Jedi. 6 | # Modifications are dual-licensed: MIT and PSF. 7 | 8 | """This module defines the data structures used to represent a grammar. 9 | 10 | These are a bit arcane because they are derived from the data 11 | structures used by Python's 'pgen' parser generator. 12 | 13 | There's also a table here mapping operators to their names in the 14 | token module; the Python tokenize module reports all operators as the 15 | fallback token code OP, but the parser needs the actual token code. 16 | 17 | """ 18 | 19 | # Python imports 20 | import pickle 21 | 22 | 23 | class Grammar(object): 24 | """Pgen parsing tables conversion class. 25 | 26 | Once initialized, this class supplies the grammar tables for the 27 | parsing engine implemented by parse.py. The parsing engine 28 | accesses the instance variables directly. The class here does not 29 | provide initialization of the tables; several subclasses exist to 30 | do this (see the conv and pgen modules). 31 | 32 | The load() method reads the tables from a pickle file, which is 33 | much faster than the other ways offered by subclasses. The pickle 34 | file is written by calling dump() (after loading the grammar 35 | tables using a subclass). The report() method prints a readable 36 | representation of the tables to stdout, for debugging. 37 | 38 | The instance variables are as follows: 39 | 40 | symbol2number -- a dict mapping symbol names to numbers. Symbol 41 | numbers are always 256 or higher, to distinguish 42 | them from token numbers, which are between 0 and 43 | 255 (inclusive). 44 | 45 | number2symbol -- a dict mapping numbers to symbol names; 46 | these two are each other's inverse. 47 | 48 | states -- a list of DFAs, where each DFA is a list of 49 | states, each state is a list of arcs, and each 50 | arc is a (i, j) pair where i is a label and j is 51 | a state number. The DFA number is the index into 52 | this list. (This name is slightly confusing.) 53 | Final states are represented by a special arc of 54 | the form (0, j) where j is its own state number. 55 | 56 | dfas -- a dict mapping symbol numbers to (DFA, first) 57 | pairs, where DFA is an item from the states list 58 | above, and first is a set of tokens that can 59 | begin this grammar rule (represented by a dict 60 | whose values are always 1). 61 | 62 | labels -- a list of (x, y) pairs where x is either a token 63 | number or a symbol number, and y is either None 64 | or a string; the strings are keywords. The label 65 | number is the index in this list; label numbers 66 | are used to mark state transitions (arcs) in the 67 | DFAs. 68 | 69 | start -- the number of the grammar's start symbol. 70 | 71 | keywords -- a dict mapping keyword strings to arc labels. 72 | 73 | tokens -- a dict mapping token numbers to arc labels. 74 | 75 | """ 76 | 77 | def __init__(self): 78 | self.symbol2number = {} 79 | self.number2symbol = {} 80 | self.states = [] 81 | self.dfas = {} 82 | self.labels = [(0, "EMPTY")] 83 | self.keywords = {} 84 | self.tokens = {} 85 | self.symbol2label = {} 86 | self.start = 256 87 | 88 | def dump(self, filename): 89 | """Dump the grammar tables to a pickle file.""" 90 | with open(filename, "wb") as f: 91 | pickle.dump(self.__dict__, f, 2) 92 | 93 | def load(self, filename): 94 | """Load the grammar tables from a pickle file.""" 95 | with open(filename, "rb") as f: 96 | d = pickle.load(f) 97 | self.__dict__.update(d) 98 | 99 | def copy(self): 100 | """ 101 | Copy the grammar. 102 | """ 103 | new = self.__class__() 104 | for dict_attr in ("symbol2number", "number2symbol", "dfas", "keywords", 105 | "tokens", "symbol2label"): 106 | setattr(new, dict_attr, getattr(self, dict_attr).copy()) 107 | new.labels = self.labels[:] 108 | new.states = self.states[:] 109 | new.start = self.start 110 | return new 111 | 112 | def report(self): 113 | """Dump the grammar tables to standard output, for debugging.""" 114 | from pprint import pprint 115 | print("s2n") 116 | pprint(self.symbol2number) 117 | print("n2s") 118 | pprint(self.number2symbol) 119 | print("states") 120 | pprint(self.states) 121 | print("dfas") 122 | pprint(self.dfas) 123 | print("labels") 124 | pprint(self.labels) 125 | print("start", self.start) 126 | -------------------------------------------------------------------------------- /lib/jedi/parser/token.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | from jedi._compatibility import is_py3, is_py35 4 | from token import * 5 | 6 | 7 | COMMENT = N_TOKENS 8 | tok_name[COMMENT] = 'COMMENT' 9 | N_TOKENS += 1 10 | 11 | NL = N_TOKENS 12 | tok_name[NL] = 'NL' 13 | N_TOKENS += 1 14 | 15 | if is_py3: 16 | BACKQUOTE = N_TOKENS 17 | tok_name[BACKQUOTE] = 'BACKQUOTE' 18 | N_TOKENS += 1 19 | else: 20 | RARROW = N_TOKENS 21 | tok_name[RARROW] = 'RARROW' 22 | N_TOKENS += 1 23 | ELLIPSIS = N_TOKENS 24 | tok_name[ELLIPSIS] = 'ELLIPSIS' 25 | N_TOKENS += 1 26 | 27 | if not is_py35: 28 | ATEQUAL = N_TOKENS 29 | tok_name[ATEQUAL] = 'ATEQUAL' 30 | N_TOKENS += 1 31 | 32 | 33 | 34 | # Map from operator to number (since tokenize doesn't do this) 35 | 36 | opmap_raw = """\ 37 | ( LPAR 38 | ) RPAR 39 | [ LSQB 40 | ] RSQB 41 | : COLON 42 | , COMMA 43 | ; SEMI 44 | + PLUS 45 | - MINUS 46 | * STAR 47 | / SLASH 48 | | VBAR 49 | & AMPER 50 | < LESS 51 | > GREATER 52 | = EQUAL 53 | . DOT 54 | % PERCENT 55 | ` BACKQUOTE 56 | { LBRACE 57 | } RBRACE 58 | @ AT 59 | == EQEQUAL 60 | != NOTEQUAL 61 | <> NOTEQUAL 62 | <= LESSEQUAL 63 | >= GREATEREQUAL 64 | ~ TILDE 65 | ^ CIRCUMFLEX 66 | << LEFTSHIFT 67 | >> RIGHTSHIFT 68 | ** DOUBLESTAR 69 | += PLUSEQUAL 70 | -= MINEQUAL 71 | *= STAREQUAL 72 | /= SLASHEQUAL 73 | %= PERCENTEQUAL 74 | &= AMPEREQUAL 75 | |= VBAREQUAL 76 | @= ATEQUAL 77 | ^= CIRCUMFLEXEQUAL 78 | <<= LEFTSHIFTEQUAL 79 | >>= RIGHTSHIFTEQUAL 80 | **= DOUBLESTAREQUAL 81 | // DOUBLESLASH 82 | //= DOUBLESLASHEQUAL 83 | -> RARROW 84 | ... ELLIPSIS 85 | """ 86 | 87 | opmap = {} 88 | for line in opmap_raw.splitlines(): 89 | op, name = line.split() 90 | opmap[op] = globals()[name] 91 | -------------------------------------------------------------------------------- /lib/jedi/parser/utils.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | import time 3 | import os 4 | import sys 5 | import json 6 | import hashlib 7 | import gc 8 | import shutil 9 | import pickle 10 | 11 | from jedi import settings 12 | from jedi import debug 13 | 14 | 15 | def underscore_memoization(func): 16 | """ 17 | Decorator for methods:: 18 | 19 | class A(object): 20 | def x(self): 21 | if self._x: 22 | self._x = 10 23 | return self._x 24 | 25 | Becomes:: 26 | 27 | class A(object): 28 | @underscore_memoization 29 | def x(self): 30 | return 10 31 | 32 | A now has an attribute ``_x`` written by this decorator. 33 | """ 34 | name = '_' + func.__name__ 35 | 36 | def wrapper(self): 37 | try: 38 | return getattr(self, name) 39 | except AttributeError: 40 | result = func(self) 41 | if inspect.isgenerator(result): 42 | result = list(result) 43 | setattr(self, name, result) 44 | return result 45 | 46 | return wrapper 47 | 48 | 49 | # for fast_parser, should not be deleted 50 | parser_cache = {} 51 | 52 | 53 | class ParserCacheItem(object): 54 | def __init__(self, parser, change_time=None): 55 | self.parser = parser 56 | if change_time is None: 57 | change_time = time.time() 58 | self.change_time = change_time 59 | 60 | 61 | def load_parser(path): 62 | """ 63 | Returns the module or None, if it fails. 64 | """ 65 | p_time = os.path.getmtime(path) if path else None 66 | try: 67 | parser_cache_item = parser_cache[path] 68 | if not path or p_time <= parser_cache_item.change_time: 69 | return parser_cache_item.parser 70 | except KeyError: 71 | if settings.use_filesystem_cache: 72 | return ParserPickling.load_parser(path, p_time) 73 | 74 | 75 | def save_parser(path, parser, pickling=True): 76 | try: 77 | p_time = None if path is None else os.path.getmtime(path) 78 | except OSError: 79 | p_time = None 80 | pickling = False 81 | 82 | item = ParserCacheItem(parser, p_time) 83 | parser_cache[path] = item 84 | if settings.use_filesystem_cache and pickling: 85 | ParserPickling.save_parser(path, item) 86 | 87 | 88 | class ParserPickling(object): 89 | 90 | version = 26 91 | """ 92 | Version number (integer) for file system cache. 93 | 94 | Increment this number when there are any incompatible changes in 95 | parser representation classes. For example, the following changes 96 | are regarded as incompatible. 97 | 98 | - Class name is changed. 99 | - Class is moved to another module. 100 | - Defined slot of the class is changed. 101 | """ 102 | 103 | def __init__(self): 104 | self.__index = None 105 | self.py_tag = 'cpython-%s%s' % sys.version_info[:2] 106 | """ 107 | Short name for distinguish Python implementations and versions. 108 | 109 | It's like `sys.implementation.cache_tag` but for Python < 3.3 110 | we generate something similar. See: 111 | http://docs.python.org/3/library/sys.html#sys.implementation 112 | 113 | .. todo:: Detect interpreter (e.g., PyPy). 114 | """ 115 | 116 | def load_parser(self, path, original_changed_time): 117 | try: 118 | pickle_changed_time = self._index[path] 119 | except KeyError: 120 | return None 121 | if original_changed_time is not None \ 122 | and pickle_changed_time < original_changed_time: 123 | # the pickle file is outdated 124 | return None 125 | 126 | with open(self._get_hashed_path(path), 'rb') as f: 127 | try: 128 | gc.disable() 129 | parser_cache_item = pickle.load(f) 130 | finally: 131 | gc.enable() 132 | 133 | debug.dbg('pickle loaded: %s', path) 134 | parser_cache[path] = parser_cache_item 135 | return parser_cache_item.parser 136 | 137 | def save_parser(self, path, parser_cache_item): 138 | self.__index = None 139 | try: 140 | files = self._index 141 | except KeyError: 142 | files = {} 143 | self._index = files 144 | 145 | with open(self._get_hashed_path(path), 'wb') as f: 146 | pickle.dump(parser_cache_item, f, pickle.HIGHEST_PROTOCOL) 147 | files[path] = parser_cache_item.change_time 148 | 149 | self._flush_index() 150 | 151 | @property 152 | def _index(self): 153 | if self.__index is None: 154 | try: 155 | with open(self._get_path('index.json')) as f: 156 | data = json.load(f) 157 | except (IOError, ValueError): 158 | self.__index = {} 159 | else: 160 | # 0 means version is not defined (= always delete cache): 161 | if data.get('version', 0) != self.version: 162 | self.clear_cache() 163 | else: 164 | self.__index = data['index'] 165 | return self.__index 166 | 167 | def _remove_old_modules(self): 168 | # TODO use 169 | change = False 170 | if change: 171 | self._flush_index(self) 172 | self._index # reload index 173 | 174 | def _flush_index(self): 175 | data = {'version': self.version, 'index': self._index} 176 | with open(self._get_path('index.json'), 'w') as f: 177 | json.dump(data, f) 178 | self.__index = None 179 | 180 | def clear_cache(self): 181 | shutil.rmtree(self._cache_directory()) 182 | self.__index = {} 183 | 184 | def _get_hashed_path(self, path): 185 | return self._get_path('%s.pkl' % hashlib.md5(path.encode("utf-8")).hexdigest()) 186 | 187 | def _get_path(self, file): 188 | dir = self._cache_directory() 189 | if not os.path.exists(dir): 190 | os.makedirs(dir) 191 | return os.path.join(dir, file) 192 | 193 | def _cache_directory(self): 194 | return os.path.join(settings.cache_directory, self.py_tag) 195 | 196 | 197 | # is a singleton 198 | ParserPickling = ParserPickling() 199 | -------------------------------------------------------------------------------- /lib/jedi/refactoring.py: -------------------------------------------------------------------------------- 1 | """ 2 | Introduce some basic refactoring functions to |jedi|. This module is still in a 3 | very early development stage and needs much testing and improvement. 4 | 5 | .. warning:: I won't do too much here, but if anyone wants to step in, please 6 | do. Refactoring is none of my priorities 7 | 8 | It uses the |jedi| `API `_ and supports currently the 9 | following functions (sometimes bug-prone): 10 | 11 | - rename 12 | - extract variable 13 | - inline variable 14 | """ 15 | import difflib 16 | 17 | from jedi import common 18 | from jedi.evaluate import helpers 19 | from jedi.parser import tree as pt 20 | 21 | 22 | class Refactoring(object): 23 | def __init__(self, change_dct): 24 | """ 25 | :param change_dct: dict(old_path=(new_path, old_lines, new_lines)) 26 | """ 27 | self.change_dct = change_dct 28 | 29 | def old_files(self): 30 | dct = {} 31 | for old_path, (new_path, old_l, new_l) in self.change_dct.items(): 32 | dct[new_path] = '\n'.join(new_l) 33 | return dct 34 | 35 | def new_files(self): 36 | dct = {} 37 | for old_path, (new_path, old_l, new_l) in self.change_dct.items(): 38 | dct[new_path] = '\n'.join(new_l) 39 | return dct 40 | 41 | def diff(self): 42 | texts = [] 43 | for old_path, (new_path, old_l, new_l) in self.change_dct.items(): 44 | if old_path: 45 | udiff = difflib.unified_diff(old_l, new_l) 46 | else: 47 | udiff = difflib.unified_diff(old_l, new_l, old_path, new_path) 48 | texts.append('\n'.join(udiff)) 49 | return '\n'.join(texts) 50 | 51 | 52 | def rename(script, new_name): 53 | """ The `args` / `kwargs` params are the same as in `api.Script`. 54 | :param operation: The refactoring operation to execute. 55 | :type operation: str 56 | :type source: str 57 | :return: list of changed lines/changed files 58 | """ 59 | return Refactoring(_rename(script.usages(), new_name)) 60 | 61 | 62 | def _rename(names, replace_str): 63 | """ For both rename and inline. """ 64 | order = sorted(names, key=lambda x: (x.module_path, x.line, x.column), 65 | reverse=True) 66 | 67 | def process(path, old_lines, new_lines): 68 | if new_lines is not None: # goto next file, save last 69 | dct[path] = path, old_lines, new_lines 70 | 71 | dct = {} 72 | current_path = object() 73 | new_lines = old_lines = None 74 | for name in order: 75 | if name.in_builtin_module(): 76 | continue 77 | if current_path != name.module_path: 78 | current_path = name.module_path 79 | 80 | process(current_path, old_lines, new_lines) 81 | if current_path is not None: 82 | # None means take the source that is a normal param. 83 | with open(current_path) as f: 84 | source = f.read() 85 | 86 | new_lines = common.splitlines(common.source_to_unicode(source)) 87 | old_lines = new_lines[:] 88 | 89 | nr, indent = name.line, name.column 90 | line = new_lines[nr - 1] 91 | new_lines[nr - 1] = line[:indent] + replace_str + \ 92 | line[indent + len(name.name):] 93 | process(current_path, old_lines, new_lines) 94 | return dct 95 | 96 | 97 | def extract(script, new_name): 98 | """ The `args` / `kwargs` params are the same as in `api.Script`. 99 | :param operation: The refactoring operation to execute. 100 | :type operation: str 101 | :type source: str 102 | :return: list of changed lines/changed files 103 | """ 104 | new_lines = common.splitlines(common.source_to_unicode(script.source)) 105 | old_lines = new_lines[:] 106 | 107 | user_stmt = script._parser.user_stmt() 108 | 109 | # TODO care for multiline extracts 110 | dct = {} 111 | if user_stmt: 112 | pos = script._pos 113 | line_index = pos[0] - 1 114 | arr, index = helpers.array_for_pos(user_stmt, pos) 115 | if arr is not None: 116 | start_pos = arr[index].start_pos 117 | end_pos = arr[index].end_pos 118 | 119 | # take full line if the start line is different from end line 120 | e = end_pos[1] if end_pos[0] == start_pos[0] else None 121 | start_line = new_lines[start_pos[0] - 1] 122 | text = start_line[start_pos[1]:e] 123 | for l in range(start_pos[0], end_pos[0] - 1): 124 | text += '\n' + l 125 | if e is None: 126 | end_line = new_lines[end_pos[0] - 1] 127 | text += '\n' + end_line[:end_pos[1]] 128 | 129 | # remove code from new lines 130 | t = text.lstrip() 131 | del_start = start_pos[1] + len(text) - len(t) 132 | 133 | text = t.rstrip() 134 | del_end = len(t) - len(text) 135 | if e is None: 136 | new_lines[end_pos[0] - 1] = end_line[end_pos[1] - del_end:] 137 | e = len(start_line) 138 | else: 139 | e = e - del_end 140 | start_line = start_line[:del_start] + new_name + start_line[e:] 141 | new_lines[start_pos[0] - 1] = start_line 142 | new_lines[start_pos[0]:end_pos[0] - 1] = [] 143 | 144 | # add parentheses in multiline case 145 | open_brackets = ['(', '[', '{'] 146 | close_brackets = [')', ']', '}'] 147 | if '\n' in text and not (text[0] in open_brackets and text[-1] == 148 | close_brackets[open_brackets.index(text[0])]): 149 | text = '(%s)' % text 150 | 151 | # add new line before statement 152 | indent = user_stmt.start_pos[1] 153 | new = "%s%s = %s" % (' ' * indent, new_name, text) 154 | new_lines.insert(line_index, new) 155 | dct[script.path] = script.path, old_lines, new_lines 156 | return Refactoring(dct) 157 | 158 | 159 | def inline(script): 160 | """ 161 | :type script: api.Script 162 | """ 163 | new_lines = common.splitlines(common.source_to_unicode(script.source)) 164 | 165 | dct = {} 166 | 167 | definitions = script.goto_assignments() 168 | with common.ignored(AssertionError): 169 | assert len(definitions) == 1 170 | stmt = definitions[0]._definition 171 | usages = script.usages() 172 | inlines = [r for r in usages 173 | if not stmt.start_pos <= (r.line, r.column) <= stmt.end_pos] 174 | inlines = sorted(inlines, key=lambda x: (x.module_path, x.line, x.column), 175 | reverse=True) 176 | expression_list = stmt.expression_list() 177 | # don't allow multiline refactorings for now. 178 | assert stmt.start_pos[0] == stmt.end_pos[0] 179 | index = stmt.start_pos[0] - 1 180 | 181 | line = new_lines[index] 182 | replace_str = line[expression_list[0].start_pos[1]:stmt.end_pos[1] + 1] 183 | replace_str = replace_str.strip() 184 | # tuples need parentheses 185 | if expression_list and isinstance(expression_list[0], pr.Array): 186 | arr = expression_list[0] 187 | if replace_str[0] not in ['(', '[', '{'] and len(arr) > 1: 188 | replace_str = '(%s)' % replace_str 189 | 190 | # if it's the only assignment, remove the statement 191 | if len(stmt.get_defined_names()) == 1: 192 | line = line[:stmt.start_pos[1]] + line[stmt.end_pos[1]:] 193 | 194 | dct = _rename(inlines, replace_str) 195 | # remove the empty line 196 | new_lines = dct[script.path][2] 197 | if line.strip(): 198 | new_lines[index] = line 199 | else: 200 | new_lines.pop(index) 201 | 202 | return Refactoring(dct) 203 | -------------------------------------------------------------------------------- /lib/jedi/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module contains variables with global |jedi| settings. To change the 3 | behavior of |jedi|, change the variables defined in :mod:`jedi.settings`. 4 | 5 | Plugins should expose an interface so that the user can adjust the 6 | configuration. 7 | 8 | 9 | Example usage:: 10 | 11 | from jedi import settings 12 | settings.case_insensitive_completion = True 13 | 14 | 15 | Completion output 16 | ~~~~~~~~~~~~~~~~~ 17 | 18 | .. autodata:: case_insensitive_completion 19 | .. autodata:: add_dot_after_module 20 | .. autodata:: add_bracket_after_function 21 | .. autodata:: no_completion_duplicates 22 | 23 | 24 | Filesystem cache 25 | ~~~~~~~~~~~~~~~~ 26 | 27 | .. autodata:: cache_directory 28 | .. autodata:: use_filesystem_cache 29 | 30 | 31 | Parser 32 | ~~~~~~ 33 | 34 | .. autodata:: fast_parser 35 | 36 | 37 | Dynamic stuff 38 | ~~~~~~~~~~~~~ 39 | 40 | .. autodata:: dynamic_array_additions 41 | .. autodata:: dynamic_params 42 | .. autodata:: dynamic_params_for_other_modules 43 | .. autodata:: additional_dynamic_modules 44 | .. autodata:: auto_import_modules 45 | 46 | 47 | .. _settings-recursion: 48 | 49 | Recursions 50 | ~~~~~~~~~~ 51 | 52 | Recursion settings are important if you don't want extremly 53 | recursive python code to go absolutely crazy. First of there is a 54 | global limit :data:`max_executions`. This limit is important, to set 55 | a maximum amount of time, the completion may use. 56 | 57 | The default values are based on experiments while completing the |jedi| library 58 | itself (inception!). But I don't think there's any other Python library that 59 | uses recursion in a similarly extreme way. These settings make the completion 60 | definitely worse in some cases. But a completion should also be fast. 61 | 62 | .. autodata:: max_until_execution_unique 63 | .. autodata:: max_function_recursion_level 64 | .. autodata:: max_executions_without_builtins 65 | .. autodata:: max_executions 66 | .. autodata:: max_dynamic_params_depth 67 | .. autodata:: scale_call_signatures 68 | 69 | 70 | Caching 71 | ~~~~~~~ 72 | 73 | .. autodata:: star_import_cache_validity 74 | .. autodata:: call_signatures_validity 75 | 76 | 77 | """ 78 | import os 79 | import platform 80 | 81 | # ---------------- 82 | # completion output settings 83 | # ---------------- 84 | 85 | case_insensitive_completion = True 86 | """ 87 | The completion is by default case insensitive. 88 | """ 89 | 90 | add_bracket_after_function = False 91 | """ 92 | Adds an opening bracket after a function, because that's normal behaviour. 93 | Removed it again, because in VIM that is not very practical. 94 | """ 95 | 96 | no_completion_duplicates = True 97 | """ 98 | If set, completions with the same name don't appear in the output anymore, 99 | but are in the `same_name_completions` attribute. 100 | """ 101 | 102 | # ---------------- 103 | # Filesystem cache 104 | # ---------------- 105 | 106 | use_filesystem_cache = True 107 | """ 108 | Use filesystem cache to save once parsed files with pickle. 109 | """ 110 | 111 | if platform.system().lower() == 'windows': 112 | _cache_directory = os.path.join(os.getenv('APPDATA') or '~', 'Jedi', 113 | 'Jedi') 114 | elif platform.system().lower() == 'darwin': 115 | _cache_directory = os.path.join('~', 'Library', 'Caches', 'Jedi') 116 | else: 117 | _cache_directory = os.path.join(os.getenv('XDG_CACHE_HOME') or '~/.cache', 118 | 'jedi') 119 | cache_directory = os.path.expanduser(_cache_directory) 120 | """ 121 | The path where all the caches can be found. 122 | 123 | On Linux, this defaults to ``~/.cache/jedi/``, on OS X to 124 | ``~/Library/Caches/Jedi/`` and on Windows to ``%APPDATA%\\Jedi\\Jedi\\``. 125 | On Linux, if environment variable ``$XDG_CACHE_HOME`` is set, 126 | ``$XDG_CACHE_HOME/jedi`` is used instead of the default one. 127 | """ 128 | 129 | # ---------------- 130 | # parser 131 | # ---------------- 132 | 133 | fast_parser = True 134 | """ 135 | Use the fast parser. This means that reparsing is only being done if 136 | something has been changed e.g. to a function. If this happens, only the 137 | function is being reparsed. 138 | """ 139 | 140 | # ---------------- 141 | # dynamic stuff 142 | # ---------------- 143 | 144 | dynamic_array_additions = True 145 | """ 146 | check for `append`, etc. on arrays: [], {}, () as well as list/set calls. 147 | """ 148 | 149 | dynamic_params = True 150 | """ 151 | A dynamic param completion, finds the callees of the function, which define 152 | the params of a function. 153 | """ 154 | 155 | dynamic_params_for_other_modules = True 156 | """ 157 | Do the same for other modules. 158 | """ 159 | 160 | additional_dynamic_modules = [] 161 | """ 162 | Additional modules in which |jedi| checks if statements are to be found. This 163 | is practical for IDEs, that want to administrate their modules themselves. 164 | """ 165 | 166 | dynamic_flow_information = True 167 | """ 168 | Check for `isinstance` and other information to infer a type. 169 | """ 170 | 171 | auto_import_modules = [ 172 | 'hashlib', # setattr 173 | ] 174 | """ 175 | Modules that are not analyzed but imported, although they contain Python code. 176 | This improves autocompletion for libraries that use ``setattr`` or 177 | ``globals()`` modifications a lot. 178 | """ 179 | 180 | # ---------------- 181 | # recursions 182 | # ---------------- 183 | 184 | max_until_execution_unique = 50 185 | """ 186 | This limit is probably the most important one, because if this limit is 187 | exceeded, functions can only be one time executed. So new functions will be 188 | executed, complex recursions with the same functions again and again, are 189 | ignored. 190 | """ 191 | 192 | max_function_recursion_level = 5 193 | """ 194 | `max_function_recursion_level` is more about whether the recursions are 195 | stopped in deepth or in width. The ratio beetween this and 196 | `max_until_execution_unique` is important here. It stops a recursion (after 197 | the number of function calls in the recursion), if it was already used 198 | earlier. 199 | """ 200 | 201 | max_executions_without_builtins = 200 202 | """ 203 | .. todo:: Document this. 204 | """ 205 | 206 | max_executions = 250 207 | """ 208 | A maximum amount of time, the completion may use. 209 | """ 210 | 211 | scale_call_signatures = 0.1 212 | """ 213 | Because call_signatures is normally used on every single key hit, it has 214 | to be faster than a normal completion. This is the factor that is used to 215 | scale `max_executions` and `max_until_execution_unique`: 216 | """ 217 | 218 | # ---------------- 219 | # caching validity (time) 220 | # ---------------- 221 | 222 | star_import_cache_validity = 60.0 223 | """ 224 | In huge packages like numpy, checking all star imports on every completion 225 | might be slow, therefore we do a star import caching, that lasts a certain 226 | time span (in seconds). 227 | """ 228 | 229 | call_signatures_validity = 3.0 230 | """ 231 | Finding function calls might be slow (0.1-0.5s). This is not acceptible for 232 | normal writing. Therefore cache it for a short time. 233 | """ 234 | -------------------------------------------------------------------------------- /lib/jedi/utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utilities for end-users. 3 | """ 4 | 5 | from __future__ import absolute_import 6 | import __main__ 7 | from collections import namedtuple 8 | import logging 9 | import traceback 10 | import re 11 | import os 12 | import sys 13 | 14 | from jedi import Interpreter 15 | from jedi.api.helpers import get_on_completion_name 16 | from jedi import common 17 | 18 | 19 | READLINE_DEBUG = False 20 | 21 | 22 | def setup_readline(namespace_module=__main__): 23 | """ 24 | Install Jedi completer to :mod:`readline`. 25 | 26 | This function setups :mod:`readline` to use Jedi in Python interactive 27 | shell. If you want to use a custom ``PYTHONSTARTUP`` file (typically 28 | ``$HOME/.pythonrc.py``), you can add this piece of code:: 29 | 30 | try: 31 | from jedi.utils import setup_readline 32 | setup_readline() 33 | except ImportError: 34 | # Fallback to the stdlib readline completer if it is installed. 35 | # Taken from http://docs.python.org/2/library/rlcompleter.html 36 | print("Jedi is not installed, falling back to readline") 37 | try: 38 | import readline 39 | import rlcompleter 40 | readline.parse_and_bind("tab: complete") 41 | except ImportError: 42 | print("Readline is not installed either. No tab completion is enabled.") 43 | 44 | This will fallback to the readline completer if Jedi is not installed. 45 | The readline completer will only complete names in the global namespace, 46 | so for example:: 47 | 48 | ran 49 | 50 | will complete to ``range`` 51 | 52 | with both Jedi and readline, but:: 53 | 54 | range(10).cou 55 | 56 | will show complete to ``range(10).count`` only with Jedi. 57 | 58 | You'll also need to add ``export PYTHONSTARTUP=$HOME/.pythonrc.py`` to 59 | your shell profile (usually ``.bash_profile`` or ``.profile`` if you use 60 | bash). 61 | 62 | """ 63 | if READLINE_DEBUG: 64 | logging.basicConfig( 65 | filename='/tmp/jedi.log', 66 | filemode='a', 67 | level=logging.DEBUG 68 | ) 69 | 70 | class JediRL(object): 71 | def complete(self, text, state): 72 | """ 73 | This complete stuff is pretty weird, a generator would make 74 | a lot more sense, but probably due to backwards compatibility 75 | this is still the way how it works. 76 | 77 | The only important part is stuff in the ``state == 0`` flow, 78 | everything else has been copied from the ``rlcompleter`` std. 79 | library module. 80 | """ 81 | if state == 0: 82 | sys.path.insert(0, os.getcwd()) 83 | # Calling python doesn't have a path, so add to sys.path. 84 | try: 85 | logging.debug("Start REPL completion: " + repr(text)) 86 | interpreter = Interpreter(text, [namespace_module.__dict__]) 87 | 88 | lines = common.splitlines(text) 89 | position = (len(lines), len(lines[-1])) 90 | name = get_on_completion_name(interpreter._get_module(), lines, position) 91 | before = text[:len(text) - len(name)] 92 | completions = interpreter.completions() 93 | except: 94 | logging.error("REPL Completion error:\n" + traceback.format_exc()) 95 | raise 96 | finally: 97 | sys.path.pop(0) 98 | 99 | self.matches = [before + c.name_with_symbols for c in completions] 100 | try: 101 | return self.matches[state] 102 | except IndexError: 103 | return None 104 | 105 | try: 106 | import readline 107 | except ImportError: 108 | print("Jedi: Module readline not available.") 109 | else: 110 | readline.set_completer(JediRL().complete) 111 | readline.parse_and_bind("tab: complete") 112 | # jedi itself does the case matching 113 | readline.parse_and_bind("set completion-ignore-case on") 114 | # because it's easier to hit the tab just once 115 | readline.parse_and_bind("set show-all-if-unmodified") 116 | readline.parse_and_bind("set show-all-if-ambiguous on") 117 | # don't repeat all the things written in the readline all the time 118 | readline.parse_and_bind("set completion-prefix-display-length 2") 119 | # No delimiters, Jedi handles that. 120 | readline.set_completer_delims('') 121 | 122 | 123 | def version_info(): 124 | """ 125 | Returns a namedtuple of Jedi's version, similar to Python's 126 | ``sys.version_info``. 127 | """ 128 | Version = namedtuple('Version', 'major, minor, micro') 129 | from jedi import __version__ 130 | tupl = re.findall('[a-z]+|\d+', __version__) 131 | return Version(*[x if i == 3 else int(x) for i, x in enumerate(tupl)]) 132 | -------------------------------------------------------------------------------- /lib/log.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | prefix: 'autocomplete-python-jedi:' 3 | debug: (msg...) -> 4 | if atom.config.get('autocomplete-python-jedi.outputDebug') 5 | return console.debug @prefix, msg... 6 | 7 | warning: (msg...) -> 8 | return console.warn @prefix, msg... 9 | -------------------------------------------------------------------------------- /lib/main.coffee: -------------------------------------------------------------------------------- 1 | os = require 'os' 2 | path = require 'path' 3 | {CompositeDisposable, Emitter} = require 'atom' 4 | 5 | [Metrics, Logger] = [] 6 | 7 | window.DEBUG = false 8 | module.exports = 9 | config: 10 | showDescriptions: 11 | type: 'boolean' 12 | default: true 13 | order: 1 14 | title: 'Show Descriptions' 15 | description: 'Show doc strings from functions, classes, etc.' 16 | useSnippets: 17 | type: 'string' 18 | default: 'none' 19 | order: 2 20 | enum: ['none', 'all', 'required'] 21 | title: 'Autocomplete Function Parameters' 22 | description: '''Automatically complete function arguments after typing 23 | left parenthesis character. Use completion key to jump between 24 | arguments. See `autocomplete-python-jedi:complete-arguments` command if you 25 | want to trigger argument completions manually. See README if it does not 26 | work for you.''' 27 | pythonPaths: 28 | type: 'string' 29 | default: '' 30 | order: 3 31 | title: 'Python Executable Paths' 32 | description: '''Optional semicolon separated list of paths to python 33 | executables (including executable names), where the first one will take 34 | higher priority over the last one. By default autocomplete-python-jedi will 35 | automatically look for virtual environments inside of your project and 36 | try to use them as well as try to find global python executable. If you 37 | use this config, automatic lookup will have lowest priority. 38 | Use `$PROJECT` or `$PROJECT_NAME` substitution for project-specific 39 | paths to point on executables in virtual environments. 40 | For example: 41 | `/Users/name/.virtualenvs/$PROJECT_NAME/bin/python;$PROJECT/venv/bin/python3;/usr/bin/python`. 42 | Such config will fall back on `/usr/bin/python` for projects not presented 43 | with same name in `.virtualenvs` and without `venv` folder inside of one 44 | of project folders. 45 | If you are using python3 executable while coding for python2 you will get 46 | python2 completions for some built-ins.''' 47 | extraPaths: 48 | type: 'string' 49 | default: '' 50 | order: 4 51 | title: 'Extra Paths For Packages' 52 | description: '''Semicolon separated list of modules to additionally 53 | include for autocomplete. You can use same substitutions as in 54 | `Python Executable Paths`. 55 | Note that it still should be valid python package. 56 | For example: 57 | `$PROJECT/env/lib/python2.7/site-packages` 58 | or 59 | `/User/name/.virtualenvs/$PROJECT_NAME/lib/python2.7/site-packages`. 60 | You don't need to specify extra paths for libraries installed with python 61 | executable you use.''' 62 | caseInsensitiveCompletion: 63 | type: 'boolean' 64 | default: true 65 | order: 5 66 | title: 'Case Insensitive Completion' 67 | description: 'The completion is by default case insensitive.' 68 | triggerCompletionRegex: 69 | type: 'string' 70 | default: '([\.\ (]|[a-zA-Z_][a-zA-Z0-9_]*)' 71 | order: 6 72 | title: 'Regex To Trigger Autocompletions' 73 | description: '''By default completions triggered after words, dots, spaces 74 | and left parenthesis. You will need to restart your editor after changing 75 | this.''' 76 | fuzzyMatcher: 77 | type: 'boolean' 78 | default: true 79 | order: 7 80 | title: 'Use Fuzzy Matcher For Completions.' 81 | description: '''Typing `stdr` will match `stderr`. 82 | First character should always match. Uses additional caching thus 83 | completions should be faster. Note that this setting does not affect 84 | built-in autocomplete-plus provider.''' 85 | outputProviderErrors: 86 | type: 'boolean' 87 | default: false 88 | order: 8 89 | title: 'Output Provider Errors' 90 | description: '''Select if you would like to see the provider errors when 91 | they happen. By default they are hidden. Note that critical errors are 92 | always shown.''' 93 | outputDebug: 94 | type: 'boolean' 95 | default: false 96 | order: 9 97 | title: 'Output Debug Logs' 98 | description: '''Select if you would like to see debug information in 99 | developer tools logs. May slow down your editor.''' 100 | showTooltips: 101 | type: 'boolean' 102 | default: false 103 | order: 10 104 | title: 'Show Tooltips with information about the object under the cursor' 105 | description: '''EXPERIMENTAL FEATURE WHICH IS NOT FINISHED YET. 106 | Feedback and ideas are welcome on github.''' 107 | suggestionPriority: 108 | type: 'integer' 109 | default: 3 110 | minimum: 0 111 | maximum: 99 112 | order: 11 113 | title: 'Suggestion Priority' 114 | description: '''You can use this to set the priority for autocomplete-python-jedi 115 | suggestions. For example, you can use lower value to give higher priority 116 | for snippets completions which has priority of 2.''' 117 | enableTouchBar: 118 | type: 'boolean' 119 | default: false 120 | order: 12 121 | title: 'Enable Touch Bar support' 122 | description: '''Proof of concept for now, requires tooltips to be enabled and Atom >=1.19.0.''' 123 | 124 | installation: null 125 | 126 | _handleGrammarChangeEvent: (grammar) -> 127 | # this should be same with activationHooks names 128 | if grammar.packageName in ['language-python', 'MagicPython', 'atom-django'] 129 | @provider.load() 130 | @emitter.emit 'did-load-provider' 131 | @disposables.dispose() 132 | 133 | load: -> 134 | @disposables = new CompositeDisposable 135 | disposable = atom.workspace.observeTextEditors (editor) => 136 | @_handleGrammarChangeEvent(editor.getGrammar()) 137 | disposable = editor.onDidChangeGrammar (grammar) => 138 | @_handleGrammarChangeEvent(grammar) 139 | @disposables.add disposable 140 | @disposables.add disposable 141 | 142 | activate: (state) -> 143 | @emitter = new Emitter 144 | @provider = require('./provider') 145 | if typeof atom.packages.hasActivatedInitialPackages == 'function' and 146 | atom.packages.hasActivatedInitialPackages() 147 | @load() 148 | else 149 | disposable = atom.packages.onDidActivateInitialPackages => 150 | @load() 151 | disposable.dispose() 152 | 153 | deactivate: -> 154 | @provider.dispose() if @provider 155 | @installation.destroy() if @installation 156 | 157 | getProvider: -> 158 | return @provider 159 | 160 | getHyperclickProvider: -> 161 | return require('./hyperclick-provider') 162 | 163 | consumeSnippets: (snippetsManager) -> 164 | disposable = @emitter.on 'did-load-provider', => 165 | @provider.setSnippetsManager snippetsManager 166 | disposable.dispose() 167 | -------------------------------------------------------------------------------- /lib/override-view.coffee: -------------------------------------------------------------------------------- 1 | {$$, SelectListView} = require 'atom-space-pen-views' 2 | path = require 'path' 3 | 4 | module.exports = 5 | class OverrideView extends SelectListView 6 | initialize: (matches) -> 7 | super 8 | @storeFocusedElement() 9 | @addClass('symbols-view') 10 | @panel ?= atom.workspace.addModalPanel(item: this) 11 | @panel.show() 12 | @setLoading('Looking for methods') 13 | @focusFilterEditor() 14 | @indent = 0 15 | @bufferPosition = null 16 | 17 | destroy: -> 18 | @cancel() 19 | @panel.destroy() 20 | 21 | viewForItem: ({parent, name, params, moduleName, fileName, line, column}) -> 22 | if not line 23 | return $$ -> 24 | @li class: 'two-lines', => 25 | @div "#{parent}.#{name}", class: 'primary-line' 26 | @div 'builtin', class: 'secondary-line' 27 | else 28 | [_, relativePath] = atom.project.relativizePath(fileName) 29 | return $$ -> 30 | @li class: 'two-lines', => 31 | @div "#{parent}.#{name}", class: 'primary-line' 32 | @div "#{relativePath}, line #{line}", class: 'secondary-line' 33 | 34 | getFilterKey: -> 'name' 35 | 36 | getEmptyMessage: (itemCount) -> 37 | if itemCount is 0 38 | 'No methods found' 39 | else 40 | super 41 | 42 | confirmed: ({parent, instance, name, params, line, column}) -> 43 | @cancelPosition = null 44 | @cancel() 45 | editor = atom.workspace.getActiveTextEditor() 46 | tabLength = editor.getTabLength() 47 | 48 | line1 = "def #{name}(#{['self'].concat(params).join(', ')}):" 49 | superCall = "super(#{instance}, self).#{name}(#{params.join(', ')})" 50 | if name in ['__init__'] 51 | line2 = "#{superCall}" 52 | else 53 | line2 = "return #{superCall}" 54 | 55 | if @indent < 1 56 | tabText = editor.getTabText() 57 | editor.insertText("#{tabText}#{line1}") 58 | editor.insertNewlineBelow() 59 | editor.setTextInBufferRange [ 60 | [@bufferPosition.row + 1, 0], 61 | [@bufferPosition.row + 1, tabLength * 2] 62 | ], 63 | "#{tabText}#{tabText}#{line2}" 64 | 65 | else 66 | userIndent = editor.getTextInRange([ 67 | [@bufferPosition.row, 0], 68 | [@bufferPosition.row, @bufferPosition.column] 69 | ]) 70 | editor.insertText("#{line1}") 71 | editor.insertNewlineBelow() 72 | editor.setTextInBufferRange [ 73 | [@bufferPosition.row + 1, 0], 74 | [@bufferPosition.row + 1, tabLength * 2] 75 | ], 76 | "#{userIndent}#{userIndent}#{line2}" 77 | 78 | cancelled: -> 79 | @panel?.hide() 80 | -------------------------------------------------------------------------------- /lib/rename-view.coffee: -------------------------------------------------------------------------------- 1 | {View} = require 'space-pen' 2 | {TextEditorView} = require 'atom-space-pen-views' 3 | 4 | module.exports = 5 | class RenameView extends View 6 | initialize: -> 7 | @panel ?= atom.workspace.addModalPanel(item: @, visible: true) 8 | atom.commands.add(@element, 'core:cancel', => @destroy()) 9 | 10 | destroy: -> 11 | @panel.hide() 12 | @.focusout() 13 | @panel.destroy() 14 | 15 | @content: (usages) -> 16 | n = usages.length 17 | name = usages[0].name 18 | @div => 19 | @div "Type new name to replace #{n} occurences of #{name} within project:" 20 | @subview 'miniEditor', new TextEditorView 21 | mini: true, placeholderText: name 22 | 23 | onInput: (callback) -> 24 | @miniEditor.focus() 25 | atom.commands.add @element, 'core:confirm': => 26 | callback(@miniEditor.getText()) 27 | @destroy() 28 | -------------------------------------------------------------------------------- /lib/scope-helpers.coffee: -------------------------------------------------------------------------------- 1 | slick = require 'atom-slick' 2 | 3 | EscapeCharacterRegex = /[-!"#$%&'*+,/:;=?@|^~()<>{}[\]]/g 4 | 5 | cachedMatchesBySelector = new WeakMap 6 | 7 | getCachedMatch = (selector, scopeChain) -> 8 | if cachedMatchesByScopeChain = cachedMatchesBySelector.get(selector) 9 | return cachedMatchesByScopeChain[scopeChain] 10 | 11 | setCachedMatch = (selector, scopeChain, match) -> 12 | unless cachedMatchesByScopeChain = cachedMatchesBySelector.get(selector) 13 | cachedMatchesByScopeChain = {} 14 | cachedMatchesBySelector.set(selector, cachedMatchesByScopeChain) 15 | cachedMatchesByScopeChain[scopeChain] = match 16 | 17 | parseScopeChain = (scopeChain) -> 18 | scopeChain = scopeChain.replace EscapeCharacterRegex, (match) -> "\\#{match[0]}" 19 | scope for scope in slick.parse(scopeChain)[0] ? [] 20 | 21 | selectorForScopeChain = (selectors, scopeChain) -> 22 | for selector in selectors 23 | cachedMatch = getCachedMatch(selector, scopeChain) 24 | if cachedMatch? 25 | if cachedMatch 26 | return selector 27 | else 28 | continue 29 | else 30 | scopes = parseScopeChain(scopeChain) 31 | while scopes.length > 0 32 | if selector.matches(scopes) 33 | setCachedMatch(selector, scopeChain, true) 34 | return selector 35 | scopes.pop() 36 | setCachedMatch(selector, scopeChain, false) 37 | 38 | null 39 | 40 | selectorsMatchScopeChain = (selectors, scopeChain) -> 41 | selectorForScopeChain(selectors, scopeChain)? 42 | 43 | module.exports = {parseScopeChain, selectorsMatchScopeChain, selectorForScopeChain} 44 | -------------------------------------------------------------------------------- /lib/tooltips.coffee: -------------------------------------------------------------------------------- 1 | log = require './log' 2 | if atom.config.get('autocomplete-python-jedi.enableTouchBar') 3 | touchbar = require './touchbar' 4 | 5 | module.exports = 6 | _showSignatureOverlay: (event) -> 7 | if @markers 8 | for marker in @markers 9 | log.debug 'destroying old marker', marker 10 | marker.destroy() 11 | else 12 | @markers = [] 13 | 14 | cursor = event.cursor 15 | editor = event.cursor.editor 16 | wordBufferRange = cursor.getCurrentWordBufferRange() 17 | scopeDescriptor = editor.scopeDescriptorForBufferPosition( 18 | event.newBufferPosition) 19 | scopeChain = scopeDescriptor.getScopeChain() 20 | 21 | disableForSelector = "#{@disableForSelector}, .source.python .numeric, .source.python .integer, .source.python .decimal, .source.python .punctuation, .source.python .keyword, .source.python .storage, .source.python .variable.parameter, .source.python .entity.name" 22 | disableForSelector = @Selector.create(disableForSelector) 23 | 24 | if @selectorsMatchScopeChain(disableForSelector, scopeChain) 25 | log.debug 'do nothing for this selector' 26 | return 27 | 28 | marker = editor.markBufferRange(wordBufferRange, {invalidate: 'never'}) 29 | 30 | @markers.push(marker) 31 | 32 | getTooltip = (editor, bufferPosition) => 33 | payload = 34 | id: @_generateRequestId('tooltip', editor, bufferPosition) 35 | lookup: 'tooltip' 36 | path: editor.getPath() 37 | source: editor.getText() 38 | line: bufferPosition.row 39 | column: bufferPosition.column 40 | config: @_generateRequestConfig() 41 | @_sendRequest(@_serialize(payload)) 42 | return new Promise (resolve) => 43 | @requests[payload.id] = resolve 44 | 45 | getTooltip(editor, event.newBufferPosition).then (results) => 46 | if marker.isDestroyed() 47 | return 48 | if results.length > 0 49 | {text, fileName, line, column, type, description} = results[0] 50 | 51 | description = description.trim() 52 | if not description 53 | return 54 | view = document.createElement('autocomplete-python-jedi-suggestion') 55 | view.appendChild(document.createTextNode(description)) 56 | decoration = editor.decorateMarker(marker, { 57 | type: 'overlay', 58 | item: view, 59 | position: 'head' 60 | }) 61 | if atom.config.get('autocomplete-python-jedi.enableTouchBar') 62 | touchbar.update(results[0]) 63 | -------------------------------------------------------------------------------- /lib/touchbar.coffee: -------------------------------------------------------------------------------- 1 | {TouchBar} = require('remote') 2 | 3 | spinning = false 4 | 5 | module.exports = 6 | update: (data) -> 7 | if not TouchBar 8 | return 9 | {TouchBarLabel, TouchBarButton, TouchBarSpacer} = TouchBar 10 | button = new TouchBarButton({ 11 | label: "#{data.text}: #{data.description.trim().split('\n')[0]}", 12 | backgroundColor: '#353232', 13 | click: () -> 14 | promise = atom.workspace.open(data.fileName) 15 | promise.then (editor) -> 16 | editor.setCursorBufferPosition([data.line, data.column]) 17 | editor.scrollToCursorPosition() 18 | }) 19 | touchBar = new TouchBar([ 20 | button, 21 | new TouchBarSpacer({size: 'small'}), 22 | ]) 23 | window = atom.getCurrentWindow() 24 | window.setTouchBar(touchBar) 25 | -------------------------------------------------------------------------------- /lib/usages-view.coffee: -------------------------------------------------------------------------------- 1 | {$$, SelectListView} = require 'atom-space-pen-views' 2 | path = require 'path' 3 | 4 | module.exports = 5 | class UsagesView extends SelectListView 6 | initialize: (matches) -> 7 | super 8 | @storeFocusedElement() 9 | @addClass('symbols-view') 10 | @panel ?= atom.workspace.addModalPanel(item: this) 11 | @panel.show() 12 | @setLoading('Looking for usages') 13 | @focusFilterEditor() 14 | 15 | destroy: -> 16 | @cancel() 17 | @panel.destroy() 18 | 19 | viewForItem: ({name, moduleName, fileName, line, column}) -> 20 | [_, relativePath] = atom.project.relativizePath(fileName) 21 | return $$ -> 22 | @li class: 'two-lines', => 23 | @div "#{name}", class: 'primary-line' 24 | @div "#{relativePath}, line #{line}", class: 'secondary-line' 25 | 26 | getFilterKey: -> 'fileName' 27 | 28 | scrollToItemView: -> 29 | super 30 | {name, moduleName, fileName, line, column} = @getSelectedItem() 31 | editor = atom.workspace.getActiveTextEditor() 32 | if editor.getBuffer().file.path is fileName 33 | editor.setSelectedBufferRange([ 34 | [line - 1, column], [line - 1, column + name.length]]) 35 | editor.scrollToBufferPosition([line - 1, column], center: true) 36 | 37 | getEmptyMessage: (itemCount) -> 38 | if itemCount is 0 39 | 'No usages found' 40 | else 41 | super 42 | 43 | confirmed: ({name, moduleName, fileName, line, column}) -> 44 | @cancelPosition = null 45 | @cancel() 46 | promise = atom.workspace.open(fileName) 47 | promise.then (editor) -> 48 | editor.setCursorBufferPosition([line - 1, column]) 49 | editor.setSelectedBufferRange([ 50 | [line - 1, column], [line - 1, column + name.length]]) 51 | editor.scrollToCursorPosition() 52 | 53 | cancelled: -> 54 | @panel?.hide() 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "autocomplete-python-jedi", 3 | "main": "./lib/main", 4 | "version": "1.10.3-0", 5 | "description": "Python completions for packages, variables, methods, functions, with their arguments. Powered by Jedi.", 6 | "repository": "https://github.com/brennv/autocomplete-python-jedi", 7 | "license": "GPL", 8 | "engines": { 9 | "atom": ">=0.194.0 <2.0.0" 10 | }, 11 | "dependencies": { 12 | "atom-slick": "^2.0.0", 13 | "atom-space-pen-views": "~2.1.0", 14 | "fuzzaldrin-plus": "^0.3.1", 15 | "selector-kit": "^0.1", 16 | "space-pen": "^5.1.2", 17 | "underscore": "^1.8.3", 18 | "mixpanel": "^0.5.0" 19 | }, 20 | "package-dependencies": {}, 21 | "providedServices": { 22 | "autocomplete.provider": { 23 | "versions": { 24 | "2.0.0": "getProvider" 25 | } 26 | }, 27 | "hyperclick.provider": { 28 | "versions": { 29 | "0.0.0": "getHyperclickProvider" 30 | } 31 | } 32 | }, 33 | "consumedServices": { 34 | "snippets": { 35 | "versions": { 36 | "0.1.0": "consumeSnippets" 37 | } 38 | } 39 | }, 40 | "contributors": [ 41 | { 42 | "name": "Dmitry Sadovnychyi", 43 | "email": "autocomplete-python@dmit.ro" 44 | }, 45 | { 46 | "name": "Daniel Hung", 47 | "email": "daniel@kite.com" 48 | } 49 | ], 50 | "bugs": { 51 | "url": "https://github.com/brennv/autocomplete-python-jedi/issues" 52 | }, 53 | "keywords": [ 54 | "python", 55 | "autocomplete", 56 | "jedi" 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /spec/provider-spec.coffee: -------------------------------------------------------------------------------- 1 | packagesToTest = 2 | Python: 3 | name: 'language-python' 4 | file: 'test.py' 5 | 6 | describe 'Python autocompletions', -> 7 | [editor, provider] = [] 8 | 9 | getCompletions = -> 10 | cursor = editor.getLastCursor() 11 | start = cursor.getBeginningOfCurrentWordBufferPosition() 12 | end = cursor.getBufferPosition() 13 | prefix = editor.getTextInRange([start, end]) 14 | request = 15 | editor: editor 16 | bufferPosition: end 17 | scopeDescriptor: cursor.getScopeDescriptor() 18 | prefix: prefix 19 | provider.getSuggestions(request) 20 | 21 | beforeEach -> 22 | waitsForPromise -> atom.packages.activatePackage('language-python') 23 | waitsForPromise -> atom.workspace.open('test.py') 24 | runs -> 25 | editor = atom.workspace.getActiveTextEditor() 26 | editor.setGrammar(atom.grammars.grammarsByScopeName['source.python']) 27 | atom.packages.loadPackage('autocomplete-python-jedi').activationHooks = [] 28 | waitsForPromise -> atom.packages.activatePackage('autocomplete-python-jedi') 29 | runs -> 30 | atom.packages.getActivePackage('autocomplete-python-jedi').mainModule.load() 31 | runs -> provider = atom.packages.getActivePackage( 32 | 'autocomplete-python-jedi').mainModule.getProvider() 33 | 34 | it 'autocompletes builtins', -> 35 | editor.setText 'isinstanc' 36 | editor.setCursorBufferPosition([1, 0]) 37 | waitsForPromise -> 38 | getCompletions().then (completions) -> 39 | for completion in completions 40 | expect(completion.text.length).toBeGreaterThan 0 41 | expect(completion.text).toBe 'isinstance' 42 | expect(completions.length).toBe 1 43 | 44 | it 'autocompletes python keywords', -> 45 | editor.setText 'impo' 46 | editor.setCursorBufferPosition([1, 0]) 47 | completions = getCompletions() 48 | for completion in completions 49 | if completion.type == 'keyword' 50 | expect(completion.text).toBe 'import' 51 | expect(completion.text.length).toBeGreaterThan 0 52 | expect(completions.length).toBe 3 53 | 54 | it 'autocompletes defined functions', -> 55 | editor.setText """ 56 | def hello_world(): 57 | return True 58 | hell 59 | """ 60 | editor.setCursorBufferPosition([3, 0]) 61 | waitsForPromise -> 62 | getCompletions().then (completions) -> 63 | expect(completions[0].text).toBe 'hello_world' 64 | expect(completions.length).toBe 1 65 | -------------------------------------------------------------------------------- /styles/autocomplete-python-jedi.less: -------------------------------------------------------------------------------- 1 | @import "ui-variables"; 2 | 3 | atom-text-editor[data-grammar="source python"], :host { 4 | .autocomplete-suggestion-list.select-list.popover-list .suggestion-description-content { 5 | white-space: pre-wrap; 6 | } 7 | } 8 | 9 | 10 | @row-line-height: 2em; 11 | @item-side-padding: .6em; 12 | @line-height: 1.3; 13 | 14 | autocomplete-python-jedi-suggestion { 15 | width: auto; 16 | display: inline-block; 17 | min-width: 200px; 18 | max-width: 800px; 19 | color: @text-color; 20 | 21 | padding: 5px 0; 22 | padding-left: @item-side-padding; 23 | padding-right: @item-side-padding; 24 | min-height: @row-line-height; 25 | line-height: @line-height; 26 | 27 | background: darken(@overlay-background-color, 4%); 28 | border-radius: 0 0 @component-border-radius @component-border-radius; 29 | 30 | max-height: @font-size * @line-height * 20; 31 | overflow-y: scroll; 32 | 33 | white-space: pre-wrap; 34 | font-family: @font-family; 35 | } 36 | --------------------------------------------------------------------------------