Skip to content

Commit 172369d

Browse files
committed
updates
1 parent 55fbb34 commit 172369d

File tree

7 files changed

+294
-8
lines changed

7 files changed

+294
-8
lines changed

11_bottles_of_beer/solution.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ def get_args():
2727
return args
2828

2929

30+
# --------------------------------------------------
31+
def main():
32+
"""Make a jazz noise here"""
33+
34+
args = get_args()
35+
print('\n\n'.join(map(verse, range(args.num, 0, -1))))
36+
37+
3038
# --------------------------------------------------
3139
def verse(bottle):
3240
"""Sing a verse"""
@@ -61,14 +69,6 @@ def test_verse():
6169
])
6270

6371

64-
# --------------------------------------------------
65-
def main():
66-
"""Make a jazz noise here"""
67-
68-
args = get_args()
69-
print('\n\n'.join(map(verse, range(args.num, 0, -1))))
70-
71-
7272
# --------------------------------------------------
7373
if __name__ == '__main__':
7474
main()

extra/06_head/Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.PHONY: test pdf
2+
3+
pdf:
4+
asciidoctor-pdf -o README.pdf README.adoc
5+
6+
test:
7+
pytest -xv test.py

extra/06_head/README.adoc

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
= Python implementation of `head`
2+
3+
In this exercise, we'll write a Python program to emulate the Unix `head` command.
4+
If you read `man head`, you will see that this program (probably) defaults to showing the first 10 lines of a given file:
5+
6+
----
7+
NAME
8+
head -- display first lines of a file
9+
10+
SYNOPSIS
11+
head [-n count | -c bytes] [file ...]
12+
13+
DESCRIPTION
14+
This filter displays the first count lines or bytes of each of the speci-
15+
fied files, or of the standard input if no files are specified. If count
16+
is omitted it defaults to 10.
17+
18+
If more than a single file is specified, each file is preceded by a
19+
header consisting of the string ``==> XXX <=='' where ``XXX'' is the name
20+
of the file.
21+
22+
EXIT STATUS
23+
The head utility exits 0 on success, and >0 if an error occurs.
24+
----
25+
26+
The program should handle a _single_ file provided as a positional argument:
27+
28+
----
29+
$ ./head.py inputs/the-bustle.txt
30+
The bustle in a house
31+
The morning after death
32+
Is solemnest of industries
33+
Enacted upon earth,--
34+
35+
The sweeping up the heart,
36+
And putting love away
37+
We shall not want to use again
38+
Until eternity.
39+
----
40+
41+
The program should accept a `-n` or `--num` option for the number of lines to show (default 10):
42+
43+
----
44+
$ ./head.py -n 5 inputs/sonnet-29.txt
45+
Sonnet 29
46+
William Shakespeare
47+
48+
When, in disgrace with fortune and men’s eyes,
49+
I all alone beweep my outcast state,
50+
----
51+
52+
The program should reject any argument that is not a readable file:
53+
54+
----
55+
$ ./head.py blargh
56+
usage: head.py [-h] [-n int] FILE
57+
head.py: error: argument FILE: can't open 'blargh': \
58+
[Errno 2] No such file or directory: 'blargh'
59+
----
60+
61+
The program should reject a `--num` argument less than 1:
62+
63+
----
64+
$ ./head.py -n 0 inputs/gettysburg.txt
65+
usage: head.py [-h] [-n int] FILE
66+
head.py: error: --num "0" must be greater than 0
67+
----
68+
69+
Given no arguments, it should print a short usage:
70+
71+
----
72+
$ ./head.py
73+
usage: head.py [-h] [-n int] FILE
74+
head.py: error: the following arguments are required: FILE
75+
----
76+
77+
Or a longer usage for `-h` or `--help`:
78+
79+
----
80+
$ ./head.py -h
81+
usage: head.py [-h] [-n int] FILE
82+
83+
Rock the Casbah
84+
85+
positional arguments:
86+
FILE Input file
87+
88+
optional arguments:
89+
-h, --help show this help message and exit
90+
-n int, --num int Number of lines (default: 10)
91+
----
92+
93+
The program should pass all tests:
94+
95+
----
96+
$ make test
97+
pytest -xv test.py
98+
============================= test session starts ==============================
99+
...
100+
collected 8 items
101+
102+
test.py::test_exists PASSED [ 12%]
103+
test.py::test_usage PASSED [ 25%]
104+
test.py::test_bad_file PASSED [ 37%]
105+
test.py::test_bad_num PASSED [ 50%]
106+
test.py::test_default PASSED [ 62%]
107+
test.py::test_num_1 PASSED [ 75%]
108+
test.py::test_n_2 PASSED [ 87%]
109+
test.py::test_num_3 PASSED [100%]
110+
111+
============================== 8 passed in 0.54s ===============================
112+
----

extra/06_head/inputs/gettysburg.txt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Four score and seven years ago our fathers brought forth on this
2+
continent, a new nation, conceived in Liberty, and dedicated to the
3+
proposition that all men are created equal.
4+
5+
Now we are engaged in a great civil war, testing whether that nation,
6+
or any nation so conceived and so dedicated, can long endure. We are
7+
met on a great battle-field of that war. We have come to dedicate a
8+
portion of that field, as a final resting place for those who here
9+
gave their lives that that nation might live. It is altogether fitting
10+
and proper that we should do this.
11+
12+
But, in a larger sense, we can not dedicate -- we can not consecrate
13+
-- we can not hallow -- this ground. The brave men, living and dead,
14+
who struggled here, have consecrated it, far above our poor power to
15+
add or detract. The world will little note, nor long remember what we
16+
say here, but it can never forget what they did here. It is for us the
17+
living, rather, to be dedicated here to the unfinished work which they
18+
who fought here have thus far so nobly advanced. It is rather for us
19+
to be here dedicated to the great task remaining before us -- that
20+
from these honored dead we take increased devotion to that cause for
21+
which they gave the last full measure of devotion -- that we here
22+
highly resolve that these dead shall not have died in vain -- that
23+
this nation, under God, shall have a new birth of freedom -- and that
24+
government of the people, by the people, for the people, shall not
25+
perish from the earth.

