Skip to content

Commit

Permalink
[cdd/tests/test_{compound,json_schema,shared,sqlalchemy}] Increase te…
Browse files Browse the repository at this point in the history
…st coverage ; [cdd/__init__.py] Bump version
  • Loading branch information
SamuelMarks committed Mar 17, 2024
1 parent 41cd670 commit d21aea2
Show file tree
Hide file tree
Showing 11 changed files with 140 additions and 88 deletions.
2 changes: 1 addition & 1 deletion cdd/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from logging import getLogger as get_logger

__author__ = "Samuel Marks" # type: str
__version__ = "0.0.99rc45" # type: str
__version__ = "0.0.99rc46" # type: str
__description__ = (
"Open API to/fro routes, models, and tests. "
"Convert between docstrings, classes, methods, argparse, pydantic, and SQLalchemy."
Expand Down
7 changes: 4 additions & 3 deletions cdd/compound/gen_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ def gen_module(
:type no_word_wrap: ```Optional[Literal[True]]```
:param imports: Import to preclude in Python file
:type imports: ```str```
:type imports: ```Optional[str]```
:param functions_and_classes: Functions and classes that have been preparsed
:type functions_and_classes: ```Optional[Tuple[AST]]```
Expand Down Expand Up @@ -466,8 +466,9 @@ def file_to_input_mapping(filepath, parse_name):

__all__ = [
"file_to_input_mapping",
"get_input_mapping_from_path",
"get_emit_kwarg",
"gen_file",
"gen_module",
"get_emit_kwarg",
"get_input_mapping_from_path",
"get_parser",
]
1 change: 1 addition & 0 deletions cdd/json_schema/utils/parse_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ def transform_ref_fk_set(ref, foreign_key):
_param.pop("anyOf"),
)
)

