Skip to content

CLI: Add option to unregister string serializable fields #51

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

Merged
merged 1 commit into from
Nov 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
15 changes: 14 additions & 1 deletion json_to_models/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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=("<Model name>", "<JSON lookup>", "<JSON file>"),
Expand Down
7 changes: 6 additions & 1 deletion json_to_models/dynamic_typing/string_serializable.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def decorator(cls):

return decorator

def remove(self, cls: type):
def remove(self, cls: T_StringSerializable):
"""
Unregister given class

Expand All @@ -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.
Expand Down
25 changes: 22 additions & 3 deletions test/test_cli/test_script.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import imp
import json
import re
import subprocess
import sys
import tempfile
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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()
Expand Down Expand Up @@ -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"