|
Blog entries by Sylvain Thenault [15]
After a quick survey, we're officially scheduling Pylint 10th years anniversary sprint from monday, June 17 to wednesday, June 19 in Logilab's Toulouse office.
There is still some room available if more people want to come, drop me a note (sylvain dot thenault at logilab dot fr).
Hi everyone,
After 10 years of hosting Pylint on our own forge at logilab.org, we've decided to publish version 1.0 and move Pylint and astng development to BitBucket. There has been repository mirrors there for some time, but we intend now to use all BitBucket features, notably Pull Request, to handle various development tasks.
There are several reasons behind this. First, using both BitBucket and our own forge is rather cumbersome, for integrators at least. This is mainly because BitBucket doesn't provide support for Mercurial's changeset evolution feature while our forge relies on it. Second, our forge has several usability drawbacks that make it hard to use for newcomers, and we lack the time to be responsive on this. Finally, we think that our quality-control process, as exposed by our forge, is a bit heavy for such community projects and may keep potential contributors away.
All in all, we hope this will help to have a wider contributor audience as well as more regular maintainers / integrators which are not Logilab employees. And so, bring the best Pylint possible to the Python community!
Logilab.org web pages will be updated to mention this, but kept as there is still valuable information there (eg tickets). We may also keep automatic tests and package building services there.
So, please use https://bitbucket.org/logilab/pylint as main web site regarding pylint development. Bug reports, feature requests as well as contributions should be done there. The same move will be done for Pylint's underlying library, logilab-astng (https://bitbucket.org/logilab/astng). We also wish in this process to move it out of the 'logilab' python package. It may be a good time to give it another name, if you have any idea don't hesitate to express yourself.
Last but not least, remember that Pylint home page may be edited using Mercurial, and that the new http://docs.pylint.org is generated using the content found in Pylint source doc subdirectory.
Pylint turning 10 and moving out of its parents is probably a good time to thank Logilab for paying me and some colleagues to create and maintain this project!
In a few week, pylint will be 10 years old (0.1 released on may 19 2003!).
At this occasion, I would like to release a 1.0. Well, not exactly at that date,
but not too long after would be great. Also, I think it would be a good time
to have a few days sprint to work a bit on this 1.0 but also to meet all together
and talk about pylint status and future, as more and more contributions come from
outside Logilab (actually mostly Google, which employs Torsten and Martin, the most
active contributors recently).
The first thing to do is to decide a date and place. Having discussed a bit with
Torsten about that, it seems reasonable to target a sprint during june or july.
Due to personal constraints, I would like to host this sprint in Logilab's
Toulouse office.
So, who would like to jump in and sprint to make pylint even better? I've created
a doodle so every one interested may tell his preferences:
http://doodle.com/4uhk26zryis5x7as
Regarding the location, is everybody ok with Toulouse? Other ideas are Paris, or
Florence around EuroPython, or... <add your proposition here>.
We'll talk about the sprint topics later, but there are plenty of exciting ideas
around there.
Please, answer quickly so we can move on. And I hope to see you all there!
Hi there,
I'm very pleased to announce the release of pylint 0.27 and
logilab-astng 0.24.2. There has been a lot of enhancements and
bug fixes since the latest release, so you're strongly encouraged
to upgrade. Here is a detailed list of changes:
- #20693: replace pylint.el by Ian Eure version (patch by J.Kotta)
- #105327: add support for --disable=all option and deprecate the
'disable-all' inline directive in favour of 'skip-file' (patch by
A.Fayolle)
- #110840: add messages I0020 and I0021 for reporting of suppressed
messages and useless suppression pragmas. (patch by Torsten Marek)
- #112728: add warning E0604 for non-string objects in __all__
(patch by Torsten Marek)
- #120657: add warning W0110/deprecated-lambda when a map/filter
of a lambda could be a comprehension (patch by Martin Pool)
- #113231: logging checker now looks at instances of Logger classes
in addition to the base logging module. (patch by Mike Bryant)
- #111799: don't warn about octal escape sequence, but warn about o
which is not octal in Python (patch by Martin Pool)
- #110839: bind <F5> to Run button in pylint-gui
- #115580: fix erroneous W0212 (access to protected member) on super call
(patch by Martin Pool)
- #110853: fix a crash when an __init__ method in a base class has been
created by assignment rather than direct function definition (patch by
Torsten Marek)
- #110838: fix pylint-gui crash when include-ids is activated (patch by
Omega Weapon)
- #112667: fix emission of reimport warnings for mixed imports and extend
the testcase (patch by Torsten Marek)
- #112698: fix crash related to non-inferable __all__ attributes and
invalid __all__ contents (patch by Torsten Marek)
- Python 3 related fixes:
- #110213: fix import of checkers broken with python 3.3, causing
"No such message id W0704" breakage
- #120635: redefine cmp function used in pylint.reporters
- Include full warning id for I0020 and I0021 and make sure to flush
warnings after each module, not at the end of the pylint run.
(patch by Torsten Marek)
- Changed the regular expression for inline options so that it must be
preceeded by a # (patch by Torsten Marek)
- Make dot output for import graph predictable and not depend
on ordering of strings in hashes. (patch by Torsten Marek)
- Add hooks for import path setup and move pylint's sys.path
modifications into them. (patch by Torsten Marek)
- pylint-brain: more subprocess.Popen faking (see #46273)
- #109562 [jython]: java modules have no __doc__, causing crash
- #120646 [py3]: fix for python3.3 _ast changes which may cause crash
- #109988 [py3]: test fixes
Many thanks to all the people who contributed to this release!
Enjoy!
I'm very pleased to announce new releases of Pylint and
underlying ASTNG library, respectivly 0.26 and 0.24.1. The great
news is that both bring a lot of new features and some bug fixes,
mostly provided by the community effort.
We're still trying to make it easier to contribute on our free
software project at Logilab, so I hope this will continue and
we'll get even more contritions in a near future, and an even
smarter/faster/whatever pylint!
For more details, see ChangeLog files or http://www.logilab.org/project/pylint/0.26.0 and http://www.logilab.org/project/logilab-astng/0.24.1
So many thanks to all those who made that release, and enjoy!
I'm pleased to announce the new release of Pylint and related projects (i.e. logilab-astng and logilab-common)!
By installing PyLint 0.25.2, ASTNG 0.24 and logilab-common 0.58.1, you'll get a bunch of bug fixes and a few new features. Among the hot stuff:
- PyLint should now work with alternative python implementations such as Jython, and at least go further with PyPy and IronPython (but those have not really been tested, please try it and provide feedback so we can improve their support)
- the new ASTNG includes a description of dynamic code it is not able to understand. This is handled by a bitbucket hosted project described in another post.
Many thanks to everyone who contributed to these releases, Torsten Marek / Boris Feld in particular (both sponsored by Google by the way, Torsten as an employee and Boris as a GSoC student).
Enjoy!
Huum, along with the new PyLint release, it's time to introduce the PyLint-Brain project I've recently started.
Despite its name, PyLint-Brain is actually a collection of extensions for ASTNG, with the goal of making ASTNG smarter (and this directly benefits PyLint) by describing stuff that is too dynamic to be understood automatically (such as functions in the hashlib module, defaultdict, etc.).
The PyLint-Brain collection of extensions is developped outside of ASTNG itself and hosted on a bitbucket project to ease community involvement and to allow distinct development cycles. Basically, ASTNG will include the PyLint-Brain extensions, but you may use earlier/custom versions by tweaking your PYTHONPATH.
Take a look at the code, it's fairly easy to contribute new descriptions, and help us make pylint smarter!
The latest release of logilab-astng (0.23), the underlying source code
representation library used by PyLint, provides a new API that may change pylint users' life in the near future...
It aims to allow registration of functions that will be called after a module has
been parsed. While this sounds dumb, it gives a chance to fix/enhance the
understanding PyLint has about your code.
I see this as a major step towards greatly enhanced code analysis, improving the
situation where PyLint users know that when running it against code using their
favorite framework (who said CubicWeb? :p ), they should expect a bunch of false
positives because of black magic in the ORM or in decorators or whatever else. There are also places in the Python standard library where dynamic code can cause false positives in PyLint.
Let's take a simple example, and see how we can improve things using the new
API. The following code:
import hashlib
def hexmd5(value):
""""return md5 checksum hexadecimal digest of the given value"""
return hashlib.md5(value).hexdigest()
def hexsha1(value):
""""return sha1 checksum hexadecimal digest of the given value"""
return hashlib.sha1(value).hexdigest()
gives the following output when analyzed through pylint:
[syt@somewhere ~]$ pylint -E example.py
No config file found, using default configuration
************* Module smarter_astng
E: 5,11:hexmd5: Module 'hashlib' has no 'md5' member
E: 9,11:hexsha1: Module 'hashlib' has no 'sha1' member
However:
[syt@somewhere ~]$ python
Python 2.7.1+ (r271:86832, Apr 11 2011, 18:13:53)
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import smarter_astng
>>> smarter_astng.hexmd5('hop')
'5f67b2845b51a17a7751f0d7fd460e70'
>>> smarter_astng.hexsha1('hop')
'cffb6b20e0eef296772f6c1457cdde0049bdfb56'
The code runs fine... Why does pylint bother me then? If we take a look at the
hashlib module, we see that there are no sha1 or md5 defined in
there. They are defined dynamically according to Openssl library availability in order to use the fastest available implementation, using code like:
for __func_name in __always_supported:
# try them all, some may not work due to the OpenSSL
# version not supporting that algorithm.
try:
globals()[__func_name] = __get_hash(__func_name)
except ValueError:
import logging
logging.exception('code for hash %s was not found.', __func_name)
Honestly I don't blame PyLint for not understanding this kind of magic. The
situation on this particular case could be improved, but that's some tedious
work, and there will always be "similar but different" case that won't be
understood.
The good news is that thanks to the new astng callback, I can help it be
smarter! See the code below:
from logilab.astng import MANAGER, scoped_nodes
def hashlib_transform(module):
if module.name == 'hashlib':
for hashfunc in ('sha1', 'md5'):
module.locals[hashfunc] = [scoped_nodes.Class(hashfunc, None)]
def register(linter):
"""called when loaded by pylint --load-plugins, register our tranformation
function here
"""
MANAGER.register_transformer(hashlib_transform)
What's in there?
- A function that will be called with each astng module built during a pylint
execution, i.e. not only the one that you analyses, but also those accessed for
type inference.
- This transformation function is fairly simple: if the module is the 'hashlib'
module, it will insert into its locals dictionary a fake class node for each
desired name.
- It is registered using the register_transformer method of astng's MANAGER
(the central access point to built syntax tree). This is done in the pylint
plugin API register callback function (called when module is imported using
'pylint --load-plugins'.
Now let's try it! Suppose I stored the above code in a 'astng_hashlib.py' module in my
PYTHONPATH, I can now run pylint with the plugin activated:
[syt@somewhere ~]$ pylint -E --load-plugins astng_hashlib example.py
No config file found, using default configuration
************* Module smarter_astng
E: 5,11:hexmd5: Instance of 'md5' has no 'hexdigest' member
E: 9,11:hexsha1: Instance of 'sha1' has no 'hexdigest' member
Huum. We have now a different error :( Pylint grasp there are some md5 and
sha1 classes but it complains they don't have a hexdigest method. Indeed,
we didn't give a clue about that.
We could continue on and on to give it a full representation of hashlib public
API using the astng nodes API. But that would be painful, trust me. Or we could
do something clever using some higher level astng API:
from logilab.astng import MANAGER
from logilab.astng.builder import ASTNGBuilder
def hashlib_transform(module):
if module.name == 'hashlib':
fake = ASTNGBuilder(MANAGER).string_build('''
class md5(object):
def __init__(self, value): pass
def hexdigest(self):
return u''
class sha1(object):
def __init__(self, value): pass
def hexdigest(self):
return u''
''')
for hashfunc in ('sha1', 'md5'):
module.locals[hashfunc] = fake.locals[hashfunc]
def register(linter):
"""called when loaded by pylint --load-plugins, register our tranformation
function here
"""
MANAGER.register_transformer(hashlib_transform)
The idea is to write a fake python implementation only documenting the prototype
of the desired class, and to get an astng from it, using the string_build method of
the astng builder. This method will return a Module node containing the astng
for the given string. It's then easy to replace or insert additional information
into the original module, as you can see in the above example.
Now if I run pylint using the updated plugin:
[syt@somewhere ~]$ pylint -E --load-plugins astng_hashlib example.py
No config file found, using default configuration
No error anymore, great!
This fairly simple change could quickly provide great enhancements. We should
probably improve the astng manipulation API now that it's exposed like
that. But we can also easily imagine a code base of such pylint plugins
maintained by each community around a python library or framework. One could
then use a plugins stack matching stuff used by its software, and have a greatly
enhanced experience of using pylint.
For a start, it would be great if pylint could be shipped with a plugin that
explains all the magic found in the standard library, wouldn't it? Left as an exercice to
the reader!
Hi there!
I'm pleased to announce new releases of pylint and its underlying
library logilab-astng. See
http://www.logilab.org/project/pylint/0.24.0 and
http://www.logilab.org/project/logilab-astng/0.22.0 for more info.
Those releases include mostly fixes and a few enhancements. Python 2.6
relative / absolute imports should now work fine and Python 3 support
has been enhanced. There are still two remaining failures in astng
test suite when using python 3, but we're unfortunatly missing
resources to fix them yet.
Many thanks to everyone who contributed to this release by submitting
patches or by participating to the latest bugs day.
Hey guys,
we'll hold the next pylint bug day on july 8th 2011 (friday). If some of you want to come and work with us in our Paris office, you'll be welcome.
You can also join us on jabber / irc:
I know the announce is a bit late, but I hope some of you will be able to come or be online anyway!
Regarding the program, the goal is to decrease the number of tickets in the tracker. I'll try to do some triage earlier this week so you'll get a chance to talk about your super-important ticket that has not been selected. Of course, if you intend to work on it, there is a bigger chance of it being fixed next week-end ;)
First of all, I've to say that pylint bugs day wasn't that successful in term of 'community event': I've been sprinting almost alone. My Logilab's felows were tied to customer projects, and no outside people shown up on jabber. Fortunatly Tarek Ziade came to visit us, and that was a nice opportunity to talk about pylint, distribute, etc ... Thank you Tarek, you saved my day ;)
As I felt a bit alone, I decided to work on somethings funnier than bug fixing: refactoring!
First, I've greatly simplified the command line: enable-msg/enable-msg-cat/enable-checker/enable-report and their disable-* counterparts were all merged into single --enable/--disable options.
I've also simplified "pylint --help" output, providing a --long-help option to get what we had before. Generic support in `logilab.common.configuration of course.
And last but not least, I refactored pylint so we can have multiple checkers with the same name. The idea behind this is that we can split checker into smaller chunks, basically
only responsible for one or a few related messages. When pylint runs, it only uses necessary checkers according to activated messages and reports. When all checkers will be splitted, it should improve performance of "pylint --error-only".
So, I can say I'm finally happy with the results of that pylint bugs day! And hopefuly we will be more people for the next edition...
Hey guys,
we'll hold the next pylint bugs day on april 16th 2010 (friday). If some of you want to come and work with us in our Paris office, you'll be much welcome.
Else you can still join us on jabber / irc:
See you then!
Remember that the first pylint bug day will be held on wednesday, november 25, from around 8am to 8pm in the Paris (France) time zone.
We'll be a few people at Logilab and hopefuly a lot of other guys all around the world, trying to make pylint better.
Join us on the #public conference room of conference.jabber.logilab.org, or if you prefer using an IRC client, join #public on irc.logilab.org which is a gateway to the jabber forum. And if you're in Paris, come to work with us in our office.
People willing to help but without knowledge of pylint internals are welcome, it's the perfect occasion to learn a lot about it, and to be able to hack on pylint in the future!
Since we don't stop being overloaded here at Logilab, and we've got some encouraging feedback after the "Pylint needs you" post, we decided to take some time to introduce more "community" in pylint.
And the easiest thing to do, rather sooner than later, is a irc/jabber synchronized bug day, which will be held on Wednesday november 25. We're based in France, so main developpers will be there between around 8am and 19pm UTC+1.
If a few of you guys are around Paris at this time and wish to come at Logilab to sprint with us, contact us and we'll try to make this possible.
The focus for this bug killing day could be:
- using logilab.org tracker : getting an account, submitting tickets, triaging existing tickets...
- using mercurial to develop pylint / astng
- guide people in the code so they're able to fix simple bugs
We will of course also try to kill a hella-lotta bugs, but the main idea is to help whoever wants to contribute to pylint... and plan for the next bug-killing day !
As we are in the process of moving to another place, we can't organize a sprint yet, but we should have some room available for the next time, so stay tuned :)
After several months with no time to fix/enhance pylint beside answering email and filing tickets, I've finally tackled some tasks yesterday night to publish bug fixes releases ([1] and [2]).
The problem is that we don't have enough free time at Logilab to lower the number of tickets in pylint tracker page .
If you take a look at the ticket tab, you'll see a lot of pendings bug and must-have features (well, and some other less necessary...).
You can already easily contribute thanks to the great mercurial dvcs, and some of you do, either by providing patches or by reporting bugs (more tickets, iiirk ! ;) Thank you all btw !!
Now I was wondering what could be done to make pylint going further, and the first ideas which came to my mind was :
- do ~3 days sprint
- do some 'tickets killing' days, as done in some popular oss projects
But for this to be useful, we need your support, so here are some questions for you:
- would you come to a sprint at Logilab (in Paris, France), so you can meet us, learn a lot about pylint, and work on tickets you wish to have in pylint?
- if France is too far away for most people, would you have another location to propose?
- would you be on jabber for a tickets killing day, providing it's ok with your agenda? if so, what's your knowledge of pylint/astng internals?
you may answer by adding a comment to this blog (please register first by using the link at the top right of this page) or by mail to sylvain.thenault@logilab.fr. If we've enough positive answers, we'll take the time to organize such a thing.
You'll find in the logilab.common.decorators module the iclassmethod decorator which may be pretty handy in some cases as it allows methods to be both called as class methods or as instance methods. In the first case the first argument will be the class and the second case it will be the instance.
Example extracted (and adapted for simplicity) from CubicWeb:
from logilab.common.decorators import iclassmethod
class Form(object):
_fields_ = []
def __init__(self):
self.fields = list(self._fields_)
@iclassmethod
def field_by_name(cls_or_self, name):
"""return field with the given name and role"""
if isinstance(cls_or_self, type):
fields = cls_or_self._fields_
else:
fields = cls_or_self.fields
for field in fields:
if field.name == name:
return field
raise Exception('FieldNotFound: %s' % name)
Example session:
>>> from logilab.common import attrdict
>>> f = Form()
>>> f.fields.append(attrdict({'name': 'something', 'value': 1})
>>> f.field_by_name('something')
{'name': 'something', 'value': 1}
>>> Form.field_by_name('something')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 15, in field_by_name
Exception: FieldNotFound: something
So we get a field_by_name method which will act differently (actually use different input data) when called as instance method or as class method.
Also notice the attrdict trick that can also be achieved with the Python 2.6 named tuple.
I recently understood why easy_install wasn't able to find so many of our packages anymore.
The problem was due to a recent change on our website. The project page was ajaxified, and since easy_install uses some screenscrapping techniques to get distribution archives, it can not find the files it is looking for.
To fix this, we should make our tarballs downloadable from PyPI, by using
python setup.py register sdist upload
instead of the current:
python setup.py register
Uploading our public python software packages to PyPI will make them easy_installable in a breeze !
Here is a piece of code I've written which I thought may be useful to some other people...
You'll find here a simple python module to use with the Google AppEngine SDK to monkey patch the datastore API in order to get an idea of the calls performed by your application.
To instrument of the datastore, put at the top level of your handler file
import instrdatastore
Note that it's important to have this before any other import in your application or in the google package to avoid that some modules will use the unpatched version of datastore functions (and hence calls to those functions wouldn't be considered).
Then add at the end of your handler function
instrdatastore.print_info()
The handler file should look like this:
"""my handler file with datastore instrumenting activated"""
import instrdatastore
# ... other initialization code
# main function so this handler module is cached
def main():
from wsgiref.handlers import CGIHandler
from ginco.wsgi.handler import ErudiWSGIApplication
application = ErudiWSGIApplication(config, vreg=vreg)
CGIHandler().run(application)
instrdatastore.print_info()
if __name__ == "__main__":
main()
Now you should see in your logs the number of Get/Put/Delete/Query which has been done during request processing
2008-06-23 06:59:12 - (root) WARNING: datastore access information
2008-06-23 06:59:12 - (root) WARNING: nb Get: 2
2008-06-23 06:59:12 - (root) WARNING: arguments (args, kwargs):
((datastore_types.Key.from_path('EGroup', u'key_users', _app=u'winecellar'),), {})
((datastore_types.Key.from_path('EUser', u'key_test@example.com', _app=u'winecellar'),), {})
2008-06-23 06:59:12 - (root) WARNING: nb Query: 1
2008-06-23 06:59:12 - (root) WARNING: arguments (args, kwargs):
(({'for_user =': None}, 'EProperty'), {})
2008-06-23 06:59:58 - (root) WARNING: nb Put: 1
2008-06-23 06:59:58 - (root) WARNING: arguments (args, kwargs):
(({u'login': None, u'last_usage_time': 1214204398.2022741, u'data': ""},), {})
I'll probably extend this as the time goes. Also notice you may encounter some problems with the automatic reloading feature of the dev app server when instrumentation is activated, in which case you should simply restart the web server.
|