extra/06_head/inputs/sonnet-29.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Sonnet 29
2+
William Shakespeare
3+
4+
When, in disgrace with fortune and men’s eyes,
5+
I all alone beweep my outcast state,
6+
And trouble deaf heaven with my bootless cries,
7+
And look upon myself and curse my fate,
8+
Wishing me like to one more rich in hope,
9+
Featured like him, like him with friends possessed,
10+
Desiring this man’s art and that man’s scope,
11+
With what I most enjoy contented least;
12+
Yet in these thoughts myself almost despising,
13+
Haply I think on thee, and then my state,
14+
(Like to the lark at break of day arising
15+
From sullen earth) sings hymns at heaven’s gate;
16+
For thy sweet love remembered such wealth brings
17+
That then I scorn to change my state with kings.

extra/06_head/inputs/the-bustle.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
The bustle in a house
2+
The morning after death
3+
Is solemnest of industries
4+
Enacted upon earth,--
5+
6+
The sweeping up the heart,
7+
And putting love away
8+
We shall not want to use again
9+
Until eternity.

extra/06_head/test.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#!/usr/bin/env python3
2+
"""tests for days.py"""
3+
4+
import os
5+
import random
6+
import re
7+
import string
8+
from subprocess import getstatusoutput
9+
10+
prg = './head.py'
11+
sonnet = './inputs/sonnet-29.txt'
12+
bustle = './inputs/the-bustle.txt'
13+
gettysburg = './inputs/gettysburg.txt'
14+
15+
16+
# --------------------------------------------------
17+
def test_exists():
18+
"""exists"""
19+
20+
assert os.path.isfile(prg)
21+
22+
23+
# --------------------------------------------------
24+
def test_usage():
25+
"""usage"""
26+
27+
for flag in ['-h', '--help']:
28+
rv, out = getstatusoutput(f'{prg} {flag}')
29+
assert rv == 0
30+
assert out.lower().startswith('usage')
31+
32+
33+
# --------------------------------------------------
34+
def test_bad_file():
35+
"""Bad file"""
36+
37+
bad = random_string()
38+
rv, out = getstatusoutput(f'{prg} {bad}')
39+
assert rv != 0
40+
assert re.search(f"No such file or directory: '{bad}'", out)
41+
42+
43+
# --------------------------------------------------
44+
def test_bad_num():
45+
"""Bad num"""
46+
47+
for bad in random.sample(range(-10, 1), 3):
48+
rv, out = getstatusoutput(f'{prg} -n {bad} {sonnet}')
49+
assert rv != 0
50+
assert re.search(f'--num "{bad}" must be greater than 0', out)
51+
52+
53+
# --------------------------------------------------
54+
def test_default():
55+
"""Default --num"""
56+
57+
rv, out = getstatusoutput(f'{prg} {sonnet}')
58+
assert rv == 0
59+
assert len(out.splitlines()) == 10
60+
expected = """
61+
Sonnet 29
62+
William Shakespeare
63+
64+
When, in disgrace with fortune and men’s eyes,
65+
I all alone beweep my outcast state,
66+
And trouble deaf heaven with my bootless cries,
67+
And look upon myself and curse my fate,
68+
Wishing me like to one more rich in hope,
69+
Featured like him, like him with friends possessed,
70+
Desiring this man’s art and that man’s scope,
71+
""".strip()
72+
assert out.strip() == expected
73+
74+
75+
# --------------------------------------------------
76+
def test_num_1():
77+
"""--num 1"""
78+
79+
rv, out = getstatusoutput(f'{prg} --num 1 {gettysburg}')
80+
assert rv == 0
81+
assert len(out.splitlines()) == 1
82+
assert out.strip(
83+
) == 'Four score and seven years ago our fathers brought forth on this'
84+
85+
86+
# --------------------------------------------------
87+
def test_n_2():
88+
"""-n 2"""
89+
90+
rv, out = getstatusoutput(f'{prg} -n 2 {sonnet}')
91+
assert rv == 0
92+
assert len(out.splitlines()) == 2
93+
expected = 'Sonnet 29\nWilliam Shakespeare'
94+
assert out.strip() == expected
95+
96+
97+
# --------------------------------------------------
98+
def test_num_3():
99+
"""--num 2"""
100+
101+
rv, out = getstatusoutput(f'{prg} --num 3 {bustle}')
102+
assert rv == 0
103+
assert len(out.splitlines()) == 3
104+
expected = '\n'.join([
105+
'The bustle in a house', 'The morning after death',
106+
'Is solemnest of industries'
107+
])
108+
assert out.strip() == expected
109+
110+
111+
# --------------------------------------------------
112+
def random_string():
113+
"""generate a random string"""
114+
115+
k = random.randint(5, 10)
116+
return ''.join(random.choices(string.ascii_letters + string.digits, k=k))

0 commit comments

Comments
 (0)