8000 Code files added · jheok/Clean-Code-in-Python@5e18464 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5e18464

Browse files
committed
Code files added
1 parent 2de6e84 commit 5e18464

File tree

199 files changed

+7740
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

199 files changed

+7740
-0
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
__pycache__
2+
*.sw[op]
3+
.mypy_cache/
4+
*.tar.gz
5+
.pytest_cache/

Chapter01/Makefile

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
typehint:
2+
mypy --ignore-missing-imports src/
3+
4+
test:
5+
pytest tests/
6+
7+
lint:
8+
pylint src/
9+
10+
checklist: lint typehint test
11+
12+
black:
13+
black -l 79 *.py
14+
15+
setup:
16+
$(VIRTUAL_ENV)/bin/pip install -r requirements.txt
17+
18+
.PHONY: typehint test lint checklist black

Chapter01/README.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Chapter 01 - Introduction, Tools, and Formatting
2+
================================================
3+
4+
Install dependencies::
5+
6+
make setup
7+
8+
Run the tests::
9+
10+
make test

Chapter01/before_black.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"""Clean Code in Python - Chapter 1: Introduction, Tools, and Formatting
2+
3+
> Black:
4+
A code that is compliant with PEP-8, but that still can be modified by back
5+
6+
7+
Run::
8+
black -l 79 before_black.py
9+
10+
To see the difference
11+
"""
12+
13+
14+
def my_function(name):
15+
"""
16+
>>> my_function('black')
17+
'received Black'
18+
"""
19+
return 'received {0}'.format(name.title())

Chapter01/requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-r ../requirements.txt
2+
pytest==3.7.1

Chapter01/src/__init__.py

Whitespace-only changes.

Chapter01/src/annotations.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"""Clean Code in Python - Chapter 1: Introduction, Tools, and Formatting
2+
3+
> Annotations
4+
"""
5+
6+
7+
class Point: # pylint: disable=R0903
8+
"""Example to be used as return type of locate"""
9+
def __init__(self, lat, long):
10+
self.lat = lat
11+
self.long = long
12+
13+
14+
def locate(latitude: float, longitude: float) -> Point:
15+
"""Find an object in the map by its coordinates"""
16+
return Point(latitude, longitude)
17+
18+
19+
class NewPoint: # pylint: disable=R0903
20+
"""Example to display its __annotations__ attribute."""
21+
lat: float
22+
long: float

Chapter01/src/test_annotations.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"""Clean Code in Python - Chapter 01: Introcution, Tools, and Formatting
2+
3+
Tests for annotations examples
4+
5+
"""
6+
import pytest
7+
8+
from src.annotations import NewPoint, Point, locate
9+
10+
11+
@pytest.mark.parametrize(
12+
"element,expected",
13+
(
14+
(locate, {"latitude": float, "longitude": float, "return": Point}),
15+
(NewPoint, {"lat": float, "long": float}),
16+
),
17+
)
18+
def test_annotations(element, expected):
19+
"""test the class/functions againts its expected annotations"""
20+
assert getattr(element, "__annotations__") == expected

Chapter01/tests

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
src

Chapter02/Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
PYTHON:=$(VIRTUAL_ENV)/bin/python
2+
3+
test:
4+
@$(PYTHON) -m doctest *.py
5+
@$(PYTHON) -m unittest *.py
6+
7+
.PHONY: test

Chapter02/README.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Chapter 02: Pythonic Code
2+
=========================
3+
4+
Test the code::
5+
6+
make test

Chapter02/callables.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""Clean Code in Python - Chapter 2: Pythonic Code
2+
3+
> Callable objects
4+
5+
"""
6+
7+
from collections import defaultdict
8+
9+
10+
class CallCount:
11+
"""
12+
>>> cc = CallCount()
13+
>>> cc(1)
14+
1
15+
>>> cc(2)
16+
1
17+
>>> cc(1)
18+
2
19+
>>> cc(1)
20+
3
21+
>>> cc("something")
22+
1
23+
24+
>>> callable(cc)
25+
True
26+
"""
27+
28+
def __init__(self):
29+
self._counts = defaultdict(int)
30+
31+
def __call__(self, argument):
32+
self._counts[argument] += 1
33+
return self._counts[argument]

Chapter02/caveats.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""Clean Code in Python - Chapter 2: Pythonic Code
2+
3+
> Caveats in Python
4+
"""
5+
6+
from collections import UserList
7+
8+
9+
def wrong_user_display(user_metadata: dict = {"name": "John", "age": 30}):
10+
name = user_metadata.pop("name")
11+
age = user_metadata.pop("age")
12+
13+
return f"{name} ({age})"
14+
15+
16+
def user_display(user_metadata: dict = None):
17+
user_metadata = user_metadata or {"name": "John", "age": 30}
18+
19+
name = user_metadata.pop("name")
20+
age = user_metadata.pop("age")
21+
22+
return f"{name} ({age})"
23+
24+
25+
class BadList(list):
26+
def __getitem__(self, index):
27+
value = super().__getitem__(index)
28+
if index % 2 == 0:
29+
prefix = "even"
30+
else:
31+
prefix = "odd"
32+
return f"[{prefix}] {value}"
33+
34+
35+
class GoodList(UserList):
36+
def __getitem__(self, index):
37+
value = super().__getitem__(index)
38+
if index % 2 == 0:
39+
prefix = "even"
40+
else:
41+
prefix = "odd"
42+
return f"[{prefix}] {value}"

