diff --git a/pystructurizr/cli.py b/pystructurizr/cli.py index 7141571..acbd103 100644 --- a/pystructurizr/cli.py +++ b/pystructurizr/cli.py @@ -16,8 +16,11 @@ help='The view file to generate.') @click.option('--as-json', is_flag=True, default=False, help='Dumps the generated code and the imported modules as a json object') -def dump(view, as_json): - diagram_code, imported_modules = generate_diagram_code_in_child_process(view) +@click.option( + "--directives", help="Flag to add extra directives (i.e. !docs) or omit", + is_flag=True, default=False) +def dump(view, as_json, directives: bool): + diagram_code, imported_modules = generate_diagram_code_in_child_process(view, directives) if as_json: print(json.dumps({ "code": diagram_code, @@ -40,7 +43,7 @@ def dev(view): async def async_behavior(): print("Generating diagram...") - diagram_code, imported_modules = generate_diagram_code_in_child_process(view) + diagram_code, imported_modules = generate_diagram_code_in_child_process(view, False) await generate_svg(diagram_code, tmp_folder) return imported_modules @@ -66,7 +69,7 @@ async def observe_loop(): def build(view, gcs_credentials, bucket_name, object_name): async def async_behavior(): # Generate diagram - diagram_code, _ = generate_diagram_code_in_child_process(view) + diagram_code, _ = generate_diagram_code_in_child_process(view, False) tmp_folder = ensure_tmp_folder_exists() # Generate SVG diff --git a/pystructurizr/cli_helper.py b/pystructurizr/cli_helper.py index 89ffcad..956e552 100644 --- a/pystructurizr/cli_helper.py +++ b/pystructurizr/cli_helper.py @@ -8,10 +8,13 @@ import httpx -def generate_diagram_code_in_child_process(view: str) -> tuple[dict, list[str]]: +def generate_diagram_code_in_child_process(view: str, directives: bool) -> tuple[dict, list[str]]: def run_child_process(): # Run a separate Python script as a child process - output = subprocess.check_output([sys.executable, "-m", "pystructurizr.generator", "dump", "--view", view]) + executable = [sys.executable, "-m", "pystructurizr.generator", "dump", "--view", view] + if directives: + executable.append("--directives") + output = subprocess.check_output(executable) return output.decode().strip() # Run the child process and capture its output diff --git a/pystructurizr/dsl.py b/pystructurizr/dsl.py index e0f1e1e..0eeb75b 100644 --- a/pystructurizr/dsl.py +++ b/pystructurizr/dsl.py @@ -1,7 +1,7 @@ import keyword import re from enum import Enum -from typing import Dict, List, Optional +from typing import Dict, List, Optional, Literal # pylint: disable=too-few-public-methods @@ -35,10 +35,15 @@ def make_identifier(name: str) -> str: class Dumper: - def __init__(self): + def __init__(self, with_directives: bool = True): + self.with_directives = with_directives self.level = 0 self.lines = [] + def add_directive(self, type: Literal['!docs', '!adrs'], value: str) -> None: + if self.with_directives: + self.add(f"{type} {value}") + def add(self, txt: str) -> None: self.lines.append(f'{" " * self.level}{txt}') @@ -106,9 +111,10 @@ def dump_relationships(self, dumper: Dumper) -> None: class Container(Element): - def __init__(self, name: str, description: Optional[str]=None, technology: Optional[str]=None, tags: Optional[List[str]]=None): + def __init__(self, name: str, description: Optional[str]=None, technology: Optional[str]=None, tags: Optional[List[str]]=None, docs: str = None): super().__init__(name, description, technology, tags) self.elements = [] + self.docs = docs def __enter__(self): return self @@ -137,6 +143,8 @@ def Group(self, *args, **kwargs) -> "Group": def dump(self, dumper: Dumper) -> None: dumper.add(f'{self.instname} = Container "{self.name}" "{self.description}" {{') dumper.indent() + if self.docs: + dumper.add_directive("!docs", f"{self.docs}") if self.technology: dumper.add(f'technology "{self.technology}"') if self.tags: @@ -154,9 +162,10 @@ def dump_relationships(self, dumper: Dumper) -> None: class SoftwareSystem(Element): - def __init__(self, name: str, description: Optional[str]=None, technology: Optional[str]=None, tags: Optional[List[str]]=None): + def __init__(self, name: str, description: Optional[str]=None, technology: Optional[str]=None, tags: Optional[List[str]]=None, docs: str = None): super().__init__(name, description, technology, tags) self.elements = [] + self.docs = docs def __enter__(self): return self @@ -185,6 +194,8 @@ def Group(self, *args, **kwargs) -> "Group": def dump(self, dumper: Dumper) -> None: dumper.add(f'{self.instname} = SoftwareSystem "{self.name}" "{self.description}" {{') dumper.indent() + if self.docs: + dumper.add_directive("!docs", f"{self.docs}") if self.technology: dumper.add(f'technology "{self.technology}"') if self.tags: @@ -382,7 +393,7 @@ def dump(self, dumper: Dumper) -> None: class Workspace: - def __init__(self): + def __init__(self, docs: str = None): self.models = [] self.views = [] self.styles = [] @@ -416,6 +427,7 @@ def __init__(self): "shape": "Cylinder" } ) + self.docs = docs def __enter__(self): return self @@ -423,9 +435,11 @@ def __enter__(self): def __exit__(self, exc_type, exc_val, exc_tb): pass - def dump(self, dumper: Dumper = Dumper()) -> None: + def dump(self, dumper: Dumper = Dumper()) -> str: dumper.add('workspace {') dumper.indent() + if self.docs: + dumper.add_directive("!docs", f"{self.docs}") dumper.add('model {') dumper.indent() diff --git a/pystructurizr/generator.py b/pystructurizr/generator.py index f9e633c..e908c50 100644 --- a/pystructurizr/generator.py +++ b/pystructurizr/generator.py @@ -4,16 +4,22 @@ import click +from pystructurizr.dsl import Dumper + @click.command() @click.option('--view', prompt='Your view file (e.g. example.componentview)', help='The view file to generate.') -def dump(view: str): +@click.option( + "--directives", help="Flag to add extra directives (i.e. !docs) or omit", + is_flag=True, default=False) +def dump(view: str, directives: bool): try: initial_modules = set(sys.modules.keys()) module = importlib.import_module(view) imported_modules = set(sys.modules.keys()) - initial_modules - code = module.workspace.dump() + dumper = Dumper(with_directives=directives) + code = module.workspace.dump(dumper=dumper) print(json.dumps({ "code": code, "imported_modules": list(imported_modules)