On Tue, Jul 7, 2009 at 1:35 PM, Ingy dot Net<[email protected]> wrote:
> 0) Primarily want a code review of what I have on
> http://github.com/ingydotnet/testml-py/
I can look at that later.
> 1) Runtime introspection - there is all kinds of things I'm having trouble
> finding at runtime. Like maybe I want to know what the file path is of the
> current python module that py.test is executing for me.
If you mean, "What is the path of the currently-executing module?",
that's ``__file__``.
Also note ``__name__``, which is the module's fully-qualified name (or
"__main__" if the file was run directly as a script).
Imported modules are cached in sys.modules as {'name': <module>}. You
can inspect the module object for metainfo.
> 1b) Stack trace introspection.
That gets into a specialized area of Python which most people don't
do. You can raise a bogus exception and assign the traceback to a
variable. From there the ``inspect`` module might help, as well as
your old friends str(e), repr(e), dir(e), vars(e), and help(e).
http://docs.python.org/library/inspect.html
(See especially the "interpreter stack" section)
> 2) Popular idioms. I get the feeling that try/except usage is much more
> common than in Perl.
try/except provides a way to isolate error code, little-used code, or
out-of-band code. It can also be (ab)used to provide an alternate
return value if the function has special cases. There's no speed
penalty for try if no exception is raised.
The main difference between 'if' and 'try' is that 'if' suggests that
any branch might be taken in a normal case, while 'except' suggests
that this branch should rarely if ever happen. Of course, you can't
be 100% strict on this. It might be more elegent to have an
'if/elif/elif/else' where the 'else' should never happen. Conversely,
you may be forced to use 'try' because a third-party function raises
an exception.
Also note the differences between the exceptions.
'ValueError': a function argument has the right type but an illegal value.
'TypeError': a function argument contains a more serious error than
ValueError, or a
variable unexpectedly has the wrong type.
'AssertionError': two parts of your own code appear to be
inconsistent. E.g., if one
routine builds a data structure and another routine reads it, or
if a variable that
should have been set is unexpectedly None. Useful in an 'else'
that should never
happen. Not normally used for function argument errors, or for errors in
third-party code.
'NotImplementedError': a good stub for functions that haven't been written, for
superclass methods that must be overridden, or to ensure that
destructive code
doesn't get executed during development.
'EnvironmentError': something is wrong in the runtime environment.
This could be
used for missing files or environment variables, unavailable
servers, invalid
configuration, etc. However, there are no standard rules for
which exceptions to
raise in these cases. You could ignore the condition and let
third-party code raise
OSError, KeyError, etc. However, this may confuse the user,
especially if the
error message is imprecise.
'RuntimeError': specifically means miscellaneous error, something that
has no better
category. Useful for temporary situations during development, especially to
inspect live code under pdb. Some people also use it for
environment/configuration
errors in the top-level script.
There are other idioms but I can't think of them off the top of my
head. Remember that subscripting and ranges are open at the right
end, so that ``range(5)`` goes 0 to 4, and ``lis[1:3]`` gets elements
1 and 2.
When setting default values for functions, never use a mutable value:
def func(lis=[], dic={}) # Bad
The same list and dict will be shared across all calls to the
function, which will royally confuse users. Instead use a dummy
immutable value such as None:
def func(lis=None):
if lis is None:
lis = []
> 3) Accessing global variables across modules. I spent 2 hours yesterday on
> this, and still feel clueless.
They are attributes of the module object. So first get the module via
import or an argument or sys.modules. Then use dotted notation if you
know the name of the variable beforehand, or getattr() if you're not
sure it exists or have to calcuate the name. You can also call
vars(m) to get the globals in dict form, but this is read-only.
> 4) How the popular python test frameworks work.
I'm not an expert on this but we can discuss it in person. Nose is
the fastest-growing test framework. The others are unittest and
py.test. There are several other test frameworks specific to web
applications, but I don't know if you're interested in that.
> 5) Module installation and distribution best practices.
There are currently two overlapping conventions for this. "distutils"
is built into Python and revolves around a setup.py file in the
package. Users can download the package manually, unpack it, and run
"python setup.py install". The distutils conventions are sufficient
for small packages.
http://docs.python.org/library/distutils.html
http://docs.python.org/distutils/index.html#distutils-index
http://docs.python.org/install/index.html#install-index
"setuptools" is a superset of distutils with more features, both in
the setup.py and in the installation procedure. "easy_install" comes
with setuptools, which can download and install a package in one step.
Larger packages may need setuptools' features and convenience
functions. Setuptools is not built into Python so you have to install
it separately.
http://peak.telecommunity.com/
(See the links under the third entry dated Friday, September 16, 2005)
"pip" is an alternative to "easy_install". It's more modern but has
had less use and testing. Due to bootstrapping issues, you have to
manually install setuptools first, then "easy_install pip", and then
you can "pip install" everything else. Pip creates a slightly
different directory structure than easy_install, one that's preferred
by pip fans and is closer to the traditional Python structure.
http://pypi.python.org/pypi/pip
Any of these installation methods can be used on any package, no
matter which one it was built for. The only caveat is that if the
setup.py depends on setuptools features, the user will have had to
install setuptools beforehand, or else the application developer will
have had to put a trick into setup.py to auto-download setuptools.
"virtualenv" creates an isolated Python runtime environment, into
which you can install packages that won't be seen outside the
environment. This is useful if you have two programs that need
conflicting library versions, or to create a test environment that can
be blown away and recreated easily. Virtualenv installs Setuptools in
the environment automatically, so that's one less step to do.
http://pypi.python.org/pypi/virtualenv
"virtualenvwrapper" is a set of command-line utilities for virtualenv.
It depends on Bash, so it doesn't work on Windows.
http://pypi.python.org/pypi/virtualenvwrapper/1.18
"buildout" (officially "zc.buildout") is a way to make a repeatable
custom environment, such as for a server farm. Its functionality
overlaps with virtualenv, but it does it in a different way. Some
people find its configuration file harder to understand. Buildout is
more often used to create a deployment configuration for a finished
product, than for interactively trying out libraries during
development. Interestingly, buildout can be used within a virtualenv.
http://pypi.python.org/pypi/zc.buildout
--
Mike Orr <[email protected]>