[blockdiag] 26/29: Import Upstream version 1.5.3+dfsg

Andreas Tille tille at debian.org
Tue Jan 10 21:35:59 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 1e929f702690e86af98130d35d0a843b8e17f9ae
Author: Andreas Tille <tille at debian.org>
Date:   Tue Jan 10 11:08:07 2017 +0100

    Import Upstream version 1.5.3+dfsg
---
 .hgignore                                          |  14 +++
 .hgtags                                            | 110 +++++++++++++++++++++
 .installed.cfg                                     |  62 ++++++++++++
 CHANGES.rst                                        |  31 ++++++
 MANIFEST.in                                        |   1 +
 PKG-INFO                                           |   2 +-
 a.diag                                             |   6 ++
 b.diag                                             |   8 ++
 b.png                                              | Bin 0 -> 7778 bytes
 blockdiag.svg                                      |  46 +++++++++
 bootstrap.pyc                                      | Bin 0 -> 4410 bytes
 c.diag                                             |   3 +
 e.diag                                             |   5 +
 setup.py                                           |   9 +-
 src/blockdiag.egg-info/PKG-INFO                    |   2 +-
 src/blockdiag.egg-info/SOURCES.txt                 |  18 +++-
 src/blockdiag.egg-info/requires.txt                |   2 +-
 src/blockdiag/__init__.py                          |   2 +-
 src/blockdiag/imagedraw/filters/linejump.py        |  56 ++++++-----
 src/blockdiag/imagedraw/svg.py                     |   7 +-
 src/blockdiag/plugins/__init__.py                  |  25 +++--
 src/blockdiag/tests/__init__.py                    |   1 +
 src/blockdiag/tests/rst/__init__.py                |   1 +
 src/blockdiag/tests/rst/test_base_directives.py    |  17 ++--
 .../tests/rst/test_blockdiag_directives.py         |  30 ++++--
 src/blockdiag/tests/test_boot_params.py            |  23 ++---
 src/blockdiag/tests/test_command.py                |  67 +++++++++++++
 src/blockdiag/tests/test_generate_diagram.py       |  70 ++++++++-----
 src/blockdiag/tests/test_imagedraw_textfolder.py   |  10 +-
 src/blockdiag/tests/test_imagedraw_utils.py        |  10 +-
 src/blockdiag/tests/test_parser.py                 |   6 +-
 src/blockdiag/tests/test_pep8.py                   |  53 ----------
 src/blockdiag/tests/test_utils.py                  |  10 +-
 src/blockdiag/tests/test_utils_fontmap.py          |  41 ++++----
 src/blockdiag/tests/utils.py                       |  12 +--
 src/blockdiag/utils/__init__.py                    |  13 +--
 src/blockdiag/utils/bootstrap.py                   |  31 +++++-
 src/blockdiag/utils/compat.py                      |   2 +-
 src/blockdiag/utils/fontmap.py                     |  16 ++-
 src/blockdiag/utils/images.py                      |  52 +++++++---
 src/blockdiag/utils/rst/directives.py              |  13 ++-
 src/blockdiag_sphinxhelper.py                      |  11 ++-
 test.sh                                            |   4 +
 tox.ini                                            |  20 +++-
 44 files changed, 698 insertions(+), 224 deletions(-)

