Add a dataclass and datavalidation, add Path example.
David Blume

David Blume commited on 2021-04-04 12:41:39
Showing 2 changed files, with 65 additions and 1 deletions.

... ...
@@ -0,0 +1,45 @@
1
+#!/usr/bin/env python3
2
+#from https://python.plainenglish.io/taming-your-python-dictionaries-with-dataclasses-marshmallow-and-desert-388dbffedaec
3
+from dataclasses import dataclass
4
+import json
5
+from marshmallow import EXCLUDE, fields, post_load, Schema, validate
6
+import requests
7
+
8
+@dataclass
9
+class Activity:
10
+    activity: str
11
+    participants: int
12
+    price: float
13
+
14
+class ActivitySchema(Schema):
15
+    activity = fields.Str(required=True)
16
+    participants = fields.Int(
17
+        required=True,
18
+        validate=validate.Range(min=1, max=50, error="Participants must be between 1 and 50 people")
19
+    )
20
+    price = fields.Float(
21
+        required=True,
22
+        validate=validate.Range(min=0, max=1, error="Price must be between $1 and $100")
23
+    )
24
+
25
+    @post_load
26
+    def transform_price(self, data, **kwargs):
27
+        # The `price` key always has the price between 0 and 1 for some reason so
28
+        # let's convert that number into dollars
29
+        data['price'] = data['price'] * 100
30
+        return data
31
+
32
+
33
+def get_activity() -> Activity:
34
+    resp = requests.get("https://www.boredapi.com/api/activity").json()
35
+    validated_resp = ActivitySchema(unknown=EXCLUDE).load(resp)
36
+
37
+    return Activity(
38
+        activity=validated_resp["activity"],
39
+        participants=validated_resp["participants"],
40
+        price=validated_resp["price"]
41
+    )
42
+
43
+
44
+if __name__ == '__main__':
45
+    print(get_activity())
... ...
@@ -9,7 +9,13 @@ import counter.counter
9 9
 import sitesize.sitesize
10 10
 from typing import Optional, Callable
11 11
 from decorators import timeit, profile
12
-
12
+from pathlib import Path
13
+try:
14
+    import marshmallow
15
+    marshmallow_imported = True
16
+    import bored.bored
17
+except ModuleNotFoundError:
18
+    marshmallow_imported = False
13 19
 
14 20
 v_print:Callable
15 21
 def set_v_print(verbose: bool) -> None:
... ...
@@ -23,6 +29,12 @@ def set_v_print(verbose: bool) -> None:
23 29
     v_print = print if verbose else lambda *a, **k: None
24 30
 
25 31
 
32
+# This class demonstrates decorators.
33
+#
34
+# Consider these alternatives for data classes:
35
+# * dataclasses.dataclass: sensible defaults, compares class type, can be frozen
36
+#                          See "bored" module for data validation with marshmallow
37
+# * collections.namedtuple: esp. useful for results of csv and sqlite3
26 38
 class Coffee:
27 39
 
28 40
     def __init__(self, price: float):
... ...
@@ -54,6 +66,13 @@ def main(debug: bool) -> None:
54 66
     v_print("Running sitesize...")
55 67
     sitesize.sitesize.run()
56 68
 
69
+    # Jessica Chen Fan's validation with dataclasses, marshmallow and desert
70
+    if marshmallow_imported:
71
+        print(bored.bored.get_activity())
72
+
73
+    if (Path.home() / 'bin').exists():
74
+        print('Your home directory has a bin/ directory.')
75
+
57 76
     cuppa = Coffee(5.00)
58 77
     cuppa.price = cuppa.price - 1.00
59 78
 
60 79