if len(_param["typ"]) > 1 and "string" in _param["typ"]:
del _param["typ"][_param["typ"].index("string")]
_param["typ"] = (
Expand Down
25 changes: 0 additions & 25 deletions cdd/shared/defaults_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,31 +187,6 @@ def extract_default(
)


def type_default_from_default(default, typ):
"""
:param default: The default value; potentially `NoneStr`
:type default: ```str```
:param typ: The type of the parameter
:type typ: ```str```
:return: (Optional[typ], None) if default is NoneStr else (typ, default)
:rtype: ```tuple[Optional[str], str]```
"""
return (
(
(
typ
if typ == "None"
else "Optional[{typ}]".format(typ=typ) if typ != "None" else typ
),
None,
)
if default is NoneStr
else (typ, default)
)


def _parse_out_default_and_doc(
_start_idx,
start_rest_offset,
Expand Down
52 changes: 3 additions & 49 deletions cdd/sqlalchemy/utils/parse_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
Utility functions for `cdd.parse.sqlalchemy`
"""

import ast
from ast import Assign, Call, ClassDef, Constant, Load, Module, Name
from itertools import chain, filterfalse
from operator import attrgetter
from ast import Assign, Call, ClassDef, Constant, Load, Name
from itertools import chain

import cdd.shared.ast_utils
import cdd.shared.source_transformer
Expand Down Expand Up @@ -146,9 +144,7 @@ def column_parse_arg(idx_arg):

val = cdd.shared.ast_utils.get_value(arg)
assert val != arg, "Unable to parse {!r}".format(arg)
if idx == 0:
return None # Column name
return None, val
return None if idx == 0 else (None, val)


def column_parse_kwarg(key_word):
Expand Down Expand Up @@ -289,48 +285,6 @@ def concat_with_whitespace(a, b):
return indent_all_but_first(res, indent_level=1, sep=tab)


def infer_imports_from_sqlalchemy(sqlalchemy_class_or_assigns):
"""
Infer imports from SQLalchemy ast
:param sqlalchemy_class_or_assigns: SQLalchemy Class or Assign
:type sqlalchemy_class_or_assigns: ```Union[ClassDef, Assign]```
:return: filter of imports (can be considered ```Iterable[str]```)
:rtype: ```filter```
"""
candidates = frozenset(
map(
attrgetter("id"),
filter(
rpartial(isinstance, Name),
ast.walk(
Module(
body=list(
filter(
rpartial(isinstance, Call),
ast.walk(sqlalchemy_class_or_assigns),
)
),
type_ignores=[],
stmt=None,
)
),
),
)
)

candidates_not_in_valid_types = frozenset(
filterfalse(
frozenset(
("list", "string", "int", "float", "complex", "long")
).__contains__,
filterfalse(sqlalchemy_top_level_imports.__contains__, candidates),
)
)
return candidates_not_in_valid_types ^ candidates


def get_pk_and_type(sqlalchemy_class):
"""
Get the primary key and its type from an SQLalchemy class
Expand Down
17 changes: 14 additions & 3 deletions cdd/tests/test_compound/test_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,19 +340,30 @@ def test_gen_json_schema_input_mapping(self) -> None:
with patch(
"cdd.json_schema.emit.json_schema_file", new_callable=MagicMock()
) as json_schema_file_mock, TemporaryDirectory() as tempdir:
json_schema_file = os.path.join(tempdir, "foo.json")
json_schema_file = os.path.join(tempdir, "foo{}json".format(os.path.extsep))
with open(json_schema_file, "wt") as f:
dump(server_error_schema, f)
# file
gen(
"{name}",
input_mapping=json_schema_file,
parse_name="json_schema",
emit_name="json_schema",
output_filename=os.path.join(
tempdir, "foo.gen{}json".format(os.path.extsep)
tempdir, "foo.gen0{}json".format(os.path.extsep)
),
)
self.assertEqual(json_schema_file_mock.call_count, 1)
# directory
gen(
"{name}",
input_mapping=tempdir,
parse_name="json_schema",
emit_name="json_schema",
output_filename=os.path.join(
tempdir, "foo.gen1{}json".format(os.path.extsep)
),
)
self.assertEqual(json_schema_file_mock.call_count, 2)


unittest_main()
Expand Down
67 changes: 65 additions & 2 deletions cdd/tests/test_compound/test_gen_utils.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
""" Tests for gen_utils """

from ast import Assign, List, Load, Module, Name, Store
from os import path
from tempfile import TemporaryDirectory
from unittest import TestCase

from cdd.compound.gen_utils import get_input_mapping_from_path
from cdd.tests.utils_for_tests import unittest_main
import cdd.shared.source_transformer
from cdd.compound.gen_utils import (
file_to_input_mapping,
gen_module,
get_input_mapping_from_path,
)
from cdd.tests.mocks.classes import class_ast
from cdd.tests.utils_for_tests import run_ast_test, unittest_main


def f(s):
Expand All @@ -17,6 +26,17 @@ def f(s):
class TestGenUtils(TestCase):
"""Test class for cdd.gen_utils"""

def test_file_to_input_mapping_else_condition(self) -> None:
"""Test that `file_to_input_mapping` else condition works"""
with TemporaryDirectory() as temp_dir:
filename: str = path.join(temp_dir, "foo{}py".format(path.extsep))
with open(filename, "wt") as f:
f.write(cdd.shared.source_transformer.to_code(class_ast))
input_mapping = file_to_input_mapping(filename, "infer")
self.assertEqual(len(input_mapping.keys()), 1)
self.assertIn(class_ast.name, input_mapping)
run_ast_test(self, input_mapping[class_ast.name], class_ast)

def test_get_input_mapping_from_path(self) -> None:
"""test `get_input_mapping_from_path`"""
self.assertEqual(f(""), "")
Expand All @@ -27,5 +47,48 @@ def test_get_input_mapping_from_path(self) -> None:
self.assertIn("f", name_to_node)
self.assertIsInstance(name_to_node["f"], dict)

def test_gen_module_when_emit_and_infer_imports(self) -> None:
"""
Tests that `emit_and_infer_imports` works when `emit_and_infer_imports` is True
"""
run_ast_test(
self,
gen_module(
decorator_list=[],
emit_and_infer_imports=True,
emit_call=False,
emit_default_doc=False,
emit_name="class",
functions_and_classes=None,
imports=None,
input_mapping_it={},
name_tpl="{name}Foo",
no_word_wrap=True,
parse_name="class",
prepend=None,
),
Module(
body=[
Assign(
targets=[
Name(
id="__all__", ctx=Store(), lineno=None, col_offset=None
)
],
value=List(
elts=[],
ctx=Load(),
expr=None,
),
type_comment=None,
expr=None,
lineno=None,
)
],
stmt=None,
type_ignores=[],
),
)


unittest_main()
6 changes: 6 additions & 0 deletions cdd/tests/test_json_schema/test_parse_json_schema_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,11 @@ def test_json_schema_property_to_param_default_none(self) -> None:
self.assertEqual(res[0], mock[0])
self.assertDictEqual(res[1], mock[1])

def test_json_schema_property_to_param_removes_string_from_anyOf(self) -> None:
"""Tests that `json_schema_property_to_param` removes `string` from `anyOf`"""
param = ("foo", {"anyOf": ["string", "can"], "typ": ["string", "can", "haz"]})
cdd.json_schema.utils.parse_utils.json_schema_property_to_param(param, {})
self.assertDictEqual(param[1], {"typ": "Optional[can]"})


unittest_main()
21 changes: 17 additions & 4 deletions cdd/tests/test_shared/test_pure_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
multiline,
namespaced_pascal_to_upper_camelcase,
namespaced_upper_camelcase_to_pascal,
pairwise,
parse_comment_from_line,
pluralise,
pp,
Expand Down Expand Up @@ -185,7 +186,7 @@ def test_multiline(self) -> None:
),
)

