Skip to content

pyreverse: use colorblind friendly default colors #8415

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .pyenchant_pylint_custom_dict.txt
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ globbing
GPL
graphname
graphviz
grey
guido's
gv
hashable
Expand Down
5 changes: 5 additions & 0 deletions doc/whatsnew/fragments/8251.breaking
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
`pyreverse` now uses a new default color palette that is more colorblind friendly.
The color scheme is taken from `Paul Tol's Notes <https://personal.sron.nl/~pault/>`_.
If you prefer other colors, you can use the `--color-palette` option to specify custom colors.

Closes #8251
27 changes: 10 additions & 17 deletions pylint/pyreverse/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,16 @@
)

DEFAULT_COLOR_PALETTE = (
"aliceblue",
"antiquewhite",
"aquamarine",
"burlywood",
"cadetblue",
"chartreuse",
"chocolate",
"coral",
"cornflowerblue",
"cyan",
"darkgoldenrod",
"darkseagreen",
"dodgerblue",
"forestgreen",
"gold",
"hotpink",
"mediumspringgreen",
# colorblind scheme taken from https://personal.sron.nl/~pault/
"#77AADD", # light blue
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"#77AADD", # light blue
# colorblind friendly palette taken from... (sorry I'm on mobile)
"#77AADD", # light blue

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it definitely makes sense to not only put the link in the news fragment, thanks for the catch!

"#99DDFF", # light cyan
"#44BB99", # mint
"#BBCC33", # pear
"#AAAA00", # olive
"#EEDD88", # light yellow
"#EE8866", # orange
"#FFAABB", # pink
"#DDDDDD", # pale grey
)

