PEP8 and nesting depth metric
Company code style is one of the most essential policies to follow for any programming-related IT-organization. It helps to organize interaction between developers, especially for Agile teams, makes code more readable and avoid conflicts like "where to put curly braces".
For the Python developers it is strongly recommended to follow PEP 8 - Style Guide for Python Code. This PEP declares:
- Code lay-out
- Imports
- Whitespace in Expressions and Statements
- Comments
- Documentation Strings
- Version Bookkeeping
- Naming Conventions
- Programming Recommendations
So it is very useful, and it is used as coding standard in many organizations. However, it doesn't declare some other useful code styles like Nesting Depth.
The Nesting Depth is a metric defined on methods that is relative to the maximum depth of the more nested scope in a method body. For example the following method has a Nesting Depth of 4:
def get_callable(lookup_view, can_fail=False):
if not callable(lookup_view):
try:
lookup_view = lookup_view.encode('ascii')
mod_name, func_name = get_mod_func(lookup_view)
if func_name != '':
lookup_view = getattr(import_module(mod_name), func_name)
if not callable(lookup_view):
raise AttributeError("'%s.%s' is not a callable." % (mod_name, func_name))
except (ImportError, AttributeError):
if not can_fail:
raise
except UnicodeEncodeError:
pass
return lookup_view
Sometimes it's confused with Cyclomatic Complexity or conditions count in if/while/etc expressions, but here we're talking only about scopes.
Why is it matter? High nesting depth decrease readability and unit-testing. For example, PEP8 declares that the code column width must be less than 80 symbols. With the high nesting depth inner scope code must be trancated to 10-20 symbols. In the most cases, this code became unreadable:
def _populate(self):
lookups = MultiValueDict()
namespaces = {}
apps = {}
for pattern in reversed(self.url_patterns):
p_pattern = pattern.regex.pattern
if p_pattern.startswith('^'):
p_pattern = p_pattern[1:]
if isinstance(pattern, RegexURLResolver):
if pattern.namespace:
namespaces[pattern.namespace] = (p_pattern, pattern)
if pattern.app_name:
apps.setdefault(pattern.app_name, []).append(
pattern.namespace)
else:
parent = normalize(pattern.regex.pattern)
for name in pattern.reverse_dict:
for matches, pat in pattern.reverse_dict.getlist(name):
new_matches = []
for piece, p_args in parent:
new_matches.extend(
[(piece + suffix, p_args + args) for (
suffix, args) in matches])
lookups.appendlist(name,
(new_matches, p_pattern + pat))
for namespace, (prefix,
sub_pattern) in pattern.namespace_dict.items():
namespaces[namespace] = (p_pattern + prefix,
sub_pattern)
for app_name, namespace_list in pattern.app_dict.items():
apps.setdefault(app_name, []).extend(namespace_list)
As you can see in the inner blocks we have to use carrying, and it's pretty difficult to read the code. Also it's difficult to remember all the conditions upon which the inner block will be executed.
As for unit-testing, each inner scope should be tested with the conditions handled (or mocked) in the way, that leads to executing this code block. For example, the inner-most block in the first example should be tested with:
- lookup_view is not callable;
- func_name is not empty (thus, lookup_view should be crafted/mocked in the way which leads to required func_name);
- inner lookup_view should not be callable too (so func_name should be crafted/mocked in the way which leads to required lookup_view).
As you can see it's a pretty complex logic, and mock classes/methods are not trivial for that case. And it's only nesting depth 4, just imagine unit-testing of the more complex functions.
So, in our company we try to stay with Nesting Depth 2 - it is optimal variant, the code is readable and can be tested without much efforts. It can looks strict, but this practice proved itself. And we modified original PEP8 validator with this check:
$ pep8.py urlresolvers.py
urlresolvers.py:74:1: W801 Function 'get_callable' - Too much nested loops or conditions: 4. Max allowed is: 2.
Of course, your policy can be different, but you can easily modify it (see class NestingCheck) - the code is distributed without any limitations. You can get it from the Free Software section.
- Posted by: nuald 21.10.2010 at 18:37 0 comments
You're currently an anonymous user. Just browsing around? That's totally cool with us. We won't bug you until you're ready to write a comment. Otherwize you have to enter your OpenID credentials to log in. If you have not one, you can easily create it!
Example OpenIDs:
- http://openid.aol.com/yourname
- http://yourname.myopenid.com/
- https://me.yahoo.com/yourname (alternately, http://yahoo.com/ works too)
- http://claimid.com/yourname
- http://yourname.wordpress.com/
- http://yourname.blogspot.com/
- http://technorati.com/people/technorati/yourname
- http://yourname.pip.verisignlabs.com/
- http://yourname.livejournal.com/
- http://www.flickr.com/photos/yourname
WHAT'S NEW
- PEP8 validation script
- Modified PEP8 validation script with Nesting Depth additional validation.
- October 21, 2010
- PEP8 and nesting depth metric
- Company code style is one of the most essential policies to follow for any programming-related IT-organization. It helps to organize interaction between developers, especially for Agile teams, makes code more ...
- October 21, 2010
- CodeExample plugin for Trac
- The Trac plugin for code examples colouring. It supports three types of examples - a simple, a correct one and an incorrect. Further details see at
- September 29, 2010
- A couple of words about TDD
- Unit-test coding supposes to be one of the most significant methodological achievements of the industry, let’s say, for about last 15 years. The Internet is full of enthusiastic exclamations [1, ...
- February 21, 2010
- Metrics - LoC
- This is going to be a small set of articles devoted to metrics. The first one is about LoC - Line of Code. I think that the first reaction on ...
- May 11, 2009