def test_(self) -> None:
def test_parse_comment_from_line(self) -> None:
"""Tests that parses the comment out of the line"""
for output_val, input_val in (
("foo", "foo#bar"),
Expand All @@ -197,6 +198,7 @@ def test_(self) -> None:
("foo = 'ba#r'", "foo = 'ba#r'"),
("foo =", "foo = #'foo'"),
("source = ndb.TextProperty()", "source = ndb.TextProperty()"),
("bar\\'", "bar" "\\" "'"),
):
self.assertEqual(output_val, parse_comment_from_line(input_val))

Expand All @@ -220,17 +222,21 @@ def test_pluralises(self) -> None:

def test_append_to_dict(self) -> None:
"""Tests `append_to_dict`"""
self.assertEqual(
self.assertDictEqual(
append_to_dict({"a": {"b": {}}}, ["a", "b", "c"], "d"),
{"a": {"b": {"c": "d"}}},
)
self.assertEqual(
self.assertDictEqual(
append_to_dict({"a": {"b": 2}}, ["a", "b", "c"], "d"), {"a": {"b": 2}}
)
self.assertEqual(
self.assertDictEqual(
append_to_dict({"a": {"b": {"c": {}}}}, ["a", "b", "c"], "d"),
{"a": {"b": {"c": "d"}}},
)
self.assertDictEqual(
append_to_dict({}, [], None),
{},
)

def test_remove_whitespace_comments(self) -> None:
"""Tests `remove_whitespace_comments` actually removes whitespace and comments from Python source"""
Expand Down Expand Up @@ -344,5 +350,12 @@ def test_namespaced_pascal_to_upper_camelcase(self) -> None:
namespaced_pascal_to_upper_camelcase("foo_bar_can"), "FooBarCan"
)

def test_pairwise(self) -> None:
"""Tests that (potentially proxy) `pairwise` implementation works"""
self.assertTupleEqual(
tuple(pairwise("ABCDEFG")),
(("A", "B"), ("B", "C"), ("C", "D"), ("D", "E"), ("E", "F"), ("F", "G")),
)


unittest_main()
14 changes: 14 additions & 0 deletions cdd/tests/test_sqlalchemy/test_emit_sqlalchemy_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,20 @@ def test_update_args_infer_typ_sqlalchemy_when_simple_array_in_typ(self) -> None
# gold=Name(id="Small", ctx=Load(), lineno=None, col_offset=None),
# )

def test_update_args_infer_typ_sqlalchemy_calls__handle_union_of_length_2(
self,
) -> None:
"""Tests that `update_args_infer_typ_sqlalchemy` calls `_handle_union_of_length_2`"""
args = []
with patch(
"cdd.sqlalchemy.utils.shared_utils._handle_union_of_length_2", lambda _: 5
):
update_args_infer_typ_sqlalchemy(
{"typ": "Union[string, Small]"}, args, "", False, {}
)
self.assertEqual(len(args), 1)
self.assertListEqual(args, [5])

def test_update_args_infer_typ_sqlalchemy_early_exit(self) -> None:
"""Tests that `update_args_infer_typ_sqlalchemy` exits early"""
_update_args_infer_typ_sqlalchemy: Callable[
Expand Down
16 changes: 15 additions & 1 deletion cdd/tests/test_sqlalchemy/test_parse_sqlalchemy_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Tests for the utils that is used by the SQLalchemy parsers
"""

from ast import ClassDef, keyword
from ast import Call, ClassDef, Load, Name, keyword
from copy import deepcopy
from unittest import TestCase

Expand Down Expand Up @@ -52,6 +52,20 @@ def test_column_call_to_param_pk(self) -> None:
self.assertEqual(gold_name, gen_name)
self.assertDictEqual(gold_param, gen_param)

def test_column_call_name_manipulator_remove_op(self) -> None:
"""
Tests that `column_call_name_manipulator` runs remove op
"""
call: Call = Call(
func=Name(id="Column", ctx=Load(), lineno=None, col_offset=None),
args=[set_value("foo")],
keywords=[],
lineno=None,
col_offset=None,
)
column_call_name_manipulator(call, "remove")
self.assertListEqual(call.args, [])

def test_column_call_to_param_server_default(self) -> None:
"""
Tests that `parse.sqlalchemy.utils.column_call_to_param` works with server_default
Expand Down

0 comments on commit d21aea2

Please sign in to comment.