add pre-commit to verify commit before create PR (#1066)

pull/1065/head
Phuoc Tran 10 months ago committed by GitHub
parent fc425adfb5
commit fa48533af3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,14 @@
name: pre-commit
on:
pull_request:
push:
branches: [main]
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
- uses: pre-commit/action@v3.0.1

@ -0,0 +1,5 @@
[settings]
line_length = 120
multi_line_output = 3
include_trailing_comma = True
known_third_party = graphviz,jinja2

@ -0,0 +1,23 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/asottile/seed-isort-config
rev: v2.2.0
hooks:
- id: seed-isort-config
- repo: https://github.com/pre-commit/mirrors-isort
rev: v5.10.1
hooks:
- id: isort
- repo: https://github.com/hhatto/autopep8
rev: v2.3.1
hooks:
- id: autopep8
args: [--in-place, --aggressive, --aggressive, --max-line-length=120]

@ -139,7 +139,8 @@ class Diagram:
if isinstance(outformat, list): if isinstance(outformat, list):
for one_format in outformat: for one_format in outformat:
if not self._validate_outformat(one_format): if not self._validate_outformat(one_format):
raise ValueError(f'"{one_format}" is not a valid output format') raise ValueError(
f'"{one_format}" is not a valid output format')
else: else:
if not self._validate_outformat(outformat): if not self._validate_outformat(outformat):
raise ValueError(f'"{outformat}" is not a valid output format') raise ValueError(f'"{outformat}" is not a valid output format')
@ -478,7 +479,8 @@ class Edge:
if label: if label:
# Graphviz complaining about using label for edges, so replace it with xlabel. # Graphviz complaining about using label for edges, so replace it with xlabel.
# Update: xlabel option causes the misaligned label position: https://github.com/mingrammer/diagrams/issues/83 # Update: xlabel option causes the misaligned label position:
# https://github.com/mingrammer/diagrams/issues/83
self._attrs["label"] = label self._attrs["label"] = label
if color: if color:
self._attrs["color"] = color self._attrs["color"] = color
@ -490,7 +492,8 @@ class Edge:
"""Implement Self - Node or Edge and Self - [Nodes]""" """Implement Self - Node or Edge and Self - [Nodes]"""
return self.connect(other) return self.connect(other)
def __rsub__(self, other: Union[List["Node"], List["Edge"]]) -> List["Edge"]: def __rsub__(self, other: Union[List["Node"],
List["Edge"]]) -> List["Edge"]:
"""Called for [Nodes] or [Edges] - Self because list don't have __sub__ operators.""" """Called for [Nodes] or [Edges] - Self because list don't have __sub__ operators."""
return self.append(other) return self.append(other)
@ -504,15 +507,23 @@ class Edge:
self.reverse = True self.reverse = True
return self.connect(other) return self.connect(other)
def __rrshift__(self, other: Union[List["Node"], List["Edge"]]) -> List["Edge"]: def __rrshift__(self,
other: Union[List["Node"],
List["Edge"]]) -> List["Edge"]:
"""Called for [Nodes] or [Edges] >> Self because list of Edges don't have __rshift__ operators.""" """Called for [Nodes] or [Edges] >> Self because list of Edges don't have __rshift__ operators."""
return self.append(other, forward=True) return self.append(other, forward=True)
def __rlshift__(self, other: Union[List["Node"], List["Edge"]]) -> List["Edge"]: def __rlshift__(self,
other: Union[List["Node"],
List["Edge"]]) -> List["Edge"]:
"""Called for [Nodes] or [Edges] << Self because list of Edges don't have __lshift__ operators.""" """Called for [Nodes] or [Edges] << Self because list of Edges don't have __lshift__ operators."""
return self.append(other, reverse=True) return self.append(other, reverse=True)
def append(self, other: Union[List["Node"], List["Edge"]], forward=None, reverse=None) -> List["Edge"]: def append(self,
other: Union[List["Node"],
List["Edge"]],
forward=None,
reverse=None) -> List["Edge"]:
result = [] result = []
for o in other: for o in other:
if isinstance(o, Edge): if isinstance(o, Edge):
@ -521,7 +532,12 @@ class Edge:
self._attrs = o.attrs.copy() self._attrs = o.attrs.copy()
result.append(o) result.append(o)
else: else:
result.append(Edge(o, forward=forward, reverse=reverse, **self._attrs)) result.append(
Edge(
o,
forward=forward,
reverse=reverse,
**self._attrs))
return result return result
def connect(self, other: Union["Node", "Edge", List["Node"]]): def connect(self, other: Union["Node", "Edge", List["Node"]]):

@ -4,7 +4,8 @@ A set of nodes and edges to visualize software architecture using the C4 model.
import html import html
import textwrap import textwrap
from diagrams import Cluster, Node, Edge
from diagrams import Cluster, Edge, Node
def _format_node_label(name, key, description): def _format_node_label(name, key, description):
@ -26,7 +27,8 @@ def _format_description(description):
""" """
wrapper = textwrap.TextWrapper(width=40, max_lines=3) wrapper = textwrap.TextWrapper(width=40, max_lines=3)
lines = [html.escape(line) for line in wrapper.wrap(description)] lines = [html.escape(line) for line in wrapper.wrap(description)]
lines += [""] * (3 - len(lines)) # fill up with empty lines so it is always three # fill up with empty lines so it is always three
lines += [""] * (3 - len(lines))
return "<br/>".join(lines) return "<br/>".join(lines)

@ -53,7 +53,7 @@ class Hibernate(_Framework):
class Jhipster(_Framework): class Jhipster(_Framework):
_icon = "jhipster.png" _icon = "jhipster.png"
class Laravel(_Framework): class Laravel(_Framework):

167
poetry.lock generated

@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. # This file is automatically @generated by Poetry 1.6.0 and should not be changed by hand.
[[package]] [[package]]
name = "astroid" name = "astroid"
@ -60,6 +60,17 @@ d = ["aiohttp (>=3.10)"]
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
uvloop = ["uvloop (>=0.15.2)"] uvloop = ["uvloop (>=0.15.2)"]
[[package]]
name = "cfgv"
version = "3.4.0"
description = "Validate configuration and produce human readable error messages."
optional = false
python-versions = ">=3.8"
files = [
{file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
{file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
]
[[package]] [[package]]
name = "click" name = "click"
version = "8.1.7" version = "8.1.7"
@ -100,6 +111,17 @@ files = [
graph = ["objgraph (>=1.7.2)"] graph = ["objgraph (>=1.7.2)"]
profile = ["gprof2dot (>=2022.7.29)"] profile = ["gprof2dot (>=2022.7.29)"]
[[package]]
name = "distlib"
version = "0.3.9"
description = "Distribution utilities"
optional = false
python-versions = "*"
files = [
{file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"},
{file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"},
]
[[package]] [[package]]
name = "exceptiongroup" name = "exceptiongroup"
version = "1.2.2" version = "1.2.2"
@ -114,6 +136,22 @@ files = [
[package.extras] [package.extras]
test = ["pytest (>=6)"] test = ["pytest (>=6)"]
[[package]]
name = "filelock"
version = "3.16.1"
description = "A platform independent file lock."
optional = false
python-versions = ">=3.8"
files = [
{file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"},
{file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"},
]
[package.extras]
docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"]
testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"]
typing = ["typing-extensions (>=4.12.2)"]
[[package]] [[package]]
name = "graphviz" name = "graphviz"
version = "0.20.3" version = "0.20.3"
@ -130,6 +168,20 @@ dev = ["flake8", "pep8-naming", "tox (>=3)", "twine", "wheel"]
docs = ["sphinx (>=5,<7)", "sphinx-autodoc-typehints", "sphinx-rtd-theme"] docs = ["sphinx (>=5,<7)", "sphinx-autodoc-typehints", "sphinx-rtd-theme"]
test = ["coverage", "pytest (>=7,<8.1)", "pytest-cov", "pytest-mock (>=3)"] test = ["coverage", "pytest (>=7,<8.1)", "pytest-cov", "pytest-mock (>=3)"]
[[package]]
name = "identify"
version = "2.6.2"
description = "File identification library for Python"
optional = false
python-versions = ">=3.9"
files = [
{file = "identify-2.6.2-py2.py3-none-any.whl", hash = "sha256:c097384259f49e372f4ea00a19719d95ae27dd5ff0fd77ad630aa891306b82f3"},
{file = "identify-2.6.2.tar.gz", hash = "sha256:fab5c716c24d7a789775228823797296a2994b075fb6080ac83a102772a98cbd"},
]
[package.extras]
license = ["ukkonen"]
[[package]] [[package]]
name = "iniconfig" name = "iniconfig"
version = "2.0.0" version = "2.0.0"
@ -264,6 +316,17 @@ files = [
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
] ]
[[package]]
name = "nodeenv"
version = "1.9.1"
description = "Node.js virtual environment builder"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
{file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"},
{file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"},
]
[[package]] [[package]]
name = "packaging" name = "packaging"
version = "24.2" version = "24.2"
@ -317,6 +380,24 @@ files = [
dev = ["pre-commit", "tox"] dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"] testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "pre-commit"
version = "4.0.1"
description = "A framework for managing and maintaining multi-language pre-commit hooks."
optional = false
python-versions = ">=3.9"
files = [
{file = "pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878"},
{file = "pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2"},
]
[package.dependencies]
cfgv = ">=2.0.0"
identify = ">=1.0.0"
nodeenv = ">=0.11.1"
pyyaml = ">=5.1"
virtualenv = ">=20.10.0"
[[package]] [[package]]
name = "pylint" name = "pylint"
version = "3.3.1" version = "3.3.1"
@ -391,6 +472,68 @@ gendocs = ["pytoolconfig[doc]", "sphinx (>=7.1.2)", "sphinx-autodoc-typehints (>
global = ["platformdirs (>=3.11.0)"] global = ["platformdirs (>=3.11.0)"]
validation = ["pydantic (>=2.5.3)"] validation = ["pydantic (>=2.5.3)"]
[[package]]
name = "pyyaml"
version = "6.0.2"
description = "YAML parser and emitter for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
{file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"},
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"},
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"},
{file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"},
{file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"},
{file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"},
{file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"},
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"},
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"},
{file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"},
{file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"},
{file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"},
{file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"},
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"},
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"},
{file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"},
{file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"},
{file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"},
{file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"},
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"},
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"},
{file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"},
{file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"},
{file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"},
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"},
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"},
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"},
{file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"},
{file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"},
{file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"},
{file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"},
{file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"},
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"},
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"},
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"},
{file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"},
{file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"},
{file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"},
{file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"},
{file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
]
[[package]] [[package]]
name = "rope" name = "rope"
version = "1.13.0" version = "1.13.0"
@ -443,7 +586,27 @@ files = [
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
] ]
[[package]]
name = "virtualenv"
version = "20.27.1"
description = "Virtual Python Environment builder"
optional = false
python-versions = ">=3.8"
files = [
{file = "virtualenv-20.27.1-py3-none-any.whl", hash = "sha256:f11f1b8a29525562925f745563bfd48b189450f61fb34c4f9cc79dd5aa32a1f4"},
{file = "virtualenv-20.27.1.tar.gz", hash = "sha256:142c6be10212543b32c6c45d3d3893dff89112cc588b7d0879ae5a1ec03a47ba"},
]
[package.dependencies]
distlib = ">=0.3.7,<1"
filelock = ">=3.12.2,<4"
platformdirs = ">=3.9.1,<5"
[package.extras]
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.9" python-versions = "^3.9"
content-hash = "1ebb1500c157c5be7de19b391c574e27f8233114ee534595cf3edce4bb8599ed" content-hash = "e1b317dacf2fc493b7e14b7a1990bdfe49adf53e6ef0a575b7dcdfe0dc0d1db5"

@ -17,6 +17,7 @@ python = "^3.9"
graphviz = ">=0.13.2,<0.21.0" graphviz = ">=0.13.2,<0.21.0"
jinja2 = ">=2.10,<4.0" jinja2 = ">=2.10,<4.0"
typed-ast = {version="^1.5.5", markers="python_version<'3.8'"} typed-ast = {version="^1.5.5", markers="python_version<'3.8'"}
pre-commit = "^4.0.1"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
pytest = "^8.2" pytest = "^8.2"

@ -5,7 +5,8 @@ from typing import Iterable
from jinja2 import Environment, FileSystemLoader, Template, exceptions from jinja2 import Environment, FileSystemLoader, Template, exceptions
import config as cfg import config as cfg
from . import app_root_dir, doc_root_dir, resource_dir, template_dir, base_dir
from . import app_root_dir, base_dir, doc_root_dir, resource_dir, template_dir
_usage = "Usage: generate.py <provider>" _usage = "Usage: generate.py <provider>"
@ -42,11 +43,11 @@ def gen_classes(pvd: str, typ: str, paths: Iterable[str]) -> str:
def gen_apidoc(pvd: str, typ_paths: dict) -> str: def gen_apidoc(pvd: str, typ_paths: dict) -> str:
try: try:
default_tmp = cfg.TMPL_APIDOC.split('.') default_tmp = cfg.TMPL_APIDOC.split(".")
tmpl_file = f"{default_tmp[0]}_{pvd}.{default_tmp[1]}" tmpl_file = f"{default_tmp[0]}_{pvd}.{default_tmp[1]}"
tmpl = load_tmpl(tmpl_file) tmpl = load_tmpl(tmpl_file)
except exceptions.TemplateNotFound: except exceptions.TemplateNotFound:
tmpl = load_tmpl(cfg.TMPL_APIDOC) tmpl = load_tmpl(cfg.TMPL_APIDOC)
# TODO: remove # TODO: remove
def _gen_class_name(path: str) -> str: def _gen_class_name(path: str) -> str:
@ -61,7 +62,8 @@ def gen_apidoc(pvd: str, typ_paths: dict) -> str:
name = _gen_class_name(path) name = _gen_class_name(path)
resource_path = os.path.join(resource_root, path) resource_path = os.path.join(resource_root, path)
alias = cfg.ALIASES[pvd].get(typ, {}).get(name) alias = cfg.ALIASES[pvd].get(typ, {}).get(name)
typ_classes[typ].append({"name": name, "alias": alias, "resource_path": resource_path}) typ_classes[typ].append(
{"name": name, "alias": alias, "resource_path": resource_path})
return tmpl.render(pvd=pvd, typ_classes=typ_classes) return tmpl.render(pvd=pvd, typ_classes=typ_classes)

@ -11,6 +11,7 @@ import subprocess
import sys import sys
import config as cfg import config as cfg
from . import resource_dir from . import resource_dir
_usage = "Usage: resource.py <cmd> <pvd>" _usage = "Usage: resource.py <cmd> <pvd>"
@ -30,7 +31,7 @@ def cleaner_aws(f):
f = f.replace("-light-bg", "") f = f.replace("-light-bg", "")
for p in cfg.FILE_PREFIXES["aws"]: for p in cfg.FILE_PREFIXES["aws"]:
if f.startswith(p): if f.startswith(p):
f = f[len(p) :] f = f[len(p):]
break break
return f.lower() return f.lower()
@ -41,7 +42,7 @@ def cleaner_azure(f):
f = "-".join(f.split()) f = "-".join(f.split())
for p in cfg.FILE_PREFIXES["azure"]: for p in cfg.FILE_PREFIXES["azure"]:
if f.startswith(p): if f.startswith(p):
f = f[len(p) :] f = f[len(p):]
break break
return f.lower() return f.lower()
@ -51,7 +52,7 @@ def cleaner_gcp(f):
f = "-".join(f.split()) f = "-".join(f.split())
for p in cfg.FILE_PREFIXES["gcp"]: for p in cfg.FILE_PREFIXES["gcp"]:
if f.startswith(p): if f.startswith(p):
f = f[len(p) :] f = f[len(p):]
break break
return f.lower() return f.lower()
@ -61,7 +62,7 @@ def cleaner_ibm(f):
f = "-".join(f.split()) f = "-".join(f.split())
for p in cfg.FILE_PREFIXES["ibm"]: for p in cfg.FILE_PREFIXES["ibm"]:
if f.startswith(p): if f.startswith(p):
f = f[len(p) :] f = f[len(p):]
break break
return f.lower() return f.lower()
@ -71,7 +72,7 @@ def cleaner_firebase(f):
f = "-".join(f.split()) f = "-".join(f.split())
for p in cfg.FILE_PREFIXES["firebase"]: for p in cfg.FILE_PREFIXES["firebase"]:
if f.startswith(p): if f.startswith(p):
f = f[len(p) :] f = f[len(p):]
break break
return f.lower() return f.lower()
@ -80,15 +81,16 @@ def cleaner_k8s(f):
f = f.replace("-256", "") f = f.replace("-256", "")
for p in cfg.FILE_PREFIXES["k8s"]: for p in cfg.FILE_PREFIXES["k8s"]:
if f.startswith(p): if f.startswith(p):
f = f[len(p) :] f = f[len(p):]
break break
return f.lower() return f.lower()
def cleaner_digitalocean(f): def cleaner_digitalocean(f):
f = f.replace("-32", "") f = f.replace("-32", "")
for p in cfg.FILE_PREFIXES["digitalocean"]: for p in cfg.FILE_PREFIXES["digitalocean"]:
if f.startswith(p): if f.startswith(p):
f = f[len(p) :] f = f[len(p):]
break break
return f.lower() return f.lower()
@ -96,7 +98,7 @@ def cleaner_digitalocean(f):
def cleaner_alibabacloud(f): def cleaner_alibabacloud(f):
for p in cfg.FILE_PREFIXES["alibabacloud"]: for p in cfg.FILE_PREFIXES["alibabacloud"]:
if f.startswith(p): if f.startswith(p):
f = f[len(p) :] f = f[len(p):]
break break
return f.lower() return f.lower()
@ -106,7 +108,7 @@ def cleaner_oci(f):
f = f.replace("_", "-") f = f.replace("_", "-")
for p in cfg.FILE_PREFIXES["oci"]: for p in cfg.FILE_PREFIXES["oci"]:
if f.startswith(p): if f.startswith(p):
f = f[len(p) :] f = f[len(p):]
break break
return f.lower() return f.lower()
@ -201,7 +203,8 @@ def svg2png2(pvd: str) -> None:
def _convert(base: str, path: str): def _convert(base: str, path: str):
path_src = os.path.join(base, path) path_src = os.path.join(base, path)
path_dest = path_src.replace(".svg", ".png") path_dest = path_src.replace(".svg", ".png")
subprocess.run([cfg.CMD_SVG2PNG_IM, *cfg.CMD_SVG2PNG_IM_OPTS, path_src, path_dest]) subprocess.run([cfg.CMD_SVG2PNG_IM, *
cfg.CMD_SVG2PNG_IM_OPTS, path_src, path_dest])
subprocess.run(["rm", path_src]) subprocess.run(["rm", path_src])
for root, _, files in os.walk(resource_dir(pvd)): for root, _, files in os.walk(resource_dir(pvd)):

@ -3,14 +3,14 @@ import random
import string import string
import unittest import unittest
from diagrams import Diagram from diagrams import Diagram, setcluster, setdiagram
from diagrams import setcluster, setdiagram from diagrams.c4 import Container, Database, Person, Relationship, System, SystemBoundary
from diagrams.c4 import Person, Container, Database, System, SystemBoundary, Relationship
class C4Test(unittest.TestCase): class C4Test(unittest.TestCase):
def setUp(self): def setUp(self):
self.name = "diagram-" + "".join([random.choice(string.hexdigits) for n in range(7)]).lower() self.name = "diagram-" + \
"".join([random.choice(string.hexdigits) for n in range(7)]).lower()
def tearDown(self): def tearDown(self):
setdiagram(None) setdiagram(None)
@ -23,8 +23,14 @@ class C4Test(unittest.TestCase):
def test_nodes(self): def test_nodes(self):
with Diagram(name=self.name, show=False): with Diagram(name=self.name, show=False):
person = Person("person", "A person.") person = Person("person", "A person.")
container = Container("container", "Java application", "The application.") container = Container(
database = Database("database", "Oracle database", "Stores information.") "container",
"Java application",
"The application.")
database = Database(
"database",
"Oracle database",
"Stores information.")
def test_external_nodes(self): def test_external_nodes(self):
with Diagram(name=self.name, show=False): with Diagram(name=self.name, show=False):

@ -1,10 +1,9 @@
import os import os
import pathlib
import shutil import shutil
import unittest import unittest
import pathlib
from diagrams import Cluster, Diagram, Edge, Node from diagrams import Cluster, Diagram, Edge, Node, getcluster, getdiagram, setcluster, setdiagram
from diagrams import getcluster, getdiagram, setcluster, setdiagram
class DiagramTest(unittest.TestCase): class DiagramTest(unittest.TestCase):
@ -103,7 +102,7 @@ class DiagramTest(unittest.TestCase):
def test_empty_name(self): def test_empty_name(self):
"""Check that providing an empty name don't crash, but save in a diagrams_image.xxx file.""" """Check that providing an empty name don't crash, but save in a diagrams_image.xxx file."""
self.name = 'diagrams_image' self.name = "diagrams_image"
with Diagram(show=False): with Diagram(show=False):
Node("node1") Node("node1")
self.assertTrue(os.path.exists(f"{self.name}.png")) self.assertTrue(os.path.exists(f"{self.name}.png"))
@ -111,12 +110,11 @@ class DiagramTest(unittest.TestCase):
def test_autolabel(self): def test_autolabel(self):
with Diagram(name=os.path.join(self.name, "nodes_to_node"), show=False): with Diagram(name=os.path.join(self.name, "nodes_to_node"), show=False):
node1 = Node("node1") node1 = Node("node1")
self.assertTrue(node1.label,"Node\nnode1") self.assertTrue(node1.label, "Node\nnode1")
def test_outformat_list(self): def test_outformat_list(self):
"""Check that outformat render all the files from the list.""" """Check that outformat render all the files from the list."""
self.name = 'diagrams_image' self.name = "diagrams_image"
with Diagram(show=False, outformat=["dot", "png"]): with Diagram(show=False, outformat=["dot", "png"]):
Node("node1") Node("node1")
# both files must exist # both files must exist
@ -240,56 +238,122 @@ class EdgeTest(unittest.TestCase):
with Cluster(): with Cluster():
node1 = Node("node1") node1 = Node("node1")
nodes = [Node("node2"), Node("node3")] nodes = [Node("node2"), Node("node3")]
self.assertEqual(nodes - Edge(color="red") - Edge(color="green") - node1, node1) self.assertEqual(
nodes -
Edge(
color="red") -
Edge(
color="green") -
node1,
node1)
def test_node_to_node_with_attributes(self): def test_node_to_node_with_attributes(self):
with Diagram(name=os.path.join(self.name, "node_to_node_with_attributes"), show=False): with Diagram(name=os.path.join(self.name, "node_to_node_with_attributes"), show=False):
with Cluster(): with Cluster():
node1 = Node("node1") node1 = Node("node1")
node2 = Node("node2") node2 = Node("node2")
self.assertEqual(node1 << Edge(color="red", label="1.1") << node2, node2) self.assertEqual(
self.assertEqual(node1 >> Edge(color="green", label="1.2") >> node2, node2) node1 << Edge(
self.assertEqual(node1 << Edge(color="blue", label="1.3") >> node2, node2) color="red",
label="1.1") << node2,
node2)
self.assertEqual(
node1 >> Edge(
color="green",
label="1.2") >> node2,
node2)
self.assertEqual(
node1 << Edge(
color="blue",
label="1.3") >> node2,
node2)
def test_node_to_node_with_additional_attributes(self): def test_node_to_node_with_additional_attributes(self):
with Diagram(name=os.path.join(self.name, "node_to_node_with_additional_attributes"), show=False): with Diagram(name=os.path.join(self.name, "node_to_node_with_additional_attributes"), show=False):
with Cluster(): with Cluster():
node1 = Node("node1") node1 = Node("node1")
node2 = Node("node2") node2 = Node("node2")
self.assertEqual(node1 << Edge(color="red", label="2.1") << Edge(color="blue") << node2, node2) self.assertEqual(
self.assertEqual(node1 >> Edge(color="green", label="2.2") >> Edge(color="red") >> node2, node2) node1 << Edge(
self.assertEqual(node1 << Edge(color="blue", label="2.3") >> Edge(color="black") >> node2, node2) color="red",
label="2.1") << Edge(
color="blue") << node2,
node2)
self.assertEqual(
node1 >> Edge(
color="green",
label="2.2") >> Edge(
color="red") >> node2,
node2)
self.assertEqual(
node1 << Edge(
color="blue",
label="2.3") >> Edge(
color="black") >> node2,
node2)
def test_nodes_to_node_with_attributes_loop(self): def test_nodes_to_node_with_attributes_loop(self):
with Diagram(name=os.path.join(self.name, "nodes_to_node_with_attributes_loop"), show=False): with Diagram(name=os.path.join(self.name, "nodes_to_node_with_attributes_loop"), show=False):
with Cluster(): with Cluster():
node = Node("node") node = Node("node")
self.assertEqual(node >> Edge(color="red", label="3.1") >> node, node) self.assertEqual(
self.assertEqual(node << Edge(color="green", label="3.2") << node, node) node >> Edge(
self.assertEqual(node >> Edge(color="blue", label="3.3") << node, node) color="red",
self.assertEqual(node << Edge(color="pink", label="3.4") >> node, node) label="3.1") >> node,
node)
self.assertEqual(
node << Edge(
color="green",
label="3.2") << node,
node)
self.assertEqual(
node >> Edge(
color="blue",
label="3.3") << node,
node)
self.assertEqual(
node << Edge(
color="pink",
label="3.4") >> node,
node)
def test_nodes_to_node_with_attributes_bothdirectional(self): def test_nodes_to_node_with_attributes_bothdirectional(self):
with Diagram(name=os.path.join(self.name, "nodes_to_node_with_attributes_bothdirectional"), show=False): with Diagram(name=os.path.join(self.name, "nodes_to_node_with_attributes_bothdirectional"), show=False):
with Cluster(): with Cluster():
node1 = Node("node1") node1 = Node("node1")
nodes = [Node("node2"), Node("node3")] nodes = [Node("node2"), Node("node3")]
self.assertEqual(nodes << Edge(color="green", label="4") >> node1, node1) self.assertEqual(
nodes << Edge(
color="green",
label="4") >> node1,
node1)
def test_nodes_to_node_with_attributes_bidirectional(self): def test_nodes_to_node_with_attributes_bidirectional(self):
with Diagram(name=os.path.join(self.name, "nodes_to_node_with_attributes_bidirectional"), show=False): with Diagram(name=os.path.join(self.name, "nodes_to_node_with_attributes_bidirectional"), show=False):
with Cluster(): with Cluster():
node1 = Node("node1") node1 = Node("node1")
nodes = [Node("node2"), Node("node3")] nodes = [Node("node2"), Node("node3")]
self.assertEqual(nodes << Edge(color="blue", label="5") >> node1, node1) self.assertEqual(
nodes << Edge(
color="blue",
label="5") >> node1,
node1)
def test_nodes_to_node_with_attributes_onedirectional(self): def test_nodes_to_node_with_attributes_onedirectional(self):
with Diagram(name=os.path.join(self.name, "nodes_to_node_with_attributes_onedirectional"), show=False): with Diagram(name=os.path.join(self.name, "nodes_to_node_with_attributes_onedirectional"), show=False):
with Cluster(): with Cluster():
node1 = Node("node1") node1 = Node("node1")
nodes = [Node("node2"), Node("node3")] nodes = [Node("node2"), Node("node3")]
self.assertEqual(nodes >> Edge(color="red", label="6.1") >> node1, node1) self.assertEqual(
self.assertEqual(nodes << Edge(color="green", label="6.2") << node1, node1) nodes >> Edge(
color="red",
label="6.1") >> node1,
node1)
self.assertEqual(
nodes << Edge(
color="green",
label="6.2") << node1,
node1)
def test_nodes_to_node_with_additional_attributes_directional(self): def test_nodes_to_node_with_additional_attributes_directional(self):
with Diagram(name=os.path.join(self.name, "nodes_to_node_with_additional_attributes_directional"), show=False): with Diagram(name=os.path.join(self.name, "nodes_to_node_with_additional_attributes_directional"), show=False):
@ -297,11 +361,19 @@ class EdgeTest(unittest.TestCase):
node1 = Node("node1") node1 = Node("node1")
nodes = [Node("node2"), Node("node3")] nodes = [Node("node2"), Node("node3")]
self.assertEqual( self.assertEqual(
nodes >> Edge(color="red", label="6.1") >> Edge(color="blue", label="6.2") >> node1, node1 nodes >> Edge(
) color="red",
label="6.1") >> Edge(
color="blue",
label="6.2") >> node1,
node1)
self.assertEqual( self.assertEqual(
nodes << Edge(color="green", label="6.3") << Edge(color="pink", label="6.4") << node1, node1 nodes << Edge(
) color="green",
label="6.3") << Edge(
color="pink",
label="6.4") << node1,
node1)
class ResourcesTest(unittest.TestCase): class ResourcesTest(unittest.TestCase):
@ -311,7 +383,13 @@ class ResourcesTest(unittest.TestCase):
i.e. resources/<provider>/<type>/<image>, so check that this depth isn't i.e. resources/<provider>/<type>/<image>, so check that this depth isn't
exceeded. exceeded.
""" """
resources_dir = pathlib.Path(__file__).parent.parent / 'resources' resources_dir = pathlib.Path(__file__).parent.parent / "resources"
max_depth = max(os.path.relpath(d, resources_dir).count(os.sep) + 1 max_depth = max(
for d, _, _ in os.walk(resources_dir)) os.path.relpath(
d,
resources_dir).count(
os.sep) +
1 for d,
_,
_ in os.walk(resources_dir))
self.assertLessEqual(max_depth, 2) self.assertLessEqual(max_depth, 2)

Loading…
Cancel
Save