-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
test: increase unit test coverage #52
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -193,14 +193,14 @@ def check_success(data: Task) -> bool: | |
time.sleep(0.5) | ||
|
||
def _request_params( | ||
self, data: Optional[str] = None, | ||
params: Optional[Dict] = None | ||
self, data: Optional[str] = None, params: Optional[Dict] = None | ||
) -> Dict[str, Any]: | ||
kwargs: Dict[str, Any] = {} | ||
kwargs['timeout'] = self.timeout | ||
kwargs['headers'] = {} | ||
kwargs['headers']['Content-type'] = 'application/json' | ||
kwargs['auth'] = (self.user, self.password) | ||
if self.user is not None and self.password is not None: | ||
kwargs['auth'] = (self.user, self.password) | ||
Comment on lines
+202
to
+203
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is to address a depreciation warning as already mentioned in the PR description. |
||
if data: | ||
kwargs['data'] = data | ||
if params: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,19 +10,15 @@ | |
from typing import Any, Dict, List, Optional, Tuple, Type, Union | ||
|
||
|
||
@attrs | ||
@attrs(repr=False) | ||
class _ListOfValidator(object): | ||
type: Type = attrib() | ||
|
||
def __call__(self, inst, attr, value): | ||
""" | ||
We use a callable class to be able to change the ``__repr__``. | ||
""" | ||
def __call__(self, inst, attr, value) -> None: | ||
if not all([isinstance(n, self.type) for n in value]): | ||
raise TypeError( | ||
"'{attr.name}' must be a list of {self.type!r} (got {value!r} " | ||
"that is a list of {values[0].__class__!r}).", | ||
attr, self.type, value, | ||
Comment on lines
-24
to
-25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Checking for just |
||
f"'{attr.name}' must be a list of {self.type!r} (got " | ||
f"{value!r}", attr | ||
Comment on lines
-13
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one took me a while: I couldn't figure out why the |
||
) | ||
|
||
def __repr__(self) -> str: | ||
|
@@ -60,15 +56,15 @@ def strconv(value: Any) -> Any: | |
# since an int64 value is encoded as a string in json we need to handle | ||
# conversion | ||
def int64conv(value: Optional[str]) -> Optional[int]: | ||
if value is not None: | ||
return int(value) | ||
return value | ||
if value is None: | ||
return value | ||
return int(value) | ||
Comment on lines
-63
to
+61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No functional changes here. This one (and the next right below for |
||
|
||
|
||
def timestampconv(value: Optional[str]) -> Optional[datetime]: | ||
if value is not None: | ||
return dateutil.parser.parse(value) | ||
return value | ||
if value is None: | ||
return value | ||
return dateutil.parser.parse(value) | ||
|
||
|
||
def datetime_json_handler(x: Any) -> str: | ||
|
@@ -294,7 +290,7 @@ def is_valid(self) -> Tuple[bool, Union[None, TypeError]]: | |
for e in self.executors: | ||
if e.image is None: | ||
errs.append("Executor image must be provided") | ||
if len(e.command) == 0: | ||
if e.command is None or len(e.command) == 0: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this additional check is necessary, or otherwise an error with traceback is thrown when the user/client passes |
||
errs.append("Executor command must be provided") | ||
if e.stdin is not None: | ||
if not os.path.isabs(e.stdin): | ||
|
@@ -306,8 +302,8 @@ def is_valid(self) -> Tuple[bool, Union[None, TypeError]]: | |
if not os.path.isabs(e.stderr): | ||
errs.append("Executor stderr must be an absolute path") | ||
if e.env is not None: | ||
for k, v in e.env: | ||
if not isinstance(k, str) and not isinstance(k, str): | ||
Comment on lines
-309
to
-310
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Obviously the check should be for both the key and the value. And Python3 requires |
||
for k, v in e.env.items(): | ||
if not isinstance(k, str) and not isinstance(v, str): | ||
errs.append( | ||
"Executor env keys and values must be StrType" | ||
) | ||
|
@@ -339,7 +335,7 @@ def is_valid(self) -> Tuple[bool, Union[None, TypeError]]: | |
errs.append("Volume paths must be absolute") | ||
|
||
if self.tags is not None: | ||
for k, v in self.tags: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Python3 requires |
||
for k, v in self.tags.items(): | ||
if not isinstance(k, str) and not isinstance(k, str): | ||
errs.append( | ||
"Tag keys and values must be StrType" | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -27,14 +27,20 @@ def __init__(self, *args, **kwargs): | |||||||||||||||||||
|
||||||||||||||||||||
|
||||||||||||||||||||
def unmarshal(j: Any, o: Type, convert_camel_case=True) -> Any: | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm reviewing the Python docs now, but what are the use cases for a variable having type There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I admit that I have added the type hints in one of the last PRs rather quickly, mostly aiming for completion and making the VS Code Pyright extension shut up (and possibly to make it easier to include static code analysis via
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So to answer your question: If I called the function with an object that is not a class, but, e.g., an instance of a class, tools like >>> type(str)
<class 'type'>
>>> type("some_string")
<class 'str'> There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's really interesting! I haven't encountered the |
||||||||||||||||||||
m: Any = None | ||||||||||||||||||||
if isinstance(j, str): | ||||||||||||||||||||
m = json.loads(j) | ||||||||||||||||||||
elif isinstance(j, dict): | ||||||||||||||||||||
m = j | ||||||||||||||||||||
try: | ||||||||||||||||||||
m = json.loads(j) | ||||||||||||||||||||
except json.decoder.JSONDecodeError: | ||||||||||||||||||||
pass | ||||||||||||||||||||
elif j is None: | ||||||||||||||||||||
return None | ||||||||||||||||||||
else: | ||||||||||||||||||||
raise TypeError("j must be a str, a dict or None") | ||||||||||||||||||||
m = j | ||||||||||||||||||||
|
||||||||||||||||||||
if not isinstance(m, dict): | ||||||||||||||||||||
raise TypeError("j must be a dictionary, a JSON string evaluation to " | ||||||||||||||||||||
"a dictionary, or None") | ||||||||||||||||||||
Comment on lines
+30
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Previously the behavior of So here I am ignoring decode parsers but then check that we are really dealing with a dictionary later on, and if not, give a single specific message of what input is expected here (dict, JSON string evaluating to dict, or |
||||||||||||||||||||
|
||||||||||||||||||||
d: Dict[str, Any] = {} | ||||||||||||||||||||
if convert_camel_case: | ||||||||||||||||||||
|
@@ -77,16 +83,8 @@ def _unmarshal(v: Any, obj: Type) -> Any: | |||||||||||||||||||
field = v | ||||||||||||||||||||
omap = fullOmap.get(o.__name__, {}) | ||||||||||||||||||||
if k in omap: | ||||||||||||||||||||
if isinstance(omap[k], tuple): | ||||||||||||||||||||
try: | ||||||||||||||||||||
obj = omap[k][0] | ||||||||||||||||||||
field = _unmarshal(v, obj) | ||||||||||||||||||||
except Exception: | ||||||||||||||||||||
obj = omap[k][1] | ||||||||||||||||||||
field = _unmarshal(v, obj) | ||||||||||||||||||||
Comment on lines
-80
to
-86
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have unmarshalled to every TES object and this code never ran. Also the model definitions do not contain any tuples. Maybe this was added to support some legacy model, but I'm pretty sure the code is not used with the current models. Anyway, instead of trying to create some artificial class to use with the Or maybe I missed something? Do you know why these tuple conditionals made it here in the first place? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My guess is that the tuple conditionals were added when Lines 35 to 43 in 8f43a45
Agreed on the removal as it's no longer necessary. |
||||||||||||||||||||
else: | ||||||||||||||||||||
obj = omap[k] | ||||||||||||||||||||
field = _unmarshal(v, obj) | ||||||||||||||||||||
obj = omap[k] | ||||||||||||||||||||
field = _unmarshal(v, obj) | ||||||||||||||||||||
r[k] = field | ||||||||||||||||||||
|
||||||||||||||||||||
try: | ||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,5 @@ coverage>=6.5.0 | |
coveralls>=3.3.1 | ||
flake8>=5.0.4 | ||
pytest>=7.2.1 | ||
pytest-cov>=4.0.0 | ||
requests_mock>=1.10.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
--cov-fail-under=99
condition is a great addition. Awesome to see and something I'll use from now on in other pytest projects!There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I've also only found out about it only now, when I replaced the old test runner
nose
(which had an equivalent option that was used in the previous CI version) withpytest
. Note that it requires thepytest-cov
extension, though.