diff --git a/.hgignore b/.hgignore
new file mode 100644
index 0000000..d531664
--- /dev/null
+++ b/.hgignore
@@ -0,0 +1,14 @@
+.pyc
+.swp
+^.installed.cfg
+^.coverage
+^.tox
+^bin/
+^build/
+^_build/
+^develop-eggs/
+^dist/
+^eggs/
+^parts/
+^src/.*.egg-info
+^src/blockdiag/tests/truetype
diff --git a/.hgtags b/.hgtags
new file mode 100644
index 0000000..69519ff
--- /dev/null
+++ b/.hgtags
@@ -0,0 +1,110 @@
+f13b2f2d914399eae8bf7b4b5105a69eb7e657ca 0.1
+af0e42d164178abd7f849b2d5659fe01b7eed69c 0.2
+211617855c50a76c0226ecb83d659ba77323c0d9 0.2.2
+0e0308059b7d67b2d7c2bd0a3e6989dcce6c7129 0.3
+183f989ca4bb9e6ac44dacf588312f77e4b715ea 0.3.1
+c799939494fcf8adefeb4e129c111af8253519c3 0.4
+3de87d6c2c78240db3b3f800f551169e3360c16a 0.4.1
+7070f6188d7278ef26ca4e6f2fa1a1faac8853c3 0.2.1
+6dfea7499697f839240ab49ffad1000e34bcf834 0.4.2
+7a86de04a5247bc775a956e252949c04753b1449 0.5
+c86ba6300a6f00cb490f952c321c7596ba54dfaa 0.5.1
+b73c44fbe87dd5c478dbe5c861d6833381022865 0.5.2
+200c7a578e3c98db5876b4db225ec3a69cb28b5b 0.5.3
+45835d84017e5da4d7618d2eaaab98ba9d575572 0.5.4
+92e1ffe35380687329273214b2f4676a5c960ed8 0.5.5
+d6af195862867049b81021ae359668f405fdb4f5 0.6
+52ed26c213248b738ddb1b8c7f97288f087c330a 0.6.1
+52ed26c213248b738ddb1b8c7f97288f087c330a 0.6.1
+0000000000000000000000000000000000000000 0.6.1
+0000000000000000000000000000000000000000 0.6.1
+63bc462fbc487ed1f1c55c2ab9e101bd97f87d99 0.6.1
+6061c7f1706c1164cc34da48c19e543c56be007c 0.6.2
+84e23355450870f51a35e2931ef80f31f1f130fb 0.6.3
+87382e9a446bc06adbb57c52530ebcee10749738 0.6.4
+4634b17e273837c2a2d6a8effaf7fd0343e226cf 0.6.5
+f2d6cbbd62619c556afe40fa17f7da314e3d66a0 0.6.6
+663a52ef768c7725984db74fd9064fd2bca0263c 0.6.7
+01ccc2e285e57e3df3656d21eb86ea3e2c4de6e7 0.7.0
+123ed155fd4e933dfb5182af6a4bb67b776f06e3 0.7.1
+2d6ef5898a4c8c127e3ca256263597dbef0192ec 0.7.2
+93e52fb89351e2b40b2058829a41157d72a6b4fe 0.7.3
+afa7e91236bebf58d9c2081b328a8f6a209158a5 0.7.4
+dc056c5edf2fdb73c68ce55957e36442cf259bda 0.7.5
+9c9bc6e859f7e1bf2cbdf296cb98bdd43e177339 0.7.6
+f14667d477b861e146ca82974852591f343955b4 0.7.7
+6c6edf112349b3dcd525ecf805cf2d1ddce95923 0.7.8
+56d8d6c04be5c495d52479cd7273054652ea0f41 0.8.0
+5317b0bcec53c4f83badc21675255fa4f810c51b 0.8.1
+5317b0bcec53c4f83badc21675255fa4f810c51b 0.8.1
+0000000000000000000000000000000000000000 0.8.1
+0000000000000000000000000000000000000000 0.8.1
+4e291393b9818f9b744599753213b2f214418367 0.8.1
+1394f7cb6d74256841d52b65559a9181f38dc3c0 0.8.2
+1394f7cb6d74256841d52b65559a9181f38dc3c0 0.8.2
+0000000000000000000000000000000000000000 0.8.2
+0000000000000000000000000000000000000000 0.8.2
+a39c2fe87c56ecd3330fa966c16a84a719c078af 0.8.2
+dee693b247f9796ef5a71c9879e7ece81e2ca826 0.8.3
+e26b9e4dea0da3d3decaee7524add158cd8f9a89 0.8.4
+3ee05b403cb89fccf29f8225238119322d574343 0.8.5
+66503507bf9021e9373a26963380c8daefe8b732 0.8.6
+58c589bf671872eb14cb8a97572cd54f4451363d 0.8.7
+a5ae7cf75f7932c938e66df154f766d530ec9a10 0.8.8
+aeb61e8fad968a0a252e7be969e558b8058087ac 0.8.9
+9547a2a4d61e3d136913ba2fd281f568b06c26f1 0.9.0
+b8fcad4288f703ce4fe68c0f66cde6f6cfda14ea 0.9.1
+4100c1b16943477421d72dbcc47f17636cf3cd07 0.9.2
+71e676fb5eb2287ac8d864be6e3f6a1ce404e66e 0.9.3
+f0c827676fda31eec4570b901bc9a4e8094f1f6e 0.9.5
+f0c827676fda31eec4570b901bc9a4e8094f1f6e 0.9.5
+0000000000000000000000000000000000000000 0.9.5
+0000000000000000000000000000000000000000 0.9.5
+9c07fdfc24682a8a935c139113f20c60a1ded6cd 0.9.5
+9c07fdfc24682a8a935c139113f20c60a1ded6cd 0.9.5
+0000000000000000000000000000000000000000 0.9.5
+0000000000000000000000000000000000000000 0.9.5
+989c2c9e33af4c1d7033738a0e3346e2d1fe78aa 0.9.5
+a110a4a98ad14ed551539d2ce9bf022e7a65099c 0.9.6
+00957a44f847d8582b6be748ae47e613be57c5b6 0.9.7
+91f704283be06c84cfbe614be97cd43c3f342845 1.0.1
+1d6039743bd6f7056293432556cea030fbf921fc 1.0.2
+e7a0149a88f05fc2064d37f948252397a8cd945f 1.0.0
+84d6892aa674d64808d30139617550a96c50054a 1.0.3
+e180448fb931f2cc5f0c3876b7e9fe050ffdcb74 1.1.0
+bbc158edfa84fc78e694d19a7fd570127e098a82 1.1.1
+f7fc3127b629accae4b82ec69d7b13016b7093b5 1.1.2
+28eb38df230448b99bf11909f30232f9028e44c3 1.1.3
+28eb38df230448b99bf11909f30232f9028e44c3 1.1.3
+0000000000000000000000000000000000000000 1.1.3
+0000000000000000000000000000000000000000 1.1.3
+caa5f3cae7574d94efd86da6a7cba5e614ef905e 1.1.3
+dc9debc39155dad8e6bb18ead11d9af876170bc6 1.1.4
+f2faaa5683f7cf3e3ffb8927ad2a9a11ef9b62e5 1.1.5
+b57b099723600a0eb289220ffadc6b52ab3aca55 1.1.6
+ddff661e8fd9fab186382c5629032d918bfe5b48 1.1.7
+0c6e79e5491ca681ab1eb74c8b6bf84b9f7db114 1.1.8
+2a028e8248876b143d02e4113cf0e6aa45ec49da 1.2.0
+5f30defbfa6425144d9f41be9dae4e6fdd5cb1f8 1.2.1
+a58111e3769fbefea485fe78c136b1bf083566dd 1.2.2
+a58111e3769fbefea485fe78c136b1bf083566dd 1.2.2
+0000000000000000000000000000000000000000 1.2.2
+0000000000000000000000000000000000000000 1.2.2
+1e7de1d0e4656230f0b367f4a76e80e40f20747f 1.2.2
+b7204023d673853f16687412f0758d66f669fe6e 1.2.3
+924f66da959d9e6ac1f4831cf8a9fbb18a57fb8a 1.2.4
+eea92cc34dd8702eb368fb274de87b4b9bfde8c9 1.3.0
+5181da3c729559101982dee60cf2bac4098913b7 1.3.1
+eaeb1c38be3dfc0ae6e996b38437e8bdf9799ea2 1.3.2
+372e5f369ba92ed5efa40139393dea58cdf475d9 1.3.3
+4746d577dcfbe02ba535d70912236b10723a52f8 1.4.0
+41e73287a1d6e019dd1f6940d184f05adcd07b33 1.4.1
+eda0e6b41fdf9939d0d160fbdf2dfce5e7f3135e 1.4.2
+b7481dd07fe9b8f815eb6989c53b2ab7dc490f50 1.4.3
+fdf8d4f28170b81b237afdb787b81c13a0d8827a 1.4.4
+dc4a431c445e57411e8800577afa761ee4e476f0 1.4.5
+8b62ba06c865d32b6c02e185fb4ebe6478322d41 1.4.6
+bb9af150c55e8497c75f93250774d64dc7798bd9 1.4.7
+b67e529c41e7fac9796c6635e7a5b801f98a4de7 1.5.0
+1f35ac7a1e98fcdf8c74164ce721d5ab725f003a 1.5.1
+90c65044ba04b5a305980f062c83a481bf7253cf 1.5.2
diff --git a/.installed.cfg b/.installed.cfg
new file mode 100644
index 0000000..49d4d3d
--- /dev/null
+++ b/.installed.cfg
@@ -0,0 +1,62 @@
+[buildout]
+installed_develop_eggs = /Users/tkomiya/work/blockdiag/develop-eggs/blockdiag.egg-link
+parts = blockdiag test static_analysis
+
+[blockdiag]
+__buildout_installed__ = /Users/tkomiya/work/blockdiag/bin/blockdiag
+	/Users/tkomiya/work/blockdiag/bin/py
+__buildout_signature__ = zc.recipe.egg-2.0.1-py2.7.egg setuptools-e254772190d6726b0025d350d2b623f9 zc.buildout-2.3.1-py2.7.egg
+_b = /Users/tkomiya/work/blockdiag/bin
+_d = /Users/tkomiya/work/blockdiag/develop-eggs
+_e = /Users/tkomiya/.buildout/buildout-eggs
+bin-directory = /Users/tkomiya/work/blockdiag/bin
+develop-eggs-directory = /Users/tkomiya/work/blockdiag/develop-eggs
+eggs = blockdiag[rst]
+eggs-directory = /Users/tkomiya/.buildout/buildout-eggs
+interpreter = py
+recipe = zc.recipe.egg
+
+[test]
+__buildout_installed__ = /Users/tkomiya/work/blockdiag/bin/test
+__buildout_signature__ = pbp.recipe.noserunner-0.2.6-py2.7.egg zc.recipe.egg-2.0.1-py2.7.egg nose-1.3.4-py2.7.egg zc.buildout-2.3.1-py2.7.egg setuptools-e254772190d6726b0025d350d2b623f9
+_b = /Users/tkomiya/work/blockdiag/bin
+_d = /Users/tkomiya/work/blockdiag/develop-eggs
+_e = /Users/tkomiya/.buildout/buildout-eggs
+bin-directory = /Users/tkomiya/work/blockdiag/bin
+develop-eggs-directory = /Users/tkomiya/work/blockdiag/develop-eggs
+eggs = blockdiag[rst]
+	blockdiag[testing]
+	coverage
+	unittest-xml-reporting
+	nose
+eggs-directory = /Users/tkomiya/.buildout/buildout-eggs
+location = /Users/tkomiya/work/blockdiag/parts/test
+recipe = pbp.recipe.noserunner
+script = /Users/tkomiya/work/blockdiag/bin/test
+
+[static_analysis]
+__buildout_installed__ = /Users/tkomiya/work/blockdiag/bin/coverage2
+	/Users/tkomiya/work/blockdiag/bin/coverage-2.7
+	/Users/tkomiya/work/blockdiag/bin/coverage
+	/Users/tkomiya/work/blockdiag/bin/flake8
+	/Users/tkomiya/work/blockdiag/bin/pyreverse
+	/Users/tkomiya/work/blockdiag/bin/pylint
+	/Users/tkomiya/work/blockdiag/bin/epylint
+	/Users/tkomiya/work/blockdiag/bin/pylint-gui
+	/Users/tkomiya/work/blockdiag/bin/symilar
+	/Users/tkomiya/work/blockdiag/bin/epylint
+	/Users/tkomiya/work/blockdiag/bin/pylint
+	/Users/tkomiya/work/blockdiag/bin/pylint-gui
+	/Users/tkomiya/work/blockdiag/bin/pyreverse
+	/Users/tkomiya/work/blockdiag/bin/symilar
+__buildout_signature__ = zc.recipe.egg-2.0.1-py2.7.egg setuptools-e254772190d6726b0025d350d2b623f9 zc.buildout-2.3.1-py2.7.egg
+_b = /Users/tkomiya/work/blockdiag/bin
+_d = /Users/tkomiya/work/blockdiag/develop-eggs
+_e = /Users/tkomiya/.buildout/buildout-eggs
+bin-directory = /Users/tkomiya/work/blockdiag/bin
+develop-eggs-directory = /Users/tkomiya/work/blockdiag/develop-eggs
+eggs = coverage
+	flake8
+	pylint
+eggs-directory = /Users/tkomiya/.buildout/buildout-eggs
+recipe = zc.recipe.egg
diff --git a/CHANGES.rst b/CHANGES.rst
index db39cac..03e4c41 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,6 +1,37 @@
 Changelog
 =========
 
