Skip to content
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,11 @@ See the [tests](/tests/test_py_d2) for more detailed usage examples.
- [x] Markdown / block strings / code in shapes
- [x] Icons in shapes
- [x] Support for empty labels
- [x] Shape links
- [ ] SQL table shapes
- [ ] Class shapes
- [ ] Comments
- [ ] Layers


## Development
Expand Down
12 changes: 11 additions & 1 deletion src/py_d2/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
from .connection import D2Connection
from .connection import Direction
from .diagram import D2Diagram
from .diagram import Layer
from .shape import D2Shape
from .shape import D2Text
from .shape import Shape
from .style import D2Style


__all__ = ["Direction", "D2Connection", "D2Diagram", "D2Shape", "D2Text", "D2Style", "Shape"]
__all__ = [
"Direction",
"D2Connection",
"D2Diagram",
"D2Shape",
"D2Text",
"D2Style",
"Shape",
"Layer",
]
54 changes: 52 additions & 2 deletions src/py_d2/diagram.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,78 @@
# -*- coding: utf-8 -*-
from __future__ import annotations

from typing import List
from typing import Optional

from py_d2.connection import D2Connection
from py_d2.helpers import indent
from py_d2.helpers import indent_lines
from py_d2.shape import D2Shape


class Layer:
def __init__(
self,
name: str,
diagram: Optional["D2Diagram"] = None,
):
self.name = name
self.diagram = diagram or D2Diagram()

def set_diagram(self, diagram: "D2Diagram"):
self.diagram = diagram

def lines(self, depth=1) -> List[str]:
lines = self.diagram.lines()

if len(lines) == 0:
return []

outer_indent_size = depth * 2
inner_indent_size = outer_indent_size + 2

# Wrap lines with layer { } and add indentation
wrapped_lines = [
indent(f"{self.name}: {{", outer_indent_size),
*indent_lines(lines, inner_indent_size),
indent("}", outer_indent_size),
]

return wrapped_lines

def __repr__(self) -> str:
lines = self.lines()
return "\n".join(lines)


class D2Diagram:
def __init__(
self,
shapes: Optional[List[D2Shape]] = None,
connections: Optional[List[D2Connection]] = None,
layers: Optional[List[Layer]] = None,
):
self.shapes = shapes or []
self.connections = connections or []
self.layers = layers or []

def add_shape(self, shape: D2Shape):
self.shapes.append(shape)

def add_connection(self, connection: D2Connection):
self.connections.append(connection)

def __repr__(self) -> str:
def add_layer(self, layer: Layer):
self.layers.append(layer)

def lines(self) -> List[str]:
shapes = [str(shape) for shape in self.shapes]
connections = [str(connection) for connection in self.connections]
layers = [line for layer in self.layers for line in layer.lines() if layer.lines()]
layers = ["layers: {"] + layers + ["}"] if layers else []

return "\n".join(shapes + connections)
return shapes + connections + layers

def __repr__(self) -> str:
lines = self.lines()
return "\n".join(lines)
10 changes: 7 additions & 3 deletions src/py_d2/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
from typing import Optional


def indent(items: List[str], n: int = 2) -> List[str]:
return [f"{' '*n}{item}" for item in items]
def indent(line, n: int = 2) -> str:
return f"{' '*n}{line}"


def indent_lines(items: List[str], n: int = 2) -> List[str]:
return [indent(item, n) for item in items]


