Skip to content.

plope

Personal tools
You are here: Home » Members » chrism's Home » What to Do When Writing Python Software
 
 

What to Do When Writing Python Software

bogeymill complained that I didn't offer alternatives to each point in my "What Not To Do When Writing Python Software".

I've been accused of purporting . Now, I think purporting is legal in Tenessee, but I live in Virginia, so here's my list of don'ts translated into "do's".

  • Instead of using from foo.bar import *, use from foo.bar import thething. Optimally, you won't use from foo.bar import onething, anotherthing you'll do from foo.bar import onething, then on the next line from foo.bar import anotherthing so that when I cut and paste your code into something else that doesn't require every name, I can copy and paste only the imports I need without editing the content of lines. This will help you too, because you won't need to edit lines either to get rid of unused imports, you'll just need to delete lines.
  • If you need to perform monkey-patches or register objects into a foreign global registry, try to push this step out to the very tippy-top of your application, preferably in the driver script that you use to run the app or as close to it as possible. If instead you write and distribute libraries that do this at import time, they may not work in other people's environments. Libraries should be general and either assume no dependencies or explicitly list them and provide instructions to resolve them. Applications are more personal. If you absolutely must supply a monkeypatch along with a library, instead of monkeypatching the code at import time in your library, write an importable module that monkeypatches what you want to monkeypatch (perhaps wrapped inside a function), but don't import it from anywhere; then instruct the library user that he needs to import it and possibly invoke something to get fixes for specific versions of dependent software.
  • When you emit deprecation warnings, have the warning point to a URL that explains the rationale for the deprecation and provides a reasonable example of changing a piece of code from the old methodology to the new methodology. For example, instead of the message saying "the frobnozz method is deprecated, use frobnozz2 instead", emit a message that says, "the frobnozz method is deprecated, use frobnozz2 instead. See http://www.example.com/deprecations/frobnozz_to_frobnozz2.". And write a rationale for the deprecation and an example of changing code that uses the frobnozz method to code that uses the frobnozz2 method withinin the document referenced by the URL.
  • Don't deprecate APIs in a library that code in the library itself still uses. I'm not sure how to state this without a "don't" somewhere in there.
  • Don't use decorators to do things you could do in a simpler way. See Gary's blog entry
  • Allow the original exception to bubble up in library code, or at least if you catch it, reraise it with all the information the original traceback contained intact. See Ian's post about this.
  • When you write tests, be careful to look at each method and function you implement and test all of the edge cases. Every method or function you write needs to be tested, Every conditional in a method or function is a new test case. Often people don't see the point of testing every method and every condition because the code "obviously works". But the larger value in tests isn't to prove that it works right now (although that's obviously a useful benefit), it's to be able to know that you haven't broken anything when you or someone else changes it later.
  • Use a real version control system. Leaving large chunks of commented-out logic in your code is a crude form of version control ("I might need this later so I can't delete it"), but invariably if the code is useful, someone else will need to read it, and making them mentally elide the commented-out code is a bit rude. Instead, take a few hours to learn how to set up and use Subversion/CVS/git or mercurial, or whatever, and use it. Then you can delete code but have a history around of older code revisions in case you need them later.
  • If you absoutely need to emit a warning in your library, emit a warning using the Python warnings module. It provides facilities that people can use to filter them. But don't just print something to my stdout or stderr, because I can't filter that, and as useful as the information might be to you, it's often useless to me as a consumer, especially if it's just "debugging" output. Don't emit a warning or print anything unless you absolutely must.
  • If you have a set of utility functions or classes within a library "foo" that you use a lot, but which has a purpose orthogonal to the functionality of the library itself (it's more general than the library), make it a separate library "bar". Then when you write a new library "baz", you can make it depend on the "bar" utility library instead of the "foo" library, which might be completely unrelated to "baz".
  • When you cut and paste someone else's code to use it as a starting point (or inherit from it), if it does things that you don't understand, try to understand them, and if what it's doing seems wasteful or there's a better way to do things now, change the code to do it a better way. Make sure you understand the intent of the code before reusing it or changing it.
Created by chrism
Last modified 2007-09-13 10:39 AM

and use pyflakes!

can't say it enough. before each commit run pyflakes and make sure there are NO warnings or errors (if necessary insert some code to make the warnings go away, i.e. if you import foo and it complains that you're not using it add a single line `foo # PYFLAKES` just to make it happy.)

now the rule of importing only one item per line comes in handy, too, because in order to delete unused imports (pyflakes rightly complains about those!) you just need to delete a line for each.