Skip to content

Commit dc28698

Browse files
committed
2 parents 7e6bdf4 + cf7871d commit dc28698

34 files changed

+1799
-0
lines changed

source-code/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ was used to develop it.
66

77
## What is it?
88

9+
1. `code-testing`: illustrations of using assertions and unit tests.
910
1. `context-manager`: illustrates how to write your own context managers.
1011
1. `coroutines`: illustrates how to write coroutines in Python.
1112
1. `decorators`: illustrates how to write decorators in Python.
@@ -15,10 +16,12 @@ was used to develop it.
1516
programming in Python.
1617
1. `introspection`: illustration of how to implement introspection in
1718
Python.
19+
1. `iterators`: illustrates iterators and functional programming concepts.
1820
1. `object-orientation`: illustrates some concepts of object-oriented
1921
programming in Python.
2022
1. `operators-functools`: illustrates some applications of the `operator`
2123
and `functools` modules in Python's standard library.
24+
1. `pyparsing`: illustration of unit testing on real world code.
2225
1. `typing`: illustrates how to use type annotation in Python, and
2326
demonstrates `mypy`.
2427
1. `unit-testing`: illustrates writing unit tests with `unittest` and
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Asserts
2+
3+
`assert` is a Python statement, so it not part of unit testing per se.
4+
It is mainly intended for contract-based programming and one can prevent
5+
code to be emitted for asserts by using the `-O` optimizatino flag when
6+
running the code.
7+
8+
## What is it?
9+
1. `fac.py`: implements factorial function, checking whether the
10+
argument is of type integer, and positive using asserts.
11+
1. `run.sh`: Bash shell script to run the Python code, once with, and once
12+
without code generated for assertions.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env python
2+
3+
def fac(n):
4+
'''compute the factorial of given number'''
5+
assert type(n) == int, 'argument must be integer'
6+
assert n >= 0, 'argument must be positive'
7+
if n > 1:
8+
return n*fac(n - 1)
9+
else:
10+
return 1
11+
12+
if __name__ == '__main__':
13+
for i in range(5):
14+
print('{0}! = {1}'.format(i, fac(i)))
15+
for i in [-2, 0.3, 3.0]:
16+
try:
17+
print('{0}! = {1}'.format(i, fac(i)))
18+
except AssertionError as error:
19+
print('{0}! failed: "{1}"'.format(i, error))
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/bin/bash
2+
3+
echo '# run with assertions enabled (default):'
4+
python ./fac.py
5+
echo '# --------------------'
6+
echo '# run with assertions disabled (-O optimization flag):'
7+
python -O ./fac.py

source-code/code-testing/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
CodeTesting
2+
===========
3+
4+
Python's standard library contains the `unittest` module, which allows
5+
to easily implement unit testing, i.e., it is a framework for setting
6+
up code for regression testing while developing, modifying, or refactoring
7+
a code base.
8+
9+
What is it?
10+
-----------
11+
1. `constant_db.py`: implements a class that creates a database containing
12+
constants (mathematical or fysical), and defines a number of methods
13+
to query it.
14+
1. `constant_db_test.py`: `unittest.TestCase` extension that implements a
15+
number of tests for the database. The database is set up in the
16+
`setUp` method, and closed and removed in the `tearDown` method.
17+
Various tests are implemented to illustrate some of the assert methods
18+
defined by `unittest.TestCase`.
19+
1. `run_test.sh`: shell script that actually runs the test code, and that
20+
uses discovery to minimize maintenance of test code.
21+
1. `Asserts`: `assert` is a Python statement, so it not part of unit
22+
testing per se. It is mainly intended for contract-based programming
23+
and one can prevent code to be emitted for asserts by using the
24+
`-O` optimizatino flag when running the code.
25+
26+
Note
27+
----
28+
The unit test that checks the number of entries in the database should
29+
fail to illustrate the output. Although there are only three constants
30+
defined in the database, `pi` was added twice, so the total number of
31+
rows is 4, not 3.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import sqlite3
2+
3+
4+
class UnknownConstantError(Exception):
5+
pass
6+
7+
8+
class ConstantDb(object):
9+
10+
def __init__(self, file_name='const.db'):
11+
self._conn = sqlite3.connect(file_name)
12+
13+
def close(self):
14+
self._conn.close()
15+
16+
def init(self):
17+
constants = {
18+
'g': (9.81, 'm/s**2'),
19+
'pi': (3.14, None),
20+
'mach': (0.00338, 'm/s'),
21+
}
22+
cursor = self._conn.cursor()
23+
cursor.execute('''CREATE TABLE constants (
24+
name TEXT NOT NULL,
25+
value REAL NOT NULL,
26+
unit TEXT
27+
)''')
28+
for name, data in constants.items():
29+
cursor.execute('''INSERT INTO constants
30+
(name, value, unit) VALUES (?, ?, ?)''',
31+
(name, data[0], data[1]))
32+
cursor.execute('''INSERT INTO constants
33+
(name, value, unit) VALUES (?, ?, ?)''',
34+
('pi', 3.1415, None))
35+
self._conn.commit()
36+
cursor.close()
37+
38+
def get_value(self, name):
39+
cursor = self._conn.cursor()
40+
cursor.execute('''SELECT value FROM constants
41+
WHERE name = ?''', (name, ))
42+
rows = cursor.fetchall()
43+
cursor.close()
44+
if not rows:
45+
msg = "constant '{0}' is undefined".format(name)
46+
raise UnknownConstantError(msg)
47+
else:
48+
return rows[0][0]
49+
50+
def get_names(self):
51+
cursor = self._conn.cursor()
52+
cursor.execute('''SELECT name FROM constants''')
53+
rows = cursor.fetchall()
54+
cursor.close()
55+
return [row[0] for row in rows]
56+
57+
def get_nr_constants(self):
58+
return len(self.get_names())
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import os
2+
import unittest
3+
4+
from constant_db import ConstantDb, UnknownConstantError
5+
6+
7+
class ConstantDbTest(unittest.TestCase):
8+
9+
def setUp(self):
10+
self.file_name = 'test.db'
11+
self.db = ConstantDb(self.file_name)
12+
self.db.init()
13+
14+
def test_g_value(self):
15+
expected_value = 9.81
16+
value = self.db.get_value('g')
17+
self.assertAlmostEqual(expected_value, value, 1.0e-4)
18+
19+
def test_constant_names(self):
20+
expected_names = {'g', 'pi', 'mach'}
21+
names = self.db.get_names()
22+
for name in names:
23+
self.assertIn(name, expected_names)
24+
25+
def test_constant_unknown(self):
26+
self.assertRaises(UnknownConstantError, self.db.get_value, 'c')
27+
28+
def test_nr_constants(self):
29+
expected_nr_constants = 3
30+
nr_constats = self.db.get_nr_constants()
31+
self.assertEquals(expected_nr_constants, nr_constats,
32+
'This test is supposed to fail by way '
33+
'of illustration')
34+
35+
def tearDown(self):
36+
self.db.close()
37+
os.remove(self.file_name)

