Kicad Python scripts: I'm doing it wrong

I’m just trying to get the simplest python script (the enumerate footprint example in section 15.4 of the Pcbnew manual) to work. I’ve tried that example script and a few others, and they all fail the same way; that is with some error message that complains about getattr. Does anybody know what I’m doing wrong? See the log following:

PyCrust 0.9.8 - KiCAD Python Shell
Python 2.7.10 (default, Jul  8 2015, 15:10:39) 
[GCC 5.1.0] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>import sys
>>>sys.path
['C:\\Program Files\\KiCad\\lib\\python27.zip', 'C:\\Program Files\\KiCad\\lib\\python2.7', 'C:\\Program Files\\KiCad\\lib\\python2.7\\plat-win32', 'C:\\Program Files\\KiCad\\lib\\python2.7\\lib-tk', 'C:\\Program Files\\KiCad\\lib\\python2.7\\lib-old', 'C:\\Program Files\\KiCad\\lib\\python2.7\\lib-dynload', 'C:\\building\\msys32\\mingw64', 'C:\\Program Files\\KiCad\\lib\\python2.7\\site-packages', 'C:\\Program Files\\KiCad\\lib\\python2.7\\site-packages\\wx-3.0-msw', '.', 'C:/Program Files/KiCad/bin/../share/kicad/scripting/plugins']
>>>import pads
3M_3E106-1230KV ->Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "./pads.py", line 10, in <module>
    print name,"->",m.GetLibRef(), m.GetReference()
  File "C:\Program Files\KiCad\lib\python2.7\site-packages/pcbnew.py", line 5769, in <lambda>
    __getattr__ = lambda self, name: _swig_getattr(self, MODULE, name)
  File "C:\Program Files\KiCad\lib\python2.7\site-packages/pcbnew.py", line 74, in _swig_getattr
    return _swig_getattr_nondynamic(self, class_type, name, 0)
  File "C:\Program Files\KiCad\lib\python2.7\site-packages/pcbnew.py", line 69, in _swig_getattr_nondynamic
    return object.__getattr__(self, name)
AttributeError: type object 'object' has no attribute '__getattr__'
>>>

(I’m running Kicad 4.0.1 on Windows 8.1. From the log, you can see that the script at least starts – “3M_3E106-1230KV” is an actual component in my footprint library, and it finds that.)

Did you try to trim the script down to bare essentials and add complexity to see what’s the derailing part?
Personally (I have no real clue about object oriented programming, maybe someday)… but above error message I don’t really understand, even when I look at the final area in question:
http://ci.kicad.org/job/kicad-doxygen/ws/build/pcbnew/doxygen-python/html/pcbnew_8py_source.html#l00069

Either m.GetLibRef() or m.GetReference() seem to fail…
I’d try to comment one out for testing each time and see where that leads to…
The following pad iteration doesn’t need that information afaik so you can even omit that part altogether?

Funny note… I can’t find:

File "C:\Program Files\KiCad\lib\python2.7\site-packages/pcbnew.py", line 5769, in <lambda>
    __getattr__ = lambda self, name: _swig_getattr(self, MODULE, name)

at line 5769 in pbnew.py, but at line 9251 and this seems to be part of a board related object/class/etc… no idea if that is correct behavior if all this code is supposed to do, was to iterate over some library and give you some details of the footprints therein… confusing.

On question for understanding, this piece of code…

m = FootprintLoad(libpath,name)
print name,"->",m.GetLibRef(), m.GetReference()

to me looks like I could also write:

FootprintLoad(libpath,name).GetLibRef()

Wouldn’t that mean, that somewhere there must be a def GetLibRef in pcbnew.py?
But searching for this with the little search function at the top right of that doxygen pcbnew nothing comes up.

def GetReference is in there a couple of times, so I’d say GetLibRef has been removed at some stage and that’s why it fails?

the problem is line 10 in pads.py. It’s probably a function or something getting passed a blank value when it expecting something.

pads.py

1 #!/usr/bin/python
2
3 from pcbnew import *
4
5 libpath = "/usr/share/kicad/modules/sockets.mod"
6 lst = FootprintEnumerate(libpath)
7
8 for name in lst:
9    m = FootprintLoad(libpath,name)
10   print name,"->",m.GetLibRef(), m.GetReference()
11   for p in m.GetPads():
12     print "\t",p.GetPadName(),p.GetPosition(),p.GetPos0(), p.GetOffset()

