Skip to content

Commit 6809655

Browse files
committed
Apply type hinting across most modules
1 parent 35f1fae commit 6809655

28 files changed

Lines changed: 157 additions & 152 deletions

ultimatepython/advanced/async.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,20 @@ def _is_valid_record(record):
3131
return record.queued_at < record.started_at
3232

3333

34-
def _current_time():
34+
def _current_time() -> datetime:
3535
"""Return current time that is timezone-naive."""
3636
return datetime.now()
3737

3838

39-
async def start_job(job_id, delay):
39+
async def start_job(job_id: str, delay: float) -> JobRecord:
4040
"""Start job ID after a certain amount of delay."""
4141
queue_time = _current_time()
4242
await asyncio.sleep(delay)
4343
start_time = _current_time()
4444
return JobRecord(job_id, queue_time, start_time)
4545

4646

47-
async def schedule_jobs():
47+
async def schedule_jobs() -> None:
4848
"""Schedule jobs concurrently."""
4949
# Start a job which also represents a coroutine
5050
single_job = start_job(uuid4().hex, _DELAY_SMALL)
@@ -77,7 +77,7 @@ async def schedule_jobs():
7777
assert all(_is_valid_record(record) for record in batch_records)
7878

7979

80-
def main():
80+
def main() -> None:
8181
asyncio.run(schedule_jobs())
8282

8383

ultimatepython/advanced/context_manager.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from contextlib import contextmanager
1010
from io import StringIO
11+
from typing import Generator
1112

