LECTURE 10 Development Tools

advertisement
LECTURE 10
Development Tools
DEVELOPMENT TOOLS
Since you are all in the early stages of your semester-long projects, today we will be
covering some useful modules and tools for developing larger python projects
(especially ones that involve multiple people).
• virtualenv
• logging
• testing
VIRTUALENV
virtualenv is a tool for creating isolated Python environments.
Let’s say you are working on two projects which require Twisted, a python-based
networking package. One of the projects requires Twisted 14.x, but the other
requires Twisted 13.x. Solve the problem by creating a custom Python environment for
each project.
To install virtualenv via pip:
$ pip install virtualenv
All of these examples are for a debian-based Linux distros.
VIRTUALENV
To create a virtual environment for a project:
$ cd my_project_folder
$ virtualenv venv
Essentially, we’re creating a copy of the Python interpreter (as well as a copy of the
pip and setuptools libraries) inside of a folder called venv. We can specify a
different Python version in the following way:
$ virtualenv -p /usr/bin/python2.7 venv
Use the –no-site-packages option to not include globally installed packages.
VIRTUALENV
After creating a new virtual environment, the next step is to activate it.
$ source venv/bin/activate
(venv) $
The name of the virtual environment will appear before your prompt after activation.
Anything you install at this point will install to your isolated environment, not to the
global site packages.
(venv) $ pip install twisted
VIRTUALENV
To deactivate a virtual environment:
(venv) $ deactivate
$
Now, we’re back to using the default Python interpreter and globally installed
packages. You can delete a virtual environment by simply deleting the folder
created, in this case called “venv”.
VIRTUALENV
For distributing your project and/or for easy set-up, freeze the current virtual
environment.
(venv) $ pip freeze > requirements.txt
This creates a list of the installed packages and versions inside of requirements.txt.
This can be used to rebuild the environment later. This is useful for allowing another
developer to run your project without having to figure out what packages and which
versions were used.
VIRTUALENV
Putting all of this together, the typical use of a virtual environment is as
follows:
$ virtualenv venv –no-site-packages
$ source venv/bin/activate
(venv) $ pip install -r requirements.txt
…
(venv) $ deactivate
$
LOGGING
The logging facility for Python is defined in the Standard Library as the module
logging.
You are encouraged to use the logging facility while developing your projects – it
aids with testing and debugging as well as allows your teammates to work with your
code without having to understand every line.
Logging can be used to do the following:
• Report events that occur during normal operation (e.g. for status monitoring, fault
investigation, or development).
• Issue a warning regarding a particular runtime event.
• Report suppression of an error without raising an exception (e.g. error handler in a
long-running server process).
LOGGING
There are a number of logging levels. Default level is WARNING, meaning no level
below WARNING will be logged.
Level
Use
DEBUG
Detailed info for development.
INFO
Logging of expected events.
WARNING
Application still behaving normally, but unexpected event happened or problem
is anticipated.
ERROR
Application is not behaving as it should due to some error.
CRITICAL
Critical error encountered that may have taken application down.
LOGGING
Most commonly, logging is performed by recording events to a log file.
import logging
logging.basicConfig(filename='example.log', level=logging.DEBUG)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')
The contents of example.log:
DEBUG:root:This message should go to the log file
INFO:root:So should this
WARNING:root:And this, too
LOGGING
You can log multiple modules in one central location. Simply initialize the logging in
your main module, and log as necessary from the others.
# myapp.py
import logging
import mylib
def main():
logging.basicConfig(filename='myapp.log', level=logging.INFO)
logging.info('Started')
mylib.do_something()
logging.info('Finished')
if __name__ == '__main__':
main()
# mylib.py
import logging
def do_something():
logging.info('Doing something')
LOGGING
You can log multiple modules in one central location. Simply initialize the logging in
your main module, and log as necessary from the others.
# myapp.py
import logging
import mylib
def main():
logging.basicConfig(filename='myapp.log', level=logging.INFO)
logging.info('Started')
mylib.do_something()
logging.info('Finished')
if __name__ == '__main__':
main()
# mylib.py
import logging
def do_something():
logging.info('Doing something')
myapp.log
INFO:root:Started
INFO:root:Doing something
INFO:root:Finished
LOGGING
You can change the format of the logged messages to be more useful.
import logging
logging.basicConfig(filename=“myapp.log”, format='%(asctime)s %(levelname)s %(message)s')
logging.warning(‘an event was logged.')
The contents of myapp.log:
2015-1-29 10:49:46,602 WARNING an event was logged.
LOGGING
A more complicated
example with rotating
log files. The logging
module defines many
components:
Loggers
Handlers
Filters
Formatters
import logging
def init_logging(logfile):
"""Logged messages are written to rotating logfiles of the form *logfile.x* where *x* is
an integer up to 5. The output *logfile* is the most recent output.""“
LOG_FILE = logfile
logger = logging.getLogger(__name__)
#initiate a logger object with the module name
logger.setLevel(logging.DEBUG)
fh = logging.handlers.RotatingFileHandler(LOG_FILE, maxBytes=10000, backupCount=5)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
logger.addHandler(fh)
logger.info("*****RESTART*****")
if __name__ == “__main__”:
init_logging(‘path/to/log/file')
from twisted.internet import reactor
reactor.run()
logger.info(“Reactor started.”)
UNITTEST
The unittest module in the Standard Library is a framework for writing unit tests, which
specifically test a small piece of code in isolation from the rest of the codebase.
Test-driven development is advantageous for the following reasons:
• Encourages modular design.
• Easier to cover every code path.
• The actual process of testing is less time-consuming.
UNITTEST
Generally speaking, the process for creating unit tests is the following:
1. Define a class derived from unittest.TestCase.
2. Define functions within your class that start with ‘test_’.
3. You run the tests by placing unittest.main() in your file, usually at the bottom.
unittest defines a wide range of assert methods as well as set-up and cleanup
methods which you can use to define your test_ functions.
UNITTEST
Here’s an example of the simplest usage of unittest.
test_even.py
even.py
import unittest
import even
def even(num):
if num%2 == 0:
return True
return False
class EvenTest(unittest.TestCase):
def test_is_two_even(self):
self.assertTrue(even.even(4))
if __name__ == '__main__':
unittest.main()
carnahan@diablo:~>python2.7 test_even.py
.
---------------------------------------------------------------------Ran 1 test in 0.011s
OK
carnahan@diablo:~>
UNITTEST
test_even.py
even.py
import unittest
import even
def even(num):
if num%2 == 0:
return True
return False
class EvenTest(unittest.TestCase):
def test_is_two_even(self):
self.assertTrue(even.even(4))
def test_two_prime_even(self):
self.assertTrue(even.prime_even(2))
if __name__ == '__main__':
unittest.main()
def prime_even(num):
if even(num):
for i in range(num):
if num % i == 0:
return False
return True
return False
UNITTEST
carnahan@diablo:~>python2.7 test_even.py
.E
======================================================================
ERROR: test_two_prime_even (__main__.EvenTest)
---------------------------------------------------------------------Traceback (most recent call last):
File "test_even.py", line 10, in test_two_prime_even
self.assertTrue(even.prime_even(2))
File "/home/faculty/carnahan/CIS4930/demos/even.py", line 9, in prime_even
if num % i == 0:
ZeroDivisionError: integer division or modulo by zero
---------------------------------------------------------------------Ran 2 tests in 0.001s
FAILED (errors=1)
carnahan@diablo:~>
UNITTEST
test_even.py
even.py
import unittest
import even
def even(num):
if num%2 == 0:
return True
return False
class EvenTest(unittest.TestCase):
def test_is_two_even(self):
self.assertTrue(even.even(4))
def test_two_prime_even(self):
self.assertTrue(even.prime_even(2))
if __name__ == '__main__':
unittest.main()
def prime_even(num):
if even(num):
for i in range(2,num):
if num % i == 0:
return False
return True
return False
UNITTEST
carnahan@diablo:~>python2.7 test_even.py
..
---------------------------------------------------------------------Ran 2 tests in 0.000s
OK
carnahan@diablo:~>
We incrementally add unit test functions and run them – when they pass, we add more code and
develop the unit tests to assert correctness. Do not remove unit tests as you pass them.
It’s advisable to create pre-commit hooks on your repository that force the code to pass the unit
tests or else changes cannot be pushed.
SPHINX
Don’t have time to cover today, but check out the Sphinx package for your
documentation! It make it easy to create readable docs.
NEXT LECTURE
Starting Python applications.
Download