Snapshot idea/138.1696 from git://git.jetbrains.org/idea/community.git

Change-Id: I50c97b83a815ce635e49a38380ba5b8765e4b16a
diff --git a/python/edu/build/build.xml b/python/edu/build/build.xml
deleted file mode 100644
index 913237c..0000000
--- a/python/edu/build/build.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<project name="PyCharm Educational Edition" default="all">
-  <property name="project.home" value="${basedir}/../../.."/>
-  <property name="python.home" value="${basedir}"/>
-  <property name="out.dir" value="${project.home}/out"/>
-  <property name="tmp.dir" value="${project.home}/out/tmp"/>
-
-  <target name="cleanup">
-    <delete dir="${out.dir}" failonerror="false"/>
-  </target>
-
-  <target name="init">
-    <mkdir dir="${out.dir}"/>
-    <mkdir dir="${tmp.dir}"/>
-  </target>
-
-  <macrodef name="call_gant">
-    <attribute name="script" />
-    <sequential>
-      <java failonerror="true" jar="${project.home}/lib/ant/lib/ant-launcher.jar" fork="true">
-        <jvmarg line="-Xmx612m -XX:MaxPermSize=152m -Didea.build.number=${idea.build.number} &quot;-DideaPath=${idea.path}&quot;"/>
-        <sysproperty key="java.awt.headless" value="true"/>
-        <arg line="&quot;-Dgant.script=@{script}&quot;"/>
-        <arg line="&quot;-Dteamcity.build.tempDir=${tmp.dir}&quot;"/>
-        <arg line="&quot;-Didea.build.number=${idea.build.number}&quot;"/>
-        <arg line="&quot;-Didea.test.group=ALL_EXCLUDE_DEFINED&quot;"/>
-        <arg value="-f"/>
-        <arg value="${project.home}/build/gant.xml"/>
-      </java>
-    </sequential>
-  </macrodef>
-
-  <target name="build" depends="init">
-    <call_gant script="${python.home}/pycharm_edu_build.gant"/>
-  </target>
-
-  <!--<target name="plugin" depends="init">-->
-    <!--<call_gant script="${python.home}/build/python_plugin_build.gant"/>-->
-  <!--</target>-->
-  <!--
-  <target name="test" depends="init">
-    <call_gant script="${project.home}/build/scripts/tests.gant"/>
-  </target>
-  -->
-
-  <target name="all" depends="cleanup,build"/>
-</project>
diff --git a/python/edu/build/desktop.ini b/python/edu/build/desktop.ini
new file mode 100644
index 0000000..f56d43c
--- /dev/null
+++ b/python/edu/build/desktop.ini
@@ -0,0 +1,100 @@
+[Settings]
+NumFields=6
+
+[Field 1]
+Type=checkbox
+Left=5
+Right=100
+Top=10
+Bottom=20
+State=0
+
+[Field 2]
+Type=checkbox
+Left=120
+Right=-1
+Top=10
+Bottom=20
+State=0
+
+[Field 3]
+Type=GroupBox
+Left=1
+Right=-1
+Top=35
+Bottom=65
+Text=Choice Python version
+
+[Field 4]
+Type=RadioButton
+Left=5
+Right=45
+Top=50
+Bottom=60
+State=1
+Text=Python 2
+
+[Field 5]
+Type=RadioButton
+Left=95
+Right=135
+Top=50
+Bottom=60
+State=0
+Text=Python 3
+
+[Field 6]
+Type=GroupBox
+Left=1
+Right=-1
+Top=75
+Bottom=105
+Text=Create Associations
+
+[Field 7]
+Type=checkbox
+Left=5
+Right=45
+Top=90
+Bottom=100
+State=0
+
+[Field 8]
+Type=checkbox
+Left=50
+Right=90
+Top=90
+Bottom=100
+State=0
+
+[Field 9]
+Type=checkbox
+Left=95
+Right=135
+Top=90
+Bottom=100
+State=0
+
+[Field 10]
+Type=checkbox
+Left=140
+Right=180
+Top=90
+Bottom=100
+State=0
+
+[Field 11]
+Type=checkbox
+Left=185
+Right=225
+Top=90
+Bottom=100
+State=0
+
+[Field 12]
+Type=checkbox
+Left=230
+Right=270
+Top=90
+Bottom=100
+State=0
diff --git a/python/edu/build/idea.nsi b/python/edu/build/idea.nsi
new file mode 100644
index 0000000..d9903c9
--- /dev/null
+++ b/python/edu/build/idea.nsi
@@ -0,0 +1,1228 @@
+!verbose 2
+
+!include "paths.nsi"
+!include "strings.nsi"
+!include "Registry.nsi"
+!include "version.nsi"
+
+; Product with version (IntelliJ IDEA #xxxx).
+
+; Used in registry to put each build info into the separate subkey
+; Add&Remove programs doesn't understand subkeys in the Uninstall key,
+; thus ${PRODUCT_WITH_VER} is used for uninstall registry information
+!define PRODUCT_REG_VER "${MUI_PRODUCT}\${VER_BUILD}"
+
+!define INSTALL_OPTION_ELEMENTS 7
+Name "${MUI_PRODUCT}"
+SetCompressor lzma
+; http://nsis.sourceforge.net/Shortcuts_removal_fails_on_Windows_Vista
+RequestExecutionLevel user
+
+;------------------------------------------------------------------------------
+; include "Modern User Interface"
+;------------------------------------------------------------------------------
+!include "MUI2.nsh"
+!include "FileFunc.nsh"
+!include UAC.nsh
+!include "InstallOptions.nsh"
+!include StrFunc.nsh
+!include LogicLib.nsh
+
+${UnStrStr}
+${UnStrLoc}
+${UnStrRep}
+${StrRep}
+
+ReserveFile "desktop.ini"
+ReserveFile "DeleteSettings.ini"
+ReserveFile '${NSISDIR}\Plugins\InstallOptions.dll'
+!insertmacro MUI_RESERVEFILE_LANGDLL
+
+!define MUI_ICON "${IMAGES_LOCATION}\${PRODUCT_ICON_FILE}"
+!define MUI_UNICON "${IMAGES_LOCATION}\${PRODUCT_UNINST_ICON_FILE}"
+
+!define MUI_HEADERIMAGE
+!define MUI_HEADERIMAGE_BITMAP "${IMAGES_LOCATION}\${PRODUCT_HEADER_FILE}"
+!define MUI_WELCOMEFINISHPAGE_BITMAP "${IMAGES_LOCATION}\${PRODUCT_LOGO_FILE}"
+
+;------------------------------------------------------------------------------
+; on GUI initialization installer checks whether IDEA is already installed
+;------------------------------------------------------------------------------
+
+!define MUI_CUSTOMFUNCTION_GUIINIT GUIInit
+
+Var baseRegKey
+Var IS_UPGRADE_60
+
+!define MUI_LANGDLL_REGISTRY_ROOT "HKCU" 
+!define MUI_LANGDLL_REGISTRY_KEY "Software\JetBrains\${MUI_PRODUCT}\${VER_BUILD}\" 
+!define MUI_LANGDLL_REGISTRY_VALUENAME "Installer Language"
+
+;check if the window is win7 or newer
+!macro INST_UNINST_SWITCH un
+  Function ${un}winVersion
+    ;The platform is returned into $0, minor version into $1.
+    ;Windows 7 is equals values of 6 as platform and 1 as minor version.
+    ;Windows 8 is equals values of 6 as platform and 2 as minor version.
+    nsisos::osversion
+    ${If} $0 == "6"
+      ${AndIf} $1 >= "1"
+      StrCpy $0 "1"
+    ${else}
+      StrCpy $0 "0"
+    ${EndIf}
+  FunctionEnd
+
+  Function ${un}compareFileInstallationTime
+    StrCpy $9 ""
+  get_first_file:
+    Pop $7
+    IfFileExists "$7" get_next_file 0
+      StrCmp $7 "Complete" complete get_first_file
+  get_next_file:
+    Pop $8
+    StrCmp $8 "Complete" 0 +2
+      ; check if there is only one property file
+      StrCmp $9 "no changes" complete different
+    IfFileExists "$8" 0 get_next_file
+    ClearErrors
+    ${GetTime} "$7" "M" $0 $1 $2 $3 $4 $5 $6
+    ${GetTime} "$8" "M" $R0 $R1 $R2 $R3 $R4 $R5 $R6
+    StrCmp $0 $R0 0 different
+      StrCmp $1 $R1 0 different
+        StrCmp $2 $R2 0 different
+          StrCmp $4 $R4 0 different
+            StrCmp $5 $R5 0 different
+              StrCmp $6 $R6 0 different
+		StrCpy $9 "no changes"
+		Goto get_next_file
+  different:
+    StrCpy $9 "Modified"
+  complete:
+FunctionEnd
+
+Function ${un}SplitStr
+Exch $0 ; str
+Push $1 ; inQ
+Push $3 ; idx
+Push $4 ; tmp
+StrCpy $1 0
+StrCpy $3 0
+loop:
+    StrCpy $4 $0 1 $3
+    ${If} $4 == '"'
+        ${If} $1 <> 0
+            StrCpy $0 $0 "" 1
+            IntOp $3 $3 - 1
+        ${EndIf}
+        IntOp $1 $1 !
+    ${EndIf}
+    ${If} $4 == '' ; The end?
+        StrCpy $1 0
+        StrCpy $4 ','
+    ${EndIf}
+    ${If} $4 == ','
+    ${AndIf} $1 = 0
+        StrCpy $4 $0 $3
+        StrCpy $1 $4 "" -1
+        ${IfThen} $1 == '"' ${|} StrCpy $4 $4 -1 ${|}
+        killspace:
+            IntOp $3 $3 + 1
+            StrCpy $0 $0 "" $3
+            StrCpy $1 $0 1
+            StrCpy $3 0
+            StrCmp $1 ',' killspace
+        Push $0 ; Remaining
+        Exch 4
+        Pop $0
+        StrCmp $4 "" 0 moreleft
+            Pop $4
+            Pop $3
+            Pop $1
+            Return
+        moreleft:
+        Exch $4
+        Exch 2
+        Pop $1
+        Pop $3
+        Return
+    ${EndIf}
+    IntOp $3 $3 + 1
+    Goto loop
+FunctionEnd
+
+!macroend
+!insertmacro INST_UNINST_SWITCH ""
+!insertmacro INST_UNINST_SWITCH "un."
+
+Function InstDirState
+	!define InstDirState `!insertmacro InstDirStateCall`
+
+	!macro InstDirStateCall _PATH _RESULT
+		Push `${_PATH}`
+		Call InstDirState
+		Pop ${_RESULT}
+	!macroend
+
+	Exch $0
+	Push $1
+	ClearErrors
+
+	FindFirst $1 $0 '$0\*.*'
+	IfErrors 0 +3
+	StrCpy $0 -1
+	goto end
+	StrCmp $0 '.' 0 +4
+	FindNext $1 $0
+	StrCmp $0 '..' 0 +2
+	FindNext $1 $0
+	FindClose $1
+	IfErrors 0 +3
+	StrCpy $0 0
+	goto end
+	StrCpy $0 1
+
+	end:
+	Pop $1
+	Exch $0
+FunctionEnd
+
+Function SplitFirstStrPart
+  Exch $R0
+  Exch
+  Exch $R1
+  Push $R2
+  Push $R3
+  StrCpy $R3 $R1
+  StrLen $R1 $R0
+  IntOp $R1 $R1 + 1
+  loop:
+    IntOp $R1 $R1 - 1
+    StrCpy $R2 $R0 1 -$R1
+    StrCmp $R1 0 exit0
+    StrCmp $R2 $R3 exit1 loop
+  exit0:
+  StrCpy $R1 ""
+  Goto exit2
+  exit1:
+    IntOp $R1 $R1 - 1
+    StrCmp $R1 0 0 +3
+     StrCpy $R2 ""
+     Goto +2
+    StrCpy $R2 $R0 "" -$R1
+    IntOp $R1 $R1 + 1
+    StrCpy $R0 $R0 -$R1
+    StrCpy $R1 $R2
+  exit2:
+  Pop $R3
+  Pop $R2
+  Exch $R1 ;rest
+  Exch
+  Exch $R0 ;first
+FunctionEnd
+
+Function VersionSplit
+    !define VersionSplit `!insertmacro VersionSplitCall`
+
+    !macro VersionSplitCall _FULL _PRODUCT _BRANCH _BUILD
+	Push `${_FULL}`
+	Call VersionSplit
+	Pop ${_PRODUCT}
+	Pop ${_BRANCH}
+	Pop ${_BUILD}
+    !macroend
+
+    Pop $R0
+    Push "-"
+    Push $R0
+    Call SplitFirstStrPart
+    Pop $R0
+    Pop $R1
+    Push "."
+    Push $R1
+    Call SplitFirstStrPart
+    Push $R0
+FunctionEnd
+
+Function OnDirectoryPageLeave
+    StrCpy $IS_UPGRADE_60 "0"
+    ${InstDirState} "$INSTDIR" $R0
+    IntCmp $R0 1 check_build skip_abort skip_abort
+check_build:
+    FileOpen $R1 "$INSTDIR\build.txt" "r"
+    IfErrors do_abort
+    FileRead $R1 $R2
+    FileClose $R1
+    IfErrors do_abort
+    ${VersionSplit} ${MIN_UPGRADE_BUILD} $R3 $R4 $R5
+    ${VersionSplit} ${MAX_UPGRADE_BUILD} $R6 $R7 $R8
+    ${VersionSplit} $R2 $R9 $R2 $R0
+    StrCmp $R9 $R3 0 do_abort
+    IntCmp $R2 $R4 0 do_abort
+    IntCmp $R0 $R5 do_accept do_abort
+
+    StrCmp $R9 $R6 0 do_abort
+    IntCmp $R2 $R7 0 0 do_abort
+    IntCmp $R0 $R8 do_abort do_accept do_abort
+
+do_accept:
+    StrCpy $IS_UPGRADE_60 "1"
+    FileClose $R1
+    Goto skip_abort
+
+do_abort:
+  ;check
+  ; - if there are no files into $INSTDIR (recursively) just excepted property files
+  ; - if property files have the same installation time.
+  StrCpy $9 "$INSTDIR"
+  Call instDirEmpty
+  StrCmp $9 "not empty" abort 0
+  Push "Complete"
+  Push "$INSTDIR\bin\${PRODUCT_EXE_FILE}.vmoptions"
+  Push "$INSTDIR\bin\idea.properties"
+  ${StrRep} $0 ${PRODUCT_EXE_FILE} ".exe" "64.exe.vmoptions"
+  Push "$INSTDIR\bin\$0"
+  Call compareFileInstallationTime
+  StrCmp $9 "Modified" abort skip_abort
+abort:
+  MessageBox MB_OK|MB_ICONEXCLAMATION "$(empty_or_upgrade_folder)"
+  Abort
+skip_abort:
+FunctionEnd
+
+
+;check if there are no files into $INSTDIR recursively just except property files.
+Function instDirEmpty
+  Push $0
+  Push $1
+  Push $2
+  ClearErrors
+  FindFirst $1 $2 "$9\*.*"
+nextElemement:
+  ;is the element a folder?
+  StrCmp $2 "." getNextElement
+  StrCmp $2 ".." getNextElement
+  IfFileExists "$9\$2\*.*" 0 nextFile
+    Push $9
+    StrCpy "$9" "$9\$2"
+    Call instDirEmpty
+    StrCmp $9 "not empty" done 0
+    Pop $9
+    Goto getNextElement
+nextFile:
+  ;is it the file property?
+  ${If} $2 != "idea.properties"
+    ${AndIf} $2 != "${PRODUCT_EXE_FILE}.vmoptions"
+      ${StrRep} $0 ${PRODUCT_EXE_FILE} ".exe" "64.exe.vmoptions"
+      ${AndIf} $2 != $0
+        StrCpy $9 "not empty"
+        Goto done
+  ${EndIf}
+getNextElement:
+  FindNext $1 $2
+  IfErrors 0 nextElemement
+done:
+  FindClose $1
+  Pop $2
+  Pop $1
+  Pop $0
+FunctionEnd
+
+
+;------------------------------------------------------------------------------
+; Variables
+;------------------------------------------------------------------------------
+  Var STARTMENU_FOLDER
+  Var config_path
+  Var system_path
+
+;------------------------------------------------------------------------------
+; configuration
+;------------------------------------------------------------------------------
+
+!insertmacro MUI_PAGE_WELCOME
+
+Page custom uninstallOldVersionDialog
+
+Var control_fields
+Var max_fields
+
+!ifdef LICENSE_FILE
+!insertmacro MUI_PAGE_LICENSE "$(myLicenseData)"
+!endif
+
+!define MUI_PAGE_CUSTOMFUNCTION_LEAVE OnDirectoryPageLeave
+!insertmacro MUI_PAGE_DIRECTORY
+
+Page custom ConfirmDesktopShortcut
+  !define MUI_STARTMENUPAGE_NODISABLE
+  !define MUI_STARTMENUPAGE_DEFAULTFOLDER "JetBrains"
+
+!insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER
+!define MUI_ABORTWARNING
+!insertmacro MUI_PAGE_INSTFILES
+!define MUI_FINISHPAGE_RUN_NOTCHECKED
+!define MUI_FINISHPAGE_RUN
+!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun
+!insertmacro MUI_PAGE_FINISH
+
+!define MUI_UNINSTALLER
+;!insertmacro MUI_UNPAGE_CONFIRM
+UninstPage custom un.ConfirmDeleteSettings
+!insertmacro MUI_UNPAGE_INSTFILES
+
+OutFile "${OUT_DIR}\${OUT_FILE}.exe"
+
+InstallDir "$PROGRAMFILES\${MANUFACTURER}\${PRODUCT_WITH_VER}"
+!define MUI_BRANDINGTEXT " "
+BrandingText " "
+
+Function PageFinishRun
+!insertmacro UAC_AsUser_ExecShell "" "$INSTDIR\bin\${PRODUCT_EXE_FILE}" "" "" ""
+FunctionEnd
+
+;------------------------------------------------------------------------------
+; languages
+;------------------------------------------------------------------------------
+!insertmacro MUI_LANGUAGE "English"
+;!insertmacro MUI_LANGUAGE "Japanese"
+!include "idea_en.nsi"
+;!include "idea_jp.nsi"
+
+!ifdef LICENSE_FILE
+LicenseLangString myLicenseData ${LANG_ENGLISH} "${LICENSE_FILE}.txt"
+LicenseLangString myLicenseData ${LANG_JAPANESE} "${LICENSE_FILE}.txt"
+!endif
+
+Function .onInit
+  StrCpy $baseRegKey "HKCU"
+  IfSilent UAC_Done
+UAC_Elevate:
+    !insertmacro UAC_RunElevated
+    StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user? - continue install under user
+    StrCmp 0 $0 0 UAC_Err ; Error?
+    StrCmp 1 $1 0 UAC_Success ;Are we the real deal or just the wrapper?
+    Quit
+UAC_Err:
+    Abort
+UAC_ElevationAborted:
+    StrCpy $INSTDIR "$APPDATA\${MANUFACTURER}\${PRODUCT_WITH_VER}"
+    goto UAC_Done
+UAC_Success:
+    StrCmp 1 $3 UAC_Admin ;Admin?
+    StrCmp 3 $1 0 UAC_ElevationAborted ;Try again?
+    goto UAC_Elevate
+UAC_Admin:
+    StrCpy $INSTDIR "$PROGRAMFILES\${MANUFACTURER}\${PRODUCT_WITH_VER}"
+    SetShellVarContext all
+    StrCpy $baseRegKey "HKLM"
+UAC_Done:	
+;  !insertmacro MUI_LANGDLL_DISPLAY
+FunctionEnd
+
+Function checkVersion
+  StrCpy $2 ""
+  StrCpy $1 "Software\${MANUFACTURER}\${PRODUCT_REG_VER}"
+;  ${If} $0 == "HKLM"
+;    StrCpy $1 "Software\${MANUFACTURER}\${PRODUCT_REG_VER}"
+;    Push $0
+;    call winVersion
+;    ${If} $0 == "1"
+;      StrCpy $1 "Software\Wow6432Node\${MANUFACTURER}\${PRODUCT_REG_VER}"
+;    ${Else}
+;      StrCpy $1 "Software\${MANUFACTURER}\${PRODUCT_REG_VER}"
+;    ${EndIf}
+;    Pop $0
+;  ${EndIf}
+  Call OMReadRegStr
+  IfFileExists $3\bin\${PRODUCT_EXE_FILE} check_version
+  Goto Done
+check_version:
+  StrCpy $2 "Build"
+  Call OMReadRegStr
+  StrCmp $3 "" Done
+  IntCmpU $3 ${VER_BUILD} ask_Install_Over Done ask_Install_Over
+ask_Install_Over:
+  MessageBox MB_YESNO|MB_ICONQUESTION "$(current_version_already_installed)" IDYES continue IDNO exit_installer
+exit_installer:
+  Abort
+continue:
+  StrCpy $0 "complete"
+Done:
+FunctionEnd
+
+
+Function searchCurrentVersion
+  ; search current version of IDEA
+  StrCpy $0 "HKCU"
+  Call checkVersion
+  StrCmp $0 "complete" Done
+  StrCpy $0 "HKLM"
+  Call checkVersion
+Done:  
+FunctionEnd
+
+
+Function uninstallOldVersion
+  ;check if the uninstalled application is running
+remove_previous_installation:
+  ;prepare a copy of launcher
+  CopyFiles "$3\bin\${PRODUCT_EXE_FILE}" "$3\bin\${PRODUCT_EXE_FILE}_copy"
+  ClearErrors
+  ;copy launcher to itself
+  CopyFiles "$3\bin\${PRODUCT_EXE_FILE}_copy" "$3\bin\${PRODUCT_EXE_FILE}"
+  Delete "$3\bin\${PRODUCT_EXE_FILE}_copy"
+  IfErrors 0 +3
+  MessageBox MB_OKCANCEL|MB_ICONQUESTION|MB_TOPMOST "$(application_running)" IDOK remove_previous_installation IDCANCEL complete
+  goto complete
+  ; uninstallation mode
+  !insertmacro INSTALLOPTIONS_READ $9 "UninstallOldVersions.ini" "Field 2" "State"
+  ${If} $9 == "1"
+    ExecWait '"$3\bin\Uninstall.exe" /S'
+  ${else}
+    ExecWait '"$3\bin\Uninstall.exe" _?=$3\bin'
+  ${EndIf}
+  IfFileExists $3\bin\${PRODUCT_EXE_FILE} 0 uninstall
+  goto complete
+uninstall:
+  ;previous installation has been removed
+  ;customer decided to keep properties?
+  IfFileExists $3\bin\idea.properties saveProperties fullRemove
+saveProperties:
+  Delete "$3\bin\Uninstall.exe"
+  Goto complete
+fullRemove:
+  RmDir /r "$3"
+complete:
+FunctionEnd
+
+
+Function checkProductVersion
+;$8 - count of already added fields to the dialog
+;$3 - an old version which will be checked if the one should be added too
+StrCpy $7 $control_fields
+StrCpy $6 ""
+loop:
+  IntOp $7 $7 + 1
+  ${If} $8 >= $7
+	!insertmacro INSTALLOPTIONS_READ $6 "UninstallOldVersions.ini" "Field $7" "Text"
+	${If} $6 == $3
+		;found the same value in list of installations
+		StrCpy $6 "duplicated"
+		Goto finish
+	${EndIf}
+    Goto loop
+  ${EndIf}
+finish:
+FunctionEnd
+
+
+Function uninstallOldVersionDialog
+  StrCpy $control_fields 2
+  StrCpy $max_fields 13
+  StrCpy $0 "HKLM"
+  StrCpy $4 0
+  ReserveFile "UninstallOldVersions.ini"
+  !insertmacro INSTALLOPTIONS_EXTRACT "UninstallOldVersions.ini"
+  StrCpy $8 $control_fields
+
+get_installation_info:
+  StrCpy $1 "Software\${MANUFACTURER}\${MUI_PRODUCT}"
+  StrCpy $5 "\bin\${PRODUCT_EXE_FILE}"
+  StrCpy $2 ""
+  Call getInstallationPath
+  StrCmp $3 "complete" next_registry_root
+  ;check if the old installation could be uninstalled
+  IfFileExists $3\bin\Uninstall.exe uninstall_dialog get_next_key
+uninstall_dialog:
+  Call checkProductVersion
+  ${If} $6 != "duplicated"
+    IntOp $8 $8 + 1
+    !insertmacro INSTALLOPTIONS_WRITE "UninstallOldVersions.ini" "Field $8" "Text" "$3"
+    StrCmp $8 $max_fields complete
+  ${EndIf}
+get_next_key:
+  IntOp $4 $4 + 1 ;to check next record from registry
+  goto get_installation_info
+
+next_registry_root:
+${If} $0 == "HKLM"
+  StrCpy $0 "HKCU"
+  StrCpy $4 0
+  Goto get_installation_info
+${EndIf}
+complete:
+!insertmacro INSTALLOPTIONS_WRITE "UninstallOldVersions.ini" "Settings" "NumFields" "$8"
+${If} $8 > $control_fields
+  ;$2 used in prompt text
+  StrCpy $2 "s"
+  StrCpy $7 $control_fields
+  IntOp $7 $7 + 1
+  StrCmp $8 $7 0 +2
+    StrCpy $2 ""
+  !insertmacro MUI_HEADER_TEXT "$(uninstall_previous_installations_title)" "$(uninstall_previous_installations)"
+  !insertmacro INSTALLOPTIONS_WRITE "UninstallOldVersions.ini" "Field 1" "Text" "$(uninstall_previous_installations_prompt)"
+  !insertmacro INSTALLOPTIONS_WRITE "UninstallOldVersions.ini" "Field 3" "Flags" "FOCUS"
+  !insertmacro INSTALLOPTIONS_DISPLAY "UninstallOldVersions.ini"
+  ;uninstall chosen installation(s)
+
+  ;no disabled controls. StrCmp $2 "OK" loop finish
+loop:
+  !insertmacro INSTALLOPTIONS_READ $0 "UninstallOldVersions.ini" "Field $8" "State"
+  !insertmacro INSTALLOPTIONS_READ $3 "UninstallOldVersions.ini" "Field $8" "Text"
+  ${If} $0 == "1"
+    Call uninstallOldVersion
+    ${EndIf}
+    IntOp $8 $8 - 1
+    StrCmp $8 $control_fields finish loop
+  ${EndIf}
+finish:
+FunctionEnd
+
+
+Function getInstallationPath
+  Push $1
+  Push $2
+  Push $5
+loop:
+  Call OMEnumRegKey
+  StrCmp $3 "" 0 getPath
+  StrCpy $3 "complete"
+  goto done
+getPath:
+  Push $1
+  StrCpy $1 "$1\$3"
+  Call OMReadRegStr
+  Pop $1
+  IfFileExists $3$5 done 0 
+  IntOp $4 $4 + 1
+  goto loop
+done:
+  Pop $5
+  Pop $2
+  Pop $1
+FunctionEnd
+
+
+Function GUIInit
+  Push $0
+  Push $1
+  Push $2
+  Push $3
+  Push $4
+  Push $5
+
+; is the current version of IDEA installed?
+  Call searchCurrentVersion
+
+; search old versions of IDEA
+  StrCpy $4 0
+  StrCpy $0 "HKCU"
+  StrCpy $1 "Software\${MANUFACTURER}\${MUI_PRODUCT}"
+  StrCpy $5 "\bin\${PRODUCT_EXE_FILE}"
+  StrCpy $2 ""
+  Call getInstallationPath
+  StrCmp $3 "complete" all_users
+  IfFileExists $3\bin\${PRODUCT_EXE_FILE} old_version_located all_users
+all_users:
+  StrCpy $4 0
+  StrCpy $0 "HKLM"
+  Call getInstallationPath
+  StrCmp $3 "complete" success
+  IfFileExists $3\bin\${PRODUCT_EXE_FILE} 0 success
+old_version_located:
+;  MessageBox MB_YESNO|MB_ICONQUESTION "$(previous_installations)" IDYES uninstall IDNO success
+;uninstall:
+;  Call uninstallOldVersions
+
+success:
+  IntCmp ${SHOULD_SET_DEFAULT_INSTDIR} 0 end_enum_versions_hklm
+  StrCpy $3 "0"        # latest build number
+  StrCpy $0 "0"        # registry key index
+
+enum_versions_hkcu:
+  EnumRegKey $1 "HKCU" "Software\${MANUFACTURER}\${MUI_PRODUCT}" $0
+  StrCmp $1 "" end_enum_versions_hkcu
+  IntCmp $1 $3 continue_enum_versions_hkcu continue_enum_versions_hkcu
+  StrCpy $3 $1
+  ReadRegStr $INSTDIR "HKCU" "Software\${MANUFACTURER}\${MUI_PRODUCT}\$3" ""
+  
+continue_enum_versions_hkcu:
+  IntOp $0 $0 + 1
+  Goto enum_versions_hkcu
+  
+end_enum_versions_hkcu:  
+
+  StrCpy $0 "0"        # registry key index
+
+enum_versions_hklm:
+  EnumRegKey $1 "HKLM" "Software\${MANUFACTURER}\${MUI_PRODUCT}" $0
+  StrCmp $1 "" end_enum_versions_hklm
+  IntCmp $1 $3 continue_enum_versions_hklm continue_enum_versions_hklm
+  StrCpy $3 $1
+  ReadRegStr $INSTDIR "HKLM" "Software\${MANUFACTURER}\${MUI_PRODUCT}\$3" ""
+  
+continue_enum_versions_hklm:
+  IntOp $0 $0 + 1
+  Goto enum_versions_hklm
+  
+end_enum_versions_hklm:
+
+  StrCmp $INSTDIR "" 0 skip_default_instdir
+  StrCpy $INSTDIR "$PROGRAMFILES\${MANUFACTURER}\${MUI_PRODUCT} ${MUI_VERSION_MAJOR}.${MUI_VERSION_MINOR}"
+skip_default_instdir:
+
+  Pop $5
+  Pop $4
+  Pop $3
+  Pop $2
+  Pop $1
+  Pop $0
+  !insertmacro INSTALLOPTIONS_EXTRACT "Desktop.ini"
+
+FunctionEnd
+
+Function DoAssociation
+ ; back up old value of an association
+ ReadRegStr $1 HKCR $R4 ""
+  StrCmp $1 "" skip_backup
+    StrCmp $1 ${PRODUCT_PATHS_SELECTOR} skip_backup
+    WriteRegStr HKCR $R4 "backup_val" $1
+skip_backup:
+  WriteRegStr HKCR $R4 "" "${PRODUCT_PATHS_SELECTOR}"
+  ReadRegStr $0 HKCR ${PRODUCT_PATHS_SELECTOR} ""
+  StrCmp $0 "" 0 command_exists
+	WriteRegStr HKCR ${PRODUCT_PATHS_SELECTOR} "" "${PRODUCT_FULL_NAME}"
+	WriteRegStr HKCR "${PRODUCT_PATHS_SELECTOR}\shell" "" "open"
+	WriteRegStr HKCR "${PRODUCT_PATHS_SELECTOR}\DefaultIcon" "" "$INSTDIR\bin\${PRODUCT_EXE_FILE},0"
+command_exists:
+  WriteRegStr HKCR "${PRODUCT_PATHS_SELECTOR}\shell\open\command" "" \
+    '$INSTDIR\bin\${PRODUCT_EXE_FILE} "%1"'
+FunctionEnd
+
+;------------------------------------------------------------------------------
+; Installer sections
+;------------------------------------------------------------------------------
+Section "IDEA Files" CopyIdeaFiles
+;  StrCpy $baseRegKey "HKCU"
+;  !insertmacro INSTALLOPTIONS_READ $R2 "Desktop.ini" "Field 3" "State"
+;  StrCmp $R2 1 continue_for_current_user
+;  SetShellVarContext all
+;  StrCpy $baseRegKey "HKLM"
+;  continue_for_current_user:
+
+; create shortcuts
+
+  !insertmacro INSTALLOPTIONS_READ $R2 "Desktop.ini" "Field 4" "State"
+  StrCmp $R2 1 "" python3
+  StrCpy $R2 "2.7"
+  goto check_python
+python3:  
+  StrCpy $R2 "3.4"
+check_python:  
+  ReadRegStr $1 "HKCU" "Software\Python\PythonCore\$R2\InstallPath" $0
+  StrCmp $1 "" installation_for_all_users
+  goto verefy_python_launcher
+installation_for_all_users:
+  ReadRegStr $1 "HKLM" "Software\Python\PythonCore\$R2\InstallPath" $0
+  StrCmp $1 "" get_python
+verefy_python_launcher:
+  IfFileExists $1\python.exe python_exists get_python
+
+get_python:
+  CreateDirectory "$INSTDIR\python"
+  StrCmp $R2 "2.7" get_python2
+  inetc::get "https://www.python.org/ftp/python/3.4.1/python-3.4.1.amd64.msi" "$INSTDIR\python\python_$R2.msi"
+  goto validate_download
+get_python2:  
+  inetc::get "http://www.python.org/ftp/python/2.7.8/python-2.7.8.msi" "$INSTDIR\python\python_$R2.msi"
+validate_download:  
+  Pop $0
+  ${If} $0 == "OK" 
+    ExecCmd::exec 'msiexec /i "$INSTDIR\python\python_$R2.msi" /quiet /qn /norestart /log "$INSTDIR\python\python_$R2_silent.log"'
+  ${EndIf}
+
+python_exists:  
+  !insertmacro INSTALLOPTIONS_READ $R2 "Desktop.ini" "Field 1" "State"
+  StrCmp $R2 1 "" skip_desktop_shortcut
+  CreateShortCut "$DESKTOP\${PRODUCT_FULL_NAME_WITH_VER}.lnk" \
+                 "$INSTDIR\bin\${PRODUCT_EXE_FILE}" "" "" "" SW_SHOWNORMAL
+
+skip_desktop_shortcut:
+  ; OS is not win7
+  Call winVersion
+  ${If} $0 == "0" 
+    !insertmacro INSTALLOPTIONS_READ $R2 "Desktop.ini" "Field 2" "State"
+    StrCmp $R2 1 "" skip_quicklaunch_shortcut
+    CreateShortCut "$QUICKLAUNCH\${PRODUCT_FULL_NAME_WITH_VER}.lnk" \
+                   "$INSTDIR\bin\${PRODUCT_EXE_FILE}" "" "" "" SW_SHOWNORMAL
+  ${EndIf}	
+skip_quicklaunch_shortcut:
+
+  !insertmacro INSTALLOPTIONS_READ $R1 "Desktop.ini" "Settings" "NumFields"
+  IntCmp $R1 ${INSTALL_OPTION_ELEMENTS} do_association done do_association
+do_association:
+  StrCpy $R2 ${INSTALL_OPTION_ELEMENTS}  
+get_user_choice:
+  !insertmacro INSTALLOPTIONS_READ $R3 "Desktop.ini" "Field $R2" "State"
+  StrCmp $R3 1 "" next_association
+  !insertmacro INSTALLOPTIONS_READ $R4 "Desktop.ini" "Field $R2" "Text"
+  call DoAssociation
+next_association:  
+  IntOp $R2 $R2 + 1
+  IntCmp $R1 $R2 get_user_choice done get_user_choice
+
+done:   
+!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
+; $STARTMENU_FOLDER stores name of IDEA folder in Start Menu,
+; save it name in the "MenuFolder" RegValue
+  CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER"
+
+  CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\${PRODUCT_FULL_NAME_WITH_VER}.lnk" \
+                 "$INSTDIR\bin\${PRODUCT_EXE_FILE}" "" "" "" SW_SHOWNORMAL
+;  CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Uninstall ${PRODUCT_FULL_NAME_WITH_VER}.lnk" \
+;                 "$INSTDIR\bin\Uninstall.exe"
+  StrCpy $0 $baseRegKey
+  StrCpy $1 "Software\${MANUFACTURER}\${PRODUCT_REG_VER}"
+  StrCpy $2 "MenuFolder"
+  StrCpy $3 "$STARTMENU_FOLDER"
+  Call OMWriteRegStr
+!insertmacro MUI_STARTMENU_WRITE_END
+
+  StrCmp ${IPR} "false" skip_ipr
+
+  ; back up old value of .ipr
+!define Index "Line${__LINE__}"
+  ReadRegStr $1 HKCR ".ipr" ""
+  StrCmp $1 "" "${Index}-NoBackup"
+    StrCmp $1 "IntelliJIdeaProjectFile" "${Index}-NoBackup"
+    WriteRegStr HKCR ".ipr" "backup_val" $1
+"${Index}-NoBackup:"
+  WriteRegStr HKCR ".ipr" "" "IntelliJIdeaProjectFile"
+  ReadRegStr $0 HKCR "IntelliJIdeaProjectFile" ""
+  StrCmp $0 "" 0 "${Index}-Skip"
+	WriteRegStr HKCR "IntelliJIdeaProjectFile" "" "IntelliJ IDEA Project File"
+	WriteRegStr HKCR "IntelliJIdeaProjectFile\shell" "" "open"
+	WriteRegStr HKCR "IntelliJIdeaProjectFile\DefaultIcon" "" "$INSTDIR\bin\idea.exe,0"
+"${Index}-Skip:"
+  WriteRegStr HKCR "IntelliJIdeaProjectFile\shell\open\command" "" \
+    '$INSTDIR\bin\${PRODUCT_EXE_FILE} "%1"'
+!undef Index
+
+skip_ipr:
+
+; readonly section
+  SectionIn RO
+!include "idea_win.nsh"
+
+  IntCmp $IS_UPGRADE_60 1 skip_properties
+  SetOutPath $INSTDIR\bin
+  File "${PRODUCT_PROPERTIES_FILE}"
+  File "${PRODUCT_VM_OPTIONS_FILE}"
+skip_properties:
+
+  StrCpy $0 $baseRegKey
+  StrCpy $1 "Software\${MANUFACTURER}\${PRODUCT_REG_VER}"
+  StrCpy $2 ""
+  StrCpy $3 "$INSTDIR"
+  Call OMWriteRegStr
+  StrCpy $2 "Build"
+  StrCpy $3 ${VER_BUILD}
+  Call OMWriteRegStr
+
+; write uninstaller & add it to add/remove programs in control panel
+  WriteUninstaller "$INSTDIR\bin\Uninstall.exe"
+  WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" \
+            "DisplayName" "${PRODUCT_FULL_NAME_WITH_VER}"
+  WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" \
+              "UninstallString" "$INSTDIR\bin\Uninstall.exe"
+  WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" \
+              "InstallLocation" "$INSTDIR"
+  WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" \
+              "DisplayIcon" "$INSTDIR\bin\${PRODUCT_EXE_FILE}"
+  WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" \
+              "DisplayVersion" "${VER_BUILD}"
+  WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" \
+              "Publisher" "JetBrains s.r.o."
+  WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" \
+              "URLInfoAbout" "http://www.jetbrains.com/products"
+  WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" \
+              "InstallType" "$baseRegKey"
+  WriteRegDWORD SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" \
+              "NoModify" 1
+  WriteRegDWORD SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" \
+              "NoRepair" 1
+
+  ExecWait "$INSTDIR\jre\jre\bin\javaw.exe -Xshare:dump"
+  SetOutPath $INSTDIR\bin
+  ; set the current time for installation files under $INSTDIR\bin
+  ExecCmd::exec 'copy "$INSTDIR\bin\*.*s" +,,'
+  call winVersion
+  ${If} $0 == "1"  
+    ;ExecCmd::exec 'icacls "$INSTDIR" /grant %username%:F /T >"$INSTDIR"\installation_log.txt 2>"$INSTDIR"\installation_error.txt'
+    AccessControl::GrantOnFile \
+      "$INSTDIR" "(S-1-5-32-545)" "GenericRead + GenericExecute"
+  ${EndIf}
+SectionEnd
+
+;------------------------------------------------------------------------------
+; Descriptions of sections
+;------------------------------------------------------------------------------
+; LangString DESC_CopyRuntime ${LANG_ENGLISH} "${MUI_PRODUCT} files"
+
+;------------------------------------------------------------------------------
+; custom install pages
+;------------------------------------------------------------------------------
+
+Function ConfirmDesktopShortcut
+  !insertmacro MUI_HEADER_TEXT "$(installation_options)" "$(installation_options_prompt)"
+  !insertmacro INSTALLOPTIONS_WRITE "Desktop.ini" "Field 1" "Text" "$(create_desktop_shortcut)"
+  call winVersion
+  !insertmacro INSTALLOPTIONS_WRITE "Desktop.ini" "Field 2" "Text" "$(create_quick_launch_shortcut)"
+  ${If} $0 == "1"
+    !insertmacro INSTALLOPTIONS_WRITE "Desktop.ini" "Field 2" "Flags" "DISABLED"
+  ${EndIf}
+  StrCmp "${ASSOCIATION}" "NoAssociation" skip_association
+  StrCpy $R0 6
+  push "${ASSOCIATION}"
+loop:
+  call SplitStr
+  Pop $0
+  StrCmp $0 "" done
+  IntOp $R0 $R0 + 1
+  !insertmacro INSTALLOPTIONS_WRITE "Desktop.ini" "Field $R0" "Text" "$0"
+  goto loop
+skip_association:
+  StrCpy $R0 2
+  call winVersion
+  ${If} $0 == "1"
+  IntOp $R0 $R0 - 1
+  ${EndIf}
+done:
+  !insertmacro INSTALLOPTIONS_WRITE "Desktop.ini" "Settings" "NumFields" "$R0"
+  !insertmacro INSTALLOPTIONS_DISPLAY "Desktop.ini"
+FunctionEnd
+
+
+;------------------------------------------------------------------------------
+; custom uninstall functions
+;------------------------------------------------------------------------------
+
+Function un.onInit
+  ;admin perm. is required to uninstall?
+  ${UnStrStr} $R0 $INSTDIR $PROGRAMFILES
+  StrCmp $R0 $INSTDIR requred_admin_perm UAC_Done
+
+requred_admin_perm:
+  ;the user has admin rights?
+  UserInfo::GetAccountType
+  Pop $R2
+  StrCmp $R2 "Admin" UAC_Admin uninstall_location
+
+uninstall_location:
+  ;check if the uninstallation is running from the product location
+  IfFileExists $APPDATA\${PRODUCT_PATHS_SELECTOR}_${VER_BUILD}_Uninstall.exe UAC_Elevate copy_uninstall
+
+copy_uninstall:
+  ;do copy for unistall.exe
+  CopyFiles "$OUTDIR\Uninstall.exe" "$APPDATA\${PRODUCT_PATHS_SELECTOR}_${VER_BUILD}_Uninstall.exe"
+  ExecWait '"$APPDATA\${PRODUCT_PATHS_SELECTOR}_${VER_BUILD}_Uninstall.exe" _?=$INSTDIR'
+  Delete "$APPDATA\${PRODUCT_PATHS_SELECTOR}_${VER_BUILD}_Uninstall.exe"
+  Quit
+
+UAC_Elevate:
+  !insertmacro UAC_RunElevated
+  StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user? - continue install under user
+  StrCmp 0 $0 0 UAC_Err ; Error?
+  StrCmp 1 $1 0 UAC_Success ;Are we the real deal or just the wrapper?
+  Quit
+UAC_ElevationAborted:
+UAC_Err:
+  Abort
+UAC_Success:
+  StrCmp 1 $3 UAC_Admin ;Admin?
+  StrCmp 3 $1 0 UAC_ElevationAborted ;Try again?
+  goto UAC_Elevate
+UAC_Admin:
+  SetShellVarContext all
+  StrCpy $baseRegKey "HKLM"
+UAC_Done:
+  !insertmacro MUI_UNGETLANGUAGE
+  !insertmacro INSTALLOPTIONS_EXTRACT "DeleteSettings.ini"
+FunctionEnd
+
+Function OMEnumRegKey
+  StrCmp $0 "HKCU" hkcu
+    EnumRegKey $3 HKLM $1 $4
+    goto done
+hkcu:
+    EnumRegKey $3 HKCU $1 $4
+done:
+FunctionEnd
+
+Function un.OMReadRegStr
+  StrCmp $0 "HKCU" hkcu
+    ReadRegStr $3 HKLM $1 $2
+    goto done
+hkcu:
+    ReadRegStr $3 HKCU $1 $2
+done:
+FunctionEnd
+
+Function un.OMDeleteRegValue
+  StrCmp $0 "HKCU" hkcu
+    DeleteRegValue HKLM $1 $2
+    goto done
+hkcu:
+    DeleteRegValue HKCU $1 $2
+done:
+FunctionEnd
+
+Function un.ReturnBackupRegValue
+  ;replace Default str with the backup value (if there is the one) and then delete backup
+  ; $1 - key (for example ".java")
+  ; $2 - name (for example "backup_val")
+  Push $0
+  ReadRegStr $0 HKCR $1 $2
+  StrCmp $0 "" "noBackup"
+    WriteRegStr HKCR $1 "" $0
+    DeleteRegValue HKCR $1 $2
+noBackup:  
+  Pop $0
+FunctionEnd
+
+Function un.OMDeleteRegKeyIfEmpty
+  StrCmp $0 "HKCU" hkcu
+    DeleteRegKey /ifempty HKLM $1
+    goto done
+hkcu:
+    DeleteRegKey /ifempty HKCU $1
+done:
+FunctionEnd
+
+Function un.OMDeleteRegKey
+  StrCmp $0 "HKCU" hkcu
+    DeleteRegKey /ifempty HKLM $1
+    goto done
+hkcu:
+    DeleteRegKey /ifempty HKCU $1
+done:
+FunctionEnd
+
+Function un.OMWriteRegStr
+  StrCmp $0 "HKCU" hkcu
+    WriteRegStr HKLM $1 $2 $3
+    goto done
+hkcu:
+    WriteRegStr HKCU $1 $2 $3
+done:
+FunctionEnd
+
+
+;------------------------------------------------------------------------------
+; custom uninstall pages
+;------------------------------------------------------------------------------
+
+Function un.ConfirmDeleteSettings
+  !insertmacro MUI_HEADER_TEXT "$(uninstall_options)" "$(uninstall_options_prompt)"
+  !insertmacro INSTALLOPTIONS_WRITE "DeleteSettings.ini" "Field 1" "Text" "$(prompt_delete_settings)"
+  !insertmacro INSTALLOPTIONS_WRITE "DeleteSettings.ini" "Field 2" "Text" $INSTDIR
+  !insertmacro INSTALLOPTIONS_WRITE "DeleteSettings.ini" "Field 3" "Text" "$(text_delete_settings)"
+  !insertmacro INSTALLOPTIONS_WRITE "DeleteSettings.ini" "Field 4" "Text" "$(confirm_delete_caches)"
+  !insertmacro INSTALLOPTIONS_WRITE "DeleteSettings.ini" "Field 5" "Text" "$(confirm_delete_settings)"
+  !insertmacro INSTALLOPTIONS_DISPLAY "DeleteSettings.ini"
+FunctionEnd
+
+
+Function un.PrepareCustomPath
+  ;Input:
+  ;$0 - name of variable
+  ;$1 - value of the variable
+  ;$2 - line from the property file
+  push $3
+  push $5
+  ${UnStrLoc} $3 $2 $0 ">"
+  StrCmp $3 "" not_found
+  StrLen $5 $0
+  IntOp $3 $3 + $5
+  StrCpy $2 $2 "" $3
+  IfFileExists "$1$2\\*.*" not_found
+  StrCpy $2 $1$2
+  goto complete
+not_found:
+  StrCpy $0 ""
+complete:
+  pop $5
+  pop $3
+FunctionEnd
+
+
+Function un.getCustomPath
+  push $0
+  push $1
+  StrCpy $0 "${user.home}/"
+  StrCpy $1 "$PROFILE/"
+  Call un.PrepareCustomPath
+  StrCmp $0 "" check_idea_var
+  goto complete
+check_idea_var:
+  StrCpy $0 "${idea.home}/"
+  StrCpy $1 "$INSTDIR/"
+  Call un.PrepareCustomPath
+  StrCmp $2 "" +1 +2
+  StrCpy $2 ""
+complete:
+  pop $1
+  pop $0
+FunctionEnd
+
+
+Function un.getPath
+; The function read lines from idea.properties and search the substring and prepare the path to settings or caches.
+  ClearErrors
+  FileOpen $3 $INSTDIR\bin\idea.properties r
+  IfErrors complete ;file can not be open. not sure if a message should be displayed in this case.
+  StrLen $5 $1
+read_line:
+  FileRead $3 $4
+  StrCmp $4 "" complete
+  ${UnStrLoc} $6 $4 $1 ">"
+  StrCmp $6 "" read_line ; there is no substring in a string from the file. go for next one.
+  IntOp $6 $6 + $5
+  ${unStrStr} $7 $4 "#" ;check if the property has been customized
+  StrCmp $7 "" custom
+  StrCpy $2 "$PROFILE/${PRODUCT_SETTINGS_DIR}/$0" ;no. use the default value.
+  goto complete
+custom:
+  StrCpy $2 $4 "" $6
+  Call un.getCustomPath
+complete:
+  FileClose $3
+  ${UnStrRep} $2 $2 "/" "\"
+FunctionEnd
+
+
+Section "Uninstall"
+  StrCpy $baseRegKey "HKCU"
+  ; Uninstaller is in the \bin directory, we need upper level dir
+  StrCpy $INSTDIR $INSTDIR\..
+
+  !insertmacro INSTALLOPTIONS_READ $R2 "DeleteSettings.ini" "Field 4" "State"
+  DetailPrint "Data: $DOCUMENTS\..\${PRODUCT_SETTINGS_DIR}\"
+  StrCmp $R2 1 "" skip_delete_caches
+   ;find the path to caches (system) folder
+   StrCpy $0 "system"
+   StrCpy $1 "idea.system.path="
+   Call un.getPath
+   StrCmp $2 "" skip_delete_caches
+   StrCpy $system_path $2
+   RmDir /r "$system_path"
+   RmDir "$system_path\\.." ; remove parent of system dir if the dir is empty
+;   RmDir /r $DOCUMENTS\..\${PRODUCT_SETTINGS_DIR}\system
+skip_delete_caches:
+
+  !insertmacro INSTALLOPTIONS_READ $R3 "DeleteSettings.ini" "Field 5" "State"
+  StrCmp $R3 1 "" skip_delete_settings
+    ;find the path to settings (config) folder
+    StrCpy $0 "config"
+    StrCpy $1 "idea.config.path="
+    Call un.getPath
+    StrCmp $2 "" skip_delete_settings
+    StrCpy $config_path $2
+    RmDir /r "$config_path"
+;    RmDir /r $DOCUMENTS\..\${PRODUCT_SETTINGS_DIR}\config
+  Delete "$INSTDIR\bin\${PRODUCT_VM_OPTIONS_NAME}"
+  Delete "$INSTDIR\bin\idea.properties"
+  StrCmp $R2 1 "" skip_delete_settings
+  RmDir "$config_path\\.." ; remove parent of config dir if the dir is empty
+;    RmDir $DOCUMENTS\..\${PRODUCT_SETTINGS_DIR}
+skip_delete_settings:
+
+; Delete uninstaller itself
+  Delete "$INSTDIR\bin\Uninstall.exe"
+  Delete "$INSTDIR\jre\jre\bin\client\classes.jsa"
+
+  Push "Complete"
+  Push "$INSTDIR\bin\${PRODUCT_EXE_FILE}.vmoptions"
+  Push "$INSTDIR\bin\idea.properties"
+  ${UnStrRep} $0 ${PRODUCT_EXE_FILE} ".exe" "64.exe.vmoptions"
+  Push "$INSTDIR\bin\$0"
+  Call un.compareFileInstallationTime
+  ${If} $9 != "Modified"
+    RMDir /r "$INSTDIR"
+  ${Else}
+    !include "unidea_win.nsh"
+    RMDir "$INSTDIR"
+  ${EndIf}
+
+  ReadRegStr $R9 HKCU "Software\${MANUFACTURER}\${PRODUCT_REG_VER}" "MenuFolder"
+  StrCmp $R9 "" "" clear_shortcuts
+  ReadRegStr $R9 HKLM "Software\${MANUFACTURER}\${PRODUCT_REG_VER}" "MenuFolder"
+  StrCmp $R9 "" clear_Registry
+  StrCpy $baseRegKey "HKLM"
+  StrCpy $5 "Software\${MANUFACTURER}"
+;  call un.winVersion
+; ${If} $0 == "1"
+;    StrCpy $5 "Software\Wow6432Node\${MANUFACTURER}"
+; ${EndIf}
+clear_shortcuts:
+  ;the user has the admin rights
+;  UserInfo::GetAccountType
+;  Pop $R2
+  IfFileExists "$DESKTOP\${PRODUCT_FULL_NAME_WITH_VER}.lnk" keep_current_user
+  SetShellVarContext all
+keep_current_user:
+  DetailPrint "Start Menu: $SMPROGRAMS\$R9\${PRODUCT_FULL_NAME_WITH_VER}"
+ 
+  Delete "$SMPROGRAMS\$R9\${PRODUCT_FULL_NAME_WITH_VER}.lnk"
+;  Delete "$SMPROGRAMS\$R9\Uninstall ${PRODUCT_FULL_NAME_WITH_VER}.lnk"
+; Delete only if empty (last IDEA version is uninstalled)
+  RMDir  "$SMPROGRAMS\$R9"
+
+  Delete "$DESKTOP\${PRODUCT_FULL_NAME_WITH_VER}.lnk"
+  Delete "$QUICKLAUNCH\${PRODUCT_FULL_NAME_WITH_VER}.lnk"
+
+clear_Registry:
+  StrCpy $5 "Software\${MANUFACTURER}"
+; call un.winVersion
+; ${If} $0 == "1"
+;    StrCpy $5 "Software\Wow6432Node\${MANUFACTURER}"
+; ${EndIf}
+
+  StrCpy $0 $baseRegKey
+  StrCpy $1 "$5\${PRODUCT_REG_VER}"
+  StrCpy $2 "MenuFolder"
+  Call un.OMDeleteRegValue
+
+  StrCmp "${ASSOCIATION}" "NoAssociation" finish_uninstall
+  push "${ASSOCIATION}"
+loop:
+  call un.SplitStr
+  Pop $0
+  StrCmp $0 "" finish_uninstall
+  StrCpy $1 $0
+  StrCpy $2 "backup_val"
+  Call un.ReturnBackupRegValue
+  goto loop
+finish_uninstall:
+  StrCpy $1 "$5\${PRODUCT_REG_VER}"
+  StrCpy $2 "Build"
+  Call un.OMDeleteRegValue
+  StrCpy $2 ""
+  Call un.OMDeleteRegValue
+
+  StrCpy $1 "$5\${PRODUCT_REG_VER}"
+  Call un.OMDeleteRegKeyIfEmpty
+
+  StrCpy $1 "$5\${MUI_PRODUCT}"
+  Call un.OMDeleteRegKeyIfEmpty
+
+  StrCpy $1 "$5"
+  Call un.OMDeleteRegKeyIfEmpty
+
+  DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}"
+
+; UNCOMMENT THIS IN RELEASE BUILD
+; ExecShell "" "http://www.jetbrains.com/idea/uninstall/"
+
+SectionEnd
diff --git a/python/edu/build/plugin-list.txt b/python/edu/build/plugin-list.txt
index ceaa608..bc4b94c 100644
--- a/python/edu/build/plugin-list.txt
+++ b/python/edu/build/plugin-list.txt
@@ -2,10 +2,6 @@
 git4idea
 remote-servers-git
 github
-terminal
 IntelliLang
-IntelliLang-xml
-IntelliLang-js
 IntelliLang-python
-rest
-python-rest
+learn-python
\ No newline at end of file
diff --git a/python/edu/build/pycharm_edu_build.gant b/python/edu/build/pycharm_edu_build.gant
index 8c857c1..2ef0bed 100644
--- a/python/edu/build/pycharm_edu_build.gant
+++ b/python/edu/build/pycharm_edu_build.gant
@@ -13,26 +13,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 import org.jetbrains.jps.LayoutInfo
 
-import static org.jetbrains.jps.idea.IdeaProjectLoader.guessHome
-
-setProperty("home", guessHome(this as Script))
-
-includeTargets << new File("${guessHome(this as Script)}/build/scripts/utils.gant")
-// signMacZip locates in ultimate_utils.gant
- includeTargets << new File("${guessHome(this)}/ultimate/build/scripts/ultimate_utils.gant")
-includeTargets << new File("${guessHome(this)}/build/scripts/libLicenses.gant")
+setProperty("home", getHome())
+includeTargets << new File("$home/community/build/scripts/utils.gant")
+includeTargets << new File("$home/community/build/scripts/libLicenses.gant")
+includeTargets << new File("$home/build/scripts/ultimate_utils.gant")
 
 requireProperty("buildNumber", requireProperty("build.number", snapshot))
-
-setProperty("ch", "$home")
+setProperty("buildName", "PE-$buildNumber")
+setProperty("ch", "$home/community")
 setProperty("pythonCommunityHome", "$ch/python")
 setProperty("pythonEduHome", "$ch/python/edu")
 
 // load ApplicationInfo.xml properties
 ant.xmlproperty(file: "$pythonEduHome/resources/idea/PyCharmEduApplicationInfo.xml", collapseAttributes: "true")
-
 setProperty("system_selector", "PyCharm${p("component.version.major")}0")
 setProperty("dryRun", false)
 setProperty("jdk16", guessJdk())
@@ -40,6 +36,12 @@
 //modules to compile
 setProperty("pluginFilter", new File("$pythonEduHome/build/plugin-list.txt").readLines())
 
+private String getHome(){
+  // current file is supposed to be at build/scripts/*.gant path
+  String uri = this["gant.file"]
+  return new File(new URI(uri).getSchemeSpecificPart()).getParentFile().getParentFile().getParentFile().getParentFile().getParentFile()
+}
+
 private List<String> pycharmPlatformApiModules() {
   return [platformApiModules, "dom-openapi"].flatten()
 }
@@ -93,6 +95,10 @@
 
   loadProject()
 
+  List resourcePaths = ["$ch/community-resources/src",
+                        "$ch/platform/icons/src",
+                        "$pythonEduHome/resources"]
+
   projectBuilder.stage("Cleaning up sandbox folder")
 
   projectBuilder.targetFolder = "${paths.sandbox}/classes"
@@ -126,6 +132,8 @@
     exclude(name: "intentionDescriptions")
     exclude(name: "tips/**/*")
     exclude(name: "tips")
+    exclude(name: "courses")
+    exclude(name: "courses/*")
   }
 
   zipSources(home, paths.artifacts)
@@ -135,13 +143,9 @@
   layoutEducational("${paths.sandbox}/classes/production", usedJars)
 
 
-  //buildNSIS([paths.distAll, paths.distWin],
-  //          "${pythonEduHome}/build/strings.nsi", "${pythonEduHome}/build/paths.nsi",
-  //          "pycharm", false, true, ".py", system_selector)
-
-  def extraArgs = ["build.code": "pycharmEDU-${buildNumber}", "build.number": "PE-$buildNumber", "artifacts.path": "${paths.artifacts}"]
-  signMacZip("pycharm", extraArgs)
-  buildDmg("pycharm", "${pythonEduHome}/build/DMG_background.png", extraArgs)
+  def extraArgs = ["build.code": "pycharm${buildName}", "build.number": "PE-$buildNumber", "artifacts.path": "${paths.artifacts}"]
+  signMacZip("pycharm", extraArgs + ["sitFileName": "pycharm${buildName}-jdk-bundled", "jdk_archive_name": "jdk_mac_redist_for_${buildNumber}.tar"])
+  buildDmg("pycharm", "${pythonEduHome}/build/DMG_background.png", extraArgs + ["sitFileName": "pycharm${buildName}-jdk-bundled", "jdk_archive_name": "jdk_mac_redist_for_${buildNumber}.tar"])
 
 }
 
@@ -152,7 +156,7 @@
     usedJars = collectUsedJars(modules(), approvedJars(), ["/ant/"], null)
   }
 
-  def appInfo = appInfoFile(classesPath)
+  def appInfo = appInfoFile()
   def paths = new Paths(home)
   buildSearchableOptions("${projectBuilder.moduleOutput(findModule("platform-resources"))}/search", [], {
     projectBuilder.moduleRuntimeClasspath(findModule("main_pycharm_edu"), false).each {
@@ -181,30 +185,42 @@
   def launcher = "${paths.distWin}/bin/pycharm.exe"
   List resourcePaths = ["$ch/community-resources/src",
     "$ch/platform/icons/src",
+    "$pythonCommunityHome/resources",
     "$pythonEduHome/resources"]
-  buildWinLauncher("$ch", "$ch/bin/WinLauncher/WinLauncher.exe", launcher,
+  buildWinLauncher(ch, "$ch/bin/WinLauncher/WinLauncher.exe", launcher,
                    appInfo, "$pythonEduHome/build/pycharm_edu_launcher.properties", system_selector, resourcePaths)
+  //signExe(launcher)
+  executeExternalAnt(["dirName": "${paths.distWin}/bin", "fileName": "pycharm.exe"], "$home/build/signBuild.xml")
 
-  buildWinZip("${paths.artifacts}/pycharmPE-${buildNumber}.zip", [paths.distAll, paths.distWin])
+  buildWinZip("${paths.artifacts}/pycharm${buildName}.zip", [paths.distAll, paths.distWin])
 
-  String tarRoot = isEap() ? "pycharm-edu-$buildNumber" : "pycharm-edu-${p("component.version.major")}.${p("component.version.minor")}"
-  buildTarGz(tarRoot, "$paths.artifacts/pycharmPE-${buildNumber}.tar", [paths.distAll, paths.distUnix])
+  buildNSIS([paths.distAll, paths.distWin],
+            "$pythonEduHome/build/strings.nsi", "$pythonEduHome/build/paths.nsi",
+            "pycharmPE-", false, true, ".py", system_selector)
 
-  String macAppRoot = isEap() ? "PyCharm EDU ${p("component.version.major")}.${p("component.version.minor")} EAP.app/Contents" : "PyCharm EDU.app/Contents"
-  buildMacZip(macAppRoot, "${paths.artifacts}/pycharmEDU-${buildNumber}.sit", [paths.distAll], paths.distMac)
+  String tarRoot = isEap() ? "pycharm-pe-$buildNumber" : "pycharm-pe-${p("component.version.major")}.${p("component.version.minor")}"
+  buildTarGz(tarRoot, "$paths.artifacts/pycharm${buildName}.tar", [paths.distAll, paths.distUnix])
+
+  String macAppRoot = isEap() ? "PyCharm PE ${p("component.version.major")}.${p("component.version.minor")} EAP.app/Contents" : "PyCharm PE.app/Contents"
+  buildMacZip(macAppRoot, "${paths.artifacts}/pycharm${buildName}.sit", [paths.distAll], paths.distMac)
+  ant.copy(file: "${paths.artifacts}/pycharm${buildName}.sit", tofile: "${paths.artifacts}/pycharm${buildName}-jdk-bundled.sit")
+  ant.delete(file: "${paths.artifacts}/pycharm${buildName}.sit")
 }
 
 private layoutPlugins(layouts) {
   dir("plugins") {
-    layouts.layoutPlugin("rest")
-    layouts.layoutPlugin("python-rest")
+    layouts.layoutPlugin("learn-python") {
+      dir("courses") {
+        fileset(dir: "$pythonEduHome/learn-python/resources/courses")
+      }
+    }
   }
 
   layouts.layoutCommunityPlugins(ch)
 }
 
-private String appInfoFile(String classesPath) {
-  return "$classesPath/python-educational/idea/PyCharmEduApplicationInfo.xml"
+private String appInfoFile() {
+  return "$pythonEduHome/resources/idea/PyCharmEduApplicationInfo.xml"
 }
 
 private layoutFull(Map args, String target, Set usedJars) {
diff --git a/python/edu/build/resources/logo.bmp b/python/edu/build/resources/logo.bmp
deleted file mode 100644
index 0f0f82d..0000000
--- a/python/edu/build/resources/logo.bmp
+++ /dev/null
Binary files differ
diff --git a/python/edu/build/resources/logo.png b/python/edu/build/resources/logo.png
new file mode 100644
index 0000000..1ccbc07
--- /dev/null
+++ b/python/edu/build/resources/logo.png
Binary files differ
diff --git a/python/edu/learn-python/gen/icons/StudyIcons.java b/python/edu/learn-python/gen/icons/StudyIcons.java
new file mode 100644
index 0000000..2840910
--- /dev/null
+++ b/python/edu/learn-python/gen/icons/StudyIcons.java
@@ -0,0 +1,29 @@
+package icons;
+
+import com.intellij.openapi.util.IconLoader;
+
+import javax.swing.*;
+
+/**
+ * NOTE THIS FILE IS AUTO-GENERATED
+ * DO NOT EDIT IT BY HAND, run build/scripts/icons.gant instead
+ */
+public class StudyIcons {
+  private static Icon load(String path) {
+    return IconLoader.getIcon(path, StudyIcons.class);
+  }
+
+  public static final Icon Add = load("/icons/com/jetbrains/python/edu/add.png"); // 16x16
+  public static final Icon Checked = load("/icons/com/jetbrains/python/edu/checked.png"); // 32x32
+  public static final Icon Failed = load("/icons/com/jetbrains/python/edu/failed.png"); // 32x32
+  public static final Icon Next = load("/icons/com/jetbrains/python/edu/next.png"); // 24x24
+  public static final Icon Playground = load("/icons/com/jetbrains/python/edu/playground.png"); // 32x28
+  public static final Icon Prev = load("/icons/com/jetbrains/python/edu/prev.png"); // 24x24
+  public static final Icon Refresh = load("/icons/com/jetbrains/python/edu/refresh.png"); // 16x16
+  public static final Icon Refresh24 = load("/icons/com/jetbrains/python/edu/refresh24.png"); // 24x24
+  public static final Icon Resolve = load("/icons/com/jetbrains/python/edu/resolve.png"); // 24x24
+  public static final Icon Run = load("/icons/com/jetbrains/python/edu/Run.png"); // 24x24
+  public static final Icon ShowHint = load("/icons/com/jetbrains/python/edu/showHint.png"); // 24x24
+  public static final Icon Unchecked = load("/icons/com/jetbrains/python/edu/unchecked.png"); // 32x32
+  public static final Icon WatchInput = load("/icons/com/jetbrains/python/edu/WatchInput.png"); // 24x24
+}
diff --git a/python/edu/learn-python/learn-python.iml b/python/edu/learn-python/learn-python.iml
new file mode 100644
index 0000000..bd539d1
--- /dev/null
+++ b/python/edu/learn-python/learn-python.iml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="platform-impl" />
+    <orderEntry type="module" module-name="python-community" />
+    <orderEntry type="module" module-name="lang-impl" />
+    <orderEntry type="library" name="gson" level="project" />
+    <orderEntry type="library" name="JUnit4" level="project" />
+  </component>
+</module>
+
diff --git a/python/edu/learn-python/resources/META-INF/plugin.xml b/python/edu/learn-python/resources/META-INF/plugin.xml
new file mode 100644
index 0000000..ec828eb
--- /dev/null
+++ b/python/edu/learn-python/resources/META-INF/plugin.xml
@@ -0,0 +1,73 @@
+<!--suppress XmlUnboundNsPrefix -->
+<idea-plugin version="2">
+  <id>com.jetbrains.python.edu.learn-python</id>
+  <name>Educational plugin for PyCharm</name>
+  <version>1.0</version>
+  <vendor email="support@yourcompany.com" url="http://www.yourcompany.com">YourCompany</vendor>
+
+  <description><![CDATA[
+
+      ]]></description>
+
+  <change-notes><![CDATA[
+
+      ]]>
+  </change-notes>
+
+  <!--depends>com.intellij.modules.python</depends-->
+
+  <!-- please see http://confluence.jetbrains.net/display/IDEADEV/Plugin+Compatibility+with+IntelliJ+Platform+Products
+       on how to target different products -->
+
+  <depends>com.intellij.modules.lang</depends>
+  <depends>com.intellij.modules.python</depends>
+  <application-components>
+  </application-components>
+
+  <project-components>
+    <component>
+      <implementation-class>com.jetbrains.python.edu.StudyTaskManager</implementation-class>
+      <interface-class>com.jetbrains.python.edu.StudyTaskManager</interface-class>
+    </component>
+  </project-components>
+
+  <application-components>
+    <component>
+      <implementation-class>com.jetbrains.python.edu.StudyInitialConfigurator</implementation-class>
+      <headless-implementation-class/>
+    </component>
+  </application-components>
+
+  <actions>
+    <action id="CheckAction" class="com.jetbrains.python.edu.actions.StudyCheckAction" text="check"
+            description="Runs tests for current tasks" icon="/icons/icon.jpg">
+    </action>
+    <action id="PrevWindowAction" class="com.jetbrains.python.edu.actions.StudyPrevWindowAction" text="PrevWindowAction" description="prev">
+    </action>
+
+    <action id="NextWindow" class="com.jetbrains.python.edu.actions.StudyNextWindowAction" text="NextWindowAction" description="next">
+    </action>
+    <action id="NextTaskAction" class="com.jetbrains.python.edu.actions.StudyNextStudyTaskAction" text="NextTaskAction" description="Next Task"/>
+    <action id="PreviousTaskAction" class="com.jetbrains.python.edu.actions.StudyPreviousStudyTaskAction" text="PreviousTaskAction"
+            description="Previous Task"/>
+    <action id="RefreshTaskAction" class="com.jetbrains.python.edu.actions.StudyRefreshTaskAction" text="RefreshTaskAction"
+            description="Refresh current task"/>
+    <action id="WatchInputAction" class="com.jetbrains.python.edu.actions.StudyEditInputAction" text="WatchInputAction"
+            description="watch input"/>
+    <action id="StudyRunAction" class="com.jetbrains.python.edu.actions.StudyRunAction" text="StudyRunAction" description="run your code"/>
+    <action id="ShowHintAction" class="com.jetbrains.python.edu.actions.StudyShowHintAction" text="Show hint"
+            description="show hint">
+      <add-to-group group-id="MainToolBar" anchor="last"/>
+    </action>
+  </actions>
+
+  <extensions defaultExtensionNs="com.intellij">
+    <toolWindow id="Course Description" anchor="right" factoryClass="com.jetbrains.python.edu.ui.StudyToolWindowFactory" conditionClass="com.jetbrains.python.edu.ui.StudyCondition"/>
+    <fileEditorProvider implementation="com.jetbrains.python.edu.editor.StudyFileEditorProvider"/>
+    <directoryProjectGenerator implementation="com.jetbrains.python.edu.StudyDirectoryProjectGenerator"/>
+    <treeStructureProvider implementation="com.jetbrains.python.edu.projectView.StudyTreeStructureProvider"/>
+    <highlightErrorFilter implementation="com.jetbrains.python.edu.StudyHighlightErrorFilter"/>
+    <applicationService serviceInterface="com.intellij.openapi.fileEditor.impl.EditorEmptyTextPainter"
+        serviceImplementation="com.jetbrains.python.edu.StudyInstructionPainter" overrides="true"/>
+  </extensions>
+</idea-plugin>
\ No newline at end of file
diff --git a/python/edu/learn-python/resources/com/jetbrains/python/edu/user_tester.py b/python/edu/learn-python/resources/com/jetbrains/python/edu/user_tester.py
new file mode 100644
index 0000000..c17e6cd
--- /dev/null
+++ b/python/edu/learn-python/resources/com/jetbrains/python/edu/user_tester.py
@@ -0,0 +1,58 @@
+import sys
+import imp
+import os
+import subprocess
+
+USER_TESTS = "userTests"
+
+TEST_FAILED = "FAILED"
+
+TEST_PASSED = "PASSED"
+
+INPUT = "input"
+OUTPUT = "output"
+
+
+def get_index(logical_name, full_name):
+    logical_name_len = len(logical_name)
+    if full_name[:logical_name_len] == logical_name:
+        return int(full_name[logical_name_len])
+    return -1
+
+
+def process_user_tests(file_path):
+    user_tests = []
+    imp.load_source('user_file', file_path)
+    user_tests_dir_path = os.path.abspath(os.path.join(file_path, os.pardir, USER_TESTS))
+    user_test_files = os.listdir(user_tests_dir_path)
+    for user_file in user_test_files:
+        index = get_index(INPUT, user_file)
+        if index == -1:
+            continue
+        output = OUTPUT + str(index)
+        if output in user_test_files:
+            input_path = os.path.abspath(os.path.join(user_tests_dir_path, user_file))
+            output_path = os.path.abspath(os.path.join(user_tests_dir_path, output))
+            user_tests.append((input_path, output_path, index))
+    return sorted(user_tests, key=(lambda x: x[2]))
+
+
+def run_user_test(python, executable_path):
+    user_tests = process_user_tests(executable_path)
+    for test in user_tests:
+        input, output, index = test
+        test_output = subprocess.check_output([python, executable_path, input])
+        expected_output = open(output).read()
+        test_status = TEST_PASSED if test_output == expected_output else TEST_FAILED
+        print "TEST" + str(index) + " " + test_status
+        print "OUTPUT:"
+        print test_output + "\n"
+        if test_status == TEST_FAILED:
+            print "EXPECTED OUTPUT:"
+            print expected_output + "\n"
+
+
+if __name__ == "__main__":
+    python = sys.argv[1]
+    executable_path = sys.argv[2]
+    run_user_test(python , executable_path)
\ No newline at end of file
diff --git a/python/edu/learn-python/resources/courses/introduction_course.zip b/python/edu/learn-python/resources/courses/introduction_course.zip
new file mode 100644
index 0000000..f3b24f2
--- /dev/null
+++ b/python/edu/learn-python/resources/courses/introduction_course.zip
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/Run.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/Run.png
new file mode 100644
index 0000000..27a6e36
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/Run.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/WatchInput.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/WatchInput.png
new file mode 100644
index 0000000..4992191
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/WatchInput.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/add.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/add.png
new file mode 100644
index 0000000..9494f2d
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/add.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/checked.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/checked.png
new file mode 100644
index 0000000..4105a01
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/checked.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/failed.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/failed.png
new file mode 100644
index 0000000..e2aaa55
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/failed.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/icon.jpg b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/icon.jpg
new file mode 100644
index 0000000..3a9716e
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/icon.jpg
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/next.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/next.png
new file mode 100644
index 0000000..dd1a5d9
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/next.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/playground.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/playground.png
new file mode 100644
index 0000000..d12a751
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/playground.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/prev.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/prev.png
new file mode 100644
index 0000000..0656f81
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/prev.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/refresh.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/refresh.png
new file mode 100644
index 0000000..d595f6b
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/refresh.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/refresh24.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/refresh24.png
new file mode 100644
index 0000000..218f075
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/refresh24.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/resolve.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/resolve.png
new file mode 100644
index 0000000..7ef960b
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/resolve.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/resolve_dark.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/resolve_dark.png
new file mode 100644
index 0000000..99aaa1d
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/resolve_dark.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/showHint.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/showHint.png
new file mode 100644
index 0000000..f10fd56
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/showHint.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/unchecked.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/unchecked.png
new file mode 100644
index 0000000..2145982
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/unchecked.png
Binary files differ
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDirectoryProjectGenerator.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDirectoryProjectGenerator.java
new file mode 100644
index 0000000..d4831d9
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDirectoryProjectGenerator.java
@@ -0,0 +1,393 @@
+package com.jetbrains.python.edu;
+
+import com.google.gson.*;
+import com.google.gson.stream.JsonReader;
+import com.intellij.facet.ui.FacetEditorValidator;
+import com.intellij.facet.ui.FacetValidatorsManager;
+import com.intellij.facet.ui.ValidationResult;
+import com.intellij.lang.javascript.boilerplate.GithubDownloadUtil;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.platform.DirectoryProjectGenerator;
+import com.intellij.platform.templates.github.GeneratorException;
+import com.intellij.platform.templates.github.ZipUtil;
+import com.jetbrains.python.edu.course.Course;
+import com.jetbrains.python.edu.course.CourseInfo;
+import com.jetbrains.python.edu.ui.StudyNewProjectPanel;
+import com.jetbrains.python.newProject.PythonProjectGenerator;
+import icons.StudyIcons;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.io.*;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+public class StudyDirectoryProjectGenerator extends PythonProjectGenerator implements DirectoryProjectGenerator {
+  private static final Logger LOG = Logger.getInstance(StudyDirectoryProjectGenerator.class.getName());
+  private static final String REPO_URL = "https://github.com/JetBrains/pycharm-courses/archive/master.zip";
+  private static final String USER_NAME = "PyCharm";
+  private static final String COURSE_META_FILE = "course.json";
+  private static final String COURSE_NAME_ATTRIBUTE = "name";
+  private static final Pattern CACHE_PATTERN = Pattern.compile("(name=(.*)) (path=(.*course.json)) (author=(.*)) (description=(.*))");
+  private static final String REPOSITORY_NAME = "pycharm-courses";
+  public static final String AUTHOR_ATTRIBUTE = "author";
+  private final File myCoursesDir = new File(PathManager.getConfigPath(), "courses");
+  private static final String CACHE_NAME = "courseNames.txt";
+  private Map<CourseInfo, File> myCourses = new HashMap<CourseInfo, File>();
+  private File mySelectedCourseFile;
+  private Project myProject;
+  public ValidationResult myValidationResult = new ValidationResult("selected course is not valid");
+
+  @Nls
+  @NotNull
+  @Override
+  public String getName() {
+    return "Study project";
+  }
+
+
+  public void setCourses(Map<CourseInfo, File> courses) {
+    myCourses = courses;
+  }
+
+  /**
+   * Finds selected course in courses by name.
+   *
+   * @param courseName name of selected course
+   */
+  public void setSelectedCourse(@NotNull CourseInfo courseName) {
+    File courseFile = myCourses.get(courseName);
+    if (courseFile == null) {
+      LOG.error("invalid course in list");
+    }
+    mySelectedCourseFile = courseFile;
+  }
+
+  /**
+   * Adds course to courses specified in params
+   *
+   * @param courseDir must be directory containing course file
+   * @return added course name or null if course is invalid
+   */
+  @Nullable
+  private static CourseInfo addCourse(Map<CourseInfo, File> courses, File courseDir) {
+    if (courseDir.isDirectory()) {
+      File[] courseFiles = courseDir.listFiles(new FilenameFilter() {
+        @Override
+        public boolean accept(File dir, String name) {
+          return name.equals(COURSE_META_FILE);
+        }
+      });
+      if (courseFiles.length != 1) {
+        LOG.info("User tried to add course with more than one or without course files");
+        return null;
+      }
+      File courseFile = courseFiles[0];
+      CourseInfo courseInfo = getCourseInfo(courseFile);
+      if (courseInfo != null) {
+        courses.put(courseInfo, courseFile);
+      }
+      return courseInfo;
+    }
+    return null;
+  }
+
+
+  /**
+   * Adds course from zip archive to courses
+   *
+   * @return added course name or null if course is invalid
+   */
+  @Nullable
+  public CourseInfo addLocalCourse(String zipFilePath) {
+    File file = new File(zipFilePath);
+    try {
+      String fileName = file.getName();
+      String unzippedName = fileName.substring(0, fileName.indexOf("."));
+      File courseDir = new File(myCoursesDir, unzippedName);
+      ZipUtil.unzip(null, courseDir, file, null, null, true);
+      CourseInfo courseName = addCourse(myCourses, courseDir);
+      flushCache();
+      return courseName;
+    }
+    catch (IOException e) {
+      LOG.error("Failed to unzip course archive");
+      LOG.error(e);
+    }
+    return null;
+  }
+
+  @Nullable
+  @Override
+  public Object showGenerationSettings(VirtualFile baseDir) throws ProcessCanceledException {
+    return null;
+  }
+
+  @Nullable
+  @Override
+  public Icon getLogo() {
+    return StudyIcons.Playground;
+  }
+
+
+  @Override
+  public void generateProject(@NotNull final Project project, @NotNull final VirtualFile baseDir,
+                              @Nullable Object settings, @NotNull Module module) {
+
+    myProject = project;
+    Reader reader = null;
+    try {
+      reader = new InputStreamReader(new FileInputStream(mySelectedCourseFile));
+      Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
+      Course course = gson.fromJson(reader, Course.class);
+      course.init(false);
+      course.create(baseDir, new File(mySelectedCourseFile.getParent()));
+      course.setResourcePath(mySelectedCourseFile.getAbsolutePath());
+      VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
+      StudyTaskManager.getInstance(project).setCourse(course);
+    }
+    catch (FileNotFoundException e) {
+      LOG.error(e);
+    }
+    finally {
+      StudyUtils.closeSilently(reader);
+    }
+  }
+
+  /**
+   * Downloads courses from {@link com.jetbrains.python.edu.StudyDirectoryProjectGenerator#REPO_URL}
+   * and unzips them into {@link com.jetbrains.python.edu.StudyDirectoryProjectGenerator#myCoursesDir}
+   */
+
+  public void downloadAndUnzip(boolean needProgressBar) {
+    File outputFile = new File(PathManager.getConfigPath(), "courses.zip");
+    try {
+      if (!needProgressBar) {
+        GithubDownloadUtil.downloadAtomically(null, REPO_URL,
+                                              outputFile, USER_NAME, REPOSITORY_NAME);
+      }
+      else {
+        GithubDownloadUtil.downloadContentToFileWithProgressSynchronously(myProject, REPO_URL, "downloading courses", outputFile, USER_NAME,
+                                                                          REPOSITORY_NAME, false);
+      }
+      if (outputFile.exists()) {
+        ZipUtil.unzip(null, myCoursesDir, outputFile, null, null, true);
+        if (!outputFile.delete()) {
+          LOG.error("Failed to delete", outputFile.getName());
+        }
+        File[] files = myCoursesDir.listFiles();
+        if (files != null) {
+          for (File file : files) {
+            String fileName = file.getName();
+            if (StudyUtils.isZip(fileName)) {
+              ZipUtil.unzip(null, new File(myCoursesDir, fileName.substring(0, fileName.indexOf("."))), file, null, null, true);
+              if (!file.delete()) {
+                LOG.error("Failed to delete", fileName);
+              }
+            }
+          }
+        }
+      } else {
+        LOG.debug("failed to download course");
+      }
+    }
+    catch (IOException e) {
+      LOG.error(e);
+    }
+    catch (GeneratorException e) {
+      LOG.error(e);
+    }
+  }
+
+  public Map<CourseInfo, File> getLoadedCourses() {
+    return myCourses;
+  }
+
+  /**
+   * Parses courses located in {@link com.jetbrains.python.edu.StudyDirectoryProjectGenerator#myCoursesDir}
+   * to {@link com.jetbrains.python.edu.StudyDirectoryProjectGenerator#myCourses}
+   *
+   * @return map with course names and course files location
+   */
+  public Map<CourseInfo, File> loadCourses() {
+    Map<CourseInfo, File> courses = new HashMap<CourseInfo, File>();
+    if (myCoursesDir.exists()) {
+      File[] courseDirs = myCoursesDir.listFiles(new FileFilter() {
+        @Override
+        public boolean accept(File pathname) {
+          return pathname.isDirectory();
+        }
+      });
+      for (File courseDir : courseDirs) {
+        addCourse(courses, courseDir);
+      }
+    }
+    return courses;
+  }
+
+  /**
+   * Parses course json meta file and finds course name
+   *
+   * @return information about course or null if course file is invalid
+   */
+  @Nullable
+  private static CourseInfo getCourseInfo(File courseFile) {
+    CourseInfo courseInfo = null;
+    BufferedReader reader = null;
+    try {
+      if (courseFile.getName().equals(COURSE_META_FILE)) {
+        reader = new BufferedReader(new InputStreamReader(new FileInputStream(courseFile)));
+        JsonReader r = new JsonReader(reader);
+        JsonParser parser = new JsonParser();
+        JsonElement el = parser.parse(r);
+        String courseName = el.getAsJsonObject().get(COURSE_NAME_ATTRIBUTE).getAsString();
+        String courseAuthor = el.getAsJsonObject().get(AUTHOR_ATTRIBUTE).getAsString();
+        String courseDescription = el.getAsJsonObject().get("description").getAsString();
+        courseInfo = new CourseInfo(courseName, courseAuthor, courseDescription);
+      }
+    }
+    catch (Exception e) {
+      //error will be shown in UI
+    }
+    finally {
+      StudyUtils.closeSilently(reader);
+    }
+    return courseInfo;
+  }
+
+  @NotNull
+  @Override
+  public ValidationResult validate(@NotNull String s) {
+    return myValidationResult;
+  }
+
+  public void setValidationResult(ValidationResult validationResult) {
+    myValidationResult = validationResult;
+  }
+
+  /**
+   * @return courses from memory or from cash file or parses course directory
+   */
+  public Map<CourseInfo, File> getCourses() {
+    if (!myCourses.isEmpty()) {
+      return myCourses;
+    }
+    if (myCoursesDir.exists()) {
+      File cacheFile = new File(myCoursesDir, CACHE_NAME);
+      if (cacheFile.exists()) {
+        myCourses = getCoursesFromCache(cacheFile);
+        if (!myCourses.isEmpty()) {
+          return myCourses;
+        }
+      }
+      myCourses = loadCourses();
+      if (!myCourses.isEmpty()) {
+        return myCourses;
+      }
+    }
+    downloadAndUnzip(false);
+    myCourses = loadCourses();
+    flushCache();
+    return myCourses;
+  }
+
+  /**
+   * Writes courses to cash file {@link com.jetbrains.python.edu.StudyDirectoryProjectGenerator#CACHE_NAME}
+   */
+  @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
+  public void flushCache() {
+    File cashFile = new File(myCoursesDir, CACHE_NAME);
+    PrintWriter writer = null;
+    try {
+      if (!cashFile.exists()) {
+        final boolean created = cashFile.createNewFile();
+        if (!created) {
+          LOG.error("Cannot flush courses cache. Can't create " + CACHE_NAME + " file");
+          return;
+        }
+      }
+      writer = new PrintWriter(cashFile);
+      for (Map.Entry<CourseInfo, File> course : myCourses.entrySet()) {
+        CourseInfo courseInfo = course.getKey();
+        String line = String
+          .format("name=%s path=%s author=%s description=%s", courseInfo.getName(), course.getValue(), courseInfo.getAuthor(),
+                  courseInfo.getDescription());
+        writer.println(line);
+      }
+    }
+    catch (FileNotFoundException e) {
+      LOG.error(e);
+    }
+    catch (IOException e) {
+      LOG.error(e);
+    }
+    finally {
+      StudyUtils.closeSilently(writer);
+    }
+  }
+
+  /**
+   * Loads courses from {@link com.jetbrains.python.edu.StudyDirectoryProjectGenerator#CACHE_NAME} file
+   *
+   * @return map of course names and course files
+   */
+  @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
+  private static Map<CourseInfo, File> getCoursesFromCache(File cashFile) {
+    Map<CourseInfo, File> coursesFromCash = new HashMap<CourseInfo, File>();
+    BufferedReader reader =  null;
+    try {
+       reader = new BufferedReader(new InputStreamReader(new FileInputStream(cashFile)));
+      String line;
+
+      while ((line = reader.readLine()) != null) {
+        Matcher matcher = CACHE_PATTERN.matcher(line);
+        if (matcher.matches()) {
+          String courseName = matcher.group(2);
+          File file = new File(matcher.group(4));
+          String author = matcher.group(6);
+          String description = matcher.group(8);
+          CourseInfo courseInfo = new CourseInfo(courseName, author, description);
+          if (file.exists()) {
+            coursesFromCash.put(courseInfo, file);
+          }
+        }
+      }
+    }
+    catch (FileNotFoundException e) {
+      LOG.error(e);
+    }
+    catch (IOException e) {
+      LOG.error(e);
+    }
+    finally {
+      StudyUtils.closeSilently(reader);
+    }
+    return coursesFromCash;
+  }
+
+  @Nullable
+  @Override
+  public JPanel extendBasePanel() throws ProcessCanceledException {
+    StudyNewProjectPanel settingsPanel = new StudyNewProjectPanel(this);
+    settingsPanel.registerValidators(new FacetValidatorsManager() {
+      public void registerValidator(FacetEditorValidator validator, JComponent... componentsToWatch) {
+        throw new UnsupportedOperationException();
+      }
+      public void validate() {
+        fireStateChanged();
+      }
+    });
+    return settingsPanel.getContentPanel();
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDocumentListener.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDocumentListener.java
new file mode 100644
index 0000000..9fdcf70
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDocumentListener.java
@@ -0,0 +1,65 @@
+package com.jetbrains.python.edu;
+
+
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.LogicalPosition;
+import com.intellij.openapi.editor.event.DocumentAdapter;
+import com.intellij.openapi.editor.event.DocumentEvent;
+import com.intellij.openapi.editor.impl.event.DocumentEventImpl;
+import com.jetbrains.python.edu.course.TaskFile;
+import com.jetbrains.python.edu.course.TaskWindow;
+
+/**
+ * author: liana
+ * data: 7/16/14.
+ * Listens changes in study files and updates
+ * coordinates of all the windows in current task file
+ */
+public class StudyDocumentListener extends DocumentAdapter {
+  private final TaskFile myTaskFile;
+  private int oldLine;
+  private int oldLineStartOffset;
+  private TaskWindow myTaskWindow;
+
+  public StudyDocumentListener(TaskFile taskFile) {
+    myTaskFile = taskFile;
+  }
+
+
+  //remembering old end before document change because of problems
+  // with fragments containing "\n"
+  @Override
+  public void beforeDocumentChange(DocumentEvent e) {
+    int offset = e.getOffset();
+    int oldEnd = offset + e.getOldLength();
+    Document document = e.getDocument();
+    oldLine = document.getLineNumber(oldEnd);
+    oldLineStartOffset = document.getLineStartOffset(oldLine);
+    int line = document.getLineNumber(offset);
+    int offsetInLine = offset - document.getLineStartOffset(line);
+    LogicalPosition pos = new LogicalPosition(line, offsetInLine);
+    myTaskWindow = myTaskFile.getTaskWindow(document, pos);
+
+  }
+
+  @Override
+  public void documentChanged(DocumentEvent e) {
+    if (e instanceof DocumentEventImpl) {
+      DocumentEventImpl event = (DocumentEventImpl)e;
+      Document document = e.getDocument();
+      int offset = e.getOffset();
+      int change = event.getNewLength() - event.getOldLength();
+      if (myTaskWindow != null) {
+        int newLength = myTaskWindow.getLength() + change;
+        myTaskWindow.setLength(newLength <= 0 ? 0 : newLength);
+      }
+      int newEnd = offset + event.getNewLength();
+      int newLine = document.getLineNumber(newEnd);
+      int lineChange = newLine - oldLine;
+      myTaskFile.incrementLines(oldLine + 1, lineChange);
+      int newEndOffsetInLine = offset + e.getNewLength() - document.getLineStartOffset(newLine);
+      int oldEndOffsetInLine = offset + e.getOldLength() - oldLineStartOffset;
+      myTaskFile.updateLine(lineChange, oldLine, newEndOffsetInLine, oldEndOffsetInLine);
+    }
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyEditorFactoryListener.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyEditorFactoryListener.java
new file mode 100644
index 0000000..7565d76
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyEditorFactoryListener.java
@@ -0,0 +1,100 @@
+package com.jetbrains.python.edu;
+
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.LogicalPosition;
+import com.intellij.openapi.editor.event.EditorFactoryEvent;
+import com.intellij.openapi.editor.event.EditorFactoryListener;
+import com.intellij.openapi.editor.event.EditorMouseAdapter;
+import com.intellij.openapi.editor.event.EditorMouseEvent;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.jetbrains.python.edu.course.StudyStatus;
+import com.jetbrains.python.edu.course.TaskFile;
+import com.jetbrains.python.edu.course.TaskWindow;
+import com.jetbrains.python.edu.editor.StudyEditor;
+import org.jetbrains.annotations.NotNull;
+
+import java.awt.*;
+
+
+class StudyEditorFactoryListener implements EditorFactoryListener {
+
+  /**
+   * draws selected task window if there is one located in mouse position
+   */
+  private static class WindowSelectionListener extends EditorMouseAdapter {
+    private final TaskFile myTaskFile;
+
+    WindowSelectionListener(TaskFile taskFile) {
+      myTaskFile = taskFile;
+    }
+
+    @Override
+    public void mouseClicked(EditorMouseEvent e) {
+      Editor editor = e.getEditor();
+      Point point = e.getMouseEvent().getPoint();
+      LogicalPosition pos = editor.xyToLogicalPosition(point);
+      TaskWindow taskWindow = myTaskFile.getTaskWindow(editor.getDocument(), pos);
+      if (taskWindow != null) {
+        myTaskFile.setSelectedTaskWindow(taskWindow);
+        taskWindow.draw(editor, taskWindow.getStatus() != StudyStatus.Solved, true);
+      }
+      else {
+        myTaskFile.drawAllWindows(editor);
+      }
+    }
+  }
+
+  @Override
+  public void editorCreated(@NotNull final EditorFactoryEvent event) {
+    final Editor editor = event.getEditor();
+
+    final Project project = editor.getProject();
+    if (project == null) {
+      return;
+    }
+    ApplicationManager.getApplication().invokeLater(
+      new Runnable() {
+        @Override
+        public void run() {
+          ApplicationManager.getApplication().runWriteAction(new Runnable() {
+            @Override
+            public void run() {
+              Document document = editor.getDocument();
+              VirtualFile openedFile = FileDocumentManager.getInstance().getFile(document);
+              if (openedFile != null) {
+                StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
+                TaskFile taskFile = taskManager.getTaskFile(openedFile);
+                if (taskFile != null) {
+                  taskFile.navigateToFirstTaskWindow(editor);
+                  editor.addEditorMouseListener(new WindowSelectionListener(taskFile));
+                  StudyDocumentListener listener = new StudyDocumentListener(taskFile);
+                  StudyEditor.addDocumentListener(document, listener);
+                  document.addDocumentListener(listener);
+                  taskFile.drawAllWindows(editor);
+                }
+              }
+            }
+          });
+        }
+      }
+    );
+  }
+
+  @Override
+  public void editorReleased(@NotNull EditorFactoryEvent event) {
+    Editor editor = event.getEditor();
+    Document document = editor.getDocument();
+    StudyDocumentListener listener = StudyEditor.getListener(document);
+    if (listener != null) {
+      document.removeDocumentListener(listener);
+      StudyEditor.removeListener(document);
+    }
+    editor.getMarkupModel().removeAllHighlighters();
+    editor.getSelectionModel().removeSelection();
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyHighlightErrorFilter.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyHighlightErrorFilter.java
new file mode 100644
index 0000000..1377128
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyHighlightErrorFilter.java
@@ -0,0 +1,23 @@
+package com.jetbrains.python.edu;
+
+import com.intellij.codeInsight.highlighting.HighlightErrorFilter;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiErrorElement;
+import org.jetbrains.annotations.NotNull;
+import com.jetbrains.python.edu.course.TaskFile;
+
+/**
+ * author: liana
+ * data: 7/23/14.
+ */
+public class StudyHighlightErrorFilter extends HighlightErrorFilter {
+  @Override
+  public boolean shouldHighlightErrorElement(@NotNull final PsiErrorElement element) {
+    VirtualFile file = element.getContainingFile().getVirtualFile();
+    Project project = element.getProject();
+    StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
+    TaskFile taskFile = taskManager.getTaskFile(file);
+    return taskFile == null;
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyInitialConfigurator.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyInitialConfigurator.java
new file mode 100644
index 0000000..34776f3
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyInitialConfigurator.java
@@ -0,0 +1,67 @@
+package com.jetbrains.python.edu;
+
+import com.intellij.codeInsight.CodeInsightSettings;
+import com.intellij.ide.RecentProjectsManagerBase;
+import com.intellij.ide.ui.UISettings;
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.project.ex.ProjectManagerEx;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.platform.templates.github.ZipUtil;
+import com.intellij.util.PathUtil;
+import com.intellij.util.messages.MessageBus;
+import org.jetbrains.annotations.NonNls;
+
+import java.io.File;
+import java.io.IOException;
+
+@SuppressWarnings({"UtilityClassWithoutPrivateConstructor", "UtilityClassWithPublicConstructor"})
+public class StudyInitialConfigurator {
+  private static final Logger LOG = Logger.getInstance(StudyInitialConfigurator.class.getName()
+  );
+  @NonNls private static final String CONFIGURED = "StudyPyCharm.InitialConfiguration";
+
+
+  /**
+   * @noinspection UnusedParameters
+   */
+  public StudyInitialConfigurator(MessageBus bus,
+                                  UISettings uiSettings,
+                                  CodeInsightSettings codeInsightSettings,
+                                  final PropertiesComponent propertiesComponent,
+                                  FileTypeManager fileTypeManager,
+                                  final ProjectManagerEx projectManager,
+                                  RecentProjectsManagerBase recentProjectsManager) {
+    if (!propertiesComponent.getBoolean(CONFIGURED, false)) {
+      final File file = new File(getCoursesRoot(), "introduction_course.zip");
+      final File newCourses = new File(PathManager.getConfigPath(), "courses");
+      try {
+        FileUtil.createDirectory(newCourses);
+        String fileName = file.getName();
+        String unzippedName = fileName.substring(0, fileName.indexOf("."));
+        File courseDir = new File(newCourses, unzippedName);
+        ZipUtil.unzip(null, courseDir, file, null, null, true);
+
+      }
+      catch (IOException e) {
+        LOG.warn("Couldn't copy bundled courses " + e);
+      }
+    }
+  }
+
+  public static File getCoursesRoot() {
+    @NonNls String jarPath = PathUtil.getJarPathForClass(StudyInitialConfigurator.class);
+    if (jarPath.endsWith(".jar")) {
+      final File jarFile = new File(jarPath);
+
+
+      File pluginBaseDir = jarFile.getParentFile();
+      return new File(pluginBaseDir, "courses");
+    }
+
+    return new File(jarPath , "courses");
+  }
+
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyInstructionPainter.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyInstructionPainter.java
new file mode 100644
index 0000000..4f34bfb
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyInstructionPainter.java
@@ -0,0 +1,47 @@
+package com.jetbrains.python.edu;
+
+import com.intellij.openapi.fileEditor.impl.EditorEmptyTextPainter;
+import com.intellij.openapi.fileEditor.impl.EditorsSplitters;
+import com.intellij.openapi.util.Couple;
+import com.intellij.ui.Gray;
+import com.intellij.ui.JBColor;
+import com.intellij.util.PairFunction;
+import com.intellij.util.ui.GraphicsUtil;
+import com.intellij.util.ui.UIUtil;
+import com.jetbrains.python.edu.ui.StudyCondition;
+
+import java.awt.*;
+
+/**
+ * author: liana
+ * data: 7/29/14.
+ */
+public class StudyInstructionPainter extends EditorEmptyTextPainter {
+  @Override
+  public void paintEmptyText(final EditorsSplitters splitters, Graphics g) {
+    if (!StudyCondition.VALUE) {
+      super.paintEmptyText(splitters, g);
+      return;
+    }
+    boolean isDarkBackground = UIUtil.isUnderDarcula();
+    UIUtil.applyRenderingHints(g);
+    GraphicsUtil.setupAntialiasing(g, true, false);
+    g.setColor(new JBColor(isDarkBackground ? Gray._230 : Gray._80, Gray._160));
+    g.setFont(UIUtil.getLabelFont().deriveFont(isDarkBackground ? 24f : 20f));
+
+    UIUtil.TextPainter painter = new UIUtil.TextPainter().withLineSpacing(1.5f);
+
+    painter.appendLine("PyCharm Educational Edition").underlined(new JBColor(Gray._150, Gray._180));
+    painter.appendLine("Navigate to the next task window with Ctrl + Enter").smaller().withBullet();
+    painter.appendLine("Navigate between task windows with Ctrl + < and Ctrl + >").smaller().withBullet();
+    painter.appendLine("Get hint for the task window using Ctrl + 7").smaller().withBullet();
+    painter.appendLine("To see your progress open the 'Course Description' panel").smaller().withBullet();
+                       painter.draw(g, new PairFunction<Integer, Integer, Couple<Integer>>() {
+                         @Override
+                         public Couple<Integer> fun(Integer width, Integer height) {
+                           Dimension s = splitters.getSize();
+                           return Couple.of((s.width - width) / 2, (s.height - height) / 2);
+                         }
+                       });
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyResourceManger.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyResourceManger.java
new file mode 100644
index 0000000..38f1c2f
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyResourceManger.java
@@ -0,0 +1,5 @@
+package com.jetbrains.python.edu;
+
+public interface StudyResourceManger {
+  String USER_TESTER = "user_tester.py";
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyTaskManager.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyTaskManager.java
new file mode 100644
index 0000000..213c1f7
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyTaskManager.java
@@ -0,0 +1,296 @@
+package com.jetbrains.python.edu;
+
+import com.intellij.ide.ui.UISettings;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.actionSystem.ex.AnActionListener;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.components.*;
+import com.intellij.openapi.editor.EditorFactory;
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.keymap.Keymap;
+import com.intellij.openapi.keymap.KeymapManager;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.DumbAwareRunnable;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.startup.StartupManager;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileAdapter;
+import com.intellij.openapi.vfs.VirtualFileEvent;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.openapi.wm.*;
+import com.intellij.util.xmlb.XmlSerializer;
+import com.jetbrains.python.edu.actions.StudyNextWindowAction;
+import com.jetbrains.python.edu.actions.StudyPrevWindowAction;
+import com.jetbrains.python.edu.actions.StudyShowHintAction;
+import com.jetbrains.python.edu.course.Course;
+import com.jetbrains.python.edu.course.Lesson;
+import com.jetbrains.python.edu.course.Task;
+import com.jetbrains.python.edu.course.TaskFile;
+import com.jetbrains.python.edu.ui.StudyCondition;
+import com.jetbrains.python.edu.ui.StudyToolWindowFactory;
+import org.jdom.Element;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implementation of class which contains all the information
+ * about study in context of current project
+ */
+
+@State(
+  name = "StudySettings",
+  storages = {
+    @Storage(
+      id = "others",
+      file = "$PROJECT_CONFIG_DIR$/study_project.xml",
+      scheme = StorageScheme.DIRECTORY_BASED
+    )}
+)
+public class StudyTaskManager implements ProjectComponent, PersistentStateComponent<Element>, DumbAware {
+  public static final String COURSE_ELEMENT = "courseElement";
+  private static Map<String, StudyTaskManager> myTaskManagers = new HashMap<String, StudyTaskManager>();
+  private static Map<String, String> myDeletedShortcuts = new HashMap<String, String>();
+  private final Project myProject;
+  private Course myCourse;
+  private FileCreatedListener myListener;
+
+
+  public void setCourse(Course course) {
+    myCourse = course;
+  }
+
+  private StudyTaskManager(@NotNull final Project project) {
+    myTaskManagers.put(project.getBasePath(), this);
+    myProject = project;
+  }
+
+
+  @Nullable
+  public Course getCourse() {
+    return myCourse;
+  }
+
+  @Nullable
+  @Override
+  public Element getState() {
+    Element el = new Element("taskManager");
+    if (myCourse != null) {
+      Element courseElement = new Element(COURSE_ELEMENT);
+      XmlSerializer.serializeInto(myCourse, courseElement);
+      el.addContent(courseElement);
+    }
+    return el;
+  }
+
+  @Override
+  public void loadState(Element el) {
+    myCourse = XmlSerializer.deserialize(el.getChild(COURSE_ELEMENT), Course.class);
+    if (myCourse != null) {
+      myCourse.init(true);
+    }
+  }
+
+  @Override
+  public void projectOpened() {
+    ApplicationManager.getApplication().invokeLater(new DumbAwareRunnable() {
+      @Override
+      public void run() {
+        ApplicationManager.getApplication().runWriteAction(new DumbAwareRunnable() {
+          @Override
+          public void run() {
+            if (myCourse != null) {
+              StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new Runnable() {
+                @Override
+                public void run() {
+                  ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PROJECT_VIEW).show(null);
+                  FileEditor[] editors = FileEditorManager.getInstance(myProject).getSelectedEditors();
+                  if (editors.length > 0) {
+                    JComponent focusedComponent = editors[0].getPreferredFocusedComponent();
+                    if (focusedComponent != null) {
+                      IdeFocusManager.getInstance(myProject).requestFocus(focusedComponent, true);
+                    }
+                  }
+                }
+              });
+              UISettings.getInstance().HIDE_TOOL_STRIPES = false;
+              UISettings.getInstance().fireUISettingsChanged();
+              ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject);
+              String toolWindowId = StudyToolWindowFactory.STUDY_TOOL_WINDOW;
+              //TODO:decide smth with tool window position
+              try {
+                Method method = toolWindowManager.getClass().getDeclaredMethod("registerToolWindow", String.class,
+                                                                               JComponent.class,
+                                                                               ToolWindowAnchor.class,
+                                                                               boolean.class, boolean.class, boolean.class);
+                method.setAccessible(true);
+                method.invoke(toolWindowManager, toolWindowId, null, ToolWindowAnchor.LEFT, true, true, true);
+              }
+              catch (Exception e) {
+                final ToolWindow toolWindow = toolWindowManager.getToolWindow(toolWindowId);
+                if (toolWindow == null)
+                  toolWindowManager.registerToolWindow(toolWindowId, true, ToolWindowAnchor.RIGHT, myProject, true);
+              }
+
+              final ToolWindow studyToolWindow = toolWindowManager.getToolWindow(toolWindowId);
+              if (studyToolWindow != null) {
+                StudyUtils.updateStudyToolWindow(myProject);
+                studyToolWindow.show(null);
+              }
+              addShortcut(StudyNextWindowAction.SHORTCUT, StudyNextWindowAction.ACTION_ID);
+              addShortcut(StudyPrevWindowAction.SHORTCUT, StudyPrevWindowAction.ACTION_ID);
+              addShortcut(StudyShowHintAction.SHORTCUT, StudyShowHintAction.ACTION_ID);
+              addShortcut(StudyNextWindowAction.SHORTCUT2, StudyNextWindowAction.ACTION_ID);
+            }
+          }
+        });
+      }
+    });
+  }
+
+
+  private static void addShortcut(@NotNull final String shortcutString, @NotNull final String actionIdString) {
+    Keymap keymap = KeymapManager.getInstance().getActiveKeymap();
+    Shortcut studyActionShortcut = new KeyboardShortcut(KeyStroke.getKeyStroke(shortcutString), null);
+    String[] actionsIds = keymap.getActionIds(studyActionShortcut);
+    for (String actionId : actionsIds) {
+      myDeletedShortcuts.put(actionId, shortcutString);
+      keymap.removeShortcut(actionId, studyActionShortcut);
+    }
+    keymap.addShortcut(actionIdString, studyActionShortcut);
+  }
+
+  @Override
+  public void projectClosed() {
+    StudyCondition.VALUE = false;
+    if (myCourse != null) {
+      ToolWindowManager.getInstance(myProject).getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW).getContentManager()
+        .removeAllContents(false);
+      if (!myDeletedShortcuts.isEmpty()) {
+        for (Map.Entry<String, String> shortcut : myDeletedShortcuts.entrySet()) {
+          Keymap keymap = KeymapManager.getInstance().getActiveKeymap();
+          Shortcut actionShortcut = new KeyboardShortcut(KeyStroke.getKeyStroke(shortcut.getValue()), null);
+          keymap.addShortcut(shortcut.getKey(), actionShortcut);
+        }
+      }
+    }
+  }
+
+  @Override
+  public void initComponent() {
+    EditorFactory.getInstance().addEditorFactoryListener(new StudyEditorFactoryListener(), myProject);
+    ActionManager.getInstance().addAnActionListener(new AnActionListener() {
+      @Override
+      public void beforeActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) {
+        AnAction[] newGroupActions = ((ActionGroup)ActionManager.getInstance().getAction("NewGroup")).getChildren(null);
+        for (AnAction newAction : newGroupActions) {
+          if (newAction == action) {
+            myListener =  new FileCreatedListener();
+            VirtualFileManager.getInstance().addVirtualFileListener(myListener);
+            break;
+          }
+        }
+      }
+
+      @Override
+      public void afterActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) {
+        AnAction[] newGroupActions = ((ActionGroup)ActionManager.getInstance().getAction("NewGroup")).getChildren(null);
+        for (AnAction newAction : newGroupActions) {
+          if (newAction == action) {
+            VirtualFileManager.getInstance().removeVirtualFileListener(myListener);
+          }
+        }
+      }
+
+      @Override
+      public void beforeEditorTyping(char c, DataContext dataContext) {
+
+      }
+    });
+  }
+
+  @Override
+  public void disposeComponent() {
+  }
+
+  @NotNull
+  @Override
+  public String getComponentName() {
+    return "StudyTaskManager";
+  }
+
+  public static StudyTaskManager getInstance(@NotNull final Project project) {
+    StudyTaskManager item = myTaskManagers.get(project.getBasePath());
+    return item != null ? item : new StudyTaskManager(project);
+  }
+
+
+  @Nullable
+  public TaskFile getTaskFile(@NotNull final VirtualFile file) {
+    if (myCourse == null) {
+      return null;
+    }
+    VirtualFile taskDir = file.getParent();
+    if (taskDir != null) {
+      String taskDirName = taskDir.getName();
+      if (taskDirName.contains(Task.TASK_DIR)) {
+        VirtualFile lessonDir = taskDir.getParent();
+        if (lessonDir != null) {
+          String lessonDirName = lessonDir.getName();
+          int lessonIndex = StudyUtils.getIndex(lessonDirName, Lesson.LESSON_DIR);
+          List<Lesson> lessons = myCourse.getLessons();
+          if (!StudyUtils.indexIsValid(lessonIndex, lessons)) {
+            return null;
+          }
+          Lesson lesson = lessons.get(lessonIndex);
+          int taskIndex = StudyUtils.getIndex(taskDirName, Task.TASK_DIR);
+          List<Task> tasks = lesson.getTaskList();
+          if (!StudyUtils.indexIsValid(taskIndex, tasks)) {
+            return null;
+          }
+          Task task = tasks.get(taskIndex);
+          return task.getFile(file.getName());
+        }
+      }
+    }
+    return null;
+  }
+
+  class FileCreatedListener extends VirtualFileAdapter {
+    @Override
+    public void fileCreated(@NotNull VirtualFileEvent event) {
+      VirtualFile createdFile = event.getFile();
+      VirtualFile taskDir = createdFile.getParent();
+      String taskLogicalName = Task.TASK_DIR;
+      if (taskDir != null && taskDir.getName().contains(taskLogicalName)) {
+        int taskIndex = StudyUtils.getIndex(taskDir.getName(), taskLogicalName);
+        VirtualFile lessonDir = taskDir.getParent();
+        String lessonLogicalName = Lesson.LESSON_DIR;
+        if (lessonDir != null && lessonDir.getName().contains(lessonLogicalName)) {
+          int lessonIndex = StudyUtils.getIndex(lessonDir.getName(), lessonLogicalName);
+          if (myCourse != null) {
+            List<Lesson> lessons = myCourse.getLessons();
+            if (StudyUtils.indexIsValid(lessonIndex, lessons)) {
+              Lesson lesson = lessons.get(lessonIndex);
+              List<Task> tasks = lesson.getTaskList();
+              if (StudyUtils.indexIsValid(taskIndex, tasks)) {
+                Task task = tasks.get(taskIndex);
+                TaskFile taskFile = new TaskFile();
+                taskFile.init(task, false);
+                taskFile.setUserCreated(true);
+                task.getTaskFiles().put(createdFile.getName(), taskFile);
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyUtils.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyUtils.java
new file mode 100644
index 0000000..d3ac1da
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyUtils.java
@@ -0,0 +1,151 @@
+package com.jetbrains.python.edu;
+
+import com.intellij.ide.SaveAndSyncHandlerImpl;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.openapi.wm.ToolWindowManager;
+import com.intellij.util.ui.UIUtil;
+import com.jetbrains.python.edu.course.TaskFile;
+import com.jetbrains.python.edu.course.TaskWindow;
+import com.jetbrains.python.edu.editor.StudyEditor;
+import com.jetbrains.python.edu.ui.StudyToolWindowFactory;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.*;
+import java.util.Collection;
+
+public class StudyUtils {
+  private static final Logger LOG = Logger.getInstance(StudyUtils.class.getName());
+  public static void closeSilently(Closeable stream) {
+    if (stream != null) {
+      try {
+        stream.close();
+      }
+      catch (IOException e) {
+        // close silently
+      }
+    }
+  }
+
+  public static boolean isZip(String fileName) {
+    return fileName.contains(".zip");
+  }
+
+  public static <T> T getFirst(Iterable<T> container) {
+    return container.iterator().next();
+  }
+
+  public static boolean indexIsValid(int index, Collection collection) {
+    int size = collection.size();
+    return index >= 0 && index < size;
+  }
+
+  @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
+  @Nullable
+  public static String getFileText(String parentDir, String fileName, boolean wrapHTML) {
+
+    File inputFile = parentDir !=null ? new File(parentDir, fileName) : new File(fileName);
+    if (!inputFile.exists()) return null;
+    StringBuilder taskText = new StringBuilder();
+    BufferedReader reader = null;
+    try {
+      reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputFile)));
+      String line;
+      while ((line = reader.readLine()) != null) {
+        taskText.append(line).append("\n");
+        if (wrapHTML) {
+          taskText.append("<br>");
+        }
+      }
+      return wrapHTML ? UIUtil.toHtml(taskText.toString()) : taskText.toString();
+    }
+    catch (IOException e) {
+      LOG.error("Failed to get file text from file " + fileName, e);
+    }
+    finally {
+      closeSilently(reader);
+    }
+    return null;
+  }
+
+  public static void updateAction(AnActionEvent e) {
+    Presentation presentation = e.getPresentation();
+    presentation.setEnabled(false);
+    Project project = e.getProject();
+    if (project != null) {
+      FileEditor[] editors = FileEditorManager.getInstance(project).getAllEditors();
+      for (FileEditor editor : editors) {
+        if (editor instanceof StudyEditor) {
+          presentation.setEnabled(true);
+        }
+      }
+    }
+  }
+
+  public static void updateStudyToolWindow(Project project) {
+    ToolWindowManager.getInstance(project).getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW).getContentManager().removeAllContents(false);
+    StudyToolWindowFactory factory =  new StudyToolWindowFactory();
+    factory.createToolWindowContent(project, ToolWindowManager.getInstance(project).getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW));
+  }
+
+  public  static void synchronize() {
+    FileDocumentManager.getInstance().saveAllDocuments();
+    SaveAndSyncHandlerImpl.refreshOpenFiles();
+    VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
+  }
+
+  /**
+   * Gets number index in directory names like "task1", "lesson2"
+   *
+   * @param fullName    full name of directory
+   * @param logicalName part of name without index
+   * @return index of object
+   */
+  public static int getIndex(@NotNull final String fullName, @NotNull final String logicalName) {
+    if (!fullName.contains(logicalName)) {
+      throw new IllegalArgumentException();
+    }
+    return Integer.parseInt(fullName.substring(logicalName.length())) - 1;
+  }
+
+  @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
+  public static VirtualFile flushWindows(Document document, TaskFile taskFile, VirtualFile file) {
+    VirtualFile taskDir = file.getParent();
+    VirtualFile fileWindows = null;
+    if (taskDir != null) {
+      String name = file.getNameWithoutExtension() + "_windows";
+      PrintWriter printWriter = null;
+      try {
+
+        fileWindows = taskDir.createChildData(taskFile, name);
+        printWriter = new PrintWriter(new FileOutputStream(fileWindows.getPath()));
+        for (TaskWindow taskWindow : taskFile.getTaskWindows()) {
+          if (!taskWindow.isValid(document)) {
+            continue;
+          }
+          int start = taskWindow.getRealStartOffset(document);
+          String windowDescription = document.getText(new TextRange(start, start + taskWindow.getLength()));
+          printWriter.println("#study_plugin_window = " + windowDescription);
+        }
+      }
+      catch (IOException e) {
+       LOG.error(e);
+      }
+      finally {
+        closeSilently(printWriter);
+        synchronize();
+      }
+    }
+    return fileWindows;
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyCheckAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyCheckAction.java
new file mode 100644
index 0000000..f8e10c9
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyCheckAction.java
@@ -0,0 +1,340 @@
+package com.jetbrains.python.edu.actions;
+
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.configurations.GeneralCommandLine;
+import com.intellij.ide.projectView.ProjectView;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.command.CommandProcessor;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.ui.popup.Balloon;
+import com.intellij.openapi.ui.popup.BalloonBuilder;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.JBColor;
+import com.jetbrains.python.edu.StudyDocumentListener;
+import com.jetbrains.python.edu.StudyTaskManager;
+import com.jetbrains.python.edu.StudyUtils;
+import com.jetbrains.python.edu.course.*;
+import com.jetbrains.python.edu.editor.StudyEditor;
+import com.jetbrains.python.sdk.PythonSdkType;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.io.*;
+import java.util.*;
+import java.util.List;
+
+public class StudyCheckAction extends DumbAwareAction {
+
+  private static final Logger LOG = Logger.getInstance(StudyCheckAction.class.getName());
+  public static final String PYTHONPATH = "PYTHONPATH";
+
+  static class StudyTestRunner {
+    public static final String TEST_OK = "#study_plugin test OK";
+    private static final String TEST_FAILED = "#study_plugin FAILED + ";
+    private final Task myTask;
+    private final VirtualFile myTaskDir;
+
+    StudyTestRunner(Task task, VirtualFile taskDir) {
+      myTask = task;
+      myTaskDir = taskDir;
+    }
+
+    Process launchTests(Project project, String executablePath) throws ExecutionException {
+      Sdk sdk = PythonSdkType.findPythonSdk(ModuleManager.getInstance(project).getModules()[0]);
+      File testRunner = new File(myTaskDir.getPath(), myTask.getTestFile());
+      GeneralCommandLine commandLine = new GeneralCommandLine();
+      commandLine.setWorkDirectory(myTaskDir.getPath());
+      final Map<String, String> env = commandLine.getEnvironment();
+      final VirtualFile courseDir = project.getBaseDir();
+      if (courseDir != null)
+        env.put(PYTHONPATH, courseDir.getPath());
+      if (sdk != null) {
+        String pythonPath = sdk.getHomePath();
+        if (pythonPath != null) {
+          commandLine.setExePath(pythonPath);
+          commandLine.addParameter(testRunner.getPath());
+          final Course course = StudyTaskManager.getInstance(project).getCourse();
+          assert course != null;
+          commandLine.addParameter(new File(course.getResourcePath()).getParent());
+          commandLine.addParameter(FileUtil.toSystemDependentName(executablePath));
+          return commandLine.createProcess();
+        }
+      }
+      return null;
+    }
+
+
+    String getPassedTests(Process p) {
+      InputStream testOutput = p.getInputStream();
+      BufferedReader testOutputReader = new BufferedReader(new InputStreamReader(testOutput));
+      String line;
+      try {
+        while ((line = testOutputReader.readLine()) != null) {
+          if (line.contains(TEST_FAILED)) {
+             return line.substring(TEST_FAILED.length(), line.length());
+          }
+        }
+      }
+      catch (IOException e) {
+        LOG.error(e);
+      }
+      finally {
+        StudyUtils.closeSilently(testOutputReader);
+      }
+      return TEST_OK;
+    }
+  }
+
+  public void check(@NotNull final Project project) {
+    ApplicationManager.getApplication().runWriteAction(new Runnable() {
+      @Override
+      public void run() {
+        CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() {
+          @Override
+          public void run() {
+        final Editor selectedEditor = StudyEditor.getSelectedEditor(project);
+        if (selectedEditor != null) {
+          final FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
+          final VirtualFile openedFile = fileDocumentManager.getFile(selectedEditor.getDocument());
+          if (openedFile != null) {
+            StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
+            final TaskFile selectedTaskFile = taskManager.getTaskFile(openedFile);
+            List<VirtualFile> filesToDelete = new ArrayList<VirtualFile>();
+            if (selectedTaskFile != null) {
+              final VirtualFile taskDir = openedFile.getParent();
+              Task currentTask = selectedTaskFile.getTask();
+              StudyStatus oldStatus = currentTask.getStatus();
+              Map<String, TaskFile> taskFiles = selectedTaskFile.getTask().getTaskFiles();
+              for (Map.Entry<String, TaskFile> entry : taskFiles.entrySet()) {
+                String name = entry.getKey();
+                TaskFile taskFile = entry.getValue();
+                VirtualFile virtualFile = taskDir.findChild(name);
+                if (virtualFile == null) {
+                  continue;
+                }
+                VirtualFile windowFile = StudyUtils.flushWindows(FileDocumentManager.getInstance().getDocument(virtualFile), taskFile, virtualFile);
+                filesToDelete.add(windowFile);
+                FileDocumentManager.getInstance().saveAllDocuments();
+              }
+
+              StudyRunAction runAction = (StudyRunAction)ActionManager.getInstance().getAction(StudyRunAction.ACTION_ID);
+              if (runAction != null && currentTask.getTaskFiles().size() == 1) {
+                runAction.run(project);
+              }
+              final StudyTestRunner testRunner = new StudyTestRunner(currentTask, taskDir);
+              Process testProcess = null;
+              try {
+                testProcess = testRunner.launchTests(project, openedFile.getPath());
+              }
+              catch (ExecutionException e) {
+                LOG.error(e);
+              }
+              if (testProcess != null) {
+                String failedMessage = testRunner.getPassedTests(testProcess);
+                if (failedMessage.equals(StudyTestRunner.TEST_OK)) {
+                  currentTask.setStatus(StudyStatus.Solved, oldStatus);
+                  StudyUtils.updateStudyToolWindow(project);
+                  selectedTaskFile.drawAllWindows(selectedEditor);
+                  ProjectView.getInstance(project).refresh();
+                  for (VirtualFile file:filesToDelete) {
+                    try {
+                      file.delete(this);
+                    }
+                    catch (IOException e) {
+                      LOG.error(e);
+                    }
+                  }
+                  createTestResultPopUp("Congratulations!", JBColor.GREEN, project);
+                  return;
+                }
+                for (Map.Entry<String, TaskFile> entry : taskFiles.entrySet()) {
+                  String name = entry.getKey();
+                  TaskFile taskFile = entry.getValue();
+                  TaskFile answerTaskFile = new TaskFile();
+                  VirtualFile virtualFile = taskDir.findChild(name);
+                  if (virtualFile == null) {
+                    continue;
+                  }
+                  VirtualFile answerFile = getCopyWithAnswers(taskDir, virtualFile, taskFile, answerTaskFile);
+                  for (TaskWindow taskWindow : answerTaskFile.getTaskWindows()) {
+                    Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
+                    if (document == null) {
+                      continue;
+                    }
+                    if (!taskWindow.isValid(document)) {
+                      continue;
+                    }
+                    check(project, taskWindow, answerFile, answerTaskFile, taskFile, document, testRunner, virtualFile);
+                  }
+                  FileEditor fileEditor = FileEditorManager.getInstance(project).getSelectedEditor(virtualFile);
+                  Editor editor = null;
+                  if (fileEditor instanceof StudyEditor) {
+                    StudyEditor studyEditor = (StudyEditor) fileEditor;
+                    editor = studyEditor.getEditor();
+                  }
+
+                  if (editor != null) {
+                    taskFile.drawAllWindows(editor);
+                    StudyUtils.synchronize();
+                  }
+                  try {
+                    answerFile.delete(this);
+                  }
+                  catch (IOException e) {
+                    LOG.error(e);
+                  }
+                }
+                for (VirtualFile file:filesToDelete) {
+                  try {
+                    file.delete(this);
+                  }
+                  catch (IOException e) {
+                    LOG.error(e);
+                  }
+                }
+                currentTask.setStatus(StudyStatus.Failed, oldStatus);
+                StudyUtils.updateStudyToolWindow(project);
+                createTestResultPopUp(failedMessage, JBColor.RED, project);
+              }
+            }
+          }
+        }
+
+         }
+      });
+      }
+    });
+  }
+
+  private void check(Project project,
+                     TaskWindow taskWindow,
+                     VirtualFile answerFile,
+                     TaskFile answerTaskFile,
+                     TaskFile usersTaskFile,
+                     Document usersDocument,
+                     StudyTestRunner testRunner,
+                     VirtualFile openedFile) {
+
+    try {
+       VirtualFile windowCopy = answerFile.copy(this, answerFile.getParent(), answerFile.getNameWithoutExtension() + "_window" + taskWindow.getIndex() + ".py");
+      final FileDocumentManager documentManager = FileDocumentManager.getInstance();
+      final Document windowDocument = documentManager.getDocument(windowCopy);
+      if (windowDocument != null) {
+        StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
+        Course course = taskManager.getCourse();
+        Task task = usersTaskFile.getTask();
+        int taskNum = task.getIndex() + 1;
+        int lessonNum = task.getLesson().getIndex() + 1;
+        assert course != null;
+        String pathToResource = FileUtil.join(new File(course.getResourcePath()).getParent(), Lesson.LESSON_DIR + lessonNum,  Task.TASK_DIR + taskNum);
+        File resourceFile = new File(pathToResource, windowCopy.getName());
+        FileUtil.copy(new File(pathToResource, openedFile.getName()), resourceFile);
+        TaskFile windowTaskFile = new TaskFile();
+        TaskFile.copy(answerTaskFile, windowTaskFile);
+        StudyDocumentListener listener = new StudyDocumentListener(windowTaskFile);
+        windowDocument.addDocumentListener(listener);
+        int start = taskWindow.getRealStartOffset(windowDocument);
+        int end = start + taskWindow.getLength();
+        TaskWindow userTaskWindow = usersTaskFile.getTaskWindows().get(taskWindow.getIndex());
+        int userStart = userTaskWindow.getRealStartOffset(usersDocument);
+        int userEnd = userStart + userTaskWindow.getLength();
+        String text = usersDocument.getText(new TextRange(userStart, userEnd));
+        windowDocument.replaceString(start, end, text);
+        ApplicationManager.getApplication().runWriteAction(new Runnable() {
+          @Override
+          public void run() {
+            documentManager.saveDocument(windowDocument);
+          }
+        });
+        VirtualFile fileWindows = StudyUtils.flushWindows(windowDocument, windowTaskFile, windowCopy);
+        Process smartTestProcess = testRunner.launchTests(project, windowCopy.getPath());
+        boolean res = testRunner.getPassedTests(smartTestProcess).equals(StudyTestRunner.TEST_OK);
+        userTaskWindow.setStatus(res ? StudyStatus.Solved : StudyStatus.Failed, StudyStatus.Unchecked);
+        windowCopy.delete(this);
+        fileWindows.delete(this);
+        if (!resourceFile.delete()) {
+          LOG.error("failed to delete", resourceFile.getPath());
+        }
+      }
+    }
+    catch (IOException e) {
+      LOG.error(e);
+    }
+    catch (ExecutionException e) {
+      LOG.error(e);
+    }
+  }
+
+
+  private VirtualFile getCopyWithAnswers(final VirtualFile taskDir,
+                                         final VirtualFile file,
+                                         final TaskFile source,
+                                         TaskFile target) {
+    VirtualFile copy = null;
+    try {
+
+      copy = file.copy(this, taskDir, file.getNameWithoutExtension() +"_answers.py");
+      final FileDocumentManager documentManager = FileDocumentManager.getInstance();
+      final Document document = documentManager.getDocument(copy);
+      if (document != null) {
+        TaskFile.copy(source, target);
+        StudyDocumentListener listener = new StudyDocumentListener(target);
+        document.addDocumentListener(listener);
+        for (TaskWindow taskWindow : target.getTaskWindows()) {
+          if (!taskWindow.isValid(document)) {
+            continue;
+          }
+          final int start = taskWindow.getRealStartOffset(document);
+          final int end = start + taskWindow.getLength();
+          final String text = taskWindow.getPossibleAnswer();
+          document.replaceString(start, end, text);
+        }
+        ApplicationManager.getApplication().runWriteAction(new Runnable() {
+          @Override
+          public void run() {
+            documentManager.saveDocument(document);
+          }
+        });
+      }
+    }
+    catch (IOException e) {
+      LOG.error(e);
+    }
+
+
+    return copy;
+  }
+
+  private static void createTestResultPopUp(final String text, Color color, @NotNull final Project project) {
+    BalloonBuilder balloonBuilder =
+      JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(text, null, color, null);
+    Balloon balloon = balloonBuilder.createBalloon();
+    StudyEditor studyEditor = StudyEditor.getSelectedStudyEditor(project);
+    assert studyEditor != null;
+    JButton checkButton = studyEditor.getCheckButton();
+    balloon.showInCenterOf(checkButton);
+  }
+
+  @Override
+  public void actionPerformed(AnActionEvent e) {
+    Project project = e.getProject();
+    if (project != null) {
+      check(project);
+    }
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyEditInputAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyEditInputAction.java
new file mode 100644
index 0000000..5b9a6fe
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyEditInputAction.java
@@ -0,0 +1,213 @@
+package com.jetbrains.python.edu.actions;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.ui.UISettings;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.popup.JBPopup;
+import com.intellij.openapi.ui.popup.JBPopupAdapter;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.ui.popup.LightweightWindowEvent;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.wm.IdeFocusManager;
+import com.intellij.ui.tabs.TabInfo;
+import com.intellij.ui.tabs.TabsListener;
+import com.intellij.ui.tabs.impl.JBEditorTabs;
+import com.jetbrains.python.edu.StudyTaskManager;
+import com.jetbrains.python.edu.StudyUtils;
+import com.jetbrains.python.edu.course.Task;
+import com.jetbrains.python.edu.course.TaskFile;
+import com.jetbrains.python.edu.course.UserTest;
+import com.jetbrains.python.edu.editor.StudyEditor;
+import com.jetbrains.python.edu.ui.StudyTestContentPanel;
+import icons.StudyIcons;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class StudyEditInputAction extends DumbAwareAction {
+
+  public static final String TEST_TAB_NAME = "test";
+  public static final String USER_TEST_INPUT = "input";
+  public static final String USER_TEST_OUTPUT = "output";
+  private static final Logger LOG = Logger.getInstance(StudyEditInputAction.class.getName());
+  private JBEditorTabs tabbedPane;
+  private Map<TabInfo, UserTest> myEditableTabs = new HashMap<TabInfo, UserTest>();
+
+  public void showInput(final Project project) {
+    final Editor selectedEditor = StudyEditor.getSelectedEditor(project);
+    if (selectedEditor != null) {
+      FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
+      final VirtualFile openedFile = fileDocumentManager.getFile(selectedEditor.getDocument());
+      StudyTaskManager studyTaskManager = StudyTaskManager.getInstance(project);
+      assert openedFile != null;
+      TaskFile taskFile = studyTaskManager.getTaskFile(openedFile);
+      assert taskFile != null;
+      final Task currentTask = taskFile.getTask();
+      tabbedPane = new JBEditorTabs(project, ActionManager.getInstance(), IdeFocusManager.findInstance(), project);
+      tabbedPane.addListener(new TabsListener.Adapter() {
+        @Override
+        public void selectionChanged(TabInfo oldSelection, TabInfo newSelection) {
+          if (newSelection.getIcon() != null) {
+            int tabCount = tabbedPane.getTabCount();
+            VirtualFile taskDir = openedFile.getParent();
+            VirtualFile testsDir = taskDir.findChild(Task.USER_TESTS);
+            assert testsDir != null;
+            UserTest userTest = createUserTest(testsDir, currentTask);
+            userTest.setEditable(true);
+            StudyTestContentPanel testContentPanel = new StudyTestContentPanel(userTest);
+            TabInfo testTab = addTestTab(tabbedPane.getTabCount(), testContentPanel, currentTask, true);
+            myEditableTabs.put(testTab, userTest);
+            tabbedPane.addTabSilently(testTab, tabCount - 1);
+            tabbedPane.select(testTab, true);
+          }
+        }
+      });
+      List<UserTest> userTests = currentTask.getUserTests();
+      int i = 1;
+      for (UserTest userTest : userTests) {
+        String inputFileText = StudyUtils.getFileText(null, userTest.getInput(), false);
+        String outputFileText = StudyUtils.getFileText(null, userTest.getOutput(), false);
+        StudyTestContentPanel myContentPanel = new StudyTestContentPanel(userTest);
+        myContentPanel.addInputContent(inputFileText);
+        myContentPanel.addOutputContent(outputFileText);
+        TabInfo testTab = addTestTab(i, myContentPanel, currentTask, userTest.isEditable());
+        tabbedPane.addTabSilently(testTab, i - 1);
+        if (userTest.isEditable()) {
+          myEditableTabs.put(testTab, userTest);
+        }
+        i++;
+      }
+      TabInfo plusTab = new TabInfo(new JPanel());
+      plusTab.setIcon(StudyIcons.Add);
+      tabbedPane.addTabSilently(plusTab, tabbedPane.getTabCount());
+      final JBPopup hint =
+        JBPopupFactory.getInstance().createComponentPopupBuilder(tabbedPane.getComponent(), tabbedPane.getComponent())
+          .setResizable(true)
+          .setMovable(true)
+          .setRequestFocus(true)
+          .createPopup();
+      StudyEditor selectedStudyEditor = StudyEditor.getSelectedStudyEditor(project);
+      assert selectedStudyEditor != null;
+      hint.showInCenterOf(selectedStudyEditor.getComponent());
+      hint.addListener(new HintClosedListener(currentTask));
+    }
+  }
+
+
+  private static void flushBuffer(@NotNull final StringBuilder buffer, @NotNull final File file) {
+    PrintWriter printWriter = null;
+    try {
+      printWriter = new PrintWriter(new FileOutputStream(file));
+      printWriter.print(buffer.toString());
+    }
+    catch (FileNotFoundException e) {
+      LOG.error(e);
+    }
+    finally {
+      StudyUtils.closeSilently(printWriter);
+    }
+    StudyUtils.synchronize();
+  }
+
+  private static UserTest createUserTest(@NotNull final VirtualFile testsDir, @NotNull final Task currentTask) {
+    UserTest userTest = new UserTest();
+    List<UserTest> userTests = currentTask.getUserTests();
+    int testNum = userTests.size() + 1;
+    String inputName = USER_TEST_INPUT + testNum;
+    File inputFile = new File(testsDir.getPath(), inputName);
+    String outputName = USER_TEST_OUTPUT + testNum;
+    File outputFile = new File(testsDir.getPath(), outputName);
+    userTest.setInput(inputFile.getPath());
+    userTest.setOutput(outputFile.getPath());
+    userTests.add(userTest);
+    return userTest;
+  }
+
+  private TabInfo addTestTab(int nameIndex, final StudyTestContentPanel contentPanel, @NotNull final Task currentTask, boolean toBeClosable) {
+    TabInfo testTab = toBeClosable ? createClosableTab(contentPanel, currentTask) : new TabInfo(contentPanel);
+    return testTab.setText(TEST_TAB_NAME + String.valueOf(nameIndex));
+  }
+
+  private TabInfo createClosableTab(StudyTestContentPanel contentPanel, Task currentTask) {
+    TabInfo closableTab = new TabInfo(contentPanel);
+    final DefaultActionGroup tabActions = new DefaultActionGroup();
+    tabActions.add(new CloseTab(closableTab, currentTask));
+    closableTab.setTabLabelActions(tabActions, ActionPlaces.EDITOR_TAB);
+    return closableTab;
+  }
+
+  public void actionPerformed(AnActionEvent e) {
+    showInput(e.getProject());
+  }
+
+  private class HintClosedListener extends  JBPopupAdapter {
+    private final Task myTask;
+    private HintClosedListener(@NotNull final Task task) {
+      myTask = task;
+    }
+
+    @Override
+    public void onClosed(LightweightWindowEvent event) {
+      for (final UserTest userTest : myTask.getUserTests()) {
+        ApplicationManager.getApplication().runWriteAction(new Runnable() {
+          @Override
+          public void run() {
+            if (userTest.isEditable()) {
+              File inputFile = new File(userTest.getInput());
+              File outputFile = new File(userTest.getOutput());
+              flushBuffer(userTest.getInputBuffer(), inputFile);
+              flushBuffer(userTest.getOutputBuffer(), outputFile);
+            }
+          }
+        });
+      }
+    }
+  }
+
+  private class CloseTab extends AnAction implements DumbAware {
+
+    private final TabInfo myTabInfo;
+    private final Task myTask;
+
+    public CloseTab(final TabInfo info, @NotNull final Task task) {
+      myTabInfo = info;
+      myTask = task;
+    }
+
+    @Override
+    public void update(final AnActionEvent e) {
+      e.getPresentation().setIcon(tabbedPane.isEditorTabs() ? AllIcons.Actions.CloseNew : AllIcons.Actions.Close);
+      e.getPresentation().setHoveredIcon(tabbedPane.isEditorTabs() ? AllIcons.Actions.CloseNewHovered : AllIcons.Actions.CloseHovered);
+      e.getPresentation().setVisible(UISettings.getInstance().SHOW_CLOSE_BUTTON);
+      e.getPresentation().setText("Delete test");
+    }
+
+    @Override
+    public void actionPerformed(final AnActionEvent e) {
+      tabbedPane.removeTab(myTabInfo);
+      UserTest userTest = myEditableTabs.get(myTabInfo);
+      File testInputFile = new File(userTest.getInput());
+      File testOutputFile = new File(userTest.getOutput());
+      if (testInputFile.delete() && testOutputFile.delete()) {
+        StudyUtils.synchronize();
+      } else {
+        LOG.error("failed to delete user tests");
+      }
+      myTask.getUserTests().remove(userTest);
+    }
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextStudyTaskAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextStudyTaskAction.java
new file mode 100644
index 0000000..81818a9
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextStudyTaskAction.java
@@ -0,0 +1,24 @@
+package com.jetbrains.python.edu.actions;
+
+import com.jetbrains.python.edu.editor.StudyEditor;
+import com.jetbrains.python.edu.course.Task;
+
+import javax.swing.*;
+
+public class StudyNextStudyTaskAction extends StudyTaskNavigationAction {
+
+  @Override
+  protected JButton getButton(StudyEditor selectedStudyEditor) {
+    return selectedStudyEditor.getNextTaskButton();
+  }
+
+  @Override
+  protected String getNavigationFinishedMessage() {
+    return "It's the last task";
+  }
+
+  @Override
+  protected Task getTargetTask(Task sourceTask) {
+    return sourceTask.next();
+  }
+}
\ No newline at end of file
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextWindowAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextWindowAction.java
new file mode 100644
index 0000000..595aeef
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextWindowAction.java
@@ -0,0 +1,32 @@
+package com.jetbrains.python.edu.actions;
+
+import com.jetbrains.python.edu.StudyUtils;
+import com.jetbrains.python.edu.course.TaskWindow;
+import icons.StudyIcons;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * move caret to next task window
+ */
+public class StudyNextWindowAction extends StudyWindowNavigationAction {
+  public static final String ACTION_ID = "NextWindow";
+  public static final String SHORTCUT = "ctrl pressed PERIOD";
+  public static final String SHORTCUT2 = "ctrl pressed ENTER";
+
+  public StudyNextWindowAction() {
+    super("NextWindowAction", "Select next window", StudyIcons.Next);
+  }
+
+  @Override
+  protected TaskWindow getNextTaskWindow(@NotNull final TaskWindow window) {
+    int index = window.getIndex();
+    List<TaskWindow> windows = window.getTaskFile().getTaskWindows();
+    if (StudyUtils.indexIsValid(index, windows)) {
+      int newIndex = index + 1;
+        return newIndex == windows.size() ? windows.get(0) : windows.get(newIndex);
+    }
+    return null;
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyPrevWindowAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyPrevWindowAction.java
new file mode 100644
index 0000000..3474561
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyPrevWindowAction.java
@@ -0,0 +1,34 @@
+package com.jetbrains.python.edu.actions;
+
+import com.jetbrains.python.edu.StudyUtils;
+import com.jetbrains.python.edu.course.TaskWindow;
+import icons.StudyIcons;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * author: liana
+ * data: 6/30/14.
+ */
+public class StudyPrevWindowAction extends StudyWindowNavigationAction {
+  public static final String ACTION_ID = "PrevWindowAction";
+  public static final String SHORTCUT = "ctrl pressed COMMA";
+
+  public StudyPrevWindowAction() {
+    super("PrevWindowAction", "Select previous window", StudyIcons.Prev);
+  }
+
+
+  @Nullable
+  @Override
+  protected TaskWindow getNextTaskWindow(@NotNull final TaskWindow window) {
+    int prevIndex = window.getIndex() - 1;
+    List<TaskWindow> windows = window.getTaskFile().getTaskWindows();
+    if (StudyUtils.indexIsValid(prevIndex, windows)) {
+      return windows.get(prevIndex);
+    }
+    return null;
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyPreviousStudyTaskAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyPreviousStudyTaskAction.java
new file mode 100644
index 0000000..bc26c28
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyPreviousStudyTaskAction.java
@@ -0,0 +1,25 @@
+package com.jetbrains.python.edu.actions;
+
+
+import com.jetbrains.python.edu.editor.StudyEditor;
+import com.jetbrains.python.edu.course.Task;
+
+import javax.swing.*;
+
+public class StudyPreviousStudyTaskAction extends StudyTaskNavigationAction {
+
+  @Override
+  protected JButton getButton(StudyEditor selectedStudyEditor) {
+    return selectedStudyEditor.getPrevTaskButton();
+  }
+
+  @Override
+  protected String getNavigationFinishedMessage() {
+    return "It's already the first task";
+  }
+
+  @Override
+  protected Task getTargetTask(Task sourceTask) {
+    return sourceTask.prev();
+  }
+}
\ No newline at end of file
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskAction.java
new file mode 100644
index 0000000..f8abb0b
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskAction.java
@@ -0,0 +1,122 @@
+package com.jetbrains.python.edu.actions;
+
+import com.intellij.ide.projectView.ProjectView;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.command.CommandProcessor;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.MessageType;
+import com.intellij.openapi.ui.popup.Balloon;
+import com.intellij.openapi.ui.popup.BalloonBuilder;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.wm.IdeFocusManager;
+import com.jetbrains.python.edu.StudyDocumentListener;
+import com.jetbrains.python.edu.StudyTaskManager;
+import com.jetbrains.python.edu.StudyUtils;
+import com.jetbrains.python.edu.course.*;
+import com.jetbrains.python.edu.editor.StudyEditor;
+
+import java.io.*;
+
+public class StudyRefreshTaskAction extends DumbAwareAction {
+  private static final Logger LOG = Logger.getInstance(StudyRefreshTaskAction.class.getName());
+
+  public void refresh(final Project project) {
+        ApplicationManager.getApplication().invokeLater(new Runnable() {
+          @Override
+          public void run() {
+            ApplicationManager.getApplication().runWriteAction(new Runnable() {
+              @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
+              @Override
+              public void run() {
+                final Editor editor = StudyEditor.getSelectedEditor(project);
+                assert editor != null;
+                final Document document = editor.getDocument();
+                StudyDocumentListener listener = StudyEditor.getListener(document);
+                if (listener != null) {
+                  document.removeDocumentListener(listener);
+                }
+                final int lineCount = document.getLineCount();
+                if (lineCount != 0) {
+                  CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() {
+                    @Override
+                    public void run() {
+                      document.deleteString(0, document.getLineEndOffset(lineCount - 1));
+                    }
+                  });
+                }
+                StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
+                Course course = taskManager.getCourse();
+                assert course != null;
+                File resourceFile = new File(course.getResourcePath());
+                File resourceRoot = resourceFile.getParentFile();
+                FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
+                VirtualFile openedFile = fileDocumentManager.getFile(document);
+                assert openedFile != null;
+                final TaskFile selectedTaskFile = taskManager.getTaskFile(openedFile);
+                assert selectedTaskFile != null;
+                Task currentTask = selectedTaskFile.getTask();
+                String lessonDir = Lesson.LESSON_DIR + String.valueOf(currentTask.getLesson().getIndex() + 1);
+                String taskDir = Task.TASK_DIR + String.valueOf(currentTask.getIndex() + 1);
+                File pattern = new File(new File(new File(resourceRoot, lessonDir), taskDir), openedFile.getName());
+                BufferedReader reader = null;
+                try {
+                  reader = new BufferedReader(new InputStreamReader(new FileInputStream(pattern)));
+                  String line;
+                  StringBuilder patternText = new StringBuilder();
+                  while ((line = reader.readLine()) != null) {
+                    patternText.append(line);
+                    patternText.append("\n");
+                  }
+                  int patternLength = patternText.length();
+                  if (patternText.charAt(patternLength - 1) == '\n') {
+                    patternText.delete(patternLength - 1, patternLength);
+                  }
+                  document.setText(patternText);
+                  StudyStatus oldStatus = currentTask.getStatus();
+                  LessonInfo lessonInfo = currentTask.getLesson().getLessonInfo();
+                  lessonInfo.update(oldStatus, -1);
+                  lessonInfo.update(StudyStatus.Unchecked, +1);
+                  StudyUtils.updateStudyToolWindow(project);
+                  for (TaskWindow taskWindow : selectedTaskFile.getTaskWindows()) {
+                    taskWindow.reset();
+                  }
+                  ProjectView.getInstance(project).refresh();
+                  if (listener != null) {
+                    document.addDocumentListener(listener);
+                  }
+                  selectedTaskFile.drawAllWindows(editor);
+                  IdeFocusManager.getInstance(project).requestFocus(editor.getContentComponent(), true);
+                  selectedTaskFile.navigateToFirstTaskWindow(editor);
+                  BalloonBuilder balloonBuilder =
+                    JBPopupFactory.getInstance().createHtmlTextBalloonBuilder("You can now start again", MessageType.INFO, null);
+                  Balloon balloon = balloonBuilder.createBalloon();
+                  StudyEditor selectedStudyEditor = StudyEditor.getSelectedStudyEditor(project);
+                  assert selectedStudyEditor != null;
+                  balloon.showInCenterOf(selectedStudyEditor.getRefreshButton());
+                }
+                catch (FileNotFoundException e1) {
+                  LOG.error(e1);
+                }
+                catch (IOException e1) {
+                  LOG.error(e1);
+                }
+                finally {
+                  StudyUtils.closeSilently(reader);
+                }
+              }
+            });
+          }
+        });
+  }
+
+  public void actionPerformed(AnActionEvent e) {
+    refresh(e.getProject());
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRunAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRunAction.java
new file mode 100644
index 0000000..71e95de
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRunAction.java
@@ -0,0 +1,89 @@
+package com.jetbrains.python.edu.actions;
+
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.RunContentExecutor;
+import com.intellij.execution.configurations.GeneralCommandLine;
+import com.intellij.execution.process.OSProcessHandler;
+import com.intellij.execution.process.ProcessHandler;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.jetbrains.python.sdk.PythonSdkType;
+import com.jetbrains.python.edu.StudyResourceManger;
+import com.jetbrains.python.edu.StudyTaskManager;
+import com.jetbrains.python.edu.course.Task;
+import com.jetbrains.python.edu.course.TaskFile;
+import com.jetbrains.python.edu.editor.StudyEditor;
+
+import java.io.File;
+
+public class StudyRunAction extends DumbAwareAction {
+  private static final Logger LOG = Logger.getInstance(StudyRunAction.class.getName());
+  public static final String ACTION_ID = "StudyRunAction";
+
+  public void run(Project project) {
+    Editor selectedEditor = StudyEditor.getSelectedEditor(project);
+    FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
+    assert selectedEditor != null;
+    VirtualFile openedFile = fileDocumentManager.getFile(selectedEditor.getDocument());
+    StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
+    if (openedFile != null && openedFile.getCanonicalPath() != null) {
+      String filePath = openedFile.getCanonicalPath();
+      GeneralCommandLine cmd = new GeneralCommandLine();
+      cmd.setWorkDirectory(openedFile.getParent().getCanonicalPath());
+      Sdk sdk = PythonSdkType.findPythonSdk(ModuleManager.getInstance(project).getModules()[0]);
+      if (sdk != null) {
+        String pythonPath = sdk.getHomePath();
+        if (pythonPath != null) {
+          cmd.setExePath(pythonPath);
+          TaskFile selectedTaskFile = taskManager.getTaskFile(openedFile);
+          assert selectedTaskFile != null;
+          Task currentTask = selectedTaskFile.getTask();
+          if (!currentTask.getUserTests().isEmpty()) {
+            cmd.addParameter(new File(project.getBaseDir().getPath(), StudyResourceManger.USER_TESTER).getPath());
+            cmd.addParameter(pythonPath);
+            cmd.addParameter(filePath);
+            Process p;
+            try {
+              p = cmd.createProcess();
+            }
+            catch (ExecutionException e) {
+              LOG.error(e);
+              return;
+            }
+            ProcessHandler handler = new OSProcessHandler(p);
+
+            RunContentExecutor executor = new RunContentExecutor(project, handler);
+            Disposer.register(project, executor);
+            executor.run();
+            return;
+          }
+          try {
+            cmd.addParameter(filePath);
+            Process p = cmd.createProcess();
+            ProcessHandler handler = new OSProcessHandler(p);
+
+            RunContentExecutor executor = new RunContentExecutor(project, handler);
+            Disposer.register(project, executor);
+            executor.run();
+          }
+
+          catch (ExecutionException e) {
+            LOG.error(e);
+          }
+        }
+      }
+    }
+  }
+
+  public void actionPerformed(AnActionEvent e) {
+    run(e.getProject());
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyShowHintAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyShowHintAction.java
new file mode 100644
index 0000000..1efa908
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyShowHintAction.java
@@ -0,0 +1,95 @@
+package com.jetbrains.python.edu.actions;
+
+import com.intellij.codeInsight.documentation.DocumentationComponent;
+import com.intellij.codeInsight.documentation.DocumentationManager;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.LogicalPosition;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.popup.JBPopup;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiManager;
+import com.jetbrains.python.edu.StudyTaskManager;
+import com.jetbrains.python.edu.StudyUtils;
+import com.jetbrains.python.edu.course.Course;
+import com.jetbrains.python.edu.course.TaskFile;
+import com.jetbrains.python.edu.course.TaskWindow;
+import com.jetbrains.python.edu.editor.StudyEditor;
+import icons.StudyIcons;
+
+import java.io.File;
+
+public class StudyShowHintAction extends DumbAwareAction {
+  public static final String ACTION_ID = "ShowHintAction";
+  public static final String SHORTCUT = "ctrl pressed 7";
+
+  public StudyShowHintAction() {
+    super("Show hint", "Show hint", StudyIcons.ShowHint);
+  }
+
+  public void actionPerformed(AnActionEvent e) {
+    Project project = e.getProject();
+    if (project != null) {
+      DocumentationManager documentationManager = DocumentationManager.getInstance(project);
+      DocumentationComponent component = new DocumentationComponent(documentationManager);
+      Editor selectedEditor = StudyEditor.getSelectedEditor(project);
+      FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
+      assert selectedEditor != null;
+      VirtualFile openedFile = fileDocumentManager.getFile(selectedEditor.getDocument());
+      if (openedFile != null) {
+        StudyTaskManager taskManager = StudyTaskManager.getInstance(e.getProject());
+        TaskFile taskFile = taskManager.getTaskFile(openedFile);
+        if (taskFile != null) {
+          PsiFile file = PsiManager.getInstance(project).findFile(openedFile);
+          if (file != null) {
+            LogicalPosition pos = selectedEditor.getCaretModel().getLogicalPosition();
+            TaskWindow taskWindow = taskFile.getTaskWindow(selectedEditor.getDocument(), pos);
+            if (taskWindow != null) {
+              String hint = taskWindow.getHint();
+              if (hint == null) {
+                return;
+              }
+              Course course = taskManager.getCourse();
+              if (course != null) {
+                File resourceFile = new File(course.getResourcePath());
+                File resourceRoot = resourceFile.getParentFile();
+                if (resourceRoot != null && resourceRoot.exists()) {
+                  File hintsDir = new File(resourceRoot, Course.HINTS_DIR);
+                  if (hintsDir.exists()) {
+                    String hintText = StudyUtils.getFileText(hintsDir.getAbsolutePath(), hint, true);
+                    if (hintText != null) {
+                      int offset = selectedEditor.getDocument().getLineStartOffset(pos.line) + pos.column;
+                      PsiElement element = file.findElementAt(offset);
+                      if (element != null) {
+                        component.setData(element, hintText, true, null);
+                        final JBPopup popup =
+                          JBPopupFactory.getInstance().createComponentPopupBuilder(component, component)
+                            .setDimensionServiceKey(project, DocumentationManager.JAVADOC_LOCATION_AND_SIZE, false)
+                            .setResizable(true)
+                            .setMovable(true)
+                            .setRequestFocus(true)
+                            .createPopup();
+                        component.setHint(popup);
+                        popup.showInBestPositionFor(selectedEditor);
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  @Override
+  public void update(AnActionEvent e) {
+    StudyUtils.updateAction(e);
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyTaskNavigationAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyTaskNavigationAction.java
new file mode 100644
index 0000000..b781e7d
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyTaskNavigationAction.java
@@ -0,0 +1,97 @@
+package com.jetbrains.python.edu.actions;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.MessageType;
+import com.intellij.openapi.ui.popup.Balloon;
+import com.intellij.openapi.ui.popup.BalloonBuilder;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.jetbrains.python.edu.StudyTaskManager;
+import com.jetbrains.python.edu.course.Lesson;
+import com.jetbrains.python.edu.course.Task;
+import com.jetbrains.python.edu.course.TaskFile;
+import com.jetbrains.python.edu.editor.StudyEditor;
+
+import javax.swing.*;
+import java.util.Map;
+
+/**
+ * author: liana
+ * data: 7/21/14.
+ */
+abstract public class StudyTaskNavigationAction extends DumbAwareAction {
+  public void navigateTask(Project project) {
+    Editor selectedEditor = StudyEditor.getSelectedEditor(project);
+    FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
+    assert selectedEditor != null;
+    VirtualFile openedFile = fileDocumentManager.getFile(selectedEditor.getDocument());
+    StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
+    assert openedFile != null;
+    TaskFile selectedTaskFile = taskManager.getTaskFile(openedFile);
+    assert selectedTaskFile != null;
+    Task currentTask = selectedTaskFile.getTask();
+    Task nextTask = getTargetTask(currentTask);
+    if (nextTask == null) {
+      BalloonBuilder balloonBuilder =
+        JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(getNavigationFinishedMessage(), MessageType.INFO, null);
+      Balloon balloon = balloonBuilder.createBalloon();
+      StudyEditor selectedStudyEditor = StudyEditor.getSelectedStudyEditor(project);
+      balloon.showInCenterOf(getButton(selectedStudyEditor));
+      return;
+    }
+    for (VirtualFile file : FileEditorManager.getInstance(project).getOpenFiles()) {
+      FileEditorManager.getInstance(project).closeFile(file);
+    }
+    int nextTaskIndex = nextTask.getIndex();
+    int lessonIndex = nextTask.getLesson().getIndex();
+    Map<String, TaskFile> nextTaskFiles = nextTask.getTaskFiles();
+    if (nextTaskFiles.isEmpty()) {
+      return;
+    }
+    VirtualFile projectDir = project.getBaseDir();
+    String lessonDirName = Lesson.LESSON_DIR + String.valueOf(lessonIndex + 1);
+    if (projectDir == null) {
+      return;
+    }
+    VirtualFile lessonDir = projectDir.findChild(lessonDirName);
+    if (lessonDir == null) {
+      return;
+    }
+    String taskDirName = Task.TASK_DIR + String.valueOf(nextTaskIndex + 1);
+    VirtualFile taskDir = lessonDir.findChild(taskDirName);
+    if (taskDir == null) {
+      return;
+    }
+    VirtualFile shouldBeActive = null;
+    for (Map.Entry<String, TaskFile> entry : nextTaskFiles.entrySet()) {
+      String name = entry.getKey();
+      TaskFile taskFile = entry.getValue();
+      VirtualFile vf = taskDir.findChild(name);
+      if (vf != null) {
+        FileEditorManager.getInstance(project).openFile(vf, true);
+        if (!taskFile.getTaskWindows().isEmpty()) {
+          shouldBeActive = vf;
+        }
+      }
+    }
+    if (shouldBeActive != null) {
+      FileEditorManager.getInstance(project).openFile(shouldBeActive, true);
+    }
+  }
+
+  protected abstract JButton getButton(StudyEditor selectedStudyEditor);
+
+  @Override
+  public void actionPerformed(AnActionEvent e) {
+    navigateTask(e.getProject());
+  }
+
+  protected abstract String getNavigationFinishedMessage();
+
+  protected abstract Task getTargetTask(Task sourceTask);
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyWindowNavigationAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyWindowNavigationAction.java
new file mode 100644
index 0000000..8c6b902
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyWindowNavigationAction.java
@@ -0,0 +1,65 @@
+package com.jetbrains.python.edu.actions;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.jetbrains.python.edu.StudyTaskManager;
+import com.jetbrains.python.edu.StudyUtils;
+import com.jetbrains.python.edu.course.TaskFile;
+import com.jetbrains.python.edu.course.TaskWindow;
+import com.jetbrains.python.edu.editor.StudyEditor;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+abstract public class StudyWindowNavigationAction extends DumbAwareAction {
+
+  public StudyWindowNavigationAction(String actionId, String description, Icon icon) {
+    super(actionId, description, icon);
+  }
+
+  public void navigateWindow(@NotNull final Project project) {
+      Editor selectedEditor = StudyEditor.getSelectedEditor(project);
+      if (selectedEditor != null) {
+        FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
+        VirtualFile openedFile = fileDocumentManager.getFile(selectedEditor.getDocument());
+        if (openedFile != null) {
+          StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
+          TaskFile selectedTaskFile = taskManager.getTaskFile(openedFile);
+          if (selectedTaskFile != null) {
+            TaskWindow selectedTaskWindow = selectedTaskFile.getSelectedTaskWindow();
+            if (selectedTaskWindow == null) {
+              return;
+            }
+            TaskWindow nextTaskWindow = getNextTaskWindow(selectedTaskWindow);
+            if (nextTaskWindow == null) {
+              return;
+            }
+            nextTaskWindow.draw(selectedEditor, true, true);
+            selectedTaskFile.setSelectedTaskWindow(nextTaskWindow);
+            }
+          }
+        }
+      }
+
+  @Nullable
+  protected abstract TaskWindow getNextTaskWindow(@NotNull final TaskWindow window);
+
+  @Override
+  public void actionPerformed(AnActionEvent e) {
+    Project project = e.getProject();
+    if (project == null) {
+      return;
+    }
+    navigateWindow(project);
+  }
+
+  @Override
+  public void update(AnActionEvent e) {
+    StudyUtils.updateAction(e);
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/course/Course.java b/python/edu/learn-python/src/com/jetbrains/python/edu/course/Course.java
new file mode 100644
index 0000000..89613ac
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/course/Course.java
@@ -0,0 +1,104 @@
+package com.jetbrains.python.edu.course;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class Course {
+
+  private static final Logger LOG = Logger.getInstance(Course.class.getName());
+  public static final String PLAYGROUND_DIR = "Playground";
+  public List<Lesson> lessons = new ArrayList<Lesson>();
+  public String description;
+  public String name;
+  public String myResourcePath = "";
+  public String author;
+  public static final String COURSE_DIR = "course";
+  public static final String HINTS_DIR = "hints";
+
+
+  public List<Lesson> getLessons() {
+    return lessons;
+  }
+
+  /**
+   * Initializes state of course
+   */
+  public void init(boolean isRestarted) {
+    for (Lesson lesson : lessons) {
+      lesson.init(this, isRestarted);
+    }
+  }
+
+  public String getAuthor() {
+    return author;
+  }
+
+  /**
+   * Creates course directory in project user created
+   *
+   * @param baseDir      project directory
+   * @param resourceRoot directory where original course is stored
+   */
+  public void create(@NotNull final VirtualFile baseDir, @NotNull final File resourceRoot) {
+    ApplicationManager.getApplication().invokeLater(
+      new Runnable() {
+        @Override
+        public void run() {
+          ApplicationManager.getApplication().runWriteAction(new Runnable() {
+            @Override
+            public void run() {
+              try {
+                for (int i = 0; i < lessons.size(); i++) {
+                  Lesson lesson = lessons.get(i);
+                  lesson.setIndex(i);
+                  lesson.create(baseDir, resourceRoot);
+                }
+                baseDir.createChildDirectory(this, PLAYGROUND_DIR);
+                File[] files = resourceRoot.listFiles(new FilenameFilter() {
+                  @Override
+                  public boolean accept(File dir, String name) {
+                   return !name.contains(Lesson.LESSON_DIR) && !name.equals("course.json") && !name.equals("hints");
+                  }
+                });
+                for (File file: files) {
+                  FileUtil.copy(file, new File(baseDir.getPath(), file.getName()));
+                }
+              }
+              catch (IOException e) {
+                LOG.error(e);
+              }
+            }
+          });
+        }
+      });
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setResourcePath(@NotNull final String resourcePath) {
+    myResourcePath = resourcePath;
+  }
+
+  public String getResourcePath() {
+    return myResourcePath;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/course/CourseInfo.java b/python/edu/learn-python/src/com/jetbrains/python/edu/course/CourseInfo.java
new file mode 100644
index 0000000..9f820c1
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/course/CourseInfo.java
@@ -0,0 +1,52 @@
+package com.jetbrains.python.edu.course;
+
+/**
+ * Implementation of class which contains information to be shawn in course description in tool window
+ * and when project is being created
+ */
+public class CourseInfo {
+  private String myName;
+  private String myAuthor;
+  private String myDescription;
+  public static CourseInfo INVALID_COURSE = new CourseInfo("", "", "");
+
+  public CourseInfo(String name, String author, String description) {
+    myName = name;
+    myAuthor = author;
+    myDescription = description;
+  }
+
+  public String getName() {
+    return myName;
+  }
+
+  public String getAuthor() {
+    return myAuthor;
+  }
+
+  public String getDescription() {
+    return myDescription;
+  }
+
+  @Override
+  public String toString() {
+    return myName;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    CourseInfo that = (CourseInfo)o;
+    return that.getName().equals(myName) && that.getAuthor().equals(myAuthor)
+           && that.getDescription().equals(myDescription);
+  }
+
+  @Override
+  public int hashCode() {
+    int result = myName != null ? myName.hashCode() : 0;
+    result = 31 * result + (myAuthor != null ? myAuthor.hashCode() : 0);
+    result = 31 * result + (myDescription != null ? myDescription.hashCode() : 0);
+    return result;
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/course/Lesson.java b/python/edu/learn-python/src/com/jetbrains/python/edu/course/Lesson.java
new file mode 100644
index 0000000..3879d51
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/course/Lesson.java
@@ -0,0 +1,109 @@
+package com.jetbrains.python.edu.course;
+
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.xmlb.annotations.Transient;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class Lesson implements Stateful{
+  public String name;
+  public List<Task> taskList = new ArrayList<Task>();
+  private Course myCourse = null;
+  public int myIndex = -1;
+  public static final String LESSON_DIR = "lesson";
+  public LessonInfo myLessonInfo = new LessonInfo();
+
+  public LessonInfo getLessonInfo() {
+    return myLessonInfo;
+  }
+
+  @Transient
+  public StudyStatus getStatus() {
+    for (Task task : taskList) {
+      StudyStatus taskStatus = task.getStatus();
+      if (taskStatus == StudyStatus.Unchecked || taskStatus == StudyStatus.Failed) {
+        return StudyStatus.Unchecked;
+      }
+    }
+    return StudyStatus.Solved;
+  }
+
+  @Override
+  public void setStatus(StudyStatus status, StudyStatus oldStatus) {
+    for (Task task : taskList) {
+      task.setStatus(status, oldStatus);
+    }
+  }
+
+  public List<Task> getTaskList() {
+    return taskList;
+  }
+
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  /**
+   * Creates lesson directory in its course folder in project user created
+   *
+   * @param courseDir    project directory of course
+   * @param resourceRoot directory where original lesson stored
+   * @throws java.io.IOException
+   */
+  public void create(@NotNull final VirtualFile courseDir, @NotNull final File resourceRoot) throws IOException {
+    String lessonDirName = LESSON_DIR + Integer.toString(myIndex + 1);
+    VirtualFile lessonDir = courseDir.createChildDirectory(this, lessonDirName);
+    for (int i = 0; i < taskList.size(); i++) {
+      Task task = taskList.get(i);
+      task.setIndex(i);
+      task.create(lessonDir, new File(resourceRoot, lessonDir.getName()));
+    }
+  }
+
+
+  /**
+   * Initializes state of lesson
+   *
+   * @param course course which lesson belongs to
+   */
+  public void init(final Course course, boolean isRestarted) {
+    myCourse = course;
+    myLessonInfo.setTaskNum(taskList.size());
+    myLessonInfo.setTaskUnchecked(taskList.size());
+    for (Task task : taskList) {
+      task.init(this, isRestarted);
+    }
+  }
+
+  public Lesson next() {
+    List<Lesson> lessons = myCourse.getLessons();
+    if (myIndex + 1 >= lessons.size()) {
+      return null;
+    }
+    return lessons.get(myIndex + 1);
+  }
+
+  public void setIndex(int index) {
+    myIndex = index;
+  }
+
+  public int getIndex() {
+    return myIndex;
+  }
+
+  public Lesson prev() {
+    if (myIndex - 1 < 0) {
+      return null;
+    }
+    return myCourse.getLessons().get(myIndex - 1);
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/course/LessonInfo.java b/python/edu/learn-python/src/com/jetbrains/python/edu/course/LessonInfo.java
new file mode 100644
index 0000000..85e2eb8
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/course/LessonInfo.java
@@ -0,0 +1,60 @@
+package com.jetbrains.python.edu.course;
+
+/**
+ * Implementation of class which contains information about student progress in current lesson
+ */
+public class LessonInfo {
+  private int myTaskNum;
+  private int myTaskFailed;
+  private int myTaskSolved;
+  private int myTaskUnchecked;
+
+  public int getTaskNum() {
+    return myTaskNum;
+  }
+
+  public void setTaskNum(int taskNum) {
+    myTaskNum = taskNum;
+  }
+
+  public int getTaskFailed() {
+    return myTaskFailed;
+  }
+
+  public void setTaskFailed(int taskFailed) {
+    myTaskFailed = taskFailed;
+  }
+
+  public int getTaskSolved() {
+    return myTaskSolved;
+  }
+
+  public void setTaskSolved(int taskSolved) {
+    myTaskSolved = taskSolved;
+  }
+
+  public int getTaskUnchecked() {
+    return myTaskUnchecked;
+  }
+
+  public void setTaskUnchecked(int taskUnchecked) {
+    myTaskUnchecked = taskUnchecked;
+  }
+
+  public void update(StudyStatus status, int delta) {
+    switch (status) {
+      case Solved: {
+        myTaskSolved += delta;
+        break;
+      }
+      case Failed: {
+        myTaskFailed += delta;
+        break;
+      }
+      case Unchecked: {
+        myTaskUnchecked += delta;
+        break;
+      }
+    }
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/course/Stateful.java b/python/edu/learn-python/src/com/jetbrains/python/edu/course/Stateful.java
new file mode 100644
index 0000000..3a16362
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/course/Stateful.java
@@ -0,0 +1,6 @@
+package com.jetbrains.python.edu.course;
+
+public interface Stateful {
+  StudyStatus getStatus();
+  void setStatus(StudyStatus status, StudyStatus oldStatus);
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/course/StudyStatus.java b/python/edu/learn-python/src/com/jetbrains/python/edu/course/StudyStatus.java
new file mode 100644
index 0000000..d95b42b
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/course/StudyStatus.java
@@ -0,0 +1,8 @@
+package com.jetbrains.python.edu.course;
+
+/**
+ * @see {@link TaskWindow#myStatus}
+ */
+public enum StudyStatus {
+  Unchecked, Solved, Failed
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/course/Task.java b/python/edu/learn-python/src/com/jetbrains/python/edu/course/Task.java
new file mode 100644
index 0000000..2323412
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/course/Task.java
@@ -0,0 +1,201 @@
+package com.jetbrains.python.edu.course;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.xmlb.annotations.Transient;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import com.jetbrains.python.edu.StudyUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implementation of task which contains task files, tests, input file for tests
+ */
+public class Task implements Stateful{
+  public static final String TASK_DIR = "task";
+  private static final String ourTestFile = "tests.py";
+  public String name;
+  private static final String ourTextFile = "task.html";
+  public Map<String, TaskFile> taskFiles = new HashMap<String, TaskFile>();
+  private Lesson myLesson;
+  public int myIndex;
+  public List<UserTest> userTests = new ArrayList<UserTest>();
+  public static final String USER_TESTS = "userTests";
+
+  public Map<String, TaskFile> getTaskFiles() {
+    return taskFiles;
+  }
+
+  @Transient
+  public StudyStatus getStatus() {
+    for (TaskFile taskFile : taskFiles.values()) {
+      StudyStatus taskFileStatus = taskFile.getStatus();
+      if (taskFileStatus == StudyStatus.Unchecked) {
+        return StudyStatus.Unchecked;
+      }
+      if (taskFileStatus == StudyStatus.Failed) {
+        return StudyStatus.Failed;
+      }
+    }
+    return StudyStatus.Solved;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public void setStatus(@NotNull final StudyStatus status, @NotNull final StudyStatus oldStatus) {
+    LessonInfo lessonInfo = myLesson.getLessonInfo();
+    if (status != oldStatus) {
+      lessonInfo.update(oldStatus, -1);
+      lessonInfo.update(status, +1);
+    }
+    for (TaskFile taskFile : taskFiles.values()) {
+      taskFile.setStatus(status, oldStatus);
+    }
+  }
+
+  public List<UserTest> getUserTests() {
+    return userTests;
+  }
+
+  public String getTestFile() {
+    return ourTestFile;
+  }
+
+  public String getText() {
+    return ourTextFile;
+  }
+
+  /**
+   * Creates task directory in its lesson folder in project user created
+   *
+   * @param lessonDir    project directory of lesson which task belongs to
+   * @param resourceRoot directory where original task file stored
+   * @throws java.io.IOException
+   */
+  public void create(@NotNull final VirtualFile lessonDir, @NotNull final File resourceRoot) throws IOException {
+    VirtualFile taskDir = lessonDir.createChildDirectory(this, TASK_DIR + Integer.toString(myIndex + 1));
+    File newResourceRoot = new File(resourceRoot, taskDir.getName());
+    int i = 0;
+    for (Map.Entry<String, TaskFile> taskFile : taskFiles.entrySet()) {
+      TaskFile taskFileContent = taskFile.getValue();
+      taskFileContent.setIndex(i);
+      i++;
+      taskFileContent.create(taskDir, newResourceRoot, taskFile.getKey());
+    }
+    File[] filesInTask = newResourceRoot.listFiles();
+    if (filesInTask != null) {
+      for (File file : filesInTask) {
+        String fileName = file.getName();
+        if (!isTaskFile(fileName)) {
+          File resourceFile = new File(newResourceRoot, fileName);
+          File fileInProject = new File(taskDir.getCanonicalPath(), fileName);
+          FileUtil.copy(resourceFile, fileInProject);
+        }
+      }
+    }
+  }
+
+  private boolean isTaskFile(@NotNull final String fileName) {
+    return taskFiles.get(fileName) != null;
+  }
+
+  @Nullable
+  public TaskFile getFile(@NotNull final String fileName) {
+    return taskFiles.get(fileName);
+  }
+
+  /**
+   * Initializes state of task file
+   *
+   * @param lesson lesson which task belongs to
+   */
+  public void init(final Lesson lesson, boolean isRestarted) {
+    myLesson = lesson;
+    for (TaskFile taskFile : taskFiles.values()) {
+      taskFile.init(this, isRestarted);
+    }
+  }
+
+  public Task next() {
+    Lesson currentLesson = this.myLesson;
+    List<Task> taskList = myLesson.getTaskList();
+    if (myIndex + 1 < taskList.size()) {
+      return taskList.get(myIndex + 1);
+    }
+    Lesson nextLesson = currentLesson.next();
+    if (nextLesson == null) {
+      return null;
+    }
+    return StudyUtils.getFirst(nextLesson.getTaskList());
+  }
+
+  public Task prev() {
+    Lesson currentLesson = this.myLesson;
+    if (myIndex - 1 >= 0) {
+      return myLesson.getTaskList().get(myIndex - 1);
+    }
+    Lesson prevLesson = currentLesson.prev();
+    if (prevLesson == null) {
+      return null;
+    }
+    //getting last task in previous lesson
+    return prevLesson.getTaskList().get(prevLesson.getTaskList().size() - 1);
+  }
+
+  public void setIndex(int index) {
+    myIndex = index;
+  }
+
+  public int getIndex() {
+    return myIndex;
+  }
+
+  public Lesson getLesson() {
+    return myLesson;
+  }
+
+
+  @Nullable
+  public VirtualFile getTaskDir(Project project) {
+    String lessonDirName = Lesson.LESSON_DIR + String.valueOf(myLesson.getIndex() + 1);
+    String taskDirName = TASK_DIR + String.valueOf(myIndex + 1);
+    VirtualFile courseDir = project.getBaseDir();
+    if (courseDir != null) {
+      VirtualFile lessonDir = courseDir.findChild(lessonDirName);
+      if (lessonDir != null) {
+        return lessonDir.findChild(taskDirName);
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Gets text of resource file such as test input file or task text in needed format
+   *
+   * @param fileName name of resource file which should exist in task directory
+   * @param wrapHTML if it's necessary to wrap text with html tags
+   * @return text of resource file wrapped with html tags if necessary
+   */
+  @Nullable
+  public String getResourceText(@NotNull final Project project, @NotNull final String fileName, boolean wrapHTML) {
+    VirtualFile taskDir = getTaskDir(project);
+    if (taskDir != null) {
+      return StudyUtils.getFileText(taskDir.getCanonicalPath(), fileName, wrapHTML);
+    }
+    return null;
+  }
+
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskFile.java b/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskFile.java
new file mode 100644
index 0000000..4f17fc0
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskFile.java
@@ -0,0 +1,228 @@
+package com.jetbrains.python.edu.course;
+
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.LogicalPosition;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.xmlb.annotations.Transient;
+import com.jetbrains.python.edu.StudyUtils;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Implementation of task file which contains task windows for student to type in and
+ * which is visible to student in project view
+ */
+
+public class TaskFile implements Stateful{
+  public List<TaskWindow> taskWindows = new ArrayList<TaskWindow>();
+  private Task myTask;
+  @Transient
+  private TaskWindow mySelectedTaskWindow = null;
+  public int myIndex = -1;
+  private boolean myUserCreated = false;
+
+  /**
+   * @return if all the windows in task file are marked as resolved
+   */
+  @Transient
+  public StudyStatus getStatus() {
+    for (TaskWindow taskWindow : taskWindows) {
+      StudyStatus windowStatus = taskWindow.getStatus();
+      if (windowStatus == StudyStatus.Failed) {
+        return StudyStatus.Failed;
+      }
+      if (windowStatus == StudyStatus.Unchecked) {
+        return StudyStatus.Unchecked;
+      }
+    }
+    return StudyStatus.Solved;
+  }
+
+  public Task getTask() {
+    return myTask;
+  }
+
+  @Nullable
+  @Transient
+  public TaskWindow getSelectedTaskWindow() {
+    return mySelectedTaskWindow;
+  }
+
+  /**
+   * @param selectedTaskWindow window from this task file to be set as selected
+   */
+  public void setSelectedTaskWindow(@NotNull final TaskWindow selectedTaskWindow) {
+    if (selectedTaskWindow.getTaskFile() == this) {
+      mySelectedTaskWindow = selectedTaskWindow;
+    }
+    else {
+      throw new IllegalArgumentException("Window may be set as selected only in task file which it belongs to");
+    }
+  }
+
+  public List<TaskWindow> getTaskWindows() {
+    return taskWindows;
+  }
+
+  /**
+   * Creates task files in its task folder in project user created
+   *
+   * @param taskDir      project directory of task which task file belongs to
+   * @param resourceRoot directory where original task file stored
+   * @throws java.io.IOException
+   */
+  public void create(@NotNull final VirtualFile taskDir, @NotNull final File resourceRoot,
+                     @NotNull final String name) throws IOException {
+    String systemIndependentName = FileUtil.toSystemIndependentName(name);
+    final int index = systemIndependentName.lastIndexOf("/");
+    if (index > 0) {
+      systemIndependentName = systemIndependentName.substring(index + 1);
+    }
+    File resourceFile = new File(resourceRoot, name);
+    File fileInProject = new File(taskDir.getPath(), systemIndependentName);
+    FileUtil.copy(resourceFile, fileInProject);
+  }
+
+  public void drawAllWindows(Editor editor) {
+    for (TaskWindow taskWindow : taskWindows) {
+      taskWindow.draw(editor, false, false);
+    }
+  }
+
+
+  /**
+   * @param pos position in editor
+   * @return task window located in specified position or null if there is no task window in this position
+   */
+  @Nullable
+  public TaskWindow getTaskWindow(@NotNull final Document document, @NotNull final LogicalPosition pos) {
+    int line = pos.line;
+    if (line >= document.getLineCount()) {
+      return null;
+    }
+    int column = pos.column;
+    int offset = document.getLineStartOffset(line) + column;
+    for (TaskWindow tw : taskWindows) {
+      if (tw.getLine() <= line) {
+        int twStartOffset = tw.getRealStartOffset(document);
+        final int length = tw.getLength() > 0 ? tw.getLength() : 0;
+        int twEndOffset = twStartOffset + length;
+        if (twStartOffset <= offset && offset <= twEndOffset) {
+          return tw;
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Updates task window lines
+   *
+   * @param startLine lines greater than this line and including this line will be updated
+   * @param change    change to be added to line numbers
+   */
+  public void incrementLines(int startLine, int change) {
+    for (TaskWindow taskTaskWindow : taskWindows) {
+      if (taskTaskWindow.getLine() >= startLine) {
+        taskTaskWindow.setLine(taskTaskWindow.getLine() + change);
+      }
+    }
+  }
+
+  /**
+   * Initializes state of task file
+   *
+   * @param task task which task file belongs to
+   */
+
+  public void init(final Task task, boolean isRestarted) {
+    myTask = task;
+    for (TaskWindow taskWindow : taskWindows) {
+      taskWindow.init(this, isRestarted);
+    }
+    Collections.sort(taskWindows);
+    for (int i = 0; i < taskWindows.size(); i++) {
+      taskWindows.get(i).setIndex(i);
+    }
+  }
+
+  /**
+   * @param index index of task file in list of task files of its task
+   */
+  public void setIndex(int index) {
+    myIndex = index;
+  }
+
+  /**
+   * Updates windows in specific line
+   *
+   * @param lineChange         change in line number
+   * @param line               line to be updated
+   * @param newEndOffsetInLine distance from line start to end of inserted fragment
+   * @param oldEndOffsetInLine distance from line start to end of changed fragment
+   */
+  public void updateLine(int lineChange, int line, int newEndOffsetInLine, int oldEndOffsetInLine) {
+    for (TaskWindow w : taskWindows) {
+      if ((w.getLine() == line) && (w.getStart() >= oldEndOffsetInLine)) {
+        int distance = w.getStart() - oldEndOffsetInLine;
+        if (lineChange != 0 || newEndOffsetInLine <= w.getStart()) {
+          w.setStart(distance + newEndOffsetInLine);
+          w.setLine(line + lineChange);
+        }
+      }
+    }
+  }
+
+  public static void copy(@NotNull final TaskFile source, @NotNull final TaskFile target) {
+    List<TaskWindow> sourceTaskWindows = source.getTaskWindows();
+    List<TaskWindow> windowsCopy = new ArrayList<TaskWindow>(sourceTaskWindows.size());
+    for (TaskWindow taskWindow : sourceTaskWindows) {
+      TaskWindow taskWindowCopy = new TaskWindow();
+      taskWindowCopy.setLine(taskWindow.getLine());
+      taskWindowCopy.setStart(taskWindow.getStart());
+      taskWindowCopy.setLength(taskWindow.getLength());
+      taskWindowCopy.setPossibleAnswer(taskWindow.getPossibleAnswer());
+      taskWindowCopy.setIndex(taskWindow.getIndex());
+      windowsCopy.add(taskWindowCopy);
+    }
+    target.setTaskWindows(windowsCopy);
+  }
+
+  public void setTaskWindows(List<TaskWindow> taskWindows) {
+    this.taskWindows = taskWindows;
+  }
+
+  public void setStatus(@NotNull final StudyStatus status, @NotNull final StudyStatus oldStatus) {
+    for (TaskWindow taskWindow : taskWindows) {
+      taskWindow.setStatus(status, oldStatus);
+    }
+  }
+
+  public void setUserCreated(boolean userCreated) {
+    myUserCreated = userCreated;
+  }
+
+  public boolean isUserCreated() {
+    return myUserCreated;
+  }
+
+  public void navigateToFirstTaskWindow(@NotNull final Editor editor) {
+    if (!taskWindows.isEmpty()) {
+      TaskWindow firstTaskWindow = StudyUtils.getFirst(taskWindows);
+      mySelectedTaskWindow = firstTaskWindow;
+      LogicalPosition taskWindowStart = new LogicalPosition(firstTaskWindow.getLine(), firstTaskWindow.getStart());
+      editor.getCaretModel().moveToLogicalPosition(taskWindowStart);
+      int startOffset = firstTaskWindow.getRealStartOffset(editor.getDocument());
+      int endOffset = startOffset + firstTaskWindow.getLength();
+      editor.getSelectionModel().setSelection(startOffset, endOffset);
+    }
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskWindow.java b/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskWindow.java
new file mode 100644
index 0000000..4fb112c
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskWindow.java
@@ -0,0 +1,177 @@
+package com.jetbrains.python.edu.course;
+
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.colors.EditorColors;
+import com.intellij.openapi.editor.colors.EditorColorsManager;
+import com.intellij.openapi.editor.markup.HighlighterLayer;
+import com.intellij.openapi.editor.markup.HighlighterTargetArea;
+import com.intellij.openapi.editor.markup.RangeHighlighter;
+import com.intellij.openapi.editor.markup.TextAttributes;
+import com.intellij.ui.JBColor;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Implementation of windows which user should type in
+ */
+
+
+public class TaskWindow implements Comparable, Stateful {
+
+  public int line = 0;
+  public int start = 0;
+  public String hint = "";
+  public String possibleAnswer = "";
+  public int length = 0;
+  private TaskFile myTaskFile;
+  public int myIndex = -1;
+  public int myInitialLine = -1;
+  public int myInitialStart = -1;
+  public int myInitialLength = -1;
+  public StudyStatus myStatus = StudyStatus.Unchecked;
+
+  public StudyStatus getStatus() {
+    return myStatus;
+  }
+
+  public void setStatus(StudyStatus status, StudyStatus oldStatus) {
+    myStatus = status;
+  }
+
+  public void setIndex(int index) {
+    myIndex = index;
+  }
+
+  public int getLength() {
+    return length;
+  }
+
+  public void setLength(int length) {
+    this.length = length;
+  }
+
+  public int getStart() {
+    return start;
+  }
+
+  public void setStart(int start) {
+    this.start = start;
+  }
+
+  public void setLine(int line) {
+    this.line = line;
+  }
+
+  public int getLine() {
+    return line;
+  }
+
+
+  /**
+   * Draw task window with color according to its status
+   */
+  public void draw(@NotNull final Editor editor, boolean drawSelection, boolean moveCaret) {
+    Document document = editor.getDocument();
+    if (!isValid(document)) {
+      return;
+    }
+    TextAttributes defaultTestAttributes =
+      EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.LIVE_TEMPLATE_ATTRIBUTES);
+    JBColor color = getColor();
+    int startOffset = document.getLineStartOffset(line) + start;
+    RangeHighlighter
+      rh = editor.getMarkupModel().addRangeHighlighter(startOffset, startOffset + length, HighlighterLayer.LAST + 1,
+                                                       new TextAttributes(defaultTestAttributes.getForegroundColor(),
+                                                                          defaultTestAttributes.getBackgroundColor(), color,
+                                                                          defaultTestAttributes.getEffectType(),
+                                                                          defaultTestAttributes.getFontType()),
+                                                       HighlighterTargetArea.EXACT_RANGE);
+    if (drawSelection) {
+      editor.getSelectionModel().setSelection(startOffset, startOffset + length);
+    }
+    if (moveCaret) {
+      editor.getCaretModel().moveToOffset(startOffset);
+    }
+    rh.setGreedyToLeft(true);
+    rh.setGreedyToRight(true);
+  }
+
+  public boolean isValid(@NotNull final Document document) {
+    boolean isLineValid = line < document.getLineCount() && line >= 0;
+    if (!isLineValid) return false;
+    boolean isStartValid = start >= 0 && start < document.getLineEndOffset(line);
+    boolean isLengthValid = (getRealStartOffset(document) + length) <= document.getTextLength();
+    return isLengthValid && isStartValid;
+  }
+
+  private JBColor getColor() {
+    if (myStatus == StudyStatus.Solved) {
+      return JBColor.GREEN;
+    }
+    if (myStatus == StudyStatus.Failed) {
+      return JBColor.RED;
+    }
+    return JBColor.BLUE;
+  }
+
+  public int getRealStartOffset(@NotNull final Document document) {
+    return document.getLineStartOffset(line) + start;
+  }
+
+  /**
+   * Initializes window
+   *
+   * @param file task file which window belongs to
+   */
+  public void init(final TaskFile file, boolean isRestarted) {
+    if (!isRestarted) {
+      myInitialLine = line;
+      myInitialLength = length;
+      myInitialStart = start;
+    }
+    myTaskFile = file;
+  }
+
+  public TaskFile getTaskFile() {
+    return myTaskFile;
+  }
+
+  @Override
+  public int compareTo(@NotNull Object o) {
+    TaskWindow taskWindow = (TaskWindow)o;
+    if (taskWindow.getTaskFile() != myTaskFile) {
+      throw new ClassCastException();
+    }
+    int lineDiff = line - taskWindow.line;
+    if (lineDiff == 0) {
+      return start - taskWindow.start;
+    }
+    return lineDiff;
+  }
+
+  /**
+   * Returns window to its initial state
+   */
+  public void reset() {
+    myStatus = StudyStatus.Unchecked;
+    line = myInitialLine;
+    start = myInitialStart;
+    length = myInitialLength;
+  }
+
+  public String getHint() {
+    return hint;
+  }
+
+  public String getPossibleAnswer() {
+    return possibleAnswer;
+  }
+
+  public void setPossibleAnswer(String possibleAnswer) {
+    this.possibleAnswer = possibleAnswer;
+  }
+
+  public int getIndex() {
+    return myIndex;
+  }
+}
\ No newline at end of file
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/course/UserTest.java b/python/edu/learn-python/src/com/jetbrains/python/edu/course/UserTest.java
new file mode 100644
index 0000000..8133e91
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/course/UserTest.java
@@ -0,0 +1,41 @@
+package com.jetbrains.python.edu.course;
+
+public class UserTest {
+  private String input;
+  private String output;
+  private StringBuilder myInputBuffer = new StringBuilder();
+  private StringBuilder myOutputBuffer =  new StringBuilder();
+  private boolean myEditable = false;
+
+  public String getInput() {
+    return input;
+  }
+
+  public void setInput(String input) {
+    this.input = input;
+  }
+
+  public String getOutput() {
+    return output;
+  }
+
+  public void setOutput(String output) {
+    this.output = output;
+  }
+
+  public StringBuilder getInputBuffer() {
+    return myInputBuffer;
+  }
+
+  public StringBuilder getOutputBuffer() {
+    return myOutputBuffer;
+  }
+
+  public boolean isEditable() {
+    return myEditable;
+  }
+
+  public void setEditable(boolean editable) {
+    myEditable = editable;
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/editor/StudyEditor.java b/python/edu/learn-python/src/com/jetbrains/python/edu/editor/StudyEditor.java
new file mode 100644
index 0000000..69c5acc
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/editor/StudyEditor.java
@@ -0,0 +1,347 @@
+package com.jetbrains.python.edu.editor;
+
+import com.intellij.codeHighlighting.BackgroundEditorHighlighter;
+import com.intellij.ide.structureView.StructureViewBuilder;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.EditorFactory;
+import com.intellij.openapi.editor.colors.EditorColorsManager;
+import com.intellij.openapi.editor.colors.EditorColorsScheme;
+import com.intellij.openapi.editor.impl.DocumentImpl;
+import com.intellij.openapi.fileEditor.*;
+import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
+import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorImpl;
+import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.pom.Navigatable;
+import com.intellij.ui.HideableTitledPanel;
+import com.intellij.ui.JBColor;
+import com.intellij.util.ui.UIUtil;
+import com.jetbrains.python.edu.StudyDocumentListener;
+import com.jetbrains.python.edu.StudyTaskManager;
+import com.jetbrains.python.edu.actions.*;
+import com.jetbrains.python.edu.course.Task;
+import com.jetbrains.python.edu.course.TaskFile;
+import icons.StudyIcons;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyledDocument;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeListener;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Implementation of StudyEditor which has panel with special buttons and task text
+ * also @see {@link com.jetbrains.python.edu.editor.StudyFileEditorProvider}
+ */
+public class StudyEditor implements TextEditor {
+  private static final String TASK_TEXT_HEADER = "Task Text";
+  private final FileEditor myDefaultEditor;
+  private final JComponent myComponent;
+  private JButton myCheckButton;
+  private JButton myNextTaskButton;
+  private JButton myPrevTaskButton;
+  private JButton myRefreshButton;
+  private static final Map<Document, StudyDocumentListener> myDocumentListeners = new HashMap<Document, StudyDocumentListener>();
+  private Project myProject;
+
+  public JButton getCheckButton() {
+    return myCheckButton;
+  }
+
+  public JButton getPrevTaskButton() {
+    return myPrevTaskButton;
+  }
+
+  private static JButton addButton(@NotNull final JComponent parentComponent, String toolTipText, Icon icon) {
+    JButton newButton = new JButton();
+    newButton.setToolTipText(toolTipText);
+    newButton.setIcon(icon);
+    newButton.setSize(new Dimension(icon.getIconWidth(), icon.getIconHeight()));
+    parentComponent.add(newButton);
+    return newButton;
+  }
+
+  public static void addDocumentListener(@NotNull final Document document, @NotNull final StudyDocumentListener listener) {
+    myDocumentListeners.put(document, listener);
+  }
+
+  @Nullable
+  public static StudyDocumentListener getListener(@NotNull final Document document) {
+    return myDocumentListeners.get(document);
+  }
+
+  public StudyEditor(@NotNull final Project project, @NotNull final VirtualFile file) {
+    myProject = project;
+    myDefaultEditor = TextEditorProvider.getInstance().createEditor(myProject, file);
+    myComponent = myDefaultEditor.getComponent();
+    JPanel studyPanel = new JPanel();
+    studyPanel.setLayout(new BoxLayout(studyPanel, BoxLayout.Y_AXIS));
+    TaskFile taskFile = StudyTaskManager.getInstance(myProject).getTaskFile(file);
+    if (taskFile != null) {
+      Task currentTask = taskFile.getTask();
+      String taskText = currentTask.getResourceText(project, currentTask.getText(), false);
+      initializeTaskText(studyPanel, taskText);
+      JPanel studyButtonPanel = new JPanel(new GridLayout(1, 2));
+      JPanel taskActionsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+      studyButtonPanel.add(taskActionsPanel);
+      studyButtonPanel.add(new JPanel());
+      initializeButtons(taskActionsPanel, taskFile);
+      studyPanel.add(studyButtonPanel);
+      myComponent.add(studyPanel, BorderLayout.NORTH);
+    }
+  }
+
+  private static void initializeTaskText(JPanel studyPanel, @Nullable String taskText) {
+    JTextPane taskTextPane = new JTextPane();
+    taskTextPane.setContentType("text/html");
+    taskTextPane.setEditable(false);
+    taskTextPane.setText(taskText);
+    EditorColorsScheme editorColorsScheme = EditorColorsManager.getInstance().getGlobalScheme();
+    int fontSize = editorColorsScheme.getEditorFontSize();
+    String fontName = editorColorsScheme.getEditorFontName();
+    setJTextPaneFont(taskTextPane, new Font(fontName, Font.PLAIN, fontSize), JBColor.BLACK);
+    taskTextPane.setBackground(UIUtil.getPanelBackground());
+    taskTextPane.setBorder(new EmptyBorder(15, 20, 0, 100));
+    HideableTitledPanel taskTextPanel = new HideableTitledPanel(TASK_TEXT_HEADER, taskTextPane, true);
+    taskTextPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
+    studyPanel.add(taskTextPanel);
+  }
+
+  private static void setJTextPaneFont(JTextPane jtp, Font font, Color c) {
+    MutableAttributeSet attrs = jtp.getInputAttributes();
+    StyleConstants.setFontFamily(attrs, font.getFamily());
+    StyleConstants.setFontSize(attrs, font.getSize());
+    StyleConstants.setItalic(attrs, (font.getStyle() & Font.ITALIC) != 0);
+    StyleConstants.setBold(attrs, (font.getStyle() & Font.BOLD) != 0);
+    StyleConstants.setForeground(attrs, c);
+    StyledDocument doc = jtp.getStyledDocument();
+    doc.setCharacterAttributes(0, doc.getLength() + 1, attrs, false);
+  }
+
+  private void initializeButtons(@NotNull final JPanel taskActionsPanel, @NotNull final TaskFile taskFile) {
+    myCheckButton = addButton(taskActionsPanel, "Check task", StudyIcons.Resolve);
+    myPrevTaskButton = addButton(taskActionsPanel, "Prev Task", StudyIcons.Prev);
+    myNextTaskButton = addButton(taskActionsPanel, "Next Task", StudyIcons.Next);
+    myRefreshButton = addButton(taskActionsPanel, "Start task again", StudyIcons.Refresh24);
+    if (!taskFile.getTask().getUserTests().isEmpty()) {
+      JButton runButton = addButton(taskActionsPanel, "Run", StudyIcons.Run);
+      runButton.addActionListener(new ActionListener() {
+        @Override
+        public void actionPerformed(ActionEvent e) {
+          StudyRunAction studyRunAction = (StudyRunAction)ActionManager.getInstance().getAction("StudyRunAction");
+          studyRunAction.run(myProject);
+        }
+      });
+      JButton watchInputButton = addButton(taskActionsPanel, "Watch test input", StudyIcons.WatchInput);
+      watchInputButton.addActionListener(new ActionListener() {
+        @Override
+        public void actionPerformed(ActionEvent e) {
+          StudyEditInputAction studyEditInputAction = (StudyEditInputAction)ActionManager.getInstance().getAction("WatchInputAction");
+          studyEditInputAction.showInput(myProject);
+        }
+      });
+    }
+    myCheckButton.addActionListener(new ActionListener() {
+      @Override
+      public void actionPerformed(ActionEvent e) {
+        StudyCheckAction studyCheckAction = (StudyCheckAction)ActionManager.getInstance().getAction("CheckAction");
+        studyCheckAction.check(myProject);
+      }
+    });
+
+    myNextTaskButton.addActionListener(new ActionListener() {
+      @Override
+      public void actionPerformed(ActionEvent e) {
+        StudyNextStudyTaskAction studyNextTaskAction = (StudyNextStudyTaskAction)ActionManager.getInstance().getAction("NextTaskAction");
+        studyNextTaskAction.navigateTask(myProject);
+      }
+    });
+    myPrevTaskButton.addActionListener(new ActionListener() {
+      @Override
+      public void actionPerformed(ActionEvent e) {
+        StudyPreviousStudyTaskAction
+          prevTaskAction = (StudyPreviousStudyTaskAction)ActionManager.getInstance().getAction("PreviousTaskAction");
+        prevTaskAction.navigateTask(myProject);
+      }
+    });
+    myRefreshButton.addActionListener(new ActionListener() {
+      @Override
+      public void actionPerformed(ActionEvent e) {
+        StudyRefreshTaskAction studyRefreshTaskAction = (StudyRefreshTaskAction)ActionManager.getInstance().getAction("RefreshTaskAction");
+        studyRefreshTaskAction.refresh(myProject);
+      }
+    });
+  }
+
+  public JButton getNextTaskButton() {
+    return myNextTaskButton;
+  }
+
+  public JButton getRefreshButton() {
+    return myRefreshButton;
+  }
+
+  FileEditor getDefaultEditor() {
+    return myDefaultEditor;
+  }
+
+  @NotNull
+  @Override
+  public JComponent getComponent() {
+    return myComponent;
+  }
+
+  @Nullable
+  @Override
+  public JComponent getPreferredFocusedComponent() {
+    return myDefaultEditor.getPreferredFocusedComponent();
+  }
+
+  @NotNull
+  @Override
+  public String getName() {
+    return "Study Editor";
+  }
+
+  @NotNull
+  @Override
+  public FileEditorState getState(@NotNull FileEditorStateLevel level) {
+    return myDefaultEditor.getState(level);
+  }
+
+  @Override
+  public void setState(@NotNull FileEditorState state) {
+    myDefaultEditor.setState(state);
+  }
+
+  @Override
+  public boolean isModified() {
+    return myDefaultEditor.isModified();
+  }
+
+  @Override
+  public boolean isValid() {
+    return myDefaultEditor.isValid();
+  }
+
+  @Override
+  public void selectNotify() {
+    myDefaultEditor.selectNotify();
+  }
+
+  @Override
+  public void deselectNotify() {
+    myDefaultEditor.deselectNotify();
+  }
+
+  @Override
+  public void addPropertyChangeListener(@NotNull PropertyChangeListener listener) {
+    myDefaultEditor.addPropertyChangeListener(listener);
+  }
+
+  @Override
+  public void removePropertyChangeListener(@NotNull PropertyChangeListener listener) {
+    myDefaultEditor.removePropertyChangeListener(listener);
+  }
+
+  @Nullable
+  @Override
+  public BackgroundEditorHighlighter getBackgroundHighlighter() {
+    return myDefaultEditor.getBackgroundHighlighter();
+  }
+
+  @Nullable
+  @Override
+  public FileEditorLocation getCurrentLocation() {
+    return myDefaultEditor.getCurrentLocation();
+  }
+
+  @Nullable
+  @Override
+  public StructureViewBuilder getStructureViewBuilder() {
+    return myDefaultEditor.getStructureViewBuilder();
+  }
+
+  @Override
+  public void dispose() {
+    Disposer.dispose(myDefaultEditor);
+  }
+
+  @Nullable
+  @Override
+  public <T> T getUserData(@NotNull Key<T> key) {
+    return myDefaultEditor.getUserData(key);
+  }
+
+  @Override
+  public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {
+    myDefaultEditor.putUserData(key, value);
+  }
+
+
+  @Nullable
+  public static StudyEditor getSelectedStudyEditor(@NotNull final Project project) {
+    try {
+      FileEditor fileEditor = FileEditorManagerEx.getInstanceEx(project).getSplitters().getCurrentWindow().
+        getSelectedEditor().getSelectedEditorWithProvider().getFirst();
+      if (fileEditor instanceof StudyEditor) {
+        return (StudyEditor)fileEditor;
+      }
+    } catch (Exception e) {
+      return null;
+    }
+    return null;
+  }
+
+  @Nullable
+  public static Editor getSelectedEditor(@NotNull final Project project) {
+    StudyEditor studyEditor = getSelectedStudyEditor(project);
+    if (studyEditor != null) {
+      FileEditor defaultEditor = studyEditor.getDefaultEditor();
+      if (defaultEditor instanceof PsiAwareTextEditorImpl) {
+        return ((PsiAwareTextEditorImpl)defaultEditor).getEditor();
+      }
+    }
+    return null;
+  }
+
+  public static void removeListener(Document document) {
+    myDocumentListeners.remove(document);
+  }
+
+  @NotNull
+  @Override
+  public Editor getEditor() {
+    if (myDefaultEditor instanceof TextEditor)
+      return ((TextEditor)myDefaultEditor).getEditor();
+    return EditorFactory.getInstance().createViewer(new DocumentImpl(""), myProject);
+  }
+
+  @Override
+  public boolean canNavigateTo(@NotNull Navigatable navigatable) {
+    if (myDefaultEditor instanceof TextEditor) {
+      ((TextEditor)myDefaultEditor).canNavigateTo(navigatable);
+    }
+    return false;
+  }
+
+  @Override
+  public void navigateTo(@NotNull Navigatable navigatable) {
+    if (myDefaultEditor instanceof TextEditor) {
+      ((TextEditor)myDefaultEditor).navigateTo(navigatable);
+    }
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/editor/StudyFileEditorProvider.java b/python/edu/learn-python/src/com/jetbrains/python/edu/editor/StudyFileEditorProvider.java
new file mode 100644
index 0000000..631b5a9
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/editor/StudyFileEditorProvider.java
@@ -0,0 +1,64 @@
+package com.jetbrains.python.edu.editor;
+
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.fileEditor.FileEditorPolicy;
+import com.intellij.openapi.fileEditor.FileEditorProvider;
+import com.intellij.openapi.fileEditor.FileEditorState;
+import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jdom.Element;
+import org.jetbrains.annotations.NotNull;
+import com.jetbrains.python.edu.StudyTaskManager;
+import com.jetbrains.python.edu.course.TaskFile;
+
+/**
+ * User: lia
+ * Date: 10.05.14
+ * Time: 12:45
+ */
+class StudyFileEditorProvider implements FileEditorProvider, DumbAware {
+  static final private String EDITOR_TYPE_ID = "StudyEditor";
+  final private FileEditorProvider defaultTextEditorProvider = TextEditorProvider.getInstance();
+
+  @Override
+  public boolean accept(@NotNull Project project, @NotNull VirtualFile file) {
+    TaskFile taskFile = StudyTaskManager.getInstance(project).getTaskFile(file);
+    return taskFile != null && !taskFile.isUserCreated();
+  }
+
+  @NotNull
+  @Override
+  public FileEditor createEditor(@NotNull Project project, @NotNull VirtualFile file) {
+    return new StudyEditor(project, file);
+  }
+
+  @Override
+  public void disposeEditor(@NotNull FileEditor editor) {
+    defaultTextEditorProvider.disposeEditor(editor);
+  }
+
+  @NotNull
+  @Override
+  public FileEditorState readState(@NotNull Element sourceElement, @NotNull Project project, @NotNull VirtualFile file) {
+    return defaultTextEditorProvider.readState(sourceElement, project, file);
+  }
+
+  @Override
+  public void writeState(@NotNull FileEditorState state, @NotNull Project project, @NotNull Element targetElement) {
+    defaultTextEditorProvider.writeState(state, project, targetElement);
+  }
+
+  @NotNull
+  @Override
+  public String getEditorTypeId() {
+    return EDITOR_TYPE_ID;
+  }
+
+  @NotNull
+  @Override
+  public FileEditorPolicy getPolicy() {
+    return FileEditorPolicy.HIDE_DEFAULT_EDITOR;
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/projectView/StudyDirectoryNode.java b/python/edu/learn-python/src/com/jetbrains/python/edu/projectView/StudyDirectoryNode.java
new file mode 100644
index 0000000..abf648c
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/projectView/StudyDirectoryNode.java
@@ -0,0 +1,112 @@
+package com.jetbrains.python.edu.projectView;
+
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.ide.projectView.ViewSettings;
+import com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.PsiElement;
+import com.intellij.ui.JBColor;
+import com.intellij.ui.SimpleTextAttributes;
+import com.jetbrains.python.edu.StudyTaskManager;
+import com.jetbrains.python.edu.StudyUtils;
+import com.jetbrains.python.edu.course.*;
+import icons.StudyIcons;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class StudyDirectoryNode extends PsiDirectoryNode {
+  private final PsiDirectory myValue;
+  private final Project myProject;
+
+  public StudyDirectoryNode(@NotNull final Project project,
+                            PsiDirectory value,
+                            ViewSettings viewSettings) {
+    super(project, value, viewSettings);
+    myValue = value;
+    myProject = project;
+  }
+
+  @Override
+  protected void updateImpl(PresentationData data) {
+    data.setIcon(StudyIcons.Unchecked);
+    String valueName = myValue.getName();
+    StudyTaskManager studyTaskManager = StudyTaskManager.getInstance(myProject);
+    Course course = studyTaskManager.getCourse();
+    if (course == null) {
+      return;
+    }
+    if (valueName.equals(myProject.getName())) {
+      data.clearText();
+      data.addText(course.getName(), new SimpleTextAttributes(SimpleTextAttributes.STYLE_BOLD, JBColor.BLUE));
+      data.addText(" (" + valueName + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES);
+      return;
+    }
+    if (valueName.contains(Task.TASK_DIR)) {
+      TaskFile file = null;
+      for (PsiElement child : myValue.getChildren()) {
+        VirtualFile virtualFile = child.getContainingFile().getVirtualFile();
+        file = studyTaskManager.getTaskFile(virtualFile);
+        if (file != null) {
+          break;
+        }
+      }
+      if (file != null) {
+        Task task = file.getTask();
+        setStudyAttributes(task, data, task.getName());
+      }
+    }
+    if (valueName.contains(Lesson.LESSON_DIR)) {
+      int lessonIndex = Integer.parseInt(valueName.substring(Lesson.LESSON_DIR.length())) - 1;
+      Lesson lesson = course.getLessons().get(lessonIndex);
+      setStudyAttributes(lesson, data, lesson.getName());
+    }
+
+    if (valueName.contains(Course.PLAYGROUND_DIR)) {
+      if (myValue.getParent() != null) {
+        if (!myValue.getParent().getName().contains(Course.PLAYGROUND_DIR)) {
+          data.setPresentableText(Course.PLAYGROUND_DIR);
+          data.setIcon(StudyIcons.Playground);
+          return;
+        }
+      }
+    }
+    data.setPresentableText(valueName);
+  }
+
+  @Override
+  public int getTypeSortWeight(boolean sortByType) {
+    String name = myValue.getName();
+    if (name.contains(Lesson.LESSON_DIR) || name.contains(Task.TASK_DIR)) {
+      String logicalName = name.contains(Lesson.LESSON_DIR) ? Lesson.LESSON_DIR : Task.TASK_DIR;
+      return StudyUtils.getIndex(name, logicalName) + 1;
+    }
+    return name.contains(Course.PLAYGROUND_DIR) ? 0 : 3;
+  }
+
+  private static void setStudyAttributes(Stateful stateful, PresentationData data, String additionalName) {
+    StudyStatus taskStatus = stateful.getStatus();
+    switch (taskStatus) {
+      case Unchecked: {
+        updatePresentation(data, additionalName, JBColor.blue, StudyIcons.Unchecked);
+        break;
+      }
+      case Solved: {
+        updatePresentation(data, additionalName, new JBColor(new Color(0, 134, 0), new Color(98, 150, 85)), StudyIcons.Checked);
+        break;
+      }
+      case Failed: {
+        updatePresentation(data, additionalName, JBColor.RED, StudyIcons.Failed);
+      }
+    }
+  }
+
+  private static void updatePresentation(PresentationData data, String additionalName, JBColor color, Icon icon) {
+    data.clearText();
+    data.addText(additionalName, new SimpleTextAttributes(Font.PLAIN, color));
+    data.setIcon(icon);
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/projectView/StudyTreeStructureProvider.java b/python/edu/learn-python/src/com/jetbrains/python/edu/projectView/StudyTreeStructureProvider.java
new file mode 100644
index 0000000..e301bc3
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/projectView/StudyTreeStructureProvider.java
@@ -0,0 +1,83 @@
+package com.jetbrains.python.edu.projectView;
+
+import com.intellij.ide.projectView.TreeStructureProvider;
+import com.intellij.ide.projectView.ViewSettings;
+import com.intellij.ide.projectView.impl.nodes.PsiFileNode;
+import com.intellij.ide.util.treeView.AbstractTreeNode;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiDirectory;
+import com.jetbrains.python.edu.StudyTaskManager;
+import com.jetbrains.python.edu.course.Course;
+import com.jetbrains.python.edu.course.Task;
+import com.jetbrains.python.edu.course.TaskFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class StudyTreeStructureProvider implements TreeStructureProvider, DumbAware {
+  @NotNull
+  @Override
+  public Collection<AbstractTreeNode> modify(@NotNull AbstractTreeNode parent,
+                                             @NotNull Collection<AbstractTreeNode> children,
+                                             ViewSettings settings) {
+    if (!needModify(parent)) {
+      return children;
+    }
+    Collection<AbstractTreeNode> nodes = new ArrayList<AbstractTreeNode>();
+    for (AbstractTreeNode node : children) {
+      Project project = node.getProject();
+      if (project != null) {
+        if (node.getValue() instanceof PsiDirectory) {
+          PsiDirectory nodeValue = (PsiDirectory)node.getValue();
+          if (!nodeValue.getName().contains(Task.USER_TESTS)) {
+            StudyDirectoryNode newNode = new StudyDirectoryNode(project, nodeValue, settings);
+            nodes.add(newNode);
+          }
+        }
+        else {
+          if (parent instanceof StudyDirectoryNode) {
+            if (node instanceof PsiFileNode) {
+              PsiFileNode psiFileNode = (PsiFileNode)node;
+              VirtualFile virtualFile = psiFileNode.getVirtualFile();
+              if (virtualFile == null) {
+                return nodes;
+              }
+              TaskFile taskFile = StudyTaskManager.getInstance(project).getTaskFile(virtualFile);
+              if (taskFile != null) {
+                nodes.add(node);
+              }
+              String parentName = parent.getName();
+              if (parentName != null) {
+                if (parentName.equals(Course.PLAYGROUND_DIR)) {
+                  nodes.add(node);
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+    return nodes;
+  }
+
+  private static boolean needModify(AbstractTreeNode parent) {
+    Project project = parent.getProject();
+    if (project != null) {
+      StudyTaskManager studyTaskManager = StudyTaskManager.getInstance(project);
+      if (studyTaskManager.getCourse() == null) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  @Nullable
+  @Override
+  public Object getData(Collection<AbstractTreeNode> selected, String dataName) {
+    return null;
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyCondition.java b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyCondition.java
new file mode 100644
index 0000000..5add6c9
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyCondition.java
@@ -0,0 +1,25 @@
+package com.jetbrains.python.edu.ui;
+
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Condition;
+import com.jetbrains.python.edu.StudyTaskManager;
+
+/**
+ * author: liana
+ * data: 7/29/14.
+ */
+public class StudyCondition implements Condition, DumbAware {
+  public static boolean VALUE = false;
+  @Override
+  public boolean value(Object o) {
+    if (o instanceof Project) {
+      Project project = (Project) o;
+      StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
+      if (taskManager.getCourse() != null) {
+        VALUE = true;
+      }
+    }
+    return false;
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.form b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.form
new file mode 100644
index 0000000..133c38d
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.form
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.python.edu.ui.StudyNewProjectPanel">
+  <grid id="27dc6" binding="myContentPanel" layout-manager="GridLayoutManager" row-count="2" column-count="4" 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="500" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <grid id="54488" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="1" column="1" row-span="1" col-span="3" vsize-policy="2" hsize-policy="4" anchor="0" fill="3" indent="0" use-parent-layout="false">
+            <minimum-size width="-1" height="60"/>
+            <preferred-size width="-1" height="60"/>
+          </grid>
+        </constraints>
+        <properties/>
+        <border type="line">
+          <color color="-6709600"/>
+        </border>
+        <children>
+          <component id="213f6" class="javax.swing.JLabel" binding="myAuthorLabel">
+            <constraints>
+              <grid row="0" 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=""/>
+            </properties>
+          </component>
+          <component id="d754d" class="javax.swing.JLabel" binding="myDescriptionLabel">
+            <constraints>
+              <grid row="1" 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=""/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <component id="6c40c" class="javax.swing.JLabel">
+        <constraints>
+          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false">
+            <preferred-size width="81" height="-1"/>
+          </grid>
+        </constraints>
+        <properties>
+          <font/>
+          <horizontalTextPosition value="0"/>
+          <text value="Courses:"/>
+        </properties>
+      </component>
+      <component id="21ac6" class="javax.swing.JComboBox" binding="myCoursesComboBox">
+        <constraints>
+          <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+      </component>
+      <component id="5c614" class="com.intellij.openapi.ui.FixedSizeButton" binding="myBrowseButton">
+        <constraints>
+          <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false">
+            <minimum-size width="30" height="25"/>
+          </grid>
+        </constraints>
+        <properties/>
+      </component>
+      <component id="f1e10" class="javax.swing.JButton" binding="myRefreshButton">
+        <constraints>
+          <grid row="0" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false">
+            <minimum-size width="30" height="23"/>
+          </grid>
+        </constraints>
+        <properties>
+          <hideActionText value="false"/>
+          <text value=""/>
+          <toolTipText value="Refresh course list"/>
+          <verticalAlignment value="1"/>
+          <verticalTextPosition value="1"/>
+        </properties>
+      </component>
+    </children>
+  </grid>
+</form>
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.java b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.java
new file mode 100644
index 0000000..0f1ec08
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.java
@@ -0,0 +1,196 @@
+package com.jetbrains.python.edu.ui;
+
+import com.intellij.facet.ui.FacetValidatorsManager;
+import com.intellij.facet.ui.ValidationResult;
+import com.intellij.openapi.fileChooser.FileChooser;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.Consumer;
+import com.jetbrains.python.edu.StudyDirectoryProjectGenerator;
+import com.jetbrains.python.edu.StudyUtils;
+import com.jetbrains.python.edu.course.CourseInfo;
+import icons.StudyIcons;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * author: liana
+ * data: 7/31/14.
+ */
+public class StudyNewProjectPanel{
+  private Set<CourseInfo> myAvailableCourses = new HashSet<CourseInfo>();
+  private JComboBox myCoursesComboBox;
+  private JButton myBrowseButton;
+  private JButton myRefreshButton;
+  private JPanel myContentPanel;
+  private JLabel myAuthorLabel;
+  private JLabel myDescriptionLabel;
+  private final StudyDirectoryProjectGenerator myGenerator;
+  private static final String CONNECTION_ERROR = "<html>Failed to download courses.<br>Check your Internet connection.</html>";
+  private static final String INVALID_COURSE = "Selected course is invalid";
+  private FacetValidatorsManager myValidationManager;
+
+  public StudyNewProjectPanel(StudyDirectoryProjectGenerator generator) {
+    myGenerator = generator;
+    Map<CourseInfo, File> courses = myGenerator.getCourses();
+    if (courses.isEmpty()) {
+      setError(CONNECTION_ERROR);
+    }
+    else {
+      myAvailableCourses = courses.keySet();
+      for (CourseInfo courseInfo : myAvailableCourses) {
+        myCoursesComboBox.addItem(courseInfo);
+      }
+      myAuthorLabel.setText("Author: " + StudyUtils.getFirst(myAvailableCourses).getAuthor());
+      myDescriptionLabel.setText(StudyUtils.getFirst(myAvailableCourses).getDescription());
+      //setting the first course in list as selected
+      myGenerator.setSelectedCourse(StudyUtils.getFirst(myAvailableCourses));
+      setOK();
+    }
+    initListeners();
+    myRefreshButton.setVisible(true);
+    myRefreshButton.setIcon(StudyIcons.Refresh);
+  }
+
+  private void initListeners() {
+
+    final FileChooserDescriptor fileChooser = new FileChooserDescriptor(true, false, false, true, false, false) {
+      @Override
+      public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) {
+        return file.isDirectory() || StudyUtils.isZip(file.getName());
+      }
+
+      @Override
+      public boolean isFileSelectable(VirtualFile file) {
+        return StudyUtils.isZip(file.getName());
+      }
+    };
+    myBrowseButton.addActionListener(new ActionListener() {
+      @Override
+      public void actionPerformed(ActionEvent e) {
+        FileChooser.chooseFile(fileChooser, null, null,
+                               new Consumer<VirtualFile>() {
+                                 @Override
+                                 public void consume(VirtualFile file) {
+                                   String fileName = file.getPath();
+                                   int oldSize = myAvailableCourses.size();
+                                   CourseInfo courseInfo = myGenerator.addLocalCourse(fileName);
+                                   if (courseInfo != null)  {
+                                     if (oldSize != myAvailableCourses.size()) {
+                                       myCoursesComboBox.addItem(courseInfo);
+                                     }
+                                     myCoursesComboBox.setSelectedItem(courseInfo);
+                                     setOK();
+                                   }
+                                   else {
+                                     setError(INVALID_COURSE);
+                                     myCoursesComboBox.removeAllItems();
+                                     myCoursesComboBox.addItem(CourseInfo.INVALID_COURSE);
+                                     for (CourseInfo course : myAvailableCourses) {
+                                       myCoursesComboBox.addItem(course);
+                                     }
+                                     myCoursesComboBox.setSelectedItem(CourseInfo.INVALID_COURSE);
+                                   }
+                                 }
+                               });
+      }
+    });
+    myRefreshButton.addActionListener(new RefreshActionListener());
+    myCoursesComboBox.addActionListener(new CourseSelectedListener());
+  }
+
+  private void setError(String errorMessage) {
+    myGenerator.setValidationResult(new ValidationResult(errorMessage));
+    if (myValidationManager != null) {
+      myValidationManager.validate();
+    }
+  }
+
+  private void setOK() {
+    myGenerator.setValidationResult(ValidationResult.OK);
+    if (myValidationManager != null) {
+      myValidationManager.validate();
+    }
+  }
+
+  public JPanel getContentPanel() {
+    return myContentPanel;
+  }
+
+  public void registerValidators(final FacetValidatorsManager manager) {
+    myValidationManager = manager;
+  }
+
+
+  /**
+   * Handles refreshing courses
+   * Old courses added to new courses only if their
+   * meta file still exists in local file system
+   */
+  private class RefreshActionListener implements ActionListener {
+    @Override
+    public void actionPerformed(ActionEvent e) {
+      myGenerator.downloadAndUnzip(true);
+      Map<CourseInfo, File> downloadedCourses = myGenerator.loadCourses();
+      if (downloadedCourses.isEmpty()) {
+        setError(CONNECTION_ERROR);
+        return;
+      }
+      Map<CourseInfo, File> oldCourses = myGenerator.getLoadedCourses();
+      Map<CourseInfo, File> newCourses = new HashMap<CourseInfo, File>();
+      for (Map.Entry<CourseInfo, File> course : oldCourses.entrySet()) {
+        File courseFile = course.getValue();
+        if (courseFile.exists()) {
+          newCourses.put(course.getKey(), courseFile);
+        }
+      }
+      for (Map.Entry<CourseInfo, File> course : downloadedCourses.entrySet()) {
+        CourseInfo courseName = course.getKey();
+        if (newCourses.get(courseName) == null) {
+          newCourses.put(courseName, course.getValue());
+        }
+      }
+      myCoursesComboBox.removeAllItems();
+
+      for (CourseInfo courseInfo : newCourses.keySet()) {
+        myCoursesComboBox.addItem(courseInfo);
+      }
+      myGenerator.setSelectedCourse(StudyUtils.getFirst(newCourses.keySet()));
+
+      myGenerator.setCourses(newCourses);
+      myAvailableCourses = newCourses.keySet();
+      myGenerator.flushCache();
+    }
+  }
+
+
+  /**
+   * Handles selecting course in combo box
+   * Sets selected course in combo box as selected in
+   * {@link StudyNewProjectPanel#myGenerator}
+   */
+  private class CourseSelectedListener implements ActionListener {
+    @Override
+    public void actionPerformed(ActionEvent e) {
+      JComboBox cb = (JComboBox)e.getSource();
+      CourseInfo selectedCourse = (CourseInfo)cb.getSelectedItem();
+      if (selectedCourse == null || selectedCourse.equals(CourseInfo.INVALID_COURSE)) {
+        myAuthorLabel.setText("");
+        myDescriptionLabel.setText("");
+        return;
+      }
+      myAuthorLabel.setText("Author: " + selectedCourse.getAuthor());
+      myCoursesComboBox.removeItem(CourseInfo.INVALID_COURSE);
+      myDescriptionLabel.setText(selectedCourse.getDescription());
+      myGenerator.setSelectedCourse(selectedCourse);
+      setOK();
+    }
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyProgressBar.java b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyProgressBar.java
new file mode 100644
index 0000000..97fa00d
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyProgressBar.java
@@ -0,0 +1,92 @@
+package com.jetbrains.python.edu.ui;
+
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.ui.GraphicsConfig;
+import com.intellij.ui.ColorUtil;
+import com.intellij.ui.Gray;
+import com.intellij.ui.JBColor;
+import com.intellij.util.ui.GraphicsUtil;
+import com.intellij.util.ui.UIUtil;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.geom.Rectangle2D;
+
+public class StudyProgressBar extends JComponent implements DumbAware {
+  public static final Color BLUE = JBColor.BLUE;
+  private static final Color SHADOW1 = new JBColor(Gray._190, JBColor.border());
+  private static final Color SHADOW2 = Gray._105;
+  private static final int BRICK_WIDTH = 10;
+  private static final int BRICK_SPACE = 2;
+  private final int myHeight;
+  private final int myIndent;
+  private double myFraction = 0.0;
+  private Color myColor = BLUE;
+
+  public StudyProgressBar(double fraction, Color color, int height, int indent) {
+    myFraction = fraction;
+    myColor = color;
+    myHeight = height;
+    myIndent = indent;
+  }
+
+  private int getBricksToDraw(double fraction) {
+    int bricksTotal = (getWidth() - 8) / (BRICK_WIDTH + BRICK_SPACE);
+    return (int)(bricksTotal * fraction) + 1;
+  }
+
+  protected void paintComponent(Graphics g) {
+    final GraphicsConfig config = GraphicsUtil.setupAAPainting(g);
+    Graphics2D g2 = (Graphics2D)g;
+    if (myFraction > 1) {
+      myFraction = 1;
+    }
+
+    Dimension size = getSize();
+    double width = size.getWidth() - 2*myIndent;
+    g2.setPaint(UIUtil.getListBackground());
+    Rectangle2D rect = new Rectangle2D.Double(myIndent, 0, width, myHeight);
+    g2.fill(rect);
+
+    g2.setPaint(new JBColor(SHADOW1, JBColor.border()));
+    rect.setRect(myIndent, 0, width, myHeight);
+    int arcWidth = 5;
+    int arcHeight = 5;
+    g2.drawRoundRect(myIndent, 0, (int)width, myHeight, arcWidth, arcHeight);
+    g2.setPaint(SHADOW2);
+    g2.drawRoundRect(myIndent, 0, (int)width, myHeight, arcWidth, arcHeight);
+
+    int y_center = myHeight / 2;
+    int y_steps = myHeight / 2 - 3;
+    int alpha_step = y_steps > 0 ? (255 - 70) / y_steps : 255 - 70;
+    int x_offset = 4;
+
+    g.setClip(4 + myIndent, 3, (int)width - 6, myHeight - 4);
+
+    int bricksToDraw = myFraction == 0 ? 0 : getBricksToDraw(myFraction);
+    for (int i = 0; i < bricksToDraw; i++) {
+      g2.setPaint(myColor);
+      UIUtil.drawLine(g2, x_offset, y_center, x_offset + BRICK_WIDTH - 1, y_center);
+      for (int j = 0; j < y_steps; j++) {
+        Color color = ColorUtil.toAlpha(myColor, 255 - alpha_step * (j + 1));
+        g2.setPaint(color);
+        UIUtil.drawLine(g2, x_offset, y_center - 1 - j, x_offset + BRICK_WIDTH - 1, y_center - 1 - j);
+        if (!(y_center % 2 != 0 && j == y_steps - 1)) {
+          UIUtil.drawLine(g2, x_offset, y_center + 1 + j, x_offset + BRICK_WIDTH - 1, y_center + 1 + j);
+        }
+      }
+      g2.setColor(
+        ColorUtil.toAlpha(myColor, 255 - alpha_step * (y_steps / 2 + 1)));
+      g2.drawRect(x_offset, y_center - y_steps, BRICK_WIDTH - 1, myHeight - 7);
+      x_offset += BRICK_WIDTH + BRICK_SPACE;
+    }
+    config.restore();
+  }
+
+  @Override
+  public Dimension getMaximumSize() {
+    Dimension dimension = super.getMaximumSize();
+    dimension.height = myHeight + 10;
+    return dimension;
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyTestContentPanel.java b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyTestContentPanel.java
new file mode 100644
index 0000000..dee4fba
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyTestContentPanel.java
@@ -0,0 +1,67 @@
+package com.jetbrains.python.edu.ui;
+
+import com.intellij.ui.DocumentAdapter;
+import com.intellij.ui.components.JBScrollPane;
+import org.jetbrains.annotations.NotNull;
+import com.jetbrains.python.edu.course.UserTest;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import javax.swing.text.BadLocationException;
+import java.awt.*;
+
+public class StudyTestContentPanel extends JPanel {
+  public static final Dimension PREFERRED_SIZE = new Dimension(300, 200);
+  private static final Font HEADER_FONT = new Font("Arial", Font.BOLD, 16);
+  private final JTextArea myInputArea = new JTextArea();
+  private final JTextArea myOutputArea = new JTextArea();
+  public StudyTestContentPanel(UserTest userTest) {
+    this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+    initContentLabel("input", myInputArea);
+    myInputArea.getDocument().addDocumentListener(new BufferUpdater(userTest.getInputBuffer()));
+    myOutputArea.getDocument().addDocumentListener(new BufferUpdater(userTest.getOutputBuffer()));
+    initContentLabel("output", myOutputArea);
+    setEditable(userTest.isEditable());
+  }
+
+  private void initContentLabel(final String headerText, @NotNull final JTextArea contentArea) {
+    JLabel headerLabel = new JLabel(headerText);
+    headerLabel.setFont(HEADER_FONT);
+    this.add(headerLabel);
+    this.add(new JSeparator(SwingConstants.HORIZONTAL));
+    JScrollPane scroll = new JBScrollPane(contentArea);
+    scroll.setPreferredSize(PREFERRED_SIZE);
+    this.add(scroll);
+  }
+
+  private void setEditable(boolean isEditable) {
+    myInputArea.setEditable(isEditable);
+    myOutputArea.setEditable(isEditable);
+  }
+  public void addInputContent(final String content) {
+    myInputArea.setText(content);
+  }
+
+  public  void addOutputContent(final String content) {
+    myOutputArea.setText(content);
+  }
+
+  private class BufferUpdater extends DocumentAdapter {
+    private final StringBuilder myBuffer;
+
+    private BufferUpdater(StringBuilder buffer) {
+      myBuffer = buffer;
+    }
+
+    @Override
+    protected void textChanged(DocumentEvent e) {
+      myBuffer.delete(0, myBuffer.length());
+      try {
+        myBuffer.append(e.getDocument().getText(0, e.getDocument().getLength()));
+      }
+      catch (BadLocationException e1) {
+        e1.printStackTrace();
+      }
+    }
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyToolWindowFactory.java b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyToolWindowFactory.java
new file mode 100644
index 0000000..a553978
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyToolWindowFactory.java
@@ -0,0 +1,81 @@
+package com.jetbrains.python.edu.ui;
+
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.wm.ToolWindow;
+import com.intellij.openapi.wm.ToolWindowFactory;
+import com.intellij.ui.JBColor;
+import com.intellij.ui.content.Content;
+import com.intellij.ui.content.ContentFactory;
+import com.intellij.util.ui.UIUtil;
+import com.jetbrains.python.edu.StudyTaskManager;
+import com.jetbrains.python.edu.course.Course;
+import com.jetbrains.python.edu.course.Lesson;
+import com.jetbrains.python.edu.course.LessonInfo;
+import com.jetbrains.python.edu.course.StudyStatus;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.List;
+
+public class StudyToolWindowFactory implements ToolWindowFactory, DumbAware {
+  public static final String STUDY_TOOL_WINDOW = "Course Description";
+  JPanel contentPanel = new JPanel();
+
+  @Override
+  public void createToolWindowContent(@NotNull final Project project, @NotNull final ToolWindow toolWindow) {
+    if (StudyTaskManager.getInstance(project).getCourse() != null) {
+      contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.PAGE_AXIS));
+      contentPanel.add(Box.createRigidArea(new Dimension(10, 0)));
+      StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
+      Course course = taskManager.getCourse();
+      if (course == null) {
+        return;
+      }
+      String courseName = UIUtil.toHtml("<h1>" + course.getName() + "</h1>", 10);
+      String description = UIUtil.toHtml(course.getDescription(), 5);
+      String author = taskManager.getCourse().getAuthor();
+      String authorLabel = UIUtil.toHtml("<b>Author: </b>" + author, 5);
+      contentPanel.add(new JLabel(courseName));
+      contentPanel.add(new JLabel(authorLabel));
+      contentPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+      contentPanel.add(new JLabel(description));
+
+      int taskNum = 0;
+      int taskSolved = 0;
+      int lessonsCompleted = 0;
+      List<Lesson> lessons = course.getLessons();
+      for (Lesson lesson : lessons) {
+        if (lesson.getStatus() == StudyStatus.Solved) {
+          lessonsCompleted++;
+        }
+        LessonInfo lessonInfo = lesson.getLessonInfo();
+        taskNum += lessonInfo.getTaskNum();
+        taskSolved += lessonInfo.getTaskSolved();
+      }
+      String completedLessons = String.format("%d of %d lessons completed", lessonsCompleted, course.getLessons().size());
+      String completedTasks = String.format("%d of %d tasks completed", taskSolved, taskNum);
+      String tasksLeft = String.format("%d of %d tasks left", taskNum - taskSolved, taskNum);
+      contentPanel.add(Box.createVerticalStrut(10));
+      addStatistics(completedLessons);
+      addStatistics(completedTasks);
+
+      double percent = (taskSolved * 100.0) / taskNum;
+      contentPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+      StudyProgressBar studyProgressBar = new StudyProgressBar(percent / 100, JBColor.GREEN, 40, 10);
+      contentPanel.add(studyProgressBar);
+      addStatistics(tasksLeft);
+      ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();
+      Content content = contentFactory.createContent(contentPanel, "", true);
+      toolWindow.getContentManager().addContent(content);
+    }
+  }
+
+  private void addStatistics(String statistics) {
+    String labelText = UIUtil.toHtml(statistics, 5);
+    contentPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+    JLabel statisticLabel = new JLabel(labelText);
+    contentPanel.add(statisticLabel);
+  }
+}
diff --git a/python/edu/learn-python/testData/course.json b/python/edu/learn-python/testData/course.json
new file mode 100644
index 0000000..bff570a
--- /dev/null
+++ b/python/edu/learn-python/testData/course.json
@@ -0,0 +1,130 @@
+{
+  "name": "Python для начинающих",
+  "description": "Начальный курс по языку Python",
+  "lessons": [
+    {
+      "name": "Первые программа",
+      "task_list": [
+        {
+          "name": "Задание 1",
+          "text": "hello-text.html",
+          "test_file": "hello-tests.py",
+          "test_num": 1,
+          "task_files": {
+            "helloworld.py": {
+              "task_windows": [
+                {
+                  "line": 0,
+                  "start": 0,
+                  "text": "type operator",
+                  "hint": "hello-text.html",
+                  "possible_answer": "print"
+                },
+                {
+                  "line": 0,
+                  "start": 33,
+                  "text": "type your name",
+                  "hint": "empty_study.docs",
+                  "possible_answer": "Liana"
+                }
+              ]
+            }
+          }
+        },
+        {
+          "name": "Задание 2",
+          "text": "matchends-text.html",
+          "test_file": "matchends-test.py",
+          "test_num": 3,
+          "task_files": {
+            "match_ends.py": {
+              "task_windows": [
+                {
+                  "line": 1,
+                  "start": 43,
+                  "text": "condition",
+                  "hint": "empty_study.docs",
+                  "possible_answer": ">="
+                },
+                {
+                  "line": 1,
+                  "start": 61,
+                  "text": "index",
+                  "hint": "empty_study.docs",
+                  "possible_answer": "0"
+                },
+                {
+                  "line": 1,
+                  "start": 73,
+                  "text": "index",
+                  "hint": "empty_study.docs",
+                  "possible_answer": "-1"
+                },
+                {
+                  "line": 2,
+                  "start": 11,
+                  "text": "function",
+                  "hint": "list.docs",
+                  "possible_answer": "len"
+                }
+              ]
+            }
+          }
+        }
+      ]
+    },
+    {
+      "name": "Простые задачи",
+      "task_list": [
+        {
+          "name": "Задание 1",
+          "text": "sum-text.html",
+          "test_file": "sum_tests.py",
+          "test_num": 3,
+          "user_tests": [
+            {
+              "input": "sum-input.txt",
+              "output": "sum-output"
+            }
+          ],
+          "task_files": {
+            "sum.py": {
+              "task_windows": [
+                {
+                  "line": 4,
+                  "start": 15,
+                  "text": "получите из консоли имя файла",
+                  "hint": "argv.docs",
+                  "possible_answer": "sys.argv[1]"
+                },
+                {
+                  "line": 5,
+                  "start": 8,
+                  "text": "откройте файл на запись",
+                  "hint": "empty_study.docs",
+                  "possible_answer": "open(filename, 'r')"
+                },
+                {
+                  "line": 10,
+                  "start": 4,
+                  "text": "закройте файл",
+                  "hint": "empty_study.docs",
+                  "possible_answer": "f.close()"
+                },
+                {
+                  "line": 11,
+                  "start": 14,
+                  "text": "правильно проинициализируйте значение",
+                  "hint": "empty_study.docs",
+                  "possible_answer": "-sys.maxint"
+                }
+              ]
+            }
+          }
+        }
+
+      ]
+
+    }
+  ]
+}
\ No newline at end of file
diff --git a/python/edu/learn-python/tests/JsonParserTest.java b/python/edu/learn-python/tests/JsonParserTest.java
new file mode 100644
index 0000000..903f0a5
--- /dev/null
+++ b/python/edu/learn-python/tests/JsonParserTest.java
@@ -0,0 +1,37 @@
+import com.google.gson.FieldNamingPolicy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import org.junit.Before;
+import org.junit.Test;
+import com.jetbrains.python.edu.StudyUtils;
+import com.jetbrains.python.edu.course.Course;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * author: liana
+ * data: 7/4/14.
+ */
+public class JsonParserTest {
+  private Course myCourse = null;
+  @Before
+  public void setUp() throws FileNotFoundException {
+    Reader reader = new InputStreamReader(new FileInputStream("EDIDE/testData/course.json"));
+    Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
+    myCourse = gson.fromJson(reader, Course.class);
+  }
+
+  @Test
+  public void testCourseLevel() {
+    assertEquals(myCourse.getName(), "Python для начинающих");
+    assertEquals(StudyUtils.getFirst(myCourse.getLessons().get(1).getTaskList().get(0).getUserTests()).getInput(), "sum-input.txt");
+    assertEquals(myCourse.getLessons().size(), 2);
+    assertEquals(myCourse.getLessons().get(0).getTaskList().size(), 2);
+    assertEquals(myCourse.getLessons().get(1).getTaskList().size(), 1);
+  }
+}
diff --git a/python/edu/main_pycharm_edu.iml b/python/edu/main_pycharm_edu.iml
index 2acdd87..12efe9b 100644
--- a/python/edu/main_pycharm_edu.iml
+++ b/python/edu/main_pycharm_edu.iml
@@ -11,11 +11,11 @@
     <orderEntry type="module" module-name="relaxng" />
     <orderEntry type="module" module-name="rest" />
     <orderEntry type="module" module-name="python-helpers" />
-    <orderEntry type="module" module-name="terminal" />
     <orderEntry type="module" module-name="python-ide-community" />
     <orderEntry type="module" module-name="platform-main" />
     <orderEntry type="module" module-name="ShortcutPromoter" />
     <orderEntry type="module" module-name="python-educational" />
+    <orderEntry type="module" module-name="learn-python" />
   </component>
 </module>
 
diff --git a/python/edu/resources/idea/PyCharmEduApplicationInfo.xml b/python/edu/resources/idea/PyCharmEduApplicationInfo.xml
index 46695ef..0fdf0d4 100644
--- a/python/edu/resources/idea/PyCharmEduApplicationInfo.xml
+++ b/python/edu/resources/idea/PyCharmEduApplicationInfo.xml
@@ -1,9 +1,9 @@
 <component>
   <company name="JetBrains s.r.o." url="http://www.jetbrains.com/?fromIDE"/>
-  <version major="3" minor="0" eap="true"/>
+  <version major="1" minor="0" eap="true"/>
   <build number="__BUILD_NUMBER__" date="__BUILD_DATE__"/>
-  <logo url="/pycharm_core_logo.png" textcolor="ffffff" progressColor="ffaa16" progressY="230" progressTailIcon="/community_progress_tail.png"/>
-  <about url="/pycharm_core_about.png" logoX="300" logoY="265" logoW="75" logoH="30" foreground="ffffff" linkColor="fca11a"/>
+  <logo url="/pycharm_edu_logo.png" textcolor="5a8179" progressColor="ffaa16" progressY="230" progressTailIcon="/community_progress_tail.png"/>
+  <about url="/pycharm_edu_about.png" logoX="300" logoY="265" logoW="75" logoH="30" foreground="5a8179" linkColor="fca11a"/>
   <icon size32="/PyCharmCore32.png" size16="/PyCharmCore16.png" size32opaque="/PyCharmCore32.png" size12="/PyCharmCore13.png" ico="PyCharmCore.ico"/>
   <package code="__PACKAGE_CODE__"/>
   <names product="PyCharm" fullname="PyCharm Educational Edition" script="charm"/>
diff --git a/python/edu/resources/pycharm_edu_about.png b/python/edu/resources/pycharm_edu_about.png
new file mode 100644
index 0000000..f3312ac
--- /dev/null
+++ b/python/edu/resources/pycharm_edu_about.png
Binary files differ
diff --git a/python/edu/resources/pycharm_edu_about@2x.png b/python/edu/resources/pycharm_edu_about@2x.png
new file mode 100644
index 0000000..2803e04
--- /dev/null
+++ b/python/edu/resources/pycharm_edu_about@2x.png
Binary files differ
diff --git a/python/edu/resources/pycharm_edu_logo.png b/python/edu/resources/pycharm_edu_logo.png
new file mode 100644
index 0000000..c8b69b3
--- /dev/null
+++ b/python/edu/resources/pycharm_edu_logo.png
Binary files differ
diff --git a/python/edu/resources/pycharm_edu_logo@2x.png b/python/edu/resources/pycharm_edu_logo@2x.png
new file mode 100644
index 0000000..6c186ad
--- /dev/null
+++ b/python/edu/resources/pycharm_edu_logo@2x.png
Binary files differ
diff --git a/python/edu/src/META-INF/PyCharmEduPlugin.xml b/python/edu/src/META-INF/PyCharmEduPlugin.xml
index 9adf03d..d5b2fdf 100644
--- a/python/edu/src/META-INF/PyCharmEduPlugin.xml
+++ b/python/edu/src/META-INF/PyCharmEduPlugin.xml
@@ -8,10 +8,16 @@
     </component>
   </application-components>
 
-  <xi:include href="/META-INF/pycharm-core.xml" xpointer="xpointer(/idea-plugin/*)"/>
+  <xi:include href="/META-INF/pycharm-community.xml" xpointer="xpointer(/idea-plugin/*)"/>
 
   <xi:include href="/META-INF/python-core.xml" xpointer="xpointer(/idea-plugin/*)"/>
 
+  <application-components>
+    <component>
+      <implementation-class>com.jetbrains.python.edu.PyCharmEduInitialConfigurator</implementation-class>
+      <headless-implementation-class/>
+    </component>
+  </application-components>
 
   <actions>
     <group overrides="true" class="com.intellij.openapi.actionSystem.EmptyActionGroup" id="ToolsMenu"/>
diff --git a/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java b/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java
index 451c519..ecf3d79 100644
--- a/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java
+++ b/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java
@@ -25,10 +25,8 @@
 import com.intellij.ide.SelectInTarget;
 import com.intellij.ide.ui.UISettings;
 import com.intellij.ide.util.PropertiesComponent;
-import com.intellij.ide.util.TipDialog;
 import com.intellij.notification.EventLog;
 import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.application.ModalityState;
 import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
 import com.intellij.openapi.extensions.Extensions;
 import com.intellij.openapi.extensions.ExtensionsArea;
@@ -41,8 +39,6 @@
 import com.intellij.openapi.project.ProjectManager;
 import com.intellij.openapi.project.ProjectManagerAdapter;
 import com.intellij.openapi.project.ex.ProjectManagerEx;
-import com.intellij.openapi.util.Disposer;
-import com.intellij.openapi.util.Ref;
 import com.intellij.openapi.vfs.VfsUtil;
 import com.intellij.openapi.wm.ToolWindowEP;
 import com.intellij.openapi.wm.ToolWindowId;
@@ -51,7 +47,6 @@
 import com.intellij.platform.PlatformProjectViewOpener;
 import com.intellij.psi.codeStyle.CodeStyleSettings;
 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
-import com.intellij.util.Alarm;
 import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.messages.MessageBus;
 import com.jetbrains.python.PythonLanguage;
@@ -91,13 +86,15 @@
                                        RecentProjectsManagerBase recentProjectsManager) {
     if (!propertiesComponent.getBoolean(CONFIGURED, false)) {
       propertiesComponent.setValue(CONFIGURED, "true");
-      recentProjectsManager.loadState(new RecentProjectsManagerBase.State());
       propertiesComponent.setValue("toolwindow.stripes.buttons.info.shown", "true");
       UISettings.getInstance().HIDE_TOOL_STRIPES = false;
       uiSettings.SHOW_MEMORY_INDICATOR = false;
       uiSettings.SHOW_DIRECTORY_FOR_NON_UNIQUE_FILENAMES = true;
+      uiSettings.SHOW_MAIN_TOOLBAR = false;
       codeInsightSettings.REFORMAT_ON_PASTE = CodeInsightSettings.NO_REFORMAT;
 
+      GeneralSettings.getInstance().setShowTipsOnStartup(false);
+
       EditorSettingsExternalizable.getInstance().setVirtualSpace(false);
       final CodeStyleSettings settings = CodeStyleSettingsManager.getInstance().getCurrentSettings();
       settings.ALIGN_MULTILINE_PARAMETERS_IN_CALLS = true;
@@ -116,23 +113,31 @@
           });
         }
       });
-      PyCodeInsightSettings.getInstance().SHOW_IMPORT_POPUP = false;
+      PyCodeInsightSettings.getInstance().SHOW_IMPORT_POPUP = true;
     }
-    bus.connect().subscribe(AppLifecycleListener.TOPIC, new AppLifecycleListener.Adapter() {
-      @Override
-      public void appFrameCreated(String[] commandLineArgs, @NotNull Ref<Boolean> willOpenProject) {
-        if (!propertiesComponent.isValueSet(DISPLAYED_PROPERTY)) {
-          GeneralSettings.getInstance().setShowTipsOnStartup(false);
-          showInitialConfigurationDialog();
-          propertiesComponent.setValue(DISPLAYED_PROPERTY, "true");
-        }
-      }
 
-      @Override
-      public void appStarting(Project projectFromCommandLine) {
-        patchKeymap();
-      }
-    });
+    if (!propertiesComponent.isValueSet(DISPLAYED_PROPERTY)) {
+
+      bus.connect().subscribe(AppLifecycleListener.TOPIC, new AppLifecycleListener.Adapter() {
+        @Override
+        public void welcomeScreenDisplayed() {
+
+          ApplicationManager.getApplication().invokeLater(new Runnable() {
+            @Override
+            public void run() {
+              if (!propertiesComponent.isValueSet(DISPLAYED_PROPERTY)) {
+                GeneralSettings.getInstance().setShowTipsOnStartup(false);
+                propertiesComponent.setValue(DISPLAYED_PROPERTY, "true");
+                showInitialConfigurationDialog();
+
+                patchKeymap();
+              }
+            }
+          });
+        }
+      });
+    }
+
     bus.connect().subscribe(ProjectManager.TOPIC, new ProjectManagerAdapter() {
       @Override
       public void projectOpened(final Project project) {
@@ -142,58 +147,16 @@
         }
 
         patchProjectAreaExtensions(project);
-
-        //StartupManager.getInstance(project).runWhenProjectIsInitialized(new DumbAwareRunnable() {
-        //  @Override
-        //  public void run() {
-        //    if (project.isDisposed()) return;
-        //
-        //    ToolWindowManager.getInstance(project).invokeLater(new Runnable() {
-        //      int count = 0;
-        //      public void run() {
-        //        if (project.isDisposed()) return;
-        //        if (count ++ < 3) {
-        //          ToolWindowManager.getInstance(project).invokeLater(this);
-        //          return;
-        //        }
-        //        if (!propertiesComponent.isValueSet(INIT_DB_DIALOG_DISPLAYED)) {
-        //          ToolWindow toolWindow = DatabaseView.getDatabaseToolWindow(project);
-        //          if (toolWindow.getType() != ToolWindowType.SLIDING) {
-        //            toolWindow.activate(null);
-        //          }
-        //          propertiesComponent.setValue(INIT_DB_DIALOG_DISPLAYED, "true");
-        //          onFirstProjectOpened(project);
-        //        }
-        //      }
-        //    });
-        //  }
-        //});
       }
     });
   }
 
-  private static void onFirstProjectOpened(@NotNull final Project project) {
-    // show python console
-
-
-    GeneralSettings.getInstance().setShowTipsOnStartup(true);
-
-    // show tips once
-    final Alarm alarm = new Alarm(project);
-    alarm.addRequest(new Runnable() {
-      @Override
-      public void run() {
-        Disposer.dispose(alarm);
-        TipDialog.createForProject(project).show();
-      }
-    }, 2000, ModalityState.NON_MODAL);
-  }
-
   private static void patchRootAreaExtensions() {
     ExtensionsArea rootArea = Extensions.getArea(null);
 
     for (ToolWindowEP ep : Extensions.getExtensions(ToolWindowEP.EP_NAME)) {
-      if (ToolWindowId.FAVORITES_VIEW.equals(ep.id) || ToolWindowId.TODO_VIEW.equals(ep.id) || EventLog.LOG_TOOL_WINDOW_ID.equals(ep.id)) {
+      if (ToolWindowId.FAVORITES_VIEW.equals(ep.id) || ToolWindowId.TODO_VIEW.equals(ep.id) || EventLog.LOG_TOOL_WINDOW_ID.equals(ep.id)
+          || "Structure".equals(ep.id)) {
         rootArea.getExtensionPoint(ToolWindowEP.EP_NAME).unregisterExtension(ep);
       }
     }
diff --git a/python/gen/icons/PythonIcons.java b/python/gen/icons/PythonIcons.java
index b4c59bd..f98387f 100644
--- a/python/gen/icons/PythonIcons.java
+++ b/python/gen/icons/PythonIcons.java
@@ -49,6 +49,8 @@
     public static final Icon Python = load("/icons/com/jetbrains/python/python.png"); // 16x16
     public static final Icon Python_24 = load("/icons/com/jetbrains/python/python_24.png"); // 24x24
     public static final Icon PythonClosed = load("/icons/com/jetbrains/python/pythonClosed.png"); // 16x16
+    public static final Icon PythonConsole = load("/icons/com/jetbrains/python/pythonConsole.png"); // 16x16
+    public static final Icon PythonConsoleToolWindow = load("/icons/com/jetbrains/python/pythonConsoleToolWindow.png"); // 13x13
     public static final Icon PythonTests = load("/icons/com/jetbrains/python/pythonTests.png"); // 16x16
     public static final Icon TemplateRoot = load("/icons/com/jetbrains/python/templateRoot.png"); // 16x16
     public static final Icon Virtualenv = load("/icons/com/jetbrains/python/virtualenv.png"); // 16x16
diff --git a/python/helpers/pycharm/_bdd_utils.py b/python/helpers/pycharm/_bdd_utils.py
index 0c92532..300feb2 100644
--- a/python/helpers/pycharm/_bdd_utils.py
+++ b/python/helpers/pycharm/_bdd_utils.py
@@ -27,7 +27,7 @@
     assert os.path.exists(what_to_run), "{} does not exist".format(what_to_run)
 
     if os.path.isfile(what_to_run):
-        base_dir = os.path.dirname(what_to_run) # User may point to the file directly
+        base_dir = os.path.dirname(what_to_run)  # User may point to the file directly
     return base_dir, what_to_run
 
 
@@ -62,8 +62,11 @@
         """"
         Runs runner. To be called right after constructor.
         """
-        self.tc_messages.testCount(self._get_number_of_tests())
+        number_of_tests = self._get_number_of_tests()
+        self.tc_messages.testCount(number_of_tests)
         self.tc_messages.testMatrixEntered()
+        if number_of_tests == 0:  # Nothing to run, so no need to report even feature/scenario start. (See PY-13623)
+            return
         self._run_tests()
 
     def __gen_location(self, location):
@@ -175,9 +178,9 @@
         num_of_steps = 0
         for feature in self._get_features_to_run():
             if feature.background:
-                num_of_steps += len(feature.background.steps) * len(feature.scenarios)
+                num_of_steps += len(list(feature.background.steps)) * len(list(feature.scenarios))
             for scenario in feature.scenarios:
-                num_of_steps += len(scenario.steps)
+                num_of_steps += len(list(scenario.steps))
         return num_of_steps
 
     @abc.abstractmethod
diff --git a/python/helpers/pycharm/behave_runner.py b/python/helpers/pycharm/behave_runner.py
index 4a1b2f6..0ad8313 100644
--- a/python/helpers/pycharm/behave_runner.py
+++ b/python/helpers/pycharm/behave_runner.py
@@ -136,20 +136,23 @@
         element.location.file = element.location.filename  # To preserve _bdd_utils contract
         if isinstance(element, Step):
             # Process step
+            step_name = "{} {}".format(element.keyword, element.name)
             if is_started:
-                self._test_started(element.name, element.location)
+                self._test_started(step_name, element.location)
             elif element.status == 'passed':
-                self._test_passed(element.name, element.duration)
+                self._test_passed(step_name, element.duration)
             elif element.status == 'failed':
                 try:
                     trace = traceback.format_exc()
                 except Exception:
                     trace = "".join(traceback.format_tb(element.exc_traceback))
-                self._test_failed(element.name, element.error_message, trace)
+                if trace in str(element.error_message):
+                    trace = None  # No reason to duplicate output (see PY-13647)
+                self._test_failed(step_name, element.error_message, trace)
             elif element.status == 'undefined':
-                self._test_undefined(element.name, element.location)
+                self._test_undefined(step_name, element.location)
             else:
-                self._test_skipped(element.name, element.status, element.location)
+                self._test_skipped(step_name, element.status, element.location)
         elif not is_started and isinstance(element, Scenario) and element.status == 'failed':
             # To process scenarios with undefined/skipped tests
             for step in element.steps:
diff --git a/python/helpers/pycharm/docrunner.py b/python/helpers/pycharm/docrunner.py
index ad619be..ed9a6f1 100644
--- a/python/helpers/pycharm/docrunner.py
+++ b/python/helpers/pycharm/docrunner.py
@@ -69,6 +69,12 @@
     self.messages.testError(self.getTestName(test),
       message='Error', details=err)
 
+  def stopTest(self, test):
+    start = getattr(test, "startTime", datetime.datetime.now())
+    d = datetime.datetime.now() - start
+    duration=d.microseconds / 1000 + d.seconds * 1000 + d.days * 86400000
+    self.messages.testFinished(self.getTestName(test), duration=int(duration))
+
 class DocTestRunner(doctest.DocTestRunner):
   """
   Special runner for doctests,
diff --git a/python/helpers/pycharm_generator_utils/module_redeclarator.py b/python/helpers/pycharm_generator_utils/module_redeclarator.py
index 0dbdbb5..bdffe3e 100644
--- a/python/helpers/pycharm_generator_utils/module_redeclarator.py
+++ b/python/helpers/pycharm_generator_utils/module_redeclarator.py
@@ -1009,6 +1009,8 @@
                 self.classes_buf.out(0, txt)
                 txt = create_function()
                 self.classes_buf.out(0, txt)
+                txt = create_method()
+                self.classes_buf.out(0, txt)
 
                 # Fake <type 'namedtuple'>
                 if version[0] >= 3 or (version[0] == 2 and version[1] >= 6):
diff --git a/python/helpers/pycharm_generator_utils/util_methods.py b/python/helpers/pycharm_generator_utils/util_methods.py
index b6805c4..273b0a2 100644
--- a/python/helpers/pycharm_generator_utils/util_methods.py
+++ b/python/helpers/pycharm_generator_utils/util_methods.py
@@ -115,6 +115,25 @@
 """
     return txt
 
+def create_method():
+    txt = """
+class __method(object):
+    '''A mock class representing method type.'''
+
+    def __init__(self):
+"""
+    if version[0] == 2:
+        txt += """
+        self.im_class = None
+        self.im_self = None
+        self.im_func = None
+"""
+    if version[0] >= 3 or (version[0] == 2 and version[1] >= 6):
+        txt += """
+        self.__func__ = None
+        self.__self__ = None
+"""
+    return txt
 
 def _searchbases(cls, accum):
     # logic copied from inspect.py
diff --git a/python/helpers/pydev/LICENSE b/python/helpers/pydev/LICENSE
new file mode 100644
index 0000000..5032843
--- /dev/null
+++ b/python/helpers/pydev/LICENSE
@@ -0,0 +1,203 @@
+Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
+LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
+CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and documentation
+   distributed under this Agreement, and
+b) in the case of each subsequent Contributor:
+    i) changes to the Program, and
+   ii) additions to the Program;
+
+   where such changes and/or additions to the Program originate from and are
+   distributed by that particular Contributor. A Contribution 'originates'
+   from a Contributor if it was added to the Program by such Contributor
+   itself or anyone acting on such Contributor's behalf. Contributions do not
+   include additions to the Program which: (i) are separate modules of
+   software distributed in conjunction with the Program under their own
+   license agreement, and (ii) are not derivative works of the Program.
+
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents" mean patent claims licensable by a Contributor which are
+necessarily infringed by the use or sale of its Contribution alone or when
+combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this
+Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement,
+including all Contributors.
+
+2. GRANT OF RIGHTS
+  a) Subject to the terms of this Agreement, each Contributor hereby grants
+     Recipient a non-exclusive, worldwide, royalty-free copyright license to
+     reproduce, prepare derivative works of, publicly display, publicly
+     perform, distribute and sublicense the Contribution of such Contributor,
+     if any, and such derivative works, in source code and object code form.
+  b) Subject to the terms of this Agreement, each Contributor hereby grants
+     Recipient a non-exclusive, worldwide, royalty-free patent license under
+     Licensed Patents to make, use, sell, offer to sell, import and otherwise
+     transfer the Contribution of such Contributor, if any, in source code and
+     object code form. This patent license shall apply to the combination of
+     the Contribution and the Program if, at the time the Contribution is
+     added by the Contributor, such addition of the Contribution causes such
+     combination to be covered by the Licensed Patents. The patent license
+     shall not apply to any other combinations which include the Contribution.
+     No hardware per se is licensed hereunder.
+  c) Recipient understands that although each Contributor grants the licenses
+     to its Contributions set forth herein, no assurances are provided by any
+     Contributor that the Program does not infringe the patent or other
+     intellectual property rights of any other entity. Each Contributor
+     disclaims any liability to Recipient for claims brought by any other
+     entity based on infringement of intellectual property rights or
+     otherwise. As a condition to exercising the rights and licenses granted
+     hereunder, each Recipient hereby assumes sole responsibility to secure
+     any other intellectual property rights needed, if any. For example, if a
+     third party patent license is required to allow Recipient to distribute
+     the Program, it is Recipient's responsibility to acquire that license
+     before distributing the Program.
+  d) Each Contributor represents that to its knowledge it has sufficient
+     copyright rights in its Contribution, if any, to grant the copyright
+     license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form under
+its own license agreement, provided that:
+
+  a) it complies with the terms and conditions of this Agreement; and
+  b) its license agreement:
+      i) effectively disclaims on behalf of all Contributors all warranties
+         and conditions, express and implied, including warranties or
+         conditions of title and non-infringement, and implied warranties or
+         conditions of merchantability and fitness for a particular purpose;
+     ii) effectively excludes on behalf of all Contributors all liability for
+         damages, including direct, indirect, special, incidental and
+         consequential damages, such as lost profits;
+    iii) states that any provisions which differ from this Agreement are
+         offered by that Contributor alone and not by any other party; and
+     iv) states that source code for the Program is available from such
+         Contributor, and informs licensees how to obtain it in a reasonable
+         manner on or through a medium customarily used for software exchange.
+
+When the Program is made available in source code form:
+
+  a) it must be made available under this Agreement; and
+  b) a copy of this Agreement must be included with each copy of the Program.
+     Contributors may not remove or alter any copyright notices contained
+     within the Program.
+
+Each Contributor must identify itself as the originator of its Contribution,
+if
+any, in a manner that reasonably allows subsequent Recipients to identify the
+originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities with
+respect to end users, business partners and the like. While this license is
+intended to facilitate the commercial use of the Program, the Contributor who
+includes the Program in a commercial product offering should do so in a manner
+which does not create potential liability for other Contributors. Therefore,
+if a Contributor includes the Program in a commercial product offering, such
+Contributor ("Commercial Contributor") hereby agrees to defend and indemnify
+every other Contributor ("Indemnified Contributor") against any losses,
+damages and costs (collectively "Losses") arising from claims, lawsuits and
+other legal actions brought by a third party against the Indemnified
+Contributor to the extent caused by the acts or omissions of such Commercial
+Contributor in connection with its distribution of the Program in a commercial
+product offering. The obligations in this section do not apply to any claims
+or Losses relating to any actual or alleged intellectual property
+infringement. In order to qualify, an Indemnified Contributor must:
+a) promptly notify the Commercial Contributor in writing of such claim, and
+b) allow the Commercial Contributor to control, and cooperate with the
+Commercial Contributor in, the defense and any related settlement
+negotiations. The Indemnified Contributor may participate in any such claim at
+its own expense.
+
+For example, a Contributor might include the Program in a commercial product
+offering, Product X. That Contributor is then a Commercial Contributor. If
+that Commercial Contributor then makes performance claims, or offers
+warranties related to Product X, those performance claims and warranties are
+such Commercial Contributor's responsibility alone. Under this section, the
+Commercial Contributor would have to defend claims against the other
+Contributors related to those performance claims and warranties, and if a
+court requires any other Contributor to pay any damages as a result, the
+Commercial Contributor must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
+IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each
+Recipient is solely responsible for determining the appropriateness of using
+and distributing the Program and assumes all risks associated with its
+exercise of rights under this Agreement , including but not limited to the
+risks and costs of program errors, compliance with applicable laws, damage to
+or loss of data, programs or equipment, and unavailability or interruption of
+operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
+CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION
+LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
+EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY
+OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of the
+remainder of the terms of this Agreement, and without further action by the
+parties hereto, such provision shall be reformed to the minimum extent
+necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Program itself
+(excluding combinations of the Program with other software or hardware)
+infringes such Recipient's patent(s), then such Recipient's rights granted
+under Section 2(b) shall terminate as of the date such litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails to
+comply with any of the material terms or conditions of this Agreement and does
+not cure such failure in a reasonable period of time after becoming aware of
+such noncompliance. If all Recipient's rights under this Agreement terminate,
+Recipient agrees to cease use and distribution of the Program as soon as
+reasonably practicable. However, Recipient's obligations under this Agreement
+and any licenses granted by Recipient relating to the Program shall continue
+and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement, but in
+order to avoid inconsistency the Agreement is copyrighted and may only be
+modified in the following manner. The Agreement Steward reserves the right to
+publish new versions (including revisions) of this Agreement from time to
+time. No one other than the Agreement Steward has the right to modify this
+Agreement. The Eclipse Foundation is the initial Agreement Steward. The
+Eclipse Foundation may assign the responsibility to serve as the Agreement
+Steward to a suitable separate entity. Each new version of the Agreement will
+be given a distinguishing version number. The Program (including
+Contributions) may always be distributed subject to the version of the
+Agreement under which it was received. In addition, after a new version of the
+Agreement is published, Contributor may elect to distribute the Program
+(including its Contributions) under the new version. Except as expressly
+stated in Sections 2(a) and 2(b) above, Recipient receives no rights or
+licenses to the intellectual property of any Contributor under this Agreement,
+whether expressly, by implication, estoppel or otherwise. All rights in the
+Program not expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the
+intellectual property laws of the United States of America. No party to this
+Agreement will bring a legal action under this Agreement more than one year
+after the cause of action arose. Each party waives its rights to a jury trial in
+any resulting litigation.
\ No newline at end of file
diff --git a/python/helpers/pydev/README.md b/python/helpers/pydev/README.md
new file mode 100644
index 0000000..7b22116
--- /dev/null
+++ b/python/helpers/pydev/README.md
@@ -0,0 +1,2 @@
+PyDev.Debugger
+==============
diff --git a/python/helpers/pydev/_pydev_execfile.py b/python/helpers/pydev/_pydev_execfile.py
deleted file mode 100644
index d60d7ed..0000000
--- a/python/helpers/pydev/_pydev_execfile.py
+++ /dev/null
@@ -1,38 +0,0 @@
-#We must redefine it in Py3k if it's not already there
-def execfile(file, glob=None, loc=None):
-    if glob is None:
-        import sys
-        glob = sys._getframe().f_back.f_globals
-    if loc is None:
-        loc = glob
-    stream = open(file, 'rb')
-    try:
-        encoding = None
-        #Get encoding!
-        for _i in range(2):
-            line = stream.readline() #Should not raise an exception even if there are no more contents
-            #Must be a comment line
-            if line.strip().startswith(b'#'):
-                #Don't import re if there's no chance that there's an encoding in the line
-                if b'coding' in line:
-                    import re
-                    p = re.search(br"coding[:=]\s*([-\w.]+)", line)
-                    if p:
-                        try:
-                            encoding = p.group(1).decode('ascii')
-                            break
-                        except:
-                            encoding = None
-    finally:
-        stream.close()
-
-    if encoding:
-        stream = open(file, encoding=encoding)
-    else:
-        stream = open(file)
-    try:
-        contents = stream.read()
-    finally:
-        stream.close()
-        
-    exec(compile(contents+"\n", file, 'exec'), glob, loc) #execute the script
\ No newline at end of file
diff --git a/python/helpers/pydev/_pydev_getopt.py b/python/helpers/pydev/_pydev_getopt.py
new file mode 100644
index 0000000..5548651
--- /dev/null
+++ b/python/helpers/pydev/_pydev_getopt.py
@@ -0,0 +1,130 @@
+
+#=======================================================================================================================
+# getopt code copied since gnu_getopt is not available on jython 2.1
+#=======================================================================================================================
+class GetoptError(Exception):
+    opt = ''
+    msg = ''
+    def __init__(self, msg, opt=''):
+        self.msg = msg
+        self.opt = opt
+        Exception.__init__(self, msg, opt)
+
+    def __str__(self):
+        return self.msg
+
+
+def gnu_getopt(args, shortopts, longopts=[]):
+    """getopt(args, options[, long_options]) -> opts, args
+
+    This function works like getopt(), except that GNU style scanning
+    mode is used by default. This means that option and non-option
+    arguments may be intermixed. The getopt() function stops
+    processing options as soon as a non-option argument is
+    encountered.
+
+    If the first character of the option string is `+', or if the
+    environment variable POSIXLY_CORRECT is set, then option
+    processing stops as soon as a non-option argument is encountered.
+    """
+
+    opts = []
+    prog_args = []
+    if type('') == type(longopts):
+        longopts = [longopts]
+    else:
+        longopts = list(longopts)
+
+    # Allow options after non-option arguments?
+    all_options_first = False
+    if shortopts.startswith('+'):
+        shortopts = shortopts[1:]
+        all_options_first = True
+
+    while args:
+        if args[0] == '--':
+            prog_args += args[1:]
+            break
+
+        if args[0][:2] == '--':
+            opts, args = do_longs(opts, args[0][2:], longopts, args[1:])
+        elif args[0][:1] == '-':
+            opts, args = do_shorts(opts, args[0][1:], shortopts, args[1:])
+        else:
+            if all_options_first:
+                prog_args += args
+                break
+            else:
+                prog_args.append(args[0])
+                args = args[1:]
+
+    return opts, prog_args
+
+def do_longs(opts, opt, longopts, args):
+    try:
+        i = opt.index('=')
+    except ValueError:
+        optarg = None
+    else:
+        opt, optarg = opt[:i], opt[i + 1:]
+
+    has_arg, opt = long_has_args(opt, longopts)
+    if has_arg:
+        if optarg is None:
+            if not args:
+                raise GetoptError('option --%s requires argument' % opt, opt)
+            optarg, args = args[0], args[1:]
+    elif optarg:
+        raise GetoptError('option --%s must not have an argument' % opt, opt)
+    opts.append(('--' + opt, optarg or ''))
+    return opts, args
+
+# Return:
+#   has_arg?
+#   full option name
+def long_has_args(opt, longopts):
+    possibilities = [o for o in longopts if o.startswith(opt)]
+    if not possibilities:
+        raise GetoptError('option --%s not recognized' % opt, opt)
+    # Is there an exact match?
+    if opt in possibilities:
+        return False, opt
+    elif opt + '=' in possibilities:
+        return True, opt
+    # No exact match, so better be unique.
+    if len(possibilities) > 1:
+        # XXX since possibilities contains all valid continuations, might be
+        # nice to work them into the error msg
+        raise GetoptError('option --%s not a unique prefix' % opt, opt)
+    assert len(possibilities) == 1
+    unique_match = possibilities[0]
+    has_arg = unique_match.endswith('=')
+    if has_arg:
+        unique_match = unique_match[:-1]
+    return has_arg, unique_match
+
+def do_shorts(opts, optstring, shortopts, args):
+    while optstring != '':
+        opt, optstring = optstring[0], optstring[1:]
+        if short_has_arg(opt, shortopts):
+            if optstring == '':
+                if not args:
+                    raise GetoptError('option -%s requires argument' % opt,
+                                      opt)
+                optstring, args = args[0], args[1:]
+            optarg, optstring = optstring, ''
+        else:
+            optarg = ''
+        opts.append(('-' + opt, optarg))
+    return opts, args
+
+def short_has_arg(opt, shortopts):
+    for i in range(len(shortopts)):
+        if opt == shortopts[i] != ':':
+            return shortopts.startswith(':', i + 1)
+    raise GetoptError('option -%s not recognized' % opt, opt)
+
+
+#=======================================================================================================================
+# End getopt code
+#=======================================================================================================================
diff --git a/python/helpers/pydev/_pydev_imports_tipper.py b/python/helpers/pydev/_pydev_imports_tipper.py
index e4b3b86..76cf2cd 100644
--- a/python/helpers/pydev/_pydev_imports_tipper.py
+++ b/python/helpers/pydev/_pydev_imports_tipper.py
@@ -4,6 +4,10 @@
 
 from _pydev_tipper_common import DoFind
 
+try:
+    xrange
+except:
+    xrange = range
 
 #completion types.
 TYPE_IMPORT = '0'
@@ -19,20 +23,20 @@
     except:
         if '.' in name:
             sub = name[0:name.rfind('.')]
-            
+
             if log is not None:
                 log.AddContent('Unable to import', name, 'trying with', sub)
                 log.AddException()
-            
+
             return _imp(sub, log)
         else:
             s = 'Unable to import module: %s - sys.path: %s' % (str(name), sys.path)
             if log is not None:
                 log.AddContent(s)
                 log.AddException()
-            
+
             raise ImportError(s)
-        
+
 
 IS_IPY = False
 if sys.platform == 'cli':
@@ -53,9 +57,9 @@
                 clr.AddReference(name)
             except:
                 pass #That's OK (not dot net module).
-        
+
         return _old_imp(initial_name, log)
-        
+
 
 
 def GetFile(mod):
@@ -69,19 +73,19 @@
                 filename = f[:-4] + '.py'
                 if os.path.exists(filename):
                     f = filename
-            
+
     return f
 
 def Find(name, log=None):
     f = None
-    
+
     mod = _imp(name, log)
     parent = mod
     foundAs = ''
-    
+
     if inspect.ismodule(mod):
         f = GetFile(mod)
-        
+
     components = name.split('.')
 
     old_comp = None
@@ -94,22 +98,22 @@
         except AttributeError:
             if old_comp != comp:
                 raise
-        
+
         if inspect.ismodule(mod):
             f = GetFile(mod)
         else:
             if len(foundAs) > 0:
                 foundAs = foundAs + '.'
             foundAs = foundAs + comp
-            
+
         old_comp = comp
-        
+
     return f, mod, parent, foundAs
 
 def Search(data):
     '''@return file, line, col
     '''
-    
+
     data = data.replace('\n', '')
     if data.endswith('.'):
         data = data.rstrip('.')
@@ -118,19 +122,19 @@
         return DoFind(f, mod), foundAs
     except:
         return DoFind(f, parent), foundAs
-    
-    
+
+
 def GenerateTip(data, log=None):
     data = data.replace('\n', '')
     if data.endswith('.'):
         data = data.rstrip('.')
-        
+
     f, mod, parent, foundAs = Find(data, log)
     #print_ >> open('temp.txt', 'w'), f
     tips = GenerateImportsTipForModule(mod)
     return f, tips
-    
-    
+
+
 def CheckChar(c):
     if c == '-' or c == '.':
         return '_'
@@ -146,7 +150,7 @@
             name, doc, args, type (from the TYPE_* constants)
     '''
     ret = []
-    
+
     if dirComps is None:
         dirComps = dir(obj_to_complete)
         if hasattr(obj_to_complete, '__dict__'):
@@ -155,22 +159,22 @@
             dirComps.append('__class__')
 
     getCompleteInfo = True
-    
+
     if len(dirComps) > 1000:
-        #ok, we don't want to let our users wait forever... 
+        #ok, we don't want to let our users wait forever...
         #no complete info for you...
-        
+
         getCompleteInfo = False
-    
+
     dontGetDocsOn = (float, int, str, tuple, list)
     for d in dirComps:
-        
+
         if d is None:
             continue
-            
+
         if not filter(d):
             continue
-        
+
         args = ''
 
         try:
@@ -182,18 +186,18 @@
             if getCompleteInfo:
                 try:
                     retType = TYPE_BUILTIN
-        
+
                     #check if we have to get docs
                     getDoc = True
                     for class_ in dontGetDocsOn:
-                            
+
                         if isinstance(obj, class_):
                             getDoc = False
                             break
-                        
+
                     doc = ''
                     if getDoc:
-                        #no need to get this info... too many constants are defined and 
+                        #no need to get this info... too many constants are defined and
                         #makes things much slower (passing all that through sockets takes quite some time)
                         try:
                             doc = inspect.getdoc(obj)
@@ -201,12 +205,12 @@
                                 doc = ''
                         except: #may happen on jython when checking java classes (so, just ignore it)
                             doc = ''
-                            
-                            
+
+
                     if inspect.ismethod(obj) or inspect.isbuiltin(obj) or inspect.isfunction(obj) or inspect.isroutine(obj):
                         try:
                             args, vargs, kwargs, defaults = inspect.getargspec(obj)
-                                
+
                             r = ''
                             for a in (args):
                                 if len(r) > 0:
@@ -250,7 +254,7 @@
                                             if major:
                                                 args = major[major.index('('):]
                                                 found = True
-                                            
+
 
                                     if not found:
                                         i = doc.find('->')
@@ -260,12 +264,12 @@
                                                 i = doc.find('\n')
                                                 if i < 0:
                                                     i = doc.find('\r')
-                                                
-                                                
+
+
                                         if i > 0:
                                             s = doc[0:i]
                                             s = s.strip()
-                                            
+
                                             #let's see if we have a docstring in the first line
                                             if s[-1] == ')':
                                                 start = s.find('(')
@@ -275,21 +279,21 @@
                                                         end = s.find(')')
                                                         if end <= 0:
                                                             end = len(s)
-                                                    
+
                                                     args = s[start:end]
                                                     if not args[-1] == ')':
                                                         args = args + ')'
-        
-                                                    
+
+
                                                     #now, get rid of unwanted chars
                                                     l = len(args) - 1
                                                     r = []
-                                                    for i in range(len(args)):
+                                                    for i in xrange(len(args)):
                                                         if i == 0 or i == l:
                                                             r.append(args[i])
                                                         else:
                                                             r.append(CheckChar(args[i]))
-                                                            
+
                                                     args = ''.join(r)
 
                                     if IS_IPY:
@@ -305,43 +309,43 @@
 
                             except:
                                 pass
-        
+
                         retType = TYPE_FUNCTION
-                        
+
                     elif inspect.isclass(obj):
                         retType = TYPE_CLASS
-                        
+
                     elif inspect.ismodule(obj):
                         retType = TYPE_IMPORT
-                        
+
                     else:
                         retType = TYPE_ATTR
-                    
-                    
+
+
                     #add token and doc to return - assure only strings.
                     ret.append((d, doc, args, retType))
-                    
+
                 except: #just ignore and get it without aditional info
                     ret.append((d, '', args, TYPE_BUILTIN))
-                
+
             else: #getCompleteInfo == False
                 if inspect.ismethod(obj) or inspect.isbuiltin(obj) or inspect.isfunction(obj) or inspect.isroutine(obj):
                     retType = TYPE_FUNCTION
-                    
+
                 elif inspect.isclass(obj):
                     retType = TYPE_CLASS
-                    
+
                 elif inspect.ismodule(obj):
                     retType = TYPE_IMPORT
-                    
+
                 else:
                     retType = TYPE_ATTR
                 #ok, no complete info, let's try to do this as fast and clean as possible
                 #so, no docs for this kind of information, only the signatures
                 ret.append((d, '', str(args), retType))
-            
+
     return ret
 
 
-    
-    
+
+
diff --git a/python/helpers/pydev/_pydev_imps/__init__.py b/python/helpers/pydev/_pydev_imps/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/python/helpers/pydev/_pydev_imps/__init__.py
diff --git a/python/helpers/pydev/_pydev_imps/_pydev_BaseHTTPServer.py b/python/helpers/pydev/_pydev_imps/_pydev_BaseHTTPServer.py
new file mode 100644
index 0000000..5f9dbfd
--- /dev/null
+++ b/python/helpers/pydev/_pydev_imps/_pydev_BaseHTTPServer.py
@@ -0,0 +1,604 @@
+"""HTTP server base class.
+
+Note: the class in this module doesn't implement any HTTP request; see
+SimpleHTTPServer for simple implementations of GET, HEAD and POST
+(including CGI scripts).  It does, however, optionally implement HTTP/1.1
+persistent connections, as of version 0.3.
+
+Contents:
+
+- BaseHTTPRequestHandler: HTTP request handler base class
+- test: test function
+
+XXX To do:
+
+- log requests even later (to capture byte count)
+- log user-agent header and other interesting goodies
+- send error log to separate file
+"""
+
+
+# See also:
+#
+# HTTP Working Group                                        T. Berners-Lee
+# INTERNET-DRAFT                                            R. T. Fielding
+# <draft-ietf-http-v10-spec-00.txt>                     H. Frystyk Nielsen
+# Expires September 8, 1995                                  March 8, 1995
+#
+# URL: http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-v10-spec-00.txt
+#
+# and
+#
+# Network Working Group                                      R. Fielding
+# Request for Comments: 2616                                       et al
+# Obsoletes: 2068                                              June 1999
+# Category: Standards Track
+#
+# URL: http://www.faqs.org/rfcs/rfc2616.html
+
+# Log files
+# ---------
+#
+# Here's a quote from the NCSA httpd docs about log file format.
+#
+# | The logfile format is as follows. Each line consists of:
+# |
+# | host rfc931 authuser [DD/Mon/YYYY:hh:mm:ss] "request" ddd bbbb
+# |
+# |        host: Either the DNS name or the IP number of the remote client
+# |        rfc931: Any information returned by identd for this person,
+# |                - otherwise.
+# |        authuser: If user sent a userid for authentication, the user name,
+# |                  - otherwise.
+# |        DD: Day
+# |        Mon: Month (calendar name)
+# |        YYYY: Year
+# |        hh: hour (24-hour format, the machine's timezone)
+# |        mm: minutes
+# |        ss: seconds
+# |        request: The first line of the HTTP request as sent by the client.
+# |        ddd: the status code returned by the server, - if not available.
+# |        bbbb: the total number of bytes sent,
+# |              *not including the HTTP/1.0 header*, - if not available
+# |
+# | You can determine the name of the file accessed through request.
+#
+# (Actually, the latter is only true if you know the server configuration
+# at the time the request was made!)
+
+__version__ = "0.3"
+
+__all__ = ["HTTPServer", "BaseHTTPRequestHandler"]
+
+import sys
+from _pydev_imps import _pydev_time as time
+from _pydev_imps import _pydev_socket as socket
+from warnings import filterwarnings, catch_warnings
+with catch_warnings():
+    if sys.py3kwarning:
+        filterwarnings("ignore", ".*mimetools has been removed",
+                        DeprecationWarning)
+    import mimetools
+
+from _pydev_imps import _pydev_SocketServer as SocketServer
+
+# Default error message template
+DEFAULT_ERROR_MESSAGE = """\
+<head>
+<title>Error response</title>
+</head>
+<body>
+<h1>Error response</h1>
+<p>Error code %(code)d.
+<p>Message: %(message)s.
+<p>Error code explanation: %(code)s = %(explain)s.
+</body>
+"""
+
+DEFAULT_ERROR_CONTENT_TYPE = "text/html"
+
+def _quote_html(html):
+    return html.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
+
+class HTTPServer(SocketServer.TCPServer):
+
+    allow_reuse_address = 1    # Seems to make sense in testing environment
+
+    def server_bind(self):
+        """Override server_bind to store the server name."""
+        SocketServer.TCPServer.server_bind(self)
+        host, port = self.socket.getsockname()[:2]
+        self.server_name = socket.getfqdn(host)
+        self.server_port = port
+
+
+class BaseHTTPRequestHandler(SocketServer.StreamRequestHandler):
+
+    """HTTP request handler base class.
+
+    The following explanation of HTTP serves to guide you through the
+    code as well as to expose any misunderstandings I may have about
+    HTTP (so you don't need to read the code to figure out I'm wrong
+    :-).
+
+    HTTP (HyperText Transfer Protocol) is an extensible protocol on
+    top of a reliable stream transport (e.g. TCP/IP).  The protocol
+    recognizes three parts to a request:
+
+    1. One line identifying the request type and path
+    2. An optional set of RFC-822-style headers
+    3. An optional data part
+
+    The headers and data are separated by a blank line.
+
+    The first line of the request has the form
+
+    <command> <path> <version>
+
+    where <command> is a (case-sensitive) keyword such as GET or POST,
+    <path> is a string containing path information for the request,
+    and <version> should be the string "HTTP/1.0" or "HTTP/1.1".
+    <path> is encoded using the URL encoding scheme (using %xx to signify
+    the ASCII character with hex code xx).
+
+    The specification specifies that lines are separated by CRLF but
+    for compatibility with the widest range of clients recommends
+    servers also handle LF.  Similarly, whitespace in the request line
+    is treated sensibly (allowing multiple spaces between components
+    and allowing trailing whitespace).
+
+    Similarly, for output, lines ought to be separated by CRLF pairs
+    but most clients grok LF characters just fine.
+
+    If the first line of the request has the form
+
+    <command> <path>
+
+    (i.e. <version> is left out) then this is assumed to be an HTTP
+    0.9 request; this form has no optional headers and data part and
+    the reply consists of just the data.
+
+    The reply form of the HTTP 1.x protocol again has three parts:
+
+    1. One line giving the response code
+    2. An optional set of RFC-822-style headers
+    3. The data
+
+    Again, the headers and data are separated by a blank line.
+
+    The response code line has the form
+
+    <version> <responsecode> <responsestring>
+
+    where <version> is the protocol version ("HTTP/1.0" or "HTTP/1.1"),
+    <responsecode> is a 3-digit response code indicating success or
+    failure of the request, and <responsestring> is an optional
+    human-readable string explaining what the response code means.
+
+    This server parses the request and the headers, and then calls a
+    function specific to the request type (<command>).  Specifically,
+    a request SPAM will be handled by a method do_SPAM().  If no
+    such method exists the server sends an error response to the
+    client.  If it exists, it is called with no arguments:
+
+    do_SPAM()
+
+    Note that the request name is case sensitive (i.e. SPAM and spam
+    are different requests).
+
+    The various request details are stored in instance variables:
+
+    - client_address is the client IP address in the form (host,
+    port);
+
+    - command, path and version are the broken-down request line;
+
+    - headers is an instance of mimetools.Message (or a derived
+    class) containing the header information;
+
+    - rfile is a file object open for reading positioned at the
+    start of the optional input data part;
+
+    - wfile is a file object open for writing.
+
+    IT IS IMPORTANT TO ADHERE TO THE PROTOCOL FOR WRITING!
+
+    The first thing to be written must be the response line.  Then
+    follow 0 or more header lines, then a blank line, and then the
+    actual data (if any).  The meaning of the header lines depends on
+    the command executed by the server; in most cases, when data is
+    returned, there should be at least one header line of the form
+
+    Content-type: <type>/<subtype>
+
+    where <type> and <subtype> should be registered MIME types,
+    e.g. "text/html" or "text/plain".
+
+    """
+
+    # The Python system version, truncated to its first component.
+    sys_version = "Python/" + sys.version.split()[0]
+
+    # The server software version.  You may want to override this.
+    # The format is multiple whitespace-separated strings,
+    # where each string is of the form name[/version].
+    server_version = "BaseHTTP/" + __version__
+
+    # The default request version.  This only affects responses up until
+    # the point where the request line is parsed, so it mainly decides what
+    # the client gets back when sending a malformed request line.
+    # Most web servers default to HTTP 0.9, i.e. don't send a status line.
+    default_request_version = "HTTP/0.9"
+
+    def parse_request(self):
+        """Parse a request (internal).
+
+        The request should be stored in self.raw_requestline; the results
+        are in self.command, self.path, self.request_version and
+        self.headers.
+
+        Return True for success, False for failure; on failure, an
+        error is sent back.
+
+        """
+        self.command = None  # set in case of error on the first line
+        self.request_version = version = self.default_request_version
+        self.close_connection = 1
+        requestline = self.raw_requestline
+        requestline = requestline.rstrip('\r\n')
+        self.requestline = requestline
+        words = requestline.split()
+        if len(words) == 3:
+            command, path, version = words
+            if version[:5] != 'HTTP/':
+                self.send_error(400, "Bad request version (%r)" % version)
+                return False
+            try:
+                base_version_number = version.split('/', 1)[1]
+                version_number = base_version_number.split(".")
+                # RFC 2145 section 3.1 says there can be only one "." and
+                #   - major and minor numbers MUST be treated as
+                #      separate integers;
+                #   - HTTP/2.4 is a lower version than HTTP/2.13, which in
+                #      turn is lower than HTTP/12.3;
+                #   - Leading zeros MUST be ignored by recipients.
+                if len(version_number) != 2:
+                    raise ValueError
+                version_number = int(version_number[0]), int(version_number[1])
+            except (ValueError, IndexError):
+                self.send_error(400, "Bad request version (%r)" % version)
+                return False
+            if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1":
+                self.close_connection = 0
+            if version_number >= (2, 0):
+                self.send_error(505,
+                          "Invalid HTTP Version (%s)" % base_version_number)
+                return False
+        elif len(words) == 2:
+            command, path = words
+            self.close_connection = 1
+            if command != 'GET':
+                self.send_error(400,
+                                "Bad HTTP/0.9 request type (%r)" % command)
+                return False
+        elif not words:
+            return False
+        else:
+            self.send_error(400, "Bad request syntax (%r)" % requestline)
+            return False
+        self.command, self.path, self.request_version = command, path, version
+
+        # Examine the headers and look for a Connection directive
+        self.headers = self.MessageClass(self.rfile, 0)
+
+        conntype = self.headers.get('Connection', "")
+        if conntype.lower() == 'close':
+            self.close_connection = 1
+        elif (conntype.lower() == 'keep-alive' and
+              self.protocol_version >= "HTTP/1.1"):
+            self.close_connection = 0
+        return True
+
+    def handle_one_request(self):
+        """Handle a single HTTP request.
+
+        You normally don't need to override this method; see the class
+        __doc__ string for information on how to handle specific HTTP
+        commands such as GET and POST.
+
+        """
+        try:
+            self.raw_requestline = self.rfile.readline(65537)
+            if len(self.raw_requestline) > 65536:
+                self.requestline = ''
+                self.request_version = ''
+                self.command = ''
+                self.send_error(414)
+                return
+            if not self.raw_requestline:
+                self.close_connection = 1
+                return
+            if not self.parse_request():
+                # An error code has been sent, just exit
+                return
+            mname = 'do_' + self.command
+            if not hasattr(self, mname):
+                self.send_error(501, "Unsupported method (%r)" % self.command)
+                return
+            method = getattr(self, mname)
+            method()
+            self.wfile.flush() #actually send the response if not already done.
+        except socket.timeout:
+            #a read or a write timed out.  Discard this connection
+            self.log_error("Request timed out: %r", sys.exc_info()[1])
+            self.close_connection = 1
+            return
+
+    def handle(self):
+        """Handle multiple requests if necessary."""
+        self.close_connection = 1
+
+        self.handle_one_request()
+        while not self.close_connection:
+            self.handle_one_request()
+
+    def send_error(self, code, message=None):
+        """Send and log an error reply.
+
+        Arguments are the error code, and a detailed message.
+        The detailed message defaults to the short entry matching the
+        response code.
+
+        This sends an error response (so it must be called before any
+        output has been generated), logs the error, and finally sends
+        a piece of HTML explaining the error to the user.
+
+        """
+
+        try:
+            short, long = self.responses[code]
+        except KeyError:
+            short, long = '???', '???'
+        if message is None:
+            message = short
+        explain = long
+        self.log_error("code %d, message %s", code, message)
+        # using _quote_html to prevent Cross Site Scripting attacks (see bug #1100201)
+        content = (self.error_message_format %
+                   {'code': code, 'message': _quote_html(message), 'explain': explain})
+        self.send_response(code, message)
+        self.send_header("Content-Type", self.error_content_type)
+        self.send_header('Connection', 'close')
+        self.end_headers()
+        if self.command != 'HEAD' and code >= 200 and code not in (204, 304):
+            self.wfile.write(content)
+
+    error_message_format = DEFAULT_ERROR_MESSAGE
+    error_content_type = DEFAULT_ERROR_CONTENT_TYPE
+
+    def send_response(self, code, message=None):
+        """Send the response header and log the response code.
+
+        Also send two standard headers with the server software
+        version and the current date.
+
+        """
+        self.log_request(code)
+        if message is None:
+            if code in self.responses:
+                message = self.responses[code][0]
+            else:
+                message = ''
+        if self.request_version != 'HTTP/0.9':
+            self.wfile.write("%s %d %s\r\n" %
+                             (self.protocol_version, code, message))
+            # print (self.protocol_version, code, message)
+        self.send_header('Server', self.version_string())
+        self.send_header('Date', self.date_time_string())
+
+    def send_header(self, keyword, value):
+        """Send a MIME header."""
+        if self.request_version != 'HTTP/0.9':
+            self.wfile.write("%s: %s\r\n" % (keyword, value))
+
+        if keyword.lower() == 'connection':
+            if value.lower() == 'close':
+                self.close_connection = 1
+            elif value.lower() == 'keep-alive':
+                self.close_connection = 0
+
+    def end_headers(self):
+        """Send the blank line ending the MIME headers."""
+        if self.request_version != 'HTTP/0.9':
+            self.wfile.write("\r\n")
+
+    def log_request(self, code='-', size='-'):
+        """Log an accepted request.
+
+        This is called by send_response().
+
+        """
+
+        self.log_message('"%s" %s %s',
+                         self.requestline, str(code), str(size))
+
+    def log_error(self, format, *args):
+        """Log an error.
+
+        This is called when a request cannot be fulfilled.  By
+        default it passes the message on to log_message().
+
+        Arguments are the same as for log_message().
+
+        XXX This should go to the separate error log.
+
+        """
+
+        self.log_message(format, *args)
+
+    def log_message(self, format, *args):
+        """Log an arbitrary message.
+
+        This is used by all other logging functions.  Override
+        it if you have specific logging wishes.
+
+        The first argument, FORMAT, is a format string for the
+        message to be logged.  If the format string contains
+        any % escapes requiring parameters, they should be
+        specified as subsequent arguments (it's just like
+        printf!).
+
+        The client host and current date/time are prefixed to
+        every message.
+
+        """
+
+        sys.stderr.write("%s - - [%s] %s\n" %
+                         (self.address_string(),
+                          self.log_date_time_string(),
+                          format%args))
+
+    def version_string(self):
+        """Return the server software version string."""
+        return self.server_version + ' ' + self.sys_version
+
+    def date_time_string(self, timestamp=None):
+        """Return the current date and time formatted for a message header."""
+        if timestamp is None:
+            timestamp = time.time()
+        year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp)
+        s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
+                self.weekdayname[wd],
+                day, self.monthname[month], year,
+                hh, mm, ss)
+        return s
+
+    def log_date_time_string(self):
+        """Return the current time formatted for logging."""
+        now = time.time()
+        year, month, day, hh, mm, ss, x, y, z = time.localtime(now)
+        s = "%02d/%3s/%04d %02d:%02d:%02d" % (
+                day, self.monthname[month], year, hh, mm, ss)
+        return s
+
+    weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
+
+    monthname = [None,
+                 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+                 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+
+    def address_string(self):
+        """Return the client address formatted for logging.
+
+        This version looks up the full hostname using gethostbyaddr(),
+        and tries to find a name that contains at least one dot.
+
+        """
+
+        host, port = self.client_address[:2]
+        return socket.getfqdn(host)
+
+    # Essentially static class variables
+
+    # The version of the HTTP protocol we support.
+    # Set this to HTTP/1.1 to enable automatic keepalive
+    protocol_version = "HTTP/1.0"
+
+    # The Message-like class used to parse headers
+    MessageClass = mimetools.Message
+
+    # Table mapping response codes to messages; entries have the
+    # form {code: (shortmessage, longmessage)}.
+    # See RFC 2616.
+    responses = {
+        100: ('Continue', 'Request received, please continue'),
+        101: ('Switching Protocols',
+              'Switching to new protocol; obey Upgrade header'),
+
+        200: ('OK', 'Request fulfilled, document follows'),
+        201: ('Created', 'Document created, URL follows'),
+        202: ('Accepted',
+              'Request accepted, processing continues off-line'),
+        203: ('Non-Authoritative Information', 'Request fulfilled from cache'),
+        204: ('No Content', 'Request fulfilled, nothing follows'),
+        205: ('Reset Content', 'Clear input form for further input.'),
+        206: ('Partial Content', 'Partial content follows.'),
+
+        300: ('Multiple Choices',
+              'Object has several resources -- see URI list'),
+        301: ('Moved Permanently', 'Object moved permanently -- see URI list'),
+        302: ('Found', 'Object moved temporarily -- see URI list'),
+        303: ('See Other', 'Object moved -- see Method and URL list'),
+        304: ('Not Modified',
+              'Document has not changed since given time'),
+        305: ('Use Proxy',
+              'You must use proxy specified in Location to access this '
+              'resource.'),
+        307: ('Temporary Redirect',
+              'Object moved temporarily -- see URI list'),
+
+        400: ('Bad Request',
+              'Bad request syntax or unsupported method'),
+        401: ('Unauthorized',
+              'No permission -- see authorization schemes'),
+        402: ('Payment Required',
+              'No payment -- see charging schemes'),
+        403: ('Forbidden',
+              'Request forbidden -- authorization will not help'),
+        404: ('Not Found', 'Nothing matches the given URI'),
+        405: ('Method Not Allowed',
+              'Specified method is invalid for this resource.'),
+        406: ('Not Acceptable', 'URI not available in preferred format.'),
+        407: ('Proxy Authentication Required', 'You must authenticate with '
+              'this proxy before proceeding.'),
+        408: ('Request Timeout', 'Request timed out; try again later.'),
+        409: ('Conflict', 'Request conflict.'),
+        410: ('Gone',
+              'URI no longer exists and has been permanently removed.'),
+        411: ('Length Required', 'Client must specify Content-Length.'),
+        412: ('Precondition Failed', 'Precondition in headers is false.'),
+        413: ('Request Entity Too Large', 'Entity is too large.'),
+        414: ('Request-URI Too Long', 'URI is too long.'),
+        415: ('Unsupported Media Type', 'Entity body in unsupported format.'),
+        416: ('Requested Range Not Satisfiable',
+              'Cannot satisfy request range.'),
+        417: ('Expectation Failed',
+              'Expect condition could not be satisfied.'),
+
+        500: ('Internal Server Error', 'Server got itself in trouble'),
+        501: ('Not Implemented',
+              'Server does not support this operation'),
+        502: ('Bad Gateway', 'Invalid responses from another server/proxy.'),
+        503: ('Service Unavailable',
+              'The server cannot process the request due to a high load'),
+        504: ('Gateway Timeout',
+              'The gateway server did not receive a timely response'),
+        505: ('HTTP Version Not Supported', 'Cannot fulfill request.'),
+        }
+
+
+def test(HandlerClass = BaseHTTPRequestHandler,
+         ServerClass = HTTPServer, protocol="HTTP/1.0"):
+    """Test the HTTP request handler class.
+
+    This runs an HTTP server on port 8000 (or the first command line
+    argument).
+
+    """
+
+    if sys.argv[1:]:
+        port = int(sys.argv[1])
+    else:
+        port = 8000
+    server_address = ('', port)
+
+    HandlerClass.protocol_version = protocol
+    httpd = ServerClass(server_address, HandlerClass)
+
+    sa = httpd.socket.getsockname()
+    print ("Serving HTTP on", sa[0], "port", sa[1], "...")
+    httpd.serve_forever()
+
+
+if __name__ == '__main__':
+    test()
diff --git a/python/helpers/pydev/_pydev_imps/_pydev_Queue.py b/python/helpers/pydev/_pydev_imps/_pydev_Queue.py
new file mode 100644
index 0000000..d351b50
--- /dev/null
+++ b/python/helpers/pydev/_pydev_imps/_pydev_Queue.py
@@ -0,0 +1,244 @@
+"""A multi-producer, multi-consumer queue."""
+
+from _pydev_imps._pydev_time import time as _time
+try:
+    import _pydev_threading as _threading
+except ImportError:
+    import dummy_threading as _threading
+from collections import deque
+import heapq
+
+__all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue']
+
+class Empty(Exception):
+    "Exception raised by Queue.get(block=0)/get_nowait()."
+    pass
+
+class Full(Exception):
+    "Exception raised by Queue.put(block=0)/put_nowait()."
+    pass
+
+class Queue:
+    """Create a queue object with a given maximum size.
+
+    If maxsize is <= 0, the queue size is infinite.
+    """
+    def __init__(self, maxsize=0):
+        self.maxsize = maxsize
+        self._init(maxsize)
+        # mutex must be held whenever the queue is mutating.  All methods
+        # that acquire mutex must release it before returning.  mutex
+        # is shared between the three conditions, so acquiring and
+        # releasing the conditions also acquires and releases mutex.
+        self.mutex = _threading.Lock()
+        # Notify not_empty whenever an item is added to the queue; a
+        # thread waiting to get is notified then.
+        self.not_empty = _threading.Condition(self.mutex)
+        # Notify not_full whenever an item is removed from the queue;
+        # a thread waiting to put is notified then.
+        self.not_full = _threading.Condition(self.mutex)
+        # Notify all_tasks_done whenever the number of unfinished tasks
+        # drops to zero; thread waiting to join() is notified to resume
+        self.all_tasks_done = _threading.Condition(self.mutex)
+        self.unfinished_tasks = 0
+
+    def task_done(self):
+        """Indicate that a formerly enqueued task is complete.
+
+        Used by Queue consumer threads.  For each get() used to fetch a task,
+        a subsequent call to task_done() tells the queue that the processing
+        on the task is complete.
+
+        If a join() is currently blocking, it will resume when all items
+        have been processed (meaning that a task_done() call was received
+        for every item that had been put() into the queue).
+
+        Raises a ValueError if called more times than there were items
+        placed in the queue.
+        """
+        self.all_tasks_done.acquire()
+        try:
+            unfinished = self.unfinished_tasks - 1
+            if unfinished <= 0:
+                if unfinished < 0:
+                    raise ValueError('task_done() called too many times')
+                self.all_tasks_done.notify_all()
+            self.unfinished_tasks = unfinished
+        finally:
+            self.all_tasks_done.release()
+
+    def join(self):
+        """Blocks until all items in the Queue have been gotten and processed.
+
+        The count of unfinished tasks goes up whenever an item is added to the
+        queue. The count goes down whenever a consumer thread calls task_done()
+        to indicate the item was retrieved and all work on it is complete.
+
+        When the count of unfinished tasks drops to zero, join() unblocks.
+        """
+        self.all_tasks_done.acquire()
+        try:
+            while self.unfinished_tasks:
+                self.all_tasks_done.wait()
+        finally:
+            self.all_tasks_done.release()
+
+    def qsize(self):
+        """Return the approximate size of the queue (not reliable!)."""
+        self.mutex.acquire()
+        n = self._qsize()
+        self.mutex.release()
+        return n
+
+    def empty(self):
+        """Return True if the queue is empty, False otherwise (not reliable!)."""
+        self.mutex.acquire()
+        n = not self._qsize()
+        self.mutex.release()
+        return n
+
+    def full(self):
+        """Return True if the queue is full, False otherwise (not reliable!)."""
+        self.mutex.acquire()
+        n = 0 < self.maxsize == self._qsize()
+        self.mutex.release()
+        return n
+
+    def put(self, item, block=True, timeout=None):
+        """Put an item into the queue.
+
+        If optional args 'block' is true and 'timeout' is None (the default),
+        block if necessary until a free slot is available. If 'timeout' is
+        a positive number, it blocks at most 'timeout' seconds and raises
+        the Full exception if no free slot was available within that time.
+        Otherwise ('block' is false), put an item on the queue if a free slot
+        is immediately available, else raise the Full exception ('timeout'
+        is ignored in that case).
+        """
+        self.not_full.acquire()
+        try:
+            if self.maxsize > 0:
+                if not block:
+                    if self._qsize() == self.maxsize:
+                        raise Full
+                elif timeout is None:
+                    while self._qsize() == self.maxsize:
+                        self.not_full.wait()
+                elif timeout < 0:
+                    raise ValueError("'timeout' must be a positive number")
+                else:
+                    endtime = _time() + timeout
+                    while self._qsize() == self.maxsize:
+                        remaining = endtime - _time()
+                        if remaining <= 0.0:
+                            raise Full
+                        self.not_full.wait(remaining)
+            self._put(item)
+            self.unfinished_tasks += 1
+            self.not_empty.notify()
+        finally:
+            self.not_full.release()
+
+    def put_nowait(self, item):
+        """Put an item into the queue without blocking.
+
+        Only enqueue the item if a free slot is immediately available.
+        Otherwise raise the Full exception.
+        """
+        return self.put(item, False)
+
+    def get(self, block=True, timeout=None):
+        """Remove and return an item from the queue.
+
+        If optional args 'block' is true and 'timeout' is None (the default),
+        block if necessary until an item is available. If 'timeout' is
+        a positive number, it blocks at most 'timeout' seconds and raises
+        the Empty exception if no item was available within that time.
+        Otherwise ('block' is false), return an item if one is immediately
+        available, else raise the Empty exception ('timeout' is ignored
+        in that case).
+        """
+        self.not_empty.acquire()
+        try:
+            if not block:
+                if not self._qsize():
+                    raise Empty
+            elif timeout is None:
+                while not self._qsize():
+                    self.not_empty.wait()
+            elif timeout < 0:
+                raise ValueError("'timeout' must be a positive number")
+            else:
+                endtime = _time() + timeout
+                while not self._qsize():
+                    remaining = endtime - _time()
+                    if remaining <= 0.0:
+                        raise Empty
+                    self.not_empty.wait(remaining)
+            item = self._get()
+            self.not_full.notify()
+            return item
+        finally:
+            self.not_empty.release()
+
+    def get_nowait(self):
+        """Remove and return an item from the queue without blocking.
+
+        Only get an item if one is immediately available. Otherwise
+        raise the Empty exception.
+        """
+        return self.get(False)
+
+    # Override these methods to implement other queue organizations
+    # (e.g. stack or priority queue).
+    # These will only be called with appropriate locks held
+
+    # Initialize the queue representation
+    def _init(self, maxsize):
+        self.queue = deque()
+
+    def _qsize(self, len=len):
+        return len(self.queue)
+
+    # Put a new item in the queue
+    def _put(self, item):
+        self.queue.append(item)
+
+    # Get an item from the queue
+    def _get(self):
+        return self.queue.popleft()
+
+
+class PriorityQueue(Queue):
+    '''Variant of Queue that retrieves open entries in priority order (lowest first).
+
+    Entries are typically tuples of the form:  (priority number, data).
+    '''
+
+    def _init(self, maxsize):
+        self.queue = []
+
+    def _qsize(self, len=len):
+        return len(self.queue)
+
+    def _put(self, item, heappush=heapq.heappush):
+        heappush(self.queue, item)
+
+    def _get(self, heappop=heapq.heappop):
+        return heappop(self.queue)
+
+
+class LifoQueue(Queue):
+    '''Variant of Queue that retrieves most recently added entries first.'''
+
+    def _init(self, maxsize):
+        self.queue = []
+
+    def _qsize(self, len=len):
+        return len(self.queue)
+
+    def _put(self, item):
+        self.queue.append(item)
+
+    def _get(self):
+        return self.queue.pop()
diff --git a/python/helpers/pydev/_pydev_imps/_pydev_SimpleXMLRPCServer.py b/python/helpers/pydev/_pydev_imps/_pydev_SimpleXMLRPCServer.py
new file mode 100644
index 0000000..5a0c2af
--- /dev/null
+++ b/python/helpers/pydev/_pydev_imps/_pydev_SimpleXMLRPCServer.py
@@ -0,0 +1,610 @@
+#Just a copy of the version in python 2.5 to be used if it's not available in jython 2.1
+
+"""Simple XML-RPC Server.
+
+This module can be used to create simple XML-RPC servers
+by creating a server and either installing functions, a
+class instance, or by extending the SimpleXMLRPCServer
+class.
+
+It can also be used to handle XML-RPC requests in a CGI
+environment using CGIXMLRPCRequestHandler.
+
+A list of possible usage patterns follows:
+
+1. Install functions:
+
+server = SimpleXMLRPCServer(("localhost", 8000))
+server.register_function(pow)
+server.register_function(lambda x,y: x+y, 'add')
+server.serve_forever()
+
+2. Install an instance:
+
+class MyFuncs:
+    def __init__(self):
+        # make all of the string functions available through
+        # string.func_name
+        import string
+        self.string = string
+    def _listMethods(self):
+        # implement this method so that system.listMethods
+        # knows to advertise the strings methods
+        return list_public_methods(self) + \
+                ['string.' + method for method in list_public_methods(self.string)]
+    def pow(self, x, y): return pow(x, y)
+    def add(self, x, y) : return x + y
+
+server = SimpleXMLRPCServer(("localhost", 8000))
+server.register_introspection_functions()
+server.register_instance(MyFuncs())
+server.serve_forever()
+
+3. Install an instance with custom dispatch method:
+
+class Math:
+    def _listMethods(self):
+        # this method must be present for system.listMethods
+        # to work
+        return ['add', 'pow']
+    def _methodHelp(self, method):
+        # this method must be present for system.methodHelp
+        # to work
+        if method == 'add':
+            return "add(2,3) => 5"
+        elif method == 'pow':
+            return "pow(x, y[, z]) => number"
+        else:
+            # By convention, return empty
+            # string if no help is available
+            return ""
+    def _dispatch(self, method, params):
+        if method == 'pow':
+            return pow(*params)
+        elif method == 'add':
+            return params[0] + params[1]
+        else:
+            raise 'bad method'
+
+server = SimpleXMLRPCServer(("localhost", 8000))
+server.register_introspection_functions()
+server.register_instance(Math())
+server.serve_forever()
+
+4. Subclass SimpleXMLRPCServer:
+
+class MathServer(SimpleXMLRPCServer):
+    def _dispatch(self, method, params):
+        try:
+            # We are forcing the 'export_' prefix on methods that are
+            # callable through XML-RPC to prevent potential security
+            # problems
+            func = getattr(self, 'export_' + method)
+        except AttributeError:
+            raise Exception('method "%s" is not supported' % method)
+        else:
+            return func(*params)
+
+    def export_add(self, x, y):
+        return x + y
+
+server = MathServer(("localhost", 8000))
+server.serve_forever()
+
+5. CGI script:
+
+server = CGIXMLRPCRequestHandler()
+server.register_function(pow)
+server.handle_request()
+"""
+
+# Written by Brian Quinlan (brian@sweetapp.com).
+# Based on code written by Fredrik Lundh.
+
+try:
+    True
+    False
+except:
+    import __builtin__
+    setattr(__builtin__, 'True', 1) #Python 3.0 does not accept __builtin__.True = 1 in its syntax
+    setattr(__builtin__, 'False', 0)
+
+
+from _pydev_imps import _pydev_xmlrpclib as xmlrpclib
+from _pydev_imps._pydev_xmlrpclib import Fault
+from _pydev_imps import _pydev_SocketServer as SocketServer
+from _pydev_imps import _pydev_BaseHTTPServer as BaseHTTPServer
+import sys
+import os
+try:
+    import fcntl
+except ImportError:
+    fcntl = None
+
+def resolve_dotted_attribute(obj, attr, allow_dotted_names=True):
+    """resolve_dotted_attribute(a, 'b.c.d') => a.b.c.d
+
+    Resolves a dotted attribute name to an object.  Raises
+    an AttributeError if any attribute in the chain starts with a '_'.
+
+    If the optional allow_dotted_names argument is false, dots are not
+    supported and this function operates similar to getattr(obj, attr).
+    """
+
+    if allow_dotted_names:
+        attrs = attr.split('.')
+    else:
+        attrs = [attr]
+
+    for i in attrs:
+        if i.startswith('_'):
+            raise AttributeError(
+                'attempt to access private attribute "%s"' % i
+                )
+        else:
+            obj = getattr(obj, i)
+    return obj
+
+def list_public_methods(obj):
+    """Returns a list of attribute strings, found in the specified
+    object, which represent callable attributes"""
+
+    return [member for member in dir(obj)
+                if not member.startswith('_') and
+                    callable(getattr(obj, member))]
+
+def remove_duplicates(lst):
+    """remove_duplicates([2,2,2,1,3,3]) => [3,1,2]
+
+    Returns a copy of a list without duplicates. Every list
+    item must be hashable and the order of the items in the
+    resulting list is not defined.
+    """
+    u = {}
+    for x in lst:
+        u[x] = 1
+
+    return u.keys()
+
+class SimpleXMLRPCDispatcher:
+    """Mix-in class that dispatches XML-RPC requests.
+
+    This class is used to register XML-RPC method handlers
+    and then to dispatch them. There should never be any
+    reason to instantiate this class directly.
+    """
+
+    def __init__(self, allow_none, encoding):
+        self.funcs = {}
+        self.instance = None
+        self.allow_none = allow_none
+        self.encoding = encoding
+
+    def register_instance(self, instance, allow_dotted_names=False):
+        """Registers an instance to respond to XML-RPC requests.
+
+        Only one instance can be installed at a time.
+
+        If the registered instance has a _dispatch method then that
+        method will be called with the name of the XML-RPC method and
+        its parameters as a tuple
+        e.g. instance._dispatch('add',(2,3))
+
+        If the registered instance does not have a _dispatch method
+        then the instance will be searched to find a matching method
+        and, if found, will be called. Methods beginning with an '_'
+        are considered private and will not be called by
+        SimpleXMLRPCServer.
+
+        If a registered function matches a XML-RPC request, then it
+        will be called instead of the registered instance.
+
+        If the optional allow_dotted_names argument is true and the
+        instance does not have a _dispatch method, method names
+        containing dots are supported and resolved, as long as none of
+        the name segments start with an '_'.
+
+            *** SECURITY WARNING: ***
+
+            Enabling the allow_dotted_names options allows intruders
+            to access your module's global variables and may allow
+            intruders to execute arbitrary code on your machine.  Only
+            use this option on a secure, closed network.
+
+        """
+
+        self.instance = instance
+        self.allow_dotted_names = allow_dotted_names
+
+    def register_function(self, function, name=None):
+        """Registers a function to respond to XML-RPC requests.
+
+        The optional name argument can be used to set a Unicode name
+        for the function.
+        """
+
+        if name is None:
+            name = function.__name__
+        self.funcs[name] = function
+
+    def register_introspection_functions(self):
+        """Registers the XML-RPC introspection methods in the system
+        namespace.
+
+        see http://xmlrpc.usefulinc.com/doc/reserved.html
+        """
+
+        self.funcs.update({'system.listMethods' : self.system_listMethods,
+                      'system.methodSignature' : self.system_methodSignature,
+                      'system.methodHelp' : self.system_methodHelp})
+
+    def register_multicall_functions(self):
+        """Registers the XML-RPC multicall method in the system
+        namespace.
+
+        see http://www.xmlrpc.com/discuss/msgReader$1208"""
+
+        self.funcs.update({'system.multicall' : self.system_multicall})
+
+    def _marshaled_dispatch(self, data, dispatch_method=None):
+        """Dispatches an XML-RPC method from marshalled (XML) data.
+
+        XML-RPC methods are dispatched from the marshalled (XML) data
+        using the _dispatch method and the result is returned as
+        marshalled data. For backwards compatibility, a dispatch
+        function can be provided as an argument (see comment in
+        SimpleXMLRPCRequestHandler.do_POST) but overriding the
+        existing method through subclassing is the prefered means
+        of changing method dispatch behavior.
+        """
+        try:
+            params, method = xmlrpclib.loads(data)
+
+            # generate response
+            if dispatch_method is not None:
+                response = dispatch_method(method, params)
+            else:
+                response = self._dispatch(method, params)
+            # wrap response in a singleton tuple
+            response = (response,)
+            response = xmlrpclib.dumps(response, methodresponse=1,
+                                       allow_none=self.allow_none, encoding=self.encoding)
+        except Fault, fault:
+            response = xmlrpclib.dumps(fault, allow_none=self.allow_none,
+                                       encoding=self.encoding)
+        except:
+            # report exception back to server
+            response = xmlrpclib.dumps(
+                xmlrpclib.Fault(1, "%s:%s" % (sys.exc_type, sys.exc_value)), #@UndefinedVariable exc_value only available when we actually have an exception
+                encoding=self.encoding, allow_none=self.allow_none,
+                )
+
+        return response
+
+    def system_listMethods(self):
+        """system.listMethods() => ['add', 'subtract', 'multiple']
+
+        Returns a list of the methods supported by the server."""
+
+        methods = self.funcs.keys()
+        if self.instance is not None:
+            # Instance can implement _listMethod to return a list of
+            # methods
+            if hasattr(self.instance, '_listMethods'):
+                methods = remove_duplicates(
+                        methods + self.instance._listMethods()
+                    )
+            # if the instance has a _dispatch method then we
+            # don't have enough information to provide a list
+            # of methods
+            elif not hasattr(self.instance, '_dispatch'):
+                methods = remove_duplicates(
+                        methods + list_public_methods(self.instance)
+                    )
+        methods.sort()
+        return methods
+
+    def system_methodSignature(self, method_name):
+        """system.methodSignature('add') => [double, int, int]
+
+        Returns a list describing the signature of the method. In the
+        above example, the add method takes two integers as arguments
+        and returns a double result.
+
+        This server does NOT support system.methodSignature."""
+
+        # See http://xmlrpc.usefulinc.com/doc/sysmethodsig.html
+
+        return 'signatures not supported'
+
+    def system_methodHelp(self, method_name):
+        """system.methodHelp('add') => "Adds two integers together"
+
+        Returns a string containing documentation for the specified method."""
+
+        method = None
+        if self.funcs.has_key(method_name):
+            method = self.funcs[method_name]
+        elif self.instance is not None:
+            # Instance can implement _methodHelp to return help for a method
+            if hasattr(self.instance, '_methodHelp'):
+                return self.instance._methodHelp(method_name)
+            # if the instance has a _dispatch method then we
+            # don't have enough information to provide help
+            elif not hasattr(self.instance, '_dispatch'):
+                try:
+                    method = resolve_dotted_attribute(
+                                self.instance,
+                                method_name,
+                                self.allow_dotted_names
+                                )
+                except AttributeError:
+                    pass
+
+        # Note that we aren't checking that the method actually
+        # be a callable object of some kind
+        if method is None:
+            return ""
+        else:
+            try:
+                import pydoc
+            except ImportError:
+                return "" #not there for jython
+            else:
+                return pydoc.getdoc(method)
+
+    def system_multicall(self, call_list):
+        """system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => \
+[[4], ...]
+
+        Allows the caller to package multiple XML-RPC calls into a single
+        request.
+
+        See http://www.xmlrpc.com/discuss/msgReader$1208
+        """
+
+        results = []
+        for call in call_list:
+            method_name = call['methodName']
+            params = call['params']
+
+            try:
+                # XXX A marshalling error in any response will fail the entire
+                # multicall. If someone cares they should fix this.
+                results.append([self._dispatch(method_name, params)])
+            except Fault, fault:
+                results.append(
+                    {'faultCode' : fault.faultCode,
+                     'faultString' : fault.faultString}
+                    )
+            except:
+                results.append(
+                    {'faultCode' : 1,
+                     'faultString' : "%s:%s" % (sys.exc_type, sys.exc_value)} #@UndefinedVariable exc_value only available when we actually have an exception
+                    )
+        return results
+
+    def _dispatch(self, method, params):
+        """Dispatches the XML-RPC method.
+
+        XML-RPC calls are forwarded to a registered function that
+        matches the called XML-RPC method name. If no such function
+        exists then the call is forwarded to the registered instance,
+        if available.
+
+        If the registered instance has a _dispatch method then that
+        method will be called with the name of the XML-RPC method and
+        its parameters as a tuple
+        e.g. instance._dispatch('add',(2,3))
+
+        If the registered instance does not have a _dispatch method
+        then the instance will be searched to find a matching method
+        and, if found, will be called.
+
+        Methods beginning with an '_' are considered private and will
+        not be called.
+        """
+
+        func = None
+        try:
+            # check to see if a matching function has been registered
+            func = self.funcs[method]
+        except KeyError:
+            if self.instance is not None:
+                # check for a _dispatch method
+                if hasattr(self.instance, '_dispatch'):
+                    return self.instance._dispatch(method, params)
+                else:
+                    # call instance method directly
+                    try:
+                        func = resolve_dotted_attribute(
+                            self.instance,
+                            method,
+                            self.allow_dotted_names
+                            )
+                    except AttributeError:
+                        pass
+
+        if func is not None:
+            return func(*params)
+        else:
+            raise Exception('method "%s" is not supported' % method)
+
+class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+    """Simple XML-RPC request handler class.
+
+    Handles all HTTP POST requests and attempts to decode them as
+    XML-RPC requests.
+    """
+
+    # Class attribute listing the accessible path components;
+    # paths not on this list will result in a 404 error.
+    rpc_paths = ('/', '/RPC2')
+
+    def is_rpc_path_valid(self):
+        if self.rpc_paths:
+            return self.path in self.rpc_paths
+        else:
+            # If .rpc_paths is empty, just assume all paths are legal
+            return True
+
+    def do_POST(self):
+        """Handles the HTTP POST request.
+
+        Attempts to interpret all HTTP POST requests as XML-RPC calls,
+        which are forwarded to the server's _dispatch method for handling.
+        """
+
+        # Check that the path is legal
+        if not self.is_rpc_path_valid():
+            self.report_404()
+            return
+
+        try:
+            # Get arguments by reading body of request.
+            # We read this in chunks to avoid straining
+            # socket.read(); around the 10 or 15Mb mark, some platforms
+            # begin to have problems (bug #792570).
+            max_chunk_size = 10 * 1024 * 1024
+            size_remaining = int(self.headers["content-length"])
+            L = []
+            while size_remaining:
+                chunk_size = min(size_remaining, max_chunk_size)
+                L.append(self.rfile.read(chunk_size))
+                size_remaining -= len(L[-1])
+            data = ''.join(L)
+
+            # In previous versions of SimpleXMLRPCServer, _dispatch
+            # could be overridden in this class, instead of in
+            # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
+            # check to see if a subclass implements _dispatch and dispatch
+            # using that method if present.
+            response = self.server._marshaled_dispatch(
+                    data, getattr(self, '_dispatch', None)
+                )
+        except: # This should only happen if the module is buggy
+            # internal error, report as HTTP server error
+            self.send_response(500)
+            self.end_headers()
+        else:
+            # got a valid XML RPC response
+            self.send_response(200)
+            self.send_header("Content-type", "text/xml")
+            self.send_header("Content-length", str(len(response)))
+            self.end_headers()
+            self.wfile.write(response)
+
+            # shut down the connection
+            self.wfile.flush()
+            self.connection.shutdown(1)
+
+    def report_404 (self):
+            # Report a 404 error
+        self.send_response(404)
+        response = 'No such page'
+        self.send_header("Content-type", "text/plain")
+        self.send_header("Content-length", str(len(response)))
+        self.end_headers()
+        self.wfile.write(response)
+        # shut down the connection
+        self.wfile.flush()
+        self.connection.shutdown(1)
+
+    def log_request(self, code='-', size='-'):
+        """Selectively log an accepted request."""
+
+        if self.server.logRequests:
+            BaseHTTPServer.BaseHTTPRequestHandler.log_request(self, code, size)
+
+class SimpleXMLRPCServer(SocketServer.TCPServer,
+                         SimpleXMLRPCDispatcher):
+    """Simple XML-RPC server.
+
+    Simple XML-RPC server that allows functions and a single instance
+    to be installed to handle requests. The default implementation
+    attempts to dispatch XML-RPC calls to the functions or instance
+    installed in the server. Override the _dispatch method inhereted
+    from SimpleXMLRPCDispatcher to change this behavior.
+    """
+
+    allow_reuse_address = True
+
+    def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
+                 logRequests=True, allow_none=False, encoding=None):
+        self.logRequests = logRequests
+
+        SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
+        SocketServer.TCPServer.__init__(self, addr, requestHandler)
+
+        # [Bug #1222790] If possible, set close-on-exec flag; if a
+        # method spawns a subprocess, the subprocess shouldn't have
+        # the listening socket open.
+        if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
+            flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
+            flags |= fcntl.FD_CLOEXEC
+            fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
+
+class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher):
+    """Simple handler for XML-RPC data passed through CGI."""
+
+    def __init__(self, allow_none=False, encoding=None):
+        SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
+
+    def handle_xmlrpc(self, request_text):
+        """Handle a single XML-RPC request"""
+
+        response = self._marshaled_dispatch(request_text)
+
+        sys.stdout.write('Content-Type: text/xml\n')
+        sys.stdout.write('Content-Length: %d\n' % len(response))
+        sys.stdout.write('\n')
+
+        sys.stdout.write(response)
+
+    def handle_get(self):
+        """Handle a single HTTP GET request.
+
+        Default implementation indicates an error because
+        XML-RPC uses the POST method.
+        """
+
+        code = 400
+        message, explain = \
+                 BaseHTTPServer.BaseHTTPRequestHandler.responses[code]
+
+        response = BaseHTTPServer.DEFAULT_ERROR_MESSAGE % { #@UndefinedVariable
+             'code' : code,
+             'message' : message,
+             'explain' : explain
+            }
+        sys.stdout.write('Status: %d %s\n' % (code, message))
+        sys.stdout.write('Content-Type: text/html\n')
+        sys.stdout.write('Content-Length: %d\n' % len(response))
+        sys.stdout.write('\n')
+
+        sys.stdout.write(response)
+
+    def handle_request(self, request_text=None):
+        """Handle a single XML-RPC request passed through a CGI post method.
+
+        If no XML data is given then it is read from stdin. The resulting
+        XML-RPC response is printed to stdout along with the correct HTTP
+        headers.
+        """
+
+        if request_text is None and \
+            os.environ.get('REQUEST_METHOD', None) == 'GET':
+            self.handle_get()
+        else:
+            # POST data is normally available through stdin
+            if request_text is None:
+                request_text = sys.stdin.read()
+
+            self.handle_xmlrpc(request_text)
+
+if __name__ == '__main__':
+    sys.stdout.write('Running XML-RPC server on port 8000\n')
+    server = SimpleXMLRPCServer(("localhost", 8000))
+    server.register_function(pow)
+    server.register_function(lambda x, y: x + y, 'add')
+    server.serve_forever()
diff --git a/python/helpers/pydev/_pydev_imps/_pydev_SocketServer.py b/python/helpers/pydev/_pydev_imps/_pydev_SocketServer.py
new file mode 100644
index 0000000..79cfb08
--- /dev/null
+++ b/python/helpers/pydev/_pydev_imps/_pydev_SocketServer.py
@@ -0,0 +1,715 @@
+"""Generic socket server classes.
+
+This module tries to capture the various aspects of defining a server:
+
+For socket-based servers:
+
+- address family:
+        - AF_INET{,6}: IP (Internet Protocol) sockets (default)
+        - AF_UNIX: Unix domain sockets
+        - others, e.g. AF_DECNET are conceivable (see <socket.h>
+- socket type:
+        - SOCK_STREAM (reliable stream, e.g. TCP)
+        - SOCK_DGRAM (datagrams, e.g. UDP)
+
+For request-based servers (including socket-based):
+
+- client address verification before further looking at the request
+        (This is actually a hook for any processing that needs to look
+         at the request before anything else, e.g. logging)
+- how to handle multiple requests:
+        - synchronous (one request is handled at a time)
+        - forking (each request is handled by a new process)
+        - threading (each request is handled by a new thread)
+
+The classes in this module favor the server type that is simplest to
+write: a synchronous TCP/IP server.  This is bad class design, but
+save some typing.  (There's also the issue that a deep class hierarchy
+slows down method lookups.)
+
+There are five classes in an inheritance diagram, four of which represent
+synchronous servers of four types:
+
+        +------------+
+        | BaseServer |
+        +------------+
+              |
+              v
+        +-----------+        +------------------+
+        | TCPServer |------->| UnixStreamServer |
+        +-----------+        +------------------+
+              |
+              v
+        +-----------+        +--------------------+
+        | UDPServer |------->| UnixDatagramServer |
+        +-----------+        +--------------------+
+
+Note that UnixDatagramServer derives from UDPServer, not from
+UnixStreamServer -- the only difference between an IP and a Unix
+stream server is the address family, which is simply repeated in both
+unix server classes.
+
+Forking and threading versions of each type of server can be created
+using the ForkingMixIn and ThreadingMixIn mix-in classes.  For
+instance, a threading UDP server class is created as follows:
+
+        class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
+
+The Mix-in class must come first, since it overrides a method defined
+in UDPServer! Setting the various member variables also changes
+the behavior of the underlying server mechanism.
+
+To implement a service, you must derive a class from
+BaseRequestHandler and redefine its handle() method.  You can then run
+various versions of the service by combining one of the server classes
+with your request handler class.
+
+The request handler class must be different for datagram or stream
+services.  This can be hidden by using the request handler
+subclasses StreamRequestHandler or DatagramRequestHandler.
+
+Of course, you still have to use your head!
+
+For instance, it makes no sense to use a forking server if the service
+contains state in memory that can be modified by requests (since the
+modifications in the child process would never reach the initial state
+kept in the parent process and passed to each child).  In this case,
+you can use a threading server, but you will probably have to use
+locks to avoid two requests that come in nearly simultaneous to apply
+conflicting changes to the server state.
+
+On the other hand, if you are building e.g. an HTTP server, where all
+data is stored externally (e.g. in the file system), a synchronous
+class will essentially render the service "deaf" while one request is
+being handled -- which may be for a very long time if a client is slow
+to read all the data it has requested.  Here a threading or forking
+server is appropriate.
+
+In some cases, it may be appropriate to process part of a request
+synchronously, but to finish processing in a forked child depending on
+the request data.  This can be implemented by using a synchronous
+server and doing an explicit fork in the request handler class
+handle() method.
+
+Another approach to handling multiple simultaneous requests in an
+environment that supports neither threads nor fork (or where these are
+too expensive or inappropriate for the service) is to maintain an
+explicit table of partially finished requests and to use select() to
+decide which request to work on next (or whether to handle a new
+incoming request).  This is particularly important for stream services
+where each client can potentially be connected for a long time (if
+threads or subprocesses cannot be used).
+
+Future work:
+- Standard classes for Sun RPC (which uses either UDP or TCP)
+- Standard mix-in classes to implement various authentication
+  and encryption schemes
+- Standard framework for select-based multiplexing
+
+XXX Open problems:
+- What to do with out-of-band data?
+
+BaseServer:
+- split generic "request" functionality out into BaseServer class.
+  Copyright (C) 2000  Luke Kenneth Casson Leighton <lkcl@samba.org>
+
+  example: read entries from a SQL database (requires overriding
+  get_request() to return a table entry from the database).
+  entry is processed by a RequestHandlerClass.
+
+"""
+
+# Author of the BaseServer patch: Luke Kenneth Casson Leighton
+
+# XXX Warning!
+# There is a test suite for this module, but it cannot be run by the
+# standard regression test.
+# To run it manually, run Lib/test/test_socketserver.py.
+
+__version__ = "0.4"
+
+
+from _pydev_imps import _pydev_socket as socket
+from _pydev_imps import _pydev_select as select
+import sys
+import os
+try:
+    import _pydev_threading as threading
+except ImportError:
+    import dummy_threading as threading
+
+__all__ = ["TCPServer","UDPServer","ForkingUDPServer","ForkingTCPServer",
+           "ThreadingUDPServer","ThreadingTCPServer","BaseRequestHandler",
+           "StreamRequestHandler","DatagramRequestHandler",
+           "ThreadingMixIn", "ForkingMixIn"]
+if hasattr(socket, "AF_UNIX"):
+    __all__.extend(["UnixStreamServer","UnixDatagramServer",
+                    "ThreadingUnixStreamServer",
+                    "ThreadingUnixDatagramServer"])
+
+class BaseServer:
+
+    """Base class for server classes.
+
+    Methods for the caller:
+
+    - __init__(server_address, RequestHandlerClass)
+    - serve_forever(poll_interval=0.5)
+    - shutdown()
+    - handle_request()  # if you do not use serve_forever()
+    - fileno() -> int   # for select()
+
+    Methods that may be overridden:
+
+    - server_bind()
+    - server_activate()
+    - get_request() -> request, client_address
+    - handle_timeout()
+    - verify_request(request, client_address)
+    - server_close()
+    - process_request(request, client_address)
+    - shutdown_request(request)
+    - close_request(request)
+    - handle_error()
+
+    Methods for derived classes:
+
+    - finish_request(request, client_address)
+
+    Class variables that may be overridden by derived classes or
+    instances:
+
+    - timeout
+    - address_family
+    - socket_type
+    - allow_reuse_address
+
+    Instance variables:
+
+    - RequestHandlerClass
+    - socket
+
+    """
+
+    timeout = None
+
+    def __init__(self, server_address, RequestHandlerClass):
+        """Constructor.  May be extended, do not override."""
+        self.server_address = server_address
+        self.RequestHandlerClass = RequestHandlerClass
+        self.__is_shut_down = threading.Event()
+        self.__shutdown_request = False
+
+    def server_activate(self):
+        """Called by constructor to activate the server.
+
+        May be overridden.
+
+        """
+        pass
+
+    def serve_forever(self, poll_interval=0.5):
+        """Handle one request at a time until shutdown.
+
+        Polls for shutdown every poll_interval seconds. Ignores
+        self.timeout. If you need to do periodic tasks, do them in
+        another thread.
+        """
+        self.__is_shut_down.clear()
+        try:
+            while not self.__shutdown_request:
+                # XXX: Consider using another file descriptor or
+                # connecting to the socket to wake this up instead of
+                # polling. Polling reduces our responsiveness to a
+                # shutdown request and wastes cpu at all other times.
+                r, w, e = select.select([self], [], [], poll_interval)
+                if self in r:
+                    self._handle_request_noblock()
+        finally:
+            self.__shutdown_request = False
+            self.__is_shut_down.set()
+
+    def shutdown(self):
+        """Stops the serve_forever loop.
+
+        Blocks until the loop has finished. This must be called while
+        serve_forever() is running in another thread, or it will
+        deadlock.
+        """
+        self.__shutdown_request = True
+        self.__is_shut_down.wait()
+
+    # The distinction between handling, getting, processing and
+    # finishing a request is fairly arbitrary.  Remember:
+    #
+    # - handle_request() is the top-level call.  It calls
+    #   select, get_request(), verify_request() and process_request()
+    # - get_request() is different for stream or datagram sockets
+    # - process_request() is the place that may fork a new process
+    #   or create a new thread to finish the request
+    # - finish_request() instantiates the request handler class;
+    #   this constructor will handle the request all by itself
+
+    def handle_request(self):
+        """Handle one request, possibly blocking.
+
+        Respects self.timeout.
+        """
+        # Support people who used socket.settimeout() to escape
+        # handle_request before self.timeout was available.
+        timeout = self.socket.gettimeout()
+        if timeout is None:
+            timeout = self.timeout
+        elif self.timeout is not None:
+            timeout = min(timeout, self.timeout)
+        fd_sets = select.select([self], [], [], timeout)
+        if not fd_sets[0]:
+            self.handle_timeout()
+            return
+        self._handle_request_noblock()
+
+    def _handle_request_noblock(self):
+        """Handle one request, without blocking.
+
+        I assume that select.select has returned that the socket is
+        readable before this function was called, so there should be
+        no risk of blocking in get_request().
+        """
+        try:
+            request, client_address = self.get_request()
+        except socket.error:
+            return
+        if self.verify_request(request, client_address):
+            try:
+                self.process_request(request, client_address)
+            except:
+                self.handle_error(request, client_address)
+                self.shutdown_request(request)
+
+    def handle_timeout(self):
+        """Called if no new request arrives within self.timeout.
+
+        Overridden by ForkingMixIn.
+        """
+        pass
+
+    def verify_request(self, request, client_address):
+        """Verify the request.  May be overridden.
+
+        Return True if we should proceed with this request.
+
+        """
+        return True
+
+    def process_request(self, request, client_address):
+        """Call finish_request.
+
+        Overridden by ForkingMixIn and ThreadingMixIn.
+
+        """
+        self.finish_request(request, client_address)
+        self.shutdown_request(request)
+
+    def server_close(self):
+        """Called to clean-up the server.
+
+        May be overridden.
+
+        """
+        pass
+
+    def finish_request(self, request, client_address):
+        """Finish one request by instantiating RequestHandlerClass."""
+        self.RequestHandlerClass(request, client_address, self)
+
+    def shutdown_request(self, request):
+        """Called to shutdown and close an individual request."""
+        self.close_request(request)
+
+    def close_request(self, request):
+        """Called to clean up an individual request."""
+        pass
+
+    def handle_error(self, request, client_address):
+        """Handle an error gracefully.  May be overridden.
+
+        The default is to print a traceback and continue.
+
+        """
+        print '-'*40
+        print 'Exception happened during processing of request from',
+        print client_address
+        import traceback
+        traceback.print_exc() # XXX But this goes to stderr!
+        print '-'*40
+
+
+class TCPServer(BaseServer):
+
+    """Base class for various socket-based server classes.
+
+    Defaults to synchronous IP stream (i.e., TCP).
+
+    Methods for the caller:
+
+    - __init__(server_address, RequestHandlerClass, bind_and_activate=True)
+    - serve_forever(poll_interval=0.5)
+    - shutdown()
+    - handle_request()  # if you don't use serve_forever()
+    - fileno() -> int   # for select()
+
+    Methods that may be overridden:
+
+    - server_bind()
+    - server_activate()
+    - get_request() -> request, client_address
+    - handle_timeout()
+    - verify_request(request, client_address)
+    - process_request(request, client_address)
+    - shutdown_request(request)
+    - close_request(request)
+    - handle_error()
+
+    Methods for derived classes:
+
+    - finish_request(request, client_address)
+
+    Class variables that may be overridden by derived classes or
+    instances:
+
+    - timeout
+    - address_family
+    - socket_type
+    - request_queue_size (only for stream sockets)
+    - allow_reuse_address
+
+    Instance variables:
+
+    - server_address
+    - RequestHandlerClass
+    - socket
+
+    """
+
+    address_family = socket.AF_INET
+
+    socket_type = socket.SOCK_STREAM
+
+    request_queue_size = 5
+
+    allow_reuse_address = False
+
+    def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
+        """Constructor.  May be extended, do not override."""
+        BaseServer.__init__(self, server_address, RequestHandlerClass)
+        self.socket = socket.socket(self.address_family,
+                                    self.socket_type)
+        if bind_and_activate:
+            self.server_bind()
+            self.server_activate()
+
+    def server_bind(self):
+        """Called by constructor to bind the socket.
+
+        May be overridden.
+
+        """
+        if self.allow_reuse_address:
+            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        self.socket.bind(self.server_address)
+        self.server_address = self.socket.getsockname()
+
+    def server_activate(self):
+        """Called by constructor to activate the server.
+
+        May be overridden.
+
+        """
+        self.socket.listen(self.request_queue_size)
+
+    def server_close(self):
+        """Called to clean-up the server.
+
+        May be overridden.
+
+        """
+        self.socket.close()
+
+    def fileno(self):
+        """Return socket file number.
+
+        Interface required by select().
+
+        """
+        return self.socket.fileno()
+
+    def get_request(self):
+        """Get the request and client address from the socket.
+
+        May be overridden.
+
+        """
+        return self.socket.accept()
+
+    def shutdown_request(self, request):
+        """Called to shutdown and close an individual request."""
+        try:
+            #explicitly shutdown.  socket.close() merely releases
+            #the socket and waits for GC to perform the actual close.
+            request.shutdown(socket.SHUT_WR)
+        except socket.error:
+            pass #some platforms may raise ENOTCONN here
+        self.close_request(request)
+
+    def close_request(self, request):
+        """Called to clean up an individual request."""
+        request.close()
+
+
+class UDPServer(TCPServer):
+
+    """UDP server class."""
+
+    allow_reuse_address = False
+
+    socket_type = socket.SOCK_DGRAM
+
+    max_packet_size = 8192
+
+    def get_request(self):
+        data, client_addr = self.socket.recvfrom(self.max_packet_size)
+        return (data, self.socket), client_addr
+
+    def server_activate(self):
+        # No need to call listen() for UDP.
+        pass
+
+    def shutdown_request(self, request):
+        # No need to shutdown anything.
+        self.close_request(request)
+
+    def close_request(self, request):
+        # No need to close anything.
+        pass
+
+class ForkingMixIn:
+
+    """Mix-in class to handle each request in a new process."""
+
+    timeout = 300
+    active_children = None
+    max_children = 40
+
+    def collect_children(self):
+        """Internal routine to wait for children that have exited."""
+        if self.active_children is None: return
+        while len(self.active_children) >= self.max_children:
+            # XXX: This will wait for any child process, not just ones
+            # spawned by this library. This could confuse other
+            # libraries that expect to be able to wait for their own
+            # children.
+            try:
+                pid, status = os.waitpid(0, 0)
+            except os.error:
+                pid = None
+            if pid not in self.active_children: continue
+            self.active_children.remove(pid)
+
+        # XXX: This loop runs more system calls than it ought
+        # to. There should be a way to put the active_children into a
+        # process group and then use os.waitpid(-pgid) to wait for any
+        # of that set, but I couldn't find a way to allocate pgids
+        # that couldn't collide.
+        for child in self.active_children:
+            try:
+                pid, status = os.waitpid(child, os.WNOHANG)
+            except os.error:
+                pid = None
+            if not pid: continue
+            try:
+                self.active_children.remove(pid)
+            except ValueError, e:
+                raise ValueError('%s. x=%d and list=%r' % (e.message, pid,
+                                                           self.active_children))
+
+    def handle_timeout(self):
+        """Wait for zombies after self.timeout seconds of inactivity.
+
+        May be extended, do not override.
+        """
+        self.collect_children()
+
+    def process_request(self, request, client_address):
+        """Fork a new subprocess to process the request."""
+        self.collect_children()
+        pid = os.fork()
+        if pid:
+            # Parent process
+            if self.active_children is None:
+                self.active_children = []
+            self.active_children.append(pid)
+            self.close_request(request) #close handle in parent process
+            return
+        else:
+            # Child process.
+            # This must never return, hence os._exit()!
+            try:
+                self.finish_request(request, client_address)
+                self.shutdown_request(request)
+                os._exit(0)
+            except:
+                try:
+                    self.handle_error(request, client_address)
+                    self.shutdown_request(request)
+                finally:
+                    os._exit(1)
+
+
+class ThreadingMixIn:
+    """Mix-in class to handle each request in a new thread."""
+
+    # Decides how threads will act upon termination of the
+    # main process
+    daemon_threads = False
+
+    def process_request_thread(self, request, client_address):
+        """Same as in BaseServer but as a thread.
+
+        In addition, exception handling is done here.
+
+        """
+        try:
+            self.finish_request(request, client_address)
+            self.shutdown_request(request)
+        except:
+            self.handle_error(request, client_address)
+            self.shutdown_request(request)
+
+    def process_request(self, request, client_address):
+        """Start a new thread to process the request."""
+        t = threading.Thread(target = self.process_request_thread,
+                             args = (request, client_address))
+        t.daemon = self.daemon_threads
+        t.start()
+
+
+class ForkingUDPServer(ForkingMixIn, UDPServer): pass
+class ForkingTCPServer(ForkingMixIn, TCPServer): pass
+
+class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
+class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
+
+if hasattr(socket, 'AF_UNIX'):
+
+    class UnixStreamServer(TCPServer):
+        address_family = socket.AF_UNIX
+
+    class UnixDatagramServer(UDPServer):
+        address_family = socket.AF_UNIX
+
+    class ThreadingUnixStreamServer(ThreadingMixIn, UnixStreamServer): pass
+
+    class ThreadingUnixDatagramServer(ThreadingMixIn, UnixDatagramServer): pass
+
+class BaseRequestHandler:
+
+    """Base class for request handler classes.
+
+    This class is instantiated for each request to be handled.  The
+    constructor sets the instance variables request, client_address
+    and server, and then calls the handle() method.  To implement a
+    specific service, all you need to do is to derive a class which
+    defines a handle() method.
+
+    The handle() method can find the request as self.request, the
+    client address as self.client_address, and the server (in case it
+    needs access to per-server information) as self.server.  Since a
+    separate instance is created for each request, the handle() method
+    can define arbitrary other instance variariables.
+
+    """
+
+    def __init__(self, request, client_address, server):
+        self.request = request
+        self.client_address = client_address
+        self.server = server
+        self.setup()
+        try:
+            self.handle()
+        finally:
+            self.finish()
+
+    def setup(self):
+        pass
+
+    def handle(self):
+        pass
+
+    def finish(self):
+        pass
+
+
+# The following two classes make it possible to use the same service
+# class for stream or datagram servers.
+# Each class sets up these instance variables:
+# - rfile: a file object from which receives the request is read
+# - wfile: a file object to which the reply is written
+# When the handle() method returns, wfile is flushed properly
+
+
+class StreamRequestHandler(BaseRequestHandler):
+
+    """Define self.rfile and self.wfile for stream sockets."""
+
+    # Default buffer sizes for rfile, wfile.
+    # We default rfile to buffered because otherwise it could be
+    # really slow for large data (a getc() call per byte); we make
+    # wfile unbuffered because (a) often after a write() we want to
+    # read and we need to flush the line; (b) big writes to unbuffered
+    # files are typically optimized by stdio even when big reads
+    # aren't.
+    rbufsize = -1
+    wbufsize = 0
+
+    # A timeout to apply to the request socket, if not None.
+    timeout = None
+
+    # Disable nagle algorithm for this socket, if True.
+    # Use only when wbufsize != 0, to avoid small packets.
+    disable_nagle_algorithm = False
+
+    def setup(self):
+        self.connection = self.request
+        if self.timeout is not None:
+            self.connection.settimeout(self.timeout)
+        if self.disable_nagle_algorithm:
+            self.connection.setsockopt(socket.IPPROTO_TCP,
+                                       socket.TCP_NODELAY, True)
+        self.rfile = self.connection.makefile('rb', self.rbufsize)
+        self.wfile = self.connection.makefile('wb', self.wbufsize)
+
+    def finish(self):
+        if not self.wfile.closed:
+            self.wfile.flush()
+        self.wfile.close()
+        self.rfile.close()
+
+
+class DatagramRequestHandler(BaseRequestHandler):
+
+    # XXX Regrettably, I cannot get this working on Linux;
+    # s.recvfrom() doesn't return a meaningful client address.
+
+    """Define self.rfile and self.wfile for datagram sockets."""
+
+    def setup(self):
+        try:
+            from cStringIO import StringIO
+        except ImportError:
+            from StringIO import StringIO
+        self.packet, self.socket = self.request
+        self.rfile = StringIO(self.packet)
+        self.wfile = StringIO()
+
+    def finish(self):
+        self.socket.sendto(self.wfile.getvalue(), self.client_address)
diff --git a/python/helpers/pydev/_pydev_imps/_pydev_execfile.py b/python/helpers/pydev/_pydev_imps/_pydev_execfile.py
new file mode 100644
index 0000000..954783c
--- /dev/null
+++ b/python/helpers/pydev/_pydev_imps/_pydev_execfile.py
@@ -0,0 +1,18 @@
+#We must redefine it in Py3k if it's not already there
+def execfile(file, glob=None, loc=None):
+    if glob is None:
+        import sys
+        glob = sys._getframe().f_back.f_globals
+    if loc is None:
+        loc = glob
+
+    # It seems that the best way is using tokenize.open(): http://code.activestate.com/lists/python-dev/131251/
+    import tokenize
+    stream = tokenize.open(file)
+    try:
+        contents = stream.read()
+    finally:
+        stream.close()
+
+    #execute the script (note: it's important to compile first to have the filename set in debug mode)
+    exec(compile(contents+"\n", file, 'exec'), glob, loc) 
\ No newline at end of file
diff --git a/python/helpers/pydev/_pydev_imps/_pydev_inspect.py b/python/helpers/pydev/_pydev_imps/_pydev_inspect.py
new file mode 100644
index 0000000..5714764
--- /dev/null
+++ b/python/helpers/pydev/_pydev_imps/_pydev_inspect.py
@@ -0,0 +1,794 @@
+"""Get useful information from live Python objects.
+
+This module encapsulates the interface provided by the internal special
+attributes (func_*, co_*, im_*, tb_*, etc.) in a friendlier fashion.
+It also provides some help for examining source code and class layout.
+
+Here are some of the useful functions provided by this module:
+
+    ismodule(), isclass(), ismethod(), isfunction(), istraceback(),
+        isframe(), iscode(), isbuiltin(), isroutine() - check object types
+    getmembers() - get members of an object that satisfy a given condition
+
+    getfile(), getsourcefile(), getsource() - find an object's source code
+    getdoc(), getcomments() - get documentation on an object
+    getmodule() - determine the module that an object came from
+    getclasstree() - arrange classes so as to represent their hierarchy
+
+    getargspec(), getargvalues() - get info about function arguments
+    formatargspec(), formatargvalues() - format an argument spec
+    getouterframes(), getinnerframes() - get info about frames
+    currentframe() - get the current stack frame
+    stack(), trace() - get info about frames on the stack or in a traceback
+"""
+
+# This module is in the public domain.  No warranties.
+
+__author__ = 'Ka-Ping Yee <ping@lfw.org>'
+__date__ = '1 Jan 2001'
+
+import sys
+import os
+import types
+import string
+import re
+import imp
+import tokenize
+
+# ----------------------------------------------------------- type-checking
+def ismodule(object):
+    """Return true if the object is a module.
+
+    Module objects provide these attributes:
+        __doc__         documentation string
+        __file__        filename (missing for built-in modules)"""
+    return isinstance(object, types.ModuleType)
+
+def isclass(object):
+    """Return true if the object is a class.
+
+    Class objects provide these attributes:
+        __doc__         documentation string
+        __module__      name of module in which this class was defined"""
+    return isinstance(object, types.ClassType) or hasattr(object, '__bases__')
+
+def ismethod(object):
+    """Return true if the object is an instance method.
+
+    Instance method objects provide these attributes:
+        __doc__         documentation string
+        __name__        name with which this method was defined
+        im_class        class object in which this method belongs
+        im_func         function object containing implementation of method
+        im_self         instance to which this method is bound, or None"""
+    return isinstance(object, types.MethodType)
+
+def ismethoddescriptor(object):
+    """Return true if the object is a method descriptor.
+
+    But not if ismethod() or isclass() or isfunction() are true.
+
+    This is new in Python 2.2, and, for example, is true of int.__add__.
+    An object passing this test has a __get__ attribute but not a __set__
+    attribute, but beyond that the set of attributes varies.  __name__ is
+    usually sensible, and __doc__ often is.
+
+    Methods implemented via descriptors that also pass one of the other
+    tests return false from the ismethoddescriptor() test, simply because
+    the other tests promise more -- you can, e.g., count on having the
+    im_func attribute (etc) when an object passes ismethod()."""
+    return (hasattr(object, "__get__")
+            and not hasattr(object, "__set__") # else it's a data descriptor
+            and not ismethod(object)           # mutual exclusion
+            and not isfunction(object)
+            and not isclass(object))
+
+def isfunction(object):
+    """Return true if the object is a user-defined function.
+
+    Function objects provide these attributes:
+        __doc__         documentation string
+        __name__        name with which this function was defined
+        func_code       code object containing compiled function bytecode
+        func_defaults   tuple of any default values for arguments
+        func_doc        (same as __doc__)
+        func_globals    global namespace in which this function was defined
+        func_name       (same as __name__)"""
+    return isinstance(object, types.FunctionType)
+
+def istraceback(object):
+    """Return true if the object is a traceback.
+
+    Traceback objects provide these attributes:
+        tb_frame        frame object at this level
+        tb_lasti        index of last attempted instruction in bytecode
+        tb_lineno       current line number in Python source code
+        tb_next         next inner traceback object (called by this level)"""
+    return isinstance(object, types.TracebackType)
+
+def isframe(object):
+    """Return true if the object is a frame object.
+
+    Frame objects provide these attributes:
+        f_back          next outer frame object (this frame's caller)
+        f_builtins      built-in namespace seen by this frame
+        f_code          code object being executed in this frame
+        f_exc_traceback traceback if raised in this frame, or None
+        f_exc_type      exception type if raised in this frame, or None
+        f_exc_value     exception value if raised in this frame, or None
+        f_globals       global namespace seen by this frame
+        f_lasti         index of last attempted instruction in bytecode
+        f_lineno        current line number in Python source code
+        f_locals        local namespace seen by this frame
+        f_restricted    0 or 1 if frame is in restricted execution mode
+        f_trace         tracing function for this frame, or None"""
+    return isinstance(object, types.FrameType)
+
+def iscode(object):
+    """Return true if the object is a code object.
+
+    Code objects provide these attributes:
+        co_argcount     number of arguments (not including * or ** args)
+        co_code         string of raw compiled bytecode
+        co_consts       tuple of constants used in the bytecode
+        co_filename     name of file in which this code object was created
+        co_firstlineno  number of first line in Python source code
+        co_flags        bitmap: 1=optimized | 2=newlocals | 4=*arg | 8=**arg
+        co_lnotab       encoded mapping of line numbers to bytecode indices
+        co_name         name with which this code object was defined
+        co_names        tuple of names of local variables
+        co_nlocals      number of local variables
+        co_stacksize    virtual machine stack space required
+        co_varnames     tuple of names of arguments and local variables"""
+    return isinstance(object, types.CodeType)
+
+def isbuiltin(object):
+    """Return true if the object is a built-in function or method.
+
+    Built-in functions and methods provide these attributes:
+        __doc__         documentation string
+        __name__        original name of this function or method
+        __self__        instance to which a method is bound, or None"""
+    return isinstance(object, types.BuiltinFunctionType)
+
+def isroutine(object):
+    """Return true if the object is any kind of function or method."""
+    return (isbuiltin(object)
+            or isfunction(object)
+            or ismethod(object)
+            or ismethoddescriptor(object))
+
+def getmembers(object, predicate=None):
+    """Return all members of an object as (name, value) pairs sorted by name.
+    Optionally, only return members that satisfy a given predicate."""
+    results = []
+    for key in dir(object):
+        value = getattr(object, key)
+        if not predicate or predicate(value):
+            results.append((key, value))
+    results.sort()
+    return results
+
+def classify_class_attrs(cls):
+    """Return list of attribute-descriptor tuples.
+
+    For each name in dir(cls), the return list contains a 4-tuple
+    with these elements:
+
+        0. The name (a string).
+
+        1. The kind of attribute this is, one of these strings:
+               'class method'    created via classmethod()
+               'static method'   created via staticmethod()
+               'property'        created via property()
+               'method'          any other flavor of method
+               'data'            not a method
+
+        2. The class which defined this attribute (a class).
+
+        3. The object as obtained directly from the defining class's
+           __dict__, not via getattr.  This is especially important for
+           data attributes:  C.data is just a data object, but
+           C.__dict__['data'] may be a data descriptor with additional
+           info, like a __doc__ string.
+    """
+
+    mro = getmro(cls)
+    names = dir(cls)
+    result = []
+    for name in names:
+        # Get the object associated with the name.
+        # Getting an obj from the __dict__ sometimes reveals more than
+        # using getattr.  Static and class methods are dramatic examples.
+        if name in cls.__dict__:
+            obj = cls.__dict__[name]
+        else:
+            obj = getattr(cls, name)
+
+        # Figure out where it was defined.
+        homecls = getattr(obj, "__objclass__", None)
+        if homecls is None:
+            # search the dicts.
+            for base in mro:
+                if name in base.__dict__:
+                    homecls = base
+                    break
+
+        # Get the object again, in order to get it from the defining
+        # __dict__ instead of via getattr (if possible).
+        if homecls is not None and name in homecls.__dict__:
+            obj = homecls.__dict__[name]
+
+        # Also get the object via getattr.
+        obj_via_getattr = getattr(cls, name)
+
+        # Classify the object.
+        if isinstance(obj, staticmethod):
+            kind = "static method"
+        elif isinstance(obj, classmethod):
+            kind = "class method"
+        elif isinstance(obj, property):
+            kind = "property"
+        elif (ismethod(obj_via_getattr) or
+              ismethoddescriptor(obj_via_getattr)):
+            kind = "method"
+        else:
+            kind = "data"
+
+        result.append((name, kind, homecls, obj))
+
+    return result
+
+# ----------------------------------------------------------- class helpers
+def _searchbases(cls, accum):
+    # Simulate the "classic class" search order.
+    if cls in accum:
+        return
+    accum.append(cls)
+    for base in cls.__bases__:
+        _searchbases(base, accum)
+
+def getmro(cls):
+    "Return tuple of base classes (including cls) in method resolution order."
+    if hasattr(cls, "__mro__"):
+        return cls.__mro__
+    else:
+        result = []
+        _searchbases(cls, result)
+        return tuple(result)
+
+# -------------------------------------------------- source code extraction
+def indentsize(line):
+    """Return the indent size, in spaces, at the start of a line of text."""
+    expline = string.expandtabs(line)
+    return len(expline) - len(string.lstrip(expline))
+
+def getdoc(object):
+    """Get the documentation string for an object.
+
+    All tabs are expanded to spaces.  To clean up docstrings that are
+    indented to line up with blocks of code, any whitespace than can be
+    uniformly removed from the second line onwards is removed."""
+    try:
+        doc = object.__doc__
+    except AttributeError:
+        return None
+    if not isinstance(doc, (str, unicode)):
+        return None
+    try:
+        lines = string.split(string.expandtabs(doc), '\n')
+    except UnicodeError:
+        return None
+    else:
+        margin = None
+        for line in lines[1:]:
+            content = len(string.lstrip(line))
+            if not content: continue
+            indent = len(line) - content
+            if margin is None: margin = indent
+            else: margin = min(margin, indent)
+        if margin is not None:
+            for i in range(1, len(lines)): lines[i] = lines[i][margin:]
+        return string.join(lines, '\n')
+
+def getfile(object):
+    """Work out which source or compiled file an object was defined in."""
+    if ismodule(object):
+        if hasattr(object, '__file__'):
+            return object.__file__
+        raise TypeError, 'arg is a built-in module'
+    if isclass(object):
+        object = sys.modules.get(object.__module__)
+        if hasattr(object, '__file__'):
+            return object.__file__
+        raise TypeError, 'arg is a built-in class'
+    if ismethod(object):
+        object = object.im_func
+    if isfunction(object):
+        object = object.func_code
+    if istraceback(object):
+        object = object.tb_frame
+    if isframe(object):
+        object = object.f_code
+    if iscode(object):
+        return object.co_filename
+    raise TypeError, 'arg is not a module, class, method, ' \
+                     'function, traceback, frame, or code object'
+
+def getmoduleinfo(path):
+    """Get the module name, suffix, mode, and module type for a given file."""
+    filename = os.path.basename(path)
+    suffixes = map(lambda (suffix, mode, mtype):
+                   (-len(suffix), suffix, mode, mtype), imp.get_suffixes())
+    suffixes.sort() # try longest suffixes first, in case they overlap
+    for neglen, suffix, mode, mtype in suffixes:
+        if filename[neglen:] == suffix:
+            return filename[:neglen], suffix, mode, mtype
+
+def getmodulename(path):
+    """Return the module name for a given file, or None."""
+    info = getmoduleinfo(path)
+    if info: return info[0]
+
+def getsourcefile(object):
+    """Return the Python source file an object was defined in, if it exists."""
+    filename = getfile(object)
+    if string.lower(filename[-4:]) in ['.pyc', '.pyo']:
+        filename = filename[:-4] + '.py'
+    for suffix, mode, kind in imp.get_suffixes():
+        if 'b' in mode and string.lower(filename[-len(suffix):]) == suffix:
+            # Looks like a binary file.  We want to only return a text file.
+            return None
+    if os.path.exists(filename):
+        return filename
+
+def getabsfile(object):
+    """Return an absolute path to the source or compiled file for an object.
+
+    The idea is for each object to have a unique origin, so this routine
+    normalizes the result as much as possible."""
+    return os.path.normcase(
+        os.path.abspath(getsourcefile(object) or getfile(object)))
+
+modulesbyfile = {}
+
+def getmodule(object):
+    """Return the module an object was defined in, or None if not found."""
+    if ismodule(object):
+        return object
+    if isclass(object):
+        return sys.modules.get(object.__module__)
+    try:
+        file = getabsfile(object)
+    except TypeError:
+        return None
+    if modulesbyfile.has_key(file):
+        return sys.modules[modulesbyfile[file]]
+    for module in sys.modules.values():
+        if hasattr(module, '__file__'):
+            modulesbyfile[getabsfile(module)] = module.__name__
+    if modulesbyfile.has_key(file):
+        return sys.modules[modulesbyfile[file]]
+    main = sys.modules['__main__']
+    if hasattr(main, object.__name__):
+        mainobject = getattr(main, object.__name__)
+        if mainobject is object:
+            return main
+    builtin = sys.modules['__builtin__']
+    if hasattr(builtin, object.__name__):
+        builtinobject = getattr(builtin, object.__name__)
+        if builtinobject is object:
+            return builtin
+
+def findsource(object):
+    """Return the entire source file and starting line number for an object.
+
+    The argument may be a module, class, method, function, traceback, frame,
+    or code object.  The source code is returned as a list of all the lines
+    in the file and the line number indexes a line in that list.  An IOError
+    is raised if the source code cannot be retrieved."""
+    try:
+        file = open(getsourcefile(object))
+    except (TypeError, IOError):
+        raise IOError, 'could not get source code'
+    lines = file.readlines()
+    file.close()
+
+    if ismodule(object):
+        return lines, 0
+
+    if isclass(object):
+        name = object.__name__
+        pat = re.compile(r'^\s*class\s*' + name + r'\b')
+        for i in range(len(lines)):
+            if pat.match(lines[i]): return lines, i
+        else: raise IOError, 'could not find class definition'
+
+    if ismethod(object):
+        object = object.im_func
+    if isfunction(object):
+        object = object.func_code
+    if istraceback(object):
+        object = object.tb_frame
+    if isframe(object):
+        object = object.f_code
+    if iscode(object):
+        if not hasattr(object, 'co_firstlineno'):
+            raise IOError, 'could not find function definition'
+        lnum = object.co_firstlineno - 1
+        pat = re.compile(r'^(\s*def\s)|(.*\slambda(:|\s))')
+        while lnum > 0:
+            if pat.match(lines[lnum]): break
+            lnum = lnum - 1
+        return lines, lnum
+    raise IOError, 'could not find code object'
+
+def getcomments(object):
+    """Get lines of comments immediately preceding an object's source code."""
+    try: lines, lnum = findsource(object)
+    except IOError: return None
+
+    if ismodule(object):
+        # Look for a comment block at the top of the file.
+        start = 0
+        if lines and lines[0][:2] == '#!': start = 1
+        while start < len(lines) and string.strip(lines[start]) in ['', '#']:
+            start = start + 1
+        if start < len(lines) and lines[start][:1] == '#':
+            comments = []
+            end = start
+            while end < len(lines) and lines[end][:1] == '#':
+                comments.append(string.expandtabs(lines[end]))
+                end = end + 1
+            return string.join(comments, '')
+
+    # Look for a preceding block of comments at the same indentation.
+    elif lnum > 0:
+        indent = indentsize(lines[lnum])
+        end = lnum - 1
+        if end >= 0 and string.lstrip(lines[end])[:1] == '#' and \
+            indentsize(lines[end]) == indent:
+            comments = [string.lstrip(string.expandtabs(lines[end]))]
+            if end > 0:
+                end = end - 1
+                comment = string.lstrip(string.expandtabs(lines[end]))
+                while comment[:1] == '#' and indentsize(lines[end]) == indent:
+                    comments[:0] = [comment]
+                    end = end - 1
+                    if end < 0: break
+                    comment = string.lstrip(string.expandtabs(lines[end]))
+            while comments and string.strip(comments[0]) == '#':
+                comments[:1] = []
+            while comments and string.strip(comments[-1]) == '#':
+                comments[-1:] = []
+            return string.join(comments, '')
+
+class ListReader:
+    """Provide a readline() method to return lines from a list of strings."""
+    def __init__(self, lines):
+        self.lines = lines
+        self.index = 0
+
+    def readline(self):
+        i = self.index
+        if i < len(self.lines):
+            self.index = i + 1
+            return self.lines[i]
+        else: return ''
+
+class EndOfBlock(Exception): pass
+
+class BlockFinder:
+    """Provide a tokeneater() method to detect the end of a code block."""
+    def __init__(self):
+        self.indent = 0
+        self.started = 0
+        self.last = 0
+
+    def tokeneater(self, type, token, (srow, scol), (erow, ecol), line):
+        if not self.started:
+            if type == tokenize.NAME: self.started = 1
+        elif type == tokenize.NEWLINE:
+            self.last = srow
+        elif type == tokenize.INDENT:
+            self.indent = self.indent + 1
+        elif type == tokenize.DEDENT:
+            self.indent = self.indent - 1
+            if self.indent == 0: raise EndOfBlock, self.last
+        elif type == tokenize.NAME and scol == 0:
+            raise EndOfBlock, self.last
+
+def getblock(lines):
+    """Extract the block of code at the top of the given list of lines."""
+    try:
+        tokenize.tokenize(ListReader(lines).readline, BlockFinder().tokeneater)
+    except EndOfBlock, eob:
+        return lines[:eob.args[0]]
+    # Fooling the indent/dedent logic implies a one-line definition
+    return lines[:1]
+
+def getsourcelines(object):
+    """Return a list of source lines and starting line number for an object.
+
+    The argument may be a module, class, method, function, traceback, frame,
+    or code object.  The source code is returned as a list of the lines
+    corresponding to the object and the line number indicates where in the
+    original source file the first line of code was found.  An IOError is
+    raised if the source code cannot be retrieved."""
+    lines, lnum = findsource(object)
+
+    if ismodule(object): return lines, 0
+    else: return getblock(lines[lnum:]), lnum + 1
+
+def getsource(object):
+    """Return the text of the source code for an object.
+
+    The argument may be a module, class, method, function, traceback, frame,
+    or code object.  The source code is returned as a single string.  An
+    IOError is raised if the source code cannot be retrieved."""
+    lines, lnum = getsourcelines(object)
+    return string.join(lines, '')
+
+# --------------------------------------------------- class tree extraction
+def walktree(classes, children, parent):
+    """Recursive helper function for getclasstree()."""
+    results = []
+    classes.sort(lambda a, b: cmp(a.__name__, b.__name__))
+    for c in classes:
+        results.append((c, c.__bases__))
+        if children.has_key(c):
+            results.append(walktree(children[c], children, c))
+    return results
+
+def getclasstree(classes, unique=0):
+    """Arrange the given list of classes into a hierarchy of nested lists.
+
+    Where a nested list appears, it contains classes derived from the class
+    whose entry immediately precedes the list.  Each entry is a 2-tuple
+    containing a class and a tuple of its base classes.  If the 'unique'
+    argument is true, exactly one entry appears in the returned structure
+    for each class in the given list.  Otherwise, classes using multiple
+    inheritance and their descendants will appear multiple times."""
+    children = {}
+    roots = []
+    for c in classes:
+        if c.__bases__:
+            for parent in c.__bases__:
+                if not children.has_key(parent):
+                    children[parent] = []
+                children[parent].append(c)
+                if unique and parent in classes: break
+        elif c not in roots:
+            roots.append(c)
+    for parent in children.keys():
+        if parent not in classes:
+            roots.append(parent)
+    return walktree(roots, children, None)
+
+# ------------------------------------------------ argument list extraction
+# These constants are from Python's compile.h.
+CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS = 1, 2, 4, 8
+
+def getargs(co):
+    """Get information about the arguments accepted by a code object.
+
+    Three things are returned: (args, varargs, varkw), where 'args' is
+    a list of argument names (possibly containing nested lists), and
+    'varargs' and 'varkw' are the names of the * and ** arguments or None."""
+    if not iscode(co): raise TypeError, 'arg is not a code object'
+
+    nargs = co.co_argcount
+    names = co.co_varnames
+    args = list(names[:nargs])
+    step = 0
+
+    # The following acrobatics are for anonymous (tuple) arguments.
+    if not sys.platform.startswith('java'):#Jython doesn't have co_code
+        code = co.co_code
+        import dis
+        for i in range(nargs):
+            if args[i][:1] in ['', '.']:
+                stack, remain, count = [], [], []
+                while step < len(code):
+                    op = ord(code[step])
+                    step = step + 1
+                    if op >= dis.HAVE_ARGUMENT:
+                        opname = dis.opname[op]
+                        value = ord(code[step]) + ord(code[step + 1]) * 256
+                        step = step + 2
+                        if opname in ['UNPACK_TUPLE', 'UNPACK_SEQUENCE']:
+                            remain.append(value)
+                            count.append(value)
+                        elif opname == 'STORE_FAST':
+                            stack.append(names[value])
+                            remain[-1] = remain[-1] - 1
+                            while remain[-1] == 0:
+                                remain.pop()
+                                size = count.pop()
+                                stack[-size:] = [stack[-size:]]
+                                if not remain: break
+                                remain[-1] = remain[-1] - 1
+                            if not remain: break
+                args[i] = stack[0]
+
+    varargs = None
+    if co.co_flags & CO_VARARGS:
+        varargs = co.co_varnames[nargs]
+        nargs = nargs + 1
+    varkw = None
+    if co.co_flags & CO_VARKEYWORDS:
+        varkw = co.co_varnames[nargs]
+    return args, varargs, varkw
+
+def getargspec(func):
+    """Get the names and default values of a function's arguments.
+
+    A tuple of four things is returned: (args, varargs, varkw, defaults).
+    'args' is a list of the argument names (it may contain nested lists).
+    'varargs' and 'varkw' are the names of the * and ** arguments or None.
+    'defaults' is an n-tuple of the default values of the last n arguments."""
+    if ismethod(func):
+        func = func.im_func
+    if not isfunction(func): raise TypeError, 'arg is not a Python function'
+    args, varargs, varkw = getargs(func.func_code)
+    return args, varargs, varkw, func.func_defaults
+
+def getargvalues(frame):
+    """Get information about arguments passed into a particular frame.
+
+    A tuple of four things is returned: (args, varargs, varkw, locals).
+    'args' is a list of the argument names (it may contain nested lists).
+    'varargs' and 'varkw' are the names of the * and ** arguments or None.
+    'locals' is the locals dictionary of the given frame."""
+    args, varargs, varkw = getargs(frame.f_code)
+    return args, varargs, varkw, frame.f_locals
+
+def joinseq(seq):
+    if len(seq) == 1:
+        return '(' + seq[0] + ',)'
+    else:
+        return '(' + string.join(seq, ', ') + ')'
+
+def strseq(object, convert, join=joinseq):
+    """Recursively walk a sequence, stringifying each element."""
+    if type(object) in [types.ListType, types.TupleType]:
+        return join(map(lambda o, c=convert, j=join: strseq(o, c, j), object))
+    else:
+        return convert(object)
+
+def formatargspec(args, varargs=None, varkw=None, defaults=None,
+                  formatarg=str,
+                  formatvarargs=lambda name: '*' + name,
+                  formatvarkw=lambda name: '**' + name,
+                  formatvalue=lambda value: '=' + repr(value),
+                  join=joinseq):
+    """Format an argument spec from the 4 values returned by getargspec.
+
+    The first four arguments are (args, varargs, varkw, defaults).  The
+    other four arguments are the corresponding optional formatting functions
+    that are called to turn names and values into strings.  The ninth
+    argument is an optional function to format the sequence of arguments."""
+    specs = []
+    if defaults:
+        firstdefault = len(args) - len(defaults)
+    for i in range(len(args)):
+        spec = strseq(args[i], formatarg, join)
+        if defaults and i >= firstdefault:
+            spec = spec + formatvalue(defaults[i - firstdefault])
+        specs.append(spec)
+    if varargs:
+        specs.append(formatvarargs(varargs))
+    if varkw:
+        specs.append(formatvarkw(varkw))
+    return '(' + string.join(specs, ', ') + ')'
+
+def formatargvalues(args, varargs, varkw, locals,
+                    formatarg=str,
+                    formatvarargs=lambda name: '*' + name,
+                    formatvarkw=lambda name: '**' + name,
+                    formatvalue=lambda value: '=' + repr(value),
+                    join=joinseq):
+    """Format an argument spec from the 4 values returned by getargvalues.
+
+    The first four arguments are (args, varargs, varkw, locals).  The
+    next four arguments are the corresponding optional formatting functions
+    that are called to turn names and values into strings.  The ninth
+    argument is an optional function to format the sequence of arguments."""
+    def convert(name, locals=locals,
+                formatarg=formatarg, formatvalue=formatvalue):
+        return formatarg(name) + formatvalue(locals[name])
+    specs = []
+    for i in range(len(args)):
+        specs.append(strseq(args[i], convert, join))
+    if varargs:
+        specs.append(formatvarargs(varargs) + formatvalue(locals[varargs]))
+    if varkw:
+        specs.append(formatvarkw(varkw) + formatvalue(locals[varkw]))
+    return '(' + string.join(specs, ', ') + ')'
+
+# -------------------------------------------------- stack frame extraction
+def getframeinfo(frame, context=1):
+    """Get information about a frame or traceback object.
+
+    A tuple of five things is returned: the filename, the line number of
+    the current line, the function name, a list of lines of context from
+    the source code, and the index of the current line within that list.
+    The optional second argument specifies the number of lines of context
+    to return, which are centered around the current line."""
+    raise NotImplementedError
+#    if istraceback(frame):
+#        frame = frame.tb_frame
+#    if not isframe(frame):
+#        raise TypeError, 'arg is not a frame or traceback object'
+#
+#    filename = getsourcefile(frame)
+#    lineno = getlineno(frame)
+#    if context > 0:
+#        start = lineno - 1 - context//2
+#        try:
+#            lines, lnum = findsource(frame)
+#        except IOError:
+#            lines = index = None
+#        else:
+#            start = max(start, 1)
+#            start = min(start, len(lines) - context)
+#            lines = lines[start:start+context]
+#            index = lineno - 1 - start
+#    else:
+#        lines = index = None
+#
+#    return (filename, lineno, frame.f_code.co_name, lines, index)
+
+def getlineno(frame):
+    """Get the line number from a frame object, allowing for optimization."""
+    # Written by Marc-Andr Lemburg; revised by Jim Hugunin and Fredrik Lundh.
+    lineno = frame.f_lineno
+    code = frame.f_code
+    if hasattr(code, 'co_lnotab'):
+        table = code.co_lnotab
+        lineno = code.co_firstlineno
+        addr = 0
+        for i in range(0, len(table), 2):
+            addr = addr + ord(table[i])
+            if addr > frame.f_lasti: break
+            lineno = lineno + ord(table[i + 1])
+    return lineno
+
+def getouterframes(frame, context=1):
+    """Get a list of records for a frame and all higher (calling) frames.
+
+    Each record contains a frame object, filename, line number, function
+    name, a list of lines of context, and index within the context."""
+    framelist = []
+    while frame:
+        framelist.append((frame,) + getframeinfo(frame, context))
+        frame = frame.f_back
+    return framelist
+
+def getinnerframes(tb, context=1):
+    """Get a list of records for a traceback's frame and all lower frames.
+
+    Each record contains a frame object, filename, line number, function
+    name, a list of lines of context, and index within the context."""
+    framelist = []
+    while tb:
+        framelist.append((tb.tb_frame,) + getframeinfo(tb, context))
+        tb = tb.tb_next
+    return framelist
+
+def currentframe():
+    """Return the frame object for the caller's stack frame."""
+    try:
+        raise 'catch me'
+    except:
+        return sys.exc_traceback.tb_frame.f_back #@UndefinedVariable
+
+if hasattr(sys, '_getframe'): currentframe = sys._getframe
+
+def stack(context=1):
+    """Return a list of records for the stack above the caller's frame."""
+    return getouterframes(currentframe().f_back, context)
+
+def trace(context=1):
+    """Return a list of records for the stack below the current exception."""
+    return getinnerframes(sys.exc_traceback, context) #@UndefinedVariable
diff --git a/python/helpers/pydev/_pydev_imps/_pydev_select.py b/python/helpers/pydev/_pydev_imps/_pydev_select.py
new file mode 100644
index 0000000..b8dad03
--- /dev/null
+++ b/python/helpers/pydev/_pydev_imps/_pydev_select.py
@@ -0,0 +1 @@
+from select import *
\ No newline at end of file
diff --git a/python/helpers/pydev/_pydev_imps/_pydev_socket.py b/python/helpers/pydev/_pydev_imps/_pydev_socket.py
new file mode 100644
index 0000000..9e96e80
--- /dev/null
+++ b/python/helpers/pydev/_pydev_imps/_pydev_socket.py
@@ -0,0 +1 @@
+from socket import *
\ No newline at end of file
diff --git a/python/helpers/pydev/_pydev_imps/_pydev_thread.py b/python/helpers/pydev/_pydev_imps/_pydev_thread.py
new file mode 100644
index 0000000..4d2fd5d
--- /dev/null
+++ b/python/helpers/pydev/_pydev_imps/_pydev_thread.py
@@ -0,0 +1,4 @@
+try:
+    from thread import *
+except:
+    from _thread import * #Py3k
diff --git a/python/helpers/pydev/_pydev_imps/_pydev_time.py b/python/helpers/pydev/_pydev_imps/_pydev_time.py
new file mode 100644
index 0000000..72705db
--- /dev/null
+++ b/python/helpers/pydev/_pydev_imps/_pydev_time.py
@@ -0,0 +1 @@
+from time import *
diff --git a/python/helpers/pydev/_pydev_imps/_pydev_xmlrpclib.py b/python/helpers/pydev/_pydev_imps/_pydev_xmlrpclib.py
new file mode 100644
index 0000000..5f6e2b7
--- /dev/null
+++ b/python/helpers/pydev/_pydev_imps/_pydev_xmlrpclib.py
@@ -0,0 +1,1493 @@
+#Just a copy of the version in python 2.5 to be used if it's not available in jython 2.1
+import sys
+
+#
+# XML-RPC CLIENT LIBRARY
+#
+# an XML-RPC client interface for Python.
+#
+# the marshalling and response parser code can also be used to
+# implement XML-RPC servers.
+#
+# Notes:
+# this version is designed to work with Python 2.1 or newer.
+#
+# History:
+# 1999-01-14 fl  Created
+# 1999-01-15 fl  Changed dateTime to use localtime
+# 1999-01-16 fl  Added Binary/base64 element, default to RPC2 service
+# 1999-01-19 fl  Fixed array data element (from Skip Montanaro)
+# 1999-01-21 fl  Fixed dateTime constructor, etc.
+# 1999-02-02 fl  Added fault handling, handle empty sequences, etc.
+# 1999-02-10 fl  Fixed problem with empty responses (from Skip Montanaro)
+# 1999-06-20 fl  Speed improvements, pluggable parsers/transports (0.9.8)
+# 2000-11-28 fl  Changed boolean to check the truth value of its argument
+# 2001-02-24 fl  Added encoding/Unicode/SafeTransport patches
+# 2001-02-26 fl  Added compare support to wrappers (0.9.9/1.0b1)
+# 2001-03-28 fl  Make sure response tuple is a singleton
+# 2001-03-29 fl  Don't require empty params element (from Nicholas Riley)
+# 2001-06-10 fl  Folded in _xmlrpclib accelerator support (1.0b2)
+# 2001-08-20 fl  Base xmlrpclib.Error on built-in Exception (from Paul Prescod)
+# 2001-09-03 fl  Allow Transport subclass to override getparser
+# 2001-09-10 fl  Lazy import of urllib, cgi, xmllib (20x import speedup)
+# 2001-10-01 fl  Remove containers from memo cache when done with them
+# 2001-10-01 fl  Use faster escape method (80% dumps speedup)
+# 2001-10-02 fl  More dumps microtuning
+# 2001-10-04 fl  Make sure import expat gets a parser (from Guido van Rossum)
+# 2001-10-10 sm  Allow long ints to be passed as ints if they don't overflow
+# 2001-10-17 sm  Test for int and long overflow (allows use on 64-bit systems)
+# 2001-11-12 fl  Use repr() to marshal doubles (from Paul Felix)
+# 2002-03-17 fl  Avoid buffered read when possible (from James Rucker)
+# 2002-04-07 fl  Added pythondoc comments
+# 2002-04-16 fl  Added __str__ methods to datetime/binary wrappers
+# 2002-05-15 fl  Added error constants (from Andrew Kuchling)
+# 2002-06-27 fl  Merged with Python CVS version
+# 2002-10-22 fl  Added basic authentication (based on code from Phillip Eby)
+# 2003-01-22 sm  Add support for the bool type
+# 2003-02-27 gvr Remove apply calls
+# 2003-04-24 sm  Use cStringIO if available
+# 2003-04-25 ak  Add support for nil
+# 2003-06-15 gn  Add support for time.struct_time
+# 2003-07-12 gp  Correct marshalling of Faults
+# 2003-10-31 mvl Add multicall support
+# 2004-08-20 mvl Bump minimum supported Python version to 2.1
+#
+# Copyright (c) 1999-2002 by Secret Labs AB.
+# Copyright (c) 1999-2002 by Fredrik Lundh.
+#
+# info@pythonware.com
+# http://www.pythonware.com
+#
+# --------------------------------------------------------------------
+# The XML-RPC client interface is
+#
+# Copyright (c) 1999-2002 by Secret Labs AB
+# Copyright (c) 1999-2002 by Fredrik Lundh
+#
+# By obtaining, using, and/or copying this software and/or its
+# associated documentation, you agree that you have read, understood,
+# and will comply with the following terms and conditions:
+#
+# Permission to use, copy, modify, and distribute this software and
+# its associated documentation for any purpose and without fee is
+# hereby granted, provided that the above copyright notice appears in
+# all copies, and that both that copyright notice and this permission
+# notice appear in supporting documentation, and that the name of
+# Secret Labs AB or the author not be used in advertising or publicity
+# pertaining to distribution of the software without specific, written
+# prior permission.
+#
+# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
+# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
+# ABILITY AND FITNESS.  IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
+# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+# --------------------------------------------------------------------
+
+#
+# things to look into some day:
+
+# TODO: sort out True/False/boolean issues for Python 2.3
+
+"""
+An XML-RPC client interface for Python.
+
+The marshalling and response parser code can also be used to
+implement XML-RPC servers.
+
+Exported exceptions:
+
+  Error          Base class for client errors
+  ProtocolError  Indicates an HTTP protocol error
+  ResponseError  Indicates a broken response package
+  Fault          Indicates an XML-RPC fault package
+
+Exported classes:
+
+  ServerProxy    Represents a logical connection to an XML-RPC server
+
+  MultiCall      Executor of boxcared xmlrpc requests
+  Boolean        boolean wrapper to generate a "boolean" XML-RPC value
+  DateTime       dateTime wrapper for an ISO 8601 string or time tuple or
+                 localtime integer value to generate a "dateTime.iso8601"
+                 XML-RPC value
+  Binary         binary data wrapper
+
+  SlowParser     Slow but safe standard parser (based on xmllib)
+  Marshaller     Generate an XML-RPC params chunk from a Python data structure
+  Unmarshaller   Unmarshal an XML-RPC response from incoming XML event message
+  Transport      Handles an HTTP transaction to an XML-RPC server
+  SafeTransport  Handles an HTTPS transaction to an XML-RPC server
+
+Exported constants:
+
+  True
+  False
+
+Exported functions:
+
+  boolean        Convert any Python value to an XML-RPC boolean
+  getparser      Create instance of the fastest available parser & attach
+                 to an unmarshalling object
+  dumps          Convert an argument tuple or a Fault instance to an XML-RPC
+                 request (or response, if the methodresponse option is used).
+  loads          Convert an XML-RPC packet to unmarshalled data plus a method
+                 name (None if not present).
+"""
+
+import re, string, time, operator
+
+from types import *
+
+# --------------------------------------------------------------------
+# Internal stuff
+
+try:
+    unicode
+except NameError:
+    unicode = None # unicode support not available
+
+try:
+    import datetime
+except ImportError:
+    datetime = None
+
+try:
+    _bool_is_builtin = False.__class__.__name__ == "bool"
+except (NameError, AttributeError):
+    _bool_is_builtin = 0
+
+def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search):
+    # decode non-ascii string (if possible)
+    if unicode and encoding and is8bit(data):
+        data = unicode(data, encoding)
+    return data
+
+def escape(s, replace=string.replace):
+    s = replace(s, "&", "&amp;")
+    s = replace(s, "<", "&lt;")
+    return replace(s, ">", "&gt;",)
+
+if unicode:
+    def _stringify(string):
+        # convert to 7-bit ascii if possible
+        try:
+            return string.encode("ascii")
+        except UnicodeError:
+            return string
+else:
+    def _stringify(string):
+        return string
+
+__version__ = "1.0.1"
+
+# xmlrpc integer limits
+try:
+    long 
+except NameError:
+    long = int
+MAXINT = long(2) ** 31 - 1
+MININT = long(-2) ** 31
+
+# --------------------------------------------------------------------
+# Error constants (from Dan Libby's specification at
+# http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)
+
+# Ranges of errors
+PARSE_ERROR = -32700
+SERVER_ERROR = -32600
+APPLICATION_ERROR = -32500
+SYSTEM_ERROR = -32400
+TRANSPORT_ERROR = -32300
+
+# Specific errors
+NOT_WELLFORMED_ERROR = -32700
+UNSUPPORTED_ENCODING = -32701
+INVALID_ENCODING_CHAR = -32702
+INVALID_XMLRPC = -32600
+METHOD_NOT_FOUND = -32601
+INVALID_METHOD_PARAMS = -32602
+INTERNAL_ERROR = -32603
+
+# --------------------------------------------------------------------
+# Exceptions
+
+##
+# Base class for all kinds of client-side errors.
+
+class Error(Exception):
+    """Base class for client errors."""
+    def __str__(self):
+        return repr(self)
+
+##
+# Indicates an HTTP-level protocol error.  This is raised by the HTTP
+# transport layer, if the server returns an error code other than 200
+# (OK).
+#
+# @param url The target URL.
+# @param errcode The HTTP error code.
+# @param errmsg The HTTP error message.
+# @param headers The HTTP header dictionary.
+
+class ProtocolError(Error):
+    """Indicates an HTTP protocol error."""
+    def __init__(self, url, errcode, errmsg, headers):
+        Error.__init__(self)
+        self.url = url
+        self.errcode = errcode
+        self.errmsg = errmsg
+        self.headers = headers
+    def __repr__(self):
+        return (
+            "<ProtocolError for %s: %s %s>" % 
+            (self.url, self.errcode, self.errmsg)
+            )
+
+##
+# Indicates a broken XML-RPC response package.  This exception is
+# raised by the unmarshalling layer, if the XML-RPC response is
+# malformed.
+
+class ResponseError(Error):
+    """Indicates a broken response package."""
+    pass
+
+##
+# Indicates an XML-RPC fault response package.  This exception is
+# raised by the unmarshalling layer, if the XML-RPC response contains
+# a fault string.  This exception can also used as a class, to
+# generate a fault XML-RPC message.
+#
+# @param faultCode The XML-RPC fault code.
+# @param faultString The XML-RPC fault string.
+
+class Fault(Error):
+    """Indicates an XML-RPC fault package."""
+    def __init__(self, faultCode, faultString, **extra):
+        Error.__init__(self)
+        self.faultCode = faultCode
+        self.faultString = faultString
+    def __repr__(self):
+        return (
+            "<Fault %s: %s>" % 
+            (self.faultCode, repr(self.faultString))
+            )
+
+# --------------------------------------------------------------------
+# Special values
+
+##
+# Wrapper for XML-RPC boolean values.  Use the xmlrpclib.True and
+# xmlrpclib.False constants, or the xmlrpclib.boolean() function, to
+# generate boolean XML-RPC values.
+#
+# @param value A boolean value.  Any true value is interpreted as True,
+#              all other values are interpreted as False.
+
+if _bool_is_builtin:
+    boolean = Boolean = bool #@UndefinedVariable
+    # to avoid breaking code which references xmlrpclib.{True,False}
+    True, False = True, False
+else:
+    class Boolean:
+        """Boolean-value wrapper.
+
+        Use True or False to generate a "boolean" XML-RPC value.
+        """
+
+        def __init__(self, value=0):
+            self.value = operator.truth(value)
+
+        def encode(self, out):
+            out.write("<value><boolean>%d</boolean></value>\n" % self.value)
+
+        def __cmp__(self, other):
+            if isinstance(other, Boolean):
+                other = other.value
+            return cmp(self.value, other)
+
+        def __repr__(self):
+            if self.value:
+                return "<Boolean True at %x>" % id(self)
+            else:
+                return "<Boolean False at %x>" % id(self)
+
+        def __int__(self):
+            return self.value
+
+        def __nonzero__(self):
+            return self.value
+
+    True, False = Boolean(1), Boolean(0)
+
+    ##
+    # Map true or false value to XML-RPC boolean values.
+    #
+    # @def boolean(value)
+    # @param value A boolean value.  Any true value is mapped to True,
+    #              all other values are mapped to False.
+    # @return xmlrpclib.True or xmlrpclib.False.
+    # @see Boolean
+    # @see True
+    # @see False
+
+    def boolean(value, _truefalse=(False, True)):
+        """Convert any Python value to XML-RPC 'boolean'."""
+        return _truefalse[operator.truth(value)]
+
+##
+# Wrapper for XML-RPC DateTime values.  This converts a time value to
+# the format used by XML-RPC.
+# <p>
+# The value can be given as a string in the format
+# "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by
+# time.localtime()), or an integer value (as returned by time.time()).
+# The wrapper uses time.localtime() to convert an integer to a time
+# tuple.
+#
+# @param value The time, given as an ISO 8601 string, a time
+#              tuple, or a integer time value.
+
+class DateTime:
+    """DateTime wrapper for an ISO 8601 string or time tuple or
+    localtime integer value to generate 'dateTime.iso8601' XML-RPC
+    value.
+    """
+
+    def __init__(self, value=0):
+        if not isinstance(value, StringType):
+            if datetime and isinstance(value, datetime.datetime):
+                self.value = value.strftime("%Y%m%dT%H:%M:%S")
+                return
+            if datetime and isinstance(value, datetime.date):
+                self.value = value.strftime("%Y%m%dT%H:%M:%S")
+                return
+            if datetime and isinstance(value, datetime.time):
+                today = datetime.datetime.now().strftime("%Y%m%d")
+                self.value = value.strftime(today + "T%H:%M:%S")
+                return
+            if not isinstance(value, (TupleType, time.struct_time)): #@UndefinedVariable
+                if value == 0:
+                    value = time.time()
+                value = time.localtime(value)
+            value = time.strftime("%Y%m%dT%H:%M:%S", value)
+        self.value = value
+
+    def __cmp__(self, other):
+        if isinstance(other, DateTime):
+            other = other.value
+        return cmp(self.value, other)
+
+    ##
+    # Get date/time value.
+    #
+    # @return Date/time value, as an ISO 8601 string.
+
+    def __str__(self):
+        return self.value
+
+    def __repr__(self):
+        return "<DateTime %s at %x>" % (repr(self.value), id(self))
+
+    def decode(self, data):
+        data = str(data)
+        self.value = string.strip(data)
+
+    def encode(self, out):
+        out.write("<value><dateTime.iso8601>")
+        out.write(self.value)
+        out.write("</dateTime.iso8601></value>\n")
+
+def _datetime(data):
+    # decode xml element contents into a DateTime structure.
+    value = DateTime()
+    value.decode(data)
+    return value
+
+def _datetime_type(data):
+    t = time.strptime(data, "%Y%m%dT%H:%M:%S") #@UndefinedVariable
+    return datetime.datetime(*tuple(t)[:6])
+
+##
+# Wrapper for binary data.  This can be used to transport any kind
+# of binary data over XML-RPC, using BASE64 encoding.
+#
+# @param data An 8-bit string containing arbitrary data.
+
+import base64
+try:
+    import cStringIO as StringIO
+except ImportError:
+    import StringIO
+
+class Binary:
+    """Wrapper for binary data."""
+
+    def __init__(self, data=None):
+        self.data = data
+
+    ##
+    # Get buffer contents.
+    #
+    # @return Buffer contents, as an 8-bit string.
+
+    def __str__(self):
+        return self.data or ""
+
+    def __cmp__(self, other):
+        if isinstance(other, Binary):
+            other = other.data
+        return cmp(self.data, other)
+
+    def decode(self, data):
+        self.data = base64.decodestring(data)
+
+    def encode(self, out):
+        out.write("<value><base64>\n")
+        base64.encode(StringIO.StringIO(self.data), out)
+        out.write("</base64></value>\n")
+
+def _binary(data):
+    # decode xml element contents into a Binary structure
+    value = Binary()
+    value.decode(data)
+    return value
+
+WRAPPERS = (DateTime, Binary)
+if not _bool_is_builtin:
+    WRAPPERS = WRAPPERS + (Boolean,)
+
+# --------------------------------------------------------------------
+# XML parsers
+
+try:
+    # optional xmlrpclib accelerator
+    import _xmlrpclib #@UnresolvedImport
+    FastParser = _xmlrpclib.Parser
+    FastUnmarshaller = _xmlrpclib.Unmarshaller
+except (AttributeError, ImportError):
+    FastParser = FastUnmarshaller = None
+
+try:
+    import _xmlrpclib #@UnresolvedImport
+    FastMarshaller = _xmlrpclib.Marshaller
+except (AttributeError, ImportError):
+    FastMarshaller = None
+
+#
+# the SGMLOP parser is about 15x faster than Python's builtin
+# XML parser.  SGMLOP sources can be downloaded from:
+#
+#     http://www.pythonware.com/products/xml/sgmlop.htm
+#
+
+try:
+    import sgmlop
+    if not hasattr(sgmlop, "XMLParser"):
+        raise ImportError()
+except ImportError:
+    SgmlopParser = None # sgmlop accelerator not available
+else:
+    class SgmlopParser:
+        def __init__(self, target):
+
+            # setup callbacks
+            self.finish_starttag = target.start
+            self.finish_endtag = target.end
+            self.handle_data = target.data
+            self.handle_xml = target.xml
+
+            # activate parser
+            self.parser = sgmlop.XMLParser()
+            self.parser.register(self)
+            self.feed = self.parser.feed
+            self.entity = {
+                "amp": "&", "gt": ">", "lt": "<",
+                "apos": "'", "quot": '"'
+                }
+
+        def close(self):
+            try:
+                self.parser.close()
+            finally:
+                self.parser = self.feed = None # nuke circular reference
+
+        def handle_proc(self, tag, attr):
+            m = re.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr) #@UndefinedVariable
+            if m:
+                self.handle_xml(m.group(1), 1)
+
+        def handle_entityref(self, entity):
+            # <string> entity
+            try:
+                self.handle_data(self.entity[entity])
+            except KeyError:
+                self.handle_data("&%s;" % entity)
+
+try:
+    from xml.parsers import expat
+    if not hasattr(expat, "ParserCreate"):
+        raise ImportError()
+except ImportError:
+    ExpatParser = None # expat not available
+else:
+    class ExpatParser:
+        # fast expat parser for Python 2.0 and later.  this is about
+        # 50% slower than sgmlop, on roundtrip testing
+        def __init__(self, target):
+            self._parser = parser = expat.ParserCreate(None, None)
+            self._target = target
+            parser.StartElementHandler = target.start
+            parser.EndElementHandler = target.end
+            parser.CharacterDataHandler = target.data
+            encoding = None
+            if not parser.returns_unicode:
+                encoding = "utf-8"
+            target.xml(encoding, None)
+
+        def feed(self, data):
+            self._parser.Parse(data, 0)
+
+        def close(self):
+            self._parser.Parse("", 1) # end of data
+            del self._target, self._parser # get rid of circular references
+
+class SlowParser:
+    """Default XML parser (based on xmllib.XMLParser)."""
+    # this is about 10 times slower than sgmlop, on roundtrip
+    # testing.
+    def __init__(self, target):
+        import xmllib # lazy subclassing (!)
+        if xmllib.XMLParser not in SlowParser.__bases__:
+            SlowParser.__bases__ = (xmllib.XMLParser,)
+        self.handle_xml = target.xml
+        self.unknown_starttag = target.start
+        self.handle_data = target.data
+        self.handle_cdata = target.data
+        self.unknown_endtag = target.end
+        try:
+            xmllib.XMLParser.__init__(self, accept_utf8=1)
+        except TypeError:
+            xmllib.XMLParser.__init__(self) # pre-2.0
+
+# --------------------------------------------------------------------
+# XML-RPC marshalling and unmarshalling code
+
+##
+# XML-RPC marshaller.
+#
+# @param encoding Default encoding for 8-bit strings.  The default
+#     value is None (interpreted as UTF-8).
+# @see dumps
+
+class Marshaller:
+    """Generate an XML-RPC params chunk from a Python data structure.
+
+    Create a Marshaller instance for each set of parameters, and use
+    the "dumps" method to convert your data (represented as a tuple)
+    to an XML-RPC params chunk.  To write a fault response, pass a
+    Fault instance instead.  You may prefer to use the "dumps" module
+    function for this purpose.
+    """
+
+    # by the way, if you don't understand what's going on in here,
+    # that's perfectly ok.
+
+    def __init__(self, encoding=None, allow_none=0):
+        self.memo = {}
+        self.data = None
+        self.encoding = encoding
+        self.allow_none = allow_none
+
+    dispatch = {}
+
+    def dumps(self, values):
+        out = []
+        write = out.append
+        dump = self.__dump
+        if isinstance(values, Fault):
+            # fault instance
+            write("<fault>\n")
+            dump({'faultCode': values.faultCode,
+                  'faultString': values.faultString},
+                 write)
+            write("</fault>\n")
+        else:
+            # parameter block
+            # FIXME: the xml-rpc specification allows us to leave out
+            # the entire <params> block if there are no parameters.
+            # however, changing this may break older code (including
+            # old versions of xmlrpclib.py), so this is better left as
+            # is for now.  See @XMLRPC3 for more information. /F
+            write("<params>\n")
+            for v in values:
+                write("<param>\n")
+                dump(v, write)
+                write("</param>\n")
+            write("</params>\n")
+        result = string.join(out, "")
+        return result
+
+    def __dump(self, value, write):
+        try:
+            f = self.dispatch[type(value)]
+        except KeyError:
+            raise TypeError("cannot marshal %s objects" % type(value))
+        else:
+            f(self, value, write)
+
+    def dump_nil (self, value, write):
+        if not self.allow_none:
+            raise TypeError("cannot marshal None unless allow_none is enabled")
+        write("<value><nil/></value>")
+    dispatch[NoneType] = dump_nil
+
+    def dump_int(self, value, write):
+        # in case ints are > 32 bits
+        if value > MAXINT or value < MININT:
+            raise OverflowError("int exceeds XML-RPC limits")
+        write("<value><int>")
+        write(str(value))
+        write("</int></value>\n")
+    dispatch[IntType] = dump_int
+
+    if _bool_is_builtin:
+        def dump_bool(self, value, write):
+            write("<value><boolean>")
+            write(value and "1" or "0")
+            write("</boolean></value>\n")
+        dispatch[bool] = dump_bool #@UndefinedVariable
+
+    def dump_long(self, value, write):
+        if value > MAXINT or value < MININT:
+            raise OverflowError("long int exceeds XML-RPC limits")
+        write("<value><int>")
+        write(str(int(value)))
+        write("</int></value>\n")
+    dispatch[LongType] = dump_long
+
+    def dump_double(self, value, write):
+        write("<value><double>")
+        write(repr(value))
+        write("</double></value>\n")
+    dispatch[FloatType] = dump_double
+
+    def dump_string(self, value, write, escape=escape):
+        write("<value><string>")
+        write(escape(value))
+        write("</string></value>\n")
+    dispatch[StringType] = dump_string
+
+    if unicode:
+        def dump_unicode(self, value, write, escape=escape):
+            value = value.encode(self.encoding)
+            write("<value><string>")
+            write(escape(value))
+            write("</string></value>\n")
+        dispatch[UnicodeType] = dump_unicode
+
+    def dump_array(self, value, write):
+        i = id(value)
+        if self.memo.has_key(i):
+            raise TypeError("cannot marshal recursive sequences")
+        self.memo[i] = None
+        dump = self.__dump
+        write("<value><array><data>\n")
+        for v in value:
+            dump(v, write)
+        write("</data></array></value>\n")
+        del self.memo[i]
+    dispatch[TupleType] = dump_array
+    dispatch[ListType] = dump_array
+
+    def dump_struct(self, value, write, escape=escape):
+        i = id(value)
+        if self.memo.has_key(i):
+            raise TypeError("cannot marshal recursive dictionaries")
+        self.memo[i] = None
+        dump = self.__dump
+        write("<value><struct>\n")
+        for k, v in value.items():
+            write("<member>\n")
+            if type(k) is not StringType:
+                if unicode and type(k) is UnicodeType:
+                    k = k.encode(self.encoding)
+                else:
+                    raise TypeError("dictionary key must be string")
+            write("<name>%s</name>\n" % escape(k))
+            dump(v, write)
+            write("</member>\n")
+        write("</struct></value>\n")
+        del self.memo[i]
+    dispatch[DictType] = dump_struct
+
+    if datetime:
+        def dump_datetime(self, value, write):
+            write("<value><dateTime.iso8601>")
+            write(value.strftime("%Y%m%dT%H:%M:%S"))
+            write("</dateTime.iso8601></value>\n")
+        dispatch[datetime.datetime] = dump_datetime
+
+        def dump_date(self, value, write):
+            write("<value><dateTime.iso8601>")
+            write(value.strftime("%Y%m%dT00:00:00"))
+            write("</dateTime.iso8601></value>\n")
+        dispatch[datetime.date] = dump_date
+
+        def dump_time(self, value, write):
+            write("<value><dateTime.iso8601>")
+            write(datetime.datetime.now().date().strftime("%Y%m%dT"))
+            write(value.strftime("%H:%M:%S"))
+            write("</dateTime.iso8601></value>\n")
+        dispatch[datetime.time] = dump_time
+
+    def dump_instance(self, value, write):
+        # check for special wrappers
+        if value.__class__ in WRAPPERS:
+            self.write = write
+            value.encode(self)
+            del self.write
+        else:
+            # store instance attributes as a struct (really?)
+            self.dump_struct(value.__dict__, write)
+    dispatch[InstanceType] = dump_instance
+
+##
+# XML-RPC unmarshaller.
+#
+# @see loads
+
+class Unmarshaller:
+    """Unmarshal an XML-RPC response, based on incoming XML event
+    messages (start, data, end).  Call close() to get the resulting
+    data structure.
+
+    Note that this reader is fairly tolerant, and gladly accepts bogus
+    XML-RPC data without complaining (but not bogus XML).
+    """
+
+    # and again, if you don't understand what's going on in here,
+    # that's perfectly ok.
+
+    def __init__(self, use_datetime=0):
+        self._type = None
+        self._stack = []
+        self._marks = []
+        self._data = []
+        self._methodname = None
+        self._encoding = "utf-8"
+        self.append = self._stack.append
+        self._use_datetime = use_datetime
+        if use_datetime and not datetime:
+            raise ValueError("the datetime module is not available")
+
+    def close(self):
+        # return response tuple and target method
+        if self._type is None or self._marks:
+            raise ResponseError()
+        if self._type == "fault":
+            raise Fault(**self._stack[0])
+        return tuple(self._stack)
+
+    def getmethodname(self):
+        return self._methodname
+
+    #
+    # event handlers
+
+    def xml(self, encoding, standalone):
+        self._encoding = encoding
+        # FIXME: assert standalone == 1 ???
+
+    def start(self, tag, attrs):
+        # prepare to handle this element
+        if tag == "array" or tag == "struct":
+            self._marks.append(len(self._stack))
+        self._data = []
+        self._value = (tag == "value")
+
+    def data(self, text):
+        self._data.append(text)
+
+    def end(self, tag, join=string.join):
+        # call the appropriate end tag handler
+        try:
+            f = self.dispatch[tag]
+        except KeyError:
+            pass # unknown tag ?
+        else:
+            return f(self, join(self._data, ""))
+
+    #
+    # accelerator support
+
+    def end_dispatch(self, tag, data):
+        # dispatch data
+        try:
+            f = self.dispatch[tag]
+        except KeyError:
+            pass # unknown tag ?
+        else:
+            return f(self, data)
+
+    #
+    # element decoders
+
+    dispatch = {}
+
+    def end_nil (self, data):
+        self.append(None)
+        self._value = 0
+    dispatch["nil"] = end_nil
+
+    def end_boolean(self, data):
+        if data == "0":
+            self.append(False)
+        elif data == "1":
+            self.append(True)
+        else:
+            raise TypeError("bad boolean value")
+        self._value = 0
+    dispatch["boolean"] = end_boolean
+
+    def end_int(self, data):
+        self.append(int(data))
+        self._value = 0
+    dispatch["i4"] = end_int
+    dispatch["int"] = end_int
+
+    def end_double(self, data):
+        self.append(float(data))
+        self._value = 0
+    dispatch["double"] = end_double
+
+    def end_string(self, data):
+        if self._encoding:
+            data = _decode(data, self._encoding)
+        self.append(_stringify(data))
+        self._value = 0
+    dispatch["string"] = end_string
+    dispatch["name"] = end_string # struct keys are always strings
+
+    def end_array(self, data):
+        mark = self._marks.pop()
+        # map arrays to Python lists
+        self._stack[mark:] = [self._stack[mark:]]
+        self._value = 0
+    dispatch["array"] = end_array
+
+    def end_struct(self, data):
+        mark = self._marks.pop()
+        # map structs to Python dictionaries
+        dict = {}
+        items = self._stack[mark:]
+        for i in range(0, len(items), 2):
+            dict[_stringify(items[i])] = items[i + 1]
+        self._stack[mark:] = [dict]
+        self._value = 0
+    dispatch["struct"] = end_struct
+
+    def end_base64(self, data):
+        value = Binary()
+        value.decode(data)
+        self.append(value)
+        self._value = 0
+    dispatch["base64"] = end_base64
+
+    def end_dateTime(self, data):
+        value = DateTime()
+        value.decode(data)
+        if self._use_datetime:
+            value = _datetime_type(data)
+        self.append(value)
+    dispatch["dateTime.iso8601"] = end_dateTime
+
+    def end_value(self, data):
+        # if we stumble upon a value element with no internal
+        # elements, treat it as a string element
+        if self._value:
+            self.end_string(data)
+    dispatch["value"] = end_value
+
+    def end_params(self, data):
+        self._type = "params"
+    dispatch["params"] = end_params
+
+    def end_fault(self, data):
+        self._type = "fault"
+    dispatch["fault"] = end_fault
+
+    def end_methodName(self, data):
+        if self._encoding:
+            data = _decode(data, self._encoding)
+        self._methodname = data
+        self._type = "methodName" # no params
+    dispatch["methodName"] = end_methodName
+
+## Multicall support
+#
+
+class _MultiCallMethod:
+    # some lesser magic to store calls made to a MultiCall object
+    # for batch execution
+    def __init__(self, call_list, name):
+        self.__call_list = call_list
+        self.__name = name
+    def __getattr__(self, name):
+        return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name))
+    def __call__(self, *args):
+        self.__call_list.append((self.__name, args))
+
+class MultiCallIterator:
+    """Iterates over the results of a multicall. Exceptions are
+    thrown in response to xmlrpc faults."""
+
+    def __init__(self, results):
+        self.results = results
+
+    def __getitem__(self, i):
+        item = self.results[i]
+        if type(item) == type({}):
+            raise Fault(item['faultCode'], item['faultString'])
+        elif type(item) == type([]):
+            return item[0]
+        else:
+            raise ValueError("unexpected type in multicall result")
+
+class MultiCall:
+    """server -> a object used to boxcar method calls
+
+    server should be a ServerProxy object.
+
+    Methods can be added to the MultiCall using normal
+    method call syntax e.g.:
+
+    multicall = MultiCall(server_proxy)
+    multicall.add(2,3)
+    multicall.get_address("Guido")
+
+    To execute the multicall, call the MultiCall object e.g.:
+
+    add_result, address = multicall()
+    """
+
+    def __init__(self, server):
+        self.__server = server
+        self.__call_list = []
+
+    def __repr__(self):
+        return "<MultiCall at %x>" % id(self)
+
+    __str__ = __repr__
+
+    def __getattr__(self, name):
+        return _MultiCallMethod(self.__call_list, name)
+
+    def __call__(self):
+        marshalled_list = []
+        for name, args in self.__call_list:
+            marshalled_list.append({'methodName' : name, 'params' : args})
+
+        return MultiCallIterator(self.__server.system.multicall(marshalled_list))
+
+# --------------------------------------------------------------------
+# convenience functions
+
+##
+# Create a parser object, and connect it to an unmarshalling instance.
+# This function picks the fastest available XML parser.
+#
+# return A (parser, unmarshaller) tuple.
+
+def getparser(use_datetime=0):
+    """getparser() -> parser, unmarshaller
+
+    Create an instance of the fastest available parser, and attach it
+    to an unmarshalling object.  Return both objects.
+    """
+    if use_datetime and not datetime:
+        raise ValueError("the datetime module is not available")
+    if FastParser and FastUnmarshaller:
+        if use_datetime:
+            mkdatetime = _datetime_type
+        else:
+            mkdatetime = _datetime
+        target = FastUnmarshaller(True, False, _binary, mkdatetime, Fault)
+        parser = FastParser(target)
+    else:
+        target = Unmarshaller(use_datetime=use_datetime)
+        if FastParser:
+            parser = FastParser(target)
+        elif SgmlopParser:
+            parser = SgmlopParser(target)
+        elif ExpatParser:
+            parser = ExpatParser(target)
+        else:
+            parser = SlowParser(target)
+    return parser, target
+
+##
+# Convert a Python tuple or a Fault instance to an XML-RPC packet.
+#
+# @def dumps(params, **options)
+# @param params A tuple or Fault instance.
+# @keyparam methodname If given, create a methodCall request for
+#     this method name.
+# @keyparam methodresponse If given, create a methodResponse packet.
+#     If used with a tuple, the tuple must be a singleton (that is,
+#     it must contain exactly one element).
+# @keyparam encoding The packet encoding.
+# @return A string containing marshalled data.
+
+def dumps(params, methodname=None, methodresponse=None, encoding=None,
+          allow_none=0):
+    """data [,options] -> marshalled data
+
+    Convert an argument tuple or a Fault instance to an XML-RPC
+    request (or response, if the methodresponse option is used).
+
+    In addition to the data object, the following options can be given
+    as keyword arguments:
+
+        methodname: the method name for a methodCall packet
+
+        methodresponse: true to create a methodResponse packet.
+        If this option is used with a tuple, the tuple must be
+        a singleton (i.e. it can contain only one element).
+
+        encoding: the packet encoding (default is UTF-8)
+
+    All 8-bit strings in the data structure are assumed to use the
+    packet encoding.  Unicode strings are automatically converted,
+    where necessary.
+    """
+
+    assert isinstance(params, TupleType) or isinstance(params, Fault), \
+           "argument must be tuple or Fault instance"
+
+    if isinstance(params, Fault):
+        methodresponse = 1
+    elif methodresponse and isinstance(params, TupleType):
+        assert len(params) == 1, "response tuple must be a singleton"
+
+    if not encoding:
+        encoding = "utf-8"
+
+    if FastMarshaller:
+        m = FastMarshaller(encoding)
+    else:
+        m = Marshaller(encoding, allow_none)
+
+    data = m.dumps(params)
+
+    if encoding != "utf-8":
+        xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)
+    else:
+        xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
+
+    # standard XML-RPC wrappings
+    if methodname:
+        # a method call
+        if not isinstance(methodname, StringType):
+            methodname = methodname.encode(encoding)
+        data = (
+            xmlheader,
+            "<methodCall>\n"
+            "<methodName>", methodname, "</methodName>\n",
+            data,
+            "</methodCall>\n"
+            )
+    elif methodresponse:
+        # a method response, or a fault structure
+        data = (
+            xmlheader,
+            "<methodResponse>\n",
+            data,
+            "</methodResponse>\n"
+            )
+    else:
+        return data # return as is
+    return string.join(data, "")
+
+##
+# Convert an XML-RPC packet to a Python object.  If the XML-RPC packet
+# represents a fault condition, this function raises a Fault exception.
+#
+# @param data An XML-RPC packet, given as an 8-bit string.
+# @return A tuple containing the unpacked data, and the method name
+#     (None if not present).
+# @see Fault
+
+def loads(data, use_datetime=0):
+    """data -> unmarshalled data, method name
+
+    Convert an XML-RPC packet to unmarshalled data plus a method
+    name (None if not present).
+
+    If the XML-RPC packet represents a fault condition, this function
+    raises a Fault exception.
+    """
+    p, u = getparser(use_datetime=use_datetime)
+    p.feed(data)
+    p.close()
+    return u.close(), u.getmethodname()
+
+
+# --------------------------------------------------------------------
+# request dispatcher
+
+class _Method:
+    # some magic to bind an XML-RPC method to an RPC server.
+    # supports "nested" methods (e.g. examples.getStateName)
+    def __init__(self, send, name):
+        self.__send = send
+        self.__name = name
+    def __getattr__(self, name):
+        return _Method(self.__send, "%s.%s" % (self.__name, name))
+    def __call__(self, *args):
+        return self.__send(self.__name, args)
+
+##
+# Standard transport class for XML-RPC over HTTP.
+# <p>
+# You can create custom transports by subclassing this method, and
+# overriding selected methods.
+
+class Transport:
+    """Handles an HTTP transaction to an XML-RPC server."""
+
+    # client identifier (may be overridden)
+    user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
+
+    def __init__(self, use_datetime=0):
+        self._use_datetime = use_datetime
+
+    ##
+    # Send a complete request, and parse the response.
+    #
+    # @param host Target host.
+    # @param handler Target PRC handler.
+    # @param request_body XML-RPC request body.
+    # @param verbose Debugging flag.
+    # @return Parsed response.
+
+    def request(self, host, handler, request_body, verbose=0):
+        # issue XML-RPC request
+
+        h = self.make_connection(host)
+        if verbose:
+            h.set_debuglevel(1)
+
+        self.send_request(h, handler, request_body)
+        self.send_host(h, host)
+        self.send_user_agent(h)
+        self.send_content(h, request_body)
+
+        errcode, errmsg, headers = h.getreply()
+
+        if errcode != 200:
+            raise ProtocolError(
+                host + handler,
+                errcode, errmsg,
+                headers
+                )
+
+        self.verbose = verbose
+
+        try:
+            sock = h._conn.sock
+        except AttributeError:
+            sock = None
+
+        return self._parse_response(h.getfile(), sock)
+
+    ##
+    # Create parser.
+    #
+    # @return A 2-tuple containing a parser and a unmarshaller.
+
+    def getparser(self):
+        # get parser and unmarshaller
+        return getparser(use_datetime=self._use_datetime)
+
+    ##
+    # Get authorization info from host parameter
+    # Host may be a string, or a (host, x509-dict) tuple; if a string,
+    # it is checked for a "user:pw@host" format, and a "Basic
+    # Authentication" header is added if appropriate.
+    #
+    # @param host Host descriptor (URL or (URL, x509 info) tuple).
+    # @return A 3-tuple containing (actual host, extra headers,
+    #     x509 info).  The header and x509 fields may be None.
+
+    def get_host_info(self, host):
+
+        x509 = {}
+        if isinstance(host, TupleType):
+            host, x509 = host
+
+        import urllib
+        auth, host = urllib.splituser(host)
+
+        if auth:
+            import base64
+            auth = base64.encodestring(urllib.unquote(auth))
+            auth = string.join(string.split(auth), "") # get rid of whitespace
+            extra_headers = [
+                ("Authorization", "Basic " + auth)
+                ]
+        else:
+            extra_headers = None
+
+        return host, extra_headers, x509
+
+    ##
+    # Connect to server.
+    #
+    # @param host Target host.
+    # @return A connection handle.
+
+    def make_connection(self, host):
+        # create a HTTP connection object from a host descriptor
+        import httplib
+        host, extra_headers, x509 = self.get_host_info(host)
+        return httplib.HTTP(host)
+
+    ##
+    # Send request header.
+    #
+    # @param connection Connection handle.
+    # @param handler Target RPC handler.
+    # @param request_body XML-RPC body.
+
+    def send_request(self, connection, handler, request_body):
+        connection.putrequest("POST", handler)
+
+    ##
+    # Send host name.
+    #
+    # @param connection Connection handle.
+    # @param host Host name.
+
+    def send_host(self, connection, host):
+        host, extra_headers, x509 = self.get_host_info(host)
+        connection.putheader("Host", host)
+        if extra_headers:
+            if isinstance(extra_headers, DictType):
+                extra_headers = extra_headers.items()
+            for key, value in extra_headers:
+                connection.putheader(key, value)
+
+    ##
+    # Send user-agent identifier.
+    #
+    # @param connection Connection handle.
+
+    def send_user_agent(self, connection):
+        connection.putheader("User-Agent", self.user_agent)
+
+    ##
+    # Send request body.
+    #
+    # @param connection Connection handle.
+    # @param request_body XML-RPC request body.
+
+    def send_content(self, connection, request_body):
+        connection.putheader("Content-Type", "text/xml")
+        connection.putheader("Content-Length", str(len(request_body)))
+        connection.endheaders()
+        if request_body:
+            connection.send(request_body)
+
+    ##
+    # Parse response.
+    #
+    # @param file Stream.
+    # @return Response tuple and target method.
+
+    def parse_response(self, file):
+        # compatibility interface
+        return self._parse_response(file, None)
+
+    ##
+    # Parse response (alternate interface).  This is similar to the
+    # parse_response method, but also provides direct access to the
+    # underlying socket object (where available).
+    #
+    # @param file Stream.
+    # @param sock Socket handle (or None, if the socket object
+    #    could not be accessed).
+    # @return Response tuple and target method.
+
+    def _parse_response(self, file, sock):
+        # read response from input file/socket, and parse it
+
+        p, u = self.getparser()
+
+        while 1:
+            if sock:
+                response = sock.recv(1024)
+            else:
+                response = file.read(1024)
+            if not response:
+                break
+            if self.verbose:
+                sys.stdout.write("body: %s\n" % repr(response))
+            p.feed(response)
+
+        file.close()
+        p.close()
+
+        return u.close()
+
+##
+# Standard transport class for XML-RPC over HTTPS.
+
+class SafeTransport(Transport):
+    """Handles an HTTPS transaction to an XML-RPC server."""
+
+    # FIXME: mostly untested
+
+    def make_connection(self, host):
+        # create a HTTPS connection object from a host descriptor
+        # host may be a string, or a (host, x509-dict) tuple
+        import httplib
+        host, extra_headers, x509 = self.get_host_info(host)
+        try:
+            HTTPS = httplib.HTTPS
+        except AttributeError:
+            raise NotImplementedError(
+                "your version of httplib doesn't support HTTPS"
+                )
+        else:
+            return HTTPS(host, None, **(x509 or {}))
+
+##
+# Standard server proxy.  This class establishes a virtual connection
+# to an XML-RPC server.
+# <p>
+# This class is available as ServerProxy and Server.  New code should
+# use ServerProxy, to avoid confusion.
+#
+# @def ServerProxy(uri, **options)
+# @param uri The connection point on the server.
+# @keyparam transport A transport factory, compatible with the
+#    standard transport class.
+# @keyparam encoding The default encoding used for 8-bit strings
+#    (default is UTF-8).
+# @keyparam verbose Use a true value to enable debugging output.
+#    (printed to standard output).
+# @see Transport
+
+class ServerProxy:
+    """uri [,options] -> a logical connection to an XML-RPC server
+
+    uri is the connection point on the server, given as
+    scheme://host/target.
+
+    The standard implementation always supports the "http" scheme.  If
+    SSL socket support is available (Python 2.0), it also supports
+    "https".
+
+    If the target part and the slash preceding it are both omitted,
+    "/RPC2" is assumed.
+
+    The following options can be given as keyword arguments:
+
+        transport: a transport factory
+        encoding: the request encoding (default is UTF-8)
+
+    All 8-bit strings passed to the server proxy are assumed to use
+    the given encoding.
+    """
+
+    def __init__(self, uri, transport=None, encoding=None, verbose=0,
+                 allow_none=0, use_datetime=0):
+        # establish a "logical" server connection
+
+        # get the url
+        import urllib
+        type, uri = urllib.splittype(uri)
+        if type not in ("http", "https"):
+            raise IOError("unsupported XML-RPC protocol")
+        self.__host, self.__handler = urllib.splithost(uri)
+        if not self.__handler:
+            self.__handler = "/RPC2"
+
+        if transport is None:
+            if type == "https":
+                transport = SafeTransport(use_datetime=use_datetime)
+            else:
+                transport = Transport(use_datetime=use_datetime)
+        self.__transport = transport
+
+        self.__encoding = encoding
+        self.__verbose = verbose
+        self.__allow_none = allow_none
+
+    def __request(self, methodname, params):
+        # call a method on the remote server
+
+        request = dumps(params, methodname, encoding=self.__encoding,
+                        allow_none=self.__allow_none)
+
+        response = self.__transport.request(
+            self.__host,
+            self.__handler,
+            request,
+            verbose=self.__verbose
+            )
+
+        if len(response) == 1:
+            response = response[0]
+
+        return response
+
+    def __repr__(self):
+        return (
+            "<ServerProxy for %s%s>" % 
+            (self.__host, self.__handler)
+            )
+
+    __str__ = __repr__
+
+    def __getattr__(self, name):
+        # magic method dispatcher
+        return _Method(self.__request, name)
+
+    # note: to call a remote object with an non-standard name, use
+    # result getattr(server, "strange-python-name")(args)
+
+# compatibility
+
+Server = ServerProxy
+
+# --------------------------------------------------------------------
+# test code
+
+if __name__ == "__main__":
+
+    # simple test program (from the XML-RPC specification)
+
+    # server = ServerProxy("http://localhost:8000") # local server
+    server = ServerProxy("http://time.xmlrpc.com/RPC2")
+
+    sys.stdout.write('%s\n' % server)
+
+    try:
+        sys.stdout.write('%s\n' % (server.currentTime.getCurrentTime(),))
+    except Error:
+        import traceback;traceback.print_exc()
+
+    multi = MultiCall(server)
+    multi.currentTime.getCurrentTime()
+    multi.currentTime.getCurrentTime()
+    try:
+        for response in multi():
+            sys.stdout.write('%s\n' % (response,))
+    except Error:
+        import traceback;traceback.print_exc()
diff --git a/python/helpers/pydev/_pydev_jy_imports_tipper.py b/python/helpers/pydev/_pydev_jy_imports_tipper.py
index 43e4d0b..1691e3e 100644
--- a/python/helpers/pydev/_pydev_jy_imports_tipper.py
+++ b/python/helpers/pydev/_pydev_jy_imports_tipper.py
@@ -1,7 +1,5 @@
 import StringIO
 import traceback
-from java.lang import StringBuffer #@UnresolvedImport
-from java.lang import String #@UnresolvedImport
 import java.lang #@UnresolvedImport
 import sys
 from _pydev_tipper_common import DoFind
@@ -14,12 +12,16 @@
     import __builtin__
     setattr(__builtin__, 'True', 1)
     setattr(__builtin__, 'False', 0)
-    
-    
+
+
 from org.python.core import PyReflectedFunction #@UnresolvedImport
 
 from org.python import core #@UnresolvedImport
-from org.python.core import PyClass #@UnresolvedImport
+
+try:
+    xrange
+except:
+    xrange = range
 
 
 #completion types.
@@ -48,11 +50,11 @@
             name = 'org.python.core.PyString'
         elif name == '__builtin__.dict':
             name = 'org.python.core.PyDictionary'
-            
+
     mod = _imp(name)
     parent = mod
     foundAs = ''
-    
+
     if hasattr(mod, '__file__'):
         f = mod.__file__
 
@@ -68,29 +70,29 @@
         except AttributeError:
             if old_comp != comp:
                 raise
-        
+
         if hasattr(mod, '__file__'):
             f = mod.__file__
         else:
             if len(foundAs) > 0:
                 foundAs = foundAs + '.'
             foundAs = foundAs + comp
-            
+
         old_comp = comp
-        
+
     return f, mod, parent, foundAs
 
 def formatParamClassName(paramClassName):
     if paramClassName.startswith('['):
         if paramClassName == '[C':
             paramClassName = 'char[]'
-        
+
         elif paramClassName == '[B':
             paramClassName = 'byte[]'
-        
+
         elif paramClassName == '[I':
             paramClassName = 'int[]'
-            
+
         elif paramClassName.startswith('[L') and paramClassName.endswith(';'):
             paramClassName = paramClassName[2:-1]
             paramClassName += '[]'
@@ -101,17 +103,17 @@
     data = data.replace('\n', '')
     if data.endswith('.'):
         data = data.rstrip('.')
-    
+
     f, mod, parent, foundAs = Find(data)
     tips = GenerateImportsTipForModule(mod)
     return f, tips
-    
+
 
 #=======================================================================================================================
 # Info
 #=======================================================================================================================
 class Info:
-    
+
     def __init__(self, name, **kwargs):
         self.name = name
         self.doc = kwargs.get('doc', None)
@@ -119,47 +121,47 @@
         self.varargs = kwargs.get('varargs', None) #string
         self.kwargs = kwargs.get('kwargs', None) #string
         self.ret = kwargs.get('ret', None) #string
-        
+
     def basicAsStr(self):
         '''@returns this class information as a string (just basic format)
         '''
-        
+
         s = 'function:%s args=%s, varargs=%s, kwargs=%s, docs:%s' % \
             (str(self.name), str(self.args), str(self.varargs), str(self.kwargs), str(self.doc))
         return s
-        
+
 
     def getAsDoc(self):
         s = str(self.name)
         if self.doc:
             s += '\n@doc %s\n' % str(self.doc)
-            
+
         if self.args:
             s += '\n@params '
             for arg in self.args:
                 s += str(formatParamClassName(arg))
                 s += '  '
-        
+
         if self.varargs:
             s += '\n@varargs '
             s += str(self.varargs)
-            
+
         if self.kwargs:
             s += '\n@kwargs '
             s += str(self.kwargs)
-            
+
         if self.ret:
             s += '\n@return '
             s += str(formatParamClassName(str(self.ret)))
-            
+
         return str(s)
-        
+
 def isclass(cls):
     return isinstance(cls, core.PyClass)
 
 def ismethod(func):
     '''this function should return the information gathered on a function
-    
+
     @param func: this is the function we want to get info on
     @return a tuple where:
         0 = indicates whether the parameter passed is a method or not
@@ -167,24 +169,24 @@
             this is a list because when we have methods from java with the same name and different signatures,
             we actually have many methods, each with its own set of arguments
     '''
-    
+
     try:
         if isinstance(func, core.PyFunction):
             #ok, this is from python, created by jython
             #print_ '    PyFunction'
-            
+
             def getargs(func_code):
                 """Get information about the arguments accepted by a code object.
-            
+
                 Three things are returned: (args, varargs, varkw), where 'args' is
                 a list of argument names (possibly containing nested lists), and
                 'varargs' and 'varkw' are the names of the * and ** arguments or None."""
-            
+
                 nargs = func_code.co_argcount
                 names = func_code.co_varnames
                 args = list(names[:nargs])
                 step = 0
-            
+
                 varargs = None
                 if func_code.co_flags & func_code.CO_VARARGS:
                     varargs = func_code.co_varnames[nargs]
@@ -193,35 +195,35 @@
                 if func_code.co_flags & func_code.CO_VARKEYWORDS:
                     varkw = func_code.co_varnames[nargs]
                 return args, varargs, varkw
-            
+
             args = getargs(func.func_code)
             return 1, [Info(func.func_name, args=args[0], varargs=args[1], kwargs=args[2], doc=func.func_doc)]
-            
+
         if isinstance(func, core.PyMethod):
             #this is something from java itself, and jython just wrapped it...
-            
+
             #things to play in func:
             #['__call__', '__class__', '__cmp__', '__delattr__', '__dir__', '__doc__', '__findattr__', '__name__', '_doget', 'im_class',
             #'im_func', 'im_self', 'toString']
             #print_ '    PyMethod'
             #that's the PyReflectedFunction... keep going to get it
             func = func.im_func
-    
+
         if isinstance(func, PyReflectedFunction):
             #this is something from java itself, and jython just wrapped it...
-            
+
             #print_ '    PyReflectedFunction'
-            
+
             infos = []
-            for i in range(len(func.argslist)):
+            for i in xrange(len(func.argslist)):
                 #things to play in func.argslist[i]:
-                    
+
                 #'PyArgsCall', 'PyArgsKeywordsCall', 'REPLACE', 'StandardCall', 'args', 'compare', 'compareTo', 'data', 'declaringClass'
                 #'flags', 'isStatic', 'matches', 'precedence']
-                
+
                 #print_ '        ', func.argslist[i].data.__class__
                 #func.argslist[i].data.__class__ == java.lang.reflect.Method
-                
+
                 if func.argslist[i]:
                     met = func.argslist[i].data
                     name = met.getName()
@@ -230,9 +232,9 @@
                     except AttributeError:
                         ret = ''
                     parameterTypes = met.getParameterTypes()
-                    
+
                     args = []
-                    for j in range(len(parameterTypes)):
+                    for j in xrange(len(parameterTypes)):
                         paramTypesClass = parameterTypes[j]
                         try:
                             try:
@@ -246,7 +248,7 @@
                             except:
                                 paramClassName = repr(paramTypesClass) #just in case something else happens... it will at least be visible
                         #if the parameter equals [C, it means it it a char array, so, let's change it
-    
+
                         a = formatParamClassName(paramClassName)
                         #a = a.replace('[]','Array')
                         #a = a.replace('Object', 'obj')
@@ -255,18 +257,18 @@
                         #a = a.replace('Char', 'c')
                         #a = a.replace('Double', 'd')
                         args.append(a) #so we don't leave invalid code
-    
-                    
+
+
                     info = Info(name, args=args, ret=ret)
                     #print_ info.basicAsStr()
                     infos.append(info)
-    
+
             return 1, infos
-    except Exception, e:
+    except Exception:
         s = StringIO.StringIO()
         traceback.print_exc(file=s)
         return 1, [Info(str('ERROR'), doc=s.getvalue())]
-        
+
     return 0, None
 
 def ismodule(mod):
@@ -274,7 +276,7 @@
     if not hasattr(mod, 'getClass') and not hasattr(mod, '__class__') \
        and hasattr(mod, '__name__'):
             return 1
-           
+
     return isinstance(mod, core.PyModule)
 
 
@@ -293,11 +295,11 @@
             except TypeError:
                 #may happen on jython when getting the java.lang.Class class
                 c = obj.getSuperclass(obj)
-                
+
             while c != None:
                 classes.append(c)
                 c = c.getSuperclass()
-            
+
             #get info about interfaces
             interfs = []
             for obj in classes:
@@ -306,57 +308,57 @@
                 except TypeError:
                     interfs.extend(obj.getInterfaces(obj))
             classes.extend(interfs)
-                
+
             #now is the time when we actually get info on the declared methods and fields
             for obj in classes:
                 try:
                     declaredMethods = obj.getDeclaredMethods()
                 except TypeError:
                     declaredMethods = obj.getDeclaredMethods(obj)
-                    
+
                 try:
                     declaredFields = obj.getDeclaredFields()
                 except TypeError:
                     declaredFields = obj.getDeclaredFields(obj)
-                    
-                for i in range(len(declaredMethods)):
+
+                for i in xrange(len(declaredMethods)):
                     name = declaredMethods[i].getName()
                     ret.append(name)
                     found.put(name, 1)
-                    
-                for i in range(len(declaredFields)):
+
+                for i in xrange(len(declaredFields)):
                     name = declaredFields[i].getName()
                     ret.append(name)
                     found.put(name, 1)
-                    
-                    
-        elif isclass(obj.__class__): 
+
+
+        elif isclass(obj.__class__):
             d = dir(obj.__class__)
             for name in d:
                 ret.append(name)
                 found.put(name, 1)
-            
+
 
     #this simple dir does not always get all the info, that's why we have the part before
-    #(e.g.: if we do a dir on String, some methods that are from other interfaces such as 
+    #(e.g.: if we do a dir on String, some methods that are from other interfaces such as
     #charAt don't appear)
     d = dir(original)
     for name in d:
         if found.get(name) != 1:
             ret.append(name)
-            
+
     return ret
 
 
 def formatArg(arg):
     '''formats an argument to be shown
     '''
-    
+
     s = str(arg)
     dot = s.rfind('.')
     if dot >= 0:
         s = s[dot + 1:]
-    
+
     s = s.replace(';', '')
     s = s.replace('[]', 'Array')
     if len(s) > 0:
@@ -364,13 +366,13 @@
         s = c + s[1:]
 
     return s
-    
-    
-    
+
+
+
 def Search(data):
     '''@return file, line, col
     '''
-    
+
     data = data.replace('\n', '')
     if data.endswith('.'):
         data = data.rstrip('.')
@@ -379,8 +381,8 @@
         return DoFind(f, mod), foundAs
     except:
         return DoFind(f, parent), foundAs
-    
-    
+
+
 def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr, filter=lambda name:True):
     '''
         @param obj_to_complete: the object from where we should get the completions
@@ -391,18 +393,18 @@
             name, doc, args, type (from the TYPE_* constants)
     '''
     ret = []
-    
+
     if dirComps is None:
         dirComps = dirObj(obj_to_complete)
-    
+
     for d in dirComps:
 
         if d is None:
             continue
-            
+
         if not filter(d):
             continue
-            
+
         args = ''
         doc = ''
         retType = TYPE_BUILTIN
@@ -421,7 +423,7 @@
             #note: this only happens when we add things to the sys.path at runtime, if they are added to the classpath
             #before the run, everything goes fine.
             #
-            #The code below ilustrates what I mean... 
+            #The code below ilustrates what I mean...
             #
             #import sys
             #sys.path.insert(1, r"C:\bin\eclipse310\plugins\org.junit_3.8.1\junit.jar" )
@@ -429,7 +431,7 @@
             #import junit.framework
             #print_ dir(junit.framework) #shows the TestCase class here
             #
-            #import junit.framework.TestCase 
+            #import junit.framework.TestCase
             #
             #raises the error:
             #Traceback (innermost last):
@@ -458,19 +460,19 @@
                 except TypeError:
                     traceback.print_exc()
                     args = '()'
-    
+
                 retType = TYPE_FUNCTION
-                
+
             elif isclass(obj):
                 retType = TYPE_CLASS
-                
+
             elif ismodule(obj):
                 retType = TYPE_IMPORT
-        
+
         #add token and doc to return - assure only strings.
         ret.append((d, doc, args, retType))
-        
-            
+
+
     return ret
 
 
diff --git a/python/helpers/pydev/_pydev_thread.py b/python/helpers/pydev/_pydev_thread.py
deleted file mode 100644
index 3971c79..0000000
--- a/python/helpers/pydev/_pydev_thread.py
+++ /dev/null
@@ -1 +0,0 @@
-from thread import *
\ No newline at end of file
diff --git a/python/helpers/pydev/_pydev_threading.py b/python/helpers/pydev/_pydev_threading.py
index 52d48c9..d7bfadf 100644
--- a/python/helpers/pydev/_pydev_threading.py
+++ b/python/helpers/pydev/_pydev_threading.py
@@ -2,14 +2,10 @@
 
 import sys as _sys
 
-try:
-    import _pydev_thread as thread
-except ImportError:
-    import thread
-
+from _pydev_imps import _pydev_thread as thread
 import warnings
 
-from _pydev_time import time as _time, sleep as _sleep
+from _pydev_imps._pydev_time import time as _time, sleep as _sleep
 from traceback import format_exc as _format_exc
 
 # Note regarding PEP 8 compliant aliases
@@ -854,7 +850,7 @@
 # module, or from the python fallback
 
 try:
-    from _pydev_thread import _local as local
+    from _pydev_imps._pydev_thread import _local as local
 except ImportError:
     from _threading_local import local
 
diff --git a/python/helpers/pydev/_pydev_tipper_common.py b/python/helpers/pydev/_pydev_tipper_common.py
index f8c46d2..8e6267f 100644
--- a/python/helpers/pydev/_pydev_tipper_common.py
+++ b/python/helpers/pydev/_pydev_tipper_common.py
@@ -2,7 +2,7 @@
     import inspect
 except:
     try:
-        import _pydev_inspect as inspect # for older versions
+        from _pydev_imps import _pydev_inspect as inspect
     except:
         import traceback;traceback.print_exc() #Ok, no inspect available (search will not work)
 
@@ -10,57 +10,58 @@
     import re
 except:
     try:
-        import _pydev_re as re # for older versions @UnresolvedImport
+        import sre as re  # for older versions
     except:
         import traceback;traceback.print_exc() #Ok, no inspect available (search will not work)
 
 
+from pydevd_constants import xrange
 
 def DoFind(f, mod):
     import linecache
     if inspect.ismodule(mod):
         return f, 0, 0
-    
+
     lines = linecache.getlines(f)
-    
+
     if inspect.isclass(mod):
         name = mod.__name__
         pat = re.compile(r'^\s*class\s*' + name + r'\b')
-        for i in range(len(lines)):
-            if pat.match(lines[i]): 
+        for i in xrange(len(lines)):
+            if pat.match(lines[i]):
                 return f, i, 0
-            
+
         return f, 0, 0
 
     if inspect.ismethod(mod):
         mod = mod.im_func
-        
+
     if inspect.isfunction(mod):
         try:
             mod = mod.func_code
         except AttributeError:
             mod = mod.__code__ #python 3k
-            
+
     if inspect.istraceback(mod):
         mod = mod.tb_frame
-        
+
     if inspect.isframe(mod):
         mod = mod.f_code
 
     if inspect.iscode(mod):
         if not hasattr(mod, 'co_filename'):
             return None, 0, 0
-        
+
         if not hasattr(mod, 'co_firstlineno'):
             return mod.co_filename, 0, 0
-        
+
         lnum = mod.co_firstlineno
         pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
         while lnum > 0:
-            if pat.match(lines[lnum]): 
+            if pat.match(lines[lnum]):
                 break
             lnum -= 1
-            
+
         return f, lnum, 0
 
     raise RuntimeError('Do not know about: ' + f + ' ' + str(mod))
diff --git a/python/helpers/pydev/_pydev_xmlrpc_hook.py b/python/helpers/pydev/_pydev_xmlrpc_hook.py
deleted file mode 100644
index 22d445a..0000000
--- a/python/helpers/pydev/_pydev_xmlrpc_hook.py
+++ /dev/null
@@ -1,74 +0,0 @@
-from pydev_imports import SimpleXMLRPCServer
-from pydev_ipython.inputhook import get_inputhook, set_return_control_callback
-import select
-import sys
-
-select_fn = select.select
-if sys.platform.startswith('java'):
-    select_fn = select.cpython_compatible_select
-
-class InputHookedXMLRPCServer(SimpleXMLRPCServer):
-    ''' An XML-RPC Server that can run hooks while polling for new requests.
-
-        This code was designed to work with IPython's inputhook methods and
-        to allow Debug framework to have a place to run commands during idle
-        too.
-    '''
-    def __init__(self, *args, **kwargs):
-        SimpleXMLRPCServer.__init__(self, *args, **kwargs)
-        # Tell the inputhook mechanisms when control should be returned
-        set_return_control_callback(self.return_control)
-        self.debug_hook = None
-        self.return_control_osc = False
-
-    def return_control(self):
-        ''' A function that the inputhooks can call (via inputhook.stdin_ready()) to find 
-            out if they should cede control and return '''
-        if self.debug_hook:
-            # Some of the input hooks check return control without doing
-            # a single operation, so we don't return True on every
-            # call when the debug hook is in place to allow the GUI to run
-            # XXX: Eventually the inputhook code will have diverged enough
-            # from the IPython source that it will be worthwhile rewriting
-            # it rather than pretending to maintain the old API
-            self.return_control_osc = not self.return_control_osc
-            if self.return_control_osc:
-                return True
-        r, unused_w, unused_e = select_fn([self], [], [], 0)
-        return bool(r)
-
-    def setDebugHook(self, debug_hook):
-        self.debug_hook = debug_hook
-
-    def serve_forever(self):
-        ''' Serve forever, running defined hooks regularly and when idle.
-            Does not support shutdown '''
-        inputhook = get_inputhook()
-        while True:
-            # Block for default 1/2 second when no GUI is in progress
-            timeout = 0.5
-            if self.debug_hook:
-                self.debug_hook()
-                timeout = 0.1
-            if inputhook:
-                try:
-                    inputhook()
-                    # The GUI has given us an opportunity to try receiving, normally
-                    # this happens because the input hook has already polled the
-                    # server has knows something is waiting
-                    timeout = 0.020
-                except:
-                    inputhook = None
-            r, unused_w, unused_e = select_fn([self], [], [], timeout)
-            if self in r:
-                try:
-                    self._handle_request_noblock()
-                except AttributeError:
-                    # Older libraries do not support _handle_request_noblock, so fall
-                    # back to the handle_request version
-                    self.handle_request()
-                # Running the request may have changed the inputhook in use
-                inputhook = get_inputhook()
-
-    def shutdown(self):
-        raise NotImplementedError('InputHookedXMLRPCServer does not support shutdown')
diff --git a/python/helpers/pydev/_pydevd_re.py b/python/helpers/pydev/_pydevd_re.py
deleted file mode 100644
index cd00672..0000000
--- a/python/helpers/pydev/_pydevd_re.py
+++ /dev/null
@@ -1,11 +0,0 @@
-
-__all__ = [ "match", "search", "sub", "subn", "split", "findall",
-    "compile", "purge", "template", "escape", "I", "L", "M", "S", "X",
-    "U", "IGNORECASE", "LOCALE", "MULTILINE", "DOTALL", "VERBOSE",
-    "UNICODE", "error" ]
-
-import sre, sys
-module = sys.modules['re']
-for name in __all__:
-    setattr(module, name, getattr(sre, name))
-
diff --git a/python/helpers/pydev/django_debug.py b/python/helpers/pydev/django_debug.py
index 37ee042..417ff01 100644
--- a/python/helpers/pydev/django_debug.py
+++ b/python/helpers/pydev/django_debug.py
@@ -1,28 +1,19 @@
 import inspect
-from django_frame import DjangoTemplateFrame, get_template_file_name, get_template_line
+from django_frame import DjangoTemplateFrame
 from pydevd_comm import CMD_SET_BREAK
-from pydevd_constants import DJANGO_SUSPEND, GetThreadId
-from pydevd_file_utils import NormFileToServer
-from runfiles import DictContains
+from pydevd_constants import DJANGO_SUSPEND, GetThreadId, DictContains
 from pydevd_breakpoints import LineBreakpoint
 import pydevd_vars
 import traceback
 
 class DjangoLineBreakpoint(LineBreakpoint):
-    def __init__(self, type, file, line, flag, condition, func_name, expression):
+
+    def __init__(self, file, line, condition, func_name, expression):
         self.file = file
-        self.line = line
-        LineBreakpoint.__init__(self, type, flag, condition, func_name, expression)
+        LineBreakpoint.__init__(self, line, condition, func_name, expression)
 
-    def __eq__(self, other):
-        if not isinstance(other, DjangoLineBreakpoint):
-            return False
-        return self.file == other.file and self.line == other.line
-
-    def is_triggered(self, frame):
-        file = get_template_file_name(frame)
-        line = get_template_line(frame)
-        return self.file == file and self.line == line
+    def is_triggered(self, template_frame_file, template_frame_line):
+        return self.file == template_frame_file and self.line == template_frame_line
 
     def __str__(self):
         return "DjangoLineBreakpoint: %s-%d" %(self.file, self.line)
diff --git a/python/helpers/pydev/django_frame.py b/python/helpers/pydev/django_frame.py
index 762df2d..4181572 100644
--- a/python/helpers/pydev/django_frame.py
+++ b/python/helpers/pydev/django_frame.py
@@ -1,11 +1,14 @@
 from pydevd_file_utils import GetFileNameAndBaseFromFile
 import pydev_log
 import traceback
+from pydevd_constants import DictContains
 
 def read_file(filename):
     f = open(filename, "r")
-    s = f.read()
-    f.close()
+    try:
+        s = f.read()
+    finally:
+        f.close()
     return s
 
 
@@ -34,7 +37,9 @@
         if hasattr(node, 'source'):
             return node.source
         else:
-            pydev_log.error_once("WARNING: Template path is not available. Please set TEMPLATE_DEBUG=True in your settings.py to make django template breakpoints working")
+            pydev_log.error_once(
+                "WARNING: Template path is not available. Please set TEMPLATE_DEBUG=True "
+                "in your settings.py to make django template breakpoints working")
             return None
 
     except:
@@ -61,41 +66,50 @@
         return None
 
 
-def get_template_line(frame):
+def get_template_line(frame, template_frame_file):
     source = get_source(frame)
-    file_name = get_template_file_name(frame)
     try:
-        return offset_to_line_number(read_file(file_name), source[1][0])
+        return offset_to_line_number(read_file(template_frame_file), source[1][0])
     except:
         return None
 
 
 class DjangoTemplateFrame:
-    def __init__(self, frame):
-        file_name = get_template_file_name(frame)
+    def __init__(
+        self,
+        frame,
+        template_frame_file=None,
+        template_frame_line=None):
+
+        if template_frame_file is None:
+            template_frame_file = get_template_file_name(frame)
+
         self.back_context = frame.f_locals['context']
-        self.f_code = FCode('Django Template', file_name)
-        self.f_lineno = get_template_line(frame)
+        self.f_code = FCode('Django Template', template_frame_file)
+
+        if template_frame_line is None:
+            template_frame_line = get_template_line(frame, template_frame_file)
+        self.f_lineno = template_frame_line
+
         self.f_back = frame
         self.f_globals = {}
-        self.f_locals = self.collect_context(self.back_context)
+        self.f_locals = self.collect_context()
         self.f_trace = None
 
-    def collect_context(self, context):
+    def collect_context(self):
         res = {}
         try:
-            for d in context.dicts:
-                for k, v in d.items():
-                    res[k] = v
-        except  AttributeError:
+            for d in self.back_context.dicts:
+                res.update(d)
+        except AttributeError:
             pass
         return res
 
     def changeVariable(self, name, value):
         for d in self.back_context.dicts:
-            for k, v in d.items():
-                if k == name:
-                    d[k] = value
+            if DictContains(d, name):
+                d[name] = value
+        self.f_locals[name] = value
 
 
 class FCode:
@@ -106,10 +120,9 @@
 
 def is_django_exception_break_context(frame):
     try:
-        name = frame.f_code.co_name
+        return frame.f_code.co_name in ['_resolve_lookup', 'find_template']
     except:
-        name = None
-    return name in ['_resolve_lookup', 'find_template']
+        return False
 
 
 def just_raised(trace):
diff --git a/python/helpers/pydev/merge_pydev_pycharm.txt b/python/helpers/pydev/merge_pydev_pycharm.txt
new file mode 100644
index 0000000..1cbd356
--- /dev/null
+++ b/python/helpers/pydev/merge_pydev_pycharm.txt
@@ -0,0 +1,138 @@
+Done in the merge (started from the PyCharm version and bringing in things from PyDev):
+
+- Added modules which were unused in PyCharm but are used in PyDev.
+
+- execfile was upgraded to the PyDev version (it had errors with BOM in utf-8).
+
+- pydevd_file_utils: automatically doing normcase.
+
+- pyded: multiprocessing supporting 2 approaches (use new connection/use same connection).
+
+- pydev_monkey: fixes from PyDev to properly deal with windows command lines.
+
+- Private variables (name-mangled) are now evaluated (so they can be hovered).
+
+- Exceptions raised from lines with a #@IgnoreException are ignored.
+
+- Fixed exception changing variable in django debugging.
+
+- Made debugging with Django breakpoints a bit faster.
+
+- Exceptions separated by caught/uncaught, so, it's no longer needed to check
+    an additional attribute to check it.
+
+- When exception is thrown evaluating breakpoint condition, the debugger will stop
+    (can be configured in main_debugger.suspend_on_breakpoint_exception).
+
+- #@DontTrace comments can be used on methods so that they are ignored when stepping
+    in (needs UI for CMD_ENABLE_DONT_TRACE).
+
+- Code which stops tracing inside python properties integrated (CMD_SET_PROPERTY_TRACE).
+
+- Find Referrers integrated.
+
+- Using same facade for IPython integration.
+
+- When the code is interrupted, the buffer in the python side is cleared.
+
+- GEvent debugging: for remote debugging, one has to import pydevd before doing the gevent patching -- even if
+    pydevd.settrace will only be done later.
+
+    Also, the gevent debugging should probably be closer to the stackless debugging,
+    where we actually show the live stackless threads -- so, we should show the live
+    gevent greenlets -- which the current version doesn't do.
+
+
+Things to be fixed in PyCharm:
+--------------------------------
+
+1. CMD_VERSION now should receive that it wants breakpoints by ID (and it
+    should give a breakpoint id which should be used to remove it later
+    when setting a breakpoint).
+
+2. Setting breakpoint: the func_name is not being properly passed from PyCharm
+    (and as such, PyCharm debugging is slower than it should be).
+
+    Note that it works passing 'None', but the func_name should be given when possible.
+
+    I.e.:
+
+    class MyClass(object):
+        def __init__(self):
+            print('here') # Break here: '__init__' as func_name
+            print(a)
+
+        def bar(self):
+            print('bar') # Break here: 'bar' as func_name
+
+3. Note (may not need to change anything):
+    Removed support for removing breakpoint without knowing its type (i.e.:
+    remove breakpoint tried to remove a breakpoint if the type wasn't
+    python-line nor django-line, now, it'll give an exception).
+
+4. break_on_exceptions_thrown_in_same_context / ignore_exceptions_thrown_in_lines_with_ignore_exception
+
+    These are currently set in the bulk operation to add exceptions, but
+    it does make sense to create a separate command for that (but it's only
+    worth doing it when/if PyCharm gets an UI to add it).
+
+5. UI to ignore exception from additional places (not only from #@IgnoreException code-comments)
+    i.e.: UI for CMD_IGNORE_THROWN_EXCEPTION_AT.
+
+6. UI to show the current exception (i.e.: deal with CMD_SEND_CURR_EXCEPTION_TRACE and
+    CMD_SEND_CURR_EXCEPTION_TRACE_PROCEEDED in the client side).
+
+7. When an exception is detected on a breakpoint condition evaluation, we'll send
+    a CMD_GET_BREAKPOINT_EXCEPTION (which should be handled by PyCharm to show some
+    UI notification).
+
+8. The CMD_ENABLE_DONT_TRACE must be sent from the UI to skip methods which have
+    an #@DontTrace above it.
+
+9. The CMD_SET_PROPERTY_TRACE must be sent from the UI to skip setter/getter/deleter
+    python properties.
+
+10. Integrate find referrers UI in PyCharm. In the backend it uses a CMD_RUN_CUSTOM_OPERATION with:
+    from pydevd_referrers import get_referrer_info
+    get_referrer_info
+
+11. CMD_RELOAD_CODE has to be integrated (when a file changes it should be issued
+    for 'hot' auto-reload of the code -- note that it's not needed if the
+    user already has some sort of auto-reload builtin -- i.e.: django without the noreload option).
+
+12. Console Completions: See: pydev_ipython_console_011.PyDevFrontEnd.getCompletions
+    Now we're completing as they come from the IPython backend (i.e.: not stripping %
+    for magic commands).
+
+13. In PyDev, interrupt can be used to clear the current buffer (whereas in PyCharm it's only
+    possible to activate it to stop the execution of a command) -- note that this is only a
+    client-side limitation.
+
+14. Console GUI event loop can have some UI integrated.
+    Note that the user can enable it manually (i.e.: by writing something as "%gui qt"
+    the qt backend is integrated, but it's possible to call 'enableGui' with the
+    backend to use from PyCharm too -- in PyDev this is an option with the possible backends).
+
+
+Things to be fixed in PyDev:
+--------------------------------
+
+.Provide UI for 'smart step into' (later)
+
+. Check what to do with 'message' from xml (later)
+
+. Deal with sendSignatureCallTrace (later)
+
+. Set IPYTHONENABLE to False/True to use IPython console (later)
+
+
+Manual test:
+---------------
+
+* Support for IPython GUI event loop in console
+* Django template debugging
+* Gevent debugging
+* Smart step into
+* Collection of type information of arguments in debug mode
+* Ability to stop tracing
+* Ability to run debugger and console on remote interpreter
diff --git a/python/helpers/pydev/pycompletion.py b/python/helpers/pydev/pycompletion.py
index e706d54..3369780 100644
--- a/python/helpers/pydev/pycompletion.py
+++ b/python/helpers/pydev/pycompletion.py
@@ -2,12 +2,10 @@
 '''
 @author Radim Kubacki
 '''
-import __builtin__
 import _pydev_imports_tipper
 import traceback
 import StringIO
 import sys
-import time
 import urllib
 import pycompletionserver
 
@@ -24,7 +22,7 @@
     except:
         s = StringIO.StringIO()
         exc_info = sys.exc_info()
-    
+
         traceback.print_exception(exc_info[0], exc_info[1], exc_info[2], limit=None, file=s)
         err = s.getvalue()
         pycompletionserver.dbg('Received error: ' + str(err), pycompletionserver.ERROR)
@@ -38,4 +36,4 @@
     mod_name = sys.argv[1]
 
     print(GetImports(mod_name))
-           
+
diff --git a/python/helpers/pydev/pycompletionserver.py b/python/helpers/pydev/pycompletionserver.py
index 2fdd539..0b11cb6 100644
--- a/python/helpers/pydev/pycompletionserver.py
+++ b/python/helpers/pydev/pycompletionserver.py
@@ -37,10 +37,7 @@
     import _pydev_imports_tipper
 
 
-if pydevd_constants.USE_LIB_COPY:
-    import _pydev_socket as socket
-else:
-    import socket
+from _pydev_imps import _pydev_socket as socket
 
 import sys
 if sys.platform == "darwin":
@@ -65,10 +62,7 @@
 
 import traceback
 
-if pydevd_constants.USE_LIB_COPY:
-    import _pydev_time as time
-else:
-    import time
+from _pydev_imps import _pydev_time as time
 
 try:
     import StringIO
@@ -93,7 +87,7 @@
 #        f = open('c:/temp/test.txt', 'a')
 #        print_ >> f, s
 #        f.close()
-   
+
 import pydev_localhost
 HOST = pydev_localhost.get_localhost() # Symbolic name meaning the local host
 
@@ -203,7 +197,7 @@
 
 
     def connectToServer(self):
-        import socket
+        from _pydev_imps import _pydev_socket as socket
 
         self.socket = s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         try:
diff --git a/python/helpers/pydev/pydev_console_utils.py b/python/helpers/pydev/pydev_console_utils.py
index 571ae87..bd7b7de 100644
--- a/python/helpers/pydev/pydev_console_utils.py
+++ b/python/helpers/pydev/pydev_console_utils.py
@@ -1,36 +1,12 @@
-from pydev_imports import xmlrpclib
+from pydev_imports import xmlrpclib, _queue, Exec
 import sys
-
-import traceback
-
 from pydevd_constants import USE_LIB_COPY
 from pydevd_constants import IS_JYTHON
-
-try:
-    if USE_LIB_COPY:
-        import _pydev_Queue as _queue
-    else:
-        import Queue as _queue
-except:
-    import queue as _queue
-
-try:
-    from pydevd_exec import Exec
-except:
-    from pydevd_exec2 import Exec
-
-try:
-    if USE_LIB_COPY:
-        import _pydev_thread as thread
-    else:
-        import thread
-except:
-    import _thread as thread
-
+from _pydev_imps import _pydev_thread as thread
 import pydevd_xml
 import pydevd_vars
-
-from pydevd_utils import *
+from pydevd_utils import *  # @UnusedWildImport
+import traceback
 
 #=======================================================================================================================
 # Null
@@ -137,7 +113,7 @@
     def __init__(self, text, is_single_line=True):
         self.text = text
         self.is_single_line = is_single_line
-        
+
     def append(self, code_fragment):
         self.text = self.text + "\n" + code_fragment.text
         if not code_fragment.is_single_line:
@@ -173,9 +149,12 @@
             self.buffer = code_fragment
         else:
             self.buffer.append(code_fragment)
-        
+
         return self.needMoreForCode(self.buffer.text)
 
+    def createStdIn(self):
+        return StdIn(self, self.host, self.client_port)
+
     def addExec(self, code_fragment):
         original_in = sys.stdin
         try:
@@ -194,7 +173,7 @@
 
         more = False
         try:
-            sys.stdin = StdIn(self, self.host, self.client_port)
+            sys.stdin = self.createStdIn()
             try:
                 if help is not None:
                     #This will enable the help() function to work.
@@ -209,8 +188,6 @@
                             self._input_error_printed = True
                             sys.stderr.write('\nError when trying to update pydoc.help.input\n')
                             sys.stderr.write('(help() may not work -- please report this as a bug in the pydev bugtracker).\n\n')
-                            import traceback
-
                             traceback.print_exc()
 
                 try:
@@ -241,8 +218,6 @@
         except SystemExit:
             raise
         except:
-            import traceback;
-
             traceback.print_exc()
 
         return more
@@ -251,7 +226,7 @@
     def doAddExec(self, codeFragment):
         '''
         Subclasses should override.
-        
+
         @return: more (True if more input is needed to complete the statement and False if the statement is complete).
         '''
         raise NotImplementedError()
@@ -260,7 +235,7 @@
     def getNamespace(self):
         '''
         Subclasses should override.
-        
+
         @return: dict with namespace.
         '''
         raise NotImplementedError()
@@ -312,14 +287,14 @@
                     pass
 
             try:
-                #if no attempt succeeded, try to return repr()... 
+                #if no attempt succeeded, try to return repr()...
                 return repr(obj)
             except:
                 try:
-                    #otherwise the class 
+                    #otherwise the class
                     return str(obj.__class__)
                 except:
-                    #if all fails, go to an empty string 
+                    #if all fails, go to an empty string
                     return ''
         except:
             traceback.print_exc()
@@ -353,6 +328,7 @@
 
 
     def interrupt(self):
+        self.buffer = None # Also clear the buffer when it's interrupted.
         try:
             if self.interruptable:
                 if hasattr(thread, 'interrupt_main'): #Jython doesn't have it
@@ -371,11 +347,13 @@
         self.interruptable = True
 
     def get_server(self):
-        if self.host is not None:
+        if getattr(self, 'host', None) is not None:
             return xmlrpclib.Server('http://%s:%s' % (self.host, self.client_port))
         else:
             return None
 
+    server = property(get_server)
+
     def finishExec(self, more):
         self.interruptable = False
 
@@ -409,7 +387,12 @@
         return xml
 
     def changeVariable(self, attr, value):
-        Exec('%s=%s' % (attr, value), self.getNamespace(), self.getNamespace())
+        def do_change_variable():
+            Exec('%s=%s' % (attr, value), self.getNamespace(), self.getNamespace())
+
+        # Important: it has to be really enabled in the main thread, so, schedule
+        # it to run in the main thread.
+        self.exec_queue.put(do_change_variable)
 
     def _findFrame(self, thread_id, frame_id):
         '''
@@ -431,39 +414,48 @@
         Used to show console with variables connection.
         Mainly, monkey-patches things in the debugger structure so that the debugger protocol works.
         '''
-        try:
-            # Try to import the packages needed to attach the debugger
-            import pydevd
-            import pydevd_vars
-            import threading
-        except:
-            # This happens on Jython embedded in host eclipse
-            import traceback;traceback.print_exc()
-            return ('pydevd is not available, cannot connect',)
+        def do_connect_to_debugger():
+            try:
+                # Try to import the packages needed to attach the debugger
+                import pydevd
+                if USE_LIB_COPY:
+                    import _pydev_threading as threading
+                else:
+                    import threading
 
-        import pydev_localhost
-        threading.currentThread().__pydevd_id__ = "console_main"
+            except:
+                # This happens on Jython embedded in host eclipse
+                traceback.print_exc()
+                sys.stderr.write('pydevd is not available, cannot connect\n',)
 
-        self.orig_findFrame = pydevd_vars.findFrame
-        pydevd_vars.findFrame = self._findFrame
+            import pydev_localhost
+            threading.currentThread().__pydevd_id__ = "console_main"
 
-        self.debugger = pydevd.PyDB()
-        try:
-            self.debugger.connect(pydev_localhost.get_localhost(), debuggerPort)
-            self.debugger.prepareToRun()
-            import pydevd_tracing
-            pydevd_tracing.SetTrace(None)
-        except:
-            import traceback;traceback.print_exc()
-            return ('Failed to connect to target debugger.')
+            self.orig_findFrame = pydevd_vars.findFrame
+            pydevd_vars.findFrame = self._findFrame
 
-        # Register to process commands when idle
-        self.debugrunning = False
-        try:
-            self.server.setDebugHook(self.debugger.processInternalCommands)
-        except:
-            import traceback;traceback.print_exc()
-            return ('Version of Python does not support debuggable Interactive Console.')
+            self.debugger = pydevd.PyDB()
+            try:
+                self.debugger.connect(pydev_localhost.get_localhost(), debuggerPort)
+                self.debugger.prepareToRun()
+                import pydevd_tracing
+                pydevd_tracing.SetTrace(None)
+            except:
+                traceback.print_exc()
+                sys.stderr.write('Failed to connect to target debugger.\n')
+
+            # Register to process commands when idle
+            self.debugrunning = False
+            try:
+                import pydevconsole
+                pydevconsole.set_debug_hook(self.debugger.processInternalCommands)
+            except:
+                traceback.print_exc()
+                sys.stderr.write('Version of Python does not support debuggable Interactive Console.\n')
+
+        # Important: it has to be really enabled in the main thread, so, schedule
+        # it to run in the main thread.
+        self.exec_queue.put(do_connect_to_debugger)
 
         return ('connect complete',)
 
@@ -476,19 +468,24 @@
             As with IPython, enabling multiple GUIs isn't an error, but
             only the last one's main loop runs and it may not work
         '''
-        from pydev_versioncheck import versionok_for_gui
-        if versionok_for_gui():
-            try:
-                from pydev_ipython.inputhook import enable_gui
-                enable_gui(guiname)
-            except:
-                sys.stderr.write("Failed to enable GUI event loop integration for '%s'\n" % guiname)
-                import traceback;traceback.print_exc()
-        elif guiname not in ['none', '', None]:
-            # Only print a warning if the guiname was going to do something
-            sys.stderr.write("PyDev console: Python version does not support GUI event loop integration for '%s'\n" % guiname)
-        # Return value does not matter, so return back what was sent
-        return guiname
+        def do_enable_gui():
+            from pydev_versioncheck import versionok_for_gui
+            if versionok_for_gui():
+                try:
+                    from pydev_ipython.inputhook import enable_gui
+                    enable_gui(guiname)
+                except:
+                    sys.stderr.write("Failed to enable GUI event loop integration for '%s'\n" % guiname)
+                    traceback.print_exc()
+            elif guiname not in ['none', '', None]:
+                # Only print a warning if the guiname was going to do something
+                sys.stderr.write("PyDev console: Python version does not support GUI event loop integration for '%s'\n" % guiname)
+            # Return value does not matter, so return back what was sent
+            return guiname
+
+        # Important: it has to be really enabled in the main thread, so, schedule
+        # it to run in the main thread.
+        self.exec_queue.put(do_enable_gui)
 
 #=======================================================================================================================
 # FakeFrame
diff --git a/python/helpers/pydev/pydev_coverage.py b/python/helpers/pydev/pydev_coverage.py
new file mode 100644
index 0000000..1690f8c
--- /dev/null
+++ b/python/helpers/pydev/pydev_coverage.py
@@ -0,0 +1,54 @@
+def execute():
+    import os
+    import sys
+    
+    files = None
+    if 'combine' not in sys.argv:
+    
+        if '--pydev-analyze' in sys.argv:
+                
+            #Ok, what we want here is having the files passed through stdin (because
+            #there may be too many files for passing in the command line -- we could
+            #just pass a dir and make the find files here, but as that's already 
+            #given in the java side, let's just gather that info here).
+            sys.argv.remove('--pydev-analyze')
+            try:
+                s = raw_input()
+            except:
+                s = input()
+            s = s.replace('\r', '')
+            s = s.replace('\n', '')
+            files = s.split('|')
+            files = [v for v in files if len(v) > 0]
+            
+            #Note that in this case we'll already be in the working dir with the coverage files, so, the
+            #coverage file location is not passed.
+            
+        else:
+            #For all commands, the coverage file is configured in pydev, and passed as the first argument
+            #in the command line, so, let's make sure this gets to the coverage module.            
+            os.environ['COVERAGE_FILE'] = sys.argv[1]
+            del sys.argv[1]
+        
+    try:
+        import coverage #@UnresolvedImport
+    except:
+        sys.stderr.write('Error: coverage module could not be imported\n')
+        sys.stderr.write('Please make sure that the coverage module (http://nedbatchelder.com/code/coverage/)\n')
+        sys.stderr.write('is properly installed in your interpreter: %s\n' % (sys.executable,))
+        
+        import traceback;traceback.print_exc()
+        return
+    
+    #print(coverage.__version__) TODO: Check if the version is a version we support (should be at least 3.4) -- note that maybe the attr is not there.
+    from coverage.cmdline import main #@UnresolvedImport
+
+    if files is not None:        
+        sys.argv.append('-r')
+        sys.argv.append('-m')
+        sys.argv += files
+        
+    main()
+
+if __name__ == '__main__':
+    execute()
\ No newline at end of file
diff --git a/python/helpers/pydev/pydev_imports.py b/python/helpers/pydev/pydev_imports.py
index 0685875..69804a8 100644
--- a/python/helpers/pydev/pydev_imports.py
+++ b/python/helpers/pydev/pydev_imports.py
@@ -1,40 +1,52 @@
-from pydevd_constants import USE_LIB_COPY
+from pydevd_constants import USE_LIB_COPY, izip
+
+
 try:
     try:
         if USE_LIB_COPY:
-            import _pydev_xmlrpclib as xmlrpclib
+            from _pydev_imps import _pydev_xmlrpclib as xmlrpclib
         else:
             import xmlrpclib
     except ImportError:
         import xmlrpc.client as xmlrpclib
 except ImportError:
-    import _pydev_xmlrpclib as xmlrpclib
+    from _pydev_imps import _pydev_xmlrpclib as xmlrpclib
+
+
 try:
     try:
         if USE_LIB_COPY:
-            from _pydev_SimpleXMLRPCServer import SimpleXMLRPCServer
+            from _pydev_imps._pydev_SimpleXMLRPCServer import SimpleXMLRPCServer
         else:
             from SimpleXMLRPCServer import SimpleXMLRPCServer
     except ImportError:
         from xmlrpc.server import SimpleXMLRPCServer
 except ImportError:
-    from _pydev_SimpleXMLRPCServer import SimpleXMLRPCServer
+    from _pydev_imps._pydev_SimpleXMLRPCServer import SimpleXMLRPCServer
+
+
+
 try:
     from StringIO import StringIO
 except ImportError:
     from io import StringIO
+
+
 try:
     execfile=execfile #Not in Py3k
 except NameError:
-    from _pydev_execfile import execfile
+    from _pydev_imps._pydev_execfile import execfile
+
+
 try:
     if USE_LIB_COPY:
-        import _pydev_Queue as _queue
+        from _pydev_imps import _pydev_Queue as _queue
     else:
         import Queue as _queue
 except:
     import queue as _queue #@UnresolvedImport
 
+
 try:
     from pydevd_exec import Exec
 except:
@@ -80,7 +92,7 @@
             return start
 
         i = 0
-        for start_seg, dest_seg in zip(orig_list, dest_list):
+        for start_seg, dest_seg in izip(orig_list, dest_list):
             if start_seg != os.path.normcase(dest_seg):
                 break
             i += 1
diff --git a/python/helpers/pydev/pydev_ipython/inputhookglut.py b/python/helpers/pydev/pydev_ipython/inputhookglut.py
index f0683ba..e1e67e9 100644
--- a/python/helpers/pydev/pydev_ipython/inputhookglut.py
+++ b/python/helpers/pydev/pydev_ipython/inputhookglut.py
@@ -29,9 +29,8 @@
 #-----------------------------------------------------------------------------
 # Imports
 #-----------------------------------------------------------------------------
-import os
 import sys
-import time
+from _pydev_imps import _pydev_time as time
 import signal
 import OpenGL.GLUT as glut
 import OpenGL.platform as platform
diff --git a/python/helpers/pydev/pydev_ipython/inputhookpyglet.py b/python/helpers/pydev/pydev_ipython/inputhookpyglet.py
index 0cbb87f..64dd2e5 100644
--- a/python/helpers/pydev/pydev_ipython/inputhookpyglet.py
+++ b/python/helpers/pydev/pydev_ipython/inputhookpyglet.py
@@ -20,9 +20,8 @@
 # Imports
 #-----------------------------------------------------------------------------
 
-import os
 import sys
-import time
+from _pydev_imps import _pydev_time as time
 from timeit import default_timer as clock
 import pyglet
 from pydev_ipython.inputhook import stdin_ready
diff --git a/python/helpers/pydev/pydev_ipython/inputhookqt4.py b/python/helpers/pydev/pydev_ipython/inputhookqt4.py
index f4f32a3..27598fa 100644
--- a/python/helpers/pydev/pydev_ipython/inputhookqt4.py
+++ b/python/helpers/pydev/pydev_ipython/inputhookqt4.py
@@ -18,7 +18,13 @@
 
 import os
 import signal
-import threading
+
+from pydevd_constants import USE_LIB_COPY
+if USE_LIB_COPY:
+    import _pydev_threading as threading
+else:
+    import threading
+
 
 from pydev_ipython.qt_for_kernel import QtCore, QtGui
 from pydev_ipython.inputhook import allow_CTRL_C, ignore_CTRL_C, stdin_ready
diff --git a/python/helpers/pydev/pydev_ipython/inputhookwx.py b/python/helpers/pydev/pydev_ipython/inputhookwx.py
index 6640884..19ffdc7 100644
--- a/python/helpers/pydev/pydev_ipython/inputhookwx.py
+++ b/python/helpers/pydev/pydev_ipython/inputhookwx.py
@@ -19,7 +19,7 @@
 
 import sys
 import signal
-import time
+from _pydev_imps import _pydev_time as time
 from timeit import default_timer as clock
 import wx
 
diff --git a/python/helpers/pydev/pydev_ipython_console.py b/python/helpers/pydev/pydev_ipython_console.py
index 859157e..0c51dfe 100644
--- a/python/helpers/pydev/pydev_ipython_console.py
+++ b/python/helpers/pydev/pydev_ipython_console.py
@@ -1,20 +1,15 @@
 import sys
 from pydev_console_utils import BaseInterpreterInterface
-import re
 
 import os
 
 os.environ['TERM'] = 'emacs' #to use proper page_more() for paging
 
 
-#Uncomment to force PyDev standard shell.   
-#raise ImportError()
+# Uncomment to force PyDev standard shell.
+# raise ImportError()
 
-try:
-    #IPython 0.11 broke compatibility...
-    from pydev_ipython_console_011 import PyDevFrontEnd
-except:
-    from pydev_ipython_console_010 import PyDevFrontEnd
+from pydev_ipython_console_011 import PyDevFrontEnd
 
 #=======================================================================================================================
 # InterpreterInterface
@@ -28,7 +23,7 @@
         BaseInterpreterInterface.__init__(self, mainThread)
         self.client_port = client_port
         self.host = host
-        self.interpreter = PyDevFrontEnd()
+        self.interpreter = PyDevFrontEnd(host, client_port)
         self._input_error_printed = False
         self.notification_succeeded = False
         self.notification_tries = 0
@@ -57,58 +52,11 @@
 
 
     def getCompletions(self, text, act_tok):
-        try:
-            ipython_completion = text.startswith('%')
-            if not ipython_completion:
-                s = re.search(r'\bcd\b', text)
-                if s is not None and s.start() == 0:
-                    ipython_completion = True
-
-            if text is None:
-                text = ""
-
-            TYPE_LOCAL = '9'
-            _line, completions = self.interpreter.complete(text)
-
-            ret = []
-            append = ret.append
-            for completion in completions:
-                if completion.startswith('%'):
-                    append((completion[1:], '', '%', TYPE_LOCAL))
-                else:
-                    append((completion, '', '', TYPE_LOCAL))
-
-            if ipython_completion:
-                return ret
-
-            #Otherwise, use the default PyDev completer (to get nice icons)
-            from _pydev_completer import Completer
-
-            completer = Completer(self.getNamespace(), None)
-            completions = completer.complete(act_tok)
-            cset = set()
-            for c in completions:
-                cset.add(c[0])
-            for c in ret:
-                if c[0] not in cset:
-                    completions.append(c)
-
-            return completions
-
-        except:
-            import traceback
-
-            traceback.print_exc()
-            return []
+        return self.interpreter.getCompletions(text, act_tok)
 
     def close(self):
         sys.exit(0)
 
-    def ipython_editor(self, file, line):
-        server = self.get_server()
-
-        if server is not None:
-            return server.IPythonEditor(os.path.realpath(file), line)
 
     def notify_about_magic(self):
         if not self.notification_succeeded:
diff --git a/python/helpers/pydev/pydev_ipython_console_010.py b/python/helpers/pydev/pydev_ipython_console_010.py
deleted file mode 100644
index e093fef..0000000
--- a/python/helpers/pydev/pydev_ipython_console_010.py
+++ /dev/null
@@ -1,129 +0,0 @@
-from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
-from pydev_console_utils import Null
-import sys
-original_stdout = sys.stdout
-original_stderr = sys.stderr
-
-
-#=======================================================================================================================
-# PyDevFrontEnd
-#=======================================================================================================================
-class PyDevFrontEnd(PrefilterFrontEnd):
-
-
-    def __init__(self, *args, **kwargs):        
-        PrefilterFrontEnd.__init__(self, *args, **kwargs)
-        #Disable the output trap: we want all that happens to go to the output directly
-        self.shell.output_trap = Null()
-        self._curr_exec_lines = []
-        self._continuation_prompt = ''
-        
-        
-    def capture_output(self):
-        pass
-    
-    
-    def release_output(self):
-        pass
-    
-    
-    def continuation_prompt(self):
-        return self._continuation_prompt
-    
-    
-    def write(self, txt, refresh=True):
-        original_stdout.write(txt)
-        
-
-    def new_prompt(self, prompt):
-        self.input_buffer = ''
-        #The java side takes care of this part.
-        #self.write(prompt)
-        
-        
-    def show_traceback(self):
-        import traceback;traceback.print_exc()
-        
-        
-    def write_out(self, txt, *args, **kwargs):
-        original_stdout.write(txt)
-    
-    
-    def write_err(self, txt, *args, **kwargs):
-        original_stderr.write(txt)
-        
-        
-    def getNamespace(self):
-        return self.shell.user_ns
-
-
-    def is_complete(self, string):
-        #Based on IPython 0.10.1
-
-        if string in ('', '\n'):
-            # Prefiltering, eg through ipython0, may return an empty
-            # string although some operations have been accomplished. We
-            # thus want to consider an empty string as a complete
-            # statement.
-            return True
-        else:
-            try:
-                # Add line returns here, to make sure that the statement is
-                # complete (except if '\' was used).
-                # This should probably be done in a different place (like
-                # maybe 'prefilter_input' method? For now, this works.
-                clean_string = string.rstrip('\n')
-                if not clean_string.endswith('\\'): clean_string += '\n\n'
-                is_complete = codeop.compile_command(clean_string,
-                                                     "<string>", "exec")
-            except Exception:
-                # XXX: Hack: return True so that the
-                # code gets executed and the error captured.
-                is_complete = True
-            return is_complete
-    
-    
-    def addExec(self, line):
-        if self._curr_exec_lines:
-            if not line:
-                self._curr_exec_lines.append(line)
-
-                #Would be the line below, but we've set the continuation_prompt to ''.
-                #buf = self.continuation_prompt() + ('\n' + self.continuation_prompt()).join(self._curr_exec_lines)
-                buf = '\n'.join(self._curr_exec_lines)
-
-                self.input_buffer = buf + '\n'
-                if self._on_enter():
-                    del self._curr_exec_lines[:]
-                    return False #execute complete (no more)
-
-                return True #needs more
-            else:
-                self._curr_exec_lines.append(line)
-                return True #needs more
-
-        else:
-
-            self.input_buffer = line
-            if not self._on_enter():
-                #Did not execute
-                self._curr_exec_lines.append(line)
-                return True #needs more
-
-            return False #execute complete (no more)
-
-    def update(self, globals, locals):
-        locals['_oh'] = self.shell.user_ns['_oh']
-        locals['_ip'] = self.shell.user_ns['_ip']
-        self.shell.user_global_ns = globals
-        self.shell.user_ns = locals
-
-    def is_automagic(self):
-        if self.ipython0.rc.automagic:
-            return True
-        else:
-            return False
-
-    def get_greeting_msg(self):
-        return 'PyDev console: using IPython 0.10\n'
-
diff --git a/python/helpers/pydev/pydev_ipython_console_011.py b/python/helpers/pydev/pydev_ipython_console_011.py
index 198d40f..54458e7 100644
--- a/python/helpers/pydev/pydev_ipython_console_011.py
+++ b/python/helpers/pydev/pydev_ipython_console_011.py
@@ -1,23 +1,295 @@
+# TODO that would make IPython integration better
+# - show output other times then when enter was pressed
+# - support proper exit to allow IPython to cleanup (e.g. temp files created with %edit)
+# - support Ctrl-D (Ctrl-Z on Windows)
+# - use IPython (numbered) prompts in PyDev
+# - better integration of IPython and PyDev completions
+# - some of the semantics on handling the code completion are not correct:
+#   eg: Start a line with % and then type c should give %cd as a completion by it doesn't
+#       however type %c and request completions and %cd is given as an option
+#   eg: Completing a magic when user typed it without the leading % causes the % to be inserted
+#       to the left of what should be the first colon.
+"""Interface to TerminalInteractiveShell for PyDev Interactive Console frontend
+   for IPython 0.11 to 1.0+.
+"""
+
+from __future__ import print_function
+
+import os
+import codeop
+
+from IPython.core.error import UsageError
+from IPython.core.inputsplitter import IPythonInputSplitter
+from IPython.core.completer import IPCompleter
+from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
+from IPython.core.usage import default_banner_parts
+from IPython.utils.strdispatch import StrDispatch
+import IPython.core.release as IPythonRelease
 try:
     from IPython.terminal.interactiveshell import TerminalInteractiveShell
 except ImportError:
+    # Versions of IPython [0.11,1.0) had an extra hierarchy level
     from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
-from IPython.utils import io
-import sys
-import codeop, re
-original_stdout = sys.stdout
-original_stderr = sys.stderr
+from IPython.utils.traitlets import CBool, Unicode
 from IPython.core import release
 
+from pydev_imports import xmlrpclib
 
-#=======================================================================================================================
-# _showtraceback
-#=======================================================================================================================
-def _showtraceback(*args, **kwargs):
-    import traceback;traceback.print_exc()
-    
-    
-    
+pydev_banner_parts = [
+    '\n',
+    'PyDev -- Python IDE for Eclipse\n',  # TODO can we get a version number in here?
+    'For help on using PyDev\'s Console see http://pydev.org/manual_adv_interactive_console.html\n',
+]
+
+default_pydev_banner_parts = default_banner_parts + pydev_banner_parts
+
+default_pydev_banner = ''.join(default_pydev_banner_parts)
+
+def show_in_pager(self, strng):
+    """ Run a string through pager """
+    # On PyDev we just output the string, there are scroll bars in the console
+    # to handle "paging". This is the same behaviour as when TERM==dump (see
+    # page.py)
+    print(strng)
+
+def create_editor_hook(pydev_host, pydev_client_port):
+    def call_editor(self, filename, line=0, wait=True):
+        """ Open an editor in PyDev """
+        if line is None:
+            line = 0
+
+        # Make sure to send an absolution path because unlike most editor hooks
+        # we don't launch a process. This is more like what happens in the zmqshell
+        filename = os.path.abspath(filename)
+
+        # Tell PyDev to open the editor
+        server = xmlrpclib.Server('http://%s:%s' % (pydev_host, pydev_client_port))
+        server.IPythonEditor(filename, str(line))
+
+        if wait:
+            try:
+                raw_input("Press Enter when done editing:")
+            except NameError:
+                input("Press Enter when done editing:")
+    return call_editor
+
+
+
+class PyDevIPCompleter(IPCompleter):
+
+    def __init__(self, *args, **kwargs):
+        """ Create a Completer that reuses the advanced completion support of PyDev
+            in addition to the completion support provided by IPython """
+        IPCompleter.__init__(self, *args, **kwargs)
+        # Use PyDev for python matches, see getCompletions below
+        self.matchers.remove(self.python_matches)
+
+class PyDevTerminalInteractiveShell(TerminalInteractiveShell):
+    banner1 = Unicode(default_pydev_banner, config=True,
+        help="""The part of the banner to be printed before the profile"""
+    )
+
+    # TODO term_title: (can PyDev's title be changed???, see terminal.py for where to inject code, in particular set_term_title as used by %cd)
+    # for now, just disable term_title
+    term_title = CBool(False)
+
+    # Note in version 0.11 there is no guard in the IPython code about displaying a
+    # warning, so with 0.11 you get:
+    #  WARNING: Readline services not available or not loaded.
+    #  WARNING: The auto-indent feature requires the readline library
+    # Disable readline, readline type code is all handled by PyDev (on Java side)
+    readline_use = CBool(False)
+    # autoindent has no meaning in PyDev (PyDev always handles that on the Java side),
+    # and attempting to enable it will print a warning in the absence of readline.
+    autoindent = CBool(False)
+    # Force console to not give warning about color scheme choice and default to NoColor.
+    # TODO It would be nice to enable colors in PyDev but:
+    # - The PyDev Console (Eclipse Console) does not support the full range of colors, so the
+    #   effect isn't as nice anyway at the command line
+    # - If done, the color scheme should default to LightBG, but actually be dependent on
+    #   any settings the user has (such as if a dark theme is in use, then Linux is probably
+    #   a better theme).
+    colors_force = CBool(True)
+    colors = Unicode("NoColor")
+
+    # In the PyDev Console, GUI control is done via hookable XML-RPC server
+    @staticmethod
+    def enable_gui(gui=None, app=None):
+        """Switch amongst GUI input hooks by name.
+        """
+        # Deferred import
+        from pydev_ipython.inputhook import enable_gui as real_enable_gui
+        try:
+            return real_enable_gui(gui, app)
+        except ValueError as e:
+            raise UsageError("%s" % e)
+
+    #-------------------------------------------------------------------------
+    # Things related to hooks
+    #-------------------------------------------------------------------------
+
+    def init_hooks(self):
+        super(PyDevTerminalInteractiveShell, self).init_hooks()
+        self.set_hook('show_in_pager', show_in_pager)
+
+    #-------------------------------------------------------------------------
+    # Things related to exceptions
+    #-------------------------------------------------------------------------
+
+    def showtraceback(self, exc_tuple=None, filename=None, tb_offset=None,
+                  exception_only=False):
+        # IPython does a lot of clever stuff with Exceptions. However mostly
+        # it is related to IPython running in a terminal instead of an IDE.
+        # (e.g. it prints out snippets of code around the stack trace)
+        # PyDev does a lot of clever stuff too, so leave exception handling
+        # with default print_exc that PyDev can parse and do its clever stuff
+        # with (e.g. it puts links back to the original source code)
+        import traceback;traceback.print_exc()
+
+
+    #-------------------------------------------------------------------------
+    # Things related to text completion
+    #-------------------------------------------------------------------------
+
+    # The way to construct an IPCompleter changed in most versions,
+    # so we have a custom, per version implementation of the construction
+
+    def _new_completer_011(self):
+        return PyDevIPCompleter(self,
+                             self.user_ns,
+                             self.user_global_ns,
+                             self.readline_omit__names,
+                             self.alias_manager.alias_table,
+                             self.has_readline)
+
+
+    def _new_completer_012(self):
+        completer = PyDevIPCompleter(shell=self,
+                             namespace=self.user_ns,
+                             global_namespace=self.user_global_ns,
+                             alias_table=self.alias_manager.alias_table,
+                             use_readline=self.has_readline,
+                             config=self.config,
+                             )
+        self.configurables.append(completer)
+        return completer
+
+
+    def _new_completer_100(self):
+        completer = PyDevIPCompleter(shell=self,
+                             namespace=self.user_ns,
+                             global_namespace=self.user_global_ns,
+                             alias_table=self.alias_manager.alias_table,
+                             use_readline=self.has_readline,
+                             parent=self,
+                             )
+        self.configurables.append(completer)
+        return completer
+
+    def _new_completer_200(self):
+        # As of writing this, IPython 2.0.0 is in dev mode so subject to change
+        completer = PyDevIPCompleter(shell=self,
+                             namespace=self.user_ns,
+                             global_namespace=self.user_global_ns,
+                             use_readline=self.has_readline,
+                             parent=self,
+                             )
+        self.configurables.append(completer)
+        return completer
+
+
+
+    def init_completer(self):
+        """Initialize the completion machinery.
+
+        This creates a completer that provides the completions that are
+        IPython specific. We use this to supplement PyDev's core code
+        completions.
+        """
+        # PyDev uses its own completer and custom hooks so that it uses
+        # most completions from PyDev's core completer which provides
+        # extra information.
+        # See getCompletions for where the two sets of results are merged
+
+        from IPython.core.completerlib import magic_run_completer, cd_completer
+        try:
+            from IPython.core.completerlib import reset_completer
+        except ImportError:
+            # reset_completer was added for rel-0.13
+            reset_completer = None
+
+        if IPythonRelease._version_major >= 2:
+            self.Completer = self._new_completer_200()
+        elif IPythonRelease._version_major >= 1:
+            self.Completer = self._new_completer_100()
+        elif IPythonRelease._version_minor >= 12:
+            self.Completer = self._new_completer_012()
+        else:
+            self.Completer = self._new_completer_011()
+
+        # Add custom completers to the basic ones built into IPCompleter
+        sdisp = self.strdispatchers.get('complete_command', StrDispatch())
+        self.strdispatchers['complete_command'] = sdisp
+        self.Completer.custom_completers = sdisp
+
+        self.set_hook('complete_command', magic_run_completer, str_key='%run')
+        self.set_hook('complete_command', cd_completer, str_key='%cd')
+        if reset_completer:
+            self.set_hook('complete_command', reset_completer, str_key='%reset')
+
+        # Only configure readline if we truly are using readline.  IPython can
+        # do tab-completion over the network, in GUIs, etc, where readline
+        # itself may be absent
+        if self.has_readline:
+            self.set_readline_completer()
+
+
+    #-------------------------------------------------------------------------
+    # Things related to aliases
+    #-------------------------------------------------------------------------
+
+    def init_alias(self):
+        # InteractiveShell defines alias's we want, but TerminalInteractiveShell defines
+        # ones we don't. So don't use super and instead go right to InteractiveShell
+        InteractiveShell.init_alias(self)
+
+    #-------------------------------------------------------------------------
+    # Things related to exiting
+    #-------------------------------------------------------------------------
+    def ask_exit(self):
+        """ Ask the shell to exit. Can be overiden and used as a callback. """
+        # TODO PyDev's console does not have support from the Python side to exit
+        # the console. If user forces the exit (with sys.exit()) then the console
+        # simply reports errors. e.g.:
+        # >>> import sys
+        # >>> sys.exit()
+        # Failed to create input stream: Connection refused
+        # >>>
+        # Console already exited with value: 0 while waiting for an answer.
+        # Error stream:
+        # Output stream:
+        # >>>
+        #
+        # Alternatively if you use the non-IPython shell this is what happens
+        # >>> exit()
+        # <type 'exceptions.SystemExit'>:None
+        # >>>
+        # <type 'exceptions.SystemExit'>:None
+        # >>>
+        #
+        super(PyDevTerminalInteractiveShell, self).ask_exit()
+        print('To exit the PyDev Console, terminate the console within Eclipse.')
+
+    #-------------------------------------------------------------------------
+    # Things related to magics
+    #-------------------------------------------------------------------------
+
+    def init_magics(self):
+        super(PyDevTerminalInteractiveShell, self).init_magics()
+        # TODO Any additional magics for PyDev?
+
+InteractiveShellABC.register(PyDevTerminalInteractiveShell)  # @UndefinedVariable
+
 #=======================================================================================================================
 # PyDevFrontEnd
 #=======================================================================================================================
@@ -25,49 +297,23 @@
 
     version = release.__version__
 
-
-    def __init__(self, *args, **kwargs):        
-        #Initialization based on: from IPython.testing.globalipapp import start_ipython
-        
-        self._curr_exec_line = 0
-        # Store certain global objects that IPython modifies
-        _displayhook = sys.displayhook
-        _excepthook = sys.excepthook
-
-        class ClosablePyDevTerminalInteractiveShell(TerminalInteractiveShell):
-            '''Override ask_exit() method for correct exit, exit(), etc. handling.'''
-            def ask_exit(self):
-                sys.exit()
+    def __init__(self, pydev_host, pydev_client_port, *args, **kwarg):
 
         # Create and initialize our IPython instance.
-        shell = ClosablePyDevTerminalInteractiveShell.instance()
+        self.ipython = PyDevTerminalInteractiveShell.instance()
 
-        shell.showtraceback = _showtraceback
-        # IPython is ready, now clean up some global state...
-        
-        # Deactivate the various python system hooks added by ipython for
-        # interactive convenience so we don't confuse the doctest system
-        sys.displayhook = _displayhook
-        sys.excepthook = _excepthook
-    
-        # So that ipython magics and aliases can be doctested (they work by making
-        # a call into a global _ip object).  Also make the top-level get_ipython
-        # now return this without recursively calling here again.
-        get_ipython = shell.get_ipython
-        try:
-            import __builtin__
-        except:
-            import builtins as __builtin__
-        __builtin__._ip = shell
-        __builtin__.get_ipython = get_ipython
+        # Back channel to PyDev to open editors (in the future other
+        # info may go back this way. This is the same channel that is
+        # used to get stdin, see StdIn in pydev_console_utils)
+        self.ipython.set_hook('editor', create_editor_hook(pydev_host, pydev_client_port))
 
-        # We want to print to stdout/err as usual.
-        io.stdout = original_stdout
-        io.stderr = original_stderr
+        # Display the IPython banner, this has version info and
+        # help info
+        self.ipython.show_banner()
 
 
+        self._curr_exec_line = 0
         self._curr_exec_lines = []
-        self.ipython = shell
 
 
     def update(self, globals, locals):
@@ -95,7 +341,7 @@
 
     def is_complete(self, string):
         #Based on IPython 0.10.1
-         
+
         if string in ('', '\n'):
             # Prefiltering, eg through ipython0, may return an empty
             # string although some operations have been accomplished. We
@@ -109,20 +355,64 @@
                 # This should probably be done in a different place (like
                 # maybe 'prefilter_input' method? For now, this works.
                 clean_string = string.rstrip('\n')
-                if not clean_string.endswith('\\'): clean_string += '\n\n' 
-                is_complete = codeop.compile_command(clean_string,
-                            "<string>", "exec")
+                if not clean_string.endswith('\\'):
+                    clean_string += '\n\n'
+
+                is_complete = codeop.compile_command(
+                    clean_string,
+                    "<string>",
+                    "exec"
+                )
             except Exception:
                 # XXX: Hack: return True so that the
                 # code gets executed and the error captured.
                 is_complete = True
             return is_complete
-        
-        
+
+
+    def getCompletions(self, text, act_tok):
+        # Get completions from IPython and from PyDev and merge the results
+        # IPython only gives context free list of completions, while PyDev
+        # gives detailed information about completions.
+        try:
+            TYPE_IPYTHON = '11'
+            TYPE_IPYTHON_MAGIC = '12'
+            _line, ipython_completions = self.complete(text)
+
+            from _pydev_completer import Completer
+            completer = Completer(self.getNamespace(), None)
+            ret = completer.complete(act_tok)
+            append = ret.append
+            ip = self.ipython
+            pydev_completions = set([f[0] for f in ret])
+            for ipython_completion in ipython_completions:
+
+                #PyCharm was not expecting completions with '%'...
+                #Could be fixed in the backend, but it's probably better
+                #fixing it at PyCharm.
+                #if ipython_completion.startswith('%'):
+                #    ipython_completion = ipython_completion[1:]
+
+                if ipython_completion not in pydev_completions:
+                    pydev_completions.add(ipython_completion)
+                    inf = ip.object_inspect(ipython_completion)
+                    if inf['type_name'] == 'Magic function':
+                        pydev_type = TYPE_IPYTHON_MAGIC
+                    else:
+                        pydev_type = TYPE_IPYTHON
+                    pydev_doc = inf['docstring']
+                    if pydev_doc is None:
+                        pydev_doc = ''
+                    append((ipython_completion, pydev_doc, '', pydev_type))
+            return ret
+        except:
+            import traceback;traceback.print_exc()
+            return []
+
+
     def getNamespace(self):
         return self.ipython.user_ns
 
-    
     def addExec(self, line):
         if self._curr_exec_lines:
             self._curr_exec_lines.append(line)
@@ -155,5 +445,21 @@
         return self.ipython.automagic
 
     def get_greeting_msg(self):
-        return 'PyDev console: using IPython %s' % self.version
+        return 'PyDev console: using IPython %s\n' % self.version
 
+
+# If we have succeeded in importing this module, then monkey patch inputhook
+# in IPython to redirect to PyDev's version. This is essential to make
+# %gui in 0.11 work (0.12+ fixes it by calling self.enable_gui, which is implemented
+# above, instead of inputhook.enable_gui).
+# See testGui (test_pydev_ipython_011.TestRunningCode) which fails on 0.11 without
+# this patch
+import IPython.lib.inputhook
+import pydev_ipython.inputhook
+IPython.lib.inputhook.enable_gui = pydev_ipython.inputhook.enable_gui
+# In addition to enable_gui, make all publics in pydev_ipython.inputhook replace
+# the IPython versions. This enables the examples in IPython's examples/lib/gui-*
+# to operate properly because those examples don't use %gui magic and instead
+# rely on using the inputhooks directly.
+for name in pydev_ipython.inputhook.__all__:
+    setattr(IPython.lib.inputhook, name, getattr(pydev_ipython.inputhook, name))
diff --git a/python/helpers/pydev/pydev_localhost.py b/python/helpers/pydev/pydev_localhost.py
index 4e7a4d9..13c4d02 100644
--- a/python/helpers/pydev/pydev_localhost.py
+++ b/python/helpers/pydev/pydev_localhost.py
@@ -1,21 +1,18 @@
-import pydevd_constants
-if pydevd_constants.USE_LIB_COPY:
-    import _pydev_socket as socket
-else:
-    import socket
+
+from _pydev_imps import _pydev_socket as socket
 
 _cache = None
 def get_localhost():
     '''
     Should return 127.0.0.1 in ipv4 and ::1 in ipv6
-    
+
     localhost is not used because on windows vista/windows 7, there can be issues where the resolving doesn't work
-    properly and takes a lot of time (had this issue on the pyunit server). 
-    
+    properly and takes a lot of time (had this issue on the pyunit server).
+
     Using the IP directly solves the problem.
     '''
     #TODO: Needs better investigation!
-    
+
     global _cache
     if _cache is None:
         try:
diff --git a/python/helpers/pydev/pydev_monkey.py b/python/helpers/pydev/pydev_monkey.py
index ed6fea5..2b12ed2 100644
--- a/python/helpers/pydev/pydev_monkey.py
+++ b/python/helpers/pydev/pydev_monkey.py
@@ -1,10 +1,11 @@
 import os
-import shlex
 import sys
 import pydev_log
 import traceback
 
-helpers = os.path.dirname(__file__).replace('\\', '/')
+pydev_src_dir = os.path.dirname(__file__)
+
+from pydevd_constants import xrange
 
 def is_python(path):
     if path.endswith("'") or path.endswith('"'):
@@ -38,7 +39,9 @@
 
                 if port is not None:
                     new_args.extend(args)
-                    new_args[indC + 1] = "import sys; sys.path.append('%s'); import pydevd; pydevd.settrace(host='%s', port=%s, suspend=False); %s"%(helpers, host, port, args[indC + 1])
+                    new_args[indC + 1] = ("import sys; sys.path.append(r'%s'); import pydevd; "
+                        "pydevd.settrace(host='%s', port=%s, suspend=False, trace_only_current_thread=False, patch_multiprocessing=True); %s") % (
+                        pydev_src_dir, host, port, args[indC + 1])
                     return new_args
             else:
                 new_args.append(args[0])
@@ -52,14 +55,14 @@
                 new_args.append(args[i])
             else:
                 break
-            i+=1
+            i += 1
 
         if args[i].endswith('pydevd.py'): #no need to add pydevd twice
             return args
 
         for x in sys.original_argv:
             if sys.platform == "win32" and not x.endswith('"'):
-                arg = '"%s"'%x
+                arg = '"%s"' % x
             else:
                 arg = x
             new_args.append(arg)
@@ -68,7 +71,7 @@
 
         while i < len(args):
             new_args.append(args[i])
-            i+=1
+            i += 1
 
         return new_args
     except:
@@ -82,26 +85,101 @@
         if x.startswith('"') and x.endswith('"'):
             quoted_args.append(x)
         else:
+            x = x.replace('"', '\\"')
             quoted_args.append('"%s"' % x)
 
     return ' '.join(quoted_args)
 
-def remove_quotes(str):
-    if str.startswith('"') and str.endswith('"'):
-        return str[1:-1]
-    else:
-        return str
 
-def str_to_args(str):
-    return [remove_quotes(x) for x in shlex.split(str)]
+def str_to_args_windows(args):
+    # see http:#msdn.microsoft.com/en-us/library/a1y7w461.aspx
+    result = []
+
+    DEFAULT = 0
+    ARG = 1
+    IN_DOUBLE_QUOTE = 2
+
+    state = DEFAULT
+    backslashes = 0
+    buf = ''
+
+    args_len = len(args)
+    for i in xrange(args_len):
+        ch = args[i]
+        if (ch == '\\'):
+            backslashes+=1
+            continue
+        elif (backslashes != 0):
+            if ch == '"':
+                while backslashes >= 2:
+                    backslashes -= 2
+                    buf += '\\'
+                if (backslashes == 1):
+                    if (state == DEFAULT):
+                        state = ARG
+
+                    buf += '"'
+                    backslashes = 0
+                    continue
+                # else fall through to switch
+            else:
+                # false alarm, treat passed backslashes literally...
+                if (state == DEFAULT):
+                    state = ARG
+
+                while backslashes > 0:
+                    backslashes-=1
+                    buf += '\\'
+                # fall through to switch
+        if ch in (' ', '\t'):
+            if (state == DEFAULT):
+                # skip
+                continue
+            elif (state == ARG):
+                state = DEFAULT
+                result.append(buf)
+                buf = ''
+                continue
+
+        if state in (DEFAULT, ARG):
+            if ch == '"':
+                state = IN_DOUBLE_QUOTE
+            else:
+                state = ARG
+                buf += ch
+
+        elif state == IN_DOUBLE_QUOTE:
+            if ch == '"':
+                if (i + 1 < args_len and args[i + 1] == '"'):
+                    # Undocumented feature in Windows:
+                    # Two consecutive double quotes inside a double-quoted argument are interpreted as
+                    # a single double quote.
+                    buf += '"'
+                    i+=1
+                elif len(buf) == 0:
+                    # empty string on Windows platform. Account for bug in constructor of JDK's java.lang.ProcessImpl.
+                    result.append("\"\"")
+                    state = DEFAULT
+                else:
+                    state = ARG
+            else:
+                buf += ch
+
+        else:
+            raise RuntimeError('Illegal condition')
+
+    if len(buf) > 0 or state != DEFAULT:
+        result.append(buf)
+
+    return result
+
 
 def patch_arg_str_win(arg_str):
-    new_arg_str = arg_str.replace('\\', '/')
-    args = str_to_args(new_arg_str)
+    args = str_to_args_windows(arg_str)
     if not is_python(args[0]):
         return arg_str
     arg_str = args_to_str(patch_args(args))
-    pydev_log.debug("New args: %s"% arg_str)
+    pydev_log.debug("New args: %s" % arg_str)
     return arg_str
 
 def monkey_patch_module(module, funcname, create_func):
@@ -120,7 +198,8 @@
     import pydev_log
 
     pydev_log.error_once(
-        "New process is launching. Breakpoints won't work.\n To debug that process please enable 'Attach to subprocess automatically while debugging' option in the debugger settings.\n")
+        "pydev debugger: New process is launching (breakpoints won't work in the new process).\n"
+        "pydev debugger: To debug that process please enable 'Attach to subprocess automatically while debugging?' option in the debugger settings.\n")
 
 
 def create_warn_multiproc(original_name):
@@ -308,3 +387,110 @@
         except ImportError:
             import _winapi as _subprocess
         monkey_patch_module(_subprocess, 'CreateProcess', create_CreateProcessWarnMultiproc)
+
+
+
+class _NewThreadStartupWithTrace:
+
+    def __init__(self, original_func):
+        self.original_func = original_func
+
+    def __call__(self, *args, **kwargs):
+        from pydevd_comm import GetGlobalDebugger
+        global_debugger = GetGlobalDebugger()
+        if global_debugger is not None:
+            global_debugger.SetTrace(global_debugger.trace_dispatch)
+
+        return self.original_func(*args, **kwargs)
+
+class _NewThreadStartupWithoutTrace:
+
+    def __init__(self, original_func):
+        self.original_func = original_func
+
+    def __call__(self, *args, **kwargs):
+        return self.original_func(*args, **kwargs)
+
+_UseNewThreadStartup = _NewThreadStartupWithTrace
+
+def _get_threading_modules():
+    threading_modules = []
+    from _pydev_imps import _pydev_thread
+    threading_modules.append(_pydev_thread)
+    try:
+        import thread as _thread
+        threading_modules.append(_thread)
+    except:
+        import _thread
+        threading_modules.append(_thread)
+    return threading_modules
+
+threading_modules = _get_threading_modules()
+
+
+
+def patch_thread_module(thread):
+
+    if getattr(thread, '_original_start_new_thread', None) is None:
+        _original_start_new_thread = thread._original_start_new_thread = thread.start_new_thread
+    else:
+        _original_start_new_thread = thread._original_start_new_thread
+
+
+    class ClassWithPydevStartNewThread:
+
+        def pydev_start_new_thread(self, function, args, kwargs={}):
+            '''
+            We need to replace the original thread.start_new_thread with this function so that threads started
+            through it and not through the threading module are properly traced.
+            '''
+            return _original_start_new_thread(_UseNewThreadStartup(function), args, kwargs)
+
+    # This is a hack for the situation where the thread.start_new_thread is declared inside a class, such as the one below
+    # class F(object):
+    #    start_new_thread = thread.start_new_thread
+    #
+    #    def start_it(self):
+    #        self.start_new_thread(self.function, args, kwargs)
+    # So, if it's an already bound method, calling self.start_new_thread won't really receive a different 'self' -- it
+    # does work in the default case because in builtins self isn't passed either.
+    pydev_start_new_thread = ClassWithPydevStartNewThread().pydev_start_new_thread
+
+    try:
+        # We need to replace the original thread.start_new_thread with this function so that threads started through
+        # it and not through the threading module are properly traced.
+        thread.start_new_thread = pydev_start_new_thread
+        thread.start_new = pydev_start_new_thread
+    except:
+        pass
+
+def patch_thread_modules():
+    for t in threading_modules:
+        patch_thread_module(t)
+
+def undo_patch_thread_modules():
+    for t in threading_modules:
+        try:
+            t.start_new_thread = t._original_start_new_thread
+        except:
+            pass
+
+        try:
+            t.start_new = t._original_start_new_thread
+        except:
+            pass
+
+def disable_trace_thread_modules():
+    '''
+    Can be used to temporarily stop tracing threads created with thread.start_new_thread.
+    '''
+    global _UseNewThreadStartup
+    _UseNewThreadStartup = _NewThreadStartupWithoutTrace
+
+
+def enable_trace_thread_modules():
+    '''
+    Can be used to start tracing threads created with thread.start_new_thread again.
+    '''
+    global _UseNewThreadStartup
+    _UseNewThreadStartup = _NewThreadStartupWithTrace
diff --git a/python/helpers/pydev/pydev_override.py b/python/helpers/pydev/pydev_override.py
new file mode 100644
index 0000000..bb0c504
--- /dev/null
+++ b/python/helpers/pydev/pydev_override.py
@@ -0,0 +1,49 @@
+def overrides(method):
+    '''
+    Initially meant to be used as
+    
+    class B:
+        @overrides(A.m1)
+        def m1(self):
+            pass
+            
+    but as we want to be compatible with Jython 2.1 where decorators have an uglier syntax (needing an assign
+    after the method), it should now be used without being a decorator as below (in which case we don't even check
+    for anything, just that the parent name was actually properly loaded).
+    
+    i.e.:
+    
+    class B:
+        overrides(A.m1)
+        def m1(self):
+            pass
+    '''
+    return
+
+#    def wrapper(func):
+#        if func.__name__ != method.__name__:
+#            msg = "Wrong @override: %r expected, but overwriting %r."
+#            msg = msg % (func.__name__, method.__name__)
+#            raise AssertionError(msg)
+#
+#        if func.__doc__ is None:
+#            func.__doc__ = method.__doc__
+#
+#        return func
+#
+#    return wrapper
+
+def implements(method):
+    return
+#    def wrapper(func):
+#        if func.__name__ != method.__name__:
+#            msg = "Wrong @implements: %r expected, but implementing %r."
+#            msg = msg % (func.__name__, method.__name__)
+#            raise AssertionError(msg)
+#
+#        if func.__doc__ is None:
+#            func.__doc__ = method.__doc__
+#
+#        return func
+#
+#    return wrapper
\ No newline at end of file
diff --git a/python/helpers/pydev/pydev_pysrc.py b/python/helpers/pydev/pydev_pysrc.py
new file mode 100644
index 0000000..b9ed49e
--- /dev/null
+++ b/python/helpers/pydev/pydev_pysrc.py
@@ -0,0 +1 @@
+'''An empty file in pysrc that can be imported (from sitecustomize) to find the location of pysrc'''
\ No newline at end of file
diff --git a/python/helpers/pydev/pydev_runfiles.py b/python/helpers/pydev/pydev_runfiles.py
new file mode 100644
index 0000000..bb704db
--- /dev/null
+++ b/python/helpers/pydev/pydev_runfiles.py
@@ -0,0 +1,831 @@
+from __future__ import nested_scopes
+
+import fnmatch
+import os.path
+from pydev_runfiles_coverage import StartCoverageSupport
+import pydev_runfiles_unittest
+from pydevd_constants import * #@UnusedWildImport
+import re
+import time
+import unittest
+
+
+#=======================================================================================================================
+# Configuration
+#=======================================================================================================================
+class Configuration:
+
+    def __init__(
+        self,
+        files_or_dirs='',
+        verbosity=2,
+        include_tests=None,
+        tests=None,
+        port=None,
+        files_to_tests=None,
+        jobs=1,
+        split_jobs='tests',
+        coverage_output_dir=None,
+        coverage_include=None,
+        coverage_output_file=None,
+        exclude_files=None,
+        exclude_tests=None,
+        include_files=None,
+        django=False,
+        ):
+        self.files_or_dirs = files_or_dirs
+        self.verbosity = verbosity
+        self.include_tests = include_tests
+        self.tests = tests
+        self.port = port
+        self.files_to_tests = files_to_tests
+        self.jobs = jobs
+        self.split_jobs = split_jobs
+        self.django = django
+
+        if include_tests:
+            assert isinstance(include_tests, (list, tuple))
+
+        if exclude_files:
+            assert isinstance(exclude_files, (list, tuple))
+
+        if exclude_tests:
+            assert isinstance(exclude_tests, (list, tuple))
+
+        self.exclude_files = exclude_files
+        self.include_files = include_files
+        self.exclude_tests = exclude_tests
+
+        self.coverage_output_dir = coverage_output_dir
+        self.coverage_include = coverage_include
+        self.coverage_output_file = coverage_output_file
+
+    def __str__(self):
+        return '''Configuration
+ - files_or_dirs: %s
+ - verbosity: %s
+ - tests: %s
+ - port: %s
+ - files_to_tests: %s
+ - jobs: %s
+ - split_jobs: %s
+
+ - include_files: %s
+ - include_tests: %s
+
+ - exclude_files: %s
+ - exclude_tests: %s
+
+ - coverage_output_dir: %s
+ - coverage_include_dir: %s
+ - coverage_output_file: %s
+
+ - django: %s
+''' % (
+        self.files_or_dirs,
+        self.verbosity,
+        self.tests,
+        self.port,
+        self.files_to_tests,
+        self.jobs,
+        self.split_jobs,
+
+        self.include_files,
+        self.include_tests,
+
+        self.exclude_files,
+        self.exclude_tests,
+
+        self.coverage_output_dir,
+        self.coverage_include,
+        self.coverage_output_file,
+
+        self.django,
+    )
+
+
+#=======================================================================================================================
+# parse_cmdline
+#=======================================================================================================================
+def parse_cmdline(argv=None):
+    """
+    Parses command line and returns test directories, verbosity, test filter and test suites
+
+    usage:
+        runfiles.py  -v|--verbosity <level>  -t|--tests <Test.test1,Test2>  dirs|files
+
+    Multiprocessing options:
+    jobs=number (with the number of jobs to be used to run the tests)
+    split_jobs='module'|'tests'
+        if == module, a given job will always receive all the tests from a module
+        if == tests, the tests will be split independently of their originating module (default)
+
+    --exclude_files  = comma-separated list of patterns with files to exclude (fnmatch style)
+    --include_files = comma-separated list of patterns with files to include (fnmatch style)
+    --exclude_tests = comma-separated list of patterns with test names to exclude (fnmatch style)
+
+    Note: if --tests is given, --exclude_files, --include_files and --exclude_tests are ignored!
+    """
+    if argv is None:
+        argv = sys.argv
+
+    verbosity = 2
+    include_tests = None
+    tests = None
+    port = None
+    jobs = 1
+    split_jobs = 'tests'
+    files_to_tests = {}
+    coverage_output_dir = None
+    coverage_include = None
+    exclude_files = None
+    exclude_tests = None
+    include_files = None
+    django = False
+
+    from _pydev_getopt import gnu_getopt
+    optlist, dirs = gnu_getopt(
+        argv[1:], "",
+        [
+            "verbosity=",
+            "tests=",
+
+            "port=",
+            "config_file=",
+
+            "jobs=",
+            "split_jobs=",
+
+            "include_tests=",
+            "include_files=",
+
+            "exclude_files=",
+            "exclude_tests=",
+
+            "coverage_output_dir=",
+            "coverage_include=",
+
+            "django="
+        ]
+    )
+
+    for opt, value in optlist:
+        if opt in ("-v", "--verbosity"):
+            verbosity = value
+
+        elif opt in ("-p", "--port"):
+            port = int(value)
+
+        elif opt in ("-j", "--jobs"):
+            jobs = int(value)
+
+        elif opt in ("-s", "--split_jobs"):
+            split_jobs = value
+            if split_jobs not in ('module', 'tests'):
+                raise AssertionError('Expected split to be either "module" or "tests". Was :%s' % (split_jobs,))
+
+        elif opt in ("-d", "--coverage_output_dir",):
+            coverage_output_dir = value.strip()
+
+        elif opt in ("-i", "--coverage_include",):
+            coverage_include = value.strip()
+
+        elif opt in ("-I", "--include_tests"):
+            include_tests = value.split(',')
+
+        elif opt in ("-E", "--exclude_files"):
+            exclude_files = value.split(',')
+
+        elif opt in ("-F", "--include_files"):
+            include_files = value.split(',')
+
+        elif opt in ("-e", "--exclude_tests"):
+            exclude_tests = value.split(',')
+
+        elif opt in ("-t", "--tests"):
+            tests = value.split(',')
+
+        elif opt in ("--django",):
+            django = value.strip() in ['true', 'True', '1']
+
+        elif opt in ("-c", "--config_file"):
+            config_file = value.strip()
+            if os.path.exists(config_file):
+                f = open(config_file, 'rU')
+                try:
+                    config_file_contents = f.read()
+                finally:
+                    f.close()
+
+                if config_file_contents:
+                    config_file_contents = config_file_contents.strip()
+
+                if config_file_contents:
+                    for line in config_file_contents.splitlines():
+                        file_and_test = line.split('|')
+                        if len(file_and_test) == 2:
+                            file, test = file_and_test
+                            if DictContains(files_to_tests, file):
+                                files_to_tests[file].append(test)
+                            else:
+                                files_to_tests[file] = [test]
+
+            else:
+                sys.stderr.write('Could not find config file: %s\n' % (config_file,))
+
+    if type([]) != type(dirs):
+        dirs = [dirs]
+
+    ret_dirs = []
+    for d in dirs:
+        if '|' in d:
+            #paths may come from the ide separated by |
+            ret_dirs.extend(d.split('|'))
+        else:
+            ret_dirs.append(d)
+
+    verbosity = int(verbosity)
+
+    if tests:
+        if verbosity > 4:
+            sys.stdout.write('--tests provided. Ignoring --exclude_files, --exclude_tests and --include_files\n')
+        exclude_files = exclude_tests = include_files = None
+
+    config = Configuration(
+        ret_dirs,
+        verbosity,
+        include_tests,
+        tests,
+        port,
+        files_to_tests,
+        jobs,
+        split_jobs,
+        coverage_output_dir,
+        coverage_include,
+        exclude_files=exclude_files,
+        exclude_tests=exclude_tests,
+        include_files=include_files,
+        django=django,
+    )
+
+    if verbosity > 5:
+        sys.stdout.write(str(config) + '\n')
+    return config
+
+
+#=======================================================================================================================
+# PydevTestRunner
+#=======================================================================================================================
+class PydevTestRunner(object):
+    """ finds and runs a file or directory of files as a unit test """
+
+    __py_extensions = ["*.py", "*.pyw"]
+    __exclude_files = ["__init__.*"]
+
+    #Just to check that only this attributes will be written to this file
+    __slots__ = [
+        'verbosity',  #Always used
+
+        'files_to_tests',  #If this one is given, the ones below are not used
+
+        'files_or_dirs',  #Files or directories received in the command line
+        'include_tests',  #The filter used to collect the tests
+        'tests',  #Strings with the tests to be run
+
+        'jobs',  #Integer with the number of jobs that should be used to run the test cases
+        'split_jobs',  #String with 'tests' or 'module' (how should the jobs be split)
+
+        'configuration',
+        'coverage',
+    ]
+
+    def __init__(self, configuration):
+        self.verbosity = configuration.verbosity
+
+        self.jobs = configuration.jobs
+        self.split_jobs = configuration.split_jobs
+
+        files_to_tests = configuration.files_to_tests
+        if files_to_tests:
+            self.files_to_tests = files_to_tests
+            self.files_or_dirs = list(files_to_tests.keys())
+            self.tests = None
+        else:
+            self.files_to_tests = {}
+            self.files_or_dirs = configuration.files_or_dirs
+            self.tests = configuration.tests
+
+        self.configuration = configuration
+        self.__adjust_path()
+
+
+    def __adjust_path(self):
+        """ add the current file or directory to the python path """
+        path_to_append = None
+        for n in xrange(len(self.files_or_dirs)):
+            dir_name = self.__unixify(self.files_or_dirs[n])
+            if os.path.isdir(dir_name):
+                if not dir_name.endswith("/"):
+                    self.files_or_dirs[n] = dir_name + "/"
+                path_to_append = os.path.normpath(dir_name)
+            elif os.path.isfile(dir_name):
+                path_to_append = os.path.dirname(dir_name)
+            else:
+                if not os.path.exists(dir_name):
+                    block_line = '*' * 120
+                    sys.stderr.write('\n%s\n* PyDev test runner error: %s does not exist.\n%s\n' % (block_line, dir_name, block_line))
+                    return
+                msg = ("unknown type. \n%s\nshould be file or a directory.\n" % (dir_name))
+                raise RuntimeError(msg)
+        if path_to_append is not None:
+            #Add it as the last one (so, first things are resolved against the default dirs and
+            #if none resolves, then we try a relative import).
+            sys.path.append(path_to_append)
+
+    def __is_valid_py_file(self, fname):
+        """ tests that a particular file contains the proper file extension
+            and is not in the list of files to exclude """
+        is_valid_fname = 0
+        for invalid_fname in self.__class__.__exclude_files:
+            is_valid_fname += int(not fnmatch.fnmatch(fname, invalid_fname))
+        if_valid_ext = 0
+        for ext in self.__class__.__py_extensions:
+            if_valid_ext += int(fnmatch.fnmatch(fname, ext))
+        return is_valid_fname > 0 and if_valid_ext > 0
+
+    def __unixify(self, s):
+        """ stupid windows. converts the backslash to forwardslash for consistency """
+        return os.path.normpath(s).replace(os.sep, "/")
+
+    def __importify(self, s, dir=False):
+        """ turns directory separators into dots and removes the ".py*" extension
+            so the string can be used as import statement """
+        if not dir:
+            dirname, fname = os.path.split(s)
+
+            if fname.count('.') > 1:
+                #if there's a file named xxx.xx.py, it is not a valid module, so, let's not load it...
+                return
+
+            imp_stmt_pieces = [dirname.replace("\\", "/").replace("/", "."), os.path.splitext(fname)[0]]
+
+            if len(imp_stmt_pieces[0]) == 0:
+                imp_stmt_pieces = imp_stmt_pieces[1:]
+
+            return ".".join(imp_stmt_pieces)
+
+        else:  #handle dir
+            return s.replace("\\", "/").replace("/", ".")
+
+    def __add_files(self, pyfiles, root, files):
+        """ if files match, appends them to pyfiles. used by os.path.walk fcn """
+        for fname in files:
+            if self.__is_valid_py_file(fname):
+                name_without_base_dir = self.__unixify(os.path.join(root, fname))
+                pyfiles.append(name_without_base_dir)
+
+
+    def find_import_files(self):
+        """ return a list of files to import """
+        if self.files_to_tests:
+            pyfiles = self.files_to_tests.keys()
+        else:
+            pyfiles = []
+
+            for base_dir in self.files_or_dirs:
+                if os.path.isdir(base_dir):
+                    if hasattr(os, 'walk'):
+                        for root, dirs, files in os.walk(base_dir):
+
+                            #Note: handling directories that should be excluded from the search because
+                            #they don't have __init__.py
+                            exclude = {}
+                            for d in dirs:
+                                for init in ['__init__.py', '__init__.pyo', '__init__.pyc', '__init__.pyw']:
+                                    if os.path.exists(os.path.join(root, d, init).replace('\\', '/')):
+                                        break
+                                else:
+                                    exclude[d] = 1
+
+                            if exclude:
+                                new = []
+                                for d in dirs:
+                                    if d not in exclude:
+                                        new.append(d)
+
+                                dirs[:] = new
+
+                            self.__add_files(pyfiles, root, files)
+                    else:
+                        # jython2.1 is too old for os.walk!
+                        os.path.walk(base_dir, self.__add_files, pyfiles)
+
+                elif os.path.isfile(base_dir):
+                    pyfiles.append(base_dir)
+
+        if self.configuration.exclude_files or self.configuration.include_files:
+            ret = []
+            for f in pyfiles:
+                add = True
+                basename = os.path.basename(f)
+                if self.configuration.include_files:
+                    add = False
+
+                    for pat in self.configuration.include_files:
+                        if fnmatch.fnmatchcase(basename, pat):
+                            add = True
+                            break
+
+                if not add:
+                    if self.verbosity > 3:
+                        sys.stdout.write('Skipped file: %s (did not match any include_files pattern: %s)\n' % (f, self.configuration.include_files))
+
+                elif self.configuration.exclude_files:
+                    for pat in self.configuration.exclude_files:
+                        if fnmatch.fnmatchcase(basename, pat):
+                            if self.verbosity > 3:
+                                sys.stdout.write('Skipped file: %s (matched exclude_files pattern: %s)\n' % (f, pat))
+
+                            elif self.verbosity > 2:
+                                sys.stdout.write('Skipped file: %s\n' % (f,))
+
+                            add = False
+                            break
+
+                if add:
+                    if self.verbosity > 3:
+                        sys.stdout.write('Adding file: %s for test discovery.\n' % (f,))
+                    ret.append(f)
+
+            pyfiles = ret
+
+
+        return pyfiles
+
+    def __get_module_from_str(self, modname, print_exception, pyfile):
+        """ Import the module in the given import path.
+            * Returns the "final" module, so importing "coilib40.subject.visu"
+            returns the "visu" module, not the "coilib40" as returned by __import__ """
+        try:
+            mod = __import__(modname)
+            for part in modname.split('.')[1:]:
+                mod = getattr(mod, part)
+            return mod
+        except:
+            if print_exception:
+                import pydev_runfiles_xml_rpc
+                import pydevd_io
+                buf_err = pydevd_io.StartRedirect(keep_original_redirection=True, std='stderr')
+                buf_out = pydevd_io.StartRedirect(keep_original_redirection=True, std='stdout')
+                try:
+                    import traceback;traceback.print_exc()
+                    sys.stderr.write('ERROR: Module: %s could not be imported (file: %s).\n' % (modname, pyfile))
+                finally:
+                    pydevd_io.EndRedirect('stderr')
+                    pydevd_io.EndRedirect('stdout')
+
+                pydev_runfiles_xml_rpc.notifyTest(
+                    'error', buf_out.getvalue(), buf_err.getvalue(), pyfile, modname, 0)
+
+            return None
+
+    def find_modules_from_files(self, pyfiles):
+        """ returns a list of modules given a list of files """
+        #let's make sure that the paths we want are in the pythonpath...
+        imports = [(s, self.__importify(s)) for s in pyfiles]
+
+        system_paths = []
+        for s in sys.path:
+            system_paths.append(self.__importify(s, True))
+
+
+        ret = []
+        for pyfile, imp in imports:
+            if imp is None:
+                continue  #can happen if a file is not a valid module
+            choices = []
+            for s in system_paths:
+                if imp.startswith(s):
+                    add = imp[len(s) + 1:]
+                    if add:
+                        choices.append(add)
+                    #sys.stdout.write(' ' + add + ' ')
+
+            if not choices:
+                sys.stdout.write('PYTHONPATH not found for file: %s\n' % imp)
+            else:
+                for i, import_str in enumerate(choices):
+                    print_exception = i == len(choices) - 1
+                    mod = self.__get_module_from_str(import_str, print_exception, pyfile)
+                    if mod is not None:
+                        ret.append((pyfile, mod, import_str))
+                        break
+
+
+        return ret
+
+    #===================================================================================================================
+    # GetTestCaseNames
+    #===================================================================================================================
+    class GetTestCaseNames:
+        """Yes, we need a class for that (cannot use outer context on jython 2.1)"""
+
+        def __init__(self, accepted_classes, accepted_methods):
+            self.accepted_classes = accepted_classes
+            self.accepted_methods = accepted_methods
+
+        def __call__(self, testCaseClass):
+            """Return a sorted sequence of method names found within testCaseClass"""
+            testFnNames = []
+            className = testCaseClass.__name__
+
+            if DictContains(self.accepted_classes, className):
+                for attrname in dir(testCaseClass):
+                    #If a class is chosen, we select all the 'test' methods'
+                    if attrname.startswith('test') and hasattr(getattr(testCaseClass, attrname), '__call__'):
+                        testFnNames.append(attrname)
+
+            else:
+                for attrname in dir(testCaseClass):
+                    #If we have the class+method name, we must do a full check and have an exact match.
+                    if DictContains(self.accepted_methods, className + '.' + attrname):
+                        if hasattr(getattr(testCaseClass, attrname), '__call__'):
+                            testFnNames.append(attrname)
+
+            #sorted() is not available in jython 2.1
+            testFnNames.sort()
+            return testFnNames
+
+
+    def _decorate_test_suite(self, suite, pyfile, module_name):
+        if isinstance(suite, unittest.TestSuite):
+            add = False
+            suite.__pydev_pyfile__ = pyfile
+            suite.__pydev_module_name__ = module_name
+
+            for t in suite._tests:
+                t.__pydev_pyfile__ = pyfile
+                t.__pydev_module_name__ = module_name
+                if self._decorate_test_suite(t, pyfile, module_name):
+                    add = True
+
+            return add
+
+        elif isinstance(suite, unittest.TestCase):
+            return True
+
+        else:
+            return False
+
+
+
+    def find_tests_from_modules(self, file_and_modules_and_module_name):
+        """ returns the unittests given a list of modules """
+        #Use our own suite!
+        unittest.TestLoader.suiteClass = pydev_runfiles_unittest.PydevTestSuite
+        loader = unittest.TestLoader()
+
+        ret = []
+        if self.files_to_tests:
+            for pyfile, m, module_name in file_and_modules_and_module_name:
+                accepted_classes = {}
+                accepted_methods = {}
+                tests = self.files_to_tests[pyfile]
+                for t in tests:
+                    accepted_methods[t] = t
+
+                loader.getTestCaseNames = self.GetTestCaseNames(accepted_classes, accepted_methods)
+
+                suite = loader.loadTestsFromModule(m)
+                if self._decorate_test_suite(suite, pyfile, module_name):
+                    ret.append(suite)
+            return ret
+
+
+        if self.tests:
+            accepted_classes = {}
+            accepted_methods = {}
+
+            for t in self.tests:
+                splitted = t.split('.')
+                if len(splitted) == 1:
+                    accepted_classes[t] = t
+
+                elif len(splitted) == 2:
+                    accepted_methods[t] = t
+
+            loader.getTestCaseNames = self.GetTestCaseNames(accepted_classes, accepted_methods)
+
+
+        for pyfile, m, module_name in file_and_modules_and_module_name:
+            suite = loader.loadTestsFromModule(m)
+            if self._decorate_test_suite(suite, pyfile, module_name):
+                ret.append(suite)
+
+        return ret
+
+
+    def filter_tests(self, test_objs, internal_call=False):
+        """ based on a filter name, only return those tests that have
+            the test case names that match """
+        if not internal_call:
+            if not self.configuration.include_tests and not self.tests and not self.configuration.exclude_tests:
+                #No need to filter if we have nothing to filter!
+                return test_objs
+
+            if self.verbosity > 1:
+                if self.configuration.include_tests:
+                    sys.stdout.write('Tests to include: %s\n' % (self.configuration.include_tests,))
+
+                if self.tests:
+                    sys.stdout.write('Tests to run: %s\n' % (self.tests,))
+
+                if self.configuration.exclude_tests:
+                    sys.stdout.write('Tests to exclude: %s\n' % (self.configuration.exclude_tests,))
+
+        test_suite = []
+        for test_obj in test_objs:
+
+            if isinstance(test_obj, unittest.TestSuite):
+                #Note: keep the suites as they are and just 'fix' the tests (so, don't use the iter_tests).
+                if test_obj._tests:
+                    test_obj._tests = self.filter_tests(test_obj._tests, True)
+                    if test_obj._tests:  #Only add the suite if we still have tests there.
+                        test_suite.append(test_obj)
+
+            elif isinstance(test_obj, unittest.TestCase):
+                try:
+                    testMethodName = test_obj._TestCase__testMethodName
+                except AttributeError:
+                    #changed in python 2.5
+                    testMethodName = test_obj._testMethodName
+
+                add = True
+                if self.configuration.exclude_tests:
+                    for pat in self.configuration.exclude_tests:
+                        if fnmatch.fnmatchcase(testMethodName, pat):
+                            if self.verbosity > 3:
+                                sys.stdout.write('Skipped test: %s (matched exclude_tests pattern: %s)\n' % (testMethodName, pat))
+
+                            elif self.verbosity > 2:
+                                sys.stdout.write('Skipped test: %s\n' % (testMethodName,))
+
+                            add = False
+                            break
+
+                if add:
+                    if self.__match_tests(self.tests, test_obj, testMethodName):
+                        include = True
+                        if self.configuration.include_tests:
+                            include = False
+                            for pat in self.configuration.include_tests:
+                                if fnmatch.fnmatchcase(testMethodName, pat):
+                                    include = True
+                                    break
+                        if include:
+                            test_suite.append(test_obj)
+                        else:
+                            if self.verbosity > 3:
+                                sys.stdout.write('Skipped test: %s (did not match any include_tests pattern %s)\n' % (self.configuration.include_tests,))
+        return test_suite
+
+
+    def iter_tests(self, test_objs):
+        #Note: not using yield because of Jython 2.1.
+        tests = []
+        for test_obj in test_objs:
+            if isinstance(test_obj, unittest.TestSuite):
+                tests.extend(self.iter_tests(test_obj._tests))
+
+            elif isinstance(test_obj, unittest.TestCase):
+                tests.append(test_obj)
+        return tests
+
+
+    def list_test_names(self, test_objs):
+        names = []
+        for tc in self.iter_tests(test_objs):
+            try:
+                testMethodName = tc._TestCase__testMethodName
+            except AttributeError:
+                #changed in python 2.5
+                testMethodName = tc._testMethodName
+            names.append(testMethodName)
+        return names
+
+
+    def __match_tests(self, tests, test_case, test_method_name):
+        if not tests:
+            return 1
+
+        for t in tests:
+            class_and_method = t.split('.')
+            if len(class_and_method) == 1:
+                #only class name
+                if class_and_method[0] == test_case.__class__.__name__:
+                    return 1
+
+            elif len(class_and_method) == 2:
+                if class_and_method[0] == test_case.__class__.__name__ and class_and_method[1] == test_method_name:
+                    return 1
+
+        return 0
+
+
+    def __match(self, filter_list, name):
+        """ returns whether a test name matches the test filter """
+        if filter_list is None:
+            return 1
+        for f in filter_list:
+            if re.match(f, name):
+                return 1
+        return 0
+
+
+    def run_tests(self, handle_coverage=True):
+        """ runs all tests """
+        sys.stdout.write("Finding files... ")
+        files = self.find_import_files()
+        if self.verbosity > 3:
+            sys.stdout.write('%s ... done.\n' % (self.files_or_dirs))
+        else:
+            sys.stdout.write('done.\n')
+        sys.stdout.write("Importing test modules ... ")
+
+
+        if handle_coverage:
+            coverage_files, coverage = StartCoverageSupport(self.configuration)
+
+        file_and_modules_and_module_name = self.find_modules_from_files(files)
+        sys.stdout.write("done.\n")
+
+        all_tests = self.find_tests_from_modules(file_and_modules_and_module_name)
+        all_tests = self.filter_tests(all_tests)
+
+        test_suite = pydev_runfiles_unittest.PydevTestSuite(all_tests)
+        import pydev_runfiles_xml_rpc
+        pydev_runfiles_xml_rpc.notifyTestsCollected(test_suite.countTestCases())
+
+        start_time = time.time()
+
+        def run_tests():
+            executed_in_parallel = False
+            if self.jobs > 1:
+                import pydev_runfiles_parallel
+
+                #What may happen is that the number of jobs needed is lower than the number of jobs requested
+                #(e.g.: 2 jobs were requested for running 1 test) -- in which case ExecuteTestsInParallel will
+                #return False and won't run any tests.
+                executed_in_parallel = pydev_runfiles_parallel.ExecuteTestsInParallel(
+                    all_tests, self.jobs, self.split_jobs, self.verbosity, coverage_files, self.configuration.coverage_include)
+
+            if not executed_in_parallel:
+                #If in coverage, we don't need to pass anything here (coverage is already enabled for this execution).
+                runner = pydev_runfiles_unittest.PydevTextTestRunner(stream=sys.stdout, descriptions=1, verbosity=self.verbosity)
+                sys.stdout.write('\n')
+                runner.run(test_suite)
+
+        if self.configuration.django:
+            MyDjangoTestSuiteRunner(run_tests).run_tests([])
+        else:
+            run_tests()
+
+        if handle_coverage:
+            coverage.stop()
+            coverage.save()
+
+        total_time = 'Finished in: %.2f secs.' % (time.time() - start_time,)
+        pydev_runfiles_xml_rpc.notifyTestRunFinished(total_time)
+
+
+try:
+    from django.test.simple import DjangoTestSuiteRunner
+except:
+    class DjangoTestSuiteRunner:
+        def __init__(self):
+            pass
+
+        def run_tests(self, *args, **kwargs):
+            raise AssertionError("Unable to run suite with DjangoTestSuiteRunner because it couldn't be imported.")
+
+class MyDjangoTestSuiteRunner(DjangoTestSuiteRunner):
+
+    def __init__(self, on_run_suite):
+        DjangoTestSuiteRunner.__init__(self)
+        self.on_run_suite = on_run_suite
+
+    def build_suite(self, *args, **kwargs):
+        pass
+
+    def suite_result(self, *args, **kwargs):
+        pass
+
+    def run_suite(self, *args, **kwargs):
+        self.on_run_suite()
+
+
+#=======================================================================================================================
+# main
+#=======================================================================================================================
+def main(configuration):
+    PydevTestRunner(configuration).run_tests()
diff --git a/python/helpers/pydev/pydev_runfiles_coverage.py b/python/helpers/pydev/pydev_runfiles_coverage.py
new file mode 100644
index 0000000..55bec06
--- /dev/null
+++ b/python/helpers/pydev/pydev_runfiles_coverage.py
@@ -0,0 +1,76 @@
+import os.path
+import sys
+from pydevd_constants import Null
+
+
+#=======================================================================================================================
+# GetCoverageFiles
+#=======================================================================================================================
+def GetCoverageFiles(coverage_output_dir, number_of_files):
+    base_dir = coverage_output_dir
+    ret = []
+    i = 0
+    while len(ret) < number_of_files:
+        while True:
+            f = os.path.join(base_dir, '.coverage.%s' % i)
+            i += 1
+            if not os.path.exists(f):
+                ret.append(f)
+                break #Break only inner for.
+    return ret
+
+
+#=======================================================================================================================
+# StartCoverageSupport
+#=======================================================================================================================
+def StartCoverageSupport(configuration):
+    return StartCoverageSupportFromParams(
+        configuration.coverage_output_dir, 
+        configuration.coverage_output_file, 
+        configuration.jobs, 
+        configuration.coverage_include, 
+    )
+    
+
+#=======================================================================================================================
+# StartCoverageSupportFromParams
+#=======================================================================================================================
+def StartCoverageSupportFromParams(coverage_output_dir, coverage_output_file, jobs, coverage_include):
+    coverage_files = []
+    coverage_instance = Null()
+    if coverage_output_dir or coverage_output_file:
+        try:
+            import coverage #@UnresolvedImport
+        except:
+            sys.stderr.write('Error: coverage module could not be imported\n')
+            sys.stderr.write('Please make sure that the coverage module (http://nedbatchelder.com/code/coverage/)\n')
+            sys.stderr.write('is properly installed in your interpreter: %s\n' % (sys.executable,))
+            
+            import traceback;traceback.print_exc()
+        else:
+            if coverage_output_dir:
+                if not os.path.exists(coverage_output_dir):
+                    sys.stderr.write('Error: directory for coverage output (%s) does not exist.\n' % (coverage_output_dir,))
+                    
+                elif not os.path.isdir(coverage_output_dir):
+                    sys.stderr.write('Error: expected (%s) to be a directory.\n' % (coverage_output_dir,))
+                    
+                else:
+                    n = jobs
+                    if n <= 0:
+                        n += 1
+                    n += 1 #Add 1 more for the current process (which will do the initial import).
+                    coverage_files = GetCoverageFiles(coverage_output_dir, n)
+                    os.environ['COVERAGE_FILE'] = coverage_files.pop(0)
+                    
+                    coverage_instance = coverage.coverage(source=[coverage_include])
+                    coverage_instance.start()
+                    
+            elif coverage_output_file:
+                #Client of parallel run.
+                os.environ['COVERAGE_FILE'] = coverage_output_file
+                coverage_instance = coverage.coverage(source=[coverage_include])
+                coverage_instance.start()
+                
+    return coverage_files, coverage_instance
+
diff --git a/python/helpers/pydev/pydev_runfiles_nose.py b/python/helpers/pydev/pydev_runfiles_nose.py
new file mode 100644
index 0000000..422d2a6
--- /dev/null
+++ b/python/helpers/pydev/pydev_runfiles_nose.py
@@ -0,0 +1,180 @@
+from nose.plugins.multiprocess import MultiProcessTestRunner  # @UnresolvedImport
+from nose.plugins.base import Plugin  # @UnresolvedImport
+import sys
+import pydev_runfiles_xml_rpc
+import time
+from pydev_runfiles_coverage import StartCoverageSupport
+
+#=======================================================================================================================
+# PydevPlugin
+#=======================================================================================================================
+class PydevPlugin(Plugin):
+
+    def __init__(self, configuration):
+        self.configuration = configuration
+        Plugin.__init__(self)
+
+
+    def begin(self):
+        # Called before any test is run (it's always called, with multiprocess or not)
+        self.start_time = time.time()
+        self.coverage_files, self.coverage = StartCoverageSupport(self.configuration)
+
+
+    def finalize(self, result):
+        # Called after all tests are run (it's always called, with multiprocess or not)
+        self.coverage.stop()
+        self.coverage.save()
+
+        pydev_runfiles_xml_rpc.notifyTestRunFinished('Finished in: %.2f secs.' % (time.time() - self.start_time,))
+
+
+
+    #===================================================================================================================
+    # Methods below are not called with multiprocess (so, we monkey-patch MultiProcessTestRunner.consolidate
+    # so that they're called, but unfortunately we loose some info -- i.e.: the time for each test in this
+    # process).
+    #===================================================================================================================
+
+
+    def reportCond(self, cond, test, captured_output, error=''):
+        '''
+        @param cond: fail, error, ok
+        '''
+
+        # test.address() is something as:
+        # ('D:\\workspaces\\temp\\test_workspace\\pytesting1\\src\\mod1\\hello.py', 'mod1.hello', 'TestCase.testMet1')
+        #
+        # and we must pass: location, test
+        #    E.g.: ['D:\\src\\mod1\\hello.py', 'TestCase.testMet1']
+        try:
+            if hasattr(test, 'address'):
+                address = test.address()
+                address = address[0], address[2]
+            else:
+                # multiprocess
+                try:
+                    address = test[0], test[1]
+                except TypeError:
+                    # It may be an error at setup, in which case it's not really a test, but a Context object.
+                    f = test.context.__file__
+                    if f.endswith('.pyc'):
+                        f = f[:-1]
+                    address = f, '?'
+        except:
+            sys.stderr.write("PyDev: Internal pydev error getting test address. Please report at the pydev bug tracker\n")
+            import traceback;traceback.print_exc()
+            sys.stderr.write("\n\n\n")
+            address = '?', '?'
+
+        error_contents = self.getIoFromError(error)
+        try:
+            time_str = '%.2f' % (time.time() - test._pydev_start_time)
+        except:
+            time_str = '?'
+
+        pydev_runfiles_xml_rpc.notifyTest(cond, captured_output, error_contents, address[0], address[1], time_str)
+
+
+    def startTest(self, test):
+        test._pydev_start_time = time.time()
+        if hasattr(test, 'address'):
+            address = test.address()
+            file, test = address[0], address[2]
+        else:
+            # multiprocess
+            file, test = test
+        pydev_runfiles_xml_rpc.notifyStartTest(file, test)
+
+
+    def getIoFromError(self, err):
+        if type(err) == type(()):
+            if len(err) != 3:
+                if len(err) == 2:
+                    return err[1]  # multiprocess
+            try:
+                from StringIO import StringIO
+            except:
+                from io import StringIO
+            s = StringIO()
+            etype, value, tb = err
+            import traceback;traceback.print_exception(etype, value, tb, file=s)
+            return s.getvalue()
+        return err
+
+
+    def getCapturedOutput(self, test):
+        if hasattr(test, 'capturedOutput') and test.capturedOutput:
+            return test.capturedOutput
+        return ''
+
+
+    def addError(self, test, err):
+        self.reportCond(
+            'error',
+            test,
+            self.getCapturedOutput(test),
+            err,
+        )
+
+
+    def addFailure(self, test, err):
+        self.reportCond(
+            'fail',
+            test,
+            self.getCapturedOutput(test),
+            err,
+        )
+
+
+    def addSuccess(self, test):
+        self.reportCond(
+            'ok',
+            test,
+            self.getCapturedOutput(test),
+            '',
+        )
+
+
+PYDEV_NOSE_PLUGIN_SINGLETON = None
+def StartPydevNosePluginSingleton(configuration):
+    global PYDEV_NOSE_PLUGIN_SINGLETON
+    PYDEV_NOSE_PLUGIN_SINGLETON = PydevPlugin(configuration)
+    return PYDEV_NOSE_PLUGIN_SINGLETON
+
+
+
+
+original = MultiProcessTestRunner.consolidate
+#=======================================================================================================================
+# NewConsolidate
+#=======================================================================================================================
+def NewConsolidate(self, result, batch_result):
+    '''
+    Used so that it can work with the multiprocess plugin.
+    Monkeypatched because nose seems a bit unsupported at this time (ideally
+    the plugin would have this support by default).
+    '''
+    ret = original(self, result, batch_result)
+
+    parent_frame = sys._getframe().f_back
+    # addr is something as D:\pytesting1\src\mod1\hello.py:TestCase.testMet4
+    # so, convert it to what reportCond expects
+    addr = parent_frame.f_locals['addr']
+    i = addr.rindex(':')
+    addr = [addr[:i], addr[i + 1:]]
+
+    output, testsRun, failures, errors, errorClasses = batch_result
+    if failures or errors:
+        for failure in failures:
+            PYDEV_NOSE_PLUGIN_SINGLETON.reportCond('fail', addr, output, failure)
+
+        for error in errors:
+            PYDEV_NOSE_PLUGIN_SINGLETON.reportCond('error', addr, output, error)
+    else:
+        PYDEV_NOSE_PLUGIN_SINGLETON.reportCond('ok', addr, output)
+
+
+    return ret
+
+MultiProcessTestRunner.consolidate = NewConsolidate
diff --git a/python/helpers/pydev/pydev_runfiles_parallel.py b/python/helpers/pydev/pydev_runfiles_parallel.py
new file mode 100644
index 0000000..e14f36d
--- /dev/null
+++ b/python/helpers/pydev/pydev_runfiles_parallel.py
@@ -0,0 +1,298 @@
+import unittest
+try:
+    import Queue
+except:
+    import queue as Queue #@UnresolvedImport
+from pydevd_constants import * #@UnusedWildImport
+import pydev_runfiles_xml_rpc
+import time
+import os
+
+#=======================================================================================================================
+# FlattenTestSuite
+#=======================================================================================================================
+def FlattenTestSuite(test_suite, ret):
+    if isinstance(test_suite, unittest.TestSuite):
+        for t in test_suite._tests:
+            FlattenTestSuite(t, ret)
+            
+    elif isinstance(test_suite, unittest.TestCase):
+        ret.append(test_suite)
+
+
+#=======================================================================================================================
+# ExecuteTestsInParallel
+#=======================================================================================================================
+def ExecuteTestsInParallel(tests, jobs, split, verbosity, coverage_files, coverage_include):
+    '''
+    @param tests: list(PydevTestSuite)
+        A list with the suites to be run
+        
+    @param split: str
+        Either 'module' or the number of tests that should be run in each batch
+        
+    @param coverage_files: list(file)
+        A list with the files that should be used for giving coverage information (if empty, coverage information 
+        should not be gathered). 
+        
+    @param coverage_include: str
+        The pattern that should be included in the coverage.
+        
+    @return: bool
+        Returns True if the tests were actually executed in parallel. If the tests were not executed because only 1
+        should be used (e.g.: 2 jobs were requested for running 1 test), False will be returned and no tests will be
+        run.
+        
+        It may also return False if in debug mode (in which case, multi-processes are not accepted) 
+    '''
+    try:
+        from pydevd_comm import GetGlobalDebugger
+        if GetGlobalDebugger() is not None:
+            return False
+    except:
+        pass #Ignore any error here.
+    
+    #This queue will receive the tests to be run. Each entry in a queue is a list with the tests to be run together When
+    #split == 'tests', each list will have a single element, when split == 'module', each list will have all the tests
+    #from a given module.
+    tests_queue = []
+    
+    queue_elements = []
+    if split == 'module':
+        module_to_tests = {}
+        for test in tests:
+            lst = []
+            FlattenTestSuite(test, lst)
+            for test in lst:
+                key = (test.__pydev_pyfile__, test.__pydev_module_name__)
+                module_to_tests.setdefault(key, []).append(test)
+        
+        for key, tests in module_to_tests.items():
+            queue_elements.append(tests)
+            
+        if len(queue_elements) < jobs:
+            #Don't create jobs we will never use.
+            jobs = len(queue_elements)
+    
+    elif split == 'tests':
+        for test in tests:
+            lst = []
+            FlattenTestSuite(test, lst)
+            for test in lst:
+                queue_elements.append([test])
+                
+        if len(queue_elements) < jobs:
+            #Don't create jobs we will never use.
+            jobs = len(queue_elements)
+    
+    else:
+        raise AssertionError('Do not know how to handle: %s' % (split,))
+    
+    for test_cases in queue_elements:
+        test_queue_elements = []
+        for test_case in test_cases:
+            try:
+                test_name = test_case.__class__.__name__+"."+test_case._testMethodName
+            except AttributeError:
+                #Support for jython 2.1 (__testMethodName is pseudo-private in the test case)
+                test_name = test_case.__class__.__name__+"."+test_case._TestCase__testMethodName
+
+            test_queue_elements.append(test_case.__pydev_pyfile__+'|'+test_name)
+        
+        tests_queue.append(test_queue_elements)
+        
+    if jobs < 2:
+        return False
+        
+    sys.stdout.write('Running tests in parallel with: %s jobs.\n' %(jobs,))
+
+    
+    queue = Queue.Queue()
+    for item in tests_queue:
+        queue.put(item, block=False)
+
+    
+    providers = []
+    clients = []
+    for i in range(jobs):
+        test_cases_provider = CommunicationThread(queue)
+        providers.append(test_cases_provider)
+        
+        test_cases_provider.start()
+        port = test_cases_provider.port
+        
+        if coverage_files:
+            clients.append(ClientThread(i, port, verbosity, coverage_files.pop(0), coverage_include))
+        else:
+            clients.append(ClientThread(i, port, verbosity))
+        
+    for client in clients:
+        client.start()
+
+    client_alive = True
+    while client_alive:    
+        client_alive = False
+        for client in clients:
+            #Wait for all the clients to exit.
+            if not client.finished:
+                client_alive = True
+                time.sleep(.2)
+                break
+    
+    for provider in providers:
+        provider.shutdown()
+        
+    return True
+    
+    
+    
+#=======================================================================================================================
+# CommunicationThread
+#=======================================================================================================================
+class CommunicationThread(threading.Thread):
+    
+    def __init__(self, tests_queue):
+        threading.Thread.__init__(self)
+        self.setDaemon(True)
+        self.queue = tests_queue
+        self.finished = False
+        from pydev_imports import SimpleXMLRPCServer
+        
+        
+        # This is a hack to patch slow socket.getfqdn calls that
+        # BaseHTTPServer (and its subclasses) make.
+        # See: http://bugs.python.org/issue6085
+        # See: http://www.answermysearches.com/xmlrpc-server-slow-in-python-how-to-fix/2140/
+        try:
+            import BaseHTTPServer
+            def _bare_address_string(self):
+                host, port = self.client_address[:2]
+                return '%s' % host
+            BaseHTTPServer.BaseHTTPRequestHandler.address_string = _bare_address_string
+            
+        except:
+            pass
+        # End hack.
+
+
+        # Create server
+        
+        import pydev_localhost
+        server = SimpleXMLRPCServer((pydev_localhost.get_localhost(), 0), logRequests=False)
+        server.register_function(self.GetTestsToRun)
+        server.register_function(self.notifyStartTest)
+        server.register_function(self.notifyTest)
+        server.register_function(self.notifyCommands)
+        self.port = server.socket.getsockname()[1]
+        self.server = server
+        
+        
+    def GetTestsToRun(self, job_id):
+        '''
+        @param job_id:
+        
+        @return: list(str)
+            Each entry is a string in the format: filename|Test.testName 
+        '''
+        try:
+            ret = self.queue.get(block=False)
+            return ret
+        except: #Any exception getting from the queue (empty or not) means we finished our work on providing the tests.
+            self.finished = True
+            return []
+
+
+    def notifyCommands(self, job_id, commands):
+        #Batch notification.
+        for command in commands:
+            getattr(self, command[0])(job_id, *command[1], **command[2])
+            
+        return True
+
+    def notifyStartTest(self, job_id, *args, **kwargs):
+        pydev_runfiles_xml_rpc.notifyStartTest(*args, **kwargs)
+        return True
+    
+    
+    def notifyTest(self, job_id, *args, **kwargs):
+        pydev_runfiles_xml_rpc.notifyTest(*args, **kwargs)
+        return True
+    
+    def shutdown(self):
+        if hasattr(self.server, 'shutdown'):
+            self.server.shutdown()
+        else:
+            self._shutdown = True
+    
+    def run(self):
+        if hasattr(self.server, 'shutdown'):
+            self.server.serve_forever()
+        else:
+            self._shutdown = False
+            while not self._shutdown:
+                self.server.handle_request()
+        
+    
+    
+#=======================================================================================================================
+# Client
+#=======================================================================================================================
+class ClientThread(threading.Thread):
+    
+    def __init__(self, job_id, port, verbosity, coverage_output_file=None, coverage_include=None):
+        threading.Thread.__init__(self)
+        self.setDaemon(True)
+        self.port = port
+        self.job_id = job_id
+        self.verbosity = verbosity
+        self.finished = False
+        self.coverage_output_file = coverage_output_file
+        self.coverage_include = coverage_include
+
+
+    def _reader_thread(self, pipe, target):
+        while True:
+            target.write(pipe.read(1))
+            
+        
+    def run(self):
+        try:
+            import pydev_runfiles_parallel_client
+            #TODO: Support Jython:
+            #
+            #For jython, instead of using sys.executable, we should use:
+            #r'D:\bin\jdk_1_5_09\bin\java.exe',
+            #'-classpath',
+            #'D:/bin/jython-2.2.1/jython.jar',
+            #'org.python.util.jython',
+                
+            args = [
+                sys.executable, 
+                pydev_runfiles_parallel_client.__file__, 
+                str(self.job_id), 
+                str(self.port), 
+                str(self.verbosity), 
+            ]
+            
+            if self.coverage_output_file and self.coverage_include:
+                args.append(self.coverage_output_file)
+                args.append(self.coverage_include)
+            
+            import subprocess
+            if False:
+                proc = subprocess.Popen(args, env=os.environ, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+                
+                stdout_thread = threading.Thread(target=self._reader_thread,args=(proc.stdout, sys.stdout))
+                stdout_thread.setDaemon(True)
+                stdout_thread.start()
+    
+                stderr_thread = threading.Thread(target=self._reader_thread,args=(proc.stderr, sys.stderr))
+                stderr_thread.setDaemon(True)
+                stderr_thread.start()
+            else:
+                proc = subprocess.Popen(args, env=os.environ, shell=False)
+                proc.wait()
+
+        finally:
+            self.finished = True
+
diff --git a/python/helpers/pydev/pydev_runfiles_parallel_client.py b/python/helpers/pydev/pydev_runfiles_parallel_client.py
new file mode 100644
index 0000000..7e5187e
--- /dev/null
+++ b/python/helpers/pydev/pydev_runfiles_parallel_client.py
@@ -0,0 +1,214 @@
+from pydevd_constants import * #@UnusedWildImport
+from pydev_imports import xmlrpclib, _queue
+Queue = _queue.Queue
+import traceback
+from pydev_runfiles_coverage import StartCoverageSupportFromParams
+
+
+
+#=======================================================================================================================
+# ParallelNotification
+#=======================================================================================================================
+class ParallelNotification(object):
+
+    def __init__(self, method, args, kwargs):
+        self.method = method
+        self.args = args
+        self.kwargs = kwargs
+
+    def ToTuple(self):
+        return self.method, self.args, self.kwargs
+
+
+#=======================================================================================================================
+# KillServer
+#=======================================================================================================================
+class KillServer(object):
+    pass
+
+
+
+#=======================================================================================================================
+# ServerComm
+#=======================================================================================================================
+class ServerComm(threading.Thread):
+
+
+
+    def __init__(self, job_id, server):
+        self.notifications_queue = Queue()
+        threading.Thread.__init__(self)
+        self.setDaemon(False) #Wait for all the notifications to be passed before exiting!
+        assert job_id is not None
+        assert port is not None
+        self.job_id = job_id
+
+        self.finished = False
+        self.server = server
+
+
+    def run(self):
+        while True:
+            kill_found = False
+            commands = []
+            command = self.notifications_queue.get(block=True)
+            if isinstance(command, KillServer):
+                kill_found = True
+            else:
+                assert isinstance(command, ParallelNotification)
+                commands.append(command.ToTuple())
+
+            try:
+                while True:
+                    command = self.notifications_queue.get(block=False) #No block to create a batch.
+                    if isinstance(command, KillServer):
+                        kill_found = True
+                    else:
+                        assert isinstance(command, ParallelNotification)
+                        commands.append(command.ToTuple())
+            except:
+                pass #That's OK, we're getting it until it becomes empty so that we notify multiple at once.
+
+
+            if commands:
+                try:
+                    #Batch notification.
+                    self.server.lock.acquire()
+                    try:
+                        self.server.notifyCommands(self.job_id, commands)
+                    finally:
+                        self.server.lock.release()
+                except:
+                    traceback.print_exc()
+
+            if kill_found:
+                self.finished = True
+                return
+
+
+
+#=======================================================================================================================
+# ServerFacade
+#=======================================================================================================================
+class ServerFacade(object):
+
+
+    def __init__(self, notifications_queue):
+        self.notifications_queue = notifications_queue
+
+
+    def notifyTestsCollected(self, *args, **kwargs):
+        pass #This notification won't be passed
+
+
+    def notifyTestRunFinished(self, *args, **kwargs):
+        pass #This notification won't be passed
+
+
+    def notifyStartTest(self, *args, **kwargs):
+        self.notifications_queue.put_nowait(ParallelNotification('notifyStartTest', args, kwargs))
+
+
+    def notifyTest(self, *args, **kwargs):
+        self.notifications_queue.put_nowait(ParallelNotification('notifyTest', args, kwargs))
+
+
+
+#=======================================================================================================================
+# run_client
+#=======================================================================================================================
+def run_client(job_id, port, verbosity, coverage_output_file, coverage_include):
+    job_id = int(job_id)
+
+    import pydev_localhost
+    server = xmlrpclib.Server('http://%s:%s' % (pydev_localhost.get_localhost(), port))
+    server.lock = threading.Lock()
+
+
+    server_comm = ServerComm(job_id, server)
+    server_comm.start()
+
+    try:
+        server_facade = ServerFacade(server_comm.notifications_queue)
+        import pydev_runfiles
+        import pydev_runfiles_xml_rpc
+        pydev_runfiles_xml_rpc.SetServer(server_facade)
+
+        #Starts None and when the 1st test is gotten, it's started (because a server may be initiated and terminated
+        #before receiving any test -- which would mean a different process got all the tests to run).
+        coverage = None
+
+        try:
+            tests_to_run = [1]
+            while tests_to_run:
+                #Investigate: is it dangerous to use the same xmlrpclib server from different threads?
+                #It seems it should be, as it creates a new connection for each request...
+                server.lock.acquire()
+                try:
+                    tests_to_run = server.GetTestsToRun(job_id)
+                finally:
+                    server.lock.release()
+
+                if not tests_to_run:
+                    break
+
+                if coverage is None:
+                    _coverage_files, coverage = StartCoverageSupportFromParams(
+                        None, coverage_output_file, 1, coverage_include)
+
+
+                files_to_tests = {}
+                for test in tests_to_run:
+                    filename_and_test = test.split('|')
+                    if len(filename_and_test) == 2:
+                        files_to_tests.setdefault(filename_and_test[0], []).append(filename_and_test[1])
+
+                configuration = pydev_runfiles.Configuration(
+                    '',
+                    verbosity,
+                    None,
+                    None,
+                    None,
+                    files_to_tests,
+                    1, #Always single job here
+                    None,
+
+                    #The coverage is handled in this loop.
+                    coverage_output_file=None,
+                    coverage_include=None,
+                )
+                test_runner = pydev_runfiles.PydevTestRunner(configuration)
+                sys.stdout.flush()
+                test_runner.run_tests(handle_coverage=False)
+        finally:
+            if coverage is not None:
+                coverage.stop()
+                coverage.save()
+
+
+    except:
+        traceback.print_exc()
+    server_comm.notifications_queue.put_nowait(KillServer())
+
+
+
+#=======================================================================================================================
+# main
+#=======================================================================================================================
+if __name__ == '__main__':
+    if len(sys.argv) -1 == 3:
+        job_id, port, verbosity = sys.argv[1:]
+        coverage_output_file, coverage_include = None, None
+
+    elif len(sys.argv) -1 == 5:
+        job_id, port, verbosity, coverage_output_file, coverage_include = sys.argv[1:]
+
+    else:
+        raise AssertionError('Could not find out how to handle the parameters: '+sys.argv[1:])
+
+    job_id = int(job_id)
+    port = int(port)
+    verbosity = int(verbosity)
+    run_client(job_id, port, verbosity, coverage_output_file, coverage_include)
+
+
diff --git a/python/helpers/pydev/pydev_runfiles_pytest2.py b/python/helpers/pydev/pydev_runfiles_pytest2.py
new file mode 100644
index 0000000..e40d60f
--- /dev/null
+++ b/python/helpers/pydev/pydev_runfiles_pytest2.py
@@ -0,0 +1,230 @@
+import pickle
+import zlib
+import base64
+import os
+import py
+from py._code import code  # @UnresolvedImport
+import pydev_runfiles_xml_rpc
+from pydevd_file_utils import _NormFile
+import pytest
+import sys
+import time
+
+
+
+#===================================================================================================
+# Load filters with tests we should skip
+#===================================================================================================
+py_test_accept_filter = None
+
+def _load_filters():
+    global py_test_accept_filter
+    if py_test_accept_filter is None:
+        py_test_accept_filter = os.environ.get('PYDEV_PYTEST_SKIP')
+        if py_test_accept_filter:
+            py_test_accept_filter = pickle.loads(zlib.decompress(base64.b64decode(py_test_accept_filter)))
+        else:
+            py_test_accept_filter = {}
+
+
+def connect_to_server_for_communication_to_xml_rpc_on_xdist():
+    main_pid = os.environ.get('PYDEV_MAIN_PID')
+    if main_pid and main_pid != str(os.getpid()):
+        port = os.environ.get('PYDEV_PYTEST_SERVER')
+        if not port:
+            sys.stderr.write('Error: no PYDEV_PYTEST_SERVER environment variable defined.\n')
+        else:
+            pydev_runfiles_xml_rpc.InitializeServer(int(port), daemon=True)
+
+
+#===================================================================================================
+# Mocking to get clickable file representations
+#===================================================================================================
+def _MockFileRepresentation():
+    code.ReprFileLocation._original_toterminal = code.ReprFileLocation.toterminal
+
+    def toterminal(self, tw):
+        # filename and lineno output for each entry,
+        # using an output format that most editors understand
+        msg = self.message
+        i = msg.find("\n")
+        if i != -1:
+            msg = msg[:i]
+
+        tw.line('File "%s", line %s\n%s' %(os.path.abspath(self.path), self.lineno, msg))
+
+    code.ReprFileLocation.toterminal = toterminal
+
+
+def _UninstallMockFileRepresentation():
+    code.ReprFileLocation.toterminal = code.ReprFileLocation._original_toterminal #@UndefinedVariable
+
+
+class State:
+    numcollected = 0
+    start_time = time.time()
+
+
+def pytest_configure(*args, **kwargs):
+    _MockFileRepresentation()
+
+
+def pytest_collectreport(report):
+
+    i = 0
+    for x in report.result:
+        if isinstance(x, pytest.Item):
+            try:
+                # Call our setup (which may do a skip, in which
+                # case we won't count it).
+                pytest_runtest_setup(x)
+                i += 1
+            except:
+                continue
+    State.numcollected += i
+
+
+def pytest_collection_modifyitems():
+    connect_to_server_for_communication_to_xml_rpc_on_xdist()
+    pydev_runfiles_xml_rpc.notifyTestsCollected(State.numcollected)
+    State.numcollected = 0
+
+
+def pytest_unconfigure(*args, **kwargs):
+    _UninstallMockFileRepresentation()
+    pydev_runfiles_xml_rpc.notifyTestRunFinished('Finished in: %.2f secs.' % (time.time() - State.start_time,))
+
+
+def pytest_runtest_setup(item):
+    filename = item.fspath.strpath
+    test = item.location[2]
+    State.start_test_time = time.time()
+
+    pydev_runfiles_xml_rpc.notifyStartTest(filename, test)
+
+
+def report_test(cond, filename, test, captured_output, error_contents, delta):
+    '''
+    @param filename: 'D:\\src\\mod1\\hello.py'
+    @param test: 'TestCase.testMet1'
+    @param cond: fail, error, ok
+    '''
+    time_str = '%.2f' % (delta,)
+    pydev_runfiles_xml_rpc.notifyTest(cond, captured_output, error_contents, filename, test, time_str)
+
+
+def pytest_runtest_makereport(item, call):
+    report_when = call.when
+    report_duration = call.stop-call.start
+    excinfo = call.excinfo
+
+    if not call.excinfo:
+        report_outcome = "passed"
+        report_longrepr = None
+    else:
+        excinfo = call.excinfo
+        if not isinstance(excinfo, py.code.ExceptionInfo):
+            report_outcome = "failed"
+            report_longrepr = excinfo
+
+        elif excinfo.errisinstance(py.test.skip.Exception):
+            report_outcome = "skipped"
+            r = excinfo._getreprcrash()
+            report_longrepr = None #(str(r.path), r.lineno, r.message)
+
+        else:
+            report_outcome = "failed"
+            if call.when == "call":
+                report_longrepr = item.repr_failure(excinfo)
+
+            else: # exception in setup or teardown
+                report_longrepr = item._repr_failure_py(excinfo, style=item.config.option.tbstyle)
+
+    filename = item.fspath.strpath
+    test = item.location[2]
+
+    status = 'ok'
+    captured_output = ''
+    error_contents = ''
+
+    if report_outcome in ('passed', 'skipped'):
+        #passed or skipped: no need to report if in setup or teardown (only on the actual test if it passed).
+        if report_when in ('setup', 'teardown'):
+            return
+
+    else:
+        #It has only passed, skipped and failed (no error), so, let's consider error if not on call.
+        if report_when == 'setup':
+            if status == 'ok':
+                status = 'error'
+
+        elif report_when == 'teardown':
+            if status == 'ok':
+                status = 'error'
+
+        else:
+            #any error in the call (not in setup or teardown) is considered a regular failure.
+            status = 'fail'
+
+
+    if call.excinfo:
+        rep = report_longrepr
+        if hasattr(rep, 'reprcrash'):
+            reprcrash = rep.reprcrash
+            error_contents += str(reprcrash)
+            error_contents += '\n'
+
+        if hasattr(rep, 'reprtraceback'):
+            error_contents += str(rep.reprtraceback)
+
+        if hasattr(rep, 'sections'):
+            for name, content, sep in rep.sections:
+                error_contents += sep * 40
+                error_contents += name
+                error_contents += sep * 40
+                error_contents += '\n'
+                error_contents += content
+                error_contents += '\n'
+
+    if status != 'skip': #I.e.: don't event report skips...
+        report_test(status, filename, test, captured_output, error_contents, report_duration)
+
+
+
+@pytest.mark.tryfirst
+def pytest_runtest_setup(item):
+    '''
+    Skips tests. With xdist will be on a secondary process.
+    '''
+    _load_filters()
+    if not py_test_accept_filter:
+        return #Keep on going (nothing to filter)
+
+    f = _NormFile(str(item.parent.fspath))
+    name = item.name
+
+    if f not in py_test_accept_filter:
+        pytest.skip() # Skip the file
+
+    accept_tests = py_test_accept_filter[f]
+
+    if item.cls is not None:
+        class_name = item.cls.__name__
+    else:
+        class_name = None
+    for test in accept_tests:
+        if test == name:
+            #Direct match of the test (just go on with the default loading)
+            return
+
+        if class_name is not None:
+            if test == class_name + '.' + name:
+                return
+
+            if class_name == test:
+                return
+
+    # If we had a match it'd have returned already.
+    pytest.skip() # Skip the test
+
+
diff --git a/python/helpers/pydev/pydev_runfiles_unittest.py b/python/helpers/pydev/pydev_runfiles_unittest.py
new file mode 100644
index 0000000..78dfa52
--- /dev/null
+++ b/python/helpers/pydev/pydev_runfiles_unittest.py
@@ -0,0 +1,174 @@
+try:
+    import unittest2 as python_unittest
+except:
+    import unittest as python_unittest
+    
+import pydev_runfiles_xml_rpc
+import time
+import pydevd_io
+import traceback
+from pydevd_constants import * #@UnusedWildImport
+
+    
+#=======================================================================================================================
+# PydevTextTestRunner
+#=======================================================================================================================
+class PydevTextTestRunner(python_unittest.TextTestRunner):
+    
+    def _makeResult(self):
+        return PydevTestResult(self.stream, self.descriptions, self.verbosity)
+
+
+_PythonTextTestResult = python_unittest.TextTestRunner()._makeResult().__class__
+
+#=======================================================================================================================
+# PydevTestResult
+#=======================================================================================================================
+class PydevTestResult(_PythonTextTestResult):
+    
+
+    def startTest(self, test):
+        _PythonTextTestResult.startTest(self, test)
+        self.buf = pydevd_io.StartRedirect(keep_original_redirection=True, std='both')
+        self.start_time = time.time()
+        self._current_errors_stack = []
+        self._current_failures_stack = []
+        
+        try:
+            test_name = test.__class__.__name__+"."+test._testMethodName
+        except AttributeError:
+            #Support for jython 2.1 (__testMethodName is pseudo-private in the test case)
+            test_name = test.__class__.__name__+"."+test._TestCase__testMethodName
+            
+        pydev_runfiles_xml_rpc.notifyStartTest(
+            test.__pydev_pyfile__, test_name)
+
+
+
+
+    def getTestName(self, test):
+        try:
+            try:
+                test_name = test.__class__.__name__ + "." + test._testMethodName
+            except AttributeError:
+                #Support for jython 2.1 (__testMethodName is pseudo-private in the test case)
+                try:
+                    test_name = test.__class__.__name__ + "." + test._TestCase__testMethodName
+                #Support for class/module exceptions (test is instance of _ErrorHolder)
+                except:
+                    test_name = test.description.split()[1][1:-1] + ' <' + test.description.split()[0] + '>'
+        except:
+            traceback.print_exc()
+            return '<unable to get test name>'
+        return test_name
+
+
+    def stopTest(self, test):
+        end_time = time.time()
+        pydevd_io.EndRedirect(std='both')
+        
+        _PythonTextTestResult.stopTest(self, test)
+        
+        captured_output = self.buf.getvalue()
+        del self.buf
+        error_contents = ''
+        test_name = self.getTestName(test)
+            
+        
+        diff_time = '%.2f' % (end_time - self.start_time)
+        if not self._current_errors_stack and not self._current_failures_stack:
+            pydev_runfiles_xml_rpc.notifyTest(
+                'ok', captured_output, error_contents, test.__pydev_pyfile__, test_name, diff_time)
+        else:
+            self._reportErrors(self._current_errors_stack, self._current_failures_stack, captured_output, test_name)
+            
+            
+    def _reportErrors(self, errors, failures, captured_output, test_name, diff_time=''):
+        error_contents = []
+        for test, s in errors+failures:
+            if type(s) == type((1,)): #If it's a tuple (for jython 2.1)
+                sio = StringIO()
+                traceback.print_exception(s[0], s[1], s[2], file=sio)
+                s = sio.getvalue()
+            error_contents.append(s)
+        
+        sep = '\n'+self.separator1
+        error_contents = sep.join(error_contents)
+        
+        if errors and not failures:
+            try:
+                pydev_runfiles_xml_rpc.notifyTest(
+                    'error', captured_output, error_contents, test.__pydev_pyfile__, test_name, diff_time)
+            except:
+                file_start = error_contents.find('File "')
+                file_end = error_contents.find('", ', file_start)
+                if file_start != -1 and file_end != -1:
+                    file = error_contents[file_start+6:file_end]
+                else:
+                    file = '<unable to get file>'
+                pydev_runfiles_xml_rpc.notifyTest(
+                    'error', captured_output, error_contents, file, test_name, diff_time)
+            
+        elif failures and not errors:
+            pydev_runfiles_xml_rpc.notifyTest(
+                'fail', captured_output, error_contents, test.__pydev_pyfile__, test_name, diff_time)
+        
+        else: #Ok, we got both, errors and failures. Let's mark it as an error in the end.
+            pydev_runfiles_xml_rpc.notifyTest(
+                'error', captured_output, error_contents, test.__pydev_pyfile__, test_name, diff_time)
+                
+
+
+    def addError(self, test, err):
+        _PythonTextTestResult.addError(self, test, err)
+        #Support for class/module exceptions (test is instance of _ErrorHolder)
+        if not hasattr(self, '_current_errors_stack') or test.__class__.__name__ == '_ErrorHolder':
+            #Not in start...end, so, report error now (i.e.: django pre/post-setup)
+            self._reportErrors([self.errors[-1]], [], '', self.getTestName(test))
+        else:
+            self._current_errors_stack.append(self.errors[-1])
+
+
+    def addFailure(self, test, err):
+        _PythonTextTestResult.addFailure(self, test, err)
+        if not hasattr(self, '_current_failures_stack'):
+            #Not in start...end, so, report error now (i.e.: django pre/post-setup)
+            self._reportErrors([], [self.failures[-1]], '', self.getTestName(test))
+        else:
+            self._current_failures_stack.append(self.failures[-1])
+
+
+try:
+    #Version 2.7 onwards has a different structure... Let's not make any changes in it for now
+    #(waiting for bug: http://bugs.python.org/issue11798)
+    try:
+        from unittest2 import suite
+    except ImportError:
+        from unittest import suite
+    #===================================================================================================================
+    # PydevTestSuite
+    #===================================================================================================================
+    class PydevTestSuite(python_unittest.TestSuite):
+        pass
+    
+    
+except ImportError:
+    
+    #===================================================================================================================
+    # PydevTestSuite
+    #===================================================================================================================
+    class PydevTestSuite(python_unittest.TestSuite):
+    
+    
+        def run(self, result):
+            for index, test in enumerate(self._tests):
+                if result.shouldStop:
+                    break
+                test(result)
+    
+                # Let the memory be released! 
+                self._tests[index] = None
+    
+            return result
+    
+
diff --git a/python/helpers/pydev/pydev_runfiles_xml_rpc.py b/python/helpers/pydev/pydev_runfiles_xml_rpc.py
new file mode 100644
index 0000000..bcaa38a
--- /dev/null
+++ b/python/helpers/pydev/pydev_runfiles_xml_rpc.py
@@ -0,0 +1,269 @@
+import traceback
+import warnings
+
+from _pydev_filesystem_encoding import getfilesystemencoding
+from pydev_imports import xmlrpclib, _queue
+Queue = _queue.Queue
+from pydevd_constants import *
+
+#This may happen in IronPython (in Python it shouldn't happen as there are
+#'fast' replacements that are used in xmlrpclib.py)
+warnings.filterwarnings(
+    'ignore', 'The xmllib module is obsolete.*', DeprecationWarning)
+
+
+file_system_encoding = getfilesystemencoding()
+
+#=======================================================================================================================
+# _ServerHolder
+#=======================================================================================================================
+class _ServerHolder:
+    '''
+    Helper so that we don't have to use a global here.
+    '''
+    SERVER = None
+
+
+#=======================================================================================================================
+# SetServer
+#=======================================================================================================================
+def SetServer(server):
+    _ServerHolder.SERVER = server
+
+
+
+#=======================================================================================================================
+# ParallelNotification
+#=======================================================================================================================
+class ParallelNotification(object):
+
+    def __init__(self, method, args):
+        self.method = method
+        self.args = args
+
+    def ToTuple(self):
+        return self.method, self.args
+
+
+
+#=======================================================================================================================
+# KillServer
+#=======================================================================================================================
+class KillServer(object):
+    pass
+
+
+#=======================================================================================================================
+# ServerFacade
+#=======================================================================================================================
+class ServerFacade(object):
+
+
+    def __init__(self, notifications_queue):
+        self.notifications_queue = notifications_queue
+
+
+    def notifyTestsCollected(self, *args):
+        self.notifications_queue.put_nowait(ParallelNotification('notifyTestsCollected', args))
+
+    def notifyConnected(self, *args):
+        self.notifications_queue.put_nowait(ParallelNotification('notifyConnected', args))
+
+
+    def notifyTestRunFinished(self, *args):
+        self.notifications_queue.put_nowait(ParallelNotification('notifyTestRunFinished', args))
+
+
+    def notifyStartTest(self, *args):
+        self.notifications_queue.put_nowait(ParallelNotification('notifyStartTest', args))
+
+
+    def notifyTest(self, *args):
+        self.notifications_queue.put_nowait(ParallelNotification('notifyTest', args))
+
+
+
+
+
+#=======================================================================================================================
+# ServerComm
+#=======================================================================================================================
+class ServerComm(threading.Thread):
+
+
+
+    def __init__(self, notifications_queue, port, daemon=False):
+        threading.Thread.__init__(self)
+        self.setDaemon(daemon) # If False, wait for all the notifications to be passed before exiting!
+        self.finished = False
+        self.notifications_queue = notifications_queue
+
+        import pydev_localhost
+
+        # It is necessary to specify an encoding, that matches
+        # the encoding of all bytes-strings passed into an
+        # XMLRPC call: "All 8-bit strings in the data structure are assumed to use the
+        # packet encoding.  Unicode strings are automatically converted,
+        # where necessary."
+        # Byte strings most likely come from file names.
+        encoding = file_system_encoding
+        if encoding == "mbcs":
+            # Windos symbolic name for the system encoding CP_ACP.
+            # We need to convert it into a encoding that is recognized by Java.
+            # Unfortunately this is not always possible. You could use
+            # GetCPInfoEx and get a name similar to "windows-1251". Then
+            # you need a table to translate on a best effort basis. Much to complicated.
+            # ISO-8859-1 is good enough.
+            encoding = "ISO-8859-1"
+
+        self.server = xmlrpclib.Server('http://%s:%s' % (pydev_localhost.get_localhost(), port),
+                                       encoding=encoding)
+
+
+    def run(self):
+        while True:
+            kill_found = False
+            commands = []
+            command = self.notifications_queue.get(block=True)
+            if isinstance(command, KillServer):
+                kill_found = True
+            else:
+                assert isinstance(command, ParallelNotification)
+                commands.append(command.ToTuple())
+
+            try:
+                while True:
+                    command = self.notifications_queue.get(block=False) #No block to create a batch.
+                    if isinstance(command, KillServer):
+                        kill_found = True
+                    else:
+                        assert isinstance(command, ParallelNotification)
+                        commands.append(command.ToTuple())
+            except:
+                pass #That's OK, we're getting it until it becomes empty so that we notify multiple at once.
+
+
+            if commands:
+                try:
+                    self.server.notifyCommands(commands)
+                except:
+                    traceback.print_exc()
+
+            if kill_found:
+                self.finished = True
+                return
+
+
+
+#=======================================================================================================================
+# InitializeServer
+#=======================================================================================================================
+def InitializeServer(port, daemon=False):
+    if _ServerHolder.SERVER is None:
+        if port is not None:
+            notifications_queue = Queue()
+            _ServerHolder.SERVER = ServerFacade(notifications_queue)
+            _ServerHolder.SERVER_COMM = ServerComm(notifications_queue, port, daemon)
+            _ServerHolder.SERVER_COMM.start()
+        else:
+            #Create a null server, so that we keep the interface even without any connection.
+            _ServerHolder.SERVER = Null()
+            _ServerHolder.SERVER_COMM = Null()
+
+    try:
+        _ServerHolder.SERVER.notifyConnected()
+    except:
+        traceback.print_exc()
+
+
+
+#=======================================================================================================================
+# notifyTest
+#=======================================================================================================================
+def notifyTestsCollected(tests_count):
+    assert tests_count is not None
+    try:
+        _ServerHolder.SERVER.notifyTestsCollected(tests_count)
+    except:
+        traceback.print_exc()
+
+
+#=======================================================================================================================
+# notifyStartTest
+#=======================================================================================================================
+def notifyStartTest(file, test):
+    '''
+    @param file: the tests file (c:/temp/test.py)
+    @param test: the test ran (i.e.: TestCase.test1)
+    '''
+    assert file is not None
+    if test is None:
+        test = '' #Could happen if we have an import error importing module.
+
+    try:
+        _ServerHolder.SERVER.notifyStartTest(file, test)
+    except:
+        traceback.print_exc()
+
+
+def _encode_if_needed(obj):
+    if not IS_PY3K:
+        if isinstance(obj, str):
+            try:
+                return xmlrpclib.Binary(obj.encode('ISO-8859-1', 'xmlcharrefreplace'))
+            except:
+                return xmlrpclib.Binary(obj)
+
+        elif isinstance(obj, unicode):
+            return xmlrpclib.Binary(obj.encode('ISO-8859-1', 'xmlcharrefreplace'))
+
+    else:
+        if isinstance(obj, str):
+            return obj.encode('ISO-8859-1', 'xmlcharrefreplace')
+
+    return obj
+
+
+#=======================================================================================================================
+# notifyTest
+#=======================================================================================================================
+def notifyTest(cond, captured_output, error_contents, file, test, time):
+    '''
+    @param cond: ok, fail, error
+    @param captured_output: output captured from stdout
+    @param captured_output: output captured from stderr
+    @param file: the tests file (c:/temp/test.py)
+    @param test: the test ran (i.e.: TestCase.test1)
+    @param time: float with the number of seconds elapsed
+    '''
+    assert cond is not None
+    assert captured_output is not None
+    assert error_contents is not None
+    assert file is not None
+    if test is None:
+        test = '' #Could happen if we have an import error importing module.
+    assert time is not None
+    try:
+        captured_output = _encode_if_needed(captured_output)
+        error_contents = _encode_if_needed(error_contents)
+
+        _ServerHolder.SERVER.notifyTest(cond, captured_output, error_contents, file, test, time)
+    except:
+        traceback.print_exc()
+
+#=======================================================================================================================
+# notifyTestRunFinished
+#=======================================================================================================================
+def notifyTestRunFinished(total_time):
+    assert total_time is not None
+    try:
+        _ServerHolder.SERVER.notifyTestRunFinished(total_time)
+    except:
+        traceback.print_exc()
+
+
+#=======================================================================================================================
+# forceServerKill
+#=======================================================================================================================
+def forceServerKill():
+    _ServerHolder.SERVER_COMM.notifications_queue.put_nowait(KillServer())
diff --git a/python/helpers/pydev/pydev_sitecustomize/__not_in_default_pythonpath.txt b/python/helpers/pydev/pydev_sitecustomize/__not_in_default_pythonpath.txt
new file mode 100644
index 0000000..29cdc5b
--- /dev/null
+++ b/python/helpers/pydev/pydev_sitecustomize/__not_in_default_pythonpath.txt
@@ -0,0 +1 @@
+(no __init__.py file)
\ No newline at end of file
diff --git a/python/helpers/pydev/pydev_sitecustomize/sitecustomize.py b/python/helpers/pydev/pydev_sitecustomize/sitecustomize.py
new file mode 100644
index 0000000..78b9c79
--- /dev/null
+++ b/python/helpers/pydev/pydev_sitecustomize/sitecustomize.py
@@ -0,0 +1,192 @@
+'''
+    This module will:
+    - change the input() and raw_input() commands to change \r\n or \r into \n
+    - execute the user site customize -- if available
+    - change raw_input() and input() to also remove any trailing \r
+    
+    Up to PyDev 3.4 it also was setting the default encoding, but it was removed because of differences when
+    running from a shell (i.e.: now we just set the PYTHONIOENCODING related to that -- which is properly 
+    treated on Py 2.7 onwards).
+'''
+DEBUG = 0 #0 or 1 because of jython
+
+import sys
+encoding = None
+
+IS_PYTHON_3K = 0
+
+try:
+    if sys.version_info[0] == 3:
+        IS_PYTHON_3K = 1
+        
+except:
+    #That's OK, not all versions of python have sys.version_info
+    if DEBUG:
+        import traceback;traceback.print_exc() #@Reimport
+        
+#-----------------------------------------------------------------------------------------------------------------------
+#Line buffering
+if IS_PYTHON_3K:
+    #Python 3 has a bug (http://bugs.python.org/issue4705) in which -u doesn't properly make output/input unbuffered
+    #so, we need to enable that ourselves here.
+    try:
+        sys.stdout._line_buffering = True
+    except:
+        pass
+    try:
+        sys.stderr._line_buffering = True
+    except:
+        pass
+    try:
+        sys.stdin._line_buffering = True
+    except:
+        pass
+    
+    
+try:
+    import org.python.core.PyDictionary #@UnresolvedImport @UnusedImport -- just to check if it could be valid
+    def DictContains(d, key):
+        return d.has_key(key)
+except:
+    try:
+        #Py3k does not have has_key anymore, and older versions don't have __contains__
+        DictContains = dict.__contains__
+    except:
+        try:
+            DictContains = dict.has_key
+        except NameError:
+            def DictContains(d, key):
+                return d.has_key(key)
+
+
+#----------------------------------------------------------------------------------------------------------------------- 
+#now that we've finished the needed pydev sitecustomize, let's run the default one (if available)
+
+#Ok, some weirdness going on in Python 3k: when removing this module from the sys.module to import the 'real'
+#sitecustomize, all the variables in this scope become None (as if it was garbage-collected), so, the the reference
+#below is now being kept to create a cyclic reference so that it neven dies)
+__pydev_sitecustomize_module__ = sys.modules.get('sitecustomize') #A ref to this module
+
+
+#remove the pydev site customize (and the pythonpath for it)
+paths_removed = []
+try:
+    for c in sys.path[:]:
+        #Pydev controls the whole classpath in Jython already, so, we don't want a a duplicate for
+        #what we've already added there (this is needed to support Jython 2.5b1 onwards -- otherwise, as
+        #we added the sitecustomize to the pythonpath and to the classpath, we'd have to remove it from the
+        #classpath too -- and I don't think there's a way to do that... or not?)
+        if c.find('pydev_sitecustomize') != -1 or c == '__classpath__' or c == '__pyclasspath__' or \
+            c == '__classpath__/' or c == '__pyclasspath__/' or  c == '__classpath__\\' or c == '__pyclasspath__\\':
+            sys.path.remove(c)
+            if c.find('pydev_sitecustomize') == -1:
+                #We'll re-add any paths removed but the pydev_sitecustomize we added from pydev.
+                paths_removed.append(c)
+            
+    if DictContains(sys.modules, 'sitecustomize'):
+        del sys.modules['sitecustomize'] #this module
+except:
+    #print the error... should never happen (so, always show, and not only on debug)!
+    import traceback;traceback.print_exc() #@Reimport
+else:
+    #Now, execute the default sitecustomize
+    try:
+        import sitecustomize #@UnusedImport
+        sitecustomize.__pydev_sitecustomize_module__ = __pydev_sitecustomize_module__
+    except:
+        pass
+    
+    if not DictContains(sys.modules, 'sitecustomize'):
+        #If there was no sitecustomize, re-add the pydev sitecustomize (pypy gives a KeyError if it's not there)
+        sys.modules['sitecustomize'] = __pydev_sitecustomize_module__
+    
+    try:
+        if paths_removed:
+            if sys is None:
+                import sys
+            if sys is not None:
+                #And after executing the default sitecustomize, restore the paths (if we didn't remove it before,
+                #the import sitecustomize would recurse).
+                sys.path.extend(paths_removed)
+    except:
+        #print the error... should never happen (so, always show, and not only on debug)!
+        import traceback;traceback.print_exc() #@Reimport
+
+
+
+
+if not IS_PYTHON_3K:
+    try:
+        #Redefine input and raw_input only after the original sitecustomize was executed
+        #(because otherwise, the original raw_input and input would still not be defined)
+        import __builtin__
+        original_raw_input = __builtin__.raw_input
+        original_input = __builtin__.input
+        
+        
+        def raw_input(prompt=''):
+            #the original raw_input would only remove a trailing \n, so, at
+            #this point if we had a \r\n the \r would remain (which is valid for eclipse)
+            #so, let's remove the remaining \r which python didn't expect.
+            ret = original_raw_input(prompt)
+                
+            if ret.endswith('\r'):
+                return ret[:-1]
+                
+            return ret
+        raw_input.__doc__ = original_raw_input.__doc__
+    
+        def input(prompt=''):
+            #input must also be rebinded for using the new raw_input defined
+            return eval(raw_input(prompt))
+        input.__doc__ = original_input.__doc__
+        
+        
+        __builtin__.raw_input = raw_input
+        __builtin__.input = input
+    
+    except:
+        #Don't report errors at this stage
+        if DEBUG:
+            import traceback;traceback.print_exc() #@Reimport
+    
+else:
+    try:
+        import builtins #Python 3.0 does not have the __builtin__ module @UnresolvedImport
+        original_input = builtins.input
+        def input(prompt=''):
+            #the original input would only remove a trailing \n, so, at
+            #this point if we had a \r\n the \r would remain (which is valid for eclipse)
+            #so, let's remove the remaining \r which python didn't expect.
+            ret = original_input(prompt)
+                
+            if ret.endswith('\r'):
+                return ret[:-1]
+                
+            return ret
+        input.__doc__ = original_input.__doc__
+        builtins.input = input
+    except:
+        #Don't report errors at this stage
+        if DEBUG:
+            import traceback;traceback.print_exc() #@Reimport
+    
+
+
+try:
+    #The original getpass doesn't work from the eclipse console, so, let's put a replacement
+    #here (note that it'll not go into echo mode in the console, so, what' the user writes
+    #will actually be seen)
+    import getpass #@UnresolvedImport
+    if IS_PYTHON_3K:
+        def pydev_getpass(msg='Password: '):
+            return input(msg)
+    else:
+        def pydev_getpass(msg='Password: '):
+            return raw_input(msg)
+    
+    getpass.getpass = pydev_getpass
+except:
+    #Don't report errors at this stage
+    if DEBUG:
+        import traceback;traceback.print_exc() #@Reimport
diff --git a/python/helpers/pydev/pydev_umd.py b/python/helpers/pydev/pydev_umd.py
new file mode 100644
index 0000000..0bfeda7
--- /dev/null
+++ b/python/helpers/pydev/pydev_umd.py
@@ -0,0 +1,172 @@
+"""
+The UserModuleDeleter and runfile methods are copied from
+Spyder and carry their own license agreement.
+http://code.google.com/p/spyderlib/source/browse/spyderlib/widgets/externalshell/sitecustomize.py
+
+Spyder License Agreement (MIT License)
+--------------------------------------
+
+Copyright (c) 2009-2012 Pierre Raybaut
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+"""
+
+import sys
+import os
+
+# The following classes and functions are mainly intended to be used from
+# an interactive Python session
+class UserModuleDeleter:
+    """
+    User Module Deleter (UMD) aims at deleting user modules
+    to force Python to deeply reload them during import
+
+    pathlist [list]: blacklist in terms of module path
+    namelist [list]: blacklist in terms of module name
+    """
+    def __init__(self, namelist=None, pathlist=None):
+        if namelist is None:
+            namelist = []
+        self.namelist = namelist
+        if pathlist is None:
+            pathlist = []
+        self.pathlist = pathlist
+        try:
+            # blacklist all files in org.python.pydev/pysrc
+            import pydev_pysrc, inspect
+            self.pathlist.append(os.path.dirname(pydev_pysrc.__file__))
+        except:
+            pass
+        self.previous_modules = list(sys.modules.keys())
+
+    def is_module_blacklisted(self, modname, modpath):
+        for path in [sys.prefix] + self.pathlist:
+            if modpath.startswith(path):
+                return True
+        else:
+            return set(modname.split('.')) & set(self.namelist)
+
+    def run(self, verbose=False):
+        """
+        Del user modules to force Python to deeply reload them
+
+        Do not del modules which are considered as system modules, i.e.
+        modules installed in subdirectories of Python interpreter's binary
+        Do not del C modules
+        """
+        log = []
+        modules_copy = dict(sys.modules)
+        for modname, module in modules_copy.items():
+            if modname == 'aaaaa':
+                print(modname, module)
+                print(self.previous_modules)
+            if modname not in self.previous_modules:
+                modpath = getattr(module, '__file__', None)
+                if modpath is None:
+                    # *module* is a C module that is statically linked into the
+                    # interpreter. There is no way to know its path, so we
+                    # choose to ignore it.
+                    continue
+                if not self.is_module_blacklisted(modname, modpath):
+                    log.append(modname)
+                    del sys.modules[modname]
+        if verbose and log:
+            print("\x1b[4;33m%s\x1b[24m%s\x1b[0m" % ("UMD has deleted",
+                                                     ": " + ", ".join(log)))
+
+__umd__ = None
+
+_get_globals_callback = None
+def _set_globals_function(get_globals):
+    global _get_globals_callback
+    _get_globals_callback = get_globals
+def _get_globals():
+    """Return current Python interpreter globals namespace"""
+    if _get_globals_callback is not None:
+        return _get_globals_callback()
+    else:
+        try:
+            from __main__ import __dict__ as namespace
+        except ImportError:
+            try:
+                # The import fails on IronPython
+                import __main__
+                namespace = __main__.__dict__
+            except:
+                namespace
+        shell = namespace.get('__ipythonshell__')
+        if shell is not None and hasattr(shell, 'user_ns'):
+            # IPython 0.12+ kernel
+            return shell.user_ns
+        else:
+            # Python interpreter
+            return namespace
+        return namespace
+
+
+def runfile(filename, args=None, wdir=None, namespace=None):
+    """
+    Run filename
+    args: command line arguments (string)
+    wdir: working directory
+    """
+    try:
+        if hasattr(filename, 'decode'):
+            filename = filename.decode('utf-8')
+    except (UnicodeError, TypeError):
+        pass
+    global __umd__
+    if os.environ.get("PYDEV_UMD_ENABLED", "").lower() == "true":
+        if __umd__ is None:
+            namelist = os.environ.get("PYDEV_UMD_NAMELIST", None)
+            if namelist is not None:
+                namelist = namelist.split(',')
+            __umd__ = UserModuleDeleter(namelist=namelist)
+        else:
+            verbose = os.environ.get("PYDEV_UMD_VERBOSE", "").lower() == "true"
+            __umd__.run(verbose=verbose)
+    if args is not None and not isinstance(args, basestring):
+        raise TypeError("expected a character buffer object")
+    if namespace is None:
+        namespace = _get_globals()
+    if '__file__' in namespace:
+        old_file = namespace['__file__']
+    else:
+        old_file = None
+    namespace['__file__'] = filename
+    sys.argv = [filename]
+    if args is not None:
+        for arg in args.split():
+            sys.argv.append(arg)
+    if wdir is not None:
+        try:
+            if hasattr(wdir, 'decode'):
+                wdir = wdir.decode('utf-8')
+        except (UnicodeError, TypeError):
+            pass
+        os.chdir(wdir)
+    execfile(filename, namespace)
+    sys.argv = ['']
+    if old_file is None:
+        del namespace['__file__']
+    else:
+        namespace['__file__'] = old_file
diff --git a/python/helpers/pydev/pydevconsole.py b/python/helpers/pydev/pydevconsole.py
index 6c0640f..2f07a82 100644
--- a/python/helpers/pydev/pydevconsole.py
+++ b/python/helpers/pydev/pydevconsole.py
@@ -10,7 +10,6 @@
 import sys
 
 from pydevd_constants import USE_LIB_COPY
-from pydevd_constants import IS_JYTHON
 
 if USE_LIB_COPY:
     import _pydev_threading as threading
@@ -23,15 +22,7 @@
 
 import pydevd_vars
 
-from pydev_imports import Exec
-
-try:
-    if USE_LIB_COPY:
-        import _pydev_Queue as _queue
-    else:
-        import Queue as _queue
-except:
-    import queue as _queue
+from pydev_imports import Exec, _queue
 
 try:
     import __builtin__
@@ -47,7 +38,7 @@
     setattr(__builtin__, 'True', 1) #Python 3.0 does not accept __builtin__.True = 1 in its syntax
     setattr(__builtin__, 'False', 0)
 
-from pydev_console_utils import BaseInterpreterInterface
+from pydev_console_utils import BaseInterpreterInterface, BaseStdIn
 from pydev_console_utils import CodeFragment
 
 IS_PYTHON_3K = False
@@ -59,17 +50,6 @@
     #That's OK, not all versions of python have sys.version_info
     pass
 
-try:
-    try:
-        if USE_LIB_COPY:
-            import _pydev_xmlrpclib as xmlrpclib
-        else:
-            import xmlrpclib
-    except ImportError:
-        import xmlrpc.client as xmlrpclib
-except ImportError:
-    import _pydev_xmlrpclib as xmlrpclib
-
 
 class Command:
     def __init__(self, interpreter, code_fragment):
@@ -145,23 +125,71 @@
 
             traceback.print_exc()
             return []
-        
+
     def close(self):
         sys.exit(0)
 
     def get_greeting_msg(self):
-        return 'PyDev console: starting.'
+        return 'PyDev console: starting.\n'
+
+
+class _ProcessExecQueueHelper:
+    _debug_hook = None
+    _return_control_osc = False
+
+def set_debug_hook(debug_hook):
+    _ProcessExecQueueHelper._debug_hook = debug_hook
 
 
 def process_exec_queue(interpreter):
+
+    from pydev_ipython.inputhook import get_inputhook, set_return_control_callback
+
+    def return_control():
+        ''' A function that the inputhooks can call (via inputhook.stdin_ready()) to find
+            out if they should cede control and return '''
+        if _ProcessExecQueueHelper._debug_hook:
+            # Some of the input hooks check return control without doing
+            # a single operation, so we don't return True on every
+            # call when the debug hook is in place to allow the GUI to run
+            # XXX: Eventually the inputhook code will have diverged enough
+            # from the IPython source that it will be worthwhile rewriting
+            # it rather than pretending to maintain the old API
+            _ProcessExecQueueHelper._return_control_osc = not _ProcessExecQueueHelper._return_control_osc
+            if _ProcessExecQueueHelper._return_control_osc:
+                return True
+
+        if not interpreter.exec_queue.empty():
+            return True
+        return False
+
+    set_return_control_callback(return_control)
+
     while 1:
+        # Running the request may have changed the inputhook in use
+        inputhook = get_inputhook()
+
+        if _ProcessExecQueueHelper._debug_hook:
+            _ProcessExecQueueHelper._debug_hook()
+
+        if inputhook:
+            try:
+                # Note: it'll block here until return_control returns True.
+                inputhook()
+            except:
+                import traceback;traceback.print_exc()
         try:
             try:
-                codeFragment = interpreter.exec_queue.get(block=True, timeout=0.05)
+                code_fragment = interpreter.exec_queue.get(block=True, timeout=1/20.) # 20 calls/second
             except _queue.Empty:
                 continue
 
-            more = interpreter.addExec(codeFragment)
+            if callable(code_fragment):
+                # It can be a callable (i.e.: something that must run in the main
+                # thread can be put in the queue for later execution).
+                code_fragment()
+            else:
+                more = interpreter.addExec(code_fragment)
         except KeyboardInterrupt:
             interpreter.buffer = None
             continue
@@ -221,16 +249,6 @@
     return "PyCharm"
 
 
-def ipython_editor(interpreter):
-    def editor(file, line):
-        if file is None:
-            file = ""
-        if line is None:
-            line = "-1"
-        interpreter.ipython_editor(file, line)
-
-    return editor
-
 #=======================================================================================================================
 # StartServer
 #=======================================================================================================================
@@ -238,11 +256,8 @@
     if port == 0:
         host = ''
 
-    try:
-        from _pydev_xmlrpc_hook import InputHookedXMLRPCServer as XMLRPCServer  #@UnusedImport
-    except:
-        #I.e.: supporting the internal Jython version in PyDev to create a Jython interactive console inside Eclipse.
-        from pydev_imports import SimpleXMLRPCServer as XMLRPCServer  #@Reimport
+    #I.e.: supporting the internal Jython version in PyDev to create a Jython interactive console inside Eclipse.
+    from pydev_imports import SimpleXMLRPCServer as XMLRPCServer  #@Reimport
 
     try:
         server = XMLRPCServer((host, port), logRequests=False, allow_none=True)
@@ -262,12 +277,10 @@
     server.register_function(interpreter.interrupt)
     server.register_function(handshake)
     server.register_function(interpreter.connectToDebugger)
+    server.register_function(interpreter.hello)
 
-    if IPYTHON:
-        try:
-            interpreter.interpreter.ipython.hooks.editor = ipython_editor(interpreter)
-        except:
-            pass
+    # Functions for GUI main loop integration
+    server.register_function(interpreter.enableGui)
 
     if port == 0:
         (h, port) = server.socket.getsockname()
@@ -279,7 +292,6 @@
     sys.stderr.write(interpreter.get_greeting_msg())
     sys.stderr.flush()
 
-    interpreter.server = server
     server.serve_forever()
 
     return server
@@ -318,10 +330,9 @@
 
     return interpreterInterface.getCompletions(text, token)
 
-def get_frame():
-    interpreterInterface = get_interpreter()
-
-    return interpreterInterface.getFrame()
+#===============================================================================
+# Debugger integration
+#===============================================================================
 
 def exec_code(code, globals, locals):
     interpreterInterface = get_interpreter()
@@ -338,20 +349,6 @@
     return False
 
 
-def read_line(s):
-    ret = ''
-
-    while True:
-        c = s.recv(1)
-
-        if c == '\n' or c == '':
-            break
-        else:
-            ret += c
-
-    return ret
-
-# Debugger integration
 
 class ConsoleWriter(InteractiveInterpreter):
     skip = 0
@@ -408,7 +405,7 @@
         sys.stderr.write(''.join(lines))
 
 def consoleExec(thread_id, frame_id, expression):
-    """returns 'False' in case expression is partialy correct
+    """returns 'False' in case expression is partially correct
     """
     frame = pydevd_vars.findFrame(thread_id, frame_id)
 
@@ -452,9 +449,8 @@
 #=======================================================================================================================
 # main
 #=======================================================================================================================
-
-
 if __name__ == '__main__':
+    sys.stdin = BaseStdIn()
     port, client_port = sys.argv[1:3]
     import pydev_localhost
 
diff --git a/python/helpers/pydev/pydevd.py b/python/helpers/pydev/pydevd.py
index 2077903..1733c26 100644
--- a/python/helpers/pydev/pydevd.py
+++ b/python/helpers/pydev/pydevd.py
@@ -1,4 +1,6 @@
 #IMPORTANT: pydevd_constants must be the 1st thing defined because it'll keep a reference to the original sys._getframe
+from __future__ import nested_scopes # Jython 2.1 support
+
 import traceback
 
 from django_debug import DjangoLineBreakpoint
@@ -35,7 +37,7 @@
                          CMD_ADD_DJANGO_EXCEPTION_BREAK, \
                          CMD_REMOVE_DJANGO_EXCEPTION_BREAK, \
                          CMD_SMART_STEP_INTO,\
-    InternalChangeVariable, \
+                         InternalChangeVariable, \
                          InternalGetCompletions, \
                          InternalEvaluateExpression, \
                          InternalConsoleExec, \
@@ -55,23 +57,35 @@
                          PydevdLog, \
                          StartClient, \
                          StartServer, \
-                         InternalSetNextStatementThread, ReloadCodeCommand
+                         InternalSetNextStatementThread, \
+                         ReloadCodeCommand, \
+    CMD_SET_PY_EXCEPTION, \
+                         CMD_IGNORE_THROWN_EXCEPTION_AT,\
+                         InternalGetBreakpointException, \
+                         InternalSendCurrExceptionTrace,\
+                         InternalSendCurrExceptionTraceProceeded,\
+                         CMD_ENABLE_DONT_TRACE, \
+                         CMD_GET_FILE_CONTENTS,\
+                         CMD_SET_PROPERTY_TRACE, CMD_RUN_CUSTOM_OPERATION,\
+                         InternalRunCustomOperation, CMD_EVALUATE_CONSOLE_EXPRESSION, InternalEvaluateConsoleExpression,\
+                         InternalConsoleGetCompletions
+
 from pydevd_file_utils import NormFileToServer, GetFilenameAndBase
 import pydevd_file_utils
 import pydevd_vars
 import pydevd_vm_type
 import pydevd_tracing
 import pydevd_io
-import pydev_monkey
 from pydevd_additional_thread_info import PyDBAdditionalThreadInfo
 from pydevd_custom_frames import CustomFramesContainer, CustomFramesContainerInit
+import pydevd_dont_trace
+import pydevd_traceproperty
 
+from _pydev_imps import _pydev_time as time
 
 if USE_LIB_COPY:
-    import _pydev_time as time
     import _pydev_threading as threading
 else:
-    import time
     import threading
 
 import os
@@ -80,10 +94,13 @@
 threadingEnumerate = threading.enumerate
 threadingCurrentThread = threading.currentThread
 
+try:
+    'dummy'.encode('utf-8') # Added because otherwise Jython 2.2.1 wasn't finding the encoding (if it wasn't loaded in the main thread).
+except:
+    pass
 
 DONT_TRACE = {
               # commonly used things from the stdlib that we don't want to trace
-              'threading.py':1,
               'Queue.py':1,
               'queue.py':1,
               'socket.py':1,
@@ -92,12 +109,19 @@
               'threading.py':1,
 
               #things from pydev that we don't want to trace
+              '_pydev_execfile.py':1,
+              '_pydev_jython_execfile.py':1,
+              '_pydev_threading':1,
+              'django_debug.py':1,
+              'django_frame.py':1,
+              'pydev_log.py':1,
               'pydevd.py':1 ,
               'pydevd_additional_thread_info.py':1,
-              'pydevd_custom_frames.py':1,
               'pydevd_comm.py':1,
               'pydevd_console.py':1 ,
               'pydevd_constants.py':1,
+              'pydevd_custom_frames.py':1,
+              'pydevd_dont_trace.py':1,
               'pydevd_exec.py':1,
               'pydevd_exec2.py':1,
               'pydevd_file_utils.py':1,
@@ -105,17 +129,18 @@
               'pydevd_import_class.py':1 ,
               'pydevd_io.py':1 ,
               'pydevd_psyco_stub.py':1,
+              'pydevd_referrers.py':1 ,
               'pydevd_reload.py':1 ,
               'pydevd_resolver.py':1 ,
+              'pydevd_save_locals.py':1 ,
+              'pydevd_signature.py':1,
               'pydevd_stackless.py':1 ,
               'pydevd_traceproperty.py':1,
               'pydevd_tracing.py':1 ,
-              'pydevd_signature.py':1,
               'pydevd_utils.py':1,
               'pydevd_vars.py':1,
               'pydevd_vm_type.py':1,
-              '_pydev_execfile.py':1,
-              '_pydev_jython_execfile.py':1
+              'pydevd_xml.py':1,
             }
 
 if IS_PY3K:
@@ -135,17 +160,28 @@
 from _pydev_filesystem_encoding import getfilesystemencoding
 file_system_encoding = getfilesystemencoding()
 
-def isThreadAlive(t):
-    try:
-        # If thread is not started yet we treat it as alive.
-        # It is required to debug threads started by start_new_thread in Python 3.4
-        if hasattr(t, '_is_stopped'):
-            alive = not t._is_stopped
-        else:
-            alive = not t.__stopped
-    except:
-        alive = t.isAlive()
-    return alive
+
+# Hack for https://sw-brainwy.rhcloud.com/tracker/PyDev/363 (i.e.: calling isAlive() can throw AssertionError under some circumstances)
+# It is required to debug threads started by start_new_thread in Python 3.4
+_temp = threading.Thread()
+if hasattr(_temp, '_is_stopped'): # Python 3.4 has this
+    def isThreadAlive(t):
+        try:
+            return not t._is_stopped
+        except:
+            return t.isAlive()
+    
+elif hasattr(_temp, '_Thread__stopped'): # Python 2.7 has this
+    def isThreadAlive(t):
+        try:
+            return not t._Thread__stopped
+        except:
+            return t.isAlive()
+    
+else: # Haven't checked all other versions, so, let's use the regular isAlive call in this case.
+    def isThreadAlive(t):
+        return t.isAlive()
+del _temp
 
 #=======================================================================================================================
 # PyDBCommandThread
@@ -159,7 +195,7 @@
         self.setName('pydevd.CommandThread')
 
     def OnRun(self):
-        for i in range(1, 10):
+        for i in xrange(1, 10):
             time.sleep(0.5) #this one will only start later on (because otherwise we may not have any non-daemon threads
             if self.killReceived:
                 return
@@ -187,7 +223,7 @@
     for t in threads:
         if hasattr(t, 'doKillPydevThread'):
             t.doKillPydevThread()
-    
+
 
 #=======================================================================================================================
 # PyDBCheckAliveThread
@@ -220,62 +256,6 @@
     def doKillPydevThread(self):
         pass
 
-if USE_LIB_COPY:
-    import _pydev_thread as thread
-else:
-    try:
-        import thread
-    except ImportError:
-        import _thread as thread #Py3K changed it.
-
-_original_start_new_thread = thread.start_new_thread
-
-if getattr(thread, '_original_start_new_thread', None) is None:
-    thread._original_start_new_thread = thread.start_new_thread
-
-#=======================================================================================================================
-# NewThreadStartup
-#=======================================================================================================================
-class NewThreadStartup:
-
-    def __init__(self, original_func, args, kwargs):
-        self.original_func = original_func
-        self.args = args
-        self.kwargs = kwargs
-
-    def __call__(self):
-        global_debugger = GetGlobalDebugger()
-        global_debugger.SetTrace(global_debugger.trace_dispatch)
-        self.original_func(*self.args, **self.kwargs)
-
-thread.NewThreadStartup = NewThreadStartup
-
-#=======================================================================================================================
-# pydev_start_new_thread
-#=======================================================================================================================
-def _pydev_start_new_thread(function, args, kwargs={}):
-    '''
-    We need to replace the original thread.start_new_thread with this function so that threads started through
-    it and not through the threading module are properly traced.
-    '''
-    if USE_LIB_COPY:
-        import _pydev_thread as thread
-    else:
-        try:
-            import thread
-        except ImportError:
-            import _thread as thread #Py3K changed it.
-
-    return thread._original_start_new_thread(thread.NewThreadStartup(function, args, kwargs), ())
-
-class PydevStartNewThread(object):
-    def __get__(self, obj, type=None):
-        return self
-
-    def __call__(self, function, args, kwargs={}):
-        return _pydev_start_new_thread(function, args, kwargs)
-
-pydev_start_new_thread = PydevStartNewThread()
 
 
 #=======================================================================================================================
@@ -304,10 +284,18 @@
         self.quitting = None
         self.cmdFactory = NetCommandFactory()
         self._cmd_queue = {}  # the hash of Queues. Key is thread id, value is thread
+
         self.breakpoints = {}
         self.django_breakpoints = {}
-        self.exception_set = {}
-        self.always_exception_set = set()
+
+        self.file_to_id_to_line_breakpoint = {}
+        self.file_to_id_to_django_breakpoint = {}
+
+        # Note: breakpoints dict should not be mutated: a copy should be created
+        # and later it should be assigned back (to prevent concurrency issues).
+        self.break_on_uncaught_exceptions = {}
+        self.break_on_caught_exceptions = {}
+
         self.django_exception_break = {}
         self.readyToRun = False
         self._main_lock = threading.Lock()
@@ -316,15 +304,31 @@
         CustomFramesContainer._py_db_command_thread_event = self._py_db_command_thread_event
         self._finishDebuggingSession = False
         self._terminationEventSent = False
-        self.force_post_mortem_stop = 0
         self.signature_factory = None
         self.SetTrace = pydevd_tracing.SetTrace
+        self.break_on_exceptions_thrown_in_same_context = False
+        self.ignore_exceptions_thrown_in_lines_with_ignore_exception = True
+
+        # Suspend debugger even if breakpoint condition raises an exception
+        SUSPEND_ON_BREAKPOINT_EXCEPTION = True
+        self.suspend_on_breakpoint_exception = SUSPEND_ON_BREAKPOINT_EXCEPTION
+
+        # By default user can step into properties getter/setter/deleter methods
+        self.disable_property_trace = False
+        self.disable_property_getter_trace = False
+        self.disable_property_setter_trace = False
+        self.disable_property_deleter_trace = False
 
         #this is a dict of thread ids pointing to thread ids. Whenever a command is passed to the java end that
         #acknowledges that a thread was created, the thread id should be passed here -- and if at some time we do not
         #find that thread alive anymore, we must remove it from this list and make the java side know that the thread
         #was killed.
         self._running_thread_ids = {}
+        self._set_breakpoints_with_id = False
+
+        # This attribute holds the file-> lines which have an @IgnoreException.
+        self.filename_to_lines_where_exceptions_are_ignored = {}
+
 
     def haveAliveThreads(self):
         for t in threadingEnumerate():
@@ -398,12 +402,12 @@
         global bufferStdErrToServer
 
         if bufferStdOutToServer:
-                initStdoutRedirect()
-                self.checkOutput(sys.stdoutBuf, 1) #@UndefinedVariable
+            initStdoutRedirect()
+            self.checkOutput(sys.stdoutBuf, 1) #@UndefinedVariable
 
         if bufferStdErrToServer:
-                initStderrRedirect()
-                self.checkOutput(sys.stderrBuf, 2) #@UndefinedVariable
+            initStderrRedirect()
+            self.checkOutput(sys.stderrBuf, 2) #@UndefinedVariable
 
     def checkOutput(self, out, outCtx):
         '''Checks the output to see if we have to send some buffered output to the debug server
@@ -521,6 +525,58 @@
             additionalInfo = None
 
 
+    def consolidate_breakpoints(self, file, id_to_breakpoint, breakpoints):
+        break_dict = {}
+        for breakpoint_id, pybreakpoint in DictIterItems(id_to_breakpoint):
+            break_dict[pybreakpoint.line] = pybreakpoint
+
+        breakpoints[file] = break_dict
+
+
+    def add_break_on_exception(
+        self,
+        exception,
+        notify_always,
+        notify_on_terminate,
+        notify_on_first_raise_only,
+        ):
+        eb = ExceptionBreakpoint(
+            exception,
+            notify_always,
+            notify_on_terminate,
+            notify_on_first_raise_only,
+        )
+
+        if eb.notify_on_terminate:
+            cp = self.break_on_uncaught_exceptions.copy()
+            cp[exception] = eb
+            if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0:
+                pydev_log.error("Exceptions to hook on terminate: %s\n" % (cp,))
+            self.break_on_uncaught_exceptions = cp
+
+        if eb.notify_always:
+            cp = self.break_on_caught_exceptions.copy()
+            cp[exception] = eb
+            if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0:
+                pydev_log.error("Exceptions to hook always: %s\n" % (cp,))
+            self.break_on_caught_exceptions = cp
+
+        return eb
+
+    def update_after_exceptions_added(self, added):
+        updated_on_caught = False
+        updated_on_uncaught = False
+
+        for eb in added:
+            if not updated_on_uncaught and eb.notify_on_terminate:
+                updated_on_uncaught = True
+                update_exception_hook(self)
+
+            if not updated_on_caught and eb.notify_always:
+                updated_on_caught = True
+                self.setTracingForUntracedContexts()
+
+
     def processNetCommand(self, cmd_id, seq, text):
         '''Processes a command received from the Java side
 
@@ -536,6 +592,7 @@
         it may be worth refactoring it (actually, reordering the ifs so that the ones used mostly come before
         probably will give better performance).
         '''
+        #print ID_TO_MEANING[str(cmd_id)], repr(text)
 
         self._main_lock.acquire()
         try:
@@ -546,9 +603,28 @@
 
                 elif cmd_id == CMD_VERSION:
                     # response is version number
-                    local_version, pycharm_os = text.split('\t', 1)
+                    # ide_os should be 'WINDOWS' or 'UNIX'.
+                    ide_os = 'WINDOWS'
 
-                    pydevd_file_utils.set_pycharm_os(pycharm_os)
+                    # Breakpoints can be grouped by 'LINE' or by 'ID'.
+                    breakpoints_by = 'LINE'
+
+                    splitted = text.split('\t')
+                    if len(splitted) == 1:
+                        _local_version = splitted
+
+                    elif len(splitted) == 2:
+                        _local_version, ide_os = splitted
+
+                    elif len(splitted) == 3:
+                        _local_version, ide_os, breakpoints_by = splitted
+
+                    if breakpoints_by == 'ID':
+                        self._set_breakpoints_with_id = True
+                    else:
+                        self._set_breakpoints_with_id = False
+
+                    pydevd_file_utils.set_ide_os(ide_os)
 
                     cmd = self.cmdFactory.makeVersionMessage(seq)
 
@@ -684,26 +760,42 @@
 
                 elif cmd_id == CMD_SET_BREAK:
                     # func name: 'None': match anything. Empty: match global, specified: only method context.
-
                     # command to add some breakpoint.
                     # text is file\tline. Add to breakpoints dictionary
-                    type, file, line, condition, expression = text.split('\t', 4)
+                    if self._set_breakpoints_with_id:
+                        breakpoint_id, type, file, line, func_name, condition, expression = text.split('\t', 6)
 
-                    if not IS_PY3K:  # In Python 3, the frame object will have unicode for the file, whereas on python 2 it has a byte-array encoded with the filesystem encoding.
-                        file = file.encode(file_system_encoding)
-
-                    if condition.startswith('**FUNC**'):
-                        func_name, condition = condition.split('\t', 1)
+                        breakpoint_id = int(breakpoint_id)
+                        line = int(line)
 
                         # We must restore new lines and tabs as done in
                         # AbstractDebugTarget.breakpointAdded
                         condition = condition.replace("@_@NEW_LINE_CHAR@_@", '\n').\
                             replace("@_@TAB_CHAR@_@", '\t').strip()
 
-                        func_name = func_name[8:]
+                        expression = expression.replace("@_@NEW_LINE_CHAR@_@", '\n').\
+                            replace("@_@TAB_CHAR@_@", '\t').strip()
                     else:
-                        func_name = 'None'  # Match anything if not specified.
+                        #Note: this else should be removed after PyCharm migrates to setting
+                        #breakpoints by id (and ideally also provides func_name).
+                        type, file, line, condition, expression = text.split('\t', 4)
+                        # If we don't have an id given for each breakpoint, consider
+                        # the id to be the line.
+                        breakpoint_id = line = int(line)
+                        if condition.startswith('**FUNC**'):
+                            func_name, condition = condition.split('\t', 1)
 
+                            # We must restore new lines and tabs as done in
+                            # AbstractDebugTarget.breakpointAdded
+                            condition = condition.replace("@_@NEW_LINE_CHAR@_@", '\n').\
+                                replace("@_@TAB_CHAR@_@", '\t').strip()
+
+                            func_name = func_name[8:]
+                        else:
+                            func_name = 'None'  # Match anything if not specified.
+
+                    if not IS_PY3K:  # In Python 3, the frame object will have unicode for the file, whereas on python 2 it has a byte-array encoded with the filesystem encoding.
+                        file = file.encode(file_system_encoding)
 
                     file = NormFileToServer(file)
 
@@ -712,7 +804,6 @@
                             ' to file that does not exist: %s (will have no effect)\n' % (file,))
                         sys.stderr.flush()
 
-                    line = int(line)
 
                     if len(condition) <= 0 or condition is None or condition == "None":
                         condition = None
@@ -721,57 +812,68 @@
                         expression = None
 
                     if type == 'python-line':
-                        breakpoint = LineBreakpoint(type, True, condition, func_name, expression)
-                        breakpoint.add(self.breakpoints, file, line, func_name)
+                        breakpoint = LineBreakpoint(line, condition, func_name, expression)
+                        breakpoints = self.breakpoints
+                        file_to_id_to_breakpoint = self.file_to_id_to_line_breakpoint
                     elif type == 'django-line':
-                        breakpoint = DjangoLineBreakpoint(type, file, line, True, condition, func_name, expression)
-                        breakpoint.add(self.django_breakpoints, file, line, func_name)
+                        breakpoint = DjangoLineBreakpoint(file, line, condition, func_name, expression)
+                        breakpoints = self.django_breakpoints
+                        file_to_id_to_breakpoint = self.file_to_id_to_django_breakpoint
                     else:
                         raise NameError(type)
 
+                    if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0:
+                        pydev_log.debug('Added breakpoint:%s - line:%s - func_name:%s\n' % (file, line, func_name.encode('utf-8')))
+                        sys.stderr.flush()
+
+                    if DictContains(file_to_id_to_breakpoint, file):
+                        id_to_pybreakpoint = file_to_id_to_breakpoint[file]
+                    else:
+                        id_to_pybreakpoint = file_to_id_to_breakpoint[file] = {}
+
+                    id_to_pybreakpoint[breakpoint_id] = breakpoint
+                    self.consolidate_breakpoints(file, id_to_pybreakpoint, breakpoints)
+
                     self.setTracingForUntracedContexts()
 
                 elif cmd_id == CMD_REMOVE_BREAK:
                     #command to remove some breakpoint
-                    #text is file\tline. Remove from breakpoints dictionary
-                    type, file, line = text.split('\t', 2)
+                    #text is type\file\tid. Remove from breakpoints dictionary
+                    breakpoint_type, file, breakpoint_id = text.split('\t', 2)
+
+                    if not IS_PY3K:  # In Python 3, the frame object will have unicode for the file, whereas on python 2 it has a byte-array encoded with the filesystem encoding.
+                        file = file.encode(file_system_encoding)
+
                     file = NormFileToServer(file)
+
                     try:
-                        line = int(line)
+                        breakpoint_id = int(breakpoint_id)
                     except ValueError:
-                        pass
+                        pydev_log.error('Error removing breakpoint. Expected breakpoint_id to be an int. Found: %s' % (breakpoint_id,))
 
                     else:
-                        found = False
+                        if breakpoint_type == 'python-line':
+                            breakpoints = self.breakpoints
+                            file_to_id_to_breakpoint = self.file_to_id_to_line_breakpoint
+                        elif breakpoint_type == 'django-line':
+                            breakpoints = self.django_breakpoints
+                            file_to_id_to_breakpoint = self.file_to_id_to_django_breakpoint
+                        else:
+                            raise NameError(breakpoint_type)
+
                         try:
-                            if type == 'django-line':
-                                del self.django_breakpoints[file][line]
-                            elif type == 'python-line':
-                                del self.breakpoints[file][line] #remove the breakpoint in that line
-                            else:
-                                try:
-                                    del self.django_breakpoints[file][line]
-                                    found = True
-                                except:
-                                    pass
-                                try:
-                                    del self.breakpoints[file][line] #remove the breakpoint in that line
-                                    found = True
-                                except:
-                                    pass
-
+                            id_to_pybreakpoint = file_to_id_to_breakpoint[file]
                             if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0:
-                                sys.stderr.write('Removed breakpoint:%s - %s\n' % (file, line))
-                                sys.stderr.flush()
+                                existing = id_to_pybreakpoint[breakpoint_id]
+                                sys.stderr.write('Removed breakpoint:%s - line:%s - func_name:%s (id: %s)\n' % (
+                                    file, existing.line, existing.func_name.encode('utf-8'), breakpoint_id))
+
+                            del id_to_pybreakpoint[breakpoint_id]
+                            self.consolidate_breakpoints(file, id_to_pybreakpoint, breakpoints)
                         except KeyError:
-                            found = False
+                            pydev_log.error("Error removing breakpoint: Breakpoint id not found: %s id: %s. Available ids: %s\n" % (
+                                file, breakpoint_id, DictKeys(id_to_pybreakpoint)))
 
-                        if not found:
-                            #ok, it's not there...
-                            if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0:
-                                #Sometimes, when adding a breakpoint, it adds a remove command before (don't really know why)
-                                sys.stderr.write("breakpoint not found: %s - %s\n" % (file, line))
-                                sys.stderr.flush()
 
                 elif cmd_id == CMD_EVALUATE_EXPRESSION or cmd_id == CMD_EXEC_EXPRESSION:
                     #command to evaluate the given expression
@@ -790,29 +892,118 @@
                     int_cmd = InternalConsoleExec(seq, thread_id, frame_id, expression)
                     self.postInternalCommand(int_cmd, thread_id)
 
+                elif cmd_id == CMD_SET_PY_EXCEPTION:
+                    # Command which receives set of exceptions on which user wants to break the debugger
+                    # text is: break_on_uncaught;break_on_caught;TypeError;ImportError;zipimport.ZipImportError;
+                    # This API is optional and works 'in bulk' -- it's possible
+                    # to get finer-grained control with CMD_ADD_EXCEPTION_BREAK/CMD_REMOVE_EXCEPTION_BREAK
+                    # which allows setting caught/uncaught per exception.
+                    #
+                    splitted = text.split(';')
+                    self.break_on_uncaught_exceptions = {}
+                    self.break_on_caught_exceptions = {}
+                    added = []
+                    if len(splitted) >= 4:
+                        if splitted[0] == 'true':
+                            break_on_uncaught = True
+                        else:
+                            break_on_uncaught = False
+
+                        if splitted[1] == 'true':
+                            break_on_caught = True
+                        else:
+                            break_on_caught = False
+
+                        if splitted[2] == 'true':
+                            self.break_on_exceptions_thrown_in_same_context = True
+                        else:
+                            self.break_on_exceptions_thrown_in_same_context = False
+
+                        if splitted[3] == 'true':
+                            self.ignore_exceptions_thrown_in_lines_with_ignore_exception = True
+                        else:
+                            self.ignore_exceptions_thrown_in_lines_with_ignore_exception = False
+
+                        for exception_type in splitted[4:]:
+                            exception_type = exception_type.strip()
+                            if not exception_type:
+                                continue
+
+                            exception_breakpoint = self.add_break_on_exception(
+                                exception_type,
+                                notify_always=break_on_caught,
+                                notify_on_terminate=break_on_uncaught,
+                                notify_on_first_raise_only=False,
+                            )
+                            added.append(exception_breakpoint)
+
+                        self.update_after_exceptions_added(added)
+
+                    else:
+                        sys.stderr.write("Error when setting exception list. Received: %s\n" % (text,))
+
+                elif cmd_id == CMD_GET_FILE_CONTENTS:
+
+                    if not IS_PY3K:  # In Python 3, the frame object will have unicode for the file, whereas on python 2 it has a byte-array encoded with the filesystem encoding.
+                        text = text.encode(file_system_encoding)
+
+                    if os.path.exists(text):
+                        f = open(text, 'r')
+                        try:
+                            source = f.read()
+                        finally:
+                            f.close()
+                        cmd = self.cmdFactory.makeGetFileContents(seq, source)
+
+                elif cmd_id == CMD_SET_PROPERTY_TRACE:
+                    # Command which receives whether to trace property getter/setter/deleter
+                    # text is feature_state(true/false);disable_getter/disable_setter/disable_deleter
+                    if text != "":
+                        splitted = text.split(';')
+                        if len(splitted) >= 3:
+                            if self.disable_property_trace is False and splitted[0] == 'true':
+                                # Replacing property by custom property only when the debugger starts
+                                pydevd_traceproperty.replace_builtin_property()
+                                self.disable_property_trace = True
+                            # Enable/Disable tracing of the property getter
+                            if splitted[1] == 'true':
+                                self.disable_property_getter_trace = True
+                            else:
+                                self.disable_property_getter_trace = False
+                            # Enable/Disable tracing of the property setter
+                            if splitted[2] == 'true':
+                                self.disable_property_setter_trace = True
+                            else:
+                                self.disable_property_setter_trace = False
+                            # Enable/Disable tracing of the property deleter
+                            if splitted[3] == 'true':
+                                self.disable_property_deleter_trace = True
+                            else:
+                                self.disable_property_deleter_trace = False
+                    else:
+                        # User hasn't configured any settings for property tracing
+                        pass
+
                 elif cmd_id == CMD_ADD_EXCEPTION_BREAK:
                     exception, notify_always, notify_on_terminate = text.split('\t', 2)
-
-                    eb = ExceptionBreakpoint(exception, notify_always, notify_on_terminate)
-
-                    self.exception_set[exception] = eb
-
-                    if eb.notify_on_terminate:
-                        update_exception_hook(self)
-                    if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0:
-                        pydev_log.error("Exceptions to hook on terminate: %s\n" % (self.exception_set,))
-
-                    if eb.notify_always:
-                        self.always_exception_set.add(exception)
-                        if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0:
-                            pydev_log.error("Exceptions to hook always: %s\n" % (self.always_exception_set,))
-                        self.setTracingForUntracedContexts()
+                    exception_breakpoint = self.add_break_on_exception(
+                        exception,
+                        notify_always=int(notify_always) > 0,
+                        notify_on_terminate = int(notify_on_terminate) == 1,
+                        notify_on_first_raise_only=int(notify_always) == 2
+                    )
+                    self.update_after_exceptions_added([exception_breakpoint])
 
                 elif cmd_id == CMD_REMOVE_EXCEPTION_BREAK:
                     exception = text
                     try:
-                        del self.exception_set[exception]
-                        self.always_exception_set.remove(exception)
+                        cp = self.break_on_uncaught_exceptions.copy()
+                        DictPop(cp, exception, None)
+                        self.break_on_uncaught_exceptions = cp
+
+                        cp = self.break_on_caught_exceptions.copy()
+                        DictPop(cp, exception, None)
+                        self.break_on_caught_exceptions = cp
                     except:
                         pydev_log.debug("Error while removing exception %s"%sys.exc_info()[0]);
                     update_exception_hook(self)
@@ -840,6 +1031,77 @@
                     except :
                         pass
 
+                elif cmd_id == CMD_EVALUATE_CONSOLE_EXPRESSION:
+                    # Command which takes care for the debug console communication
+                    if text != "":
+                        thread_id, frame_id, console_command = text.split('\t', 2)
+                        console_command, line = console_command.split('\t')
+                        if console_command == 'EVALUATE':
+                            int_cmd = InternalEvaluateConsoleExpression(seq, thread_id, frame_id, line)
+                        elif console_command == 'GET_COMPLETIONS':
+                            int_cmd = InternalConsoleGetCompletions(seq, thread_id, frame_id, line)
+                        self.postInternalCommand(int_cmd, thread_id)
+
+                elif cmd_id == CMD_RUN_CUSTOM_OPERATION:
+                    # Command which runs a custom operation
+                    if text != "":
+                        try:
+                            location, custom = text.split('||', 1)
+                        except:
+                            sys.stderr.write('Custom operation now needs a || separator. Found: %s\n' % (text,))
+                            raise
+
+                        thread_id, frame_id, scopeattrs = location.split('\t', 2)
+
+                        if scopeattrs.find('\t') != -1:  # there are attributes beyond scope
+                            scope, attrs = scopeattrs.split('\t', 1)
+                        else:
+                            scope, attrs = (scopeattrs, None)
+
+                        # : style: EXECFILE or EXEC
+                        # : encoded_code_or_file: file to execute or code
+                        # : fname: name of function to be executed in the resulting namespace
+                        style, encoded_code_or_file, fnname = custom.split('\t', 3)
+                        int_cmd = InternalRunCustomOperation(seq, thread_id, frame_id, scope, attrs,
+                                                             style, encoded_code_or_file, fnname)
+                        self.postInternalCommand(int_cmd, thread_id)
+
+                elif cmd_id == CMD_IGNORE_THROWN_EXCEPTION_AT:
+                    if text:
+                        replace = 'REPLACE:'  # Not all 3.x versions support u'REPLACE:', so, doing workaround.
+                        if not IS_PY3K:
+                            replace = unicode(replace)
+
+                        if text.startswith(replace):
+                            text = text[8:]
+                            self.filename_to_lines_where_exceptions_are_ignored.clear()
+
+                        if text:
+                            for line in text.split('||'):  # Can be bulk-created (one in each line)
+                                filename, line_number = line.split('|')
+                                if not IS_PY3K:
+                                    filename = filename.encode(file_system_encoding)
+
+                                filename = NormFileToServer(filename)
+
+                                if os.path.exists(filename):
+                                    lines_ignored = self.filename_to_lines_where_exceptions_are_ignored.get(filename)
+                                    if lines_ignored is None:
+                                        lines_ignored = self.filename_to_lines_where_exceptions_are_ignored[filename] = {}
+                                    lines_ignored[int(line_number)] = 1
+                                else:
+                                    sys.stderr.write('pydev debugger: warning: trying to ignore exception thrown'\
+                                        ' on file that does not exist: %s (will have no effect)\n' % (filename,))
+
+                elif cmd_id == CMD_ENABLE_DONT_TRACE:
+                    if text:
+                        true_str = 'true'  # Not all 3.x versions support u'str', so, doing workaround.
+                        if not IS_PY3K:
+                            true_str = unicode(true_str)
+
+                        mode = text.strip() == true_str
+                        pydevd_dont_trace.trace_filter(mode)
+
                 else:
                     #I have no idea what this is all about
                     cmd = self.cmdFactory.makeErrorMessage(seq, "unexpected command " + str(cmd_id))
@@ -881,6 +1143,44 @@
         thread.additionalInfo.pydev_state = STATE_SUSPEND
         thread.stop_reason = stop_reason
 
+        # If conditional breakpoint raises any exception during evaluation send details to Java
+        if stop_reason == CMD_SET_BREAK and self.suspend_on_breakpoint_exception:
+            self.sendBreakpointConditionException(thread)
+
+
+    def sendBreakpointConditionException(self, thread):
+        """If conditional breakpoint raises an exception during evaluation
+        send exception details to java
+        """
+        thread_id = GetThreadId(thread)
+        conditional_breakpoint_exception_tuple = thread.additionalInfo.conditional_breakpoint_exception
+        # conditional_breakpoint_exception_tuple - should contain 2 values (exception_type, stacktrace)
+        if conditional_breakpoint_exception_tuple and len(conditional_breakpoint_exception_tuple) == 2:
+            exc_type, stacktrace = conditional_breakpoint_exception_tuple
+            int_cmd = InternalGetBreakpointException(thread_id, exc_type, stacktrace)
+            # Reset the conditional_breakpoint_exception details to None
+            thread.additionalInfo.conditional_breakpoint_exception = None
+            self.postInternalCommand(int_cmd, thread_id)
+
+
+    def sendCaughtExceptionStack(self, thread, arg, curr_frame_id):
+        """Sends details on the exception which was caught (and where we stopped) to the java side.
+
+        arg is: exception type, description, traceback object
+        """
+        thread_id = GetThreadId(thread)
+        int_cmd = InternalSendCurrExceptionTrace(thread_id, arg, curr_frame_id)
+        self.postInternalCommand(int_cmd, thread_id)
+
+
+    def sendCaughtExceptionStackProceeded(self, thread):
+        """Sends that some thread was resumed and is no longer showing an exception trace.
+        """
+        thread_id = GetThreadId(thread)
+        int_cmd = InternalSendCurrExceptionTraceProceeded(thread_id)
+        self.postInternalCommand(int_cmd, thread_id)
+        self.processInternalCommands()
+
 
     def doWaitSuspend(self, thread, frame, event, arg): #@UnusedVariable
         """ busy waits until the thread state changes to RUN
@@ -898,7 +1198,7 @@
         try:
             from_this_thread = []
 
-            for frame_id, custom_frame in CustomFramesContainer.custom_frames.items():
+            for frame_id, custom_frame in DictIterItems(CustomFramesContainer.custom_frames):
                 if custom_frame.thread_id == thread.ident:
                     # print >> sys.stderr, 'Frame created: ', frame_id
                     self.writer.addCommand(self.cmdFactory.makeCustomFrameCreatedMessage(frame_id, custom_frame.name))
@@ -991,7 +1291,6 @@
 
     def handle_post_mortem_stop(self, additionalInfo, t):
         pydev_log.debug("We are stopping in post-mortem\n")
-        self.force_post_mortem_stop -= 1
         frame, frames_byid = additionalInfo.pydev_force_stop_at_exception
         thread_id = GetThreadId(t)
         pydevd_vars.addAdditionalFrameById(thread_id, frames_byid)
@@ -1060,9 +1359,9 @@
             if additionalInfo.is_tracing:
                 f = frame
                 while f is not None:
-                    fname, bs = GetFilenameAndBase(f)
-                    if bs == 'pydevd_frame.py':
-                        if 'trace_dispatch' == f.f_code.co_name:
+                    if 'trace_dispatch' == f.f_code.co_name:
+                        _fname, bs = GetFilenameAndBase(f)
+                        if bs == 'pydevd_frame.py':
                             return None  #we don't wan't to trace code invoked from pydevd_frame.trace_dispatch
                     f = f.f_back
 
@@ -1071,9 +1370,6 @@
                 self.processThreadNotAlive(GetThreadId(t))
                 return None  # suspend tracing
 
-            if is_file_to_ignore:
-                return None
-
             # each new frame...
             return additionalInfo.CreateDbFrame((self, filename, additionalInfo, t, frame)).trace_dispatch(frame, event, arg)
 
@@ -1120,18 +1416,18 @@
 
     def update_trace(self, frame, dispatch_func, overwrite_prev):
         if frame.f_trace is None:
-          frame.f_trace = dispatch_func
+            frame.f_trace = dispatch_func
         else:
-          if overwrite_prev:
-              frame.f_trace = dispatch_func
-          else:
-              try:
-                  #If it's the trace_exception, go back to the frame trace dispatch!
-                  if frame.f_trace.im_func.__name__ == 'trace_exception':
-                      frame.f_trace = frame.f_trace.im_self.trace_dispatch
-              except AttributeError:
-                  pass
-              frame = frame.f_back
+            if overwrite_prev:
+                frame.f_trace = dispatch_func
+            else:
+                try:
+                    #If it's the trace_exception, go back to the frame trace dispatch!
+                    if frame.f_trace.im_func.__name__ == 'trace_exception':
+                        frame.f_trace = frame.f_trace.im_self.trace_dispatch
+                except AttributeError:
+                    pass
+                frame = frame.f_back
         del frame
 
     def prepareToRun(self):
@@ -1150,6 +1446,7 @@
         PyDBCommandThread(self).start()
         PyDBCheckAliveThread(self).start()
 
+
     def patch_threads(self):
         try:
             # not available in jython!
@@ -1157,11 +1454,8 @@
         except:
             pass
 
-        try:
-            thread.start_new_thread = pydev_start_new_thread
-            thread.start_new = pydev_start_new_thread
-        except:
-            pass
+        from pydev_monkey import patch_thread_modules
+        patch_thread_modules()
 
 
     def run(self, file, globals=None, locals=None, set_trace=True):
@@ -1185,7 +1479,7 @@
             sys.modules['__main__'] = m
             if hasattr(sys.modules['pydevd'], '__loader__'):
                 setattr(m, '__loader__', getattr(sys.modules['pydevd'], '__loader__'))
-                
+
             m.__file__ = file
             globals = m.__dict__
             try:
@@ -1246,7 +1540,8 @@
     setup['server'] = False
     setup['port'] = 0
     setup['file'] = ''
-    setup['multiproc'] = False
+    setup['multiproc'] = False #Used by PyCharm (reuses connection: ssh tunneling)
+    setup['multiprocess'] = False # Used by PyDev (creates new connection to ide)
     setup['save-signatures'] = False
     i = 0
     del argv[0]
@@ -1279,6 +1574,9 @@
         elif (argv[i] == '--multiproc'):
             del argv[i]
             setup['multiproc'] = True
+        elif (argv[i] == '--multiprocess'):
+            del argv[i]
+            setup['multiprocess'] = True
         elif (argv[i] == '--save-signatures'):
             del argv[i]
             setup['save-signatures'] = True
@@ -1423,7 +1721,7 @@
 
         CustomFramesContainer.custom_frames_lock.acquire()
         try:
-            for _frameId, custom_frame in CustomFramesContainer.custom_frames.items():
+            for _frameId, custom_frame in DictIterItems(CustomFramesContainer.custom_frames):
                 debugger.SetTraceForFrameAndParents(custom_frame.frame, False)
         finally:
             CustomFramesContainer.custom_frames_lock.release()
@@ -1492,24 +1790,21 @@
             threading.settrace(None) # for all future threads
         except:
             pass
-        
-        try:
-            thread.start_new_thread = _original_start_new_thread
-            thread.start_new = _original_start_new_thread
-        except:
-            pass
-    
+
+        from pydev_monkey import undo_patch_thread_modules
+        undo_patch_thread_modules()
+
         debugger = GetGlobalDebugger()
-        
+
         if debugger:
             debugger.trace_dispatch = None
-    
+
             debugger.SetTraceForFrameAndParents(GetFrame(), False)
-        
+
             debugger.exiting()
-        
-            killAllPydevThreads()  
-        
+
+            killAllPydevThreads()
+
         connected = False
 
 class Dispatcher(object):
@@ -1544,21 +1839,28 @@
             self.killReceived = True
 
 
+DISPATCH_APPROACH_NEW_CONNECTION = 1 # Used by PyDev
+DISPATCH_APPROACH_EXISTING_CONNECTION = 2 # Used by PyCharm
+DISPATCH_APPROACH = DISPATCH_APPROACH_NEW_CONNECTION
+
 def dispatch():
-    argv = sys.original_argv[:]
-    setup = processCommandLine(argv)
+    setup = SetupHolder.setup
     host = setup['client']
     port = setup['port']
-    dispatcher = Dispatcher()
-    try:
-        dispatcher.connect(host, port)
-        port = dispatcher.port
-    finally:
-        dispatcher.close()
+    if DISPATCH_APPROACH == DISPATCH_APPROACH_EXISTING_CONNECTION:
+        dispatcher = Dispatcher()
+        try:
+            dispatcher.connect(host, port)
+            port = dispatcher.port
+        finally:
+            dispatcher.close()
     return host, port
 
 
 def settrace_forked():
+    '''
+    When creating a fork from a process in the debugger, we need to reset the whole debugger environment!
+    '''
     host, port = dispatch()
 
     import pydevd_tracing
@@ -1578,6 +1880,15 @@
             overwrite_prev_trace=True,
             patch_multiprocessing=True,
             )
+
+#=======================================================================================================================
+# SetupHolder
+#=======================================================================================================================
+class SetupHolder:
+
+    setup = None
+
+
 #=======================================================================================================================
 # main
 #=======================================================================================================================
@@ -1586,6 +1897,7 @@
     try:
         sys.original_argv = sys.argv[:]
         setup = processCommandLine(sys.argv)
+        SetupHolder.setup = setup
     except ValueError:
         traceback.print_exc()
         usage(1)
@@ -1611,62 +1923,73 @@
     f = setup['file']
     fix_app_engine_debug = False
 
-    if setup['multiproc']:
-        pydev_log.debug("Started in multiproc mode\n")
 
-        dispatcher = Dispatcher()
-        try:
-            dispatcher.connect(host, port)
-            if dispatcher.port is not None:
-                port = dispatcher.port
-                pydev_log.debug("Received port %d\n" %port)
-                pydev_log.info("pydev debugger: process %d is connecting\n"% os.getpid())
-
-                try:
-                    pydev_monkey.patch_new_process_functions()
-                except:
-                    pydev_log.error("Error patching process functions\n")
-                    traceback.print_exc()
-            else:
-                pydev_log.error("pydev debugger: couldn't get port for new debug process\n")
-        finally:
-            dispatcher.close()
+    try:
+        import pydev_monkey
+    except:
+        pass #Not usable on jython 2.1
     else:
-        pydev_log.info("pydev debugger: starting\n")
+        if setup['multiprocess']: # PyDev
+            pydev_monkey.patch_new_process_functions()
 
-        try:
-            pydev_monkey.patch_new_process_functions_with_warning()
-        except:
-            pydev_log.error("Error patching process functions\n")
-            traceback.print_exc()
+        elif setup['multiproc']: # PyCharm
+            pydev_log.debug("Started in multiproc mode\n")
+            # Note: we're not inside method, so, no need for 'global'
+            DISPATCH_APPROACH = DISPATCH_APPROACH_EXISTING_CONNECTION
 
-        # Only do this patching if we're not running with multiprocess turned on.
-        if f.find('dev_appserver.py') != -1:
-            if os.path.basename(f).startswith('dev_appserver.py'):
-                appserver_dir = os.path.dirname(f)
-                version_file = os.path.join(appserver_dir, 'VERSION')
-                if os.path.exists(version_file):
+            dispatcher = Dispatcher()
+            try:
+                dispatcher.connect(host, port)
+                if dispatcher.port is not None:
+                    port = dispatcher.port
+                    pydev_log.debug("Received port %d\n" %port)
+                    pydev_log.info("pydev debugger: process %d is connecting\n"% os.getpid())
+
                     try:
-                        stream = open(version_file, 'r')
-                        try:
-                            for line in stream.read().splitlines():
-                                line = line.strip()
-                                if line.startswith('release:'):
-                                    line = line[8:].strip()
-                                    version = line.replace('"', '')
-                                    version = version.split('.')
-                                    if int(version[0]) > 1:
-                                        fix_app_engine_debug = True
-
-                                    elif int(version[0]) == 1:
-                                        if int(version[1]) >= 7:
-                                            # Only fix from 1.7 onwards
-                                            fix_app_engine_debug = True
-                                    break
-                        finally:
-                            stream.close()
+                        pydev_monkey.patch_new_process_functions()
                     except:
+                        pydev_log.error("Error patching process functions\n")
                         traceback.print_exc()
+                else:
+                    pydev_log.error("pydev debugger: couldn't get port for new debug process\n")
+            finally:
+                dispatcher.close()
+        else:
+            pydev_log.info("pydev debugger: starting\n")
+
+            try:
+                pydev_monkey.patch_new_process_functions_with_warning()
+            except:
+                pydev_log.error("Error patching process functions\n")
+                traceback.print_exc()
+
+            # Only do this patching if we're not running with multiprocess turned on.
+            if f.find('dev_appserver.py') != -1:
+                if os.path.basename(f).startswith('dev_appserver.py'):
+                    appserver_dir = os.path.dirname(f)
+                    version_file = os.path.join(appserver_dir, 'VERSION')
+                    if os.path.exists(version_file):
+                        try:
+                            stream = open(version_file, 'r')
+                            try:
+                                for line in stream.read().splitlines():
+                                    line = line.strip()
+                                    if line.startswith('release:'):
+                                        line = line[8:].strip()
+                                        version = line.replace('"', '')
+                                        version = version.split('.')
+                                        if int(version[0]) > 1:
+                                            fix_app_engine_debug = True
+
+                                        elif int(version[0]) == 1:
+                                            if int(version[1]) >= 7:
+                                                # Only fix from 1.7 onwards
+                                                fix_app_engine_debug = True
+                                        break
+                            finally:
+                                stream.close()
+                        except:
+                            traceback.print_exc()
 
     try:
         # In the default run (i.e.: run directly on debug mode), we try to patch stackless as soon as possible
@@ -1718,16 +2041,21 @@
             import pydevd_psyco_stub
             sys.modules['psyco'] = pydevd_psyco_stub
 
-    debugger = PyDB()
+        debugger = PyDB()
 
-    if setup['save-signatures']:
-        if pydevd_vm_type.GetVmType() == pydevd_vm_type.PydevdVmType.JYTHON:
-            sys.stderr.write("Collecting run-time type information is not supported for Jython\n")
-        else:
-            debugger.signature_factory = SignatureFactory()
+        if setup['save-signatures']:
+            if pydevd_vm_type.GetVmType() == pydevd_vm_type.PydevdVmType.JYTHON:
+                sys.stderr.write("Collecting run-time type information is not supported for Jython\n")
+            else:
+                debugger.signature_factory = SignatureFactory()
 
-    debugger.connect(host, port)
+        try:
+            debugger.connect(host, port)
+        except:
+            sys.stderr.write("Could not connect to %s: %s\n" % (host, port))
+            traceback.print_exc()
+            sys.exit(1)
 
-    connected = True #Mark that we're connected when started from inside ide.
+        connected = True  # Mark that we're connected when started from inside ide.
 
-    debugger.run(setup['file'], None, None)
+        debugger.run(setup['file'], None, None)
diff --git a/python/helpers/pydev/pydevd_additional_thread_info.py b/python/helpers/pydev/pydevd_additional_thread_info.py
index 1b0fc2c..fa906ad 100644
--- a/python/helpers/pydev/pydevd_additional_thread_info.py
+++ b/python/helpers/pydev/pydevd_additional_thread_info.py
@@ -12,7 +12,7 @@
 #=======================================================================================================================
 class AbstractPyDBAdditionalThreadInfo:
     def __init__(self):
-        self.pydev_state = STATE_RUN 
+        self.pydev_state = STATE_RUN
         self.pydev_step_stop = None
         self.pydev_step_cmd = None
         self.pydev_notify_kill = False
@@ -20,51 +20,52 @@
         self.pydev_smart_step_stop = None
         self.pydev_django_resolve_frame = None
         self.is_tracing = False
+        self.conditional_breakpoint_exception = None
 
-        
+
     def IterFrames(self):
         raise NotImplementedError()
-    
+
     def CreateDbFrame(self, args):
         #args = mainDebugger, filename, base, additionalInfo, t, frame
         raise NotImplementedError()
-    
+
     def __str__(self):
         return 'State:%s Stop:%s Cmd: %s Kill:%s' % (self.pydev_state, self.pydev_step_stop, self.pydev_step_cmd, self.pydev_notify_kill)
 
-    
+
 #=======================================================================================================================
 # PyDBAdditionalThreadInfoWithCurrentFramesSupport
 #=======================================================================================================================
 class PyDBAdditionalThreadInfoWithCurrentFramesSupport(AbstractPyDBAdditionalThreadInfo):
-    
+
     def IterFrames(self):
         #sys._current_frames(): dictionary with thread id -> topmost frame
         return sys._current_frames().values() #return a copy... don't know if it's changed if we did get an iterator
 
     #just create the db frame directly
     CreateDbFrame = PyDBFrame
-    
+
 #=======================================================================================================================
 # PyDBAdditionalThreadInfoWithoutCurrentFramesSupport
 #=======================================================================================================================
 class PyDBAdditionalThreadInfoWithoutCurrentFramesSupport(AbstractPyDBAdditionalThreadInfo):
-    
+
     def __init__(self):
         AbstractPyDBAdditionalThreadInfo.__init__(self)
-        #That's where the last frame entered is kept. That's needed so that we're able to 
+        #That's where the last frame entered is kept. That's needed so that we're able to
         #trace contexts that were previously untraced and are currently active. So, the bad thing
         #is that the frame may be kept alive longer than it would if we go up on the frame stack,
         #and is only disposed when some other frame is removed.
-        #A better way would be if we could get the topmost frame for each thread, but that's 
+        #A better way would be if we could get the topmost frame for each thread, but that's
         #not possible (until python 2.5 -- which is the PyDBAdditionalThreadInfoWithCurrentFramesSupport version)
         #Or if the user compiled threadframe (from http://www.majid.info/mylos/stories/2004/06/10/threadframe.html)
-        
+
         #NOT RLock!! (could deadlock if it was)
         self.lock = threading.Lock()
         self._acquire_lock = self.lock.acquire
         self._release_lock = self.lock.release
-        
+
         #collection with the refs
         d = {}
         self.pydev_existing_frames = d
@@ -72,8 +73,8 @@
             self._iter_frames = d.iterkeys
         except AttributeError:
             self._iter_frames = d.keys
-            
-        
+
+
     def _OnDbFrameCollected(self, ref):
         '''
             Callback to be called when a given reference is garbage-collected.
@@ -83,8 +84,8 @@
             del self.pydev_existing_frames[ref]
         finally:
             self._release_lock()
-        
-    
+
+
     def _AddDbFrame(self, db_frame):
         self._acquire_lock()
         try:
@@ -94,8 +95,8 @@
             self.pydev_existing_frames[r] = r
         finally:
             self._release_lock()
-    
-        
+
+
     def CreateDbFrame(self, args):
         #the frame must be cached as a weak-ref (we return the actual db frame -- which will be kept
         #alive until its trace_dispatch method is not referenced anymore).
@@ -106,14 +107,14 @@
         db_frame.frame = args[-1]
         self._AddDbFrame(db_frame)
         return db_frame
-    
-    
+
+
     def IterFrames(self):
         #We cannot use yield (because of the lock)
         self._acquire_lock()
         try:
             ret = []
-            
+
             for weak_db_frame in self._iter_frames():
                 try:
                     ret.append(weak_db_frame().frame)
diff --git a/python/helpers/pydev/pydevd_breakpoints.py b/python/helpers/pydev/pydevd_breakpoints.py
index beebebf..82a230d 100644
--- a/python/helpers/pydev/pydevd_breakpoints.py
+++ b/python/helpers/pydev/pydevd_breakpoints.py
@@ -2,14 +2,12 @@
 import pydevd_tracing
 import sys
 import pydev_log
+import pydevd_import_class
 
 _original_excepthook = None
 _handle_exceptions = None
 
 
-NOTIFY_ALWAYS="NOTIFY_ALWAYS"
-NOTIFY_ON_TERMINATE="NOTIFY_ON_TERMINATE"
-
 if USE_LIB_COPY:
     import _pydev_threading as threading
 else:
@@ -20,52 +18,39 @@
 from pydevd_comm import GetGlobalDebugger
 
 class ExceptionBreakpoint:
-    def __init__(self, qname, notify_always, notify_on_terminate):
-        exctype = get_class(qname)
+
+    def __init__(
+        self,
+        qname,
+        notify_always,
+        notify_on_terminate,
+        notify_on_first_raise_only,
+        ):
+        exctype = _get_class(qname)
         self.qname = qname
         if exctype is not None:
             self.name = exctype.__name__
         else:
             self.name = None
 
-        self.notify_on_terminate = int(notify_on_terminate) == 1
-        self.notify_always = int(notify_always) > 0
-        self.notify_on_first_raise_only = int(notify_always) == 2
+        self.notify_on_terminate = notify_on_terminate
+        self.notify_always = notify_always
+        self.notify_on_first_raise_only = notify_on_first_raise_only
 
         self.type = exctype
-        self.notify = {NOTIFY_ALWAYS: self.notify_always, NOTIFY_ON_TERMINATE: self.notify_on_terminate}
 
 
     def __str__(self):
         return self.qname
 
 class LineBreakpoint:
-    def __init__(self, type, flag, condition, func_name, expression):
-        self.type = type
+
+    def __init__(self, line, condition, func_name, expression):
+        self.line = line
         self.condition = condition
         self.func_name = func_name
         self.expression = expression
 
-    def get_break_dict(self, breakpoints, file):
-        if DictContains(breakpoints, file):
-            breakDict = breakpoints[file]
-        else:
-            breakDict = {}
-        breakpoints[file] = breakDict
-        return breakDict
-
-    def trace(self, file, line, func_name):
-        if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0:
-            pydev_log.debug('Added breakpoint:%s - line:%s - func_name:%s\n' % (file, line, func_name))
-            sys.stderr.flush()
-
-    def add(self, breakpoints, file, line, func_name):
-      self.trace(file, line, func_name)
-
-      breakDict = self.get_break_dict(breakpoints, file)
-
-      breakDict[line] = self
-
 def get_exception_full_qname(exctype):
     if not exctype:
         return None
@@ -77,41 +62,41 @@
     return exctype.__name__
 
 
-def get_exception_breakpoint(exctype, exceptions, notify_class):
-    name = get_exception_full_qname(exctype)
+def get_exception_breakpoint(exctype, exceptions):
+    exception_full_qname = get_exception_full_qname(exctype)
+
     exc = None
     if exceptions is not None:
-        for k, e in exceptions.items():
-          if e.notify[notify_class]:
-            if name == k:
-                return e
-            if (e.type is not None and issubclass(exctype, e.type)):
-                if exc is None or issubclass(e.type, exc.type):
-                    exc = e
+        try:
+            return exceptions[exception_full_qname]
+        except KeyError:
+            for exception_breakpoint in DictIterValues(exceptions):
+                if exception_breakpoint.type is not None and issubclass(exctype, exception_breakpoint.type):
+                    if exc is None or issubclass(exception_breakpoint.type, exc.type):
+                        exc = exception_breakpoint
     return exc
 
 #=======================================================================================================================
-# excepthook
+# _excepthook
 #=======================================================================================================================
-def excepthook(exctype, value, tb):
+def _excepthook(exctype, value, tb):
     global _handle_exceptions
-    if _handle_exceptions is not None:
-        exception_breakpoint = get_exception_breakpoint(exctype, _handle_exceptions, NOTIFY_ON_TERMINATE)
+    if _handle_exceptions:
+        exception_breakpoint = get_exception_breakpoint(exctype, _handle_exceptions)
     else:
         exception_breakpoint = None
 
-    if exception_breakpoint is None:
-        return _original_excepthook(exctype, value, tb)
-
     #Always call the original excepthook before going on to call the debugger post mortem to show it.
     _original_excepthook(exctype, value, tb)
 
+    if not exception_breakpoint:
+        return
+
     if tb is None:  #sometimes it can be None, e.g. with GTK
-      return
+        return
 
     frames = []
 
-    traceback = tb
     while tb:
         frames.append(tb.tb_frame)
         tb = tb.tb_next
@@ -122,9 +107,7 @@
     thread.additionalInfo.exception = (exctype, value, tb)
     thread.additionalInfo.pydev_force_stop_at_exception = (frame, frames_byid)
     thread.additionalInfo.message = exception_breakpoint.qname
-    #sys.exc_info = lambda : (exctype, value, traceback)
     debugger = GetGlobalDebugger()
-    debugger.force_post_mortem_stop += 1
 
     pydevd_tracing.SetTrace(None) #no tracing from here
 
@@ -133,38 +116,27 @@
     debugger.handle_post_mortem_stop(thread.additionalInfo, thread)
 
 #=======================================================================================================================
-# set_pm_excepthook
+# _set_pm_excepthook
 #=======================================================================================================================
-def set_pm_excepthook(handle_exceptions_arg=None):
+def _set_pm_excepthook(handle_exceptions_dict=None):
     '''
     Should be called to register the excepthook to be used.
 
-    It's only useful for uncaucht exceptions. I.e.: exceptions that go up to the excepthook.
+    It's only useful for uncaught exceptions. I.e.: exceptions that go up to the excepthook.
 
-    Can receive a parameter to stop only on some exceptions.
-
-    E.g.:
-        register_excepthook((IndexError, ValueError))
-
-        or
-
-        register_excepthook(IndexError)
-
-        if passed without a parameter, will break on any exception
-
-    @param handle_exceptions: exception or tuple(exceptions)
+    @param handle_exceptions: dict(exception -> ExceptionBreakpoint)
         The exceptions that should be handled.
     '''
     global _handle_exceptions
     global _original_excepthook
-    if sys.excepthook != excepthook:
-        #Only keep the original if it's not our own excepthook (if called many times).
+    if sys.excepthook != _excepthook:
+        #Only keep the original if it's not our own _excepthook (if called many times).
         _original_excepthook = sys.excepthook
 
-    _handle_exceptions = handle_exceptions_arg
-    sys.excepthook = excepthook
+    _handle_exceptions = handle_exceptions_dict
+    sys.excepthook = _excepthook
 
-def restore_pm_excepthook():
+def _restore_pm_excepthook():
     global _original_excepthook
     if _original_excepthook:
         sys.excepthook = _original_excepthook
@@ -172,27 +144,16 @@
 
 
 def update_exception_hook(dbg):
-    if dbg.exception_set:
-        set_pm_excepthook(dict(dbg.exception_set))
+    if dbg.break_on_uncaught_exceptions:
+        _set_pm_excepthook(dbg.break_on_uncaught_exceptions)
     else:
-        restore_pm_excepthook()
+        _restore_pm_excepthook()
 
-def get_class( kls ):
+def _get_class( kls ):
     if IS_PY24 and "BaseException" == kls:
         kls = "Exception"
-    parts = kls.split('.')
-    module = ".".join(parts[:-1])
-    if module == "":
-        if IS_PY3K:
-            module = "builtins"
-        else:
-            module = "__builtin__"
+
     try:
-        m = __import__( module )
-        for comp in parts[-1:]:
-            if m is None:
-                return None
-            m = getattr(m, comp, None)
-        return m
-    except ImportError:
-        return None
\ No newline at end of file
+        return eval(kls)
+    except:
+        return pydevd_import_class.ImportName(kls)
diff --git a/python/helpers/pydev/pydevd_comm.py b/python/helpers/pydev/pydevd_comm.py
index b4cf585..c7f39a1 100644
--- a/python/helpers/pydev/pydevd_comm.py
+++ b/python/helpers/pydev/pydevd_comm.py
@@ -61,32 +61,14 @@
 
 import sys
 
-if USE_LIB_COPY:
-    import _pydev_time as time
-    import _pydev_threading as threading
-    try:
-        import _pydev_thread as thread
-    except ImportError:
-        import _thread as thread #Py3K changed it.
-    import _pydev_Queue as _queue
-    from _pydev_socket import socket
-    from _pydev_socket import AF_INET, SOCK_STREAM
-    from _pydev_socket import SHUT_RD, SHUT_WR
-else:
-    import time
-    import threading
-    try:
-        import thread
-    except ImportError:
-        import _thread as thread #Py3K changed it.
+from _pydev_imps import _pydev_time as time
 
-    try:
-        import Queue as _queue
-    except ImportError:
-        import queue as _queue
-    from socket import socket
-    from socket import AF_INET, SOCK_STREAM
-    from socket import SHUT_RD, SHUT_WR
+if USE_LIB_COPY:
+    import _pydev_threading as threading
+else:
+    import threading
+from _pydev_imps._pydev_socket import socket, AF_INET, SOCK_STREAM, SHUT_RD, SHUT_WR
+from pydev_imports import _queue
 
 try:
     from urllib import quote, quote_plus, unquote, unquote_plus
@@ -103,6 +85,8 @@
 import _pydev_completer
 
 from pydevd_tracing import GetExceptionTracebackStr
+import pydevd_console
+from pydev_monkey import disable_trace_thread_modules, enable_trace_thread_modules
 
 
 
@@ -126,6 +110,8 @@
 CMD_RUN_TO_LINE = 118
 CMD_RELOAD_CODE = 119
 CMD_GET_COMPLETIONS = 120
+
+# Note: renumbered (conflicted on merge)
 CMD_CONSOLE_EXEC = 121
 CMD_ADD_EXCEPTION_BREAK = 122
 CMD_REMOVE_EXCEPTION_BREAK = 123
@@ -136,6 +122,24 @@
 CMD_SMART_STEP_INTO = 128
 CMD_EXIT = 129
 CMD_SIGNATURE_CALL_TRACE = 130
+
+
+
+CMD_SET_PY_EXCEPTION = 131
+CMD_GET_FILE_CONTENTS = 132
+CMD_SET_PROPERTY_TRACE = 133
+# Pydev debug console commands
+CMD_EVALUATE_CONSOLE_EXPRESSION = 134
+CMD_RUN_CUSTOM_OPERATION = 135
+CMD_GET_BREAKPOINT_EXCEPTION = 136
+CMD_STEP_CAUGHT_EXCEPTION = 137
+CMD_SEND_CURR_EXCEPTION_TRACE = 138
+CMD_SEND_CURR_EXCEPTION_TRACE_PROCEEDED = 139
+CMD_IGNORE_THROWN_EXCEPTION_AT = 140
+CMD_ENABLE_DONT_TRACE = 141
+
+
+
 CMD_VERSION = 501
 CMD_RETURN = 502
 CMD_ERROR = 901
@@ -171,6 +175,19 @@
     '128':'CMD_SMART_STEP_INTO',
     '129': 'CMD_EXIT',
     '130': 'CMD_SIGNATURE_CALL_TRACE',
+
+    '131': 'CMD_SET_PY_EXCEPTION',
+    '132': 'CMD_GET_FILE_CONTENTS',
+    '133': 'CMD_SET_PROPERTY_TRACE',
+    '134': 'CMD_EVALUATE_CONSOLE_EXPRESSION',
+    '135': 'CMD_RUN_CUSTOM_OPERATION',
+    '136': 'CMD_GET_BREAKPOINT_EXCEPTION',
+    '137': 'CMD_STEP_CAUGHT_EXCEPTION',
+    '138': 'CMD_SEND_CURR_EXCEPTION_TRACE',
+    '139': 'CMD_SEND_CURR_EXCEPTION_TRACE_PROCEEDED',
+    '140': 'CMD_IGNORE_THROWN_EXCEPTION_AT',
+    '141': 'CMD_ENABLE_DONT_TRACE',
+
     '501':'CMD_VERSION',
     '502':'CMD_RETURN',
     '901':'CMD_ERROR',
@@ -274,7 +291,7 @@
         #We must close the socket so that it doesn't stay halted there.
         self.killReceived = True
         try:
-            self.sock.shutdown(SHUT_RD) #shotdown the socket for read
+            self.sock.shutdown(SHUT_RD) #shutdown the socket for read
         except:
             #just ignore that
             pass
@@ -564,68 +581,69 @@
         except:
             return self.makeErrorMessage(0, GetExceptionTracebackStr())
 
-    def makeThreadSuspendMessage(self, thread_id, frame, stop_reason, message):
-
+    def makeThreadSuspendStr(self, thread_id, frame, stop_reason, message):
         """ <xml>
             <thread id="id" stop_reason="reason">
                     <frame id="id" name="functionName " file="file" line="line">
                     <var variable stuffff....
                 </frame>
             </thread>
-           """
+        """
+        cmdTextList = ["<xml>"]
+
+        if message:
+            message = pydevd_vars.makeValidXmlValue(str(message))
+
+        cmdTextList.append('<thread id="%s" stop_reason="%s" message="%s">' % (thread_id, stop_reason, message))
+
+        curFrame = frame
         try:
-            cmdTextList = ["<xml>"]
+            while curFrame:
+                #print cmdText
+                myId = str(id(curFrame))
+                #print "id is ", myId
 
-            if message:
-                message = pydevd_vars.makeValidXmlValue(str(message))
+                if curFrame.f_code is None:
+                    break #Iron Python sometimes does not have it!
 
-            cmdTextList.append('<thread id="%s" stop_reason="%s" message="%s">' % (thread_id, stop_reason, message))
+                myName = curFrame.f_code.co_name #method name (if in method) or ? if global
+                if myName is None:
+                    break #Iron Python sometimes does not have it!
 
-            curFrame = frame
-            try:
-                while curFrame:
-                    #print cmdText
-                    myId = str(id(curFrame))
-                    #print "id is ", myId
+                #print "name is ", myName
 
-                    if curFrame.f_code is None:
-                        break #Iron Python sometimes does not have it!
+                filename, base = pydevd_file_utils.GetFilenameAndBase(curFrame)
 
-                    myName = curFrame.f_code.co_name #method name (if in method) or ? if global
-                    if myName is None:
-                        break #Iron Python sometimes does not have it!
+                myFile = pydevd_file_utils.NormFileToClient(filename)
+                if file_system_encoding.lower() != "utf-8" and hasattr(myFile, "decode"):
+                    # myFile is a byte string encoded using the file system encoding
+                    # convert it to utf8
+                    myFile = myFile.decode(file_system_encoding).encode("utf-8")
 
-                    #print "name is ", myName
+                #print "file is ", myFile
+                #myFile = inspect.getsourcefile(curFrame) or inspect.getfile(frame)
 
-                    filename, base = pydevd_file_utils.GetFilenameAndBase(curFrame)
+                myLine = str(curFrame.f_lineno)
+                #print "line is ", myLine
 
-                    myFile = pydevd_file_utils.NormFileToClient(filename)
-                    if file_system_encoding.lower() != "utf-8" and hasattr(myFile, "decode"):
-                        # myFile is a byte string encoded using the file system encoding
-                        # convert it to utf8
-                        myFile = myFile.decode(file_system_encoding).encode("utf-8")
+                #the variables are all gotten 'on-demand'
+                #variables = pydevd_vars.frameVarsToXML(curFrame.f_locals)
 
-                    #print "file is ", myFile
-                    #myFile = inspect.getsourcefile(curFrame) or inspect.getfile(frame)
+                variables = ''
+                cmdTextList.append('<frame id="%s" name="%s" ' % (myId , pydevd_vars.makeValidXmlValue(myName)))
+                cmdTextList.append('file="%s" line="%s">"' % (quote(myFile, '/>_= \t'), myLine))
+                cmdTextList.append(variables)
+                cmdTextList.append("</frame>")
+                curFrame = curFrame.f_back
+        except :
+            traceback.print_exc()
 
-                    myLine = str(curFrame.f_lineno)
-                    #print "line is ", myLine
+        cmdTextList.append("</thread></xml>")
+        return ''.join(cmdTextList)
 
-                    #the variables are all gotten 'on-demand'
-                    #variables = pydevd_vars.frameVarsToXML(curFrame.f_locals)
-
-                    variables = ''
-                    cmdTextList.append('<frame id="%s" name="%s" ' % (myId , pydevd_vars.makeValidXmlValue(myName)))
-                    cmdTextList.append('file="%s" line="%s">"' % (quote(myFile, '/>_= \t'), myLine))
-                    cmdTextList.append(variables)
-                    cmdTextList.append("</frame>")
-                    curFrame = curFrame.f_back
-            except :
-                traceback.print_exc()
-
-            cmdTextList.append("</thread></xml>")
-            cmdText = ''.join(cmdTextList)
-            return NetCommand(CMD_THREAD_SUSPEND, 0, cmdText)
+    def makeThreadSuspendMessage(self, thread_id, frame, stop_reason, message):
+        try:
+            return NetCommand(CMD_THREAD_SUSPEND, 0, self.makeThreadSuspendStr(thread_id, frame, stop_reason, message))
         except:
             return self.makeErrorMessage(0, GetExceptionTracebackStr())
 
@@ -660,6 +678,51 @@
         except Exception:
             return self.makeErrorMessage(seq, GetExceptionTracebackStr())
 
+    def makeGetFileContents(self, seq, payload):
+        try:
+            return NetCommand(CMD_GET_FILE_CONTENTS, seq, payload)
+        except Exception:
+            return self.makeErrorMessage(seq, GetExceptionTracebackStr())
+
+    def makeSendBreakpointExceptionMessage(self, seq, payload):
+        try:
+            return NetCommand(CMD_GET_BREAKPOINT_EXCEPTION, seq, payload)
+        except Exception:
+            return self.makeErrorMessage(seq, GetExceptionTracebackStr())
+
+    def makeSendCurrExceptionTraceMessage(self, seq, thread_id, curr_frame_id, exc_type, exc_desc, trace_obj):
+        try:
+            while trace_obj.tb_next is not None:
+                trace_obj = trace_obj.tb_next
+
+            exc_type = pydevd_vars.makeValidXmlValue(str(exc_type)).replace('\t', '  ') or 'exception: type unknown'
+            exc_desc = pydevd_vars.makeValidXmlValue(str(exc_desc)).replace('\t', '  ') or 'exception: no description'
+
+            payload = str(curr_frame_id) + '\t' + exc_type + "\t" + exc_desc + "\t" + \
+                self.makeThreadSuspendStr(thread_id, trace_obj.tb_frame, CMD_SEND_CURR_EXCEPTION_TRACE, '')
+
+            return NetCommand(CMD_SEND_CURR_EXCEPTION_TRACE, seq, payload)
+        except Exception:
+            return self.makeErrorMessage(seq, GetExceptionTracebackStr())
+
+    def makeSendCurrExceptionTraceProceededMessage(self, seq, thread_id):
+        try:
+            return NetCommand(CMD_SEND_CURR_EXCEPTION_TRACE_PROCEEDED, 0, str(thread_id))
+        except:
+            return self.makeErrorMessage(0, GetExceptionTracebackStr())
+
+    def makeSendConsoleMessage(self, seq, payload):
+        try:
+            return NetCommand(CMD_EVALUATE_CONSOLE_EXPRESSION, seq, payload)
+        except Exception:
+            return self.makeErrorMessage(seq, GetExceptionTracebackStr())
+
+    def makeCustomOperationMessage(self, seq, payload):
+        try:
+            return NetCommand(CMD_RUN_CUSTOM_OPERATION, seq, payload)
+        except Exception:
+            return self.makeErrorMessage(seq, GetExceptionTracebackStr())
+
     def makeLoadSourceMessage(self, seq, source, dbg=None):
         try:
             net = NetCommand(CMD_LOAD_SOURCE, seq, '%s' % source)
@@ -698,7 +761,7 @@
     def canBeExecutedBy(self, thread_id):
         '''By default, it must be in the same thread to be executed
         '''
-        return self.thread_id == thread_id
+        return self.thread_id == thread_id or self.thread_id.endswith('|' + thread_id)
 
     def doIt(self, dbg):
         raise NotImplementedError("you have to override doIt")
@@ -929,7 +992,7 @@
         try:
             result = pydevd_vars.evaluateExpression(self.thread_id, self.frame_id, self.expression, self.doExec)
             xml = "<xml>"
-            xml += pydevd_vars.varToXML(result, "", self.doTrim)
+            xml += pydevd_vars.varToXML(result, self.expression, self.doTrim)
             xml += "</xml>"
             cmd = dbg.cmdFactory.makeEvaluateExpressionMessage(self.sequence, xml)
             dbg.writer.addCommand(cmd)
@@ -961,7 +1024,6 @@
                 frame = pydevd_vars.findFrame(self.thread_id, self.frame_id)
                 if frame is not None:
 
-
                     msg = _pydev_completer.GenerateCompletionsAsXML(frame, self.act_tok)
 
                     cmd = dbg.cmdFactory.makeGetCompletionsMessage(self.sequence, msg)
@@ -981,6 +1043,182 @@
             cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error evaluating expression " + exc)
             dbg.writer.addCommand(cmd)
 
+#=======================================================================================================================
+# InternalGetBreakpointException
+#=======================================================================================================================
+class InternalGetBreakpointException(InternalThreadCommand):
+    """ Send details of exception raised while evaluating conditional breakpoint """
+    def __init__(self, thread_id, exc_type, stacktrace):
+        self.sequence = 0
+        self.thread_id = thread_id
+        self.stacktrace = stacktrace
+        self.exc_type = exc_type
+
+    def doIt(self, dbg):
+        try:
+            callstack = "<xml>"
+
+            makeValid = pydevd_vars.makeValidXmlValue
+
+            for filename, line, methodname, methodobj in self.stacktrace:
+                if file_system_encoding.lower() != "utf-8" and hasattr(filename, "decode"):
+                    # filename is a byte string encoded using the file system encoding
+                    # convert it to utf8
+                    filename = filename.decode(file_system_encoding).encode("utf-8")
+
+                callstack += '<frame thread_id = "%s" file="%s" line="%s" name="%s" obj="%s" />' \
+                                    % (self.thread_id, makeValid(filename), line, makeValid(methodname), makeValid(methodobj))
+            callstack += "</xml>"
+
+            cmd = dbg.cmdFactory.makeSendBreakpointExceptionMessage(self.sequence, self.exc_type + "\t" + callstack)
+            dbg.writer.addCommand(cmd)
+        except:
+            exc = GetExceptionTracebackStr()
+            sys.stderr.write('%s\n' % (exc,))
+            cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error Sending Exception: " + exc)
+            dbg.writer.addCommand(cmd)
+
+
+#=======================================================================================================================
+# InternalSendCurrExceptionTrace
+#=======================================================================================================================
+class InternalSendCurrExceptionTrace(InternalThreadCommand):
+    """ Send details of the exception that was caught and where we've broken in.
+    """
+    def __init__(self, thread_id, arg, curr_frame_id):
+        '''
+        :param arg: exception type, description, traceback object
+        '''
+        self.sequence = 0
+        self.thread_id = thread_id
+        self.curr_frame_id = curr_frame_id
+        self.arg = arg
+
+    def doIt(self, dbg):
+        try:
+            cmd = dbg.cmdFactory.makeSendCurrExceptionTraceMessage(self.sequence, self.thread_id, self.curr_frame_id, *self.arg)
+            del self.arg
+            dbg.writer.addCommand(cmd)
+        except:
+            exc = GetExceptionTracebackStr()
+            sys.stderr.write('%s\n' % (exc,))
+            cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error Sending Current Exception Trace: " + exc)
+            dbg.writer.addCommand(cmd)
+
+#=======================================================================================================================
+# InternalSendCurrExceptionTraceProceeded
+#=======================================================================================================================
+class InternalSendCurrExceptionTraceProceeded(InternalThreadCommand):
+    """ Send details of the exception that was caught and where we've broken in.
+    """
+    def __init__(self, thread_id):
+        self.sequence = 0
+        self.thread_id = thread_id
+
+    def doIt(self, dbg):
+        try:
+            cmd = dbg.cmdFactory.makeSendCurrExceptionTraceProceededMessage(self.sequence, self.thread_id)
+            dbg.writer.addCommand(cmd)
+        except:
+            exc = GetExceptionTracebackStr()
+            sys.stderr.write('%s\n' % (exc,))
+            cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error Sending Current Exception Trace Proceeded: " + exc)
+            dbg.writer.addCommand(cmd)
+
+
+#=======================================================================================================================
+# InternalEvaluateConsoleExpression
+#=======================================================================================================================
+class InternalEvaluateConsoleExpression(InternalThreadCommand):
+    """ Execute the given command in the debug console """
+
+    def __init__(self, seq, thread_id, frame_id, line):
+        self.sequence = seq
+        self.thread_id = thread_id
+        self.frame_id = frame_id
+        self.line = line
+
+    def doIt(self, dbg):
+        """ Create an XML for console output, error and more (true/false)
+        <xml>
+            <output message=output_message></output>
+            <error message=error_message></error>
+            <more>true/false</more>
+        </xml>
+        """
+        try:
+            frame = pydevd_vars.findFrame(self.thread_id, self.frame_id)
+            if frame is not None:
+                console_message = pydevd_console.execute_console_command(frame, self.thread_id, self.frame_id, self.line)
+                cmd = dbg.cmdFactory.makeSendConsoleMessage(self.sequence, console_message.toXML())
+            else:
+                from pydevd_console import ConsoleMessage
+                console_message = ConsoleMessage()
+                console_message.add_console_message(
+                    pydevd_console.CONSOLE_ERROR, 
+                    "Select the valid frame in the debug view (thread: %s, frame: %s invalid)" % (self.thread_id, self.frame_id), 
+                )
+                cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, console_message.toXML())
+        except:
+            exc = GetExceptionTracebackStr()
+            cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error evaluating expression " + exc)
+        dbg.writer.addCommand(cmd)
+
+
+#=======================================================================================================================
+# InternalRunCustomOperation
+#=======================================================================================================================
+class InternalRunCustomOperation(InternalThreadCommand):
+    """ Run a custom command on an expression
+    """
+    def __init__(self, seq, thread_id, frame_id, scope, attrs, style, encoded_code_or_file, fnname):
+        self.sequence = seq
+        self.thread_id = thread_id
+        self.frame_id = frame_id
+        self.scope = scope
+        self.attrs = attrs
+        self.style = style
+        self.code_or_file = unquote_plus(encoded_code_or_file)
+        self.fnname = fnname
+
+    def doIt(self, dbg):
+        try:
+            res = pydevd_vars.customOperation(self.thread_id, self.frame_id, self.scope, self.attrs,
+                                              self.style, self.code_or_file, self.fnname)
+            resEncoded = quote_plus(res)
+            cmd = dbg.cmdFactory.makeCustomOperationMessage(self.sequence, resEncoded)
+            dbg.writer.addCommand(cmd)
+        except:
+            exc = GetExceptionTracebackStr()
+            cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error in running custom operation" + exc)
+            dbg.writer.addCommand(cmd)
+
+
+#=======================================================================================================================
+# InternalConsoleGetCompletions
+#=======================================================================================================================
+class InternalConsoleGetCompletions(InternalThreadCommand):
+    """ Fetch the completions in the debug console
+    """
+    def __init__(self, seq, thread_id, frame_id, act_tok):
+        self.sequence = seq
+        self.thread_id = thread_id
+        self.frame_id = frame_id
+        self.act_tok = act_tok
+
+    def doIt(self, dbg):
+        """ Get completions and write back to the client
+        """
+        try:
+            frame = pydevd_vars.findFrame(self.thread_id, self.frame_id)
+            completions_xml = pydevd_console.get_completions(frame, self.act_tok)
+            cmd = dbg.cmdFactory.makeSendConsoleMessage(self.sequence, completions_xml)
+            dbg.writer.addCommand(cmd)
+        except:
+            exc = GetExceptionTracebackStr()
+            cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error in fetching completions" + exc)
+            dbg.writer.addCommand(cmd)
+
 
 #=======================================================================================================================
 # InternalConsoleExec
@@ -996,13 +1234,10 @@
 
     def doIt(self, dbg):
         """ Converts request into python variable """
-        pydev_start_new_thread = None
         try:
             try:
-                pydev_start_new_thread = thread.start_new_thread
-
-                thread.start_new_thread = thread._original_start_new_thread #don't trace new threads created by console command
-                thread.start_new = thread._original_start_new_thread
+                #don't trace new threads created by console command
+                disable_trace_thread_modules()
 
                 result = pydevconsole.consoleExec(self.thread_id, self.frame_id, self.expression)
                 xml = "<xml>"
@@ -1016,8 +1251,8 @@
                 cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error evaluating console expression " + exc)
                 dbg.writer.addCommand(cmd)
         finally:
-            thread.start_new_thread = pydev_start_new_thread
-            thread.start_new = pydev_start_new_thread
+            enable_trace_thread_modules()
+
             sys.stderr.flush()
             sys.stdout.flush()
 
diff --git a/python/helpers/pydev/pydevd_console.py b/python/helpers/pydev/pydevd_console.py
new file mode 100644
index 0000000..52b18bb
--- /dev/null
+++ b/python/helpers/pydev/pydevd_console.py
@@ -0,0 +1,212 @@
+'''An helper file for the pydev debugger (REPL) console
+'''
+from code import InteractiveConsole
+import sys
+import traceback
+
+import _pydev_completer
+from pydevd_tracing import GetExceptionTracebackStr
+from pydevd_vars import makeValidXmlValue
+from pydev_imports import Exec
+from pydevd_io import IOBuf
+from pydev_console_utils import BaseInterpreterInterface, BaseStdIn
+from pydev_override import overrides
+import pydevd_save_locals
+
+CONSOLE_OUTPUT = "output"
+CONSOLE_ERROR = "error"
+
+
+#=======================================================================================================================
+# ConsoleMessage
+#=======================================================================================================================
+class ConsoleMessage:
+    """Console Messages
+    """
+    def __init__(self):
+        self.more = False
+        # List of tuple [('error', 'error_message'), ('message_list', 'output_message')]
+        self.console_messages = []
+
+    def add_console_message(self, message_type, message):
+        """add messages in the console_messages list
+        """
+        for m in message.split("\n"):
+            if m.strip():
+                self.console_messages.append((message_type, m))
+
+    def update_more(self, more):
+        """more is set to true if further input is required from the user
+        else more is set to false
+        """
+        self.more = more
+
+    def toXML(self):
+        """Create an XML for console message_list, error and more (true/false)
+        <xml>
+            <message_list>console message_list</message_list>
+            <error>console error</error>
+            <more>true/false</more>
+        </xml>
+        """
+        makeValid = makeValidXmlValue
+
+        xml = '<xml><more>%s</more>' % (self.more)
+
+        for message_type, message in self.console_messages:
+            xml += '<%s message="%s"></%s>' % (message_type, makeValid(message), message_type)
+
+        xml += '</xml>'
+
+        return xml
+
+
+#=======================================================================================================================
+# DebugConsoleStdIn
+#=======================================================================================================================
+class DebugConsoleStdIn(BaseStdIn):
+
+    overrides(BaseStdIn.readline)
+    def readline(self, *args, **kwargs):
+        sys.stderr.write('Warning: Reading from stdin is still not supported in this console.\n')
+        return '\n'
+
+#=======================================================================================================================
+# DebugConsole
+#=======================================================================================================================
+class DebugConsole(InteractiveConsole, BaseInterpreterInterface):
+    """Wrapper around code.InteractiveConsole, in order to send
+    errors and outputs to the debug console
+    """
+
+    overrides(BaseInterpreterInterface.createStdIn)
+    def createStdIn(self):
+        return DebugConsoleStdIn() #For now, raw_input is not supported in this console.
+
+
+    overrides(InteractiveConsole.push)
+    def push(self, line, frame):
+        """Change built-in stdout and stderr methods by the
+        new custom StdMessage.
+        execute the InteractiveConsole.push.
+        Change the stdout and stderr back be the original built-ins
+
+        Return boolean (True if more input is required else False),
+        output_messages and input_messages
+        """
+        more = False
+        original_stdout = sys.stdout
+        original_stderr = sys.stderr
+        try:
+            try:
+                self.frame = frame
+                out = sys.stdout = IOBuf()
+                err = sys.stderr = IOBuf()
+                more = self.addExec(line)
+            except Exception:
+                exc = GetExceptionTracebackStr()
+                err.buflist.append("Internal Error: %s" % (exc,))
+        finally:
+            #Remove frame references.
+            self.frame = None
+            frame = None
+            sys.stdout = original_stdout
+            sys.stderr = original_stderr
+
+        return more, out.buflist, err.buflist
+
+
+    overrides(BaseInterpreterInterface.doAddExec)
+    def doAddExec(self, line):
+        return InteractiveConsole.push(self, line)
+
+
+    overrides(InteractiveConsole.runcode)
+    def runcode(self, code):
+        """Execute a code object.
+
+        When an exception occurs, self.showtraceback() is called to
+        display a traceback.  All exceptions are caught except
+        SystemExit, which is reraised.
+
+        A note about KeyboardInterrupt: this exception may occur
+        elsewhere in this code, and may not always be caught.  The
+        caller should be prepared to deal with it.
+
+        """
+        try:
+            Exec(code, self.frame.f_globals, self.frame.f_locals)
+            pydevd_save_locals.save_locals(self.frame)
+        except SystemExit:
+            raise
+        except:
+            self.showtraceback()
+
+
+#=======================================================================================================================
+# InteractiveConsoleCache
+#=======================================================================================================================
+class InteractiveConsoleCache:
+
+    thread_id = None
+    frame_id = None
+    interactive_console_instance = None
+
+
+#Note: On Jython 2.1 we can't use classmethod or staticmethod, so, just make the functions below free-functions.
+def get_interactive_console(thread_id, frame_id, frame, console_message):
+    """returns the global interactive console.
+    interactive console should have been initialized by this time
+    """
+    if InteractiveConsoleCache.thread_id == thread_id and InteractiveConsoleCache.frame_id == frame_id:
+        return InteractiveConsoleCache.interactive_console_instance
+
+    InteractiveConsoleCache.interactive_console_instance = DebugConsole()
+    InteractiveConsoleCache.thread_id = thread_id
+    InteractiveConsoleCache.frame_id = frame_id
+
+    console_stacktrace = traceback.extract_stack(frame, limit=1)
+    if console_stacktrace:
+        current_context = console_stacktrace[0] # top entry from stacktrace
+        context_message = 'File "%s", line %s, in %s' % (current_context[0], current_context[1], current_context[2])
+        console_message.add_console_message(CONSOLE_OUTPUT, "[Current context]: %s" % (context_message,))
+    return InteractiveConsoleCache.interactive_console_instance
+
+
+def clear_interactive_console():
+    InteractiveConsoleCache.thread_id = None
+    InteractiveConsoleCache.frame_id = None
+    InteractiveConsoleCache.interactive_console_instance = None
+
+
+def execute_console_command(frame, thread_id, frame_id, line):
+    """fetch an interactive console instance from the cache and
+    push the received command to the console.
+
+    create and return an instance of console_message
+    """
+    console_message = ConsoleMessage()
+
+    interpreter = get_interactive_console(thread_id, frame_id, frame, console_message)
+    more, output_messages, error_messages = interpreter.push(line, frame)
+    console_message.update_more(more)
+
+    for message in output_messages:
+        console_message.add_console_message(CONSOLE_OUTPUT, message)
+
+    for message in error_messages:
+        console_message.add_console_message(CONSOLE_ERROR, message)
+
+    return console_message
+
+
+def get_completions(frame, act_tok):
+    """ fetch all completions, create xml for the same
+    return the completions xml
+    """
+    return _pydev_completer.GenerateCompletionsAsXML(frame, act_tok)
+
+
+
+
+
diff --git a/python/helpers/pydev/pydevd_constants.py b/python/helpers/pydev/pydevd_constants.py
index 71fe4ae..74e8974 100644
--- a/python/helpers/pydev/pydevd_constants.py
+++ b/python/helpers/pydev/pydevd_constants.py
@@ -1,7 +1,6 @@
 '''
 This module holds the constants used for specifying the states of the debugger.
 '''
-
 STATE_RUN = 1
 STATE_SUSPEND = 2
 
@@ -17,13 +16,13 @@
     setattr(__builtin__, 'False', 0)
 
 class DebugInfoHolder:
-    #we have to put it here because it can be set through the command line (so, the 
+    #we have to put it here because it can be set through the command line (so, the
     #already imported references would not have it).
     DEBUG_RECORD_SOCKET_READS = False
     DEBUG_TRACE_LEVEL = -1
     DEBUG_TRACE_BREAKPOINTS = -1
 
-#Optimize with psyco? This gave a 50% speedup in the debugger in tests 
+#Optimize with psyco? This gave a 50% speedup in the debugger in tests
 USE_PSYCO_OPTIMIZATION = True
 
 #Hold a reference to the original _getframe (because psyco will change that as soon as it's imported)
@@ -111,11 +110,48 @@
             return default
 
 
+if IS_PY3K:
+    def DictKeys(d):
+        return list(d.keys())
+
+    def DictValues(d):
+        return list(d.values())
+
+    DictIterValues = dict.values
+
+    def DictIterItems(d):
+        return d.items()
+
+    def DictItems(d):
+        return list(d.items())
+
+else:
+    DictKeys = dict.keys
+    try:
+        DictIterValues = dict.itervalues
+    except:
+        DictIterValues = dict.values #Older versions don't have the itervalues
+
+    DictValues = dict.values
+
+    def DictIterItems(d):
+        return d.iteritems()
+
+    def DictItems(d):
+        return d.items()
+
+
 try:
-    xrange
+    xrange = xrange
 except:
     #Python 3k does not have it
     xrange = range
+    
+try:
+    import itertools
+    izip = itertools.izip
+except:
+    izip = zip
 
 try:
     object
@@ -128,10 +164,10 @@
 except:
     def enumerate(lst):
         ret = []
-        i=0
+        i = 0
         for element in lst:
             ret.append((i, element))
-            i+=1
+            i += 1
         return ret
 
 #=======================================================================================================================
@@ -174,8 +210,7 @@
                 except AttributeError:
                     try:
                         #Jython does not have it!
-                        import java.lang.management.ManagementFactory #@UnresolvedImport -- just for jython
-
+                        import java.lang.management.ManagementFactory  #@UnresolvedImport -- just for jython
                         pid = java.lang.management.ManagementFactory.getRuntimeMXBean().getName()
                         pid = pid.replace('@', '_')
                     except:
@@ -262,4 +297,4 @@
 if __name__ == '__main__':
     if Null():
         sys.stdout.write('here\n')
-        
+
diff --git a/python/helpers/pydev/pydevd_dont_trace.py b/python/helpers/pydev/pydevd_dont_trace.py
new file mode 100644
index 0000000..2d5ad95
--- /dev/null
+++ b/python/helpers/pydev/pydevd_dont_trace.py
@@ -0,0 +1,127 @@
+'''
+Support for a tag that allows skipping over functions while debugging.
+'''
+import linecache
+import re
+from pydevd_constants import DictContains
+
+# To suppress tracing a method, add the tag @DontTrace
+# to a comment either preceding or on the same line as
+# the method definition
+#
+# E.g.:
+# #@DontTrace
+# def test1():
+#     pass
+#
+#  ... or ...
+#
+# def test2(): #@DontTrace
+#     pass
+DONT_TRACE_TAG = '@DontTrace'
+
+# Regular expression to match a decorator (at the beginning
+# of a line).
+RE_DECORATOR = re.compile(r'^\s*@')
+
+# Mapping from code object to bool.
+# If the key exists, the value is the cached result of should_trace_hook
+_filename_to_ignored_lines = {}
+
+def default_should_trace_hook(frame, filename):
+    '''
+    Return True if this frame should be traced, False if tracing should be blocked.
+    '''
+    # First, check whether this code object has a cached value
+    ignored_lines = _filename_to_ignored_lines.get(filename)
+    if ignored_lines is None:
+        # Now, look up that line of code and check for a @DontTrace
+        # preceding or on the same line as the method.
+        # E.g.:
+        # #@DontTrace
+        # def test():
+        #     pass
+        #  ... or ...
+        # def test(): #@DontTrace
+        #     pass
+        ignored_lines = {}
+        lines = linecache.getlines(filename)
+        i_line = 0  # Could use enumerate, but not there on all versions...
+        for line in lines:
+            j = line.find('#')
+            if j >= 0:
+                comment = line[j:]
+                if DONT_TRACE_TAG in comment:
+                    ignored_lines[i_line] = 1
+                    
+                    #Note: when it's found in the comment, mark it up and down for the decorator lines found.
+                    k = i_line - 1
+                    while k >= 0:
+                        if RE_DECORATOR.match(lines[k]):
+                            ignored_lines[k] = 1
+                            k -= 1
+                        else:
+                            break
+                        
+                    k = i_line + 1
+                    while k <= len(lines):
+                        if RE_DECORATOR.match(lines[k]):
+                            ignored_lines[k] = 1
+                            k += 1
+                        else:
+                            break
+                        
+            i_line += 1
+                    
+
+        _filename_to_ignored_lines[filename] = ignored_lines
+
+    func_line = frame.f_code.co_firstlineno - 1 # co_firstlineno is 1-based, so -1 is needed
+    return not (
+        DictContains(ignored_lines, func_line - 1) or #-1 to get line before method 
+        DictContains(ignored_lines, func_line)) #method line
+
+
+should_trace_hook = None
+
+
+def clear_trace_filter_cache():
+    '''
+    Clear the trace filter cache.
+    Call this after reloading.
+    '''
+    global should_trace_hook
+    try:
+        # Need to temporarily disable a hook because otherwise
+        # _filename_to_ignored_lines.clear() will never complete.
+        old_hook = should_trace_hook
+        should_trace_hook = None
+
+        # Clear the linecache
+        linecache.clearcache()
+        _filename_to_ignored_lines.clear()
+
+    finally:
+        should_trace_hook = old_hook
+
+
+def trace_filter(mode):
+    '''
+    Set the trace filter mode.
+
+    mode: Whether to enable the trace hook.
+      True: Trace filtering on (skipping methods tagged @DontTrace)
+      False: Trace filtering off (trace methods tagged @DontTrace)
+      None/default: Toggle trace filtering.
+    '''
+    global should_trace_hook
+    if mode is None:
+        mode = should_trace_hook is None
+
+    if mode:
+        should_trace_hook = default_should_trace_hook
+    else:
+        should_trace_hook = None
+
+    return mode
+
diff --git a/python/helpers/pydev/pydevd_file_utils.py b/python/helpers/pydev/pydevd_file_utils.py
index b4f8d50..c135c4b 100644
--- a/python/helpers/pydev/pydevd_file_utils.py
+++ b/python/helpers/pydev/pydevd_file_utils.py
@@ -3,72 +3,91 @@
         - The case of a file will match the actual file in the filesystem (otherwise breakpoints won't be hit).
         - Providing means for the user to make path conversions when doing a remote debugging session in
           one machine and debugging in another.
-    
+
     To do that, the PATHS_FROM_ECLIPSE_TO_PYTHON constant must be filled with the appropriate paths.
-    
-    @note: 
-        in this context, the server is where your python process is running 
+
+    @note:
+        in this context, the server is where your python process is running
         and the client is where eclipse is running.
-    
-    E.g.: 
+
+    E.g.:
         If the server (your python process) has the structure
-            /user/projects/my_project/src/package/module1.py  
-        
-        and the client has: 
-            c:\my_project\src\package\module1.py  
-            
+            /user/projects/my_project/src/package/module1.py
+
+        and the client has:
+            c:\my_project\src\package\module1.py
+
         the PATHS_FROM_ECLIPSE_TO_PYTHON would have to be:
             PATHS_FROM_ECLIPSE_TO_PYTHON = [(r'c:\my_project\src', r'/user/projects/my_project/src')]
-    
+
     @note: DEBUG_CLIENT_SERVER_TRANSLATION can be set to True to debug the result of those translations
-    
+
     @note: the case of the paths is important! Note that this can be tricky to get right when one machine
     uses a case-independent filesystem and the other uses a case-dependent filesystem (if the system being
-    debugged is case-independent, 'normcase()' should be used on the paths defined in PATHS_FROM_ECLIPSE_TO_PYTHON). 
-    
+    debugged is case-independent, 'normcase()' should be used on the paths defined in PATHS_FROM_ECLIPSE_TO_PYTHON).
+
     @note: all the paths with breakpoints must be translated (otherwise they won't be found in the server)
-    
+
     @note: to enable remote debugging in the target machine (pydev extensions in the eclipse installation)
         import pydevd;pydevd.settrace(host, stdoutToServer, stderrToServer, port, suspend)
-        
+
         see parameter docs on pydevd.py
-        
-    @note: for doing a remote debugging session, all the pydevd_ files must be on the server accessible 
-        through the PYTHONPATH (and the PATHS_FROM_ECLIPSE_TO_PYTHON only needs to be set on the target 
+
+    @note: for doing a remote debugging session, all the pydevd_ files must be on the server accessible
+        through the PYTHONPATH (and the PATHS_FROM_ECLIPSE_TO_PYTHON only needs to be set on the target
         machine for the paths that'll actually have breakpoints).
 '''
 
-
-
-
-from pydevd_constants import * #@UnusedWildImport
 import os.path
 import sys
 import traceback
 
-
-
+os_normcase = os.path.normcase
 basename = os.path.basename
 exists = os.path.exists
 join = os.path.join
 
 try:
-    rPath = os.path.realpath #@UndefinedVariable
+    rPath = os.path.realpath  #@UndefinedVariable
 except:
     # jython does not support os.path.realpath
     # realpath is a no-op on systems without islink support
-    rPath = os.path.abspath 
-  
+    rPath = os.path.abspath
+
 #defined as a list of tuples where the 1st element of the tuple is the path in the client machine
 #and the 2nd element is the path in the server machine.
 #see module docstring for more details.
 PATHS_FROM_ECLIPSE_TO_PYTHON = []
 
-
 #example:
 #PATHS_FROM_ECLIPSE_TO_PYTHON = [
-#(normcase(r'd:\temp\temp_workspace_2\test_python\src\yyy\yyy'),
-# normcase(r'd:\temp\temp_workspace_2\test_python\src\hhh\xxx'))]
+#  (r'd:\temp\temp_workspace_2\test_python\src\yyy\yyy',
+#   r'd:\temp\temp_workspace_2\test_python\src\hhh\xxx')
+#]
+
+
+normcase = os_normcase # May be rebound on set_ide_os
+
+def set_ide_os(os):
+    '''
+    We need to set the IDE os because the host where the code is running may be
+    actually different from the client (and the point is that we want the proper
+    paths to translate from the client to the server).
+    '''
+    global normcase
+    if os == 'UNIX':
+        normcase = lambda f:f #Change to no-op if the client side is on unix/mac.
+    else:
+        normcase = os_normcase
+
+    # After setting the ide OS, apply the normcase to the existing paths.
+
+    # Note: not using enumerate nor list comprehension because it may not be available in older python versions...
+    i = 0
+    for path in PATHS_FROM_ECLIPSE_TO_PYTHON[:]:
+        PATHS_FROM_ECLIPSE_TO_PYTHON[i] = (normcase(path[0]), normcase(path[1]))
+        i += 1
+
 
 DEBUG_CLIENT_SERVER_TRANSLATION = False
 
@@ -79,14 +98,6 @@
 NORM_FILENAME_TO_CLIENT_CONTAINER = {}
 
 
-pycharm_os = None
-
-def normcase(file):
-    global pycharm_os
-    if pycharm_os == 'UNIX':
-        return file
-    else:
-        return os.path.normcase(file)
 
 
 def _NormFile(filename):
@@ -147,7 +158,7 @@
             return None
     return None
 
-    
+
 #Now, let's do a quick test to see if we're working with a version of python that has no problems
 #related to the names generated...
 try:
@@ -162,11 +173,11 @@
         sys.stderr.write('pydev debugger: Related bug: http://bugs.python.org/issue1666807\n')
         sys.stderr.write('-------------------------------------------------------------------------------\n')
         sys.stderr.flush()
-        
+
         NORM_SEARCH_CACHE = {}
-        
+
         initial_norm_file = _NormFile
-        def _NormFile(filename): #Let's redefine _NormFile to work with paths that may be incorrect
+        def _NormFile(filename):  #Let's redefine _NormFile to work with paths that may be incorrect
             try:
                 return NORM_SEARCH_CACHE[filename]
             except KeyError:
@@ -180,7 +191,7 @@
                     else:
                         sys.stderr.write('pydev debugger: Unable to find real location for: %s\n' % (filename,))
                         ret = filename
-                        
+
                 NORM_SEARCH_CACHE[filename] = ret
                 return ret
 except:
@@ -195,28 +206,28 @@
     for eclipse_prefix, server_prefix in PATHS_FROM_ECLIPSE_TO_PYTHON:
         if eclipse_sep is not None and python_sep is not None:
             break
-        
+
         if eclipse_sep is None:
             for c in eclipse_prefix:
                 if c in ('/', '\\'):
                     eclipse_sep = c
                     break
-                
+
         if python_sep is None:
             for c in server_prefix:
                 if c in ('/', '\\'):
                     python_sep = c
                     break
-        
+
     #If they're the same or one of them cannot be determined, just make it all None.
     if eclipse_sep == python_sep or eclipse_sep is None or python_sep is None:
         eclipse_sep = python_sep = None
-            
-                
-    #only setup translation functions if absolutely needed! 
+
+
+    #only setup translation functions if absolutely needed!
     def NormFileToServer(filename):
         #Eclipse will send the passed filename to be translated to the python process
-        #So, this would be 'NormFileFromEclipseToPython' 
+        #So, this would be 'NormFileFromEclipseToPython'
         try:
             return NORM_FILENAME_TO_SERVER_CONTAINER[filename]
         except KeyError:
@@ -234,17 +245,17 @@
                 if DEBUG_CLIENT_SERVER_TRANSLATION:
                     sys.stderr.write('pydev debugger: to server: unable to find matching prefix for: %s in %s\n' % \
                         (translated, [x[0] for x in PATHS_FROM_ECLIPSE_TO_PYTHON]))
-                    
+
             #Note that when going to the server, we do the replace first and only later do the norm file.
             if eclipse_sep is not None:
                 translated = translated.replace(eclipse_sep, python_sep)
             translated = _NormFile(translated)
-                
+
             NORM_FILENAME_TO_SERVER_CONTAINER[filename] = translated
             return translated
-        
-    
-    def NormFileToClient(filename): 
+
+
+    def NormFileToClient(filename):
         #The result of this method will be passed to eclipse
         #So, this would be 'NormFileFromPythonToEclipse'
         try:
@@ -264,15 +275,15 @@
                 if DEBUG_CLIENT_SERVER_TRANSLATION:
                     sys.stderr.write('pydev debugger: to client: unable to find matching prefix for: %s in %s\n' % \
                         (translated, [x[1] for x in PATHS_FROM_ECLIPSE_TO_PYTHON]))
-                        
+
             if eclipse_sep is not None:
                 translated = translated.replace(python_sep, eclipse_sep)
-            
+
             #The resulting path is not in the python process, so, we cannot do a _NormFile here,
             #only at the beginning of this method.
             NORM_FILENAME_TO_CLIENT_CONTAINER[filename] = translated
             return translated
-        
+
 else:
     #no translation step needed (just inline the calls)
     NormFileToClient = _NormFile
@@ -299,6 +310,3 @@
             f = f[:-1]
     return GetFileNameAndBaseFromFile(f)
 
-def set_pycharm_os(os):
-    global pycharm_os
-    pycharm_os = os
diff --git a/python/helpers/pydev/pydevd_frame.py b/python/helpers/pydev/pydevd_frame.py
index 05faaeb..374d281 100644
--- a/python/helpers/pydev/pydevd_frame.py
+++ b/python/helpers/pydev/pydevd_frame.py
@@ -1,18 +1,29 @@
-from django_debug import is_django_render_call, get_template_file_name, get_template_line, is_django_suspended, suspend_django, is_django_resolve_call, is_django_context_get_call
-from django_debug import find_django_render_frame
-from django_frame import just_raised
-from django_frame import is_django_exception_break_context
-from django_frame import DjangoTemplateFrame
-from pydevd_comm import * #@UnusedWildImport
-from pydevd_breakpoints import * #@UnusedWildImport
-import traceback #@Reimport
+import linecache
 import os.path
-import sys
+import re
+import traceback  # @Reimport
+
+from django_debug import find_django_render_frame
+from django_debug import is_django_render_call, is_django_suspended, suspend_django, is_django_resolve_call, is_django_context_get_call
+from django_frame import DjangoTemplateFrame
+from django_frame import is_django_exception_break_context
+from django_frame import just_raised, get_template_file_name, get_template_line
 import pydev_log
+from pydevd_breakpoints import get_exception_breakpoint, get_exception_name
+from pydevd_comm import CMD_ADD_DJANGO_EXCEPTION_BREAK, \
+    CMD_STEP_CAUGHT_EXCEPTION, CMD_STEP_RETURN, CMD_STEP_OVER, CMD_SET_BREAK, \
+    CMD_STEP_INTO, CMD_SMART_STEP_INTO, CMD_RUN_TO_LINE, CMD_SET_NEXT_STATEMENT
+from pydevd_constants import *  # @UnusedWildImport
+from pydevd_file_utils import GetFilenameAndBase
 from pydevd_signature import sendSignatureCallTrace
+import pydevd_vars
+import pydevd_dont_trace
 
 basename = os.path.basename
 
+IGNORE_EXCEPTION_TAG = re.compile('[^#]*#.*@IgnoreException')
+
+
 #=======================================================================================================================
 # PyDBFrame
 #=======================================================================================================================
@@ -22,6 +33,13 @@
     is reused for the entire context.
     '''
 
+    #Note: class (and not instance) attributes.
+
+    #Same thing in the main debugger but only considering the file contents, while the one in the main debugger
+    #considers the user input (so, the actual result must be a join of both).
+    filename_to_lines_where_exceptions_are_ignored = {}
+    filename_to_stat_info = {}
+
     def __init__(self, args):
         #args = mainDebugger, filename, base, info, t, frame
         #yeap, much faster than putting in self and then getting it from self later on
@@ -33,84 +51,234 @@
     def doWaitSuspend(self, *args, **kwargs):
         self._args[0].doWaitSuspend(*args, **kwargs)
 
+    def _is_django_render_call(self, frame):
+        try:
+            return self._cached_is_django_render_call
+        except:
+            # Calculate lazily: note that a PyDBFrame always deals with the same
+            # frame over and over, so, we can cache this.
+            # -- although we can't cache things which change over time (such as
+            #    the breakpoints for the file).
+            ret = self._cached_is_django_render_call = is_django_render_call(frame)
+            return ret
+
     def trace_exception(self, frame, event, arg):
         if event == 'exception':
-            (flag, frame) = self.shouldStopOnException(frame, event, arg)
+            flag, frame = self.should_stop_on_exception(frame, event, arg)
 
             if flag:
-              self.handle_exception(frame, event, arg)
-              return self.trace_dispatch
+                self.handle_exception(frame, event, arg)
+                return self.trace_dispatch
 
         return self.trace_exception
 
-    def shouldStopOnException(self, frame, event, arg):
-      mainDebugger, filename, info, thread = self._args
-      flag = False
+    def should_stop_on_exception(self, frame, event, arg):
+        mainDebugger, _filename, info, thread = self._args
+        flag = False
 
-      if info.pydev_state != STATE_SUSPEND:  #and breakpoint is not None:
-          (exception, value, trace) = arg
+        if info.pydev_state != STATE_SUSPEND:  #and breakpoint is not None:
+            exception, value, trace = arg
 
-          if trace is not None: #on jython trace is None on the first event
-              exception_breakpoint = get_exception_breakpoint(exception, dict(mainDebugger.exception_set), NOTIFY_ALWAYS)
-              if exception_breakpoint is not None:
-                  if not exception_breakpoint.notify_on_first_raise_only or just_raised(trace):
-                      curr_func_name = frame.f_code.co_name
-                      add_exception_to_frame(frame, (exception, value, trace))
-                      self.setSuspend(thread, CMD_ADD_EXCEPTION_BREAK)
-                      thread.additionalInfo.message = exception_breakpoint.qname
-                      flag = True
-                  else:
-                      flag = False
-              else:
-                  try:
-                      if mainDebugger.django_exception_break and get_exception_name(exception) in ['VariableDoesNotExist', 'TemplateDoesNotExist', 'TemplateSyntaxError'] and just_raised(trace) and is_django_exception_break_context(frame):
-                          render_frame = find_django_render_frame(frame)
-                          if render_frame:
-                              suspend_frame = suspend_django(self, mainDebugger, thread, render_frame, CMD_ADD_DJANGO_EXCEPTION_BREAK)
+            if trace is not None: #on jython trace is None on the first event
+                exception_breakpoint = get_exception_breakpoint(
+                    exception, mainDebugger.break_on_caught_exceptions)
 
-                              if suspend_frame:
-                                  add_exception_to_frame(suspend_frame, (exception, value, trace))
-                                  flag = True
-                                  thread.additionalInfo.message = 'VariableDoesNotExist'
-                                  suspend_frame.f_back = frame
-                                  frame = suspend_frame
-                  except :
-                      flag = False
+                if exception_breakpoint is not None:
+                    if not exception_breakpoint.notify_on_first_raise_only or just_raised(trace):
+                        # print frame.f_code.co_name
+                        add_exception_to_frame(frame, (exception, value, trace))
+                        thread.additionalInfo.message = exception_breakpoint.qname
+                        flag = True
+                    else:
+                        flag = False
+                else:
+                    try:
+                        if mainDebugger.django_exception_break and get_exception_name(exception) in [
+                                'VariableDoesNotExist', 'TemplateDoesNotExist', 'TemplateSyntaxError'] \
+                                and just_raised(trace) and is_django_exception_break_context(frame):
 
-      return (flag, frame)
+                            render_frame = find_django_render_frame(frame)
+                            if render_frame:
+                                suspend_frame = suspend_django(
+                                    self, mainDebugger, thread, render_frame, CMD_ADD_DJANGO_EXCEPTION_BREAK)
+
+                                if suspend_frame:
+                                    add_exception_to_frame(suspend_frame, (exception, value, trace))
+                                    flag = True
+                                    thread.additionalInfo.message = 'VariableDoesNotExist'
+                                    suspend_frame.f_back = frame
+                                    frame = suspend_frame
+                    except :
+                        flag = False
+
+        return flag, frame
 
     def handle_exception(self, frame, event, arg):
-      mainDebugger = self._args[0]
-      thread = self._args[3]
-      self.doWaitSuspend(thread, frame, event, arg)
-      mainDebugger.SetTraceForFrameAndParents(frame)
+        try:
+            # print 'handle_exception', frame.f_lineno, frame.f_code.co_name
+
+            # We have 3 things in arg: exception type, description, traceback object
+            trace_obj = arg[2]
+            mainDebugger = self._args[0]
+
+            if not hasattr(trace_obj, 'tb_next'):
+                return  #Not always there on Jython...
+
+            initial_trace_obj = trace_obj
+            if trace_obj.tb_next is None and trace_obj.tb_frame is frame:
+                #I.e.: tb_next should be only None in the context it was thrown (trace_obj.tb_frame is frame is just a double check).
+
+                if mainDebugger.break_on_exceptions_thrown_in_same_context:
+                    #Option: Don't break if an exception is caught in the same function from which it is thrown
+                    return
+            else:
+                #Get the trace_obj from where the exception was raised...
+                while trace_obj.tb_next is not None:
+                    trace_obj = trace_obj.tb_next
+
+
+            if mainDebugger.ignore_exceptions_thrown_in_lines_with_ignore_exception:
+                for check_trace_obj in (initial_trace_obj, trace_obj):
+                    filename = GetFilenameAndBase(check_trace_obj.tb_frame)[0]
+
+
+                    filename_to_lines_where_exceptions_are_ignored = self.filename_to_lines_where_exceptions_are_ignored
+
+
+                    lines_ignored = filename_to_lines_where_exceptions_are_ignored.get(filename)
+                    if lines_ignored is None:
+                        lines_ignored = filename_to_lines_where_exceptions_are_ignored[filename] = {}
+
+                    try:
+                        curr_stat = os.stat(filename)
+                        curr_stat = (curr_stat.st_size, curr_stat.st_mtime)
+                    except:
+                        curr_stat = None
+
+                    last_stat = self.filename_to_stat_info.get(filename)
+                    if last_stat != curr_stat:
+                        self.filename_to_stat_info[filename] = curr_stat
+                        lines_ignored.clear()
+                        try:
+                            linecache.checkcache(filename)
+                        except:
+                            #Jython 2.1
+                            linecache.checkcache()
+
+                    from_user_input = mainDebugger.filename_to_lines_where_exceptions_are_ignored.get(filename)
+                    if from_user_input:
+                        merged = {}
+                        merged.update(lines_ignored)
+                        #Override what we have with the related entries that the user entered
+                        merged.update(from_user_input)
+                    else:
+                        merged = lines_ignored
+
+                    exc_lineno = check_trace_obj.tb_lineno
+
+                    # print ('lines ignored', lines_ignored)
+                    # print ('user input', from_user_input)
+                    # print ('merged', merged, 'curr', exc_lineno)
+
+                    if not DictContains(merged, exc_lineno):  #Note: check on merged but update lines_ignored.
+                        try:
+                            line = linecache.getline(filename, exc_lineno, check_trace_obj.tb_frame.f_globals)
+                        except:
+                            #Jython 2.1
+                            line = linecache.getline(filename, exc_lineno)
+
+                        if IGNORE_EXCEPTION_TAG.match(line) is not None:
+                            lines_ignored[exc_lineno] = 1
+                            return
+                        else:
+                            #Put in the cache saying not to ignore
+                            lines_ignored[exc_lineno] = 0
+                    else:
+                        #Ok, dict has it already cached, so, let's check it...
+                        if merged.get(exc_lineno, 0):
+                            return
+
+
+            thread = self._args[3]
+
+            try:
+                frame_id_to_frame = {}
+                frame_id_to_frame[id(frame)] = frame
+                f = trace_obj.tb_frame
+                while f is not None:
+                    frame_id_to_frame[id(f)] = f
+                    f = f.f_back
+                f = None
+
+                thread_id = GetThreadId(thread)
+                pydevd_vars.addAdditionalFrameById(thread_id, frame_id_to_frame)
+                try:
+                    mainDebugger.sendCaughtExceptionStack(thread, arg, id(frame))
+                    self.setSuspend(thread, CMD_STEP_CAUGHT_EXCEPTION)
+                    self.doWaitSuspend(thread, frame, event, arg)
+                    mainDebugger.sendCaughtExceptionStackProceeded(thread)
+
+                finally:
+                    pydevd_vars.removeAdditionalFrameById(thread_id)
+            except:
+                traceback.print_exc()
+
+            mainDebugger.SetTraceForFrameAndParents(frame)
+        finally:
+            #Clear some local variables...
+            trace_obj = None
+            initial_trace_obj = None
+            check_trace_obj = None
+            f = None
+            frame_id_to_frame = None
+            mainDebugger = None
+            thread = None
 
     def trace_dispatch(self, frame, event, arg):
-        mainDebugger, filename, info, thread = self._args
+        main_debugger, filename, info, thread = self._args
         try:
             info.is_tracing = True
 
-            if mainDebugger._finishDebuggingSession:
+            if main_debugger._finishDebuggingSession:
                 return None
 
             if getattr(thread, 'pydev_do_not_trace', None):
                 return None
 
-            if event == 'call':
-                sendSignatureCallTrace(mainDebugger, frame, filename)
+            if event == 'call' and main_debugger.signature_factory:
+                sendSignatureCallTrace(main_debugger, frame, filename)
 
-            if event not in ('line', 'call', 'return'):
-                if event == 'exception':
-                    (flag, frame) = self.shouldStopOnException(frame, event, arg)
+            is_exception_event = event == 'exception'
+            has_exception_breakpoints = main_debugger.break_on_caught_exceptions or main_debugger.django_exception_break
+
+            if is_exception_event:
+                if has_exception_breakpoints:
+                    flag, frame = self.should_stop_on_exception(frame, event, arg)
                     if flag:
                         self.handle_exception(frame, event, arg)
                         return self.trace_dispatch
-                else:
-                #I believe this can only happen in jython on some frontiers on jython and java code, which we don't want to trace.
-                    return None
 
-            if event is not 'exception':
-                breakpoints_for_file = mainDebugger.breakpoints.get(filename)
+            elif event not in ('line', 'call', 'return'):
+                #I believe this can only happen in jython on some frontiers on jython and java code, which we don't want to trace.
+                return None
+
+            stop_frame = info.pydev_step_stop
+            step_cmd = info.pydev_step_cmd
+
+            if is_exception_event:
+                breakpoints_for_file = None
+            else:
+                # If we are in single step mode and something causes us to exit the current frame, we need to make sure we break
+                # eventually.  Force the step mode to step into and the step stop frame to None.
+                # I.e.: F6 in the end of a function should stop in the next possible position (instead of forcing the user
+                # to make a step in or step over at that location).
+                # Note: this is especially troublesome when we're skipping code with the
+                # @DontTrace comment.
+                if stop_frame is frame and event in ('return', 'exception') and step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER):
+                    info.pydev_step_cmd = CMD_STEP_INTO
+                    info.pydev_step_stop = None
+
+                breakpoints_for_file = main_debugger.breakpoints.get(filename)
 
                 can_skip = False
 
@@ -118,10 +286,11 @@
                     #we can skip if:
                     #- we have no stop marked
                     #- we should make a step return/step over and we're not in the current frame
-                    can_skip = (info.pydev_step_cmd is None and info.pydev_step_stop is None)\
-                    or (info.pydev_step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER) and info.pydev_step_stop is not frame)
+                    can_skip = (step_cmd is None and stop_frame is None)\
+                        or (step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER) and stop_frame is not frame)
 
-                if  mainDebugger.django_breakpoints:
+                check_stop_on_django_render_call = main_debugger.django_breakpoints and self._is_django_render_call(frame)
+                if check_stop_on_django_render_call:
                     can_skip = False
 
                 # Let's check to see if we are in a function that has a breakpoint. If we don't have a breakpoint,
@@ -130,7 +299,7 @@
                 #so, that's why the additional checks are there.
                 if not breakpoints_for_file:
                     if can_skip:
-                        if mainDebugger.always_exception_set or mainDebugger.django_exception_break:
+                        if has_exception_breakpoints:
                             return self.trace_exception
                         else:
                             return None
@@ -143,17 +312,18 @@
                     if curr_func_name in ('?', '<module>'):
                         curr_func_name = ''
 
-                    for breakpoint in breakpoints_for_file.values(): #jython does not support itervalues()
+                    for breakpoint in DictIterValues(breakpoints_for_file): #jython does not support itervalues()
                         #will match either global or some function
                         if breakpoint.func_name in ('None', curr_func_name):
                             break
 
                     else: # if we had some break, it won't get here (so, that's a context that we want to skip)
                         if can_skip:
-                            #print 'skipping', frame.f_lineno, info.pydev_state, info.pydev_step_stop, info.pydev_step_cmd
-                            return None
-            else:
-                breakpoints_for_file = None
+                            if has_exception_breakpoints:
+                                return self.trace_exception
+                            else:
+                                return None
+
 
             #We may have hit a breakpoint or we are already in step mode. Either way, let's check what we should do in this frame
             #print 'NOT skipped', frame.f_lineno, frame.f_code.co_name, event
@@ -163,33 +333,63 @@
 
 
                 flag = False
-                if event == 'call' and info.pydev_state != STATE_SUSPEND and mainDebugger.django_breakpoints \
-                and is_django_render_call(frame):
-                    (flag, frame) = self.shouldStopOnDjangoBreak(frame, event, arg)
+                if event == 'call' and info.pydev_state != STATE_SUSPEND and check_stop_on_django_render_call:
+                    flag, frame = self.should_stop_on_django_breakpoint(frame, event, arg)
 
                 #return is not taken into account for breakpoint hit because we'd have a double-hit in this case
                 #(one for the line and the other for the return).
 
                 if not flag and event != 'return' and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None\
-                and DictContains(breakpoints_for_file, line):
+                    and DictContains(breakpoints_for_file, line):
                     #ok, hit breakpoint, now, we have to discover if it is a conditional breakpoint
                     # lets do the conditional stuff here
                     breakpoint = breakpoints_for_file[line]
 
                     stop = True
-                    if info.pydev_step_cmd == CMD_STEP_OVER and info.pydev_step_stop is frame and event in ('line', 'return'):
+                    if step_cmd == CMD_STEP_OVER and stop_frame is frame and event in ('line', 'return'):
                         stop = False #we don't stop on breakpoint if we have to stop by step-over (it will be processed later)
                     else:
-                        if breakpoint.condition is not None:
+                        condition = breakpoint.condition
+                        if condition is not None:
                             try:
-                                val = eval(breakpoint.condition, frame.f_globals, frame.f_locals)
+                                val = eval(condition, frame.f_globals, frame.f_locals)
                                 if not val:
                                     return self.trace_dispatch
 
                             except:
-                                pydev_log.info('Error while evaluating condition \'%s\': %s\n' % (breakpoint.condition, sys.exc_info()[1]))
+                                if type(condition) != type(''):
+                                    if hasattr(condition, 'encode'):
+                                        condition = condition.encode('utf-8')
 
-                                return self.trace_dispatch
+                                msg = 'Error while evaluating expression: %s\n' % (condition,)
+                                sys.stderr.write(msg)
+                                traceback.print_exc()
+                                if not main_debugger.suspend_on_breakpoint_exception:
+                                    return self.trace_dispatch
+                                else:
+                                    stop = True
+                                    try:
+                                        additional_info = None
+                                        try:
+                                            additional_info = thread.additionalInfo
+                                        except AttributeError:
+                                            pass  #that's ok, no info currently set
+
+                                        if additional_info is not None:
+                                            # add exception_type and stacktrace into thread additional info
+                                            etype, value, tb = sys.exc_info()
+                                            try:
+                                                error = ''.join(traceback.format_exception_only(etype, value))
+                                                stack = traceback.extract_stack(f=tb.tb_frame.f_back)
+
+                                                # On self.setSuspend(thread, CMD_SET_BREAK) this info will be
+                                                # sent to the client.
+                                                additional_info.conditional_breakpoint_exception = \
+                                                    ('Condition:\n' + condition + '\n\nError:\n' + error, stack)
+                                            finally:
+                                                etype, value, tb = None, None, None
+                                    except:
+                                        traceback.print_exc()
 
                     if breakpoint.expression is not None:
                         try:
@@ -216,30 +416,45 @@
             #step handling. We stop when we hit the right frame
             try:
                 django_stop = False
-                if info.pydev_step_cmd == CMD_STEP_INTO:
+
+                should_skip = False
+                if pydevd_dont_trace.should_trace_hook is not None:
+                    if not hasattr(self, 'should_skip'):
+                        # I.e.: cache the result on self.should_skip (no need to evaluate the same frame multiple times).
+                        # Note that on a code reload, we won't re-evaluate this because in practice, the frame.f_code
+                        # Which will be handled by this frame is read-only, so, we can cache it safely.
+                        should_skip = self.should_skip = not pydevd_dont_trace.should_trace_hook(frame, filename)
+                    else:
+                        should_skip = self.should_skip
+
+                if should_skip:
+                    stop = False
+
+                elif step_cmd == CMD_STEP_INTO:
                     stop = event in ('line', 'return')
+
                     if is_django_suspended(thread):
                         #django_stop = event == 'call' and is_django_render_call(frame)
                         stop = stop and is_django_resolve_call(frame.f_back) and not is_django_context_get_call(frame)
                         if stop:
                             info.pydev_django_resolve_frame = 1 #we remember that we've go into python code from django rendering frame
 
-                elif info.pydev_step_cmd == CMD_STEP_OVER:
+                elif step_cmd == CMD_STEP_OVER:
                     if is_django_suspended(thread):
-                        django_stop = event == 'call' and is_django_render_call(frame)
+                        django_stop = event == 'call' and self._is_django_render_call(frame)
 
                         stop = False
                     else:
                         if event == 'return' and info.pydev_django_resolve_frame is not None and is_django_resolve_call(frame.f_back):
                             #we return to Django suspend mode and should not stop before django rendering frame
-                            info.pydev_step_stop = info.pydev_django_resolve_frame
+                            stop_frame = info.pydev_step_stop = info.pydev_django_resolve_frame
                             info.pydev_django_resolve_frame = None
                             thread.additionalInfo.suspend_type = DJANGO_SUSPEND
 
 
-                        stop = info.pydev_step_stop is frame and event in ('line', 'return')
+                        stop = stop_frame is frame and event in ('line', 'return')
 
-                elif info.pydev_step_cmd == CMD_SMART_STEP_INTO:
+                elif step_cmd == CMD_SMART_STEP_INTO:
                     stop = False
                     if info.pydev_smart_step_stop is frame:
                         info.pydev_func_name = None
@@ -253,12 +468,12 @@
                             curr_func_name = ''
 
                         if curr_func_name == info.pydev_func_name:
-                                stop = True
+                            stop = True
 
-                elif info.pydev_step_cmd == CMD_STEP_RETURN:
-                    stop = event == 'return' and info.pydev_step_stop is frame
+                elif step_cmd == CMD_STEP_RETURN:
+                    stop = event == 'return' and stop_frame is frame
 
-                elif info.pydev_step_cmd == CMD_RUN_TO_LINE or info.pydev_step_cmd == CMD_SET_NEXT_STATEMENT:
+                elif step_cmd == CMD_RUN_TO_LINE or step_cmd == CMD_SET_NEXT_STATEMENT:
                     stop = False
 
                     if event == 'line' or event == 'exception':
@@ -286,13 +501,13 @@
                     stop = False
 
                 if django_stop:
-                    frame = suspend_django(self, mainDebugger, thread, frame)
+                    frame = suspend_django(self, main_debugger, thread, frame)
                     if frame:
                         self.doWaitSuspend(thread, frame, event, arg)
                 elif stop:
                     #event is always == line or return at this point
                     if event == 'line':
-                        self.setSuspend(thread, info.pydev_step_cmd)
+                        self.setSuspend(thread, step_cmd)
                         self.doWaitSuspend(thread, frame, event, arg)
                     else: #return event
                         back = frame.f_back
@@ -300,12 +515,18 @@
                             #When we get to the pydevd run function, the debugging has actually finished for the main thread
                             #(note that it can still go on for other threads, but for this one, we just make it finish)
                             #So, just setting it to None should be OK
-                            if basename(back.f_code.co_filename) == 'pydevd.py' and back.f_code.co_name == 'run':
+                            base = basename(back.f_code.co_filename)
+                            if base == 'pydevd.py' and back.f_code.co_name == 'run':
                                 back = None
 
+                            elif base == 'pydevd_traceproperty.py':
+                                # We dont want to trace the return event of pydevd_traceproperty (custom property for debugging)
+                                #if we're in a return, we want it to appear to the user in the previous frame!
+                                return None
+
                         if back is not None:
                             #if we're in a return, we want it to appear to the user in the previous frame!
-                            self.setSuspend(thread, info.pydev_step_cmd)
+                            self.setSuspend(thread, step_cmd)
                             self.doWaitSuspend(thread, back, event, arg)
                         else:
                             #in jython we may not have a back frame
@@ -320,7 +541,7 @@
 
             #if we are quitting, let's stop the tracing
             retVal = None
-            if not mainDebugger.quitting:
+            if not main_debugger.quitting:
                 retVal = self.trace_dispatch
 
             return retVal
@@ -339,24 +560,36 @@
                 sys.exc_clear() #don't keep the traceback
             pass #ok, psyco not available
 
-    def shouldStopOnDjangoBreak(self, frame, event, arg):
-        mainDebugger, filename, info, thread = self._args
+    def should_stop_on_django_breakpoint(self, frame, event, arg):
+        mainDebugger = self._args[0]
+        thread = self._args[3]
         flag = False
-        filename = get_template_file_name(frame)
-        pydev_log.debug("Django is rendering a template: %s\n" % filename)
-        django_breakpoints_for_file = mainDebugger.django_breakpoints.get(filename)
+        template_frame_file = get_template_file_name(frame)
+
+        #pydev_log.debug("Django is rendering a template: %s\n" % template_frame_file)
+
+        django_breakpoints_for_file = mainDebugger.django_breakpoints.get(template_frame_file)
         if django_breakpoints_for_file:
-            pydev_log.debug("Breakpoints for that file: %s\n" % django_breakpoints_for_file)
-            template_line = get_template_line(frame)
-            pydev_log.debug("Tracing template line: %d\n" % template_line)
 
-            if DictContains(django_breakpoints_for_file, template_line):
-                django_breakpoint = django_breakpoints_for_file[template_line]
+            #pydev_log.debug("Breakpoints for that file: %s\n" % django_breakpoints_for_file)
 
-                if django_breakpoint.is_triggered(frame):
-                    pydev_log.debug("Breakpoint is triggered.\n")
+            template_frame_line = get_template_line(frame, template_frame_file)
+
+            #pydev_log.debug("Tracing template line: %d\n" % template_frame_line)
+
+            if DictContains(django_breakpoints_for_file, template_frame_line):
+                django_breakpoint = django_breakpoints_for_file[template_frame_line]
+
+                if django_breakpoint.is_triggered(template_frame_file, template_frame_line):
+
+                    #pydev_log.debug("Breakpoint is triggered.\n")
+
                     flag = True
-                    new_frame = DjangoTemplateFrame(frame)
+                    new_frame = DjangoTemplateFrame(
+                        frame,
+                        template_frame_file=template_frame_file,
+                        template_frame_line=template_frame_line,
+                    )
 
                     if django_breakpoint.condition is not None:
                         try:
@@ -379,7 +612,7 @@
                                 thread.additionalInfo.message = val
                     if flag:
                         frame = suspend_django(self, mainDebugger, thread, frame)
-        return (flag, frame)
+        return flag, frame
 
 def add_exception_to_frame(frame, exception_info):
     frame.f_locals['__exception__'] = exception_info
\ No newline at end of file
diff --git a/python/helpers/pydev/pydevd_io.py b/python/helpers/pydev/pydevd_io.py
index a83adc8..2e74154 100644
--- a/python/helpers/pydev/pydevd_io.py
+++ b/python/helpers/pydev/pydevd_io.py
@@ -24,10 +24,10 @@
             r.flush()
 
     def __getattr__(self, name):
-            for r in self._redirectTo:
-                if hasattr(r, name):
-                    return r.__getattribute__(name)
-            raise AttributeError(name)
+        for r in self._redirectTo:
+            if hasattr(r, name):
+                return r.__getattribute__(name)
+        raise AttributeError(name)
 
 class IOBuf:
     '''This class works as a replacement for stdio and stderr.
diff --git a/python/helpers/pydev/pydevd_referrers.py b/python/helpers/pydev/pydevd_referrers.py
new file mode 100644
index 0000000..66b1a0e
--- /dev/null
+++ b/python/helpers/pydev/pydevd_referrers.py
@@ -0,0 +1,238 @@
+from pydevd_constants import DictContains
+import sys
+import pydevd_vars
+from os.path import basename
+import traceback
+try:
+    from urllib import quote, quote_plus, unquote, unquote_plus
+except:
+    from urllib.parse import quote, quote_plus, unquote, unquote_plus  #@Reimport @UnresolvedImport
+
+#===================================================================================================
+# print_var_node
+#===================================================================================================
+def print_var_node(xml_node, stream):
+    name = xml_node.getAttribute('name')
+    value = xml_node.getAttribute('value')
+    val_type = xml_node.getAttribute('type')
+
+    found_as = xml_node.getAttribute('found_as')
+    stream.write('Name: ')
+    stream.write(unquote_plus(name))
+    stream.write(', Value: ')
+    stream.write(unquote_plus(value))
+    stream.write(', Type: ')
+    stream.write(unquote_plus(val_type))
+    if found_as:
+        stream.write(', Found as: %s' % (unquote_plus(found_as),))
+    stream.write('\n')
+
+#===================================================================================================
+# print_referrers
+#===================================================================================================
+def print_referrers(obj, stream=None):
+    if stream is None:
+        stream = sys.stdout
+    result = get_referrer_info(obj)
+    from xml.dom.minidom import parseString
+    dom = parseString(result)
+
+    xml = dom.getElementsByTagName('xml')[0]
+    for node in xml.childNodes:
+        if node.nodeType == node.TEXT_NODE:
+            continue
+
+        if node.localName == 'for':
+            stream.write('Searching references for: ')
+            for child in node.childNodes:
+                if child.nodeType == node.TEXT_NODE:
+                    continue
+                print_var_node(child, stream)
+
+        elif node.localName == 'var':
+            stream.write('Referrer found: ')
+            print_var_node(node, stream)
+
+        else:
+            sys.stderr.write('Unhandled node: %s\n' % (node,))
+
+    return result
+
+
+#===================================================================================================
+# get_referrer_info
+#===================================================================================================
+def get_referrer_info(searched_obj):
+    DEBUG = 0
+    if DEBUG:
+        sys.stderr.write('Getting referrers info.\n')
+    try:
+        try:
+            if searched_obj is None:
+                ret = ['<xml>\n']
+
+                ret.append('<for>\n')
+                ret.append(pydevd_vars.varToXML(
+                    searched_obj,
+                    'Skipping getting referrers for None',
+                    additionalInXml=' id="%s"' % (id(searched_obj),)))
+                ret.append('</for>\n')
+                ret.append('</xml>')
+                ret = ''.join(ret)
+                return ret
+
+            obj_id = id(searched_obj)
+
+            try:
+                if DEBUG:
+                    sys.stderr.write('Getting referrers...\n')
+                import gc
+                referrers = gc.get_referrers(searched_obj)
+            except:
+                traceback.print_exc()
+                ret = ['<xml>\n']
+
+                ret.append('<for>\n')
+                ret.append(pydevd_vars.varToXML(
+                    searched_obj,
+                    'Exception raised while trying to get_referrers.',
+                    additionalInXml=' id="%s"' % (id(searched_obj),)))
+                ret.append('</for>\n')
+                ret.append('</xml>')
+                ret = ''.join(ret)
+                return ret
+
+            if DEBUG:
+                sys.stderr.write('Found %s referrers.\n' % (len(referrers),))
+
+            curr_frame = sys._getframe()
+            frame_type = type(curr_frame)
+
+            #Ignore this frame and any caller frame of this frame
+
+            ignore_frames = {}  #Should be a set, but it's not available on all python versions.
+            while curr_frame is not None:
+                if basename(curr_frame.f_code.co_filename).startswith('pydev'):
+                    ignore_frames[curr_frame] = 1
+                curr_frame = curr_frame.f_back
+
+
+            ret = ['<xml>\n']
+
+            ret.append('<for>\n')
+            if DEBUG:
+                sys.stderr.write('Searching Referrers of obj with id="%s"\n' % (obj_id,))
+
+            ret.append(pydevd_vars.varToXML(
+                searched_obj,
+                'Referrers of obj with id="%s"' % (obj_id,)))
+            ret.append('</for>\n')
+
+            all_objects = None
+
+            for r in referrers:
+                try:
+                    if DictContains(ignore_frames, r):
+                        continue  #Skip the references we may add ourselves
+                except:
+                    pass  #Ok: unhashable type checked...
+
+                if r is referrers:
+                    continue
+
+                r_type = type(r)
+                r_id = str(id(r))
+
+                representation = str(r_type)
+
+                found_as = ''
+                if r_type == frame_type:
+                    if DEBUG:
+                        sys.stderr.write('Found frame referrer: %r\n' % (r,))
+                    for key, val in r.f_locals.items():
+                        if val is searched_obj:
+                            found_as = key
+                            break
+
+                elif r_type == dict:
+                    if DEBUG:
+                        sys.stderr.write('Found dict referrer: %r\n' % (r,))
+
+                    # Try to check if it's a value in the dict (and under which key it was found)
+                    for key, val in r.items():
+                        if val is searched_obj:
+                            found_as = key
+                            if DEBUG:
+                                sys.stderr.write('    Found as %r in dict\n' % (found_as,))
+                            break
+
+                    #Ok, there's one annoying thing: many times we find it in a dict from an instance,
+                    #but with this we don't directly have the class, only the dict, so, to workaround that
+                    #we iterate over all reachable objects ad check if one of those has the given dict.
+                    if all_objects is None:
+                        all_objects = gc.get_objects()
+
+                    for x in all_objects:
+                        try:
+                            if getattr(x, '__dict__', None) is r:
+                                r = x
+                                r_type = type(x)
+                                r_id = str(id(r))
+                                representation = str(r_type)
+                                break
+                        except:
+                            pass  #Just ignore any error here (i.e.: ReferenceError, etc.)
+
+                elif r_type in (tuple, list):
+                    if DEBUG:
+                        sys.stderr.write('Found tuple referrer: %r\n' % (r,))
+
+                    #Don't use enumerate() because not all Python versions have it.
+                    i = 0
+                    for x in r:
+                        if x is searched_obj:
+                            found_as = '%s[%s]' % (r_type.__name__, i)
+                            if DEBUG:
+                                sys.stderr.write('    Found as %s in tuple: \n' % (found_as,))
+                            break
+                        i += 1
+
+                if found_as:
+                    found_as = ' found_as="%s"' % (pydevd_vars.makeValidXmlValue(found_as),)
+
+                ret.append(pydevd_vars.varToXML(
+                    r,
+                    representation,
+                    additionalInXml=' id="%s"%s' % (r_id, found_as)))
+        finally:
+            if DEBUG:
+                sys.stderr.write('Done searching for references.\n')
+
+            #If we have any exceptions, don't keep dangling references from this frame to any of our objects.
+            all_objects = None
+            referrers = None
+            searched_obj = None
+            r = None
+            x = None
+            key = None
+            val = None
+            curr_frame = None
+            ignore_frames = None
+    except:
+        traceback.print_exc()
+        ret = ['<xml>\n']
+
+        ret.append('<for>\n')
+        ret.append(pydevd_vars.varToXML(
+            searched_obj,
+            'Error getting referrers for:',
+            additionalInXml=' id="%s"' % (id(searched_obj),)))
+        ret.append('</for>\n')
+        ret.append('</xml>')
+        ret = ''.join(ret)
+        return ret
+
+    ret.append('</xml>')
+    ret = ''.join(ret)
+    return ret
+
diff --git a/python/helpers/pydev/pydevd_resolver.py b/python/helpers/pydev/pydevd_resolver.py
index 614549f..3fe895c 100644
--- a/python/helpers/pydev/pydevd_resolver.py
+++ b/python/helpers/pydev/pydevd_resolver.py
@@ -13,6 +13,7 @@
     setattr(__builtin__, 'False', 0)
 
 import pydevd_constants
+from pydevd_constants import DictIterItems, xrange, izip
 
 
 MAX_ITEMS_TO_HANDLE = 500
@@ -58,7 +59,7 @@
 class AbstractResolver:
     '''
         This class exists only for documentation purposes to explain how to create a resolver.
-        
+
         Some examples on how to resolve things:
         - list: getDictionary could return a dict with index->item and use the index to resolve it later
         - set: getDictionary could return a dict with id(object)->object and reiterate in that array to resolve it later
@@ -69,7 +70,7 @@
         '''
             In this method, we'll resolve some child item given the string representation of the item in the key
             representing the previously asked dictionary.
-            
+
             @param var: this is the actual variable to be resolved.
             @param attribute: this is the string representation of a key previously returned in getDictionary.
         '''
@@ -78,7 +79,7 @@
     def getDictionary(self, var):
         '''
             @param var: this is the variable that should have its children gotten.
-            
+
             @return: a dictionary where each pair key, value should be shown to the user as children items
             in the variables view for the given var.
         '''
@@ -128,12 +129,12 @@
 
                 declaredMethods = obj.getDeclaredMethods()
                 declaredFields = obj.getDeclaredFields()
-                for i in range(len(declaredMethods)):
+                for i in xrange(len(declaredMethods)):
                     name = declaredMethods[i].getName()
                     ret[name] = declaredMethods[i].toString()
                     found.put(name, 1)
 
-                for i in range(len(declaredFields)):
+                for i in xrange(len(declaredFields)):
                     name = declaredFields[i].getName()
                     found.put(name, 1)
                     #if declaredFields[i].isAccessible():
@@ -145,7 +146,7 @@
                         ret[name] = declaredFields[i].toString()
 
         #this simple dir does not always get all the info, that's why we have the part before
-        #(e.g.: if we do a dir on String, some methods that are from other interfaces such as 
+        #(e.g.: if we do a dir on String, some methods that are from other interfaces such as
         #charAt don't appear)
         try:
             d = dir(original)
@@ -169,8 +170,8 @@
             names = var.__members__
         d = {}
 
-        #Be aware that the order in which the filters are applied attempts to 
-        #optimize the operation by removing as many items as possible in the 
+        #Be aware that the order in which the filters are applied attempts to
+        #optimize the operation by removing as many items as possible in the
         #first filters, leaving fewer items for later filters
 
         if filterBuiltIn or filterFunction:
@@ -212,18 +213,18 @@
 class DictResolver:
 
     def resolve(self, dict, key):
-        if key == '__len__':
+        if key in ('__len__', TOO_LARGE_ATTR):
             return None
 
         if '(' not in key:
             #we have to treat that because the dict resolver is also used to directly resolve the global and local
-            #scopes (which already have the items directly) 
+            #scopes (which already have the items directly)
             return dict[key]
 
         #ok, we have to iterate over the items to find the one that matches the id, because that's the only way
         #to actually find the reference from the string we have before.
         expected_id = int(key.split('(')[-1][:-1])
-        for key, val in dict.items():
+        for key, val in DictIterItems(dict):
             if id(key) == expected_id:
                 return val
 
@@ -241,10 +242,15 @@
     def getDictionary(self, dict):
         ret = {}
 
-        for key, val in dict.items():
+        i = 0
+        for key, val in DictIterItems(dict):
+            i += 1
             #we need to add the id because otherwise we cannot find the real object to get its contents later on.
             key = '%s (%s)' % (self.keyStr(key), id(key))
             ret[key] = val
+            if i > MAX_ITEMS_TO_HANDLE:
+                ret[TOO_LARGE_ATTR] = TOO_LARGE_MSG
+                break
 
         ret['__len__'] = len(dict)
         return ret
@@ -261,7 +267,7 @@
             @param var: that's the original attribute
             @param attribute: that's the key passed in the dict (as a string)
         '''
-        if attribute == '__len__' or attribute == TOO_LARGE_ATTR:
+        if attribute in ('__len__', TOO_LARGE_ATTR):
             return None
         return var[int(attribute)]
 
@@ -270,12 +276,12 @@
         # modified 'cause jython does not have enumerate support
         l = len(var)
         d = {}
-        
+
         if l < MAX_ITEMS_TO_HANDLE:
             format = '%0' + str(int(len(str(l)))) + 'd'
-            
-            
-            for i, item in zip(range(l), var):
+
+
+            for i, item in izip(xrange(l), var):
                 d[ format % i ] = item
         else:
             d[TOO_LARGE_ATTR] = TOO_LARGE_MSG
@@ -293,7 +299,7 @@
     '''
 
     def resolve(self, var, attribute):
-        if attribute == '__len__':
+        if attribute in ('__len__', TOO_LARGE_ATTR):
             return None
 
         attribute = int(attribute)
@@ -305,8 +311,16 @@
 
     def getDictionary(self, var):
         d = {}
+        i = 0
         for item in var:
-            d[ id(item) ] = item
+            i+= 1
+            d[id(item)] = item
+            
+            if i > MAX_ITEMS_TO_HANDLE:
+                d[TOO_LARGE_ATTR] = TOO_LARGE_MSG
+                break
+
+            
         d['__len__'] = len(var)
         return d
 
@@ -325,7 +339,7 @@
         ret = {}
 
         declaredFields = obj.__class__.getDeclaredFields()
-        for i in range(len(declaredFields)):
+        for i in xrange(len(declaredFields)):
             name = declaredFields[i].getName()
             try:
                 declaredFields[i].setAccessible(True)
@@ -352,7 +366,7 @@
     def getDictionary(self, obj):
         ret = {}
 
-        for i in range(len(obj)):
+        for i in xrange(len(obj)):
             ret[ i ] = obj[i]
 
         ret['__len__'] = len(obj)
diff --git a/python/helpers/pydev/pydevd_save_locals.py b/python/helpers/pydev/pydevd_save_locals.py
index 2808081..15a7382 100644
--- a/python/helpers/pydev/pydevd_save_locals.py
+++ b/python/helpers/pydev/pydevd_save_locals.py
@@ -2,6 +2,7 @@
 Utility for saving locals.
 """
 import sys
+import pydevd_vars
 
 def is_save_locals_available():
     try:
@@ -12,7 +13,7 @@
     except:
         pass
 
-    
+
     try:
         import ctypes
     except:
@@ -22,7 +23,7 @@
         func = ctypes.pythonapi.PyFrame_LocalsToFast
     except:
         return False
-    
+
     return True
 
 def save_locals(frame):
@@ -32,6 +33,10 @@
     Note: the 'save_locals' branch had a different approach wrapping the frame (much more code, but it gives ideas
     on how to save things partially, not the 'whole' locals).
     """
+    if not isinstance(frame, pydevd_vars.frame_type):
+        # Fix exception when changing Django variable (receiving DjangoTemplateFrame)
+        return
+
     try:
         if '__pypy__' in sys.builtin_module_names:
             import __pypy__
@@ -40,7 +45,7 @@
             return
     except:
         pass
-    
+
 
     try:
         import ctypes
diff --git a/python/helpers/pydev/pydevd_signature.py b/python/helpers/pydev/pydevd_signature.py
index e11bb5d..03dc0eb 100644
--- a/python/helpers/pydev/pydevd_signature.py
+++ b/python/helpers/pydev/pydevd_signature.py
@@ -6,6 +6,7 @@
 import gc
 from pydevd_comm import CMD_SIGNATURE_CALL_TRACE, NetCommand
 import pydevd_vars
+from pydevd_constants import xrange
 
 class Signature(object):
     def __init__(self, file, name):
@@ -43,7 +44,7 @@
             locals = frame.f_locals
             filename, modulename, funcname = self.file_module_function_of(frame)
             res = Signature(filename, funcname)
-            for i in range(0, code.co_argcount):
+            for i in xrange(0, code.co_argcount):
                 name = code.co_varnames[i]
                 tp = type(locals[name])
                 class_name = tp.__name__
@@ -123,9 +124,8 @@
     return NetCommand(CMD_SIGNATURE_CALL_TRACE, 0, cmdText)
 
 def sendSignatureCallTrace(dbg, frame, filename):
-    if dbg.signature_factory:
-        if dbg.signature_factory.is_in_scope(filename):
-            dbg.writer.addCommand(create_signature_message(dbg.signature_factory.create_signature(frame)))
+    if dbg.signature_factory.is_in_scope(filename):
+        dbg.writer.addCommand(create_signature_message(dbg.signature_factory.create_signature(frame)))
 
 
 
diff --git a/python/helpers/pydev/pydevd_stackless.py b/python/helpers/pydev/pydevd_stackless.py
index bd3b306..c2fd508 100644
--- a/python/helpers/pydev/pydevd_stackless.py
+++ b/python/helpers/pydev/pydevd_stackless.py
@@ -7,6 +7,7 @@
 import weakref
 from pydevd_file_utils import GetFilenameAndBase
 from pydevd import DONT_TRACE
+from pydevd_constants import DictItems
 
 
 # Used so that we don't loose the id (because we'll remove when it's not alive and would generate a new id for the
@@ -195,7 +196,7 @@
             register_tasklet_info(prev)
 
         try:
-            for tasklet_ref, tasklet_info in list(_weak_tasklet_registered_to_info.items()):  # Make sure it's a copy!
+            for tasklet_ref, tasklet_info in DictItems(_weak_tasklet_registered_to_info):  # Make sure it's a copy!
                 tasklet = tasklet_ref()
                 if tasklet is None or not tasklet.alive:
                     # Garbage-collected already!
@@ -269,7 +270,7 @@
                 register_tasklet_info(prev)
 
             try:
-                for tasklet_ref, tasklet_info in list(_weak_tasklet_registered_to_info.items()):  # Make sure it's a copy!
+                for tasklet_ref, tasklet_info in DictItems(_weak_tasklet_registered_to_info):  # Make sure it's a copy!
                     tasklet = tasklet_ref()
                     if tasklet is None or not tasklet.alive:
                         # Garbage-collected already!
@@ -388,7 +389,7 @@
         _application_set_schedule_callback = callable
         return old
 
-    def get_schedule_callback(callable):
+    def get_schedule_callback():
         global _application_set_schedule_callback
         return _application_set_schedule_callback
 
diff --git a/python/helpers/pydev/pydevd_traceproperty.py b/python/helpers/pydev/pydevd_traceproperty.py
new file mode 100644
index 0000000..d8e7e5f
--- /dev/null
+++ b/python/helpers/pydev/pydevd_traceproperty.py
@@ -0,0 +1,108 @@
+'''For debug purpose we are replacing actual builtin property by the debug property
+'''
+from pydevd_comm import GetGlobalDebugger
+from pydevd_constants import * #@UnusedWildImport
+import pydevd_tracing
+
+#=======================================================================================================================
+# replace_builtin_property
+#=======================================================================================================================
+def replace_builtin_property(new_property=None):
+    if new_property is None:
+        new_property = DebugProperty
+    original = property
+    if not IS_PY3K:
+        try:
+            import __builtin__
+            __builtin__.__dict__['property'] = new_property
+        except:
+            if DebugInfoHolder.DEBUG_TRACE_LEVEL:
+                import traceback;traceback.print_exc() #@Reimport
+    else:
+        try:
+            import builtins #Python 3.0 does not have the __builtin__ module @UnresolvedImport
+            builtins.__dict__['property'] = new_property
+        except:
+            if DebugInfoHolder.DEBUG_TRACE_LEVEL:
+                import traceback;traceback.print_exc() #@Reimport
+    return original
+
+
+#=======================================================================================================================
+# DebugProperty
+#=======================================================================================================================
+class DebugProperty(object):
+    """A custom property which allows python property to get
+    controlled by the debugger and selectively disable/re-enable
+    the tracing.
+    """
+
+
+    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
+        self.fget = fget
+        self.fset = fset
+        self.fdel = fdel
+        self.__doc__ = doc
+
+
+    def __get__(self, obj, objtype=None):
+        if obj is None:
+            return self
+        global_debugger = GetGlobalDebugger()
+        try:
+            if global_debugger is not None and global_debugger.disable_property_getter_trace:
+                pydevd_tracing.SetTrace(None)
+            if self.fget is None:
+                raise AttributeError("unreadable attribute")
+            return self.fget(obj)
+        finally:
+            if global_debugger is not None:
+                pydevd_tracing.SetTrace(global_debugger.trace_dispatch)
+
+
+    def __set__(self, obj, value):
+        global_debugger = GetGlobalDebugger()
+        try:
+            if global_debugger is not None and global_debugger.disable_property_setter_trace:
+                pydevd_tracing.SetTrace(None)
+            if self.fset is None:
+                raise AttributeError("can't set attribute")
+            self.fset(obj, value)
+        finally:
+            if global_debugger is not None:
+                pydevd_tracing.SetTrace(global_debugger.trace_dispatch)
+
+
+    def __delete__(self, obj):
+        global_debugger = GetGlobalDebugger()
+        try:
+            if global_debugger is not None and global_debugger.disable_property_deleter_trace:
+                pydevd_tracing.SetTrace(None)
+            if self.fdel is None:
+                raise AttributeError("can't delete attribute")
+            self.fdel(obj)
+        finally:
+            if global_debugger is not None:
+                pydevd_tracing.SetTrace(global_debugger.trace_dispatch)
+
+
+    def getter(self, fget):
+        """Overriding getter decorator for the property
+        """
+        self.fget = fget
+        return self
+
+
+    def setter(self, fset):
+        """Overriding setter decorator for the property
+        """
+        self.fset = fset
+        return self
+
+
+    def deleter(self, fdel):
+        """Overriding deleter decorator for the property
+        """
+        self.fdel = fdel
+        return self
+
diff --git a/python/helpers/pydev/pydevd_tracing.py b/python/helpers/pydev/pydevd_tracing.py
index 1a5a833..7bc1ba5 100644
--- a/python/helpers/pydev/pydevd_tracing.py
+++ b/python/helpers/pydev/pydevd_tracing.py
@@ -65,7 +65,7 @@
                     sys.stderr.flush()
 
     if TracingFunctionHolder._original_tracing:
-            TracingFunctionHolder._original_tracing(tracing_func)
+        TracingFunctionHolder._original_tracing(tracing_func)
 
 def SetTrace(tracing_func):
     if TracingFunctionHolder._original_tracing is None:
diff --git a/python/helpers/pydev/pydevd_vars.py b/python/helpers/pydev/pydevd_vars.py
index de8c241..0cc45f7 100644
--- a/python/helpers/pydev/pydevd_vars.py
+++ b/python/helpers/pydev/pydevd_vars.py
@@ -3,7 +3,6 @@
 """
 import pickle
 from django_frame import DjangoTemplateFrame
-from pydevd_constants import * #@UnusedWildImport
 from types import * #@UnusedWildImport
 
 from pydevd_custom_frames import getCustomFrame
@@ -19,16 +18,15 @@
     import _pydev_threading as threading
 else:
     import threading
-import pydevd_resolver
 import traceback
 import pydevd_save_locals
-from pydev_imports import Exec, quote, execfile
+from pydev_imports import Exec, execfile
 
 try:
     import types
     frame_type = types.FrameType
 except:
-    frame_type = None
+    frame_type = type(sys._getframe())
 
 
 #-------------------------------------------------------------------------- defining true and false for earlier versions
@@ -37,25 +35,14 @@
     __setFalse = False
 except:
     import __builtin__
-
     setattr(__builtin__, 'True', 1)
     setattr(__builtin__, 'False', 0)
 
 #------------------------------------------------------------------------------------------------------ class for errors
 
-class VariableError(RuntimeError): pass
+class VariableError(RuntimeError):pass
 
-class FrameNotFoundError(RuntimeError): pass
-
-
-if USE_PSYCO_OPTIMIZATION:
-    try:
-        import psyco
-
-        varToXML = psyco.proxy(varToXML)
-    except ImportError:
-        if hasattr(sys, 'exc_clear'): #jython does not have it
-            sys.exc_clear() #don't keep the traceback -- clients don't want to see it
+class FrameNotFoundError(RuntimeError):pass
 
 def iterFrames(initialFrame):
     '''NO-YIELD VERSION: Iterates through all the frames starting at the specified frame (which will be the first returned item)'''
@@ -166,50 +153,127 @@
         traceback.print_exc()
         return None
 
-def resolveCompoundVariable(thread_id, frame_id, scope, attrs):
-    """ returns the value of the compound variable as a dictionary"""
+def getVariable(thread_id, frame_id, scope, attrs):
+    """
+    returns the value of a variable
+
+    :scope: can be BY_ID, EXPRESSION, GLOBAL, LOCAL, FRAME
+
+    BY_ID means we'll traverse the list of all objects alive to get the object.
+
+    :attrs: after reaching the proper scope, we have to get the attributes until we find
+            the proper location (i.e.: obj\tattr1\tattr2)
+
+    :note: when BY_ID is used, the frame_id is considered the id of the object to find and
+           not the frame (as we don't care about the frame in this case).
+    """
+    if scope == 'BY_ID':
+        if thread_id != GetThreadId(threading.currentThread()) :
+            raise VariableError("getVariable: must execute on same thread")
+
+        try:
+            import gc
+            objects = gc.get_objects()
+        except:
+            pass  #Not all python variants have it.
+        else:
+            frame_id = int(frame_id)
+            for var in objects:
+                if id(var) == frame_id:
+                    if attrs is not None:
+                        attrList = attrs.split('\t')
+                        for k in attrList:
+                            _type, _typeName, resolver = getType(var)
+                            var = resolver.resolve(var, k)
+
+                    return var
+
+        #If it didn't return previously, we coudn't find it by id (i.e.: alrceady garbage collected).
+        sys.stderr.write('Unable to find object with id: %s\n' % (frame_id,))
+        return None
+
     frame = findFrame(thread_id, frame_id)
     if frame is None:
         return {}
 
-    attrList = attrs.split('\t')
-    
-    if scope == "GLOBAL":
-        var = frame.f_globals
-        del attrList[0] # globals are special, and they get a single dummy unused attribute
+    if attrs is not None:
+        attrList = attrs.split('\t')
     else:
-        var = frame.f_locals
-        type, _typeName, resolver = getType(var)
-        try:
-            resolver.resolve(var, attrList[0])
-        except:
-            var = frame.f_globals
+        attrList = []
 
-    for k in attrList:
-        type, _typeName, resolver = getType(var)
-        var = resolver.resolve(var, k)
+    if scope == 'EXPRESSION':
+        for count in xrange(len(attrList)):
+            if count == 0:
+                # An Expression can be in any scope (globals/locals), therefore it needs to evaluated as an expression
+                var = evaluateExpression(thread_id, frame_id, attrList[count], False)
+            else:
+                _type, _typeName, resolver = getType(var)
+                var = resolver.resolve(var, attrList[count])
+    else:
+        if scope == "GLOBAL":
+            var = frame.f_globals
+            del attrList[0]  # globals are special, and they get a single dummy unused attribute
+        else:
+            var = frame.f_locals
+
+        for k in attrList:
+            _type, _typeName, resolver = getType(var)
+            var = resolver.resolve(var, k)
+
+    return var
+
+
+def resolveCompoundVariable(thread_id, frame_id, scope, attrs):
+    """ returns the value of the compound variable as a dictionary"""
+
+    var = getVariable(thread_id, frame_id, scope, attrs)
 
     try:
-        type, _typeName, resolver = getType(var)
+        _type, _typeName, resolver = getType(var)
         return resolver.getDictionary(var)
     except:
+        sys.stderr.write('Error evaluating: thread_id: %s\nframe_id: %s\nscope: %s\nattrs: %s\n' % (
+            thread_id, frame_id, scope, attrs,))
         traceback.print_exc()
-        
-        
+
+
 def resolveVar(var, attrs):
     attrList = attrs.split('\t')
-    
+
     for k in attrList:
         type, _typeName, resolver = getType(var)
-        
+
         var = resolver.resolve(var, k)
-    
+
     try:
         type, _typeName, resolver = getType(var)
         return resolver.getDictionary(var)
     except:
         traceback.print_exc()
-    
+
+
+def customOperation(thread_id, frame_id, scope, attrs, style, code_or_file, operation_fn_name):
+    """
+    We'll execute the code_or_file and then search in the namespace the operation_fn_name to execute with the given var.
+
+    code_or_file: either some code (i.e.: from pprint import pprint) or a file to be executed.
+    operation_fn_name: the name of the operation to execute after the exec (i.e.: pprint)
+    """
+    expressionValue = getVariable(thread_id, frame_id, scope, attrs)
+
+    try:
+        namespace = {'__name__': '<customOperation>'}
+        if style == "EXECFILE":
+            namespace['__file__'] = code_or_file
+            execfile(code_or_file, namespace, namespace)
+        else:  # style == EXEC
+            namespace['__file__'] = '<customOperationCode>'
+            Exec(code_or_file, namespace, namespace)
+
+        return str(namespace[operation_fn_name](expressionValue))
+    except:
+        traceback.print_exc()
+
 
 def evaluateExpression(thread_id, frame_id, expression, doExec):
     '''returns the result of the evaluated expression
@@ -230,6 +294,7 @@
     updated_globals.update(frame.f_locals)  #locals later because it has precedence over the actual globals
 
     try:
+
         if doExec:
             try:
                 #try to make it an eval (if it is an eval we can print it, otherwise we'll exec it and
@@ -240,7 +305,7 @@
                 pydevd_save_locals.save_locals(frame)
             else:
                 result = eval(compiled, updated_globals, frame.f_locals)
-                if result is not None: #Only print if it's not None (as python does)
+                if result is not None:  #Only print if it's not None (as python does)
                     sys.stdout.write('%s\n' % (result,))
             return
 
@@ -251,7 +316,6 @@
             except Exception:
                 s = StringIO()
                 traceback.print_exc(file=s)
-
                 result = s.getvalue()
 
                 try:
@@ -265,6 +329,22 @@
 
                 result = ExceptionOnEvaluate(result)
 
+                # Ok, we have the initial error message, but let's see if we're dealing with a name mangling error...
+                try:
+                    if '__' in expression:
+                        # Try to handle '__' name mangling...
+                        split = expression.split('.')
+                        curr = frame.f_locals.get(split[0])
+                        for entry in split[1:]:
+                            if entry.startswith('__') and not hasattr(curr, entry):
+                                entry = '_%s%s' % (curr.__class__.__name__, entry)
+                            curr = getattr(curr, entry)
+
+                        result = curr
+                except:
+                    pass
+
+
             return result
     finally:
         #Should not be kept alive if an exception happens and this frame is kept in the stack.
@@ -273,22 +353,18 @@
 
 def changeAttrExpression(thread_id, frame_id, attr, expression):
     '''Changes some attribute in a given frame.
-    @note: it will not (currently) work if we're not in the topmost frame (that's a python
-    deficiency -- and it appears that there is no way of making it currently work --
-    will probably need some change to the python internals)
     '''
     frame = findFrame(thread_id, frame_id)
     if frame is None:
         return
 
-    if isinstance(frame, DjangoTemplateFrame):
-        result = eval(expression, frame.f_globals, frame.f_locals)
-        frame.changeVariable(attr, result)
-
     try:
         expression = expression.replace('@LINE@', '\n')
 
-
+        if isinstance(frame, DjangoTemplateFrame):
+            result = eval(expression, frame.f_globals, frame.f_locals)
+            frame.changeVariable(attr, result)
+            return
 
         if attr[:7] == "Globals":
             attr = attr[8:]
diff --git a/python/helpers/pydev/pydevd_xml.py b/python/helpers/pydev/pydevd_xml.py
index ac3f71c..52bb186 100644
--- a/python/helpers/pydev/pydevd_xml.py
+++ b/python/helpers/pydev/pydevd_xml.py
@@ -1,6 +1,7 @@
 import pydev_log
 import traceback
 import pydevd_resolver
+import sys
 from pydevd_constants import * #@UnusedWildImport
 
 from pydev_imports import quote
@@ -146,9 +147,9 @@
             pydev_log.error("Unexpected error, recovered safely.\n")
 
     return xml
-    
-    
-def varToXML(val, name, doTrim=True):
+
+
+def varToXML(val, name, doTrim=True, additionalInXml=''):
     """ single variable or dictionary to xml representation """
 
     is_exception_on_eval = isinstance(val, ExceptionOnEvaluate)
@@ -162,19 +163,22 @@
 
     try:
         if hasattr(v, '__class__'):
-            try:
-                cName = str(v.__class__)
-                if cName.find('.') != -1:
-                    cName = cName.split('.')[-1]
+            if v.__class__ == frame_type:
+                value = pydevd_resolver.frameResolver.getFrameName(v)
+            else:
+                try:
+                    cName = str(v.__class__)
+                    if cName.find('.') != -1:
+                        cName = cName.split('.')[-1]
 
-                elif cName.find("'") != -1: #does not have '.' (could be something like <type 'int'>)
-                    cName = cName[cName.index("'") + 1:]
+                    elif cName.find("'") != -1: #does not have '.' (could be something like <type 'int'>)
+                        cName = cName[cName.index("'") + 1:]
 
-                if cName.endswith("'>"):
-                    cName = cName[:-2]
-            except:
-                cName = str(v.__class__)
-            value = '%s: %s' % (cName, v)
+                    if cName.endswith("'>"):
+                        cName = cName[:-2]
+                except:
+                    cName = str(v.__class__)
+                value = '%s: %s' % (cName, v)
         else:
             value = str(v)
     except:
@@ -218,4 +222,13 @@
         else:
             xmlCont = ''
 
-    return ''.join((xml, xmlValue, xmlCont, ' />\n'))
+    return ''.join((xml, xmlValue, xmlCont, additionalInXml, ' />\n'))
+
+if USE_PSYCO_OPTIMIZATION:
+    try:
+        import psyco
+
+        varToXML = psyco.proxy(varToXML)
+    except ImportError:
+        if hasattr(sys, 'exc_clear'): #jython does not have it
+            sys.exc_clear() #don't keep the traceback -- clients don't want to see it
diff --git a/python/helpers/pydev/runfiles.py b/python/helpers/pydev/runfiles.py
index 4a25469..67c88be 100644
--- a/python/helpers/pydev/runfiles.py
+++ b/python/helpers/pydev/runfiles.py
@@ -1,530 +1,249 @@
-import fnmatch
-import os.path
-import re
-import sys
-import unittest
+import os
+
+def main():
+    import sys
+
+    #Separate the nose params and the pydev params.
+    pydev_params = []
+    other_test_framework_params = []
+    found_other_test_framework_param = None
+
+    NOSE_PARAMS = '--nose-params'
+    PY_TEST_PARAMS = '--py-test-params'
+
+    for arg in sys.argv[1:]:
+        if not found_other_test_framework_param and arg != NOSE_PARAMS and arg != PY_TEST_PARAMS:
+            pydev_params.append(arg)
+
+        else:
+            if not found_other_test_framework_param:
+                found_other_test_framework_param = arg
+            else:
+                other_test_framework_params.append(arg)
 
 
+    #Here we'll run either with nose or with the pydev_runfiles.
+    import pydev_runfiles
+    import pydev_runfiles_xml_rpc
+    import pydevd_constants
+    from pydevd_file_utils import _NormFile
 
+    DEBUG = 0
+    if DEBUG:
+        sys.stdout.write('Received parameters: %s\n' % (sys.argv,))
+        sys.stdout.write('Params for pydev: %s\n' % (pydev_params,))
+        if found_other_test_framework_param:
+            sys.stdout.write('Params for test framework: %s, %s\n' % (found_other_test_framework_param, other_test_framework_params))
 
-try:
-    __setFalse = False
-except:
-    import __builtin__
-    setattr(__builtin__, 'True', 1)
-    setattr(__builtin__, 'False', 0)
-
-
-
-
-#=======================================================================================================================
-# Jython?
-#=======================================================================================================================
-try:
-    import org.python.core.PyDictionary #@UnresolvedImport @UnusedImport -- just to check if it could be valid
-    def DictContains(d, key):
-        return d.has_key(key)
-except:
     try:
-        #Py3k does not have has_key anymore, and older versions don't have __contains__
-        DictContains = dict.__contains__
+        configuration = pydev_runfiles.parse_cmdline([sys.argv[0]] + pydev_params)
     except:
-        DictContains = dict.has_key
+        sys.stderr.write('Command line received: %s\n' % (sys.argv,))
+        raise
+    pydev_runfiles_xml_rpc.InitializeServer(configuration.port) #Note that if the port is None, a Null server will be initialized.
 
-try:
-    xrange
-except:
-    #Python 3k does not have it
-    xrange = range
-
-try:
-    enumerate
-except:
-    def enumerate(lst):
-        ret = []
-        i=0
-        for element in lst:
-            ret.append((i, element))
-            i+=1
-        return ret
-    
-
-
-#=======================================================================================================================
-# getopt code copied since gnu_getopt is not available on jython 2.1
-#=======================================================================================================================
-class GetoptError(Exception):
-    opt = ''
-    msg = ''
-    def __init__(self, msg, opt=''):
-        self.msg = msg
-        self.opt = opt
-        Exception.__init__(self, msg, opt)
-
-    def __str__(self):
-        return self.msg
-
-
-def gnu_getopt(args, shortopts, longopts=[]):
-    """getopt(args, options[, long_options]) -> opts, args
-
-    This function works like getopt(), except that GNU style scanning
-    mode is used by default. This means that option and non-option
-    arguments may be intermixed. The getopt() function stops
-    processing options as soon as a non-option argument is
-    encountered.
-
-    If the first character of the option string is `+', or if the
-    environment variable POSIXLY_CORRECT is set, then option
-    processing stops as soon as a non-option argument is encountered.
-    """
-
-    opts = []
-    prog_args = []
-    if isinstance(longopts, ''.__class__):
-        longopts = [longopts]
-    else:
-        longopts = list(longopts)
-
-    # Allow options after non-option arguments?
-    if shortopts.startswith('+'):
-        shortopts = shortopts[1:]
-        all_options_first = True
-    elif os.environ.get("POSIXLY_CORRECT"):
-        all_options_first = True
-    else:
-        all_options_first = False
-
-    while args:
-        if args[0] == '--':
-            prog_args += args[1:]
-            break
-
-        if args[0][:2] == '--':
-            opts, args = do_longs(opts, args[0][2:], longopts, args[1:])
-        elif args[0][:1] == '-':
-            opts, args = do_shorts(opts, args[0][1:], shortopts, args[1:])
-        else:
-            if all_options_first:
-                prog_args += args
-                break
-            else:
-                prog_args.append(args[0])
-                args = args[1:]
-
-    return opts, prog_args
-
-def do_longs(opts, opt, longopts, args):
+    NOSE_FRAMEWORK = 1
+    PY_TEST_FRAMEWORK = 2
     try:
-        i = opt.index('=')
-    except ValueError:
-        optarg = None
+        if found_other_test_framework_param:
+            test_framework = 0 #Default (pydev)
+            if found_other_test_framework_param == NOSE_PARAMS:
+                import nose
+                test_framework = NOSE_FRAMEWORK
+
+            elif found_other_test_framework_param == PY_TEST_PARAMS:
+                import pytest
+                test_framework = PY_TEST_FRAMEWORK
+
+            else:
+                raise ImportError()
+
+        else:
+            raise ImportError()
+
+    except ImportError:
+        if found_other_test_framework_param:
+            sys.stderr.write('Warning: Could not import the test runner: %s. Running with the default pydev unittest runner instead.\n' % (
+                found_other_test_framework_param,))
+
+        test_framework = 0
+
+    #Clear any exception that may be there so that clients don't see it.
+    #See: https://sourceforge.net/tracker/?func=detail&aid=3408057&group_id=85796&atid=577329
+    if hasattr(sys, 'exc_clear'):
+        sys.exc_clear()
+
+    if test_framework == 0:
+
+        pydev_runfiles.main(configuration)
+
     else:
-        opt, optarg = opt[:i], opt[i + 1:]
+        #We'll convert the parameters to what nose or py.test expects.
+        #The supported parameters are:
+        #runfiles.py  --config-file|-t|--tests <Test.test1,Test2>  dirs|files --nose-params xxx yyy zzz
+        #(all after --nose-params should be passed directly to nose)
 
-    has_arg, opt = long_has_args(opt, longopts)
-    if has_arg:
-        if optarg is None:
-            if not args:
-                raise GetoptError('option --%s requires argument' % opt, opt)
-            optarg, args = args[0], args[1:]
-    elif optarg:
-        raise GetoptError('option --%s must not have an argument' % opt, opt)
-    opts.append(('--' + opt, optarg or ''))
-    return opts, args
-
-# Return:
-#   has_arg?
-#   full option name
-def long_has_args(opt, longopts):
-    possibilities = [o for o in longopts if o.startswith(opt)]
-    if not possibilities:
-        raise GetoptError('option --%s not recognized' % opt, opt)
-    # Is there an exact match?
-    if opt in possibilities:
-        return False, opt
-    elif opt + '=' in possibilities:
-        return True, opt
-    # No exact match, so better be unique.
-    if len(possibilities) > 1:
-        # XXX since possibilities contains all valid continuations, might be
-        # nice to work them into the error msg
-        raise GetoptError('option --%s not a unique prefix' % opt, opt)
-    assert len(possibilities) == 1
-    unique_match = possibilities[0]
-    has_arg = unique_match.endswith('=')
-    if has_arg:
-        unique_match = unique_match[:-1]
-    return has_arg, unique_match
-
-def do_shorts(opts, optstring, shortopts, args):
-    while optstring != '':
-        opt, optstring = optstring[0], optstring[1:]
-        if short_has_arg(opt, shortopts):
-            if optstring == '':
-                if not args:
-                    raise GetoptError('option -%s requires argument' % opt,
-                                      opt)
-                optstring, args = args[0], args[1:]
-            optarg, optstring = optstring, ''
-        else:
-            optarg = ''
-        opts.append(('-' + opt, optarg))
-    return opts, args
-
-def short_has_arg(opt, shortopts):
-    for i in range(len(shortopts)):
-        if opt == shortopts[i] != ':':
-            return shortopts.startswith(':', i + 1)
-    raise GetoptError('option -%s not recognized' % opt, opt)
+        #In java:
+        #--tests = Constants.ATTR_UNITTEST_TESTS
+        #--config-file = Constants.ATTR_UNITTEST_CONFIGURATION_FILE
 
 
-#=======================================================================================================================
-# End getopt code
-#=======================================================================================================================
+        #The only thing actually handled here are the tests that we want to run, which we'll
+        #handle and pass as what the test framework expects.
 
+        py_test_accept_filter = {}
+        files_to_tests = configuration.files_to_tests
 
+        if files_to_tests:
+            #Handling through the file contents (file where each line is a test)
+            files_or_dirs = []
+            for file, tests in files_to_tests.items():
+                if test_framework == NOSE_FRAMEWORK:
+                    for test in tests:
+                        files_or_dirs.append(file + ':' + test)
 
+                elif test_framework == PY_TEST_FRAMEWORK:
+                    file = _NormFile(file)
+                    py_test_accept_filter[file] = tests
+                    files_or_dirs.append(file)
 
-
-
-
-
-
-
-#=======================================================================================================================
-# parse_cmdline
-#=======================================================================================================================
-def parse_cmdline():
-    """ parses command line and returns test directories, verbosity, test filter and test suites
-        usage: 
-            runfiles.py  -v|--verbosity <level>  -f|--filter <regex>  -t|--tests <Test.test1,Test2>  dirs|files
-    """
-    verbosity = 2
-    test_filter = None
-    tests = None
-
-    optlist, dirs = gnu_getopt(sys.argv[1:], "v:f:t:", ["verbosity=", "filter=", "tests="])
-    for opt, value in optlist:
-        if opt in ("-v", "--verbosity"):
-            verbosity = value
-
-        elif opt in ("-f", "--filter"):
-            test_filter = value.split(',')
-
-        elif opt in ("-t", "--tests"):
-            tests = value.split(',')
-
-    if type([]) != type(dirs):
-        dirs = [dirs]
-
-    ret_dirs = []
-    for d in dirs:
-        if '|' in d:
-            #paths may come from the ide separated by |
-            ret_dirs.extend(d.split('|'))
-        else:
-            ret_dirs.append(d)
-
-    return ret_dirs, int(verbosity), test_filter, tests
-
-
-#=======================================================================================================================
-# PydevTestRunner
-#=======================================================================================================================
-class PydevTestRunner:
-    """ finds and runs a file or directory of files as a unit test """
-
-    __py_extensions = ["*.py", "*.pyw"]
-    __exclude_files = ["__init__.*"]
-
-    def __init__(self, test_dir, test_filter=None, verbosity=2, tests=None):
-        self.test_dir = test_dir
-        self.__adjust_path()
-        self.test_filter = self.__setup_test_filter(test_filter)
-        self.verbosity = verbosity
-        self.tests = tests
-
-
-    def __adjust_path(self):
-        """ add the current file or directory to the python path """
-        path_to_append = None
-        for n in xrange(len(self.test_dir)):
-            dir_name = self.__unixify(self.test_dir[n])
-            if os.path.isdir(dir_name):
-                if not dir_name.endswith("/"):
-                    self.test_dir[n] = dir_name + "/"
-                path_to_append = os.path.normpath(dir_name)
-            elif os.path.isfile(dir_name):
-                path_to_append = os.path.dirname(dir_name)
-            else:
-                msg = ("unknown type. \n%s\nshould be file or a directory.\n" % (dir_name))
-                raise RuntimeError(msg)
-        if path_to_append is not None:
-            #Add it as the last one (so, first things are resolved against the default dirs and 
-            #if none resolves, then we try a relative import).
-            sys.path.append(path_to_append)
-        return
-
-    def __setup_test_filter(self, test_filter):
-        """ turn a filter string into a list of filter regexes """
-        if test_filter is None or len(test_filter) == 0:
-            return None
-        return [re.compile("test%s" % f) for f in test_filter]
-
-    def __is_valid_py_file(self, fname):
-        """ tests that a particular file contains the proper file extension 
-            and is not in the list of files to exclude """
-        is_valid_fname = 0
-        for invalid_fname in self.__class__.__exclude_files:
-            is_valid_fname += int(not fnmatch.fnmatch(fname, invalid_fname))
-        if_valid_ext = 0
-        for ext in self.__class__.__py_extensions:
-            if_valid_ext += int(fnmatch.fnmatch(fname, ext))
-        return is_valid_fname > 0 and if_valid_ext > 0
-
-    def __unixify(self, s):
-        """ stupid windows. converts the backslash to forwardslash for consistency """
-        return os.path.normpath(s).replace(os.sep, "/")
-
-    def __importify(self, s, dir=False):
-        """ turns directory separators into dots and removes the ".py*" extension 
-            so the string can be used as import statement """
-        if not dir:
-            dirname, fname = os.path.split(s)
-
-            if fname.count('.') > 1:
-                #if there's a file named xxx.xx.py, it is not a valid module, so, let's not load it...
-                return
-
-            imp_stmt_pieces = [dirname.replace("\\", "/").replace("/", "."), os.path.splitext(fname)[0]]
-
-            if len(imp_stmt_pieces[0]) == 0:
-                imp_stmt_pieces = imp_stmt_pieces[1:]
-
-            return ".".join(imp_stmt_pieces)
-
-        else: #handle dir
-            return s.replace("\\", "/").replace("/", ".")
-
-    def __add_files(self, pyfiles, root, files):
-        """ if files match, appends them to pyfiles. used by os.path.walk fcn """
-        for fname in files:
-            if self.__is_valid_py_file(fname):
-                name_without_base_dir = self.__unixify(os.path.join(root, fname))
-                pyfiles.append(name_without_base_dir)
-        return
-
-
-    def find_import_files(self):
-        """ return a list of files to import """
-        pyfiles = []
-
-        for base_dir in self.test_dir:
-            if os.path.isdir(base_dir):
-                if hasattr(os, 'walk'):
-                    for root, dirs, files in os.walk(base_dir):
-                        self.__add_files(pyfiles, root, files)
                 else:
-                    # jython2.1 is too old for os.walk!
-                    os.path.walk(base_dir, self.__add_files, pyfiles)
+                    raise AssertionError('Cannot handle test framework: %s at this point.' % (test_framework,))
 
-            elif os.path.isfile(base_dir):
-                pyfiles.append(base_dir)
+        else:
+            if configuration.tests:
+                #Tests passed (works together with the files_or_dirs)
+                files_or_dirs = []
+                for file in configuration.files_or_dirs:
+                    if test_framework == NOSE_FRAMEWORK:
+                        for t in configuration.tests:
+                            files_or_dirs.append(file + ':' + t)
 
-        return pyfiles
-
-    def __get_module_from_str(self, modname, print_exception):
-        """ Import the module in the given import path.
-            * Returns the "final" module, so importing "coilib40.subject.visu" 
-            returns the "visu" module, not the "coilib40" as returned by __import__ """
-        try:
-            mod = __import__(modname)
-            for part in modname.split('.')[1:]:
-                mod = getattr(mod, part)
-            return mod
-        except:
-            if print_exception:
-                import traceback;traceback.print_exc()
-                sys.stderr.write('ERROR: Module: %s could not be imported.\n' % (modname,))
-            return None
-
-    def find_modules_from_files(self, pyfiles):
-        """ returns a lisst of modules given a list of files """
-        #let's make sure that the paths we want are in the pythonpath...
-        imports = [self.__importify(s) for s in pyfiles]
-
-        system_paths = []
-        for s in sys.path:
-            system_paths.append(self.__importify(s, True))
-
-
-        ret = []
-        for imp in imports:
-            if imp is None:
-                continue #can happen if a file is not a valid module
-            choices = []
-            for s in system_paths:
-                if imp.startswith(s):
-                    add = imp[len(s) + 1:]
-                    if add:
-                        choices.append(add)
-                    #sys.stdout.write(' ' + add + ' ')
-
-            if not choices:
-                sys.stdout.write('PYTHONPATH not found for file: %s\n' % imp)
-            else:
-                for i, import_str in enumerate(choices):
-                    mod = self.__get_module_from_str(import_str, print_exception=i == len(choices) - 1)
-                    if mod is not None:
-                        ret.append(mod)
-                        break
-
-
-        return ret
-
-    def find_tests_from_modules(self, modules):
-        """ returns the unittests given a list of modules """
-        loader = unittest.TestLoader()
-
-        ret = []
-        if self.tests:
-            accepted_classes = {}
-            accepted_methods = {}
-
-            for t in self.tests:
-                splitted = t.split('.')
-                if len(splitted) == 1:
-                    accepted_classes[t] = t
-
-                elif len(splitted) == 2:
-                    accepted_methods[t] = t
-
-            #===========================================================================================================
-            # GetTestCaseNames
-            #===========================================================================================================
-            class GetTestCaseNames:
-                """Yes, we need a class for that (cannot use outer context on jython 2.1)"""
-
-                def __init__(self, accepted_classes, accepted_methods):
-                    self.accepted_classes = accepted_classes
-                    self.accepted_methods = accepted_methods
-
-                def __call__(self, testCaseClass):
-                    """Return a sorted sequence of method names found within testCaseClass"""
-                    testFnNames = []
-                    className = testCaseClass.__name__
-
-                    if DictContains(self.accepted_classes, className):
-                        for attrname in dir(testCaseClass):
-                            #If a class is chosen, we select all the 'test' methods'
-                            if attrname.startswith('test') and hasattr(getattr(testCaseClass, attrname), '__call__'):
-                                testFnNames.append(attrname)
+                    elif test_framework == PY_TEST_FRAMEWORK:
+                        file = _NormFile(file)
+                        py_test_accept_filter[file] = configuration.tests
+                        files_or_dirs.append(file)
 
                     else:
-                        for attrname in dir(testCaseClass):
-                            #If we have the class+method name, we must do a full check and have an exact match.
-                            if DictContains(self.accepted_methods, className + '.' + attrname):
-                                if hasattr(getattr(testCaseClass, attrname), '__call__'):
-                                    testFnNames.append(attrname)
+                        raise AssertionError('Cannot handle test framework: %s at this point.' % (test_framework,))
+            else:
+                #Only files or dirs passed (let it do the test-loading based on those paths)
+                files_or_dirs = configuration.files_or_dirs
 
-                    #sorted() is not available in jython 2.1
-                    testFnNames.sort()
-                    return testFnNames
+        argv = other_test_framework_params + files_or_dirs
 
 
-            loader.getTestCaseNames = GetTestCaseNames(accepted_classes, accepted_methods)
+        if test_framework == NOSE_FRAMEWORK:
+            #Nose usage: http://somethingaboutorange.com/mrl/projects/nose/0.11.2/usage.html
+            #show_stdout_option = ['-s']
+            #processes_option = ['--processes=2']
+            argv.insert(0, sys.argv[0])
+            if DEBUG:
+                sys.stdout.write('Final test framework args: %s\n' % (argv[1:],))
+
+            import pydev_runfiles_nose
+            PYDEV_NOSE_PLUGIN_SINGLETON = pydev_runfiles_nose.StartPydevNosePluginSingleton(configuration)
+            argv.append('--with-pydevplugin')
+            nose.run(argv=argv, addplugins=[PYDEV_NOSE_PLUGIN_SINGLETON])
+
+        elif test_framework == PY_TEST_FRAMEWORK:
+            if DEBUG:
+                sys.stdout.write('Final test framework args: %s\n' % (argv,))
+                sys.stdout.write('py_test_accept_filter: %s\n' % (py_test_accept_filter,))
+
+            import os
+
+            try:
+                xrange
+            except:
+                xrange = range
+
+            for i in xrange(len(argv)):
+                arg = argv[i]
+                #Workaround bug in py.test: if we pass the full path it ends up importing conftest
+                #more than once (so, always work with relative paths).
+                if os.path.isfile(arg) or os.path.isdir(arg):
+                    from pydev_imports import relpath
+                    arg = relpath(arg)
+                    argv[i] = arg
+
+            d = os.path.dirname(__file__)
+            if d not in sys.path:
+                sys.path.insert(0, d)
+
+            import pickle, zlib, base64
+
+            # Update environment PYTHONPATH so that it finds our plugin if using xdist.
+            os.environ['PYTHONPATH'] = os.pathsep.join(sys.path)
+
+            # Set what should be skipped in the plugin through an environment variable
+            s = base64.b64encode(zlib.compress(pickle.dumps(py_test_accept_filter)))
+            if pydevd_constants.IS_PY3K:
+                s = s.decode('ascii') # Must be str in py3.
+            os.environ['PYDEV_PYTEST_SKIP'] = s
+
+            # Identifies the main pid (i.e.: if it's not the main pid it has to connect back to the
+            # main pid to give xml-rpc notifications).
+            os.environ['PYDEV_MAIN_PID'] = str(os.getpid())
+            os.environ['PYDEV_PYTEST_SERVER'] = str(configuration.port)
+
+            argv.append('-p')
+            argv.append('pydev_runfiles_pytest2')
+            pytest.main(argv)
+
+        else:
+            raise AssertionError('Cannot handle test framework: %s at this point.' % (test_framework,))
 
 
-        ret.extend([loader.loadTestsFromModule(m) for m in modules])
-
-        return ret
-
-
-    def filter_tests(self, test_objs):
-        """ based on a filter name, only return those tests that have
-            the test case names that match """
-        test_suite = []
-        for test_obj in test_objs:
-
-            if isinstance(test_obj, unittest.TestSuite):
-                if test_obj._tests:
-                    test_obj._tests = self.filter_tests(test_obj._tests)
-                    if test_obj._tests:
-                        test_suite.append(test_obj)
-
-            elif isinstance(test_obj, unittest.TestCase):
-                test_cases = []
-                for tc in test_objs:
-                    try:
-                        testMethodName = tc._TestCase__testMethodName
-                    except AttributeError:
-                        #changed in python 2.5
-                        testMethodName = tc._testMethodName
-
-                    if self.__match(self.test_filter, testMethodName) and self.__match_tests(self.tests, tc, testMethodName):
-                        test_cases.append(tc)
-                return test_cases
-        return test_suite
-
-
-    def __match_tests(self, tests, test_case, test_method_name):
-        if not tests:
-            return 1
-
-        for t in tests:
-            class_and_method = t.split('.')
-            if len(class_and_method) == 1:
-                #only class name
-                if class_and_method[0] == test_case.__class__.__name__:
-                    return 1
-
-            elif len(class_and_method) == 2:
-                if class_and_method[0] == test_case.__class__.__name__ and class_and_method[1] == test_method_name:
-                    return 1
-
-        return 0
-
-
-
-
-    def __match(self, filter_list, name):
-        """ returns whether a test name matches the test filter """
-        if filter_list is None:
-            return 1
-        for f in filter_list:
-            if re.match(f, name):
-                return 1
-        return 0
-
-
-    def run_tests(self):
-        """ runs all tests """
-        sys.stdout.write("Finding files...\n")
-        files = self.find_import_files()
-        sys.stdout.write('%s %s\n' % (self.test_dir, '... done'))
-        sys.stdout.write("Importing test modules ... ")
-        modules = self.find_modules_from_files(files)
-        sys.stdout.write("done.\n")
-        all_tests = self.find_tests_from_modules(modules)
-        if self.test_filter or self.tests:
-
-            if self.test_filter:
-                sys.stdout.write('Test Filter: %s' % ([p.pattern for p in self.test_filter],))
-
-            if self.tests:
-                sys.stdout.write('Tests to run: %s' % (self.tests,))
-
-            all_tests = self.filter_tests(all_tests)
-
-        sys.stdout.write('\n')
-        runner = unittest.TextTestRunner(stream=sys.stdout, descriptions=1, verbosity=verbosity)
-        runner.run(unittest.TestSuite(all_tests))
-        return
-
-#=======================================================================================================================
-# main        
-#=======================================================================================================================
 if __name__ == '__main__':
-    dirs, verbosity, test_filter, tests = parse_cmdline()
-    PydevTestRunner(dirs, test_filter, verbosity, tests).run_tests()
+    try:
+        main()
+    finally:
+        try:
+            #The server is not a daemon thread, so, we have to ask for it to be killed!
+            import pydev_runfiles_xml_rpc
+            pydev_runfiles_xml_rpc.forceServerKill()
+        except:
+            pass #Ignore any errors here
+
+    import sys
+    import threading
+    if hasattr(sys, '_current_frames') and hasattr(threading, 'enumerate'):
+        import time
+        import traceback
+
+        class DumpThreads(threading.Thread):
+            def run(self):
+                time.sleep(10)
+
+                thread_id_to_name = {}
+                try:
+                    for t in threading.enumerate():
+                        thread_id_to_name[t.ident] = '%s  (daemon: %s)' % (t.name, t.daemon)
+                except:
+                    pass
+
+                stack_trace = [
+                    '===============================================================================',
+                    'pydev pyunit runner: Threads still found running after tests finished',
+                    '================================= Thread Dump =================================']
+
+                for thread_id, stack in sys._current_frames().items():
+                    stack_trace.append('\n-------------------------------------------------------------------------------')
+                    stack_trace.append(" Thread %s" % thread_id_to_name.get(thread_id, thread_id))
+                    stack_trace.append('')
+
+                    if 'self' in stack.f_locals:
+                        sys.stderr.write(str(stack.f_locals['self'])+'\n')
+
+                    for filename, lineno, name, line in traceback.extract_stack(stack):
+                        stack_trace.append(' File "%s", line %d, in %s' % (filename, lineno, name))
+                        if line:
+                            stack_trace.append("   %s" % (line.strip()))
+                stack_trace.append('\n=============================== END Thread Dump ===============================')
+                sys.stderr.write('\n'.join(stack_trace))
+
+
+        dump_current_frames_thread = DumpThreads()
+        dump_current_frames_thread.setDaemon(True) # Daemon so that this thread doesn't halt it!
+        dump_current_frames_thread.start()
diff --git a/python/helpers/pydev/stubs/_django_manager_body.py b/python/helpers/pydev/stubs/_django_manager_body.py
new file mode 100644
index 0000000..2bf4706
--- /dev/null
+++ b/python/helpers/pydev/stubs/_django_manager_body.py
@@ -0,0 +1,414 @@
+# This is a dummy for code-completion purposes.
+
+def __unicode__(self):
+    """
+    Return "app_label.model_label.manager_name". 
+    """
+
+def _copy_to_model(self, model):
+    """
+    Makes a copy of the manager and assigns it to 'model', which should be
+    a child of the existing model (used when inheriting a manager from an
+    abstract base class).
+    """
+
+
+def _db(self):
+    """
+
+    """
+
+
+def _get_queryset_methods(cls, queryset_class):
+    """
+
+    """
+
+
+def _hints(self):
+    """
+    dict() -> new empty dictionary
+    dict(mapping) -> new dictionary initialized from a mapping object's
+        (key, value) pairs
+    dict(iterable) -> new dictionary initialized as if via:
+        d = {}
+        for k, v in iterable:
+            d[k] = v
+    dict(**kwargs) -> new dictionary initialized with the name=value pairs
+        in the keyword argument list.  For example:  dict(one=1, two=2)
+    """
+
+
+def _inherited(self):
+    """
+
+    """
+
+
+def _insert(self, *args, **kwargs):
+    """
+    Inserts a new record for the given model. This provides an interface to
+    the InsertQuery class and is how Model.save() is implemented.
+    """
+
+
+def _queryset_class(self):
+    """
+    Represents a lazy database lookup for a set of objects.
+    """
+
+
+def _set_creation_counter(self):
+    """
+    Sets the creation counter value for this instance and increments the
+    class-level copy.
+    """
+
+
+def _update(self, *args, **kwargs):
+    """
+    A version of update that accepts field objects instead of field names.
+    Used primarily for model saving and not intended for use by general
+    code (it requires too much poking around at model internals to be
+    useful at that level).
+    """
+
+
+def aggregate(self, *args, **kwargs):
+    """
+    Returns a dictionary containing the calculations (aggregation)
+    over the current queryset
+    
+    If args is present the expression is passed as a kwarg using
+    the Aggregate object's default alias.
+    """
+
+
+def all(self):
+    """
+    @rtype: django.db.models.query.QuerySet
+    """
+
+
+def annotate(self, *args, **kwargs):
+    """
+    Return a query set in which the returned objects have been annotated
+    with data aggregated from related fields.
+    """
+
+
+def bulk_create(self, *args, **kwargs):
+    """
+    Inserts each of the instances into the database. This does *not* call
+    save() on each of the instances, does not send any pre/post save
+    signals, and does not set the primary key attribute if it is an
+    autoincrement field.
+    """
+
+
+def check(self, **kwargs):
+    """
+
+    """
+
+
+def complex_filter(self, *args, **kwargs):
+    """
+    Returns a new QuerySet instance with filter_obj added to the filters.
+    
+    filter_obj can be a Q object (or anything with an add_to_query()
+    method) or a dictionary of keyword lookup arguments.
+    
+    This exists to support framework features such as 'limit_choices_to',
+    and usually it will be more natural to use other methods.
+    
+    @rtype: django.db.models.query.QuerySet
+    """
+
+
+def contribute_to_class(self, model, name):
+    """
+
+    """
+
+
+def count(self, *args, **kwargs):
+    """
+    Performs a SELECT COUNT() and returns the number of records as an
+    integer.
+    
+    If the QuerySet is already fully cached this simply returns the length
+    of the cached results set to avoid multiple SELECT COUNT(*) calls.
+    """
+
+
+def create(self, *args, **kwargs):
+    """
+    Creates a new object with the given kwargs, saving it to the database
+    and returning the created object.
+    """
+
+
+def creation_counter(self):
+    """
+
+    """
+
+
+def dates(self, *args, **kwargs):
+    """
+    Returns a list of date objects representing all available dates for
+    the given field_name, scoped to 'kind'.
+    """
+
+
+def datetimes(self, *args, **kwargs):
+    """
+    Returns a list of datetime objects representing all available
+    datetimes for the given field_name, scoped to 'kind'.
+    """
+
+
+def db(self):
+    """
+
+    """
+
+
+def db_manager(self, using=None, hints=None):
+    """
+
+    """
+
+
+def defer(self, *args, **kwargs):
+    """
+    Defers the loading of data for certain fields until they are accessed.
+    The set of fields to defer is added to any existing set of deferred
+    fields. The only exception to this is if None is passed in as the only
+    parameter, in which case all deferrals are removed (None acts as a
+    reset option).
+    """
+
+
+def distinct(self, *args, **kwargs):
+    """
+    Returns a new QuerySet instance that will select only distinct results.
+    
+    @rtype: django.db.models.query.QuerySet
+    """
+
+
+def earliest(self, *args, **kwargs):
+    """
+
+    """
+
+
+def exclude(self, *args, **kwargs):
+    """
+    Returns a new QuerySet instance with NOT (args) ANDed to the existing
+    set.
+    
+    @rtype: django.db.models.query.QuerySet
+    """
+
+
+def exists(self, *args, **kwargs):
+    """
+
+    """
+
+
+def extra(self, *args, **kwargs):
+    """
+    Adds extra SQL fragments to the query.
+    """
+
+
+def filter(self, *args, **kwargs):
+    """
+    Returns a new QuerySet instance with the args ANDed to the existing
+    set.
+    
+    @rtype: django.db.models.query.QuerySet
+    """
+
+
+def first(self, *args, **kwargs):
+    """
+    Returns the first object of a query, returns None if no match is found.
+    """
+
+
+def from_queryset(cls, queryset_class, class_name=None):
+    """
+
+    """
+
+
+def get(self, *args, **kwargs):
+    """
+    Performs the query and returns a single object matching the given
+    keyword arguments.
+    """
+
+
+def get_or_create(self, *args, **kwargs):
+    """
+    Looks up an object with the given kwargs, creating one if necessary.
+    Returns a tuple of (object, created), where created is a boolean
+    specifying whether an object was created.
+    """
+
+
+def get_queryset(self):
+    """
+    Returns a new QuerySet object.  Subclasses can override this method to
+    easily customize the behavior of the Manager.
+    
+    @rtype: django.db.models.query.QuerySet
+    """
+
+
+def in_bulk(self, *args, **kwargs):
+    """
+    Returns a dictionary mapping each of the given IDs to the object with
+    that ID.
+    """
+
+
+def iterator(self, *args, **kwargs):
+    """
+    An iterator over the results from applying this QuerySet to the
+    database.
+    """
+
+
+def last(self, *args, **kwargs):
+    """
+    Returns the last object of a query, returns None if no match is found.
+    """
+
+
+def latest(self, *args, **kwargs):
+    """
+
+    """
+
+
+def model(self):
+    """
+    MyModel(id)
+    """
+
+
+def none(self, *args, **kwargs):
+    """
+    Returns an empty QuerySet.
+    
+    @rtype: django.db.models.query.QuerySet
+    """
+
+
+def only(self, *args, **kwargs):
+    """
+    Essentially, the opposite of defer. Only the fields passed into this
+    method and that are not already specified as deferred are loaded
+    immediately when the queryset is evaluated.
+    """
+
+
+def order_by(self, *args, **kwargs):
+    """
+    Returns a new QuerySet instance with the ordering changed.
+    
+    @rtype: django.db.models.query.QuerySet
+    """
+
+
+def prefetch_related(self, *args, **kwargs):
+    """
+    Returns a new QuerySet instance that will prefetch the specified
+    Many-To-One and Many-To-Many related objects when the QuerySet is
+    evaluated.
+    
+    When prefetch_related() is called more than once, the list of lookups to
+    prefetch is appended to. If prefetch_related(None) is called, the list
+    is cleared.
+    
+    @rtype: django.db.models.query.QuerySet
+    """
+
+
+def raw(self, *args, **kwargs):
+    """
+
+    """
+
+
+def reverse(self, *args, **kwargs):
+    """
+    Reverses the ordering of the QuerySet.
+    
+    @rtype: django.db.models.query.QuerySet
+    """
+
+
+def select_for_update(self, *args, **kwargs):
+    """
+    Returns a new QuerySet instance that will select objects with a
+    FOR UPDATE lock.
+    
+    @rtype: django.db.models.query.QuerySet
+    """
+
+
+def select_related(self, *args, **kwargs):
+    """
+    Returns a new QuerySet instance that will select related objects.
+    
+    If fields are specified, they must be ForeignKey fields and only those
+    related objects are included in the selection.
+    
+    If select_related(None) is called, the list is cleared.
+    
+    @rtype: django.db.models.query.QuerySet
+    """
+
+
+def update(self, *args, **kwargs):
+    """
+    Updates all elements in the current QuerySet, setting all the given
+    fields to the appropriate values.
+    """
+
+
+def update_or_create(self, *args, **kwargs):
+    """
+    Looks up an object with the given kwargs, updating one with defaults
+    if it exists, otherwise creates a new one.
+    Returns a tuple (object, created), where created is a boolean
+    specifying whether an object was created.
+    """
+
+
+def using(self, *args, **kwargs):
+    """
+    Selects which database this QuerySet should execute its query against.
+    
+    @rtype: django.db.models.query.QuerySet
+    """
+
+
+def values(self, *args, **kwargs):
+    """
+
+    """
+
+
+def values_list(self, *args, **kwargs):
+    """
+
+    """
+
diff --git a/python/helpers/pydev/stubs/_get_tips.py b/python/helpers/pydev/stubs/_get_tips.py
new file mode 100644
index 0000000..b98e1c5
--- /dev/null
+++ b/python/helpers/pydev/stubs/_get_tips.py
@@ -0,0 +1,280 @@
+import os.path
+import inspect
+import sys
+
+# completion types.
+TYPE_IMPORT = '0'
+TYPE_CLASS = '1'
+TYPE_FUNCTION = '2'
+TYPE_ATTR = '3'
+TYPE_BUILTIN = '4'
+TYPE_PARAM = '5'
+
+def _imp(name, log=None):
+    try:
+        return __import__(name)
+    except:
+        if '.' in name:
+            sub = name[0:name.rfind('.')]
+
+            if log is not None:
+                log.AddContent('Unable to import', name, 'trying with', sub)
+                # log.AddContent('PYTHONPATH:')
+                # log.AddContent('\n'.join(sorted(sys.path)))
+                log.AddException()
+
+            return _imp(sub, log)
+        else:
+            s = 'Unable to import module: %s - sys.path: %s' % (str(name), sys.path)
+            if log is not None:
+                log.AddContent(s)
+                log.AddException()
+
+            raise ImportError(s)
+
+
+IS_IPY = False
+if sys.platform == 'cli':
+    IS_IPY = True
+    _old_imp = _imp
+    def _imp(name, log=None):
+        # We must add a reference in clr for .Net
+        import clr  # @UnresolvedImport
+        initial_name = name
+        while '.' in name:
+            try:
+                clr.AddReference(name)
+                break  # If it worked, that's OK.
+            except:
+                name = name[0:name.rfind('.')]
+        else:
+            try:
+                clr.AddReference(name)
+            except:
+                pass  # That's OK (not dot net module).
+
+        return _old_imp(initial_name, log)
+
+
+
+def GetFile(mod):
+    f = None
+    try:
+        f = inspect.getsourcefile(mod) or inspect.getfile(mod)
+    except:
+        if hasattr(mod, '__file__'):
+            f = mod.__file__
+            if f.lower(f[-4:]) in ['.pyc', '.pyo']:
+                filename = f[:-4] + '.py'
+                if os.path.exists(filename):
+                    f = filename
+
+    return f
+
+def Find(name, log=None):
+    f = None
+
+    mod = _imp(name, log)
+    parent = mod
+    foundAs = ''
+
+    if inspect.ismodule(mod):
+        f = GetFile(mod)
+
+    components = name.split('.')
+
+    old_comp = None
+    for comp in components[1:]:
+        try:
+            # this happens in the following case:
+            # we have mx.DateTime.mxDateTime.mxDateTime.pyd
+            # but after importing it, mx.DateTime.mxDateTime shadows access to mxDateTime.pyd
+            mod = getattr(mod, comp)
+        except AttributeError:
+            if old_comp != comp:
+                raise
+
+        if inspect.ismodule(mod):
+            f = GetFile(mod)
+        else:
+            if len(foundAs) > 0:
+                foundAs = foundAs + '.'
+            foundAs = foundAs + comp
+
+        old_comp = comp
+
+    return f, mod, parent, foundAs
+
+
+def GenerateTip(data, log=None):
+    data = data.replace('\n', '')
+    if data.endswith('.'):
+        data = data.rstrip('.')
+
+    f, mod, parent, foundAs = Find(data, log)
+    # print_ >> open('temp.txt', 'w'), f
+    tips = GenerateImportsTipForModule(mod)
+    return f, tips
+
+
+def CheckChar(c):
+    if c == '-' or c == '.':
+        return '_'
+    return c
+
+def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr, filter=lambda name:True):
+    '''
+        @param obj_to_complete: the object from where we should get the completions
+        @param dirComps: if passed, we should not 'dir' the object and should just iterate those passed as a parameter
+        @param getattr: the way to get a given object from the obj_to_complete (used for the completer)
+        @param filter: a callable that receives the name and decides if it should be appended or not to the results
+        @return: list of tuples, so that each tuple represents a completion with:
+            name, doc, args, type (from the TYPE_* constants)
+    '''
+    ret = []
+
+    if dirComps is None:
+        dirComps = dir(obj_to_complete)
+        if hasattr(obj_to_complete, '__dict__'):
+            dirComps.append('__dict__')
+        if hasattr(obj_to_complete, '__class__'):
+            dirComps.append('__class__')
+
+    getCompleteInfo = True
+
+    if len(dirComps) > 1000:
+        # ok, we don't want to let our users wait forever...
+        # no complete info for you...
+
+        getCompleteInfo = False
+
+    dontGetDocsOn = (float, int, str, tuple, list)
+    for d in dirComps:
+
+        if d is None:
+            continue
+
+        if not filter(d):
+            continue
+
+        args = ''
+
+        try:
+            obj = getattr(obj_to_complete, d)
+        except:  # just ignore and get it without aditional info
+            ret.append((d, '', args, TYPE_BUILTIN))
+        else:
+
+            if getCompleteInfo:
+                retType = TYPE_BUILTIN
+
+                # check if we have to get docs
+                getDoc = True
+                for class_ in dontGetDocsOn:
+
+                    if isinstance(obj, class_):
+                        getDoc = False
+                        break
+
+                doc = ''
+                if getDoc:
+                    # no need to get this info... too many constants are defined and
+                    # makes things much slower (passing all that through sockets takes quite some time)
+                    try:
+                        doc = inspect.getdoc(obj)
+                        if doc is None:
+                            doc = ''
+                    except:  # may happen on jython when checking java classes (so, just ignore it)
+                        doc = ''
+
+
+                if inspect.ismethod(obj) or inspect.isbuiltin(obj) or inspect.isfunction(obj) or inspect.isroutine(obj):
+                    try:
+                        args, vargs, kwargs, defaults = inspect.getargspec(obj)
+                    except:
+                        args, vargs, kwargs, defaults = (('self',), None, None, None)
+                    if defaults is not None:
+                        start_defaults_at = len(args) - len(defaults)
+
+
+                    r = ''
+                    for i, a in enumerate(args):
+
+                        if len(r) > 0:
+                            r = r + ', '
+
+                        r = r + str(a)
+
+                        if defaults is not None and i >= start_defaults_at:
+                            default =  defaults[i - start_defaults_at]
+                            r += '=' +str(default)
+
+
+                    others = ''
+                    if vargs:
+                        others += '*' + vargs
+
+                    if kwargs:
+                        if others:
+                            others+= ', '
+                        others += '**' + kwargs
+
+                    if others:
+                        r+= ', '
+
+
+                    args = '(%s%s)' % (r, others)
+                    retType = TYPE_FUNCTION
+
+                elif inspect.isclass(obj):
+                    retType = TYPE_CLASS
+
+                elif inspect.ismodule(obj):
+                    retType = TYPE_IMPORT
+
+                else:
+                    retType = TYPE_ATTR
+
+
+                # add token and doc to return - assure only strings.
+                ret.append((d, doc, args, retType))
+
+
+            else:  # getCompleteInfo == False
+                if inspect.ismethod(obj) or inspect.isbuiltin(obj) or inspect.isfunction(obj) or inspect.isroutine(obj):
+                    retType = TYPE_FUNCTION
+
+                elif inspect.isclass(obj):
+                    retType = TYPE_CLASS
+
+                elif inspect.ismodule(obj):
+                    retType = TYPE_IMPORT
+
+                else:
+                    retType = TYPE_ATTR
+                # ok, no complete info, let's try to do this as fast and clean as possible
+                # so, no docs for this kind of information, only the signatures
+                ret.append((d, '', str(args), retType))
+
+    return ret
+
+
+
+
+if __name__ == '__main__':
+    # To use when we have some object: i.e.: obj_to_complete=MyModel.objects
+    temp = '''
+def %(method_name)s%(args)s:
+    """
+%(doc)s
+    """
+'''
+
+    for entry in GenerateImportsTipForModule(obj_to_complete):
+        import textwrap
+        doc = textwrap.dedent(entry[1])
+        lines = []
+        for line in doc.splitlines():
+            lines.append('    ' + line)
+        doc = '\n'.join(lines)
+        print temp % dict(method_name=entry[0], args=entry[2] or '(self)', doc=doc)
diff --git a/python/helpers/pydev/test_debug.py b/python/helpers/pydev/test_debug.py
index bc55de1..2196ca6 100644
--- a/python/helpers/pydev/test_debug.py
+++ b/python/helpers/pydev/test_debug.py
@@ -3,7 +3,7 @@
 import unittest
 import os
 
-test_data_path = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', '..', '..', '..', 'python', 'testData', 'debug'))
+test_data_path = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', '..', 'testData', 'debug'))
 
 class PyDevTestCase(unittest.TestCase):
     def testZipFileExits(self):
diff --git a/python/helpers/pydev/test_pydevd_reload/test_pydevd_reload.py b/python/helpers/pydev/test_pydevd_reload/test_pydevd_reload.py
new file mode 100644
index 0000000..062ead2
--- /dev/null
+++ b/python/helpers/pydev/test_pydevd_reload/test_pydevd_reload.py
@@ -0,0 +1,516 @@
+import os  # @NoMove
+import sys  # @NoMove
+sys.path.insert(0, os.path.realpath(os.path.abspath('..')))
+
+import pydevd_reload
+import tempfile
+import unittest
+
+
+SAMPLE_CODE = """
+class C:
+    def foo(self):
+        return 0
+
+    @classmethod
+    def bar(cls):
+        return (0, 0)
+
+    @staticmethod
+    def stomp():
+        return (0, 0, 0)
+
+    def unchanged(self):
+        return 'unchanged'
+"""
+
+
+
+class Test(unittest.TestCase):
+
+
+    def setUp(self):
+        unittest.TestCase.setUp(self)
+        self.tempdir = None
+        self.save_path = None
+        self.tempdir = tempfile.mkdtemp()
+        self.save_path = list(sys.path)
+        sys.path.append(self.tempdir)
+        try:
+            del sys.modules['x']
+        except:
+            pass
+
+
+    def tearDown(self):
+        unittest.TestCase.tearDown(self)
+        sys.path = self.save_path
+        try:
+            del sys.modules['x']
+        except:
+            pass
+
+    def make_mod(self, name="x", repl=None, subst=None, sample=SAMPLE_CODE):
+        fn = os.path.join(self.tempdir, name + ".py")
+        f = open(fn, "w")
+        if repl is not None and subst is not None:
+            sample = sample.replace(repl, subst)
+        try:
+            f.write(sample)
+        finally:
+            f.close()
+
+
+    def test_pydevd_reload(self):
+
+        self.make_mod()
+        import x
+
+        C = x.C
+        COut = C
+        Cfoo = C.foo
+        Cbar = C.bar
+        Cstomp = C.stomp
+
+        def check2(expected):
+            C = x.C
+            Cfoo = C.foo
+            Cbar = C.bar
+            Cstomp = C.stomp
+            b = C()
+            bfoo = b.foo
+            self.assertEqual(expected, b.foo())
+            self.assertEqual(expected, bfoo())
+            self.assertEqual(expected, Cfoo(b))
+
+        def check(expected):
+            b = COut()
+            bfoo = b.foo
+            self.assertEqual(expected, b.foo())
+            self.assertEqual(expected, bfoo())
+            self.assertEqual(expected, Cfoo(b))
+            self.assertEqual((expected, expected), Cbar())
+            self.assertEqual((expected, expected, expected), Cstomp())
+            check2(expected)
+
+        check(0)
+
+        # modify mod and reload
+        count = 0
+        while count < 1:
+            count += 1
+            self.make_mod(repl="0", subst=str(count))
+            pydevd_reload.xreload(x)
+            check(count)
+
+
+    def test_pydevd_reload2(self):
+
+        self.make_mod()
+        import x
+
+        c = x.C()
+        cfoo = c.foo
+        self.assertEqual(0, c.foo())
+        self.assertEqual(0, cfoo())
+
+        self.make_mod(repl="0", subst='1')
+        pydevd_reload.xreload(x)
+        self.assertEqual(1, c.foo())
+        self.assertEqual(1, cfoo())
+
+    def test_pydevd_reload3(self):
+        class F:
+            def m1(self):
+                return 1
+        class G:
+            def m1(self):
+                return 2
+
+        self.assertEqual(F().m1(), 1)
+        pydevd_reload.Reload(None)._update(None, None, F, G)
+        self.assertEqual(F().m1(), 2)
+
+
+    def test_pydevd_reload4(self):
+        class F:
+            pass
+        F.m1 = lambda a:None
+        class G:
+            pass
+        G.m1 = lambda a:10
+
+        self.assertEqual(F().m1(), None)
+        pydevd_reload.Reload(None)._update(None, None, F, G)
+        self.assertEqual(F().m1(), 10)
+
+
+
+    def test_if_code_obj_equals(self):
+        class F:
+            def m1(self):
+                return 1
+        class G:
+            def m1(self):
+                return 1
+        class H:
+            def m1(self):
+                return 2
+
+        if hasattr(F.m1, 'func_code'):
+            self.assertTrue(pydevd_reload.code_objects_equal(F.m1.func_code, G.m1.func_code))
+            self.assertFalse(pydevd_reload.code_objects_equal(F.m1.func_code, H.m1.func_code))
+        else:
+            self.assertTrue(pydevd_reload.code_objects_equal(F.m1.__code__, G.m1.__code__))
+            self.assertFalse(pydevd_reload.code_objects_equal(F.m1.__code__, H.m1.__code__))
+
+
+
+    def test_metaclass(self):
+
+        class Meta(type):
+            def __init__(cls, name, bases, attrs):
+                super(Meta, cls).__init__(name, bases, attrs)
+
+        class F:
+            __metaclass__ = Meta
+
+            def m1(self):
+                return 1
+
+
+        class G:
+            __metaclass__ = Meta
+
+            def m1(self):
+                return 2
+
+        self.assertEqual(F().m1(), 1)
+        pydevd_reload.Reload(None)._update(None, None, F, G)
+        self.assertEqual(F().m1(), 2)
+
+
+
+    def test_change_hierarchy(self):
+
+        class F(object):
+
+            def m1(self):
+                return 1
+
+
+        class B(object):
+            def super_call(self):
+                return 2
+
+        class G(B):
+
+            def m1(self):
+                return self.super_call()
+
+        self.assertEqual(F().m1(), 1)
+        old = pydevd_reload.notify_error
+        self._called = False
+        def on_error(*args):
+            self._called = True
+        try:
+            pydevd_reload.notify_error = on_error
+            pydevd_reload.Reload(None)._update(None, None, F, G)
+            self.assertTrue(self._called)
+        finally:
+            pydevd_reload.notify_error = old
+
+
+    def test_change_hierarchy_old_style(self):
+
+        class F:
+
+            def m1(self):
+                return 1
+
+
+        class B:
+            def super_call(self):
+                return 2
+
+        class G(B):
+
+            def m1(self):
+                return self.super_call()
+
+
+        self.assertEqual(F().m1(), 1)
+        old = pydevd_reload.notify_error
+        self._called = False
+        def on_error(*args):
+            self._called = True
+        try:
+            pydevd_reload.notify_error = on_error
+            pydevd_reload.Reload(None)._update(None, None, F, G)
+            self.assertTrue(self._called)
+        finally:
+            pydevd_reload.notify_error = old
+
+
+    def test_create_class(self):
+        SAMPLE_CODE1 = """
+class C:
+    def foo(self):
+        return 0
+"""
+        # Creating a new class and using it from old class
+        SAMPLE_CODE2 = """
+class B:
+    pass
+
+class C:
+    def foo(self):
+        return B
+"""
+
+        self.make_mod(sample=SAMPLE_CODE1)
+        import x
+        foo = x.C().foo
+        self.assertEqual(foo(), 0)
+        self.make_mod(sample=SAMPLE_CODE2)
+        pydevd_reload.xreload(x)
+        self.assertEqual(foo().__name__, 'B')
+
+    def test_create_class2(self):
+        SAMPLE_CODE1 = """
+class C(object):
+    def foo(self):
+        return 0
+"""
+        # Creating a new class and using it from old class
+        SAMPLE_CODE2 = """
+class B(object):
+    pass
+
+class C(object):
+    def foo(self):
+        return B
+"""
+
+        self.make_mod(sample=SAMPLE_CODE1)
+        import x
+        foo = x.C().foo
+        self.assertEqual(foo(), 0)
+        self.make_mod(sample=SAMPLE_CODE2)
+        pydevd_reload.xreload(x)
+        self.assertEqual(foo().__name__, 'B')
+
+    def test_parent_function(self):
+        SAMPLE_CODE1 = """
+class B(object):
+    def foo(self):
+        return 0
+
+class C(B):
+    def call(self):
+        return self.foo()
+"""
+        # Creating a new class and using it from old class
+        SAMPLE_CODE2 = """
+class B(object):
+    def foo(self):
+        return 0
+    def bar(self):
+        return 'bar'
+
+class C(B):
+    def call(self):
+        return self.bar()
+"""
+
+        self.make_mod(sample=SAMPLE_CODE1)
+        import x
+        call = x.C().call
+        self.assertEqual(call(), 0)
+        self.make_mod(sample=SAMPLE_CODE2)
+        pydevd_reload.xreload(x)
+        self.assertEqual(call(), 'bar')
+
+
+    def test_update_constant(self):
+        SAMPLE_CODE1 = """
+CONSTANT = 1
+
+class B(object):
+    def foo(self):
+        return CONSTANT
+"""
+        SAMPLE_CODE2 = """
+CONSTANT = 2
+
+class B(object):
+    def foo(self):
+        return CONSTANT
+"""
+
+        self.make_mod(sample=SAMPLE_CODE1)
+        import x
+        foo = x.B().foo
+        self.assertEqual(foo(), 1)
+        self.make_mod(sample=SAMPLE_CODE2)
+        pydevd_reload.xreload(x)
+        self.assertEqual(foo(), 1) #Just making it explicit we don't reload constants.
+
+
+    def test_update_constant_with_custom_code(self):
+        SAMPLE_CODE1 = """
+CONSTANT = 1
+
+class B(object):
+    def foo(self):
+        return CONSTANT
+"""
+        SAMPLE_CODE2 = """
+CONSTANT = 2
+
+def __xreload_old_new__(namespace, name, old, new):
+    if name == 'CONSTANT':
+        namespace[name] = new
+
+class B(object):
+    def foo(self):
+        return CONSTANT
+"""
+
+        self.make_mod(sample=SAMPLE_CODE1)
+        import x
+        foo = x.B().foo
+        self.assertEqual(foo(), 1)
+        self.make_mod(sample=SAMPLE_CODE2)
+        pydevd_reload.xreload(x)
+        self.assertEqual(foo(), 2) #Actually updated it now!
+
+
+    def test_reload_custom_code_after_changes(self):
+        SAMPLE_CODE1 = """
+CONSTANT = 1
+
+class B(object):
+    def foo(self):
+        return CONSTANT
+"""
+        SAMPLE_CODE2 = """
+CONSTANT = 1
+
+def __xreload_after_reload_update__(namespace):
+    namespace['CONSTANT'] = 2
+
+class B(object):
+    def foo(self):
+        return CONSTANT
+"""
+
+        self.make_mod(sample=SAMPLE_CODE1)
+        import x
+        foo = x.B().foo
+        self.assertEqual(foo(), 1)
+        self.make_mod(sample=SAMPLE_CODE2)
+        pydevd_reload.xreload(x)
+        self.assertEqual(foo(), 2) #Actually updated it now!
+
+
+    def test_reload_custom_code_after_changes_in_class(self):
+        SAMPLE_CODE1 = """
+
+class B(object):
+    CONSTANT = 1
+
+    def foo(self):
+        return self.CONSTANT
+"""
+        SAMPLE_CODE2 = """
+
+
+class B(object):
+    CONSTANT = 1
+
+    @classmethod
+    def __xreload_after_reload_update__(cls):
+        cls.CONSTANT = 2
+
+    def foo(self):
+        return self.CONSTANT
+"""
+
+        self.make_mod(sample=SAMPLE_CODE1)
+        import x
+        foo = x.B().foo
+        self.assertEqual(foo(), 1)
+        self.make_mod(sample=SAMPLE_CODE2)
+        pydevd_reload.xreload(x)
+        self.assertEqual(foo(), 2) #Actually updated it now!
+
+
+    def test_update_constant_with_custom_code(self):
+        SAMPLE_CODE1 = """
+
+class B(object):
+    CONSTANT = 1
+
+    def foo(self):
+        return self.CONSTANT
+"""
+        SAMPLE_CODE2 = """
+
+
+class B(object):
+
+    CONSTANT = 2
+
+    def __xreload_old_new__(cls, name, old, new):
+        if name == 'CONSTANT':
+            cls.CONSTANT = new
+    __xreload_old_new__ = classmethod(__xreload_old_new__)
+
+    def foo(self):
+        return self.CONSTANT
+"""
+
+        self.make_mod(sample=SAMPLE_CODE1)
+        import x
+        foo = x.B().foo
+        self.assertEqual(foo(), 1)
+        self.make_mod(sample=SAMPLE_CODE2)
+        pydevd_reload.xreload(x)
+        self.assertEqual(foo(), 2) #Actually updated it now!
+
+
+    def test_update_with_slots(self):
+        SAMPLE_CODE1 = """
+class B(object):
+
+    __slots__ = ['bar']
+
+"""
+        SAMPLE_CODE2 = """
+class B(object):
+
+    __slots__ = ['bar', 'foo']
+
+    def m1(self):
+        self.bar = 10
+        return 1
+
+"""
+
+        self.make_mod(sample=SAMPLE_CODE1)
+        import x
+        B = x.B
+        self.make_mod(sample=SAMPLE_CODE2)
+        pydevd_reload.xreload(x)
+        b = B()
+        self.assertEqual(1, b.m1())
+        self.assertEqual(10, b.bar)
+        self.assertRaises(Exception, setattr, b, 'foo', 20) #__slots__ can't be updated
+
+
+
+
+if __name__ == "__main__":
+#     import sys;sys.argv = ['', 'Test.test_reload_custom_code_after_changes_in_class']
+    unittest.main()
diff --git a/python/helpers/pydev/tests/__not_in_default_pythonpath.txt b/python/helpers/pydev/tests/__not_in_default_pythonpath.txt
new file mode 100644
index 0000000..29cdc5b
--- /dev/null
+++ b/python/helpers/pydev/tests/__not_in_default_pythonpath.txt
@@ -0,0 +1 @@
+(no __init__.py file)
\ No newline at end of file
diff --git a/python/helpers/pydev/tests/check_pydevconsole.py b/python/helpers/pydev/tests/check_pydevconsole.py
new file mode 100644
index 0000000..7d1b7ee
--- /dev/null
+++ b/python/helpers/pydev/tests/check_pydevconsole.py
@@ -0,0 +1,105 @@
+import sys
+import os
+
+#Put pydevconsole in the path.
+sys.argv[0] = os.path.dirname(sys.argv[0]) 
+sys.path.insert(1, os.path.join(os.path.dirname(sys.argv[0])))
+
+print('Running tests with:', sys.executable)
+print('PYTHONPATH:')
+print('\n'.join(sorted(sys.path)))
+
+import threading
+import unittest
+
+import pydevconsole
+from pydev_imports import xmlrpclib, SimpleXMLRPCServer
+
+try:
+    raw_input
+    raw_input_name = 'raw_input'
+except NameError:
+    raw_input_name = 'input'
+
+#=======================================================================================================================
+# Test
+#=======================================================================================================================
+class Test(unittest.TestCase):
+
+    
+    def startClientThread(self, client_port):
+        class ClientThread(threading.Thread):
+            def __init__(self, client_port):
+                threading.Thread.__init__(self)
+                self.client_port = client_port
+                
+            def run(self):
+                class HandleRequestInput:
+                    def RequestInput(self):
+                        return 'RequestInput: OK'
+                
+                handle_request_input = HandleRequestInput()
+                
+                import pydev_localhost
+                print('Starting client with:', pydev_localhost.get_localhost(), self.client_port)
+                client_server = SimpleXMLRPCServer((pydev_localhost.get_localhost(), self.client_port), logRequests=False)
+                client_server.register_function(handle_request_input.RequestInput)
+                client_server.serve_forever()
+                
+        client_thread = ClientThread(client_port)
+        client_thread.setDaemon(True)
+        client_thread.start()
+        return client_thread
+
+        
+    def getFreeAddresses(self):
+        import socket
+        s = socket.socket()
+        s.bind(('', 0))
+        port0 = s.getsockname()[1]
+        
+        s1 = socket.socket()
+        s1.bind(('', 0))
+        port1 = s1.getsockname()[1]
+        s.close()
+        s1.close()
+        return port0, port1
+        
+        
+    def testServer(self):
+        client_port, server_port = self.getFreeAddresses()
+        class ServerThread(threading.Thread):
+            def __init__(self, client_port, server_port):
+                threading.Thread.__init__(self)
+                self.client_port = client_port
+                self.server_port = server_port
+                
+            def run(self):
+                import pydev_localhost
+                print('Starting server with:', pydev_localhost.get_localhost(), self.server_port, self.client_port)
+                pydevconsole.StartServer(pydev_localhost.get_localhost(), self.server_port, self.client_port)
+        server_thread = ServerThread(client_port, server_port)
+        server_thread.setDaemon(True)
+        server_thread.start()
+
+        client_thread = self.startClientThread(client_port) #@UnusedVariable
+        
+        import time
+        time.sleep(.3) #let's give it some time to start the threads
+        
+        import pydev_localhost
+        server = xmlrpclib.Server('http://%s:%s' % (pydev_localhost.get_localhost(), server_port))
+        server.addExec("import sys; print('Running with: %s %s' % (sys.executable or sys.platform, sys.version))")
+        server.addExec('class Foo:')
+        server.addExec('    pass')
+        server.addExec('')
+        server.addExec('foo = Foo()')
+        server.addExec('a = %s()' % raw_input_name)
+        server.addExec('print (a)')
+        
+#=======================================================================================================================
+# main        
+#=======================================================================================================================
+if __name__ == '__main__':
+    unittest.main()
+
diff --git a/python/helpers/pydev/tests/test_get_referrers.py b/python/helpers/pydev/tests/test_get_referrers.py
new file mode 100644
index 0000000..7fc8514
--- /dev/null
+++ b/python/helpers/pydev/tests/test_get_referrers.py
@@ -0,0 +1,139 @@
+import os.path
+import sys
+import threading
+import time
+
+IS_JYTHON = sys.platform.find('java') != -1
+
+try:
+    this_file_name = __file__
+except NameError:
+    # stupid jython. plain old __file__ isnt working for some reason
+    import test_runfiles  #@UnresolvedImport - importing the module itself
+    this_file_name = test_runfiles.__file__
+
+
+desired_runfiles_path = os.path.normpath(os.path.dirname(this_file_name) + "/..")
+sys.path.insert(0, desired_runfiles_path)
+
+import unittest
+import pydevd_referrers
+from pydev_imports import StringIO
+
+#=======================================================================================================================
+# Test
+#=======================================================================================================================
+class Test(unittest.TestCase):
+
+
+    def testGetReferrers1(self):
+
+        container = []
+        contained = [1, 2]
+        container.append(0)
+        container.append(contained)
+
+        # Ok, we have the contained in this frame and inside the given list (which on turn is in this frame too).
+        # we should skip temporary references inside the get_referrer_info.
+        result = pydevd_referrers.get_referrer_info(contained)
+        assert 'list[1]' in result
+        pydevd_referrers.print_referrers(contained, stream=StringIO())
+
+    def testGetReferrers2(self):
+
+        class MyClass(object):
+            def __init__(self):
+                pass
+
+        contained = [1, 2]
+        obj = MyClass()
+        obj.contained = contained
+        del contained
+
+        # Ok, we have the contained in this frame and inside the given list (which on turn is in this frame too).
+        # we should skip temporary references inside the get_referrer_info.
+        result = pydevd_referrers.get_referrer_info(obj.contained)
+        assert 'found_as="contained"' in result
+        assert 'MyClass' in result
+
+
+    def testGetReferrers3(self):
+
+        class MyClass(object):
+            def __init__(self):
+                pass
+
+        contained = [1, 2]
+        obj = MyClass()
+        obj.contained = contained
+        del contained
+
+        # Ok, we have the contained in this frame and inside the given list (which on turn is in this frame too).
+        # we should skip temporary references inside the get_referrer_info.
+        result = pydevd_referrers.get_referrer_info(obj.contained)
+        assert 'found_as="contained"' in result
+        assert 'MyClass' in result
+
+
+    def testGetReferrers4(self):
+
+        class MyClass(object):
+            def __init__(self):
+                pass
+
+        obj = MyClass()
+        obj.me = obj
+
+        # Let's see if we detect the cycle...
+        result = pydevd_referrers.get_referrer_info(obj)
+        assert 'found_as="me"' in result  #Cyclic ref
+
+
+    def testGetReferrers5(self):
+        container = dict(a=[1])
+
+        # Let's see if we detect the cycle...
+        result = pydevd_referrers.get_referrer_info(container['a'])
+        assert 'testGetReferrers5' not in result  #I.e.: NOT in the current method
+        assert 'found_as="a"' in result
+        assert 'dict' in result
+        assert str(id(container)) in result
+
+
+    def testGetReferrers6(self):
+        container = dict(a=[1])
+
+        def should_appear(obj):
+            # Let's see if we detect the cycle...
+            return pydevd_referrers.get_referrer_info(obj)
+
+        result = should_appear(container['a'])
+        assert 'should_appear' in result
+
+
+    def testGetReferrers7(self):
+
+        class MyThread(threading.Thread):
+            def run(self):
+                #Note: we do that because if we do
+                self.frame = sys._getframe()
+
+        t = MyThread()
+        t.start()
+        while not hasattr(t, 'frame'):
+            time.sleep(0.01)
+
+        result = pydevd_referrers.get_referrer_info(t.frame)
+        assert 'MyThread' in result
+
+
+if __name__ == "__main__":
+    #this is so that we can run it frem the jython tests -- because we don't actually have an __main__ module
+    #(so, it won't try importing the __main__ module)
+    try:
+        import gc
+        gc.get_referrers(unittest)
+    except:
+        pass
+    else:
+        unittest.TextTestRunner().run(unittest.makeSuite(Test))
diff --git a/python/helpers/pydev/tests/test_jyserver.py b/python/helpers/pydev/tests/test_jyserver.py
new file mode 100644
index 0000000..8765400
--- /dev/null
+++ b/python/helpers/pydev/tests/test_jyserver.py
@@ -0,0 +1,165 @@
+'''
+@author Fabio Zadrozny 
+'''
+import sys
+import unittest
+import socket
+import urllib
+
+
+IS_JYTHON = sys.platform.find('java') != -1
+
+if IS_JYTHON:
+    import os
+    
+    #make it as if we were executing from the directory above this one (so that we can use jycompletionserver
+    #without the need for it being in the pythonpath)
+    sys.argv[0] = os.path.dirname(sys.argv[0]) 
+    #twice the dirname to get the previous level from this file.
+    sys.path.insert(1, os.path.join(os.path.dirname(sys.argv[0])))
+    
+    import pycompletionserver as jycompletionserver
+    
+    
+    DEBUG = 0
+
+def dbg(s):
+    if DEBUG:
+        sys.stdout.write('TEST %s\n' % s)
+
+class Test(unittest.TestCase):
+
+    def setUp(self):
+        unittest.TestCase.setUp(self)
+
+    def tearDown(self):
+        unittest.TestCase.tearDown(self)
+    
+    def testIt(self):
+        dbg('ok')
+        
+    def testMessage(self):
+        t = jycompletionserver.T(0)
+        
+        l = []
+        l.append(('Def', 'description'  , 'args'))
+        l.append(('Def1', 'description1', 'args1'))
+        l.append(('Def2', 'description2', 'args2'))
+        
+        msg = t.processor.formatCompletionMessage('test_jyserver.py', l)
+        
+        self.assertEquals('@@COMPLETIONS(test_jyserver.py,(Def,description,args),(Def1,description1,args1),(Def2,description2,args2))END@@', msg)
+        
+        l = []
+        l.append(('Def', 'desc,,r,,i()ption', ''))
+        l.append(('Def(1', 'descriptio(n1', ''))
+        l.append(('De,f)2', 'de,s,c,ription2', ''))
+        msg = t.processor.formatCompletionMessage(None, l)
+        expected = '@@COMPLETIONS(None,(Def,desc%2C%2Cr%2C%2Ci%28%29ption, ),(Def%281,descriptio%28n1, ),(De%2Cf%292,de%2Cs%2Cc%2Cription2, ))END@@'
+        
+        self.assertEquals(expected, msg)
+
+
+
+
+
+
+    def testCompletionSocketsAndMessages(self):
+        dbg('testCompletionSocketsAndMessages')
+        t, socket = self.createConnections()
+        self.socket = socket
+        dbg('connections created')
+        
+        try:
+            #now that we have the connections all set up, check the code completion messages.
+            msg = urllib.quote_plus('math')
+
+            toWrite = '@@IMPORTS:%sEND@@' % msg
+            dbg('writing' + str(toWrite))
+            socket.send(toWrite)  #math completions
+            completions = self.readMsg()
+            dbg(urllib.unquote_plus(completions))
+            
+            start = '@@COMPLETIONS('
+            self.assert_(completions.startswith(start), '%s DOESNT START WITH %s' % (completions, start))
+            self.assert_(completions.find('@@COMPLETIONS') != -1)
+            self.assert_(completions.find('END@@') != -1)
+
+
+            msg = urllib.quote_plus('__builtin__.str')
+            toWrite = '@@IMPORTS:%sEND@@' % msg
+            dbg('writing' + str(toWrite))
+            socket.send(toWrite)  #math completions
+            completions = self.readMsg()
+            dbg(urllib.unquote_plus(completions))
+            
+            start = '@@COMPLETIONS('
+            self.assert_(completions.startswith(start), '%s DOESNT START WITH %s' % (completions, start))
+            self.assert_(completions.find('@@COMPLETIONS') != -1)
+            self.assert_(completions.find('END@@') != -1)
+
+
+        
+        finally:
+            try:
+                self.sendKillMsg(socket)
+                
+        
+                while not t.ended:
+                    pass  #wait until it receives the message and quits.
+        
+                    
+                socket.close()
+            except:
+                pass
+
+
+
+
+    def createConnections(self, p1=50001):
+        '''
+        Creates the connections needed for testing.
+        '''
+        t = jycompletionserver.T(p1)
+        
+        t.start()
+
+        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        server.bind((jycompletionserver.HOST, p1))
+        server.listen(1)
+
+        sock, _addr = server.accept()
+
+        return t, sock
+        
+
+    def readMsg(self):
+        msg = '@@PROCESSING_END@@'
+        while msg.startswith('@@PROCESSING'):
+            msg = self.socket.recv(1024)
+            if msg.startswith('@@PROCESSING:'):
+                dbg('Status msg:' + str(msg))
+        
+        while msg.find('END@@') == -1:
+            msg += self.socket.recv(1024)
+        
+        return msg
+        
+    def sendKillMsg(self, socket):
+        socket.send(jycompletionserver.MSG_KILL_SERVER)
+        
+    
+
+
+#"C:\Program Files\Java\jdk1.5.0_04\bin\java.exe" -Dpython.path="C:\bin\jython21\Lib";"C:\bin\jython21";"C:\Program Files\Java\jdk1.5.0_04\jre\lib\rt.jar" -classpath C:/bin/jython21/jython.jar org.python.util.jython D:\eclipse_workspace\org.python.pydev\pysrc\pycompletionserver.py 53795 58659
+#
+#"C:\Program Files\Java\jdk1.5.0_04\bin\java.exe" -Dpython.path="C:\bin\jython21\Lib";"C:\bin\jython21";"C:\Program Files\Java\jdk1.5.0_04\jre\lib\rt.jar" -classpath C:/bin/jython21/jython.jar org.python.util.jython D:\eclipse_workspace\org.python.pydev\pysrc\tests\test_jyserver.py
+#
+#"C:\Program Files\Java\jdk1.5.0_04\bin\java.exe" -Dpython.path="C:\bin\jython21\Lib";"C:\bin\jython21";"C:\Program Files\Java\jdk1.5.0_04\jre\lib\rt.jar" -classpath C:/bin/jython21/jython.jar org.python.util.jython d:\runtime-workbench-workspace\jython_test\src\test.py        
+if __name__ == '__main__':
+    if IS_JYTHON:
+        suite = unittest.makeSuite(Test)
+        unittest.TextTestRunner(verbosity=1).run(suite)
+    else:
+        sys.stdout.write('Not running jython tests for non-java platform: %s' % sys.platform)
+
diff --git a/python/helpers/pydev/tests/test_jysimpleTipper.py b/python/helpers/pydev/tests/test_jysimpleTipper.py
new file mode 100644
index 0000000..4a75563
--- /dev/null
+++ b/python/helpers/pydev/tests/test_jysimpleTipper.py
@@ -0,0 +1,255 @@
+#line to run:
+#java -classpath D:\bin\jython-2.1\jython.jar;D:\bin\eclipse331_1\plugins\org.junit_3.8.2.v200706111738\junit.jar;D:\bin\eclipse331_1\plugins\org.apache.ant_1.7.0.v200706080842\lib\ant.jar org.python.util.jython w:\org.python.pydev\pysrc\tests\test_jysimpleTipper.py
+
+import unittest
+import os
+import sys
+#make it as if we were executing from the directory above this one (so that we can use pycompletionserver
+#without the need for it being in the pythonpath)
+sys.argv[0] = os.path.dirname(sys.argv[0]) 
+#twice the dirname to get the previous level from this file.
+sys.path.insert(1, os.path.join(os.path.dirname(sys.argv[0])))
+
+#this does not work (they must be in the system pythonpath)
+#sys.path.insert(1, r"D:\bin\eclipse321\plugins\org.junit_3.8.1\junit.jar" ) #some late loading jar tests
+#sys.path.insert(1, r"D:\bin\eclipse331_1\plugins\org.apache.ant_1.7.0.v200706080842\lib\ant.jar" ) #some late loading jar tests
+
+if sys.platform.find('java') != -1:
+    from _pydev_jy_imports_tipper import ismethod
+    from _pydev_jy_imports_tipper import isclass
+    from _pydev_jy_imports_tipper import dirObj
+    import _pydev_jy_imports_tipper
+    from java.lang.reflect import Method #@UnresolvedImport
+    from java.lang import System #@UnresolvedImport
+    from java.lang import String #@UnresolvedImport
+    from java.lang.System import arraycopy #@UnresolvedImport
+    from java.lang.System import out #@UnresolvedImport
+    import java.lang.String #@UnresolvedImport
+
+__DBG = 0
+def dbg(s):
+    if __DBG:
+        sys.stdout.write('%s\n' % (s,))
+        
+
+
+class TestMod(unittest.TestCase):
+    
+    def assertArgs(self, tok, args, tips):
+        for a in tips:
+            if tok == a[0]:
+                self.assertEquals(args, a[2])
+                return
+        raise AssertionError('%s not in %s', tok, tips)
+
+    def assertIn(self, tok, tips):
+        self.assertEquals(4, len(tips[0]))
+        for a in tips:
+            if tok == a[0]:
+                return a
+        s = ''
+        for a in tips:
+            s += str(a)
+            s += '\n'
+        raise AssertionError('%s not in %s' % (tok, s))
+
+    def testImports1a(self):
+        f, tip = _pydev_jy_imports_tipper.GenerateTip('java.util.HashMap')
+        assert f.endswith('rt.jar')
+
+    def testImports1c(self):
+        f, tip = _pydev_jy_imports_tipper.GenerateTip('java.lang.Class')
+        assert f.endswith('rt.jar')
+        
+    def testImports1b(self):
+        try:
+            f, tip = _pydev_jy_imports_tipper.GenerateTip('__builtin__.m')
+            self.fail('err')
+        except:
+            pass
+
+    def testImports1(self):
+        f, tip = _pydev_jy_imports_tipper.GenerateTip('junit.framework.TestCase')
+        assert f.endswith('junit.jar')
+        ret = self.assertIn('assertEquals', tip)
+#        self.assertEquals('', ret[2])
+        
+    def testImports2(self):
+        f, tip = _pydev_jy_imports_tipper.GenerateTip('junit.framework')
+        assert f.endswith('junit.jar')
+        ret = self.assertIn('TestCase', tip)
+        self.assertEquals('', ret[2])
+        
+    def testImports2a(self):
+        f, tip = _pydev_jy_imports_tipper.GenerateTip('org.apache.tools.ant')
+        assert f.endswith('ant.jar')
+        ret = self.assertIn('Task', tip)
+        self.assertEquals('', ret[2])
+        
+    def testImports3(self):
+        f, tip = _pydev_jy_imports_tipper.GenerateTip('os')
+        assert f.endswith('os.py')
+        ret = self.assertIn('path', tip)
+        self.assertEquals('', ret[2])
+        
+    def testTipOnString(self):
+        f, tip = _pydev_jy_imports_tipper.GenerateTip('string')
+        self.assertIn('join', tip)
+        self.assertIn('uppercase', tip)
+        
+    def testImports(self):
+        tip = _pydev_jy_imports_tipper.GenerateTip('__builtin__')[1]
+        self.assertIn('tuple'          , tip)
+        self.assertIn('RuntimeError'   , tip)
+        self.assertIn('RuntimeWarning' , tip)
+
+    def testImports5(self):
+        f, tip = _pydev_jy_imports_tipper.GenerateTip('java.lang')
+        assert f.endswith('rt.jar')
+        tup = self.assertIn('String' , tip)
+        self.assertEquals(str(_pydev_jy_imports_tipper.TYPE_CLASS), tup[3])
+        
+        tip = _pydev_jy_imports_tipper.GenerateTip('java')[1]
+        tup = self.assertIn('lang' , tip)
+        self.assertEquals(str(_pydev_jy_imports_tipper.TYPE_IMPORT), tup[3])
+        
+        tip = _pydev_jy_imports_tipper.GenerateTip('java.lang.String')[1]
+        tup = self.assertIn('indexOf'          , tip)
+        self.assertEquals(str(_pydev_jy_imports_tipper.TYPE_FUNCTION), tup[3])
+
+        tip = _pydev_jy_imports_tipper.GenerateTip('java.lang.String')[1]
+        tup = self.assertIn('charAt'          , tip)
+        self.assertEquals(str(_pydev_jy_imports_tipper.TYPE_FUNCTION), tup[3])
+        self.assertEquals('(int)', tup[2])
+
+        tup = self.assertIn('format'          , tip)
+        self.assertEquals(str(_pydev_jy_imports_tipper.TYPE_FUNCTION), tup[3])
+        self.assertEquals('(string, objectArray)', tup[2])
+        self.assert_(tup[1].find('[Ljava.lang.Object;') == -1)
+
+        tup = self.assertIn('getBytes'          , tip)
+        self.assertEquals(str(_pydev_jy_imports_tipper.TYPE_FUNCTION), tup[3])
+        self.assert_(tup[1].find('[B') == -1)
+        self.assert_(tup[1].find('byte[]') != -1)
+
+        f, tip = _pydev_jy_imports_tipper.GenerateTip('__builtin__.str')
+        assert f.endswith('jython.jar')
+        self.assertIn('find'          , tip)
+
+        f, tip = _pydev_jy_imports_tipper.GenerateTip('__builtin__.dict')
+        assert f.endswith('jython.jar')
+        self.assertIn('get'          , tip)
+
+
+class TestSearch(unittest.TestCase):
+
+    def testSearchOnJython(self):
+        self.assertEqual('javaos.py', _pydev_jy_imports_tipper.Search('os')[0][0].split(os.sep)[-1])
+        self.assertEqual(0, _pydev_jy_imports_tipper.Search('os')[0][1])
+        
+        self.assertEqual('javaos.py', _pydev_jy_imports_tipper.Search('os.makedirs')[0][0].split(os.sep)[-1])
+        self.assertNotEqual(0, _pydev_jy_imports_tipper.Search('os.makedirs')[0][1])
+        
+        #print _pydev_jy_imports_tipper.Search('os.makedirs')
+
+class TestCompl(unittest.TestCase):
+
+    def setUp(self):
+        unittest.TestCase.setUp(self)
+
+    def tearDown(self):
+        unittest.TestCase.tearDown(self)
+
+    def testGettingInfoOnJython(self):
+        
+        dbg('\n\n--------------------------- java')
+        assert not ismethod(java)[0]
+        assert not isclass(java)
+        assert _pydev_jy_imports_tipper.ismodule(java)
+            
+        dbg('\n\n--------------------------- java.lang')
+        assert not ismethod(java.lang)[0]
+        assert not isclass(java.lang)
+        assert _pydev_jy_imports_tipper.ismodule(java.lang)
+            
+        dbg('\n\n--------------------------- Method')
+        assert not ismethod(Method)[0]
+        assert isclass(Method)
+            
+        dbg('\n\n--------------------------- System')
+        assert not ismethod(System)[0]
+        assert isclass(System)
+            
+        dbg('\n\n--------------------------- String')
+        assert not ismethod(System)[0]
+        assert isclass(String)
+        assert len(dirObj(String)) > 10
+            
+        dbg('\n\n--------------------------- arraycopy')
+        isMet = ismethod(arraycopy)
+        assert isMet[0]
+        assert isMet[1][0].basicAsStr() == "function:arraycopy args=['java.lang.Object', 'int', 'java.lang.Object', 'int', 'int'], varargs=None, kwargs=None, docs:None"
+        assert not isclass(arraycopy)
+            
+        dbg('\n\n--------------------------- out')
+        isMet = ismethod(out)
+        assert not isMet[0]
+        assert not isclass(out)
+            
+        dbg('\n\n--------------------------- out.println')
+        isMet = ismethod(out.println) #@UndefinedVariable
+        assert isMet[0]
+        assert len(isMet[1]) == 10
+        self.assertEquals(isMet[1][0].basicAsStr(), "function:println args=[], varargs=None, kwargs=None, docs:None")
+        assert isMet[1][1].basicAsStr() == "function:println args=['long'], varargs=None, kwargs=None, docs:None"
+        assert not isclass(out.println) #@UndefinedVariable
+        
+        dbg('\n\n--------------------------- str')
+        isMet = ismethod(str)
+        #the code below should work, but is failing on jython 22a1
+        #assert isMet[0]
+        #assert isMet[1][0].basicAsStr() == "function:str args=['org.python.core.PyObject'], varargs=None, kwargs=None, docs:None"
+        assert not isclass(str)
+        
+        
+        def met1():
+            a = 3
+            return a
+        
+        dbg('\n\n--------------------------- met1')
+        isMet = ismethod(met1)
+        assert isMet[0]
+        assert isMet[1][0].basicAsStr() == "function:met1 args=[], varargs=None, kwargs=None, docs:None"
+        assert not isclass(met1)
+        
+        def met2(arg1, arg2, *vararg, **kwarg):
+            '''docmet2'''
+            
+            a = 1
+            return a
+        
+        dbg('\n\n--------------------------- met2')
+        isMet = ismethod(met2)
+        assert isMet[0]
+        assert isMet[1][0].basicAsStr() == "function:met2 args=['arg1', 'arg2'], varargs=vararg, kwargs=kwarg, docs:docmet2"
+        assert not isclass(met2)
+        
+
+
+if __name__ == '__main__':
+    if sys.platform.find('java') != -1:
+        #Only run if jython
+        suite = unittest.makeSuite(TestCompl)
+        suite2 = unittest.makeSuite(TestMod)
+        suite3 = unittest.makeSuite(TestSearch)
+        
+        unittest.TextTestRunner(verbosity=1).run(suite)
+        unittest.TextTestRunner(verbosity=1).run(suite2)
+        unittest.TextTestRunner(verbosity=1).run(suite3)
+        
+#        suite.addTest(Test('testCase12'))
+#        suite = unittest.TestSuite()
+#        unittest.TextTestRunner(verbosity=1).run(suite)
+
+    else:
+        sys.stdout.write('Not running jython tests for non-java platform: %s' % sys.platform)
diff --git a/python/helpers/pydev/tests/test_pydev_ipython_010.py b/python/helpers/pydev/tests/test_pydev_ipython_010.py
new file mode 100644
index 0000000..5ce1dc3
--- /dev/null
+++ b/python/helpers/pydev/tests/test_pydev_ipython_010.py
@@ -0,0 +1,80 @@
+#TODO: This test no longer works (check if it should be fixed or removed altogether).
+
+#import unittest
+#import sys
+#import os
+##make it as if we were executing from the directory above this one
+#sys.argv[0] = os.path.dirname(sys.argv[0])
+##twice the dirname to get the previous level from this file.
+#sys.path.insert(1, os.path.join(os.path.dirname(sys.argv[0])))
+#
+#from pydev_localhost import get_localhost
+#
+#
+#IS_JYTHON = sys.platform.find('java') != -1
+#
+##=======================================================================================================================
+## TestCase
+##=======================================================================================================================
+#class TestCase(unittest.TestCase):
+#
+#    def setUp(self):
+#        unittest.TestCase.setUp(self)
+#
+#    def tearDown(self):
+#        unittest.TestCase.tearDown(self)
+#
+#    def testIPython(self):
+#        try:
+#            from pydev_ipython_console import PyDevFrontEnd
+#        except:
+#            if IS_JYTHON:
+#                return
+#        front_end = PyDevFrontEnd(get_localhost(), 0)
+#
+#        front_end.input_buffer = 'if True:'
+#        self.assert_(not front_end._on_enter())
+#
+#        front_end.input_buffer = 'if True:\n' + \
+#            front_end.continuation_prompt() + '    a = 10\n'
+#        self.assert_(not front_end._on_enter())
+#
+#
+#        front_end.input_buffer = 'if True:\n' + \
+#            front_end.continuation_prompt() + '    a = 10\n\n'
+#        self.assert_(front_end._on_enter())
+#
+#
+##        front_end.input_buffer = '  print a'
+##        self.assert_(not front_end._on_enter())
+##        front_end.input_buffer = ''
+##        self.assert_(front_end._on_enter())
+#
+#
+##        front_end.input_buffer = 'a.'
+##        front_end.complete_current_input()
+##        front_end.input_buffer = 'if True:'
+##        front_end._on_enter()
+#        front_end.input_buffer = 'a = 30'
+#        front_end._on_enter()
+#        front_end.input_buffer = 'print a'
+#        front_end._on_enter()
+#        front_end.input_buffer = 'a?'
+#        front_end._on_enter()
+#        print front_end.complete('%')
+#        print front_end.complete('%e')
+#        print front_end.complete('cd c:/t')
+#        print front_end.complete('cd c:/temp/')
+##        front_end.input_buffer = 'print raw_input("press enter\\n")'
+##        front_end._on_enter()
+##
+#
+##=======================================================================================================================
+## main
+##=======================================================================================================================
+#if __name__ == '__main__':
+#    if sys.platform.find('java') == -1:
+#        #IPython not available for Jython
+#        unittest.main()
+#    else:
+#        print('not supported on Jython')
diff --git a/python/helpers/pydev/tests/test_pydev_ipython_011.py b/python/helpers/pydev/tests/test_pydev_ipython_011.py
new file mode 100644
index 0000000..3cfa70f
--- /dev/null
+++ b/python/helpers/pydev/tests/test_pydev_ipython_011.py
@@ -0,0 +1,193 @@
+import sys
+import unittest
+import threading
+import os
+from nose.tools import eq_
+from pydev_imports import StringIO, SimpleXMLRPCServer
+from pydev_localhost import get_localhost
+from pydev_console_utils import StdIn
+import socket
+
+# make it as if we were executing from the directory above this one
+sys.argv[0] = os.path.dirname(sys.argv[0])
+# twice the dirname to get the previous level from this file.
+sys.path.insert(1, os.path.join(os.path.dirname(sys.argv[0])))
+
+# PyDevFrontEnd depends on singleton in IPython, so you
+# can't make multiple versions. So we reuse front_end for
+# all the tests
+
+orig_stdout = sys.stdout
+orig_stderr = sys.stderr
+
+stdout = sys.stdout = StringIO()
+stderr = sys.stderr = StringIO()
+
+from pydev_ipython_console_011 import PyDevFrontEnd
+s = socket.socket()
+s.bind(('', 0))
+client_port = s.getsockname()[1]
+s.close()
+front_end = PyDevFrontEnd(get_localhost(), client_port)
+
+
+def addExec(code, expected_more=False):
+    more = front_end.addExec(code)
+    eq_(expected_more, more)
+
+class TestBase(unittest.TestCase):
+    def setUp(self):
+        front_end.input_splitter.reset()
+        stdout.truncate(0)
+        stdout.seek(0)
+        stderr.truncate(0)
+        stderr.seek(0)
+    def tearDown(self):
+        pass
+
+
+class TestPyDevFrontEnd(TestBase):
+    def testAddExec_1(self):
+        addExec('if True:', True)
+    def testAddExec_2(self):
+        addExec('if True:\n    testAddExec_a = 10\n', True)
+    def testAddExec_3(self):
+        assert 'testAddExec_a' not in front_end.getNamespace()
+        addExec('if True:\n    testAddExec_a = 10\n\n')
+        assert 'testAddExec_a' in front_end.getNamespace()
+        eq_(front_end.getNamespace()['testAddExec_a'], 10)
+
+    def testGetNamespace(self):
+        assert 'testGetNamespace_a' not in front_end.getNamespace()
+        addExec('testGetNamespace_a = 10')
+        assert 'testGetNamespace_a' in front_end.getNamespace()
+        eq_(front_end.getNamespace()['testGetNamespace_a'], 10)
+
+    def testComplete(self):
+        unused_text, matches = front_end.complete('%')
+        assert len(matches) > 1, 'at least one magic should appear in completions'
+
+    def testCompleteDoesNotDoPythonMatches(self):
+        # Test that IPython's completions do not do the things that
+        # PyDev's completions will handle
+        addExec('testComplete_a = 5')
+        addExec('testComplete_b = 10')
+        addExec('testComplete_c = 15')
+        unused_text, matches = front_end.complete('testComplete_')
+        assert len(matches) == 0
+
+    def testGetCompletions_1(self):
+        # Test the merged completions include the standard completions
+        addExec('testComplete_a = 5')
+        addExec('testComplete_b = 10')
+        addExec('testComplete_c = 15')
+        res = front_end.getCompletions('testComplete_', 'testComplete_')
+        matches = [f[0] for f in res]
+        assert len(matches) == 3
+        eq_(set(['testComplete_a', 'testComplete_b', 'testComplete_c']), set(matches))
+
+    def testGetCompletions_2(self):
+        # Test that we get IPython completions in results
+        # we do this by checking kw completion which PyDev does
+        # not do by default
+        addExec('def ccc(ABC=123): pass')
+        res = front_end.getCompletions('ccc(', '')
+        matches = [f[0] for f in res]
+        assert 'ABC=' in matches
+
+    def testGetCompletions_3(self):
+        # Test that magics return IPYTHON magic as type
+        res = front_end.getCompletions('%cd', '%cd')
+        assert len(res) == 1
+        eq_(res[0][3], '12')  # '12' == IToken.TYPE_IPYTHON_MAGIC
+        assert len(res[0][1]) > 100, 'docstring for %cd should be a reasonably long string'
+
+class TestRunningCode(TestBase):
+    def testPrint(self):
+        addExec('print("output")')
+        eq_(stdout.getvalue(), 'output\n')
+
+    def testQuestionMark_1(self):
+        addExec('?')
+        assert len(stdout.getvalue()) > 1000, 'IPython help should be pretty big'
+
+    def testQuestionMark_2(self):
+        addExec('int?')
+        assert stdout.getvalue().find('Convert') != -1
+
+
+    def testGui(self):
+        from pydev_ipython.inputhook import get_inputhook, set_stdin_file
+        set_stdin_file(sys.stdin)
+        assert get_inputhook() is None
+        addExec('%gui tk')
+        # we can't test the GUI works here because we aren't connected to XML-RPC so
+        # nowhere for hook to run
+        assert get_inputhook() is not None
+        addExec('%gui none')
+        assert get_inputhook() is None
+
+    def testHistory(self):
+        ''' Make sure commands are added to IPython's history '''
+        addExec('a=1')
+        addExec('b=2')
+        _ih = front_end.getNamespace()['_ih']
+        eq_(_ih[-1], 'b=2')
+        eq_(_ih[-2], 'a=1')
+
+        addExec('history')
+        hist = stdout.getvalue().split('\n')
+        eq_(hist[-1], '')
+        eq_(hist[-2], 'history')
+        eq_(hist[-3], 'b=2')
+        eq_(hist[-4], 'a=1')
+
+    def testEdit(self):
+        ''' Make sure we can issue an edit command '''
+        called_RequestInput = [False]
+        called_IPythonEditor = [False]
+        def startClientThread(client_port):
+            class ClientThread(threading.Thread):
+                def __init__(self, client_port):
+                    threading.Thread.__init__(self)
+                    self.client_port = client_port
+                def run(self):
+                    class HandleRequestInput:
+                        def RequestInput(self):
+                            called_RequestInput[0] = True
+                            return '\n'
+                        def IPythonEditor(self, name, line):
+                            called_IPythonEditor[0] = (name, line)
+                            return True
+
+                    handle_request_input = HandleRequestInput()
+
+                    import pydev_localhost
+                    client_server = SimpleXMLRPCServer((pydev_localhost.get_localhost(), self.client_port), logRequests=False)
+                    client_server.register_function(handle_request_input.RequestInput)
+                    client_server.register_function(handle_request_input.IPythonEditor)
+                    client_server.serve_forever()
+
+            client_thread = ClientThread(client_port)
+            client_thread.setDaemon(True)
+            client_thread.start()
+            return client_thread
+
+        startClientThread(client_port)
+        orig_stdin = sys.stdin
+        sys.stdin = StdIn(self, get_localhost(), client_port)
+        try:
+            filename = 'made_up_file.py'
+            addExec('%edit ' + filename)
+            eq_(called_IPythonEditor[0], (os.path.abspath(filename), 0))
+            assert called_RequestInput[0], "Make sure the 'wait' parameter has been respected"
+        finally:
+            sys.stdin = orig_stdin
+
+if __name__ == '__main__':
+
+    #Just doing: unittest.main() was not working for me when run directly (not sure why)
+    #And doing it the way below the test with the import: from pydev_ipython.inputhook import get_inputhook, set_stdin_file
+    #is failing (but if I do a Ctrl+F9 in PyDev to run it, it works properly, so, I'm a bit puzzled here).
+    unittest.TextTestRunner(verbosity=1).run(unittest.makeSuite(TestRunningCode))
+    unittest.TextTestRunner(verbosity=1).run(unittest.makeSuite(TestPyDevFrontEnd))
diff --git a/python/helpers/pydev/tests/test_pydevconsole.py b/python/helpers/pydev/tests/test_pydevconsole.py
new file mode 100644
index 0000000..9a9e3ed
--- /dev/null
+++ b/python/helpers/pydev/tests/test_pydevconsole.py
@@ -0,0 +1,231 @@
+import threading
+import unittest
+import sys
+import os
+
+sys.argv[0] = os.path.dirname(sys.argv[0])
+sys.path.insert(1, os.path.join(os.path.dirname(sys.argv[0])))
+import pydevconsole
+from pydev_imports import xmlrpclib, SimpleXMLRPCServer, StringIO
+
+try:
+    raw_input
+    raw_input_name = 'raw_input'
+except NameError:
+    raw_input_name = 'input'
+
+#=======================================================================================================================
+# Test
+#=======================================================================================================================
+class Test(unittest.TestCase):
+
+    def setUp(self):
+        self.original_stdout = sys.stdout
+        sys.stdout = StringIO()
+
+
+    def tearDown(self):
+        ret = sys.stdout  #@UnusedVariable
+        sys.stdout = self.original_stdout
+        #print_ ret.getvalue() -- use to see test output
+
+    def testConsoleHello(self):
+        client_port, _server_port = self.getFreeAddresses()
+        client_thread = self.startClientThread(client_port)  #@UnusedVariable
+        import time
+        time.sleep(.3)  #let's give it some time to start the threads
+
+        import pydev_localhost
+        interpreter = pydevconsole.InterpreterInterface(pydev_localhost.get_localhost(), client_port, server=None)
+
+        (result,) = interpreter.hello("Hello pydevconsole")
+        self.assertEqual(result, "Hello eclipse")
+
+
+    def testConsoleRequests(self):
+        client_port, _server_port = self.getFreeAddresses()
+        client_thread = self.startClientThread(client_port)  #@UnusedVariable
+        import time
+        time.sleep(.3)  #let's give it some time to start the threads
+
+        import pydev_localhost
+        interpreter = pydevconsole.InterpreterInterface(pydev_localhost.get_localhost(), client_port, server=None)
+        interpreter.addExec('class Foo:')
+        interpreter.addExec('   CONSTANT=1')
+        interpreter.addExec('')
+        interpreter.addExec('foo=Foo()')
+        interpreter.addExec('foo.__doc__=None')
+        interpreter.addExec('val = %s()' % (raw_input_name,))
+        interpreter.addExec('50')
+        interpreter.addExec('print (val)')
+        found = sys.stdout.getvalue().split()
+        try:
+            self.assertEqual(['50', 'input_request'], found)
+        except:
+            self.assertEqual(['input_request'], found)  #IPython
+
+        comps = interpreter.getCompletions('foo.', 'foo.')
+        self.assert_(
+            ('CONSTANT', '', '', '3') in comps or ('CONSTANT', '', '', '4') in comps, \
+            'Found: %s' % comps
+        )
+
+        comps = interpreter.getCompletions('"".', '"".')
+        self.assert_(
+            ('__add__', 'x.__add__(y) <==> x+y', '', '3') in comps or
+            ('__add__', '', '', '4') in comps or
+            ('__add__', 'x.__add__(y) <==> x+y\r\nx.__add__(y) <==> x+y', '()', '2') in comps or
+            ('__add__', 'x.\n__add__(y) <==> x+yx.\n__add__(y) <==> x+y', '()', '2'),
+            'Did not find __add__ in : %s' % (comps,)
+        )
+
+
+        completions = interpreter.getCompletions('', '')
+        for c in completions:
+            if c[0] == 'AssertionError':
+                break
+        else:
+            self.fail('Could not find AssertionError')
+
+        completions = interpreter.getCompletions('Assert', 'Assert')
+        for c in completions:
+            if c[0] == 'RuntimeError':
+                self.fail('Did not expect to find RuntimeError there')
+
+        self.assert_(('__doc__', None, '', '3') not in interpreter.getCompletions('foo.CO', 'foo.'))
+
+        comps = interpreter.getCompletions('va', 'va')
+        self.assert_(('val', '', '', '3') in comps or ('val', '', '', '4') in comps)
+
+        interpreter.addExec('s = "mystring"')
+
+        desc = interpreter.getDescription('val')
+        self.assert_(desc.find('str(object) -> string') >= 0 or
+                     desc == "'input_request'" or
+                     desc.find('str(string[, encoding[, errors]]) -> str') >= 0 or
+                     desc.find('str(Char* value)') >= 0 or
+                     desc.find('str(value: Char*)') >= 0,
+                     'Could not find what was needed in %s' % desc)
+
+        desc = interpreter.getDescription('val.join')
+        self.assert_(desc.find('S.join(sequence) -> string') >= 0 or
+                     desc.find('S.join(sequence) -> str') >= 0 or
+                     desc.find('S.join(iterable) -> string') >= 0 or
+                     desc == "<builtin method 'join'>"  or
+                     desc == "<built-in method join of str object>" or
+                     desc.find('str join(str self, list sequence)') >= 0 or
+                     desc.find('S.join(iterable) -> str') >= 0 or
+                     desc.find('join(self: str, sequence: list) -> str') >= 0,
+                     "Could not recognize: %s" % (desc,))
+
+
+    def startClientThread(self, client_port):
+        class ClientThread(threading.Thread):
+            def __init__(self, client_port):
+                threading.Thread.__init__(self)
+                self.client_port = client_port
+            def run(self):
+                class HandleRequestInput:
+                    def RequestInput(self):
+                        return 'input_request'
+
+                handle_request_input = HandleRequestInput()
+
+                import pydev_localhost
+                client_server = SimpleXMLRPCServer((pydev_localhost.get_localhost(), self.client_port), logRequests=False)
+                client_server.register_function(handle_request_input.RequestInput)
+                client_server.serve_forever()
+
+        client_thread = ClientThread(client_port)
+        client_thread.setDaemon(True)
+        client_thread.start()
+        return client_thread
+
+
+    def startDebuggerServerThread(self, debugger_port, socket_code):
+        class DebuggerServerThread(threading.Thread):
+            def __init__(self, debugger_port, socket_code):
+                threading.Thread.__init__(self)
+                self.debugger_port = debugger_port
+                self.socket_code = socket_code
+            def run(self):
+                import socket
+                s = socket.socket()
+                s.bind(('', debugger_port))
+                s.listen(1)
+                socket, unused_addr = s.accept()
+                socket_code(socket)
+
+        debugger_thread = DebuggerServerThread(debugger_port, socket_code)
+        debugger_thread.setDaemon(True)
+        debugger_thread.start()
+        return debugger_thread
+
+
+    def getFreeAddresses(self):
+        import socket
+        s = socket.socket()
+        s.bind(('', 0))
+        port0 = s.getsockname()[1]
+
+        s1 = socket.socket()
+        s1.bind(('', 0))
+        port1 = s1.getsockname()[1]
+        s.close()
+        s1.close()
+
+        if port0 <= 0 or port1 <= 0:
+            #This happens in Jython...
+            from java.net import ServerSocket
+            s0 = ServerSocket(0)
+            port0 = s0.getLocalPort()
+
+            s1 = ServerSocket(0)
+            port1 = s1.getLocalPort()
+
+            s0.close()
+            s1.close()
+
+        assert port0 != port1
+        assert port0 > 0
+        assert port1 > 0
+
+        return port0, port1
+
+
+    def testServer(self):
+        client_port, server_port = self.getFreeAddresses()
+        class ServerThread(threading.Thread):
+            def __init__(self, client_port, server_port):
+                threading.Thread.__init__(self)
+                self.client_port = client_port
+                self.server_port = server_port
+
+            def run(self):
+                import pydev_localhost
+                pydevconsole.StartServer(pydev_localhost.get_localhost(), self.server_port, self.client_port)
+        server_thread = ServerThread(client_port, server_port)
+        server_thread.setDaemon(True)
+        server_thread.start()
+
+        client_thread = self.startClientThread(client_port)  #@UnusedVariable
+
+        import time
+        time.sleep(.3)  #let's give it some time to start the threads
+
+        import pydev_localhost
+        server = xmlrpclib.Server('http://%s:%s' % (pydev_localhost.get_localhost(), server_port))
+        server.addExec('class Foo:')
+        server.addExec('    pass')
+        server.addExec('')
+        server.addExec('foo = Foo()')
+        server.addExec('a = %s()' % (raw_input_name,))
+        server.addExec('print (a)')
+        self.assertEqual(['input_request'], sys.stdout.getvalue().split())
+
+#=======================================================================================================================
+# main
+#=======================================================================================================================
+if __name__ == '__main__':
+    unittest.main()
+
diff --git a/python/helpers/pydev/tests/test_pyserver.py b/python/helpers/pydev/tests/test_pyserver.py
new file mode 100644
index 0000000..a74876b
--- /dev/null
+++ b/python/helpers/pydev/tests/test_pyserver.py
@@ -0,0 +1,173 @@
+'''
+@author Fabio Zadrozny 
+'''
+import sys
+import os
+
+#make it as if we were executing from the directory above this one (so that we can use pycompletionserver
+#without the need for it being in the pythonpath)
+sys.argv[0] = os.path.dirname(sys.argv[0]) 
+#twice the dirname to get the previous level from this file.
+sys.path.insert(1, os.path.join(os.path.dirname(sys.argv[0])))
+
+IS_PYTHON_3K = 0
+if sys.platform.find('java') == -1:
+    
+    
+    try:
+        import inspect
+        import pycompletionserver
+        import socket
+        try:
+            from urllib import quote_plus, unquote_plus
+            def send(s, msg):
+                s.send(msg)
+        except ImportError:
+            IS_PYTHON_3K = 1
+            from urllib.parse import quote_plus, unquote_plus  #Python 3.0
+            def send(s, msg):
+                s.send(bytearray(msg, 'utf-8'))
+    except ImportError:
+        pass  #Not available in jython
+    
+    import unittest
+    
+    class Test(unittest.TestCase):
+    
+        def setUp(self):
+            unittest.TestCase.setUp(self)
+    
+        def tearDown(self):
+            unittest.TestCase.tearDown(self)
+        
+        def testMessage(self):
+            t = pycompletionserver.T(0)
+            
+            l = []
+            l.append(('Def', 'description'  , 'args'))
+            l.append(('Def1', 'description1', 'args1'))
+            l.append(('Def2', 'description2', 'args2'))
+            
+            msg = t.processor.formatCompletionMessage(None, l)
+            self.assertEquals('@@COMPLETIONS(None,(Def,description,args),(Def1,description1,args1),(Def2,description2,args2))END@@', msg)
+            
+            l = []
+            l.append(('Def', 'desc,,r,,i()ption', ''))
+            l.append(('Def(1', 'descriptio(n1', ''))
+            l.append(('De,f)2', 'de,s,c,ription2', ''))
+            msg = t.processor.formatCompletionMessage(None, l)
+            self.assertEquals('@@COMPLETIONS(None,(Def,desc%2C%2Cr%2C%2Ci%28%29ption, ),(Def%281,descriptio%28n1, ),(De%2Cf%292,de%2Cs%2Cc%2Cription2, ))END@@', msg)
+    
+        def createConnections(self, p1=50002):
+            '''
+            Creates the connections needed for testing.
+            '''
+            t = pycompletionserver.T(p1)
+            
+            t.start()
+    
+            server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            server.bind((pycompletionserver.HOST, p1))
+            server.listen(1)  #socket to receive messages.
+    
+            s, addr = server.accept()
+    
+            return t, s
+            
+    
+        def readMsg(self):
+            finish = False
+            msg = ''
+            while finish == False:
+                m = self.socket.recv(1024 * 4)
+                if IS_PYTHON_3K:
+                    m = m.decode('utf-8')
+                if m.startswith('@@PROCESSING'):
+                    sys.stdout.write('Status msg: %s\n' % (msg,))
+                else:
+                    msg += m
+    
+                if msg.find('END@@') != -1:
+                    finish = True
+    
+            return msg
+    
+        def testCompletionSocketsAndMessages(self):
+            t, socket = self.createConnections()
+            self.socket = socket
+            
+            try:
+                #now that we have the connections all set up, check the code completion messages.
+                msg = quote_plus('math')
+                send(socket, '@@IMPORTS:%sEND@@' % msg)  #math completions
+                completions = self.readMsg()
+                #print_ unquote_plus(completions)
+                
+                #math is a builtin and because of that, it starts with None as a file
+                start = '@@COMPLETIONS(None,(__doc__,'
+                start_2 = '@@COMPLETIONS(None,(__name__,'
+                self.assert_(completions.startswith(start) or completions.startswith(start_2), '%s DOESNT START WITH %s' % (completions, (start, start_2)))
+        
+                self.assert_('@@COMPLETIONS' in completions)
+                self.assert_('END@@' in completions)
+    
+    
+                #now, test i
+                msg = quote_plus('__builtin__.list')
+                send(socket, "@@IMPORTS:%s\nEND@@" % msg)
+                found = self.readMsg()
+                self.assert_('sort' in found, 'Could not find sort in: %s' % (found,))
+    
+                #now, test search
+                msg = quote_plus('inspect.ismodule')
+                send(socket, '@@SEARCH%sEND@@' % msg)  #math completions
+                found = self.readMsg()
+                self.assert_('inspect.py' in found)
+                self.assert_('33' in found or '34' in found or '51' in found or '50' in found, 'Could not find 33, 34, 50 or 51 in %s' % found)
+    
+                #now, test search
+                msg = quote_plus('inspect.CO_NEWLOCALS')
+                send(socket, '@@SEARCH%sEND@@' % msg)  #math completions
+                found = self.readMsg()
+                self.assert_('inspect.py' in found)
+                self.assert_('CO_NEWLOCALS' in found)
+                
+                #now, test search
+                msg = quote_plus('inspect.BlockFinder.tokeneater')
+                send(socket, '@@SEARCH%sEND@@' % msg) 
+                found = self.readMsg()
+                self.assert_('inspect.py' in found)
+    #            self.assert_('CO_NEWLOCALS' in found)
+    
+            #reload modules test
+    #        send(socket, '@@RELOAD_MODULES_END@@')
+    #        ok = self.readMsg()
+    #        self.assertEquals('@@MSG_OK_END@@' , ok)
+    #        this test is not executed because it breaks our current enviroment.
+            
+            
+            finally:
+                try:
+                    sys.stdout.write('succedded...sending kill msg\n')
+                    self.sendKillMsg(socket)
+                    
+            
+    #                while not hasattr(t, 'ended'):
+    #                    pass #wait until it receives the message and quits.
+            
+                        
+                    socket.close()
+                    self.socket.close()
+                except:
+                    pass
+            
+        def sendKillMsg(self, socket):
+            socket.send(pycompletionserver.MSG_KILL_SERVER)
+
+        
+if __name__ == '__main__':
+    if sys.platform.find('java') == -1:
+        unittest.main()
+    else:
+        sys.stdout.write('Not running python tests in platform: %s\n' % (sys.platform,))
+
diff --git a/python/helpers/pydev/tests/test_simpleTipper.py b/python/helpers/pydev/tests/test_simpleTipper.py
new file mode 100644
index 0000000..f759ad6
--- /dev/null
+++ b/python/helpers/pydev/tests/test_simpleTipper.py
@@ -0,0 +1,209 @@
+'''
+@author Fabio Zadrozny 
+'''
+import os
+import sys
+#make it as if we were executing from the directory above this one (so that we can use pycompletionserver
+#without the need for it being in the pythonpath)
+#twice the dirname to get the previous level from this file.
+sys.path.insert(1, os.path.split(os.path.split(__file__)[0])[0])
+
+try:
+    import __builtin__ #@UnusedImport
+    BUILTIN_MOD = '__builtin__'
+except ImportError:
+    BUILTIN_MOD = 'builtins'
+
+
+if sys.platform.find('java') == -1:
+    
+    HAS_WX = False
+    
+    import unittest
+    import _pydev_imports_tipper
+    import inspect
+    
+    class Test(unittest.TestCase):
+    
+        def p(self, t):
+            for a in t:
+                sys.stdout.write('%s\n' % (a,))
+     
+        def testImports3(self):
+            tip = _pydev_imports_tipper.GenerateTip('os')
+            ret = self.assertIn('path', tip)
+            self.assertEquals('', ret[2])
+    
+        def testImports2(self):
+            try:
+                tip = _pydev_imports_tipper.GenerateTip('OpenGL.GLUT')
+                self.assertIn('glutDisplayFunc', tip)
+                self.assertIn('glutInitDisplayMode', tip)
+            except ImportError:
+                pass
+    
+        def testImports4(self):
+            try:
+                tip = _pydev_imports_tipper.GenerateTip('mx.DateTime.mxDateTime.mxDateTime')
+                self.assertIn('now', tip)
+            except ImportError:
+                pass
+    
+        def testImports5(self):
+            tip = _pydev_imports_tipper.GenerateTip('__builtin__.list')
+            s = self.assertIn('sort', tip)
+            self.CheckArgs(
+                s, 
+                '(cmp=None, key=None, reverse=False)', 
+                '(self, object cmp, object key, bool reverse)',
+                '(self, cmp: object, key: object, reverse: bool)'
+            )
+            
+        def testImports2a(self):
+            tips = _pydev_imports_tipper.GenerateTip('%s.RuntimeError' % BUILTIN_MOD)
+            self.assertIn('__doc__', tips)
+            
+        def testImports2b(self):
+            tips = _pydev_imports_tipper.GenerateTip('%s' % BUILTIN_MOD)
+            t = self.assertIn('file' , tips)
+            self.assert_('->' in t[1].strip() or 'file' in t[1])
+            
+        def testImports2c(self):
+            tips = _pydev_imports_tipper.GenerateTip('%s.file' % BUILTIN_MOD)
+            t = self.assertIn('readlines' , tips)
+            self.assert_('->' in t[1] or 'sizehint' in t[1])
+            
+        def testImports(self):
+            '''
+            You can print_ the results to check...
+            '''
+            if HAS_WX:
+                tip = _pydev_imports_tipper.GenerateTip('wxPython.wx')
+                self.assertIn('wxApp'        , tip)
+                
+                tip = _pydev_imports_tipper.GenerateTip('wxPython.wx.wxApp')
+                
+                try:
+                    tip = _pydev_imports_tipper.GenerateTip('qt')
+                    self.assertIn('QWidget'        , tip)
+                    self.assertIn('QDialog'        , tip)
+                    
+                    tip = _pydev_imports_tipper.GenerateTip('qt.QWidget')
+                    self.assertIn('rect'           , tip)
+                    self.assertIn('rect'           , tip)
+                    self.assertIn('AltButton'      , tip)
+            
+                    tip = _pydev_imports_tipper.GenerateTip('qt.QWidget.AltButton')
+                    self.assertIn('__xor__'      , tip)
+            
+                    tip = _pydev_imports_tipper.GenerateTip('qt.QWidget.AltButton.__xor__')
+                    self.assertIn('__class__'      , tip)
+                except ImportError:
+                    pass
+                
+            tip = _pydev_imports_tipper.GenerateTip(BUILTIN_MOD)
+    #        for t in tip[1]:
+    #            print_ t
+            self.assertIn('object'         , tip)
+            self.assertIn('tuple'          , tip)
+            self.assertIn('list'          , tip)
+            self.assertIn('RuntimeError'   , tip)
+            self.assertIn('RuntimeWarning' , tip)
+            
+            t = self.assertIn('cmp' , tip)
+            
+            self.CheckArgs(t, '(x, y)', '(object x, object y)', '(x: object, y: object)') #args
+            
+            t = self.assertIn('isinstance' , tip)
+            self.CheckArgs(t, '(object, class_or_type_or_tuple)', '(object o, type typeinfo)', '(o: object, typeinfo: type)') #args
+            
+            t = self.assertIn('compile' , tip)
+            self.CheckArgs(t, '(source, filename, mode)', '()', '(o: object, name: str, val: object)') #args
+            
+            t = self.assertIn('setattr' , tip)
+            self.CheckArgs(t, '(object, name, value)', '(object o, str name, object val)', '(o: object, name: str, val: object)') #args
+            
+            try:
+                import compiler
+                compiler_module = 'compiler'
+            except ImportError:
+                try:
+                    import ast
+                    compiler_module = 'ast'
+                except ImportError:
+                    compiler_module = None
+                
+            if compiler_module is not None: #Not available in iron python
+                tip = _pydev_imports_tipper.GenerateTip(compiler_module) 
+                if compiler_module == 'compiler':
+                    self.assertArgs('parse', '(buf, mode)', tip)
+                    self.assertArgs('walk', '(tree, visitor, walker, verbose)', tip)
+                    self.assertIn('parseFile'      , tip)
+                else:
+                    self.assertArgs('parse', '(source, filename, mode)', tip)
+                    self.assertArgs('walk', '(node)', tip)
+                self.assertIn('parse'          , tip)
+            
+            
+        def CheckArgs(self, t, *expected):
+            for x in expected:
+                if x == t[2]:
+                    return
+            self.fail('Found: %s. Expected: %s' % (t[2], expected))
+            
+            
+        def assertArgs(self, tok, args, tips):
+            for a in tips[1]:
+                if tok == a[0]:
+                    self.assertEquals(args, a[2])
+                    return
+            raise AssertionError('%s not in %s', tok, tips)
+    
+        def assertIn(self, tok, tips):
+            for a in tips[1]:
+                if tok == a[0]:
+                    return a
+            raise AssertionError('%s not in %s' % (tok, tips))
+        
+        
+        def testSearch(self):
+            s = _pydev_imports_tipper.Search('inspect.ismodule')
+            (f, line, col), foundAs = s
+            self.assert_(line > 0)
+            
+            
+        def testDotNetLibraries(self):
+            if sys.platform == 'cli':
+                tip = _pydev_imports_tipper.GenerateTip('System.Drawing')
+                self.assertIn('Brushes' , tip)
+                
+                tip = _pydev_imports_tipper.GenerateTip('System.Drawing.Brushes')
+                self.assertIn('Aqua' , tip)
+            
+    
+        def testInspect(self):
+            
+            class C(object):
+                def metA(self, a, b):
+                    pass
+            
+            obj = C.metA
+            if inspect.ismethod (obj):
+                pass
+    #            print_ obj.im_func
+    #            print_ inspect.getargspec(obj.im_func)
+                
+            
+    def suite():
+        s = unittest.TestSuite()
+        s.addTest(Test("testImports5"))
+        unittest.TextTestRunner(verbosity=2).run(s)
+
+
+if __name__ == '__main__':
+    if sys.platform.find('java') == -1:
+#        suite()
+        unittest.main()
+    else:
+        sys.stdout.write('Not running python tests in platform: %s\n' % (sys.platform,))
+    
diff --git a/python/helpers/pydev/tests_mainloop/README b/python/helpers/pydev/tests_mainloop/README
new file mode 100644
index 0000000..65e699b
--- /dev/null
+++ b/python/helpers/pydev/tests_mainloop/README
@@ -0,0 +1,4 @@
+# Parts of IPython, files from: https://github.com/ipython/ipython/tree/rel-1.0.0/examples/lib
+# The files in this folder are manual tests for main loop integration
+
+# These tests have been modified to work in the PyDev Console context
diff --git a/python/helpers/pydev/tests_mainloop/__not_in_default_pythonpath.txt b/python/helpers/pydev/tests_mainloop/__not_in_default_pythonpath.txt
new file mode 100644
index 0000000..29cdc5b
--- /dev/null
+++ b/python/helpers/pydev/tests_mainloop/__not_in_default_pythonpath.txt
@@ -0,0 +1 @@
+(no __init__.py file)
\ No newline at end of file
diff --git a/python/helpers/pydev/tests_mainloop/gui-glut.py b/python/helpers/pydev/tests_mainloop/gui-glut.py
new file mode 100644
index 0000000..f05a4bc
--- /dev/null
+++ b/python/helpers/pydev/tests_mainloop/gui-glut.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+"""Simple GLUT example to manually test event loop integration.
+
+To run this:
+1) Enable the PyDev GUI event loop integration for glut
+2) do an execfile on this script
+3) ensure you have a working GUI simultaneously with an
+   interactive console
+4) run: gl.glClearColor(1,1,1,1)
+"""
+
+#!/usr/bin/env python
+import sys
+import OpenGL.GL as gl
+import OpenGL.GLUT as glut
+
+def close():
+    glut.glutDestroyWindow(glut.glutGetWindow())
+
+def display():
+    gl.glClear (gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
+    glut.glutSwapBuffers()
+
+def resize(width,height):
+    gl.glViewport(0, 0, width, height+4)
+    gl.glMatrixMode(gl.GL_PROJECTION)
+    gl.glLoadIdentity()
+    gl.glOrtho(0, width, 0, height+4, -1, 1)
+    gl.glMatrixMode(gl.GL_MODELVIEW)
+
+if glut.glutGetWindow() > 0:
+    interactive = True
+    glut.glutInit(sys.argv)
+    glut.glutInitDisplayMode(glut.GLUT_DOUBLE |
+                             glut.GLUT_RGBA   |
+                             glut.GLUT_DEPTH)
+else:
+    interactive = False
+
+glut.glutCreateWindow('gui-glut')
+glut.glutDisplayFunc(display)
+glut.glutReshapeFunc(resize)
+# This is necessary on osx to be able to close the window
+#  (else the close button is disabled)
+if sys.platform == 'darwin' and not bool(glut.HAVE_FREEGLUT):
+    glut.glutWMCloseFunc(close)
+gl.glClearColor(0,0,0,1)
+
+if not interactive:
+    glut.glutMainLoop()
diff --git a/python/helpers/pydev/tests_mainloop/gui-gtk.py b/python/helpers/pydev/tests_mainloop/gui-gtk.py
new file mode 100644
index 0000000..978f8f9
--- /dev/null
+++ b/python/helpers/pydev/tests_mainloop/gui-gtk.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+"""Simple GTK example to manually test event loop integration.
+
+To run this:
+1) Enable the PyDev GUI event loop integration for gtk
+2) do an execfile on this script
+3) ensure you have a working GUI simultaneously with an
+   interactive console
+"""
+
+import pygtk
+pygtk.require('2.0')
+import gtk
+
+
+def hello_world(wigdet, data=None):
+    print("Hello World")
+
+def delete_event(widget, event, data=None):
+    return False
+
+def destroy(widget, data=None):
+    gtk.main_quit()
+
+window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+window.connect("delete_event", delete_event)
+window.connect("destroy", destroy)
+button = gtk.Button("Hello World")
+button.connect("clicked", hello_world, None)
+
+window.add(button)
+button.show()
+window.show()
+
diff --git a/python/helpers/pydev/tests_mainloop/gui-gtk3.py b/python/helpers/pydev/tests_mainloop/gui-gtk3.py
new file mode 100644
index 0000000..a787f7e
--- /dev/null
+++ b/python/helpers/pydev/tests_mainloop/gui-gtk3.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+"""Simple Gtk example to manually test event loop integration.
+
+To run this:
+1) Enable the PyDev GUI event loop integration for gtk3
+2) do an execfile on this script
+3) ensure you have a working GUI simultaneously with an
+   interactive console
+"""
+
+from gi.repository import Gtk
+
+
+def hello_world(wigdet, data=None):
+    print("Hello World")
+
+def delete_event(widget, event, data=None):
+    return False
+
+def destroy(widget, data=None):
+    Gtk.main_quit()
+
+window = Gtk.Window(Gtk.WindowType.TOPLEVEL)
+window.connect("delete_event", delete_event)
+window.connect("destroy", destroy)
+button = Gtk.Button("Hello World")
+button.connect("clicked", hello_world, None)
+
+window.add(button)
+button.show()
+window.show()
+
diff --git a/python/helpers/pydev/tests_mainloop/gui-pyglet.py b/python/helpers/pydev/tests_mainloop/gui-pyglet.py
new file mode 100644
index 0000000..b646093
--- /dev/null
+++ b/python/helpers/pydev/tests_mainloop/gui-pyglet.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+"""Simple pyglet example to manually test event loop integration.
+
+To run this:
+1) Enable the PyDev GUI event loop integration for pyglet
+2) do an execfile on this script
+3) ensure you have a working GUI simultaneously with an
+   interactive console
+"""
+
+import pyglet
+
+
+window = pyglet.window.Window()
+label = pyglet.text.Label('Hello, world',
+                          font_name='Times New Roman',
+                          font_size=36,
+                          x=window.width//2, y=window.height//2,
+                          anchor_x='center', anchor_y='center')
+@window.event
+def on_close():
+    window.close()
+
+@window.event
+def on_draw():
+    window.clear()
+    label.draw()
diff --git a/python/helpers/pydev/tests_mainloop/gui-qt.py b/python/helpers/pydev/tests_mainloop/gui-qt.py
new file mode 100644
index 0000000..c27cbd6
--- /dev/null
+++ b/python/helpers/pydev/tests_mainloop/gui-qt.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+"""Simple Qt4 example to manually test event loop integration.
+
+To run this:
+1) Enable the PyDev GUI event loop integration for qt
+2) do an execfile on this script
+3) ensure you have a working GUI simultaneously with an
+   interactive console
+
+Ref: Modified from http://zetcode.com/tutorials/pyqt4/firstprograms/
+"""
+
+import sys
+from PyQt4 import QtGui, QtCore
+
+class SimpleWindow(QtGui.QWidget):
+    def __init__(self, parent=None):
+        QtGui.QWidget.__init__(self, parent)
+
+        self.setGeometry(300, 300, 200, 80)
+        self.setWindowTitle('Hello World')
+
+        quit = QtGui.QPushButton('Close', self)
+        quit.setGeometry(10, 10, 60, 35)
+
+        self.connect(quit, QtCore.SIGNAL('clicked()'),
+                     self, QtCore.SLOT('close()'))
+
+if __name__ == '__main__':
+    app = QtCore.QCoreApplication.instance()
+    if app is None:
+        app = QtGui.QApplication([])
+
+    sw = SimpleWindow()
+    sw.show()
diff --git a/python/helpers/pydev/tests_mainloop/gui-tk.py b/python/helpers/pydev/tests_mainloop/gui-tk.py
new file mode 100644
index 0000000..69ceb0b
--- /dev/null
+++ b/python/helpers/pydev/tests_mainloop/gui-tk.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+"""Simple Tk example to manually test event loop integration.
+
+To run this:
+1) Enable the PyDev GUI event loop integration for tk
+2) do an execfile on this script
+3) ensure you have a working GUI simultaneously with an
+   interactive console
+"""
+
+try:
+    from Tkinter import *
+except:
+    # Python 3
+    from tkinter import *
+
+class MyApp:
+
+    def __init__(self, root):
+        frame = Frame(root)
+        frame.pack()
+
+        self.button = Button(frame, text="Hello", command=self.hello_world)
+        self.button.pack(side=LEFT)
+
+    def hello_world(self):
+        print("Hello World!")
+
+root = Tk()
+
+app = MyApp(root)
diff --git a/python/helpers/pydev/tests_mainloop/gui-wx.py b/python/helpers/pydev/tests_mainloop/gui-wx.py
new file mode 100644
index 0000000..2101e7f
--- /dev/null
+++ b/python/helpers/pydev/tests_mainloop/gui-wx.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+"""
+A Simple wx example to test PyDev's event loop integration.
+
+To run this:
+1) Enable the PyDev GUI event loop integration for wx
+2) do an execfile on this script
+3) ensure you have a working GUI simultaneously with an
+   interactive console
+
+Ref: Modified from wxPython source code wxPython/samples/simple/simple.py
+"""
+
+import wx
+
+
+class MyFrame(wx.Frame):
+    """
+    This is MyFrame.  It just shows a few controls on a wxPanel,
+    and has a simple menu.
+    """
+    def __init__(self, parent, title):
+        wx.Frame.__init__(self, parent, -1, title,
+                          pos=(150, 150), size=(350, 200))
+
+        # Create the menubar
+        menuBar = wx.MenuBar()
+
+        # and a menu
+        menu = wx.Menu()
+
+        # add an item to the menu, using \tKeyName automatically
+        # creates an accelerator, the third param is some help text
+        # that will show up in the statusbar
+        menu.Append(wx.ID_EXIT, "E&xit\tAlt-X", "Exit this simple sample")
+
+        # bind the menu event to an event handler
+        self.Bind(wx.EVT_MENU, self.OnTimeToClose, id=wx.ID_EXIT)
+
+        # and put the menu on the menubar
+        menuBar.Append(menu, "&File")
+        self.SetMenuBar(menuBar)
+
+        self.CreateStatusBar()
+
+        # Now create the Panel to put the other controls on.
+        panel = wx.Panel(self)
+
+        # and a few controls
+        text = wx.StaticText(panel, -1, "Hello World!")
+        text.SetFont(wx.Font(14, wx.SWISS, wx.NORMAL, wx.BOLD))
+        text.SetSize(text.GetBestSize())
+        btn = wx.Button(panel, -1, "Close")
+        funbtn = wx.Button(panel, -1, "Just for fun...")
+
+        # bind the button events to handlers
+        self.Bind(wx.EVT_BUTTON, self.OnTimeToClose, btn)
+        self.Bind(wx.EVT_BUTTON, self.OnFunButton, funbtn)
+
+        # Use a sizer to layout the controls, stacked vertically and with
+        # a 10 pixel border around each
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        sizer.Add(text, 0, wx.ALL, 10)
+        sizer.Add(btn, 0, wx.ALL, 10)
+        sizer.Add(funbtn, 0, wx.ALL, 10)
+        panel.SetSizer(sizer)
+        panel.Layout()
+
+
+    def OnTimeToClose(self, evt):
+        """Event handler for the button click."""
+        print("See ya later!")
+        self.Close()
+
+    def OnFunButton(self, evt):
+        """Event handler for the button click."""
+        print("Having fun yet?")
+
+
+class MyApp(wx.App):
+    def OnInit(self):
+        frame = MyFrame(None, "Simple wxPython App")
+        self.SetTopWindow(frame)
+
+        print("Print statements go to this stdout window by default.")
+
+        frame.Show(True)
+        return True
+
+
+if __name__ == '__main__':
+
+    app = wx.GetApp()
+    if app is None:
+        app = MyApp(redirect=False, clearSigInt=False)
+    else:
+        frame = MyFrame(None, "Simple wxPython App")
+        app.SetTopWindow(frame)
+        print("Print statements go to this stdout window by default.")
+        frame.Show(True)
+
diff --git a/python/helpers/pydev/tests_python/__not_in_default_pythonpath.txt b/python/helpers/pydev/tests_python/__not_in_default_pythonpath.txt
new file mode 100644
index 0000000..29cdc5b
--- /dev/null
+++ b/python/helpers/pydev/tests_python/__not_in_default_pythonpath.txt
@@ -0,0 +1 @@
+(no __init__.py file)
\ No newline at end of file
diff --git a/python/helpers/pydev/tests_python/_debugger_case1.py b/python/helpers/pydev/tests_python/_debugger_case1.py
new file mode 100644
index 0000000..964d951
--- /dev/null
+++ b/python/helpers/pydev/tests_python/_debugger_case1.py
@@ -0,0 +1,61 @@
+import sys
+import weakref
+
+def SetUp():
+    observable = Observable()
+    observer = Observer()
+    observable.AddObserver(observer)
+    return observable
+
+
+class Observable(object):
+    def __init__(self):
+        self.observers = []
+        
+    def AddObserver(self, observer):
+        sys.stdout.write( 'observer %s\n' % (observer,))
+        ref = weakref.ref(observer)
+        self.observers.append(ref)
+        sys.stdout.write('weakref: %s\n' % (ref(),))
+        
+    def Notify(self):
+        for o in self.observers:
+            o = o()
+            
+            
+            try:
+                import gc
+            except ImportError:
+                o = None #some jython does not have gc, so, there's no sense testing this in it
+            else:
+                try:
+                    gc.get_referrers(o)
+                except:
+                    o = None #jython and ironpython do not have get_referrers
+            
+            if o is not None:
+                sys.stdout.write('still observing %s\n' % (o,))
+                sys.stdout.write('number of referrers: %s\n' % len(gc.get_referrers(o)))
+                frame = gc.get_referrers(o)[0]
+                frame_referrers = gc.get_referrers(frame)
+                sys.stdout.write('frame referrer %s\n' % (frame_referrers,))
+                referrers1 = gc.get_referrers(frame_referrers[1])
+                sys.stdout.write('%s\n' % (referrers1,))
+                sys.stderr.write('TEST FAILED: The observer should have died, even when running in debug\n')
+            else:
+                sys.stdout.write('TEST SUCEEDED: observer died\n')
+                
+            sys.stdout.flush()
+            sys.stderr.flush()
+                
+class Observer(object):
+    pass
+
+    
+def main():
+    observable = SetUp()
+    observable.Notify()
+    
+    
+if __name__ == '__main__':
+    main()
diff --git a/python/helpers/pydev/tests_python/_debugger_case10.py b/python/helpers/pydev/tests_python/_debugger_case10.py
new file mode 100644
index 0000000..323deda
--- /dev/null
+++ b/python/helpers/pydev/tests_python/_debugger_case10.py
@@ -0,0 +1,18 @@
+def Method1():
+    print('m1')
+    print('m1')
+    
+def Method1a():
+    print('m1a')
+    print('m1a')
+
+def Method2():
+    print('m2 before')
+    Method1()
+    Method1a()
+    print('m2 after')
+
+   
+if __name__ == '__main__': 
+    Method2()
+    print('TEST SUCEEDED!')
diff --git a/python/helpers/pydev/tests_python/_debugger_case13.py b/python/helpers/pydev/tests_python/_debugger_case13.py
new file mode 100644
index 0000000..dbdbbd4
--- /dev/null
+++ b/python/helpers/pydev/tests_python/_debugger_case13.py
@@ -0,0 +1,43 @@
+
+class TestProperty(object):
+    def __init__(self, name = "Default"):
+        self._x = None
+        self.name = name
+
+    def get_name(self):
+        return self.__name
+
+
+    def set_name(self, value):
+        self.__name = value
+
+
+    def del_name(self):
+        del self.__name
+    name = property(get_name, set_name, del_name, "name's docstring")
+
+    @property
+    def x(self):
+        return self._x
+
+    @x.setter
+    def x(self, value):
+        self._x = value
+
+    @x.deleter
+    def x(self):
+        del self._x
+
+def main():
+    """
+    """
+    testObj = TestProperty()
+    testObj.x = 10
+    val = testObj.x
+    
+    testObj.name = "Pydev"
+    debugType = testObj.name
+    print('TEST SUCEEDED!')
+    
+if __name__ == '__main__':
+    main()
\ No newline at end of file
diff --git a/python/helpers/pydev/tests_python/_debugger_case14.py b/python/helpers/pydev/tests_python/_debugger_case14.py
new file mode 100644
index 0000000..2a5e181
--- /dev/null
+++ b/python/helpers/pydev/tests_python/_debugger_case14.py
@@ -0,0 +1,29 @@
+
+class Car(object):
+    """A car class"""
+    def __init__(self, model, make, color):
+        self.model = model
+        self.make = make
+        self.color = color
+        self.price = None
+
+    def get_price(self):
+        return self.price
+
+    def set_price(self, value):
+        self.price = value
+
+availableCars = []
+def main():
+    global availableCars
+
+    #Create a new car obj
+    carObj = Car("Maruti SX4", "2011", "Black")
+    carObj.set_price(950000)  # Set price
+    # Add this to available cars
+    availableCars.append(carObj)
+
+    print('TEST SUCEEDED')
+
+if __name__ == '__main__':
+    main()
diff --git a/python/helpers/pydev/tests_python/_debugger_case15.py b/python/helpers/pydev/tests_python/_debugger_case15.py
new file mode 100644
index 0000000..2a5e181
--- /dev/null
+++ b/python/helpers/pydev/tests_python/_debugger_case15.py
@@ -0,0 +1,29 @@
+
+class Car(object):
+    """A car class"""
+    def __init__(self, model, make, color):
+        self.model = model
+        self.make = make
+        self.color = color
+        self.price = None
+
+    def get_price(self):
+        return self.price
+
+    def set_price(self, value):
+        self.price = value
+
+availableCars = []
+def main():
+    global availableCars
+
+    #Create a new car obj
+    carObj = Car("Maruti SX4", "2011", "Black")
+    carObj.set_price(950000)  # Set price
+    # Add this to available cars
+    availableCars.append(carObj)
+
+    print('TEST SUCEEDED')
+
+if __name__ == '__main__':
+    main()
diff --git a/python/helpers/pydev/tests_python/_debugger_case15_execfile.py b/python/helpers/pydev/tests_python/_debugger_case15_execfile.py
new file mode 100644
index 0000000..7123209
--- /dev/null
+++ b/python/helpers/pydev/tests_python/_debugger_case15_execfile.py
@@ -0,0 +1 @@
+f=lambda x: 'val=%s' % x
diff --git a/python/helpers/pydev/tests_python/_debugger_case16.py b/python/helpers/pydev/tests_python/_debugger_case16.py
new file mode 100644
index 0000000..5622813
--- /dev/null
+++ b/python/helpers/pydev/tests_python/_debugger_case16.py
@@ -0,0 +1,12 @@
+# this test requires numpy to be installed
+import numpy
+
+def main():
+    smallarray = numpy.arange(100) * 1 + 1j
+    bigarray = numpy.arange(100000).reshape((10,10000)) # 100 thousand
+    hugearray = numpy.arange(10000000)  # 10 million
+
+    pass  # location of breakpoint after all arrays defined
+
+main()
+print('TEST SUCEEDED')
diff --git a/python/helpers/pydev/tests_python/_debugger_case17.py b/python/helpers/pydev/tests_python/_debugger_case17.py
new file mode 100644
index 0000000..0177683
--- /dev/null
+++ b/python/helpers/pydev/tests_python/_debugger_case17.py
@@ -0,0 +1,38 @@
+def get_here():
+    a = 10
+
+def foo(func): 
+    return func
+
+def m1(): # @DontTrace
+    get_here()
+
+# @DontTrace
+def m2():
+    get_here()
+
+# @DontTrace
+@foo
+def m3():
+    get_here()
+
+@foo
+@foo
+def m4(): # @DontTrace
+    get_here()
+
+
+def main():
+
+    m1()
+    
+    m2()
+    
+    m3()
+    
+    m4()
+
+if __name__ == '__main__':
+    main()
+    
+    print('TEST SUCEEDED')
diff --git a/python/helpers/pydev/tests_python/_debugger_case18.py b/python/helpers/pydev/tests_python/_debugger_case18.py
new file mode 100644
index 0000000..69717b2
--- /dev/null
+++ b/python/helpers/pydev/tests_python/_debugger_case18.py
@@ -0,0 +1,23 @@
+
+
+def m2(a):
+    a = 10
+    b = 20 #Break here and set a = 40
+    c = 30
+    
+    def function2():
+        print a
+
+    return a
+
+
+def m1(a):
+    return m2(a)
+
+
+if __name__ == '__main__':
+    found = m1(10)
+    if found == 40:
+        print('TEST SUCEEDED')
+    else:
+        raise AssertionError('Expected variable to be changed to 40. Found: %s' % (found,))
diff --git a/python/helpers/pydev/tests_python/_debugger_case19.py b/python/helpers/pydev/tests_python/_debugger_case19.py
new file mode 100644
index 0000000..aaf380c
--- /dev/null
+++ b/python/helpers/pydev/tests_python/_debugger_case19.py
@@ -0,0 +1,10 @@
+class A:
+    
+    def __init__(self):
+        self.__var = 10
+
+if __name__ == '__main__':
+    a = A()
+    print a._A__var
+    # Evaluate 'a.__var' should give a._A__var_
+    print('TEST SUCEEDED')
diff --git a/python/helpers/pydev/tests_python/_debugger_case2.py b/python/helpers/pydev/tests_python/_debugger_case2.py
new file mode 100644
index 0000000..e47a5e21
--- /dev/null
+++ b/python/helpers/pydev/tests_python/_debugger_case2.py
@@ -0,0 +1,24 @@
+
+def Call4():
+    print('Start Call4')
+    print('End Call4')
+
+def Call3():
+    print('Start Call3')
+    Call4()
+    print('End Call3')
+
+def Call2():
+    print('Start Call2')
+    Call3()
+    print('End Call2 - a')
+    print('End Call2 - b')
+
+def Call1():
+    print('Start Call1')
+    Call2()
+    print('End Call1')
+
+if __name__ == '__main__':
+    Call1()
+    print('TEST SUCEEDED!')
diff --git a/python/helpers/pydev/tests_python/_debugger_case3.py b/python/helpers/pydev/tests_python/_debugger_case3.py
new file mode 100644
index 0000000..aa05032
--- /dev/null
+++ b/python/helpers/pydev/tests_python/_debugger_case3.py
@@ -0,0 +1,8 @@
+import time
+if __name__ == '__main__':
+    for i in range(15):
+        print('here')
+        time.sleep(.2)
+    
+    print('TEST SUCEEDED')
+        
diff --git a/python/helpers/pydev/tests_python/_debugger_case4.py b/python/helpers/pydev/tests_python/_debugger_case4.py
new file mode 100644
index 0000000..009da4a
--- /dev/null
+++ b/python/helpers/pydev/tests_python/_debugger_case4.py
@@ -0,0 +1,8 @@
+import time
+if __name__ == '__main__':
+    for i in range(10):
+        print('here %s' % i)
+        time.sleep(1)
+    
+    print('TEST SUCEEDED')
+        
diff --git a/python/helpers/pydev/tests_python/_debugger_case56.py b/python/helpers/pydev/tests_python/_debugger_case56.py
new file mode 100644
index 0000000..e5de28d
--- /dev/null
+++ b/python/helpers/pydev/tests_python/_debugger_case56.py
@@ -0,0 +1,9 @@
+def Call2():
+    print('Call2')
+
+def Call1(a):
+    print('Call1')
+    
+if __name__ == '__main__':
+    Call1(Call2())
+    print('TEST SUCEEDED!')
diff --git a/python/helpers/pydev/tests_python/_debugger_case7.py b/python/helpers/pydev/tests_python/_debugger_case7.py
new file mode 100644
index 0000000..263110b
--- /dev/null
+++ b/python/helpers/pydev/tests_python/_debugger_case7.py
@@ -0,0 +1,8 @@
+def Call():
+    variable_for_test_1 = 10
+    variable_for_test_2 = 20
+    variable_for_test_3 = 30
+    
+if __name__ == '__main__':
+    Call()
+    print 'TEST SUCEEDED!'
diff --git a/python/helpers/pydev/tests_python/_debugger_case89.py b/python/helpers/pydev/tests_python/_debugger_case89.py
new file mode 100644
index 0000000..e6f32dd
--- /dev/null
+++ b/python/helpers/pydev/tests_python/_debugger_case89.py
@@ -0,0 +1,16 @@
+def Method1():
+    print 'm1'
+
+def Method2():
+    print 'm2 before'
+    Method1()
+    print 'm2 after'
+
+def Method3():
+    print 'm3 before'
+    Method2()
+    print 'm3 after'
+   
+if __name__ == '__main__': 
+    Method3()
+    print 'TEST SUCEEDED!'
diff --git a/python/helpers/pydev/tests_python/test_additional_thread_info.py b/python/helpers/pydev/tests_python/test_additional_thread_info.py
new file mode 100644
index 0000000..6ae260d
--- /dev/null
+++ b/python/helpers/pydev/tests_python/test_additional_thread_info.py
@@ -0,0 +1,111 @@
+import sys
+import os
+sys.path.insert(0, os.path.split(os.path.split(__file__)[0])[0])
+
+from pydevd_constants import Null
+import unittest
+
+#=======================================================================================================================
+# TestCase
+#=======================================================================================================================
+class TestCase(unittest.TestCase):
+    '''
+        Used for profiling the PyDBAdditionalThreadInfoWithoutCurrentFramesSupport version
+    '''
+    
+    def testMetNoFramesSupport(self):
+        from pydevd_additional_thread_info import PyDBAdditionalThreadInfoWithoutCurrentFramesSupport
+        info = PyDBAdditionalThreadInfoWithoutCurrentFramesSupport()
+        
+        mainDebugger = Null()
+        filename = ''
+        base = ''
+        additionalInfo = Null()
+        t = Null()
+        frame = Null()
+        
+        times = 10
+        for i in range(times):
+            info.CreateDbFrame((mainDebugger, filename, additionalInfo, t, frame))
+            
+        #we haven't kept any reference, so, they must have been garbage-collected already!
+        self.assertEqual(0, len(info.IterFrames()))
+        
+        kept_frames = []
+        for i in range(times):
+            kept_frames.append(info.CreateDbFrame((mainDebugger, filename, additionalInfo, t, frame)))
+        
+        for i in range(times):
+            self.assertEqual(times, len(info.IterFrames()))
+            
+            
+    def testStartNewThread(self):
+        import pydevd
+        import thread
+        original = thread.start_new_thread
+        thread.start_new_thread = pydevd.pydev_start_new_thread
+        try:
+            found = {}
+            def function(a, b, *args, **kwargs):
+                found['a'] = a
+                found['b'] = b
+                found['args'] = args
+                found['kwargs'] = kwargs
+            thread.start_new_thread(function, (1,2,3,4), {'d':1, 'e':2})
+            import time
+            for _i in xrange(20):
+                if len(found) == 4:
+                    break
+                time.sleep(.1)
+            else:
+                raise AssertionError('Could not get to condition before 2 seconds')
+            
+            self.assertEqual({'a': 1, 'b': 2, 'args': (3, 4), 'kwargs': {'e': 2, 'd': 1}}, found)
+        finally:
+            thread.start_new_thread = original
+            
+            
+    def testStartNewThread2(self):
+        import pydevd
+        import thread
+        
+        original = thread.start_new_thread
+        thread.start_new_thread = pydevd.pydev_start_new_thread
+        try:
+            found = {}
+            
+            class F(object):
+                start_new_thread = thread.start_new_thread
+                
+                def start_it(self):
+                    try:
+                        self.start_new_thread(self.function, (1,2,3,4), {'d':1, 'e':2})
+                    except:
+                        import traceback;traceback.print_exc()
+
+                def function(self, a, b, *args, **kwargs):
+                    found['a'] = a
+                    found['b'] = b
+                    found['args'] = args
+                    found['kwargs'] = kwargs
+            
+            f = F()
+            f.start_it()
+            import time
+            for _i in xrange(20):
+                if len(found) == 4:
+                    break
+                time.sleep(.1)
+            else:
+                raise AssertionError('Could not get to condition before 2 seconds')
+            
+            self.assertEqual({'a': 1, 'b': 2, 'args': (3, 4), 'kwargs': {'e': 2, 'd': 1}}, found)
+        finally:
+            thread.start_new_thread = original
+        
+
+#=======================================================================================================================
+# main        
+#=======================================================================================================================
+if __name__ == '__main__':
+    unittest.main()
diff --git a/python/helpers/pydev/tests_python/test_debugger.py b/python/helpers/pydev/tests_python/test_debugger.py
new file mode 100644
index 0000000..3a216cb
--- /dev/null
+++ b/python/helpers/pydev/tests_python/test_debugger.py
@@ -0,0 +1,1205 @@
+'''
+    The idea is that we record the commands sent to the debugger and reproduce them from this script
+    (so, this works as the client, which spawns the debugger as a separate process and communicates
+    to it as if it was run from the outside)
+
+    Note that it's a python script but it'll spawn a process to run as jython, ironpython and as python.
+'''
+CMD_SET_PROPERTY_TRACE, CMD_EVALUATE_CONSOLE_EXPRESSION, CMD_RUN_CUSTOM_OPERATION, CMD_ENABLE_DONT_TRACE = 133, 134, 135, 141
+PYTHON_EXE = None
+IRONPYTHON_EXE = None
+JYTHON_JAR_LOCATION = None
+JAVA_LOCATION = None
+
+
+import unittest
+import pydev_localhost
+
+port = None
+
+def UpdatePort():
+    global port
+    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    s.bind((pydev_localhost.get_localhost(), 0))
+    _, port = s.getsockname()
+    s.close()
+
+import os
+def NormFile(filename):
+    try:
+        rPath = os.path.realpath  # @UndefinedVariable
+    except:
+        # jython does not support os.path.realpath
+        # realpath is a no-op on systems without islink support
+        rPath = os.path.abspath
+    return os.path.normcase(rPath(filename))
+
+PYDEVD_FILE = NormFile('../pydevd.py')
+import sys
+sys.path.append(os.path.dirname(PYDEVD_FILE))
+
+SHOW_WRITES_AND_READS = False
+SHOW_RESULT_STR = False
+SHOW_OTHER_DEBUG_INFO = False
+
+
+import subprocess
+import socket
+import threading
+import time
+from urllib import quote_plus, quote, unquote_plus
+
+
+#=======================================================================================================================
+# ReaderThread
+#=======================================================================================================================
+class ReaderThread(threading.Thread):
+
+    def __init__(self, sock):
+        threading.Thread.__init__(self)
+        self.setDaemon(True)
+        self.sock = sock
+        self.lastReceived = None
+
+    def run(self):
+        try:
+            buf = ''
+            while True:
+                l = self.sock.recv(1024)
+                buf += l
+
+                if '\n' in buf:
+                    self.lastReceived = buf
+                    buf = ''
+
+                if SHOW_WRITES_AND_READS:
+                    print 'Test Reader Thread Received %s' % self.lastReceived.strip()
+        except:
+            pass  # ok, finished it
+
+    def DoKill(self):
+        self.sock.close()
+
+#=======================================================================================================================
+# AbstractWriterThread
+#=======================================================================================================================
+class AbstractWriterThread(threading.Thread):
+
+    def __init__(self):
+        threading.Thread.__init__(self)
+        self.setDaemon(True)
+        self.finishedOk = False
+        self._next_breakpoint_id = 0
+
+    def DoKill(self):
+        if hasattr(self, 'readerThread'):
+            # if it's not created, it's not there...
+            self.readerThread.DoKill()
+        self.sock.close()
+
+    def Write(self, s):
+        last = self.readerThread.lastReceived
+        if SHOW_WRITES_AND_READS:
+            print 'Test Writer Thread Written %s' % (s,)
+        self.sock.send(s + '\n')
+        time.sleep(0.2)
+
+        i = 0
+        while last == self.readerThread.lastReceived and i < 10:
+            i += 1
+            time.sleep(0.1)
+
+
+    def StartSocket(self):
+        if SHOW_WRITES_AND_READS:
+            print 'StartSocket'
+
+        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        s.bind(('', port))
+        s.listen(1)
+        if SHOW_WRITES_AND_READS:
+            print 'Waiting in socket.accept()'
+        newSock, addr = s.accept()
+        if SHOW_WRITES_AND_READS:
+            print 'Test Writer Thread Socket:', newSock, addr
+
+        readerThread = self.readerThread = ReaderThread(newSock)
+        readerThread.start()
+        self.sock = newSock
+
+        self._sequence = -1
+        # initial command is always the version
+        self.WriteVersion()
+
+    def NextBreakpointId(self):
+        self._next_breakpoint_id += 1
+        return self._next_breakpoint_id
+
+    def NextSeq(self):
+        self._sequence += 2
+        return self._sequence
+
+
+    def WaitForNewThread(self):
+        i = 0
+        # wait for hit breakpoint
+        while not '<xml><thread name="' in self.readerThread.lastReceived or '<xml><thread name="pydevd.' in self.readerThread.lastReceived:
+            i += 1
+            time.sleep(1)
+            if i >= 15:
+                raise AssertionError('After %s seconds, a thread was not created.' % i)
+
+        # we have something like <xml><thread name="MainThread" id="12103472" /></xml>
+        splitted = self.readerThread.lastReceived.split('"')
+        threadId = splitted[3]
+        return threadId
+
+    def WaitForBreakpointHit(self, reason='111', get_line=False):
+        '''
+            108 is over
+            109 is return
+            111 is breakpoint
+        '''
+        i = 0
+        # wait for hit breakpoint
+        while not ('stop_reason="%s"' % reason) in self.readerThread.lastReceived:
+            i += 1
+            time.sleep(1)
+            if i >= 10:
+                raise AssertionError('After %s seconds, a break with reason: %s was not hit. Found: %s' % \
+                    (i, reason, self.readerThread.lastReceived))
+
+        # we have something like <xml><thread id="12152656" stop_reason="111"><frame id="12453120" ...
+        splitted = self.readerThread.lastReceived.split('"')
+        threadId = splitted[1]
+        frameId = splitted[7]
+        if get_line:
+            return threadId, frameId, int(splitted[13])
+
+        return threadId, frameId
+
+    def WaitForCustomOperation(self, expected):
+        i = 0
+        # wait for custom operation response, the response is double encoded
+        expectedEncoded = quote(quote_plus(expected))
+        while not expectedEncoded in self.readerThread.lastReceived:
+            i += 1
+            time.sleep(1)
+            if i >= 10:
+                raise AssertionError('After %s seconds, the custom operation not received. Last found:\n%s\nExpected (encoded)\n%s' % 
+                    (i, self.readerThread.lastReceived, expectedEncoded))
+
+        return True
+
+    def WaitForEvaluation(self, expected):
+        return self._WaitFor(expected, 'the expected evaluation was not found')
+
+
+    def WaitForVars(self, expected):
+        i = 0
+        # wait for hit breakpoint
+        while not expected in self.readerThread.lastReceived:
+            i += 1
+            time.sleep(1)
+            if i >= 10:
+                raise AssertionError('After %s seconds, the vars were not found. Last found:\n%s' % 
+                    (i, self.readerThread.lastReceived))
+
+        return True
+
+    def WaitForVar(self, expected):
+        self._WaitFor(expected, 'the var was not found')
+        
+    def _WaitFor(self, expected, error_msg):
+        '''
+        :param expected:
+            If a list we'll work with any of the choices.
+        '''
+        if not isinstance(expected, (list, tuple)):
+            expected = [expected]
+            
+        i = 0
+        found = False
+        while not found:
+            last = self.readerThread.lastReceived
+            for e in expected:
+                if e in last:
+                    found = True
+                    break
+                
+            last = unquote_plus(last)
+            for e in expected:
+                if e in last:
+                    found = True
+                    break
+
+            if found:
+                break
+                        
+            i += 1
+            time.sleep(1)
+            if i >= 10:
+                raise AssertionError('After %s seconds, %s. Last found:\n%s' % 
+                    (i, error_msg, last))
+
+        return True
+
+    def WaitForMultipleVars(self, expected_vars):
+        i = 0
+        # wait for hit breakpoint
+        while True:
+            for expected in expected_vars:
+                if expected not in self.readerThread.lastReceived:
+                    break  # Break out of loop (and don't get to else)
+            else:
+                return True
+
+            i += 1
+            time.sleep(1)
+            if i >= 10:
+                raise AssertionError('After %s seconds, the vars were not found. Last found:\n%s' % 
+                    (i, self.readerThread.lastReceived))
+
+        return True
+
+    def WriteMakeInitialRun(self):
+        self.Write("101\t%s\t" % self.NextSeq())
+
+    def WriteVersion(self):
+        self.Write("501\t%s\t1.0\tWINDOWS\tID" % self.NextSeq())
+
+    def WriteAddBreakpoint(self, line, func):
+        '''
+            @param line: starts at 1
+        '''
+        breakpoint_id = self.NextBreakpointId()
+        self.Write("111\t%s\t%s\t%s\t%s\t%s\t%s\tNone\tNone" % (self.NextSeq(), breakpoint_id, 'python-line', self.TEST_FILE, line, func))
+        return breakpoint_id
+
+    def WriteRemoveBreakpoint(self, breakpoint_id):
+        self.Write("112\t%s\t%s\t%s\t%s" % (self.NextSeq(), 'python-line', self.TEST_FILE, breakpoint_id))
+
+    def WriteChangeVariable(self, thread_id, frame_id, varname, value):
+        self.Write("117\t%s\t%s\t%s\t%s\t%s\t%s" % (self.NextSeq(), thread_id, frame_id, 'FRAME', varname, value))
+
+    def WriteGetFrame(self, threadId, frameId):
+        self.Write("114\t%s\t%s\t%s\tFRAME" % (self.NextSeq(), threadId, frameId))
+
+    def WriteGetVariable(self, threadId, frameId, var_attrs):
+        self.Write("110\t%s\t%s\t%s\tFRAME\t%s" % (self.NextSeq(), threadId, frameId, var_attrs))
+
+    def WriteStepOver(self, threadId):
+        self.Write("108\t%s\t%s" % (self.NextSeq(), threadId,))
+
+    def WriteStepIn(self, threadId):
+        self.Write("107\t%s\t%s" % (self.NextSeq(), threadId,))
+
+    def WriteStepReturn(self, threadId):
+        self.Write("109\t%s\t%s" % (self.NextSeq(), threadId,))
+
+    def WriteSuspendThread(self, threadId):
+        self.Write("105\t%s\t%s" % (self.NextSeq(), threadId,))
+
+    def WriteRunThread(self, threadId):
+        self.Write("106\t%s\t%s" % (self.NextSeq(), threadId,))
+
+    def WriteKillThread(self, threadId):
+        self.Write("104\t%s\t%s" % (self.NextSeq(), threadId,))
+
+    def WriteDebugConsoleExpression(self, locator):
+        self.Write("%s\t%s\t%s" % (CMD_EVALUATE_CONSOLE_EXPRESSION, self.NextSeq(), locator))
+
+    def WriteCustomOperation(self, locator, style, codeOrFile, operation_fn_name):
+        self.Write("%s\t%s\t%s||%s\t%s\t%s" % (CMD_RUN_CUSTOM_OPERATION, self.NextSeq(), locator, style, codeOrFile, operation_fn_name))
+        
+    def WriteEvaluateExpression(self, locator, expression):
+        self.Write("113\t%s\t%s\t%s\t1" % (self.NextSeq(), locator, expression))
+
+    def WriteEnableDontTrace(self, enable):
+        if enable:
+            enable = 'true'
+        else:
+            enable = 'false'
+        self.Write("%s\t%s\t%s" % (CMD_ENABLE_DONT_TRACE, self.NextSeq(), enable))
+
+
+#=======================================================================================================================
+# WriterThreadCase19 - [Test Case]: Evaluate '__' attributes
+#======================================================================================================================
+class WriterThreadCase19(AbstractWriterThread):
+
+    TEST_FILE = NormFile('_debugger_case19.py')
+
+    def run(self):
+        self.StartSocket()
+        self.WriteAddBreakpoint(8, None)
+        self.WriteMakeInitialRun()
+
+        threadId, frameId, line = self.WaitForBreakpointHit('111', True)
+
+        assert line == 8, 'Expected return to be in line 8, was: %s' % line
+        
+        self.WriteEvaluateExpression('%s\t%s\t%s' % (threadId, frameId, 'LOCAL'), 'a.__var')
+        self.WaitForEvaluation('<var name="a.__var" type="int" value="int')
+        self.WriteRunThread(threadId)
+
+        
+        self.finishedOk = True
+
+
+#=======================================================================================================================
+# WriterThreadCase18 - [Test Case]: change local variable
+#======================================================================================================================
+class WriterThreadCase18(AbstractWriterThread):
+
+    TEST_FILE = NormFile('_debugger_case18.py')
+
+    def run(self):
+        self.StartSocket()
+        self.WriteAddBreakpoint(5, 'm2')
+        self.WriteMakeInitialRun()
+
+        thread_id, frame_id, line = self.WaitForBreakpointHit('111', True)
+        assert line == 5, 'Expected return to be in line 2, was: %s' % line
+
+        self.WriteChangeVariable(thread_id, frame_id, 'a', '40')
+        self.WriteRunThread(thread_id)
+        
+        self.finishedOk = True
+
+#=======================================================================================================================
+# WriterThreadCase17 - [Test Case]: dont trace
+#======================================================================================================================
+class WriterThreadCase17(AbstractWriterThread):
+
+    TEST_FILE = NormFile('_debugger_case17.py')
+
+    def run(self):
+        self.StartSocket()
+        self.WriteEnableDontTrace(True)
+        self.WriteAddBreakpoint(27, 'main')
+        self.WriteAddBreakpoint(29, 'main')
+        self.WriteAddBreakpoint(31, 'main')
+        self.WriteAddBreakpoint(33, 'main')
+        self.WriteMakeInitialRun()
+
+        for i in range(4):
+            threadId, frameId, line = self.WaitForBreakpointHit('111', True)
+    
+            self.WriteStepIn(threadId)
+            threadId, frameId, line = self.WaitForBreakpointHit('107', True)
+            # Should Skip step into properties setter
+            assert line == 2, 'Expected return to be in line 2, was: %s' % line
+            self.WriteRunThread(threadId)
+
+        
+        self.finishedOk = True
+
+#=======================================================================================================================
+# WriterThreadCase16 - [Test Case]: numpy.ndarray resolver
+#======================================================================================================================
+class WriterThreadCase16(AbstractWriterThread):
+
+    TEST_FILE = NormFile('_debugger_case16.py')
+
+    def run(self):
+        self.StartSocket()
+        self.WriteAddBreakpoint(9, 'main')
+        self.WriteMakeInitialRun()
+
+        threadId, frameId, line = self.WaitForBreakpointHit('111', True)
+
+        # In this test we check that the three arrays of different shapes, sizes and types
+        # are all resolved properly as ndarrays.
+
+        # First pass check is that we have all three expected variables defined
+        self.WriteGetFrame(threadId, frameId)
+        self.WaitForVars('<var name="smallarray" type="ndarray" value="ndarray%253A %255B  0.%252B1.j   1.%252B1.j   2.%252B1.j   3.%252B1.j   4.%252B1.j   5.%252B1.j   6.%252B1.j   7.%252B1.j%250A   8.%252B1.j   9.%252B1.j  10.%252B1.j  11.%252B1.j  12.%252B1.j  13.%252B1.j  14.%252B1.j  15.%252B1.j%250A  16.%252B1.j  17.%252B1.j  18.%252B1.j  19.%252B1.j  20.%252B1.j  21.%252B1.j  22.%252B1.j  23.%252B1.j%250A  24.%252B1.j  25.%252B1.j  26.%252B1.j  27.%252B1.j  28.%252B1.j  29.%252B1.j  30.%252B1.j  31.%252B1.j%250A  32.%252B1.j  33.%252B1.j  34.%252B1.j  35.%252B1.j  36.%252B1.j  37.%252B1.j  38.%252B1.j  39.%252B1.j%250A  40.%252B1.j  41.%252B1.j  42.%252B1.j  43.%252B1.j  44.%252B1.j  45.%252B1.j  46.%252B1.j  47.%252B1.j%250A  48.%252B1.j  49.%252B1.j  50.%252B1.j  51.%252B1.j  52.%252B1.j  53.%252B1.j  54.%252B1.j  55.%252B1.j%250A  56.%252B1.j  57.%252B1.j  58.%252B1.j  59.%252B1.j  60.%252B1.j  61.%252B1.j  62.%252B1.j  63.%252B1.j%250A  64.%252B1.j  65.%252B1.j  66.%252B1.j  67.%252B1.j  68.%252B1.j  69.%252B1.j  70.%252B1.j  71.%252B1.j%250A  72.%252B1.j  73.%252B1.j  74.%252B1.j  75.%252B1.j  76.%252B1.j  77.%252B1.j  78.%252B1.j  79.%252B1.j%250A  80.%252B1.j  81.%252B1.j  82.%252B1.j  83.%252B1.j  84.%252B1.j  85.%252B1.j  86.%252B1.j  87.%252B1.j%250A  88.%252B1.j  89.%252B1.j  90.%252B1.j  91.%252B1.j  92.%252B1.j  93.%252B1.j  94.%252B1.j  95.%252B1.j%250A  96.%252B1.j  97.%252B1.j  98.%252B1.j  99.%252B1.j%255D" isContainer="True" />')
+        self.WaitForVars('<var name="bigarray" type="ndarray" value="ndarray%253A %255B%255B    0     1     2 ...%252C  9997  9998  9999%255D%250A %255B10000 10001 10002 ...%252C 19997 19998 19999%255D%250A %255B20000 20001 20002 ...%252C 29997 29998 29999%255D%250A ...%252C %250A %255B70000 70001 70002 ...%252C 79997 79998 79999%255D%250A %255B80000 80001 80002 ...%252C 89997 89998 89999%255D%250A %255B90000 90001 90002 ...%252C 99997 99998 99999%255D%255D" isContainer="True" />')
+        self.WaitForVars('<var name="hugearray" type="ndarray" value="ndarray%253A %255B      0       1       2 ...%252C 9999997 9999998 9999999%255D" isContainer="True" />')
+
+        # For each variable, check each of the resolved (meta data) attributes...
+        self.WriteGetVariable(threadId, frameId, 'smallarray')
+        self.WaitForVar('<var name="min" type="complex128"')
+        self.WaitForVar('<var name="max" type="complex128"')
+        self.WaitForVar('<var name="shape" type="tuple"')
+        self.WaitForVar('<var name="dtype" type="dtype"')
+        self.WaitForVar('<var name="size" type="int"')
+        # ...and check that the internals are resolved properly
+        self.WriteGetVariable(threadId, frameId, 'smallarray\t__internals__')
+        self.WaitForVar('<var name="%27size%27')
+
+        self.WriteGetVariable(threadId, frameId, 'bigarray')
+        self.WaitForVar(['<var name="min" type="int64" value="int64%253A 0" />', '<var name="size" type="int" value="int%3A 100000" />'])  # TODO: When on a 32 bit python we get an int32 (which makes this test fail).
+        self.WaitForVar(['<var name="max" type="int64" value="int64%253A 99999" />', '<var name="max" type="int32" value="int32%253A 99999" />'])
+        self.WaitForVar('<var name="shape" type="tuple"')
+        self.WaitForVar('<var name="dtype" type="dtype"')
+        self.WaitForVar('<var name="size" type="int"')
+        self.WriteGetVariable(threadId, frameId, 'bigarray\t__internals__')
+        self.WaitForVar('<var name="%27size%27')
+
+        # this one is different because it crosses the magic threshold where we don't calculate
+        # the min/max
+        self.WriteGetVariable(threadId, frameId, 'hugearray')
+        self.WaitForVar('<var name="min" type="str" value="str%253A ndarray too big%252C calculating min would slow down debugging" />')
+        self.WaitForVar('<var name="max" type="str" value="str%253A ndarray too big%252C calculating max would slow down debugging" />')
+        self.WaitForVar('<var name="shape" type="tuple"')
+        self.WaitForVar('<var name="dtype" type="dtype"')
+        self.WaitForVar('<var name="size" type="int"')
+        self.WriteGetVariable(threadId, frameId, 'hugearray\t__internals__')
+        self.WaitForVar('<var name="%27size%27')
+
+        self.WriteRunThread(threadId)
+        self.finishedOk = True
+
+
+#=======================================================================================================================
+# WriterThreadCase15 - [Test Case]: Custom Commands
+#======================================================================================================================
+class WriterThreadCase15(AbstractWriterThread):
+
+    TEST_FILE = NormFile('_debugger_case15.py')
+
+    def run(self):
+        self.StartSocket()
+        self.WriteAddBreakpoint(22, 'main')
+        self.WriteMakeInitialRun()
+
+        threadId, frameId, line = self.WaitForBreakpointHit('111', True)
+
+        # Access some variable
+        self.WriteCustomOperation("%s\t%s\tEXPRESSION\tcarObj.color" % (threadId, frameId), "EXEC", "f=lambda x: 'val=%s' % x", "f")
+        self.WaitForCustomOperation('val=Black')
+        assert 7 == self._sequence, 'Expected 7. Had: %s' % self._sequence
+
+        self.WriteCustomOperation("%s\t%s\tEXPRESSION\tcarObj.color" % (threadId, frameId), "EXECFILE", NormFile('_debugger_case15_execfile.py'), "f")
+        self.WaitForCustomOperation('val=Black')
+        assert 9 == self._sequence, 'Expected 9. Had: %s' % self._sequence
+
+        self.WriteRunThread(threadId)
+        self.finishedOk = True
+
+
+
+#=======================================================================================================================
+# WriterThreadCase14 - [Test Case]: Interactive Debug Console
+#======================================================================================================================
+class WriterThreadCase14(AbstractWriterThread):
+
+    TEST_FILE = NormFile('_debugger_case14.py')
+
+    def run(self):
+        self.StartSocket()
+        self.WriteAddBreakpoint(22, 'main')
+        self.WriteMakeInitialRun()
+
+        threadId, frameId, line = self.WaitForBreakpointHit('111', True)
+        assert threadId, '%s not valid.' % threadId
+        assert frameId, '%s not valid.' % frameId
+
+        # Access some variable
+        self.WriteDebugConsoleExpression("%s\t%s\tEVALUATE\tcarObj.color" % (threadId, frameId))
+        self.WaitForMultipleVars(['<more>False</more>', '%27Black%27'])
+        assert 7 == self._sequence, 'Expected 9. Had: %s' % self._sequence
+
+        # Change some variable
+        self.WriteDebugConsoleExpression("%s\t%s\tEVALUATE\tcarObj.color='Red'" % (threadId, frameId))
+        self.WriteDebugConsoleExpression("%s\t%s\tEVALUATE\tcarObj.color" % (threadId, frameId))
+        self.WaitForMultipleVars(['<more>False</more>', '%27Red%27'])
+        assert 11 == self._sequence, 'Expected 13. Had: %s' % self._sequence
+
+        # Iterate some loop
+        self.WriteDebugConsoleExpression("%s\t%s\tEVALUATE\tfor i in range(3):" % (threadId, frameId))
+        self.WaitForVars('<xml><more>True</more></xml>')
+        self.WriteDebugConsoleExpression("%s\t%s\tEVALUATE\t    print i" % (threadId, frameId))
+        self.WriteDebugConsoleExpression("%s\t%s\tEVALUATE\t" % (threadId, frameId))
+        self.WaitForVars('<xml><more>False</more><output message="0"></output><output message="1"></output><output message="2"></output></xml>')
+        assert 17 == self._sequence, 'Expected 19. Had: %s' % self._sequence
+
+        self.WriteRunThread(threadId)
+        self.finishedOk = True
+
+
+#=======================================================================================================================
+# WriterThreadCase13
+#======================================================================================================================
+class WriterThreadCase13(AbstractWriterThread):
+
+    TEST_FILE = NormFile('_debugger_case13.py')
+
+    def run(self):
+        self.StartSocket()
+        self.WriteAddBreakpoint(35, 'main')
+        self.Write("%s\t%s\t%s" % (CMD_SET_PROPERTY_TRACE, self.NextSeq(), "true;false;false;true"))
+        self.WriteMakeInitialRun()
+        threadId, frameId, line = self.WaitForBreakpointHit('111', True)
+
+        self.WriteGetFrame(threadId, frameId)
+
+        self.WriteStepIn(threadId)
+        threadId, frameId, line = self.WaitForBreakpointHit('107', True)
+        # Should go inside setter method
+        assert line == 25, 'Expected return to be in line 25, was: %s' % line
+
+        self.WriteStepIn(threadId)
+        threadId, frameId, line = self.WaitForBreakpointHit('107', True)
+
+        self.WriteStepIn(threadId)
+        threadId, frameId, line = self.WaitForBreakpointHit('107', True)
+        # Should go inside getter method
+        assert line == 21, 'Expected return to be in line 21, was: %s' % line
+
+        self.WriteStepIn(threadId)
+        threadId, frameId, line = self.WaitForBreakpointHit('107', True)
+
+        # Disable property tracing
+        self.Write("%s\t%s\t%s" % (CMD_SET_PROPERTY_TRACE, self.NextSeq(), "true;true;true;true"))
+        self.WriteStepIn(threadId)
+        threadId, frameId, line = self.WaitForBreakpointHit('107', True)
+        # Should Skip step into properties setter
+        assert line == 39, 'Expected return to be in line 39, was: %s' % line
+
+        # Enable property tracing
+        self.Write("%s\t%s\t%s" % (CMD_SET_PROPERTY_TRACE, self.NextSeq(), "true;false;false;true"))
+        self.WriteStepIn(threadId)
+        threadId, frameId, line = self.WaitForBreakpointHit('107', True)
+        # Should go inside getter method
+        assert line == 8, 'Expected return to be in line 8, was: %s' % line
+
+        self.WriteRunThread(threadId)
+
+        self.finishedOk = True
+
+#=======================================================================================================================
+# WriterThreadCase12
+#======================================================================================================================
+class WriterThreadCase12(AbstractWriterThread):
+
+    TEST_FILE = NormFile('_debugger_case10.py')
+
+    def run(self):
+        self.StartSocket()
+        self.WriteAddBreakpoint(2, '')  # Should not be hit: setting empty function (not None) should only hit global.
+        self.WriteAddBreakpoint(6, 'Method1a')
+        self.WriteAddBreakpoint(11, 'Method2')
+        self.WriteMakeInitialRun()
+
+        threadId, frameId, line = self.WaitForBreakpointHit('111', True)
+
+        assert line == 11, 'Expected return to be in line 11, was: %s' % line
+
+        self.WriteStepReturn(threadId)
+
+        threadId, frameId, line = self.WaitForBreakpointHit('111', True)  # not a return (it stopped in the other breakpoint)
+
+        assert line == 6, 'Expected return to be in line 6, was: %s' % line
+
+        self.WriteRunThread(threadId)
+
+        assert 13 == self._sequence, 'Expected 13. Had: %s' % self._sequence
+
+        self.finishedOk = True
+
+
+
+#=======================================================================================================================
+# WriterThreadCase11
+#======================================================================================================================
+class WriterThreadCase11(AbstractWriterThread):
+
+    TEST_FILE = NormFile('_debugger_case10.py')
+
+    def run(self):
+        self.StartSocket()
+        self.WriteAddBreakpoint(2, 'Method1')
+        self.WriteMakeInitialRun()
+
+        threadId, frameId = self.WaitForBreakpointHit('111')
+
+        self.WriteStepOver(threadId)
+
+        threadId, frameId, line = self.WaitForBreakpointHit('108', True)
+
+        assert line == 3, 'Expected return to be in line 3, was: %s' % line
+
+        self.WriteStepOver(threadId)
+
+        threadId, frameId, line = self.WaitForBreakpointHit('108', True)
+
+        assert line == 11, 'Expected return to be in line 11, was: %s' % line
+
+        self.WriteStepOver(threadId)
+
+        threadId, frameId, line = self.WaitForBreakpointHit('108', True)
+
+        assert line == 12, 'Expected return to be in line 12, was: %s' % line
+
+        self.WriteRunThread(threadId)
+
+        assert 13 == self._sequence, 'Expected 13. Had: %s' % self._sequence
+
+        self.finishedOk = True
+
+
+
+
+#=======================================================================================================================
+# WriterThreadCase10
+#======================================================================================================================
+class WriterThreadCase10(AbstractWriterThread):
+
+    TEST_FILE = NormFile('_debugger_case10.py')
+
+    def run(self):
+        self.StartSocket()
+        self.WriteAddBreakpoint(2, 'None')  # None or Method should make hit.
+        self.WriteMakeInitialRun()
+
+        threadId, frameId = self.WaitForBreakpointHit('111')
+
+        self.WriteStepReturn(threadId)
+
+        threadId, frameId, line = self.WaitForBreakpointHit('109', True)
+
+        assert line == 11, 'Expected return to be in line 11, was: %s' % line
+
+        self.WriteStepOver(threadId)
+
+        threadId, frameId, line = self.WaitForBreakpointHit('108', True)
+
+        assert line == 12, 'Expected return to be in line 12, was: %s' % line
+
+        self.WriteRunThread(threadId)
+
+        assert 11 == self._sequence, 'Expected 11. Had: %s' % self._sequence
+
+        self.finishedOk = True
+
+
+
+#=======================================================================================================================
+# WriterThreadCase9
+#======================================================================================================================
+class WriterThreadCase9(AbstractWriterThread):
+
+    TEST_FILE = NormFile('_debugger_case89.py')
+
+    def run(self):
+        self.StartSocket()
+        self.WriteAddBreakpoint(10, 'Method3')
+        self.WriteMakeInitialRun()
+
+        threadId, frameId = self.WaitForBreakpointHit('111')
+
+        self.WriteStepOver(threadId)
+
+        threadId, frameId, line = self.WaitForBreakpointHit('108', True)
+
+        assert line == 11, 'Expected return to be in line 11, was: %s' % line
+
+        self.WriteStepOver(threadId)
+
+        threadId, frameId, line = self.WaitForBreakpointHit('108', True)
+
+        assert line == 12, 'Expected return to be in line 12, was: %s' % line
+
+        self.WriteRunThread(threadId)
+
+        assert 11 == self._sequence, 'Expected 11. Had: %s' % self._sequence
+
+        self.finishedOk = True
+
+
+#=======================================================================================================================
+# WriterThreadCase8
+#======================================================================================================================
+class WriterThreadCase8(AbstractWriterThread):
+
+    TEST_FILE = NormFile('_debugger_case89.py')
+
+    def run(self):
+        self.StartSocket()
+        self.WriteAddBreakpoint(10, 'Method3')
+        self.WriteMakeInitialRun()
+
+        threadId, frameId = self.WaitForBreakpointHit('111')
+
+        self.WriteStepReturn(threadId)
+
+        threadId, frameId, line = self.WaitForBreakpointHit('109', True)
+
+        assert line == 15, 'Expected return to be in line 15, was: %s' % line
+
+        self.WriteRunThread(threadId)
+
+        assert 9 == self._sequence, 'Expected 9. Had: %s' % self._sequence
+
+        self.finishedOk = True
+
+
+
+
+#=======================================================================================================================
+# WriterThreadCase7
+#======================================================================================================================
+class WriterThreadCase7(AbstractWriterThread):
+
+    TEST_FILE = NormFile('_debugger_case7.py')
+
+    def run(self):
+        self.StartSocket()
+        self.WriteAddBreakpoint(2, 'Call')
+        self.WriteMakeInitialRun()
+
+        threadId, frameId = self.WaitForBreakpointHit('111')
+
+        self.WriteGetFrame(threadId, frameId)
+
+        self.WaitForVars('<xml></xml>')  # no vars at this point
+
+        self.WriteStepOver(threadId)
+
+        self.WriteGetFrame(threadId, frameId)
+
+        self.WaitForVars('<xml><var name="variable_for_test_1" type="int" value="int%253A 10" />%0A</xml>')
+
+        self.WriteStepOver(threadId)
+
+        self.WriteGetFrame(threadId, frameId)
+
+        self.WaitForVars('<xml><var name="variable_for_test_1" type="int" value="int%253A 10" />%0A<var name="variable_for_test_2" type="int" value="int%253A 20" />%0A</xml>')
+
+        self.WriteRunThread(threadId)
+
+        assert 17 == self._sequence, 'Expected 17. Had: %s' % self._sequence
+
+        self.finishedOk = True
+
+
+
+#=======================================================================================================================
+# WriterThreadCase6
+#=======================================================================================================================
+class WriterThreadCase6(AbstractWriterThread):
+
+    TEST_FILE = NormFile('_debugger_case56.py')
+
+    def run(self):
+        self.StartSocket()
+        self.WriteAddBreakpoint(2, 'Call2')
+        self.WriteMakeInitialRun()
+
+        threadId, frameId = self.WaitForBreakpointHit()
+
+        self.WriteGetFrame(threadId, frameId)
+
+        self.WriteStepReturn(threadId)
+
+        threadId, frameId, line = self.WaitForBreakpointHit('109', True)
+
+        assert line == 8, 'Expecting it to go to line 8. Went to: %s' % line
+
+        self.WriteStepIn(threadId)
+
+        threadId, frameId, line = self.WaitForBreakpointHit('107', True)
+
+        # goes to line 4 in jython (function declaration line)
+        assert line in (4, 5), 'Expecting it to go to line 4 or 5. Went to: %s' % line
+
+        self.WriteRunThread(threadId)
+
+        assert 13 == self._sequence, 'Expected 15. Had: %s' % self._sequence
+
+        self.finishedOk = True
+
+#=======================================================================================================================
+# WriterThreadCase5
+#=======================================================================================================================
+class WriterThreadCase5(AbstractWriterThread):
+
+    TEST_FILE = NormFile('_debugger_case56.py')
+
+    def run(self):
+        self.StartSocket()
+        breakpoint_id = self.WriteAddBreakpoint(2, 'Call2')
+        self.WriteMakeInitialRun()
+
+        threadId, frameId = self.WaitForBreakpointHit()
+
+        self.WriteGetFrame(threadId, frameId)
+
+        self.WriteRemoveBreakpoint(breakpoint_id)
+
+        self.WriteStepReturn(threadId)
+
+        threadId, frameId, line = self.WaitForBreakpointHit('109', True)
+
+        assert line == 8, 'Expecting it to go to line 8. Went to: %s' % line
+
+        self.WriteStepIn(threadId)
+
+        threadId, frameId, line = self.WaitForBreakpointHit('107', True)
+
+        # goes to line 4 in jython (function declaration line)
+        assert line in (4, 5), 'Expecting it to go to line 4 or 5. Went to: %s' % line
+
+        self.WriteRunThread(threadId)
+
+        assert 15 == self._sequence, 'Expected 15. Had: %s' % self._sequence
+
+        self.finishedOk = True
+
+
+#=======================================================================================================================
+# WriterThreadCase4
+#=======================================================================================================================
+class WriterThreadCase4(AbstractWriterThread):
+
+    TEST_FILE = NormFile('_debugger_case4.py')
+
+    def run(self):
+        self.StartSocket()
+        self.WriteMakeInitialRun()
+
+        threadId = self.WaitForNewThread()
+
+        self.WriteSuspendThread(threadId)
+
+        time.sleep(4)  # wait for time enough for the test to finish if it wasn't suspended
+
+        self.WriteRunThread(threadId)
+
+        self.finishedOk = True
+
+
+#=======================================================================================================================
+# WriterThreadCase3
+#=======================================================================================================================
+class WriterThreadCase3(AbstractWriterThread):
+
+    TEST_FILE = NormFile('_debugger_case3.py')
+
+    def run(self):
+        self.StartSocket()
+        self.WriteMakeInitialRun()
+        time.sleep(1)
+        breakpoint_id = self.WriteAddBreakpoint(4, '')
+        self.WriteAddBreakpoint(5, 'FuncNotAvailable')  # Check that it doesn't get hit in the global when a function is available
+
+        threadId, frameId = self.WaitForBreakpointHit()
+
+        self.WriteGetFrame(threadId, frameId)
+
+        self.WriteRunThread(threadId)
+
+        threadId, frameId = self.WaitForBreakpointHit()
+
+        self.WriteGetFrame(threadId, frameId)
+
+        self.WriteRemoveBreakpoint(breakpoint_id)
+
+        self.WriteRunThread(threadId)
+
+        assert 17 == self._sequence, 'Expected 17. Had: %s' % self._sequence
+
+        self.finishedOk = True
+
+#=======================================================================================================================
+# WriterThreadCase2
+#=======================================================================================================================
+class WriterThreadCase2(AbstractWriterThread):
+
+    TEST_FILE = NormFile('_debugger_case2.py')
+
+    def run(self):
+        self.StartSocket()
+        self.WriteAddBreakpoint(3, 'Call4')  # seq = 3
+        self.WriteMakeInitialRun()
+
+        threadId, frameId = self.WaitForBreakpointHit()
+
+        self.WriteGetFrame(threadId, frameId)
+
+        self.WriteAddBreakpoint(14, 'Call2')
+
+        self.WriteRunThread(threadId)
+
+        threadId, frameId = self.WaitForBreakpointHit()
+
+        self.WriteGetFrame(threadId, frameId)
+
+        self.WriteRunThread(threadId)
+
+        assert 15 == self._sequence, 'Expected 15. Had: %s' % self._sequence
+
+        self.finishedOk = True
+
+#=======================================================================================================================
+# WriterThreadCase1
+#=======================================================================================================================
+class WriterThreadCase1(AbstractWriterThread):
+
+    TEST_FILE = NormFile('_debugger_case1.py')
+
+    def run(self):
+        self.StartSocket()
+        self.WriteAddBreakpoint(6, 'SetUp')
+        self.WriteMakeInitialRun()
+
+        threadId, frameId = self.WaitForBreakpointHit()
+
+        self.WriteGetFrame(threadId, frameId)
+
+        self.WriteStepOver(threadId)
+
+        self.WriteGetFrame(threadId, frameId)
+
+        self.WriteRunThread(threadId)
+
+        assert 13 == self._sequence, 'Expected 13. Had: %s' % self._sequence
+
+        self.finishedOk = True
+
+#=======================================================================================================================
+# DebuggerBase
+#=======================================================================================================================
+class DebuggerBase(object):
+
+    def getCommandLine(self):
+        raise NotImplementedError
+
+    def CheckCase(self, writerThreadClass):
+        UpdatePort()
+        writerThread = writerThreadClass()
+        writerThread.start()
+
+        localhost = pydev_localhost.get_localhost()
+        args = self.getCommandLine()
+        args += [
+            PYDEVD_FILE,
+            '--DEBUG_RECORD_SOCKET_READS',
+            '--client',
+            localhost,
+            '--port',
+            str(port),
+            '--file',
+            writerThread.TEST_FILE,
+        ]
+
+        if SHOW_OTHER_DEBUG_INFO:
+            print 'executing', ' '.join(args)
+
+#         process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=os.path.dirname(PYDEVD_FILE))
+        process = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=os.path.dirname(PYDEVD_FILE))
+        class ProcessReadThread(threading.Thread):
+            def run(self):
+                self.resultStr = None
+                self.resultStr = process.stdout.read()
+                process.stdout.close()
+
+            def DoKill(self):
+                process.stdout.close()
+
+        processReadThread = ProcessReadThread()
+        processReadThread.setDaemon(True)
+        processReadThread.start()
+        if SHOW_OTHER_DEBUG_INFO:
+            print 'Both processes started'
+
+        # polls can fail (because the process may finish and the thread still not -- so, we give it some more chances to
+        # finish successfully).
+        pools_failed = 0
+        while writerThread.isAlive():
+            if process.poll() is not None:
+                pools_failed += 1
+            time.sleep(.2)
+            if pools_failed == 10:
+                break
+
+        if process.poll() is None:
+            for i in range(10):
+                if processReadThread.resultStr is None:
+                    time.sleep(.5)
+                else:
+                    break
+            else:
+                writerThread.DoKill()
+
+        else:
+            if process.poll() < 0:
+                self.fail("The other process exited with error code: " + str(process.poll()) + " result:" + processReadThread.resultStr)
+
+
+        if SHOW_RESULT_STR:
+            print processReadThread.resultStr
+
+        if processReadThread.resultStr is None:
+            self.fail("The other process may still be running -- and didn't give any output")
+
+        if 'TEST SUCEEDED' not in processReadThread.resultStr:
+            self.fail(processReadThread.resultStr)
+
+        if not writerThread.finishedOk:
+            self.fail("The thread that was doing the tests didn't finish successfully. Output: %s" % processReadThread.resultStr)
+
+    def testCase1(self):
+        self.CheckCase(WriterThreadCase1)
+
+    def testCase2(self):
+        self.CheckCase(WriterThreadCase2)
+
+    def testCase3(self):
+        self.CheckCase(WriterThreadCase3)
+
+    def testCase4(self):
+        self.CheckCase(WriterThreadCase4)
+
+    def testCase5(self):
+        self.CheckCase(WriterThreadCase5)
+
+    def testCase6(self):
+        self.CheckCase(WriterThreadCase6)
+
+    def testCase7(self):
+        self.CheckCase(WriterThreadCase7)
+
+    def testCase8(self):
+        self.CheckCase(WriterThreadCase8)
+
+    def testCase9(self):
+        self.CheckCase(WriterThreadCase9)
+
+    def testCase10(self):
+        self.CheckCase(WriterThreadCase10)
+
+    def testCase11(self):
+        self.CheckCase(WriterThreadCase11)
+
+    def testCase12(self):
+        self.CheckCase(WriterThreadCase12)
+
+    def testCase13(self):
+        self.CheckCase(WriterThreadCase13)
+
+    def testCase14(self):
+        self.CheckCase(WriterThreadCase14)
+
+    def testCase15(self):
+        self.CheckCase(WriterThreadCase15)
+
+    def testCase16(self):
+        self.CheckCase(WriterThreadCase16)
+
+    def testCase17(self):
+        self.CheckCase(WriterThreadCase17)
+        
+    def testCase18(self):
+        self.CheckCase(WriterThreadCase18)
+        
+    def testCase19(self):
+        self.CheckCase(WriterThreadCase19)
+
+
+class TestPython(unittest.TestCase, DebuggerBase):
+    def getCommandLine(self):
+        return [PYTHON_EXE]
+
+class TestJython(unittest.TestCase, DebuggerBase):
+    def getCommandLine(self):
+        return [
+                JAVA_LOCATION,
+                '-classpath',
+                JYTHON_JAR_LOCATION,
+                'org.python.util.jython'
+            ]
+
+    # This case requires decorators to work (which are not present on Jython 2.1), so, this test is just removed from the jython run.
+    def testCase13(self):
+        self.skipTest("Unsupported Decorators")
+
+    def testCase16(self):
+        self.skipTest("Unsupported numpy")
+
+    # This case requires decorators to work (which are not present on Jython 2.1), so, this test is just removed from the jython run.
+    def testCase17(self):
+        self.skipTest("Unsupported Decorators")
+
+    def testCase18(self):
+        self.skipTest("Unsupported assign to local")
+
+class TestIronPython(unittest.TestCase, DebuggerBase):
+    def getCommandLine(self):
+        return [
+                IRONPYTHON_EXE,
+                '-X:Frames'
+            ]
+
+    def testCase16(self):
+        self.skipTest("Unsupported numpy")
+
+
+def GetLocationFromLine(line):
+    loc = line.split('=')[1].strip()
+    if loc.endswith(';'):
+        loc = loc[:-1]
+    if loc.endswith('"'):
+        loc = loc[:-1]
+    if loc.startswith('"'):
+        loc = loc[1:]
+    return loc
+
+
+def SplitLine(line):
+    if '=' not in line:
+        return None, None
+    var = line.split('=')[0].strip()
+    return var, GetLocationFromLine(line)
+
+
+
+import platform
+sysname = platform.system().lower()
+test_dependent = os.path.join('../../../', 'org.python.pydev.core', 'tests', 'org', 'python', 'pydev', 'core', 'TestDependent.' + sysname + '.properties')
+f = open(test_dependent)
+try:
+    for line in f.readlines():
+        var, loc = SplitLine(line)
+        if 'PYTHON_EXE' == var:
+            PYTHON_EXE = loc
+
+        if 'IRONPYTHON_EXE' == var:
+            IRONPYTHON_EXE = loc
+
+        if 'JYTHON_JAR_LOCATION' == var:
+            JYTHON_JAR_LOCATION = loc
+
+        if 'JAVA_LOCATION' == var:
+            JAVA_LOCATION = loc
+finally:
+    f.close()
+
+assert PYTHON_EXE, 'PYTHON_EXE not found in %s' % (test_dependent,)
+assert IRONPYTHON_EXE, 'IRONPYTHON_EXE not found in %s' % (test_dependent,)
+assert JYTHON_JAR_LOCATION, 'JYTHON_JAR_LOCATION not found in %s' % (test_dependent,)
+assert JAVA_LOCATION, 'JAVA_LOCATION not found in %s' % (test_dependent,)
+assert os.path.exists(PYTHON_EXE), 'The location: %s is not valid' % (PYTHON_EXE,)
+assert os.path.exists(IRONPYTHON_EXE), 'The location: %s is not valid' % (IRONPYTHON_EXE,)
+assert os.path.exists(JYTHON_JAR_LOCATION), 'The location: %s is not valid' % (JYTHON_JAR_LOCATION,)
+assert os.path.exists(JAVA_LOCATION), 'The location: %s is not valid' % (JAVA_LOCATION,)
+
+if False:
+    suite = unittest.TestSuite()
+    #PYTHON_EXE = r'C:\bin\Anaconda\python.exe'
+#     suite.addTest(TestPython('testCase10'))
+#     suite.addTest(TestPython('testCase3'))
+#     suite.addTest(TestPython('testCase16'))
+#     suite.addTest(TestPython('testCase17'))
+#     suite.addTest(TestPython('testCase18'))
+#     suite.addTest(TestPython('testCase19'))
+    suite = unittest.makeSuite(TestPython)
+    unittest.TextTestRunner(verbosity=3).run(suite)
+    
+#    unittest.TextTestRunner(verbosity=3).run(suite)
+#    
+#    suite = unittest.makeSuite(TestJython)
+#    unittest.TextTestRunner(verbosity=3).run(suite)
diff --git a/python/helpers/pydev/tests_python/test_pydev_monkey.py b/python/helpers/pydev/tests_python/test_pydev_monkey.py
new file mode 100644
index 0000000..3eb7930
--- /dev/null
+++ b/python/helpers/pydev/tests_python/test_pydev_monkey.py
@@ -0,0 +1,21 @@
+import unittest
+import pydev_monkey
+import sys
+
+
+
+class TestCase(unittest.TestCase):
+
+    def test_monkey(self):
+        check='''C:\\bin\\python.exe -u -c "
+connect(\\"127.0.0.1\\")
+"'''
+        sys.original_argv = []
+        self.assertEqual('"-u" "-c" "\nconnect(\\"127.0.0.1\\")\n"', pydev_monkey.patch_arg_str_win(check))
+
+    def test_str_to_args_windows(self):
+        
+        self.assertEqual(['a', 'b'], pydev_monkey.str_to_args_windows('a "b"'))
+        
+if __name__ == '__main__':
+    unittest.main()
\ No newline at end of file
diff --git a/python/helpers/pydev/tests_python/test_save_locals.py b/python/helpers/pydev/tests_python/test_save_locals.py
new file mode 100644
index 0000000..fe65d4d
--- /dev/null
+++ b/python/helpers/pydev/tests_python/test_save_locals.py
@@ -0,0 +1,99 @@
+import inspect
+import sys
+import unittest
+
+from pydevd_save_locals import save_locals
+
+
+def use_save_locals(name, value):
+    """
+    Attempt to set the local of the given name to value, using locals_to_fast.
+    """
+    frame = inspect.currentframe().f_back
+    locals_dict = frame.f_locals
+    locals_dict[name] = value
+
+    save_locals(frame)
+
+
+def test_method(fn):
+    """
+    A harness for testing methods that attempt to modify the values of locals on the stack.
+    """
+    x = 1
+
+    # The method 'fn' should attempt to set x = 2 in the current frame.
+    fn('x', 2)
+
+    return x
+
+
+
+class TestSetLocals(unittest.TestCase):
+    """
+    Test setting locals in one function from another function using several approaches.
+    """
+
+
+    def test_set_locals_using_save_locals(self):
+        x = test_method(use_save_locals)
+        self.assertEqual(x, 2)  # Expected to succeed
+
+
+    def test_frame_simple_change(self):
+        frame = sys._getframe()
+        a = 20
+        frame.f_locals['a'] = 50
+        save_locals(frame)
+        self.assertEquals(50, a)
+
+
+    def test_frame_co_freevars(self):
+
+        outer_var = 20
+
+        def func():
+            frame = sys._getframe()
+            frame.f_locals['outer_var'] = 50
+            save_locals(frame)
+            self.assertEquals(50, outer_var)
+
+        func()
+
+    def test_frame_co_cellvars(self):
+
+        def check_co_vars(a):
+            frame = sys._getframe()
+            def function2():
+                print a
+
+            assert 'a' in frame.f_code.co_cellvars
+            frame = sys._getframe()
+            frame.f_locals['a'] = 50
+            save_locals(frame)
+            self.assertEquals(50, a)
+
+        check_co_vars(1)
+
+
+    def test_frame_change_in_inner_frame(self):
+        def change(f):
+            self.assert_(f is not sys._getframe())
+            f.f_locals['a']= 50
+            save_locals(f)
+
+
+        frame = sys._getframe()
+        a = 20
+        change(frame)
+        self.assertEquals(50, a)
+
+
+if __name__ == '__main__':
+    suite = unittest.TestSuite()
+#    suite.addTest(TestSetLocals('test_set_locals_using_dict'))
+#    #suite.addTest(Test('testCase10a'))
+#    unittest.TextTestRunner(verbosity=3).run(suite)
+
+    suite = unittest.makeSuite(TestSetLocals)
+    unittest.TextTestRunner(verbosity=3).run(suite)
diff --git a/python/helpers/pydev/tests_runfiles/not_in_default_pythonpath.txt b/python/helpers/pydev/tests_runfiles/not_in_default_pythonpath.txt
new file mode 100644
index 0000000..29cdc5b
--- /dev/null
+++ b/python/helpers/pydev/tests_runfiles/not_in_default_pythonpath.txt
@@ -0,0 +1 @@
+(no __init__.py file)
\ No newline at end of file
diff --git a/python/helpers/pydev/tests_runfiles/samples/.cvsignore b/python/helpers/pydev/tests_runfiles/samples/.cvsignore
new file mode 100644
index 0000000..d1c8995
--- /dev/null
+++ b/python/helpers/pydev/tests_runfiles/samples/.cvsignore
@@ -0,0 +1,2 @@
+*.class
+*.pyc
diff --git a/python/helpers/pydev/tests_runfiles/samples/__init__.py b/python/helpers/pydev/tests_runfiles/samples/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/python/helpers/pydev/tests_runfiles/samples/__init__.py
diff --git a/python/helpers/pydev/tests_runfiles/samples/nested_dir/__init__.py b/python/helpers/pydev/tests_runfiles/samples/nested_dir/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/python/helpers/pydev/tests_runfiles/samples/nested_dir/__init__.py
@@ -0,0 +1 @@
+
diff --git a/python/helpers/pydev/tests_runfiles/samples/nested_dir/nested2/__init__.py b/python/helpers/pydev/tests_runfiles/samples/nested_dir/nested2/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/python/helpers/pydev/tests_runfiles/samples/nested_dir/nested2/__init__.py
@@ -0,0 +1 @@
+
diff --git a/python/helpers/pydev/tests_runfiles/samples/nested_dir/nested2/deep_nest_test.py b/python/helpers/pydev/tests_runfiles/samples/nested_dir/nested2/deep_nest_test.py
new file mode 100644
index 0000000..7b1972b
--- /dev/null
+++ b/python/helpers/pydev/tests_runfiles/samples/nested_dir/nested2/deep_nest_test.py
@@ -0,0 +1,22 @@
+import unittest
+
+class SampleTest(unittest.TestCase):
+    
+    def setUp(self):
+        return
+
+    def tearDown(self):
+        return
+
+    def test_non_unique_name(self):
+        pass
+
+    def test_asdf2(self):
+        pass
+
+    def test_i_am_a_unique_test_name(self):
+        pass
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/python/helpers/pydev/tests_runfiles/samples/nested_dir/nested2/non_test_file.py b/python/helpers/pydev/tests_runfiles/samples/nested_dir/nested2/non_test_file.py
new file mode 100644
index 0000000..470c650
--- /dev/null
+++ b/python/helpers/pydev/tests_runfiles/samples/nested_dir/nested2/non_test_file.py
@@ -0,0 +1,3 @@
+
+""" i am a python file with no tests """
+pass
diff --git a/python/helpers/pydev/tests_runfiles/samples/nested_dir/nested3/__init__.py b/python/helpers/pydev/tests_runfiles/samples/nested_dir/nested3/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/python/helpers/pydev/tests_runfiles/samples/nested_dir/nested3/__init__.py
@@ -0,0 +1 @@
+
diff --git a/python/helpers/pydev/tests_runfiles/samples/nested_dir/nested3/junk.txt b/python/helpers/pydev/tests_runfiles/samples/nested_dir/nested3/junk.txt
new file mode 100644
index 0000000..14dd4dd
--- /dev/null
+++ b/python/helpers/pydev/tests_runfiles/samples/nested_dir/nested3/junk.txt
@@ -0,0 +1 @@
+im a junk file
diff --git a/python/helpers/pydev/tests_runfiles/samples/nested_dir/nested3/non_test_file.py b/python/helpers/pydev/tests_runfiles/samples/nested_dir/nested3/non_test_file.py
new file mode 100644
index 0000000..470c650
--- /dev/null
+++ b/python/helpers/pydev/tests_runfiles/samples/nested_dir/nested3/non_test_file.py
@@ -0,0 +1,3 @@
+
+""" i am a python file with no tests """
+pass
diff --git a/python/helpers/pydev/tests_runfiles/samples/nested_dir/non_test_file.py b/python/helpers/pydev/tests_runfiles/samples/nested_dir/non_test_file.py
new file mode 100644
index 0000000..470c650
--- /dev/null
+++ b/python/helpers/pydev/tests_runfiles/samples/nested_dir/non_test_file.py
@@ -0,0 +1,3 @@
+
+""" i am a python file with no tests """
+pass
diff --git a/python/helpers/pydev/tests_runfiles/samples/nested_dir/simple4_test.py b/python/helpers/pydev/tests_runfiles/samples/nested_dir/simple4_test.py
new file mode 100644
index 0000000..ba5d45f
--- /dev/null
+++ b/python/helpers/pydev/tests_runfiles/samples/nested_dir/simple4_test.py
@@ -0,0 +1,16 @@
+import unittest
+
+class NestedSampleTest(unittest.TestCase):
+    
+    def setUp(self):
+        return
+
+    def tearDown(self):
+        return
+
+    def test_non_unique_name(self):
+        pass
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/python/helpers/pydev/tests_runfiles/samples/non_test_file.py b/python/helpers/pydev/tests_runfiles/samples/non_test_file.py
new file mode 100644
index 0000000..470c650
--- /dev/null
+++ b/python/helpers/pydev/tests_runfiles/samples/non_test_file.py
@@ -0,0 +1,3 @@
+
+""" i am a python file with no tests """
+pass
diff --git a/python/helpers/pydev/tests_runfiles/samples/simple2_test.py b/python/helpers/pydev/tests_runfiles/samples/simple2_test.py
new file mode 100644
index 0000000..d46468e
--- /dev/null
+++ b/python/helpers/pydev/tests_runfiles/samples/simple2_test.py
@@ -0,0 +1,16 @@
+import unittest
+
+class YetAnotherSampleTest(unittest.TestCase):
+    
+    def setUp(self):
+        return
+
+    def tearDown(self):
+        return
+
+    def test_abc(self):
+        pass
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/python/helpers/pydev/tests_runfiles/samples/simple3_test.py b/python/helpers/pydev/tests_runfiles/samples/simple3_test.py
new file mode 100644
index 0000000..da1ccbf
--- /dev/null
+++ b/python/helpers/pydev/tests_runfiles/samples/simple3_test.py
@@ -0,0 +1,16 @@
+import unittest
+
+class StillYetAnotherSampleTest(unittest.TestCase):
+    
+    def setUp(self):
+        return
+
+    def tearDown(self):
+        return
+
+    def test_non_unique_name(self):
+        pass
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/python/helpers/pydev/tests_runfiles/samples/simpleClass_test.py b/python/helpers/pydev/tests_runfiles/samples/simpleClass_test.py
new file mode 100644
index 0000000..3a9c900
--- /dev/null
+++ b/python/helpers/pydev/tests_runfiles/samples/simpleClass_test.py
@@ -0,0 +1,14 @@
+import unittest
+
+class SetUpClassTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        raise ValueError("This is an INTENTIONAL value error in setUpClass.")
+
+    def test_blank(self):
+        pass
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/python/helpers/pydev/tests_runfiles/samples/simpleModule_test.py b/python/helpers/pydev/tests_runfiles/samples/simpleModule_test.py
new file mode 100644
index 0000000..fdde67e
--- /dev/null
+++ b/python/helpers/pydev/tests_runfiles/samples/simpleModule_test.py
@@ -0,0 +1,16 @@
+import unittest
+
+def setUpModule():
+    raise ValueError("This is an INTENTIONAL value error in setUpModule.")
+
+class SetUpModuleTest(unittest.TestCase):
+    
+    def setUp(cls):
+        pass
+
+    def test_blank(self):
+        pass
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/python/helpers/pydev/tests_runfiles/samples/simple_test.py b/python/helpers/pydev/tests_runfiles/samples/simple_test.py
new file mode 100644
index 0000000..619df7c
--- /dev/null
+++ b/python/helpers/pydev/tests_runfiles/samples/simple_test.py
@@ -0,0 +1,45 @@
+import unittest
+
+class SampleTest(unittest.TestCase):
+    
+    def setUp(self):
+        pass
+
+    def tearDown(self):
+        pass
+
+    def test_xxxxxx1(self):
+        self.fail('Fail test 2')
+    def test_xxxxxx2(self):
+        pass
+    def test_xxxxxx3(self):
+        pass
+    def test_xxxxxx4(self):
+        pass
+    def test_non_unique_name(self):
+        print('non unique name ran')
+
+
+class AnotherSampleTest(unittest.TestCase):
+    def setUp(self):
+        pass
+
+    def tearDown(self):
+        pass
+
+    def test_1(self):
+        pass
+    def test_2(self):
+        """ im a doc string"""
+        pass
+    def todo_not_tested(self):
+        '''
+        Not there by default!
+        '''
+
+
+if __name__ == '__main__':
+#    suite = unittest.makeSuite(SampleTest, 'test')
+#    runner = unittest.TextTestRunner( verbosity=3 )
+#    runner.run(suite)
+    unittest.main()
diff --git a/python/helpers/pydev/tests_runfiles/test_pydevd_property.py b/python/helpers/pydev/tests_runfiles/test_pydevd_property.py
new file mode 100644
index 0000000..64fa9b6
--- /dev/null
+++ b/python/helpers/pydev/tests_runfiles/test_pydevd_property.py
@@ -0,0 +1,134 @@
+'''
+Created on Aug 22, 2011
+
+@author: hussain.bohra
+@author: fabioz
+'''
+
+import os
+import sys
+import unittest
+
+#=======================================================================================================================
+# Test
+#=======================================================================================================================
+class Test(unittest.TestCase):
+    """Test cases to validate custom property implementation in pydevd 
+    """
+    
+    def setUp(self, nused=None):
+        self.tempdir = os.path.join(os.path.dirname(os.path.dirname(__file__)))
+        sys.path.insert(0, self.tempdir)
+        import pydevd_traceproperty
+        self.old = pydevd_traceproperty.replace_builtin_property()
+    
+    
+    def tearDown(self, unused=None):
+        import pydevd_traceproperty
+        pydevd_traceproperty.replace_builtin_property(self.old)
+        sys.path.remove(self.tempdir)
+
+
+    def testProperty(self):
+        """Test case to validate custom property
+        """
+        
+        import pydevd_traceproperty
+        class TestProperty(object):
+            
+            def __init__(self):
+                self._get = 0
+                self._set = 0
+                self._del = 0
+                
+            def get_name(self):
+                self._get += 1
+                return self.__name
+            
+            def set_name(self, value):
+                self._set += 1
+                self.__name = value
+                
+            def del_name(self):
+                self._del += 1
+                del self.__name
+            name = property(get_name, set_name, del_name, "name's docstring")
+            self.assertEqual(name.__class__, pydevd_traceproperty.DebugProperty)
+            
+        testObj = TestProperty()
+        self._check(testObj)
+        
+        
+    def testProperty2(self):
+        """Test case to validate custom property
+        """
+        
+        class TestProperty(object):
+            
+            def __init__(self):
+                self._get = 0
+                self._set = 0
+                self._del = 0
+            
+            def name(self):
+                self._get += 1
+                return self.__name
+            name = property(name)
+            
+            def set_name(self, value):
+                self._set += 1
+                self.__name = value
+            name.setter(set_name)
+                
+            def del_name(self):
+                self._del += 1
+                del self.__name
+            name.deleter(del_name)
+
+        testObj = TestProperty()
+        self._check(testObj)
+        
+        
+    def testProperty3(self):
+        """Test case to validate custom property
+        """
+        
+        class TestProperty(object):
+            
+            def __init__(self):
+                self._name = 'foo'
+            
+            def name(self):
+                return self._name
+            name = property(name)
+
+        testObj = TestProperty()
+        self.assertRaises(AttributeError, setattr, testObj, 'name', 'bar')
+        self.assertRaises(AttributeError, delattr, testObj, 'name')
+        
+        
+    def _check(self, testObj):
+        testObj.name = "Custom"
+        self.assertEqual(1, testObj._set)
+        
+        self.assertEqual(testObj.name, "Custom")
+        self.assertEqual(1, testObj._get)
+        
+        self.assert_(hasattr(testObj, 'name'))
+        del testObj.name
+        self.assertEqual(1, testObj._del)
+        
+        self.assert_(not hasattr(testObj, 'name'))
+        testObj.name = "Custom2"
+        self.assertEqual(testObj.name, "Custom2")
+
+
+        
+#=======================================================================================================================
+# main
+#=======================================================================================================================
+if __name__ == '__main__':
+    #this is so that we can run it from the jython tests -- because we don't actually have an __main__ module
+    #(so, it won't try importing the __main__ module)
+    unittest.TextTestRunner().run(unittest.makeSuite(Test))
+    
diff --git a/python/helpers/pydev/tests_runfiles/test_pydevdio.py b/python/helpers/pydev/tests_runfiles/test_pydevdio.py
new file mode 100644
index 0000000..7a48a63
--- /dev/null
+++ b/python/helpers/pydev/tests_runfiles/test_pydevdio.py
@@ -0,0 +1,40 @@
+import sys
+import os
+
+
+import unittest
+
+class Test(unittest.TestCase):
+    
+    def testIt(self):
+        #make it as if we were executing from the directory above this one (so that we can use jycompletionserver
+        #without the need for it being in the pythonpath)
+        #(twice the dirname to get the previous level from this file.)
+        import test_pydevdio #@UnresolvedImport - importing itself
+        ADD_TO_PYTHONPATH = os.path.join(os.path.dirname(os.path.dirname(test_pydevdio.__file__)))
+        sys.path.insert(0, ADD_TO_PYTHONPATH)
+        
+        try:
+            import pydevd_io
+            original = sys.stdout
+            
+            try:
+                sys.stdout = pydevd_io.IOBuf()
+                print('foo')
+                print('bar')
+                
+                self.assertEquals('foo\nbar\n', sys.stdout.getvalue()) #@UndefinedVariable
+                
+                print('ww')
+                print('xx')
+                self.assertEquals('ww\nxx\n', sys.stdout.getvalue()) #@UndefinedVariable
+            finally:
+                sys.stdout = original
+        finally:
+            #remove it to leave it ok for other tests
+            sys.path.remove(ADD_TO_PYTHONPATH)
+        
+if __name__ == '__main__':
+    #this is so that we can run it frem the jython tests -- because we don't actually have an __main__ module
+    #(so, it won't try importing the __main__ module)
+    unittest.TextTestRunner().run(unittest.makeSuite(Test))
diff --git a/python/helpers/pydev/tests_runfiles/test_runfiles.py b/python/helpers/pydev/tests_runfiles/test_runfiles.py
new file mode 100644
index 0000000..0c04764
--- /dev/null
+++ b/python/helpers/pydev/tests_runfiles/test_runfiles.py
@@ -0,0 +1,393 @@
+import os.path
+import sys
+
+IS_JYTHON = sys.platform.find('java') != -1
+
+try:
+    this_file_name = __file__
+except NameError:
+    # stupid jython. plain old __file__ isnt working for some reason
+    import test_runfiles  #@UnresolvedImport - importing the module itself
+    this_file_name = test_runfiles.__file__
+
+
+desired_runfiles_path = os.path.normpath(os.path.dirname(this_file_name) + "/..")
+sys.path.insert(0, desired_runfiles_path)
+
+import pydev_runfiles_unittest
+import pydev_runfiles_xml_rpc
+import pydevd_io
+
+#remove existing pydev_runfiles from modules (if any), so that we can be sure we have the correct version
+if 'pydev_runfiles' in sys.modules:
+    del sys.modules['pydev_runfiles']
+
+
+import pydev_runfiles
+import unittest
+import tempfile
+
+try:
+    set
+except:
+    from sets import Set as set
+
+#this is an early test because it requires the sys.path changed
+orig_syspath = sys.path
+a_file = pydev_runfiles.__file__
+pydev_runfiles.PydevTestRunner(pydev_runfiles.Configuration(files_or_dirs=[a_file]))
+file_dir = os.path.dirname(a_file)
+assert file_dir in sys.path
+sys.path = orig_syspath[:]
+
+#remove it so that we leave it ok for other tests
+sys.path.remove(desired_runfiles_path)
+
+class RunfilesTest(unittest.TestCase):
+
+    def _setup_scenario(
+        self,
+        path,
+        include_tests=None,
+        tests=None,
+        files_to_tests=None,
+        exclude_files=None,
+        exclude_tests=None,
+        include_files=None,
+        ):
+        self.MyTestRunner = pydev_runfiles.PydevTestRunner(
+            pydev_runfiles.Configuration(
+                files_or_dirs=path,
+                include_tests=include_tests,
+                verbosity=1,
+                tests=tests,
+                files_to_tests=files_to_tests,
+                exclude_files=exclude_files,
+                exclude_tests=exclude_tests,
+                include_files=include_files,
+            )
+        )
+        self.files = self.MyTestRunner.find_import_files()
+        self.modules = self.MyTestRunner.find_modules_from_files(self.files)
+        self.all_tests = self.MyTestRunner.find_tests_from_modules(self.modules)
+        self.filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
+
+    def setUp(self):
+        self.file_dir = [os.path.abspath(os.path.join(desired_runfiles_path, 'tests_runfiles/samples'))]
+        self._setup_scenario(self.file_dir, None)
+
+
+    def test_suite_used(self):
+        for suite in self.all_tests + self.filtered_tests:
+            self.assert_(isinstance(suite, pydev_runfiles_unittest.PydevTestSuite))
+
+    def test_parse_cmdline(self):
+        sys.argv = "pydev_runfiles.py ./".split()
+        configuration = pydev_runfiles.parse_cmdline()
+        self.assertEquals([sys.argv[1]], configuration.files_or_dirs)
+        self.assertEquals(2, configuration.verbosity)  # default value
+        self.assertEquals(None, configuration.include_tests)  # default value
+
+        sys.argv = "pydev_runfiles.py ../images c:/temp".split()
+        configuration = pydev_runfiles.parse_cmdline()
+        self.assertEquals(sys.argv[1:3], configuration.files_or_dirs)
+        self.assertEquals(2, configuration.verbosity)
+
+        sys.argv = "pydev_runfiles.py --verbosity 3 ../junk c:/asdf ".split()
+        configuration = pydev_runfiles.parse_cmdline()
+        self.assertEquals(sys.argv[3:], configuration.files_or_dirs)
+        self.assertEquals(int(sys.argv[2]), configuration.verbosity)
+
+        sys.argv = "pydev_runfiles.py --include_tests test_def ./".split()
+        configuration = pydev_runfiles.parse_cmdline()
+        self.assertEquals([sys.argv[-1]], configuration.files_or_dirs)
+        self.assertEquals([sys.argv[2]], configuration.include_tests)
+
+        sys.argv = "pydev_runfiles.py --include_tests Abc.test_def,Mod.test_abc c:/junk/".split()
+        configuration = pydev_runfiles.parse_cmdline()
+        self.assertEquals([sys.argv[-1]], configuration.files_or_dirs)
+        self.assertEquals(sys.argv[2].split(','), configuration.include_tests)
+
+        sys.argv = ('C:\\eclipse-SDK-3.2-win32\\eclipse\\plugins\\org.python.pydev.debug_1.2.2\\pysrc\\pydev_runfiles.py ' + 
+                    '--verbosity 1 ' + 
+                    'C:\\workspace_eclipse\\fronttpa\\tests\\gui_tests\\calendar_popup_control_test.py ').split()
+        configuration = pydev_runfiles.parse_cmdline()
+        self.assertEquals([sys.argv[-1]], configuration.files_or_dirs)
+        self.assertEquals(1, configuration.verbosity)
+
+        sys.argv = "pydev_runfiles.py --verbosity 1 --include_tests Mod.test_abc c:/junk/ ./".split()
+        configuration = pydev_runfiles.parse_cmdline()
+        self.assertEquals(sys.argv[5:], configuration.files_or_dirs)
+        self.assertEquals(int(sys.argv[2]), configuration.verbosity)
+        self.assertEquals([sys.argv[4]], configuration.include_tests)
+
+        sys.argv = "pydev_runfiles.py --exclude_files=*.txt,a*.py".split()
+        configuration = pydev_runfiles.parse_cmdline()
+        self.assertEquals(['*.txt', 'a*.py'], configuration.exclude_files)
+
+        sys.argv = "pydev_runfiles.py --exclude_tests=*__todo,test*bar".split()
+        configuration = pydev_runfiles.parse_cmdline()
+        self.assertEquals(['*__todo', 'test*bar'], configuration.exclude_tests)
+
+
+    def test___adjust_python_path_works_for_directories(self):
+        orig_syspath = sys.path
+        tempdir = tempfile.gettempdir()
+        pydev_runfiles.PydevTestRunner(pydev_runfiles.Configuration(files_or_dirs=[tempdir]))
+        self.assertEquals(1, tempdir in sys.path)
+        sys.path = orig_syspath[:]
+
+
+    def test___is_valid_py_file(self):
+        isvalid = self.MyTestRunner._PydevTestRunner__is_valid_py_file
+        self.assertEquals(1, isvalid("test.py"))
+        self.assertEquals(0, isvalid("asdf.pyc"))
+        self.assertEquals(0, isvalid("__init__.py"))
+        self.assertEquals(0, isvalid("__init__.pyc"))
+        self.assertEquals(1, isvalid("asdf asdf.pyw"))
+
+    def test___unixify(self):
+        unixify = self.MyTestRunner._PydevTestRunner__unixify
+        self.assertEquals("c:/temp/junk/asdf.py", unixify("c:SEPtempSEPjunkSEPasdf.py".replace('SEP', os.sep)))
+
+    def test___importify(self):
+        importify = self.MyTestRunner._PydevTestRunner__importify
+        self.assertEquals("temp.junk.asdf", importify("temp/junk/asdf.py"))
+        self.assertEquals("asdf", importify("asdf.py"))
+        self.assertEquals("abc.def.hgi", importify("abc/def/hgi"))
+
+    def test_finding_a_file_from_file_system(self):
+        test_file = "simple_test.py"
+        self.MyTestRunner.files_or_dirs = [self.file_dir[0] + test_file]
+        files = self.MyTestRunner.find_import_files()
+        self.assertEquals(1, len(files))
+        self.assertEquals(files[0], self.file_dir[0] + test_file)
+
+    def test_finding_files_in_dir_from_file_system(self):
+        self.assertEquals(1, len(self.files) > 0)
+        for import_file in self.files:
+            self.assertEquals(-1, import_file.find(".pyc"))
+            self.assertEquals(-1, import_file.find("__init__.py"))
+            self.assertEquals(-1, import_file.find("\\"))
+            self.assertEquals(-1, import_file.find(".txt"))
+
+    def test___get_module_from_str(self):
+        my_importer = self.MyTestRunner._PydevTestRunner__get_module_from_str
+        my_os_path = my_importer("os.path", True, 'unused')
+        from os import path
+        import os.path as path2
+        self.assertEquals(path, my_os_path)
+        self.assertEquals(path2, my_os_path)
+        self.assertNotEquals(__import__("os.path"), my_os_path)
+        self.assertNotEquals(__import__("os"), my_os_path)
+
+    def test_finding_modules_from_import_strings(self):
+        self.assertEquals(1, len(self.modules) > 0)
+
+    def test_finding_tests_when_no_filter(self):
+        # unittest.py will create a TestCase with 0 tests in it
+        # since it just imports what is given
+        self.assertEquals(1, len(self.all_tests) > 0)
+        files_with_tests = [1 for t in self.all_tests if len(t._tests) > 0]
+        self.assertNotEquals(len(self.files), len(files_with_tests))
+
+    def count_tests(self, tests):
+        total = 0
+        for t in tests:
+            total += t.countTestCases()
+        return total
+
+    def test___match(self):
+        matcher = self.MyTestRunner._PydevTestRunner__match
+        self.assertEquals(1, matcher(None, "aname"))
+        self.assertEquals(1, matcher([".*"], "aname"))
+        self.assertEquals(0, matcher(["^x$"], "aname"))
+        self.assertEquals(0, matcher(["abc"], "aname"))
+        self.assertEquals(1, matcher(["abc", "123"], "123"))
+
+    def test_finding_tests_from_modules_with_bad_filter_returns_0_tests(self):
+        self._setup_scenario(self.file_dir, ["NO_TESTS_ARE_SURE_TO_HAVE_THIS_NAME"])
+        self.assertEquals(0, self.count_tests(self.all_tests))
+
+    def test_finding_test_with_unique_name_returns_1_test(self):
+        self._setup_scenario(self.file_dir, include_tests=["test_i_am_a_unique_test_name"])
+        filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
+        self.assertEquals(1, self.count_tests(filtered_tests))
+
+    def test_finding_test_with_non_unique_name(self):
+        self._setup_scenario(self.file_dir, include_tests=["test_non_unique_name"])
+        filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
+        self.assertEquals(1, self.count_tests(filtered_tests) > 2)
+
+    def test_finding_tests_with_regex_filters(self):
+        self._setup_scenario(self.file_dir, include_tests=["test_non*"])
+        filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
+        self.assertEquals(1, self.count_tests(filtered_tests) > 2)
+
+        self._setup_scenario(self.file_dir, ["^$"])
+        filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
+        self.assertEquals(0, self.count_tests(filtered_tests))
+
+        self._setup_scenario(self.file_dir, None, exclude_tests=["*"])
+        filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
+        self.assertEquals(0, self.count_tests(filtered_tests))
+
+    def test_matching_tests(self):
+        self._setup_scenario(self.file_dir, None, ['StillYetAnotherSampleTest'])
+        filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
+        self.assertEqual(1, self.count_tests(filtered_tests))
+
+        self._setup_scenario(self.file_dir, None, ['SampleTest.test_xxxxxx1'])
+        filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
+        self.assertEqual(1, self.count_tests(filtered_tests))
+
+        self._setup_scenario(self.file_dir, None, ['SampleTest'])
+        filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
+        self.assertEqual(8, self.count_tests(filtered_tests))
+
+        self._setup_scenario(self.file_dir, None, ['AnotherSampleTest.todo_not_tested'])
+        filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
+        self.assertEqual(1, self.count_tests(filtered_tests))
+
+        self._setup_scenario(self.file_dir, None, ['StillYetAnotherSampleTest', 'SampleTest.test_xxxxxx1'])
+        filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
+        self.assertEqual(2, self.count_tests(filtered_tests))
+
+        self._setup_scenario(self.file_dir, None, exclude_tests=['*'])
+        filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
+        self.assertEqual(self.count_tests(filtered_tests), 0)
+
+
+        self._setup_scenario(self.file_dir, None, exclude_tests=['*a*'])
+        filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
+        self.assertEqual(self.count_tests(filtered_tests), 6)
+
+        self.assertEqual(
+            set(self.MyTestRunner.list_test_names(filtered_tests)),
+            set(['test_1', 'test_2', 'test_xxxxxx1', 'test_xxxxxx2', 'test_xxxxxx3', 'test_xxxxxx4'])
+        )
+
+        self._setup_scenario(self.file_dir, None, exclude_tests=['*a*', '*x*'])
+        filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
+        self.assertEqual(self.count_tests(filtered_tests), 2)
+
+        self.assertEqual(
+            set(self.MyTestRunner.list_test_names(filtered_tests)),
+            set(['test_1', 'test_2'])
+        )
+
+        self._setup_scenario(self.file_dir, None, exclude_files=['simple_test.py'])
+        filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
+        names = self.MyTestRunner.list_test_names(filtered_tests)
+        self.assert_('test_xxxxxx1' not in names, 'Found: %s' % (names,))
+
+        self.assertEqual(
+            set(['test_abc', 'test_non_unique_name', 'test_non_unique_name', 'test_asdf2', 'test_i_am_a_unique_test_name', 'test_non_unique_name', 'test_blank']),
+            set(names)
+        )
+
+        self._setup_scenario(self.file_dir, None, include_files=['simple3_test.py'])
+        filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
+        names = self.MyTestRunner.list_test_names(filtered_tests)
+        self.assert_('test_xxxxxx1' not in names, 'Found: %s' % (names,))
+
+        self.assertEqual(
+            set(['test_non_unique_name']),
+            set(names)
+        )
+
+    def test_xml_rpc_communication(self):
+        notifications = []
+        class Server:
+
+            def __init__(self, notifications):
+                self.notifications = notifications
+
+            def notifyConnected(self):
+                #This method is called at the very start (in runfiles.py), and we do not check this here
+                raise AssertionError('Should not be called from the run tests.')
+
+
+            def notifyTestsCollected(self, number_of_tests):
+                self.notifications.append(('notifyTestsCollected', number_of_tests))
+
+
+            def notifyStartTest(self, file, test):
+                pass
+
+            def notifyTest(self, cond, captured_output, error_contents, file, test, time):
+                try:
+                    #I.e.: when marked as Binary in xml-rpc
+                    captured_output = captured_output.data
+                except:
+                    pass
+                try:
+                    #I.e.: when marked as Binary in xml-rpc
+                    error_contents = error_contents.data
+                except:
+                    pass
+                if error_contents:
+                    error_contents = error_contents.splitlines()[-1].strip()
+                self.notifications.append(('notifyTest', cond, captured_output.strip(), error_contents, file, test))
+
+            def notifyTestRunFinished(self, total_time):
+                self.notifications.append(('notifyTestRunFinished',))
+
+        server = Server(notifications)
+        pydev_runfiles_xml_rpc.SetServer(server)
+        simple_test = os.path.join(self.file_dir[0], 'simple_test.py')
+        simple_test2 = os.path.join(self.file_dir[0], 'simple2_test.py')
+        simpleClass_test = os.path.join(self.file_dir[0], 'simpleClass_test.py')
+        simpleModule_test = os.path.join(self.file_dir[0], 'simpleModule_test.py')
+
+        files_to_tests = {}
+        files_to_tests.setdefault(simple_test , []).append('SampleTest.test_xxxxxx1')
+        files_to_tests.setdefault(simple_test , []).append('SampleTest.test_xxxxxx2')
+        files_to_tests.setdefault(simple_test , []).append('SampleTest.test_non_unique_name')
+        files_to_tests.setdefault(simple_test2, []).append('YetAnotherSampleTest.test_abc')
+        files_to_tests.setdefault(simpleClass_test, []).append('SetUpClassTest.test_blank')
+        files_to_tests.setdefault(simpleModule_test, []).append('SetUpModuleTest.test_blank')
+
+        self._setup_scenario(None, files_to_tests=files_to_tests)
+        self.MyTestRunner.verbosity = 2
+
+        buf = pydevd_io.StartRedirect(keep_original_redirection=False)
+        try:
+            self.MyTestRunner.run_tests()
+            self.assertEqual(8, len(notifications))
+            expected = [
+                    ('notifyTestsCollected', 6),
+                    ('notifyTest', 'ok', 'non unique name ran', '', simple_test, 'SampleTest.test_non_unique_name'),
+                    ('notifyTest', 'fail', '', 'AssertionError: Fail test 2', simple_test, 'SampleTest.test_xxxxxx1'),
+                    ('notifyTest', 'ok', '', '', simple_test, 'SampleTest.test_xxxxxx2'),
+                    ('notifyTest', 'ok', '', '', simple_test2, 'YetAnotherSampleTest.test_abc'),
+                ]
+            if not IS_JYTHON:
+                expected.append(('notifyTest', 'error', '', 'ValueError: This is an INTENTIONAL value error in setUpClass.',
+                        simpleClass_test.replace('/', os.path.sep), 'samples.simpleClass_test.SetUpClassTest <setUpClass>'))
+                expected.append(('notifyTest', 'error', '', 'ValueError: This is an INTENTIONAL value error in setUpModule.',
+                            simpleModule_test.replace('/', os.path.sep), 'samples.simpleModule_test <setUpModule>'))
+            else:
+                expected.append(('notifyTest', 'ok', '', '', simpleClass_test, 'SetUpClassTest.test_blank'))
+                expected.append(('notifyTest', 'ok', '', '', simpleModule_test, 'SetUpModuleTest.test_blank'))
+
+            expected.append(('notifyTestRunFinished',))
+            expected.sort()
+            notifications.sort()
+            self.assertEqual(
+                expected,
+                notifications
+            )
+        finally:
+            pydevd_io.EndRedirect()
+        b = buf.getvalue()
+        if not IS_JYTHON:
+            self.assert_(b.find('Ran 4 tests in ') != -1, 'Found: ' + b)
+        else:
+            self.assert_(b.find('Ran 6 tests in ') != -1, 'Found: ' + b)
+
+
+if __name__ == "__main__":
+    #this is so that we can run it frem the jython tests -- because we don't actually have an __main__ module
+    #(so, it won't try importing the __main__ module)
+    unittest.TextTestRunner().run(unittest.makeSuite(RunfilesTest))
diff --git a/python/ide/src/com/jetbrains/python/PythonSdkChooserCombo.java b/python/ide/src/com/jetbrains/python/PythonSdkChooserCombo.java
index 3010b74..69a98de 100644
--- a/python/ide/src/com/jetbrains/python/PythonSdkChooserCombo.java
+++ b/python/ide/src/com/jetbrains/python/PythonSdkChooserCombo.java
@@ -64,8 +64,15 @@
     comboBox.addActionListener(new ActionListener() {
       public void actionPerformed(ActionEvent e) {
         notifyChanged(e);
+        updateTooltip();
       }
     });
+    updateTooltip();
+  }
+
+  private void updateTooltip() {
+    final Object item = getComboBox().getSelectedItem();
+    getComboBox().setToolTipText(item instanceof Sdk ? ((Sdk)item).getHomePath() : null);
   }
 
   private void showOptions(final Project project) {
diff --git a/python/ide/src/com/jetbrains/python/newProject/actions/AbstractProjectSettingsStep.java b/python/ide/src/com/jetbrains/python/newProject/actions/AbstractProjectSettingsStep.java
index 6ba283c..dd68cdb 100644
--- a/python/ide/src/com/jetbrains/python/newProject/actions/AbstractProjectSettingsStep.java
+++ b/python/ide/src/com/jetbrains/python/newProject/actions/AbstractProjectSettingsStep.java
@@ -205,7 +205,19 @@
     final Project project = ProjectManager.getInstance().getDefaultProject();
     final List<Sdk> sdks = PyConfigurableInterpreterList.getInstance(project).getAllPythonSdks();
     VirtualEnvProjectFilter.removeAllAssociated(sdks);
-    final Sdk preferred = sdks.isEmpty() ? null : sdks.iterator().next();
+    Sdk compatibleSdk = sdks.isEmpty() ? null : sdks.iterator().next();
+    DirectoryProjectGenerator generator = getProjectGenerator();
+    if (generator instanceof PyFrameworkProjectGenerator && !((PyFrameworkProjectGenerator)generator).supportsPython3()) {
+      if (compatibleSdk != null && PythonSdkType.getLanguageLevelForSdk(compatibleSdk).isPy3K()) {
+        Sdk python2Sdk = PythonSdkType.findPython2Sdk(sdks);
+        if (python2Sdk != null) {
+          compatibleSdk = python2Sdk;
+
+        }
+      }
+    }
+
+    final Sdk preferred = compatibleSdk;
     mySdkCombo = new PythonSdkChooserCombo(project, sdks, new Condition<Sdk>() {
       @Override
       public boolean value(Sdk sdk) {
@@ -231,6 +243,8 @@
 
   @Nullable
   protected JPanel extendBasePanel() {
+    if (myProjectGenerator instanceof PythonProjectGenerator)
+      return ((PythonProjectGenerator)myProjectGenerator).extendBasePanel();
     return null;
   }
 
@@ -354,17 +368,19 @@
   }
 
   public void selectCompatiblePython() {
-    DirectoryProjectGenerator generator = getProjectGenerator();
-    if (generator instanceof PyFrameworkProjectGenerator && !((PyFrameworkProjectGenerator)generator).supportsPython3()) {
-      Sdk sdk = getSdk();
-      if (sdk != null && PythonSdkType.getLanguageLevelForSdk(sdk).isPy3K()) {
-        Sdk python2Sdk = PythonSdkType.findPython2Sdk(null);
-        if (python2Sdk != null) {
-          mySdkCombo.getComboBox().setSelectedItem(python2Sdk);
-          mySdkCombo.getComboBox().repaint();
-        }
-      }
-    }
+    //DirectoryProjectGenerator generator = getProjectGenerator();
+    //if (generator instanceof PyFrameworkProjectGenerator && !((PyFrameworkProjectGenerator)generator).supportsPython3()) {
+    //  Sdk sdk = getSdk();
+    //  if (sdk != null && PythonSdkType.getLanguageLevelForSdk(sdk).isPy3K()) {
+    //    Sdk python2Sdk = PythonSdkType.findPython2Sdk(null);
+    //    if (python2Sdk != null) {
+    //      mySdkCombo.getComboBox().setSelectedItem(python2Sdk);
+    //      mySdkCombo.getComboBox().revalidate();
+    //      mySdkCombo.getComboBox().repaint();
+    //
+    //    }
+    //  }
+    //}
   }
 
   private static boolean acceptsRemoteSdk(DirectoryProjectGenerator generator) {
diff --git a/python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificAction.java b/python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificAction.java
index 950e11e..4397a09 100644
--- a/python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificAction.java
+++ b/python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificAction.java
@@ -42,6 +42,5 @@
   @Override
   public void actionPerformed(AnActionEvent e) {
     super.actionPerformed(e);
-    mySettings.selectCompatiblePython();
   }
 }
diff --git a/python/openapi/src/com/jetbrains/python/newProject/PythonProjectGenerator.java b/python/openapi/src/com/jetbrains/python/newProject/PythonProjectGenerator.java
index 1432e46..3216bfc 100644
--- a/python/openapi/src/com/jetbrains/python/newProject/PythonProjectGenerator.java
+++ b/python/openapi/src/com/jetbrains/python/newProject/PythonProjectGenerator.java
@@ -19,6 +19,11 @@
     return null;
   }
 
+  @Nullable
+  public JPanel extendBasePanel() throws ProcessCanceledException {
+    return null;
+  }
+
   public Object getProjectSettings() {
     return new PyNewProjectSettings();
   }
diff --git a/python/openapi/src/com/jetbrains/python/templateLanguages/TemplateLanguagePanel.java b/python/openapi/src/com/jetbrains/python/templateLanguages/TemplateLanguagePanel.java
index d2c453e..6196b54 100644
--- a/python/openapi/src/com/jetbrains/python/templateLanguages/TemplateLanguagePanel.java
+++ b/python/openapi/src/com/jetbrains/python/templateLanguages/TemplateLanguagePanel.java
@@ -10,6 +10,9 @@
 import java.awt.*;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.io.File;
 import java.util.List;
 
 public class TemplateLanguagePanel extends JPanel {
@@ -17,7 +20,7 @@
   private JPanel myMainPanel;
   private JLabel myTemplatesFolderLabel;
   private JComboBox myTemplateLanguage;
-
+  private boolean myTemplateFolderModified = false;
   private static final String DEFAULT_TEMPLATES_FOLDER = "templates";
 
   public TemplateLanguagePanel() {
@@ -33,6 +36,15 @@
         myTemplateLanguage.addItem(configuration);
     }
     myTemplatesFolder.setText(DEFAULT_TEMPLATES_FOLDER);
+    myTemplatesFolder.addKeyListener(new KeyAdapter() {
+      @Override
+      public void keyTyped(KeyEvent e) {
+        final int dot = myTemplatesFolder.getCaret().getDot();
+        final int index = myTemplatesFolder.getText().indexOf(File.separator);
+        if (index >= dot)
+          myTemplateFolderModified = true;
+      }
+    });
   }
 
   public String getTemplatesFolder() {
@@ -58,6 +70,19 @@
     myTemplatesFolder.setText(folder);
   }
 
+  public void locationChanged(@NotNull final String baseLocation) {
+    final String templatesFolder = myTemplatesFolder.getText();
+    final int index = templatesFolder.indexOf(File.separator);
+    final String templateFolderName = index >= 0 ? templatesFolder.substring(index) : File.separator + "templates";
+    final String oldBase = index >= 0 ? templatesFolder.substring(0, index) : "";
+    if (oldBase.equals(baseLocation)) {
+      myTemplateFolderModified = false;
+    }
+    if (!myTemplateFolderModified) {
+      myTemplatesFolder.setText(baseLocation + templateFolderName);
+    }
+  }
+
   public Dimension getLabelSize() {
     return new JBLabel("Template language:").getPreferredSize();
   }
diff --git a/python/pluginResources/META-INF/plugin.xml b/python/pluginResources/META-INF/plugin.xml
index 31f714c..4539eda 100644
--- a/python/pluginResources/META-INF/plugin.xml
+++ b/python/pluginResources/META-INF/plugin.xml
@@ -22,4 +22,11 @@
   <vendor url="http://www.jetbrains.com/pycharm/" logo="/com/jetbrains/python/python.png">JetBrains</vendor>
   <xi:include href="/META-INF/python-core.xml" xpointer="xpointer(/idea-plugin/*)"/>
   <xi:include href="/META-INF/python-plugin-core.xml" xpointer="xpointer(/idea-plugin/*)"/>
+
+  <application-components>
+    <component>
+      <interface-class>com.jetbrains.python.console.PythonConsoleRunnerFactory</interface-class>
+      <implementation-class>com.jetbrains.python.console.PydevConsoleRunnerFactory</implementation-class>
+    </component>
+  </application-components>
 </idea-plugin>
diff --git a/python/pluginSrc/META-INF/python-plugin-core.xml b/python/pluginSrc/META-INF/python-plugin-core.xml
index f3deb12..d797dc8 100644
--- a/python/pluginSrc/META-INF/python-plugin-core.xml
+++ b/python/pluginSrc/META-INF/python-plugin-core.xml
@@ -39,6 +39,13 @@
     <action id="PyManagePackages" class="com.jetbrains.python.packaging.PyManagePackagesAction" text="Manage Python Packages...">
       <add-to-group group-id="ToolsMenu" anchor="last"/>
     </action>
+
+    <!-- Console -->
+    <action id="com.jetbrains.python.run.RunPythonConsoleAction"
+            class="com.jetbrains.python.run.RunPythonConsoleAction"
+            text="Run Python Console..." description="Allows to quickly run Python console">
+      <add-to-group group-id="ToolsMenu" anchor="last"/>
+    </action>
   </actions>
 
 </idea-plugin>
\ No newline at end of file
diff --git a/python/pluginSrc/com/jetbrains/python/run/RunPythonConsoleAction.java b/python/pluginSrc/com/jetbrains/python/run/RunPythonConsoleAction.java
new file mode 100644
index 0000000..6b9e631
--- /dev/null
+++ b/python/pluginSrc/com/jetbrains/python/run/RunPythonConsoleAction.java
@@ -0,0 +1,58 @@
+/*
+ * 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.run;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.util.Pair;
+import com.jetbrains.python.console.PydevConsoleRunner;
+import com.jetbrains.python.console.PythonConsoleRunnerFactory;
+import icons.PythonIcons;
+
+/**
+ * @author oleg
+ */
+public class RunPythonConsoleAction extends AnAction implements DumbAware {
+
+  public RunPythonConsoleAction() {
+    super();
+    getTemplatePresentation().setIcon(PythonIcons.Python.PythonConsole);
+  }
+
+  @Override
+  public void update(final AnActionEvent e) {
+    e.getPresentation().setVisible(true);
+    e.getPresentation().setEnabled(false);
+    final Project project = e.getData(CommonDataKeys.PROJECT);
+    if (project != null) {
+      Pair<Sdk, Module> sdkAndModule = PydevConsoleRunner.findPythonSdkAndModule(project, e.getData(LangDataKeys.MODULE));
+      if (sdkAndModule.first != null) {
+        e.getPresentation().setEnabled(true);
+      }
+    }
+  }
+
+  public void actionPerformed(final AnActionEvent e) {
+    PydevConsoleRunner runner = PythonConsoleRunnerFactory.getInstance().createConsoleRunner(e.getData(CommonDataKeys.PROJECT), e.getData(LangDataKeys.MODULE));
+    runner.run();
+  }
+}
diff --git a/python/psi-api/src/com/jetbrains/python/PyNames.java b/python/psi-api/src/com/jetbrains/python/PyNames.java
index 94e35f7..bd927c5 100644
--- a/python/psi-api/src/com/jetbrains/python/PyNames.java
+++ b/python/psi-api/src/com/jetbrains/python/PyNames.java
@@ -60,6 +60,7 @@
   public static final String FAKE_OLD_BASE = "___Classobj";
   public static final String FAKE_GENERATOR = "__generator";
   public static final String FAKE_FUNCTION = "__function";
+  public static final String FAKE_METHOD = "__method";
   public static final String FAKE_NAMEDTUPLE = "__namedtuple";
 
   public static final String FUTURE_MODULE = "__future__";
@@ -471,4 +472,33 @@
   public static boolean isRightOperatorName(@Nullable String name) {
     return name != null && name.matches("__r[a-z]+__");
   }
+
+  /**
+   * Available in Python 3 and Python 2 starting from 2.6.
+   * <p/>
+   * Attributes {@code __doc__}, {@code __dict__} and {@code __module__} should be inherited from object.
+   */
+  public static final ImmutableSet<String> FUNCTION_SPECIAL_ATTRIBUTES = ImmutableSet.of(
+    "__defaults__",
+    "__globals__",
+    "__closure__",
+    "__code__",
+    "__name__"
+  );
+
+  public static final ImmutableSet<String> LEGACY_FUNCTION_SPECIAL_ATTRIBUTES = ImmutableSet.of(
+    "func_defaults",
+    "func_globals",
+    "func_closure",
+    "func_code",
+    "func_name",
+    "func_doc",
+    "func_dict"
+  );
+
+  public static final ImmutableSet<String> PY3_ONLY_FUNCTION_SPECIAL_ATTRIBUTES = ImmutableSet.of("__annotations__", "__kwdefaults__");
+
+  public static final ImmutableSet<String> METHOD_SPECIAL_ATTRIBUTES = ImmutableSet.of("__func__", "__self__");
+
+  public static final ImmutableSet<String> LEGACY_METHOD_SPECIAL_ATTRIBUTES = ImmutableSet.of("im_func", "im_self", "im_class");
 }
diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyElementGenerator.java b/python/psi-api/src/com/jetbrains/python/psi/PyElementGenerator.java
index daa8b30..539d3ab 100644
--- a/python/psi-api/src/com/jetbrains/python/psi/PyElementGenerator.java
+++ b/python/psi-api/src/com/jetbrains/python/psi/PyElementGenerator.java
@@ -90,8 +90,6 @@
   @NotNull
   public abstract PyCallExpression createCallExpression(final LanguageLevel langLevel, String functionName);
 
-  public abstract PyImportStatement createImportStatementFromText(final LanguageLevel languageLevel, String text);
-
   public abstract PyImportElement createImportElement(final LanguageLevel languageLevel, String name);
 
   public abstract PyFunction createProperty(final LanguageLevel languageLevel,
@@ -139,4 +137,32 @@
    */
   @NotNull
   public abstract PsiElement createNewLine();
+
+  /**
+   * Creates import statement of form {@code from qualifier import name as alias}.
+   *
+   * @param languageLevel language level for created element
+   * @param qualifier     from where {@code name} will be imported (module name)
+   * @param name          text of the reference in import element
+   * @param alias         optional alias for {@code as alias} part
+   * @return created {@link com.jetbrains.python.psi.PyFromImportStatement}
+   */
+  @NotNull
+  public abstract PyFromImportStatement createFromImportStatement(@NotNull LanguageLevel languageLevel,
+                                                                  @NotNull String qualifier,
+                                                                  @NotNull String name,
+                                                                  @Nullable String alias);
+
+  /**
+   * Creates import statement of form {@code import name as alias}.
+   *
+   * @param languageLevel language level for created element
+   * @param name          text of the reference in import element (module name)
+   * @param alias         optional alias for {@code as alias} part
+   * @return created {@link com.jetbrains.python.psi.PyImportStatement}
+   */
+  @NotNull
+  public abstract PyImportStatement createImportStatement(@NotNull LanguageLevel languageLevel,
+                                                          @NotNull String name,
+                                                          @Nullable String alias);
 }
diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyStatementPart.java b/python/psi-api/src/com/jetbrains/python/psi/PyStatementPart.java
index a14a91e..90ef99c 100644
--- a/python/psi-api/src/com/jetbrains/python/psi/PyStatementPart.java
+++ b/python/psi-api/src/com/jetbrains/python/psi/PyStatementPart.java
@@ -15,8 +15,6 @@
  */
 package com.jetbrains.python.psi;
 
-import org.jetbrains.annotations.NotNull;
-
 /**
  * Abstract part of a multipart statement.
  * User: dcheryasov
@@ -24,10 +22,4 @@
  */
 public interface PyStatementPart extends PyElement, PyStatementListContainer {
   PyStatementPart[] EMPTY_ARRAY = new PyStatementPart[0];
-
-  /**
-   * @return the body of the part.
-   */
-  @NotNull
-  PyStatementList getStatementList();
 }
diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java
index 9e347e0..5f962b2 100644
--- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java
+++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java
@@ -5,9 +5,11 @@
  */
 package com.jetbrains.python.debugger.pydev;
 
+import com.google.common.collect.Maps;
 import com.intellij.execution.ui.ConsoleViewContentType;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.SystemInfo;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.util.TimeoutUtil;
@@ -54,6 +56,8 @@
   private final Map<Integer, ProtocolFrame> myResponseQueue = new HashMap<Integer, ProtocolFrame>();
   private final TempVarsHolder myTempVars = new TempVarsHolder();
 
+  private Map<Pair<String, Integer>, String> myTempBreakpoints = Maps.newHashMap();
+
 
   private final List<RemoteDebuggerCloseListener> myCloseListeners = ContainerUtil.createLockFreeCopyOnWriteList();
   private DebuggerReader myDebuggerReader;
@@ -383,12 +387,18 @@
     final SetBreakpointCommand command =
       new SetBreakpointCommand(this, type, file, line);
     execute(command);  // set temp. breakpoint
+    myTempBreakpoints.put(Pair.create(file, line), type);
   }
 
   @Override
   public void removeTempBreakpoint(String file, int line) {
-    final RemoveBreakpointCommand command = new RemoveBreakpointCommand(this, "all", file, line);
-    execute(command);  // remove temp. breakpoint
+    String type = myTempBreakpoints.get(Pair.create(file, line));
+    if (type != null) {
+      final RemoveBreakpointCommand command = new RemoveBreakpointCommand(this, type, file, line);
+      execute(command);  // remove temp. breakpoint
+    } else {
+      LOG.error("Temp breakpoint not found for " + file + ":" + line);
+    }
   }
 
   @Override
diff --git a/python/resources/icons/com/jetbrains/python/pythonConsole.png b/python/resources/icons/com/jetbrains/python/pythonConsole.png
new file mode 100644
index 0000000..74231f2
--- /dev/null
+++ b/python/resources/icons/com/jetbrains/python/pythonConsole.png
Binary files differ
diff --git a/python/resources/icons/com/jetbrains/python/pythonConsole@2x.png b/python/resources/icons/com/jetbrains/python/pythonConsole@2x.png
new file mode 100644
index 0000000..77922c7
--- /dev/null
+++ b/python/resources/icons/com/jetbrains/python/pythonConsole@2x.png
Binary files differ
diff --git a/python/resources/icons/com/jetbrains/python/pythonConsole@2x_dark.png b/python/resources/icons/com/jetbrains/python/pythonConsole@2x_dark.png
new file mode 100644
index 0000000..24167e3
--- /dev/null
+++ b/python/resources/icons/com/jetbrains/python/pythonConsole@2x_dark.png
Binary files differ
diff --git a/python/resources/icons/com/jetbrains/python/pythonConsoleToolWindow.png b/python/resources/icons/com/jetbrains/python/pythonConsoleToolWindow.png
new file mode 100644
index 0000000..dc6ee5f
--- /dev/null
+++ b/python/resources/icons/com/jetbrains/python/pythonConsoleToolWindow.png
Binary files differ
diff --git a/python/resources/icons/com/jetbrains/python/pythonConsoleToolWindow@2x.png b/python/resources/icons/com/jetbrains/python/pythonConsoleToolWindow@2x.png
new file mode 100644
index 0000000..5355d7b
--- /dev/null
+++ b/python/resources/icons/com/jetbrains/python/pythonConsoleToolWindow@2x.png
Binary files differ
diff --git a/python/resources/icons/com/jetbrains/python/pythonConsoleToolWindow@2x_dark.png b/python/resources/icons/com/jetbrains/python/pythonConsoleToolWindow@2x_dark.png
new file mode 100644
index 0000000..a034e00
--- /dev/null
+++ b/python/resources/icons/com/jetbrains/python/pythonConsoleToolWindow@2x_dark.png
Binary files differ
diff --git a/python/resources/icons/com/jetbrains/python/pythonConsoleToolWindow_dark.png b/python/resources/icons/com/jetbrains/python/pythonConsoleToolWindow_dark.png
new file mode 100644
index 0000000..e6aed97
--- /dev/null
+++ b/python/resources/icons/com/jetbrains/python/pythonConsoleToolWindow_dark.png
Binary files differ
diff --git a/python/resources/icons/com/jetbrains/python/pythonConsole_dark.png b/python/resources/icons/com/jetbrains/python/pythonConsole_dark.png
new file mode 100644
index 0000000..9cdc3ac
--- /dev/null
+++ b/python/resources/icons/com/jetbrains/python/pythonConsole_dark.png
Binary files differ
diff --git a/python/resources/idea/PyCharmCoreApplicationInfo.xml b/python/resources/idea/PyCharmCoreApplicationInfo.xml
index ef906b0..41c0c3e 100644
--- a/python/resources/idea/PyCharmCoreApplicationInfo.xml
+++ b/python/resources/idea/PyCharmCoreApplicationInfo.xml
@@ -1,6 +1,6 @@
 <component>
   <company name="JetBrains s.r.o." url="http://www.jetbrains.com/?fromIDE"/>
-  <version major="3" minor="0" eap="true"/>
+  <version major="4" minor="0" eap="true"/>
   <build number="__BUILD_NUMBER__" date="__BUILD_DATE__"/>
   <logo url="/pycharm_core_logo.png" textcolor="ffffff" progressColor="ffaa16" progressY="230" progressTailIcon="/community_progress_tail.png"/>
   <about url="/pycharm_core_about.png" logoX="300" logoY="265" logoW="75" logoH="30" foreground="ffffff" linkColor="fca11a"/>
diff --git a/python/resources/pycharm_core_logo.png b/python/resources/pycharm_core_logo.png
index 3daabba..9bc912b 100644
--- a/python/resources/pycharm_core_logo.png
+++ b/python/resources/pycharm_core_logo.png
Binary files differ
diff --git a/python/resources/pycharm_core_logo@2x.png b/python/resources/pycharm_core_logo@2x.png
index 83bf65b..0f4595c 100644
--- a/python/resources/pycharm_core_logo@2x.png
+++ b/python/resources/pycharm_core_logo@2x.png
Binary files differ
diff --git a/python/src/META-INF/PyCharmCorePlugin.xml b/python/src/META-INF/PyCharmCorePlugin.xml
index a250bdc..99a39a1 100644
--- a/python/src/META-INF/PyCharmCorePlugin.xml
+++ b/python/src/META-INF/PyCharmCorePlugin.xml
@@ -1,4 +1,4 @@
 <idea-plugin version="2" xmlns:xi="http://www.w3.org/2001/XInclude">
-  <xi:include href="/META-INF/pycharm-core.xml" xpointer="xpointer(/idea-plugin/*)"/>
+  <xi:include href="/META-INF/pycharm-community.xml" xpointer="xpointer(/idea-plugin/*)"/>
   <xi:include href="/META-INF/python-core.xml" xpointer="xpointer(/idea-plugin/*)"/>
 </idea-plugin>
diff --git a/python/src/META-INF/pycharm-community.xml b/python/src/META-INF/pycharm-community.xml
new file mode 100644
index 0000000..6851857
--- /dev/null
+++ b/python/src/META-INF/pycharm-community.xml
@@ -0,0 +1,11 @@
+<idea-plugin version="2" xmlns:xi="http://www.w3.org/2001/XInclude">
+  <!-- Components and extensions declared in this file work ONLY in PyCharm, not in Python plugin. -->
+  <xi:include href="/META-INF/pycharm-core.xml" xpointer="xpointer(/idea-plugin/*)"/>
+
+  <application-components>
+    <component>
+      <interface-class>com.jetbrains.python.console.PythonConsoleRunnerFactory</interface-class>
+      <implementation-class>com.jetbrains.python.console.PythonToolWindowConsoleRunnerFactory</implementation-class>
+    </component>
+  </application-components>
+</idea-plugin>
diff --git a/python/src/META-INF/pycharm-core.xml b/python/src/META-INF/pycharm-core.xml
index 13ba05d..4732ad0 100644
--- a/python/src/META-INF/pycharm-core.xml
+++ b/python/src/META-INF/pycharm-core.xml
@@ -24,6 +24,12 @@
     </component>
   </project-components>
 
+  <project-components>
+    <component>
+      <implementation-class>com.jetbrains.python.console.PythonConsoleToolWindow</implementation-class>
+    </component>
+  </project-components>
+
   <module value="com.intellij.modules.xml"/>
 
   <extensions defaultExtensionNs="com.intellij">
@@ -75,6 +81,10 @@
 
     <renameHandler implementation="com.intellij.platform.renameProject.RenameProjectHandler"/>
     <renameHandler implementation="com.intellij.platform.renameProject.ProjectFolderRenameHandler"/>
+
+    <!-- Console -->
+    <toolWindow id="Python Console" anchor="bottom" icon="PythonIcons.Python.PythonConsoleToolWindow"
+                factoryClass="com.jetbrains.python.console.PythonConsoleToolWindowFactory" secondary="false"/>
   </extensions>
 
   <actions>
diff --git a/python/src/META-INF/python-core.xml b/python/src/META-INF/python-core.xml
index a27eb44..292237a 100644
--- a/python/src/META-INF/python-core.xml
+++ b/python/src/META-INF/python-core.xml
@@ -529,11 +529,6 @@
     <moduleService serviceInterface="com.jetbrains.python.packaging.PyPackageRequirementsSettings"
                    serviceImplementation="com.jetbrains.python.packaging.PyPackageRequirementsSettings"/>
 
-    <!-- Console -->
-    <toolWindow id="Python Console" anchor="bottom" icon=""
-                factoryClass="com.jetbrains.python.console.PythonConsoleToolWindowFactory" secondary="false"/>
-
-
   </extensions>
 
   <extensionPoints>    
@@ -621,12 +616,6 @@
     </component>
   </project-components>
 
-  <project-components>
-    <component>
-      <implementation-class>com.jetbrains.python.console.PythonConsoleToolWindow</implementation-class>
-    </component>
-  </project-components>
-
   <actions>
     <group id="PyTypeHierarchyPopupMenu">
       <reference ref="TypeHierarchyBase.BaseOnThisType"/>
@@ -655,11 +644,7 @@
       <reference ref="CompareFileWithEditor"/>
     </group>
 
-    <action id="com.jetbrains.python.console.RunPythonConsoleAction"
-            class="com.jetbrains.python.console.RunPythonConsoleAction"
-            text="Run Python Console..." description="Allows to quickly run Python console">
-      <add-to-group group-id="ToolsMenu" anchor="last"/>
-    </action>
+
 
     <action id="com.jetbrains.python.console.PyOpenDebugConsoleAction"
             class="com.jetbrains.python.console.PyOpenDebugConsoleAction"
@@ -669,7 +654,7 @@
 
 
     <action id="ExecuteInPyConsoleAction"
-            class="com.jetbrains.python.actions.ExecuteInConsoleAction"
+            class="com.jetbrains.python.actions.PyExecuteSelectionAction"
             text="Execute selection in console"
             description="Executes selected code fragment in Python/Django console">
       <add-to-group group-id="EditorPopupMenu" anchor="before" relative-to-action="CompareClipboardWithSelection"/>
diff --git a/python/src/com/jetbrains/python/actions/ExecuteInConsoleAction.java b/python/src/com/jetbrains/python/actions/PyExecuteSelectionAction.java
similarity index 83%
rename from python/src/com/jetbrains/python/actions/ExecuteInConsoleAction.java
rename to python/src/com/jetbrains/python/actions/PyExecuteSelectionAction.java
index cb90d49..4aea63a 100644
--- a/python/src/com/jetbrains/python/actions/ExecuteInConsoleAction.java
+++ b/python/src/com/jetbrains/python/actions/PyExecuteSelectionAction.java
@@ -32,18 +32,20 @@
 import com.intellij.util.NotNullFunction;
 import com.jetbrains.python.console.PyCodeExecutor;
 import com.jetbrains.python.console.PydevConsoleRunner;
-import com.jetbrains.python.console.RunPythonConsoleAction;
+import com.jetbrains.python.console.PythonConsoleRunnerFactory;
+import com.jetbrains.python.console.PythonConsoleToolWindow;
 import com.jetbrains.python.psi.PyFile;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.util.Collection;
+import java.util.List;
 
-public class ExecuteInConsoleAction extends AnAction {
+public class PyExecuteSelectionAction extends AnAction {
 
   public static final String EXECUTE_SELECTION_IN_CONSOLE = "Execute Selection in Console";
 
-  public ExecuteInConsoleAction() {
+  public PyExecuteSelectionAction() {
     super(EXECUTE_SELECTION_IN_CONSOLE);
   }
 
@@ -186,6 +188,12 @@
   }
 
   private static Collection<RunContentDescriptor> getConsoles(Project project) {
+    PythonConsoleToolWindow toolWindow = PythonConsoleToolWindow.getInstance(project);
+
+    if (toolWindow != null) {
+      return toolWindow.getConsoleContentDescriptors();
+    }
+
     return ExecutionHelper.findRunningConsole(project, new NotNullFunction<RunContentDescriptor, Boolean>() {
       @NotNull
       @Override
@@ -214,15 +222,34 @@
   private static void startConsole(final Project project,
                                    final Consumer<PyCodeExecutor> consumer,
                                    Module context) {
-    PydevConsoleRunner runner = RunPythonConsoleAction.runPythonConsole(project, context, null);
-    runner.addConsoleListener(new PydevConsoleRunner.ConsoleListener() {
-      @Override
-      public void handleConsoleInitialized(LanguageConsoleView consoleView) {
-        if (consoleView instanceof PyCodeExecutor) {
-          consumer.consume((PyCodeExecutor)consoleView);
+    final PythonConsoleToolWindow toolWindow = PythonConsoleToolWindow.getInstance(project);
+
+    if (toolWindow != null) {
+      toolWindow.activate(new Runnable() {
+        @Override
+        public void run() {
+          List<RunContentDescriptor> descs = toolWindow.getConsoleContentDescriptors();
+
+          RunContentDescriptor descriptor = descs.get(0);
+          if (descriptor != null && descriptor.getExecutionConsole() instanceof PyCodeExecutor) {
+            consumer.consume((PyCodeExecutor)descriptor.getExecutionConsole());
+          }
         }
-      }
-    });
+      });
+    }
+    else {
+      PythonConsoleRunnerFactory consoleRunnerFactory = PythonConsoleRunnerFactory.getInstance();
+      PydevConsoleRunner runner = consoleRunnerFactory.createConsoleRunner(project, null);
+      runner.addConsoleListener(new PydevConsoleRunner.ConsoleListener() {
+        @Override
+        public void handleConsoleInitialized(LanguageConsoleView consoleView) {
+          if (consoleView instanceof PyCodeExecutor) {
+            consumer.consume((PyCodeExecutor)consoleView);
+          }
+        }
+      });
+      runner.run();
+    }
   }
 
   private static boolean canFindConsole(AnActionEvent e) {
diff --git a/python/src/com/jetbrains/python/buildout/BuildoutConfigPanel.java b/python/src/com/jetbrains/python/buildout/BuildoutConfigPanel.java
index 394de61..eceedeb 100644
--- a/python/src/com/jetbrains/python/buildout/BuildoutConfigPanel.java
+++ b/python/src/com/jetbrains/python/buildout/BuildoutConfigPanel.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.
@@ -81,6 +81,7 @@
     myErrorPanel.add(facetErrorPanel.getComponent(), BorderLayout.CENTER);
 
     facetErrorPanel.getValidatorsManager().registerValidator(new FacetEditorValidator() {
+      @NotNull
       @Override
       public ValidationResult check() {
         if (!myFacetEnabled) {
diff --git a/python/src/com/jetbrains/python/codeInsight/imports/AddImportHelper.java b/python/src/com/jetbrains/python/codeInsight/imports/AddImportHelper.java
index 1c09ee2..d33e97c 100644
--- a/python/src/com/jetbrains/python/codeInsight/imports/AddImportHelper.java
+++ b/python/src/com/jetbrains/python/codeInsight/imports/AddImportHelper.java
@@ -21,14 +21,14 @@
 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.psi.util.PsiTreeUtil;
+import com.intellij.psi.util.QualifiedName;
 import com.intellij.util.IncorrectOperationException;
 import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
 import com.jetbrains.python.documentation.DocStringUtil;
 import com.jetbrains.python.psi.*;
-import com.intellij.psi.util.QualifiedName;
 import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
 import com.jetbrains.python.sdk.PythonSdkType;
 import org.jetbrains.annotations.NotNull;
@@ -36,6 +36,8 @@
 
 import java.util.List;
 
+import static com.jetbrains.python.psi.PyUtil.sure;
+
 /**
  * Does the actual job of adding an import statement into a file.
  * User: dcheryasov
@@ -47,6 +49,34 @@
   private AddImportHelper() {
   }
 
+  public static void addLocalImportStatement(@NotNull PyElement element, @NotNull String name) {
+    final PyElementGenerator generator = PyElementGenerator.getInstance(element.getProject());
+    final LanguageLevel languageLevel = LanguageLevel.forElement(element);
+
+    final PsiElement anchor = getLocalInsertPosition(element);
+    final PsiElement parentElement = sure(anchor).getParent();
+    if (parentElement != null) {
+      parentElement.addBefore(generator.createImportStatement(languageLevel, name, null), anchor);
+    }
+  }
+
+  public static void addLocalFromImportStatement(@NotNull PyElement element, @NotNull String qualifier, @NotNull String name) {
+    final PyElementGenerator generator = PyElementGenerator.getInstance(element.getProject());
+    final LanguageLevel languageLevel = LanguageLevel.forElement(element);
+
+    final PsiElement anchor = getLocalInsertPosition(element);
+    final PsiElement parentElement = sure(anchor).getParent();
+    if (parentElement != null) {
+      parentElement.addBefore(generator.createFromImportStatement(languageLevel, qualifier, name, null), anchor);
+    }
+
+  }
+
+  @Nullable
+  public static PsiElement getLocalInsertPosition(@NotNull PyElement anchor) {
+    return PsiTreeUtil.getParentOfType(anchor, PyStatement.class, false);
+  }
+
   public enum ImportPriority {
     BUILTIN, THIRD_PARTY, PROJECT
   }
@@ -81,7 +111,8 @@
       // maybe we arrived at the doc comment stmt; skip over it, too
       else if (!skippedOverImports && !skippedOverDoc && file instanceof PyFile) {
         PsiElement doc_elt =
-          DocStringUtil.findDocStringExpression((PyElement)file); // this gives the literal; its parent is the expr seeker may have encountered
+          DocStringUtil
+            .findDocStringExpression((PyElement)file); // this gives the literal; its parent is the expr seeker may have encountered
         if (doc_elt != null && doc_elt.getParent() == feeler) {
           feeler = feeler.getNextSibling();
           seeker = feeler; // skip over doc even if there's nothing below it
@@ -147,19 +178,13 @@
    * @param file   where to operate
    * @param name   which to import (qualified is OK)
    * @param asName optional name for 'as' clause
+   * @return whether import statement was actually added
    */
   public static boolean addImportStatement(PsiFile file, String name, @Nullable String asName, ImportPriority priority) {
-    String as_clause;
-    if (asName == null) {
-      as_clause = "";
-    }
-    else {
-      as_clause = " as " + asName;
-    }
     if (!(file instanceof PyFile)) {
       return false;
     }
-    List<PyImportElement> existingImports = ((PyFile)file).getImportTargets();
+    final List<PyImportElement> existingImports = ((PyFile)file).getImportTargets();
     for (PyImportElement element : existingImports) {
       final QualifiedName qName = element.getImportedQName();
       if (qName != null && name.equals(qName.toString())) {
@@ -171,7 +196,7 @@
 
     final PyElementGenerator generator = PyElementGenerator.getInstance(file.getProject());
     final LanguageLevel languageLevel = LanguageLevel.forElement(file);
-    final PyImportStatement importNodeToInsert = generator.createImportStatementFromText(languageLevel, "import " + name + as_clause);
+    final PyImportStatement importNodeToInsert = generator.createImportStatement(languageLevel, name, asName);
     try {
       file.addBefore(importNodeToInsert, getInsertPosition(file, name, priority));
     }
@@ -180,6 +205,7 @@
     }
     return true;
   }
+
   /**
    * Adds an "import ... from ..." statement below other top-level imports.
    *
@@ -189,20 +215,20 @@
    * @param asName optional name for 'as' clause
    */
   public static void addImportFromStatement(PsiFile file, String from, String name, @Nullable String asName, ImportPriority priority) {
-    String asClause = asName == null ? "" : " as " + asName;
-
-    final PyFromImportStatement importNodeToInsert = PyElementGenerator.getInstance(file.getProject()).createFromText(
-      LanguageLevel.forElement(file), PyFromImportStatement.class, "from " + from + " import " + name + asClause);
+    final PyElementGenerator generator = PyElementGenerator.getInstance(file.getProject());
+    final LanguageLevel languageLevel = LanguageLevel.forElement(file);
+    final PyFromImportStatement nodeToInsert = generator.createFromImportStatement(languageLevel, from, name, asName);
     try {
       if (InjectedLanguageManager.getInstance(file.getProject()).isInjectedFragment(file)) {
-        final PsiElement element = file.addBefore(importNodeToInsert, getInsertPosition(file, from, priority));
+        final PsiElement element = file.addBefore(nodeToInsert, getInsertPosition(file, from, priority));
         PsiElement whitespace = element.getNextSibling();
-        if (!(whitespace instanceof PsiWhiteSpace))
+        if (!(whitespace instanceof PsiWhiteSpace)) {
           whitespace = PsiParserFacade.SERVICE.getInstance(file.getProject()).createWhiteSpaceFromText("  >>> ");
+        }
         file.addBefore(whitespace, element);
       }
       else {
-        file.addBefore(importNodeToInsert, getInsertPosition(file, from, priority));
+        file.addBefore(nodeToInsert, getInsertPosition(file, from, priority));
       }
     }
     catch (IncorrectOperationException e) {
@@ -228,7 +254,7 @@
           }
         }
         final PyElementGenerator generator = PyElementGenerator.getInstance(file.getProject());
-        PyImportElement importElement = generator.createImportElement(LanguageLevel.forElement(file), name);
+        final PyImportElement importElement = generator.createImportElement(LanguageLevel.forElement(file), name);
         existingImport.add(importElement);
         return true;
       }
@@ -239,7 +265,8 @@
 
   public static void addImport(final PsiNamedElement target, final PsiFile file, final PyElement element) {
     final boolean useQualified = !PyCodeInsightSettings.getInstance().PREFER_FROM_IMPORT;
-    final PsiFileSystemItem toImport = target instanceof PsiFileSystemItem ? ((PsiFileSystemItem)target).getParent() : target.getContainingFile();
+    final PsiFileSystemItem toImport =
+      target instanceof PsiFileSystemItem ? ((PsiFileSystemItem)target).getParent() : target.getContainingFile();
     final ImportPriority priority = getImportPriority(file, toImport);
     final QualifiedName qName = QualifiedNameFinder.findCanonicalImportPath(target, element);
     if (qName == null) return;
diff --git a/python/src/com/jetbrains/python/codeInsight/imports/AutoImportQuickFix.java b/python/src/com/jetbrains/python/codeInsight/imports/AutoImportQuickFix.java
index 47c9ca9..ce3c079 100644
--- a/python/src/com/jetbrains/python/codeInsight/imports/AutoImportQuickFix.java
+++ b/python/src/com/jetbrains/python/codeInsight/imports/AutoImportQuickFix.java
@@ -29,6 +29,7 @@
 import com.intellij.psi.PsiFile;
 import com.intellij.psi.PsiFileSystemItem;
 import com.intellij.psi.PsiReference;
+import com.intellij.psi.util.QualifiedName;
 import com.intellij.util.IncorrectOperationException;
 import com.jetbrains.python.PyBundle;
 import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
@@ -36,7 +37,6 @@
 import com.jetbrains.python.psi.PyFunction;
 import com.jetbrains.python.psi.PyImportElement;
 import com.jetbrains.python.psi.PyQualifiedExpression;
-import com.intellij.psi.util.QualifiedName;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -138,7 +138,7 @@
       myImports.size() > 1,
       ImportCandidateHolder.getQualifiedName(name, myImports.get(0).getPath(), myImports.get(0).getImportElement())
     );
-    final ImportFromExistingAction action = new ImportFromExistingAction(myNode, myImports, name, myUseQualifiedImport);
+    final ImportFromExistingAction action = new ImportFromExistingAction(myNode, myImports, name, myUseQualifiedImport, false);
     action.onDone(new Runnable() {
       public void run() {
         myExpended = true;
@@ -166,11 +166,16 @@
     if (!FileModificationService.getInstance().prepareFileForWrite(file)) return;
     if (ImportFromExistingAction.isResolved(myReference)) return;
     // act
-    ImportFromExistingAction action = new ImportFromExistingAction(myNode, myImports, getNameToImport(), myUseQualifiedImport);
+    ImportFromExistingAction action = createAction();
     action.execute(); // assume that action runs in WriteAction on its own behalf
     myExpended = true;
   }
 
+  @NotNull
+  protected ImportFromExistingAction createAction() {
+    return new ImportFromExistingAction(myNode, myImports, getNameToImport(), myUseQualifiedImport, false);
+  }
+
   public void sortCandidates() {
     Collections.sort(myImports);
   }
@@ -203,4 +208,27 @@
     }
     return false;
   }
+
+  @NotNull
+  public AutoImportQuickFix forLocalImport() {
+    return new AutoImportQuickFix(myNode, myReference, myUseQualifiedImport) {
+      @NotNull
+      @Override
+      public String getName() {
+        return super.getName() + " locally";
+      }
+
+      @NotNull
+      @Override
+      public String getFamilyName() {
+        return "import locally";
+      }
+
+      @NotNull
+      @Override
+      protected ImportFromExistingAction createAction() {
+        return new ImportFromExistingAction(myNode, myImports, getNameToImport(), myUseQualifiedImport, true);
+      }
+    };
+  }
 }
diff --git a/python/src/com/jetbrains/python/codeInsight/imports/ImportCandidateHolder.java b/python/src/com/jetbrains/python/codeInsight/imports/ImportCandidateHolder.java
index b60348d..e75df5b 100644
--- a/python/src/com/jetbrains/python/codeInsight/imports/ImportCandidateHolder.java
+++ b/python/src/com/jetbrains/python/codeInsight/imports/ImportCandidateHolder.java
@@ -24,18 +24,25 @@
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiFile;
 import com.intellij.psi.PsiFileSystemItem;
-import com.jetbrains.python.psi.*;
 import com.intellij.psi.util.QualifiedName;
+import com.jetbrains.python.psi.*;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 /**
  * An immutable holder of information for one auto-import candidate.
- * User: dcheryasov
- * Date: Apr 23, 2009 4:17:50 PM
+ * <p/>
+ * There can be do different flavors of such candidates:
+ * <ul>
+ *   <li>Candidates based on existing imports in module. In this case {@link #getImportElement()} must return not {@code null}.</li>
+ *   <li>Candidates not yet imported. In this case {@link #getPath()} must return not {@code null}.</li>
+ * </ul>
+ * <p/>
+ *
+ * @author dcheryasov
  */
 // visibility is intentionally package-level
-class ImportCandidateHolder implements Comparable {
+class ImportCandidateHolder implements Comparable<ImportCandidateHolder> {
   private final PsiElement myImportable;
   private final PyImportElement myImportElement;
   private final PsiFileSystemItem myFile;
@@ -43,15 +50,19 @@
 
   /**
    * Creates new instance.
-   * @param importable an element that could be imported either from import element or from file.
-   * @param file the file which is the source of the importable
+   *
+   * @param importable    an element that could be imported either from import element or from file.
+   * @param file          the file which is the source of the importable (module for symbols, containing directory for modules and packages)
    * @param importElement an existing import element that can be a source for the importable.
-   * @param path import path for the file, as a qualified name (a.b.c)
+   * @param path          import path for the file, as a qualified name (a.b.c)
+   *                      For top-level imported symbols it's <em>qualified name of containing module</em> (or package for __init__.py).
+   *                      For modules and packages it should be <em>qualified name of their parental package</em>
+   *                      (empty for modules and packages located at source roots).
+   *
+   * @see com.jetbrains.python.codeInsight.imports.PythonReferenceImporter#proposeImportFix
    */
-  public ImportCandidateHolder(
-    @NotNull PsiElement importable, @NotNull PsiFileSystemItem file,
-    @Nullable PyImportElement importElement, @Nullable QualifiedName path
-  ) {
+  public ImportCandidateHolder(@NotNull PsiElement importable, @NotNull PsiFileSystemItem file,
+                               @Nullable PyImportElement importElement, @Nullable QualifiedName path) {
     myFile = file;
     myImportable = importable;
     myImportElement = importElement;
@@ -59,18 +70,22 @@
     assert importElement != null || path != null; // one of these must be present
   }
 
+  @NotNull
   public PsiElement getImportable() {
     return myImportable;
   }
 
+  @Nullable
   public PyImportElement getImportElement() {
     return myImportElement;
   }
 
+  @NotNull
   public PsiFileSystemItem getFile() {
     return myFile;
   }
 
+  @Nullable
   public QualifiedName getPath() {
     return myPath;
   }
@@ -78,15 +93,17 @@
   /**
    * Helper method that builds an import path, handling all these "import foo", "import foo as bar", "from bar import foo", etc.
    * Either importPath or importSource must be not null.
-   * @param name what is ultimately imported.
+   *
+   * @param name       what is ultimately imported.
    * @param importPath known path to import the name.
-   * @param source known ImportElement to import the name; its 'as' clause is used if present.
+   * @param source     known ImportElement to import the name; its 'as' clause is used if present.
    * @return a properly qualified name.
    */
-  public static String getQualifiedName(String name, QualifiedName importPath, PyImportElement source) {
-    StringBuilder sb = new StringBuilder();
+  @NotNull
+  public static String getQualifiedName(@NotNull String name, @Nullable QualifiedName importPath, @Nullable PyImportElement source) {
+    final StringBuilder sb = new StringBuilder();
     if (source != null) {
-      PsiElement parent = source.getParent();
+      final PsiElement parent = source.getParent();
       if (parent instanceof PyFromImportStatement) {
         sb.append(name);
       }
@@ -95,7 +112,7 @@
       }
     }
     else {
-      if (importPath.getComponentCount() > 0) {
+      if (importPath != null && importPath.getComponentCount() > 0) {
         sb.append(importPath).append(".");
       }
       sb.append(name);
@@ -103,8 +120,9 @@
     return sb.toString();
   }
 
-  public String getPresentableText(String myName) {
-    StringBuilder sb = new StringBuilder(getQualifiedName(myName, myPath, myImportElement));
+  @NotNull
+  public String getPresentableText(@NotNull String myName) {
+    final StringBuilder sb = new StringBuilder(getQualifiedName(myName, myPath, myImportElement));
     PsiElement parent = null;
     if (myImportElement != null) {
       parent = myImportElement.getParent();
@@ -113,13 +131,15 @@
       sb.append(((PyFunction)myImportable).getParameterList().getPresentableText(false));
     }
     else if (myImportable instanceof PyClass) {
-      PyClass[] supers = ((PyClass)myImportable).getSuperClasses();
+      final PyClass[] supers = ((PyClass)myImportable).getSuperClasses();
       if (supers.length > 0) {
         sb.append("(");
         // ", ".join(x.getName() for x in getSuperClasses())
-        String[] super_names = new String[supers.length];
-        for (int i=0; i < supers.length; i += 1) super_names[i] = supers[i].getName();
-        sb.append(StringUtil.join(super_names, ", "));
+        final String[] superNames = new String[supers.length];
+        for (int i = 0; i < supers.length; i += 1) {
+          superNames[i] = supers[i].getName();
+        }
+        sb.append(StringUtil.join(superNames, ", "));
         sb.append(")");
       }
     }
@@ -135,10 +155,9 @@
     return sb.toString();
   }
 
-  public int compareTo(Object o) {
-    ImportCandidateHolder rhs = (ImportCandidateHolder) o;
-    int lRelevance = getRelevance();
-    int rRelevance = rhs.getRelevance();
+  public int compareTo(@NotNull ImportCandidateHolder rhs) {
+    final int lRelevance = getRelevance();
+    final int rRelevance = rhs.getRelevance();
     if (rRelevance != lRelevance) {
       return rRelevance - lRelevance;
     }
@@ -150,7 +169,7 @@
   }
 
   int getRelevance() {
-    Project project = myImportable.getProject();
+    final Project project = myImportable.getProject();
     final PsiFile psiFile = myImportable.getContainingFile();
     final VirtualFile vFile = psiFile == null ? null : psiFile.getVirtualFile();
     if (vFile == null) return 0;
diff --git a/python/src/com/jetbrains/python/codeInsight/imports/ImportFromExistingAction.java b/python/src/com/jetbrains/python/codeInsight/imports/ImportFromExistingAction.java
index 58254d9..e7a8151 100644
--- a/python/src/com/jetbrains/python/codeInsight/imports/ImportFromExistingAction.java
+++ b/python/src/com/jetbrains/python/codeInsight/imports/ImportFromExistingAction.java
@@ -53,6 +53,7 @@
   String myName;
   boolean myUseQualifiedImport;
   private Runnable myOnDoneCallback;
+  private final boolean myImportLocally;
 
   /**
    * @param target element to become qualified as imported.
@@ -60,12 +61,13 @@
    * @param name relevant name ot the target element (e.g. of identifier in an expression).
    * @param useQualified if True, use qualified "import modulename" instead of "from modulename import ...".
    */
-  public ImportFromExistingAction(@NotNull PyElement target, @NotNull List<ImportCandidateHolder> sources, String name,
-                                  boolean useQualified) {
+  public ImportFromExistingAction(@NotNull PyElement target, @NotNull List<ImportCandidateHolder> sources, @NotNull String name,
+                                  boolean useQualified, boolean importLocally) {
     myTarget = target;
     mySources = sources;
     myName = name;
     myUseQualifiedImport = useQualified;
+    myImportLocally = importLocally;
   }
 
   public void onDone(Runnable callback) {
@@ -151,25 +153,41 @@
     if (manager.isInjectedFragment(file)) {
       file = manager.getTopLevelFile(myTarget);
     }
+    // We are trying to import top-level module or package which thus cannot be qualified
     if (isRoot(item.getFile())) {
-      AddImportHelper.addImportStatement(file, myName, null, priority);
+      if (myImportLocally) {
+        AddImportHelper.addLocalImportStatement(myTarget, myName);
+      } else {
+        AddImportHelper.addImportStatement(file, myName, null, priority);
+      }
     }
     else {
-      String qualifiedName = item.getPath().toString();
+      final String qualifiedName = item.getPath().toString();
       if (myUseQualifiedImport) {
         String nameToImport = qualifiedName;
         if (item.getImportable() instanceof PsiFileSystemItem) {
           nameToImport += "." + myName;
         }
-        AddImportHelper.addImportStatement(file, nameToImport, null, priority);
+        if (myImportLocally) {
+          AddImportHelper.addLocalImportStatement(myTarget, nameToImport);
+        }
+        else {
+          AddImportHelper.addImportStatement(file, nameToImport, null, priority);
+        }
         myTarget.replace(gen.createExpressionFromText(LanguageLevel.forElement(myTarget), qualifiedName + "." + myName));
       }
       else {
-        AddImportHelper.addImportFrom(file, myTarget, qualifiedName, myName, null, priority);
+        if (myImportLocally) {
+          AddImportHelper.addLocalFromImportStatement(myTarget, qualifiedName, myName);
+        }
+        else {
+          AddImportHelper.addImportFromStatement(file, qualifiedName, myName, null, priority);
+        }
       }
     }
   }
 
+
   private void addToExistingImport(PyImportElement src) {
     final PyElementGenerator gen = PyElementGenerator.getInstance(myTarget.getProject());
     // did user choose 'import' or 'from import'?
diff --git a/python/src/com/jetbrains/python/codeInsight/imports/PyImportOptimizer.java b/python/src/com/jetbrains/python/codeInsight/imports/PyImportOptimizer.java
index 5d24e3b..d5fd5a6 100644
--- a/python/src/com/jetbrains/python/codeInsight/imports/PyImportOptimizer.java
+++ b/python/src/com/jetbrains/python/codeInsight/imports/PyImportOptimizer.java
@@ -92,7 +92,7 @@
           for (PyImportElement importElement : importStatement.getImportElements()) {
             myMissorted = true;
             PsiElement toImport = importElement.resolve();
-            final PyImportStatement splitImport = myGenerator.createImportStatementFromText(langLevel, "import " + importElement.getText());
+            final PyImportStatement splitImport = myGenerator.createImportStatement(langLevel, importElement.getText(), null);
             prioritize(splitImport, toImport);
           }
         }
diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java
index 346d40f..c28bc29 100644
--- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java
+++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java
@@ -36,6 +36,8 @@
 import java.util.Map;
 import java.util.Set;
 
+import static com.jetbrains.python.psi.PyUtil.as;
+
 /**
  * @author yole
  */
@@ -132,6 +134,23 @@
           }
         }
       }
+      else if ("__builtin__.tuple.__add__".equals(qname) && callSite instanceof PyBinaryExpression) {
+        final PyBinaryExpression expression = (PyBinaryExpression)callSite;
+        final PyTupleType leftTupleType = as(context.getType(expression.getLeftExpression()), PyTupleType.class);
+        if (expression.getRightExpression() != null) {
+          final PyTupleType rightTupleType = as(context.getType(expression.getRightExpression()), PyTupleType.class);
+          if (leftTupleType != null && rightTupleType != null) {
+            final PyType[] elementTypes = new PyType[leftTupleType.getElementCount() + rightTupleType.getElementCount()];
+            for (int i = 0; i < leftTupleType.getElementCount(); i++) {
+              elementTypes[i] = leftTupleType.getElementType(i);
+            }
+            for (int i = 0; i < rightTupleType.getElementCount(); i++) {
+              elementTypes[i + leftTupleType.getElementCount()] = rightTupleType.getElementType(i);
+            }
+            return PyTupleType.create(function, elementTypes);
+          }
+        }
+      }
     }
     return null;
   }
diff --git a/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java b/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java
index 8afd9cc..fa869bf 100644
--- a/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java
+++ b/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.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.
@@ -120,6 +120,7 @@
     myErrorPanel.add(facetErrorPanel.getComponent(), BorderLayout.CENTER);
 
     facetErrorPanel.getValidatorsManager().registerValidator(new FacetEditorValidator() {
+      @NotNull
       @Override
       public ValidationResult check() {
         final Sdk sdk = PythonSdkType.findPythonSdk(myModule);
diff --git a/python/src/com/jetbrains/python/console/PyConsoleOptions.java b/python/src/com/jetbrains/python/console/PyConsoleOptions.java
index e76c2ae..59ae687 100644
--- a/python/src/com/jetbrains/python/console/PyConsoleOptions.java
+++ b/python/src/com/jetbrains/python/console/PyConsoleOptions.java
@@ -95,7 +95,7 @@
 
   @Tag("console-settings")
   public static class PyConsoleSettings {
-    public String myCustomStartScript = RunPythonConsoleAction.CONSOLE_START_COMMAND;
+    public String myCustomStartScript = PydevConsoleRunner.CONSOLE_START_COMMAND;
     public String mySdkHome = null;
     public String myInterpreterOptions = "";
     public boolean myUseModuleSdk;
diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java
index 848818b..b3ebacd 100644
--- a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java
+++ b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java
@@ -17,9 +17,8 @@
 
 import com.google.common.base.CharMatcher;
 import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.Lists;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Collections2;
 import com.google.common.collect.Maps;
 import com.intellij.execution.ExecutionException;
 import com.intellij.execution.ExecutionHelper;
@@ -48,6 +47,8 @@
 import com.intellij.openapi.editor.actions.SplitLineAction;
 import com.intellij.openapi.editor.ex.EditorEx;
 import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
 import com.intellij.openapi.progress.ProgressIndicator;
 import com.intellij.openapi.progress.ProgressManager;
 import com.intellij.openapi.progress.Task;
@@ -58,19 +59,17 @@
 import com.intellij.openapi.ui.Messages;
 import com.intellij.openapi.util.Couple;
 import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.io.FileUtil;
 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.openapi.wm.ToolWindow;
-import com.intellij.openapi.wm.ToolWindowManager;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiFile;
 import com.intellij.psi.impl.source.tree.FileElement;
 import com.intellij.remote.RemoteSshProcess;
 import com.intellij.testFramework.LightVirtualFile;
-import com.intellij.ui.content.Content;
 import com.intellij.util.ArrayUtil;
 import com.intellij.util.IJSwingUtilities;
 import com.intellij.util.PathMappingSettings;
@@ -94,6 +93,7 @@
 import com.jetbrains.python.run.PythonCommandLineState;
 import com.jetbrains.python.run.PythonTracebackFilter;
 import com.jetbrains.python.sdk.PySdkUtil;
+import com.jetbrains.python.sdk.PythonSdkType;
 import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
 import icons.PythonIcons;
 import org.apache.xmlrpc.XmlRpcException;
@@ -115,6 +115,9 @@
  * @author oleg
  */
 public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonConsoleView> {
+  public static final String WORKING_DIR_ENV = "WORKING_DIR_AND_PYTHON_PATHS";
+  public static final String CONSOLE_START_COMMAND = "import sys; print('Python %s on %s' % (sys.version, sys.platform))\n" +
+                                                     "sys.path.extend([" + WORKING_DIR_ENV + "])\n";
   private static final Logger LOG = Logger.getInstance(PydevConsoleRunner.class.getName());
   @SuppressWarnings("SpellCheckingInspection")
   public static final String PYDEV_PYDEVCONSOLE_PY = "pydev/pydevconsole.py";
@@ -139,18 +142,100 @@
 
   private static final long APPROPRIATE_TO_WAIT = 60000;
   private PyRemoteSdkCredentials myRemoteCredentials;
-  private ToolWindow myToolWindow;
 
   private String myConsoleTitle = null;
 
-  protected PydevConsoleRunner(@NotNull final Project project,
+  public PydevConsoleRunner(@NotNull final Project project,
                                @NotNull Sdk sdk, @NotNull final PyConsoleType consoleType,
                                @Nullable final String workingDir,
-                               Map<String, String> environmentVariables) {
+                               Map<String, String> environmentVariables, String ... statementsToExecute) {
     super(project, consoleType.getTitle(), workingDir);
     mySdk = sdk;
     myConsoleType = consoleType;
     myEnvironmentVariables = environmentVariables;
+    myStatementsToExecute = statementsToExecute;
+  }
+
+  public static PathMappingSettings getMappings(Project project, Sdk sdk) {
+    PathMappingSettings mappingSettings = null;
+    if (PySdkUtil.isRemote(sdk)) {
+      PythonRemoteInterpreterManager instance = PythonRemoteInterpreterManager.getInstance();
+      if (instance != null) {
+        //noinspection ConstantConditions
+        mappingSettings =
+          instance.setupMappings(project, (PyRemoteSdkAdditionalDataBase)sdk.getSdkAdditionalData(), null);
+      }
+    }
+    return mappingSettings;
+  }
+
+  @NotNull
+  public static Pair<Sdk, Module> findPythonSdkAndModule(@NotNull Project project, @Nullable Module contextModule) {
+    Sdk sdk = null;
+    Module module = null;
+    PyConsoleOptions.PyConsoleSettings settings = PyConsoleOptions.getInstance(project).getPythonConsoleSettings();
+    String sdkHome = settings.getSdkHome();
+    if (sdkHome != null) {
+      sdk = PythonSdkType.findSdkByPath(sdkHome);
+      if (settings.getModuleName() != null) {
+        module = ModuleManager.getInstance(project).findModuleByName(settings.getModuleName());
+      }
+      else {
+        module = contextModule;
+        if (module == null && ModuleManager.getInstance(project).getModules().length > 0) {
+          module = ModuleManager.getInstance(project).getModules()[0];
+        }
+      }
+    }
+    if (sdk == null && settings.isUseModuleSdk()) {
+      if (contextModule != null) {
+        module = contextModule;
+      }
+      else if (settings.getModuleName() != null) {
+        module = ModuleManager.getInstance(project).findModuleByName(settings.getModuleName());
+      }
+      if (module != null) {
+        if (PythonSdkType.findPythonSdk(module) != null) {
+          sdk = PythonSdkType.findPythonSdk(module);
+        }
+      }
+    }
+    else if (contextModule != null) {
+      if (module == null) {
+        module = contextModule;
+      }
+      if (sdk == null) {
+        sdk = PythonSdkType.findPythonSdk(module);
+      }
+    }
+
+    if (sdk == null) {
+      for (Module m : ModuleManager.getInstance(project).getModules()) {
+        if (PythonSdkType.findPythonSdk(m) != null) {
+          sdk = PythonSdkType.findPythonSdk(m);
+          module = m;
+          break;
+        }
+      }
+    }
+    if (sdk == null) {
+      if (PythonSdkType.getAllSdks().size() > 0) {
+        //noinspection UnusedAssignment
+        sdk = PythonSdkType.getAllSdks().get(0); //take any python sdk
+      }
+    }
+    return Pair.create(sdk, module);
+  }
+
+  public static String constructPythonPathCommand(Collection<String> pythonPath, String command) {
+    final String path = Joiner.on(", ").join(Collections2.transform(pythonPath, new Function<String, String>() {
+      @Override
+      public String apply(String input) {
+        return "'" + input.replace("\\", "\\\\").replace("'", "\\'") + "'";
+      }
+    }));
+
+    return command.replace(WORKING_DIR_ENV, path);
   }
 
   public void setStatementsToExecute(String... statementsToExecute) {
@@ -196,21 +281,6 @@
     return actions;
   }
 
-  @NotNull
-  public static PydevConsoleRunner createAndRun(@NotNull final Project project,
-                                                @NotNull final Sdk sdk,
-                                                @NotNull final PyConsoleType consoleType,
-                                                @Nullable final String workingDirectory,
-                                                @NotNull final Map<String, String> environmentVariables,
-                                                @Nullable final ToolWindow toolWindow,
-                                                final String... statements2execute) {
-    final PydevConsoleRunner consoleRunner = create(project, sdk, consoleType, workingDirectory, environmentVariables);
-    consoleRunner.setToolWindow(toolWindow);
-    consoleRunner.setStatementsToExecute(statements2execute);
-    consoleRunner.run();
-    return consoleRunner;
-  }
-
   public void run() {
     UIUtil.invokeAndWaitIfNeeded(new Runnable() {
       @Override
@@ -250,16 +320,7 @@
                                           Sdk sdk,
                                           PyConsoleType consoleType,
                                           String workingDirectory) {
-    return create(project, sdk, consoleType, workingDirectory, Maps.<String, String>newHashMap());
-  }
-
-  @NotNull
-  private static PydevConsoleRunner create(@NotNull final Project project,
-                                           @NotNull final Sdk sdk,
-                                           @NotNull final PyConsoleType consoleType,
-                                           @Nullable final String workingDirectory,
-                                           @NotNull final Map<String, String> environmentVariables) {
-    return new PydevConsoleRunner(project, sdk, consoleType, workingDirectory, environmentVariables);
+    return new PydevConsoleRunner(project, sdk, consoleType, workingDirectory, Maps.<String, String>newHashMap(), new String[]{});
   }
 
   private static int[] findAvailablePorts(Project project, PyConsoleType consoleType) {
@@ -501,12 +562,6 @@
     return myConsoleTitle;
   }
 
-  @Override
-  protected void showConsole(Executor defaultExecutor, RunContentDescriptor contentDescriptor) {
-    PythonConsoleToolWindow terminalView = PythonConsoleToolWindow.getInstance(getProject());
-    terminalView.init(getToolWindow(), contentDescriptor);
-  }
-
   protected AnAction createRerunAction() {
     return new RestartAction(this);
   }
@@ -631,10 +686,7 @@
     return stopAction;
   }
 
-  private void clearContent(RunContentDescriptor descriptor) {
-    Content content = getToolWindow().getContentManager().findContent(descriptor.getDisplayName());
-    assert content != null;
-    getToolWindow().getContentManager().removeContent(content, true);
+  protected void clearContent(RunContentDescriptor descriptor) {
   }
 
   private AnAction createConsoleStoppingAction(final AnAction generalStopAction) {
@@ -792,16 +844,6 @@
     }
   }
 
-  public ToolWindow getToolWindow() {
-    if (myToolWindow == null) {
-      myToolWindow = ToolWindowManager.getInstance(getProject()).getToolWindow(PythonConsoleToolWindowFactory.ID);
-    }
-    return myToolWindow;
-  }
-
-  public void setToolWindow(ToolWindow toolWindow) {
-    myToolWindow = toolWindow;
-  }
 
   public interface ConsoleListener {
     void handleConsoleInitialized(LanguageConsoleView consoleView);
@@ -955,20 +997,7 @@
     return session;
   }
 
-  @Override
-  protected List<String> getActiveConsoleNames(final String consoleTitle) {
-    return FluentIterable.from(
-      Lists.newArrayList(PythonConsoleToolWindow.getInstance(getProject()).getToolWindow().getContentManager().getContents())).transform(
-      new Function<Content, String>() {
-        @Override
-        public String apply(Content input) {
-          return input.getDisplayName();
-        }
-      }).filter(new Predicate<String>() {
-      @Override
-      public boolean apply(String input) {
-        return input.contains(consoleTitle);
-      }
-    }).toList();
+  public static PythonConsoleRunnerFactory factory() {
+    return new PydevConsoleRunnerFactory();
   }
 }
diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunnerFactory.java b/python/src/com/jetbrains/python/console/PydevConsoleRunnerFactory.java
new file mode 100644
index 0000000..5bed751
--- /dev/null
+++ b/python/src/com/jetbrains/python/console/PydevConsoleRunnerFactory.java
@@ -0,0 +1,122 @@
+/*
+ * 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.console;
+
+import com.google.common.collect.Maps;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.PathMappingSettings;
+import com.jetbrains.python.buildout.BuildoutFacet;
+import com.jetbrains.python.run.PythonCommandLineState;
+import com.jetbrains.python.sdk.PythonEnvUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+* @author traff
+*/
+public class PydevConsoleRunnerFactory extends PythonConsoleRunnerFactory {
+  @Override
+  public PydevConsoleRunner createConsoleRunner(@NotNull Project project,
+                                                @Nullable Module contextModule) {
+    Pair<Sdk, Module> sdkAndModule = PydevConsoleRunner.findPythonSdkAndModule(project, contextModule);
+
+    Module module = sdkAndModule.second;
+    Sdk sdk = sdkAndModule.first;
+
+    assert sdk != null;
+
+    PathMappingSettings mappingSettings = PydevConsoleRunner.getMappings(project, sdk);
+
+    String[] setupFragment;
+
+    PyConsoleOptions.PyConsoleSettings settingsProvider = PyConsoleOptions.getInstance(project).getPythonConsoleSettings();
+    Collection<String> pythonPath = PythonCommandLineState.collectPythonPath(module, settingsProvider.addContentRoots(),
+                                                                             settingsProvider.addSourceRoots());
+
+    if (mappingSettings != null) {
+      pythonPath = mappingSettings.convertToRemote(pythonPath);
+    }
+
+    String customStartScript = settingsProvider == null ? "" : settingsProvider.getCustomStartScript();
+
+    if (customStartScript.trim().length() > 0) {
+      customStartScript = "\n" + customStartScript;
+    }
+
+    String selfPathAppend = PydevConsoleRunner.constructPythonPathCommand(pythonPath, customStartScript);
+
+    String workingDir = settingsProvider.getWorkingDirectory();
+    if (StringUtil.isEmpty(workingDir)) {
+      if (module != null && ModuleRootManager.getInstance(module).getContentRoots().length > 0) {
+        workingDir = ModuleRootManager.getInstance(module).getContentRoots()[0].getPath();
+      }
+      else {
+        if (ModuleManager.getInstance(project).getModules().length > 0) {
+          VirtualFile[] roots = ModuleRootManager.getInstance(ModuleManager.getInstance(project).getModules()[0]).getContentRoots();
+          if (roots.length > 0) {
+            workingDir = roots[0].getPath();
+          }
+        }
+      }
+    }
+
+    if (mappingSettings != null) {
+      workingDir = mappingSettings.convertToRemote(workingDir);
+    }
+
+    BuildoutFacet facet = null;
+    if (module != null) {
+      facet = BuildoutFacet.getInstance(module);
+    }
+
+    if (facet != null) {
+      List<String> path = facet.getAdditionalPythonPath();
+      if (mappingSettings != null) {
+        path = mappingSettings.convertToRemote(path);
+      }
+      String prependStatement = facet.getPathPrependStatement(path);
+      setupFragment = new String[]{prependStatement, selfPathAppend};
+    }
+    else {
+      setupFragment = new String[]{selfPathAppend};
+    }
+
+    Map<String, String> envs = Maps.newHashMap(settingsProvider.getEnvs());
+    String ipythonEnabled = PyConsoleOptions.getInstance(project).isIpythonEnabled() ? "True" : "False";
+    envs.put(PythonEnvUtil.IPYTHONENABLE, ipythonEnabled);
+
+
+    return createConsoleRunner(project, sdk, workingDir, envs, PyConsoleType.PYTHON, setupFragment);
+  }
+
+  protected PydevConsoleRunner createConsoleRunner(Project project,
+                                                   Sdk sdk,
+                                                   String workingDir,
+                                                   Map<String, String> envs, PyConsoleType consoleType, String ... setupFragment) {
+    return new PydevConsoleRunner(project, sdk, consoleType, workingDir, envs, setupFragment);
+  }
+}
diff --git a/python/src/com/jetbrains/python/console/PythonConsoleRunnerFactory.java b/python/src/com/jetbrains/python/console/PythonConsoleRunnerFactory.java
new file mode 100644
index 0000000..0963865
--- /dev/null
+++ b/python/src/com/jetbrains/python/console/PythonConsoleRunnerFactory.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.console;
+
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+* @author traff
+*/
+public abstract class PythonConsoleRunnerFactory {
+  @NotNull
+  public static PythonConsoleRunnerFactory getInstance() {
+    return ServiceManager.getService(PythonConsoleRunnerFactory.class);
+  }
+  public abstract PydevConsoleRunner createConsoleRunner(@NotNull final Project project,
+                                         @Nullable Module contextModule);
+}
diff --git a/python/src/com/jetbrains/python/console/PythonConsoleToolWindow.java b/python/src/com/jetbrains/python/console/PythonConsoleToolWindow.java
index e8c50e4..177c352 100644
--- a/python/src/com/jetbrains/python/console/PythonConsoleToolWindow.java
+++ b/python/src/com/jetbrains/python/console/PythonConsoleToolWindow.java
@@ -1,8 +1,14 @@
 package com.jetbrains.python.console;
 
+import com.google.common.base.Function;
+import com.google.common.base.Predicates;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Lists;
 import com.intellij.execution.ui.RunContentDescriptor;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.SimpleToolWindowPanel;
+import com.intellij.openapi.util.ActionCallback;
+import com.intellij.openapi.util.Key;
 import com.intellij.openapi.wm.ToolWindow;
 import com.intellij.openapi.wm.ToolWindowManager;
 import com.intellij.openapi.wm.ex.ToolWindowManagerEx;
@@ -11,20 +17,25 @@
 import com.intellij.ui.content.Content;
 import com.intellij.ui.content.ContentFactory;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
 import java.awt.event.FocusEvent;
 import java.awt.event.FocusListener;
+import java.util.List;
 
 /**
  * @author traff
  */
 public class PythonConsoleToolWindow {
+  public static final Key<RunContentDescriptor> CONTENT_DESCRIPTOR = Key.create("CONTENT_DESCRIPTOR");
 
   private final Project myProject;
 
   private boolean myInitialized = false;
 
+  private ActionCallback myActivation = new ActionCallback();
+
   public PythonConsoleToolWindow(Project project) {
     myProject = project;
   }
@@ -33,6 +44,17 @@
     return project.getComponent(PythonConsoleToolWindow.class);
   }
 
+  public List<RunContentDescriptor> getConsoleContentDescriptors() {
+    return FluentIterable.from(Lists.newArrayList(getToolWindow().getContentManager().getContents()))
+      .transform(new Function<Content, RunContentDescriptor>() {
+        @Override
+        public RunContentDescriptor apply(@Nullable Content input) {
+          return input != null ? input.getUserData(CONTENT_DESCRIPTOR) : null;
+        }
+      }).filter(
+        Predicates.notNull()).toList();
+  }
+
 
   public void init(final @NotNull ToolWindow toolWindow, final @NotNull RunContentDescriptor contentDescriptor) {
     addContent(toolWindow, contentDescriptor);
@@ -42,7 +64,7 @@
     }
   }
 
-  private void doInit(final ToolWindow toolWindow) {
+  private void doInit(@NotNull final ToolWindow toolWindow) {
     myInitialized = true;
 
     toolWindow.setToHideOnEmptyContent(true);
@@ -58,7 +80,8 @@
         if (window != null) {
           boolean visible = window.isVisible();
           if (visible && toolWindow.getContentManager().getContentCount() == 0) {
-            RunPythonConsoleAction.runPythonConsole(myProject, null, toolWindow);
+            PydevConsoleRunner runner = PythonConsoleRunnerFactory.getInstance().createConsoleRunner(myProject, null);
+            runner.run();
           }
         }
       }
@@ -98,10 +121,11 @@
 
   private static void resetContent(RunContentDescriptor contentDescriptor, SimpleToolWindowPanel panel, Content content) {
     panel.setContent(contentDescriptor.getComponent());
-    //panel.addFocusListener(createFocusListener(toolWindow));
 
     content.setComponent(panel);
     content.setPreferredFocusableComponent(contentDescriptor.getComponent());
+
+    content.putUserData(CONTENT_DESCRIPTOR, contentDescriptor);
   }
 
   private static FocusListener createFocusListener(final ToolWindow toolWindow) {
@@ -124,4 +148,13 @@
   private static JComponent getComponentToFocus(ToolWindow window) {
     return window.getContentManager().getComponent();
   }
+
+  public void initialized() {
+    myActivation.setDone();
+  }
+
+  public void activate(@NotNull Runnable runnable) {
+    myActivation.doWhenDone(runnable);
+    getToolWindow().activate(null);
+  }
 }
diff --git a/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java b/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java
index f042a53..2a49d5e 100644
--- a/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java
+++ b/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java
@@ -15,10 +15,12 @@
  */
 package com.jetbrains.python.console;
 
+import com.intellij.execution.console.LanguageConsoleView;
 import com.intellij.openapi.project.DumbAware;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.wm.ToolWindow;
 import com.intellij.openapi.wm.ToolWindowFactory;
+import org.jetbrains.annotations.NotNull;
 
 /**
  * @author traff
@@ -27,7 +29,14 @@
   public static final String ID = "Python Console";
 
   @Override
-  public void createToolWindowContent(Project project, ToolWindow toolWindow) {
-    RunPythonConsoleAction.runPythonConsole(project, null, toolWindow);
+  public void createToolWindowContent(final @NotNull Project project, final @NotNull ToolWindow toolWindow) {
+    PydevConsoleRunner runner = PythonConsoleRunnerFactory.getInstance().createConsoleRunner(project, null);
+    runner.addConsoleListener(new PydevConsoleRunner.ConsoleListener() {
+      @Override
+      public void handleConsoleInitialized(LanguageConsoleView consoleView) {
+        PythonConsoleToolWindow.getInstance(project).initialized();
+      }
+    });
+    runner.run();
   }
 }
diff --git a/python/src/com/jetbrains/python/console/PythonConsoleView.java b/python/src/com/jetbrains/python/console/PythonConsoleView.java
index 699294e..86cb0e9 100644
--- a/python/src/com/jetbrains/python/console/PythonConsoleView.java
+++ b/python/src/com/jetbrains/python/console/PythonConsoleView.java
@@ -29,6 +29,7 @@
 import com.intellij.ide.GeneralSettings;
 import com.intellij.openapi.Disposable;
 import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.colors.EditorColorsScheme;
@@ -119,7 +120,7 @@
     myExecuteActionHandler = consoleExecuteActionHandler;
   }
 
-  private void addSaveContentFocusListener(JComponent component){
+  private void addSaveContentFocusListener(JComponent component) {
     component.addFocusListener(new FocusListener() {
       @Override
       public void focusGained(FocusEvent e) {
@@ -129,7 +130,8 @@
       }
 
       @Override
-      public void focusLost(FocusEvent e) {}
+      public void focusLost(FocusEvent e) {
+      }
     });
   }
 
@@ -150,38 +152,53 @@
 
   @Override
   public void executeCode(final @NotNull String code, @Nullable final Editor editor) {
-    ProgressManager.getInstance().run(new Task.Backgroundable(null, "Executing code in console...", false) {
+    showConsole(new Runnable() {
       @Override
-      public void run(@NotNull final ProgressIndicator indicator) {
-        long time = System.currentTimeMillis();
-        while (!myExecuteActionHandler.isEnabled() || !myExecuteActionHandler.canExecuteNow()) {
-          if (indicator.isCanceled()) {
-            break;
-          }
-          if (System.currentTimeMillis() - time > 1000) {
-            if (editor != null) {
-              UIUtil.invokeLaterIfNeeded(new Runnable() {
-                @Override
-                public void run() {
-                  HintManager.getInstance().showErrorHint(editor, myExecuteActionHandler.getCantExecuteMessage());
+      public void run() {
+        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()) {
+              if (indicator.isCanceled()) {
+                break;
+              }
+              if (System.currentTimeMillis() - time > 1000) {
+                if (editor != null) {
+                  UIUtil.invokeLaterIfNeeded(new Runnable() {
+                    @Override
+                    public void run() {
+                      HintManager.getInstance().showErrorHint(editor, myExecuteActionHandler.getCantExecuteMessage());
+                    }
+                  });
                 }
-              });
+                return;
+              }
+              try {
+                Thread.sleep(300);
+              }
+              catch (InterruptedException ignored) {
+              }
             }
-            return;
+            if (!indicator.isCanceled()) {
+              doExecute(code);
+            }
           }
-          try {
-            Thread.sleep(300);
-          }
-          catch (InterruptedException ignored) {
-          }
-        }
-        if (!indicator.isCanceled()) {
-          doExecute(code);
-        }
+        });
       }
     });
   }
 
+  private void showConsole(@NotNull Runnable runnable) {
+    PythonConsoleToolWindow toolWindow = PythonConsoleToolWindow.getInstance(myProject);
+    if (toolWindow != null && !ApplicationManager.getApplication().isUnitTestMode()) {
+      toolWindow.getToolWindow().activate(runnable);
+    }
+    else {
+      runnable.run();
+    }
+  }
+
 
   private void doExecute(String code) {
     String codeFragment = PyConsoleIndentUtil.normalize(code, myExecuteActionHandler.getCurrentIndentSize());
diff --git a/python/src/com/jetbrains/python/console/PythonToolWindowConsoleRunner.java b/python/src/com/jetbrains/python/console/PythonToolWindowConsoleRunner.java
new file mode 100644
index 0000000..5c98bd5
--- /dev/null
+++ b/python/src/com/jetbrains/python/console/PythonToolWindowConsoleRunner.java
@@ -0,0 +1,85 @@
+/*
+ * 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.console;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Lists;
+import com.intellij.execution.Executor;
+import com.intellij.execution.ui.RunContentDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.wm.ToolWindow;
+import com.intellij.openapi.wm.ToolWindowManager;
+import com.intellij.ui.content.Content;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author traff
+ */
+public class PythonToolWindowConsoleRunner extends PydevConsoleRunner {
+  private ToolWindow myToolWindow;
+
+  public PythonToolWindowConsoleRunner(@NotNull Project project,
+                                          @NotNull Sdk sdk,
+                                          @NotNull PyConsoleType consoleType,
+                                          @Nullable String workingDir, Map<String, String> environmentVariables,
+                                          String ... statementsToExecute) {
+    super(project, sdk, consoleType, workingDir, environmentVariables, statementsToExecute);
+  }
+
+  public ToolWindow getToolWindow() {
+    if (myToolWindow == null) {
+      myToolWindow = ToolWindowManager.getInstance(getProject()).getToolWindow(PythonConsoleToolWindowFactory.ID);
+    }
+    return myToolWindow;
+  }
+
+  @Override
+  protected void showConsole(Executor defaultExecutor, @NotNull RunContentDescriptor contentDescriptor) {
+    PythonConsoleToolWindow terminalView = PythonConsoleToolWindow.getInstance(getProject());
+    terminalView.init(getToolWindow(), contentDescriptor);
+  }
+
+  @Override
+  protected void clearContent(RunContentDescriptor descriptor) {
+    Content content = getToolWindow().getContentManager().findContent(descriptor.getDisplayName());
+    assert content != null;
+    getToolWindow().getContentManager().removeContent(content, true);
+  }
+
+  @Override
+  protected List<String> getActiveConsoleNames(final String consoleTitle) {
+    return FluentIterable.from(
+      Lists.newArrayList(PythonConsoleToolWindow.getInstance(getProject()).getToolWindow().getContentManager().getContents())).transform(
+      new Function<Content, String>() {
+        @Override
+        public String apply(Content input) {
+          return input.getDisplayName();
+        }
+      }).filter(new Predicate<String>() {
+      @Override
+      public boolean apply(String input) {
+        return input.contains(consoleTitle);
+      }
+    }).toList();
+  }
+}
diff --git a/python/src/com/jetbrains/python/console/PythonToolWindowConsoleRunnerFactory.java b/python/src/com/jetbrains/python/console/PythonToolWindowConsoleRunnerFactory.java
new file mode 100644
index 0000000..240f3ab
--- /dev/null
+++ b/python/src/com/jetbrains/python/console/PythonToolWindowConsoleRunnerFactory.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.console;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+
+import java.util.Map;
+
+/**
+ * @author traff
+ */
+public class PythonToolWindowConsoleRunnerFactory extends PydevConsoleRunnerFactory {
+  @Override
+  protected PydevConsoleRunner createConsoleRunner(Project project,
+                                                   Sdk sdk,
+                                                   String workingDir,
+                                                   Map<String, String> envs, PyConsoleType consoleType, String ... setupFragment) {
+    return new PythonToolWindowConsoleRunner(project, sdk, consoleType, workingDir, envs, setupFragment);
+  }
+}
diff --git a/python/src/com/jetbrains/python/console/RunPythonConsoleAction.java b/python/src/com/jetbrains/python/console/RunPythonConsoleAction.java
deleted file mode 100644
index 566adea..0000000
--- a/python/src/com/jetbrains/python/console/RunPythonConsoleAction.java
+++ /dev/null
@@ -1,240 +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.console;
-
-import com.google.common.base.Function;
-import com.google.common.base.Joiner;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.Maps;
-import com.intellij.openapi.actionSystem.AnAction;
-import com.intellij.openapi.actionSystem.AnActionEvent;
-import com.intellij.openapi.actionSystem.CommonDataKeys;
-import com.intellij.openapi.actionSystem.LangDataKeys;
-import com.intellij.openapi.module.Module;
-import com.intellij.openapi.module.ModuleManager;
-import com.intellij.openapi.project.DumbAware;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.projectRoots.Sdk;
-import com.intellij.openapi.roots.ModuleRootManager;
-import com.intellij.openapi.util.Pair;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.openapi.wm.ToolWindow;
-import com.intellij.util.PathMappingSettings;
-import com.jetbrains.python.buildout.BuildoutFacet;
-import com.jetbrains.python.remote.PyRemoteSdkAdditionalDataBase;
-import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
-import com.jetbrains.python.run.PythonCommandLineState;
-import com.jetbrains.python.sdk.PySdkUtil;
-import com.jetbrains.python.sdk.PythonEnvUtil;
-import com.jetbrains.python.sdk.PythonSdkType;
-import icons.PythonIcons;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-/**
- * @author oleg
- */
-public class RunPythonConsoleAction extends AnAction implements DumbAware {
-
-  public static final String WORKING_DIR_ENV = "WORKING_DIR_AND_PYTHON_PATHS";
-
-  public static final String CONSOLE_START_COMMAND = "import sys; print('Python %s on %s' % (sys.version, sys.platform))\n" +
-                                                   "sys.path.extend([" + WORKING_DIR_ENV + "])\n";
-
-  public RunPythonConsoleAction() {
-    super();
-    getTemplatePresentation().setIcon(PythonIcons.Python.Python);
-  }
-
-  @Override
-  public void update(final AnActionEvent e) {
-    e.getPresentation().setVisible(true);
-    e.getPresentation().setEnabled(false);
-    final Project project = e.getData(CommonDataKeys.PROJECT);
-    if (project != null) {
-      Pair<Sdk, Module> sdkAndModule = findPythonSdkAndModule(project, e.getData(LangDataKeys.MODULE));
-      if (sdkAndModule.first != null) {
-        e.getPresentation().setEnabled(true);
-      }
-    }
-  }
-
-  public void actionPerformed(final AnActionEvent e) {
-    final Project project = e.getData(CommonDataKeys.PROJECT);
-    runPythonConsole(project, e.getData(LangDataKeys.MODULE), null);
-  }
-
-  @NotNull
-  public static PydevConsoleRunner runPythonConsole(Project project, Module contextModule, @Nullable ToolWindow toolWindow) {
-    assert project != null : "Project is null";
-
-    Pair<Sdk, Module> sdkAndModule = findPythonSdkAndModule(project, contextModule);
-
-    Module module = sdkAndModule.second;
-    Sdk sdk = sdkAndModule.first;
-
-    assert sdk != null;
-
-    PathMappingSettings mappingSettings = getMappings(project, sdk);
-
-    String[] setupFragment;
-
-    PyConsoleOptions.PyConsoleSettings settingsProvider = PyConsoleOptions.getInstance(project).getPythonConsoleSettings();
-    Collection<String> pythonPath = PythonCommandLineState.collectPythonPath(module, settingsProvider.addContentRoots(),
-                                                                             settingsProvider.addSourceRoots());
-
-    if (mappingSettings != null) {
-      pythonPath = mappingSettings.convertToRemote(pythonPath);
-    }
-
-    String customStartScript = settingsProvider == null ? "" : settingsProvider.getCustomStartScript();
-
-    if(customStartScript.trim().length() > 0){
-      customStartScript = "\n" + customStartScript;
-    }
-
-    String selfPathAppend = constructPythonPathCommand(pythonPath, customStartScript);
-
-    String workingDir = settingsProvider.getWorkingDirectory();
-    if (StringUtil.isEmpty(workingDir)) {
-      if (module != null && ModuleRootManager.getInstance(module).getContentRoots().length > 0) {
-        workingDir = ModuleRootManager.getInstance(module).getContentRoots()[0].getPath();
-      }
-      else {
-        if (ModuleManager.getInstance(project).getModules().length > 0) {
-          VirtualFile[] roots = ModuleRootManager.getInstance(ModuleManager.getInstance(project).getModules()[0]).getContentRoots();
-          if (roots.length > 0) {
-            workingDir = roots[0].getPath();
-          }
-        }
-      }
-    }
-
-    if (mappingSettings != null) {
-      workingDir = mappingSettings.convertToRemote(workingDir);
-    }
-
-    BuildoutFacet facet = null;
-    if (module != null) {
-      facet = BuildoutFacet.getInstance(module);
-    }
-
-    if (facet != null) {
-      List<String> path = facet.getAdditionalPythonPath();
-      if (mappingSettings != null) {
-        path = mappingSettings.convertToRemote(path);
-      }
-      String prependStatement = facet.getPathPrependStatement(path);
-      setupFragment = new String[]{prependStatement, selfPathAppend};
-    }
-    else {
-      setupFragment = new String[]{selfPathAppend};
-    }
-
-    Map<String, String> envs  = Maps.newHashMap(settingsProvider.getEnvs());
-    String ipythonEnabled = PyConsoleOptions.getInstance(project).isIpythonEnabled() ? "True" : "False";
-    envs.put(PythonEnvUtil.IPYTHONENABLE, ipythonEnabled);
-
-    return PydevConsoleRunner
-      .createAndRun(project, sdk, PyConsoleType.PYTHON, workingDir, envs, toolWindow, setupFragment);
-  }
-
-  public static PathMappingSettings getMappings(Project project, Sdk sdk) {
-    PathMappingSettings mappingSettings = null;
-    if (PySdkUtil.isRemote(sdk)) {
-      PythonRemoteInterpreterManager instance = PythonRemoteInterpreterManager.getInstance();
-      if (instance != null) {
-        mappingSettings =
-          instance.setupMappings(project, (PyRemoteSdkAdditionalDataBase)sdk.getSdkAdditionalData(), null);
-      }
-    }
-    return mappingSettings;
-  }
-
-  @NotNull
-  private static Pair<Sdk, Module> findPythonSdkAndModule(Project project, Module contextModule) {
-    Sdk sdk = null;
-    Module module = null;
-    PyConsoleOptions.PyConsoleSettings settings = PyConsoleOptions.getInstance(project).getPythonConsoleSettings();
-    String sdkHome = settings.getSdkHome();
-    if (sdkHome != null) {
-      sdk = PythonSdkType.findSdkByPath(sdkHome);
-      if (settings.getModuleName() != null) {
-        module = ModuleManager.getInstance(project).findModuleByName(settings.getModuleName());
-      }
-      else {
-        module = contextModule;
-        if (module == null && ModuleManager.getInstance(project).getModules().length > 0) {
-          module = ModuleManager.getInstance(project).getModules()[0];
-        }
-      }
-    }
-    if (sdk == null && settings.isUseModuleSdk()) {
-      if (contextModule != null) {
-        module = contextModule;
-      }
-      else if (settings.getModuleName() != null) {
-        module = ModuleManager.getInstance(project).findModuleByName(settings.getModuleName());
-      }
-      if (module != null) {
-        if (PythonSdkType.findPythonSdk(module) != null) {
-          sdk = PythonSdkType.findPythonSdk(module);
-        }
-      }
-    }
-    else if (contextModule != null) {
-      if (module == null) {
-        module = contextModule;
-      }
-      if (sdk == null) {
-        sdk = PythonSdkType.findPythonSdk(module);
-      }
-    }
-
-    if (sdk == null) {
-      for (Module m : ModuleManager.getInstance(project).getModules()) {
-        if (PythonSdkType.findPythonSdk(m) != null) {
-          sdk = PythonSdkType.findPythonSdk(m);
-          module = m;
-          break;
-        }
-      }
-    }
-    if (sdk == null) {
-      if (PythonSdkType.getAllSdks().size() > 0) {
-        //noinspection UnusedAssignment
-        sdk = PythonSdkType.getAllSdks().get(0); //take any python sdk
-      }
-    }
-    return Pair.create(sdk, module);
-  }
-
-  public static String constructPythonPathCommand(Collection<String> pythonPath, String command) {
-    final String path = Joiner.on(", ").join(Collections2.transform(pythonPath, new Function<String, String>() {
-      @Override
-      public String apply(String input) {
-        return "'" + input.replace("\\", "\\\\").replace("'", "\\'") + "'";
-      }
-    }));
-
-    return command.replace(WORKING_DIR_ENV, path);
-  }
-}
diff --git a/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java b/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java
index 32ad8a3..4acfcbd 100644
--- a/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java
+++ b/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java
@@ -89,7 +89,8 @@
                                 BLANK_LINES);
     }
     else if (settingsType == SettingsType.WRAPPING_AND_BRACES_SETTINGS) {
-      consumer.showStandardOptions("KEEP_LINE_BREAKS",
+      consumer.showStandardOptions("RIGHT_MARGIN",
+                                   "KEEP_LINE_BREAKS",
                                    "WRAP_LONG_LINES",
                                    "ALIGN_MULTILINE_PARAMETERS",
                                    "ALIGN_MULTILINE_PARAMETERS_IN_CALLS");
diff --git a/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java b/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java
index 162fa0b..40efca3 100644
--- a/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java
@@ -41,6 +41,7 @@
 
 import static com.jetbrains.python.inspections.PyStringFormatParser.filterSubstitutions;
 import static com.jetbrains.python.inspections.PyStringFormatParser.parsePercentFormat;
+import static com.jetbrains.python.psi.PyUtil.as;
 
 /**
  * @author Alexey.Ivanov
@@ -416,9 +417,9 @@
           inspectValues(((PyParenthesizedExpression)rightExpression).getContainedExpression());
         }
         else {
-          final PyType type = myTypeEvalContext.getType(rightExpression);
+          final PyClassType type = as(myTypeEvalContext.getType(rightExpression), PyClassType.class);
           if (type != null) {
-            if (myUsedMappingKeys.size() > 0 && !("dict".equals(type.getName()))) {
+            if (myUsedMappingKeys.size() > 0 && !PyABCUtil.isSubclass(type.getPyClass(), PyNames.MAPPING)) {
               registerProblem(rightExpression, PyBundle.message("INSP.format.requires.mapping"));
               return;
             }
diff --git a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java
index c0fb3c1..4fd5862 100644
--- a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java
+++ b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java
@@ -69,7 +69,6 @@
 
 import javax.swing.*;
 import java.util.*;
-import java.util.HashSet;
 
 import static com.jetbrains.python.inspections.quickfix.AddIgnoredIdentifierQuickFix.END_WILDCARD;
 
@@ -833,6 +832,9 @@
         else {
           actions.add(importFix);
         }
+        if (ScopeUtil.getScopeOwner(node) instanceof PyFunction) {
+          actions.add(importFix.forLocalImport());
+        }
       }
     }
 
diff --git a/python/src/com/jetbrains/python/packaging/PyPIPackageUtil.java b/python/src/com/jetbrains/python/packaging/PyPIPackageUtil.java
index fbbdfa2..2b8591f 100644
--- a/python/src/com/jetbrains/python/packaging/PyPIPackageUtil.java
+++ b/python/src/com/jetbrains/python/packaging/PyPIPackageUtil.java
@@ -239,8 +239,6 @@
       if (connection instanceof HttpsURLConnection) {
         ((HttpsURLConnection)connection).setSSLSocketFactory(sslContext.getSocketFactory());
       }
-      connection.setConnectTimeout(5000);
-      connection.setReadTimeout(5000);
       InputStream is = connection.getInputStream();
       Reader reader = new InputStreamReader(is);
       try{
diff --git a/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java b/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java
index bb0e29f..6356a1f 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java
@@ -20,6 +20,7 @@
 import com.intellij.lang.ASTNode;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiFile;
@@ -285,12 +286,6 @@
     throw new IllegalArgumentException("Invalid call expression text " + functionName);
   }
 
-  public PyImportStatement createImportStatementFromText(final LanguageLevel languageLevel,
-                                                         final String text) {
-    final PsiFile dummyFile = createDummyFile(languageLevel, text);
-    return (PyImportStatement)dummyFile.getFirstChild();
-  }
-
   @Override
   public PyImportElement createImportElement(final LanguageLevel languageLevel, String name) {
     return createFromText(languageLevel, PyImportElement.class, "from foo import " + name, new int[]{0, 6});
@@ -427,6 +422,23 @@
     return createFromText(LanguageLevel.getDefault(), PsiWhiteSpace.class, " \n\n ");
   }
 
+  @NotNull
+  @Override
+  public PyFromImportStatement createFromImportStatement(@NotNull LanguageLevel languageLevel, @NotNull String qualifier,
+                                                         @NotNull String name, @Nullable String alias) {
+    final String asClause = StringUtil.isNotEmpty(alias) ? " as " + alias : "";
+    final String statement = "from " + qualifier + " import " + name + asClause;
+    return createFromText(languageLevel, PyFromImportStatement.class, statement);
+  }
+
+  @NotNull
+  @Override
+  public PyImportStatement createImportStatement(@NotNull LanguageLevel languageLevel, @NotNull String name, @Nullable String alias) {
+    final String asClause = StringUtil.isNotEmpty(alias) ? " as " + alias : "";
+    final String statement = "import " + name + asClause;
+    return createFromText(languageLevel, PyImportStatement.class, statement);
+  }
+
   private static class CommasOnly extends NotNullPredicate<LeafPsiElement> {
     @Override
     protected boolean applyNotNull(@NotNull final LeafPsiElement input) {
diff --git a/python/src/com/jetbrains/python/psi/types/PyFunctionType.java b/python/src/com/jetbrains/python/psi/types/PyFunctionType.java
index 4b3adfd..4bdbae6 100644
--- a/python/src/com/jetbrains/python/psi/types/PyFunctionType.java
+++ b/python/src/com/jetbrains/python/psi/types/PyFunctionType.java
@@ -18,10 +18,12 @@
 import com.intellij.psi.PsiElement;
 import com.intellij.util.ArrayUtil;
 import com.intellij.util.ProcessingContext;
+import com.intellij.util.containers.ContainerUtil;
 import com.jetbrains.python.PyNames;
 import com.jetbrains.python.psi.*;
 import com.jetbrains.python.psi.impl.PyBuiltinCache;
 import com.jetbrains.python.psi.resolve.PyResolveContext;
+import com.jetbrains.python.psi.resolve.QualifiedResolveResult;
 import com.jetbrains.python.psi.resolve.RatedResolveResult;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -30,6 +32,9 @@
 import java.util.Collections;
 import java.util.List;
 
+import static com.jetbrains.python.psi.PyFunction.Modifier.STATICMETHOD;
+import static com.jetbrains.python.psi.PyUtil.as;
+
 /**
  * Type of a particular function that is represented as a {@link Callable} in the PSI tree.
  *
@@ -74,20 +79,79 @@
                                                           @Nullable PyExpression location,
                                                           @NotNull AccessDirection direction,
                                                           @NotNull PyResolveContext resolveContext) {
-    final PyClassTypeImpl functionType = PyBuiltinCache.getInstance(getCallable()).getObjectType(PyNames.FAKE_FUNCTION);
-    if (functionType == null) {
+    final PyClassType delegate = selectFakeType(location, resolveContext.getTypeEvalContext());
+    if (delegate == null) {
       return Collections.emptyList();
     }
-    return functionType.resolveMember(name, location, direction, resolveContext);
+    return delegate.resolveMember(name, location, direction, resolveContext);
   }
 
   @Override
   public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) {
-    final PyClassTypeImpl functionType = PyBuiltinCache.getInstance(getCallable()).getObjectType(PyNames.FAKE_FUNCTION);
-    if (functionType == null) {
+    final TypeEvalContext typeEvalContext = TypeEvalContext.userInitiated(location.getContainingFile());
+    final PyClassType delegate;
+    if (location instanceof PyReferenceExpression) {
+      delegate = selectFakeType(((PyReferenceExpression)location).getQualifier(), typeEvalContext);
+    }
+    else {
+      delegate = PyBuiltinCache.getInstance(getCallable()).getObjectType(PyNames.FAKE_FUNCTION);
+    }
+    if (delegate == null) {
       return ArrayUtil.EMPTY_OBJECT_ARRAY;
     }
-    return functionType.getCompletionVariants(completionPrefix, location, context);
+    return delegate.getCompletionVariants(completionPrefix, location, context);
+  }
+
+  /**
+   * Select either {@link PyNames#FAKE_FUNCTION} or {@link PyNames#FAKE_METHOD} fake class depending on concrete reference used and
+   * language level. Will fallback to fake function type.
+   */
+  @Nullable
+  private PyClassTypeImpl selectFakeType(@Nullable PyExpression location, @NotNull TypeEvalContext context) {
+    if (location instanceof PyReferenceExpression && isBoundMethodReference(((PyReferenceExpression)location), context)) {
+      return PyBuiltinCache.getInstance(getCallable()).getObjectType(PyNames.FAKE_METHOD);
+    }
+    return PyBuiltinCache.getInstance(getCallable()).getObjectType(PyNames.FAKE_FUNCTION);
+  }
+
+  private boolean isBoundMethodReference(@NotNull PyReferenceExpression location, @NotNull TypeEvalContext context) {
+    final PyFunction function = as(getCallable(), PyFunction.class);
+    final boolean isNonStaticMethod = function != null && function.getContainingClass() != null && function.getModifier() != STATICMETHOD;
+    if (isNonStaticMethod) {
+      // In Python 2 unbound methods have __method fake type
+      if (LanguageLevel.forElement(location).isOlderThan(LanguageLevel.PYTHON30)) {
+        return true;
+      }
+      final PyExpression qualifier;
+      if (location.isQualified()) {
+        qualifier = location.getQualifier();
+      }
+      else {
+        final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context);
+        final QualifiedResolveResult resolveResult = location.followAssignmentsChain(resolveContext);
+        final List<PyExpression> qualifiers = resolveResult.getQualifiers();
+        qualifier = ContainerUtil.isEmpty(qualifiers) ? null : qualifiers.get(qualifiers.size() - 1);
+      }
+      if (qualifier != null) {
+        //noinspection ConstantConditions
+        final PyType qualifierType = PyTypeChecker.toNonWeakType(context.getType(qualifier), context);
+        if (isInstanceType(qualifierType)) {
+          return true;
+        }
+        else if (qualifierType instanceof PyUnionType) {
+          for (PyType type : ((PyUnionType)qualifierType).getMembers()) {
+            if (isInstanceType(type)) {
+              return true;
+            }
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  private static boolean isInstanceType(@Nullable PyType type) {
+    return type instanceof PyClassType && !((PyClassType)type).isDefinition();
   }
 
   @Override
diff --git a/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java b/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java
index e4259d8..fa2b4cf 100644
--- a/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java
+++ b/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java
@@ -109,7 +109,7 @@
       final PyElementGenerator generator = PyElementGenerator.getInstance(project);
       final LanguageLevel langLevel = LanguageLevel.forElement(element1);
       final PyExpression expression = generator.createFromText(langLevel, PyAssignmentStatement.class, "z=" + selection).getAssignedValue();
-      if (PsiUtilCore.hasErrorElementChild(expression) || !(expression instanceof PyBinaryExpression)) {
+      if (!(expression instanceof PyBinaryExpression) || PsiUtilCore.hasErrorElementChild(expression)) {
         return null;
       }
       final String parentText = parent.getText();
diff --git a/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java b/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java
index f4cb9e6..2cebb3e 100644
--- a/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java
+++ b/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java
@@ -47,6 +47,15 @@
  * @author Dennis.Ushakov
  */
 public class PyReplaceExpressionUtil implements PyElementTypes {
+  /**
+   * This marker is added in cases where valid selection nevertheless breaks existing expression.
+   * It can happen in cases like (here {@code <start> and <end>} represent selection boundaries):
+   * <ul>
+   *   <li>Selection conflicts with operator precedence: {@code n = 1 * <start>2 + 3<end>}</li>
+   *   <li>Selection conflicts with operator associativity: {@code n = 1 + <start>2 + 3<end>}</li>
+   *   <li>Part of string literal is selected: {@code s = 'green <start>eggs<end> and ham'}</li>
+   * </ul>
+   */
   public static final Key<Pair<PsiElement, TextRange>> SELECTION_BREAKS_AST_NODE =
     new Key<Pair<PsiElement, TextRange>>("python.selection.breaks.ast.node");
 
diff --git a/python/src/com/jetbrains/python/refactoring/inline/PyInlineLocalHandler.java b/python/src/com/jetbrains/python/refactoring/inline/PyInlineLocalHandler.java
index a582b17..575399c 100644
--- a/python/src/com/jetbrains/python/refactoring/inline/PyInlineLocalHandler.java
+++ b/python/src/com/jetbrains/python/refactoring/inline/PyInlineLocalHandler.java
@@ -134,7 +134,7 @@
     if (editor != null && !ApplicationManager.getApplication().isUnitTestMode()) {
       highlightManager.addOccurrenceHighlights(editor, refsToInline, attributes, true, null);
       int occurrencesCount = refsToInline.length;
-      String occurencesString = RefactoringBundle.message("occurences.string", occurrencesCount);
+      String occurencesString = RefactoringBundle.message("occurrences.string", occurrencesCount);
       final String promptKey = "inline.local.variable.prompt";
       final String question = RefactoringBundle.message(promptKey, localName) + " " + occurencesString;
       RefactoringMessageDialog dialog = new RefactoringMessageDialog(REFACTORING_NAME, question, HELP_ID, "OptionPane.questionIcon", true, project);
diff --git a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java
index 29fc441..00448c8 100644
--- a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java
+++ b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java
@@ -69,6 +69,11 @@
 abstract public class IntroduceHandler implements RefactoringActionHandler {
   protected static PsiElement findAnchor(List<PsiElement> occurrences) {
     PsiElement anchor = occurrences.get(0);
+    final Pair<PsiElement, TextRange> data = anchor.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE);
+    // Search anchor in the origin file, not in dummy.py, if selection breaks statement and thus element was generated
+    if (data != null && occurrences.size() == 1) {
+      return PsiTreeUtil.getParentOfType(data.getFirst(), PyStatement.class);
+    }
     next:
     do {
       final PyStatement statement = PsiTreeUtil.getParentOfType(anchor, PyStatement.class);
@@ -192,7 +197,7 @@
     String text = expression.getText();
     final Pair<PsiElement, TextRange> selection = expression.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE);
     if (selection != null) {
-      text = selection.getSecond().substring(text);
+      text = selection.getSecond().substring(selection.getFirst().getText());
     }
     if (expression instanceof PyCallExpression) {
       final PyExpression callee = ((PyCallExpression)expression).getCallee();
diff --git a/python/src/com/jetbrains/python/refactoring/introduce/constant/PyIntroduceConstantHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/constant/PyIntroduceConstantHandler.java
index af9ff58..44fdee8 100644
--- a/python/src/com/jetbrains/python/refactoring/introduce/constant/PyIntroduceConstantHandler.java
+++ b/python/src/com/jetbrains/python/refactoring/introduce/constant/PyIntroduceConstantHandler.java
@@ -24,6 +24,7 @@
 import com.jetbrains.python.codeInsight.imports.AddImportHelper;
 import com.jetbrains.python.psi.PyExpression;
 import com.jetbrains.python.psi.PyFile;
+import com.jetbrains.python.psi.PyParameterList;
 import com.jetbrains.python.refactoring.PyReplaceExpressionUtil;
 import com.jetbrains.python.refactoring.introduce.IntroduceHandler;
 import com.jetbrains.python.refactoring.introduce.IntroduceOperation;
@@ -66,6 +67,11 @@
   }
 
   @Override
+  protected boolean isValidIntroduceContext(PsiElement element) {
+    return super.isValidIntroduceContext(element) || PsiTreeUtil.getParentOfType(element, PyParameterList.class) != null;
+  }
+
+  @Override
   protected String getHelpId() {
     return "python.reference.introduceConstant";
   }
diff --git a/python/src/com/jetbrains/python/run/PythonCommandLineState.java b/python/src/com/jetbrains/python/run/PythonCommandLineState.java
index a4d1718..220939e 100644
--- a/python/src/com/jetbrains/python/run/PythonCommandLineState.java
+++ b/python/src/com/jetbrains/python/run/PythonCommandLineState.java
@@ -87,7 +87,7 @@
   private Boolean myMultiprocessDebug = null;
 
   public boolean isDebug() {
-    return PyDebugRunner.PY_DEBUG_RUNNER.equals(getEnvironment().getRunnerId());
+    return PyDebugRunner.PY_DEBUG_RUNNER.equals(getEnvironment().getRunner().getRunnerId());
   }
 
   public static ServerSocket createServerSocket() throws ExecutionException {
@@ -171,23 +171,13 @@
    * @throws ExecutionException
    */
   protected ProcessHandler startProcess(CommandLinePatcher... patchers) throws ExecutionException {
-
-
     GeneralCommandLine commandLine = generateCommandLine(patchers);
 
     // Extend command line
-    RunnerSettings runnerSettings = getRunnerSettings();
-    String runnerId = getEnvironment().getRunnerId();
-    if (runnerId != null) {
-      PythonRunConfigurationExtensionsManager.getInstance().patchCommandLine(myConfig, runnerSettings, commandLine, runnerId);
-    }
-
+    PythonRunConfigurationExtensionsManager.getInstance().patchCommandLine(myConfig, getRunnerSettings(), commandLine, getEnvironment().getRunner().getRunnerId());
     Sdk sdk = PythonSdkType.findSdkByPath(myConfig.getInterpreterPath());
-
-
     final ProcessHandler processHandler;
     if (PySdkUtil.isRemote(sdk)) {
-      assert sdk != null;
       processHandler =
         createRemoteProcessStarter().startRemoteProcess(sdk, commandLine, myConfig.getProject(), myConfig.getMappingSettings());
     }
diff --git a/python/src/com/jetbrains/python/run/PythonRunner.java b/python/src/com/jetbrains/python/run/PythonRunner.java
index b95da7c..3007b0b 100644
--- a/python/src/com/jetbrains/python/run/PythonRunner.java
+++ b/python/src/com/jetbrains/python/run/PythonRunner.java
@@ -59,9 +59,6 @@
     else {
       executionResult = state.execute(env.getExecutor(), this);
     }
-    if (executionResult == null) return null;
-
-    final RunContentBuilder contentBuilder = new RunContentBuilder(this, executionResult, env);
-    return contentBuilder.showRunContent(contentToReuse);
+    return executionResult == null ? null : new RunContentBuilder(executionResult, env).showRunContent(contentToReuse);
   }
 }
diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkType.java b/python/src/com/jetbrains/python/sdk/PythonSdkType.java
index f194f35..58de093 100644
--- a/python/src/com/jetbrains/python/sdk/PythonSdkType.java
+++ b/python/src/com/jetbrains/python/sdk/PythonSdkType.java
@@ -910,6 +910,16 @@
     return null;
   }
 
+  @Nullable
+  public static Sdk findPython2Sdk(List<Sdk> sdks) {
+    Collections.sort(sdks, PreferredSdkComparator.INSTANCE);
+    for (Sdk sdk : sdks) {
+      if (!getLanguageLevelForSdk(sdk).isPy3K()) {
+        return sdk;
+      }
+    }
+    return null;
+  }
 
   @Nullable
   public static Sdk findLocalCPython(@Nullable Module module) {
@@ -997,7 +1007,7 @@
   public static boolean isIncompleteRemote(Sdk sdk) {
     if (PySdkUtil.isRemote(sdk)) {
       //noinspection ConstantConditions
-      if (!((PyRemoteSdkAdditionalDataBase)sdk.getSdkAdditionalData()).isInitialized()) {
+      if (!((PyRemoteSdkAdditionalDataBase)sdk.getSdkAdditionalData()).isValid()) {
         return true;
       }
     }
diff --git a/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java b/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java
index 284a125..4e4a431 100644
--- a/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java
+++ b/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java
@@ -20,7 +20,6 @@
 import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
 import com.intellij.execution.ExecutionException;
 import com.intellij.notification.Notification;
-import com.intellij.notification.NotificationListener;
 import com.intellij.notification.NotificationType;
 import com.intellij.notification.Notifications;
 import com.intellij.openapi.application.ApplicationManager;
@@ -35,6 +34,7 @@
 import com.intellij.openapi.util.Ref;
 import com.intellij.openapi.util.SystemInfo;
 import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.JarFileSystem;
 import com.intellij.openapi.vfs.LocalFileSystem;
 import com.intellij.openapi.vfs.VirtualFile;
@@ -58,7 +58,6 @@
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-import javax.swing.event.HyperlinkEvent;
 import java.awt.*;
 import java.io.*;
 import java.util.*;
@@ -164,18 +163,27 @@
       else {
         message = PyBundle.message("sdk.errorlog.$0.mods.fail.in.$1.sdks", module_errors, errors.size());
       }
-      Notifications.Bus.notify(
-        new Notification(
-          PythonSdkType.SKELETONS_TOPIC, PyBundle.message("sdk.some.skeletons.failed"), message,
-          NotificationType.WARNING,
-          new NotificationListener() {
-            @Override
-            public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) {
-              new SkeletonErrorsDialog(errors, failedSdks).setVisible(true);
-            }
-          }
-        )
-      );
+      logErrors(errors, failedSdks, message);
+    }
+  }
+
+  private static void logErrors(@NotNull final Map<String, List<String>> errors, @NotNull final List<String> failedSdks,
+                                @NotNull final String message) {
+    LOG.warn(PyBundle.message("sdk.some.skeletons.failed"));
+    LOG.warn(message);
+
+    if (failedSdks.size() > 0) {
+      LOG.warn(PyBundle.message("sdk.error.dialog.failed.sdks"));
+      LOG.warn(StringUtil.join(failedSdks, ", "));
+    }
+
+    if (errors.size() > 0) {
+      LOG.warn(PyBundle.message("sdk.error.dialog.failed.modules"));
+      for (String sdkName : errors.keySet()) {
+        for (String moduleName : errors.get(sdkName)) {
+          LOG.warn(moduleName);
+        }
+      }
     }
   }
 
diff --git a/python/src/com/jetbrains/python/sdk/skeletons/SkeletonErrorsDialog.form b/python/src/com/jetbrains/python/sdk/skeletons/SkeletonErrorsDialog.form
deleted file mode 100644
index c82d5fa..0000000
--- a/python/src/com/jetbrains/python/sdk/skeletons/SkeletonErrorsDialog.form
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.python.sdk.skeletons.SkeletonErrorsDialog">
-  <grid id="cbd77" binding="contentPane" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
-    <margin top="10" left="10" bottom="10" right="10"/>
-    <constraints>
-      <xy x="48" y="54" width="436" height="409"/>
-    </constraints>
-    <properties/>
-    <border type="none"/>
-    <children>
-      <grid id="94766" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
-        <margin top="0" left="0" bottom="0" right="0"/>
-        <constraints>
-          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
-        </constraints>
-        <properties/>
-        <border type="none"/>
-        <children>
-          <grid id="9538f" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
-            <margin top="0" left="0" bottom="0" right="0"/>
-            <constraints>
-              <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
-            </constraints>
-            <properties/>
-            <border type="none"/>
-            <children>
-              <component id="e7465" class="javax.swing.JButton" binding="buttonOK">
-                <constraints>
-                  <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
-                </constraints>
-                <properties>
-                  <text value="OK"/>
-                </properties>
-              </component>
-            </children>
-          </grid>
-          <scrollpane id="d5fbd" class="com.intellij.ui.components.JBScrollPane" binding="myScroller">
-            <constraints>
-              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
-            </constraints>
-            <properties/>
-            <border type="none"/>
-            <children>
-              <component id="10d56" class="javax.swing.JTextPane" binding="myMessagePane">
-                <constraints/>
-                <properties>
-                  <editable value="false"/>
-                  <enabled value="true"/>
-                </properties>
-                <clientProperties>
-                  <JEditorPane.honorDisplayProperties class="java.lang.Boolean" value="true"/>
-                  <caretAspectRatio class="java.lang.Float" value="0.04"/>
-                </clientProperties>
-              </component>
-            </children>
-          </scrollpane>
-        </children>
-      </grid>
-    </children>
-  </grid>
-</form>
diff --git a/python/src/com/jetbrains/python/sdk/skeletons/SkeletonErrorsDialog.java b/python/src/com/jetbrains/python/sdk/skeletons/SkeletonErrorsDialog.java
deleted file mode 100644
index f6c6b45..0000000
--- a/python/src/com/jetbrains/python/sdk/skeletons/SkeletonErrorsDialog.java
+++ /dev/null
@@ -1,94 +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.skeletons;
-
-import com.intellij.ui.components.JBScrollPane;
-import com.jetbrains.python.PyBundle;
-
-import javax.swing.*;
-import javax.swing.border.EmptyBorder;
-import java.awt.*;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.util.List;
-import java.util.Map;
-
-public class SkeletonErrorsDialog extends JDialog {
-  private JPanel contentPane;
-  private JButton buttonOK;
-  private JBScrollPane myScroller;
-  private JTextPane myMessagePane;
-
-  public SkeletonErrorsDialog(Map<String, List<String>> errors, List<String> failed_sdks) {
-    setContentPane(contentPane);
-    setModal(true);
-    getRootPane().setDefaultButton(buttonOK);
-
-    buttonOK.addActionListener(new ActionListener() {
-      public void actionPerformed(ActionEvent e) {
-        dispose();
-      }
-    });
-
-    // fill data
-    myMessagePane.setContentType("text/html");
-    myMessagePane.setBorder(new EmptyBorder(0, 0, 0, 0));
-    StringBuilder sb = new StringBuilder("<html><body style='margin: 4pt;' ");
-    final Color foreground = getParent().getForeground();
-    final Color background = getParent().getBackground();
-    if (foreground != null && background != null) {
-      sb.append("text='").append(getHTMLColor(foreground)).append("' ");
-      sb.append("bgcolor='").append(getHTMLColor(background)).append("'");
-    }
-    sb.append(">");
-
-    if (failed_sdks.size() > 0) {
-      sb.append("<h1>").append(PyBundle.message("sdk.error.dialog.failed.sdks")).append("</h1>");
-      sb.append("<ul>");
-      for (String sdk_name : failed_sdks) {
-        sb.append("<li>").append(sdk_name).append("</li>");
-      }
-      sb.append("</ul><br>");
-    }
-
-    if (errors.size() > 0) {
-      sb.append("<h1>").append(PyBundle.message("sdk.error.dialog.failed.modules")).append("</h1>");
-      for (String sdk_name : errors.keySet()) {
-        sb.append("<b>").append(sdk_name).append("</b><br>");
-        sb.append("<ul>");
-        for (String module_name : errors.get(sdk_name)) {
-          sb.append("<li>").append(module_name).append("</li>");
-        }
-        sb.append("</ul>");
-      }
-      sb.append(PyBundle.message("sdk.error.dialog.were.blacklisted"));
-    }
-
-    sb.append("</body></html>");
-    myMessagePane.setText(sb.toString());
-
-    setTitle(PyBundle.message("sdk.error.dialog.problems"));
-
-    pack();
-    setLocationRelativeTo(getParent());
-  }
-
-  private static String getHTMLColor(Color color) {
-    StringBuilder sb = new StringBuilder("#");
-    sb.append(Integer.toHexString(color.getRGB() & 0xffffff));
-    return sb.toString();
-  }
-}
diff --git a/python/src/com/jetbrains/python/testing/PyRerunFailedTestsAction.java b/python/src/com/jetbrains/python/testing/PyRerunFailedTestsAction.java
index cc468c5..0b92251 100644
--- a/python/src/com/jetbrains/python/testing/PyRerunFailedTestsAction.java
+++ b/python/src/com/jetbrains/python/testing/PyRerunFailedTestsAction.java
@@ -72,7 +72,7 @@
     public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment env) throws ExecutionException {
       final AbstractPythonRunConfiguration configuration = ((AbstractPythonRunConfiguration)getPeer());
       return new FailedPythonTestCommandLineStateBase(configuration, env,
-                                                            (PythonTestCommandLineStateBase)configuration.getState(executor, env));
+                                                      (PythonTestCommandLineStateBase)configuration.getState(executor, env));
     }
   }
 
diff --git a/python/testData/MockSdk2.7/python_stubs/__builtin__.py b/python/testData/MockSdk2.7/python_stubs/__builtin__.py
index ca174bb..5ef056b 100644
--- a/python/testData/MockSdk2.7/python_stubs/__builtin__.py
+++ b/python/testData/MockSdk2.7/python_stubs/__builtin__.py
@@ -652,6 +652,19 @@
         self.__name__ = ''
 
 
+class __method(object):
+    '''A mock class representing method type.'''
+
+    def __init__(self):
+
+        self.im_class = None
+        self.im_self = None
+        self.im_func = None
+
+        self.__func__ = None
+        self.__self__ = None
+
+
 class __namedtuple(tuple):
     '''A mock base class for named tuples.'''
 
diff --git a/python/testData/MockSdk3.2/python_stubs/builtins.py b/python/testData/MockSdk3.2/python_stubs/builtins.py
index 88b74ab..58fcaac 100644
--- a/python/testData/MockSdk3.2/python_stubs/builtins.py
+++ b/python/testData/MockSdk3.2/python_stubs/builtins.py
@@ -613,14 +613,24 @@
         self.__dict__ = ''
         self.__module__ = ''
 
-        self.__annotations__ = {}
         self.__defaults__ = {}
         self.__globals__ = {}
-        self.__kwdefaults__ = {}
         self.__closure__ = None
         self.__code__ = None
         self.__name__ = ''
 
+        self.__annotations__ = {}
+        self.__kwdefaults__ = {}
+
+
+class __method(object):
+    '''A mock class representing method type.'''
+
+    def __init__(self):
+
+        self.__func__ = None
+        self.__self__ = None
+
 
 class __namedtuple(tuple):
     '''A mock base class for named tuples.'''
diff --git a/python/testData/__init__.py b/python/testData/__init__.py
new file mode 100644
index 0000000..595e381
--- /dev/null
+++ b/python/testData/__init__.py
@@ -0,0 +1 @@
+__author__ = 'traff'
diff --git a/python/testData/addImport/localFromImport.after.py b/python/testData/addImport/localFromImport.after.py
new file mode 100644
index 0000000..92920f7
--- /dev/null
+++ b/python/testData/addImport/localFromImport.after.py
@@ -0,0 +1,6 @@
+def func():
+    for _ range(10):
+        from package.module import foo
+
+        foo
+#       <ref>
\ No newline at end of file
diff --git a/python/testData/addImport/localFromImport.py b/python/testData/addImport/localFromImport.py
new file mode 100644
index 0000000..e93e2d1
--- /dev/null
+++ b/python/testData/addImport/localFromImport.py
@@ -0,0 +1,4 @@
+def func():
+    for _ range(10):
+        foo
+#       <ref>
\ No newline at end of file
diff --git a/python/testData/addImport/localImport.after.py b/python/testData/addImport/localImport.after.py
new file mode 100644
index 0000000..883e080
--- /dev/null
+++ b/python/testData/addImport/localImport.after.py
@@ -0,0 +1,8 @@
+def func():
+    try:
+        import module
+
+        module
+#       <ref>
+    except:
+        pass
diff --git a/python/testData/addImport/localImport.py b/python/testData/addImport/localImport.py
new file mode 100644
index 0000000..9428584
--- /dev/null
+++ b/python/testData/addImport/localImport.py
@@ -0,0 +1,6 @@
+def func():
+    try:
+        module
+#       <ref>
+    except:
+        pass
diff --git a/python/testData/addImport/localImportInlineBranch.after.py b/python/testData/addImport/localImportInlineBranch.after.py
new file mode 100644
index 0000000..c7f7d38
--- /dev/null
+++ b/python/testData/addImport/localImportInlineBranch.after.py
@@ -0,0 +1,6 @@
+def func():
+    if True:
+        import module
+
+        module
+#            <ref>
\ No newline at end of file
diff --git a/python/testData/addImport/localImportInlineBranch.py b/python/testData/addImport/localImportInlineBranch.py
new file mode 100644
index 0000000..89d4935
--- /dev/null
+++ b/python/testData/addImport/localImportInlineBranch.py
@@ -0,0 +1,3 @@
+def func():
+    if True: module
+#            <ref>
\ No newline at end of file
diff --git a/python/testData/addImport/localImportInlineFunctionBody.after.py b/python/testData/addImport/localImportInlineFunctionBody.after.py
new file mode 100644
index 0000000..13d2a1e
--- /dev/null
+++ b/python/testData/addImport/localImportInlineFunctionBody.after.py
@@ -0,0 +1,5 @@
+def func():
+    import module
+
+    module
+#           <ref>
\ No newline at end of file
diff --git a/python/testData/addImport/localImportInlineFunctionBody.py b/python/testData/addImport/localImportInlineFunctionBody.py
new file mode 100644
index 0000000..b76c830
--- /dev/null
+++ b/python/testData/addImport/localImportInlineFunctionBody.py
@@ -0,0 +1,2 @@
+def func(): module
+#           <ref>
\ No newline at end of file
diff --git a/python/testData/completion/boundMethodSpecialAttributes.py b/python/testData/completion/boundMethodSpecialAttributes.py
new file mode 100644
index 0000000..02afa56
--- /dev/null
+++ b/python/testData/completion/boundMethodSpecialAttributes.py
@@ -0,0 +1,5 @@
+class MyClass(object):
+    def method(self):
+        pass
+
+MyClass().method.__<caret>
\ No newline at end of file
diff --git a/python/testData/completion/lambdaSpecialAttributes.py b/python/testData/completion/lambdaSpecialAttributes.py
new file mode 100644
index 0000000..0b0465e
--- /dev/null
+++ b/python/testData/completion/lambdaSpecialAttributes.py
@@ -0,0 +1 @@
+(lambda: 42).__<caret>
\ No newline at end of file
diff --git a/python/testData/completion/reassignedMethodSpecialAttributes.py b/python/testData/completion/reassignedMethodSpecialAttributes.py
new file mode 100644
index 0000000..faf25e0
--- /dev/null
+++ b/python/testData/completion/reassignedMethodSpecialAttributes.py
@@ -0,0 +1,6 @@
+class MyClass(object):
+    def method(self):
+        pass
+
+m = MyClass().method
+m.__<caret>
\ No newline at end of file
diff --git a/python/testData/completion/staticMethodSpecialAttributes.py b/python/testData/completion/staticMethodSpecialAttributes.py
new file mode 100644
index 0000000..84b0e75
--- /dev/null
+++ b/python/testData/completion/staticMethodSpecialAttributes.py
@@ -0,0 +1,6 @@
+class MyClass(object):
+    @staticmethod
+    def method(self):
+        pass
+
+MyClass().method.__<caret>
\ No newline at end of file
diff --git a/python/testData/completion/unboundMethodSpecialAttributes.py b/python/testData/completion/unboundMethodSpecialAttributes.py
new file mode 100644
index 0000000..b7e9b52
--- /dev/null
+++ b/python/testData/completion/unboundMethodSpecialAttributes.py
@@ -0,0 +1,5 @@
+class MyClass(object):
+    def method(self):
+        pass
+
+MyClass.method.__<caret>
\ No newline at end of file
diff --git a/python/testData/completion/weakQualifierBoundMethodAttributes.py b/python/testData/completion/weakQualifierBoundMethodAttributes.py
new file mode 100644
index 0000000..2433cfb
--- /dev/null
+++ b/python/testData/completion/weakQualifierBoundMethodAttributes.py
@@ -0,0 +1,10 @@
+class MyClass(object):
+    def method(self):
+        pass
+
+if True:
+    inst = MyClass()
+else:
+    inst = unresolved
+
+inst.method.__<caret>
\ No newline at end of file
diff --git a/python/testData/debug/Adder-0.1.egg b/python/testData/debug/Adder-0.1.egg
new file mode 100644
index 0000000..eb98b6a
--- /dev/null
+++ b/python/testData/debug/Adder-0.1.egg
Binary files differ
diff --git a/python/testData/debug/Test_Resume.py b/python/testData/debug/Test_Resume.py
new file mode 100644
index 0000000..dcec51c
--- /dev/null
+++ b/python/testData/debug/Test_Resume.py
@@ -0,0 +1,8 @@
+def foo(x):
+    print(x)
+
+foo(1)
+foo(2)
+
+
+
diff --git a/python/testData/debug/__init__.py b/python/testData/debug/__init__.py
new file mode 100644
index 0000000..595e381
--- /dev/null
+++ b/python/testData/debug/__init__.py
@@ -0,0 +1 @@
+__author__ = 'traff'
diff --git a/python/testData/debug/pycharm-debug.egg b/python/testData/debug/pycharm-debug.egg
new file mode 100644
index 0000000..5a17292
--- /dev/null
+++ b/python/testData/debug/pycharm-debug.egg
Binary files differ
diff --git a/python/testData/debug/test1.py b/python/testData/debug/test1.py
new file mode 100644
index 0000000..f654959
--- /dev/null
+++ b/python/testData/debug/test1.py
@@ -0,0 +1,5 @@
+i = 0
+while True:
+	print(i)
+	i = i + 1
+
diff --git a/python/testData/debug/test2.py b/python/testData/debug/test2.py
new file mode 100644
index 0000000..246363a
--- /dev/null
+++ b/python/testData/debug/test2.py
@@ -0,0 +1,8 @@
+def foo(x):
+    y = x + 2
+    print(y)
+
+z = 1
+foo(z)
+z += 1
+print(z)
diff --git a/python/testData/debug/test3.py b/python/testData/debug/test3.py
new file mode 100644
index 0000000..6122a53
--- /dev/null
+++ b/python/testData/debug/test3.py
@@ -0,0 +1,26 @@
+class A:
+    def __init__(self, z):
+        self.z = z
+
+    def foo(self, x):
+        y = 2 * x + self.z
+        return 1 + y
+
+
+def zoo(x):
+    y = int((x - 2) / (x - 1))
+
+    return A(y)
+
+print(zoo(2).foo(2))
+
+try:
+    try:
+        print(zoo(1).foo(2)) #we got ZeroDivision here
+    finally:
+        print(zoo(0).foo(2))
+except:
+    pass
+
+a = zoo(-1)
+print(a.foo(2))
\ No newline at end of file
diff --git a/python/testData/debug/test4.py b/python/testData/debug/test4.py
new file mode 100644
index 0000000..b8b7e91
--- /dev/null
+++ b/python/testData/debug/test4.py
@@ -0,0 +1,5 @@
+xval = 0
+xvalue1 = 1
+xvalue2 = 2
+print(xvalue1 + xvalue2)
+
diff --git a/python/testData/debug/test_continuation.py b/python/testData/debug/test_continuation.py
new file mode 100644
index 0000000..5957e3d
--- /dev/null
+++ b/python/testData/debug/test_continuation.py
@@ -0,0 +1,16 @@
+class Boo:
+    def bu(self, y):
+        return 1 + y
+
+
+class Foo:
+    def fu(self):
+        return Boo()
+
+x = 0
+print(x)
+x = Foo().fu()\
+.bu(x)
+print(x)
+x=2
+print(x)
\ No newline at end of file
diff --git a/python/testData/debug/test_continuation2.py b/python/testData/debug/test_continuation2.py
new file mode 100644
index 0000000..3b0246d
--- /dev/null
+++ b/python/testData/debug/test_continuation2.py
@@ -0,0 +1,8 @@
+x = 0
+print(x)
+x = 1+\
+2+\
+3
+print(x)
+x=2
+print(x)
\ No newline at end of file
diff --git a/python/testData/debug/test_egg.py b/python/testData/debug/test_egg.py
new file mode 100644
index 0000000..82bec29
--- /dev/null
+++ b/python/testData/debug/test_egg.py
@@ -0,0 +1,4 @@
+from adder import adder
+
+x = adder.add(7, 9)
+print(x)
\ No newline at end of file
diff --git a/python/testData/debug/test_exceptbreak.py b/python/testData/debug/test_exceptbreak.py
new file mode 100644
index 0000000..46b0ebc
--- /dev/null
+++ b/python/testData/debug/test_exceptbreak.py
@@ -0,0 +1,8 @@
+def foo(x):
+    return 1/x
+
+def zoo(x):
+    res = foo(x)
+    return res
+
+print(zoo(0))
diff --git a/python/testData/debug/test_input.py b/python/testData/debug/test_input.py
new file mode 100644
index 0000000..5ac9bac
--- /dev/null
+++ b/python/testData/debug/test_input.py
@@ -0,0 +1,7 @@
+while True:
+    promt = "print command > "
+    try:
+        string = raw_input(promt)
+    except :
+        string = input(promt)
+    print ("command was " + string)
\ No newline at end of file
diff --git a/python/testData/debug/test_multiprocess.py b/python/testData/debug/test_multiprocess.py
new file mode 100644
index 0000000..5fc6be4
--- /dev/null
+++ b/python/testData/debug/test_multiprocess.py
@@ -0,0 +1,13 @@
+from concurrent.futures import ProcessPoolExecutor
+def my_foo(arg_):
+    return arg_
+
+def main():
+    arg = ['Result:OK']
+    with ProcessPoolExecutor(1) as exec:
+        result = exec.map(my_foo, arg)
+        for i in result:
+            print(i)
+
+if __name__ == '__main__':
+    main()
\ No newline at end of file
diff --git a/python/testData/debug/test_multithread.py b/python/testData/debug/test_multithread.py
new file mode 100644
index 0000000..03c0811
--- /dev/null
+++ b/python/testData/debug/test_multithread.py
@@ -0,0 +1,24 @@
+try:
+    import thread
+except :
+    import _thread as thread
+
+import threading
+
+def bar(y):
+    z = 100 + y
+    print("Z=%d"%z)
+
+def foo(x):
+    y = x + 1
+    print("Y=%d"%y)
+
+    t = threading.Thread(target=bar, args=(y,))
+    t.start()
+
+
+id = thread.start_new_thread(foo, (1,))
+
+while True:
+    pass
+
diff --git a/python/testData/debug/test_remote.py b/python/testData/debug/test_remote.py
new file mode 100644
index 0000000..8bccbeb
--- /dev/null
+++ b/python/testData/debug/test_remote.py
@@ -0,0 +1,19 @@
+import sys
+
+if __name__ == '__main__':
+    if len(sys.argv) < 2:
+        sys.stderr.write("Not enough arguments")
+        sys.exit(1)
+
+    port = int(sys.argv[1])
+
+    x = 0
+
+    from pydev import pydevd
+    pydevd.settrace('localhost', port=port, stdoutToServer=True, stderrToServer=True)
+
+    x = 1
+    x = 2
+    x = 3
+
+    print("OK")
diff --git a/python/testData/debug/test_runtoline.py b/python/testData/debug/test_runtoline.py
new file mode 100644
index 0000000..804c56c
--- /dev/null
+++ b/python/testData/debug/test_runtoline.py
@@ -0,0 +1,8 @@
+x = 0
+print(x)
+while x<2:
+    x+=1
+    print(x)
+
+x+=10
+print("x = %d" % x)
\ No newline at end of file
diff --git a/python/testData/debug/test_stepOverCondition.py b/python/testData/debug/test_stepOverCondition.py
new file mode 100644
index 0000000..1b41c60
--- /dev/null
+++ b/python/testData/debug/test_stepOverCondition.py
@@ -0,0 +1,4 @@
+x = 1
+y = 2
+y = x + y
+print(y)
\ No newline at end of file
diff --git a/python/testData/debug/zipped_lib.zip b/python/testData/debug/zipped_lib.zip
new file mode 100644
index 0000000..fe2ac04
--- /dev/null
+++ b/python/testData/debug/zipped_lib.zip
Binary files differ
diff --git a/python/testData/dotNet/PythonLibs.dll b/python/testData/dotNet/PythonLibs.dll
new file mode 100644
index 0000000..cfdfa74
--- /dev/null
+++ b/python/testData/dotNet/PythonLibs.dll
Binary files differ
diff --git a/python/testData/dotNet/SingleNameSpace.dll b/python/testData/dotNet/SingleNameSpace.dll
new file mode 100644
index 0000000..da6dacb
--- /dev/null
+++ b/python/testData/dotNet/SingleNameSpace.dll
Binary files differ
diff --git a/python/testData/dotNet/__init__.py b/python/testData/dotNet/__init__.py
new file mode 100644
index 0000000..595e381
--- /dev/null
+++ b/python/testData/dotNet/__init__.py
@@ -0,0 +1 @@
+__author__ = 'traff'
diff --git a/python/testData/dotNet/expected.skeleton.Deep.py b/python/testData/dotNet/expected.skeleton.Deep.py
new file mode 100644
index 0000000..52abbf6
--- /dev/null
+++ b/python/testData/dotNet/expected.skeleton.Deep.py
@@ -0,0 +1,17 @@
+# encoding: utf-8
+# module SingleNameSpace.Some.Deep calls itself Deep
+# from SingleNameSpace, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
+# by generator 1.135
+# no doc
+# no imports
+
+# no functions
+# classes
+
+from object import object
+
+class WeHaveClass(object):
+    """ WeHaveClass() """
+    MyClass = None
+
+
diff --git a/python/testData/dotNet/expected.skeleton.SingleNameSpace.py b/python/testData/dotNet/expected.skeleton.SingleNameSpace.py
new file mode 100644
index 0000000..b2048fd
--- /dev/null
+++ b/python/testData/dotNet/expected.skeleton.SingleNameSpace.py
@@ -0,0 +1,22 @@
+# encoding: utf-8
+# module SingleNameSpace
+# from SingleNameSpace, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
+# by generator 1.135
+# no doc
+# no imports
+
+# no functions
+# classes
+
+from object import object
+
+class AnotherClass(object):
+    """ AnotherClass() """
+
+from object import object
+
+class MyClass(object):
+    """ MyClass() """
+
+# variables with complex values
+
diff --git a/python/testData/dotNet/expected.skeleton.java.py b/python/testData/dotNet/expected.skeleton.java.py
new file mode 100644
index 0000000..1ba6b3a
--- /dev/null
+++ b/python/testData/dotNet/expected.skeleton.java.py
@@ -0,0 +1,24 @@
+# encoding: utf-8
+# module com.just.like.java calls itself java
+# from PythonLibs, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
+# by generator 1.135
+# no doc
+# no imports
+
+# no functions
+# classes
+
+from object import object
+
+class LikeJavaClass(object):
+    """ LikeJavaClass() """
+    def likeJavaInstanceMethod(self, i):
+        """ likeJavaInstanceMethod(self: LikeJavaClass, i: int) -> int """
+        pass
+
+    @staticmethod
+    def likeJavaStaticMethod(i):
+        """ likeJavaStaticMethod(i: int) """
+        pass
+
+
diff --git a/python/testData/dotNet/import_class_from_module.py b/python/testData/dotNet/import_class_from_module.py
new file mode 100644
index 0000000..bf36d0e
--- /dev/null
+++ b/python/testData/dotNet/import_class_from_module.py
@@ -0,0 +1,6 @@
+import clr
+
+clr.AddReferenceByPartialName("PythonLibs")
+
+from c<caret>om.just.like.java import LikeJavaClass
+print LikeJavaClass
\ No newline at end of file
diff --git a/python/testData/dotNet/import_class_from_module_alias.py b/python/testData/dotNet/import_class_from_module_alias.py
new file mode 100644
index 0000000..92d82f8
--- /dev/null
+++ b/python/testData/dotNet/import_class_from_module_alias.py
@@ -0,0 +1,6 @@
+import clr
+
+clr.AddReferenceByPartialName("PythonLibs")
+
+from c<caret>om.just.like.java import LikeJavaClass as MyClass
+print MyClass
\ No newline at end of file
diff --git a/python/testData/dotNet/import_module_from_package.py b/python/testData/dotNet/import_module_from_package.py
new file mode 100644
index 0000000..c31e92e
--- /dev/null
+++ b/python/testData/dotNet/import_module_from_package.py
@@ -0,0 +1,7 @@
+import clr
+
+clr.AddReferenceByPartialName("PythonLibs")
+
+
+from c<caret>om.just.like import java
+print java.LikeJavaClass
\ No newline at end of file
diff --git a/python/testData/dotNet/import_several_classes_from_module.py b/python/testData/dotNet/import_several_classes_from_module.py
new file mode 100644
index 0000000..08ab814
--- /dev/null
+++ b/python/testData/dotNet/import_several_classes_from_module.py
@@ -0,0 +1,7 @@
+import clr
+
+clr.AddReferenceByPartialName("PythonLibs")
+
+from c<caret>om.just.like.java import LikeJavaClass
+
+print LikeJavaClass
\ No newline at end of file
diff --git a/python/testData/dotNet/import_system.py b/python/testData/dotNet/import_system.py
new file mode 100644
index 0000000..dbfc1fe
--- /dev/null
+++ b/python/testData/dotNet/import_system.py
@@ -0,0 +1,2 @@
+from S<caret>ystem.Web import AspNetHostingPermission
+print AspNetHostingPermission
\ No newline at end of file
diff --git a/python/testData/dotNet/inner_class.py b/python/testData/dotNet/inner_class.py
new file mode 100644
index 0000000..9a22cf5
--- /dev/null
+++ b/python/testData/dotNet/inner_class.py
@@ -0,0 +1,6 @@
+import clr
+
+clr.AddReferenceByPartialName("SingleNameSpace")
+
+from <caret>SingleNameSpace.Some.Deep.WeHaveClass import MyClass
+print MyClass
\ No newline at end of file
diff --git a/python/testData/dotNet/single_class.py b/python/testData/dotNet/single_class.py
new file mode 100644
index 0000000..679e947
--- /dev/null
+++ b/python/testData/dotNet/single_class.py
@@ -0,0 +1,6 @@
+import clr
+
+clr.AddReferenceByPartialName("SingleNameSpace")
+
+from S<caret>ingleNameSpace import MyClass
+print MyClass
\ No newline at end of file
diff --git a/python/testData/dotNet/testSkeleton.py b/python/testData/dotNet/testSkeleton.py
new file mode 100644
index 0000000..c501ddf
--- /dev/null
+++ b/python/testData/dotNet/testSkeleton.py
@@ -0,0 +1,6 @@
+import clr
+
+clr.AddReferenceByPartialName("PythonLibs")
+import <caret>com.just.like.java
+
+com.just.like.java.LikeJavaClass.likeJavaStaticMethod(1)
diff --git a/python/testData/dotNet/whole_namespace.py b/python/testData/dotNet/whole_namespace.py
new file mode 100644
index 0000000..b07fa0e
--- /dev/null
+++ b/python/testData/dotNet/whole_namespace.py
@@ -0,0 +1,6 @@
+import clr
+
+clr.AddReferenceByPartialName("SingleNameSpace")
+
+<caret>ingleNameSpace
+print SingleNameSpace.MyClass
\ No newline at end of file
diff --git a/python/testData/inspections/PyStringFormatInspection/expected.xml b/python/testData/inspections/PyStringFormatInspection/expected.xml
index 62301ce..25e7028 100644
--- a/python/testData/inspections/PyStringFormatInspection/expected.xml
+++ b/python/testData/inspections/PyStringFormatInspection/expected.xml
@@ -160,4 +160,20 @@
     <line>99</line>
     <description>Too few arguments for format string</description>
   </problem>
+  <problem>
+    <file>string-format.py</file>
+    <line>103</line>
+    <description>Unexpected type</description>
+  </problem>
+  <problem>
+    <file>string-format.py</file>
+    <line>104</line>
+    <description>Too few arguments for format string</description>
+  </problem>
+  <problem>
+    <file>string-format.py</file>
+    <line>105</line>
+    <description>Too many arguments for format string</description>
+  </problem>
+
 </problems>
diff --git a/python/testData/inspections/PyStringFormatInspection/src/string-format.py b/python/testData/inspections/PyStringFormatInspection/src/string-format.py
index e60e4bb..972aaed 100644
--- a/python/testData/inspections/PyStringFormatInspection/src/string-format.py
+++ b/python/testData/inspections/PyStringFormatInspection/src/string-format.py
@@ -96,4 +96,26 @@
 my_tuple = (1,2,3,4,5,6,7,8)
 print '%d, %d' % my_tuple[:7:3]
 print '%d, %d, %d' % my_tuple[:7:3]
-print '%d, %d, %d, %d' % my_tuple[:7:3]
\ No newline at end of file
+print '%d, %d, %d, %d' % my_tuple[:7:3]
+
+# PY-12801
+print '%d %s' % ((42,) + ('spam',))
+print '%d %s' % (('ham',) + ('spam',))
+print '%d %s' % ((42,) + ())
+print '%d' % ((42,) + ('spam',))
+
+# PY-11274
+import collections
+print '%(foo)s' % collections.OrderedDict(foo=None)
+
+class MyDict(collections.Mapping):
+    def __getitem__(self, key):
+        return 'spam'
+
+    def __iter__(self):
+        yield 'spam'
+
+    def __len__(self):
+        return 1
+
+print '%(foo)s' % MyDict()
diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/methodSpecialAttributes.py b/python/testData/inspections/PyUnresolvedReferencesInspection/methodSpecialAttributes.py
new file mode 100644
index 0000000..7a60e53
--- /dev/null
+++ b/python/testData/inspections/PyUnresolvedReferencesInspection/methodSpecialAttributes.py
@@ -0,0 +1,25 @@
+class MyClass(object):
+    def method(self):
+        pass
+
+    @staticmethod
+    def static_method():
+        pass
+
+
+# Unbound method still treated as __method in Python 2
+MyClass.method.__func__
+MyClass.method.<warning descr="Cannot find reference '__defaults__' in 'function'">__defaults__</warning>
+
+# Bound method with qualifier
+inst = MyClass()
+inst.method.__func__
+inst.method.<warning descr="Cannot find reference '__defaults__' in 'function'">__defaults__</warning>
+
+# Reassigned bound method without qualifier
+m = inst.method
+
+# Static method
+# This reference should be marked as unresolved, but such warnings are suppressed for methods with decorators
+inst.static_method.__func__
+inst.static_method.__defaults__
diff --git a/python/testData/refactoring/introduceConstant/fromParameterDefaultValue.after.py b/python/testData/refactoring/introduceConstant/fromParameterDefaultValue.after.py
new file mode 100644
index 0000000..3f7191e
--- /dev/null
+++ b/python/testData/refactoring/introduceConstant/fromParameterDefaultValue.after.py
@@ -0,0 +1,5 @@
+a = 1 + 2
+
+
+def func(x=a + 3):
+    pass
\ No newline at end of file
diff --git a/python/testData/refactoring/introduceConstant/fromParameterDefaultValue.py b/python/testData/refactoring/introduceConstant/fromParameterDefaultValue.py
new file mode 100644
index 0000000..507a2df
--- /dev/null
+++ b/python/testData/refactoring/introduceConstant/fromParameterDefaultValue.py
@@ -0,0 +1,2 @@
+def func(x=<selection>1 + 2</selection> + 3):
+    pass
\ No newline at end of file
diff --git a/python/testData/refactoring/introduceVariable/selectionBreaksBinaryOperator.after.py b/python/testData/refactoring/introduceVariable/selectionBreaksBinaryOperator.after.py
new file mode 100644
index 0000000..775927c
--- /dev/null
+++ b/python/testData/refactoring/introduceVariable/selectionBreaksBinaryOperator.after.py
@@ -0,0 +1,3 @@
+def foo():
+    a = 2 + 3
+    print 1 + a
\ No newline at end of file
diff --git a/python/testData/refactoring/introduceVariable/selectionBreaksBinaryOperator.py b/python/testData/refactoring/introduceVariable/selectionBreaksBinaryOperator.py
new file mode 100644
index 0000000..c5eee8c
--- /dev/null
+++ b/python/testData/refactoring/introduceVariable/selectionBreaksBinaryOperator.py
@@ -0,0 +1,2 @@
+def foo():
+    print 1 + <selection>2 + 3</selection>
\ No newline at end of file
diff --git a/python/testData/testRunner/__init__.py b/python/testData/testRunner/__init__.py
new file mode 100644
index 0000000..595e381
--- /dev/null
+++ b/python/testData/testRunner/__init__.py
@@ -0,0 +1 @@
+__author__ = 'traff'
diff --git a/python/testData/testRunner/env/__init__.py b/python/testData/testRunner/env/__init__.py
new file mode 100644
index 0000000..595e381
--- /dev/null
+++ b/python/testData/testRunner/env/__init__.py
@@ -0,0 +1 @@
+__author__ = 'traff'
diff --git a/python/testData/testRunner/env/doc/__init__.py b/python/testData/testRunner/env/doc/__init__.py
new file mode 100644
index 0000000..595e381
--- /dev/null
+++ b/python/testData/testRunner/env/doc/__init__.py
@@ -0,0 +1 @@
+__author__ = 'traff'
diff --git a/python/testData/testRunner/env/doc/test1.py b/python/testData/testRunner/env/doc/test1.py
new file mode 100644
index 0000000..3fd0252
--- /dev/null
+++ b/python/testData/testRunner/env/doc/test1.py
@@ -0,0 +1,39 @@
+def factorial(n):
+    """Return the factorial of n, an exact integer >= 0.
+
+    If the result is small enough to fit in an int, return an int.
+    Else return a long.
+
+    >>> [factorial(n) for n in range(6)]
+    [1, 1, 2, 6, 24, 120]
+    """
+
+    import math
+    if not n >= 0:
+        raise ValueError("n must be >= 0")
+    if math.floor(n) != n:
+        raise ValueError("n must be exact integer")
+    if n+1 == n:  # catch a value like 1e300
+        raise OverflowError("n too large")
+    result = 1
+    factor = 2
+    while factor <= n:
+        result *= factor
+        factor += 1
+    return result
+
+class FirstGoodTest:
+  """
+  >>> [factorial(n) for n in range(6)]
+  [1, 1, 2, 6, 24, 120]
+  """
+  def test_passes(self):
+    pass
+
+class SecondGoodTest:
+  def test_passes(self):
+    """
+    >>> [factorial(n) for n in range(6)]
+    [1, 1, 2, 6, 24, 120]
+    """
+    pass
\ No newline at end of file
diff --git a/python/testData/testRunner/env/doc/test2.py b/python/testData/testRunner/env/doc/test2.py
new file mode 100644
index 0000000..e0c4619
--- /dev/null
+++ b/python/testData/testRunner/env/doc/test2.py
@@ -0,0 +1,39 @@
+def factorial(n):
+    """Return the factorial of n, an exact integer >= 0.
+
+    If the result is small enough to fit in an int, return an int.
+    Else return a long.
+
+    >>> [factorial(n) for n in range(6)]
+    [1, 1, 2, 6, 24, 120]
+    """
+
+    import math
+    if not n >= 0:
+        raise ValueError("n must be >= 0")
+    if math.floor(n) != n:
+        raise ValueError("n must be exact integer")
+    if n+1 == n:  # catch a value like 1e300
+        raise OverflowError("n too large")
+    result = 1
+    factor = 2
+    while factor <= n:
+        result *= factor
+        factor += 1
+    return result
+
+class FirstGoodTest:
+  """
+  >>> [factorial(n) for n in range(6)]
+  [1, 1]
+  """
+  def test_passes(self):
+    pass
+
+class SecondGoodTest:
+  def test_passes(self):
+    """
+    >>> [factorial(n) for n in range(6)]
+    [1, 1, 2, 6]
+    """
+    pass
\ No newline at end of file
diff --git a/python/testData/testRunner/env/nose/__init__.py b/python/testData/testRunner/env/nose/__init__.py
new file mode 100644
index 0000000..595e381
--- /dev/null
+++ b/python/testData/testRunner/env/nose/__init__.py
@@ -0,0 +1 @@
+__author__ = 'traff'
diff --git a/python/testData/testRunner/env/nose/test1.py b/python/testData/testRunner/env/nose/test1.py
new file mode 100644
index 0000000..972270f
--- /dev/null
+++ b/python/testData/testRunner/env/nose/test1.py
@@ -0,0 +1,11 @@
+#from unittest import TestCase
+
+class TestNose:
+    def testOne(self):
+        assert 4 == 2*2
+
+    def testTwo(self):
+        assert True
+
+def testThree():
+    assert 4 == 2*2
diff --git a/python/testData/testRunner/env/nose/test2.py b/python/testData/testRunner/env/nose/test2.py
new file mode 100644
index 0000000..0a6f80e
--- /dev/null
+++ b/python/testData/testRunner/env/nose/test2.py
@@ -0,0 +1,18 @@
+#from unittest import TestCase
+
+class TestNose:
+    def testOne(self):
+        assert 5 == 2*2
+
+    def testTwo(self):
+        assert True
+
+def testThree():
+    assert 4 == 2*2
+
+def test_evens():
+    for i in range(0, 5):
+        yield check_even, i, i*3
+
+def check_even(n, nn):
+    assert n % 2 == 0 or nn % 2 == 0
\ No newline at end of file
diff --git a/python/testData/testRunner/env/pytest/__init__.py b/python/testData/testRunner/env/pytest/__init__.py
new file mode 100644
index 0000000..595e381
--- /dev/null
+++ b/python/testData/testRunner/env/pytest/__init__.py
@@ -0,0 +1 @@
+__author__ = 'traff'
diff --git a/python/testData/testRunner/env/pytest/test1.py b/python/testData/testRunner/env/pytest/test1.py
new file mode 100644
index 0000000..6501634
--- /dev/null
+++ b/python/testData/testRunner/env/pytest/test1.py
@@ -0,0 +1,9 @@
+class TestPyTest:
+    def testOne(self):
+        assert 4 == 2*2
+
+    def testTwo(self):
+        assert True
+
+def testThree():
+    assert 4 == 2*2
diff --git a/python/testData/testRunner/env/pytest/test2.py b/python/testData/testRunner/env/pytest/test2.py
new file mode 100644
index 0000000..b6c7b08
--- /dev/null
+++ b/python/testData/testRunner/env/pytest/test2.py
@@ -0,0 +1,16 @@
+class TestPyTest:
+    def testOne(self):
+        assert 5 == 2*2
+
+    def testTwo(self):
+        assert True
+
+def testThree():
+    assert 4 == 2*2
+
+def test_evens():
+    for i in range(0, 5):
+        yield check_even, i, i*3
+
+def check_even(n, nn):
+    assert n % 2 == 0 or nn % 2 == 0
\ No newline at end of file
diff --git a/python/testData/testRunner/env/unit/__init__.py b/python/testData/testRunner/env/unit/__init__.py
new file mode 100644
index 0000000..7c7597a
--- /dev/null
+++ b/python/testData/testRunner/env/unit/__init__.py
@@ -0,0 +1 @@
+__author__ = 'ktisha'
diff --git a/python/testData/testRunner/env/unit/dependentTests/__init__.py b/python/testData/testRunner/env/unit/dependentTests/__init__.py
new file mode 100644
index 0000000..595e381
--- /dev/null
+++ b/python/testData/testRunner/env/unit/dependentTests/__init__.py
@@ -0,0 +1 @@
+__author__ = 'traff'
diff --git a/python/testData/testRunner/env/unit/dependentTests/test_my_class.py b/python/testData/testRunner/env/unit/dependentTests/test_my_class.py
new file mode 100644
index 0000000..8c6a68e
--- /dev/null
+++ b/python/testData/testRunner/env/unit/dependentTests/test_my_class.py
@@ -0,0 +1,7 @@
+import unittest
+from testedCode.my_class import *
+
+class MyClassTest(unittest.TestCase):
+  def test_foo(self):
+    c = MyClass()
+    self.assertEquals("bar", c.foo())
diff --git a/python/testData/testRunner/env/unit/dependentTests/testedCode/__init__.py b/python/testData/testRunner/env/unit/dependentTests/testedCode/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/python/testData/testRunner/env/unit/dependentTests/testedCode/__init__.py
diff --git a/python/testData/testRunner/env/unit/dependentTests/testedCode/my_class.py b/python/testData/testRunner/env/unit/dependentTests/testedCode/my_class.py
new file mode 100644
index 0000000..fbf4632
--- /dev/null
+++ b/python/testData/testRunner/env/unit/dependentTests/testedCode/my_class.py
@@ -0,0 +1,4 @@
+class MyClass:
+  def foo(self):
+    return "bar"
+    
\ No newline at end of file
diff --git a/python/testData/testRunner/env/unit/subfolder/__init__.py b/python/testData/testRunner/env/unit/subfolder/__init__.py
new file mode 100644
index 0000000..595e381
--- /dev/null
+++ b/python/testData/testRunner/env/unit/subfolder/__init__.py
@@ -0,0 +1 @@
+__author__ = 'traff'
diff --git a/python/testData/testRunner/env/unit/subfolder/test2.py b/python/testData/testRunner/env/unit/subfolder/test2.py
new file mode 100644
index 0000000..b2c21b2
--- /dev/null
+++ b/python/testData/testRunner/env/unit/subfolder/test2.py
@@ -0,0 +1,7 @@
+import unittest
+
+class SubTest(unittest.TestCase):
+  def test_in_subfolder(self):
+    self.assertEquals("foo", "fo" + "o")
+
+    
\ No newline at end of file
diff --git a/python/testData/testRunner/env/unit/test1.py b/python/testData/testRunner/env/unit/test1.py
new file mode 100644
index 0000000..8b2daad
--- /dev/null
+++ b/python/testData/testRunner/env/unit/test1.py
@@ -0,0 +1,8 @@
+from unittest import TestCase
+
+class UTests(TestCase):
+    def testOne(self):
+        self.assertEqual(4, 2*2)
+
+    def testTwo(self):
+        self.assertTrue(False or True)
diff --git a/python/testData/testRunner/env/unit/test2.py b/python/testData/testRunner/env/unit/test2.py
new file mode 100644
index 0000000..c819529
--- /dev/null
+++ b/python/testData/testRunner/env/unit/test2.py
@@ -0,0 +1,11 @@
+from unittest import TestCase
+
+class UTests(TestCase):
+    def testOne(self):
+        self.assertEqual(5, 2*2)
+
+    def testTwo(self):
+        self.assertTrue(False)
+
+    def testThree(self):
+        self.assertTrue(True)
diff --git a/python/testData/testRunner/env/unit/test_file.py b/python/testData/testRunner/env/unit/test_file.py
new file mode 100644
index 0000000..9b2ed16
--- /dev/null
+++ b/python/testData/testRunner/env/unit/test_file.py
@@ -0,0 +1,12 @@
+import unittest
+
+class GoodTest(unittest.TestCase):
+  def test_passes(self):
+    self.assertEqual(2+2, 4)
+
+class BadTest(unittest.TestCase):
+  def test_fails(self):
+    self.assertEqual(2+2, 5)
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/python/testData/testRunner/env/unit/test_folder/__init__.py b/python/testData/testRunner/env/unit/test_folder/__init__.py
new file mode 100644
index 0000000..7c7597a
--- /dev/null
+++ b/python/testData/testRunner/env/unit/test_folder/__init__.py
@@ -0,0 +1 @@
+__author__ = 'ktisha'
diff --git a/python/testData/testRunner/env/unit/test_folder/test1.py b/python/testData/testRunner/env/unit/test_folder/test1.py
new file mode 100644
index 0000000..8b2daad
--- /dev/null
+++ b/python/testData/testRunner/env/unit/test_folder/test1.py
@@ -0,0 +1,8 @@
+from unittest import TestCase
+
+class UTests(TestCase):
+    def testOne(self):
+        self.assertEqual(4, 2*2)
+
+    def testTwo(self):
+        self.assertTrue(False or True)
diff --git a/python/testData/testRunner/env/unit/test_folder/test2.py b/python/testData/testRunner/env/unit/test_folder/test2.py
new file mode 100644
index 0000000..c819529
--- /dev/null
+++ b/python/testData/testRunner/env/unit/test_folder/test2.py
@@ -0,0 +1,11 @@
+from unittest import TestCase
+
+class UTests(TestCase):
+    def testOne(self):
+        self.assertEqual(5, 2*2)
+
+    def testTwo(self):
+        self.assertTrue(False)
+
+    def testThree(self):
+        self.assertTrue(True)
diff --git a/python/testSrc/com/jetbrains/env/PyEnvSufficiencyTest.java b/python/testSrc/com/jetbrains/env/PyEnvSufficiencyTest.java
new file mode 100644
index 0000000..83846a1
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/PyEnvSufficiencyTest.java
@@ -0,0 +1,51 @@
+package com.jetbrains.env;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.testFramework.UsefulTestCase;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author traff
+ */
+public class PyEnvSufficiencyTest extends PyEnvTestCase {
+  private static final List<String> BASE_TAGS =
+    ImmutableList.<String>builder().add("python3", "django", "jython", "ipython", "ipython011", "ipython012", "nose", "pytest").build();
+
+  public void testSufficiency() {
+    if (UsefulTestCase.IS_UNDER_TEAMCITY && IS_ENV_CONFIGURATION) {
+      Set<String> tags = Sets.newHashSet();
+      List<String> roots = getPythonRoots();
+      if (roots.size() == 0) {
+        return;         // not on env agent
+      }
+      for (String root : roots) {
+        tags.addAll(loadEnvTags(root));
+      }
+
+      List<String> missing = Lists.newArrayList();
+      for (String tag : necessaryTags()) {
+        if (!tags.contains(tag)) {
+          missing.add(tag);
+        }
+      }
+
+
+      assertEmpty("Agent is missing environments: " + StringUtil.join(missing, ", "), missing);
+    }
+  }
+
+  private static List<String> necessaryTags() {
+    if (SystemInfo.isWindows) {
+      return ImmutableList.<String>builder().addAll(BASE_TAGS).add("iron").build();
+    }
+    else {
+      return ImmutableList.<String>builder().addAll(BASE_TAGS).add("packaging").build();
+    }
+  }
+}
diff --git a/python/testSrc/com/jetbrains/env/PyEnvTaskRunner.java b/python/testSrc/com/jetbrains/env/PyEnvTaskRunner.java
new file mode 100644
index 0000000..baabd5a
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/PyEnvTaskRunner.java
@@ -0,0 +1,112 @@
+package com.jetbrains.env;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.jetbrains.python.sdk.PythonSdkType;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author traff
+ */
+public class PyEnvTaskRunner {
+  private final List<String> myRoots;
+
+  public PyEnvTaskRunner(List<String> roots) {
+    myRoots = roots;
+  }
+
+  public void runTask(PyTestTask testTask, String testName) {
+    boolean wasExecuted = false;
+
+    List<String> passedRoots = Lists.newArrayList();
+
+    for (String root : myRoots) {
+
+      if (!isSuitableForTask(PyEnvTestCase.loadEnvTags(root), testTask) || !shouldRun(root, testTask)) {
+        continue;
+      }
+
+      try {
+        testTask.setUp(testName);
+        wasExecuted = true;
+        if (isJython(root)) {
+          testTask.useLongTimeout();
+        }
+        else {
+          testTask.useNormalTimeout();
+        }
+        final String executable = getExecutable(root, testTask);
+        if (executable == null) {
+          throw new RuntimeException("Cannot find Python interpreter in " + root);
+        }
+        testTask.runTestOn(executable);
+        passedRoots.add(root);
+      }
+      catch (Throwable e) {
+        throw new RuntimeException(
+          PyEnvTestCase.joinStrings(passedRoots, "Tests passed environments: ") + "Test failed on " + getEnvType() + " environment " + root,
+          e);
+      }
+      finally {
+        try {
+          testTask.tearDown();
+        }
+        catch (Exception e) {
+          throw new RuntimeException("Couldn't tear down task", e);
+        }
+      }
+    }
+
+    if (!wasExecuted) {
+      throw new RuntimeException("test" +
+                                 testName +
+                                 " was not executed.\n" +
+                                 PyEnvTestCase.joinStrings(myRoots, "All roots: ") +
+                                 "\n" +
+                                 PyEnvTestCase.joinStrings(testTask.getTags(), "Required tags in tags.txt in root: "));
+    }
+  }
+
+  protected boolean shouldRun(String root, PyTestTask task) {
+    return true;
+  }
+
+  protected String getExecutable(String root, PyTestTask testTask) {
+    return PythonSdkType.getPythonExecutable(root);
+  }
+
+  protected String getEnvType() {
+    return "local";
+  }
+
+  private static boolean isSuitableForTask(List<String> tags, PyTestTask task) {
+    return isSuitableForTags(tags, task.getTags());
+  }
+
+  public static boolean isSuitableForTags(List<String> envTags, Set<String> taskTags) {
+    Set<String> necessaryTags = Sets.newHashSet(taskTags);
+
+    for (String tag : envTags) {
+      necessaryTags.remove(tag.trim());
+    }
+
+    for (String tag : taskTags) {
+      if (tag.startsWith("-")) { //do not run on envs with that tag
+        if (envTags.contains(tag.substring(1))) {
+          return false;
+        }
+        necessaryTags.remove(tag);
+      }
+    }
+
+    return necessaryTags.isEmpty();
+  }
+
+
+  protected static boolean isJython(@NotNull String sdkHome) {
+    return sdkHome.toLowerCase().contains("jython");
+  }
+}
diff --git a/python/testSrc/com/jetbrains/env/PyEnvTestCase.java b/python/testSrc/com/jetbrains/env/PyEnvTestCase.java
new file mode 100644
index 0000000..853a550
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/PyEnvTestCase.java
@@ -0,0 +1,201 @@
+package com.jetbrains.env;
+
+import com.google.common.collect.Lists;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.testFramework.UsefulTestCase;
+import com.intellij.util.SystemProperties;
+import com.intellij.util.ui.UIUtil;
+import com.jetbrains.python.fixtures.PyTestCase;
+import org.hamcrest.Matchers;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Assume;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * @author traff
+ */
+public abstract class PyEnvTestCase extends UsefulTestCase {
+  private static final Logger LOG = Logger.getInstance(PyEnvTestCase.class.getName());
+
+  private static final String TAGS_FILE = "tags.txt";
+  private static final String PYCHARM_PYTHON_ENVS = "PYCHARM_PYTHON_ENVS";
+  private static final String PYCHARM_PYTHON_VIRTUAL_ENVS = "PYCHARM_PYTHON_VIRTUAL_ENVS";
+
+  protected static final boolean IS_ENV_CONFIGURATION = System.getProperty("pycharm.env") != null;
+
+
+  public static final boolean RUN_REMOTE = SystemProperties.getBooleanProperty("pycharm.run_remote", false);
+
+  public static final boolean RUN_LOCAL = SystemProperties.getBooleanProperty("pycharm.run_local", true);
+
+  /**
+   * Tags that should exist between all tags, available on all interpreters for test to run.
+   * See {@link #PyEnvTestCase(String...)}
+   */
+  @Nullable
+  private final String[] myRequiredTags;
+
+  /**
+   * @param requiredTags tags that should exist on some interpreter for this test to run.
+   *                     if some of these tags do not exist on any interpreter, all test methods would be skipped using
+   *                     {@link org.junit.Assume}.
+   *                     See <a href="http://junit.sourceforge.net/javadoc/org/junit/Assume.html">Assume manual</a>.
+   *                     Check [IDEA-122939] and [TW-25043] as well.
+   */
+  @SuppressWarnings("JUnitTestCaseWithNonTrivialConstructors")
+  protected PyEnvTestCase(@NotNull final String... requiredTags) {
+    myRequiredTags = requiredTags.length > 0 ? requiredTags.clone() : null;
+
+    PyTestCase.initPlatformPrefix();
+  }
+
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+    if (myRequiredTags != null) { // Ensure all tags exist between available interpreters
+      Assume.assumeThat(
+        "Can't find some tags between all available interpreter, test (all methods) will be skipped",
+        getAvailableTags(),
+        Matchers.hasItems(myRequiredTags)
+      );
+    }
+  }
+
+  /**
+   * @return all tags available between all interpreters
+   */
+  @NotNull
+  private static Collection<String> getAvailableTags() {
+    final Collection<String> allAvailableTags = new HashSet<String>();
+    for (final String pythonRoot : getPythonRoots()) {
+      allAvailableTags.addAll(loadEnvTags(pythonRoot));
+    }
+    return allAvailableTags;
+  }
+
+  @Override
+  protected void invokeTestRunnable(@NotNull final Runnable runnable) throws Exception {
+    if (runInWriteAction()) {
+      UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+        public void run() {
+          ApplicationManager.getApplication().runWriteAction(runnable);
+        }
+      });
+    }
+    else {
+      runnable.run();
+    }
+  }
+
+  @Override
+  protected boolean runInDispatchThread() {
+    return false;
+  }
+
+  protected boolean runInWriteAction() {
+    return false;
+  }
+
+  public void runPythonTest(final PyTestTask testTask) {
+    runTest(testTask, getTestName(false));
+  }
+
+  public void runTest(@NotNull PyTestTask testTask, @NotNull String testName) {
+    if (notEnvConfiguration()) {
+      fail("Running under teamcity but not by Env configuration. Skipping.");
+      return;
+    }
+
+    List<String> roots = getPythonRoots();
+
+    if (roots.size() == 0) {
+      String msg = testName +
+                   ": environments are not defined. Skipping. \nSpecify either " +
+                   PYCHARM_PYTHON_ENVS +
+                   " or " +
+                   PYCHARM_PYTHON_VIRTUAL_ENVS +
+                   " environment variable.";
+      LOG.warn(msg);
+      System.out.println(msg);
+      return;
+    }
+
+    doRunTests(testTask, testName, roots);
+  }
+
+  protected void doRunTests(PyTestTask testTask, String testName, List<String> roots) {
+    if (RUN_LOCAL) {
+      PyEnvTaskRunner taskRunner = new PyEnvTaskRunner(roots);
+
+      taskRunner.runTask(testTask, testName);
+    }
+  }
+
+
+  public static boolean notEnvConfiguration() {
+    return UsefulTestCase.IS_UNDER_TEAMCITY && !IS_ENV_CONFIGURATION;
+  }
+
+  public static List<String> getPythonRoots() {
+    List<String> roots = Lists.newArrayList();
+
+    String envs = System.getenv(PYCHARM_PYTHON_ENVS);
+    if (envs != null) {
+      roots.addAll(Lists.newArrayList(envs.split(File.pathSeparator)));
+    }
+
+    String virtualEnvs = System.getenv(PYCHARM_PYTHON_VIRTUAL_ENVS);
+
+    if (virtualEnvs != null) {
+      roots.addAll(readVirtualEnvRoots(virtualEnvs));
+    }
+    return roots;
+  }
+
+  protected static List<String> readVirtualEnvRoots(@NotNull String envs) {
+    List<String> result = Lists.newArrayList();
+    String[] roots = envs.split(File.pathSeparator);
+    for (String root : roots) {
+      File virtualEnvRoot = new File(root);
+      File[] virtualenvs = virtualEnvRoot.listFiles();
+      if (virtualenvs != null) {
+        for (File f : virtualenvs) {
+          result.add(f.getAbsolutePath());
+        }
+      } else {
+        LOG.error(root + " is not a directory of doesn't exist");
+      }
+    }
+
+    return result;
+  }
+
+  public static List<String> loadEnvTags(String env) {
+    List<String> envTags;
+
+    try {
+      File parent = new File(env);
+      if (parent.isFile()) {
+        parent = parent.getParentFile();
+      }
+      envTags = com.intellij.openapi.util.io.FileUtil.loadLines(new File(parent, TAGS_FILE));
+    }
+    catch (IOException e) {
+      envTags = Lists.newArrayList();
+    }
+    return envTags;
+  }
+
+  public static String joinStrings(Collection<String> roots, String rootsName) {
+    return roots.size() > 0 ? rootsName + StringUtil.join(roots, ", ") + "\n" : "";
+  }
+}
+
diff --git a/python/testSrc/com/jetbrains/env/PyExecutionFixtureTestTask.java b/python/testSrc/com/jetbrains/env/PyExecutionFixtureTestTask.java
new file mode 100644
index 0000000..bdb426d
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/PyExecutionFixtureTestTask.java
@@ -0,0 +1,174 @@
+package com.jetbrains.env;
+
+import com.google.common.collect.Lists;
+import com.intellij.execution.process.ProcessHandler;
+import com.intellij.ide.util.projectWizard.EmptyModuleBuilder;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.module.ModuleTypeManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.testFramework.LightProjectDescriptor;
+import com.intellij.testFramework.builders.ModuleFixtureBuilder;
+import com.intellij.testFramework.fixtures.*;
+import com.intellij.testFramework.fixtures.impl.ModuleFixtureBuilderImpl;
+import com.intellij.testFramework.fixtures.impl.ModuleFixtureImpl;
+import com.intellij.util.ui.UIUtil;
+import com.jetbrains.python.PythonModuleTypeBase;
+import com.jetbrains.python.PythonTestUtil;
+import com.jetbrains.python.sdk.InvalidSdkException;
+import com.jetbrains.python.sdkTools.PyTestSdkTools;
+import com.jetbrains.python.sdkTools.SdkCreationType;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Assert;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author traff
+ */
+public abstract class PyExecutionFixtureTestTask extends PyTestTask {
+  public static final int NORMAL_TIMEOUT = 30000;
+  public static final int LONG_TIMEOUT = 120000;
+  protected int myTimeout = NORMAL_TIMEOUT;
+  protected CodeInsightTestFixture myFixture;
+
+  public Project getProject() {
+    return myFixture.getProject();
+  }
+
+  public void useNormalTimeout() {
+    myTimeout = NORMAL_TIMEOUT;
+  }
+
+  public void useLongTimeout() {
+    myTimeout = LONG_TIMEOUT;
+  }
+
+  public void setUp(final String testName) throws Exception {
+    initFixtureBuilder();
+
+    final TestFixtureBuilder<IdeaProjectTestFixture> fixtureBuilder = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(
+      testName);
+
+    myFixture = IdeaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(fixtureBuilder.getFixture());
+
+    UIUtil.invokeAndWaitIfNeeded(
+      new Runnable() {
+        @Override
+        public void run() {
+          ModuleFixtureBuilder moduleFixtureBuilder = fixtureBuilder.addModule(MyModuleFixtureBuilder.class);
+          moduleFixtureBuilder.addSourceContentRoot(myFixture.getTempDirPath());
+          moduleFixtureBuilder.addSourceContentRoot(getTestDataPath());
+          final List<String> contentRoots = getContentRoots();
+          for (String contentRoot : contentRoots) {
+            moduleFixtureBuilder.addContentRoot(getTestDataPath() + contentRoot);
+          }
+        }
+      }
+    );
+
+
+    myFixture.setUp();
+    myFixture.setTestDataPath(getTestDataPath());
+  }
+
+  protected List<String> getContentRoots() {
+    return Lists.newArrayList();
+  }
+
+  protected String getTestDataPath() {
+    return PythonTestUtil.getTestDataPath();
+  }
+
+  protected void initFixtureBuilder() {
+    IdeaTestFixtureFactory.getFixtureFactory().registerFixtureBuilder(MyModuleFixtureBuilder.class, MyModuleFixtureBuilderImpl.class);
+  }
+
+  public void tearDown() throws Exception {
+    if (myFixture != null) {
+      myFixture.tearDown();
+      myFixture = null;
+    }
+  }
+
+  @Nullable
+  protected LightProjectDescriptor getProjectDescriptor() {
+    return null;
+  }
+
+  protected void disposeProcess(ProcessHandler h) throws InterruptedException {
+    h.destroyProcess();
+    if (!waitFor(h)) {
+      new Throwable("Can't stop process").printStackTrace();
+    }
+  }
+
+  protected boolean waitFor(ProcessHandler p) throws InterruptedException {
+    return p.waitFor(myTimeout);
+  }
+
+  protected boolean waitFor(@NotNull Semaphore s) throws InterruptedException {
+    return waitFor(s, myTimeout);
+  }
+
+  protected static boolean waitFor(@NotNull Semaphore s, long timeout) throws InterruptedException {
+    return s.tryAcquire(timeout, TimeUnit.MILLISECONDS);
+  }
+
+  public static class MyModuleFixtureBuilderImpl extends ModuleFixtureBuilderImpl<ModuleFixture> implements MyModuleFixtureBuilder {
+    public MyModuleFixtureBuilderImpl(TestFixtureBuilder<? extends IdeaProjectTestFixture> fixtureBuilder) {
+      super(new PlatformPythonModuleType(), fixtureBuilder);
+    }
+
+    @Override
+    protected ModuleFixture instantiateFixture() {
+      return new ModuleFixtureImpl(this);
+    }
+  }
+
+  public static class PlatformPythonModuleType extends PythonModuleTypeBase<EmptyModuleBuilder> {
+    @NotNull
+    public static PlatformPythonModuleType getInstance() {
+      return (PlatformPythonModuleType)ModuleTypeManager.getInstance().findByID(PYTHON_MODULE);
+    }
+
+
+    @NotNull
+    @Override
+    public EmptyModuleBuilder createModuleBuilder() {
+      return new EmptyModuleBuilder() {
+        @Override
+        public ModuleType getModuleType() {
+          return getInstance();
+        }
+      };
+    }
+  }
+
+
+  /**
+   * Creates SDK by its path
+   *
+   * @param sdkHome         path to sdk (probably obtained by {@link #runTestOn(String)})
+   * @param sdkCreationType SDK creation strategy (see {@link com.jetbrains.python.sdkTools.SdkCreationType} doc)
+   * @return sdk
+   */
+  @NotNull
+  protected Sdk createTempSdk(@NotNull final String sdkHome, @NotNull final SdkCreationType sdkCreationType)
+    throws InvalidSdkException, IOException {
+    final VirtualFile sdkHomeFile = LocalFileSystem.getInstance().findFileByPath(sdkHome);
+    Assert.assertNotNull("Interpreter file not found: " + sdkHome, sdkHomeFile);
+    return PyTestSdkTools.createTempSdk(sdkHomeFile, sdkCreationType, myFixture.getModule());
+  }
+
+
+  public interface MyModuleFixtureBuilder extends ModuleFixtureBuilder<ModuleFixture> {
+
+  }
+}
diff --git a/python/testSrc/com/jetbrains/env/PyTestTask.java b/python/testSrc/com/jetbrains/env/PyTestTask.java
new file mode 100644
index 0000000..bfc1a68
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/PyTestTask.java
@@ -0,0 +1,81 @@
+package com.jetbrains.env;
+
+import com.google.common.collect.Sets;
+import com.intellij.openapi.util.io.FileUtil;
+
+import java.util.Set;
+
+/**
+ * @author traff
+ */
+public abstract class PyTestTask {
+  private String myWorkingFolder;
+  private String myScriptName;
+  private String myScriptParameters;
+
+  public void setWorkingFolder(String workingFolder) {
+    myWorkingFolder = workingFolder;
+  }
+
+  public void setScriptName(String scriptName) {
+    myScriptName = scriptName;
+  }
+
+  public void setScriptParameters(String scriptParameters) {
+    myScriptParameters = scriptParameters;
+  }
+
+  public void setUp(String testName) throws Exception {
+  }
+
+  public void tearDown() throws Exception {
+  }
+
+  /**
+   * Run test on certain SDK path.
+   * To create SDK from path, use {@link PyExecutionFixtureTestTask#createTempSdk(String, com.jetbrains.python.sdkTools.SdkCreationType)}
+   *
+   * @param sdkHome sdk path
+   */
+  public abstract void runTestOn(String sdkHome) throws Exception;
+
+  public void before() throws Exception {
+  }
+
+  public void testing() throws Exception {
+  }
+
+  public void after() throws Exception {
+  }
+
+  public void useNormalTimeout() {
+  }
+
+  public void useLongTimeout() {
+  }
+
+  public String getScriptName() {
+    return myScriptName;
+  }
+
+  public String getScriptPath() {
+    return getFilePath(myScriptName);
+  }
+
+  public String getFilePath(String scriptName) {
+    return FileUtil
+      .toSystemDependentName(myWorkingFolder.endsWith("/") ? myWorkingFolder + scriptName : myWorkingFolder + "/" + scriptName);
+  }
+
+  public String getScriptParameters() {
+    return myScriptParameters;
+  }
+
+  public String getWorkingFolder() {
+    return myWorkingFolder;
+  }
+
+  public Set<String> getTags() {
+    return Sets.newHashSet();
+  }
+}
diff --git a/python/testSrc/com/jetbrains/env/python/IPythonConsoleTest.java b/python/testSrc/com/jetbrains/env/python/IPythonConsoleTest.java
new file mode 100644
index 0000000..a039344
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/python/IPythonConsoleTest.java
@@ -0,0 +1,100 @@
+package com.jetbrains.env.python;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableSet;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiErrorElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.jetbrains.env.PyEnvTestCase;
+import com.jetbrains.env.python.console.PyConsoleTask;
+import org.hamcrest.Matchers;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Assert;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * @author traff
+ */
+public class IPythonConsoleTest extends PyEnvTestCase {
+  public void testQuestion() throws Exception {
+    runPythonTest(new IPythonTask() {
+      @Override
+      public void testing() throws Exception {
+        exec("import multiprocessing");
+        exec("multiprocessing?");
+        waitForOutput("Type:", "module");
+      }
+    });
+  }
+
+  public void testParsing() throws Exception {
+    runPythonTest(new IPythonTask() {
+      @Override
+      public void testing() throws Exception {
+        waitForReady();
+        addTextToEditor("sys?");
+        ApplicationManager.getApplication().runReadAction(new Runnable() {
+          @Override
+          public void run() {
+            PsiFile psi =
+              PsiDocumentManager.getInstance(getProject())
+                .getPsiFile(getConsoleView().getLanguageConsole().getConsoleEditor().getDocument());
+            Assert.assertThat("No errors expected", getErrors(psi), Matchers.empty());
+          }
+        });
+      }
+    });
+  }
+
+  @NotNull
+  private static Collection<String> getErrors(PsiFile psi) { //TODO: NotNull?
+    if (!PsiTreeUtil.hasErrorElements(psi)) {
+      return Collections.emptyList();
+    }
+
+    return Collections2.transform(PsiTreeUtil.findChildrenOfType(psi, PsiErrorElement.class), new Function<PsiErrorElement, String>() {
+      @Override
+      public String apply(PsiErrorElement input) {
+        return input.getErrorDescription();
+      }
+    });
+  }
+
+  public void testParsingNoIPython() throws Exception {
+    runPythonTest(new IPythonTask() {
+      @Override
+      public void testing() throws Exception {
+        waitForReady();
+        addTextToEditor("sys?");
+        ApplicationManager.getApplication().runReadAction(new Runnable() {
+          @Override
+          public void run() {
+            PsiFile psi =
+              PsiDocumentManager.getInstance(getProject()).getPsiFile(getConsoleView().getLanguageConsole().getConsoleEditor().getDocument());
+            //TreeUtil.ensureParsed(psi.getNode());
+            assertTrue(PsiTreeUtil.hasErrorElements(psi));
+          }
+        });
+
+      }
+
+      @Override
+      public Set<String> getTags() {
+        return ImmutableSet.of("-ipython");
+      }
+    });
+  }
+
+  private static class IPythonTask extends PyConsoleTask {
+    @Override
+    public Set<String> getTags() {
+      return ImmutableSet.of("ipython");
+    }
+  }
+}
diff --git a/python/testSrc/com/jetbrains/env/python/PyPackageRequirementsInspectionTest.java b/python/testSrc/com/jetbrains/env/python/PyPackageRequirementsInspectionTest.java
new file mode 100644
index 0000000..8e3b75d
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/python/PyPackageRequirementsInspectionTest.java
@@ -0,0 +1,83 @@
+package com.jetbrains.env.python;
+
+import com.google.common.collect.ImmutableSet;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.ModuleRootModificationUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.testFramework.PsiTestUtil;
+import com.jetbrains.env.PyEnvTestCase;
+import com.jetbrains.env.PyExecutionFixtureTestTask;
+import com.jetbrains.python.PythonTestUtil;
+import com.jetbrains.python.inspections.PyPackageRequirementsInspection;
+import com.jetbrains.python.sdkTools.SdkCreationType;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Set;
+
+/**
+ * @author vlan
+ */
+public class PyPackageRequirementsInspectionTest extends PyEnvTestCase {
+  public static final ImmutableSet<String> TAGS = ImmutableSet.of("requirements");
+
+  public void testPartiallySatisfiedRequirementsTxt() {
+    doTest("test1.py");
+  }
+
+  public void testPartiallySatisfiedSetupPy() {
+    doTest("test1.py");
+  }
+
+  public void testImportsNotInRequirementsTxt() {
+    doTest("test1.py");
+  }
+
+  public void testDuplicateInstallAndTests() {
+    doTest("test1.py");
+  }
+
+  private void doTest(@NotNull final String filename) {
+    final String dir = getTestName(false);
+    runPythonTest(new PyExecutionFixtureTestTask() {
+      @Override
+      protected String getTestDataPath() {
+        return PythonTestUtil.getTestDataPath() + "/inspections/PyPackageRequirementsInspection";
+      }
+
+      @Override
+      public void runTestOn(String sdkHome) throws Exception {
+        myFixture.enableInspections(PyPackageRequirementsInspection.class);
+        final Sdk sdk = createTempSdk(sdkHome, SdkCreationType.SDK_PACKAGES_ONLY);
+        final String perSdkDir = Integer.toHexString(System.identityHashCode(sdk));
+        final VirtualFile root = myFixture.copyDirectoryToProject(dir, perSdkDir);
+        assertNotNull(root);
+        final Module module = myFixture.getModule();
+        setupModuleSdk(module, sdk, root);
+        try {
+          final VirtualFile file = root.findFileByRelativePath(filename);
+          assertNotNull(file);
+          edt(new Runnable() {
+            @Override
+            public void run() {
+              myFixture.testHighlighting(true, true, true, file);
+            }
+          });
+        }
+        finally {
+          PsiTestUtil.removeAllRoots(module, sdk);
+        }
+      }
+
+      @Override
+      public Set<String> getTags() {
+        return TAGS;
+      }
+    });
+  }
+
+  private static void setupModuleSdk(@NotNull Module module, @NotNull Sdk sdk, @NotNull VirtualFile root) {
+    ModuleRootModificationUtil.setModuleSdk(module, sdk);
+    PsiTestUtil.addContentRoot(module, root);
+  }
+}
diff --git a/python/testSrc/com/jetbrains/env/python/PyPackagingTest.java b/python/testSrc/com/jetbrains/env/python/PyPackagingTest.java
new file mode 100644
index 0000000..54afb70
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/python/PyPackagingTest.java
@@ -0,0 +1,164 @@
+package com.jetbrains.env.python;
+
+import com.google.common.collect.Sets;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.testFramework.UsefulTestCase;
+import com.jetbrains.env.PyEnvTestCase;
+import com.jetbrains.env.PyExecutionFixtureTestTask;
+import com.jetbrains.env.PyTestTask;
+import com.jetbrains.python.packaging.*;
+import com.jetbrains.python.psi.LanguageLevel;
+import com.jetbrains.python.sdk.PythonSdkType;
+import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
+import com.jetbrains.python.sdk.flavors.VirtualEnvSdkFlavor;
+import com.jetbrains.python.sdkTools.SdkCreationType;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author vlan
+ */
+public class PyPackagingTest extends PyEnvTestCase {
+  @Override
+  public void runPythonTest(PyTestTask testTask) {
+    if (UsefulTestCase.IS_UNDER_TEAMCITY && SystemInfo.isWindows) {
+      return; //Don't run under Windows as after deleting from created virtualenvs original interpreter got spoiled
+    }
+
+    super.runPythonTest(testTask);
+  }
+
+  public void testGetPackages() {
+    runPythonTest(new PyPackagingTestTask() {
+      @Override
+      public void runTestOn(String sdkHome) throws Exception {
+        final Sdk sdk = createTempSdk(sdkHome, SdkCreationType.EMPTY_SDK);
+        List<PyPackage> packages = null;
+        try {
+          packages = ((PyPackageManagerImpl)PyPackageManager.getInstance(sdk)).getPackages();
+        }
+        catch (PyExternalProcessException e) {
+          final int retcode = e.getRetcode();
+          if (retcode != PyPackageManagerImpl.ERROR_NO_PIP && retcode != PyPackageManagerImpl.ERROR_NO_SETUPTOOLS) {
+            fail(String.format("Error for interpreter '%s': %s", sdk.getHomePath(), e.getMessage()));
+          }
+        }
+        if (packages != null) {
+          assertTrue(packages.size() > 0);
+          for (PyPackage pkg : packages) {
+            assertTrue(pkg.getName().length() > 0);
+          }
+        }
+      }
+    });
+  }
+
+  public void testCreateVirtualEnv() {
+    runPythonTest(new PyPackagingTestTask() {
+      @Override
+      public void runTestOn(String sdkHome) throws Exception {
+        final Sdk sdk = createTempSdk(sdkHome, SdkCreationType.EMPTY_SDK);
+        try {
+          final LanguageLevel languageLevel = PythonSdkType.getLanguageLevelForSdk(sdk);
+          // virtualenv >= 0.10 supports Python >= 2.6
+          if (languageLevel.isOlderThan(LanguageLevel.PYTHON26)) {
+            return;
+          }
+          final File tempDir = FileUtil.createTempDirectory(getTestName(false), null);
+          final File venvDir = new File(tempDir, "venv");
+          final String venvSdkHome = ((PyPackageManagerImpl)PyPackageManagerImpl.getInstance(sdk)).createVirtualEnv(venvDir.toString(),
+                                                                                                                    false);
+          final Sdk venvSdk = createTempSdk(venvSdkHome, SdkCreationType.EMPTY_SDK);
+          assertNotNull(venvSdk);
+          assertTrue(PythonSdkType.isVirtualEnv(venvSdk));
+          assertInstanceOf(PythonSdkFlavor.getPlatformIndependentFlavor(venvSdk.getHomePath()), VirtualEnvSdkFlavor.class);
+          final List<PyPackage> packages = ((PyPackageManagerImpl)PyPackageManagerImpl.getInstance(venvSdk)).getPackages();
+          final PyPackage setuptools = findPackage("setuptools", packages);
+          assertNotNull(setuptools);
+          assertEquals("setuptools", setuptools.getName());
+          assertEquals(PyPackageManagerImpl.SETUPTOOLS_VERSION, setuptools.getVersion());
+          final PyPackage pip = findPackage("pip", packages);
+          assertNotNull(pip);
+          assertEquals("pip", pip.getName());
+          assertEquals(PyPackageManagerImpl.PIP_VERSION, pip.getVersion());
+        }
+        catch (IOException e) {
+          throw new RuntimeException(e);
+        }
+        catch (PyExternalProcessException e) {
+          throw new RuntimeException(String.format("Error for interpreter '%s': %s", sdk.getHomePath(), e.getMessage()), e);
+        }
+      }
+    });
+  }
+
+  public void testInstallPackage() {
+    runPythonTest(new PyPackagingTestTask() {
+
+      @Override
+      public void runTestOn(String sdkHome) throws Exception {
+        final Sdk sdk = createTempSdk(sdkHome, SdkCreationType.EMPTY_SDK);
+        try {
+          final File tempDir = FileUtil.createTempDirectory(getTestName(false), null);
+          final File venvDir = new File(tempDir, "venv");
+          final String venvSdkHome = ((PyPackageManagerImpl)PyPackageManager.getInstance(sdk)).createVirtualEnv(venvDir.getPath(), false);
+          final Sdk venvSdk = createTempSdk(venvSdkHome, SdkCreationType.EMPTY_SDK);
+          assertNotNull(venvSdk);
+          final PyPackageManagerImpl manager = (PyPackageManagerImpl)PyPackageManager.getInstance(venvSdk);
+          final List<PyPackage> packages1 = manager.getPackages();
+          // TODO: Install Markdown from a local file
+          manager.install(list(PyRequirement.fromString("Markdown<2.2"),
+                               new PyRequirement("httplib2")), Collections.<String>emptyList());
+          final List<PyPackage> packages2 = manager.getPackages();
+          final PyPackage markdown2 = findPackage("Markdown", packages2);
+          assertNotNull(markdown2);
+          assertTrue(markdown2.isInstalled());
+          final PyPackage pip1 = findPackage("pip", packages1);
+          assertNotNull(pip1);
+          assertEquals("pip", pip1.getName());
+          assertEquals(PyPackageManagerImpl.PIP_VERSION, pip1.getVersion());
+          manager.uninstall(list(pip1));
+          final List<PyPackage> packages3 = manager.getPackages();
+          final PyPackage pip2 = findPackage("pip", packages3);
+          assertNull(pip2);
+        }
+        catch (PyExternalProcessException e) {
+          new RuntimeException(String.format("Error for interpreter '%s': %s", sdk.getHomePath(), e.getMessage()), e);
+        }
+        catch (IOException e) {
+          throw new RuntimeException(e);
+        }
+      }
+    });
+  }
+
+  @Nullable
+  private static PyPackage findPackage(String name, List<PyPackage> packages) {
+    for (PyPackage pkg : packages) {
+      if (name.equals(pkg.getName())) {
+        return pkg;
+      }
+    }
+    return null;
+  }
+
+  private static <T> List<T> list(T... xs) {
+    return Arrays.asList(xs);
+  }
+
+
+  private abstract static class PyPackagingTestTask extends PyExecutionFixtureTestTask {
+    @Override
+    public Set<String> getTags() {
+      return Sets.newHashSet("packaging");
+    }
+  }
+}
diff --git a/python/testSrc/com/jetbrains/env/python/PythonConsoleTest.java b/python/testSrc/com/jetbrains/env/python/PythonConsoleTest.java
new file mode 100644
index 0000000..4134b16
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/python/PythonConsoleTest.java
@@ -0,0 +1,130 @@
+package com.jetbrains.env.python;
+
+import com.google.common.collect.Sets;
+import com.jetbrains.env.PyEnvTestCase;
+import com.jetbrains.env.python.console.PyConsoleTask;
+import org.junit.Assert;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author traff
+ */
+public class PythonConsoleTest extends PyEnvTestCase {
+  public void testConsolePrint() throws Exception {
+    runPythonTest(new PyConsoleTask() {
+      @Override
+      public void testing() throws Exception {
+        exec("x = 96");
+        exec("x += 1");
+        exec("print(1)");
+        exec("print(x)");
+        waitForOutput("97");
+      }
+    });
+  }
+
+  public void testExecuteMultiline() throws Exception {   //PY-4329
+    runPythonTest(new PyConsoleTask() {
+      @Override
+      public void testing() throws Exception {
+        exec("if True:\n" +
+             "        x=1\n" +
+             "y=x+100\n" +
+             "for i in range(1):\n" +
+             "  print(y)\n");
+        waitForOutput("101");
+      }
+
+      @Override
+      public Set<String> getTags() {
+        return Sets.newHashSet("-jython"); //jython doesn't support multiline execution: http://bugs.jython.org/issue2106
+      }
+    });
+  }
+
+  public void testInterruptAsync() throws Exception {
+    runPythonTest(new PyConsoleTask() {
+      @Override
+      public void testing() throws Exception {
+        exec("import time");
+        execNoWait("for i in range(10000):\n" +
+                   "  print(i)\n" +
+                   "  time.sleep(0.1)");
+        waitForOutput("3\n4\n5");
+        Assert.assertFalse(canExecuteNow());
+        interrupt();
+        waitForFinish();
+        waitForReady();
+      }
+
+      @Override
+      public Set<String> getTags() {
+        return Sets.newHashSet("-iron", "-jython");
+      }
+    });
+  }
+
+  public void testLineByLineInput() throws Exception {
+    runPythonTest(new PyConsoleTask() {
+      @Override
+      public void testing() throws Exception {
+        exec("x = 96");
+        exec("x +=1");
+        exec("if True:");
+        exec("  print(x)");
+        exec("");
+        exec("");
+        waitForOutput("97");
+      }
+    });
+  }
+
+
+  public void testVariablesView() throws Exception {
+    runPythonTest(new PyConsoleTask() {
+      @Override
+      public void testing() throws Exception {
+        exec("x = 1");
+        exec("print(x)");
+        waitForOutput("1");
+        
+        assertTrue("Variable has wrong value", 
+                   hasValue("x", "1"));
+      }
+    });
+  }
+
+  public void testCompoundVariable() throws Exception {
+    runPythonTest(new PyConsoleTask() {
+      @Override
+      public void testing() throws Exception {
+        exec("x = [1, 2, 3]");
+        exec("print(x)");
+        waitForOutput("[1, 2, 3]");
+
+        List<String> values = getCompoundValueChildren(getValue("x"));
+        Collections.sort(values);
+        assertContainsElements(values, "1", "2", "3", "3");
+      }
+    });
+  }
+
+  public void testChangeVariable() throws Exception {
+    runPythonTest(new PyConsoleTask() {
+      @Override
+      public void testing() throws Exception {
+        exec("x = 1");
+        exec("print(x)");
+        waitForOutput("1");
+        
+        setValue("x", "2");
+
+        assertTrue("Variable has wrong value",
+                   hasValue("x", "2"));
+      }
+    });
+  }
+}
diff --git a/python/testSrc/com/jetbrains/env/python/PythonDebuggerTest.java b/python/testSrc/com/jetbrains/env/python/PythonDebuggerTest.java
new file mode 100644
index 0000000..4372715
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/python/PythonDebuggerTest.java
@@ -0,0 +1,579 @@
+package com.jetbrains.env.python;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.intellij.testFramework.fixtures.IdeaProjectTestFixture;
+import com.intellij.xdebugger.XDebuggerTestUtil;
+import com.jetbrains.env.PyEnvTestCase;
+import com.jetbrains.env.python.debug.PyDebuggerTask;
+import com.jetbrains.env.ut.PyUnitTestTask;
+import com.jetbrains.python.PythonHelpersLocator;
+import com.jetbrains.python.console.pydev.PydevCompletionVariant;
+import com.jetbrains.python.debugger.PyDebuggerException;
+import com.jetbrains.python.debugger.PyExceptionBreakpointProperties;
+import com.jetbrains.python.debugger.PyExceptionBreakpointType;
+import com.jetbrains.python.debugger.pydev.ProcessDebugger;
+import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author traff
+ */
+
+public class PythonDebuggerTest extends PyEnvTestCase {
+  public void testBreakpointStopAndEval() throws Exception {
+    runPythonTest(new PyDebuggerTask("/debug", "test1.py") {
+      @Override
+      public void before() throws Exception {
+        toggleBreakpoint(getScriptPath(), 3);
+      }
+
+      @Override
+      public void testing() throws Exception {
+        waitForPause();
+
+        eval("i").hasValue("0");
+
+        resume();
+
+        waitForPause();
+
+        eval("i").hasValue("1");
+
+        resume();
+
+        waitForPause();
+
+        eval("i").hasValue("2");
+      }
+    });
+  }
+
+  public void testDebugger() {
+    runPythonTest(new PyUnitTestTask("", "test_debug.py") {
+      @Override
+      protected String getTestDataPath() {
+        return PythonHelpersLocator.getPythonCommunityPath() + "/helpers/pydev";
+      }
+
+      @Override
+      public void after() {
+        allTestsPassed();
+      }
+    });
+  }
+
+  public void testConditionalBreakpoint() throws Exception {
+    runPythonTest(new PyDebuggerTask("/debug", "test1.py") {
+      @Override
+      public void before() throws Exception {
+        toggleBreakpoint(getScriptPath(), 3);
+        XDebuggerTestUtil.setBreakpointCondition(getProject(), 3, "i == 1 or i == 11 or i == 111");
+      }
+
+      @Override
+      public void testing() throws Exception {
+        waitForPause();
+
+        eval("i").hasValue("1");
+
+        resume();
+
+        waitForPause();
+
+        eval("i").hasValue("11");
+
+        resume();
+
+        waitForPause();
+
+        eval("i").hasValue("111");
+      }
+    });
+  }
+
+  public void testDebugConsole() throws Exception {
+    runPythonTest(new PyDebuggerTask("/debug", "test1.py") {
+      @Override
+      public void before() throws Exception {
+        toggleBreakpoint(getScriptPath(), 3);
+      }
+
+      @Override
+      public void testing() throws Exception {
+        waitForPause();
+
+        eval("i").hasValue("0");
+
+        resume();
+
+        waitForPause();
+
+        consoleExec("'i=%d'%i");
+
+        waitForOutput("'i=1'");
+
+        consoleExec("x");
+
+        waitForOutput("name 'x' is not defined");
+
+        consoleExec("1-;");
+
+        waitForOutput("SyntaxError");
+
+        resume();
+      }
+
+      private void consoleExec(String command) {
+        myDebugProcess.consoleExec(command, new ProcessDebugger.DebugCallback<String>() {
+          @Override
+          public void ok(String value) {
+
+          }
+
+          @Override
+          public void error(PyDebuggerException exception) {
+          }
+        });
+      }
+    });
+  }
+
+
+  public void testDebugCompletion() throws Exception {
+    runPythonTest(new PyDebuggerTask("/debug", "test4.py") {
+      @Override
+      public void before() throws Exception {
+        toggleBreakpoint(getScriptPath(), 3);
+      }
+
+      @Override
+      public void testing() throws Exception {
+        waitForPause();
+
+        List<PydevCompletionVariant> list = myDebugProcess.getCompletions("xvalu");
+        assertEquals(2, list.size());
+      }
+    });
+  }
+
+  public void testBreakpointLogExpression() throws Exception {
+    runPythonTest(new PyDebuggerTask("/debug", "test1.py") {
+      @Override
+      public void before() throws Exception {
+        toggleBreakpoint(getScriptPath(), 3);
+        XDebuggerTestUtil.setBreakpointLogExpression(getProject(), 3, "'i = %d'%i");
+      }
+
+      @Override
+      public void testing() throws Exception {
+        waitForPause();
+        resume();
+        waitForOutput("i = 1");
+      }
+    });
+  }
+
+  public void testStepOver() throws Exception {
+    runPythonTest(new PyDebuggerTask("/debug", "test2.py") {
+      @Override
+      public void before() throws Exception {
+        toggleBreakpoint(getScriptPath(), 5);
+      }
+
+      @Override
+      public void testing() throws Exception {
+        waitForPause();
+        stepOver();
+        waitForPause();
+        stepOver();
+        waitForPause();
+        eval("z").hasValue("2");
+      }
+    });
+  }
+
+  public void testStepInto() throws Exception {
+    runPythonTest(new PyDebuggerTask("/debug", "test2.py") {
+      @Override
+      public void before() throws Exception {
+        toggleBreakpoint(getScriptPath(), 5);
+      }
+
+      @Override
+      public void testing() throws Exception {
+        waitForPause();
+        stepInto();
+        waitForPause();
+        eval("x").hasValue("1");
+        stepOver();
+        waitForPause();
+        eval("y").hasValue("3");
+        stepOver();
+        waitForPause();
+        eval("z").hasValue("1");
+      }
+    });
+  }
+
+  public void testSmartStepInto() throws Exception {
+    runPythonTest(new PyDebuggerTask("/debug", "test3.py") {
+      @Override
+      public void before() throws Exception {
+        toggleBreakpoint(getScriptPath(), 14);
+      }
+
+      @Override
+      public void testing() throws Exception {
+        waitForPause();
+        smartStepInto("foo");
+        waitForPause();
+        stepOver();
+        waitForPause();
+        eval("y").hasValue("4");
+      }
+    });
+  }
+
+  public void testSmartStepInto2() throws Exception {
+    runPythonTest(new PyDebuggerTask("/debug", "test3.py") {
+      @Override
+      public void before() throws Exception {
+        toggleBreakpoint(getScriptPath(), 18);
+        toggleBreakpoint(getScriptPath(), 25);
+      }
+
+      @Override
+      public void testing() throws Exception {
+        waitForPause();
+        toggleBreakpoint(getScriptPath(), 18);
+        smartStepInto("foo");
+        waitForPause();
+        eval("a.z").hasValue("1");
+      }
+    });
+  }
+
+  public void testInput() throws Exception {
+    runPythonTest(new PyDebuggerTask("/debug", "test_input.py") {
+      @Override
+      public void before() throws Exception {
+      }
+
+      @Override
+      public void testing() throws Exception {
+        waitForOutput("print command >");
+        input("GO!");
+        waitForOutput("command was GO!");
+      }
+
+      @Override
+      public Set<String> getTags() {
+        return ImmutableSet.of("-jython"); //can't run on jython
+      }
+    });
+  }
+
+  public void testRunToLine() throws Exception {
+    runPythonTest(new PyDebuggerTask("/debug", "test_runtoline.py") {
+      @Override
+      public void before() throws Exception {
+        toggleBreakpoint(getScriptPath(), 1);
+        toggleBreakpoint(getScriptPath(), 7);
+      }
+
+      @Override
+      public void testing() throws Exception {
+        waitForPause();
+        eval("x").hasValue("0");
+        runToLine(4);
+        eval("x").hasValue("1");
+        resume();
+        waitForPause();
+        eval("x").hasValue("12");
+        resume();
+
+        waitForOutput("x = 12");
+      }
+    });
+  }
+
+  private static void addExceptionBreakpoint(IdeaProjectTestFixture fixture, PyExceptionBreakpointProperties properties) {
+    XDebuggerTestUtil.addBreakpoint(fixture.getProject(), PyExceptionBreakpointType.class, properties);
+  }
+
+  public void testExceptionBreakpointOnTerminate() throws Exception {
+    runPythonTest(new PyDebuggerTask("/debug", "test_exceptbreak.py") {
+      @Override
+      public void before() throws Exception {
+        createExceptionBreak(myFixture, true, false, false);
+      }
+
+      @Override
+      public void testing() throws Exception {
+        waitForPause();
+        eval("__exception__[0].__name__").hasValue("'ZeroDivisionError'");
+        resume();
+        waitForTerminate();
+      }
+
+      @Override
+      public Set<String> getTags() {
+        return ImmutableSet.of("-iron");
+      }
+    });
+  }
+
+  private static void createExceptionBreak(IdeaProjectTestFixture fixture,
+                                           boolean notifyOnTerminate,
+                                           boolean notifyAlways,
+                                           boolean notifyOnFirst) {
+    XDebuggerTestUtil.removeAllBreakpoints(fixture.getProject());
+    XDebuggerTestUtil.setDefaultBreakpointEnabled(fixture.getProject(), PyExceptionBreakpointType.class, false);
+
+    PyExceptionBreakpointProperties properties = new PyExceptionBreakpointProperties("exceptions.ZeroDivisionError");
+    properties.setNotifyOnTerminate(notifyOnTerminate);
+    properties.setNotifyAlways(notifyAlways);
+    properties.setNotifyOnlyOnFirst(notifyOnFirst);
+    addExceptionBreakpoint(fixture, properties);
+    properties = new PyExceptionBreakpointProperties("builtins.ZeroDivisionError"); //for python 3
+    properties.setNotifyOnTerminate(notifyOnTerminate);
+    properties.setNotifyAlways(notifyAlways);
+    properties.setNotifyOnlyOnFirst(notifyOnFirst);
+    addExceptionBreakpoint(fixture, properties);
+  }
+
+  public void testExceptionBreakpointAlways() throws Exception {
+    runPythonTest(new PyDebuggerTask("/debug", "test_exceptbreak.py") {
+      @Override
+      public void before() throws Exception {
+        createExceptionBreak(myFixture, false, true, false);
+      }
+
+      @Override
+      public void testing() throws Exception {
+        waitForPause();
+        eval("__exception__[0].__name__").hasValue("'ZeroDivisionError'");
+        resume();
+        waitForPause();
+        resume();
+        waitForPause();
+        resume();
+        waitForTerminate();
+      }
+
+      @Override
+      public Set<String> getTags() {
+        return ImmutableSet.of("-pypy"); //TODO: fix it for Pypy
+      }
+    });
+  }
+
+  public void testExceptionBreakpointOnFirstRaise() throws Exception {
+    runPythonTest(new PyDebuggerTask("/debug", "test_exceptbreak.py") {
+      @Override
+      public void before() throws Exception {
+        createExceptionBreak(myFixture, false, false, true);
+      }
+
+      @Override
+      public void testing() throws Exception {
+        waitForPause();
+        eval("__exception__[0].__name__").hasValue("'ZeroDivisionError'");
+        resume();
+        waitForTerminate();
+      }
+
+      @Override
+      public Set<String> getTags() {
+        return ImmutableSet.of("-iron");
+      }
+    });
+  }
+
+  public void testMultithreading() throws Exception {
+    runPythonTest(new PyDebuggerTask("/debug", "test_multithread.py") {
+      @Override
+      public void before() throws Exception {
+        toggleBreakpoint(getScriptPath(), 9);
+        toggleBreakpoint(getScriptPath(), 13);
+      }
+
+      @Override
+      public void testing() throws Exception {
+        waitForPause();
+        eval("y").hasValue("2");
+        resume();
+        waitForPause();
+        eval("z").hasValue("102");
+        resume();
+      }
+
+      @Override
+      public Set<String> getTags() {
+        return ImmutableSet.of("-pypy"); //TODO: fix that for PyPy
+      }
+    });
+  }
+
+  public void testEggDebug() throws Exception {
+    runPythonTest(new PyDebuggerTask("/debug", "test_egg.py") {
+      @Override
+      public void before() throws Exception {
+        String egg = getFilePath("Adder-0.1.egg");
+        toggleBreakpointInEgg(egg, "adder/adder.py", 2);
+        PythonSdkFlavor flavor = PythonSdkFlavor.getFlavor(getRunConfiguration().getSdkHome());
+        if (flavor != null) {
+          flavor.initPythonPath(Lists.newArrayList(egg), getRunConfiguration().getEnvs());
+        }
+        else {
+          getRunConfiguration().getEnvs().put("PYTHONPATH", egg);
+        }
+      }
+
+      @Override
+      public void testing() throws Exception {
+        waitForPause();
+        eval("ret").hasValue("16");
+        resume();
+      }
+
+      @Override
+      public Set<String> getTags() {
+        return ImmutableSet.of("-jython"); //TODO: fix that for Jython if anybody needs it
+      }
+    });
+  }
+
+  public void testStepOverConditionalBreakpoint() throws Exception {
+    runPythonTest(new PyDebuggerTask("/debug", "test_stepOverCondition.py") {
+      @Override
+      public void before() throws Exception {
+        toggleBreakpoint(getScriptPath(), 1);
+        toggleBreakpoint(getScriptPath(), 2);
+        XDebuggerTestUtil.setBreakpointCondition(getProject(), 2, "y == 3");
+      }
+
+      @Override
+      public void testing() throws Exception {
+        waitForPause();
+        stepOver();
+        waitForPause();
+        eval("y").hasValue("2");
+      }
+    });
+  }
+
+  public void testMultiprocess() throws Exception {
+    runPythonTest(new PyDebuggerTask("/debug", "test_multiprocess.py") {
+      @Override
+      protected void init() {
+        setMultiprocessDebug(true);
+      }
+
+      @Override
+      public void before() throws Exception {
+        toggleBreakpoint(getScriptPath(), 9);
+      }
+
+      @Override
+      public void testing() throws Exception {
+        waitForPause();
+
+        eval("i").hasValue("'Result:OK'");
+
+        resume();
+
+        waitForOutput("Result:OK");
+      }
+
+      @Override
+      public Set<String> getTags() {
+        return Sets.newHashSet("python3");
+      }
+    });
+  }
+
+
+  //TODO: fix me as I don't work properly sometimes (something connected with process termination on agent)
+  //public void testResume() throws Exception {
+  //  runPythonTest(new PyDebuggerTask("/debug", "Test_Resume.py") {
+  //    @Override
+  //    public void before() throws Exception {
+  //      toggleBreakpoint(getScriptPath(), 2);
+  //    }
+  //
+  //    @Override
+  //    public void testing() throws Exception {
+  //      waitForPause();
+  //      eval("x").hasValue("1");
+  //      resume();
+  //      waitForPause();
+  //      eval("x").hasValue("2");
+  //      resume();
+  //    }
+  //  });
+  //}
+
+
+  //TODO: first fix strange hanging of that test
+  //public void testRemoteDebug() throws Exception {
+  //  runPythonTest(new PyRemoteDebuggerTask("/debug", "test_remote.py") {
+  //    @Override
+  //    public void before() throws Exception {
+  //    }
+  //
+  //    @Override
+  //    public void testing() throws Exception {
+  //      waitForPause();
+  //      eval("x").hasValue("0");
+  //      stepOver();
+  //      waitForPause();
+  //      eval("x").hasValue("1");
+  //      stepOver();
+  //      waitForPause();
+  //      eval("x").hasValue("2");
+  //      resume();
+  //    }
+  //
+  //    @Override
+  //    protected void checkOutput(ProcessOutput output) {
+  //      assertEmpty(output.getStderr());
+  //      assertEquals("OK", output.getStdout().trim());
+  //    }
+  //
+  //    @Override
+  //    public void after() throws Exception {
+  //      stopDebugServer();
+  //    }
+  //  });
+  //}
+
+  //TODO: That doesn't work now: case from test_continuation.py and test_continuation2.py are treated differently by interpreter
+  // (first line is executed in first case and last line in second)
+
+  //public void testBreakOnContinuationLine() throws Exception {
+  //  runPythonTest(new PyDebuggerTask("/debug", "test_continuation.py") {
+  //    @Override
+  //    public void before() throws Exception {
+  //      toggleBreakpoint(getScriptPath(), 13);
+  //    }
+  //
+  //    @Override
+  //    public void testing() throws Exception {
+  //      waitForPause();
+  //      eval("x").hasValue("0");
+  //      stepOver();
+  //      waitForPause();
+  //      eval("x").hasValue("1");
+  //      stepOver();
+  //      waitForPause();
+  //      eval("x").hasValue("2");
+  //    }
+  //  });
+  //}
+}
+
diff --git a/python/testSrc/com/jetbrains/env/python/PythonGeneratorTest.java b/python/testSrc/com/jetbrains/env/python/PythonGeneratorTest.java
new file mode 100644
index 0000000..273cb3f
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/python/PythonGeneratorTest.java
@@ -0,0 +1,24 @@
+package com.jetbrains.env.python;
+
+import com.jetbrains.env.PyEnvTestCase;
+import com.jetbrains.env.ut.PyUnitTestTask;
+import com.jetbrains.python.PythonHelpersLocator;
+
+/**
+ * @author traff
+ */
+public class PythonGeneratorTest extends PyEnvTestCase{
+  public void testGenerator() {
+    runPythonTest(new PyUnitTestTask("", "test_generator.py") {
+      @Override
+      protected String getTestDataPath() {
+        return PythonHelpersLocator.getPythonCommunityPath() + "/helpers";
+      }
+
+      @Override
+      public void after() {
+        allTestsPassed();
+      }
+    });
+  }
+}
diff --git a/python/testSrc/com/jetbrains/env/python/PythonSkeletonsTest.java b/python/testSrc/com/jetbrains/env/python/PythonSkeletonsTest.java
new file mode 100644
index 0000000..c6b150f
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/python/PythonSkeletonsTest.java
@@ -0,0 +1,202 @@
+package com.jetbrains.env.python;
+
+import com.google.common.collect.ImmutableSet;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiFile;
+import com.jetbrains.env.PyEnvTestCase;
+import com.jetbrains.env.PyExecutionFixtureTestTask;
+import com.jetbrains.env.PyTestTask;
+import com.jetbrains.python.PythonFileType;
+import com.jetbrains.python.PythonTestUtil;
+import com.jetbrains.python.documentation.PythonDocumentationProvider;
+import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection;
+import com.jetbrains.python.psi.*;
+import com.jetbrains.python.psi.impl.PyBuiltinCache;
+import com.jetbrains.python.psi.resolve.PythonSdkPathCache;
+import com.jetbrains.python.psi.types.PyType;
+import com.jetbrains.python.psi.types.TypeEvalContext;
+import com.jetbrains.python.sdk.PythonSdkType;
+import com.jetbrains.python.sdk.skeletons.PySkeletonRefresher;
+import com.jetbrains.python.sdk.skeletons.SkeletonVersionChecker;
+import com.jetbrains.python.sdkTools.SdkCreationType;
+import com.jetbrains.python.toolbox.Maybe;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.util.Set;
+
+/**
+ * Heavyweight integration tests of skeletons of Python binary modules.
+ * <p/>
+ * An environment test environment must have a 'skeletons' tag in order to be compatible with this test case. No specific packages are
+ * required currently. Both Python 2 and Python 3 are OK. All platforms are OK. At least one Python 2.6+ environment is required.
+ *
+ * @author vlan
+ */
+public class PythonSkeletonsTest extends PyEnvTestCase {
+  public static final ImmutableSet<String> TAGS = ImmutableSet.of("skeletons");
+
+  public void testBuiltins() {
+    runTest(new SkeletonsTask() {
+      @Override
+      public void runTestOn(@NotNull Sdk sdk) {
+        // Check the builtin skeleton header
+        final Project project = myFixture.getProject();
+        final PyFile builtins = PyBuiltinCache.getBuiltinsForSdk(project, sdk);
+        assertNotNull(builtins);
+        final VirtualFile virtualFile = builtins.getVirtualFile();
+        assertNotNull(virtualFile);
+        assertTrue(virtualFile.isInLocalFileSystem());
+        final String path = virtualFile.getPath();
+        final PySkeletonRefresher.SkeletonHeader header = PySkeletonRefresher.readSkeletonHeader(new File(path));
+        assertNotNull(header);
+        final int version = header.getVersion();
+        assertTrue("Header version must be > 0, currently it is " + version, version > 0);
+        assertEquals(SkeletonVersionChecker.BUILTIN_NAME, header.getBinaryFile());
+
+        // Run inspections on a file that uses builtins
+        myFixture.configureByFile(getTestName(false) + ".py");
+
+
+        PsiFile expr = myFixture.getFile();
+
+        final Module module = ModuleUtil.findModuleForPsiElement(expr);
+
+        final Sdk sdkFromModule = PythonSdkType.findPythonSdk(module);
+        assertNotNull(sdkFromModule);
+
+        final Sdk sdkFromPsi = PyBuiltinCache.findSdkForFile(expr.getContainingFile());
+        final PyFile builtinsFromSdkCache = PythonSdkPathCache.getInstance(project, sdkFromPsi).getBuiltins().getBuiltinsFile();
+        assertNotNull(builtinsFromSdkCache);
+        assertEquals(builtins, builtinsFromSdkCache);
+
+        final PyFile builtinsFromPsi = PyBuiltinCache.getInstance(expr).getBuiltinsFile();
+        assertNotNull(builtinsFromPsi);
+        assertEquals(builtins, builtinsFromPsi);
+
+        myFixture.enableInspections(PyUnresolvedReferencesInspection.class);
+        edt(new Runnable() {
+          @Override
+          public void run() {
+            myFixture.checkHighlighting(true, false, false);
+          }
+        });
+      }
+    });
+  }
+
+  // PY-4349
+  public void testFakeNamedTuple() {
+    runTest(new SkeletonsTask() {
+      @Override
+      protected void runTestOn(@NotNull Sdk sdk) {
+        final LanguageLevel languageLevel = PythonSdkType.getLanguageLevelForSdk(sdk);
+        // Named tuples have been introduced in Python 2.6
+        if (languageLevel.isOlderThan(LanguageLevel.PYTHON26)) {
+          return;
+        }
+
+        // XXX: A workaround for invalidating VFS cache with the test file copied to our temp directory
+        LocalFileSystem.getInstance().refresh(false);
+
+        // Run inspections on code that uses named tuples
+        myFixture.configureByFile(getTestName(false) + ".py");
+        myFixture.enableInspections(PyUnresolvedReferencesInspection.class);
+
+        edt(new Runnable() {
+          @Override
+          public void run() {
+            myFixture.checkHighlighting(true, false, false);
+          }
+        });
+      }
+    });
+  }
+
+  public void testKnownPropertiesTypes() {
+    runTest(new SkeletonsTask() {
+      @Override
+      protected void runTestOn(@NotNull Sdk sdk) {
+        myFixture.configureByText(PythonFileType.INSTANCE,
+                                  "expr = slice(1, 2).start\n");
+        final PyExpression expr = myFixture.findElementByText("expr", PyExpression.class);
+        final TypeEvalContext context = TypeEvalContext.codeAnalysis(myFixture.getFile());
+        ApplicationManager.getApplication().runReadAction(new Runnable() {
+          @Override
+          public void run() {
+            final PyType type = context.getType(expr);
+            final String actualType = PythonDocumentationProvider.getTypeName(type, context);
+            assertEquals("int", actualType);
+          }
+        });
+      }
+    });
+  }
+
+  // PY-9797
+  public void testReadWriteDeletePropertyDefault() {
+    runTest(new SkeletonsTask() {
+      @Override
+      protected void runTestOn(@NotNull Sdk sdk) {
+        final LanguageLevel languageLevel = PythonSdkType.getLanguageLevelForSdk(sdk);
+        // We rely on int.real property that is not explicitly annotated in the skeletons generator
+        if (languageLevel.isOlderThan(LanguageLevel.PYTHON26)) {
+          return;
+        }
+        final Project project = myFixture.getProject();
+        final PyFile builtins = PyBuiltinCache.getBuiltinsForSdk(project, sdk);
+        assertNotNull(builtins);
+        ApplicationManager.getApplication().runReadAction(new Runnable() {
+          @Override
+          public void run() {
+            final PyClass cls = builtins.findTopLevelClass("int");
+            assertNotNull(cls);
+            final Property prop = cls.findProperty("real", true);
+            assertNotNull(prop);
+            assertIsNotNull(prop.getGetter());
+            assertIsNotNull(prop.getSetter());
+            assertIsNotNull(prop.getDeleter());
+          }
+        });
+      }
+
+      private void assertIsNotNull(Maybe<Callable> accessor) {
+        if (accessor.isDefined()) {
+          assertNotNull(accessor.valueOrNull());
+        }
+      }
+    });
+  }
+
+
+  private void runTest(@NotNull PyTestTask task) {
+    runPythonTest(task);
+  }
+
+
+  private abstract class SkeletonsTask extends PyExecutionFixtureTestTask {
+    @Override
+    protected String getTestDataPath() {
+      return PythonTestUtil.getTestDataPath() + "/skeletons/";
+    }
+
+    @Override
+    public void runTestOn(String sdkHome) throws Exception {
+      final Sdk sdk = createTempSdk(sdkHome, SdkCreationType.SDK_PACKAGES_AND_SKELETONS);
+      runTestOn(sdk);
+    }
+
+    @Override
+    public Set<String> getTags() {
+      return TAGS;
+    }
+
+    protected abstract void runTestOn(@NotNull Sdk sdk);
+  }
+}
diff --git a/python/testSrc/com/jetbrains/env/python/console/PyConsoleTask.java b/python/testSrc/com/jetbrains/env/python/console/PyConsoleTask.java
new file mode 100644
index 0000000..9a24293
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/python/console/PyConsoleTask.java
@@ -0,0 +1,380 @@
+package com.jetbrains.env.python.console;
+
+import com.google.common.collect.Lists;
+import com.intellij.execution.console.LanguageConsoleView;
+import com.intellij.execution.process.ProcessAdapter;
+import com.intellij.execution.process.ProcessEvent;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.util.ui.UIUtil;
+import com.intellij.xdebugger.frame.XValueChildrenList;
+import com.jetbrains.env.PyExecutionFixtureTestTask;
+import com.jetbrains.python.console.*;
+import com.jetbrains.python.console.pydev.ConsoleCommunicationListener;
+import com.jetbrains.python.debugger.PyDebugValue;
+import com.jetbrains.python.debugger.PyDebuggerException;
+import com.jetbrains.python.sdkTools.SdkCreationType;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Assert;
+
+import java.util.List;
+import java.util.concurrent.Semaphore;
+
+/**
+ * @author traff
+ */
+public class PyConsoleTask extends PyExecutionFixtureTestTask {
+  private boolean myProcessCanTerminate;
+
+  protected PyConsoleProcessHandler myProcessHandler;
+  protected PydevConsoleCommunication myCommunication;
+
+  private boolean shouldPrintOutput = false;
+  private PythonConsoleView myConsoleView;
+  private Semaphore mySemaphore;
+  private Semaphore mySemaphore0;
+  private PydevConsoleExecuteActionHandler myExecuteHandler;
+
+  public PyConsoleTask() {
+    setWorkingFolder(getTestDataPath());
+  }
+
+  public PythonConsoleView getConsoleView() {
+    return myConsoleView;
+  }
+
+  @Override
+  public void setUp(final String testName) throws Exception {
+    UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+      @Override
+      public void run() {
+        try {
+          if (myFixture == null) {
+            PyConsoleTask.super.setUp(testName);
+          }
+        }
+        catch (Exception e) {
+          throw new RuntimeException(e);
+        }
+      }
+    });
+  }
+
+  @NotNull
+  protected String output() {
+    return myConsoleView.getConsole().getHistoryViewer().getDocument().getText();
+  }
+
+  public void setProcessCanTerminate(boolean processCanTerminate) {
+    myProcessCanTerminate = processCanTerminate;
+  }
+
+  @Override
+  public void tearDown() throws Exception {
+    UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+      @Override
+      public void run() {
+        try {
+          if (myConsoleView != null) {
+            disposeConsole();
+          }
+          PyConsoleTask.super.tearDown();
+        }
+        catch (Exception e) {
+          throw new RuntimeException(e);
+        }
+      }
+    });
+  }
+
+  private void disposeConsole() throws InterruptedException {
+    if (myCommunication != null) {
+      UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+        @Override
+        public void run() {
+          try {
+            myCommunication.close();
+          }
+          catch (Exception e) {
+            e.printStackTrace();
+          }
+          myCommunication = null;
+        }
+      });
+    }
+
+    disposeConsoleProcess();
+
+    if (myConsoleView != null) {
+      new WriteAction() {
+        @Override
+        protected void run(@NotNull Result result) throws Throwable {
+          Disposer.dispose(myConsoleView);
+          myConsoleView = null;
+        }
+      }.execute();
+    }
+  }
+
+  @Override
+  public void runTestOn(final String sdkHome) throws Exception {
+    final Project project = getProject();
+
+    final Sdk sdk = createTempSdk(sdkHome, SdkCreationType.EMPTY_SDK);
+
+    setProcessCanTerminate(false);
+
+    PydevConsoleRunner consoleRunner = PydevConsoleRunner.create(project, sdk, PyConsoleType.PYTHON, getWorkingFolder());
+    before();
+
+    mySemaphore0 = new Semaphore(0);
+
+    consoleRunner.addConsoleListener(new PydevConsoleRunner.ConsoleListener() {
+      @Override
+      public void handleConsoleInitialized(LanguageConsoleView consoleView) {
+        mySemaphore0.release();
+      }
+    });
+
+    consoleRunner.run();
+
+    waitFor(mySemaphore0);
+
+    mySemaphore = new Semaphore(0);
+
+    myConsoleView = consoleRunner.getConsoleView();
+    myProcessHandler = (PyConsoleProcessHandler)consoleRunner.getProcessHandler();
+
+    myExecuteHandler = (PydevConsoleExecuteActionHandler)consoleRunner.getConsoleExecuteActionHandler();
+
+    myCommunication = consoleRunner.getPydevConsoleCommunication();
+
+    myCommunication.addCommunicationListener(new ConsoleCommunicationListener() {
+      @Override
+      public void commandExecuted(boolean more) {
+        mySemaphore.release();
+      }
+
+      @Override
+      public void inputRequested() {
+      }
+    });
+
+    myProcessHandler.addProcessListener(new ProcessAdapter() {
+      @Override
+      public void processTerminated(ProcessEvent event) {
+        if (event.getExitCode() != 0 && !myProcessCanTerminate) {
+          Assert.fail("Process terminated unexpectedly\n" + output());
+        }
+      }
+    });
+
+    OutputPrinter myOutputPrinter = null;
+    if (shouldPrintOutput) {
+      myOutputPrinter = new OutputPrinter();
+      myOutputPrinter.start();
+    }
+
+    waitForOutput("PyDev console");
+
+    try {
+      testing();
+      after();
+    }
+    finally {
+      setProcessCanTerminate(true);
+
+      if (myOutputPrinter != null) {
+        myOutputPrinter.stop();
+      }
+
+      disposeConsole();
+    }
+  }
+
+  private void disposeConsoleProcess() throws InterruptedException {
+    myProcessHandler.destroyProcess();
+
+    waitFor(myProcessHandler);
+
+    if (!myProcessHandler.isProcessTerminated()) {
+      if (!waitFor(myProcessHandler)) {
+        if (!myProcessHandler.isProcessTerminated()) {
+          throw new RuntimeException("Cannot stop console process");
+        }
+      }
+    }
+    myProcessHandler = null;
+  }
+
+  /**
+   * Waits until all passed strings appear in output.
+   * If they don't appear in time limit, then exception is raised.
+   *
+   * @param string
+   * @throws InterruptedException
+   */
+  public void waitForOutput(String... string) throws InterruptedException {
+    int count = 0;
+    while (true) {
+      List<String> missing = Lists.newArrayList();
+      String out = output();
+      boolean flag = true;
+      for (String s : string) {
+        if (!out.contains(s)) {
+          flag = false;
+          missing.add(s);
+        }
+      }
+      if (flag) {
+        break;
+      }
+      if (count > 10) {
+        Assert.fail("Strings: <--\n" + StringUtil.join(missing, "\n---\n") + "-->" + "are not present in output.\n" + output());
+      }
+      Thread.sleep(2000);
+      count++;
+    }
+  }
+
+  protected void waitForReady() throws InterruptedException {
+    int count = 0;
+    while (!myExecuteHandler.isEnabled() || !canExecuteNow()) {
+      if (count > 10) {
+        Assert.fail("Console is not ready");
+      }
+      Thread.sleep(300);
+      count++;
+    }
+  }
+
+  protected boolean canExecuteNow() {
+    return myExecuteHandler.canExecuteNow();
+  }
+
+  public void setShouldPrintOutput(boolean shouldPrintOutput) {
+    this.shouldPrintOutput = shouldPrintOutput;
+  }
+
+  private class OutputPrinter {
+    private Thread myThread;
+    private int myLen = 0;
+
+    public void start() {
+      myThread = new Thread(new Runnable() {
+        @Override
+        public void run() {
+          doJob();
+        }
+      });
+      myThread.setDaemon(true);
+      myThread.start();
+    }
+
+    private void doJob() {
+      try {
+        while (true) {
+          printToConsole();
+
+          Thread.sleep(500);
+        }
+      }
+      catch (Exception ignored) {
+      }
+    }
+
+    private synchronized void printToConsole() {
+      String s = output();
+      if (s.length() > myLen) {
+        System.out.print(s.substring(myLen));
+      }
+      myLen = s.length();
+    }
+
+    public void stop() {
+      printToConsole();
+      myThread.interrupt();
+    }
+  }
+
+  protected void exec(String command) throws InterruptedException {
+    waitForReady();
+    int p = mySemaphore.availablePermits();
+    myConsoleView.executeInConsole(command);
+    mySemaphore.acquire(p + 1);
+    //waitForOutput(">>> " + command);
+  }
+
+  protected boolean hasValue(String varName, String value) throws PyDebuggerException {
+    PyDebugValue val = getValue(varName);
+    return val != null && value.equals(val.getValue());
+  }
+
+  protected void setValue(String varName, String value) throws PyDebuggerException {
+    PyDebugValue val = getValue(varName);
+    myCommunication.changeVariable(val, value);
+  }
+  
+  protected PyDebugValue getValue(String varName) throws PyDebuggerException {
+    XValueChildrenList l = myCommunication.loadFrame();
+
+    if (l == null) {
+      return null;
+    }
+    for (int i = 0; i < l.size(); i++) {
+      String name = l.getName(i);
+      if (varName.equals(name)) {
+        return (PyDebugValue)l.getValue(i);
+      }
+    }
+    
+    return null;
+  }
+  
+  protected List<String> getCompoundValueChildren(PyDebugValue value) throws PyDebuggerException {
+    XValueChildrenList list = myCommunication.loadVariable(value);
+    List<String> result = Lists.newArrayList();
+    for (int i = 0; i<list.size(); i++) {
+      result.add(((PyDebugValue)list.getValue(i)).getValue());
+    }
+    return result;
+  }
+
+  protected void input(String text) {
+    myConsoleView.executeInConsole(text);
+  }
+
+  protected void waitForFinish() throws InterruptedException {
+    waitFor(mySemaphore);
+  }
+
+  protected void execNoWait(final String command) {
+    UIUtil.invokeLaterIfNeeded(new Runnable() {
+      @Override
+      public void run() {
+        myConsoleView.executeCode(command, null);
+      }
+    });
+  }
+
+  protected void interrupt() {
+    myCommunication.interrupt();
+  }
+
+
+  public void addTextToEditor(final String text) {
+    UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+      @Override
+      public void run() {
+        getConsoleView().getLanguageConsole().setInputText(text);
+        PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
+      }
+    }
+    );
+  }
+}
diff --git a/python/testSrc/com/jetbrains/env/python/debug/PyBaseDebuggerTask.java b/python/testSrc/com/jetbrains/env/python/debug/PyBaseDebuggerTask.java
new file mode 100644
index 0000000..38eed73
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/python/debug/PyBaseDebuggerTask.java
@@ -0,0 +1,361 @@
+package com.jetbrains.env.python.debug;
+
+import com.google.common.collect.Sets;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.ui.UIUtil;
+import com.intellij.xdebugger.*;
+import com.intellij.xdebugger.frame.XValue;
+import com.jetbrains.env.PyExecutionFixtureTestTask;
+import com.jetbrains.python.console.PythonDebugLanguageConsoleView;
+import com.jetbrains.python.debugger.PyDebugProcess;
+import com.jetbrains.python.debugger.PyDebugValue;
+import com.jetbrains.python.debugger.PyDebuggerException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Assert;
+
+import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Set;
+import java.util.concurrent.Semaphore;
+
+/**
+ * @author traff
+ */
+public abstract class PyBaseDebuggerTask extends PyExecutionFixtureTestTask {
+  private Set<Pair<String, Integer>> myBreakpoints = Sets.newHashSet();
+  protected PyDebugProcess myDebugProcess;
+  protected XDebugSession mySession;
+  protected Semaphore myPausedSemaphore;
+  protected Semaphore myTerminateSemaphore;
+  protected boolean shouldPrintOutput = false;
+  protected boolean myProcessCanTerminate;
+
+  protected void waitForPause() throws InterruptedException, InvocationTargetException {
+    Assert.assertTrue("Debugger didn't stopped within timeout\nOutput:" + output(), waitFor(myPausedSemaphore));
+
+    XDebuggerTestUtil.waitForSwing();
+  }
+
+  protected void waitForTerminate() throws InterruptedException, InvocationTargetException {
+    setProcessCanTerminate(true);
+
+    Assert.assertTrue("Debugger didn't terminated within timeout\nOutput:" + output(), waitFor(myTerminateSemaphore));
+    XDebuggerTestUtil.waitForSwing();
+  }
+
+  protected void runToLine(int line) throws InvocationTargetException, InterruptedException {
+    XDebugSession currentSession = XDebuggerManager.getInstance(getProject()).getCurrentSession();
+    XSourcePosition position = currentSession.getCurrentPosition();
+
+
+    currentSession.runToPosition(XDebuggerUtil.getInstance().createPosition(position.getFile(), line), false);
+
+    waitForPause();
+  }
+
+  protected void resume() {
+    XDebugSession currentSession = XDebuggerManager.getInstance(getProject()).getCurrentSession();
+
+    Assert.assertTrue(currentSession.isSuspended());
+    Assert.assertEquals(0, myPausedSemaphore.availablePermits());
+
+    currentSession.resume();
+  }
+
+  protected void stepOver() {
+    XDebugSession currentSession = XDebuggerManager.getInstance(getProject()).getCurrentSession();
+
+    Assert.assertTrue(currentSession.isSuspended());
+    Assert.assertEquals(0, myPausedSemaphore.availablePermits());
+
+    currentSession.stepOver(false);
+  }
+
+  protected void stepInto() {
+    XDebugSession currentSession = XDebuggerManager.getInstance(getProject()).getCurrentSession();
+
+    Assert.assertTrue(currentSession.isSuspended());
+    Assert.assertEquals(0, myPausedSemaphore.availablePermits());
+
+    currentSession.stepInto();
+  }
+
+  protected void smartStepInto(String funcName) {
+    XDebugSession currentSession = XDebuggerManager.getInstance(getProject()).getCurrentSession();
+
+    Assert.assertTrue(currentSession.isSuspended());
+    Assert.assertEquals(0, myPausedSemaphore.availablePermits());
+
+    myDebugProcess.startSmartStepInto(funcName);
+  }
+
+  @NotNull
+  protected String output() {
+    if (mySession != null && mySession.getConsoleView() != null) {
+      PythonDebugLanguageConsoleView pydevConsoleView = (PythonDebugLanguageConsoleView)mySession.getConsoleView();
+      if (pydevConsoleView != null) {
+        return XDebuggerTestUtil.getConsoleText(pydevConsoleView.getTextConsole());
+      }
+    }
+    return "Console output not available.";
+  }
+
+  protected void input(String text) {
+    PrintWriter pw = new PrintWriter(myDebugProcess.getProcessHandler().getProcessInput());
+    pw.println(text);
+    pw.flush();
+  }
+
+  private void outputContains(String substring) {
+    Assert.assertTrue(output().contains(substring));
+  }
+
+  public void setProcessCanTerminate(boolean processCanTerminate) {
+    myProcessCanTerminate = processCanTerminate;
+  }
+
+  protected void clearAllBreakpoints() {
+
+    UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+      @Override
+      public void run() {
+        XDebuggerTestUtil.removeAllBreakpoints(getProject());
+      }
+    });
+  }
+
+  /**
+   * Toggles breakpoint
+   *
+   * @param file getScriptPath() or path to script
+   * @param line starting with 0
+   */
+  protected void toggleBreakpoint(final String file, final int line) {
+    UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+      @Override
+      public void run() {
+        doToggleBreakpoint(file, line);
+      }
+    });
+
+    addOrRemoveBreakpoint(file, line);
+  }
+
+  private void addOrRemoveBreakpoint(String file, int line) {
+    if (myBreakpoints.contains(Pair.create(file, line))) {
+      myBreakpoints.remove(Pair.create(file, line));
+    }
+    else {
+      myBreakpoints.add(Pair.create(file, line));
+    }
+  }
+
+  protected void toggleBreakpointInEgg(final String file, final String innerPath, final int line) {
+    UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+      @Override
+      public void run() {
+        VirtualFile f = LocalFileSystem.getInstance().findFileByPath(file);
+        Assert.assertNotNull(f);
+        final VirtualFile jarRoot = JarFileSystem.getInstance().getJarRootForLocalFile(f);
+        Assert.assertNotNull(jarRoot);
+        VirtualFile innerFile = jarRoot.findFileByRelativePath(innerPath);
+        Assert.assertNotNull(innerFile);
+        XDebuggerTestUtil.toggleBreakpoint(getProject(), innerFile, line);
+      }
+    });
+
+    addOrRemoveBreakpoint(file, line);
+  }
+
+  public boolean canPutBreakpointAt(Project project, String file, int line) {
+    VirtualFile vFile = LocalFileSystem.getInstance().findFileByPath(file);
+    Assert.assertNotNull(vFile);
+    return XDebuggerUtil.getInstance().canPutBreakpointAt(project, vFile, line);
+  }
+
+  private void doToggleBreakpoint(String file, int line) {
+    Assert.assertTrue(canPutBreakpointAt(getProject(), file, line));
+    XDebuggerTestUtil.toggleBreakpoint(getProject(), LocalFileSystem.getInstance().findFileByPath(file), line);
+  }
+
+  protected Variable eval(String name) throws InterruptedException {
+    Assert.assertTrue("Eval works only while suspended", mySession.isSuspended());
+    XValue var = XDebuggerTestUtil.evaluate(mySession, name).first;
+    Assert.assertNotNull("There is no variable named " + name, var);
+    return new Variable(var);
+  }
+
+  protected void setVal(String name, String value) throws InterruptedException, PyDebuggerException {
+    XValue var = XDebuggerTestUtil.evaluate(mySession, name).first;
+    myDebugProcess.changeVariable((PyDebugValue)var, value);
+  }
+
+  public void waitForOutput(String ... string) throws InterruptedException {
+    long started = System.currentTimeMillis();
+
+    while (!containsOneOf(output(), string)) {
+      if (System.currentTimeMillis() - started > myTimeout) {
+        Assert.fail("None of '" + StringUtil.join(string, ", ") + "'" + " is not present in output.\n" + output());
+      }
+      Thread.sleep(2000);
+    }
+  }
+
+  protected boolean containsOneOf(String output, String[] strings) {
+    for (String s: strings) {
+      if (output.contains(s)) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+
+  public void setShouldPrintOutput(boolean shouldPrintOutput) {
+    this.shouldPrintOutput = shouldPrintOutput;
+  }
+
+  @Override
+  public void setUp(final String testName) throws Exception {
+    UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+      @Override
+      public void run() {
+        try {
+          if (myFixture == null) {
+            PyBaseDebuggerTask.super.setUp(testName);
+          }
+        }
+        catch (Exception e) {
+          throw new RuntimeException(e);
+        }
+      }
+    });
+  }
+
+  @Override
+  public void tearDown() throws Exception {
+    UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+      public void run() {
+        try {
+          if (mySession != null) {
+            finishSession();
+          }
+          PyBaseDebuggerTask.super.tearDown();
+        }
+        catch (Exception e) {
+          throw new RuntimeException(e);
+        }
+      }
+    });
+  }
+
+  protected void finishSession() throws InterruptedException {
+    disposeDebugProcess();
+
+    if (mySession != null) {
+      new WriteAction() {
+        protected void run(Result result) throws Throwable {
+          mySession.stop();
+        }
+      }.execute();
+
+      waitFor(mySession.getDebugProcess().getProcessHandler()); //wait for process termination after session.stop() which is async
+
+      XDebuggerTestUtil.disposeDebugSession(mySession);
+      mySession = null;
+      myDebugProcess = null;
+      myPausedSemaphore = null;
+    }
+  }
+
+  protected abstract void disposeDebugProcess() throws InterruptedException;
+
+  protected void doTest(@Nullable OutputPrinter myOutputPrinter) throws InterruptedException {
+    try {
+      testing();
+      after();
+    }
+    catch (Throwable e) {
+      throw new RuntimeException(output(), e);
+    }
+    finally {
+      clearAllBreakpoints();
+
+      setProcessCanTerminate(true);
+
+      if (myOutputPrinter != null) {
+        myOutputPrinter.stop();
+      }
+
+      finishSession();
+    }
+  }
+
+  protected static class Variable {
+    private final XTestValueNode myValueNode;
+
+    public Variable(XValue value) throws InterruptedException {
+      myValueNode = XDebuggerTestUtil.computePresentation(value);
+    }
+
+    public Variable hasValue(String value) {
+      Assert.assertEquals(value, myValueNode.myValue);
+      return this;
+    }
+
+    public Variable hasType(String type) {
+      Assert.assertEquals(type, myValueNode.myType);
+      return this;
+    }
+
+    public Variable hasName(String name) {
+      Assert.assertEquals(name, myValueNode.myName);
+      return this;
+    }
+  }
+
+  public class OutputPrinter {
+    private Thread myThread;
+
+    public void start() {
+      myThread = new Thread(new Runnable() {
+        @Override
+        public void run() {
+          doJob();
+        }
+      });
+      myThread.setDaemon(true);
+      myThread.start();
+    }
+
+    private void doJob() {
+      int len = 0;
+      try {
+        while (true) {
+          String s = output();
+          if (s.length() > len) {
+            System.out.print(s.substring(len));
+          }
+          len = s.length();
+
+          Thread.sleep(500);
+        }
+      }
+      catch (Exception e) {
+      }
+    }
+
+    public void stop() {
+      myThread.interrupt();
+    }
+  }
+}
diff --git a/python/testSrc/com/jetbrains/env/python/debug/PyDebuggerTask.java b/python/testSrc/com/jetbrains/env/python/debug/PyDebuggerTask.java
new file mode 100644
index 0000000..7de13d1
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/python/debug/PyDebuggerTask.java
@@ -0,0 +1,211 @@
+package com.jetbrains.env.python.debug;
+
+import com.intellij.execution.*;
+import com.intellij.execution.configurations.ConfigurationFactory;
+import com.intellij.execution.configurations.RunProfile;
+import com.intellij.execution.executors.DefaultDebugExecutor;
+import com.intellij.execution.process.KillableColoredProcessHandler;
+import com.intellij.execution.process.ProcessAdapter;
+import com.intellij.execution.process.ProcessEvent;
+import com.intellij.execution.process.ProcessHandler;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.intellij.xdebugger.*;
+import com.jetbrains.python.debugger.PyDebugProcess;
+import com.jetbrains.python.debugger.PyDebugRunner;
+import com.jetbrains.python.run.PythonCommandLineState;
+import com.jetbrains.python.run.PythonConfigurationType;
+import com.jetbrains.python.run.PythonRunConfiguration;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Assert;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.util.concurrent.Semaphore;
+
+/**
+ * @author traff
+ */
+public class PyDebuggerTask extends PyBaseDebuggerTask {
+
+  private boolean myMultiprocessDebug = false;
+  private PythonRunConfiguration myRunConfiguration;
+
+  public PyDebuggerTask() {
+    init();
+  }
+
+  public PyDebuggerTask(String workingFolder, String scriptName, String scriptParameters) {
+    setWorkingFolder(getTestDataPath() + workingFolder);
+    setScriptName(scriptName);
+    setScriptParameters(scriptParameters);
+    init();
+  }
+
+  public PyDebuggerTask(String workingFolder, String scriptName) {
+    this(workingFolder, scriptName, null);
+  }
+
+  protected void init() {
+
+  }
+
+  public void runTestOn(String sdkHome) throws Exception {
+    final Project project = getProject();
+
+    final ConfigurationFactory factory = PythonConfigurationType.getInstance().getConfigurationFactories()[0];
+
+
+    final RunnerAndConfigurationSettings settings =
+      RunManager.getInstance(project).createRunConfiguration("test", factory);
+
+    myRunConfiguration = (PythonRunConfiguration)settings.getConfiguration();
+
+    myRunConfiguration.setSdkHome(sdkHome);
+    myRunConfiguration.setScriptName(getScriptPath());
+    myRunConfiguration.setWorkingDirectory(getWorkingFolder());
+    myRunConfiguration.setScriptParameters(getScriptParameters());
+
+    new WriteAction() {
+      @Override
+      protected void run(Result result) throws Throwable {
+        RunManagerEx.getInstanceEx(project).addConfiguration(settings, false);
+        RunManagerEx.getInstanceEx(project).setSelectedConfiguration(settings);
+        Assert.assertSame(settings, RunManagerEx.getInstanceEx(project).getSelectedConfiguration());
+      }
+    }.execute();
+
+    final PyDebugRunner runner = (PyDebugRunner)ProgramRunnerUtil.getRunner(DefaultDebugExecutor.EXECUTOR_ID, settings);
+    Assert.assertTrue(runner.canRun(DefaultDebugExecutor.EXECUTOR_ID, myRunConfiguration));
+
+    final Executor executor = DefaultDebugExecutor.getDebugExecutorInstance();
+    final ExecutionEnvironment env = new ExecutionEnvironment(executor, runner, settings, project);
+
+    final PythonCommandLineState pyState = (PythonCommandLineState)myRunConfiguration.getState(executor, env);
+
+    assert pyState != null;
+    pyState.setMultiprocessDebug(isMultiprocessDebug());
+
+    final ServerSocket serverSocket;
+    try {
+      //noinspection SocketOpenedButNotSafelyClosed
+      serverSocket = new ServerSocket(0);
+    }
+    catch (IOException e) {
+      throw new ExecutionException("Failed to find free socket port", e);
+    }
+
+
+    final int serverLocalPort = serverSocket.getLocalPort();
+    final RunProfile profile = env.getRunProfile();
+
+    before();
+
+    setProcessCanTerminate(false);
+
+    myTerminateSemaphore = new Semaphore(0);
+    
+    new WriteAction<ExecutionResult>() {
+      @Override
+      protected void run(@NotNull Result<ExecutionResult> result) throws Throwable {
+        final ExecutionResult res =
+          pyState.execute(executor, PyDebugRunner.createCommandLinePatchers(myFixture.getProject(), pyState, profile, serverLocalPort));
+
+        mySession = XDebuggerManager.getInstance(getProject()).
+          startSession(runner, env, env.getContentToReuse(), new XDebugProcessStarter() {
+            @NotNull
+            public XDebugProcess start(@NotNull final XDebugSession session) {
+              myDebugProcess =
+                new PyDebugProcess(session, serverSocket, res.getExecutionConsole(), res.getProcessHandler(), isMultiprocessDebug());
+
+              myDebugProcess.getProcessHandler().addProcessListener(new ProcessAdapter() {
+
+                @Override
+                public void onTextAvailable(ProcessEvent event, Key outputType) {
+                }
+
+                @Override
+                public void processTerminated(ProcessEvent event) {
+                  myTerminateSemaphore.release();
+                  if (event.getExitCode() != 0 && !myProcessCanTerminate) {
+                    Assert.fail("Process terminated unexpectedly\n" + output());
+                  }
+                }
+              });
+
+
+              myDebugProcess.getProcessHandler().startNotify();
+
+              return myDebugProcess;
+            }
+          });
+        result.setResult(res);
+      }
+    }.execute().getResultObject();
+
+    OutputPrinter myOutputPrinter = null;
+    if (shouldPrintOutput) {
+      myOutputPrinter = new OutputPrinter();
+      myOutputPrinter.start();
+    }
+
+
+    myPausedSemaphore = new Semaphore(0);
+    
+
+    mySession.addSessionListener(new XDebugSessionAdapter() {
+      @Override
+      public void sessionPaused() {
+        if (myPausedSemaphore != null) {
+          myPausedSemaphore.release();
+        }
+      }
+    });
+
+    doTest(myOutputPrinter);
+  }
+
+  public PythonRunConfiguration getRunConfiguration() {
+    return myRunConfiguration;
+  }
+
+  private boolean isMultiprocessDebug() {
+    return myMultiprocessDebug;
+  }
+
+  public void setMultiprocessDebug(boolean multiprocessDebug) {
+    myMultiprocessDebug = multiprocessDebug;
+  }
+
+  @Override
+  protected void disposeDebugProcess() throws InterruptedException {
+    if (myDebugProcess != null) {
+      ProcessHandler processHandler = myDebugProcess.getProcessHandler();
+
+      myDebugProcess.stop();
+
+      waitFor(processHandler);
+
+      if (!processHandler.isProcessTerminated()) {
+        killDebugProcess();
+        if (!waitFor(processHandler)) {
+          new Throwable("Cannot stop debugger process").printStackTrace();
+        }
+      }
+    }
+  }
+
+  private void killDebugProcess() {
+    if (myDebugProcess.getProcessHandler() instanceof KillableColoredProcessHandler) {
+      KillableColoredProcessHandler h = (KillableColoredProcessHandler)myDebugProcess.getProcessHandler();
+
+      h.killProcess();
+    }
+    else {
+      myDebugProcess.getProcessHandler().destroyProcess();
+    }
+  }
+}
diff --git a/python/testSrc/com/jetbrains/env/python/dotNet/PyIronPythonTest.java b/python/testSrc/com/jetbrains/env/python/dotNet/PyIronPythonTest.java
new file mode 100644
index 0000000..a6f9dd6
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/python/dotNet/PyIronPythonTest.java
@@ -0,0 +1,145 @@
+package com.jetbrains.env.python.dotNet;
+
+import com.intellij.openapi.application.ReadAction;
+import com.intellij.openapi.application.Result;
+import com.jetbrains.env.PyEnvTestCase;
+import com.jetbrains.python.psi.PyFile;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Assert;
+
+/**
+ * IronPython.NET specific tests
+ *
+ * @author Ilya.Kazakevich
+ */
+public class PyIronPythonTest extends PyEnvTestCase {
+
+  /**
+   * IronPython tag
+   */
+  static final String IRON_TAG = "iron";
+
+  public PyIronPythonTest() {
+    super(IRON_TAG);
+  }
+
+  /**
+   * Tests skeleton generation
+   */
+  public void testSkeletons() throws Exception {
+    runPythonTest(new SkeletonTestTask(
+      "dotNet/expected.skeleton.java.py",
+      "com.just.like.java",
+      "testSkeleton.py",
+      null
+    ));
+  }
+
+  /**
+   * Tests skeleton generation with "from" statements
+   */
+  public void testClassFromModule() throws Exception {
+    runPythonTest(new SkeletonTestTask(
+      "dotNet/expected.skeleton.java.py",
+      "com.just.like.java",
+      "import_class_from_module.py",
+      null
+    ));
+  }
+
+  /**
+   * Tests skeleton generation when imported as alias
+   */
+  public void testClassFromModuleAlias() throws Exception {
+    runPythonTest(new SkeletonTestTask(
+      "dotNet/expected.skeleton.java.py",
+      "com.just.like.java",
+      "import_class_from_module_alias.py",
+      null
+    ));
+  }
+
+  /**
+   * Tests skeleton generation when module is imported
+   */
+  public void testModuleFromPackage() throws Exception {
+    runPythonTest(new SkeletonTestTask(
+      "dotNet/expected.skeleton.java.py",
+      "com.just.like.java",
+      "import_module_from_package.py",
+      null
+    ));
+  }
+
+  /**
+   * Tests skeleton generation when several classes are imported
+   */
+  public void testSeveralClasses() throws Exception {
+    runPythonTest(new SkeletonTestTask(
+      "dotNet/expected.skeleton.java.py",
+      "com.just.like.java",
+      "import_several_classes_from_module.py",
+      "com.just.like.java.LikeJavaClass"
+    ));
+  }
+
+  /**
+   * Tests skeletons for built-in classes. We can't compare files (CLR class may be changed from version to version),
+   * but we are sure there should be class System.Web.AspNetHostingPermissionLevel which is part of public API
+   */
+  public void testImportBuiltInSystem() throws Exception {
+    final SkeletonTestTask task = new SkeletonTestTask(
+      null,
+      "System.Web",
+      "import_system.py",
+      null
+    );
+    runPythonTest(task);
+    final PyFile skeleton = task.getGeneratedSkeleton();
+    new ReadAction() {
+      @Override
+      protected void run(@NotNull Result result) throws Throwable {
+        Assert.assertNotNull("System.Web does not contain class AspNetHostingPermissionLevel. Error generating stub? It has classes  " +
+                             skeleton.getTopLevelClasses(),
+                             skeleton.findTopLevelClass("AspNetHostingPermissionLevel"));
+      }
+    }.execute();
+
+  }
+
+  /**
+   * Test importing of inner classes
+   */
+  public void testImportInnerClass() throws Exception {
+    runPythonTest(new SkeletonTestTask(
+      "dotNet/expected.skeleton.Deep.py",
+      "SingleNameSpace.Some.Deep",
+      "inner_class.py",
+      null
+    ));
+  }
+
+  /**
+   * Test importing of the whole namespace
+   */
+  public void testWholeNameSpace() throws Exception {
+    runPythonTest(new SkeletonTestTask(
+      "dotNet/expected.skeleton.SingleNameSpace.py",
+      "SingleNameSpace",
+      "whole_namespace.py",
+      null
+    ));
+  }
+
+  /**
+   * Test importing of single class
+   */
+  public void testSingleClass() throws Exception {
+    runPythonTest(new SkeletonTestTask(
+      "dotNet/expected.skeleton.SingleNameSpace.py",
+      "SingleNameSpace",
+      "single_class.py",
+      null
+    ));
+  }
+}
diff --git a/python/testSrc/com/jetbrains/env/python/dotNet/SkeletonTestTask.java b/python/testSrc/com/jetbrains/env/python/dotNet/SkeletonTestTask.java
new file mode 100644
index 0000000..e10916c
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/python/dotNet/SkeletonTestTask.java
@@ -0,0 +1,142 @@
+package com.jetbrains.env.python.dotNet;
+
+import com.google.common.collect.Sets;
+import com.intellij.codeInsight.intention.IntentionAction;
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ex.QuickFixWrapper;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.progress.util.AbstractProgressIndicatorBase;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.util.ui.UIUtil;
+import com.jetbrains.env.PyExecutionFixtureTestTask;
+import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.PyNames;
+import com.jetbrains.python.inspections.quickfix.GenerateBinaryStubsFix;
+import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection;
+import com.jetbrains.python.psi.PyFile;
+import com.jetbrains.python.sdk.InvalidSdkException;
+import com.jetbrains.python.sdk.PythonSdkType;
+import com.jetbrains.python.sdkTools.SdkCreationType;
+import org.hamcrest.Matchers;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Assert;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Task for test that checks skeleton generation
+ *
+ * @author Ilya.Kazakevich
+ */
+class SkeletonTestTask extends PyExecutionFixtureTestTask {
+
+  /**
+   * Tags for this task to run
+   */
+  private static final Set<String> IRON_TAGS = Sets.newHashSet(PyIronPythonTest.IRON_TAG);
+  /**
+   * Number of seconds we wait for skeleton generation external process (should be enough)
+   */
+  private static final int SECONDS_TO_WAIT_FOR_SKELETON_GENERATION = 20;
+
+  @Nullable
+  private final String myExpectedSkeletonFile;
+  @NotNull
+  private final String myModuleNameToBeGenerated;
+  @NotNull
+  private final String mySourceFileToRunGenerationOn;
+  @NotNull
+  private final String myUseQuickFixWithThisModuleOnly;
+  private PyFile myGeneratedSkeleton;
+
+  /**
+   * @param expectedSkeletonFile          if you want test to compare generated result with some file, provide its name.
+   *                                      Pass null if you do not want to compare result with anything (you may do it yourself with {@link #getGeneratedSkeleton()})
+   * @param moduleNameToBeGenerated       name of module you think we should generate in dotted notation (like "System.Web" or "com.myModule").
+   *                                      System will wait for skeleton file for this module to be generated
+   * @param sourceFileToRunGenerationOn   Source file where we should run "generate stubs" on. Be sure to place "caret" on appropriate place!
+   * @param useQuickFixWithThisModuleOnly If there are several quick fixes in code, you may run fix only on this module.
+   *                                      Pass null if you are sure there would be only one quickfix
+   */
+  SkeletonTestTask(@Nullable final String expectedSkeletonFile,
+                   @NotNull final String moduleNameToBeGenerated,
+                   @NotNull final String sourceFileToRunGenerationOn,
+                   @Nullable final String useQuickFixWithThisModuleOnly) {
+    myExpectedSkeletonFile = expectedSkeletonFile;
+    myModuleNameToBeGenerated = moduleNameToBeGenerated.replace('.', '/');
+    mySourceFileToRunGenerationOn = sourceFileToRunGenerationOn;
+    myUseQuickFixWithThisModuleOnly = useQuickFixWithThisModuleOnly != null ? useQuickFixWithThisModuleOnly : "";
+  }
+
+
+  @Override
+  public void runTestOn(@NotNull final String sdkHome) throws IOException, InvalidSdkException {
+    final Sdk sdk = createTempSdk(sdkHome, SdkCreationType.SDK_PACKAGES_ONLY);
+    final File skeletonsPath = new File(PythonSdkType.getSkeletonsPath(PathManager.getSystemPath(), sdk.getHomePath()));
+    File skeletonFileOrDirectory = new File(skeletonsPath, myModuleNameToBeGenerated); // File with module skeleton
+
+    // Module may be stored in "moduleName.py" or "moduleName/__init__.py"
+    if (skeletonFileOrDirectory.isDirectory()) {
+      skeletonFileOrDirectory = new File(skeletonFileOrDirectory, PyNames.INIT_DOT_PY);
+    }
+    else {
+      skeletonFileOrDirectory = new File(skeletonFileOrDirectory.getAbsolutePath() + PyNames.DOT_PY);
+    }
+
+    final File skeletonFile = skeletonFileOrDirectory;
+
+    if (skeletonFile.exists()) { // To make sure we do not reuse it
+      assert skeletonFile.delete() : "Failed to delete file " + skeletonFile;
+    }
+
+    myFixture.copyFileToProject("dotNet/" + mySourceFileToRunGenerationOn, mySourceFileToRunGenerationOn); // File that uses CLR library
+    myFixture.copyFileToProject("dotNet/PythonLibs.dll", "PythonLibs.dll"); // Library itself
+    myFixture.copyFileToProject("dotNet/SingleNameSpace.dll", "SingleNameSpace.dll"); // Another library
+    myFixture.configureByFile(mySourceFileToRunGenerationOn);
+    myFixture.enableInspections(PyUnresolvedReferencesInspection.class); // This inspection should suggest us to generate stubs
+
+
+    UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+      @Override
+      public void run() {
+        PsiDocumentManager.getInstance(myFixture.getProject()).commitAllDocuments();
+        final String intentionName = PyBundle.message("sdk.gen.stubs.for.binary.modules", myUseQuickFixWithThisModuleOnly);
+        final IntentionAction intention = myFixture.findSingleIntention(intentionName);
+        Assert.assertNotNull("No intention found to generate skeletons!", intention);
+        Assert.assertThat("Intention should be quick fix to run", intention, Matchers.instanceOf(QuickFixWrapper.class));
+        final LocalQuickFix quickFix = ((QuickFixWrapper)intention).getFix();
+        Assert.assertThat("Quick fix should be 'generate binary skeletons' fix to run", quickFix,
+                          Matchers.instanceOf(GenerateBinaryStubsFix.class));
+        final Task fixTask = ((GenerateBinaryStubsFix)quickFix).getFixTask(myFixture.getFile());
+        fixTask.run(new AbstractProgressIndicatorBase());
+      }
+    });
+
+    FileUtil.copy(skeletonFile, new File(myFixture.getTempDirPath(), skeletonFile.getName()));
+    if (myExpectedSkeletonFile != null) {
+      myFixture.checkResultByFile(skeletonFile.getName(), myExpectedSkeletonFile, false);
+    }
+    myGeneratedSkeleton = (PyFile)myFixture.configureByFile(skeletonFile.getName());
+  }
+
+
+  @Override
+  public Set<String> getTags() {
+    return Collections.unmodifiableSet(IRON_TAGS);
+  }
+
+  /**
+   * @return File for generated skeleton. Call it after {@link #runTestOn(String)} only!
+   */
+  @NotNull
+  PyFile getGeneratedSkeleton() {
+    return myGeneratedSkeleton;
+  }
+}
diff --git a/python/testSrc/com/jetbrains/env/python/dotNet/package-info.java b/python/testSrc/com/jetbrains/env/python/dotNet/package-info.java
new file mode 100644
index 0000000..e56e620
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/python/dotNet/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * IronPython tests. Install IronPython, .NET and add "iron" to "tags.txt" file. Check for more info: http://confluence.jetbrains.com/display/PYINT/Env+Tests
+ * @author Ilya.Kazakevich
+ */
+package com.jetbrains.env.python.dotNet;
\ No newline at end of file
diff --git a/python/testSrc/com/jetbrains/env/python/testing/PythonDocTestingTest.java b/python/testSrc/com/jetbrains/env/python/testing/PythonDocTestingTest.java
new file mode 100644
index 0000000..411b604
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/python/testing/PythonDocTestingTest.java
@@ -0,0 +1,66 @@
+package com.jetbrains.env.python.testing;
+
+import com.jetbrains.env.PyEnvTestCase;
+import com.jetbrains.env.ut.PyDocTestTask;
+
+/**
+ * User : catherine
+ */
+public class PythonDocTestingTest extends PyEnvTestCase{
+  public void testUTRunner() {
+    runPythonTest(new PyDocTestTask("/testRunner/env/doc", "test1.py") {
+
+      @Override
+      public void after() {
+        assertEquals(3, allTestsCount());
+        assertEquals(3, passedTestsCount());
+        allTestsPassed();
+      }
+    });
+  }
+
+  public void testClass() {
+    runPythonTest(new PyDocTestTask("/testRunner/env/doc", "test1.py::FirstGoodTest") {
+
+      @Override
+      public void after() {
+        assertEquals(1, allTestsCount());
+        assertEquals(1, passedTestsCount());
+      }
+    });
+  }
+
+  public void testMethod() {
+    runPythonTest(new PyDocTestTask("/testRunner/env/doc", "test1.py::SecondGoodTest::test_passes") {
+
+      @Override
+      public void after() {
+        assertEquals(1, allTestsCount());
+        assertEquals(1, passedTestsCount());
+      }
+    });
+  }
+
+  public void testFunction() {
+    runPythonTest(new PyDocTestTask("/testRunner/env/doc", "test1.py::factorial") {
+
+      @Override
+      public void after() {
+        assertEquals(1, allTestsCount());
+        assertEquals(1, passedTestsCount());
+      }
+    });
+  }
+
+  public void testUTRunner2() {
+    runPythonTest(new PyDocTestTask("/testRunner/env/doc", "test2.py") {
+
+      @Override
+      public void after() {
+        assertEquals(3, allTestsCount());
+        assertEquals(1, passedTestsCount());
+        assertEquals(2, failedTestsCount());
+      }
+    });
+  }
+}
diff --git a/python/testSrc/com/jetbrains/env/python/testing/PythonNoseTestingTest.java b/python/testSrc/com/jetbrains/env/python/testing/PythonNoseTestingTest.java
new file mode 100644
index 0000000..33250ae
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/python/testing/PythonNoseTestingTest.java
@@ -0,0 +1,33 @@
+package com.jetbrains.env.python.testing;
+
+import com.jetbrains.env.PyEnvTestCase;
+import com.jetbrains.env.ut.PyNoseTestTask;
+
+/**
+ * User : catherine
+ */
+public class PythonNoseTestingTest extends PyEnvTestCase{
+  public void testNoseRunner() {
+    runPythonTest(new PyNoseTestTask("/testRunner/env/nose", "test1.py") {
+
+      @Override
+      public void after() {
+        assertEquals(3, allTestsCount());
+        assertEquals(3, passedTestsCount());
+        allTestsPassed();
+      }
+    });
+  }
+
+  public void testNoseRunner2() {
+    runPythonTest(new PyNoseTestTask("/testRunner/env/nose", "test2.py") {
+
+      @Override
+      public void after() {
+        assertEquals(8, allTestsCount());
+        assertEquals(5, passedTestsCount());
+        assertEquals(3, failedTestsCount());
+      }
+    });
+  }
+}
diff --git a/python/testSrc/com/jetbrains/env/python/testing/PythonPyTestingTest.java b/python/testSrc/com/jetbrains/env/python/testing/PythonPyTestingTest.java
new file mode 100644
index 0000000..ab3dee6
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/python/testing/PythonPyTestingTest.java
@@ -0,0 +1,33 @@
+package com.jetbrains.env.python.testing;
+
+import com.jetbrains.env.PyEnvTestCase;
+import com.jetbrains.env.ut.PyTestTestTask;
+
+/**
+ * User : catherine
+ */
+public class PythonPyTestingTest extends PyEnvTestCase{
+  public void testPytestRunner() {
+    runPythonTest(new PyTestTestTask("/testRunner/env/pytest", "test1.py") {
+
+      @Override
+      public void after() {
+        assertEquals(3, allTestsCount());
+        assertEquals(3, passedTestsCount());
+        allTestsPassed();
+      }
+    });
+  }
+
+  public void testPytestRunner2() {
+    runPythonTest(new PyTestTestTask("/testRunner/env/pytest", "test2.py") {
+
+      @Override
+      public void after() {
+        assertEquals(8, allTestsCount());
+        assertEquals(5, passedTestsCount());
+        assertEquals(3, failedTestsCount());
+      }
+    });
+  }
+}
diff --git a/python/testSrc/com/jetbrains/env/python/testing/PythonUnitTestingTest.java b/python/testSrc/com/jetbrains/env/python/testing/PythonUnitTestingTest.java
new file mode 100644
index 0000000..682eb81
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/python/testing/PythonUnitTestingTest.java
@@ -0,0 +1,78 @@
+package com.jetbrains.env.python.testing;
+
+import com.jetbrains.env.PyEnvTestCase;
+import com.jetbrains.env.ut.PyUnitTestTask;
+import junit.framework.Assert;
+
+/**
+ * @author traff
+ */
+public class PythonUnitTestingTest extends PyEnvTestCase{
+  public void testUTRunner() {
+    runPythonTest(new PyUnitTestTask("/testRunner/env/unit", "test1.py") {
+
+      @Override
+      public void after() {
+        Assert.assertEquals(2, allTestsCount());
+        Assert.assertEquals(2, passedTestsCount());
+        allTestsPassed();
+      }
+    });
+  }
+
+  public void testUTRunner2() {
+    runPythonTest(new PyUnitTestTask("/testRunner/env/unit", "test2.py") {
+
+      @Override
+      public void after() {
+        assertEquals(3, allTestsCount());
+        assertEquals(1, passedTestsCount());
+        assertEquals(2, failedTestsCount());
+      }
+    });
+  }
+
+  public void testClass() {
+    runPythonTest(new PyUnitTestTask("/testRunner/env/unit", "test_file.py::GoodTest") {
+
+      @Override
+      public void after() {
+        assertEquals(1, allTestsCount());
+        assertEquals(1, passedTestsCount());
+      }
+    });
+  }
+
+  public void testMethod() {
+    runPythonTest(new PyUnitTestTask("/testRunner/env/unit", "test_file.py::GoodTest::test_passes") {
+
+      @Override
+      public void after() {
+        assertEquals(1, allTestsCount());
+        assertEquals(1, passedTestsCount());
+      }
+    });
+  }
+
+  public void testFolder() {
+    runPythonTest(new PyUnitTestTask("/testRunner/env/unit", "test_folder/") {
+
+      @Override
+      public void after() {
+        assertEquals(5, allTestsCount());
+        assertEquals(3, passedTestsCount());
+      }
+    });
+  }
+
+  public void testDependent() {
+    runPythonTest(new PyUnitTestTask("/testRunner/env/unit", "dependentTests/test_my_class.py") {
+
+      @Override
+      public void after() {
+        assertEquals(1, allTestsCount());
+        assertEquals(1, passedTestsCount());
+      }
+    });
+  }
+}
diff --git a/python/testSrc/com/jetbrains/env/ut/PyDocTestTask.java b/python/testSrc/com/jetbrains/env/ut/PyDocTestTask.java
new file mode 100644
index 0000000..e8228d7
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/ut/PyDocTestTask.java
@@ -0,0 +1,24 @@
+package com.jetbrains.env.ut;
+
+import com.intellij.execution.configurations.ConfigurationFactory;
+import com.intellij.openapi.project.Project;
+import com.jetbrains.python.testing.PythonTestConfigurationType;
+
+/**
+ * User : catherine
+ */
+public abstract class PyDocTestTask extends PyUnitTestTask {
+  public PyDocTestTask(String workingFolder, String scriptName, String scriptParameters) {
+    super(workingFolder, scriptName, scriptParameters);
+  }
+
+  public PyDocTestTask(String workingFolder, String scriptName) {
+    this(workingFolder, scriptName, null);
+  }
+
+  public void runTestOn(String sdkHome) throws Exception {
+    final Project project = getProject();
+    final ConfigurationFactory factory = PythonTestConfigurationType.getInstance().PY_DOCTEST_FACTORY;
+    runConfiguration(factory, sdkHome, project);
+  }
+}
diff --git a/python/testSrc/com/jetbrains/env/ut/PyNoseTestTask.java b/python/testSrc/com/jetbrains/env/ut/PyNoseTestTask.java
new file mode 100644
index 0000000..e130dd5
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/ut/PyNoseTestTask.java
@@ -0,0 +1,32 @@
+package com.jetbrains.env.ut;
+
+import com.google.common.collect.ImmutableSet;
+import com.intellij.execution.configurations.ConfigurationFactory;
+import com.intellij.openapi.project.Project;
+import com.jetbrains.python.testing.PythonTestConfigurationType;
+
+import java.util.Set;
+
+/**
+ * User : catherine
+ */
+public abstract class PyNoseTestTask extends PyUnitTestTask {
+  public PyNoseTestTask(String workingFolder, String scriptName, String scriptParameters) {
+    super(workingFolder, scriptName, scriptParameters);
+  }
+
+  public PyNoseTestTask(String workingFolder, String scriptName) {
+    this(workingFolder, scriptName, null);
+  }
+
+  public void runTestOn(String sdkHome) throws Exception {
+    final Project project = getProject();
+    final ConfigurationFactory factory = PythonTestConfigurationType.getInstance().PY_NOSETEST_FACTORY;
+    runConfiguration(factory, sdkHome, project);
+  }
+
+  @Override
+  public Set<String> getTags() {
+    return ImmutableSet.of("nose");
+  }
+}
diff --git a/python/testSrc/com/jetbrains/env/ut/PyTestTestTask.java b/python/testSrc/com/jetbrains/env/ut/PyTestTestTask.java
new file mode 100644
index 0000000..1e42611
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/ut/PyTestTestTask.java
@@ -0,0 +1,40 @@
+package com.jetbrains.env.ut;
+
+import com.google.common.collect.ImmutableSet;
+import com.intellij.execution.configurations.ConfigurationFactory;
+import com.intellij.openapi.project.Project;
+import com.jetbrains.python.testing.AbstractPythonTestRunConfiguration;
+import com.jetbrains.python.testing.PythonTestConfigurationType;
+import com.jetbrains.python.testing.pytest.PyTestRunConfiguration;
+
+import java.util.Set;
+
+/**
+ * User : catherine
+ */
+public abstract class PyTestTestTask extends PyUnitTestTask {
+  public PyTestTestTask(String workingFolder, String scriptName, String scriptParameters) {
+    super(workingFolder, scriptName, scriptParameters);
+  }
+
+  public PyTestTestTask(String workingFolder, String scriptName) {
+    this(workingFolder, scriptName, null);
+  }
+
+  public void runTestOn(String sdkHome) throws Exception {
+    final Project project = getProject();
+    final ConfigurationFactory factory = PythonTestConfigurationType.getInstance().PY_PYTEST_FACTORY;
+    runConfiguration(factory, sdkHome, project);
+  }
+
+  @Override
+  public Set<String> getTags() {
+    return ImmutableSet.of("pytest");
+  }
+
+  protected void configure(AbstractPythonTestRunConfiguration config) {
+    if (config instanceof PyTestRunConfiguration)
+      ((PyTestRunConfiguration)config).setTestToRun(getScriptPath());
+  }
+
+}
diff --git a/python/testSrc/com/jetbrains/env/ut/PyUnitTestTask.java b/python/testSrc/com/jetbrains/env/ut/PyUnitTestTask.java
new file mode 100644
index 0000000..01401ca
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/ut/PyUnitTestTask.java
@@ -0,0 +1,241 @@
+package com.jetbrains.env.ut;
+
+import com.google.common.collect.Lists;
+import com.intellij.execution.*;
+import com.intellij.execution.configurations.ConfigurationFactory;
+import com.intellij.execution.executors.DefaultRunExecutor;
+import com.intellij.execution.process.ProcessAdapter;
+import com.intellij.execution.process.ProcessEvent;
+import com.intellij.execution.process.ProcessHandler;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.execution.runners.ExecutionEnvironmentBuilder;
+import com.intellij.execution.runners.ProgramRunner;
+import com.intellij.execution.testframework.Filter;
+import com.intellij.execution.testframework.sm.runner.SMTestProxy;
+import com.intellij.execution.testframework.sm.runner.ui.SMTRunnerConsoleView;
+import com.intellij.execution.testframework.sm.runner.ui.TestResultsViewer;
+import com.intellij.execution.ui.RunContentDescriptor;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.Key;
+import com.intellij.util.ui.UIUtil;
+import com.intellij.xdebugger.XDebuggerTestUtil;
+import com.jetbrains.env.PyExecutionFixtureTestTask;
+import com.jetbrains.python.sdk.PythonEnvUtil;
+import com.jetbrains.python.sdk.flavors.JythonSdkFlavor;
+import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
+import com.jetbrains.python.testing.AbstractPythonTestRunConfiguration;
+import com.jetbrains.python.testing.PythonTestConfigurationType;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Assert;
+
+/**
+ * @author traff
+ */
+public abstract class PyUnitTestTask extends PyExecutionFixtureTestTask {
+
+  protected ProcessHandler myProcessHandler;
+  private boolean shouldPrintOutput = false;
+  private SMTestProxy.SMRootTestProxy myTestProxy;
+  private boolean mySetUp = false;
+  private SMTRunnerConsoleView myConsoleView;
+  private RunContentDescriptor myDescriptor;
+  private StringBuilder myOutput;
+
+  public PyUnitTestTask() {
+  }
+
+  public PyUnitTestTask(String workingFolder, String scriptName, String scriptParameters) {
+    setWorkingFolder(getTestDataPath() + workingFolder);
+    setScriptName(scriptName);
+    setScriptParameters(scriptParameters);
+  }
+
+  public PyUnitTestTask(String workingFolder, String scriptName) {
+    this(workingFolder, scriptName, null);
+  }
+
+  @Override
+  public void setUp(final String testName) throws Exception {
+    UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+      @Override
+      public void run() {
+        try {
+          if (myFixture == null) {
+            PyUnitTestTask.super.setUp(testName);
+            mySetUp = true;
+          }
+        }
+        catch (Exception e) {
+          throw new RuntimeException(e);
+        }
+      }
+    });
+  }
+
+  @NotNull
+  protected String output() {
+    return myOutput.toString();
+  }
+
+  @Override
+  public void tearDown() throws Exception {
+    UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+      @Override
+      public void run() {
+        try {
+          if (mySetUp) {
+            if (myConsoleView != null) {
+              Disposer.dispose(myConsoleView);
+              myConsoleView = null;
+            }
+            if (myDescriptor != null) {
+              Disposer.dispose(myDescriptor);
+              myDescriptor = null;
+            }
+
+
+            PyUnitTestTask.super.tearDown();
+
+            mySetUp = false;
+          }
+        }
+        catch (Exception e) {
+          throw new RuntimeException(e);
+        }
+      }
+    }
+
+    );
+  }
+
+  @Override
+  public void runTestOn(String sdkHome) throws Exception {
+    final Project project = getProject();
+    final ConfigurationFactory factory = PythonTestConfigurationType.getInstance().PY_UNITTEST_FACTORY;
+    runConfiguration(factory, sdkHome, project);
+  }
+
+  protected void runConfiguration(ConfigurationFactory factory, String sdkHome, final Project project) throws Exception {
+    final RunnerAndConfigurationSettings settings =
+      RunManager.getInstance(project).createRunConfiguration("test", factory);
+
+    AbstractPythonTestRunConfiguration config = (AbstractPythonTestRunConfiguration)settings.getConfiguration();
+
+
+    config.setSdkHome(sdkHome);
+    config.setScriptName(getScriptPath());
+    config.setWorkingDirectory(getWorkingFolder());
+
+    PythonSdkFlavor sdk = PythonSdkFlavor.getFlavor(sdkHome);
+
+
+    if (sdk instanceof JythonSdkFlavor) {
+      config.setInterpreterOptions(JythonSdkFlavor.getPythonPathCmdLineArgument(Lists.<String>newArrayList(getWorkingFolder())));
+    }
+    else {
+      PythonEnvUtil.addToPythonPath(config.getEnvs(), getWorkingFolder());
+    }
+
+
+    configure(config);
+
+    new WriteAction() {
+      @Override
+      protected void run(@NotNull Result result) throws Throwable {
+        RunManagerEx.getInstanceEx(project).addConfiguration(settings, false);
+        RunManagerEx.getInstanceEx(project).setSelectedConfiguration(settings);
+        Assert.assertSame(settings, RunManagerEx.getInstanceEx(project).getSelectedConfiguration());
+      }
+    }.execute();
+
+    final ExecutionEnvironment environment = ExecutionEnvironmentBuilder.create(DefaultRunExecutor.getRunExecutorInstance(), settings).build();
+    //noinspection ConstantConditions
+    Assert.assertTrue(environment.getRunner().canRun(DefaultRunExecutor.EXECUTOR_ID, config));
+
+    before();
+
+    final com.intellij.util.concurrency.Semaphore s = new com.intellij.util.concurrency.Semaphore();
+    s.down();
+
+    myOutput = new StringBuilder();
+
+    UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+      @Override
+      public void run() {
+        try {
+          environment.getRunner().execute(environment, new ProgramRunner.Callback() {
+            @Override
+            public void processStarted(RunContentDescriptor descriptor) {
+              myDescriptor = descriptor;
+              myProcessHandler = myDescriptor.getProcessHandler();
+              myProcessHandler.addProcessListener(new ProcessAdapter() {
+                @Override
+                public void onTextAvailable(ProcessEvent event, Key outputType) {
+                  myOutput.append(event.getText());
+                }
+              });
+              myConsoleView = (com.intellij.execution.testframework.sm.runner.ui.SMTRunnerConsoleView)descriptor.getExecutionConsole();
+              myTestProxy = myConsoleView.getResultsViewer().getTestsRootNode();
+              myConsoleView.getResultsViewer().addEventsListener(new TestResultsViewer.SMEventsAdapter() {
+                @Override
+                public void onTestingFinished(TestResultsViewer sender) {
+                  s.up();
+                }
+              });
+            }
+          });
+        }
+        catch (Exception e) {
+          throw new RuntimeException(e);
+        }
+      }
+    });
+
+    Assert.assertTrue(s.waitFor(60000));
+
+    XDebuggerTestUtil.waitForSwing();
+
+    assertFinished();
+
+    Assert.assertTrue(output(), allTestsCount() > 0);
+
+    after();
+
+    disposeProcess(myProcessHandler);
+  }
+
+  protected void configure(AbstractPythonTestRunConfiguration config) {
+  }
+
+  public void allTestsPassed() {
+    Assert.assertEquals(output(), 0, myTestProxy.getChildren(Filter.NOT_PASSED).size());
+    Assert.assertEquals(output(), 0, failedTestsCount());
+  }
+
+  public int failedTestsCount() {
+    return myTestProxy.collectChildren(NOT_SUIT.and(Filter.FAILED_OR_INTERRUPTED)).size();
+  }
+
+  public int passedTestsCount() {
+    return myTestProxy.collectChildren(NOT_SUIT.and(Filter.PASSED)).size();
+  }
+
+  public void assertFinished() {
+    Assert.assertTrue("State is " + myTestProxy.getMagnitudeInfo().getTitle() + "\n" + output(),
+                      myTestProxy.wasLaunched() && !myTestProxy.wasTerminated());
+  }
+
+  public int allTestsCount() {
+    return myTestProxy.collectChildren(NOT_SUIT).size();
+  }
+
+  public static final Filter<SMTestProxy> NOT_SUIT = new Filter<SMTestProxy>() {
+    @Override
+    public boolean shouldAccept(SMTestProxy test) {
+      return !test.isSuite();
+    }
+  };
+}
diff --git a/python/testSrc/com/jetbrains/python/PyAddImportTest.java b/python/testSrc/com/jetbrains/python/PyAddImportTest.java
index 01f8384..f95187a 100644
--- a/python/testSrc/com/jetbrains/python/PyAddImportTest.java
+++ b/python/testSrc/com/jetbrains/python/PyAddImportTest.java
@@ -17,8 +17,13 @@
 
 import com.intellij.openapi.application.Result;
 import com.intellij.openapi.command.WriteCommandAction;
+import com.intellij.psi.PsiPolyVariantReference;
 import com.jetbrains.python.codeInsight.imports.AddImportHelper;
+import com.jetbrains.python.fixtures.PyResolveTestCase;
 import com.jetbrains.python.fixtures.PyTestCase;
+import com.jetbrains.python.psi.PyElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 /**
  * @author yole
@@ -53,4 +58,46 @@
     }.execute();
     myFixture.checkResultByFile("addImport/" + getTestName(true) + ".after.py");
   }
+
+  // PY-6020
+  public void testLocalFromImport() {
+    doAddLocalImport("foo", "package.module");
+  }
+
+  // PY-6020
+  public void testLocalImport() {
+    doAddLocalImport("module", null);
+  }
+
+  // PY-13668
+  public void testLocalImportInlineFunctionBody() {
+    testLocalImport();
+  }
+
+  // PY-13668
+  public void testLocalImportInlineBranch() {
+    testLocalImport();
+  }
+
+  /**
+   * Add local import statement
+   * @param name      reference name in corresponding import element
+   * @param qualifier if not {@code null} form {@code from qualifier import name} will be used, otherwise {@code import name}
+   */
+  private void doAddLocalImport(@NotNull final String name, @Nullable final String qualifier) {
+    myFixture.configureByFile("addImport/" + getTestName(true) + ".py");
+    new WriteCommandAction(myFixture.getProject(), myFixture.getFile()) {
+      @Override
+      protected void run(Result result) throws Throwable {
+        final PsiPolyVariantReference reference = PyResolveTestCase.findReferenceByMarker(myFixture.getFile());
+        if (qualifier != null) {
+          AddImportHelper.addLocalFromImportStatement((PyElement)reference.getElement(), qualifier, name);
+        }
+        else {
+          AddImportHelper.addLocalImportStatement((PyElement)reference.getElement(), name);
+        }
+      }
+    }.execute();
+    myFixture.checkResultByFile("addImport/" + getTestName(true) + ".after.py");
+  }
 }
diff --git a/python/testSrc/com/jetbrains/python/PyTypeTest.java b/python/testSrc/com/jetbrains/python/PyTypeTest.java
index 88243f0..b0e41bc 100644
--- a/python/testSrc/com/jetbrains/python/PyTypeTest.java
+++ b/python/testSrc/com/jetbrains/python/PyTypeTest.java
@@ -829,6 +829,12 @@
            "    pass\n");
   }
 
+  // PY-12801
+  public void testTupleConcatenation() {
+    doTest("(int, bool, str)",
+           "expr = (1,) + (True, 'spam') + ()");
+  }
+
   private static TypeEvalContext getTypeEvalContext(@NotNull PyExpression element) {
     return TypeEvalContext.userInitiated(element.getContainingFile()).withTracing();
   }
diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java
index f365eea..a1366ac 100644
--- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java
+++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java
@@ -626,23 +626,85 @@
   }
 
   // PY-4073
-  public void testSpecialFunctionAttributes() throws Exception {
-    setLanguageLevel(LanguageLevel.PYTHON27);
-    try {
-      List<String> suggested = doTestByText("def func(): pass; func.func_<caret>");
-      assertNotNull(suggested);
-      assertContainsElements(suggested,
-                             "func_defaults", "func_globals", "func_closure",
-                             "func_code", "func_name", "func_doc", "func_dict");
+  public void testFunctionSpecialAttributes() {
+    runWithLanguageLevel(LanguageLevel.PYTHON27, new Runnable() {
+      @Override
+      public void run() {
+        List<String> suggested = doTestByText("def func(): pass; func.func_<caret>");
+        assertNotNull(suggested);
+        assertContainsElements(suggested, PyNames.LEGACY_FUNCTION_SPECIAL_ATTRIBUTES);
 
-      suggested = doTestByText("def func(): pass; func.__<caret>");
-      assertNotNull(suggested);
-      assertContainsElements(suggested, "__defaults__", "__globals__", "__closure__",
-                             "__code__", "__name__", "__doc__", "__dict__", "__module__");
-      assertDoesntContain(suggested, "__annotations__", "__kwdefaults__");
-    }
-    finally {
-      setLanguageLevel(null);
-    }
+        suggested = doTestByText("def func(): pass; func.__<caret>");
+        assertNotNull(suggested);
+        assertContainsElements(suggested, PyNames.FUNCTION_SPECIAL_ATTRIBUTES);
+        assertDoesntContain(suggested, PyNames.PY3_ONLY_FUNCTION_SPECIAL_ATTRIBUTES);
+      }
+    });
+  }
+
+  // PY-9342
+  public void testBoundMethodSpecialAttributes() {
+    List<String>  suggested = doTestByText("{}.update.im_<caret>");
+    assertNotNull(suggested);
+    assertContainsElements(suggested, PyNames.LEGACY_METHOD_SPECIAL_ATTRIBUTES);
+
+    suggested = doTestByText("{}.update.__<caret>");
+    assertNotNull(suggested);
+    assertContainsElements(suggested, PyNames.METHOD_SPECIAL_ATTRIBUTES);
+    assertDoesntContain(suggested, PyNames.FUNCTION_SPECIAL_ATTRIBUTES);
+  }
+
+  // PY-9342
+  public void testWeakQualifierBoundMethodAttributes() {
+    assertUnderscoredMethodSpecialAttributesSuggested();
+  }
+
+  private void assertUnderscoredMethodSpecialAttributesSuggested() {
+    myFixture.configureByFile("completion/" + getTestName(true) + ".py");
+    myFixture.completeBasic();
+    final List<String> suggested = myFixture.getLookupElementStrings();
+    assertNotNull(suggested);
+    assertContainsElements(suggested, PyNames.METHOD_SPECIAL_ATTRIBUTES);
+    assertDoesntContain(suggested, PyNames.FUNCTION_SPECIAL_ATTRIBUTES);
+  }
+
+  // PY-9342
+  public void testUnboundMethodSpecialAttributes() {
+    runWithLanguageLevel(LanguageLevel.PYTHON27, new Runnable() {
+      @Override
+      public void run() {
+        assertUnderscoredMethodSpecialAttributesSuggested();
+      }
+    });
+    runWithLanguageLevel(LanguageLevel.PYTHON32, new Runnable() {
+      @Override
+      public void run() {
+        assertUnderscoredFunctionAttributesSuggested();
+      }
+    });
+  }
+
+  // PY-9342
+  public void testStaticMethodSpecialAttributes() {
+    assertUnderscoredFunctionAttributesSuggested();
+  }
+
+  // PY-9342
+  public void testLambdaSpecialAttributes() {
+    assertUnderscoredFunctionAttributesSuggested();
+  }
+
+  // PY-9342
+  public void testReassignedMethodSpecialAttributes() {
+    assertUnderscoredMethodSpecialAttributesSuggested();
+  }
+
+  private void assertUnderscoredFunctionAttributesSuggested() {
+    myFixture.configureByFile("completion/" + getTestName(true) + ".py");
+    myFixture.completeBasic();
+    final List<String> suggested = myFixture.getLookupElementStrings();
+    assertNotNull(suggested);
+    assertContainsElements(suggested, PyNames.FUNCTION_SPECIAL_ATTRIBUTES);
+    assertDoesntContain(suggested, PyNames.METHOD_SPECIAL_ATTRIBUTES);
   }
 }
diff --git a/python/testSrc/com/jetbrains/python/fixtures/PyCommandLineTestCase.java b/python/testSrc/com/jetbrains/python/fixtures/PyCommandLineTestCase.java
index bbaf312..65815c1 100644
--- a/python/testSrc/com/jetbrains/python/fixtures/PyCommandLineTestCase.java
+++ b/python/testSrc/com/jetbrains/python/fixtures/PyCommandLineTestCase.java
@@ -20,16 +20,15 @@
 import com.intellij.execution.Executor;
 import com.intellij.execution.configurations.ConfigurationFactory;
 import com.intellij.execution.configurations.ConfigurationType;
-import com.intellij.execution.configurations.GeneralCommandLine;
 import com.intellij.execution.executors.DefaultDebugExecutor;
 import com.intellij.execution.executors.DefaultRunExecutor;
-import com.intellij.execution.runners.ExecutionEnvironment;
 import com.intellij.execution.runners.ExecutionEnvironmentBuilder;
 import com.intellij.openapi.project.Project;
 import com.jetbrains.python.PythonHelpersLocator;
 import com.jetbrains.python.debugger.PyDebugRunner;
 import com.jetbrains.python.run.AbstractPythonRunConfiguration;
 import com.jetbrains.python.run.PythonCommandLineState;
+import org.jetbrains.annotations.NotNull;
 
 import java.util.List;
 
@@ -59,11 +58,9 @@
 
   protected List<String> buildRunCommandLine(AbstractPythonRunConfiguration configuration) {
     try {
-      final Executor executor = DefaultRunExecutor.getRunExecutorInstance();
-      ExecutionEnvironment env = new ExecutionEnvironmentBuilder(myFixture.getProject(), executor).setRunProfile(configuration).build();
-      final PythonCommandLineState state = (PythonCommandLineState)configuration.getState(executor, env);
-      final GeneralCommandLine generalCommandLine = state.generateCommandLine();
-      return generalCommandLine.getParametersList().getList();
+      PythonCommandLineState state = getState(configuration, DefaultRunExecutor.getRunExecutorInstance());
+      assert state != null;
+      return state.generateCommandLine().getParametersList().getList();
     }
     catch (ExecutionException e) {
       throw new RuntimeException(e);
@@ -72,15 +69,21 @@
 
   protected List<String> buildDebugCommandLine(AbstractPythonRunConfiguration configuration) {
     try {
-      final Executor executor = DefaultDebugExecutor.getDebugExecutorInstance();
-      ExecutionEnvironment env = new ExecutionEnvironmentBuilder(myFixture.getProject(), executor).setRunProfile(configuration).build();
-      final PythonCommandLineState state = (PythonCommandLineState)configuration.getState(executor, env);
-      final GeneralCommandLine generalCommandLine =
-        state.generateCommandLine(PyDebugRunner.createCommandLinePatchers(configuration.getProject(), state, configuration, PORT));
-      return generalCommandLine.getParametersList().getList();
+      PythonCommandLineState state = getState(configuration, DefaultDebugExecutor.getDebugExecutorInstance());
+      assert state != null;
+      return state.generateCommandLine(PyDebugRunner.createCommandLinePatchers(configuration.getProject(), state, configuration, PORT))
+        .getParametersList()
+        .getList();
     }
     catch (ExecutionException e) {
       throw new RuntimeException(e);
     }
   }
+
+
+  private static PythonCommandLineState getState(@NotNull AbstractPythonRunConfiguration configuration, @NotNull Executor executor) throws ExecutionException {
+    return (PythonCommandLineState)ExecutionEnvironmentBuilder.create(executor, configuration)
+      .build()
+      .getState();
+  }
 }
diff --git a/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java b/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java
index 45d582a..99da2a4 100644
--- a/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java
+++ b/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java
@@ -18,6 +18,8 @@
 import com.intellij.codeInsight.intention.IntentionAction;
 import com.intellij.codeInspection.LocalQuickFix;
 import com.intellij.codeInspection.ex.QuickFixWrapper;
+import com.intellij.find.findUsages.CustomUsageSearcher;
+import com.intellij.find.findUsages.FindUsagesOptions;
 import com.intellij.openapi.application.PathManager;
 import com.intellij.openapi.extensions.Extensions;
 import com.intellij.openapi.module.Module;
@@ -33,6 +35,7 @@
 import com.intellij.psi.PsiDocumentManager;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiReference;
+import com.intellij.psi.search.searches.ReferencesSearch;
 import com.intellij.testFramework.LightProjectDescriptor;
 import com.intellij.testFramework.PlatformTestCase;
 import com.intellij.testFramework.TestDataPath;
@@ -42,6 +45,10 @@
 import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory;
 import com.intellij.testFramework.fixtures.TestFixtureBuilder;
 import com.intellij.testFramework.fixtures.impl.LightTempDirTestFixtureImpl;
+import com.intellij.usageView.UsageInfo;
+import com.intellij.usages.Usage;
+import com.intellij.usages.rules.PsiElementUsage;
+import com.intellij.util.CommonProcessors.CollectProcessor;
 import com.jetbrains.python.PythonHelpersLocator;
 import com.jetbrains.python.PythonMockSdk;
 import com.jetbrains.python.PythonModuleTypeBase;
@@ -55,6 +62,9 @@
 import org.jetbrains.annotations.Nullable;
 
 import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 
 /**
  * @author yole
@@ -184,6 +194,36 @@
     myFixture.getEditor().getCaretModel().moveToOffset(element.getTextOffset());
   }
 
+  /**
+   * Finds all usages of element. Works much like method in {@link com.intellij.testFramework.fixtures.CodeInsightTestFixture#findUsages(com.intellij.psi.PsiElement)},
+   * but supports {@link com.intellij.find.findUsages.CustomUsageSearcher} and {@link com.intellij.psi.search.searches.ReferencesSearch} as well
+   *
+   * @param element what to find
+   * @return usages
+   */
+  @NotNull
+  protected Collection<PsiElement> findUsage(@NotNull final PsiElement element) {
+    final Collection<PsiElement> result = new ArrayList<PsiElement>();
+    final CollectProcessor<Usage> usageCollector = new CollectProcessor<Usage>();
+    for (final CustomUsageSearcher searcher : CustomUsageSearcher.EP_NAME.getExtensions()) {
+      searcher.processElementUsages(element, usageCollector, new FindUsagesOptions(myFixture.getProject()));
+    }
+    for (final Usage usage : usageCollector.getResults()) {
+      if (usage instanceof PsiElementUsage) {
+        result.add(((PsiElementUsage)usage).getElement());
+      }
+    }
+    for (final PsiReference reference : ReferencesSearch.search(element).findAll()) {
+      result.add(reference.getElement());
+    }
+
+    for (final UsageInfo info : myFixture.findUsages(element)) {
+      result.add(info.getElement());
+    }
+
+    return result;
+  }
+
   protected static class PyLightProjectDescriptor implements LightProjectDescriptor {
     private final String myPythonVersion;
 
diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java
index a0a2a12..3451712 100644
--- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java
+++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java
@@ -371,6 +371,11 @@
     doMultiFileTest();
   }
 
+  // PY-9342
+  public void testMethodSpecialAttributes() {
+    doTest();
+  }
+
   // PY-11472
   public void testUnusedImportBeforeStarImport() {
     doMultiFileTest();
diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceConstantTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceConstantTest.java
index 769e720..a54c68a 100644
--- a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceConstantTest.java
+++ b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceConstantTest.java
@@ -51,7 +51,7 @@
   public void testSuggestUniqueNames() {  // PY-4409
     doTestSuggestions(PyExpression.class, "S1");
   }
-  
+
   public void testSuggestUniqueNamesGlobalScope() {  // PY-4409
     doTestSuggestions(PyExpression.class, "S1");
   }
@@ -60,6 +60,11 @@
     doTestInplace(null);
   }
 
+  // PY-13484
+  public void testFromParameterDefaultValue() {
+    doTest();
+  }
+
   @Override
   protected String getTestDataPath() {
     return super.getTestDataPath() + "/refactoring/introduceConstant";
diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java
index f211797..33a191f 100644
--- a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java
+++ b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java
@@ -245,6 +245,8 @@
     }
   }
 
+  public void testSelectionBreaksBinaryOperator() {doTest();}
+
   private void doTestCannotPerform() {
     boolean thrownExpectedException = false;
     try {
diff --git a/python/testSrc/com/jetbrains/python/sdkTools/PyTestSdkTools.java b/python/testSrc/com/jetbrains/python/sdkTools/PyTestSdkTools.java
new file mode 100644
index 0000000..2d97e74
--- /dev/null
+++ b/python/testSrc/com/jetbrains/python/sdkTools/PyTestSdkTools.java
@@ -0,0 +1,142 @@
+package com.jetbrains.python.sdkTools;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkModificator;
+import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil;
+import com.intellij.openapi.roots.ModuleRootModificationUtil;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.testFramework.UsefulTestCase;
+import com.jetbrains.python.sdk.InvalidSdkException;
+import com.jetbrains.python.sdk.PythonSdkType;
+import com.jetbrains.python.sdk.skeletons.PySkeletonRefresher;
+import com.jetbrains.python.sdk.skeletons.SkeletonVersionChecker;
+import org.hamcrest.Matchers;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Assert;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Engine to create SDK for tests.
+ * See {@link #createTempSdk(com.intellij.openapi.vfs.VirtualFile, SdkCreationType, com.intellij.openapi.module.Module)}
+ *
+ * @author Ilya.Kazakevich
+ */
+public final class PyTestSdkTools {
+
+  private static final Sdk[] NO_SDK = new Sdk[0];
+
+  private PyTestSdkTools() {
+
+  }
+
+  /**
+   * Creates SDK by its path and associates it with module.
+   *
+   * @param sdkHome         path to sdk
+   * @param sdkCreationType SDK creation strategy (see {@link SdkCreationType} doc)
+   * @return sdk
+   */
+  @NotNull
+  public static Sdk createTempSdk(@NotNull final VirtualFile sdkHome,
+                                  @NotNull final SdkCreationType sdkCreationType,
+                                  @NotNull final Module module
+  )
+    throws InvalidSdkException, IOException {
+    final Ref<Sdk> ref = Ref.create();
+    UsefulTestCase.edt(new Runnable() {
+
+      @Override
+      public void run() {
+        final Sdk sdk = SdkConfigurationUtil.setupSdk(NO_SDK, sdkHome, PythonSdkType.getInstance(), true, null, null);
+        Assert.assertNotNull("Failed to create SDK on " + sdkHome, sdk);
+        ref.set(sdk);
+      }
+    });
+    final Sdk sdk = ref.get();
+    if (sdkCreationType != SdkCreationType.EMPTY_SDK) {
+      generateTempSkeletonsOrPackages(sdk, sdkCreationType == SdkCreationType.SDK_PACKAGES_AND_SKELETONS, module);
+    }
+    UsefulTestCase.edt(new Runnable() {
+      @Override
+      public void run() {
+        SdkConfigurationUtil.addSdk(sdk);
+      }
+    });
+    return sdk;
+  }
+
+
+  /**
+   * Adds installed eggs to SDK, generates skeletons (optionally) and associates it with modle.
+   *
+   * @param sdk          sdk to process
+   * @param addSkeletons add skeletons or only packages
+   * @param module       module to associate with
+   * @throws InvalidSdkException bas sdk
+   * @throws IOException         failed to read eggs
+   */
+  private static void generateTempSkeletonsOrPackages(@NotNull final Sdk sdk,
+                                                      final boolean addSkeletons,
+                                                      @NotNull final Module module)
+    throws InvalidSdkException, IOException {
+    final Project project = module.getProject();
+    ModuleRootModificationUtil.setModuleSdk(module, sdk);
+
+    UsefulTestCase.edt(new Runnable() {
+      @Override
+      public void run() {
+        ApplicationManager.getApplication().runWriteAction(new Runnable() {
+          @Override
+          public void run() {
+            ProjectRootManager.getInstance(project).setProjectSdk(sdk);
+          }
+        });
+      }
+    });
+
+
+    final SdkModificator modificator = sdk.getSdkModificator();
+    modificator.removeRoots(OrderRootType.CLASSES);
+
+    for (final String path : PythonSdkType.getSysPathsFromScript(sdk.getHomePath())) {
+      PythonSdkType.addSdkRoot(modificator, path);
+    }
+    if (!addSkeletons) {
+      UsefulTestCase.edt(new Runnable() {
+        @Override
+        public void run() {
+          modificator.commitChanges();
+        }
+      });
+      return;
+    }
+
+    final File tempDir = FileUtil.createTempDirectory(PyTestSdkTools.class.getName(), null);
+    final File skeletonsDir = new File(tempDir, PythonSdkType.SKELETON_DIR_NAME);
+    FileUtil.createDirectory(skeletonsDir);
+    final String skeletonsPath = skeletonsDir.toString();
+    PythonSdkType.addSdkRoot(modificator, skeletonsPath);
+
+    UsefulTestCase.edt(new Runnable() {
+      @Override
+      public void run() {
+        modificator.commitChanges();
+      }
+    });
+
+    final SkeletonVersionChecker checker = new SkeletonVersionChecker(0);
+    final PySkeletonRefresher refresher = new PySkeletonRefresher(project, null, sdk, skeletonsPath, null, null);
+    final List<String> errors = refresher.regenerateSkeletons(checker, null);
+    Assert.assertThat("Errors found", errors, Matchers.empty());
+  }
+}
diff --git a/python/testSrc/com/jetbrains/python/sdkTools/SdkCreationType.java b/python/testSrc/com/jetbrains/python/sdkTools/SdkCreationType.java
new file mode 100644
index 0000000..231f243
--- /dev/null
+++ b/python/testSrc/com/jetbrains/python/sdkTools/SdkCreationType.java
@@ -0,0 +1,20 @@
+package com.jetbrains.python.sdkTools;
+
+/**
+ * SDK creation type
+ * @author Ilya.Kazakevich
+ */
+public enum SdkCreationType {
+  /**
+   * SDK only (no packages nor skeletons)
+   */
+  EMPTY_SDK,
+  /**
+   * SDK + installed packages from syspath
+   */
+  SDK_PACKAGES_ONLY,
+  /**
+   * SDK + installed packages from syspath + skeletons
+   */
+  SDK_PACKAGES_AND_SKELETONS
+}
diff --git a/python/testSrc/com/jetbrains/python/sdkTools/package-info.java b/python/testSrc/com/jetbrains/python/sdkTools/package-info.java
new file mode 100644
index 0000000..8b8fb74
--- /dev/null
+++ b/python/testSrc/com/jetbrains/python/sdkTools/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * Engine to create SDK in tests.
+ * See {@link com.jetbrains.python.sdkTools.PyTestSdkTools}
+ * @author Ilya.Kazakevich
+ */
+package com.jetbrains.python.sdkTools;
\ No newline at end of file
diff --git a/python/testSrc/python-community-tests.iml b/python/testSrc/python-community-tests.iml
index 6511d88..6198034 100644
--- a/python/testSrc/python-community-tests.iml
+++ b/python/testSrc/python-community-tests.iml
@@ -16,6 +16,9 @@
     <orderEntry type="module" module-name="python-ide-community" />
     <orderEntry type="module" module-name="python-helpers" scope="RUNTIME" />
     <orderEntry type="library" name="Groovy" level="project" />
+    <orderEntry type="module" module-name="xdebugger-impl" scope="TEST" />
+    <orderEntry type="module" module-name="python-pydev" scope="TEST" />
+    <orderEntry type="module" module-name="smRunner" scope="TEST" />
   </component>
 </module>