Wing Tipshttps://wingware.com/Tips, tricks, and helpful hints for Wingware's Python IDEsMon, 14 Oct 2019 22:16:23 GMTPyRSS2Gen-1.0.0http://www.dalkescientific.com/Python/PyRSS2Gen.htmlPython Code Warnings in Wing Pro 7https://wingware.com/hints/code-warnings<p>Wing Pro 7 introduced an improved code warnings system that flags likely errors as you work on Python code, using both Wing's built-in static analysis system and (optionally) external code checkers like Pylint, pep8, and mypy. Likely problems are indicated on the editor and listed 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/code-warnings/overview.png" alt="/images/blog/code-warnings/overview.png" backrefs="" class="doc-image" dupnames="" ids="" names="" width="893px" /><p>Examples of warnings that Wing might flag include syntax errors, indentation problems, uses of an undefined variable, imports that cannot be resolved, or variables that are set but never used.</p> <p>Code warnings save development time because they help to identify errors before code is even run. New code is checked as you work, although Wing will wait until you have finished typing so that it doesn't warn about code that is still being entered.</p> <div class="section"> <h3 class="title-3">Navigating Warnings</h3> <p>Clicking on warnings in the <tt class="literal"><span class="pre">Code</span> <span class="pre">Warnings</span></tt> tool or pressing the <tt class="literal"><span class="pre">Enter</span></tt> key in the list navigates to that warning in the editor, highlighting it briefly with a callout, as configured from the <tt class="literal"><span class="pre">Editor</span> <span class="pre">&gt;</span> <span class="pre">Callouts</span></tt> preferences group.</p> <p>The <img src="https://wingware.com/images/doc/en/icons/wingide-warnings.png" alt="warning" backrefs="" class="inline-image" dupnames="" height="18" ids="" names="" width="18" /> code warnings icon appears in the top right of any editor that has some code warnings. This can be clicked in order to jump to selected warnings in the file.</p> <p>When code warnings are displayed on the editor, hovering the mouse cursor over the indicator will display details for that warning in a tooltip, as shown above.</p> </div> <div class="section"> <h3 class="title-3">Configuration</h3> <p>The types of code warnings that Wing shows can be configured from the <tt class="literal"><span class="pre">Configuration:</span> <span class="pre">Defaults</span></tt> page in the drop-down menu at the top of the <tt class="literal"><span class="pre">Code</span> <span class="pre">Warnings</span></tt> tool. All the warnings Wing supports are documented in <a class="reference" href="/doc/warnings/warning-types">Warning Types</a>. Some of these offer configuration options to control which variants of that type of warning will be shown:</p> <img src="https://wingware.com/images/blog/code-warnings/config.png" alt="/images/blog/code-warnings/config.png" backrefs="" class="doc-image" dupnames="" ids="" names="" width="417px" /><p>Incorporating warnings found by external checkers like Pylint, pep8, and mypy is also done from this configuration page, as described previously in <a class="reference" href="/hints/external-checkers">Using External Code Quality Checkers with Wing Pro 7</a></p> </div> <div class="section"> <h3 class="title-3">Disabling Warnings</h3> <p>Since all code checkers have only a limited understanding of what happens when code is actually run, they may show incorrect warnings. Wing allows you to disable warnings either for a single case, for an entire file, or for all files.</p> <p>The quickest way to disable a warning is to press the red <tt class="literal"><span class="pre">X</span></tt> icon that appears in the tooltips shown in the editor or in the currently selected item 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/code-warnings/x-icon.png" alt="/images/blog/code-warnings/x-icon.png" backrefs="" class="doc-image" dupnames="" ids="" names="" width="333px" /><p>Wing disables most individual warnings only for the scope it appears in, so an identical problem in another scope will still be flagged. However, undefined attribute warnings are always disabled in all files where that attribute appears.</p> <p><strong>How it Works</strong></p> <p>When a warning is disabled, Wing adds a rule to the <tt class="literal"><span class="pre">Configuration:</span> <span class="pre">Disabled</span> <span class="pre">Warnings</span></tt> page in the drop-down menu at the top of the <tt class="literal"><span class="pre">Code</span> <span class="pre">Warnings</span></tt> tool:</p> <img src="https://wingware.com/images/blog/code-warnings/disabled.png" alt="/images/blog/code-warnings/disabled.png" backrefs="" class="doc-image" dupnames="" ids="" names="" width="396px" /><p>Rules can be removed from here to reenable a warning, dragged between sections to control how widely the warning is being ignored, or edited by right-clicking in order to fine-tune the scope and nature of the rule.</p> <p>For external checkers like Pylint, pep8, and mypy, warnings are disabled globally by the type of warning, making it relatively easy to develop a custom filter according to coding style and individual preference.</p> <p><strong>Resolving Imports</strong></p> <p>One thing to note here is that all the error checkers need some knowledge of your <tt class="literal"><span class="pre">PYTHONPATH</span></tt> to trace down imports correctly. Often, no additional configuration is needed, but in cases where imports cannot be resolved you may need to add to the <tt class="literal"><span class="pre">Python</span> <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.</p> </div> <div class="section"> <h3 class="title-3">Sharing Code Warnings Configurations</h3> <p>The current code warnings configuration, including all the warnings you have disabled, may be exported to the user settings area, or to a selected file 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. Projects may then share the configuration through the <tt class="literal"><span class="pre">Use</span> <span class="pre">Configuration</span> <span class="pre">From</span></tt> item in the <tt class="literal"><span class="pre">Code</span> <span class="pre">Warnings</span></tt> tool's <tt class="literal"><span class="pre">Options</span></tt> menu:</p> <img src="https://wingware.com/images/blog/code-warnings/options-menu.png" alt="/images/blog/code-warnings/options-menu.png" backrefs="" class="doc-image" dupnames="" ids="" names="" width="416px" /><p>The shared configuration file may be checked into revision control, so it can also be used by other developers working on your project.</p> <br> <br><p>That's it for now! We'll be back soon with more <a class="reference" href="/hints">Wing Tips</a> for Wing Python IDE.</p> </div> https://wingware.com/hints/code-warningsThu, 10 Oct 2019 01:00:00 GMTDebugging Python Code Running in Docker Containers with Wing 7https://wingware.com/hints/docker<p><a class="reference" href="https://www.docker.com/">Docker</a> is a containerization system that uses a relatively light-weight form of virtualization to package and isolate application components from the host system, making it easier to spin up uniformly configured virtual machines for use in application development, testing, and deployment.</p> <p>Wing 7 can be used to develop and debug Python code running inside of Docker containers. This is accomplished by setting up a mapping of local (host-side) directories into the container, and then configuring Wing so it can accept debug connections from the container.</p> <div class="section"> <h3 class="title-3">Prerequisites</h3> <p>Before you can work with Docker you will need to download and install it.</p> <p><strong>On Windows and macOS</strong>, downloading Docker Desktop from the <a class="reference" href="https://www.docker.com/">Docker</a> website is the easiest way to install it. Be sure to launch the Docker Desktop after you install it, so the daemon is started.</p> <p><strong>On most Linux distributions</strong>, Docker CE (the free community edition) can be installed with the <tt class="literal"><span class="pre">docker-engine</span></tt> package as <a class="reference" href="https://runnable.com/docker/install-docker-on-linux">described here</a>.</p> <p>You should also install <a class="reference" href="https://wingware.com/downloads/wing-pro">Wing Pro</a> if you don't already have it.</p> </div> <div class="section"> <h3 class="title-3">Create a Working Example</h3> <p>Next set up a small real world example by creating a directory <tt class="literal"><span class="pre">docker</span></tt> and placing the following files into it.</p> <p><strong>Dockerfile</strong>:</p> <pre class="literal-block"> FROM python:3.7 WORKDIR /app RUN pip install --trusted-host pypi.python.org Flask EXPOSE 80 CMD [&quot;python&quot;, &quot;app.py&quot;] </pre> <p><strong>app.py:</strong></p> <div class="python-highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span> <span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span> <span class="nd">@app.route</span><span class="p">(</span><span class="s">&quot;/&quot;</span><span class="p">)</span> <span class="k">def</span> <span class="nf">hello</span><span class="p">():</span> <span class="k">return</span> <span class="s">&quot;&lt;h3&gt;Hello World!&lt;/h3&gt;Your app is working.&lt;br/&gt;&lt;/br/&gt;&quot;</span> <span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&quot;__main__&quot;</span><span class="p">:</span> <span class="n">app</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">host</span><span class="o">=</span><span class="s">&#39;0.0.0.0&#39;</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="mi">80</span><span class="p">,</span> <span class="n">use_reloader</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span> </pre></div> <p>Then build the Docker container by typing the following in the <tt class="literal"><span class="pre">docker</span></tt> directory:</p> <pre class="literal-block"> docker build --tag=myapp . </pre> <p>You can now run your container like this:</p> <pre class="literal-block"> docker run -v &quot;/path/to/docker&quot;:/app -p 4000:80 myapp </pre> <p>You will need to substitute <tt class="literal"><span class="pre">/path/to/docker</span></tt> with the path to the <tt class="literal"><span class="pre">docker</span></tt> directory you created above; the quotes make it work if the path has spaces in it.</p> <p>You can now try this tiny Flask- web app by pointing a browser running on your host system at it:</p> <p><strong>If you are using Docker Desktop</strong>, then use <a class="reference" href="http://localhost:4000/">http://localhost:4000/</a></p> <p><strong>If you are using Docker CE</strong>, you will need to determine the IP address of your container and use that instead of <tt class="literal"><span class="pre">localhost</span></tt>. One way to do this is to type <tt class="literal"><span class="pre">docker</span> <span class="pre">ps</span></tt> to find the Container ID for your container and then use it in the following in place of <tt class="literal"><span class="pre">c052478b0f8a</span></tt>:</p> <pre class="literal-block"> docker inspect -f &quot;{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}&quot; c052478b0f8a </pre> <p>Notice that if you make a change to <tt class="literal"><span class="pre">app.py</span></tt> in Wing, then the change will be reflected in your browser when you reload the page. This is due to using both the <tt class="literal"><span class="pre">-v</span></tt> argument for <tt class="literal"><span class="pre">docker</span> <span class="pre">run</span></tt> to mount a volume in the container, and the fact that <tt class="literal"><span class="pre">app.run()</span></tt> for Flask is being passed <tt class="literal"><span class="pre">use_reloader=True</span></tt>.</p> </div> <div class="section"> <h3 class="title-3">Configure Debugging</h3> <p>In order to debug <tt class="literal"><span class="pre">app.py</span></tt> in Wing, you will need to copy in and configure some additional files that allow invocation of Wing's debugger and connection to the IDE.</p> <p><strong>(1) Install the debugger</strong></p> <p>To access Wing's debugger on the container, add another <tt class="literal"><span class="pre">-v</span></tt> mapping to your <tt class="literal"><span class="pre">docker</span> <span class="pre">run</span></tt> command line, so the Wing installation on the host is made available to the container. For example on Windows:</p> <pre class="literal-block"> docker run -v &quot;C:/Program Files (x86)/Wing Pro 7.1&quot;:/wingpro7 -v C:/Users/test/docker:/app myapp </pre> <p>Or on Linux:</p> <pre class="literal-block"> docker run -v /usr/lib/wingpro7:/wingpro7 -v /home/test/docker:/app myapp </pre> <p>Or for macOS:</p> <pre class="literal-block"> docker run -v /Applications/WingPro.app/Contents/Resources:/wingpro7 -v /Users/test/docker:/app myapp </pre> <p>You will need to substitute the correct installation location for Wing on your host, which can be seen in Wing's <tt class="literal"><span class="pre">About</span></tt> box, and the full path to the <tt class="literal"><span class="pre">docker</span></tt> directory you created earlier.</p> <p>Mapping the Wing installation across OSes (for example from Windows host to Linux container) works because Wing's installation has all the files necessary files for debugging on every supported OS.</p> <p><strong>(2) Copy and configure wingdbstub.py</strong></p> <p>Debugging is initiated on the Docker side by importing Wing's <tt class="literal"><span class="pre">wingdbstub</span></tt> module. To use this, copy <tt class="literal"><span class="pre">wingdbstub.py</span></tt> from your Wing installation to your mapped directory on the host. For example on a Windows host:</p> <pre class="literal-block"> copy &quot;C:/Program Files (x86)/Wing Pro 7.1/wingdbstub.py&quot; C:/Users/test/docker </pre> <p>Or on a Linux host:</p> <pre class="literal-block"> cp /usr/lib/wingpro7/wingdbstub.py /home/test/docker </pre> <p>Or a macOS host:</p> <pre class="literal-block"> cp /Applications/WingPro.app/Contents/Resources/wingdbstub.py /Users/test/docker </pre> <p>After copying, you will need to edit the file to change <tt class="literal"><span class="pre">kWingHostPost</span></tt> from <tt class="literal"><span class="pre">localhost:50005</span></tt> to a value that uses the IP address or name of the host computer, for example if your host's IP address is <tt class="literal"><span class="pre">192.168.1.50</span></tt>:</p> <div class="python-highlight"><pre><span class="n">kWingHostPort</span> <span class="o">=</span> <span class="s">&#39;192.168.1.50:50005&#39;</span> </pre></div> <p>You will also need to set <tt class="literal"><span class="pre">WINGHOME</span></tt> to the location where you have mapped your Wing installation on the container:</p> <div class="python-highlight"><pre><span class="n">WINGHOME</span> <span class="o">=</span> <span class="s">&#39;/wingpro7&#39;</span> </pre></div> <p><strong>(3) Enable access</strong></p> <p>Next you need to copy the authentication token file <tt class="literal"><span class="pre">wingdebugpw</span></tt> from the <tt class="literal"><span class="pre">Settings</span> <span class="pre">Directory</span></tt> listed in Wing's <tt class="literal"><span class="pre">About</span></tt> box to the same directory as your copy of <tt class="literal"><span class="pre">wingdbstub.py</span></tt>, in this case the <tt class="literal"><span class="pre">docker</span></tt> directory on the host system.</p> <p>Then add to the <tt class="literal"><span class="pre">Debugger</span> <span class="pre">&gt;</span> <span class="pre">Advanced</span> <span class="pre">&gt;</span> <span class="pre">Allowed</span> <span class="pre">Hosts</span></tt> preference either the host's IP address (if using Docker Desktop) or the container's IP address determined with <tt class="literal"><span class="pre">docker</span> <span class="pre">inspect</span></tt> as described above (if using Docker CE). The host IP is used for Docker Desktop because of how it configures networking for containers; there is access from container to host but no access in the other direction, so the host thinks it is receiving a connection from one of its own network interfaces.</p> <p>You will also need to tell Wing to listen for debug connections initiated from the outside by clicking on the bug icon in the lower left of Wing's window and enabling <tt class="literal"><span class="pre">Accept</span> <span class="pre">Debug</span> <span class="pre">Connections</span></tt>.</p> <p><strong>(4) Establish a file mapping</strong></p> <p>If the <tt class="literal"><span class="pre">docker</span></tt> directory you mapped with the <tt class="literal"><span class="pre">-v</span></tt> option for <tt class="literal"><span class="pre">docker</span> <span class="pre">run</span></tt> does not appear on the same path on the host and container then you will need to communicate the mapping to Wing as well, with the <tt class="literal"><span class="pre">Debugger</span> <span class="pre">&gt;</span> <span class="pre">Advanced</span> <span class="pre">&gt;</span> <span class="pre">Location</span> <span class="pre">Map</span></tt> preference.</p> <p>For the <tt class="literal"><span class="pre">docker</span> <span class="pre">run</span></tt> example above and container IP address of <tt class="literal"><span class="pre">172.17.0.2</span></tt> you would add an entry as follows:</p> <b>Remote IP Address:</b> 172.17.0.2<br/> <b>File Mappings:</b><br/> &nbsp;&nbsp;&nbsp;&nbsp;&#x1f518;&nbsp;<b>Specify Mapping</b><br/> &nbsp;&nbsp;&nbsp;&nbsp;<b>Remote:</b> /app<br/> &nbsp;&nbsp;&nbsp;&nbsp;<b>Local:</b> C:/Users/test/docker<br/> <br><p>This step could be skipped entirely if the location of files on the container and the host are the same (for example using <tt class="literal"><span class="pre">/app</span></tt> also on the host instead of creating a directory named <tt class="literal"><span class="pre">docker</span></tt>).</p> <p>Also, if using Docker Desktop where the container IP is the same as the hosts's, it is important to choose a location for the container side of the mapping that either (a) does not exist on the host, or (b) is the same as the location on the host. If the directory exists on the host but has different Python files in it, the <tt class="literal"><span class="pre">Location</span> <span class="pre">Map</span></tt> will be incorrectly applied to them if you try to debug them.</p> <p><strong>(5) Initiate debug</strong></p> <p>Once these steps are complete, you can initiate debug from Python code running in the container by importing the module <tt class="literal"><span class="pre">wingdbstub.py</span></tt> as follows:</p> <div class="python-highlight"><pre><span class="kn">import</span> <span class="nn">wingdbstub</span> </pre></div> <p>This can be added as the first line of <tt class="literal"><span class="pre">app.py</span></tt>. After saving the file, Flask should auto-reload it, which will initiate debug and connect to the IDE so that the bug icon in the lower left of Wing's Window turns green and the toolbar changes to its debug configuration. The application keeps running until it reaches a breakpoint or exception.</p> <p>Next set a breakpoint by clicking leftmost margin to the left of the <tt class="literal"><span class="pre">return</span></tt> statement in <tt class="literal"><span class="pre">app.py</span></tt> and then trigger it by reloading the page in your browser. Now you can use Wing to step through and inspect the data being used in the debug process.</p> <p>To learn more about Wing's debugger, take a look at the <tt class="literal"><span class="pre">Tutorial</span></tt> in Wing's Help menu or the <tt class="literal"><span class="pre">Debugging</span> <span class="pre">Code</span></tt> section of the <a class="reference" href="/doc/howtos/quickstart">Quick Start Guide</a>.</p> </div> <div class="section"> <h3 class="title-3">Trouble-Shooting</h3> <p>If your configuration does not work, try setting <tt class="literal"><span class="pre">kLogFile</span></tt> in your copy of <tt class="literal"><span class="pre">wingdbstub.py</span></tt> to see whether the debugger is reporting errors. Also, looking at the end of <tt class="literal"><span class="pre">ide.log</span></tt> in the <tt class="literal"><span class="pre">Settings</span> <span class="pre">Directory</span></tt> listed in Wing's <tt class="literal"><span class="pre">About</span></tt> box may reveal why a connection is failing, if it is being refused by the IDE.</p> <p>Setting <tt class="literal"><span class="pre">kExitOnFailure</span></tt> in your copy of <tt class="literal"><span class="pre">wingdbstub.py</span></tt> is another way to see why debug or the connection to the IDE is failing. In this case, when you restart the container it will fail to start and print a message indicating the error encountered during <tt class="literal"><span class="pre">import</span> <span class="pre">wingdbstub</span></tt>.</p> <p>If the debug connection is established but breakpoints are not reached, the <tt class="literal"><span class="pre">Location</span> <span class="pre">Map</span></tt> preference is likely incorrect. One way to diagnose this is to add <tt class="literal"><span class="pre">assert</span> <span class="pre">0</span></tt> to your code. Wing will always stop on that and will report the file it thinks it should be opening in the <tt class="literal"><span class="pre">Exceptions</span></tt> tool.</p> <p>And, as always, don't hesitate to email <a class="reference" href="mailto:support&#64;wingware.com">support&#64;wingware.com</a> for help.</p> </div> <div class="section"> <h3 class="title-3">Notes</h3> <p>Docker CE (but not Docker Desktop) is sometimes used to host a more complete installation of Linux, acting more like a stand-alone system that includes the ability to <tt class="literal"><span class="pre">ssh</span></tt> from the host system into the container. In this case, Wing Pro's <a class="reference" href="https://wingware.com/doc/proj/remote-hosts">Remote Development</a> capability can be used, with much less manual configuration, to debug code running under Docker. For more information, see <a class="reference" href="/doc/howtos/remote-development">Remote Python Development</a> (if the debug process can be launched from the IDE) or <a class="reference" href="/doc/howtos/debugging-web-remote">Remote Web Development</a> (if the debug process is launched from outside of the IDE).</p> <br> <br><p>That's it for now! We'll be back soon with more <a class="reference" href="/hints">Wing Tips</a> for Wing Python IDE.</p> </div> https://wingware.com/hints/dockerTue, 24 Sep 2019 01:00:00 GMTViewing Arrays and Data Frames in Wing Pro 7https://wingware.com/hints/array-viewer<p>Wing Pro 7 introduced an array and data frame viewer that can be used to inspect data objects in the debugger. Values are transferred to the IDE according to what portion of the data is visible on the screen, so working with large data sets won't slow down the IDE.</p> <p>The array viewer works with Pandas, numpy, sqlite3, xarray, Python's builtin lists, tuples, and dicts, and other classes that emulate lists, tuples, or dicts.</p> <p>To use the array viewer, right-click on a value in the <tt class="literal"><span class="pre">Stack</span> <span class="pre">Data</span></tt> tool in Wing Pro and select <tt class="literal"><span class="pre">Show</span> <span class="pre">Value</span> <span class="pre">as</span> <span class="pre">Array</span></tt>:</p> <img src="https://wingware.com/images/blog/array-viewer/menu.png" alt="/images/blog/array-viewer/menu.png" backrefs="" class="doc-image" dupnames="" ids="" names="" width="1018px" /><p>This reveals the array viewer and displays the selected item from the <tt class="literal"><span class="pre">Stack</span> <span class="pre">Data</span></tt> tree, in this case the global variable <tt class="literal"><span class="pre">pandas_df</span></tt>:</p> <img src="https://wingware.com/images/blog/array-viewer/array.png" alt="/images/blog/array-viewer/array.png" backrefs="" class="doc-image" dupnames="" ids="" names="" width="1019px" /><p>Wing fetches data for display as you move the scroll bars. The <tt class="literal"><span class="pre">Filter</span></tt> can be used to display only matching rows:</p> <img src="https://wingware.com/images/blog/array-viewer/filter.png" alt="/images/blog/array-viewer/filter.png" backrefs="" class="doc-image" dupnames="" ids="" names="" width="582px" /><p>The drop down next to the <tt class="literal"><span class="pre">Filter</span></tt> field may be used to select plain text, wildcard, or regular expression searching, to control whether searches are case sensitive, and to select whether to search on all columns or only the visible columns.</p> <p>If more space is needed to view data, the <tt class="literal"><span class="pre">Stack</span> <span class="pre">Data</span></tt> tool's tab can be dragged out of the window, to create a separate window for it.</p> <br> <br><p>That's it for now! We'll be back soon with more <a class="reference" href="/hints">Wing Tips</a> for Wing Python IDE.</p> https://wingware.com/hints/array-viewerThu, 19 Sep 2019 01:00:00 GMTPresentation Mode in Wing 7https://wingware.com/hints/presentation-mode<p>Presentation Mode, added in Wing 7, temporarily applies a selected magnification to the entire user interface, so the screen can be read more easily during meetings or talks.</p> <p>To activate this mode, check on <tt class="literal"><span class="pre">Presentation</span> <span class="pre">Mode</span></tt> in the high-level configuration menu accessed with the <img src="https://wingware.com/images/blog/dark-mode/menu.png" alt="menuicon" backrefs="" class="inline-image" dupnames="" ids="" names="" /> icon at the top right of Wing's window:</p> <img src="https://wingware.com/images/blog/presentation-mode/menu.png" alt="/images/blog/presentation-mode/menu.png" backrefs="" class="doc-image" dupnames="" height="320px" ids="" names="" width="275px" /><p>You will be presented with a confirmation dialog that also provides a link to the preference that controls the level of magnification:</p> <img src="https://wingware.com/images/blog/presentation-mode/dialog.png" alt="/images/blog/presentation-mode/dialog.png" backrefs="" class="doc-image" dupnames="" height="118px" ids="" names="" width="672px" /><p>To apply the mode change, Wing restarts and reloads the current project in the same state as you left it, but with the contents of the window magnified.</p> <div class="section"> <h3 class="title-3">Before</h3> <img src="https://wingware.com/images/blog/presentation-mode/window-1.0.png" alt="/images/blog/presentation-mode/window-1.0.png" backrefs="" class="doc-image" dupnames="" ids="" names="" /></div> <div class="section"> <h3 class="title-3">After</h3> <img src="https://wingware.com/images/blog/presentation-mode/window-1.5.png" alt="/images/blog/presentation-mode/window-1.5.png" backrefs="" class="doc-image" dupnames="" ids="" names="" /><p>To disable Presentation Mode, uncheck the high-level configuration menu item again.</p> <br> <br><p>That's it for now! We'll be back soon with more <a class="reference" href="/hints">Wing Tips</a> for Wing Python IDE.</p> </div> https://wingware.com/hints/presentation-modeWed, 11 Sep 2019 01:00:00 GMTDark Mode and Color Configuration in Wing Python IDEhttps://wingware.com/hints/dark-mode<p>Wing 7 added four new dark color palettes and the ability to quickly toggle between light and dark mode using the <img src="https://wingware.com/images/blog/dark-mode/menu.png" alt="menuicon" backrefs="" class="inline-image" dupnames="" ids="" names="" /> menu icon in the top right of the IDE window. When <tt class="literal"><span class="pre">Dark</span> <span class="pre">Mode</span></tt> is selected, Wing switches to the most recently used dark color configuration, or the default dark configuration if none has been used.</p> <p>To select which dark mode is used, change <tt class="literal"><span class="pre">Color</span> <span class="pre">Palette</span></tt> on the first page of Wing's <tt class="literal"><span class="pre">Preferences</span></tt>. The dark palettes that ship with Wing 7 are:</p> <img src="https://wingware.com/images/blog/dark-mode/black-background.png" alt="/images/blog/dark-mode/black-background.png" backrefs="" caption="Black Background: The classic original dark mode for Wing" class="doc-image" dupnames="" height="574px" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" width="773px" /><p style="padding-top:0px;"><i>Black Background: The classic original dark mode for Wing</i></p><img src="https://wingware.com/images/blog/dark-mode/cherry-blossom.png" alt="/images/blog/dark-mode/cherry-blossom.png" backrefs="" caption="Cherry Blossom: New in Wing 7" class="doc-image" dupnames="" height="574px" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" width="773px" /><p style="padding-top:0px;"><i>Cherry Blossom: New in Wing 7</i></p><img src="https://wingware.com/images/blog/dark-mode/dracula.png" alt="/images/blog/dark-mode/dracula.png" backrefs="" caption="Dracula: New in Wing 7" class="doc-image" dupnames="" height="574px" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" width="773px" /><p style="padding-top:0px;"><i>Dracula: New in Wing 7</i></p><img src="https://wingware.com/images/blog/dark-mode/monokai.png" alt="/images/blog/dark-mode/monokai.png" backrefs="" caption="Monokai" class="doc-image" dupnames="" height="574px" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" width="773px" /><p style="padding-top:0px;"><i>Monokai</i></p><img src="https://wingware.com/images/blog/dark-mode/one-dark.png" alt="/images/blog/dark-mode/one-dark.png" backrefs="" caption="One Dark: The default dark color palette" class="doc-image" dupnames="" height="574px" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" width="773px" /><p style="padding-top:0px;"><i>One Dark: The default dark color palette</i></p><img src="https://wingware.com/images/blog/dark-mode/positronic.png" alt="/images/blog/dark-mode/positronic.png" backrefs="" caption="Positronic: New in Wing 7" class="doc-image" dupnames="" height="574px" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" width="773px" /><p style="padding-top:0px;"><i>Positronic: New in Wing 7</i></p><img src="https://wingware.com/images/blog/dark-mode/solarized-dark.png" alt="/images/blog/dark-mode/solarized-dark.png" backrefs="" caption="Solarized Dark" class="doc-image" dupnames="" height="574px" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" width="773px" /><p style="padding-top:0px;"><i>Solarized Dark</i></p><img src="https://wingware.com/images/blog/dark-mode/sun-steel.png" alt="/images/blog/dark-mode/sun-steel.png" backrefs="" caption="Sun Steel: New in Wing 7" class="doc-image" dupnames="" height="574px" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" width="773px" /><p style="padding-top:0px;"><i>Sun Steel: New in Wing 7</i></p><p>In most cases you will also want to enable the <tt class="literal"><span class="pre">User</span> <span class="pre">Interface</span> <span class="pre">&gt;</span> <span class="pre">Use</span> <span class="pre">Color</span> <span class="pre">Palette</span> <span class="pre">Throughout</span> <span class="pre">the</span> <span class="pre">UI</span></tt> preference, so that the color palette is applied to more than just editors. This preference is enabled automatically when the <tt class="literal"><span class="pre">Dark</span> <span class="pre">Mode</span></tt> menu item is used for the first time, and is enabled in all of the above screenshots. However, it may be disabled so only the editors are displayed dark. Wing will remember that choice when subsequently changing between light and dark modes.</p> <div class="note"> Note that on macOS 10.14+ with Wing 7.1+, the system-defined Dark Mode may be used instead, by leaving the <tt class="literal"><span class="pre">User</span> <span class="pre">Interface</span> <span class="pre">&gt;</span> <span class="pre">Use</span> <span class="pre">Color</span> <span class="pre">Palette</span> <span class="pre">Throughout</span> <span class="pre">the</span> <span class="pre">UI</span></tt> preference unchecked, and then selecting Dark Mode in the macOS System Preferences. In this approach, the <tt class="literal"><span class="pre">Color</span> <span class="pre">Palette</span></tt> preference in Wing should be set to <tt class="literal"><span class="pre">Classic</span> <span class="pre">Default</span></tt> or one of the dark color palettes.</div> <div class="section"> <h3 class="title-3">Color Configuration</h3> <p>Aside from selecting the overall color palette with the <tt class="literal"><span class="pre">User</span> <span class="pre">Interface</span> <span class="pre">&gt;</span> <span class="pre">Color</span> <span class="pre">Palette</span></tt> preference, it is also possible to override individual colors throughout the preferences, or to write your own color palette, including colors for the UI and optionally also for syntax highlighting in the editor. This is described in more detail in <a class="reference" href="/doc/custom/qt-styles">Display Style and Colors</a>.</p> <br> <br><p>That's it for now! We'll be back soon with more <a class="reference" href="/hints">Wing Tips</a> for Wing Python IDE.</p> </div> https://wingware.com/hints/dark-modeMon, 02 Sep 2019 01:00:00 GMTIntroducing Variables with Refactoring in Wing Prohttps://wingware.com/hints/refactor-intro-var<p>In past issues of <a class="reference" href="/hints">Wing Tips</a> we covered a number of the refactoring operations available in Wing Pro, such as <a class="reference" href="/hints/refactor-rename">renaming symbols</a>, <a class="reference" href="/hints/refactoring-move">moving symbols</a>, and <a class="reference" href="/hints/refactor-introduce">introducing functions and methods</a>. To finish our series on refactoring, let's take a look at how to introduce a variable based on existing Python code, using Wing Pro's <tt class="literal"><span class="pre">Introduce</span> <span class="pre">Variable</span></tt> refactoring operation.</p> <p>This operation is used to replace selected occurrences of an expression with a new local variable, either to make code more readable or to avoid redundant computation.</p> <div class="section"> <h3 class="title-3">Example</h3> <p>Here's a simple example that introduces a local variable <tt class="literal"><span class="pre">tokens</span></tt> to replace repeated use of the expression <tt class="literal"><span class="pre">logical.fTokens</span></tt>, in order to make the code more readable:</p> <img src="https://wingware.com/images/blog/refactor-intro-var/introduce-example.gif" alt="/images/blog/refactor-intro-var/introduce-example.gif" backrefs="" caption="Shown Above: Select the expression &quot;logical.fTokens&quot; to assign to the new local variable, Right-click to initiate Introduce Variable, type in the variable name &quot;tokens&quot;, execute the introduce operation, select the new line of code, and then select the new variable to highlight where it is being used." class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Shown Above: Select the expression "logical.fTokens" to assign to the new local variable, Right-click to initiate Introduce Variable, type in the variable name "tokens", execute the introduce operation, select the new line of code, and then select the new variable to highlight where it is being used.</i></p><p>Notice that Wing replaces all occurrences of the selected expression <tt class="literal"><span class="pre">logical.fTokens</span></tt> with a reference to the new variable <tt class="literal"><span class="pre">tokens</span></tt>. Using refactoring to introduce a new variable is usually much easier and less prone to errors than making edits of this type manually.</p> <p>When only a subset of the instances of an expression should be replaced with the new variable, some matches in the <tt class="literal"><span class="pre">Refactoring</span></tt> tool can be unchecked, or in some cases using <a class="reference" href="/hints/multi-selection">multi-selection</a> may be preferable.</p> </div> <div class="section"> <h3 class="title-3">Try It Yourself</h3> <p>You can easily try this out in your copy of Wing Pro, by selecting any expression in your Python code and choosing <tt class="literal"><span class="pre">Introduce</span> <span class="pre">Variable</span></tt> from the <tt class="literal"><span class="pre">Refactor</span></tt> menu. As in the above example, you will be asked to choose the name of the new variable and Wing will replace all occurrences of the expression with a reference to the new variable. An unwanted introduction can be backed out with the <tt class="literal"><span class="pre">Revert</span></tt> button in the <tt class="literal"><span class="pre">Refactoring</span></tt> tool.</p> <br> <br><p>That's it for now! We'll be back soon with more <a class="reference" href="/hints">Wing Tips</a> for Wing Python IDE.</p> </div> https://wingware.com/hints/refactor-intro-varMon, 26 Aug 2019 01:00:00 GMTMoving Code with Refactoring in Wing Prohttps://wingware.com/hints/refactoring-move<p>In this issue of <a class="reference" href="/hints">Wing Tips</a> we explain how to quickly move functions, methods, classes, and other symbols around in Python code, using Wing Pro's <tt class="literal"><span class="pre">Move</span> <span class="pre">Symbol</span></tt> refactoring operation.</p> <p>This operation takes care of updating all the points of reference for the symbol that is being moved. For example, if a function is moved from one module to another then Wing will update all the points of call for that function to import the module it has been moved into and invoke the function from there.</p> <div class="section"> <h3 class="title-3">Example</h3> <p>Here's a simple example that moves a class from one file to another:</p> <img src="https://wingware.com/images/blog/refactor-move/move-example.gif" alt="/images/blog/refactor-move/move-example.gif" backrefs="" caption="Shown Above: Right-click to initiate Move Symbol, select target location testtarget.py, execute the move operation, select the moved function, and then select the added import statement." class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Shown Above: Right-click to initiate Move Symbol, select target location testtarget.py, execute the move operation, select the moved function, and then select the added import statement.</i></p><p>Notice that Wing adds the necessary <tt class="literal"><span class="pre">import</span> <span class="pre">testtarget</span></tt> to <tt class="literal"><span class="pre">testmove.py</span></tt> and changes the two places where the class <tt class="literal"><span class="pre">MyTestClass</span></tt> is used to reference the class in its new location in <tt class="literal"><span class="pre">testtarget.py</span></tt>. Using refactoring is much easier and less prone to errors than making edits of this type manually.</p> </div> <div class="section"> <h3 class="title-3">Try It Yourself</h3> <p>You can easily try this out in your copy of Wing Pro, by selecting any symbol in your Python code base, and choosing <tt class="literal"><span class="pre">Move</span> <span class="pre">Symbol</span></tt> from the <tt class="literal"><span class="pre">Refactor</span></tt> menu. As in the above example, you will be asked to choose the target location for the move, which can either be the top level of any module, or within a class or def. An unwanted move can be backed out with the <tt class="literal"><span class="pre">Revert</span></tt> button in the <tt class="literal"><span class="pre">Refactoring</span></tt> tool.</p> <br> <br><p>That's it for now! We'll be back soon with more <a class="reference" href="/hints">Wing Tips</a> for Wing Python IDE.</p> </div> https://wingware.com/hints/refactoring-moveMon, 19 Aug 2019 01:00:00 GMTIntroducing Functions and Methods with Refactoring in Wing Prohttps://wingware.com/hints/refactor-introduce<p>In this issue of <a class="reference" href="/hints">Wing Tips</a> we explain how to quickly create new functions and methods out of existing blocks of Python code, using Wing Pro's <tt class="literal"><span class="pre">Extract</span> <span class="pre">Method/Function</span></tt> refactoring operation.</p> <p>This is useful whenever you have some existing code that you want to reuse in other places, or in cases where code gets out of hand and needs to be split up to make it more readable, testable, and maintainable.</p> <p>Wing supports extracting functions and methods for any selected code, so long as that code does not contain <tt class="literal"><span class="pre">return</span></tt> or <tt class="literal"><span class="pre">yield</span></tt> statements. In that case automatic extraction is not possible, since Wing cannot determine how the extracted function should be called from or interact with the original code.</p> <div class="section"> <h3 class="title-3">Example</h3> <p>Here's a simple example that extracts several lines of code for reuse elsewhere:</p> <img src="https://wingware.com/images/blog/refactor-introduce/extract-example.gif" alt="/images/blog/refactor-introduce/extract-example.gif" backrefs="" caption="Shown Above: Select code to extract, right-click to initiate Extract Function / Method, enter test_extract as the function name, select local nested function as the extraction type, then highlight the extracted point of call, and go to definition of the new nested function." class="doc-image" dupnames="" ids="" names="" style="padding-bottom:5px;margin-bottom:5px;" /><p style="padding-top:0px;"><i>Shown Above: Select code to extract, right-click to initiate Extract Function / Method, enter test_extract as the function name, select local nested function as the extraction type, then highlight the extracted point of call, and go to definition of the new nested function.</i></p><p>Notice that when the code is extracted, Wing takes care of adding the necessary arguments and return values and places an invocation of the new method or function into the original context. This is done in a way that does not alter the functionality of the code, even though it has been restructured.</p> </div> <div class="section"> <h3 class="title-3">Try It Yourself</h3> <p>You can easily try this out in your copy of Wing Pro, by selecting some lines in your Python code base, and choosing <tt class="literal"><span class="pre">Extract</span> <span class="pre">Method/Function</span></tt> from the <tt class="literal"><span class="pre">Refactor</span></tt> menu. As in the above example, you will be asked to enter a name and choose whether to place the extracted code into a function at the top level of the module, in a method in the current class, or in a nested function in the current method or function. An unwanted extraction can be backed out with the <tt class="literal"><span class="pre">Revert</span></tt> button in the <tt class="literal"><span class="pre">Refactoring</span></tt> tool.</p> <p>If you want to move the code to some other module or class, you can do this after initially extracting it by using the <tt class="literal"><span class="pre">Move</span> <span class="pre">Symbol</span></tt> operation in the <tt class="literal"><span class="pre">Refactor</span></tt> menu. We'll take a look at that next time.</p> <br> <br><p>That's it for now! We'll be back soon with more <a class="reference" href="/hints">Wing Tips</a> for Wing Python IDE.</p> </div> https://wingware.com/hints/refactor-introduceMon, 22 Jul 2019 01:00:00 GMTExtending 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 GMT