Following up from my previous post on Python and static typing, I'm proud to announce PySmell v0.1!
UPDATE2: PySmell v0.5 released
UPDATE: PySmell v0.2 released, get it here
What is PySmell?
PySmell is a python IDE completion helper, that covers 80% of the cases, leaving the rest to superior human brains.
It tries to statically analyze Python source code, without executing it, and generates information about a project's structure that IDE tools can use. There is currently support for Vim's omnicompletion, but porting to other editors which provide similar mechanisms should be straightforward.
In its current state it doesn't do any type inferencing, but it's surprisingly useful even without that. I plan to add simple type inferencing in the coming versions.
Having nothing more to add, I'm copying from the README:
Download and Installation
PySmell's code is available at GitHub. You can click 'Download' to get it as a zip/tar if you don't have git installed.
Extract and drop the pysmell package somewhere in your PYTHONPATH. Distutils coming soon!
Usage
To generate a PYSMELLTAGS file, use:
cd /root/of/project
python /dir/of/pysmelltags.py
If you want to specifically include or exclude some files or directories (eg. tests), you can use:
python /dir/of/pysmelltags.py [Package Package File File ...] [-x Excluded Excluded ...]
Partial tags
If there are files that you want to analyze, but have them only be accessible from a specific package, you can create the PYSMELLTAGS file as above, rename it to PYSMELLTAGS.partial, and put it in the directory you want it to be accessible from.
For example, if you have a bunch of custom test case files in your FunctionalTests package, along with a thousand actual functional tests, you can use:
cd FunctionalTests
python /dir/of/pysmelltags.py FunctionalTest.py UndoTestCase.py
mv PYSMELLTAGS PYSMELLTAGS.partial
The information in FunctionalTest and UndoTestCase will only be accessible when editing a file inside the FunctionalTests package.
Vim
To use PySmell omnicompletion from inside Vim, you have to have:
- Python support
- The pysmell package in your PYTHONPATH (sometimes Vim is silly about this)
- Source pysmell/pysmell.vim
You can then use ^X^O to invoke Vim's omnicompletion.
Reporting issues
Send me an email at orestis@orestis.gr. If you can create a unit test that exposes that behaviour, it'd be great!
Comments
Comment by Orestis Markou , 2 years ago :
Hey, thanks for the comment :)
Indeed, that part of the code is a bit hairy - I need to find an easier way to generate source code from AST. I'll cleanup that part though!
BTW, PySmell doesn't handle packages well enough. Caught the case with Django, which does a lot of monkeying with __init__.py files. I will issue an update in the coming days.
Comment by Carlos Perilla , 2 years ago :
Well looks like the files are dos-newline encoded, a bit annoying for us who use *nix.
Still looking into it.
Comment by Orestis Markou , 2 years ago :
Gah. I've been developing on a Windows machine, but I spent much time on my Mac as well. I'll convert everything to unix...
Comment by Ronny Pfannschmidt , 2 years ago :
You guys ever noticed rope
its not only doing source analysis, but also refactoring
take a look at http://rope.sourceforge.net/
Comment by Orestis Markou , 2 years ago :
PySmell v0.2 released, with proper support for packages.
Get it here: http://tinyurl.com/56y5tg
This post is older than 30 days and comments have been turned off.

Comment by Paulo Köch , 2 years ago :
Hi!
There's something odd in your code.
commit dcc52d386f2ab0d3d4d3ad278f45451e77555a7a, codefinder.py, line 210:
≥if isinstance(node, (ast.Getattr,), ):≤
The last comma caught me a bit offhanded. So I tried asking the compiler what that expression meant.
Python 2.5.1 (r251:54863, Apr 15 2008, 22:57:26)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import compiler
>>> tree = compiler.parse('isinstance(node, (ast.Getattr,), )')
>>> tree
Module(None, Stmt([Discard(CallFunc(Name('isinstance'), [Name('node'), Tuple([Getattr(Name('ast'), 'Getattr')])], None, None))]))
Ok... The comma seems to have no effect. So I asked for the coma-less version:
>>> tree = compiler.parse('isinstance(node, (ast.Getattr,))')
>>> tree
Module(None, Stmt([Discard(CallFunc(Name('isinstance'), [Name('node'), Tuple([Getattr(Name('ast'), 'Getattr')])], None, None))]))
Exactly the same thing. So, why the coma? It's the same AST, as more confusion for the reader!