OPTIONS: Options = (
Expand Down
2 changes: 1 addition & 1 deletion pylint/pyreverse/plantuml_printer.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def emit_node(
stereotype = " << interface >>" if type_ is NodeType.INTERFACE else ""
nodetype = self.NODES[type_]
if properties.color and properties.color != self.DEFAULT_COLOR:
color = f" #{properties.color}"
color = f" #{properties.color.lstrip('#')}"
else:
color = ""
body = []
Expand Down
16 changes: 8 additions & 8 deletions tests/pyreverse/data/classes_colorized.dot
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
digraph "classes_colorized" {
rankdir=BT
charset="utf-8"
"data.clientmodule_test.Ancestor" [color="aliceblue", fontcolor="black", label=<{Ancestor|attr : str<br ALIGN="LEFT"/>cls_member<br ALIGN="LEFT"/>|get_value()<br ALIGN="LEFT"/>set_value(value)<br ALIGN="LEFT"/>}>, shape="record", style="filled"];
"data.suppliermodule_test.CustomException" [color="aliceblue", fontcolor="red", label=<{CustomException|<br ALIGN="LEFT"/>|}>, shape="record", style="filled"];
"data.suppliermodule_test.DoNothing" [color="aliceblue", fontcolor="black", label=<{DoNothing|<br ALIGN="LEFT"/>|}>, shape="record", style="filled"];
"data.suppliermodule_test.DoNothing2" [color="aliceblue", fontcolor="black", label=<{DoNothing2|<br ALIGN="LEFT"/>|}>, shape="record", style="filled"];
"data.suppliermodule_test.DoSomething" [color="aliceblue", fontcolor="black", label=<{DoSomething|my_int : Optional[int]<br ALIGN="LEFT"/>my_int_2 : Optional[int]<br ALIGN="LEFT"/>my_string : str<br ALIGN="LEFT"/>|do_it(new_int: int): int<br ALIGN="LEFT"/>}>, shape="record", style="filled"];
"data.suppliermodule_test.Interface" [color="aliceblue", fontcolor="black", label=<{Interface|<br ALIGN="LEFT"/>|<I>get_value</I>()<br ALIGN="LEFT"/><I>set_value</I>(value)<br ALIGN="LEFT"/>}>, shape="record", style="filled"];
"data.property_pattern.PropertyPatterns" [color="aliceblue", fontcolor="black", label=<{PropertyPatterns|prop1<br ALIGN="LEFT"/>prop2<br ALIGN="LEFT"/>|}>, shape="record", style="filled"];
"data.clientmodule_test.Specialization" [color="aliceblue", fontcolor="black", label=<{Specialization|TYPE : str<br ALIGN="LEFT"/>relation<br ALIGN="LEFT"/>relation2<br ALIGN="LEFT"/>top : str<br ALIGN="LEFT"/>|from_value(value: int)<br ALIGN="LEFT"/>increment_value(): None<br ALIGN="LEFT"/>transform_value(value: int): int<br ALIGN="LEFT"/>}>, shape="record", style="filled"];
"data.clientmodule_test.Ancestor" [color="#77AADD", fontcolor="black", label=<{Ancestor|attr : str<br ALIGN="LEFT"/>cls_member<br ALIGN="LEFT"/>|get_value()<br ALIGN="LEFT"/>set_value(value)<br ALIGN="LEFT"/>}>, shape="record", style="filled"];
"data.suppliermodule_test.CustomException" [color="#77AADD", fontcolor="red", label=<{CustomException|<br ALIGN="LEFT"/>|}>, shape="record", style="filled"];
"data.suppliermodule_test.DoNothing" [color="#77AADD", fontcolor="black", label=<{DoNothing|<br ALIGN="LEFT"/>|}>, shape="record", style="filled"];
"data.suppliermodule_test.DoNothing2" [color="#77AADD", fontcolor="black", label=<{DoNothing2|<br ALIGN="LEFT"/>|}>, shape="record", style="filled"];
"data.suppliermodule_test.DoSomething" [color="#77AADD", fontcolor="black", label=<{DoSomething|my_int : Optional[int]<br ALIGN="LEFT"/>my_int_2 : Optional[int]<br ALIGN="LEFT"/>my_string : str<br ALIGN="LEFT"/>|do_it(new_int: int): int<br ALIGN="LEFT"/>}>, shape="record", style="filled"];
"data.suppliermodule_test.Interface" [color="#77AADD", fontcolor="black", label=<{Interface|<br ALIGN="LEFT"/>|<I>get_value</I>()<br ALIGN="LEFT"/><I>set_value</I>(value)<br ALIGN="LEFT"/>}>, shape="record", style="filled"];
"data.property_pattern.PropertyPatterns" [color="#77AADD", fontcolor="black", label=<{PropertyPatterns|prop1<br ALIGN="LEFT"/>prop2<br ALIGN="LEFT"/>|}>, shape="record", style="filled"];
"data.clientmodule_test.Specialization" [color="#77AADD", fontcolor="black", label=<{Specialization|TYPE : str<br ALIGN="LEFT"/>relation<br ALIGN="LEFT"/>relation2<br ALIGN="LEFT"/>top : str<br ALIGN="LEFT"/>|from_value(value: int)<br ALIGN="LEFT"/>increment_value(): None<br ALIGN="LEFT"/>transform_value(value: int): int<br ALIGN="LEFT"/>}>, shape="record", style="filled"];
"data.clientmodule_test.Specialization" -> "data.clientmodule_test.Ancestor" [arrowhead="empty", arrowtail="none"];
"data.suppliermodule_test.DoNothing" -> "data.clientmodule_test.Ancestor" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="cls_member", style="solid"];
"data.suppliermodule_test.DoNothing" -> "data.clientmodule_test.Specialization" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="relation", style="solid"];
Expand Down
16 changes: 8 additions & 8 deletions tests/pyreverse/data/classes_colorized.puml
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
@startuml classes_colorized
set namespaceSeparator none
class "Ancestor" as data.clientmodule_test.Ancestor #aliceblue {
class "Ancestor" as data.clientmodule_test.Ancestor #77AADD {
attr : str
cls_member
get_value()
set_value(value)
}
class "<color:red>CustomException</color>" as data.suppliermodule_test.CustomException #aliceblue {
class "<color:red>CustomException</color>" as data.suppliermodule_test.CustomException #77AADD {
}
class "DoNothing" as data.suppliermodule_test.DoNothing #aliceblue {
class "DoNothing" as data.suppliermodule_test.DoNothing #77AADD {
}
class "DoNothing2" as data.suppliermodule_test.DoNothing2 #aliceblue {
class "DoNothing2" as data.suppliermodule_test.DoNothing2 #77AADD {
}
class "DoSomething" as data.suppliermodule_test.DoSomething #aliceblue {
class "DoSomething" as data.suppliermodule_test.DoSomething #77AADD {
my_int : Optional[int]
my_int_2 : Optional[int]
my_string : str
do_it(new_int: int) -> int
}
class "Interface" as data.suppliermodule_test.Interface #aliceblue {
class "Interface" as data.suppliermodule_test.Interface #77AADD {
{abstract}get_value()
{abstract}set_value(value)
}
class "PropertyPatterns" as data.property_pattern.PropertyPatterns #aliceblue {
class "PropertyPatterns" as data.property_pattern.PropertyPatterns #77AADD {
prop1
prop2
}
class "Specialization" as data.clientmodule_test.Specialization #aliceblue {
class "Specialization" as data.clientmodule_test.Specialization #77AADD {
TYPE : str
relation
relation2
Expand Down
8 changes: 4 additions & 4 deletions tests/pyreverse/data/packages_colorized.dot
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
digraph "packages_colorized" {
rankdir=BT
charset="utf-8"
"data" [color="aliceblue", label=<data>, shape="box", style="filled"];
"data.clientmodule_test" [color="aliceblue", label=<data.clientmodule_test>, shape="box", style="filled"];
"data.property_pattern" [color="aliceblue", label=<data.property_pattern>, shape="box", style="filled"];
"data.suppliermodule_test" [color="aliceblue", label=<data.suppliermodule_test>, shape="box", style="filled"];
"data" [color="#77AADD", label=<data>, shape="box", style="filled"];
"data.clientmodule_test" [color="#77AADD", label=<data.clientmodule_test>, shape="box", style="filled"];
"data.property_pattern" [color="#77AADD", label=<data.property_pattern>, shape="box", style="filled"];
"data.suppliermodule_test" [color="#77AADD", label=<data.suppliermodule_test>, shape="box", style="filled"];
"data.clientmodule_test" -> "data.suppliermodule_test" [arrowhead="open", arrowtail="none"];
}
8 changes: 4 additions & 4 deletions tests/pyreverse/data/packages_colorized.puml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
@startuml packages_colorized
set namespaceSeparator none
package "data" as data #aliceblue {
package "data" as data #77AADD {

}
package "data.clientmodule_test" as data.clientmodule_test #aliceblue {
package "data.clientmodule_test" as data.clientmodule_test #77AADD {

}
package "data.property_pattern" as data.property_pattern #aliceblue {
package "data.property_pattern" as data.property_pattern #77AADD {

}
package "data.suppliermodule_test" as data.suppliermodule_test #aliceblue {
package "data.suppliermodule_test" as data.suppliermodule_test #77AADD {

}
data.clientmodule_test --> data.suppliermodule_test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
@startuml classes
set namespaceSeparator none
class "CheckerCollector" as colorized.CheckerCollector #aliceblue {
class "CheckerCollector" as colorized.CheckerCollector #77AADD {
checker1
checker2
checker3
}
class "ElseifUsedChecker" as pylint.extensions.check_elif.ElseifUsedChecker #antiquewhite {
class "ElseifUsedChecker" as pylint.extensions.check_elif.ElseifUsedChecker #99DDFF {
msgs : dict
name : str
leave_module(_: nodes.Module) -> None
process_tokens(tokens: list[TokenInfo]) -> None
visit_if(node: nodes.If) -> None
}
class "ExceptionsChecker" as pylint.checkers.exceptions.ExceptionsChecker #aquamarine {
class "ExceptionsChecker" as pylint.checkers.exceptions.ExceptionsChecker #44BB99 {
msgs : dict
name : str
options : tuple
Expand All @@ -22,7 +22,7 @@ class "ExceptionsChecker" as pylint.checkers.exceptions.ExceptionsChecker #aquam
visit_raise(node: nodes.Raise) -> None
visit_tryexcept(node: nodes.TryExcept) -> None
}
class "StdlibChecker" as pylint.checkers.stdlib.StdlibChecker #aquamarine {
class "StdlibChecker" as pylint.checkers.stdlib.StdlibChecker #44BB99 {
msgs : dict[str, MessageDefinitionTuple]
name : str
deprecated_arguments(method: str) -> tuple[tuple[int | None, str], ...]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
digraph "classes" {
rankdir=BT
charset="utf-8"
"custom_colors.CheckerCollector" [color="red", fontcolor="black", label=<{CheckerCollector|checker1<br ALIGN="LEFT"/>checker2<br ALIGN="LEFT"/>checker3<br ALIGN="LEFT"/>|}>, shape="record", style="filled"];
"pylint.extensions.check_elif.ElseifUsedChecker" [color="#44BB88", fontcolor="black", label=<{ElseifUsedChecker|msgs : dict<br ALIGN="LEFT"/>name : str<br ALIGN="LEFT"/>|leave_module(_: nodes.Module): None<br ALIGN="LEFT"/>process_tokens(tokens: list[TokenInfo]): None<br ALIGN="LEFT"/>visit_if(node: nodes.If): None<br ALIGN="LEFT"/>}>, shape="record", style="filled"];
"pylint.checkers.exceptions.ExceptionsChecker" [color="yellow", fontcolor="black", label=<{ExceptionsChecker|msgs : dict<br ALIGN="LEFT"/>name : str<br ALIGN="LEFT"/>options : tuple<br ALIGN="LEFT"/>|open(): None<br ALIGN="LEFT"/>visit_binop(node: nodes.BinOp): None<br ALIGN="LEFT"/>visit_compare(node: nodes.Compare): None<br ALIGN="LEFT"/>visit_raise(node: nodes.Raise): None<br ALIGN="LEFT"/>visit_tryexcept(node: nodes.TryExcept): None<br ALIGN="LEFT"/>}>, shape="record", style="filled"];
"pylint.checkers.stdlib.StdlibChecker" [color="yellow", fontcolor="black", label=<{StdlibChecker|msgs : dict[str, MessageDefinitionTuple]<br ALIGN="LEFT"/>name : str<br ALIGN="LEFT"/>|deprecated_arguments(method: str): tuple[tuple[int | None, str], ...]<br ALIGN="LEFT"/>deprecated_classes(module: str): Iterable[str]<br ALIGN="LEFT"/>deprecated_decorators(): Iterable[str]<br ALIGN="LEFT"/>deprecated_methods(): set[str]<br ALIGN="LEFT"/>visit_boolop(node: nodes.BoolOp): None<br ALIGN="LEFT"/>visit_call(node: nodes.Call): None<br ALIGN="LEFT"/>visit_functiondef(node: nodes.FunctionDef): None<br ALIGN="LEFT"/>visit_if(node: nodes.If): None<br ALIGN="LEFT"/>visit_ifexp(node: nodes.IfExp): None<br ALIGN="LEFT"/>visit_unaryop(node: nodes.UnaryOp): None<br ALIGN="LEFT"/>}>, shape="record", style="filled"];
"pylint.checkers.exceptions.ExceptionsChecker" -> "custom_colors.CheckerCollector" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="checker1", style="solid"];
"pylint.checkers.stdlib.StdlibChecker" -> "custom_colors.CheckerCollector" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="checker3", style="solid"];
"pylint.extensions.check_elif.ElseifUsedChecker" -> "custom_colors.CheckerCollector" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="checker2", style="solid"];
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class "CheckerCollector" as custom_colors.CheckerCollector #red {
checker2
checker3
}
class "ElseifUsedChecker" as pylint.extensions.check_elif.ElseifUsedChecker #green {
class "ElseifUsedChecker" as pylint.extensions.check_elif.ElseifUsedChecker #44BB88 {
msgs : dict
name : str
leave_module(_: nodes.Module) -> None
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[testoptions]
output_formats=puml
command_line_args=-S --colorized --max-color-depth=2 --color-palette=red,green,yellow
output_formats=puml,dot
command_line_args=-S --colorized --max-color-depth=2 --color-palette=red,#44BB88,yellow