graphinate.cli._get_kwargs()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 2
rs 10
c 0
b 0
f 0
cc 1
nop 1
1
import importlib
2
import json
3
from pathlib import Path
4
from typing import Any
5
6
import click
7
8
from graphinate import GraphModel, builders, graphql
9
from graphinate.renderers.graphql import DEFAULT_PORT
10
11
12
def _get_kwargs(ctx) -> dict:
13
    return dict([item.strip('--').split('=') for item in ctx.args if item.startswith("--")])  # NOSONAR
14
15
16
def import_from_string(import_str: Any) -> Any:
17
    """Import an object from a string reference {module-name}:{variable-name}
18
    For example, if `model=GraphModel()` is defined in app.py file, then the
19
    reference would be app:model.
20
    """
21
22
    if not isinstance(import_str, str):
23
        return import_str
24
25
    module_str, _, attrs_str = import_str.partition(":")
26
    if not module_str or not attrs_str:
27
        message = f"Import string '{import_str}' must be in format '<module>:<attribute>'."
28
        raise ImportFromStringError(message)
29
30
    try:
31
        module = importlib.import_module(module_str)
32
    except ModuleNotFoundError as exc:
33
        if exc.name != module_str:
34
            raise exc from None
35
        message = f"Could not import module '{module_str}'."
36
        raise ImportFromStringError(message) from exc
37
38
    instance = module
39
    try:
40
        for attr_str in attrs_str.split("."):
41
            instance = getattr(instance, attr_str)
42
    except AttributeError as exc:
43
        message = f"Attribute '{attrs_str}' not found in module '{module_str}'."
44
        raise ImportFromStringError(message) from exc
45
46
    return instance
47
48
49
class ImportFromStringError(Exception):
50
    pass
51
52
53
class GraphModelType(click.ParamType):
54
    name = "MODEL"
55
56
    def convert(self, value, param, ctx) -> GraphModel:
57
        if isinstance(value, GraphModel):
58
            return value
59
60
        try:
61
            return import_from_string(value) if isinstance(value, str) else value
62
        except Exception as e:
63
            self.fail(str(e))
64
65
66
model_option = click.option('-m', '--model',
67
                            type=GraphModelType(),
68
                            help="A GraphModel instance reference {module-name}:{GraphModel-instance-variable-name}"
69
                                 " For example given a var `model=GraphModel()` defined in app.py file, then the"
70
                                 " reference would be app:model")
71
72
73
@click.group()
74
@click.pass_context
75
def cli(ctx):
76
    ctx.ensure_object(dict)
77
78
79
@cli.command()
80
@model_option
81
@click.pass_context
82
def save(ctx, model: GraphModel):
83
    file_path = Path(f"{model.name}.d3_graph.json")
84
85
    if file_path.is_absolute():
86
        raise click.ClickException("Please provide a relative file path for saving the graph.")
87
88
    if file_path.parent != Path('.'):
89
        raise click.ClickException("Saving to subdirectories is not supported. Please provide a file name only.")
90
91
    if file_path.exists():
92
        click.confirm(f"The file '{file_path}' already exists. Do you want to overwrite it?", abort=True)
93
94
    kwargs = _get_kwargs(ctx)
95
    with open(file_path, mode='w') as fp:
96
        graph = builders.D3Builder(model, **kwargs).build()
97
        json.dump(graph, fp=fp, default=str, **kwargs)
98
99
100
@cli.command()
101
@model_option
102
@click.option('-p', '--port', type=int, default=DEFAULT_PORT, help='Port number.')
103
@click.option('-b', '--browse', type=bool, default=False, help='Open server address in browser.')
104
@click.pass_context
105
def server(ctx, model: GraphModel, port: int, browse: bool):
106
    message = """
107
     ██████╗ ██████╗  █████╗ ██████╗ ██╗  ██╗██╗███╗   ██╗ █████╗ ████████╗███████╗
108
    ██╔════╝ ██╔══██╗██╔══██╗██╔══██╗██║  ██║██║████╗  ██║██╔══██╗╚══██╔══╝██╔════╝
109
    ██║  ███╗██████╔╝███████║██████╔╝███████║██║██╔██╗ ██║███████║   ██║   █████╗
110
    ██║   ██║██╔══██╗██╔══██║██╔═══╝ ██╔══██║██║██║╚██╗██║██╔══██║   ██║   ██╔══╝
111
    ╚██████╔╝██║  ██║██║  ██║██║     ██║  ██║██║██║ ╚████║██║  ██║   ██║   ███████╗
112
     ╚═════╝ ╚═╝  ╚═╝╚═╝  ╚═╝╚═╝     ╚═╝  ╚═╝╚═╝╚═╝  ╚═══╝╚═╝  ╚═╝   ╚═╝   ╚══════╝"""
113
    click.echo(message)
114
    schema = builders.GraphQLBuilder(model).build()
115
    graphql.server(schema, port=port, browse=browse, **_get_kwargs(ctx))
116