[blockdiag] 14/29: Import Upstream version 1.3.2
Andreas Tille
tille at debian.org
Tue Jan 10 21:35:58 UTC 2017
This is an automated email from the git hooks/post-receive script.
tille pushed a commit to branch master
in repository blockdiag.
commit 8f8eaf3aa06d7c6b898b15b7d8b9158cf2cbba1a
Author: Andreas Tille <tille at debian.org>
Date: Tue Jan 10 11:08:04 2017 +0100
Import Upstream version 1.3.2
---
MANIFEST.in | 17 +-
PKG-INFO | 992 +++++++++++----------
src/README.txt => README.rst | 966 ++++++++++----------
bootstrap.py | 234 ++---
buildout.cfg | 94 +-
examples/blockdiagrc | 2 +
setup.cfg | 3 +
setup.py | 231 +++--
src/TODO.txt | 14 -
src/blockdiag.egg-info/PKG-INFO | 992 +++++++++++----------
src/blockdiag.egg-info/SOURCES.txt | 11 +-
src/blockdiag.egg-info/entry_points.txt | 64 +-
src/blockdiag.egg-info/requires.txt | 5 +-
src/blockdiag/__init__.py | 32 +-
src/blockdiag/builder.py | 80 +-
src/blockdiag/command.py | 14 +-
src/blockdiag/drawer.py | 8 +-
src/blockdiag/elements.py | 5 +-
src/blockdiag/imagedraw/base.py | 5 +-
src/blockdiag/imagedraw/filters/__init__.py | 28 +-
src/blockdiag/imagedraw/filters/linejump.py | 13 +-
src/blockdiag/imagedraw/pdf.py | 9 +-
src/blockdiag/imagedraw/png.py | 62 +-
src/blockdiag/imagedraw/simplesvg.py | 32 +-
src/blockdiag/imagedraw/svg.py | 31 +-
src/blockdiag/imagedraw/textfolder.py | 20 +-
src/blockdiag/imagedraw/utils/__init__.py | 65 +-
src/blockdiag/imagedraw/utils/ellipse.py | 4 +-
src/blockdiag/metrics.py | 272 +++---
src/blockdiag/noderenderer/__init__.py | 11 +-
src/blockdiag/noderenderer/actor.py | 13 +-
src/blockdiag/noderenderer/circle.py | 5 +-
src/blockdiag/noderenderer/cloud.py | 23 +-
src/blockdiag/noderenderer/diamond.py | 9 +-
src/blockdiag/noderenderer/flowchart/database.py | 11 +-
src/blockdiag/noderenderer/flowchart/input.py | 6 +-
src/blockdiag/noderenderer/flowchart/loopin.py | 7 +-
src/blockdiag/noderenderer/flowchart/loopout.py | 7 +-
src/blockdiag/noderenderer/flowchart/terminator.py | 2 +-
src/blockdiag/noderenderer/roundedbox.py | 2 +-
src/blockdiag/noderenderer/square.py | 5 +-
src/blockdiag/noderenderer/textbox.py | 5 +-
src/blockdiag/parser.py | 223 +++--
src/blockdiag/tests/diagrams/branched.diag | 10 +-
src/blockdiag/tests/diagrams/circular_ref.diag | 10 +-
.../tests/diagrams/circular_ref_to_root.diag | 10 +-
.../tests/diagrams/circular_skipped_edge.diag | 10 +-
src/blockdiag/tests/diagrams/edge_attribute.diag | 10 +-
src/blockdiag/tests/diagrams/edge_styles.diag | 18 +-
src/blockdiag/tests/diagrams/empty_group.diag | 10 +-
.../tests/diagrams/empty_nested_group.diag | 14 +-
.../diagrams/errors/belongs_to_two_groups.diag | 18 +-
.../tests/diagrams/errors/group_follows_node.diag | 12 +-
.../tests/diagrams/errors/node_follows_group.diag | 12 +-
src/blockdiag/tests/diagrams/flowable_node.diag | 10 +-
src/blockdiag/tests/diagrams/folded_edge.diag | 12 +-
.../group_id_and_node_id_are_not_conflicted.diag | 14 +-
.../tests/diagrams/group_works_node_decorator.diag | 18 +-
.../tests/diagrams/large_group_and_node.diag | 20 +-
.../tests/diagrams/large_group_and_node2.diag | 14 +-
.../tests/diagrams/large_group_and_two_nodes.diag | 22 +-
src/blockdiag/tests/diagrams/multiple_groups.diag | 32 +-
.../tests/diagrams/multiple_nested_groups.diag | 28 +-
src/blockdiag/tests/diagrams/nested_groups.diag | 18 +-
.../tests/diagrams/nested_groups_and_edges.diag | 22 +-
.../nested_groups_work_node_decorator.diag | 22 +-
.../tests/diagrams/nested_skipped_circular.diag | 14 +-
src/blockdiag/tests/diagrams/node_attribute.diag | 24 +-
.../diagrams/node_in_group_follows_outer_node.diag | 14 +-
src/blockdiag/tests/diagrams/node_styles.diag | 10 +-
.../diagrams/outer_node_follows_node_in_group.diag | 14 +-
.../tests/diagrams/reverse_multiple_groups.diag | 32 +-
src/blockdiag/tests/diagrams/self_ref.diag | 8 +-
src/blockdiag/tests/diagrams/simple_group.diag | 14 +-
src/blockdiag/tests/diagrams/single_edge.diag | 6 +-
src/blockdiag/tests/diagrams/single_node.diag | 6 +-
src/blockdiag/tests/diagrams/skipped_circular.diag | 12 +-
src/blockdiag/tests/diagrams/skipped_edge.diag | 10 +-
src/blockdiag/tests/diagrams/triple_branched.diag | 12 +-
.../tests/diagrams/twin_circular_ref_to_root.diag | 10 +-
src/blockdiag/tests/diagrams/two_edges.diag | 6 +-
src/blockdiag/tests/diagrams/white.gif | Bin 0 -> 43 bytes
src/blockdiag/tests/test_boot_params.py | 203 ++---
src/blockdiag/tests/test_builder.py | 317 ++++---
src/blockdiag/tests/test_builder_edge.py | 294 +++---
src/blockdiag/tests/test_builder_errors.py | 192 ++--
src/blockdiag/tests/test_builder_group.py | 419 ++++-----
src/blockdiag/tests/test_builder_node.py | 273 +++---
src/blockdiag/tests/test_builder_separate.py | 80 +-
src/blockdiag/tests/test_generate_diagram.py | 169 ++--
src/blockdiag/tests/test_imagedraw_textfolder.py | 33 +-
src/blockdiag/tests/test_imagedraw_utils.py | 64 ++
src/blockdiag/tests/test_parser.py | 283 +++---
src/blockdiag/tests/test_pep8.py | 105 +--
src/blockdiag/tests/test_rst_directives.py | 236 ++---
src/blockdiag/tests/test_utils_fontmap.py | 101 ++-
src/blockdiag/tests/utils.py | 133 ++-
src/blockdiag/utils/__init__.py | 38 +-
src/blockdiag/utils/bootstrap.py | 56 +-
src/blockdiag/utils/collections.py | 39 -
src/blockdiag/utils/compat.py | 60 ++
src/blockdiag/utils/config.py | 13 +-
src/blockdiag/utils/fontmap.py | 5 +-
src/blockdiag/utils/functools.py | 39 -
src/blockdiag/utils/images.py | 74 +-
src/blockdiag/utils/jpeg.py | 5 +-
src/blockdiag/utils/myitertools.py | 18 +-
src/blockdiag/utils/namedtuple.py | 18 -
src/blockdiag/utils/rst/directives.py | 62 +-
src/blockdiag/utils/urlutil.py | 5 +-
src/blockdiag/utils/uuid.py | 4 +-
src/blockdiag_sphinxhelper.py | 20 +-
tox.ini | 20 +
113 files changed, 4184 insertions(+), 4386 deletions(-)
diff --git a/MANIFEST.in b/MANIFEST.in
index 3e2ed84..02701f8 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,8 +1,9 @@
-include buildout.cfg
-include bootstrap.py
-include MANIFEST.in
-include LICENSE
-include blockdiag.1
-recursive-include examples *.diag *.png *.svg
-recursive-include src *.py *.txt *.diag
-
+include buildout.cfg
+include bootstrap.py
+include MANIFEST.in
+include README.rst
+include LICENSE
+include blockdiag.1
+include tox.ini
+recursive-include examples blockdiagrc *.diag *.png *.svg
+recursive-include src *.py *.diag *.gif
diff --git a/PKG-INFO b/PKG-INFO
index 74aa02e..be630f3 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,509 +1,513 @@
-Metadata-Version: 1.1
+Metadata-Version: 1.0
Name: blockdiag
-Version: 1.2.4
-Summary: blockdiag generate block-diagram image file from spec-text file.
+Version: 1.3.2
+Summary: blockdiag generates block-diagram image from text
Home-page: http://blockdiag.com/
Author: Takeshi Komiya
Author-email: i.tkomiya at gmail.com
License: Apache License 2.0
Download-URL: http://pypi.python.org/pypi/blockdiag
-Description: `blockdiag` generate block-diagram image file from spec-text file.
-
- Features
- ========
- * Generate block-diagram from dot like text (basic feature).
- * Multilingualization for node-label (utf-8 only).
-
- You can get some examples and generated images on
- `blockdiag.com <http://blockdiag.com/blockdiag/build/html/index.html>`_ .
-
- Setup
- =====
-
- by easy_install
- ----------------
- Make environment::
-
- $ easy_install blockdiag
-
- If you want to export as PDF format, give pdf arguments::
-
- $ easy_install "blockdiag[pdf]"
-
- by buildout
- ------------
- Make environment::
-
- $ hg clone http://bitbucket.org/tk0miya/blockdiag
- $ cd blockdiag
- $ python bootstrap.py
- $ bin/buildout
-
- Copy and modify ini file. example::
-
- $ cp <blockdiag installed path>/blockdiag/examples/simple.diag .
- $ vi simple.diag
-
- Please refer to `spec-text setting sample`_ section for the format of the
- `simpla.diag` configuration file.
-
- spec-text setting sample
- ========================
- Few examples are available.
- You can get more examples at
- `blockdiag.com <http://blockdiag.com/blockdiag/build/html/index.html>`_ .
-
- simple.diag
- ------------
- simple.diag is simply define nodes and transitions by dot-like text format::
-
- diagram admin {
- top_page -> config -> config_edit -> config_confirm -> top_page;
- }
-
- screen.diag
- ------------
- screen.diag is more complexly sample. diaglam nodes have a alternative label
- and some transitions::
-
- diagram admin {
- top_page [label = "Top page"];
-
- foo_index [label = "List of FOOs"];
- foo_detail [label = "Detail FOO"];
- foo_add [label = "Add FOO"];
- foo_add_confirm [label = "Add FOO (confirm)"];
- foo_edit [label = "Edit FOO"];
- foo_edit_confirm [label = "Edit FOO (confirm)"];
- foo_delete_confirm [label = "Delete FOO (confirm)"];
-
- bar_detail [label = "Detail of BAR"];
- bar_edit [label = "Edit BAR"];
- bar_edit_confirm [label = "Edit BAR (confirm)"];
-
- logout;
-
- top_page -> foo_index;
- top_page -> bar_detail;
-
- foo_index -> foo_detail;
- foo_detail -> foo_edit;
- foo_detail -> foo_delete_confirm;
- foo_index -> foo_add -> foo_add_confirm -> foo_index;
- foo_index -> foo_edit -> foo_edit_confirm -> foo_index;
- foo_index -> foo_delete_confirm -> foo_index;
-
- bar_detail -> bar_edit -> bar_edit_confirm -> bar_detail;
- }
-
-
- Usage
- =====
- Execute blockdiag command::
-
- $ blockdiag simple.diag
- $ ls simple.png
- simple.png
-
-
- Requirements
- ============
- * Python 2.4 or later (not support 3.x)
- * Python Imaging Library 1.1.5 or later.
- * funcparserlib 0.3.4 or later.
- * setuptools or distribute.
-
-
- License
- =======
- Apache License 2.0
-
-
- History
- =======
-
- 1.2.4 (2012-11-21)
- ------------------
- * Fix bugs
-
- 1.2.3 (2012-11-05)
- ------------------
- * Fix bugs
-
- 1.2.2 (2012-10-28)
- ------------------
- * Fix bugs
-
- 1.2.1 (2012-10-28)
- ------------------
- * Add external imagedraw plugin supports
- * Add node attribute: label_orientation*
- * Fix bugs
-
- 1.2.0 (2012-10-22)
- ------------------
- * Optimize algorithm for rendering shadow
- * Add options to docutils directive
- * Fix bugs
-
- 1.1.8 (2012-09-28)
- ------------------
- * Add --ignore-pil option
- * Fix bugs
-
- 1.1.7 (2012-09-20)
- ------------------
- * Add diagram attribute: shadow_style
- * Add font path for centos 6.2
- * Add a setting 'antialias' in the configuration file
- * Fix bugs
-
- 1.1.6 (2012-06-06)
- ------------------
- * Support for readthedocs.org
- * reST directive supports :caption: option
- * Fix bugs
-
- 1.1.5 (2012-04-22)
- ------------------
- * Embed source code to SVG document as description
- * Fix bugs
-
- 1.1.4 (2012-03-15)
- ------------------
- * Add new edge.hstyles: oneone, onemany, manyone, manymany
- * Add edge attribute: description (for build description-tables)
- * Fix bugs
-
- 1.1.3 (2012-02-13)
- ------------------
- * Add new edge type for data-models (thanks to David Lang)
- * Add --no-transparency option
- * Fix bugs
-
- 1.1.2 (2011-12-26)
- ------------------
- * Support font-index for TrueType Font Collections (.ttc file)
- * Allow to use reST syntax in descriptions of nodes
- * Fix bugs
-
- 1.1.1 (2011-11-27)
- ------------------
- * Add node attribute: href (thanks to @r_rudi!)
- * Fix bugs
-
- 1.1.0 (2011-11-19)
- ------------------
- * Add shape: square and circle
- * Add fontfamily attribute for switching fontface
- * Fix bugs
-
- 1.0.3 (2011-11-13)
- ------------------
- * Add plugin: attributes
- * Change plugin syntax; (cf. plugin attributes [attr = value, attr, value])
- * Fix bugs
-
- 1.0.2 (2011-11-07)
- ------------------
- * Fix bugs
-
- 1.0.1 (2011-11-06)
- ------------------
- * Add group attribute: shape
- * Fix bugs
-
- 1.0.0 (2011-11-04)
- ------------------
- * Add node attribute: linecolor
- * Rename diagram attributes:
- * fontsize -> default_fontsize
- * default_line_color -> default_linecolor
- * default_text_color -> default_textcolor
- * Add docutils extention
- * Fix bugs
-
- 0.9.7 (2011-11-01)
- ------------------
- * Add node attribute: fontsize
- * Add edge attributes: thick, fontsize
- * Add group attribute: fontsize
- * Change color of shadow in PDF mode
- * Add class feature (experimental)
- * Add handler-plugin framework (experimental)
-
- 0.9.6 (2011-10-22)
- ------------------
- * node.style supports dashed_array format style
- * Fix bugs
-
- 0.9.5 (2011-10-19)
- ------------------
- * Add node attributes: width and height
- * Fix bugs
-
- 0.9.4 (2011-10-07)
- ------------------
- * Fix bugs
-
- 0.9.3 (2011-10-06)
- ------------------
- * Replace SVG core by original's (simplesvg.py)
- * Refactored
- * Fix bugs
-
- 0.9.2 (2011-09-30)
- ------------------
- * Add node attribute: textcolor
- * Add group attribute: textcolor
- * Add edge attribute: textcolor
- * Add diagram attributes: default_text_attribute
- * Fix beginpoint shape and endpoint shape were reversed
- * Fix bugs
-
- 0.9.1 (2011-09-26)
- ------------------
- * Add diagram attributes: default_node_color, default_group_color and default_line_color
- * Fix bugs
-
- 0.9.0 (2011-09-25)
- ------------------
- * Add icon attribute to node
- * Make transparency to background of PNG images
- * Fix bugs
-
- 0.8.9 (2011-08-09)
- ------------------
- * Fix bugs
-
- 0.8.8 (2011-08-08)
- ------------------
- * Fix bugs
-
- 0.8.7 (2011-08-06)
- ------------------
- * Fix bugs
-
- 0.8.6 (2011-08-01)
- ------------------
- * Support Pillow as replacement of PIL (experimental)
- * Fix bugs
-
- 0.8.5 (2011-07-31)
- ------------------
- * Allow dot characters in node_id
- * Fix bugs
-
- 0.8.4 (2011-07-05)
- ------------------
- * Fix bugs
-
- 0.8.3 (2011-07-03)
- ------------------
- * Support input from stdin
- * Fix bugs
-
- 0.8.2 (2011-06-29)
- ------------------
- * Add node.stacked
- * Add node shapes: dots, none
- * Add hiragino-font to font search list
- * Support background image fetching from web
- * Add diagram.edge_layout (experimental)
- * Fix bugs
-
- 0.8.1 (2011-05-14)
- ------------------
- * Change license to Apache License 2.0
- * Fix bugs
-
- 0.8.0 (2011-05-04)
- ------------------
- * Add --separate option and --version option
- * Fix bugs
-
- 0.7.8 (2011-04-19)
- ------------------
- * Update layout engine
- * Update requirements: PIL >= 1.1.5
- * Update parser for tokenize performance
- * Add --nodoctype option
- * Fix bugs
- * Add many testcases
-
- 0.7.7 (2011-03-29)
- ------------------
- * Fix bugs
-
- 0.7.6 (2011-03-26)
- ------------------
- * Add new layout manager for portrait edges
- * Fix bugs
-
- 0.7.5 (2011-03-20)
- ------------------
- * Support multiple nodes relations (cf. A -> B, C)
- * Support node group declaration at attribute of nodes
- * Fix bugs
-
- 0.7.4 (2011-03-08)
- ------------------
- * Fix bugs
-
- 0.7.3 (2011-03-02)
- ------------------
- * Use UTF-8 characters as Name token (by @swtw7466)
- * Fix htmlentities included in labels was not escaped on SVG images
- * Fix bugs
-
- 0.7.2 (2011-02-28)
- ------------------
- * Add default_shape attribute to diagram
-
- 0.7.1 (2011-02-27)
- ------------------
- * Fix edge has broken with antialias option
-
- 0.7.0 (2011-02-25)
- ------------------
- * Support node shape
-
- 0.6.7 (2011-02-12)
- ------------------
- * Change noderenderer interface to new style
- * Render dashed ellipse more clearly (contributed by @cocoatomo)
- * Support PDF exporting
-
- 0.6.6 (2011-01-31)
- ------------------
- * Support diagram.shape_namespace
- * Add new node shapes; mail, cloud, beginpoint, endpoint, minidiamond, actor
- * Support plug-in structure to install node shapes
- * Fix bugs
-
- 0.6.5 (2011-01-18)
- ------------------
- * Support node shape (experimental)
-
- 0.6.4 (2011-01-17)
- ------------------
- * Fix bugs
-
- 0.6.3 (2011-01-15)
- ------------------
- * Fix bugs
-
- 0.6.2 (2011-01-08)
- ------------------
- * Fix bugs
-
- 0.6.1 (2011-01-07)
- ------------------
- * Implement 'folded' attribute for edge
- * Refactor layout engine
-
- 0.6 (2011-01-02)
- ------------------
- * Support nested groups.
-
- 0.5.5 (2010-12-24)
- ------------------
- * Specify direction of edges as syntax (->, --, <-, <->)
- * Fix bugs.
-
- 0.5.4 (2010-12-23)
- ------------------
- * Remove debug codes.
-
- 0.5.3 (2010-12-23)
- ------------------
- * Support NodeGroup.label.
- * Implement --separate option (experimental)
- * Fix right-up edge overrapped on other nodes.
- * Support configration file: .blockdiagrc
-
- 0.5.2 (2010-11-06)
- ------------------
- * Fix unicode errors for UTF-8'ed SVG exportion.
- * Refactoring codes for running on GAE.
-
- 0.5.1 (2010-10-26)
- ------------------
- * Fix license text on diagparser.py
- * Update layout engine.
-
- 0.5 (2010-10-15)
- ------------------
- * Support background-image of node (SVG)
- * Support labels for edge.
- * Fix bugs.
-
- 0.4.2 (2010-10-10)
- ------------------
- * Support background-color of node groups.
- * Draw edge has jumped at edge's cross-points.
- * Fix bugs.
-
- 0.4.1 (2010-10-07)
- ------------------
- * Fix bugs.
-
- 0.4 (2010-10-07)
- ------------------
- * Support SVG exporting.
- * Support dashed edge drawing.
- * Support background image of nodes (PNG only)
-
- 0.3.1 (2010-09-29)
- ------------------
- * Fasten anti-alias process.
- * Fix text was broken on windows.
-
- 0.3 (2010-09-26)
- ------------------
- * Add --antialias option.
- * Fix bugs.
-
- 0.2.2 (2010-09-25)
- ------------------
- * Fix edge bugs.
-
- 0.2.1 (2010-09-25)
- ------------------
- * Fix bugs.
- * Fix package style.
-
- 0.2 (2010-09-23)
- ------------------
- * Update layout engine.
- * Support group { ... } sentence for create Node-Groups.
- * Support numbered badge on node (cf. A [numbered = 5])
-
- 0.1 (2010-09-20)
- -----------------
- * first release
-
- Todos
- ======
-
- Functionals
- ------------
- * Reimplement --separate option
- * Support diagram legends
- * Support other block diagram structure
-
- Known Issues
- -------------
- * Fix some experimental features.
- * PDF renderer does not support blur shadow
- * PDF renderer does not support path rendering
+Description: `blockdiag` generate block-diagram image file from spec-text file.
+
+ Features
+ ========
+ * Generate block-diagram from dot like text (basic feature).
+ * Multilingualization for node-label (utf-8 only).
+
+ You can get some examples and generated images on
+ `blockdiag.com <http://blockdiag.com/blockdiag/build/html/index.html>`_ .
+
+ Setup
+ =====
+
+ by easy_install
+ ----------------
+ Make environment::
+
+ $ easy_install blockdiag
+
+ If you want to export as PDF format, give pdf arguments::
+
+ $ easy_install "blockdiag[pdf]"
+
+ by buildout
+ ------------
+ Make environment::
+
+ $ hg clone http://bitbucket.org/tk0miya/blockdiag
+ $ cd blockdiag
+ $ python bootstrap.py
+ $ bin/buildout
+
+ Copy and modify ini file. example::
+
+ $ cp <blockdiag installed path>/blockdiag/examples/simple.diag .
+ $ vi simple.diag
+
+ Please refer to `spec-text setting sample`_ section for the format of the
+ `simpla.diag` configuration file.
+
+ spec-text setting sample
+ ========================
+ Few examples are available.
+ You can get more examples at
+ `blockdiag.com <http://blockdiag.com/blockdiag/build/html/index.html>`_ .
+
+ simple.diag
+ ------------
+ simple.diag is simply define nodes and transitions by dot-like text format::
+
+ diagram admin {
+ top_page -> config -> config_edit -> config_confirm -> top_page;
+ }
+
+ screen.diag
+ ------------
+ screen.diag is more complexly sample. diaglam nodes have a alternative label
+ and some transitions::
+
+ diagram admin {
+ top_page [label = "Top page"];
+
+ foo_index [label = "List of FOOs"];
+ foo_detail [label = "Detail FOO"];
+ foo_add [label = "Add FOO"];
+ foo_add_confirm [label = "Add FOO (confirm)"];
+ foo_edit [label = "Edit FOO"];
+ foo_edit_confirm [label = "Edit FOO (confirm)"];
+ foo_delete_confirm [label = "Delete FOO (confirm)"];
+
+ bar_detail [label = "Detail of BAR"];
+ bar_edit [label = "Edit BAR"];
+ bar_edit_confirm [label = "Edit BAR (confirm)"];
+
+ logout;
+
+ top_page -> foo_index;
+ top_page -> bar_detail;
+
+ foo_index -> foo_detail;
+ foo_detail -> foo_edit;
+ foo_detail -> foo_delete_confirm;
+ foo_index -> foo_add -> foo_add_confirm -> foo_index;
+ foo_index -> foo_edit -> foo_edit_confirm -> foo_index;
+ foo_index -> foo_delete_confirm -> foo_index;
+
+ bar_detail -> bar_edit -> bar_edit_confirm -> bar_detail;
+ }
+
+
+ Usage
+ =====
+ Execute blockdiag command::
+
+ $ blockdiag simple.diag
+ $ ls simple.png
+ simple.png
+
+
+ Requirements
+ ============
+ * Python 2.6, 2.7, 3.2, 3.3
+ * Pillow 2.2.1
+ * funcparserlib 0.3.6
+ * setuptools
+
+
+ License
+ =======
+ Apache License 2.0
+
+
+ History
+ =======
+
+ 1.3.2 (2013-11-19)
+ ------------------
+ * Fix bugs
+
+ 1.3.1 (2013-10-22)
+ ------------------
+ * Fix bugs
+
+ 1.3.0 (2013-10-05)
+ ------------------
+ * Support python 3.2 and 3.3 (thanks to @masayuko)
+ * Drop supports for python 2.4 and 2.5
+ * Replace dependency: PIL -> Pillow
+
+ 1.2.4 (2012-11-21)
+ ------------------
+ * Fix bugs
+
+ 1.2.3 (2012-11-05)
+ ------------------
+ * Fix bugs
+
+ 1.2.2 (2012-10-28)
+ ------------------
+ * Fix bugs
+
+ 1.2.1 (2012-10-28)
+ ------------------
+ * Add external imagedraw plugin supports
+ * Add node attribute: label_orientation*
+ * Fix bugs
+
+ 1.2.0 (2012-10-22)
+ ------------------
+ * Optimize algorithm for rendering shadow
+ * Add options to docutils directive
+ * Fix bugs
+
+ 1.1.8 (2012-09-28)
+ ------------------
+ * Add --ignore-pil option
+ * Fix bugs
+
+ 1.1.7 (2012-09-20)
+ ------------------
+ * Add diagram attribute: shadow_style
+ * Add font path for centos 6.2
+ * Add a setting 'antialias' in the configuration file
+ * Fix bugs
+
+ 1.1.6 (2012-06-06)
+ ------------------
+ * Support for readthedocs.org
+ * reST directive supports :caption: option
+ * Fix bugs
+
+ 1.1.5 (2012-04-22)
+ ------------------
+ * Embed source code to SVG document as description
+ * Fix bugs
+
+ 1.1.4 (2012-03-15)
+ ------------------
+ * Add new edge.hstyles: oneone, onemany, manyone, manymany
+ * Add edge attribute: description (for build description-tables)
+ * Fix bugs
+
+ 1.1.3 (2012-02-13)
+ ------------------
+ * Add new edge type for data-models (thanks to David Lang)
+ * Add --no-transparency option
+ * Fix bugs
+
+ 1.1.2 (2011-12-26)
+ ------------------
+ * Support font-index for TrueType Font Collections (.ttc file)
+ * Allow to use reST syntax in descriptions of nodes
+ * Fix bugs
+
+ 1.1.1 (2011-11-27)
+ ------------------
+ * Add node attribute: href (thanks to @r_rudi!)
+ * Fix bugs
+
+ 1.1.0 (2011-11-19)
+ ------------------
+ * Add shape: square and circle
+ * Add fontfamily attribute for switching fontface
+ * Fix bugs
+
+ 1.0.3 (2011-11-13)
+ ------------------
+ * Add plugin: attributes
+ * Change plugin syntax; (cf. plugin attributes [attr = value, attr, value])
+ * Fix bugs
+
+ 1.0.2 (2011-11-07)
+ ------------------
+ * Fix bugs
+
+ 1.0.1 (2011-11-06)
+ ------------------
+ * Add group attribute: shape
+ * Fix bugs
+
+ 1.0.0 (2011-11-04)
+ ------------------
+ * Add node attribute: linecolor
+ * Rename diagram attributes:
+ * fontsize -> default_fontsize
+ * default_line_color -> default_linecolor
+ * default_text_color -> default_textcolor
+ * Add docutils extention
+ * Fix bugs
+
+ 0.9.7 (2011-11-01)
+ ------------------
+ * Add node attribute: fontsize
+ * Add edge attributes: thick, fontsize
+ * Add group attribute: fontsize
+ * Change color of shadow in PDF mode
+ * Add class feature (experimental)
+ * Add handler-plugin framework (experimental)
+
+ 0.9.6 (2011-10-22)
+ ------------------
+ * node.style supports dashed_array format style
+ * Fix bugs
+
+ 0.9.5 (2011-10-19)
+ ------------------
+ * Add node attributes: width and height
+ * Fix bugs
+
+ 0.9.4 (2011-10-07)
+ ------------------
+ * Fix bugs
+
+ 0.9.3 (2011-10-06)
+ ------------------
+ * Replace SVG core by original's (simplesvg.py)
+ * Refactored
+ * Fix bugs
+
+ 0.9.2 (2011-09-30)
+ ------------------
+ * Add node attribute: textcolor
+ * Add group attribute: textcolor
+ * Add edge attribute: textcolor
+ * Add diagram attributes: default_text_attribute
+ * Fix beginpoint shape and endpoint shape were reversed
+ * Fix bugs
+
+ 0.9.1 (2011-09-26)
+ ------------------
+ * Add diagram attributes: default_node_color, default_group_color and default_line_color
+ * Fix bugs
+
+ 0.9.0 (2011-09-25)
+ ------------------
+ * Add icon attribute to node
+ * Make transparency to background of PNG images
+ * Fix bugs
+
+ 0.8.9 (2011-08-09)
+ ------------------
+ * Fix bugs
+
+ 0.8.8 (2011-08-08)
+ ------------------
+ * Fix bugs
+
+ 0.8.7 (2011-08-06)
+ ------------------
+ * Fix bugs
+
+ 0.8.6 (2011-08-01)
+ ------------------
+ * Support Pillow as replacement of PIL (experimental)
+ * Fix bugs
+
+ 0.8.5 (2011-07-31)
+ ------------------
+ * Allow dot characters in node_id
+ * Fix bugs
+
+ 0.8.4 (2011-07-05)
+ ------------------
+ * Fix bugs
+
+ 0.8.3 (2011-07-03)
+ ------------------
+ * Support input from stdin
+ * Fix bugs
+
+ 0.8.2 (2011-06-29)
+ ------------------
+ * Add node.stacked
+ * Add node shapes: dots, none
+ * Add hiragino-font to font search list
+ * Support background image fetching from web
+ * Add diagram.edge_layout (experimental)
+ * Fix bugs
+
+ 0.8.1 (2011-05-14)
+ ------------------
+ * Change license to Apache License 2.0
+ * Fix bugs
+
+ 0.8.0 (2011-05-04)
+ ------------------
+ * Add --separate option and --version option
+ * Fix bugs
+
+ 0.7.8 (2011-04-19)
+ ------------------
+ * Update layout engine
+ * Update requirements: PIL >= 1.1.5
+ * Update parser for tokenize performance
+ * Add --nodoctype option
+ * Fix bugs
+ * Add many testcases
+
+ 0.7.7 (2011-03-29)
+ ------------------
+ * Fix bugs
+
+ 0.7.6 (2011-03-26)
+ ------------------
+ * Add new layout manager for portrait edges
+ * Fix bugs
+
+ 0.7.5 (2011-03-20)
+ ------------------
+ * Support multiple nodes relations (cf. A -> B, C)
+ * Support node group declaration at attribute of nodes
+ * Fix bugs
+
+ 0.7.4 (2011-03-08)
+ ------------------
+ * Fix bugs
+
+ 0.7.3 (2011-03-02)
+ ------------------
+ * Use UTF-8 characters as Name token (by @swtw7466)
+ * Fix htmlentities included in labels was not escaped on SVG images
+ * Fix bugs
+
+ 0.7.2 (2011-02-28)
+ ------------------
+ * Add default_shape attribute to diagram
+
+ 0.7.1 (2011-02-27)
+ ------------------
+ * Fix edge has broken with antialias option
+
+ 0.7.0 (2011-02-25)
+ ------------------
+ * Support node shape
+
+ 0.6.7 (2011-02-12)
+ ------------------
+ * Change noderenderer interface to new style
+ * Render dashed ellipse more clearly (contributed by @cocoatomo)
+ * Support PDF exporting
+
+ 0.6.6 (2011-01-31)
+ ------------------
+ * Support diagram.shape_namespace
+ * Add new node shapes; mail, cloud, beginpoint, endpoint, minidiamond, actor
+ * Support plug-in structure to install node shapes
+ * Fix bugs
+
+ 0.6.5 (2011-01-18)
+ ------------------
+ * Support node shape (experimental)
+
+ 0.6.4 (2011-01-17)
+ ------------------
+ * Fix bugs
+
+ 0.6.3 (2011-01-15)
+ ------------------
+ * Fix bugs
+
+ 0.6.2 (2011-01-08)
+ ------------------
+ * Fix bugs
+
+ 0.6.1 (2011-01-07)
+ ------------------
+ * Implement 'folded' attribute for edge
+ * Refactor layout engine
+
+ 0.6 (2011-01-02)
+ ------------------
+ * Support nested groups.
+
+ 0.5.5 (2010-12-24)
+ ------------------
+ * Specify direction of edges as syntax (->, --, <-, <->)
+ * Fix bugs.
+
+ 0.5.4 (2010-12-23)
+ ------------------
+ * Remove debug codes.
+
+ 0.5.3 (2010-12-23)
+ ------------------
+ * Support NodeGroup.label.
+ * Implement --separate option (experimental)
+ * Fix right-up edge overrapped on other nodes.
+ * Support configration file: .blockdiagrc
+
+ 0.5.2 (2010-11-06)
+ ------------------
+ * Fix unicode errors for UTF-8'ed SVG exportion.
+ * Refactoring codes for running on GAE.
+
+ 0.5.1 (2010-10-26)
+ ------------------
+ * Fix license text on diagparser.py
+ * Update layout engine.
+
+ 0.5 (2010-10-15)
+ ------------------
+ * Support background-image of node (SVG)
+ * Support labels for edge.
+ * Fix bugs.
+
+ 0.4.2 (2010-10-10)
+ ------------------
+ * Support background-color of node groups.
+ * Draw edge has jumped at edge's cross-points.
+ * Fix bugs.
+
+ 0.4.1 (2010-10-07)
+ ------------------
+ * Fix bugs.
+
+ 0.4 (2010-10-07)
+ ------------------
+ * Support SVG exporting.
+ * Support dashed edge drawing.
+ * Support background image of nodes (PNG only)
+
+ 0.3.1 (2010-09-29)
+ ------------------
+ * Fasten anti-alias process.
+ * Fix text was broken on windows.
+
+ 0.3 (2010-09-26)
+ ------------------
+ * Add --antialias option.
+ * Fix bugs.
+
+ 0.2.2 (2010-09-25)
+ ------------------
+ * Fix edge bugs.
+
+ 0.2.1 (2010-09-25)
+ ------------------
+ * Fix bugs.
+ * Fix package style.
+
+ 0.2 (2010-09-23)
+ ------------------
+ * Update layout engine.
+ * Support group { ... } sentence for create Node-Groups.
+ * Support numbered badge on node (cf. A [numbered = 5])
+
+ 0.1 (2010-09-20)
+ -----------------
+ * first release
+
Keywords: diagram,generator
Platform: UNKNOWN
-Classifier: Development Status :: 4 - Beta
+Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3.2
+Classifier: Programming Language :: Python :: 3.3
Classifier: Topic :: Software Development
Classifier: Topic :: Software Development :: Documentation
Classifier: Topic :: Text Processing :: Markup
diff --git a/src/README.txt b/README.rst
similarity index 94%
rename from src/README.txt
rename to README.rst
index 132832d..fa950b3 100644
--- a/src/README.txt
+++ b/README.rst
@@ -1,476 +1,490 @@
-`blockdiag` generate block-diagram image file from spec-text file.
-
-Features
-========
-* Generate block-diagram from dot like text (basic feature).
-* Multilingualization for node-label (utf-8 only).
-
-You can get some examples and generated images on
-`blockdiag.com <http://blockdiag.com/blockdiag/build/html/index.html>`_ .
-
-Setup
-=====
-
-by easy_install
-----------------
-Make environment::
-
- $ easy_install blockdiag
-
-If you want to export as PDF format, give pdf arguments::
-
- $ easy_install "blockdiag[pdf]"
-
-by buildout
-------------
-Make environment::
-
- $ hg clone http://bitbucket.org/tk0miya/blockdiag
- $ cd blockdiag
- $ python bootstrap.py
- $ bin/buildout
-
-Copy and modify ini file. example::
-
- $ cp <blockdiag installed path>/blockdiag/examples/simple.diag .
- $ vi simple.diag
-
-Please refer to `spec-text setting sample`_ section for the format of the
-`simpla.diag` configuration file.
-
-spec-text setting sample
-========================
-Few examples are available.
-You can get more examples at
-`blockdiag.com <http://blockdiag.com/blockdiag/build/html/index.html>`_ .
-
-simple.diag
-------------
-simple.diag is simply define nodes and transitions by dot-like text format::
-
- diagram admin {
- top_page -> config -> config_edit -> config_confirm -> top_page;
- }
-
-screen.diag
-------------
-screen.diag is more complexly sample. diaglam nodes have a alternative label
-and some transitions::
-
- diagram admin {
- top_page [label = "Top page"];
-
- foo_index [label = "List of FOOs"];
- foo_detail [label = "Detail FOO"];
- foo_add [label = "Add FOO"];
- foo_add_confirm [label = "Add FOO (confirm)"];
- foo_edit [label = "Edit FOO"];
- foo_edit_confirm [label = "Edit FOO (confirm)"];
- foo_delete_confirm [label = "Delete FOO (confirm)"];
-
- bar_detail [label = "Detail of BAR"];
- bar_edit [label = "Edit BAR"];
- bar_edit_confirm [label = "Edit BAR (confirm)"];
-
- logout;
-
- top_page -> foo_index;
- top_page -> bar_detail;
-
- foo_index -> foo_detail;
- foo_detail -> foo_edit;
- foo_detail -> foo_delete_confirm;
- foo_index -> foo_add -> foo_add_confirm -> foo_index;
- foo_index -> foo_edit -> foo_edit_confirm -> foo_index;
- foo_index -> foo_delete_confirm -> foo_index;
-
- bar_detail -> bar_edit -> bar_edit_confirm -> bar_detail;
- }
-
-
-Usage
-=====
-Execute blockdiag command::
-
- $ blockdiag simple.diag
- $ ls simple.png
- simple.png
-
-
-Requirements
-============
-* Python 2.4 or later (not support 3.x)
-* Python Imaging Library 1.1.5 or later.
-* funcparserlib 0.3.4 or later.
-* setuptools or distribute.
-
-
-License
-=======
-Apache License 2.0
-
-
-History
-=======
-
-1.2.4 (2012-11-21)
-------------------
-* Fix bugs
-
-1.2.3 (2012-11-05)
-------------------
-* Fix bugs
-
-1.2.2 (2012-10-28)
-------------------
-* Fix bugs
-
-1.2.1 (2012-10-28)
-------------------
-* Add external imagedraw plugin supports
-* Add node attribute: label_orientation*
-* Fix bugs
-
-1.2.0 (2012-10-22)
-------------------
-* Optimize algorithm for rendering shadow
-* Add options to docutils directive
-* Fix bugs
-
-1.1.8 (2012-09-28)
-------------------
-* Add --ignore-pil option
-* Fix bugs
-
-1.1.7 (2012-09-20)
-------------------
-* Add diagram attribute: shadow_style
-* Add font path for centos 6.2
-* Add a setting 'antialias' in the configuration file
-* Fix bugs
-
-1.1.6 (2012-06-06)
-------------------
-* Support for readthedocs.org
-* reST directive supports :caption: option
-* Fix bugs
-
-1.1.5 (2012-04-22)
-------------------
-* Embed source code to SVG document as description
-* Fix bugs
-
-1.1.4 (2012-03-15)
-------------------
-* Add new edge.hstyles: oneone, onemany, manyone, manymany
-* Add edge attribute: description (for build description-tables)
-* Fix bugs
-
-1.1.3 (2012-02-13)
-------------------
-* Add new edge type for data-models (thanks to David Lang)
-* Add --no-transparency option
-* Fix bugs
-
-1.1.2 (2011-12-26)
-------------------
-* Support font-index for TrueType Font Collections (.ttc file)
-* Allow to use reST syntax in descriptions of nodes
-* Fix bugs
-
-1.1.1 (2011-11-27)
-------------------
-* Add node attribute: href (thanks to @r_rudi!)
-* Fix bugs
-
-1.1.0 (2011-11-19)
-------------------
-* Add shape: square and circle
-* Add fontfamily attribute for switching fontface
-* Fix bugs
-
-1.0.3 (2011-11-13)
-------------------
-* Add plugin: attributes
-* Change plugin syntax; (cf. plugin attributes [attr = value, attr, value])
-* Fix bugs
-
-1.0.2 (2011-11-07)
-------------------
-* Fix bugs
-
-1.0.1 (2011-11-06)
-------------------
-* Add group attribute: shape
-* Fix bugs
-
-1.0.0 (2011-11-04)
-------------------
-* Add node attribute: linecolor
-* Rename diagram attributes:
- * fontsize -> default_fontsize
- * default_line_color -> default_linecolor
- * default_text_color -> default_textcolor
-* Add docutils extention
-* Fix bugs
-
-0.9.7 (2011-11-01)
-------------------
-* Add node attribute: fontsize
-* Add edge attributes: thick, fontsize
-* Add group attribute: fontsize
-* Change color of shadow in PDF mode
-* Add class feature (experimental)
-* Add handler-plugin framework (experimental)
-
-0.9.6 (2011-10-22)
-------------------
-* node.style supports dashed_array format style
-* Fix bugs
-
-0.9.5 (2011-10-19)
-------------------
-* Add node attributes: width and height
-* Fix bugs
-
-0.9.4 (2011-10-07)
-------------------
-* Fix bugs
-
-0.9.3 (2011-10-06)
-------------------
-* Replace SVG core by original's (simplesvg.py)
-* Refactored
-* Fix bugs
-
-0.9.2 (2011-09-30)
-------------------
-* Add node attribute: textcolor
-* Add group attribute: textcolor
-* Add edge attribute: textcolor
-* Add diagram attributes: default_text_attribute
-* Fix beginpoint shape and endpoint shape were reversed
-* Fix bugs
-
-0.9.1 (2011-09-26)
-------------------
-* Add diagram attributes: default_node_color, default_group_color and default_line_color
-* Fix bugs
-
-0.9.0 (2011-09-25)
-------------------
-* Add icon attribute to node
-* Make transparency to background of PNG images
-* Fix bugs
-
-0.8.9 (2011-08-09)
-------------------
-* Fix bugs
-
-0.8.8 (2011-08-08)
-------------------
-* Fix bugs
-
-0.8.7 (2011-08-06)
-------------------
-* Fix bugs
-
-0.8.6 (2011-08-01)
-------------------
-* Support Pillow as replacement of PIL (experimental)
-* Fix bugs
-
-0.8.5 (2011-07-31)
-------------------
-* Allow dot characters in node_id
-* Fix bugs
-
-0.8.4 (2011-07-05)
-------------------
-* Fix bugs
-
-0.8.3 (2011-07-03)
-------------------
-* Support input from stdin
-* Fix bugs
-
-0.8.2 (2011-06-29)
-------------------
-* Add node.stacked
-* Add node shapes: dots, none
-* Add hiragino-font to font search list
-* Support background image fetching from web
-* Add diagram.edge_layout (experimental)
-* Fix bugs
-
-0.8.1 (2011-05-14)
-------------------
-* Change license to Apache License 2.0
-* Fix bugs
-
-0.8.0 (2011-05-04)
-------------------
-* Add --separate option and --version option
-* Fix bugs
-
-0.7.8 (2011-04-19)
-------------------
-* Update layout engine
-* Update requirements: PIL >= 1.1.5
-* Update parser for tokenize performance
-* Add --nodoctype option
-* Fix bugs
-* Add many testcases
-
-0.7.7 (2011-03-29)
-------------------
-* Fix bugs
-
-0.7.6 (2011-03-26)
-------------------
-* Add new layout manager for portrait edges
-* Fix bugs
-
-0.7.5 (2011-03-20)
-------------------
-* Support multiple nodes relations (cf. A -> B, C)
-* Support node group declaration at attribute of nodes
-* Fix bugs
-
-0.7.4 (2011-03-08)
-------------------
-* Fix bugs
-
-0.7.3 (2011-03-02)
-------------------
-* Use UTF-8 characters as Name token (by @swtw7466)
-* Fix htmlentities included in labels was not escaped on SVG images
-* Fix bugs
-
-0.7.2 (2011-02-28)
-------------------
-* Add default_shape attribute to diagram
-
-0.7.1 (2011-02-27)
-------------------
-* Fix edge has broken with antialias option
-
-0.7.0 (2011-02-25)
-------------------
-* Support node shape
-
-0.6.7 (2011-02-12)
-------------------
-* Change noderenderer interface to new style
-* Render dashed ellipse more clearly (contributed by @cocoatomo)
-* Support PDF exporting
-
-0.6.6 (2011-01-31)
-------------------
-* Support diagram.shape_namespace
-* Add new node shapes; mail, cloud, beginpoint, endpoint, minidiamond, actor
-* Support plug-in structure to install node shapes
-* Fix bugs
-
-0.6.5 (2011-01-18)
-------------------
-* Support node shape (experimental)
-
-0.6.4 (2011-01-17)
-------------------
-* Fix bugs
-
-0.6.3 (2011-01-15)
-------------------
-* Fix bugs
-
-0.6.2 (2011-01-08)
-------------------
-* Fix bugs
-
-0.6.1 (2011-01-07)
-------------------
-* Implement 'folded' attribute for edge
-* Refactor layout engine
-
-0.6 (2011-01-02)
-------------------
-* Support nested groups.
-
-0.5.5 (2010-12-24)
-------------------
-* Specify direction of edges as syntax (->, --, <-, <->)
-* Fix bugs.
-
-0.5.4 (2010-12-23)
-------------------
-* Remove debug codes.
-
-0.5.3 (2010-12-23)
-------------------
-* Support NodeGroup.label.
-* Implement --separate option (experimental)
-* Fix right-up edge overrapped on other nodes.
-* Support configration file: .blockdiagrc
-
-0.5.2 (2010-11-06)
-------------------
-* Fix unicode errors for UTF-8'ed SVG exportion.
-* Refactoring codes for running on GAE.
-
-0.5.1 (2010-10-26)
-------------------
-* Fix license text on diagparser.py
-* Update layout engine.
-
-0.5 (2010-10-15)
-------------------
-* Support background-image of node (SVG)
-* Support labels for edge.
-* Fix bugs.
-
-0.4.2 (2010-10-10)
-------------------
-* Support background-color of node groups.
-* Draw edge has jumped at edge's cross-points.
-* Fix bugs.
-
-0.4.1 (2010-10-07)
-------------------
-* Fix bugs.
-
-0.4 (2010-10-07)
-------------------
-* Support SVG exporting.
-* Support dashed edge drawing.
-* Support background image of nodes (PNG only)
-
-0.3.1 (2010-09-29)
-------------------
-* Fasten anti-alias process.
-* Fix text was broken on windows.
-
-0.3 (2010-09-26)
-------------------
-* Add --antialias option.
-* Fix bugs.
-
-0.2.2 (2010-09-25)
-------------------
-* Fix edge bugs.
-
-0.2.1 (2010-09-25)
-------------------
-* Fix bugs.
-* Fix package style.
-
-0.2 (2010-09-23)
-------------------
-* Update layout engine.
-* Support group { ... } sentence for create Node-Groups.
-* Support numbered badge on node (cf. A [numbered = 5])
-
-0.1 (2010-09-20)
------------------
-* first release
-
+`blockdiag` generate block-diagram image file from spec-text file.
+
+Features
+========
+* Generate block-diagram from dot like text (basic feature).
+* Multilingualization for node-label (utf-8 only).
+
+You can get some examples and generated images on
+`blockdiag.com <http://blockdiag.com/blockdiag/build/html/index.html>`_ .
+
+Setup
+=====
+
+by easy_install
+----------------
+Make environment::
+
+ $ easy_install blockdiag
+
+If you want to export as PDF format, give pdf arguments::
+
+ $ easy_install "blockdiag[pdf]"
+
+by buildout
+------------
+Make environment::
+
+ $ hg clone http://bitbucket.org/tk0miya/blockdiag
+ $ cd blockdiag
+ $ python bootstrap.py
+ $ bin/buildout
+
+Copy and modify ini file. example::
+
+ $ cp <blockdiag installed path>/blockdiag/examples/simple.diag .
+ $ vi simple.diag
+
+Please refer to `spec-text setting sample`_ section for the format of the
+`simpla.diag` configuration file.
+
+spec-text setting sample
+========================
+Few examples are available.
+You can get more examples at
+`blockdiag.com <http://blockdiag.com/blockdiag/build/html/index.html>`_ .
+
+simple.diag
+------------
+simple.diag is simply define nodes and transitions by dot-like text format::
+
+ diagram admin {
+ top_page -> config -> config_edit -> config_confirm -> top_page;
+ }
+
+screen.diag
+------------
+screen.diag is more complexly sample. diaglam nodes have a alternative label
+and some transitions::
+
+ diagram admin {
+ top_page [label = "Top page"];
+
+ foo_index [label = "List of FOOs"];
+ foo_detail [label = "Detail FOO"];
+ foo_add [label = "Add FOO"];
+ foo_add_confirm [label = "Add FOO (confirm)"];
+ foo_edit [label = "Edit FOO"];
+ foo_edit_confirm [label = "Edit FOO (confirm)"];
+ foo_delete_confirm [label = "Delete FOO (confirm)"];
+
+ bar_detail [label = "Detail of BAR"];
+ bar_edit [label = "Edit BAR"];
+ bar_edit_confirm [label = "Edit BAR (confirm)"];
+
+ logout;
+
+ top_page -> foo_index;
+ top_page -> bar_detail;
+
+ foo_index -> foo_detail;
+ foo_detail -> foo_edit;
+ foo_detail -> foo_delete_confirm;
+ foo_index -> foo_add -> foo_add_confirm -> foo_index;
+ foo_index -> foo_edit -> foo_edit_confirm -> foo_index;
+ foo_index -> foo_delete_confirm -> foo_index;
+
+ bar_detail -> bar_edit -> bar_edit_confirm -> bar_detail;
+ }
+
+
+Usage
+=====
+Execute blockdiag command::
+
+ $ blockdiag simple.diag
+ $ ls simple.png
+ simple.png
+
+
+Requirements
+============
+* Python 2.6, 2.7, 3.2, 3.3
+* Pillow 2.2.1
+* funcparserlib 0.3.6
+* setuptools
+
+
+License
+=======
+Apache License 2.0
+
+
+History
+=======
+
+1.3.2 (2013-11-19)
+------------------
+* Fix bugs
+
+1.3.1 (2013-10-22)
+------------------
+* Fix bugs
+
+1.3.0 (2013-10-05)
+------------------
+* Support python 3.2 and 3.3 (thanks to @masayuko)
+* Drop supports for python 2.4 and 2.5
+* Replace dependency: PIL -> Pillow
+
+1.2.4 (2012-11-21)
+------------------
+* Fix bugs
+
+1.2.3 (2012-11-05)
+------------------
+* Fix bugs
+
+1.2.2 (2012-10-28)
+------------------
+* Fix bugs
+
+1.2.1 (2012-10-28)
+------------------
+* Add external imagedraw plugin supports
+* Add node attribute: label_orientation*
+* Fix bugs
+
+1.2.0 (2012-10-22)
+------------------
+* Optimize algorithm for rendering shadow
+* Add options to docutils directive
+* Fix bugs
+
+1.1.8 (2012-09-28)
+------------------
+* Add --ignore-pil option
+* Fix bugs
+
+1.1.7 (2012-09-20)
+------------------
+* Add diagram attribute: shadow_style
+* Add font path for centos 6.2
+* Add a setting 'antialias' in the configuration file
+* Fix bugs
+
+1.1.6 (2012-06-06)
+------------------
+* Support for readthedocs.org
+* reST directive supports :caption: option
+* Fix bugs
+
+1.1.5 (2012-04-22)
+------------------
+* Embed source code to SVG document as description
+* Fix bugs
+
+1.1.4 (2012-03-15)
+------------------
+* Add new edge.hstyles: oneone, onemany, manyone, manymany
+* Add edge attribute: description (for build description-tables)
+* Fix bugs
+
+1.1.3 (2012-02-13)
+------------------
+* Add new edge type for data-models (thanks to David Lang)
+* Add --no-transparency option
+* Fix bugs
+
+1.1.2 (2011-12-26)
+------------------
+* Support font-index for TrueType Font Collections (.ttc file)
+* Allow to use reST syntax in descriptions of nodes
+* Fix bugs
+
+1.1.1 (2011-11-27)
+------------------
+* Add node attribute: href (thanks to @r_rudi!)
+* Fix bugs
+
+1.1.0 (2011-11-19)
+------------------
+* Add shape: square and circle
+* Add fontfamily attribute for switching fontface
+* Fix bugs
+
+1.0.3 (2011-11-13)
+------------------
+* Add plugin: attributes
+* Change plugin syntax; (cf. plugin attributes [attr = value, attr, value])
+* Fix bugs
+
+1.0.2 (2011-11-07)
+------------------
+* Fix bugs
+
+1.0.1 (2011-11-06)
+------------------
+* Add group attribute: shape
+* Fix bugs
+
+1.0.0 (2011-11-04)
+------------------
+* Add node attribute: linecolor
+* Rename diagram attributes:
+ * fontsize -> default_fontsize
+ * default_line_color -> default_linecolor
+ * default_text_color -> default_textcolor
+* Add docutils extention
+* Fix bugs
+
+0.9.7 (2011-11-01)
+------------------
+* Add node attribute: fontsize
+* Add edge attributes: thick, fontsize
+* Add group attribute: fontsize
+* Change color of shadow in PDF mode
+* Add class feature (experimental)
+* Add handler-plugin framework (experimental)
+
+0.9.6 (2011-10-22)
+------------------
+* node.style supports dashed_array format style
+* Fix bugs
+
+0.9.5 (2011-10-19)
+------------------
+* Add node attributes: width and height
+* Fix bugs
+
+0.9.4 (2011-10-07)
+------------------
+* Fix bugs
+
+0.9.3 (2011-10-06)
+------------------
+* Replace SVG core by original's (simplesvg.py)
+* Refactored
+* Fix bugs
+
+0.9.2 (2011-09-30)
+------------------
+* Add node attribute: textcolor
+* Add group attribute: textcolor
+* Add edge attribute: textcolor
+* Add diagram attributes: default_text_attribute
+* Fix beginpoint shape and endpoint shape were reversed
+* Fix bugs
+
+0.9.1 (2011-09-26)
+------------------
+* Add diagram attributes: default_node_color, default_group_color and default_line_color
+* Fix bugs
+
+0.9.0 (2011-09-25)
+------------------
+* Add icon attribute to node
+* Make transparency to background of PNG images
+* Fix bugs
+
+0.8.9 (2011-08-09)
+------------------
+* Fix bugs
+
+0.8.8 (2011-08-08)
+------------------
+* Fix bugs
+
+0.8.7 (2011-08-06)
+------------------
+* Fix bugs
+
+0.8.6 (2011-08-01)
+------------------
+* Support Pillow as replacement of PIL (experimental)
+* Fix bugs
+
+0.8.5 (2011-07-31)
+------------------
+* Allow dot characters in node_id
+* Fix bugs
+
+0.8.4 (2011-07-05)
+------------------
+* Fix bugs
+
+0.8.3 (2011-07-03)
+------------------
+* Support input from stdin
+* Fix bugs
+
+0.8.2 (2011-06-29)
+------------------
+* Add node.stacked
+* Add node shapes: dots, none
+* Add hiragino-font to font search list
+* Support background image fetching from web
+* Add diagram.edge_layout (experimental)
+* Fix bugs
+
+0.8.1 (2011-05-14)
+------------------
+* Change license to Apache License 2.0
+* Fix bugs
+
+0.8.0 (2011-05-04)
+------------------
+* Add --separate option and --version option
+* Fix bugs
+
+0.7.8 (2011-04-19)
+------------------
+* Update layout engine
+* Update requirements: PIL >= 1.1.5
+* Update parser for tokenize performance
+* Add --nodoctype option
+* Fix bugs
+* Add many testcases
+
+0.7.7 (2011-03-29)
+------------------
+* Fix bugs
+
+0.7.6 (2011-03-26)
+------------------
+* Add new layout manager for portrait edges
+* Fix bugs
+
+0.7.5 (2011-03-20)
+------------------
+* Support multiple nodes relations (cf. A -> B, C)
+* Support node group declaration at attribute of nodes
+* Fix bugs
+
+0.7.4 (2011-03-08)
+------------------
+* Fix bugs
+
+0.7.3 (2011-03-02)
+------------------
+* Use UTF-8 characters as Name token (by @swtw7466)
+* Fix htmlentities included in labels was not escaped on SVG images
+* Fix bugs
+
+0.7.2 (2011-02-28)
+------------------
+* Add default_shape attribute to diagram
+
+0.7.1 (2011-02-27)
+------------------
+* Fix edge has broken with antialias option
+
+0.7.0 (2011-02-25)
+------------------
+* Support node shape
+
+0.6.7 (2011-02-12)
+------------------
+* Change noderenderer interface to new style
+* Render dashed ellipse more clearly (contributed by @cocoatomo)
+* Support PDF exporting
+
+0.6.6 (2011-01-31)
+------------------
+* Support diagram.shape_namespace
+* Add new node shapes; mail, cloud, beginpoint, endpoint, minidiamond, actor
+* Support plug-in structure to install node shapes
+* Fix bugs
+
+0.6.5 (2011-01-18)
+------------------
+* Support node shape (experimental)
+
+0.6.4 (2011-01-17)
+------------------
+* Fix bugs
+
+0.6.3 (2011-01-15)
+------------------
+* Fix bugs
+
+0.6.2 (2011-01-08)
+------------------
+* Fix bugs
+
+0.6.1 (2011-01-07)
+------------------
+* Implement 'folded' attribute for edge
+* Refactor layout engine
+
+0.6 (2011-01-02)
+------------------
+* Support nested groups.
+
+0.5.5 (2010-12-24)
+------------------
+* Specify direction of edges as syntax (->, --, <-, <->)
+* Fix bugs.
+
+0.5.4 (2010-12-23)
+------------------
+* Remove debug codes.
+
+0.5.3 (2010-12-23)
+------------------
+* Support NodeGroup.label.
+* Implement --separate option (experimental)
+* Fix right-up edge overrapped on other nodes.
+* Support configration file: .blockdiagrc
+
+0.5.2 (2010-11-06)
+------------------
+* Fix unicode errors for UTF-8'ed SVG exportion.
+* Refactoring codes for running on GAE.
+
+0.5.1 (2010-10-26)
+------------------
+* Fix license text on diagparser.py
+* Update layout engine.
+
+0.5 (2010-10-15)
+------------------
+* Support background-image of node (SVG)
+* Support labels for edge.
+* Fix bugs.
+
+0.4.2 (2010-10-10)
+------------------
+* Support background-color of node groups.
+* Draw edge has jumped at edge's cross-points.
+* Fix bugs.
+
+0.4.1 (2010-10-07)
+------------------
+* Fix bugs.
+
+0.4 (2010-10-07)
+------------------
+* Support SVG exporting.
+* Support dashed edge drawing.
+* Support background image of nodes (PNG only)
+
+0.3.1 (2010-09-29)
+------------------
+* Fasten anti-alias process.
+* Fix text was broken on windows.
+
+0.3 (2010-09-26)
+------------------
+* Add --antialias option.
+* Fix bugs.
+
+0.2.2 (2010-09-25)
+------------------
+* Fix edge bugs.
+
+0.2.1 (2010-09-25)
+------------------
+* Fix bugs.
+* Fix package style.
+
+0.2 (2010-09-23)
+------------------
+* Update layout engine.
+* Support group { ... } sentence for create Node-Groups.
+* Support numbered badge on node (cf. A [numbered = 5])
+
+0.1 (2010-09-20)
+-----------------
+* first release
+
diff --git a/bootstrap.py b/bootstrap.py
index 716795f..1b28969 100644
--- a/bootstrap.py
+++ b/bootstrap.py
@@ -18,75 +18,14 @@ The script accepts buildout command-line options, so you can
use the -c option to specify an alternate configuration file.
"""
-import os, shutil, sys, tempfile, urllib, urllib2, subprocess
-from optparse import OptionParser
-
-if sys.platform == 'win32':
- def quote(c):
- if ' ' in c:
- return '"%s"' % c # work around spawn lamosity on windows
- else:
- return c
-else:
- quote = str
-
-# See zc.buildout.easy_install._has_broken_dash_S for motivation and comments.
-stdout, stderr = subprocess.Popen(
- [sys.executable, '-Sc',
- 'try:\n'
- ' import ConfigParser\n'
- 'except ImportError:\n'
- ' print 1\n'
- 'else:\n'
- ' print 0\n'],
- stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
-has_broken_dash_S = bool(int(stdout.strip()))
-
-# In order to be more robust in the face of system Pythons, we want to
-# run without site-packages loaded. This is somewhat tricky, in
-# particular because Python 2.6's distutils imports site, so starting
-# with the -S flag is not sufficient. However, we'll start with that:
-if not has_broken_dash_S and 'site' in sys.modules:
- # We will restart with python -S.
- args = sys.argv[:]
- args[0:0] = [sys.executable, '-S']
- args = map(quote, args)
- os.execv(sys.executable, args)
-# Now we are running with -S. We'll get the clean sys.path, import site
-# because distutils will do it later, and then reset the path and clean
-# out any namespace packages from site-packages that might have been
-# loaded by .pth files.
-clean_path = sys.path[:]
-import site # imported because of its side effects
-sys.path[:] = clean_path
-for k, v in sys.modules.items():
- if k in ('setuptools', 'pkg_resources') or (
- hasattr(v, '__path__') and
- len(v.__path__) == 1 and
- not os.path.exists(os.path.join(v.__path__[0], '__init__.py'))):
- # This is a namespace package. Remove it.
- sys.modules.pop(k)
-
-is_jython = sys.platform.startswith('java')
-
-setuptools_source = 'http://peak.telecommunity.com/dist/ez_setup.py'
-distribute_source = 'http://python-distribute.org/distribute_setup.py'
+import os
+import shutil
+import sys
+import tempfile
+from optparse import OptionParser
-# parsing arguments
-def normalize_to_url(option, opt_str, value, parser):
- if value:
- if '://' not in value: # It doesn't smell like a URL.
- value = 'file://%s' % (
- urllib.pathname2url(
- os.path.abspath(os.path.expanduser(value))),)
- if opt_str == '--download-base' and not value.endswith('/'):
- # Download base needs a trailing slash to make the world happy.
- value += '/'
- else:
- value = None
- name = opt_str[2:].replace('-', '_')
- setattr(parser.values, name, value)
+tmpeggs = tempfile.mkdtemp()
usage = '''\
[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options]
@@ -96,31 +35,13 @@ Bootstraps a buildout-based project.
Simply run this script in a directory containing a buildout.cfg, using the
Python that you want bin/buildout to use.
-Note that by using --setup-source and --download-base to point to
-local resources, you can keep this script from going over the network.
+Note that by using --find-links to point to local resources, you can keep
+this script from going over the network.
'''
parser = OptionParser(usage=usage)
-parser.add_option("-v", "--version", dest="version",
- help="use a specific zc.buildout version")
-parser.add_option("-d", "--distribute",
- action="store_true", dest="use_distribute", default=False,
- help="Use Distribute rather than Setuptools.")
-parser.add_option("--setup-source", action="callback", dest="setup_source",
- callback=normalize_to_url, nargs=1, type="string",
- help=("Specify a URL or file location for the setup file. "
- "If you use Setuptools, this will default to " +
- setuptools_source + "; if you use Distribute, this "
- "will default to " + distribute_source + "."))
-parser.add_option("--download-base", action="callback", dest="download_base",
- callback=normalize_to_url, nargs=1, type="string",
- help=("Specify a URL or directory for downloading "
- "zc.buildout and either Setuptools or Distribute. "
- "Defaults to PyPI."))
-parser.add_option("--eggs",
- help=("Specify a directory for storing eggs. Defaults to "
- "a temporary directory that is deleted when the "
- "bootstrap script completes."))
+parser.add_option("-v", "--version", help="use a specific zc.buildout version")
+
parser.add_option("-t", "--accept-buildout-test-releases",
dest='accept_buildout_test_releases',
action="store_true", default=False,
@@ -130,50 +51,38 @@ parser.add_option("-t", "--accept-buildout-test-releases",
"extensions for you. If you use this flag, "
"bootstrap and buildout will get the newest releases "
"even if they are alphas or betas."))
-parser.add_option("-c", None, action="store", dest="config_file",
- help=("Specify the path to the buildout configuration "
- "file to be used."))
-
-options, orig_args = parser.parse_args()
-
-args = []
-
-# if -c was provided, we push it back into args for buildout's main function
-if options.config_file is not None:
- args += ['-c', options.config_file]
+parser.add_option("-c", "--config-file",
+ help=("Specify the path to the buildout configuration "
+ "file to be used."))
+parser.add_option("-f", "--find-links",
+ help=("Specify a URL to search for buildout releases"))
-if options.eggs:
- eggs_dir = os.path.abspath(os.path.expanduser(options.eggs))
-else:
- eggs_dir = tempfile.mkdtemp()
-if options.setup_source is None:
- if options.use_distribute:
- options.setup_source = distribute_source
- else:
- options.setup_source = setuptools_source
+options, args = parser.parse_args()
-if options.accept_buildout_test_releases:
- args.append('buildout:accept-buildout-test-releases=true')
+######################################################################
+# load/install setuptools
+to_reload = False
try:
import pkg_resources
- import setuptools # A flag. Sometimes pkg_resources is installed alone.
- if not hasattr(pkg_resources, '_distribute'):
- raise ImportError
+ import setuptools
except ImportError:
- ez_code = urllib2.urlopen(
- options.setup_source).read().replace('\r\n', '\n')
ez = {}
- exec ez_code in ez
- setup_args = dict(to_dir=eggs_dir, download_delay=0)
- if options.download_base:
- setup_args['download_base'] = options.download_base
- if options.use_distribute:
- setup_args['no_fake'] = True
+
+ try:
+ from urllib.request import urlopen
+ except ImportError:
+ from urllib2 import urlopen
+
+ # XXX use a more permanent ez_setup.py URL when available.
+ exec(urlopen('https://bitbucket.org/pypa/setuptools/raw/0.7.2/ez_setup.py'
+ ).read(), ez)
+ setup_args = dict(to_dir=tmpeggs, download_delay=0)
ez['use_setuptools'](**setup_args)
- if 'pkg_resources' in sys.modules:
- reload(sys.modules['pkg_resources'])
+
+ if to_reload:
+ reload(pkg_resources)
import pkg_resources
# This does not (always?) update the default working set. We will
# do it.
@@ -181,31 +90,26 @@ except ImportError:
if path not in pkg_resources.working_set.entries:
pkg_resources.working_set.add_entry(path)
-cmd = [quote(sys.executable),
- '-c',
- quote('from setuptools.command.easy_install import main; main()'),
- '-mqNxd',
- quote(eggs_dir)]
+######################################################################
+# Install buildout
-if not has_broken_dash_S:
- cmd.insert(1, '-S')
+ws = pkg_resources.working_set
+
+cmd = [sys.executable, '-c',
+ 'from setuptools.command.easy_install import main; main()',
+ '-mZqNxd', tmpeggs]
-find_links = options.download_base
-if not find_links:
- find_links = os.environ.get('bootstrap-testing-find-links')
+find_links = os.environ.get(
+ 'bootstrap-testing-find-links',
+ options.find_links or
+ ('http://downloads.buildout.org/'
+ if options.accept_buildout_test_releases else None)
+ )
if find_links:
- cmd.extend(['-f', quote(find_links)])
+ cmd.extend(['-f', find_links])
-if options.use_distribute:
- setup_requirement = 'distribute'
-else:
- setup_requirement = 'setuptools'
-ws = pkg_resources.working_set
-setup_requirement_path = ws.find(
- pkg_resources.Requirement.parse(setup_requirement)).location
-env = dict(
- os.environ,
- PYTHONPATH=setup_requirement_path)
+setuptools_path = ws.find(
+ pkg_resources.Requirement.parse('setuptools')).location
requirement = 'zc.buildout'
version = options.version
@@ -220,7 +124,7 @@ if version is None and not options.accept_buildout_test_releases:
return False
return True
index = setuptools.package_index.PackageIndex(
- search_path=[setup_requirement_path])
+ search_path=[setuptools_path])
if find_links:
index.add_find_links((find_links,))
req = pkg_resources.Requirement.parse(requirement)
@@ -242,25 +146,25 @@ if version:
requirement = '=='.join((requirement, version))
cmd.append(requirement)
-if is_jython:
- import subprocess
- exitcode = subprocess.Popen(cmd, env=env).wait()
-else: # Windows prefers this, apparently; otherwise we would prefer subprocess
- exitcode = os.spawnle(*([os.P_WAIT, sys.executable] + cmd + [env]))
-if exitcode != 0:
- sys.stdout.flush()
- sys.stderr.flush()
- print ("An error occurred when trying to install zc.buildout. "
- "Look above this message for any errors that "
- "were output by easy_install.")
- sys.exit(exitcode)
+import subprocess
+if subprocess.call(cmd, env=dict(os.environ, PYTHONPATH=setuptools_path)) != 0:
+ raise Exception(
+ "Failed to execute command:\n%s",
+ repr(cmd)[1:-1])
-ws.add_entry(eggs_dir)
+######################################################################
+# Import and run buildout
+
+ws.add_entry(tmpeggs)
ws.require(requirement)
import zc.buildout.buildout
-if orig_args:
- # run buildout with commands passed to bootstrap.py, then actually bootstrap
- zc.buildout.buildout.main(args + orig_args)
-zc.buildout.buildout.main(args + ['bootstrap'])
-if not options.eggs: # clean up temporary egg directory
- shutil.rmtree(eggs_dir)
+
+if not [a for a in args if '=' not in a]:
+ args.append('bootstrap')
+
+# if -c was provided, we push it back into args for buildout' main function
+if options.config_file is not None:
+ args[0:0] = ['-c', options.config_file]
+
+zc.buildout.buildout.main(args)
+shutil.rmtree(tmpeggs)
diff --git a/buildout.cfg b/buildout.cfg
index cb44b9f..69df527 100644
--- a/buildout.cfg
+++ b/buildout.cfg
@@ -1,49 +1,45 @@
-[buildout]
-parts = blockdiag test coverage pyflakes pylint
-
-develop = .
-
-[blockdiag]
-recipe = zc.recipe.egg
-eggs = blockdiag[rst]
-interpreter = py
-
-[test]
-recipe = pbp.recipe.noserunner
-eggs =
- blockdiag[rst]
- blockdiag[test]
- coverage
- unittest-xml-reporting
-
-[coverage]
-recipe = zc.recipe.egg
-eggs = coverage
-
-[pyflakes]
-recipe = zc.recipe.egg
-eggs = pyflakes
-scripts = pyflakes
-entry-points = pyflakes=pyflakes.scripts.pyflakes:main
-
-[pylint]
-recipe = zc.recipe.egg
-eggs =
- pylint
- blockdiag[test]
-scripts = pylint
-
-[test-extra]
-recipe = iw.recipe.cmd:py
-on_install = true
-cmds =
- >>> url = "http://sourceforge.jp/frs/redir.php?m=jaist&f=%2Fvlgothic%2F46966%2FVLGothic-20100416.zip"
- >>> buildout_dir = buildout.get('directory', '.')
- >>> path = os.path.join(buildout_dir, 'src/blockdiag/tests/truetype')
- >>> if not os.path.exists(path):
- ... os.makedirs(path)
- ... import cStringIO, urllib2, zipfile
- ... archive = urllib2.urlopen(url).read()
- ... zip = zipfile.ZipFile(cStringIO.StringIO(archive))
- ... ttf = zip.read('VLGothic/VL-PGothic-Regular.ttf')
- ... open(os.path.join(path, 'VL-PGothic-Regular.ttf'), 'wb').write(ttf)
+[buildout]
+parts = blockdiag test tox static_analysis
+
+develop = .
+
+[blockdiag]
+recipe = zc.recipe.egg
+eggs = blockdiag[rst]
+interpreter = py
+
+[test]
+recipe = pbp.recipe.noserunner
+eggs =
+ blockdiag[rst]
+ blockdiag[test]
+ coverage
+ unittest-xml-reporting
+
+[tox]
+recipe = zc.recipe.egg
+eggs =
+ tox
+ detox
+
+[static_analysis]
+recipe = zc.recipe.egg
+eggs =
+ coverage
+ flake8
+ pylint
+
+[test-extra]
+recipe = iw.recipe.cmd:py
+on_install = true
+cmds =
+ >>> url = "http://sourceforge.jp/frs/redir.php?m=jaist&f=%2Fvlgothic%2F46966%2FVLGothic-20100416.zip"
+ >>> buildout_dir = buildout.get('directory', '.')
+ >>> path = os.path.join(buildout_dir, 'src/blockdiag/tests/truetype')
+ >>> if not os.path.exists(path):
+ ... os.makedirs(path)
+ ... import cStringIO, urllib2, zipfile
+ ... archive = urllib2.urlopen(url).read()
+ ... zip = zipfile.ZipFile(cStringIO.StringIO(archive))
+ ... ttf = zip.read('VLGothic/VL-PGothic-Regular.ttf')
+ ... open(os.path.join(path, 'VL-PGothic-Regular.ttf'), 'wb').write(ttf)
diff --git a/examples/blockdiagrc b/examples/blockdiagrc
new file mode 100644
index 0000000..6572995
--- /dev/null
+++ b/examples/blockdiagrc
@@ -0,0 +1,2 @@
+[blockdiag]
+fontpath = /usr/share/fonts/truetype/kochi/kochi-mincho.ttf
diff --git a/setup.cfg b/setup.cfg
index be3189c..18b023c 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -9,3 +9,6 @@ build-base = _build
[sdist]
formats = gztar
+[flake8]
+ignore = _
+
diff --git a/setup.py b/setup.py
index 1f72f01..0a0f1a7 100644
--- a/setup.py
+++ b/setup.py
@@ -1,129 +1,102 @@
-# -*- coding: utf-8 -*-
-from setuptools import setup, find_packages
-import os, sys
-import pkg_resources
-
-sys.path.insert(0, 'src')
-import blockdiag
-
-
-def is_installed(name):
- try:
- pkg_resources.get_distribution(name)
- return True
- except:
- return False
-
-
-long_description = \
- open(os.path.join("src","README.txt")).read() + \
- open(os.path.join("src","TODO.txt")).read()
-
-classifiers = [
- "Development Status :: 4 - Beta",
- "Intended Audience :: System Administrators",
- "License :: OSI Approved :: Apache Software License",
- "Programming Language :: Python",
- "Topic :: Software Development",
- "Topic :: Software Development :: Documentation",
- "Topic :: Text Processing :: Markup",
-]
-
-requires = ['setuptools',
- 'funcparserlib',
- 'webcolors']
-deplinks = []
-
-# For readthedocs.org
-# http://read-the-docs.readthedocs.org/en/latest/faq.html#how-do-i-change-behavior-for-read-the-docs
-# Find imaging libraries
-
-if 'READTHEDOCS' in os.environ:
- requires.append('Pillow')
-elif is_installed('PIL'):
- requires.append('PIL')
-elif is_installed('Pillow'):
- requires.append('Pillow')
-elif sys.platform == 'win32':
- requires.append('Pillow')
-else:
- requires.append('PIL')
-
-
-# only for Python2.6
-if sys.version_info > (2, 6) and sys.version_info < (2, 7):
- requires.append('OrderedDict')
-
-
-setup(
- name='blockdiag',
- version=blockdiag.__version__,
- description='blockdiag generate block-diagram image file from spec-text file.',
- long_description=long_description,
- classifiers=classifiers,
- keywords=['diagram','generator'],
- author='Takeshi Komiya',
- author_email='i.tkomiya at gmail.com',
- url='http://blockdiag.com/',
- download_url='http://pypi.python.org/pypi/blockdiag',
- license='Apache License 2.0',
- py_modules=['blockdiag_sphinxhelper'],
- packages=find_packages('src'),
- package_dir={'': 'src'},
- package_data = {'': ['buildout.cfg']},
- include_package_data=True,
- install_requires=requires,
- extras_require=dict(
- test=[
- 'Nose',
- 'pep8>=1.3',
- 'unittest2',
- ],
- pdf=[
- 'reportlab',
- ],
- rst=[
- 'docutils',
- ],
- ),
- dependency_links=deplinks,
- test_suite='nose.collector',
- tests_require=['Nose','pep8'],
- entry_points="""
- [console_scripts]
- blockdiag = blockdiag.command:main
-
- [blockdiag_noderenderer]
- box = blockdiag.noderenderer.box
- square = blockdiag.noderenderer.square
- roundedbox = blockdiag.noderenderer.roundedbox
- diamond = blockdiag.noderenderer.diamond
- minidiamond = blockdiag.noderenderer.minidiamond
- mail = blockdiag.noderenderer.mail
- note = blockdiag.noderenderer.note
- cloud = blockdiag.noderenderer.cloud
- circle = blockdiag.noderenderer.circle
- ellipse = blockdiag.noderenderer.ellipse
- beginpoint = blockdiag.noderenderer.beginpoint
- endpoint = blockdiag.noderenderer.endpoint
- actor = blockdiag.noderenderer.actor
- flowchart.database = blockdiag.noderenderer.flowchart.database
- flowchart.input = blockdiag.noderenderer.flowchart.input
- flowchart.loopin = blockdiag.noderenderer.flowchart.loopin
- flowchart.loopout = blockdiag.noderenderer.flowchart.loopout
- flowchart.terminator = blockdiag.noderenderer.flowchart.terminator
- textbox = blockdiag.noderenderer.textbox
- dots = blockdiag.noderenderer.dots
- none = blockdiag.noderenderer.none
-
- [blockdiag_plugins]
- attributes = blockdiag.plugins.attributes
- autoclass = blockdiag.plugins.autoclass
-
- [blockdiag_imagedrawers]
- imagedraw_png = blockdiag.imagedraw.png
- imagedraw_svg = blockdiag.imagedraw.svg
- imagedraw_pdf = blockdiag.imagedraw.pdf
- """,
-)
-
+# -*- coding: utf-8 -*-
+import os
+import sys
+from setuptools import setup, find_packages
+
+sys.path.insert(0, 'src')
+import blockdiag
+
+classifiers = [
+ "Development Status :: 5 - Production/Stable",
+ "Intended Audience :: System Administrators",
+ "License :: OSI Approved :: Apache Software License",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 2.6",
+ "Programming Language :: Python :: 2.7",
+ "Programming Language :: Python :: 3.2",
+ "Programming Language :: Python :: 3.3",
+ "Topic :: Software Development",
+ "Topic :: Software Development :: Documentation",
+ "Topic :: Text Processing :: Markup",
+]
+
+requires = ['setuptools',
+ 'funcparserlib',
+ 'webcolors',
+ 'Pillow']
+test_requires = ['Nose',
+ 'pep8>=1.3']
+
+
+# only for Python2.6
+if sys.version_info > (2, 6) and sys.version_info < (2, 7):
+ requires.append('OrderedDict')
+ test_requires.append('unittest2')
+
+
+setup(
+ name='blockdiag',
+ version=blockdiag.__version__,
+ description='blockdiag generates block-diagram image from text',
+ long_description=open("README.rst").read(),
+ classifiers=classifiers,
+ keywords=['diagram', 'generator'],
+ author='Takeshi Komiya',
+ author_email='i.tkomiya at gmail.com',
+ url='http://blockdiag.com/',
+ download_url='http://pypi.python.org/pypi/blockdiag',
+ license='Apache License 2.0',
+ py_modules=['blockdiag_sphinxhelper'],
+ packages=find_packages('src'),
+ package_dir={'': 'src'},
+ package_data={'': ['buildout.cfg']},
+ include_package_data=True,
+ install_requires=requires,
+ extras_require=dict(
+ test=test_requires,
+ pdf=[
+ 'reportlab',
+ ],
+ rst=[
+ 'docutils',
+ ],
+ ),
+ test_suite='nose.collector',
+ tests_require=test_requires,
+ entry_points="""
+ [console_scripts]
+ blockdiag = blockdiag.command:main
+
+ [blockdiag_noderenderer]
+ box = blockdiag.noderenderer.box
+ square = blockdiag.noderenderer.square
+ roundedbox = blockdiag.noderenderer.roundedbox
+ diamond = blockdiag.noderenderer.diamond
+ minidiamond = blockdiag.noderenderer.minidiamond
+ mail = blockdiag.noderenderer.mail
+ note = blockdiag.noderenderer.note
+ cloud = blockdiag.noderenderer.cloud
+ circle = blockdiag.noderenderer.circle
+ ellipse = blockdiag.noderenderer.ellipse
+ beginpoint = blockdiag.noderenderer.beginpoint
+ endpoint = blockdiag.noderenderer.endpoint
+ actor = blockdiag.noderenderer.actor
+ flowchart.database = blockdiag.noderenderer.flowchart.database
+ flowchart.input = blockdiag.noderenderer.flowchart.input
+ flowchart.loopin = blockdiag.noderenderer.flowchart.loopin
+ flowchart.loopout = blockdiag.noderenderer.flowchart.loopout
+ flowchart.terminator = blockdiag.noderenderer.flowchart.terminator
+ textbox = blockdiag.noderenderer.textbox
+ dots = blockdiag.noderenderer.dots
+ none = blockdiag.noderenderer.none
+
+ [blockdiag_plugins]
+ attributes = blockdiag.plugins.attributes
+ autoclass = blockdiag.plugins.autoclass
+
+ [blockdiag_imagedrawers]
+ imagedraw_png = blockdiag.imagedraw.png
+ imagedraw_svg = blockdiag.imagedraw.svg
+ imagedraw_pdf = blockdiag.imagedraw.pdf
+ """,
+)
diff --git a/src/TODO.txt b/src/TODO.txt
deleted file mode 100644
index b28943a..0000000
--- a/src/TODO.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-Todos
-======
-
-Functionals
-------------
-* Reimplement --separate option
-* Support diagram legends
-* Support other block diagram structure
-
-Known Issues
--------------
-* Fix some experimental features.
-* PDF renderer does not support blur shadow
-* PDF renderer does not support path rendering
diff --git a/src/blockdiag.egg-info/PKG-INFO b/src/blockdiag.egg-info/PKG-INFO
index 74aa02e..be630f3 100644
--- a/src/blockdiag.egg-info/PKG-INFO
+++ b/src/blockdiag.egg-info/PKG-INFO
@@ -1,509 +1,513 @@
-Metadata-Version: 1.1
+Metadata-Version: 1.0
Name: blockdiag
-Version: 1.2.4
-Summary: blockdiag generate block-diagram image file from spec-text file.
+Version: 1.3.2
+Summary: blockdiag generates block-diagram image from text
Home-page: http://blockdiag.com/
Author: Takeshi Komiya
Author-email: i.tkomiya at gmail.com
License: Apache License 2.0
Download-URL: http://pypi.python.org/pypi/blockdiag
-Description: `blockdiag` generate block-diagram image file from spec-text file.
-
- Features
- ========
- * Generate block-diagram from dot like text (basic feature).
- * Multilingualization for node-label (utf-8 only).
-
- You can get some examples and generated images on
- `blockdiag.com <http://blockdiag.com/blockdiag/build/html/index.html>`_ .
-
- Setup
- =====
-
- by easy_install
- ----------------
- Make environment::
-
- $ easy_install blockdiag
-
- If you want to export as PDF format, give pdf arguments::
-
- $ easy_install "blockdiag[pdf]"
-
- by buildout
- ------------
- Make environment::
-
- $ hg clone http://bitbucket.org/tk0miya/blockdiag
- $ cd blockdiag
- $ python bootstrap.py
- $ bin/buildout
-
- Copy and modify ini file. example::
-
- $ cp <blockdiag installed path>/blockdiag/examples/simple.diag .
- $ vi simple.diag
-
- Please refer to `spec-text setting sample`_ section for the format of the
- `simpla.diag` configuration file.
-
- spec-text setting sample
- ========================
- Few examples are available.
- You can get more examples at
- `blockdiag.com <http://blockdiag.com/blockdiag/build/html/index.html>`_ .
-
- simple.diag
- ------------
- simple.diag is simply define nodes and transitions by dot-like text format::
-
- diagram admin {
- top_page -> config -> config_edit -> config_confirm -> top_page;
- }
-
- screen.diag
- ------------
- screen.diag is more complexly sample. diaglam nodes have a alternative label
- and some transitions::
-
- diagram admin {
- top_page [label = "Top page"];
-
- foo_index [label = "List of FOOs"];
- foo_detail [label = "Detail FOO"];
- foo_add [label = "Add FOO"];
- foo_add_confirm [label = "Add FOO (confirm)"];
- foo_edit [label = "Edit FOO"];
- foo_edit_confirm [label = "Edit FOO (confirm)"];
- foo_delete_confirm [label = "Delete FOO (confirm)"];
-
- bar_detail [label = "Detail of BAR"];
- bar_edit [label = "Edit BAR"];
- bar_edit_confirm [label = "Edit BAR (confirm)"];
-
- logout;
-
- top_page -> foo_index;
- top_page -> bar_detail;
-
- foo_index -> foo_detail;
- foo_detail -> foo_edit;
- foo_detail -> foo_delete_confirm;
- foo_index -> foo_add -> foo_add_confirm -> foo_index;
- foo_index -> foo_edit -> foo_edit_confirm -> foo_index;
- foo_index -> foo_delete_confirm -> foo_index;
-
- bar_detail -> bar_edit -> bar_edit_confirm -> bar_detail;
- }
-
-
- Usage
- =====
- Execute blockdiag command::
-
- $ blockdiag simple.diag
- $ ls simple.png
- simple.png
-
-
- Requirements
- ============
- * Python 2.4 or later (not support 3.x)
- * Python Imaging Library 1.1.5 or later.
- * funcparserlib 0.3.4 or later.
- * setuptools or distribute.
-
-
- License
- =======
- Apache License 2.0
-
-
- History
- =======
-
- 1.2.4 (2012-11-21)
- ------------------
- * Fix bugs
-
- 1.2.3 (2012-11-05)
- ------------------
- * Fix bugs
-
- 1.2.2 (2012-10-28)
- ------------------
- * Fix bugs
-
- 1.2.1 (2012-10-28)
- ------------------
- * Add external imagedraw plugin supports
- * Add node attribute: label_orientation*
- * Fix bugs
-
- 1.2.0 (2012-10-22)
- ------------------
- * Optimize algorithm for rendering shadow
- * Add options to docutils directive
- * Fix bugs
-
- 1.1.8 (2012-09-28)
- ------------------
- * Add --ignore-pil option
- * Fix bugs
-
- 1.1.7 (2012-09-20)
- ------------------
- * Add diagram attribute: shadow_style
- * Add font path for centos 6.2
- * Add a setting 'antialias' in the configuration file
- * Fix bugs
-
- 1.1.6 (2012-06-06)
- ------------------
- * Support for readthedocs.org
- * reST directive supports :caption: option
- * Fix bugs
-
- 1.1.5 (2012-04-22)
- ------------------
- * Embed source code to SVG document as description
- * Fix bugs
-
- 1.1.4 (2012-03-15)
- ------------------
- * Add new edge.hstyles: oneone, onemany, manyone, manymany
- * Add edge attribute: description (for build description-tables)
- * Fix bugs
-
- 1.1.3 (2012-02-13)
- ------------------
- * Add new edge type for data-models (thanks to David Lang)
- * Add --no-transparency option
- * Fix bugs
-
- 1.1.2 (2011-12-26)
- ------------------
- * Support font-index for TrueType Font Collections (.ttc file)
- * Allow to use reST syntax in descriptions of nodes
- * Fix bugs
-
- 1.1.1 (2011-11-27)
- ------------------
- * Add node attribute: href (thanks to @r_rudi!)
- * Fix bugs
-
- 1.1.0 (2011-11-19)
- ------------------
- * Add shape: square and circle
- * Add fontfamily attribute for switching fontface
- * Fix bugs
-
- 1.0.3 (2011-11-13)
- ------------------
- * Add plugin: attributes
- * Change plugin syntax; (cf. plugin attributes [attr = value, attr, value])
- * Fix bugs
-
- 1.0.2 (2011-11-07)
- ------------------
- * Fix bugs
-
- 1.0.1 (2011-11-06)
- ------------------
- * Add group attribute: shape
- * Fix bugs
-
- 1.0.0 (2011-11-04)
- ------------------
- * Add node attribute: linecolor
- * Rename diagram attributes:
- * fontsize -> default_fontsize
- * default_line_color -> default_linecolor
- * default_text_color -> default_textcolor
- * Add docutils extention
- * Fix bugs
-
- 0.9.7 (2011-11-01)
- ------------------
- * Add node attribute: fontsize
- * Add edge attributes: thick, fontsize
- * Add group attribute: fontsize
- * Change color of shadow in PDF mode
- * Add class feature (experimental)
- * Add handler-plugin framework (experimental)
-
- 0.9.6 (2011-10-22)
- ------------------
- * node.style supports dashed_array format style
- * Fix bugs
-
- 0.9.5 (2011-10-19)
- ------------------
- * Add node attributes: width and height
- * Fix bugs
-
- 0.9.4 (2011-10-07)
- ------------------
- * Fix bugs
-
- 0.9.3 (2011-10-06)
- ------------------
- * Replace SVG core by original's (simplesvg.py)
- * Refactored
- * Fix bugs
-
- 0.9.2 (2011-09-30)
- ------------------
- * Add node attribute: textcolor
- * Add group attribute: textcolor
- * Add edge attribute: textcolor
- * Add diagram attributes: default_text_attribute
- * Fix beginpoint shape and endpoint shape were reversed
- * Fix bugs
-
- 0.9.1 (2011-09-26)
- ------------------
- * Add diagram attributes: default_node_color, default_group_color and default_line_color
- * Fix bugs
-
- 0.9.0 (2011-09-25)
- ------------------
- * Add icon attribute to node
- * Make transparency to background of PNG images
- * Fix bugs
-
- 0.8.9 (2011-08-09)
- ------------------
- * Fix bugs
-
- 0.8.8 (2011-08-08)
- ------------------
- * Fix bugs
-
- 0.8.7 (2011-08-06)
- ------------------
- * Fix bugs
-
- 0.8.6 (2011-08-01)
- ------------------
- * Support Pillow as replacement of PIL (experimental)
- * Fix bugs
-
- 0.8.5 (2011-07-31)
- ------------------
- * Allow dot characters in node_id
- * Fix bugs
-
- 0.8.4 (2011-07-05)
- ------------------
- * Fix bugs
-
- 0.8.3 (2011-07-03)
- ------------------
- * Support input from stdin
- * Fix bugs
-
- 0.8.2 (2011-06-29)
- ------------------
- * Add node.stacked
- * Add node shapes: dots, none
- * Add hiragino-font to font search list
- * Support background image fetching from web
- * Add diagram.edge_layout (experimental)
- * Fix bugs
-
- 0.8.1 (2011-05-14)
- ------------------
- * Change license to Apache License 2.0
- * Fix bugs
-
- 0.8.0 (2011-05-04)
- ------------------
- * Add --separate option and --version option
- * Fix bugs
-
- 0.7.8 (2011-04-19)
- ------------------
- * Update layout engine
- * Update requirements: PIL >= 1.1.5
- * Update parser for tokenize performance
- * Add --nodoctype option
- * Fix bugs
- * Add many testcases
-
- 0.7.7 (2011-03-29)
- ------------------
- * Fix bugs
-
- 0.7.6 (2011-03-26)
- ------------------
- * Add new layout manager for portrait edges
- * Fix bugs
-
- 0.7.5 (2011-03-20)
- ------------------
- * Support multiple nodes relations (cf. A -> B, C)
- * Support node group declaration at attribute of nodes
- * Fix bugs
-
- 0.7.4 (2011-03-08)
- ------------------
- * Fix bugs
-
- 0.7.3 (2011-03-02)
- ------------------
- * Use UTF-8 characters as Name token (by @swtw7466)
- * Fix htmlentities included in labels was not escaped on SVG images
- * Fix bugs
-
- 0.7.2 (2011-02-28)
- ------------------
- * Add default_shape attribute to diagram
-
- 0.7.1 (2011-02-27)
- ------------------
- * Fix edge has broken with antialias option
-
- 0.7.0 (2011-02-25)
- ------------------
- * Support node shape
-
- 0.6.7 (2011-02-12)
- ------------------
- * Change noderenderer interface to new style
- * Render dashed ellipse more clearly (contributed by @cocoatomo)
- * Support PDF exporting
-
- 0.6.6 (2011-01-31)
- ------------------
- * Support diagram.shape_namespace
- * Add new node shapes; mail, cloud, beginpoint, endpoint, minidiamond, actor
- * Support plug-in structure to install node shapes
- * Fix bugs
-
- 0.6.5 (2011-01-18)
- ------------------
- * Support node shape (experimental)
-
- 0.6.4 (2011-01-17)
- ------------------
- * Fix bugs
-
- 0.6.3 (2011-01-15)
- ------------------
- * Fix bugs
-
- 0.6.2 (2011-01-08)
- ------------------
- * Fix bugs
-
- 0.6.1 (2011-01-07)
- ------------------
- * Implement 'folded' attribute for edge
- * Refactor layout engine
-
- 0.6 (2011-01-02)
- ------------------
- * Support nested groups.
-
- 0.5.5 (2010-12-24)
- ------------------
- * Specify direction of edges as syntax (->, --, <-, <->)
- * Fix bugs.
-
- 0.5.4 (2010-12-23)
- ------------------
- * Remove debug codes.
-
- 0.5.3 (2010-12-23)
- ------------------
- * Support NodeGroup.label.
- * Implement --separate option (experimental)
- * Fix right-up edge overrapped on other nodes.
- * Support configration file: .blockdiagrc
-
- 0.5.2 (2010-11-06)
- ------------------
- * Fix unicode errors for UTF-8'ed SVG exportion.
- * Refactoring codes for running on GAE.
-
- 0.5.1 (2010-10-26)
- ------------------
- * Fix license text on diagparser.py
- * Update layout engine.
-
- 0.5 (2010-10-15)
- ------------------
- * Support background-image of node (SVG)
- * Support labels for edge.
- * Fix bugs.
-
- 0.4.2 (2010-10-10)
- ------------------
- * Support background-color of node groups.
- * Draw edge has jumped at edge's cross-points.
- * Fix bugs.
-
- 0.4.1 (2010-10-07)
- ------------------
- * Fix bugs.
-
- 0.4 (2010-10-07)
- ------------------
- * Support SVG exporting.
- * Support dashed edge drawing.
- * Support background image of nodes (PNG only)
-
- 0.3.1 (2010-09-29)
- ------------------
- * Fasten anti-alias process.
- * Fix text was broken on windows.
-
- 0.3 (2010-09-26)
- ------------------
- * Add --antialias option.
- * Fix bugs.
-
- 0.2.2 (2010-09-25)
- ------------------
- * Fix edge bugs.
-
- 0.2.1 (2010-09-25)
- ------------------
- * Fix bugs.
- * Fix package style.
-
- 0.2 (2010-09-23)
- ------------------
- * Update layout engine.
- * Support group { ... } sentence for create Node-Groups.
- * Support numbered badge on node (cf. A [numbered = 5])
-
- 0.1 (2010-09-20)
- -----------------
- * first release
-
- Todos
- ======
-
- Functionals
- ------------
- * Reimplement --separate option
- * Support diagram legends
- * Support other block diagram structure
-
- Known Issues
- -------------
- * Fix some experimental features.
- * PDF renderer does not support blur shadow
- * PDF renderer does not support path rendering
+Description: `blockdiag` generate block-diagram image file from spec-text file.
+
+ Features
+ ========
+ * Generate block-diagram from dot like text (basic feature).
+ * Multilingualization for node-label (utf-8 only).
+
+ You can get some examples and generated images on
+ `blockdiag.com <http://blockdiag.com/blockdiag/build/html/index.html>`_ .
+
+ Setup
+ =====
+
+ by easy_install
+ ----------------
+ Make environment::
+
+ $ easy_install blockdiag
+
+ If you want to export as PDF format, give pdf arguments::
+
+ $ easy_install "blockdiag[pdf]"
+
+ by buildout
+ ------------
+ Make environment::
+
+ $ hg clone http://bitbucket.org/tk0miya/blockdiag
+ $ cd blockdiag
+ $ python bootstrap.py
+ $ bin/buildout
+
+ Copy and modify ini file. example::
+
+ $ cp <blockdiag installed path>/blockdiag/examples/simple.diag .
+ $ vi simple.diag
+
+ Please refer to `spec-text setting sample`_ section for the format of the
+ `simpla.diag` configuration file.
+
+ spec-text setting sample
+ ========================
+ Few examples are available.
+ You can get more examples at
+ `blockdiag.com <http://blockdiag.com/blockdiag/build/html/index.html>`_ .
+
+ simple.diag
+ ------------
+ simple.diag is simply define nodes and transitions by dot-like text format::
+
+ diagram admin {
+ top_page -> config -> config_edit -> config_confirm -> top_page;
+ }
+
+ screen.diag
+ ------------
+ screen.diag is more complexly sample. diaglam nodes have a alternative label
+ and some transitions::
+
+ diagram admin {
+ top_page [label = "Top page"];
+
+ foo_index [label = "List of FOOs"];
+ foo_detail [label = "Detail FOO"];
+ foo_add [label = "Add FOO"];
+ foo_add_confirm [label = "Add FOO (confirm)"];
+ foo_edit [label = "Edit FOO"];
+ foo_edit_confirm [label = "Edit FOO (confirm)"];
+ foo_delete_confirm [label = "Delete FOO (confirm)"];
+
+ bar_detail [label = "Detail of BAR"];
+ bar_edit [label = "Edit BAR"];
+ bar_edit_confirm [label = "Edit BAR (confirm)"];
+
+ logout;
+
+ top_page -> foo_index;
+ top_page -> bar_detail;
+
+ foo_index -> foo_detail;
+ foo_detail -> foo_edit;
+ foo_detail -> foo_delete_confirm;
+ foo_index -> foo_add -> foo_add_confirm -> foo_index;
+ foo_index -> foo_edit -> foo_edit_confirm -> foo_index;
+ foo_index -> foo_delete_confirm -> foo_index;
+
+ bar_detail -> bar_edit -> bar_edit_confirm -> bar_detail;
+ }
+
+
+ Usage
+ =====
+ Execute blockdiag command::
+
+ $ blockdiag simple.diag
+ $ ls simple.png
+ simple.png
+
+
+ Requirements
+ ============
+ * Python 2.6, 2.7, 3.2, 3.3
+ * Pillow 2.2.1
+ * funcparserlib 0.3.6
+ * setuptools
+
+
+ License
+ =======
+ Apache License 2.0
+
+
+ History
+ =======
+
+ 1.3.2 (2013-11-19)
+ ------------------
+ * Fix bugs
+
+ 1.3.1 (2013-10-22)
+ ------------------
+ * Fix bugs
+
+ 1.3.0 (2013-10-05)
+ ------------------
+ * Support python 3.2 and 3.3 (thanks to @masayuko)
+ * Drop supports for python 2.4 and 2.5
+ * Replace dependency: PIL -> Pillow
+
+ 1.2.4 (2012-11-21)
+ ------------------
+ * Fix bugs
+
+ 1.2.3 (2012-11-05)
+ ------------------
+ * Fix bugs
+
+ 1.2.2 (2012-10-28)
+ ------------------
+ * Fix bugs
+
+ 1.2.1 (2012-10-28)
+ ------------------
+ * Add external imagedraw plugin supports
+ * Add node attribute: label_orientation*
+ * Fix bugs
+
+ 1.2.0 (2012-10-22)
+ ------------------
+ * Optimize algorithm for rendering shadow
+ * Add options to docutils directive
+ * Fix bugs
+
+ 1.1.8 (2012-09-28)
+ ------------------
+ * Add --ignore-pil option
+ * Fix bugs
+
+ 1.1.7 (2012-09-20)
+ ------------------
+ * Add diagram attribute: shadow_style
+ * Add font path for centos 6.2
+ * Add a setting 'antialias' in the configuration file
+ * Fix bugs
+
+ 1.1.6 (2012-06-06)
+ ------------------
+ * Support for readthedocs.org
+ * reST directive supports :caption: option
+ * Fix bugs
+
+ 1.1.5 (2012-04-22)
+ ------------------
+ * Embed source code to SVG document as description
+ * Fix bugs
+
+ 1.1.4 (2012-03-15)
+ ------------------
+ * Add new edge.hstyles: oneone, onemany, manyone, manymany
+ * Add edge attribute: description (for build description-tables)
+ * Fix bugs
+
+ 1.1.3 (2012-02-13)
+ ------------------
+ * Add new edge type for data-models (thanks to David Lang)
+ * Add --no-transparency option
+ * Fix bugs
+
+ 1.1.2 (2011-12-26)
+ ------------------
+ * Support font-index for TrueType Font Collections (.ttc file)
+ * Allow to use reST syntax in descriptions of nodes
+ * Fix bugs
+
+ 1.1.1 (2011-11-27)
+ ------------------
+ * Add node attribute: href (thanks to @r_rudi!)
+ * Fix bugs
+
+ 1.1.0 (2011-11-19)
+ ------------------
+ * Add shape: square and circle
+ * Add fontfamily attribute for switching fontface
+ * Fix bugs
+
+ 1.0.3 (2011-11-13)
+ ------------------
+ * Add plugin: attributes
+ * Change plugin syntax; (cf. plugin attributes [attr = value, attr, value])
+ * Fix bugs
+
+ 1.0.2 (2011-11-07)
+ ------------------
+ * Fix bugs
+
+ 1.0.1 (2011-11-06)
+ ------------------
+ * Add group attribute: shape
+ * Fix bugs
+
+ 1.0.0 (2011-11-04)
+ ------------------
+ * Add node attribute: linecolor
+ * Rename diagram attributes:
+ * fontsize -> default_fontsize
+ * default_line_color -> default_linecolor
+ * default_text_color -> default_textcolor
+ * Add docutils extention
+ * Fix bugs
+
+ 0.9.7 (2011-11-01)
+ ------------------
+ * Add node attribute: fontsize
+ * Add edge attributes: thick, fontsize
+ * Add group attribute: fontsize
+ * Change color of shadow in PDF mode
+ * Add class feature (experimental)
+ * Add handler-plugin framework (experimental)
+
+ 0.9.6 (2011-10-22)
+ ------------------
+ * node.style supports dashed_array format style
+ * Fix bugs
+
+ 0.9.5 (2011-10-19)
+ ------------------
+ * Add node attributes: width and height
+ * Fix bugs
+
+ 0.9.4 (2011-10-07)
+ ------------------
+ * Fix bugs
+
+ 0.9.3 (2011-10-06)
+ ------------------
+ * Replace SVG core by original's (simplesvg.py)
+ * Refactored
+ * Fix bugs
+
+ 0.9.2 (2011-09-30)
+ ------------------
+ * Add node attribute: textcolor
+ * Add group attribute: textcolor
+ * Add edge attribute: textcolor
+ * Add diagram attributes: default_text_attribute
+ * Fix beginpoint shape and endpoint shape were reversed
+ * Fix bugs
+
+ 0.9.1 (2011-09-26)
+ ------------------
+ * Add diagram attributes: default_node_color, default_group_color and default_line_color
+ * Fix bugs
+
+ 0.9.0 (2011-09-25)
+ ------------------
+ * Add icon attribute to node
+ * Make transparency to background of PNG images
+ * Fix bugs
+
+ 0.8.9 (2011-08-09)
+ ------------------
+ * Fix bugs
+
+ 0.8.8 (2011-08-08)
+ ------------------
+ * Fix bugs
+
+ 0.8.7 (2011-08-06)
+ ------------------
+ * Fix bugs
+
+ 0.8.6 (2011-08-01)
+ ------------------
+ * Support Pillow as replacement of PIL (experimental)
+ * Fix bugs
+
+ 0.8.5 (2011-07-31)
+ ------------------
+ * Allow dot characters in node_id
+ * Fix bugs
+
+ 0.8.4 (2011-07-05)
+ ------------------
+ * Fix bugs
+
+ 0.8.3 (2011-07-03)
+ ------------------
+ * Support input from stdin
+ * Fix bugs
+
+ 0.8.2 (2011-06-29)
+ ------------------
+ * Add node.stacked
+ * Add node shapes: dots, none
+ * Add hiragino-font to font search list
+ * Support background image fetching from web
+ * Add diagram.edge_layout (experimental)
+ * Fix bugs
+
+ 0.8.1 (2011-05-14)
+ ------------------
+ * Change license to Apache License 2.0
+ * Fix bugs
+
+ 0.8.0 (2011-05-04)
+ ------------------
+ * Add --separate option and --version option
+ * Fix bugs
+
+ 0.7.8 (2011-04-19)
+ ------------------
+ * Update layout engine
+ * Update requirements: PIL >= 1.1.5
+ * Update parser for tokenize performance
+ * Add --nodoctype option
+ * Fix bugs
+ * Add many testcases
+
+ 0.7.7 (2011-03-29)
+ ------------------
+ * Fix bugs
+
+ 0.7.6 (2011-03-26)
+ ------------------
+ * Add new layout manager for portrait edges
+ * Fix bugs
+
+ 0.7.5 (2011-03-20)
+ ------------------
+ * Support multiple nodes relations (cf. A -> B, C)
+ * Support node group declaration at attribute of nodes
+ * Fix bugs
+
+ 0.7.4 (2011-03-08)
+ ------------------
+ * Fix bugs
+
+ 0.7.3 (2011-03-02)
+ ------------------
+ * Use UTF-8 characters as Name token (by @swtw7466)
+ * Fix htmlentities included in labels was not escaped on SVG images
+ * Fix bugs
+
+ 0.7.2 (2011-02-28)
+ ------------------
+ * Add default_shape attribute to diagram
+
+ 0.7.1 (2011-02-27)
+ ------------------
+ * Fix edge has broken with antialias option
+
+ 0.7.0 (2011-02-25)
+ ------------------
+ * Support node shape
+
+ 0.6.7 (2011-02-12)
+ ------------------
+ * Change noderenderer interface to new style
+ * Render dashed ellipse more clearly (contributed by @cocoatomo)
+ * Support PDF exporting
+
+ 0.6.6 (2011-01-31)
+ ------------------
+ * Support diagram.shape_namespace
+ * Add new node shapes; mail, cloud, beginpoint, endpoint, minidiamond, actor
+ * Support plug-in structure to install node shapes
+ * Fix bugs
+
+ 0.6.5 (2011-01-18)
+ ------------------
+ * Support node shape (experimental)
+
+ 0.6.4 (2011-01-17)
+ ------------------
+ * Fix bugs
+
+ 0.6.3 (2011-01-15)
+ ------------------
+ * Fix bugs
+
+ 0.6.2 (2011-01-08)
+ ------------------
+ * Fix bugs
+
+ 0.6.1 (2011-01-07)
+ ------------------
+ * Implement 'folded' attribute for edge
+ * Refactor layout engine
+
+ 0.6 (2011-01-02)
+ ------------------
+ * Support nested groups.
+
+ 0.5.5 (2010-12-24)
+ ------------------
+ * Specify direction of edges as syntax (->, --, <-, <->)
+ * Fix bugs.
+
+ 0.5.4 (2010-12-23)
+ ------------------
+ * Remove debug codes.
+
+ 0.5.3 (2010-12-23)
+ ------------------
+ * Support NodeGroup.label.
+ * Implement --separate option (experimental)
+ * Fix right-up edge overrapped on other nodes.
+ * Support configration file: .blockdiagrc
+
+ 0.5.2 (2010-11-06)
+ ------------------
+ * Fix unicode errors for UTF-8'ed SVG exportion.
+ * Refactoring codes for running on GAE.
+
+ 0.5.1 (2010-10-26)
+ ------------------
+ * Fix license text on diagparser.py
+ * Update layout engine.
+
+ 0.5 (2010-10-15)
+ ------------------
+ * Support background-image of node (SVG)
+ * Support labels for edge.
+ * Fix bugs.
+
+ 0.4.2 (2010-10-10)
+ ------------------
+ * Support background-color of node groups.
+ * Draw edge has jumped at edge's cross-points.
+ * Fix bugs.
+
+ 0.4.1 (2010-10-07)
+ ------------------
+ * Fix bugs.
+
+ 0.4 (2010-10-07)
+ ------------------
+ * Support SVG exporting.
+ * Support dashed edge drawing.
+ * Support background image of nodes (PNG only)
+
+ 0.3.1 (2010-09-29)
+ ------------------
+ * Fasten anti-alias process.
+ * Fix text was broken on windows.
+
+ 0.3 (2010-09-26)
+ ------------------
+ * Add --antialias option.
+ * Fix bugs.
+
+ 0.2.2 (2010-09-25)
+ ------------------
+ * Fix edge bugs.
+
+ 0.2.1 (2010-09-25)
+ ------------------
+ * Fix bugs.
+ * Fix package style.
+
+ 0.2 (2010-09-23)
+ ------------------
+ * Update layout engine.
+ * Support group { ... } sentence for create Node-Groups.
+ * Support numbered badge on node (cf. A [numbered = 5])
+
+ 0.1 (2010-09-20)
+ -----------------
+ * first release
+
Keywords: diagram,generator
Platform: UNKNOWN
-Classifier: Development Status :: 4 - Beta
+Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3.2
+Classifier: Programming Language :: Python :: 3.3
Classifier: Topic :: Software Development
Classifier: Topic :: Software Development :: Documentation
Classifier: Topic :: Text Processing :: Markup
diff --git a/src/blockdiag.egg-info/SOURCES.txt b/src/blockdiag.egg-info/SOURCES.txt
index 2fe42fc..e78203b 100644
--- a/src/blockdiag.egg-info/SOURCES.txt
+++ b/src/blockdiag.egg-info/SOURCES.txt
@@ -1,10 +1,13 @@
LICENSE
MANIFEST.in
+README.rst
blockdiag.1
bootstrap.py
buildout.cfg
setup.cfg
setup.py
+tox.ini
+examples/blockdiagrc
examples/group.diag
examples/group.png
examples/group.svg
@@ -20,8 +23,6 @@ examples/screen.svg
examples/simple.diag
examples/simple.png
examples/simple.svg
-src/README.txt
-src/TODO.txt
src/blockdiag_sphinxhelper.py
src/blockdiag/__init__.py
src/blockdiag/builder.py
@@ -83,6 +84,7 @@ src/blockdiag/tests/test_builder_node.py
src/blockdiag/tests/test_builder_separate.py
src/blockdiag/tests/test_generate_diagram.py
src/blockdiag/tests/test_imagedraw_textfolder.py
+src/blockdiag/tests/test_imagedraw_utils.py
src/blockdiag/tests/test_parser.py
src/blockdiag/tests/test_pep8.py
src/blockdiag/tests/test_rst_directives.py
@@ -203,6 +205,7 @@ src/blockdiag/tests/diagrams/twin_circular_ref_to_root.diag
src/blockdiag/tests/diagrams/twin_forked.diag
src/blockdiag/tests/diagrams/twin_multiple_parent_node.diag
src/blockdiag/tests/diagrams/two_edges.diag
+src/blockdiag/tests/diagrams/white.gif
src/blockdiag/tests/diagrams/errors/belongs_to_two_groups.diag
src/blockdiag/tests/diagrams/errors/group_follows_node.diag
src/blockdiag/tests/diagrams/errors/lexer_error.diag
@@ -224,14 +227,12 @@ src/blockdiag/tests/diagrams/errors/unknown_node_style.diag
src/blockdiag/tests/diagrams/errors/unknown_plugin.diag
src/blockdiag/utils/__init__.py
src/blockdiag/utils/bootstrap.py
-src/blockdiag/utils/collections.py
+src/blockdiag/utils/compat.py
src/blockdiag/utils/config.py
src/blockdiag/utils/fontmap.py
-src/blockdiag/utils/functools.py
src/blockdiag/utils/images.py
src/blockdiag/utils/jpeg.py
src/blockdiag/utils/myitertools.py
-src/blockdiag/utils/namedtuple.py
src/blockdiag/utils/urlutil.py
src/blockdiag/utils/uuid.py
src/blockdiag/utils/rst/__init__.py
diff --git a/src/blockdiag.egg-info/entry_points.txt b/src/blockdiag.egg-info/entry_points.txt
index 07a8580..6a48927 100644
--- a/src/blockdiag.egg-info/entry_points.txt
+++ b/src/blockdiag.egg-info/entry_points.txt
@@ -1,36 +1,36 @@
- [console_scripts]
- blockdiag = blockdiag.command:main
+ [console_scripts]
+ blockdiag = blockdiag.command:main
- [blockdiag_noderenderer]
- box = blockdiag.noderenderer.box
- square = blockdiag.noderenderer.square
- roundedbox = blockdiag.noderenderer.roundedbox
- diamond = blockdiag.noderenderer.diamond
- minidiamond = blockdiag.noderenderer.minidiamond
- mail = blockdiag.noderenderer.mail
- note = blockdiag.noderenderer.note
- cloud = blockdiag.noderenderer.cloud
- circle = blockdiag.noderenderer.circle
- ellipse = blockdiag.noderenderer.ellipse
- beginpoint = blockdiag.noderenderer.beginpoint
- endpoint = blockdiag.noderenderer.endpoint
- actor = blockdiag.noderenderer.actor
- flowchart.database = blockdiag.noderenderer.flowchart.database
- flowchart.input = blockdiag.noderenderer.flowchart.input
- flowchart.loopin = blockdiag.noderenderer.flowchart.loopin
- flowchart.loopout = blockdiag.noderenderer.flowchart.loopout
- flowchart.terminator = blockdiag.noderenderer.flowchart.terminator
- textbox = blockdiag.noderenderer.textbox
- dots = blockdiag.noderenderer.dots
- none = blockdiag.noderenderer.none
+ [blockdiag_noderenderer]
+ box = blockdiag.noderenderer.box
+ square = blockdiag.noderenderer.square
+ roundedbox = blockdiag.noderenderer.roundedbox
+ diamond = blockdiag.noderenderer.diamond
+ minidiamond = blockdiag.noderenderer.minidiamond
+ mail = blockdiag.noderenderer.mail
+ note = blockdiag.noderenderer.note
+ cloud = blockdiag.noderenderer.cloud
+ circle = blockdiag.noderenderer.circle
+ ellipse = blockdiag.noderenderer.ellipse
+ beginpoint = blockdiag.noderenderer.beginpoint
+ endpoint = blockdiag.noderenderer.endpoint
+ actor = blockdiag.noderenderer.actor
+ flowchart.database = blockdiag.noderenderer.flowchart.database
+ flowchart.input = blockdiag.noderenderer.flowchart.input
+ flowchart.loopin = blockdiag.noderenderer.flowchart.loopin
+ flowchart.loopout = blockdiag.noderenderer.flowchart.loopout
+ flowchart.terminator = blockdiag.noderenderer.flowchart.terminator
+ textbox = blockdiag.noderenderer.textbox
+ dots = blockdiag.noderenderer.dots
+ none = blockdiag.noderenderer.none
- [blockdiag_plugins]
- attributes = blockdiag.plugins.attributes
- autoclass = blockdiag.plugins.autoclass
+ [blockdiag_plugins]
+ attributes = blockdiag.plugins.attributes
+ autoclass = blockdiag.plugins.autoclass
- [blockdiag_imagedrawers]
- imagedraw_png = blockdiag.imagedraw.png
- imagedraw_svg = blockdiag.imagedraw.svg
- imagedraw_pdf = blockdiag.imagedraw.pdf
-
\ No newline at end of file
+ [blockdiag_imagedrawers]
+ imagedraw_png = blockdiag.imagedraw.png
+ imagedraw_svg = blockdiag.imagedraw.svg
+ imagedraw_pdf = blockdiag.imagedraw.pdf
+
\ No newline at end of file
diff --git a/src/blockdiag.egg-info/requires.txt b/src/blockdiag.egg-info/requires.txt
index 3dc7b6e..b054c00 100644
--- a/src/blockdiag.egg-info/requires.txt
+++ b/src/blockdiag.egg-info/requires.txt
@@ -1,7 +1,7 @@
setuptools
funcparserlib
webcolors
-PIL
+Pillow
[rst]
docutils
@@ -11,5 +11,4 @@ reportlab
[test]
Nose
-pep8>=1.3
-unittest2
\ No newline at end of file
+pep8>=1.3
\ No newline at end of file
diff --git a/src/blockdiag/__init__.py b/src/blockdiag/__init__.py
index 4343b09..09ae5dc 100644
--- a/src/blockdiag/__init__.py
+++ b/src/blockdiag/__init__.py
@@ -1,16 +1,16 @@
-# -*- coding: utf-8 -*-
-# Copyright 2011 Takeshi KOMIYA
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-__version__ = '1.2.4'
+# -*- coding: utf-8 -*-
+# Copyright 2011 Takeshi KOMIYA
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+__version__ = '1.3.2'
diff --git a/src/blockdiag/builder.py b/src/blockdiag/builder.py
index f632e15..7ab63cf 100644
--- a/src/blockdiag/builder.py
+++ b/src/blockdiag/builder.py
@@ -16,6 +16,7 @@
from blockdiag import parser
from blockdiag.elements import Diagram, DiagramNode, NodeGroup, DiagramEdge
from blockdiag.utils import unquote, XY
+from blockdiag.utils.compat import cmp_to_key
class DiagramTreeBuilder:
@@ -67,7 +68,7 @@ class DiagramTreeBuilder:
def instantiate(self, group, tree):
for stmt in tree.stmts:
- # Translate Node having group attribute to SubGraph
+ # Translate Node having group attribute to Group
if isinstance(stmt, parser.Node):
group_attr = [a for a in stmt.attrs if a.name == 'group']
if group_attr:
@@ -75,7 +76,7 @@ class DiagramTreeBuilder:
stmt.attrs.remove(group_id)
if group_id.value != group.id:
- stmt = parser.SubGraph(group_id.value, [stmt])
+ stmt = parser.Group(group_id.value, [stmt])
# Instantiate statements
if isinstance(stmt, parser.Node):
@@ -84,42 +85,33 @@ class DiagramTreeBuilder:
self.belong_to(node, group)
elif isinstance(stmt, parser.Edge):
- nodes = stmt.nodes.pop(0)
- edge_from = [DiagramNode.get(n) for n in nodes]
- for node in edge_from:
- self.belong_to(node, group)
-
- while len(stmt.nodes):
- edge_type, edge_to = stmt.nodes.pop(0)
- edge_to = [DiagramNode.get(n) for n in edge_to]
- for node in edge_to:
- self.belong_to(node, group)
+ from_nodes = [DiagramNode.get(n) for n in stmt.from_nodes]
+ to_nodes = [DiagramNode.get(n) for n in stmt.to_nodes]
- for node1 in edge_from:
- for node2 in edge_to:
- edge = DiagramEdge.get(node1, node2)
- if edge_type:
- attrs = [parser.Attr('dir', edge_type)]
- edge.set_attributes(attrs)
- edge.set_attributes(stmt.attrs)
+ for node in from_nodes + to_nodes:
+ self.belong_to(node, group)
- edge_from = edge_to
+ for node1 in from_nodes:
+ for node2 in to_nodes:
+ edge = DiagramEdge.get(node1, node2)
+ edge.set_dir(stmt.edge_type)
+ edge.set_attributes(stmt.attrs)
- elif isinstance(stmt, parser.SubGraph):
+ elif isinstance(stmt, parser.Group):
subgroup = NodeGroup.get(stmt.id)
subgroup.level = group.level + 1
self.belong_to(subgroup, group)
self.instantiate(subgroup, stmt)
- elif isinstance(stmt, parser.DefAttrs):
- group.set_attributes(stmt.attrs)
-
- elif isinstance(stmt, parser.AttrClass):
- name = unquote(stmt.name)
- Diagram.classes[name] = stmt
+ elif isinstance(stmt, parser.Attr):
+ group.set_attribute(stmt)
- elif isinstance(stmt, parser.AttrPlugin):
- self.diagram.set_plugin(stmt.name, stmt.attrs)
+ elif isinstance(stmt, parser.Extension):
+ if stmt.type == 'class':
+ name = unquote(stmt.name)
+ Diagram.classes[name] = stmt
+ elif stmt.type == 'plugin':
+ self.diagram.set_plugin(stmt.name, stmt.attrs)
elif isinstance(stmt, parser.Statements):
self.instantiate(group, stmt)
@@ -201,7 +193,7 @@ class DiagramLayoutManager:
else:
related.append(uniq_node)
- related.sort(lambda x, y: cmp(x.order, y.order))
+ related.sort(key=lambda x: x.order)
return related
def get_parent_nodes(self, node):
@@ -250,9 +242,7 @@ class DiagramLayoutManager:
if not parent in circular:
parents.append(parent)
- parents.sort(lambda x, y: cmp(x.order, y.order))
-
- for parent in parents:
+ for parent in sorted(parents, key=lambda x: x.order):
children = self.get_child_nodes(parent)
if node1 in children and node2 in children:
if circular.index(node1) > circular.index(node2):
@@ -364,11 +354,17 @@ class DiagramLayoutManager:
x.node1 = x.node1.group
y.node1 = y.node1.group
- return cmp(x.node1.order, y.node1.order)
+ # cmp x.node1.order and y.node1.order
+ if x.node1.order < y.node1.order:
+ return -1
+ elif x.node1.order == y.node1.order:
+ return 0
+ else:
+ return 1
edges = (DiagramEdge.find(parent, node1) +
DiagramEdge.find(parent, node2))
- edges.sort(compare)
+ edges.sort(key=cmp_to_key(compare))
if len(edges) == 0:
return 0
elif edges[0].node2 == node2:
@@ -390,9 +386,17 @@ class DiagramLayoutManager:
node.xy = XY(node.xy.x, height)
self.mark_xy(node.xy, node.colwidth, node.colheight)
+ def cmp(x, y):
+ if x.xy.x < y.xy.y:
+ return -1
+ elif x.xy.x == y.xy.y:
+ return 0
+ else:
+ return 1
+
count = 0
children = self.get_child_nodes(node)
- children.sort(lambda x, y: cmp(x.xy.x, y.xy.y))
+ children.sort(key=cmp_to_key(cmp))
grandchild = 0
for child in children:
@@ -710,9 +714,9 @@ class SeparateDiagramBuilder(ScreenNodeBuilder):
# pick up nodes to base diagram
nodes1 = [e.node1 for e in DiagramEdge.find(None, group)]
- nodes1.sort(lambda x, y: cmp(x.order, y.order))
+ nodes1.sort(key=lambda x: x.order)
nodes2 = [e.node2 for e in DiagramEdge.find(group, None)]
- nodes2.sort(lambda x, y: cmp(x.order, y.order))
+ nodes2.sort(key=lambda x: x.order)
nodes = nodes1 + [group] + nodes2
for i, n in enumerate(nodes):
diff --git a/src/blockdiag/command.py b/src/blockdiag/command.py
index 6563fdb..830aece 100644
--- a/src/blockdiag/command.py
+++ b/src/blockdiag/command.py
@@ -14,6 +14,7 @@
# limitations under the License.
import re
+import sys
import blockdiag
import blockdiag.builder
import blockdiag.drawer
@@ -21,11 +22,6 @@ import blockdiag.parser
from blockdiag.utils.bootstrap import Application, Options
-# FIXME: for compatibility
-from blockdiag.utils.bootstrap import detectfont
-detectfont
-
-
class BlockdiagOptions(Options):
def build_parser(self):
super(BlockdiagOptions, self).build_parser()
@@ -38,8 +34,8 @@ class BlockdiagOptions(Options):
class BlockdiagApp(Application):
module = blockdiag
- def parse_options(self):
- self.options = BlockdiagOptions(self.module).parse()
+ def parse_options(self, args):
+ self.options = BlockdiagOptions(self.module).parse(args)
def build_diagram(self, tree):
if not self.options.separate:
@@ -62,5 +58,5 @@ class BlockdiagApp(Application):
return 0
-def main():
- return BlockdiagApp().run()
+def main(args=sys.argv[1:]):
+ return BlockdiagApp().run(args)
diff --git a/src/blockdiag/drawer.py b/src/blockdiag/drawer.py
index 1a62aab..cae5d15 100644
--- a/src/blockdiag/drawer.py
+++ b/src/blockdiag/drawer.py
@@ -13,13 +13,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from collections import defaultdict
from blockdiag import imagedraw, noderenderer
from blockdiag.metrics import AutoScaler, DiagramMetrics
-from blockdiag.utils.collections import defaultdict
class DiagramDraw(object):
- MetricsClass = None # TODO: obsoleted interface
shadow_colors = defaultdict(lambda: (0, 0, 0))
shadow_colors['PNG'] = (64, 64, 64)
shadow_colors['PDF'] = (144, 144, 144)
@@ -52,10 +51,7 @@ class DiagramDraw(object):
self.drawer.set_options(jump_radius=self.metrics.cellsize / 2)
def create_metrics(self, *args, **kwargs):
- if self.MetricsClass:
- return self.MetricsClass(*args, **kwargs)
- else:
- return DiagramMetrics(*args, **kwargs)
+ return DiagramMetrics(*args, **kwargs)
@property
def nodes(self):
diff --git a/src/blockdiag/elements.py b/src/blockdiag/elements.py
index f46919f..bc67124 100644
--- a/src/blockdiag/elements.py
+++ b/src/blockdiag/elements.py
@@ -18,6 +18,7 @@ import re
import sys
import copy
from blockdiag.utils import images, unquote, urlutil, uuid, XY
+from blockdiag.utils.compat import u
from blockdiag import noderenderer, plugins
@@ -224,7 +225,7 @@ class DiagramNode(Element):
for name in self.desctable:
value = getattr(self, name)
if value is None:
- attrs.append(u"")
+ attrs.append(u(""))
else:
attrs.append(value)
@@ -645,7 +646,7 @@ class Diagram(NodeGroup):
def set_edge_layout(self, value):
value = value.lower()
if value in ('normal', 'flowchart'):
- msg = "WARNING: edge_layout is very experimental feature!\n"
+ msg = u("WARNING: edge_layout is very experimental feature!\n")
sys.stderr.write(msg)
self.edge_layout = value
diff --git a/src/blockdiag/imagedraw/base.py b/src/blockdiag/imagedraw/base.py
index 030c401..f53b940 100644
--- a/src/blockdiag/imagedraw/base.py
+++ b/src/blockdiag/imagedraw/base.py
@@ -13,9 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from functools import partial
from blockdiag.imagedraw import textfolder
from blockdiag.utils import Box
-from blockdiag.utils.functools import partial
class ImageDraw(object):
@@ -72,8 +72,5 @@ class ImageDraw(object):
def image(self, box, url):
pass
- def loadImage(self, filename, box): # TODO: obsoleted
- return self.image(box, filename)
-
def save(self, filename, size, _format):
pass
diff --git a/src/blockdiag/imagedraw/filters/__init__.py b/src/blockdiag/imagedraw/filters/__init__.py
index 5c383c2..bd36e96 100644
--- a/src/blockdiag/imagedraw/filters/__init__.py
+++ b/src/blockdiag/imagedraw/filters/__init__.py
@@ -1,14 +1,14 @@
-# -*- coding: utf-8 -*-
-# Copyright 2011 Takeshi KOMIYA
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# -*- coding: utf-8 -*-
+# Copyright 2011 Takeshi KOMIYA
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/src/blockdiag/imagedraw/filters/linejump.py b/src/blockdiag/imagedraw/filters/linejump.py
index d52c009..c3c41fe 100644
--- a/src/blockdiag/imagedraw/filters/linejump.py
+++ b/src/blockdiag/imagedraw/filters/linejump.py
@@ -13,10 +13,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from blockdiag.utils import functools, Box, XY
+import functools
+from blockdiag.utils import Box, XY
-class LazyReciever(object):
+class LazyReceiver(object):
def __init__(self, target):
self.target = target
self.calls = []
@@ -31,9 +32,9 @@ class LazyReciever(object):
def _(*args, **kwargs):
if name in self.target.self_generative_methods:
ret = method(self.target, *args, **kwargs)
- reciever = LazyReciever(ret)
- self.nested.append(reciever)
- return reciever
+ receiver = LazyReceiver(ret)
+ self.nested.append(receiver)
+ return receiver
else:
self.calls.append((method, args, kwargs))
return self
@@ -59,7 +60,7 @@ class LazyReciever(object):
method(self.target, *args, **kwargs)
-class LineJumpDrawFilter(LazyReciever):
+class LineJumpDrawFilter(LazyReceiver):
def __init__(self, target, jump_radius):
super(LineJumpDrawFilter, self).__init__(target)
self.ytree = []
diff --git a/src/blockdiag/imagedraw/pdf.py b/src/blockdiag/imagedraw/pdf.py
index 0daaa53..a474072 100644
--- a/src/blockdiag/imagedraw/pdf.py
+++ b/src/blockdiag/imagedraw/pdf.py
@@ -23,7 +23,7 @@ from blockdiag.imagedraw import base
from blockdiag.imagedraw.utils import cached
from blockdiag.utils import urlutil, Box, Size
from blockdiag.utils.fontmap import parse_fontpath
-from blockdiag.utils.functools import partial
+from blockdiag.utils.compat import string_types
class PDFImageDraw(base.ImageDraw):
@@ -34,6 +34,8 @@ class PDFImageDraw(base.ImageDraw):
self.canvas = None
self.fonts = {}
+ self.set_canvas_size(Size(1, 1)) # This line make textsize() workable
+
def set_canvas_size(self, size):
self.canvas = canvas.Canvas(self.filename, pagesize=size)
self.size = size
@@ -89,7 +91,7 @@ class PDFImageDraw(base.ImageDraw):
self.canvas.setDash()
def set_stroke_color(self, color="black"):
- if isinstance(color, basestring):
+ if isinstance(color, string_types):
self.canvas.setStrokeColor(color)
elif color:
rgb = (color[0] / 256.0, color[1] / 256.0, color[2] / 256.0)
@@ -98,7 +100,7 @@ class PDFImageDraw(base.ImageDraw):
self.set_stroke_color()
def set_fill_color(self, color="white"):
- if isinstance(color, basestring):
+ if isinstance(color, string_types):
if color != 'none':
self.canvas.setFillColor(color)
elif color:
@@ -128,6 +130,7 @@ class PDFImageDraw(base.ImageDraw):
@cached
def textlinesize(self, string, font):
+ self.set_font(font)
width = self.canvas.stringWidth(string, font.path, font.size)
return Size(int(math.ceil(width)), font.size)
diff --git a/src/blockdiag/imagedraw/png.py b/src/blockdiag/imagedraw/png.py
index bfcb8c1..e7529a6 100644
--- a/src/blockdiag/imagedraw/png.py
+++ b/src/blockdiag/imagedraw/png.py
@@ -13,42 +13,38 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from __future__ import division
import re
+import sys
import math
-from itertools import izip, tee
+from itertools import tee
+try:
+ from future_builtins import zip
+except ImportError:
+ pass
+from functools import partial, wraps
+from PIL import Image, ImageDraw, ImageFont, ImageFilter
from blockdiag.imagedraw import base
-from blockdiag.imagedraw.utils import cached, ellipse
+from blockdiag.imagedraw.utils import cached
from blockdiag.imagedraw.utils.ellipse import dots as ellipse_dots
from blockdiag.utils import urlutil, Box, Size, XY
from blockdiag.utils.fontmap import parse_fontpath, FontMap
-from blockdiag.utils.functools import partial, wraps
from blockdiag.utils.myitertools import istep, stepslice
-try:
- from PIL import Image
- from PIL import ImageDraw
- from PIL import ImageFont
- from PIL import ImageFilter
-except ImportError:
- import Image
- import ImageDraw
- import ImageFont
- import ImageFilter
-
def point_pairs(xylist):
iterable = iter(xylist)
for pt in iterable:
if isinstance(pt, int):
- yield (pt, iterable.next())
+ yield (pt, next(iterable))
else:
yield pt
def line_segments(xylist):
p1, p2 = tee(point_pairs(xylist))
- p2.next()
- return izip(p1, p2)
+ next(p2)
+ return zip(p1, p2)
def dashize_line(line, length):
@@ -57,7 +53,7 @@ def dashize_line(line, length):
if pt1[1] > pt2[1]:
pt2, pt1 = line
- r = stepslice(xrange(pt1[1], pt2[1]), length)
+ r = stepslice(range(pt1[1], pt2[1]), length)
for y1, y2 in istep(n for n in r):
yield [(pt1[0], y1), (pt1[0], y2)]
@@ -65,7 +61,7 @@ def dashize_line(line, length):
if pt1[0] > pt2[0]:
pt2, pt1 = line
- r = stepslice(xrange(pt1[0], pt2[0]), length)
+ r = stepslice(range(pt1[0], pt2[0]), length)
for x1, x2 in istep(n for n in r):
yield [(x1, pt1[1]), (x2, pt1[1])]
else: # diagonal
@@ -171,7 +167,7 @@ class ImageDrawExBase(base.ImageDraw):
for pt in ellipse_dots(box, cycle, start, end):
self.draw.line([pt, pt], fill=kwargs['fill'])
else:
- self.draw.arc(box, start, end, **kwargs)
+ self.draw.arc(box.to_integer_point(), start, end, **kwargs)
def ellipse(self, box, **kwargs):
if 'filter' in kwargs:
@@ -199,7 +195,7 @@ class ImageDrawExBase(base.ImageDraw):
if kwargs.get('fill') == 'none':
del kwargs['fill']
- self.draw.ellipse(box, **kwargs)
+ self.draw.ellipse(box.to_integer_point(), **kwargs)
def line(self, xy, **kwargs):
if 'jump' in kwargs:
@@ -351,17 +347,20 @@ class ImageDrawExBase(base.ImageDraw):
rendered = True
if not rendered and font.size > 0:
- font.size = int(font.size * 0.8)
- self.textarea(box, string, font, **kwargs)
+ _font = font.duplicate()
+ _font.size = int(font.size * 0.8)
+ self.textarea(box, string, _font, **kwargs)
def image(self, box, url):
if urlutil.isurl(url):
- import cStringIO
+ try:
+ from io import StringIO
+ except ImportError:
+ from cStringIO import StringIO
import urllib
try:
- url = cStringIO.StringIO(urllib.urlopen(url).read())
+ url = StringIO(urllib.urlopen(url).read())
except:
- import sys
msg = "WARNING: Could not retrieve: %s\n" % url
sys.stderr.write(msg)
return
@@ -375,12 +374,12 @@ class ImageDrawExBase(base.ImageDraw):
# centering image.
w, h = image.size
if box.width > w:
- x = box[0] + (box.width - w) / 2
+ x = box[0] + (box.width - w) // 2
else:
x = box[0]
if box.height > h:
- y = box[1] + (box.height - h) / 2
+ y = box[1] + (box.height - h) // 2
else:
y = box[1]
@@ -401,8 +400,11 @@ class ImageDrawExBase(base.ImageDraw):
self._image.save(self.filename, _format)
image = None
else:
- import cStringIO
- tmp = cStringIO.StringIO()
+ try:
+ from io import StringIO
+ except ImportError:
+ from cStringIO import StringIO
+ tmp = StringIO()
self._image.save(tmp, _format)
image = tmp.getvalue()
diff --git a/src/blockdiag/imagedraw/simplesvg.py b/src/blockdiag/imagedraw/simplesvg.py
index 273a297..fd5fe0b 100644
--- a/src/blockdiag/imagedraw/simplesvg.py
+++ b/src/blockdiag/imagedraw/simplesvg.py
@@ -14,11 +14,15 @@
# limitations under the License.
import re
-import cStringIO
+from blockdiag.utils.compat import u, string_types
+try:
+ from io import StringIO
+except ImportError:
+ from cStringIO import StringIO
def _escape(s):
- if not isinstance(s, (str, unicode)):
+ if not isinstance(s, string_types):
s = str(s)
return s.replace("&", "&").replace("<", "<").replace(">", ">")
@@ -53,28 +57,26 @@ class base(object):
clsname = self.__class__.__name__
indent = ' ' * level
- io.write('%s<%s' % (indent, clsname))
+ io.write(u('%s<%s') % (indent, clsname))
for key in sorted(self.attributes):
value = self.attributes[key]
if value is not None:
- io.write(' %s=%s' % (_escape(key), _quote(value)))
+ io.write(u(' %s=%s') % (_escape(key), _quote(value)))
if self.elements == []:
if self.text is not None:
- text = _escape(self.text).encode('utf-8')
- io.write(">%s</%s>\n" % (text, clsname))
+ io.write(u(">%s</%s>\n") % (_escape(self.text), clsname))
else:
- io.write(" />\n")
+ io.write(u(" />\n"))
elif self.elements:
if self.text is not None:
- text = _escape(self.text).encode('utf-8')
- io.write(">%s\n" % (text,))
+ io.write(u(">%s\n") % (_escape(self.text),))
else:
- io.write(">\n")
+ io.write(u(">\n"))
for e in self.elements:
e.to_xml(io, level + 1)
- io.write('%s</%s>\n' % (indent, clsname))
+ io.write(u('%s</%s>\n') % (indent, clsname))
class element(base):
@@ -100,13 +102,13 @@ class svg(base):
self.add_attribute('xmlns', 'http://www.w3.org/2000/svg')
def to_xml(self):
- io = cStringIO.StringIO()
+ io = StringIO()
if not self.nodoctype:
url = "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
- io.write("<?xml version='1.0' encoding='UTF-8'?>\n")
- io.write('<!DOCTYPE svg PUBLIC '
- '"-//W3C//DTD SVG 1.0//EN" "%s">\n' % url)
+ io.write(u("<?xml version='1.0' encoding='UTF-8'?>\n"))
+ io.write(u('<!DOCTYPE svg PUBLIC ') +
+ u('"-//W3C//DTD SVG 1.0//EN" "%s">\n') % url)
super(svg, self).to_xml(io)
diff --git a/src/blockdiag/imagedraw/svg.py b/src/blockdiag/imagedraw/svg.py
index 1168572..c2a159f 100644
--- a/src/blockdiag/imagedraw/svg.py
+++ b/src/blockdiag/imagedraw/svg.py
@@ -16,11 +16,13 @@
import re
import base64
from blockdiag.imagedraw import base as _base
-from blockdiag.imagedraw.simplesvg import *
+from blockdiag.imagedraw.simplesvg import (
+ svg, svgclass, filter, title, desc, defs, g, a, text,
+ rect, polygon, ellipse, path, pathdata, image
+)
from blockdiag.imagedraw.utils import cached
from blockdiag.imagedraw.utils.ellipse import endpoints as ellipse_endpoints
-from blockdiag.utils import urlutil, Box, XY
-from blockdiag.utils.functools import partial
+from blockdiag.utils import urlutil, Box, XY, is_Pillow_available
feGaussianBlur = svgclass('feGaussianBlur')
@@ -82,10 +84,6 @@ class SVGImageDrawElement(_base.ImageDraw):
def __init__(self, svg, parent=None):
self.svg = svg
- if parent and parent.ignore_pil:
- self.ignore_pil = True
- else:
- self.ignore_pil = False
def path(self, pd, **kwargs):
fill = kwargs.get('fill')
@@ -107,15 +105,15 @@ class SVGImageDrawElement(_base.ImageDraw):
@cached
def textlinesize(self, string, font, **kwargs):
- if kwargs.get('ignore_pil', self.ignore_pil):
- from blockdiag.imagedraw.utils import textsize
- return textsize(string, font)
- else:
+ if is_Pillow_available():
if not hasattr(self, '_pil_drawer'):
from blockdiag.imagedraw import png
self._pil_drawer = png.ImageDrawEx(None)
return self._pil_drawer.textlinesize(string, font)
+ else:
+ from blockdiag.imagedraw.utils import textsize
+ return textsize(string, font)
def text(self, point, string, font, **kwargs):
fill = kwargs.get('fill')
@@ -141,8 +139,9 @@ class SVGImageDrawElement(_base.ImageDraw):
rendered = True
if not rendered and font.size > 0:
- font.size = int(font.size * 0.8)
- self.textarea(box, string, font, **kwargs)
+ _font = font.duplicate()
+ _font.size = int(font.size * 0.8)
+ self.textarea(box, string, _font, **kwargs)
def rotated_textarea(self, box, string, font, **kwargs):
angle = int(kwargs['rotate']) % 360
@@ -182,7 +181,6 @@ class SVGImageDrawElement(_base.ImageDraw):
self.svg.addElement(p)
def arc(self, box, start, end, **kwargs):
- thick = kwargs.get('thick')
fill = kwargs.get('fill')
w = box.width / 2
@@ -231,7 +229,7 @@ class SVGImageDrawElement(_base.ImageDraw):
def image(self, box, url):
if not urlutil.isurl(url):
string = open(url, 'rb').read()
- url = "data:;base64," + base64.b64encode(string)
+ url = "data:;base64," + str(base64.b64encode(string))
im = image(url, box.x1, box.y1, box.width, box.height)
self.svg.addElement(im)
@@ -256,7 +254,6 @@ class SVGImageDraw(SVGImageDrawElement):
self.filename = filename
self.options = kwargs
- self.ignore_pil = kwargs.get('ignore_pil')
self.set_canvas_size((0, 0))
def set_canvas_size(self, size):
@@ -293,7 +290,7 @@ class SVGImageDraw(SVGImageDrawElement):
image = self.svg.to_xml()
if self.filename:
- open(self.filename, 'w').write(image)
+ open(self.filename, 'wb').write(image.encode('utf-8'))
return image
diff --git a/src/blockdiag/imagedraw/textfolder.py b/src/blockdiag/imagedraw/textfolder.py
index 83a1d8d..803cd77 100644
--- a/src/blockdiag/imagedraw/textfolder.py
+++ b/src/blockdiag/imagedraw/textfolder.py
@@ -15,6 +15,7 @@
import re
from blockdiag.utils import Box, Size, XY
+from blockdiag.utils.compat import u, string_types
def splitlabel(string):
@@ -24,8 +25,9 @@ def splitlabel(string):
"""
string = re.sub('^\s*', '', string)
string = re.sub('\s*$', '', string)
- string = re.sub('(?:\xa5|\\\\){2}', '\x00', string)
- string = re.sub('(?:\xa5|\\\\)n', '\n', string)
+ string = re.sub('\xa5', '\\\\', string)
+ string = re.sub('(\\\\){2}', '\x00', string)
+ string = re.sub('\\\\n', '\n', string)
for line in string.splitlines():
yield re.sub('\x00', '\\\\', line).strip()
@@ -33,7 +35,7 @@ def splitlabel(string):
def splittext(metrics, text, bound, measure='width'):
folded = []
if text == '':
- folded.append(u' ')
+ folded.append(u(' '))
for i in range(len(text), 0, -1):
textsize = metrics.textsize(text[0:i])
@@ -84,7 +86,7 @@ class VerticalTextFolder(object):
self._result = self._lines()
def textsize(self, text, scaled=False):
- if isinstance(text, (str, unicode)):
+ if isinstance(text, string_types):
size = [self.drawer.textlinesize(c, self.font) for c in text]
width = max(s.width for s in size)
height = (sum(s.height for s in size) +
@@ -206,7 +208,7 @@ class HorizontalTextFolder(object):
self._result = self._lines()
def textsize(self, text, scaled=False):
- if isinstance(text, (str, unicode)):
+ if isinstance(text, string_types):
textsize = self.drawer.textlinesize(text, self.font)
else:
if text:
@@ -286,9 +288,11 @@ class HorizontalTextFolder(object):
if height + textsize.height + self.line_spacing < maxheight:
lines.append(folded)
height += textsize.height + self.line_spacing
- elif len(lines) > 0:
- lines[-1] = truncate_text(self, lines[-1],
- maxwidth, measure)
+ else:
+ if len(lines) > 0:
+ lines[-1] = truncate_text(self, lines[-1],
+ maxwidth, measure)
+
finished = True
break
diff --git a/src/blockdiag/imagedraw/utils/__init__.py b/src/blockdiag/imagedraw/utils/__init__.py
index 27af44c..364c6d3 100644
--- a/src/blockdiag/imagedraw/utils/__init__.py
+++ b/src/blockdiag/imagedraw/utils/__init__.py
@@ -16,82 +16,37 @@
import math
import unicodedata
from blockdiag.utils import Size
+from blockdiag.utils.compat import u
def is_zenkaku(char):
- u"""Detect given character is Japanese ZENKAKU character
-
- >>> is_zenkaku(u"A")
- False
- >>> is_zenkaku(u"あ")
- True
- """
+ """Detect given character is Japanese ZENKAKU character"""
char_width = unicodedata.east_asian_width(char)
- return char_width in u"WFA"
+ return char_width in u("WFA")
def zenkaku_len(string):
- u"""
- Count Japanese ZENKAKU characters from string
-
- >>> zenkaku_len(u"abc")
- 0
- >>> zenkaku_len(u"あいう")
- 3
- >>> zenkaku_len(u"あいc")
- 2
- """
+ """Count Japanese ZENKAKU characters from string"""
return len([x for x in string if is_zenkaku(x)])
def hankaku_len(string):
- u"""Count non Japanese ZENKAKU characters from string
-
- >>> hankaku_len(u"abc")
- 3
- >>> hankaku_len(u"あいう")
- 0
- >>> hankaku_len(u"あいc")
- 1
- """
+ """Count non Japanese ZENKAKU characters from string"""
return len([x for x in string if not is_zenkaku(x)])
def string_width(string):
- u"""Measure rendering width of string.
- Count ZENKAKU-character as 2-point and non ZENKAKU-character as 1-point
-
- >>> string_width(u"abc")
- 3
- >>> string_width(u"あいう")
- 6
- >>> string_width(u"あいc")
- 5
+ """Measure rendering width of string.
+ Count ZENKAKU-character as 2-point and non ZENKAKU-character as 1-point
"""
widthmap = {'Na': 1, 'N': 1, 'H': 1, 'W': 2, 'F': 2, 'A': 2}
return sum(widthmap[unicodedata.east_asian_width(c)] for c in string)
def textsize(string, font):
- u"""Measure rendering size (width and height) of line.
- Returned size will not be exactly as rendered text size,
- Because this method does not use fonts to measure size.
-
- >>> from blockdiag.utils.fontmap import FontInfo
- >>> box = [0, 0, 100, 50]
- >>> font = FontInfo('serif', None, 11)
- >>> textsize(u"abc", font)
- Size(width=19, height=11)
- >>> textsize(u"あいう", font)
- Size(width=33, height=11)
- >>> textsize(u"あいc", font)
- Size(width=29, height=11)
- >>> font = FontInfo('serif', None, 24)
- >>> textsize(u"abc", font)
- Size(width=40, height=24)
- >>> font = FontInfo('serif', None, 18)
- >>> textsize(u"あいう", font)
- Size(width=54, height=18)
+ """Measure rendering size (width and height) of line.
+ Returned size will not be exactly as rendered text size,
+ Because this method does not use fonts to measure size.
"""
width = (zenkaku_len(string) * font.size +
hankaku_len(string) * font.size * 0.55)
diff --git a/src/blockdiag/imagedraw/utils/ellipse.py b/src/blockdiag/imagedraw/utils/ellipse.py
index c3d48a8..a362076 100644
--- a/src/blockdiag/imagedraw/utils/ellipse.py
+++ b/src/blockdiag/imagedraw/utils/ellipse.py
@@ -34,8 +34,8 @@ def _coordinates(du, a, b, start, end):
def endpoints(du, a, b, start, end):
- pt1 = iter(_coordinates(du, a, b, start, start + 1)).next()
- pt2 = iter(_coordinates(du, a, b, end, end + 1)).next()
+ pt1 = next(iter(_coordinates(du, a, b, start, start + 1)))
+ pt2 = next(iter(_coordinates(du, a, b, end, end + 1)))
return [XY(*pt1), XY(*pt2)]
diff --git a/src/blockdiag/metrics.py b/src/blockdiag/metrics.py
index ac64e62..dcb671d 100644
--- a/src/blockdiag/metrics.py
+++ b/src/blockdiag/metrics.py
@@ -13,12 +13,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from __future__ import division
import copy
+from collections import defaultdict
from blockdiag import noderenderer
from blockdiag.elements import DiagramNode
from blockdiag.utils import Box, Size, XY
from blockdiag.utils.fontmap import FontInfo, FontMap
-from blockdiag.utils.collections import defaultdict
cellsize = 8
@@ -234,6 +235,10 @@ class DiagramMetrics(object):
class SubMetrics(object):
def __getattr__(self, name):
+ # avoid recursion-error on Python 2.6
+ if 'metrics' not in self.__dict__:
+ raise AttributeError()
+
return getattr(self.metrics, name)
@@ -289,12 +294,12 @@ class SpreadSheetMetrics(SubMetrics):
if use_padding:
width = node.width or self.metrics.node_width
- xdiff = (self.node_width[x] - width) / 2
+ xdiff = (self.node_width[x] - width) // 2
if xdiff < 0:
xdiff = 0
height = node.height or self.metrics.node_height
- ydiff = (self.node_height[y] - height) / 2
+ ydiff = (self.node_height[y] - height) // 2
if ydiff < 0:
ydiff = 0
else:
@@ -319,12 +324,12 @@ class SpreadSheetMetrics(SubMetrics):
if use_padding:
width = node.width or self.metrics.node_width
- xdiff = (self.node_width[x] - width) / 2
+ xdiff = (self.node_width[x] - width) // 2
if xdiff < 0:
xdiff = 0
height = node.height or self.metrics.node_height
- ydiff = (self.node_height[y] - height) / 2
+ ydiff = (self.node_height[y] - height) // 2
if ydiff < 0:
ydiff = 0
else:
@@ -369,10 +374,10 @@ class NodeMetrics(SubMetrics):
@property
def marginbox(self):
- return Box(self._box.x1 - self.span_width / 8,
- self._box.y1 - self.span_height / 4,
- self._box.x2 + self.span_width / 8,
- self._box.y2 + self.span_height / 4)
+ return Box(self._box.x1 - self.span_width // 8,
+ self._box.y1 - self.span_height // 4,
+ self._box.x2 + self.span_width // 8,
+ self._box.y2 + self.span_height // 4)
@property
def corebox(self):
@@ -383,7 +388,7 @@ class NodeMetrics(SubMetrics):
@property
def grouplabelbox(self):
- return Box(self._box.x1, self._box.y1 - self.span_height / 2,
+ return Box(self._box.x1, self._box.y1 - self.span_height // 2,
self._box.x2, self._box.y1)
@@ -421,30 +426,30 @@ class EdgeMetrics(SubMetrics):
if direct == 'up':
xy = node.bottom
head.append(XY(xy.x, xy.y + 1))
- head.append(XY(xy.x - cell / 2, xy.y + cell))
+ head.append(XY(xy.x - cell // 2, xy.y + cell))
head.append(XY(xy.x, xy.y + cell * 2))
- head.append(XY(xy.x + cell / 2, xy.y + cell))
+ head.append(XY(xy.x + cell // 2, xy.y + cell))
head.append(XY(xy.x, xy.y + 1))
elif direct == 'down':
xy = node.top
head.append(XY(xy.x, xy.y - 1))
- head.append(XY(xy.x - cell / 2, xy.y - cell))
+ head.append(XY(xy.x - cell // 2, xy.y - cell))
head.append(XY(xy.x, xy.y - cell * 2))
- head.append(XY(xy.x + cell / 2, xy.y - cell))
+ head.append(XY(xy.x + cell // 2, xy.y - cell))
head.append(XY(xy.x, xy.y - 1))
elif direct == 'right':
xy = node.left
head.append(XY(xy.x - 1, xy.y))
- head.append(XY(xy.x - cell, xy.y - cell / 2))
+ head.append(XY(xy.x - cell, xy.y - cell // 2))
head.append(XY(xy.x - cell * 2, xy.y))
- head.append(XY(xy.x - cell, xy.y + cell / 2))
+ head.append(XY(xy.x - cell, xy.y + cell // 2))
head.append(XY(xy.x - 1, xy.y))
elif direct == 'left':
xy = node.right
head.append(XY(xy.x + 1, xy.y))
- head.append(XY(xy.x + cell, xy.y - cell / 2))
+ head.append(XY(xy.x + cell, xy.y - cell // 2))
head.append(XY(xy.x + cell * 2, xy.y))
- head.append(XY(xy.x + cell, xy.y + cell / 2))
+ head.append(XY(xy.x + cell, xy.y + cell // 2))
head.append(XY(xy.x + 1, xy.y))
elif direct == 'rup':
xy = node.bottom
@@ -587,12 +592,12 @@ class LandscapeEdgeMetrics(EdgeMetrics):
shaft.moveTo(node1.right)
if self.edge.skipped:
- shaft.lineTo(cell1.right.x + span.x / 2, cell1.right.y)
- shaft.lineTo(cell1.right.x + span.x / 2,
- cell1.bottomright.y + span.y / 2)
- shaft.lineTo(cell2.left.x - span.x / 4,
- cell2.bottomright.y + span.y / 2)
- shaft.lineTo(cell2.left.x - span.x / 4, cell2.left.y)
+ shaft.lineTo(cell1.right.x + span.x // 2, cell1.right.y)
+ shaft.lineTo(cell1.right.x + span.x // 2,
+ cell1.bottomright.y + span.y // 2)
+ shaft.lineTo(cell2.left.x - span.x // 4,
+ cell2.bottomright.y + span.y // 2)
+ shaft.lineTo(cell2.left.x - span.x // 4, cell2.left.y)
shaft.lineTo(node2.left)
@@ -600,40 +605,40 @@ class LandscapeEdgeMetrics(EdgeMetrics):
shaft.moveTo(node1.right)
if self.edge.skipped:
- shaft.lineTo(cell1.right.x + span.x / 2, cell1.right.y)
- shaft.lineTo(cell1.right.x + span.x / 2,
- cell2.bottomleft.y + span.y / 2)
- shaft.lineTo(cell2.left.x - span.x / 4,
- cell2.bottomleft.y + span.y / 2)
- shaft.lineTo(cell2.left.x - span.x / 4, cell2.left.y)
+ shaft.lineTo(cell1.right.x + span.x // 2, cell1.right.y)
+ shaft.lineTo(cell1.right.x + span.x // 2,
+ cell2.bottomleft.y + span.y // 2)
+ shaft.lineTo(cell2.left.x - span.x // 4,
+ cell2.bottomleft.y + span.y // 2)
+ shaft.lineTo(cell2.left.x - span.x // 4, cell2.left.y)
else:
- shaft.lineTo(cell2.left.x - span.x / 4, cell1.right.y)
- shaft.lineTo(cell2.left.x - span.x / 4, cell2.left.y)
+ shaft.lineTo(cell2.left.x - span.x // 4, cell1.right.y)
+ shaft.lineTo(cell2.left.x - span.x // 4, cell2.left.y)
shaft.lineTo(node2.left)
elif _dir == 'right-down':
shaft.moveTo(node1.right)
- shaft.lineTo(cell1.right.x + span.x / 2, cell1.right.y)
+ shaft.lineTo(cell1.right.x + span.x // 2, cell1.right.y)
if self.edge.skipped:
- shaft.lineTo(cell1.right.x + span.x / 2,
- cell2.topleft.y - span.y / 2)
- shaft.lineTo(cell2.left.x - span.x / 4,
- cell2.topleft.y - span.y / 2)
- shaft.lineTo(cell2.left.x - span.x / 4, cell2.left.y)
+ shaft.lineTo(cell1.right.x + span.x // 2,
+ cell2.topleft.y - span.y // 2)
+ shaft.lineTo(cell2.left.x - span.x // 4,
+ cell2.topleft.y - span.y // 2)
+ shaft.lineTo(cell2.left.x - span.x // 4, cell2.left.y)
else:
- shaft.lineTo(cell1.right.x + span.x / 2, cell2.left.y)
+ shaft.lineTo(cell1.right.x + span.x // 2, cell2.left.y)
shaft.lineTo(node2.left)
elif _dir == 'up':
if self.edge.skipped:
shaft.moveTo(node1.right)
- shaft.lineTo(cell1.right.x + span.x / 4, cell1.right.y)
- shaft.lineTo(cell1.right.x + span.x / 4,
- cell2.bottom.y + span.y / 2)
- shaft.lineTo(cell2.bottom.x, cell2.bottom.y + span.y / 2)
+ shaft.lineTo(cell1.right.x + span.x // 4, cell1.right.y)
+ shaft.lineTo(cell1.right.x + span.x // 4,
+ cell2.bottom.y + span.y // 2)
+ shaft.lineTo(cell2.bottom.x, cell2.bottom.y + span.y // 2)
else:
shaft.moveTo(node1.top)
@@ -641,36 +646,36 @@ class LandscapeEdgeMetrics(EdgeMetrics):
elif _dir in ('left-up', 'left', 'same'):
shaft.moveTo(node1.right)
- shaft.lineTo(cell1.right.x + span.x / 4, cell1.right.y)
- shaft.lineTo(cell1.right.x + span.x / 4,
- cell2.top.y - span.y / 2 + span.y / 8)
+ shaft.lineTo(cell1.right.x + span.x // 4, cell1.right.y)
+ shaft.lineTo(cell1.right.x + span.x // 4,
+ cell2.top.y - span.y // 2 + span.y // 8)
shaft.lineTo(cell2.top.x,
- cell2.top.y - span.y / 2 + span.y / 8)
+ cell2.top.y - span.y // 2 + span.y // 8)
shaft.lineTo(node2.top)
elif _dir == 'left-down':
if self.edge.skipped:
shaft.moveTo(node1.right)
- shaft.lineTo(cell1.right.x + span.x / 2, cell1.right.y)
- shaft.lineTo(cell1.right.x + span.x / 2,
- cell2.top.y - span.y / 2)
- shaft.lineTo(cell2.top.x, cell2.top.y - span.y / 2)
+ shaft.lineTo(cell1.right.x + span.x // 2, cell1.right.y)
+ shaft.lineTo(cell1.right.x + span.x // 2,
+ cell2.top.y - span.y // 2)
+ shaft.lineTo(cell2.top.x, cell2.top.y - span.y // 2)
else:
shaft.moveTo(node1.bottom)
shaft.lineTo(cell1.bottom.x,
- cell2.top.y - span.y / 2)
- shaft.lineTo(cell2.top.x, cell2.top.y - span.y / 2)
+ cell2.top.y - span.y // 2)
+ shaft.lineTo(cell2.top.x, cell2.top.y - span.y // 2)
shaft.lineTo(node2.top)
elif _dir == 'down':
if self.edge.skipped:
shaft.moveTo(node1.right)
- shaft.lineTo(cell1.right.x + span.x / 2, cell1.right.y)
- shaft.lineTo(cell1.right.x + span.x / 2,
- cell2.top.y - span.y / 2 + span.y / 8)
+ shaft.lineTo(cell1.right.x + span.x // 2, cell1.right.y)
+ shaft.lineTo(cell1.right.x + span.x // 2,
+ cell2.top.y - span.y // 2 + span.y // 8)
shaft.lineTo(cell2.top.x,
- cell2.top.y - span.y / 2 + span.y / 8)
+ cell2.top.y - span.y // 2 + span.y // 8)
else:
shaft.moveTo(node1.bottom)
@@ -692,40 +697,40 @@ class LandscapeEdgeMetrics(EdgeMetrics):
box = Box(node1.bottomright.x + span.x,
node1.bottomright.y,
node2.bottomleft.x - span.x,
- node2.bottomleft.y + span.y / 2)
+ node2.bottomleft.y + span.y // 2)
else:
- box = Box(node1.topright.x, node1.topright.y - span.y / 8,
- node2.left.x, node2.left.y - span.y / 8)
+ box = Box(node1.topright.x, node1.topright.y - span.y // 8,
+ node2.left.x, node2.left.y - span.y // 8)
elif _dir == 'right-up':
- box = Box(node2.left.x - span.x, node1.top.y - node.y / 2,
+ box = Box(node2.left.x - span.x, node1.top.y - node.y // 2,
node2.bottomleft.x, node1.top.y)
elif _dir == 'right-down':
- box = Box(node1.right.x, node2.topleft.y - span.y / 8,
- node1.right.x + span.x, node2.left.y - span.y / 8)
+ box = Box(node1.right.x, node2.topleft.y - span.y // 8,
+ node1.right.x + span.x, node2.left.y - span.y // 8)
elif _dir in ('up', 'left-up', 'left', 'same'):
if self.edge.node2.xy.y < self.edge.node1.xy.y:
- box = Box(node1.topright.x - span.x / 2 + span.x / 4,
- node1.topright.y - span.y / 2,
- node1.topright.x + span.x / 2 + span.x / 4,
+ box = Box(node1.topright.x - span.x // 2 + span.x // 4,
+ node1.topright.y - span.y // 2,
+ node1.topright.x + span.x // 2 + span.x // 4,
node1.topright.y)
else:
- box = Box(node1.top.x + span.x / 4,
+ box = Box(node1.top.x + span.x // 4,
node1.top.y - span.y,
- node1.topright.x + span.x / 4,
- node1.topright.y - span.y / 2)
+ node1.topright.x + span.x // 4,
+ node1.topright.y - span.y // 2)
elif _dir in ('left-down', 'down'):
- box = Box(node2.top.x + span.x / 4,
+ box = Box(node2.top.x + span.x // 4,
node2.top.y - span.y,
- node2.topright.x + span.x / 4,
- node2.topright.y - span.y / 2)
+ node2.topright.x + span.x // 4,
+ node2.topright.y - span.y // 2)
# shrink box
- box = Box(box[0] + span.x / 8, box[1],
- box[2] - span.x / 8, box[3])
+ box = Box(box[0] + span.x // 8, box[1],
+ box[2] - span.x // 8, box[3])
return box
@@ -793,64 +798,64 @@ class PortraitEdgeMetrics(EdgeMetrics):
shaft.lineTo(node2.left)
else:
shaft.moveTo(node1.bottom)
- shaft.lineTo(cell1.bottom.x, cell1.bottom.y + span.y / 2)
- shaft.lineTo(cell2.right.x + span.x / 4,
- cell1.bottom.y + span.y / 2)
- shaft.lineTo(cell2.right.x + span.x / 4,
- cell2.top.y - span.y / 2 + span.y / 8)
+ shaft.lineTo(cell1.bottom.x, cell1.bottom.y + span.y // 2)
+ shaft.lineTo(cell2.right.x + span.x // 4,
+ cell1.bottom.y + span.y // 2)
+ shaft.lineTo(cell2.right.x + span.x // 4,
+ cell2.top.y - span.y // 2 + span.y // 8)
shaft.lineTo(cell2.top.x,
- cell2.top.y - span.y / 2 + span.y / 8)
+ cell2.top.y - span.y // 2 + span.y // 8)
shaft.lineTo(node2.top)
elif _dir == 'right-down':
shaft.moveTo(node1.bottom)
- shaft.lineTo(cell1.bottom.x, cell1.bottom.y + span.y / 2)
+ shaft.lineTo(cell1.bottom.x, cell1.bottom.y + span.y // 2)
if self.edge.skipped:
- shaft.lineTo(cell2.left.x - span.x / 2,
- cell1.bottom.y + span.y / 2)
- shaft.lineTo(cell2.topleft.x - span.x / 2,
- cell2.topleft.y - span.y / 2)
- shaft.lineTo(cell2.top.x, cell2.top.y - span.y / 2)
+ shaft.lineTo(cell2.left.x - span.x // 2,
+ cell1.bottom.y + span.y // 2)
+ shaft.lineTo(cell2.topleft.x - span.x // 2,
+ cell2.topleft.y - span.y // 2)
+ shaft.lineTo(cell2.top.x, cell2.top.y - span.y // 2)
else:
- shaft.lineTo(cell2.top.x, cell1.bottom.y + span.y / 2)
+ shaft.lineTo(cell2.top.x, cell1.bottom.y + span.y // 2)
shaft.lineTo(node2.top)
elif _dir in ('left-up', 'left', 'same'):
shaft.moveTo(node1.right)
- shaft.lineTo(cell1.right.x + span.x / 4, cell1.right.y)
- shaft.lineTo(cell1.right.x + span.x / 4,
- cell2.top.y - span.y / 2 + span.y / 8)
+ shaft.lineTo(cell1.right.x + span.x // 4, cell1.right.y)
+ shaft.lineTo(cell1.right.x + span.x // 4,
+ cell2.top.y - span.y // 2 + span.y // 8)
shaft.lineTo(cell2.top.x,
- cell2.top.y - span.y / 2 + span.y / 8)
+ cell2.top.y - span.y // 2 + span.y // 8)
shaft.lineTo(node2.top)
elif _dir == 'left-down':
shaft.moveTo(node1.bottom)
if self.edge.skipped:
- shaft.lineTo(cell1.bottom.x, cell1.bottom.y + span.y / 2)
- shaft.lineTo(cell2.right.x + span.x / 2,
- cell1.bottom.y + span.y / 2)
- shaft.lineTo(cell2.right.x + span.x / 2,
- cell2.top.y - span.y / 2)
+ shaft.lineTo(cell1.bottom.x, cell1.bottom.y + span.y // 2)
+ shaft.lineTo(cell2.right.x + span.x // 2,
+ cell1.bottom.y + span.y // 2)
+ shaft.lineTo(cell2.right.x + span.x // 2,
+ cell2.top.y - span.y // 2)
else:
- shaft.lineTo(cell1.bottom.x, cell2.top.y - span.y / 2)
+ shaft.lineTo(cell1.bottom.x, cell2.top.y - span.y // 2)
- shaft.lineTo(cell2.top.x, cell2.top.y - span.y / 2)
+ shaft.lineTo(cell2.top.x, cell2.top.y - span.y // 2)
shaft.lineTo(node2.top)
elif _dir == 'down':
shaft.moveTo(node1.bottom)
if self.edge.skipped:
- shaft.lineTo(cell1.bottom.x, cell1.bottom.y + span.y / 2)
- shaft.lineTo(cell1.right.x + span.x / 2,
- cell1.bottom.y + span.y / 2)
- shaft.lineTo(cell2.right.x + span.x / 2,
- cell2.top.y - span.y / 2)
- shaft.lineTo(cell2.top.x, cell2.top.y - span.y / 2)
+ shaft.lineTo(cell1.bottom.x, cell1.bottom.y + span.y // 2)
+ shaft.lineTo(cell1.right.x + span.x // 2,
+ cell1.bottom.y + span.y // 2)
+ shaft.lineTo(cell2.right.x + span.x // 2,
+ cell2.top.y - span.y // 2)
+ shaft.lineTo(cell2.top.x, cell2.top.y - span.y // 2)
shaft.lineTo(node2.top)
@@ -869,44 +874,44 @@ class PortraitEdgeMetrics(EdgeMetrics):
box = Box(node1.bottomright.x + span.x,
node1.bottomright.y,
node2.bottomleft.x - span.x,
- node2.bottomleft.y + span.y / 2)
+ node2.bottomleft.y + span.y // 2)
else:
- box = Box(node1.topright.x, node1.topright.y - span.y / 8,
- node2.left.x, node2.left.y - span.y / 8)
+ box = Box(node1.topright.x, node1.topright.y - span.y // 8,
+ node2.left.x, node2.left.y - span.y // 8)
elif _dir == 'right-up':
box = Box(node2.left.x - span.x, node2.left.y,
node2.bottomleft.x, node2.bottomleft.y)
elif _dir == 'right-down':
- box = Box(node2.topleft.x, node2.topleft.y - span.y / 2,
+ box = Box(node2.topleft.x, node2.topleft.y - span.y // 2,
node2.top.x, node2.top.y)
elif _dir in ('up', 'left-up', 'left', 'same'):
if self.edge.node2.xy.y < self.edge.node1.xy.y:
- box = Box(node1.topright.x - span.x / 2 + span.x / 4,
- node1.topright.y - span.y / 2,
- node1.topright.x + span.x / 2 + span.x / 4,
+ box = Box(node1.topright.x - span.x // 2 + span.x // 4,
+ node1.topright.y - span.y // 2,
+ node1.topright.x + span.x // 2 + span.x // 4,
node1.topright.y)
else:
- box = Box(node1.top.x + span.x / 4,
+ box = Box(node1.top.x + span.x // 4,
node1.top.y - span.y,
- node1.topright.x + span.x / 4,
- node1.topright.y - span.y / 2)
+ node1.topright.x + span.x // 4,
+ node1.topright.y - span.y // 2)
elif _dir == 'down':
- box = Box(node2.top.x + span.x / 4,
- node2.top.y - span.y / 2,
- node2.topright.x + span.x / 4,
+ box = Box(node2.top.x + span.x // 4,
+ node2.top.y - span.y // 2,
+ node2.topright.x + span.x // 4,
node2.topright.y)
elif _dir == 'left-down':
box = Box(node1.bottomleft.x, node1.bottomleft.y,
- node1.bottom.x, node1.bottom.y + span.y / 2)
+ node1.bottom.x, node1.bottom.y + span.y // 2)
# shrink box
- box = Box(box[0] + span.x / 8, box[1],
- box[2] - span.x / 8, box[3])
+ box = Box(box[0] + span.x // 8, box[1],
+ box[2] - span.x // 8, box[3])
return box
@@ -950,10 +955,10 @@ class FlowchartLandscapeEdgeMetrics(LandscapeEdgeMetrics):
shaft.moveTo(node1.bottom)
if self.edge.skipped:
- shaft.lineTo(cell1.bottom.x, cell1.bottom.y + span.y / 2)
- shaft.lineTo(cell2.left.x - span.x / 4,
- cell1.bottom.y + span.y / 2)
- shaft.lineTo(cell2.left.x - span.x / 4, cell2.left.y)
+ shaft.lineTo(cell1.bottom.x, cell1.bottom.y + span.y // 2)
+ shaft.lineTo(cell2.left.x - span.x // 4,
+ cell1.bottom.y + span.y // 2)
+ shaft.lineTo(cell2.left.x - span.x // 4, cell2.left.y)
else:
shaft.lineTo(cell1.bottom.x, cell2.left.y)
@@ -974,9 +979,9 @@ class FlowchartLandscapeEdgeMetrics(LandscapeEdgeMetrics):
if self.edge.skipped:
box = Box(cell1.bottom.x, cell1.bottom.y,
cell1.bottomright.x,
- cell1.bottomright.y + span.y / 2)
+ cell1.bottomright.y + span.y // 2)
else:
- box = Box(cell1.bottom.x, cell2.left.y - span.y / 2,
+ box = Box(cell1.bottom.x, cell2.left.y - span.y // 2,
cell1.bottom.x, cell2.left.y)
else:
box = super(FlowchartLandscapeEdgeMetrics, self).labelbox
@@ -1023,10 +1028,11 @@ class FlowchartPortraitEdgeMetrics(PortraitEdgeMetrics):
shaft.moveTo(node1.right)
if self.edge.skipped:
- shaft.lineTo(cell1.right.x + span.x * 3 / 4, cell1.right.y)
- shaft.lineTo(cell1.right.x + span.x * 3 / 4,
- cell2.topleft.y - span.y / 2)
- shaft.lineTo(cell2.top.x, cell2.top.y - span.y / 2)
+ shaft.lineTo(cell1.right.x + span.x * 3 // 4,
+ cell1.right.y)
+ shaft.lineTo(cell1.right.x + span.x * 3 // 4,
+ cell2.topleft.y - span.y // 2)
+ shaft.lineTo(cell2.top.x, cell2.top.y - span.y // 2)
else:
shaft.lineTo(cell2.top.x, cell1.right.y)
@@ -1044,15 +1050,15 @@ class FlowchartPortraitEdgeMetrics(PortraitEdgeMetrics):
cell2 = self.cell(self.edge.node2, use_padding=False)
if _dir == 'down':
- box = Box(cell2.topleft.x, cell2.top.y - span.y / 2,
+ box = Box(cell2.topleft.x, cell2.top.y - span.y // 2,
cell2.top.x, cell2.top.y)
elif _dir == 'right':
if self.edge.skipped:
box = Box(cell1.bottom.x, cell1.bottom.y,
cell1.bottomright.x,
- cell1.bottomright.y + span.y / 2)
+ cell1.bottomright.y + span.y // 2)
else:
- box = Box(cell1.bottom.x, cell2.left.y - span.y / 2,
+ box = Box(cell1.bottom.x, cell2.left.y - span.y // 2,
cell1.bottom.x, cell2.left.y)
else:
box = super(FlowchartPortraitEdgeMetrics, self).labelbox
diff --git a/src/blockdiag/noderenderer/__init__.py b/src/blockdiag/noderenderer/__init__.py
index 96fcdfd..9bf590f 100644
--- a/src/blockdiag/noderenderer/__init__.py
+++ b/src/blockdiag/noderenderer/__init__.py
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from __future__ import division
import pkg_resources
from blockdiag.utils import images, Box, XY
@@ -66,10 +67,10 @@ class NodeShape(object):
if image_size is None:
iconsize = (0, 0)
else:
- boundedbox = [metrics.node_width / 2, metrics.node_height]
+ boundedbox = [metrics.node_width // 2, metrics.node_height]
iconsize = images.calc_image_size(image_size, boundedbox)
- vmargin = (metrics.node_height - iconsize[1]) / 2
+ vmargin = (metrics.node_height - iconsize[1]) // 2
self.iconbox = Box(m.topleft.x,
m.topleft.y + vmargin,
m.topleft.x + iconsize[0],
@@ -85,7 +86,7 @@ class NodeShape(object):
node.background = ""
for i in range(2, 0, -1):
# use original_metrics FORCE
- r = self.metrics.original_metrics.cellsize / 2 * i
+ r = self.metrics.original_metrics.cellsize // 2 * i
metrics = self.metrics.shift(r, r)
self.__class__(node, metrics).render(drawer, _format,
@@ -101,7 +102,7 @@ class NodeShape(object):
self.render_number_badge(drawer, **kwargs)
def render_icon(self, drawer, **kwargs):
- if self.node.icon is not None and kwargs.get('shadow') is False:
+ if self.node.icon is not None and kwargs.get('shadow') is not True:
drawer.image(self.iconbox, self.node.icon)
def render_shape(self, drawer, _, **kwargs):
@@ -121,7 +122,7 @@ class NodeShape(object):
badgeFill = kwargs.get('badgeFill')
xy = self.metrics.cell(self.node).topleft
- r = self.metrics.cellsize * 3 / 2
+ r = self.metrics.cellsize * 3 // 2
box = Box(xy.x - r, xy.y - r, xy.x + r, xy.y + r)
font = self.metrics.font_for(self.node)
diff --git a/src/blockdiag/noderenderer/actor.py b/src/blockdiag/noderenderer/actor.py
index bd5c520..17be67c 100644
--- a/src/blockdiag/noderenderer/actor.py
+++ b/src/blockdiag/noderenderer/actor.py
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from __future__ import division
from blockdiag.noderenderer import NodeShape
from blockdiag.noderenderer import install_renderer
from blockdiag.utils import XY, Box
@@ -24,16 +25,16 @@ class Actor(NodeShape):
shortside = min(self.node.width or metrics.node_height,
self.node.height or metrics.node_height)
- r = self.radius = shortside / 8 # radius of actor's head
+ r = self.radius = shortside // 8 # radius of actor's head
self.center = metrics.cell(node).center
- self.connectors[0] = XY(self.center.x, self.center.y - r * 9 / 2)
+ self.connectors[0] = XY(self.center.x, self.center.y - r * 9 // 2)
self.connectors[1] = XY(self.center.x + r * 4, self.center.y)
self.connectors[2] = XY(self.center.x, self.center.y + r * 4)
self.connectors[3] = XY(self.center.x - r * 4, self.center.y)
def head_part(self):
- r = self.radius * 3 / 2
+ r = self.radius * 3 // 2
pt = self.metrics.cell(self.node).center.shift(y=-self.radius * 3)
return Box(pt.x - r, pt.y - r, pt.x + r, pt.y + r)
@@ -42,12 +43,12 @@ class Actor(NodeShape):
m = self.metrics.cell(self.node)
bodyC = m.center
- neckWidth = r * 2 / 3 # neck size
+ neckWidth = r * 2 // 3 # neck size
arm = r * 4 # arm length
armWidth = r
- bodyWidth = r * 2 / 3 # half of body width
+ bodyWidth = r * 2 // 3 # half of body width
bodyHeight = r
- legXout = r * 7 / 2 # toe outer position
+ legXout = r * 7 // 2 # toe outer position
legYout = bodyHeight + r * 3
legXin = r * 2 # toe inner position
legYin = bodyHeight + r * 3
diff --git a/src/blockdiag/noderenderer/circle.py b/src/blockdiag/noderenderer/circle.py
index 90afd9b..da404ab 100644
--- a/src/blockdiag/noderenderer/circle.py
+++ b/src/blockdiag/noderenderer/circle.py
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
+from __future__ import division
from blockdiag.noderenderer import NodeShape
from blockdiag.noderenderer import install_renderer
from blockdiag.utils import Box, XY
@@ -8,8 +9,8 @@ class Circle(NodeShape):
def __init__(self, node, metrics=None):
super(Circle, self).__init__(node, metrics)
- r = min(metrics.node_width, metrics.node_height) / 2 + \
- metrics.cellsize / 2
+ r = min(metrics.node_width, metrics.node_height) // 2 + \
+ metrics.cellsize // 2
pt = metrics.cell(node).center
self.connectors = [XY(pt.x, pt.y - r), # top
XY(pt.x + r, pt.y), # right
diff --git a/src/blockdiag/noderenderer/cloud.py b/src/blockdiag/noderenderer/cloud.py
index 9e22bd7..9fca4d4 100644
--- a/src/blockdiag/noderenderer/cloud.py
+++ b/src/blockdiag/noderenderer/cloud.py
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from __future__ import division
from blockdiag.noderenderer import NodeShape
from blockdiag.noderenderer import install_renderer
from blockdiag.utils import Box
@@ -24,8 +25,8 @@ class Cloud(NodeShape):
super(Cloud, self).__init__(node, metrics)
pt = metrics.cell(node).topleft
- rx = (self.node.width or self.metrics.node_width) / 12
- ry = (self.node.height or self.metrics.node_height) / 5
+ rx = (self.node.width or self.metrics.node_width) // 12
+ ry = (self.node.height or self.metrics.node_height) // 5
self.textbox = Box(pt.x + rx * 2, pt.y + ry,
pt.x + rx * 11, pt.y + ry * 4)
@@ -41,8 +42,8 @@ class Cloud(NodeShape):
m = self.metrics.cell(self.node)
pt = m.topleft
- rx = (self.node.width or self.metrics.node_width) / 12
- ry = (self.node.height or self.metrics.node_height) / 5
+ rx = (self.node.width or self.metrics.node_width) // 12
+ ry = (self.node.height or self.metrics.node_height) // 5
ellipses = [Box(pt.x + rx * 2, pt.y + ry,
pt.x + rx * 5, pt.y + ry * 3),
@@ -95,8 +96,8 @@ class Cloud(NodeShape):
# create pathdata
m = self.metrics.cell(self.node)
- rx = (self.node.width or self.metrics.node_width) / 12
- ry = (self.node.height or self.metrics.node_height) / 5
+ rx = (self.node.width or self.metrics.node_width) // 12
+ ry = (self.node.height or self.metrics.node_height) // 5
pt = m.topleft
if kwargs.get('shadow'):
@@ -104,12 +105,12 @@ class Cloud(NodeShape):
path = pathdata(pt.x + rx * 2, pt.y + ry * 2)
path.ellarc(rx * 2, ry, 0, 0, 1, pt.x + rx * 4, pt.y + ry)
- path.ellarc(rx * 2, ry * 3 / 4, 0, 0, 1, pt.x + rx * 9, pt.y + ry)
+ path.ellarc(rx * 2, ry * 3 // 4, 0, 0, 1, pt.x + rx * 9, pt.y + ry)
path.ellarc(rx * 2, ry, 0, 0, 1, pt.x + rx * 11, pt.y + ry * 2)
path.ellarc(rx * 2, ry, 0, 0, 1, pt.x + rx * 11, pt.y + ry * 4)
- path.ellarc(rx * 2, ry * 5 / 2, 0, 0, 1, pt.x + rx * 8, pt.y + ry * 4)
- path.ellarc(rx * 2, ry * 5 / 2, 0, 0, 1, pt.x + rx * 5, pt.y + ry * 4)
- path.ellarc(rx * 2, ry * 5 / 2, 0, 0, 1, pt.x + rx * 2, pt.y + ry * 4)
+ path.ellarc(rx * 2, ry * 5 // 2, 0, 0, 1, pt.x + rx * 8, pt.y + ry * 4)
+ path.ellarc(rx * 2, ry * 5 // 2, 0, 0, 1, pt.x + rx * 5, pt.y + ry * 4)
+ path.ellarc(rx * 2, ry * 5 // 2, 0, 0, 1, pt.x + rx * 2, pt.y + ry * 4)
path.ellarc(rx * 2, ry, 0, 0, 1, pt.x + rx * 2, pt.y + ry * 2)
# draw outline
@@ -121,7 +122,7 @@ class Cloud(NodeShape):
drawer.path(path, fill=fill, outline=fill)
elif self.node.background:
drawer.path(path, fill=self.node.color, outline=self.node.color)
- drawer.loadImage(self.node.background, self.textbox)
+ drawer.image(self.textbox, self.node.background)
drawer.path(path, fill="none", outline=self.node.linecolor,
style=self.node.style)
else:
diff --git a/src/blockdiag/noderenderer/diamond.py b/src/blockdiag/noderenderer/diamond.py
index 32c8179..4567c22 100644
--- a/src/blockdiag/noderenderer/diamond.py
+++ b/src/blockdiag/noderenderer/diamond.py
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from __future__ import division
from blockdiag.noderenderer import NodeShape
from blockdiag.noderenderer import install_renderer
from blockdiag.utils import Box, XY
@@ -29,10 +30,10 @@ class Diamond(NodeShape):
XY(m.bottom.x, m.bottom.y + r),
XY(m.left.x - r, m.left.y),
XY(m.top.x, m.top.y - r)]
- self.textbox = Box((self.connectors[0].x + self.connectors[3].x) / 2,
- (self.connectors[0].y + self.connectors[3].y) / 2,
- (self.connectors[1].x + self.connectors[2].x) / 2,
- (self.connectors[1].y + self.connectors[2].y) / 2)
+ self.textbox = Box((self.connectors[0].x + self.connectors[3].x) // 2,
+ (self.connectors[0].y + self.connectors[3].y) // 2,
+ (self.connectors[1].x + self.connectors[2].x) // 2,
+ (self.connectors[1].y + self.connectors[2].y) // 2)
def render_shape(self, drawer, _, **kwargs):
fill = kwargs.get('fill')
diff --git a/src/blockdiag/noderenderer/flowchart/database.py b/src/blockdiag/noderenderer/flowchart/database.py
index 404b93c..2396483 100644
--- a/src/blockdiag/noderenderer/flowchart/database.py
+++ b/src/blockdiag/noderenderer/flowchart/database.py
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from __future__ import division
from blockdiag.noderenderer import NodeShape
from blockdiag.noderenderer import install_renderer
from blockdiag.utils import XY, Box
@@ -25,8 +26,8 @@ class Database(NodeShape):
m = self.metrics.cell(self.node)
r = self.metrics.cellsize
- self.textbox = Box(m.topleft.x, m.topleft.y + r * 3 / 2,
- m.bottomright.x, m.bottomright.y - r / 2)
+ self.textbox = Box(m.topleft.x, m.topleft.y + r * 3 // 2,
+ m.bottomright.x, m.bottomright.y - r // 2)
def render_shape(self, drawer, _, **kwargs):
# draw background
@@ -99,9 +100,9 @@ class Database(NodeShape):
box = self.shift_shadow(box)
path = pathdata(box[0], box[1] + r)
- path.ellarc(width / 2, r, 0, 0, 1, box[2], box[1] + r)
+ path.ellarc(width // 2, r, 0, 0, 1, box[2], box[1] + r)
path.line(box[2], box[3] - r)
- path.ellarc(width / 2, r, 0, 0, 1, box[0], box[3] - r)
+ path.ellarc(width // 2, r, 0, 0, 1, box[0], box[3] - r)
path.line(box[0], box[1] + r)
# draw outline
@@ -124,7 +125,7 @@ class Database(NodeShape):
# draw cap of cylinder
if not kwargs.get('shadow'):
path = pathdata(box[2], box[1] + r)
- path.ellarc(width / 2, r, 0, 0, 1, box[0], box[1] + r)
+ path.ellarc(width // 2, r, 0, 0, 1, box[0], box[1] + r)
drawer.path(path, fill=self.node.color,
outline=self.node.linecolor, style=self.node.style)
diff --git a/src/blockdiag/noderenderer/flowchart/input.py b/src/blockdiag/noderenderer/flowchart/input.py
index 8092dd7..6e928c4 100644
--- a/src/blockdiag/noderenderer/flowchart/input.py
+++ b/src/blockdiag/noderenderer/flowchart/input.py
@@ -34,11 +34,11 @@ class Input(NodeShape):
m = self.metrics.cell(self.node)
r = self.metrics.cellsize * 3
- shape = [XY(m.topleft.x + r, m.topleft.y),
+ shape = [XY(m.topleft.x + r, m.topleft.y),
XY(m.topright.x, m.topright.y),
XY(m.bottomright.x - r, m.bottomright.y),
- XY(m.bottomleft.x, m.bottomleft.y),
- XY(m.topleft.x + r, m.topleft.y)]
+ XY(m.bottomleft.x, m.bottomleft.y),
+ XY(m.topleft.x + r, m.topleft.y)]
# draw outline
if kwargs.get('shadow'):
diff --git a/src/blockdiag/noderenderer/flowchart/loopin.py b/src/blockdiag/noderenderer/flowchart/loopin.py
index 5ab7324..ac25d0c 100644
--- a/src/blockdiag/noderenderer/flowchart/loopin.py
+++ b/src/blockdiag/noderenderer/flowchart/loopin.py
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from __future__ import division
from blockdiag.noderenderer import NodeShape
from blockdiag.noderenderer import install_renderer
from blockdiag.utils import Box, XY
@@ -23,7 +24,7 @@ class LoopIn(NodeShape):
super(LoopIn, self).__init__(node, metrics)
m = self.metrics.cell(self.node)
- ydiff = self.metrics.node_height / 4
+ ydiff = self.metrics.node_height // 4
self.textbox = Box(m.topleft.x, m.topleft.y + ydiff,
m.bottomright.x, m.bottomright.y)
@@ -32,8 +33,8 @@ class LoopIn(NodeShape):
fill = kwargs.get('fill')
m = self.metrics.cell(self.node)
- xdiff = self.metrics.node_width / 4
- ydiff = self.metrics.node_height / 4
+ xdiff = self.metrics.node_width // 4
+ ydiff = self.metrics.node_height // 4
shape = [XY(m.topleft.x + xdiff, m.topleft.y),
XY(m.topright.x - xdiff, m.topleft.y),
diff --git a/src/blockdiag/noderenderer/flowchart/loopout.py b/src/blockdiag/noderenderer/flowchart/loopout.py
index e534e36..19b962d 100644
--- a/src/blockdiag/noderenderer/flowchart/loopout.py
+++ b/src/blockdiag/noderenderer/flowchart/loopout.py
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from __future__ import division
from blockdiag.noderenderer import NodeShape
from blockdiag.noderenderer import install_renderer
from blockdiag.utils import Box, XY
@@ -23,7 +24,7 @@ class LoopOut(NodeShape):
super(LoopOut, self).__init__(node, metrics)
m = self.metrics.cell(self.node)
- ydiff = self.metrics.node_height / 4
+ ydiff = self.metrics.node_height // 4
self.textbox = Box(m.topleft.x, m.topleft.y,
m.bottomright.x, m.bottomright.y - ydiff)
@@ -32,8 +33,8 @@ class LoopOut(NodeShape):
fill = kwargs.get('fill')
m = self.metrics.cell(self.node)
- xdiff = self.metrics.node_width / 4
- ydiff = self.metrics.node_height / 4
+ xdiff = self.metrics.node_width // 4
+ ydiff = self.metrics.node_height // 4
shape = [XY(m.topleft.x, m.topleft.y),
XY(m.topright.x, m.topright.y),
diff --git a/src/blockdiag/noderenderer/flowchart/terminator.py b/src/blockdiag/noderenderer/flowchart/terminator.py
index c8ba449..4d77baa 100644
--- a/src/blockdiag/noderenderer/flowchart/terminator.py
+++ b/src/blockdiag/noderenderer/flowchart/terminator.py
@@ -106,7 +106,7 @@ class Terminator(NodeShape):
drawer.path(path, fill=fill, outline=fill)
elif self.node.background:
drawer.path(path, fill=self.node.color, outline=self.node.color)
- drawer.loadImage(self.node.background, self.textbox)
+ drawer.image(self.textbox, self.node.background)
drawer.path(path, fill="none",
outline=self.node.linecolor, style=self.node.style)
else:
diff --git a/src/blockdiag/noderenderer/roundedbox.py b/src/blockdiag/noderenderer/roundedbox.py
index 54fe322..90f2351 100644
--- a/src/blockdiag/noderenderer/roundedbox.py
+++ b/src/blockdiag/noderenderer/roundedbox.py
@@ -120,7 +120,7 @@ class RoundedBox(NodeShape):
drawer.path(path, fill=fill, outline=fill)
elif self.node.background:
drawer.path(path, fill=self.node.color, outline=self.node.color)
- drawer.loadImage(self.node.background, self.textbox)
+ drawer.image(self.textbox, self.node.background)
drawer.path(path, fill="none",
outline=self.node.linecolor, style=self.node.style)
else:
diff --git a/src/blockdiag/noderenderer/square.py b/src/blockdiag/noderenderer/square.py
index 6019949..6b874e1 100644
--- a/src/blockdiag/noderenderer/square.py
+++ b/src/blockdiag/noderenderer/square.py
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
+from __future__ import division
from blockdiag.noderenderer import NodeShape
from blockdiag.noderenderer import install_renderer
from blockdiag.utils import Box, XY
@@ -8,8 +9,8 @@ class Square(NodeShape):
def __init__(self, node, metrics=None):
super(Square, self).__init__(node, metrics)
- r = min(metrics.node_width, metrics.node_height) / 2 + \
- metrics.cellsize / 2
+ r = min(metrics.node_width, metrics.node_height) // 2 + \
+ metrics.cellsize // 2
pt = metrics.cell(node).center
self.connectors = [XY(pt.x, pt.y - r), # top
XY(pt.x + r, pt.y), # right
diff --git a/src/blockdiag/noderenderer/textbox.py b/src/blockdiag/noderenderer/textbox.py
index 9ff5ccb..3b58cb1 100644
--- a/src/blockdiag/noderenderer/textbox.py
+++ b/src/blockdiag/noderenderer/textbox.py
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from __future__ import division
from blockdiag.noderenderer import NodeShape
from blockdiag.noderenderer import install_renderer
from blockdiag.utils import images, Box, XY
@@ -27,8 +28,8 @@ class TextBox(NodeShape):
size = images.calc_image_size(size, self.textbox.size)
pt = self.textbox.center
- self.textbox = Box(pt.x - size[0] / 2, pt.y - size[1] / 2,
- pt.x + size[0] / 2, pt.y + size[1] / 2)
+ self.textbox = Box(pt.x - size[0] // 2, pt.y - size[1] // 2,
+ pt.x + size[0] // 2, pt.y + size[1] // 2)
self.connectors[0] = XY(pt.x, self.textbox[1])
self.connectors[1] = XY(self.textbox[2], pt.y)
diff --git a/src/blockdiag/parser.py b/src/blockdiag/parser.py
index 3adb1e9..33bf44a 100644
--- a/src/blockdiag/parser.py
+++ b/src/blockdiag/parser.py
@@ -35,24 +35,23 @@ At the moment, the parser builds only a parse tree, not an abstract syntax tree
[1]: http://www.graphviz.org/doc/info/lang.html
'''
-import codecs
+import io
from re import MULTILINE, DOTALL
+from collections import namedtuple
from funcparserlib.lexer import make_tokenizer, Token, LexerError
from funcparserlib.parser import (some, a, maybe, many, finished, skip,
forward_decl)
+from blockdiag.utils.compat import u
-from blockdiag.utils.collections import namedtuple
ENCODING = 'utf-8'
-Graph = namedtuple('Graph', 'type id stmts')
-SubGraph = namedtuple('SubGraph', 'id stmts')
+Diagram = namedtuple('Diagram', 'type id stmts')
+Group = namedtuple('Group', 'id stmts')
Node = namedtuple('Node', 'id attrs')
Attr = namedtuple('Attr', 'name value')
-Edge = namedtuple('Edge', 'nodes attrs')
-DefAttrs = namedtuple('DefAttrs', 'object attrs')
-AttrPlugin = namedtuple('AttrPlugin', 'name attrs')
-AttrClass = namedtuple('AttrClass', 'name attrs')
+Edge = namedtuple('Edge', 'from_nodes edge_type to_nodes attrs')
+Extension = namedtuple('Extension', 'type name attrs')
Statements = namedtuple('Statements', 'stmts')
@@ -61,144 +60,184 @@ class ParseException(Exception):
def tokenize(string):
- 'str -> Sequence(Token)'
- specs = [
- ('Comment', (r'/\*(.|[\r\n])*?\*/', MULTILINE)),
- ('Comment', (r'(//|#).*',)),
- ('NL', (r'[\r\n]+',)),
- ('Space', (r'[ \t\r\n]+',)),
- ('Name', (ur'[A-Za-z_0-9\u0080-\uffff]'
- ur'[A-Za-z_\-.0-9\u0080-\uffff]*',)),
- ('Op', (r'[{};,=\[\]]|(<->)|(<-)|(--)|(->)|(>-<)|(-<)|(>-)',)),
- ('Number', (r'-?(\.[0-9]+)|([0-9]+(\.[0-9]*)?)',)),
- ('String', (r'(?P<quote>"|\').*?(?<!\\)(?P=quote)', DOTALL)),
+ """str -> Sequence(Token)"""
+ # flake8: NOQA
+ specs = [ # NOQA
+ ('Comment', (r'/\*(.|[\r\n])*?\*/', MULTILINE)), # NOQA
+ ('Comment', (r'(//|#).*',)), # NOQA
+ ('NL', (r'[\r\n]+',)), # NOQA
+ ('Space', (r'[ \t\r\n]+',)), # NOQA
+ ('Name', (u('[A-Za-z_0-9\u0080-\uffff]') + # NOQA
+ u('[A-Za-z_\\-.0-9\u0080-\uffff]*'),)), # NOQA
+ ('Op', (r'[{};,=\[\]]|(<->)|(<-)|(--)|(->)|(>-<)|(-<)|(>-)',)), # NOQA
+ ('Number', (r'-?(\.[0-9]+)|([0-9]+(\.[0-9]*)?)',)), # NOQA
+ ('String', (r'(?P<quote>"|\').*?(?<!\\)(?P=quote)', DOTALL)), # NOQA
]
useless = ['Comment', 'NL', 'Space']
t = make_tokenizer(specs)
return [x for x in t(string) if x.type not in useless]
+def create_mapper(fn, default_value=None):
+ if default_value is None:
+ return lambda args: fn(*args)
+ else:
+ return lambda args: fn(*args) if args else default_value
+
+
+def flatten(seq):
+ return sum(seq, [])
+
+
+def oneplus_to_list(first, more):
+ return [first] + more
+
+
def parse(seq):
- 'Sequence(Token) -> object'
- unarg = lambda f: lambda args: f(*args)
+ """Sequence(Token) -> object"""
tokval = lambda x: x.value
- flatten = lambda list: sum(list, [])
- node_flatten = lambda l: sum([[l[0]]] + list(l[1:]), [])
- n = lambda s: a(Token('Name', s)) >> tokval
op = lambda s: a(Token('Op', s)) >> tokval
op_ = lambda s: skip(op(s))
- _id = some(lambda t:
- t.type in ['Name', 'Number', 'String']).named('id') >> tokval
- make_nodes = lambda args: Statements([Node(n, args[-1]) for n in args[0]])
- make_graph_attr = lambda args: DefAttrs(u'graph', [Attr(*args)])
- make_edge = lambda x, x2, xs, attrs: Edge([x, x2] + xs, attrs)
+ _id = some(lambda t: t.type in ['Name', 'Number', 'String']) >> tokval
+ keyword = lambda s: a(Token('Name', s)) >> tokval
+
+ def make_node_list(node_list, attrs):
+ return Statements([Node(node, attrs) for node in node_list])
+
+ def make_edge(first, edge_type, second, followers, attrs):
+ edges = [Edge(first, edge_type, second, attrs)]
+
+ from_node = second
+ for edge_type, to_node in followers:
+ edges.append(Edge(from_node, edge_type, to_node, attrs))
+ from_node = to_node
+
+ return Statements(edges)
#
# parts of syntax
#
- node_id = _id # + maybe(port)
node_list = (
- node_id +
- many(op_(',') + node_id)
- >> node_flatten)
- a_list = (
_id +
- maybe(op_('=') + _id) +
- skip(maybe(op(',')))
- >> unarg(Attr))
- attr_list = (
- many(op_('[') + many(a_list) + op_(']'))
- >> flatten)
- graph_attr = _id + op_('=') + _id >> make_graph_attr
-
- # nodes statements::
+ many(op_(',') + _id)
+ >> create_mapper(oneplus_to_list)
+ )
+ option_stmt = (
+ _id +
+ maybe(op_('=') + _id)
+ >> create_mapper(Attr)
+ )
+ option_list = (
+ maybe(op_('[') + option_stmt + many(op_(',') + option_stmt) + op_(']'))
+ >> create_mapper(oneplus_to_list, default_value=[])
+ )
+
+ # node (node list) statement::
# A;
# B [attr = value, attr = value];
# C, D [attr = value, attr = value];
#
- multi_node_stmt = node_list + attr_list >> make_nodes
+ node_stmt = (
+ node_list + option_list
+ >> create_mapper(make_node_list)
+ )
- # edge statements::
+ # edge statement::
# A -> B;
# A <- B;
#
- edge_rhs = (op('->') | op('--') | op('<-') | op('<->') |
- op('>-') | op('-<') | op('>-<')) + node_list
+ edge_relation = (
+ op('->') | op('--') | op('<-') | op('<->') |
+ op('>-') | op('-<') | op('>-<')
+ )
edge_stmt = (
node_list +
- edge_rhs +
- many(edge_rhs) +
- attr_list
- >> unarg(make_edge))
+ edge_relation +
+ node_list +
+ many(edge_relation + node_list) +
+ option_list
+ >> create_mapper(make_edge)
+ )
- # class statements::
- # class red [color = red];
+ # attributes statement::
+ # default_shape = box;
+ # default_fontsize = 16;
#
- class_stmt = (
- skip(n('class')) +
- node_id +
- attr_list
- >> unarg(AttrClass))
+ attribute_stmt = (
+ _id + op_('=') + _id
+ >> create_mapper(Attr)
+ )
- # plugin statements::
+ # extension statement (class, plugin)::
+ # class red [color = red];
# plugin attributes [name = Name];
#
- plugin_stmt = (
- skip(n('plugin')) +
- node_id +
- attr_list
- >> unarg(AttrPlugin))
+ extension_stmt = (
+ (keyword('class') | keyword('plugin')) +
+ _id +
+ option_list
+ >> create_mapper(Extension)
+ )
- # group statements::
+ # group statement::
# group {
# A;
# }
#
- group = forward_decl()
- stmt = (
- edge_stmt
- | class_stmt
- | plugin_stmt
- | group
- | graph_attr
- | multi_node_stmt
+ group_stmt = forward_decl()
+ group_inline_stmt = (
+ edge_stmt |
+ group_stmt |
+ attribute_stmt |
+ node_stmt
)
- stmt_list = many(stmt + skip(maybe(op(';'))))
- group.define(
- skip(n('group')) +
+ group_inline_stmt_list = (
+ many(group_inline_stmt + skip(maybe(op(';'))))
+ )
+ group_stmt.define(
+ skip(keyword('group')) +
maybe(_id) +
op_('{') +
- stmt_list +
+ group_inline_stmt_list +
op_('}')
- >> unarg(SubGraph))
+ >> create_mapper(Group)
+ )
#
- # graph
+ # diagram statement::
+ # blockdiag {
+ # A;
+ # }
#
- graph = (
- maybe(n('diagram') | n('blockdiag')) +
+ diagram_inline_stmt = (
+ extension_stmt |
+ group_inline_stmt
+ )
+ diagram_inline_stmt_list = (
+ many(diagram_inline_stmt + skip(maybe(op(';'))))
+ )
+ diagram = (
+ maybe(keyword('diagram') | keyword('blockdiag')) +
maybe(_id) +
op_('{') +
- stmt_list +
+ diagram_inline_stmt_list +
op_('}')
- >> unarg(Graph))
- dotfile = graph + skip(finished)
+ >> create_mapper(Diagram)
+ )
+ dotfile = diagram + skip(finished)
return dotfile.parse(seq)
def sort_tree(tree):
def weight(node):
- if isinstance(node, (Attr, DefAttrs, AttrPlugin, AttrClass)):
+ if isinstance(node, (Attr, Extension)):
return 1
else:
return 2
- def compare(a, b):
- return cmp(weight(a), weight(b))
-
if hasattr(tree, 'stmts'):
- tree.stmts.sort(compare)
+ tree.stmts.sort(key=lambda x: weight(x))
for stmt in tree.stmts:
sort_tree(stmt)
@@ -209,13 +248,13 @@ def parse_string(string):
try:
tree = parse(tokenize(string))
return sort_tree(tree)
- except LexerError, e:
+ except LexerError as e:
message = "Got unexpected token at line %d column %d" % e.place
raise ParseException(message)
- except Exception, e:
+ except Exception as e:
raise ParseException(str(e))
def parse_file(path):
- code = codecs.open(path, 'r', 'utf-8').read()
+ code = io.open(path, 'r', encoding='utf-8-sig').read()
return parse_string(code)
diff --git a/src/blockdiag/tests/diagrams/branched.diag b/src/blockdiag/tests/diagrams/branched.diag
index 5a9aae7..e4d0908 100644
--- a/src/blockdiag/tests/diagrams/branched.diag
+++ b/src/blockdiag/tests/diagrams/branched.diag
@@ -1,5 +1,5 @@
-diagram {
- A -> B -> C;
- A -> D -> E;
- Z
-}
+diagram {
+ A -> B -> C;
+ A -> D -> E;
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/circular_ref.diag b/src/blockdiag/tests/diagrams/circular_ref.diag
index a028996..6ab650a 100644
--- a/src/blockdiag/tests/diagrams/circular_ref.diag
+++ b/src/blockdiag/tests/diagrams/circular_ref.diag
@@ -1,5 +1,5 @@
-diagram {
- A -> B -> C -> B
- B -> D
- Z
-}
+diagram {
+ A -> B -> C -> B
+ B -> D
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/circular_ref_to_root.diag b/src/blockdiag/tests/diagrams/circular_ref_to_root.diag
index bb7d0bb..2c3e6e2 100644
--- a/src/blockdiag/tests/diagrams/circular_ref_to_root.diag
+++ b/src/blockdiag/tests/diagrams/circular_ref_to_root.diag
@@ -1,5 +1,5 @@
-diagram {
- A -> B -> C -> A
- B -> D
- Z
-}
+diagram {
+ A -> B -> C -> A
+ B -> D
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/circular_skipped_edge.diag b/src/blockdiag/tests/diagrams/circular_skipped_edge.diag
index 9afb986..511cba3 100644
--- a/src/blockdiag/tests/diagrams/circular_skipped_edge.diag
+++ b/src/blockdiag/tests/diagrams/circular_skipped_edge.diag
@@ -1,5 +1,5 @@
-diagram {
- A -> B -> C -> A
- A -> C
- Z
-}
+diagram {
+ A -> B -> C -> A
+ A -> C
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/edge_attribute.diag b/src/blockdiag/tests/diagrams/edge_attribute.diag
index da131c3..0ecc4cf 100644
--- a/src/blockdiag/tests/diagrams/edge_attribute.diag
+++ b/src/blockdiag/tests/diagrams/edge_attribute.diag
@@ -1,5 +1,5 @@
-diagram {
- A -> B -> C [color = red]
- D -> E [dir = none]
- F -> G [thick]
-}
+diagram {
+ A -> B -> C [color = red]
+ D -> E [dir = none]
+ F -> G [thick]
+}
diff --git a/src/blockdiag/tests/diagrams/edge_styles.diag b/src/blockdiag/tests/diagrams/edge_styles.diag
index 074c367..4779bdf 100644
--- a/src/blockdiag/tests/diagrams/edge_styles.diag
+++ b/src/blockdiag/tests/diagrams/edge_styles.diag
@@ -1,9 +1,9 @@
-diagram {
- A -> B [style = "none"];
- B -> C [style = "solid"];
- C -> D [style = "dashed"];
- D -> E [style = "dotted"];
- E -> F [hstyle = "generalization"];
- F -> H [hstyle = "composition"];
- H -> I [hstyle = "aggregation"];
-}
+diagram {
+ A -> B [style = "none"];
+ B -> C [style = "solid"];
+ C -> D [style = "dashed"];
+ D -> E [style = "dotted"];
+ E -> F [hstyle = "generalization"];
+ F -> H [hstyle = "composition"];
+ H -> I [hstyle = "aggregation"];
+}
diff --git a/src/blockdiag/tests/diagrams/empty_group.diag b/src/blockdiag/tests/diagrams/empty_group.diag
index 35c4f88..a1e36d4 100644
--- a/src/blockdiag/tests/diagrams/empty_group.diag
+++ b/src/blockdiag/tests/diagrams/empty_group.diag
@@ -1,5 +1,5 @@
-diagram {
- group {
- }
- Z
-}
+diagram {
+ group {
+ }
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/empty_nested_group.diag b/src/blockdiag/tests/diagrams/empty_nested_group.diag
index 63fa0f4..0872fe2 100644
--- a/src/blockdiag/tests/diagrams/empty_nested_group.diag
+++ b/src/blockdiag/tests/diagrams/empty_nested_group.diag
@@ -1,7 +1,7 @@
-diagram {
- group {
- group {
- }
- }
- Z;
-}
+diagram {
+ group {
+ group {
+ }
+ }
+ Z;
+}
diff --git a/src/blockdiag/tests/diagrams/errors/belongs_to_two_groups.diag b/src/blockdiag/tests/diagrams/errors/belongs_to_two_groups.diag
index d2f9deb..2a8b71a 100644
--- a/src/blockdiag/tests/diagrams/errors/belongs_to_two_groups.diag
+++ b/src/blockdiag/tests/diagrams/errors/belongs_to_two_groups.diag
@@ -1,9 +1,9 @@
-diagram {
- group {
- A
- }
- group {
- A
- }
- Z
-}
+diagram {
+ group {
+ A
+ }
+ group {
+ A
+ }
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/errors/group_follows_node.diag b/src/blockdiag/tests/diagrams/errors/group_follows_node.diag
index 0ba78f2..2255ab4 100644
--- a/src/blockdiag/tests/diagrams/errors/group_follows_node.diag
+++ b/src/blockdiag/tests/diagrams/errors/group_follows_node.diag
@@ -1,6 +1,6 @@
-diagram {
- A -> group {
- B
- }
- Z
-}
+diagram {
+ A -> group {
+ B
+ }
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/errors/node_follows_group.diag b/src/blockdiag/tests/diagrams/errors/node_follows_group.diag
index 0ba78f2..2255ab4 100644
--- a/src/blockdiag/tests/diagrams/errors/node_follows_group.diag
+++ b/src/blockdiag/tests/diagrams/errors/node_follows_group.diag
@@ -1,6 +1,6 @@
-diagram {
- A -> group {
- B
- }
- Z
-}
+diagram {
+ A -> group {
+ B
+ }
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/flowable_node.diag b/src/blockdiag/tests/diagrams/flowable_node.diag
index a44399c..0891a41 100644
--- a/src/blockdiag/tests/diagrams/flowable_node.diag
+++ b/src/blockdiag/tests/diagrams/flowable_node.diag
@@ -1,5 +1,5 @@
-diagram {
- B -> C
- A -> B
- Z
-}
+diagram {
+ B -> C
+ A -> B
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/folded_edge.diag b/src/blockdiag/tests/diagrams/folded_edge.diag
index 58d4f35..4300950 100644
--- a/src/blockdiag/tests/diagrams/folded_edge.diag
+++ b/src/blockdiag/tests/diagrams/folded_edge.diag
@@ -1,6 +1,6 @@
-diagram {
- A -> B -> C [nofolded]
- B -> D -> E[folded]
- D -> F
- Z
-}
+diagram {
+ A -> B -> C [nofolded]
+ B -> D -> E[folded]
+ D -> F
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/group_id_and_node_id_are_not_conflicted.diag b/src/blockdiag/tests/diagrams/group_id_and_node_id_are_not_conflicted.diag
index fd7789f..eeb7470 100644
--- a/src/blockdiag/tests/diagrams/group_id_and_node_id_are_not_conflicted.diag
+++ b/src/blockdiag/tests/diagrams/group_id_and_node_id_are_not_conflicted.diag
@@ -1,7 +1,7 @@
-diagram {
- A -> B
- group B {
- C -> D
- }
- Z
-}
+diagram {
+ A -> B
+ group B {
+ C -> D
+ }
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/group_works_node_decorator.diag b/src/blockdiag/tests/diagrams/group_works_node_decorator.diag
index 1c87a02..c824197 100644
--- a/src/blockdiag/tests/diagrams/group_works_node_decorator.diag
+++ b/src/blockdiag/tests/diagrams/group_works_node_decorator.diag
@@ -1,9 +1,9 @@
-diagram {
- A -> B -> C
- A -> B -> D
- A -> E
- group {
- A; B; D; E
- }
- Z
-}
+diagram {
+ A -> B -> C
+ A -> B -> D
+ A -> E
+ group {
+ A; B; D; E
+ }
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/large_group_and_node.diag b/src/blockdiag/tests/diagrams/large_group_and_node.diag
index 909b08c..485cd3f 100644
--- a/src/blockdiag/tests/diagrams/large_group_and_node.diag
+++ b/src/blockdiag/tests/diagrams/large_group_and_node.diag
@@ -1,10 +1,10 @@
-diagram {
- group {
- A -> B
- A -> C
- A -> D
- A -> E
- }
- B -> F
- Z
-}
+diagram {
+ group {
+ A -> B
+ A -> C
+ A -> D
+ A -> E
+ }
+ B -> F
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/large_group_and_node2.diag b/src/blockdiag/tests/diagrams/large_group_and_node2.diag
index fcf1aaa..8e14ec7 100644
--- a/src/blockdiag/tests/diagrams/large_group_and_node2.diag
+++ b/src/blockdiag/tests/diagrams/large_group_and_node2.diag
@@ -1,7 +1,7 @@
-diagram {
- group {
- A -> B -> C
- }
- C -> D -> E
- Z
-}
+diagram {
+ group {
+ A -> B -> C
+ }
+ C -> D -> E
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/large_group_and_two_nodes.diag b/src/blockdiag/tests/diagrams/large_group_and_two_nodes.diag
index f4837a4..b5895c1 100644
--- a/src/blockdiag/tests/diagrams/large_group_and_two_nodes.diag
+++ b/src/blockdiag/tests/diagrams/large_group_and_two_nodes.diag
@@ -1,11 +1,11 @@
-diagram {
- group {
- A -> B
- A -> C
- A -> D
- A -> E
- }
- B -> F
- C -> G
- Z
-}
+diagram {
+ group {
+ A -> B
+ A -> C
+ A -> D
+ A -> E
+ }
+ B -> F
+ C -> G
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/multiple_groups.diag b/src/blockdiag/tests/diagrams/multiple_groups.diag
index 17a1b77..b1b4881 100644
--- a/src/blockdiag/tests/diagrams/multiple_groups.diag
+++ b/src/blockdiag/tests/diagrams/multiple_groups.diag
@@ -1,16 +1,16 @@
-diagram {
- group {
- A; B; C; D
- }
- group {
- E; F; G
- }
- group {
- H; I
- }
- group {
- J
- }
- A -> E -> H -> J
- Z
-}
+diagram {
+ group {
+ A; B; C; D
+ }
+ group {
+ E; F; G
+ }
+ group {
+ H; I
+ }
+ group {
+ J
+ }
+ A -> E -> H -> J
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/multiple_nested_groups.diag b/src/blockdiag/tests/diagrams/multiple_nested_groups.diag
index da88c1f..65272b8 100644
--- a/src/blockdiag/tests/diagrams/multiple_nested_groups.diag
+++ b/src/blockdiag/tests/diagrams/multiple_nested_groups.diag
@@ -1,14 +1,14 @@
-diagram {
- group {
- A -> B;
- A -> C;
-
- group {
- B
- }
- group {
- C
- }
- }
- Z
-}
+diagram {
+ group {
+ A -> B;
+ A -> C;
+
+ group {
+ B
+ }
+ group {
+ C
+ }
+ }
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/nested_groups.diag b/src/blockdiag/tests/diagrams/nested_groups.diag
index 09b46f0..f60042b 100644
--- a/src/blockdiag/tests/diagrams/nested_groups.diag
+++ b/src/blockdiag/tests/diagrams/nested_groups.diag
@@ -1,9 +1,9 @@
-diagram {
- group {
- A;
- group {
- B;
- }
- }
- Z;
-}
+diagram {
+ group {
+ A;
+ group {
+ B;
+ }
+ }
+ Z;
+}
diff --git a/src/blockdiag/tests/diagrams/nested_groups_and_edges.diag b/src/blockdiag/tests/diagrams/nested_groups_and_edges.diag
index 3de67a5..34f7762 100644
--- a/src/blockdiag/tests/diagrams/nested_groups_and_edges.diag
+++ b/src/blockdiag/tests/diagrams/nested_groups_and_edges.diag
@@ -1,11 +1,11 @@
-diagram {
- A -> B -> C;
-
- group {
- B;
- group {
- C;
- }
- }
- Z;
-}
+diagram {
+ A -> B -> C;
+
+ group {
+ B;
+ group {
+ C;
+ }
+ }
+ Z;
+}
diff --git a/src/blockdiag/tests/diagrams/nested_groups_work_node_decorator.diag b/src/blockdiag/tests/diagrams/nested_groups_work_node_decorator.diag
index f566be0..0677cf9 100644
--- a/src/blockdiag/tests/diagrams/nested_groups_work_node_decorator.diag
+++ b/src/blockdiag/tests/diagrams/nested_groups_work_node_decorator.diag
@@ -1,11 +1,11 @@
-diagram {
- A; B;
-
- group {
- A
- group {
- B
- }
- }
- Z
-}
+diagram {
+ A; B;
+
+ group {
+ A
+ group {
+ B
+ }
+ }
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/nested_skipped_circular.diag b/src/blockdiag/tests/diagrams/nested_skipped_circular.diag
index 6bad6ee..d049966 100644
--- a/src/blockdiag/tests/diagrams/nested_skipped_circular.diag
+++ b/src/blockdiag/tests/diagrams/nested_skipped_circular.diag
@@ -1,7 +1,7 @@
-diagram {
- A -> B -> F -> G
- B -> C -> E -> F
- C -> D -> E
- F -> A
- Z
-}
+diagram {
+ A -> B -> F -> G
+ B -> C -> E -> F
+ C -> D -> E
+ F -> A
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/node_attribute.diag b/src/blockdiag/tests/diagrams/node_attribute.diag
index ee9af22..f31d784 100644
--- a/src/blockdiag/tests/diagrams/node_attribute.diag
+++ b/src/blockdiag/tests/diagrams/node_attribute.diag
@@ -1,12 +1,12 @@
-diagram {
- A [label="B", color="red"];
- B [label="double quoted"];
- C [label='single quoted', color = 'red'];
- D [label="'\"double\" quoted'", color = 'red'];
- E [label='"\'single\' quoted"', color = 'red', numbered = "1"];
- F [textcolor = red];
- G [stacked];
- H [fontsize = 16];
- I [linecolor = red];
- J [label="Hello", label_orientation=vertical];
-}
+diagram {
+ A [label="B", color="red"];
+ B [label="double quoted"];
+ C [label='single quoted', color = 'red'];
+ D [label="'\"double\" quoted'", color = 'red'];
+ E [label='"\'single\' quoted"', color = 'red', numbered = "1"];
+ F [textcolor = red];
+ G [stacked];
+ H [fontsize = 16];
+ I [linecolor = red];
+ J [label="Hello", label_orientation=vertical];
+}
diff --git a/src/blockdiag/tests/diagrams/node_in_group_follows_outer_node.diag b/src/blockdiag/tests/diagrams/node_in_group_follows_outer_node.diag
index 7b364f1..223551b 100644
--- a/src/blockdiag/tests/diagrams/node_in_group_follows_outer_node.diag
+++ b/src/blockdiag/tests/diagrams/node_in_group_follows_outer_node.diag
@@ -1,7 +1,7 @@
-diagram {
- group {
- A -> B
- }
- B -> C
- Z
-}
+diagram {
+ group {
+ A -> B
+ }
+ B -> C
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/node_styles.diag b/src/blockdiag/tests/diagrams/node_styles.diag
index e94e61b..e3892ae 100644
--- a/src/blockdiag/tests/diagrams/node_styles.diag
+++ b/src/blockdiag/tests/diagrams/node_styles.diag
@@ -1,5 +1,5 @@
-diagram {
- A [shape = "roundedbox", style = "dotted"];
- B [shape = "ellipse", style = "dashed"];
- C [shape = "flowchart.database", style = "dashed"];
-}
+diagram {
+ A [shape = "roundedbox", style = "dotted"];
+ B [shape = "ellipse", style = "dashed"];
+ C [shape = "flowchart.database", style = "dashed"];
+}
diff --git a/src/blockdiag/tests/diagrams/outer_node_follows_node_in_group.diag b/src/blockdiag/tests/diagrams/outer_node_follows_node_in_group.diag
index 5fa91fd..db32858 100644
--- a/src/blockdiag/tests/diagrams/outer_node_follows_node_in_group.diag
+++ b/src/blockdiag/tests/diagrams/outer_node_follows_node_in_group.diag
@@ -1,7 +1,7 @@
-diagram {
- group {
- B -> C
- }
- A -> B
- Z
-}
+diagram {
+ group {
+ B -> C
+ }
+ A -> B
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/reverse_multiple_groups.diag b/src/blockdiag/tests/diagrams/reverse_multiple_groups.diag
index 8792260..12103ee 100644
--- a/src/blockdiag/tests/diagrams/reverse_multiple_groups.diag
+++ b/src/blockdiag/tests/diagrams/reverse_multiple_groups.diag
@@ -1,16 +1,16 @@
-diagram {
- group {
- A; B; C; D
- }
- group {
- E; F; G
- }
- group {
- H; I
- }
- group {
- J
- }
- J -> H -> E -> A
- Z
-}
+diagram {
+ group {
+ A; B; C; D
+ }
+ group {
+ E; F; G
+ }
+ group {
+ H; I
+ }
+ group {
+ J
+ }
+ J -> H -> E -> A
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/self_ref.diag b/src/blockdiag/tests/diagrams/self_ref.diag
index d4d953a..e4c1f38 100644
--- a/src/blockdiag/tests/diagrams/self_ref.diag
+++ b/src/blockdiag/tests/diagrams/self_ref.diag
@@ -1,4 +1,4 @@
-diagram {
- A -> B -> B
- Z
-}
+diagram {
+ A -> B -> B
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/simple_group.diag b/src/blockdiag/tests/diagrams/simple_group.diag
index 68bf4c2..efd57a5 100644
--- a/src/blockdiag/tests/diagrams/simple_group.diag
+++ b/src/blockdiag/tests/diagrams/simple_group.diag
@@ -1,7 +1,7 @@
-diagram {
- group {
- A -> B
- A -> C
- }
- Z
-}
+diagram {
+ group {
+ A -> B
+ A -> C
+ }
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/single_edge.diag b/src/blockdiag/tests/diagrams/single_edge.diag
index f36edf5..b08ad62 100644
--- a/src/blockdiag/tests/diagrams/single_edge.diag
+++ b/src/blockdiag/tests/diagrams/single_edge.diag
@@ -1,3 +1,3 @@
-diagram {
- A -> B;
-}
+diagram {
+ A -> B;
+}
diff --git a/src/blockdiag/tests/diagrams/single_node.diag b/src/blockdiag/tests/diagrams/single_node.diag
index 203f740..8621ae7 100644
--- a/src/blockdiag/tests/diagrams/single_node.diag
+++ b/src/blockdiag/tests/diagrams/single_node.diag
@@ -1,3 +1,3 @@
-diagram {
- A;
-}
+diagram {
+ A;
+}
diff --git a/src/blockdiag/tests/diagrams/skipped_circular.diag b/src/blockdiag/tests/diagrams/skipped_circular.diag
index fd7ce2b..cd212bf 100644
--- a/src/blockdiag/tests/diagrams/skipped_circular.diag
+++ b/src/blockdiag/tests/diagrams/skipped_circular.diag
@@ -1,6 +1,6 @@
-diagram {
- A -> C
- A -> B -> C
- C -> A
- Z
-}
+diagram {
+ A -> C
+ A -> B -> C
+ C -> A
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/skipped_edge.diag b/src/blockdiag/tests/diagrams/skipped_edge.diag
index 5a99655..fd156b1 100644
--- a/src/blockdiag/tests/diagrams/skipped_edge.diag
+++ b/src/blockdiag/tests/diagrams/skipped_edge.diag
@@ -1,5 +1,5 @@
-diagram {
- A -> B -> C
- A -> C
- Z
-}
+diagram {
+ A -> B -> C
+ A -> C
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/triple_branched.diag b/src/blockdiag/tests/diagrams/triple_branched.diag
index 8155ac9..fe5aa2a 100644
--- a/src/blockdiag/tests/diagrams/triple_branched.diag
+++ b/src/blockdiag/tests/diagrams/triple_branched.diag
@@ -1,6 +1,6 @@
-diagram {
- A -> D
- B -> D
- C -> D
- Z
-}
+diagram {
+ A -> D
+ B -> D
+ C -> D
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/twin_circular_ref_to_root.diag b/src/blockdiag/tests/diagrams/twin_circular_ref_to_root.diag
index 31495dd..4ec6226 100644
--- a/src/blockdiag/tests/diagrams/twin_circular_ref_to_root.diag
+++ b/src/blockdiag/tests/diagrams/twin_circular_ref_to_root.diag
@@ -1,5 +1,5 @@
-diagram {
- A -> B -> A
- A -> C -> A
- Z
-}
+diagram {
+ A -> B -> A
+ A -> C -> A
+ Z
+}
diff --git a/src/blockdiag/tests/diagrams/two_edges.diag b/src/blockdiag/tests/diagrams/two_edges.diag
index 6683b17..cbc7704 100644
--- a/src/blockdiag/tests/diagrams/two_edges.diag
+++ b/src/blockdiag/tests/diagrams/two_edges.diag
@@ -1,3 +1,3 @@
-diagram {
- A -> B -> C;
-}
+diagram {
+ A -> B -> C;
+}
diff --git a/src/blockdiag/tests/diagrams/white.gif b/src/blockdiag/tests/diagrams/white.gif
new file mode 100644
index 0000000..c761db5
Binary files /dev/null and b/src/blockdiag/tests/diagrams/white.gif differ
diff --git a/src/blockdiag/tests/test_boot_params.py b/src/blockdiag/tests/test_boot_params.py
index e0c2c2b..bf850f3 100644
--- a/src/blockdiag/tests/test_boot_params.py
+++ b/src/blockdiag/tests/test_boot_params.py
@@ -1,253 +1,186 @@
# -*- coding: utf-8 -*-
-import os
import sys
+if sys.version_info < (2, 7):
+ import unittest2 as unittest
+else:
+ import unittest
+
+import os
+import io
import tempfile
-import unittest2
-from blockdiag.tests.utils import argv_wrapper, assertRaises, with_pdf
+from blockdiag.tests.utils import with_pdf
import blockdiag
from blockdiag.command import BlockdiagOptions
from blockdiag.utils.bootstrap import detectfont
+from blockdiag.utils.compat import u
-class TestBootParams(unittest2.TestCase):
+class TestBootParams(unittest.TestCase):
def setUp(self):
self.parser = BlockdiagOptions(blockdiag)
- @argv_wrapper
def test_type_option_svg(self):
- sys.argv = ['', '-Tsvg', 'input.diag']
- options = self.parser.parse()
+ options = self.parser.parse(['-Tsvg', 'input.diag'])
self.assertEqual(options.output, 'input.svg')
- sys.argv = ['', '-TSVG', 'input.diag']
- options = self.parser.parse()
+ options = self.parser.parse(['-TSVG', 'input.diag'])
self.assertEqual(options.output, 'input.svg')
- sys.argv = ['', '-TSvg', 'input.diag']
- options = self.parser.parse()
+ options = self.parser.parse(['-TSvg', 'input.diag'])
self.assertEqual(options.output, 'input.svg')
- sys.argv = ['', '-TSvg', 'input.test.diag']
- options = self.parser.parse()
+ options = self.parser.parse(['-TSvg', 'input.test.diag'])
self.assertEqual(options.output, 'input.test.svg')
- @argv_wrapper
def test_type_option_png(self):
- sys.argv = ['', '-Tpng', 'input.diag']
- options = self.parser.parse()
+ options = self.parser.parse(['-Tpng', 'input.diag'])
self.assertEqual(options.output, 'input.png')
@with_pdf
- @argv_wrapper
def test_type_option_pdf(self):
- sys.argv = ['', '-Tpdf', 'input.diag']
- options = self.parser.parse()
+ options = self.parser.parse(['-Tpdf', 'input.diag'])
self.assertEqual(options.output, 'input.pdf')
- @assertRaises(RuntimeError)
- @argv_wrapper
def test_invalid_type_option(self):
- sys.argv = ['', '-Tsvgz', 'input.diag']
- self.parser.parse()
+ with self.assertRaises(RuntimeError):
+ self.parser.parse(['-Tsvgz', 'input.diag'])
- @argv_wrapper
def test_separate_option_svg(self):
- sys.argv = ['', '-Tsvg', '--separate', 'input.diag']
- self.parser.parse()
+ self.parser.parse(['-Tsvg', '--separate', 'input.diag'])
- @argv_wrapper
def test_separate_option_png(self):
- sys.argv = ['', '-Tpng', '--separate', 'input.diag']
- self.parser.parse()
+ self.parser.parse(['-Tpng', '--separate', 'input.diag'])
@with_pdf
- @argv_wrapper
def test_separate_option_pdf(self):
- sys.argv = ['', '-Tpdf', '--separate', 'input.diag']
- self.parser.parse()
-
- @argv_wrapper
- def test_svg_ignore_pil_option(self):
- sys.argv = ['', '-Tsvg', '--ignore-pil', 'input.diag']
- self.parser.parse()
-
- @assertRaises(RuntimeError)
- @argv_wrapper
- def test_png_ignore_pil_option(self):
- sys.argv = ['', '-Tpng', '--ignore-pil', 'input.diag']
- self.parser.parse()
-
- @assertRaises(RuntimeError)
- @argv_wrapper
- def test_pdf_ignore_pil_option(self):
- sys.argv = ['', '-Tpdf', '--ignore-pil', 'input.diag']
- self.parser.parse()
-
- @argv_wrapper
+ self.parser.parse(['-Tpdf', '--separate', 'input.diag'])
+
def test_svg_nodoctype_option(self):
- sys.argv = ['', '-Tsvg', '--nodoctype', 'input.diag']
- self.parser.parse()
+ self.parser.parse(['-Tsvg', '--nodoctype', 'input.diag'])
- @assertRaises(RuntimeError)
- @argv_wrapper
def test_png_nodoctype_option(self):
- sys.argv = ['', '-Tpng', '--nodoctype', 'input.diag']
- self.parser.parse()
+ with self.assertRaises(RuntimeError):
+ self.parser.parse(['-Tpng', '--nodoctype', 'input.diag'])
- @assertRaises(RuntimeError)
- @argv_wrapper
def test_pdf_nodoctype_option(self):
- sys.argv = ['', '-Tpdf', '--nodoctype', 'input.diag']
- self.parser.parse()
+ with self.assertRaises(RuntimeError):
+ self.parser.parse(['-Tpdf', '--nodoctype', 'input.diag'])
- @assertRaises(RuntimeError)
- @argv_wrapper
def test_svg_notransparency_option(self):
- sys.argv = ['', '-Tsvg', '--no-transparency', 'input.diag']
- self.parser.parse()
+ with self.assertRaises(RuntimeError):
+ self.parser.parse(['-Tsvg', '--no-transparency', 'input.diag'])
- @argv_wrapper
def test_png_notransparency_option(self):
- sys.argv = ['', '-Tpng', '--no-transparency', 'input.diag']
- self.parser.parse()
+ self.parser.parse(['-Tpng', '--no-transparency', 'input.diag'])
- @assertRaises(RuntimeError)
- @argv_wrapper
def test_pdf_notransparency_option(self):
- sys.argv = ['', '-Tpdf', '--no-transparency', 'input.diag']
- self.parser.parse()
+ with self.assertRaises(RuntimeError):
+ self.parser.parse(['-Tpdf', '--no-transparency', 'input.diag'])
- @argv_wrapper
def test_config_option(self):
try:
tmp = tempfile.mkstemp()
- sys.argv = ['', '-c', tmp[1], 'input.diag']
- self.parser.parse()
+ self.parser.parse(['-c', tmp[1], 'input.diag'])
finally:
os.close(tmp[0])
os.unlink(tmp[1])
- @argv_wrapper
def test_config_option_with_bom(self):
try:
tmp = tempfile.mkstemp()
- fp = os.fdopen(tmp[0], 'wt')
- fp.write("\xEF\xBB\xBF[blockdiag]\n")
+ fp = io.open(tmp[0], 'wt', encoding='utf-8-sig')
+ fp.write(u("[blockdiag]\n"))
fp.close()
- sys.argv = ['', '-c', tmp[1], 'input.diag']
- self.parser.parse()
+ self.parser.parse(['-c', tmp[1], 'input.diag'])
finally:
os.unlink(tmp[1])
- @assertRaises(RuntimeError)
- @argv_wrapper
def test_invalid_config_option(self):
- sys.argv = ['', '-c', '/unknown_config_file', 'input.diag']
- self.parser.parse()
+ with self.assertRaises(RuntimeError):
+ self.parser.parse(['-c', '/unknown_config_file', 'input.diag'])
- @assertRaises(RuntimeError)
- @argv_wrapper
def test_invalid_dir_config_option(self):
try:
tmp = tempfile.mkdtemp()
- sys.argv = ['', '-c', tmp, 'input.diag']
- self.parser.parse()
+ with self.assertRaises(RuntimeError):
+ self.parser.parse(['-c', tmp, 'input.diag'])
finally:
os.rmdir(tmp)
- @argv_wrapper
def test_config_option_fontpath(self):
try:
tmp = tempfile.mkstemp()
- config = '[blockdiag]\nfontpath = /path/to/font\n'
- os.fdopen(tmp[0], 'wt').write(config)
+ config = u("[blockdiag]\nfontpath = /path/to/font\n")
+ io.open(tmp[0], 'wt').write(config)
- sys.argv = ['', '-c', tmp[1], 'input.diag']
- options = self.parser.parse()
+ options = self.parser.parse(['-c', tmp[1], 'input.diag'])
self.assertEqual(options.font, ['/path/to/font'])
finally:
os.unlink(tmp[1])
- @argv_wrapper
def test_exist_font_config_option(self):
try:
tmp = tempfile.mkstemp()
- sys.argv = ['', '-f', tmp[1], 'input.diag']
- options = self.parser.parse()
+ options = self.parser.parse(['-f', tmp[1], 'input.diag'])
self.assertEqual(options.font, [tmp[1]])
fontpath = detectfont(options)
self.assertEqual(fontpath, tmp[1])
finally:
os.unlink(tmp[1])
- @assertRaises(RuntimeError)
- @argv_wrapper
def test_not_exist_font_config_option(self):
- sys.argv = ['', '-f', '/font_is_not_exist', 'input.diag']
- options = self.parser.parse()
- detectfont(options)
+ with self.assertRaises(RuntimeError):
+ args = ['-f', '/font_is_not_exist', 'input.diag']
+ options = self.parser.parse(args)
+ detectfont(options)
- @assertRaises(RuntimeError)
- @argv_wrapper
def test_not_exist_font_config_option2(self):
- sys.argv = ['', '-f', '/font_is_not_exist',
+ with self.assertRaises(RuntimeError):
+ args = ['-f', '/font_is_not_exist',
'-f', '/font_is_not_exist2', 'input.diag']
- options = self.parser.parse()
- detectfont(options)
+ options = self.parser.parse(args)
+ detectfont(options)
- @argv_wrapper
def test_no_size_option(self):
- sys.argv = ['', 'input.diag']
- options = self.parser.parse()
+ options = self.parser.parse(['input.diag'])
self.assertEqual(None, options.size)
- @argv_wrapper
def test_size_option(self):
- sys.argv = ['', '--size', '480x360', 'input.diag']
- options = self.parser.parse()
+ options = self.parser.parse(['--size', '480x360', 'input.diag'])
self.assertEqual([480, 360], options.size)
- @assertRaises(RuntimeError)
- @argv_wrapper
def test_invalid_size_option1(self):
- sys.argv = ['', '--size', '480-360', 'input.diag']
- self.parser.parse()
+ with self.assertRaises(RuntimeError):
+ self.parser.parse(['--size', '480-360', 'input.diag'])
- @assertRaises(RuntimeError)
- @argv_wrapper
def test_invalid_size_option2(self):
- sys.argv = ['', '--size', '480', 'input.diag']
- self.parser.parse()
+ with self.assertRaises(RuntimeError):
+ self.parser.parse(['--size', '480', 'input.diag'])
- @assertRaises(RuntimeError)
- @argv_wrapper
def test_invalid_size_option3(self):
- sys.argv = ['', '--size', 'foobar', 'input.diag']
- self.parser.parse()
+ with self.assertRaises(RuntimeError):
+ self.parser.parse(['--size', 'foobar', 'input.diag'])
- @argv_wrapper
def test_auto_font_detection(self):
- sys.argv = ['', 'input.diag']
- options = self.parser.parse()
+ options = self.parser.parse(['input.diag'])
fontpath = detectfont(options)
self.assertTrue(fontpath)
- @assertRaises(RuntimeError)
- @argv_wrapper
def test_not_exist_fontmap_config(self):
- sys.argv = ['', '--fontmap', '/fontmap_is_not_exist', 'input.diag']
- options = self.parser.parse()
- fontpath = detectfont(options)
- self.assertTrue(fontpath)
+ with self.assertRaises(RuntimeError):
+ args = ['--fontmap', '/fontmap_is_not_exist', 'input.diag']
+ options = self.parser.parse(args)
+ fontpath = detectfont(options)
+ self.assertTrue(fontpath)
- @assertRaises(RuntimeError)
def test_unknown_image_driver(self):
from blockdiag.drawer import DiagramDraw
from blockdiag.elements import Diagram
- DiagramDraw('unknown', Diagram())
+ with self.assertRaises(RuntimeError):
+ DiagramDraw('unknown', Diagram())
diff --git a/src/blockdiag/tests/test_builder.py b/src/blockdiag/tests/test_builder.py
index cefe4bf..60f22f8 100644
--- a/src/blockdiag/tests/test_builder.py
+++ b/src/blockdiag/tests/test_builder.py
@@ -1,164 +1,157 @@
# -*- coding: utf-8 -*-
-from nose.tools import eq_
-from blockdiag.tests.utils import __build_diagram, __validate_node_attributes
-
-
-def test_diagram_attributes():
- diagram = __build_diagram('diagram_attributes.diag')
-
- eq_(160, diagram.node_width)
- eq_(160, diagram.node_height)
- eq_(32, diagram.span_width)
- eq_(32, diagram.span_height)
- eq_((128, 128, 128), diagram.linecolor) # gray
- eq_('diamond', diagram.nodes[0].shape)
- eq_((255, 0, 0), diagram.nodes[0].color) # red
- eq_((0, 128, 0), diagram.nodes[0].textcolor) # green
- eq_(16, diagram.nodes[0].fontsize)
- eq_((0, 0, 255), diagram.nodes[1].color) # blue
- eq_((0, 128, 0), diagram.nodes[1].textcolor) # green
- eq_(16, diagram.nodes[1].fontsize)
-
- eq_((128, 128, 128), diagram.edges[0].color) # gray
- eq_((0, 128, 0), diagram.edges[0].textcolor) # green
- eq_(16, diagram.edges[0].fontsize)
-
-
-def test_diagram_attributes_order_diagram():
- colors = {'A': (255, 0, 0), 'B': (255, 0, 0)}
- linecolors = {'A': (255, 0, 0), 'B': (255, 0, 0)}
- __validate_node_attributes('diagram_attributes_order.diag',
- color=colors, linecolor=linecolors)
-
-
-def test_circular_ref_to_root_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0),
- 'D': (2, 1), 'Z': (0, 2)}
- __validate_node_attributes('circular_ref_to_root.diag', xy=positions)
-
-
-def test_circular_ref_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0),
- 'D': (2, 1), 'Z': (0, 2)}
- __validate_node_attributes('circular_ref.diag', xy=positions)
-
-
-def test_circular_ref2_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0), 'D': (3, 0),
- 'E': (3, 1), 'F': (4, 0), 'Z': (0, 2)}
- __validate_node_attributes('circular_ref2.diag', xy=positions)
-
-
-def test_circular_ref_and_parent_node_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (1, 1),
- 'D': (2, 1), 'Z': (0, 2)}
- __validate_node_attributes('circular_ref_and_parent_node.diag',
- xy=positions)
-
-
-def test_labeled_circular_ref_diagram():
- positions = {'A': (0, 0), 'B': (2, 0), 'C': (1, 0),
- 'Z': (0, 1)}
- __validate_node_attributes('labeled_circular_ref.diag', xy=positions)
-
-
-def test_twin_forked_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (1, 2), 'D': (2, 0),
- 'E': (3, 0), 'F': (3, 1), 'G': (4, 1), 'Z': (0, 3)}
- __validate_node_attributes('twin_forked.diag', xy=positions)
-
-
-def test_skipped_edge_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0), 'Z': (0, 1)}
- __validate_node_attributes('skipped_edge.diag', xy=positions)
-
-
-def test_circular_skipped_edge_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0), 'Z': (0, 1)}
- __validate_node_attributes('circular_skipped_edge.diag', xy=positions)
-
-
-def test_triple_branched_diagram():
- positions = {'A': (0, 0), 'B': (0, 1), 'C': (0, 2),
- 'D': (1, 0), 'Z': (0, 3)}
- __validate_node_attributes('triple_branched.diag', xy=positions)
-
-
-def test_twin_circular_ref_to_root_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (1, 1), 'Z': (0, 2)}
- __validate_node_attributes('twin_circular_ref_to_root.diag', xy=positions)
-
-
-def test_twin_circular_ref_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0),
- 'D': (1, 1), 'Z': (0, 2)}
- __validate_node_attributes('twin_circular_ref.diag', xy=positions)
-
-
-def test_skipped_circular_diagram():
- positions = {'A': (0, 0), 'B': (1, 1), 'C': (2, 0),
- 'Z': (0, 2)}
- __validate_node_attributes('skipped_circular.diag', xy=positions)
-
-
-def test_skipped_twin_circular_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 1),
- 'D': (2, 2), 'E': (3, 0), 'Z': (0, 3)}
- __validate_node_attributes('skipped_twin_circular.diag', xy=positions)
-
-
-def test_nested_skipped_circular_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 1),
- 'D': (3, 2), 'E': (4, 1), 'F': (5, 0),
- 'G': (6, 0), 'Z': (0, 3)}
- __validate_node_attributes('nested_skipped_circular.diag', xy=positions)
-
-
-def test_self_ref_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'Z': (0, 1)}
- __validate_node_attributes('self_ref.diag', xy=positions)
-
-
-def test_diagram_orientation_diagram():
- positions = {'A': (0, 0), 'B': (0, 1), 'C': (0, 2),
- 'D': (1, 2), 'Z': (2, 0)}
- __validate_node_attributes('diagram_orientation.diag', xy=positions)
-
-
-def test_nested_group_orientation2_diagram():
- positions = {'A': (0, 0), 'B': (0, 1), 'C': (0, 2), 'D': (1, 2),
- 'E': (2, 2), 'F': (2, 3), 'Z': (3, 0)}
- __validate_node_attributes('nested_group_orientation2.diag', xy=positions)
-
-
-def test_slided_children_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0), 'D': (1, 3),
- 'E': (2, 3), 'F': (3, 2), 'G': (2, 1), 'H': (4, 1)}
- __validate_node_attributes('slided_children.diag', xy=positions)
-
-
-def test_rhombus_relation_height_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (1, 1), 'D': (2, 0),
- 'E': (3, 0), 'F': (3, 1), 'Z': (0, 2)}
- __validate_node_attributes('rhombus_relation_height.diag', xy=positions)
-
-
-def test_non_rhombus_relation_height_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0), 'D': (0, 1),
- 'E': (0, 2), 'F': (1, 2), 'G': (1, 3), 'H': (2, 3),
- 'I': (2, 4), 'J': (1, 5), 'K': (2, 5), 'Z': (0, 6)}
- __validate_node_attributes('non_rhombus_relation_height.diag',
- xy=positions)
-
-
-def test_define_class_diagram():
- colors = {'A': (255, 0, 0), 'B': (255, 255, 255), 'C': (255, 255, 255)}
- styles = {'A': 'dashed', 'B': None, 'C': None}
-
- edge_colors = {('A', 'B'): (255, 0, 0), ('B', 'C'): (0, 0, 0)}
- edge_styles = {('A', 'B'): 'dashed', ('B', 'C'): None}
-
- __validate_node_attributes('define_class.diag',
- color=colors, edge_color=edge_colors,
- style=styles, edge_style=edge_styles)
+from blockdiag.tests.utils import BuilderTestCase
+
+
+class TestBuilder(BuilderTestCase):
+ def test_diagram_attributes(self):
+ diagram = self.build('diagram_attributes.diag')
+
+ self.assertEqual(160, diagram.node_width)
+ self.assertEqual(160, diagram.node_height)
+ self.assertEqual(32, diagram.span_width)
+ self.assertEqual(32, diagram.span_height)
+ self.assertEqual((128, 128, 128), diagram.linecolor) # gray
+ self.assertEqual('diamond', diagram.nodes[0].shape)
+ self.assertEqual((255, 0, 0), diagram.nodes[0].color) # red
+ self.assertEqual((0, 128, 0), diagram.nodes[0].textcolor) # green
+ self.assertEqual(16, diagram.nodes[0].fontsize)
+ self.assertEqual((0, 0, 255), diagram.nodes[1].color) # blue
+ self.assertEqual((0, 128, 0), diagram.nodes[1].textcolor) # green
+ self.assertEqual(16, diagram.nodes[1].fontsize)
+
+ self.assertEqual((128, 128, 128), diagram.edges[0].color) # gray
+ self.assertEqual((0, 128, 0), diagram.edges[0].textcolor) # green
+ self.assertEqual(16, diagram.edges[0].fontsize)
+
+ def test_diagram_attributes_order_diagram(self):
+ diagram = self.build('diagram_attributes_order.diag')
+ self.assertNodeColor(diagram, {'A': (255, 0, 0), 'B': (255, 0, 0)})
+ self.assertNodeLineColor(diagram, {'A': (255, 0, 0), 'B': (255, 0, 0)})
+
+ def test_circular_ref_to_root_diagram(self):
+ diagram = self.build('circular_ref_to_root.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 0), 'D': (2, 1),
+ 'Z': (0, 2)})
+
+ def test_circular_ref_diagram(self):
+ diagram = self.build('circular_ref.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 0), 'D': (2, 1),
+ 'Z': (0, 2)})
+
+ def test_circular_ref2_diagram(self):
+ diagram = self.build('circular_ref2.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 0), 'D': (3, 0),
+ 'E': (3, 1), 'F': (4, 0),
+ 'Z': (0, 2)})
+
+ def test_circular_ref_and_parent_node_diagram(self):
+ diagram = self.build('circular_ref_and_parent_node.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (1, 1), 'D': (2, 1),
+ 'Z': (0, 2)})
+
+ def test_labeled_circular_ref_diagram(self):
+ diagram = self.build('labeled_circular_ref.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (2, 0),
+ 'C': (1, 0), 'Z': (0, 1)})
+
+ def test_twin_forked_diagram(self):
+ diagram = self.build('twin_forked.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (1, 2), 'D': (2, 0),
+ 'E': (3, 0), 'F': (3, 1),
+ 'G': (4, 1), 'Z': (0, 3)})
+
+ def test_skipped_edge_diagram(self):
+ diagram = self.build('skipped_edge.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 0), 'Z': (0, 1)})
+
+ def test_circular_skipped_edge_diagram(self):
+ diagram = self.build('circular_skipped_edge.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 0), 'Z': (0, 1)})
+
+ def test_triple_branched_diagram(self):
+ diagram = self.build('triple_branched.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (0, 1),
+ 'C': (0, 2), 'D': (1, 0),
+ 'Z': (0, 3)})
+
+ def test_twin_circular_ref_to_root_diagram(self):
+ diagram = self.build('twin_circular_ref_to_root.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (1, 1), 'Z': (0, 2)})
+
+ def test_twin_circular_ref_diagram(self):
+ diagram = self.build('twin_circular_ref.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 0), 'D': (1, 1),
+ 'Z': (0, 2)})
+
+ def test_skipped_circular_diagram(self):
+ diagram = self.build('skipped_circular.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 1),
+ 'C': (2, 0), 'Z': (0, 2)})
+
+ def test_skipped_twin_circular_diagram(self):
+ diagram = self.build('skipped_twin_circular.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 1), 'D': (2, 2),
+ 'E': (3, 0), 'Z': (0, 3)})
+
+ def test_nested_skipped_circular_diagram(self):
+ diagram = self.build('nested_skipped_circular.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 1), 'D': (3, 2),
+ 'E': (4, 1), 'F': (5, 0),
+ 'G': (6, 0), 'Z': (0, 3)})
+
+ def test_self_ref_diagram(self):
+ diagram = self.build('self_ref.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'Z': (0, 1)})
+
+ def test_diagram_orientation_diagram(self):
+ diagram = self.build('diagram_orientation.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (0, 1),
+ 'C': (0, 2), 'D': (1, 2),
+ 'Z': (2, 0)})
+
+ def test_nested_group_orientation2_diagram(self):
+ diagram = self.build('nested_group_orientation2.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (0, 1),
+ 'C': (0, 2), 'D': (1, 2),
+ 'E': (2, 2), 'F': (2, 3),
+ 'Z': (3, 0)})
+
+ def test_slided_children_diagram(self):
+ diagram = self.build('slided_children.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 0), 'D': (1, 3),
+ 'E': (2, 3), 'F': (3, 2),
+ 'G': (2, 1), 'H': (4, 1)})
+
+ def test_non_rhombus_relation_height_diagram(self):
+ diagram = self.build('non_rhombus_relation_height.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 0), 'D': (0, 1),
+ 'E': (0, 2), 'F': (1, 2),
+ 'G': (1, 3), 'H': (2, 3),
+ 'I': (2, 4), 'J': (1, 5),
+ 'K': (2, 5), 'Z': (0, 6)})
+
+ def test_define_class_diagram(self):
+ diagram = self.build('define_class.diag')
+ self.assertNodeColor(diagram, {'A': (255, 0, 0),
+ 'B': (255, 255, 255),
+ 'C': (255, 255, 255)})
+ self.assertNodeStyle(diagram, {'A': 'dashed', 'B': None, 'C': None})
+ self.assertEdgeColor(diagram, {('A', 'B'): (255, 0, 0),
+ ('B', 'C'): (0, 0, 0)})
+ self.assertEdgeStyle(diagram, {('A', 'B'): 'dashed',
+ ('B', 'C'): None})
diff --git a/src/blockdiag/tests/test_builder_edge.py b/src/blockdiag/tests/test_builder_edge.py
index d1bd5b8..9eacd7f 100644
--- a/src/blockdiag/tests/test_builder_edge.py
+++ b/src/blockdiag/tests/test_builder_edge.py
@@ -1,155 +1,143 @@
# -*- coding: utf-8 -*-
-from blockdiag.tests.utils import (stderr_wrapper, __build_diagram,
- __validate_node_attributes)
-
-
-def test_single_edge_diagram():
- diagram = __build_diagram('single_edge.diag')
-
- assert len(diagram.nodes) == 2
- assert len(diagram.edges) == 1
-
- positions = {'A': (0, 0), 'B': (1, 0)}
- labels = {'A': 'A', 'B': 'B'}
- __validate_node_attributes('single_edge.diag', xy=positions, label=labels)
-
-
-def test_two_edges_diagram():
- diagram = __build_diagram('two_edges.diag')
-
- assert len(diagram.nodes) == 3
- assert len(diagram.edges) == 2
-
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0)}
- __validate_node_attributes('two_edges.diag', xy=positions)
-
-
-def test_edge_shape():
- diagram = __build_diagram('edge_shape.diag')
-
- for edge in diagram.edges:
- if edge.node1.id == 'A':
- assert edge.dir == 'none'
- elif edge.node1.id == 'B':
- assert edge.dir == 'forward'
- elif edge.node1.id == 'C':
- assert edge.dir == 'back'
- elif edge.node1.id == 'D':
- assert edge.dir == 'both'
-
-
-def test_edge_attribute():
- diagram = __build_diagram('edge_attribute.diag')
-
- for edge in diagram.edges:
- if edge.node1.id == 'D':
- assert edge.dir == 'none'
- assert edge.color == (0, 0, 0)
- assert edge.thick is None
- elif edge.node1.id == 'F':
- assert edge.dir == 'forward'
- assert edge.color == (0, 0, 0)
- assert edge.thick == 3
- else:
- assert edge.dir == 'forward'
- assert edge.color == (255, 0, 0) # red
- assert edge.thick is None
-
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0),
- 'D': (0, 1), 'E': (1, 1), 'F': (0, 2), 'G': (1, 2)}
- __validate_node_attributes('edge_attribute.diag', xy=positions)
-
-
-def test_folded_edge_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0), 'D': (0, 1),
- 'E': (0, 2), 'F': (1, 1), 'Z': (0, 3)}
- __validate_node_attributes('folded_edge.diag', xy=positions)
-
-
-def test_skipped_edge_right_diagram():
- filename = 'skipped_edge_right.diag'
- skipped = {('A', 'B'): False, ('A', 'C'): True}
- __validate_node_attributes(filename, edge_skipped=skipped)
-
-
-def test_skipped_edge_rightup_diagram():
- filename = 'skipped_edge_rightup.diag'
- skipped = {('A', 'B'): False, ('D', 'C'): True}
- __validate_node_attributes(filename, edge_skipped=skipped)
-
-
-def test_skipped_edge_rightdown_diagram():
- filename = 'skipped_edge_rightdown.diag'
- skipped = {('A', 'B'): False, ('A', 'C'): True}
- __validate_node_attributes(filename, edge_skipped=skipped)
-
-
-def test_skipped_edge_up_diagram():
- filename = 'skipped_edge_up.diag'
- skipped = {('C', 'A'): True}
- __validate_node_attributes(filename, edge_skipped=skipped)
-
-
-def test_skipped_edge_down_diagram():
- filename = 'skipped_edge_down.diag'
- skipped = {('A', 'C'): True}
- __validate_node_attributes(filename, edge_skipped=skipped)
-
-
-def test_skipped_edge_leftdown_diagram():
- filename = 'skipped_edge_leftdown.diag'
- skipped = {('A', 'B'): False, ('C', 'G'): True}
- __validate_node_attributes(filename, edge_skipped=skipped)
-
-
- at stderr_wrapper
-def test_skipped_edge_flowchart_rightdown_diagram():
- filename = 'skipped_edge_flowchart_rightdown.diag'
- skipped = {('A', 'B'): False, ('A', 'D'): True}
- __validate_node_attributes(filename, edge_skipped=skipped)
-
-
- at stderr_wrapper
-def test_skipped_edge_flowchart_rightdown2_diagram():
- filename = 'skipped_edge_flowchart_rightdown2.diag'
- skipped = {('B', 'C'): False, ('A', 'C'): True}
- __validate_node_attributes(filename, edge_skipped=skipped)
-
-
-def test_skipped_edge_portrait_right_diagram():
- filename = 'skipped_edge_portrait_right.diag'
- skipped = {('A', 'C'): True}
- __validate_node_attributes(filename, edge_skipped=skipped)
-
-
-def test_skipped_edge_portrait_rightdown_diagram():
- filename = 'skipped_edge_portrait_rightdown.diag'
- skipped = {('A', 'B'): False, ('A', 'E'): True}
- __validate_node_attributes(filename, edge_skipped=skipped)
-
-
-def test_skipped_edge_portrait_leftdown_diagram():
- filename = 'skipped_edge_portrait_leftdown.diag'
- skipped = {('A', 'B'): False, ('D', 'C'): True}
- __validate_node_attributes(filename, edge_skipped=skipped)
-
-
-def test_skipped_edge_portrait_down_diagram():
- filename = 'skipped_edge_portrait_down.diag'
- skipped = {('A', 'B'): False, ('A', 'C'): True}
- __validate_node_attributes(filename, edge_skipped=skipped)
-
-
- at stderr_wrapper
-def test_skipped_edge_portrait_flowchart_rightdown_diagram():
- filename = 'skipped_edge_portrait_flowchart_rightdown.diag'
- skipped = {('A', 'B'): False, ('A', 'D'): True}
- __validate_node_attributes(filename, edge_skipped=skipped)
-
-
- at stderr_wrapper
-def test_skipped_edge_portrait_flowchart_rightdown2_diagram():
- filename = 'skipped_edge_portrait_flowchart_rightdown2.diag'
- skipped = {('B', 'C'): False, ('A', 'C'): True}
- __validate_node_attributes(filename, edge_skipped=skipped)
+from blockdiag.tests.utils import BuilderTestCase, capture_stderr
+
+
+class TestBuilderEdge(BuilderTestCase):
+ def test_diagram_attributes(self):
+ diagram = self.build('diagram_attributes.diag')
+ self.assertEqual(2, len(diagram.nodes))
+ self.assertEqual(1, len(diagram.edges))
+
+ def test_single_edge_diagram(self):
+ diagram = self.build('single_edge.diag')
+ self.assertEqual(2, len(diagram.nodes))
+ self.assertEqual(1, len(diagram.edges))
+
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0)})
+ self.assertNodeLabel(diagram, {'A': 'A', 'B': 'B'})
+
+ def test_two_edges_diagram(self):
+ diagram = self.build('two_edges.diag')
+ self.assertEqual(3, len(diagram.nodes))
+ self.assertEqual(2, len(diagram.edges))
+
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0), 'C': (2, 0)})
+
+ def test_edge_shape(self):
+ diagram = self.build('edge_shape.diag')
+ self.assertEdgeDir(diagram, {('A', 'B'): 'none',
+ ('B', 'C'): 'forward',
+ ('C', 'D'): 'back',
+ ('D', 'E'): 'both'})
+
+ def test_edge_attribute(self):
+ diagram = self.build('edge_attribute.diag')
+ self.assertEdgeDir(diagram, {('A', 'B'): 'forward',
+ ('B', 'C'): 'forward',
+ ('C', 'D'): 'forward',
+ ('D', 'E'): 'none',
+ ('E', 'F'): 'both',
+ ('F', 'G'): 'forward'})
+ self.assertEdgeColor(diagram, {('A', 'B'): (255, 0, 0), # red
+ ('B', 'C'): (255, 0, 0), # red
+ ('C', 'D'): (255, 0, 0), # red
+ ('D', 'E'): (0, 0, 0),
+ ('E', 'F'): (255, 0, 0), # red
+ ('F', 'G'): (0, 0, 0)})
+ self.assertEdgeThick(diagram, {('A', 'B'): None,
+ ('B', 'C'): None,
+ ('C', 'D'): None,
+ ('D', 'E'): None,
+ ('E', 'F'): None,
+ ('F', 'G'): 3})
+
+ def test_folded_edge_diagram(self):
+ diagram = self.build('folded_edge.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 0), 'D': (0, 1),
+ 'E': (0, 2), 'F': (1, 1),
+ 'Z': (0, 3)})
+
+ def test_skipped_edge_right_diagram(self):
+ diagram = self.build('skipped_edge_right.diag')
+ self.assertEdgeSkipped(diagram, {('A', 'B'): False,
+ ('A', 'C'): True,
+ ('B', 'C'): False})
+
+ def test_skipped_edge_rightdown_diagram(self):
+ diagram = self.build('skipped_edge_rightdown.diag')
+ self.assertEdgeSkipped(diagram, {('A', 'B'): False,
+ ('A', 'C'): True,
+ ('A', 'D'): False,
+ ('B', 'C'): False,
+ ('B', 'D'): False})
+
+ def test_skipped_edge_up_diagram(self):
+ diagram = self.build('skipped_edge_up.diag')
+ self.assertEdgeSkipped(diagram, {('C', 'A'): True})
+
+ def test_skipped_edge_down_diagram(self):
+ diagram = self.build('skipped_edge_down.diag')
+ self.assertEdgeSkipped(diagram, {('A', 'C'): True})
+
+ def test_skipped_edge_leftdown_diagram(self):
+ diagram = self.build('skipped_edge_leftdown.diag')
+ self.assertEdgeSkipped(diagram, {('A', 'B'): False,
+ ('B', 'C'): False,
+ ('B', 'D'): False,
+ ('C', 'G'): True,
+ ('F', 'G'): False})
+
+ @capture_stderr
+ def test_skipped_edge_flowchart_rightdown_diagram(self):
+ diagram = self.build('skipped_edge_flowchart_rightdown.diag')
+ self.assertEdgeSkipped(diagram, {('A', 'B'): False,
+ ('A', 'C'): False,
+ ('A', 'D'): True,
+ ('C', 'D'): False})
+
+ @capture_stderr
+ def test_skipped_edge_flowchart_rightdown2_diagram(self):
+ diagram = self.build('skipped_edge_flowchart_rightdown2.diag')
+ self.assertEdgeSkipped(diagram, {('B', 'C'): False,
+ ('A', 'C'): True})
+
+ def test_skipped_edge_portrait_right_diagram(self):
+ diagram = self.build('skipped_edge_portrait_right.diag')
+ self.assertEdgeSkipped(diagram, {('A', 'C'): True})
+
+ def test_skipped_edge_portrait_rightdown_diagram(self):
+ diagram = self.build('skipped_edge_portrait_rightdown.diag')
+ self.assertEdgeSkipped(diagram, {('A', 'B'): False,
+ ('A', 'C'): False,
+ ('A', 'E'): True,
+ ('B', 'D'): False,
+ ('C', 'E'): False})
+
+ def test_skipped_edge_portrait_leftdown_diagram(self):
+ diagram = self.build('skipped_edge_portrait_leftdown.diag')
+ self.assertEdgeSkipped(diagram, {('A', 'B'): False,
+ ('B', 'C'): False,
+ ('D', 'C'): True,
+ ('D', 'E'): False})
+
+ def test_skipped_edge_portrait_down_diagram(self):
+ diagram = self.build('skipped_edge_portrait_down.diag')
+ self.assertEdgeSkipped(diagram, {('A', 'B'): False,
+ ('A', 'C'): True,
+ ('B', 'C'): False})
+
+ @capture_stderr
+ def test_skipped_edge_portrait_flowchart_rightdown_diagram(self):
+ diagram = self.build('skipped_edge_portrait_flowchart_rightdown.diag')
+ self.assertEdgeSkipped(diagram, {('A', 'B'): False,
+ ('A', 'C'): False,
+ ('A', 'D'): True,
+ ('C', 'D'): False})
+
+ @capture_stderr
+ def test_skipped_edge_portrait_flowchart_rightdown2_diagram(self):
+ diagram = self.build('skipped_edge_portrait_flowchart_rightdown2.diag')
+ self.assertEdgeSkipped(diagram, {('B', 'C'): False,
+ ('A', 'C'): True})
diff --git a/src/blockdiag/tests/test_builder_errors.py b/src/blockdiag/tests/test_builder_errors.py
index 0e1e6b5..bfe15ec 100644
--- a/src/blockdiag/tests/test_builder_errors.py
+++ b/src/blockdiag/tests/test_builder_errors.py
@@ -1,101 +1,101 @@
# -*- coding: utf-8 -*-
-from nose.tools import raises
-from blockdiag.tests.utils import __build_diagram
-
+from blockdiag.tests.utils import BuilderTestCase
from blockdiag.parser import ParseException
- at raises(AttributeError)
-def test_unknown_diagram_default_shape_diagram():
- __build_diagram('errors/unknown_diagram_default_shape.diag')
-
-
- at raises(AttributeError)
-def test_unknown_diagram_edge_layout_diagram():
- __build_diagram('errors/unknown_diagram_edge_layout.diag')
-
-
- at raises(AttributeError)
-def test_unknown_diagram_orientation_diagram():
- __build_diagram('errors/unknown_diagram_orientation.diag')
-
-
- at raises(AttributeError)
-def test_unknown_node_shape_diagram():
- __build_diagram('errors/unknown_node_shape.diag')
-
-
- at raises(AttributeError)
-def test_unknown_node_attribute_diagram():
- __build_diagram('errors/unknown_node_attribute.diag')
-
-
- at raises(AttributeError)
-def test_unknown_node_style_diagram():
- __build_diagram('errors/unknown_node_style.diag')
-
-
- at raises(AttributeError)
-def test_unknown_node_class_diagram():
- __build_diagram('errors/unknown_node_class.diag')
-
-
- at raises(AttributeError)
-def test_unknown_edge_dir_diagram():
- __build_diagram('errors/unknown_edge_dir.diag')
-
-
- at raises(AttributeError)
-def test_unknown_edge_style_diagram():
- __build_diagram('errors/unknown_edge_style.diag')
-
-
- at raises(AttributeError)
-def test_unknown_edge_hstyle_diagram():
- __build_diagram('errors/unknown_edge_hstyle.diag')
-
-
- at raises(AttributeError)
-def test_unknown_edge_class_diagram():
- __build_diagram('errors/unknown_edge_class.diag')
-
-
- at raises(AttributeError)
-def test_unknown_group_shape_diagram():
- __build_diagram('errors/unknown_group_shape.diag')
-
-
- at raises(AttributeError)
-def test_unknown_group_class_diagram():
- __build_diagram('errors/unknown_group_class.diag')
-
-
- at raises(AttributeError)
-def test_unknown_group_orientation_diagram():
- __build_diagram('errors/unknown_group_orientation.diag')
-
-
- at raises(RuntimeError)
-def test_belongs_to_two_groups_diagram():
- __build_diagram('errors/belongs_to_two_groups.diag')
-
-
- at raises(AttributeError)
-def test_unknown_plugin_diagram():
- __build_diagram('errors/unknown_plugin.diag')
-
-
- at raises(ParseException)
-def test_node_follows_group_diagram():
- __build_diagram('errors/node_follows_group.diag')
-
-
- at raises(ParseException)
-def test_group_follows_node_diagram():
- __build_diagram('errors/group_follows_node.diag')
-
-
- at raises(ParseException)
-def test_lexer_error_diagram():
- __build_diagram('errors/lexer_error.diag')
+class TestBuilderError(BuilderTestCase):
+ def test_unknown_diagram_default_shape_diagram(self):
+ filename = 'errors/unknown_diagram_default_shape.diag'
+ with self.assertRaises(AttributeError):
+ self.build(filename)
+
+ def test_unknown_diagram_edge_layout_diagram(self):
+ filename = 'errors/unknown_diagram_edge_layout.diag'
+ with self.assertRaises(AttributeError):
+ self.build(filename)
+
+ def test_unknown_diagram_orientation_diagram(self):
+ filename = 'errors/unknown_diagram_orientation.diag'
+ with self.assertRaises(AttributeError):
+ self.build(filename)
+
+ def test_unknown_node_shape_diagram(self):
+ filename = 'errors/unknown_node_shape.diag'
+ with self.assertRaises(AttributeError):
+ self.build(filename)
+
+ def test_unknown_node_attribute_diagram(self):
+ filename = 'errors/unknown_node_attribute.diag'
+ with self.assertRaises(AttributeError):
+ self.build(filename)
+
+ def test_unknown_node_style_diagram(self):
+ filename = 'errors/unknown_node_style.diag'
+ with self.assertRaises(AttributeError):
+ self.build(filename)
+
+ def test_unknown_node_class_diagram(self):
+ filename = 'errors/unknown_node_class.diag'
+ with self.assertRaises(AttributeError):
+ self.build(filename)
+
+ def test_unknown_edge_dir_diagram(self):
+ filename = 'errors/unknown_edge_dir.diag'
+ with self.assertRaises(AttributeError):
+ self.build(filename)
+
+ def test_unknown_edge_style_diagram(self):
+ filename = 'errors/unknown_edge_style.diag'
+ with self.assertRaises(AttributeError):
+ self.build(filename)
+
+ def test_unknown_edge_hstyle_diagram(self):
+ filename = 'errors/unknown_edge_hstyle.diag'
+ with self.assertRaises(AttributeError):
+ self.build(filename)
+
+ def test_unknown_edge_class_diagram(self):
+ filename = 'errors/unknown_edge_class.diag'
+ with self.assertRaises(AttributeError):
+ self.build(filename)
+
+ def test_unknown_group_shape_diagram(self):
+ filename = 'errors/unknown_group_shape.diag'
+ with self.assertRaises(AttributeError):
+ self.build(filename)
+
+ def test_unknown_group_class_diagram(self):
+ filename = 'errors/unknown_group_class.diag'
+ with self.assertRaises(AttributeError):
+ self.build(filename)
+
+ def test_unknown_group_orientation_diagram(self):
+ filename = 'errors/unknown_group_orientation.diag'
+ with self.assertRaises(AttributeError):
+ self.build(filename)
+
+ def test_belongs_to_two_groups_diagram(self):
+ filename = 'errors/belongs_to_two_groups.diag'
+ with self.assertRaises(RuntimeError):
+ self.build(filename)
+
+ def test_unknown_plugin_diagram(self):
+ filename = 'errors/unknown_plugin.diag'
+ with self.assertRaises(AttributeError):
+ self.build(filename)
+
+ def test_node_follows_group_diagram(self):
+ filename = 'errors/node_follows_group.diag'
+ with self.assertRaises(ParseException):
+ self.build(filename)
+
+ def test_group_follows_node_diagram(self):
+ filename = 'errors/group_follows_node.diag'
+ with self.assertRaises(ParseException):
+ self.build(filename)
+
+ def test_lexer_error_diagram(self):
+ filename = 'errors/lexer_error.diag'
+ with self.assertRaises(ParseException):
+ self.build(filename)
diff --git a/src/blockdiag/tests/test_builder_group.py b/src/blockdiag/tests/test_builder_group.py
index efea8b5..8767bf0 100644
--- a/src/blockdiag/tests/test_builder_group.py
+++ b/src/blockdiag/tests/test_builder_group.py
@@ -1,209 +1,214 @@
# -*- coding: utf-8 -*-
-from blockdiag.tests.utils import __build_diagram, __validate_node_attributes
-
-
-def test_nested_groups_diagram():
- positions = {'A': (0, 0), 'B': (0, 1), 'Z': (0, 2)}
- __validate_node_attributes('nested_groups.diag', xy=positions)
-
-
-def test_nested_groups_and_edges_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0), 'Z': (0, 1)}
- __validate_node_attributes('nested_groups_and_edges.diag', xy=positions)
-
-
-def test_empty_group_diagram():
- positions = {'Z': (0, 0)}
- __validate_node_attributes('empty_group.diag', xy=positions)
-
-
-def test_empty_nested_group_diagram():
- positions = {'Z': (0, 0)}
- __validate_node_attributes('empty_nested_group.diag', xy=positions)
-
-
-def test_empty_group_declaration_diagram():
- positions = {'A': (0, 0), 'Z': (0, 1)}
- __validate_node_attributes('empty_group_declaration.diag', xy=positions)
-
-
-def test_simple_group_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (1, 1), 'Z': (0, 2)}
- __validate_node_attributes('simple_group.diag', xy=positions)
-
-
-def test_group_declare_as_node_attribute_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0),
- 'D': (2, 1), 'E': (2, 2), 'Z': (0, 3)}
- __validate_node_attributes('group_declare_as_node_attribute.diag',
- xy=positions)
-
-
-def test_group_attribute():
- diagram = __build_diagram('group_attribute.diag')
-
- for node in (x for x in diagram.nodes if not x.drawable):
- node.color = 'red'
- node.label = 'group label'
- node.shape = 'line'
-
-
-def test_merge_groups_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (0, 1),
- 'D': (1, 1), 'Z': (0, 2)}
- __validate_node_attributes('merge_groups.diag', xy=positions)
-
-
-def test_node_attribute_and_group_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0), 'Z': (0, 1)}
- labels = {'A': 'foo', 'B': 'bar', 'C': 'baz', 'Z': 'Z'}
- colors = {'A': (255, 0, 0), 'B': '#888888', 'C': (0, 0, 255),
- 'Z': (255, 255, 255)}
- __validate_node_attributes('node_attribute_and_group.diag',
- xy=positions, label=labels, color=colors)
-
-
-def test_group_sibling_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (1, 2), 'D': (2, 0),
- 'E': (2, 1), 'F': (2, 2), 'Z': (0, 3)}
- __validate_node_attributes('group_sibling.diag', xy=positions)
-
-
-def test_group_order_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (1, 1), 'Z': (0, 2)}
- __validate_node_attributes('group_order.diag', xy=positions)
-
-
-def test_group_order2_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (1, 1),
- 'D': (2, 1), 'E': (1, 2), 'F': (2, 2), 'Z': (0, 3)}
- __validate_node_attributes('group_order2.diag', xy=positions)
-
-
-def test_group_order3_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0),
- 'D': (2, 1), 'E': (1, 2), 'Z': (0, 3)}
- __validate_node_attributes('group_order3.diag', xy=positions)
-
-
-def test_group_children_height_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (1, 1), 'D': (1, 2),
- 'E': (2, 0), 'F': (2, 2), 'Z': (0, 3)}
- __validate_node_attributes('group_children_height.diag', xy=positions)
-
-
-def test_group_children_order_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (1, 1), 'D': (1, 2),
- 'E': (2, 0), 'F': (2, 1), 'G': (2, 2), 'Z': (0, 3)}
- __validate_node_attributes('group_children_order.diag', xy=positions)
-
-
-def test_group_children_order2_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (1, 1), 'D': (1, 2),
- 'E': (2, 1), 'F': (2, 0), 'G': (2, 2), 'Z': (0, 3)}
- __validate_node_attributes('group_children_order2.diag', xy=positions)
-
-
-def test_group_children_order3_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (1, 1), 'D': (1, 2),
- 'E': (2, 0), 'F': (2, 1), 'G': (2, 2), 'Q': (0, 3),
- 'Z': (0, 4)}
- __validate_node_attributes('group_children_order3.diag', xy=positions)
-
-
-def test_group_children_order4_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (1, 1), 'D': (1, 2),
- 'E': (2, 0), 'Z': (0, 3)}
- __validate_node_attributes('group_children_order4.diag', xy=positions)
-
-
-def test_node_in_group_follows_outer_node_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0), 'Z': (0, 1)}
- __validate_node_attributes('node_in_group_follows_outer_node.diag',
- xy=positions)
-
-
-def test_group_id_and_node_id_are_not_conflicted_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (0, 1),
- 'D': (1, 1), 'Z': (0, 2)}
- __validate_node_attributes('group_id_and_node_id_are_not_conflicted.diag',
- xy=positions)
-
-
-def test_outer_node_follows_node_in_group_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0), 'Z': (0, 1)}
- __validate_node_attributes('outer_node_follows_node_in_group.diag',
- xy=positions)
-
-
-def test_large_group_and_node_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (1, 1), 'D': (1, 2),
- 'E': (1, 3), 'F': (2, 0), 'Z': (0, 4)}
- __validate_node_attributes('large_group_and_node.diag', xy=positions)
-
-
-def test_large_group_and_node2_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0), 'D': (3, 0),
- 'E': (4, 0), 'F': (5, 0), 'Z': (0, 1)}
- __validate_node_attributes('large_group_and_node2.diag', xy=positions)
-
-
-def test_large_group_and_two_nodes_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (1, 1), 'D': (1, 2),
- 'E': (1, 3), 'F': (2, 0), 'G': (2, 1), 'Z': (0, 4)}
- __validate_node_attributes('large_group_and_two_nodes.diag', xy=positions)
-
-
-def test_group_height_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0),
- 'D': (2, 1), 'E': (1, 2), 'Z': (0, 3)}
- __validate_node_attributes('group_height.diag', xy=positions)
-
-
-def test_multiple_groups_diagram():
- positions = {'A': (0, 0), 'B': (0, 1), 'C': (0, 2), 'D': (0, 3),
- 'E': (1, 0), 'F': (1, 1), 'G': (1, 2), 'H': (2, 0),
- 'I': (2, 1), 'J': (3, 0), 'Z': (0, 4)}
- __validate_node_attributes('multiple_groups.diag', xy=positions)
-
-
-def test_multiple_nested_groups_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (1, 1), 'Z': (0, 2)}
- __validate_node_attributes('multiple_nested_groups.diag', xy=positions)
-
-
-def test_group_works_node_decorator_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (3, 0),
- 'D': (2, 0), 'E': (1, 1), 'Z': (0, 2)}
- __validate_node_attributes('group_works_node_decorator.diag', xy=positions)
-
-
-def test_nested_groups_work_node_decorator_diagram():
- positions = {'A': (0, 0), 'B': (0, 1), 'Z': (0, 2)}
- __validate_node_attributes('nested_groups_work_node_decorator.diag',
- xy=positions)
-
-
-def test_reversed_multiple_groups_diagram():
- positions = {'A': (3, 0), 'B': (3, 1), 'C': (3, 2), 'D': (3, 3),
- 'E': (2, 0), 'F': (2, 1), 'G': (2, 2), 'H': (1, 0),
- 'I': (1, 1), 'J': (0, 0), 'Z': (0, 4)}
- __validate_node_attributes('reverse_multiple_groups.diag', xy=positions)
-
-
-def test_group_and_skipped_edge_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0),
- 'D': (3, 0), 'E': (1, 1), 'Z': (0, 2)}
- __validate_node_attributes('group_and_skipped_edge.diag', xy=positions)
-
-
-def test_group_orientation_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (1, 1),
- 'D': (2, 1), 'Z': (0, 2)}
- __validate_node_attributes('group_orientation.diag', xy=positions)
-
-
-def test_nested_group_orientation_diagram():
- positions = {'A': (0, 0), 'B': (0, 1), 'C': (1, 0), 'Z': (0, 2)}
- __validate_node_attributes('nested_group_orientation.diag', xy=positions)
+from blockdiag.tests.utils import BuilderTestCase
+
+
+class TestBuilderGroup(BuilderTestCase):
+ def test_nested_groups_diagram(self):
+ diagram = self.build('nested_groups.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (0, 1),
+ 'Z': (0, 2)})
+
+ def test_nested_groups_and_edges_diagram(self):
+ diagram = self.build('nested_groups_and_edges.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (0, 1),
+ 'C': (2, 0), 'Z': (0, 1)})
+
+ def test_empty_group_diagram(self):
+ diagram = self.build('empty_group.diag')
+ self.assertNodeXY(diagram, {'Z': (0, 0)})
+
+ def test_empty_nested_group_diagram(self):
+ diagram = self.build('empty_nested_group.diag')
+ self.assertNodeXY(diagram, {'Z': (0, 0)})
+
+ def test_empty_group_declaration_diagram(self):
+ diagram = self.build('empty_group_declaration.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'Z': (0, 1)})
+
+ def test_simple_group_diagram(self):
+ diagram = self.build('simple_group.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (1, 1), 'Z': (0, 2)})
+
+ def test_group_declare_as_node_attribute_diagram(self):
+ diagram = self.build('group_declare_as_node_attribute.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 0), 'D': (2, 1),
+ 'E': (2, 2), 'Z': (0, 3)})
+
+ def test_group_attribute(self):
+ diagram = self.build('group_attribute.diag')
+ groups = list(diagram.traverse_groups())
+ self.assertEqual(1, len(groups))
+ self.assertEqual((255, 0, 0), groups[0].color)
+ self.assertEqual('line', groups[0].shape)
+
+ def test_merge_groups_diagram(self):
+ diagram = self.build('merge_groups.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (0, 1), 'D': (1, 1),
+ 'Z': (0, 2)})
+
+ def test_node_attribute_and_group_diagram(self):
+ diagram = self.build('node_attribute_and_group.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 0), 'Z': (0, 1)})
+ self.assertNodeLabel(diagram, {'A': 'foo', 'B': 'bar',
+ 'C': 'baz', 'Z': 'Z'})
+ self.assertNodeColor(diagram, {'A': (255, 0, 0), 'B': '#888888',
+ 'C': (0, 0, 255), 'Z': (255, 255, 255)})
+
+ def test_group_sibling_diagram(self):
+ diagram = self.build('group_sibling.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (1, 2), 'D': (2, 0),
+ 'E': (2, 1), 'F': (2, 2),
+ 'Z': (0, 3)})
+
+ def test_group_order_diagram(self):
+ diagram = self.build('group_order.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (1, 1), 'Z': (0, 2)})
+
+ def test_group_order2_diagram(self):
+ diagram = self.build('group_order2.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (1, 1), 'D': (2, 1),
+ 'E': (1, 2), 'F': (2, 2),
+ 'Z': (0, 3)})
+
+ def test_group_order3_diagram(self):
+ diagram = self.build('group_order3.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 0), 'D': (2, 1),
+ 'E': (1, 2), 'Z': (0, 3)})
+
+ def test_group_children_height_diagram(self):
+ diagram = self.build('group_children_height.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (1, 1), 'D': (1, 2),
+ 'E': (2, 0), 'F': (2, 2),
+ 'Z': (0, 3)})
+
+ def test_group_children_order_diagram(self):
+ diagram = self.build('group_children_order.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (1, 1), 'D': (1, 2),
+ 'E': (2, 0), 'F': (2, 1),
+ 'G': (2, 2), 'Z': (0, 3)})
+
+ def test_group_children_order2_diagram(self):
+ diagram = self.build('group_children_order2.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (1, 1), 'D': (1, 2),
+ 'E': (2, 1), 'F': (2, 0),
+ 'G': (2, 2), 'Z': (0, 3)})
+
+ def test_group_children_order3_diagram(self):
+ diagram = self.build('group_children_order3.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (1, 1), 'D': (1, 2),
+ 'E': (2, 0), 'F': (2, 1),
+ 'G': (2, 2), 'Q': (0, 3),
+ 'Z': (0, 4)})
+
+ def test_group_children_order4_diagram(self):
+ diagram = self.build('group_children_order4.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (1, 1), 'D': (1, 2),
+ 'E': (2, 0), 'Z': (0, 3)})
+
+ def test_node_in_group_follows_outer_node_diagram(self):
+ diagram = self.build('node_in_group_follows_outer_node.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 0), 'Z': (0, 1)})
+
+ def test_group_id_and_node_id_are_not_conflicted_diagram(self):
+ diagram = self.build('group_id_and_node_id_are_not_conflicted.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (0, 1), 'D': (1, 1),
+ 'Z': (0, 2)})
+
+ def test_outer_node_follows_node_in_group_diagram(self):
+ diagram = self.build('outer_node_follows_node_in_group.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 0), 'Z': (0, 1)})
+
+ def test_large_group_and_node_diagram(self):
+ diagram = self.build('large_group_and_node.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (1, 1), 'D': (1, 2),
+ 'E': (1, 3), 'F': (2, 0),
+ 'Z': (0, 4)})
+
+ def test_large_group_and_node2_diagram(self):
+ diagram = self.build('large_group_and_node2.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 0), 'D': (3, 0),
+ 'E': (4, 0), 'F': (5, 0),
+ 'Z': (0, 1)})
+
+ def test_large_group_and_two_nodes_diagram(self):
+ diagram = self.build('large_group_and_two_nodes.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (1, 1), 'D': (1, 2),
+ 'E': (1, 3), 'F': (2, 0),
+ 'G': (2, 1), 'Z': (0, 4)})
+
+ def test_group_height_diagram(self):
+ diagram = self.build('group_height.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 0), 'D': (2, 1),
+ 'E': (1, 2), 'Z': (0, 3)})
+
+ def test_multiple_groups_diagram(self):
+ diagram = self.build('multiple_groups.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (0, 1),
+ 'C': (0, 2), 'D': (0, 3),
+ 'E': (1, 0), 'F': (1, 1),
+ 'G': (1, 2), 'H': (2, 0),
+ 'I': (2, 1), 'J': (3, 0),
+ 'Z': (0, 4)})
+
+ def test_multiple_nested_groups_diagram(self):
+ diagram = self.build('multiple_nested_groups.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (1, 1), 'Z': (0, 2)})
+
+ def test_group_works_node_decorator_diagram(self):
+ diagram = self.build('group_works_node_decorator.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (3, 0), 'D': (2, 0),
+ 'E': (1, 1), 'Z': (0, 2)})
+
+ def test_nested_groups_work_node_decorator_diagram(self):
+ diagram = self.build('nested_groups_work_node_decorator.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (0, 1),
+ 'Z': (0, 2)})
+
+ def test_reversed_multiple_groups_diagram(self):
+ diagram = self.build('reverse_multiple_groups.diag')
+ self.assertNodeXY(diagram, {'A': (3, 0), 'B': (3, 1),
+ 'C': (3, 2), 'D': (3, 3),
+ 'E': (2, 0), 'F': (2, 1),
+ 'G': (2, 2), 'H': (1, 0),
+ 'I': (1, 1), 'J': (0, 0),
+ 'Z': (0, 4)})
+
+ def test_group_and_skipped_edge_diagram(self):
+ diagram = self.build('group_and_skipped_edge.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 0), 'D': (3, 0),
+ 'E': (1, 1), 'Z': (0, 2)})
+
+ def test_group_orientation_diagram(self):
+ diagram = self.build('group_orientation.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (1, 1), 'D': (2, 1),
+ 'Z': (0, 2)})
+
+ def test_nested_group_orientation_diagram(self):
+ diagram = self.build('nested_group_orientation.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (0, 1),
+ 'C': (1, 0), 'Z': (0, 2)})
diff --git a/src/blockdiag/tests/test_builder_node.py b/src/blockdiag/tests/test_builder_node.py
index 67a2814..4291afd 100644
--- a/src/blockdiag/tests/test_builder_node.py
+++ b/src/blockdiag/tests/test_builder_node.py
@@ -1,138 +1,139 @@
# -*- coding: utf-8 -*-
-from blockdiag.tests.utils import __build_diagram, __validate_node_attributes
-
-from blockdiag.utils.collections import defaultdict
-
-
-def test_single_node_diagram():
- diagram = __build_diagram('single_node.diag')
-
- assert len(diagram.nodes) == 1
- assert len(diagram.edges) == 0
- assert diagram.nodes[0].label == 'A'
- assert diagram.nodes[0].xy == (0, 0)
-
-
-def test_node_shape_diagram():
- shapes = {'A': 'box', 'B': 'roundedbox', 'C': 'diamond',
- 'D': 'ellipse', 'E': 'note', 'F': 'cloud',
- 'G': 'mail', 'H': 'beginpoint', 'I': 'endpoint',
- 'J': 'minidiamond', 'K': 'flowchart.condition',
- 'L': 'flowchart.database', 'M': 'flowchart.input',
- 'N': 'flowchart.loopin', 'O': 'flowchart.loopout',
- 'P': 'actor', 'Q': 'flowchart.terminator', 'R': 'textbox',
- 'S': 'dots', 'T': 'none', 'U': 'square', 'V': 'circle',
- 'Z': 'box'}
- __validate_node_attributes('node_shape.diag', shape=shapes)
-
-
-def test_node_shape_namespace_diagram():
- shapes = {'A': 'flowchart.condition', 'B': 'condition', 'Z': 'box'}
- __validate_node_attributes('node_shape_namespace.diag', shape=shapes)
-
-
-def test_node_has_multilined_label_diagram():
- positions = {'A': (0, 0), 'Z': (0, 1)}
- labels = {'A': "foo\nbar", 'Z': 'Z'}
- __validate_node_attributes('node_has_multilined_label.diag',
- xy=positions, label=labels)
-
-
-def test_quoted_node_id_diagram():
- positions = {'A': (0, 0), "'A'": (1, 0), 'B': (2, 0), 'Z': (0, 1)}
- __validate_node_attributes('quoted_node_id.diag', xy=positions)
-
-
-def test_node_id_includes_dot_diagram():
- positions = {'A.B': (0, 0), 'C.D': (1, 0), 'Z': (0, 1)}
- __validate_node_attributes('node_id_includes_dot.diag', xy=positions)
-
-
-def test_multiple_nodes_definition_diagram():
- positions = {'A': (0, 0), 'B': (0, 1), 'Z': (0, 2)}
- colors = {'A': (255, 0, 0), 'B': (255, 0, 0), 'Z': (255, 255, 255)}
- __validate_node_attributes('multiple_nodes_definition.diag', xy=positions,
- color=colors)
-
-
-def test_multiple_node_relation_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (1, 1),
- 'D': (2, 0), 'Z': (0, 2)}
- __validate_node_attributes('multiple_node_relation.diag', xy=positions)
-
-
-def test_node_attribute():
- labels = {'A': 'B', 'B': 'double quoted', 'C': 'single quoted',
- 'D': '\'"double" quoted\'', 'E': '"\'single\' quoted"',
- 'F': 'F', 'G': 'G', 'H': 'H', 'I': 'I', 'J': 'Hello'}
- colors = {'A': (255, 0, 0), 'B': (255, 255, 255), 'C': (255, 0, 0),
- 'D': (255, 0, 0), 'E': (255, 0, 0), 'F': (255, 255, 255),
- 'G': (255, 255, 255), 'H': (255, 255, 255), 'I': (255, 255, 255),
- 'J': (255, 255, 255)}
- textcolors = defaultdict(lambda: (0, 0, 0))
- textcolors['F'] = (255, 0, 0)
- numbered = defaultdict(lambda: None)
- numbered['E'] = '1'
- stacked = defaultdict(lambda: False)
- stacked['G'] = True
- fontsize = defaultdict(lambda: None)
- fontsize['H'] = 16
- linecolors = defaultdict(lambda: (0, 0, 0))
- linecolors['I'] = (255, 0, 0)
- orientations = defaultdict(lambda: 'horizontal')
- orientations['J'] = 'vertical'
-
- __validate_node_attributes('node_attribute.diag', label=labels,
- color=colors, textcolor=textcolors,
- numbered=numbered, stacked=stacked,
- fontsize=fontsize, linecolor=linecolors,
- label_orientation=orientations)
-
-
-def test_node_height_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0),
- 'D': (2, 1), 'E': (1, 1), 'Z': (0, 2)}
- __validate_node_attributes('node_height.diag', xy=positions)
-
-
-def test_branched_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0),
- 'D': (1, 1), 'E': (2, 1), 'Z': (0, 2)}
- __validate_node_attributes('branched.diag', xy=positions)
-
-
-def test_multiple_parent_node_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (0, 2),
- 'D': (1, 2), 'E': (0, 1), 'Z': (0, 3)}
- __validate_node_attributes('multiple_parent_node.diag', xy=positions)
-
-
-def test_twin_multiple_parent_node_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (0, 1),
- 'D': (1, 1), 'E': (0, 2), 'Z': (0, 3)}
- __validate_node_attributes('twin_multiple_parent_node.diag', xy=positions)
-
-
-def test_flowable_node_diagram():
- positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0), 'Z': (0, 1)}
- __validate_node_attributes('flowable_node.diag', xy=positions)
-
-
-def test_plugin_autoclass_diagram():
- positions = {'A_emphasis': (0, 0), 'B_emphasis': (1, 0), 'C': (1, 1)}
- styles = {'A_emphasis': 'dashed', 'B_emphasis': 'dashed', 'C': None}
- colors = {'A_emphasis': (255, 0, 0), 'B_emphasis': (255, 0, 0),
- 'C': (255, 255, 255)}
-
- __validate_node_attributes('plugin_autoclass.diag', xy=positions,
- style=styles, color=colors)
-
-
-def test_plugin_attributes_diagram():
- attr1 = {'A': "1", 'B': None}
- attr2 = {'A': "2", 'B': None}
- attr3 = {'A': "3", 'B': None}
-
- __validate_node_attributes('plugin_attributes.diag', test_attr1=attr1,
- test_attr2=attr2, test_attr3=attr3)
+from collections import defaultdict
+from blockdiag.tests.utils import BuilderTestCase
+
+
+class TestBuilderNode(BuilderTestCase):
+ def test_single_node_diagram(self):
+ diagram = self.build('single_node.diag')
+
+ self.assertEqual(1, len(diagram.nodes))
+ self.assertEqual(0, len(diagram.edges))
+ self.assertEqual('A', diagram.nodes[0].label)
+ self.assertEqual((0, 0), diagram.nodes[0].xy)
+
+ def test_node_shape_diagram(self):
+ expected = {'A': 'box', 'B': 'roundedbox', 'C': 'diamond',
+ 'D': 'ellipse', 'E': 'note', 'F': 'cloud',
+ 'G': 'mail', 'H': 'beginpoint', 'I': 'endpoint',
+ 'J': 'minidiamond', 'K': 'flowchart.condition',
+ 'L': 'flowchart.database', 'M': 'flowchart.input',
+ 'N': 'flowchart.loopin', 'O': 'flowchart.loopout',
+ 'P': 'actor', 'Q': 'flowchart.terminator', 'R': 'textbox',
+ 'S': 'dots', 'T': 'none', 'U': 'square', 'V': 'circle',
+ 'Z': 'box'}
+ diagram = self.build('node_shape.diag')
+ self.assertNodeShape(diagram, expected)
+
+ def test_node_shape_namespace_diagram(self):
+ diagram = self.build('node_shape_namespace.diag')
+ self.assertNodeShape(diagram, {'A': 'flowchart.condition',
+ 'B': 'condition',
+ 'Z': 'box'})
+
+ def test_node_has_multilined_label_diagram(self):
+ diagram = self.build('node_has_multilined_label.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'Z': (0, 1)})
+ self.assertNodeLabel(diagram, {'A': "foo\nbar", 'Z': 'Z'})
+
+ def test_quoted_node_id_diagram(self):
+ diagram = self.build('quoted_node_id.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), "'A'": (1, 0),
+ 'B': (2, 0), 'Z': (0, 1)})
+
+ def test_node_id_includes_dot_diagram(self):
+ diagram = self.build('node_id_includes_dot.diag')
+ self.assertNodeXY(diagram, {'A.B': (0, 0), 'C.D': (1, 0),
+ 'Z': (0, 1)})
+
+ def test_multiple_nodes_definition_diagram(self):
+ diagram = self.build('multiple_nodes_definition.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (0, 1),
+ 'Z': (0, 2)})
+ self.assertNodeColor(diagram, {'A': (255, 0, 0), 'B': (255, 0, 0),
+ 'Z': (255, 255, 255)})
+
+ def test_multiple_node_relation_diagram(self):
+ diagram = self.build('multiple_node_relation.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (1, 1), 'D': (2, 0),
+ 'Z': (0, 2)})
+
+ def test_node_attribute(self):
+ labels = {'A': 'B', 'B': 'double quoted', 'C': 'single quoted',
+ 'D': '\'"double" quoted\'', 'E': '"\'single\' quoted"',
+ 'F': 'F', 'G': 'G', 'H': 'H', 'I': 'I', 'J': 'Hello'}
+ colors = {'A': (255, 0, 0), 'B': (255, 255, 255), 'C': (255, 0, 0),
+ 'D': (255, 0, 0), 'E': (255, 0, 0), 'F': (255, 255, 255),
+ 'G': (255, 255, 255), 'H': (255, 255, 255),
+ 'I': (255, 255, 255), 'J': (255, 255, 255)}
+ textcolors = defaultdict(lambda: (0, 0, 0))
+ textcolors['F'] = (255, 0, 0)
+ linecolors = defaultdict(lambda: (0, 0, 0))
+ linecolors['I'] = (255, 0, 0)
+ numbered = defaultdict(lambda: None)
+ numbered['E'] = '1'
+ stacked = defaultdict(lambda: False)
+ stacked['G'] = True
+ fontsize = defaultdict(lambda: None)
+ fontsize['H'] = 16
+ orientations = defaultdict(lambda: 'horizontal')
+ orientations['J'] = 'vertical'
+
+ diagram = self.build('node_attribute.diag')
+ self.assertNodeLabel(diagram, labels)
+ self.assertNodeColor(diagram, colors)
+ self.assertNodeTextColor(diagram, textcolors)
+ self.assertNodeLineColor(diagram, linecolors)
+ self.assertNodeNumbered(diagram, numbered)
+ self.assertNodeStacked(diagram, stacked)
+ self.assertNodeFontsize(diagram, fontsize)
+ self.assertNodeLabel_Orientation(diagram, orientations)
+
+ def test_node_height_diagram(self):
+ diagram = self.build('node_height.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 0), 'D': (2, 1),
+ 'E': (1, 1), 'Z': (0, 2)})
+
+ def test_branched_diagram(self):
+ diagram = self.build('branched.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 0), 'D': (1, 1),
+ 'E': (2, 1), 'Z': (0, 2)})
+
+ def test_multiple_parent_node_diagram(self):
+ diagram = self.build('multiple_parent_node.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (0, 2), 'D': (1, 2),
+ 'E': (0, 1), 'Z': (0, 3)})
+
+ def test_twin_multiple_parent_node_diagram(self):
+ diagram = self.build('twin_multiple_parent_node.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (0, 1), 'D': (1, 1),
+ 'E': (0, 2), 'Z': (0, 3)})
+
+ def test_flowable_node_diagram(self):
+ diagram = self.build('flowable_node.diag')
+ self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0),
+ 'C': (2, 0), 'Z': (0, 1)})
+
+ def test_plugin_autoclass_diagram(self):
+ diagram = self.build('plugin_autoclass.diag')
+ self.assertNodeXY(diagram, {'A_emphasis': (0, 0),
+ 'B_emphasis': (1, 0),
+ 'C': (1, 1)})
+ self.assertNodeStyle(diagram, {'A_emphasis': 'dashed',
+ 'B_emphasis': 'dashed',
+ 'C': None})
+ self.assertNodeColor(diagram, {'A_emphasis': (255, 0, 0),
+ 'B_emphasis': (255, 0, 0),
+ 'C': (255, 255, 255)})
+
+ def test_plugin_attributes_diagram(self):
+ diagram = self.build('plugin_attributes.diag')
+ self.assertNodeTest_Attr1(diagram, {'A': "1", 'B': None})
+ self.assertNodeTest_Attr2(diagram, {'A': "2", 'B': None})
+ self.assertNodeTest_Attr3(diagram, {'A': "3", 'B': None})
diff --git a/src/blockdiag/tests/test_builder_separate.py b/src/blockdiag/tests/test_builder_separate.py
index ced9322..d25fa95 100644
--- a/src/blockdiag/tests/test_builder_separate.py
+++ b/src/blockdiag/tests/test_builder_separate.py
@@ -1,47 +1,41 @@
# -*- coding: utf-8 -*-
+from __future__ import print_function
from blockdiag.builder import SeparateDiagramBuilder
from blockdiag.elements import DiagramNode
-from blockdiag.parser import parse_string
-
-
-def __build_diagram(filename):
- import os
- testdir = os.path.dirname(__file__)
- pathname = "%s/diagrams/%s" % (testdir, filename)
-
- code = open(pathname).read()
- tree = parse_string(code)
- return SeparateDiagramBuilder.build(tree)
-
-
-def test_separate1_diagram():
- diagram = __build_diagram('separate1.diag')
-
- assert_pos = {0: {'B': (0, 0), 'C': (1, 0), 'D': (4, 0),
- 'E': (2, 0), 'F': (3, 0)},
- 1: {'A': (0, 0), 'B': (1, 0), 'D': (3, 0)},
- 2: {'A': (0, 0), 'Z': (0, 1)}}
-
- for i, diagram in enumerate(diagram):
- for node in diagram.traverse_nodes():
- if isinstance(node, DiagramNode):
- print node, assert_pos[i][node.id]
- assert node.xy == assert_pos[i][node.id]
-
-
-def test_separate2_diagram():
- diagram = __build_diagram('separate2.diag')
-
- assert_pos = {0: {'A': (0, 0), 'C': (1, 0), 'D': (2, 0),
- 'E': (0, 2), 'G': (3, 0), 'H': (3, 1)},
- 1: {'A': (0, 0), 'B': (1, 0), 'E': (2, 0),
- 'F': (4, 2), 'G': (4, 0), 'H': (4, 1)},
- 2: {'A': (0, 0), 'F': (2, 2), 'G': (2, 0),
- 'H': (2, 1), 'Z': (0, 3)}}
-
- for i, diagram in enumerate(diagram):
- for node in diagram.traverse_nodes():
- if isinstance(node, DiagramNode):
- print node, assert_pos[i][node.id]
- assert node.xy == assert_pos[i][node.id]
+from blockdiag.tests.utils import BuilderTestCase
+
+
+class TestBuilderSeparated(BuilderTestCase):
+ def _build(self, tree):
+ return SeparateDiagramBuilder.build(tree)
+
+ def test_separate1_diagram(self):
+ diagram = self.build('separate1.diag')
+
+ assert_pos = {0: {'B': (0, 0), 'C': (1, 0), 'D': (4, 0),
+ 'E': (2, 0), 'F': (3, 0)},
+ 1: {'A': (0, 0), 'B': (1, 0), 'D': (3, 0)},
+ 2: {'A': (0, 0), 'Z': (0, 1)}}
+
+ for i, diagram in enumerate(diagram):
+ for node in diagram.traverse_nodes():
+ if isinstance(node, DiagramNode):
+ print(node)
+ self.assertEqual(assert_pos[i][node.id], node.xy)
+
+ def test_separate2_diagram(self):
+ diagram = self.build('separate2.diag')
+
+ assert_pos = {0: {'A': (0, 0), 'C': (1, 0), 'D': (2, 0),
+ 'E': (0, 2), 'G': (3, 0), 'H': (3, 1)},
+ 1: {'A': (0, 0), 'B': (1, 0), 'E': (2, 0),
+ 'F': (4, 2), 'G': (4, 0), 'H': (4, 1)},
+ 2: {'A': (0, 0), 'F': (2, 2), 'G': (2, 0),
+ 'H': (2, 1), 'Z': (0, 3)}}
+
+ for i, diagram in enumerate(diagram):
+ for node in diagram.traverse_nodes():
+ if isinstance(node, DiagramNode):
+ print(node)
+ self.assertEqual(assert_pos[i][node.id], node.xy)
diff --git a/src/blockdiag/tests/test_generate_diagram.py b/src/blockdiag/tests/test_generate_diagram.py
index 0e4fba7..615c99f 100644
--- a/src/blockdiag/tests/test_generate_diagram.py
+++ b/src/blockdiag/tests/test_generate_diagram.py
@@ -1,147 +1,110 @@
# -*- coding: utf-8 -*-
import os
-import sys
import re
-import tempfile
-from blockdiag.tests.utils import argv_wrapper, stderr_wrapper
-from blockdiag.tests.utils import with_pil, supported_pil, with_pdf
+from nose.tools import nottest
+from blockdiag.tests.utils import capture_stderr, TemporaryDirectory
+from blockdiag.tests.utils import supported_pil, supported_pdf
import blockdiag
import blockdiag.command
-def get_fontpath():
- filename = "VL-PGothic-Regular.ttf"
- testdir = os.path.dirname(__file__)
- return "%s/truetype/%s" % (testdir, filename)
-
-
-def extra_case(func):
- pathname = get_fontpath()
-
- if os.path.exists(pathname):
- func.__test__ = True
- else:
- func.__test__ = False
-
- return func
+def get_fontpath(testdir=None):
+ if testdir is None:
+ testdir = os.path.dirname(__file__)
+ return os.path.join(testdir, 'truetype', 'VL-PGothic-Regular.ttf')
- at argv_wrapper
- at stderr_wrapper
-def __build_diagram(filename, _format, args):
- testdir = os.path.dirname(__file__)
- diagpath = "%s/diagrams/%s" % (testdir, filename)
- fontpath = get_fontpath()
-
- try:
- tmpdir = tempfile.mkdtemp()
- tmpfile = tempfile.mkstemp(dir=tmpdir)
- os.close(tmpfile[0])
-
- sys.argv = ['blockdiag.py', '-T', _format, '-o', tmpfile[1], diagpath]
- if args:
- if isinstance(args[0], (list, tuple)):
- sys.argv += args[0]
- else:
- sys.argv += args
- if os.path.exists(fontpath):
- sys.argv += ['-f', fontpath]
-
- blockdiag.command.main()
-
- if re.search('ERROR', sys.stderr.getvalue()):
- raise RuntimeError(sys.stderr.getvalue())
- finally:
- for filename in os.listdir(tmpdir):
- os.unlink(tmpdir + "/" + filename)
- os.rmdir(tmpdir)
+def get_diagram_files(testdir):
+ diagramsdir = os.path.join(testdir, 'diagrams')
+ skipped = ['errors', 'white.gif']
+ for file in os.listdir(diagramsdir):
+ if file in skipped:
+ pass
+ else:
+ yield os.path.join(diagramsdir, file)
-def diagram_files():
- testdir = os.path.dirname(__file__)
- pathname = "%s/diagrams/" % testdir
- skipped = ['errors',
- 'white.gif']
+def test_generate():
+ mainfunc = blockdiag.command.main
+ basepath = os.path.dirname(__file__)
+ files = get_diagram_files(basepath)
+ options = []
- return [d for d in os.listdir(pathname) if d not in skipped]
+ for testcase in testcase_generator(basepath, mainfunc, files, options):
+ yield testcase
-def test_generator_svg():
- args = []
- if not supported_pil():
- args.append('--ignore-pil')
+def test_generate_with_separate():
+ mainfunc = blockdiag.command.main
+ basepath = os.path.dirname(__file__)
+ files = get_diagram_files(basepath)
+ filtered = (f for f in files if re.search('separate', f))
+ options = ['--separate']
- for testcase in generator_core('svg', args):
+ for testcase in testcase_generator(basepath, mainfunc, filtered, options):
yield testcase
- at with_pil
- at extra_case
-def test_generator_png():
- for testcase in generator_core('png'):
- yield testcase
+ at nottest
+def testcase_generator(basepath, mainfunc, files, options):
+ fontpath = get_fontpath(basepath)
+ if os.path.exists(fontpath):
+ options = options + ['-f', fontpath]
+ for source in files:
+ yield generate, mainfunc, 'svg', source, options
- at with_pdf
- at extra_case
-def test_generator_pdf():
- for testcase in generator_core('pdf'):
- yield testcase
+ if supported_pil() and os.path.exists(fontpath):
+ yield generate, mainfunc, 'png', source, options
+ yield generate, mainfunc, 'png', source, options + ['--antialias']
+ if supported_pdf() and os.path.exists(fontpath):
+ yield generate, mainfunc, 'pdf', source, options
-def generator_core(_format, *args):
- for diagram in diagram_files():
- yield __build_diagram, diagram, _format, args
- if re.search('separate', diagram):
- _args = list(args) + ['--separate']
- yield __build_diagram, diagram, _format, _args
+ at capture_stderr
+def generate(mainfunc, filetype, source, options):
+ try:
+ tmpdir = TemporaryDirectory()
+ fd, tmpfile = tmpdir.mkstemp()
+ os.close(fd)
- if _format == 'png':
- _args = list(args) + ['--antialias']
- yield __build_diagram, diagram, _format, _args
+ mainfunc(['-T', filetype, '-o', tmpfile, source] + list(options))
+ finally:
+ tmpdir.clean()
- at extra_case
- at argv_wrapper
def not_exist_font_config_option_test():
fontpath = get_fontpath()
- sys.argv = ['', '-f', '/font_is_not_exist', '-f', fontpath, 'input.diag']
- options = blockdiag.command.BlockdiagOptions(blockdiag).parse()
- blockdiag.command.detectfont(options)
+ if os.path.exists(fontpath):
+ args = ['-f', '/font_is_not_exist', '-f', fontpath, 'input.diag']
+ options = blockdiag.command.BlockdiagOptions(blockdiag).parse(args)
+
+ from blockdiag.utils.bootstrap import detectfont
+ detectfont(options)
- at argv_wrapper
- at stderr_wrapper
+ at capture_stderr
def svg_includes_source_code_tag_test():
from xml.etree import ElementTree
testdir = os.path.dirname(__file__)
- diagpath = "%s/diagrams/single_edge.diag" % testdir
- fontpath = get_fontpath()
+ diagpath = os.path.join(testdir, 'diagrams', 'single_edge.diag')
try:
- tmpdir = tempfile.mkdtemp()
- tmpfile = tempfile.mkstemp(dir=tmpdir)
- os.close(tmpfile[0])
-
- sys.argv = ['blockdiag.py', '-T', 'SVG', '-o', tmpfile[1], diagpath]
- if os.path.exists(fontpath):
- sys.argv += ['-f', fontpath]
- if not supported_pil():
- sys.argv += ['--ignore-pil']
-
- blockdiag.command.main()
+ tmpdir = TemporaryDirectory()
+ fd, tmpfile = tmpdir.mkstemp()
+ os.close(fd)
- if re.search('ERROR', sys.stderr.getvalue()):
- raise RuntimeError(sys.stderr.getvalue())
+ args = ['-T', 'SVG', '-o', tmpfile, diagpath]
+ blockdiag.command.main(args)
# compare embeded source code
source_code = open(diagpath).read()
- tree = ElementTree.parse(tmpfile[1])
+ tree = ElementTree.parse(tmpfile)
desc = tree.find('{http://www.w3.org/2000/svg}desc')
# strip spaces
@@ -149,6 +112,4 @@ def svg_includes_source_code_tag_test():
embeded = re.sub('\s+', ' ', desc.text)
assert source_code == embeded
finally:
- for filename in os.listdir(tmpdir):
- os.unlink(tmpdir + "/" + filename)
- os.rmdir(tmpdir)
+ tmpdir.clean()
diff --git a/src/blockdiag/tests/test_imagedraw_textfolder.py b/src/blockdiag/tests/test_imagedraw_textfolder.py
index a7dd075..4d1fa2d 100644
--- a/src/blockdiag/tests/test_imagedraw_textfolder.py
+++ b/src/blockdiag/tests/test_imagedraw_textfolder.py
@@ -1,10 +1,16 @@
# -*- coding: utf-8 -*-
-import unittest2
+import sys
+if sys.version_info < (2, 7):
+ import unittest2 as unittest
+else:
+ import unittest
+
from blockdiag.imagedraw.textfolder import splitlabel
from blockdiag.imagedraw.textfolder import splittext
from blockdiag.imagedraw.textfolder import truncate_text
from blockdiag.utils import Size
+from blockdiag.utils.compat import u
CHAR_WIDTH = 14
@@ -17,43 +23,46 @@ class Metrics(object):
return Size(CHAR_WIDTH * length, CHAR_HEIGHT)
-class TestTextFolder(unittest2.TestCase):
+class TestTextFolder(unittest.TestCase):
def test_splitlabel(self):
# single line text
text = "abc"
- self.assertItemsEqual(['abc'], splitlabel(text))
+ self.assertEqual(['abc'], list(splitlabel(text)))
# text include \n (as char a.k.a. \x5c)
text = "abc\ndef"
- self.assertItemsEqual(['abc', 'def'], splitlabel(text))
+ self.assertEqual(['abc', 'def'], list(splitlabel(text)))
# text include \n (as mac yensign a.k.a. \xa5)
text = "abc\xa5ndef"
- self.assertItemsEqual(['abc', 'def'], splitlabel(text))
+ self.assertEqual(['abc', 'def'], list(splitlabel(text)))
# text includes \n (as text)
text = "abc\\ndef"
- self.assertItemsEqual(['abc', 'def'], splitlabel(text))
+ self.assertEqual(['abc', 'def'], list(splitlabel(text)))
# text includes escaped \n
text = "abc\\\\ndef"
- self.assertItemsEqual(['abc\\ndef'], splitlabel(text))
+ self.assertEqual(['abc\\ndef'], list(splitlabel(text)))
# text includes escaped \n (\x5c and mac yensign mixed)
- text = u"abc\xa5\\ndef"
- self.assertItemsEqual(['abc\\ndef'], splitlabel(text))
+ if sys.version_info[0] == 2:
+ text = u("abc\xa5\\\\ndef")
+ else:
+ text = u("abc\xa5\\ndef")
+ self.assertEqual(['abc\\ndef'], list(splitlabel(text)))
# text include \n and spaces
text = " abc \n def "
- self.assertItemsEqual(['abc', 'def'], splitlabel(text))
+ self.assertEqual(['abc', 'def'], list(splitlabel(text)))
# text starts empty line
text = " \nabc\ndef"
- self.assertItemsEqual(['abc', 'def'], splitlabel(text))
+ self.assertEqual(['abc', 'def'], list(splitlabel(text)))
# text starts empty line with \n (as text)
text = " \\nabc\\ndef"
- self.assertItemsEqual(['', 'abc', 'def'], splitlabel(text))
+ self.assertEqual(['', 'abc', 'def'], list(splitlabel(text)))
def test_splittext_width(self):
metrics = Metrics()
diff --git a/src/blockdiag/tests/test_imagedraw_utils.py b/src/blockdiag/tests/test_imagedraw_utils.py
new file mode 100644
index 0000000..c2e26d9
--- /dev/null
+++ b/src/blockdiag/tests/test_imagedraw_utils.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+
+import sys
+if sys.version_info < (2, 7):
+ import unittest2 as unittest
+else:
+ import unittest
+
+from blockdiag.imagedraw.utils import (
+ is_zenkaku, zenkaku_len, hankaku_len,
+ string_width, textsize
+)
+from blockdiag.utils.compat import u
+
+
+class TestUtils(unittest.TestCase):
+ def test_is_zenkaku(self):
+ # A
+ self.assertEqual(False, is_zenkaku(u("A")))
+ # あ
+ self.assertEqual(True, is_zenkaku(u("\u3042")))
+
+ def test_zenkaku_len(self):
+ # abc
+ self.assertEqual(0, zenkaku_len(u("abc")))
+ # あいう
+ self.assertEqual(3, zenkaku_len(u("\u3042\u3044\u3046")))
+ # あいc
+ self.assertEqual(2, zenkaku_len(u("\u3042\u3044c")))
+
+ def test_hankaku_len(self):
+ # abc
+ self.assertEqual(3, hankaku_len(u("abc")))
+ # あいう
+ self.assertEqual(0, hankaku_len(u("\u3042\u3044\u3046")))
+ # あいc
+ self.assertEqual(1, hankaku_len(u("\u3042\u3044c")))
+
+ def test_string_width(self):
+ # abc
+ self.assertEqual(3, string_width(u("abc")))
+ # あいう
+ self.assertEqual(6, string_width(u("\u3042\u3044\u3046")))
+ # あいc
+ self.assertEqual(5, string_width(u("\u3042\u3044c")))
+
+ def test_test_textsize(self):
+ from blockdiag.utils.fontmap import FontInfo
+ font = FontInfo('serif', None, 11)
+
+ # abc
+ self.assertEqual((19, 11), textsize(u("abc"), font))
+ # あいう
+ self.assertEqual((33, 11), textsize(u("\u3042\u3044\u3046"), font))
+ # あいc
+ self.assertEqual((29, 11), textsize(u("\u3042\u3044c"), font))
+
+ # abc
+ font = FontInfo('serif', None, 24)
+ self.assertEqual((40, 24), textsize(u("abc"), font))
+
+ # あいう
+ font = FontInfo('serif', None, 18)
+ self.assertEqual((54, 18), textsize(u("\u3042\u3044\u3046"), font))
diff --git a/src/blockdiag/tests/test_parser.py b/src/blockdiag/tests/test_parser.py
index 8676eb3..cde0d6f 100644
--- a/src/blockdiag/tests/test_parser.py
+++ b/src/blockdiag/tests/test_parser.py
@@ -1,141 +1,148 @@
# -*- coding: utf-8 -*-
+from __future__ import print_function
+
+import sys
+if sys.version_info < (2, 7):
+ import unittest2 as unittest
+else:
+ import unittest
from blockdiag.parser import parse_string, ParseException
-from blockdiag.parser import Graph, SubGraph, Statements, Node, Edge
-from nose.tools import raises
-
-
-def test_parser_basic():
- # basic digram
- code = """
- diagram test {
- A -> B -> C, D;
- }
- """
-
- tree = parse_string(code)
- assert isinstance(tree, Graph)
-
-
-def test_parser_without_diagram_id():
- code = """
- diagram {
- A -> B -> C, D;
- }
- """
- tree = parse_string(code)
- assert isinstance(tree, Graph)
-
- code = """
- {
- A -> B -> C, D;
- }
- """
- tree = parse_string(code)
- assert isinstance(tree, Graph)
-
-
-def test_parser_empty_diagram():
- code = """
- diagram {
- }
- """
- tree = parse_string(code)
- assert isinstance(tree, Graph)
-
- code = """
- {
- }
- """
- tree = parse_string(code)
- assert isinstance(tree, Graph)
-
-
-def test_parser_diagram_includes_nodes():
- code = """
- diagram {
- A;
- B [label = "foobar"];
- C [color = "red"];
- }
- """
- tree = parse_string(code)
- assert isinstance(tree, Graph)
- assert len(tree.stmts) == 3
- assert isinstance(tree.stmts[0], Statements)
- assert isinstance(tree.stmts[0].stmts[0], Node)
- assert isinstance(tree.stmts[1], Statements)
- assert isinstance(tree.stmts[1].stmts[0], Node)
- assert isinstance(tree.stmts[2], Statements)
- assert isinstance(tree.stmts[2].stmts[0], Node)
-
-
-def test_parser_diagram_includes_edges():
- code = """
- diagram {
- A -> B -> C;
- }
- """
- tree = parse_string(code)
- assert isinstance(tree, Graph)
- print tree.stmts
- assert len(tree.stmts) == 1
- assert isinstance(tree.stmts[0], Edge)
-
- code = """
- diagram {
- A -> B -> C [style = dotted];
- D -> E, F;
- }
- """
- tree = parse_string(code)
- assert isinstance(tree, Graph)
- print tree.stmts
- assert len(tree.stmts) == 2
- assert isinstance(tree.stmts[0], Edge)
- assert isinstance(tree.stmts[1], Edge)
-
-
-def test_parser_diagram_includes_groups():
- code = """
- diagram {
- group {
- A; B;
- }
- group {
- C -> D;
- }
- }
- """
- tree = parse_string(code)
- assert isinstance(tree, Graph)
- assert len(tree.stmts) == 2
-
- assert isinstance(tree.stmts[0], SubGraph)
- assert len(tree.stmts[0].stmts) == 2
- assert isinstance(tree.stmts[0].stmts[0], Statements)
- assert isinstance(tree.stmts[0].stmts[0].stmts[0], Node)
- assert isinstance(tree.stmts[0].stmts[1], Statements)
- assert isinstance(tree.stmts[0].stmts[1].stmts[0], Node)
-
- assert isinstance(tree.stmts[1], SubGraph)
- assert len(tree.stmts[1].stmts) == 1
- assert isinstance(tree.stmts[1].stmts[0], Edge)
-
-
-def test_parser_diagram_includes_diagram_attributes():
- code = """
- diagram {
- fontsize = 12;
- node_width = 80;
- }
- """
- tree = parse_string(code)
- assert isinstance(tree, Graph)
- assert len(tree.stmts) == 2
-
-
- at raises(ParseException)
-def test_parser_parenthesis_ness():
- code = ""
- parse_string(code)
+from blockdiag.parser import Diagram, Group, Statements, Node, Edge
+
+
+class TestParser(unittest.TestCase):
+ def test_basic(self):
+ # basic digram
+ code = """
+ diagram test {
+ A -> B -> C, D;
+ }
+ """
+
+ tree = parse_string(code)
+ self.assertIsInstance(tree, Diagram)
+
+ def test_without_diagram_id(self):
+ code = """
+ diagram {
+ A -> B -> C, D;
+ }
+ """
+ tree = parse_string(code)
+ self.assertIsInstance(tree, Diagram)
+
+ code = """
+ {
+ A -> B -> C, D;
+ }
+ """
+ tree = parse_string(code)
+ self.assertIsInstance(tree, Diagram)
+
+ def test_empty_diagram(self):
+ code = """
+ diagram {
+ }
+ """
+ tree = parse_string(code)
+ self.assertIsInstance(tree, Diagram)
+
+ code = """
+ {
+ }
+ """
+ tree = parse_string(code)
+ self.assertIsInstance(tree, Diagram)
+
+ def test_diagram_includes_nodes(self):
+ code = """
+ diagram {
+ A;
+ B [label = "foobar"];
+ C [color = "red"];
+ }
+ """
+ tree = parse_string(code)
+ self.assertIsInstance(tree, Diagram)
+ self.assertEqual(3, len(tree.stmts))
+ self.assertIsInstance(tree.stmts[0], Statements)
+ self.assertIsInstance(tree.stmts[0].stmts[0], Node)
+ self.assertIsInstance(tree.stmts[1], Statements)
+ self.assertIsInstance(tree.stmts[1].stmts[0], Node)
+ self.assertIsInstance(tree.stmts[2], Statements)
+ self.assertIsInstance(tree.stmts[2].stmts[0], Node)
+
+ def test_diagram_includes_edges(self):
+ code = """
+ diagram {
+ A -> B -> C;
+ }
+ """
+ tree = parse_string(code)
+ self.assertIsInstance(tree, Diagram)
+ self.assertEqual(1, len(tree.stmts))
+ self.assertIsInstance(tree.stmts[0], Statements)
+ self.assertEqual(2, len(tree.stmts[0].stmts))
+ self.assertIsInstance(tree.stmts[0].stmts[0], Edge)
+ self.assertIsInstance(tree.stmts[0].stmts[1], Edge)
+
+ code = """
+ diagram {
+ A -> B -> C [style = dotted];
+ D -> E, F;
+ }
+ """
+ tree = parse_string(code)
+ self.assertIsInstance(tree, Diagram)
+ self.assertEqual(2, len(tree.stmts))
+ self.assertIsInstance(tree.stmts[0], Statements)
+ self.assertEqual(2, len(tree.stmts[0].stmts))
+ self.assertIsInstance(tree.stmts[0].stmts[0], Edge)
+ self.assertIsInstance(tree.stmts[0].stmts[1], Edge)
+ self.assertIsInstance(tree.stmts[1], Statements)
+ self.assertEqual(1, len(tree.stmts[1].stmts))
+ self.assertIsInstance(tree.stmts[1].stmts[0], Edge)
+
+ def test_diagram_includes_groups(self):
+ code = """
+ diagram {
+ group {
+ A; B;
+ }
+ group {
+ C -> D;
+ }
+ }
+ """
+ tree = parse_string(code)
+ self.assertIsInstance(tree, Diagram)
+ self.assertEqual(2, len(tree.stmts))
+
+ self.assertIsInstance(tree.stmts[0], Group)
+ self.assertEqual(2, len(tree.stmts[0].stmts))
+ self.assertIsInstance(tree.stmts[0].stmts[0], Statements)
+ self.assertIsInstance(tree.stmts[0].stmts[0].stmts[0], Node)
+ self.assertIsInstance(tree.stmts[0].stmts[1], Statements)
+ self.assertIsInstance(tree.stmts[0].stmts[1].stmts[0], Node)
+
+ self.assertIsInstance(tree.stmts[1], Group)
+ self.assertEqual(1, len(tree.stmts[1].stmts))
+ self.assertIsInstance(tree.stmts[1].stmts[0], Statements)
+ self.assertIsInstance(tree.stmts[1].stmts[0].stmts[0], Edge)
+
+ def test_diagram_includes_diagram_attributes(self):
+ code = """
+ diagram {
+ fontsize = 12;
+ node_width = 80;
+ }
+ """
+ tree = parse_string(code)
+ self.assertIsInstance(tree, Diagram)
+ self.assertEqual(2, len(tree.stmts))
+
+ def test_parenthesis_ness(self):
+ with self.assertRaises(ParseException):
+ code = ""
+ parse_string(code)
diff --git a/src/blockdiag/tests/test_pep8.py b/src/blockdiag/tests/test_pep8.py
index 5d946eb..287ef95 100644
--- a/src/blockdiag/tests/test_pep8.py
+++ b/src/blockdiag/tests/test_pep8.py
@@ -1,52 +1,53 @@
-# -*- coding: utf-8 -*-
-
-import os
-import sys
-import pep8
-
-CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
-BASE_DIR = os.path.dirname(CURRENT_DIR)
-
-
-def test_pep8():
- arglist = [['statistics', True],
- ['show-source', True],
- ['repeat', True],
- ['paths', [BASE_DIR]]]
-
- pep8style = pep8.StyleGuide(arglist, parse_argv=False, config_file=True)
- options = pep8style.options
- if options.doctest:
- import doctest
- fail_d, done_d = doctest.testmod(report=False, verbose=options.verbose)
- fail_s, done_s = pep8.selftest(options)
- count_failed = fail_s + fail_d
- if not options.quiet:
- count_passed = done_d + done_s - count_failed
- print("%d passed and %d failed." % (count_passed, count_failed))
- if count_failed:
- print("Test failed.")
- else:
- print("Test passed.")
- if count_failed:
- sys.exit(1)
- if options.testsuite:
- pep8.init_tests(pep8style)
- report = pep8style.check_files()
- if options.statistics:
- report.print_statistics()
- if options.benchmark:
- report.print_benchmark()
- if options.testsuite and not options.quiet:
- report.print_results()
- if report.total_errors:
- if options.count:
- sys.stderr.write(str(report.total_errors) + '\n')
- #sys.exit(1)
-
- # reporting errors (additional summary)
- errors = report.get_count('E')
- warnings = report.get_count('W')
- message = 'pep8: %d errors / %d warnings' % (errors, warnings)
- print message
- assert report.total_errors == 0, message
+# -*- coding: utf-8 -*-
+
+from __future__ import print_function
+import os
+import sys
+import pep8
+
+CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
+BASE_DIR = os.path.dirname(CURRENT_DIR)
+
+
+def test_pep8():
+ arglist = [['statistics', True],
+ ['show-source', True],
+ ['repeat', True],
+ ['paths', [BASE_DIR]]]
+
+ pep8style = pep8.StyleGuide(arglist, parse_argv=False, config_file=True)
+ options = pep8style.options
+ if options.doctest:
+ import doctest
+ fail_d, done_d = doctest.testmod(report=False, verbose=options.verbose)
+ fail_s, done_s = pep8.selftest(options)
+ count_failed = fail_s + fail_d
+ if not options.quiet:
+ count_passed = done_d + done_s - count_failed
+ print("%d passed and %d failed." % (count_passed, count_failed))
+ if count_failed:
+ print("Test failed.")
+ else:
+ print("Test passed.")
+ if count_failed:
+ sys.exit(1)
+ if options.testsuite:
+ pep8.init_tests(pep8style)
+ report = pep8style.check_files()
+ if options.statistics:
+ report.print_statistics()
+ if options.benchmark:
+ report.print_benchmark()
+ if options.testsuite and not options.quiet:
+ report.print_results()
+ if report.total_errors:
+ if options.count:
+ sys.stderr.write(str(report.total_errors) + '\n')
+ #sys.exit(1)
+
+ # reporting errors (additional summary)
+ errors = report.get_count('E')
+ warnings = report.get_count('W')
+ message = 'pep8: %d errors / %d warnings' % (errors, warnings)
+ print(message)
+ assert report.total_errors == 0, message
diff --git a/src/blockdiag/tests/test_rst_directives.py b/src/blockdiag/tests/test_rst_directives.py
index 956306e..c22884e 100644
--- a/src/blockdiag/tests/test_rst_directives.py
+++ b/src/blockdiag/tests/test_rst_directives.py
@@ -1,9 +1,15 @@
# -*- coding: utf-8 -*-
+import sys
+if sys.version_info < (2, 7):
+ import unittest2 as unittest
+else:
+ import unittest
+
import os
-import tempfile
-import unittest2
-from blockdiag.tests.utils import stderr_wrapper, assertRaises
+import io
+from blockdiag.utils.compat import u
+from blockdiag.tests.utils import capture_stderr, with_pil, TemporaryDirectory
from docutils import nodes
from docutils.core import publish_doctree, publish_parts
@@ -11,35 +17,22 @@ from docutils.parsers.rst import directives as docutils
from blockdiag.utils.rst import directives
-def setup_directive_base(func):
- def _(self):
- klass = directives.BlockdiagDirectiveBase
- docutils.register_directive('blockdiag', klass)
- func(self)
-
- _.__name__ = func.__name__
- return _
-
-
-def use_tmpdir(func):
- def _(self):
- try:
- tmpdir = tempfile.mkdtemp()
- func(self, tmpdir)
- finally:
- for filename in os.listdir(tmpdir):
- os.unlink(tmpdir + "/" + filename)
- os.rmdir(tmpdir)
-
- _.__name__ = func.__name__
- return _
-
+class TestRstDirectives(unittest.TestCase):
+ def setUp(self):
+ docutils.register_directive('blockdiag',
+ directives.BlockdiagDirectiveBase)
+ self._tmpdir = TemporaryDirectory()
-class TestRstDirectives(unittest2.TestCase):
def tearDown(self):
if 'blockdiag' in docutils._directives:
del docutils._directives['blockdiag']
+ self._tmpdir.clean()
+
+ @property
+ def tmpdir(self):
+ return self._tmpdir.name
+
def test_setup(self):
directives.setup()
options = directives.directive_options
@@ -53,12 +46,10 @@ class TestRstDirectives(unittest2.TestCase):
self.assertEqual(False, options['nodoctype'])
self.assertEqual(False, options['noviewbox'])
self.assertEqual(False, options['inline_svg'])
- self.assertEqual(False, options['ignore_pil'])
def test_setup_with_args(self):
directives.setup(format='SVG', antialias=True, fontpath='/dev/null',
- nodoctype=True, noviewbox=True, inline_svg=True,
- ignore_pil=True)
+ nodoctype=True, noviewbox=True, inline_svg=True)
options = directives.directive_options
self.assertIn('blockdiag', docutils._directives)
@@ -70,17 +61,14 @@ class TestRstDirectives(unittest2.TestCase):
self.assertEqual(True, options['nodoctype'])
self.assertEqual(True, options['noviewbox'])
self.assertEqual(True, options['inline_svg'])
- self.assertEqual(True, options['ignore_pil'])
- @stderr_wrapper
- @setup_directive_base
+ @capture_stderr
def test_base_noargs(self):
text = ".. blockdiag::"
doctree = publish_doctree(text)
self.assertEqual(1, len(doctree))
self.assertEqual(nodes.system_message, type(doctree[0]))
- @setup_directive_base
def test_base_with_block(self):
text = ".. blockdiag::\n\n { A -> B }"
doctree = publish_doctree(text)
@@ -90,15 +78,13 @@ class TestRstDirectives(unittest2.TestCase):
self.assertEqual(None, doctree[0]['alt'])
self.assertEqual({}, doctree[0]['options'])
- @stderr_wrapper
- @setup_directive_base
+ @capture_stderr
def test_base_with_emptyblock(self):
text = ".. blockdiag::\n\n \n"
doctree = publish_doctree(text)
self.assertEqual(1, len(doctree))
self.assertEqual(nodes.system_message, type(doctree[0]))
- @setup_directive_base
def test_base_with_filename(self):
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, 'diagrams/node_attribute.diag')
@@ -107,26 +93,23 @@ class TestRstDirectives(unittest2.TestCase):
self.assertEqual(1, len(doctree))
self.assertEqual(directives.blockdiag, type(doctree[0]))
- self.assertEqual(open(filename).read(), doctree[0]['code'])
+ self.assertEqual(io.open(filename).read(), doctree[0]['code'])
self.assertEqual(None, doctree[0]['alt'])
self.assertEqual({}, doctree[0]['options'])
- @stderr_wrapper
- @setup_directive_base
+ @capture_stderr
def test_base_with_filename_not_exists(self):
text = ".. blockdiag:: unknown.diag"
doctree = publish_doctree(text)
self.assertEqual(nodes.system_message, type(doctree[0]))
- @stderr_wrapper
- @setup_directive_base
+ @capture_stderr
def test_base_with_block_and_filename(self):
text = ".. blockdiag:: unknown.diag\n\n { A -> B }"
doctree = publish_doctree(text)
self.assertEqual(1, len(doctree))
self.assertEqual(nodes.system_message, type(doctree[0]))
- @setup_directive_base
def test_base_with_options(self):
text = ".. blockdiag::\n :alt: hello world\n :desctable:\n" + \
" :maxwidth: 100\n\n { A -> B }"
@@ -138,47 +121,42 @@ class TestRstDirectives(unittest2.TestCase):
self.assertEqual(None, doctree[0]['options']['desctable'])
self.assertEqual(100, doctree[0]['options']['maxwidth'])
- @use_tmpdir
- def test_block(self, path):
- directives.setup(format='SVG', outputdir=path)
+ def test_block(self):
+ directives.setup(format='SVG', outputdir=self.tmpdir)
text = ".. blockdiag::\n\n { A -> B }"
doctree = publish_doctree(text)
self.assertEqual(1, len(doctree))
self.assertEqual(nodes.image, type(doctree[0]))
self.assertFalse('alt' in doctree[0])
- self.assertEqual(0, doctree[0]['uri'].index(path))
+ self.assertEqual(0, doctree[0]['uri'].index(self.tmpdir))
self.assertFalse('target' in doctree[0])
- @use_tmpdir
- def test_block_alt(self, path):
- directives.setup(format='SVG', outputdir=path)
+ def test_block_alt(self):
+ directives.setup(format='SVG', outputdir=self.tmpdir)
text = ".. blockdiag::\n :alt: hello world\n\n { A -> B }"
doctree = publish_doctree(text)
self.assertEqual(1, len(doctree))
self.assertEqual(nodes.image, type(doctree[0]))
self.assertEqual('hello world', doctree[0]['alt'])
- self.assertEqual(0, doctree[0]['uri'].index(path))
+ self.assertEqual(0, doctree[0]['uri'].index(self.tmpdir))
self.assertFalse('target' in doctree[0])
- @use_tmpdir
- @assertRaises(RuntimeError)
- def test_block_fontpath1(self, path):
- directives.setup(format='SVG', fontpath=['dummy.ttf'],
- outputdir=path)
- text = ".. blockdiag::\n :alt: hello world\n\n { A -> B }"
- publish_doctree(text)
-
- @use_tmpdir
- @assertRaises(RuntimeError)
- def test_block_fontpath2(self, path):
- directives.setup(format='SVG', fontpath='dummy.ttf',
- outputdir=path)
- text = ".. blockdiag::\n :alt: hello world\n\n { A -> B }"
- publish_doctree(text)
-
- @use_tmpdir
- def test_caption(self, path):
- directives.setup(format='SVG', outputdir=path)
+ def test_block_fontpath1(self):
+ with self.assertRaises(RuntimeError):
+ directives.setup(format='SVG', fontpath=['dummy.ttf'],
+ outputdir=self.tmpdir)
+ text = ".. blockdiag::\n :alt: hello world\n\n { A -> B }"
+ publish_doctree(text)
+
+ def test_block_fontpath2(self):
+ with self.assertRaises(RuntimeError):
+ directives.setup(format='SVG', fontpath='dummy.ttf',
+ outputdir=self.tmpdir)
+ text = ".. blockdiag::\n :alt: hello world\n\n { A -> B }"
+ publish_doctree(text)
+
+ def test_caption(self):
+ directives.setup(format='SVG', outputdir=self.tmpdir)
text = ".. blockdiag::\n :caption: hello world\n\n { A -> B }"
doctree = publish_doctree(text)
self.assertEqual(1, len(doctree))
@@ -190,20 +168,18 @@ class TestRstDirectives(unittest2.TestCase):
self.assertEqual(nodes.Text, type(doctree[0][1][0]))
self.assertEqual('hello world', doctree[0][1][0])
- @use_tmpdir
- def test_block_maxwidth(self, path):
- directives.setup(format='SVG', outputdir=path)
+ def test_block_maxwidth(self):
+ directives.setup(format='SVG', outputdir=self.tmpdir)
text = ".. blockdiag::\n :maxwidth: 100\n\n { A -> B }"
doctree = publish_doctree(text)
self.assertEqual(1, len(doctree))
self.assertEqual(nodes.image, type(doctree[0]))
self.assertFalse('alt' in doctree[0])
- self.assertEqual(0, doctree[0]['uri'].index(path))
- self.assertFalse(0, doctree[0]['target'].index(path))
+ self.assertEqual(0, doctree[0]['uri'].index(self.tmpdir))
+ self.assertFalse(0, doctree[0]['target'].index(self.tmpdir))
- @use_tmpdir
- def test_block_nodoctype_false(self, path):
- directives.setup(format='SVG', outputdir=path, nodoctype=False)
+ def test_block_nodoctype_false(self):
+ directives.setup(format='SVG', outputdir=self.tmpdir, nodoctype=False)
text = ".. blockdiag::\n :alt: hello world\n\n { A -> B }"
doctree = publish_doctree(text)
self.assertEqual(1, len(doctree))
@@ -212,9 +188,8 @@ class TestRstDirectives(unittest2.TestCase):
self.assertEqual("<?xml version='1.0' encoding='UTF-8'?>\n"
"<!DOCTYPE ", svg[:49])
- @use_tmpdir
- def test_block_nodoctype_true(self, path):
- directives.setup(format='SVG', outputdir=path, nodoctype=True)
+ def test_block_nodoctype_true(self):
+ directives.setup(format='SVG', outputdir=self.tmpdir, nodoctype=True)
text = ".. blockdiag::\n :alt: hello world\n\n { A -> B }"
doctree = publish_doctree(text)
self.assertEqual(1, len(doctree))
@@ -223,9 +198,8 @@ class TestRstDirectives(unittest2.TestCase):
self.assertNotEqual("<?xml version='1.0' encoding='UTF-8'?>\n"
"<!DOCTYPE ", svg[:49])
- @use_tmpdir
- def test_block_noviewbox_false(self, path):
- directives.setup(format='SVG', outputdir=path, noviewbox=False)
+ def test_block_noviewbox_false(self):
+ directives.setup(format='SVG', outputdir=self.tmpdir, noviewbox=False)
text = ".. blockdiag::\n :alt: hello world\n\n { A -> B }"
doctree = publish_doctree(text)
self.assertEqual(1, len(doctree))
@@ -233,9 +207,8 @@ class TestRstDirectives(unittest2.TestCase):
svg = open(doctree[0]['uri']).read()
self.assertRegexpMatches(svg, '<svg viewBox="0 0 \d+ \d+" ')
- @use_tmpdir
- def test_block_noviewbox_true(self, path):
- directives.setup(format='SVG', outputdir=path, noviewbox=True)
+ def test_block_noviewbox_true(self):
+ directives.setup(format='SVG', outputdir=self.tmpdir, noviewbox=True)
text = ".. blockdiag::\n :alt: hello world\n\n { A -> B }"
doctree = publish_doctree(text)
self.assertEqual(1, len(doctree))
@@ -243,18 +216,16 @@ class TestRstDirectives(unittest2.TestCase):
svg = open(doctree[0]['uri']).read()
self.assertRegexpMatches(svg, '<svg height="\d+" width="\d+" ')
- @use_tmpdir
- def test_block_inline_svg_false(self, path):
- directives.setup(format='SVG', outputdir=path, inline_svg=False)
+ def test_block_inline_svg_false(self):
+ directives.setup(format='SVG', outputdir=self.tmpdir, inline_svg=False)
text = ".. blockdiag::\n :alt: hello world\n\n { A -> B }"
doctree = publish_doctree(text)
self.assertEqual(1, len(doctree))
self.assertEqual(nodes.image, type(doctree[0]))
- self.assertEqual(1, len(os.listdir(path)))
+ self.assertEqual(1, len(os.listdir(self.tmpdir)))
- @use_tmpdir
- def test_block_inline_svg_true(self, path):
- directives.setup(format='SVG', outputdir=path, inline_svg=True)
+ def test_block_inline_svg_true(self):
+ directives.setup(format='SVG', outputdir=self.tmpdir, inline_svg=True)
text = ".. blockdiag::\n :alt: hello world\n\n { A -> B }"
doctree = publish_doctree(text)
self.assertEqual(1, len(doctree))
@@ -263,26 +234,23 @@ class TestRstDirectives(unittest2.TestCase):
self.assertEqual(nodes.Text, type(doctree[0][0]))
self.assertEqual("<?xml version='1.0' encoding='UTF-8'?>\n"
"<!DOCTYPE ", doctree[0][0][:49])
- self.assertEqual(0, len(os.listdir(path)))
+ self.assertEqual(0, len(os.listdir(self.tmpdir)))
- @use_tmpdir
- def test_block_inline_svg_true_but_nonsvg_format(self, path):
- directives.setup(format='PNG', outputdir=path, inline_svg=True)
+ @with_pil
+ def test_block_inline_svg_true_but_nonsvg_format(self):
+ directives.setup(format='PNG', outputdir=self.tmpdir, inline_svg=True)
text = ".. blockdiag::\n :alt: hello world\n\n { A -> B }"
doctree = publish_doctree(text)
self.assertEqual(1, len(doctree))
self.assertEqual(nodes.image, type(doctree[0]))
- @use_tmpdir
- def test_block_inline_svg_true_with_multibytes(self, path):
- directives.setup(format='SVG', outputdir=path,
- inline_svg=True, ignore_pil=True)
- text = u".. blockdiag::\n :alt: hello world\n\n { あ -> い }"
+ def test_block_inline_svg_true_with_multibytes(self):
+ directives.setup(format='SVG', outputdir=self.tmpdir)
+ text = u(".. blockdiag::\n :alt: hello world\n\n { あ -> い }")
publish_parts(source=text)
- @use_tmpdir
- def test_block_max_width_inline_svg(self, path):
- directives.setup(format='SVG', outputdir=path,
+ def test_block_max_width_inline_svg(self):
+ directives.setup(format='SVG', outputdir=self.tmpdir,
nodoctype=True, noviewbox=True, inline_svg=True)
text = ".. blockdiag::\n :maxwidth: 100\n\n { A -> B }"
doctree = publish_doctree(text)
@@ -292,33 +260,15 @@ class TestRstDirectives(unittest2.TestCase):
self.assertRegexpMatches(doctree[0][0],
'<svg height="\d+" width="100" ')
- @use_tmpdir
- def test_block_ignore_pil_false(self, path):
- directives.setup(format='SVG', outputdir=path, ignore_pil=False)
- text = ".. blockdiag::\n :alt: hello world\n\n { A -> B }"
- doctree = publish_doctree(text)
- self.assertEqual(1, len(doctree))
- self.assertEqual(nodes.image, type(doctree[0]))
-
- @use_tmpdir
- def test_block_ignore_pil_true(self, path):
- directives.setup(format='SVG', outputdir=path, ignore_pil=True)
- text = ".. blockdiag::\n :alt: hello world\n\n { A -> B }"
- doctree = publish_doctree(text)
- self.assertEqual(1, len(doctree))
- self.assertEqual(nodes.image, type(doctree[0]))
-
- @use_tmpdir
- def test_desctable_without_description(self, path):
- directives.setup(format='SVG', outputdir=path)
+ def test_desctable_without_description(self):
+ directives.setup(format='SVG', outputdir=self.tmpdir)
text = ".. blockdiag::\n :desctable:\n\n { A -> B }"
doctree = publish_doctree(text)
self.assertEqual(1, len(doctree))
self.assertEqual(nodes.image, type(doctree[0]))
- @use_tmpdir
- def test_desctable(self, path):
- directives.setup(format='SVG', outputdir=path)
+ def test_desctable(self):
+ directives.setup(format='SVG', outputdir=self.tmpdir)
text = ".. blockdiag::\n :desctable:\n\n" + \
" { A [description = foo]; B [description = bar]; }"
doctree = publish_doctree(text)
@@ -351,9 +301,8 @@ class TestRstDirectives(unittest2.TestCase):
self.assertEqual('B', tbody[1][0][0][0])
self.assertEqual('bar', tbody[1][1][0][0])
- @use_tmpdir
- def test_desctable_using_node_group(self, path):
- directives.setup(format='SVG', outputdir=path)
+ def test_desctable_using_node_group(self):
+ directives.setup(format='SVG', outputdir=self.tmpdir)
text = ".. blockdiag::\n :desctable:\n\n { A -> B; group { A } }"
text = ".. blockdiag::\n :desctable:\n\n" + \
" { A [description = foo]; B [description = bar]; " + \
@@ -411,9 +360,8 @@ class TestRstDirectives(unittest2.TestCase):
self.assertEqual(1, len(tbody[1][1]))
self.assertEqual('bar', tbody[1][1][0][0])
- @use_tmpdir
- def test_desctable_with_rest_markups(self, path):
- directives.setup(format='SVG', outputdir=path)
+ def test_desctable_with_rest_markups(self):
+ directives.setup(format='SVG', outputdir=self.tmpdir)
text = ".. blockdiag::\n :desctable:\n\n" + \
" { A [description = \"foo *bar* **baz**\"]; " + \
" B [description = \"**foo** *bar* baz\"]; }"
@@ -468,9 +416,8 @@ class TestRstDirectives(unittest2.TestCase):
self.assertEqual(nodes.Text, type(tbody[1][1][0][3]))
self.assertEqual(' baz', str(tbody[1][1][0][3]))
- @use_tmpdir
- def test_desctable_with_numbered(self, path):
- directives.setup(format='SVG', outputdir=path)
+ def test_desctable_with_numbered(self):
+ directives.setup(format='SVG', outputdir=self.tmpdir)
text = ".. blockdiag::\n :desctable:\n\n" + \
" { A [numbered = 2]; B [numbered = 1]; }"
doctree = publish_doctree(text)
@@ -503,9 +450,8 @@ class TestRstDirectives(unittest2.TestCase):
self.assertEqual('2', tbody[1][0][0][0])
self.assertEqual('A', tbody[1][1][0][0])
- @use_tmpdir
- def test_desctable_with_numbered_and_description(self, path):
- directives.setup(format='SVG', outputdir=path)
+ def test_desctable_with_numbered_and_description(self):
+ directives.setup(format='SVG', outputdir=self.tmpdir)
text = ".. blockdiag::\n :desctable:\n\n" + \
" { A [description = foo, numbered = 2]; " + \
" B [description = bar, numbered = 1]; }"
@@ -545,9 +491,8 @@ class TestRstDirectives(unittest2.TestCase):
self.assertEqual('A', tbody[1][1][0][0])
self.assertEqual('foo', tbody[1][2][0][0])
- @use_tmpdir
- def test_desctable_for_edges(self, path):
- directives.setup(format='SVG', outputdir=path)
+ def test_desctable_for_edges(self):
+ directives.setup(format='SVG', outputdir=self.tmpdir)
text = ".. blockdiag::\n :desctable:\n\n" + \
" { A -> B [description = \"foo\"]; " + \
" C -> D [description = \"bar\"]; " + \
@@ -587,9 +532,8 @@ class TestRstDirectives(unittest2.TestCase):
self.assertEqual(nodes.Text, type(tbody[1][1][0][0]))
self.assertEqual('bar', str(tbody[1][1][0][0]))
- @use_tmpdir
- def test_desctable_for_nodes_and_edges(self, path):
- directives.setup(format='SVG', outputdir=path)
+ def test_desctable_for_nodes_and_edges(self):
+ directives.setup(format='SVG', outputdir=self.tmpdir)
text = ".. blockdiag::\n :desctable:\n\n" + \
" { A -> B [description = \"foo\"]; " + \
" C -> D [description = \"bar\"]; " + \
diff --git a/src/blockdiag/tests/test_utils_fontmap.py b/src/blockdiag/tests/test_utils_fontmap.py
index 6bd4b8a..464c40f 100644
--- a/src/blockdiag/tests/test_utils_fontmap.py
+++ b/src/blockdiag/tests/test_utils_fontmap.py
@@ -1,20 +1,28 @@
# -*- coding: utf-8 -*-
-import os
import sys
-import tempfile
-import unittest2
-from blockdiag.tests.utils import stderr_wrapper, assertRaises
+if sys.version_info < (2, 7):
+ import unittest2 as unittest
+else:
+ import unittest
-from cStringIO import StringIO
-from blockdiag.utils.collections import namedtuple
+import os
+import tempfile
+from blockdiag.utils.compat import u
+from blockdiag.tests.utils import capture_stderr
+
+try:
+ from io import StringIO
+except ImportError:
+ from cStringIO import StringIO
+from collections import namedtuple
from blockdiag.utils.fontmap import FontInfo, FontMap
FontElement = namedtuple('FontElement', 'fontfamily fontsize')
-class TestUtilsFontmap(unittest2.TestCase):
+class TestUtilsFontmap(unittest.TestCase):
def setUp(self):
fontpath1 = __file__
fontpath2 = os.path.join(os.path.dirname(__file__), 'utils.py')
@@ -33,33 +41,33 @@ class TestUtilsFontmap(unittest2.TestCase):
FontInfo("my-cursive", None, 11)
FontInfo("-fantasy", None, 11)
- @assertRaises(AttributeError)
def test_fontinfo_invalid_familyname1(self):
- FontInfo("unknown", None, 11)
+ with self.assertRaises(AttributeError):
+ FontInfo("unknown", None, 11)
- @assertRaises(AttributeError)
def test_fontinfo_invalid_familyname2(self):
- FontInfo("sansserif-", None, 11)
+ with self.assertRaises(AttributeError):
+ FontInfo("sansserif-", None, 11)
- @assertRaises(AttributeError)
def test_fontinfo_invalid_familyname3(self):
- FontInfo("monospace-unkown", None, 11)
+ with self.assertRaises(AttributeError):
+ FontInfo("monospace-unkown", None, 11)
- @assertRaises(AttributeError)
def test_fontinfo_invalid_familyname4(self):
- FontInfo("cursive-bold-bold", None, 11)
+ with self.assertRaises(AttributeError):
+ FontInfo("cursive-bold-bold", None, 11)
- @assertRaises(AttributeError)
def test_fontinfo_invalid_familyname5(self):
- FontInfo("SERIF", None, 11)
+ with self.assertRaises(AttributeError):
+ FontInfo("SERIF", None, 11)
- @assertRaises(TypeError)
def test_fontinfo_invalid_fontsize1(self):
- FontInfo("serif", None, None)
+ with self.assertRaises(TypeError):
+ FontInfo("serif", None, None)
- @assertRaises(ValueError)
def test_fontinfo_invalid_fontsize2(self):
- FontInfo("serif", None, '')
+ with self.assertRaises(ValueError):
+ FontInfo("serif", None, '')
def test_fontinfo_parse(self):
font = FontInfo("serif", None, 11)
@@ -120,9 +128,9 @@ class TestUtilsFontmap(unittest2.TestCase):
font = FontInfo("-serif", None, 11)
self.assertEqual('serif-normal', font.familyname)
- @stderr_wrapper
+ @capture_stderr
def test_fontmap_empty_config(self):
- config = StringIO("")
+ config = StringIO(u(""))
fmap = FontMap(config)
font1 = fmap.find()
@@ -150,7 +158,7 @@ class TestUtilsFontmap(unittest2.TestCase):
self.assertEqual(font1.path, font4.path)
self.assertEqual(font1.size, font4.size)
- @stderr_wrapper
+ @capture_stderr
def test_fontmap_none_config(self):
fmap = FontMap()
@@ -161,7 +169,7 @@ class TestUtilsFontmap(unittest2.TestCase):
self.assertEqual(11, font1.size)
def test_fontmap_normal_config(self):
- _config = "[fontmap]\nsansserif: %s\nsansserif-bold: %s\n" % \
+ _config = u("[fontmap]\nsansserif: %s\nsansserif-bold: %s\n") % \
(self.fontpath[0], self.fontpath[1])
config = StringIO(_config)
fmap = FontMap(config)
@@ -203,20 +211,25 @@ class TestUtilsFontmap(unittest2.TestCase):
self.assertEqual(font1.size, font6.size)
def test_fontmap_duplicated_fontentry1(self):
- _config = "[fontmap]\nsansserif: %s\nsansserif: %s\n" % \
+ _config = u("[fontmap]\nsansserif: %s\nsansserif: %s\n") % \
(self.fontpath[0], self.fontpath[1])
config = StringIO(_config)
- fmap = FontMap(config)
+ if sys.version_info[0] == 2:
+ fmap = FontMap(config)
- font1 = fmap.find()
- self.assertEqual('sansserif', font1.generic_family)
- self.assertEqual(self.fontpath[1], font1.path)
- self.assertEqual(11, font1.size)
+ font1 = fmap.find()
+ self.assertEqual('sansserif', font1.generic_family)
+ self.assertEqual(self.fontpath[1], font1.path)
+ self.assertEqual(11, font1.size)
+ else:
+ import configparser
+ with self.assertRaises(configparser.DuplicateOptionError):
+ FontMap(config)
def test_fontmap_duplicated_fontentry2(self):
# this testcase is only for python2.6 or later
if sys.version_info > (2, 6):
- _config = "[fontmap]\nsansserif: %s\nsansserif-normal: %s\n" % \
+ _config = u("[fontmap]\nsansserif: %s\nsansserif-normal: %s\n") % \
(self.fontpath[0], self.fontpath[1])
config = StringIO(_config)
fmap = FontMap(config)
@@ -226,9 +239,9 @@ class TestUtilsFontmap(unittest2.TestCase):
self.assertEqual(self.fontpath[1], font1.path)
self.assertEqual(11, font1.size)
- @stderr_wrapper
+ @capture_stderr
def test_fontmap_with_nodefault_fontentry(self):
- _config = "[fontmap]\nserif: %s\n" % self.fontpath[0]
+ _config = u("[fontmap]\nserif: %s\n") % self.fontpath[0]
config = StringIO(_config)
fmap = FontMap(config)
@@ -249,9 +262,9 @@ class TestUtilsFontmap(unittest2.TestCase):
self.assertEqual(None, font3.path)
self.assertEqual(20, font3.size)
- @stderr_wrapper
+ @capture_stderr
def test_fontmap_with_nonexistence_fontpath(self):
- _config = "[fontmap]\nserif: unknown_file\n"
+ _config = u("[fontmap]\nserif: unknown_file\n")
config = StringIO(_config)
fmap = FontMap(config)
@@ -261,7 +274,7 @@ class TestUtilsFontmap(unittest2.TestCase):
self.assertEqual(11, font1.size)
def test_fontmap_switch_defaultfamily(self):
- _config = "[fontmap]\nserif-bold: %s\n" % self.fontpath[0]
+ _config = u("[fontmap]\nserif-bold: %s\n") % self.fontpath[0]
config = StringIO(_config)
fmap = FontMap(config)
@@ -289,8 +302,8 @@ class TestUtilsFontmap(unittest2.TestCase):
self.assertEqual(20, font4.size)
def test_fontmap_using_fontalias(self):
- _config = ("[fontmap]\nserif-bold: %s\n" +
- "[fontalias]\ntest = serif-bold\n") % self.fontpath[0]
+ _config = (u("[fontmap]\nserif-bold: %s\n") +
+ u("[fontalias]\ntest = serif-bold\n")) % self.fontpath[0]
config = StringIO(_config)
fmap = FontMap(config)
@@ -303,7 +316,7 @@ class TestUtilsFontmap(unittest2.TestCase):
def test_fontmap_by_file(self):
tmp = tempfile.mkstemp()
- _config = "[fontmap]\nsansserif: %s\nsansserif-bold: %s\n" % \
+ _config = u("[fontmap]\nsansserif: %s\nsansserif-bold: %s\n") % \
(self.fontpath[0], self.fontpath[1])
fp = os.fdopen(tmp[0], 'wt')
@@ -322,13 +335,13 @@ class TestUtilsFontmap(unittest2.TestCase):
def test_fontmap_including_bom_by_file(self):
tmp = tempfile.mkstemp()
- _config = ("\xEF\xBB\xBF[fontmap]\nsansserif: %s\n"
- "sansserif-bold: %s\n") % \
+ _config = (u("[fontmap]\nsansserif: %s\n") +
+ u("sansserif-bold: %s\n")) % \
(self.fontpath[0], self.fontpath[1])
try:
- fp = os.fdopen(tmp[0], 'wt')
- fp.write(_config)
+ fp = os.fdopen(tmp[0], 'wb')
+ fp.write(_config.encode('utf-8-sig'))
fp.close()
fmap = FontMap(tmp[1])
diff --git a/src/blockdiag/tests/utils.py b/src/blockdiag/tests/utils.py
index f4cd434..d51ddd8 100644
--- a/src/blockdiag/tests/utils.py
+++ b/src/blockdiag/tests/utils.py
@@ -1,17 +1,30 @@
# -*- coding: utf-8 -*-
-import re
+from __future__ import print_function
+
import sys
-from StringIO import StringIO
-from nose.tools import eq_
+if sys.version_info < (2, 7):
+ import unittest2 as unittest
+else:
+ import unittest
+
+import os
+import re
+import functools
+from shutil import rmtree
+from tempfile import mkdtemp, mkstemp
from blockdiag.builder import ScreenNodeBuilder
-from blockdiag.parser import parse_string
+from blockdiag.parser import parse_file
+
+try:
+ from io import StringIO
+except ImportError:
+ from cStringIO import StringIO
def supported_pil():
try:
- import _imagingft
+ from PIL import _imagingft
_imagingft
-
return True
except:
return False
@@ -41,87 +54,73 @@ def with_pdf(fn):
return fn
-def argv_wrapper(func, argv=[]):
- def wrap(*args, **kwargs):
- try:
- argv = sys.argv
- sys.argv = []
- func(*args, **kwargs)
- finally:
- sys.argv = argv
-
- wrap.__name__ = func.__name__
- return wrap
-
-
-def stderr_wrapper(func):
+def capture_stderr(func):
def wrap(*args, **kwargs):
try:
stderr = sys.stderr
sys.stderr = StringIO()
- print args, kwargs
func(*args, **kwargs)
+
+ if re.search('ERROR', sys.stderr.getvalue()):
+ raise AssertionError('Caught error')
finally:
if sys.stderr.getvalue():
- print "---[ stderr ] ---"
- print sys.stderr.getvalue()
+ print("---[ stderr ] ---")
+ print(sys.stderr.getvalue())
sys.stderr = stderr
- wrap.__name__ = func.__name__
- return wrap
+ return functools.wraps(func)(wrap)
+
+stderr_wrapper = capture_stderr # FIXME: deprecated
-def assertRaises(exc):
- def decorator(func):
- def fn(self, *args, **kwargs):
- try:
- func(self, *args, **kwargs)
- except exc:
- pass
- else:
- msg = '%s does not raise exceptions: %s' % \
- (func.__name__, str(exc))
- self.fail(msg)
- fn.__name__ = func.__name__
- return fn
+class TemporaryDirectory(object):
+ def __init__(self, suffix='', prefix='tmp', dir=None):
+ self.name = mkdtemp(suffix, prefix, dir)
- return decorator
+ def __del__(self):
+ self.clean()
+ def clean(self):
+ if os.path.exists(self.name):
+ rmtree(self.name)
-def __build_diagram(filename):
- import os
- testdir = os.path.dirname(__file__)
- pathname = "%s/diagrams/%s" % (testdir, filename)
+ def mkstemp(self, suffix='', prefix='tmp', text=False):
+ return mkstemp(suffix, prefix, self.name, text)
- code = open(pathname).read()
- tree = parse_string(code)
- return ScreenNodeBuilder.build(tree)
+class BuilderTestCase(unittest.TestCase):
+ def build(self, filename):
+ basedir = os.path.dirname(__file__)
+ pathname = os.path.join(basedir, 'diagrams', filename)
+ return self._build(parse_file(pathname))
-def __validate_node_attributes(filename, **kwargs):
- diagram = __build_diagram(filename)
+ def _build(self, tree):
+ return ScreenNodeBuilder.build(tree)
- for name, values in kwargs.items():
- if re.match('edge_', name):
- print "[%s]" % name
- name = re.sub('edge_', '', name)
- for (id1, id2), value in values.items():
- found = False
+ def __getattr__(self, name):
+ if name.startswith('assertNode'):
+ def asserter(diagram, attributes):
+ attr_name = name.replace('assertNode', '').lower()
+ print("[node.%s]" % attr_name)
+ for node in (n for n in diagram.nodes if n.drawable):
+ print(node)
+ excepted = attributes[node.id]
+ self.assertEqual(excepted, getattr(node, attr_name))
+
+ return asserter
+ elif name.startswith('assertEdge'):
+ def asserter(diagram, attributes):
+ attr_name = name.replace('assertEdge', '').lower()
+ print("[edge.%s]" % attr_name)
for edge in diagram.edges:
- if edge.node1.id == id1 and edge.node2.id == id2:
- print edge
- eq_(value, getattr(edge, name))
- found = True
-
- if not found:
- raise RuntimeError('edge (%s -> %s) is not found' %
- (id1, id2))
+ print(edge)
+ expected = attributes[(edge.node1.id, edge.node2.id)]
+ self.assertEqual(expected, getattr(edge, attr_name))
+
+ return asserter
else:
- print "[node.%s]" % name
- for node in (n for n in diagram.nodes if n.drawable):
- print node
- value = getattr(node, name)
- eq_(values[node.id], value)
+ return getattr(super(BuilderTestCase, self), name)
diff --git a/src/blockdiag/utils/__init__.py b/src/blockdiag/utils/__init__.py
index 2a3eeb6..8b06a0a 100644
--- a/src/blockdiag/utils/__init__.py
+++ b/src/blockdiag/utils/__init__.py
@@ -13,9 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from __future__ import division
import re
import math
-from blockdiag.utils.collections import namedtuple
+from collections import namedtuple
Size = namedtuple('Size', 'width height')
@@ -28,7 +29,13 @@ class XY(tuple):
return super(XY, cls).__new__(cls, (x, y))
def __getattr__(self, name):
- return self[self.mapper[name]]
+ try:
+ return self[self.mapper[name]]
+ except KeyError:
+ raise AttributeError(name)
+
+ def __setattr__(self, name, value):
+ raise TypeError("'XY' object does not support item assignment")
def shift(self, x=0, y=0):
return self.__class__(self.x + x, self.y + y)
@@ -41,7 +48,10 @@ class Box(list):
super(Box, self).__init__((x1, y1, x2, y2))
def __getattr__(self, name):
- return self[self.mapper[name]]
+ try:
+ return self[self.mapper[name]]
+ except KeyError:
+ raise AttributeError(name)
def __repr__(self):
_format = "<%s (%s, %s) %dx%d at 0x%08x>"
@@ -92,7 +102,7 @@ class Box(list):
@property
def top(self):
- return XY(self.x1 + self.width / 2, self.y1)
+ return XY(self.x1 + self.width // 2, self.y1)
@property
def topright(self):
@@ -104,7 +114,7 @@ class Box(list):
@property
def bottom(self):
- return XY(self.x1 + self.width / 2, self.y2)
+ return XY(self.x1 + self.width // 2, self.y2)
@property
def bottomright(self):
@@ -112,15 +122,18 @@ class Box(list):
@property
def left(self):
- return XY(self.x1, self.y1 + self.height / 2)
+ return XY(self.x1, self.y1 + self.height // 2)
@property
def right(self):
- return XY(self.x2, self.y1 + self.height / 2)
+ return XY(self.x2, self.y1 + self.height // 2)
@property
def center(self):
- return XY(self.x1 + self.width / 2, self.y1 + self.height / 2)
+ return XY(self.x1 + self.width // 2, self.y1 + self.height // 2)
+
+ def to_integer_point(self):
+ return Box(*[int(i) for i in self])
def unquote(string):
@@ -144,3 +157,12 @@ def unquote(string):
return string
else:
return string
+
+
+def is_Pillow_available():
+ try:
+ from PIL import _imagingft
+ _imagingft
+ return True
+ except ImportError:
+ return False
diff --git a/src/blockdiag/utils/bootstrap.py b/src/blockdiag/utils/bootstrap.py
index 2ee10a4..535f728 100644
--- a/src/blockdiag/utils/bootstrap.py
+++ b/src/blockdiag/utils/bootstrap.py
@@ -16,7 +16,8 @@
import os
import re
import sys
-from optparse import OptionParser
+import codecs
+from optparse import OptionParser, SUPPRESS_HELP
from blockdiag import imagedraw
from blockdiag.utils.config import ConfigParser
from blockdiag.utils.fontmap import parse_fontpath, FontMap
@@ -26,21 +27,21 @@ class Application(object):
module = None
options = None
- def run(self):
+ def run(self, args):
try:
- self.parse_options()
+ self.parse_options(args)
self.create_fontmap()
parsed = self.parse_diagram()
return self.build_diagram(parsed)
- except SystemExit, e:
+ except SystemExit as e:
return e
- except UnicodeEncodeError, e:
+ except UnicodeEncodeError as e:
msg = "ERROR: UnicodeEncodeError caught " + \
"(check your font settings)\n"
sys.stderr.write(msg)
return -1
- except Exception, e:
+ except Exception as e:
if self.options and self.options.debug:
import traceback
traceback.print_exc()
@@ -48,36 +49,19 @@ class Application(object):
sys.stderr.write("ERROR: %s\n" % e)
return -1
- def parse_options(self):
- self.options = Options(self.module).parse()
+ def parse_options(self, args):
+ self.options = Options(self.module).parse(args)
def create_fontmap(self):
self.fontmap = create_fontmap(self.options)
- fontpath = self.fontmap.find().path
- _format = self.options.type.lower()
- ignore_pil = self.options.ignore_pil
- if _format in ('png', 'svg') and fontpath and ignore_pil is False:
- try:
- try:
- from PIL import _imagingft
- except ImportError:
- import _imagingft
- except:
- msg = "PIL does not support TrueType fonts, " \
- "reinstall PIL (and libfreetype2)"
- if _format != 'png':
- msg += " or use --ignore-pil option"
-
- raise RuntimeError(msg)
-
def parse_diagram(self):
- import codecs
if self.options.input == '-':
- stream = codecs.getreader('utf-8')(sys.stdin)
+ stream = codecs.getreader('utf-8-sig')(sys.stdin)
self.code = stream.read()
else:
- self.code = codecs.open(self.options.input, 'r', 'utf-8').read()
+ fp = codecs.open(self.options.input, 'r', 'utf-8-sig')
+ self.code = fp.read()
return self.module.parser.parse_string(self.code)
@@ -89,7 +73,6 @@ class Application(object):
drawer = DiagramDraw(self.options.type, diagram,
self.options.output, fontmap=self.fontmap,
code=self.code, antialias=self.options.antialias,
- ignore_pil=self.options.ignore_pil,
nodoctype=self.options.nodoctype,
transparency=self.options.transparency)
drawer.draw()
@@ -107,8 +90,8 @@ class Options(object):
self.module = module
self.build_parser()
- def parse(self):
- self.options, self.args = self.parser.parse_args()
+ def parse(self, args):
+ self.options, self.args = self.parser.parse_args(args)
self.validate()
self.read_configfile()
@@ -131,8 +114,7 @@ class Options(object):
p.add_option('--fontmap',
help='use FONTMAP file to draw diagram', metavar='FONT')
p.add_option('--ignore-pil', dest='ignore_pil',
- default=False, action='store_true',
- help='do not use PIL module forcely (SVG only)')
+ default=False, action='store_true', help=SUPPRESS_HELP)
p.add_option('--no-transparency', dest='transparency',
default=True, action='store_false',
help='do not make transparent background of diagram ' +
@@ -184,9 +166,10 @@ class Options(object):
msg = "could not output PDF format; Install reportlab."
raise RuntimeError(msg)
- if self.options.ignore_pil and self.options.type != 'SVG':
- msg = "--ignore-pil option work in SVG images."
- raise RuntimeError(msg)
+ if self.options.ignore_pil:
+ msg = "WARNING: --ignore-pil option is deprecated " + \
+ "(detect automatically).\n"
+ sys.stderr.write(msg)
if self.options.nodoctype and self.options.type != 'SVG':
msg = "--nodoctype option work in SVG images."
@@ -253,6 +236,7 @@ def detectfont(options):
'AppleGothic.ttf',
'msgothic.ttf',
'msgoth04.ttf',
+ 'msgothic.ttc',
]
fontpath = None
diff --git a/src/blockdiag/utils/collections.py b/src/blockdiag/utils/collections.py
deleted file mode 100644
index 89bfee9..0000000
--- a/src/blockdiag/utils/collections.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright 2011 Takeshi KOMIYA
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-class defaultdict(dict):
- def __init__(self, default_factory, *args, **kwargs):
- self.default_factory = default_factory
-
- def __getitem__(self, key):
- try:
- return super(defaultdict, self).__getitem__(key)
- except:
- return self.default_factory()
-
-
-def namedtuple(name, fields):
- 'Only space-delimited fields are supported.'
- def prop(i, name):
- return (name, property(lambda self: self[i]))
- methods = dict(prop(i, f) for i, f in enumerate(fields.split(' ')))
- methods.update({
- '__new__': lambda cls, *args: tuple.__new__(cls, args),
- '__repr__': lambda self: '%s(%s)' % (
- name,
- ', '.join('%s=%r' % (
- f, getattr(self, f)) for f in fields.split(' ')))})
- return type(name, (tuple,), methods)
diff --git a/src/blockdiag/utils/compat.py b/src/blockdiag/utils/compat.py
new file mode 100644
index 0000000..fb7d85a
--- /dev/null
+++ b/src/blockdiag/utils/compat.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+# Copyright 2011 Takeshi KOMIYA
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sys
+
+if sys.version_info[0] == 2:
+ string_types = (str, unicode) # NOQA: pyflakes complains to unicode in py3
+else:
+ string_types = (str,)
+
+
+def u(string):
+ if sys.version_info[0] == 2:
+ return unicode(string, "unicode_escape") # NOQA: pyflakes complains to unicode in py3
+ else:
+ return string
+
+
+def cmp_to_key(mycmp):
+ """Convert a cmp= function into a key= function"""
+ class K(object):
+ __slots__ = ['obj']
+
+ def __init__(self, obj, *args):
+ self.obj = obj
+
+ def __lt__(self, other):
+ return mycmp(self.obj, other.obj) < 0
+
+ def __gt__(self, other):
+ return mycmp(self.obj, other.obj) > 0
+
+ def __eq__(self, other):
+ return mycmp(self.obj, other.obj) == 0
+
+ def __le__(self, other):
+ return mycmp(self.obj, other.obj) <= 0
+
+ def __ge__(self, other):
+ return mycmp(self.obj, other.obj) >= 0
+
+ def __ne__(self, other):
+ return mycmp(self.obj, other.obj) != 0
+
+ def __hash__(self):
+ raise TypeError('hash not implemented')
+
+ return K
diff --git a/src/blockdiag/utils/config.py b/src/blockdiag/utils/config.py
index d404780..142bd17 100644
--- a/src/blockdiag/utils/config.py
+++ b/src/blockdiag/utils/config.py
@@ -13,9 +13,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import io
import sys
-import codecs
-from ConfigParser import SafeConfigParser
+try:
+ from configparser import SafeConfigParser
+except ImportError:
+ from ConfigParser import SafeConfigParser
class ConfigParser(SafeConfigParser):
@@ -30,10 +33,6 @@ class ConfigParser(SafeConfigParser):
SafeConfigParser.__init__(self)
def read(self, path):
- if sys.version_info > (2, 5):
- fd = codecs.open(path, 'r', 'utf-8-sig')
- else:
- fd = codecs.open(path, 'r', 'utf-8')
-
+ fd = io.open(path, 'r', encoding='utf-8-sig')
self.readfp(fd)
fd.close()
diff --git a/src/blockdiag/utils/fontmap.py b/src/blockdiag/utils/fontmap.py
index 48133f7..2264283 100644
--- a/src/blockdiag/utils/fontmap.py
+++ b/src/blockdiag/utils/fontmap.py
@@ -17,8 +17,9 @@ import re
import os
import sys
import copy
+from collections import namedtuple
from blockdiag.utils.config import ConfigParser
-from blockdiag.utils.collections import namedtuple
+from blockdiag.utils.compat import u
def parse_fontpath(path):
@@ -151,7 +152,7 @@ class FontMap(object):
font.size = fontsize
elif element is not None:
msg = "Unknown fontfamily: %s" % fontfamily
- sys.stderr.write("WARNING: %s\n" % msg)
+ sys.stderr.write(u("WARNING: %s\n") % msg)
elem = namedtuple('Font', 'fontsize')(fontsize)
font = self.find(elem)
else:
diff --git a/src/blockdiag/utils/functools.py b/src/blockdiag/utils/functools.py
deleted file mode 100644
index 6d078da..0000000
--- a/src/blockdiag/utils/functools.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright 2011 Takeshi KOMIYA
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-def partial(func, *args, **keywords):
- def newfunc(*fargs, **fkeywords):
- newkeywords = keywords.copy()
- newkeywords.update(fkeywords)
- return func(*(args + fargs), **newkeywords)
- newfunc.func = func
- newfunc.args = args
- newfunc.keywords = keywords
- return newfunc
-
-
-def wraps(src):
- def newfunc(dest):
- dest.__module__ = src.__module__
- dest.__name__ = src.__name__
- dest.__doc__ = src.__doc__
- d = getattr(src, '__dict__', {})
- if hasattr(dest, '__dict__'):
- dest.__dict__.update(d)
- else:
- dest.__dict__ = d.copy()
- return dest
- return newfunc
diff --git a/src/blockdiag/utils/images.py b/src/blockdiag/utils/images.py
index 9ad96a4..6cc4b92 100644
--- a/src/blockdiag/utils/images.py
+++ b/src/blockdiag/utils/images.py
@@ -13,46 +13,45 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from __future__ import division
import re
from blockdiag.utils import urlutil
+from blockdiag.utils.compat import string_types
try:
from PIL import Image
except ImportError:
- try:
- import Image
- except ImportError:
- class Image:
- @classmethod
- def open(cls, filename):
- return cls(filename)
-
- def __init__(self, filename):
- self.filename = filename
-
- @property
- def size(self):
- from blockdiag.utils import jpeg
- import png
+ class Image:
+ @classmethod
+ def open(cls, filename):
+ return cls(filename)
+ def __init__(self, filename):
+ self.filename = filename
+
+ @property
+ def size(self):
+ from blockdiag.utils import jpeg
+ import png
+
+ try:
+ size = jpeg.JpegFile.get_size(self.filename)
+ except:
try:
- size = jpeg.JpegFile.get_size(self.filename)
+ if isinstance(self.filename, string_types):
+ content = open(self.filename, 'r')
+ else:
+ self.filename.seek(0)
+ content = self.filename
+ image = png.Reader(file=content).read()
+ size = (image[0], image[1])
except:
- try:
- if isinstance(self.filename, (str, unicode)):
- content = open(self.filename, 'r')
- else:
- self.filename.seek(0)
- content = self.filename
- image = png.Reader(file=content).read()
- size = (image[0], image[1])
- except:
- size = None
-
- if hasattr(self.filename, 'seek'):
- self.filename.seek(0)
-
- return size
+ size = None
+
+ if hasattr(self.filename, 'seek'):
+ self.filename.seek(0)
+
+ return size
_image_size_cache = {}
@@ -61,10 +60,13 @@ def get_image_size(filename):
if filename not in _image_size_cache:
uri = filename
if urlutil.isurl(filename):
- import cStringIO
+ try:
+ from io import StringIO
+ except ImportError:
+ from cStringIO import StringIO
import urllib
try:
- uri = cStringIO.StringIO(urllib.urlopen(filename).read())
+ uri = StringIO(urllib.urlopen(filename).read())
except:
return None
@@ -75,10 +77,10 @@ def get_image_size(filename):
def calc_image_size(size, bounded):
if bounded[0] < size[0] or bounded[1] < size[1]:
- if (size[0] * 1.0 / bounded[0]) < (size[1] * 1.0 / bounded[1]):
- size = (size[0] * bounded[1] / size[1], bounded[1])
+ if (size[0] * 1.0 // bounded[0]) < (size[1] * 1.0 // bounded[1]):
+ size = (size[0] * bounded[1] // size[1], bounded[1])
else:
- size = (bounded[0], size[1] * bounded[0] / size[0])
+ size = (bounded[0], size[1] * bounded[0] // size[0])
return size
diff --git a/src/blockdiag/utils/jpeg.py b/src/blockdiag/utils/jpeg.py
index 1c482cf..85345f4 100644
--- a/src/blockdiag/utils/jpeg.py
+++ b/src/blockdiag/utils/jpeg.py
@@ -14,6 +14,9 @@
# limitations under the License.
+from blockdiag.utils.compat import string_types
+
+
class StreamReader(object):
def __init__(self, stream):
self.stream = stream
@@ -74,7 +77,7 @@ class JpegFile(object):
@classmethod
def get_size(self, filename):
- if isinstance(filename, (str, unicode)):
+ if isinstance(filename, string_types):
image = open(filename, 'rb').read()
else:
image = filename.read()
diff --git a/src/blockdiag/utils/myitertools.py b/src/blockdiag/utils/myitertools.py
index f67ca4d..364cc5c 100644
--- a/src/blockdiag/utils/myitertools.py
+++ b/src/blockdiag/utils/myitertools.py
@@ -19,7 +19,7 @@ from itertools import cycle
def istep(seq, step=2):
iterable = iter(seq)
while True:
- yield [iterable.next() for _ in range(step)]
+ yield [next(iterable) for _ in range(step)]
def stepslice(iterable, steps):
@@ -28,19 +28,19 @@ def stepslice(iterable, steps):
while True:
# skip (1)
- n = step.next()
+ n = next(step)
if n == 0:
pass
elif n == 1:
- o = iterable.next()
+ o = next(iterable)
yield o
yield o
else:
- yield iterable.next()
- for _ in xrange(n - 2):
- iterable.next()
- yield iterable.next()
+ yield next(iterable)
+ for _ in range(n - 2):
+ next(iterable)
+ yield next(iterable)
# skip (2)
- for _ in xrange(step.next()):
- iterable.next()
+ for _ in range(next(step)):
+ next(iterable)
diff --git a/src/blockdiag/utils/namedtuple.py b/src/blockdiag/utils/namedtuple.py
deleted file mode 100644
index ec1fe3f..0000000
--- a/src/blockdiag/utils/namedtuple.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright 2011 Takeshi KOMIYA
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-from blockdiag.utils.collections import namedtuple
-namedtuple
diff --git a/src/blockdiag/utils/rst/directives.py b/src/blockdiag/utils/rst/directives.py
index 77d07db..18f116b 100644
--- a/src/blockdiag/utils/rst/directives.py
+++ b/src/blockdiag/utils/rst/directives.py
@@ -14,26 +14,25 @@
# limitations under the License.
import os
-import codecs
+import io
+from collections import namedtuple
from docutils import nodes
from docutils.parsers import rst
from docutils.statemachine import ViewList
from blockdiag import parser
from blockdiag.builder import ScreenNodeBuilder
from blockdiag.drawer import DiagramDraw
-from blockdiag.utils.bootstrap import detectfont
-from blockdiag.utils.collections import namedtuple
+from blockdiag.utils.bootstrap import create_fontmap
+from blockdiag.utils.compat import string_types
from blockdiag.utils.rst.nodes import blockdiag
-
directive_options_default = dict(format='PNG',
antialias=False,
fontpath=None,
outputdir=None,
nodoctype=False,
noviewbox=False,
- inline_svg=False,
- ignore_pil=False)
+ inline_svg=False)
directive_options = {}
@@ -47,20 +46,6 @@ def relfn2path(env, filename):
return relfn, os.path.join(env.srcdir, relfn)
-def cmp_node_number(a, b):
- try:
- n1 = int(a[0])
- except (TypeError, ValueError):
- n1 = 65535
-
- try:
- n2 = int(b[0])
- except (TypeError, ValueError):
- n2 = 65535
-
- return cmp(n1, n2)
-
-
class BlockdiagDirectiveBase(rst.Directive):
""" Directive to insert arbitrary dot markup. """
name = "blockdiag"
@@ -87,7 +72,7 @@ class BlockdiagDirectiveBase(rst.Directive):
try:
filename = self.source_filename(self.arguments[0])
- fp = codecs.open(filename, 'r', 'utf-8')
+ fp = io.open(filename, 'r', encoding='utf-8-sig')
try:
dotcode = fp.read()
finally:
@@ -136,7 +121,7 @@ class BlockdiagDirective(BlockdiagDirectiveBase):
try:
diagram = self.node2diagram(node)
- except Exception, e:
+ except Exception as e:
raise self.warning(e.message)
if 'desctable' in node['options']:
@@ -164,7 +149,7 @@ class BlockdiagDirective(BlockdiagDirectiveBase):
def node2image(self, node, diagram):
options = node['options']
filename = self.image_filename(node)
- fontpath = self.detectfont()
+ fontmap = self.create_fontmap()
_format = self.global_options['format'].lower()
if _format == 'svg' and self.global_options['inline_svg'] is True:
@@ -172,7 +157,8 @@ class BlockdiagDirective(BlockdiagDirectiveBase):
kwargs = dict(self.global_options)
del kwargs['format']
- drawer = DiagramDraw(_format, diagram, filename, **kwargs)
+ drawer = DiagramDraw(_format, diagram, filename,
+ fontmap=fontmap, **kwargs)
if filename is None or not os.path.isfile(filename):
drawer.draw()
@@ -185,7 +171,7 @@ class BlockdiagDirective(BlockdiagDirectiveBase):
new_size = (options['maxwidth'], int(size[1] * ratio))
content = drawer.save(new_size)
- return nodes.raw('', content.decode('utf-8'), format='html')
+ return nodes.raw('', content, format='html')
size = drawer.pagesize()
if 'maxwidth' in options and options['maxwidth'] < size[0]:
@@ -207,17 +193,17 @@ class BlockdiagDirective(BlockdiagDirectiveBase):
return image
- def detectfont(self):
- Options = namedtuple('Options', 'font')
+ def create_fontmap(self):
+ Options = namedtuple('Options', 'font fontmap')
fontpath = self.global_options['fontpath']
if isinstance(fontpath, (list, tuple)):
- options = Options(fontpath)
- elif isinstance(fontpath, (str, unicode)):
- options = Options([fontpath])
+ options = Options(fontpath, None)
+ elif isinstance(fontpath, string_types):
+ options = Options([fontpath], None)
else:
- options = Options([])
+ options = Options([], None)
- return detectfont(options)
+ return create_fontmap(options)
def image_filename(self, node, prefix='', ext='png'):
try:
@@ -230,7 +216,7 @@ class BlockdiagDirective(BlockdiagDirectiveBase):
options = dict(node['options'])
options.update(font=self.global_options['fontpath'],
antialias=self.global_options['antialias'])
- hashseed = node['code'].encode('utf-8') + str(options)
+ hashseed = (node['code'] + str(options)).encode('utf-8')
hashed = sha(hashseed).hexdigest()
_format = self.global_options['format']
@@ -260,8 +246,14 @@ class BlockdiagDirective(BlockdiagDirectiveBase):
widths = [25] + [50] * (len(klass.desctable) - 1)
headers = [klass.attrname[n] for n in klass.desctable]
+ def node_number(node):
+ try:
+ return int(node[0])
+ except (TypeError, ValueError):
+ return 65535
+
descriptions = [n.to_desctable() for n in nodes if n.drawable]
- descriptions.sort(cmp_node_number)
+ descriptions.sort(key=node_number)
for i in reversed(range(len(headers))):
if any(desc[i] for desc in descriptions):
@@ -313,7 +305,7 @@ class BlockdiagDirective(BlockdiagDirectiveBase):
row = nodes.row()
for attr in desc:
entry = nodes.entry()
- if not isinstance(attr, (str, unicode)):
+ if not isinstance(attr, string_types):
attr = str(attr)
self.state.nested_parse(ViewList([attr], source=attr),
0, entry)
diff --git a/src/blockdiag/utils/urlutil.py b/src/blockdiag/utils/urlutil.py
index a1a061e..6d18c45 100644
--- a/src/blockdiag/utils/urlutil.py
+++ b/src/blockdiag/utils/urlutil.py
@@ -1,6 +1,9 @@
# -*- coding: utf-8 -*-
-import urlparse
+try:
+ import urlparse
+except ImportError:
+ import urllib.parse as urlparse
def isurl(url):
diff --git a/src/blockdiag/utils/uuid.py b/src/blockdiag/utils/uuid.py
index 26e28b1..feb93ee 100644
--- a/src/blockdiag/utils/uuid.py
+++ b/src/blockdiag/utils/uuid.py
@@ -20,6 +20,8 @@ except ImportError:
from random import random
uuid = random
+from blockdiag.utils.compat import u
+
def generate():
- return str(uuid())
+ return u(str(uuid()))
diff --git a/src/blockdiag_sphinxhelper.py b/src/blockdiag_sphinxhelper.py
index 5e9cc99..cb250d3 100644
--- a/src/blockdiag_sphinxhelper.py
+++ b/src/blockdiag_sphinxhelper.py
@@ -13,24 +13,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+__all__ = [
+ 'core', 'utils'
+]
+
import blockdiag.parser
import blockdiag.builder
import blockdiag.drawer
core = blockdiag
import blockdiag.utils.bootstrap
-import blockdiag.utils.collections
+import blockdiag.utils.compat
import blockdiag.utils.fontmap
+import blockdiag.utils.rst.nodes
+import blockdiag.utils.rst.directives
utils = blockdiag.utils
-
-from blockdiag.utils.rst import nodes
-from blockdiag.utils.rst import directives
-
-# FIXME: obsoleted interface (keep for compatibility)
-from blockdiag import command, parser, builder, drawer
-from blockdiag.utils import collections
-from blockdiag.utils.fontmap import FontMap
-from blockdiag.utils.rst.directives import blockdiag, BlockdiagDirective
-
-(command, parser, builder, drawer, collections,
- FontMap, blockdiag, BlockdiagDirective)
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..ad483b6
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,20 @@
+[tox]
+envlist=py26,py27,py32,py33
+
+[testenv]
+deps=
+ nose
+ pep8
+ flake8
+ docutils
+commands=
+ nosetests
+ flake8 src
+
+[testenv:py26]
+deps=
+ nose
+ pep8
+ flake8
+ docutils
+ unittest2
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/blockdiag.git
More information about the debian-science-commits
mailing list