1213
# Simple directory with file contents
1314
_FILESYSTEM = {
@@ -18,7 +19,7 @@
1819

1920

2021
@contextmanager
21-
def file(filename):
22+
def file(filename: str) -> Generator[StringIO, None, None]:
2223
"""File context manager.
2324
2425
This is the function variant of the context manager. Context managers
@@ -42,19 +43,19 @@ class FileHandler:
4243
class or simply write a function.
4344
"""
4445

45-
def __init__(self, filename):
46+
def __init__(self, filename: str) -> None:
4647
self.io_buffer = StringIO(_FILESYSTEM[filename])
4748

48-
def __enter__(self):
49+
def __enter__(self) -> StringIO:
4950
"""Pass the buffer to the context block."""
5051
return self.io_buffer
5152

52-
def __exit__(self, *args):
53+
def __exit__(self, *args) -> None:
5354
"""Close the buffer unconditionally."""
5455
self.io_buffer.close()
5556

5657

57-
def main():
58+
def main() -> None:
5859
# An example of a function-based context manager
5960
with file("a.txt") as txt_buffer:
6061
assert txt_buffer.read() == "Hello World"

ultimatepython/advanced/date_time.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from datetime import datetime, timezone
2727

2828

29-
def convert_dt_to_utc_epoch(dt):
29+
def convert_dt_to_utc_epoch(dt: datetime) -> int:
3030
"""Convert datetime to UTC epoch seconds.
3131
3232
Note that the timestamp method assumes that an offset-naive
@@ -36,27 +36,27 @@ def convert_dt_to_utc_epoch(dt):
3636
return dt.timestamp()
3737

3838

39-
def convert_utc_epoch_to_dt(epoch):
39+
def convert_utc_epoch_to_dt(epoch: int) -> datetime:
4040
"""Convert UTC epoch seconds to datetime."""
4141
return datetime.fromtimestamp(epoch, tz=timezone.utc)
4242

4343

44-
def convert_dt_timezone(dt, tz):
44+
def convert_dt_timezone(dt: datetime, tz: timezone) -> datetime:
4545
"""Convert datetime timezone."""
4646
return dt.astimezone(tz=tz)
4747

4848

49-
def get_utc_now_as_dt():
49+
def get_utc_now_as_dt() -> datetime:
5050
"""Get current UTC time as datetime."""
5151
return datetime.now(tz=timezone.utc)
5252

5353

54-
def get_utc_now_as_epoch():
54+
def get_utc_now_as_epoch() -> int:
5555
"""Get current UTC time as epoch seconds."""
5656
return convert_dt_to_utc_epoch(get_utc_now_as_dt())
5757

5858

59-
def main():
59+
def main() -> None:
6060
# Create offset-naive datetime
6161
naive_dt = datetime.now()
6262
assert naive_dt.tzinfo is None

ultimatepython/advanced/decorator.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
"""
77

88
from functools import wraps
9+
from typing import Any, Callable
910

1011
# Module-level constants
1112
_MASKING = "*"
1213

1314

14-
def run_with_stringy(fn):
15+
def run_with_stringy(fn: Callable[[str], str]) -> Callable[[Any], Any]:
1516
"""Run a string function with a string or a collection of strings.
1617
1718
We define a custom decorator that allows us to convert a function whose
@@ -40,7 +41,7 @@ def run_with_stringy(fn):
4041
"""
4142

4243
@wraps(fn)
43-
def wrapper(obj):
44+
def wrapper(obj: Any) -> Any:
4445
"""Apply wrapped function to a string or a collection.
4546
4647
This looks like a policy-based engine which runs a `return` statement
@@ -65,14 +66,14 @@ def wrapper(obj):
6566

6667

6768
@run_with_stringy
68-
def hide_content(content):
69+
def hide_content(content: str) -> str:
6970
"""Hide half of the string content."""
7071
start_point = len(content) // 2
7172
num_of_asterisks = len(content) // 2 + len(content) % 2
7273
return content[:start_point] + _MASKING * num_of_asterisks
7374

7475

75-
def _is_hidden(obj):
76+
def _is_hidden(obj: Any) -> bool:
7677
"""Check whether string or collection is hidden."""
7778
if isinstance(obj, str):
7879
return _MASKING in obj
@@ -81,7 +82,7 @@ def _is_hidden(obj):
8182
return all(_is_hidden(value) for value in obj)
8283

8384

84-
def main():
85+
def main() -> None:
8586
# There is so much plain-text data out in the open
8687
insecure_data = [
8788
{"username": "johndoe", "country": "USA"}, # User information

ultimatepython/advanced/file_handling.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,34 +13,34 @@
1313
_TARGET_FILE = "sample.txt"
1414

1515

16-
def read_file(filename):
16+
def read_file(filename: str) -> str:
1717
"""Read content from existing file."""
1818
with open(filename, "r") as file:
1919
content = file.read()
2020
return content
2121

2222

23-
def write_file(filename, content):
23+
def write_file(filename: str, content: str) -> str:
2424
"""Write content to new file."""
2525
with open(filename, "w") as file:
2626
file.write(content)
2727
return f"Content written to '{filename}'."
2828

2929

30-
def append_file(filename, content):
30+
def append_file(filename: str, content: str) -> str:
3131
"""Append content to existing file."""
3232
with open(filename, "a") as file:
3333
file.write(content)
3434
return f"Content appended to '{filename}'."
3535

3636

37-
def delete_file(filename):
37+
def delete_file(filename: str) -> str:
3838
"""Delete content of existing file."""
3939
os.remove(filename)
4040
return f"'{filename}' has been deleted."
4141

4242

43-
def main():
43+
def main() -> None:
4444
result = write_file(_TARGET_FILE, "This is a test.")
4545
assert result == f"Content written to '{_TARGET_FILE}'."
4646

ultimatepython/advanced/thread.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,27 @@
2424
import time
2525
from concurrent.futures import ThreadPoolExecutor, as_completed
2626
from datetime import datetime
27+
from typing import Callable, Iterable, Set
2728

2829
# Module-level constants
2930
_MULTIPLY_DELAY = 0.01 # delay is long enough for threads to be faster
3031

3132

32-
def multiply_by_two(item):
33+
def multiply_by_two(item: int) -> int:
3334
"""This multiplication has a small delay."""
3435
time.sleep(_MULTIPLY_DELAY)
3536
return item * 2
3637

3738

38-
def run_thread_workers(work, data):
39+
def run_thread_workers(work: Callable[[int], int], data: Iterable[int]) -> Set[int]:
3940
"""Run thread workers that invoke work on each data element.
4041
4142
The inspiration for this function comes directly from an example
4243
in the Python 3.x documentation:
4344
4445
https://docs.python.org/3/library/concurrent.futures.html
4546
"""
46-
results = set()
47+
results: Set[int] = set()
4748

4849
# We can use a with statement to ensure workers are cleaned up promptly
4950
with ThreadPoolExecutor() as executor:
@@ -55,7 +56,7 @@ def run_thread_workers(work, data):
5556
return results
5657

5758

58-
def main():
59+
def main() -> None:
5960
original_data = {num for num in range(5)}
6061
expected_data = {(item * 2) for item in original_data}
6162

ultimatepython/classes/abstract_class.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,20 @@ class Employee(ABC):
2020
can work and relax is different from another type of employee.
2121
"""
2222

23-
def __init__(self, name, title):
23+
def __init__(self, name: str, title: str) -> None:
2424
self.name = name
2525
self.title = title
2626

27-
def __str__(self):
27+
def __str__(self) -> str:
2828
return self.name
2929

3030
@abstractmethod
31-
def do_work(self):
31+
def do_work(self) -> str:
3232
"""Do something for work."""
3333
raise NotImplementedError
3434

3535
@abstractmethod
36-
def do_relax(self):
36+
def do_relax(self) -> str:
3737
"""Do something to relax."""
3838
raise NotImplementedError
3939

@@ -49,14 +49,14 @@ class Engineer(Employee):
4949
is something that a manager prefers not to do.
5050
"""
5151

52-
def __init__(self, name, title, skill):
52+
def __init__(self, name: str, title: str, skill: str) -> None:
5353
super().__init__(name, title)
5454
self.skill = skill
5555

56-
def do_work(self):
56+
def do_work(self) -> str:
5757
return f"{self} is coding in {self.skill}"
5858

59-
def do_relax(self):
59+
def do_relax(self) -> str:
6060
return f"{self} is watching YouTube"
6161

6262
def do_refactor(self):
@@ -73,22 +73,22 @@ class is concrete. Notice that a manager has direct reports and
7373
engineer.
7474
"""
7575

76-
def __init__(self, name, title, direct_reports):
76+
def __init__(self, name: str, title: str, direct_reports: list) -> None:
7777
super().__init__(name, title)
7878
self.direct_reports = direct_reports
7979

80-
def do_work(self):
80+
def do_work(self) -> str:
8181
return f"{self} is meeting up with {len(self.direct_reports)} reports"
8282

83-
def do_relax(self):
83+
def do_relax(self) -> str:
8484
return f"{self} is taking a trip to the Bahamas"
8585

86-
def do_hire(self):
86+
def do_hire(self) -> str:
8787
"""Do the hard work of hiring employees, unlike engineers."""
8888
return f"{self} is hiring employees"
8989

9090

91-
def main():
91+
def main() -> None:
9292
# Declare two engineers
9393
engineer_john = Engineer("John Doe", "Software Engineer", "Android")
9494
engineer_jane = Engineer("Jane Doe", "Software Engineer", "iOS")

ultimatepython/classes/basic_class.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,27 @@ class Car:
1515
class definition.
1616
"""
1717

18-
def __init__(self, make, model, year, miles):
18+
def __init__(self, make: str, model: str, year: int, miles: float) -> None:
1919
"""Constructor logic."""
2020
self.make = make
2121
self.model = model
2222
self.year = year
2323
self.miles = miles
2424

25-
def __repr__(self):
25+
def __repr__(self) -> str:
2626
"""Formal representation for developers."""
2727
return f"<Car make={self.make} model={self.model} year={self.year}>"
2828

29-
def __str__(self):
29+
def __str__(self) -> str:
3030
"""Informal representation for users."""
3131
return f"{self.make} {self.model} ({self.year})"
3232

33-
def drive(self, rate_in_mph):
33+
def drive(self, rate_in_mph: int) -> str:
3434
"""Drive car at a certain rate in MPH."""
3535
return f"{self} is driving at {rate_in_mph} MPH"
3636

3737

38-
def main():
38+
def main() -> None:
3939
# Create a car with the provided class constructor
4040
car = Car("Bumble", "Bee", 2000, 200000.0)
4141

0 commit comments

Comments
 (0)