Wing Tipshttps://wingware.com/Tips, tricks, and helpful hints for Wingware's Python IDEsFri, 19 Jul 2019 02:54:10 GMTPyRSS2Gen-1.0.0http://www.dalkescientific.com/Python/PyRSS2Gen.htmlExtending Wing with Python (Part 4 of 4)https://wingware.com/hints/scripting-4<p>In this issue of <a class="reference" href="/hints">Wing Tips</a> we continue to look at how to extend Wing's functionality, by taking a closer look at at the scripting API and writing up a more complex script.</p> <p>If you haven't read the previous installments of this series, you may want to take a look at <a class="reference" href="/hints/scripting-1">Part 1</a> where we introduced Wing's scripting framework and set up auto-completion for the scripting API, <a class="reference" href="/hints/scripting-2">Part 2</a> where we used Wing to debug itself for easier extension script development, and <a class="reference" href="/hints/scripting-3">Part 3</a> where we looked at how to collect arguments from the user.</p> <div class="section"> <h3 class="title-3">Overview of the Scripting API</h3> <p>Wing's formal scripting API is found in the file <tt class="literal"><span class="pre">wingapi.py</span></tt>, which is located inside <tt class="literal"><span class="pre">src</span></tt> in the <tt class="literal"><span class="pre">Install</span> <span class="pre">Directory</span></tt> listed in Wing's <tt class="literal"><span class="pre">About</span></tt> box. This is the API that we worked with in the previous three installments of this series. It lets you control the application and its configuration, run and inspect code in the debugger, open and alter files in an editor, create and manage projects, search files and directories, and access Wing's static analysis of Python code.</p> <p>The API is divided into a number of classes for accessing each of these areas of functionality:</p> <div class="bullet-list"><ul><li><span class="bullet"> <tt class="literal"><span class="pre">CAPIApplication</span></tt> is a singleton found in <tt class="literal"><span class="pre">wingapi.gApplication</span></tt>. This is the the main point of access to the application, with support for creating and accessing windows, editors, projects, tools, the debugger, child processes, preferences, source code analysis, and other parts of Wing's functionality.<p>Some of this functionality is exposed through the other API classes listed below. Other parts of Wing's functionality is instead accessed through the method <tt class="literal"><span class="pre">CAPIApplication.ExecuteCommand()</span></tt>, which can invoke all of the <a class="reference" href="/doc/commands/index">documented commands</a> which are not exposed directly through the Python API. Keyword arguments can be passed to commands that take them, for example <tt class="literal"><span class="pre">ExecuteCommand('replace-string',</span> <span class="pre">search_string=&quot;tset&quot;,</span> <span class="pre">replace_string=&quot;test&quot;)</span></tt></p> <p><tt class="literal"><span class="pre">CAPIApplication</span></tt> also provides access to all of Wing's <a class="reference" href="/doc/preferences/index">documented preferences</a> with <tt class="literal"><span class="pre">GetPreference()</span></tt> and <tt class="literal"><span class="pre">SetPreference()</span></tt>.</p> </span></li> <li><span class="bullet"> <tt class="literal"><span class="pre">CAPIDebugger</span></tt> and <tt class="literal"><span class="pre">CAPIDebugRunState</span></tt> allow controlling the debugger and debug processes.</span></li> <li><span class="bullet"> <tt class="literal"><span class="pre">CAPIDocument</span></tt> and <tt class="literal"><span class="pre">CAPIEditor</span></tt> implement document and editor functionality. Each <tt class="literal"><span class="pre">CAPIDocument</span></tt> may be displayed in one or more <tt class="literal"><span class="pre">CAPIEditor</span></tt> instances in different splits in the user interface.</span></li> <li><span class="bullet"> <tt class="literal"><span class="pre">CAPIProject</span></tt> lets you create projects, access project-level functionality, and alter project configuration.</span></li> <li><span class="bullet"> <tt class="literal"><span class="pre">CAPISearch</span></tt> provides access to Wing's search capabilities, to search files or directories.</span></li> <li><span class="bullet"> <tt class="literal"><span class="pre">CAPIStaticAnalysis</span></tt> is available for each <tt class="literal"><span class="pre">CAPIDocument</span></tt> that contains Python, to access Wing's static analysis of that file. Information on each symbol found in code is encapsulated in <tt class="literal"><span class="pre">CAPISymbolInfo</span></tt>.</span></li> </ul></div><p>Scripts also have access to the entire Python standard library in the version of Python that Wing uses internally to run itself (currently 2.7)</p> <p>The <a class="reference" href="/doc/scripting/api-reference">API Reference</a> details the entire API and documentation is displayed in the <tt class="literal"><span class="pre">Source</span> <span class="pre">Assistant</span></tt> in Wing as you work with <tt class="literal"><span class="pre">wingapi</span></tt>, provided that you configured your project for scripting as described in <a class="reference" href="/hints/scripting-1">Part 1</a> of this series.</p> </div> <div class="section"> <h3 class="title-3">A More Advanced Scripting Example</h3> <p>As a final example, let's look at a simplified test code generator, that creates a <tt class="literal"><span class="pre">unittest</span></tt> testing skeleton for a selected class, opens it side by side with the code being tested, and scrolls to the method being tested as you move through the test skeleton. Here is the complete solution, with comments explaining each part:</p> <div class="python-highlight"><pre><span class="kn">import</span> <span class="nn">os</span> <span class="kn">import</span> <span class="nn">wingapi</span> <span class="n">g_last_target</span> <span class="o">=</span> <span class="p">[</span><span class="bp">None</span><span class="p">]</span> <span class="k">def</span> <span class="nf">generate_tests_for_class</span><span class="p">():</span> <span class="sd">&quot;&quot;&quot;Generate a test skeleton for the class at the current editor focus, open</span> <span class="sd"> it side by side with the original source, and set up automatic navigation</span> <span class="sd"> of the source being tested.&quot;&quot;&quot;</span> <span class="c"># Collect objects we need to work with: The app and the editor we&#39;re starting from</span> <span class="n">app</span> <span class="o">=</span> <span class="n">wingapi</span><span class="o">.</span><span class="n">gApplication</span> <span class="n">code_editor</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">GetActiveEditor</span><span class="p">()</span> <span class="c"># Try to find a class at current editor focus</span> <span class="n">fn</span><span class="p">,</span> <span class="n">scope</span> <span class="o">=</span> <span class="n">_find_class</span><span class="p">(</span><span class="n">code_editor</span><span class="p">)</span> <span class="k">if</span> <span class="n">scope</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span> <span class="n">app</span><span class="o">.</span><span class="n">ShowMessageDialog</span><span class="p">(</span><span class="s">&quot;No Class Found&quot;</span><span class="p">,</span> <span class="s">&quot;Could not generate tests: &quot;</span> <span class="s">&quot;No class was found at current focus&quot;</span><span class="p">)</span> <span class="k">return</span> <span class="c"># Get the methods for the class</span> <span class="n">code_ana</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">GetAnalysis</span><span class="p">(</span><span class="n">fn</span><span class="p">)</span> <span class="n">contents</span> <span class="o">=</span> <span class="n">code_ana</span><span class="o">.</span><span class="n">GetScopeContents</span><span class="p">(</span><span class="s">&#39;.&#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">scope</span><span class="p">),</span> <span class="n">timeout</span><span class="o">=</span><span class="mf">3.0</span><span class="p">)</span> <span class="n">methods</span> <span class="o">=</span> <span class="p">[</span><span class="n">s</span> <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">contents</span> <span class="k">if</span> <span class="s">&#39;method&#39;</span> <span class="ow">in</span> <span class="n">contents</span><span class="p">[</span><span class="n">s</span><span class="p">]]</span> <span class="c"># Create the test skeleton, with one test for each public method</span> <span class="n">output</span> <span class="o">=</span> <span class="p">[</span> <span class="s">&#39;import unittest&#39;</span><span class="p">,</span> <span class="s">&#39;&#39;</span><span class="p">,</span> <span class="s">&#39;class Test{}(unittest.TestCase):&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">scope</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]),</span> <span class="p">]</span> <span class="k">for</span> <span class="n">method</span> <span class="ow">in</span> <span class="n">methods</span><span class="p">:</span> <span class="k">if</span> <span class="n">method</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s">&#39;_&#39;</span><span class="p">):</span> <span class="k">continue</span> <span class="n">output</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span> <span class="s">&#39;&#39;</span><span class="p">,</span> <span class="n">_indent</span><span class="p">(</span><span class="n">code_editor</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="s">&#39;def test{}(self):&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">method</span><span class="p">),</span> <span class="n">_indent</span><span class="p">(</span><span class="n">code_editor</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s">&#39;pass&#39;</span><span class="p">,</span> <span class="p">])</span> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">output</span><span class="p">)</span> <span class="o">==</span> <span class="mi">3</span><span class="p">:</span> <span class="n">output</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span> <span class="s">&#39;&#39;</span><span class="p">,</span> <span class="n">_indent</span><span class="p">(</span><span class="n">code_editor</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span><span class="s">&#39;pass&#39;</span><span class="p">,</span> <span class="p">])</span> <span class="c"># Set up a side-by-side mode after saving the current visual state</span> <span class="n">visual_state</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">GetVisualState</span><span class="p">(</span><span class="n">style</span><span class="o">=</span><span class="s">&#39;tools-and-editors&#39;</span><span class="p">)</span> <span class="n">app</span><span class="o">.</span><span class="n">ExecuteCommand</span><span class="p">(</span><span class="s">&#39;unsplit&#39;</span><span class="p">)</span> <span class="n">app</span><span class="o">.</span><span class="n">ExecuteCommand</span><span class="p">(</span><span class="s">&#39;split-horizontally&#39;</span><span class="p">)</span> <span class="c"># Open up the test skeleton in an unsaved file with name based on the original code</span> <span class="n">test_fn</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">fn</span><span class="p">),</span> <span class="s">&#39;test_{}&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">basename</span><span class="p">(</span><span class="n">fn</span><span class="p">)))</span> <span class="n">test_editor</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">OpenEditor</span><span class="p">(</span><span class="n">test_fn</span><span class="p">)</span> <span class="n">test_editor</span><span class="o">.</span><span class="n">GetDocument</span><span class="p">()</span><span class="o">.</span><span class="n">SetText</span><span class="p">(</span><span class="n">code_editor</span><span class="o">.</span><span class="n">GetEol</span><span class="p">()</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">output</span><span class="p">))</span> <span class="c"># Connect to the test editor so we can show the matching method in the source</span> <span class="c"># when the caret moves around within the tests</span> <span class="k">def</span> <span class="nf">track_position</span><span class="p">(</span><span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">):</span> <span class="n">lineno</span> <span class="o">=</span> <span class="n">test_editor</span><span class="o">.</span><span class="n">GetDocument</span><span class="p">()</span><span class="o">.</span><span class="n">GetLineNumberFromPosition</span><span class="p">(</span><span class="n">start</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span> <span class="n">test_ana</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">GetAnalysis</span><span class="p">(</span><span class="n">test_editor</span><span class="o">.</span><span class="n">GetDocument</span><span class="p">()</span><span class="o">.</span><span class="n">GetFilename</span><span class="p">())</span> <span class="n">scope</span> <span class="o">=</span> <span class="n">test_ana</span><span class="o">.</span><span class="n">FindScopeContainingLine</span><span class="p">(</span><span class="n">lineno</span><span class="p">)</span> <span class="n">scope</span> <span class="o">=</span> <span class="n">scope</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">&#39;.&#39;</span><span class="p">)</span> <span class="k">if</span> <span class="n">scope</span><span class="p">:</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">scope</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s">&#39;Test&#39;</span><span class="p">)</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">scope</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s">&#39;test&#39;</span><span class="p">):</span> <span class="k">return</span> <span class="n">scope</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">scope</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="nb">len</span><span class="p">(</span><span class="s">&#39;Test&#39;</span><span class="p">):]</span> <span class="n">scope</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">scope</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="nb">len</span><span class="p">(</span><span class="s">&#39;test&#39;</span><span class="p">):]</span> <span class="n">scope</span> <span class="o">=</span> <span class="n">_find_symbol</span><span class="p">(</span><span class="n">code_ana</span><span class="p">,</span> <span class="n">scope</span><span class="p">,</span> <span class="s">&#39;method&#39;</span><span class="p">)</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">scope</span><span class="p">:</span> <span class="k">return</span> <span class="k">if</span> <span class="n">g_last_target</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="n">scope</span><span class="p">:</span> <span class="k">return</span> <span class="n">g_last_target</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">scope</span> <span class="n">infos</span> <span class="o">=</span> <span class="n">code_ana</span><span class="o">.</span><span class="n">GetSymbolInfo</span><span class="p">(</span><span class="s">&#39;.&#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">scope</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]),</span> <span class="n">scope</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">infos</span><span class="p">:</span> <span class="k">return</span> <span class="n">lineno</span> <span class="o">=</span> <span class="n">infos</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">lineStart</span> <span class="n">start</span> <span class="o">=</span> <span class="n">code_editor</span><span class="o">.</span><span class="n">GetDocument</span><span class="p">()</span><span class="o">.</span><span class="n">GetLineStart</span><span class="p">(</span><span class="n">lineno</span><span class="p">)</span> <span class="o">+</span> <span class="n">infos</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">pos</span> <span class="n">end</span> <span class="o">=</span> <span class="n">start</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">scope</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span> <span class="n">code_editor</span><span class="o">.</span><span class="n">ScrollToLine</span><span class="p">(</span><span class="nb">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">lineno</span><span class="o">-</span><span class="mi">2</span><span class="p">),</span> <span class="n">select</span><span class="o">=</span><span class="p">(</span><span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">),</span> <span class="n">pos</span><span class="o">=</span><span class="s">&#39;top&#39;</span><span class="p">,</span> <span class="n">callout</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> <span class="n">test_editor</span><span class="o">.</span><span class="n">Connect</span><span class="p">(</span><span class="s">&#39;selection-changed&#39;</span><span class="p">,</span> <span class="n">track_position</span><span class="p">)</span> <span class="c"># Connect to the test editor so we can restore the prior visual state when it is closed</span> <span class="k">def</span> <span class="nf">restore_state</span><span class="p">(</span><span class="n">unused</span><span class="p">):</span> <span class="n">app</span><span class="o">.</span><span class="n">SetVisualState</span><span class="p">(</span><span class="n">visual_state</span><span class="p">)</span> <span class="n">test_editor</span><span class="o">.</span><span class="n">Connect</span><span class="p">(</span><span class="s">&#39;destroy&#39;</span><span class="p">,</span> <span class="n">restore_state</span><span class="p">)</span> <span class="n">app</span><span class="o">.</span><span class="n">ShowMessageDialog</span><span class="p">(</span><span class="s">&quot;Tests Generated&quot;</span><span class="p">,</span> <span class="s">&quot;Tests for class {} have been generated. &quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">scope</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span> <span class="o">+</span> <span class="s">&quot;As you fill in the tests, the method being tested is shown on the left as you &quot;</span> <span class="s">&quot;move around the test file on the right. Closing the tests will return Wing to &quot;</span> <span class="s">&quot;the previous visual state.&quot;</span><span class="p">)</span> <span class="c"># Command configuration</span> <span class="k">def</span> <span class="nf">_generate_tests_for_class_available</span><span class="p">():</span> <span class="n">ed</span> <span class="o">=</span> <span class="n">wingapi</span><span class="o">.</span><span class="n">gApplication</span><span class="o">.</span><span class="n">GetActiveEditor</span><span class="p">()</span> <span class="k">if</span> <span class="n">ed</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span> <span class="k">return</span> <span class="bp">False</span> <span class="n">mime</span> <span class="o">=</span> <span class="n">wingapi</span><span class="o">.</span><span class="n">gApplication</span><span class="o">.</span><span class="n">GetMimeType</span><span class="p">(</span><span class="n">ed</span><span class="o">.</span><span class="n">GetDocument</span><span class="p">()</span><span class="o">.</span><span class="n">GetFilename</span><span class="p">())</span> <span class="k">if</span> <span class="n">mime</span> <span class="o">!=</span> <span class="s">&#39;text/x-python&#39;</span><span class="p">:</span> <span class="k">return</span> <span class="bp">False</span> <span class="k">return</span> <span class="n">_find_class</span><span class="p">(</span><span class="n">ed</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span> <span class="n">generate_tests_for_class</span><span class="o">.</span><span class="n">available</span> <span class="o">=</span> <span class="n">_generate_tests_for_class_available</span> <span class="n">generate_tests_for_class</span><span class="o">.</span><span class="n">label</span> <span class="o">=</span> <span class="s">&quot;Generate Tests for Class&quot;</span> <span class="n">generate_tests_for_class</span><span class="o">.</span><span class="n">contexts</span> <span class="o">=</span> <span class="p">[</span><span class="n">wingapi</span><span class="o">.</span><span class="n">kContextNewMenu</span><span class="p">(</span><span class="s">&#39;Scripts&#39;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)]</span> <span class="c"># Utilities</span> <span class="k">def</span> <span class="nf">_indent</span><span class="p">(</span><span class="n">ed</span><span class="p">,</span> <span class="n">level</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;Get indentation that matches the file we&#39;re writing the test for&quot;&quot;&quot;</span> <span class="n">indent_style</span> <span class="o">=</span> <span class="n">ed</span><span class="o">.</span><span class="n">GetIndentStyle</span><span class="p">()</span> <span class="k">if</span> <span class="n">indent_style</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span> <span class="k">return</span> <span class="s">&#39; &#39;</span> <span class="o">*</span> <span class="n">ed</span><span class="o">.</span><span class="n">GetIndentSize</span><span class="p">()</span> <span class="o">*</span> <span class="n">level</span> <span class="k">elif</span> <span class="n">indent_style</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span> <span class="k">return</span> <span class="s">&#39;</span><span class="se">\t</span><span class="s">&#39;</span> <span class="o">*</span> <span class="n">level</span> <span class="k">else</span><span class="p">:</span> <span class="n">tabs</span> <span class="o">=</span> <span class="n">ed</span><span class="o">.</span><span class="n">GetIndentSize</span><span class="p">()</span> <span class="o">*</span> <span class="n">level</span> <span class="o">/</span> <span class="n">ed</span><span class="o">.</span><span class="n">GetTabSize</span><span class="p">()</span> <span class="n">spaces</span> <span class="o">=</span> <span class="n">ed</span><span class="o">.</span><span class="n">GetIndentSize</span><span class="p">()</span> <span class="o">*</span> <span class="n">level</span> <span class="o">-</span> <span class="p">(</span><span class="n">tabs</span> <span class="o">*</span> <span class="n">ed</span><span class="o">.</span><span class="n">GetTabSize</span><span class="p">)</span> <span class="k">return</span> <span class="s">&#39;</span><span class="se">\t</span><span class="s">&#39;</span> <span class="o">*</span> <span class="n">tabs</span> <span class="o">+</span> <span class="s">&#39; &#39;</span> <span class="o">*</span> <span class="n">spaces</span> <span class="k">def</span> <span class="nf">_find_symbol</span><span class="p">(</span><span class="n">ana</span><span class="p">,</span> <span class="n">scope</span><span class="p">,</span> <span class="n">symbol_type</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;Find the outermost symbol of given type in the given scope&quot;&quot;&quot;</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">scope</span><span class="p">)):</span> <span class="n">infos</span> <span class="o">=</span> <span class="n">ana</span><span class="o">.</span><span class="n">GetSymbolInfo</span><span class="p">(</span><span class="s">&#39;.&#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">scope</span><span class="p">[:</span><span class="n">i</span><span class="p">]),</span> <span class="n">scope</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="k">for</span> <span class="n">info</span> <span class="ow">in</span> <span class="n">infos</span><span class="p">:</span> <span class="k">if</span> <span class="n">symbol_type</span> <span class="ow">in</span> <span class="n">info</span><span class="o">.</span><span class="n">generalType</span><span class="p">:</span> <span class="k">return</span> <span class="n">scope</span><span class="p">[:</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="k">return</span> <span class="bp">None</span> <span class="k">def</span> <span class="nf">_find_class</span><span class="p">(</span><span class="n">ed</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;Find the current top-level class in the given code editor&quot;&quot;&quot;</span> <span class="k">if</span> <span class="n">ed</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span> <span class="k">return</span> <span class="bp">None</span><span class="p">,</span> <span class="bp">None</span> <span class="n">scope</span> <span class="o">=</span> <span class="n">ed</span><span class="o">.</span><span class="n">GetSourceScope</span><span class="p">()</span> <span class="k">if</span> <span class="n">scope</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span> <span class="k">return</span> <span class="bp">None</span><span class="p">,</span> <span class="bp">None</span> <span class="n">fn</span><span class="p">,</span> <span class="n">lineno</span> <span class="o">=</span> <span class="n">scope</span><span class="p">[:</span><span class="mi">2</span><span class="p">]</span> <span class="n">ana</span> <span class="o">=</span> <span class="n">wingapi</span><span class="o">.</span><span class="n">gApplication</span><span class="o">.</span><span class="n">GetAnalysis</span><span class="p">(</span><span class="n">fn</span><span class="p">)</span> <span class="n">scope</span> <span class="o">=</span> <span class="n">_find_symbol</span><span class="p">(</span><span class="n">ana</span><span class="p">,</span> <span class="n">scope</span><span class="p">[</span><span class="mi">2</span><span class="p">:],</span> <span class="s">&#39;class&#39;</span><span class="p">)</span> <span class="k">return</span> <span class="n">fn</span><span class="p">,</span> <span class="n">scope</span> </pre></div> </div> <div class="section"> <h3 class="title-3">Key Concepts</h3> <p>The important things to observe in this example are:</p> <p>(1) Wing's source code analyzer is used to find the current class, in the utilities <tt class="literal"><span class="pre">_find_class</span></tt> and <tt class="literal"><span class="pre">_find_symbol</span></tt>: <tt class="literal"><span class="pre">GetSourceScope()</span></tt> is called to get the scope list for the current position in the active editor, and this is inspected with <tt class="literal"><span class="pre">GetSymbolInfo()</span></tt> to find the outermost class in the path to the current scope. For example, if you are within a nested function <tt class="literal"><span class="pre">nested_function</span></tt> in a method <tt class="literal"><span class="pre">MyMethod</span></tt> of class <tt class="literal"><span class="pre">MyClass</span></tt>, your scope is <tt class="literal"><span class="pre">[&quot;MyClass&quot;,</span> <span class="pre">&quot;MyMethod&quot;,</span> <span class="pre">&quot;nested_function&quot;]</span></tt> and the script needs to determine that <tt class="literal"><span class="pre">MyClass</span></tt> is the outermost class.</p> <p>(2) The attributes in the class are found with <tt class="literal"><span class="pre">GetScopeContents()</span></tt> and then narrowed to contain only methods using the filter utility <tt class="literal"><span class="pre">_find_symbol</span></tt> that was also used to find the class.</p> <p>(3) The script uses <tt class="literal"><span class="pre">ExecuteCommand</span></tt> to invoke other Wing commands (<tt class="literal"><span class="pre">unsplit</span></tt> and <tt class="literal"><span class="pre">split-horizontally</span></tt>) in order to set up the side-by-side display mode. This is how you can access all of the <a class="reference" href="/doc/commands/index">documented commands</a>. Since calling these commands changes the layout of the user inteface, the original layout is saved with <tt class="literal"><span class="pre">GetVisualState</span></tt> and restored later.</p> <p>(4) Signal connections made with <tt class="literal"><span class="pre">Connect</span></tt> are used on the editor to hook functionality to specific events in the user interface. In this case, the <tt class="literal"><span class="pre">selection-changed</span></tt> signal on the editor that contains the generated test skeleton is used to scroll the original source to the method that is being tested. The script also connects to <tt class="literal"><span class="pre">destroy</span></tt> on the same editor, in order to terminate the session and restore the original visual layout when the generated test skeleton is closed. Most of the classes in the API provide signals like this. Although there are none in this example, regularly occurring tasks may also be scheduled, using <tt class="literal"><span class="pre">wingapi.gApplication.InstallTimeout()</span></tt>.</p> </div> <div class="section"> <h3 class="title-3">Try it Out</h3> <p>To try the example, copy the following into a new file in the <tt class="literal"><span class="pre">scripts</span></tt> directory in Wing's settings directory (listed in Wing's About box) and use <tt class="literal"><span class="pre">Reload</span> <span class="pre">All</span> <span class="pre">Scripts</span></tt> so Wing discovers and starts watching and reloading the new script file when it changes. A <tt class="literal"><span class="pre">Scripts</span></tt> menu with item <tt class="literal"><span class="pre">Generate</span> <span class="pre">Tests</span> <span class="pre">for</span> <span class="pre">Class</span></tt> should appear in the menu bar. If the menu item does not appear, try restarting Wing (this may be needed on Windows in some versions of Wing).</p> <p>In order for this to work, you will need open a Python file with a class that has public methods (with names not starting with <tt class="literal"><span class="pre">_</span></tt> underscore) and move the caret into the class. If you don't have one, open <tt class="literal"><span class="pre">src/wingapi.py</span></tt> from your Wing installation and use one of the classes in that file.</p> <p>After you invoke the command, it splits the display, places the original code on the left and the test skeleton on the right, and displays a message dialog:</p> <img src="https://wingware.com/images/blog/scripting-4/side-by-side.png" alt="/images/blog/scripting-4/side-by-side.png" backrefs="" class="doc-image" dupnames="" height="658" ids="" names="" width="1092" /><p>If you close the dialog and move the caret around the test skeleton, <tt class="literal"><span class="pre">track_position()</span></tt> in the script will be called each time the editor selection changes in order to track your movement in the original source code like this:</p> <img src="https://wingware.com/images/blog/scripting-4/tracking.gif" alt="/images/blog/scripting-4/tracking.gif" backrefs="" class="doc-image" dupnames="" ids="" names="" /><p>Closing the test skeleton (with or without saving it) will invoke <tt class="literal"><span class="pre">restore_state</span></tt> to return Wing's display to how it was before you invoked the script to generate the skeleton.</p> </div> <div class="section"> <h3 class="title-3">Further Reading</h3> <p>A real world version of this example would need to merge new test classes and methods into an existing file, allow entering test mode without generating a new skeleton, keep tests in file or alphabetical order, add <tt class="literal"><span class="pre">setUp</span></tt> and <tt class="literal"><span class="pre">tearDown</span></tt> methods or other boilerplate, match the active test framework for the current project, and make other refinements. All of these are feasible using Wing's scripting API.</p> <p>For more information on writing extension scripts, see <a class="reference" href="/doc/scripting/index">Scripting and Extending Wing</a> in the documentation.</p> <p>Other example scripts can be found in the <tt class="literal"><span class="pre">scripts</span></tt> directory in your Wing installation, and extensions may be contributed to other users at <a class="reference" href="https://bitbucket.org/sdeibel/wing-contrib">wing-contrib</a>.</p> <br> <br><p>That's it for now! We'll be back in the next <a class="reference" href="/hints">Wing Tip</a> with more helpful hints for Wing Python IDE.</p> </div> https://wingware.com/hints/scripting-4Wed, 10 Jul 2019 01:00:00 GMTExtending Wing with Python (Part 3 of 4)https://wingware.com/hints/scripting-3<p>In this issue of <a class="reference" href="/hints">Wing Tips</a> we continue to look at how to extend Wing's functionality, by taking a look at extension scripts that collect arguments from the user.</p> <p>This article assumes you already know how to add and try out extension scripts in Wing. If you haven't read the previous installments of this series, you may want to take a look at <a class="reference" href="/hints/scripting-1">Part 1</a> where we introduced Wing's scripting framework and set up auto-completion for the scripting API, and <a class="reference" href="/hints/scripting-2">Part 2</a> where we used Wing to debug itself for easier extension script development.</p> <div class="section"> <h3 class="title-3">Script Arguments</h3> <p>Extension scripts may be defined with arguments, which Wing will try to collect from the user if not specified. For example:</p> <div class="python-highlight"><pre><span class="kn">import</span> <span class="nn">wingapi</span> <span class="k">def</span> <span class="nf">test_argument</span><span class="p">(</span><span class="n">text</span><span class="p">):</span> <span class="n">wingapi</span><span class="o">.</span><span class="n">gApplication</span><span class="o">.</span><span class="n">ShowMessageDialog</span><span class="p">(</span><span class="s">&quot;Result&quot;</span><span class="p">,</span> <span class="s">&quot;You typed: {}&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">text</span><span class="p">))</span> </pre></div> <p>After the script is loaded, you can use <tt class="literal"><span class="pre">Command</span> <span class="pre">by</span> <span class="pre">Name</span></tt> from the <tt class="literal"><span class="pre">Edit</span></tt> menu to execute it by typing <tt class="literal"><span class="pre">test-argument</span></tt> as the command name. Because the type of argument <tt class="literal"><span class="pre">text</span></tt> is undefined, Wing collects it as a string in an entry area shown at the bottom of the IDE window:</p> <img src="https://wingware.com/images/blog/scripting-3/arg-collection.png" alt="/images/blog/scripting-3/arg-collection.png" backrefs="" class="doc-image" dupnames="" height="35px" ids="" names="" width="247px" /><p>In key bindings and added toolbar items, it is often useful to provide the value of an argument as part of the invocation like this: <tt class="literal"><span class="pre">test-argument(text=&quot;hello&quot;)</span></tt></p> <p>This displays the message dialog without first prompting for the value of <tt class="literal"><span class="pre">text</span></tt>:</p> <img src="https://wingware.com/images/blog/scripting-3/arg-collection-result.png" alt="/images/blog/scripting-3/arg-collection-result.png" backrefs="" class="doc-image" dupnames="" height="118px" ids="" names="" width="163px" /><p>Script arguments must always be passed using the keyword <tt class="literal"><span class="pre">arg=value</span></tt> form. If a script has multiple arguments and only some are specified in its invocation, Wing collects only the unspecifed arguments from the user.</p> <p>Default values may be specified in the script definition, in order to avoid collecting the argument from the user if it is not given in the invocation:</p> <div class="python-highlight"><pre><span class="kn">import</span> <span class="nn">wingapi</span> <span class="k">def</span> <span class="nf">test_argument</span><span class="p">(</span><span class="n">text</span><span class="o">=</span><span class="s">&quot;default&quot;</span><span class="p">):</span> <span class="n">wingapi</span><span class="o">.</span><span class="n">gApplication</span><span class="o">.</span><span class="n">ShowMessageDialog</span><span class="p">(</span><span class="s">&quot;Result&quot;</span><span class="p">,</span> <span class="s">&quot;You typed: {}&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">text</span><span class="p">))</span> </pre></div> <p>This simplest form, without specifying argument type or interface, is sufficient for many scripting tasks that require argument collection.</p> </div> <div class="section"> <h3 class="title-3">Argument Type and Interface</h3> <p>Extension scripts may also specify the type and interface to use for arguments by by setting the <tt class="literal"><span class="pre">arginfo</span></tt> function attribute on the script. This is a map from argument name to a specification that includes documentation, data type, argument collection interface, and a label.</p> <p>This example collects two arguments, a filename and a choice from a popup menu:</p> <div class="python-highlight"><pre><span class="kn">import</span> <span class="nn">wingapi</span> <span class="kn">from</span> <span class="nn">wingutils</span> <span class="kn">import</span> <span class="n">datatype</span> <span class="kn">from</span> <span class="nn">guiutils</span> <span class="kn">import</span> <span class="n">formbuilder</span> <span class="k">def</span> <span class="nf">test_arg_entry</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">word</span><span class="p">):</span> <span class="n">wingapi</span><span class="o">.</span><span class="n">gApplication</span><span class="o">.</span><span class="n">ShowMessageDialog</span><span class="p">(</span><span class="s">&#39;Choice {}&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">word</span><span class="p">),</span> <span class="s">&quot;You chose: {}&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">filename</span><span class="p">))</span> <span class="n">_choices</span> <span class="o">=</span> <span class="p">[</span> <span class="p">(</span><span class="s">&quot;Default&quot;</span><span class="p">,</span> <span class="bp">None</span><span class="p">),</span> <span class="bp">None</span><span class="p">,</span> <span class="p">(</span><span class="s">&quot;One&quot;</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="s">&quot;Two&quot;</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="p">(</span><span class="s">&quot;Three&quot;</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="p">]</span> <span class="n">test_arg_entry</span><span class="o">.</span><span class="n">arginfo</span> <span class="o">=</span> <span class="p">{</span> <span class="s">&#39;filename&#39;</span><span class="p">:</span> <span class="n">wingapi</span><span class="o">.</span><span class="n">CArgInfo</span><span class="p">(</span> <span class="s">&quot;The filename to enter&quot;</span><span class="p">,</span> <span class="c"># The tooltip shown to use over this field</span> <span class="n">datatype</span><span class="o">.</span><span class="n">CType</span><span class="p">(</span><span class="s">&#39;&#39;</span><span class="p">),</span> <span class="c"># The data type is string</span> <span class="n">formbuilder</span><span class="o">.</span><span class="n">CFileSelectorGui</span><span class="p">(),</span> <span class="c"># Use a file selection field to collect the value</span> <span class="s">&quot;Filename:&quot;</span> <span class="c"># The field label</span> <span class="p">),</span> <span class="s">&#39;word&#39;</span><span class="p">:</span> <span class="n">wingapi</span><span class="o">.</span><span class="n">CArgInfo</span><span class="p">(</span> <span class="s">&quot;The word to enter&quot;</span><span class="p">,</span> <span class="n">datatype</span><span class="o">.</span><span class="n">CType</span><span class="p">(</span><span class="s">&#39;&#39;</span><span class="p">),</span> <span class="n">formbuilder</span><span class="o">.</span><span class="n">CPopupChoiceGui</span><span class="p">(</span><span class="n">_choices</span><span class="p">),</span> <span class="c"># Use a popup menu to collect this value</span> <span class="s">&quot;Word:&quot;</span> <span class="p">)</span> <span class="p">}</span> <span class="c"># This causes the script to be listed in a new menu Scripts in the menu bar</span> <span class="n">test_arg_entry</span><span class="o">.</span><span class="n">contexts</span> <span class="o">=</span> <span class="p">[</span><span class="n">wingapi</span><span class="o">.</span><span class="n">kContextNewMenu</span><span class="p">(</span><span class="s">&#39;Scripts&#39;</span><span class="p">)]</span> </pre></div> <p>Notice that this imports some additional modules not used in our previous examples in this series: <tt class="literal"><span class="pre">datatype</span></tt> is used to specify the type of an argument, and <tt class="literal"><span class="pre">formbuilder</span></tt> is used to specify how the value is collected from the user. These are documented in <a class="reference" href="/doc/scripting/arginfo">Argument Collection</a> in the users manual.</p> <p>Once you load the script, you can try it with <tt class="literal"><span class="pre">Test</span> <span class="pre">arg</span> <span class="pre">entry</span></tt> in the <tt class="literal"><span class="pre">Scripts</span></tt> menu that should appear in the menu bar. The value collection area will look like this:</p> <img src="https://wingware.com/images/blog/scripting-3/argentry.png" alt="/images/blog/scripting-3/argentry.png" backrefs="" class="doc-image" dupnames="" height="64px" ids="" names="" width="288px" /><p>Pressing <tt class="literal"><span class="pre">Tab</span></tt> moves between the fields and <tt class="literal"><span class="pre">Enter</span></tt> executes the command, which displays this dialog:</p> <img src="https://wingware.com/images/blog/scripting-3/argentry-result.png" alt="/images/blog/scripting-3/argentry-result.png" backrefs="" class="doc-image" dupnames="" height="118px" ids="" names="" width="220px" /></div> <div class="section"> <h3 class="title-3">Using a Dialog for Argument Entry</h3> <p>Arguments may also be collected in a dialog, rather than at the bottom of the IDE window. For the above example, this would be done by appending the following code:</p> <div class="python-highlight"><pre><span class="n">test_arg_entry</span><span class="o">.</span><span class="n">flags</span> <span class="o">=</span> <span class="p">{</span><span class="s">&#39;force_dialog_argentry&#39;</span><span class="p">:</span> <span class="bp">True</span><span class="p">}</span> </pre></div> <p>After this change is saved, executing <tt class="literal"><span class="pre">Test</span> <span class="pre">arg</span> <span class="pre">entry</span></tt> from the <tt class="literal"><span class="pre">Scripts</span></tt> menu displays the argument entry fields in a dialog instead:</p> <img src="https://wingware.com/images/blog/scripting-3/dialog-argentry.png" alt="/images/blog/scripting-3/dialog-argentry.png" backrefs="" class="doc-image" dupnames="" height="122px" ids="" names="" width="400px" /></div> <div class="section"> <h3 class="title-3">Providing History and Completion</h3> <p>Argument entry can also implement history (accessible by pressing the up and down arrows) and completion of matching choices with <tt class="literal"><span class="pre">Tab</span></tt> or <tt class="literal"><span class="pre">Enter</span></tt>. The following example does both for its argument <tt class="literal"><span class="pre">text</span></tt>:</p> <div class="python-highlight"><pre><span class="kn">import</span> <span class="nn">wingapi</span> <span class="kn">from</span> <span class="nn">wingutils</span> <span class="kn">import</span> <span class="n">datatype</span> <span class="kn">from</span> <span class="nn">guiutils</span> <span class="kn">import</span> <span class="n">formbuilder</span> <span class="c"># The command does nothing other than adding the argument to history</span> <span class="c"># (first=most recent)</span> <span class="n">_history</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">def</span> <span class="nf">test_arg_entry</span><span class="p">(</span><span class="n">text</span><span class="p">):</span> <span class="k">if</span> <span class="n">text</span> <span class="ow">in</span> <span class="n">_history</span><span class="p">:</span> <span class="n">_history</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">text</span><span class="p">)</span> <span class="n">_history</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">text</span><span class="p">)</span> <span class="c"># For completions, we just use the matching words in this file</span> <span class="k">def</span> <span class="nf">_completions</span><span class="p">(</span><span class="n">fragment</span><span class="p">):</span> <span class="n">fn</span> <span class="o">=</span> <span class="n">__file__</span> <span class="k">if</span> <span class="n">fn</span><span class="o">.</span><span class="n">endswith</span><span class="p">((</span><span class="s">&#39;.pyc&#39;</span><span class="p">,</span> <span class="s">&#39;.pyo&#39;</span><span class="p">)):</span> <span class="n">fn</span> <span class="o">=</span> <span class="n">fn</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">fn</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span> <span class="n">txt</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">()</span> <span class="k">return</span> <span class="nb">sorted</span><span class="p">([</span><span class="n">t</span> <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">txt</span><span class="o">.</span><span class="n">split</span><span class="p">()</span> <span class="k">if</span> <span class="n">t</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="n">fragment</span><span class="p">)])</span> <span class="n">test_arg_entry</span><span class="o">.</span><span class="n">arginfo</span> <span class="o">=</span> <span class="p">{</span> <span class="s">&#39;text&#39;</span><span class="p">:</span> <span class="n">wingapi</span><span class="o">.</span><span class="n">CArgInfo</span><span class="p">(</span> <span class="s">&quot;Test with completion and history&quot;</span><span class="p">,</span> <span class="n">datatype</span><span class="o">.</span><span class="n">CType</span><span class="p">(</span><span class="s">&#39;&#39;</span><span class="p">),</span> <span class="n">formbuilder</span><span class="o">.</span><span class="n">CSmallTextGui</span><span class="p">(</span><span class="n">history</span><span class="o">=</span><span class="n">_history</span><span class="p">,</span> <span class="n">choices</span><span class="o">=</span><span class="n">_completions</span><span class="p">)</span> <span class="p">),</span> <span class="p">}</span> <span class="n">test_arg_entry</span><span class="o">.</span><span class="n">contexts</span> <span class="o">=</span> <span class="p">[</span><span class="n">wingapi</span><span class="o">.</span><span class="n">kContextNewMenu</span><span class="p">(</span><span class="s">&#39;Scripts&#39;</span><span class="p">)]</span> <span class="n">test_arg_entry</span><span class="o">.</span><span class="n">flags</span> <span class="o">=</span> <span class="p">{</span><span class="s">&#39;force_dialog_argentry&#39;</span><span class="p">:</span> <span class="bp">True</span><span class="p">}</span> </pre></div> <p>After replacing the script created earlier with the above code you should see an auto-completer as you type:</p> <img src="https://wingware.com/images/blog/scripting-3/auto-completion.png" alt="/images/blog/scripting-3/auto-completion.png" backrefs="" class="doc-image" dupnames="" height="188px" ids="" names="" width="415px" /><p>After executing the command one or more times, the up and down arrows traverse the stored history:</p> <img src="https://wingware.com/images/blog/scripting-3/history.gif" alt="/images/blog/scripting-3/history.gif" backrefs="" class="doc-image" dupnames="" height="188px" ids="" names="" width="415px" /><p>Note that in this implementation the history is lost each time the script is reloaded. One way to save history or any other value across script reload or sessions is to store it using <tt class="literal"><span class="pre">SetAttribute()</span></tt> on the current project obtained from <tt class="literal"><span class="pre">wingapi.gApplication.GetProject()</span></tt>.</p> <p>The above covers most argument collection needed by extension scripts for Wing. A few other data entry methods are also supported, as documented in <a class="reference" href="/doc/scripting/arginfo">Argument Collection</a> in the users manual.</p> <br> <br><p>That's it for now! In the next <a class="reference" href="/hints">Wing Tip</a> we'll take a look at the API in more detail, in order to write a more complex scripting example.</p> </div> https://wingware.com/hints/scripting-3Fri, 28 Jun 2019 01:00:00 GMTExtending Wing with Python (Part 2 of 4)https://wingware.com/hints/scripting-2<p>In this issue of <a class="reference" href="/hints">Wing Tips</a> we continue to look at how to extend Wing's functionality, by setting up a project that can be used to develop and debug extension scripts written for (and with) Wing, just as you would work with any other Python code.</p> <p>If you haven't already read it, you may want to check out <a class="reference" href="/hints/scripting-1">Part 1</a> of this series, where we introduced Wing's scripting framework and set up auto-completion for the scripting API.</p> <div class="section"> <h3 class="title-3">Creating a Scripting Project</h3> <p>To debug extension scripts written for Wing, you will need to set up a new project that is configured so that Wing can debug itself. The manual steps for doing this are documented in <a class="reference" href="/doc/scripting/debugging">Debugging Extension Scripts</a>. However, let's use an extension script to do this automatically.</p> <p>Copy the following Python code and paste it into a new file in Wing, using the <tt class="literal"><span class="pre">File</span> <span class="pre">&gt;</span> <span class="pre">New</span></tt> menu item:</p> <div class="python-highlight"><pre><span class="kn">import</span> <span class="nn">sys</span> <span class="kn">import</span> <span class="nn">os</span> <span class="kn">import</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="nn">wingapi</span> <span class="k">def</span> <span class="nf">new_scripting_project</span><span class="p">():</span> <span class="sd">&quot;&quot;&quot;Create a new Wing project that is set up for developing and</span> <span class="sd"> debugging scripts using the current instance of Wing&quot;&quot;&quot;</span> <span class="n">app</span> <span class="o">=</span> <span class="n">wingapi</span><span class="o">.</span><span class="n">gApplication</span> <span class="k">def</span> <span class="nf">setup_project</span><span class="p">():</span> <span class="c"># Set the Python Executable to the Python that runs Wing</span> <span class="n">proj</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">GetProject</span><span class="p">()</span> <span class="n">proj</span><span class="o">.</span><span class="n">SetPythonExecutable</span><span class="p">(</span><span class="bp">None</span><span class="p">,</span> <span class="n">sys</span><span class="o">.</span><span class="n">executable</span><span class="p">)</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">__debug__</span><span class="p">:</span> <span class="kn">from</span> <span class="nn">proj.attribs</span> <span class="kn">import</span> <span class="n">kPyRunArgs</span> <span class="n">app</span><span class="o">.</span><span class="n">fSingletons</span><span class="o">.</span><span class="n">fFileAttribMgr</span><span class="o">.</span><span class="n">SetValue</span><span class="p">(</span><span class="n">kPyRunArgs</span><span class="p">,</span> <span class="bp">None</span><span class="p">,</span> <span class="p">(</span><span class="s">&#39;custom&#39;</span><span class="p">,</span> <span class="s">&#39;-u -O&#39;</span><span class="p">))</span> <span class="c"># Add Wing&#39;s installation directory to the project</span> <span class="n">proj</span><span class="o">.</span><span class="n">AddDirectory</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">GetWingHome</span><span class="p">())</span> <span class="c"># Set main debug file to Wing&#39;s entry point</span> <span class="n">wingpy</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">GetWingHome</span><span class="p">(),</span> <span class="s">&#39;bootstrap&#39;</span><span class="p">,</span> <span class="s">&#39;wing.py&#39;</span><span class="p">)</span> <span class="n">proj</span><span class="o">.</span><span class="n">SetMainDebugFile</span><span class="p">(</span><span class="n">wingpy</span><span class="p">)</span> <span class="c"># Setup the environment for auto-completion on the API and some additional</span> <span class="c"># values needed only on macOS; use OrderedDict because later envs depend</span> <span class="c"># on earlier ones</span> <span class="n">env</span> <span class="o">=</span> <span class="n">collections</span><span class="o">.</span><span class="n">OrderedDict</span><span class="p">()</span> <span class="n">env</span><span class="p">[</span><span class="s">&#39;PYTHONPATH&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">GetWingHome</span><span class="p">(),</span> <span class="s">&#39;src&#39;</span><span class="p">)</span> <span class="k">if</span> <span class="n">sys</span><span class="o">.</span><span class="n">platform</span> <span class="o">==</span> <span class="s">&#39;darwin&#39;</span><span class="p">:</span> <span class="n">runtime_dir</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">GetWingHome</span><span class="p">(),</span> <span class="s">&#39;bin&#39;</span><span class="p">,</span> <span class="s">&#39;__os__&#39;</span><span class="p">,</span> <span class="s">&#39;osx&#39;</span><span class="p">)</span> <span class="n">files</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">runtime_dir</span><span class="p">)</span> <span class="k">for</span> <span class="n">fn</span> <span class="ow">in</span> <span class="n">files</span><span class="p">:</span> <span class="k">if</span> <span class="n">fn</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s">&#39;runtime-qt&#39;</span><span class="p">):</span> <span class="n">qt_ver</span> <span class="o">=</span> <span class="n">fn</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="s">&#39;runtime-&#39;</span><span class="p">):]</span> <span class="k">break</span> <span class="n">env</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">collections</span><span class="o">.</span><span class="n">OrderedDict</span><span class="p">([</span> <span class="p">(</span><span class="s">&#39;RUNTIMES&#39;</span><span class="p">,</span> <span class="n">runtime_dir</span><span class="p">),</span> <span class="p">(</span><span class="s">&#39;QTVERSION&#39;</span><span class="p">,</span> <span class="n">qt_ver</span><span class="p">),</span> <span class="p">(</span><span class="s">&#39;QTRUNTIME&#39;</span><span class="p">,</span> <span class="s">&#39;${RUNTIMES}/runtime-${QTVERSION}&#39;</span><span class="p">),</span> <span class="p">(</span><span class="s">&#39;SCIRUNTIME&#39;</span><span class="p">,</span> <span class="s">&#39;${RUNTIMES}/runtime-scintillaedit-${QTVERSION}&#39;</span><span class="p">),</span> <span class="p">(</span><span class="s">&#39;DYLD_LIBRARY_PATH&#39;</span><span class="p">,</span> <span class="s">&#39;${QTRUNTIME}/lib:${SCIRUNTIME}/lib&#39;</span><span class="p">),</span> <span class="p">(</span><span class="s">&#39;DYLD_FRAMEWORK_PATH&#39;</span><span class="p">,</span> <span class="s">&#39;${DYLD_LIBRARY_PATH}&#39;</span><span class="p">),</span> <span class="p">]))</span> <span class="n">app</span><span class="o">.</span><span class="n">GetProject</span><span class="p">()</span><span class="o">.</span><span class="n">SetEnvironment</span><span class="p">(</span><span class="bp">None</span><span class="p">,</span> <span class="s">&#39;startup&#39;</span><span class="p">,</span> <span class="n">env</span><span class="p">)</span> <span class="c"># Create a new project; this prompts the user to save any unsaved files first</span> <span class="c"># and only calls the completion callback if they don&#39;t cancel</span> <span class="n">app</span><span class="o">.</span><span class="n">NewProject</span><span class="p">(</span><span class="n">setup_project</span><span class="p">)</span> <span class="n">new_scripting_project</span><span class="o">.</span><span class="n">contexts</span> <span class="o">=</span> <span class="p">[</span><span class="n">wingapi</span><span class="o">.</span><span class="n">kContextNewMenu</span><span class="p">(</span><span class="s">&#39;Scripts&#39;</span><span class="p">)]</span> </pre></div> <p>Save this to a file named <tt class="literal"><span class="pre">utils.py</span></tt> or any other name ending in <tt class="literal"><span class="pre">.py</span></tt> in the <tt class="literal"><span class="pre">scripts</span></tt> directory that is located inside Wing's <a class="reference" href="/doc/install/user-settings-dir">Settings Directory</a>, as listed in Wing's <tt class="literal"><span class="pre">About</span></tt> box. Then select <tt class="literal"><span class="pre">Reload</span> <span class="pre">All</span> <span class="pre">Scripts</span></tt> from the <tt class="literal"><span class="pre">Edit</span></tt> menu to get Wing to discover the new script file. Once that's done, Wing monitors the file and will reload it automatically if you edit it and save changes to disk.</p> <p>You can now create your project with <tt class="literal"><span class="pre">New</span> <span class="pre">scripting</span> <span class="pre">project</span></tt> from the <tt class="literal"><span class="pre">Scripting</span></tt> menu that should appear in the menu bar:</p> <img src="https://wingware.com/images/blog/scripting-2/new-scripting-menu-item.png" alt="/images/blog/scripting-2/new-scripting-menu-item.png" backrefs="" class="doc-image" dupnames="" height="48px" ids="" names="" width="177px" /><p>This prompts to save any edited files before closing the current project, and then creates and configures the new project.</p> </div> <div class="section"> <h3 class="title-3">Debugging Extension Scripts</h3> <p>Now you're ready to develop and debug extension scripts with Wing. Try this now by appending the following to the script file that you created in the previous section:</p> <div class="python-highlight"><pre><span class="kn">import</span> <span class="nn">wingapi</span> <span class="k">def</span> <span class="nf">hello_world</span><span class="p">():</span> <span class="n">wingapi</span><span class="o">.</span><span class="n">gApplication</span><span class="o">.</span><span class="n">ShowMessageDialog</span><span class="p">(</span><span class="s">&quot;Hello&quot;</span><span class="p">,</span> <span class="s">&quot;Hello world!&quot;</span><span class="p">)</span> <span class="n">hello_world</span><span class="o">.</span><span class="n">contexts</span> <span class="o">=</span> <span class="p">[</span><span class="n">wingapi</span><span class="o">.</span><span class="n">kContextNewMenu</span><span class="p">(</span><span class="s">&quot;Scripts&quot;</span><span class="p">)]</span> </pre></div> <p>Save this to disk and then set a breakpoint on the third line, on the call to <tt class="literal"><span class="pre">ShowMessageDialog</span></tt>.</p> <p>Next, uncheck the <tt class="literal"><span class="pre">Projects</span> <span class="pre">&gt;</span> <span class="pre">Auto-reopen</span> <span class="pre">Last</span> <span class="pre">Project</span></tt> preference so that the debugged copy of Wing opens the default project and not your already-open scripting project:</p> <img src="https://wingware.com/images/blog/scripting-2/no-auto-reopen.png" alt="/images/blog/scripting-2/no-auto-reopen.png" backrefs="" class="doc-image" dupnames="" height="54px" ids="" names="" width="386px" /><p>Then select <tt class="literal"><span class="pre">Start/Continue</span></tt> from the <tt class="literal"><span class="pre">Debug</span></tt> menu to start up a copy of Wing running under its own debugger. This copy of Wing should also contain a <tt class="literal"><span class="pre">Scripts</span></tt> menu in its menu bar, with an item <tt class="literal"><span class="pre">Hello</span> <span class="pre">world</span></tt>:</p> <img src="https://wingware.com/images/blog/scripting-2/scripts-menu.png" alt="/images/blog/scripting-2/scripts-menu.png" backrefs="" class="doc-image" dupnames="" height="67px" ids="" names="" width="194px" /><p>Select that and you will reach the breakpoint you set in the outer instance of Wing (the one you started debug from):</p> <img src="https://wingware.com/images/blog/scripting-2/breakpoint.png" alt="/images/blog/scripting-2/breakpoint.png" backrefs="" class="doc-image" dupnames="" height="85px" ids="" names="" width="594px" /><p>You will see and can navigate the entire stack, but Wing will not be able to show source files for most of its internals. If you need to see the source code of Wing itself, you will have to obtain the source code as described in <a class="reference" href="/doc/scripting/advanced">Advanced Scripting</a>.</p> <br> <br><p>That's it for now! In the next <a class="reference" href="/hints">Wing Tip</a> we'll look look at how extension scripts can collect arguments from the user.</p> </div> https://wingware.com/hints/scripting-2Mon, 17 Jun 2019 01:00:00 GMTExtending Wing with Python (Part 1 of 4)https://wingware.com/hints/scripting-1<p>In this issue of <a class="reference" href="/hints">Wing Tips</a> we start to look at how to extend Wing's functionality by writing Python code. Extension scripts can be used to automate custom editing tasks, access Wing's source code analysis, control the debugger, and much more.</p> <div class="section"> <h3 class="title-3">A Simple Example</h3> <p>Scripts written to extend Wing are regular Python files containing one or more function definitions at the top level of the file. Each of these functions creates a new IDE command that can be used just like Wing's built-in command set.</p> <p>The following example creates a new command <tt class="literal"><span class="pre">hello-world</span></tt> in the IDE:</p> <div class="python-highlight"><pre><span class="kn">import</span> <span class="nn">wingapi</span> <span class="k">def</span> <span class="nf">hello_world</span><span class="p">():</span> <span class="n">wingapi</span><span class="o">.</span><span class="n">gApplication</span><span class="o">.</span><span class="n">ShowMessageDialog</span><span class="p">(</span><span class="s">&quot;Hello&quot;</span><span class="p">,</span> <span class="s">&quot;Hello world!&quot;</span><span class="p">)</span> </pre></div> <p>To try this, drop it into a file named <tt class="literal"><span class="pre">test.py</span></tt> in the <tt class="literal"><span class="pre">scripts</span></tt> directory inside the <a class="reference" href="/doc/install/user-settings-dir">Settings Directory</a> listed in Wing's <tt class="literal"><span class="pre">About</span></tt> box. Then select <tt class="literal"><span class="pre">Reload</span> <span class="pre">All</span> <span class="pre">Scripts</span></tt> from the <tt class="literal"><span class="pre">Edit</span></tt> menu so Wing scans for newly added scripts. Once that's done, you don't need to use <tt class="literal"><span class="pre">Reload</span> <span class="pre">All</span> <span class="pre">Scripts</span></tt> again since Wing watches already-loaded script files and reloads them automatically when you save them to disk.</p> <p>Now you can run your script with <tt class="literal"><span class="pre">Command</span> <span class="pre">by</span> <span class="pre">Name</span></tt> from the <tt class="literal"><span class="pre">Edit</span></tt> menu, by typing the command name <tt class="literal"><span class="pre">hello-world</span></tt> into the mini-buffer that appears at the bottom of Wing's window and then pressing <tt class="literal"><span class="pre">Enter</span></tt>:</p> <img src="https://wingware.com/images/blog/scripting-1/command-by-name.png" alt="/images/blog/scripting-1/command-by-name.png" backrefs="" class="doc-image" dupnames="" height="40px" ids="" names="" width="262px" /><p>You can also use the <tt class="literal"><span class="pre">User</span> <span class="pre">Interface</span> <span class="pre">&gt;</span> <span class="pre">Keyboard</span> <span class="pre">&gt;</span> <span class="pre">Custom</span> <span class="pre">Key</span> <span class="pre">Bindings</span></tt> preference to bind the command <tt class="literal"><span class="pre">hello-world</span></tt> to any key, or add the command to the tool bar with the <tt class="literal"><span class="pre">User</span> <span class="pre">Interface</span> <span class="pre">&gt;</span> <span class="pre">Toolbar</span> <span class="pre">&gt;</span> <span class="pre">Custom</span> <span class="pre">Items</span></tt> preference.</p> <p>Whatever method you use to invoke <tt class="literal"><span class="pre">hello-world</span></tt>, you should see a dialog that looks something like this:</p> <img src="https://wingware.com/images/blog/scripting-1/hello-world.png" alt="/images/blog/scripting-1/hello-world.png" backrefs="" class="doc-image" dupnames="" height="118px" ids="" names="" width="189px" /><p><strong>Adding Menus</strong></p> <p>Commands created by scripts may also be added to new menus in the menu bar, by setting a function attribute as follows:</p> <div class="python-highlight"><pre><span class="kn">import</span> <span class="nn">wingapi</span> <span class="k">def</span> <span class="nf">hello_world</span><span class="p">():</span> <span class="n">wingapi</span><span class="o">.</span><span class="n">gApplication</span><span class="o">.</span><span class="n">ShowMessageDialog</span><span class="p">(</span><span class="s">&quot;Hello&quot;</span><span class="p">,</span> <span class="s">&quot;Hello world!&quot;</span><span class="p">)</span> <span class="n">hello_world</span><span class="o">.</span><span class="n">contexts</span> <span class="o">=</span> <span class="p">[</span><span class="n">wingapi</span><span class="o">.</span><span class="n">kContextNewMenu</span><span class="p">(</span><span class="s">&quot;Scripts&quot;</span><span class="p">)]</span> </pre></div> <p>After making this change in your copy of <tt class="literal"><span class="pre">test.py</span></tt>, you should see the <tt class="literal"><span class="pre">Scripts</span></tt> menu appear in the menu bar immediately after saving the file:</p> <img src="https://wingware.com/images/blog/scripting-1/menu.png" alt="/images/blog/scripting-1/menu.png" backrefs="" class="doc-image" dupnames="" height="75px" ids="" names="" width="690px" /><p>Scripts can also add items to the editor context menu, project context menu, or the high-level configuration menu in the top right of Wing's window. We'll go over these in a future article in this series.</p> </div> <div class="section"> <h3 class="title-3">Enabling Auto-Completion and Documentation</h3> <p>Creating scripts is a lot easier if Wing offers auto-completion and documentation in the <tt class="literal"><span class="pre">Source</span> <span class="pre">Assistant</span></tt> tool. To enable that, let's use a bit of script-based automation to take care of the necessary project setup.</p> <p>Start by appending the following to the <tt class="literal"><span class="pre">test.py</span></tt> script file that you created earlier, and then save to disk:</p> <div class="python-highlight"><pre><span class="kn">import</span> <span class="nn">wingapi</span> <span class="kn">import</span> <span class="nn">os</span> <span class="k">def</span> <span class="nf">setup_script_completion</span><span class="p">():</span> <span class="sd">&quot;&quot;&quot;Set up the current project to provide auto-completion on Wing&#39;s extension API&quot;&quot;&quot;</span> <span class="n">app</span> <span class="o">=</span> <span class="n">wingapi</span><span class="o">.</span><span class="n">gApplication</span> <span class="n">proj</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">GetProject</span><span class="p">()</span> <span class="n">pypath</span> <span class="o">=</span> <span class="n">proj</span><span class="o">.</span><span class="n">GetEnvironment</span><span class="p">()</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;PYTHONPATH&#39;</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span> <span class="n">dirname</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">GetWingHome</span><span class="p">(),</span> <span class="s">&#39;src&#39;</span><span class="p">)</span> <span class="k">if</span> <span class="n">pypath</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span> <span class="n">pypath</span> <span class="o">=</span> <span class="n">dirname</span> <span class="k">elif</span> <span class="ow">not</span> <span class="n">dirname</span> <span class="ow">in</span> <span class="n">pypath</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">pathsep</span><span class="p">):</span> <span class="n">pypath</span> <span class="o">+=</span> <span class="n">os</span><span class="o">.</span><span class="n">pathsep</span> <span class="o">+</span> <span class="n">dirname</span> <span class="n">app</span><span class="o">.</span><span class="n">GetProject</span><span class="p">()</span><span class="o">.</span><span class="n">SetEnvironment</span><span class="p">(</span><span class="bp">None</span><span class="p">,</span> <span class="s">&#39;startup&#39;</span><span class="p">,</span> <span class="p">{</span><span class="s">&#39;PYTHONPATH&#39;</span><span class="p">:</span> <span class="n">pypath</span><span class="p">})</span> <span class="n">setup_script_completion</span><span class="o">.</span><span class="n">contexts</span> <span class="o">=</span> <span class="p">[</span><span class="n">wingapi</span><span class="o">.</span><span class="n">kContextNewMenu</span><span class="p">(</span><span class="s">&quot;Scripts&quot;</span><span class="p">)]</span> </pre></div> <p>Now you can configure any project for easier scripting by selecting <tt class="literal"><span class="pre">Setup</span> <span class="pre">script</span> <span class="pre">completion</span></tt> from the <tt class="literal"><span class="pre">Scripts</span></tt> menu. This adds the directory that contains Wing's scripting API to the <tt class="literal"><span class="pre">Python</span> <span class="pre">Path</span></tt> in your project's <tt class="literal"><span class="pre">Project</span> <span class="pre">Properties</span></tt>.</p> <p>Once this is done, the auto-completer works on the contents of <tt class="literal"><span class="pre">wingapi</span></tt>, documentation for the API appears in the <tt class="literal"><span class="pre">Source</span> <span class="pre">Assistant</span></tt> as you type or navigate code, and <tt class="literal"><span class="pre">Goto</span> <span class="pre">Definition</span></tt> will bring up the API's implementation source:</p> <img src="https://wingware.com/images/blog/scripting-1/complete-sassist.gif" alt="/images/blog/scripting-1/complete-sassist.gif" backrefs="" class="doc-image" dupnames="" ids="" names="" /><p>You may of course want to apply this configuration only to a project you create for working on your Wing extension scripts, to avoid altering the <tt class="literal"><span class="pre">Python</span> <span class="pre">Path</span></tt> used in other projects.</p> </div> <div class="section"> <h3 class="title-3">A More Complex Example</h3> <p>Here's a more complex real world example that moves the caret to the start of the current block in Python code. You can try this by appending it to your copy of <tt class="literal"><span class="pre">test.py</span></tt>, saving to disk, and then using``Goto Start of Block`` in the <tt class="literal"><span class="pre">Scripts</span></tt> menu in any Python file:</p> <div class="python-highlight"><pre><span class="kn">import</span> <span class="nn">wingapi</span> <span class="kn">from</span> <span class="nn">wingbase</span> <span class="kn">import</span> <span class="n">textutils</span> <span class="k">def</span> <span class="nf">_indent_chars</span><span class="p">(</span><span class="n">line</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;Count indent characters at start of the given line&quot;&quot;&quot;</span> <span class="n">indent_chars</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">c</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">line</span><span class="p">):</span> <span class="k">if</span> <span class="n">c</span> <span class="ow">in</span> <span class="s">&#39; </span><span class="se">\t</span><span class="s">&#39;</span><span class="p">:</span> <span class="n">indent_chars</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">else</span><span class="p">:</span> <span class="k">break</span> <span class="k">return</span> <span class="n">indent_chars</span> <span class="k">def</span> <span class="nf">start_of_block</span><span class="p">():</span> <span class="sd">&quot;&quot;&quot;Move to the start of the current block of code&quot;&quot;&quot;</span> <span class="n">ed</span> <span class="o">=</span> <span class="n">wingapi</span><span class="o">.</span><span class="n">gApplication</span><span class="o">.</span><span class="n">GetActiveEditor</span><span class="p">()</span> <span class="n">doc</span> <span class="o">=</span> <span class="n">ed</span><span class="o">.</span><span class="n">GetDocument</span><span class="p">()</span> <span class="n">txt</span> <span class="o">=</span> <span class="n">doc</span><span class="o">.</span><span class="n">GetText</span><span class="p">()</span> <span class="n">pos</span><span class="p">,</span> <span class="n">end</span> <span class="o">=</span> <span class="n">ed</span><span class="o">.</span><span class="n">GetSelection</span><span class="p">()</span> <span class="n">first_indent</span> <span class="o">=</span> <span class="bp">None</span> <span class="n">lineno</span> <span class="o">=</span> <span class="n">doc</span><span class="o">.</span><span class="n">GetLineNumberFromPosition</span><span class="p">(</span><span class="n">pos</span><span class="p">)</span> <span class="k">while</span> <span class="n">lineno</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">:</span> <span class="n">line</span> <span class="o">=</span> <span class="n">txt</span><span class="p">[</span><span class="n">doc</span><span class="o">.</span><span class="n">GetLineStart</span><span class="p">(</span><span class="n">lineno</span><span class="p">):</span><span class="n">doc</span><span class="o">.</span><span class="n">GetLineEnd</span><span class="p">(</span><span class="n">lineno</span><span class="p">)]</span> <span class="n">indent</span> <span class="o">=</span> <span class="n">_indent_chars</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="k">if</span> <span class="n">first_indent</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span> <span class="k">if</span> <span class="n">indent</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span> <span class="n">first_indent</span> <span class="o">=</span> <span class="n">indent</span> <span class="n">lineno</span> <span class="o">-=</span> <span class="mi">1</span> <span class="k">elif</span> <span class="n">indent</span> <span class="o">&gt;=</span> <span class="n">first_indent</span><span class="p">:</span> <span class="n">lineno</span> <span class="o">-=</span> <span class="mi">1</span> <span class="k">elif</span> <span class="ow">not</span> <span class="n">line</span><span class="o">.</span><span class="n">strip</span><span class="p">():</span> <span class="n">lineno</span> <span class="o">-=</span> <span class="mi">1</span> <span class="k">else</span><span class="p">:</span> <span class="k">break</span> <span class="n">target</span> <span class="o">=</span> <span class="n">doc</span><span class="o">.</span><span class="n">GetLineStart</span><span class="p">(</span><span class="n">lineno</span><span class="p">)</span> <span class="n">target</span> <span class="o">+=</span> <span class="n">_indent_chars</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="n">ed</span><span class="o">.</span><span class="n">SetSelection</span><span class="p">(</span><span class="n">target</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span> <span class="k">def</span> <span class="nf">_start_of_block_available</span><span class="p">():</span> <span class="n">ed</span> <span class="o">=</span> <span class="n">wingapi</span><span class="o">.</span><span class="n">gApplication</span><span class="o">.</span><span class="n">GetActiveEditor</span><span class="p">()</span> <span class="k">if</span> <span class="n">ed</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span> <span class="k">return</span> <span class="bp">False</span> <span class="n">mime</span> <span class="o">=</span> <span class="n">wingapi</span><span class="o">.</span><span class="n">gApplication</span><span class="o">.</span><span class="n">GetMimeType</span><span class="p">(</span><span class="n">ed</span><span class="o">.</span><span class="n">GetDocument</span><span class="p">()</span><span class="o">.</span><span class="n">GetFilename</span><span class="p">())</span> <span class="k">return</span> <span class="n">mime</span> <span class="o">==</span> <span class="s">&#39;text/x-python&#39;</span> <span class="n">start_of_block</span><span class="o">.</span><span class="n">available</span> <span class="o">=</span><span class="n">_start_of_block_available</span> <span class="n">start_of_block</span><span class="o">.</span><span class="n">label</span> <span class="o">=</span> <span class="s">&quot;Goto Start of Block&quot;</span> <span class="n">start_of_block</span><span class="o">.</span><span class="n">contexts</span> <span class="o">=</span> <span class="p">[</span><span class="n">wingapi</span><span class="o">.</span><span class="n">kContextNewMenu</span><span class="p">(</span><span class="s">&#39;Scripts&#39;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)]</span> </pre></div> <p>This example introduces a few other concepts:</p> <p><strong>(1)</strong> You can place utility functions at the top of a script file by prefixing their names with <tt class="literal"><span class="pre">_</span></tt> (underscore). This prevents Wing from creating a new command out of that function.</p> <p><strong>(2)</strong> Setting the function attribute <tt class="literal"><span class="pre">available</span></tt> on a script controls when the command created by it is available in the user interface; when it isn't, menu items are greyed out.</p> <p><strong>(3)</strong> The <tt class="literal"><span class="pre">label</span></tt> attribute replaces Wing's automatically generated label for the command, when it is shown in menus.</p> <p><strong>(4)</strong> A group number can be included in <tt class="literal"><span class="pre">kContextNewMenu</span></tt> to group script-created commands into separate sections of menus.</p> <p>This is just a small sampling of the capabilities of extension scripts in Wing. We'll go into more depth in the next few installments in this series. Or see <a class="reference" href="/doc/scripting">Scripting and Extending Wing</a> for detailed documentation.</p> <br> <br><p>That's it for now! In the next <a class="reference" href="/hints">Wing Tip</a> we'll look at how to use Wing to debug extension scripts, to make it much easier to develop more complex extensions.</p> </div> https://wingware.com/hints/scripting-1Fri, 07 Jun 2019 01:00:00 GMTUsing External Code Quality Checkers with Wing Pro 7https://wingware.com/hints/external-checkers<p>Wing Pro 7 introduced a new code warnings system with expanded built-in code inspection capabilities and improved integration with external code quality checkers. In this issue of <a class="reference" href="/hints">Wing Tips</a>, we describe how to configure Wing Pro 7 to take advantage of external code quality checkers like Pylint, pep8, and mypy.</p> <p><a class="reference" href="https://www.pylint.org/">Pylint</a> is a powerful general-purpose code quality checker that flags a wide variety of real code errors and stylistic issues, <a class="reference" href="https://pypi.org/project/pep8/">pep8</a> checks compliance with the <a class="reference" href="https://www.python.org/dev/peps/pep-0008/">PEP 8 Style Guide for Python Code</a>, and <a class="reference" href="http://mypy-lang.org/">mypy</a> is a type checker for Python 3 code that makes use of optional static type declarations.</p> <div class="section"> <h3 class="title-3">Installing the External Checkers</h3> <p>If you need to install Pylint, pep8, or mypy, you can do this on the command line outside of Wing like this:</p> <pre class="literal-block"> pip install pylint </pre> <p>Or if you are using Anaconda, you should use <tt class="literal"><span class="pre">conda</span></tt> instead:</p> <pre class="literal-block"> conda install pylint </pre> <p>The package name for the other two checkers are <tt class="literal"><span class="pre">pep8</span></tt> and <tt class="literal"><span class="pre">mypy</span></tt>.</p> <p>A quick way to see whether the checkers are properly installed is to attempt to import them in Wing's <tt class="literal"><span class="pre">Python</span> <span class="pre">Shell</span></tt> tool:</p> <div class="python-highlight"><pre><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">pylint</span> <span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">pep8</span> <span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">mypy</span> </pre></div> <p>If you need to change which Python installation Wing uses, set the <tt class="literal"><span class="pre">Python</span> <span class="pre">Executable</span></tt> under the <tt class="literal"><span class="pre">Environment</span></tt> tab in <tt class="literal"><span class="pre">Project</span> <span class="pre">Properties</span></tt>, from the <tt class="literal"><span class="pre">Project</span></tt> menu, to the value produced by <tt class="literal"><span class="pre">import</span> <span class="pre">sys;</span> <span class="pre">print(sys.executable)</span></tt> in the Python interpreter that you want to use. Then restart Wing's <tt class="literal"><span class="pre">Python</span> <span class="pre">Shell</span></tt> from its <tt class="literal"><span class="pre">Options</span></tt> menu.</p> </div> <div class="section"> <h3 class="title-3">Configuring External Checkers</h3> <p>Once the checkers you plan to use are installed, you can enable them in Wing Pro's <tt class="literal"><span class="pre">Code</span> <span class="pre">Warnings</span></tt> tool, from the <tt class="literal"><span class="pre">Tools</span></tt> menu. Click on the <tt class="literal"><span class="pre">Configuration</span></tt> tab, check on <tt class="literal"><span class="pre">Enable</span> <span class="pre">external</span> <span class="pre">checkers</span></tt>, and then press the <tt class="literal"><span class="pre">Configure</span></tt> button. This displays a dialog like this:</p> <img src="https://wingware.com/images/blog/external-checkers/conf-dialog.png" alt="/images/blog/external-checkers/conf-dialog.png" backrefs="" class="doc-image" dupnames="" height="434px" ids="" names="" width="616px" /><p>Here you can check on <tt class="literal"><span class="pre">Enable</span></tt> for each of the external checkers that you want to use with your project, and select some options for when and how they are run. In most cases the default configuration will work. Wing will run the checker when a file is opened and re-run it each time the file is saved. For Pylint, you can select whether Wing will display only errors, or also warnings and informational messages.</p> <p>Notice that Wing avoids running the checkers on source files above the configured <tt class="literal"><span class="pre">Maximum</span> <span class="pre">File</span> <span class="pre">Size</span></tt>, in order to avoid overly lengthy CPU-intensive computation.</p> </div> <div class="section"> <h3 class="title-3">Using the Checkers</h3> <p>Once this is done, Wing will interleave errors and warnings found by the enabled checkers into its own warnings in the <tt class="literal"><span class="pre">Code</span> <span class="pre">Warnings</span></tt> tool:</p> <img src="https://wingware.com/images/blog/external-checkers/warnings-tool.png" alt="/images/blog/external-checkers/warnings-tool.png" backrefs="" class="doc-image" dupnames="" height="292px" ids="" names="" width="644px" /><p>Warnings are also shown on the editor, using indicators that can be configured with the <tt class="literal"><span class="pre">Editor</span> <span class="pre">&gt;</span> <span class="pre">Code</span> <span class="pre">Warnings</span></tt> preferences group. Hovering the mouse over an indicator in the editor displays the warning in a tooltip:</p> <img src="https://wingware.com/images/blog/external-checkers/editor-indicators.png" alt="/images/blog/external-checkers/editor-indicators.png" backrefs="" class="doc-image" dupnames="" height="197px" ids="" names="" width="423px" /></div> <div class="section"> <h3 class="title-3">Filtering Out Warnings</h3> <p>Checkers like Pylint can produce lots of stylistic warnings that may not be relevant to your development team's coding standards. These can be a distraction, making it harder to see real problems in code.</p> <p>Wing lets you quickly hide incorrect or uninteresting warnings by clicking on the red <img src="https://wingware.com/images/blog/external-checkers/x.png" alt="x" backrefs="" class="inline-image" dupnames="" height="16px" ids="" names="" width="16px" /> icon that appears when you mouse over items in the <tt class="literal"><span class="pre">Code</span> <span class="pre">Warnings</span></tt> tool.</p> <p>For external checkers like Pylint and pep8, this hides all warnings of that type, by adding a rule to <tt class="literal"><span class="pre">Removed</span> <span class="pre">from</span> <span class="pre">any</span> <span class="pre">file</span></tt> section under the <tt class="literal"><span class="pre">Configuration</span></tt> tab in the <tt class="literal"><span class="pre">Code</span> <span class="pre">Warnings</span></tt> tool:</p> <img src="https://wingware.com/images/blog/external-checkers/remove-warnings.png" alt="/images/blog/external-checkers/remove-warnings.png" backrefs="" class="doc-image" dupnames="" height="301px" ids="" names="" width="578px" /><p>In this way, you should be able to drill down quickly to just the warnings that you are interested in.</p> <p>You can export code warnings configurations or set the location Wing writes the configuration from the <tt class="literal"><span class="pre">Options</span></tt> menu in the <tt class="literal"><span class="pre">Code</span> <span class="pre">Warnings</span></tt> tool. This lets you share warnings configurations with other users or projects, for example by checking the configuration into revision control.</p> </div> <div class="section"> <h3 class="title-3">Recommendations</h3> <p>If you are required to write PEP 8 compatible code, enabling Wing's <a class="reference" href="/doc/edit/pep8">PEP 8 Reformatting</a> feature may be preferable to using the pep8 code checker. This can reformat entire files or just the parts that you edit.</p> <p>We encourage you to think carefully about how you use code checkers. While they do find errors early and can improve code quality, they may also lead to polishing that takes more time than it is worth. We've tried to design Wing's integration with external code checkers in a way that supports quick customization, so you can focus on the issues that you care about.</p> <br> <br><p>That's it for now! We'll be back next week with more <a class="reference" href="/hints">Wing Tips</a> for Wing Python IDE.</p> </div> https://wingware.com/hints/external-checkersFri, 31 May 2019 01:00:00 GMTRemote Development with Wing Prohttps://wingware.com/hints/remote-dev-2<p>In this issue of <a class="reference" href="/hints">Wing Tips</a> we take a quick look at Wing Pro's remote development capabilities.</p> <div class="section"> <h3 class="title-3">Setting up SSH Access</h3> <p>Wing Pro's remote development support requires using an SSH public/private key pair and SSH agent rather than entering a password each time you connect. This is more secure and convenient, and it allows Wing to seamlessly re-establish the remote connection as needed over time. If you currently enter a password (other than a 2-factor authentication card selector) each time you <tt class="literal"><span class="pre">ssh</span></tt> to the remote host, then please see <a class="reference" href="/doc/proj/ssh-setup-details">SSH Setup Details</a> for instructions.</p> </div> <div class="section"> <h3 class="title-3">Creating a Remote Project</h3> <p>To create a Wing Pro project that works with a remote host, select <tt class="literal"><span class="pre">New</span> <span class="pre">Project</span></tt> from the <tt class="literal"><span class="pre">Project</span></tt> menu and use <tt class="literal"><span class="pre">Connect</span> <span class="pre">to</span> <span class="pre">Remote</span> <span class="pre">Host</span> <span class="pre">via</span> <span class="pre">SSH</span></tt> as the project type. You will need to choose an <tt class="literal"><span class="pre">Identifier`</span></tt> that Wing uses to refer to the host, and enter the <tt class="literal"><span class="pre">Host</span> <span class="pre">Name</span></tt> either as an IP address or name (optionally in <tt class="literal"><span class="pre">username&#64;hostname</span></tt> form).</p> <p>If <tt class="literal"><span class="pre">python</span></tt> is not on the <tt class="literal"><span class="pre">PATH</span></tt> on your remote host, or is not the Python you want to use, then you will also need to paste the full path to Python into the <tt class="literal"><span class="pre">Python</span> <span class="pre">Executable</span></tt> field. This is typically the value printed by <tt class="literal"><span class="pre">import</span> <span class="pre">sys;</span> <span class="pre">print(sys.executable)</span></tt> in the Python that you want to use.</p> <p>You will only rarely need to specify any of the other values in a remote host configuration. For now, leave them set to their default values. For example:</p> <img src="https://wingware.com/images/blog/remote-dev-2/new-project.png" alt="/images/blog/remote-dev-2/new-project.png" backrefs="" class="doc-image" dupnames="" height="454" ids="" names="" width="656" /><p>Once you submit this dialog, Wing will probe the remote host and install the remote agent. When this is complete, you should see a dialog with some information about the remote host:</p> <img src="https://wingware.com/images/blog/remote-dev-2/install-succeeded.png" alt="/images/blog/remote-dev-2/install-succeeded.png" backrefs="" class="doc-image" dupnames="" height="262" ids="" names="" width="672" /><p>You can now point Wing to your remotely stored source code with <tt class="literal"><span class="pre">Add</span> <span class="pre">Existing</span> <span class="pre">Directory</span></tt> in the <tt class="literal"><span class="pre">Project</span></tt> menu. When this is complete, save the project to local disk with <tt class="literal"><span class="pre">Save</span> <span class="pre">Project</span></tt> in the <tt class="literal"><span class="pre">Project</span></tt> menu.</p> <p><strong>That's all there is to it!</strong></p> <p>At this point all of Wing's features should work with files on the remote host, including editing, debugging, unit testing, working in the <tt class="literal"><span class="pre">Python</span> <span class="pre">Shell</span></tt> (after restarting from its <tt class="literal"><span class="pre">Options</span></tt> menu), using version control, searching, code navigation, running processes from the <tt class="literal"><span class="pre">OS</span> <span class="pre">Commands</span></tt> tool, and so forth.</p> </div> <div class="section"> <h3 class="title-3">How it Works</h3> <p>Using <tt class="literal"><span class="pre">New</span> <span class="pre">Project</span></tt> takes care of a few steps that can also be done manually, in the event that a project or remote host connection needs to be reconfigured:</p> <p>(1) Your remote host configuration can be viewed and edited from <tt class="literal"><span class="pre">Remote</span> <span class="pre">Hosts</span></tt> in the <tt class="literal"><span class="pre">Project</span></tt> menu. From here, remote host configurations can also be marked as shared, so they can be reused by multiple projects or used to open projects that are stored on the remote host.</p> <p>(2) Pointing a project at a remote host is done by changing the <tt class="literal"><span class="pre">Python</span> <span class="pre">Executable</span></tt> under the <tt class="literal"><span class="pre">Environment</span></tt> tab in <tt class="literal"><span class="pre">Project</span> <span class="pre">Properties</span></tt> to <tt class="literal"><span class="pre">Remote</span></tt> and selecting the remote host configuration. The remote host configuration determines which Python is used on the remote host.</p> </div> <div class="section"> <h3 class="title-3">Further Reading</h3> <p>If your code runs in a web server or other framework on the remote host, you will need to initiate debug using Wing's <tt class="literal"><span class="pre">wingdbstub.py</span></tt> module as described in the section <tt class="literal"><span class="pre">Initiating</span> <span class="pre">Debug</span></tt> in <a class="reference" href="/doc/howtos/debugging-web-remote">Remote Web Development</a>, or check out the <a class="reference" href="/doc/howtos">How-Tos</a> for details on using Wing Pro with specific frameworks and tools.</p> <p>For detailed documentation on Wing Pro's remote development capabilities see <a class="reference" href="/doc/proj/remote-hosts">Remote Development</a> in the product manual.</p> </div> <div class="section"> <h3 class="title-3">Getting Help</h3> <p>As always, don't hesitate to email <a class="reference" href="mailto:support&#64;wingware.com?subject=Remote%20Development%20Question">support&#64;wingware.com</a> or post on the <a class="reference" href="https://ask.wingware.com">Q&amp;A Forum</a> for help!</p> <br> <br><p>That's it for now! We'll be back next week with more <a class="reference" href="/hints">Wing Tips</a> for Wing Python IDE.</p> </div> https://wingware.com/hints/remote-dev-2Thu, 23 May 2019 01:00:00 GMTSelecting Logical Units of Python Code in Winghttps://wingware.com/hints/quick-select<p>In this issue of <a class="reference" href="/hints">Wing Tips</a> we take a look at quickly selecting Python code in logical units, which can make some editing tasks easier.</p> <div class="section"> <h3 class="title-3">Select More and Select Less</h3> <p>The easiest way to select code from the keyboard, starting from the current selection or caret position, is to repeatedly press <tt class="literal"><span class="pre">Ctrl-Up</span></tt> (the <tt class="literal"><span class="pre">Control</span></tt> key together with the up arrow key). Wing selects more and more code, working outward in logical units, as follows:</p> <img src="https://wingware.com/images/blog/quick-select/quick-select-1.gif" alt="/images/blog/quick-select/quick-select-1.gif" backrefs="" caption="Press Ctrl-Up repeatedly to select increasingly larger units of Python code" class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Press Ctrl-Up repeatedly to select increasingly larger units of Python code</i></p><p>If you select too much, pressing <tt class="literal"><span class="pre">Ctrl-Down</span></tt> instead reduces the selection size again:</p> <img src="https://wingware.com/images/blog/quick-select/quick-select-2.gif" alt="/images/blog/quick-select/quick-select-2.gif" backrefs="" caption="Press Ctrl-Down repeatedly to return to selecting smaller units of Python code" class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Press Ctrl-Down repeatedly to return to selecting smaller units of Python code</i></p></div> <div class="section"> <h3 class="title-3">Select Statement, Block or Scope</h3> <p>Wing also provides commands for selecting the current, previous, or next <tt class="literal"><span class="pre">Statement</span></tt> (a single logical line of code that may span multiple physical lines), <tt class="literal"><span class="pre">Block</span></tt> (a contiguous section of code at same indent level, without any blank lines), or <tt class="literal"><span class="pre">Scope</span></tt> (an entire function, method, or class).</p> <p>Here's an example using several of these commands:</p> <img src="https://wingware.com/images/blog/quick-select/quick-select-3.gif" alt="/images/blog/quick-select/quick-select-3.gif" backrefs="" caption="Execute &quot;Select Statement&quot;, then &quot;Select Block&quot;, &quot;Select Scope&quot;, and finally &quot;Select Next Scope&quot;" class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Execute "Select Statement", then "Select Block", "Select Scope", and finally "Select Next Scope"</i></p></div> <div class="section"> <h3 class="title-3">Adding Key Bindings</h3> <p>If you plan to use these commands, you will probably want to bind them to keys using the <tt class="literal"><span class="pre">User</span> <span class="pre">Interface</span> <span class="pre">&gt;</span> <span class="pre">Keyboard</span> <span class="pre">&gt;</span> <span class="pre">Custom</span> <span class="pre">Key</span> <span class="pre">Bindings</span></tt> preference for the following commands:</p> <pre class="literal-block"> select-statement next-statement previous-statement select-block next-block previous-block select-scope next-scope previous-scope </pre> <p>Since free key combinations are often in short supply, you may want to make use of a multi-key sequence in your bindings. For example, pressing <tt class="literal"><span class="pre">Ctrl-\</span></tt> followed by <tt class="literal"><span class="pre">B</span></tt> results in the binding <tt class="literal"><span class="pre">Ctrl-\</span> <span class="pre">B</span></tt>:</p> <img src="https://wingware.com/images/blog/quick-select/quick-select-4.gif" alt="/images/blog/quick-select/quick-select-4.gif" backrefs="" caption="Add a key binding &quot;Ctrl-\ B&quot; for &quot;select-block&quot;" class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Add a key binding "Ctrl-\ B" for "select-block"</i></p><p>This only works if <tt class="literal"><span class="pre">Ctrl-\</span></tt> is not itself already a binding. Any other free key combination (and not only <tt class="literal"><span class="pre">Ctrl-\</span></tt>) can be used as the starting key in the sequence.</p> <br> <br><p>That's it for now! We'll be back next week with more <a class="reference" href="/hints">Wing Tips</a> for Wing Python IDE.</p> </div> https://wingware.com/hints/quick-selectMon, 20 May 2019 01:00:00 GMTRenaming Symbols and Attributes in Python Code with Wing Pro's Refactoring Toolhttps://wingware.com/hints/refactor-rename<p>In the <a class="reference" href="/blog/multi-selection">previous Wing Tips post</a> we looked at using multiple selections to edit several parts of code at once. As part of that, we briefly mentioned that refactoring is a better approach when renaming a symbol or attribute globally. Let's take a closer look at that now.</p> <div class="section"> <h3 class="title-3">What is Refactoring Anyway?</h3> <p>Refactoring is the process of changing code in a way that does not alter its functionality, in order to better organize the code or make it easier to read and maintain. A round of refactoring is often appropriate before working on code that has become a bit crufty over time.</p> <p>IDEs like Wing Pro can help with this process by automating some of the operations commonly made during refactoring, including renaming symbols or attributes, moving symbols around, collecting code into a new function or method, and so forth.</p> </div> <div class="section"> <h3 class="title-3">Renaming Symbols and Attributes</h3> <p>Rename refactoring is often used to make code more readable by selecting clearer or more appropriate names. It may also be used to change a method on a class from <tt class="literal"><span class="pre">__Private</span></tt> form, which in Python can only be accessed from code in the class itself, to a form that can be called from code outside of the class. For example:</p> <img src="https://wingware.com/images/blog/refactor-rename/refactor-rename-1.gif" alt="/images/blog/refactor-rename/refactor-rename-1.gif" backrefs="" caption="Renaming method &quot;__SetPosition&quot; to &quot;_SetPosition&quot; with refactoring, so it can be used from outside of the class" class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Renaming method "__SetPosition" to "_SetPosition" with refactoring, so it can be used from outside of the class</i></p></div> <div class="section"> <h3 class="title-3">Renaming Modules and Packages</h3> <p>Rename refactoring may also be used on whole modules or packages, by renaming any use of the module or package name. Wing Pro will rename the associated disk files and directories and track the change in the active revision control system, if any.</p> <img src="https://wingware.com/images/blog/refactor-rename/refactor-rename-2.gif" alt="/images/blog/refactor-rename/refactor-rename-2.gif" backrefs="" caption="Renaming module &quot;urlutils&quot; to &quot;urlops&quot; with refactoring" class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Renaming module "urlutils" to "urlops" with refactoring</i></p></div> <div class="section"> <h3 class="title-3">Like-Named Symbols and Symbol Identity</h3> <p>Wing Pro's rename refactoring uses static source analysis of your code to determine which symbols are actually the same symbol. For example, in the following code there are two distinct symbols called <tt class="literal"><span class="pre">name</span></tt>, one in the scope <tt class="literal"><span class="pre">show_name</span></tt> and another in the scope <tt class="literal"><span class="pre">process_name</span></tt>:</p> <div class="python-highlight"><pre><span class="k">def</span> <span class="nf">show_name</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span> <span class="k">if</span> <span class="n">name</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span> <span class="k">print</span><span class="p">(</span><span class="n">name</span><span class="p">)</span> <span class="k">def</span> <span class="nf">process_name</span><span class="p">(</span><span class="n">name</span><span class="p">):</span> <span class="n">show</span> <span class="o">=</span> <span class="n">enter_name</span><span class="p">(</span><span class="n">name</span><span class="p">)</span> <span class="k">if</span> <span class="n">show</span><span class="p">:</span> <span class="n">show_name</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="n">name</span><span class="p">)</span> </pre></div> <p>Renaming <tt class="literal"><span class="pre">name</span></tt> in the first function should only affect that scope, and any code that is passing the argument by name, as in the following example:</p> <img src="https://wingware.com/images/blog/refactor-rename/refactor-rename-3.gif" alt="/images/blog/refactor-rename/refactor-rename-3.gif" backrefs="" caption="Refactoring to rename only one of two distinct but like-named symbols &quot;name&quot;" class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Refactoring to rename only one of two distinct but like-named symbols "name"</i></p></div> <div class="section"> <h3 class="title-3">Uncertain Symbol Identity</h3> <p>In some cases, Wing Pro cannot determine for certain that a like-named symbol is actually the same symbol as the one you are renaming. In the following example, a missing import statement prevents Wing from determining that the instance of <tt class="literal"><span class="pre">name</span></tt> in the file <tt class="literal"><span class="pre">testanother.py</span></tt> is definitely the same symbol:</p> <img src="https://wingware.com/images/blog/refactor-rename/refactor-rename-4.gif" alt="/images/blog/refactor-rename/refactor-rename-4.gif" backrefs="" caption="Renaming &quot;name&quot; finds an uncertain match, where a missing import prevents analysis from establishing the symbol's identity" class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Renaming "name" finds an uncertain match, where a missing import prevents analysis from establishing the symbol's identity</i></p><p>When this occurs, Wing marks the potential match with a <tt class="literal"><span class="pre">?</span></tt> and won't rename it unless you check the checkbox next to it. Items can be visited in the editor by clicking on them in the <tt class="literal"><span class="pre">Refactoring</span></tt> tool.</p> <p>If you find Wing is failing to identify many symbols with certainty, you may want to check that your configured <tt class="literal"><span class="pre">Python</span> <span class="pre">Path</span></tt> in <tt class="literal"><span class="pre">Project</span> <span class="pre">Properties</span></tt> is allowing Wing to trace down the modules that you import in your code. You should see code warning indicators on imports that cannot be resolved.</p> <p>In some other cases, <a class="reference" href="/doc/edit/analysis-helping-type-hints">adding type hints</a> may also help Wing's static analysis of your code.</p> <br> <br><p>Wing Pro also provides a number of other refactoring operations that we'll eventually go through here in Wing Tips. For more information, take a look at <a class="reference" href="/doc/refactoring">Refactoring</a> in the product manual.</p> <p>That's it for now! We'll be back next week with more <a class="reference" href="/hints">Wing Tips</a> for Wing Python IDE.</p> </div> https://wingware.com/hints/refactor-renameThu, 09 May 2019 01:00:00 GMTUsing Multiple Selections in Wing Python IDE to Avoid Repetitive Code Editing Taskshttps://wingware.com/hints/multi-selection<p>In this issue of <a class="reference" href="/hints">Wing Tips</a> we take a look at making multiple concurrent selections on the editor, in order to apply a single edit simultaneously to several parts of code.</p> <p>The most common way to make multiple selections is to select a range of text and then add other occurrences of the same text with <tt class="literal"><span class="pre">Add</span> <span class="pre">Selection</span> <span class="pre">for</span> <span class="pre">Next</span> <span class="pre">Occurrence</span></tt> from the <tt class="literal"><span class="pre">Edit</span> <span class="pre">&gt;</span> <span class="pre">Multiple</span> <span class="pre">Selections</span></tt> menu or by clicking on the multi-selection toolbar item, which looks like this:</p> <img src="https://wingware.com/images/blog/multi-selection/multi-select-icon.png" alt="/images/blog/multi-selection/multi-select-icon.png" backrefs="" class="doc-image" dupnames="" height="40" ids="" names="" width="45" /><p>You'll usually want to use the key binding for this, which is <tt class="literal"><span class="pre">Ctrl-D</span></tt>, or <tt class="literal"><span class="pre">Command-D</span></tt> on OS X, and <tt class="literal"><span class="pre">Ctrl-&gt;</span></tt> in the emacs keyboard personality.</p> <p>Once the selections are made your edits will be applied to all of them in the same way, as in the following example:</p> <img src="https://wingware.com/images/blog/multi-selection/multi-select-1.gif" alt="/images/blog/multi-selection/multi-select-1.gif" backrefs="" caption="Shown above: Select &quot;stocks&quot;, add four other occurrences, and replace all the selections by typing &quot;trades&quot;." class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Shown above: Select "stocks", add four other occurrences, and replace all the selections by typing "trades".</i></p><p>Edits applied to all selections can include cursor movement, pasting, and other editing actions. So for example selecting a number of commas, pressing the right arrow key and then <tt class="literal"><span class="pre">Space</span></tt> adds a space after all the commas:</p> <img src="https://wingware.com/images/blog/multi-selection/multi-select-2.gif" alt="/images/blog/multi-selection/multi-select-2.gif" backrefs="" caption="Shown above: Select the first comma, add the other occurrences, press right arrow, and then press Space to insert a space after all the commas at once." class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Shown above: Select the first comma, add the other occurrences, press right arrow, and then press Space to insert a space after all the commas at once.</i></p><div class="section"> <h3 class="title-3">Multi-Select with the Mouse</h3> <p>Multiple selections can also be made by clicking or double-clicking the mouse while holding <tt class="literal"><span class="pre">Ctrl-Alt</span></tt> (or <tt class="literal"><span class="pre">Option-Command</span></tt> on OS X). This allows selecting ranges that don't necessarily contain the same text:</p> <img src="https://wingware.com/images/blog/multi-selection/multi-select-3.gif" alt="/images/blog/multi-selection/multi-select-3.gif" backrefs="" caption="Shown above: Press Ctrl-Alt (or Option-Command on OS X) while double-clicking to select several words and type &quot;foo&quot; to replace them all." class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Shown above: Press Ctrl-Alt (or Option-Command on OS X) while double-clicking to select several words and type "foo" to replace them all.</i></p></div> <div class="section"> <h3 class="title-3">Renaming Variables and Attributes</h3> <p>If you have Wing Pro and are trying to change the name of a variable or attribute globally then multi-selection is usually not the right tool for the job. Instead, use <tt class="literal"><span class="pre">Rename</span> <span class="pre">Symbol</span></tt> in the <tt class="literal"><span class="pre">Refactor</span></tt> menu.</p> </div> <div class="section"> <h3 class="title-3">Configuring Multi-Selections</h3> <p>As shown in the first example above, when more than one selection is made, Wing displays a floating window that enumerates the selections. You can remove selections from here by moving the mouse over an item and clicking on the red <tt class="literal"><span class="pre">X</span></tt> icon that appears over it:</p> <img src="https://wingware.com/images/blog/multi-selection/multi-select-4.gif" alt="/images/blog/multi-selection/multi-select-4.gif" backrefs="" caption="Shown above: Select all occurrences of &quot;foo&quot;, remove one selection, and and type &quot;test&quot; to replace the remaining two instances." class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Shown above: Select all occurrences of "foo", remove one selection, and and type "test" to replace the remaining two instances.</i></p><p>This window can be disabled entirely with <tt class="literal"><span class="pre">Configure</span> <span class="pre">Selections</span> <span class="pre">Window</span></tt> in the <tt class="literal"><span class="pre">Edit</span> <span class="pre">&gt;</span> <span class="pre">Multiple</span> <span class="pre">Selections</span></tt> menu or the multi-selection toolbar item.</p> <p>These menus are also the place to select whether occurrences match only whole words or are case sensitive, and they provide a way to quickly select all occurrences in the file, class, function/method, or block:</p> <img src="https://wingware.com/images/blog/multi-selection/multi-select-menu.png" alt="/images/blog/multi-selection/multi-select-menu.png" backrefs="" class="doc-image" dupnames="" height="333px" ids="" names="" width="335px" /><p>For more information on multi-selection, take a look at <a class="reference" href="/doc/edit/multiple-selections">Multiple Selections</a> in the product manual.</p> <br> <br><p>That's it for now! We'll be back next week with more <a class="reference" href="/hints">Wing Tips</a> for Wing Python IDE.</p> </div> https://wingware.com/hints/multi-selectionFri, 03 May 2019 01:00:00 GMTOverlaying Debug Data onto Wing Pro 7's Editorhttps://wingware.com/hints/debug-value-tips<p>Wing 7 has been released, so in this issue of <a class="reference" href="/hints">Wing Tips</a> we take a look at one of the new debugger features in Wing Pro 7: The ability to press and hold Shift-Space to display the value of all visible symbols, using an overlay on top of the editor.</p> <p>This is what it looks like:</p> <img src="https://wingware.com/images/blog/debug-value-tips/debug-value-tips.gif" alt="/images/blog/debug-value-tips/debug-value-tips.gif" backrefs="" class="doc-image" dupnames="" ids="" names="" /><p>Notice that values are shown for any variable and also for dotted names like <tt class="literal"><span class="pre">self.args.config_vars</span></tt>. When <tt class="literal"><span class="pre">Shift-Space</span></tt> is released, the values are hidden.</p> <div class="section"> <h3 class="title-3">Try it Yourself</h3> <p>To try this out, paste the following into a new file in Wing and save it to disk with name <tt class="literal"><span class="pre">testtips.py</span></tt>:</p> <div class="python-highlight"><pre><span class="kn">import</span> <span class="nn">random</span> <span class="n">x</span> <span class="o">=</span> <span class="mf">3.14</span> <span class="o">**</span> <span class="mi">2</span> <span class="n">y</span> <span class="o">=</span> <span class="s">&#39;test &#39;</span> <span class="o">*</span> <span class="mi">8</span> <span class="n">z</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1000000</span><span class="p">)</span> <span class="k">print</span><span class="p">(</span><span class="s">&quot;&lt;-- Set breakpoint here by clicking on leftmost margin&quot;</span><span class="p">)</span> </pre></div> <p>Then set a breakpoint on the last line by clicking on the margin to the left, or by moving the editor caret to that line and using <tt class="literal"><span class="pre">Add</span> <span class="pre">Breakpoint</span></tt> in the <tt class="literal"><span class="pre">Debug</span></tt> menu.</p> <p>Next start debug with <tt class="literal"><span class="pre">Debug</span> <span class="pre">Current</span> <span class="pre">File</span></tt> in the <tt class="literal"><span class="pre">Debug</span></tt> menu and let Wing run to the breakpoint.</p> <p>Finally, press and hold <tt class="literal"><span class="pre">Shift-Space</span></tt> and you should see the value of <tt class="literal"><span class="pre">x</span></tt>, <tt class="literal"><span class="pre">y</span></tt>, and <tt class="literal"><span class="pre">z</span></tt> overlaid onto the editor as follows:</p> <img src="https://wingware.com/images/blog/debug-value-tips/testtips.gif" alt="/images/blog/debug-value-tips/testtips.gif" backrefs="" class="doc-image" dupnames="" ids="" names="" /><p>When you release <tt class="literal"><span class="pre">Shift-Space</span></tt> the overlay is removed.</p> </div> <div class="section"> <h3 class="title-3">Binding Alternative Keys</h3> <p>If you don't like having to hold down the keys, another option is to bind different keys to the commands <tt class="literal"><span class="pre">debug-show-value-tips</span></tt> and <tt class="literal"><span class="pre">debug-hide-value-tips</span></tt>. This is done with the <tt class="literal"><span class="pre">User</span> <span class="pre">Interface</span> <span class="pre">&gt;</span> <span class="pre">Keyboard</span> <span class="pre">&gt;</span> <span class="pre">Custom</span> <span class="pre">Key</span> <span class="pre">Bindings</span></tt> preference in Wing.</p> <p>For example, you might bind the single key <tt class="literal"><span class="pre">Ctrl-'</span></tt> to the command sequence <tt class="literal"><span class="pre">debug-show-value-tips,</span> <span class="pre">debug-hide-value-tips</span></tt>. Wing executes the first available command in a comma-separated list, so pressing <tt class="literal"><span class="pre">Ctrl-'</span></tt> the first time will show the values and pressing it again will hide them.</p> <p>If you also want to override the default binding for <tt class="literal"><span class="pre">Shift-Space</span></tt>, so Wing inserts a space even when the debugger is active, use the command <tt class="literal"><span class="pre">send-keys(keys=&quot;</span> <span class="pre">&quot;)</span></tt></p> <br> <br><p>That's it for now! In the next <a class="reference" href="/hints">Wing Tip</a> we'll take a look at using multiple selections to more easily complete some types of edits.</p> </div> https://wingware.com/hints/debug-value-tipsTue, 30 Apr 2019 01:00:00 GMTUsing Anaconda with Wing Python IDEhttps://wingware.com/hints/anaconda<p>In this issue of <a class="reference" href="/hints">Wing Tips</a> we take a look at how to use the <a class="reference" href="https://www.anaconda.com/distribution/">Anaconda Distribution</a> of Python with <a class="reference" href="/">Wing</a>.</p> <p>Anaconda's key advantage is its easy-to-use package management system. Anaconda comes with a large collection of third party packages that are not in an installation of Python from <a class="reference" href="https://python.org">python.org</a>. Many <a class="reference" href="https://docs.anaconda.com/anaconda/packages/pkg-docs/">additional packages</a> can be installed quickly and easily as needed, from the command line with <tt class="literal"><span class="pre">conda</span> <span class="pre">install</span></tt>.</p> <p>Anaconda's marketing focuses on data science and machine learning applications, but its extensive packages library makes it a good option also for other types of desktop and web development.</p> <p>There is much <a class="reference" href="https://www.bernat.tech/pep-517-518/">ongoing work</a> in the world of Python packaging but, at least for now, Anaconda seems to fail less often than other solutions for resolving dependencies and installing necessary packages automatically.</p> <div class="section"> <h3 class="title-3">Configuring the Environment</h3> <p>To use the Anaconda with Wing, simply set the <tt class="literal"><span class="pre">Python</span> <span class="pre">Executable</span></tt> used in your Wing project to Anaconda's <tt class="literal"><span class="pre">python</span></tt> or <tt class="literal"><span class="pre">python.exe</span></tt>. How you do this depends on whether you are creating a new project or have an existing project that you want to modify.</p> <div class="section"> <h4 class="title-4">New Projects</h4> <p>To create a new Wing project that uses Anaconda, select <tt class="literal"><span class="pre">New</span> <span class="pre">Project</span></tt> from the <tt class="literal"><span class="pre">Project</span></tt> menu and then under <tt class="literal"><span class="pre">Python</span> <span class="pre">Executable</span></tt> select <tt class="literal"><span class="pre">Custom</span></tt> and enter the full path to Anaconda's <tt class="literal"><span class="pre">python</span></tt> or <tt class="literal"><span class="pre">python.exe</span></tt>.</p> <p>In many cases, Wing will automatically find Anaconda and include it in the drop down menu to the right of the entry area that enables when <tt class="literal"><span class="pre">Custom</span></tt> is chosen:</p> <img src="https://wingware.com/images/blog/anaconda/new-project.gif" alt="/images/blog/anaconda/new-project.gif" backrefs="" caption="Shown Above: Choose &quot;New Project&quot; from the &quot;Project Menu&quot;, select &quot;Custom&quot; for &quot;Python Executable&quot;, find Anaconda in the drop down menu, and press &quot;OK&quot; to create the new project." class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Shown Above: Choose "New Project" from the "Project Menu", select "Custom" for "Python Executable", find Anaconda in the drop down menu, and press "OK" to create the new project.</i></p><p>If Anaconda does not appear in Wing's list of available Python installations, and you don't know the full path to use, then you can start Anaconda from the command line outside of Wing and use the value printed by the following, when run interactively:</p> <div class="python-highlight"><pre><span class="kn">import</span> <span class="nn">sys</span> <span class="k">print</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">executable</span><span class="p">)</span> </pre></div> </div> <div class="section"> <h4 class="title-4">Existing Projects</h4> <p>To change an existing project to use Anaconda Python, the steps are the same as above except that the change is made under <tt class="literal"><span class="pre">Project</span> <span class="pre">Properties</span></tt> in the <tt class="literal"><span class="pre">Project</span></tt> menu.</p> </div> <div class="section"> <h4 class="title-4">Virtualenv</h4> <p>If you are using virtualenv with Anaconda, use the full path of the virtualenv's <tt class="literal"><span class="pre">python.exe</span></tt> or <tt class="literal"><span class="pre">python</span></tt> instead. When in doubt, you can print <tt class="literal"><span class="pre">sys.executable</span></tt> as already described to find the correct full path to use in Wing's <tt class="literal"><span class="pre">Python</span> <span class="pre">Executable</span></tt> configuration.</p> </div> <div class="section"> <h4 class="title-4">☕ Et Voila!</h4> <p>In most cases, that is all that you need to do. Wing will start using your Anaconda installation immediately for source intelligence, for the next debug session, and in the integrated <tt class="literal"><span class="pre">Python</span> <span class="pre">Shell</span></tt> after it is restarted from its <tt class="literal"><span class="pre">Options</span></tt> menu.</p> </div> </div> <div class="section"> <h3 class="title-3">Fixing Import Errors on Windows</h3> <p>On Windows, Anaconda may fail to load DLLs when its <tt class="literal"><span class="pre">python.exe</span></tt> is run directly from the command line or within Wing. This is due to the fact that by default the Anaconda installer no longer sets the <tt class="literal"><span class="pre">PATH</span></tt> that it needs to run, in order to avoid conflicting with different Python installations on the same system.</p> <div class="section"> <h4 class="title-4">Simple Solution</h4> <p>A simple solution to fix this problem is to run <tt class="literal"><span class="pre">Scripts\activate</span></tt> (located within your Anaconda installation) on the command line, and then start Wing from within the activated environment, for example with <tt class="literal"><span class="pre">c:\Program</span> <span class="pre">Files</span> <span class="pre">(x86)\Wing</span> <span class="pre">Pro</span> <span class="pre">7\bin\wing.exe</span></tt>. This causes Wing to inherit the necessary <tt class="literal"><span class="pre">PATH</span></tt> that was set up by Anaconda's <tt class="literal"><span class="pre">activate</span></tt> script.</p> <p>This solution works well if you will be using the same Anaconda installation for all projects that you open in a session. If you change to projects that use a different Python installation, you will need to quit and restart Wing in the correct environment.</p> </div> <div class="section"> <h4 class="title-4">Recommended Solutions</h4> <p>Our recommended solutions require a bit more work up front, but once in place they automatically set the necessary <tt class="literal"><span class="pre">PATH</span></tt> without the potential for polluting other Python installations with unwanted environment.</p> <p>A good one-time fix is to create a small wrapper script called <tt class="literal"><span class="pre">wrap_anaconda.bat</span></tt> and place the following into it:</p> <pre class="literal-block"> &#64;echo off call %USERPROFILE%\Anaconda3\Scripts\activate python %* </pre> <p>You will need to change the path on the second line to match where Anaconda is installed on your system.</p> <p>Then set <tt class="literal"><span class="pre">Python</span> <span class="pre">Executable</span></tt> in Wing's <tt class="literal"><span class="pre">Project</span> <span class="pre">Properties</span></tt> to the full path of this batch file. This sets up the necessary environment and then runs Anaconda's Python. No other configuration is necessary, and this script can also be used on the command line or in other IDEs.</p> <p>A similar solution that does not require creating a wrapper script is to set the necessary <tt class="literal"><span class="pre">PATH</span></tt> in Wing's <tt class="literal"><span class="pre">Project</span> <span class="pre">Properties</span></tt>, from the <tt class="literal"><span class="pre">Project</span></tt> menu. Add the following to <tt class="literal"><span class="pre">Environment</span></tt> under the <tt class="literal"><span class="pre">Environment</span></tt> tab:</p> <pre class="literal-block"> ANACONDA_DIR=${USERPROFILE}\Anaconda3 PATH=${PATH};$(ANACONDA_DIR);${ANACONDA_DIR}\DLLS;${ANACONDA_DIR}\Library; ${ANACONDA_DIR}\Library\bin;${ANACONDA_DIR}\Scripts;${ANACONDA_DIR}\mingw-w64\bin </pre> <p>You may need to change the value of <tt class="literal"><span class="pre">ANACONDA_DIR</span></tt> to match where Anaconda is installed on your system.</p> <p>Both of these solutions work well if there are multiple Python installations on your system, because it ensures that the correct <tt class="literal"><span class="pre">PATH</span></tt> is always set when the project is open, allowing other projects to use a different environment.</p> <br> <br><p>That's it for now! In next week's <a class="reference" href="/hints">Wing Tips</a> we'll get back to looking at some of the lesser-known but useful features in Wing.</p> </div> </div> https://wingware.com/hints/anacondaThu, 18 Apr 2019 01:00:00 GMTAuto-Editing in Wing Pro (Part 3 of 3)https://wingware.com/hints/wingpro-auto-editing-3<p>Wing Pro implements a suite of auto-editing operations that take care of common low-level editing tasks. In the previous installments of this 3-part <a class="reference" href="/hints">Wing Tips</a> series on Wing Pro's auto-editing features we looked at <a class="reference" href="/hints/wingpro-auto-editing-1">managing Python code blocks</a> and <a class="reference" href="/hints/wingpro-auto-editing-2">auto-invocation</a>.</p> <p>To finish up this series, let's take a look at PEP 8 compliant auto-spacing and a few less frequently needed but useful operations.</p> <div class="note"> <strong>Note:</strong> If you have Wing Personal, you don't have the features described here. We'll return to features that are also present in Wing Personal next time. Or <a class="reference" href="/">try Wing Pro</a> on a free trial.</div> <div class="section"> <h3 class="title-3">Auto-Entering Spaces and PEP 8</h3> <p>Wing Pro can auto-enter spacing as you type, but this operation is off by default since it may take some getting used to. Once enabled with the <tt class="literal"><span class="pre">Editor</span> <span class="pre">&gt;</span> <span class="pre">Auto-Editing</span> <span class="pre">&gt;</span> <span class="pre">Auto-Enter</span> <span class="pre">Spaces</span></tt> preference, you can type <tt class="literal"><span class="pre">x=1</span></tt> to get <tt class="literal"><span class="pre">x</span> <span class="pre">=</span> <span class="pre">1</span></tt> or <tt class="literal"><span class="pre">i=range(x=0,10)</span></tt> to enter <tt class="literal"><span class="pre">i</span> <span class="pre">=</span> <span class="pre">range(x=0,</span> <span class="pre">10)</span></tt>. Spacing is entered in a PEP 8 compliant way.</p> <img src="https://wingware.com/images/blog/wingpro-auto-editing/auto-spacing.gif" alt="/images/blog/wingpro-auto-editing/auto-spacing.gif" backrefs="" caption="Shown above: Type &quot;t([1,2,3],y=f)&quot; without any spaces; PEP 8 compliant spacing is auto-entered as needed." class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Shown above: Type "t([1,2,3],y=f)" without any spaces; PEP 8 compliant spacing is auto-entered as needed.</i></p><p>Some options for auto-spacing are available on the <tt class="literal"><span class="pre">Editor</span> <span class="pre">&gt;</span> <span class="pre">Auto-Editing</span></tt> preferences page, to control whether spaces are auto-entered after unambiguous keyword names are entered, whether to prevent insertion of spacing that is not PEP 8 compliant, and how to place spaces in type annotations.</p> <div class="note"> <strong>PEP 8 Reformatting:</strong> If you're interested in maintaining strict PEP 8 compliance, you can enable automatic PEP 8 reformatting in the <tt class="literal"><span class="pre">Editor</span> <span class="pre">&gt;</span> <span class="pre">PEP</span> <span class="pre">8</span></tt> preferences group. Reformatting takes place either after leaving an edited line or for the whole file when it is saved to disk. Or, trigger reformatting manually from the <tt class="literal"><span class="pre">Source</span> <span class="pre">&gt;</span> <span class="pre">PEP</span> <span class="pre">8</span></tt> menu group.</div> </div> <div class="section"> <h3 class="title-3">Correcting Out-of-Order Typing</h3> <p>Wing recognizes some types of out-of-order typing and adjusts them automatically, as a way to save on keystrokes. Some of the transformations include:</p> <table border="0" class="table" frame="border"> <colgroup> <col width="29%" /> <col width="71%" /> </colgroup> <thead valign="bottom"> <tr><th>Keys Pressed</th> <th>Keys Entered</th> </tr> </thead> <tbody valign="top"> <tr><td>if x(:)</td> <td>if x(): followed by a new line and indentation</td> </tr> <tr><td>def x(-)</td> <td>def x() -&gt; for entering a type annotation</td> </tr> <tr><td>x()t</td> <td>x().t</td> </tr> <tr><td>x(.y)</td> <td>x().y</td> </tr> <tr><td>x(,)</td> <td>x(),</td> </tr> </tbody> </table> <img src="https://wingware.com/images/blog/wingpro-auto-editing/out-of-order.gif" alt="/images/blog/wingpro-auto-editing/out-of-order.gif" backrefs="" caption="Shown above: Type &quot;def x(:)&quot; to start the first def, &quot;def y(a,b-)&quot; for the second, and finally &quot;c=a,x(,)b&quot;" class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Shown above: Type "def x(:)" to start the first def, "def y(a,b-)" for the second, and finally "c=a,x(,)b"</i></p></div> <div class="section"> <h3 class="title-3">Changing Type of Quotes on a String</h3> <p>When the caret is just after a string and a quote character is pressed, the quotes around the string are changed to that type of quote. This works with regular and triple-quoted strings, although it's most useful for regular strings when the string needs to be edited to contain a quote character.</p> <img src="https://wingware.com/images/blog/wingpro-auto-editing/change-quote.gif" alt="/images/blog/wingpro-auto-editing/change-quote.gif" backrefs="" caption="Shown above: Type 'x = &quot;test&quot;', move caret to right, and then press ' to change to single quotes so the string can contain a double quote" class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Shown above: Type 'x = "test"', move caret to right, and then press ' to change to single quotes so the string can contain a double quote</i></p><br> <br><p>That's it for our three-part <a class="reference" href="/hints">Wing Tips</a> series on auto-editing in Wing Pro. For details on all the available auto-editing operations, see <a class="reference" href="/doc/edit/auto-editing">Auto-Editing</a> in the Wing Pro manual.</p> <p>Auto-editing is just one way in which Wing helps with writing, testing, and debugging Python code. Next week we'll move on to some of the others.</p> </div> https://wingware.com/hints/wingpro-auto-editing-3Thu, 11 Apr 2019 01:00:00 GMTAuto-Editing in Wing Pro (Part 2 of 3)https://wingware.com/hints/wingpro-auto-editing-2<p>Wing Pro implements a suite of auto-editing operations that take care of common low-level editing tasks. <a class="reference" href="/hints/wingpro-auto-editing-1">Last week</a> we looked at creating and managing blocks in Python code.</p> <p>In part two of this <a class="reference" href="/hints">Wing Tips</a> series on Wing Pro's auto-editing feature we turn to auto-invocation, which makes writing Python code that calls functions and methods easier and less prone to errors.</p> <div class="note"> <strong>Note:</strong> If you have Wing Personal, you don't have these features. Please bear with us through this and the next installment in this series. We'll return to features that are also present in Wing Personal after that. Or <a class="reference" href="/">try Wing Pro</a> on a free trial.</div> <div class="section"> <h3 class="title-3">Auto-Enter Invocation Args</h3> <p>When an invocation is typed in Wing Pro, for example <tt class="literal"><span class="pre">getattr(</span></tt>, the editor temporarily inserts all the arguments for <tt class="literal"><span class="pre">getattr</span></tt> and enters a data entry mode in which the <tt class="literal"><span class="pre">Tab</span></tt> key can be used to move between the arguments and default values. When the caret moves out of the argument list, data entry mode ends and any unchanged default arguments are removed.</p> <img src="https://wingware.com/images/blog/wingpro-auto-editing/auto-invocation.gif" alt="/images/blog/wingpro-auto-editing/auto-invocation.gif" backrefs="" caption="Shown above: Type &quot;self.a&quot; followed by Tab for completion, Tab five times to reach the last argument value, and then &quot;Fa&quot; followed by tab for completion; after leaving the invocation, unchanged default arguments are removed." class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Shown above: Type "self.a" followed by Tab for completion, Tab five times to reach the last argument value, and then "Fa" followed by tab for completion; after leaving the invocation, unchanged default arguments are removed.</i></p><p>In cases like the above, where locals are named the same as the required arguments, very little extra work is needed to complete the invocation. In other cases, you may just want to overtype what Wing enters, which is easy to do since the all the arguments are initially selected.</p> <div class="note"> <strong>Configuring Python:</strong> Auto-invocation and many of Wing Pro's other features rely on the IDE's analysis of your source code and the modules that you import. This only works if Wing knows which Python you are using and where to look for modules. In most cases, setting <tt class="literal"><span class="pre">Python</span> <span class="pre">Executable</span></tt> in <tt class="literal"><span class="pre">Project</span> <span class="pre">Properties</span></tt> from the <tt class="literal"><span class="pre">Project</span></tt> menu is all you'll need. This should be set to the value of <tt class="literal"><span class="pre">sys.executable</span></tt> (after <tt class="literal"><span class="pre">import</span> <span class="pre">sys</span></tt>) in the target Python installation. In some cases you may also need to add to <tt class="literal"><span class="pre">Python</span> <span class="pre">Path</span></tt> in <tt class="literal"><span class="pre">Project</span> <span class="pre">Properties</span></tt>, so all the modules you import can be found.</div> </div> <div class="section"> <h3 class="title-3">Auto-Entering Overridden Methods</h3> <p>A similar auto-editing operation enters an overridden method's arguments, when defining a method that overrides a method in an inherited class.</p> <img src="https://wingware.com/images/blog/wingpro-auto-editing/auto-override.gif" alt="/images/blog/wingpro-auto-editing/auto-override.gif" backrefs="" caption="Shown above: Type &quot;def G&quot; followed by Tab for completion, and then ``:`` to move into the method body; method arguments are auto-entered based on the overridden method in class CNewsPage." class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Shown above: Type "def G" followed by Tab for completion, and then ``:`` to move into the method body; method arguments are auto-entered based on the overridden method in class CNewsPage.</i></p></div> <div class="section"> <h3 class="title-3">Nested Invocations</h3> <p>Nested invocations are also supported, for cases where another invocation is made within an outer invocation.</p> <img src="https://wingware.com/images/blog/wingpro-auto-editing/recursive-invoke.gif" alt="/images/blog/wingpro-auto-editing/recursive-invoke.gif" backrefs="" caption="Shown above: Type &quot;d = join(&quot; to start invocation, press Tab to move to first argument, enter &quot;convert(&quot; to start recursive invocation, enter arguments for &quot;convert&quot; by tabbing between them, Tab to return to and complete invocation of &quot;join&quot;." class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Shown above: Type "d = join(" to start invocation, press Tab to move to first argument, enter "convert(" to start recursive invocation, enter arguments for "convert" by tabbing between them, Tab to return to and complete invocation of "join".</i></p><br><br><p>That's it for now. In the next and last part of this 3-part <a class="reference" href="/hints">Wing Tips</a> series on Wing Pro's auto-editing features we'll be looking at auto-spacing, PEP 8, and a few other operations.</p> </div> https://wingware.com/hints/wingpro-auto-editing-2Thu, 04 Apr 2019 01:00:00 GMTAuto-Editing in Wing Pro (Part 1 of 3)https://wingware.com/hints/wingpro-auto-editing-1<p><a class="reference" href="/">Wing Pro</a> implements a suite of auto-editing operations that take care of common low-level editing tasks, like moving Python code into a new block, entering invocation arguments, and maintaining PEP 8 compliance as you type. Some of the simpler auto-editing operations, like auto-closing <tt class="literal"><span class="pre">(</span></tt> or <tt class="literal"><span class="pre">[</span></tt>, are enabled by default and easy to understand. Others may be missed unless you know about them, and some need to be enabled in preferences before they can be used.</p> <p>In this and the next two installments of <a class="reference" href="/hints">Wing Tips</a> we'll be looking at some useful Wing Pro auto-editing operations that are not so easy to discover.</p> <div class="note"> <strong>Note:</strong> If you have Wing Personal, you don't have these features. Please bear with us through this and the next two installments. We'll return to features that are also present in Wing Personal after that. Or <a class="reference" href="/">try Wing Pro</a> on a free trial.</div> <div class="section"> <h3 class="title-3">Creating Blocks with the Colon Key</h3> <p>To quickly turn an existing section of Python code into a new block, select it and then press the colon key. Wing Pro indents the selected lines and positions the caret so you can type <tt class="literal"><span class="pre">if</span></tt>, <tt class="literal"><span class="pre">for</span></tt>, <tt class="literal"><span class="pre">while</span></tt>, <tt class="literal"><span class="pre">def</span></tt> or any other keyword that starts a block.</p> <img src="https://wingware.com/images/blog/wingpro-auto-editing/apply-colon.gif" alt="/images/blog/wingpro-auto-editing/apply-colon.gif" backrefs="" caption="Shown above: Select lines then type &quot;:if ar&quot; followed by Tab for auto-completion; a new block is created from the selection." class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Shown above: Select lines then type ":if ar" followed by Tab for auto-completion; a new block is created from the selection.</i></p></div> <div class="section"> <h3 class="title-3">Creating a Try/Except Block</h3> <p>If you select lines of Python code and type <tt class="literal"><span class="pre">:try</span></tt> Wing adds <tt class="literal"><span class="pre">except</span></tt> automatically and selects it so you can either replace <tt class="literal"><span class="pre">except</span></tt> with <tt class="literal"><span class="pre">finally</span></tt>, press the right arrow key to enter an exception specifier, and/or use <tt class="literal"><span class="pre">Tab</span></tt> to move into the <tt class="literal"><span class="pre">except</span></tt> or <tt class="literal"><span class="pre">finally</span></tt> block.</p> <img src="https://wingware.com/images/blog/wingpro-auto-editing/apply-colon-try.gif" alt="/images/blog/wingpro-auto-editing/apply-colon-try.gif" backrefs="" caption="Shown above: Select lines then type &quot;:try&quot; followed Right Arrow, Space, &quot;P&quot;, Tab to auto-complete &quot;ParseError&quot;, and then Tab to move into the except block." class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Shown above: Select lines then type ":try" followed Right Arrow, Space, "P", Tab to auto-complete "ParseError", and then Tab to move into the except block.</i></p><div class="note"> <strong>Note:</strong> Other keys can also be applied to selections. For example <tt class="literal"><span class="pre">&quot;</span></tt> encloses the selection in quotes, <tt class="literal"><span class="pre">(</span></tt> encloses it in parenthesis, and <tt class="literal"><span class="pre">#</span></tt> toggles whether it is commented out.</div> </div> <div class="section"> <h3 class="title-3">Creating Blocks without Selecting</h3> <p>It's also possible to create new blocks without selecting any lines first. In some versions of Wing, this option needs to be enabled with the <tt class="literal"><span class="pre">Editor</span> <span class="pre">&gt;</span> <span class="pre">Auto-Editing</span> <span class="pre">&gt;</span> <span class="pre">Manage</span> <span class="pre">Blocks</span> <span class="pre">on</span> <span class="pre">Repeated</span> <span class="pre">Colon</span> <span class="pre">Presses</span></tt> preference. Once that is done and a new block is entered, the colon key can be pressed a second time to move the next line into the new block, and a third time to also move the rest of a contiguous block of lines into the new block.</p> <img src="https://wingware.com/images/blog/wingpro-auto-editing/colon-manage.gif" alt="/images/blog/wingpro-auto-editing/colon-manage.gif" backrefs="" caption="Shown above: Type &quot;if use_&quot; followed by Tab for completion and ``:`` three times to pull more and more code into the new block." class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Shown above: Type "if use_" followed by Tab for completion and ``:`` three times to pull more and more code into the new block.</i></p><p>Note that you can also just select a block of code and press the <tt class="literal"><span class="pre">Tab</span></tt> key to reindent it. If multiple indentations are possible for that block, Wing toggles between them each time you press <tt class="literal"><span class="pre">Tab</span></tt>.</p> <br> <br><p>That's it for now. In the next part of this 3-part <a class="reference" href="/hints">Wing Tips</a> series on auto-editing in Wing Pro we'll be looking at auto-invocation, which makes writing Python code that calls functions and methods easier and less prone to errors.</p> </div> https://wingware.com/hints/wingpro-auto-editing-1Thu, 28 Mar 2019 01:00:00 GMTDeveloping and Debugging Python Code Running on Vagrant Containershttps://wingware.com/hints/vagrant<img src="https://wingware.com/images/screenshots/wing6-screenshot-dark.png" width=560 height=419 class="eye-candy-image"><p><a class="reference" href="http://www.vagrantup.com/">Vagrant</a> provides a way to set up lightweight containers that are isolated from your host OS. Containers are very useful in software development because they are easy to share with other developers, so that everybody on a software development team is working with exactly the same coding environment.</p> <p>However, since these containers are often fairly stripped down, developing and debugging on the container may not be as convenient as working on your regular development system.</p> <p>One way to make this easier for Python is to set up remote development from <a class="reference" href="https://wingware.com">Wing Pro</a>. In this model, code is developed, tested, and debugged on the container from a copy of Wing Pro running on your regular desktop or laptop system.</p> <div class="section"> <h3 class="title-3">Prerequisites</h3> <p>This guide assumes you have already installed and started using <a class="reference" href="http://www.vagrantup.com/">Vagrant</a>. Wing Pro supports working with Vagrant boxes running Linux (32-bit or 64-bit) or OS X. As of now, other OS types such as FreeBSD won't work.</p> <p>You will also need to make sure that Python is installed in the Vagrant box. If Python is not present, Wing Pro's remote agent installation process will fail.</p> <p>On Windows, you may need to install PuTTY or one of the OpenSSH ports if you don't already have this set up, so you can connect to the Vagrant container. See <a class="reference" href="/doc/proj/ssh-setup-details">SSH Setup Details</a> in Wing Pro's documentation for details.</p> <p>If you do not already have Wing Pro installed, <a class="reference" href="https://wingware.com/downloads/wingpro">download it now</a>.</p> </div> <div class="section"> <h3 class="title-3">Creating a Project</h3> <p>After you have started Wing Pro, creating a project for an existing Vagrant container is easy:</p> <div class="ordered-list"><ul><li><span class="bullet"> Start your container as usual, with <tt class="literal"><span class="pre">vagrant</span> <span class="pre">up</span></tt></span></li> <li><span class="bullet"> Use <tt class="literal"><span class="pre">New</span> <span class="pre">Project</span></tt> from the <tt class="literal"><span class="pre">Project</span></tt> menu in Wing Pro and then select <tt class="literal"><span class="pre">Connect</span> <span class="pre">to</span> <span class="pre">Vagrant</span></tt> as your project type</span></li> <li><span class="bullet"> Fill in or browse to the full path of your Vagrant project directory which contains your <tt class="literal"><span class="pre">Vagrantfile</span></tt> and press <tt class="literal"><span class="pre">OK</span></tt></span></li> <li><span class="bullet"> Only on Windows with PuTTY, you will be asked to convert Vagrant's private key into a PuTTY key. To do this, Wing starts <tt class="literal"><span class="pre">puttygen</span></tt> with the private key already loaded into it. Press <tt class="literal"><span class="pre">Save</span> <span class="pre">private</span> <span class="pre">key</span></tt> to save the key as <tt class="literal"><span class="pre">private_key.ppk</span></tt> in the current directory. Confirm saving without password (the original also doesn't have a password) and then quit <tt class="literal"><span class="pre">puttygen</span></tt> to continue the project setup process in Wing.</span></li> <li><span class="bullet"> Install the remote agent when prompted.</span></li> </ul></div><p>Once this is done, Wing should confirm that the remote agent installation is working.</p> <p>Then you can add your source directories to the project with <tt class="literal"><span class="pre">Add</span> <span class="pre">Existing</span> <span class="pre">Directory</span></tt> in the <tt class="literal"><span class="pre">Project</span></tt> menu. Finally, use <tt class="literal"><span class="pre">Save</span> <span class="pre">Project</span></tt> in the <tt class="literal"><span class="pre">Project</span></tt> menu to write Wing Pro's project file to local disk.</p> <p>You can now edit, debug, test, search, and manage files on the Vagrant container, or launch commands running in the container from Wing Pro's <tt class="literal"><span class="pre">OS</span> <span class="pre">Commands</span></tt> tool. Even though Wing Pro is running on your host OS, everything you do takes place in the Vagrant container, as controlled by the remote agent.</p> <p>If you have any problems getting this to work, feel free to email <a class="reference" href="mailto:support&#64;wingware.com">support&#64;wingware.com</a> for help.</p> <p>To learn more about Wing Pro's remote development capabilities, see <a class="reference" href="/doc/proj/remote-hosts">Remote Hosts</a>.</p> <p>To learn more about Wing Pro's features, take a look at the Tutorial in the <tt class="literal"><span class="pre">Help</span></tt> menu or the <a class="reference" href="/doc/howtos/quickstart">Quickstart Guide</a>.</p> </div> <div class="section"> <h3 class="title-3">How It Works</h3> <p>Wing uses <tt class="literal"><span class="pre">vagrant</span> <span class="pre">ssh-config</span></tt> to inspect your Vagrant container and fill in the necessary settings in Wing's project file. Then all communication with the container is through SSH.</p> <p>To see the settings that Wing created during <tt class="literal"><span class="pre">New</span> <span class="pre">Project</span></tt>, take a look at <tt class="literal"><span class="pre">Project</span> <span class="pre">Properties</span></tt> from the <tt class="literal"><span class="pre">Project</span></tt> menu. The <tt class="literal"><span class="pre">Python</span> <span class="pre">Executable</span></tt> was set to point to a remote host named <tt class="literal"><span class="pre">vagrant</span></tt>. Click on <tt class="literal"><span class="pre">Edit</span></tt> here or use <tt class="literal"><span class="pre">Remote</span> <span class="pre">Hosts</span></tt> in the <tt class="literal"><span class="pre">Project</span></tt> menu to access the remote host configuration. The values that Wing sets up are: <tt class="literal"><span class="pre">Identifier</span></tt>, <tt class="literal"><span class="pre">Hostname</span></tt>, and <tt class="literal"><span class="pre">WINGHOME</span></tt> under the <tt class="literal"><span class="pre">General</span></tt> tab, and <tt class="literal"><span class="pre">SSH</span> <span class="pre">Port</span></tt> and <tt class="literal"><span class="pre">Private</span> <span class="pre">Key</span></tt> under the <tt class="literal"><span class="pre">Options</span></tt> tab. Settings these values manually achieves exactly the same result as using <tt class="literal"><span class="pre">New</span> <span class="pre">Project</span></tt>.</p> <p>The installation process will also add the Vagrant container's SSH host key as a known host in <tt class="literal"><span class="pre">~/.ssh/known_hosts</span></tt> or in the registry if using PuTTY on Windows.</p> </div> <div class="section"> <h3 class="title-3">Usage Hints</h3> <div class="section"> <h4 class="title-4">Selecting the Python Installation</h4> <p>In cases where Wing is not picking up the right Python to use automatically, for example if multiple installations of Python are present on the Vagrant container, you can select the correct one with <tt class="literal"><span class="pre">Python</span> <span class="pre">Executable</span></tt> in the remote host configuration, which you can access from <tt class="literal"><span class="pre">Remote</span> <span class="pre">Hosts</span></tt> in the <tt class="literal"><span class="pre">Project</span></tt> menu.</p> <p>To check that the correct installation is being used, select <tt class="literal"><span class="pre">Restart</span> <span class="pre">Shell</span></tt> from the <tt class="literal"><span class="pre">Options</span></tt> menu of the <tt class="literal"><span class="pre">Python</span> <span class="pre">Shell</span></tt> tool and type the following in the shell:</p> <div class="python-highlight"><pre><span class="kn">import</span> <span class="nn">sys</span> <span class="n">sys</span><span class="o">.</span><span class="n">executable</span> </pre></div> </div> <div class="section"> <h4 class="title-4">Synced Folders</h4> <p>As far as Wing is concerned, all files and directories are located in the Vagrant container and Wing never accesses local copies of the files maintained by Vagrant's synchronization commands.</p> <p>If you need to update your local copies of files for some other reason while working with Wing, run <tt class="literal"><span class="pre">vagrant</span> <span class="pre">rsync</span></tt>, or set up continuous synchronization with <tt class="literal"><span class="pre">vagrant</span> <span class="pre">rsync-auto</span></tt>.</p> </div> <div class="section"> <h4 class="title-4">Password-less Private Keys</h4> <p>Vagrant uses password-less private keys by default. If you want to use private keys with passwords, you'll need to load the key into the SSH user agent (<tt class="literal"><span class="pre">ssh-agent</span></tt> or <tt class="literal"><span class="pre">pageant</span></tt> for PuTTY) and change <tt class="literal"><span class="pre">Options</span> <span class="pre">&gt;</span> <span class="pre">Private</span> <span class="pre">Key</span></tt> in Wing's <tt class="literal"><span class="pre">vagrant</span></tt> remote host configuration to <tt class="literal"><span class="pre">Use</span> <span class="pre">SSH</span> <span class="pre">User</span> <span class="pre">Agent</span></tt>.</p> </div> </div> <div class="section"> <h3 class="title-3">Related Documents</h3> <p>For more information see:</p> <div class="bullet-list"><ul><li><span class="bullet"> <a class="reference" href="http://vagrantup.com/">Vagrant home page</a> provides downloads and documentation.</span></li> <li><span class="bullet"> <a class="reference" href="/doc/proj/remote-hosts">Remote Hosts</a> for details on configuring remote development in Wing Pro.</span></li> <li><span class="bullet"> <a class="reference" href="/doc/howtos/quickstart">Quickstart Guide</a> contains additional basic information about getting started with Wing Pro.</span></li> <li><span class="bullet"> <a class="reference" href="/doc/intro/tutorial">Tutorial</a> provides a gentler introduction to Wing Pro's features.</span></li> <li><span class="bullet"> <a class="reference" href="/doc/manual">Wing Pro Reference Manual</a> documents Wing Pro in detail.</span></li> </ul></div></div> https://wingware.com/hints/vagrantTue, 25 Sep 2018 01:00:00 GMT