+1.5.3 (2015-07-30)
+------------------
+* Fix bug
+
+  - Fix #67 Group overlaps with nodes having href
+
+1.5.2 (2015-05-17)
+------------------
+* Fix dependency; webcolors-1.5 does not support py32
+* Fix bug
+
+  - Fix images.open() failed with PIL
+
+1.5.1 (2015-02-21)
+------------------
+* Fix bug
+
+  - Fix labels are overwrapped on antialias mode
+
+1.5.0 (2015-01-01)
+------------------
+* Refactor cleanup procedures
+* Fix bugs
+
+  - Fix get_image_size() closes Image object automatically
+  - Fix pasting Image object failed on SVG mode
+  - Fix #61 images.urlopen() got PermissionError on Windows
+  - Fix #61 images.open() raises exception if ghostscript not found
+  - Fix #62 Use "sans-serif" to font-family property on SVG output ("sansserif" is invalid)
+  - Fix #63 AttributeError on get_image_size(); Pillow (<= 2.4.x) does not have Image#close()
+
 1.4.7 (2014-10-21)
 ------------------
 * Fix bugs
diff --git a/MANIFEST.in b/MANIFEST.in
index 01212f6..73d0f29 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -5,6 +5,7 @@ include MANIFEST.in
 include LICENSE
 include blockdiag.1
 include tox.ini
