Skip to content

Commit

Permalink
* examples Makefile
Browse files Browse the repository at this point in the history
  - fixed dependencies
* examples/custom.ini, examples/py2puml-custom.puml
  - using epilog for class associations
* tests/test_puml_generator.py, examples/example_globals_NS.puml
  - can be generated by simple command line
* examples/py2puml-custom.puml
  - puml_generator.deco_marker made private
* minor improvements in cli_parser
* corrected tests equality assertions
* track and test cli usage message
  • Loading branch information
RockyRoad29 committed Sep 1, 2017
1 parent 9bdbaca commit 3f69975
Show file tree
Hide file tree
Showing 17 changed files with 148 additions and 59 deletions.
30 changes: 19 additions & 11 deletions py2puml/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,28 @@ results as long as the syntax is correct.
## Features
* optionally use namespaces to represent packages/folders
* Supports simple and multiple inheritance
* Include argument lists
* configurable prolog and epilog, useful for styling
* Optionnally include argument lists
* Optionnally exclude 'self' and defaults from arg lists
* configurable prolog and epilog, useful for styling, and class associations
* allow several input sources for one plantuml output.

## Command line interface

usage: py2uml [-h] [--config CONFIG] [-o [OUTPUT]] [-r ROOT]
py_file [py_file ...]
usage: py2uml [-h] [-c CONFIG] [-o OUTPUT] [-r ROOT] py_file [py_file ...]

py2puml from Martin B. K. Grønholdt, v0.2.4 by Michelle Baert.
Create PlantUML classes from Python source code.
py2puml v1.0.0
by Michelle Baert, based on work from Martin B. K. Grønholdt.

Create PlantUML classes from Python source code.

positional arguments:
py_file the Python source files to parse.

optional arguments:
-h, --help show this help message and exit
--config CONFIG Configuration file (replace defaults)
-o [OUTPUT], --output [OUTPUT]
-c CONFIG, --config CONFIG
Configuration file (replace defaults)
-o OUTPUT, --output OUTPUT
The name of the ouput PlantUML file.
-r ROOT, --root ROOT Project root directory. Create namespaces from there

Expand All @@ -38,6 +41,9 @@ results as long as the syntax is correct.
- <WORK_DIR>/.py2puml.ini
- <WORK_DIR>/py2puml.ini

If the provided config filename cannot be found,
the program will use no config at all.

## Examples

Several examples are provided. Here is how you build a diagram from this program source code:
Expand All @@ -53,13 +59,15 @@ Hand-made `Makefile`s are there to help automatisation.

![py2puml.py classes](examples/py2puml-custom.png)

Of course the diagram can be enriched, with class associations
for example, by editing the generated .puml file, or maybe with an epilog
in custom config.
Of course the generated PlantUML script can be edited to fine-tune your diagram,
but a lot of customization can be done by specifying prolog and epilog in a
configuration file.

Here is an example with multiple source files, multiple inheritance, and
[project-specific configuration](examples/cal_clock3/py2puml.ini):

![cal_clock3](examples/cal_clock3/calendar_clock.png):

## TODO
- detect and warn about empty results
- namespace specification of imported names (e.g. inheritance)
34 changes: 25 additions & 9 deletions py2puml/examples/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,51 @@ PLANTUML=java -jar /usr/local/share/plantuml/plantuml.jar
PY2PUML=../py2puml.py
PY2UML_SOURCES = ../py2puml.py ../puml_generator.py ../code_info.py ../ast_visitor.py
DIAGRAMS=person.png py2puml.png py2puml_NS.png py2puml-custom.png cal_clock3
MORE_EXAMPLES= dbdia2sql.py.puml \
dbpuml2sql.puml \
dbsql2puml.puml \
person.puml \
MORE_DIAGRAMS= dbdia2sql.py.png \
dbpuml2sql.png \
dbsql2puml.png
MORE_EXAMPLES= person.puml \
example_globals_NS.puml \
example_globals.puml \
example.puml