source-code/code-testing/run_tests.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/bash -l
2+
3+
python -m unittest discover -p '*_test.py'

source-code/iterators/README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Iterators
2+
An iterator is a nice concept since it allows to create laze sequences,
3+
i.e., sequences that have elements that are computed only when they are
4+
requested by retrieving the enxt element.
5+
6+
Python allows for two basic ways to implement iterators, either as
7+
function that have a LHS yield statements, or as class that implement
8+
an `__iter__` and a `next` method.
9+
10+
However, often it is not necessary to implements iterators from scratch,
11+
often they can be constructed by using the Python standard library's
12+
`itertools` functionality.
13+
14+
## What is it?
15+
1. `accumulator.py`: illustrates the use of `itertools`'s `accumulate` in
16+
for various types of data and operators.
17+
1. `count_down.py`: simple illustration of a class that implements an
18+
iterable.
19+
1. `event_generate.py`: a sequence of `Event` objects is generated by
20+
an instance of the `EventIter` class. Events have a type, a start time,
21+
and a duration. Events of the same type can not overlap. The
22+
`EventIter` constructor takes a list of event types, and a start time.
23+
It generates sequence of random type, start time and duration, until an
24+
event is generated that last later than the stop time.
25+
1. `generators.ipynb`: Jupyter notebook illustrating generators.
26+
1. `people.py`: illustration of `itertools`'s `groupby`, and `operator`'s
27+
`attrgetter` methods. Note that `groupby` does not reorder the
28+
original iterators element, but only groups consecutive elements that
29+
have the same key.
30+
1. `primes.py`: this script will generate the sequence of prime numbers
31+
until it is interupted. The iterator is implemented by a function with
32+
a `yield` statement.
33+
1. `primes_multiple_calls.py`: illustrates that a function with `yield`
34+
instantiates a generator when called, and hence "starts over" for
35+
each `for`-loop.
36+
1. `primes_itertools.py`: this script also generates a potentially
37+
infinite sequence of prime numbers, but it is implemented using
38+
the `count` function of the `itertools` module in Python's standard
39+
library, as well as the `filter` function.
40+
1. `dataset.py`: illustrates the `__iter__` and `__next__` methods, as well
41+
as utilities of the `operator` module.
42+
1. `generating_data.py`: a retake of the data geenration script in
43+
Fundamentals, now using `itertools` and built-in Python functional
44+
features.

source-code/iterators/accumulator.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env python
2+
3+
from itertools import accumulate
4+
from operator import add, mul, concat
5+
6+
if __name__ == '__main__':
7+
data = range(10)
8+
for x in accumulate(data, add):
9+
print(x)
10+
for x in accumulate(data[1:], mul):
11+
print(x)
12+
for x in ([y] for y in data):
13+
print(x)
14+
for x in accumulate(([y] for y in data), concat):
15+
print(x)
16+
for fragment in accumulate('hello world!', concat):
17+
print(fragment)

0 commit comments

Comments
 (0)