Merge idea133 into master

This CL merges the following CLs from aosp/idea133 into aosp/master.

dabd634b Snapshot e2750ea61032f2a041cb012bb7b90cffa0deba73 from idea/133.124 of git://git.jetbrains.org/idea/community.git
29ab773f Make default test target run Android specific tests
967117a7 Merge "Make default test target run Android specific tests" into idea133
1eb71f20 Bump memory settings for unit tests
b13ea0d4 Don't bind the delete key to restoring property in layout editor
a20ccfa9 Add Gradle import module
0980f4a2 Add gradle-import to build script
cee6f8ca Fix Gradle notification lookup on Windows.
8668e1be Snapshot 020d29497847701e84e383d965abf543b80758e2 from idea/133.370 of git://git.jetbrains.org/idea/community.git
18f77669 Merge remote-tracking branch 'aosp/snapshot-master' into merge
36ac8cba Update from Guava 13.0.1 to Guava 15
4d451f93 Update libraries to lombok 0.2.2
ee06b1d0 Remove lint-cli dependency from the idea project
ab73dade Updater: add unit tests.
dd558b6e Updater: on Windows, add "Retry" on file op failures.
f2f7178a Snapshot c11f3ac9bbde3f85d1f837ec3eb48a395ed7dd10 from idea/133.471 of git://git.jetbrains.org/idea/community.git
5e4c77db Merge remote-tracking branch 'aosp/snapshot-master' into merge
8d957349 Fix junit.jar path in updater project.
58c3e0ae Include cloud tools tests in default test group
63cd9779 Temporarily disable errors in project structure dialog
e2d6089d Snapshot b9931c55d2175b6552f90b2225eb09c13bd6dfed from idea/133.609 of git://git.jetbrains.org/idea/community.git
031a291e Merge remote-tracking branch 'aosp/snapshot-master' into merge
ea628d6e Remove versions from Info.plist
809cb3e7 Snapshot 9e6329d622cc9649c9c035f28faddc29564a5b7a from idea/133.696 of git://git.jetbrains.org/idea/community.git
d6cfe6ec Merge remote-tracking branch 'aosp/snapshot-master' into merge
38f8c6f0 Gracefully handle build.gradle files in modules without a configured JDK
70ae6f2a Snapshot dc1944e804515a346297e368c3b9c35a203c9912 from idea/133.818 of git://git.jetbrains.org/idea/community.git
ac91a6de Merge remote-tracking branch 'aosp/snapshot-master' into merge
2f51d957 Gradle: respect build classpath order and use both classes and sources jars if available
0ecdb509 Snapshot c50a8ad26a72432f26e39046d6a6f21fd7a190ee from idea/134.1160 of git://git.jetbrains.org/idea/community.git
e8c22ad7 Merge remote-tracking branch 'aosp/snapshot-master' into merge
72253f7d Turn off android framework detection
93f77ee6 Temporarily remove GCT tests
88f318c9 Snapshot 34f078c3452e79ba209d28a551962857e0970e5d from idea/134.1342 of git://git.jetbrains.org/idea/community.git
afb54e4b Merge remote-tracking branch 'aosp/snapshot-master' into merge
4dc795dc Fix updater UI tests.
57a49ed1 Studio patch: more logging.
aa614ee0 table greyer (ability to disable a table)
5c571417 Use Gradle model prebuilts v0.9.0 in Studio.
cb38c25d build script: Run jarjar on the updater
f273ca07 Add App Engine templates dir to build
7607404f Removed android-builder library from Studio (not needed.)
8f29b4eb Merge idea133 changes into master

Change-Id: I12231f26e886dbf5e2e5ac0b1c4bfe18f274d78f
diff --git a/python/src/com/jetbrains/NotNullPredicate.java b/python/src/com/jetbrains/NotNullPredicate.java
new file mode 100644
index 0000000..b34ab62
--- /dev/null
+++ b/python/src/com/jetbrains/NotNullPredicate.java
@@ -0,0 +1,29 @@
+package com.jetbrains;
+
+import com.google.common.base.Predicate;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Filters out nullable elements allowing children to filter not-null elements
+ *
+ * @author Ilya.Kazakevich
+ */
+public class NotNullPredicate<T> implements Predicate<T> {
+  /**
+   * Simply filters nulls
+   */
+  public static final Predicate<Object> INSTANCE = new NotNullPredicate<Object>();
+
+  @Override
+  public final boolean apply(@Nullable final T input) {
+    if (input == null) {
+      return false;
+    }
+    return applyNotNull(input);
+  }
+
+  protected boolean applyNotNull(@NotNull final T input) {
+    return true;
+  }
+}
diff --git a/python/src/com/jetbrains/python/PyBundle.java b/python/src/com/jetbrains/python/PyBundle.java
index 3cb565d..28b853e 100644
--- a/python/src/com/jetbrains/python/PyBundle.java
+++ b/python/src/com/jetbrains/python/PyBundle.java
@@ -18,33 +18,34 @@
 import com.intellij.CommonBundle;
 import com.intellij.reference.SoftReference;
 import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.PropertyKey;
 
 import java.lang.ref.Reference;
 import java.util.ResourceBundle;
 
 // A copy of Ruby's.
+
 /**
  * Resource bundle access.
  * Date: Nov 25, 2008 2:36:10 AM
  */
 public class PyBundle {
-  private static Reference<ResourceBundle> ourBundle;
 
+  public static String message(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, @NotNull Object... params) {
+    return CommonBundle.message(getBundle(), key, params);
+  }
+
+  private static Reference<ResourceBundle> ourBundle;
   @NonNls
   private static final String BUNDLE = "com.jetbrains.python.PyBundle";
 
   private PyBundle() {
   }
 
-  public static String message(@PropertyKey(resourceBundle = BUNDLE)String key, Object... params) {
-    return CommonBundle.message(getBundle(), key, params);
-  }
-
   // Cached loading
   private static ResourceBundle getBundle() {
-    ResourceBundle bundle = null;
-    if (ourBundle != null) bundle = ourBundle.get();
+    ResourceBundle bundle = SoftReference.dereference(ourBundle);
     if (bundle == null) {
       bundle = ResourceBundle.getBundle(BUNDLE);
       ourBundle = new SoftReference<ResourceBundle>(bundle);
diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties
index 5ee3ef7..2e280b4 100644
--- a/python/src/com/jetbrains/python/PyBundle.properties
+++ b/python/src/com/jetbrains/python/PyBundle.properties
@@ -511,7 +511,14 @@
 INSP.NAME.global.undefined=Global variable is undefined at the module level
 INSP.NAME.global.$0.undefined=Global variable ''{0}'' is undefined at the module level
 
+#PyAssignmentToLoopOrWithParameterInspection
+INSP.NAME.assignment.to.loop.or.with.parameter.display.name=Assignment to 'for' loop or 'with' statement parameter
+INSP.NAME.assignment.to.loop.or.with.parameter.display.message=Variable ''{0}'' already declared in ''for'' loop or ''with'' statement above
+
 # Refactoring
+refactoring.will.not.be.accessible=Member, you are trying to move depends on ''{0}'' which will not be accessible after this refactoring
+
+
 # introduce
 refactoring.introduce.name.error=Incorrect name
 refactoring.introduce.selection.error=Cannot perform refactoring using selected element(s)
@@ -529,9 +536,11 @@
 
 # pull up
 refactoring.pull.up.dialog.title=Pull members up to
+refactoring.pull.up.dialog.move.members.to.class=Move members to class
+refactoring.pull.up.dialog.members.to.be.moved=Following members would be moved
 refactoring.pull.up.error.cannot.perform.refactoring.using.selected.elements=Cannot perform pull member up using selected element(s)
 refactoring.pull.up.error.cannot.perform.refactoring.not.inside.class=Cannot perform pull member up: not inside the class
-refactoring.pull.up.error.cannot.perform.refactoring.no.base.classes=Class {0} has no super classes
+refactoring.pull.up.error.cannot.perform.refactoring.no.base.classes=Class {0} has no super classes or none of them could be used for refactoring
 
 # push down
 refactoring.push.down.dialog.title=Push members down from
@@ -553,8 +562,10 @@
 refactoring.extract.method.error.cannot.perform.refactoring.with.local=Cannot perform refactoring from expression with local variables modifications and return instructions inside code fragment
 
 # extract superclass
-refactoring.extract.super.target.path.outside.roots=Target directory is outside the project.<br>Must be within content roots
-refactoring.extract.super.name.0.must.be.ident=Name ''{0}'' is invalid.<br/>Must be a valid Python identifier
+refactoring.extract.super.target.path.outside.roots=Target directory is outside the project. Must be within content roots
+refactoring.extract.super.target.class.already.exists=Class ''{0}'' already exists in this module
+refactoring.extract.super.name.0.must.be.ident=Name ''{0}'' is invalid. Must be a valid Python identifier
+refactoring.extract.super.class.no.members.allowed=None of members could be extracted
 
 # move
 refactoring.move.class.or.function=Move class or function
@@ -585,26 +596,26 @@
 refactoring.change.signature.error.wrong.caret.position.method.name=The caret should be positioned at the name of the method to be refactored.
 
 ### Annotators ###
-ANN.deleting.none=deleting None
-ANN.assign.to.none=assignment to None
-ANN.cant.assign.to.call=can't assign to function call
-ANN.cant.delete.call=can't delete function call
-ANN.cant.aug.assign.to.generator=augmented assign to generator expression not possible
-ANN.cant.aug.assign.to.tuple.or.generator=augmented assign to tuple literal or generator expression not possible
-ANN.cant.assign.to.generator=assign to generator expression not possible
-ANN.cant.assign.to.operator=can't assign to operator
-ANN.cant.assign.to.parens=can't assign to ()
-ANN.cant.assign.to.brackets=can't assign to []
-ANN.cant.aug.assign.to.list.or.comprh=augmented assign to list literal or comprehension not possible
-ANN.cant.assign.to.comprh=can't assign to list comprehension
-ANN.cant.assign.to.dict.comprh=can't assign to dict comprehension
-ANN.cant.assign.to.set.comprh=can't assign to set comprehension
-ANN.cant.aug.assign.to.comprh=augmented assign to list comprehension not possible
-ANN.cant.aug.assign.to.dict.comprh=augmented assign to dict comprehension not possible
-ANN.cant.aug.assign.to.set.comprh=augmented assign to set comprehension not possible
-ANN.cant.assign.to.literal=can't assign to literal
-ANN.cant.delete.literal=can't delete literal
-ANN.cant.assign.to.lambda=can't assign to lambda
+ANN.deleting.none=Deleting None
+ANN.assign.to.none=Assignment to None
+ANN.cant.assign.to.call=Can't assign to function call
+ANN.cant.delete.call=Can't delete function call
+ANN.cant.aug.assign.to.generator=Augmented assign to generator expression not possible
+ANN.cant.aug.assign.to.tuple.or.generator=Augmented assign to tuple literal or generator expression not possible
+ANN.cant.assign.to.generator=Assign to generator expression not possible
+ANN.cant.assign.to.operator=Can't assign to operator
+ANN.cant.assign.to.parens=Can't assign to ()
+ANN.cant.assign.to.brackets=Can't assign to []
+ANN.cant.aug.assign.to.list.or.comprh=Augmented assign to list literal or comprehension not possible
+ANN.cant.assign.to.comprh=Can't assign to list comprehension
+ANN.cant.assign.to.dict.comprh=Can't assign to dict comprehension
+ANN.cant.assign.to.set.comprh=Can't assign to set comprehension
+ANN.cant.aug.assign.to.comprh=Augmented assign to list comprehension not possible
+ANN.cant.aug.assign.to.dict.comprh=Augmented assign to dict comprehension not possible
+ANN.cant.aug.assign.to.set.comprh=Augmented assign to set comprehension not possible
+ANN.cant.assign.to.literal=Can't assign to literal
+ANN.cant.delete.literal=Can't delete literal
+ANN.cant.assign.to.lambda=Can't assign to lambda
 
 ANN.break.outside.loop='break' outside loop
 ANN.continue.outside.loop='continue' outside loop
@@ -637,13 +648,14 @@
 PARSE.expected.colon=':' expected
 PARSE.expected.rpar=')' expected
 PARSE.expected.lpar='(' expected
+PARSE.expected.rbrace='}' expected
 PARSE.expected.tick='`' (backtick) expected
 PARSE.expected.name=name expected
 PARSE.expected.colon.or.rbracket=':' or ']' expected
 PARSE.expected.comma.or.rpar=',' or ')' expected
 PARSE.expected.else='else' expected
 
-PARSE.expected.func.name=function name expected
+PARSE.expected.identifier=Identifier expected
 PARSE.expected.comma.lpar.rpar=',' or '(' or ')' expected
 PARSE.expected.statement.break=Statement break expected
 [email protected]='@' or 'def' expected
@@ -769,9 +781,9 @@
 sdk.error.dialog.failed.modules=Failed modules
 sdk.error.dialog.were.blacklisted=Generation of skeletons for the modules above will be tried again when the modules are updated or a new version of generator is available.
 sdk.gen.querying.$0=Querying skeleton generator for {0}...
-sdk.gen.updating.builtins.$0=Updating skeletons of builtins for {0}
-sdk.gen.updating.$0=Updating skeletons for {0}
-sdk.gen.cleaning.$0=Cleaning up skeletons for {0}
+sdk.gen.updating.builtins.$0=Updating skeletons of builtins for {0}...
+sdk.gen.updating.$0=Updating skeletons for {0}...
+sdk.gen.cleaning.$0=Cleaning up skeletons for {0}...
 sdk.gen.reloading=Reloading generated skeletons...
 sdk.gen.reading.versions.file=Reading versions file...
 sdk.gen.notify.converting.old.skels=Converting old skeletons
diff --git a/python/src/com/jetbrains/python/PyGotoClassContributor.java b/python/src/com/jetbrains/python/PyGotoClassContributor.java
index 11b759e..2662f66 100644
--- a/python/src/com/jetbrains/python/PyGotoClassContributor.java
+++ b/python/src/com/jetbrains/python/PyGotoClassContributor.java
@@ -19,11 +19,14 @@
 import com.intellij.navigation.NavigationItem;
 import com.intellij.openapi.project.Project;
 import com.intellij.util.ArrayUtil;
-import com.jetbrains.python.psi.PyClass;
+import com.intellij.util.containers.HashSet;
 import com.jetbrains.python.psi.stubs.PyClassNameIndex;
+import com.jetbrains.python.psi.stubs.PyModuleNameIndex;
 import org.jetbrains.annotations.NotNull;
 
-import java.util.Collection;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
 
 /**
  * @author yole
@@ -31,13 +34,18 @@
 public class PyGotoClassContributor implements ChooseByNameContributor {
   @NotNull
   public String[] getNames(final Project project, final boolean includeNonProjectItems) {
-    final Collection<String> classNames = PyClassNameIndex.allKeys(project);
-    return ArrayUtil.toStringArray(classNames);
+    Set<String> results = new HashSet<String>();
+    results.addAll(PyClassNameIndex.allKeys(project));
+    results.addAll(PyModuleNameIndex.getAllKeys(project));
+    return ArrayUtil.toStringArray(results);
   }
 
   @NotNull
-  public NavigationItem[] getItemsByName(final String name, final String pattern, final Project project, final boolean includeNonProjectItems) {
-    final Collection<PyClass> classes = PyClassNameIndex.find(name, project, includeNonProjectItems);
-    return classes.toArray(new NavigationItem[classes.size()]);
+  public NavigationItem[] getItemsByName(final String name, final String pattern, final Project project,
+                                         final boolean includeNonProjectItems) {
+    final List<NavigationItem> results = new ArrayList<NavigationItem>();
+    results.addAll(PyClassNameIndex.find(name, project, includeNonProjectItems));
+    results.addAll(PyModuleNameIndex.find(name, project, includeNonProjectItems));
+    return results.toArray(new NavigationItem[results.size()]);
   }
 }
diff --git a/python/src/com/jetbrains/python/PyGotoSymbolContributor.java b/python/src/com/jetbrains/python/PyGotoSymbolContributor.java
index 6a283fd..e5eb410 100644
--- a/python/src/com/jetbrains/python/PyGotoSymbolContributor.java
+++ b/python/src/com/jetbrains/python/PyGotoSymbolContributor.java
@@ -25,6 +25,7 @@
 import com.jetbrains.python.psi.search.PyProjectScopeBuilder;
 import com.jetbrains.python.psi.stubs.PyClassNameIndex;
 import com.jetbrains.python.psi.stubs.PyFunctionNameIndex;
+import com.jetbrains.python.psi.stubs.PyModuleNameIndex;
 import com.jetbrains.python.psi.stubs.PyVariableNameIndex;
 import org.jetbrains.annotations.NotNull;
 
@@ -41,6 +42,7 @@
   public String[] getNames(final Project project, final boolean includeNonProjectItems) {
     Set<String> symbols = new HashSet<String>();
     symbols.addAll(PyClassNameIndex.allKeys(project));
+    symbols.addAll(PyModuleNameIndex.getAllKeys(project));
     symbols.addAll(StubIndex.getInstance().getAllKeys(PyFunctionNameIndex.KEY, project));
     symbols.addAll(StubIndex.getInstance().getAllKeys(PyVariableNameIndex.KEY, project));
     return ArrayUtil.toStringArray(symbols);
@@ -54,6 +56,7 @@
 
     List<NavigationItem> symbols = new ArrayList<NavigationItem>();
     symbols.addAll(PyClassNameIndex.find(name, project, scope));
+    symbols.addAll(PyModuleNameIndex.find(name, project, includeNonProjectItems));
     symbols.addAll(PyFunctionNameIndex.find(name, project, scope));
     symbols.addAll(PyVariableNameIndex.find(name, project, scope));
 
diff --git a/python/src/com/jetbrains/python/PyParameterInfoHandler.java b/python/src/com/jetbrains/python/PyParameterInfoHandler.java
index b3f99f2..5aec444 100644
--- a/python/src/com/jetbrains/python/PyParameterInfoHandler.java
+++ b/python/src/com/jetbrains/python/PyParameterInfoHandler.java
@@ -51,7 +51,7 @@
     return ArrayUtil.EMPTY_OBJECT_ARRAY;  // we don't
   }
 
-  public PyArgumentList findElementForParameterInfo(final CreateParameterInfoContext context) {
+  public PyArgumentList findElementForParameterInfo(@NotNull final CreateParameterInfoContext context) {
     PyArgumentList arglist = findArgumentList(context);
     if (arglist != null) {
       final TypeEvalContext typeEvalContext = TypeEvalContext.userInitiated(arglist.getContainingFile());
@@ -69,11 +69,11 @@
     return ParameterInfoUtils.findParentOfType(context.getFile(), context.getOffset(), PyArgumentList.class);
   }
 
-  public void showParameterInfo(@NotNull final PyArgumentList element, final CreateParameterInfoContext context) {
+  public void showParameterInfo(@NotNull final PyArgumentList element, @NotNull final CreateParameterInfoContext context) {
     context.showHint(element, element.getTextOffset(), this);
   }
 
-  public PyArgumentList findElementForUpdatingParameterInfo(final UpdateParameterInfoContext context) {
+  public PyArgumentList findElementForUpdatingParameterInfo(@NotNull final UpdateParameterInfoContext context) {
     return findArgumentList(context);
   }
 
@@ -82,7 +82,7 @@
    We cannot store an index since we cannot determine what is an argument until we actually map arguments to parameters.
    This is because a tuple in arguments may be a whole argument or map to a tuple parameter.
    */
-  public void updateParameterInfo(@NotNull final PyArgumentList arglist, final UpdateParameterInfoContext context) {
+  public void updateParameterInfo(@NotNull final PyArgumentList arglist, @NotNull final UpdateParameterInfoContext context) {
     if (context.getParameterOwner() != arglist) {
       context.removeHint();
       return;
@@ -125,7 +125,7 @@
   }
 
   @Override
-  public void updateUI(final CallArgumentsMapping prevResult, final ParameterInfoUIContext context) {
+  public void updateUI(final CallArgumentsMapping prevResult, @NotNull final ParameterInfoUIContext context) {
     if (prevResult == null) return;
     final PyArgumentList argList = prevResult.getArgumentList();
     if (!argList.isValid()) return;
diff --git a/python/src/com/jetbrains/python/buildout/BuildoutFacet.java b/python/src/com/jetbrains/python/buildout/BuildoutFacet.java
index eaaa603..e5e607a 100644
--- a/python/src/com/jetbrains/python/buildout/BuildoutFacet.java
+++ b/python/src/com/jetbrains/python/buildout/BuildoutFacet.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -73,7 +73,7 @@
 
     VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() {
       @Override
-      public void contentsChanged(VirtualFileEvent event) {
+      public void contentsChanged(@NotNull VirtualFileEvent event) {
         if (Comparing.equal(event.getFile(), getScript())) {
           updatePaths();
           attachLibrary(module);
diff --git a/python/src/com/jetbrains/python/codeInsight/PyBreakContinueGotoProvider.java b/python/src/com/jetbrains/python/codeInsight/PyBreakContinueGotoProvider.java
index 51ff238..778999a 100644
--- a/python/src/com/jetbrains/python/codeInsight/PyBreakContinueGotoProvider.java
+++ b/python/src/com/jetbrains/python/codeInsight/PyBreakContinueGotoProvider.java
@@ -26,15 +26,15 @@
 import com.jetbrains.python.PyTokenTypes;
 import com.jetbrains.python.PythonLanguage;
 import com.jetbrains.python.psi.*;
+import org.jetbrains.annotations.Nullable;
 
 /**
  * Provides reaction on ctrl+click for {@code break} and {@code continue} statements.
- * User: dcheryasov
- * Date: Nov 5, 2009 4:58:54 AM
+ * @author dcheryasov
  */
 public class PyBreakContinueGotoProvider extends GotoDeclarationHandlerBase {
-
-  public PsiElement getGotoDeclarationTarget(PsiElement source, Editor editor) {
+  @Override
+  public PsiElement getGotoDeclarationTarget(@Nullable PsiElement source, Editor editor) {
     if (source != null && source.getLanguage() instanceof PythonLanguage) {
       final PyLoopStatement loop = PsiTreeUtil.getParentOfType(source, PyLoopStatement.class, false, PyFunction.class, PyClass.class);
       if (loop != null) {
@@ -66,6 +66,7 @@
         }
       }
     }
+
     return null;
   }
 }
diff --git a/python/src/com/jetbrains/python/codeInsight/PyContainerProvider.java b/python/src/com/jetbrains/python/codeInsight/PyContainerProvider.java
new file mode 100644
index 0000000..11a3fe5
--- /dev/null
+++ b/python/src/com/jetbrains/python/codeInsight/PyContainerProvider.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.codeInsight;
+
+import com.intellij.codeInsight.ContainerProvider;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.StubBasedPsiElement;
+import com.intellij.psi.stubs.StubElement;
+import com.jetbrains.python.psi.PyElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author vlan
+ */
+public class PyContainerProvider implements ContainerProvider {
+  @Nullable
+  @Override
+  public PsiElement getContainer(@NotNull PsiElement item) {
+    if (item instanceof PyElement && item instanceof StubBasedPsiElement) {
+      return getContainerByStub((StubBasedPsiElement)item);
+    }
+    return null;
+  }
+
+  @Nullable
+  private static PsiElement getContainerByStub(@NotNull StubBasedPsiElement element) {
+    final StubElement stub = element.getStub();
+    if (stub != null) {
+      final StubElement parentStub = stub.getParentStub();
+      if (parentStub != null) {
+        return parentStub.getPsi();
+      }
+    }
+    return null;
+  }
+}
diff --git a/python/src/com/jetbrains/python/codeInsight/PyInjectionUtil.java b/python/src/com/jetbrains/python/codeInsight/PyInjectionUtil.java
index d68e6c6..fe51d64 100644
--- a/python/src/com/jetbrains/python/codeInsight/PyInjectionUtil.java
+++ b/python/src/com/jetbrains/python/codeInsight/PyInjectionUtil.java
@@ -23,6 +23,7 @@
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import java.util.Arrays;
 import java.util.List;
 
 import static com.jetbrains.python.inspections.PyStringFormatParser.*;
@@ -31,43 +32,85 @@
  * @author vlan
  */
 public class PyInjectionUtil {
+
+  public static class InjectionResult {
+    public static InjectionResult EMPTY = new InjectionResult(false, true);
+
+    private final boolean myInjected;
+    private final boolean myStrict;
+
+    private InjectionResult(boolean injected, boolean strict) {
+      myInjected = injected;
+      myStrict = strict;
+    }
+
+    public boolean isInjected() {
+      return myInjected;
+    }
+
+    public boolean isStrict() {
+      return myStrict;
+    }
+
+    public InjectionResult append(@NotNull InjectionResult result) {
+      return new InjectionResult(myInjected || result.isInjected(), myStrict && result.isStrict());
+    }
+  }
+
+  public static final List<Class<? extends PyExpression>> ELEMENTS_TO_INJECT_IN =
+    Arrays.asList(PyStringLiteralExpression.class, PyParenthesizedExpression.class, PyBinaryExpression.class, PyCallExpression.class);
+
   private PyInjectionUtil() {}
 
   /**
-   * Returns true if the element is the largest expression that represents a string literal, possibly with concatenation, parentheses,
-   * or formatting.
+   * Returns the largest expression in the specified context that represents a string literal suitable for language injection, possibly
+   * with concatenation, parentheses, or formatting.
    */
-  public static boolean isLargestStringLiteral(@NotNull PsiElement element) {
-    final PsiElement parent = element.getParent();
-    return isStringLiteralPart(element) && (parent == null || !isStringLiteralPart(parent));
+  @Nullable
+  public static PsiElement getLargestStringLiteral(@NotNull PsiElement context) {
+    PsiElement element = null;
+    for (PsiElement current = context; current != null && isStringLiteralPart(current, element); current = current.getParent()) {
+      element = current;
+    }
+    return element;
   }
 
   /**
    * Registers language injections in the given registrar for the specified string literal element or its ancestor that contains
    * string concatenations or formatting.
    */
-  public static void registerStringLiteralInjection(@NotNull PsiElement element, @NotNull MultiHostRegistrar registrar) {
-    processStringLiteral(element, registrar, "", "", Formatting.NONE);
+  @NotNull
+  public static InjectionResult registerStringLiteralInjection(@NotNull PsiElement element, @NotNull MultiHostRegistrar registrar) {
+    // Assume percent formatting since the MySQL parser cannot handle Python-style substitutions
+    return processStringLiteral(element, registrar, "", "", Formatting.PERCENT);
   }
 
-  private static boolean isStringLiteralPart(@NotNull PsiElement element) {
-    if (element instanceof PyStringLiteralExpression) {
+  private static boolean isStringLiteralPart(@NotNull PsiElement element, @Nullable PsiElement context) {
+    if (element == context) {
+      return true;
+    }
+    else if (element instanceof PyStringLiteralExpression) {
       return true;
     }
     else if (element instanceof PyParenthesizedExpression) {
       final PyExpression contained = ((PyParenthesizedExpression)element).getContainedExpression();
-      return contained != null && isStringLiteralPart(contained);
+      return contained != null && isStringLiteralPart(contained, context);
     }
     else if (element instanceof PyBinaryExpression) {
       final PyBinaryExpression expr = (PyBinaryExpression)element;
       final PyExpression left = expr.getLeftExpression();
       final PyExpression right = expr.getRightExpression();
-      return (expr.isOperator("+") && (isStringLiteralPart(left) || right != null && isStringLiteralPart(right))) ||
-              expr.isOperator("%") && isStringLiteralPart(left);
+      if (expr.isOperator("+")) {
+        return isStringLiteralPart(left, context) || right != null && isStringLiteralPart(right, context);
+      }
+      else if (expr.isOperator("%")) {
+        return right != context && isStringLiteralPart(left, context);
+      }
+      return false;
     }
     else if (element instanceof PyCallExpression) {
       final PyExpression qualifier = getFormatCallQualifier((PyCallExpression)element);
-      return qualifier != null && isStringLiteralPart(qualifier);
+      return qualifier != null && isStringLiteralPart(qualifier, context);
     }
     return false;
   }
@@ -85,10 +128,13 @@
     return null;
   }
 
-  private static void processStringLiteral(@NotNull PsiElement element, @NotNull MultiHostRegistrar registrar, @NotNull String prefix,
-                                           @NotNull String suffix, @NotNull Formatting formatting) {
-    final String missingValue = "missing";
+  @NotNull
+  private static InjectionResult processStringLiteral(@NotNull PsiElement element, @NotNull MultiHostRegistrar registrar,
+                                                      @NotNull String prefix, @NotNull String suffix, @NotNull Formatting formatting) {
+    final String missingValue = "missing_value";
     if (element instanceof PyStringLiteralExpression) {
+      boolean injected = false;
+      boolean strict = true;
       final PyStringLiteralExpression expr = (PyStringLiteralExpression)element;
       final List<TextRange> ranges = expr.getStringValueTextRanges();
       final String text = expr.getText();
@@ -96,53 +142,78 @@
         if (formatting != Formatting.NONE) {
           final String part = range.substring(text);
           final List<FormatStringChunk> chunks = formatting == Formatting.NEW_STYLE ? parseNewStyleFormat(part) : parsePercentFormat(part);
+          if (!filterSubstitutions(chunks).isEmpty()) {
+            strict = false;
+          }
           for (int i = 0; i < chunks.size(); i++) {
             final FormatStringChunk chunk = chunks.get(i);
             if (chunk instanceof ConstantChunk) {
               final int nextIndex = i + 1;
-              final String chunkPrefix = i == 1 && chunks.get(0) instanceof SubstitutionChunk ? missingValue : "";
-              final String chunkSuffix = nextIndex < chunks.size() &&
-                                         chunks.get(nextIndex) instanceof SubstitutionChunk ? missingValue : "";
+              final String chunkPrefix;
+              if (i == 1 && chunks.get(0) instanceof SubstitutionChunk) {
+                chunkPrefix = missingValue;
+              }
+              else if (i == 0) {
+                chunkPrefix = prefix;
+              } else {
+                chunkPrefix = "";
+              }
+              final String chunkSuffix;
+              if (nextIndex < chunks.size() && chunks.get(nextIndex) instanceof SubstitutionChunk) {
+                chunkSuffix = missingValue;
+              }
+              else if (nextIndex == chunks.size()) {
+                chunkSuffix = suffix;
+              }
+              else {
+                chunkSuffix = "";
+              }
               final TextRange chunkRange = chunk.getTextRange().shiftRight(range.getStartOffset());
               registrar.addPlace(chunkPrefix, chunkSuffix, expr, chunkRange);
+              injected = true;
             }
           }
         }
         else {
           registrar.addPlace(prefix, suffix, expr, range);
+          injected = true;
         }
       }
+      return new InjectionResult(injected, strict);
     }
     else if (element instanceof PyParenthesizedExpression) {
       final PyExpression contained = ((PyParenthesizedExpression)element).getContainedExpression();
       if (contained != null) {
-        processStringLiteral(contained, registrar, prefix, suffix, formatting);
+        return processStringLiteral(contained, registrar, prefix, suffix, formatting);
       }
     }
     else if (element instanceof PyBinaryExpression) {
       final PyBinaryExpression expr = (PyBinaryExpression)element;
       final PyExpression left = expr.getLeftExpression();
       final PyExpression right = expr.getRightExpression();
-      final boolean isLeftString = isStringLiteralPart(left);
+      final boolean isLeftString = isStringLiteralPart(left, null);
       if (expr.isOperator("+")) {
-        final boolean isRightString = right != null && isStringLiteralPart(right);
+        final boolean isRightString = right != null && isStringLiteralPart(right, null);
+        InjectionResult result = InjectionResult.EMPTY;
         if (isLeftString) {
-          processStringLiteral(left, registrar, prefix, isRightString ? "" : missingValue, formatting);
+          result = result.append(processStringLiteral(left, registrar, prefix, isRightString ? "" : missingValue, formatting));
         }
         if (isRightString) {
-          processStringLiteral(right, registrar, isLeftString ? "" : missingValue, suffix, formatting);
+          result = result.append(processStringLiteral(right, registrar, isLeftString ? "" : missingValue, suffix, formatting));
         }
+        return result;
       }
       else if (expr.isOperator("%")) {
-        processStringLiteral(left, registrar, prefix, suffix, Formatting.PERCENT);
+        return processStringLiteral(left, registrar, prefix, suffix, Formatting.PERCENT);
       }
     }
     else if (element instanceof PyCallExpression) {
       final PyExpression qualifier = getFormatCallQualifier((PyCallExpression)element);
       if (qualifier != null) {
-        processStringLiteral(qualifier, registrar, prefix, suffix, Formatting.NEW_STYLE);
+        return processStringLiteral(qualifier, registrar, prefix, suffix, Formatting.NEW_STYLE);
       }
     }
+    return InjectionResult.EMPTY;
   }
 
   private enum Formatting {
diff --git a/python/src/com/jetbrains/python/codeInsight/PyInjectorBase.java b/python/src/com/jetbrains/python/codeInsight/PyInjectorBase.java
new file mode 100644
index 0000000..4fd2560
--- /dev/null
+++ b/python/src/com/jetbrains/python/codeInsight/PyInjectorBase.java
@@ -0,0 +1,45 @@
+package com.jetbrains.python.codeInsight;
+
+import com.intellij.lang.Language;
+import com.intellij.lang.injection.MultiHostInjector;
+import com.intellij.lang.injection.MultiHostRegistrar;
+import com.intellij.psi.PsiElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * @author vlan
+ */
+public abstract class PyInjectorBase implements MultiHostInjector {
+  @Override
+  public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) {
+    registerInjection(registrar, context);
+  }
+
+  @NotNull
+  @Override
+  public List<? extends Class<? extends PsiElement>> elementsToInjectIn() {
+    return PyInjectionUtil.ELEMENTS_TO_INJECT_IN;
+  }
+
+  @Nullable
+  public abstract Language getInjectedLanguage(@NotNull PsiElement context);
+
+  protected PyInjectionUtil.InjectionResult registerInjection(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) {
+    final Language language = getInjectedLanguage(context);
+    if (language != null) {
+      final PsiElement element = PyInjectionUtil.getLargestStringLiteral(context);
+      if (element != null) {
+        registrar.startInjecting(language);
+        final PyInjectionUtil.InjectionResult result = PyInjectionUtil.registerStringLiteralInjection(element, registrar);
+        if (result.isInjected()) {
+          registrar.doneInjecting();
+        }
+        return result;
+      }
+    }
+    return PyInjectionUtil.InjectionResult.EMPTY;
+  }
+}
diff --git a/python/src/com/jetbrains/python/codeInsight/KeywordTypedHandler.java b/python/src/com/jetbrains/python/codeInsight/PyKeywordTypedHandler.java
similarity index 94%
rename from python/src/com/jetbrains/python/codeInsight/KeywordTypedHandler.java
rename to python/src/com/jetbrains/python/codeInsight/PyKeywordTypedHandler.java
index 5e3e6ca..3bf094d 100644
--- a/python/src/com/jetbrains/python/codeInsight/KeywordTypedHandler.java
+++ b/python/src/com/jetbrains/python/codeInsight/PyKeywordTypedHandler.java
@@ -30,7 +30,7 @@
  * User: dcheryasov
  * Date: May 29, 2009 4:42:03 AM
  */
-public class KeywordTypedHandler extends TypedHandlerDelegate {
+public class PyKeywordTypedHandler extends TypedHandlerDelegate {
   @Override
   public Result beforeCharTyped(char character, Project project, Editor editor, PsiFile file, FileType fileType) {
     if (!(fileType instanceof PythonFileType)) return Result.CONTINUE; // else we'd mess up with other file types!
@@ -51,7 +51,7 @@
         editor.getCaretModel().moveToOffset(offset + 1); // overtype, that is, jump over
         return Result.STOP;
       }
-      UnindentingInsertHandler.unindentAsNeeded(project, editor, file);
+      PyUnindentingInsertHandler.unindentAsNeeded(project, editor, file);
     }
 
     return Result.CONTINUE; // the default
diff --git a/python/src/com/jetbrains/python/codeInsight/UnindentingInsertHandler.java b/python/src/com/jetbrains/python/codeInsight/PyUnindentingInsertHandler.java
similarity index 71%
rename from python/src/com/jetbrains/python/codeInsight/UnindentingInsertHandler.java
rename to python/src/com/jetbrains/python/codeInsight/PyUnindentingInsertHandler.java
index cd29026..d932d71 100644
--- a/python/src/com/jetbrains/python/codeInsight/UnindentingInsertHandler.java
+++ b/python/src/com/jetbrains/python/codeInsight/PyUnindentingInsertHandler.java
@@ -25,6 +25,7 @@
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiFile;
 import com.intellij.psi.util.PsiTreeUtil;
+import com.jetbrains.python.PyNames;
 import com.jetbrains.python.codeInsight.completion.PythonLookupElement;
 import com.jetbrains.python.psi.PyStatementWithElse;
 import com.jetbrains.python.psi.PyTryExceptStatement;
@@ -34,10 +35,10 @@
  * User: dcheryasov
  * Date: Mar 2, 2010 6:48:40 PM
  */
-public class UnindentingInsertHandler implements InsertHandler<PythonLookupElement> {
-  public final static UnindentingInsertHandler INSTANCE = new UnindentingInsertHandler();
+public class PyUnindentingInsertHandler implements InsertHandler<PythonLookupElement> {
+  public final static PyUnindentingInsertHandler INSTANCE = new PyUnindentingInsertHandler();
 
-  private UnindentingInsertHandler() {
+  private PyUnindentingInsertHandler() {
   }
 
   public void handleInsert(InsertionContext context, PythonLookupElement item) {
@@ -46,6 +47,7 @@
 
   /**
    * Unindent current line to be flush with a starting part, detecting the part if necessary.
+   *
    * @param project
    * @param editor
    * @param file
@@ -56,7 +58,9 @@
     final Document document = editor.getDocument();
     int offset = editor.getCaretModel().getOffset();
     CharSequence text = document.getCharsSequence();
-    if (offset >= text.length()) offset = text.length() - 1;
+    if (offset >= text.length()) {
+      offset = text.length() - 1;
+    }
 
     int line_start_offset = document.getLineStartOffset(document.getLineNumber(offset));
     int nonspace_offset = findBeginning(line_start_offset, text);
@@ -64,26 +68,29 @@
 
     Class<? extends PsiElement> parentClass = null;
 
-    int last_offset = nonspace_offset + "finally".length(); // the longest of all
+    int last_offset = nonspace_offset + PyNames.FINALLY.length(); // the longest of all
     if (last_offset > offset) last_offset = offset;
     int local_length = last_offset - nonspace_offset + 1;
     if (local_length > 0) {
-      String piece = text.subSequence(nonspace_offset, last_offset+1).toString();
-      final int else_len = "else".length();
+      String piece = text.subSequence(nonspace_offset, last_offset + 1).toString();
+      final int else_len = PyNames.ELSE.length();
       if (local_length >= else_len) {
-        if ((piece.startsWith("else") || piece.startsWith("elif")) && (else_len == piece.length() || piece.charAt(else_len) < 'a' || piece.charAt(else_len) > 'z')) {
+        if ((piece.startsWith(PyNames.ELSE) || piece.startsWith(PyNames.ELIF)) &&
+            (else_len == piece.length() || piece.charAt(else_len) < 'a' || piece.charAt(else_len) > 'z')) {
           parentClass = PyStatementWithElse.class;
         }
       }
-      final int except_len = "except".length();
+      final int except_len = PyNames.EXCEPT.length();
       if (local_length >= except_len) {
-        if (piece.startsWith("except") && (except_len == piece.length() || piece.charAt(except_len) < 'a' || piece.charAt(except_len) > 'z')) {
+        if (piece.startsWith(PyNames.EXCEPT) &&
+            (except_len == piece.length() || piece.charAt(except_len) < 'a' || piece.charAt(except_len) > 'z')) {
           parentClass = PyTryExceptStatement.class;
         }
       }
-      final int finally_len = "finally".length();
+      final int finally_len = PyNames.FINALLY.length();
       if (local_length >= finally_len) {
-        if (piece.startsWith("finally") && (finally_len == piece.length() || piece.charAt(finally_len) < 'a' || piece.charAt(finally_len) > 'z')) {
+        if (piece.startsWith(PyNames.FINALLY) &&
+            (finally_len == piece.length() || piece.charAt(finally_len) < 'a' || piece.charAt(finally_len) > 'z')) {
           parentClass = PyTryExceptStatement.class;
         }
       }
@@ -94,14 +101,17 @@
 
     PsiDocumentManager.getInstance(project).commitDocument(document); // reparse
 
-    PsiElement token = file.findElementAt(offset-2); // -1 is our ':'; -2 is even safer.
+    PsiElement token = file.findElementAt(offset - 2); // -1 is our ':'; -2 is even safer.
     PsiElement outer = PsiTreeUtil.getParentOfType(token, parentClass);
     if (outer != null) {
       int outer_offset = outer.getTextOffset();
       int outer_indent = outer_offset - document.getLineStartOffset(document.getLineNumber(outer_offset));
       assert outer_indent >= 0;
       int current_indent = nonspace_offset - line_start_offset;
-      EditorActionUtil.indentLine(project, editor, document.getLineNumber(offset), outer_indent - current_indent);
+      int indent = outer_indent - current_indent;
+      EditorActionUtil.indentLine(project, editor, document.getLineNumber(offset), editor.getSettings().isUseTabCharacter(project)
+                                                                                   ? indent * editor.getSettings().getTabSize(project)
+                                                                                   : indent);
       return true;
     }
     return false;
diff --git a/python/src/com/jetbrains/python/codeInsight/completion/OverwriteEqualsInsertHandler.java b/python/src/com/jetbrains/python/codeInsight/completion/OverwriteEqualsInsertHandler.java
index 1fb1070..e70d1e6 100644
--- a/python/src/com/jetbrains/python/codeInsight/completion/OverwriteEqualsInsertHandler.java
+++ b/python/src/com/jetbrains/python/codeInsight/completion/OverwriteEqualsInsertHandler.java
@@ -34,7 +34,7 @@
     }
     Document doc = context.getDocument();
     int tailOffset = context.getTailOffset();
-    if (doc.getCharsSequence().charAt(tailOffset) == '=') {
+    if (tailOffset < doc.getCharsSequence().length() && doc.getCharsSequence().charAt(tailOffset) == '=') {
       doc.deleteString(tailOffset, tailOffset+1);
     }
   }
diff --git a/python/src/com/jetbrains/python/codeInsight/completion/PyBracketProtectingCompletionContributor.java b/python/src/com/jetbrains/python/codeInsight/completion/PyBracketProtectingCompletionContributor.java
new file mode 100644
index 0000000..766e5be
--- /dev/null
+++ b/python/src/com/jetbrains/python/codeInsight/completion/PyBracketProtectingCompletionContributor.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.codeInsight.completion;
+
+import com.intellij.codeInsight.completion.CompletionContributor;
+import com.intellij.codeInsight.completion.CompletionInitializationContext;
+import com.intellij.psi.PsiReference;
+import com.jetbrains.python.psi.impl.references.PyOperatorReference;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author yole
+ */
+public class PyBracketProtectingCompletionContributor extends CompletionContributor {
+  @Override
+  public void beforeCompletion(@NotNull CompletionInitializationContext context) {
+    PsiReference ref = context.getFile().findReferenceAt(context.getSelectionEndOffset());
+    if (ref instanceof PyOperatorReference) {
+      context.setReplacementOffset(context.getIdentifierEndOffset());
+    }
+  }
+}
diff --git a/python/src/com/jetbrains/python/codeInsight/completion/PyClassNameCompletionContributor.java b/python/src/com/jetbrains/python/codeInsight/completion/PyClassNameCompletionContributor.java
index 7725203c..24318f8 100644
--- a/python/src/com/jetbrains/python/codeInsight/completion/PyClassNameCompletionContributor.java
+++ b/python/src/com/jetbrains/python/codeInsight/completion/PyClassNameCompletionContributor.java
@@ -54,7 +54,7 @@
     if (parameters.isExtendedCompletion()) {
       final PsiElement element = parameters.getPosition();
       final PsiElement parent = element.getParent();
-      if (parent instanceof PyReferenceExpression && ((PyReferenceExpression)parent).getQualifier() != null) {
+      if (parent instanceof PyReferenceExpression && ((PyReferenceExpression)parent).isQualified()) {
         return;
       }
       if (parent instanceof PyStringLiteralExpression) {
@@ -71,12 +71,12 @@
       final PsiFile originalFile = parameters.getOriginalFile();
       addVariantsFromIndex(result, originalFile, PyClassNameIndex.KEY,
                            parent instanceof PyStringLiteralExpression ? STRING_LITERAL_INSERT_HANDLER : IMPORTING_INSERT_HANDLER,
-                           Conditions.<PyClass>alwaysTrue());
+                           Conditions.<PyClass>alwaysTrue(), PyClass.class);
       addVariantsFromIndex(result, originalFile, PyFunctionNameIndex.KEY,
-                           getFunctionInsertHandler(parent), IS_TOPLEVEL);
+                           getFunctionInsertHandler(parent), IS_TOPLEVEL, PyFunction.class);
       addVariantsFromIndex(result, originalFile, PyVariableNameIndex.KEY,
                            parent instanceof PyStringLiteralExpression ? STRING_LITERAL_INSERT_HANDLER : IMPORTING_INSERT_HANDLER,
-                           IS_TOPLEVEL);
+                           IS_TOPLEVEL, PyTargetExpression.class);
       addVariantsFromModules(result, originalFile, parent instanceof PyStringLiteralExpression);
     }
   }
@@ -117,13 +117,13 @@
                                                                        final PsiFile targetFile,
                                                                        final StubIndexKey<String, T> key,
                                                                        final InsertHandler<LookupElement> insertHandler,
-                                                                       final Condition<? super T> condition) {
+                                                                       final Condition<? super T> condition, Class<T> elementClass) {
     final Project project = targetFile.getProject();
     GlobalSearchScope scope = PyProjectScopeBuilder.excludeSdkTestsScope(targetFile);
 
     Collection<String> keys = StubIndex.getInstance().getAllKeys(key, project);
     for (final String elementName : CompletionUtil.sortMatching(resultSet.getPrefixMatcher(), keys)) {
-      for (T element : StubIndex.getInstance().get(key, elementName, project, scope)) {
+      for (T element : StubIndex.getElements(key, elementName, project, scope, elementClass)) {
         if (condition.value(element)) {
           resultSet.addElement(LookupElementBuilder.createWithIcon(element)
                                  .withTailText(" " + ((NavigationItem)element).getPresentation().getLocationString(), true)
diff --git a/python/src/com/jetbrains/python/codeInsight/completion/PyDocstringCompletionContributor.java b/python/src/com/jetbrains/python/codeInsight/completion/PyDocstringCompletionContributor.java
index 84d4b88..272adfa 100644
--- a/python/src/com/jetbrains/python/codeInsight/completion/PyDocstringCompletionContributor.java
+++ b/python/src/com/jetbrains/python/codeInsight/completion/PyDocstringCompletionContributor.java
@@ -16,6 +16,7 @@
 package com.jetbrains.python.codeInsight.completion;
 
 import com.intellij.codeInsight.completion.*;
+import com.intellij.codeInsight.lookup.AutoCompletionPolicy;
 import com.intellij.codeInsight.lookup.LookupElementBuilder;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.module.ModuleUtilCore;
@@ -23,8 +24,6 @@
 import com.intellij.psi.PsiFile;
 import com.intellij.psi.util.PsiTreeUtil;
 import com.intellij.util.ProcessingContext;
-import com.jetbrains.python.PyTokenTypes;
-import com.jetbrains.python.documentation.PyDocumentationSettings;
 import com.jetbrains.python.psi.PyDocStringOwner;
 import com.jetbrains.python.psi.PyStringLiteralExpression;
 import com.jetbrains.python.refactoring.PyRefactoringUtil;
@@ -32,7 +31,9 @@
 
 import java.util.Collection;
 
+import static com.intellij.patterns.PlatformPatterns.psiComment;
 import static com.intellij.patterns.PlatformPatterns.psiElement;
+import static com.intellij.patterns.StandardPatterns.or;
 
 /**
  * User : ktisha
@@ -40,7 +41,7 @@
 public class PyDocstringCompletionContributor extends CompletionContributor {
   public PyDocstringCompletionContributor() {
     extend(CompletionType.BASIC,
-           psiElement().inside(PyStringLiteralExpression.class).withElementType(PyTokenTypes.DOCSTRING),
+           or(psiElement().inside(PyStringLiteralExpression.class), psiComment()),
            new IdentifierCompletionProvider());
   }
 
@@ -54,23 +55,22 @@
                                   ProcessingContext context,
                                   @NotNull CompletionResultSet result) {
       if (parameters.isAutoPopup()) return;
-      final PyDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(parameters.getOriginalPosition(), PyDocStringOwner.class);
-      if (docStringOwner != null) {
-        final PsiFile file = docStringOwner.getContainingFile();
-        final Module module = ModuleUtilCore.findModuleForPsiElement(docStringOwner);
-        if (module != null) {
-          final PyDocumentationSettings settings = PyDocumentationSettings.getInstance(module);
-          if (!settings.isPlain(file)) return;
-          result = result.withPrefixMatcher(getPrefix(parameters.getOffset(), file));
-          final Collection<String> identifiers = PyRefactoringUtil.collectUsedNames(docStringOwner);
-          for (String identifier : identifiers)
-            result.addElement(LookupElementBuilder.create(identifier));
+      final PsiElement element = parameters.getOriginalPosition();
+      if (element == null) return;
+      final PsiFile file = element.getContainingFile();
+      if (file.findReferenceAt(parameters.getOffset()) != null) return;
+      final PyDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(element, PyDocStringOwner.class);
+      final Module module = ModuleUtilCore.findModuleForPsiElement(element);
+      if (module != null) {
+        result = result.withPrefixMatcher(getPrefix(parameters.getOffset(), file));
+        final Collection<String> identifiers = PyRefactoringUtil.collectUsedNames(docStringOwner);
+        for (String identifier : identifiers)
+          result.addElement(LookupElementBuilder.create(identifier).withAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE));
 
 
-          final Collection<String> fileIdentifiers = PyRefactoringUtil.collectUsedNames(parameters.getOriginalFile());
-          for (String identifier : fileIdentifiers)
-            result.addElement(LookupElementBuilder.create(identifier));
-        }
+        final Collection<String> fileIdentifiers = PyRefactoringUtil.collectUsedNames(parameters.getOriginalFile());
+        for (String identifier : fileIdentifiers)
+          result.addElement(LookupElementBuilder.create(identifier).withAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE));
       }
     }
   }
diff --git a/python/src/com/jetbrains/python/codeInsight/completion/PyKeywordCompletionContributor.java b/python/src/com/jetbrains/python/codeInsight/completion/PyKeywordCompletionContributor.java
index 44ef899..ed6f2e3 100644
--- a/python/src/com/jetbrains/python/codeInsight/completion/PyKeywordCompletionContributor.java
+++ b/python/src/com/jetbrains/python/codeInsight/completion/PyKeywordCompletionContributor.java
@@ -33,7 +33,7 @@
 import com.jetbrains.python.PyNames;
 import com.jetbrains.python.PyTokenTypes;
 import com.jetbrains.python.PythonLanguage;
-import com.jetbrains.python.codeInsight.UnindentingInsertHandler;
+import com.jetbrains.python.codeInsight.PyUnindentingInsertHandler;
 import com.jetbrains.python.documentation.doctest.PyDocstringFile;
 import com.jetbrains.python.psi.*;
 import org.jetbrains.annotations.NonNls;
@@ -286,9 +286,9 @@
     psiElement()
       .inside(PyConditionalStatementPart.class)
       .andOr(
-        psiElement().afterLeaf(psiElement().withText("if")),
-        psiElement().afterLeaf(psiElement().withText("elif")),
-        psiElement().afterLeaf(psiElement().withText("while"))
+        psiElement().afterLeaf(psiElement().withText(PyNames.IF)),
+        psiElement().afterLeaf(psiElement().withText(PyNames.ELIF)),
+        psiElement().afterLeaf(psiElement().withText(PyNames.WHILE))
       );
 
   private static final PsiElementPattern.Capture<PsiElement> IN_IMPORT_STMT =
@@ -308,6 +308,9 @@
   private static final PsiElementPattern.Capture<PsiElement> IN_EXCEPT_BODY =
     psiElement().inside(psiElement(PyStatementList.class).inside(psiElement(PyExceptPart.class)));
 
+  private static final PsiElementPattern.Capture<PsiElement> IN_ELSE_BODY_OF_TRY =
+    psiElement().inside(psiElement(PyStatementList.class).inside(psiElement(PyElsePart.class).inside(PyTryExceptStatement.class)));
+
   private static final PsiElementPattern.Capture<PsiElement> AFTER_IF = afterStatement(psiElement(PyIfStatement.class).withLastChild(
     psiElement(PyIfPart.class)));
   private static final PsiElementPattern.Capture<PsiElement> AFTER_TRY = afterStatement(psiElement(PyTryExceptStatement.class));
@@ -378,8 +381,8 @@
         protected void addCompletions(
           @NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result
         ) {
-          putKeywords(result, TailType.NONE, "def", "class", "for", "if", "while", "with");
-          putKeywords(result, TailType.CASE_COLON, "try");
+          putKeywords(result, TailType.NONE, PyNames.DEF, PyNames.CLASS, PyNames.FOR, PyNames.IF, PyNames.WHILE, PyNames.WITH);
+          putKeywords(result, TailType.CASE_COLON, PyNames.TRY);
         }
       }
     );
@@ -402,13 +405,13 @@
         protected void addCompletions(
           @NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result
         ) {
-          putKeywords(result, TailType.SPACE, "assert", "del", "exec", "from", "import", "raise");
+          putKeywords(result, TailType.SPACE, PyNames.ASSERT, PyNames.DEL, PyNames.EXEC, PyNames.FROM, PyNames.IMPORT, PyNames.RAISE);
           putKeywords(result, TailType.NONE, PyNames.PASS);
         }
       }
     );
 
-    extend(CompletionType.BASIC, inStatement.andNot(PY3K), new PyKeywordCompletionProvider(TailType.SPACE, "print"));
+    extend(CompletionType.BASIC, inStatement.andNot(PY3K), new PyKeywordCompletionProvider(TailType.SPACE, PyNames.PRINT));
   }
 
   private void addBreak() {
@@ -421,7 +424,7 @@
       .andNot(IN_ARG_LIST)
       .and(IN_LOOP)
       ,
-      new PyKeywordCompletionProvider(TailType.NONE, "break")
+      new PyKeywordCompletionProvider(TailType.NONE, PyNames.BREAK)
     );
   }
 
@@ -436,7 +439,7 @@
       .andNot(IN_FINALLY_NO_LOOP)
       .and(IN_LOOP)
       ,
-      new PyKeywordCompletionProvider(TailType.NONE, "continue")
+      new PyKeywordCompletionProvider(TailType.NONE, PyNames.CONTINUE)
     );
   }
 
@@ -448,7 +451,7 @@
       .and(IN_BEGIN_STMT)
       .andNot(AFTER_QUALIFIER)
       ,
-      new PyKeywordCompletionProvider("global", "return", "yield")
+      new PyKeywordCompletionProvider(PyNames.GLOBAL, PyNames.RETURN, PyNames.YIELD)
     );
 
     extend(
@@ -459,7 +462,7 @@
       .and(PY3K)
       .andNot(AFTER_QUALIFIER)
       ,
-      new PyKeywordCompletionProvider("nonlocal")
+      new PyKeywordCompletionProvider(PyNames.NONLOCAL)
     );
   }
 
@@ -471,7 +474,7 @@
       .andOr(IN_IF_BODY, AFTER_IF)
       .andNot(AFTER_QUALIFIER).andNot(IN_STRING_LITERAL)
       ,
-      new PyKeywordCompletionProvider(TailType.NONE, UnindentingInsertHandler.INSTANCE, "elif"));
+      new PyKeywordCompletionProvider(TailType.NONE, PyUnindentingInsertHandler.INSTANCE, PyNames.ELIF));
   }
 
   private void addWithinTry() {
@@ -479,7 +482,7 @@
       CompletionType.BASIC, psiElement()
       .withLanguage(PythonLanguage.getInstance())
       .and(FIRST_ON_LINE)
-      .andOr(IN_TRY_BODY, IN_EXCEPT_BODY, AFTER_TRY)
+      .andOr(IN_TRY_BODY, IN_EXCEPT_BODY, AFTER_TRY, IN_ELSE_BODY_OF_TRY)
         //.andNot(RIGHT_AFTER_COLON)
       .andNot(AFTER_QUALIFIER).andNot(IN_STRING_LITERAL)
       ,
@@ -487,8 +490,8 @@
         protected void addCompletions(
           @NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result
         ) {
-          putKeyword("except", UnindentingInsertHandler.INSTANCE, TailType.NONE, result);
-          putKeyword("finally", UnindentingInsertHandler.INSTANCE, TailType.CASE_COLON, result);
+          putKeyword(PyNames.EXCEPT, PyUnindentingInsertHandler.INSTANCE, TailType.NONE, result);
+          putKeyword(PyNames.FINALLY, PyUnindentingInsertHandler.INSTANCE, TailType.CASE_COLON, result);
         }
       }
     );
@@ -502,7 +505,7 @@
       .andOr(IN_COND_STMT, IN_EXCEPT_BODY, AFTER_COND_STMT_NO_ELSE, AFTER_LOOP_NO_ELSE, AFTER_EXCEPT)
       .andNot(AFTER_QUALIFIER).andNot(IN_STRING_LITERAL)
       ,
-      new PyKeywordCompletionProvider(TailType.CASE_COLON, UnindentingInsertHandler.INSTANCE, "else"));
+      new PyKeywordCompletionProvider(TailType.CASE_COLON, PyUnindentingInsertHandler.INSTANCE, PyNames.ELSE));
   }
 
   private void addInfixOperators() {
@@ -516,7 +519,7 @@
       .andNot(AFTER_QUALIFIER).
         andNot(IN_STRING_LITERAL).and(IN_BEGIN_STMT)
       ,
-      new PyKeywordCompletionProvider("and", "or", "is", "in")
+      new PyKeywordCompletionProvider(PyNames.AND, PyNames.OR, PyNames.IS, PyNames.IN)
     );
   }
 
@@ -530,7 +533,7 @@
       .andNot(IN_FUNCTION_HEADER)
       .andNot(AFTER_QUALIFIER).andNot(IN_STRING_LITERAL)
       ,
-      new PyKeywordCompletionProvider("not", "lambda")
+      new PyKeywordCompletionProvider(PyNames.NOT, PyNames.LAMBDA)
     );
   }
 
@@ -545,7 +548,7 @@
       .andNot(AFTER_QUALIFIER)
       .andNot(IN_FUNCTION_HEADER)
       ,
-      new PyKeywordCompletionProvider(TailType.NONE, "True", "False", "None")
+      new PyKeywordCompletionProvider(TailType.NONE, PyNames.TRUE, PyNames.FALSE, PyNames.NONE)
     );
   }
 
@@ -556,7 +559,7 @@
       .andOr(IN_IMPORT_AFTER_REF, IN_WITH_AFTER_REF, IN_EXCEPT_AFTER_REF)
       .andNot(AFTER_QUALIFIER)
       ,
-      new PyKeywordCompletionProvider("as")
+      new PyKeywordCompletionProvider(PyNames.AS)
     );
   }
 
@@ -567,7 +570,7 @@
       .and(IN_FROM_IMPORT_AFTER_REF)
       .andNot(AFTER_QUALIFIER)
       ,
-      new PyKeywordCompletionProvider("import")
+      new PyKeywordCompletionProvider(PyNames.IMPORT)
     );
   }
 
@@ -603,9 +606,9 @@
       .withLanguage(PythonLanguage.getInstance())
       .afterLeafSkipping(psiElement().whitespace(),
                          psiElement().inside(psiElement(PyConditionalExpression.class))
-                           .and(psiElement().afterLeaf("if")))
+                           .and(psiElement().afterLeaf(PyNames.IF)))
       ,
-      new PyKeywordCompletionProvider(TailType.SPACE, "else"));
+      new PyKeywordCompletionProvider(TailType.SPACE, PyNames.ELSE));
   }
 
   private void addRaiseFrom() {
@@ -614,7 +617,7 @@
              .withLanguage(PythonLanguage.getInstance())
              .and(PY3K)
              .afterLeaf(psiElement().inside(PyRaiseStatement.class)),
-           new PyKeywordCompletionProvider("from"));
+           new PyKeywordCompletionProvider(PyNames.FROM));
   }
 
   private void addYieldFrom() {
@@ -623,7 +626,7 @@
              .withLanguage(PythonLanguage.getInstance())
              .and(PY3K)
              .afterLeaf(psiElement().withElementType(PyTokenTypes.YIELD_KEYWORD)),
-           new PyKeywordCompletionProvider("from"));
+           new PyKeywordCompletionProvider(PyNames.FROM));
   }
 
   public PyKeywordCompletionContributor() {
@@ -654,14 +657,14 @@
              .withLanguage(PythonLanguage.getInstance())
              .inside(psiElement(PySequenceExpression.class))
              .andNot(psiElement().afterLeaf(or(psiElement(PyTokenTypes.LBRACE), psiElement(PyTokenTypes.LBRACKET), psiElement(PyTokenTypes.LPAR)))),
-           new PyKeywordCompletionProvider("for"));
+           new PyKeywordCompletionProvider(PyNames.FOR));
   }
 
   private void addInToFor() {
     extend(CompletionType.BASIC,
            psiElement()
              .withLanguage(PythonLanguage.getInstance())
-             .and(psiElement()).afterLeaf(psiElement().afterLeaf("for")),
+             .and(psiElement()).afterLeaf(psiElement().afterLeaf(PyNames.FOR)),
            new PyKeywordCompletionProvider("in"));
 
   }
diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/ControlFlowCache.java b/python/src/com/jetbrains/python/codeInsight/controlflow/ControlFlowCache.java
index 02b7040..a8db9b6 100644
--- a/python/src/com/jetbrains/python/codeInsight/controlflow/ControlFlowCache.java
+++ b/python/src/com/jetbrains/python/codeInsight/controlflow/ControlFlowCache.java
@@ -39,7 +39,7 @@
 
   public static ControlFlow getControlFlow(@NotNull ScopeOwner element) {
     SoftReference<ControlFlow> ref = element.getUserData(CONTROL_FLOW_KEY);
-    ControlFlow flow = ref != null ? ref.get() : null;
+    ControlFlow flow = SoftReference.dereference(ref);
     if (flow == null) {
       flow = new PyControlFlowBuilder().buildControlFlow(element);
       element.putUserData(CONTROL_FLOW_KEY, new SoftReference<ControlFlow>(flow));
@@ -50,7 +50,7 @@
   @NotNull
   public static Scope getScope(@NotNull ScopeOwner element) {
     SoftReference<Scope> ref = element.getUserData(SCOPE_KEY);
-    Scope scope = ref != null ? ref.get() : null;
+    Scope scope = SoftReference.dereference(ref);
     if (scope == null) {
       scope = new ScopeImpl(element);
       element.putUserData(SCOPE_KEY, new SoftReference<Scope>(scope));
diff --git a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/Scope.java b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/Scope.java
index 5cf0286..eedf3a6 100644
--- a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/Scope.java
+++ b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/Scope.java
@@ -19,6 +19,7 @@
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiNamedElement;
 import com.jetbrains.python.psi.PyImportedNameDefiner;
+import com.jetbrains.python.psi.PyTargetExpression;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -49,4 +50,7 @@
 
   @NotNull
   Collection<PsiNamedElement> getNamedElements();
+
+  @NotNull
+  Collection<PyTargetExpression> getTargetExpressions();
 }
diff --git a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java
index d0eb376..19a947fe 100644
--- a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java
+++ b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java
@@ -47,6 +47,7 @@
   private volatile Map<String, PsiNamedElement> myNamedElements;
   private volatile List<PyImportedNameDefiner> myImportedNameDefiners;  // Declarations which declare unknown set of imported names
   private volatile Set<String> myAugAssignments;
+  private List<PyTargetExpression> myTargetExpressions;
 
   public ScopeImpl(final ScopeOwner flowOwner) {
     myFlowOwner = flowOwner;
@@ -170,6 +171,15 @@
     return myNamedElements.values();
   }
 
+  @NotNull
+  @Override
+  public Collection<PyTargetExpression> getTargetExpressions() {
+    if (myTargetExpressions == null) {
+      collectDeclarations();
+    }
+    return myTargetExpressions;
+  }
+
   private void collectDeclarations() {
     final Map<String, PsiNamedElement> namedElements = new HashMap<String, PsiNamedElement>();
     final List<PyImportedNameDefiner> importedNameDefiners = new ArrayList<PyImportedNameDefiner>();
@@ -177,11 +187,13 @@
     final Set<String> globals = new HashSet<String>();
     final Set<String> nonlocals = new HashSet<String>();
     final Set<String> augAssignments = new HashSet<String>();
+    final List<PyTargetExpression> targetExpressions = new ArrayList<PyTargetExpression>();
     myFlowOwner.acceptChildren(new PyRecursiveElementVisitor() {
       @Override
       public void visitPyTargetExpression(PyTargetExpression node) {
+        targetExpressions.add(node);
         final PsiElement parent = node.getParent();
-        if (node.getQualifier() == null && !(parent instanceof PyImportElement)) {
+        if (!node.isQualified() && !(parent instanceof PyImportElement)) {
           super.visitPyTargetExpression(node);
         }
       }
@@ -249,5 +261,6 @@
     myGlobals = globals;
     myNonlocals = nonlocals;
     myAugAssignments = augAssignments;
+    myTargetExpressions = targetExpressions;
   }
 }
diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/moveUpDown/PyStatementMover.java b/python/src/com/jetbrains/python/codeInsight/editorActions/moveUpDown/PyStatementMover.java
index f527506..17b5f5b 100644
--- a/python/src/com/jetbrains/python/codeInsight/editorActions/moveUpDown/PyStatementMover.java
+++ b/python/src/com/jetbrains/python/codeInsight/editorActions/moveUpDown/PyStatementMover.java
@@ -23,12 +23,14 @@
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.SelectionModel;
 import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.TextRange;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.psi.*;
 import com.intellij.psi.codeStyle.CodeStyleManager;
 import com.intellij.psi.impl.source.PostprocessReformattingAspect;
 import com.intellij.psi.util.PsiTreeUtil;
+import com.jetbrains.python.PythonStringUtil;
 import com.jetbrains.python.psi.*;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -54,6 +56,9 @@
     PsiElement elementToMove1 = PyUtil.findNonWhitespaceAtOffset(file, start);
     PsiElement elementToMove2 = PyUtil.findNonWhitespaceAtOffset(file, end);
     if (elementToMove1 == null || elementToMove2 == null) return false;
+
+    if (ifInsideString(document, lineNumber, elementToMove1, down)) return false;
+
     elementToMove1 = getCommentOrStatement(document, elementToMove1);
     elementToMove2 = getCommentOrStatement(document, elementToMove2);
 
@@ -72,6 +77,24 @@
     return true;
   }
 
+  private static boolean ifInsideString(@NotNull final Document document, int lineNumber, @NotNull final PsiElement elementToMove1, boolean down) {
+    int start = document.getLineStartOffset(lineNumber);
+    final int end = document.getLineEndOffset(lineNumber);
+    int nearLine = down ? lineNumber + 1 : lineNumber - 1;
+    if (nearLine >= document.getLineCount() || nearLine <= 0) return false;
+    final PyStringLiteralExpression stringLiteralExpression = PsiTreeUtil.getParentOfType(elementToMove1, PyStringLiteralExpression.class);
+    if (stringLiteralExpression != null) {
+      final Pair<String,String> quotes = PythonStringUtil.getQuotes(stringLiteralExpression.getText());
+      if (quotes != null && (quotes.first.equals("'''") || quotes.first.equals("\"\"\""))) {
+        final String text1 = document.getText(TextRange.create(start, end)).trim();
+        final String text2 = document.getText(TextRange.create(document.getLineStartOffset(nearLine), document.getLineEndOffset(nearLine))).trim();
+        if (!text1.startsWith(quotes.first) && !text1.endsWith(quotes.second) && !text2.startsWith(quotes.first) && !text2.endsWith(quotes.second))
+          return true;
+      }
+    }
+    return false;
+  }
+
   @Nullable
   private static LineRange getDestinationScope(@NotNull final PsiFile file, @NotNull final Editor editor,
                                                @NotNull final PsiElement elementToMove, boolean down) {
@@ -80,7 +103,7 @@
 
     final int offset = down ? elementToMove.getTextRange().getEndOffset() : elementToMove.getTextRange().getStartOffset();
     int lineNumber = down ? document.getLineNumber(offset) + 1 : document.getLineNumber(offset) - 1;
-    if (moveOutsideFile(elementToMove, document, lineNumber)) return null;
+    if (moveOutsideFile(document, lineNumber)) return null;
     int lineEndOffset = document.getLineEndOffset(lineNumber);
     final int startOffset = document.getLineStartOffset(lineNumber);
     lineEndOffset = startOffset != lineEndOffset ? lineEndOffset - 1 : lineEndOffset;
@@ -118,24 +141,8 @@
     return new LineRange(startLine, endLine + 1);
   }
 
-  private static boolean moveOutsideFile(@NotNull final PsiElement elementToMove, @NotNull final Document document, int lineNumber) {
-    if (lineNumber < 0) return true;
-    if (lineNumber >= document.getLineCount()) {
-      final int elementOffset = elementToMove.getTextRange().getStartOffset();
-      final int lineStartOffset = document.getLineStartOffset(document.getLineNumber(elementOffset));
-      final int insertIndex = lineNumber < 0 ? 0 : document.getTextLength();
-      if (elementOffset != lineStartOffset) {
-        ApplicationManager.getApplication().runWriteAction(new Runnable() {
-          @Override
-          public void run() {
-            document.insertString(insertIndex, "\n");
-            PsiDocumentManager.getInstance(elementToMove.getProject()).commitAllDocuments();
-          }
-        });
-      }
-      else return true;
-    }
-    return false;
+  private static boolean moveOutsideFile(@NotNull final Document document, int lineNumber) {
+    return lineNumber < 0 || lineNumber >= document.getLineCount();
   }
 
   private static boolean moveToEmptyLine(@NotNull final PsiElement elementToMove, boolean down) {
diff --git a/python/src/com/jetbrains/python/codeInsight/imports/AddImportHelper.java b/python/src/com/jetbrains/python/codeInsight/imports/AddImportHelper.java
index aa6563f..1c09ee2 100644
--- a/python/src/com/jetbrains/python/codeInsight/imports/AddImportHelper.java
+++ b/python/src/com/jetbrains/python/codeInsight/imports/AddImportHelper.java
@@ -21,6 +21,7 @@
 import com.intellij.openapi.module.ModuleUtilCore;
 import com.intellij.openapi.projectRoots.Sdk;
 import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.*;
 import com.intellij.util.IncorrectOperationException;
@@ -179,7 +180,6 @@
     }
     return true;
   }
-
   /**
    * Adds an "import ... from ..." statement below other top-level imports.
    *
diff --git a/python/src/com/jetbrains/python/codeInsight/imports/AutoImportQuickFix.java b/python/src/com/jetbrains/python/codeInsight/imports/AutoImportQuickFix.java
index 4b46413..47c9ca9 100644
--- a/python/src/com/jetbrains/python/codeInsight/imports/AutoImportQuickFix.java
+++ b/python/src/com/jetbrains/python/codeInsight/imports/AutoImportQuickFix.java
@@ -129,7 +129,7 @@
     if (HintManager.getInstance().hasShownHintsThatWillHideByOtherHint(true)) {
       return false;
     }
-    if ((myNode instanceof PyQualifiedExpression) && ((((PyQualifiedExpression)myNode).getQualifier() != null))) return false; // we cannot be qualified
+    if ((myNode instanceof PyQualifiedExpression) && ((((PyQualifiedExpression)myNode).isQualified()))) return false; // we cannot be qualified
     String name = getNameToImport();
     if (!name.equals(myInitialName)) {
       return false;
diff --git a/python/src/com/jetbrains/python/codeInsight/imports/ImportFromExistingAction.java b/python/src/com/jetbrains/python/codeInsight/imports/ImportFromExistingAction.java
index f90ce61..58254d9 100644
--- a/python/src/com/jetbrains/python/codeInsight/imports/ImportFromExistingAction.java
+++ b/python/src/com/jetbrains/python/codeInsight/imports/ImportFromExistingAction.java
@@ -82,7 +82,7 @@
     // check if the tree is sane
     PsiDocumentManager.getInstance(myTarget.getProject()).commitAllDocuments();
     if (!myTarget.isValid()) return false;
-    if ((myTarget instanceof PyQualifiedExpression) && ((((PyQualifiedExpression)myTarget).getQualifier() != null))) return false; // we cannot be qualified
+    if ((myTarget instanceof PyQualifiedExpression) && ((((PyQualifiedExpression)myTarget).isQualified()))) return false; // we cannot be qualified
     for (ImportCandidateHolder item : mySources) {
       if (!item.getImportable().isValid()) return false;
       if (!item.getFile().isValid()) return false;
diff --git a/python/src/com/jetbrains/python/codeInsight/imports/PythonReferenceImporter.java b/python/src/com/jetbrains/python/codeInsight/imports/PythonReferenceImporter.java
index 06e4f89..ae96ac5 100644
--- a/python/src/com/jetbrains/python/codeInsight/imports/PythonReferenceImporter.java
+++ b/python/src/com/jetbrains/python/codeInsight/imports/PythonReferenceImporter.java
@@ -69,7 +69,7 @@
     for (PsiElement element : elements) {
       if (element instanceof PyReferenceExpression && isImportable(element)) {
         final PyReferenceExpression refExpr = (PyReferenceExpression)element;
-        if (refExpr.getQualifier() == null) {
+        if (!refExpr.isQualified()) {
           final PsiPolyVariantReference reference = refExpr.getReference();
           if (reference.resolve() == null) {
             AutoImportQuickFix fix = proposeImportFix(refExpr, reference);
@@ -92,7 +92,7 @@
     PsiReference element = file.findReferenceAt(offset);
     if (element instanceof PyReferenceExpression && isImportable((PsiElement)element)) {
       final PyReferenceExpression refExpr = (PyReferenceExpression)element;
-      if (refExpr.getQualifier() == null) {
+      if (!refExpr.isQualified()) {
         final PsiPolyVariantReference reference = refExpr.getReference();
         if (reference.resolve() == null) {
           AutoImportQuickFix fix = proposeImportFix(refExpr, reference);
diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ImportFromToImportIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ImportFromToImportIntention.java
index 9f1c366..0170b8b 100644
--- a/python/src/com/jetbrains/python/codeInsight/intentions/ImportFromToImportIntention.java
+++ b/python/src/com/jetbrains/python/codeInsight/intentions/ImportFromToImportIntention.java
@@ -28,7 +28,7 @@
 import com.intellij.util.containers.HashMap;
 import com.jetbrains.python.PyBundle;
 import com.jetbrains.python.psi.*;
-import com.jetbrains.python.psi.resolve.PyResolveUtil;
+import com.jetbrains.python.psi.impl.PyPsiUtils;
 import com.jetbrains.python.psi.types.PyModuleType;
 import com.jetbrains.python.psi.types.TypeEvalContext;
 import org.jetbrains.annotations.NotNull;
@@ -84,7 +84,7 @@
         ret.myModuleReference = ret.myFromImportStatement.getImportSource();
       }
       if (ret.myModuleReference != null) {
-        ret.myModuleName = PyResolveUtil.toPath(ret.myModuleReference);
+        ret.myModuleName = PyPsiUtils.toPath(ret.myModuleReference);
       }
       return ret;
     }
@@ -117,7 +117,7 @@
     if (info.myModuleReference != null) {
       PyExpression remaining_module = info.myModuleReference.getQualifier();
       if (remaining_module instanceof PyQualifiedExpression) {
-        remaining_name = PyResolveUtil.toPath((PyQualifiedExpression)remaining_module);
+        remaining_name = PyPsiUtils.toPath((PyQualifiedExpression)remaining_module);
       }
       else remaining_name = ""; // unqualified name: "...module"
       separated_name = info.myModuleReference.getReferencedName();
@@ -169,7 +169,7 @@
       }
     }
     if (info.myModuleReference != null) {
-      info.myModuleName = PyResolveUtil.toPath(info.myModuleReference);
+      info.myModuleName = PyPsiUtils.toPath(info.myModuleReference);
     }
     if (info.myModuleReference != null && info.myModuleName != null && info.myFromImportStatement != null) {
       myText = info.getText();
@@ -211,7 +211,7 @@
         public boolean execute(@NotNull PsiElement element) {
           if (element instanceof PyReferenceExpression && PsiTreeUtil.getParentOfType(element, PyImportElement.class) == null && element.isValid()) {
             PyReferenceExpression ref = (PyReferenceExpression)element;
-            if (ref.getQualifier() == null) {
+            if (!ref.isQualified()) {
               ResolveResult[] resolved = ref.getReference().multiResolve(false);
               for (ResolveResult rr : resolved) {
                 if (rr.isValidResult()) {
diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ImportToImportFromIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ImportToImportFromIntention.java
index 453c2895..fc35833 100644
--- a/python/src/com/jetbrains/python/codeInsight/intentions/ImportToImportFromIntention.java
+++ b/python/src/com/jetbrains/python/codeInsight/intentions/ImportToImportFromIntention.java
@@ -30,7 +30,7 @@
 import com.intellij.util.containers.HashSet;
 import com.jetbrains.python.PyBundle;
 import com.jetbrains.python.psi.*;
-import com.jetbrains.python.psi.resolve.PyResolveUtil;
+import com.jetbrains.python.psi.impl.PyPsiUtils;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -94,7 +94,7 @@
       // usages of imported name are qualifiers; what they refer to?
       PyReferenceExpression reference = myImportElement.getImportReferenceExpression();
       if (reference != null) {
-        myModuleName = PyResolveUtil.toPath(reference);
+        myModuleName = PyPsiUtils.toPath(reference);
         myQualifierName = myImportElement.getVisibleName();
         myReferee = reference.getReference().resolve();
         myHasModuleReference = false;
@@ -104,7 +104,7 @@
             public boolean execute(@NotNull PsiElement element) {
               if (element instanceof PyReferenceExpression && PsiTreeUtil.getParentOfType(element, PyImportElement.class) == null) {
                 PyReferenceExpression ref = (PyReferenceExpression)element;
-                if (myQualifierName.equals(PyResolveUtil.toPath(ref))) {  // filter out other names that might resolve to our target
+                if (myQualifierName.equals(PyPsiUtils.toPath(ref))) {  // filter out other names that might resolve to our target
                   PsiElement parent_elt = ref.getParent();
                   if (parent_elt instanceof PyQualifiedExpression) { // really qualified by us, not just referencing?
                     PsiElement resolved = ref.getReference().resolve();
@@ -197,7 +197,7 @@
       String module_name = "?";
       if (myImportElement != null) {
         PyReferenceExpression reference = myImportElement.getImportReferenceExpression();
-        if (reference != null) module_name = PyResolveUtil.toPath(reference);
+        if (reference != null) module_name = PyPsiUtils.toPath(reference);
       }
       return PyBundle.message("INTN.convert.to.from.$0.import.$1", getDots()+module_name, "...");
     }
diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ImportToggleAliasIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ImportToggleAliasIntention.java
index f9f1ee1..621adf8 100644
--- a/python/src/com/jetbrains/python/codeInsight/intentions/ImportToggleAliasIntention.java
+++ b/python/src/com/jetbrains/python/codeInsight/intentions/ImportToggleAliasIntention.java
@@ -38,7 +38,7 @@
 import com.jetbrains.python.PyNames;
 import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
 import com.jetbrains.python.psi.*;
-import com.jetbrains.python.psi.resolve.PyResolveUtil;
+import com.jetbrains.python.psi.impl.PyPsiUtils;
 import org.jetbrains.annotations.NotNull;
 
 import java.util.ArrayList;
@@ -136,7 +136,7 @@
     PyReferenceExpression reference = sure(state.myImportElement.getImportReferenceExpression());
     // search for references to us with the right name
     try {
-      String imported_name = PyResolveUtil.toPath(reference);
+      String imported_name = PyPsiUtils.toPath(reference);
       if (state.myAlias != null) {
         // have to remove alias, rename everything to original
         target_name = imported_name;
@@ -181,13 +181,16 @@
                 InjectedLanguageManager.getInstance(project).getInjectedPsiFiles(host);
               if (files != null) {
                 for (Pair<PsiElement, TextRange> pair : files) {
-                  final ScopeOwner scopeOwner = (ScopeOwner)pair.getFirst();
-                  PsiTreeUtil.processElements(scopeOwner, new PsiElementProcessor() {
-                    public boolean execute(@NotNull PsiElement element) {
-                      getReferences(element);
-                      return true;
-                    }
-                  });
+                  final PsiElement first = pair.getFirst();
+                  if (first instanceof ScopeOwner) {
+                    final ScopeOwner scopeOwner = (ScopeOwner)first;
+                    PsiTreeUtil.processElements(scopeOwner, new PsiElementProcessor() {
+                      public boolean execute(@NotNull PsiElement element) {
+                        getReferences(element);
+                        return true;
+                      }
+                    });
+                  }
                 }
               }
             }
@@ -198,7 +201,7 @@
             if (element instanceof PyReferenceExpression && PsiTreeUtil.getParentOfType(element,
                                                                                         PyImportElement.class) == null) {
               PyReferenceExpression ref = (PyReferenceExpression)element;
-               if (remove_name.equals(PyResolveUtil.toPath(ref))) {  // filter out other names that might resolve to our target
+               if (remove_name.equals(PyPsiUtils.toPath(ref))) {  // filter out other names that might resolve to our target
                 PsiElement resolved = ref.getReference().resolve();
                 if (resolved == referee) references.add(ref.getReference());
               }
diff --git a/python/src/com/jetbrains/python/codeInsight/liveTemplates/PyIterableVariableMacro.java b/python/src/com/jetbrains/python/codeInsight/liveTemplates/PyIterableVariableMacro.java
new file mode 100644
index 0000000..68eec70
--- /dev/null
+++ b/python/src/com/jetbrains/python/codeInsight/liveTemplates/PyIterableVariableMacro.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.codeInsight.liveTemplates;
+
+import com.intellij.codeInsight.lookup.LookupElement;
+import com.intellij.codeInsight.lookup.LookupElementBuilder;
+import com.intellij.codeInsight.template.*;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiNamedElement;
+import com.jetbrains.python.PyNames;
+import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
+import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
+import com.jetbrains.python.codeInsight.dataflow.scope.Scope;
+import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
+import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.psi.PyImportedNameDefiner;
+import com.jetbrains.python.psi.PyTypedElement;
+import com.jetbrains.python.psi.types.PyABCUtil;
+import com.jetbrains.python.psi.types.PyType;
+import com.jetbrains.python.psi.types.TypeEvalContext;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author vlan
+ */
+public class PyIterableVariableMacro extends Macro {
+  @Override
+  public String getName() {
+    return "pyIterableVariable";
+  }
+
+  @Override
+  public String getPresentableName() {
+    return "pyIterableVariable()";
+  }
+
+  @Nullable
+  @Override
+  public Result calculateResult(@NotNull Expression[] params, @NotNull ExpressionContext context) {
+    final PsiElement element = context.getPsiElementAtStartOffset();
+    if (element != null) {
+      final List<PsiNamedElement> components = getIterableElements(element);
+      if (!components.isEmpty()) {
+        return new PsiElementResult(components.get(0));
+      }
+    }
+    return null;
+  }
+
+  @Nullable
+  @Override
+  public LookupElement[] calculateLookupItems(@NotNull Expression[] params, ExpressionContext context) {
+    final List<LookupElement> results = new ArrayList<LookupElement>();
+    final PsiElement element = context.getPsiElementAtStartOffset();
+    if (element != null) {
+      for (PsiNamedElement iterableElement : getIterableElements(element)) {
+        results.add(LookupElementBuilder.create(iterableElement));
+      }
+    }
+    return results.toArray(new LookupElement[results.size()]);
+  }
+
+  @Override
+  public boolean isAcceptableInContext(TemplateContextType context) {
+    return context instanceof PythonTemplateContextType;
+  }
+
+  @NotNull
+  protected List<PsiNamedElement> getIterableElements(@NotNull PsiElement element) {
+    final TypeEvalContext typeEvalContext = TypeEvalContext.userInitiated(element.getContainingFile());
+    final List<PsiNamedElement> components = new ArrayList<PsiNamedElement>();
+    for (PsiNamedElement namedElement : getVisibleNamedElements(element)) {
+      if (namedElement instanceof PyTypedElement) {
+        final PyType type = typeEvalContext.getType((PyTypedElement)namedElement);
+        if (type != null && PyABCUtil.isSubtype(type, PyNames.ITERABLE, typeEvalContext)) {
+          components.add(namedElement);
+        }
+      }
+    }
+    return components;
+  }
+
+  @NotNull
+  private static List<PsiNamedElement> getVisibleNamedElements(@NotNull PsiElement anchor) {
+    final List<PsiNamedElement> results = new ArrayList<PsiNamedElement>();
+    for (ScopeOwner owner = ScopeUtil.getScopeOwner(anchor); owner != null; owner = ScopeUtil.getScopeOwner(owner)) {
+      final Scope scope = ControlFlowCache.getScope(owner);
+      results.addAll(scope.getNamedElements());
+      for (PyImportedNameDefiner importedNameDefiner : scope.getImportedNameDefiners()) {
+        for (PyElement importedElement : importedNameDefiner.iterateNames()) {
+          if (importedElement instanceof PsiNamedElement) {
+            results.add((PsiNamedElement)importedElement);
+          }
+        }
+      }
+    }
+    return results;
+  }
+}
diff --git a/python/src/com/jetbrains/python/codeInsight/liveTemplates/PythonTemplateContextType.java b/python/src/com/jetbrains/python/codeInsight/liveTemplates/PythonTemplateContextType.java
index d381562..4ae7abe 100644
--- a/python/src/com/jetbrains/python/codeInsight/liveTemplates/PythonTemplateContextType.java
+++ b/python/src/com/jetbrains/python/codeInsight/liveTemplates/PythonTemplateContextType.java
@@ -16,7 +16,15 @@
 package com.jetbrains.python.codeInsight.liveTemplates;
 
 import com.intellij.codeInsight.template.FileTypeBasedContextType;
+import com.intellij.patterns.PsiElementPattern;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.util.ProcessingContext;
+import com.jetbrains.python.PyTokenTypes;
 import com.jetbrains.python.PythonFileType;
+import org.jetbrains.annotations.NotNull;
+
+import static com.intellij.patterns.PlatformPatterns.psiElement;
 
 /**
  * @author yole
@@ -25,4 +33,22 @@
   public PythonTemplateContextType() {
     super("Python", "Python", PythonFileType.INSTANCE);
   }
+
+  @Override
+  public boolean isInContext(@NotNull PsiFile file, int offset) {
+    if (super.isInContext(file, offset)) {
+      final PsiElement element = file.findElementAt(offset);
+      if (element != null) {
+        return !isAfterDot(element);
+      }
+    }
+    return false;
+  }
+
+  private static boolean isAfterDot(@NotNull PsiElement element) {
+    ProcessingContext context = new ProcessingContext();
+    final PsiElementPattern.Capture<PsiElement> capture = psiElement().afterLeafSkipping(psiElement().whitespace(),
+                                                                                         psiElement().withElementType(PyTokenTypes.DOT));
+    return capture.accepts(element, context);
+  }
 }
diff --git a/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java b/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java
index e49b0bd..8783a7a 100644
--- a/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java
+++ b/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java
@@ -156,7 +156,8 @@
     final int offset = editor.getCaretModel().getOffset();
     PsiElement anchor = null;
     for (PyStatement statement: statementList.getStatements()) {
-      if (statement.getTextRange().getStartOffset() < offset) {
+      if (statement.getTextRange().getStartOffset() < offset ||
+          (statement instanceof PyExpressionStatement && ((PyExpressionStatement)statement).getExpression() instanceof PyStringLiteralExpression)) {
         anchor = statement;
       }
     }
diff --git a/python/src/com/jetbrains/python/codeInsight/regexp/PythonRegexpInjector.java b/python/src/com/jetbrains/python/codeInsight/regexp/PythonRegexpInjector.java
index 39c9aff..029eabc 100644
--- a/python/src/com/jetbrains/python/codeInsight/regexp/PythonRegexpInjector.java
+++ b/python/src/com/jetbrains/python/codeInsight/regexp/PythonRegexpInjector.java
@@ -18,12 +18,17 @@
 import com.intellij.lang.Language;
 import com.intellij.lang.injection.MultiHostInjector;
 import com.intellij.lang.injection.MultiHostRegistrar;
+import com.intellij.openapi.util.Pair;
 import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
 import com.intellij.psi.PsiPolyVariantReference;
+import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
+import com.intellij.psi.impl.source.tree.injected.MultiHostRegistrarImpl;
+import com.intellij.psi.impl.source.tree.injected.Place;
 import com.intellij.psi.util.PsiTreeUtil;
 import com.intellij.util.ArrayUtil;
 import com.jetbrains.python.codeInsight.PyInjectionUtil;
-import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
+import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
 import com.jetbrains.python.psi.*;
 import com.jetbrains.python.psi.resolve.PyResolveContext;
 import org.jetbrains.annotations.NotNull;
@@ -67,7 +72,7 @@
   @Override
   public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) {
     final PsiElement contextParent = context.getParent();
-    if (PyInjectionUtil.isLargestStringLiteral(context) && contextParent instanceof PyArgumentList) {
+    if (PyInjectionUtil.getLargestStringLiteral(context) == context && contextParent instanceof PyArgumentList) {
       final PyExpression[] args = ((PyArgumentList)contextParent).getArguments();
       int index = ArrayUtil.indexOf(args, context);
       PyCallExpression call = PsiTreeUtil.getParentOfType(context, PyCallExpression.class);
@@ -77,12 +82,20 @@
           final PsiPolyVariantReference ref = ((PyReferenceExpression)callee).getReference(PyResolveContext.noImplicits());
           if (ref != null) {
             final PsiElement element = ref.resolve();
-            if (element != null && PsiTreeUtil.getParentOfType(element, ScopeOwner.class) instanceof PyFile &&
+            if (element != null && ScopeUtil.getScopeOwner(element) instanceof PyFile &&
                 element.getContainingFile().getName().equals("re.py") && isRegexpMethod(element, index)) {
               final Language language = isVerbose(call) ? PythonVerboseRegexpLanguage.INSTANCE : PythonRegexpLanguage.INSTANCE;
               registrar.startInjecting(language);
-              PyInjectionUtil.registerStringLiteralInjection(context, registrar);
-              registrar.doneInjecting();
+              final PyInjectionUtil.InjectionResult result = PyInjectionUtil.registerStringLiteralInjection(context, registrar);
+              if (result.isInjected()) {
+                registrar.doneInjecting();
+                if (!result.isStrict()) {
+                  final PsiFile file = getInjectedFile(registrar);
+                  if (file != null) {
+                    file.putUserData(InjectedLanguageUtil.FRANKENSTEIN_INJECTION, Boolean.TRUE);
+                  }
+                }
+              }
             }
           }
         }
@@ -90,6 +103,13 @@
     }
   }
 
+  @Nullable
+  private static PsiFile getInjectedFile(@NotNull MultiHostRegistrar registrar) {
+    // Don't add a dependency on IntelliLang here now, but this injector should become IntelliLang-based in the future
+    final List<Pair<Place,PsiFile>> result = ((MultiHostRegistrarImpl)registrar).getResult();
+    return result == null || result.isEmpty() ? null : result.get(result.size() - 1).second;
+  }
+
   @NotNull
   @Override
   public List<? extends Class<? extends PsiElement>> elementsToInjectIn() {
diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java
index cf6b9ef..9cdf4c3 100644
--- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java
+++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java
@@ -21,6 +21,8 @@
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.util.QualifiedName;
 import com.jetbrains.python.PyNames;
+import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
+import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
 import com.jetbrains.python.psi.*;
 import com.jetbrains.python.psi.impl.PyBuiltinCache;
 import com.jetbrains.python.psi.impl.PyTypeProvider;
@@ -30,6 +32,7 @@
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -37,7 +40,8 @@
  * @author yole
  */
 public class PyStdlibTypeProvider extends PyTypeProviderBase {
-  private static final Set<String> OPEN_FUNCTIONS = ImmutableSet.of("__builtin__.open", "io.open", "os.fdopen");
+  private static final Set<String> OPEN_FUNCTIONS = ImmutableSet.of("__builtin__.open", "io.open", "os.fdopen",
+                                                                    "pathlib.Path.open");
   private static final String BINARY_FILE_TYPE = "io.FileIO[bytes]";
   private static final String TEXT_FILE_TYPE = "io.TextIOWrapper[unicode]";
 
@@ -53,6 +57,103 @@
 
   @Override
   public PyType getReferenceType(@NotNull PsiElement referenceTarget, @NotNull TypeEvalContext context, @Nullable PsiElement anchor) {
+    PyType type = getNamedTupleType(referenceTarget, anchor);
+    if (type != null) {
+      return type;
+    }
+    type = getEnumType(referenceTarget, context, anchor);
+    if (type != null) {
+      return type;
+    }
+    return null;
+  }
+
+  @Nullable
+  private static PyType getEnumType(@NotNull PsiElement referenceTarget, @NotNull TypeEvalContext context,
+                                    @Nullable PsiElement anchor) {
+    if (referenceTarget instanceof PyTargetExpression) {
+      final PyTargetExpression target = (PyTargetExpression)referenceTarget;
+      final ScopeOwner owner = ScopeUtil.getScopeOwner(target);
+      if (owner instanceof PyClass) {
+        final PyClass cls = (PyClass)owner;
+        final List<PyClassLikeType> types = cls.getAncestorTypes(context);
+        for (PyClassLikeType type : types) {
+          if (type != null && "enum.Enum".equals(type.getClassQName())) {
+            final PyType classType = context.getType(cls);
+            if (classType instanceof PyClassType) {
+              return ((PyClassType)classType).toInstance();
+            }
+          }
+        }
+      }
+    }
+    if (referenceTarget instanceof PyQualifiedNameOwner) {
+      final PyQualifiedNameOwner qualifiedNameOwner = (PyQualifiedNameOwner)referenceTarget;
+      final String name = qualifiedNameOwner.getQualifiedName();
+      if ("enum.Enum.name".equals(name)) {
+        return PyBuiltinCache.getInstance(referenceTarget).getStrType();
+      }
+      else if ("enum.Enum.value".equals(name) && anchor instanceof PyReferenceExpression && context.maySwitchToAST(anchor)) {
+        final PyReferenceExpression anchorExpr = (PyReferenceExpression)anchor;
+        final PyExpression qualifier = anchorExpr.getQualifier();
+        if (qualifier instanceof PyReferenceExpression) {
+          final PyReferenceExpression qualifierExpr = (PyReferenceExpression)qualifier;
+          final PsiElement resolvedQualifier = qualifierExpr.getReference().resolve();
+          if (resolvedQualifier instanceof PyTargetExpression) {
+            final PyTargetExpression qualifierTarget = (PyTargetExpression)resolvedQualifier;
+            // Requires switching to AST, we cannot use getType(qualifierTarget) here, because its type is overridden by this type provider
+            if (context.maySwitchToAST(qualifierTarget)) {
+              final PyExpression value = qualifierTarget.findAssignedValue();
+              if (value != null) {
+                return context.getType(value);
+              }
+            }
+          }
+        }
+      }
+      else if ("enum.EnumMeta.__members__".equals(name)) {
+        return PyTypeParser.getTypeByName(referenceTarget, "dict[str, unknown]");
+      }
+    }
+    return null;
+  }
+
+  @Nullable
+  @Override
+  public PyType getReturnType(@NotNull PyFunction function, @Nullable PyQualifiedExpression callSite, @NotNull TypeEvalContext context) {
+    final String qname = getQualifiedName(function, callSite);
+    if (qname != null) {
+      if (OPEN_FUNCTIONS.contains(qname) && callSite != null) {
+        final PyTypeChecker.AnalyzeCallResults results = PyTypeChecker.analyzeCallSite(callSite, context);
+        if (results != null) {
+          final PyType type = getOpenFunctionType(qname, results.getArguments(), callSite);
+          if (type != null) {
+            return type;
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  @Nullable
+  @Override
+  public PyType getContextManagerVariableType(@NotNull PyClass contextManager, @NotNull PyExpression withExpression, @NotNull TypeEvalContext context) {
+    if ("contextlib.closing".equals(contextManager.getQualifiedName()) && withExpression instanceof PyCallExpression) {
+      PyExpression closee = ((PyCallExpression)withExpression).getArgument(0, PyExpression.class);
+      if (closee != null) {
+        return context.getType(closee);
+      }
+    }
+    final String name = contextManager.getName();
+    if ("FileIO".equals(name) || "TextIOWrapper".equals(name) || "IOBase".equals(name) || "_IOBase".equals(name)) {
+      return context.getType(withExpression);
+    }
+    return null;
+  }
+
+  @Nullable
+  private static PyType getNamedTupleType(@NotNull PsiElement referenceTarget, @Nullable PsiElement anchor) {
     if (referenceTarget instanceof PyTargetExpression) {
       final PyTargetExpression target = (PyTargetExpression)referenceTarget;
       final QualifiedName calleeName = target.getCalleeName();
@@ -81,52 +182,6 @@
   }
 
   @Nullable
-  @Override
-  public PyType getReturnType(@NotNull PyFunction function, @Nullable PyQualifiedExpression callSite, @NotNull TypeEvalContext context) {
-    final String qname = getQualifiedName(function, callSite);
-    if (qname != null) {
-      if (OPEN_FUNCTIONS.contains(qname) && callSite != null) {
-        final PyTypeChecker.AnalyzeCallResults results = PyTypeChecker.analyzeCallSite(callSite, context);
-        if (results != null) {
-          final PyType type = getOpenFunctionType(qname, results.getArguments(), callSite);
-          if (type != null) {
-            return type;
-          }
-        }
-      }
-    }
-    return null;
-  }
-
-  @Nullable
-  @Override
-  public PyType getIterationType(@NotNull PyClass iterable) {
-    final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(iterable);
-    if (builtinCache.hasInBuiltins(iterable)) {
-      if ("file".equals(iterable.getName())) {
-        return builtinCache.getStrType();
-      }
-    }
-    return null;
-  }
-
-  @Nullable
-  @Override
-  public PyType getContextManagerVariableType(@NotNull PyClass contextManager, @NotNull PyExpression withExpression, @NotNull TypeEvalContext context) {
-    if ("contextlib.closing".equals(contextManager.getQualifiedName()) && withExpression instanceof PyCallExpression) {
-      PyExpression closee = ((PyCallExpression)withExpression).getArgument(0, PyExpression.class);
-      if (closee != null) {
-        return context.getType(closee);
-      }
-    }
-    final String name = contextManager.getName();
-    if ("FileIO".equals(name) || "TextIOWrapper".equals(name) || "IOBase".equals(name) || "_IOBase".equals(name)) {
-      return context.getType(withExpression);
-    }
-    return null;
-  }
-
-  @Nullable
   private static PyType getOpenFunctionType(@NotNull String callQName,
                                             @NotNull Map<PyExpression, PyNamedParameter> arguments,
                                             @NotNull PsiElement anchor) {
@@ -134,7 +189,10 @@
     for (Map.Entry<PyExpression, PyNamedParameter> entry : arguments.entrySet()) {
       final PyNamedParameter parameter = entry.getValue();
       if ("mode".equals(parameter.getName())) {
-        final PyExpression argument = entry.getKey();
+        PyExpression argument = entry.getKey();
+        if (argument instanceof PyKeywordArgument) {
+          argument = ((PyKeywordArgument)argument).getValueExpression();
+        }
         if (argument instanceof PyStringLiteralExpression) {
           mode = ((PyStringLiteralExpression)argument).getStringValue();
           break;
diff --git a/python/src/com/jetbrains/python/codeInsight/testIntegration/CreateTestDialog.java b/python/src/com/jetbrains/python/codeInsight/testIntegration/CreateTestDialog.java
index 08f3d190ab..c01a1b2 100644
--- a/python/src/com/jetbrains/python/codeInsight/testIntegration/CreateTestDialog.java
+++ b/python/src/com/jetbrains/python/codeInsight/testIntegration/CreateTestDialog.java
@@ -22,6 +22,7 @@
 import com.intellij.openapi.ui.TextFieldWithBrowseButton;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.ui.BooleanTableCellRenderer;
+import com.intellij.ui.TableUtil;
 import org.jetbrains.annotations.NotNull;
 
 import javax.swing.*;
@@ -29,7 +30,6 @@
 import javax.swing.event.DocumentListener;
 import javax.swing.table.DefaultTableModel;
 import javax.swing.table.TableColumn;
-import javax.swing.table.TableColumnModel;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.util.ArrayList;
@@ -72,14 +72,12 @@
     myTableModel = new DefaultTableModel(methods, 2);
     myMethodsTable.setModel(myTableModel);
 
-    TableColumnModel model = myMethodsTable.getColumnModel();
-    model.getColumn(0).setMaxWidth(new JCheckBox().getPreferredSize().width);
-
     TableColumn checkColumn = myMethodsTable.getColumnModel().getColumn(0);
+    TableUtil.setupCheckboxColumn(checkColumn);
     checkColumn.setCellRenderer(new BooleanTableCellRenderer());
     checkColumn.setCellEditor(new DefaultCellEditor(new JCheckBox()));
 
-    model.getColumn(1).setHeaderValue("Test method");
+    myMethodsTable.getColumnModel().getColumn(1).setHeaderValue("Test method");
     checkColumn.setHeaderValue("");
     getOKAction().setEnabled(true);
   }
diff --git a/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsClassMembersProvider.java b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsClassMembersProvider.java
index a35d8fd..5ac7fc1 100644
--- a/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsClassMembersProvider.java
+++ b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsClassMembersProvider.java
@@ -22,6 +22,7 @@
 import com.jetbrains.python.psi.PyTargetExpression;
 import com.jetbrains.python.psi.types.PyClassMembersProviderBase;
 import com.jetbrains.python.psi.types.PyClassType;
+import com.jetbrains.python.psi.types.PyOverridingAncestorsClassMembersProvider;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -33,7 +34,7 @@
 /**
  * @author vlan
  */
-public class PyUserSkeletonsClassMembersProvider extends PyClassMembersProviderBase {
+public class PyUserSkeletonsClassMembersProvider extends PyClassMembersProviderBase implements PyOverridingAncestorsClassMembersProvider {
   @NotNull
   @Override
   public Collection<PyDynamicMember> getMembers(@NotNull PyClassType classType, PsiElement location) {
diff --git a/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsUtil.java b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsUtil.java
index da0ad54..ea6db074 100644
--- a/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsUtil.java
+++ b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsUtil.java
@@ -17,11 +17,13 @@
 
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.extensions.Extensions;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.projectRoots.Sdk;
 import com.intellij.openapi.projectRoots.SdkModificator;
 import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.util.Key;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.LocalFileSystem;
 import com.intellij.openapi.vfs.VfsUtilCore;
@@ -53,7 +55,11 @@
  */
 public class PyUserSkeletonsUtil {
   public static final String USER_SKELETONS_DIR = "python-skeletons";
+  private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.codeInsight.userSkeletons.PyUserSkeletonsUtil");
+  public static final Key<Boolean> HAS_SKELETON = Key.create("PyUserSkeleton.hasSkeleton");
+
   @Nullable private static VirtualFile ourUserSkeletonsDirectory;
+  private static boolean ourNoSkeletonsErrorReported = false;
 
   @NotNull
   private static List<String> getPossibleUserSkeletonsPaths() {
@@ -75,6 +81,10 @@
         }
       }
     }
+    if (!ourNoSkeletonsErrorReported && ourUserSkeletonsDirectory == null) {
+      ourNoSkeletonsErrorReported = true;
+      LOG.warn("python-skeletons directory not found in paths: " + getPossibleUserSkeletonsPaths());
+    }
     return ourUserSkeletonsDirectory;
   }
 
@@ -169,6 +179,10 @@
 
   @Nullable
   private static PyFile getUserSkeletonForFile(@NotNull PyFile file) {
+    final Boolean hasSkeleton = file.getUserData(HAS_SKELETON);
+    if (hasSkeleton != null && !hasSkeleton) {
+      return null;
+    }
     final VirtualFile moduleVirtualFile = file.getVirtualFile();
     if (moduleVirtualFile != null) {
       String moduleName = QualifiedNameFinder.findShortestImportableName(file, moduleVirtualFile);
@@ -180,7 +194,9 @@
             moduleName = restored.toString();
           }
         }
-        return getUserSkeletonForModuleQName(moduleName, file);
+        final PyFile skeletonFile = getUserSkeletonForModuleQName(moduleName, file);
+        file.putUserData(HAS_SKELETON, skeletonFile != null);
+        return skeletonFile;
       }
     }
     return null;
diff --git a/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.form b/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.form
index 5aacd41..b6a71ee 100644
--- a/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.form
+++ b/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.form
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.python.configuration.PyIntegratedToolsConfigurable">
-  <grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="8" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+  <grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="9" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
     <margin top="0" left="0" bottom="0" right="0"/>
     <constraints>
       <xy x="20" y="20" width="657" height="302"/>
@@ -24,12 +24,12 @@
       </component>
       <vspacer id="fc19e">
         <constraints>
-          <grid row="7" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+          <grid row="8" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
         </constraints>
       </vspacer>
       <grid id="728f8" binding="myErrorPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
         <constraints>
-          <grid row="6" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+          <grid row="7" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
         </constraints>
         <properties/>
         <border type="none"/>
@@ -52,7 +52,7 @@
       <grid id="e7a5f" binding="myDocStringsPanel" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
         <margin top="0" left="0" bottom="0" right="0"/>
         <constraints>
-          <grid row="2" column="0" row-span="2" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+          <grid row="3" column="0" row-span="2" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
         </constraints>
         <properties/>
         <border type="none"/>
@@ -87,7 +87,7 @@
       <grid id="9f040" binding="myRestPanel" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
         <margin top="0" left="0" bottom="0" right="0"/>
         <constraints>
-          <grid row="4" column="0" row-span="2" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+          <grid row="5" column="0" row-span="2" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
         </constraints>
         <properties/>
         <border type="none"/>
@@ -117,6 +117,20 @@
           </component>
         </children>
       </grid>
+      <component id="a95d2" class="javax.swing.JComboBox" binding="myTemplateLanguage">
+        <constraints>
+          <grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+      </component>
+      <component id="36fa5" class="javax.swing.JLabel">
+        <constraints>
+          <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text value="Template language:"/>
+        </properties>
+      </component>
     </children>
   </grid>
 </form>
diff --git a/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java b/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java
index 9637706..63d88c8 100644
--- a/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java
+++ b/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java
@@ -21,6 +21,12 @@
 import com.intellij.facet.ui.FacetConfigurationQuickFix;
 import com.intellij.facet.ui.FacetEditorValidator;
 import com.intellij.facet.ui.ValidationResult;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.EditorFactory;
+import com.intellij.openapi.editor.ex.EditorEx;
+import com.intellij.openapi.editor.highlighter.EditorHighlighter;
+import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory;
 import com.intellij.openapi.fileChooser.FileChooserDescriptor;
 import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
 import com.intellij.openapi.fileTypes.PlainTextFileType;
@@ -48,6 +54,7 @@
 import com.jetbrains.python.documentation.PyDocumentationSettings;
 import com.jetbrains.python.packaging.*;
 import com.jetbrains.python.sdk.PythonSdkType;
+import com.jetbrains.python.templateLanguages.TemplatesService;
 import com.jetbrains.python.testing.PythonTestConfigurationsModel;
 import com.jetbrains.python.testing.TestRunnerService;
 import com.jetbrains.python.testing.VFSTestFrameworkListener;
@@ -56,6 +63,7 @@
 
 import javax.swing.*;
 import java.awt.*;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -77,6 +85,9 @@
   private JCheckBox analyzeDoctest;
   private JPanel myDocStringsPanel;
   private JPanel myRestPanel;
+  private JComboBox myTemplateLanguage;
+  private TemplatesConfigurationsModel myTemplatesModel;
+  private TemplatesService myTemplatesService;
 
   public PyIntegratedToolsConfigurable(@NotNull Module module) {
     myModule = module;
@@ -96,6 +107,7 @@
 
     myDocStringsPanel.setBorder(IdeBorderFactory.createTitledBorder("Docstrings"));
     myRestPanel.setBorder(IdeBorderFactory.createTitledBorder("reStructuredText"));
+    myTemplatesService = TemplatesService.getInstance(module);
   }
 
   @NotNull
@@ -186,6 +198,10 @@
     myModel = new PythonTestConfigurationsModel(configurations,
                                                 TestRunnerService.getInstance(myModule).getProjectConfiguration(), myModule);
 
+    List<String> templateConfigurations = TemplatesService.getAllTemplateLanguages();
+    myTemplatesModel = new TemplatesConfigurationsModel(templateConfigurations, myTemplatesService);
+    //noinspection unchecked
+    myTemplateLanguage.setModel(myTemplatesModel);
     updateConfigurations();
     initErrorValidation();
     return myMainPanel;
@@ -215,6 +231,9 @@
     if (!getRequirementsPath().equals(myRequirementsPathField.getText())) {
       return true;
     }
+    if (myTemplateLanguage.getSelectedItem() != myTemplatesModel.getTemplateLanguage()) {
+      return true;
+    }
     return false;
   }
 
@@ -223,6 +242,11 @@
     if (!Comparing.equal(myDocstringFormatComboBox.getSelectedItem(), myDocumentationSettings.myDocStringFormat)) {
       DaemonCodeAnalyzer.getInstance(myProject).restart();
     }
+    if (myTemplateLanguage.getSelectedItem() != myTemplatesModel.getTemplateLanguage()) {
+      myTemplatesModel.apply();
+      reparseFiles(Arrays.asList("html", "xml", "js")); //TODO: get from file extensions
+    }
+
     if (analyzeDoctest.isSelected() != myDocumentationSettings.analyzeDoctest) {
       final List<VirtualFile> files = Lists.newArrayList();
       ProjectRootManager.getInstance(myProject).getFileIndex().iterateContent(new ContentIterator() {
@@ -242,25 +266,43 @@
     reSTService.setWorkdir(myWorkDir.getText());
     if (txtIsRst.isSelected() != reSTService.txtIsRst()) {
       reSTService.setTxtIsRst(txtIsRst.isSelected());
-      reparseRstFiles();
+      reparseFiles(Collections.singletonList(PlainTextFileType.INSTANCE.getDefaultExtension()));
     }
     myDocumentationSettings.analyzeDoctest = analyzeDoctest.isSelected();
     PyPackageRequirementsSettings.getInstance(myModule).setRequirementsPath(myRequirementsPathField.getText());
     DaemonCodeAnalyzer.getInstance(myProject).restart();
   }
 
-  public void reparseRstFiles() {
+  public void reparseFiles(final List<String> extensions) {
     final List<VirtualFile> filesToReparse = Lists.newArrayList();
     ProjectRootManager.getInstance(myProject).getFileIndex().iterateContent(new ContentIterator() {
       @Override
       public boolean processFile(VirtualFile fileOrDir) {
-        if (!fileOrDir.isDirectory() && PlainTextFileType.INSTANCE.getDefaultExtension().equals(fileOrDir.getExtension())) {
+        if (!fileOrDir.isDirectory() && extensions.contains(fileOrDir.getExtension())) {
           filesToReparse.add(fileOrDir);
         }
         return true;
       }
     });
     FileContentUtilCore.reparseFiles(filesToReparse);
+
+    ApplicationManager.getApplication().runWriteAction(new Runnable() {
+      @Override
+      public void run() {
+
+        for (Editor editor : EditorFactory.getInstance().getAllEditors()) {
+          if (editor instanceof EditorEx && editor.getProject() == myProject) {
+            final VirtualFile vFile = ((EditorEx)editor).getVirtualFile();
+            if (vFile != null) {
+              final EditorHighlighter highlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(myProject, vFile);
+              ((EditorEx)editor).setHighlighter(highlighter);
+            }
+          }
+        }
+      }
+    });
+
+    DaemonCodeAnalyzer.getInstance(myProject).restart();
   }
 
   @Override
@@ -273,6 +315,9 @@
     txtIsRst.setSelected(ReSTService.getInstance(myModule).txtIsRst());
     analyzeDoctest.setSelected(myDocumentationSettings.analyzeDoctest);
     myRequirementsPathField.setText(getRequirementsPath());
+    myTemplateLanguage.setSelectedItem(myTemplatesModel.getTemplateLanguage());
+    myTemplatesModel.reset();
+
   }
 
   @Override
diff --git a/python/src/com/jetbrains/python/configuration/TemplatesConfigurationsModel.java b/python/src/com/jetbrains/python/configuration/TemplatesConfigurationsModel.java
new file mode 100644
index 0000000..0722b36
--- /dev/null
+++ b/python/src/com/jetbrains/python/configuration/TemplatesConfigurationsModel.java
@@ -0,0 +1,33 @@
+package com.jetbrains.python.configuration;
+
+import com.intellij.ui.CollectionComboBoxModel;
+import com.jetbrains.python.templateLanguages.TemplatesService;
+
+import java.util.List;
+
+/**
+ * User: catherine
+ */
+public class TemplatesConfigurationsModel extends CollectionComboBoxModel {
+  private String myTemplateLanguage;
+  private final TemplatesService myTemplatesService;
+
+  public TemplatesConfigurationsModel(final List items, TemplatesService templatesService) {
+    super(items, templatesService.getTemplateLanguage());
+    myTemplatesService = templatesService;
+    myTemplateLanguage = myTemplatesService.getTemplateLanguage();
+  }
+
+  public void reset() {
+    setSelectedItem(myTemplateLanguage);
+  }
+
+  public void apply() {
+    myTemplateLanguage = (String)getSelectedItem();
+    myTemplatesService.setTemplateLanguage(myTemplateLanguage);
+  }
+
+  public Object getTemplateLanguage() {
+    return myTemplateLanguage;
+  }
+}
\ No newline at end of file
diff --git a/python/src/com/jetbrains/python/console/PyConsoleOptions.java b/python/src/com/jetbrains/python/console/PyConsoleOptions.java
index 6816b93..d02b670 100644
--- a/python/src/com/jetbrains/python/console/PyConsoleOptions.java
+++ b/python/src/com/jetbrains/python/console/PyConsoleOptions.java
@@ -19,8 +19,12 @@
 import com.intellij.openapi.components.*;
 import com.intellij.openapi.module.ModuleManager;
 import com.intellij.openapi.project.Project;
+import com.intellij.util.PathMappingSettings;
 import com.intellij.util.containers.ComparatorUtil;
+import com.intellij.util.xmlb.annotations.*;
 import com.jetbrains.python.run.AbstractPyCommonOptionsForm;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.Map;
 
@@ -80,6 +84,7 @@
     public boolean myShowSeparatorLine = true;
   }
 
+  @Tag("console-settings")
   public static class PyConsoleSettings {
     public String myCustomStartScript = "";
     public String mySdkHome = null;
@@ -90,14 +95,8 @@
     public String myWorkingDirectory = "";
     public boolean myAddContentRoots = true;
     public boolean myAddSourceRoots;
-
-    public String getCustomStartScript() {
-      return myCustomStartScript;
-    }
-
-    public String getSdkHome() {
-      return mySdkHome;
-    }
+    @NotNull
+    private PathMappingSettings myMappings = new PathMappingSettings();
 
     public void apply(AbstractPyCommonOptionsForm form) {
       mySdkHome = form.getSdkHome();
@@ -109,6 +108,7 @@
 
       myAddContentRoots = form.addContentRoots();
       myAddSourceRoots = form.addSourceRoots();
+      myMappings = form.getMappingSettings() == null ? new PathMappingSettings() : form.getMappingSettings();
     }
 
     public boolean isModified(AbstractPyCommonOptionsForm form) {
@@ -119,7 +119,8 @@
              myAddContentRoots != form.addContentRoots() ||
              myAddSourceRoots != form.addSourceRoots()
              || !ComparatorUtil.equalsNullable(myModuleName, form.getModule() == null ? null : form.getModule().getName())
-             || !myWorkingDirectory.equals(form.getWorkingDirectory());
+             || !myWorkingDirectory.equals(form.getWorkingDirectory())
+             || !myMappings.equals(form.getMappingSettings());
     }
 
     public void reset(Project project, AbstractPyCommonOptionsForm form) {
@@ -143,33 +144,103 @@
         myModuleName = form.getModule().getName();
       }
 
-      form.setWorkingDirectory(form.getWorkingDirectory());
+      form.setWorkingDirectory(myWorkingDirectory);
+
+      form.setMappingSettings(myMappings);
     }
 
+    @Attribute("custom-start-script")
+    public String getCustomStartScript() {
+      return myCustomStartScript;
+    }
+
+    @Attribute("sdk-home")
+    public String getSdkHome() {
+      return mySdkHome;
+    }
+
+    @Attribute("module-name")
     public String getModuleName() {
       return myModuleName;
     }
 
+    @Attribute("working-directory")
     public String getWorkingDirectory() {
       return myWorkingDirectory;
     }
 
+    @Attribute("is-module-sdk")
     public boolean isUseModuleSdk() {
       return myUseModuleSdk;
     }
 
+    @Tag("envs")
+    @Property(surroundWithTag = false)
+    @MapAnnotation(surroundWithTag = false, surroundKeyWithTag = false, keyAttributeName = "key",
+                   entryTagName = "env", valueAttributeName = "value", surroundValueWithTag = false)
     public Map<String, String> getEnvs() {
       return myEnvs;
     }
 
+    @Attribute("add-content-roots")
     public boolean addContentRoots() {
       return myAddContentRoots;
     }
 
+    @Attribute("add-source-roots")
     public boolean addSourceRoots() {
       return myAddSourceRoots;
     }
 
+    @Attribute("interpreter-options")
+    public String getInterpreterOptions() {
+      return myInterpreterOptions;
+    }
+
+    @AbstractCollection(surroundWithTag = false)
+    public PathMappingSettings getMappings() {
+      return myMappings;
+    }
+
+    public void setCustomStartScript(String customStartScript) {
+      myCustomStartScript = customStartScript;
+    }
+
+    public void setSdkHome(String sdkHome) {
+      mySdkHome = sdkHome;
+    }
+
+    public void setInterpreterOptions(String interpreterOptions) {
+      myInterpreterOptions = interpreterOptions;
+    }
+
+    public void setUseModuleSdk(boolean useModuleSdk) {
+      myUseModuleSdk = useModuleSdk;
+    }
+
+    public void setModuleName(String moduleName) {
+      myModuleName = moduleName;
+    }
+
+    public void setEnvs(Map<String, String> envs) {
+      myEnvs = envs;
+    }
+
+    public void setWorkingDirectory(String workingDirectory) {
+      myWorkingDirectory = workingDirectory;
+    }
+
+    public void setAddContentRoots(boolean addContentRoots) {
+      myAddContentRoots = addContentRoots;
+    }
+
+    public void setAddSourceRoots(boolean addSourceRoots) {
+      myAddSourceRoots = addSourceRoots;
+    }
+
+    public void setMappings(@Nullable PathMappingSettings mappings) {
+      myMappings = mappings != null ? mappings : new PathMappingSettings();
+    }
   }
 }
 
diff --git a/python/src/com/jetbrains/python/console/PyConsoleProcessHandler.java b/python/src/com/jetbrains/python/console/PyConsoleProcessHandler.java
index 4ea06d6..c0c65fc 100644
--- a/python/src/com/jetbrains/python/console/PyConsoleProcessHandler.java
+++ b/python/src/com/jetbrains/python/console/PyConsoleProcessHandler.java
@@ -24,7 +24,7 @@
 import java.nio.charset.Charset;
 
 /**
- * @author oleg
+ * @author traff
  */
 public class PyConsoleProcessHandler extends PythonProcessHandler {
   private final PythonConsoleView myConsoleView;
@@ -57,6 +57,11 @@
     return !myPydevConsoleCommunication.isExecuting();
   }
 
+  @Override
+  protected boolean shouldKillProcessSoftly() {
+    return false;
+  }
+
   private void doCloseCommunication() {
     if (myPydevConsoleCommunication != null) {
 
diff --git a/python/src/com/jetbrains/python/console/PyOpenDebugConsoleAction.java b/python/src/com/jetbrains/python/console/PyOpenDebugConsoleAction.java
index 3f4e801..45906d5 100644
--- a/python/src/com/jetbrains/python/console/PyOpenDebugConsoleAction.java
+++ b/python/src/com/jetbrains/python/console/PyOpenDebugConsoleAction.java
@@ -22,7 +22,6 @@
 import com.intellij.openapi.actionSystem.AnActionEvent;
 import com.intellij.openapi.actionSystem.CommonDataKeys;
 import com.intellij.openapi.actionSystem.DataContext;
-import com.intellij.openapi.actionSystem.PlatformDataKeys;
 import com.intellij.openapi.project.DumbAware;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.wm.IdeFocusManager;
@@ -53,6 +52,7 @@
     }
   }
 
+  @Override
   public void actionPerformed(final AnActionEvent e) {
     final Project project = e.getData(CommonDataKeys.PROJECT);
     if (project != null) {
diff --git a/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java b/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java
index b0ef1d3..6af6c13 100644
--- a/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java
+++ b/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java
@@ -53,10 +53,11 @@
  *
  * @author Fabio
  */
-public class PydevConsoleCommunication extends AbstractConsoleCommunication implements IScriptConsoleCommunication, XmlRpcHandler,
+public class PydevConsoleCommunication extends AbstractConsoleCommunication implements XmlRpcHandler,
                                                                                        PyFrameAccessor {
 
   private static final String EXEC_LINE = "execLine";
+  private static final String EXEC_MULTILINE = "execMultipleLines";
   private static final String GET_COMPLETIONS = "getCompletions";
   private static final String GET_DESCRIPTION = "getDescription";
   private static final String GET_FRAME = "getFrame";
@@ -64,6 +65,7 @@
   private static final String CHANGE_VARIABLE = "changeVariable";
   private static final String HANDSHAKE = "handshake";
   private static final String CLOSE = "close";
+
   /**
    * XML-RPC client for sending messages to the server.
    */
@@ -167,7 +169,7 @@
    */
   public Object execute(String method, Vector params) throws Exception {
     if ("NotifyFinished".equals(method)) {
-      return execNotifyFinished();
+      return execNotifyFinished((Boolean)params.get(0));
     }
     else if ("RequestInput".equals(method)) {
       return execRequestInput();
@@ -223,9 +225,9 @@
     return Boolean.FALSE;
   }
 
-  private Object execNotifyFinished() {
+  private Object execNotifyFinished(boolean more) {
     setExecuting(false);
-    notifyCommandExecuted();
+    notifyCommandExecuted(more);
     return true;
   }
 
@@ -261,12 +263,13 @@
   /**
    * Executes the needed command
    *
+   * @param command
    * @return a Pair with (null, more) or (error, false)
    * @throws XmlRpcException
    */
-  protected Pair<String, Boolean> exec(final String command) throws XmlRpcException {
+  protected Pair<String, Boolean> exec(final ConsoleCodeFragment command) throws XmlRpcException {
     setExecuting(true);
-    Object execute = myClient.execute(EXEC_LINE, new Object[]{command});
+    Object execute = myClient.execute(command.isSingleLine() ? EXEC_LINE : EXEC_MULTILINE, new Object[]{command.getText()});
 
     Object object;
     if (execute instanceof Vector) {
@@ -278,6 +281,15 @@
     else {
       object = execute;
     }
+    Pair<String, Boolean> result = parseResult(object);
+    if (result.second) {
+      setExecuting(false);
+    }
+
+    return result;
+  }
+
+  private Pair<String, Boolean> parseResult(Object object) {
     if (object instanceof Boolean) {
       return new Pair<String, Boolean>(null, (Boolean)object);
     }
@@ -314,10 +326,10 @@
    *
    * @param command the command to be executed in the client
    */
-  public void execInterpreter(final String command, final Function<InterpreterResponse, Object> onResponseReceived) {
+  public void execInterpreter(final ConsoleCodeFragment command, final Function<InterpreterResponse, Object> onResponseReceived) {
     nextResponse = null;
     if (waitingForInput) {
-      inputReceived = command;
+      inputReceived = command.getText();
       waitingForInput = false;
       //the thread that we started in the last exec is still alive if we were waiting for an input.
     }
diff --git a/python/src/com/jetbrains/python/console/PydevConsoleExecuteActionHandler.java b/python/src/com/jetbrains/python/console/PydevConsoleExecuteActionHandler.java
index 20e8655..b4d8360 100644
--- a/python/src/com/jetbrains/python/console/PydevConsoleExecuteActionHandler.java
+++ b/python/src/com/jetbrains/python/console/PydevConsoleExecuteActionHandler.java
@@ -18,11 +18,10 @@
 import com.intellij.codeInsight.hint.HintManager;
 import com.intellij.execution.console.LanguageConsoleImpl;
 import com.intellij.execution.console.LanguageConsoleView;
+import com.intellij.execution.console.ProcessBackedConsoleExecuteActionHandler;
 import com.intellij.execution.process.ProcessHandler;
-import com.intellij.execution.runners.ConsoleExecuteActionHandler;
 import com.intellij.openapi.application.Result;
 import com.intellij.openapi.command.WriteCommandAction;
-import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.EditorModificationUtil;
 import com.intellij.openapi.fileTypes.PlainTextLanguage;
@@ -32,6 +31,7 @@
 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
 import com.intellij.psi.impl.source.codeStyle.IndentHelperImpl;
 import com.intellij.util.Function;
+import com.intellij.util.ui.UIUtil;
 import com.jetbrains.python.PythonFileType;
 import com.jetbrains.python.PythonLanguage;
 import com.jetbrains.python.console.pydev.ConsoleCommunication;
@@ -40,13 +40,10 @@
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-import java.util.Scanner;
-
 /**
  * @author traff
  */
-public class PydevConsoleExecuteActionHandler extends ConsoleExecuteActionHandler implements ConsoleCommunicationListener {
-
+public class PydevConsoleExecuteActionHandler extends ProcessBackedConsoleExecuteActionHandler implements ConsoleCommunicationListener {
   private final LanguageConsoleView myConsoleView;
 
   private String myInMultilineStringState = null;
@@ -66,20 +63,21 @@
   }
 
   @Override
-  public void processLine(final String text) {
-    processLine(text, true);
+  public void processLine(@NotNull final String text) {
+    processLine(text, false);
   }
 
-  public void processLine(final String text, boolean execAnyway) {
+  public void processLine(@NotNull final String text, boolean execAnyway) {
     int indentBefore = myCurrentIndentSize;
     if (text.isEmpty()) {
       processOneLine(text);
     }
     else {
-      Scanner s = new Scanner(text);
-      while (s.hasNextLine()) {
-        String line = s.nextLine();
-        processOneLine(line);
+      if (StringUtil.countNewLines(text.trim()) > 0) {
+        executeMultiLine(text);
+      }
+      else {
+        processOneLine(text);
       }
     }
     if (execAnyway && myCurrentIndentSize > 0 && indentBefore == 0) { //if code was indented and we need to exec anyway
@@ -87,6 +85,19 @@
     }
   }
 
+  private void executeMultiLine(@NotNull String text) {
+    if (myInputBuffer == null) {
+      myInputBuffer = new StringBuilder();
+    }
+
+    myInputBuffer.append(text);
+
+    final LanguageConsoleImpl console = myConsoleView.getConsole();
+    final Editor currentEditor = console.getConsoleEditor();
+
+    sendLineToConsole(new ConsoleCommunication.ConsoleCodeFragment(myInputBuffer.toString(), false), console, currentEditor);
+  }
+
   private void processOneLine(String line) {
     int indentSize = IndentHelperImpl.getIndent(getProject(), PythonFileType.INSTANCE, line, false);
     line = StringUtil.trimTrailing(line);
@@ -169,21 +180,28 @@
         indentEditor(currentEditor, indent);
         more(console, currentEditor);
 
+        myConsoleCommunication.notifyCommandExecuted(true);
         return;
       }
     }
 
 
+    sendLineToConsole(new ConsoleCommunication.ConsoleCodeFragment(myInputBuffer.toString(), true), console, currentEditor);
+  }
+
+  private void sendLineToConsole(@NotNull final ConsoleCommunication.ConsoleCodeFragment code,
+                                 @NotNull final LanguageConsoleImpl console,
+                                 @NotNull final Editor currentEditor) {
     if (myConsoleCommunication != null) {
       final boolean waitedForInputBefore = myConsoleCommunication.isWaitingForInput();
-      final String command = myInputBuffer.toString();
       if (myConsoleCommunication.isWaitingForInput()) {
         myInputBuffer.setLength(0);
       }
       else {
         executingPrompt(console);
       }
-      myConsoleCommunication.execInterpreter(command, new Function<InterpreterResponse, Object>() {
+      myConsoleCommunication.execInterpreter(code, new Function<InterpreterResponse, Object>() {
+        @Override
         public Object fun(final InterpreterResponse interpreterResponse) {
           // clear
           myInputBuffer = null;
@@ -192,9 +210,15 @@
             more(console, currentEditor);
             if (myCurrentIndentSize == 0) {
               // compute current indentation
-              setCurrentIndentSize(IndentHelperImpl.getIndent(getProject(), PythonFileType.INSTANCE, line, false) + getPythonIndent());
+              setCurrentIndentSize(
+                IndentHelperImpl.getIndent(getProject(), PythonFileType.INSTANCE, lastLine(code.getText()), false) + getPythonIndent());
               // In this case we can insert indent automatically
-              indentEditor(currentEditor, myCurrentIndentSize);
+              UIUtil.invokeLaterIfNeeded(new Runnable() {
+                @Override
+                public void run() {
+                  indentEditor(currentEditor, myCurrentIndentSize);
+                }
+              });
             }
           }
           else {
@@ -215,6 +239,11 @@
     }
   }
 
+  private static String lastLine(@NotNull String text) {
+    String[] lines = StringUtil.splitByLinesDontTrim(text);
+    return lines[lines.length - 1];
+  }
+
   private void ordinaryPrompt(LanguageConsoleImpl console, Editor currentEditor) {
     if (!myConsoleCommunication.isExecuting()) {
       if (!PyConsoleUtil.ORDINARY_PROMPT.equals(console.getPrompt())) {
@@ -243,10 +272,13 @@
   }
 
   @Override
-  public void commandExecuted() {
-    final LanguageConsoleImpl console = myConsoleView.getConsole();
-    final Editor currentEditor = console.getConsoleEditor();
-    ordinaryPrompt(console, currentEditor);
+  public void commandExecuted(boolean more) {
+    if (!more) {
+      final LanguageConsoleImpl console = myConsoleView.getConsole();
+      final Editor currentEditor = console.getConsoleEditor();
+
+      ordinaryPrompt(console, currentEditor);
+    }
   }
 
   @Override
@@ -261,7 +293,7 @@
     setCurrentIndentSize(1);
   }
 
-  @Override
+  @SuppressWarnings({"override", "deprecation"})
   public void finishExecution() {
     final LanguageConsoleImpl console = myConsoleView.getConsole();
     final Editor currentEditor = console.getConsoleEditor();
@@ -303,7 +335,7 @@
   private void indentEditor(final Editor editor, final int indentSize) {
     new WriteCommandAction(getProject()) {
       @Override
-      protected void run(Result result) throws Throwable {
+      protected void run(@NotNull Result result) throws Throwable {
         EditorModificationUtil.insertStringAtCaret(editor, IndentHelperImpl.fillIndent(getProject(), PythonFileType.INSTANCE, indentSize));
       }
     }.execute();
@@ -312,7 +344,7 @@
   private void cleanEditor(final Editor editor) {
     new WriteCommandAction(getProject()) {
       @Override
-      protected void run(Result result) throws Throwable {
+      protected void run(@NotNull Result result) throws Throwable {
         editor.getDocument().setText("");
       }
     }.execute();
@@ -335,27 +367,26 @@
   }
 
   @Override
-  public void runExecuteAction(LanguageConsoleImpl languageConsole) {
+  public void runExecuteAction(@NotNull LanguageConsoleView console) {
     if (isEnabled()) {
       if (!canExecuteNow()) {
-        HintManager.getInstance().showErrorHint(languageConsole.getConsoleEditor(), getPrevCommandRunningMessage());
+        HintManager.getInstance().showErrorHint(console.getConsole().getConsoleEditor(), getPrevCommandRunningMessage());
       }
       else {
-        doRunExecuteAction(languageConsole);
+        doRunExecuteAction(console);
       }
     }
     else {
-      HintManager.getInstance().showErrorHint(languageConsole.getConsoleEditor(), getConsoleIsNotEnabledMessage());
+      HintManager.getInstance().showErrorHint(console.getConsole().getConsoleEditor(), getConsoleIsNotEnabledMessage());
     }
   }
 
-  private void doRunExecuteAction(LanguageConsoleImpl languageConsole) {
-    if (shouldCopyToHistory(languageConsole)) {
-      copyToHistoryAndExecute(languageConsole);
+  private void doRunExecuteAction(LanguageConsoleView console) {
+    if (shouldCopyToHistory(console.getConsole())) {
+      copyToHistoryAndExecute(console);
     }
     else {
-      final Document document = languageConsole.getConsoleEditor().getDocument();
-      processLine(document.getText());
+      processLine(console.getConsole().getConsoleEditor().getDocument().getText());
     }
   }
 
@@ -363,8 +394,8 @@
     return !PyConsoleUtil.isPagingPrompt(console.getPrompt());
   }
 
-  private void copyToHistoryAndExecute(LanguageConsoleImpl languageConsole) {
-    super.runExecuteAction(languageConsole);
+  private void copyToHistoryAndExecute(LanguageConsoleView console) {
+    super.runExecuteAction(console);
   }
 
   public boolean canExecuteNow() {
diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java
index 947b3c9..213f635 100644
--- a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java
+++ b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java
@@ -23,12 +23,12 @@
 import com.intellij.execution.configurations.GeneralCommandLine;
 import com.intellij.execution.console.ConsoleHistoryController;
 import com.intellij.execution.console.LanguageConsoleView;
+import com.intellij.execution.console.ProcessBackedConsoleExecuteActionHandler;
 import com.intellij.execution.process.CommandLineArgumentsProvider;
 import com.intellij.execution.process.ProcessAdapter;
 import com.intellij.execution.process.ProcessEvent;
 import com.intellij.execution.process.ProcessOutputTypes;
 import com.intellij.execution.runners.AbstractConsoleRunnerWithHistory;
-import com.intellij.execution.runners.ConsoleExecuteActionHandler;
 import com.intellij.execution.ui.RunContentDescriptor;
 import com.intellij.icons.AllIcons;
 import com.intellij.openapi.actionSystem.*;
@@ -50,10 +50,11 @@
 import com.intellij.openapi.util.io.StreamUtil;
 import com.intellij.openapi.vfs.CharsetToolkit;
 import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.encoding.EncodingManager;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiFile;
 import com.intellij.psi.impl.source.tree.FileElement;
-import com.intellij.remotesdk.RemoteSdkData;
+import com.intellij.remotesdk.RemoteSdkCredentials;
 import com.intellij.remotesdk.RemoteSshProcess;
 import com.intellij.testFramework.LightVirtualFile;
 import com.intellij.util.ArrayUtil;
@@ -61,16 +62,11 @@
 import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.net.NetUtils;
 import com.intellij.util.ui.UIUtil;
-import com.intellij.xdebugger.impl.frame.XStandaloneVariablesView;
 import com.jetbrains.python.PythonHelpersLocator;
 import com.jetbrains.python.console.completion.PydevConsoleElement;
 import com.jetbrains.python.console.parsing.PythonConsoleData;
 import com.jetbrains.python.console.pydev.ConsoleCommunication;
-import com.jetbrains.python.console.pydev.ConsoleCommunicationListener;
-import com.jetbrains.python.debugger.PyDebuggerEditorsProvider;
 import com.jetbrains.python.debugger.PySourcePosition;
-import com.jetbrains.python.debugger.PyStackFrame;
-import com.jetbrains.python.debugger.PyStackFrameInfo;
 import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
 import com.jetbrains.python.run.ProcessRunner;
 import com.jetbrains.python.run.PythonCommandLineState;
@@ -81,11 +77,12 @@
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import java.awt.event.InputEvent;
 import java.awt.event.KeyEvent;
 import java.io.File;
 import java.io.IOException;
+import java.nio.charset.Charset;
 import java.util.*;
-import java.util.List;
 
 import static com.jetbrains.python.sdk.PythonEnvUtil.setPythonIOEncoding;
 import static com.jetbrains.python.sdk.PythonEnvUtil.setPythonUnbuffered;
@@ -95,6 +92,7 @@
  */
 public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonConsoleView> {
   private static final Logger LOG = Logger.getInstance(PydevConsoleRunner.class.getName());
+  @SuppressWarnings("SpellCheckingInspection")
   public static final String PYDEV_PYDEVCONSOLE_PY = "pydev/pydevconsole.py";
   public static final int PORTS_WAITING_TIMEOUT = 20000;
 
@@ -134,7 +132,11 @@
   }
 
   public static Map<String, String> addDefaultEnvironments(Sdk sdk, Map<String, String> envs) {
-    setPythonIOEncoding(setPythonUnbuffered(envs), "utf-8");
+    Charset defaultCharset = EncodingManager.getInstance().getDefaultCharset();
+
+    final String encoding = defaultCharset != null ? defaultCharset.name() : "utf-8";
+    setPythonIOEncoding(setPythonUnbuffered(envs), encoding);
+
     PythonSdkFlavor.initPythonPath(envs, true, PythonCommandLineState.getAddedPaths(sdk));
     return envs;
   }
@@ -188,6 +190,7 @@
       @Override
       public void run() {
         ProgressManager.getInstance().run(new Task.Backgroundable(getProject(), "Connecting to console", false) {
+          @Override
           public void run(@NotNull final ProgressIndicator indicator) {
             indicator.setText("Connecting to console...");
             try {
@@ -195,6 +198,7 @@
             }
             catch (ExecutionException e) {
               LOG.warn("Error running console", e);
+              assert myProject != null;
               ExecutionHelper.showErrors(myProject, Arrays.<Exception>asList(e), getTitle(), null);
             }
           }
@@ -247,14 +251,17 @@
       args.add(String.valueOf(port));
     }
     return new CommandLineArgumentsProvider() {
+      @Override
       public String[] getArguments() {
         return ArrayUtil.toStringArray(args);
       }
 
+      @Override
       public boolean passParentEnvs() {
         return false;
       }
 
+      @Override
       public Map<String, String> getAdditionalEnvs() {
         return addDefaultEnvironments(sdk, environmentVariables);
       }
@@ -295,22 +302,25 @@
 
   private Process createRemoteConsoleProcess(PythonRemoteInterpreterManager manager, String[] command, Map<String, String> env)
     throws ExecutionException {
-    RemoteSdkData data = (RemoteSdkData)mySdk.getSdkAdditionalData();
+    RemoteSdkCredentials data = (RemoteSdkCredentials)mySdk.getSdkAdditionalData();
+    assert data != null;
 
     GeneralCommandLine commandLine = new GeneralCommandLine(command);
+
+
     commandLine.getEnvironment().putAll(env);
 
     commandLine.getParametersList().set(1, PythonRemoteInterpreterManager.toSystemDependent(new File(data.getHelpersPath(),
                                                                                                      PYDEV_PYDEVCONSOLE_PY)
                                                                                               .getPath(),
                                                                                             PySourcePosition.isWindowsPath(
-                                                                                              data.getInterpreterPath())));
+                                                                                              data.getInterpreterPath())
+    ));
     commandLine.getParametersList().set(2, "0");
     commandLine.getParametersList().set(3, "0");
 
     myCommandLine = commandLine.getCommandLineString();
 
-
     RemoteSshProcess remoteProcess =
       manager.createRemoteProcess(getProject(), data, commandLine, true);
 
@@ -345,7 +355,7 @@
         try {
           return Integer.parseInt(line);
         }
-        catch (NumberFormatException e) {
+        catch (NumberFormatException ignored) {
           continue;
         }
       }
@@ -354,7 +364,7 @@
 
         Thread.sleep(200);
       }
-      catch (InterruptedException e1) {
+      catch (InterruptedException ignored) {
       }
 
       if (process.exitValue() != 0) {
@@ -362,7 +372,7 @@
         try {
           error = "Console process terminated with error:\n" + StreamUtil.readText(process.getErrorStream());
         }
-        catch (Exception e) {
+        catch (Exception ignored) {
           error = "Console process terminated with exit code " + process.exitValue();
         }
         throw new ExecutionException(error);
@@ -444,7 +454,7 @@
         e.getPresentation().setEnabled(enabled);
       }
     };
-    anAction.registerCustomShortcutSet(KeyEvent.VK_C, KeyEvent.CTRL_MASK, getConsoleView().getConsole().getConsoleEditor().getComponent());
+    anAction.registerCustomShortcutSet(KeyEvent.VK_C, InputEvent.CTRL_MASK, getConsoleView().getConsole().getConsoleEditor().getComponent());
     anAction.getTemplatePresentation().setVisible(false);
     return anAction;
   }
@@ -455,7 +465,8 @@
       @Override
       public void actionPerformed(final AnActionEvent e) {
         new WriteCommandAction(getLanguageConsole().getProject(), getLanguageConsole().getFile()) {
-          protected void run(final Result result) throws Throwable {
+          @Override
+          protected void run(@NotNull final Result result) throws Throwable {
             String text = getLanguageConsole().getEditorDocument().getText();
             String newText = text.substring(0, text.length() - myConsoleExecuteActionHandler.getPythonIndent());
             getLanguageConsole().getEditorDocument().setText(newText);
@@ -492,7 +503,7 @@
       try {
         res = myPydevConsoleCommunication.handshake();
       }
-      catch (XmlRpcException e) {
+      catch (XmlRpcException ignored) {
         res = false;
       }
       if (res) {
@@ -507,7 +518,7 @@
           try {
             Thread.sleep(100);
           }
-          catch (InterruptedException e) {
+          catch (InterruptedException ignored) {
           }
         }
       }
@@ -546,7 +557,7 @@
             // waiting for REPL communication before destroying process handler
             Thread.sleep(300);
           }
-          catch (Exception e1) {
+          catch (Exception ignored) {
             // Ignore
           }
           generalStopAction.actionPerformed(furtherActionEvent);
@@ -565,12 +576,12 @@
 
   @NotNull
   @Override
-  protected ConsoleExecuteActionHandler createConsoleExecuteActionHandler() {
+  protected ProcessBackedConsoleExecuteActionHandler createExecuteActionHandler() {
     myConsoleExecuteActionHandler =
       new PydevConsoleExecuteActionHandler(getConsoleView(), getProcessHandler(), myPydevConsoleCommunication);
     myConsoleExecuteActionHandler.setEnabled(false);
     myHistoryController = new ConsoleHistoryController(myConsoleType.getTypeId(), "", getLanguageConsole(),
-                                 myConsoleExecuteActionHandler.getConsoleHistoryModel());
+                                                       myConsoleExecuteActionHandler.getConsoleHistoryModel());
     myHistoryController.install();
     return myConsoleExecuteActionHandler;
   }
@@ -670,8 +681,9 @@
       @Override
       public void run(@NotNull ProgressIndicator indicator) {
         UIUtil.invokeLaterIfNeeded(new Runnable() {
+          @Override
           public void run() {
-            PydevConsoleRunner.this.closeCommunication();
+            closeCommunication();
           }
         });
 
diff --git a/python/src/com/jetbrains/python/console/PythonConsoleView.java b/python/src/com/jetbrains/python/console/PythonConsoleView.java
index ddf62ed..a32f3fc 100644
--- a/python/src/com/jetbrains/python/console/PythonConsoleView.java
+++ b/python/src/com/jetbrains/python/console/PythonConsoleView.java
@@ -67,22 +67,31 @@
 
   private static final Logger LOG = Logger.getInstance(PythonConsoleView.class);
 
-  private Project myProject;
+  private final Project myProject;
   private PydevConsoleExecuteActionHandler myExecuteActionHandler;
   private PyConsoleSourceHighlighter mySourceHighlighter;
   private boolean myIsIPythonOutput = false;
-  private PyHighlighter myPyHighlighter;
-  private EditorColorsScheme myScheme;
+  private final PyHighlighter myPyHighlighter;
+  private final EditorColorsScheme myScheme;
   private boolean myHyperlink;
 
   private final LanguageConsoleViewImpl myLanguageConsoleView;
   
-  private Disposable mySplittedDisposable;
+  private Disposable mySplitDisposable;
 
-  public PythonConsoleView(final Project project, final String title, Sdk sdk) {
+  public PythonConsoleView(final Project project, final String title, final Sdk sdk) {
     super(new BorderLayout());
 
-    myLanguageConsoleView = new LanguageConsoleViewImpl(new PythonLanguageConsole(project, title, sdk));
+    LanguageConsoleImpl languageConsole = new LanguageConsoleImpl(project, title, PythonLanguage.getInstance(), false);
+    if (languageConsole.getFile().getVirtualFile() != null) {
+      languageConsole.getFile().getVirtualFile().putUserData(LanguageLevel.KEY, PythonSdkType.getLanguageLevelForSdk(sdk));
+    }
+    // Mark editor as console one, to prevent autopopup completion
+    languageConsole.getConsoleEditor().putUserData(PythonConsoleAutopopupBlockingHandler.REPL_KEY, new Object());
+    languageConsole.setShowSeparatorLine(PyConsoleOptions.getInstance(project).isShowSeparatorLine());
+    languageConsole.initComponents();
+
+    myLanguageConsoleView = new LanguageConsoleViewImpl(languageConsole);
 
     add(myLanguageConsoleView.getComponent(), BorderLayout.CENTER);
 
@@ -96,21 +105,22 @@
   }
 
   public void setConsoleCommunication(final ConsoleCommunication communication) {
-    getPythonLanguageConsole().setConsoleCommunication(communication);
+    getPythonLanguageConsole().getFile().putCopyableUserData(PydevConsoleRunner.CONSOLE_KEY, communication);
   }
 
   public void setExecutionHandler(@NotNull PydevConsoleExecuteActionHandler consoleExecuteActionHandler) {
     myExecuteActionHandler = consoleExecuteActionHandler;
   }
 
+  @Override
   public void requestFocus() {
     IdeFocusManager.findInstance().requestFocus(getPythonLanguageConsole().getConsoleEditor().getContentComponent(), true);
     myLanguageConsoleView.updateUI();
     getLanguageConsole().getHistoryViewer().getComponent().updateUI();
   }
 
-  private PythonLanguageConsole getPythonLanguageConsole() {
-    return ((PythonLanguageConsole)getLanguageConsole());
+  private LanguageConsoleImpl getPythonLanguageConsole() {
+    return getLanguageConsole();
   }
 
   public LanguageConsoleImpl getLanguageConsole() {
@@ -120,6 +130,7 @@
   @Override
   public void executeCode(final @NotNull String code, @Nullable final Editor editor) {
     ProgressManager.getInstance().run(new Task.Backgroundable(null, "Executing code in console...", false) {
+      @Override
       public void run(@NotNull final ProgressIndicator indicator) {
         long time = System.currentTimeMillis();
         while (!myExecuteActionHandler.isEnabled() || !myExecuteActionHandler.canExecuteNow()) {
@@ -140,7 +151,7 @@
           try {
             Thread.sleep(300);
           }
-          catch (InterruptedException e) {
+          catch (InterruptedException ignored) {
           }
         }
         if (!indicator.isCanceled()) {
@@ -152,7 +163,9 @@
 
 
   private void doExecute(String code) {
-    executeInConsole(PyConsoleIndentUtil.normalize(code, myExecuteActionHandler.getCurrentIndentSize()));
+    String codeFragment = PyConsoleIndentUtil.normalize(code, myExecuteActionHandler.getCurrentIndentSize());
+    codeFragment += "\n";
+    executeInConsole(codeFragment);
   }
 
   public void executeInConsole(final String code) {
@@ -161,11 +174,11 @@
       public void run() {
         String text = getPythonLanguageConsole().getConsoleEditor().getDocument().getText();
 
-        getPythonLanguageConsole().setTextToEditor(code);
-        myExecuteActionHandler.runExecuteAction(getPythonLanguageConsole());
+        getPythonLanguageConsole().setInputText(code);
+        myExecuteActionHandler.runExecuteAction(myLanguageConsoleView);
 
         if (!StringUtil.isEmpty(text)) {
-          getPythonLanguageConsole().setTextToEditor(text);
+          getPythonLanguageConsole().setInputText(text);
         }
       }
     });
@@ -184,7 +197,8 @@
     myLanguageConsoleView.print(text, outputType);
   }
 
-  public void print(String text, final ConsoleViewContentType outputType) {
+  @Override
+  public void print(@NotNull String text, @NotNull final ConsoleViewContentType outputType) {
     detectIPython(text, outputType);
     if (PyConsoleUtil.detectIPythonEnd(text)) {
       myIsIPythonOutput = false;
@@ -343,7 +357,7 @@
   }
 
   public void setSdk(Sdk sdk) {
-    getPythonLanguageConsole().setSdk(sdk);
+    getPythonLanguageConsole().getFile().putCopyableUserData(PydevConsoleRunner.CONSOLE_SDK, sdk);
   }
 
   @Override
@@ -383,7 +397,7 @@
     final XStandaloneVariablesView view = new XStandaloneVariablesView(myProject, new PyDebuggerEditorsProvider(), stackFrame);
     consoleCommunication.addCommunicationListener(new ConsoleCommunicationListener() {
       @Override
-      public void commandExecuted() {
+      public void commandExecuted(boolean more) {
         view.rebuildView();
       }
 
@@ -398,7 +412,7 @@
     removeAll();
     JSplitPane p = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
     p.add(myLanguageConsoleView.getComponent(), JSplitPane.LEFT);
-    mySplittedDisposable = componentDisposable;
+    mySplitDisposable = componentDisposable;
     p.add(component, JSplitPane.RIGHT);
     p.setDividerLocation((int)getSize().getWidth()*2/3);
     add(p, BorderLayout.CENTER);
@@ -412,38 +426,9 @@
     add(myLanguageConsoleView.getComponent(), BorderLayout.CENTER);
     validate();
     repaint();
-    if (mySplittedDisposable != null) {
-      Disposer.dispose(mySplittedDisposable);
-      mySplittedDisposable = null;
-    }
-  }
-
-  private static class PythonLanguageConsole extends LanguageConsoleImpl {
-
-    public PythonLanguageConsole(final Project project, final String title, final Sdk sdk) {
-      super(project, title, PythonLanguage.getInstance(), false);
-      initLanguageLevel(sdk);
-      // Mark editor as console one, to prevent autopopup completion
-      getConsoleEditor().putUserData(PythonConsoleAutopopupBlockingHandler.REPL_KEY, new Object());
-
-      setShowSeparatorLine(PyConsoleOptions.getInstance(project).isShowSeparatorLine());
-
-      initComponents();
-    }
-
-    private void initLanguageLevel(@Nullable Sdk sdk) {
-      if (myFile.getVirtualFile() != null) {
-        //noinspection ConstantConditions
-        myFile.getVirtualFile().putUserData(LanguageLevel.KEY, PythonSdkType.getLanguageLevelForSdk(sdk));
-      }
-    }
-
-    public void setConsoleCommunication(final ConsoleCommunication communication) {
-      myFile.putCopyableUserData(PydevConsoleRunner.CONSOLE_KEY, communication);
-    }
-
-    public void setSdk(Sdk sdk) {
-      myFile.putCopyableUserData(PydevConsoleRunner.CONSOLE_SDK, sdk);
+    if (mySplitDisposable != null) {
+      Disposer.dispose(mySplitDisposable);
+      mySplitDisposable = null;
     }
   }
 }
diff --git a/python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java b/python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java
index 8f55aed..d24a2a8a 100644
--- a/python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java
+++ b/python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java
@@ -63,8 +63,8 @@
     return false;
   }
 
-  protected void exec(final String command, final ProcessDebugger.DebugCallback<Pair<String, Boolean>> callback) {
-    myDebugProcess.consoleExec(command, new ProcessDebugger.DebugCallback<String>() {
+  protected void exec(final ConsoleCodeFragment command, final ProcessDebugger.DebugCallback<Pair<String, Boolean>> callback) {
+    myDebugProcess.consoleExec(command.getText(), new ProcessDebugger.DebugCallback<String>() {
       @Override
       public void ok(String value) {
         callback.ok(parseExecResponseString(value));
@@ -77,9 +77,9 @@
     });
   }
 
-  public void execInterpreter(String s, final Function<InterpreterResponse, Object> callback) {
-    myExpression.append(s);
-    exec(myExpression.toString(), new ProcessDebugger.DebugCallback<Pair<String, Boolean>>() {
+  public void execInterpreter(ConsoleCodeFragment code, final Function<InterpreterResponse, Object> callback) {
+    myExpression.append(code.getText());
+    exec(new ConsoleCodeFragment(myExpression.toString(), false), new ProcessDebugger.DebugCallback<Pair<String, Boolean>>() {
       @Override
       public void ok(Pair<String, Boolean> executed) {
         boolean more = executed.second;
diff --git a/python/src/com/jetbrains/python/console/PythonDebugLanguageConsoleView.java b/python/src/com/jetbrains/python/console/PythonDebugLanguageConsoleView.java
index 71b4e21..495c26d 100644
--- a/python/src/com/jetbrains/python/console/PythonDebugLanguageConsoleView.java
+++ b/python/src/com/jetbrains/python/console/PythonDebugLanguageConsoleView.java
@@ -63,7 +63,7 @@
 
   public PythonDebugLanguageConsoleView(final Project project, Sdk sdk, ConsoleView consoleView) {
     super(new CardLayout());
-    myPydevConsoleView = createConsoleView(project, sdk);
+    myPydevConsoleView = new PythonConsoleView(project, "Python Console", sdk);
     myTextConsole = consoleView;
 
     add(myTextConsole.getComponent(), TEXT_CONSOLE_PANEL);
@@ -85,10 +85,6 @@
     getPydevConsoleView().executeCode(code, e);
   }
 
-  private static PythonConsoleView createConsoleView(Project project, Sdk sdk) {
-    return new PythonConsoleView(project, "Python Console", sdk);
-  }
-
   private void doShowConsole(String type) {
     CardLayout cl = (CardLayout)(getLayout());
     cl.show(this, type);
@@ -141,7 +137,7 @@
   }
 
   @Override
-  public void print(String s, ConsoleViewContentType contentType) {
+  public void print(@NotNull String s, @NotNull ConsoleViewContentType contentType) {
     myPydevConsoleView.print(s, contentType);
     myTextConsole.print(s, contentType);
   }
@@ -242,19 +238,23 @@
       myConsole = console;
     }
 
+    @Override
     public boolean isSelected(final AnActionEvent event) {
       return myConsole.isDebugConsole();
     }
 
+    @Override
     public void setSelected(final AnActionEvent event, final boolean flag) {
       myConsole.showDebugConsole(flag);
       ApplicationManager.getApplication().invokeLater(new Runnable() {
+        @Override
         public void run() {
           update(event);
         }
       });
     }
 
+    @Override
     public void update(final AnActionEvent event) {
       super.update(event);
       final Presentation presentation = event.getPresentation();
diff --git a/python/src/com/jetbrains/python/console/RunPythonConsoleAction.java b/python/src/com/jetbrains/python/console/RunPythonConsoleAction.java
index c21288e..9c610e7 100644
--- a/python/src/com/jetbrains/python/console/RunPythonConsoleAction.java
+++ b/python/src/com/jetbrains/python/console/RunPythonConsoleAction.java
@@ -23,7 +23,6 @@
 import com.intellij.openapi.actionSystem.AnActionEvent;
 import com.intellij.openapi.actionSystem.CommonDataKeys;
 import com.intellij.openapi.actionSystem.LangDataKeys;
-import com.intellij.openapi.actionSystem.PlatformDataKeys;
 import com.intellij.openapi.fileEditor.FileDocumentManager;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.module.ModuleManager;
@@ -36,7 +35,7 @@
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.util.PathMappingSettings;
 import com.jetbrains.python.buildout.BuildoutFacet;
-import com.jetbrains.python.remote.PyRemoteSdkData;
+import com.jetbrains.python.remote.PyRemoteSdkCredentials;
 import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
 import com.jetbrains.python.run.PythonCommandLineState;
 import com.jetbrains.python.sdk.PySdkUtil;
@@ -154,7 +153,7 @@
       PythonRemoteInterpreterManager instance = PythonRemoteInterpreterManager.getInstance();
       if (instance != null) {
         mappingSettings =
-          instance.setupMappings(project, (PyRemoteSdkData)sdk.getSdkAdditionalData(), null);
+          instance.setupMappings(project, (PyRemoteSdkCredentials)sdk.getSdkAdditionalData(), null);
       }
     }
     return mappingSettings;
diff --git a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java
index 759bf71..312db25 100644
--- a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java
+++ b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java
@@ -77,6 +77,7 @@
   private final List<PyThreadInfo> mySuspendedThreads = Collections.synchronizedList(Lists.<PyThreadInfo>newArrayList());
   private final Map<String, XValueChildrenList> myStackFrameCache = Maps.newHashMap();
   private final Map<String, PyDebugValue> myNewVariableValue = Maps.newHashMap();
+  private boolean myDownloadSources = false;
 
   private boolean myClosing = false;
 
@@ -185,6 +186,7 @@
     return myPositionConverter;
   }
 
+  @NotNull
   @Override
   public XBreakpointHandler<?>[] getBreakpointHandlers() {
     return myBreakpointHandlers;
@@ -422,8 +424,17 @@
     cleanUp();
   }
 
+  public boolean isDownloadSources() {
+    return myDownloadSources;
+  }
+
+  public void setDownloadSources(boolean downloadSources) {
+    myDownloadSources = downloadSources;
+  }
+
   protected void cleanUp() {
     mySuspendedThreads.clear();
+    myDownloadSources = false;
   }
 
   @Override
@@ -662,7 +673,7 @@
   }
 
   public PyStackFrame createStackFrame(PyStackFrameInfo frameInfo) {
-    return new PyStackFrame(this.getSession().getProject(), this, frameInfo,
+    return new PyStackFrame(getSession().getProject(), this, frameInfo,
                             getPositionConverter().convertFromPython(frameInfo.getPosition()));
   }
 
diff --git a/python/src/com/jetbrains/python/debugger/PyDebugRunner.java b/python/src/com/jetbrains/python/debugger/PyDebugRunner.java
index 7ca044b..3199200 100644
--- a/python/src/com/jetbrains/python/debugger/PyDebugRunner.java
+++ b/python/src/com/jetbrains/python/debugger/PyDebugRunner.java
@@ -19,11 +19,11 @@
 import com.intellij.execution.ExecutionException;
 import com.intellij.execution.ExecutionResult;
 import com.intellij.execution.configurations.*;
+import com.intellij.execution.console.LanguageConsoleBuilder;
 import com.intellij.execution.executors.DefaultDebugExecutor;
 import com.intellij.execution.process.ProcessHandler;
 import com.intellij.execution.runners.ExecutionEnvironment;
 import com.intellij.execution.runners.GenericProgramRunner;
-import com.intellij.execution.runners.LanguageConsoleBuilder;
 import com.intellij.execution.ui.ExecutionConsole;
 import com.intellij.execution.ui.RunContentDescriptor;
 import com.intellij.openapi.application.ApplicationManager;
@@ -40,7 +40,6 @@
 import com.jetbrains.python.console.PythonConsoleView;
 import com.jetbrains.python.console.PythonDebugConsoleCommunication;
 import com.jetbrains.python.console.PythonDebugLanguageConsoleView;
-import com.jetbrains.python.console.pydev.ConsoleCommunication;
 import com.jetbrains.python.run.AbstractPythonRunConfiguration;
 import com.jetbrains.python.run.CommandLinePatcher;
 import com.jetbrains.python.run.PythonCommandLineState;
@@ -58,27 +57,32 @@
 public class PyDebugRunner extends GenericProgramRunner {
   public static final String PY_DEBUG_RUNNER = "PyDebugRunner";
 
+  @SuppressWarnings("SpellCheckingInspection")
   public static final String DEBUGGER_MAIN = "pydev/pydevd.py";
   public static final String CLIENT_PARAM = "--client";
   public static final String PORT_PARAM = "--port";
   public static final String FILE_PARAM = "--file";
   public static final String PYCHARM_PROJECT_ROOTS = "PYCHARM_PROJECT_ROOTS";
+  @SuppressWarnings("SpellCheckingInspection")
   public static final String GEVENT_SUPPORT = "GEVENT_SUPPORT";
 
+  @Override
   @NotNull
   public String getRunnerId() {
     return PY_DEBUG_RUNNER;
   }
 
+  @Override
   public boolean canRun(@NotNull String executorId, @NotNull RunProfile profile) {
     return DefaultDebugExecutor.EXECUTOR_ID.equals(executorId) &&
            profile instanceof AbstractPythonRunConfiguration &&
            ((AbstractPythonRunConfiguration)profile).canRunWithCoverage();
   }
 
-  protected RunContentDescriptor doExecute(final Project project, RunProfileState profileState,
+  @Override
+  protected RunContentDescriptor doExecute(@NotNull final Project project, @NotNull RunProfileState profileState,
                                            RunContentDescriptor contentToReuse,
-                                           ExecutionEnvironment env) throws ExecutionException {
+                                           @NotNull ExecutionEnvironment env) throws ExecutionException {
     FileDocumentManager.getInstance().saveAllDocuments();
 
     final PythonCommandLineState pyState = (PythonCommandLineState)profileState;
@@ -89,6 +93,7 @@
 
     final XDebugSession session = XDebuggerManager.getInstance(project).
       startSession(this, env, contentToReuse, new XDebugProcessStarter() {
+        @Override
         @NotNull
         public XDebugProcess start(@NotNull final XDebugSession session) {
           PyDebugProcess pyDebugProcess =
@@ -117,23 +122,18 @@
                                                                   @NotNull final ExecutionResult result,
                                                                   @NotNull PyDebugProcess debugProcess) {
     ExecutionConsole console = result.getExecutionConsole();
-    ProcessHandler processHandler = result.getProcessHandler();
-
     if (console instanceof PythonDebugLanguageConsoleView) {
       PythonConsoleView pythonConsoleView = ((PythonDebugLanguageConsoleView)console).getPydevConsoleView();
+      pythonConsoleView.setConsoleCommunication(new PythonDebugConsoleCommunication(project, debugProcess));
 
-
-      ConsoleCommunication consoleCommunication = new PythonDebugConsoleCommunication(project, debugProcess);
-      pythonConsoleView.setConsoleCommunication(consoleCommunication);
-
+      ProcessHandler processHandler = result.getProcessHandler();
       PydevDebugConsoleExecuteActionHandler consoleExecuteActionHandler = new PydevDebugConsoleExecuteActionHandler(pythonConsoleView,
                                                                                                                     processHandler,
-                                                                                                                    consoleCommunication);
-
+                                                                                                                    new PythonDebugConsoleCommunication(project, debugProcess));
       pythonConsoleView.setExecutionHandler(consoleExecuteActionHandler);
 
       debugProcess.getSession().addSessionListener(consoleExecuteActionHandler);
-      new LanguageConsoleBuilder().console(pythonConsoleView).processHandler(processHandler).initActions(consoleExecuteActionHandler, "py");
+      new LanguageConsoleBuilder(pythonConsoleView).processHandler(processHandler).initActions(consoleExecuteActionHandler, "py");
     }
   }
 
@@ -156,9 +156,8 @@
                                                              final PythonCommandLineState pyState,
                                                              final int serverLocalPort) {
     return new CommandLinePatcher() {
+      @Override
       public void patchCommandLine(GeneralCommandLine commandLine) {
-
-
         // script name is the last parameter; all other params are for python interpreter; insert just before name
         final ParametersList parametersList = commandLine.getParametersList();
 
@@ -170,11 +169,13 @@
 
         final PythonSdkFlavor flavor = pyState.getSdkFlavor();
         if (flavor != null) {
+          assert exeParams != null;
           for (String option : flavor.getExtraDebugOptions()) {
             exeParams.addParameter(option);
           }
         }
 
+        assert debugParams != null;
         fillDebugParameters(project, debugParams, serverLocalPort, pyState, commandLine);
       }
     };
@@ -187,6 +188,7 @@
                                           @NotNull GeneralCommandLine generalCommandLine) {
     debugParams.addParameter(PythonHelpersLocator.getHelperPath(DEBUGGER_MAIN));
     if (pyState.isMultiprocessDebug()) {
+      //noinspection SpellCheckingInspection
       debugParams.addParameter("--multiproc");
     }
 
diff --git a/python/src/com/jetbrains/python/documentation/DocStringParameterReference.java b/python/src/com/jetbrains/python/documentation/DocStringParameterReference.java
index 9e4e63d..968d413 100644
--- a/python/src/com/jetbrains/python/documentation/DocStringParameterReference.java
+++ b/python/src/com/jetbrains/python/documentation/DocStringParameterReference.java
@@ -17,14 +17,17 @@
 
 import com.google.common.collect.Lists;
 import com.intellij.lang.annotation.HighlightSeverity;
+import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.TextRange;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiReference;
 import com.intellij.psi.PsiReferenceBase;
 import com.intellij.psi.util.PsiTreeUtil;
 import com.intellij.util.ArrayUtil;
+import com.intellij.util.IncorrectOperationException;
 import com.intellij.util.containers.HashSet;
 import com.jetbrains.python.PyNames;
+import com.jetbrains.python.PythonStringUtil;
 import com.jetbrains.python.psi.*;
 import com.jetbrains.python.psi.impl.ParamHelper;
 import com.jetbrains.python.psi.types.TypeEvalContext;
@@ -37,10 +40,10 @@
 /**
  * @author yole
  */
-public class DocStringParameterReference extends PsiReferenceBase<PsiElement> implements PsiReferenceEx {
-  private final String myType;
+public class DocStringParameterReference extends PsiReferenceBase<PyStringLiteralExpression> implements PsiReferenceEx {
+  private final StructuredDocStringBase.ReferenceType myType;
 
-  public DocStringParameterReference(PsiElement element, TextRange range, String refType) {
+  public DocStringParameterReference(PyStringLiteralExpression element, TextRange range, StructuredDocStringBase.ReferenceType refType) {
     super(element, range);
     myType = refType;
   }
@@ -55,26 +58,42 @@
       final PyFunction init = ((PyClass)owner).findMethodByName(PyNames.INIT, false);
       if (init != null) {
         PsiElement element = resolveParameter(init);
-        if (element == null)
-          element = resolveClassVariable(owner);
+        if (element == null && (myType.equals(StructuredDocStringBase.ReferenceType.CLASS_VARIABLE) ||
+                                myType.equals(StructuredDocStringBase.ReferenceType.PARAMETER_TYPE)))
+          element = resolveClassVariable((PyClass)owner);
+        if (element == null && (myType.equals(StructuredDocStringBase.ReferenceType.INSTANCE_VARIABLE) ||
+                                myType.equals(StructuredDocStringBase.ReferenceType.PARAMETER_TYPE)))
+          element = resolveInstanceVariable((PyClass)owner);
         return element;
       }
       else {
-        return resolveClassVariable(owner);
+        if (myType.equals(StructuredDocStringBase.ReferenceType.CLASS_VARIABLE) ||
+                                myType.equals(StructuredDocStringBase.ReferenceType.PARAMETER_TYPE))
+          return resolveClassVariable((PyClass)owner);
+        if (myType.equals(StructuredDocStringBase.ReferenceType.INSTANCE_VARIABLE) ||
+                                myType.equals(StructuredDocStringBase.ReferenceType.PARAMETER_TYPE))
+          return resolveInstanceVariable((PyClass)owner);
       }
     }
     return null;
   }
 
   @Nullable
-  private PsiElement resolveClassVariable(final PyDocStringOwner owner) {
-    final PyStatementList statementList = ((PyClass)owner).getStatementList();
-    for (PsiElement element : statementList.getChildren()) {
-      if (element instanceof PyAssignmentStatement) {
-        final PyExpression[] targets = ((PyAssignmentStatement)element).getTargets();
-        if (targets.length > 0 && targets[0].getText().equals(getCanonicalText()))
-          return targets[0];
-      }
+  private PsiElement resolveInstanceVariable(final PyClass owner) {
+    final List<PyTargetExpression> attributes = owner.getInstanceAttributes();
+    for (PyTargetExpression element : attributes) {
+      if (getCanonicalText().equals(element.getName()))
+        return element;
+    }
+    return null;
+  }
+
+  @Nullable
+  private PsiElement resolveClassVariable(@NotNull final PyClass owner) {
+    final List<PyTargetExpression> attributes = owner.getClassAttributes();
+    for (PyTargetExpression element : attributes) {
+      if (getCanonicalText().equals(element.getName()))
+        return element;
     }
     return null;
   }
@@ -123,7 +142,7 @@
     return ArrayUtil.EMPTY_OBJECT_ARRAY;
   }
   
-  public String getType() {
+  public StructuredDocStringBase.ReferenceType getType() {
     return myType;
   }
 
@@ -143,4 +162,18 @@
     }
     return null;
   }
+
+  @Override
+  public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
+    TextRange range = getRangeInElement();
+    Pair<String, String> quotes = PythonStringUtil.getQuotes(range.substring(myElement.getText()));
+
+    if (quotes != null) {
+      range = TextRange.create(range.getStartOffset() + quotes.first.length(), range.getEndOffset() - quotes.second.length());
+    }
+
+    String newName = range.replace(myElement.getText(), newElementName);
+    myElement.updateText(newName);
+    return myElement;
+  }
 }
diff --git a/python/src/com/jetbrains/python/documentation/DocStringReferenceProvider.java b/python/src/com/jetbrains/python/documentation/DocStringReferenceProvider.java
index 2891726..9eea80c 100644
--- a/python/src/com/jetbrains/python/documentation/DocStringReferenceProvider.java
+++ b/python/src/com/jetbrains/python/documentation/DocStringReferenceProvider.java
@@ -59,18 +59,24 @@
         // XXX: It does not work with multielement docstrings
         StructuredDocString docString = DocStringUtil.parse(text);
         if (docString != null) {
-          result.addAll(referencesFromNames(element, offset, docString,
+          result.addAll(referencesFromNames(expr, offset, docString,
                                             docString.getTagArguments(StructuredDocStringBase.PARAM_TAGS),
-                                            StructuredDocStringBase.PARAMETER));
-          result.addAll(referencesFromNames(element, offset, docString,
+                                            StructuredDocStringBase.ReferenceType.PARAMETER));
+          result.addAll(referencesFromNames(expr, offset, docString,
                                             docString.getTagArguments(StructuredDocStringBase.PARAM_TYPE_TAGS),
-                                            StructuredDocStringBase.PARAMETER_TYPE));
-          result.addAll(referencesFromNames(element, offset, docString,
-                                            docString.getKeywordArgumentSubstrings(), StructuredDocStringBase.KEYWORD));
+                                            StructuredDocStringBase.ReferenceType.PARAMETER_TYPE));
+          result.addAll(referencesFromNames(expr, offset, docString,
+                                            docString.getKeywordArgumentSubstrings(), StructuredDocStringBase.ReferenceType.KEYWORD));
 
-          result.addAll(referencesFromNames(element, offset, docString,
-                                            docString.getTagArguments(StructuredDocStringBase.VARIABLE_TAGS),
-                                            StructuredDocStringBase.VARIABLE));
+          result.addAll(referencesFromNames(expr, offset, docString,
+                                            docString.getTagArguments("var"),
+                                            StructuredDocStringBase.ReferenceType.VARIABLE));
+          result.addAll(referencesFromNames(expr, offset, docString,
+                                            docString.getTagArguments("cvar"),
+                                            StructuredDocStringBase.ReferenceType.CLASS_VARIABLE));
+          result.addAll(referencesFromNames(expr, offset, docString,
+                                            docString.getTagArguments("ivar"),
+                                            StructuredDocStringBase.ReferenceType.INSTANCE_VARIABLE));
           result.addAll(returnTypes(element, docString, offset));
         }
         return result.toArray(new PsiReference[result.size()]);
@@ -90,11 +96,11 @@
     }
     return result;
   }
-  private static List<PsiReference> referencesFromNames(PsiElement element,
+  private static List<PsiReference> referencesFromNames(PyStringLiteralExpression element,
                                                         int offset,
                                                         StructuredDocString docString,
                                                         List<Substring> paramNames,
-                                                        String refType) {
+                                                        StructuredDocStringBase.ReferenceType refType) {
     List<PsiReference> result = new ArrayList<PsiReference>();
     for (Substring name : paramNames) {
       final String s = name.toString();
@@ -102,7 +108,7 @@
         final TextRange range = name.getTextRange().shiftRight(offset);
         result.add(new DocStringParameterReference(element, range, refType));
       }
-      if (refType.equals(StructuredDocStringBase.PARAMETER_TYPE)) {
+      if (refType.equals(StructuredDocStringBase.ReferenceType.PARAMETER_TYPE)) {
         final Substring type = docString.getParamTypeSubstring(s);
         if (type != null) {
           result.addAll(parseTypeReferences(element, type, offset));
diff --git a/python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java b/python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java
index d7358f1..d09af58 100644
--- a/python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java
+++ b/python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java
@@ -222,8 +222,9 @@
       }
       replacementText.append(line);
     }
-    if (replacementText.length() > 0)
-      replacementText.deleteCharAt(replacementText.length()-1);
+    if (replacementText.length() > 0) {
+      replacementText.deleteCharAt(replacementText.length() - 1);
+    }
     addParams(replacementText, false, paramsToAdd);
     for (int i = ind; i != lines.length; ++i) {
       String line = lines[i];
@@ -250,7 +251,7 @@
     final Module module = ModuleUtilCore.findModuleForPsiElement(myDocStringOwner);
     if (module != null) {
       PyDocumentationSettings documentationSettings = PyDocumentationSettings.getInstance(module);
-      if (documentationSettings.isPlain(getFile())) return replacementText.length()-1;
+      if (documentationSettings.isPlain(getFile())) return replacementText.length() - 1;
     }
 
     int i = 0;
@@ -418,7 +419,8 @@
                                                             "def " + myFunction.getName() + myFunction.getParameterList().getText()
                                                             + ":\n" + StringUtil.repeat(" ", getIndentSize(myFunction))
                                                             + replacement + "\n" +
-                                                            StringUtil.repeat(" ", getIndentSize(myFunction)) + list.getText());
+                                                            StringUtil.repeat(" ", getIndentSize(myFunction)) + list.getText()
+          );
 
           myFunction = (PyFunction)myFunction.replace(func);
         }
@@ -429,7 +431,9 @@
       }
 
       myFunction = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(myFunction);
-      myDocStringExpression = myFunction.getDocStringExpression();
+      if (myFunction != null) {
+        myDocStringExpression = myFunction.getDocStringExpression();
+      }
     }
   }
 
diff --git a/python/src/com/jetbrains/python/documentation/PyDocumentationBuilder.java b/python/src/com/jetbrains/python/documentation/PyDocumentationBuilder.java
index ec4becc..4c3432c 100644
--- a/python/src/com/jetbrains/python/documentation/PyDocumentationBuilder.java
+++ b/python/src/com/jetbrains/python/documentation/PyDocumentationBuilder.java
@@ -95,7 +95,7 @@
             PyType type = context.getType(qual);
             if (type instanceof PyClassType) {
               cls = ((PyClassType)type).getPyClass();
-              Property property = cls.findProperty(elementName);
+              Property property = cls.findProperty(elementName, true);
               if (property != null) {
                 is_property = true;
                 final AccessDirection dir = AccessDirection.of((PyElement)outer);
diff --git a/python/src/com/jetbrains/python/documentation/PyDocumentationSettings.java b/python/src/com/jetbrains/python/documentation/PyDocumentationSettings.java
index edde7f2..6ea6540 100644
--- a/python/src/com/jetbrains/python/documentation/PyDocumentationSettings.java
+++ b/python/src/com/jetbrains/python/documentation/PyDocumentationSettings.java
@@ -37,7 +37,7 @@
        storages = {@Storage(file = "$MODULE_FILE$")}
 )
 public class PyDocumentationSettings implements PersistentStateComponent<PyDocumentationSettings> {
-  public String myDocStringFormat = "";
+  public String myDocStringFormat = DocStringFormat.REST;
   public boolean analyzeDoctest = true;
 
   public boolean isEpydocFormat(PsiFile file) {
diff --git a/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java b/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java
index 3a504aa..55bc1e4 100644
--- a/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java
+++ b/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java
@@ -487,7 +487,7 @@
                                                ".\nWould you like to configure it now?",
                                                "Python External Documentation",
                                                Messages.getQuestionIcon());
-          if (rc == 0) {
+          if (rc == Messages.OK) {
             ShowSettingsUtilImpl.showSettingsDialog(project, PythonDocumentationConfigurable.ID, "");
           }
         }
diff --git a/python/src/com/jetbrains/python/documentation/StructuredDocStringBase.java b/python/src/com/jetbrains/python/documentation/StructuredDocStringBase.java
index 0e3d9e3..9f6cecb 100644
--- a/python/src/com/jetbrains/python/documentation/StructuredDocStringBase.java
+++ b/python/src/com/jetbrains/python/documentation/StructuredDocStringBase.java
@@ -50,11 +50,9 @@
   public static String[] RAISES_TAGS = new String[] { "raises", "raise", "except", "exception" };
   public static String[] RETURN_TAGS = new String[] { "return", "returns" };
 
-  public static String PARAMETER = "parameter";
+  public enum ReferenceType {PARAMETER, PARAMETER_TYPE, KEYWORD, VARIABLE, CLASS_VARIABLE, INSTANCE_VARIABLE}
+
   public static String TYPE = "type";
-  public static String PARAMETER_TYPE = "parameter_type";
-  public static String KEYWORD = "keyword";
-  public static String VARIABLE = "variable";
 
   protected StructuredDocStringBase(@NotNull String docStringText, String tagPrefix) {
     final Substring docString = new Substring(docStringText);
diff --git a/python/src/com/jetbrains/python/documentation/doctest/PyDocReferenceExpression.java b/python/src/com/jetbrains/python/documentation/doctest/PyDocReferenceExpression.java
index 1199583..62356d2 100644
--- a/python/src/com/jetbrains/python/documentation/doctest/PyDocReferenceExpression.java
+++ b/python/src/com/jetbrains/python/documentation/doctest/PyDocReferenceExpression.java
@@ -19,7 +19,6 @@
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiPolyVariantReference;
 import com.intellij.psi.util.PsiTreeUtil;
-import com.jetbrains.python.psi.PyExpression;
 import com.jetbrains.python.psi.PyFromImportStatement;
 import com.jetbrains.python.psi.PyImportElement;
 import com.jetbrains.python.psi.impl.PyReferenceExpressionImpl;
@@ -40,8 +39,7 @@
 
   @NotNull
   public PsiPolyVariantReference getReference(PyResolveContext context) {
-    final PyExpression qualifier = getQualifier();
-    if (qualifier != null) {
+    if (isQualified()) {
       return new PyQualifiedReference(this, context);
     }
     final PsiElement importParent = PsiTreeUtil.getParentOfType(this, PyImportElement.class, PyFromImportStatement.class);
diff --git a/python/src/com/jetbrains/python/editor/PythonEnterHandler.java b/python/src/com/jetbrains/python/editor/PythonEnterHandler.java
index c29817a..c055606 100644
--- a/python/src/com/jetbrains/python/editor/PythonEnterHandler.java
+++ b/python/src/com/jetbrains/python/editor/PythonEnterHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
 import com.intellij.codeInsight.editorActions.enter.EnterHandlerDelegateAdapter;
 import com.intellij.ide.DataManager;
 import com.intellij.injected.editor.EditorWindow;
+import com.intellij.lang.ASTNode;
 import com.intellij.lang.injection.InjectedLanguageManager;
 import com.intellij.openapi.actionSystem.DataContext;
 import com.intellij.openapi.editor.Document;
@@ -29,6 +30,7 @@
 import com.intellij.openapi.util.Ref;
 import com.intellij.openapi.util.TextRange;
 import com.intellij.psi.*;
+import com.intellij.psi.impl.source.tree.TreeUtil;
 import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
 import com.intellij.psi.util.PsiTreeUtil;
 import com.jetbrains.python.PyTokenTypes;
@@ -67,6 +69,7 @@
     PyFunction.class,
     PySliceExpression.class,
     PySubscriptionExpression.class,
+    PyGeneratorExpression.class
   };
 
   @Override
@@ -192,8 +195,17 @@
         return false;
       }
     }
-    PsiElement statementBefore = findStatementBeforeCaret(file, offset);
-    PsiElement statementAfter = findStatementAfterCaret(file, offset);
+    PsiElement atCaret = file.findElementAt(offset);
+    if (atCaret == null) {
+      return false;
+    }
+    ASTNode nodeAtCaret = atCaret.getNode();
+    return needInsertBackslash(nodeAtCaret, autoWrapInProgress);
+  }
+
+  public static boolean needInsertBackslash(ASTNode nodeAtCaret, boolean autoWrapInProgress) {
+    PsiElement statementBefore = findStatementBeforeCaret(nodeAtCaret);
+    PsiElement statementAfter = findStatementAfterCaret(nodeAtCaret);
     if (statementBefore != statementAfter) {  // Enter pressed at statement break
       return false;
     }
@@ -209,12 +221,12 @@
       // if we're in middle of typing, it's expected that we will have error elements
     }
 
-    if (inFromImportParentheses(statementBefore, offset)) {
+    if (inFromImportParentheses(statementBefore, nodeAtCaret.getTextRange().getStartOffset())) {
       return false;
     }
 
-    PsiElement wrappableBefore = findWrappable(file, offset, true);
-    PsiElement wrappableAfter = findWrappable(file, offset, false);
+    PsiElement wrappableBefore = findWrappable(nodeAtCaret, true);
+    PsiElement wrappableAfter = findWrappable(nodeAtCaret, false);
     if (!(wrappableBefore instanceof PsiComment)) {
       while (wrappableBefore != null) {
         PsiElement next = PsiTreeUtil.getParentOfType(wrappableBefore, WRAPPABLE_CLASSES);
@@ -258,14 +270,14 @@
   }
 
   @Nullable
-  private static PsiElement findWrappable(PsiFile file, int offset, boolean before) {
+  private static PsiElement findWrappable(ASTNode nodeAtCaret, boolean before) {
     PsiElement wrappable = before
-                                 ? findBeforeCaret(file, offset, WRAPPABLE_CLASSES)
-                                 : findAfterCaret(file, offset, WRAPPABLE_CLASSES);
+                                 ? findBeforeCaret(nodeAtCaret, WRAPPABLE_CLASSES)
+                                 : findAfterCaret(nodeAtCaret, WRAPPABLE_CLASSES);
     if (wrappable == null) {
       PsiElement emptyTuple = before
-                              ? findBeforeCaret(file, offset, PyTupleExpression.class)
-                              : findAfterCaret(file, offset, PyTupleExpression.class);
+                              ? findBeforeCaret(nodeAtCaret, PyTupleExpression.class)
+                              : findAfterCaret(nodeAtCaret, PyTupleExpression.class);
       if (emptyTuple != null && emptyTuple.getNode().getFirstChildNode().getElementType() == PyTokenTypes.LPAR) {
         wrappable = emptyTuple;
       }
@@ -274,35 +286,31 @@
   }
 
   @Nullable
-  private static PsiElement findStatementBeforeCaret(PsiFile file, int offset) {
-    return findBeforeCaret(file, offset, PyStatement.class);
+  private static PsiElement findStatementBeforeCaret(ASTNode node) {
+    return findBeforeCaret(node, PyStatement.class);
   }
 
   @Nullable
-  private static PsiElement findStatementAfterCaret(PsiFile file, int offset) {
-    return findAfterCaret(file, offset, PyStatement.class);
+  private static PsiElement findStatementAfterCaret(ASTNode node) {
+    return findAfterCaret(node, PyStatement.class);
   }
 
-  @Nullable
-  private static PsiElement findBeforeCaret(PsiFile file, int offset, Class<? extends PsiElement>... classes) {
-    while(offset > 0) {
-      offset--;
-      final PsiElement element = file.findElementAt(offset);
-      if (element != null && !(element instanceof PsiWhiteSpace)) {
-        return getNonStrictParentOfType(element, classes);
+  private static PsiElement findBeforeCaret(ASTNode atCaret, Class<? extends PsiElement>... classes) {
+    while (atCaret != null) {
+      atCaret = TreeUtil.prevLeaf(atCaret);
+      if (atCaret != null && atCaret.getElementType() != TokenType.WHITE_SPACE) {
+        return getNonStrictParentOfType(atCaret.getPsi(), classes);
       }
     }
     return null;
   }
 
-  @Nullable
-  private static PsiElement findAfterCaret(PsiFile file, int offset, Class<? extends PsiElement>... classes) {
-    while(offset < file.getTextLength()) {
-      final PsiElement element = file.findElementAt(offset);
-      if (element != null && !(element instanceof PsiWhiteSpace)) {
-        return getNonStrictParentOfType(element, classes);
+  private static PsiElement findAfterCaret(ASTNode atCaret, Class<? extends PsiElement>... classes) {
+    while (atCaret != null) {
+      if (atCaret.getElementType() != TokenType.WHITE_SPACE) {
+        return getNonStrictParentOfType(atCaret.getPsi(), classes);
       }
-      offset++;
+      atCaret = TreeUtil.nextLeaf(atCaret);
     }
     return null;
   }
diff --git a/python/src/com/jetbrains/python/editor/selectWord/PyListSelectionHandler.java b/python/src/com/jetbrains/python/editor/selectWord/PyListSelectionHandler.java
index a45c8c2..b394203 100644
--- a/python/src/com/jetbrains/python/editor/selectWord/PyListSelectionHandler.java
+++ b/python/src/com/jetbrains/python/editor/selectWord/PyListSelectionHandler.java
@@ -19,6 +19,7 @@
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.util.TextRange;
 import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiErrorElement;
 import com.intellij.psi.PsiWhiteSpace;
 import com.jetbrains.python.psi.PyArgumentList;
 import com.jetbrains.python.psi.PyListLiteralExpression;
@@ -42,6 +43,9 @@
   public List<TextRange> select(PsiElement e, CharSequence editorText, int cursorOffset, Editor editor) {
     TextRange stringRange = e.getTextRange();
     PsiElement firstChild = e.getFirstChild().getNextSibling();
+    if (firstChild instanceof PsiErrorElement) {
+      return Collections.emptyList();
+    }
     int startShift = 1;
     if (firstChild instanceof PsiWhiteSpace)
       startShift += firstChild.getTextLength();
@@ -50,7 +54,7 @@
     if (lastChild instanceof PsiWhiteSpace)
       endShift += lastChild.getTextLength();
 
-    TextRange offsetRange = new TextRange(stringRange.getStartOffset() + startShift, stringRange.getEndOffset() - endShift );
+    final TextRange offsetRange = new TextRange(stringRange.getStartOffset() + startShift, stringRange.getEndOffset() - endShift);
     if (offsetRange.contains(cursorOffset) && offsetRange.getLength() > 1) {
       return Collections.singletonList(offsetRange);
     }
diff --git a/python/src/com/jetbrains/python/findUsages/PyFindUsagesHandlerFactory.java b/python/src/com/jetbrains/python/findUsages/PyFindUsagesHandlerFactory.java
index efc1e68..78d9d65 100644
--- a/python/src/com/jetbrains/python/findUsages/PyFindUsagesHandlerFactory.java
+++ b/python/src/com/jetbrains/python/findUsages/PyFindUsagesHandlerFactory.java
@@ -71,13 +71,13 @@
                                                                           " overrides method of class " +
                                                                           ((PyFunction)next).getContainingClass().getName() +
                                                                           ".\nDo you want to find usages of the base method?",  "Find Usages", Messages.getQuestionIcon());
-            if (rc == 0) {
+            if (rc == Messages.YES) {
               List<PsiElement> allMethods = new ArrayList<PsiElement>();
               allMethods.add(element);
               allMethods.addAll(superMethods);
               return new PyFunctionFindUsagesHandler(element, allMethods);
             }
-            if (rc == 1) {
+            if (rc == Messages.NO) {
               return new PyFunctionFindUsagesHandler(element);
             }
             return FindUsagesHandler.NULL_HANDLER;
diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java
index 6d343e2..0c535c1 100644
--- a/python/src/com/jetbrains/python/formatter/PyBlock.java
+++ b/python/src/com/jetbrains/python/formatter/PyBlock.java
@@ -141,39 +141,65 @@
     Wrap wrap = null;
     Indent childIndent = Indent.getNoneIndent();
     Alignment childAlignment = null;
+
+    if (parentType == PyElementTypes.BINARY_EXPRESSION && !isInControlStatement()) {
+      //Setup alignments for binary expression
+      childAlignment = getAlignmentForChildren();
+
+      PyBlock p = myParent; //Check grandparents
+      while (p != null) {
+        ASTNode pNode = p.getNode();
+        if (ourListElementTypes.contains(pNode.getElementType())) {
+          if (needListAlignment(child) && !isEmptyList(_node.getPsi())) {
+
+            childAlignment = p.getChildAlignment();
+            break;
+          }
+        }
+        else if (pNode == PyElementTypes.BINARY_EXPRESSION) {
+          childAlignment = p.getChildAlignment();
+        }
+        if (!breaksAlignment(pNode.getElementType())) {
+          p = p.myParent;
+        }
+        else {
+          break;
+        }
+      }
+    }
+
     if (childType == PyElementTypes.STATEMENT_LIST) {
       if (hasLineBreaksBefore(child, 1) || needLineBreakInStatement()) {
         childIndent = Indent.getNormalIndent();
       }
     }
-    else if (childType == PyElementTypes.IMPORT_ELEMENT && hasLineBreaksBefore(child, 1)) {
+    else if (childType == PyElementTypes.IMPORT_ELEMENT) {
+      wrap = Wrap.createWrap(WrapType.NORMAL, true);
       childIndent = Indent.getNormalIndent();
     }
     if (ourListElementTypes.contains(parentType)) {
       // wrapping in non-parenthesized tuple expression is not allowed (PY-1792)
       if ((parentType != PyElementTypes.TUPLE_EXPRESSION || grandparentType == PyElementTypes.PARENTHESIZED_EXPRESSION) &&
-          !ourBrackets.contains(childType) && childType != PyTokenTypes.COMMA && !isSliceOperand(child) /*&& !isSubscriptionOperand(child)*/) {
+          !ourBrackets.contains(childType) &&
+          childType != PyTokenTypes.COMMA &&
+          !isSliceOperand(child) /*&& !isSubscriptionOperand(child)*/) {
         wrap = Wrap.createWrap(WrapType.NORMAL, true);
       }
       if (needListAlignment(child) && !isEmptyList(_node.getPsi())) {
         childAlignment = getAlignmentForChildren();
       }
+      if (childType == PyTokenTypes.END_OF_LINE_COMMENT) {
+        childIndent = Indent.getNormalIndent();
+      }
     }
     else if (parentType == PyElementTypes.BINARY_EXPRESSION &&
-             (PythonDialectsTokenSetProvider.INSTANCE.getExpressionTokens().contains(childType) || PyTokenTypes.OPERATIONS.contains(childType))) {
-      if (isInControlStatement() ) {
+             (PythonDialectsTokenSetProvider.INSTANCE.getExpressionTokens().contains(childType) ||
+              PyTokenTypes.OPERATIONS.contains(childType))) {
+      if (isInControlStatement()) {
         PyParenthesizedExpression parens = PsiTreeUtil.getParentOfType(_node.getPsi(), PyParenthesizedExpression.class, true,
                                                                        PyStatementPart.class);
         childIndent = parens != null ? Indent.getNormalIndent() : Indent.getContinuationIndent();
       }
-      else {
-        if (grandparentType == PyElementTypes.BINARY_EXPRESSION && myParent != null) {
-          childAlignment = myParent.getAlignmentForChildren();
-        }
-        else {
-          childAlignment = getAlignmentForChildren();
-        }
-      }
     }
 
     if (parentType == PyElementTypes.LIST_LITERAL_EXPRESSION || parentType == PyElementTypes.LIST_COMP_EXPRESSION) {
@@ -184,18 +210,8 @@
         childIndent = Indent.getNormalIndent();
       }
     }
-    else if (parentType == PyElementTypes.ARGUMENT_LIST || parentType == PyElementTypes.PARAMETER_LIST) {
-      if (childType == PyTokenTypes.RPAR) {
-        childIndent = Indent.getNoneIndent();
-      }
-      else {
-        childIndent = parentType == PyElementTypes.PARAMETER_LIST || isInControlStatement()
-                      ? Indent.getContinuationIndent()
-                      : Indent.getNormalIndent(/*true*/);
-      }
-    }
     else if (parentType == PyElementTypes.DICT_LITERAL_EXPRESSION || parentType == PyElementTypes.SET_LITERAL_EXPRESSION ||
-      parentType == PyElementTypes.SET_COMP_EXPRESSION || parentType == PyElementTypes.DICT_COMP_EXPRESSION) {
+             parentType == PyElementTypes.SET_COMP_EXPRESSION || parentType == PyElementTypes.DICT_COMP_EXPRESSION) {
       if (childType == PyTokenTypes.RBRACE || !hasLineBreaksBefore(child, 1)) {
         childIndent = Indent.getNoneIndent();
       }
@@ -209,28 +225,60 @@
       }
     }
     else if (parentType == PyElementTypes.FROM_IMPORT_STATEMENT) {
-      if ((childType == PyElementTypes.IMPORT_ELEMENT || childType == PyTokenTypes.RPAR) &&
-          _node.findChildByType(PyTokenTypes.LPAR) != null) {
-        if (myContext.getPySettings().ALIGN_MULTILINE_IMPORTS) {
-          childAlignment = getAlignmentForChildren();
+      if (_node.findChildByType(PyTokenTypes.LPAR) != null) {
+        if (childType == PyElementTypes.IMPORT_ELEMENT) {
+          if (myContext.getPySettings().ALIGN_MULTILINE_IMPORTS) {
+            childAlignment = getAlignmentForChildren();
+          }
+          else {
+            childIndent = Indent.getNormalIndent();
+          }
         }
-        else {
-          childIndent = Indent.getNormalIndent();
+        if (childType == PyTokenTypes.RPAR) {
+          childIndent = Indent.getNoneIndent();
         }
       }
     }
     else if (parentType == PyElementTypes.KEY_VALUE_EXPRESSION) {
-      PyKeyValueExpression keyValue = (PyKeyValueExpression) _node.getPsi();
+      PyKeyValueExpression keyValue = (PyKeyValueExpression)_node.getPsi();
       if (keyValue != null && child.getPsi() == keyValue.getValue()) {
         childIndent = Indent.getNormalIndent();
       }
     }
-    else if (parentType == PyElementTypes.PARENTHESIZED_EXPRESSION || parentType == PyElementTypes.GENERATOR_EXPRESSION) {
+    //Align elements vertically if there is an argument in the first line of parenthesized expression
+    else if (((parentType == PyElementTypes.PARENTHESIZED_EXPRESSION && myContext.getSettings().ALIGN_MULTILINE_PARENTHESIZED_EXPRESSION)
+              || (parentType == PyElementTypes.ARGUMENT_LIST && myContext.getSettings().ALIGN_MULTILINE_PARAMETERS_IN_CALLS)
+              || (parentType == PyElementTypes.PARAMETER_LIST && myContext.getSettings().ALIGN_MULTILINE_PARAMETERS)) &&
+             !isIndentNext(child) &&
+             !hasLineBreaksBefore(_node.getFirstChildNode(), 1)
+             && !ourListElementTypes.contains(childType)) {
+
+      if (!ourBrackets.contains(childType)) {
+        childAlignment = getAlignmentForChildren();
+        if (parentType != PyElementTypes.CALL_EXPRESSION) {
+          childIndent = Indent.getNormalIndent();
+        }
+      }
+      else if (childType == PyTokenTypes.RPAR) {
+        childIndent = Indent.getNoneIndent();
+      }
+    }
+    else if (parentType == PyElementTypes.GENERATOR_EXPRESSION || parentType == PyElementTypes.PARENTHESIZED_EXPRESSION) {
       if (childType == PyTokenTypes.RPAR || !hasLineBreaksBefore(child, 1)) {
         childIndent = Indent.getNoneIndent();
       }
       else {
-        childIndent = Indent.getNormalIndent();
+        childIndent = isIndentNext(child) ? Indent.getContinuationIndent() : Indent.getNormalIndent();
+      }
+    }
+    else if (parentType == PyElementTypes.ARGUMENT_LIST || parentType == PyElementTypes.PARAMETER_LIST) {
+      if (childType == PyTokenTypes.RPAR) {
+        childIndent = Indent.getNoneIndent();
+      }
+      else {
+        childIndent = parentType == PyElementTypes.PARAMETER_LIST || isInControlStatement()
+                      ? Indent.getContinuationIndent()
+                      : Indent.getNormalIndent(/*true*/);
       }
     }
     else if (parentType == PyElementTypes.SUBSCRIPTION_EXPRESSION) {
@@ -239,8 +287,31 @@
         childIndent = Indent.getNormalIndent();
       }
     }
+    else if (parentType == PyElementTypes.REFERENCE_EXPRESSION) {
+      if (child != _node.getFirstChildNode()) {
+        childIndent = Indent.getNormalIndent();
+        if (hasLineBreaksBefore(child, 1)) {
+          if (isInControlStatement()) {
+            childIndent = Indent.getContinuationIndent();
+          }
+          else {
+            PyBlock b = myParent;
+            while (b != null) {
+              if (b.getNode().getPsi() instanceof PyParenthesizedExpression ||
+                  b.getNode().getPsi() instanceof PyArgumentList ||
+                  b.getNode().getPsi() instanceof PyParameterList) {
+                childAlignment = getAlignmentOfChild(b, 1);
+                break;
+              }
+              b = b.myParent;
+            }
+          }
+        }
+      }
+    }
 
-    if (isAfterStatementList(child) && !hasLineBreaksBefore(child, 2)) {  // maybe enter was pressed and cut us from a previous (nested) statement list
+    if (isAfterStatementList(child) && !hasLineBreaksBefore(child, 2) && child.getElementType() != PyTokenTypes.END_OF_LINE_COMMENT) {
+      // maybe enter was pressed and cut us from a previous (nested) statement list
       childIndent = Indent.getNormalIndent();
     }
 
@@ -248,7 +319,7 @@
     while (prev != null && prev.getElementType() == TokenType.WHITE_SPACE) {
       if (prev.getText().contains("\\") && !childIndent.equals(Indent.getContinuationIndent()) &&
           !childIndent.equals(Indent.getContinuationIndent(true))) {
-        childIndent = Indent.getNormalIndent();
+        childIndent = isIndentNext(child) ? Indent.getContinuationIndent() : Indent.getNormalIndent();
         break;
       }
       prev = prev.getTreePrev();
@@ -257,9 +328,35 @@
     return new PyBlock(this, child, childAlignment, childIndent, wrap, myContext);
   }
 
+  private static boolean breaksAlignment(IElementType type) {
+    return type != PyElementTypes.BINARY_EXPRESSION;
+  }
+
+  private static Alignment getAlignmentOfChild(PyBlock b, int childNum) {
+    if (b.getSubBlocks().size() > childNum) {
+      ChildAttributes attributes = b.getChildAttributes(childNum);
+      return attributes.getAlignment();
+    }
+    return null;
+  }
+
+  private static boolean isIndentNext(ASTNode child) {
+    PsiElement psi = PsiTreeUtil.getParentOfType(child.getPsi(), PyStatement.class);
+
+    return psi instanceof PyIfStatement ||
+           psi instanceof PyForStatement ||
+           psi instanceof PyWithStatement ||
+           psi instanceof PyClass ||
+           psi instanceof PyFunction ||
+           psi instanceof PyTryExceptStatement ||
+           psi instanceof PyElsePart ||
+           psi instanceof PyIfPart ||
+           psi instanceof PyWhileStatement;
+  }
+
   private static boolean isSubscriptionOperand(ASTNode child) {
     return child.getTreeParent().getElementType() == PyElementTypes.SUBSCRIPTION_EXPRESSION &&
-           child.getPsi() == ((PySubscriptionExpression) child.getTreeParent().getPsi()).getOperand();
+           child.getPsi() == ((PySubscriptionExpression)child.getTreeParent().getPsi()).getOperand();
   }
 
   private boolean isInControlStatement() {
@@ -278,10 +375,10 @@
 
   private static boolean isEmptyList(PsiElement psi) {
     if (psi instanceof PyDictLiteralExpression) {
-      return ((PyDictLiteralExpression) psi).getElements().length == 0;
+      return ((PyDictLiteralExpression)psi).getElements().length == 0;
     }
     if (psi instanceof PySequenceExpression) {
-      return ((PySequenceExpression) psi).getElements().length == 0;
+      return ((PySequenceExpression)psi).getElements().length == 0;
     }
     return false;
   }
@@ -315,7 +412,9 @@
     }
     if (PyTokenTypes.CLOSE_BRACES.contains(childType)) {
       ASTNode prevNonSpace = findPrevNonSpaceNode(child);
-      if (prevNonSpace != null && prevNonSpace.getElementType() == PyTokenTypes.COMMA && myContext.getMode() == FormattingMode.ADJUST_INDENT) {
+      if (prevNonSpace != null &&
+          prevNonSpace.getElementType() == PyTokenTypes.COMMA &&
+          myContext.getMode() == FormattingMode.ADJUST_INDENT) {
         return true;
       }
       return false;
@@ -351,7 +450,8 @@
   private static ASTNode findPrevNonSpaceNode(ASTNode node) {
     do {
       node = node.getTreePrev();
-    } while(node != null && (node.getElementType() == TokenType.WHITE_SPACE || PyTokenTypes.WHITESPACE.contains(node.getElementType())));
+    }
+    while (node != null && (node.getElementType() == TokenType.WHITE_SPACE || PyTokenTypes.WHITESPACE.contains(node.getElementType())));
     return node;
   }
 
@@ -365,7 +465,7 @@
     if (node != null && node.getElementType() == TokenType.WHITE_SPACE) {
       String prevNodeText = node.getText();
       int count = 0;
-      for(int i=0; i<prevNodeText.length(); i++) {
+      for (int i = 0; i < prevNodeText.length(); i++) {
         if (prevNodeText.charAt(i) == '\n') {
           count++;
           if (count == minCount) {
@@ -422,10 +522,36 @@
           return Spacing.createSpacing(0, 0, 1, true, myContext.getSettings().KEEP_BLANK_LINES_IN_CODE);
         }
       }
+
+      if ((node1.getElementType() == PyElementTypes.FUNCTION_DECLARATION || node1.getElementType() == PyElementTypes.CLASS_DECLARATION)
+          && _node.getElementType() instanceof PyFileElementType) {
+
+        if (psi2 instanceof PsiComment) {
+          final PsiElement psi3 = PsiTreeUtil.getNextSiblingOfType(psi2, PyElement.class);
+
+          if (psi3 != null) {
+            IElementType type3 = psi3.getNode().getElementType();
+
+            if (type3 == PyElementTypes.CLASS_DECLARATION || type3 == PyElementTypes.FUNCTION_DECLARATION) {
+              return getBlankLinesForOption(myContext.getPySettings().BLANK_LINES_AROUND_TOP_LEVEL_CLASSES_FUNCTIONS);
+            }
+          }
+        }
+      }
+
+      if (psi2 instanceof PsiComment && !hasLineBreaksBefore(psi2.getNode(), 1) && myContext.getPySettings().SPACE_BEFORE_NUMBER_SIGN) {
+        return Spacing.createSpacing(2, 0, 0, false, 0);
+      }
     }
     return myContext.getSpacingBuilder().getSpacing(this, child1, child2);
   }
 
+  private Spacing getBlankLinesForOption(final int option) {
+    int blankLines = option + 1;
+    return Spacing
+      .createSpacing(0, 0, blankLines, myContext.getSettings().KEEP_LINE_BREAKS, myContext.getSettings().KEEP_BLANK_LINES_IN_DECLARATIONS);
+  }
+
   private boolean needLineBreakInStatement() {
     PyStatement statement = PsiTreeUtil.getParentOfType(_node.getPsi(), PyStatement.class);
     if (statement != null) {
@@ -607,7 +733,7 @@
     }
 
     if (afterNode != null && afterNode.getElementType() == PyElementTypes.KEY_VALUE_EXPRESSION) {
-      PyKeyValueExpression keyValue = (PyKeyValueExpression) afterNode.getPsi();
+      PyKeyValueExpression keyValue = (PyKeyValueExpression)afterNode.getPsi();
       if (keyValue != null && keyValue.getValue() == null) {  // incomplete
         return Indent.getContinuationIndent();
       }
diff --git a/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java b/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java
index 961ca72..88ddd41 100644
--- a/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java
+++ b/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java
@@ -38,6 +38,9 @@
   public boolean NEW_LINE_AFTER_COLON = false;
   public boolean NEW_LINE_AFTER_COLON_MULTI_CLAUSE = true;
 
+  public boolean SPACE_AFTER_NUMBER_SIGN = true;
+  public boolean SPACE_BEFORE_NUMBER_SIGN = true;
+
   public PyCodeStyleSettings(CodeStyleSettings container) {
     super("Python", container);
   }
diff --git a/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java b/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java
index c26a1a1..9e7f97d 100644
--- a/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java
+++ b/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java
@@ -72,9 +72,12 @@
       consumer.showCustomOption(PyCodeStyleSettings.class, "SPACE_AROUND_EQ_IN_KEYWORD_ARGUMENT", "Around = in keyword argument",
                                 SPACES_AROUND_OPERATORS);
       consumer.showCustomOption(PyCodeStyleSettings.class, "SPACE_WITHIN_BRACES", "Braces", SPACES_WITHIN);
-      consumer.showCustomOption(PyCodeStyleSettings.class, "SPACE_BEFORE_PY_COLON", ApplicationBundle.message("checkbox.spaces.before.colon"), SPACES_OTHER);
+      consumer.showCustomOption(PyCodeStyleSettings.class, "SPACE_BEFORE_PY_COLON",
+                                ApplicationBundle.message("checkbox.spaces.before.colon"), SPACES_OTHER);
       consumer.showCustomOption(PyCodeStyleSettings.class, "SPACE_AFTER_PY_COLON", ApplicationBundle.message("checkbox.spaces.after.colon"), SPACES_OTHER);
       consumer.showCustomOption(PyCodeStyleSettings.class, "SPACE_BEFORE_BACKSLASH", "Before '\\'", SPACES_OTHER);
+      consumer.showCustomOption(PyCodeStyleSettings.class, "SPACE_BEFORE_NUMBER_SIGN", "Before '#'", SPACES_OTHER);
+      consumer.showCustomOption(PyCodeStyleSettings.class, "SPACE_AFTER_NUMBER_SIGN", "After '#'", SPACES_OTHER);
     }
     else if (settingsType == SettingsType.BLANK_LINES_SETTINGS) {
       consumer.showStandardOptions("BLANK_LINES_AROUND_CLASS",
@@ -112,7 +115,7 @@
     CommonCodeStyleSettings.IndentOptions indentOptions = defaultSettings.initIndentOptions();
     indentOptions.INDENT_SIZE = 4;
     defaultSettings.ALIGN_MULTILINE_PARAMETERS_IN_CALLS = true;
-    return defaultSettings; 
+    return defaultSettings;
   }
 
   @Override
diff --git a/python/src/com/jetbrains/python/formatter/PyLineWrapPositionStrategy.java b/python/src/com/jetbrains/python/formatter/PyLineWrapPositionStrategy.java
index d7fa5f0..47b6b7c 100644
--- a/python/src/com/jetbrains/python/formatter/PyLineWrapPositionStrategy.java
+++ b/python/src/com/jetbrains/python/formatter/PyLineWrapPositionStrategy.java
@@ -24,6 +24,7 @@
 import com.intellij.psi.PsiFile;
 import com.intellij.psi.util.PsiTreeUtil;
 import com.intellij.util.text.CharArrayUtil;
+import com.jetbrains.python.PyTokenTypes;
 import com.jetbrains.python.psi.StringLiteralExpression;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -44,9 +45,6 @@
     addRule(new Rule('(', WrapCondition.AFTER));
     addRule(new Rule('[', WrapCondition.AFTER));
     addRule(new Rule('{', WrapCondition.AFTER));
-
-    // Symbols to wrap before
-    addRule(new Rule('.', WrapCondition.BEFORE));
   }
 
   @Override
@@ -75,11 +73,20 @@
                                    int maxPreferredOffset,
                                    boolean allowToBeyondMaxPreferredOffset,
                                    boolean virtual) {
+
     int wrapPosition =
       super.calculateWrapPosition(document, project, startOffset, endOffset, maxPreferredOffset, allowToBeyondMaxPreferredOffset, virtual);
     if (wrapPosition < 0) return wrapPosition;
     final CharSequence text = document.getCharsSequence();
 
+    if (wrapPosition > 0) {
+      char charBefore = text.charAt(wrapPosition - 1);
+      if (charBefore == '\'' || charBefore == '"') {
+        //don't wrap the first char of string literal
+        return wrapPosition + 1;
+      }
+    }
+
     char c = text.charAt(wrapPosition);
     if (!StringUtil.isWhiteSpace(c) || project == null) {
       return wrapPosition;
diff --git a/python/src/com/jetbrains/python/formatter/PyPreFormatProcessor.java b/python/src/com/jetbrains/python/formatter/PyPreFormatProcessor.java
new file mode 100644
index 0000000..ee56016
--- /dev/null
+++ b/python/src/com/jetbrains/python/formatter/PyPreFormatProcessor.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.formatter;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.PsiComment;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.codeStyle.CodeStyleSettings;
+import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
+import com.intellij.psi.impl.source.codeStyle.PreFormatProcessor;
+import com.jetbrains.python.PythonLanguage;
+import com.jetbrains.python.psi.LanguageLevel;
+import com.jetbrains.python.psi.PyElementGenerator;
+import com.jetbrains.python.psi.PyRecursiveElementVisitor;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author traff
+ */
+public class PyPreFormatProcessor implements PreFormatProcessor {
+  @NotNull
+  @Override
+  public TextRange process(@NotNull ASTNode element, @NotNull TextRange range) {
+    PsiElement psiElement = element.getPsi();
+    if (psiElement == null) return range;
+
+    if (!psiElement.getLanguage().is(PythonLanguage.getInstance())) return range;
+
+    PsiFile file = psiElement.isValid() ? psiElement.getContainingFile() : null;
+    if (file == null) return range;
+
+    Project project = psiElement.getProject();
+
+    return new PyCommentFormatter(project).process(psiElement, range);
+  }
+
+  /**
+   * @author traff
+   */
+  public static class PyCommentFormatter extends PyRecursiveElementVisitor {
+    private final Project myProject;
+    private final CodeStyleSettings mySettings;
+    private final PyCodeStyleSettings myPyCodeStyleSettings;
+    private TextRange myRange;
+    private int myDelta = 0;
+
+    public PyCommentFormatter(Project project) {
+      myProject = project;
+      mySettings = CodeStyleSettingsManager.getSettings(project);
+      myPyCodeStyleSettings = mySettings.getCustomSettings(PyCodeStyleSettings.class);
+    }
+
+    public TextRange process(PsiElement element, TextRange range) {
+      if (!myPyCodeStyleSettings.SPACE_AFTER_NUMBER_SIGN) {
+        return range;
+      }
+      myRange = range;
+      element.accept(this);
+      return TextRange.create(range.getStartOffset(), range.getEndOffset() + myDelta);
+    }
+
+    @Override
+    public void visitComment(PsiComment element) {
+      if (!myRange.contains(element.getTextRange())) {
+        return;
+      }
+      String text = element.getText();
+      int commentStart = text.indexOf('#');
+      if (commentStart != -1 && (commentStart + 1) < text.length()) {
+        String commentText = StringUtil.trimLeading(text.substring(commentStart + 1));
+
+        String newText = "# " + commentText;
+        if (!newText.equals(text)) {
+          myDelta += newText.length() - text.length();
+          element.replace(
+            PyElementGenerator.getInstance(myProject).createFromText(LanguageLevel.getDefault(), PsiComment.class, newText));
+        }
+      }
+    }
+  }
+}
diff --git a/python/src/com/jetbrains/python/formatter/PyWhiteSpaceFormattingStrategy.java b/python/src/com/jetbrains/python/formatter/PyWhiteSpaceFormattingStrategy.java
index c78e490..6a09e9c7 100644
--- a/python/src/com/jetbrains/python/formatter/PyWhiteSpaceFormattingStrategy.java
+++ b/python/src/com/jetbrains/python/formatter/PyWhiteSpaceFormattingStrategy.java
@@ -15,6 +15,7 @@
  */
 package com.jetbrains.python.formatter;
 
+import com.intellij.lang.ASTNode;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.codeStyle.CodeStyleSettings;
@@ -44,12 +45,16 @@
     CharSequence whiteSpace =  super.adjustWhiteSpaceIfNecessary(whiteSpaceText, startElement, startOffset, endOffset, codeStyleSettings);
     if (whiteSpace.length() > 0 && whiteSpace.charAt(0) == '\n' && !StringUtil.contains(whiteSpace, 0, whiteSpace.length(), '\\') &&
         PythonEnterHandler.needInsertBackslash(startElement.getContainingFile(), startOffset, false)) {
-      PyCodeStyleSettings settings = codeStyleSettings.getCustomSettings(PyCodeStyleSettings.class);
-      return (settings.SPACE_BEFORE_BACKSLASH ? " \\" : "\\") + whiteSpace.toString();
+      return addBackslashPrefix(whiteSpace, codeStyleSettings);
     }
     return whiteSpace;
   }
 
+  private static String addBackslashPrefix(CharSequence whiteSpace, CodeStyleSettings settings) {
+    PyCodeStyleSettings pySettings = settings.getCustomSettings(PyCodeStyleSettings.class);
+    return (pySettings.SPACE_BEFORE_BACKSLASH ? " \\" : "\\") + whiteSpace.toString();
+  }
+
   /**
    * Python uses backslashes at the end of the line as indication that next line is an extension of the current one.
    * <p/>
@@ -61,6 +66,7 @@
    * @param startOffset       start offset to use with the given text (inclusive)
    * @param endOffset         end offset to use with the given text (exclusive)
    * @param codeStyleSettings the code style settings
+   * @param nodeAfter
    * @return                  symbols to use for replacing <code>[startOffset; endOffset)</code> sub-sequence of the given text
    */
   @NotNull
@@ -69,11 +75,15 @@
                                                   @NotNull CharSequence text,
                                                   int startOffset,
                                                   int endOffset,
-                                                  CodeStyleSettings codeStyleSettings)
+                                                  CodeStyleSettings codeStyleSettings, ASTNode nodeAfter)
   {
     // The general idea is that '\' symbol before line feed should be preserved.
     TIntIntHashMap initialBackSlashes = countBackSlashes(text, startOffset, endOffset);
     if (initialBackSlashes.isEmpty()) {
+      if (nodeAfter != null && whiteSpaceText.length() > 0 && whiteSpaceText.charAt(0) == '\n' &&
+        PythonEnterHandler.needInsertBackslash(nodeAfter, false)) {
+        return addBackslashPrefix(whiteSpaceText, codeStyleSettings);
+      }
       return whiteSpaceText;
     }
 
@@ -124,6 +134,9 @@
   static TIntIntHashMap countBackSlashes(CharSequence text, int start, int end) {
     TIntIntHashMap result = new TIntIntHashMap();
     int line = 0;
+    if (end > text.length()) {
+      end = text.length();
+    }
     for (int i = start; i < end; i++) {
       char c = text.charAt(i);
       switch (c) {
diff --git a/python/src/com/jetbrains/python/inspections/PyArgumentEqualDefaultInspection.java b/python/src/com/jetbrains/python/inspections/PyArgumentEqualDefaultInspection.java
index 82013d3..6a06f87 100644
--- a/python/src/com/jetbrains/python/inspections/PyArgumentEqualDefaultInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyArgumentEqualDefaultInspection.java
@@ -131,6 +131,10 @@
         if (key.getText().equals(defaultValue.getText()))
           return true;
       }
+      if (key instanceof PyBinaryExpression && defaultValue instanceof PyBinaryExpression) {
+        if (key.getText().equals(defaultValue.getText()))
+          return true;
+      }
       else if (key instanceof PyStringLiteralExpression && defaultValue instanceof PyStringLiteralExpression) {
         if (((PyStringLiteralExpression)key).getStringValue().equals(((PyStringLiteralExpression)defaultValue).getStringValue()))
           return true;
diff --git a/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java b/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java
index 97a5a36..9a87959 100644
--- a/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java
@@ -68,32 +68,29 @@
 
     @Override
     public void visitPyDecoratorList(final PyDecoratorList node) {
-      PyDecorator[] decos = node.getDecorators();
-      for (PyDecorator deco : decos) {
-        if (! deco.hasArgumentList()) {
-          // empty arglist; deco function must have a non-kwarg first arg
-          PyCallExpression.PyMarkedCallee mkfunc = deco.resolveCallee(resolveWithoutImplicits());
-          if (mkfunc != null && !mkfunc.isImplicitlyResolved()) {
-            Callable callable = mkfunc.getCallable();
-            int first_param_offset =  mkfunc.getImplicitOffset();
-            final List<PyParameter> params = PyUtil.getParameters(callable, myTypeEvalContext);
-            final PyNamedParameter alleged_first_param = params.size() < first_param_offset ?
-                                                         null : params.get(first_param_offset-1).getAsNamed();
-            if (alleged_first_param == null || alleged_first_param.isKeywordContainer()) {
-              // no parameters left to pass function implicitly, or wrong param type
-              registerProblem(deco, PyBundle.message("INSP.func.$0.lacks.first.arg", callable.getName())); // TODO: better names for anon lambdas
-            }
-            else {
-              // possible unfilled params
-              for (int i=first_param_offset; i < params.size(); i += 1) {
-                PyNamedParameter par = params.get(i).getAsNamed();
-                // param tuples, non-starred or non-default won't do
-                if (par == null || (! par.isKeywordContainer() && ! par.isPositionalContainer() && !par.hasDefaultValue())) {
-                  String par_name;
-                  if (par != null) par_name = par.getName();
-                  else par_name = "(...)"; // can't be bothered to find the first non-tuple inside it
-                  registerProblem(deco, PyBundle.message("INSP.parameter.$0.unfilled", par_name));
-                }
+      PyDecorator[] decorators = node.getDecorators();
+      for (PyDecorator deco : decorators) {
+        if (deco.hasArgumentList()) continue;
+        final PyCallExpression.PyMarkedCallee markedCallee = deco.resolveCallee(resolveWithoutImplicits());
+        if (markedCallee != null && !markedCallee.isImplicitlyResolved()) {
+          final Callable callable = markedCallee.getCallable();
+          int firstParamOffset =  markedCallee.getImplicitOffset();
+          final List<PyParameter> params = PyUtil.getParameters(callable, myTypeEvalContext);
+          final PyNamedParameter allegedFirstParam = params.size() < firstParamOffset ?
+                                                       null : params.get(firstParamOffset-1).getAsNamed();
+          if (allegedFirstParam == null || allegedFirstParam.isKeywordContainer()) {
+            // no parameters left to pass function implicitly, or wrong param type
+            registerProblem(deco, PyBundle.message("INSP.func.$0.lacks.first.arg", callable.getName())); // TODO: better names for anon lambdas
+          }
+          else { // possible unfilled params
+            for (int i = firstParamOffset; i < params.size(); i += 1) {
+              final PyParameter parameter = params.get(i);
+              if (parameter instanceof PySingleStarParameter) continue;
+              final PyNamedParameter par = parameter.getAsNamed();
+              // param tuples, non-starred or non-default won't do
+              if (par == null || (!par.isKeywordContainer() && !par.isPositionalContainer() &&!par.hasDefaultValue())) {
+                String parameterName = par != null ? par.getName() : "(...)";
+                registerProblem(deco, PyBundle.message("INSP.parameter.$0.unfilled", parameterName));
               }
             }
           }
@@ -176,12 +173,12 @@
           PyType inside_type = context.getType(content);
           if (inside_type != null && !PyTypeChecker.isUnknown(inside_type)) {
             if (((PyStarArgument)arg).isKeyword()) {
-              if (!PyABCUtil.isSubtype(inside_type, PyNames.MAPPING)) {
+              if (!PyABCUtil.isSubtype(inside_type, PyNames.MAPPING, context)) {
                 holder.registerProblem(arg, PyBundle.message("INSP.expected.dict.got.$0", inside_type.getName()));
               }
             }
             else { // * arg
-              if (!PyABCUtil.isSubtype(inside_type, PyNames.ITERABLE)) {
+              if (!PyABCUtil.isSubtype(inside_type, PyNames.ITERABLE, context)) {
                 holder.registerProblem(arg, PyBundle.message("INSP.expected.iter.got.$0", inside_type.getName()));
               }
             }
diff --git a/python/src/com/jetbrains/python/inspections/PyAssignmentToLoopOrWithParameterInspection.java b/python/src/com/jetbrains/python/inspections/PyAssignmentToLoopOrWithParameterInspection.java
new file mode 100644
index 0000000..2efaf98
--- /dev/null
+++ b/python/src/com/jetbrains/python/inspections/PyAssignmentToLoopOrWithParameterInspection.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.inspections;
+
+import com.intellij.codeInspection.LocalInspectionToolSession;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.openapi.util.Condition;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiElementVisitor;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
+import com.jetbrains.python.psi.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+//TODO: Try to share logic with AssignmentToForLoopParameterInspection
+
+/**
+ * Checks for cases when you rewrite loop variable with inner loop.
+ * It finds all <code>with</code> and <code>for</code> statements, takes variables declared by them and ensures none of parent
+ * <code>with</code> or <code>for</code> declares variable with the same name
+ *
+ * @author link
+ */
+public class PyAssignmentToLoopOrWithParameterInspection extends PyInspection {
+
+  private static final String NAME = PyBundle.message("INSP.NAME.assignment.to.loop.or.with.parameter.display.name");
+
+  @NotNull
+  @Override
+  public String getDisplayName() {
+    return NAME;
+  }
+
+
+  @NotNull
+  @Override
+  public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder,
+                                        boolean isOnTheFly,
+                                        @NotNull final LocalInspectionToolSession session) {
+    return new Visitor(holder, session);
+  }
+
+  private static class Visitor extends PyInspectionVisitor {
+    private Visitor(@Nullable ProblemsHolder holder, @NotNull LocalInspectionToolSession session) {
+      super(holder, session);
+    }
+
+    @Override
+    public void visitPyWithStatement(PyWithStatement node) {
+      checkNotReDeclaringUpperLoopOrStatement(node);
+    }
+
+    @Override
+    public void visitPyForStatement(PyForStatement node) {
+      checkNotReDeclaringUpperLoopOrStatement(node);
+    }
+
+    /**
+     * Finds first parent of specific type (See {@link #isRequiredStatement(com.intellij.psi.PsiElement)})
+     * that declares one of names, declared in this statement
+     */
+    private void checkNotReDeclaringUpperLoopOrStatement(NameDefiner statement) {
+      for (PsiElement declaredVar : statement.iterateNames()) {
+        Filter filter = new Filter(handleSubscriptionsAndResolveSafely(declaredVar));
+        PsiElement firstParent = PsiTreeUtil.findFirstParent(statement, true, filter);
+        if (firstParent != null && isRequiredStatement(firstParent)) {
+          registerProblem(declaredVar,
+                          PyBundle.message("INSP.NAME.assignment.to.loop.or.with.parameter.display.message", declaredVar.getText()));
+        }
+      }
+    }
+  }
+
+  /**
+   * Filters list of parents trying to find parent that declares var that refers to {@link #node}
+   * Returns {@link com.jetbrains.python.codeInsight.controlflow.ScopeOwner} if nothing found.
+   * Returns parent otherwise.
+   */
+  private static class Filter implements Condition<PsiElement> {
+    private final PsiElement node;
+
+    private Filter(PsiElement node) {
+      this.node = node;
+    }
+
+    @Override
+    public boolean value(PsiElement psiElement) {
+      if (psiElement instanceof ScopeOwner) {
+        return true; //Do not go any further
+      }
+      if (!(isRequiredStatement(psiElement))) {
+        return false; //Parent has wrong type, skip
+      }
+      Iterable<PyElement> varsDeclaredInStatement = ((NameDefiner)psiElement).iterateNames();
+      for (PsiElement varDeclaredInStatement : varsDeclaredInStatement) {
+        //For each variable, declared by this parent take first declaration and open subscription list if any
+        PsiReference reference = handleSubscriptionsAndResolveSafely(varDeclaredInStatement).getReference();
+        if (reference != null && reference.isReferenceTo(node)) {
+          return true; //One of variables declared by this parent refers to node
+        }
+      }
+      return false;
+    }
+
+  }
+
+  /**
+   * Opens subscription list (<code>i[n][q][f] --&gt; i</code>) and resolves ref recursively to the topmost element,
+   * but not further than file borders (to prevent Stub to AST conversion)
+   *
+   * @param element element to open and resolve
+   * @return opened and resolved element
+   */
+  private static PsiElement handleSubscriptionsAndResolveSafely(PsiElement element) {
+    assert element != null;
+    if (element instanceof PySubscriptionExpression) {
+      element = ((PySubscriptionExpression)element).getRootOperand();
+    }
+    element = PyUtil.resolveToTheTop(element);
+    return element;
+  }
+
+  /**
+   * Checks if element is statement this inspection should work with
+   *
+   * @param element to check
+   * @return true if inspection should work with this element
+   */
+  private static boolean isRequiredStatement(PsiElement element) {
+    assert element != null;
+    return element instanceof PyWithStatement || element instanceof PyForStatement;
+  }
+}
diff --git a/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java b/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java
index db3b137..791a1b5 100644
--- a/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java
@@ -70,6 +70,9 @@
       if (!isApplicable(containingClass)) {
         return;
       }
+
+      final PyFunction.Modifier modifier = node.getModifier();
+      if (modifier != null) return;
       final List<PyTargetExpression> classAttributes = containingClass.getClassAttributes();
 
       Map<String, PyTargetExpression> attributesInInit = new HashMap<String, PyTargetExpression>();
@@ -97,7 +100,7 @@
       for (Map.Entry<String, PyTargetExpression> attribute : attributes.entrySet()) {
         String attributeName = attribute.getKey();
         if (attributeName == null) continue;
-        final Property property = containingClass.findProperty(attributeName);
+        final Property property = containingClass.findProperty(attributeName, true);
         if (!attributesInInit.containsKey(attributeName) && property == null) {
           registerProblem(attribute.getValue(), PyBundle.message("INSP.attribute.$0.outside.init", attributeName),
                           new PyMoveAttributeToInitQuickFix());
diff --git a/python/src/com/jetbrains/python/inspections/PyCallingNonCallableInspection.java b/python/src/com/jetbrains/python/inspections/PyCallingNonCallableInspection.java
index 47f55a4..4af6767 100644
--- a/python/src/com/jetbrains/python/inspections/PyCallingNonCallableInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyCallingNonCallableInspection.java
@@ -20,7 +20,10 @@
 import com.intellij.psi.PsiElementVisitor;
 import com.jetbrains.python.PyNames;
 import com.jetbrains.python.psi.*;
-import com.jetbrains.python.psi.types.*;
+import com.jetbrains.python.psi.types.PyClassType;
+import com.jetbrains.python.psi.types.PyType;
+import com.jetbrains.python.psi.types.PyTypeChecker;
+import com.jetbrains.python.psi.types.TypeEvalContext;
 import org.jetbrains.annotations.Nls;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -89,7 +92,7 @@
 
   @Nullable
   private static Boolean isCallable(@NotNull PyExpression element, @NotNull TypeEvalContext context) {
-    if (element instanceof PyQualifiedExpression && PyNames.CLASS.equals(element.getName())) {
+    if (element instanceof PyQualifiedExpression && PyNames.__CLASS__.equals(element.getName())) {
       return true;
     }
     return PyTypeChecker.isCallable(context.getType(element));
diff --git a/python/src/com/jetbrains/python/inspections/PyMethodFirstArgAssignmentInspection.java b/python/src/com/jetbrains/python/inspections/PyMethodFirstArgAssignmentInspection.java
index bb5dfab..0d29a05 100644
--- a/python/src/com/jetbrains/python/inspections/PyMethodFirstArgAssignmentInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyMethodFirstArgAssignmentInspection.java
@@ -57,7 +57,7 @@
     }
 
     private void handleTarget(PyQualifiedExpression target, String name) {
-      if (target.getQualifier() == null && name.equals(target.getText())) {
+      if (!target.isQualified() && name.equals(target.getText())) {
         complain(target, name);
       }
     }
diff --git a/python/src/com/jetbrains/python/inspections/PyMethodMayBeStaticInspection.java b/python/src/com/jetbrains/python/inspections/PyMethodMayBeStaticInspection.java
index 2bc6424..71f2772 100644
--- a/python/src/com/jetbrains/python/inspections/PyMethodMayBeStaticInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyMethodMayBeStaticInspection.java
@@ -70,7 +70,9 @@
       if (!supers.isEmpty()) return;
       final Collection<PyFunction> overrides = PyOverridingMethodsSearch.search(node, true).findAll();
       if (!overrides.isEmpty()) return;
-      if (PyUtil.isDecoratedAsAbstract(node) || node.getModifier() != null) return;
+      final PyDecoratorList decoratorList = node.getDecoratorList();
+      if (decoratorList != null) return;
+      if (node.getModifier() != null) return;
       final Property property = containingClass.findPropertyByCallable(node);
       if (property != null) return;
 
diff --git a/python/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java b/python/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java
index 0a4c362..9087180 100644
--- a/python/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java
@@ -139,7 +139,7 @@
                 if (args.length > 0) {
                   String firstArg = args[0].getText();
                   final String qualifiedName = cl.getQualifiedName();
-                  if (firstArg.equals(cl.getName()) || firstArg.equals(CANONICAL_SELF+"."+ CLASS) ||
+                  if (firstArg.equals(cl.getName()) || firstArg.equals(CANONICAL_SELF+"."+ __CLASS__) ||
                       (qualifiedName != null && qualifiedName.endsWith(firstArg)))
                       return true;
                   for (PyClass s : cl.getAncestorClasses(myTypeEvalContext)) {
diff --git a/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java b/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java
index 5a3bdce..ec37611 100644
--- a/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java
@@ -41,7 +41,7 @@
 import com.jetbrains.python.packaging.*;
 import com.jetbrains.python.packaging.ui.PyChooseRequirementsDialog;
 import com.jetbrains.python.psi.*;
-import com.jetbrains.python.psi.resolve.PyResolveUtil;
+import com.jetbrains.python.psi.impl.PyPsiUtils;
 import com.jetbrains.python.sdk.PythonSdkType;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -147,9 +147,8 @@
           return;
         }
       }
-      final List<PyExpression> expressions = PyResolveUtil.unwindQualifiers(importedExpression);
-      if (!expressions.isEmpty()) {
-        final PyExpression packageReferenceExpression = expressions.get(0);
+      final PyExpression packageReferenceExpression = PyPsiUtils.getFirstQualifier(importedExpression);
+      if (packageReferenceExpression != null) {
         final String packageName = packageReferenceExpression.getName();
         if (packageName != null && !myIgnoredPackages.contains(packageName)) {
           if (!ApplicationManager.getApplication().isUnitTestMode() && !PyPIPackageUtil.INSTANCE.isInPyPI(packageName)) {
diff --git a/python/src/com/jetbrains/python/inspections/PyPep8NamingInspection.java b/python/src/com/jetbrains/python/inspections/PyPep8NamingInspection.java
index 9b26d94..f3fddba 100644
--- a/python/src/com/jetbrains/python/inspections/PyPep8NamingInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyPep8NamingInspection.java
@@ -22,9 +22,10 @@
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.psi.PsiElementVisitor;
 import com.intellij.psi.util.PsiTreeUtil;
-import com.intellij.util.containers.hash.HashMap;
-import com.jetbrains.python.psi.*;
 import com.intellij.psi.util.QualifiedName;
+import com.intellij.util.containers.hash.HashMap;
+import com.intellij.util.containers.hash.HashSet;
+import com.jetbrains.python.psi.*;
 import com.jetbrains.python.psi.search.PySuperMethodsSearch;
 import com.jetbrains.python.psi.types.PyModuleType;
 import com.jetbrains.python.psi.types.PyType;
@@ -33,7 +34,9 @@
 import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
+import java.util.Collection;
 import java.util.Map;
+import java.util.Set;
 import java.util.regex.Pattern;
 
 /**
@@ -68,9 +71,17 @@
     public void visitPyAssignmentStatement(PyAssignmentStatement node) {
       final PyFunction function = PsiTreeUtil.getParentOfType(node, PyFunction.class, true, PyClass.class);
       if (function == null) return;
+      final Collection<PyGlobalStatement> globalStatements = PsiTreeUtil.findChildrenOfType(function, PyGlobalStatement.class);
+      final Set<String> globals = new HashSet<String>();
+      for (PyGlobalStatement statement : globalStatements) {
+        final PyTargetExpression[] statementGlobals = statement.getGlobals();
+        for (PyTargetExpression global : statementGlobals) {
+          globals.add(global.getName());
+        }
+      }
       for (PyExpression expression : node.getTargets()) {
         final String name = expression.getName();
-        if (name == null) continue;
+        if (name == null || globals.contains(name)) continue;
         if (expression instanceof PyTargetExpression) {
           final PyExpression qualifier = ((PyTargetExpression)expression).getQualifier();
           if (qualifier != null) {
@@ -126,7 +137,7 @@
       final String asName = node.getAsName();
       final QualifiedName importedQName = node.getImportedQName();
       if (importedQName == null) return;
-      final String name = importedQName.toString();
+      final String name = importedQName.getLastComponent();
 
       if (asName == null || name == null) return;
       if (UPPERCASE_REGEX.matcher(name).matches()) {
diff --git a/python/src/com/jetbrains/python/inspections/PyPropertyAccessInspection.java b/python/src/com/jetbrains/python/inspections/PyPropertyAccessInspection.java
index 425c585..8e48067 100644
--- a/python/src/com/jetbrains/python/inspections/PyPropertyAccessInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyPropertyAccessInspection.java
@@ -86,7 +86,7 @@
               property = myPropertyCache.get(key);
             }
             else {
-              property = cls.findProperty(name);
+              property = cls.findProperty(name, true);
             }
             myPropertyCache.put(key, property); // we store nulls, too, to know that a property does not exist
             if (property != null) {
diff --git a/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java b/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java
index 8daea863..fe9ac7f 100644
--- a/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java
@@ -20,9 +20,10 @@
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiElementVisitor;
 import com.intellij.psi.PsiReference;
-import com.intellij.psi.util.PsiTreeUtil;
 import com.jetbrains.python.PyBundle;
 import com.jetbrains.python.PyNames;
+import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
+import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
 import com.jetbrains.python.psi.PyClass;
 import com.jetbrains.python.psi.PyExpression;
 import com.jetbrains.python.psi.PyReferenceExpression;
@@ -67,20 +68,20 @@
       if (qualifier == null || PyNames.CANONICAL_SELF.equals(qualifier.getText())) return;
       final String name = node.getName();
       if (name != null && name.startsWith("_") && !name.startsWith("__") && !name.endsWith("__")) {
-        final PyClass parentClass = PsiTreeUtil.getParentOfType(node, PyClass.class);
+        final PyClass parentClass = getClassOwner(node);
         if (parentClass != null) {
           final PsiReference reference = node.getReference();
           final PsiElement resolvedExpression = reference.resolve();
-          final PyClass resolvedClass = PsiTreeUtil.getParentOfType(resolvedExpression, PyClass.class);
+          final PyClass resolvedClass = getClassOwner(resolvedExpression);
           if (parentClass.isSubclass(resolvedClass))
             return;
 
-          PyClass outerClass = PsiTreeUtil.getParentOfType(parentClass, PyClass.class);
+          PyClass outerClass = getClassOwner(parentClass);
           while (outerClass != null) {
             if (outerClass.isSubclass(resolvedClass))
               return;
 
-            outerClass = PsiTreeUtil.getParentOfType(outerClass, PyClass.class);
+            outerClass = getClassOwner(outerClass);
           }
         }
         final PyType type = myTypeEvalContext.getType(qualifier);
@@ -91,5 +92,14 @@
       }
     }
 
+    @Nullable
+    private static PyClass getClassOwner(@Nullable PsiElement element) {
+      for (ScopeOwner owner = ScopeUtil.getScopeOwner(element); owner != null; owner = ScopeUtil.getScopeOwner(owner)) {
+        if (owner instanceof PyClass) {
+          return (PyClass)owner;
+        }
+      }
+      return null;
+    }
   }
 }
diff --git a/python/src/com/jetbrains/python/inspections/PySetFunctionToLiteralInspection.java b/python/src/com/jetbrains/python/inspections/PySetFunctionToLiteralInspection.java
index aaebc16b..f0bdeb3 100644
--- a/python/src/com/jetbrains/python/inspections/PySetFunctionToLiteralInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PySetFunctionToLiteralInspection.java
@@ -93,7 +93,7 @@
     }
 
     private static boolean isInBuiltins(PyExpression callee) {
-      if (callee instanceof PyQualifiedExpression && (((PyQualifiedExpression)callee).getQualifier() != null)) {
+      if (callee instanceof PyQualifiedExpression && (((PyQualifiedExpression)callee).isQualified())) {
         return false;
       }
       PsiReference reference = callee.getReference();
diff --git a/python/src/com/jetbrains/python/inspections/PyShadowingBuiltinsInspection.java b/python/src/com/jetbrains/python/inspections/PyShadowingBuiltinsInspection.java
index 8f6b5af..fa7a1b9 100644
--- a/python/src/com/jetbrains/python/inspections/PyShadowingBuiltinsInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyShadowingBuiltinsInspection.java
@@ -92,7 +92,7 @@
 
     @Override
     public void visitPyTargetExpression(@NotNull PyTargetExpression node) {
-      if (node.getQualifier() == null) {
+      if (!node.isQualified()) {
         processElement(node);
       }
     }
diff --git a/python/src/com/jetbrains/python/inspections/PyShadowingNamesInspection.java b/python/src/com/jetbrains/python/inspections/PyShadowingNamesInspection.java
index 82b3cdc..07f92ff 100644
--- a/python/src/com/jetbrains/python/inspections/PyShadowingNamesInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyShadowingNamesInspection.java
@@ -77,7 +77,7 @@
 
     @Override
     public void visitPyTargetExpression(@NotNull PyTargetExpression node) {
-      if (node.getQualifier() == null) {
+      if (!node.isQualified()) {
         processElement(node);
       }
     }
diff --git a/python/src/com/jetbrains/python/inspections/PyStatementEffectInspection.java b/python/src/com/jetbrains/python/inspections/PyStatementEffectInspection.java
index b35a6f75..28ccafd 100644
--- a/python/src/com/jetbrains/python/inspections/PyStatementEffectInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyStatementEffectInspection.java
@@ -21,8 +21,6 @@
 import com.intellij.psi.ResolveResult;
 import com.intellij.psi.util.PsiTreeUtil;
 import com.jetbrains.python.PyBundle;
-import com.jetbrains.python.documentation.DocStringUtil;
-import com.jetbrains.python.inspections.quickfix.StatementEffectDocstringQuickFix;
 import com.jetbrains.python.inspections.quickfix.StatementEffectFunctionCallQuickFix;
 import com.jetbrains.python.inspections.quickfix.StatementEffectIntroduceVariableQuickFix;
 import com.jetbrains.python.psi.*;
@@ -66,6 +64,11 @@
         return;
       if (hasEffect(expression)) return;
 
+      // https://twitter.com/gvanrossum/status/112670605505077248
+      if (expression instanceof PyStringLiteralExpression) {
+        return;
+      }
+
       final PyTryPart tryPart = PsiTreeUtil.getParentOfType(node, PyTryPart.class);
       if (tryPart != null) {
         final PyStatementList statementList = tryPart.getStatementList();
@@ -76,10 +79,7 @@
           return;
         }
       }
-      if (checkStringLiteral(expression)) {
-        return;
-      }
-      if (expression instanceof PyReferenceExpression && ((PyReferenceExpression)expression).getQualifier() == null) {
+      if (expression instanceof PyReferenceExpression && !((PyReferenceExpression)expression).isQualified()) {
         registerProblem(expression, PyBundle.message("INSP.NAME.statement.message"));
       }
       else {
@@ -87,18 +87,6 @@
       }
     }
 
-    private boolean checkStringLiteral(PyExpression expression) {
-      if (expression instanceof PyStringLiteralExpression) {
-        PyDocStringOwner parent = PsiTreeUtil.getParentOfType(expression, PyFunction.class, PyClass.class);
-        if (parent != null && parent.getDocStringExpression() == null) {
-          registerProblem(expression, "Docstring seems to be misplaced",
-                      new StatementEffectDocstringQuickFix());
-          return true;
-        }
-      }
-      return false;
-    }
-
     private boolean hasEffect(@Nullable PyExpression expression) {
       if (expression == null) {
         return false;
@@ -106,10 +94,6 @@
       if (expression instanceof PyCallExpression || expression instanceof PyYieldExpression) {
         return true;
       }
-
-      if (expression instanceof PyStringLiteralExpression) {
-        if (DocStringUtil.isDocStringExpression(expression)) return true;
-      }
       else if (expression instanceof PyListCompExpression) {
         if (hasEffect(((PyListCompExpression)expression).getResultExpression())) {
           return true;
diff --git a/python/src/com/jetbrains/python/inspections/PyStringFormatParser.java b/python/src/com/jetbrains/python/inspections/PyStringFormatParser.java
index 5f935cf..158206c 100644
--- a/python/src/com/jetbrains/python/inspections/PyStringFormatParser.java
+++ b/python/src/com/jetbrains/python/inspections/PyStringFormatParser.java
@@ -29,11 +29,14 @@
 import java.util.List;
 import java.util.Map;
 import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * @author yole
  */
 public class PyStringFormatParser {
+  private static final Pattern NEW_STYLE_FORMAT_TOKENS = Pattern.compile("(\\{\\{)|(\\}\\})|(\\{[^\\{\\}]*\\})|([^\\{\\}]+)");
+
   public static abstract class FormatStringChunk {
     private final int myStartIndex;
     protected int myEndIndex;
@@ -173,30 +176,22 @@
   @NotNull
   public static List<FormatStringChunk> parseNewStyleFormat(@NotNull String s) {
     final List<FormatStringChunk> results = new ArrayList<FormatStringChunk>();
-    int pos = 0;
-    final int n = s.length();
-    while (pos < n) {
-      int next = s.indexOf('{', pos);
-      while (next > 0 && next < n - 1 && s.charAt(next + 1) == '{') {
-        next = s.indexOf('{', next + 2);
+    final Matcher matcher = NEW_STYLE_FORMAT_TOKENS.matcher(s);
+    while (matcher.find()) {
+      final String group = matcher.group();
+      final int start = matcher.start();
+      final int end = matcher.end();
+      if ("{{".equals(group)) {
+        results.add(new ConstantChunk(start, end));
       }
-      if (next < 0) {
-        break;
+      else if ("}}".equals(group)) {
+        results.add(new ConstantChunk(start, end));
       }
-      if (next > pos) {
-        results.add(new ConstantChunk(pos, next));
-      }
-      pos = next;
-      next = s.indexOf('}', pos);
-      while (next > 0 && next < n - 1 && s.charAt(next + 1) == '}') {
-        next = s.indexOf('}', next + 2);
-      }
-      if (next > pos) {
-        final SubstitutionChunk chunk = new SubstitutionChunk(pos);
-        final int nameStart = pos + 1;
-        final int chunkEnd = next + 1;
-        chunk.setEndIndex(chunkEnd);
-        final int nameEnd = StringUtil.indexOfAny(s, "!:.[}", nameStart, chunkEnd);
+      else if (group.startsWith("{") && group.endsWith("}")) {
+        final SubstitutionChunk chunk = new SubstitutionChunk(start);
+        chunk.setEndIndex(end);
+        final int nameStart = start + 1;
+        final int nameEnd = StringUtil.indexOfAny(s, "!:.[}", nameStart, end);
         if (nameEnd > 0 && nameStart < nameEnd) {
           final String name = s.substring(nameStart, nameEnd);
           try {
@@ -209,10 +204,9 @@
         // TODO: Parse substitution details
         results.add(chunk);
       }
-      pos = next + 1;
-    }
-    if (pos < n) {
-      results.add(new ConstantChunk(pos, n));
+      else {
+        results.add(new ConstantChunk(start, end));
+      }
     }
     return results;
   }
diff --git a/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java b/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java
index 484ac05..45df382 100644
--- a/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java
@@ -78,7 +78,7 @@
       final PyExpression source = node.getForPart().getSource();
       if (source != null) {
         final PyType type = myTypeEvalContext.getType(source);
-        if (type != null && !PyTypeChecker.isUnknown(type) && !PyABCUtil.isSubtype(type, PyNames.ITERABLE)) {
+        if (type != null && !PyTypeChecker.isUnknown(type) && !PyABCUtil.isSubtype(type, PyNames.ITERABLE, myTypeEvalContext)) {
           registerProblem(source, String.format("Expected 'collections.Iterable', got '%s' instead",
                                                 PythonDocumentationProvider.getTypeName(type, myTypeEvalContext)));
         }
diff --git a/python/src/com/jetbrains/python/inspections/PyUnboundLocalVariableInspection.java b/python/src/com/jetbrains/python/inspections/PyUnboundLocalVariableInspection.java
index 38b0a47..abec5b1 100644
--- a/python/src/com/jetbrains/python/inspections/PyUnboundLocalVariableInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyUnboundLocalVariableInspection.java
@@ -79,7 +79,7 @@
         return;
       }
       // Ignore qualifier inspections
-      if (node.getQualifier() != null) {
+      if (node.isQualified()) {
         return;
       }
       // Ignore import subelements
diff --git a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java
index 0739893..9e08a47 100644
--- a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java
@@ -29,7 +29,9 @@
 import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
 import com.intellij.psi.*;
 import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.psi.util.QualifiedName;
 import com.intellij.util.Consumer;
+import com.intellij.util.PlatformUtils;
 import com.jetbrains.python.PyBundle;
 import com.jetbrains.python.PyNames;
 import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
@@ -51,7 +53,6 @@
 import com.jetbrains.python.psi.impl.PyBuiltinCache;
 import com.jetbrains.python.psi.impl.PyImportStatementNavigator;
 import com.jetbrains.python.psi.impl.PyImportedModule;
-import com.intellij.psi.util.QualifiedName;
 import com.jetbrains.python.psi.impl.references.PyImportReference;
 import com.jetbrains.python.psi.impl.references.PyOperatorReference;
 import com.jetbrains.python.psi.resolve.ImportedResolveResult;
@@ -60,6 +61,7 @@
 import com.jetbrains.python.psi.resolve.RatedResolveResult;
 import com.jetbrains.python.psi.types.*;
 import com.jetbrains.python.sdk.PythonSdkType;
+import com.jetbrains.python.sdk.skeletons.PySkeletonRefresher;
 import org.jetbrains.annotations.Nls;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -125,12 +127,22 @@
     private Set<PsiElement> myUsedImports = Collections.synchronizedSet(new HashSet<PsiElement>());
     private Set<NameDefiner> myAllImports = Collections.synchronizedSet(new HashSet<NameDefiner>());
     private final ImmutableSet<String> myIgnoredIdentifiers;
+    private volatile Boolean myIsEnabled = null;
 
     public Visitor(@Nullable ProblemsHolder holder, @NotNull LocalInspectionToolSession session, List<String> ignoredIdentifiers) {
       super(holder, session);
       myIgnoredIdentifiers = ImmutableSet.copyOf(ignoredIdentifiers);
     }
 
+    public boolean isEnabled(@NotNull PsiElement anchor) {
+      if (myIsEnabled == null) {
+        final boolean isPyCharm = PlatformUtils.isPyCharm();
+        myIsEnabled = (isPyCharm && PythonSdkType.getSdk(anchor) != null || !isPyCharm) &&
+                      !PySkeletonRefresher.isGeneratingSkeletons();
+      }
+      return myIsEnabled;
+    }
+
     @Override
     public void visitPyTargetExpression(PyTargetExpression node) {
       checkSlots(node);
@@ -174,7 +186,7 @@
     public void visitPyImportElement(PyImportElement node) {
       super.visitPyImportElement(node);
       final PyFromImportStatement fromImport = PsiTreeUtil.getParentOfType(node, PyFromImportStatement.class);
-      if (fromImport == null || !fromImport.isFromFuture()) {
+      if (isEnabled(node) && (fromImport == null || !fromImport.isFromFuture())) {
         myAllImports.add(node);
       }
     }
@@ -182,7 +194,9 @@
     @Override
     public void visitPyStarImportElement(PyStarImportElement node) {
       super.visitPyStarImportElement(node);
-      myAllImports.add(node);
+      if (isEnabled(node)) {
+        myAllImports.add(node);
+      }
     }
 
     @Nullable
@@ -223,9 +237,9 @@
             calls.add((PyCallExpression)cond);
           }
           if (cond != null) {
-            final PyCallExpression[] callExprs = PsiTreeUtil.getChildrenOfType(cond, PyCallExpression.class);
-            if (callExprs != null) {
-              calls.addAll(Arrays.asList(callExprs));
+            final PyCallExpression[] callExpressions = PsiTreeUtil.getChildrenOfType(cond, PyCallExpression.class);
+            if (callExpressions != null) {
+              calls.addAll(Arrays.asList(callExpressions));
             }
             for (PyCallExpression call : calls) {
               final PyExpression callee = call.getCallee();
@@ -298,7 +312,9 @@
     }
 
     private void processReference(PyElement node, @Nullable PsiReference reference) {
-      if (reference == null || reference.isSoft()) return;
+      if (!isEnabled(node) || reference == null || reference.isSoft()) {
+        return;
+      }
       HighlightSeverity severity = HighlightSeverity.ERROR;
       if (reference instanceof PsiReferenceEx) {
         severity = ((PsiReferenceEx)reference).getUnresolvedHighlightSeverity(myTypeEvalContext);
@@ -411,9 +427,9 @@
       }
       final PsiElement element = reference.getElement();
       final List<LocalQuickFix> actions = new ArrayList<LocalQuickFix>(2);
-      final String refname = (element instanceof PyQualifiedExpression) ? ((PyQualifiedExpression)element).getReferencedName() : ref_text;
+      final String refName = (element instanceof PyQualifiedExpression) ? ((PyQualifiedExpression)element).getReferencedName() : ref_text;
       // Empty text, nothing to highlight
-      if (refname == null || refname.length() <= 0) {
+      if (refName == null || refName.length() <= 0) {
         return;
       }
 
@@ -433,47 +449,48 @@
         }
       }
       // Legacy non-qualified ignore patterns
-      if (myIgnoredIdentifiers.contains(refname)) {
+      if (myIgnoredIdentifiers.contains(refName)) {
         return;
       }
 
       if (element instanceof PyReferenceExpression) {
-        PyReferenceExpression refex = (PyReferenceExpression)element;
-        if (PyNames.COMPARISON_OPERATORS.contains(refname)) {
+        PyReferenceExpression expr = (PyReferenceExpression)element;
+        if (PyNames.COMPARISON_OPERATORS.contains(refName)) {
           return;
         }
-        if (refex.getQualifier() != null) {
+        if (expr.isQualified()) {
           final PyClassTypeImpl object_type = (PyClassTypeImpl)PyBuiltinCache.getInstance(node).getObjectType();
-          if ((object_type != null) && object_type.getPossibleInstanceMembers().contains(refname)) return;
+          if ((object_type != null) && object_type.getPossibleInstanceMembers().contains(refName)) return;
         }
         else {
-          if (PyUnreachableCodeInspection.hasAnyInterruptedControlFlowPaths(refex)) {
+          if (PyUnreachableCodeInspection.hasAnyInterruptedControlFlowPaths(expr)) {
             return;
           }
           if (LanguageLevel.forElement(node).isOlderThan(LanguageLevel.PYTHON26)) {
-            if ("with".equals(refname)) {
+            if ("with".equals(refName)) {
               actions.add(new UnresolvedRefAddFutureImportQuickFix());
             }
           }
           if (ref_text.equals("true") || ref_text.equals("false")) {
             actions.add(new UnresolvedRefTrueFalseQuickFix(element));
           }
-          addAddSelfFix(node, refex, actions);
+          addAddSelfFix(node, expr, actions);
           PyCallExpression callExpression = PsiTreeUtil.getParentOfType(element, PyCallExpression.class);
-          if (callExpression != null) {
-            actions.add(new UnresolvedRefCreateFunctionQuickFix(callExpression, refex));
+          if (callExpression != null && (!(callExpression.getCallee() instanceof PyQualifiedExpression) ||
+              ((PyQualifiedExpression)callExpression.getCallee()).getQualifier() == null)) {
+            actions.add(new UnresolvedRefCreateFunctionQuickFix(callExpression, expr));
           }
           PyFunction parentFunction = PsiTreeUtil.getParentOfType(element, PyFunction.class);
           PyDecorator decorator = PsiTreeUtil.getParentOfType(element, PyDecorator.class);
           PyImportStatement importStatement = PsiTreeUtil.getParentOfType(element, PyImportStatement.class);
           if (parentFunction != null && decorator == null && importStatement == null) {
-            actions.add(new UnresolvedReferenceAddParameterQuickFix(refname));
+            actions.add(new UnresolvedReferenceAddParameterQuickFix(refName));
           }
           actions.add(new PyRenameUnresolvedRefQuickFix());
         }
         // unqualified:
         // may be module's
-        if (PyModuleType.getPossibleInstanceMembers().contains(refname)) return;
+        if (PyModuleType.getPossibleInstanceMembers().contains(refName)) return;
         // may be a "try: import ..."; not an error not to resolve
         if ((
           PsiTreeUtil.getParentOfType(
@@ -496,31 +513,31 @@
           if ("__qualname__".equals(ref_text) && LanguageLevel.forElement(element).isAtLeast(LanguageLevel.PYTHON33)) {
             return;
           }
-          final PyQualifiedExpression qexpr = (PyQualifiedExpression)element;
-          if (PyNames.COMPARISON_OPERATORS.contains(qexpr.getReferencedName()) || refname == null) {
+          final PyQualifiedExpression expr = (PyQualifiedExpression)element;
+          if (PyNames.COMPARISON_OPERATORS.contains(expr.getReferencedName())) {
             return;
           }
-          final PyExpression qualifier = qexpr.getQualifier();
+          final PyExpression qualifier = expr.getQualifier();
           if (qualifier != null) {
-            PyType qtype = myTypeEvalContext.getType(qualifier);
-            if (qtype != null) {
-              if (ignoreUnresolvedMemberForType(qtype, reference, refname)) {
+            PyType type = myTypeEvalContext.getType(qualifier);
+            if (type != null) {
+              if (ignoreUnresolvedMemberForType(type, reference, refName)) {
                 return;
               }
-              addCreateMemberFromUsageFixes(qtype, reference, ref_text, actions);
-              if (qtype instanceof PyClassTypeImpl) {
+              addCreateMemberFromUsageFixes(type, reference, ref_text, actions);
+              if (type instanceof PyClassTypeImpl) {
                 if (reference instanceof PyOperatorReference) {
                   description = PyBundle.message("INSP.unresolved.operator.ref",
-                                                 qtype.getName(), refname,
+                                                 type.getName(), refName,
                                                  ((PyOperatorReference)reference).getReadableOperatorName());
                 }
                 else {
-                  description = PyBundle.message("INSP.unresolved.ref.$0.for.class.$1", ref_text, qtype.getName());
+                  description = PyBundle.message("INSP.unresolved.ref.$0.for.class.$1", ref_text, type.getName());
                 }
                 marked_qualified = true;
               }
               else {
-                description = PyBundle.message("INSP.cannot.find.$0.in.$1", ref_text, qtype.getName());
+                description = PyBundle.message("INSP.cannot.find.$0.in.$1", ref_text, type.getName());
                 marked_qualified = true;
               }
             }
@@ -559,20 +576,9 @@
       }
       addPluginQuickFixes(reference, actions);
 
-      final PsiElement point;
-      final TextRange range;
-      final PsiElement lastChild = node.getLastChild();
-      if (reference instanceof PyOperatorReference || lastChild == null) {
-        point = node;
-        range = rangeInElement;
-      }
-      else {
-        point = lastChild; // usually the identifier at the end of qual ref
-        range = rangeInElement.shiftRight(-point.getStartOffsetInParent());
-      }
-      if (reference instanceof PyImportReference && refname != null) {
+      if (reference instanceof PyImportReference) {
         // TODO: Ignore references in the second part of the 'from ... import ...' expression
-        final QualifiedName qname = QualifiedName.fromDottedString(refname);
+        final QualifiedName qname = QualifiedName.fromDottedString(refName);
         final List<String> components = qname.getComponents();
         if (!components.isEmpty()) {
           final String packageName = components.get(0);
@@ -589,7 +595,7 @@
           }
         }
       }
-      registerProblem(point, description, hl_type, null, range, actions.toArray(new LocalQuickFix[actions.size()]));
+      registerProblem(node, description, hl_type, null, rangeInElement, actions.toArray(new LocalQuickFix[actions.size()]));
     }
 
     /**
@@ -663,23 +669,23 @@
       return null;
     }
 
-    private boolean ignoreUnresolvedMemberForType(@NotNull PyType qtype, PsiReference reference, String name) {
-      if (qtype instanceof PyNoneType || PyTypeChecker.isUnknown(qtype)) {
+    private boolean ignoreUnresolvedMemberForType(@NotNull PyType type, PsiReference reference, String name) {
+      if (type instanceof PyNoneType || PyTypeChecker.isUnknown(type)) {
         // this almost always means that we don't know the type, so don't show an error in this case
         return true;
       }
-      if (qtype instanceof PyImportedModuleType) {
-        PyImportedModule module = ((PyImportedModuleType)qtype).getImportedModule();
+      if (type instanceof PyImportedModuleType) {
+        PyImportedModule module = ((PyImportedModuleType)type).getImportedModule();
         if (module.resolve() == null) {
           return true;
         }
       }
-      if (qtype instanceof PyClassTypeImpl) {
-        PyClass cls = ((PyClassType)qtype).getPyClass();
+      if (type instanceof PyClassTypeImpl) {
+        PyClass cls = ((PyClassType)type).getPyClass();
         if (overridesGetAttr(cls, myTypeEvalContext)) {
           return true;
         }
-        if (cls.findProperty(name) != null) {
+        if (cls.findProperty(name, true) != null) {
           return true;
         }
         if (PyUtil.hasUnresolvedAncestors(cls, myTypeEvalContext)) {
@@ -688,27 +694,27 @@
         if (isDecoratedAsDynamic(cls, true)) {
           return true;
         }
-        if (hasUnresolvedDynamicMember((PyClassType)qtype, reference, name)) return true;
+        if (hasUnresolvedDynamicMember((PyClassType)type, reference, name)) return true;
       }
-      if (qtype instanceof PyFunctionType) {
-        final Callable callable = ((PyFunctionType)qtype).getCallable();
+      if (type instanceof PyFunctionType) {
+        final Callable callable = ((PyFunctionType)type).getCallable();
         if (callable instanceof PyFunction && ((PyFunction)callable).getDecoratorList() != null) {
           return true;
         }
       }
       for (PyInspectionExtension extension : Extensions.getExtensions(PyInspectionExtension.EP_NAME)) {
-        if (extension.ignoreUnresolvedMember(qtype, name)) {
+        if (extension.ignoreUnresolvedMember(type, name)) {
           return true;
         }
       }
       return false;
     }
 
-    private static boolean hasUnresolvedDynamicMember(@NotNull final PyClassType qtype,
+    private static boolean hasUnresolvedDynamicMember(@NotNull final PyClassType type,
                                                       PsiReference reference,
                                                       @NotNull final String name) {
       for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) {
-        final Collection<PyDynamicMember> resolveResult = provider.getMembers(qtype, reference.getElement());
+        final Collection<PyDynamicMember> resolveResult = provider.getMembers(type, reference.getElement());
         for (PyDynamicMember member : resolveResult) {
           if (member.getName().equals(name)) return true;
         }
@@ -739,26 +745,26 @@
       return false;
     }
 
-    private static void addCreateMemberFromUsageFixes(PyType qtype, PsiReference reference, String refText, List<LocalQuickFix> actions) {
+    private static void addCreateMemberFromUsageFixes(PyType type, PsiReference reference, String refText, List<LocalQuickFix> actions) {
       PsiElement element = reference.getElement();
-      if (qtype instanceof PyClassTypeImpl) {
-        PyClass cls = ((PyClassType)qtype).getPyClass();
+      if (type instanceof PyClassTypeImpl) {
+        PyClass cls = ((PyClassType)type).getPyClass();
         if (!PyBuiltinCache.getInstance(element).hasInBuiltins(cls)) {
           if (element.getParent() instanceof PyCallExpression) {
-            actions.add(new AddMethodQuickFix(refText, (PyClassType)qtype, true));
+            actions.add(new AddMethodQuickFix(refText, (PyClassType)type, true));
           }
           else if (!(reference instanceof PyOperatorReference)) {
-            actions.add(new AddFieldQuickFix(refText, (PyClassType)qtype, "None"));
+            actions.add(new AddFieldQuickFix(refText, (PyClassType)type, "None"));
           }
         }
       }
-      else if (qtype instanceof PyModuleType) {
-        PyFile file = ((PyModuleType)qtype).getModule();
+      else if (type instanceof PyModuleType) {
+        PyFile file = ((PyModuleType)type).getModule();
         actions.add(new AddFunctionQuickFix(refText, file));
       }
     }
 
-    private void addAddSelfFix(PyElement node, PyReferenceExpression refex, List<LocalQuickFix> actions) {
+    private void addAddSelfFix(PyElement node, PyReferenceExpression expr, List<LocalQuickFix> actions) {
       final PyClass containedClass = PsiTreeUtil.getParentOfType(node, PyClass.class);
       final PyFunction function = PsiTreeUtil.getParentOfType(node, PyFunction.class);
       if (containedClass != null && function != null) {
@@ -766,29 +772,29 @@
         if (parameters.length == 0) return;
         final String qualifier = parameters[0].getText();
         final PyDecoratorList decoratorList = function.getDecoratorList();
-        boolean isClassmethod = false;
+        boolean isClassMethod = false;
         if (decoratorList != null) {
           for (PyDecorator decorator : decoratorList.getDecorators()) {
             final PyExpression callee = decorator.getCallee();
             if (callee != null && PyNames.CLASSMETHOD.equals(callee.getText()))
-              isClassmethod = true;
+              isClassMethod = true;
           }
         }
         for (PyTargetExpression target : containedClass.getInstanceAttributes()) {
-          if (!isClassmethod && Comparing.strEqual(node.getName(), target.getName())) {
-            actions.add(new UnresolvedReferenceAddSelfQuickFix(refex, qualifier));
+          if (!isClassMethod && Comparing.strEqual(node.getName(), target.getName())) {
+            actions.add(new UnresolvedReferenceAddSelfQuickFix(expr, qualifier));
           }
         }
         for (PyStatement statement : containedClass.getStatementList().getStatements()) {
           if (statement instanceof PyAssignmentStatement) {
             PyExpression lhsExpression = ((PyAssignmentStatement)statement).getLeftHandSideExpression();
-            if (lhsExpression != null && lhsExpression.getText().equals(refex.getText())) {
-              PyExpression callexpr = ((PyAssignmentStatement)statement).getAssignedValue();
-              if (callexpr instanceof PyCallExpression) {
-                PyType type = myTypeEvalContext.getType(callexpr);
+            if (lhsExpression != null && lhsExpression.getText().equals(expr.getText())) {
+              PyExpression assignedValue = ((PyAssignmentStatement)statement).getAssignedValue();
+              if (assignedValue instanceof PyCallExpression) {
+                PyType type = myTypeEvalContext.getType(assignedValue);
                 if (type != null && type instanceof PyClassTypeImpl) {
-                  if (((PyCallExpression)callexpr).isCalleeText(PyNames.PROPERTY)) {
-                    actions.add(new UnresolvedReferenceAddSelfQuickFix(refex, qualifier));
+                  if (((PyCallExpression)assignedValue).isCalleeText(PyNames.PROPERTY)) {
+                    actions.add(new UnresolvedReferenceAddSelfQuickFix(expr, qualifier));
                   }
                 }
               }
@@ -796,8 +802,8 @@
           }
         }
         for (PyFunction method : containedClass.getMethods()) {
-          if (refex.getText().equals(method.getName())) {
-            actions.add(new UnresolvedReferenceAddSelfQuickFix(refex, qualifier));
+          if (expr.getText().equals(method.getName())) {
+            actions.add(new UnresolvedReferenceAddSelfQuickFix(expr, qualifier));
           }
         }
       }
@@ -839,9 +845,9 @@
           PsiTreeUtil.getParentOfType(element, PyImportStatementBase.class) == null) {
         PsiElement anchor = element;
         if (element instanceof PyQualifiedExpression) {
-          final PyExpression qexpr = ((PyQualifiedExpression)element).getQualifier();
-          if (qexpr != null) {
-            final PyType type = myTypeEvalContext.getType(qexpr);
+          final PyExpression expr = ((PyQualifiedExpression)element).getQualifier();
+          if (expr != null) {
+            final PyType type = myTypeEvalContext.getType(expr);
             if (type instanceof PyModuleType) {
               anchor = ((PyModuleType)type).getModule();
             }
@@ -909,7 +915,7 @@
       if (myAllImports.isEmpty()) {
         return Collections.emptyList();
       }
-      // PY-1315 Unused imports inspection shouldn't work in python repl console
+      // PY-1315 Unused imports inspection shouldn't work in python REPL console
       final NameDefiner first = myAllImports.iterator().next();
       if (first.getContainingFile() instanceof PyExpressionCodeFragment || PydevConsoleRunner.isInPydevConsole(first)) {
         return Collections.emptyList();
@@ -968,6 +974,10 @@
             final PyImportElement importElement = (PyImportElement)unusedImport;
             final PsiElement element = importElement.resolve();
             if (element == null) {
+              if (importElement.getImportedQName() != null) {
+                //Mark import as unused even if it can't be resolved
+                result.add(importElement.getParent());
+              }
               continue;
             }
             if (dunderAll != null && dunderAll.contains(importElement.getVisibleName())) {
diff --git a/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java b/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java
index f770c5d..f788353 100644
--- a/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java
+++ b/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java
@@ -207,7 +207,8 @@
       owner.acceptChildren(new PyRecursiveElementVisitor(){
         @Override
         public void visitPyCallExpression(final PyCallExpression node) {
-          if ("locals".equals(node.getCallee().getName())){
+          final PyExpression callee = node.getCallee();
+          if (callee != null && "locals".equals(callee.getName())){
             throw new DontPerformException();
           }
           node.acceptChildren(this); // look at call expr in arguments
@@ -290,7 +291,7 @@
                 isEmpty = isEmptyFunction(func);
                 emptyFunctions.put(func, isEmpty);
               }
-              if (isEmpty) {
+              if (isEmpty && !mayBeField) {
                 continue;
               }
             }
diff --git a/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java
index b3e9e2d..2306a47 100644
--- a/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java
+++ b/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java
@@ -149,15 +149,15 @@
     for (int i = 1; i != parameters.length; i++) {
       PyParameter p = parameters[i];
       if (p.getDefaultValue() != null) continue;
-      String param;
-      param = p.getText();
-      if (param.startsWith("**")) {
+      final String param = p.getName();
+      String paramText = p.getText();
+      if (paramText.startsWith("**")) {
         addDouble = true;
         if (doubleStarName == null)
           doubleStarName = p.getText();
         continue;
       }
-      if (param.startsWith("*")) {
+      if (paramText.startsWith("*")) {
         addStar = true;
         if (starName == null)
           starName = p.getText();
diff --git a/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java
index e888add..89de355 100644
--- a/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java
+++ b/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java
@@ -66,20 +66,18 @@
   @Nullable
   public static PsiElement appendToMethod(PyFunction init, Function<String, PyStatement> callback) {
     // add this field as the last stmt of the constructor
-    final PyStatementList stmt_list = init.getStatementList();
-    PyStatement[] stmts = stmt_list.getStatements(); // NOTE: rather wasteful, consider iterable stmt list
-    PyStatement last_stmt = null;
-    if (stmts.length > 0) last_stmt = stmts[stmts.length-1];
+    final PyStatementList statementList = init.getStatementList();
+    assert statementList != null;
     // name of 'self' may be different for fancier styles
     PyParameter[] params = init.getParameterList().getParameters();
-    String self_name = PyNames.CANONICAL_SELF;
+    String selfName = PyNames.CANONICAL_SELF;
     if (params.length > 0) {
-      self_name = params[0].getName();
+      selfName = params[0].getName();
     }
-    PyStatement new_stmt = callback.fun(self_name);
-    if (!FileModificationService.getInstance().preparePsiElementForWrite(stmt_list)) return null;
-    final PsiElement result = stmt_list.addAfter(new_stmt, last_stmt);
-    PyPsiUtils.removeRedundantPass(stmt_list);
+    PyStatement newStmt = callback.fun(selfName);
+    if (!FileModificationService.getInstance().preparePsiElementForWrite(statementList)) return null;
+    final PsiElement result = PyUtil.addElementToStatementList(newStmt, statementList, true);
+    PyPsiUtils.removeRedundantPass(statementList);
     return result;
   }
 
diff --git a/python/src/com/jetbrains/python/inspections/quickfix/AddFunctionQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/AddFunctionQuickFix.java
index fbd22d7..4d71b42 100644
--- a/python/src/com/jetbrains/python/inspections/quickfix/AddFunctionQuickFix.java
+++ b/python/src/com/jetbrains/python/inspections/quickfix/AddFunctionQuickFix.java
@@ -66,9 +66,7 @@
 
   public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
     try {
-      // descriptor points to the unresolved identifier
-      // there can be no name clash, else the name would have resolved, and it hasn't.
-      PsiElement problem_elt = descriptor.getPsiElement().getParent(); // id -> ref expr
+      final PsiElement problem_elt = descriptor.getPsiElement();
       sure(myPyFile);
       sure(FileModificationService.getInstance().preparePsiElementForWrite(myPyFile));
       // try to at least match parameter count
diff --git a/python/src/com/jetbrains/python/inspections/quickfix/AddGlobalQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/AddGlobalQuickFix.java
index 349a911..9bf3df0 100644
--- a/python/src/com/jetbrains/python/inspections/quickfix/AddGlobalQuickFix.java
+++ b/python/src/com/jetbrains/python/inspections/quickfix/AddGlobalQuickFix.java
@@ -80,6 +80,7 @@
         statementList = ((PyFunction)owner).getStatementList();
         if (((PyFunction)owner).getDocStringExpression() != null) hasDocString = true;
       }
+      assert statementList != null;
       PyStatement first = statementList.getStatements()[0];
       if (hasDocString)
         first = statementList.getStatements()[1];
diff --git a/python/src/com/jetbrains/python/inspections/quickfix/AddMethodQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/AddMethodQuickFix.java
index f2246b1..30d9910 100644
--- a/python/src/com/jetbrains/python/inspections/quickfix/AddMethodQuickFix.java
+++ b/python/src/com/jetbrains/python/inspections/quickfix/AddMethodQuickFix.java
@@ -67,9 +67,8 @@
 
   public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
     try {
-      // descriptor points to the unresolved identifier
       // there can be no name clash, else the name would have resolved, and it hasn't.
-      PsiElement problem_elt = descriptor.getPsiElement().getParent(); // id -> ref expr
+      PsiElement problem_elt = descriptor.getPsiElement();
       PyClass cls = myQualifierType.getPyClass();
       boolean call_by_class = myQualifierType.isDefinition();
       String item_name = myIdentifier;
diff --git a/python/src/com/jetbrains/python/inspections/quickfix/ConvertDictCompQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/ConvertDictCompQuickFix.java
index 45f20be..67a2bdd 100644
--- a/python/src/com/jetbrains/python/inspections/quickfix/ConvertDictCompQuickFix.java
+++ b/python/src/com/jetbrains/python/inspections/quickfix/ConvertDictCompQuickFix.java
@@ -52,16 +52,38 @@
   }
 
   private static void replaceComprehension(Project project, PyDictCompExpression expression) {
-    List<ComprhForComponent> forComponents = expression.getForComponents();
     if (expression.getResultExpression() instanceof PyKeyValueExpression) {
-      PyKeyValueExpression keyValueExpression = (PyKeyValueExpression)expression.getResultExpression();
-      PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project);
+      final PyKeyValueExpression keyValueExpression = (PyKeyValueExpression)expression.getResultExpression();
+      final PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project);
       assert keyValueExpression.getValue() != null;
-      expression.replace(elementGenerator.createFromText(LanguageLevel.getDefault(), PyExpressionStatement.class,
-                                                         "dict([(" + keyValueExpression.getKey().getText() + ", " +
-                                                         keyValueExpression.getValue().getText() + ") for " +
-                                                         forComponents.get(0).getIteratorVariable().getText() + " in " +
-                                                         forComponents.get(0).getIteratedList().getText() + "])"));
+
+      final List<ComprehensionComponent> components = expression.getComponents();
+      final StringBuilder replacement = new StringBuilder("dict([(" + keyValueExpression.getKey().getText() + ", " +
+                                                            keyValueExpression.getValue().getText() + ")");
+      int slashNum = 1;
+      for (ComprehensionComponent component : components) {
+        if (component instanceof ComprhForComponent) {
+          replacement.append("for ");
+          replacement.append(((ComprhForComponent)component).getIteratorVariable().getText());
+          replacement.append(" in ");
+          replacement.append(((ComprhForComponent)component).getIteratedList().getText());
+          replacement.append(" ");
+        }
+        if (component instanceof ComprhIfComponent) {
+          final PyExpression test = ((ComprhIfComponent)component).getTest();
+          if (test != null) {
+            replacement.append("if ");
+            replacement.append(test.getText());
+            replacement.append(" ");
+          }
+        }
+        for (int i = 0; i != slashNum; ++i)
+          replacement.append("\t");
+        ++slashNum;
+      }
+      replacement.append("])");
+
+      expression.replace(elementGenerator.createFromText(LanguageLevel.getDefault(), PyExpressionStatement.class, replacement.toString()));
     }
   }
 
diff --git a/python/src/com/jetbrains/python/inspections/quickfix/GenerateBinaryStubsFix.java b/python/src/com/jetbrains/python/inspections/quickfix/GenerateBinaryStubsFix.java
index c6d670c..5dd76ec 100644
--- a/python/src/com/jetbrains/python/inspections/quickfix/GenerateBinaryStubsFix.java
+++ b/python/src/com/jetbrains/python/inspections/quickfix/GenerateBinaryStubsFix.java
@@ -19,7 +19,7 @@
 import com.intellij.codeInspection.LocalQuickFix;
 import com.intellij.codeInspection.ProblemDescriptor;
 import com.intellij.execution.process.ProcessOutput;
-import com.intellij.notification.Notification;
+import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.module.ModuleUtilCore;
 import com.intellij.openapi.progress.ProgressIndicator;
@@ -34,10 +34,10 @@
 import com.intellij.psi.PsiFile;
 import com.intellij.psi.PsiReference;
 import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.psi.util.QualifiedName;
 import com.intellij.util.Consumer;
 import com.jetbrains.python.PythonHelpersLocator;
 import com.jetbrains.python.psi.*;
-import com.intellij.psi.util.QualifiedName;
 import com.jetbrains.python.psi.impl.references.PyImportReference;
 import com.jetbrains.python.sdk.InvalidSdkException;
 import com.jetbrains.python.sdk.PySdkUtil;
@@ -58,6 +58,8 @@
  * @author yole
  */
 public class GenerateBinaryStubsFix implements LocalQuickFix {
+  private static final Logger LOG = Logger.getInstance("#" + GenerateBinaryStubsFix.class.getName());
+
   private String myQualifiedName;
   private Sdk mySdk;
 
@@ -119,8 +121,7 @@
           }
         }
         catch (InvalidSdkException e) {
-          final Notification notification = PythonSdkType.createInvalidSdkNotification(project);
-          notification.notify(project);
+          LOG.error(e);
         }
       }
     };
diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyRenameUnresolvedRefQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyRenameUnresolvedRefQuickFix.java
index 4726aaae..ec198aa 100644
--- a/python/src/com/jetbrains/python/inspections/quickfix/PyRenameUnresolvedRefQuickFix.java
+++ b/python/src/com/jetbrains/python/inspections/quickfix/PyRenameUnresolvedRefQuickFix.java
@@ -30,7 +30,6 @@
 import com.intellij.psi.PsiFile;
 import com.intellij.psi.PsiReference;
 import com.intellij.psi.ResolveResult;
-import com.intellij.psi.util.PsiTreeUtil;
 import com.jetbrains.python.PyBundle;
 import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
 import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
@@ -65,8 +64,10 @@
   @Override
   public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
     final PsiElement element = descriptor.getPsiElement();
-    final PyReferenceExpression referenceExpression = PsiTreeUtil.getParentOfType(element, PyReferenceExpression.class);
-    if (referenceExpression == null) return;
+    if (!(element instanceof PyReferenceExpression)) {
+      return;
+    }
+    final PyReferenceExpression referenceExpression = (PyReferenceExpression)element;
 
     ScopeOwner parentScope = ScopeUtil.getScopeOwner(referenceExpression);
     if (parentScope == null) return;
diff --git a/python/src/com/jetbrains/python/inspections/quickfix/StatementEffectDocstringQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/StatementEffectDocstringQuickFix.java
deleted file mode 100644
index fa944a9..0000000
--- a/python/src/com/jetbrains/python/inspections/quickfix/StatementEffectDocstringQuickFix.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2000-2013 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jetbrains.python.inspections.quickfix;
-
-import com.intellij.codeInspection.LocalQuickFix;
-import com.intellij.codeInspection.ProblemDescriptor;
-import com.intellij.openapi.project.Project;
-import com.intellij.psi.PsiElement;
-import com.intellij.psi.util.PsiTreeUtil;
-import com.jetbrains.python.PyBundle;
-import com.jetbrains.python.psi.*;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * User: catherine
- *
- * QuickFix to move misplaced docstring
- */
-public class StatementEffectDocstringQuickFix implements LocalQuickFix {
-  @NotNull
-  public String getName() {
-    return PyBundle.message("QFIX.statement.effect.move.docstring");
-  }
-
-  @NotNull
-  public String getFamilyName() {
-    return getName();
-  }
-
-  public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
-    PsiElement expression = descriptor.getPsiElement();
-    if (expression instanceof PyStringLiteralExpression) {
-      PyStatement st = PsiTreeUtil.getParentOfType(expression, PyStatement.class);
-      if (st != null) {
-        PyDocStringOwner parent = PsiTreeUtil.getParentOfType(expression, PyDocStringOwner.class);
-
-        if (parent instanceof PyClass || parent instanceof PyFunction) {
-          PyStatementList statementList = PsiTreeUtil.findChildOfType(parent, PyStatementList.class);
-          if (statementList != null) {
-            PyStatement[] statements = statementList.getStatements();
-            if (statements.length > 0) {
-              statementList.addBefore(st, statements[0]);
-              st.delete();
-            }
-          }
-        }
-      }
-    }
-  }
-
-}
diff --git a/python/src/com/jetbrains/python/lexer/PyStringLiteralLexer.java b/python/src/com/jetbrains/python/lexer/PyStringLiteralLexer.java
index 10c8c9b..4ed6435 100644
--- a/python/src/com/jetbrains/python/lexer/PyStringLiteralLexer.java
+++ b/python/src/com/jetbrains/python/lexer/PyStringLiteralLexer.java
@@ -57,7 +57,6 @@
    */
   public PyStringLiteralLexer(final IElementType originalLiteralToken) {
     myOriginalLiteralToken = originalLiteralToken;
-    myIsTriple = PyTokenTypes.TRIPLE_NODES.contains(myOriginalLiteralToken);
   }
 
   public void start(@NotNull CharSequence buffer, int startOffset, int endOffset, int initialState) {
@@ -85,6 +84,8 @@
     assert (c == '"') || (c == '\'') : "String must be quoted by single or double quote. Found '" + c + "' in string " + buffer;
     myQuoteChar = c;
 
+    myIsTriple = (buffer.length() > i + 2) && (buffer.charAt(i + 1) == c) && (buffer.charAt(i + 2) == c);
+
     // calculate myEnd at last
     myEnd = locateToken(myStart);
   }
diff --git a/python/src/com/jetbrains/python/lexer/PythonEditorHighlighter.java b/python/src/com/jetbrains/python/lexer/PythonEditorHighlighter.java
index d0e3de6..9893d4f 100644
--- a/python/src/com/jetbrains/python/lexer/PythonEditorHighlighter.java
+++ b/python/src/com/jetbrains/python/lexer/PythonEditorHighlighter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -80,7 +80,7 @@
   }
 
   @Override
-  public void setEditor(HighlighterClient editor) {
+  public void setEditor(@NotNull HighlighterClient editor) {
     Lexer l = getLexer();
     if (l instanceof LayeredLexer) {
       editor.getDocument().putUserData(KEY, editor.getDocument().getText().indexOf(PyNames.UNICODE_LITERALS) == -1);
diff --git a/python/src/com/jetbrains/python/lexer/PythonIndentingProcessor.java b/python/src/com/jetbrains/python/lexer/PythonIndentingProcessor.java
index 546724f..29fb657 100644
--- a/python/src/com/jetbrains/python/lexer/PythonIndentingProcessor.java
+++ b/python/src/com/jetbrains/python/lexer/PythonIndentingProcessor.java
@@ -194,9 +194,6 @@
     if (DUMP_TOKENS) {
       System.out.println("\n--- LEXER START---");
     }
-    if (startOffset != 0 || initialState != 0) {
-      throw new RuntimeException("Indenting lexer does not support incremental lexing");
-    }
   }
 
   private void setStartState() {
diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java
index fecce4f..82a5fd9 100644
--- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java
+++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -50,7 +50,7 @@
 import com.intellij.openapi.vfs.newvfs.BulkFileListener;
 import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
 import com.intellij.remotesdk.RemoteFile;
-import com.intellij.remotesdk.RemoteSdkData;
+import com.intellij.remotesdk.RemoteSdkCredentials;
 import com.intellij.util.ArrayUtil;
 import com.intellij.util.Function;
 import com.intellij.util.SystemProperties;
@@ -261,7 +261,7 @@
             }
           }, ModalityState.current());
         }
-        if (warning[0] != 0) return true;
+        if (warning[0] != Messages.YES) return true;
       }
       catch (PyExternalProcessException e) {
         LOG.info("Error loading packages dependents: " + e.getMessage(), e);
@@ -752,10 +752,10 @@
   private String getHelperPath(String helper) {
     String helperPath;
     final SdkAdditionalData sdkData = mySdk.getSdkAdditionalData();
-    if (sdkData instanceof RemoteSdkData) {
-      final RemoteSdkData remoteSdkData = (RemoteSdkData)sdkData;
-      if (!StringUtil.isEmpty(remoteSdkData.getHelpersPath())) {
-        helperPath = new RemoteFile(remoteSdkData.getHelpersPath(),
+    if (sdkData instanceof RemoteSdkCredentials) {
+      final RemoteSdkCredentials remoteSdkCredentials = (RemoteSdkCredentials)sdkData;
+      if (!StringUtil.isEmpty(remoteSdkCredentials.getHelpersPath())) {
+        helperPath = new RemoteFile(remoteSdkCredentials.getHelpersPath(),
                                     helper).getPath();
       }
       else {
@@ -778,8 +778,8 @@
     if (homePath == null) {
       throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args, "Cannot find interpreter for SDK");
     }
-    if (sdkData instanceof RemoteSdkData) { //remote interpreter
-      final RemoteSdkData remoteSdkData = (RemoteSdkData)sdkData;
+    if (sdkData instanceof RemoteSdkCredentials) { //remote interpreter
+      final RemoteSdkCredentials remoteSdkCredentials = (RemoteSdkCredentials)sdkData;
       final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
       if (manager != null) {
         final List<String> cmdline = new ArrayList<String>();
@@ -793,11 +793,11 @@
         }));
         try {
           if (askForSudo) {
-            askForSudo = !manager.ensureCanWrite(null, remoteSdkData, remoteSdkData.getInterpreterPath());
+            askForSudo = !manager.ensureCanWrite(null, remoteSdkCredentials, remoteSdkCredentials.getInterpreterPath());
           }
           ProcessOutput processOutput;
           do {
-            processOutput = manager.runRemoteProcess(null, remoteSdkData, ArrayUtil.toStringArray(cmdline), workingDir, askForSudo);
+            processOutput = manager.runRemoteProcess(null, remoteSdkCredentials, ArrayUtil.toStringArray(cmdline), workingDir, askForSudo);
             if (askForSudo && processOutput.getStderr().contains("sudo: 3 incorrect password attempts")) {
               continue;
             }
diff --git a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java
index 3f30b1d..d894786 100644
--- a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java
+++ b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java
@@ -34,6 +34,7 @@
 import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
 import org.jetbrains.annotations.NotNull;
 
+import java.awt.*;
 import java.util.List;
 import java.util.Set;
 
@@ -50,6 +51,7 @@
 
   public PyInstalledPackagesPanel(Project project, PackagesNotificationPanel area) {
     super(project, area);
+    setPreferredSize(new Dimension(500, 500));
 
     myNotificationArea.addLinkHandler(INSTALL_SETUPTOOLS, new Runnable() {
       @Override
diff --git a/python/src/com/jetbrains/python/parsing/ExpressionParsing.java b/python/src/com/jetbrains/python/parsing/ExpressionParsing.java
index 65c791a..790b155 100644
--- a/python/src/com/jetbrains/python/parsing/ExpressionParsing.java
+++ b/python/src/com/jetbrains/python/parsing/ExpressionParsing.java
@@ -236,7 +236,7 @@
           break;
         }
       }
-      myBuilder.advanceLexer();
+      checkMatches(PyTokenTypes.RBRACE, message("PARSE.expected.rbrace"));
       startMarker.done(PyElementTypes.DICT_LITERAL_EXPRESSION);
     }
   }
@@ -264,7 +264,7 @@
         break;
       }
     }
-    myBuilder.advanceLexer();
+    checkMatches(PyTokenTypes.RBRACE, message("PARSE.expected.rbrace"));
     startMarker.done(PyElementTypes.SET_LITERAL_EXPRESSION);
   }
 
diff --git a/python/src/com/jetbrains/python/parsing/FollowingCommentBinder.java b/python/src/com/jetbrains/python/parsing/FollowingCommentBinder.java
index 0df9269..6133be9 100644
--- a/python/src/com/jetbrains/python/parsing/FollowingCommentBinder.java
+++ b/python/src/com/jetbrains/python/parsing/FollowingCommentBinder.java
@@ -29,6 +29,7 @@
 
   @Override
   public int getEdgePosition(List<IElementType> tokens, boolean atStreamEdge, TokenTextGetter getter) {
+    if (tokens.size() <= 1) return 0;
     int pos = 0;
     // TODO[yole] handle more cases?
     while (pos < tokens.size() && tokens.get(pos) == PyTokenTypes.LINE_BREAK) {
diff --git a/python/src/com/jetbrains/python/parsing/FunctionParsing.java b/python/src/com/jetbrains/python/parsing/FunctionParsing.java
index 768fa0a..3fb67a8 100644
--- a/python/src/com/jetbrains/python/parsing/FunctionParsing.java
+++ b/python/src/com/jetbrains/python/parsing/FunctionParsing.java
@@ -16,6 +16,7 @@
 package com.jetbrains.python.parsing;
 
 import com.intellij.lang.PsiBuilder;
+import com.intellij.lang.WhitespacesBinders;
 import com.intellij.psi.tree.IElementType;
 import com.jetbrains.python.PyElementTypes;
 import com.jetbrains.python.PyTokenTypes;
@@ -44,7 +45,7 @@
 
   protected void parseFunctionInnards(PsiBuilder.Marker functionMarker) {
     myBuilder.advanceLexer();
-    checkMatchesOrSkip(PyTokenTypes.IDENTIFIER, message("PARSE.expected.func.name"));
+    parseIdentifierOrSkip();
     parseParameterList();
     parseReturnTypeAnnotation();
     checkMatches(PyTokenTypes.COLON, message("PARSE.expected.colon"));
@@ -81,7 +82,7 @@
       }
       else { // empty arglist node, so we always have it
         PsiBuilder.Marker argListMarker = myBuilder.mark();
-        argListMarker.setCustomEdgeTokenBinders(LeftBiasedWhitespaceBinder.INSTANCE, null);
+        argListMarker.setCustomEdgeTokenBinders(WhitespacesBinders.GREEDY_LEFT_BINDER, null);
         argListMarker.done(PyElementTypes.ARGUMENT_LIST);
       }
       if (atToken(PyTokenTypes.STATEMENT_BREAK)) {
@@ -110,6 +111,7 @@
       myBuilder.error(message("[email protected]"));
       PsiBuilder.Marker parameterList = myBuilder.mark(); // To have non-empty parameters list at all the time.
       parameterList.done(PyElementTypes.PARAMETER_LIST);
+      myBuilder.mark().done(PyElementTypes.STATEMENT_LIST); // To have non-empty empty statement list
       endMarker.done(getFunctionType());
     }
   }
diff --git a/python/src/com/jetbrains/python/parsing/LeftBiasedWhitespaceBinder.java b/python/src/com/jetbrains/python/parsing/LeadingCommentsBinder.java
similarity index 61%
rename from python/src/com/jetbrains/python/parsing/LeftBiasedWhitespaceBinder.java
rename to python/src/com/jetbrains/python/parsing/LeadingCommentsBinder.java
index 38bd079..dde4c7a 100644
--- a/python/src/com/jetbrains/python/parsing/LeftBiasedWhitespaceBinder.java
+++ b/python/src/com/jetbrains/python/parsing/LeadingCommentsBinder.java
@@ -17,17 +17,30 @@
 
 import com.intellij.lang.WhitespacesAndCommentsBinder;
 import com.intellij.psi.tree.IElementType;
+import com.jetbrains.python.PyTokenTypes;
 
 import java.util.List;
 
 /**
  * @author yole
  */
-public class LeftBiasedWhitespaceBinder implements WhitespacesAndCommentsBinder {
-  public static LeftBiasedWhitespaceBinder INSTANCE = new LeftBiasedWhitespaceBinder();
+public class LeadingCommentsBinder implements WhitespacesAndCommentsBinder {
+  public static final LeadingCommentsBinder INSTANCE = new LeadingCommentsBinder();
 
   @Override
   public int getEdgePosition(List<IElementType> tokens, boolean atStreamEdge, TokenTextGetter getter) {
-    return 0;
+    if (tokens.size() > 1) {
+      boolean seenLF = false;
+      for (int i = 0; i < tokens.size(); i++) {
+        IElementType token = tokens.get(i);
+        if (token == PyTokenTypes.LINE_BREAK) {
+          seenLF = true;
+        }
+        else if (token == PyTokenTypes.END_OF_LINE_COMMENT && seenLF) {
+          return i;
+        }
+      }
+    }
+    return tokens.size();
   }
 }
diff --git a/python/src/com/jetbrains/python/parsing/Parsing.java b/python/src/com/jetbrains/python/parsing/Parsing.java
index 99eab6f..5550589 100644
--- a/python/src/com/jetbrains/python/parsing/Parsing.java
+++ b/python/src/com/jetbrains/python/parsing/Parsing.java
@@ -18,7 +18,9 @@
 import com.intellij.lang.PsiBuilder;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.psi.tree.IElementType;
+import com.jetbrains.python.PyBundle;
 import com.jetbrains.python.PyElementTypes;
+import com.jetbrains.python.PyTokenTypes;
 import com.jetbrains.python.psi.PyElementType;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -61,15 +63,19 @@
     return false;
   }
 
-  protected boolean checkMatchesOrSkip(final IElementType token, final String message) {
-    if (myBuilder.getTokenType() == token) {
+  protected boolean parseIdentifierOrSkip() {
+    if (myBuilder.getTokenType() == PyTokenTypes.IDENTIFIER) {
       myBuilder.advanceLexer();
       return true;
     }
-    PsiBuilder.Marker mark = myBuilder.mark();
-    myBuilder.advanceLexer();
-    mark.error(message);
-    return false;
+    else {
+      final PsiBuilder.Marker nameExpected = myBuilder.mark();
+      if (myBuilder.getTokenType() != PyTokenTypes.STATEMENT_BREAK) {
+        myBuilder.advanceLexer();
+      }
+      nameExpected.error(PyBundle.message("PARSE.expected.identifier"));
+      return false;
+    }
   }
 
   protected void assertCurrentToken(final PyElementType tokenType) {
@@ -95,7 +101,7 @@
   protected boolean matchToken(final IElementType tokenType) {
     if (myBuilder.getTokenType() == tokenType) {
       myBuilder.advanceLexer();
-      return true;      
+      return true;
     }
     return false;
   }
diff --git a/python/src/com/jetbrains/python/parsing/StatementParsing.java b/python/src/com/jetbrains/python/parsing/StatementParsing.java
index 518caa7..9c5a9d8 100644
--- a/python/src/com/jetbrains/python/parsing/StatementParsing.java
+++ b/python/src/com/jetbrains/python/parsing/StatementParsing.java
@@ -631,6 +631,10 @@
       myBuilder.advanceLexer();
       return true;
     }
+    else if (myBuilder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) {
+      myBuilder.error("Colon expected");
+      return true;
+    }
     final PsiBuilder.Marker marker = myBuilder.mark();
     while (!atAnyOfTokens(null, PyTokenTypes.DEDENT, PyTokenTypes.STATEMENT_BREAK, PyTokenTypes.COLON)) {
       myBuilder.advanceLexer();
@@ -767,7 +771,7 @@
       if (myBuilder.getTokenType() == PyTokenTypes.AS_KEYWORD) {
         myBuilder.advanceLexer();
         if (!getExpressionParser().parseSingleExpression(true)) {
-          myBuilder.error("identifier expected");
+          myBuilder.error("Identifier expected");
           // 'as' is followed by a target
         }
       }
@@ -788,7 +792,7 @@
   public void parseClassDeclaration(PsiBuilder.Marker classMarker, ParsingScope scope) {
     assertCurrentToken(PyTokenTypes.CLASS_KEYWORD);
     myBuilder.advanceLexer();
-    checkMatchesOrSkip(PyTokenTypes.IDENTIFIER, IDENTIFIER_EXPECTED);
+    parseIdentifierOrSkip();
     if (myBuilder.getTokenType() == PyTokenTypes.LPAR) {
       getExpressionParser().parseArgumentList();
     }
@@ -809,10 +813,8 @@
       myBuilder.advanceLexer();
 
       final PsiBuilder.Marker marker = myBuilder.mark();
-      if (myBuilder.getTokenType() != PyTokenTypes.INDENT) {
-        myBuilder.error("Indent expected");
-      }
-      else {
+      final boolean indentFound = myBuilder.getTokenType() == PyTokenTypes.INDENT;
+      if (indentFound) {
         myBuilder.advanceLexer();
         if (myBuilder.eof()) {
           myBuilder.error("Indented block expected");
@@ -823,13 +825,16 @@
           }
         }
       }
+      else {
+        myBuilder.error("Indent expected");
+      }
 
       marker.done(PyElementTypes.STATEMENT_LIST);
-      marker.setCustomEdgeTokenBinders(null, FollowingCommentBinder.INSTANCE);
+      marker.setCustomEdgeTokenBinders(LeadingCommentsBinder.INSTANCE, FollowingCommentBinder.INSTANCE);
       if (endMarker != null) {
         endMarker.done(elType);
       }
-      if (!myBuilder.eof()) {
+      if (indentFound && !myBuilder.eof()) {
         checkMatches(PyTokenTypes.DEDENT, "Dedent expected");
       }
       // NOTE: the following line advances the PsiBuilder lexer and thus
diff --git a/python/src/com/jetbrains/python/patterns/PythonPatterns.java b/python/src/com/jetbrains/python/patterns/PythonPatterns.java
index f4ce5e7..1ce1d75 100644
--- a/python/src/com/jetbrains/python/patterns/PythonPatterns.java
+++ b/python/src/com/jetbrains/python/patterns/PythonPatterns.java
@@ -22,11 +22,14 @@
 import com.intellij.psi.util.PsiTreeUtil;
 import com.intellij.util.ProcessingContext;
 import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
+import com.jetbrains.python.documentation.DocStringUtil;
 import com.jetbrains.python.psi.*;
 import com.jetbrains.python.psi.resolve.PyResolveContext;
 import com.jetbrains.python.psi.types.TypeEvalContext;
 import org.jetbrains.annotations.Nullable;
 
+import java.util.regex.Pattern;
+
 /**
  * @author yole
  */
@@ -39,6 +42,23 @@
     });
   }
 
+  public static PyElementPattern.Capture<PyStringLiteralExpression> pyStringLiteralMatches(final String regexp) {
+    final Pattern pattern = Pattern.compile(regexp, Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
+    return new PyElementPattern.Capture<PyStringLiteralExpression>(new InitialPatternCondition<PyStringLiteralExpression>(PyStringLiteralExpression.class) {
+      @Override
+      public boolean accepts(@Nullable Object o, ProcessingContext context) {
+        if (o instanceof PyStringLiteralExpression) {
+          final PyStringLiteralExpression expr = (PyStringLiteralExpression)o;
+          if (!DocStringUtil.isDocStringExpression(expr)) {
+            final String value = expr.getStringValue();
+            return pattern.matcher(value).matches();
+          }
+        }
+        return false;
+      }
+    });
+  }
+
   public static PyElementPattern.Capture<PyExpression> pyArgument(final String functionName, final int index) {
     return new PyElementPattern.Capture<PyExpression>(new InitialPatternCondition<PyExpression>(PyExpression.class) {
       public boolean accepts(@Nullable final Object o, final ProcessingContext context) {
diff --git a/python/src/com/jetbrains/python/projectView/PyTreeStructureProvider.java b/python/src/com/jetbrains/python/projectView/PyTreeStructureProvider.java
index ae923ed..cb9e257 100644
--- a/python/src/com/jetbrains/python/projectView/PyTreeStructureProvider.java
+++ b/python/src/com/jetbrains/python/projectView/PyTreeStructureProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -32,6 +32,7 @@
 import com.jetbrains.python.psi.PyFile;
 import com.jetbrains.python.psi.PyFunction;
 import com.jetbrains.python.sdk.PythonSdkType;
+import org.jetbrains.annotations.NotNull;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -41,8 +42,9 @@
  * @author yole
  */
 public class PyTreeStructureProvider implements SelectableTreeStructureProvider, DumbAware {
+  @NotNull
   @Override
-  public Collection<AbstractTreeNode> modify(AbstractTreeNode parent, Collection<AbstractTreeNode> children, ViewSettings settings) {
+  public Collection<AbstractTreeNode> modify(@NotNull AbstractTreeNode parent, @NotNull Collection<AbstractTreeNode> children, ViewSettings settings) {
     if (parent instanceof NamedLibraryElementNode) {
       return hideSkeletons((NamedLibraryElementNode)parent, children);
     }
diff --git a/python/src/com/jetbrains/python/psi/PyFileElementType.java b/python/src/com/jetbrains/python/psi/PyFileElementType.java
index 07d87c9..150745e 100644
--- a/python/src/com/jetbrains/python/psi/PyFileElementType.java
+++ b/python/src/com/jetbrains/python/psi/PyFileElementType.java
@@ -63,7 +63,7 @@
   @Override
   public int getStubVersion() {
     // Don't forget to update versions of indexes that use the updated stub-based elements
-    return 48;
+    return 49;
   }
 
   @Nullable
diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java
index e939857..83036ee 100644
--- a/python/src/com/jetbrains/python/psi/PyUtil.java
+++ b/python/src/com/jetbrains/python/psi/PyUtil.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
  */
 package com.jetbrains.python.psi;
 
+import com.google.common.collect.Collections2;
 import com.google.common.collect.Maps;
 import com.intellij.codeInsight.FileModificationService;
 import com.intellij.codeInsight.completion.PrioritizedLookupElement;
@@ -44,13 +45,14 @@
 import com.intellij.psi.stubs.StubElement;
 import com.intellij.psi.tree.TokenSet;
 import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.psi.util.QualifiedName;
 import com.intellij.ui.awt.RelativePoint;
 import com.intellij.util.ArrayUtil;
 import com.intellij.util.IncorrectOperationException;
 import com.intellij.util.PlatformIcons;
 import com.intellij.util.SmartList;
 import com.intellij.util.containers.ContainerUtil;
-import com.intellij.util.containers.HashSet;
+import com.jetbrains.NotNullPredicate;
 import com.jetbrains.python.PyBundle;
 import com.jetbrains.python.PyNames;
 import com.jetbrains.python.PyTokenTypes;
@@ -60,11 +62,10 @@
 import com.jetbrains.python.codeInsight.stdlib.PyNamedTupleType;
 import com.jetbrains.python.psi.impl.PyBuiltinCache;
 import com.jetbrains.python.psi.impl.PyPsiUtils;
-import com.intellij.psi.util.QualifiedName;
-import com.jetbrains.python.psi.resolve.PyResolveContext;
-import com.jetbrains.python.psi.resolve.QualifiedResolveResult;
 import com.jetbrains.python.psi.types.*;
+import com.jetbrains.python.refactoring.classes.PyDependenciesComparator;
 import com.jetbrains.python.refactoring.classes.extractSuperclass.PyExtractSuperclassHelper;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -106,21 +107,6 @@
     return node != null && node.getElementType().equals(TokenType.WHITE_SPACE);
   }
 
-
-  @NotNull
-  public static Set<PsiElement> getComments(PsiElement start) {
-    final Set<PsiElement> comments = new HashSet<PsiElement>();
-    PsiElement seeker = start.getPrevSibling();
-    if (seeker == null) seeker = start.getParent().getPrevSibling();
-    while (seeker instanceof PsiWhiteSpace || seeker instanceof PsiComment) {
-      if (seeker instanceof PsiComment) {
-        comments.add(seeker);
-      }
-      seeker = seeker.getPrevSibling();
-    }
-    return comments;
-  }
-
   @Nullable
   public static PsiElement getFirstNonCommentAfter(PsiElement start) {
     PsiElement seeker = start;
@@ -415,7 +401,7 @@
       PyExpression qualifier = ref.getQualifier();
       if (qualifier != null) {
         String attr_name = ref.getReferencedName();
-        if (PyNames.CLASS.equals(attr_name)) {
+        if (PyNames.__CLASS__.equals(attr_name)) {
           PyType qualifierType = context.getType(qualifier);
           if (qualifierType instanceof PyClassType) {
             return new PyClassTypeImpl(((PyClassType)qualifierType).getPyClass(), true); // always as class, never instance
@@ -700,14 +686,11 @@
       return false;
     }
     for (PyDecorator decorator : decoratorList.getDecorators()) {
-      final PyExpression callee = decorator.getCallee();
-      if (callee instanceof PyReferenceExpression) {
-        final PsiReference reference = callee.getReference();
-        if (reference == null) continue;
-        final PsiElement resolved = reference.resolve();
-        if (resolved instanceof PyQualifiedNameOwner) {
-          final String name = ((PyQualifiedNameOwner)resolved).getQualifiedName();
-          return PyNames.ABSTRACTMETHOD.equals(name) || PyNames.ABSTRACTPROPERTY.equals(name);
+      final QualifiedName qualifiedName = decorator.getQualifiedName();
+      if (qualifiedName != null) {
+        final String name = qualifiedName.toString();
+        if (name.endsWith(PyNames.ABSTRACTMETHOD) || name.endsWith(PyNames.ABSTRACTPROPERTY)) {
+          return true;
         }
       }
     }
@@ -718,6 +701,38 @@
     return PyElementGenerator.getInstance(element.getProject()).createNameIdentifier(name, LanguageLevel.forElement(element));
   }
 
+  /**
+   * Finds element declaration by resolving its references top the top but not further than file (to prevent unstubing)
+   * @param element element to resolve
+   * @return its declaration
+   */
+  @NotNull
+  public static PsiElement resolveToTheTop(@NotNull final PsiElement elementToResolve) {
+    PsiElement currentElement = elementToResolve;
+    while (true) {
+      final PsiReference reference = currentElement.getReference();
+      if (reference == null) {
+        break;
+      }
+      final PsiElement resolve = reference.resolve();
+      if ((resolve == null) || resolve.equals(currentElement) || !inSameFile(resolve, currentElement)) {
+        break;
+      }
+      currentElement = resolve;
+    }
+    return currentElement;
+  }
+
+  /**
+   * Gets class init method
+   * @param pyClass class where to find init
+   * @return class init method if any
+   */
+  @Nullable
+  public static PyFunction getInitMethod(@NotNull final PyClass pyClass) {
+    return pyClass.findMethodByName(PyNames.INIT, false);
+  }
+
   public static class KnownDecoratorProviderHolder {
     public static PyKnownDecoratorProvider[] KNOWN_DECORATOR_PROVIDERS = Extensions.getExtensions(PyKnownDecoratorProvider.EP_NAME);
 
@@ -920,6 +935,10 @@
     return selfName;
   }
 
+  /**
+   *
+   * @return Source roots <strong>and</strong> content roots for element's project
+   */
   @NotNull
   public static Collection<VirtualFile> getSourceRoots(@NotNull PsiElement foothold) {
     final Module module = ModuleUtilCore.findModuleForPsiElement(foothold);
@@ -929,6 +948,10 @@
     return Collections.emptyList();
   }
 
+  /**
+   *
+   * @return Source roots <strong>and</strong> content roots for module
+   */
   @NotNull
   public static Collection<VirtualFile> getSourceRoots(@NotNull Module module) {
     final Set<VirtualFile> result = new LinkedHashSet<VirtualFile>();
@@ -1097,6 +1120,11 @@
       }
       return null;
     }
+
+    //TODO: Doc
+    public boolean isInstanceMethod() {
+      return ! (myIsClassMethod || myIsStaticMethod);
+    }
   }
 
   public static boolean isSuperCall(@NotNull PyCallExpression node) {
@@ -1114,7 +1142,7 @@
         PyExpression[] args = node.getArguments();
         if (args.length > 0) {
           String firstArg = args[0].getText();
-          if (firstArg.equals(klass.getName()) || firstArg.equals(PyNames.CANONICAL_SELF + "." + PyNames.CLASS)) {
+          if (firstArg.equals(klass.getName()) || firstArg.equals(PyNames.CANONICAL_SELF + "." + PyNames.__CLASS__)) {
             return true;
           }
           for (PyClass s : klass.getAncestorClasses()) {
@@ -1204,49 +1232,6 @@
   }
 
   @Nullable
-  public static PyClass getMetaClass(@NotNull final PyClass pyClass) {
-    final PyTargetExpression metaClassAttribute = pyClass.findClassAttribute(PyNames.DUNDER_METACLASS, false);
-    if (metaClassAttribute != null) {
-      final PyExpression expression = metaClassAttribute.findAssignedValue();
-      final PyClass metaclass = getMetaFromExpression(expression);
-      if (metaclass != null) return metaclass;
-    }
-    final PsiFile containingFile = pyClass.getContainingFile();
-    if (containingFile instanceof PyFile) {
-      final PsiElement element = ((PyFile)containingFile).getElementNamed(PyNames.DUNDER_METACLASS);
-      if (element instanceof PyTargetExpression) {
-        final PyExpression expression = ((PyTargetExpression)element).findAssignedValue();
-        final PyClass metaclass = getMetaFromExpression(expression);
-        if (metaclass != null) return metaclass;
-      }
-    }
-
-    if (LanguageLevel.forElement(pyClass).isPy3K()) {
-      final PyExpression[] superClassExpressions = pyClass.getSuperClassExpressions();
-      for (PyExpression superClassExpression : superClassExpressions) {
-        if (superClassExpression instanceof PyKeywordArgument &&
-            PyNames.METACLASS.equals(((PyKeywordArgument)superClassExpression).getKeyword())) {
-          final PyExpression expression = ((PyKeywordArgument)superClassExpression).getValueExpression();
-          final PyClass metaclass = getMetaFromExpression(expression);
-          if (metaclass != null) return metaclass;
-        }
-      }
-    }
-    return null;
-  }
-
-  @Nullable
-  private static PyClass getMetaFromExpression(final PyExpression metaclass) {
-    if (metaclass instanceof PyReferenceExpression) {
-      final QualifiedResolveResult result = ((PyReferenceExpression)metaclass).followAssignmentsChain(PyResolveContext.noImplicits());
-      if (result.getElement() instanceof PyClass) {
-        return (PyClass)result.getElement();
-      }
-    }
-    return null;
-  }
-
-  @Nullable
   public static PsiElement findPrevAtOffset(PsiFile psiFile, int caretOffset, Class ... toSkip) {
     PsiElement element = psiFile.findElementAt(caretOffset);
     if (element == null || caretOffset < 0) {
@@ -1301,6 +1286,36 @@
     return instanceOf(element, toSkip) ? null : element;
   }
 
+  /**
+   * Adds element to statement list to the correct place according to its dependencies.
+   * @param element to insert
+   * @param statementList where element should be inserted
+   * @return inserted element
+   */
+  public static <T extends PyElement>T addElementToStatementList(@NotNull final T element,
+                                                     @NotNull final PyStatementList statementList) {
+    PsiElement before = null;
+    PsiElement after = null;
+    for (final PyStatement statement : statementList.getStatements()) {
+      if (PyDependenciesComparator.depends(element, statement)) {
+        after = statement;
+      }else if (PyDependenciesComparator.depends(statement, element)) {
+        before = statement;
+      }
+    }
+    final PsiElement result;
+    if (after != null) {
+
+      result = statementList.addAfter(element, after);
+    }else if (before != null) {
+      result = statementList.addBefore(element, before);
+    } else {
+      result = addElementToStatementList(element, statementList, true);
+    }
+    @SuppressWarnings("unchecked") // Inserted element can't have different type
+    final T resultCasted = (T)result;
+    return resultCasted;
+  }
 
   public static PsiElement addElementToStatementList(@NotNull PsiElement element,
                                                      @NotNull PyStatementList statementList,
@@ -1313,13 +1328,31 @@
       final PyStatement[] statements = statementList.getStatements();
       if (toTheBeginning && statements.length > 0) {
         final PyDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(statementList, PyDocStringOwner.class);
-        final PyStatement firstStatement = statements[0];
-        if (docStringOwner != null && firstStatement instanceof PyExpressionStatement &&
-            ((PyExpressionStatement)firstStatement).getExpression() == docStringOwner.getDocStringExpression()) {
-          element = statementList.addAfter(element, firstStatement);
+        PyStatement anchor = statements[0];
+        if (docStringOwner != null && anchor instanceof PyExpressionStatement &&
+            ((PyExpressionStatement)anchor).getExpression() == docStringOwner.getDocStringExpression()) {
+          final PyStatement next = PsiTreeUtil.getNextSiblingOfType(anchor, PyStatement.class);
+          if (next == null) {
+            return statementList.addAfter(element, anchor);
+          }
+          anchor = next;
         }
-        else
-          element = statementList.addBefore(element, firstStatement);
+        while (anchor instanceof PyExpressionStatement) {
+          final PyExpression expression = ((PyExpressionStatement)anchor).getExpression();
+          if (expression instanceof PyCallExpression) {
+            final PyExpression callee = ((PyCallExpression)expression).getCallee();
+            if ((isSuperCall((PyCallExpression)expression) || (callee != null && PyNames.INIT.equals(callee.getName())))) {
+              final PyStatement next = PsiTreeUtil.getNextSiblingOfType(anchor, PyStatement.class);
+              if (next == null) {
+                return statementList.addAfter(element, anchor);
+              }
+              anchor = next;
+            }
+            else break;
+          }
+          else break;
+        }
+        element = statementList.addBefore(element, anchor);
       }
       else {
         element = statementList.add(element);
@@ -1378,7 +1411,7 @@
   private static int optionalParametersCount(@NotNull List<PyParameter> parameters) {
     int n = 0;
     for (PyParameter parameter : parameters) {
-      if (parameter.getDefaultValue() != null) {
+      if (parameter.hasDefaultValue()) {
         n++;
       }
     }
@@ -1428,4 +1461,57 @@
     }
     return false;
   }
+
+  public static boolean isInit(@NotNull final PyFunction function) {
+    return PyNames.INIT.equals(function.getName());
+  }
+
+
+  private static boolean isObject(@NotNull final PyMemberInfo<PyElement> classMemberInfo) {
+    final PyElement element = classMemberInfo.getMember();
+    if ((element instanceof PyClass) && PyNames.OBJECT.equals(element.getName())) {
+      return true;
+    }
+    return false;
+
+  }
+
+  /**
+   * Filters out {@link com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo}
+   * that should not be displayed in this refactoring (like object)
+   *
+   * @param pyMemberInfos collection to sort
+   * @return sorted collection
+   */
+  @NotNull
+  public static Collection<PyMemberInfo<PyElement>> filterOutObject(@NotNull final Collection<PyMemberInfo<PyElement>> pyMemberInfos) {
+    return Collections2.filter(pyMemberInfos, new ObjectPredicate(false));
+  }
+
+  /**
+   * Filters only pyclass object (new class)
+   */
+  public static class ObjectPredicate extends NotNullPredicate<PyMemberInfo<PyElement>> {
+    private final boolean myAllowObjects;
+
+    /**
+     * @param allowObjects allows only objects if true. Allows all but objects otherwise.
+     */
+    public ObjectPredicate(final boolean allowObjects) {
+      myAllowObjects = allowObjects;
+    }
+
+    @Override
+    public boolean applyNotNull(@NotNull final PyMemberInfo<PyElement> input) {
+      return myAllowObjects == isObject(input);
+    }
+
+    private static boolean isObject(@NotNull final PyMemberInfo<PyElement> classMemberInfo) {
+      final PyElement element = classMemberInfo.getMember();
+      if ((element instanceof PyClass) && PyNames.OBJECT.equals(element.getName())) {
+        return true;
+      }
+      return false;
+    }
+  }
 }
diff --git a/python/src/com/jetbrains/python/psi/impl/PropertyBunch.java b/python/src/com/jetbrains/python/psi/impl/PropertyBunch.java
index 64f8986..59cc287 100644
--- a/python/src/com/jetbrains/python/psi/impl/PropertyBunch.java
+++ b/python/src/com/jetbrains/python/psi/impl/PropertyBunch.java
@@ -74,7 +74,7 @@
       PyExpression callee = call.getCallee();
       if (callee instanceof PyReferenceExpression) {
         PyReferenceExpression ref = (PyReferenceExpression)callee;
-        if (ref.getQualifier() != null) return null;
+        if (ref.isQualified()) return null;
         if (PyNames.PROPERTY.equals(callee.getName())) {
           PsiFile file = source.getContainingFile();
           if (isBuiltinFile(file) || !resolvesLocally(ref)) {
diff --git a/python/src/com/jetbrains/python/psi/impl/PyArgumentListImpl.java b/python/src/com/jetbrains/python/psi/impl/PyArgumentListImpl.java
index 50ba541..dfe12ca 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyArgumentListImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyArgumentListImpl.java
@@ -15,13 +15,15 @@
  */
 package com.jetbrains.python.psi.impl;
 
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Queues;
 import com.intellij.lang.ASTFactory;
 import com.intellij.lang.ASTNode;
-import com.intellij.psi.PsiElement;
 import com.intellij.psi.tree.IElementType;
 import com.intellij.psi.tree.TokenSet;
 import com.intellij.psi.util.PsiTreeUtil;
 import com.intellij.util.IncorrectOperationException;
+import com.jetbrains.NotNullPredicate;
 import com.jetbrains.python.PyElementTypes;
 import com.jetbrains.python.PyTokenTypes;
 import com.jetbrains.python.PythonDialectsTokenSetProvider;
@@ -30,9 +32,13 @@
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-import java.util.Arrays;
+import java.util.*;
 
 public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList {
+
+  // Filters all expressions but keyword arguments
+  private static final NoKeyArguments NO_KEY_ARGUMENTS = new NoKeyArguments();
+
   public PyArgumentListImpl(ASTNode astNode) {
     super(astNode);
   }
@@ -61,47 +67,64 @@
     return null;
   }
 
-  public void addArgument(PyExpression arg) {
-    // it should find the comma after the argument to add after, and add after
-    // that. otherwise it won't deal with comments nicely
+  @Override
+  public void addArgument(@NotNull final PyExpression arg) {
+    final PyElementGenerator generator = new PyElementGeneratorImpl(getProject());
+
+    // Adds param to appropriate place
+    final Deque<PyKeywordArgument> keywordArguments = getKeyWordArguments();
+    final Deque<PyExpression> parameters = getParameters();
+
+    if (keywordArguments.isEmpty() && parameters.isEmpty()) {
+      generator.insertItemIntoListRemoveRedundantCommas(this, null, arg);
+      return;
+    }
+
+
     if (arg instanceof PyKeywordArgument) {
-      PyKeywordArgument keywordArgument = (PyKeywordArgument)arg;
-      PyKeywordArgument lastKeyArg = null;
-      PyExpression firstNonKeyArg = null;
-      for (PsiElement element : getChildren()) {
-        if (element instanceof PyKeywordArgument) {
-          lastKeyArg = (PyKeywordArgument)element;
-        }
-        else if (element instanceof PyExpression && firstNonKeyArg == null) {
-          firstNonKeyArg = (PyExpression)element;
-        }
-      }
-      if (lastKeyArg != null) {
-        // add after last key arg
-        addArgumentNode(keywordArgument, lastKeyArg.getNode().getTreeNext(), true);
-
-      }
-      else if (firstNonKeyArg != null) {
-        // add before first non key arg
-        addArgumentNode(keywordArgument, firstNonKeyArg.getNode(), true);
-
+      if (parameters.isEmpty()) {
+        generator.insertItemIntoListRemoveRedundantCommas(this, keywordArguments.getLast(), arg);
       }
       else {
-        // add as only argument
-        addArgumentLastWithoutComma(arg);
+        if (keywordArguments.isEmpty()) {
+          generator.insertItemIntoListRemoveRedundantCommas(this, parameters.getLast(), arg);
+        }
+        else {
+          generator.insertItemIntoListRemoveRedundantCommas(this, keywordArguments.getLast(), arg);
+        }
       }
     }
     else {
-      final PyExpression[] args = getArguments();
-      if (args.length > 0) {
-        addArgumentAfter(arg, args [args.length-1]);
+      if (parameters.isEmpty()) {
+        generator.insertItemIntoListRemoveRedundantCommas(this, null, arg);
       }
       else {
-        addArgumentLastWithoutComma(arg);
+        generator.insertItemIntoListRemoveRedundantCommas(this, parameters.getLast(), arg);
       }
     }
   }
 
+
+  /**
+   * @return parameters (as opposite to keyword arguments)
+   */
+  @NotNull
+  private Deque<PyExpression> getParameters() {
+    final PyExpression[] childrenOfType = PsiTreeUtil.getChildrenOfType(this, PyExpression.class);
+    if (childrenOfType == null) {
+      return new ArrayDeque<PyExpression>(0);
+    }
+    return Queues.newArrayDeque(Collections2.filter(Arrays.asList(childrenOfType), NO_KEY_ARGUMENTS));
+  }
+
+  /**
+   * @return keyword arguments (as opposite to parameters)
+   */
+  @NotNull
+  private Deque<PyKeywordArgument> getKeyWordArguments() {
+    return Queues.newArrayDeque(PsiTreeUtil.findChildrenOfType(this, PyKeywordArgument.class));
+  }
+
   public void addArgumentFirst(PyExpression arg) {
     ASTNode node = getNode();
     ASTNode[] pars = node.getChildren(TokenSet.create(PyTokenTypes.LPAR));
@@ -113,13 +136,12 @@
       catch (IncorrectOperationException e1) {
         throw new IllegalStateException(e1);
       }
-
     }
     else {
       ASTNode before = PyUtil.getNextNonWhitespace(pars[0]);
       ASTNode anchorBefore;
       if (before != null && elementPrecedesElementsOfType(before, PythonDialectsTokenSetProvider.INSTANCE.getExpressionTokens())) {
-        ASTNode comma = PyElementGenerator.getInstance(getProject()).createComma();
+        ASTNode comma = createComma();
         node.addChild(comma, before);
         node.addChild(ASTFactory.whitespace(" "), before);
         anchorBefore = comma;
@@ -137,6 +159,14 @@
     }
   }
 
+  /**
+   * @return newly created comma
+   */
+  @NotNull
+  private ASTNode createComma() {
+    return PyElementGenerator.getInstance(getProject()).createComma();
+  }
+
   private static boolean elementPrecedesElementsOfType(ASTNode before, TokenSet expressions) {
     ASTNode node = before;
     while (node != null) {
@@ -279,4 +309,11 @@
     }
     return ret;
   }
+
+  private static class NoKeyArguments extends NotNullPredicate<PyExpression> {
+    @Override
+    protected boolean applyNotNull(@NotNull final PyExpression input) {
+      return (PsiTreeUtil.getParentOfType(input, PyKeywordArgument.class) == null) && !(input instanceof PyKeywordArgument);
+    }
+  }
 }
diff --git a/python/src/com/jetbrains/python/psi/impl/PyAssignmentStatementImpl.java b/python/src/com/jetbrains/python/psi/impl/PyAssignmentStatementImpl.java
index 1ee93e2..9f76798 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyAssignmentStatementImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyAssignmentStatementImpl.java
@@ -209,7 +209,7 @@
     final List<PyExpression> expressions = PyUtil.flattenedParensAndStars(getTargets());
     List<PyElement> result = new ArrayList<PyElement>();
     for (PyExpression expression : expressions) {
-      if (expression instanceof PyQualifiedExpression && ((PyQualifiedExpression)expression).getQualifier() != null) {
+      if (expression instanceof PyQualifiedExpression && ((PyQualifiedExpression)expression).isQualified()) {
         continue;
       }
       result.add(expression);
@@ -223,7 +223,7 @@
     PyExpression[] targets = getTargets();
     if (targets.length == 1 && targets[0] instanceof PyTargetExpression) {
       PyTargetExpression target = (PyTargetExpression)targets[0];
-      return target.getQualifier() == null && the_name.equals(target.getName()) ? target : null;
+      return !target.isQualified() && the_name.equals(target.getName()) ? target : null;
     }
     return IterHelper.findName(iterateNames(), the_name);
   }
diff --git a/python/src/com/jetbrains/python/psi/impl/PyBinaryExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyBinaryExpressionImpl.java
index 095e322..bee876a 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyBinaryExpressionImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyBinaryExpressionImpl.java
@@ -20,10 +20,14 @@
 import com.intellij.psi.PsiPolyVariantReference;
 import com.intellij.psi.tree.IElementType;
 import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.psi.util.QualifiedName;
 import com.intellij.util.IncorrectOperationException;
 import com.jetbrains.python.PyElementTypes;
 import com.jetbrains.python.PyNames;
-import com.jetbrains.python.psi.*;
+import com.jetbrains.python.psi.PyBinaryExpression;
+import com.jetbrains.python.psi.PyElementType;
+import com.jetbrains.python.psi.PyElementVisitor;
+import com.jetbrains.python.psi.PyExpression;
 import com.jetbrains.python.psi.impl.references.PyOperatorReference;
 import com.jetbrains.python.psi.resolve.PyResolveContext;
 import com.jetbrains.python.psi.types.*;
@@ -149,6 +153,17 @@
     return getLeftExpression();
   }
 
+  @Nullable
+  @Override
+  public QualifiedName asQualifiedName() {
+    return PyPsiUtils.asQualifiedName(this);
+  }
+
+  @Override
+  public boolean isQualified() {
+    return getQualifier() != null;
+  }
+
   @Override
   public String getReferencedName() {
     final PyElementType t = getOperator();
diff --git a/python/src/com/jetbrains/python/psi/impl/PyBlockEvaluator.java b/python/src/com/jetbrains/python/psi/impl/PyBlockEvaluator.java
index 83b2949..c561eb6 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyBlockEvaluator.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyBlockEvaluator.java
@@ -93,7 +93,7 @@
       public void visitPyAugAssignmentStatement(PyAugAssignmentStatement node) {
         PyExpression target = node.getTarget();
         String name = target.getName();
-        if (target instanceof PyReferenceExpression && ((PyReferenceExpression)target).getQualifier() == null && name != null) {
+        if (target instanceof PyReferenceExpression && !((PyReferenceExpression)target).isQualified() && name != null) {
           Object currentValue = myNamespace.get(name);
           if (currentValue != null) {
             Object rhs = prepareEvaluator().evaluate(node.getValue());
@@ -121,7 +121,7 @@
           PyExpression qualifier = calleeRef.getQualifier();
           if (qualifier instanceof PyReferenceExpression) {
             PyReferenceExpression qualifierRef = (PyReferenceExpression)qualifier;
-            if (qualifierRef.getQualifier() == null) {
+            if (!qualifierRef.isQualified()) {
               if (PyNames.EXTEND.equals(calleeRef.getReferencedName()) && node.getArguments().length == 1) {
                 processExtendCall(node, qualifierRef.getReferencedName());
               }
diff --git a/python/src/com/jetbrains/python/psi/impl/PyBuiltinCache.java b/python/src/com/jetbrains/python/psi/impl/PyBuiltinCache.java
index a785911a..b545290 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyBuiltinCache.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyBuiltinCache.java
@@ -16,7 +16,7 @@
 package com.jetbrains.python.psi.impl;
 
 import com.intellij.openapi.module.Module;
-import com.intellij.openapi.module.ModuleUtil;
+import com.intellij.openapi.module.ModuleUtilCore;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.projectRoots.Sdk;
 import com.intellij.openapi.projectRoots.SdkTypeId;
@@ -24,9 +24,8 @@
 import com.intellij.openapi.roots.OrderEntry;
 import com.intellij.openapi.roots.ProjectRootManager;
 import com.intellij.openapi.roots.impl.ModuleLibraryOrderEntryImpl;
-import com.intellij.openapi.util.Ref;
 import com.intellij.openapi.vfs.LocalFileSystem;
-import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiFile;
@@ -53,11 +52,28 @@
  * Provides access to Python builtins via skeletons.
  */
 public class PyBuiltinCache {
-  public static final @NonNls String BUILTIN_FILE = "__builtin__.py";
-  @NonNls public static final String BUILTIN_FILE_3K = "builtins.py";
+  public static final String BUILTIN_FILE = "__builtin__.py";
+  public static final String BUILTIN_FILE_3K = "builtins.py";
   public static final String EXCEPTIONS_FILE = "exceptions.py";
 
-  private PyType STRING_TYPE_PY2 = null;
+  private static final PyBuiltinCache DUD_INSTANCE = new PyBuiltinCache(null, null);
+
+  /**
+   * Stores the most often used types, returned by getNNNType().
+   */
+  @NotNull private final Map<String, PyClassTypeImpl> myTypeCache = new HashMap<String, PyClassTypeImpl>();
+
+  @Nullable private PyFile myBuiltinsFile;
+  @Nullable private PyFile myExceptionsFile;
+  private long myModStamp = -1;
+
+  public PyBuiltinCache() {
+  }
+
+  public PyBuiltinCache(@Nullable final PyFile builtins, @Nullable PyFile exceptions) {
+    myBuiltinsFile = builtins;
+    myExceptionsFile = exceptions;
+  }
 
   /**
    * Returns an instance of builtin cache. Instances differ per module and are cached.
@@ -80,7 +96,7 @@
     if (psifile == null) {
       return null;
     }
-    Module module = ModuleUtil.findModuleForPsiElement(psifile);
+    Module module = ModuleUtilCore.findModuleForPsiElement(psifile);
     if (module != null) {
       return PythonSdkType.findPythonSdk(module);
     }
@@ -124,7 +140,7 @@
       for (String url : urls) {
         if (url.contains(PythonSdkType.SKELETON_DIR_NAME)) {
           final String builtins_url = url + "/" + name;
-          File builtins = new File(VfsUtil.urlToPath(builtins_url));
+          File builtins = new File(VfsUtilCore.urlToPath(builtins_url));
           if (builtins.isFile() && builtins.canRead()) {
             VirtualFile builtins_vfile = LocalFileSystem.getInstance().findFileByIoFile(builtins);
             if (builtins_vfile != null) {
@@ -140,8 +156,6 @@
     return null;
   }
 
-  private static final PyBuiltinCache DUD_INSTANCE = new PyBuiltinCache(null, null);
-
   @Nullable
   static PyType createLiteralCollectionType(final PySequenceExpression sequence, final String name) {
     final PyBuiltinCache builtinCache = getInstance(sequence);
@@ -152,26 +166,13 @@
     return null;
   }
 
-
-  @Nullable private PyFile myBuiltinsFile;
-  @Nullable private PyFile myExceptionsFile;
-
-
-  public PyBuiltinCache() {
-  }
-
-  public PyBuiltinCache(@Nullable final PyFile builtins, @Nullable PyFile exceptions) {
-    myBuiltinsFile = builtins;
-    myExceptionsFile = exceptions;
-  }
-
   @Nullable
   public PyFile getBuiltinsFile() {
     return myBuiltinsFile;
   }
 
   public boolean isValid() {
-    return myBuiltinsFile == null || myBuiltinsFile.isValid();
+    return myBuiltinsFile != null && myBuiltinsFile.isValid();
   }
 
   /**
@@ -195,13 +196,6 @@
     return null;
   }
 
-  /**
-   * Stores the most often used types, returned by getNNNType().
-   */
-  private final Map<String, PyClassTypeImpl> myTypeCache = new HashMap<String, PyClassTypeImpl>();
-  private final Map<String, Ref<PyType>> myStdlibTypeCache = new HashMap<String, Ref<PyType>>();
-  private long myModStamp = -1;
-
   @Nullable
   public PyClassTypeImpl getObjectType(@NonNls String name) {
     PyClassTypeImpl val;
@@ -316,10 +310,7 @@
   }
 
   private PyType getStrOrUnicodeType() {
-    if (STRING_TYPE_PY2 == null) {
-      STRING_TYPE_PY2 = PyUnionType.union(getObjectType("str"), getObjectType("unicode"));
-    }
-    return STRING_TYPE_PY2;
+    return PyUnionType.union(getObjectType("str"), getObjectType("unicode"));
   }
 
   @Nullable
@@ -342,51 +333,6 @@
     return getObjectType("staticmethod");
   }
 
-  @Nullable
-  public Ref<PyType> getStdlibType(@NotNull String key, @NotNull TypeEvalContext context) {
-    synchronized (myStdlibTypeCache) {
-      final Ref<PyType> ref = myStdlibTypeCache.get(key);
-      if (ref != null) {
-        if (!isValid(ref.get(), context)) {
-          myStdlibTypeCache.clear();
-          return null;
-        }
-      }
-      return ref;
-    }
-  }
-
-  private static boolean isValid(@Nullable PyType type, @NotNull TypeEvalContext context) {
-    if (type instanceof PyCollectionType) {
-      final PyType elementType = ((PyCollectionType)type).getElementType(context);
-      if (!isValid(elementType, context)) {
-        return false;
-      }
-    }
-
-    if (type instanceof PyClassType) {
-      return ((PyClassType)type).isValid();
-    }
-    else if (type instanceof PyUnionType) {
-      for (PyType member : ((PyUnionType)type).getMembers()) {
-        if (!isValid(member, context)) {
-          return false;
-        }
-      }
-      return true;
-    }
-    else if (type instanceof PyFunctionType) {
-      return ((PyFunctionType)type).getCallable().isValid();
-    }
-    return true;
-  }
-
-  public void storeStdlibType(@NotNull String key, @Nullable PyType result) {
-    synchronized (myStdlibTypeCache) {
-      myStdlibTypeCache.put(key, new Ref<PyType>(result));
-    }
-  }
-
   /**
    * @param target an element to check.
    * @return true iff target is inside the __builtins__.py
diff --git a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java
index 506fdb0..781dc28 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java
@@ -43,6 +43,7 @@
   }
 
   /**
+   * TODO: Copy/Paste with {@link com.jetbrains.python.psi.PyArgumentList#addArgument(com.jetbrains.python.psi.PyExpression)}
    * Adds an argument to the end of argument list.
    * @param us the arg list
    * @param expression what to add
@@ -490,7 +491,7 @@
               if (first_arg instanceof PyReferenceExpression) {
                 final PyReferenceExpression firstArgRef = (PyReferenceExpression)first_arg;
                 final PyExpression qualifier = firstArgRef.getQualifier();
-                if (qualifier != null && PyNames.CLASS.equals(firstArgRef.getReferencedName())) {
+                if (qualifier != null && PyNames.__CLASS__.equals(firstArgRef.getReferencedName())) {
                   final PsiReference qRef = qualifier.getReference();
                   final PsiElement element = qRef == null ? null : qRef.resolve();
                   if (element instanceof PyParameter) {
@@ -507,7 +508,9 @@
                 }
               }
             }
-            else if (((PyFile)call.getContainingFile()).getLanguageLevel().isPy3K() && containingClass != null) {
+            else if ((call.getContainingFile() instanceof PyFile) &&
+                     ((PyFile)call.getContainingFile()).getLanguageLevel().isPy3K() &&
+                     (containingClass != null)) {
               return new Maybe<PyType>(getSuperClassUnionType(containingClass));
             }
           }
diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java
index 0a9959d..bea0e10 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java
@@ -148,6 +148,7 @@
     pyVisitor.visitPyClass(this);
   }
 
+  @Override
   @NotNull
   public PyStatementList getStatementList() {
     final PyStatementList statementList = childToPsi(PyElementTypes.STATEMENT_LIST);
@@ -585,7 +586,7 @@
 
   @Nullable
   @Override
-  public Property findProperty(@NotNull final String name) {
+  public Property findProperty(@NotNull final String name, boolean inherited) {
     Property property = findLocalProperty(name);
     if (property != null) {
       return property;
@@ -593,10 +594,12 @@
     if (findMethodByName(name, false) != null || findClassAttribute(name, false) != null) {
       return null;
     }
-    for (PyClass aClass : getAncestorClasses()) {
-      final Property ancestorProperty = ((PyClassImpl)aClass).findLocalProperty(name);
-      if (ancestorProperty != null) {
-        return ancestorProperty;
+    if (inherited) {
+      for (PyClass aClass : getAncestorClasses()) {
+        final Property ancestorProperty = ((PyClassImpl)aClass).findLocalProperty(name);
+        if (ancestorProperty != null) {
+          return ancestorProperty;
+        }
       }
     }
     return null;
@@ -1167,6 +1170,67 @@
     return manager.getParameterizedCachedValue(this, myCachedValueKey, myCachedAncestorsProvider, false, context);
   }
 
+  @Nullable
+  @Override
+  public PyType getMetaClassType(@NotNull TypeEvalContext context) {
+    if (context.maySwitchToAST(this)) {
+      final PyExpression expression = getMetaClassExpression();
+      if (expression != null) {
+        final PyType type = context.getType(expression);
+        if (type != null) {
+          return type;
+        }
+      }
+    }
+    else {
+      final PyClassStub stub = getStub();
+      final QualifiedName name = stub != null ? stub.getMetaClass() : PyPsiUtils.asQualifiedName(getMetaClassExpression());
+      final PsiFile file = getContainingFile();
+      if (file instanceof PyFile) {
+        final PyFile pyFile = (PyFile)file;
+        if (name != null) {
+          return classTypeFromQName(name, pyFile, context);
+        }
+      }
+    }
+    final LanguageLevel level = LanguageLevel.forElement(this);
+    if (level.isOlderThan(LanguageLevel.PYTHON30)) {
+      final PsiFile file = getContainingFile();
+      if (file instanceof PyFile) {
+        final PyFile pyFile = (PyFile)file;
+        final PsiElement element = pyFile.getElementNamed(PyNames.DUNDER_METACLASS);
+        if (element instanceof PyTypedElement) {
+          return context.getType((PyTypedElement)element);
+        }
+      }
+    }
+    return null;
+  }
+
+  @Nullable
+  @Override
+  public PyExpression getMetaClassExpression() {
+    final LanguageLevel level = LanguageLevel.forElement(this);
+    if (level.isAtLeast(LanguageLevel.PYTHON30)) {
+      // Requires AST access
+      for (PyExpression expression : getSuperClassExpressions()) {
+        if (expression instanceof PyKeywordArgument) {
+          final PyKeywordArgument argument = (PyKeywordArgument)expression;
+          if (PyNames.METACLASS.equals(argument.getKeyword())) {
+            return argument.getValueExpression();
+          }
+        }
+      }
+    }
+    else {
+      final PyTargetExpression attribute = findClassAttribute(PyNames.DUNDER_METACLASS, false);
+      if (attribute != null) {
+        return attribute;
+      }
+    }
+    return null;
+  }
+
   @NotNull
   private List<PyClassLikeType> getMROAncestorTypes(@NotNull TypeEvalContext context) {
     final PyType thisType = context.getType(this);
diff --git a/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java b/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java
index f15a664..92e75ed 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java
@@ -26,7 +26,6 @@
 import com.jetbrains.python.PythonDialectsTokenSetProvider;
 import com.jetbrains.python.psi.*;
 import com.jetbrains.python.psi.resolve.PyResolveContext;
-import com.jetbrains.python.psi.resolve.PyResolveUtil;
 import com.jetbrains.python.psi.stubs.PyDecoratorStub;
 import com.jetbrains.python.psi.types.PyType;
 import com.jetbrains.python.psi.types.TypeEvalContext;
@@ -34,8 +33,6 @@
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-import java.util.List;
-
 /**
  * @author dcheryasov
  */
@@ -74,8 +71,8 @@
   }
 
   public boolean hasArgumentList() {
-    ASTNode arglist_node = getNode().findChildByType(PyElementTypes.ARGUMENT_LIST);
-    return (arglist_node != null) && (arglist_node.findChildByType(PyTokenTypes.LPAR) != null);
+    final ASTNode arglistNode = getNode().findChildByType(PyElementTypes.ARGUMENT_LIST);
+    return (arglistNode != null) && (arglistNode.findChildByType(PyTokenTypes.LPAR) != null);
   }
 
   public QualifiedName getQualifiedName() {
@@ -84,13 +81,9 @@
       return stub.getQualifiedName();
     }
     else {
-      PyReferenceExpression node = PsiTreeUtil.getChildOfType(this, PyReferenceExpression.class);
+      final PyReferenceExpression node = PsiTreeUtil.getChildOfType(this, PyReferenceExpression.class);
       if (node != null) {
-        List<PyExpression> parts = PyResolveUtil.unwindQualifiers(node);
-        if (parts != null) {
-          //Collections.reverse(parts);
-          return PyQualifiedNameFactory.fromReferenceChain(parts);
-        }
+        return node.asQualifiedName();
       }
       return null;
     }
@@ -122,7 +115,7 @@
   @Override
   public <T extends PsiElement> T getArgument(int index, Class<T> argClass) {
     PyExpression[] args = getArguments();
-    return args.length >= index && argClass.isInstance(args[index]) ? argClass.cast(args[index]) : null;
+    return args.length > index && argClass.isInstance(args[index]) ? argClass.cast(args[index]) : null;
   }
 
   @Override
diff --git a/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java b/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java
index 1a8f123..c306116 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java
@@ -15,6 +15,8 @@
  */
 package com.jetbrains.python.psi.impl;
 
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Queues;
 import com.intellij.lang.ASTNode;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.Pair;
@@ -23,9 +25,12 @@
 import com.intellij.psi.PsiFile;
 import com.intellij.psi.PsiFileFactory;
 import com.intellij.psi.impl.PsiFileFactoryImpl;
+import com.intellij.psi.impl.source.tree.LeafPsiElement;
 import com.intellij.psi.tree.TokenSet;
+import com.intellij.psi.util.PsiTreeUtil;
 import com.intellij.testFramework.LightVirtualFile;
 import com.intellij.util.IncorrectOperationException;
+import com.jetbrains.NotNullPredicate;
 import com.jetbrains.python.PyTokenTypes;
 import com.jetbrains.python.PythonFileType;
 import com.jetbrains.python.PythonLanguage;
@@ -37,12 +42,15 @@
 import java.nio.charset.Charset;
 import java.nio.charset.CharsetEncoder;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Deque;
 import java.util.Formatter;
 
 /**
  * @author yole
  */
 public class PyElementGeneratorImpl extends PyElementGenerator {
+  private static final CommasOnly COMMAS_ONLY = new CommasOnly();
   private final Project myProject;
 
   public PyElementGeneratorImpl(Project project) {
@@ -173,6 +181,30 @@
     return dot.copyElement();
   }
 
+  @Override
+  @NotNull
+  public PsiElement insertItemIntoListRemoveRedundantCommas(
+    @NotNull final PyElement list,
+    @Nullable final PyExpression afterThis,
+    @NotNull final PyExpression toInsert) {
+    // TODO: #insertItemIntoList is probably buggy. In such case, fix it and get rid of this method
+    final PsiElement result = insertItemIntoList(list, afterThis, toInsert);
+    final LeafPsiElement[] leafs = PsiTreeUtil.getChildrenOfType(list, LeafPsiElement.class);
+    if (leafs != null) {
+      final Deque<LeafPsiElement> commas = Queues.newArrayDeque(Collections2.filter(Arrays.asList(leafs), COMMAS_ONLY));
+      if (! commas.isEmpty()) {
+        final LeafPsiElement lastComma = commas.getLast();
+        if (PsiTreeUtil.getNextSiblingOfType(lastComma, PyExpression.class) == null) { //Comma has no expression after it
+          lastComma.delete();
+        }
+      }
+    }
+
+    return result;
+  }
+
+  // TODO: Adds comma to empty list: adding "foo" to () will create (foo,). That is why "insertItemIntoListRemoveRedundantCommas" was created.
+  // We probably need to fix this method and delete insertItemIntoListRemoveRedundantCommas
   public PsiElement insertItemIntoList(PyElement list, @Nullable PyExpression afterThis, PyExpression toInsert)
     throws IncorrectOperationException {
     ASTNode add = toInsert.getNode().copyElement();
@@ -225,6 +257,7 @@
     return createExpressionFromText(LanguageLevel.getDefault(), text);
   }
 
+  @NotNull
   public PyExpression createExpressionFromText(final LanguageLevel languageLevel, final String text) {
     final PsiFile dummyFile = createDummyFile(languageLevel, text);
     final PsiElement element = dummyFile.getFirstChild();
@@ -364,4 +397,11 @@
     return createFromText(LanguageLevel.getDefault(),
                           PyExpressionStatement.class, content + "\n");
   }
+
+  private static class CommasOnly extends NotNullPredicate<LeafPsiElement> {
+    @Override
+    protected boolean applyNotNull(@NotNull final LeafPsiElement input) {
+      return input.getNode().getElementType().equals(PyTokenTypes.COMMA);
+    }
+  }
 }
diff --git a/python/src/com/jetbrains/python/psi/impl/PyEvaluator.java b/python/src/com/jetbrains/python/psi/impl/PyEvaluator.java
index db114df..321ffc4 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyEvaluator.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyEvaluator.java
@@ -105,7 +105,7 @@
   }
 
   protected Object evaluateReferenceExpression(PyReferenceExpression expr) {
-    if (expr.getQualifier() == null) {
+    if (!expr.isQualified()) {
       if (myNamespace != null) {
         return myNamespace.get(expr.getReferencedName());
       }
diff --git a/python/src/com/jetbrains/python/psi/impl/PyExceptPartImpl.java b/python/src/com/jetbrains/python/psi/impl/PyExceptPartImpl.java
index 812072d..c051d1b 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyExceptPartImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyExceptPartImpl.java
@@ -63,6 +63,7 @@
   }
 
   public PyElement getElementNamed(final String the_name) {
+    // Requires switching from stubs to AST in getTarget()
     return IterHelper.findName(iterateNames(), the_name);
   }
 
diff --git a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java
index f16850e..c1aa84c 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java
@@ -16,15 +16,19 @@
 package com.jetbrains.python.psi.impl;
 
 import com.intellij.extapi.psi.PsiFileBase;
+import com.intellij.icons.AllIcons;
 import com.intellij.lang.ASTNode;
 import com.intellij.lang.Language;
+import com.intellij.navigation.ItemPresentation;
 import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.roots.ProjectFileIndex;
 import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.*;
 import com.intellij.psi.scope.PsiScopeProcessor;
 import com.intellij.psi.stubs.StubElement;
-import com.intellij.psi.templateLanguages.TemplateLanguageFileViewProvider;
 import com.intellij.psi.tree.IElementType;
 import com.intellij.psi.util.PsiModificationTracker;
 import com.intellij.psi.util.QualifiedName;
@@ -33,11 +37,15 @@
 import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.containers.MultiMap;
 import com.intellij.util.indexing.IndexingDataKeys;
-import com.jetbrains.python.*;
+import com.jetbrains.python.PyElementTypes;
+import com.jetbrains.python.PyNames;
+import com.jetbrains.python.PythonFileType;
+import com.jetbrains.python.PythonLanguage;
 import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
 import com.jetbrains.python.documentation.DocStringUtil;
 import com.jetbrains.python.inspections.PythonVisitorFilter;
 import com.jetbrains.python.psi.*;
+import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
 import com.jetbrains.python.psi.resolve.RatedResolveResult;
 import com.jetbrains.python.psi.resolve.ResolveImportUtil;
 import com.jetbrains.python.psi.resolve.VariantsProcessor;
@@ -49,6 +57,7 @@
 import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
+import java.io.File;
 import java.util.*;
 
 public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
@@ -59,8 +68,7 @@
   private final Map<FutureFeature, Boolean> myFutureFeatures;
   private List<String> myDunderAll;
   private boolean myDunderAllCalculated;
-  private SoftReference<ExportedNameCache> myExportedNameCache = new SoftReference<ExportedNameCache>(null);
-  private final Object myENCLock = new Object();
+  private volatile SoftReference<ExportedNameCache> myExportedNameCache = new SoftReference<ExportedNameCache>(null);
   private final PsiModificationTracker myModificationTracker;
 
   private class ExportedNameCache {
@@ -305,19 +313,12 @@
   }
 
   public boolean isAcceptedFor(@NotNull Class visitorClass) {
-    final FileViewProvider viewProvider = getViewProvider();
-    final Language lang;
-    if (viewProvider instanceof TemplateLanguageFileViewProvider) {
-      lang = viewProvider.getBaseLanguage();
-    }
-    else {
-      lang = getLanguage();
-    }
-    final List<PythonVisitorFilter> filters = PythonVisitorFilter.INSTANCE.allForLanguage(lang);
-    if (filters.isEmpty()) return true;
-    for (PythonVisitorFilter filter : filters) {
-      if (!filter.isSupported(visitorClass, this))
-        return false;
+    for (Language lang : getViewProvider().getLanguages()) {
+      final List<PythonVisitorFilter> filters = PythonVisitorFilter.INSTANCE.allForLanguage(lang);
+      for (PythonVisitorFilter filter : filters) {
+        if (!filter.isSupported(visitorClass, this))
+          return false;
+      }
     }
     return true;
   }
@@ -441,17 +442,15 @@
 
   private ExportedNameCache getExportedNameCache() {
     ExportedNameCache cache;
-    synchronized (myENCLock) {
-      cache = myExportedNameCache != null ? myExportedNameCache.get() : null;
-      final long modificationStamp = getModificationStamp();
-      if (myExportedNameCache != null && cache != null && modificationStamp != cache.getModificationStamp()) {
-        myExportedNameCache.clear();
-        cache = null;
-      }
-      if (cache == null) {
-        cache = new ExportedNameCache(modificationStamp);
-        myExportedNameCache = new SoftReference<ExportedNameCache>(cache);
-      }
+    cache = myExportedNameCache != null ? myExportedNameCache.get() : null;
+    final long modificationStamp = getModificationStamp();
+    if (myExportedNameCache != null && cache != null && modificationStamp != cache.getModificationStamp()) {
+      myExportedNameCache.clear();
+      cache = null;
+    }
+    if (cache == null) {
+      cache = new ExportedNameCache(modificationStamp);
+      myExportedNameCache = new SoftReference<ExportedNameCache>(cache);
     }
     return cache;
   }
@@ -614,7 +613,7 @@
 
     @Nullable
     private List<String> getStringListFromValue(PyExpression expression) {
-      if (expression instanceof PyReferenceExpression && ((PyReferenceExpression)expression).getQualifier() == null) {
+      if (expression instanceof PyReferenceExpression && !((PyReferenceExpression)expression).isQualified()) {
         return myDunderLike.get(((PyReferenceExpression)expression).getReferencedName());
       }
       return PyUtil.strListValue(expression);
@@ -747,9 +746,7 @@
     ControlFlowCache.clear(this);
     myDunderAllCalculated = false;
     myFutureFeatures.clear(); // probably no need to synchronize
-    synchronized (myENCLock) {
-      myExportedNameCache.clear();
-    }
+    myExportedNameCache.clear();
   }
 
   @Override
@@ -782,4 +779,78 @@
     }
     return elementType == PyElementTypes.IMPORT_STATEMENT || elementType == PyElementTypes.FROM_IMPORT_STATEMENT;
   }
+
+  @Override
+  public ItemPresentation getPresentation() {
+    return new ItemPresentation() {
+      @Override
+      public String getPresentableText() {
+        return getModuleName(PyFileImpl.this);
+      }
+
+      @Override
+      public String getLocationString() {
+        final String name = getLocationName();
+        return name != null ? "(" + name + ")" : null;
+      }
+
+      @Override
+      public Icon getIcon(final boolean open) {
+        if (PyUtil.isPackage(PyFileImpl.this)) {
+          return AllIcons.Modules.SourceFolder;
+        }
+        return PyFileImpl.this.getIcon(0);
+      }
+
+      @NotNull
+      private String getModuleName(@NotNull PyFile file) {
+        if (PyUtil.isPackage(file)) {
+          final PsiDirectory dir = file.getContainingDirectory();
+          if (dir != null) {
+            return dir.getName();
+          }
+        }
+        return FileUtil.getNameWithoutExtension(file.getName());
+      }
+
+      @Nullable
+      private String getLocationName() {
+        final QualifiedName name = QualifiedNameFinder.findShortestImportableQName(PyFileImpl.this);
+        if (name != null) {
+          final QualifiedName prefix = name.removeTail(1);
+          if (prefix.getComponentCount() > 0) {
+            return prefix.toString();
+          }
+        }
+        final String relativePath = getRelativeContainerPath();
+        if (relativePath != null) {
+          return relativePath;
+        }
+        final PsiDirectory psiDirectory = getParent();
+        if (psiDirectory != null) {
+          return psiDirectory.getVirtualFile().getPresentableUrl();
+        }
+        return null;
+      }
+
+      @Nullable
+      private String getRelativeContainerPath() {
+        final PsiDirectory psiDirectory = getParent();
+        if (psiDirectory != null) {
+          final VirtualFile virtualFile = getVirtualFile();
+          if (virtualFile != null) {
+            final VirtualFile root = ProjectFileIndex.SERVICE.getInstance(getProject()).getContentRootForFile(virtualFile);
+            if (root != null) {
+              final VirtualFile parent = virtualFile.getParent();
+              final VirtualFile rootParent = root.getParent();
+              if (rootParent != null && parent != null) {
+                return VfsUtilCore.getRelativePath(parent, rootParent, File.separatorChar);
+              }
+            }
+          }
+        }
+        return null;
+      }
+    };
+  }
 }
diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionBuilder.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionBuilder.java
index 82b21ad..53dea09 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyFunctionBuilder.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionBuilder.java
@@ -20,25 +20,78 @@
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.codeStyle.CodeStyleSettings;
 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
+import com.intellij.util.ArrayUtil;
 import com.jetbrains.python.PyNames;
 import com.jetbrains.python.PythonFileType;
-import com.jetbrains.python.psi.LanguageLevel;
-import com.jetbrains.python.psi.PyElementGenerator;
-import com.jetbrains.python.psi.PyFunction;
+import com.jetbrains.python.psi.*;
+
+import org.jetbrains.annotations.NotNull;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.regex.Pattern;
 
 /**
  * @author yole
  */
 public class PyFunctionBuilder {
+  private static final String COMMENTS_BOUNDARY = "\"\"\"";
+  private static final Pattern INDENT_REMOVE_PATTERN = Pattern.compile("^\\s+", Pattern.MULTILINE);
   private final String myName;
   private final List<String> myParameters = new ArrayList<String>();
   private final List<String> myStatements = new ArrayList<String>();
   private final List<String> myDecorators = new ArrayList<String>();
   private String myAnnotation = null;
+  private String[] myDocStringLines = null;
+
+  /**
+   * Creates builder copying signature and doc from another one.
+   * @param source what to copy
+   * @param decoratorsToCopyIfExist list of decorator names to be copied to new function.
+   * @return builder configured by this function
+   */
+  @NotNull
+  public static PyFunctionBuilder copySignature(@NotNull final PyFunction source, @NotNull final String... decoratorsToCopyIfExist) {
+    final String name = source.getName();
+    final PyFunctionBuilder functionBuilder = new PyFunctionBuilder((name != null) ? name : "");
+    for (final PyParameter parameter : source.getParameterList().getParameters()) {
+      final String parameterName = parameter.getName();
+      if (parameterName != null) {
+        functionBuilder.parameter(parameterName);
+      }
+    }
+    final PyDecoratorList decoratorList = source.getDecoratorList();
+    if (decoratorList != null) {
+      for (final PyDecorator decorator : decoratorList.getDecorators()) {
+        final String decoratorName = decorator.getName();
+        if (decoratorName != null) {
+          if (ArrayUtil.contains(decoratorName, decoratorsToCopyIfExist)) {
+            functionBuilder.decorate(decoratorName);
+          }
+        }
+      }
+    }
+    final String docString = source.getDocStringValue();
+    if (docString != null) {
+      functionBuilder.docString(docString);
+    }
+    return functionBuilder;
+  }
+
+  /**
+   * Adds docstring to function. Provide doc with out of comment blocks.
+   * @param docString doc
+   */
+  public void docString(@NotNull final String docString) {
+    myDocStringLines = StringUtil.splitByLines(removeIndent(docString));
+  }
+
+  @NotNull
+  private String removeIndent(@NotNull final String string) {
+    return INDENT_REMOVE_PATTERN.matcher(string).replaceAll("");
+  }
 
   public PyFunctionBuilder(String name) {
     myName = name;
@@ -47,7 +100,7 @@
   public PyFunctionBuilder parameter(String baseName) {
     String name = baseName;
     int uniqueIndex = 0;
-    while(myParameters.contains(name)) {
+    while (myParameters.contains(name)) {
       uniqueIndex++;
       name = baseName + uniqueIndex;
     }
@@ -66,11 +119,11 @@
   }
 
   public PyFunction addFunction(PsiElement target, final LanguageLevel languageLevel) {
-    return (PyFunction) target.add(buildFunction(target.getProject(), languageLevel));
+    return (PyFunction)target.add(buildFunction(target.getProject(), languageLevel));
   }
 
   public PyFunction addFunctionAfter(PsiElement target, PsiElement anchor, final LanguageLevel languageLevel) {
-    return (PyFunction) target.addAfter(buildFunction(target.getProject(), languageLevel), anchor);
+    return (PyFunction)target.addAfter(buildFunction(target.getProject(), languageLevel), anchor);
   }
 
   public PyFunction buildFunction(Project project, final LanguageLevel languageLevel) {
@@ -93,6 +146,16 @@
     }
     builder.append(":");
     List<String> statements = myStatements.isEmpty() ? Collections.singletonList(PyNames.PASS) : myStatements;
+
+    if (myDocStringLines != null) {
+      final List<String> comments = new ArrayList<String>(myDocStringLines.length + 2);
+      comments.add(COMMENTS_BOUNDARY);
+      comments.addAll(Arrays.asList(myDocStringLines));
+      comments.add(COMMENTS_BOUNDARY);
+      statements = new ArrayList<String>(statements);
+      statements.addAll(0, comments);
+    }
+
     final CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getInstance(project).getCurrentSettings();
     int indentSize = codeStyleSettings.getIndentOptions(PythonFileType.INSTANCE).INDENT_SIZE;
     String indent = StringUtil.repeatSymbol(' ', indentSize);
@@ -105,4 +168,11 @@
   public void decorate(String decoratorName) {
     myDecorators.add("@" + decoratorName);
   }
+
+  @NotNull
+  private static String getIndent(@NotNull final Project project) {
+    final CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getInstance(project).getCurrentSettings();
+    final int indentSize = codeStyleSettings.getIndentOptions(PythonFileType.INSTANCE).INDENT_SIZE;
+    return StringUtil.repeatSymbol(' ', indentSize);
+  }
 }
diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java
index 93469c6..2ffc8e6 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java
@@ -143,9 +143,12 @@
     return getRequiredStubOrPsiChild(PyElementTypes.PARAMETER_LIST);
   }
 
-  @Nullable
+  @Override
+  @NotNull
   public PyStatementList getStatementList() {
-    return childToPsi(PyElementTypes.STATEMENT_LIST);
+    final PyStatementList statementList = childToPsi(PyElementTypes.STATEMENT_LIST);
+    assert statementList != null : "Statement list missing for function " + getText();
+    return statementList;
   }
 
   public PyClass getContainingClass() {
@@ -174,29 +177,28 @@
   @Nullable
   @Override
   public PyType getReturnType(@NotNull TypeEvalContext context, @Nullable PyQualifiedExpression callSite) {
-    final PyType type = getGenericReturnType(context, callSite);
-
+    PyType type = getGenericReturnType(context, callSite);
     if (callSite == null) {
       return type;
     }
     final PyTypeChecker.AnalyzeCallResults results = PyTypeChecker.analyzeCallSite(callSite, context);
-
     if (PyTypeChecker.hasGenerics(type, context)) {
       if (results != null) {
         final Map<PyGenericType, PyType> substitutions = PyTypeChecker.unifyGenericCall(this, results.getReceiver(), results.getArguments(),
                                                                                         context);
-        if (substitutions != null) {
-          return PyTypeChecker.substitute(type, substitutions, context);
-        }
+        type = substitutions != null ? PyTypeChecker.substitute(type, substitutions, context) : null;
       }
-      return null;
+      else {
+        type = null;
+      }
+    }
+    if (results != null) {
+      type = replaceSelf(type, results.getReceiver(), context);
     }
     if (results != null && isDynamicallyEvaluated(results.getArguments().values(), context)) {
       return PyUnionType.createWeakType(type);
     }
-    else {
-      return type;
-    }
+    return type;
   }
 
   @Nullable
@@ -205,16 +207,36 @@
    */
   public PyType getReturnTypeWithoutCallSite(@NotNull TypeEvalContext context,
                                              @Nullable PyExpression receiver) {
-    final PyType type = getGenericReturnType(context, null);
+    PyType type = getGenericReturnType(context, null);
     if (PyTypeChecker.hasGenerics(type, context)) {
-      final Map<PyGenericType, PyType> substitutions =
-        PyTypeChecker.unifyGenericCall(this, receiver, Maps.<PyExpression, PyNamedParameter>newHashMap(), context);
+      final Map<PyGenericType, PyType> substitutions = PyTypeChecker.unifyGenericCall(this, receiver,
+                                                                                      Maps.<PyExpression, PyNamedParameter>newHashMap(),
+                                                                                      context);
       if (substitutions != null) {
-        return PyTypeChecker.substitute(type, substitutions, context);
+        type = PyTypeChecker.substitute(type, substitutions, context);
       }
-      return null;
+      else {
+        type = null;
+      }
     }
-    return type;
+    return replaceSelf(type, receiver, context);
+  }
+
+  @Nullable
+  private PyType replaceSelf(@Nullable PyType returnType, @Nullable PyExpression receiver, @NotNull TypeEvalContext context) {
+    if (receiver != null) {
+      // TODO: Currently we substitute only simple subclass types, but we could handle union and collection types as well
+      if (returnType instanceof PyClassType) {
+        final PyClassType returnClassType = (PyClassType)returnType;
+        if (returnClassType.getPyClass() == getContainingClass()) {
+          final PyType receiverType = context.getType(receiver);
+          if (receiverType instanceof PyClassType && PyTypeChecker.match(returnType, receiverType, context)) {
+            return returnClassType.isDefinition() ? receiverType : ((PyClassType)receiverType).toInstance();
+          }
+        }
+      }
+    }
+    return returnType;
   }
 
   private static boolean isDynamicallyEvaluated(@NotNull Collection<PyNamedParameter> parameters, @NotNull TypeEvalContext context) {
@@ -251,9 +273,9 @@
       return docStringType;
     }
     if (typeEvalContext.allowReturnTypes(this)) {
-      final PyType yieldType = getYieldStatementType(typeEvalContext);
-      if (yieldType != null) {
-        return yieldType;
+      final Ref<? extends PyType> yieldTypeRef = getYieldStatementType(typeEvalContext);
+      if (yieldTypeRef != null) {
+        return yieldTypeRef.get();
       }
       return getReturnStatementType(typeEvalContext);
     }
@@ -261,13 +283,12 @@
   }
 
   @Nullable
-  private PyType getYieldStatementType(@NotNull final TypeEvalContext context) {
+  private Ref<? extends PyType> getYieldStatementType(@NotNull final TypeEvalContext context) {
     Ref<PyType> elementType = null;
     final PyBuiltinCache cache = PyBuiltinCache.getInstance(this);
-    final PyClass listClass = cache.getClass("list");
     final PyStatementList statements = getStatementList();
     final Set<PyType> types = new LinkedHashSet<PyType>();
-    if (statements != null && listClass != null) {
+    if (statements != null) {
       statements.accept(new PyRecursiveElementVisitor() {
         @Override
         public void visitPyYieldExpression(PyYieldExpression node) {
@@ -297,9 +318,12 @@
     if (elementType != null) {
       final PyClass generator = cache.getClass(PyNames.FAKE_GENERATOR);
       if (generator != null) {
-        return new PyCollectionTypeImpl(generator, false, elementType.get());
+        return Ref.create(new PyCollectionTypeImpl(generator, false, elementType.get()));
       }
     }
+    if (!types.isEmpty()) {
+      return Ref.create(null);
+    }
     return null;
   }
 
diff --git a/python/src/com/jetbrains/python/psi/impl/PyImportElementImpl.java b/python/src/com/jetbrains/python/psi/impl/PyImportElementImpl.java
index ddeaf12..70e3ecf 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyImportElementImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyImportElementImpl.java
@@ -28,7 +28,6 @@
 import com.jetbrains.python.PyElementTypes;
 import com.jetbrains.python.PythonDialectsTokenSetProvider;
 import com.jetbrains.python.psi.*;
-import com.jetbrains.python.psi.resolve.PyResolveUtil;
 import com.jetbrains.python.psi.resolve.ResolveImportUtil;
 import com.jetbrains.python.psi.stubs.PyImportElementStub;
 import org.jetbrains.annotations.NotNull;
@@ -36,7 +35,6 @@
 
 import javax.swing.*;
 import java.util.Collections;
-import java.util.List;
 
 /**
  * The "import foo" or "import foo as bar" parts.
@@ -150,7 +148,7 @@
             buf.append("from ");
             PyReferenceExpression imp_src = ((PyFromImportStatement)elt).getImportSource();
             if (imp_src != null) {
-              buf.append(PyResolveUtil.toPath(imp_src));
+              buf.append(PyPsiUtils.toPath(imp_src));
             }
             else {
               buf.append("<?>");
@@ -185,10 +183,7 @@
     if (ret == null) {
       final PyReferenceExpression importReference = getImportReferenceExpression();
       if (importReference != null) {
-        final List<PyExpression> qualifiers = PyResolveUtil.unwindQualifiers(importReference);
-        if (qualifiers.size() > 0) {
-          ret = qualifiers.get(0);
-        }
+        ret = PyPsiUtils.getFirstQualifier(importReference);
       }
     }
     if (ret == null) {
diff --git a/python/src/com/jetbrains/python/psi/impl/PyKeywordArgumentManipulator.java b/python/src/com/jetbrains/python/psi/impl/PyKeywordArgumentManipulator.java
index fb4538e..5b56518 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyKeywordArgumentManipulator.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyKeywordArgumentManipulator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -23,13 +23,14 @@
 import com.jetbrains.python.psi.PyCallExpression;
 import com.jetbrains.python.psi.PyElementGenerator;
 import com.jetbrains.python.psi.PyKeywordArgument;
+import org.jetbrains.annotations.NotNull;
 
 /**
  * @author yole
  */
 public class PyKeywordArgumentManipulator extends AbstractElementManipulator<PyKeywordArgument> {
   @Override
-  public PyKeywordArgument handleContentChange(PyKeywordArgument element, TextRange range, String newContent) throws IncorrectOperationException {
+  public PyKeywordArgument handleContentChange(@NotNull PyKeywordArgument element, @NotNull TextRange range, String newContent) throws IncorrectOperationException {
     final ASTNode keywordNode = element.getKeywordNode();
     if (keywordNode != null && keywordNode.getTextRange().shiftRight(-element.getTextRange().getStartOffset()).equals(range)) {
       final LanguageLevel langLevel = LanguageLevel.forElement(element);
diff --git a/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java b/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java
index b0a0367..6ad33d6 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java
@@ -31,6 +31,8 @@
 import com.jetbrains.python.PyNames;
 import com.jetbrains.python.PyTokenTypes;
 import com.jetbrains.python.PythonDialectsTokenSetProvider;
+import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
+import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
 import com.jetbrains.python.psi.*;
 import com.jetbrains.python.psi.resolve.PyResolveContext;
 import com.jetbrains.python.psi.stubs.PyNamedParameterStub;
@@ -236,6 +238,9 @@
           if (defaultValue != null) {
             final PyType type = context.getType(defaultValue);
             if (type != null && !(type instanceof PyNoneType)) {
+              if (type instanceof PyTupleType) {
+                return PyTypeParser.getTypeByName(this, "collections.Iterable");
+              }
               return type;
             }
           }
@@ -247,15 +252,18 @@
             @Override
             public boolean process(@NotNull PyCallExpression call) {
               final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context);
-              final CallArgumentsMapping mapping = call.getArgumentList().analyzeCall(resolveContext);
-              for (Map.Entry<PyExpression, PyNamedParameter> entry : mapping.getPlainMappedParams().entrySet()) {
-                if (entry.getValue() == PyNamedParameterImpl.this) {
-                  final PyExpression argument = entry.getKey();
-                  if (argument != null) {
-                    final PyType type = context.getType(argument);
-                    if (type != null) {
-                      types.add(type);
-                      return true;
+              final PyArgumentList argumentList = call.getArgumentList();
+              if (argumentList != null) {
+                final CallArgumentsMapping mapping = argumentList.analyzeCall(resolveContext);
+                for (Map.Entry<PyExpression, PyNamedParameter> entry : mapping.getPlainMappedParams().entrySet()) {
+                  if (entry.getValue() == PyNamedParameterImpl.this) {
+                    final PyExpression argument = entry.getKey();
+                    if (argument != null) {
+                      final PyType type = context.getType(argument);
+                      if (type != null) {
+                        types.add(type);
+                        return true;
+                      }
                     }
                   }
                 }
@@ -298,9 +306,9 @@
   @NotNull
   @Override
   public SearchScope getUseScope() {
-    PyFunction func = PsiTreeUtil.getParentOfType(this, PyFunction.class);
-    if (func != null) {
-      return new LocalSearchScope(func);
+    final ScopeOwner owner = ScopeUtil.getScopeOwner(this);
+    if (owner instanceof PyFunction) {
+      return new LocalSearchScope(owner);
     }
     return new LocalSearchScope(getContainingFile());
   }
diff --git a/python/src/com/jetbrains/python/psi/impl/PyPathEvaluator.java b/python/src/com/jetbrains/python/psi/impl/PyPathEvaluator.java
index d5e1527..7a22165 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyPathEvaluator.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyPathEvaluator.java
@@ -85,7 +85,7 @@
     else if (PyNames.CURDIR.equals(expr.getName())) {
       return ".";
     }
-    if (expr.getQualifier() == null && PyNames.FILE.equals(expr.getReferencedName())) {
+    if (!expr.isQualified() && PyNames.FILE.equals(expr.getReferencedName())) {
       return myContainingFilePath;
     }
     return super.evaluateReferenceExpression(expr);
diff --git a/python/src/com/jetbrains/python/psi/impl/PyPrefixExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyPrefixExpressionImpl.java
index 946e719..71658f0 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyPrefixExpressionImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyPrefixExpressionImpl.java
@@ -19,6 +19,7 @@
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiPolyVariantReference;
 import com.intellij.psi.PsiReference;
+import com.intellij.psi.util.QualifiedName;
 import com.jetbrains.python.PyElementTypes;
 import com.jetbrains.python.PyNames;
 import com.jetbrains.python.PyTokenTypes;
@@ -98,6 +99,17 @@
     return getOperand();
   }
 
+  @Nullable
+  @Override
+  public QualifiedName asQualifiedName() {
+    return PyPsiUtils.asQualifiedName(this);
+  }
+
+  @Override
+  public boolean isQualified() {
+    return getQualifier() != null;
+  }
+
   @Override
   public String getReferencedName() {
     PyElementType t = getOperator();
diff --git a/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java
index 7b778a5..47ae58965 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java
@@ -17,6 +17,7 @@
 
 import com.intellij.lang.ASTNode;
 import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.extensions.ExtensionException;
 import com.intellij.openapi.extensions.Extensions;
 import com.intellij.openapi.util.Ref;
 import com.intellij.psi.*;
@@ -50,6 +51,7 @@
  */
 public class PyReferenceExpressionImpl extends PyElementImpl implements PyReferenceExpression {
   private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.psi.impl.PyReferenceExpressionImpl");
+  private QualifiedName myQualifiedName = null;
 
   public PyReferenceExpressionImpl(ASTNode astNode) {
     super(astNode);
@@ -99,6 +101,11 @@
     return (PyExpression)(nodes.length == 1 ? nodes[0].getPsi() : null);
   }
 
+  @Override
+  public boolean isQualified() {
+    return getQualifier() != null;
+  }
+
   @Nullable
   public String getReferencedName() {
     final ASTNode nameElement = getNameElement();
@@ -173,7 +180,10 @@
 
   @Nullable
   public QualifiedName asQualifiedName() {
-    return PyQualifiedNameFactory.fromReferenceChain(PyResolveUtil.unwindQualifiers(this));
+    if (myQualifiedName == null) {
+      myQualifiedName = PyPsiUtils.asQualifiedName(this);
+    }
+    return myQualifiedName;
   }
 
   @Override
@@ -186,8 +196,8 @@
       return null;
     }
     try {
-      final PyExpression qualifier = getQualifier();
-      if (qualifier == null) {
+      final boolean qualified = isQualified();
+      if (!qualified) {
         String name = getReferencedName();
         if (PyNames.NONE.equals(name)) {
           return PyNoneType.INSTANCE;
@@ -197,7 +207,7 @@
       if (type != null) {
         return type;
       }
-      if (qualifier != null) {
+      if (qualified) {
         PyType maybe_type = PyUtil.getSpecialAttributeType(this, context);
         if (maybe_type != null) return maybe_type;
         Ref<PyType> typeOfProperty = getTypeOfProperty(context);
@@ -264,7 +274,7 @@
     if (qualifierType instanceof PyClassType) {
       final PyClassType classType = (PyClassType)qualifierType;
       PyClass pyClass = classType.getPyClass();
-      Property property = pyClass.findProperty(name);
+      Property property = pyClass.findProperty(name, true);
       if (property != null) {
         if (classType.isDefinition()) {
           return Ref.<PyType>create(PyBuiltinCache.getInstance(pyClass).getObjectType(PyNames.PROPERTY));
@@ -291,7 +301,7 @@
   }
 
   @Nullable
-  private PyType getTypeFromProviders(TypeEvalContext context) {
+  private PyType getTypeFromProviders(@NotNull TypeEvalContext context) {
     for (PyTypeProvider provider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) {
       try {
         final PyType type = provider.getReferenceExpressionType(this, context);
@@ -300,7 +310,7 @@
         }
       }
       catch (AbstractMethodError e) {
-        LOG.info(e);
+        LOG.info(new ExtensionException(provider.getClass()));
       }
     }
     return null;
@@ -404,6 +414,12 @@
     return null;
   }
 
+  @Override
+  public void subtreeChanged() {
+    super.subtreeChanged();
+    myQualifiedName = null;
+  }
+
   private static class QualifiedResolveResultImpl extends RatedResolveResult implements QualifiedResolveResult {
     // a trivial implementation
     private List<PyExpression> myQualifiers;
diff --git a/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionManipulator.java b/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionManipulator.java
index 5ea4583..9e20ba5 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionManipulator.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionManipulator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,18 +20,21 @@
 import com.intellij.psi.AbstractElementManipulator;
 import com.intellij.util.IncorrectOperationException;
 import com.jetbrains.python.psi.PyReferenceExpression;
+import org.jetbrains.annotations.NotNull;
 
 /**
  * @author oleg
  */
 public class PyReferenceExpressionManipulator extends AbstractElementManipulator<PyReferenceExpression> {
-  public PyReferenceExpression handleContentChange(final PyReferenceExpression element, final TextRange range, final String newContent)
+  @Override
+  public PyReferenceExpression handleContentChange(@NotNull final PyReferenceExpression element, @NotNull final TextRange range, final String newContent)
     throws IncorrectOperationException {
     return null;
   }
 
+  @NotNull
   @Override
-  public TextRange getRangeInElement(final PyReferenceExpression element) {
+  public TextRange getRangeInElement(@NotNull final PyReferenceExpression element) {
     final ASTNode nameElement = element.getNameElement();
     final int startOffset = nameElement != null ? nameElement.getStartOffset() : element.getTextRange().getEndOffset();
     return new TextRange(startOffset - element.getTextOffset(), element.getTextLength());
diff --git a/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java b/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java
index a30a2de..307ac8e 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java
@@ -22,7 +22,6 @@
 import com.intellij.util.containers.HashSet;
 import com.jetbrains.python.psi.*;
 import com.jetbrains.python.psi.resolve.PyResolveContext;
-import com.jetbrains.python.psi.resolve.PyResolveUtil;
 import com.jetbrains.python.psi.resolve.RatedResolveResult;
 import com.jetbrains.python.psi.types.PyModuleType;
 import com.jetbrains.python.toolbox.ChainIterable;
@@ -101,7 +100,7 @@
         if (elt != null) { // always? who knows :)
           PyReferenceExpression imp_src = elt.getImportSource();
           if (imp_src != null) {
-            return PyResolveUtil.toPath(imp_src);
+            return PyPsiUtils.toPath(imp_src);
           }
         }
         return "<?>";
diff --git a/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionImpl.java
index 7f03dcd..bf51973 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionImpl.java
@@ -21,7 +21,6 @@
 import com.intellij.lang.injection.InjectedLanguageManager;
 import com.intellij.navigation.ItemPresentation;
 import com.intellij.openapi.util.Pair;
-import com.intellij.openapi.util.Ref;
 import com.intellij.openapi.util.TextRange;
 import com.intellij.psi.*;
 import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry;
@@ -50,9 +49,21 @@
   public static final Pattern PATTERN_ESCAPE = Pattern
       .compile("\\\\(\n|\\\\|'|\"|a|b|f|n|r|t|v|([0-7]{1,3})|x([0-9a-fA-F]{1,2})" + "|N(\\{.*?\\})|u([0-9a-fA-F]{4})|U([0-9a-fA-F]{8}))");
          //        -> 1                        ->   2      <-->     3          <-     ->   4     <-->    5      <-   ->  6           <-<-
+
+  private enum EscapeRegexGroup {
+    WHOLE_MATCH,
+    ESCAPED_SUBSTRING,
+    OCTAL,
+    HEXADECIMAL,
+    UNICODE_NAMED,
+    UNICODE_16BIT,
+    UNICODE_32BIT
+  }
+
   private static final Map<String, String> escapeMap = initializeEscapeMap();
   private String stringValue;
   private List<TextRange> valueTextRanges;
+  @Nullable private List<Pair<TextRange, String>> myDecodedFragments;
   private final DefaultRegExpPropertiesProvider myPropertiesProvider;
 
   private static Map<String, String> initializeEscapeMap() {
@@ -85,6 +96,7 @@
     super.subtreeChanged();
     stringValue = null;
     valueTextRanges = null;
+    myDecodedFragments = null;
   }
 
   public List<TextRange> getStringValueTextRanges() {
@@ -133,7 +145,19 @@
 
   private static boolean isUnicode(String text) {
     return text.length() > 0 && Character.toUpperCase(text.charAt(0)) == 'U';                       //TODO[ktisha]
-  } 
+  }
+
+  private boolean isUnicodeByDefault() {
+    if (LanguageLevel.forElement(this).isAtLeast(LanguageLevel.PYTHON30)) {
+      return true;
+    }
+    final PsiFile file = getContainingFile();
+    if (file instanceof PyFile) {
+      final PyFile pyFile = (PyFile)file;
+      return pyFile.hasImportFromFuture(FutureFeature.UNICODE_LITERALS);
+    }
+    return false;
+  }
 
   private static boolean isBytes(String text) {
     return text.length() > 0 && Character.toUpperCase(text.charAt(0)) == 'B';
@@ -143,20 +167,92 @@
     return text.length() > 0 && Character.toUpperCase(text.charAt(0)) == 'C';
   }
 
-  public void iterateCharacterRanges(TextRangeConsumer consumer) {
-    int elStart = getTextRange().getStartOffset();
-    for (ASTNode child : getStringNodes()) {
-      final String text = child.getText();
-      TextRange textRange = getNodeTextRange(text);
-      int offset = child.getTextRange().getStartOffset() - elStart + textRange.getStartOffset();
-      String undecoded = textRange.substring(text);
-      if (!iterateCharacterRanges(consumer, undecoded, offset, isRaw(text), isUnicode(text))) {
-        break;
+  @Override
+  @NotNull
+  public List<Pair<TextRange, String>> getDecodedFragments() {
+    if (myDecodedFragments == null) {
+      final List<Pair<TextRange, String>> result = new ArrayList<Pair<TextRange, String>>();
+      final int elementStart = getTextRange().getStartOffset();
+      final boolean unicodeByDefault = isUnicodeByDefault();
+      for (ASTNode node : getStringNodes()) {
+        final String text = node.getText();
+        final TextRange textRange = getNodeTextRange(text);
+        final int offset = node.getTextRange().getStartOffset() - elementStart + textRange.getStartOffset();
+        final String encoded = textRange.substring(text);
+        result.addAll(getDecodedFragments(encoded, offset, isRaw(text), unicodeByDefault || isUnicode(text)));
       }
+      myDecodedFragments = result;
     }
+    return myDecodedFragments;
   }
 
+  @NotNull
+  private static List<Pair<TextRange, String>> getDecodedFragments(@NotNull String encoded, int offset, boolean raw, boolean unicode) {
+    final List<Pair<TextRange, String>> result = new ArrayList<Pair<TextRange, String>>();
+    final Matcher escMatcher = PATTERN_ESCAPE.matcher(encoded);
+    int index = 0;
+    while (escMatcher.find(index)) {
+      if (index < escMatcher.start()) {
+        final TextRange range = TextRange.create(index, escMatcher.start());
+        final TextRange offsetRange = range.shiftRight(offset);
+        result.add(Pair.create(offsetRange, range.substring(encoded)));
+      }
 
+      final String octal = escapeRegexGroup(escMatcher, EscapeRegexGroup.OCTAL);
+      final String hex = escapeRegexGroup(escMatcher, EscapeRegexGroup.HEXADECIMAL);
+      // TODO: Implement unicode character name escapes: EscapeRegexGroup.UNICODE_NAMED
+      final String unicode16 = escapeRegexGroup(escMatcher, EscapeRegexGroup.UNICODE_16BIT);
+      final String unicode32 = escapeRegexGroup(escMatcher, EscapeRegexGroup.UNICODE_32BIT);
+      final String wholeMatch = escapeRegexGroup(escMatcher, EscapeRegexGroup.WHOLE_MATCH);
+
+      final boolean escapedUnicode = raw && unicode || !raw;
+
+      final String str;
+      if (!raw && octal != null) {
+        str = new String(new char[]{(char)Integer.parseInt(octal, 8)});
+      }
+      else if (!raw && hex != null) {
+        str = new String(new char[]{(char)Integer.parseInt(hex, 16)});
+      }
+      else if (escapedUnicode && unicode16 != null) {
+        str = unicode ? new String(new char[]{(char)Integer.parseInt(unicode16, 16)}) : wholeMatch;
+      }
+      else if (escapedUnicode && unicode32 != null) {
+        String s = wholeMatch;
+        if (unicode) {
+          try {
+            s = new String(Character.toChars((int)Long.parseLong(unicode32, 16)));
+          }
+          catch (IllegalArgumentException ignored) {
+          }
+        }
+        str = s;
+      }
+      else if (raw) {
+        str = wholeMatch;
+      }
+      else {
+        final String toReplace = escapeRegexGroup(escMatcher, EscapeRegexGroup.ESCAPED_SUBSTRING);
+        str = escapeMap.get(toReplace);
+      }
+
+      if (str != null) {
+        final TextRange wholeMatchRange = TextRange.create(escMatcher.start(), escMatcher.end());
+        result.add(Pair.create(wholeMatchRange.shiftRight(offset), str));
+      }
+
+      index = escMatcher.end();
+    }
+    final TextRange range = TextRange.create(index, encoded.length());
+    final TextRange offRange = range.shiftRight(offset);
+    result.add(Pair.create(offRange, range.substring(encoded)));
+    return result;
+  }
+
+  @Nullable
+  private static String escapeRegexGroup(@NotNull Matcher matcher, EscapeRegexGroup group) {
+    return matcher.group(group.ordinal());
+  }
 
   public List<ASTNode> getStringNodes() {
     return Arrays.asList(getNode().getChildren(PyTokenTypes.STRING_NODES));
@@ -165,14 +261,11 @@
   public String getStringValue() {
     //ASTNode child = getNode().getFirstChildNode();
     //assert child != null;
-   if (stringValue == null) {
+    if (stringValue == null) {
       final StringBuilder out = new StringBuilder();
-      iterateCharacterRanges(new TextRangeConsumer() {
-        public boolean process(int startOffset, int endOffset, String value) {
-          out.append(value);
-          return true;
-        }
-      });
+      for (Pair<TextRange, String> fragment : getDecodedFragments()) {
+        out.append(fragment.getSecond());
+      }
       stringValue = out.toString();
     }
     return stringValue;
@@ -190,106 +283,6 @@
     return new TextRange(0, getTextLength());
   }
 
-  private static boolean iterateCharacterRanges(TextRangeConsumer consumer, String undecoded, int off, boolean raw, boolean unicode) {
-    if (raw) {
-      return iterateRawCharacterRanges(consumer, undecoded, off, unicode);
-    }
-    Matcher escMatcher = PATTERN_ESCAPE.matcher(undecoded);
-    int index = 0;
-    while (escMatcher.find(index)) {
-      for (int i = index; i < escMatcher.start(); i++) {
-        if (!consumer.process(off + i, off + i + 1, Character.toString(undecoded.charAt(i)))) {
-          return false;
-        }
-      }
-      String octal = escMatcher.group(2);
-      String hex = escMatcher.group(3);
-      String str = null;
-      if (octal != null) {
-        str = new String(new char[]{(char)Integer.parseInt(octal, 8)});
-
-      }
-      else if (hex != null) {
-        str = new String(new char[]{(char)Integer.parseInt(hex, 16)});
-
-      }
-      else {
-        String toReplace = escMatcher.group(1);
-        String replacement = escapeMap.get(toReplace);
-        if (replacement != null) {
-          str = replacement;
-        }
-      }
-      String unicodeName = escMatcher.group(4);
-      String unicode32 = escMatcher.group(6);
-
-      if (unicode32 != null) {
-        str = unicode ? new String(Character.toChars((int)Long.parseLong(unicode32, 16))) : unicode32;
-      }
-      if (unicodeName != null) {
-        //TOLATER: implement unicode character name escapes
-      }
-      String unicode16 = escMatcher.group(5);
-      if (unicode16 != null) {
-        str = unicode ? new String(new char[]{(char)Integer.parseInt(unicode16, 16)}) : unicode16;
-      }
-
-      if (str != null) {
-        int start = escMatcher.start();
-        int end = escMatcher.end();
-        if (!consumer.process(off + start, off + end, str)) {
-          return false;
-        }
-      }
-      index = escMatcher.end();
-    }
-    for (int i = index; i < undecoded.length(); i++) {
-      if (!consumer.process(off + i, off + i + 1, Character.toString(undecoded.charAt(i)))) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  private static boolean iterateRawCharacterRanges(TextRangeConsumer consumer, String undecoded, int off, boolean unicode) {
-    for (int i = 0; i < undecoded.length(); i++) {
-      char c = undecoded.charAt(i);
-      if (unicode && c == '\\' && i < undecoded.length()-1) {
-        char c2 = undecoded.charAt(i+1);
-        if (c2 == 'u' && i < undecoded.length()-5) {
-          try {
-            char u = (char) Integer.parseInt(undecoded.substring(i+2, i+6), 16);
-            if (!consumer.process(off+i, off+i+ 6, Character.toString(u))) {
-              return false;
-            }
-          }
-          catch (NumberFormatException ignore) { }
-          //noinspection AssignmentToForLoopParameter
-          i += 5;
-          continue;
-        }
-        if (c2 == 'U' && i < undecoded.length()-9) {
-          // note: Java has 16-bit chars, so this code will truncate characters which don't fit in 16 bits
-          try {
-            char u = (char) Long.parseLong(undecoded.substring(i+2, i+10), 16);
-            if (!consumer.process(off+i, off+i+10, Character.toString(u))) {
-              return false;
-            }
-          }
-          catch (NumberFormatException ignore) { }
-          //noinspection AssignmentToForLoopParameter
-          i += 9;
-          continue;
-        }
-      }
-      if (!consumer.process(off + i, off + i + 1, Character.toString(c))) {
-        return false;
-      }
-    }
-
-    return true;
-  }
-
   @Override
   public String toString() {
     return super.toString() + ": " + getStringValue();
@@ -368,57 +361,60 @@
 
     @Override
     public boolean decode(@NotNull final TextRange rangeInsideHost, @NotNull final StringBuilder outChars) {
-      final PyDocStringOwner
-        docStringOwner = PsiTreeUtil.getParentOfType(myHost, PyDocStringOwner.class);
-      if (docStringOwner != null && myHost.equals(docStringOwner.getDocStringExpression())) {
-        outChars.append(myHost.getText(), rangeInsideHost.getStartOffset(), rangeInsideHost.getEndOffset());
-      }
-      else {
-        myHost.iterateCharacterRanges(new TextRangeConsumer() {
-          public boolean process(int startOffset, int endOffset, String value) {
-            int xsectStart = Math.max(startOffset, rangeInsideHost.getStartOffset());
-            int xsectEnd = Math.min(endOffset, rangeInsideHost.getEndOffset());
-            if (xsectEnd > xsectStart) {
-              outChars.append(value);
-            }
-            return endOffset < rangeInsideHost.getEndOffset();
+      for (Pair<TextRange, String> fragment : myHost.getDecodedFragments()) {
+        final TextRange encodedTextRange = fragment.getFirst();
+        final TextRange intersection = encodedTextRange.intersection(rangeInsideHost);
+        if (intersection != null && !intersection.isEmpty()) {
+          final String value = fragment.getSecond();
+          final String intersectedValue;
+          if (value.length() == 1 || value.length() == intersection.getLength()) {
+            intersectedValue = value;
           }
-        });
+          else {
+            final int start = Math.max(0, rangeInsideHost.getStartOffset() - encodedTextRange.getStartOffset());
+            final int end = Math.min(value.length(), start + intersection.getLength());
+            intersectedValue = value.substring(start, end);
+          }
+          outChars.append(intersectedValue);
+        }
       }
       return true;
     }
 
     @Override
     public int getOffsetInHost(final int offsetInDecoded, @NotNull final TextRange rangeInsideHost) {
-      final Ref<Integer> resultRef = Ref.create(-1);
-      final Ref<Integer> indexRef = Ref.create(0);
-      final Ref<Integer> lastEndOffsetRef = Ref.create(-1);
-      myHost.iterateCharacterRanges(new TextRangeConsumer() {
-        @Override
-        public boolean process(int startOffset, int endOffset, String value) {
-          if (startOffset > rangeInsideHost.getEndOffset()) {
-            return false;
+      int offset = 0;
+      int endOffset = -1;
+      for (Pair<TextRange, String> fragment : myHost.getDecodedFragments()) {
+        final TextRange encodedTextRange = fragment.getFirst();
+        final TextRange intersection = encodedTextRange.intersection(rangeInsideHost);
+        if (intersection != null && !intersection.isEmpty()) {
+          final String value = fragment.getSecond();
+          final int valueLength = value.length();
+          final int intersectionLength = intersection.getLength();
+          if (valueLength == 0) {
+            return -1;
           }
-          lastEndOffsetRef.set(endOffset);
-          if (startOffset >= rangeInsideHost.getStartOffset()) {
-            final int i = indexRef.get();
-            if (i == offsetInDecoded) {
-              resultRef.set(startOffset);
-              return false;
+          else if (valueLength == 1) {
+            if (offset == offsetInDecoded) {
+              return intersection.getStartOffset();
             }
-            indexRef.set(i + 1);
+            offset++;
           }
-          return true;
+          else {
+            if (offset + intersectionLength >= offsetInDecoded) {
+              final int delta = offsetInDecoded - offset;
+              return intersection.getStartOffset() + delta;
+            }
+            offset += intersectionLength;
+          }
+          endOffset = intersection.getEndOffset();
         }
-      });
-      final int result = resultRef.get();
-      if (result != -1) {
-        return result;
       }
-      // We should handle the position of a character at the end of rangeInsideHost, because LeafPatcher expects it to be valid
-      final int lastEndOffset = lastEndOffsetRef.get();
-      if (indexRef.get() == offsetInDecoded && lastEndOffset == rangeInsideHost.getEndOffset()) {
-        return lastEndOffset;
+      // XXX: According to the real use of getOffsetInHost() it should return the correct host offset for the offset in decoded at the
+      // end of the range inside host, not -1
+      if (offset == offsetInDecoded) {
+        return endOffset;
       }
       return -1;
     }
@@ -431,23 +427,7 @@
 
   @Override
   public int valueOffsetToTextOffset(int valueOffset) {
-    final Ref<Integer> offsetInDecodedRef = new Ref<Integer>(valueOffset);
-    final Ref<Integer> result = new Ref<Integer>(-1);
-    iterateCharacterRanges(new TextRangeConsumer() {
-      public boolean process(int startOffset, int endOffset, String value) {
-        if (value.length() > offsetInDecodedRef.get()) {
-          result.set(startOffset + offsetInDecodedRef.get());
-          return false;
-        }
-        offsetInDecodedRef.set(offsetInDecodedRef.get() - value.length());
-        if (offsetInDecodedRef.get() == 0) {
-          result.set(endOffset);
-          return false;
-        }
-        return true;
-      }
-    });
-    return result.get();
+    return createLiteralTextEscaper().getOffsetInHost(valueOffset, getStringValueTextRange());
   }
 
   public boolean characterNeedsEscaping(char c) {
diff --git a/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionManipulator.java b/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionManipulator.java
index f98acde..5032d12 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionManipulator.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionManipulator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,13 +20,15 @@
 import com.intellij.psi.AbstractElementManipulator;
 import com.jetbrains.python.PythonStringUtil;
 import com.jetbrains.python.psi.PyElementGenerator;
+import org.jetbrains.annotations.NotNull;
 
 /**
  * @author traff
  */
 public class PyStringLiteralExpressionManipulator extends AbstractElementManipulator<PyStringLiteralExpressionImpl> {
 
-  public PyStringLiteralExpressionImpl handleContentChange(PyStringLiteralExpressionImpl element, TextRange range, String newContent) {
+  @Override
+  public PyStringLiteralExpressionImpl handleContentChange(@NotNull PyStringLiteralExpressionImpl element, @NotNull TextRange range, String newContent) {
     Pair<String, String> quotes = PythonStringUtil.getQuotes(range.substring(element.getText()));
 
     if (quotes != null) {
@@ -39,8 +41,9 @@
       .replace(PyElementGenerator.getInstance(element.getProject()).createStringLiteralAlreadyEscaped(newName));
   }
 
+  @NotNull
   @Override
-  public TextRange getRangeInElement(PyStringLiteralExpressionImpl element) {
+  public TextRange getRangeInElement(@NotNull PyStringLiteralExpressionImpl element) {
     Pair<String, String> pair = PythonStringUtil.getQuotes(element.getText());
     if (pair != null) {
       return TextRange.from(pair.first.length(), element.getTextLength() - pair.first.length() - pair.second.length());
diff --git a/python/src/com/jetbrains/python/psi/impl/PySubscriptionExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PySubscriptionExpressionImpl.java
index c19e9ad..6143828 100644
--- a/python/src/com/jetbrains/python/psi/impl/PySubscriptionExpressionImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PySubscriptionExpressionImpl.java
@@ -19,6 +19,7 @@
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiPolyVariantReference;
 import com.intellij.psi.PsiReference;
+import com.intellij.psi.util.QualifiedName;
 import com.jetbrains.python.PyNames;
 import com.jetbrains.python.PyTokenTypes;
 import com.jetbrains.python.PythonDialectsTokenSetProvider;
@@ -37,10 +38,21 @@
     super(astNode);
   }
 
+  @NotNull
   public PyExpression getOperand() {
     return childToPsiNotNull(PythonDialectsTokenSetProvider.INSTANCE.getExpressionTokens(), 0);
   }
 
+  @NotNull
+  @Override
+  public PyExpression getRootOperand() {
+    PyExpression operand = getOperand();
+    while (operand instanceof PySubscriptionExpression) {
+      operand = ((PySubscriptionExpression)operand).getOperand();
+    }
+    return operand;
+  }
+
   @Nullable
   public PyExpression getIndexExpression() {
     return childToPsi(PythonDialectsTokenSetProvider.INSTANCE.getExpressionTokens(), 1);
@@ -74,7 +86,7 @@
           res = ((PySubscriptableType)type).getElementType(indexExpression, context);
         }
         else if (type instanceof PyCollectionType) {
-          res = ((PyCollectionType) type).getElementType(context);
+          res = ((PyCollectionType)type).getElementType(context);
         }
       }
     }
@@ -96,6 +108,17 @@
     return getOperand();
   }
 
+  @Nullable
+  @Override
+  public QualifiedName asQualifiedName() {
+    return PyPsiUtils.asQualifiedName(this);
+  }
+
+  @Override
+  public boolean isQualified() {
+    return getQualifier() != null;
+  }
+
   @Override
   public String getReferencedName() {
     String res = PyNames.GETITEM;
diff --git a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java
index fd49c25..c56111e 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java
@@ -60,6 +60,8 @@
  * @author yole
  */
 public class PyTargetExpressionImpl extends PyPresentableElementImpl<PyTargetExpressionStub> implements PyTargetExpression {
+  QualifiedName myQualifiedName;
+
   public PyTargetExpressionImpl(ASTNode astNode) {
     super(astNode);
   }
@@ -241,8 +243,8 @@
       if (exprType instanceof PyClassType) {
         final PyClass cls = ((PyClassType)exprType).getPyClass();
         final PyFunction enter = cls.findMethodByName(PyNames.ENTER, true);
-        if (enter != null) {
-          final PyType enterType = enter.getReturnType(context, null);
+        if (enter instanceof PyFunctionImpl) {
+          final PyType enterType = ((PyFunctionImpl)enter).getReturnTypeWithoutCallSite(context, expression);
           if (enterType != null) {
             return enterType;
           }
@@ -356,7 +358,7 @@
     }
     if (source != null) {
       final PyType sourceType = context.getType(source);
-      final PyType type = getIterationType(sourceType, source, context);
+      final PyType type = getIterationType(sourceType, source, this, context);
       if (type instanceof PyTupleType && target instanceof PyTupleExpression) {
         return getTypeFromTupleAssignment((PyTupleExpression)target, (PyTupleType)type);
       }
@@ -368,7 +370,8 @@
   }
 
   @Nullable
-  private static PyType getIterationType(@Nullable PyType iterableType, @Nullable PyExpression source, @NotNull TypeEvalContext context) {
+  private static PyType getIterationType(@Nullable PyType iterableType, @Nullable PyExpression source, @NotNull PsiElement anchor,
+                                         @NotNull TypeEvalContext context) {
     PyType result = null;
     if (iterableType instanceof PyCollectionType) {
       result = ((PyCollectionType)iterableType).getElementType(context);
@@ -386,43 +389,32 @@
       final Collection<PyType> members = ((PyUnionType)iterableType).getMembers();
       final List<PyType> iterationTypes = new ArrayList<PyType>();
       for (PyType member : members) {
-        iterationTypes.add(getIterationType(member, source, context));
+        iterationTypes.add(getIterationType(member, source, anchor, context));
       }
       return PyUnionType.union(iterationTypes);
     }
-    else if (iterableType instanceof PyClassType) {
-      final PyClass pyClass = ((PyClassType)iterableType).getPyClass();
-      for (PyTypeProvider provider: Extensions.getExtensions(PyTypeProvider.EP_NAME)) {
-        final PyType iterationType = provider.getIterationType(pyClass);
-        if (iterationType != null) {
-          result = iterationType;
-          break;
+    else if (iterableType != null && PyABCUtil.isSubtype(iterableType, PyNames.ITERATOR, context)) {
+      final PyFunction iterateMethod = findMethodByName(iterableType, PyNames.ITER, context);
+      PyType iterateMethodType = null;
+      if (iterateMethod != null) {
+        iterateMethodType = getContextSensitiveType(iterateMethod, context, source);
+      }
+      if (iterateMethodType instanceof PyCollectionType) {
+        final PyCollectionType collectionType = (PyCollectionType)iterateMethodType;
+        result = collectionType.getElementType(context);
+      }
+      if (result == null) {
+        final String nextMethodName = LanguageLevel.forElement(anchor).isAtLeast(LanguageLevel.PYTHON30) ?
+                                      PyNames.DUNDER_NEXT : PyNames.NEXT;
+        final PyFunction next = findMethodByName(iterableType, nextMethodName, context);
+        if (next != null) {
+          result = getContextSensitiveType(next, context, source);
         }
       }
-      if (PyABCUtil.isSubclass(pyClass, PyNames.ITERATOR)) {
-        final PyFunction iterateMethod = pyClass.findMethodByName(PyNames.ITER, true);
-        PyType iterateMethodType = null;
-        if (iterateMethod != null) {
-          iterateMethodType = getContextSensitiveType(iterateMethod, context, source);
-        }
-        if (iterateMethodType instanceof PyCollectionType) {
-          final PyCollectionType collectionType = (PyCollectionType)iterateMethodType;
-          result = collectionType.getElementType(context);
-        }
-        if (result == null) {
-          PyFunction next = pyClass.findMethodByName(PyNames.NEXT, true);
-          if (next == null) {
-            next = pyClass.findMethodByName(PyNames.DUNDER_NEXT, true);
-          }
-          if (next != null) {
-            result = getContextSensitiveType(next, context, source);
-          }
-        }
-        if (result == null) {
-          final PyFunction getItem = pyClass.findMethodByName(PyNames.GETITEM, true);
-          if (getItem != null) {
-            result = getContextSensitiveType(getItem, context, source);
-          }
+      if (result == null) {
+        final PyFunction getItem = findMethodByName(iterableType, PyNames.GETITEM, context);
+        if (getItem != null) {
+          result = getContextSensitiveType(getItem, context, source);
         }
       }
     }
@@ -430,6 +422,20 @@
   }
 
   @Nullable
+  private static PyFunction findMethodByName(@NotNull PyType type, @NotNull String name, @NotNull TypeEvalContext context) {
+    final PyResolveContext resolveContext = PyResolveContext.defaultContext().withTypeEvalContext(context);
+    final List<? extends RatedResolveResult> results = type.resolveMember(name, null, AccessDirection.READ, resolveContext);
+    if (results != null && !results.isEmpty()) {
+      final RatedResolveResult result = results.get(0);
+      final PsiElement element = result.getElement();
+      if (element instanceof PyFunction) {
+        return (PyFunction)element;
+      }
+    }
+    return null;
+  }
+
+  @Nullable
   private static PyType getContextSensitiveType(@NotNull PyFunction function, @NotNull TypeEvalContext context,
                                                 @Nullable PyExpression source) {
     if (function instanceof PyFunctionImpl) {
@@ -459,6 +465,15 @@
     return qualifier != null ? (PyExpression) qualifier.getPsi() : null;
   }
 
+  @Nullable
+  @Override
+  public QualifiedName asQualifiedName() {
+    if (myQualifiedName == null) {
+      myQualifiedName = PyPsiUtils.asQualifiedName(this);
+    }
+    return myQualifiedName;
+  }
+
   public String toString() {
     return super.toString() + ": " + getName();
   }
@@ -501,7 +516,7 @@
       }
       return null;
     }
-    return PyQualifiedNameFactory.fromExpression(findAssignedValue());
+    return PyPsiUtils.asQualifiedName(findAssignedValue());
   }
 
   @Nullable
@@ -552,7 +567,7 @@
     final PyExpression value = findAssignedValue();
     if (value instanceof PyCallExpression) {
       final PyExpression callee = ((PyCallExpression)value).getCallee();
-      return PyQualifiedNameFactory.fromExpression(callee);
+      return PyPsiUtils.asQualifiedName(callee);
     }
     return null;
   }
@@ -565,7 +580,7 @@
 
   @NotNull
   public PsiPolyVariantReference getReference(final PyResolveContext resolveContext) {
-    if (getQualifier() != null) {
+    if (isQualified()) {
       return new PyQualifiedReference(this, resolveContext);
     }
     return new PyTargetReference(this, resolveContext);
@@ -574,6 +589,9 @@
   @NotNull
   @Override
   public SearchScope getUseScope() {
+    if (isQualified()) {
+      return super.getUseScope();
+    }
     final ScopeOwner owner = ScopeUtil.getScopeOwner(this);
     if (owner != null) {
       final Scope scope = ControlFlowCache.getScope(owner);
@@ -590,7 +608,7 @@
     while(true) {
       PyElement parentContainer = PsiTreeUtil.getParentOfType(container, PyFunction.class, PyClass.class);
       if (parentContainer instanceof PyClass) {
-        if (getQualifier() != null) {
+        if (isQualified()) {
           return super.getUseScope();
         }
         break;
@@ -677,4 +695,10 @@
     }
     return null;
   }
+
+  @Override
+  public void subtreeChanged() {
+    super.subtreeChanged();
+    myQualifiedName = null;
+  }
 }
diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyFromImportSourceReference.java b/python/src/com/jetbrains/python/psi/impl/references/PyFromImportSourceReference.java
index 61ffb43..8cba7fb 100644
--- a/python/src/com/jetbrains/python/psi/impl/references/PyFromImportSourceReference.java
+++ b/python/src/com/jetbrains/python/psi/impl/references/PyFromImportSourceReference.java
@@ -18,7 +18,6 @@
 import com.intellij.lang.annotation.HighlightSeverity;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.util.PsiTreeUtil;
-import com.jetbrains.python.psi.PyExpression;
 import com.jetbrains.python.psi.PyFromImportStatement;
 import com.jetbrains.python.psi.impl.PyReferenceExpressionImpl;
 import com.jetbrains.python.psi.resolve.PyResolveContext;
@@ -53,7 +52,6 @@
 
   @Override
   public HighlightSeverity getUnresolvedHighlightSeverity(TypeEvalContext context) {
-    PyExpression qualifier = myElement.getQualifier();
-    return qualifier == null ? HighlightSeverity.ERROR : HighlightSeverity.WARNING;
+    return myElement.isQualified() ? HighlightSeverity.WARNING : HighlightSeverity.ERROR;
   }
 }
diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyQualifiedReference.java b/python/src/com/jetbrains/python/psi/impl/references/PyQualifiedReference.java
index 750c20f..e66a219 100644
--- a/python/src/com/jetbrains/python/psi/impl/references/PyQualifiedReference.java
+++ b/python/src/com/jetbrains/python/psi/impl/references/PyQualifiedReference.java
@@ -35,9 +35,14 @@
 import com.intellij.util.ProcessingContext;
 import com.intellij.util.indexing.FileBasedIndex;
 import com.jetbrains.python.PyNames;
+import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
 import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
+import com.jetbrains.python.codeInsight.dataflow.scope.Scope;
+import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
 import com.jetbrains.python.psi.*;
-import com.jetbrains.python.psi.impl.*;
+import com.jetbrains.python.psi.impl.PyBuiltinCache;
+import com.jetbrains.python.psi.impl.PyImportedModule;
+import com.jetbrains.python.psi.impl.ResolveResultList;
 import com.jetbrains.python.psi.resolve.*;
 import com.jetbrains.python.psi.search.PyProjectScopeBuilder;
 import com.jetbrains.python.psi.stubs.PyClassNameIndexInsensitive;
@@ -372,15 +377,28 @@
   }
 
   private static Collection<PyExpression> collectAssignedAttributes(PyQualifiedExpression qualifier) {
-    QualifiedName qualifierPath = PyQualifiedNameFactory.fromReferenceChain(PyResolveUtil.unwindQualifiers(qualifier));
-    if (qualifierPath != null) {
-      AssignmentCollectProcessor proc = new AssignmentCollectProcessor(qualifierPath);
-      PyResolveUtil.treeCrawlUp(proc, qualifier);
-      return proc.getResult();
+    final Set<String> names = new HashSet<String>();
+    final QualifiedName qualifierQName = qualifier.asQualifiedName();
+    if (qualifierQName != null) {
+      final List<PyExpression> results = new ArrayList<PyExpression>();
+      for (ScopeOwner owner = ScopeUtil.getScopeOwner(qualifier); owner != null; owner = ScopeUtil.getScopeOwner(owner)) {
+        final Scope scope = ControlFlowCache.getScope(owner);
+        for (PyTargetExpression target : scope.getTargetExpressions()) {
+          final QualifiedName targetQName = target.asQualifiedName();
+          if (targetQName != null) {
+            if (targetQName.getComponentCount() == qualifierQName.getComponentCount() + 1 && targetQName.matchesPrefix(qualifierQName)) {
+              final String name = target.getName();
+              if (!names.contains(name)) {
+                names.add(name);
+                results.add(target);
+              }
+            }
+          }
+        }
+      }
+      return results;
     }
-    else {
-      return Collections.emptyList();
-    }
+    return Collections.emptyList();
   }
 
   @Override
@@ -468,8 +486,8 @@
       return true;
     }
     if (element instanceof PyTargetExpression) {
-      return ((PyTargetExpression)element).getQualifier() == null &&
-             PsiTreeUtil.getParentOfType(element, ScopeOwner.class) instanceof PyFunction;
+      final PyTargetExpression target = (PyTargetExpression)element;
+      return !target.isQualified() && ScopeUtil.getScopeOwner(target) instanceof PyFunction;
     }
     return false;
   }
diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java
index 6a1b116..df4fb34 100644
--- a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java
@@ -550,10 +550,10 @@
   }
 
   private boolean haveQualifiers(PsiElement element) {
-    if (myElement.getQualifier() != null) {
+    if (myElement.isQualified()) {
       return true;
     }
-    if (element instanceof PyQualifiedExpression && ((PyQualifiedExpression)element).getQualifier() != null) {
+    if (element instanceof PyQualifiedExpression && ((PyQualifiedExpression)element).isQualified()) {
       return true;
     }
     return false;
diff --git a/python/src/com/jetbrains/python/psi/impl/stubs/PyClassElementType.java b/python/src/com/jetbrains/python/psi/impl/stubs/PyClassElementType.java
index 813fb5c..e3d399c 100644
--- a/python/src/com/jetbrains/python/psi/impl/stubs/PyClassElementType.java
+++ b/python/src/com/jetbrains/python/psi/impl/stubs/PyClassElementType.java
@@ -24,7 +24,6 @@
 import com.jetbrains.python.psi.impl.PyClassImpl;
 import com.jetbrains.python.psi.impl.PyPsiUtils;
 import com.intellij.psi.util.QualifiedName;
-import com.jetbrains.python.psi.impl.PyQualifiedNameFactory;
 import com.jetbrains.python.psi.stubs.PyClassNameIndex;
 import com.jetbrains.python.psi.stubs.PyClassNameIndexInsensitive;
 import com.jetbrains.python.psi.stubs.PyClassStub;
@@ -59,12 +58,16 @@
     final PyExpression[] exprs = psi.getSuperClassExpressions();
     List<QualifiedName> superClasses = new ArrayList<QualifiedName>();
     for (PyExpression expression : exprs) {
+      if (expression instanceof PyKeywordArgument) {
+        continue;
+      }
       expression = PyClassImpl.unfoldClass(expression);
-      superClasses.add(PyQualifiedNameFactory.fromExpression(expression));
+      superClasses.add(PyPsiUtils.asQualifiedName(expression));
     }
     final PyStringLiteralExpression docStringExpression = psi.getDocStringExpression();
     return new PyClassStubImpl(psi.getName(), parentStub,
                                superClasses.toArray(new QualifiedName[superClasses.size()]),
+                               PyPsiUtils.asQualifiedName(psi.getMetaClassExpression()),
                                psi.getOwnSlots(),
                                PyPsiUtils.strValue(docStringExpression),
                                getStubElementType());
@@ -77,6 +80,7 @@
     for (QualifiedName s : classes) {
       QualifiedName.serialize(s, dataStream);
     }
+    QualifiedName.serialize(pyClassStub.getMetaClass(), dataStream);
     PyFileElementType.writeNullableList(dataStream, pyClassStub.getSlots());
     final String docString = pyClassStub.getDocString();
     dataStream.writeUTFFast(docString != null ? docString : "");
@@ -90,9 +94,11 @@
     for (int i = 0; i < superClassCount; i++) {
       superClasses[i] = QualifiedName.deserialize(dataStream);
     }
+    final QualifiedName metaClass = QualifiedName.deserialize(dataStream);
     List<String> slots = PyFileElementType.readNullableList(dataStream);
     final String docString = dataStream.readUTFFast();
-    return new PyClassStubImpl(name, parentStub, superClasses, slots, docString.length() > 0 ? docString : null, getStubElementType());
+    return new PyClassStubImpl(name, parentStub, superClasses, metaClass, slots, docString.length() > 0 ? docString : null,
+                               getStubElementType());
   }
 
   public void indexStub(@NotNull final PyClassStub stub, @NotNull final IndexSink sink) {
diff --git a/python/src/com/jetbrains/python/psi/impl/stubs/PyClassStubImpl.java b/python/src/com/jetbrains/python/psi/impl/stubs/PyClassStubImpl.java
index e607478..0de9181 100644
--- a/python/src/com/jetbrains/python/psi/impl/stubs/PyClassStubImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/stubs/PyClassStubImpl.java
@@ -21,6 +21,7 @@
 import com.jetbrains.python.psi.PyClass;
 import com.intellij.psi.util.QualifiedName;
 import com.jetbrains.python.psi.stubs.PyClassStub;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.List;
 
@@ -30,14 +31,16 @@
 public class PyClassStubImpl extends StubBase<PyClass> implements PyClassStub {
   private final String myName;
   private final QualifiedName[] mySuperClasses;
+  @Nullable private final QualifiedName myMetaClass;
   private final List<String> mySlots;
   private final String myDocString;
 
-  public PyClassStubImpl(final String name, StubElement parentStub, final QualifiedName[] superClasses, final List<String> slots,
-                         String docString, IStubElementType stubElementType) {
+  public PyClassStubImpl(final String name, StubElement parentStub, final QualifiedName[] superClasses, @Nullable QualifiedName metaClass,
+                         final List<String> slots, String docString, IStubElementType stubElementType) {
     super(parentStub, stubElementType);
     myName = name;
     mySuperClasses = superClasses;
+    myMetaClass = metaClass;
     mySlots = slots;
     myDocString = docString;
   }
@@ -50,6 +53,12 @@
     return mySuperClasses;
   }
 
+  @Nullable
+  @Override
+  public QualifiedName getMetaClass() {
+    return myMetaClass;
+  }
+
   @Override
   public List<String> getSlots() {
     return mySlots;
diff --git a/python/src/com/jetbrains/python/psi/impl/stubs/PyTargetExpressionElementType.java b/python/src/com/jetbrains/python/psi/impl/stubs/PyTargetExpressionElementType.java
index f2a0678..aa535e8 100644
--- a/python/src/com/jetbrains/python/psi/impl/stubs/PyTargetExpressionElementType.java
+++ b/python/src/com/jetbrains/python/psi/impl/stubs/PyTargetExpressionElementType.java
@@ -85,7 +85,7 @@
         initializer = ((PyReferenceExpression) callee).asQualifiedName();
       }
     }
-    return new PyTargetExpressionStubImpl(name, docString, initializerType, initializer, psi.getQualifier() != null, parentStub);
+    return new PyTargetExpressionStubImpl(name, docString, initializerType, initializer, psi.isQualified(), parentStub);
   }
 
   public void serialize(@NotNull final PyTargetExpressionStub stub, @NotNull final StubOutputStream stream)
diff --git a/python/src/com/jetbrains/python/psi/resolve/AssignmentCollectProcessor.java b/python/src/com/jetbrains/python/psi/resolve/AssignmentCollectProcessor.java
index 0dde395..1f02473 100644
--- a/python/src/com/jetbrains/python/psi/resolve/AssignmentCollectProcessor.java
+++ b/python/src/com/jetbrains/python/psi/resolve/AssignmentCollectProcessor.java
@@ -19,11 +19,10 @@
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.ResolveState;
 import com.intellij.psi.scope.PsiScopeProcessor;
+import com.intellij.psi.util.QualifiedName;
 import com.jetbrains.python.psi.PyAssignmentStatement;
 import com.jetbrains.python.psi.PyExpression;
 import com.jetbrains.python.psi.PyTargetExpression;
-import com.intellij.psi.util.QualifiedName;
-import com.jetbrains.python.psi.impl.PyQualifiedNameFactory;
 import org.jetbrains.annotations.NotNull;
 
 import java.util.*;
@@ -56,15 +55,12 @@
       for (PyExpression ex : assignment.getTargets()) {
         if (ex instanceof PyTargetExpression) {
           final PyTargetExpression target = (PyTargetExpression)ex;
-          List<PyExpression> qualsExpr = PyResolveUtil.unwindQualifiers(target);
-          QualifiedName qualifiedName = PyQualifiedNameFactory.fromReferenceChain(qualsExpr);
+          final QualifiedName qualifiedName = target.asQualifiedName();
           if (qualifiedName != null) {
             if (qualifiedName.getComponentCount() == myQualifier.getComponentCount() + 1 && qualifiedName.matchesPrefix(myQualifier)) {
-              // a new attribute follows last qualifier; collect it.
-              PyExpression last_elt = qualsExpr.get(qualsExpr.size() - 1); // last item is the outermost, new, attribute.
-              String last_elt_name = last_elt.getName();
+              String last_elt_name = target.getName();
               if (!mySeenNames.contains(last_elt_name)) { // no dupes, only remember the latest
-                myResult.add(last_elt);
+                myResult.add(target);
                 mySeenNames.add(last_elt_name);
               }
             }
diff --git a/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java b/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java
index 9646170..da8c0d6 100644
--- a/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java
+++ b/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java
@@ -35,14 +35,9 @@
 import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
 import com.jetbrains.python.psi.*;
 import com.jetbrains.python.psi.impl.PyPsiUtils;
-import com.intellij.psi.util.QualifiedName;
-import com.jetbrains.python.psi.impl.PyQualifiedNameFactory;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-import java.util.LinkedList;
-import java.util.List;
-
 /**
  * Ref resolution routines.
  * User: dcheryasov
@@ -62,7 +57,7 @@
    * @return previous statement, or null.
    */
   @Nullable
-  public static PsiElement getPrevNodeOf(PsiElement elt, TokenSet elementTypes) {
+  private static PsiElement getPrevNodeOf(PsiElement elt, TokenSet elementTypes) {
     ASTNode seeker = elt.getNode();
     while (seeker != null) {
       ASTNode feeler = seeker.getTreePrev();
@@ -89,7 +84,7 @@
   }
 
   @Nullable
-  public static PsiElement getPrevNodeOf(PsiElement elt) {
+  private static PsiElement getPrevNodeOf(PsiElement elt) {
     if (elt instanceof PsiFile) return null;  // no sense to get the previous node of a file
     return getPrevNodeOf(elt, PythonDialectsTokenSetProvider.INSTANCE.getNameDefinerTokens());
   }
@@ -164,7 +159,10 @@
    * @param processor a visitor that says when the crawl is done and collects info.
    * @param elt       element from which we start (not checked by processor); if null, the search immediately returns null.
    * @return first element that the processor accepted.
+   *
+   * @deprecated Use {@link #scopeCrawlUp} instead.
    */
+  @Deprecated
   @Nullable
   public static PsiElement treeCrawlUp(PsiScopeProcessor processor, PsiElement elt) {
     if (elt == null || !elt.isValid()) return null; // can't find anyway.
@@ -231,7 +229,7 @@
    * @return true if an outer element is in a class context, while the inner is a method or function inside it.
    * @see com.jetbrains.python.psi.PyUtil#getConcealingParent(com.intellij.psi.PsiElement)
    */
-  protected static boolean refersFromMethodToClass(final PyFunction innerFunction, final PsiElement outer) {
+  private static boolean refersFromMethodToClass(final PyFunction innerFunction, final PsiElement outer) {
     if (innerFunction == null) {
       return false;
     }
@@ -244,49 +242,6 @@
   }
 
   /**
-   * Unwinds a multi-level qualified expression into a path, as seen in source text, i.e. outermost qualifier first.
-   *
-   * @param expr an expression to unwind.
-   * @return path as a list of ref expressions.
-   */
-  @NotNull
-  public static List<PyExpression> unwindQualifiers(@NotNull final PyQualifiedExpression expr) {
-    final List<PyExpression> path = new LinkedList<PyExpression>();
-    PyQualifiedExpression e = expr;
-    while (e != null) {
-      path.add(0, e);
-      final PyExpression q = e.getQualifier();
-      e = q instanceof PyQualifiedExpression ? (PyQualifiedExpression)q : null;
-    }
-    return path;
-  }
-
-  public static List<String> unwindQualifiersAsStrList(final PyQualifiedExpression expr) {
-    final List<String> path = new LinkedList<String>();
-    PyQualifiedExpression e = expr;
-    while (e != null) {
-      path.add(0, e.getText());
-      final PyExpression q = e.getQualifier();
-      e = q instanceof PyQualifiedExpression ? (PyQualifiedExpression)q : null;
-    }
-    return path;
-  }
-
-  public static String toPath(PyQualifiedExpression expr) {
-    if (expr == null) return "";
-    List<PyExpression> path = unwindQualifiers(expr);
-    final QualifiedName qName = PyQualifiedNameFactory.fromReferenceChain(path);
-    if (qName != null) {
-      return qName.toString();
-    }
-    String name = expr.getName();
-    if (name != null) {
-      return name;
-    }
-    return "";
-  }
-
-  /**
    * Accepts only targets that are not the given object.
    */
   public static class FilterNotInstance implements Condition<PsiElement> {
diff --git a/python/src/com/jetbrains/python/psi/resolve/PythonPathCache.java b/python/src/com/jetbrains/python/psi/resolve/PythonPathCache.java
index 28413c3..d2c0aa6 100644
--- a/python/src/com/jetbrains/python/psi/resolve/PythonPathCache.java
+++ b/python/src/com/jetbrains/python/psi/resolve/PythonPathCache.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
 import com.intellij.openapi.vfs.*;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.util.QualifiedName;
+import org.jetbrains.annotations.NotNull;
 
 import java.util.HashMap;
 import java.util.List;
@@ -53,27 +54,27 @@
 
   protected class MyVirtualFileAdapter extends VirtualFileAdapter {
     @Override
-    public void fileCreated(VirtualFileEvent event) {
+    public void fileCreated(@NotNull VirtualFileEvent event) {
       clearCache();
     }
 
     @Override
-    public void fileDeleted(VirtualFileEvent event) {
+    public void fileDeleted(@NotNull VirtualFileEvent event) {
       clearCache();
     }
 
     @Override
-    public void fileMoved(VirtualFileMoveEvent event) {
+    public void fileMoved(@NotNull VirtualFileMoveEvent event) {
       clearCache();
     }
 
     @Override
-    public void fileCopied(VirtualFileCopyEvent event) {
+    public void fileCopied(@NotNull VirtualFileCopyEvent event) {
       clearCache();
     }
 
     @Override
-    public void propertyChanged(VirtualFilePropertyEvent event) {
+    public void propertyChanged(@NotNull VirtualFilePropertyEvent event) {
       if (event.getPropertyName().equals(VirtualFile.PROP_NAME)) {
         clearCache();
       }
diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java
index 57fe1a6..6a80851 100644
--- a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java
+++ b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java
@@ -15,6 +15,7 @@
  */
 package com.jetbrains.python.psi.resolve;
 
+import com.intellij.openapi.extensions.Extensions;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.module.ModuleUtilCore;
 import com.intellij.openapi.projectRoots.Sdk;
@@ -366,6 +367,9 @@
           if (vFile != null && vFile.getLength() > 0) {
             rate += 100;
           }
+          for (PyResolveResultRater rater : Extensions.getExtensions(PyResolveResultRater.EP_NAME)) {
+            rate += rater.getRate(target);
+          }
         }
         ret.poke(target, rate);
       }
diff --git a/python/src/com/jetbrains/python/psi/search/PyClassInheritorsSearchExecutor.java b/python/src/com/jetbrains/python/psi/search/PyClassInheritorsSearchExecutor.java
index 4a7f084..bb29b7a 100644
--- a/python/src/com/jetbrains/python/psi/search/PyClassInheritorsSearchExecutor.java
+++ b/python/src/com/jetbrains/python/psi/search/PyClassInheritorsSearchExecutor.java
@@ -56,8 +56,8 @@
       if (processed.contains(superClass)) return true;
       processed.add(superClass);
       Project project = superClass.getProject();
-      final Collection<PyClass> candidates = StubIndex.getInstance().get(PySuperClassIndex.KEY, superClassName, project,
-                                                                         ProjectScope.getAllScope(project));
+      final Collection<PyClass> candidates = StubIndex.getElements(PySuperClassIndex.KEY, superClassName, project,
+                                                                   ProjectScope.getAllScope(project), PyClass.class);
       for (PyClass candidate : candidates) {
         final PyClass[] classes = candidate.getSuperClasses();
         for (PyClass superClassCandidate : classes) {
diff --git a/python/src/com/jetbrains/python/psi/search/PyKeywordArgumentSearchExecutor.java b/python/src/com/jetbrains/python/psi/search/PyKeywordArgumentSearchExecutor.java
index f8becd8d..1b4fd6a 100644
--- a/python/src/com/jetbrains/python/psi/search/PyKeywordArgumentSearchExecutor.java
+++ b/python/src/com/jetbrains/python/psi/search/PyKeywordArgumentSearchExecutor.java
@@ -21,6 +21,8 @@
 import com.intellij.psi.search.searches.ReferencesSearch;
 import com.intellij.psi.util.PsiTreeUtil;
 import com.intellij.util.Processor;
+import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
+import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
 import com.jetbrains.python.psi.*;
 import org.jetbrains.annotations.NotNull;
 
@@ -34,8 +36,8 @@
     if (!(element instanceof PyNamedParameter)) {
       return;
     }
-    PyFunction owner = PsiTreeUtil.getParentOfType(element, PyFunction.class);
-    if (owner == null) {
+    final ScopeOwner owner = ScopeUtil.getScopeOwner(element);
+    if (!(owner instanceof PyFunction)) {
       return;
     }
     ReferencesSearch.search(owner, queryParameters.getScope()).forEach(new Processor<PsiReference>() {
diff --git a/python/src/com/jetbrains/python/psi/search/PyStringReferenceSearch.java b/python/src/com/jetbrains/python/psi/search/PyStringReferenceSearch.java
index fa92691..dabd591 100644
--- a/python/src/com/jetbrains/python/psi/search/PyStringReferenceSearch.java
+++ b/python/src/com/jetbrains/python/psi/search/PyStringReferenceSearch.java
@@ -15,8 +15,6 @@
  */
 package com.jetbrains.python.psi.search;
 
-import com.intellij.openapi.application.AccessToken;
-import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.application.QueryExecutorBase;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.psi.PsiDirectory;
@@ -36,6 +34,11 @@
  * @author traff
  */
 public class PyStringReferenceSearch extends QueryExecutorBase<PsiReference, ReferencesSearch.SearchParameters> {
+
+  public PyStringReferenceSearch() {
+    super(true);
+  }
+
   public void processQuery(@NotNull final ReferencesSearch.SearchParameters params,
                            @NotNull final Processor<PsiReference> consumer) {
     final PsiElement element = params.getElementToSearch();
@@ -43,20 +46,12 @@
       return;
     }
 
-    AccessToken token = ApplicationManager.getApplication().acquireReadActionLock();
-    String name;
-    SearchScope searchScope;
-    try {
-      searchScope = params.getEffectiveSearchScope();
-      if (searchScope instanceof GlobalSearchScope) {
-        searchScope = GlobalSearchScope.getScopeRestrictedByFileTypes((GlobalSearchScope)searchScope, PythonFileType.INSTANCE);
-      }
+    SearchScope searchScope = params.getEffectiveSearchScope();
+    if (searchScope instanceof GlobalSearchScope) {
+      searchScope = GlobalSearchScope.getScopeRestrictedByFileTypes((GlobalSearchScope)searchScope, PythonFileType.INSTANCE);
+    }
 
-      name = PyUtil.computeElementNameForStringSearch(element);
-    }
-    finally {
-      token.finish();
-    }
+    String name = PyUtil.computeElementNameForStringSearch(element);
 
     if (StringUtil.isEmpty(name)) {
       return;
diff --git a/python/src/com/jetbrains/python/psi/stubs/PyClassNameIndex.java b/python/src/com/jetbrains/python/psi/stubs/PyClassNameIndex.java
index 8aed157..814f450 100644
--- a/python/src/com/jetbrains/python/psi/stubs/PyClassNameIndex.java
+++ b/python/src/com/jetbrains/python/psi/stubs/PyClassNameIndex.java
@@ -41,7 +41,7 @@
   }
 
   public static Collection<PyClass> find(String name, Project project, GlobalSearchScope scope) {
-    return StubIndex.getInstance().get(KEY, name, project, scope);
+    return StubIndex.getElements(KEY, name, project, scope, PyClass.class);
   }
 
   public static Collection<PyClass> find(String name, Project project, boolean includeNonProjectItems) {
diff --git a/python/src/com/jetbrains/python/psi/stubs/PyClassNameIndexInsensitive.java b/python/src/com/jetbrains/python/psi/stubs/PyClassNameIndexInsensitive.java
index e65ba65f2..a275426 100644
--- a/python/src/com/jetbrains/python/psi/stubs/PyClassNameIndexInsensitive.java
+++ b/python/src/com/jetbrains/python/psi/stubs/PyClassNameIndexInsensitive.java
@@ -38,6 +38,6 @@
   }
 
   public static Collection<PyClass> find(String name, Project project) {
-    return StubIndex.getInstance().get(KEY, name.toLowerCase(), project, ProjectScope.getProjectScope(project));
+    return StubIndex.getElements(KEY, name.toLowerCase(), project, ProjectScope.getProjectScope(project), PyClass.class);
   }
 }
diff --git a/python/src/com/jetbrains/python/psi/stubs/PyFunctionNameIndex.java b/python/src/com/jetbrains/python/psi/stubs/PyFunctionNameIndex.java
index 8b01c2b..ba87608 100644
--- a/python/src/com/jetbrains/python/psi/stubs/PyFunctionNameIndex.java
+++ b/python/src/com/jetbrains/python/psi/stubs/PyFunctionNameIndex.java
@@ -39,11 +39,11 @@
   }
 
   public static Collection<PyFunction> find(String name, Project project, GlobalSearchScope scope) {
-    return StubIndex.getInstance().get(KEY, name, project, scope);
+    return StubIndex.getElements(KEY, name, project, scope, PyFunction.class);
   }
 
   public static Collection<PyFunction> find(String name, Project project) {
-    return StubIndex.getInstance().get(KEY, name, project, ProjectScope.getAllScope(project));
+    return StubIndex.getElements(KEY, name, project, ProjectScope.getAllScope(project), PyFunction.class);
   }
 
   public static Collection<String> allKeys(Project project) {
diff --git a/python/src/com/jetbrains/python/psi/stubs/PyInstanceAttributeIndex.java b/python/src/com/jetbrains/python/psi/stubs/PyInstanceAttributeIndex.java
index 94b196f..58ea933 100644
--- a/python/src/com/jetbrains/python/psi/stubs/PyInstanceAttributeIndex.java
+++ b/python/src/com/jetbrains/python/psi/stubs/PyInstanceAttributeIndex.java
@@ -43,6 +43,6 @@
   }
 
   public static Collection<PyTargetExpression> find(String name, Project project, GlobalSearchScope scope) {
-    return StubIndex.getInstance().get(KEY, name, project, scope);
+    return StubIndex.getElements(KEY, name, project, scope, PyTargetExpression.class);
   }
 }
diff --git a/python/src/com/jetbrains/python/psi/stubs/PyModuleNameIndex.java b/python/src/com/jetbrains/python/psi/stubs/PyModuleNameIndex.java
new file mode 100644
index 0000000..105aec5
--- /dev/null
+++ b/python/src/com/jetbrains/python/psi/stubs/PyModuleNameIndex.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.psi.stubs;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiManager;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.util.indexing.*;
+import com.intellij.util.io.EnumeratorStringDescriptor;
+import com.intellij.util.io.KeyDescriptor;
+import com.jetbrains.python.PyNames;
+import com.jetbrains.python.PythonFileType;
+import com.jetbrains.python.psi.PyFile;
+import com.jetbrains.python.psi.search.PyProjectScopeBuilder;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.*;
+
+/**
+ * @author vlan
+ */
+public class PyModuleNameIndex extends ScalarIndexExtension<String> {
+  public static final ID<String, Void> NAME = ID.create("Py.module.name");
+
+  private final EnumeratorStringDescriptor myKeyDescriptor = new EnumeratorStringDescriptor();
+  private final DataIndexer<String, Void, FileContent> myDataIndexer = new DataIndexer<String, Void, FileContent>() {
+    @NotNull
+    @Override
+    public Map<String, Void> map(FileContent inputData) {
+      final VirtualFile file = inputData.getFile();
+      final String name = file.getName();
+      if (PyNames.INIT_DOT_PY.equals(name)) {
+        final VirtualFile parent = file.getParent();
+        if (parent != null && parent.isDirectory()) {
+          return Collections.singletonMap(parent.getName(), null);
+        }
+      }
+      else {
+        return Collections.singletonMap(FileUtil.getNameWithoutExtension(name), null);
+      }
+      return Collections.emptyMap();
+    }
+  };
+
+  @NotNull
+  @Override
+  public ID<String, Void> getName() {
+    return NAME;
+  }
+
+  @NotNull
+  @Override
+  public DataIndexer<String, Void, FileContent> getIndexer() {
+    return myDataIndexer;
+  }
+
+  @Override
+  public KeyDescriptor<String> getKeyDescriptor() {
+    return myKeyDescriptor;
+  }
+
+  @Override
+  public FileBasedIndex.InputFilter getInputFilter() {
+    return new DefaultFileTypeSpecificInputFilter(PythonFileType.INSTANCE);
+  }
+
+  @Override
+  public boolean dependsOnFileContent() {
+    return true;
+  }
+
+  @Override
+  public int getVersion() {
+    return 0;
+  }
+
+  @NotNull
+  public static Collection<String> getAllKeys(@NotNull Project project) {
+    return FileBasedIndex.getInstance().getAllKeys(NAME, project);
+  }
+
+  @NotNull
+  public static List<PyFile> find(@NotNull String name, @NotNull Project project, boolean includeNonProjectItems) {
+    final List<PyFile> results = new ArrayList<PyFile>();
+    final GlobalSearchScope scope = includeNonProjectItems
+                                    ? PyProjectScopeBuilder.excludeSdkTestsScope(project)
+                                    : GlobalSearchScope.projectScope(project);
+    final Collection<VirtualFile> files = FileBasedIndex.getInstance().getContainingFiles(NAME, name, scope);
+    for (VirtualFile virtualFile : files) {
+      final PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
+      if (psiFile instanceof PyFile) {
+        results.add((PyFile)psiFile);
+      }
+    }
+    return results;
+  }
+}
diff --git a/python/src/com/jetbrains/python/psi/stubs/PyVariableNameIndex.java b/python/src/com/jetbrains/python/psi/stubs/PyVariableNameIndex.java
index ca7b420..1fce6df 100644
--- a/python/src/com/jetbrains/python/psi/stubs/PyVariableNameIndex.java
+++ b/python/src/com/jetbrains/python/psi/stubs/PyVariableNameIndex.java
@@ -42,6 +42,6 @@
   }
 
   public static Collection<PyTargetExpression> find(String name, Project project, GlobalSearchScope scope) {
-    return StubIndex.getInstance().get(KEY, name, project, scope);
+    return StubIndex.getElements(KEY, name, project, scope, PyTargetExpression.class);
   }
 }
diff --git a/python/src/com/jetbrains/python/psi/types/PyABCUtil.java b/python/src/com/jetbrains/python/psi/types/PyABCUtil.java
index faf10ba..fcd3289 100644
--- a/python/src/com/jetbrains/python/psi/types/PyABCUtil.java
+++ b/python/src/com/jetbrains/python/psi/types/PyABCUtil.java
@@ -83,16 +83,26 @@
     return false;
   }
 
-  public static boolean isSubtype(@NotNull PyType type, @NotNull String superClassName) {
+  public static boolean isSubtype(@NotNull PyType type, @NotNull String superClassName, @NotNull TypeEvalContext context) {
     if (type instanceof PyClassType) {
-      final PyClass pyClass = ((PyClassType)type).getPyClass();
-      return isSubclass(pyClass, superClassName, true);
+      final PyClassType classType = (PyClassType)type;
+      final PyClass pyClass = classType.getPyClass();
+      if (classType.isDefinition()) {
+        final PyClassLikeType metaClassType = classType.getMetaClassType(context, true);
+        if (metaClassType instanceof PyClassType) {
+          final PyClassType metaClass = (PyClassType)metaClassType;
+          return isSubclass(metaClass.getPyClass(), superClassName, true);
+        }
+      }
+      else {
+        return isSubclass(pyClass, superClassName, true);
+      }
     }
     if (type instanceof PyUnionType) {
       final PyUnionType unionType = (PyUnionType)type;
       for (PyType m : unionType.getMembers()) {
         if (m != null) {
-          if (!isSubtype(m, superClassName)) {
+          if (!isSubtype(m, superClassName, context)) {
             return false;
           }
         }
diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java
index 7be077f..715421a 100644
--- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java
+++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java
@@ -23,6 +23,7 @@
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.Key;
 import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.Ref;
 import com.intellij.openapi.util.UserDataHolderBase;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiInvalidElementAccessException;
@@ -156,22 +157,9 @@
     }
 
     if (resolveContext.allowProperties()) {
-      Property property = myClass.findProperty(name);
-      if (property != null) {
-        Maybe<Callable> accessor = property.getByDirection(direction);
-        if (accessor.isDefined()) {
-          Callable accessor_code = accessor.value();
-          ResolveResultList ret = new ResolveResultList();
-          if (accessor_code != null) ret.poke(accessor_code, RatedResolveResult.RATE_NORMAL);
-          PyTargetExpression site = property.getDefinitionSite();
-          if (site != null) ret.poke(site, RatedResolveResult.RATE_LOW);
-          if (ret.size() > 0) {
-            return ret;
-          }
-          else {
-            return null;
-          } // property is found, but the required accessor is explicitly absent
-        }
+      final Ref<ResolveResultList> resultRef = findProperty(name, direction, true);
+      if (resultRef != null) {
+        return resultRef.get();
       }
     }
 
@@ -198,6 +186,11 @@
       return ResolveResultList.to(classMember);
     }
 
+    classMember = resolveByOverridingAncestorsMembersProviders(this, name, location);
+    if (classMember != null) {
+      return ResolveResultList.to(classMember);
+    }
+
     if (inherited) {
       for (PyClassLikeType type : myClass.getAncestorTypes(context)) {
         if (type instanceof PyClassType) {
@@ -216,7 +209,7 @@
     }
 
     if (isDefinition() && myClass.isNewStyleClass()) {
-      PyClassType typeType = getMetaclassType();
+      final PyClassLikeType typeType = getMetaClassType(context, inherited);
       if (typeType != null) {
         List<? extends RatedResolveResult> typeMembers = typeType.resolveMember(name, location, direction, resolveContext);
         if (typeMembers != null && !typeMembers.isEmpty()) {
@@ -237,12 +230,10 @@
       for (PyClassLikeType type : myClass.getAncestorTypes(context)) {
         if (type instanceof PyClassType) {
           final PyClass pyClass = ((PyClassType)type).getPyClass();
-          if (pyClass != null) {
-            PsiElement superMember = resolveByMembersProviders(new PyClassTypeImpl(pyClass, isDefinition()), name, location);
+          PsiElement superMember = resolveByMembersProviders(new PyClassTypeImpl(pyClass, isDefinition()), name, location);
 
-            if (superMember != null) {
-              return ResolveResultList.to(superMember);
-            }
+          if (superMember != null) {
+            return ResolveResultList.to(superMember);
           }
         }
       }
@@ -251,13 +242,47 @@
     return Collections.emptyList();
   }
 
-  @Nullable
-  private PyClassType getMetaclassType() {
-    final PyClass metaClass = PyUtil.getMetaClass(myClass);
-    if (metaClass != null) {
-      return new PyClassTypeImpl(metaClass, false);
+  private Ref<ResolveResultList> findProperty(String name, AccessDirection direction, boolean inherited) {
+    Ref<ResolveResultList> resultRef = null;
+    Property property = myClass.findProperty(name, inherited);
+    if (property != null) {
+      Maybe<Callable> accessor = property.getByDirection(direction);
+      if (accessor.isDefined()) {
+        Callable accessor_code = accessor.value();
+        ResolveResultList ret = new ResolveResultList();
+        if (accessor_code != null) ret.poke(accessor_code, RatedResolveResult.RATE_NORMAL);
+        PyTargetExpression site = property.getDefinitionSite();
+        if (site != null) ret.poke(site, RatedResolveResult.RATE_LOW);
+        if (ret.size() > 0) {
+          resultRef = Ref.create(ret);
+        }
+        else {
+          resultRef = Ref.create();
+        } // property is found, but the required accessor is explicitly absent
+      }
     }
-    return PyBuiltinCache.getInstance(myClass).getObjectType("type");
+    return resultRef;
+  }
+
+  @Nullable
+  @Override
+  public PyClassLikeType getMetaClassType(@NotNull TypeEvalContext context, boolean inherited) {
+    final PyType ownMeta = myClass.getMetaClassType(context);
+    if (ownMeta != null) {
+      return (ownMeta instanceof PyClassLikeType) ? (PyClassLikeType)ownMeta : null;
+    }
+    if (inherited) {
+      for (PyClassLikeType ancestor : myClass.getAncestorTypes(context)) {
+        if (ancestor != null) {
+          final PyClassLikeType ancestorMeta = ancestor.getMetaClassType(context, false);
+          if (ancestorMeta != null) {
+            return ancestorMeta;
+          }
+        }
+      }
+      return PyBuiltinCache.getInstance(myClass).getObjectType("type");
+    }
+    return null;
   }
 
   @Override
@@ -330,6 +355,17 @@
   }
 
   @Nullable
+  private static PsiElement resolveByOverridingAncestorsMembersProviders(PyClassType type, String name, @Nullable PyExpression location) {
+    for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) {
+      if (provider instanceof PyOverridingAncestorsClassMembersProvider) {
+        final PsiElement resolveResult = provider.resolveMember(type, name, location);
+        if (resolveResult != null) return resolveResult;
+      }
+    }
+    return null;
+  }
+
+  @Nullable
   private static PsiElement resolveInner(@NotNull PyClass cls,
                                          boolean isDefinition,
                                          @NotNull String name,
@@ -395,7 +431,7 @@
     }
 
     if (isDefinition() && myClass.isNewStyleClass()) {
-      final PyClassType typeType = getMetaclassType();
+      final PyClassLikeType typeType = getMetaClassType(typeEvalContext, true);
       if (typeType != null) {
         Collections.addAll(ret, typeType.getCompletionVariants(prefix, location, context));
       }
diff --git a/python/src/com/jetbrains/python/psi/types/functionalParser/FunctionalParserBase.java b/python/src/com/jetbrains/python/psi/types/functionalParser/FunctionalParserBase.java
index bde4559..6d33b68 100644
--- a/python/src/com/jetbrains/python/psi/types/functionalParser/FunctionalParserBase.java
+++ b/python/src/com/jetbrains/python/psi/types/functionalParser/FunctionalParserBase.java
@@ -203,11 +203,9 @@
         myCache.clear();
       }
       final SoftReference<Pair<R, State>> ref = myCache.get(state.getPos());
-      if (ref != null) {
-        final Pair<R, State> cached = ref.get();
-        if (cached != null) {
-          return cached;
-        }
+      final Pair<R, State> cached = SoftReference.dereference(ref);
+      if (cached != null) {
+        return cached;
       }
       final Pair<R, State> result = myParser.parse(tokens, state);
       myCache.put(state.getPos(), new SoftReference<Pair<R, State>>(result));
diff --git a/python/src/com/jetbrains/python/quickFixes/RemoveTrailingBlankLinesFix.java b/python/src/com/jetbrains/python/quickFixes/RemoveTrailingBlankLinesFix.java
new file mode 100644
index 0000000..318c129
--- /dev/null
+++ b/python/src/com/jetbrains/python/quickFixes/RemoveTrailingBlankLinesFix.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.quickFixes;
+
+import com.intellij.codeInsight.intention.HighPriorityAction;
+import com.intellij.codeInsight.intention.IntentionAction;
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author yole
+ */
+public class RemoveTrailingBlankLinesFix implements LocalQuickFix, IntentionAction, HighPriorityAction {
+  @NotNull
+  @Override
+  public String getText() {
+    return "Remove trailing blank lines";
+  }
+
+  @Override
+  public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
+    return true;
+  }
+
+  @Override
+  public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
+    removeTrailingBlankLines(file);
+  }
+
+  @Override
+  public boolean startInWriteAction() {
+    return true;
+  }
+
+  @NotNull
+  @Override
+  public String getName() {
+    return getText();
+  }
+
+  @NotNull
+  @Override
+  public String getFamilyName() {
+    return getText();
+  }
+
+  @Override
+  public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
+    removeTrailingBlankLines(descriptor.getPsiElement().getContainingFile());
+  }
+
+  private static void removeTrailingBlankLines(PsiFile file) {
+    Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
+    if (document == null) {
+      return;
+    }
+    int lastBlankLineOffset = -1;
+    for (int i = document.getLineCount() - 1; i >= 0; i--) {
+      int lineStart = document.getLineStartOffset(i);
+      String trimmed = document.getCharsSequence().subSequence(lineStart, document.getLineEndOffset(i)).toString().trim();
+      if (trimmed.length() > 0) {
+        break;
+      }
+      lastBlankLineOffset = lineStart;
+    }
+    if (lastBlankLineOffset > 0) {
+      document.deleteString(lastBlankLineOffset, document.getTextLength());
+    }
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureHandler.java b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureHandler.java
index 200c11e..719bc83 100644
--- a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureHandler.java
+++ b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureHandler.java
@@ -17,8 +17,6 @@
 
 import com.intellij.openapi.actionSystem.CommonDataKeys;
 import com.intellij.openapi.actionSystem.DataContext;
-import com.intellij.openapi.actionSystem.LangDataKeys;
-import com.intellij.openapi.actionSystem.PlatformDataKeys;
 import com.intellij.openapi.application.ex.ApplicationManagerEx;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.project.Project;
@@ -171,9 +169,9 @@
                                                 REFACTORING_NAME, Messages.getQuestionIcon());
       }
       switch (choice) {
-        case 0:
+        case Messages.YES:
           return deepestSuperMethod;
-        case 1:
+        case Messages.NO:
           return function;
         default:
           return null;
diff --git a/python/src/com/jetbrains/python/refactoring/classes/DependencyVisitor.java b/python/src/com/jetbrains/python/refactoring/classes/DependencyVisitor.java
new file mode 100644
index 0000000..b22449d
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/DependencyVisitor.java
@@ -0,0 +1,87 @@
+package com.jetbrains.python.refactoring.classes;
+
+import com.intellij.openapi.util.Condition;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiPolyVariantReference;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.jetbrains.python.psi.*;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Searches for element in another element's usages.
+ * Parametrize it with needle and make stack to accept it.
+ *
+ * @author Ilya.Kazakevich
+ */
+class DependencyVisitor extends PyRecursiveElementVisitor {
+
+  @NotNull
+  private final PyElement myElementToFind;
+  private boolean myDependencyFound;
+
+  /**
+   * @param elementToFind what to find
+   */
+  DependencyVisitor(@NotNull final PyElement elementToFind) {
+    myElementToFind = elementToFind;
+  }
+
+  @Override
+  public void visitPyCallExpression(@NotNull final PyCallExpression node) {
+    final PyExpression callee = node.getCallee();
+    if (callee != null) {
+      final PsiReference calleeReference = callee.getReference();
+      if ((calleeReference != null) && calleeReference.isReferenceTo(myElementToFind)) {
+        myDependencyFound = true;
+        return;
+      }
+      final String calleeName = callee.getName();
+
+      if ((calleeName != null) && calleeName.equals(myElementToFind.getName())) {  // Check by name also
+        myDependencyFound = true;
+      }
+    }
+  }
+
+  @Override
+  public void visitPyReferenceExpression(final PyReferenceExpression node) {
+
+    final PsiPolyVariantReference reference = node.getReference();
+    if (reference.isReferenceTo(myElementToFind)) {
+      myDependencyFound = true;
+      return;
+    }
+    // TODO: This step is member-type specific. Move to MemberManagers?
+    if (myElementToFind instanceof PyAssignmentStatement) {
+      final PyExpression[] targets = ((PyAssignmentStatement)myElementToFind).getTargets();
+
+      if (targets.length != 1) {
+        return;
+      }
+      final PyExpression expression = targets[0];
+
+      if (reference.isReferenceTo(expression)) {
+        myDependencyFound = true;
+        return;
+      }
+      if (node.getText().equals(expression.getText())) { // Check by name also
+        myDependencyFound = true;
+      }
+      return;
+    }
+    final PsiElement declaration = reference.resolve();
+    myDependencyFound = PsiTreeUtil.findFirstParent(declaration, new PsiElementCondition()) != null;
+  }
+
+  public boolean isDependencyFound() {
+    return myDependencyFound;
+  }
+
+  private class PsiElementCondition implements Condition<PsiElement> {
+    @Override
+    public boolean value(final PsiElement psiElement) {
+      return psiElement.equals(myElementToFind);
+    }
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/PyClassMembersRefactoringSupport.java b/python/src/com/jetbrains/python/refactoring/classes/PyClassMembersRefactoringSupport.java
index 71f920c..d2a0b0a 100644
--- a/python/src/com/jetbrains/python/refactoring/classes/PyClassMembersRefactoringSupport.java
+++ b/python/src/com/jetbrains/python/refactoring/classes/PyClassMembersRefactoringSupport.java
@@ -22,6 +22,7 @@
 import com.intellij.refactoring.classMembers.MemberInfoBase;
 import com.jetbrains.python.psi.PyClass;
 import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
 
 /**
  * @author Dennis.Ushakov
@@ -30,7 +31,7 @@
 
   public static PyMemberInfoStorage getSelectedMemberInfos(PyClass clazz, PsiElement element1, PsiElement element2) {
     final PyMemberInfoStorage infoStorage = new PyMemberInfoStorage(clazz);
-    for (PyMemberInfo member : infoStorage.getClassMemberInfos(clazz)) {
+    for (PyMemberInfo<PyElement> member : infoStorage.getClassMemberInfos(clazz)) {
       final PyElement function = member.getMember();
       member.setChecked(PsiTreeUtil.isAncestor(function, element1, false) ||
                         PsiTreeUtil.isAncestor(function, element2, false));
diff --git a/python/src/com/jetbrains/python/refactoring/classes/PyClassRefactoringHandler.java b/python/src/com/jetbrains/python/refactoring/classes/PyClassRefactoringHandler.java
index ebb20ff..54c5dd1 100644
--- a/python/src/com/jetbrains/python/refactoring/classes/PyClassRefactoringHandler.java
+++ b/python/src/com/jetbrains/python/refactoring/classes/PyClassRefactoringHandler.java
@@ -19,6 +19,7 @@
 import com.intellij.openapi.actionSystem.DataContext;
 import com.intellij.openapi.actionSystem.LangDataKeys;
 import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.application.ex.ApplicationManagerEx;
 import com.intellij.openapi.editor.CaretModel;
 import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.editor.Editor;
@@ -31,6 +32,7 @@
 import com.intellij.refactoring.util.CommonRefactoringUtil;
 import com.jetbrains.python.PyBundle;
 import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyUtil;
 import org.jetbrains.annotations.NotNull;
 
 /**
@@ -68,7 +70,27 @@
     doRefactor(project, elements[0], elements[elements.length - 1], editor, file, dataContext);
   }
 
-  protected abstract void doRefactor(Project project, PsiElement element1, PsiElement element2, Editor editor, PsiFile file, DataContext dataContext);
+  private void doRefactor(Project project, PsiElement element1, PsiElement element2, Editor editor, PsiFile file, DataContext dataContext) {
+    if (ApplicationManagerEx.getApplicationEx().isUnitTestMode()) return;
+
+    CommonRefactoringUtil.checkReadOnlyStatus(project, file);
+
+    final PyClass clazz = PyUtil.getContainingClassOrSelf(element1);
+    if (!inClass(clazz, project, editor, "refactoring.pull.up.error.cannot.perform.refactoring.not.inside.class")) return;
+    assert clazz != null;
+
+    final PyMemberInfoStorage infoStorage = PyClassMembersRefactoringSupport.getSelectedMemberInfos(clazz, element1, element2);
+
+    doRefactorImpl(project, clazz, infoStorage, editor);
+  }
+
+
+  protected abstract void doRefactorImpl(@NotNull final Project project,
+                                         @NotNull final PyClass classUnderRefactoring,
+                                         @NotNull final PyMemberInfoStorage infoStorage,
+                                         @NotNull final Editor editor);
+
+
 
   protected boolean inClass(PyClass clazz, Project project, Editor editor, String errorMessageId) {
     if (clazz == null) {
diff --git a/python/src/com/jetbrains/python/refactoring/classes/PyClassRefactoringUtil.java b/python/src/com/jetbrains/python/refactoring/classes/PyClassRefactoringUtil.java
index f5ba918..79f0382 100644
--- a/python/src/com/jetbrains/python/refactoring/classes/PyClassRefactoringUtil.java
+++ b/python/src/com/jetbrains/python/refactoring/classes/PyClassRefactoringUtil.java
@@ -15,26 +15,28 @@
  */
 package com.jetbrains.python.refactoring.classes;
 
+import com.google.common.collect.Collections2;
+import com.intellij.lang.ASTNode;
 import com.intellij.lang.injection.InjectedLanguageManager;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Comparing;
 import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.*;
 import com.intellij.psi.util.PsiTreeUtil;
 import com.intellij.psi.util.PsiUtilBase;
-import com.intellij.psi.util.PsiUtilCore;
+import com.intellij.psi.util.QualifiedName;
+import com.intellij.util.ArrayUtil;
+import com.jetbrains.NotNullPredicate;
 import com.jetbrains.python.PyNames;
-import com.jetbrains.python.PythonFileType;
 import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
 import com.jetbrains.python.codeInsight.imports.AddImportHelper;
-import com.jetbrains.python.documentation.DocStringTypeReference;
+import com.jetbrains.python.codeInsight.imports.PyImportOptimizer;
 import com.jetbrains.python.psi.*;
 import com.jetbrains.python.psi.impl.PyBuiltinCache;
 import com.jetbrains.python.psi.impl.PyImportedModule;
 import com.jetbrains.python.psi.impl.PyPsiUtils;
-import com.intellij.psi.util.QualifiedName;
 import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -44,137 +46,176 @@
 /**
  * @author Dennis.Ushakov
  */
-public class PyClassRefactoringUtil {
+public final class PyClassRefactoringUtil {
   private static final Logger LOG = Logger.getInstance(PyClassRefactoringUtil.class.getName());
   private static final Key<PsiNamedElement> ENCODED_IMPORT = Key.create("PyEncodedImport");
   private static final Key<Boolean> ENCODED_USE_FROM_IMPORT = Key.create("PyEncodedUseFromImport");
   private static final Key<String> ENCODED_IMPORT_AS = Key.create("PyEncodedImportAs");
 
-  private PyClassRefactoringUtil() {}
 
-  public static void moveSuperclasses(PyClass clazz, Set<String> superClasses, PyClass superClass) {
-    if (superClasses.size() == 0) return;
-    final Project project = clazz.getProject();
-    final List<PyExpression> toAdd = removeAndGetSuperClasses(clazz, superClasses);
-    addSuperclasses(project, superClass, toAdd, superClasses);
+  private PyClassRefactoringUtil() {
   }
 
-  public static void addSuperclasses(Project project, PyClass superClass,
-                                     @Nullable Collection<PyExpression> superClassesAsPsi,
-                                     Collection<String> superClassesAsStrings) {
-    if (superClassesAsStrings.size() == 0) return;
-    PyArgumentList argList = superClass.getSuperClassExpressionList();
-    if (argList != null) {
-      if (superClassesAsPsi != null) {
-        for (PyExpression element : superClassesAsPsi) {
-          argList.addArgument(element);
-        }
+
+  /**
+   * Copies class field declarations to some other place
+   *
+   * @param assignmentStatements list of class fields
+   *                             @param dequalifyIfDeclaredInClass If not null method will check if field declared in this class.
+   *                                                               If declared -- qualifier will be removed.
+   *                                                               For example: MyClass.Foo will become Foo it this param is MyClass.
+   * @return new (copied) fields
+   */
+  @NotNull
+  public static List<PyAssignmentStatement> copyFieldDeclarationToStatement(@NotNull final Collection<PyAssignmentStatement> assignmentStatements,
+                                                                            @NotNull final PyStatementList superClassStatement,
+                                                                            @Nullable final PyClass dequalifyIfDeclaredInClass) {
+    final List<PyAssignmentStatement> declarations = new ArrayList<PyAssignmentStatement>(assignmentStatements.size());
+    Collections.sort(declarations, PyDependenciesComparator.INSTANCE);
+
+
+    for (final PyAssignmentStatement pyAssignmentStatement : assignmentStatements) {
+      final PyElement value = pyAssignmentStatement.getAssignedValue();
+      final PyAssignmentStatement newDeclaration = (PyAssignmentStatement)pyAssignmentStatement.copy();
+
+      if (value instanceof PyReferenceExpression && dequalifyIfDeclaredInClass != null) {
+        final String newValue = getNewValueToAssign((PyReferenceExpression)value, dequalifyIfDeclaredInClass);
+
+        setNewAssigneeValue(newDeclaration, newValue);
+
       }
-      else {
-        for (String s : superClassesAsStrings) {
-          argList.addArgument(PyElementGenerator.getInstance(project).createExpressionFromText(s));
-        }
-      }
+
+      declarations.add(PyUtil.addElementToStatementList(newDeclaration, superClassStatement));
+      PyPsiUtils.removeRedundantPass(superClassStatement);
+    }
+    return declarations;
+  }
+
+  /**
+   * Sets new value to assignment statement.
+   * @param assignmentStatement statement to change
+   * @param newValue new value
+   */
+  private static void setNewAssigneeValue(@NotNull final PyAssignmentStatement assignmentStatement, @NotNull final String newValue) {
+    final PyExpression oldValue = assignmentStatement.getAssignedValue();
+    final PyExpression newExpression =
+      PyElementGenerator.getInstance(assignmentStatement.getProject()).createExpressionFromText(LanguageLevel.forElement(assignmentStatement), newValue);
+    if (oldValue != null) {
+      oldValue.replace(newExpression);
     } else {
-      addSuperclasses(project, superClass, superClassesAsStrings);
+      assignmentStatement.add(newExpression);
     }
   }
 
-  public static List<PyExpression> removeAndGetSuperClasses(PyClass clazz, Set<String> superClasses) {
-    if (superClasses.size() == 0) return Collections.emptyList();
-    final List<PyExpression> toAdd = new ArrayList<PyExpression>();
-    final PyExpression[] elements = clazz.getSuperClassExpressions();
-    for (PyExpression element : elements) {
-      if (superClasses.contains(element.getText())) {
-        toAdd.add(element);
-        PyUtil.removeListNode(element);
-      }
+  /**
+   * Checks if current value declared in provided class and removes class qualifier if true
+   * @param currentValue current value
+   * @param dequalifyIfDeclaredInClass  class to check
+   * @return value as string
+   */
+  @NotNull
+  private static String getNewValueToAssign(@NotNull final PyReferenceExpression currentValue, @NotNull final PyClass dequalifyIfDeclaredInClass) {
+    final PyExpression qualifier = currentValue.getQualifier();
+    if ((qualifier instanceof PyReferenceExpression) &&
+        ((PyReferenceExpression)qualifier).getReference().isReferenceTo(dequalifyIfDeclaredInClass)) {
+      final String name = currentValue.getName();
+      return ((name != null) ? name : currentValue.getText());
     }
-    return toAdd;
+    return currentValue.getText();
   }
 
-  public static void addSuperclasses(Project project, PyClass superClass, Collection<String> superClasses) {
-    if (superClasses.size() == 0) return;
-    final StringBuilder builder = new StringBuilder("(");
-    boolean hasChanges = false;
-    for (String element : superClasses) {
-      if (builder.length() > 1) builder.append(",");
-      if (!alreadyHasSuperClass(superClass, element)) {
-        builder.append(element);
-        hasChanges = true;
-      }
+  @NotNull
+  public static List<PyFunction> copyMethods(Collection<PyFunction> methods, PyClass superClass) {
+    if (methods.isEmpty()) {
+      return Collections.emptyList();
     }
-    builder.append(")");
-    if (!hasChanges) return;
-
-    final PsiFile file = PsiFileFactory.getInstance(project).createFileFromText(superClass.getName() + "temp", PythonFileType.INSTANCE, builder.toString());
-    final PsiElement expression = file.getFirstChild().getFirstChild();
-    PsiElement colon = superClass.getFirstChild();
-    while (colon != null && !colon.getText().equals(":")) {
-      colon = colon.getNextSibling();
-    }
-    LOG.assertTrue(colon != null && expression != null);
-    PyPsiUtils.addBeforeInParent(colon, expression);
-  }
-
-  private static boolean alreadyHasSuperClass(PyClass superClass, String className) {
-    for (PyClass aClass : superClass.getSuperClasses()) {
-      if (Comparing.strEqual(aClass.getName(), className)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  public static void moveMethods(Collection<PyFunction> methods, PyClass superClass) {
-    if (methods.size() == 0) return;
-    for (PsiElement e : methods) {
+    for (final PsiElement e : methods) {
       rememberNamedReferences(e);
     }
-    final PyElement[] elements = methods.toArray(new PyElement[methods.size()]);
-    addMethods(superClass, elements, true);
-    removeMethodsWithComments(elements);
+    final PyFunction[] elements = methods.toArray(new PyFunction[methods.size()]);
+    return addMethods(superClass, true,  elements);
   }
 
-  private static void removeMethodsWithComments(PyElement[] elements) {
-    for (PyElement element : elements) {
-      final Set<PsiElement> comments = PyUtil.getComments(element);
-      if (comments.size() > 0) {
-        PyPsiUtils.removeElements(PsiUtilCore.toPsiElementArray(comments));
+  /**
+   * Adds methods to class.
+   *
+   * @param destination where to add methods
+   * @param methods     methods
+   * @param skipIfExist do not add anything if method already exists
+   * @return newly added methods or existing one (if skipIfExists is true and method already exists)
+   */
+  @NotNull
+  public static List<PyFunction> addMethods(@NotNull final PyClass destination, final boolean skipIfExist, @NotNull final PyFunction... methods) {
+
+    final PyStatementList destStatementList = destination.getStatementList();
+    final List<PyFunction> result = new ArrayList<PyFunction>(methods.length);
+
+    for (final PyFunction method : methods) {
+
+      final PyFunction existingMethod = destination.findMethodByName(method.getName(), false);
+      if ((existingMethod != null) && skipIfExist) {
+        result.add(existingMethod);
+        continue; //We skip adding if class already has this method.
+      }
+
+
+      final PyFunction newMethod = insertMethodInProperPlace(destStatementList, method);
+      result.add(newMethod);
+      restoreNamedReferences(newMethod);
+    }
+
+    PyPsiUtils.removeRedundantPass(destStatementList);
+    return result;
+  }
+
+  /**
+   * Adds init methods before all other methods (but after class vars and docs).
+   * Adds all other methods to the bottom
+   *
+   * @param destStatementList where to add methods
+   * @param method            method to add
+   * @return newlty added method
+   */
+  @NotNull
+  private static PyFunction insertMethodInProperPlace(
+    @NotNull final PyStatementList destStatementList,
+    @NotNull final PyFunction method) {
+    boolean methodIsInit = PyUtil.isInit(method);
+    if (!methodIsInit) {
+      //Not init method could be inserted in the bottom
+      return (PyFunction)destStatementList.add(method);
+    }
+
+    //We should find appropriate place for init
+    for (final PsiElement element : destStatementList.getChildren()) {
+      final boolean elementComment = element instanceof PyExpressionStatement;
+      final boolean elementClassField = element instanceof PyAssignmentStatement;
+
+      if (!(elementComment || elementClassField)) {
+        return (PyFunction)destStatementList.addBefore(method, element);
       }
     }
-    PyPsiUtils.removeElements(elements);
+    return (PyFunction)destStatementList.add(method);
   }
 
-  public static void insertPassIfNeeded(PyClass clazz) {
-    final PyStatementList statements = clazz.getStatementList();
+
+  public static <T extends PyElement & PyStatementListContainer> void insertPassIfNeeded(@NotNull T element) {
+    final PyStatementList statements = element.getStatementList();
     if (statements.getStatements().length == 0) {
-      statements.add(PyElementGenerator.getInstance(clazz.getProject()).createFromText(LanguageLevel.getDefault(), PyPassStatement.class, PyNames.PASS));
+      statements.add(
+        PyElementGenerator.getInstance(element.getProject())
+          .createFromText(LanguageLevel.getDefault(), PyPassStatement.class, PyNames.PASS)
+      );
     }
   }
 
-  public static void addMethods(final PyClass superClass, final PyElement[] elements, final boolean up) {
-    if (elements.length == 0) return;
-    final PyStatementList statements = superClass.getStatementList();
-    for (PyElement newStatement : elements) {
-      if (up && newStatement instanceof PyFunction) {
-        final String name = newStatement.getName();
-        if (name != null && superClass.findMethodByName(name, false) != null) {
-          continue;
-        }
-      }
-      if (newStatement instanceof PyExpressionStatement && newStatement.getFirstChild() instanceof PyStringLiteralExpression) continue;
-      final PsiElement anchor = statements.add(newStatement);
-      restoreNamedReferences(anchor);
-      final Set<PsiElement> comments = PyUtil.getComments(newStatement);
-      for (PsiElement comment : comments) {
-        statements.addBefore(comment, anchor);
-      }
-    }
-    PyPsiUtils.removeRedundantPass(statements);
-  }
-
-  public static void restoreNamedReferences(@NotNull PsiElement element) {
+  /**
+   * Restores references saved by {@link #rememberNamedReferences(com.intellij.psi.PsiElement, String...)}.
+   *
+   * @param element newly created element to restore references
+   * @see #rememberNamedReferences(com.intellij.psi.PsiElement, String...)
+   */
+  public static void restoreNamedReferences(@NotNull final PsiElement element) {
     restoreNamedReferences(element, null);
   }
 
@@ -190,7 +231,7 @@
       public void visitPyStringLiteralExpression(PyStringLiteralExpression node) {
         super.visitPyStringLiteralExpression(node);
         for (PsiReference ref : node.getReferences()) {
-          if (ref instanceof DocStringTypeReference && ref.isReferenceTo(oldElement)) {
+          if (ref.isReferenceTo(oldElement)) {
             ref.bindToElement(newElement);
           }
         }
@@ -198,6 +239,7 @@
     });
   }
 
+
   private static void restoreReference(final PyReferenceExpression node) {
     PsiNamedElement target = node.getCopyableUserData(ENCODED_IMPORT);
     final String asName = node.getCopyableUserData(ENCODED_IMPORT_AS);
@@ -239,7 +281,7 @@
     if (components.isEmpty()) {
       return false;
     }
-    for (String s: components) {
+    for (String s : components) {
       if (!PyNames.isIdentifier(s) || PyNames.isReserved(s)) {
         return false;
       }
@@ -283,7 +325,16 @@
     }
   }
 
-  public static void rememberNamedReferences(@NotNull final PsiElement element) {
+  /**
+   * Searches for references inside some element (like {@link com.jetbrains.python.psi.PyAssignmentStatement}, {@link com.jetbrains.python.psi.PyFunction} etc
+   * and stored them.
+   * After that you can add element to some new parent. Newly created element then should be processed via {@link #restoreNamedReferences(com.intellij.psi.PsiElement)}
+   * and all references would be restored.
+   *
+   * @param element     element to store references for
+   * @param namesToSkip if reference inside of element has one of this names, it will not be saved.
+   */
+  public static void rememberNamedReferences(@NotNull final PsiElement element, @NotNull final String... namesToSkip) {
     element.acceptChildren(new PyRecursiveElementVisitor() {
       @Override
       public void visitPyReferenceExpression(PyReferenceExpression node) {
@@ -295,7 +346,9 @@
         if (importElement != null && PsiTreeUtil.isAncestor(element, importElement, false)) {
           return;
         }
-        rememberReference(node, element);
+        if (!ArrayUtil.contains(node.getText(), namesToSkip)) { //Do not remember name if it should be skipped
+          rememberReference(node, element);
+        }
       }
     });
   }
@@ -347,7 +400,7 @@
     final String name = getOriginalName(element);
     if (name != null) {
       PyImportElement importElement = null;
-      for (PyImportElement e: importStatement.getImportElements()) {
+      for (PyImportElement e : importStatement.getImportElements()) {
         if (name.equals(getOriginalName(e))) {
           importElement = e;
         }
@@ -364,11 +417,14 @@
         }
         if (deleteImportElement) {
           if (importStatement.getImportElements().length == 1) {
-            final boolean isInjected = InjectedLanguageManager.getInstance(importElement.getProject()).isInjectedFragment(importElement.getContainingFile());
-            if (!isInjected)
+            final boolean isInjected =
+              InjectedLanguageManager.getInstance(importElement.getProject()).isInjectedFragment(importElement.getContainingFile());
+            if (!isInjected) {
               importStatement.delete();
-            else
+            }
+            else {
               deleteImportStatementFromInjected(importStatement);
+            }
           }
           else {
             importElement.delete();
@@ -381,8 +437,7 @@
   private static void deleteImportStatementFromInjected(@NotNull final PyImportStatementBase importStatement) {
     final PsiElement sibling = importStatement.getPrevSibling();
     importStatement.delete();
-    if (sibling instanceof PsiWhiteSpace)
-      sibling.delete();
+    if (sibling instanceof PsiWhiteSpace) sibling.delete();
   }
 
   @Nullable
@@ -405,4 +460,108 @@
     }
     return null;
   }
+
+  /**
+   * Adds super classes to certain class.
+   *
+   * @param project      project where refactoring takes place
+   * @param clazz        destination
+   * @param superClasses classes to add
+   */
+  public static void addSuperclasses(@NotNull final Project project,
+                                     @NotNull final PyClass clazz,
+                                     @NotNull final PyClass... superClasses) {
+
+    final Collection<String> superClassNames = new ArrayList<String>();
+
+
+    for (final PyClass superClass : Collections2.filter(Arrays.asList(superClasses), NotNullPredicate.INSTANCE)) {
+      if (superClass.getName() != null) {
+        superClassNames.add(superClass.getName());
+        insertImport(clazz, superClass);
+      }
+    }
+
+    addSuperClassExpressions(project, clazz, superClassNames, null);
+  }
+
+
+  /**
+   * Adds expressions to superclass list
+   *
+   * @param project          project
+   * @param clazz            class to add expressions to superclass list
+   * @param paramExpressions param expressions. Like "object" or "MySuperClass". Will not add any param exp. if null.
+   * @param keywordArguments keyword args like "metaclass=ABCMeta". key-value pairs.  Will not add any keyword arg. if null.
+   */
+  public static void addSuperClassExpressions(@NotNull final Project project,
+                                              @NotNull final PyClass clazz,
+                                              @Nullable final Collection<String> paramExpressions,
+                                              @Nullable final Collection<Pair<String, String>> keywordArguments) {
+    final PyElementGenerator generator = PyElementGenerator.getInstance(project);
+    final LanguageLevel languageLevel = LanguageLevel.forElement(clazz);
+
+    PyArgumentList superClassExpressionList = clazz.getSuperClassExpressionList();
+    boolean addExpression = false;
+    if (superClassExpressionList == null) {
+      superClassExpressionList = generator.createFromText(languageLevel, PyClass.class, "class foo():pass").getSuperClassExpressionList();
+      assert superClassExpressionList != null : "expression not created";
+      addExpression = true;
+    }
+
+
+    generator.createFromText(LanguageLevel.PYTHON34, PyClass.class, "class foo(object, metaclass=Foo): pass").getSuperClassExpressionList();
+    if (paramExpressions != null) {
+      for (final String paramExpression : paramExpressions) {
+        superClassExpressionList.addArgument(generator.createParameter(paramExpression));
+      }
+    }
+
+    if (keywordArguments != null) {
+      for (final Pair<String, String> keywordArgument : keywordArguments) {
+        superClassExpressionList.addArgument(generator.createKeywordArgument(languageLevel, keywordArgument.first, keywordArgument.second));
+      }
+    }
+
+    // If class has no expression list, then we need to add it manually.
+    if (addExpression) {
+      final ASTNode classNameNode = clazz.getNameNode(); // For nameless classes we simply add expression list directly to them
+      final PsiElement elementToAddAfter = (classNameNode == null) ? clazz.getFirstChild() : classNameNode.getPsi();
+      clazz.addAfter(superClassExpressionList, elementToAddAfter);
+    }
+  }
+
+  /**
+   * Optimizes imports resorting them and removing unneeded
+   *
+   * @param file file to optimize imports
+   */
+  public static void optimizeImports(@NotNull final PsiFile file) {
+    new PyImportOptimizer().processFile(file).run();
+  }
+
+  /**
+   * Adds class attributeName (field) if it does not exist. like __metaclass__ = ABCMeta. Or CLASS_FIELD = 42.
+   *
+   * @param aClass        where to add
+   * @param attributeName attribute's name. Like __metaclass__ or CLASS_FIELD
+   * @param value         it's value. Like ABCMeta or 42.
+   * @return newly inserted attribute
+   */
+  @Nullable
+  public static PsiElement addClassAttributeIfNotExist(
+    @NotNull final PyClass aClass,
+    @NotNull final String attributeName,
+    @NotNull final String value) {
+    if (aClass.findClassAttribute(attributeName, false) != null) {
+      return null; //Do not add any if exist already
+    }
+    final PyElementGenerator generator = PyElementGenerator.getInstance(aClass.getProject());
+    final String text = String.format("%s = %s", attributeName, value);
+    final LanguageLevel level = LanguageLevel.forElement(aClass);
+
+    final PyAssignmentStatement assignmentStatement = generator.createFromText(level, PyAssignmentStatement.class, text);
+    //TODO: Add metaclass to the top. Add others between last attributeName and first method
+    return PyUtil.addElementToStatementList(assignmentStatement, aClass.getStatementList(), true);
+  }
 }
diff --git a/python/src/com/jetbrains/python/refactoring/classes/PyDependenciesComparator.java b/python/src/com/jetbrains/python/refactoring/classes/PyDependenciesComparator.java
new file mode 100644
index 0000000..700a300
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/PyDependenciesComparator.java
@@ -0,0 +1,70 @@
+package com.jetbrains.python.refactoring.classes;
+
+import com.jetbrains.python.psi.*;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * Compares elements by their dependencies.
+ * If A depends on B, then A &lt; B
+ */
+public class PyDependenciesComparator implements Comparator<PyElement>, Serializable {
+
+  /**
+   * Singleton comparator instance
+   */
+  public static final PyDependenciesComparator INSTANCE = new PyDependenciesComparator();
+
+  private PyDependenciesComparator() {
+  }
+
+  @Override
+  public int compare(@NotNull final PyElement o1, @NotNull final PyElement o2) {
+    if (depends(o1, o2)) {
+      return 1;
+    }
+    if (depends(o2, o1)) {
+      return -1;
+    }
+    return getBlockType(o1).compareTo(getBlockType(o2));
+  }
+
+  @NotNull
+  private static BlockType getBlockType(@NotNull final PyElement statement) {
+    for (BlockType type : BlockType.values()) {
+      if (type.myClass.isAssignableFrom(statement.getClass())) {
+        return type;
+      }
+    }
+
+    return BlockType.OTHER;
+  }
+
+  /**
+   * @return true if first param depends on second.
+   */
+  public static boolean depends(@NotNull final PyElement o1, @NotNull final PyElement o2) {
+    final DependencyVisitor visitor = new DependencyVisitor(o2);
+    o1.accept(visitor);
+    return visitor.isDependencyFound();
+  }
+
+  /**
+   * Types of class members in order, they should appear
+   */
+  private enum BlockType {
+    DOC(PyExpressionStatement.class),
+    DECLARATION(PyAssignmentStatement.class),
+    METHOD(PyFunction.class),
+    OTHER(PyElement.class);
+
+    @NotNull
+    private final Class<? extends PyElement> myClass;
+
+    BlockType(@NotNull final Class<? extends PyElement> aClass) {
+      myClass = aClass;
+    }
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/PyDependentMembersCollector.java b/python/src/com/jetbrains/python/refactoring/classes/PyDependentMembersCollector.java
index 5c51118..c47c474 100644
--- a/python/src/com/jetbrains/python/refactoring/classes/PyDependentMembersCollector.java
+++ b/python/src/com/jetbrains/python/refactoring/classes/PyDependentMembersCollector.java
@@ -17,7 +17,7 @@
 
 import com.intellij.refactoring.classMembers.DependentMembersCollectorBase;
 import com.jetbrains.python.psi.*;
-import com.jetbrains.python.psi.resolve.PyResolveContext;
+import com.jetbrains.python.refactoring.classes.membersManager.MembersManager;
 
 /**
  * @author Dennis.Ushakov
@@ -28,25 +28,7 @@
   }
 
   @Override
-  public void collect(PyElement member) {
-    final PyRecursiveElementVisitor visitor = new PyRecursiveElementVisitor() {
-      @Override
-      public void visitPyCallExpression(PyCallExpression node) {
-        final Callable markedFunction = node.resolveCalleeFunction(PyResolveContext.noImplicits());
-        final PyFunction function = markedFunction != null ? markedFunction.asMethod() : null;
-        if (!existsInSuperClass(function)) {
-          myCollection.add(function);
-        }
-      }
-    };
-    member.accept(visitor);
-  }
-
-  private boolean existsInSuperClass(PyFunction classMember) {
-    if (getSuperClass() == null) return false;
-    final String name = classMember != null ? classMember.getName() : null;
-    if (name == null) return false;
-    final PyFunction methodBySignature = (getSuperClass()).findMethodByName(name, true);
-    return methodBySignature != null;
+  public void collect(final PyElement member) {
+    myCollection.addAll(MembersManager.getAllDependencies(myClass, member, getSuperClass()));
   }
 }
diff --git a/python/src/com/jetbrains/python/refactoring/classes/PyMemberInfo.java b/python/src/com/jetbrains/python/refactoring/classes/PyMemberInfo.java
deleted file mode 100644
index 405e6e5..0000000
--- a/python/src/com/jetbrains/python/refactoring/classes/PyMemberInfo.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2000-2013 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jetbrains.python.refactoring.classes;
-
-import com.intellij.refactoring.RefactoringBundle;
-import com.intellij.refactoring.classMembers.MemberInfoBase;
-import com.jetbrains.python.psi.*;
-import com.jetbrains.python.refactoring.classes.ui.PyClassCellRenderer;
-
-/**
- * @author Dennis.Ushakov
- */
-public class PyMemberInfo extends MemberInfoBase<PyElement> {
-  public PyMemberInfo(PyElement member) {
-    super(member);
-    final PyClass clazz = PyUtil.getContainingClassOrSelf(member);
-    assert clazz != null;
-
-    if (member instanceof PyFunction) {
-      PyFunction function = (PyFunction)member;
-      displayName = buildDisplayMethodName(function);
-      for (PyClass aClass : clazz.getSuperClasses()) {
-        final PyFunction parentMethod = aClass.findMethodByName(function.getName(), true);
-        if (parentMethod != null) {
-          overrides = true;
-        }
-      }
-    } else if (member instanceof PyClass) {
-      displayName = RefactoringBundle.message("member.info.extends.0", PyClassCellRenderer.getClassText((PyClass)member));
-    }
-  }
-
-  private static String buildDisplayMethodName(PyFunction method) {
-    final StringBuilder builder = new StringBuilder(method.getName());
-    builder.append("(");
-    final PyParameter[] arguments = method.getParameterList().getParameters();
-    for (PyParameter parameter : arguments) {
-      builder.append(parameter.getName());
-      if (arguments.length > 1 && parameter != arguments[arguments.length - 1]) {
-        builder.append(", ");
-      }
-    }
-    builder.append(")");
-    return builder.toString();
-  }
-
-  @Override
-  public boolean equals(Object obj) {
-    if (obj instanceof PyMemberInfo) {
-      return getMember().equals(((PyMemberInfo)obj).getMember());
-    }
-    return false;
-  }
-
-  @Override
-  public int hashCode() {
-    return getMember().hashCode();
-  }
-}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/PyMemberInfoStorage.java b/python/src/com/jetbrains/python/refactoring/classes/PyMemberInfoStorage.java
index e5c050a..91d9093 100644
--- a/python/src/com/jetbrains/python/refactoring/classes/PyMemberInfoStorage.java
+++ b/python/src/com/jetbrains/python/refactoring/classes/PyMemberInfoStorage.java
@@ -15,23 +15,22 @@
  */
 package com.jetbrains.python.refactoring.classes;
 
-import com.intellij.psi.PsiElement;
 import com.intellij.refactoring.classMembers.AbstractMemberInfoStorage;
 import com.intellij.refactoring.classMembers.MemberInfoBase;
+import com.intellij.util.containers.HashSet;
 import com.jetbrains.python.psi.PyClass;
 import com.jetbrains.python.psi.PyElement;
 import com.jetbrains.python.psi.PyFunction;
 import com.jetbrains.python.refactoring.PyRefactoringUtil;
+import com.jetbrains.python.refactoring.classes.membersManager.MembersManager;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
 
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
 
 /**
  * @author Dennis.Ushakov
  */
-public class PyMemberInfoStorage extends AbstractMemberInfoStorage<PyElement, PyClass, PyMemberInfo> {
-  private Collection<PyClass> myClasses;
+public class PyMemberInfoStorage extends AbstractMemberInfoStorage<PyElement, PyClass, PyMemberInfo<PyElement>> {
 
   public PyMemberInfoStorage(PyClass aClass) {
     this(aClass, new MemberInfoBase.EmptyFilter<PyElement>());
@@ -53,12 +52,8 @@
 
   private void buildSubClassesMapImpl(PyClass aClass, HashSet<PyClass> visited) {
     visited.add(aClass);
-    if (myClasses == null) {
-      myClasses = new HashSet<PyClass>();
-    }
     for (PyClass clazz : aClass.getSuperClasses()) {
       getSubclasses(clazz).add(aClass);
-      myClasses.add(clazz);
       if (!visited.contains(clazz)) {
         buildSubClassesMapImpl(clazz, visited);
       }
@@ -66,13 +61,8 @@
   }
 
   @Override
-  protected void extractClassMembers(PyClass aClass, ArrayList<PyMemberInfo> temp) {
-    for (PyFunction function : aClass.getMethods()) {
-      temp.add(new PyMemberInfo(function));
-    }
-    for (PyClass pyClass : aClass.getSuperClasses()) {
-      temp.add(new PyMemberInfo(pyClass));
-    }
+  protected void extractClassMembers(PyClass aClass, ArrayList<PyMemberInfo<PyElement>> temp) {
+    temp.addAll(MembersManager.getAllMembersCouldBeMoved(aClass));
   }
 
   @Override
@@ -80,8 +70,4 @@
     return member1 instanceof PyFunction && member instanceof PyFunction &&
            PyRefactoringUtil.areConflictingMethods((PyFunction)member, (PyFunction)member1);
   }
-
-  public Collection<PyClass> getClasses() {
-    return myClasses;
-  }
 }
diff --git a/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassDialog.java b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassDialog.java
deleted file mode 100644
index c0bc437..0000000
--- a/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassDialog.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * Copyright 2000-2013 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jetbrains.python.refactoring.classes.extractSuperclass;
-
-import com.intellij.lang.LanguageNamesValidation;
-import com.intellij.lang.refactoring.NamesValidator;
-import com.intellij.openapi.fileChooser.FileChooserDescriptor;
-import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.roots.ProjectRootManager;
-import com.intellij.openapi.ui.TextComponentAccessor;
-import com.intellij.openapi.ui.TextFieldWithBrowseButton;
-import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.refactoring.RefactoringBundle;
-import com.intellij.refactoring.classMembers.AbstractUsesDependencyMemberInfoModel;
-import com.intellij.refactoring.classMembers.DependencyMemberInfoModel;
-import com.intellij.refactoring.ui.ConflictsDialog;
-import com.jetbrains.python.PyBundle;
-import com.jetbrains.python.PythonLanguage;
-import com.jetbrains.python.psi.PyClass;
-import com.jetbrains.python.psi.PyElement;
-import com.jetbrains.python.refactoring.classes.PyMemberInfo;
-import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
-import com.jetbrains.python.refactoring.classes.ui.UpDirectedMembersMovingDialog;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import javax.swing.*;
-import java.awt.*;
-import java.io.File;
-import java.io.IOException;
-import java.util.Collection;
-
-/**
- * @author Dennis.Ushakov
- */
-public class PyExtractSuperclassDialog  extends UpDirectedMembersMovingDialog {
-  private final NamesValidator myNamesValidator = LanguageNamesValidation.INSTANCE.forLanguage(PythonLanguage.getInstance());
-  protected JTextField mySourceClassField;
-  protected JLabel mySuperNameLabel;
-  protected JTextField myExtractedSuperNameField;
-  protected TextFieldWithBrowseButton myTargetDirField;
-  protected JLabel myDirLabel;
-  private static final String FILE_BROWSER_TITLE = "Extract superclass to file or directory:";
-
-  public PyExtractSuperclassDialog(Project project, PyClass clazz, PyMemberInfoStorage infoStorage) {
-    super(project, clazz);
-    myMemberInfos = infoStorage.getClassMemberInfos(myClass);
-
-    myExtractedSuperNameField = new JTextField();
-    myTargetDirField = new TextFieldWithBrowseButton();
-    initSourceClassField();
-
-    setTitle(PyExtractSuperclassHandler.REFACTORING_NAME);
-
-    init();
-  }
-
-  protected void initSourceClassField() {
-    mySourceClassField = new JTextField();
-    mySourceClassField.setEditable(false);
-    mySourceClassField.setText(myClass.getName());
-  }
-
-  @Override
-  protected void doOKAction() {
-    final String name = getSuperBaseName();
-    if (!myNamesValidator.isIdentifier(name, myClass.getProject())) {
-      setErrorText(PyBundle.message("refactoring.extract.super.name.0.must.be.ident", name));
-      return;
-    }
-    boolean found_root = false;
-    try {
-      String target_dir = FileUtil.toSystemIndependentName(new File(myTargetDirField.getText()).getCanonicalPath());
-      for (VirtualFile file : ProjectRootManager.getInstance(myClass.getProject()).getContentRoots()) {
-        if (StringUtil.startsWithIgnoreCase(target_dir, file.getPath())) {
-          found_root = true;
-          break;
-        }
-      }
-    }
-    catch (IOException ignore) {
-    }
-    if (! found_root) {
-      setErrorText(PyBundle.message("refactoring.extract.super.target.path.outside.roots"));
-      return;
-    }
-    super.doOKAction();
-  }
-
-  @Override
-  public JComponent getPreferredFocusedComponent() {
-    return myExtractedSuperNameField;
-  }
-
-  protected JPanel createNorthPanel() {
-    Box box = createBox();
-    box.add(Box.createVerticalStrut(10));
-
-    JPanel panel = new JPanel(new BorderLayout());
-    panel.add(box, BorderLayout.CENTER);
-    return panel;
-  }
-
-  protected Box createBox() {
-    Box box = Box.createVerticalBox();
-
-    JPanel _panel = new JPanel(new BorderLayout());
-    _panel.add(new JLabel(RefactoringBundle.message("extract.superclass.from")), BorderLayout.NORTH);
-    _panel.add(mySourceClassField, BorderLayout.CENTER);
-    box.add(_panel);
-
-    box.add(Box.createVerticalStrut(10));
-
-    mySuperNameLabel = new JLabel();
-    mySuperNameLabel.setText(RefactoringBundle.message("superclass.name"));
-
-    _panel = new JPanel(new BorderLayout());
-    _panel.add(mySuperNameLabel, BorderLayout.NORTH);
-    _panel.add(myExtractedSuperNameField, BorderLayout.CENTER);
-    box.add(_panel);
-    box.add(Box.createVerticalStrut(5));
-
-    final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFileOrFolderDescriptor();
-    final VirtualFile root = getRoot();
-    assert root != null;
-
-    final Project project = myClass.getProject();
-    descriptor.setRoots(ProjectRootManager.getInstance(project).getContentRoots());
-    descriptor.setIsTreeRootVisible(true);
-    myTargetDirField.setText(FileUtil.toSystemDependentName(root.getPath()));
-    myTargetDirField.addBrowseFolderListener(FILE_BROWSER_TITLE,
-                                       null, project, descriptor, TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT);
-
-    _panel = new JPanel(new BorderLayout());
-    myDirLabel = new JLabel();
-    myDirLabel.setText(FILE_BROWSER_TITLE);
-
-    _panel.add(myDirLabel, BorderLayout.NORTH);
-    _panel.add(myTargetDirField, BorderLayout.CENTER);
-    box.add(_panel);
-    return box;
-  }
-
-  @Nullable
-  protected VirtualFile getRoot() {
-    return myClass.getContainingFile().getVirtualFile();
-  }
-
-  @Override
-  protected String getMembersBorderTitle() {
-    return RefactoringBundle.message("members.to.form.superclass");
-  }
-
-  @Override
-  protected String getHelpId() {
-    return "python.reference.extractSuperclass";
-  }
-
-  @Override
-  protected DependencyMemberInfoModel<PyElement, PyMemberInfo> createMemberInfoModel() {
-    return new MyMemberInfoModel(myClass);
-  }
-
-  @Override
-  public boolean checkConflicts() {
-    final Collection<PyMemberInfo> infos = getSelectedMemberInfos();
-    if (!checkWritable(myClass, infos)) return false;
-    if (infos.size() == 0) {
-      ConflictsDialog conflictsDialog = new ConflictsDialog(myClass.getProject(), RefactoringBundle.message("no.members.selected"));
-      conflictsDialog.show();
-      return conflictsDialog.isOK();
-    }
-    return true;
-  }
-
-  public String getSuperBaseName() {
-    return myExtractedSuperNameField.getText();
-  }
-
-  public String getTargetFile() {
-    return myTargetDirField.getText();
-  }
-
-  private static class MyMemberInfoModel extends AbstractUsesDependencyMemberInfoModel<PyElement, PyClass, PyMemberInfo> {
-    public MyMemberInfoModel(PyClass clazz) {
-      super(clazz, null, false);
-    }
-
-    public boolean isAbstractEnabled(PyMemberInfo member) {
-      return false;
-    }
-
-    public int checkForProblems(@NotNull PyMemberInfo member) {
-      return member.isChecked() ? OK : super.checkForProblems(member);
-    }
-
-    @Override
-    protected int doCheck(@NotNull PyMemberInfo memberInfo, int problem) {
-      return problem;
-    }
-  }
-}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassHandler.java b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassHandler.java
index a68860d..747d9bf 100644
--- a/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassHandler.java
+++ b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassHandler.java
@@ -15,23 +15,18 @@
  */
 package com.jetbrains.python.refactoring.classes.extractSuperclass;
 
-import com.intellij.openapi.actionSystem.DataContext;
-import com.intellij.openapi.application.ex.ApplicationManagerEx;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.project.Project;
-import com.intellij.psi.PsiElement;
-import com.intellij.psi.PsiFile;
 import com.intellij.refactoring.RefactoringBundle;
 import com.intellij.refactoring.util.CommonRefactoringUtil;
-import com.intellij.util.PsiNavigateUtil;
+import com.jetbrains.python.PyBundle;
 import com.jetbrains.python.psi.PyClass;
 import com.jetbrains.python.psi.PyUtil;
-import com.jetbrains.python.refactoring.classes.PyClassMembersRefactoringSupport;
 import com.jetbrains.python.refactoring.classes.PyClassRefactoringHandler;
-import com.jetbrains.python.refactoring.classes.PyMemberInfo;
 import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
-
-import java.util.Collection;
+import com.jetbrains.python.vp.Creator;
+import com.jetbrains.python.vp.ViewPresenterUtils;
+import org.jetbrains.annotations.NotNull;
 
 /**
  * @author Dennis.Ushakov
@@ -39,27 +34,39 @@
 public class PyExtractSuperclassHandler extends PyClassRefactoringHandler {
   public static final String REFACTORING_NAME = RefactoringBundle.message("extract.superclass.title");
 
+
   @Override
-  protected void doRefactor(Project project, PsiElement element1, PsiElement element2, Editor editor, PsiFile file, DataContext dataContext) {
-    CommonRefactoringUtil.checkReadOnlyStatus(project, file);
-
-    final PyClass clazz = PyUtil.getContainingClassOrSelf(element1);
-    if (!inClass(clazz, project, editor, "refactoring.pull.up.error.cannot.perform.refactoring.not.inside.class")) return;
-
-    final PyMemberInfoStorage infoStorage = PyClassMembersRefactoringSupport.getSelectedMemberInfos(clazz, element1, element2);
-
-    if (ApplicationManagerEx.getApplicationEx().isUnitTestMode()) return;
-
-    final PyExtractSuperclassDialog dialog = new PyExtractSuperclassDialog(project, clazz, infoStorage);
-    dialog.show();
-    if(dialog.isOK()) {
-      extractWithHelper(clazz, dialog.getSelectedMemberInfos(), dialog.getSuperBaseName(), dialog.getTargetFile());
+  protected void doRefactorImpl(@NotNull final Project project,
+                                @NotNull final PyClass classUnderRefactoring,
+                                @NotNull final PyMemberInfoStorage infoStorage,
+                                @NotNull final Editor editor) {
+    //TODO: Move to presenter
+    if (PyUtil.filterOutObject(infoStorage.getClassMemberInfos(classUnderRefactoring)).isEmpty()) {
+      CommonRefactoringUtil.showErrorHint(project, editor, PyBundle
+        .message("refactoring.extract.super.class.no.members.allowed"), RefactoringBundle.message("extract.superclass.elements.header"),
+                                          null);
+      return;
     }
+
+    ViewPresenterUtils.linkViewWithPresenterAndLaunch(PyExtractSuperclassPresenter.class, PyExtractSuperclassView.class,
+                                                      new Creator<PyExtractSuperclassView, PyExtractSuperclassPresenter>() {
+                                                        @NotNull
+                                                        @Override
+                                                        public PyExtractSuperclassPresenter createPresenter(@NotNull final PyExtractSuperclassView view) {
+                                                          return new PyExtractSuperclassPresenterImpl(view, classUnderRefactoring,
+                                                                                                      infoStorage);
+                                                        }
+
+                                                        @NotNull
+                                                        @Override
+                                                        public PyExtractSuperclassView createView(@NotNull final PyExtractSuperclassPresenter presenter) {
+                                                          return new PyExtractSuperclassViewSwingImpl(classUnderRefactoring, project,
+                                                                                                      presenter);
+                                                        }
+                                                      }
+    );
   }
 
-  private static void extractWithHelper(PyClass clazz, Collection<PyMemberInfo> selectedMemberInfos, String superBaseName, String targetFile) {
-    PsiNavigateUtil.navigate(PyExtractSuperclassHelper.extractSuperclass(clazz, selectedMemberInfos, superBaseName, targetFile));
-  }
 
   @Override
   protected String getTitle() {
diff --git a/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassHelper.java b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassHelper.java
index 0d80337..52657c6 100644
--- a/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassHelper.java
+++ b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassHelper.java
@@ -15,95 +15,89 @@
  */
 package com.jetbrains.python.refactoring.classes.extractSuperclass;
 
-import com.intellij.openapi.application.ApplicationManager;
+import com.google.common.base.Predicate;
 import com.intellij.openapi.application.ex.ApplicationManagerEx;
-import com.intellij.openapi.command.CommandProcessor;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.roots.ProjectRootManager;
 import com.intellij.openapi.util.Comparing;
-import com.intellij.openapi.util.Ref;
 import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.openapi.vfs.LocalFileSystem;
-import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.openapi.vfs.VirtualFileManager;
 import com.intellij.psi.*;
-import com.intellij.refactoring.RefactoringBundle;
 import com.intellij.util.PathUtil;
 import com.jetbrains.python.PyNames;
 import com.jetbrains.python.PythonFileType;
 import com.jetbrains.python.psi.*;
-import com.jetbrains.python.psi.impl.PyPsiUtils;
 import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
-import com.jetbrains.python.refactoring.classes.PyMemberInfo;
+import com.jetbrains.python.refactoring.classes.membersManager.MembersManager;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.io.IOException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 
 /**
  * @author Dennis.Ushakov
  */
-public class PyExtractSuperclassHelper {
+public final class PyExtractSuperclassHelper {
   private static final Logger LOG = Logger.getInstance(PyExtractSuperclassHelper.class.getName());
+  /**
+   * Accepts only those members whose element is PyClass object (new classes)
+   */
+  private static final Predicate<PyMemberInfo<PyElement>> ALLOW_OBJECT = new PyUtil.ObjectPredicate(true);
 
-  private PyExtractSuperclassHelper() {}
+  private PyExtractSuperclassHelper() {
+  }
 
-  public static PsiElement extractSuperclass(final PyClass clazz,
-                                             final Collection<PyMemberInfo> selectedMemberInfos,
-                                             final String superBaseName,
-                                             final String targetFile) {
-    final Set<String> superClasses = new HashSet<String>();
-    final Set<PsiNamedElement> extractedClasses = new HashSet<PsiNamedElement>();
-    final List<PyFunction> methods = new ArrayList<PyFunction>();
-    for (PyMemberInfo member : selectedMemberInfos) {
-      final PyElement element = member.getMember();
-      if (element instanceof PyFunction) methods.add((PyFunction)element);
-      else if (element instanceof PyClass) {
-        extractedClasses.add((PyClass)element);
-        superClasses.add(element.getName());
+  static void extractSuperclass(final PyClass clazz,
+                                @NotNull Collection<PyMemberInfo<PyElement>> selectedMemberInfos,
+                                final String superBaseName,
+                                final String targetFile) {
+    //We will need to change it probably while param may be read-only
+    //noinspection AssignmentToMethodParameter
+    selectedMemberInfos = new ArrayList<PyMemberInfo<PyElement>>(selectedMemberInfos);
+
+    // PY-12171
+    final PyMemberInfo<PyElement> objectMember = MembersManager.findMember(selectedMemberInfos, ALLOW_OBJECT);
+    if (LanguageLevel.forElement(clazz).isPy3K()) {
+      // Remove object from list if Py3
+      if (objectMember != null) {
+        selectedMemberInfos.remove(objectMember);
       }
-      else LOG.error("unmatched member class " + element.getClass());
-    }
-
-    // 'object' superclass is always pulled up, even if not selected explicitly
-    for (PyExpression expr : clazz.getSuperClassExpressions()) {
-      if (PyNames.OBJECT.equals(expr.getText()) && !superClasses.contains(PyNames.OBJECT)) {
-        superClasses.add(PyNames.OBJECT);
+    } else {
+      // Always add object if < Py3
+      if (objectMember == null) {
+        final PyMemberInfo<PyElement> object = MembersManager.findMember(clazz, ALLOW_OBJECT);
+        if (object != null) {
+          selectedMemberInfos.add(object);
+        }
       }
     }
 
     final Project project = clazz.getProject();
-    final Ref<PyClass> newClassRef = new Ref<PyClass>();
-    CommandProcessor.getInstance().executeCommand(project, new Runnable() {
-      public void run() {
-        ApplicationManager.getApplication().runWriteAction(new Runnable() {
-          public void run() {
-            final PyElement[] elements = methods.toArray(new PyElement[methods.size()]);
-            final String text = "class " + superBaseName + ":\n  pass" + "\n";
-            PyClass newClass = PyElementGenerator.getInstance(project).createFromText(LanguageLevel.getDefault(), PyClass.class, text);
-            newClass = placeNewClass(project, newClass, clazz, targetFile);
-            newClassRef.set(newClass);
-            PyClassRefactoringUtil.moveMethods(methods, newClass);
-            PyClassRefactoringUtil.moveSuperclasses(clazz, superClasses, newClass);
-            PyClassRefactoringUtil.addSuperclasses(project, clazz, null, Collections.singleton(superBaseName));
-            PyClassRefactoringUtil.insertImport(newClass, extractedClasses);
-            if (elements.length > 0) {
-              PyPsiUtils.removeElements(elements);
-            }
-            PyClassRefactoringUtil.insertPassIfNeeded(clazz);
-          }
-        });
-      }
-    }, RefactoringBundle.message("extract.superclass.command.name", superBaseName, clazz.getName()), null);
-    return newClassRef.get();
+
+    final String text = "class " + superBaseName + ":\n  pass" + "\n";
+    PyClass newClass = PyElementGenerator.getInstance(project).createFromText(LanguageLevel.getDefault(), PyClass.class, text);
+
+    newClass = placeNewClass(project, newClass, clazz, targetFile);
+    MembersManager.moveAllMembers(selectedMemberInfos, clazz, newClass);
+    if (! newClass.getContainingFile().equals(clazz.getContainingFile())) {
+      PyClassRefactoringUtil.optimizeImports(clazz.getContainingFile()); // To remove unneeded imports only if user used different file
+    }
+    PyClassRefactoringUtil.addSuperclasses(project, clazz, null, newClass);
+
   }
 
-  private static PyClass placeNewClass(Project project, PyClass newClass, @NotNull PyClass clazz, String targetFile) {
-    VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(ApplicationManagerEx.getApplicationEx().isUnitTestMode() ? targetFile : VfsUtil.pathToUrl(targetFile));
+  private static PyClass placeNewClass(final Project project, PyClass newClass, @NotNull final PyClass clazz, final String targetFile) {
+    VirtualFile file = VirtualFileManager.getInstance()
+      .findFileByUrl(ApplicationManagerEx.getApplicationEx().isUnitTestMode() ? targetFile : VfsUtilCore.pathToUrl(targetFile));
     // file is the same as the source
     if (Comparing.equal(file, clazz.getContainingFile().getVirtualFile())) {
       return (PyClass)clazz.getParent().addBefore(newClass, clazz);
@@ -131,7 +125,8 @@
       else { // existing file
         psiFile = PsiManager.getInstance(project).findFile(file);
       }
-    } catch (IOException e) {
+    }
+    catch (IOException e) {
       LOG.error(e);
     }
 
@@ -147,6 +142,7 @@
 
   /**
    * Places a file at the end of given path, creating intermediate dirs and inits.
+   *
    * @param project
    * @param path
    * @param filename
@@ -157,6 +153,7 @@
     return placeFile(project, path, filename, null);
   }
 
+  //TODO: Mover to the other class? That is not good to dependent PyUtils on this class
   public static PsiFile placeFile(Project project, String path, String filename, @Nullable String content) throws IOException {
     PsiDirectory psiDir = createDirectories(project, path);
     LOG.assertTrue(psiDir != null);
@@ -177,8 +174,9 @@
 
   /**
    * Create all intermediate dirs with inits from one of roots up to target dir.
+   *
    * @param project
-   * @param target a full path to target dir
+   * @param target  a full path to target dir
    * @return deepest child directory, or null if target is not in roots or process fails at some point.
    */
   @Nullable
@@ -198,7 +196,7 @@
       }
     }
     if (the_root == null) {
-      throw new IOException("Can't find '"+ target +"' among roots");
+      throw new IOException("Can't find '" + target + "' among roots");
     }
     if (the_rest != null) {
       final LocalFileSystem lfs = LocalFileSystem.getInstance();
@@ -213,7 +211,9 @@
             throw new IOException("Expected dir, but got non-dir: " + subdir.getPath());
           }
         }
-        else subdir = the_root.createChildDirectory(lfs, dirs[i]);
+        else {
+          subdir = the_root.createChildDirectory(lfs, dirs[i]);
+        }
         VirtualFile init_vfile = subdir.findChild(PyNames.INIT_DOT_PY);
         if (init_vfile == null) init_vfile = subdir.createChildData(lfs, PyNames.INIT_DOT_PY);
         /*
@@ -235,5 +235,4 @@
     }
     return ret;
   }
-
 }
diff --git a/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassInfoModel.java b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassInfoModel.java
new file mode 100644
index 0000000..2bae38a
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassInfoModel.java
@@ -0,0 +1,31 @@
+package com.jetbrains.python.refactoring.classes.extractSuperclass;
+
+import com.intellij.refactoring.classMembers.AbstractUsesDependencyMemberInfoModel;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Ilya.Kazakevich
+ */
+class PyExtractSuperclassInfoModel extends AbstractUsesDependencyMemberInfoModel<PyElement, PyClass, PyMemberInfo<PyElement>> {
+  PyExtractSuperclassInfoModel(@NotNull final PyClass clazz) {
+    super(clazz, null, false);
+  }
+
+  @Override
+  public boolean isAbstractEnabled(final PyMemberInfo<PyElement> member) {
+    return member.isCouldBeAbstract() && isMemberEnabled(member);
+  }
+
+  @Override
+  public int checkForProblems(@NotNull final PyMemberInfo<PyElement> member) {
+    return member.isChecked() ? OK : super.checkForProblems(member);
+  }
+
+  @Override
+  protected int doCheck(@NotNull final PyMemberInfo<PyElement> memberInfo, final int problem) {
+    return problem;
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassInitializationInfo.java b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassInitializationInfo.java
new file mode 100644
index 0000000..9ba93c2
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassInitializationInfo.java
@@ -0,0 +1,46 @@
+package com.jetbrains.python.refactoring.classes.extractSuperclass;
+
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.refactoring.classMembers.MemberInfoModel;
+import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
+import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersViewInitializationInfo;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+
+/**
+ * View configuration for "extract superclass"
+ *
+ * @author Ilya.Kazakevich
+ */
+class PyExtractSuperclassInitializationInfo extends MembersViewInitializationInfo {
+
+  @NotNull
+  private final String myDefaultFilePath;
+  @NotNull
+  private final VirtualFile[] myRoots;
+
+  /**
+   * @param defaultFilePath module file path to display. User will be able to change it later.
+   * @param roots           virtual files where user may add new module
+   */
+  PyExtractSuperclassInitializationInfo(@NotNull final MemberInfoModel<PyElement, PyMemberInfo<PyElement>> memberInfoModel,
+                                        @NotNull final Collection<PyMemberInfo<PyElement>> memberInfos,
+                                        @NotNull final String defaultFilePath,
+                                        @NotNull final VirtualFile... roots) {
+    super(memberInfoModel, memberInfos);
+    myDefaultFilePath = defaultFilePath;
+    myRoots = roots.clone();
+  }
+
+  @NotNull
+  public String getDefaultFilePath() {
+    return myDefaultFilePath;
+  }
+
+  @NotNull
+  public VirtualFile[] getRoots() {
+    return myRoots.clone();
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassPresenter.java b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassPresenter.java
new file mode 100644
index 0000000..4e5edf7
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassPresenter.java
@@ -0,0 +1,10 @@
+package com.jetbrains.python.refactoring.classes.extractSuperclass;
+
+import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedPresenter;
+
+/**
+ * @author Ilya.Kazakevich
+ */
+public interface PyExtractSuperclassPresenter extends MembersBasedPresenter {
+  //TODO: Remove?
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassPresenterImpl.java b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassPresenterImpl.java
new file mode 100644
index 0000000..87ec445
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassPresenterImpl.java
@@ -0,0 +1,114 @@
+package com.jetbrains.python.refactoring.classes.extractSuperclass;
+
+import com.intellij.lang.LanguageNamesValidation;
+import com.intellij.lang.refactoring.NamesValidator;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiManager;
+import com.intellij.refactoring.RefactoringBundle;
+import com.intellij.refactoring.classMembers.MemberInfoModel;
+import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.PythonLanguage;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.psi.PyFile;
+import com.jetbrains.python.psi.PyUtil;
+import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
+import com.jetbrains.python.refactoring.classes.membersManager.vp.BadDataException;
+import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedPresenterNoPreviewImpl;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * @author Ilya.Kazakevich
+ */
+class PyExtractSuperclassPresenterImpl extends MembersBasedPresenterNoPreviewImpl<PyExtractSuperclassView,
+  MemberInfoModel<PyElement, PyMemberInfo<PyElement>>>
+  implements PyExtractSuperclassPresenter {
+  private final NamesValidator myNamesValidator = LanguageNamesValidation.INSTANCE.forLanguage(PythonLanguage.getInstance());
+
+  PyExtractSuperclassPresenterImpl(@NotNull final PyExtractSuperclassView view,
+                                   @NotNull final PyClass classUnderRefactoring,
+                                   @NotNull final PyMemberInfoStorage infoStorage) {
+    super(view, classUnderRefactoring, infoStorage, new PyExtractSuperclassInfoModel(classUnderRefactoring));
+  }
+
+  @Override
+  protected void validateView() throws BadDataException {
+    super.validateView();
+    final Project project = myClassUnderRefactoring.getProject();
+    if (!myNamesValidator.isIdentifier(myView.getSuperClassName(), project)) {
+      throw new BadDataException(PyBundle.message("refactoring.extract.super.name.0.must.be.ident", myView.getSuperClassName()));
+    }
+    boolean rootFound = false;
+    final File moduleFile = new File(myView.getModuleFile());
+    try {
+      final String targetDir = FileUtil.toSystemIndependentName(moduleFile.getCanonicalPath());
+      for (final VirtualFile file : ProjectRootManager.getInstance(project).getContentRoots()) {
+        if (StringUtil.startsWithIgnoreCase(targetDir, file.getPath())) {
+          rootFound = true;
+          break;
+        }
+      }
+    }
+    catch (final IOException ignore) {
+    }
+    if (!rootFound) {
+      throw new BadDataException(PyBundle.message("refactoring.extract.super.target.path.outside.roots"));
+    }
+
+    // TODO: Cover with test. It can't be done for now, because testFixture reports root path incorrectly
+    // PY-12173
+    myView.getModuleFile();
+    final VirtualFile moduleVirtualFile = LocalFileSystem.getInstance().findFileByIoFile(moduleFile);
+    if (moduleVirtualFile != null) {
+      final PsiFile psiFile = PsiManager.getInstance(project).findFile(moduleVirtualFile);
+      if (psiFile instanceof PyFile) {
+        if (((PyFile)psiFile).findTopLevelClass(myView.getSuperClassName()) != null) {
+          throw new BadDataException(PyBundle.message("refactoring.extract.super.target.class.already.exists", myView.getSuperClassName()));
+        }
+      }
+    }
+  }
+
+  @Override
+  public void launch() {
+    final String defaultFilePath = FileUtil.toSystemDependentName(myClassUnderRefactoring.getContainingFile().getVirtualFile().getPath());
+    final VirtualFile[] roots = ProjectRootManager.getInstance(myClassUnderRefactoring.getProject()).getContentRoots();
+    final Collection<PyMemberInfo<PyElement>> pyMemberInfos =
+      PyUtil.filterOutObject(myStorage.getClassMemberInfos(myClassUnderRefactoring));
+    myView.configure(
+      new PyExtractSuperclassInitializationInfo(myModel, pyMemberInfos, defaultFilePath,
+                                                roots)
+    );
+    myView.initAndShow();
+  }
+
+  @NotNull
+  @Override
+  protected String getCommandName() {
+    return RefactoringBundle.message("extract.superclass.command.name", myView.getSuperClassName(), myClassUnderRefactoring.getName());
+  }
+
+  @Override
+  protected void refactorNoPreview() {
+    PyExtractSuperclassHelper
+      .extractSuperclass(myClassUnderRefactoring, myView.getSelectedMemberInfos(), myView.getSuperClassName(), myView.getModuleFile());
+  }
+
+  @NotNull
+  @Override
+  protected Iterable<? extends PyClass> getDestClassesToCheckConflicts() {
+    return Collections.emptyList(); // No conflict can take place in newly created classes
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassView.java b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassView.java
new file mode 100644
index 0000000..d4dd249
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassView.java
@@ -0,0 +1,25 @@
+package com.jetbrains.python.refactoring.classes.extractSuperclass;
+
+import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedView;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Ilya.Kazakevich
+ */
+public interface PyExtractSuperclassView extends MembersBasedView<PyExtractSuperclassInitializationInfo> {
+
+  /**
+   *
+   * @return path to destination file (module) where user wants to create new class
+   */
+  @NotNull
+  String getModuleFile();
+
+  /**
+   *
+   * @return name user wants to give to new class
+   */
+  @NotNull
+  String getSuperClassName();
+
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassViewSwingImpl.java b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassViewSwingImpl.java
new file mode 100644
index 0000000..aed7301
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassViewSwingImpl.java
@@ -0,0 +1,108 @@
+package com.jetbrains.python.refactoring.classes.extractSuperclass;
+
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.ui.TextComponentAccessor;
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.refactoring.RefactoringBundle;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedViewSwingImpl;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * @author Ilya.Kazakevich
+ */
+class PyExtractSuperclassViewSwingImpl
+  extends MembersBasedViewSwingImpl<PyExtractSuperclassPresenter, PyExtractSuperclassInitializationInfo>
+  implements PyExtractSuperclassView {
+
+  private static final String FILE_OR_DIRECTORY = RefactoringBundle.message("extract.superclass.elements.header");
+  @NotNull
+  private final JTextArea myExtractedSuperNameField = new JTextArea();
+  @NotNull
+  private final FileChooserDescriptor myFileChooserDescriptor;
+  @NotNull
+  private final TextFieldWithBrowseButton myTargetDirField;
+
+  PyExtractSuperclassViewSwingImpl(@NotNull final PyClass classUnderRefactoring,
+                                   @NotNull final Project project,
+                                   @NotNull final PyExtractSuperclassPresenter presenter) {
+    super(project, presenter, RefactoringBundle.message("extract.superclass.from"), true);
+    setTitle(PyExtractSuperclassHandler.REFACTORING_NAME);
+
+
+    final Box box = Box.createVerticalBox();
+
+    JPanel panel = new JPanel(new BorderLayout());
+    panel.add(new JLabel(RefactoringBundle.message("extract.superclass.from")), BorderLayout.NORTH);
+    final JTextField sourceClassField = new JTextField();
+    sourceClassField.setEditable(false);
+    sourceClassField.setText(classUnderRefactoring.getName());
+    panel.add(sourceClassField, BorderLayout.CENTER);
+    box.add(panel);
+
+    box.add(Box.createVerticalStrut(10));
+
+    final JLabel superNameLabel = new JLabel();
+    superNameLabel.setText(RefactoringBundle.message("superclass.name"));
+
+    panel = new JPanel(new BorderLayout());
+    panel.add(superNameLabel, BorderLayout.NORTH);
+    panel.add(myExtractedSuperNameField, BorderLayout.CENTER);
+    box.add(panel);
+    box.add(Box.createVerticalStrut(5));
+
+    myFileChooserDescriptor = FileChooserDescriptorFactory.createSingleFileOrFolderDescriptor();
+
+
+    myFileChooserDescriptor.setRoots(ProjectRootManager.getInstance(project).getContentRoots());
+    myFileChooserDescriptor.setIsTreeRootVisible(true);
+    myTargetDirField = new TextFieldWithBrowseButton();
+    myTargetDirField
+      .addBrowseFolderListener(FILE_OR_DIRECTORY, null, project, myFileChooserDescriptor, TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT);
+
+    panel = new JPanel(new BorderLayout());
+    final JLabel dirLabel = new JLabel();
+    dirLabel.setText(FILE_OR_DIRECTORY); //u18n
+
+    panel.add(dirLabel, BorderLayout.NORTH);
+    panel.add(myTargetDirField, BorderLayout.CENTER);
+    box.add(panel);
+
+    box.add(Box.createVerticalStrut(10));
+
+
+    myTopPanel.add(box, BorderLayout.CENTER);
+    myCenterPanel.add(myPyMemberSelectionPanel, BorderLayout.CENTER);
+    setPreviewResults(false);
+  }
+
+  @Override
+  public JComponent getPreferredFocusedComponent() {
+    return myExtractedSuperNameField;
+  }
+
+  @Override
+  public void configure(@NotNull final PyExtractSuperclassInitializationInfo configInfo) {
+    super.configure(configInfo);
+    myFileChooserDescriptor.setRoots(configInfo.getRoots());
+    myTargetDirField.setText(configInfo.getDefaultFilePath());
+  }
+
+  @NotNull
+  @Override
+  public String getModuleFile() {
+    return myTargetDirField.getText();
+  }
+
+  @NotNull
+  @Override
+  public String getSuperClassName() {
+    return myExtractedSuperNameField.getText();
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/package-info.java b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/package-info.java
new file mode 100644
index 0000000..32852cb
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * "Extract superclass" refactoring
+ * @author Ilya.Kazakevich
+ */
+package com.jetbrains.python.refactoring.classes.extractSuperclass;
\ No newline at end of file
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/ClassFieldsManager.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/ClassFieldsManager.java
new file mode 100644
index 0000000..3bf4f00
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/ClassFieldsManager.java
@@ -0,0 +1,54 @@
+package com.jetbrains.python.refactoring.classes.membersManager;
+
+import com.jetbrains.python.psi.PyAssignmentStatement;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.psi.PyTargetExpression;
+import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Moves class attributes up
+ *
+ * @author Ilya.Kazakevich
+ */
+class ClassFieldsManager extends FieldsManager {
+
+  ClassFieldsManager() {
+    super(true);
+  }
+
+  @Override
+  public boolean hasConflict(@NotNull final PyTargetExpression member, @NotNull final PyClass aClass) {
+    return NamePredicate.hasElementWithSameName(member, aClass.getClassAttributes());
+  }
+
+  @Override
+  protected Collection<PyElement> moveAssignments(@NotNull final PyClass from,
+                                                  @NotNull final Collection<PyAssignmentStatement> statements,
+                                                  @NotNull final PyClass... to) {
+    //TODO: Copy/paste with InstanceFieldsManager. Move to parent?
+    final List<PyElement> result = new ArrayList<PyElement>();
+    for (final PyClass destClass : to) {
+      result.addAll(PyClassRefactoringUtil.copyFieldDeclarationToStatement(statements, destClass.getStatementList(), destClass));
+    }
+    deleteElements(statements);
+    PyClassRefactoringUtil.insertPassIfNeeded(from);
+    return result;
+  }
+
+  @Override
+  protected boolean classHasField(@NotNull final PyClass pyClass, @NotNull final String fieldName) {
+    return pyClass.findClassAttribute(fieldName, true) != null;
+  }
+
+  @NotNull
+  @Override
+  protected List<PyTargetExpression> getFieldsByClass(@NotNull final PyClass pyClass) {
+    return pyClass.getClassAttributes();
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/FieldsManager.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/FieldsManager.java
new file mode 100644
index 0000000..cb15b2b5
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/FieldsManager.java
@@ -0,0 +1,157 @@
+package com.jetbrains.python.refactoring.classes.membersManager;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.util.containers.MultiMap;
+import com.jetbrains.NotNullPredicate;
+import com.jetbrains.python.psi.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Parent of all field-based plugins (like class fields, instance fields and so on)
+ *
+ * @author Ilya.Kazakevich
+ */
+abstract class FieldsManager extends MembersManager<PyTargetExpression> {
+  private static final SimpleAssignmentsOnly SIMPLE_ASSIGNMENTS_ONLY = new SimpleAssignmentsOnly();
+  private static final AssignmentTransform ASSIGNMENT_TRANSFORM = new AssignmentTransform();
+  private final boolean myStatic;
+
+  /**
+   * @param isStatic is field static or not?
+   */
+  protected FieldsManager(final boolean isStatic) {
+    super(PyTargetExpression.class);
+    myStatic = isStatic;
+  }
+
+
+  @NotNull
+  @Override
+  protected Collection<PyElement> getDependencies(@NotNull final MultiMap<PyClass, PyElement> usedElements) {
+    return Collections.emptyList();
+  }
+
+  @NotNull
+  @Override
+  protected MultiMap<PyClass, PyElement> getDependencies(@NotNull final PyElement member) {
+    final MultiMap<PyClass, PyElement> result = new MultiMap<PyClass, PyElement>();
+    member.accept(new MyPyRecursiveElementVisitor(result));
+    return result;
+  }
+
+  @Override
+  protected Collection<? extends PyElement> getElementsToStoreReferences(@NotNull final Collection<PyTargetExpression> elements) {
+    // We need to save references from assignments
+    return Collections2.transform(elements, ASSIGNMENT_TRANSFORM);
+  }
+
+  @NotNull
+  @Override
+  protected List<PyElement> getMembersCouldBeMoved(@NotNull final PyClass pyClass) {
+    return Lists.<PyElement>newArrayList(Collections2.filter(getFieldsByClass(pyClass), SIMPLE_ASSIGNMENTS_ONLY));
+  }
+
+  @Override
+  protected Collection<PyElement> moveMembers(@NotNull final PyClass from,
+                                              @NotNull final Collection<PyMemberInfo<PyTargetExpression>> members,
+                                              @NotNull final PyClass... to) {
+    return moveAssignments(from, Collections2
+      .filter(Collections2.transform(fetchElements(members), ASSIGNMENT_TRANSFORM), NotNullPredicate.INSTANCE),
+                           to);
+  }
+
+  protected abstract Collection<PyElement> moveAssignments(@NotNull PyClass from,
+                                                           @NotNull Collection<PyAssignmentStatement> statements,
+                                                           @NotNull PyClass... to);
+
+  /**
+   * Checks if class has fields. Only child may know how to obtain field
+   *
+   * @param pyClass   class to check
+   * @param fieldName field name
+   * @return true if has one
+   */
+  protected abstract boolean classHasField(@NotNull PyClass pyClass, @NotNull String fieldName);
+
+  /**
+   * Returns all fields by class. Only child may know how to obtain fields
+   *
+   * @param pyClass class to check
+   * @return list of fields in target expression (declaration) form
+   */
+  @NotNull
+  protected abstract List<PyTargetExpression> getFieldsByClass(@NotNull PyClass pyClass);
+
+
+  @NotNull
+  @Override
+  public PyMemberInfo<PyTargetExpression> apply(@NotNull final PyTargetExpression input) {
+    return new PyMemberInfo<PyTargetExpression>(input, myStatic, input.getText(), isOverrides(input), this, false);
+  }
+
+  @Nullable
+  private Boolean isOverrides(@NotNull final PyTargetExpression input) {
+    final PyClass aClass = input.getContainingClass();
+    final String name = input.getName();
+    if (name == null) {
+      return null; //Field with out of name can't override something
+    }
+
+    assert aClass != null : "Target expression declared outside of class:" + input;
+
+    return classHasField(aClass, name) ? true : null;
+  }
+
+
+  private static class SimpleAssignmentsOnly extends NotNullPredicate<PyTargetExpression> {
+    //Support only simplest cases like CLASS_VAR = 42.
+    //Tuples (CLASS_VAR_1, CLASS_VAR_2) = "spam", "eggs" are not supported by now
+    @Override
+    public boolean applyNotNull(@NotNull final PyTargetExpression input) {
+      final PsiElement parent = input.getParent();
+      return (parent != null) && PyAssignmentStatement.class.isAssignableFrom(parent.getClass());
+    }
+  }
+
+
+  //Transforms expressions to its assignment step
+  private static class AssignmentTransform implements Function<PyTargetExpression, PyAssignmentStatement> {
+    @Nullable
+    @Override
+    public PyAssignmentStatement apply(@NotNull final PyTargetExpression input) {
+      return PsiTreeUtil.getParentOfType(input, PyAssignmentStatement.class);
+    }
+  }
+
+  /**
+   * Fetches field declarations
+   */
+  private static class MyPyRecursiveElementVisitor extends PyRecursiveElementVisitor {
+    @NotNull
+    private final MultiMap<PyClass, PyElement> myResult;
+
+    private MyPyRecursiveElementVisitor(@NotNull final MultiMap<PyClass, PyElement> result) {
+      myResult = result;
+    }
+
+    @Override
+    public void visitPyReferenceExpression(final PyReferenceExpression node) {
+      final PsiElement declaration = node.getReference().resolve();
+      if (declaration instanceof PyElement) {
+        final PyClass parent = PsiTreeUtil.getParentOfType(declaration, PyClass.class);
+        if (parent != null) {
+          myResult.putValue(parent, (PyElement)declaration);
+        }
+      }
+    }
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/InstanceFieldsManager.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/InstanceFieldsManager.java
new file mode 100644
index 0000000..a1e19c4
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/InstanceFieldsManager.java
@@ -0,0 +1,118 @@
+package com.jetbrains.python.refactoring.classes.membersManager;
+
+import com.google.common.collect.Collections2;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.jetbrains.NotNullPredicate;
+import com.jetbrains.python.PyNames;
+import com.jetbrains.python.psi.*;
+import com.jetbrains.python.psi.impl.PyFunctionBuilder;
+import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author Ilya.Kazakevich
+ */
+class InstanceFieldsManager extends FieldsManager {
+
+  // PY-12170
+
+  InstanceFieldsManager() {
+    super(false);
+  }
+
+  @Override
+  public boolean hasConflict(@NotNull final PyTargetExpression member, @NotNull final PyClass aClass) {
+    return NamePredicate.hasElementWithSameName(member, aClass.getInstanceAttributes());
+  }
+
+  @Override
+  protected Collection<PyElement> moveAssignments(@NotNull final PyClass from,
+                                                  @NotNull final Collection<PyAssignmentStatement> statements,
+                                                  @NotNull final PyClass... to) {
+    //TODO: Copy/paste with ClassFieldsManager. Move to parent?
+
+    final List<PyElement> result = new ArrayList<PyElement>();
+    for (final PyClass destClass : to) {
+      result.addAll(copyInstanceFields(statements, destClass));
+    }
+    // Delete only declarations made in __init__ to prevent PY-12170
+    final PyFunction fromInitMethod = PyUtil.getInitMethod(from);
+    if (fromInitMethod != null) { // If class has no init method that means all its fields declared in other methods, so nothing to remove
+      deleteElements(Collections2.filter(statements, new InitsOnly(fromInitMethod)));
+      //We can't leave class constructor with empty body
+      PyClassRefactoringUtil.insertPassIfNeeded(fromInitMethod);
+    }
+    return result;
+  }
+
+  /**
+   * Copies class' fields in form of assignments (instance fields) to another class.
+   * Creates init method if there is no any
+   *
+   * @param members assignments to copy
+   * @param to      destination
+   * @return newly created fields
+   */
+  @NotNull
+  private static List<PyAssignmentStatement> copyInstanceFields(@NotNull final Collection<PyAssignmentStatement> members,
+                                                                @NotNull final PyClass to) {
+    //We need __init__ method, and if there is no any -- we need to create it
+    PyFunction toInitMethod = PyUtil.getInitMethod(to);
+    if (toInitMethod == null) {
+      toInitMethod = createInitMethod(to);
+    }
+    final PyStatementList statementList = toInitMethod.getStatementList();
+    return PyClassRefactoringUtil.copyFieldDeclarationToStatement(members, statementList, null);
+  }
+
+  /**
+   * Creates init method and adds it to certain class.
+   *
+   * @param to Class where method should be added
+   * @return newly created method
+   */
+  //TODO: Move to utils?
+  @NotNull
+  private static PyFunction createInitMethod(@NotNull final PyClass to) {
+    final PyFunctionBuilder functionBuilder = new PyFunctionBuilder(PyNames.INIT);
+    functionBuilder.parameter(PyNames.CANONICAL_SELF); //TODO: Take param from codestyle?
+    final PyFunction function = functionBuilder.buildFunction(to.getProject(), LanguageLevel.forElement(to));
+    return PyClassRefactoringUtil.addMethods(to, true, function).get(0);
+  }
+
+  @Override
+  protected boolean classHasField(@NotNull final PyClass pyClass, @NotNull final String fieldName) {
+    return pyClass.findInstanceAttribute(fieldName, true) != null;
+  }
+
+  @NotNull
+  @Override
+  protected List<PyTargetExpression> getFieldsByClass(@NotNull final PyClass pyClass) {
+    return pyClass.getInstanceAttributes();
+  }
+
+  private static class InitsOnly extends NotNullPredicate<PyAssignmentStatement> {
+    @NotNull
+    private final PyFunction myInitMethod;
+
+    private InitsOnly(@NotNull final PyFunction initMethod) {
+      myInitMethod = initMethod;
+    }
+
+    @Override
+    protected boolean applyNotNull(@NotNull final PyAssignmentStatement input) {
+      final PyExpression expression = input.getLeftHandSideExpression();
+      if (expression == null) {
+        return false;
+      }
+
+      final PyFunction functionWhereDeclared = PsiTreeUtil.getParentOfType(PyUtil.resolveToTheTop(expression), PyFunction.class);
+      return myInitMethod.equals(functionWhereDeclared);
+    }
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/MembersConflictDialog.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/MembersConflictDialog.java
new file mode 100644
index 0000000..b288f95
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/MembersConflictDialog.java
@@ -0,0 +1,62 @@
+package com.jetbrains.python.refactoring.classes.membersManager;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.intellij.refactoring.RefactoringBundle;
+import com.intellij.refactoring.ui.ConflictsDialog;
+import com.intellij.refactoring.util.RefactoringUIUtil;
+import com.intellij.util.containers.MultiMap;
+import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.psi.PyClass;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+
+/**
+ * Displays error messages about fact that destination class already contains some member infos
+ * or members under refactoring would not be available at the new place.
+ *
+ * @author Ilya.Kazakevich
+ */
+public class MembersConflictDialog extends ConflictsDialog {
+  /**
+   * @param project               project under refactoring
+   * @param duplicatesConflict    duplicates conflicts : that means destination class has the same member.
+   *                              If member "foo" already exists in class "bar": pass [bar] -] [foo].
+   * @param dependenciesConflicts dependency conflict: list of elements used by member under refactoring and would not be available
+   *                              at new destination. If user wants to move method, that uses field "bar" which would not be available at new class,
+   *                              pass [bar] field
+   */
+  public MembersConflictDialog(
+    @NotNull final Project project,
+    @NotNull final MultiMap<PyClass, PyMemberInfo<?>> duplicatesConflict,
+    @NotNull final Collection<PyMemberInfo<?>> dependenciesConflicts) {
+    super(project, convertDescription(duplicatesConflict, dependenciesConflicts), null, true, false);
+  }
+
+  @NotNull
+  private static MultiMap<PsiElement, String> convertDescription(
+    @NotNull final MultiMap<PyClass, PyMemberInfo<?>> duplicateConflictDescriptions,
+    @NotNull final Collection<PyMemberInfo<?>> dependenciesConflicts) {
+    final MultiMap<PsiElement, String> result = new MultiMap<PsiElement, String>();
+    for (final PyClass aClass : duplicateConflictDescriptions.keySet()) {
+      for (final PyMemberInfo<?> pyMemberInfo : duplicateConflictDescriptions.get(aClass)) {
+        final String message = RefactoringBundle.message("0.already.contains.a.1",
+                                                         RefactoringUIUtil.getDescription(aClass, false),
+                                                         RefactoringUIUtil.getDescription(pyMemberInfo.getMember(), false));
+        result.putValue(aClass, message);
+      }
+    }
+
+    for (final PyMemberInfo<?> memberUnderConflict : dependenciesConflicts) {
+      result.putValue(memberUnderConflict.getMember(), PyBundle.message(
+                        "refactoring.will.not.be.accessible",
+                        RefactoringUIUtil.getDescription(memberUnderConflict.getMember(), false)
+                      )
+      );
+    }
+
+
+    return result;
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/MembersManager.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/MembersManager.java
new file mode 100644
index 0000000..437b808
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/MembersManager.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.refactoring.classes.membersManager;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiNamedElement;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.containers.MultiMap;
+import com.jetbrains.NotNullPredicate;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
+import com.jetbrains.python.refactoring.classes.PyDependenciesComparator;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+/**
+ * Moves members between classes via its plugins (managers).
+ * To move members use {@link #getAllMembersCouldBeMoved(com.jetbrains.python.psi.PyClass)}   and {@link #moveAllMembers(java.util.Collection, com.jetbrains.python.psi.PyClass, com.jetbrains.python.psi.PyClass...)}
+ * To add new manager, extend this class and add it to {@link #MANAGERS}
+ *
+ * @author Ilya.Kazakevich
+ */
+public abstract class MembersManager<T extends PyElement> implements Function<T, PyMemberInfo<T>> {
+  /**
+   * List of managers. Class delegates all logic to them.
+   */
+  private static final Collection<? extends MembersManager<? extends PyElement>> MANAGERS =
+    Arrays.asList(new MethodsManager(), new SuperClassesManager(), new ClassFieldsManager(), new InstanceFieldsManager());
+
+  @NotNull
+  private final Class<T> myExpectedClass;
+
+  protected MembersManager(@NotNull final Class<T> expectedClass) {
+    myExpectedClass = expectedClass;
+  }
+
+  /**
+   * Get all members that could be moved out of certain class
+   *
+   * @param pyClass class to find members
+   * @return list of members could be moved
+   */
+  @NotNull
+  public static List<PyMemberInfo<PyElement>> getAllMembersCouldBeMoved(@NotNull final PyClass pyClass) {
+    final List<PyMemberInfo<PyElement>> result = new ArrayList<PyMemberInfo<PyElement>>();
+
+    for (final MembersManager<? extends PyElement> manager : MANAGERS) {
+      result.addAll(transformSafely(pyClass, manager));
+    }
+    return result;
+  }
+
+
+  /**
+   * Transforms elements, manager says it could move to appropriate {@link com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo}.
+   * Types are checked at runtime.
+   *
+   * @param pyClass class whose members we want to move
+   * @param manager manager that should check class and report list of memebers
+   * @return member infos
+   */
+  //TODO: Move to  TypeSafeMovingStrategy
+  @NotNull
+  @SuppressWarnings({"unchecked", "rawtypes"}) //We check type at runtime
+  private static Collection<PyMemberInfo<PyElement>> transformSafely(@NotNull final PyClass pyClass,
+                                                                     @NotNull final MembersManager<?> manager) {
+    final List<PyElement> membersCouldBeMoved = manager.getMembersCouldBeMoved(pyClass);
+    manager.checkElementTypes((Iterable)membersCouldBeMoved);
+    return (Collection<PyMemberInfo<PyElement>>)Collections2.transform(membersCouldBeMoved, (Function)manager);
+  }
+
+
+  /**
+   * Moves members from one class to another
+   *
+   * @param memberInfos members to move
+   * @param from        source
+   * @param to          destination
+   */
+  public static void moveAllMembers(
+    @NotNull final Collection<PyMemberInfo<PyElement>> memberInfos,
+    @NotNull final PyClass from,
+    @NotNull final PyClass... to
+  ) {
+    List<PyMemberInfo<PyElement>> memberInfosSorted = new ArrayList<PyMemberInfo<PyElement>>(memberInfos);
+    Collections.sort(memberInfosSorted, new Comparator<PyMemberInfo<PyElement>>() {
+      @Override
+      public int compare(PyMemberInfo<PyElement> o1, PyMemberInfo<PyElement> o2) {
+        return PyDependenciesComparator.INSTANCE.compare(o1.getMember(), o2.getMember());
+      }
+    });
+
+    for (PyMemberInfo<PyElement> info : memberInfosSorted) {
+      TypeSafeMovingStrategy.moveCheckingTypesAtRunTime(from, info.getMembersManager(), Collections.singleton(info), to);
+    }
+
+
+    /*//Move at once, sort
+    final Multimap<MembersManager<PyElement>, PyMemberInfo<PyElement>> managerToMember = ArrayListMultimap.create();
+    //Collect map (manager)->(list_of_memebers)
+    for (final PyMemberInfo<PyElement> memberInfo : memberInfos) {
+      managerToMember.put(memberInfo.getMembersManager(), memberInfo);
+    }
+    //Move members via manager
+    for (final MembersManager<PyElement> membersManager : managerToMember.keySet()) {
+      final Collection<PyMemberInfo<PyElement>> members = managerToMember.get(membersManager);
+      TypeSafeMovingStrategy.moveCheckingTypesAtRunTime(from, membersManager, members, to);
+    }*/
+    PyClassRefactoringUtil.insertPassIfNeeded(from);
+  }
+
+
+  /**
+   * Checks that all elements has allowed type for manager
+   *
+   * @param elements elements to check against manager
+   */
+  void checkElementTypes(@NotNull final Iterable<T> elements) {
+    for (final PyElement pyElement : elements) {
+      Preconditions.checkArgument(myExpectedClass.isAssignableFrom(pyElement.getClass()),
+                                  String.format("Manager %s expected %s but got %s", this, myExpectedClass, pyElement));
+    }
+  }
+
+  /**
+   * Finds member by predicate
+   *
+   * @param members   where to find
+   * @param predicate what to find
+   * @return member or null if not found
+   */
+  @Nullable
+  public static PyMemberInfo<PyElement> findMember(@NotNull final Collection<PyMemberInfo<PyElement>> members,
+                                                   @NotNull final Predicate<PyMemberInfo<PyElement>> predicate) {
+    for (final PyMemberInfo<PyElement> pyMemberInfo : members) {
+      if (predicate.apply(pyMemberInfo)) {
+        return pyMemberInfo;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Finds member of class by predicate
+   *
+   * @param predicate what to find
+   * @param pyClass   class to find members
+   * @return member or null if not found
+   */
+  @Nullable
+  public static PyMemberInfo<PyElement> findMember(@NotNull final PyClass pyClass,
+                                                   @NotNull final Predicate<PyMemberInfo<PyElement>> predicate) {
+    return findMember(getAllMembersCouldBeMoved(pyClass), predicate);
+  }
+
+  /**
+   * Finds member in class.
+   * @param pyClass class to find member in
+   * @param pyElement element to find
+   * @return member info with element
+   */
+  @NotNull
+  public static PyMemberInfo<PyElement> findMember(@NotNull final PyClass pyClass, @NotNull final PyElement pyElement) {
+    final PyMemberInfo<PyElement> result = findMember(pyClass, new FindByElement(pyElement));
+    if (result != null) {
+      return result;
+    }
+    throw new IllegalArgumentException(String.format("Element %s not found in class %s or can't be moved", pyElement, pyClass));
+  }
+
+  /**
+   * Get list of elements certain plugin could move out of the class
+   *
+   * @param pyClass class with members
+   * @return list of members
+   */
+  @NotNull
+  protected abstract List<PyElement> getMembersCouldBeMoved(@NotNull PyClass pyClass);
+
+
+  /**
+   * Filters out named elements (ones that subclasses {@link com.intellij.psi.PsiNamedElement}) and {@link com.jetbrains.python.psi.PyElement})
+   * that are null or has null name.
+   * You need it sometimes when code has errors (i.e. bad formatted code with annotation may treat annotation as method with null name.
+   * note: we should probably throw exceptions in such cases and display "refactoring not available" window in handler)
+   *
+   * @param elementsToFilter collection of elements to filter
+   * @param <T>              element type
+   * @return collection of T with out of nulls and elemens whos {@link com.intellij.psi.PsiNamedElement#getName()}  returns null
+   */
+  @NotNull
+  protected static <T extends PsiNamedElement & PyElement> Collection<T> filterNameless(@NotNull final Collection<T> elementsToFilter) {
+    return Collections2.filter(elementsToFilter, new NamelessFilter<T>());
+  }
+
+  /**
+   * Returns list of elements that may require reference storing aid from {@link com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil#rememberNamedReferences(com.intellij.psi.PsiElement, String...)}
+   *
+   * @param elements members chosen by user. In most cases members their selves could be stored, but different managers may support other strategies
+   * @return elements to store
+   * @see #moveAllMembers(java.util.Collection, com.jetbrains.python.psi.PyClass, com.jetbrains.python.psi.PyClass...)
+   */
+  protected Collection<? extends PyElement> getElementsToStoreReferences(@NotNull final Collection<T> elements) {
+    return elements;
+  }
+
+  /**
+   * Moves element from one class to another. Returns members that may require reference restoring aid from
+   * ({@link com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil#restoreNamedReferences(com.intellij.psi.PsiElement)})
+   *
+   * @see #getElementsToStoreReferences(java.util.Collection)
+   */
+  protected abstract Collection<PyElement> moveMembers(
+    @NotNull PyClass from,
+    @NotNull Collection<PyMemberInfo<T>> members,
+    @NotNull PyClass... to);
+
+
+  /**
+   * Creates {@link com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo} from {@link com.jetbrains.python.psi.PyElement}
+   * This process is plugin-specific and should be implemented in each plugin
+   *
+   * @param input element
+   * @return member info
+   */
+  @SuppressWarnings("NullableProblems") //IDEA-120100
+  @NotNull
+  @Override
+  public abstract PyMemberInfo<T> apply(@NotNull T input);
+
+  /**
+   * Deletes all elements
+   *
+   * @param pyElementsToDelete elements to delete
+   */
+  protected static void deleteElements(@NotNull final Collection<? extends PsiElement> pyElementsToDelete) {
+    for (final PsiElement element : pyElementsToDelete) {
+      element.delete();
+    }
+  }
+
+  /**
+   * Fetches elements from member info.
+   *
+   * @param memberInfos member info to fetch elements from
+   * @param <T>         type of element
+   * @return list of elements
+   */
+  @NotNull
+  protected static <T extends PyElement> Collection<T> fetchElements(@NotNull final Collection<PyMemberInfo<T>> memberInfos) {
+    return Collections2.transform(memberInfos, new PyMemberExtractor<T>());
+  }
+
+  /**
+   * Checks if moving certain member to certain class may lead to conflict (actually that means
+   * that class already has this member)
+   *
+   * @param member member to check
+   * @param aClass class where this member wanna be moved
+   * @return true if conflict exists.
+   */
+  public abstract boolean hasConflict(@NotNull T member, @NotNull PyClass aClass);
+
+  /**
+   * Returns all elements this member depends on.
+   *
+   * @param classWhereMemberDeclared class where member declared
+   * @param member                   member itself
+   * @param destinationClass         where this member would be moved (or null if new class is unknown)
+   * @return collection of elements this member depends on excluding those, would be available in destination class
+   */
+  @NotNull
+  public static Collection<? extends PyElement> getAllDependencies(
+    @NotNull final PyClass classWhereMemberDeclared,
+    @NotNull final PyElement member,
+    @Nullable final PyClass destinationClass) {
+    final PyMemberInfo<PyElement> memberInfo = findMember(classWhereMemberDeclared, member);
+
+
+    final Collection<? extends PyElement> elementsToCheckDependency =
+      memberInfo.getMembersManager().getElementsToStoreReferences(Collections.singleton(member));
+
+    final MultiMap<PyClass, PyElement> dependencies = new MultiMap<PyClass, PyElement>();
+
+    final Collection<PyElement> result = new HashSet<PyElement>();
+    for (final MembersManager<? extends PyElement> manager : MANAGERS) {
+      for (final PyElement elementToCheckDependency : elementsToCheckDependency) {
+        dependencies.putAllValues(manager.getDependencies(elementToCheckDependency));
+      }
+    }
+
+    if (destinationClass != null) {
+      final Iterator<PyClass> classesIterator = dependencies.keySet().iterator();
+      while (classesIterator.hasNext()) {
+        final PyClass memberClass = classesIterator.next();
+        if (memberClass.equals(destinationClass) ||
+            ArrayUtil.contains(memberClass, destinationClass.getSuperClasses())) { // IF still would be available
+          classesIterator.remove();
+        }
+      }
+    }
+
+    for (final MembersManager<? extends PyElement> manager : MANAGERS) {
+      result.addAll(manager.getDependencies(dependencies));
+    }
+    result.addAll(dependencies.values());
+    return result;
+  }
+
+  /**
+   * Fetch dependencies this element depends on.
+   * Manager should return them in format "class, where member declared" -- "member itself".
+   * For example: if parameter is function, and this function uses field "foo" declared in class "bar", then manager (responsible for fields)
+   * returns "bar" -] reference to "foo"
+   *
+   * @param member member to check dependencies for
+   * @return dependencies
+   */
+  @NotNull
+  protected abstract MultiMap<PyClass, PyElement> getDependencies(@NotNull PyElement member);
+
+  /**
+   * Get dependencies by members and classes they declared in (obtained from {@link #getDependencies(com.jetbrains.python.psi.PyElement)})
+   * For example manager, responsible for "extends SomeClass" members may return list of classes
+   *
+   * @param usedElements class-to-element dependencies
+   * @return dependencies
+   */
+  @NotNull
+  protected abstract Collection<PyElement> getDependencies(@NotNull MultiMap<PyClass, PyElement> usedElements);
+
+  private static class PyMemberExtractor<T extends PyElement> implements Function<PyMemberInfo<T>, T> {
+    @SuppressWarnings("NullableProblems") //IDEA-120100
+    @Override
+    public T apply(@NotNull final PyMemberInfo<T> input) {
+      return input.getMember();
+    }
+  }
+
+  private static class NamelessFilter<T extends PyElement & PsiNamedElement> extends NotNullPredicate<T> {
+    @Override
+    public boolean applyNotNull(@NotNull final T input) {
+      return input.getName() != null;
+    }
+  }
+
+  private static class FindByElement extends NotNullPredicate<PyMemberInfo<PyElement>> {
+    private final PyElement myPyElement;
+
+    private FindByElement(final PyElement pyElement) {
+      myPyElement = pyElement;
+    }
+
+    @Override
+    public boolean applyNotNull(@NotNull final PyMemberInfo<PyElement> input) {
+      return input.getMember().equals(myPyElement);
+    }
+  }
+}
+
+
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/MethodsManager.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/MethodsManager.java
new file mode 100644
index 0000000..7cba0a2
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/MethodsManager.java
@@ -0,0 +1,284 @@
+package com.jetbrains.python.refactoring.classes.membersManager;
+
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.intellij.openapi.util.Pair;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiReference;
+import com.intellij.util.containers.MultiMap;
+import com.jetbrains.NotNullPredicate;
+import com.jetbrains.python.PyNames;
+import com.jetbrains.python.codeInsight.imports.AddImportHelper;
+import com.jetbrains.python.psi.*;
+import com.jetbrains.python.psi.impl.PyFunctionBuilder;
+import com.jetbrains.python.psi.types.PyType;
+import com.jetbrains.python.psi.types.TypeEvalContext;
+import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+/**
+ * Plugin that moves class methods
+ *
+ * @author Ilya.Kazakevich
+ */
+class MethodsManager extends MembersManager<PyFunction> {
+  private static final String ABC_META_CLASS = "ABCMeta";
+
+  /**
+   * Some decorators should be copied with methods if method is marked abstract. Here is list.
+   */
+  private static final String[] DECORATORS_MAY_BE_COPIED_TO_ABSTRACT =
+    {PyNames.PROPERTY, PyNames.CLASSMETHOD, PyNames.STATICMETHOD};
+
+  public static final String ABC_META_PACKAGE = "abc";
+
+  MethodsManager() {
+    super(PyFunction.class);
+  }
+
+  @Override
+  public boolean hasConflict(@NotNull final PyFunction member, @NotNull final PyClass aClass) {
+    return NamePredicate.hasElementWithSameName(member, Arrays.asList(aClass.getMethods()));
+  }
+
+  @NotNull
+  @Override
+  protected Collection<PyElement> getDependencies(@NotNull final MultiMap<PyClass, PyElement> usedElements) {
+    return Collections.emptyList();
+  }
+
+  @NotNull
+  @Override
+  protected MultiMap<PyClass, PyElement> getDependencies(@NotNull final PyElement member) {
+    final MultiMap<PyClass, PyElement> result = new MultiMap<PyClass, PyElement>();
+    member.accept(new MyPyRecursiveElementVisitor(result));
+
+    return result;
+  }
+
+  @NotNull
+  @Override
+  protected List<PyElement> getMembersCouldBeMoved(@NotNull final PyClass pyClass) {
+    return Lists.<PyElement>newArrayList(filterNameless(Arrays.asList(pyClass.getMethods())));
+  }
+
+  @Override
+  protected Collection<PyElement> moveMembers(@NotNull final PyClass from,
+                                              @NotNull final Collection<PyMemberInfo<PyFunction>> members,
+                                              @NotNull final PyClass... to) {
+    final Collection<PyFunction> methodsToMove = fetchElements(Collections2.filter(members, new AbstractFilter(false)));
+    final Collection<PyFunction> methodsToAbstract = fetchElements(Collections2.filter(members, new AbstractFilter(true)));
+
+    makeMethodsAbstract(methodsToAbstract, to);
+    return moveMethods(from, methodsToMove, to);
+  }
+
+  /**
+   * Creates abstract version of each method in each class (does not touch method itself as opposite to {@link #moveMethods(com.jetbrains.python.psi.PyClass, java.util.Collection, com.jetbrains.python.psi.PyClass...)})
+   *
+   * @param currentFunctions functions to make them abstract
+   * @param to               classes where abstract method should be created
+   */
+  private static void makeMethodsAbstract(final Collection<PyFunction> currentFunctions, final PyClass... to) {
+    final Set<PsiFile> filesToCheckImport = new HashSet<PsiFile>();
+    final Set<PyClass> classesToAddMetaAbc = new HashSet<PyClass>();
+
+    for (final PyFunction function : currentFunctions) {
+      for (final PyClass destClass : to) {
+        final PyFunctionBuilder functionBuilder = PyFunctionBuilder.copySignature(function, DECORATORS_MAY_BE_COPIED_TO_ABSTRACT);
+        functionBuilder.decorate(PyNames.ABSTRACTMETHOD);
+        final LanguageLevel level = LanguageLevel.forElement(destClass);
+        PyClassRefactoringUtil.addMethods(destClass, false, functionBuilder.buildFunction(destClass.getProject(), level));
+        classesToAddMetaAbc.add(destClass);
+      }
+    }
+
+    // Add ABCMeta to new classes if needed
+    for (final PyClass aClass : classesToAddMetaAbc) {
+      if (addMetaAbcIfNeeded(aClass)) {
+        filesToCheckImport.add(aClass.getContainingFile());
+      }
+    }
+
+    // Add imports for ABC if needed
+    for (final PsiFile file : filesToCheckImport) {
+      addImportFromAbc(file, PyNames.ABSTRACTMETHOD);
+      addImportFromAbc(file, ABC_META_CLASS);
+      PyClassRefactoringUtil.optimizeImports(file); //To remove redundant imports
+    }
+  }
+
+  /**
+   * Adds metaclass = ABCMeta for class if has no.
+   *
+   * @param aClass class where it should be added
+   * @return true if added. False if class already has metaclass so we did not touch it.
+   */
+  // TODO: Copy/Paste with PyClass.getMeta..
+  private static boolean addMetaAbcIfNeeded(@NotNull final PyClass aClass) {
+    final PsiFile file = aClass.getContainingFile();
+    final PyType type = aClass.getMetaClassType(TypeEvalContext.userInitiated(file));
+    if (type != null) {
+      return false; //User already has metaclass. He probably knows about metaclasses, so we should not add ABCMeta
+    }
+    final LanguageLevel languageLevel = LanguageLevel.forElement(aClass);
+    if (languageLevel.isPy3K()) { //TODO: Copy/paste, use strategy because we already has the same check in #couldBeAbstract
+      // Add (metaclass= for Py3K
+      PyClassRefactoringUtil
+        .addSuperClassExpressions(aClass.getProject(), aClass, null, Collections.singletonList(Pair.create(PyNames.METACLASS,
+                                                                                                           ABC_META_CLASS)));
+    }
+    else {
+      // Add __metaclass__ for Py2
+      PyClassRefactoringUtil.addClassAttributeIfNotExist(aClass, PyNames.DUNDER_METACLASS, ABC_META_CLASS);
+    }
+    return true;
+  }
+
+  /**
+   * Adds import from ABC module
+   *
+   * @param file         where to add import
+   * @param nameToImport what to import
+   */
+  private static void addImportFromAbc(@NotNull final PsiFile file, @NotNull final String nameToImport) {
+    AddImportHelper.addImportFromStatement(file, ABC_META_PACKAGE, nameToImport, null,
+                                           AddImportHelper.ImportPriority.BUILTIN);
+  }
+
+  /**
+   * Moves methods (as opposite to {@link #makeMethodsAbstract(java.util.Collection, com.jetbrains.python.psi.PyClass...)})
+   *
+   * @param from          source
+   * @param methodsToMove what to move
+   * @param to            where
+   * @return newly added methods
+   */
+  private static List<PyElement> moveMethods(final PyClass from, final Collection<PyFunction> methodsToMove, final PyClass... to) {
+    final List<PyElement> result = new ArrayList<PyElement>();
+    for (final PyClass destClass : to) {
+      //We move copies here because there may be several destinations
+      final List<PyFunction> copies = new ArrayList<PyFunction>(methodsToMove.size());
+      for (final PyFunction element : methodsToMove) {
+        final PyFunction newMethod = (PyFunction)element.copy();
+        copies.add(newMethod);
+      }
+
+      result.addAll(PyClassRefactoringUtil.copyMethods(copies, destClass));
+    }
+    deleteElements(methodsToMove);
+
+    PyClassRefactoringUtil.insertPassIfNeeded(from);
+    return result;
+  }
+
+  @NotNull
+  @Override
+  public PyMemberInfo<PyFunction> apply(@NotNull final PyFunction pyFunction) {
+    final PyUtil.MethodFlags flags = PyUtil.MethodFlags.of(pyFunction);
+    assert flags != null : "No flags return while element is function " + pyFunction;
+    final boolean isStatic = flags.isStaticMethod() || flags.isClassMethod();
+    return new PyMemberInfo<PyFunction>(pyFunction, isStatic, buildDisplayMethodName(pyFunction), isOverrides(pyFunction), this,
+                                        couldBeAbstract(pyFunction));
+  }
+
+  /**
+   * @return if method could be made abstract? (that means "create abstract version if method in parent class")
+   */
+  private static boolean couldBeAbstract(@NotNull final PyFunction function) {
+    if (PyUtil.isInit(function)) {
+      return false; // Who wants to make __init__ abstract?!
+    }
+    final PyUtil.MethodFlags flags = PyUtil.MethodFlags.of(function);
+    assert flags != null : "Function should be called on method!";
+
+    final boolean py3K = LanguageLevel.forElement(function).isPy3K();
+
+    //TODO: use strategy because we already has the same check in #addMetaAbcIfNeeded
+    return flags.isInstanceMethod() || py3K; //Any method could be made abstract in py3
+  }
+
+
+  @Nullable
+  private static Boolean isOverrides(final PyFunction pyFunction) {
+    final PyClass clazz = PyUtil.getContainingClassOrSelf(pyFunction);
+    assert clazz != null : "Refactoring called on function, not method: " + pyFunction;
+    for (final PyClass parentClass : clazz.getSuperClasses()) {
+      final PyFunction parentMethod = parentClass.findMethodByName(pyFunction.getName(), true);
+      if (parentMethod != null) {
+        return true;
+      }
+    }
+    return null;
+  }
+
+  @NotNull
+  private static String buildDisplayMethodName(@NotNull final PyFunction pyFunction) {
+    final StringBuilder builder = new StringBuilder(pyFunction.getName());
+    builder.append('(');
+    final PyParameter[] arguments = pyFunction.getParameterList().getParameters();
+    for (final PyParameter parameter : arguments) {
+      builder.append(parameter.getName());
+      if (arguments.length > 1 && parameter != arguments[arguments.length - 1]) {
+        builder.append(", ");
+      }
+    }
+    builder.append(')');
+    return builder.toString();
+  }
+
+
+  /**
+   * Filters member infos to find if they should be abstracted
+   */
+  private static class AbstractFilter extends NotNullPredicate<PyMemberInfo<PyFunction>> {
+    private final boolean myAllowAbstractOnly;
+
+    /**
+     * @param allowAbstractOnly returns only methods to be abstracted. Returns only methods to be moved otherwise.
+     */
+    private AbstractFilter(final boolean allowAbstractOnly) {
+      myAllowAbstractOnly = allowAbstractOnly;
+    }
+
+    @Override
+    protected boolean applyNotNull(@NotNull final PyMemberInfo<PyFunction> input) {
+      return input.isToAbstract() == myAllowAbstractOnly;
+    }
+  }
+
+  private static class MyPyRecursiveElementVisitor extends PyRecursiveElementVisitor {
+    @NotNull
+    private final MultiMap<PyClass, PyElement> myResult;
+
+    private MyPyRecursiveElementVisitor(@NotNull final MultiMap<PyClass, PyElement> result) {
+      myResult = result;
+    }
+
+    @Override
+    public void visitPyCallExpression(final PyCallExpression node) {
+      // TODO: refactor, messy code
+      final PyExpression callee = node.getCallee();
+      if (callee != null) {
+        final PsiReference calleeRef = callee.getReference();
+        if (calleeRef != null) {
+          final PsiElement calleeDeclaration = calleeRef.resolve();
+          if (calleeDeclaration instanceof PyFunction) {
+            final PyFunction calleeFunction = (PyFunction)calleeDeclaration;
+            final PyClass clazz = calleeFunction.getContainingClass();
+            if (clazz != null) {
+              if (PyUtil.isInit(calleeFunction)) {
+                return; // Init call should not be marked as dependency
+              }
+              myResult.putValue(clazz, calleeFunction);
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/NamePredicate.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/NamePredicate.java
new file mode 100644
index 0000000..7a5ddc6
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/NamePredicate.java
@@ -0,0 +1,45 @@
+package com.jetbrains.python.refactoring.classes.membersManager;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Iterables;
+import com.intellij.navigation.NavigationItem;
+import com.jetbrains.NotNullPredicate;
+import com.jetbrains.python.psi.PyElement;
+import org.jetbrains.annotations.NotNull;
+
+
+/**
+ * Finds elements by name
+ *
+ * @author Ilya.Kazakevich
+ */
+class NamePredicate extends NotNullPredicate<PyElement> {
+  @NotNull
+  private final String myName;
+
+
+  NamePredicate(@NotNull final String name) {
+    myName = name;
+  }
+
+  @Override
+  protected boolean applyNotNull(@NotNull final PyElement input) {
+    return myName.equals(input.getName());
+  }
+
+  /**
+   * Checks if collection has {@link com.jetbrains.python.psi.PyElement} with name equals to name of provided element.
+   * If element has no name -- returns false any way.
+   * @param needle element to take name from
+   * @param stock collection elements to search between
+   * @return true if stock contains element with name equal to needle's name
+   */
+  static boolean hasElementWithSameName(@NotNull final NavigationItem needle, @NotNull final Iterable<? extends PyElement> stock) {
+    final String name = needle.getName();
+    if (name != null) {
+      final Optional<? extends PyElement> optional = Iterables.tryFind(stock, new NamePredicate(name));
+      return optional.isPresent();
+    }
+    return false;
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/PyMemberInfo.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/PyMemberInfo.java
new file mode 100644
index 0000000..819ab90
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/PyMemberInfo.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.refactoring.classes.membersManager;
+
+import com.intellij.refactoring.classMembers.MemberInfoBase;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Dennis.Ushakov
+ */
+public class PyMemberInfo<T extends PyElement> extends MemberInfoBase<T> {
+  @NotNull
+  private final MembersManager<T> myMembersManager;
+  private final boolean myCouldBeAbstract;
+
+  /**
+   * @param couldBeAbstract if element could be marked as abstract (like abstract method)
+   * @param member         element itself
+   * @param isStatic       is it static or not?
+   * @param displayName    element display name
+   * @param overrides      does it overrides something? TRUE if is overriden, FALSE if implemented, null if not implemented or overriden
+   *                       TODO: use primitive instead? "Implemeneted" has nothing to do with python duck-typing
+   * @param membersManager manager that knows how to handle this member
+   */
+  PyMemberInfo(@NotNull final T member,
+               final boolean isStatic,
+               @NotNull final String displayName,
+               @Nullable final Boolean overrides,
+               @NotNull final MembersManager<T> membersManager,
+               final boolean couldBeAbstract) {
+    super(member);
+    this.isStatic = isStatic;
+    this.displayName = displayName;
+    this.overrides = overrides;
+    myMembersManager = membersManager;
+    myCouldBeAbstract = couldBeAbstract;
+  }
+
+  @NotNull
+  MembersManager<T> getMembersManager() {
+    return myMembersManager;
+  }
+
+  public boolean isCouldBeAbstract() {
+    return myCouldBeAbstract;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof PyMemberInfo) {
+      return getMember().equals(((PyMemberInfo<?>)obj).getMember());
+    }
+    return false;
+  }
+
+  @Override
+  public int hashCode() {
+    return getMember().hashCode();
+  }
+
+  /**
+   * Checks if moving this member to some class may create conflict.
+   * @param destinationClass destination class to check
+   * @return true if conflict.
+   */
+  public boolean hasConflict(@NotNull final PyClass destinationClass) {
+    return myMembersManager.hasConflict(myMember, destinationClass);
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/PyMembersRefactoringBaseProcessor.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/PyMembersRefactoringBaseProcessor.java
new file mode 100644
index 0000000..983deb6
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/PyMembersRefactoringBaseProcessor.java
@@ -0,0 +1,86 @@
+package com.jetbrains.python.refactoring.classes.membersManager;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.intellij.refactoring.BaseRefactoringProcessor;
+import com.intellij.usageView.UsageInfo;
+import com.intellij.usageView.UsageViewDescriptor;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Processor for member-based refactorings. It moves members from one place to another using {@link com.jetbrains.python.refactoring.classes.membersManager.MembersManager}.
+ * Inheritors only need to implement {@link com.intellij.usageView.UsageViewDescriptor} methods (while this interface is also implemented by this class)
+ *
+ * @author Ilya.Kazakevich
+ */
+public abstract class PyMembersRefactoringBaseProcessor extends BaseRefactoringProcessor implements UsageViewDescriptor {
+
+  @NotNull
+  protected final Collection<PyMemberInfo<PyElement>> myMembersToMove;
+  @NotNull
+  protected final PyClass myFrom;
+  @NotNull
+  private final PyClass[] myTo;
+
+  /**
+   * @param membersToMove what to move
+   * @param from          source
+   * @param to            where to move
+   */
+  protected PyMembersRefactoringBaseProcessor(
+    @NotNull final Project project,
+    @NotNull final Collection<PyMemberInfo<PyElement>> membersToMove,
+    @NotNull final PyClass from,
+    @NotNull final PyClass... to) {
+    super(project);
+    myFrom = from;
+    myMembersToMove = new ArrayList<PyMemberInfo<PyElement>>(membersToMove);
+    myTo = to.clone();
+  }
+
+  @NotNull
+  @Override
+  protected UsageViewDescriptor createUsageViewDescriptor(final UsageInfo[] usages) {
+    return this;
+  }
+
+  @NotNull
+  @Override
+  public PsiElement[] getElements() {
+    return myTo.clone();
+  }
+
+  /**
+   * @return destinations (so user would be able to choose if she wants to move member to certain place or not)
+   */
+  @NotNull
+  @Override
+  protected final PyUsageInfo[] findUsages() {
+    final List<PyUsageInfo> result = new ArrayList<PyUsageInfo>(myTo.length);
+    for (final PyClass pyDestinationClass : myTo) {
+      result.add(new PyUsageInfo(pyDestinationClass));
+    }
+    return result.toArray(new PyUsageInfo[result.size()]);
+  }
+
+  @Override
+  protected final void performRefactoring(final UsageInfo[] usages) {
+    final Collection<PyClass> destinations = new ArrayList<PyClass>(usages.length);
+    for (final UsageInfo usage : usages) {
+      if (!(usage instanceof PyUsageInfo)) {
+        throw new IllegalArgumentException("Only PyUsageInfo is accepted here");
+      }
+      //We collect destination info to pass it to members manager
+      destinations.add(((PyUsageInfo)usage).getTo());
+    }
+    MembersManager.moveAllMembers(myMembersToMove, myFrom, destinations.toArray(new PyClass[destinations.size()]));
+    PyClassRefactoringUtil.optimizeImports(myFrom.getContainingFile()); // To remove unneeded imports
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/PyUsageInfo.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/PyUsageInfo.java
new file mode 100644
index 0000000..c8e1f1f
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/PyUsageInfo.java
@@ -0,0 +1,26 @@
+package com.jetbrains.python.refactoring.classes.membersManager;
+
+import com.intellij.usageView.UsageInfo;
+import com.jetbrains.python.psi.PyClass;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * TODO: Make it generic to allow to reuse in another projects?
+ * Usage info that displays destination (where should member be moved)
+ *
+ * @author Ilya.Kazakevich
+ */
+class PyUsageInfo extends UsageInfo {
+  @NotNull
+  private final PyClass myTo;
+
+  PyUsageInfo(@NotNull final PyClass to) {
+    super(to, true); //TODO: Make super generic and get rid of field?
+    myTo = to;
+  }
+
+  @NotNull
+  public PyClass getTo() {
+    return myTo;
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/SuperClassesManager.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/SuperClassesManager.java
new file mode 100644
index 0000000..92ffe76
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/SuperClassesManager.java
@@ -0,0 +1,109 @@
+package com.jetbrains.python.refactoring.classes.membersManager;
+
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.intellij.refactoring.RefactoringBundle;
+import com.intellij.util.containers.MultiMap;
+import com.jetbrains.NotNullPredicate;
+import com.jetbrains.python.PyNames;
+import com.jetbrains.python.psi.*;
+import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
+import com.jetbrains.python.refactoring.classes.ui.PyClassCellRenderer;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.*;
+
+/**
+ * Plugin that moves superclasses from one class to another
+ *
+ * @author Ilya.Kazakevich
+ */
+class SuperClassesManager extends MembersManager<PyClass> {
+
+  private static final NoFakeSuperClasses NO_FAKE_SUPER_CLASSES = new NoFakeSuperClasses();
+
+  SuperClassesManager() {
+    super(PyClass.class);
+  }
+
+
+  @NotNull
+  @Override
+  protected Collection<PyElement> getDependencies(@NotNull final MultiMap<PyClass, PyElement> usedElements) {
+    return Lists.<PyElement>newArrayList(usedElements.keySet());
+  }
+
+  @NotNull
+  protected MultiMap<PyClass, PyElement> getDependencies(@NotNull PyElement member) {
+    return MultiMap.emptyInstance();
+  }
+
+  @Override
+  public boolean hasConflict(@NotNull final PyClass member, @NotNull final PyClass aClass) {
+    final List<PyExpression> expressionList = getExpressionsBySuperClass(aClass, Collections.singleton(member));
+    return !expressionList.isEmpty();
+  }
+
+  @NotNull
+  @Override
+  protected List<PyElement> getMembersCouldBeMoved(@NotNull final PyClass pyClass) {
+    return Lists.<PyElement>newArrayList(Collections2.filter(Arrays.asList(pyClass.getSuperClasses()), NO_FAKE_SUPER_CLASSES));
+  }
+
+  @Override
+  protected Collection<PyElement> moveMembers(@NotNull final PyClass from,
+                                              @NotNull final Collection<PyMemberInfo<PyClass>> members,
+                                              @NotNull final PyClass... to) {
+    final Collection<PyClass> elements = fetchElements(members);
+    for (final PyClass destClass : to) {
+      PyClassRefactoringUtil.addSuperclasses(from.getProject(), destClass, elements.toArray(new PyClass[members.size()]));
+    }
+
+    final List<PyExpression> expressionsToDelete = getExpressionsBySuperClass(from, elements);
+    for (final PyExpression expressionToDelete : expressionsToDelete) {
+      expressionToDelete.delete();
+    }
+
+    return Collections.emptyList(); //Hack: we know that "superclass expression" can't have reference
+  }
+
+  /**
+   * Returns superclass expressions that are resolved to one or more classes from collection
+   * @param from class to get superclass expressions from
+   * @param classes classes to check superclasses against
+   * @return collection of expressions that are resolved to one or more class from classes param
+   */
+  @NotNull
+  private static List<PyExpression> getExpressionsBySuperClass(@NotNull final PyClass from, @NotNull final Collection<PyClass> classes) {
+    final List<PyExpression> expressionsToDelete = new ArrayList<PyExpression>(classes.size());
+
+    for (final PyExpression expression : from.getSuperClassExpressions()) {
+      // Remove all superclass expressions that point to class from memberinfo
+      if (!(expression instanceof PyQualifiedExpression)) {
+        continue;
+      }
+      final PyReferenceExpression reference = (PyReferenceExpression)expression;
+      for (final PyClass element : classes) {
+        if (reference.getReference().isReferenceTo(element)) {
+          expressionsToDelete.add(expression);
+        }
+      }
+    }
+    return expressionsToDelete;
+  }
+
+  @NotNull
+  @Override
+  public PyMemberInfo<PyClass> apply(@NotNull final PyClass input) {
+    final String name = RefactoringBundle.message("member.info.extends.0", PyClassCellRenderer.getClassText(input));
+    //TODO: Check for "overrides"
+    return new PyMemberInfo<PyClass>(input, false, name, false, this, false);
+  }
+
+  private static class NoFakeSuperClasses extends NotNullPredicate<PyClass> {
+    @Override
+    protected boolean applyNotNull(@NotNull final PyClass input) {
+      return !PyNames.FAKE_OLD_BASE.equals(input.getName());
+    }
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/TypeSafeMovingStrategy.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/TypeSafeMovingStrategy.java
new file mode 100644
index 0000000..2cf9efd
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/TypeSafeMovingStrategy.java
@@ -0,0 +1,70 @@
+package com.jetbrains.python.refactoring.classes.membersManager;
+
+import com.jetbrains.python.PyNames;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Moves members checking types at runtime.
+ *
+ * @author Ilya.Kazakevich
+ */
+class TypeSafeMovingStrategy<T extends PyElement> {
+  @NotNull private final PyClass myFrom;
+  @NotNull private final MembersManager<T> myManager;
+  @NotNull private final Collection<PyMemberInfo<T>> myMemberInfoCollection;
+  @NotNull private final PyClass[] myTo;
+
+  /**
+   * Move members.
+   * @param from source
+   * @param manager manager to be used
+   * @param memberInfoCollection what to move
+   * @param to where
+   */
+  @SuppressWarnings({"unchecked", "rawtypes"}) //We check types at runtime
+  static void moveCheckingTypesAtRunTime(@NotNull final PyClass from,
+                   @NotNull final MembersManager<?> manager,
+                   @NotNull final Collection<PyMemberInfo<PyElement>> memberInfoCollection,
+                   @NotNull final PyClass... to) {
+    manager.checkElementTypes((Collection)MembersManager.fetchElements(memberInfoCollection));
+    new TypeSafeMovingStrategy(from, manager, memberInfoCollection, to).moveTyped();
+  }
+
+  private TypeSafeMovingStrategy(@NotNull final PyClass from,
+                                 @NotNull final MembersManager<T> manager,
+                                 @NotNull final Collection<PyMemberInfo<T>> memberInfoCollection,
+                                 @NotNull final PyClass[] to) {
+    myFrom = from;
+    myManager = manager;
+    myMemberInfoCollection = new ArrayList<PyMemberInfo<T>>(memberInfoCollection);
+    myTo = to.clone();
+  }
+
+
+  /**
+   * While types are already checked at runtime, this method could move everything in type-safe manner.
+   */
+  private void moveTyped() {
+    final Collection<T> elementsCollection = MembersManager.fetchElements(myMemberInfoCollection);
+    final Collection<? extends PyElement> references = myManager.getElementsToStoreReferences(elementsCollection);
+
+    // Store references to add required imports
+    for (final PyElement element : references) {
+      PyClassRefactoringUtil.rememberNamedReferences(element, PyNames.CANONICAL_SELF); //"self" is not reference we need to move
+    }
+
+    // Move
+    final Collection<PyElement> newElements = myManager.moveMembers(myFrom, myMemberInfoCollection, myTo);
+
+    // Restore references to add appropriate imports
+    for (final PyElement element : newElements) {
+      PyClassRefactoringUtil.restoreNamedReferences(element);
+    }
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/package-info.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/package-info.java
new file mode 100644
index 0000000..9fb380a
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/package-info.java
@@ -0,0 +1,12 @@
+/**
+ * Incapsulates knowledge about class members that could be moved to some other class.
+ * To use (get list of members to move or actually move them) use {@link com.jetbrains.python.refactoring.classes.membersManager.MembersManager#getAllMembersCouldBeMoved(com.jetbrains.python.psi.PyClass)}
+ * and                                                            {@link com.jetbrains.python.refactoring.classes.membersManager.MembersManager#moveAllMembers(java.util.Collection, com.jetbrains.python.psi.PyClass, com.jetbrains.python.psi.PyClass)}
+ *
+ * This class delegates its behaviour to its managers (some kind of plugins). There is one for each member type (one for method, one for field etc).
+ * You need to extend {@link com.jetbrains.python.refactoring.classes.membersManager.MembersManager} to add some. See its javadoc for more info.
+ *
+ *
+ * @author Ilya.Kazakevich
+ */
+package com.jetbrains.python.refactoring.classes.membersManager;
\ No newline at end of file
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/BadDataException.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/BadDataException.java
new file mode 100644
index 0000000..684576d
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/BadDataException.java
@@ -0,0 +1,18 @@
+package com.jetbrains.python.refactoring.classes.membersManager.vp;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * To be thrown when {@link com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedViewSwingImpl} or its children
+ * assumes that data entered by user in {@link com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedView} is invalid.
+ * See {@link MembersBasedPresenterImpl#validateView()} for info why exception user
+ * @author Ilya.Kazakevich
+ */
+public class BadDataException extends Exception {
+  /**
+   * @param message what exactly is wrong with data
+   */
+  public BadDataException(@NotNull final String message) {
+    super(message);
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/MembersBasedPresenter.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/MembersBasedPresenter.java
new file mode 100644
index 0000000..f57f280
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/MembersBasedPresenter.java
@@ -0,0 +1,21 @@
+package com.jetbrains.python.refactoring.classes.membersManager.vp;
+
+import com.jetbrains.python.vp.Presenter;
+
+/**
+ * Presenter for dialogs that display members
+ *
+ * @author Ilya.Kazakevich
+ */
+public interface MembersBasedPresenter extends Presenter {
+  /**
+   * User clicked on "ok" button
+   */
+  void okClicked();
+
+  /**
+   * @return true if dialog button "preview" should be displayed.
+   * Preview uses {@link com.jetbrains.python.refactoring.classes.membersManager.PyMembersRefactoringBaseProcessor}
+   */
+  boolean showPreview();
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/MembersBasedPresenterImpl.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/MembersBasedPresenterImpl.java
new file mode 100644
index 0000000..57f437f
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/MembersBasedPresenterImpl.java
@@ -0,0 +1,124 @@
+package com.jetbrains.python.refactoring.classes.membersManager.vp;
+
+import com.intellij.refactoring.RefactoringBundle;
+import com.intellij.refactoring.classMembers.MemberInfoModel;
+import com.intellij.util.containers.MultiMap;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * All presenters that use members inherits this class.
+ * <strong>Warning</strong>: Do not inherit it directly.
+ * Check {@link com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedPresenterNoPreviewImpl}
+ * or {@link com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedPresenterWithPreviewImpl} instead
+ *
+ * @param <T> view for that presenter
+ * @param <M> Type of model {@link #myModel}
+ * @author Ilya.Kazakevich
+ */
+abstract class MembersBasedPresenterImpl<T extends MembersBasedView<?>,
+  M extends MemberInfoModel<PyElement, PyMemberInfo<PyElement>>> implements MembersBasedPresenter {
+  @NotNull
+  protected final T myView;
+  @NotNull
+  protected final PyClass myClassUnderRefactoring;
+  @NotNull
+  protected final PyMemberInfoStorage myStorage;
+  /**
+   * Member model
+   */
+  @NotNull
+  protected final M myModel;
+
+  /**
+   * @param view                  View for presenter
+   * @param classUnderRefactoring class to be refactored
+   * @param infoStorage           info storage
+   * @param model                 Member model (to be used for dependencies checking)
+   */
+  MembersBasedPresenterImpl(@NotNull final T view,
+                            @NotNull final PyClass classUnderRefactoring,
+                            @NotNull final PyMemberInfoStorage infoStorage,
+                            @NotNull final M model) {
+    myView = view;
+    myClassUnderRefactoring = classUnderRefactoring;
+    myStorage = infoStorage;
+    myModel = model;
+  }
+
+  //TODO: Mark Async ?
+  @Override
+  public void okClicked() {
+    final MultiMap<PyClass, PyMemberInfo<?>> conflicts = getConflicts();
+    final Collection<PyMemberInfo<?>> dependencyConflicts = new ArrayList<PyMemberInfo<?>>();
+    for (final PyMemberInfo<PyElement> memberInfo : myStorage.getClassMemberInfos(myClassUnderRefactoring)) {
+      if (myModel.checkForProblems(memberInfo) != MemberInfoModel.OK) {
+        dependencyConflicts.add(memberInfo);
+      }
+    }
+
+    if ((conflicts.isEmpty() && dependencyConflicts.isEmpty()) || myView.showConflictsDialog(conflicts, dependencyConflicts)) {
+      try {
+        validateView();
+        doRefactor();
+      }
+      catch (final BadDataException e) {
+        myView.showError(e.getMessage()); //Show error message if presenter says view in invalid
+      }
+    }
+  }
+
+  /**
+   * Validates view (used by presenter to check if view is valid).
+   * When overwrite, <strong>always</strong> call "super" <strong>first</strong>!
+   * Throw {@link com.jetbrains.python.refactoring.classes.membersManager.vp.BadDataException} in case of error.
+   * Do nothing, otherwise.
+   * Method is designed to be overwritten and exception is used to simplify this process: children do not need parent's result.
+   * They just call super.
+   *
+   * @throws BadDataException
+   */
+  protected void validateView() throws BadDataException {
+    if (myView.getSelectedMemberInfos().isEmpty()) {
+      throw new BadDataException(RefactoringBundle.message("no.members.selected"));
+    }
+  }
+
+  /**
+   * Does refactoring itself
+   */
+  abstract void doRefactor();
+
+  /**
+   * Checks if one of destination classes already has members that should be moved, so conflict would take place.
+   *
+   * @return map of conflicts (if any)
+   * @see #getDestClassesToCheckConflicts()
+   */
+  @NotNull
+  protected final MultiMap<PyClass, PyMemberInfo<?>> getConflicts() {
+    final MultiMap<PyClass, PyMemberInfo<?>> result = new MultiMap<PyClass, PyMemberInfo<?>>();
+    final Collection<PyMemberInfo<PyElement>> memberInfos = myView.getSelectedMemberInfos();
+    for (final PyClass destinationClass : getDestClassesToCheckConflicts()) {
+      for (final PyMemberInfo<PyElement> pyMemberInfo : memberInfos) {
+        if (pyMemberInfo.hasConflict(destinationClass)) {
+          result.putValue(destinationClass, pyMemberInfo);
+        }
+      }
+    }
+    return result;
+  }
+
+  /**
+   * @return classes where this refactoring will move members. To be used to check for conflicts (if one of target classes already has members)
+   * @see #getConflicts()
+   */
+  @NotNull
+  protected abstract Iterable<? extends PyClass> getDestClassesToCheckConflicts();
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/MembersBasedPresenterNoPreviewImpl.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/MembersBasedPresenterNoPreviewImpl.java
new file mode 100644
index 0000000..dfbe3da
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/MembersBasedPresenterNoPreviewImpl.java
@@ -0,0 +1,69 @@
+package com.jetbrains.python.refactoring.classes.membersManager.vp;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.command.CommandProcessor;
+import com.intellij.refactoring.classMembers.MemberInfoModel;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Presenter that has not preview. Children should implement {@link #refactorNoPreview()}.
+ * To "preview" button would be displayed
+ *
+ * @param <T> view for this presenter
+ * @param <M> Type of model
+ * @author Ilya.Kazakevich
+ */
+
+public abstract class MembersBasedPresenterNoPreviewImpl<T extends MembersBasedView<?>,
+  M extends MemberInfoModel<PyElement, PyMemberInfo<PyElement>>> extends MembersBasedPresenterImpl<T, M> {
+  /**
+   * @param view                  view for this presenter
+   * @param classUnderRefactoring class to refactor
+   * @param infoStorage           info storage
+   * @param model                 Member model (to be used for dependencies checking)
+   */
+  protected MembersBasedPresenterNoPreviewImpl(@NotNull final T view,
+                                               @NotNull final PyClass classUnderRefactoring,
+                                               @NotNull final PyMemberInfoStorage infoStorage,
+                                               @NotNull final M model) {
+    super(view, classUnderRefactoring, infoStorage, model);
+  }
+
+  @Override
+  public boolean showPreview() {
+    return false;
+  }
+
+  @Override
+  void doRefactor() {
+    CommandProcessor.getInstance().executeCommand(myClassUnderRefactoring.getProject(), new Runnable() {
+      @Override
+      public void run() {
+        ApplicationManager.getApplication().runWriteAction(new MyRunnableRefactoring());
+      }
+    }, getCommandName(), null);
+    myView.close();
+  }
+
+  /**
+   * @return Command name for this preview
+   */
+  @NotNull
+  protected abstract String getCommandName();
+
+  /**
+   * Do refactor with out of preview. Implement this method to do refactoring.
+   */
+  protected abstract void refactorNoPreview();
+
+  private class MyRunnableRefactoring implements Runnable {
+    @Override
+    public void run() {
+      refactorNoPreview();
+    }
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/MembersBasedPresenterWithPreviewImpl.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/MembersBasedPresenterWithPreviewImpl.java
new file mode 100644
index 0000000..8950a14
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/MembersBasedPresenterWithPreviewImpl.java
@@ -0,0 +1,52 @@
+package com.jetbrains.python.refactoring.classes.membersManager.vp;
+
+import com.intellij.refactoring.BaseRefactoringProcessor;
+import com.intellij.refactoring.classMembers.MemberInfoModel;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Does refactoring with preview (based on {@link com.intellij.refactoring.BaseRefactoringProcessor}).
+ * Child must implement {@link #createProcessor()} and return appropriate processor.
+ * "Preview" button would be displayed.
+ *
+ * @param <T> view for this presenter
+ * @param <M> Type of model
+ * @author Ilya.Kazakevich
+ */
+public abstract class MembersBasedPresenterWithPreviewImpl<T extends MembersBasedView<?>,
+  M extends MemberInfoModel<PyElement, PyMemberInfo<PyElement>>> extends MembersBasedPresenterImpl<T, M> {
+
+
+  /**
+   * @param view                  view for this presenter
+   * @param classUnderRefactoring class to refactor
+   * @param infoStorage           info storage
+   * @param model                 Member model (to be used for dependencies checking)
+   */
+  protected MembersBasedPresenterWithPreviewImpl(@NotNull final T view,
+                                                 @NotNull final PyClass classUnderRefactoring,
+                                                 @NotNull final PyMemberInfoStorage infoStorage,
+                                                 @NotNull final M  model) {
+    super(view, classUnderRefactoring, infoStorage, model);
+  }
+
+  @Override
+  public boolean showPreview() {
+    return true;
+  }
+
+  @Override
+  protected void doRefactor() {
+    myView.invokeRefactoring(createProcessor());
+  }
+
+  /**
+   * @return processor for refactoring
+   */
+  @NotNull
+  public abstract BaseRefactoringProcessor createProcessor();
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/MembersBasedView.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/MembersBasedView.java
new file mode 100644
index 0000000..01a7eca
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/MembersBasedView.java
@@ -0,0 +1,75 @@
+package com.jetbrains.python.refactoring.classes.membersManager.vp;
+
+import com.intellij.refactoring.BaseRefactoringProcessor;
+import com.intellij.util.containers.MultiMap;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+
+/**
+ * View to display dialog with members.
+ * First, configure it with {@link #configure(MembersViewInitializationInfo)}.
+ * Then, display with {@link #initAndShow()}
+ *
+ * @param <C> initialization info for this view. See {@link com.jetbrains.python.refactoring.classes.membersManager.vp.MembersViewInitializationInfo}
+ *            for more info
+ * @author Ilya.Kazakevich
+ */
+public interface MembersBasedView<C extends MembersViewInitializationInfo> {
+  /**
+   * Display conflict dialogs.
+   *
+   * @param duplicatesConflict    duplicates conflicts : that means destination class has the same member.
+   *                              If member "foo" already exists in class "bar": pass [bar] -] [foo].
+   * @param dependenciesConflicts dependency conflict: list of elements used by member under refactoring and would not be available
+   *                              at new destination. If user wants to move method, that uses field "bar" which would not be available at new class,
+   *                              pass [bar] field
+   * @return true if user's choice is "continue". False if "cancel"
+   */
+  boolean showConflictsDialog(
+    @NotNull MultiMap<PyClass, PyMemberInfo<?>> duplicatesConflict,
+    @NotNull Collection<PyMemberInfo<?>> dependenciesConflicts);
+
+  /**
+   * Displays error message
+   *
+   * @param message message to display
+   */
+  void showError(@NotNull String message);
+
+  /**
+   * Configures view and <strong>must</strong> be called once, before {@link #initAndShow()}
+   * It accepts configuration info class
+   * Children may rewrite method to do additional configuration, but they should <strong>always</strong> call "super" first!
+   *
+   * @param configInfo configuration info
+   */
+  void configure(@NotNull C configInfo);
+
+  /**
+   * @return collection of member infos user selected
+   */
+  @NotNull
+  Collection<PyMemberInfo<PyElement>> getSelectedMemberInfos();
+
+  /**
+   * Runs refactoring based on {@link com.intellij.refactoring.BaseRefactoringProcessor}.
+   * It may display "preview" first.
+   *
+   * @param processor refactoring processor
+   */
+  void invokeRefactoring(@NotNull BaseRefactoringProcessor processor);
+
+  /**
+   * Displays dialog. Be sure to run {@link #configure(MembersViewInitializationInfo)} first
+   */
+  void initAndShow();
+
+  /**
+   * Closes dialog
+   */
+  void close();
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/MembersBasedViewSwingImpl.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/MembersBasedViewSwingImpl.java
new file mode 100644
index 0000000..a8df16e
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/MembersBasedViewSwingImpl.java
@@ -0,0 +1,143 @@
+package com.jetbrains.python.refactoring.classes.membersManager.vp;
+
+import com.google.common.base.Preconditions;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Pair;
+import com.intellij.refactoring.BaseRefactoringProcessor;
+import com.intellij.refactoring.ui.RefactoringDialog;
+import com.intellij.util.containers.MultiMap;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.refactoring.classes.membersManager.MembersConflictDialog;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
+import com.jetbrains.python.refactoring.classes.ui.PyMemberSelectionPanel;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.Collection;
+
+/**
+ * {@link com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedView} implementation on swing.
+ * Consists of {@link #myTopPanel} and {@link #myCenterPanel}. Children must fill them in constructor.
+ * Presenter is stored in {@link #myPresenter}.
+ * Panel with members in {@link #myPyMemberSelectionPanel}
+ *
+ * @param <P> View presenter class
+ * @param <C> View configuration class
+ */
+public abstract class MembersBasedViewSwingImpl<P extends MembersBasedPresenter, C extends MembersViewInitializationInfo>
+  extends RefactoringDialog implements MembersBasedView<C> {
+
+  /**
+   * Panel to be displayed on the top
+   */
+  @NotNull
+  protected final JPanel myTopPanel;
+  /**
+   * Panel to be displayed at the center
+   */
+  @NotNull
+  protected final JComponent myCenterPanel;
+
+  /**
+   * Presenter
+   */
+  @NotNull
+  protected final P myPresenter;
+
+  /**
+   * Panel with members
+   */
+  @NotNull
+  protected final PyMemberSelectionPanel myPyMemberSelectionPanel;
+
+  private boolean myConfigured;
+
+
+  /**
+   *
+   * @param project         project this view runs
+   * @param presenter       view's presenter
+   * @param title           window title
+   * @param supportAbstract supports "abstract" column?
+   */
+  protected MembersBasedViewSwingImpl(@NotNull final Project project, @NotNull final P presenter, @NotNull final String title,
+                                      final boolean supportAbstract) {
+    super(project, true);
+    myTopPanel = new JPanel(new BorderLayout());
+    myCenterPanel = new JPanel(new BorderLayout());
+    myPresenter = presenter;
+    myPyMemberSelectionPanel = new PyMemberSelectionPanel(title, supportAbstract);
+    //TODO: Take this from presenter to prevent inconsistence: now it is possible to create view that supports abstract backed by presenter that does not. And vice versa.
+  }
+
+
+  @Override
+  public boolean showConflictsDialog(@NotNull final MultiMap<PyClass, PyMemberInfo<?>> duplicatesConflict,
+                                     @NotNull final Collection<PyMemberInfo<?>> dependenciesConflicts) {
+    Preconditions.checkArgument(!(duplicatesConflict.isEmpty() && dependenciesConflicts.isEmpty()), "Can't show dialog for empty conflicts");
+    final DialogWrapper conflictsDialog = new MembersConflictDialog(myProject, duplicatesConflict, dependenciesConflicts);
+    conflictsDialog.show();
+    return conflictsDialog.isOK();
+  }
+
+  @Override
+  public void showError(@NotNull final String message) {
+    Messages.showErrorDialog(getContentPane(), message);
+  }
+
+  @Override
+  protected boolean hasPreviewButton() {
+    return myPresenter.showPreview();
+  }
+
+  @Override
+  protected void doAction() {
+    myPresenter.okClicked();
+  }
+
+  @NotNull
+  @Override
+  protected JComponent createNorthPanel() {
+    return myTopPanel;
+  }
+
+  @Override
+  public void close() {
+    close(OK_EXIT_CODE);
+  }
+
+  @Override
+  protected JComponent createCenterPanel() {
+    return myCenterPanel;
+  }
+
+  @NotNull
+  @Override
+  public Collection<PyMemberInfo<PyElement>> getSelectedMemberInfos() {
+    return myPyMemberSelectionPanel.getSelectedMemberInfos();
+  }
+
+  @Override
+  public void invokeRefactoring(@NotNull final BaseRefactoringProcessor processor) {
+    super.invokeRefactoring(processor);
+  }
+
+  @Override
+  public void configure(@NotNull final C configInfo) {
+    Preconditions.checkArgument(!myConfigured, "Already configured");
+    myConfigured = true;
+    myPyMemberSelectionPanel.init(configInfo.getMemberInfoModel(), configInfo.getMemberInfos());
+  }
+
+  @Override
+  public void initAndShow() {
+    Preconditions.checkArgument(myConfigured, "Not configured, run 'configure' first!");
+    init();
+    myPyMemberSelectionPanel.redraw();  // To display errors for checked member
+    show();
+  }
+}
\ No newline at end of file
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/MembersViewInitializationInfo.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/MembersViewInitializationInfo.java
new file mode 100644
index 0000000..7803105
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/MembersViewInitializationInfo.java
@@ -0,0 +1,49 @@
+package com.jetbrains.python.refactoring.classes.membersManager.vp;
+
+import com.intellij.refactoring.classMembers.MemberInfoModel;
+import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * Configuration for {@link com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedView}
+ *
+ * @author Ilya.Kazakevich
+ */
+public class MembersViewInitializationInfo {
+
+  @NotNull
+  private final MemberInfoModel<PyElement, PyMemberInfo<PyElement>> myMemberInfoModel;
+  @NotNull
+  private final Collection<PyMemberInfo<PyElement>> myMemberInfos;
+
+  /**
+   * @param memberInfoModel model to be used in members panel
+   * @param memberInfos     members to displau
+   */
+  public MembersViewInitializationInfo(@NotNull final MemberInfoModel<PyElement, PyMemberInfo<PyElement>> memberInfoModel,
+                                       @NotNull final Collection<PyMemberInfo<PyElement>> memberInfos) {
+    myMemberInfos = new ArrayList<PyMemberInfo<PyElement>>(memberInfos);
+    myMemberInfoModel = memberInfoModel;
+  }
+
+  /**
+   * @return model to be used in members panel
+   */
+  @NotNull
+  public MemberInfoModel<PyElement, PyMemberInfo<PyElement>> getMemberInfoModel() {
+    return myMemberInfoModel;
+  }
+
+  /**
+   * @return members to display
+   */
+  @NotNull
+  public Collection<PyMemberInfo<PyElement>> getMemberInfos() {
+    return Collections.unmodifiableCollection(myMemberInfos);
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/package-info.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/package-info.java
new file mode 100644
index 0000000..f714797
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/vp/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * MVP ({@link com.jetbrains.python.vp}) implementation for refactorings that use members ({@link com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo})
+ * and package {@link com.jetbrains.python.refactoring.classes.membersManager}
+ * @author Ilya.Kazakevich
+ */
+package com.jetbrains.python.refactoring.classes.membersManager.vp;
\ No newline at end of file
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pullUp/PullUpConflictsUtil.java b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PullUpConflictsUtil.java
deleted file mode 100644
index ee659c1..0000000
--- a/python/src/com/jetbrains/python/refactoring/classes/pullUp/PullUpConflictsUtil.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2000-2013 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jetbrains.python.refactoring.classes.pullUp;
-
-import com.intellij.psi.PsiElement;
-import com.intellij.refactoring.RefactoringBundle;
-import com.intellij.refactoring.util.RefactoringUIUtil;
-import com.intellij.util.containers.MultiMap;
-import com.jetbrains.python.psi.PyClass;
-import com.jetbrains.python.psi.PyFunction;
-import com.jetbrains.python.refactoring.classes.PyMemberInfo;
-
-import java.util.Collection;
-
-/**
- * @author Dennis.Ushakov
- */
-public class PullUpConflictsUtil {
-  private PullUpConflictsUtil() {
-  }
-
-  public static MultiMap<PsiElement, String> checkConflicts(final Collection<PyMemberInfo> infos, final PyClass superClass) {
-    final MultiMap<PsiElement, String> conflictsList = new MultiMap<PsiElement, String>();
-    for (PyMemberInfo info : infos) {
-      PsiElement member = info.getMember();
-      boolean isConflict = false;
-      if (member instanceof PyFunction) {
-        final String name = ((PyFunction)member).getName();
-        if (name == null) continue;
-        final PyFunction superClassMethod = superClass.findMethodByName(name, false);
-        isConflict = superClassMethod != null;
-      } else if (member instanceof PyClass) {
-        final PyClass clazz = (PyClass)member;
-        for (PyClass aClass : superClass.getSuperClasses()) {
-          if (aClass == clazz) {
-            conflictsList.putValue(superClass,
-                                   RefactoringUIUtil.getDescription(superClass, false) + " already extends " + RefactoringUIUtil.getDescription(clazz, false));
-          }
-        }
-      }
-
-      if (isConflict) {
-        final String message = RefactoringBundle.message("0.already.contains.a.1",
-                                                         RefactoringUIUtil.getDescription(superClass, false),
-                                                         RefactoringUIUtil.getDescription(member, false));
-        conflictsList.putValue(superClass, message);
-      }
-    }
-
-    return conflictsList;
-  }
-}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyAncestorsUtils.java b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyAncestorsUtils.java
new file mode 100644
index 0000000..05c41c4
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyAncestorsUtils.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.refactoring.classes.pullUp;
+
+
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Sets;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.jetbrains.NotNullPredicate;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyUtil;
+import com.jetbrains.python.psi.types.TypeEvalContext;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author Ilya.Kazakevich
+ */
+class PyAncestorsUtils extends NotNullPredicate<PyClass> {
+  @NotNull
+  private final Set<VirtualFile> mySourceRoots;
+
+  /**
+   * Returns list of class parents that are under user control
+   *
+   * @param pyClass class to  find parents for
+   * @return list of parents
+   */
+  @NotNull
+  static Collection<PyClass> getAncestorsUnderUserControl(@NotNull final PyClass pyClass) {
+    final List<PyClass> allAncestors = pyClass.getAncestorClasses(TypeEvalContext.userInitiated(pyClass.getContainingFile()));
+    return Collections2.filter(allAncestors, new PyAncestorsUtils(PyUtil.getSourceRoots(pyClass)));
+  }
+
+  private PyAncestorsUtils(@NotNull final Collection<VirtualFile> sourceRoots) {
+    mySourceRoots = Sets.newHashSet(sourceRoots);
+  }
+
+  @Override
+  public boolean applyNotNull(@NotNull final PyClass input) {
+    return VfsUtilCore.isUnder(input.getContainingFile().getVirtualFile(), mySourceRoots);
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpDialog.java b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpDialog.java
deleted file mode 100644
index e9631a8..0000000
--- a/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpDialog.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright 2000-2013 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jetbrains.python.refactoring.classes.pullUp;
-
-import com.intellij.openapi.project.Project;
-import com.intellij.psi.PsiElement;
-import com.intellij.refactoring.RefactoringBundle;
-import com.intellij.refactoring.classMembers.AbstractUsesDependencyMemberInfoModel;
-import com.intellij.refactoring.classMembers.DependencyMemberInfoModel;
-import com.intellij.refactoring.ui.ConflictsDialog;
-import com.intellij.util.containers.MultiMap;
-import com.jetbrains.python.psi.PyClass;
-import com.jetbrains.python.psi.PyElement;
-import com.jetbrains.python.refactoring.classes.PyMemberInfo;
-import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
-import com.jetbrains.python.refactoring.classes.ui.PyClassCellRenderer;
-import com.jetbrains.python.refactoring.classes.ui.UpDirectedMembersMovingDialog;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import javax.swing.*;
-import java.awt.*;
-import java.awt.event.ItemEvent;
-import java.awt.event.ItemListener;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.TreeSet;
-
-/**
- * @author Dennis.Ushakov
- */
-public class PyPullUpDialog extends UpDirectedMembersMovingDialog {
-  private JComboBox myClassCombo;
-  private Collection<PyClass> mySuperClasses;
-  private final PyMemberInfoStorage myStorage;
-
-  public PyPullUpDialog(final Project project, final PyClass clazz, final Collection<PyClass> superClasses, final PyMemberInfoStorage storage) {
-    super(project, clazz);
-    myStorage = storage;
-    mySuperClasses = new TreeSet<PyClass>(new Comparator<PyClass>() {
-      public int compare(final PyClass o1, final PyClass o2) {
-        final String name1 = PyClassCellRenderer.getClassText(o1);
-        final String name2 = PyClassCellRenderer.getClassText(o2);
-        return name1 == null ? -1 : name1.compareTo(name2);
-      }
-    });
-    mySuperClasses = superClasses;
-    myMemberInfos = myStorage.getClassMemberInfos(myClass);
-
-    setTitle(PyPullUpHandler.REFACTORING_NAME);
-
-    init();
-  }
-
-  protected DependencyMemberInfoModel<PyElement, PyMemberInfo> createMemberInfoModel() {
-    return new MyMemberInfoModel(myClass);
-  }
-
-  protected String getHelpId() {
-    return "python.reference.pullMembersUp";
-  }
-
-  protected JComponent createNorthPanel() {
-    JPanel panel = new JPanel();
-    panel.setLayout(new GridBagLayout());
-    GridBagConstraints gbConstraints = new GridBagConstraints();
-
-    gbConstraints.insets = new Insets(4, 8, 4, 8);
-    gbConstraints.weighty = 1;
-    gbConstraints.weightx = 1;
-    gbConstraints.gridy = 0;
-    gbConstraints.gridwidth = GridBagConstraints.REMAINDER;
-    gbConstraints.fill = GridBagConstraints.BOTH;
-    gbConstraints.anchor = GridBagConstraints.WEST;
-    final JLabel classComboLabel = new JLabel();
-    panel.add(classComboLabel, gbConstraints);
-
-    myClassCombo = new JComboBox(mySuperClasses.toArray());
-    myClassCombo.setRenderer(new PyClassCellRenderer());
-    final String fqn = PyClassCellRenderer.getClassText(myClass);
-    classComboLabel.setText(RefactoringBundle.message("pull.up.members.to", fqn));
-    classComboLabel.setLabelFor(myClassCombo);
-    myClassCombo.addItemListener(new ItemListener() {
-      public void itemStateChanged(ItemEvent e) {
-        if (e.getStateChange() == ItemEvent.SELECTED) {
-          updateMembersInfo();
-          if (myMemberSelectionPanel != null) {
-            ((MyMemberInfoModel)myMemberInfoModel).setSuperClass(getSuperClass());
-            myMemberSelectionPanel.getTable().setMemberInfos(myMemberInfos);
-            myMemberSelectionPanel.getTable().fireExternalDataChange();
-          }
-        }
-      }
-    });
-    gbConstraints.gridy++;
-    panel.add(myClassCombo, gbConstraints);
-    updateMembersInfo();
-
-    return panel;
-  }
-
-  private void updateMembersInfo() {
-    final PyClass targetClass = (PyClass)myClassCombo.getSelectedItem();
-    myMemberInfos = myStorage.getIntermediateMemberInfosList(targetClass);
-  }
-
-  @Override
-  public boolean checkConflicts() {
-    final Collection<PyMemberInfo> infos = getSelectedMemberInfos();
-    PyClass superClass = getSuperClass();
-    if (!checkWritable(superClass, infos)) return false;
-    MultiMap<PsiElement,String> conflicts = PullUpConflictsUtil.checkConflicts(infos, superClass);
-    if (!conflicts.isEmpty()) {
-      ConflictsDialog conflictsDialog = new ConflictsDialog(myClass.getProject(), conflicts);
-      conflictsDialog.show();
-      final boolean ok = conflictsDialog.isOK();
-      if (!ok && conflictsDialog.isShowConflicts()) close(CANCEL_EXIT_CODE);
-      return ok;
-    }
-    return true;
-  }
-
-  @Nullable
-  public PyClass getSuperClass() {
-    return myClassCombo != null ? (PyClass)myClassCombo.getSelectedItem() : null;
-  }
-
-  protected String getMembersBorderTitle() {
-    return RefactoringBundle.message("members.to.be.pulled.up");
-  }
-
-  private class MyMemberInfoModel extends AbstractUsesDependencyMemberInfoModel<PyElement, PyClass, PyMemberInfo> {
-    public MyMemberInfoModel(PyClass clazz) {
-      super(clazz, getSuperClass(), false);
-    }
-
-
-    public boolean isMemberEnabled(PyMemberInfo member) {
-      PyClass currentSuperClass = getSuperClass();
-      return (currentSuperClass == null ||
-             !myStorage.getDuplicatedMemberInfos(currentSuperClass).contains(member)) &&
-             member.getMember() != currentSuperClass;
-    }
-
-    public boolean isAbstractEnabled(PyMemberInfo member) {
-      return false;
-    }
-
-    public int checkForProblems(@NotNull PyMemberInfo member) {
-      return member.isChecked() ? OK : super.checkForProblems(member);
-    }
-
-    @Override
-    protected int doCheck(@NotNull PyMemberInfo memberInfo, int problem) {
-      if (problem == ERROR && memberInfo.isStatic()) {
-        return WARNING;
-      }
-      return problem;
-    }
-  }
-}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpHandler.java b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpHandler.java
index 0e95700..4fa0eac 100644
--- a/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpHandler.java
+++ b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpHandler.java
@@ -15,24 +15,15 @@
  */
 package com.jetbrains.python.refactoring.classes.pullUp;
 
-import com.intellij.openapi.actionSystem.DataContext;
-import com.intellij.openapi.application.ex.ApplicationManagerEx;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.project.Project;
-import com.intellij.psi.PsiElement;
-import com.intellij.psi.PsiFile;
-import com.intellij.refactoring.RefactoringBundle;
-import com.intellij.refactoring.util.CommonRefactoringUtil;
-import com.intellij.util.PsiNavigateUtil;
 import com.jetbrains.python.PyBundle;
 import com.jetbrains.python.psi.PyClass;
-import com.jetbrains.python.psi.PyUtil;
-import com.jetbrains.python.refactoring.classes.PyClassMembersRefactoringSupport;
 import com.jetbrains.python.refactoring.classes.PyClassRefactoringHandler;
-import com.jetbrains.python.refactoring.classes.PyMemberInfo;
 import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
-
-import java.util.Collection;
+import com.jetbrains.python.vp.Creator;
+import com.jetbrains.python.vp.ViewPresenterUtils;
+import org.jetbrains.annotations.NotNull;
 
 /**
  * @author: Dennis.Ushakov
@@ -41,34 +32,36 @@
   public static final String REFACTORING_NAME = PyBundle.message("refactoring.pull.up.dialog.title");
 
   @Override
-  protected void doRefactor(Project project, PsiElement element1, PsiElement element2, Editor editor, PsiFile file, DataContext dataContext) {
-    CommonRefactoringUtil.checkReadOnlyStatus(project, file);
+  protected void doRefactorImpl(@NotNull final Project project,
+                                @NotNull final PyClass classUnderRefactoring,
+                                @NotNull final PyMemberInfoStorage infoStorage,
+                                @NotNull final Editor editor) {
+    //TODO: Move to vp (presenter) as well
+    final PyPullUpNothingToRefactorMessage nothingToRefactor = new PyPullUpNothingToRefactorMessage(project, editor, classUnderRefactoring);
 
-    final PyClass clazz = PyUtil.getContainingClassOrSelf(element1);
-    if (!inClass(clazz, project, editor, "refactoring.pull.up.error.cannot.perform.refactoring.not.inside.class")) return;
-
-    final PyMemberInfoStorage infoStorage = PyClassMembersRefactoringSupport.getSelectedMemberInfos(clazz, element1, element2);
-    final Collection<PyClass> classes = infoStorage.getClasses();
-    if (classes.size() == 0) {
-      assert clazz != null;
-      CommonRefactoringUtil.showErrorHint(project, editor, PyBundle.message("refactoring.pull.up.error.cannot.perform.refactoring.no.base.classes", clazz.getName()),
-                                          RefactoringBundle.message("pull.members.up.title"),
-                                          "members.pull.up");
+    if (PyAncestorsUtils.getAncestorsUnderUserControl(classUnderRefactoring).isEmpty()) {
+      nothingToRefactor.showNothingToRefactor();
       return;
     }
 
-    if (ApplicationManagerEx.getApplicationEx().isUnitTestMode()) return;
-       
-    final PyPullUpDialog dialog = new PyPullUpDialog(project, clazz, classes, infoStorage);
-    dialog.show();
-    if(dialog.isOK()) {
-      pullUpWithHelper(clazz, dialog.getSelectedMemberInfos(), dialog.getSuperClass());
-    }
+
+    ViewPresenterUtils
+      .linkViewWithPresenterAndLaunch(PyPullUpPresenter.class, PyPullUpView.class, new Creator<PyPullUpView, PyPullUpPresenter>() {
+                                        @NotNull
+                                        @Override
+                                        public PyPullUpPresenter createPresenter(@NotNull final PyPullUpView view) {
+                                          return new PyPullUpPresenterImpl(view, infoStorage, classUnderRefactoring);
+                                        }
+
+                                        @NotNull
+                                        @Override
+                                        public PyPullUpView createView(@NotNull final PyPullUpPresenter presenter) {
+                                          return new PyPullUpViewSwingImpl(project, presenter, classUnderRefactoring, nothingToRefactor);
+                                        }
+                                      }
+      );
   }
 
-  private static void pullUpWithHelper(PyClass clazz, Collection<PyMemberInfo> selectedMemberInfos, PyClass superClass) {
-    PsiNavigateUtil.navigate(PyPullUpHelper.pullUp(clazz, selectedMemberInfos, superClass));
-  }
 
   @Override
   protected String getTitle() {
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpHelper.java b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpHelper.java
deleted file mode 100644
index 0046ba2..0000000
--- a/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpHelper.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2000-2013 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jetbrains.python.refactoring.classes.pullUp;
-
-import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.command.CommandProcessor;
-import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.psi.PsiNamedElement;
-import com.intellij.refactoring.RefactoringBundle;
-import com.jetbrains.python.psi.PyClass;
-import com.jetbrains.python.psi.PyElement;
-import com.jetbrains.python.psi.PyFunction;
-import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
-import com.jetbrains.python.refactoring.classes.PyMemberInfo;
-
-import java.util.*;
-
-/**
- * @author Dennis.Ushakov
- */
-public class PyPullUpHelper {
-  private static final Logger LOG = Logger.getInstance(PyPullUpHelper.class.getName());
-  private PyPullUpHelper() {}
-
-  public static PyElement pullUp(final PyClass clazz, final Collection<PyMemberInfo> selectedMemberInfos, final PyClass superClass) {
-    final Set<String> superClasses = new HashSet<String>();
-    final Set<PsiNamedElement> extractedClasses = new HashSet<PsiNamedElement>();
-    final List<PyFunction> methods = new ArrayList<PyFunction>();
-    for (PyMemberInfo member : selectedMemberInfos) {
-      final PyElement element = member.getMember();
-      if (element instanceof PyFunction) methods.add((PyFunction)element);
-      else if (element instanceof PyClass) {
-        superClasses.add(element.getName());
-        extractedClasses.add((PyClass)element);
-      }
-      else LOG.error("unmatched member class " + element.getClass());
-    }
-    
-    CommandProcessor.getInstance().executeCommand(clazz.getProject(), new Runnable() {
-      public void run() {
-        ApplicationManager.getApplication().runWriteAction(new Runnable() {
-          public void run() {
-            // move methods
-            PyClassRefactoringUtil.moveMethods(methods, superClass);
-
-            // move superclasses declarations
-            PyClassRefactoringUtil.moveSuperclasses(clazz, superClasses, superClass);
-            PyClassRefactoringUtil.insertImport(superClass, extractedClasses);
-            PyClassRefactoringUtil.insertPassIfNeeded(clazz);
-          }
-        });
-      }
-    }, RefactoringBundle.message("pull.members.up.title"), null);
-
-    return superClass;
-  }
-
-}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpInfoModel.java b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpInfoModel.java
new file mode 100644
index 0000000..c86907d
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpInfoModel.java
@@ -0,0 +1,52 @@
+package com.jetbrains.python.refactoring.classes.pullUp;
+
+import com.intellij.refactoring.classMembers.AbstractUsesDependencyMemberInfoModel;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Dependencies model for PyPullUp refactoring
+* @author Ilya.Kazakevich
+*/
+class PyPullUpInfoModel extends AbstractUsesDependencyMemberInfoModel<PyElement, PyClass, PyMemberInfo<PyElement>> {
+  @NotNull
+  private final PyPullUpView myView;
+
+
+  PyPullUpInfoModel(@NotNull final PyClass classUnderRefactoring,
+                    @NotNull final PyPullUpView view) {
+    super(classUnderRefactoring, null, false);
+    myView = view;
+  }
+
+  @Override
+  public boolean isAbstractEnabled(final PyMemberInfo<PyElement> member) {
+    return member.isCouldBeAbstract() && isMemberEnabled(member); // TODO: copy paste with other models, get rid of
+  }
+
+  @Override
+  public int checkForProblems(@NotNull final PyMemberInfo<PyElement> member) {
+    return member.isChecked() ? OK : super.checkForProblems(member);
+  }
+
+
+  @Override
+  protected int doCheck(@NotNull final PyMemberInfo<PyElement> memberInfo, final int problem) {
+    return problem;
+  }
+
+  @Override
+  public boolean isMemberEnabled(final PyMemberInfo<PyElement> member) {
+    final PyClass currentSuperClass = myView.getSelectedParent();
+    if (member.getMember() instanceof PyClass) {
+      //TODO: Delegate to Memebers Managers
+      final PyClass memberClass = (PyClass)member.getMember();
+      if (memberClass.isSubclass(currentSuperClass) || currentSuperClass.isSubclass(memberClass)) {
+        return false; //Class is already parent of superclass
+      }
+    }
+    return true;
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpNothingToRefactorMessage.java b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpNothingToRefactorMessage.java
new file mode 100644
index 0000000..cbb599c
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpNothingToRefactorMessage.java
@@ -0,0 +1,48 @@
+package com.jetbrains.python.refactoring.classes.pullUp;
+
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.refactoring.RefactoringBundle;
+import com.intellij.refactoring.util.CommonRefactoringUtil;
+import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.psi.PyClass;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Displays "nothing to refactor" message
+ * @author Ilya.Kazakevich
+ */
+class PyPullUpNothingToRefactorMessage {
+
+  @NotNull
+  private final Project myProject;
+  @NotNull
+  private final Editor myEditor;
+  @NotNull
+  private final PyClass myClassUnderRefactoring;
+
+  /**
+   *
+   * @param project project to be used
+   * @param editor editor to be used
+   * @param classUnderRefactoring class user refactors
+   */
+  PyPullUpNothingToRefactorMessage(@NotNull final Project project,
+                                   @NotNull final Editor editor,
+                                   @NotNull final PyClass classUnderRefactoring) {
+    myProject = project;
+    myEditor = editor;
+    myClassUnderRefactoring = classUnderRefactoring;
+  }
+
+  /**
+   * Display message
+   */
+  void showNothingToRefactor() {
+    CommonRefactoringUtil.showErrorHint(myProject, myEditor, PyBundle
+                                          .message("refactoring.pull.up.error.cannot.perform.refactoring.no.base.classes",
+                                                   myClassUnderRefactoring.getName()), RefactoringBundle.message("pull.members.up.title"),
+                                        "members.pull.up"
+    );
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpPresenter.java b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpPresenter.java
new file mode 100644
index 0000000..1e97f77
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpPresenter.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.refactoring.classes.pullUp;
+
+
+import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedPresenter;
+
+/**
+ * Presenter for pull-up refactoring
+ *
+ * TODO: Interface left empty. Remove?
+ *
+ * @author Ilya.Kazakevich
+ */
+public interface PyPullUpPresenter extends MembersBasedPresenter {
+
+  /**
+   * To be called when user changed parent
+   */
+  void parentChanged();
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpPresenterImpl.java b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpPresenterImpl.java
new file mode 100644
index 0000000..b78433a
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpPresenterImpl.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.refactoring.classes.pullUp;
+
+import com.google.common.base.Preconditions;
+import com.intellij.openapi.project.Project;
+import com.intellij.refactoring.BaseRefactoringProcessor;
+import com.intellij.refactoring.util.CommonRefactoringUtil;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.psi.PyUtil;
+import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
+import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedPresenterWithPreviewImpl;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * Pull-up presenter implementation
+ *
+ * @author Ilya.Kazakevich
+ */
+class PyPullUpPresenterImpl extends MembersBasedPresenterWithPreviewImpl<PyPullUpView, PyPullUpInfoModel> implements PyPullUpPresenter {
+  @NotNull
+  private final Collection<PyClass> myParents;
+
+  /**
+   * @param view        view
+   * @param infoStorage member storage
+   * @param clazz       class to refactor
+   */
+  PyPullUpPresenterImpl(@NotNull final PyPullUpView view, @NotNull final PyMemberInfoStorage infoStorage, @NotNull final PyClass clazz) {
+    super(view, clazz, infoStorage, new PyPullUpInfoModel(clazz, view));
+    myParents = PyAncestorsUtils.getAncestorsUnderUserControl(clazz);
+    Preconditions.checkArgument(!myParents.isEmpty(), "No parents found");
+  }
+
+
+  @Override
+  public void launch() {
+    myView.configure(
+      new PyPullUpViewInitializationInfo(myModel, myStorage.getClassMemberInfos(myClassUnderRefactoring), myParents));
+
+    // If there is no enabled member then only error should be displayed
+
+    boolean atLeastOneEnabled = false;
+    for (final PyMemberInfo<PyElement> info : myStorage.getClassMemberInfos(myClassUnderRefactoring)) {
+      if (myModel.isMemberEnabled(info)) {
+        atLeastOneEnabled = true;
+      }
+    }
+
+
+    if (atLeastOneEnabled) {
+      myView.initAndShow();
+    } else {
+      myView.showNothingToRefactor();
+    }
+  }
+
+  @Override
+  public void okClicked() {
+    if (!isWritable()) {
+      return; //TODO: Strange behaviour
+    }
+    super.okClicked();
+  }
+
+  @NotNull
+  @Override
+  public BaseRefactoringProcessor createProcessor() {
+    return new PyPullUpProcessor(myClassUnderRefactoring, myView.getSelectedParent(), myView.getSelectedMemberInfos());
+  }
+
+  private boolean isWritable() {
+    final Collection<PyMemberInfo<PyElement>> infos = myView.getSelectedMemberInfos();
+    if (infos.isEmpty()) {
+      return true;
+    }
+    final PyElement element = infos.iterator().next().getMember();
+    final Project project = element.getProject();
+    if (!CommonRefactoringUtil.checkReadOnlyStatus(project, myView.getSelectedParent())) return false;
+    final PyClass container = PyUtil.getContainingClassOrSelf(element);
+    if (container == null || !CommonRefactoringUtil.checkReadOnlyStatus(project, container)) return false;
+    for (final PyMemberInfo<PyElement> info : infos) {
+      final PyElement member = info.getMember();
+      if (!CommonRefactoringUtil.checkReadOnlyStatus(project, member)) return false;
+    }
+    return true;
+  }
+
+  @Override
+  public void parentChanged() {
+    myModel.setSuperClass(myView.getSelectedParent());
+  }
+
+  @NotNull
+  @Override
+  protected Iterable<? extends PyClass> getDestClassesToCheckConflicts() {
+    return Collections.singletonList(myView.getSelectedParent());
+  }
+}
+
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpProcessor.java b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpProcessor.java
new file mode 100644
index 0000000..1eaa742
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpProcessor.java
@@ -0,0 +1,45 @@
+package com.jetbrains.python.refactoring.classes.pullUp;
+
+import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMembersRefactoringBaseProcessor;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+
+/**
+ *
+ *
+ * @author Ilya.Kazakevich
+ */
+class PyPullUpProcessor extends PyMembersRefactoringBaseProcessor {
+
+  PyPullUpProcessor(@NotNull final PyClass from, @NotNull final PyClass to, @NotNull final Collection<PyMemberInfo<PyElement>> membersToMove) {
+    super(from.getProject(), membersToMove, from, to);
+  }
+
+
+  @Override
+  protected String getCommandName() {
+    return PyPullUpHandler.REFACTORING_NAME;
+  }
+
+  @Override
+  public String getProcessedElementsHeader() {
+    return PyBundle.message("refactoring.pull.up.dialog.move.members.to.class");
+  }
+
+  @Override
+  public String getCodeReferencesText(final int usagesCount, final int filesCount) {
+    return PyBundle.message("refactoring.pull.up.dialog.members.to.be.moved");
+  }
+
+  @Nullable
+  @Override
+  public String getCommentReferencesText(final int usagesCount, final int filesCount) {
+    return getCodeReferencesText(usagesCount, filesCount);
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpView.java b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpView.java
new file mode 100644
index 0000000..0a770ea
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpView.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.refactoring.classes.pullUp;
+
+
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedView;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Ilya.Kazakevich
+ *         View for pull-up refactoring
+ */
+public interface PyPullUpView extends MembersBasedView<PyPullUpViewInitializationInfo> {
+
+  /**
+   * @return Parent that user selected
+   */
+  @NotNull
+  PyClass getSelectedParent();
+
+  /**
+   * Display "nothing to refactor" message.
+   */
+  void showNothingToRefactor();
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpViewInitializationInfo.java b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpViewInitializationInfo.java
new file mode 100644
index 0000000..c438e84
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpViewInitializationInfo.java
@@ -0,0 +1,38 @@
+package com.jetbrains.python.refactoring.classes.pullUp;
+
+import com.intellij.refactoring.classMembers.MemberInfoModel;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
+import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersViewInitializationInfo;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Configuration for pull-up view
+ *
+ * @author Ilya.Kazakevich
+ */
+class PyPullUpViewInitializationInfo extends MembersViewInitializationInfo {
+  @NotNull
+  private final Collection<PyClass> myParents;
+
+  /**
+   * @param parents list of possible parents to display.
+   */
+  PyPullUpViewInitializationInfo(@NotNull final MemberInfoModel<PyElement, PyMemberInfo<PyElement>> memberInfoModel,
+                                 @NotNull final List<PyMemberInfo<PyElement>> memberInfos,
+                                 @NotNull final Collection<PyClass> parents) {
+    super(memberInfoModel, memberInfos);
+    myParents = new ArrayList<PyClass>(parents);
+  }
+
+  @NotNull
+  public Collection<PyClass> getParents() {
+    return Collections.unmodifiableCollection(myParents);
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpViewSwingImpl.java b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpViewSwingImpl.java
new file mode 100644
index 0000000..3f0fa2a
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpViewSwingImpl.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.refactoring.classes.pullUp;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.ComboBox;
+import com.intellij.refactoring.RefactoringBundle;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedViewSwingImpl;
+import com.jetbrains.python.refactoring.classes.ui.PyClassCellRenderer;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+
+/**
+ * @author Ilya.Kazakevich
+ *         Pull up view implementation
+ */
+class PyPullUpViewSwingImpl extends MembersBasedViewSwingImpl<PyPullUpPresenter, PyPullUpViewInitializationInfo> implements PyPullUpView,
+                                                                                                                            ItemListener {
+  @NotNull
+  private final ComboBox myParentsCombo;
+  @NotNull
+  private final DefaultComboBoxModel myParentsComboBoxModel;
+  @NotNull
+  private final PyPullUpNothingToRefactorMessage myNothingToRefactorMessage;
+
+  /**
+   * @param project   project where refactoring takes place
+   * @param presenter presenter for this view
+   * @param clazz     class to refactor
+   * @param nothingToRefactorMessage class that displays message "nothing to refactor" when presenter calls {@link #showNothingToRefactor()}
+   */
+  PyPullUpViewSwingImpl(
+    @NotNull final Project project,
+    @NotNull final PyPullUpPresenter presenter,
+    @NotNull final PyClass clazz,
+    @NotNull final PyPullUpNothingToRefactorMessage nothingToRefactorMessage) {
+    super(project, presenter, RefactoringBundle.message("members.to.be.pulled.up"), true);
+    setTitle(PyPullUpHandler.REFACTORING_NAME);
+    myNothingToRefactorMessage = nothingToRefactorMessage;
+
+    myParentsComboBoxModel = new DefaultComboBoxModel();
+
+    myParentsCombo = new ComboBox(myParentsComboBoxModel);
+    myParentsCombo.setRenderer(new PyClassCellRenderer());
+
+    final JLabel mainLabel = new JLabel();
+    mainLabel.setText(RefactoringBundle.message("pull.up.members.to", PyClassCellRenderer.getClassText(clazz)));
+    mainLabel.setLabelFor(myParentsCombo);
+
+
+    myTopPanel.setLayout(new GridBagLayout());
+    final GridBagConstraints gbConstraints = new GridBagConstraints();
+
+    gbConstraints.insets = new Insets(4, 8, 4, 8);
+    gbConstraints.weighty = 1;
+    gbConstraints.weightx = 1;
+    gbConstraints.gridy = 0;
+    gbConstraints.gridwidth = GridBagConstraints.REMAINDER;
+    gbConstraints.fill = GridBagConstraints.BOTH;
+    gbConstraints.anchor = GridBagConstraints.WEST;
+    myTopPanel.add(mainLabel, gbConstraints);
+    myTopPanel.add(mainLabel, gbConstraints);
+    gbConstraints.gridy++;
+    myTopPanel.add(myParentsCombo, gbConstraints);
+
+    gbConstraints.gridy++;
+    myCenterPanel.add(myPyMemberSelectionPanel, BorderLayout.CENTER);
+  }
+
+  @Override
+  @NotNull
+  protected String getHelpId() {
+    return "python.reference.pullMembersUp";
+  }
+
+
+  @NotNull
+  @Override
+  public PyClass getSelectedParent() {
+    return (PyClass)myParentsComboBoxModel.getSelectedItem();
+  }
+
+  @Override
+  public void showNothingToRefactor() {
+    myNothingToRefactorMessage.showNothingToRefactor();
+  }
+
+  @Override
+  public void configure(@NotNull final PyPullUpViewInitializationInfo configInfo) {
+    super.configure(configInfo);
+    for (final PyClass parent : configInfo.getParents()) {
+      myParentsComboBoxModel.addElement(parent);
+    }
+    myPresenter.parentChanged();
+    myParentsCombo.addItemListener(this);
+  }
+
+  @Override
+  public void itemStateChanged(final ItemEvent e) {
+    if (e.getStateChange() == ItemEvent.SELECTED) {
+      myPyMemberSelectionPanel.redraw();
+      myPresenter.parentChanged();
+    }
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pullUp/package-info.java b/python/src/com/jetbrains/python/refactoring/classes/pullUp/package-info.java
new file mode 100644
index 0000000..4623236
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/pullUp/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * "Pull members up" refactoring
+ * @author Ilya.Kazakevich
+ */
+package com.jetbrains.python.refactoring.classes.pullUp;
\ No newline at end of file
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownConflicts.java b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownConflicts.java
deleted file mode 100644
index c159b2f..0000000
--- a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownConflicts.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2000-2013 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jetbrains.python.refactoring.classes.pushDown;
-
-import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.util.Comparing;
-import com.intellij.psi.PsiElement;
-import com.intellij.refactoring.RefactoringBundle;
-import com.intellij.refactoring.util.RefactoringUIUtil;
-import com.intellij.util.Function;
-import com.intellij.util.containers.ContainerUtil;
-import com.intellij.util.containers.MultiMap;
-import com.jetbrains.python.psi.*;
-import com.jetbrains.python.psi.resolve.PyResolveContext;
-import com.jetbrains.python.refactoring.classes.PyMemberInfo;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * @author Dennis.Ushakov
- */
-public class PyPushDownConflicts {
-  private static final Logger LOG = Logger.getInstance(PyPushDownProcessor.class.getName());
-
-  private final PyClass myClass;
-  private final Collection<PyMemberInfo> myMembers;
-  private final MultiMap<PsiElement, String> myConflicts;
-
-  public PyPushDownConflicts(final PyClass clazz, final Collection<PyMemberInfo> members) {
-    myClass = clazz;
-    myMembers = members;
-    myConflicts = new MultiMap<PsiElement, String>();
-  }
-
-  public MultiMap<PsiElement, String> getConflicts() {
-    return myConflicts;
-  }
-
-  public void checkTargetClassConflicts(PyClass clazz) {
-    checkPlacementConflicts(clazz);    
-  }
-
-  private void checkPlacementConflicts(PyClass clazz) {
-    for (PyMemberInfo member : myMembers) {
-      final PyElement element = member.getMember();
-      if (element instanceof PyFunction) {
-        for (PyFunction function : clazz.getMethods()) {
-          if (Comparing.strEqual(function.getName(), element.getName())) {
-             final String message = RefactoringBundle.message("0.is.already.overridden.in.1",
-                                                 RefactoringUIUtil.getDescription(element, false),
-                                                 RefactoringUIUtil.getDescription(clazz, false));
-             myConflicts.putValue(element, message);
-          }
-        }
-      } else if (element instanceof PyClass) {
-      } else LOG.error("unmatched member class " + clazz.getClass());
-    }
-  }
-
-  public void checkSourceClassConflicts() {
-    final List<PyElement> elements = ContainerUtil.map(myMembers, new Function<PyMemberInfo, PyElement>() {
-      public PyElement fun(PyMemberInfo pyMemberInfo) {
-        return pyMemberInfo.getMember();
-      }
-    });
-    for (PyFunction pyFunction : myClass.getMethods()) {
-      final UsedMembersCollector collector = new UsedMembersCollector(elements);
-      pyFunction.accept(collector);
-      final List<PyElement> conflicts = collector.getCollection();
-
-      for (PyElement conflict : conflicts) {
-        final String message = RefactoringBundle.message("0.uses.1.which.is.pushed.down",
-                                                 RefactoringUIUtil.getDescription(pyFunction, false),
-                                                 RefactoringUIUtil.getDescription(conflict, false));
-        myConflicts.putValue(pyFunction, message);
-      }
-    }
-  }
-
-  private static class UsedMembersCollector extends PyRecursiveElementVisitor {
-    private final List<PyElement> myCollection = new ArrayList<PyElement>();
-    private final Collection<PyElement> myMovedMembers;
-
-    private UsedMembersCollector(Collection<PyElement> movedMembers) {
-      myMovedMembers = movedMembers;
-    }
-
-    @Override
-    public void visitPyCallExpression(PyCallExpression node) {
-      final Callable function = node.resolveCalleeFunction(PyResolveContext.noImplicits());
-      if (function != null && myMovedMembers.contains(function)) {
-        myCollection.add(function);
-      }
-    }
-
-    public List<PyElement> getCollection() {
-      return myCollection;
-    }
-  }
-}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownDialog.java b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownDialog.java
deleted file mode 100644
index 2235c57..0000000
--- a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownDialog.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright 2000-2013 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jetbrains.python.refactoring.classes.pushDown;
-
-import com.intellij.openapi.project.Project;
-import com.intellij.refactoring.RefactoringBundle;
-import com.intellij.refactoring.classMembers.MemberInfoChange;
-import com.intellij.refactoring.classMembers.MemberInfoModel;
-import com.intellij.refactoring.classMembers.UsedByDependencyMemberInfoModel;
-import com.intellij.refactoring.ui.RefactoringDialog;
-import com.jetbrains.python.psi.PyClass;
-import com.jetbrains.python.psi.PyElement;
-import com.jetbrains.python.refactoring.classes.PyMemberInfo;
-import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
-import com.jetbrains.python.refactoring.classes.ui.PyMemberSelectionPanel;
-
-import javax.swing.*;
-import java.awt.*;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * @author Dennis.Ushakov
- */
-public class PyPushDownDialog extends RefactoringDialog {
-  private final List<PyMemberInfo> myMemberInfos;
-  private final PyClass myClass;
-  private MemberInfoModel<PyElement, PyMemberInfo> myMemberInfoModel;
-
-  public PyPushDownDialog(Project project, PyClass aClass, PyMemberInfoStorage memberInfos) {
-    super(project, true);
-    myMemberInfos = memberInfos.getClassMemberInfos(aClass);
-    myClass = aClass;
-
-    setTitle(PyPushDownHandler.REFACTORING_NAME);
-
-    init();
-  }
-
-  protected String getHelpId() {
-    return "python.reference.pushMembersDown";
-  }
-
-  @Override
-  protected void doAction() {
-    if(!isOKActionEnabled()) return;
-
-    final PyPushDownProcessor processor = new PyPushDownProcessor(getProject(), myClass, getSelectedMemberInfos());
-    invokeRefactoring(processor);
-  }
-
-  protected JComponent createNorthPanel() {
-    GridBagConstraints gbConstraints = new GridBagConstraints();
-
-    JPanel panel = new JPanel(new GridBagLayout());
-
-    gbConstraints.insets = new Insets(4, 0, 4, 8);
-    gbConstraints.weighty = 1;
-    gbConstraints.weightx = 1;
-    gbConstraints.gridy = 0;
-    gbConstraints.gridwidth = GridBagConstraints.REMAINDER;
-    gbConstraints.fill = GridBagConstraints.BOTH;
-    gbConstraints.anchor = GridBagConstraints.WEST;
-    final String fqn = myClass.getName();
-    panel.add(new JLabel(RefactoringBundle.message("push.members.from.0.down.label", fqn)), gbConstraints);
-    return panel;
-  }
-
-  protected JComponent createCenterPanel() {
-    JPanel panel = new JPanel(new BorderLayout());
-    final PyMemberSelectionPanel memberSelectionPanel = new PyMemberSelectionPanel(
-      RefactoringBundle.message("members.to.be.pushed.down.panel.title"),
-      myMemberInfos, null);
-    panel.add(memberSelectionPanel, BorderLayout.CENTER);
-
-    myMemberInfoModel = new UsedByDependencyMemberInfoModel<PyElement, PyClass, PyMemberInfo>(myClass);
-    myMemberInfoModel.memberInfoChanged(new MemberInfoChange<PyElement, PyMemberInfo>(myMemberInfos));
-    memberSelectionPanel.getTable().setMemberInfoModel(myMemberInfoModel);
-    memberSelectionPanel.getTable().addMemberInfoChangeListener(myMemberInfoModel);
-
-    return panel;
-  }
-
-  public Collection<PyMemberInfo> getSelectedMemberInfos() {
-    ArrayList<PyMemberInfo> list = new ArrayList<PyMemberInfo>(myMemberInfos.size());
-    for (PyMemberInfo info : myMemberInfos) {
-      if (info.isChecked() && myMemberInfoModel.isMemberEnabled(info)) {
-        list.add(info);
-      }
-    }
-    return list;
-  }
-}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownHandler.java b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownHandler.java
index 689d508..08fda71 100644
--- a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownHandler.java
+++ b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownHandler.java
@@ -15,21 +15,18 @@
  */
 package com.jetbrains.python.refactoring.classes.pushDown;
 
-import com.intellij.openapi.actionSystem.DataContext;
-import com.intellij.openapi.application.ex.ApplicationManagerEx;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.project.Project;
-import com.intellij.psi.PsiElement;
-import com.intellij.psi.PsiFile;
 import com.intellij.refactoring.RefactoringBundle;
 import com.intellij.refactoring.util.CommonRefactoringUtil;
 import com.intellij.util.Query;
 import com.jetbrains.python.psi.PyClass;
-import com.jetbrains.python.psi.PyUtil;
 import com.jetbrains.python.psi.search.PyClassInheritorsSearch;
-import com.jetbrains.python.refactoring.classes.PyClassMembersRefactoringSupport;
 import com.jetbrains.python.refactoring.classes.PyClassRefactoringHandler;
 import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
+import com.jetbrains.python.vp.Creator;
+import com.jetbrains.python.vp.ViewPresenterUtils;
+import org.jetbrains.annotations.NotNull;
 
 /**
  * @author Dennis.Ushakov
@@ -38,26 +35,33 @@
   public static final String REFACTORING_NAME = RefactoringBundle.message("push.members.down.title");
 
   @Override
-  protected void doRefactor(Project project, PsiElement element1, PsiElement element2, Editor editor, PsiFile file, DataContext dataContext) {
-    CommonRefactoringUtil.checkReadOnlyStatus(project, file);
+  protected void doRefactorImpl(@NotNull final Project project,
+                                @NotNull final PyClass classUnderRefactoring,
+                                @NotNull final PyMemberInfoStorage infoStorage,
+                                @NotNull Editor editor) {
 
-    final PyClass clazz = PyUtil.getContainingClassOrSelf(element1);
-    if (!inClass(clazz, project, editor, "refactoring.pull.up.error.cannot.perform.refactoring.not.inside.class")) return;
-
-    final Query<PyClass> query = PyClassInheritorsSearch.search(clazz, false);
+    //TODO: Move to presenter?
+    final Query<PyClass> query = PyClassInheritorsSearch.search(classUnderRefactoring, false);
     if (query.findFirst() == null) {
-      assert clazz != null;
-      final String message = RefactoringBundle.message("class.0.does.not.have.inheritors", clazz.getName());
+      final String message = RefactoringBundle.message("class.0.does.not.have.inheritors", classUnderRefactoring.getName());
       CommonRefactoringUtil.showErrorHint(project, editor, message, getTitle(), getHelpId());
       return;
     }
 
-    final PyMemberInfoStorage infoStorage = PyClassMembersRefactoringSupport.getSelectedMemberInfos(clazz, element1, element2);
+    ViewPresenterUtils
+      .linkViewWithPresenterAndLaunch(PyPushDownPresenter.class, PyPushDownView.class, new Creator<PyPushDownView, PyPushDownPresenter>() {
+        @NotNull
+        @Override
+        public PyPushDownPresenter createPresenter(@NotNull PyPushDownView view) {
+          return new PyPushDownPresenterImpl(project, view, classUnderRefactoring, infoStorage);
+        }
 
-    if (ApplicationManagerEx.getApplicationEx().isUnitTestMode()) return;
-
-    final PyPushDownDialog dialog = new PyPushDownDialog(project, clazz, infoStorage);
-    dialog.show();
+        @NotNull
+        @Override
+        public PyPushDownView createView(@NotNull PyPushDownPresenter presenter) {
+          return new PyPushDownViewSwingImpl(classUnderRefactoring, project, presenter);
+        }
+      });
   }
 
   @Override
@@ -69,5 +73,4 @@
   protected String getHelpId() {
     return "members.push.down";
   }
-
 }
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownPresenter.java b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownPresenter.java
new file mode 100644
index 0000000..357aa66
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownPresenter.java
@@ -0,0 +1,9 @@
+package com.jetbrains.python.refactoring.classes.pushDown;
+
+import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedPresenter;
+
+/**
+ * @author Ilya.Kazakevich
+ */
+public interface PyPushDownPresenter extends MembersBasedPresenter {
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownPresenterImpl.java b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownPresenterImpl.java
new file mode 100644
index 0000000..ce9306f
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownPresenterImpl.java
@@ -0,0 +1,49 @@
+package com.jetbrains.python.refactoring.classes.pushDown;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.refactoring.BaseRefactoringProcessor;
+import com.intellij.refactoring.classMembers.MemberInfoModel;
+import com.intellij.refactoring.classMembers.UsedByDependencyMemberInfoModel;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.psi.PyUtil;
+import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
+import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedPresenterWithPreviewImpl;
+import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersViewInitializationInfo;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Ilya.Kazakevich
+ */
+public class PyPushDownPresenterImpl extends MembersBasedPresenterWithPreviewImpl<PyPushDownView, MemberInfoModel<PyElement, PyMemberInfo<PyElement>>> implements PyPushDownPresenter {
+  @NotNull
+  private final Project myProject;
+
+  public PyPushDownPresenterImpl(@NotNull final Project project,
+                                 @NotNull final PyPushDownView view,
+                                 @NotNull final PyClass classUnderRefactoring,
+                                 @NotNull final PyMemberInfoStorage infoStorage) {
+    super(view, classUnderRefactoring, infoStorage,  new UsedByDependencyMemberInfoModel<PyElement, PyClass, PyMemberInfo<PyElement>>(classUnderRefactoring));
+    myProject = project;
+  }
+
+  @NotNull
+  @Override
+  public BaseRefactoringProcessor createProcessor() {
+    return new PyPushDownProcessor(myProject, myView.getSelectedMemberInfos(), myClassUnderRefactoring);
+  }
+
+  @NotNull
+  @Override
+  protected Iterable<? extends PyClass> getDestClassesToCheckConflicts() {
+    return PyPushDownProcessor.getInheritors(myClassUnderRefactoring);
+  }
+
+  @Override
+  public void launch() {
+    myView
+      .configure(new MembersViewInitializationInfo(myModel, PyUtil.filterOutObject(myStorage.getClassMemberInfos(myClassUnderRefactoring))));
+    myView.initAndShow();
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownProcessor.java b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownProcessor.java
index e7026aa..283dd72 100644
--- a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownProcessor.java
+++ b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownProcessor.java
@@ -15,120 +15,60 @@
  */
 package com.jetbrains.python.refactoring.classes.pushDown;
 
-import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.ui.Messages;
-import com.intellij.openapi.util.Ref;
-import com.intellij.psi.PsiElement;
-import com.intellij.psi.PsiNamedElement;
-import com.intellij.refactoring.BaseRefactoringProcessor;
 import com.intellij.refactoring.RefactoringBundle;
-import com.intellij.usageView.UsageInfo;
-import com.intellij.usageView.UsageViewDescriptor;
-import com.intellij.util.Function;
-import com.intellij.util.containers.ContainerUtil;
+import com.intellij.usageView.UsageViewBundle;
 import com.jetbrains.python.psi.PyClass;
 import com.jetbrains.python.psi.PyElement;
-import com.jetbrains.python.psi.PyExpression;
-import com.jetbrains.python.psi.PyFunction;
-import com.jetbrains.python.psi.impl.PyPsiUtils;
 import com.jetbrains.python.psi.search.PyClassInheritorsSearch;
-import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
-import com.jetbrains.python.refactoring.classes.PyMemberInfo;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMembersRefactoringBaseProcessor;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
-import java.util.*;
+import java.util.Collection;
 
 /**
  * @author Dennis.Ushakov
  */
-public class PyPushDownProcessor extends BaseRefactoringProcessor {
-  private static final Logger LOG = Logger.getInstance(PyPushDownProcessor.class.getName());
+public class PyPushDownProcessor extends PyMembersRefactoringBaseProcessor {
 
-  private PyClass myClass;
-  private final Collection<PyMemberInfo> mySelectedMemberInfos;
+  private static final String HEADER = RefactoringBundle.message("push.down.members.elements.header");
 
-  public PyPushDownProcessor(Project project, PyClass clazz, Collection<PyMemberInfo> selectedMemberInfos) {
-    super(project);
-    myClass = clazz;
-    mySelectedMemberInfos = selectedMemberInfos;
+  public PyPushDownProcessor(
+    @NotNull final Project project,
+    @NotNull final Collection<PyMemberInfo<PyElement>> membersToMove,
+    @NotNull final PyClass from) {
+    super(project, membersToMove, from, getChildren(from));
   }
 
   @NotNull
-  @Override
-  protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) {
-    return new PyPushDownUsageViewDescriptor(myClass);
+  private static PyClass[] getChildren(@NotNull final PyClass from) {
+    final Collection<PyClass> all = getInheritors(from);
+    return all.toArray(new PyClass[all.size()]);
   }
 
+  /**
+   * @param from class to check for inheritors
+   * @return inheritors of class
+   */
   @NotNull
-  @Override
-  protected UsageInfo[] findUsages() {
-    final Collection<PyClass> subClasses = PyClassInheritorsSearch.search(myClass, false).findAll();
-    final UsageInfo[] result = new UsageInfo[subClasses.size()];
-    ContainerUtil.map2Array(subClasses, result, new Function<PyClass, UsageInfo>() {
-      public UsageInfo fun(PyClass pyClass) {
-        return new UsageInfo(pyClass);
-      }
-    });
-    return result;
+  static Collection<PyClass> getInheritors(@NotNull final PyClass from) {
+    return PyClassInheritorsSearch.search(from, false).findAll();
   }
 
-  @Override
-  protected void refreshElements(PsiElement[] elements) {
-    if (elements.length == 1 && elements[0] instanceof PyClass) {
-      myClass = (PyClass)elements[0];
-    }
+
+  public String getProcessedElementsHeader() {
+    return HEADER;
   }
 
-  @Override
-  protected void performRefactoring(UsageInfo[] usages) {
-    final Set<String> superClasses = new HashSet<String>();
-    final Set<PsiNamedElement> extractedClasses = new HashSet<PsiNamedElement>();
-    final List<PyFunction> methods = new ArrayList<PyFunction>();
-    for (PyMemberInfo member : mySelectedMemberInfos) {
-      final PyElement element = member.getMember();
-      if (element instanceof PyFunction) methods.add((PyFunction)element);
-      else if (element instanceof PyClass) {
-        superClasses.add(element.getName());
-        extractedClasses.add((PyClass)element);
-      }
-      else LOG.error("unmatched member class " + element.getClass());
-    }
-    final PyElement[] elements = methods.toArray(new PyElement[methods.size()]);
-
-    final List<PyExpression> superClassesElements = PyClassRefactoringUtil.removeAndGetSuperClasses(myClass, superClasses);
-
-    for (UsageInfo usage : usages) {
-      final PyClass targetClass = (PyClass)usage.getElement();
-      PyClassRefactoringUtil.addMethods(targetClass, elements, false);
-      PyClassRefactoringUtil.addSuperclasses(myClass.getProject(), targetClass, superClassesElements, superClasses);
-      PyClassRefactoringUtil.insertImport(targetClass, extractedClasses);
-    }
-
-    if (methods.size() != 0) {
-      PyPsiUtils.removeElements(elements);
-      PyClassRefactoringUtil.insertPassIfNeeded(myClass);
-    }
+  public String getCodeReferencesText(int usagesCount, int filesCount) {
+    return RefactoringBundle.message("classes.to.push.down.members.to", UsageViewBundle.getReferencesString(usagesCount, filesCount));
   }
 
-  @Override
-  protected boolean preprocessUsages(Ref<UsageInfo[]> ref) {
-    final UsageInfo[] usages = ref.get();
-    final PyPushDownConflicts conflicts = new PyPushDownConflicts(myClass, mySelectedMemberInfos);
-    conflicts.checkSourceClassConflicts();
-
-    if (usages.length == 0) {
-      final String message = RefactoringBundle.message("class.0.does.not.have.inheritors", myClass.getName()) + "\nPushing members down will result in them being deleted";
-      final int answer = Messages.showYesNoDialog(message, PyPushDownHandler.REFACTORING_NAME, Messages.getWarningIcon());
-      if (answer != 0) {
-        return false;
-      }
-    }
-
-    for (UsageInfo usage : usages) {
-       conflicts.checkTargetClassConflicts((PyClass)usage.getElement());
-    }
-    return showConflicts(conflicts.getConflicts(), usages);
+  @Nullable
+  public String getCommentReferencesText(int usagesCount, int filesCount) {
+    return null;
   }
 
   @Override
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownUsageViewDescriptor.java b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownUsageViewDescriptor.java
deleted file mode 100644
index 97f858a..0000000
--- a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownUsageViewDescriptor.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2000-2013 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jetbrains.python.refactoring.classes.pushDown;
-
-import com.intellij.psi.PsiElement;
-import com.intellij.refactoring.RefactoringBundle;
-import com.intellij.usageView.UsageViewBundle;
-import com.intellij.usageView.UsageViewDescriptor;
-import com.jetbrains.python.psi.PyClass;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-/**
- * @author Dennis.Ushakov
- */
-public class PyPushDownUsageViewDescriptor implements UsageViewDescriptor {
-  private final PyClass myClass;
-  private static final String HEADER = RefactoringBundle.message("push.down.members.elements.header");
-
-  public PyPushDownUsageViewDescriptor(final PyClass clazz) {
-    myClass = clazz;
-  }
-
-  @NotNull
-  public PsiElement[] getElements() {
-    return new PsiElement[] {myClass};
-  }
-
-  public String getProcessedElementsHeader() {
-    return HEADER;
-  }
-
-  public String getCodeReferencesText(int usagesCount, int filesCount) {
-    return RefactoringBundle.message("classes.to.push.down.members.to", UsageViewBundle.getReferencesString(usagesCount, filesCount));
-  }
-
-  @Nullable
-  public String getCommentReferencesText(int usagesCount, int filesCount) {
-    return null;
-  }
-}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownView.java b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownView.java
new file mode 100644
index 0000000..5b1848e
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownView.java
@@ -0,0 +1,10 @@
+package com.jetbrains.python.refactoring.classes.pushDown;
+
+import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedView;
+import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersViewInitializationInfo;
+
+/**
+ * @author Ilya.Kazakevich
+ */
+public interface PyPushDownView extends MembersBasedView<MembersViewInitializationInfo> {
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownViewSwingImpl.java b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownViewSwingImpl.java
new file mode 100644
index 0000000..9fd0cb5
--- /dev/null
+++ b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownViewSwingImpl.java
@@ -0,0 +1,26 @@
+package com.jetbrains.python.refactoring.classes.pushDown;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.refactoring.RefactoringBundle;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedViewSwingImpl;
+import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersViewInitializationInfo;
+import org.jetbrains.annotations.NotNull;
+
+import java.awt.*;
+
+/**
+ * @author Ilya.Kazakevich
+ */
+public class PyPushDownViewSwingImpl extends MembersBasedViewSwingImpl<PyPushDownPresenter, MembersViewInitializationInfo>
+  implements PyPushDownView {
+  public PyPushDownViewSwingImpl(
+    @NotNull final PyClass classUnderRefactoring,
+    @NotNull final Project project,
+    @NotNull final PyPushDownPresenter presenter) {
+    super(project, presenter, RefactoringBundle.message("push.members.from.0.down.label", classUnderRefactoring.getName()), false);
+
+    myCenterPanel.add(myPyMemberSelectionPanel, BorderLayout.CENTER);
+    setTitle(PyPushDownHandler.REFACTORING_NAME);
+  }
+}
diff --git a/python/src/com/jetbrains/python/refactoring/classes/ui/PyMemberSelectionPanel.java b/python/src/com/jetbrains/python/refactoring/classes/ui/PyMemberSelectionPanel.java
index cc4357e..0b6ef07 100644
--- a/python/src/com/jetbrains/python/refactoring/classes/ui/PyMemberSelectionPanel.java
+++ b/python/src/com/jetbrains/python/refactoring/classes/ui/PyMemberSelectionPanel.java
@@ -15,39 +15,95 @@
  */
 package com.jetbrains.python.refactoring.classes.ui;
 
+import com.google.common.base.Preconditions;
 import com.intellij.refactoring.classMembers.MemberInfoModel;
 import com.intellij.ui.IdeBorderFactory;
 import com.intellij.ui.ScrollPaneFactory;
 import com.jetbrains.python.psi.PyElement;
-import com.jetbrains.python.refactoring.classes.PyMemberInfo;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
+import org.jetbrains.annotations.NotNull;
 
 import javax.swing.*;
 import javax.swing.border.Border;
 import java.awt.*;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 
 /**
+ * Panel that handles table with list of class members with selection checkboxes.
+ *
  * @author Dennis.Ushakov
  */
 public class PyMemberSelectionPanel extends JPanel {
+  private static final List<PyMemberInfo<PyElement>> EMPTY_MEMBER_INFO = Collections.emptyList();
   private final PyMemberSelectionTable myTable;
+  private boolean myInitialized;
 
-  public PyMemberSelectionPanel(String title, List<PyMemberInfo> memberInfo, final MemberInfoModel<PyElement, PyMemberInfo> model) {
-    super();
+
+  /**
+   * Creates empty panel to be filled later by {@link #init(com.intellij.refactoring.classMembers.MemberInfoModel, java.util.Collection)}
+   *
+   * @param title
+   */
+  public PyMemberSelectionPanel(@NotNull String title, boolean supportAbstract) {
+    this(title, EMPTY_MEMBER_INFO, null, supportAbstract);
+  }
+
+  /**
+   * Creates panel and fills its table (see {@link #init(com.intellij.refactoring.classMembers.MemberInfoModel, java.util.Collection)} ) with members info
+   *
+   * @param title      Title for panel
+   * @param memberInfo list of members
+   * @param model      model
+   */
+  public PyMemberSelectionPanel(
+    String title,
+    List<PyMemberInfo<PyElement>> memberInfo,
+    final MemberInfoModel<PyElement, PyMemberInfo<PyElement>> model,
+    final boolean supportAbstract) {
     Border titledBorder = IdeBorderFactory.createTitledBorder(title, false);
     Border emptyBorder = BorderFactory.createEmptyBorder(0, 5, 5, 5);
     Border border = BorderFactory.createCompoundBorder(titledBorder, emptyBorder);
     setBorder(border);
     setLayout(new BorderLayout());
 
-    myTable = new PyMemberSelectionTable(memberInfo, model);
+    myTable = new PyMemberSelectionTable(memberInfo, model, supportAbstract);
     JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myTable);
 
 
     add(scrollPane, BorderLayout.CENTER);
   }
 
-  public PyMemberSelectionTable getTable() {
-    return myTable;
+
+  /**
+   * Inits panel.
+   *
+   * @param memberInfoModel model to display memebers in table
+   * @param members         members to display
+   */
+  public void init(@NotNull final MemberInfoModel<PyElement, PyMemberInfo<PyElement>> memberInfoModel,
+                   @NotNull final Collection<PyMemberInfo<PyElement>> members) {
+    Preconditions.checkState(!myInitialized, "Already myInitialized");
+    myTable.setMemberInfos(members);
+    myTable.setMemberInfoModel(memberInfoModel);
+    myTable.addMemberInfoChangeListener(memberInfoModel);
+    myInitialized = true;
+  }
+
+  /**
+   * @return list of members, selected by user
+   */
+  @NotNull
+  public Collection<PyMemberInfo<PyElement>> getSelectedMemberInfos() {
+    Preconditions.checkState(myInitialized, "Call #init first");
+    return myTable.getSelectedMemberInfos();
+  }
+
+  /**
+   * Redraws table. Call it when some new data is available.
+   */
+  public void redraw() {
+    myTable.redraw();
   }
 }
diff --git a/python/src/com/jetbrains/python/refactoring/classes/ui/PyMemberSelectionTable.java b/python/src/com/jetbrains/python/refactoring/classes/ui/PyMemberSelectionTable.java
index 11d3e42..229b2b5 100644
--- a/python/src/com/jetbrains/python/refactoring/classes/ui/PyMemberSelectionTable.java
+++ b/python/src/com/jetbrains/python/refactoring/classes/ui/PyMemberSelectionTable.java
@@ -17,12 +17,15 @@
 
 import com.intellij.icons.AllIcons;
 import com.intellij.psi.PsiElement;
+import com.intellij.refactoring.RefactoringBundle;
 import com.intellij.refactoring.classMembers.MemberInfoModel;
 import com.intellij.refactoring.ui.AbstractMemberSelectionTable;
 import com.intellij.ui.RowIcon;
 import com.jetbrains.python.psi.PyElement;
 import com.jetbrains.python.psi.PyFunction;
-import com.jetbrains.python.refactoring.classes.PyMemberInfo;
+import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
 import java.util.List;
@@ -30,23 +33,37 @@
 /**
  * @author Dennis.Ushakov
  */
-public class PyMemberSelectionTable extends AbstractMemberSelectionTable<PyElement, PyMemberInfo> {
-  public PyMemberSelectionTable(final List<PyMemberInfo> memberInfos,
-                                  final MemberInfoModel<PyElement, PyMemberInfo> model) {
-    super(memberInfos, model, null);
+public class PyMemberSelectionTable extends AbstractMemberSelectionTable<PyElement, PyMemberInfo<PyElement>> {
+
+  private static final String ABSTRACT_TITLE = RefactoringBundle.message("make.abstract");
+  private final boolean mySupportAbstract;
+
+  public PyMemberSelectionTable(
+    @NotNull final List<PyMemberInfo<PyElement>> memberInfos,
+    @Nullable final MemberInfoModel<PyElement, PyMemberInfo<PyElement>> model,
+    final boolean supportAbstract) {
+    super(memberInfos, model, (supportAbstract ? ABSTRACT_TITLE : null));
+    mySupportAbstract = supportAbstract;
   }
 
-  protected Object getAbstractColumnValue(PyMemberInfo memberInfo) {
-    return null;
+  @Nullable
+  @Override
+  protected Object getAbstractColumnValue(final PyMemberInfo<PyElement> memberInfo) {
+    //TODO: Too many logic, move to presenters
+    return (mySupportAbstract && memberInfo.isChecked() && myMemberInfoModel.isAbstractEnabled(memberInfo)) ? memberInfo.isToAbstract() : null;
   }
 
-  protected boolean isAbstractColumnEditable(int rowIndex) {
-    return false;
+  @Override
+  protected boolean isAbstractColumnEditable(final int rowIndex) {
+    return mySupportAbstract && myMemberInfoModel.isAbstractEnabled(myMemberInfos.get(rowIndex));
   }
 
-  protected void setVisibilityIcon(PyMemberInfo memberInfo, RowIcon icon) {}
+  @Override
+  protected void setVisibilityIcon(PyMemberInfo<PyElement> memberInfo, RowIcon icon) {
+  }
 
-  protected Icon getOverrideIcon(PyMemberInfo memberInfo) {
+  @Override
+  protected Icon getOverrideIcon(PyMemberInfo<PyElement> memberInfo) {
     final PsiElement member = memberInfo.getMember();
     Icon overrideIcon = EMPTY_OVERRIDE_ICON;
     if (member instanceof PyFunction && memberInfo.getOverrides() != null && memberInfo.getOverrides()) {
diff --git a/python/src/com/jetbrains/python/refactoring/classes/ui/UpDirectedMembersMovingDialog.java b/python/src/com/jetbrains/python/refactoring/classes/ui/UpDirectedMembersMovingDialog.java
deleted file mode 100644
index 7ac313d..0000000
--- a/python/src/com/jetbrains/python/refactoring/classes/ui/UpDirectedMembersMovingDialog.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright 2000-2013 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jetbrains.python.refactoring.classes.ui;
-
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.ui.DialogWrapper;
-import com.intellij.refactoring.classMembers.DependencyMemberInfoModel;
-import com.intellij.refactoring.classMembers.MemberInfoChange;
-import com.intellij.refactoring.util.CommonRefactoringUtil;
-import com.jetbrains.python.psi.PyClass;
-import com.jetbrains.python.psi.PyElement;
-import com.jetbrains.python.psi.PyUtil;
-import com.jetbrains.python.refactoring.classes.PyMemberInfo;
-
-import javax.swing.*;
-import java.awt.*;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * @author Dennis.Ushakov
- */
-public abstract class UpDirectedMembersMovingDialog extends DialogWrapper {
-  protected DependencyMemberInfoModel<PyElement, PyMemberInfo> myMemberInfoModel;
-  protected PyMemberSelectionPanel myMemberSelectionPanel;
-  protected PyClass myClass;
-  protected List<PyMemberInfo> myMemberInfos;
-
-  public UpDirectedMembersMovingDialog(Project project, final PyClass clazz) {
-    super(project, true);
-    myClass = clazz;
-  }
-
-  protected JComponent createCenterPanel() {
-    JPanel panel = new JPanel(new BorderLayout());
-    myMemberSelectionPanel = new PyMemberSelectionPanel(getMembersBorderTitle(), myMemberInfos, null);
-    myMemberInfoModel = createMemberInfoModel();
-    myMemberInfoModel.memberInfoChanged(new MemberInfoChange<PyElement, PyMemberInfo>(myMemberInfos));
-    myMemberSelectionPanel.getTable().setMemberInfoModel(myMemberInfoModel);
-    myMemberSelectionPanel.getTable().addMemberInfoChangeListener(myMemberInfoModel);
-    panel.add(myMemberSelectionPanel, BorderLayout.CENTER);
-
-    return panel;
-  }
-
-  protected abstract String getMembersBorderTitle();
-
-  protected abstract DependencyMemberInfoModel<PyElement, PyMemberInfo> createMemberInfoModel();
-
-  protected void doOKAction() {
-    if(!checkConflicts()) return;
-    close(OK_EXIT_CODE);
-  }
-
-  public boolean isOKActionEnabled() {
-    return getSelectedMemberInfos().size() > 0 && super.isOKActionEnabled();
-  }
-
-  public abstract boolean checkConflicts();
-
-  protected static boolean checkWritable(final PyClass superClass, final Collection<PyMemberInfo> infos) {
-    if (infos.size() ==0) {
-      return true;
-    }
-    final PyElement element = infos.iterator().next().getMember();
-    final Project project = element.getProject();
-    if (!CommonRefactoringUtil.checkReadOnlyStatus(project, superClass)) return false;
-    final PyClass container = PyUtil.getContainingClassOrSelf(element);
-    if (container == null || !CommonRefactoringUtil.checkReadOnlyStatus(project, container)) return false;
-    for (PyMemberInfo info : infos) {
-      final PyElement member = info.getMember();
-      if (!CommonRefactoringUtil.checkReadOnlyStatus(project, member)) return false;
-    }
-    return true;
-  }
-
-  public Collection<PyMemberInfo> getSelectedMemberInfos() {
-    ArrayList<PyMemberInfo> list = new ArrayList<PyMemberInfo>(myMemberInfos.size());
-    for (PyMemberInfo info : myMemberInfos) {
-      if (info.isChecked() && myMemberInfoModel.isMemberEnabled(info)) {
-        list.add(info);
-      }
-    }
-    return list;
-  }
-}
diff --git a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java
index 8a1bd6b..d16712d 100644
--- a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java
+++ b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java
@@ -40,7 +40,10 @@
 import com.intellij.util.Function;
 import com.intellij.util.IncorrectOperationException;
 import com.intellij.util.containers.hash.HashMap;
-import com.jetbrains.python.*;
+import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.PyNames;
+import com.jetbrains.python.PythonFileType;
+import com.jetbrains.python.PythonLanguage;
 import com.jetbrains.python.codeInsight.codeFragment.PyCodeFragment;
 import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
 import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
@@ -56,6 +59,7 @@
 import java.util.*;
 
 /**
+ * * TODO: Merge with {@link com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil#createMethod(String, com.jetbrains.python.psi.PyClass, com.jetbrains.python.psi.PyFunction.Modifier, java.util.Collection, String...)}
  * @author oleg
  */
 public class PyExtractMethodUtil {
@@ -332,9 +336,12 @@
     }
   }
 
-  private static void setSelectionAndCaret(Editor editor, final PsiElement callElement) {
+  private static void setSelectionAndCaret(Editor editor, @Nullable final PsiElement callElement) {
     editor.getSelectionModel().removeSelection();
-    editor.getCaretModel().moveToOffset(callElement.getTextOffset());
+    if (callElement != null) {
+      final int offset = callElement.getTextOffset();
+      editor.getCaretModel().moveToOffset(offset);
+    }
   }
 
   private static PsiElement replaceElements(final List<PsiElement> elementsRange, @NotNull PsiElement callElement) {
@@ -558,7 +565,7 @@
           throw new CommonRefactoringUtil.RefactoringErrorHintException(error);
         }
         if (Messages.showOkCancelDialog(error + ". " + RefactoringBundle.message("do.you.wish.to.continue"),
-                                        RefactoringBundle.message("warning.title"), Messages.getWarningIcon()) != 0){
+                                        RefactoringBundle.message("warning.title"), Messages.getWarningIcon()) != Messages.OK){
           throw new CommonRefactoringUtil.RefactoringErrorHintException(error);
         }
       }
diff --git a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java
index e17c76c..7f77401 100644
--- a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java
+++ b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java
@@ -104,7 +104,7 @@
     }
     int offset = editor.getCaretModel().getOffset();
     for (PsiElement occurrence : occurrences) {
-      if (occurrence.getTextRange().contains(offset)) {
+      if (occurrence != null && occurrence.getTextRange().contains(offset)) {
         return occurrence;
       }
     }
diff --git a/python/src/com/jetbrains/python/refactoring/introduce/field/PyIntroduceFieldHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/field/PyIntroduceFieldHandler.java
index 3818370e..6cdc4b3 100644
--- a/python/src/com/jetbrains/python/refactoring/introduce/field/PyIntroduceFieldHandler.java
+++ b/python/src/com/jetbrains/python/refactoring/introduce/field/PyIntroduceFieldHandler.java
@@ -310,7 +310,7 @@
 
   private static void putCaretOnFieldName(Editor editor, PsiElement occurrence) {
     PyQualifiedExpression qExpr = PsiTreeUtil.getParentOfType(occurrence, PyQualifiedExpression.class, false);
-    if (qExpr != null && qExpr.getQualifier() == null) {
+    if (qExpr != null && !qExpr.isQualified()) {
       qExpr = PsiTreeUtil.getParentOfType(qExpr, PyQualifiedExpression.class);
     }
     if (qExpr != null) {
diff --git a/python/src/com/jetbrains/python/refactoring/move/PyMoveClassOrFunctionProcessor.java b/python/src/com/jetbrains/python/refactoring/move/PyMoveClassOrFunctionProcessor.java
index fe4da8e..e5f5c26 100644
--- a/python/src/com/jetbrains/python/refactoring/move/PyMoveClassOrFunctionProcessor.java
+++ b/python/src/com/jetbrains/python/refactoring/move/PyMoveClassOrFunctionProcessor.java
@@ -23,6 +23,7 @@
 import com.intellij.psi.*;
 import com.intellij.psi.util.PsiTreeUtil;
 import com.intellij.psi.util.PsiUtilCore;
+import com.intellij.psi.util.QualifiedName;
 import com.intellij.refactoring.BaseRefactoringProcessor;
 import com.intellij.refactoring.ui.UsageViewDescriptorAdapter;
 import com.intellij.refactoring.util.CommonRefactoringUtil;
@@ -33,9 +34,7 @@
 import com.jetbrains.python.PyNames;
 import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
 import com.jetbrains.python.codeInsight.imports.PyImportOptimizer;
-import com.jetbrains.python.documentation.DocStringTypeReference;
 import com.jetbrains.python.psi.*;
-import com.intellij.psi.util.QualifiedName;
 import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
 import com.jetbrains.python.refactoring.PyRefactoringUtil;
 import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
@@ -141,7 +140,7 @@
     // TODO: Remove extra empty lines after the removed element
     element.delete();
     if (file != null) {
-      optimizeImports(file);
+      PyClassRefactoringUtil.optimizeImports(file);
     }
   }
 
@@ -184,7 +183,7 @@
       if (oldElement instanceof PyClass && PyNames.INIT.equals(expr.getName())) {
         return;
       }
-      if (expr.getQualifier() != null) {
+      if (expr.isQualified()) {
         final PyElementGenerator generator = PyElementGenerator.getInstance(expr.getProject());
         final PyExpression generated = generator.createExpressionFromText(LanguageLevel.forElement(expr), expr.getName());
         final PsiElement newExpr = expr.replace(generated);
@@ -193,7 +192,7 @@
     }
     if (usage instanceof PyStringLiteralExpression) {
       for (PsiReference ref : usage.getReferences()) {
-        if (ref instanceof DocStringTypeReference && ref.isReferenceTo(oldElement)) {
+        if (ref.isReferenceTo(oldElement)) {
           ref.bindToElement(newElement);
         }
       }
@@ -210,15 +209,12 @@
       if (resolvesToLocalStarImport(usage)) {
         PyClassRefactoringUtil.insertImport(usage, newElement);
         if (usageFile != null) {
-          optimizeImports(usageFile);
+          PyClassRefactoringUtil.optimizeImports(usageFile);
         }
       }
     }
   }
 
-  private static void optimizeImports(@NotNull PsiFile file) {
-    new PyImportOptimizer().processFile(file).run();
-  }
 
   private static boolean resolvesToLocalStarImport(@NotNull PsiElement element) {
     final PsiReference ref = element.getReference();
diff --git a/python/src/com/jetbrains/python/refactoring/move/PyMoveFileHandler.java b/python/src/com/jetbrains/python/refactoring/move/PyMoveFileHandler.java
index 84164cb..901aeee 100644
--- a/python/src/com/jetbrains/python/refactoring/move/PyMoveFileHandler.java
+++ b/python/src/com/jetbrains/python/refactoring/move/PyMoveFileHandler.java
@@ -109,7 +109,7 @@
           }
           else if (element instanceof PyReferenceExpression) {
             updatedFiles.add(file);
-            if (((PyReferenceExpression)element).getQualifier() != null) {
+            if (((PyReferenceExpression)element).isQualified()) {
               final QualifiedName newQualifiedName = QualifiedNameFinder.findCanonicalImportPath(newElement, element);
               replaceWithQualifiedExpression(element, newQualifiedName);
             } else {
diff --git a/python/src/com/jetbrains/python/refactoring/rename/RenamePyFunctionProcessor.java b/python/src/com/jetbrains/python/refactoring/rename/RenamePyFunctionProcessor.java
index ce91ea6..36e0a00 100644
--- a/python/src/com/jetbrains/python/refactoring/rename/RenamePyFunctionProcessor.java
+++ b/python/src/com/jetbrains/python/refactoring/rename/RenamePyFunctionProcessor.java
@@ -79,10 +79,10 @@
       String message = "Method " + function.getName() + " of class " + containingClass.getQualifiedName() + "\noverrides method of class "
                        + deepestSuperMethod.getContainingClass().getQualifiedName() + ".\nDo you want to rename the base method?";
       int rc = Messages.showYesNoCancelDialog(element.getProject(), message, "Rename", Messages.getQuestionIcon());
-      if (rc == 0) {
+      if (rc == Messages.YES) {
         return deepestSuperMethod;
       }
-      if (rc == 1) {
+      if (rc == Messages.NO) {
         return function;
       }
       return null;
@@ -98,8 +98,8 @@
                                              property.getName(), function.getName());
         final int rc = Messages.showYesNoCancelDialog(element.getProject(), message, "Rename", Messages.getQuestionIcon());
         switch (rc) {
-          case 0: return site;
-          case 1: return function;
+          case Messages.YES: return site;
+          case Messages.NO: return function;
           default: return null;
         }
       }
diff --git a/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithTryExceptSurrounder.java b/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithTryExceptSurrounder.java
index 5cdaba2..25a9f26 100644
--- a/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithTryExceptSurrounder.java
+++ b/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithTryExceptSurrounder.java
@@ -22,7 +22,6 @@
 import com.intellij.openapi.util.TextRange;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiFile;
-import com.intellij.psi.codeStyle.CodeStyleManager;
 import com.intellij.psi.util.PsiTreeUtil;
 import com.intellij.util.IncorrectOperationException;
 import com.jetbrains.python.PyBundle;
@@ -53,9 +52,10 @@
 
     final PsiFile psiFile = parent.getContainingFile();
     final Document document = psiFile.getViewProvider().getDocument();
-    final RangeMarker rangeMarker = document.createRangeMarker(tryStatement.getTextRange());
+    final TextRange range = tryStatement.getTextRange();
+    assert document != null;
+    final RangeMarker rangeMarker = document.createRangeMarker(range);
 
-    CodeStyleManager.getInstance(project).reformat(psiFile);
     final PsiElement element = psiFile.findElementAt(rangeMarker.getStartOffset());
     tryStatement = PsiTreeUtil.getParentOfType(element, PyTryExceptStatement.class);
     if (tryStatement != null) {
@@ -70,7 +70,9 @@
 
   protected TextRange getResultRange(PyTryExceptStatement tryStatement) {
     final PyExceptPart part = tryStatement.getExceptParts()[0];
-    return part.getStatementList().getTextRange();
+    final PyStatementList list = part.getStatementList();
+    assert list != null;
+    return list.getTextRange();
   }
 
   public String getTemplateDescription() {
diff --git a/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithTryFinallySurrounder.java b/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithTryFinallySurrounder.java
index 54e2c27..d457e32 100644
--- a/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithTryFinallySurrounder.java
+++ b/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithTryFinallySurrounder.java
@@ -17,6 +17,8 @@
 
 import com.intellij.codeInsight.CodeInsightBundle;
 import com.intellij.openapi.util.TextRange;
+import com.jetbrains.python.psi.PyFinallyPart;
+import com.jetbrains.python.psi.PyStatementList;
 import com.jetbrains.python.psi.PyTryExceptStatement;
 
 /**
@@ -37,6 +39,10 @@
 
   @Override
   protected TextRange getResultRange(PyTryExceptStatement tryStatement) {
-    return tryStatement.getFinallyPart().getStatementList().getTextRange();
+    final PyFinallyPart finallyPart = tryStatement.getFinallyPart();
+    assert finallyPart != null;
+    final PyStatementList statementList = finallyPart.getStatementList();
+    assert statementList != null;
+    return statementList.getTextRange();
   }
 }
diff --git a/python/src/com/jetbrains/python/remote/PyRemoteSdkData.java b/python/src/com/jetbrains/python/remote/PyRemoteSdkAdditionalDataBase.java
similarity index 71%
copy from python/src/com/jetbrains/python/remote/PyRemoteSdkData.java
copy to python/src/com/jetbrains/python/remote/PyRemoteSdkAdditionalDataBase.java
index ea2f8df..deb8030 100644
--- a/python/src/com/jetbrains/python/remote/PyRemoteSdkData.java
+++ b/python/src/com/jetbrains/python/remote/PyRemoteSdkAdditionalDataBase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,11 +15,10 @@
  */
 package com.jetbrains.python.remote;
 
-import com.intellij.remotesdk.RemoteSdkData;
+import com.intellij.remotesdk2.RemoteSdkAdditionalData2;
 
 /**
- * @author yole
+ * @author traff
  */
-public interface PyRemoteSdkData extends RemoteSdkData {
-  String getSkeletonsPath();
+public interface PyRemoteSdkAdditionalDataBase extends RemoteSdkAdditionalData2<PyRemoteSdkCredentials>, PySkeletonsPathAware {
 }
diff --git a/python/src/com/jetbrains/python/remote/PyRemoteSdkData.java b/python/src/com/jetbrains/python/remote/PyRemoteSdkCredentials.java
similarity index 81%
rename from python/src/com/jetbrains/python/remote/PyRemoteSdkData.java
rename to python/src/com/jetbrains/python/remote/PyRemoteSdkCredentials.java
index ea2f8df..5b5fc9b 100644
--- a/python/src/com/jetbrains/python/remote/PyRemoteSdkData.java
+++ b/python/src/com/jetbrains/python/remote/PyRemoteSdkCredentials.java
@@ -15,11 +15,11 @@
  */
 package com.jetbrains.python.remote;
 
-import com.intellij.remotesdk.RemoteSdkData;
+import com.intellij.remotesdk.RemoteSdkCredentials;
 
 /**
  * @author yole
  */
-public interface PyRemoteSdkData extends RemoteSdkData {
-  String getSkeletonsPath();
+public interface PyRemoteSdkCredentials extends RemoteSdkCredentials, PySkeletonsPathAware {
+
 }
diff --git a/python/src/com/jetbrains/python/remote/PySkeletonsPathAware.java b/python/src/com/jetbrains/python/remote/PySkeletonsPathAware.java
new file mode 100644
index 0000000..789d1ba
--- /dev/null
+++ b/python/src/com/jetbrains/python/remote/PySkeletonsPathAware.java
@@ -0,0 +1,10 @@
+package com.jetbrains.python.remote;
+
+/**
+ * @author traff
+ */
+public interface PySkeletonsPathAware {
+  String getSkeletonsPath();
+
+  void setSkeletonsPath(String path);
+}
diff --git a/python/src/com/jetbrains/python/remote/PythonRemoteInterpreterManager.java b/python/src/com/jetbrains/python/remote/PythonRemoteInterpreterManager.java
index 8be1df8..40e34f1 100644
--- a/python/src/com/jetbrains/python/remote/PythonRemoteInterpreterManager.java
+++ b/python/src/com/jetbrains/python/remote/PythonRemoteInterpreterManager.java
@@ -28,7 +28,7 @@
 import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.remotesdk.RemoteInterpreterException;
-import com.intellij.remotesdk.RemoteSdkData;
+import com.intellij.remotesdk.RemoteSdkCredentials;
 import com.intellij.remotesdk.RemoteSshProcess;
 import com.intellij.util.NullableConsumer;
 import com.intellij.util.PathMappingSettings;
@@ -52,14 +52,14 @@
     "Remote interpreter can't be executed. Please enable the Remote Hosts Access plugin.";
 
   public abstract ProcessHandler startRemoteProcess(@Nullable Project project,
-                                                    @NotNull PyRemoteSdkData data,
+                                                    @NotNull PyRemoteSdkCredentials data,
                                                     @NotNull GeneralCommandLine commandLine,
                                                     @Nullable
                                                     PathMappingSettings mappingSettings)
     throws RemoteInterpreterException;
 
   public abstract ProcessHandler startRemoteProcessWithPid(@Nullable Project project,
-                                                           @NotNull PyRemoteSdkData data,
+                                                           @NotNull PyRemoteSdkCredentials data,
                                                            @NotNull GeneralCommandLine commandLine,
                                                            @Nullable
                                                            PathMappingSettings mappingSettings)
@@ -70,7 +70,7 @@
 
 
   public abstract ProcessOutput runRemoteProcess(@Nullable Project project,
-                                                 RemoteSdkData data,
+                                                 RemoteSdkCredentials data,
                                                  String[] command,
                                                  @Nullable String workingDir,
                                                  boolean askForSudo)
@@ -78,7 +78,7 @@
 
   @NotNull
   public abstract RemoteSshProcess createRemoteProcess(@Nullable Project project,
-                                                         @NotNull RemoteSdkData data,
+                                                         @NotNull RemoteSdkCredentials data,
                                                          @NotNull GeneralCommandLine commandLine, boolean allocatePty)
     throws RemoteInterpreterException;
 
@@ -89,18 +89,18 @@
                                                                     @NotNull Sdk sdk,
                                                                     String path);
 
-  public abstract boolean ensureCanWrite(@Nullable Object projectOrComponent, RemoteSdkData data, String path);
+  public abstract boolean ensureCanWrite(@Nullable Object projectOrComponent, RemoteSdkCredentials data, String path);
 
   @Nullable
-  public abstract RemoteProjectSettings showRemoteProjectSettingsDialog(VirtualFile baseDir, RemoteSdkData data);
+  public abstract RemoteProjectSettings showRemoteProjectSettingsDialog(VirtualFile baseDir, RemoteSdkCredentials data);
 
   public abstract void createDeployment(Project project,
                                         VirtualFile projectDir,
                                         RemoteProjectSettings settings,
-                                        RemoteSdkData data);
+                                        RemoteSdkCredentials data);
 
   public abstract void copyFromRemote(@NotNull Project project,
-                                      RemoteSdkData data,
+                                      RemoteSdkCredentials data,
                                       List<PathMappingSettings.PathMapping> mappings);
 
   @Nullable
@@ -127,7 +127,7 @@
     return FileUtil.toSystemIndependentName(path).replace('/', separator);
   }
 
-  public static void addHelpersMapping(@NotNull RemoteSdkData data, @Nullable PathMappingSettings newMappingSettings) {
+  public static void addHelpersMapping(@NotNull RemoteSdkCredentials data, @Nullable PathMappingSettings newMappingSettings) {
     if (newMappingSettings == null) {
       newMappingSettings = new PathMappingSettings();
     }
@@ -135,7 +135,7 @@
   }
 
   public abstract PathMappingSettings setupMappings(@Nullable Project project,
-                                                    @NotNull PyRemoteSdkData data,
+                                                    @NotNull PyRemoteSdkCredentials data,
                                                     @Nullable PathMappingSettings mappingSettings);
 
   public abstract SdkAdditionalData loadRemoteSdkData(Sdk sdk, Element additional);
diff --git a/python/src/com/jetbrains/python/run/PyRemoteProcessStarter.java b/python/src/com/jetbrains/python/run/PyRemoteProcessStarter.java
index 5fc0bc8..5941d5f 100644
--- a/python/src/com/jetbrains/python/run/PyRemoteProcessStarter.java
+++ b/python/src/com/jetbrains/python/run/PyRemoteProcessStarter.java
@@ -23,9 +23,11 @@
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.projectRoots.Sdk;
-import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.projectRoots.SdkAdditionalData;
+import com.intellij.remotesdk2.RemoteSdkAdditionalData2;
 import com.intellij.util.PathMappingSettings;
-import com.jetbrains.python.remote.PyRemoteSdkData;
+import com.jetbrains.python.remote.PyRemoteSdkAdditionalDataBase;
+import com.jetbrains.python.remote.PyRemoteSdkCredentials;
 import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -43,21 +45,15 @@
     if (manager != null) {
       ProcessHandler processHandler;
 
-      while (true) {
-        try {
-          processHandler = doStartRemoteProcess(sdk, commandLine, manager, project, mappingSettings);
-          break;
+      try {
+        processHandler = doStartRemoteProcess(sdk, commandLine, manager, project, mappingSettings);
+      }
+      catch (ExecutionException e) {
+        final Application application = ApplicationManager.getApplication();
+        if (application != null && (application.isUnitTestMode() || application.isHeadlessEnvironment())) {
+          throw new RuntimeException(e);
         }
-        catch (ExecutionException e) {
-          final Application application = ApplicationManager.getApplication();
-          if (application != null && (application.isUnitTestMode() || application.isHeadlessEnvironment())) {
-            throw new RuntimeException(e);
-          }
-          if (Messages.showYesNoDialog(e.getMessage() + "\nTry again?", "Can't Run Remote Interpreter", Messages.getErrorIcon()) ==
-              Messages.NO) {
-            throw new ExecutionException("Can't run remote python interpreter: " + e.getMessage(), e);
-          }
-        }
+        throw new ExecutionException("Can't run remote python interpreter: " + e.getMessage(), e);
       }
       ProcessTerminatedListener.attach(processHandler);
       return processHandler;
@@ -74,7 +70,14 @@
                                                 @Nullable PathMappingSettings settings)
     throws ExecutionException {
 
-    return manager.startRemoteProcess(project, (PyRemoteSdkData)sdk.getSdkAdditionalData(), commandLine,
-                                      settings);
+    SdkAdditionalData data = sdk.getSdkAdditionalData();
+    assert data instanceof PyRemoteSdkAdditionalDataBase;
+    try {
+      return manager.startRemoteProcess(project, ((PyRemoteSdkAdditionalDataBase)data).getRemoteSdkCredentials(), commandLine,
+                                        settings);
+    }
+    catch (InterruptedException e) {
+      throw new ExecutionException(e); //TODO: handle exception
+    }
   }
 }
diff --git a/python/src/com/jetbrains/python/run/PythonRunConfigurationProducer.java b/python/src/com/jetbrains/python/run/PythonRunConfigurationProducer.java
index c14bde2..5817768 100644
--- a/python/src/com/jetbrains/python/run/PythonRunConfigurationProducer.java
+++ b/python/src/com/jetbrains/python/run/PythonRunConfigurationProducer.java
@@ -17,6 +17,7 @@
 
 import com.intellij.execution.Location;
 import com.intellij.execution.actions.ConfigurationContext;
+import com.intellij.execution.actions.ConfigurationFromContext;
 import com.intellij.execution.actions.RunConfigurationProducer;
 import com.intellij.openapi.extensions.Extensions;
 import com.intellij.openapi.module.Module;
@@ -94,4 +95,8 @@
     }
     return true;
   }
+  @Override
+  public boolean isPreferredConfiguration(ConfigurationFromContext self, ConfigurationFromContext other) {
+    return other.isProducedBy(PythonRunConfigurationProducer.class);
+  }
 }
diff --git a/python/src/com/jetbrains/python/run/PythonRunner.java b/python/src/com/jetbrains/python/run/PythonRunner.java
index c2a8937..b95da7c 100644
--- a/python/src/com/jetbrains/python/run/PythonRunner.java
+++ b/python/src/com/jetbrains/python/run/PythonRunner.java
@@ -44,10 +44,10 @@
 
   @Override
   protected RunContentDescriptor doExecute(
-    Project project,
-    RunProfileState state,
+    @NotNull Project project,
+    @NotNull RunProfileState state,
     RunContentDescriptor contentToReuse,
-    ExecutionEnvironment env
+    @NotNull ExecutionEnvironment env
   ) throws ExecutionException {
     FileDocumentManager.getInstance().saveAllDocuments();
 
diff --git a/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.form b/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.form
index bd84b37..ea6172e 100644
--- a/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.form
+++ b/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.form
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.python.sdk.CreateVirtualEnvDialog">
-  <grid id="cbd77" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="7" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+  <grid id="cbd77" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="6" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
     <margin top="0" left="0" bottom="0" right="0"/>
     <constraints>
       <xy x="48" y="54" width="550" height="342"/>
@@ -16,7 +16,7 @@
       </component>
       <vspacer id="b277d">
         <constraints>
-          <grid row="6" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+          <grid row="5" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
         </constraints>
       </vspacer>
       <component id="48fd" class="com.intellij.ui.components.JBLabel">
@@ -60,30 +60,22 @@
       </component>
       <component id="41e08" class="com.intellij.ui.components.JBCheckBox" binding="myMakeAvailableToAllProjectsCheckbox">
         <constraints>
-          <grid row="5" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+          <grid row="4" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
         </constraints>
         <properties>
           <selected value="false"/>
           <text value="Make available to &amp;all projects"/>
         </properties>
       </component>
-      <component id="9835d" class="com.intellij.ui.components.JBCheckBox" binding="mySitePackagesCheckBox">
+      <component id="d10a9" class="com.intellij.ui.components.JBCheckBox" binding="mySitePackagesCheckBox">
         <constraints>
-          <grid row="3" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+          <grid row="3" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
         </constraints>
         <properties>
+          <selected value="false"/>
           <text value="&amp;Inherit global site-packages"/>
         </properties>
       </component>
-      <component id="fd5f3" class="com.intellij.ui.components.JBCheckBox" binding="mySetAsProjectInterpreterCheckbox" default-binding="true">
-        <constraints>
-          <grid row="4" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
-        </constraints>
-        <properties>
-          <selected value="true"/>
-          <text value="&amp;Set as project interpreter for this project"/>
-        </properties>
-      </component>
     </children>
   </grid>
 </form>
diff --git a/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java b/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java
index e86fca4..1043866 100644
--- a/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java
+++ b/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java
@@ -15,6 +15,8 @@
  */
 package com.jetbrains.python.sdk;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
 import com.intellij.facet.ui.FacetEditorValidator;
 import com.intellij.facet.ui.FacetValidatorsManager;
 import com.intellij.openapi.application.Application;
@@ -33,7 +35,7 @@
 import com.intellij.openapi.vfs.LocalFileSystem;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.platform.LocationNameFieldsBinding;
-import com.intellij.remotesdk.RemoteSdkDataHolder;
+import com.intellij.remotesdk.RemoteSdkCredentialsHolder;
 import com.intellij.ui.CollectionComboBoxModel;
 import com.intellij.ui.DocumentAdapter;
 import com.intellij.ui.components.JBCheckBox;
@@ -67,18 +69,16 @@
   private JTextField myName;
   private JBCheckBox mySitePackagesCheckBox;
   private JBCheckBox myMakeAvailableToAllProjectsCheckbox;
-  private JBCheckBox mySetAsProjectInterpreterCheckbox;
   @Nullable private Project myProject;
   private String myInitialPath;
 
   public interface VirtualEnvCallback {
-    void virtualEnvCreated(Sdk sdk, boolean associateWithProject, boolean setAsProjectInterpreter);
+    void virtualEnvCreated(Sdk sdk, boolean associateWithProject);
   }
 
-  private static void setupVirtualEnvSdk(List<Sdk> allSdks,
+  private void setupVirtualEnvSdk(List<Sdk> allSdks,
                                          final String path,
                                          boolean associateWithProject,
-                                         final boolean makeActive,
                                          VirtualEnvCallback callback) {
     final VirtualFile sdkHome =
       ApplicationManager.getApplication().runWriteAction(new Computable<VirtualFile>() {
@@ -92,46 +92,45 @@
         SdkConfigurationUtil.createUniqueSdkName(PythonSdkType.getInstance(), sdkHome.getPath(), allSdks);
       final ProjectJdkImpl sdk = new ProjectJdkImpl(name, PythonSdkType.getInstance());
       sdk.setHomePath(sdkHome.getPath());
-      callback.virtualEnvCreated(sdk, associateWithProject, makeActive);
+      callback.virtualEnvCreated(sdk, associateWithProject);
+      PythonSdkType.setupSdkPaths(sdk, myProject, null);
     }
   }
 
   public CreateVirtualEnvDialog(Project project,
-                                boolean isNewProject,
                                 final List<Sdk> allSdks,
                                 @Nullable Sdk suggestedBaseSdk) {
     super(project);
-    setupDialog(project, isNewProject, allSdks, suggestedBaseSdk);
+    setupDialog(project, allSdks, suggestedBaseSdk);
   }
 
   public CreateVirtualEnvDialog(Component owner,
-                                boolean isNewProject,
                                 final List<Sdk> allSdks,
                                 @Nullable Sdk suggestedBaseSdk) {
     super(owner);
-    setupDialog(null, isNewProject, allSdks, suggestedBaseSdk);
+    setupDialog(null, allSdks, suggestedBaseSdk);
   }
 
-  private void setupDialog(Project project, boolean isNewProject, List<Sdk> allSdks, @Nullable Sdk suggestedBaseSdk) {
+  private void setupDialog(Project project, List<Sdk> allSdks, @Nullable Sdk suggestedBaseSdk) {
     myProject = project;
     init();
     setTitle("Create Virtual Environment");
     if (suggestedBaseSdk == null && allSdks.size() > 0) {
+      Iterables.removeIf(allSdks, new Predicate<Sdk>() {
+        @Override
+        public boolean apply(Sdk s) {
+          return PythonSdkType.isInvalid(s) || PythonSdkType.isVirtualEnv(s) || RemoteSdkCredentialsHolder.isRemoteSdk(s.getHomePath());
+        }
+      });
       List<Sdk> sortedSdks = new ArrayList<Sdk>(allSdks);
       Collections.sort(sortedSdks, new PreferredSdkComparator());
       suggestedBaseSdk = sortedSdks.get(0);
     }
     updateSdkList(allSdks, suggestedBaseSdk);
 
-    myMakeAvailableToAllProjectsCheckbox.setBorder(BorderFactory.createEmptyBorder(8, 0, 0, 0));
     if (project == null || project.isDefault() || !PlatformUtils.isPyCharm()) {
       myMakeAvailableToAllProjectsCheckbox.setSelected(true);
       myMakeAvailableToAllProjectsCheckbox.setVisible(false);
-      mySetAsProjectInterpreterCheckbox.setSelected(false);
-      mySetAsProjectInterpreterCheckbox.setVisible(false);
-    }
-    else if (isNewProject) {
-      mySetAsProjectInterpreterCheckbox.setText("Set as project interpreter for the project being created");
     }
 
     setOKActionEnabled(false);
@@ -238,17 +237,7 @@
 
   private void updateSdkList(final List<Sdk> allSdks, @Nullable Sdk initialSelection) {
     mySdkCombo.setRenderer(new PySdkListCellRenderer());
-    List<Sdk> baseSdks = new ArrayList<Sdk>();
-    for (Sdk s : allSdks) {
-      if (!PythonSdkType.isInvalid(s) && !PythonSdkType.isVirtualEnv(s) && !RemoteSdkDataHolder.isRemoteSdk(s.getHomePath())) {
-        baseSdks.add(s);
-      }
-      else if (s.equals(initialSelection)){
-        initialSelection = null;
-      }
-    }
-
-    mySdkCombo.setModel(new CollectionComboBoxModel(baseSdks, initialSelection));
+    mySdkCombo.setModel(new CollectionComboBoxModel(allSdks, initialSelection));
   }
 
   @Override
@@ -288,10 +277,6 @@
     return !myMakeAvailableToAllProjectsCheckbox.isSelected();
   }
 
-  public boolean setAsProjectInterpreter() {
-    return mySetAsProjectInterpreterCheckbox.isSelected();
-  }
-
   @Override
   public JComponent getPreferredFocusedComponent() {
     return myName;
@@ -326,7 +311,7 @@
           application.invokeLater(new Runnable() {
             @Override
             public void run() {
-              setupVirtualEnvSdk(allSdks, myPath, associateWithProject(), setAsProjectInterpreter(), callback);
+              setupVirtualEnvSdk(allSdks, myPath, associateWithProject(), callback);
             }
           }, ModalityState.any());
         }
diff --git a/python/src/com/jetbrains/python/sdk/InterpreterPathChooser.java b/python/src/com/jetbrains/python/sdk/InterpreterPathChooser.java
deleted file mode 100644
index ab57e35..0000000
--- a/python/src/com/jetbrains/python/sdk/InterpreterPathChooser.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright 2000-2013 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jetbrains.python.sdk;
-
-import com.google.common.collect.Lists;
-import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.application.ModalityState;
-import com.intellij.openapi.options.ShowSettingsUtil;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.projectRoots.Sdk;
-import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil;
-import com.intellij.openapi.ui.Messages;
-import com.intellij.openapi.ui.popup.JBPopupFactory;
-import com.intellij.openapi.ui.popup.ListPopup;
-import com.intellij.openapi.ui.popup.ListPopupStep;
-import com.intellij.openapi.ui.popup.PopupStep;
-import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
-import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.openapi.vfs.LocalFileSystem;
-import com.intellij.ui.awt.RelativePoint;
-import com.intellij.util.NullableConsumer;
-import com.intellij.util.SystemProperties;
-import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
-import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import javax.swing.*;
-import java.awt.*;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-/**
-* @author yole
-*/
-public class InterpreterPathChooser extends BaseListPopupStep<String> {
-  private final Project myProject;
-  private final Component myOwnerComponent;
-  private final Sdk[] myExistingSdks;
-  private final NullableConsumer<Sdk> myCallback;
-
-  private static final String LOCAL = "Local...";
-  private static final String REMOTE = "Remote...";
-  private static final String VIRTUALENV = "Create VirtualEnv...";
-
-  public static void show(final Project project,
-                          final Sdk[] existingSdks,
-                          final RelativePoint popupPoint,
-                          final boolean showVirtualEnv,
-                          final NullableConsumer<Sdk> callback) {
-    ListPopupStep sdkHomesStep = new InterpreterPathChooser(project, popupPoint.getComponent(), existingSdks, showVirtualEnv, callback);
-    final ListPopup popup = JBPopupFactory.getInstance().createListPopup(sdkHomesStep);
-    popup.show(popupPoint);
-  }
-
-  public InterpreterPathChooser(Project project,
-                                Component ownerComponent,
-                                Sdk[] existingSdks,
-                                boolean showVirtualEnv,
-                                NullableConsumer<Sdk> callback) {
-    super("Select Interpreter Path", getSuggestedPythonSdkPaths(existingSdks, showVirtualEnv));
-    myProject = project;
-    myOwnerComponent = ownerComponent;
-    myExistingSdks = existingSdks;
-    myCallback = callback;
-  }
-
-  private static List<String> getSuggestedPythonSdkPaths(Sdk[] existingSdks, boolean showVirtualEnv) {
-    List<String> paths = new ArrayList<String>();
-    Collection<String> sdkHomes = PythonSdkType.getInstance().suggestHomePaths();
-    for (String sdkHome : SdkConfigurationUtil.filterExistingPaths(PythonSdkType.getInstance(), sdkHomes, existingSdks)) {
-      paths.add(FileUtil.getLocationRelativeToUserHome(sdkHome));
-    }
-    paths.add(LOCAL);
-    if (PythonRemoteInterpreterManager.getInstance() != null) {
-      paths.add(REMOTE);
-    }
-    if (showVirtualEnv) {
-      paths.add(VIRTUALENV);
-    }
-    return paths;
-  }
-
-  @Nullable
-  @Override
-  public Icon getIconFor(String aValue) {
-    if (LOCAL.equals(aValue) || REMOTE.equals(aValue) || VIRTUALENV.equals(aValue)) return null;
-    String filePath = aValue;
-    if (StringUtil.startsWithChar(filePath, '~')) {
-      String home = SystemProperties.getUserHome();
-      filePath = home + filePath.substring(1);
-    }
-    final PythonSdkFlavor flavor = PythonSdkFlavor.getPlatformIndependentFlavor(filePath);
-    return flavor != null ? flavor.getIcon() : PythonSdkType.getInstance().getIcon();
-  }
-
-  @NotNull
-  @Override
-  public String getTextFor(String value) {
-    return FileUtil.toSystemDependentName(value);
-  }
-
-  private void sdkSelected(final String selectedValue) {
-    if (LOCAL.equals(selectedValue)) {
-      createLocalSdk();
-    }
-    else if (REMOTE.equals(selectedValue)) {
-      createRemoteSdk();
-    }
-    else if (VIRTUALENV.equals(selectedValue)) {
-      createVirtualEnvSdk();
-    }
-    else {
-      createSdkFromPath(selectedValue);
-    }
-  }
-
-  private void createLocalSdk() {
-    ApplicationManager.getApplication().invokeLater(new Runnable() {
-      @Override
-      public void run() {
-        SdkConfigurationUtil.createSdk(myProject, myExistingSdks, myCallback, PythonSdkType.getInstance());
-      }
-    }, ModalityState.any());
-  }
-
-  private void createRemoteSdk() {
-    PythonRemoteInterpreterManager remoteInterpreterManager = PythonRemoteInterpreterManager.getInstance();
-    if (remoteInterpreterManager != null) {
-      remoteInterpreterManager.addRemoteSdk(myProject, myOwnerComponent, Lists.newArrayList(myExistingSdks), myCallback);
-    }
-    else {
-      Messages.showErrorDialog("The Remote Hosts Access plugin is missing. Please enable the plugin in " +
-                               ShowSettingsUtil.getSettingsMenuName() +
-                               " | Plugins.", "Add Remote Interpreter");
-    }
-  }
-
-  private void createSdkFromPath(String selectedPath) {
-    String filePath = selectedPath;
-    if (StringUtil.startsWithChar(filePath, '~')) {
-      String home = SystemProperties.getUserHome();
-      filePath = home + filePath.substring(1);
-    }
-    Sdk sdk = SdkConfigurationUtil.setupSdk(myExistingSdks,
-                                            LocalFileSystem.getInstance().findFileByPath(filePath),
-                                            PythonSdkType.getInstance(), false, null, null);
-    myCallback.consume(sdk);
-  }
-
-  private void createVirtualEnvSdk() {
-    final CreateVirtualEnvDialog dialog;
-    final List<Sdk> allSdks = Arrays.asList(myExistingSdks);
-    if (myProject != null) {
-      dialog = new CreateVirtualEnvDialog(myProject, false, allSdks, null);
-    }
-    else {
-      dialog = new CreateVirtualEnvDialog(myOwnerComponent, false, allSdks, null);
-    }
-    dialog.show();
-    if (dialog.isOK()) {
-      dialog.createVirtualEnv(allSdks, new CreateVirtualEnvDialog.VirtualEnvCallback() {
-        @Override
-        public void virtualEnvCreated(Sdk sdk, boolean associateWithProject, boolean setAsProjectInterpreter) {
-          myCallback.consume(sdk);
-        }
-      });
-    }
-  }
-
-  @Override
-  public boolean canBeHidden(String value) {
-    return true;
-  }
-
-  @Override
-  public PopupStep onChosen(final String selectedValue, boolean finalChoice) {
-    return doFinalStep(new Runnable() {
-      public void run() {
-        sdkSelected(selectedValue);
-      }
-    });
-  }
-}
diff --git a/python/src/com/jetbrains/python/sdk/PreferredSdkComparator.java b/python/src/com/jetbrains/python/sdk/PreferredSdkComparator.java
index 50bc771..ff1cea9 100644
--- a/python/src/com/jetbrains/python/sdk/PreferredSdkComparator.java
+++ b/python/src/com/jetbrains/python/sdk/PreferredSdkComparator.java
@@ -37,6 +37,12 @@
     if (remote1Weight != remote2Weight) {
       return remote2Weight - remote1Weight;
     }
+    int detectedWeight1 = o1 instanceof PyDetectedSdk ? 0 : 1;
+    int detectedWeight2 = o2 instanceof PyDetectedSdk ? 0 : 1;
+    if (detectedWeight1 != detectedWeight2) {
+      return detectedWeight2 - detectedWeight1;
+    }
+
     int venv1weight = PythonSdkType.isVirtualEnv(o1) ? 0 : 1;
     int venv2weight = PythonSdkType.isVirtualEnv(o2) ? 0 : 1;
     if (venv1weight != venv2weight) {
@@ -47,6 +53,7 @@
     if (flavor1weight != flavor2weight) {
       return flavor2weight - flavor1weight;
     }
+
     return -Comparing.compare(o1.getVersionString(), o2.getVersionString());
   }
 }
diff --git a/python/src/com/jetbrains/python/sdk/PyDetectedSdk.java b/python/src/com/jetbrains/python/sdk/PyDetectedSdk.java
new file mode 100644
index 0000000..098462e
--- /dev/null
+++ b/python/src/com/jetbrains/python/sdk/PyDetectedSdk.java
@@ -0,0 +1,11 @@
+package com.jetbrains.python.sdk;
+
+import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;
+
+public class PyDetectedSdk extends ProjectJdkImpl {
+  public PyDetectedSdk(String name) {
+    super(name, PythonSdkType.getInstance());
+    setHomePath(name);
+  }
+
+}
diff --git a/python/src/com/jetbrains/python/sdk/PySdkListCellRenderer.java b/python/src/com/jetbrains/python/sdk/PySdkListCellRenderer.java
index 9065d37..9886224 100644
--- a/python/src/com/jetbrains/python/sdk/PySdkListCellRenderer.java
+++ b/python/src/com/jetbrains/python/sdk/PySdkListCellRenderer.java
@@ -19,6 +19,7 @@
 import com.intellij.openapi.projectRoots.Sdk;
 import com.intellij.openapi.projectRoots.SdkModificator;
 import com.intellij.openapi.projectRoots.SdkType;
+import com.intellij.openapi.util.IconLoader;
 import com.intellij.ui.LayeredIcon;
 import com.intellij.ui.ListCellRendererWrapper;
 import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
@@ -27,9 +28,10 @@
 import javax.swing.*;
 import java.util.Map;
 
-public class PySdkListCellRenderer extends ListCellRendererWrapper<Sdk> {
+public class PySdkListCellRenderer extends ListCellRendererWrapper<Object> {
   private final String myNullText;
   private final Map<Sdk, SdkModificator> mySdkModifiers;
+  public static final String SEPARATOR = "separator";
 
   public PySdkListCellRenderer() {
     myNullText = "";
@@ -42,8 +44,9 @@
   }
 
   @Override
-  public void customize(JList list, Sdk sdk, int index, boolean selected, boolean hasFocus) {
-    if (sdk != null) {
+  public void customize(JList list, Object item, int index, boolean selected, boolean hasFocus) {
+    if (item instanceof Sdk) {
+      Sdk sdk = (Sdk)item;
       final PythonSdkFlavor flavor = PythonSdkFlavor.getPlatformIndependentFlavor(sdk.getHomePath());
       final Icon icon = flavor != null ? flavor.getIcon() : ((SdkType)sdk.getSdkType()).getIcon();
 
@@ -54,7 +57,6 @@
       else {
         name = sdk.getName();
       }
-
       if (PythonSdkType.isInvalid(sdk)) {
         setText("[invalid] " + name);
         setIcon(wrapIconWithWarningDecorator(icon));
@@ -63,17 +65,22 @@
         setText("[incomplete] " + name);
         setIcon(wrapIconWithWarningDecorator(icon));
       }
+      else if (sdk instanceof PyDetectedSdk){
+        setText(name);
+        setIcon(IconLoader.getTransparentIcon(icon));
+      }
       else {
         setText(name);
         setIcon(icon);
       }
     }
-    else {
+    else if (SEPARATOR.equals(item))
+      setSeparator();
+    else if (item == null)
       setText(myNullText);
-    }
   }
 
-  private LayeredIcon wrapIconWithWarningDecorator(Icon icon) {
+  private static LayeredIcon wrapIconWithWarningDecorator(Icon icon) {
     final LayeredIcon layered = new LayeredIcon(2);
     layered.setIcon(icon, 0);
     // TODO: Create a separate invalid SDK overlay icon (DSGN-497)
diff --git a/python/src/com/jetbrains/python/sdk/PySdkUtil.java b/python/src/com/jetbrains/python/sdk/PySdkUtil.java
index a0ec72e..4185da7 100644
--- a/python/src/com/jetbrains/python/sdk/PySdkUtil.java
+++ b/python/src/com/jetbrains/python/sdk/PySdkUtil.java
@@ -26,6 +26,7 @@
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiFile;
 import com.intellij.remotesdk.RemoteCredentials;
+import com.intellij.remotesdk2.RemoteSdkAdditionalData2;
 import com.intellij.util.ArrayUtil;
 import com.intellij.util.containers.HashMap;
 import org.jetbrains.annotations.NonNls;
@@ -213,7 +214,7 @@
   }
 
   public static boolean isRemote(@Nullable Sdk sdk) {
-    return sdk != null && sdk.getSdkAdditionalData() instanceof RemoteCredentials;
+    return sdk != null && sdk.getSdkAdditionalData() instanceof RemoteSdkAdditionalData2;
   }
 
   public static boolean isElementInSkeletons(@NotNull final PsiElement element) {
diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkDetailsStep.java b/python/src/com/jetbrains/python/sdk/PythonSdkDetailsStep.java
new file mode 100644
index 0000000..df04801
--- /dev/null
+++ b/python/src/com/jetbrains/python/sdk/PythonSdkDetailsStep.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.sdk;
+
+import com.google.common.collect.Lists;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.options.ShowSettingsUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkAdditionalData;
+import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;
+import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.popup.*;
+import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
+import com.intellij.util.NullableConsumer;
+import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
+import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class PythonSdkDetailsStep extends BaseListPopupStep<String> {
+  private static DialogWrapper myMore;
+  private final Project myProject;
+  private final Component myOwnerComponent;
+  private final Sdk[] myExistingSdks;
+  private final NullableConsumer<Sdk> myCallback;
+
+  private static final String LOCAL = "Add Local";
+  private static final String REMOTE = "Add Remote";
+  private static final String VIRTUALENV = "Create VirtualEnv";
+  private static final String MORE = "More...";
+
+  public static void show(final Project project,
+                          final Sdk[] existingSdks,
+                          DialogWrapper moreDialog,
+                          JComponent ownerComponent, final Point popupPoint,
+                          final boolean showMore,
+                          final NullableConsumer<Sdk> callback) {
+    myMore = moreDialog;
+    final ListPopupStep sdkHomesStep = new PythonSdkDetailsStep(project, ownerComponent, existingSdks, showMore, callback);
+    final ListPopup popup = JBPopupFactory.getInstance().createListPopup(sdkHomesStep);
+    popup.showInScreenCoordinates(ownerComponent, popupPoint);
+  }
+
+  public PythonSdkDetailsStep(Project project,
+                              Component ownerComponent,
+                              Sdk[] existingSdks,
+                              boolean showMore,
+                              NullableConsumer<Sdk> callback) {
+    super(null, getAvailableOptions(showMore));
+    myProject = project;
+    myOwnerComponent = ownerComponent;
+    myExistingSdks = existingSdks;
+    myCallback = callback;
+  }
+
+  private static List<String> getAvailableOptions(boolean showMore) {
+    final List<String> options = new ArrayList<String>();
+    options.add(LOCAL);
+    if (PythonRemoteInterpreterManager.getInstance() != null) {
+      options.add(REMOTE);
+    }
+    options.add(VIRTUALENV);
+
+    if (showMore) {
+      options.add(MORE);
+    }
+    return options;
+  }
+
+  @Nullable
+  @Override
+  public ListSeparator getSeparatorAbove(String value) {
+    return MORE.equals(value) ? new ListSeparator() : null;
+  }
+
+  private void optionSelected(final String selectedValue) {
+    if (LOCAL.equals(selectedValue)) {
+      createLocalSdk();
+    }
+    else if (REMOTE.equals(selectedValue)) {
+      createRemoteSdk();
+    }
+    else if (VIRTUALENV.equals(selectedValue)) {
+      createVirtualEnvSdk();
+    }
+    else {
+      myMore.show();
+    }
+  }
+
+  private void createLocalSdk() {
+    ApplicationManager.getApplication().invokeLater(new Runnable() {
+      @Override
+      public void run() {
+        SdkConfigurationUtil.createSdk(myProject, myExistingSdks, myCallback, false, PythonSdkType.getInstance());
+      }
+    }, ModalityState.any());
+  }
+
+  private void createRemoteSdk() {
+    PythonRemoteInterpreterManager remoteInterpreterManager = PythonRemoteInterpreterManager.getInstance();
+    if (remoteInterpreterManager != null) {
+      remoteInterpreterManager.addRemoteSdk(myProject, myOwnerComponent, Lists.newArrayList(myExistingSdks), myCallback);
+    }
+    else {
+      Messages.showErrorDialog("The Remote Hosts Access plugin is missing. Please enable the plugin in " +
+                               ShowSettingsUtil.getSettingsMenuName() +
+                               " | Plugins.", "Add Remote Interpreter");
+    }
+  }
+
+  private void createVirtualEnvSdk() {
+    CreateVirtualEnvDialog.VirtualEnvCallback callback = new CreateVirtualEnvDialog.VirtualEnvCallback() {
+      @Override
+      public void virtualEnvCreated(Sdk sdk, boolean associateWithProject) {
+        PythonSdkType.setupSdkPaths(sdk, myProject, null);
+        if (associateWithProject) {
+          SdkAdditionalData additionalData = sdk.getSdkAdditionalData();
+          if (additionalData == null) {
+            additionalData = new PythonSdkAdditionalData(PythonSdkFlavor.getFlavor(sdk.getHomePath()));
+            ((ProjectJdkImpl)sdk).setSdkAdditionalData(additionalData);
+          }
+          ((PythonSdkAdditionalData)additionalData).associateWithProject(myProject);
+        }
+        myCallback.consume(sdk);
+      }
+    };
+
+    final CreateVirtualEnvDialog dialog;
+    final List<Sdk> allSdks = Lists.newArrayList(myExistingSdks);
+
+    final List<PythonSdkFlavor> flavors = PythonSdkFlavor.getApplicableFlavors(false);
+    for (PythonSdkFlavor flavor : flavors) {
+      final Collection<String> strings = flavor.suggestHomePaths();
+      for (String string : strings) {
+        allSdks.add(new PyDetectedSdk(string));
+      }
+    }
+
+    if (myProject != null) {
+      dialog = new CreateVirtualEnvDialog(myProject, allSdks, null);
+    }
+    else {
+      dialog = new CreateVirtualEnvDialog(myOwnerComponent, allSdks, null);
+    }
+    dialog.show();
+    if (dialog.isOK()) {
+      dialog.createVirtualEnv(allSdks, callback);
+    }
+  }
+
+  @Override
+  public boolean canBeHidden(String value) {
+    return true;
+  }
+
+  @Override
+  public PopupStep onChosen(final String selectedValue, boolean finalChoice) {
+    return doFinalStep(new Runnable() {
+      public void run() {
+        optionSelected(selectedValue);
+      }
+    });
+  }
+}
diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkType.java b/python/src/com/jetbrains/python/sdk/PythonSdkType.java
index 65d80821..6aa0c6d 100644
--- a/python/src/com/jetbrains/python/sdk/PythonSdkType.java
+++ b/python/src/com/jetbrains/python/sdk/PythonSdkType.java
@@ -21,19 +21,14 @@
 import com.intellij.facet.FacetConfiguration;
 import com.intellij.facet.FacetManager;
 import com.intellij.ide.DataManager;
-import com.intellij.notification.Notification;
-import com.intellij.notification.NotificationListener;
-import com.intellij.notification.NotificationType;
 import com.intellij.openapi.actionSystem.CommonDataKeys;
 import com.intellij.openapi.application.Application;
 import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.application.ModalityState;
 import com.intellij.openapi.application.PathManager;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.fileChooser.FileChooserDescriptor;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.module.ModuleUtilCore;
-import com.intellij.openapi.options.ShowSettingsUtil;
 import com.intellij.openapi.progress.ProgressIndicator;
 import com.intellij.openapi.progress.ProgressManager;
 import com.intellij.openapi.progress.Task;
@@ -50,11 +45,14 @@
 import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.openapi.util.text.CharFilter;
 import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.openapi.vfs.*;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.PsiElement;
-import com.intellij.remotesdk.RemoteSdkData;
-import com.intellij.remotesdk.RemoteSdkDataHolder;
-import com.intellij.ui.awt.RelativePoint;
+import com.intellij.reference.SoftReference;
+import com.intellij.remotesdk.RemoteSdkCredentials;
+import com.intellij.remotesdk.RemoteSdkCredentialsHolder;
 import com.intellij.util.ArrayUtil;
 import com.intellij.util.Consumer;
 import com.intellij.util.NullableConsumer;
@@ -78,10 +76,10 @@
 import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
-import javax.swing.event.HyperlinkEvent;
 import java.awt.*;
 import java.io.File;
 import java.io.FilenameFilter;
+import java.io.IOException;
 import java.lang.ref.WeakReference;
 import java.util.*;
 import java.util.List;
@@ -149,6 +147,10 @@
   @NonNls
   @Nullable
   public String suggestHomePath() {
+    final String pythonFromPath = findPythonInPath();
+    if (pythonFromPath != null) {
+      return pythonFromPath;
+    }
     for (PythonSdkFlavor flavor : PythonSdkFlavor.getApplicableFlavors()) {
       TreeSet<String> candidates = createVersionSet();
       candidates.addAll(flavor.suggestHomePaths());
@@ -161,6 +163,23 @@
     return null;
   }
 
+  @Nullable
+  private static String findPythonInPath() {
+    final String defaultCommand = SystemInfo.isWindows ? "python.exe" : "python";
+    final String path = System.getenv("PATH");
+    for (String root : path.split(File.pathSeparator)) {
+      final File file = new File(root, defaultCommand);
+      if (file.exists()) {
+        try {
+          return file.getCanonicalPath();
+        }
+        catch (IOException ignored) {
+        }
+      }
+    }
+    return null;
+  }
+
   @Override
   public Collection<String> suggestHomePaths() {
     List<String> candidates = new ArrayList<String>();
@@ -260,15 +279,18 @@
 
   public void showCustomCreateUI(SdkModel sdkModel, final JComponent parentComponent, final Consumer<Sdk> sdkCreatedCallback) {
     Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(parentComponent));
-    InterpreterPathChooser.show(project, sdkModel.getSdks(), RelativePoint.getCenterOf(parentComponent), true, new NullableConsumer<Sdk>() {
-      @Override
-      public void consume(@Nullable Sdk sdk) {
-        if (sdk != null) {
-          sdk.putUserData(SDK_CREATOR_COMPONENT_KEY, new WeakReference<Component>(parentComponent));
-          sdkCreatedCallback.consume(sdk);
+    final Point point = parentComponent.getMousePosition();
+    SwingUtilities.convertPointToScreen(point, parentComponent);
+    PythonSdkDetailsStep
+      .show(project, sdkModel.getSdks(), null, parentComponent, point, false, new NullableConsumer<Sdk>() {
+        @Override
+        public void consume(@Nullable Sdk sdk) {
+          if (sdk != null) {
+            sdk.putUserData(SDK_CREATOR_COMPONENT_KEY, new WeakReference<Component>(parentComponent));
+            sdkCreatedCallback.consume(sdk);
+          }
         }
-      }
-    });
+      });
   }
 
   public static boolean isVirtualEnv(Sdk sdk) {
@@ -406,15 +428,16 @@
     return null;
   }
 
-  public void saveAdditionalData(final SdkAdditionalData additionalData, final Element additional) {
+  @Override
+  public void saveAdditionalData(@NotNull final SdkAdditionalData additionalData, @NotNull final Element additional) {
     if (additionalData instanceof PythonSdkAdditionalData) {
       ((PythonSdkAdditionalData)additionalData).save(additional);
     }
   }
 
   @Override
-  public SdkAdditionalData loadAdditionalData(final Sdk currentSdk, final Element additional) {
-    if (RemoteSdkDataHolder.isRemoteSdk(currentSdk.getHomePath())) {
+  public SdkAdditionalData loadAdditionalData(@NotNull final Sdk currentSdk, final Element additional) {
+    if (RemoteSdkCredentialsHolder.isRemoteSdk(currentSdk.getHomePath())) {
       PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
       if (manager != null) {
         return manager.loadRemoteSdkData(currentSdk, additional);
@@ -423,42 +446,12 @@
     return PythonSdkAdditionalData.load(currentSdk, additional);
   }
 
-  private boolean switchPathToInterpreter(Sdk currentSdk, String... variants) {
-    File sdk_file = new File(currentSdk.getHomePath());
-    final String sdk_name = currentSdk.getName();
-    boolean success = false;
-    for (String interpreter : variants) {
-      File binary = interpreter.startsWith("/") ? new File(interpreter) : new File(sdk_file, interpreter);
-      if (binary.exists()) {
-        if (currentSdk instanceof SdkModificator) {
-          final SdkModificator sdk_as_modificator = (SdkModificator)currentSdk;
-          sdk_as_modificator.setHomePath(binary.getPath());
-          sdk_as_modificator.setName(suggestSdkName(currentSdk.getName(), binary.getAbsolutePath()));
-          //setupSdkPaths(currentSdk);
-          success = true;
-          break;
-        }
-      }
-    }
-    if (!success) {
-      ApplicationManager.getApplication().invokeLater(new Runnable() {
-        public void run() {
-          Messages.showWarningDialog(
-            "Failed to convert Python SDK '" + sdk_name + "'\nplease delete and re-create it",
-            "Converting Python SDK"
-          );
-        }
-      }, ModalityState.NON_MODAL);
-    }
-    return success;
-  }
-
   @Nullable
   public static String findSkeletonsPath(Sdk sdk) {
     final String[] urls = sdk.getRootProvider().getUrls(BUILTIN_ROOT_TYPE);
     for (String url : urls) {
       if (url.contains(SKELETON_DIR_NAME)) {
-        return VfsUtil.urlToPath(url);
+        return VfsUtilCore.urlToPath(url);
       }
     }
     return null;
@@ -495,11 +488,8 @@
 
   public void setupSdkPaths(@NotNull final Sdk sdk) {
     final Project project;
-    Component ownerComponent = null;
     final WeakReference<Component> ownerComponentRef = sdk.getUserData(SDK_CREATOR_COMPONENT_KEY);
-    if (ownerComponentRef != null) {
-      ownerComponent = ownerComponentRef.get();
-    }
+    Component ownerComponent = SoftReference.dereference(ownerComponentRef);
     if (ownerComponent != null) {
       project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(ownerComponent));
     }
@@ -532,57 +522,46 @@
       LOG.error("For refreshing skeletons of remote SDK, either project or owner component must be specified");
     }
     final ProgressManager progressManager = ProgressManager.getInstance();
-    final Ref<Boolean> success = new Ref<Boolean>();
-    success.set(true);
+    final Ref<Boolean> sdkPathsUpdatedRef = new Ref<Boolean>(false);
     final Task.Modal setupTask = new Task.Modal(project, "Setting up library files for " + sdk.getName(), false) {
-      // TODO: make this a backgroundable task. see #setupSdkPaths(final Sdk sdk) and its modificator handling
       public void run(@NotNull final ProgressIndicator indicator) {
         sdkModificator.removeAllRoots();
         try {
           updateSdkRootsFromSysPath(sdk, sdkModificator, indicator);
           updateUserAddedPaths(sdk, sdkModificator, indicator);
-          if (!ApplicationManager.getApplication().isUnitTestMode()) {
-            PySkeletonRefresher.refreshSkeletonsOfSdk(project, ownerComponent,
-                                                      getSkeletonsPath(PathManager.getSystemPath(), sdk.getHomePath()),
-                                                      null, sdk
-            );
-            PythonSdkUpdater.getInstance().markAlreadyUpdated(sdk.getHomePath());
-          }
+          PythonSdkUpdater.getInstance().markAlreadyUpdated(sdk.getHomePath());
+          sdkPathsUpdatedRef.set(true);
         }
-        catch (InvalidSdkException e) {
-          if (!isInvalid(sdk)) {
-            LOG.warn(e);
-            final Notification notification = createInvalidSdkNotification(project);
-            notification.notify(project);
-          }
+        catch (InvalidSdkException ignored) {
         }
       }
     };
     progressManager.run(setupTask);
-    return success.get();
-  }
-
-  @NotNull
-  public static Notification createInvalidSdkNotification(@Nullable final Project project) {
-    String message = "Cannot run the project interpreter.";
-    if (project != null && !project.isDisposed()) {
-      message += " <a href=\"xxx\">Configure...</a>";
+    final Boolean sdkPathsUpdated = sdkPathsUpdatedRef.get();
+    final Application application = ApplicationManager.getApplication();
+    if (sdkPathsUpdated && !application.isUnitTestMode()) {
+      application.invokeLater(new Runnable() {
+        @Override
+        public void run() {
+          progressManager.run(new Task.Backgroundable(project, PyBundle.message("sdk.gen.updating.skels"), false) {
+            @Override
+            public void run(@NotNull ProgressIndicator indicator) {
+              try {
+                final String skeletonsPath = getSkeletonsPath(PathManager.getSystemPath(), sdk.getHomePath());
+                PySkeletonRefresher.refreshSkeletonsOfSdk(project, ownerComponent, skeletonsPath, null, sdk);
+              }
+              catch (InvalidSdkException e) {
+                // If the SDK is invalid, the user should worry about the SDK itself, not about skeletons generation errors
+                if (!isInvalid(sdk)) {
+                  LOG.error(e);
+                }
+              }
+            }
+          });
+        }
+      });
     }
-    return new Notification("xxx",
-                            "Invalid Project Interpreter",
-                            message,
-                            NotificationType.ERROR,
-                            new NotificationListener() {
-                              @Override
-                              public void hyperlinkUpdate(@NotNull Notification notification,
-                                                          @NotNull HyperlinkEvent event) {
-                                if (project != null && !project.isDisposed()) {
-                                  final ShowSettingsUtil settings = ShowSettingsUtil.getInstance();
-                                  settings.showSettingsDialog(project, "Project Interpreter");
-                                }
-                                notification.expire();
-                              }
-                            });
+    return sdkPathsUpdated;
   }
 
   /**
@@ -959,9 +938,9 @@
   }
 
   public static boolean isIncompleteRemote(Sdk sdk) {
-    if (sdk.getSdkAdditionalData() instanceof RemoteSdkData) {
+    if (sdk.getSdkAdditionalData() instanceof RemoteSdkCredentials) {
       //noinspection ConstantConditions
-      if (!((RemoteSdkData)sdk.getSdkAdditionalData()).isInitialized()) {
+      if (!((RemoteSdkCredentials)sdk.getSdkAdditionalData()).isInitialized()) {
         return true;
       }
     }
diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkUpdater.java b/python/src/com/jetbrains/python/sdk/PythonSdkUpdater.java
index fc0d798..2cc749f 100644
--- a/python/src/com/jetbrains/python/sdk/PythonSdkUpdater.java
+++ b/python/src/com/jetbrains/python/sdk/PythonSdkUpdater.java
@@ -15,7 +15,6 @@
  */
 package com.jetbrains.python.sdk;
 
-import com.intellij.notification.Notification;
 import com.intellij.openapi.application.Application;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.diagnostic.Logger;
@@ -115,9 +114,7 @@
                     }
                     catch (InvalidSdkException e) {
                       if (!PythonSdkType.isInvalid(sdk)) {
-                        LOG.warn(e);
-                        final Notification notification = PythonSdkType.createInvalidSdkNotification(myProject);
-                        notification.notify(myProject);
+                        LOG.error(e);
                       }
                     }
                     myAlreadyUpdated.add(sdk.getHomePath());
diff --git a/python/src/com/jetbrains/python/sdk/flavors/MacPythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/flavors/MacPythonSdkFlavor.java
index 780effe..5d241b66 100644
--- a/python/src/com/jetbrains/python/sdk/flavors/MacPythonSdkFlavor.java
+++ b/python/src/com/jetbrains/python/sdk/flavors/MacPythonSdkFlavor.java
@@ -16,6 +16,7 @@
 package com.jetbrains.python.sdk.flavors;
 
 import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VFileProperty;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
 
@@ -57,7 +58,7 @@
             if (binDir != null && binDir.isDirectory()) {
               for (String name : POSSIBLE_BINARY_NAMES) {
                 final VirtualFile child = binDir.findChild(name);
-                if (child != null) {
+                if (child != null && !child.is(VFileProperty.SYMLINK)) {
                   candidates.add(child.getPath());
                   break;
                 }
diff --git a/python/src/com/jetbrains/python/sdk/flavors/PythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/flavors/PythonSdkFlavor.java
index 60fa802..07c8e1c 100644
--- a/python/src/com/jetbrains/python/sdk/flavors/PythonSdkFlavor.java
+++ b/python/src/com/jetbrains/python/sdk/flavors/PythonSdkFlavor.java
@@ -72,6 +72,10 @@
   }
 
   public static List<PythonSdkFlavor> getApplicableFlavors() {
+    return getApplicableFlavors(true);
+  }
+
+  public static List<PythonSdkFlavor> getApplicableFlavors(boolean addPlatformIndependent) {
     List<PythonSdkFlavor> result = new ArrayList<PythonSdkFlavor>();
 
     if (SystemInfo.isWindows) {
@@ -84,7 +88,8 @@
       result.add(UnixPythonSdkFlavor.INSTANCE);
     }
 
-    result.addAll(getPlatformIndependentFlavors());
+    if (addPlatformIndependent)
+      result.addAll(getPlatformIndependentFlavors());
 
     return result;
   }
diff --git a/python/src/com/jetbrains/python/sdk/flavors/UnixPythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/flavors/UnixPythonSdkFlavor.java
index c28587c..2440661 100644
--- a/python/src/com/jetbrains/python/sdk/flavors/UnixPythonSdkFlavor.java
+++ b/python/src/com/jetbrains/python/sdk/flavors/UnixPythonSdkFlavor.java
@@ -16,6 +16,7 @@
 package com.jetbrains.python.sdk.flavors;
 
 import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VFileProperty;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
 
@@ -47,14 +48,15 @@
       if (rootDir instanceof NewVirtualFile) {
         ((NewVirtualFile)rootDir).markDirty();
       }
-      rootDir.refresh(false, false);
+      rootDir.refresh(true, false);
       VirtualFile[] suspects = rootDir.getChildren();
       for (VirtualFile child : suspects) {
         if (!child.isDirectory()) {
           final String childName = child.getName();
           for (String name : NAMES) {
             if (childName.startsWith(name)) {
-              if (!childName.endsWith("-config") && !childName.startsWith("pythonw")) {
+              if (!childName.endsWith("-config") && !childName.startsWith("pythonw") &&
+                  !childName.endsWith("m") && !child.is(VFileProperty.SYMLINK)) {
                 candidates.add(child.getPath());
               }
               break;
diff --git a/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvSdkFlavor.java b/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvSdkFlavor.java
index 5bdecd7..b9de2e2 100644
--- a/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvSdkFlavor.java
+++ b/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvSdkFlavor.java
@@ -83,7 +83,7 @@
   public static Collection<String> findInDirectory(VirtualFile rootDir) {
     List<String> candidates = new ArrayList<String>();
     if (rootDir != null) {
-      rootDir.refresh(false, false);
+      rootDir.refresh(true, false);
       VirtualFile[] suspects = rootDir.getChildren();
       for (VirtualFile child : suspects) {
         if (child.isDirectory()) {
diff --git a/python/src/com/jetbrains/python/sdk/flavors/WinPythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/flavors/WinPythonSdkFlavor.java
index 2c45c83..9de862f 100644
--- a/python/src/com/jetbrains/python/sdk/flavors/WinPythonSdkFlavor.java
+++ b/python/src/com/jetbrains/python/sdk/flavors/WinPythonSdkFlavor.java
@@ -20,6 +20,7 @@
 import com.intellij.openapi.vfs.LocalFileSystem;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
+import com.jetbrains.python.PythonHelpersLocator;
 
 import java.io.File;
 import java.util.Collection;
@@ -39,6 +40,7 @@
   public Collection<String> suggestHomePaths() {
     Set<String> candidates = new TreeSet<String>();
     findInCandidatePaths(candidates, "python.exe", "jython.bat", "pypy.exe");
+    candidates.add(PythonHelpersLocator.getHelpersRoot().getParent());
     return candidates;
   }
 
@@ -75,7 +77,7 @@
       if (rootVDir instanceof NewVirtualFile) {
         ((NewVirtualFile)rootVDir).markDirty();
       }
-      rootVDir.refresh(false, false);
+      rootVDir.refresh(true, false);
       for (VirtualFile dir : rootVDir.getChildren()) {
         if (dir.isDirectory() && dir.getName().toLowerCase().startsWith(dir_prefix)) {
           VirtualFile python_exe = dir.findChild(exe_name);
diff --git a/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java b/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java
index c9454fb..077ced3 100644
--- a/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java
+++ b/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java
@@ -41,6 +41,7 @@
 import com.intellij.util.io.ZipUtil;
 import com.jetbrains.python.PyBundle;
 import com.jetbrains.python.PyNames;
+import com.jetbrains.python.codeInsight.userSkeletons.PyUserSkeletonsUtil;
 import com.jetbrains.python.packaging.PyExternalProcessException;
 import com.jetbrains.python.packaging.PyPackageManager;
 import com.jetbrains.python.packaging.PyPackageManagerImpl;
@@ -91,6 +92,8 @@
   private static final Pattern FROM_LINE_V2 = Pattern.compile("# from (.*)$");
   private static final Pattern BY_LINE_V2 = Pattern.compile("# by generator (.*)$");
 
+  private static int ourGeneratingCount = 0;
+
   private String myExtraSyspath;
   private VirtualFile myPregeneratedSkeletons;
   private int myGeneratorVersion;
@@ -103,6 +106,14 @@
     refreshSkeletonsOfSdk(project, null, PythonSdkType.findSkeletonsPath(sdk), new Ref<Boolean>(false), sdk);
   }
 
+  public static synchronized boolean isGeneratingSkeletons() {
+    return ourGeneratingCount > 0;
+  }
+
+  private static synchronized void changeGeneratingSkeletons(int increment) {
+    ourGeneratingCount += increment;
+  }
+
   public static void refreshSkeletonsOfSdk(@Nullable Project project,
                                            Component ownerComponent,
                                            String skeletonsPath,
@@ -120,16 +131,23 @@
       LOG.info("Refreshing skeletons for " + homePath);
       SkeletonVersionChecker checker = new SkeletonVersionChecker(0); // this default version won't be used
       final PySkeletonRefresher refresher = new PySkeletonRefresher(project, ownerComponent, sdk, skeletonsPath, indicator);
-      List<String> sdkErrors = refresher.regenerateSkeletons(checker, migrationFlag);
-      if (sdkErrors.size() > 0) {
-        String sdkName = sdk.getName();
-        List<String> knownErrors = errors.get(sdkName);
-        if (knownErrors == null) {
-          errors.put(sdkName, sdkErrors);
+
+      changeGeneratingSkeletons(1);
+      try {
+        List<String> sdkErrors = refresher.regenerateSkeletons(checker, migrationFlag);
+        if (sdkErrors.size() > 0) {
+          String sdkName = sdk.getName();
+          List<String> knownErrors = errors.get(sdkName);
+          if (knownErrors == null) {
+            errors.put(sdkName, sdkErrors);
+          }
+          else {
+            knownErrors.addAll(sdkErrors);
+          }
         }
-        else {
-          knownErrors.addAll(sdkErrors);
-        }
+      }
+      finally {
+        changeGeneratingSkeletons(-1);
       }
     }
     if (failedSdks.size() > 0 || errors.size() > 0) {
@@ -207,13 +225,14 @@
     final VirtualFile[] classDirs = sdk.getRootProvider().getFiles(OrderRootType.CLASSES);
     final StringBuilder builder = new StringBuilder("");
     int countAddedPaths = 0;
+    final VirtualFile userSkeletonsDir = PyUserSkeletonsUtil.getUserSkeletonsDirectory();
     for (VirtualFile file : classDirs) {
       if (countAddedPaths > 0) {
         builder.append(File.pathSeparator);
       }
       if (file.isInLocalFileSystem()) {
         final String pathname = file.getPath();
-        if (pathname != null && !pathname.equals(skeletonsPath)) {
+        if (pathname != null && !pathname.equals(skeletonsPath) && !file.equals(userSkeletonsDir)) {
           builder.append(pathname);
           countAddedPaths += 1;
         }
@@ -248,6 +267,7 @@
     final String skeletonsPath = getSkeletonsPath();
     final File skeletonsDir = new File(skeletonsPath);
     if (!skeletonsDir.exists()) {
+      //noinspection ResultOfMethodCallIgnored
       skeletonsDir.mkdirs();
     }
     final String readablePath = FileUtil.getLocationRelativeToUserHome(homePath);
@@ -427,7 +447,7 @@
         reader.close();
       }
     }
-    catch (IOException e) {
+    catch (IOException ignored) {
     }
     return null;
   }
diff --git a/python/src/com/jetbrains/python/spellchecker/PythonSpellcheckerStrategy.java b/python/src/com/jetbrains/python/spellchecker/PythonSpellcheckerStrategy.java
index 4a0a59d..b24e53c 100644
--- a/python/src/com/jetbrains/python/spellchecker/PythonSpellcheckerStrategy.java
+++ b/python/src/com/jetbrains/python/spellchecker/PythonSpellcheckerStrategy.java
@@ -15,6 +15,7 @@
  */
 package com.jetbrains.python.spellchecker;
 
+import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.TextRange;
 import com.intellij.psi.PsiElement;
 import com.intellij.spellchecker.inspections.PlainTextSplitter;
@@ -26,7 +27,6 @@
 import com.jetbrains.python.inspections.PyStringFormatParser;
 import com.jetbrains.python.psi.PyBinaryExpression;
 import com.jetbrains.python.psi.PyStringLiteralExpression;
-import com.jetbrains.python.psi.impl.PyStringLiteralExpressionImpl;
 import org.jetbrains.annotations.NotNull;
 
 import java.util.List;
@@ -41,9 +41,11 @@
       Splitter splitter = PlainTextSplitter.getInstance();
       String text = element.getText();
       if (text.indexOf('\\') >= 0) {
-        MyTextRangeConsumer textRangeConsumer = new MyTextRangeConsumer(element, consumer);
-        ((PyStringLiteralExpressionImpl) element).iterateCharacterRanges(textRangeConsumer);
-        textRangeConsumer.processCurrentToken();   // process last token
+        for (Pair<TextRange, String> fragment : element.getDecodedFragments()) {
+          final String value = fragment.getSecond();
+          consumer.consumeToken(element, value, false, fragment.getFirst().getStartOffset(), TextRange.allOf(value),
+                                PlainTextSplitter.getInstance());
+        }
       }
       else if (text.startsWith("u") || text.startsWith("U") || text.startsWith("r") || text.startsWith("R") ||
           text.startsWith("b") || text.startsWith("B")) {
@@ -57,40 +59,6 @@
         consumer.consumeToken(element, splitter);
       }
     }
-
-    private static class MyTextRangeConsumer implements PyStringLiteralExpressionImpl.TextRangeConsumer {
-      private final StringBuilder myCurrentToken = new StringBuilder();
-      private final PyStringLiteralExpression myElement;
-      private final TokenConsumer myTokenConsumer;
-      private int myTokenStart;
-
-      public MyTextRangeConsumer(PyStringLiteralExpression element, TokenConsumer tokenConsumer) {
-        myElement = element;
-        myTokenConsumer = tokenConsumer;
-      }
-
-      @Override
-      public boolean process(int startOffset, int endOffset, String value) {
-        if (endOffset == startOffset + 1) {
-          if (myCurrentToken.length() == 0) {
-            myTokenStart = startOffset;
-          }
-          myCurrentToken.append(value);
-        }
-        else {
-          if (myCurrentToken.length() > 0) {
-            processCurrentToken();
-            myCurrentToken.setLength(0);
-          }
-        }
-        return true;
-      }
-
-      private void processCurrentToken() {
-        String token = myCurrentToken.toString();
-        myTokenConsumer.consumeToken(myElement, token, false, myTokenStart, TextRange.allOf(token), PlainTextSplitter.getInstance());
-      }
-    }
   }
 
   private static class FormatStringTokenizer extends Tokenizer<PyStringLiteralExpression> {
diff --git a/python/src/com/jetbrains/python/structureView/PyInheritedMembersFilter.java b/python/src/com/jetbrains/python/structureView/PyInheritedMembersFilter.java
index b61edd6..2b5483f 100644
--- a/python/src/com/jetbrains/python/structureView/PyInheritedMembersFilter.java
+++ b/python/src/com/jetbrains/python/structureView/PyInheritedMembersFilter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -64,11 +64,13 @@
                                       AllIcons.Hierarchy.Supertypes);
   }
 
+  @NotNull
   @Override
   public String getCheckBoxText() {
     return IdeBundle.message("file.structure.toggle.show.inherited");
   }
 
+  @NotNull
   @Override
   public Shortcut[] getShortcut() {
     return KeymapManager.getInstance().getActiveKeymap().getShortcuts("FileStructurePopup");
diff --git a/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java b/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java
index 1f58a73..93c44f7 100644
--- a/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java
+++ b/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
 import com.jetbrains.python.PyNames;
 import com.jetbrains.python.psi.*;
 import icons.PythonIcons;
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
@@ -120,6 +121,7 @@
     return name != null ? name.hashCode() : 0;
   }
 
+  @NotNull
   public StructureViewTreeElement[] getChildren() {
     final Collection<StructureViewTreeElement> children = new ArrayList<StructureViewTreeElement>();
     for (PyElement e : getElementChildren(myElement)) {
@@ -220,7 +222,7 @@
     if (element instanceof PyClass || element instanceof PyFunction) {
       return true;
     }
-    if (!(parent instanceof PyClass) && (element instanceof PyTargetExpression) && ((PyTargetExpression)element).getQualifier() == null) {
+    if (!(parent instanceof PyClass) && (element instanceof PyTargetExpression) && !((PyTargetExpression)element).isQualified()) {
       PsiElement e = element.getParent();
       if (e instanceof PyAssignmentStatement) {
         e = e.getParent();
@@ -232,6 +234,7 @@
     return false;
   }
 
+  @NotNull
   @Override
   public ItemPresentation getPresentation() {
     return new ColoredItemPresentation() {
diff --git a/python/src/com/jetbrains/python/structureView/PyStructureViewModel.java b/python/src/com/jetbrains/python/structureView/PyStructureViewModel.java
index 6621792..3e8e4f1 100644
--- a/python/src/com/jetbrains/python/structureView/PyStructureViewModel.java
+++ b/python/src/com/jetbrains/python/structureView/PyStructureViewModel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -66,7 +66,7 @@
   }
 
   @Override
-  public boolean isAutoExpand(StructureViewTreeElement element) {
+  public boolean isAutoExpand(@NotNull StructureViewTreeElement element) {
     return element.getValue() instanceof PsiFile;
   }
 
diff --git a/python/src/com/jetbrains/python/testing/PyTestFrameworkService.java b/python/src/com/jetbrains/python/testing/PyTestFrameworkService.java
new file mode 100644
index 0000000..8eac088
--- /dev/null
+++ b/python/src/com/jetbrains/python/testing/PyTestFrameworkService.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.testing;
+
+import com.intellij.openapi.components.*;
+import com.intellij.util.containers.HashMap;
+import com.intellij.util.xmlb.XmlSerializerUtil;
+
+import java.util.Map;
+
+@State(
+  name = "PyTestFrameworkService",
+  storages = {
+    @Storage(
+      file = StoragePathMacros.APP_CONFIG + "/other.xml"
+    )}
+)
+public class PyTestFrameworkService implements PersistentStateComponent<PyTestFrameworkService> {
+
+  public static PyTestFrameworkService getInstance() {
+    return ServiceManager.getService(PyTestFrameworkService.class);
+  }
+
+  public Map<String, Boolean> SDK_TO_PYTEST = new HashMap<String, Boolean>();
+  public Map <String, Boolean> SDK_TO_NOSETEST = new HashMap<String, Boolean>();
+  public Map <String, Boolean> SDK_TO_ATTEST = new HashMap<String, Boolean>();
+
+  @Override
+  public PyTestFrameworkService getState() {
+    return this;
+  }
+
+  @Override
+  public void loadState(PyTestFrameworkService state) {
+    XmlSerializerUtil.copyBean(state, this);
+  }
+}
diff --git a/python/src/com/jetbrains/python/testing/PythonTestCommandLineStateBase.java b/python/src/com/jetbrains/python/testing/PythonTestCommandLineStateBase.java
index ef16a65..39e4e96 100644
--- a/python/src/com/jetbrains/python/testing/PythonTestCommandLineStateBase.java
+++ b/python/src/com/jetbrains/python/testing/PythonTestCommandLineStateBase.java
@@ -106,9 +106,10 @@
     return cmd;
   }
 
-  private void setWorkingDirectory(@NotNull final GeneralCommandLine cmd) {
-    if (!StringUtil.isEmptyOrSpaces(myConfiguration.getWorkingDirectory())) {
-      cmd.setWorkDirectory(myConfiguration.getWorkingDirectory());
+  protected void setWorkingDirectory(@NotNull final GeneralCommandLine cmd) {
+    final String workingDirectory = myConfiguration.getWorkingDirectory();
+    if (!StringUtil.isEmptyOrSpaces(workingDirectory)) {
+      cmd.setWorkDirectory(workingDirectory);
     }
     else if (myConfiguration instanceof AbstractPythonTestRunConfiguration) {
       final String folderName = ((AbstractPythonTestRunConfiguration)myConfiguration).getFolderName();
diff --git a/python/src/com/jetbrains/python/testing/PythonTestConfigurationProducer.java b/python/src/com/jetbrains/python/testing/PythonTestConfigurationProducer.java
index 3875cd3..8c28e08 100644
--- a/python/src/com/jetbrains/python/testing/PythonTestConfigurationProducer.java
+++ b/python/src/com/jetbrains/python/testing/PythonTestConfigurationProducer.java
@@ -18,6 +18,7 @@
 import com.google.common.collect.Sets;
 import com.intellij.execution.Location;
 import com.intellij.execution.actions.ConfigurationContext;
+import com.intellij.execution.actions.ConfigurationFromContext;
 import com.intellij.execution.actions.RunConfigurationProducer;
 import com.intellij.execution.configurations.ConfigurationFactory;
 import com.intellij.facet.Facet;
@@ -39,6 +40,7 @@
 import com.jetbrains.python.PythonModuleTypeBase;
 import com.jetbrains.python.facet.PythonFacetSettings;
 import com.jetbrains.python.psi.*;
+import com.jetbrains.python.run.PythonRunConfigurationProducer;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -254,4 +256,8 @@
     return PythonUnitTestUtil.getTestCaseClassesFromFile(pyFile);
   }
 
+  @Override
+  public boolean isPreferredConfiguration(ConfigurationFromContext self, ConfigurationFromContext other) {
+    return other.isProducedBy(PythonTestConfigurationProducer.class) || other.isProducedBy(PythonRunConfigurationProducer.class);
+  }
 }
\ No newline at end of file
diff --git a/python/src/com/jetbrains/python/testing/PythonUnitTestTestIdUrlProvider.java b/python/src/com/jetbrains/python/testing/PythonUnitTestTestIdUrlProvider.java
index d0dd2c2..a53828a 100644
--- a/python/src/com/jetbrains/python/testing/PythonUnitTestTestIdUrlProvider.java
+++ b/python/src/com/jetbrains/python/testing/PythonUnitTestTestIdUrlProvider.java
@@ -16,22 +16,12 @@
 package com.jetbrains.python.testing;
 
 import com.intellij.execution.Location;
-import com.intellij.execution.PsiLocation;
-import com.intellij.openapi.progress.ProgressManager;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.psi.PsiFile;
 import com.intellij.testIntegration.TestLocationProvider;
-import com.jetbrains.python.psi.PyClass;
-import com.jetbrains.python.psi.PyFunction;
-import com.jetbrains.python.psi.stubs.PyClassNameIndex;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
 
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -55,55 +45,20 @@
     // parse path as [ns.]*fileName.className[.methodName]
 
     if (listSize == 2) {
-      return findLocations(project, list.get(0), list.get(1), null);
+      return PythonUnitTestUtil.findLocations(project, list.get(0), list.get(1), null);
     }
     if (listSize > 2) {
       final String className = list.get(listSize - 2);
       final String methodName = list.get(listSize - 1);
 
       String fileName = list.get(listSize - 3);
-      final List<Location> locations = findLocations(project, fileName, className, methodName);
+      final List<Location> locations = PythonUnitTestUtil.findLocations(project, fileName, className, methodName);
       if (locations.size() > 0) {
         return locations;
       }
-      return findLocations(project, list.get(listSize-2), list.get(listSize-1), null);
+      return PythonUnitTestUtil.findLocations(project, list.get(listSize-2), list.get(listSize-1), null);
     }
     return Collections.emptyList();
   }
 
-
-  private static List<Location> findLocations(Project project,
-                                              String fileName,
-                                              String className,
-                                              @Nullable String methodName) {
-    if (fileName.indexOf("%") >= 0) {
-      fileName = fileName.substring(0, fileName.lastIndexOf("%"));
-    }
-
-    final List<Location> locations = new ArrayList<Location>();
-    for (PyClass cls : PyClassNameIndex.find(className, project, false)) {
-      ProgressManager.checkCanceled();
-
-      final PsiFile containingFile = cls.getContainingFile();
-      final VirtualFile virtualFile = containingFile.getVirtualFile();
-      final String clsFileName = virtualFile == null? containingFile.getName() : virtualFile.getPath();
-      final String clsFileNameWithoutExt = FileUtil.getNameWithoutExtension(clsFileName);
-      if (!clsFileNameWithoutExt.endsWith(fileName)) {
-        continue;
-      }
-      if (methodName == null) {
-        locations.add(new PsiLocation<PyClass>(project, cls));
-      }
-      else {
-        final PyFunction method = cls.findMethodByName(methodName, true);
-        if (method == null) {
-          continue;
-        }
-
-        locations.add(new PsiLocation<PyFunction>(project, method));
-      }
-    }
-
-    return locations;
-  }
 }
diff --git a/python/src/com/jetbrains/python/testing/PythonUnitTestUtil.java b/python/src/com/jetbrains/python/testing/PythonUnitTestUtil.java
index d0b4ea1..0830897 100644
--- a/python/src/com/jetbrains/python/testing/PythonUnitTestUtil.java
+++ b/python/src/com/jetbrains/python/testing/PythonUnitTestUtil.java
@@ -17,14 +17,25 @@
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import com.intellij.execution.Location;
+import com.intellij.execution.PsiLocation;
 import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiManager;
 import com.intellij.util.containers.Stack;
 import com.jetbrains.python.psi.*;
+import com.jetbrains.python.psi.stubs.PyClassNameIndex;
+import com.jetbrains.python.psi.stubs.PyFunctionNameIndex;
 import com.jetbrains.python.psi.types.PyClassLikeType;
 import com.jetbrains.python.psi.types.TypeEvalContext;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.*;
 import java.util.regex.Pattern;
@@ -93,7 +104,7 @@
       }
     }
     for (PyFunction cls : file.getTopLevelFunctions()) {
-      if (isTestCaseFunction(cls)) {
+      if (isTestCaseFunction(cls, false)) {
         result.add(cls);
       }
     }
@@ -115,9 +126,9 @@
     }
     if (checkAssert) {
       boolean hasAssert = hasAssertOrYield(function.getStatementList());
-      if (!hasAssert) return false;
+      if (hasAssert) return true;
     }
-    return true;
+    return false;
   }
 
   private static boolean hasAssertOrYield(PyStatementList list) {
@@ -148,7 +159,9 @@
           return true;
         }
         String clsName = cls.getQualifiedName();
-        String[] names = clsName.split("\\.");
+        String[] names = new String[0];
+        if (clsName != null)
+          names = clsName.split("\\.");
         clsName = names[names.length - 1];
         if (TEST_MATCH_PATTERN.matcher(clsName).find()) {
           return true;
@@ -157,4 +170,62 @@
     }
     return false;
   }
+
+  public static List<Location> findLocations(@NotNull final Project project,
+                                             @NotNull String fileName,
+                                             @Nullable String className,
+                                             @Nullable String methodName) {
+    if (fileName.contains("%")) {
+      fileName = fileName.substring(0, fileName.lastIndexOf("%"));
+    }
+    final List<Location> locations = new ArrayList<Location>();
+    if (methodName == null && className == null) {
+      final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(fileName);
+      if (virtualFile == null) return locations;
+      final PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
+      if (psiFile != null)
+        locations.add(new PsiLocation<PsiFile>(project, psiFile));
+    }
+
+    if (className != null) {
+      for (PyClass cls : PyClassNameIndex.find(className, project, false)) {
+        ProgressManager.checkCanceled();
+
+        final PsiFile containingFile = cls.getContainingFile();
+        final VirtualFile virtualFile = containingFile.getVirtualFile();
+        final String clsFileName = virtualFile == null? containingFile.getName() : virtualFile.getPath();
+        final String clsFileNameWithoutExt = FileUtil.getNameWithoutExtension(clsFileName);
+        if (!clsFileNameWithoutExt.endsWith(fileName)) {
+          continue;
+        }
+        if (methodName == null) {
+          locations.add(new PsiLocation<PyClass>(project, cls));
+        }
+        else {
+          final PyFunction method = cls.findMethodByName(methodName, true);
+          if (method == null) {
+            continue;
+          }
+
+          locations.add(new PsiLocation<PyFunction>(project, method));
+        }
+      }
+    }
+    else if (methodName != null) {
+      for (PyFunction function : PyFunctionNameIndex.find(methodName, project)) {
+        ProgressManager.checkCanceled();
+        if (function.getContainingClass() == null) {
+          final PsiFile containingFile = function.getContainingFile();
+          final VirtualFile virtualFile = containingFile.getVirtualFile();
+          final String clsFileName = virtualFile == null? containingFile.getName() : virtualFile.getPath();
+          final String clsFileNameWithoutExt = FileUtil.getNameWithoutExtension(clsFileName);
+          if (!clsFileNameWithoutExt.endsWith(fileName)) {
+            continue;
+          }
+          locations.add(new PsiLocation<PyFunction>(project, function));
+        }
+      }
+    }
+    return locations;
+  }
 }
diff --git a/python/src/com/jetbrains/python/testing/TestRunnerService.java b/python/src/com/jetbrains/python/testing/TestRunnerService.java
index 32bcc26..3301909 100644
--- a/python/src/com/jetbrains/python/testing/TestRunnerService.java
+++ b/python/src/com/jetbrains/python/testing/TestRunnerService.java
@@ -34,7 +34,7 @@
 )
 public class TestRunnerService implements PersistentStateComponent<TestRunnerService> {
   private List<String> myConfigurations = new ArrayList<String>();
-  public String PROJECT_TEST_RUNNER = "";
+  public String PROJECT_TEST_RUNNER = PythonTestConfigurationsModel.PYTHONS_UNITTEST_NAME;
 
   public TestRunnerService() {
     myConfigurations.add(PythonTestConfigurationsModel.PYTHONS_UNITTEST_NAME);
diff --git a/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java b/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java
index 7130b1a..69d1ce0 100644
--- a/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java
+++ b/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java
@@ -16,22 +16,22 @@
 package com.jetbrains.python.testing;
 
 import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.components.*;
+import com.intellij.openapi.components.ApplicationComponent;
+import com.intellij.openapi.components.ServiceManager;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.projectRoots.Sdk;
 import com.intellij.openapi.roots.OrderRootType;
 import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.openapi.vfs.VirtualFileManager;
 import com.intellij.openapi.vfs.newvfs.BulkFileListener;
 import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent;
 import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
-import com.intellij.util.containers.HashMap;
 import com.intellij.util.messages.MessageBus;
 import com.intellij.util.ui.update.MergingUpdateQueue;
 import com.intellij.util.ui.update.Update;
-import com.intellij.util.xmlb.XmlSerializerUtil;
 import com.jetbrains.python.PyNames;
 import com.jetbrains.python.packaging.PyExternalProcessException;
 import com.jetbrains.python.packaging.PyPackageManager;
@@ -41,25 +41,17 @@
 import org.jetbrains.annotations.NotNull;
 
 import java.util.List;
-import java.util.Map;
 
 /**
  * User: catherine
  */
-@State(
-  name = "VFSTestFrameworkListener",
-  storages = {
-    @Storage(
-      file = StoragePathMacros.APP_CONFIG + "/other.xml"
-    )}
-)
-public class VFSTestFrameworkListener implements ApplicationComponent, PersistentStateComponent<VFSTestFrameworkListener> {
-
+public class VFSTestFrameworkListener implements ApplicationComponent {
   private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.testing.VFSTestFrameworkListener");
-
   private static final MergingUpdateQueue myQueue = new MergingUpdateQueue("TestFrameworkChecker", 5000, true, null);
+  private PyTestFrameworkService myService;
 
   public VFSTestFrameworkListener() {
+    myService = PyTestFrameworkService.getInstance();
     MessageBus messageBus = ApplicationManager.getApplication().getMessageBus();
     messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener.Adapter() {
       @Override
@@ -76,8 +68,9 @@
             if (PySdkUtil.isRemote(sdk)) {
               continue;
             }
-            for (String root : sdk.getRootProvider().getUrls(OrderRootType.CLASSES)) {
-              if (path.contains(root)) {
+            for (VirtualFile virtualFile : sdk.getRootProvider().getFiles(OrderRootType.CLASSES)) {
+              String root = virtualFile.getCanonicalPath();
+              if (root != null && path.contains(root)) {
                 if (containsNose) {
                   updateTestFrameworks(sdk, PyNames.NOSE_TEST);
                   return;
@@ -152,26 +145,12 @@
     return ServiceManager.getService(VFSTestFrameworkListener.class);
   }
 
-  public Map<String, Boolean> SDK_TO_PYTEST = new HashMap<String, Boolean>();
-  public Map <String, Boolean> SDK_TO_NOSETEST = new HashMap<String, Boolean>();
-  public Map <String, Boolean> SDK_TO_ATTEST = new HashMap<String, Boolean>();
-
-  @Override
-  public VFSTestFrameworkListener getState() {
-    return this;
-  }
-
-  @Override
-  public void loadState(VFSTestFrameworkListener state) {
-    XmlSerializerUtil.copyBean(state, this);
-  }
-
   public void pyTestInstalled(boolean installed, String sdkHome) {
-    SDK_TO_PYTEST.put(sdkHome, installed);
+    myService.SDK_TO_PYTEST.put(sdkHome, installed);
   }
 
   public boolean isPyTestInstalled(final Sdk sdk) {
-    Boolean isInstalled = SDK_TO_PYTEST.get(sdk.getHomePath());
+    Boolean isInstalled = myService.SDK_TO_PYTEST.get(sdk.getHomePath());
     if (isInstalled == null) {
       updateTestFrameworks(sdk, PyNames.PY_TEST);
       return true;
@@ -180,11 +159,11 @@
   }
 
   public void noseTestInstalled(boolean installed, String sdkHome) {
-    SDK_TO_NOSETEST.put(sdkHome, installed);
+    myService.SDK_TO_NOSETEST.put(sdkHome, installed);
   }
 
   public boolean isNoseTestInstalled(final Sdk sdk) {
-    Boolean isInstalled = SDK_TO_NOSETEST.get(sdk.getHomePath());
+    Boolean isInstalled = myService.SDK_TO_NOSETEST.get(sdk.getHomePath());
     if (isInstalled == null) {
       updateTestFrameworks(sdk, PyNames.NOSE_TEST);
       return true;
@@ -193,11 +172,11 @@
   }
 
   public void atTestInstalled(boolean installed, String sdkHome) {
-    SDK_TO_ATTEST.put(sdkHome, installed);
+    myService.SDK_TO_ATTEST.put(sdkHome, installed);
   }
 
   public boolean isAtTestInstalled(final Sdk sdk) {
-    Boolean isInstalled = SDK_TO_ATTEST.get(sdk.getHomePath());
+    Boolean isInstalled = myService.SDK_TO_ATTEST.get(sdk.getHomePath());
     if (isInstalled == null) {
       updateTestFrameworks(sdk, PyNames.AT_TEST);
       return true;
diff --git a/python/src/com/jetbrains/python/testing/doctest/PythonDocTestConfigurationProducer.java b/python/src/com/jetbrains/python/testing/doctest/PythonDocTestConfigurationProducer.java
index cfa2be7..e3eef29 100644
--- a/python/src/com/jetbrains/python/testing/doctest/PythonDocTestConfigurationProducer.java
+++ b/python/src/com/jetbrains/python/testing/doctest/PythonDocTestConfigurationProducer.java
@@ -21,6 +21,8 @@
 
 import com.intellij.execution.Location;
 import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiFile;
 import com.intellij.psi.PsiRecursiveElementVisitor;
@@ -85,4 +87,10 @@
       }
     }
   }
+
+  @Override
+  protected boolean isTestFolder(@NotNull VirtualFile virtualFile, @NotNull Project project) {
+    return false;
+  }
+
 }
\ No newline at end of file
diff --git a/python/src/com/jetbrains/python/testing/nosetest/PythonNoseTestConfigurationProducer.java b/python/src/com/jetbrains/python/testing/nosetest/PythonNoseTestConfigurationProducer.java
index 5cf9784..b7dc704 100644
--- a/python/src/com/jetbrains/python/testing/nosetest/PythonNoseTestConfigurationProducer.java
+++ b/python/src/com/jetbrains/python/testing/nosetest/PythonNoseTestConfigurationProducer.java
@@ -47,6 +47,6 @@
 
   @Override
   protected boolean isTestFunction(@NotNull final PyFunction pyFunction, @Nullable final AbstractPythonTestRunConfiguration configuration) {
-    return PythonUnitTestUtil.isTestCaseFunction(pyFunction, false);
+    return PythonUnitTestUtil.isTestCaseFunction(pyFunction, true);
   }
 }
\ No newline at end of file
diff --git a/python/src/com/jetbrains/python/testing/nosetest/PythonNoseTestUrlProvider.java b/python/src/com/jetbrains/python/testing/nosetest/PythonNoseTestUrlProvider.java
new file mode 100644
index 0000000..2c7787c
--- /dev/null
+++ b/python/src/com/jetbrains/python/testing/nosetest/PythonNoseTestUrlProvider.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.testing.nosetest;
+
+import com.intellij.execution.Location;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.testIntegration.TestLocationProvider;
+import com.jetbrains.python.testing.PythonUnitTestUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+import java.util.List;
+
+public class PythonNoseTestUrlProvider implements TestLocationProvider {
+  @NonNls
+  private static final String PROTOCOL_ID = "python_nosetestid";
+
+  @NotNull
+  public List<Location> getLocation(@NotNull final String protocolId, @NotNull final String path,
+                                    final Project project) {
+    if (!PROTOCOL_ID.equals(protocolId)) {
+      return Collections.emptyList();
+    }
+
+    final List<String> list = StringUtil.split(path, ".");
+    if (list.isEmpty()) {
+      return Collections.emptyList();
+    }
+    final int listSize = list.size();
+
+    // parse path as [ns.]*fileName[.className][.methodName]
+    if (listSize == 2) {
+    final List<Location> classes = PythonUnitTestUtil.findLocations(project, list.get(0), list.get(1), null);
+    if (classes.size() > 0)
+      return classes;
+    final List<Location> functions = PythonUnitTestUtil.findLocations(project, list.get(0), null, list.get(1));
+    if (functions.size() > 0)
+      return functions;
+    }
+    
+    if (listSize > 2) {
+      final String className = list.get(listSize - 2);
+      final String methodName = list.get(listSize - 1);
+
+      String fileName = list.get(listSize - 3);
+      List<Location> locations = PythonUnitTestUtil.findLocations(project, fileName, className, methodName);
+      if (locations.size() > 0) {
+        return locations;
+      }
+      locations = PythonUnitTestUtil.findLocations(project, list.get(listSize - 2), list.get(listSize - 1), null);
+      if (locations.size() > 0) {
+        return locations;
+      }
+      final List<Location> functions = PythonUnitTestUtil.findLocations(project, list.get(listSize - 2), null, list.get(listSize - 1));
+      if (functions.size() > 0)
+        return functions;
+    }
+    return PythonUnitTestUtil.findLocations(project, project.getBasePath() + "/" + StringUtil.join(list, "/") + ".py", null, null);
+  }
+}
diff --git a/python/src/com/jetbrains/python/testing/pytest/PyTestRunnableScriptFilter.java b/python/src/com/jetbrains/python/testing/pytest/PyTestRunnableScriptFilter.java
deleted file mode 100644
index 036663b..0000000
--- a/python/src/com/jetbrains/python/testing/pytest/PyTestRunnableScriptFilter.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2000-2013 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jetbrains.python.testing.pytest;
-
-import com.intellij.execution.Location;
-import com.intellij.openapi.module.Module;
-import com.intellij.openapi.projectRoots.Sdk;
-import com.intellij.psi.PsiFile;
-import com.jetbrains.python.psi.PyClass;
-import com.jetbrains.python.psi.PyFile;
-import com.jetbrains.python.psi.PyFunction;
-import com.jetbrains.python.psi.PyRecursiveElementVisitor;
-import com.jetbrains.python.run.RunnableScriptFilter;
-import com.jetbrains.python.sdk.PythonSdkType;
-import com.jetbrains.python.testing.PythonTestConfigurationsModel;
-import com.jetbrains.python.testing.TestRunnerService;
-import com.jetbrains.python.testing.VFSTestFrameworkListener;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * @author yole
- */
-public class PyTestRunnableScriptFilter implements RunnableScriptFilter {
-  public boolean isRunnableScript(PsiFile script, @NotNull Module module, Location location) {
-    return isPyTestInstalled(module) && isPyTestScript(script) &&
-      TestRunnerService.getInstance(module).getProjectConfiguration().
-        equals(PythonTestConfigurationsModel.PY_TEST_NAME);
-  }
-
-  private static boolean isPyTestInstalled(Module module) {
-    // TODO[yole] add caching to avoid disk I/O in findPyTestRunner()?
-    final Sdk sdk = PythonSdkType.findPythonSdk(module);
-    return sdk != null && VFSTestFrameworkListener.getInstance().isPyTestInstalled(sdk);
-  }
-
-  public static boolean isPyTestScript(PsiFile script) {
-    if (!(script instanceof PyFile)) {
-      return false;
-    }
-    PyTestVisitor testVisitor = new PyTestVisitor();
-    script.accept(testVisitor);
-    return testVisitor.isTestsFound();
-  }
-
-  private static class PyTestVisitor extends PyRecursiveElementVisitor {
-    private boolean myTestsFound = false;
-
-    public boolean isTestsFound() {
-      return myTestsFound;
-    }
-
-    @Override
-    public void visitPyFunction(PyFunction node) {
-      super.visitPyFunction(node);
-      String name = node.getName();
-      if (name != null && name.startsWith("test")) {
-        myTestsFound = true;
-      }
-    }
-
-    @Override
-    public void visitPyClass(PyClass node) {
-      super.visitPyClass(node);
-      String name = node.getName();
-      if (name != null && name.startsWith("Test")) {
-        myTestsFound = true;
-      }
-    }
-  }
-}
diff --git a/python/src/com/jetbrains/python/validation/CompatibilityVisitor.java b/python/src/com/jetbrains/python/validation/CompatibilityVisitor.java
index d63d8c7..f48af7f 100644
--- a/python/src/com/jetbrains/python/validation/CompatibilityVisitor.java
+++ b/python/src/com/jetbrains/python/validation/CompatibilityVisitor.java
@@ -51,6 +51,7 @@
     AVAILABLE_PREFIXES.put(LanguageLevel.PYTHON31, Sets.newHashSet("R", "B", "BR"));
     AVAILABLE_PREFIXES.put(LanguageLevel.PYTHON32, Sets.newHashSet("R", "B", "BR"));
     AVAILABLE_PREFIXES.put(LanguageLevel.PYTHON33, Sets.newHashSet("R", "U", "B", "BR", "RB"));
+    AVAILABLE_PREFIXES.put(LanguageLevel.PYTHON34, Sets.newHashSet("R", "U", "B", "BR", "RB"));
   }
 
   public CompatibilityVisitor(List<LanguageLevel> versionsToProcess) {
diff --git a/python/src/com/jetbrains/python/validation/HighlightingAnnotator.java b/python/src/com/jetbrains/python/validation/HighlightingAnnotator.java
index 728a6e1..cc6d445 100644
--- a/python/src/com/jetbrains/python/validation/HighlightingAnnotator.java
+++ b/python/src/com/jetbrains/python/validation/HighlightingAnnotator.java
@@ -37,7 +37,7 @@
   @Override
   public void visitPyReferenceExpression(PyReferenceExpression node) {
     final String referencedName = node.getReferencedName();
-    if (node.getQualifier() == null && referencedName != null) {
+    if (!node.isQualified() && referencedName != null) {
       PyFunction function = PsiTreeUtil.getParentOfType(node, PyFunction.class);
       if (function != null) {
         final PyNamedParameter element = function.getParameterList().findParameterByName(referencedName);
diff --git a/python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java b/python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java
index 6292d8e..c115ba4 100644
--- a/python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java
+++ b/python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java
@@ -52,6 +52,7 @@
 import com.jetbrains.python.codeInsight.imports.OptimizeImportsQuickFix;
 import com.jetbrains.python.inspections.PyPep8Inspection;
 import com.jetbrains.python.inspections.quickfix.ReformatFix;
+import com.jetbrains.python.quickFixes.RemoveTrailingBlankLinesFix;
 import com.jetbrains.python.sdk.PreferredSdkComparator;
 import com.jetbrains.python.sdk.PySdkUtil;
 import com.jetbrains.python.sdk.PythonSdkType;
@@ -253,6 +254,9 @@
         if (problem.myCode.equals("E401")) {
           annotation.registerUniversalFix(new OptimizeImportsQuickFix(), null, null);
         }
+        else if (problem.myCode.equals("W391")) {
+          annotation.registerUniversalFix(new RemoveTrailingBlankLinesFix(), null, null);
+        }
         else {
           annotation.registerUniversalFix(new ReformatFix(), null, null);
         }
diff --git a/python/src/com/jetbrains/python/validation/PyBuiltinAnnotator.java b/python/src/com/jetbrains/python/validation/PyBuiltinAnnotator.java
index 7e69319..417c362 100644
--- a/python/src/com/jetbrains/python/validation/PyBuiltinAnnotator.java
+++ b/python/src/com/jetbrains/python/validation/PyBuiltinAnnotator.java
@@ -36,7 +36,7 @@
     final String name = node.getName();
     if (name == null) return; 
     boolean highlighted_as_attribute = highlightAsAttribute(node, name);
-    if (! highlighted_as_attribute && node.getQualifier() == null) {
+    if (! highlighted_as_attribute && !node.isQualified()) {
       // things like len()
       ResolveResult[] resolved = node.getReference().multiResolve(false); // constructors, etc may give multiple results...
       if (resolved.length > 0) {
@@ -71,7 +71,7 @@
     if (PyNames.UnderscoredAttributes.contains(name) || PyNames.getBuiltinMethods(languageLevel).containsKey(name)) {
       // things like __len__
       if (
-        (node.getQualifier() != null) // foo.__len__
+        node.isQualified() // foo.__len__
         || (PyUtil.getConcealingParent(node) instanceof PyClass) // class Foo: ... __len__ = myLenImpl
       ) {
         final ASTNode astNode = node.getNode();
diff --git a/python/src/com/jetbrains/python/validation/PyMultiplePsiFilesVisitorFilter.java b/python/src/com/jetbrains/python/validation/PyMultiplePsiFilesVisitorFilter.java
new file mode 100644
index 0000000..9b6e6b7
--- /dev/null
+++ b/python/src/com/jetbrains/python/validation/PyMultiplePsiFilesVisitorFilter.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.validation;
+
+import com.intellij.psi.MultiplePsiFilesPerDocumentFileViewProvider;
+import com.intellij.psi.PsiFile;
+import com.jetbrains.python.inspections.PythonVisitorFilter;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author vlan
+ */
+public class PyMultiplePsiFilesVisitorFilter implements PythonVisitorFilter {
+  @Override
+  public boolean isSupported(@NotNull Class visitorClass, @NotNull PsiFile file) {
+    if (visitorClass == StringLiteralQuotesAnnotator.class &&
+        file.getViewProvider() instanceof MultiplePsiFilesPerDocumentFileViewProvider) {
+      return false;
+    }
+    return true;
+  }
+}
diff --git a/python/src/com/jetbrains/python/validation/UnsupportedFeaturesUtil.java b/python/src/com/jetbrains/python/validation/UnsupportedFeaturesUtil.java
index d8e4cae..2945de4 100644
--- a/python/src/com/jetbrains/python/validation/UnsupportedFeaturesUtil.java
+++ b/python/src/com/jetbrains/python/validation/UnsupportedFeaturesUtil.java
@@ -82,6 +82,7 @@
     ALL_LANGUAGE_LEVELS.add(LanguageLevel.PYTHON31.toString());
     ALL_LANGUAGE_LEVELS.add(LanguageLevel.PYTHON32.toString());
     ALL_LANGUAGE_LEVELS.add(LanguageLevel.PYTHON33.toString());
+    ALL_LANGUAGE_LEVELS.add(LanguageLevel.PYTHON34.toString());
   }
 
   private static void fillMaps() throws IOException {
diff --git a/python/src/com/jetbrains/python/vp/Creator.java b/python/src/com/jetbrains/python/vp/Creator.java
new file mode 100644
index 0000000..3d5bffe
--- /dev/null
+++ b/python/src/com/jetbrains/python/vp/Creator.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.vp;
+
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Creates view and presenter allowing them to have links to each other.
+ * Implement it and pass to {@link com.jetbrains.python.vp.ViewPresenterUtils#linkViewWithPresenterAndLaunch(Class, Class, Creator)}
+ *
+ * @author Ilya.Kazakevich
+ * @param <V> view interface
+ * @param <P> presenter interface
+ */
+public interface Creator<V, P extends Presenter> {
+
+  /**
+   * Create presenter
+   *
+   * @param view for that presenter
+   * @return presenter
+   */
+  @NotNull
+  P createPresenter(@NotNull V view);
+
+  /**
+   * Creates view
+   *
+   * @param presenter for this view
+   * @return view
+   */
+  @NotNull
+  V createView(@NotNull P presenter);
+
+}
diff --git a/python/src/com/jetbrains/python/remote/PyRemoteSdkData.java b/python/src/com/jetbrains/python/vp/Presenter.java
similarity index 64%
copy from python/src/com/jetbrains/python/remote/PyRemoteSdkData.java
copy to python/src/com/jetbrains/python/vp/Presenter.java
index ea2f8df..251aff6 100644
--- a/python/src/com/jetbrains/python/remote/PyRemoteSdkData.java
+++ b/python/src/com/jetbrains/python/vp/Presenter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,13 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.jetbrains.python.remote;
+package com.jetbrains.python.vp;
 
-import com.intellij.remotesdk.RemoteSdkData;
 
 /**
- * @author yole
+ * Interface each presenter should implement
+ * @author Ilya.Kazakevich
  */
-public interface PyRemoteSdkData extends RemoteSdkData {
-  String getSkeletonsPath();
+public interface Presenter {
+  /**
+   * Launches dialog. Presenter should fetch data and start view.
+   * TODO: Say you run initand show and launch
+   */
+  void launch();
 }
diff --git a/python/src/com/jetbrains/python/vp/PresenterHandler.java b/python/src/com/jetbrains/python/vp/PresenterHandler.java
new file mode 100644
index 0000000..e610604
--- /dev/null
+++ b/python/src/com/jetbrains/python/vp/PresenterHandler.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.vp;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+/**
+ * Wrapper for presenter.
+ * @author Ilya.Kazakevich
+ * @param <C> presenter class
+ */
+class PresenterHandler<C> implements InvocationHandler {
+  /**
+   * Presenter, created by user with {@link com.jetbrains.python.vp.Creator#createPresenter(Object)}
+   */
+  private C realPresenter;
+
+  void setRealPresenter(@NotNull C realPresenter) {
+    this.realPresenter = realPresenter;
+  }
+
+  @Override
+  public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
+    /**
+     * TODO: Implement async call.
+     * The idea is void methods marked with @Async should be called in background thread.
+     * That will allow presenter to be agnostic about EDT
+     */
+    return method.invoke(realPresenter, args);
+  }
+}
diff --git a/python/src/com/jetbrains/python/vp/ViewHandler.java b/python/src/com/jetbrains/python/vp/ViewHandler.java
new file mode 100644
index 0000000..84e9963
--- /dev/null
+++ b/python/src/com/jetbrains/python/vp/ViewHandler.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.vp;
+
+import com.google.common.base.Preconditions;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Wrapper for view.
+ * @author Ilya.Kazakevich
+ * @param <C> view class
+ */
+class ViewHandler<C> implements InvocationHandler {
+  /**
+   * Real view, created by user using {@link com.jetbrains.python.vp.Creator#createView(Presenter)}
+   */
+  private C realView;
+
+  public void setRealView(@NotNull C realView) {
+    this.realView = realView;
+  }
+
+  @Override
+  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+    Preconditions.checkState(realView != null, "Real view not set");
+    Invoker invoker = new Invoker(realView, method, args);
+    ApplicationManager.getApplication().invokeAndWait(invoker, ModalityState.defaultModalityState());
+    if (invoker.exception != null) {
+      throw invoker.exception;
+    }
+    return invoker.result;
+  }
+
+  /**
+   * Class that invokes view methods in appropriate thread
+   */
+  private static class Invoker implements Runnable {
+    @NotNull
+    private final Method method;
+    @Nullable
+    private final Object[] args;
+    @NotNull
+    private final Object target;
+
+    private InvocationTargetException exception;
+    private Object result;
+
+    private Invoker(@NotNull Object target, @NotNull Method method, @Nullable Object[] args) {
+      this.target = target;
+      this.method = method;
+      this.args = args;
+    }
+
+    @Override
+    public void run() {
+      try {
+        result = method.invoke(target, args);
+      }
+      catch (IllegalAccessException e) {
+        throw new IllegalStateException("Method is unaccessible: " + method, e);
+      }
+      catch (InvocationTargetException e) {
+        exception = e;
+      }
+    }
+  }
+}
diff --git a/python/src/com/jetbrains/python/vp/ViewPresenterUtils.java b/python/src/com/jetbrains/python/vp/ViewPresenterUtils.java
new file mode 100644
index 0000000..cfd96779
--- /dev/null
+++ b/python/src/com/jetbrains/python/vp/ViewPresenterUtils.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.vp;
+
+
+import com.google.common.base.Preconditions;
+import org.jetbrains.annotations.NotNull;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+
+/**
+ * Entry point to package. Use {@link #linkViewWithPresenterAndLaunch(Class, Class, Creator)}
+ * @author Ilya.Kazakevich
+ */
+public final class ViewPresenterUtils {
+  private ViewPresenterUtils() {
+  }
+
+  /**
+   * TODO: Write about do not call anything in constructor
+   * Creates link between view and presenter and launches them using {@link Presenter#launch()}. Be sure to read package info first.
+   *
+   * @param presenterInterface presenter interface
+   * @param viewInterface      view interface
+   * @param creator            class that handles presenter and view instances actual creation
+   * @param <V>                view interface
+   * @param <P>                presenter interface
+   */
+  public static <V, P extends Presenter> void linkViewWithPresenterAndLaunch(@NotNull Class<P> presenterInterface,
+                                                                             @NotNull Class<V> viewInterface,
+                                                                             @NotNull Creator<V, P> creator) {
+    Preconditions.checkArgument(presenterInterface.isInterface(), "Presenter is not interface");
+    Preconditions.checkArgument(viewInterface.isInterface(), "View is not interface");
+
+    //TODO: Use cglib?
+    PresenterHandler<P> presenterHandler = new PresenterHandler<P>();
+    ViewHandler<V> viewHandler = new ViewHandler<V>();
+    V viewProxy = createProxy(viewInterface, viewHandler);
+    P presenterProxy = createProxy(presenterInterface, presenterHandler);
+
+    V realView = creator.createView(presenterProxy);
+    viewHandler.setRealView(realView);
+    P realPresenter = creator.createPresenter(viewProxy);
+    presenterHandler.setRealPresenter(realPresenter);
+    realPresenter.launch();
+  }
+
+
+  @SuppressWarnings("unchecked") //Proxy always returns correct class
+  private static <C> C createProxy(Class<C> clazz, InvocationHandler handler) {
+    assert clazz != null;
+    assert handler != null;
+    return (C)Proxy.newProxyInstance(ViewPresenterUtils.class.getClassLoader(), new Class[]{clazz}, handler);
+  }
+}
diff --git a/python/src/com/jetbrains/python/vp/package-info.java b/python/src/com/jetbrains/python/vp/package-info.java
new file mode 100644
index 0000000..834ca29
--- /dev/null
+++ b/python/src/com/jetbrains/python/vp/package-info.java
@@ -0,0 +1,26 @@
+/**
+ * <h1>Model-view presenter package</h1>
+ * <h2>How to use?</h2>
+ * <p>
+ *   <a href="http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter">MVP</a> pattern implementation with only view and presenter for now.
+ * <dl>
+ *  <dt>Presenter</dt>
+ *  <dd>Handles all business logic and has <strong>no</strong> references to awt/swing, so it is 100% testable</dd>
+ *  <dt>View</dt>
+ *  <dd>Handles only view: it may import any swing/awt packages but should contain almost no logic, because it is untestable.</dd>
+ *  </dl>
+ *  One implements <strong>Presenter</strong> and <strong>View</strong>. Both may have links to each other.
+ *  You run {@link com.jetbrains.python.vp.ViewPresenterUtils#linkViewWithPresenterAndLaunch(Class, Class, Creator)} to link and launch them.
+ *  See its javadoc
+ * </p>
+ * <h2>Threading issues</h2>
+ *
+ * <p>
+ *   Presenter and View should be thread-agnostic.
+ *   Any call to <strong>view</strong> is invoked in EDT automatically. <br/>
+ *   Call to <strong>presenter</strong> may be invoked in background (not implemented yet, see {@link com.jetbrains.python.vp.PresenterHandler})
+ * </p>
+ *
+ * @author Ilya.Kazakevich
+ */
+package com.jetbrains.python.vp;
\ No newline at end of file