Thanks, I didn’t even know where to start at parsing the error message. (I’m a total newbie to python.)

It would seem that the online copy of pcbnew.py is very old… Much has changed. The latest file is half the size, apparently at the expense of documentation. Following the python trace into pcbnew.py isn’t very helpful, since Pcbnew.py is actually a SWIG (I had to look it up) interface module that simply connects to some C++ code that lives in the heart of Pcbnew.

If I remove the call to GetLibRef, the problem moves to the next line. As Sparky mentioned, GetLibRef can only be found in the Pcbnew manual. I don’t know why python doesn’t complain about that instead. On line 11, where the next error occurs, there is a call to GetPads. If I remove that, well there isn’t much point to the script then. At least GetPads is defined in pcbnew.py and in the pcbnew source code.

If I try the example in section 15.3 of the Pcbnew manual, I get the same error on line 11 in the call to module.Value().SetVisible. Again, if I remove that call, what is the point to the script?

It seems that the sample scripts are outdated (the example in section 15.4 would only have enumerated legacy footprints anyways) and some of the linkages to internal modules have become broken or have changed. I will see if I can find some other scripts that will actually run, so I can at least get started.

It looks like It’s looping through all the footprints in “/usr/share/kicad/modules/sockets.mod”. You probably hit a bad footprint somewhere in there that’s missing a “reference” (whatever that is). If you put a “print name” just after line 8 you can probably find the guilty culprit.

Thanks for the push SBW. I got it working. The script was already printing the footprint name; you can see the first footprint name in my log above, “3M_3E106-1230KV”. I tried deleting that footprint, and the next one had the same problem, etc. So that didn’t fix the script. While poking around in the Kicad source, I found an “examples” directory. (It was one of the 2 places where GetLibRef appeared; the other was in various versions of the pcbnew.html file.) It seems to contain the 5 example scripts for the manual, but somehow they are not making it into the manual when it gets built. The “listPcbLibrary.py” script still had the bad call to GetLibRef, but the bad call to GetPads was replaced with a call to Pads. Here is a working version of the script:

#!/usr/bin/python

from pcbnew import *

libpath = "c:/old_d/pcb/wcsc.mod"
lst = FootprintEnumerate(libpath)

for name in lst:
    m = FootprintLoad(libpath,name)
    print name,"->", m.GetReference()
    for p in m.Pads():
        print "\t",p.GetPadName(),p.GetPosition(),p.GetPos0(), p.GetOffset()

Obviously it would be helpful to have documentation available for the current pcbnew classes. The current online documentation is old, incorrect and insufficient. There may be some way to extract the needed information from the C++ source files. Perhaps SWIG has a solution. I’ll have to poke around.

I have one last problem. I find that I can execute a script exactly once using an “import” statement. After that the statement seems to be ignored and I have to exit both kicad and pcbnew to run the script again. Is there some other command to reload a script and try again?

Congratulations! I probably should have also mentioned another handy way to debug python is just run the commands interactively. You can use help() and dir() to poke around and see what objects can do. For example, at the command line, just type:

    $ python
    
    >>> from pcbnew import *
    >>> m = FootprintLoad("path_to_my_cool_footprint_library","my_footprint_name")
    >>> dir(m)