Chapter02/container.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""Chapter 2 - Containers"""
2+
3+
4+
def mark_coordinate(grid, coord):
5+
if 0 <= coord.x < grid.width and 0 <= coord.y < grid.height:
6+
grid[coord] = 1
7+
8+
if coord in grid:
9+
grid[coord] = 1
10+
11+
12+
class Boundaries:
13+
def __init__(self, width, height):
14+
self.width = width
15+
self.height = heigh
16+
17+
def __contains__(self, coord):
18+
x, y = coord
19+
return 0 <= x < self.width and 0 <= y < self.height
20+
21+
22+
class Grid:
23+
def __init__(self, width, height):
24+
self.width = width
25+
self.height = height
26+
self.limits = Boundaries(width, height)
27+
28+
def __contains__(self, coord):
29+
return coord in self.limits

Chapter02/contextmanagers.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import contextlib
2+
3+
4+
run = print
5+
6+
7+
def stop_database():
8+
run("systemctl stop postgresql.service")
9+
10+
11+
def start_database():
12+
run("systemctl start postgresql.service")
13+
14+
15+
class DBHandler:
16+
def __enter__(self):
17+
stop_database()
18+
return self
19+
20+
def __exit__(self, exc_type, ex_value, ex_traceback):
21+
start_database()
22+
23+
24+
def db_backup():
25+
run("pg_dump database")
26+
27+
28+
@contextlib.contextmanager
29+
def db_handler():
30+
stop_database()
31+
yield
32+
start_database()
33+
34+
35+
class dbhandler_decorator(contextlib.ContextDecorator):
36+
def __enter__(self):
37+
stop_database()
38+
39+
def __exit__(self, ext_type, ex_value, ex_traceback):
40+
start_database()
41+
42+
43+
@dbhandler_decorator()
44+
def offline_backup():
45+
run("pg_dump database")
46+
47+
48+
def main():
49+
with DBHandler():
50+
db_backup()
51+
52+
with db_handler():
53+
db_backup()
54+
55+
offline_backup()
56+
57+
58+
if __name__ == "__main__":
59+
main()

Chapter02/dynamic.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""Clean Code in Python - Chapter 2: Pythonic Code
2+
3+
> Dynamic Attributes
4+
5+
"""
6+
7+
8+
class DynamicAttributes:
9+
"""
10+
>>> dyn = DynamicAttributes("value")
11+
>>> dyn.attribute
12+
'value'
13+
14+
>>> dyn.fallback_test
15+
'[fallback resolved] test'
16+
17+
>>> dyn.__dict__["fallback_new"] = "new value"
18+
>>> dyn.fallback_new
19+
'new value'
20+
21+
>>> getattr(dyn, "something", "default")
22+
'default'
23+
"""
24+
25+
def __init__(self, attribute):
26+
self.attribute = attribute
27+
28+
def __getattr__(self, attr):
29+
if attr.startswith("fallback_"):
30+
name = attr.replace("fallback_", "")
31+
return f"[fallback resolved] {name}"
32+
raise AttributeError(
33+
f"{self.__class__.__name__} has no attribute {attr}"
34+
)

Chapter02/indices.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"""Indexes and slices
2+
Getting elements by an index or range
3+
"""
4+
import doctest
5+
6+
7+
def index_last():
8+
"""
9+
>>> my_numbers = (4, 5, 3, 9)
10+
>>> my_numbers[-1]
11+
9
12+
>>> my_numbers[-3]
13+
5
14+
"""
15+
16+
17+
def get_slices():
18+
"""
19+
>>> my_numbers = (1, 1, 2, 3, 5, 8, 13, 21)
20+
>>> my_numbers[2:5]
21+
(2, 3, 5)
22+
>>> my_numbers[:3]
23+
(1, 1, 2)
24+
>>> my_numbers[3:]
25+
(3, 5, 8, 13, 21)
26+
>>> my_numbers[::]
27+
(1, 1, 2, 3, 5, 8, 13, 21)
28+
>>> my_numbers[1:7:2]
29+
(1, 3, 8)
30+
31+
>>> interval = slice(1, 7, 2)
32+
>>> my_numbers[interval]
33+
(1, 3, 8)
34+
35+
>>> interval = slice(None, 3)
36+
>>> my_numbers[interval] == my_numbers[:3]
37+
True
38+
"""
39+
40+
41+
def main():
42+
index_last()
43+
get_slices()
44+
fail_count, _ = doctest.testmod(verbose=True)
45+
raise SystemExit(fail_count)
46+
47+
48+
if __name__ == "__main__":
49+
main()

0 commit comments

Comments
 (0)
0