def add_label_and_properties(
Expand All @@ -26,7 +30,7 @@ def add_label_and_properties(
first_line += " {"

if properties and has_properties:
return [first_line, *indent(properties), "}"]
return [first_line, *indent_lines(properties), "}"]

return [first_line]

Expand Down
10 changes: 8 additions & 2 deletions src/py_d2/shape.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from py_d2.connection import D2Connection
from py_d2.helpers import add_label_and_properties
from py_d2.helpers import flatten
from py_d2.helpers import indent
from py_d2.helpers import indent_lines
from py_d2.style import D2Style


Expand Down Expand Up @@ -78,6 +78,8 @@ def __init__(
connections: Optional[List[D2Connection]] = None,
# A shape this is near
near: Optional[str] = None,
# A link for a shape (when clicked)
link: Optional[str] = None,
**kwargs: D2Text,
):
self.name = name
Expand All @@ -88,6 +90,7 @@ def __init__(
self.icon = icon
self.connections = connections or []
self.near = near
self.link = link
self.kwargs = kwargs

def add_shape(self, shape: D2Shape):
Expand All @@ -107,6 +110,9 @@ def lines(self) -> List[str]:
if self.near:
properties.append(f"near: {self.near}")

if self.link:
properties.append(f"link: {self.link}")

if self.style:
properties += self.style.lines()

Expand All @@ -120,7 +126,7 @@ def lines(self) -> List[str]:
other_property_line_end = other_property[-1]
properties += [
f"{key}: {other_property_line_1}",
*indent(other_property_lines_other),
*indent_lines(other_property_lines_other),
other_property_line_end,
]

Expand Down
67 changes: 67 additions & 0 deletions tests/test_py_d2/test_d2_layer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
from py_d2.connection import D2Connection
from py_d2.diagram import D2Diagram
from py_d2.diagram import Layer


def test_d2_layer():
layer = Layer(name="my_layer")
diagram = D2Diagram(layers=[layer])
assert str(diagram) == ""


def test_d2_layer_single_subdiagram():
"""Test a root diagram with a connection and a single layer containing its own connection."""
root_connection = D2Connection(shape_1="x", shape_2="y")

layer_connection = D2Connection(shape_1="1", shape_2="2")
layer_diagram = D2Diagram(connections=[layer_connection])

layer = Layer(name="numbers", diagram=layer_diagram)

root_diagram = D2Diagram(connections=[root_connection], layers=[layer])

expected_output = "x -> y\n" "layers: {\n" " numbers: {\n" " 1 -> 2\n" " }\n" "}"

assert str(root_diagram) == expected_output


def test_d2_layer_two_layers_depth_1():
"""Test diagram with two layers at depth 1"""
connection1 = D2Connection(shape_1="a", shape_2="b")
layer1_diagram = D2Diagram(connections=[connection1])
layer1 = Layer(name="layer1", diagram=layer1_diagram)

connection2 = D2Connection(shape_1="c", shape_2="d")
layer2_diagram = D2Diagram(connections=[connection2])
layer2 = Layer(name="layer2", diagram=layer2_diagram)

root_diagram = D2Diagram(layers=[layer1, layer2])
expected_output = "layers: {\n" " layer1: {\n" " a -> b\n" " }\n" " layer2: {\n" " c -> d\n" " }\n" "}"
assert str(root_diagram) == expected_output


def test_d2_layer_nested_layer_depth_2():
"""Test diagram with a layer nested inside another layer (depth 2)"""
inner_connection = D2Connection(shape_1="e", shape_2="f")
inner_diagram = D2Diagram(connections=[inner_connection])
inner_layer = Layer(name="inner_layer", diagram=inner_diagram)

outer_connection = D2Connection(shape_1="g", shape_2="h")
outer_diagram = D2Diagram(connections=[outer_connection], layers=[inner_layer])
outer_layer = Layer(name="outer_layer", diagram=outer_diagram)

root_diagram = D2Diagram(layers=[outer_layer])
expected_output = (
"layers: {\n"
" outer_layer: {\n"
" g -> h\n"
" layers: {\n"
" inner_layer: {\n"
" e -> f\n"
" }\n"
" }\n"
" }\n"
"}"
)
assert str(root_diagram) == expected_output
5 changes: 5 additions & 0 deletions tests/test_py_d2/test_d2_shape.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ def test_d2_shape_near():
assert str(shape) == "shape_name: {\n near: some_other_shape\n}"


def test_d2_shape_link():
shape = D2Shape(name="shape_name", link="https://github.com/MrBlenny/py-d2")
assert str(shape) == "shape_name: {\n link: https://github.com/MrBlenny/py-d2\n}"


def test_d2_shape_other_properties():
text = "Some text"
shape = D2Shape(name="shape_name", thing=D2Text(text=text, formatting="md"))
Expand Down