all: $(DIAGRAMS) $(MORE_EXAMPLES)
all: $(DIAGRAMS) $(MORE_DIAGRAMS) $(MORE_EXAMPLES)

py2puml.puml:../py2puml.py
$(PY2PUML) $< -o $@

py2puml_NS.puml:$(PY2UML_SOURCES)
$(PY2PUML) -r .. -o $@ $^

py2puml-custom.puml:$(PY2UML_SOURCES)
$(PY2PUML) --config custom.ini --root .. -o $@ $^
py2puml-custom.puml:$(PY2UML_SOURCES) custom.ini
$(PY2PUML) --config custom.ini --root .. -o $@ $(PY2UML_SOURCES)

example_globals.puml: example.py globals.ini
$(PY2PUML) -c globals.ini -o $@ $<

example_globals_NS.puml: example.py globals.ini
$(PY2PUML) -c globals.ini -r .. -o $@ $<

cal_clock3:
DBPUML2SQL_SOURCES = ../../dbpuml2sql/dbpuml2sql.py \
../../dbpuml2sql/__init__.py \
../../dbpuml2sql/pumlreader.py \
../../dbpuml2sql/table.py \
../../dbpuml2sql/test_Table.py
DBPUML2SQL_OPTIONS = # -r ../../dbpuml2sql
dbpuml2sql.puml:$(PY2UML_SOURCES) $(DBPUML2SQL_SOURCES) dbpuml2sql.ini
$(PY2PUML) -c dbpuml2sql.ini $(DBPUML2SQL_OPTIONS) -o $@ $(DBPUML2SQL_SOURCES)

DBSQL2PUML_SOURCES = ../../dbsql2puml/dbsql2puml.py \
../../dbsql2puml/sql2puml.py \
../../dbsql2puml/sqlparsetables.py
DBSQL2PUML_OPTIONS = # -r ../../dbsql2puml
dbsql2puml.puml:$(PY2UML_SOURCES) $(DBSQL2PUML_SOURCES) dbsql2puml.ini
$(PY2PUML) -c dbsql2puml.ini $(DBSQL2PUML_OPTIONS) -o $@ $(DBSQL2PUML_SOURCES)

cal_clock3: $(PY2UML_SOURCES)
cd $@ && $(MAKE)

%.puml: %.py ../py2puml.py
%.puml: %.py ../py2puml.py$ (PY2UML_SOURCES)
$(PY2PUML) $< -o $@

%.png: %.puml
Expand Down
8 changes: 4 additions & 4 deletions py2puml/examples/custom.ini
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[puml]
prolog = skinparam monochrome false
skinparam classAttributeIconSize 0
' set namespaceSeparator none
prolog = skinparam classAttributeIconSize 0
scale 1.2

epilog = ast_visitor.TreeVisitor ..> puml_generator.PUML_Generator : context
TreeVisitor ..> ClassInfo : current class
TreeVisitor ..> Code : module globals
Binary file modified py2puml/examples/dbdia2sql.py.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 4 additions & 11 deletions py2puml/examples/dbpuml2sql.ini
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,10 @@ prolog = skinparam monochrome true
# "py2uml.ini" in your project's root directory.
epilog = PUMLReader o-- Table

# Below a few ideas of further configuration, not implemented yet.
[methods]
omit-self = False
write-arg-list = True

[members]

[class-variables]

# With namespaces, use rather this:
# epilog = pumlreader.PUMLReader o-- table.Table
# note top of test_Table.Table : imported from .table
# 'table.Table ..> test_Table.Table : "import"

[module]
write-variables = False
write-functions = False
write-globals = False
16 changes: 16 additions & 0 deletions py2puml/examples/dbsql2puml.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[puml]
# puml prolog is useful to customize the style of produced diagrams
# See http://plantuml.com/class-diagram#Skinparam
# and http://plantuml.com/commons for details
prolog = skinparam monochrome true
skinparam classAttributeIconSize 0
scale 2

# puml epilog allows to add associations and notes
# to add information to generated classes.
# Usually project-specific, define it in a file
# "py2uml.ini" in your project's root directory.
epilog =