+include src/blockdiag/tests/VLGothic/*
 recursive-include examples blockdiagrc *.diag *.png *.svg
 recursive-include src README *.py *.diag *.gif *.png
 
diff --git a/PKG-INFO b/PKG-INFO
index e40f1f6..9961b0d 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: blockdiag
-Version: 1.4.7
+Version: 1.5.3
 Summary: blockdiag generates block-diagram image from text
 Home-page: http://blockdiag.com/
 Author: Takeshi Komiya
diff --git a/a.diag b/a.diag
new file mode 100644
index 0000000..d648088
--- /dev/null
+++ b/a.diag
@@ -0,0 +1,6 @@
+{
+  A -> B;
+  C [width = 200, shape = roundedbox];
+  A [label = "hello\n\nwolrd hello world hello world"];
+  A;
+}
diff --git a/b.diag b/b.diag
new file mode 100644
index 0000000..66aa5cd
--- /dev/null
+++ b/b.diag
@@ -0,0 +1,8 @@
+{
+  A -> B -> C;
+  D -> E;
+
+  A, B [shape = circle];
+  E [shape = square];
+  pgqywz
+}
diff --git a/b.png b/b.png
new file mode 100644
index 0000000..e5fc122
Binary files /dev/null and b/b.png differ
diff --git a/blockdiag.svg b/blockdiag.svg
new file mode 100644
index 0000000..f507e66
--- /dev/null
+++ b/blockdiag.svg
@@ -0,0 +1,46 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg viewBox="0 0 448 280" xmlns="http://www.w3.org/2000/svg" xmlns:inkspace="http://www.inkscape.org/namespaces/inkscape" xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs id="defs_block">
+    <filter height="1.504" id="filter_blur" inkspace:collect="always" width="1.1575" x="-0.07875" y="-0.252">
+      <feGaussianBlur id="feGaussianBlur3780" inkspace:collect="always" stdDeviation="4.2" />
+    </filter>
+  </defs>
+  <title>blockdiag</title>
+  <desc>blockdiag { default_node_color = lightyellow; default_group_color = red; default_linecolor = blue; default_textcolor = black;
+A[href="http://google.com"]; A[color = "lightgreen"]; A[numbered = 007];
+A -> B -> C; B -> D; group { A;C; } }
+</desc>
+  <rect fill="rgb(255,0,0)" height="140" style="filter:url(#filter_blur)" width="144" x="248" y="30" />
+  <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="67" y="46" />
+  <a xlink:href="http://google.com">
+    <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="259" y="46" />
+  </a>
+  <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="259" y="126" />
+  <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="259" y="206" />
+  <rect fill="rgb(255,255,224)" height="40" stroke="rgb(0,0,255)" width="128" x="64" y="40" />
+  <text fill="rgb(0,0,0)" font-family="sans-serif" font-size="11" font-style="normal" font-weight="normal" text-anchor="middle" textLength="8" x="128" y="66">B</text>
+  <a xlink:href="http://google.com">
+    <rect fill="rgb(144,238,144)" height="40" stroke="rgb(0,0,255)" width="128" x="256" y="40" />
+    <text fill="rgb(0,0,0)" font-family="sans-serif" font-size="11" font-style="normal" font-weight="normal" text-anchor="middle" textLength="9" x="320" y="65">A</text>
+    <ellipse cx="256" cy="40" fill="pink" rx="12" ry="12" stroke="rgb(0,0,255)" />
+    <text fill="rgb(0,0,0)" font-family="sans-serif" font-size="11" font-style="normal" font-weight="normal" text-anchor="middle" textLength="22" x="256" y="45">007</text>
+  </a>
+  <rect fill="rgb(255,255,224)" height="40" stroke="rgb(0,0,255)" width="128" x="256" y="120" />
+  <text fill="rgb(0,0,0)" font-family="sans-serif" font-size="11" font-style="normal" font-weight="normal" text-anchor="middle" textLength="9" x="320" y="145">C</text>
+  <rect fill="rgb(255,255,224)" height="40" stroke="rgb(0,0,255)" width="128" x="256" y="200" />
+  <text fill="rgb(0,0,0)" font-family="sans-serif" font-size="11" font-style="normal" font-weight="normal" text-anchor="middle" textLength="9" x="320" y="226">D</text>
+  <path d="M 192 60 L 224 60" fill="none" stroke="rgb(0,0,255)" />
+  <path d="M 224 60 L 224 140" fill="none" stroke="rgb(0,0,255)" />
+  <path d="M 224 140 L 248 140" fill="none" stroke="rgb(0,0,255)" />
+  <polygon fill="rgb(0,0,255)" points="255,140 248,136 248,144 255,140" stroke="rgb(0,0,255)" />
+  <path d="M 192 60 L 224 60" fill="none" stroke="rgb(0,0,255)" />
+  <path d="M 224 60 L 224 220" fill="none" stroke="rgb(0,0,255)" />
+  <path d="M 224 220 L 248 220" fill="none" stroke="rgb(0,0,255)" />
+  <polygon fill="rgb(0,0,255)" points="255,220 248,216 248,224 255,220" stroke="rgb(0,0,255)" />
+  <path d="M 384 60 L 400 60" fill="none" stroke="rgb(0,0,255)" />
+  <path d="M 400 60 L 400 25" fill="none" stroke="rgb(0,0,255)" />
+  <path d="M 128 25 L 400 25" fill="none" stroke="rgb(0,0,255)" />
+  <path d="M 128 25 L 128 32" fill="none" stroke="rgb(0,0,255)" />
+  <polygon fill="rgb(0,0,255)" points="128,39 124,32 132,32 128,39" stroke="rgb(0,0,255)" />
+</svg>
diff --git a/bootstrap.pyc b/bootstrap.pyc
new file mode 100644
index 0000000..1d5b6cc
Binary files /dev/null and b/bootstrap.pyc differ
diff --git a/c.diag b/c.diag
new file mode 100644
index 0000000..a6b2076
--- /dev/null
+++ b/c.diag
@@ -0,0 +1,3 @@
+{
+  A -> B -> C -> D, E -> F, G -> C;
+}
diff --git a/e.diag b/e.diag
new file mode 100644
index 0000000..2a5238d
--- /dev/null
+++ b/e.diag
@@ -0,0 +1,5 @@
+{
+  plugin autoclass;
+  default_shape = circle;
+  A -> B;
+}
diff --git a/setup.py b/setup.py
index 359b825..ec0cbb1 100644
--- a/setup.py
+++ b/setup.py
@@ -21,7 +21,6 @@ classifiers = [
 
 requires = ['setuptools',
             'funcparserlib',
-            'webcolors',
             'Pillow']
 pdf_requires = ['reportlab']
 test_requires = ['nose',
@@ -32,11 +31,17 @@ test_requires = ['nose',
 
 
 # only for Python2.6
-if sys.version_info > (2, 6) and sys.version_info < (2, 7):
+if (2, 6) < sys.version_info < (2, 7):
     requires.append('OrderedDict')
     pdf_requires[0] = 'reportlab < 3.0'
     test_requires.append('unittest2')
 
+# webcolors
+if (3, 2) < sys.version_info < (3, 3):
+    requires.append('webcolors < 1.5')  # webcolors-1.5 does not support py32
+else:
+    requires.append('webcolors')
+
 
 setup(
     name='blockdiag',
diff --git a/src/blockdiag.egg-info/PKG-INFO b/src/blockdiag.egg-info/PKG-INFO
index e40f1f6..9961b0d 100644
--- a/src/blockdiag.egg-info/PKG-INFO
+++ b/src/blockdiag.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: blockdiag
-Version: 1.4.7
+Version: 1.5.3
 Summary: blockdiag generates block-diagram image from text
 Home-page: http://blockdiag.com/
 Author: Takeshi Komiya
diff --git a/src/blockdiag.egg-info/SOURCES.txt b/src/blockdiag.egg-info/SOURCES.txt
index 436e95e..3cd88c4 100644
--- a/src/blockdiag.egg-info/SOURCES.txt
+++ b/src/blockdiag.egg-info/SOURCES.txt
@@ -1,12 +1,23 @@
+.hgignore
+.hgtags
+.installed.cfg
 CHANGES.rst
 LICENSE
 MANIFEST.in
 README.rst
+a.diag
+b.diag
+b.png
 blockdiag.1
+blockdiag.svg
 bootstrap.py
+bootstrap.pyc
 buildout.cfg
+c.diag
+e.diag
 setup.cfg
 setup.py
+test.sh
 tox.ini
 examples/blockdiagrc
 examples/group.diag
@@ -85,14 +96,19 @@ src/blockdiag/tests/test_builder_errors.py
 src/blockdiag/tests/test_builder_group.py
 src/blockdiag/tests/test_builder_node.py
 src/blockdiag/tests/test_builder_separate.py
+src/blockdiag/tests/test_command.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_utils.py
 src/blockdiag/tests/test_utils_fontmap.py
 src/blockdiag/tests/utils.py
+src/blockdiag/tests/VLGothic/LICENSE
+src/blockdiag/tests/VLGothic/LICENSE.en
+src/blockdiag/tests/VLGothic/LICENSE_E.mplus
+src/blockdiag/tests/VLGothic/LICENSE_J.mplus
+src/blockdiag/tests/VLGothic/VL-Gothic-Regular.ttf
 src/blockdiag/tests/diagrams/README
 src/blockdiag/tests/diagrams/auto_jumping_edge.diag
 src/blockdiag/tests/diagrams/background_url_image.diag
diff --git a/src/blockdiag.egg-info/requires.txt b/src/blockdiag.egg-info/requires.txt
index ff04388..bd90ed3 100644
--- a/src/blockdiag.egg-info/requires.txt
+++ b/src/blockdiag.egg-info/requires.txt
@@ -1,7 +1,7 @@
 setuptools
 funcparserlib
-webcolors
 Pillow
+webcolors
 
 [pdf]
 reportlab
diff --git a/src/blockdiag/__init__.py b/src/blockdiag/__init__.py
index 33edc89..bcee0a6 100644
--- a/src/blockdiag/__init__.py
+++ b/src/blockdiag/__init__.py
@@ -13,4 +13,4 @@
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
 
-__version__ = '1.4.7'
+__version__ = '1.5.3'
diff --git a/src/blockdiag/imagedraw/filters/linejump.py b/src/blockdiag/imagedraw/filters/linejump.py
index c3c41fe..0f40ea6 100644
--- a/src/blockdiag/imagedraw/filters/linejump.py
+++ b/src/blockdiag/imagedraw/filters/linejump.py
@@ -21,30 +21,32 @@ class LazyReceiver(object):
     def __init__(self, target):
         self.target = target
         self.calls = []
-        self.nested = []
 
     def __getattr__(self, name):
         return self.get_lazy_method(name)
 
     def get_lazy_method(self, name):
-        method = self._find_method(name)
-
-        def _(*args, **kwargs):
-            if name in self.target.self_generative_methods:
-                ret = method(self.target, *args, **kwargs)
-                receiver = LazyReceiver(ret)
-                self.nested.append(receiver)
+        if name in self.target.nosideeffect_methods:
+            method = self._find_method(name)
+            return functools.partial(method, self.target)
+        elif name in self.target.self_generative_methods:
+            def _(*args, **kwargs):
+                receiver = LazySubReceiver(name, self.target, *args, **kwargs)
+                self.calls.append((receiver, args, kwargs))
                 return receiver
-            else:
-                self.calls.append((method, args, kwargs))
-                return self
 
-        if method.__name__ in self.target.nosideeffect_methods:
-            return functools.partial(method, self.target)
+            return _
         else:
+            def _(*args, **kwargs):
+                self.calls.append((name, args, kwargs))
+                return self
+
             return _
 
     def _find_method(self, name):
+        if isinstance(name, LazyReceiver):
+            return name
+
         for p in self.target.__class__.__mro__:
             if name in p.__dict__:
                 return p.__dict__[name]
@@ -53,13 +55,24 @@ class LazyReceiver(object):
                              (self.target.__class__.__name__, name))
 
     def _run(self):
-        for recv in self.nested:
-            recv._run()
-
-        for method, args, kwargs in self.calls:
+        for name, args, kwargs in self.calls:
+            method = self._find_method(name)
             method(self.target, *args, **kwargs)
 
 
+class LazySubReceiver(LazyReceiver):
+    def __init__(self, name, target, *args, **kwargs):
+        self.name = name
+        self.args = args
+        self.kwargs = kwargs
+        super(LazySubReceiver, self).__init__(target)
+
+    def __call__(self, target, *args, **kwargs):
+        method = self._find_method(self.name)
+        self.target = method(self.target, *self.args, **self.kwargs)
+        self._run()
+
+
 class LineJumpDrawFilter(LazyReceiver):
     def __init__(self, target, jump_radius):
         super(LineJumpDrawFilter, self).__init__(target)
@@ -81,12 +94,8 @@ class LineJumpDrawFilter(LazyReceiver):
             self.jump_shift = kwargs['jump_shift']
 
     def _run(self):
-        for recv in self.nested:
-            recv._run()
-
-        line_method = self._find_method("line")
-        for method, args, kwargs in self.calls:
-            if method == line_method and kwargs.get('jump'):
+        for name, args, kwargs in self.calls:
+            if name == 'line' and kwargs.get('jump'):
                 ((x1, y1), (x2, y2)) = args[0]
                 if self.forward == 'holizonal' and y1 == y2:
                     self._holizonal_jumpline(x1, y1, x2, y2, **kwargs)
@@ -95,6 +104,7 @@ class LineJumpDrawFilter(LazyReceiver):
                     self._vertical_jumpline(x1, y1, x2, y2, **kwargs)
                     continue
 
+            method = self._find_method(name)
             method(self.target, *args, **kwargs)
 
     def _holizonal_jumpline(self, x1, y1, x2, y2, **kwargs):
diff --git a/src/blockdiag/imagedraw/svg.py b/src/blockdiag/imagedraw/svg.py
index 5d8af22..bc71574 100644
--- a/src/blockdiag/imagedraw/svg.py
+++ b/src/blockdiag/imagedraw/svg.py
@@ -15,6 +15,7 @@
 
 import os
 import re
+from PIL.Image import Image
 from base64 import b64encode
 from blockdiag.imagedraw import base as _base
 from blockdiag.imagedraw.simplesvg import (
@@ -234,7 +235,11 @@ class SVGImageDrawElement(_base.ImageDraw):
         if hasattr(url, 'read'):
             url = "data:;base64," + str(b64encode(url.read()))
         else:
-            ext = os.path.splitext(url)[1].lower()
+            if isinstance(url, Image):
+                ext = None
+            else:
+                ext = os.path.splitext(url)[1].lower()
+
             if ext not in ('.jpg', '.png', '.gif'):
                 stream = None
                 try:
diff --git a/src/blockdiag/plugins/__init__.py b/src/blockdiag/plugins/__init__.py
index dd550de..df50207 100644
--- a/src/blockdiag/plugins/__init__.py
+++ b/src/blockdiag/plugins/__init__.py
@@ -38,14 +38,6 @@ def load(plugins, diagram, **kwargs):
             raise AttributeError(msg)
 
 
-def unload_all():
-    for name in general_handlers.keys():
-        del general_handlers[name]
-
-    for handler in node_handlers[:]:
-        node_handlers.remove(handler)
-
-
 def install_general_handler(name, handler):
     if name not in general_handlers:
         general_handlers[name] = []
@@ -86,3 +78,20 @@ class NodeHandler(object):
 
     def on_build_finished(self, node):
         return True
+
+
+def cleanup():
+    fire_general_event('cleanup')
+
+    for name in list(general_handlers.keys()):
+        del general_handlers[name]
+
+    for handler in node_handlers[:]:
+        node_handlers.remove(handler)
+
+    for plugin in loaded_plugins[:]:
+        loaded_plugins.remove(plugin)
+
+
+def setup(app):
+    app.register_cleanup_handler(cleanup)
diff --git a/src/blockdiag/tests/__init__.py b/src/blockdiag/tests/__init__.py
index e69de29..40a96af 100644
--- a/src/blockdiag/tests/__init__.py
+++ b/src/blockdiag/tests/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/src/blockdiag/tests/rst/__init__.py b/src/blockdiag/tests/rst/__init__.py
index e69de29..40a96af 100644
--- a/src/blockdiag/tests/rst/__init__.py
+++ b/src/blockdiag/tests/rst/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/src/blockdiag/tests/rst/test_base_directives.py b/src/blockdiag/tests/rst/test_base_directives.py
index 3baedf8..938d220 100644
--- a/src/blockdiag/tests/rst/test_base_directives.py
+++ b/src/blockdiag/tests/rst/test_base_directives.py
@@ -1,20 +1,19 @@
 # -*- coding: utf-8 -*-
 
-import sys
-if sys.version_info < (2, 7):
-    import unittest2 as unittest
-else:
-    import unittest
-
-import os
 import io
-from blockdiag.tests.utils import capture_stderr, TemporaryDirectory
-
+import os
+import sys
 from docutils import nodes
 from docutils.core import publish_doctree
 from docutils.parsers.rst import directives as docutils
 from blockdiag.utils.rst import directives
 from blockdiag.utils.rst.nodes import blockdiag as blockdiag_node
+from blockdiag.tests.utils import capture_stderr, TemporaryDirectory
+
+if sys.version_info < (2, 7):
+    import unittest2 as unittest
+else:
+    import unittest
 
 
 class TestRstDirectives(unittest.TestCase):
diff --git a/src/blockdiag/tests/rst/test_blockdiag_directives.py b/src/blockdiag/tests/rst/test_blockdiag_directives.py
index 8e1850f..33aa4ba 100644
--- a/src/blockdiag/tests/rst/test_blockdiag_directives.py
+++ b/src/blockdiag/tests/rst/test_blockdiag_directives.py
@@ -1,19 +1,19 @@
 # -*- coding: utf-8 -*-
 
-import sys
-if sys.version_info < (2, 7):
-    import unittest2 as unittest
-else:
-    import unittest
-
 import os
-from blockdiag.utils.compat import u
-from blockdiag.tests.utils import capture_stderr, with_pil, TemporaryDirectory
-
+import sys
 from docutils import nodes
 from docutils.core import publish_doctree
 from docutils.parsers.rst import directives as docutils
+from blockdiag.utils.compat import u
 from blockdiag.utils.rst import directives
+from blockdiag.tests.utils import capture_stderr, with_pil, TemporaryDirectory
+
+
+if sys.version_info < (2, 7):
+    import unittest2 as unittest
+else:
+    import unittest
 
 
 class TestRstDirectives(unittest.TestCase):
@@ -59,6 +59,18 @@ class TestRstDirectives(unittest.TestCase):
         self.assertEqual(True, options['noviewbox'])
         self.assertEqual(True, options['inline_svg'])
 
+    @capture_stderr
+    def test_cleanup(self):
+        directives.setup(format='SVG', outputdir=self.tmpdir, noviewbox=False)
+        text = (".. blockdiag::\n"
+                "\n"
+                "   plugin autoclass\n"
+                "   A -> B")
+        publish_doctree(text)
+
+        from blockdiag import plugins
+        self.assertEqual([], plugins.loaded_plugins)
+
     def test_setup_fontpath1(self):
         with self.assertRaises(RuntimeError):
             directives.setup(format='SVG', fontpath=['dummy.ttf'],
diff --git a/src/blockdiag/tests/test_boot_params.py b/src/blockdiag/tests/test_boot_params.py
index 846ee09..702b78a 100644
--- a/src/blockdiag/tests/test_boot_params.py
+++ b/src/blockdiag/tests/test_boot_params.py
@@ -1,13 +1,8 @@
 # -*- coding: utf-8 -*-
 
-import sys
-if sys.version_info < (2, 7):
-    import unittest2 as unittest
-else:
-    import unittest
-
 import os
 import io
+import sys
 import tempfile
 from blockdiag.tests.utils import with_pdf
 
@@ -16,6 +11,11 @@ from blockdiag.command import BlockdiagOptions
 from blockdiag.utils.bootstrap import detectfont
 from blockdiag.utils.compat import u
 
+if sys.version_info < (2, 7):
+    import unittest2 as unittest
+else:
+    import unittest
+
 
 class TestBootParams(unittest.TestCase):
     def setUp(self):
@@ -124,14 +124,15 @@ class TestBootParams(unittest.TestCase):
 
     def test_exist_font_config_option(self):
         try:
-            tmp = tempfile.mkstemp()
+            fd, path = tempfile.mkstemp()
+            os.close(fd)
 
-            options = self.parser.parse(['-f', tmp[1], 'input.diag'])
-            self.assertEqual(options.font, [tmp[1]])
+            options = self.parser.parse(['-f', path, 'input.diag'])
+            self.assertEqual(options.font, [path])
             fontpath = detectfont(options)
-            self.assertEqual(fontpath, tmp[1])
+            self.assertEqual(fontpath, path)
         finally:
-            os.unlink(tmp[1])
+            os.unlink(path)
 
     def test_not_exist_font_config_option(self):
         with self.assertRaises(RuntimeError):
diff --git a/src/blockdiag/tests/test_command.py b/src/blockdiag/tests/test_command.py
new file mode 100644
index 0000000..55fbfea
--- /dev/null
+++ b/src/blockdiag/tests/test_command.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+
+import os
+import sys
+from blockdiag.command import BlockdiagApp
+from blockdiag.tests.utils import TemporaryDirectory
+
+if sys.version_info < (2, 7):
+    import unittest2 as unittest
+else:
+    import unittest
+
+
+class TestBlockdiagApp(unittest.TestCase):
+    def test_app_cleans_up_images(self):
+        testdir = os.path.dirname(__file__)
+        diagpath = os.path.join(testdir,
+                                'diagrams',
+                                'background_url_image.diag')
+        urlopen_cache = {}
+
+        def cleanup():
+            from blockdiag.utils import images
+            urlopen_cache.update(images.urlopen_cache)
+
+        try:
+            tmpdir = TemporaryDirectory()
+            fd, tmpfile = tmpdir.mkstemp()
+            os.close(fd)
+
+            args = ['-T', 'SVG', '-o', tmpfile, diagpath]
+            app = BlockdiagApp()
+            app.register_cleanup_handler(cleanup)  # to get internal state
+            app.run(args)
+
+            self.assertTrue(urlopen_cache)  # check images were cached
+            for path in urlopen_cache.values():
+                self.assertFalse(os.path.exists(path))  # and removed finally
+        finally:
+            tmpdir.clean()
+
+    def test_app_cleans_up_plugins(self):
+        testdir = os.path.dirname(__file__)
+        diagpath = os.path.join(testdir,
+                                'diagrams',
+                                'plugin_autoclass.diag')
+        loaded_plugins = []
+
+        def cleanup():
+            from blockdiag import plugins
+            loaded_plugins.extend(plugins.loaded_plugins)
+
+        try:
+            tmpdir = TemporaryDirectory()
+            fd, tmpfile = tmpdir.mkstemp()
+            os.close(fd)
+
+            args = ['-T', 'SVG', '-o', tmpfile, diagpath]
+            app = BlockdiagApp()
+            app.register_cleanup_handler(cleanup)  # to get internal state
+            app.run(args)
+
+            from blockdiag import plugins
+            self.assertTrue(loaded_plugins)  # check plugins were loaded
+            self.assertFalse(plugins.loaded_plugins)  # and unloaded finally
+        finally:
+            tmpdir.clean()
diff --git a/src/blockdiag/tests/test_generate_diagram.py b/src/blockdiag/tests/test_generate_diagram.py
index 2c0b458..e083711 100644
--- a/src/blockdiag/tests/test_generate_diagram.py
+++ b/src/blockdiag/tests/test_generate_diagram.py
@@ -2,24 +2,25 @@
 
 import os
 import re
+import sys
 from nose.tools import nottest
+import blockdiag
+import blockdiag.command
 from blockdiag.tests.utils import capture_stderr, TemporaryDirectory
 from blockdiag.tests.utils import supported_pil, supported_pdf
 
-import sys
 if sys.version_info < (2, 7):
     import unittest2 as unittest
 else:
     import unittest
 
-import blockdiag
-import blockdiag.command
 
+TESTDIR = os.path.dirname(__file__)
+FONTPATH = os.path.join(TESTDIR, 'VLGothic', 'VL-Gothic-Regular.ttf')
 
-def get_fontpath(testdir=None):
-    if testdir is None:
-        testdir = os.path.dirname(__file__)
-    return os.path.join(testdir, 'truetype', 'VL-PGothic-Regular.ttf')
+
+def get_fontpath(testdir):
+    return os.path.join(testdir, 'VLGothic', 'VL-Gothic-Regular.ttf')
 
 
 def get_diagram_files(testdir):
@@ -58,23 +59,29 @@ def test_generate_with_separate():
 @nottest
 def testcase_generator(basepath, mainfunc, files, options):
     fontpath = get_fontpath(basepath)
-    if os.path.exists(fontpath):
-        options = options + ['-f', fontpath]
+    options = options + ['-f', fontpath]
 
     for source in files:
         yield generate, mainfunc, 'svg', source, options
 
-        if supported_pil() and os.path.exists(fontpath):
-            yield generate, mainfunc, 'png', source, options
-            yield generate, mainfunc, 'png', source, options + ['--antialias']
-        else:
+        if not supported_pil():
             yield unittest.skip("Pillow is not available")(generate)
             yield unittest.skip("Pillow is not available")(generate)
-
-        if supported_pdf() and os.path.exists(fontpath):
-            yield generate, mainfunc, 'pdf', source, options
+        elif os.environ.get('ALL_TESTS') is None:
+            message = "Skipped by default. To enable it, specify $ALL_TESTS=1"
+            yield unittest.skip(message)(generate)
+            yield unittest.skip(message)(generate)
         else:
+            yield generate, mainfunc, 'png', source, options
+            yield generate, mainfunc, 'png', source, options + ['--antialias']
+
+        if not supported_pdf():
             yield unittest.skip("reportlab is not available")(generate)
+        elif os.environ.get('ALL_TESTS') is None:
+            message = "Skipped by default. To enable it, specify $ALL_TESTS=1"
+            yield unittest.skip(message)(generate)
+        else:
+            yield generate, mainfunc, 'pdf', source, options
 
 
 @capture_stderr
@@ -91,13 +98,11 @@ def generate(mainfunc, filetype, source, options):
 
 
 def not_exist_font_config_option_test():
-    fontpath = get_fontpath()
-    if os.path.exists(fontpath):
-        args = ['-f', '/font_is_not_exist', '-f', fontpath, 'input.diag']
-        options = blockdiag.command.BlockdiagOptions(blockdiag).parse(args)
+    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)
+    from blockdiag.utils.bootstrap import detectfont
+    detectfont(options)
 
 
 def stdin_test():
@@ -121,6 +126,27 @@ def stdin_test():
 
 
 @capture_stderr
+def ghostscript_not_found_test():
+    testdir = os.path.dirname(__file__)
+    diagpath = os.path.join(testdir, 'diagrams', 'background_url_image.diag')
+
+    try:
+        old_path = os.environ['PATH']
+        os.environ['PATH'] = ''
+        tmpdir = TemporaryDirectory()
+        fd, tmpfile = tmpdir.mkstemp()
+        os.close(fd)
+
+        args = ['-T', 'SVG', '-o', tmpfile, diagpath]
+        ret = blockdiag.command.main(args)
+        assert 'Could not convert image:' in sys.stderr.getvalue()
+        assert ret == 0
+    finally:
+        tmpdir.clean()
+        os.environ['PATH'] = old_path
+
+
+ at capture_stderr
 def svg_includes_source_code_tag_test():
     from xml.etree import ElementTree
 
diff --git a/src/blockdiag/tests/test_imagedraw_textfolder.py b/src/blockdiag/tests/test_imagedraw_textfolder.py
index 4d1fa2d..a4d244b 100644
--- a/src/blockdiag/tests/test_imagedraw_textfolder.py
+++ b/src/blockdiag/tests/test_imagedraw_textfolder.py
@@ -1,17 +1,17 @@
 # -*- coding: utf-8 -*-
 
 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
 
+if sys.version_info < (2, 7):
+    import unittest2 as unittest
+else:
+    import unittest
+
 
 CHAR_WIDTH = 14
 CHAR_HEIGHT = 10
diff --git a/src/blockdiag/tests/test_imagedraw_utils.py b/src/blockdiag/tests/test_imagedraw_utils.py
index c2e26d9..7439e2f 100644
--- a/src/blockdiag/tests/test_imagedraw_utils.py
+++ b/src/blockdiag/tests/test_imagedraw_utils.py
@@ -1,17 +1,17 @@
 # -*- 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
 
+if sys.version_info < (2, 7):
+    import unittest2 as unittest
+else:
+    import unittest
+
 
 class TestUtils(unittest.TestCase):
     def test_is_zenkaku(self):
diff --git a/src/blockdiag/tests/test_parser.py b/src/blockdiag/tests/test_parser.py
index cde0d6f..d225147 100644
--- a/src/blockdiag/tests/test_parser.py
+++ b/src/blockdiag/tests/test_parser.py
@@ -2,14 +2,14 @@
 from __future__ import print_function
 
 import sys
+from blockdiag.parser import parse_string, ParseException
+from blockdiag.parser import Diagram, Group, Statements, Node, Edge
+
 if sys.version_info < (2, 7):
     import unittest2 as unittest
 else:
     import unittest
 
-from blockdiag.parser import parse_string, ParseException
-from blockdiag.parser import Diagram, Group, Statements, Node, Edge
-
 
 class TestParser(unittest.TestCase):
     def test_basic(self):
diff --git a/src/blockdiag/tests/test_pep8.py b/src/blockdiag/tests/test_pep8.py
deleted file mode 100644
index 3d74cbf..0000000
--- a/src/blockdiag/tests/test_pep8.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# -*- 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_utils.py b/src/blockdiag/tests/test_utils.py
index 97ed133..e701f91 100644
--- a/src/blockdiag/tests/test_utils.py
+++ b/src/blockdiag/tests/test_utils.py
@@ -1,13 +1,13 @@
 # -*- coding: utf-8 -*-
 
 import sys
+from blockdiag.utils import Size, unquote
+
 if sys.version_info < (2, 7):
     import unittest2 as unittest
 else:
     import unittest
 
-from blockdiag.utils import Size
-
 
 class TestUtils(unittest.TestCase):
     def test_size_resize(self):
@@ -38,3 +38,9 @@ class TestUtils(unittest.TestCase):
         size = Size(1.5, 2.5)
 
         self.assertEqual((1, 2), size.to_integer_point())
+
+    def test_unquote(self):
+        self.assertEqual('test', unquote('"test"'))
+        self.assertEqual('test', unquote("'test'"))
+        self.assertEqual("'half quoted", unquote("'half quoted"))
+        self.assertEqual('"half quoted', unquote('"half quoted'))
diff --git a/src/blockdiag/tests/test_utils_fontmap.py b/src/blockdiag/tests/test_utils_fontmap.py
index 5c79da3..7ab87cf 100644
--- a/src/blockdiag/tests/test_utils_fontmap.py
+++ b/src/blockdiag/tests/test_utils_fontmap.py
@@ -1,19 +1,18 @@
 # -*- coding: utf-8 -*-
 
-import sys
-if sys.version_info < (2, 7):
-    import unittest2 as unittest
-else:
-    import unittest
-
 import os
+import sys
 import tempfile
-from blockdiag.utils.compat import u
-from blockdiag.tests.utils import capture_stderr
-
 from io import StringIO
 from collections import namedtuple
+from blockdiag.utils.compat import u
 from blockdiag.utils.fontmap import FontInfo, FontMap
+from blockdiag.tests.utils import capture_stderr
+
+if sys.version_info < (2, 7):
+    import unittest2 as unittest
+else:
+    import unittest
 
 
 FontElement = namedtuple('FontElement', 'fontfamily fontsize')
@@ -75,7 +74,7 @@ class TestUtilsFontmap(unittest.TestCase):
 
         font = FontInfo("sansserif-bold", None, 11)
         self.assertEqual('', font.name)
-        self.assertEqual('sansserif', font.generic_family)
+        self.assertEqual('sans-serif', font.generic_family)
         self.assertEqual('bold', font.weight)
         self.assertEqual('normal', font.style)
 
@@ -132,7 +131,7 @@ class TestUtilsFontmap(unittest.TestCase):
 
         font1 = fmap.find()
         self.assertTrue(font1)
-        self.assertEqual('sansserif', font1.generic_family)
+        self.assertEqual('sans-serif', font1.generic_family)
         self.assertEqual(None, font1.path)
         self.assertEqual(11, font1.size)
 
@@ -161,7 +160,7 @@ class TestUtilsFontmap(unittest.TestCase):
 
         font1 = fmap.find()
         self.assertTrue(font1)
-        self.assertEqual('sansserif', font1.generic_family)
+        self.assertEqual('sans-serif', font1.generic_family)
         self.assertEqual(None, font1.path)
         self.assertEqual(11, font1.size)
 
@@ -173,7 +172,7 @@ class TestUtilsFontmap(unittest.TestCase):
 
         font1 = fmap.find()
         self.assertTrue(font1)
-        self.assertEqual('sansserif', font1.generic_family)
+        self.assertEqual('sans-serif', font1.generic_family)
         self.assertEqual(self.fontpath[0], font1.path)
         self.assertEqual(11, font1.size)
 
@@ -215,7 +214,7 @@ class TestUtilsFontmap(unittest.TestCase):
             fmap = FontMap(config)
 
             font1 = fmap.find()
-            self.assertEqual('sansserif', font1.generic_family)
+            self.assertEqual('sans-serif', font1.generic_family)
             self.assertEqual(self.fontpath[1], font1.path)
             self.assertEqual(11, font1.size)
         else:
@@ -232,7 +231,7 @@ class TestUtilsFontmap(unittest.TestCase):
             fmap = FontMap(config)
 
             font1 = fmap.find()
-            self.assertEqual('sansserif', font1.generic_family)
+            self.assertEqual('sans-serif', font1.generic_family)
             self.assertEqual(self.fontpath[1], font1.path)
             self.assertEqual(11, font1.size)
 
@@ -244,7 +243,7 @@ class TestUtilsFontmap(unittest.TestCase):
 
         element = FontElement('CapitalCase-sansserif', 11)
         font1 = fmap.find(element)
-        self.assertEqual('sansserif', font1.generic_family)
+        self.assertEqual('sans-serif', font1.generic_family)
         self.assertEqual('capitalcase-sansserif-normal', font1.familyname)
         self.assertEqual(self.fontpath[0], font1.path)
         self.assertEqual(11, font1.size)
@@ -256,7 +255,7 @@ class TestUtilsFontmap(unittest.TestCase):
         fmap = FontMap(config)
 
         font1 = fmap.find()
-        self.assertEqual('sansserif', font1.generic_family)
+        self.assertEqual('sans-serif', font1.generic_family)
         self.assertEqual(None, font1.path)
         self.assertEqual(11, font1.size)
 
@@ -268,7 +267,7 @@ class TestUtilsFontmap(unittest.TestCase):
 
         element = FontElement('fantasy', 20)
         font3 = fmap.find(element)
-        self.assertEqual('sansserif', font3.generic_family)
+        self.assertEqual('sans-serif', font3.generic_family)
         self.assertEqual(None, font3.path)
         self.assertEqual(20, font3.size)
 
@@ -279,7 +278,7 @@ class TestUtilsFontmap(unittest.TestCase):
         fmap = FontMap(config)
 
         font1 = fmap.find()
-        self.assertEqual('sansserif', font1.generic_family)
+        self.assertEqual('sans-serif', font1.generic_family)
         self.assertEqual(None, font1.path)
         self.assertEqual(11, font1.size)
 
@@ -336,7 +335,7 @@ class TestUtilsFontmap(unittest.TestCase):
 
         font1 = fmap.find()
         self.assertTrue(font1)
-        self.assertEqual('sansserif', font1.generic_family)
+        self.assertEqual('sans-serif', font1.generic_family)
         self.assertEqual(self.fontpath[0], font1.path)
         self.assertEqual(11, font1.size)
 
@@ -357,7 +356,7 @@ class TestUtilsFontmap(unittest.TestCase):
 
             font1 = fmap.find()
             self.assertTrue(font1)
-            self.assertEqual('sansserif', font1.generic_family)
+            self.assertEqual('sans-serif', font1.generic_family)
             self.assertEqual(self.fontpath[0], font1.path)
             self.assertEqual(11, font1.size)
         finally:
diff --git a/src/blockdiag/tests/utils.py b/src/blockdiag/tests/utils.py
index 87d4aef..a651a30 100644
--- a/src/blockdiag/tests/utils.py
+++ b/src/blockdiag/tests/utils.py
@@ -1,20 +1,20 @@
 # -*- coding: utf-8 -*-
 from __future__ import print_function
 
-import sys
-if sys.version_info < (2, 7):
-    import unittest2 as unittest
-else:
-    import unittest
-
 import os
 import re
+import sys
 import functools
 from shutil import rmtree
 from tempfile import mkdtemp, mkstemp
 from blockdiag.builder import ScreenNodeBuilder
 from blockdiag.parser import parse_file
 
+if sys.version_info < (2, 7):
+    import unittest2 as unittest
+else:
+    import unittest
+
 try:
     # sys.stderr in py2.x allows mixture of str and unicode
     from cStringIO import StringIO
diff --git a/src/blockdiag/utils/__init__.py b/src/blockdiag/utils/__init__.py
index 231aa72..df84ac9 100644
--- a/src/blockdiag/utils/__init__.py
+++ b/src/blockdiag/utils/__init__.py
@@ -167,18 +167,7 @@ class Box(list):
 
 
 def unquote(string):
-    """
-    Remove quotas from string
-
-    >>> unquote('"test"')
-    'test'
-    >>> unquote("'test'")
-    'test'
-    >>> unquote("'half quoted")
-    "'half quoted"
-    >>> unquote('"half quoted')
-    '"half quoted'
-    """
+    """ Remove quotas from string """
     if string:
         m = re.match('\A(?P<quote>"|\')((.|\s)*)(?P=quote)\Z', string, re.M)
         if m:
diff --git a/src/blockdiag/utils/bootstrap.py b/src/blockdiag/utils/bootstrap.py
index 47dc42f..b0b6cbf 100644
--- a/src/blockdiag/utils/bootstrap.py
+++ b/src/blockdiag/utils/bootstrap.py
@@ -20,6 +20,7 @@ import traceback
 from optparse import OptionParser, SUPPRESS_HELP
 from blockdiag import imagedraw
 from blockdiag import plugins
+from blockdiag.utils import images
 from blockdiag.utils.compat import codecs
 from blockdiag.utils.config import ConfigParser
 from blockdiag.utils.fontmap import parse_fontpath, FontMap
@@ -30,10 +31,24 @@ class Application(object):
     module = None
     options = None
 
+    def __init__(self):
+        self.cleanup_handlers = []
+
+    def __enter__(self):
+        self.setup()
+        return self
+
+    def __exit__(self, *args):
+        self.cleanup()
+
+    def register_cleanup_handler(self, handler):
+        self.cleanup_handlers.append(handler)
+
     def run(self, args):
         try:
             self.parse_options(args)
             self.create_fontmap()
+            self.setup()
 
             parsed = self.parse_diagram()
             return self.build_diagram(parsed)
@@ -49,8 +64,7 @@ class Application(object):
                 error("%s" % e)
             return -1
         finally:
-            plugins.fire_general_event('cleanup')
-            plugins.unload_all()
+            self.cleanup()
 
     def parse_options(self, args):
         self.options = Options(self.module).parse(args)
@@ -58,6 +72,10 @@ class Application(object):
     def create_fontmap(self):
         self.fontmap = create_fontmap(self.options)
 
+    def setup(self):
+        images.setup(self)
+        plugins.setup(self)
+
     def parse_diagram(self):
         if self.options.input == '-':
             stream = codecs.getreader('utf-8-sig')(sys.stdin)
@@ -90,6 +108,15 @@ class Application(object):
 
         return 0
 
+    def cleanup(self):
+        for handler in self.cleanup_handlers[:]:
+            try:
+                handler()
+            except Exception as exc:
+                error("%s" % exc)
+            finally:
+                self.cleanup_handlers.remove(handler)
+
 
 class Options(object):
     def __init__(self, module):
diff --git a/src/blockdiag/utils/compat.py b/src/blockdiag/utils/compat.py
index fb1a83a..df902b5 100644
--- a/src/blockdiag/utils/compat.py
+++ b/src/blockdiag/utils/compat.py
@@ -14,6 +14,7 @@
 #  limitations under the License.
 
 import sys
+import codecs
 
 if sys.version_info[0] == 2:
     string_types = (str, unicode)  # NOQA: pyflakes complains to unicode in py3
@@ -31,7 +32,6 @@ def u(string):
 
 
 # replace codecs.getreader
-import codecs
 if sys.version_info[0] == 3:
     getreader = codecs.getreader
 
diff --git a/src/blockdiag/utils/fontmap.py b/src/blockdiag/utils/fontmap.py
index 05c0236..3a21eb6 100644
--- a/src/blockdiag/utils/fontmap.py
+++ b/src/blockdiag/utils/fontmap.py
@@ -43,6 +43,10 @@ class FontInfo(object):
         self.weight = family[2]
         self.style = family[3]
 
+    def __repr__(self):
+        return ("<FontInfo familyname=%r size=%r>" %
+                (self.familyname, self.size))
+
     @property
     def familyname(self):
         if self.name:
@@ -50,10 +54,15 @@ class FontInfo(object):
         else:
             name = ''
 
+        if self.generic_family == 'sans-serif':
+            generic_family = 'sansserif'
+        else:
+            generic_family = self.generic_family
+
         if self.weight == 'bold':
-            return "%s%s-%s" % (name, self.generic_family, self.weight)
+            return "%s%s-%s" % (name, generic_family, self.weight)
         else:
-            return "%s%s-%s" % (name, self.generic_family, self.style)
+            return "%s%s-%s" % (name, generic_family, self.style)
 
     def _parse(self, familyname):
         pattern = '^(?:(.*)-)?' + \
@@ -69,6 +78,9 @@ class FontInfo(object):
         generic_family = match.group(2)
         style = match.group(3) or ''
 
+        if generic_family == 'sansserif':
+            generic_family = 'sans-serif'
+
         if style == 'bold':
             weight = 'bold'
             style = 'normal'
diff --git a/src/blockdiag/utils/images.py b/src/blockdiag/utils/images.py
index 7859a9a..5fa94e1 100644
--- a/src/blockdiag/utils/images.py
+++ b/src/blockdiag/utils/images.py
@@ -15,6 +15,7 @@
 
 from __future__ import division
 import io
+import os
 import re
 from PIL import Image
 from tempfile import NamedTemporaryFile
@@ -30,22 +31,25 @@ def urlopen(url, *args, **kwargs):
     from blockdiag.utils.compat import urlopen as orig_urlopen
 
     if url not in urlopen_cache:
-        tmpfile = NamedTemporaryFile()
-        tmpfile.write(orig_urlopen(url, *args, **kwargs).read())
-        tmpfile.flush()
-        urlopen_cache[url] = tmpfile
+        with NamedTemporaryFile(delete=False) as tmpfile:
+            tmpfile.write(orig_urlopen(url, *args, **kwargs).read())
+            tmpfile.flush()
+            urlopen_cache[url] = tmpfile.name
 
-    return io.open(urlopen_cache[url].name, 'rb')
+    return io.open(urlopen_cache[url], 'rb')
 
 
-def get_image_size(filename):
-    image = None
-    try:
-        image = open(filename)
+def get_image_size(image):
+    if isinstance(image, Image.Image):
         return image.size
-    finally:
-        if image:
-            image.close()
+    else:
+        stream = None
+        try:
+            stream = open(image)
+            return stream.size
+        finally:
+            if stream and hasattr(stream, 'close'):
+                stream.close()
 
 
 def calc_image_size(size, bounded):
@@ -120,9 +124,27 @@ def open(url, mode='Pillow'):
         # stream will be closed by GC
         return image
     else:  # mode == 'png'
-        png_image = io.BytesIO()
-        image.save(png_image, 'PNG')
-        stream.close()
+        try:
+            png_image = io.BytesIO()
+            image.save(png_image, 'PNG')
+            if hasattr(stream, 'close'):  # close() is implemented on Pillow
+                stream.close()
+        except:
+            warning(u("Could not convert image: %s"), url)
+            raise IOError
 
         png_image.seek(0)
         return png_image
+
+
+def cleanup():
+    for url in list(urlopen_cache.keys()):
+        path = urlopen_cache.pop(url)
+        try:
+            os.remove(path)
+        except:
+            pass
+
+
+def setup(app):
+    app.register_cleanup_handler(cleanup)
diff --git a/src/blockdiag/utils/rst/directives.py b/src/blockdiag/utils/rst/directives.py
index fa5e218..7846280 100644
--- a/src/blockdiag/utils/rst/directives.py
+++ b/src/blockdiag/utils/rst/directives.py
@@ -16,13 +16,14 @@
 import os
 import io
 from hashlib import sha1
+from functools import wraps
 from collections import namedtuple
 from docutils import nodes
 from docutils.parsers import rst
 from docutils.parsers.rst.roles import set_classes
 from docutils.statemachine import ViewList
 
-from blockdiag.utils.bootstrap import create_fontmap
+from blockdiag.utils.bootstrap import create_fontmap, Application
 from blockdiag.utils.compat import string_types
 from blockdiag.utils.rst.nodes import blockdiag as blockdiag_node
 
@@ -46,6 +47,15 @@ def relfn2path(env, filename):
     return relfn, os.path.join(env.srcdir, relfn)
 
 
+def with_blockdiag(fn):
+    @wraps(fn)
+    def decorator(*args):
+        with Application():
+            return fn(*args)
+
+    return decorator
+
+
 def align(argument):
     align_values = ('left', 'center', 'right')
     return rst.directives.choice(argument, align_values)
@@ -142,6 +152,7 @@ class BlockdiagDirectiveBase(rst.Directive):
 class BlockdiagDirective(BlockdiagDirectiveBase):
     processor = None  # backward compatibility for 1.4.0
 
+    @with_blockdiag
     def run(self):
         figwidth = self.options.pop('figwidth', None)
         figclasses = self.options.pop('figclass', None)
diff --git a/src/blockdiag_sphinxhelper.py b/src/blockdiag_sphinxhelper.py
index cb250d3..87cc8ca 100644
--- a/src/blockdiag_sphinxhelper.py
+++ b/src/blockdiag_sphinxhelper.py
@@ -13,18 +13,19 @@
 #  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.compat
 import blockdiag.utils.fontmap
 import blockdiag.utils.rst.nodes
 import blockdiag.utils.rst.directives
+
+__all__ = [
+    'core', 'utils'
+]
+
+core = blockdiag
 utils = blockdiag.utils
diff --git a/test.sh b/test.sh
new file mode 100755
index 0000000..0fb6c95
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+bin/blockdiag --debug -Tsvg -o blockdiag.svg $1 $2 $3 $4 $5
+#bin/blockdiag --debug -Tpng -o blockdiag.png $1 $2 $3 $4 $5
diff --git a/tox.ini b/tox.ini
index 3b0b529..4e7339b 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,14 +1,17 @@
 [tox]
-envlist=py26,py27,py32,py33,py34
+envlist=py26,py27,py32,py33,py34,pillow2.0,pillow2.2,pillow2.4
 
 [testenv]
 deps=
     nose
     mock
     flake8
+    flake8-coding
     docutils
     reportlab
     wand
+passenv=
+    ALL_TESTS
 commands=
     nosetests
     flake8 src
@@ -22,3 +25,18 @@ deps=
     unittest2
     reportlab
     wand
+
+[testenv:pillow2.0]
+deps=
+    {[testenv]deps}
+    Pillow<=2.0.9999
+
+[testenv:pillow2.2]
+deps=
+    {[testenv]deps}
+    Pillow<=2.2.9999
+
+[testenv:pillow2.4]
+deps=
+    {[testenv]deps}
+    Pillow<=2.4.9999

-- 
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