# HG changeset patch # User Jordi GutiƩrrez Hermoso # Date 1518639263 18000 # Node ID 56ea66d7630987874fa0e652325bcb2d9592b5ee # Parent bd5ad617c85ab386e1f914d7af89548c194cc9f9 elpy: version 1.18 diff --git a/elpa/elpy-1.14.1/elpy-pkg.el b/elpa/elpy-1.14.1/elpy-pkg.el deleted file mode 100644 --- a/elpa/elpy-1.14.1/elpy-pkg.el +++ /dev/null @@ -1,10 +0,0 @@ -(define-package "elpy" "1.14.1" "Emacs Python Development Environment" - '((company "0.8.2") - (find-file-in-project "3.3") - (highlight-indentation "0.5.0") - (pyvenv "1.3") - (yasnippet "0.8.0") - (s "1.10.0"))) -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/elpa/elpy-1.14.1/elpy/impmagic.py b/elpa/elpy-1.14.1/elpy/impmagic.py deleted file mode 100644 --- a/elpa/elpy-1.14.1/elpy/impmagic.py +++ /dev/null @@ -1,118 +0,0 @@ -"""Glue for the "importmagic" library. - -""" - -import os -import sys -import threading - - -try: - import importmagic.index - import importmagic.symbols - import importmagic.importer -except ImportError: # pragma: no cover - importmagic = None - - -class ImportMagicError(Exception): - """Used to pass defined errors from importmagic to the RPC layer.""" - - -class ImportMagic(object): - - def __init__(self): - self.is_enabled = bool(importmagic) - # fail_message is reported to the user when symbol_index - # is (still) None - self.fail_message = "symbol index is not yet ready" - self.project_root = None - self.symbol_index = None - self.favorites = set() - self._thread = None - - def _build_symbol_index(self, project_root, custom_path, blacklist_re): - try: - index = importmagic.index.SymbolIndex(blacklist_re=blacklist_re) - if os.environ.get('ELPY_TEST'): - # test suite support: do not index the whole PYTHONPATH, it - # takes much too long - index.build_index([]) - elif custom_path: - index.build_index(custom_path) - else: - index.build_index([project_root] + sys.path) - except Exception as e: - self.fail_message = "symbol index failed to build: %s" % e - else: - self.symbol_index = index - - def build_index(self, project_root, custom_path=None, blacklist_re=None): - self.project_root = None - self._thread = threading.Thread(target=self._build_symbol_index, - args=(project_root, custom_path, - blacklist_re)) - self._thread.setDaemon(True) - self._thread.start() - - def get_import_symbols(self, symbol): - scores = self.symbol_index.symbol_scores(symbol) - - def sort_key(item): - score, mod, var = item - if mod in self.favorites: - return 2 + score, mod, var - return score, mod, var - - scores.sort(key=sort_key, reverse=True) - return ["from %s import %s" % (mod, var) if var else "import %s" % mod - for (_, mod, var) in scores] - - def add_import(self, source, statement): - imports = importmagic.importer.Imports(self.symbol_index, source) - if statement.startswith('import '): - sepalias = None - alias = None - if ' as ' in statement: - sepalias = statement.find(' as ') - alias = statement[sepalias+4:] - modname = statement[7:sepalias] - imports.add_import(modname, alias) - self.favorites.add(modname) - else: - sep = statement.find(' import ') - sepalias = None - alias = None - if ' as ' in statement: - sepalias = statement.find(' as ') - alias = statement[sepalias+4:] - modname = statement[5:sep] - name = statement[sep+8:sepalias] - if sep > -1: - self.favorites.add(modname) - imports.add_import_from(modname, name, alias) - start_line, end_line, import_block = imports.get_update() - return start_line, end_line, import_block - - def get_unresolved_symbols(self, source): - try: - scope = importmagic.symbols.Scope.from_source(source) - except SyntaxError: - raise ImportMagicError('cannot find unresolved names in ' - 'incomplete file') - unres, unref = scope.find_unresolved_and_unreferenced_symbols() - return list(unres) - - def remove_unreferenced_imports(self, source): - try: - scope = importmagic.symbols.Scope.from_source(source) - except SyntaxError: - raise ImportMagicError('cannot find unreferenced imports in ' - 'incomplete file') - unres, unref = scope.find_unresolved_and_unreferenced_symbols() - # Note: we do not supply "unres" to the call below, since we do - # not want to add imports without querying the user from which - # module symbols should be imported. - start_line, end_line, import_block = importmagic.importer.get_update( - source, self.symbol_index, set(), unref) - return start_line, end_line, import_block diff --git a/elpa/elpy-1.14.1/elpy/ropebackend.py b/elpa/elpy-1.14.1/elpy/ropebackend.py deleted file mode 100644 --- a/elpa/elpy-1.14.1/elpy/ropebackend.py +++ /dev/null @@ -1,290 +0,0 @@ -"""Elpy backend using the Rope library. - -This backend uses the Rope library: - -http://rope.sourceforge.net/ - -""" - -import os -import time - -import rope.contrib.codeassist -import rope.base.project -import rope.base.libutils -import rope.base.exceptions -import rope.contrib.findit - -from elpy import rpc -import elpy.pydocutils - -VALIDATE_EVERY_SECONDS = 5 -MAXFIXES = 5 - - -class RopeBackend(object): - """The Rope backend class. - - Implements the RPC calls we can pass on to Rope. Also subclasses - the native backend to provide methods Rope does not provide, if - any. - - """ - name = "rope" - - def __init__(self, project_root): - super(RopeBackend, self).__init__() - self.last_validation = 0 - if not os.path.exists(project_root): - raise rpc.Fault( - "rope does not support files without a local project root", - code=400 - ) - self.project_root = project_root - self.completions = {} - prefs = dict(ignored_resources=['*.pyc', '*~', '.ropeproject', - '.hg', '.svn', '_svn', '.git'], - python_files=['*.py'], - save_objectdb=False, - compress_objectdb=False, - automatic_soa=True, - soa_followed_calls=0, - perform_doa=True, - validate_objectdb=True, - max_history_items=32, - save_history=False, - compress_history=False, - indent_size=4, - extension_modules=[], - import_dynload_stdmods=True, - ignore_syntax_errors=False, - ignore_bad_imports=False) - self.project = rope.base.project.Project(self.project_root, - ropefolder=None, - **prefs) - - def get_resource(self, filename): - if filename is not None and os.path.exists(filename): - return rope.base.libutils.path_to_resource(self.project, - filename, - 'file') - else: - return None - - def validate(self): - """Validate the stored project. - - This should be called before every use of Rope. It will - revalidate the project, but do some call throttling. - - """ - now = time.time() - if now > self.last_validation + VALIDATE_EVERY_SECONDS: - try: - self.project.validate() - except rope.base.exceptions.ResourceNotFoundError: - pass - self.last_validation = now - - def call_rope(self, rope_function, filename, source, offset, - **kwargs): - self.validate() - resource = self.get_resource(filename) - try: - return rope_function(self.project, - source, offset, - resource, - maxfixes=MAXFIXES, - **kwargs) - except Exception: - return None - - def rpc_get_completions(self, filename, source, offset): - proposals = self.call_rope( - rope.contrib.codeassist.code_assist, - filename, source, offset - ) - if proposals is None: - return [] - try: - starting_offset = rope.contrib.codeassist.starting_offset(source, - offset) - except Exception: - return [] - prefixlen = offset - starting_offset - try: - self.completions = dict((proposal.name, proposal) - for proposal in proposals) - return [{'name': proposal.name, - 'suffix': proposal.name[prefixlen:], - 'annotation': proposal.type, - 'meta': str(proposal)} - for proposal in proposals] - except Exception: - return [] - - def rpc_get_completion_docstring(self, completion): - proposal = self.completions.get(completion) - if proposal is None: - return None - else: - return proposal.get_doc() - - def rpc_get_completion_location(self, completion): - proposal = self.completions.get(completion) - if proposal is None: - return None - else: - if not proposal.pyname: - return None - module, lineno = proposal.pyname.get_definition_location() - if module is None: - return None - resource = module.get_module().get_resource() - return (resource.real_path, lineno) - - def rpc_get_definition(self, filename, source, offset): - location = self.call_rope( - rope.contrib.findit.find_definition, - filename, source, offset - ) - if location is None: - return None - else: - return (location.resource.real_path, location.offset) - - def rpc_get_calltip(self, filename, source, offset): - offset = find_called_name_offset(source, offset) - if 0 < offset < len(source) and source[offset] == ')': - offset -= 1 - - calltip = self.call_rope( - rope.contrib.codeassist.get_calltip, - filename, source, offset, - remove_self=True - ) - if calltip is None: - return None - - calltip = calltip.replace(".__init__(", "(") - calltip = calltip.replace("(self)", "()") - calltip = calltip.replace("(self, ", "(") - # "elpy.tests.support.source_and_offset(source)" - # => - # "support.source_and_offset(source)" - try: - openpos = calltip.index("(") - period2 = calltip.rindex(".", 0, openpos) - period1 = calltip.rindex(".", 0, period2) - calltip = calltip[period1 + 1:] - except ValueError: - pass - return calltip - - def rpc_get_docstring(self, filename, source, offset): - return self.call_rope( - rope.contrib.codeassist.get_doc, - filename, source, offset - ) - - -def find_called_name_offset(source, orig_offset): - """Return the offset of a calling function. - - This only approximates movement. - - """ - offset = min(orig_offset, len(source) - 1) - paren_count = 0 - while True: - if offset <= 1: - return orig_offset - elif source[offset] == '(': - if paren_count == 0: - return offset - 1 - else: - paren_count -= 1 - elif source[offset] == ')': - paren_count += 1 - offset -= 1 - - -################################################################## -# A recurring problem in Rope for Elpy is that it searches the whole -# project root for Python files. If the user edits a file in their -# home directory, this can easily read a whole lot of files, making -# Rope practically useless. We change the file finding algorithm here -# to only recurse into directories with an __init__.py file in them. -def find_source_folders(self, folder): - for resource in folder.get_folders(): - if self._is_package(resource): - return [folder] - result = [] - for resource in folder.get_files(): - if resource.name.endswith('.py'): - result.append(folder) - break - for resource in folder.get_folders(): - if self._is_package(resource): - result.append(resource) - return result - -import rope.base.pycore -rope.base.pycore.PyCore._find_source_folders = find_source_folders - - -def get_files(self): - if self.files is None: - self.files = get_python_project_files(self.project) - return self.files - -rope.base.project._FileListCacher.get_files = get_files - - -def get_python_project_files(project): - for dirname, subdirs, files in os.walk(project.root.real_path): - for filename in files: - yield rope.base.libutils.path_to_resource( - project, os.path.join(dirname, filename), 'file') - subdirs[:] = [subdir for subdir in subdirs - if os.path.exists(os.path.join(dirname, subdir, - "__init__.py"))] - - -################################################################## -# Monkey patching a method in rope because it doesn't complete import -# statements. - -orig_code_completions = (rope.contrib.codeassist. - _PythonCodeAssist._code_completions) - - -def code_completions(self): - proposals = get_import_completions(self) - if proposals: - return proposals - else: - return orig_code_completions(self) - - -def get_import_completions(self): - if not self.word_finder.is_import_statement(self.offset): - return [] - modulename = self.word_finder.get_primary_at(self.offset) - # Rope can handle modules in packages - if "." in modulename: - return [] - return dict((name, FakeProposal(name)) - for name in elpy.pydocutils.get_modules() - if name.startswith(modulename)) - - -class FakeProposal(object): - def __init__(self, name): - self.name = name - self.type = "mock" - - def get_doc(self): - return None - -rope.contrib.codeassist._PythonCodeAssist._code_completions = code_completions diff --git a/elpa/elpy-1.14.1/elpy/tests/test_impmagic.py b/elpa/elpy-1.14.1/elpy/tests/test_impmagic.py deleted file mode 100644 --- a/elpa/elpy-1.14.1/elpy/tests/test_impmagic.py +++ /dev/null @@ -1,73 +0,0 @@ -# coding: utf-8 - -"""Tests for the elpy.impmagic module""" - -import re -import sys -import unittest - -from elpy import impmagic -from elpy.tests.support import BackendTestCase - -TEST_SOURCE = '''# test file - -import time -import logging - -os.getcwd() -time.sleep(1) -''' - - -@unittest.skipIf(sys.version_info >= (3, 5), "importmagic fails in 3.5") -class ImportMagicTestCase(BackendTestCase): - def setUp(self): - if not impmagic.importmagic: - raise unittest.SkipTest - self.importmagic = impmagic.ImportMagic() - super(ImportMagicTestCase, self).setUp() - - def build_index(self): - self.project_file('mymod.py', 'class AnUncommonName:\n pass\n') - self.importmagic.build_index(self.project_root, - custom_path=[self.project_root], - blacklist_re=re.compile('^$')) - self.importmagic._thread.join() - - def test_get_symbols(self): - self.build_index() - candidates = self.importmagic.get_import_symbols('AnUncommonName') - self.assertEqual(candidates, ['from mymod import AnUncommonName']) - candidates = self.importmagic.get_import_symbols('mymod') - self.assertEqual(candidates, ['import mymod']) - - def test_add_import(self): - self.build_index() - start, end, newblock = self.importmagic.add_import( - TEST_SOURCE, 'from mymod import AnUncommonName') - self.assertEqual(start, 2) - self.assertEqual(end, 5) - self.assertEqual(newblock.strip(), - 'import logging\n' - 'import time\n' - 'from mymod import AnUncommonName') - - start, end, newblock = self.importmagic.add_import( - TEST_SOURCE, 'import mymod') - self.assertEqual(start, 2) - self.assertEqual(end, 5) - self.assertEqual(newblock.strip(), - 'import logging\nimport mymod\nimport time') - - def test_get_unresolved_symbols(self): - self.build_index() - symbols = self.importmagic.get_unresolved_symbols('x = a + b\ny = c.d') - self.assertEqual(sorted(symbols), ['a', 'b', 'c.d']) - - def test_remove_unreferenced_imports(self): - self.build_index() - start, end, newblock = \ - self.importmagic.remove_unreferenced_imports(TEST_SOURCE) - self.assertEqual(start, 2) - self.assertEqual(end, 5) - self.assertEqual(newblock.strip(), 'import time') diff --git a/elpa/elpy-1.14.1/elpy/tests/test_ropebackend.py b/elpa/elpy-1.14.1/elpy/tests/test_ropebackend.py deleted file mode 100644 --- a/elpa/elpy-1.14.1/elpy/tests/test_ropebackend.py +++ /dev/null @@ -1,205 +0,0 @@ -"""Tests for elpy.ropebackend.""" - -import os -import shutil -import sys -import tempfile - -import mock - -from elpy import ropebackend -from elpy import rpc -from elpy.tests import compat -from elpy.tests.support import BackendTestCase -from elpy.tests.support import RPCGetCompletionsTests -from elpy.tests.support import RPCGetCompletionDocstringTests -from elpy.tests.support import RPCGetCompletionLocationTests -from elpy.tests.support import RPCGetDefinitionTests -from elpy.tests.support import RPCGetCalltipTests -from elpy.tests.support import RPCGetDocstringTests - - -class RopeBackendTestCase(BackendTestCase): - def setUp(self): - super(RopeBackendTestCase, self).setUp() - self.backend = ropebackend.RopeBackend(self.project_root) - - -class ShouldCallValidateTest(object): - def test_should_call_validate(self): - with mock.patch.object(self.backend, 'validate') as validate: - self.rpc(None, "", 0) - - self.assertTrue(validate.called) - - -class TestInit(RopeBackendTestCase): - def test_should_have_rope_as_name(self): - self.assertEqual(self.backend.name, "rope") - - def test_should_patch_project_files(self): - self.project_file("foo.txt", "") - self.project_file("baddir/file.py", "") - - self.backend.project.validate() - - actual = [f.real_path for f in - self.backend.project.file_list.get_files()] - self.assertEqual([os.path.join(self.project_root, "foo.txt")], - actual) - - def test_should_fail_for_inexisting_project_root(self): - with self.assertRaises(rpc.Fault): - ropebackend.RopeBackend("/does/not/exist/") - - -class TestValidate(RopeBackendTestCase): - def test_should_call_validate_after_timeout(self): - with mock.patch("time.time") as t: - t.return_value = 10 - self.backend.validate() - with mock.patch.object(self.backend, 'project') as project: - t.return_value = 10 + ropebackend.VALIDATE_EVERY_SECONDS + 1 - self.backend.validate() - - self.assertTrue(project.validate.called) - - def test_should_not_call_validate_before_timeout(self): - with mock.patch("time.time") as t: - t.return_value = 10 - self.backend.validate() - with mock.patch.object(self.backend, 'project') as project: - t.return_value = 10 + ropebackend.VALIDATE_EVERY_SECONDS - 1 - self.backend.validate() - - self.assertFalse(project.validate.called) - - def test_should_not_fail_if_root_vanishes(self): - # Bug #353 - tmpdir = tempfile.mkdtemp(prefix="elpy-test-validate-") - try: - backend = ropebackend.RopeBackend(tmpdir) - finally: - shutil.rmtree(tmpdir) - backend.validate() - - -class TestRPCGetCompletions(RPCGetCompletionsTests, - RopeBackendTestCase): - pass - - -class TestRPCGetCompletionDocstring(RPCGetCompletionDocstringTests, - RopeBackendTestCase): - pass - - -class TestRPCGetCompletionLocation(RPCGetCompletionLocationTests, - RopeBackendTestCase): - pass - - -class TestRPCGetDefinition(RPCGetDefinitionTests, - ShouldCallValidateTest, - RopeBackendTestCase): - pass - - -class TestRPCGetCalltip(RPCGetCalltipTests, - ShouldCallValidateTest, - RopeBackendTestCase): - ADD_CALLTIP = 'Add.add(a, b)' - RADIX_CALLTIP = "Decimal.radix()" - if compat.PYTHON3: - THREAD_CALLTIP = ( - "threading.Thread(group=None, target=None, " - "name=None, args=(), kwargs=None, daemon=None, *)" - ) - KEYS_CALLTIP = "builtins.keys()" - else: - THREAD_CALLTIP = ( - "threading.Thread(group=None, target=None, " - "name=None, args=(), kwargs=None, verbose=None)" - ) - KEYS_CALLTIP = "__builtin__.keys()" - - -class TestRPCGetDocstring(RPCGetDocstringTests, - ShouldCallValidateTest, - RopeBackendTestCase): - if sys.version_info < (2, 7): - JSON_LOADS_DOCSTRING = ( - 'loads(s, encoding=None, cls=None, object_hook=None, ' - 'parse_float=None, parse_int=None, parse_constant=None, ' - '**kw):' - ) - else: - JSON_LOADS_DOCSTRING = ( - 'loads(s, encoding=None, cls=None, object_hook=None, ' - 'parse_float=None, parse_int=None, parse_constant=None, ' - 'object_pairs_hook=None, **kw):' - ) - - -class TestGetPythonProjectFiles(RopeBackendTestCase): - def test(self): - self.project_file("foo.txt", "") - self.project_file("gooddir/__init__.py", "") - self.project_file("gooddir/file.py", "") - self.project_file("baddir/file.py", "") - expected = set(os.path.join(self.project_root, name) - for name in ["foo.txt", "gooddir/__init__.py", - "gooddir/file.py"]) - project = self.backend.project - - actual = set(resource.real_path - for resource - in ropebackend.get_python_project_files(project)) - - self.assertEqual(expected, actual) - - -class TestPatchProjectFiles(RopeBackendTestCase): - def test(self): - self.project_file("foo.txt", "") - self.project_file("gooddir/__init__.py", "") - self.project_file("gooddir/file.py", "") - self.project_file("baddir/file.py", "") - expected = set(os.path.join(self.project_root, name) - for name in ["foo.txt", "gooddir/__init__.py", - "gooddir/file.py"]) - - actual = set(resource.real_path - for resource - in self.backend.project.get_files()) - self.assertEqual(expected, actual) - - -class TestCallRope(RopeBackendTestCase): - def test_should_return_value(self): - func = mock.MagicMock() - func.return_value = 23 - - actual = self.backend.call_rope( - func, "foo.py", "", 0 - ) - - self.assertEqual(23, actual) - - def test_should_raise_fault_with_data_on_exception(self): - func = mock.MagicMock() - func.side_effect = RuntimeError("Stuff!") - func.__module__ = "rope.test" - func.__name__ = "test_function" - - try: - self.backend.call_rope( - func, "foo.py", "", 0 - ) - except rpc.Fault as e: - self.assertEqual(500, e.code) - self.assertEqual("Stuff!", e.message) - self.assertIn("traceback", e.data) - self.assertIn("rope_debug_info", e.data) - self.assertEqual("rope.test.test_function", - e.data["rope_debug_info"]["function_name"]) diff --git a/elpa/elpy-1.14.1/NEWS.rst b/elpa/elpy-1.18.0/NEWS.rst rename from elpa/elpy-1.14.1/NEWS.rst rename to elpa/elpy-1.18.0/NEWS.rst --- a/elpa/elpy-1.14.1/NEWS.rst +++ b/elpa/elpy-1.18.0/NEWS.rst @@ -1,3 +1,73 @@ +New in Elpy 1.18.0 +================== + +- Elpy is now using MELPA Stable as the official distribution point. + Please remove jorgenschaefer.github.io from your sources list if you + still use it. It will not receive further updates after 1.18. +- New commands for interacting with the Python shell, thanks to Rainer + Gemulla. Check them out at + https://elpy.readthedocs.io/en/latest/ide.html#evaluating-code-fragments +- Shells started using ``C-c C-z`` will now use the project root as + the current directory, to be in line with other code sending + functionality. +- importmagic has been removed. While this package added some nice + functionality to Elpy, the way it worked by importing every module + caused a lot of bugs. It was not an easy decision, but this is + better for most Elpy users. +- Rope is no longer supported for completions and similar + functionality. Refactoring support using Rope is still there. Sadly, + Rope has not received much of any update in a long time, and + attempts to revive it failed. Maintaining multiple code paths to + support both Jedi and Rope was complicated, so we decided to get rid + of Rope for these features. Jedi is almost always an equivalent if + not better choice. +- The Green test runner is now supported by default. +- Beyond all of this, there were numerous bugfixes. + +We are happy to report that Elpy now has more maintainers! Daniel +Gopar, Rainer Gemulla and @galaunay are now helping regularly with +this project. + +Thanks to all the contributors! + + +New in Elpy 1.17.0 +================== + +- The xref functionality in newer Emacsen is now supported for + following symbols at point. +- Elpy now supports PEP 397 for Windows executable names. +- In addition to pylint, Elpy now also supports epylint correctly. +- A number of features for working with interactive Python have been added to + Elpy; e.g., commands for sending code fragments to the Python shell and the + ability to echo their output in the message area. See the documentation for a + full list. +- Bunch of little bugfixes. + + +New in Elpy 1.16.0 +================== + +- You can now change which function is used to run test commands, + instead of the default ``compile``, using + ``elpy-test-compilation-function``. This allows using ``pdb`` to run + tests in a debugger, for example. +- Elpy now sets ``IPY_TEST_SIMPLE_PROMPT``, which should prevent a + number of problems with IPython 5. +- If you like Elpy, you can now sponsor its development using Patreon + at https://www.patreon.com/jorgenschaefer + + +New in Elpy 1.15.0 +================== + +- Elpy now supports profiling, try ``elpy-profile-buffer-or-region``! +- Do not randomly downcase completion candidates anymore. +- Work around a bug in Emacs 25.1 and before related to current + IPython shells. +- And lots of other bugfixes. + + New in Elpy 1.14.0 ================== @@ -14,7 +84,8 @@ - Fewer surprises with syntax checks, ``C-c C-z``, reformatting - Improved behavior for reformatting. -- Improved documentation for IPython. IPython 5 broke a lot of things with Emacs. Use it at your own risk. +- Improved documentation for IPython. IPython 5 broke a lot of things + with Emacs. Use it at your own risk. New in Elpy 1.12.0 diff --git a/elpa/elpy-1.14.1/elpy-autoloads.el b/elpa/elpy-1.18.0/elpy-autoloads.el rename from elpa/elpy-1.14.1/elpy-autoloads.el rename to elpa/elpy-1.18.0/elpy-autoloads.el --- a/elpa/elpy-1.14.1/elpy-autoloads.el +++ b/elpa/elpy-1.18.0/elpy-autoloads.el @@ -1,9 +1,9 @@ ;;; elpy-autoloads.el --- automatically extracted autoloads ;; ;;; Code: -(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) +(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path)))) -;;;### (autoloads nil "elpy" "elpy.el" (22708 23356 115000 877000)) +;;;### (autoloads nil "elpy" "elpy.el" (23172 38714 746030 40000)) ;;; Generated autoloads from elpy.el (autoload 'elpy-enable "elpy" "\ @@ -38,8 +38,8 @@ ;;;*** -;;;### (autoloads nil nil ("elpy-django.el" "elpy-pkg.el" "elpy-refactor.el") -;;;;;; (22708 23356 436805 199000)) +;;;### (autoloads nil nil ("elpy-django.el" "elpy-pkg.el" "elpy-profile.el" +;;;;;; "elpy-refactor.el" "elpy-shell.el") (23172 38715 78030 718000)) ;;;*** diff --git a/elpa/elpy-1.14.1/elpy-django.el b/elpa/elpy-1.18.0/elpy-django.el rename from elpa/elpy-1.14.1/elpy-django.el rename to elpa/elpy-1.18.0/elpy-django.el --- a/elpa/elpy-1.14.1/elpy-django.el +++ b/elpa/elpy-1.18.0/elpy-django.el @@ -123,7 +123,7 @@ ;; get a list of commands from the output of manage.py -h ;; What would be the pattern to optimize this ? (setq dj-commands-str (split-string dj-commands-str "\n")) - (setq dj-commands-str (-remove (lambda (x) (string= x "")) dj-commands-str)) + (setq dj-commands-str (cl-remove-if (lambda (x) (string= x "")) dj-commands-str)) (setq dj-commands-str (mapcar (lambda (x) (s-trim x)) dj-commands-str)) (sort dj-commands-str 'string-lessp))) diff --git a/elpa/elpy-1.18.0/elpy-pkg.el b/elpa/elpy-1.18.0/elpy-pkg.el new file mode 100644 --- /dev/null +++ b/elpa/elpy-1.18.0/elpy-pkg.el @@ -0,0 +1,11 @@ +(define-package "elpy" "1.18.0" "Emacs Python Development Environment" + '((company "0.9.2") + (emacs "24.4") + (find-file-in-project "3.3") + (highlight-indentation "0.5.0") + (pyvenv "1.3") + (yasnippet "0.8.0") + (s "1.11.0"))) +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/elpa/elpy-1.18.0/elpy-profile.el b/elpa/elpy-1.18.0/elpy-profile.el new file mode 100644 --- /dev/null +++ b/elpa/elpy-1.18.0/elpy-profile.el @@ -0,0 +1,111 @@ +;;; elpy-profile.el --- Profiling capabilitiss for elpy + +;; Copyright (C) 2013-2016 Jorgen Schaefer + +;; Author: Gaby Launay +;; URL: https://github.com/jorgenschaefer/elpy + +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License +;; as published by the Free Software Foundation; either version 3 +;; of the License, or (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; This file serves as an extension to elpy by adding profiling capabilities + +;;; Code: + + +;;;;;;;;;;;;;;;;;;;;;; +;;; User customization + +(defcustom elpy-profile-visualizer "snakeviz" + "Visualizer for elpy profile results." + :type 'str + :group 'elpy) + +;;;;;;;;;;;;;;;;;;;;;; +;;; Helper Functions + +(defun elpy-profile--display-profiling (file) + "Display the profile result FILE using `elpy-profile-visualizer'." + (let ((exec (car (split-string elpy-profile-visualizer " " t))) + (args (append (cdr (split-string elpy-profile-visualizer " " t)) (list file)))) + (if (executable-find exec) + (apply 'call-process exec nil 0 nil args) + (message "Elpy profile visualizer '%s' not found" exec)))) + +(defun elpy-profile--sentinel (process string) + "Elpy profile sentinel." + (let ((filename (file-name-nondirectory (process-get process 'file))) + (prof-file (process-get process 'prof-file)) + (dont-display (process-get process 'dont-display))) + (with-current-buffer "*elpy-profile-log*" + (view-mode)) + (if (not (string-equal string "finished\n")) + (progn + (message "[%s] Profiling failed" filename) + (display-buffer "*elpy-profile-log*")) + (message "[%s] Profiling succeeded" filename) + (when (not dont-display) + (elpy-profile--display-profiling prof-file))))) + +(defun elpy-profile--file (file &optional in-dir dont-display) + "Profile asynchronously FILE and display the result using +`elpy-profile-visualizer'. + +If IN-DIR is non nil, profile result is saved in the same +directory as the script. +If DONT-DISPLAY is non nil, don't display the profile results." + (ignore-errors (kill-buffer "*elpy-profile-log*")) + (let* ((prof-file (if in-dir + (concat (file-name-sans-extension file) ".profile") + (concat (make-temp-file "elpy-profile-" nil ".profile")))) + (proc-name (format "elpy-profile-%s" file)) + (proc-cmd (list python-shell-interpreter "-m" "cProfile" "-o" prof-file file)) + (proc (make-process :name proc-name + :buffer "*elpy-profile-log*" + :sentinel 'elpy-profile--sentinel + :command proc-cmd))) + (message "[%s] Profiling ..." (file-name-nondirectory file)) + (process-put proc 'prof-file prof-file) + (process-put proc 'file file) + (process-put proc 'dont-display dont-display) + prof-file)) + +;;;;;;;;;;;;;;;;;;;;;; +;;; User Functions + +(defun elpy-profile-buffer-or-region (&optional in-dir dont-display) + "Profile asynchronously the active region or the current buffer +and display the result using `elpy-profile-visualizer'. + +If IN-DIR is non nil, profile result is saved in the same +directory as the script. +If DONT-DISPLAY is non nil, don't display the profile results." + (interactive "P") + (let* ((file-name (buffer-name)) + (file-dir (file-name-directory (buffer-file-name))) + (beg (if (region-active-p) (region-beginning) (point-min))) + (end (if (region-active-p) (region-end) (point-max))) + (tmp-file-prefix (if (region-active-p) "_region_" "")) + (tmp-file (if in-dir + (concat file-dir "/" tmp-file-prefix file-name) + (concat (make-temp-file "elpy-profile-" t) "/" tmp-file-prefix file-name))) + (region (elpy-shell--region-without-indentation beg end))) + (with-temp-buffer + (insert region) + (write-region (point-min) (point-max) tmp-file nil t)) + (elpy-profile--file tmp-file t dont-display))) + +(provide 'elpy-profile) +;;; elpy-profile.el ends here diff --git a/elpa/elpy-1.14.1/elpy-refactor.el b/elpa/elpy-1.18.0/elpy-refactor.el rename from elpa/elpy-1.14.1/elpy-refactor.el rename to elpa/elpy-1.18.0/elpy-refactor.el diff --git a/elpa/elpy-1.18.0/elpy-shell.el b/elpa/elpy-1.18.0/elpy-shell.el new file mode 100644 --- /dev/null +++ b/elpa/elpy-1.18.0/elpy-shell.el @@ -0,0 +1,1001 @@ +;;; elpy-shell.el --- Interactive Python support for elpy -*- lexical-binding: t -*- +;; +;; Copyright (C) 2012-2016 Jorgen Schaefer +;; +;; Author: Jorgen Schaefer , Rainer Gemulla +;; URL: https://github.com/jorgenschaefer/elpy +;; +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License +;; as published by the Free Software Foundation; either version 3 +;; of the License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . +;; +;;; Commentary: +;; +;; Adds support for interactive Python to elpy +;; +;;; Code: + +;;;;;;;;;;;;;;;;;;;;;; +;;; User customization + +(defcustom elpy-dedicated-shells nil + "Non-nil if Elpy should use dedicated shells. + +Elpy can use a unique Python shell for all buffers and support +manually started dedicated shells. Setting this option to non-nil +force the creation of dedicated shells for each buffers." + :type 'boolean + :group 'elpy) + +(defcustom elpy-shell-display-buffer-after-send nil ; + "Whether to display the Python shell after sending something to it." + :type 'boolean + :group 'elpy) + +(defcustom elpy-shell-echo-output 'when-shell-not-visible + "Whether to echo the Python shell output in the echo area after input has been sent to the shell. + + Possible choices are nil (=never), `when-shell-not-visible', or + t (=always)." + :type '(choice (const :tag "Never" nil) + (const :tag "When shell not visible" when-shell-not-visible) + (const :tag "Always" t)) + :group 'elpy) + +(defcustom elpy-shell-capture-last-multiline-output t + "Whether to capture the output of the last Python statement when sending multiple statements to the Python shell. + + If nil, no output is captured (nor echoed in the shell) when + sending multiple statements. This is the default behavior of + python.el. If non-nil and the last statement is an expression, + captures its output so that it is echoed in the shell." + :type 'boolean + :group 'elpy) + +(defcustom elpy-shell-echo-input t + "Whether to echo input sent to the Python shell as input in the +shell buffer. + +Truncation of long inputs can be controlled via +`elpy-shell-echo-input-lines-head' and +`elpy-shell-echo-input-lines-tail'." + :type 'boolean + :group 'elpy) + +(defcustom elpy-shell-echo-input-cont-prompt t + "Whether to show a continuation prompt when echoing multi-line +input to the Python shell." + :type 'boolean + :group 'elpy) + +(defcustom elpy-shell-echo-input-lines-head 10 + "Maximum number of lines to show before truncating input echoed +in the Python shell." + :type 'integer + :group 'elpy) + +(defcustom elpy-shell-echo-input-lines-tail 10 + "Maximum number of lines to show after truncating input echoed +in the Python shell." + :type 'integer + :group 'elpy) + +(defcustom elpy-shell-use-project-root t + "Whether to use project root as default directory when starting a Python shells. + +The project root is determined using `elpy-project-root`. If this variable is set to +nil, the current directory is used instead." + :type 'boolean + :group 'elpy) + +(defcustom elpy-shell-cell-boundary-regexp + (concat "^\\(?:" + "##.*" "\\|" + "#\\s-*<.+>" "\\|" + "#\\s-*\\(?:In\\|Out\\)\\[.*\\]:" + "\\)\\s-*$") + "Regular expression for matching a line indicating the boundary +of a cell (beginning or ending). By default, lines starting with +``##`` are treated as a cell boundaries, as are the boundaries in +Python files exported from IPython or Jupyter notebooks (e.g., +``# ``, ``# In[1]:'', or ``# Out[1]:``)." + :type 'string + :group 'elpy) + +(defcustom elpy-shell-codecell-beginning-regexp + (concat "^\\(?:" + "##.*" "\\|" + "#\\s-*" "\\|" + "#\\s-*In\\[.*\\]:" + "\\)\\s-*$") + "Regular expression for matching a line indicating the +beginning of a code cell. By default, lines starting with ``##`` +are treated as beginnings of a code cell, as are the code cell +beginnings (and only the code cell beginnings) in Python files +exported from IPython or Jupyter notebooks (e.g., ``# +`` or ``# In[1]:``). + +Note that `elpy-shell-cell-boundary-regexp' must also match +the code cell beginnings defined here." + :type 'string + :group 'elpy) + + +;;;;;;;;;;;;;;;;;; +;;; Shell commands + +(defvar elpy--shell-last-py-buffer nil + "Help keep track of python buffer when changing to pyshell.") + +(defun elpy-shell-display-buffer () + "Display inferior Python process buffer." + (display-buffer (process-buffer (elpy-shell-get-or-create-process)) + nil + 'visible)) + +;; better name would be pop-to-shell +(defun elpy-shell-switch-to-shell () + "Switch to inferior Python process buffer." + (interactive) + (setq elpy--shell-last-py-buffer (buffer-name)) + (pop-to-buffer (process-buffer (elpy-shell-get-or-create-process)))) + +(defun elpy-shell-switch-to-buffer () + "Switch from inferior Python process buffer to recent Python buffer." + (interactive) + (pop-to-buffer elpy--shell-last-py-buffer)) + +(defun elpy-shell-switch-to-shell-in-current-window () + (interactive) + (setq elpy--shell-last-py-buffer (buffer-name)) + (switch-to-buffer (process-buffer (elpy-shell-get-or-create-process)))) + +(defun elpy-shell-switch-to-buffer-in-current-window () + (interactive) + (switch-to-buffer elpy--shell-last-py-buffer)) + +(defun elpy-shell-kill (&optional kill-buff) + "Kill the current python shell. + +If `elpy-dedicated-shells' is non-nil, +kill the current buffer dedicated shell. + +If KILL-BUFF is non-nil, also kill the associated buffer." + (interactive) + (let ((shell-buffer (python-shell-get-buffer))) + (cond + (shell-buffer + (delete-process shell-buffer) + (when kill-buff + (kill-buffer shell-buffer)) + (message "Killed %s shell" shell-buffer)) + (t + (message "No python shell to kill"))))) + +(defun elpy-shell-kill-all (&optional kill-buffers ask-for-each-one) + "Kill all active python shells. + +If KILL-BUFFERS is non-nil, also kill the associated buffers. +If ASK-FOR-EACH-ONE is non-nil, ask before killing each python process. +" + (interactive) + (let ((python-buffer-list ())) + ;; Get active python shell buffers and kill inactive ones (if asked) + (loop for buffer being the buffers do + (when (and (buffer-name buffer) + (string-match (rx bol "*Python" (opt "[" (* (not (any "]"))) "]") "*" eol) + (buffer-name buffer))) + (if (get-buffer-process buffer) + (push buffer python-buffer-list) + (when kill-buffers + (kill-buffer buffer))))) + (cond + ;; Ask for each buffers and kill + ((and python-buffer-list ask-for-each-one) + (loop for buffer in python-buffer-list do + (when (y-or-n-p (format "Kill %s ?" buffer)) + (delete-process buffer) + (when kill-buffers + (kill-buffer buffer))))) + ;; Ask and kill every buffers + (python-buffer-list + (if (y-or-n-p (format "Kill %s python shells ?" (length python-buffer-list))) + (loop for buffer in python-buffer-list do + (delete-process buffer) + (when kill-buffers + (kill-buffer buffer))))) + ;; No shell to close + (t + (message "No python shell to close"))))) + +(defun elpy-shell-get-or-create-process (&optional sit) + "Get or create an inferior Python process for current buffer and return it. + +If SIT is non-nil, sit for that many seconds after creating a +Python process. This allows the process to start up." + (let* ((bufname (format "*%s*" (python-shell-get-process-name nil))) + (dedbufname (format "*%s*" (python-shell-get-process-name t))) + (proc (get-buffer-process bufname)) + (dedproc (get-buffer-process dedbufname))) + (if elpy-dedicated-shells + (if dedproc + dedproc + (let ((default-directory (or (and elpy-shell-use-project-root + (elpy-project-root)) + default-directory))) + (run-python (python-shell-parse-command) t t)) + (when sit + (sit-for sit)) + (when (elpy-project-root) + (python-shell-send-string + (format "import sys;sys.path.append('%s')" (elpy-project-root)))) + (get-buffer-process dedbufname)) + (if dedproc + dedproc + (if proc + proc + (let ((default-directory (or (and elpy-shell-use-project-root + (elpy-project-root)) + default-directory))) + (run-python (python-shell-parse-command) nil t)) + (when sit + (sit-for sit)) + (when (elpy-project-root) + (python-shell-send-string + (format "import sys;sys.path.append('%s')" (elpy-project-root)))) + (get-buffer-process bufname)))))) + +(defun elpy-shell--ensure-shell-running () + "Ensure that the Python shell for the current buffer is running. + +If the shell is not running, waits until the first prompt is visible and +commands can be sent to the shell." + (with-current-buffer (process-buffer (elpy-shell-get-or-create-process)) + (let ((cumtime 0)) + (while (and (when (boundp 'python-shell--first-prompt-received) + (not python-shell--first-prompt-received)) + (< cumtime 3)) + (sleep-for 0.1) + (setq cumtime (+ cumtime 0.1))))) + (elpy-shell-get-or-create-process)) + +(defun elpy-shell--region-without-indentation (beg end) + "Return the current region as a string, but without indentation." + (if (= beg end) + "" + (let ((region (buffer-substring beg end)) + (indent-level nil) + (indent-tabs-mode nil)) + (with-temp-buffer + (insert region) + (goto-char (point-min)) + (while (< (point) (point-max)) + (cond + ((or (elpy-shell--current-line-only-whitespace-p) + (python-info-current-line-comment-p))) + ((not indent-level) + (setq indent-level (current-indentation))) + ((and indent-level + (< (current-indentation) indent-level)) + (error (message "X%sX" (thing-at-point 'line))))) + ;; (error "Can't adjust indentation, consecutive lines indented less than starting line"))) + (forward-line)) + (indent-rigidly (point-min) + (point-max) + (- indent-level)) + ;; 'indent-rigidly' introduces tabs despite the fact that 'indent-tabs-mode' is nil + ;; 'untabify' fix that + (untabify (point-min) (point-max)) + (buffer-string))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Flash input sent to shell + +;; functions for flashing a region; only flashes when package eval-sexp-fu is +;; loaded and its minor mode enabled +(defun elpy-shell--flash-and-message-region (begin end) + "Displays information about code fragments sent to the shell. + +BEGIN and END refer to the region of the current buffer containing the code being sent. Displays a message with the first line of that region. If `eval-sexp-fu-flash-mode' is active, additionally flashes that region briefly." + (when (> end begin) + (save-excursion + (goto-char begin) + (end-of-line) + (if (<= end (point)) + (message "Sent: %s" (string-trim (thing-at-point 'line))) + (message "Sent: %s..." (string-trim (thing-at-point 'line))))) + (when (bound-and-true-p eval-sexp-fu-flash-mode) + (multiple-value-bind (bounds hi unhi eflash) (eval-sexp-fu-flash (cons begin end)) + (eval-sexp-fu-flash-doit (lambda () t) hi unhi))))) + +;;;;;;;;;;;;;;;;;;; +;; Helper functions + +(defun elpy-shell--current-line-else-or-elif-p () + (eq (string-match-p "\\s-*el\\(?:se:\\|if[^\w]\\)" (thing-at-point 'line)) 0)) + +(defun elpy-shell--current-line-indented-p () + (eq (string-match-p "\\s-+[^\\s-]+" (thing-at-point 'line)) 0)) + +(defun elpy-shell--current-line-only-whitespace-p () + "Whether the current line contains only whitespace characters (or is empty)." + (eq (string-match-p "\\s-*$" (thing-at-point 'line)) 0)) + +(defun elpy-shell--current-line-code-line-p () + (and (not (elpy-shell--current-line-only-whitespace-p)) + (not (python-info-current-line-comment-p)))) + +(defun elpy-shell--current-line-defun-p () + "Whether a function definition starts at the current line." + (eq (string-match-p + "\\s-*\\(?:def\\|async\\s-+def\\)\\s\-" + (thing-at-point 'line)) + 0)) + +(defun elpy-shell--current-line-defclass-p () + "Whether a class definition starts at the current line." + (eq (string-match-p + "\\s-*class\\s\-" + (thing-at-point 'line)) + 0)) + +(defun elpy-shell--skip-to-next-code-line (&optional backwards) + "Move the point to the next line containing code. + +If the current line has code, point is not moved. If BACKWARDS is +non-nil, skips backwards." + (if backwards + (while (and (not (elpy-shell--current-line-code-line-p)) + (not (eq (point) (point-min)))) + (forward-line -1)) + (while (and (not (elpy-shell--current-line-code-line-p)) + (not (eq (point) (point-max)))) + (forward-line)))) + +;;;;;;;;;; +;; Echoing + +(defmacro elpy-shell--with-maybe-echo (body) + `(elpy-shell--with-maybe-echo-output + (elpy-shell--with-maybe-echo-input + ,body))) + +(defmacro elpy-shell--with-maybe-echo-input (body) + "Run BODY so that it adheres `elpy-shell-echo-input' and `elpy-shell-display-buffer'." + `(progn + (elpy-shell--enable-echo) + (prog1 + (if elpy-shell-display-buffer-after-send + (prog1 (progn ,body) + (elpy-shell-display-buffer)) + (cl-flet ((elpy-shell-display-buffer () ())) + (progn ,body))) + (elpy-shell--disable-echo)))) + +(defvar-local elpy-shell--capture-output nil + "Non-nil when the Python shell should capture output for display in the echo area.") + +(defvar-local elpy-shell--captured-output nil + "Current captured output of the Python shell.") + +(defmacro elpy-shell--with-maybe-echo-output (body) + "Run BODY and grab shell output according to `elpy-shell-echo-output' and `elpy-shell-capture-last-multiline-output'." + `(cl-letf (((symbol-function 'python-shell-send-file) + (if elpy-shell-capture-last-multiline-output + (symbol-function 'elpy-shell-send-file) + (symbol-function 'python-shell-send-file)))) + (let* ((process (elpy-shell--ensure-shell-running)) + (process-buf (process-buffer process)) + (shell-visible (or elpy-shell-display-buffer-after-send + (get-buffer-window process-buf)))) + (with-current-buffer process-buf + (setq-local elpy-shell--capture-output + (and elpy-shell-echo-output + (or (not (eq elpy-shell-echo-output 'when-shell-not-visible)) + (not shell-visible))))) + (progn ,body)))) + +(defun elpy-shell--enable-output-filter () + (add-hook 'comint-output-filter-functions 'elpy-shell--output-filter nil t)) + +(defun elpy-shell--output-filter (string) + "Filter used in `elpy-shell--with-maybe-echo-output' to grab output. + +No actual filtering is performed. STRING is the output received +to this point from the process. If `elpy-shell--capture-output' +is set, captures and messages shell output in the echo area (once +complete). Otherwise, does nothing." + ;; capture the output and message it when complete + (when elpy-shell--capture-output + ;; remember the new output + (setq-local elpy-shell--captured-output + (concat elpy-shell--captured-output (ansi-color-filter-apply string))) + + ;; Output ends when `elpy-shell--captured-output' contains + ;; the prompt attached at the end of it. If so, message it. + (when (python-shell-comint-end-of-output-p elpy-shell--captured-output) + (let ((output (substring + elpy-shell--captured-output + 0 (match-beginning 0))) + (message-log-max)) + (if (string-empty-p output) + (message "No output was produced.") + (message "%s" (replace-regexp-in-string "\n\\'" "" output)))) + (setq-local elpy-shell--captured-output nil))) + + ;; return input unmodified + string) + +(defun elpy-shell--insert-and-font-lock (string face &optional no-font-lock) + "Inject STRING into the Python shell buffer." + (let ((from-point (point))) + (insert string) + (if (not no-font-lock) + (add-text-properties from-point (point) + (list 'front-sticky t 'font-lock-face face))))) + +(defun elpy-shell--append-to-shell-output (string &optional no-font-lock prepend-cont-prompt) + "Append the given STRING to the output of the Python shell buffer. + +Unless NO-FONT-LOCK is set, formats STRING as shell input. +Prepends a continuation promt if PREPEND-CONT-PROMPT is set." + (let* ((process (elpy-shell-get-or-create-process)) + (process-buf (process-buffer process)) + (mark-point (process-mark process))) + (with-current-buffer process-buf + (save-excursion + (goto-char mark-point) + (if prepend-cont-prompt + (let* ((column (+ (- (point) (progn (forward-line -1) (end-of-line) (point))) 1)) + (prompt (concat (make-string (max 0 (- column 7)) ? ) "...: ")) + (lines (split-string string "\n"))) + (goto-char mark-point) + (elpy-shell--insert-and-font-lock + (car lines) 'comint-highlight-input no-font-lock) + (if (cdr lines) + ;; no additional newline at end for multiline + (dolist (line (cdr lines)) + (insert "\n") + (elpy-shell--insert-and-font-lock + prompt 'comint-highlight-prompt no-font-lock) + (elpy-shell--insert-and-font-lock + line 'comint-highlight-input no-font-lock)) + ;; but put one for single line + (insert "\n"))) + (elpy-shell--insert-and-font-lock + string 'comint-highlight-input no-font-lock)) + (set-marker (process-mark process) (point)))))) + +(defun elpy-shell--string-head-lines (string n) + "Extract the first N lines from STRING." + (let* ((any "\\(?:.\\|\n\\)") + (line "\\(?:\\(?:.*\n\\)\\|\\(?:.+\\'\\)\\)") + (lines (concat line "\\{" (number-to-string n) "\\}")) + (regexp (concat "\\`" "\\(" lines "\\)"))) + (if (string-match regexp string) + (match-string 1 string) + string))) + +(defun elpy-shell--string-tail-lines (string n) + "Extract the last N lines from STRING." + (let* ((any "\\(?:.\\|\n\\)") + (line "\\(?:\\(?:.*\n\\)\\|\\(?:.+\\'\\)\\)") + (lines (concat line "\\{" (number-to-string n) "\\}")) + (regexp (concat "\\(" lines "\\)" "\\'"))) + (if (string-match regexp string) + (match-string 1 string) + string))) + +(defun elpy-shell--python-shell-send-string-echo-advice (string &optional process msg) + "Advice to enable echoing of input in the Python shell." + (interactive) + (let* ((append-string ; strip setup code from Python shell + (if (string-match "import codecs, os.*__pyfile = codecs.open.*$" string) + (replace-match "" nil nil string) + string)) + (append-string ; here too + (if (string-match "^# -\\*- coding: utf-8 -\\*-\n*$" append-string) + (replace-match "" nil nil append-string) + append-string)) + (append-string ; strip newlines from beginning and white space from end + (string-trim-right + (if (string-match "\\`\n+" append-string) + (replace-match "" nil nil append-string) + append-string))) + (head (elpy-shell--string-head-lines append-string elpy-shell-echo-input-lines-head)) + (tail (elpy-shell--string-tail-lines append-string elpy-shell-echo-input-lines-tail)) + (append-string (if (> (length append-string) (+ (length head) (length tail))) + (concat head "...\n" tail) + append-string))) + + ;; append the modified string to the shell output; prepend a newline for + ;; multi-line strings + (if elpy-shell-echo-input-cont-prompt + (elpy-shell--append-to-shell-output append-string nil t) + (elpy-shell--append-to-shell-output + (concat (if (string-match "\n" append-string) "\n" "") + append-string + "\n"))))) + +(defun elpy-shell--enable-echo () + "Enable input echoing when `elpy-shell-echo-input' is set." + (when elpy-shell-echo-input + (advice-add 'python-shell-send-string + :before 'elpy-shell--python-shell-send-string-echo-advice))) + +(defun elpy-shell--disable-echo () + "Disable input echoing." + (advice-remove 'python-shell-send-string + 'elpy-shell--python-shell-send-string-echo-advice)) + +(defun elpy-shell-send-file (file-name &optional process temp-file-name + delete msg) + """Like `python-shell-send-file' but evaluates last expression separately. + + See `python-shell-send-file' for a description of the + arguments. This function differs in that it breaks up the + Python code in FILE-NAME into statements. If the last statement + is a Python expression, it is evaluated separately in 'eval' + mode. This way, the interactive python shell can capture (and + print) the output of the last expression.""" + (interactive + (list + (read-file-name "File to send: ") ; file-name + nil ; process + nil ; temp-file-name + nil ; delete + t)) ; msg + (let* ((process (or process (python-shell-get-process-or-error msg))) + (encoding (with-temp-buffer + (insert-file-contents + (or temp-file-name file-name)) + (python-info-encoding))) + (file-name (expand-file-name + (or (file-remote-p file-name 'localname) + file-name))) + (temp-file-name (when temp-file-name + (expand-file-name + (or (file-remote-p temp-file-name 'localname) + temp-file-name))))) + (python-shell-send-string + (format + (concat + "import codecs, os, ast;" + "__pyfile = codecs.open('''%s''', encoding='''%s''');" + "__code = __pyfile.read().encode('''%s''');" + "__pyfile.close();" + (when (and delete temp-file-name) + (format "os.remove('''%s''');" temp-file-name)) + "__block = ast.parse(__code, '''%s''', mode='exec');" + "__last = __block.body[-1];" ;; the last statement + "__isexpr = isinstance(__last,ast.Expr);" ;; is it an expression? + "__block.body.pop() if __isexpr else None;" ;; if so, remove it + "exec(compile(__block, '''%s''', mode='exec'));" ;; execute everything else + "eval(compile(ast.Expression(__last.value), '''%s''', mode='eval')) if __isexpr else None" ;; if it was an expression, it has been removed; now evaluate it + ) + (or temp-file-name file-name) encoding encoding file-name file-name file-name) + process))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Navigation commands for sending + +(defun elpy-shell--nav-beginning-of-statement () + "Move the point to the beginning of the current or next Python statement. + +If the current line starts with a statement, behaves exactly like +`python-nav-beginning-of-statement'. If the line is part of a +statement but not a statement itself, goes backwards to the +beginning of the statement. If the current line is not a code +line, skips forward to the next code line and navigates from +there." + (elpy-shell--skip-to-next-code-line) + (python-nav-beginning-of-statement) + (let ((p)) + (while (and (not (eq p (point))) + (elpy-shell--current-line-else-or-elif-p)) + (elpy-nav-backward-block) + (setq p (point))))) + +(defun elpy-shell--nav-end-of-statement () + "Move the point to the end of the current Python statement. + +Assumes that the point is precisely at the beginning of a +statement (e.g., after calling +`elpy-shell--nav-beginning-of-statement')." + (let ((indent (current-column)) + (continue t) + (p)) + (while (and (not (eq p (point))) + continue) + ;; check if there is a another block at the same indentation level + (setq p (point)) + (elpy-nav-forward-block) + + ;; if not, go to the end of the block and done + (if (eq p (point)) + (progn + (python-nav-end-of-block) + (setq continue nil)) + ;; otherwise check if its an else/elif clause + (unless (elpy-shell--current-line-else-or-elif-p) + (forward-line -1) + (elpy-shell--skip-to-next-code-line t) + (setq continue nil))))) + (end-of-line)) + +(defun elpy-shell--nav-beginning-of-top-statement () + "Move the point to the beginning of the current or next top-level statement. + +If the point is within a top-level statement, moves to its +beginning. Otherwise, moves to the beginning of the next top-level +statement." + (interactive) + (elpy-shell--nav-beginning-of-statement) + (let ((p)) + (while (and (not (eq p (point))) + (elpy-shell--current-line-indented-p)) + (forward-line -1) + (elpy-shell--skip-to-next-code-line t) + (elpy-shell--nav-beginning-of-statement)))) + +(defun elpy-shell--nav-beginning-of-def (def-p) + "Move point to the beginning of the current definition. + +DEF-P is a predicate function that decides whether the current +line starts a definition. + +It the current line starts a definition, uses this definition. If +the current line does not start a definition and is a code line, +searches for the definition that contains the current line. +Otherwise, searches for the definition that contains the next +code line. + +If a definition is found, moves point to the start of the +definition and returns t. Otherwise, retains point position and +returns nil." + (if (funcall def-p) + (progn + (python-nav-beginning-of-statement) + t) + (let ((beg-ts (save-excursion + (elpy-shell--skip-to-next-code-line t) + (elpy-shell--nav-beginning-of-top-statement) + (point))) + (orig-p (point)) + (max-indent (save-excursion + (elpy-shell--skip-to-next-code-line) + (- (current-indentation) 1))) + (found)) + (while (and (not found) + (>= (point) beg-ts)) + (if (and (funcall def-p) + (<= (current-indentation) max-indent)) + (setq found t) + (when (elpy-shell--current-line-code-line-p) + (setq max-indent (min max-indent + (- (current-indentation) 1)))) + (forward-line -1))) + (if found + (python-nav-beginning-of-statement) + (goto-char orig-p)) + found))) + +(defun elpy-shell--nav-beginning-of-defun () + "Move point to the beginning of the current function definition. + +If a definition is found, moves point to the start of the +definition and returns t. Otherwise, retains point position and +returns nil. + +See `elpy-shell--nav-beginning-of-def' for details." + (elpy-shell--nav-beginning-of-def 'elpy-shell--current-line-defun-p)) + +(defun elpy-shell--nav-beginning-of-defclass () + "Move point to the beginning of the current class definition. + +If a definition is found, moves point to the start of the +definition and returns t. Otherwise, retains point position and +returns nil. + +See `elpy-shell--nav-beginning-of-def' for details." + (elpy-shell--nav-beginning-of-def 'elpy-shell--current-line-defclass-p)) + +(defun elpy-shell--nav-beginning-of-group () + "Move point to the beginning of the current or next group of top-level statements. + +A sequence of top-level statements is a group if they are not +separated by empty lines. Empty lines within each top-level +statement are ignored. + +If the point is within a top-level statement, moves to the +beginning of the group containing this statement. Otherwise, moves +to the first top-level statement below point." + (elpy-shell--nav-beginning-of-top-statement) + (while (not (or (elpy-shell--current-line-only-whitespace-p) + (eq (point) (point-min)))) + (unless (python-info-current-line-comment-p) + (elpy-shell--nav-beginning-of-top-statement)) + (forward-line -1) + (beginning-of-line)) + (when (elpy-shell--current-line-only-whitespace-p) + (forward-line 1) + (beginning-of-line))) + +;;;;;;;;;;;;;;;;; +;;; Send commands + +(defun elpy-shell-send-statement-and-step () + "Send current or next statement to Python shell and step. + +If the current line is part of a statement, sends this statement. +Otherwise, skips forward to the next code line and sends the +corresponding statement." + (interactive) + (elpy-shell--ensure-shell-running) + (when (not elpy-shell-echo-input) (elpy-shell--append-to-shell-output "\n")) + (let ((beg (progn (elpy-shell--nav-beginning-of-statement) + (save-excursion + (beginning-of-line) + (point)))) + (end (progn (elpy-shell--nav-end-of-statement) (point)))) + (unless (eq beg end) + (elpy-shell--flash-and-message-region beg end) + (elpy-shell--with-maybe-echo + (python-shell-send-string (elpy-shell--region-without-indentation beg end))))) + (python-nav-forward-statement)) + +(defun elpy-shell-send-top-statement-and-step () + "Send the current or next top-level statement to the Python shell and step. + +If the current line is part of a top-level statement, sends this +top-level statement. Otherwise, skips forward to the next code +line and sends the corresponding top-level statement." + (interactive) + (elpy-shell--ensure-shell-running) + (let* ((beg (progn (elpy-shell--nav-beginning-of-top-statement) (point))) + (end (progn (elpy-shell--nav-end-of-statement) (point)))) + (elpy-shell--flash-and-message-region beg end) + (if (string-match-p "\\`[^\n]*\\'" (buffer-substring beg end)) + ;; single line + (elpy-shell-send-statement-and-step) + ;; multiple lines + (elpy-shell--with-maybe-echo + (python-shell-send-region beg end)) + (setq mark-active nil) + (python-nav-forward-statement)))) + +(defun elpy-shell-send-defun-and-step () + "Send the function definition that contains the current line +to the Python shell and steps. + +See `elpy-shell--nav-beginning-of-def' for details." + (interactive) + (if (elpy-shell--nav-beginning-of-defun) + (elpy-shell-send-statement-and-step) + (message "There is no function definition that includes the current line."))) + +(defun elpy-shell-send-defclass-and-step () + "Send the class definition that contains the current line to +the Python shell and steps. + +See `elpy-shell--nav-beginning-of-def' for details." + (interactive) + (if (elpy-shell--nav-beginning-of-defclass) + (elpy-shell-send-statement-and-step) + (message "There is no class definition that includes the current line."))) + +(defun elpy-shell-send-group-and-step () + "Send the current or next group of top-level statements to the Python shell and step. + +A sequence of top-level statements is a group if they are not +separated by empty lines. Empty lines within each top-level +statement are ignored. + +If the point is within a top-level statement, send the group +around this statement. Otherwise, go to the top-level statement +below point and send the group around this statement." + (interactive) + (elpy-shell--ensure-shell-running) + (let* ((beg (progn (elpy-shell--nav-beginning-of-group) (point))) + (end (progn + ;; go forward to end of group + (unless (python-info-current-line-comment-p) + (elpy-shell--nav-end-of-statement)) + (let ((p)) + (while (not (eq p (point))) + (setq p (point)) + (forward-line) + (if (elpy-shell--current-line-only-whitespace-p) + (goto-char p) ;; done + (unless (python-info-current-line-comment-p) + (elpy-shell--nav-end-of-statement))))) + (point)))) + (if (> end beg) + (progn + (elpy-shell--flash-and-message-region beg end) + ;; send the region and jump to next statement + (if (string-match-p "\\`[^\n]*\\'" (buffer-substring beg end)) + ;; single line + (elpy-shell-send-statement-and-step) + ;; multiple lines + (when (not elpy-shell-echo-input) + (elpy-shell--append-to-shell-output "\n")) + (elpy-shell--with-maybe-echo + (python-shell-send-region beg end)) + (python-nav-forward-statement))) + (goto-char (point-max))) + (setq mark-active nil))) + +(defun elpy-shell-send-codecell-and-step () + "Send the current code cell to the Python shell and step. + +Signals an error if the point is not inside a code cell. + +Cell beginnings and cell boundaries can be customized via the +variables `elpy-shell-cell-boundary-regexp' and +`elpy-shell-codecell-beginning-regexp', which see." + (interactive) + (let ((beg (save-excursion + (end-of-line) + (re-search-backward elpy-shell-cell-boundary-regexp nil t) + (beginning-of-line) + (and (string-match-p elpy-shell-codecell-beginning-regexp + (thing-at-point 'line)) + (point)))) + (end (save-excursion + (forward-line) + (if (re-search-forward elpy-shell-cell-boundary-regexp nil t) + (forward-line -1) + (end-of-buffer)) + (end-of-line) + (point)))) + (if beg + (progn + (elpy-shell--flash-and-message-region beg end) + (when (not elpy-shell-echo-input) + (elpy-shell--append-to-shell-output "\n")) + (elpy-shell--with-maybe-echo + (python-shell-send-region beg end)) + (goto-char end) + (python-nav-forward-statement)) + (message "Not in a codecell.")))) + +(defun elpy-shell-send-region-or-buffer-and-step (&optional arg) + "Send the active region or the buffer to the Python shell and step. + +If there is an active region, send that. Otherwise, send the +whole buffer. + +In Emacs 24.3 and later, without prefix argument and when there +is no active region, this will escape the Python idiom of if +__name__ == '__main__' to be false to avoid accidental execution +of code. With prefix argument, this code is executed." + (interactive "P") + (if (use-region-p) + (elpy-shell--flash-and-message-region (region-beginning) (region-end)) + (elpy-shell--flash-and-message-region (point-min) (point-max))) + (elpy-shell--with-maybe-echo + (elpy-shell--send-region-or-buffer-internal arg)) + (if (use-region-p) + (goto-char (region-end)) + (goto-char (point-max)))) + +(defun elpy-shell--send-region-or-buffer-internal (&optional arg) + "Send the active region or the buffer to the Python shell and step. + +If there is an active region, send that. Otherwise, send the +whole buffer. + +In Emacs 24.3 and later, without prefix argument and when there +is no active region, this will escape the Python idiom of if +__name__ == '__main__' to be false to avoid accidental execution +of code. With prefix argument, this code is executed." + (interactive "P") + (elpy-shell--ensure-shell-running) + (when (not elpy-shell-echo-input) (elpy-shell--append-to-shell-output "\n")) + (let ((if-main-regex "^if +__name__ +== +[\"']__main__[\"'] *:") + (has-if-main-and-removed nil)) + (if (use-region-p) + (let ((region (elpy-shell--region-without-indentation + (region-beginning) (region-end)))) + (when (string-match "\t" region) + (message "Region contained tabs, this might cause weird errors")) + (python-shell-send-string region)) + (unless arg + (save-excursion + (goto-char (point-min)) + (setq has-if-main-and-removed (re-search-forward if-main-regex nil t)))) + (python-shell-send-buffer arg)) + (when has-if-main-and-removed + (message (concat "Removed if __name__ == '__main__' construct, " + "use a prefix argument to evaluate."))))) + +(defun elpy-shell-send-buffer-and-step (&optional arg) + "Send entire buffer to Python shell. + +In Emacs 24.3 and later, without prefix argument, this will +escape the Python idiom of if __name__ == '__main__' to be false +to avoid accidental execution of code. With prefix argument, this +code is executed." + (interactive "P") + (let ((p)) + (save-mark-and-excursion + (deactivate-mark) + (elpy-shell-send-region-or-buffer-and-step arg) + (setq p (point))) + (goto-char p))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Send command variations (with/without step; with/without go) + +(defun elpy-shell--send-with-step-go (step-fun step go prefix-arg) + "Run a function with STEP and/or GO. + +STEP-FUN should be a function that sends something to the shell +and moves point to code position right after what has been sent. + +When STEP is nil, keeps point position. When GO is non-nil, +switches focus to Python shell buffer." + (let ((orig (point))) + (setq current-prefix-arg prefix-arg) + (call-interactively step-fun) + (when (not step) + (goto-char orig))) + (when go + (elpy-shell-switch-to-shell))) + +(defmacro elpy-shell--defun-step-go (fun-and-step) + "Defines fun, fun-and-go, fun-and-step-and-go for the given FUN-AND-STEP function." + (let ((name (string-remove-suffix "-and-step" (symbol-name fun-and-step)))) + (list + 'progn + (let ((fun (intern name))) + `(defun ,fun (&optional arg) + ,(concat "Run `" (symbol-name fun-and-step) "' but retain point position.") + (interactive "P") + (elpy-shell--send-with-step-go ',fun-and-step nil nil arg))) + (let ((fun-and-go (intern (concat name "-and-go")))) + `(defun ,fun-and-go (&optional arg) + ,(concat "Run `" (symbol-name fun-and-step) "' but retain point position and switch to Python shell.") + (interactive "P") + (elpy-shell--send-with-step-go ',fun-and-step nil t arg))) + (let ((fun-and-step-and-go (intern (concat name "-and-step-and-go")))) + `(defun ,fun-and-step-and-go (&optional arg) + ,(concat "Run `" (symbol-name fun-and-step) "' and switch to Python shell.") + (interactive "P") + (elpy-shell--send-with-step-go ',fun-and-step t t arg)))))) + +(elpy-shell--defun-step-go elpy-shell-send-statement-and-step) +(elpy-shell--defun-step-go elpy-shell-send-top-statement-and-step) +(elpy-shell--defun-step-go elpy-shell-send-defun-and-step) +(elpy-shell--defun-step-go elpy-shell-send-defclass-and-step) +(elpy-shell--defun-step-go elpy-shell-send-group-and-step) +(elpy-shell--defun-step-go elpy-shell-send-codecell-and-step) +(elpy-shell--defun-step-go elpy-shell-send-region-or-buffer-and-step) +(elpy-shell--defun-step-go elpy-shell-send-buffer-and-step) + +;;;;;;;;;;;;;;;;;;;;;;; +;; Deprecated functions + +(defun elpy-use-ipython (&optional ipython) + "Deprecated; see https://elpy.readthedocs.io/en/latest/ide.html#interpreter-setup" + (error "elpy-use-ipython is deprecated; see https://elpy.readthedocs.io/en/latest/ide.html#interpreter-setup")) +(make-obsolete 'elpy-use-ipython nil "Jan 2017") + +(defun elpy-use-cpython (&optional cpython) + "Deprecated; see https://elpy.readthedocs.io/en/latest/ide.html#interpreter-setup" + (error "elpy-use-cpython is deprecated; see https://elpy.readthedocs.io/en/latest/ide.html#interpreter-setup")) +(make-obsolete 'elpy-use-cpython nil "Jan 2017") + +(provide 'elpy-shell) + +;;; elpy-shell.el ends here diff --git a/elpa/elpy-1.14.1/elpy.el b/elpa/elpy-1.18.0/elpy.el rename from elpa/elpy-1.14.1/elpy.el rename to elpa/elpy-1.18.0/elpy.el --- a/elpa/elpy-1.14.1/elpy.el +++ b/elpa/elpy-1.18.0/elpy.el @@ -4,9 +4,9 @@ ;; Author: Jorgen Schaefer ;; URL: https://github.com/jorgenschaefer/elpy -;; Version: 1.14.1 +;; Version: 1.18.0 ;; Keywords: Python, IDE, Languages, Tools -;; Package-Requires: ((company "0.8.2") (find-file-in-project "3.3") (highlight-indentation "0.5.0") (pyvenv "1.3") (yasnippet "0.8.0") (s "1.10.0")) +;; Package-Requires: ((company "0.9.2") (emacs "24.4") (find-file-in-project "3.3") (highlight-indentation "0.5.0") (pyvenv "1.3") (yasnippet "0.8.0") (s "1.11.0")) ;; This program is free software; you can redistribute it and/or ;; modify it under the terms of the GNU General Public License @@ -42,12 +42,18 @@ (require 'ido) (require 'json) (require 'python) +(require 'subr-x) +(require 'xref nil t) +(require 'cl-lib) ; for `cl-every', `cl-copy-list', `cl-delete-if-not' (require 'elpy-refactor) (require 'elpy-django) +(require 'elpy-profile) +(require 'elpy-shell) (require 'pyvenv) - -(defconst elpy-version "1.14.1" +(require 'find-file-in-project) + +(defconst elpy-version "1.18.0" "The version of the Elpy lisp code.") ;;;;;;;;;;;;;;;;;;;;;; @@ -96,15 +102,30 @@ elpy-module-sane-defaults)) :group 'elpy) -(defcustom elpy-project-ignored-directories ' (".bzr" "CVS" ".git" ".hg" ".svn" - ".tox" "build" "dist" - ".cask") - "Directories ignored by functions working on the whole project." +(defcustom elpy-project-ignored-directories + '(".tox" "build" "dist" ".cask" ".ipynb_checkpoints") + "Directories ignored by functions working on the whole project. +This is in addition to `vc-directory-exclusion-list' +and `grep-find-ignored-directories', as appropriate." :type '(repeat string) :safe (lambda (val) (cl-every #'stringp val)) :group 'elpy) +(defun elpy-project-ignored-directories () + "Compute the list of ignored directories for project. +Combines + `elpy-project-ignored-directories' + `vc-directory-exclusion-list' + `grep-find-ignored-directories'" + (delete-dups + (append elpy-project-ignored-directories + vc-directory-exclusion-list + (if (fboundp 'rgrep-find-ignored-directories) + (rgrep-find-ignored-directories (elpy-project-root)) + (cl-delete-if-not #'stringp (cl-copy-list + grep-find-ignored-directories)))))) + (defcustom elpy-project-root nil "The root of the project the current buffer is in. @@ -158,30 +179,6 @@ :type 'boolean :group 'elpy) -(defcustom elpy-dedicated-shells nil - "Non-nil if Elpy should use dedicated shells. - -Elpy can use a unique python shell for all buffers and support -manually started dedicated shells. Setting this option to non-nil -force the creation of dedicated shells for each buffers." - :type 'boolean - :group 'elpy) - -(defcustom elpy-rpc-backend nil - "Your preferred backend. - -Elpy can use different backends for code introspection. These -need to be installed separately using pip or other mechanisms to -make them available to Python. If you prefer not to do this, you -can use the native backend, which is very limited but does not -have any external requirements." - :type '(choice (const :tag "Rope" "rope") - (const :tag "Jedi" "jedi") - (const :tag "Automatic" nil)) - :safe (lambda (val) - (member val '("rope" "jedi" "native" nil))) - :group 'elpy) - (defcustom elpy-rpc-maximum-buffer-age (* 5 60) "Seconds after which Elpy automatically closes an unused RPC buffer. @@ -219,7 +216,9 @@ :group 'elpy) (defcustom elpy-rpc-python-command (if (equal system-type 'windows-nt) - "pythonw" + (or (executable-find "py") + (executable-find "pythonw") + "python") "python") "The Python interpreter for the RPC backend. @@ -229,6 +228,7 @@ (const :tag "python2" "python2") (const :tag "python3" "python3") (const :tag "pythonw (Python on Windows)" "pythonw") + (const :tag "py (other Python on Windows)" "py") (string :tag "Other")) :safe (lambda (val) (member val '("python" "python2" "python3" "pythonw"))) @@ -301,6 +301,7 @@ (defcustom elpy-test-runner 'elpy-test-discover-runner "The test runner to use to run tests." :type '(choice (const :tag "Unittest Discover" elpy-test-discover-runner) + (const :tag "Green" elpy-test-green-runner) (const :tag "Django Discover" elpy-test-django-runner) (const :tag "Nose" elpy-test-nose-runner) (const :tag "py.test" elpy-test-pytest-runner) @@ -313,6 +314,11 @@ :type '(repeat string) :group 'elpy) +(defcustom elpy-test-green-runner-command '("green") + "The command to use for `elpy-test-green-runner'." + :type '(repeat string) + :group 'elpy) + (defcustom elpy-test-django-runner-command '("django-admin.py" "test" "--noinput") "The command to use for `elpy-test-django-runner'." @@ -346,6 +352,14 @@ :type '(repeat string) :group 'elpy) +(defcustom elpy-test-compilation-function 'compile + "Function used by `elpy-test-run' to run a test command. + +The function should behave similarly to `compile'. Another good +option is `pdb'." + :type 'string + :group 'elpy) + (defcustom elpy-rgrep-file-pattern "*.py" "FILES to use for `elpy-rgrep-symbol'." :type 'string @@ -363,9 +377,6 @@ ;;;;;;;;;;;;; ;;; Elpy Mode -(defvar elpy--shell-last-py-buffer nil - "Help keep track of python buffer when changing to pyshell.") - (defvar elpy-refactor-map (let ((map (make-sparse-keymap "Refactor"))) (define-key map (kbd "i") (cons (format "%smport fixup" @@ -412,7 +423,7 @@ (define-key map (kbd "") 'elpy-open-and-indent-line-below) (define-key map (kbd "") 'elpy-open-and-indent-line-above) - (define-key map (kbd "") 'elpy-shell-send-current-statement) + (define-key map (kbd "") 'elpy-shell-send-statement-and-step) (define-key map (kbd "") 'elpy-nav-forward-block) (define-key map (kbd "") 'elpy-nav-backward-block) @@ -424,9 +435,49 @@ (define-key map (kbd "") 'elpy-nav-indent-shift-left) (define-key map (kbd "") 'elpy-nav-indent-shift-right) - (define-key map (kbd "M-.") 'elpy-goto-definition) - (define-key map (kbd "C-x 4 M-.") 'elpy-goto-definition-other-window) - (define-key map (kbd "M-TAB") 'elpy-company-backend) + (if (not (fboundp 'xref-find-definitions)) + (define-key map (kbd "M-.") 'elpy-goto-definition)) + (if (not (fboundp 'xref-find-definitions-other-window)) + (define-key map (kbd "C-x 4 M-.") 'elpy-goto-definition-other-window) + (define-key map (kbd "C-x 4 M-.") 'xref-find-definitions-other-window)) + (if (fboundp 'xref-pop-marker-stack) + (define-key map (kbd "M-*") 'xref-pop-marker-stack)) + + (define-key map (kbd "M-TAB") 'elpy-company-backend) + + ;; key bindings for elpy-shell + (define-key map (kbd "C-c C-y e") 'elpy-shell-send-statement) + (define-key map (kbd "C-c C-y E") 'elpy-shell-send-statement-and-go) + (define-key map (kbd "C-c C-y s") 'elpy-shell-send-top-statement) + (define-key map (kbd "C-c C-y S") 'elpy-shell-send-top-statement-and-go) + (define-key map (kbd "C-c C-y f") 'elpy-shell-send-defun) + (define-key map (kbd "C-c C-y F") 'elpy-shell-send-defun-and-go) + (define-key map (kbd "C-c C-y c") 'elpy-shell-send-defclass) + (define-key map (kbd "C-c C-y C") 'elpy-shell-send-defclass-and-go) + (define-key map (kbd "C-c C-y g") 'elpy-shell-send-group) + (define-key map (kbd "C-c C-y G") 'elpy-shell-send-group-and-go) + (define-key map (kbd "C-c C-y w") 'elpy-shell-send-codecell) + (define-key map (kbd "C-c C-y W") 'elpy-shell-send-codecell-and-go) + (define-key map (kbd "C-c C-y r") 'elpy-shell-send-region-or-buffer) + (define-key map (kbd "C-c C-y R") 'elpy-shell-send-region-or-buffer-and-go) + (define-key map (kbd "C-c C-y b") 'elpy-shell-send-buffer) + (define-key map (kbd "C-c C-y B") 'elpy-shell-send-buffer-and-go) + (define-key map (kbd "C-c C-y C-e") 'elpy-shell-send-statement-and-step) + (define-key map (kbd "C-c C-y C-S-E") 'elpy-shell-send-statement-and-step-and-go) + (define-key map (kbd "C-c C-y C-s") 'elpy-shell-send-top-statement-and-step) + (define-key map (kbd "C-c C-y C-S-S") 'elpy-shell-send-top-statement-and-step-and-go) + (define-key map (kbd "C-c C-y C-f") 'elpy-shell-send-defun-and-step) + (define-key map (kbd "C-c C-y C-S-F") 'elpy-shell-send-defun-and-step-and-go) + (define-key map (kbd "C-c C-y C-c") 'elpy-shell-send-defclass-and-step) + (define-key map (kbd "C-c C-y C-S-C") 'elpy-shell-send-defclass-and-step-and-go) + (define-key map (kbd "C-c C-y C-g") 'elpy-shell-send-group-and-step) + (define-key map (kbd "C-c C-y C-S-G") 'elpy-shell-send-group-and-step-and-go) + (define-key map (kbd "C-c C-y C-w") 'elpy-shell-send-codecell-and-step) + (define-key map (kbd "C-c C-y C-S-W") 'elpy-shell-send-codecell-and-step-and-go) + (define-key map (kbd "C-c C-y C-r") 'elpy-shell-send-region-or-buffer-and-step) + (define-key map (kbd "C-c C-y C-S-R") 'elpy-shell-send-region-or-buffer-and-step-and-go) + (define-key map (kbd "C-c C-y C-b") 'elpy-shell-send-buffer-and-step) + (define-key map (kbd "C-c C-y C-S-B") 'elpy-shell-send-buffer-and-step-and-go) map) "Key map for the Emacs Lisp Python Environment.") @@ -515,12 +566,14 @@ (elpy-modules-global-init) (define-key inferior-python-mode-map (kbd "C-c C-z") 'elpy-shell-switch-to-buffer) (add-hook 'python-mode-hook 'elpy-mode) - (add-hook 'pyvenv-post-activate-hooks 'elpy-rpc--disconnect)) + (add-hook 'pyvenv-post-activate-hooks 'elpy-rpc--disconnect) + (add-hook 'inferior-python-mode-hook 'elpy-shell--enable-output-filter)) (defun elpy-disable () "Disable Elpy in all future Python buffers." (interactive) (remove-hook 'python-mode-hook 'elpy-mode) + (remove-hook 'inferior-python-mode-hook 'elpy-shell--enable-output-filter) (elpy-modules-global-stop)) ;;;###autoload @@ -536,6 +589,8 @@ :lighter " Elpy" (when (not (derived-mode-p 'python-mode)) (error "Elpy only works with `python-mode'")) + (when (boundp 'xref-backend-functions) + (add-hook 'xref-backend-functions #'elpy--xref-backend nil t)) (cond (elpy-mode (elpy-modules-buffer-init)) @@ -633,14 +688,6 @@ config['rope_latest'] = latest('rope') try: - import importmagic - config['importmagic_version'] = importmagic.__version__ - config['importmagic_latest'] = latest('importmagic', config['importmagic_version']) -except: - config['importmagic_version'] = None - config['importmagic_latest'] = latest('importmagic') - -try: import autopep8 config['autopep8_version'] = autopep8.__version__ config['autopep8_latest'] = latest('autopep8', config['autopep8_version']) @@ -820,34 +867,13 @@ ;; Requested backend unavailable (when (and (gethash "python_rpc_executable" config) - (or (and (equal elpy-rpc-backend "rope") - (not (gethash "rope_version" config))) - (and (equal elpy-rpc-backend "jedi") - (not (gethash "jedi_version" config))))) + (not (gethash "jedi_version" config))) (elpy-insert--para - "You requested Elpy to use the backend " elpy-rpc-backend ", " - "but the Python interpreter could not load that module. Make " - "sure the module is installed, or change the value of " - "`elpy-rpc-backend' below to one of the available backends.\n") + "The jedi package is not available. Completion and code navigation will" + " not work.\n") (insert "\n") (widget-create 'elpy-insert--pip-button - :package (if (equal elpy-rpc-backend "rope") - rope-pypi-package - "jedi")) - (insert "\n\n")) - - ;; No backend available. - (when (and (gethash "python_rpc_executable" config) - (and (not elpy-rpc-backend) - (not (gethash "rope_version" config)) - (not (gethash "jedi_version" config)))) - (elpy-insert--para - "There is no backend available. Please install either Rope or Jedi." - "See https://github.com/jorgenschaefer/elpy/wiki/FAQ#q-should-i-use-rope-or-jedi for guidance.\n") - (insert "\n") - (widget-create 'elpy-insert--pip-button :package rope-pypi-package) - (insert "\n") - (widget-create 'elpy-insert--pip-button :package "jedi") + :package "jedi") (insert "\n\n")) ;; Newer version of Rope available @@ -870,25 +896,6 @@ :package "jedi" :upgrade t) (insert "\n\n")) - ;; No importmagic available - (when (not (gethash "importmagic_version" config)) - (elpy-insert--para - "The importmagic package is not available. Commands using this will " - "not work.\n") - (insert "\n") - (widget-create 'elpy-insert--pip-button - :package "importmagic") - (insert "\n\n")) - - ;; Newer version of importmagic available - (when (and (gethash "importmagic_version" config) - (gethash "importmagic_latest" config)) - (elpy-insert--para - "There is a newer version of the importmagic package available.\n") - (insert "\n") - (widget-create 'elpy-insert--pip-button - :package "importmagic" :upgrade t) - (insert "\n\n")) ;; No autopep8 available (when (not (gethash "autopep8_version" config)) @@ -1016,8 +1023,6 @@ (jedi-latest (gethash "jedi_latest" config)) (rope-version (gethash "rope_version" config)) (rope-latest (gethash "rope_latest" config)) - (importmagic-version (gethash "importmagic_version" config)) - (importmagic-latest (gethash "importmagic_latest" config)) (autopep8-version (gethash "autopep8_version" config)) (autopep8-latest (gethash "autopep8_latest" config)) (yapf-version (gethash "yapf_version" config)) @@ -1070,9 +1075,6 @@ ("Rope" . ,(elpy-config--package-link "rope" rope-version rope-latest)) - ("Importmagic" . ,(elpy-config--package-link "importmagic" - importmagic-version - importmagic-latest)) ("Autopep8" . ,(elpy-config--package-link "autopep8" autopep8-version autopep8-latest)) @@ -1102,7 +1104,7 @@ (cdr row) "\n")))) -(defun elpy-config--package-link (name version latest) +(defun elpy-config--package-link (_name version latest) "Return a string detailing a Python package. NAME is the PyPI name of the package. VERSION is the currently @@ -1186,7 +1188,7 @@ (widget-put widget :command command) (insert command))) -(defun elpy-insert--pip-button-action (widget &optional event) +(defun elpy-insert--pip-button-action (widget &optional _event) "The :action option for the pip button widget." (async-shell-command (widget-get widget :command))) @@ -1326,24 +1328,19 @@ REGEXP defaults to the symbol at point, or the current region if active. -With a prefix argument, always prompt for a string to search -for." +With a prefix argument, always prompt for a string to search for." (interactive (list - (cond - (current-prefix-arg - (read-from-minibuffer "Search in project for regexp: ")) - ((use-region-p) - (buffer-substring-no-properties (region-beginning) - (region-end))) - (t - (let ((symbol (thing-at-point 'symbol))) - (if symbol - (format "\\<%s\\>" symbol) - (read-from-minibuffer "Search in project for regexp: "))))))) + (let ((symbol + (if (use-region-p) + (buffer-substring-no-properties (region-beginning) + (region-end)) + (thing-at-point 'symbol)))) + (if (and symbol (not current-prefix-arg)) + (concat "\\<" symbol "\\>") + (read-from-minibuffer "Search in project for regexp: " symbol))))) (grep-compute-defaults) - (let ((grep-find-ignored-directories (append elpy-project-ignored-directories - grep-find-ignored-directories))) + (let ((grep-find-ignored-directories (elpy-project-ignored-directories))) (rgrep regexp elpy-rgrep-file-pattern (or (elpy-project-root) @@ -1359,6 +1356,35 @@ ;;;;;;;;;;;;;;;;;;;;;; ;;; Find Project Files +(defcustom elpy-ffip-prune-patterns '() + "Elpy-specific extension of `ffip-prune-patterns'. +This is in addition to `elpy-project-ignored-directories' +and `completion-ignored-extensions'. +The final value of `ffip-prune-patterns' used is computed +by the eponymous function `elpy-ffip-prune-patterns'." + :type '(repeat string) + :safe (lambda (val) + (cl-every #'stringp val)) + :group 'elpy) + +(defun elpy-ffip-prune-patterns () + "Compute `ffip-prune-patterns' from other variables. +This combines + `elpy-ffip-prune-patterns' + `elpy-project-ignored-directories' + `completion-ignored-extensions' + `ffip-prune-patterns'." + (delete-dups + (nconc + (mapcar (lambda (dir) (concat "*/" dir "/*")) + elpy-project-ignored-directories) + (mapcar (lambda (ext) (if (s-ends-with? "/" ext) + (concat "*" ext "*") + (concat "*" ext))) + completion-ignored-extensions) + (cl-copy-list elpy-ffip-prune-patterns) + (cl-copy-list ffip-prune-patterns)))) + (defun elpy-find-file (&optional dwim) "Efficiently find a file in the current project. @@ -1393,7 +1419,7 @@ (find-file test-file) (elpy-find-file nil)))) (t - (let ((ffip-prune-patterns elpy-project-ignored-directories) + (let ((ffip-prune-patterns (elpy-ffip-prune-patterns)) (ffip-project-root (or (elpy-project-root) default-directory)) ;; Set up ido to use vertical file lists. @@ -1484,259 +1510,6 @@ (setq path (file-name-directory path))))) nil)) -;;;;;;;;;;;;;;;;;;;;; -;;; Interactive Shell - -(defun elpy-use-ipython (&optional ipython) - "Set defaults to use IPython instead of the standard interpreter. - -With prefix arg, prompt for the command to use." - (interactive (list (when current-prefix-arg - (read-file-name "IPython command: ")))) - (when (not ipython) - (setq ipython "ipython")) - (when (not (executable-find ipython)) - (error "Command %S not found" ipython)) - (cond - ;; Emacs 24 until 24.3 - ((boundp 'python-python-command) - (setq python-python-command ipython)) - ;; Emacs 24.3 - ((and (version<= "24.3" emacs-version) - (not (boundp 'python-shell-interpreter-interactive-arg))) - ;; This is from the python.el commentary. - ;; Settings for IPython 0.11: - (setq python-shell-interpreter ipython - python-shell-interpreter-args "" - python-shell-prompt-regexp "In \\[[0-9]+\\]: " - python-shell-prompt-output-regexp "Out\\[[0-9]+\\]: " - python-shell-completion-setup-code - "from IPython.core.completerlib import module_completion" - python-shell-completion-module-string-code - "';'.join(module_completion('''%s'''))\n" - python-shell-completion-string-code - "';'.join(get_ipython().Completer.all_completions('''%s'''))\n")) - ;; Emacs 24.4 - ((boundp 'python-shell-interpreter-interactive-arg) - (setq python-shell-interpreter ipython - python-shell-interpreter-args "-i") - ;; Windows requires some special handling here, see #422 - (let ((exe "C:\\Python27\\python.exe") - (ipython_py "C:\\Python27\\Scripts\\ipython-script.py")) - (when (and (eq system-type 'windows-nt) - (file-exists-p exe) - (file-exists-p ipython_py)) - (setq python-shell-interpreter exe - python-shell-interpreter-args "-i " + ipython_py)))) - (t - (error "I don't know how to set ipython settings for this Emacs")))) - -(defun elpy-use-cpython (&optional cpython) - "Set defaults to use the standard interpreter instead of IPython. - -With prefix arg, prompt for the command to use." - (interactive (list (when current-prefix-arg - (read-file-name "Python command: ")))) - (when (not cpython) - (setq cpython "python")) - (when (not (executable-find cpython)) - (error "Command %S not found" cpython)) - (cond - ;; Emacs 24 until 24.3 - ((boundp 'python-python-command) - (setq python-python-command cpython)) - ;; Emacs 24.3 and onwards. - ((and (version<= "24.3" emacs-version) - (not (boundp 'python-shell-interpreter-interactive-arg))) - (setq python-shell-interpreter cpython - python-shell-interpreter-args "-i" - python-shell-prompt-regexp ">>> " - python-shell-prompt-output-regexp "" - python-shell-completion-setup-code - "try: - import readline -except ImportError: - def __COMPLETER_all_completions(text): [] -else: - import rlcompleter - readline.set_completer(rlcompleter.Completer().complete) - def __COMPLETER_all_completions(text): - import sys - completions = [] - try: - i = 0 - while True: - res = readline.get_completer()(text, i) - if not res: break - i += 1 - completions.append(res) - except NameError: - pass - return completions" - python-shell-completion-module-string-code "" - python-shell-completion-string-code - "';'.join(__COMPLETER_all_completions('''%s'''))\n")) - ;; Emacs 24.4 - ((boundp 'python-shell-interpreter-interactive-arg) - (setq python-shell-interpreter cpython - python-shell-interpreter-args "-i")) - (t - (error "I don't know how to set ipython settings for this Emacs")))) - -(defun elpy-shell-display-buffer () - "Display inferior Python process buffer." - (display-buffer (process-buffer (elpy-shell-get-or-create-process)) - nil - 'visible)) - -(defun elpy-shell-send-region-or-buffer (&optional arg) - "Send the active region or the buffer to the Python shell. - -If there is an active region, send that. Otherwise, send the -whole buffer. - -In Emacs 24.3 and later, without prefix argument, this will -escape the Python idiom of if __name__ == '__main__' to be false -to avoid accidental execution of code. With prefix argument, this -code is executed." - (interactive "P") - ;; Ensure process exists - (elpy-shell-get-or-create-process) - (let ((if-main-regex "^if +__name__ +== +[\"']__main__[\"'] *:") - (has-if-main nil)) - (if (use-region-p) - (let ((region (elpy-shell--region-without-indentation - (region-beginning) (region-end)))) - (setq has-if-main (string-match if-main-regex region)) - (when (string-match "\t" region) - (message "Region contained tabs, this might cause weird errors")) - (python-shell-send-string region)) - (save-excursion - (goto-char (point-min)) - (setq has-if-main (re-search-forward if-main-regex nil t))) - (python-shell-send-buffer arg)) - (elpy-shell-display-buffer) - (when has-if-main - (message (concat "Removed if __main__ == '__main__' construct, " - "use a prefix argument to evaluate."))))) - -(defun elpy-shell-send-current-statement () - "Send current statement to Python shell." - (interactive) - (let ((beg (python-nav-beginning-of-statement)) - (end (python-nav-end-of-statement))) - (elpy-shell-get-or-create-process) - (python-shell-send-string (buffer-substring beg end))) - (elpy-shell-display-buffer) - (python-nav-forward-statement)) - -(defun elpy-shell-switch-to-shell () - "Switch to inferior Python process buffer." - (interactive) - (setq elpy--shell-last-py-buffer (buffer-name)) - (pop-to-buffer (process-buffer (elpy-shell-get-or-create-process)))) - -(defun elpy-shell-switch-to-buffer () - "Switch from inferior Python process buffer to recent Python buffer." - (interactive) - (pop-to-buffer elpy--shell-last-py-buffer)) - -(defun elpy-shell-kill (&optional kill-buff) - "Kill the current python shell. - -If `elpy-dedicated-shells' is non-nil, -kill the current buffer dedicated shell. - -If KILL-BUFF is non-nil, also kill the associated buffer." - (interactive) - (let ((shell-buffer (python-shell-get-buffer))) - (cond - (shell-buffer - (delete-process shell-buffer) - (when kill-buff - (kill-buffer shell-buffer)) - (message "Killed %s shell" shell-buffer)) - (t - (message "No python shell to kill"))))) - -(defun elpy-shell-kill-all (&optional kill-buffers ask-for-each-one) - "Kill all active python shells. - -If KILL-BUFFERS is non-nil, also kill the associated buffers. -If ASK-FOR-EACH-ONE is non-nil, ask before killing each python process. -" - (interactive) - (let ((python-buffer-list ())) - ;; Get active python shell buffers and kill inactive ones (if asked) - (loop for buffer being the buffers do - (when (string-match "^\*Python\\\[.*\\]\*$" (buffer-name buffer)) - (if (get-buffer-process buffer) - (push buffer python-buffer-list) - (when kill-buffers - (kill-buffer buffer))))) - (cond - ;; Ask for each buffers and kill - ((and python-buffer-list ask-for-each-one) - (loop for buffer in python-buffer-list do - (when (y-or-n-p (format "Kill %s ?" buffer)) - (delete-process buffer) - (when kill-buffers - (kill-buffer buffer))))) - ;; Ask and kill every buffers - (python-buffer-list - (if (y-or-n-p (format "Kill %s python shells ?" (length python-buffer-list))) - (loop for buffer in python-buffer-list do - (delete-process buffer) - (when kill-buffers - (kill-buffer buffer))))) - ;; No shell to close - (t - (message "No python shell to close"))))) - -(defun elpy-shell-get-or-create-process () - "Get or create an inferior Python process for current buffer and return it." - (let* ((bufname (format "*%s*" (python-shell-get-process-name nil))) - (dedbufname (format "*%s*" (python-shell-get-process-name t))) - (proc (get-buffer-process bufname)) - (dedproc (get-buffer-process dedbufname))) - (if elpy-dedicated-shells - (if dedproc - dedproc - (run-python (python-shell-parse-command) t) - (get-buffer-process dedbufname)) - (if dedproc - dedproc - (if proc - proc - (run-python (python-shell-parse-command)) - (get-buffer-process bufname)))))) - -(defun elpy-shell--region-without-indentation (beg end) - "Return the current region as a string, but without indentation." - (if (= beg end) - "" - (let ((region (buffer-substring beg end)) - (indent-level nil) - (indent-tabs-mode nil)) - (with-temp-buffer - (insert region) - (goto-char (point-min)) - (while (< (point) (point-max)) - (cond - ((and (not indent-level) - (not (looking-at "[ \t]*$"))) - (setq indent-level (current-indentation))) - ((and indent-level - (not (looking-at "[ \t]*$")) - (< (current-indentation) - indent-level)) - (error "Can't adjust indentation, consecutive lines indented less than starting line"))) - (forward-line)) - (indent-rigidly (point-min) - (point-max) - (- indent-level)) - (buffer-string))))) - ;;;;;;;;;;;;;;;;; ;;; Syntax Checks @@ -1758,11 +1531,11 @@ (buffer-file-name)))) (extra-args (if whole-project-p (concat - (if (equal python-check-command "pylint") + (if (string-match "pylint$" python-check-command) " --ignore=" " --exclude=") (mapconcat #'identity - elpy-project-ignored-directories + (elpy-project-ignored-directories) ",")) ""))) (compilation-start (concat python-check-command @@ -1770,7 +1543,7 @@ (shell-quote-argument file-name-or-directory) extra-args) nil - (lambda (mode-name) + (lambda (_mode-name) "*Python Check*")))) ;;;;;;;;;;;;;; @@ -1788,6 +1561,14 @@ (elpy-goto-location (car location) (cadr location)) (error "No definition found")))) +(defun elpy-goto-assignment () + "Go to the assignment of the symbol at point, if found." + (interactive) + (let ((location (elpy-rpc-get-assignment))) + (if location + (elpy-goto-location (car location) (cadr location)) + (error "No assignment found")))) + (defun elpy-goto-definition-other-window () "Go to the definition of the symbol at point in other window, if found." (interactive) @@ -1796,6 +1577,14 @@ (elpy-goto-location (car location) (cadr location) 'other-window) (error "No definition found")))) +(defun elpy-goto-assignment-other-window () + "Go to the assignment of the symbol at point in other window, if found." + (interactive) + (let ((location (elpy-rpc-get-assignment))) + (if location + (elpy-goto-location (car location) (cadr location) 'other-window) + (error "No assignment found")))) + (defun elpy-goto-location (filename offset &optional other-window-p) "Show FILENAME at OFFSET to the user. @@ -1884,15 +1673,21 @@ (defun elpy-nav-move-line-or-region-down (&optional beg end) "Move the current line or active region down." - (interactive "r") - (if (use-region-p) + (interactive + (if (use-region-p) + (list (region-beginning) (region-end)) + (list nil nil))) + (if beg (elpy--nav-move-region-vertically beg end 1) (elpy--nav-move-line-vertically 1))) (defun elpy-nav-move-line-or-region-up (&optional beg end) "Move the current line or active region down." - (interactive "r") - (if (use-region-p) + (interactive + (if (use-region-p) + (list (region-beginning) (region-end)) + (list nil nil))) + (if beg (elpy--nav-move-region-vertically beg end -1) (elpy--nav-move-line-vertically -1))) @@ -1973,7 +1768,7 @@ (when (not (= (point) (line-beginning-position))) (end-of-line)))) -(defun elpy-nav-indent-shift-right (&optional count) +(defun elpy-nav-indent-shift-right (&optional _count) "Shift current line by COUNT columns to the right. COUNT defaults to `python-indent-offset'. @@ -1985,7 +1780,7 @@ (python-indent-shift-right (region-beginning) (region-end) current-prefix-arg)) (python-indent-shift-right (line-beginning-position) (line-end-position) current-prefix-arg))) -(defun elpy-nav-indent-shift-left (&optional count) +(defun elpy-nav-indent-shift-left (&optional _count) "Shift current line by COUNT columns to the left. COUNT defaults to `python-indent-offset'. @@ -2109,11 +1904,12 @@ (defun elpy-test-run (working-directory command &rest args) "Run COMMAND with ARGS in WORKING-DIRECTORY as a test command." (let ((default-directory working-directory)) - (compile (mapconcat #'shell-quote-argument + (funcall elpy-test-compilation-function + (mapconcat #'shell-quote-argument (cons command args) " ")))) -(defun elpy-test-discover-runner (top file module test) +(defun elpy-test-discover-runner (top _file module test) "Test the project using the python unittest discover runner. This requires Python 2.7 or later." @@ -2128,7 +1924,19 @@ (list test))))) (put 'elpy-test-discover-runner 'elpy-test-runner-p t) -(defun elpy-test-django-runner (top file module test) +(defun elpy-test-green-runner (top _file module test) + "Test the project using the green runner." + (interactive (elpy-test-at-point)) + (let* ((test (cond + (test (format "%s.%s" module test)) + (module module))) + (command (if test + (append elpy-test-green-runner-command (list test)) + elpy-test-green-runner-command))) + (apply #'elpy-test-run top command))) +(put 'elpy-test-green-runner 'elpy-test-runner-p t) + +(defun elpy-test-django-runner (top _file module test) "Test the project using the Django discover runner, or with manage.py if elpy-test-django-with-manage is true. @@ -2153,10 +1961,20 @@ module)))) (apply #'elpy-test-run top - elpy-test-django-runner-command))) + (append + (if elpy-test-django-with-manage + (append (list (concat (expand-file-name + (locate-dominating-file + (if (elpy-project-root) + (elpy-project-root) + ".") + (car elpy-test-django-runner-manage-command))) + (car elpy-test-django-runner-manage-command))) + (cdr elpy-test-django-runner-manage-command)) + elpy-test-django-runner-command))))) (put 'elpy-test-django-runner 'elpy-test-runner-p t) -(defun elpy-test-nose-runner (top file module test) +(defun elpy-test-nose-runner (top _file module test) "Test the project using the nose test runner. This requires the nose package to be installed." @@ -2173,7 +1991,7 @@ elpy-test-nose-runner-command))) (put 'elpy-test-nose-runner 'elpy-test-runner-p t) -(defun elpy-test-trial-runner (top file module test) +(defun elpy-test-trial-runner (top _file module test) "Test the project using Twisted's Trial test runner. This requires the twisted-core package to be installed." @@ -2221,8 +2039,7 @@ If there is no documentation for the symbol at point, or if a prefix argument is given, prompt for a symbol from the user." (interactive) - (let ((symbol-at-point nil) - (doc nil)) + (let ((doc nil)) (when (not current-prefix-arg) (setq doc (elpy-rpc-get-docstring)) (when (not doc) @@ -2299,78 +2116,16 @@ (insert rep) (delete-region beg end)))) - -;;;;;;;;;;;;;;;;;;;;;;; -;;; Import manipulation - -(defun elpy-importmagic--add-import-read-args (&optional object prompt ask-for-alias) - (let* ((default-object (save-excursion - (let ((bounds (with-syntax-table python-dotty-syntax-table - (bounds-of-thing-at-point 'symbol)))) - (if bounds (buffer-substring (car bounds) (cdr bounds)) "")))) - (object-to-import (or object (read-string "Object to import: " default-object))) - (possible-imports (elpy-rpc "get_import_symbols" (list buffer-file-name - (elpy-rpc--buffer-contents) - object-to-import))) - (statement-prompt (or prompt "New import statement: "))) - (cond - ;; An elpy warning (i.e. index not ready) is returned as a string. - ((stringp possible-imports) - "") - ;; If there is no candidate, we exit immediately. - ((null possible-imports) - (message "No import candidate found") - "") - ;; We have some candidates, let the user choose one. - (t - (let* ((first-choice (car possible-imports)) - (user-choice (completing-read statement-prompt possible-imports)) - (alias (if ask-for-alias (read-string (format "Import \"%s\" as: " object-to-import)) ""))) - (concat (if (equal user-choice "") first-choice user-choice) - (if (not (or (equal alias "") (equal alias object-to-import))) (concat " as " alias)))))))) - -(defun elpy-importmagic-add-import (&optional statement ask-for-alias) - "Prompt to import thing at point, show possible imports and add selected import. - -With prefix arg, also ask for an import alias." - (interactive "i\nP") - (let* ((statement (or statement (elpy-importmagic--add-import-read-args nil nil ask-for-alias))) - (res (elpy-rpc "add_import" (list buffer-file-name - (elpy-rpc--buffer-contents) - statement)))) - (unless (equal statement "") - (elpy-buffer--replace-block res)))) - +;;;;;;;;;;;;;;;;;;;;; +;;; Importmagic - make obsolete + +(defun elpy-importmagic-add-import () + (interactive)) (defun elpy-importmagic-fixup () - "Query for new imports of unresolved symbols, and remove unreferenced imports. - -Also sort the imports in the import statement blocks." - (interactive) - ;; get all unresolved names, and interactively add imports for them - (let* ((res (elpy-rpc "get_unresolved_symbols" (list buffer-file-name - (elpy-rpc--buffer-contents)))) - (unresolved-aliases (list))) - (unless (stringp res) - (if (null res) (message "No imports to add.")) - (dolist (object res) - (let* ((prompt (format "How to import \"%s\": " object)) - (choice (elpy-importmagic--add-import-read-args object prompt nil))) - (when (equal choice "") - (add-to-list 'unresolved-aliases (car (split-string object "\\.")))) - (elpy-importmagic-add-import choice nil)))) - ;; ask for unresolved aliases real names and add import for them - (dolist (alias unresolved-aliases) - (let* ((object (read-string (format "Real name of \"%s\" alias: " alias nil))) - (prompt (format "How to import \"%s\": " object)) - (choice (concat - (elpy-importmagic--add-import-read-args object prompt nil) - (concat " as " alias)))) - (elpy-importmagic-add-import choice nil)))) - ;; now get a new import statement block (this also sorts) - (let* ((res (elpy-rpc "remove_unreferenced_imports" (list buffer-file-name - (elpy-rpc--buffer-contents))))) - (unless (stringp res) - (elpy-buffer--replace-block res)))) + (interactive)) + +(make-obsolete 'elpy-importmagic-add-import "support for importmagic has been dropped.") +(make-obsolete 'elpy-importmagic-fixup "support for importmagic has been dropped.") ;;;;;;;;;;;;;;;;;;;;; ;;; Code reformatting @@ -2411,10 +2166,12 @@ ;; Vector instead of list, json.el in Emacs 24.3 and before ;; breaks for single-element lists of alists. (let ((new-block (elpy-rpc method (vector (elpy-rpc--buffer-contents)))) - (beg (point-min)) (end (point-max))) + (beg (point-min)) + (end (point-max))) (elpy-buffer--replace-region beg end new-block) - (forward-line (1- line)) - (forward-char col))))) + (when (bobp) + (forward-line (1- line)) + (forward-char col)))))) ;;;;;;;;;;;;;; ;;; Multi-Edit @@ -2453,8 +2210,8 @@ (delete-overlay ov)) (setq elpy-multiedit-overlays nil)) -(defun elpy-multiedit--overlay-changed (ov after-change beg end - &optional pre-change-length) +(defun elpy-multiedit--overlay-changed (ov after-change _beg _end + &optional _pre-change-length) "Called for each overlay that changes. This updates all other overlays." @@ -2573,7 +2330,7 @@ (elpy-insert--para "The symbol '" name "' was found in multiple files. Editing " "all locations:\n\n") - (maphash (lambda (key value) + (maphash (lambda (key _value) (when (not (member key filenames)) (setq filenames (cons key filenames)))) locations) @@ -2753,6 +2510,9 @@ (defvar elpy-rpc--last-error-popup nil "The last time an error popup happened.") +(defvar elpy-rpc--jedi-available nil + "Whether jedi is available or not.") + (defun elpy-rpc (method params &optional success error) "Call METHOD with PARAMS in the backend. @@ -2870,9 +2630,6 @@ (defun elpy-rpc--open (library-root python-command) "Start a new RPC process and return the associated buffer." - (when (and elpy-rpc-backend - (not (stringp elpy-rpc-backend))) - (error "`elpy-rpc-backend' should be nil or a string.")) (elpy-rpc--cleanup-buffers) (let* ((full-python-command (executable-find python-command)) (name (format " *elpy-rpc [project:%s python:%s]*" @@ -2903,14 +2660,10 @@ (set-process-query-on-exit-flag proc nil) (set-process-sentinel proc #'elpy-rpc--sentinel) (set-process-filter proc #'elpy-rpc--filter) - (elpy-rpc-init elpy-rpc-backend library-root + (elpy-rpc-init library-root (lambda (result) - (let ((backend (cdr (assq 'backend result)))) - (when (and elpy-rpc-backend - (not (equal backend elpy-rpc-backend))) - (elpy-config-error - "Can't set backend %s, using %s instead" - elpy-rpc-backend backend)))))) + (setq elpy-rpc--jedi-available + (cdr (assq 'jedi_available result)))))) new-elpy-rpc-buffer)) (defun elpy-rpc--cleanup-buffers () @@ -2940,7 +2693,7 @@ (buffer-live-p buffer)) (with-current-buffer buffer (when elpy-rpc--backend-callbacks - (maphash (lambda (call-id promise) + (maphash (lambda (_call-id promise) (ignore-errors (elpy-promise-reject promise err))) elpy-rpc--backend-callbacks) @@ -2961,7 +2714,7 @@ (json nil) (did-read-json nil)) (goto-char (point-min)) - (condition-case err + (condition-case _err (progn (setq json (let ((json-array-type 'list)) (json-read))) @@ -3015,8 +2768,10 @@ (elpy-insert--header "Output from Backend") (elpy-insert--para "There was some unexpected output from the Elpy backend. " - "This is usually some module that does not use correct logging, " - "but might indicate a configuration problem.\n\n") + "This is usually not a problem and should usually not be " + "reported as a bug with Elpy. You can safely hide this " + "buffer and ignore it. You can also see the output below " + "in case there is an actual problem.\n\n") (elpy-insert--header "Output") (setq buf (current-buffer)))) (with-current-buffer buf @@ -3203,7 +2958,7 @@ (ignore-errors (kill-buffer buffer))))) -(defun elpy-rpc-init (backend library-root &optional success error) +(defun elpy-rpc-init (library-root &optional success error) "Initialize the backend. This has to be called as the first method, else Elpy won't be @@ -3212,8 +2967,7 @@ ;; This uses a vector because otherwise, json-encode in ;; older Emacsen gets seriously confused, especially when ;; backend is nil. - (vector `((backend . ,backend) - (project_root . ,(expand-file-name library-root)))) + (vector `((project_root . ,(expand-file-name library-root)))) success error)) (defun elpy-rpc-get-calltip (&optional success error) @@ -3264,6 +3018,17 @@ (point-min))) success error)) +(defun elpy-rpc-get-assignment (&optional success error) + "Call the find_assignment API function. + +Returns nil or a list of (filename, point)." + (elpy-rpc "get_assignment" + (list buffer-file-name + (elpy-rpc--buffer-contents) + (- (point) + (point-min))) + success error)) + (defun elpy-rpc-get-docstring (&optional success error) "Call the get_docstring API function. @@ -3288,6 +3053,7 @@ success error)) (defun elpy-rpc-get-usages (&optional success error) + "Return the symbol under point usages as a list" (elpy-rpc "get_usages" (list buffer-file-name (elpy-rpc--buffer-contents) @@ -3295,6 +3061,183 @@ (point-min))) success error)) +(defun elpy-rpc-get-names (&optional success error) + "Return all names (possible candidates for jumping to definition)." + (elpy-rpc "get_names" + (list buffer-file-name + (elpy-rpc--buffer-contents) + (- (point) + (point-min))) + success error)) + +;;;;;;;;;;;;;;;; +;;; Xref backend +(defun elpy--xref-backend () + "Return the name of the elpy xref backend." + (if elpy-rpc--jedi-available + 'elpy + nil)) + +(defvar elpy-xref--format-references + (let ((str "%s:\t%s")) + (put-text-property 0 4 'face 'font-lock-comment-face str) + str) + "String used to format references in xref buffers.") + +;; Elpy location structure +(when (featurep 'xref) + (cl-defstruct (xref-elpy-location + (:constructor xref-make-elpy-location (file pos))) + "Location of a python symbol definition." + file pos) + + (defun xref-make-elpy-location (file pos) + "Return an elpy location structure. +Points to file FILE, at position POS." + (make-instance 'xref-etags-location + :file (expand-file-name file) + :pos pos)) + + (cl-defmethod xref-location-marker ((l xref-elpy-location)) + (with-current-buffer (find-file-noselect (xref-elpy-location-file l)) + (save-excursion + (goto-char (xref-elpy-location-pos l)) + (point-marker)))) + + (cl-defmethod xref-location-group ((l xref-elpy-location)) + (xref-elpy-location-file l)) + + ;; Identifiers + (cl-defmethod xref-backend-identifier-at-point ((_backend (eql elpy))) + (elpy-xref--identifier-at-point)) + + (defun elpy-xref--identifier-at-point () + "Return identifier at point. + +Is a string, formatted as \"LINE_NUMBER: VARIABLE_NAME\"." + (let ((symb (substring-no-properties (symbol-name (symbol-at-point)))) + (assign (elpy-rpc-get-assignment))) + (when assign + (format "%s: %s" + (line-number-at-pos (+ 1 (car (cdr assign)))) symb)))) + + (defun elpy-xref--identifier-name (id) + "Return the identifier ID variable name." + (string-match ".*: \\([^:]*\\)" id) + (match-string 1 id)) + + (defun elpy-xref--identifier-line (id) + "Return the identifier ID line number." + (string-match "\\([^:]*\\):.*" id) + (string-to-number (match-string 1 id))) + + (defun elpy-xref--goto-identifier (id) + "Goto the identifier ID in the current buffer. +This is needed to get information on the identifier with jedi +\(that work only on the symbol at point\)" + (goto-line (elpy-xref--identifier-line id)) + (search-forward (elpy-xref--identifier-name id)) + (goto-char (match-beginning 0))) + + ;; Find definition + (cl-defmethod xref-backend-definitions ((_backend (eql elpy)) id) + (elpy-xref--definitions id)) + + (defun elpy-xref--definitions (id) + "Return SYMBOL definition position as a xref object." + (save-excursion + (elpy-xref--goto-identifier id) + (let* ((location (elpy-rpc-get-definition))) + (if (not location) + (error "No definition found") + (let* ((file (expand-file-name (car location))) + (pos (+ 1 (cadr location))) + (line (with-current-buffer (find-file-noselect file) + (goto-char (+ pos 1)) + (buffer-substring (line-beginning-position) (line-end-position)))) + (linenumber (with-current-buffer (find-file-noselect file) + (line-number-at-pos pos))) + (summary (format elpy-xref--format-references linenumber line)) + (loc (xref-make-elpy-location file pos))) + (list (xref-make summary loc))))))) + + ;; Find references + (cl-defmethod xref-backend-references ((_backend (eql elpy)) id) + (elpy-xref--references id)) + + (defun elpy-xref--references (id) + "Return SYMBOL references as a list of xref objects." + (save-excursion + (elpy-xref--goto-identifier id) + (let* ((references (elpy-rpc-get-usages))) + (cl-loop + for ref in references + for file = (alist-get 'filename ref) + for pos = (+ (alist-get 'offset ref) 1) + for line = (with-current-buffer (find-file-noselect file) + (save-excursion + (goto-char (+ pos 1)) + (buffer-substring (line-beginning-position) (line-end-position)))) + for linenumber = (with-current-buffer (find-file-noselect file) + (line-number-at-pos pos)) + for summary = (format elpy-xref--format-references linenumber line) + for loc = (xref-make-elpy-location file pos) + collect (xref-make summary loc))))) + + ;; Completion table (used when calling `xref-find-references`) + (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql elpy))) + (elpy-xref--get-completion-table)) + + (defun elpy-xref--get-completion-table () + "Return the completion table for identifiers." + (let ((table nil)) + (cl-loop + for ref in (nreverse (elpy-rpc-get-names)) + for offset = (+ (alist-get 'offset ref) 1) + for filename = (alist-get 'filename ref) + ;; ensure that variables with same assignment are not represented twice + do (with-current-buffer (find-file-noselect filename) + (save-excursion + (goto-char offset) + (let* ((def (elpy-rpc-get-assignment)) + (tmp_filename (car def)) + (tmp_offset (car (cdr def)))) + (when def + (setq filename tmp_filename) + (setq offset (+ tmp_offset 1)))))) + for line = (with-current-buffer (find-file-noselect filename) + (line-number-at-pos offset)) + for id = (format "%s: %s" line (alist-get 'name ref)) + unless (member id table) + do (push id table)) + table)) + + ;; Apropos + (cl-defmethod xref-backend-apropos ((_backend (eql elpy)) regex) + (elpy-xref--apropos regex)) + + (defun elpy-xref--apropos (regex) + "Return identifiers matching REGEX." + (let ((ids (elpy-rpc-get-names)) + (case-fold-search nil)) + (cl-loop + for id in ids + for name = (alist-get 'name id) + for filename = (alist-get 'filename id) + for pos = (+ (alist-get 'offset id) 1) + if (string-match regex name) + collect (with-current-buffer (find-file-noselect filename) + (goto-char pos) + (save-excursion + (let* ((linenumber (line-number-at-pos)) + (line (buffer-substring + (line-beginning-position) + (line-end-position))) + (summary (format elpy-xref--format-references linenumber line)) + (loc (xref-make-elpy-location filename pos))) + (xref-make summary loc))))))) + ) + ;;;;;;;;;;; ;;; Modules @@ -3333,8 +3276,8 @@ "Run the buffer-stop method of Elpy modules." (elpy-modules-run 'buffer-stop)) -(defun elpy-modules-remove-modeline-lighter (mode-name) - "Remove the lighter for MODE-NAME. +(defun elpy-modules-remove-modeline-lighter (modename) + "Remove the lighter for MODENAME. It should not be necessary to see (Python Elpy yas company ElDoc) all the time. @@ -3345,10 +3288,10 @@ (interactive) (when elpy-remove-modeline-lighter (cond - ((eq mode-name 'eldoc-minor-mode) + ((eq modename 'eldoc-minor-mode) (setq eldoc-minor-mode-string nil)) (t - (let ((cell (assq mode-name minor-mode-alist))) + (let ((cell (assq modename minor-mode-alist))) (when cell (setcdr cell (list "")))))))) @@ -3356,7 +3299,7 @@ ;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Module: Sane Defaults -(defun elpy-module-sane-defaults (command &rest args) +(defun elpy-module-sane-defaults (command &rest _args) (pcase command (`buffer-init ;; Set `forward-sexp-function' to nil in python-mode. See @@ -3371,7 +3314,7 @@ ;;;;;;;;;;;;;;;;;;; ;;; Module: Company -(defun elpy-module-company (command &rest args) +(defun elpy-module-company (command &rest _args) "Module to support company-mode completions." (pcase command (`global-init @@ -3397,7 +3340,12 @@ (delq 'company-ropemacs (delq 'company-capf (mapcar #'identity company-backends)))))) - (company-mode 1)) + (company-mode 1) + (when (> (buffer-size) elpy-rpc-ignored-buffer-size) + (message + (concat "Buffer %s larger than elpy-rpc-ignored-buffer-size (%d)." + " Elpy will turn off completion.") + (buffer-name) elpy-rpc-ignored-buffer-size))) (`buffer-stop (company-mode -1) (kill-local-variable 'company-idle-delay) @@ -3438,8 +3386,8 @@ "Store RESULT in the candidate cache and return candidates." (elpy-company--cache-clear) (mapcar (lambda (completion) - (let* ((suffix (cdr (assq 'suffix completion))) - (name (concat prefix suffix))) + (let* ((suffix (cdr (assq 'name completion))) + (name (concat (s-chop-suffix (company-grab-symbol) prefix) suffix))) (puthash name completion elpy-company--cache) name)) result)) @@ -3535,8 +3483,7 @@ ;; and scipy) and `elpy-company--cache' does not allow to identify ;; callable instances. ;; It looks easy to modify `elpy-company--cache' cheaply for the jedi - ;; backend to eliminate the `elpy-rpc-get-calltip' call below, but - ;; not for the rope backend. + ;; backend to eliminate the `elpy-rpc-get-calltip' call below. (insert "()") (backward-char 1) (when (not (elpy-rpc-get-calltip)) @@ -3628,7 +3575,7 @@ ;;;;;;;;;;;;;;;;; ;;; Module: ElDoc -(defun elpy-module-eldoc (command &rest args) +(defun elpy-module-eldoc (command &rest _args) "Module to support ElDoc for Python files." (pcase command (`global-init @@ -3683,7 +3630,7 @@ ;;;;;;;;;;;;;;;;;;; ;;; Module: Flymake -(defun elpy-module-flymake (command &rest args) +(defun elpy-module-flymake (command &rest _args) "Enable Flymake support for Python." (pcase command (`global-init @@ -3774,7 +3721,7 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Module: Highlight Indentation -(defun elpy-module-highlight-indentation (command &rest args) +(defun elpy-module-highlight-indentation (command &rest _args) "Module to highlight indentation in Python files." (pcase command (`global-init @@ -3787,7 +3734,7 @@ ;;;;;;;;;;;;;;;;;; ;;; Module: pyvenv -(defun elpy-module-pyvenv (command &rest args) +(defun elpy-module-pyvenv (command &rest _args) "Module to display the current virtualenv in the mode line." (pcase command (`global-init @@ -3798,7 +3745,7 @@ ;;;;;;;;;;;;;;;;;;;;; ;;; Module: Yasnippet -(defun elpy-module-yasnippet (command &rest args) +(defun elpy-module-yasnippet (command &rest _args) "Module to enable YASnippet snippets." (pcase command (`global-init @@ -3831,7 +3778,7 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Module: Elpy-Django -(defun elpy-module-django (command &rest args) +(defun elpy-module-django (command &rest _args) "Module to provide Django support." (pcase command (`buffer-init @@ -3842,6 +3789,9 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Backwards compatibility +;; TODO Some/most of this compatibility code can now go, as minimum required +;; Emacs version is 24.4. + ;; Functions for Emacs 24 before 24.3 (when (not (fboundp 'python-info-current-defun)) (defalias 'python-info-current-defun 'python-current-defun)) @@ -3868,7 +3818,7 @@ process-environment)) (when (not (fboundp 'python-shell-get-process-name)) - (defun python-shell-get-process-name (dedicated) + (defun python-shell-get-process-name (_dedicated) "Compatibility function for older Emacsen." "Python")) @@ -3878,7 +3828,7 @@ python-python-command)) (when (not (fboundp 'python-shell-send-buffer)) - (defun python-shell-send-buffer (&optional arg) + (defun python-shell-send-buffer (&optional _arg) (python-send-buffer))) (when (not (fboundp 'python-shell-send-string)) @@ -3950,6 +3900,39 @@ highlight-indent-active) (highlight-indentation))))) +;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=25753#44 +(when (version< emacs-version "25.2") + (defun python-shell-completion-native-try () + "Return non-nil if can trigger native completion." + (let ((python-shell-completion-native-enable t) + (python-shell-completion-native-output-timeout + python-shell-completion-native-try-output-timeout)) + (python-shell-completion-native-get-completions + (get-buffer-process (current-buffer)) + nil "_")))) + +;; python.el in Emacs 24 does not have functions to determine buffer encoding. +;; In these versions, we fix the encoding to utf-8 (safe choice when no encoding +;; is defined since Python 2 uses ASCII and Python 3 UTF-8). +(unless (fboundp 'python-info-encoding) + (defun python-info-encoding () + 'utf-8)) + +;; Added in Emacs 25 +(unless (fboundp 'python-shell-comint-end-of-output-p) + (defun python-shell-comint-end-of-output-p (output) + "Return non-nil if OUTPUT is ends with input prompt." + (string-match + ;; XXX: It seems on macOS an extra carriage return is attached + ;; at the end of output, this handles that too. + (concat + "\r?\n?" + ;; Remove initial caret from calculated regexp + (replace-regexp-in-string + (rx string-start ?^) "" + python-shell--prompt-calculated-input-regexp) + (rx eos)) + output))) (provide 'elpy) ;;; elpy.el ends here diff --git a/elpa/elpy-1.14.1/elpy/__init__.py b/elpa/elpy-1.18.0/elpy/__init__.py rename from elpa/elpy-1.14.1/elpy/__init__.py rename to elpa/elpy-1.18.0/elpy/__init__.py --- a/elpa/elpy-1.14.1/elpy/__init__.py +++ b/elpa/elpy-1.18.0/elpy/__init__.py @@ -37,5 +37,5 @@ """ __author__ = "Jorgen Schaefer" -__version__ = "1.14.1" +__version__ = "1.18.0" __license__ = "GPL" diff --git a/elpa/elpy-1.14.1/elpy/__main__.py b/elpa/elpy-1.18.0/elpy/__main__.py rename from elpa/elpy-1.14.1/elpy/__main__.py rename to elpa/elpy-1.18.0/elpy/__main__.py diff --git a/elpa/elpy-1.14.1/elpy/auto_pep8.py b/elpa/elpy-1.18.0/elpy/auto_pep8.py rename from elpa/elpy-1.14.1/elpy/auto_pep8.py rename to elpa/elpy-1.18.0/elpy/auto_pep8.py diff --git a/elpa/elpy-1.14.1/elpy/compat.py b/elpa/elpy-1.18.0/elpy/compat.py rename from elpa/elpy-1.14.1/elpy/compat.py rename to elpa/elpy-1.18.0/elpy/compat.py diff --git a/elpa/elpy-1.14.1/elpy/jedibackend.py b/elpa/elpy-1.18.0/elpy/jedibackend.py rename from elpa/elpy-1.14.1/elpy/jedibackend.py rename to elpa/elpy-1.18.0/elpy/jedibackend.py --- a/elpa/elpy-1.14.1/elpy/jedibackend.py +++ b/elpa/elpy-1.18.0/elpy/jedibackend.py @@ -60,14 +60,10 @@ def rpc_get_docstring(self, filename, source, offset): line, column = pos_to_linecol(source, offset) - try: - locations = run_with_debug(jedi, 'goto_definitions', - source=source, line=line, column=column, - path=filename, encoding='utf-8', - re_raise=jedi.NotFoundError) - except jedi.NotFoundError: - return None - if locations: + locations = run_with_debug(jedi, 'goto_definitions', + source=source, line=line, column=column, + path=filename, encoding='utf-8') + if locations and locations[-1].docstring(): return ('Documentation for {0}:\n\n'.format( locations[-1].full_name) + locations[-1].docstring()) else: @@ -75,13 +71,9 @@ def rpc_get_definition(self, filename, source, offset): line, column = pos_to_linecol(source, offset) - try: - locations = run_with_debug(jedi, 'goto_definitions', - source=source, line=line, column=column, - path=filename, encoding='utf-8', - re_raise=jedi.NotFoundError) - except jedi.NotFoundError: - return None + locations = run_with_debug(jedi, 'goto_definitions', + source=source, line=line, column=column, + path=filename, encoding='utf-8') # goto_definitions() can return silly stuff like __builtin__ # for int variables, so we fall back on goto() in those # cases. See issue #76. @@ -114,6 +106,32 @@ return None return (loc.module_path, offset) + def rpc_get_assignment(self, filename, source, offset): + line, column = pos_to_linecol(source, offset) + locations = run_with_debug(jedi, 'goto_assignments', + source=source, line=line, column=column, + path=filename, encoding='utf-8') + if not locations: + return None + else: + loc = locations[-1] + try: + if loc.module_path: + if loc.module_path == filename: + offset = linecol_to_pos(source, + loc.line, + loc.column) + else: + with open(loc.module_path) as f: + offset = linecol_to_pos(f.read(), + loc.line, + loc.column) + else: + return None + except IOError: + return None + return (loc.module_path, offset) + def rpc_get_calltip(self, filename, source, offset): line, column = pos_to_linecol(source, offset) calls = run_with_debug(jedi, 'call_signatures', @@ -125,17 +143,6 @@ call = None if not call: return None - try: - call.index - except AttributeError as e: - if "get_definition" in str(e): - # Bug #627 / jedi#573 - return None - elif "get_subscope_by_name" in str(e): - # Bug #677 / jedi#628 - return None - else: - raise return {"name": call.name, "index": call.index, "params": [param.description for param in call.params]} @@ -148,14 +155,9 @@ """ line, column = pos_to_linecol(source, offset) - try: - uses = run_with_debug(jedi, 'usages', - source=source, line=line, column=column, - path=filename, encoding='utf-8', - re_raise=(jedi.NotFoundError,)) - except jedi.NotFoundError: - return [] - + uses = run_with_debug(jedi, 'usages', + source=source, line=line, column=column, + path=filename, encoding='utf-8') if uses is None: return None result = [] @@ -173,6 +175,27 @@ return result + def rpc_get_names(self, filename, source, offset): + """Return the list of possible names""" + names = jedi.api.names(source=source, + path=filename, encoding='utf-8', + all_scopes=True, + definitions=True, + references=True) + + result = [] + for name in names: + if name.module_path == filename: + offset = linecol_to_pos(source, name.line, name.column) + elif name.module_path is not None: + with open(name.module_path) as f: + text = f.read() + offset = linecol_to_pos(text, name.line, name.column) + result.append({"name": name.name, + "filename": name.module_path, + "offset": offset}) + return result + # From the Jedi documentation: # @@ -219,39 +242,12 @@ def run_with_debug(jedi, name, *args, **kwargs): re_raise = kwargs.pop('re_raise', ()) - # Remove form feed characters, they confuse Jedi (jedi#424) - if 'source' in kwargs: - kwargs['source'] = kwargs['source'].replace("\f", " ") try: script = jedi.Script(*args, **kwargs) return getattr(script, name)() except Exception as e: if isinstance(e, re_raise): raise - # Bug jedi#417 - if isinstance(e, TypeError) and str(e) == 'no dicts allowed': - return None - # Bug jedi#427 - if isinstance(e, UnicodeDecodeError): - return None - # Bug jedi#429 - if isinstance(e, IndexError): - return None - # Bug jedi#431 - if isinstance(e, AttributeError) and str(e).endswith("'end_pos'"): - return None - # Bug in Python 2.6, see #275 - if isinstance(e, OSError) and e.errno == 13: - return None - # Bug jedi#466 - if ( - isinstance(e, SyntaxError) and - "EOL while scanning string literal" in str(e) - ): - return None - # Bug jedi#482 - if isinstance(e, UnicodeEncodeError): - return None # Bug jedi#485 if ( isinstance(e, ValueError) and @@ -264,63 +260,6 @@ "truncated \\xXX escape" in str(e) ): return None - # Bug jedi#465 - if ( - isinstance(e, SyntaxError) and - "encoding declaration in Unicode string" in str(e) - ): - return None - # Bug #337 / jedi#471 - if ( - isinstance(e, ImportError) and - "No module named" in str(e) - ): - return None - # Bug #365 / jedi#486 - fixed in Jedi 0.8.2 - if ( - isinstance(e, UnboundLocalError) and - "local variable 'path' referenced before assignment" in str(e) - ): - return None - # Bug #366 / jedi#491 - if ( - isinstance(e, ValueError) and - "__loader__ is None" in str(e) - ): - return None - # Bug #353 - if ( - isinstance(e, OSError) and - "No such file or directory" in str(e) - ): - return None - # Bug #561, #564, #570, #588, #593, #599 / jedi#572, jedi#579, jedi#590 - if isinstance(e, KeyError): - return None - # Bug #519 / jedi#610 - if ( - isinstance(e, RuntimeError) and - "maximum recursion depth exceeded" in str(e) - ): - return None - # Bug #563 / jedi#589 - if ( - isinstance(e, AttributeError) and - "MergedNamesDict" in str(e) - ): - return None - # Bug #615 / jedi#592 - if ( - isinstance(e, AttributeError) and - "ListComprehension" in str(e) - ): - return None - # Bug #569 / jedi#593 - if ( - isinstance(e, AttributeError) and - "names_dict" in str(e) - ): - return None from jedi import debug diff --git a/elpa/elpy-1.14.1/elpy/pydocutils.py b/elpa/elpy-1.18.0/elpy/pydocutils.py rename from elpa/elpy-1.14.1/elpy/pydocutils.py rename to elpa/elpy-1.18.0/elpy/pydocutils.py diff --git a/elpa/elpy-1.14.1/elpy/refactor.py b/elpa/elpy-1.18.0/elpy/refactor.py rename from elpa/elpy-1.14.1/elpy/refactor.py rename to elpa/elpy-1.18.0/elpy/refactor.py diff --git a/elpa/elpy-1.14.1/elpy/rpc.py b/elpa/elpy-1.18.0/elpy/rpc.py rename from elpa/elpy-1.14.1/elpy/rpc.py rename to elpa/elpy-1.18.0/elpy/rpc.py diff --git a/elpa/elpy-1.14.1/elpy/server.py b/elpa/elpy-1.18.0/elpy/server.py rename from elpa/elpy-1.14.1/elpy/server.py rename to elpa/elpy-1.18.0/elpy/server.py --- a/elpa/elpy-1.14.1/elpy/server.py +++ b/elpa/elpy-1.18.0/elpy/server.py @@ -11,7 +11,6 @@ from elpy.pydocutils import get_pydoc_completions from elpy.rpc import JSONRPCServer, Fault -from elpy.impmagic import ImportMagic, ImportMagicError from elpy.auto_pep8 import fix_code from elpy.yapfutil import fix_code as fix_code_with_yapf @@ -21,11 +20,6 @@ except ImportError: # pragma: no cover jedibackend = None -try: - from elpy import ropebackend -except ImportError: # pragma: no cover - ropebackend = None - class ElpyRPCServer(JSONRPCServer): """The RPC server for elpy. @@ -36,7 +30,6 @@ def __init__(self, *args, **kwargs): super(ElpyRPCServer, self).__init__(*args, **kwargs) self.backend = None - self.import_magic = ImportMagic() self.project_root = None def _call_backend(self, method, default, *args, **kwargs): @@ -61,23 +54,13 @@ def rpc_init(self, options): self.project_root = options["project_root"] - if self.import_magic.is_enabled: - self.import_magic.build_index(self.project_root) - - if ropebackend and options["backend"] == "rope": - self.backend = ropebackend.RopeBackend(self.project_root) - elif jedibackend and options["backend"] == "jedi": - self.backend = jedibackend.JediBackend(self.project_root) - elif ropebackend: - self.backend = ropebackend.RopeBackend(self.project_root) - elif jedibackend: + if jedibackend: self.backend = jedibackend.JediBackend(self.project_root) else: self.backend = None return { - 'backend': (self.backend.name if self.backend is not None - else None) + 'jedi_available': (self.backend is not None) } def rpc_get_calltip(self, filename, source, offset): @@ -122,6 +105,13 @@ return self._call_backend("rpc_get_definition", None, filename, get_source(source), offset) + def rpc_get_assignment(self, filename, source, offset): + """Get the location of the assignment for the symbol at the offset. + + """ + return self._call_backend("rpc_get_assignment", None, filename, + get_source(source), offset) + def rpc_get_docstring(self, filename, source, offset): """Get the docstring for the symbol at the offset. @@ -198,55 +188,16 @@ raise Fault("get_usages not implemented by current backend", code=400) - def _ensure_import_magic(self): # pragma: no cover - if not self.import_magic.is_enabled: - raise Fault("fixup_imports not enabled; install importmagic module", - code=400) - if not self.import_magic.symbol_index: - raise Fault(self.import_magic.fail_message, code=200) # XXX code? - - def rpc_get_import_symbols(self, filename, source, symbol): - """Return a list of modules from which the given symbol can be imported. - - """ - self._ensure_import_magic() - try: - return self.import_magic.get_import_symbols(symbol) - except ImportMagicError as err: - raise Fault(str(err), code=200) - - def rpc_add_import(self, filename, source, statement): - """Add an import statement to the module. + def rpc_get_names(self, filename, source, offset): + """Get all possible names """ - self._ensure_import_magic() source = get_source(source) - try: - return self.import_magic.add_import(source, statement) - except ImportMagicError as err: - raise Fault(str(err), code=200) - - def rpc_get_unresolved_symbols(self, filename, source): - """Return a list of unreferenced symbols in the module. - - """ - self._ensure_import_magic() - source = get_source(source) - try: - return self.import_magic.get_unresolved_symbols(source) - except ImportMagicError as err: - raise Fault(str(err), code=200) - - def rpc_remove_unreferenced_imports(self, filename, source): - """Remove unused import statements. - - """ - self._ensure_import_magic() - source = get_source(source) - try: - return self.import_magic.remove_unreferenced_imports(source) - except ImportMagicError as err: - raise Fault(str(err), code=200) + if hasattr(self.backend, "rpc_get_names"): + return self.backend.rpc_get_names(filename, source, offset) + else: + raise Fault("get_names not implemented by current backend", + code=400) def rpc_fix_code(self, source): """Formats Python code to conform to the PEP 8 style guide. diff --git a/elpa/elpy-1.14.1/elpy/tests/__init__.py b/elpa/elpy-1.18.0/elpy/tests/__init__.py rename from elpa/elpy-1.14.1/elpy/tests/__init__.py rename to elpa/elpy-1.18.0/elpy/tests/__init__.py diff --git a/elpa/elpy-1.14.1/elpy/tests/compat.py b/elpa/elpy-1.18.0/elpy/tests/compat.py rename from elpa/elpy-1.14.1/elpy/tests/compat.py rename to elpa/elpy-1.18.0/elpy/tests/compat.py diff --git a/elpa/elpy-1.14.1/elpy/tests/support.py b/elpa/elpy-1.18.0/elpy/tests/support.py rename from elpa/elpy-1.14.1/elpy/tests/support.py rename to elpa/elpy-1.18.0/elpy/tests/support.py --- a/elpa/elpy-1.14.1/elpy/tests/support.py +++ b/elpa/elpy-1.18.0/elpy/tests/support.py @@ -110,7 +110,6 @@ self.rpc(filename, source, offset) def test_should_not_fail_for_bad_indentation(self): - # Bug in Rope: rope#80 source, offset = source_and_offset( "def foo():\n" " print(23)_|_\n" @@ -122,7 +121,6 @@ @unittest.skipIf((3, 3) <= sys.version_info < (3, 4), "Bug in jedi for Python 3.3") def test_should_not_fail_for_relative_import(self): - # Bug in Rope: rope#81 and rope#82 source, offset = source_and_offset( "from .. import foo_|_" ) @@ -141,7 +139,6 @@ self.rpc(filename, source, offset) def test_should_not_fail_with_bad_encoding(self): - # Bug in Rope: rope#83 source, offset = source_and_offset( u'# coding: utf-8X_|_\n' ) @@ -338,7 +335,7 @@ def test_should_complete_builtin(self): source, offset = source_and_offset("o_|_") - expected = ["object", "oct", "open", "or", "ord"] + expected = self.BUILTINS actual = [cand['name'] for cand in self.backend.rpc_get_completions("test.py", source, offset)] @@ -595,6 +592,103 @@ (filename, 0)) +class RPCGetAssignmentTests(GenericRPCTests): + METHOD = "rpc_get_assignment" + + def test_should_return_assignment_location_same_file(self): + source, offset = source_and_offset("import threading\n" + "class TestClass(object):\n" + " def __init__(self, a, b):\n" + " self.a = a\n" + " self.b = b\n" + "\n" + "testclass = TestClass(2, 4)" + "\n" + "testcl_|_ass(\n") + filename = self.project_file("test.py", source) + + location = self.backend.rpc_get_assignment(filename, + source, + offset) + + self.assertEqual(location[0], filename) + # On def or on the function name + self.assertEqual(location[1], 111) + + def test_should_return_location_in_same_file_if_not_saved(self): + source, offset = source_and_offset("import threading\n" + "class TestClass(object):\n" + " def __init__(self, a, b):\n" + " self.a = a\n" + " self.b = b\n" + "\n" + "testclass = TestClass(2, 4)" + "\n" + "testcl_|_ass(\n") + filename = self.project_file("test.py", "") + + location = self.backend.rpc_get_assignment(filename, + source, + offset) + + self.assertEqual(location[0], filename) + # def or function name + self.assertEqual(location[1], 111) + + def test_should_return_location_in_different_file(self): + source1 = ("class TestClass(object):\n" + " def __init__(self, a, b):\n" + " self.a = a\n" + " self.b = b\n" + "testclass = TestClass(3, 5)\n") + file1 = self.project_file("test1.py", source1) + source2, offset = source_and_offset("from test1 import testclass\n" + "testcl_|_ass.a\n") + file2 = self.project_file("test2.py", source2) + # First jump goes to import statement + assignment = self.backend.rpc_get_assignment(file2, + source2, + offset) + # Second jump goes to test1 file + self.assertEqual(assignment[0], file2) + assignment = self.backend.rpc_get_assignment(file2, + source2, + assignment[1]) + + self.assertEqual(assignment[0], file1) + self.assertEqual(assignment[1], 93) + + def test_should_return_none_if_location_not_found(self): + source, offset = source_and_offset("test_f_|_unction()\n") + filename = self.project_file("test.py", source) + + assignment = self.backend.rpc_get_assignment(filename, + source, + offset) + + self.assertIsNone(assignment) + + def test_should_return_none_if_outside_of_symbol(self): + source, offset = source_and_offset("testcl(_|_)ass\n") + filename = self.project_file("test.py", source) + + assignment = self.backend.rpc_get_assignment(filename, + source, + offset) + + self.assertIsNone(assignment) + + def test_should_find_variable_assignment(self): + source, offset = source_and_offset("SOME_VALUE = 1\n" + "\n" + "variable = _|_SOME_VALUE\n") + filename = self.project_file("test.py", source) + self.assertEqual(self.backend.rpc_get_assignment(filename, + source, + offset), + (filename, 0)) + + class RPCGetCalltipTests(GenericRPCTests): METHOD = "rpc_get_calltip" @@ -739,6 +833,53 @@ self.assertIsNone(docstring) +class RPCGetNamesTests(GenericRPCTests): + METHOD = "rpc_get_names" + + def test_shouldreturn_names_in_same_file(self): + filename = self.project_file("test.py", "") + source, offset = source_and_offset( + "def foo(x, y):\n" + " return x + y\n" + "c = _|_foo(5, 2)\n") + + names = self.backend.rpc_get_names(filename, + source, + offset) + + self.assertEqual(names, + [{'name': 'foo', + 'filename': filename, + 'offset': 4}, + {'name': 'x', + 'filename': filename, + 'offset': 8}, + {'name': 'y', + 'filename': filename, + 'offset': 11}, + {'name': 'x', + 'filename': filename, + 'offset': 26}, + {'name': 'y', + 'filename': filename, + 'offset': 30}, + {'name': 'c', + 'filename': filename, + 'offset': 32}, + {'name': 'foo', + 'filename': filename, + 'offset': 36}]) + + def test_should_not_fail_without_symbol(self): + filename = self.project_file("test.py", "") + + names = self.backend.rpc_get_names(filename, + "", + 0) + + self.assertEqual(names, []) + + class RPCGetUsagesTests(GenericRPCTests): METHOD = "rpc_get_usages" diff --git a/elpa/elpy-1.14.1/elpy/tests/test_auto_pep8.py b/elpa/elpy-1.18.0/elpy/tests/test_auto_pep8.py rename from elpa/elpy-1.14.1/elpy/tests/test_auto_pep8.py rename to elpa/elpy-1.18.0/elpy/tests/test_auto_pep8.py diff --git a/elpa/elpy-1.14.1/elpy/tests/test_jedibackend.py b/elpa/elpy-1.18.0/elpy/tests/test_jedibackend.py rename from elpa/elpy-1.14.1/elpy/tests/test_jedibackend.py rename to elpa/elpy-1.18.0/elpy/tests/test_jedibackend.py --- a/elpa/elpy-1.14.1/elpy/tests/test_jedibackend.py +++ b/elpa/elpy-1.18.0/elpy/tests/test_jedibackend.py @@ -15,8 +15,10 @@ from elpy.tests.support import RPCGetCompletionLocationTests from elpy.tests.support import RPCGetDocstringTests from elpy.tests.support import RPCGetDefinitionTests +from elpy.tests.support import RPCGetAssignmentTests from elpy.tests.support import RPCGetCalltipTests from elpy.tests.support import RPCGetUsagesTests +from elpy.tests.support import RPCGetNamesTests class JediBackendTestCase(BackendTestCase): @@ -32,7 +34,7 @@ class TestRPCGetCompletions(RPCGetCompletionsTests, JediBackendTestCase): - pass + BUILTINS = ['object', 'oct', 'open', 'ord', 'OSError', 'OverflowError'] class TestRPCGetCompletionDocstring(RPCGetCompletionDocstringTests, @@ -57,6 +59,16 @@ self.assertEqual(lines[0], 'Documentation for json.loads:') self.assertEqual(lines[2], self.JSON_LOADS_DOCSTRING) + @mock.patch("elpy.jedibackend.run_with_debug") + def test_should_not_return_empty_docstring(self, run_with_debug): + location = mock.MagicMock() + location.full_name = "testthing" + location.docstring.return_value = "" + run_with_debug.return_value = [location] + filename = self.project_file("test.py", "print") + docstring = self.backend.rpc_get_docstring(filename, "print", 0) + self.assertIsNone(docstring) + class TestRPCGetDefinition(RPCGetDefinitionTests, JediBackendTestCase): @@ -80,21 +92,35 @@ self.assertIsNone(location) +class TestRPCGetAssignment(RPCGetAssignmentTests, + JediBackendTestCase): + @mock.patch("jedi.Script") + def test_should_not_fail_if_module_path_is_none(self, Script): + """Do not fail if loc.module_path is None. + + """ + locations = [ + mock.Mock(module_path=None) + ] + script = Script.return_value + script.goto_assignments.return_value = locations + script.goto_assignments.return_value = locations + + location = self.rpc("", "", 0) + + self.assertIsNone(location) + + class TestRPCGetCalltip(RPCGetCalltipTests, JediBackendTestCase): KEYS_CALLTIP = {'index': 0, - 'params': [''], + 'params': ['param '], 'name': u'keys'} - if sys.version_info >= (3, 5): - RADIX_CALLTIP = {'index': 0, - 'params': ['10'], - 'name': u'radix'} - else: - RADIX_CALLTIP = {'index': None, - 'params': [], - 'name': u'radix'} + RADIX_CALLTIP = {'index': None, + 'params': [], + 'name': u'radix'} ADD_CALLTIP = {'index': 0, - 'params': [u'a', u'b'], + 'params': [u'param a', u'param b'], 'name': u'add'} if compat.PYTHON3: THREAD_CALLTIP = {"name": "Thread", @@ -107,12 +133,12 @@ "index": 0} else: THREAD_CALLTIP = {"name": "Thread", - "params": ["group=None", - "target=None", - "name=None", - "args=()", - "kwargs=None", - "verbose=None"], + "params": ["param group=None", + "param target=None", + "param name=None", + "param args=()", + "param kwargs=None", + "param verbose=None"], "index": 0} def test_should_not_fail_with_get_subscope_by_name(self): @@ -125,8 +151,7 @@ offset = 37 sigs = self.backend.rpc_get_calltip(filename, source, offset) - if sigs is not None: - sigs[0].index + sigs["index"] class TestRPCGetUsages(RPCGetUsagesTests, @@ -140,6 +165,11 @@ self.rpc(filename, source, offset) +class TestRPCGetNames(RPCGetNamesTests, + JediBackendTestCase): + pass + + class TestPosToLinecol(unittest.TestCase): def test_should_handle_beginning_of_string(self): self.assertEqual(jedibackend.pos_to_linecol("foo", 0), diff --git a/elpa/elpy-1.14.1/elpy/tests/test_pydocutils.py b/elpa/elpy-1.18.0/elpy/tests/test_pydocutils.py rename from elpa/elpy-1.14.1/elpy/tests/test_pydocutils.py rename to elpa/elpy-1.18.0/elpy/tests/test_pydocutils.py diff --git a/elpa/elpy-1.14.1/elpy/tests/test_refactor.py b/elpa/elpy-1.18.0/elpy/tests/test_refactor.py rename from elpa/elpy-1.14.1/elpy/tests/test_refactor.py rename to elpa/elpy-1.18.0/elpy/tests/test_refactor.py diff --git a/elpa/elpy-1.14.1/elpy/tests/test_rpc.py b/elpa/elpy-1.18.0/elpy/tests/test_rpc.py rename from elpa/elpy-1.14.1/elpy/tests/test_rpc.py rename to elpa/elpy-1.18.0/elpy/tests/test_rpc.py diff --git a/elpa/elpy-1.14.1/elpy/tests/test_server.py b/elpa/elpy-1.18.0/elpy/tests/test_server.py rename from elpa/elpy-1.14.1/elpy/tests/test_server.py rename to elpa/elpy-1.18.0/elpy/tests/test_server.py --- a/elpa/elpy-1.14.1/elpy/tests/test_server.py +++ b/elpa/elpy-1.18.0/elpy/tests/test_server.py @@ -47,98 +47,37 @@ class TestRPCInit(ServerTestCase): @mock.patch("elpy.jedibackend.JediBackend") - @mock.patch("elpy.ropebackend.RopeBackend") - def test_should_set_project_root(self, RopeBackend, JediBackend): - self.srv.rpc_init({"project_root": "/project/root", - "backend": "rope"}) + def test_should_set_project_root(self, JediBackend): + self.srv.rpc_init({"project_root": "/project/root"}) self.assertEqual("/project/root", self.srv.project_root) @mock.patch("elpy.jedibackend.JediBackend") - @mock.patch("elpy.ropebackend.RopeBackend") - def test_should_initialize_rope(self, RopeBackend, JediBackend): - self.srv.rpc_init({"project_root": "/project/root", - "backend": "rope"}) - - RopeBackend.assert_called_with("/project/root") - - @mock.patch("elpy.jedibackend.JediBackend") - @mock.patch("elpy.ropebackend.RopeBackend") - def test_should_initialize_jedi(self, RopeBackend, JediBackend): - self.srv.rpc_init({"project_root": "/project/root", - "backend": "jedi"}) + def test_should_initialize_jedi(self, JediBackend): + self.srv.rpc_init({"project_root": "/project/root"}) JediBackend.assert_called_with("/project/root") + @mock.patch("elpy.jedibackend.JediBackend") - @mock.patch("elpy.ropebackend.RopeBackend") - def test_should_use_rope_if_available_and_requested( - self, RopeBackend, JediBackend): - RopeBackend.return_value.name = "rope" + def test_should_use_jedi_if_available(self, JediBackend): JediBackend.return_value.name = "jedi" - self.srv.rpc_init({"project_root": "/project/root", - "backend": "rope"}) - - self.assertEqual("rope", self.srv.backend.name) - - @mock.patch("elpy.jedibackend.JediBackend") - @mock.patch("elpy.ropebackend.RopeBackend") - def test_should_use_jedi_if_available_and_requested( - self, RopeBackend, JediBackend): - RopeBackend.return_value.name = "rope" - JediBackend.return_value.name = "jedi" - - self.srv.rpc_init({"project_root": "/project/root", - "backend": "jedi"}) + self.srv.rpc_init({"project_root": "/project/root"}) self.assertEqual("jedi", self.srv.backend.name) - @mock.patch("elpy.jedibackend.JediBackend") - @mock.patch("elpy.ropebackend.RopeBackend") - def test_should_use_rope_if_available_and_nothing_requested( - self, RopeBackend, JediBackend): - RopeBackend.return_value.name = "rope" - JediBackend.return_value.name = "jedi" - - self.srv.rpc_init({"project_root": "/project/root", - "backend": None}) - - self.assertEqual("rope", self.srv.backend.name) @mock.patch("elpy.jedibackend.JediBackend") - @mock.patch("elpy.ropebackend.RopeBackend") - def test_should_use_jedi_if_rope_not_available_and_nothing_requested( - self, RopeBackend, JediBackend): - RopeBackend.return_value.name = "rope" + def test_should_use_none_if_nothing_available( + self, JediBackend): JediBackend.return_value.name = "jedi" - old_rope = server.ropebackend - server.ropebackend = None - - try: - self.srv.rpc_init({"project_root": "/project/root", - "backend": None}) - finally: - server.ropebackend = old_rope - - self.assertEqual("jedi", self.srv.backend.name) - - @mock.patch("elpy.jedibackend.JediBackend") - @mock.patch("elpy.ropebackend.RopeBackend") - def test_should_use_none_if_nothing_available( - self, RopeBackend, JediBackend): - RopeBackend.return_value.name = "rope" - JediBackend.return_value.name = "jedi" - old_rope = server.ropebackend old_jedi = server.jedibackend - server.ropebackend = None server.jedibackend = None try: - self.srv.rpc_init({"project_root": "/project/root", - "backend": None}) + self.srv.rpc_init({"project_root": "/project/root"}) finally: - server.ropebackend = old_rope server.jedibackend = old_jedi self.assertIsNone(self.srv.backend) @@ -230,6 +169,16 @@ "offset")) +class TestRPCGetAssignment(BackendCallTestCase): + def test_should_call_backend(self): + self.assert_calls_backend("rpc_get_assignment") + + def test_should_handle_no_backend(self): + self.srv.backend = None + self.assertIsNone(self.srv.rpc_get_assignment("filname", "source", + "offset")) + + class TestRPCGetDocstring(BackendCallTestCase): def test_should_call_backend(self): self.assert_calls_backend("rpc_get_docstring") @@ -341,17 +290,14 @@ "offset")) -class TestRPCImportMagic(ServerTestCase): - def test_should_call_importmagic(self): - with mock.patch.object(self.srv, "import_magic") as impmagic: - self.srv.rpc_get_import_symbols("filename", "source", "os") - impmagic.get_import_symbols.assert_called_with("os") - self.srv.rpc_add_import("filename", "source", "import os") - impmagic.add_import.assert_called_with("source", "import os") - self.srv.rpc_get_unresolved_symbols("filename", "source") - impmagic.get_unresolved_symbols.assert_called_with("source") - self.srv.rpc_remove_unreferenced_imports("filename", "source") - impmagic.remove_unreferenced_imports.assert_called_with("source") +class TestRPCGetNames(BackendCallTestCase): + def test_should_call_backend(self): + self.assert_calls_backend("rpc_get_names") + + def test_should_handle_no_backend(self): + self.srv.backend = None + with self.assertRaises(rpc.Fault): + self.assertIsNone(self.srv.rpc_get_names("filname", "source", 0)) class TestGetSource(unittest.TestCase): diff --git a/elpa/elpy-1.14.1/elpy/tests/test_support.py b/elpa/elpy-1.18.0/elpy/tests/test_support.py rename from elpa/elpy-1.14.1/elpy/tests/test_support.py rename to elpa/elpy-1.18.0/elpy/tests/test_support.py diff --git a/elpa/elpy-1.14.1/elpy/tests/test_yapf.py b/elpa/elpy-1.18.0/elpy/tests/test_yapf.py rename from elpa/elpy-1.14.1/elpy/tests/test_yapf.py rename to elpa/elpy-1.18.0/elpy/tests/test_yapf.py diff --git a/elpa/elpy-1.14.1/elpy/yapfutil.py b/elpa/elpy-1.18.0/elpy/yapfutil.py rename from elpa/elpy-1.14.1/elpy/yapfutil.py rename to elpa/elpy-1.18.0/elpy/yapfutil.py diff --git a/elpa/elpy-1.14.1/snippets/python-mode/.yas-setup.el b/elpa/elpy-1.18.0/snippets/python-mode/.yas-setup.el rename from elpa/elpy-1.14.1/snippets/python-mode/.yas-setup.el rename to elpa/elpy-1.18.0/snippets/python-mode/.yas-setup.el diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__abs__ b/elpa/elpy-1.18.0/snippets/python-mode/__abs__ rename from elpa/elpy-1.14.1/snippets/python-mode/__abs__ rename to elpa/elpy-1.18.0/snippets/python-mode/__abs__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__add__ b/elpa/elpy-1.18.0/snippets/python-mode/__add__ rename from elpa/elpy-1.14.1/snippets/python-mode/__add__ rename to elpa/elpy-1.18.0/snippets/python-mode/__add__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__and__ b/elpa/elpy-1.18.0/snippets/python-mode/__and__ rename from elpa/elpy-1.14.1/snippets/python-mode/__and__ rename to elpa/elpy-1.18.0/snippets/python-mode/__and__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__bool__ b/elpa/elpy-1.18.0/snippets/python-mode/__bool__ rename from elpa/elpy-1.14.1/snippets/python-mode/__bool__ rename to elpa/elpy-1.18.0/snippets/python-mode/__bool__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__call__ b/elpa/elpy-1.18.0/snippets/python-mode/__call__ rename from elpa/elpy-1.14.1/snippets/python-mode/__call__ rename to elpa/elpy-1.18.0/snippets/python-mode/__call__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__cmp__ b/elpa/elpy-1.18.0/snippets/python-mode/__cmp__ rename from elpa/elpy-1.14.1/snippets/python-mode/__cmp__ rename to elpa/elpy-1.18.0/snippets/python-mode/__cmp__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__coerce__ b/elpa/elpy-1.18.0/snippets/python-mode/__coerce__ rename from elpa/elpy-1.14.1/snippets/python-mode/__coerce__ rename to elpa/elpy-1.18.0/snippets/python-mode/__coerce__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__complex__ b/elpa/elpy-1.18.0/snippets/python-mode/__complex__ rename from elpa/elpy-1.14.1/snippets/python-mode/__complex__ rename to elpa/elpy-1.18.0/snippets/python-mode/__complex__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__contains__ b/elpa/elpy-1.18.0/snippets/python-mode/__contains__ rename from elpa/elpy-1.14.1/snippets/python-mode/__contains__ rename to elpa/elpy-1.18.0/snippets/python-mode/__contains__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__del__ b/elpa/elpy-1.18.0/snippets/python-mode/__del__ rename from elpa/elpy-1.14.1/snippets/python-mode/__del__ rename to elpa/elpy-1.18.0/snippets/python-mode/__del__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__delattr__ b/elpa/elpy-1.18.0/snippets/python-mode/__delattr__ rename from elpa/elpy-1.14.1/snippets/python-mode/__delattr__ rename to elpa/elpy-1.18.0/snippets/python-mode/__delattr__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__delete__ b/elpa/elpy-1.18.0/snippets/python-mode/__delete__ rename from elpa/elpy-1.14.1/snippets/python-mode/__delete__ rename to elpa/elpy-1.18.0/snippets/python-mode/__delete__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__delitem__ b/elpa/elpy-1.18.0/snippets/python-mode/__delitem__ rename from elpa/elpy-1.14.1/snippets/python-mode/__delitem__ rename to elpa/elpy-1.18.0/snippets/python-mode/__delitem__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__div__ b/elpa/elpy-1.18.0/snippets/python-mode/__div__ rename from elpa/elpy-1.14.1/snippets/python-mode/__div__ rename to elpa/elpy-1.18.0/snippets/python-mode/__div__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__divmod__ b/elpa/elpy-1.18.0/snippets/python-mode/__divmod__ rename from elpa/elpy-1.14.1/snippets/python-mode/__divmod__ rename to elpa/elpy-1.18.0/snippets/python-mode/__divmod__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__enter__ b/elpa/elpy-1.18.0/snippets/python-mode/__enter__ rename from elpa/elpy-1.14.1/snippets/python-mode/__enter__ rename to elpa/elpy-1.18.0/snippets/python-mode/__enter__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__eq__ b/elpa/elpy-1.18.0/snippets/python-mode/__eq__ rename from elpa/elpy-1.14.1/snippets/python-mode/__eq__ rename to elpa/elpy-1.18.0/snippets/python-mode/__eq__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__exit__ b/elpa/elpy-1.18.0/snippets/python-mode/__exit__ rename from elpa/elpy-1.14.1/snippets/python-mode/__exit__ rename to elpa/elpy-1.18.0/snippets/python-mode/__exit__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__float__ b/elpa/elpy-1.18.0/snippets/python-mode/__float__ rename from elpa/elpy-1.14.1/snippets/python-mode/__float__ rename to elpa/elpy-1.18.0/snippets/python-mode/__float__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__floordiv__ b/elpa/elpy-1.18.0/snippets/python-mode/__floordiv__ rename from elpa/elpy-1.14.1/snippets/python-mode/__floordiv__ rename to elpa/elpy-1.18.0/snippets/python-mode/__floordiv__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__ge__ b/elpa/elpy-1.18.0/snippets/python-mode/__ge__ rename from elpa/elpy-1.14.1/snippets/python-mode/__ge__ rename to elpa/elpy-1.18.0/snippets/python-mode/__ge__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__get__ b/elpa/elpy-1.18.0/snippets/python-mode/__get__ rename from elpa/elpy-1.14.1/snippets/python-mode/__get__ rename to elpa/elpy-1.18.0/snippets/python-mode/__get__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__getattr__ b/elpa/elpy-1.18.0/snippets/python-mode/__getattr__ rename from elpa/elpy-1.14.1/snippets/python-mode/__getattr__ rename to elpa/elpy-1.18.0/snippets/python-mode/__getattr__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__getattribute__ b/elpa/elpy-1.18.0/snippets/python-mode/__getattribute__ rename from elpa/elpy-1.14.1/snippets/python-mode/__getattribute__ rename to elpa/elpy-1.18.0/snippets/python-mode/__getattribute__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__getitem__ b/elpa/elpy-1.18.0/snippets/python-mode/__getitem__ rename from elpa/elpy-1.14.1/snippets/python-mode/__getitem__ rename to elpa/elpy-1.18.0/snippets/python-mode/__getitem__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__gt__ b/elpa/elpy-1.18.0/snippets/python-mode/__gt__ rename from elpa/elpy-1.14.1/snippets/python-mode/__gt__ rename to elpa/elpy-1.18.0/snippets/python-mode/__gt__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__hash__ b/elpa/elpy-1.18.0/snippets/python-mode/__hash__ rename from elpa/elpy-1.14.1/snippets/python-mode/__hash__ rename to elpa/elpy-1.18.0/snippets/python-mode/__hash__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__hex__ b/elpa/elpy-1.18.0/snippets/python-mode/__hex__ rename from elpa/elpy-1.14.1/snippets/python-mode/__hex__ rename to elpa/elpy-1.18.0/snippets/python-mode/__hex__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__iadd__ b/elpa/elpy-1.18.0/snippets/python-mode/__iadd__ rename from elpa/elpy-1.14.1/snippets/python-mode/__iadd__ rename to elpa/elpy-1.18.0/snippets/python-mode/__iadd__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__iand__ b/elpa/elpy-1.18.0/snippets/python-mode/__iand__ rename from elpa/elpy-1.14.1/snippets/python-mode/__iand__ rename to elpa/elpy-1.18.0/snippets/python-mode/__iand__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__idiv__ b/elpa/elpy-1.18.0/snippets/python-mode/__idiv__ rename from elpa/elpy-1.14.1/snippets/python-mode/__idiv__ rename to elpa/elpy-1.18.0/snippets/python-mode/__idiv__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__ifloordiv__ b/elpa/elpy-1.18.0/snippets/python-mode/__ifloordiv__ rename from elpa/elpy-1.14.1/snippets/python-mode/__ifloordiv__ rename to elpa/elpy-1.18.0/snippets/python-mode/__ifloordiv__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__ilshift__ b/elpa/elpy-1.18.0/snippets/python-mode/__ilshift__ rename from elpa/elpy-1.14.1/snippets/python-mode/__ilshift__ rename to elpa/elpy-1.18.0/snippets/python-mode/__ilshift__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__imod__ b/elpa/elpy-1.18.0/snippets/python-mode/__imod__ rename from elpa/elpy-1.14.1/snippets/python-mode/__imod__ rename to elpa/elpy-1.18.0/snippets/python-mode/__imod__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__imul__ b/elpa/elpy-1.18.0/snippets/python-mode/__imul__ rename from elpa/elpy-1.14.1/snippets/python-mode/__imul__ rename to elpa/elpy-1.18.0/snippets/python-mode/__imul__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__index__ b/elpa/elpy-1.18.0/snippets/python-mode/__index__ rename from elpa/elpy-1.14.1/snippets/python-mode/__index__ rename to elpa/elpy-1.18.0/snippets/python-mode/__index__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__init__ b/elpa/elpy-1.18.0/snippets/python-mode/__init__ rename from elpa/elpy-1.14.1/snippets/python-mode/__init__ rename to elpa/elpy-1.18.0/snippets/python-mode/__init__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__instancecheck__ b/elpa/elpy-1.18.0/snippets/python-mode/__instancecheck__ rename from elpa/elpy-1.14.1/snippets/python-mode/__instancecheck__ rename to elpa/elpy-1.18.0/snippets/python-mode/__instancecheck__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__int__ b/elpa/elpy-1.18.0/snippets/python-mode/__int__ rename from elpa/elpy-1.14.1/snippets/python-mode/__int__ rename to elpa/elpy-1.18.0/snippets/python-mode/__int__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__invert__ b/elpa/elpy-1.18.0/snippets/python-mode/__invert__ rename from elpa/elpy-1.14.1/snippets/python-mode/__invert__ rename to elpa/elpy-1.18.0/snippets/python-mode/__invert__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__ior__ b/elpa/elpy-1.18.0/snippets/python-mode/__ior__ rename from elpa/elpy-1.14.1/snippets/python-mode/__ior__ rename to elpa/elpy-1.18.0/snippets/python-mode/__ior__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__ipow__ b/elpa/elpy-1.18.0/snippets/python-mode/__ipow__ rename from elpa/elpy-1.14.1/snippets/python-mode/__ipow__ rename to elpa/elpy-1.18.0/snippets/python-mode/__ipow__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__irshift__ b/elpa/elpy-1.18.0/snippets/python-mode/__irshift__ rename from elpa/elpy-1.14.1/snippets/python-mode/__irshift__ rename to elpa/elpy-1.18.0/snippets/python-mode/__irshift__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__isub__ b/elpa/elpy-1.18.0/snippets/python-mode/__isub__ rename from elpa/elpy-1.14.1/snippets/python-mode/__isub__ rename to elpa/elpy-1.18.0/snippets/python-mode/__isub__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__iter__ b/elpa/elpy-1.18.0/snippets/python-mode/__iter__ rename from elpa/elpy-1.14.1/snippets/python-mode/__iter__ rename to elpa/elpy-1.18.0/snippets/python-mode/__iter__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__itruediv__ b/elpa/elpy-1.18.0/snippets/python-mode/__itruediv__ rename from elpa/elpy-1.14.1/snippets/python-mode/__itruediv__ rename to elpa/elpy-1.18.0/snippets/python-mode/__itruediv__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__ixor__ b/elpa/elpy-1.18.0/snippets/python-mode/__ixor__ rename from elpa/elpy-1.14.1/snippets/python-mode/__ixor__ rename to elpa/elpy-1.18.0/snippets/python-mode/__ixor__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__le__ b/elpa/elpy-1.18.0/snippets/python-mode/__le__ rename from elpa/elpy-1.14.1/snippets/python-mode/__le__ rename to elpa/elpy-1.18.0/snippets/python-mode/__le__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__len__ b/elpa/elpy-1.18.0/snippets/python-mode/__len__ rename from elpa/elpy-1.14.1/snippets/python-mode/__len__ rename to elpa/elpy-1.18.0/snippets/python-mode/__len__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__long__ b/elpa/elpy-1.18.0/snippets/python-mode/__long__ rename from elpa/elpy-1.14.1/snippets/python-mode/__long__ rename to elpa/elpy-1.18.0/snippets/python-mode/__long__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__lshift__ b/elpa/elpy-1.18.0/snippets/python-mode/__lshift__ rename from elpa/elpy-1.14.1/snippets/python-mode/__lshift__ rename to elpa/elpy-1.18.0/snippets/python-mode/__lshift__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__lt__ b/elpa/elpy-1.18.0/snippets/python-mode/__lt__ rename from elpa/elpy-1.14.1/snippets/python-mode/__lt__ rename to elpa/elpy-1.18.0/snippets/python-mode/__lt__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__mod__ b/elpa/elpy-1.18.0/snippets/python-mode/__mod__ rename from elpa/elpy-1.14.1/snippets/python-mode/__mod__ rename to elpa/elpy-1.18.0/snippets/python-mode/__mod__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__mul__ b/elpa/elpy-1.18.0/snippets/python-mode/__mul__ rename from elpa/elpy-1.14.1/snippets/python-mode/__mul__ rename to elpa/elpy-1.18.0/snippets/python-mode/__mul__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__ne__ b/elpa/elpy-1.18.0/snippets/python-mode/__ne__ rename from elpa/elpy-1.14.1/snippets/python-mode/__ne__ rename to elpa/elpy-1.18.0/snippets/python-mode/__ne__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__neg__ b/elpa/elpy-1.18.0/snippets/python-mode/__neg__ rename from elpa/elpy-1.14.1/snippets/python-mode/__neg__ rename to elpa/elpy-1.18.0/snippets/python-mode/__neg__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__new__ b/elpa/elpy-1.18.0/snippets/python-mode/__new__ rename from elpa/elpy-1.14.1/snippets/python-mode/__new__ rename to elpa/elpy-1.18.0/snippets/python-mode/__new__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__nonzero__ b/elpa/elpy-1.18.0/snippets/python-mode/__nonzero__ rename from elpa/elpy-1.14.1/snippets/python-mode/__nonzero__ rename to elpa/elpy-1.18.0/snippets/python-mode/__nonzero__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__oct__ b/elpa/elpy-1.18.0/snippets/python-mode/__oct__ rename from elpa/elpy-1.14.1/snippets/python-mode/__oct__ rename to elpa/elpy-1.18.0/snippets/python-mode/__oct__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__or__ b/elpa/elpy-1.18.0/snippets/python-mode/__or__ rename from elpa/elpy-1.14.1/snippets/python-mode/__or__ rename to elpa/elpy-1.18.0/snippets/python-mode/__or__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__pos__ b/elpa/elpy-1.18.0/snippets/python-mode/__pos__ rename from elpa/elpy-1.14.1/snippets/python-mode/__pos__ rename to elpa/elpy-1.18.0/snippets/python-mode/__pos__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__pow__ b/elpa/elpy-1.18.0/snippets/python-mode/__pow__ rename from elpa/elpy-1.14.1/snippets/python-mode/__pow__ rename to elpa/elpy-1.18.0/snippets/python-mode/__pow__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__radd__ b/elpa/elpy-1.18.0/snippets/python-mode/__radd__ rename from elpa/elpy-1.14.1/snippets/python-mode/__radd__ rename to elpa/elpy-1.18.0/snippets/python-mode/__radd__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__rand__ b/elpa/elpy-1.18.0/snippets/python-mode/__rand__ rename from elpa/elpy-1.14.1/snippets/python-mode/__rand__ rename to elpa/elpy-1.18.0/snippets/python-mode/__rand__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__rdivmod__ b/elpa/elpy-1.18.0/snippets/python-mode/__rdivmod__ rename from elpa/elpy-1.14.1/snippets/python-mode/__rdivmod__ rename to elpa/elpy-1.18.0/snippets/python-mode/__rdivmod__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__repr__ b/elpa/elpy-1.18.0/snippets/python-mode/__repr__ rename from elpa/elpy-1.14.1/snippets/python-mode/__repr__ rename to elpa/elpy-1.18.0/snippets/python-mode/__repr__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__reversed__ b/elpa/elpy-1.18.0/snippets/python-mode/__reversed__ rename from elpa/elpy-1.14.1/snippets/python-mode/__reversed__ rename to elpa/elpy-1.18.0/snippets/python-mode/__reversed__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__rfloordiv__ b/elpa/elpy-1.18.0/snippets/python-mode/__rfloordiv__ rename from elpa/elpy-1.14.1/snippets/python-mode/__rfloordiv__ rename to elpa/elpy-1.18.0/snippets/python-mode/__rfloordiv__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__rlshift__ b/elpa/elpy-1.18.0/snippets/python-mode/__rlshift__ rename from elpa/elpy-1.14.1/snippets/python-mode/__rlshift__ rename to elpa/elpy-1.18.0/snippets/python-mode/__rlshift__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__rmod__ b/elpa/elpy-1.18.0/snippets/python-mode/__rmod__ rename from elpa/elpy-1.14.1/snippets/python-mode/__rmod__ rename to elpa/elpy-1.18.0/snippets/python-mode/__rmod__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__rmul__ b/elpa/elpy-1.18.0/snippets/python-mode/__rmul__ rename from elpa/elpy-1.14.1/snippets/python-mode/__rmul__ rename to elpa/elpy-1.18.0/snippets/python-mode/__rmul__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__ror__ b/elpa/elpy-1.18.0/snippets/python-mode/__ror__ rename from elpa/elpy-1.14.1/snippets/python-mode/__ror__ rename to elpa/elpy-1.18.0/snippets/python-mode/__ror__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__rpow__ b/elpa/elpy-1.18.0/snippets/python-mode/__rpow__ rename from elpa/elpy-1.14.1/snippets/python-mode/__rpow__ rename to elpa/elpy-1.18.0/snippets/python-mode/__rpow__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__rrshift__ b/elpa/elpy-1.18.0/snippets/python-mode/__rrshift__ rename from elpa/elpy-1.14.1/snippets/python-mode/__rrshift__ rename to elpa/elpy-1.18.0/snippets/python-mode/__rrshift__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__rshift__ b/elpa/elpy-1.18.0/snippets/python-mode/__rshift__ rename from elpa/elpy-1.14.1/snippets/python-mode/__rshift__ rename to elpa/elpy-1.18.0/snippets/python-mode/__rshift__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__rsub__ b/elpa/elpy-1.18.0/snippets/python-mode/__rsub__ rename from elpa/elpy-1.14.1/snippets/python-mode/__rsub__ rename to elpa/elpy-1.18.0/snippets/python-mode/__rsub__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__rtruediv__ b/elpa/elpy-1.18.0/snippets/python-mode/__rtruediv__ rename from elpa/elpy-1.14.1/snippets/python-mode/__rtruediv__ rename to elpa/elpy-1.18.0/snippets/python-mode/__rtruediv__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__rxor__ b/elpa/elpy-1.18.0/snippets/python-mode/__rxor__ rename from elpa/elpy-1.14.1/snippets/python-mode/__rxor__ rename to elpa/elpy-1.18.0/snippets/python-mode/__rxor__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__set__ b/elpa/elpy-1.18.0/snippets/python-mode/__set__ rename from elpa/elpy-1.14.1/snippets/python-mode/__set__ rename to elpa/elpy-1.18.0/snippets/python-mode/__set__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__setattr__ b/elpa/elpy-1.18.0/snippets/python-mode/__setattr__ rename from elpa/elpy-1.14.1/snippets/python-mode/__setattr__ rename to elpa/elpy-1.18.0/snippets/python-mode/__setattr__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__setitem__ b/elpa/elpy-1.18.0/snippets/python-mode/__setitem__ rename from elpa/elpy-1.14.1/snippets/python-mode/__setitem__ rename to elpa/elpy-1.18.0/snippets/python-mode/__setitem__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__slots__ b/elpa/elpy-1.18.0/snippets/python-mode/__slots__ rename from elpa/elpy-1.14.1/snippets/python-mode/__slots__ rename to elpa/elpy-1.18.0/snippets/python-mode/__slots__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__str__ b/elpa/elpy-1.18.0/snippets/python-mode/__str__ rename from elpa/elpy-1.14.1/snippets/python-mode/__str__ rename to elpa/elpy-1.18.0/snippets/python-mode/__str__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__sub__ b/elpa/elpy-1.18.0/snippets/python-mode/__sub__ rename from elpa/elpy-1.14.1/snippets/python-mode/__sub__ rename to elpa/elpy-1.18.0/snippets/python-mode/__sub__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__subclasscheck__ b/elpa/elpy-1.18.0/snippets/python-mode/__subclasscheck__ rename from elpa/elpy-1.14.1/snippets/python-mode/__subclasscheck__ rename to elpa/elpy-1.18.0/snippets/python-mode/__subclasscheck__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__truediv__ b/elpa/elpy-1.18.0/snippets/python-mode/__truediv__ rename from elpa/elpy-1.14.1/snippets/python-mode/__truediv__ rename to elpa/elpy-1.18.0/snippets/python-mode/__truediv__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__unicode__ b/elpa/elpy-1.18.0/snippets/python-mode/__unicode__ rename from elpa/elpy-1.14.1/snippets/python-mode/__unicode__ rename to elpa/elpy-1.18.0/snippets/python-mode/__unicode__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/__xor__ b/elpa/elpy-1.18.0/snippets/python-mode/__xor__ rename from elpa/elpy-1.14.1/snippets/python-mode/__xor__ rename to elpa/elpy-1.18.0/snippets/python-mode/__xor__ diff --git a/elpa/elpy-1.14.1/snippets/python-mode/ase b/elpa/elpy-1.18.0/snippets/python-mode/ase rename from elpa/elpy-1.14.1/snippets/python-mode/ase rename to elpa/elpy-1.18.0/snippets/python-mode/ase diff --git a/elpa/elpy-1.14.1/snippets/python-mode/asne b/elpa/elpy-1.18.0/snippets/python-mode/asne rename from elpa/elpy-1.14.1/snippets/python-mode/asne rename to elpa/elpy-1.18.0/snippets/python-mode/asne diff --git a/elpa/elpy-1.14.1/snippets/python-mode/asr b/elpa/elpy-1.18.0/snippets/python-mode/asr rename from elpa/elpy-1.14.1/snippets/python-mode/asr rename to elpa/elpy-1.18.0/snippets/python-mode/asr diff --git a/elpa/elpy-1.14.1/snippets/python-mode/class b/elpa/elpy-1.18.0/snippets/python-mode/class rename from elpa/elpy-1.14.1/snippets/python-mode/class rename to elpa/elpy-1.18.0/snippets/python-mode/class diff --git a/elpa/elpy-1.14.1/snippets/python-mode/defs b/elpa/elpy-1.18.0/snippets/python-mode/defs rename from elpa/elpy-1.14.1/snippets/python-mode/defs rename to elpa/elpy-1.18.0/snippets/python-mode/defs diff --git a/elpa/elpy-1.14.1/snippets/python-mode/enc b/elpa/elpy-1.18.0/snippets/python-mode/enc rename from elpa/elpy-1.14.1/snippets/python-mode/enc rename to elpa/elpy-1.18.0/snippets/python-mode/enc diff --git a/elpa/elpy-1.14.1/snippets/python-mode/env b/elpa/elpy-1.18.0/snippets/python-mode/env rename from elpa/elpy-1.14.1/snippets/python-mode/env rename to elpa/elpy-1.18.0/snippets/python-mode/env diff --git a/elpa/elpy-1.14.1/snippets/python-mode/from b/elpa/elpy-1.18.0/snippets/python-mode/from rename from elpa/elpy-1.14.1/snippets/python-mode/from rename to elpa/elpy-1.18.0/snippets/python-mode/from diff --git a/elpa/elpy-1.14.1/snippets/python-mode/pdb b/elpa/elpy-1.18.0/snippets/python-mode/pdb rename from elpa/elpy-1.14.1/snippets/python-mode/pdb rename to elpa/elpy-1.18.0/snippets/python-mode/pdb diff --git a/elpa/elpy-1.14.1/snippets/python-mode/py3 b/elpa/elpy-1.18.0/snippets/python-mode/py3 rename from elpa/elpy-1.14.1/snippets/python-mode/py3 rename to elpa/elpy-1.18.0/snippets/python-mode/py3 diff --git a/elpa/elpy-1.14.1/snippets/python-mode/super b/elpa/elpy-1.18.0/snippets/python-mode/super rename from elpa/elpy-1.14.1/snippets/python-mode/super rename to elpa/elpy-1.18.0/snippets/python-mode/super