diff --git a/README.md b/README.md index 5adc36f..1f822f7 100644 --- a/README.md +++ b/README.md @@ -568,7 +568,11 @@ Arguments: * **Example**: `--code-generator-kwargs kwarg1=true kwarg2=10 "kwarg3=It is string with spaces"` * **Optional** -One of model arguments (`-m` or `-l`) is required. +* `--disable-str-serializable-types` - List of python types for which StringSerializable should be disabled. + Alternatively you could use the name of StringSerializable subclass itself (i.e. IntString). + * **Format**: `--disable-str-serializable-types [TYPE [TYPE ...]]` + * **Example**: `--disable-str-serializable-types float int BooleanString IsoDatetimeString` + * **Optional** ### Low level API diff --git a/json_to_models/cli.py b/json_to_models/cli.py index 05d90a1..806fe6a 100644 --- a/json_to_models/cli.py +++ b/json_to_models/cli.py @@ -20,7 +20,7 @@ yaml = None from . import __version__ as VERSION -from .dynamic_typing import ModelMeta, register_datetime_classes +from .dynamic_typing import ModelMeta, register_datetime_classes, registry from .generator import MetadataGenerator from .models import ModelsStructureType from .models.attr import AttrsModelCodeGenerator @@ -96,6 +96,9 @@ def parse_args(self, args: List[str] = None): dict_keys_fields: List[str] = namespace.dict_keys_fields preamble: str = namespace.preamble + for name in namespace.disable_str_serializable_types: + registry.remove_by_name(name) + self.setup_models_data(namespace.model or (), namespace.list or (), parser) self.validate(merge_policy, framework, code_generator) self.set_args(merge_policy, structure, framework, code_generator, code_generator_kwargs_raw, @@ -367,6 +370,16 @@ def _create_argparser(cls) -> argparse.ArgumentParser: type=str, help="Code to insert into the generated file after the imports and before the list of classes\n\n" ) + parser.add_argument( + "--disable-str-serializable-types", + metavar="TYPE", + default=[], + nargs="*", type=str, + help="List of python types for which StringSerializable should be disabled, i.e:\n" + "--disable-str-serializable-types float int\n" + "Alternatively you could use the name of StringSerializable subclass itself (i.e. IntString)" + "\n\n" + ) parser.add_argument( "-l", "--list", nargs=3, action="append", metavar=("", "", ""), diff --git a/json_to_models/dynamic_typing/string_serializable.py b/json_to_models/dynamic_typing/string_serializable.py index e86a589..6f42bc0 100644 --- a/json_to_models/dynamic_typing/string_serializable.py +++ b/json_to_models/dynamic_typing/string_serializable.py @@ -86,7 +86,7 @@ def decorator(cls): return decorator - def remove(self, cls: type): + def remove(self, cls: T_StringSerializable): """ Unregister given class @@ -97,6 +97,11 @@ def remove(self, cls: type): if replace is cls or base is cls: self.replaces.remove((base, replace)) + def remove_by_name(self, name: str): + for cls in self.types[:]: + if cls.__name__ == name or cls.actual_type.__name__ == name: + self.remove(cls) + def resolve(self, *types: T_StringSerializable) -> Collection[T_StringSerializable]: """ Return set of StringSerializable classes which can represent all classes from types argument. diff --git a/test/test_cli/test_script.py b/test/test_cli/test_script.py index 0d30d0f..7e2ce3c 100644 --- a/test/test_cli/test_script.py +++ b/test/test_cli/test_script.py @@ -1,5 +1,6 @@ import imp import json +import re import subprocess import sys import tempfile @@ -94,7 +95,7 @@ def test_help(): ] -def execute_test(command, output_file: Path = None, output=None) -> Tuple[str, str]: +def execute_test(command, output_file: Path = None, output=None) -> str: proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = map(bytes.decode, proc.communicate()) if output_file: @@ -172,7 +173,6 @@ def test_script_custom(command): @pytest.mark.parametrize("command", test_commands) def test_add_preamble(command): - PREAMBLE_TEXT = """ # this is some test code # to be added to the file @@ -187,8 +187,13 @@ def test_add_preamble(command): @pytest.mark.parametrize("command", test_commands) -def test_add_trim_preamble(command): +def test_disable_some_string_types_smoke(command): + command += " --disable-str-serializable-types float int" + execute_test(command) + +@pytest.mark.parametrize("command", test_commands) +def test_add_trim_preamble(command): def trim_header(line_string): """remove the quoted command and everything from the first class declaration onwards""" lines = line_string.splitlines() @@ -247,3 +252,17 @@ def test_script_output_file(command): file = tmp_path / 'out.py' command += f" -o {file}" execute_test(command, output_file=file) + + +cmds = [ + pytest.param(f"""{executable} -m User "{test_data_path / 'users.json'}" -f pydantic --disable-str-serializable-types float int""", + id="users") +] + + +@pytest.mark.parametrize("command", cmds) +def test_disable_some_string_types(command): + stdout = execute_test(command) + assert 'lat: str' in stdout + assert 'lng: str' in stdout + assert not any(re.match(r'\s+zipcode:.+int.+', line) for line in stdout.split('\n')), "zipcode should not be parsed as int"