Strictly for the sake of this plugin I started Snaked’s development. It requires rope for it’s work.
Package modules are presented like package.module or package instead module.py or __init__.py. Very useful extension to distinguish similar file names. Must have for every Pytonier.
Default F3 shortcut navigates to symbol definition under cursor. It also handles symbols in quotes and comments. Like:
def func(param):
"""Some function
:param param: Some param of some_package.some_module.SomeClass
"""
pass
To see param class you need to place cursor on SomeClass and hit F3. Or if rope can infer param type you can place cursor there and hit F3.
Snaked uses gtksourceview2’s completion framework. I’ve only implement python provider. All UI work are done by gtksourceview.
<ctrl>space activates popup. Also there is support for showing pydocs in detailed proposal information.
This is the most exiting Snaked’s part. It allows to provide additional type information to rope for better type inferring and as consequence better completion and code navigation.
What hints can be provided:
There is special file to configure hints: .ropeproject/ropehints.py in your project root. It is ordinary python file which must define function init(provider), where provider is default project hint provider with build-in scope matcher and doc string hint support.
Take note, without configured hints you have doc string hint provider anyway.
Default snaked’s hint provider
It is created automatically for each rope project and passed to .ropeproject/ropehints.py init function as first parameter.
Contains build-in ScopeHintProvider with it’s scope matcher accessed via self.db and DocStringHintProvider.
Also provides hints for re module. Custom providers can be added via add_hint_provider():
def init(provider):
provider.db.add_class_attribute('django\.http\.HttpRequest$', 'render$', 'app.render')
from snaked.plugins.python.djangohints import DjangoHintProvider
provider.add_hint_provider(DjangoHintProvider(provider, 'settings'))
Inserts provider into collection.
Last added provider has max priority.
Working horse of type hinting
Common usage is in conjunction with ReScopeMatcher (also see examples there)
Parameters: |
|
---|
Allows to hint functions/method parameters and return values types through doc strings
This is build-in provider so you have no do any addition steps to enable it.
Hints can be provided using Sphinx syntax:
def func(text):
'''
:type text: str
:rtype: str
'''
# now in func body 'text' parameter's type is resolved into 'str'
# And all rest code now knows ``func`` return type is also 'str'
ScopeHintProvider matcher based on regular expressions
Matching is done with re.match function (pay attention to differences with re.search)
See add_attribute(), add_param_hint().
Add attribute type hint for module or class/object
Can be used to provide module attributes types, for example in case or complex module loading (werkzeug) or complex runtime behavior (flask). Here is example .ropeproject/ropehints.py:
def init(provider):
provider.db.add_attribute('flask$', 'request', 'flask.wrappers.Request()')
provider.db.add_attribute('werkzeug$', 'Request', 'werkzeug.wrappers.Request')
What happens here? flask.request is magic proxy to isolate thread contexts from each other. In runtime it is flask.wrappers.Request object (in default flask setup), so first line adds this missing information. But this is not enough. flask.wrappers.Request is a child of werkzeug.Request which can not be resolved because of werkzeug’s module loading system. So there is second line adding necessary mapping: module attribute werkzeug.Request should be werkzeug.wrappers.Request indeed. Take note about parentheses, flask.request is instance so type declared with them as opposite to werkzeug.Request which is class.
Also one can add class attributes hint. Here is example from my django project:
provider.db.add_attribute('django\.http\.HttpRequest$', 'cur', 'app.Cur')
provider.db.add_attribute('django\.http\.HttpRequest$', 'render', 'app.render')
Here are some explanations. Every request object has cur attribute (I know about contexts, don’t ask me why I need it) which is instance of app.Cur, so first line injects such info. Second line resolves render function also bounded to request.
Add function/method parameter or return value type hint.
Very useful in case of mass type hinting. For example part of snaked’s ropehints.py:
def init(provider):
provider.db.add_param_hint('.*', 'editor$', 'snaked.core.editor.Editor()')
provider.db.add_param_hint('snaked\.plugins\..*?\.init$', 'manager$',
'snaked.core.plugins.ShortcutsHolder()')
Snaked consist of many small functions passing around current text buffer (named editor) as parameter and first line allows to provide such type hint. Second line resolves all plugins init function’s manager parameter.
Or take look at Django’s view function’s request resolving:
provider.db.add_param_hint('.*\.views\..*', 'request$', 'django.http.HttpRequest()')
If name is return type hint is provided for function’s return value type. Following example shows it:
provider.db.add_param_hint('re\.compile$', 'return$', 're.RegexObject()')
provider.db.add_param_hint('re\.search$', 'return$', 're.MatchObject()')
provider.db.add_param_hint('re\.match$', 'return$', 're.MatchObject()')
provider.db.add_attribute('re$', 'RegexObject', 'snaked.plugins.python.stub.RegexObject')
provider.db.add_attribute('re$', 'MatchObject', 'snaked.plugins.python.stub.MatchObject')
Take notice, re.compile, re.search, re.match return absent classes which are mapped with add_attribute_hint() to existing stubs later.
Look at image:
Cool, isn’t it? Simply add django support into your .ropeproject/ropehints.py:
def init(provider):
from snaked.plugins.python.djangohints import add_django_support
add_django_support(provider)
Note
Django hints were developed against django 0.97 (yeah, I maintain such old project) codebase and not tested on current versions. Get me know if you will have any issues.
Image again:
Who is there? BuilderAware is a simple wrapper which delegates missing attributes to GtkBuilder. Window is BuilderAware class constructed from glade file. vbox1 is a GtkVBox defined in glade file and PyGtk hint provider resolves class attributes from it.
Besides that, goto definition (F3) opens glade file and place cursor at vbox1 declaration.
And more, if there are any signal handlers in glade file they parameters also will be resolved.
You only need to add pygtk support and assign glade file to class via ropehints.py:
def init(provider):
from snaked.plugins.python.pygtkhints import add_gtk_support
add_gtk_support(provider)
And define glade file in class pydoc:
class Window(BuilderAware):
"""glade-file: main.glade"""
...
This is a holy grail of modern development. Honestly, I didn’t plan to integrate unit testing support in snaked – running py.test from terminal completely satisfied my needs, but during heavy tests reorganization I realized too much time was spent for snaked/terminal switching and searching fail’s cause in py.test output.
Plugin completely based on py.test capabilities and you need latest (2.0) its version. Unit testing features:
At first you need to configure django environment. Create conftest.py module in the root of you project:
import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
Then you should tell py.test which modules are tests proper. Create pytest.ini file in project root:
[pytest]
python_files = tests.py
That’s all. Now you can run django tests with py.test and snaked.