['Add', 'Add3DModel', 'AddChild', 'Back', 'CalculateBoundingBox', 'Cast', 'Cast_to_BOARD', 'Cast_to_DIMENSION', 'Cast_to_DRAWSEGMENT', 'Cast_to_D_PAD', 'Cast_to_EDGE_MODULE', 'Cast_to_MARKER_PCB', 'Cast_to_MODULE', 'Cast_to_PCB_TARGET', 'Cast_to_TEXTE_MODULE', 'Cast_to_TEXTE_PCB', 'Cast_to_TRACK', 'Cast_to_VIA', 'Cast_to_ZONE_CONTAINER', 'ClassOf', 'ClearAllNets', 'ClearBrightened', 'ClearFlags', 'ClearHighlighted', 'ClearSelected', 'Clone', 'Copy', 'CopyNetlistSettings', 'DeleteChild', 'DeleteStructure', 'Draw', 'DrawAncre', 'DrawEdgesOnly', 'DrawOutlinesWhenMoving', 'Duplicate', 'DuplicateAndAddItem', 'FindPadByName', 'Flip', 'FormatAngle', 'FormatInternalUnits', 'GetArea', 'GetAttributes', 'GetBoard', 'GetBoundingBox', 'GetCenter', 'GetClass', 'GetDescription', 'GetFPID', 'GetFlag', 'GetFlags', 'GetFootprintRect', 'GetInitialComments', 'GetKeywords', 'GetLastEditTime', 'GetLayer', 'GetLayerName', 'GetLayerSet', 'GetLink', 'GetList', 'GetLocalClearance', 'GetLocalSolderMaskMargin', 'GetLocalSolderPasteMargin', 'GetLocalSolderPasteMarginRatio', 'GetMenuImage', 'GetMsgPanelInfo', 'GetNextPadName', 'GetOrientation', 'GetPad', 'GetPadCount', 'GetParent', 'GetPath', 'GetPlacementCost180', 'GetPlacementCost90', 'GetPosition', 'GetReference', 'GetReferencePrefix', 'GetSelectMenuText', 'GetState', 'GetStatus', 'GetThermalGap', 'GetThermalWidth', 'GetTimeStamp', 'GetUniquePadCount', 'GetValue', 'GetZoneConnection', 'GraphicalItems', 'HitTest', 'IncrementFlag', 'IncrementItemReference', 'IncrementReference', 'IsBrightened', 'IsConnected', 'IsDragging', 'IsFlipped', 'IsHighlighted', 'IsLibNameValid', 'IsLocked', 'IsModified', 'IsMoving', 'IsNew', 'IsOnLayer', 'IsPlaced', 'IsReplaceable', 'IsResized', 'IsSelected', 'IsTrack', 'IsWireImage', 'IterateForward', 'Matches', 'Models', 'Move', 'MoveAnchorPosition', 'NeedsPlaced', 'Next', 'PadCoverageRatio', 'Pads', 'PadsLocked', 'Reference', 'RemoveChild', 'Replace', 'Rotate', 'RunOnChildren', 'SetAttributes', 'SetBrightened', 'SetDescription', 'SetFPID', 'SetFlag', 'SetFlags', 'SetForceVisible', 'SetHighlighted', 'SetImage', 'SetInitialComments', 'SetIsPlaced', 'SetKeywords', 'SetLastEditTime', 'SetLayer', 'SetLink', 'SetList', 'SetLocalClearance', 'SetLocalSolderMaskMargin', 'SetLocalSolderPasteMargin', 'SetLocalSolderPasteMarginRatio', 'SetLocked', 'SetModified', 'SetNeedsPlaced', 'SetOrientation', 'SetPadsLocked', 'SetParent', 'SetPath', 'SetPlacementCost180', 'SetPlacementCost90', 'SetPos', 'SetPosition', 'SetReference', 'SetSelected', 'SetStartEnd', 'SetState', 'SetStatus', 'SetThermalGap', 'SetThermalWidth', 'SetTimeStamp', 'SetValue', 'SetWireImage', 'SetZoneConnection', 'ShowShape', 'Sort', 'StringLibNameInvalidChars', 'SwapData', 'TransformGraphicShapesWithClearanceToPolygonSet', 'TransformPadsShapesWithClearanceToPolygon', 'Type', 'UnLink', 'Value', 'ViewBBox', 'ViewGetLOD', 'ViewGetLayers', 'ViewUpdate', 'Visit', '__class__', '__del__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattr__', '__getattribute__', '__hash__', '__init__', '__lt__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__swig_destroy__', '__swig_getmethods__', '__swig_setmethods__', '__weakref__', '_s', 'this']

If the module was writtnen in python, you can also do:

>>> help(m)
>>> help(m.some_method)

to get the documentation that’s in the code.

S

Thanks SBW, that’s really helpful.

The Kicad python documentation would appear to be available in the form of C++ header files in the Kicad pcbnew source directory. In particular there are almost 20 header files with names like class_board.h and class_footprint.h that include doygenated comments. There is also a Doxygen configuration file called Doxyfile_python in that directory. I haven’t attempted to build the Doxygen documentation (yet), since I’m on Windows and such things aren’t nearly as straight forward as they might be on Linux.