[module]
write-globals = False
4 changes: 4 additions & 0 deletions py2puml/examples/dbsql2puml.puml
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
@startuml
skinparam monochrome true
skinparam classAttributeIconSize 0
scale 2

ValueError <|-- NoTableException
class NoTableException {
}
Expand Down
4 changes: 4 additions & 0 deletions py2puml/examples/example_globals_NS.puml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@startuml
namespace examples {
namespace example {
class A {
Expand Down Expand Up @@ -28,3 +29,6 @@ namespace examples {
+global_func(arg1, arg2, arg3=None)
}

}
}
@enduml
Binary file modified py2puml/examples/py2puml-custom.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 5 additions & 3 deletions py2puml/examples/py2puml-custom.puml
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
@startuml
skinparam monochrome false
skinparam classAttributeIconSize 0
' set namespaceSeparator none
scale 1.2

namespace py2puml {
Expand All @@ -24,7 +22,7 @@ namespace puml_generator {
+header(self)
+footer(self)
+do_file(self, srcfile, errormsg=None)
+deco_marker(dec){static}
-_deco_marker(dec){static}
+is_static_method(meth){static}
+print_classinfo(self, classinfo)
+print_codeinfo(self, codeinfo)
Expand Down Expand Up @@ -90,4 +88,8 @@ namespace ast_visitor {
}

}
ast_visitor.TreeVisitor ..> puml_generator.PUML_Generator : context
TreeVisitor ..> ClassInfo : current class
TreeVisitor ..> Code : module globals

@enduml
2 changes: 1 addition & 1 deletion py2puml/examples/py2puml_NS.puml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ namespace puml_generator {
+header(self)
+footer(self)
+do_file(self, srcfile, errormsg=None)
+deco_marker(dec){static}
-_deco_marker(dec){static}
+is_static_method(meth){static}
+print_classinfo(self, classinfo)
+print_codeinfo(self, codeinfo)
Expand Down
28 changes: 28 additions & 0 deletions py2puml/examples/usage.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
usage: py2uml [-h] [-c CONFIG] [-o OUTPUT] [-r ROOT] py_file [py_file ...]

py2puml v1.0.0
by Michelle Baert, based on work from Martin B. K. Grønholdt.

Create PlantUML classes from Python source code.

positional arguments:
py_file the Python source files to parse.

optional arguments:
-h, --help show this help message and exit
-c CONFIG, --config CONFIG
Configuration file (replace defaults)
-o OUTPUT, --output OUTPUT
The name of the ouput PlantUML file.
-r ROOT, --root ROOT Project root directory. Create namespaces from there

If no config file is provided, settings are loaded
sequentially from all available files in :
- <PROGRAM_DIR>/py2puml.ini
- <USER_HOME>/.config/py2puml.ini
- <USER_HOME>/.py2puml.ini
- <WORK_DIR>/.py2puml.ini
- <WORK_DIR>/py2puml.ini

If the provided config filename cannot be found,
the program will use no config at all.
12 changes: 7 additions & 5 deletions py2puml/py2puml.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,18 @@ def cli_parser():
# Takes a python file as a parameter.
parser = argparse.ArgumentParser(
prog='py2uml',
description='py2puml' +
' from Martin B. K. Grønholdt, v' + __version__ + ' by Michelle Baert.\n' +
'Create PlantUML classes from Python source code.',
epilog='If no config file is provided, settings are loaded \n' +
description='py2puml v' + __version__ +
'\nby Michelle Baert, based on work from Martin B. K. Grønholdt.\n\n' +
' Create PlantUML classes from Python source code.',
epilog='If no config file is provided, settings are loaded\n' +
'sequentially from all available files in :\n' +
' - <PROGRAM_DIR>/py2puml.ini\n' +
' - <USER_HOME>/.config/py2puml.ini\n' +
' - <USER_HOME>/.py2puml.ini\n' +
' - <WORK_DIR>/.py2puml.ini\n' +
' - <WORK_DIR>/py2puml.ini\n',
' - <WORK_DIR>/py2puml.ini\n' +
'\nIf the provided config filename cannot be found,\n' +
'the program will use no config at all.\n',
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('-c', '--config',
help='Configuration file (replace defaults)')
Expand Down
1 change: 0 additions & 1 deletion py2puml/tests/test_ast_visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import io
# pylint: disable= invalid-name, missing-docstring, no-self-use, too-few-public-methods

# TODO one test file per module
from ast_visitor import TreeVisitor
from puml_generator import PUML_Generator

Expand Down
2 changes: 2 additions & 0 deletions py2puml/tests/test_puml_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,9 @@ def test_folder_tree(self, gen):
def test_write_globals(self, cfg_write_globals):
dest = io.StringIO()
gen = PUML_Generator_NS(dest, root='.', config=cfg_write_globals)
gen.header()
gen.do_file('examples/example.py')
gen.footer()
assert "global_func" in gen.dest.getvalue()
assert_match_file(gen, 'examples/example_globals_NS.puml')

Expand Down
38 changes: 26 additions & 12 deletions py2puml/tests/test_py2puml.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@
# pylint: disable=invalid-name, missing-docstring

from py2puml import run, cli_parser
from version import __version__

def test_cli_usage(capsys):
cli_parser().print_help()
with open('examples/usage.txt') as f:
expected = f.read()

# Replace version number
s = "py2puml v1.0.0\n"
p = expected.find(s)
q = p+len(s)
expected = (expected[:p] + "py2puml v" + __version__ + "\n" + expected[q:])

# compare with captured output
out, err = capsys.readouterr()
assert err == ''
assert expected == out

def test_run_multiple_sources(capsys):
args = cli_parser().parse_args(
Expand All @@ -18,11 +35,11 @@ def test_run_multiple_sources(capsys):

with open('examples/py2puml_NS.puml') as f:
expected = f.read()
assert out == expected
assert expected == out

def test_run_dbpuml2sql(capsys):
args = cli_parser().parse_args(['-c', 'examples/dbpuml2sql.ini',
#'-r', '..',
#'-r', '../dbpuml2sql',
'../dbpuml2sql/dbpuml2sql.py',
'../dbpuml2sql/__init__.py',
'../dbpuml2sql/pumlreader.py',
Expand All @@ -39,18 +56,16 @@ def test_run_dbpuml2sql(capsys):

with open('examples/dbpuml2sql.puml') as f:
expected = f.read()
assert out == expected

def test_run_dbspq2puml(capsys):
assert expected == out

def test_run_dbsql2puml(capsys):
args = cli_parser().parse_args(['-c', 'examples/dbsql2puml.ini',
#'-r', '..',
# '-r', '../dbsql2puml',
'../dbsql2puml/dbsql2puml.py',
'../dbsql2puml/sql2puml.py',
'../dbsql2puml/sqlparsetables.py'])
# missing config file silently ignores any config. Call it a feature
# FIXME include package name for base classes imported directly
# workaround: add a plantuml alias in prolog or epilog
# FIXME relations between namespaces
run(args)
out, err = capsys.readouterr()

Expand All @@ -62,8 +77,7 @@ def test_run_dbspq2puml(capsys):

with open('examples/dbsql2puml.puml') as f:
expected = f.read()
assert out == expected
# FIXME arglists missing
assert expected == out

def test_run_syntax_error(capsys):
args = cli_parser().parse_args('examples/bugged.py examples/person.py'.split())
Expand All @@ -72,7 +86,7 @@ def test_run_syntax_error(capsys):

with open('examples/person.puml') as f:
expected = f.read()
assert out == expected
assert expected == out

assert err == """\
Syntax error in examples/bugged.py:6:57: while i<10 #Begin loop, repeat until there's ten numbers
Expand All @@ -86,7 +100,7 @@ def test_run_filenotfound_error(capsys):

with open('examples/person.puml') as f:
expected = f.read()
assert out == expected
assert expected == out

assert err == """\
[Errno 2] No such file or directory: 'missing.py', skipping
Expand Down
Loading

0 comments on commit 3f69975

Please sign in to comment.