Skip to content

Commit f265ae8

Browse files
Merge pull request #23 from GSS-Cogs/#17-validate-json
validate_json_schema() function
2 parents 0f38bf0 + 34b3202 commit f265ae8

23 files changed

+730
-250
lines changed

.vscode/settings.json

+8-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,12 @@
44
"dpytools"
55
],
66
"python.testing.unittestEnabled": false,
7-
"python.testing.pytestEnabled": true
7+
"python.testing.pytestEnabled": true,
8+
"python.testing.unittestArgs": [
9+
"-v",
10+
"-s",
11+
"./tests",
12+
"-p",
13+
"test_*.py"
14+
],
815
}

Makefile

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ help:
55
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
66

77
fmt: ## (Format) - runs black and isort against the codebase
8-
poetry run black ./src/*
9-
poetry run isort ./src/*
8+
poetry run black ./dpytools/*
9+
poetry run isort ./dpytools/*
1010

1111
lint: ## Run the ruff python linter
12-
poetry run ruff ./src/*
12+
poetry run ruff ./dpytools/*
1313

1414
test: ## Run pytest and check test coverage
1515
poetry run pytest --cov-report term-missing --cov=dpytools

dpytools/config/config.py

+23-18
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,20 @@
77
from .properties.intproperty import IntegerProperty
88
from .properties.string import StringProperty
99

10+
1011
class Config:
11-
1212
def __init__(self):
1313
self._properties_to_validate: List[BaseProperty] = []
1414

1515
@staticmethod
1616
def from_env(config_dict: Dict[str, Dict[str, Any]]) -> Config:
17-
1817
config = Config()
1918

2019
for env_var_name, value in config_dict.items():
21-
2220
value_for_property = os.environ.get(env_var_name, None)
23-
assert value_for_property is not None, f'Required envionrment value "{env_var_name}" could not be found.'
21+
assert (
22+
value_for_property is not None
23+
), f'Required envionrment value "{env_var_name}" could not be found.'
2424

2525
if value["class"] == StringProperty:
2626
if value["kwargs"]:
@@ -31,13 +31,13 @@ def from_env(config_dict: Dict[str, Dict[str, Any]]) -> Config:
3131
regex = None
3232
min_len = None
3333
max_len = None
34-
34+
3535
stringprop = StringProperty(
36-
_name = value["property"],
37-
_value = value_for_property,
38-
regex = regex,
39-
min_len = min_len,
40-
max_len = max_len
36+
_name=value["property"],
37+
_value=value_for_property,
38+
regex=regex,
39+
min_len=min_len,
40+
max_len=max_len,
4141
)
4242

4343
prop_name = value["property"]
@@ -53,10 +53,10 @@ def from_env(config_dict: Dict[str, Dict[str, Any]]) -> Config:
5353
max_val = None
5454

5555
intprop = IntegerProperty(
56-
_name = value["property"],
57-
_value = value_for_property,
58-
min_val = min_val,
59-
max_val = max_val
56+
_name=value["property"],
57+
_value=value_for_property,
58+
min_val=min_val,
59+
max_val=max_val,
6060
)
6161

6262
prop_name = value["property"]
@@ -65,10 +65,11 @@ def from_env(config_dict: Dict[str, Dict[str, Any]]) -> Config:
6565

6666
else:
6767
prop_type = value["class"]
68-
raise TypeError(f"Unsupported property type specified via 'property' field, got {prop_type}. Should be of type StringProperty or IntegerProperty")
69-
70-
return config
68+
raise TypeError(
69+
f"Unsupported property type specified via 'property' field, got {prop_type}. Should be of type StringProperty or IntegerProperty"
70+
)
7171

72+
return config
7273

7374
def assert_valid_config(self):
7475
"""
@@ -79,4 +80,8 @@ def assert_valid_config(self):
7980
property.type_is_valid()
8081
property.secondary_validation()
8182

82-
self._properties_to_validate = []
83+
self._properties_to_validate = []
84+
85+
# For each of the properties you imbided above, run
86+
# self.type_is_valid()
87+
# self.secondary_validation()
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1+
from .intproperty import IntegerProperty
12
from .string import StringProperty
2-
from .intproperty import IntegerProperty

dpytools/config/properties/base.py

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from abc import ABCMeta, abstractmethod
22
from dataclasses import dataclass
3-
from typing import Any, Union, Tuple, Optional
3+
from typing import Any
4+
45

56
@dataclass
67
class BaseProperty(metaclass=ABCMeta):
@@ -13,21 +14,25 @@ def name(self):
1314

1415
@name.setter
1516
def name(self, value):
16-
raise ValueError(f"Trying to change name property to value {value} but you cannot change a property name after instantiation.")
17+
raise ValueError(
18+
f"Trying to change name property to value {value} but you cannot change a property name after instantiation."
19+
)
1720

1821
@property
1922
def value(self):
2023
return self._value
2124

2225
@value.setter
2326
def value(self, value):
24-
raise ValueError(f"Trying to change value to {value} but you cannot change a property value after instantiation.")
27+
raise ValueError(
28+
f"Trying to change value to {value} but you cannot change a property value after instantiation."
29+
)
2530

2631
@abstractmethod
2732
def type_is_valid(self):
2833
"""
2934
Validate that the property looks like
30-
its of the correct type
35+
its of the correct type
3136
"""
3237
...
3338

@@ -39,4 +44,4 @@ def secondary_validation(self):
3944
Non type based validation you might want to
4045
run against a configuration value.
4146
"""
42-
...
47+
...
+13-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
from typing import Optional
21
from dataclasses import dataclass
2+
from typing import Optional
3+
34
from .base import BaseProperty
45

6+
57
@dataclass
68
class IntegerProperty(BaseProperty):
79
min_val: Optional[int]
@@ -10,12 +12,14 @@ class IntegerProperty(BaseProperty):
1012
def type_is_valid(self):
1113
"""
1214
Validate that the property looks like
13-
its of the correct type
15+
its of the correct type
1416
"""
1517
try:
1618
int(self._value)
1719
except Exception as err:
18-
raise Exception(f"Cannot cast {self._name} value {self._value} to integer.") from err
20+
raise Exception(
21+
f"Cannot cast {self._name} value {self._value} to integer."
22+
) from err
1923

2024
def secondary_validation(self):
2125
"""
@@ -26,7 +30,11 @@ def secondary_validation(self):
2630
raise ValueError(f"Integer value for {self._name} does not exist.")
2731

2832
if self.min_val and self._value < self.min_val:
29-
raise ValueError(f"Integer value for {self._name} is lower than allowed minimum.")
33+
raise ValueError(
34+
f"Integer value for {self._name} is lower than allowed minimum."
35+
)
3036

3137
if self.max_val and self._value > self.max_val:
32-
raise ValueError(f"Integer value for {self._name} is higher than allowed maximum.")
38+
raise ValueError(
39+
f"Integer value for {self._name} is higher than allowed maximum."
40+
)

dpytools/config/properties/string.py

+18-9
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
from typing import Optional
1+
import re
22
from dataclasses import dataclass
3+
from typing import Optional
4+
35
from .base import BaseProperty
46

5-
import re
67

78
@dataclass
89
class StringProperty(BaseProperty):
@@ -13,32 +14,40 @@ class StringProperty(BaseProperty):
1314
def type_is_valid(self):
1415
"""
1516
Validate that the property looks like
16-
its of the correct type
17+
its of the correct type
1718
"""
1819
try:
1920
str(self._value)
2021
except Exception as err:
21-
raise Exception(f"Cannot cast {self.name} value {self._value} to string.") from err
22+
raise Exception(
23+
f"Cannot cast {self.name} value {self._value} to string."
24+
) from err
2225

2326
def secondary_validation(self):
2427
"""
2528
Non type based validation you might want to
2629
run against a configuration value of this kind.
2730
"""
28-
31+
2932
if len(self._value) == 0:
3033
raise ValueError(f"Str value for {self.name} is an empty string")
31-
34+
3235
if self.regex:
3336
# TODO - confirm the value matches the regex
3437
regex_search = re.search(self.regex, self._value)
3538
if not regex_search:
36-
raise ValueError(f"Str value for {self.name} does not match the given regex.")
39+
raise ValueError(
40+
f"Str value for {self.name} does not match the given regex."
41+
)
3742

3843
if self.min_len:
3944
if len(self._value) < self.min_len:
40-
raise ValueError(f"Str value for {self.name} is shorter than minimum length {self.min_len}")
45+
raise ValueError(
46+
f"Str value for {self.name} is shorter than minimum length {self.min_len}"
47+
)
4148

4249
if self.max_len:
4350
if len(self._value) > self.max_len:
44-
raise ValueError(f"Str value for {self.name} is longer than maximum length {self.max_len}")
51+
raise ValueError(
52+
f"Str value for {self.name} is longer than maximum length {self.max_len}"
53+
)

dpytools/http_clients/base.py

+20-20
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import logging
2+
13
import backoff
24
import requests
35
from requests.exceptions import HTTPError
4-
import logging
56

67

78
# Function to log retry attempts
@@ -15,19 +16,14 @@ def __init__(self, backoff_max=30):
1516
self.backoff_max = backoff_max
1617

1718
# GET request method with exponential backoff
18-
@backoff.on_exception(
19-
backoff.expo,
20-
HTTPError,
21-
max_time=30,
22-
on_backoff=log_retry
23-
)
19+
@backoff.on_exception(backoff.expo, HTTPError, max_time=30, on_backoff=log_retry)
2420
def get(self, url, *args, **kwargs):
2521
"""
2622
Sends a GET request to the specified URL with optional extra arguments.
2723
28-
This method is a thin wrapper around `requests.get()`. Any additional arguments
29-
are passed directly to `requests.get()`. For more information on the available
30-
arguments, refer to the `requests.get()` documentation:
24+
This method is a thin wrapper around `requests.get()`. Any additional arguments
25+
are passed directly to `requests.get()`. For more information on the available
26+
arguments, refer to the `requests.get()` documentation:
3127
https://docs.python-requests.org/en/latest/api/#requests.get
3228
3329
Args:
@@ -40,22 +36,22 @@ def get(self, url, *args, **kwargs):
4036
Raises:
4137
HTTPError: If the request fails for a network-related reason.
4238
"""
43-
return self._handle_request('GET', url, *args, **kwargs)
39+
return self._handle_request("GET", url, *args, **kwargs)
4440

4541
# POST request method with exponential backoff
4642
@backoff.on_exception(
47-
backoff.expo,
43+
backoff.expo,
4844
HTTPError,
49-
max_time=30,
45+
max_time=30,
5046
on_backoff=log_retry,
5147
)
5248
def post(self, url, *args, **kwargs):
5349
"""
5450
Sends a POST request to the specified URL with optional extra arguments.
5551
56-
This method is a thin wrapper around `requests.post()`. Any additional arguments
57-
are passed directly to `requests.post()`. For more information on the available
58-
arguments, refer to the `requests.post()` documentation:
52+
This method is a thin wrapper around `requests.post()`. Any additional arguments
53+
are passed directly to `requests.post()`. For more information on the available
54+
arguments, refer to the `requests.post()` documentation:
5955
https://docs.python-requests.org/en/latest/api/#requests.post
6056
6157
Args:
@@ -69,8 +65,8 @@ def post(self, url, *args, **kwargs):
6965
Raises:
7066
HTTPError: If the request fails for a network-related reason.
7167
"""
72-
return self._handle_request('POST', url, *args, **kwargs)
73-
68+
return self._handle_request("POST", url, *args, **kwargs)
69+
7470
# Method to handle requests for GET and POST
7571
def _handle_request(self, method, url, *args, **kwargs):
7672
logging.info(f"Sending {method} request to {url}")
@@ -80,8 +76,12 @@ def _handle_request(self, method, url, *args, **kwargs):
8076
return response
8177

8278
except HTTPError as http_err:
83-
logging.error(f"HTTP error occurred: {http_err} when sending a {method} to {url} with headers {kwargs.get('headers')}")
79+
logging.error(
80+
f"HTTP error occurred: {http_err} when sending a {method} to {url} with headers {kwargs.get('headers')}"
81+
)
8482
raise http_err
8583
except Exception as err:
86-
logging.error(f"Other error occurred: {err} when sending a {method} to {url} with headers {kwargs.get('headers')}")
84+
logging.error(
85+
f"Other error occurred: {err} when sending a {method} to {url} with headers {kwargs.get('headers')}"
86+
)
8787
raise err

dpytools/logger/logger.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
from typing import Dict, List, Optional, Union
2-
import structlog
3-
import traceback
41
import json
2+
import traceback
53
from datetime import datetime, timezone
4+
from typing import Dict, List, Optional
5+
6+
import structlog
67

78

89
def level_to_severity(level: int) -> int:

0 commit comments

Comments
 (0)