Helping Wing Analyze Code
Wing's source analyser can only read Python code and does not contain support for understanding C/C++ extension module code other than by attempting to import the extension module and introspecting its contents (which yields only a limited amount of information and cannot determine argument number, name, or types). Also, since Python is a dynamic language, it is possible to craft code that Wing's static analysis engine cannot understand.
There are a number of ways of assistant Wing's static source analyzer in determining the type of values in Python code.
Using Live Runtime State
When a debug process is active, or when working in the Python Shell, Wing extracts relevant type information from the live runtime state associated with your Python code. Since this yields complete and correct type information even for code that Wing's static analysis engine cannot understand, it is often useful to run to a breakpoint before designing new code that is intended to work in that context.
In the editor, the cog icon in the auto-completer indicates that type information was found in the live runtime state.
In Wing IDE Professional, the Debug Probe can be used to immediately try out new code in the runtime environment for which it is being designed.
Both the Python Shell and (in Wing Pro) the Debug Probe can mark an active range in the editor so code can quickly be reevaluated as it is being edited. This is done by selecting the code and pressing the Active Range icon in the upper right of the tool into which you want to set the active range.
Using isinstance() to Assist Analysis
One way to inform the static analysis engine of the type of a variable is to add an isinstance call in your code. For example isinstance(obj, CMyClass) or assert isinstance(obj, CMyClass) when runtime type checking is desired. The code analyzer will pick up on these and present more complete information for the asserted values.
In cases where doing this introduces a circular import, you can use a conditional to allow Wing's static analyser to process the code without causing problems when it is executed:
if 0: import othermodule assert isinstance(myvariable, othermodule.COtherClass)
In most code, a few isinstance calls go a long way to making code faster and easier to edit and navigate.
Using *.pi Files to Assist Analysis
It is also possible to create a *.pi (Python Interface) file that describes the contents of a module. This file is simply a Python skeleton with the appropriate structure, call signature, and return values to match the functions, attributes, classes, and methods defined in a module. Wing IDE will read this file and merge its contents with any information it can obtain through static analysis or by loading an extension module.
In somes cases, as for Python bindings for GUI and other toolkits, these *.pi files can be auto-generated from interface description files. The code that Wing uses to automatically generate *.pi files from extension modules is in src/wingutils/generate_pi.py in your Wing IDE installation, and another example that is used to generate interface information for PyGTK is in src/wingutils/pygtk_to_pi.py.
Naming and Placing *.pi Files
Wing expects the *.pi file name to match the name of the module. For example, if the name referenced by import as mymodule then Wing looks for mymodule.pi.
The most common place to put the *.pi file is in the same directory as the *.pyd, *.so, or *.py for the module is is describing. *.pi files that describe entire packages (directories containing __init__.py) should be placed in the package directory's parent directory.
If Wing cannot find the *.pi file in the same directory as the module, it proceeds to search as follows, choosing the first matching *.pi file:
- In the path set with the Source Analysis > Advanced > Interfaces Path preference.
- In the resources/builtin-pi-files in the Wing IDE installation. This is used to ship type overrides for Python's builtin types and standard library.
- In resources/package-pi-files, which is used to ship some *.pi files for commonly used third party packages.
For all of these, Wing inspects the path directory for a matching *.pi file and treats any sub-directories as packages.
In cases where Wing cannot find a *.pi at all for an extension module, it will still attempt to load the extension module by name, in a separate process space, so that it can introspect its contents. The results of this operation are stored in pi-cache within the Cache Directory shown in Wing's About box. This file is regenerated only if the *.pyd or *.so for the loaded extension module changes.
For Python source modules, absence of a *.pi causes Wing to fall back on static analysis and (if available) runtime analysis through the debugger.
Merging *.pi Name Spaces
When Wing finds a *.pi file in the same directory as a Python module or extension module, or if it finds it using the Source Analysis > Advanced > Interfaces Path preference, then Wing merges the contents of the *.pi file with any information found by analyzing or introspecting the module. The contents of the *.pi file take precedence when symbols are defined in both places.
Creating Variants by Python Version
In rare cases, you may need to create variants of your *.pi files according to Python version. An example of this is in resources/builtin-pi-files, the directory used to ship type overrides for Python's builtin types and standard library.
As noted above, Wing always looks first at the top level of an interface path directory for a matching *.pi file. If this fails then Wing tries looking in a sub-directory #.# named according to the major and minor version of Python being used with your source base, and subsequently in each lower major/minor version back to 2.0.
For example, if c:\share\pi\pi-files is on the interfaces path and Python 2.7 is being used, Wing will check first in c:\share\pi\pi-files, then in c:\share\pi\pi-files\2.7. then in c:\share\pi\pi-files\2.6, and so forth.