diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 255694de..7faf6eb5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,50 +1,33 @@ repos: -- repo: https://github.com/psf/black - rev: 24.2.0 - hooks: - - id: black - args: [--safe, --quiet] -- repo: https://github.com/asottile/blacken-docs - rev: 1.16.0 - hooks: - - id: blacken-docs - additional_dependencies: [black==23.1.0] +- repo: https://github.com/PyCQA/autoflake + rev: v2.3.0 + hooks: + - id: autoflake + name: autoflake + entry: autoflake --in-place --remove-all-unused-imports + language: python + files: \.py$ +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: "v0.2.2" + hooks: + - id: ruff-format - repo: https://github.com/pre-commit/mirrors-prettier rev: "v4.0.0-alpha.8" hooks: - id: prettier types_or: [css, javascript] -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 - hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: debug-statements -- repo: https://github.com/PyCQA/autoflake - rev: v2.3.0 - hooks: - - id: autoflake - name: autoflake - entry: autoflake --in-place --remove-all-unused-imports - language: python - files: \.py$ -- repo: https://github.com/asottile/reorder-python-imports - rev: v3.12.0 - hooks: - - id: reorder-python-imports - args: ['--application-directories=.:src', --py36-plus] -- repo: local - hooks: - - id: rst - name: rst - entry: rst-lint --encoding utf-8 - files: ^(HISTORY.rst|README.rst)$ - language: python - additional_dependencies: [pygments, restructuredtext_lint] -- repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.8.0 - hooks: - - id: mypy - files: ^(src/|tests/) - args: [] - additional_dependencies: [types-attrs] +- repo: local + hooks: + - id: rst + name: rst + entry: rst-lint --encoding utf-8 + files: ^(HISTORY.rst|README.rst)$ + language: python + additional_dependencies: [pygments, restructuredtext_lint] +- repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.8.0 + hooks: + - id: mypy + files: ^(src/|tests/) + args: [] + additional_dependencies: [types-attrs] diff --git a/docs/conf.py b/docs/conf.py index e225b25f..6e470a06 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -16,5 +16,5 @@ "sphinx.ext.autosummary", ] -autodoc_default_flags = ['members'] +autodoc_default_flags = ["members"] autosummary_generate = True diff --git a/examples/drag_and_drop/main.py b/examples/drag_and_drop/main.py index 1ab79553..f85f0b42 100644 --- a/examples/drag_and_drop/main.py +++ b/examples/drag_and_drop/main.py @@ -22,7 +22,7 @@ def create_drag_button(text, qmx_style, parent=None): button.setText(text) # # You can set an icon to the button with: # button.setIcon(...) - button.setProperty('qmx_style', qmx_style) + button.setProperty("qmx_style", qmx_style) button.setToolTip("Drag me into the graph widget") return button @@ -35,14 +35,14 @@ class DragButton(QPushButton): def mousePressEvent(self, event): mime_data = qmxgraph.mime.create_qt_mime_data( { - 'vertices': [ + "vertices": [ { - 'dx': 0, - 'dy': 0, - 'width': 120, - 'height': 40, - 'label': self.text(), - 'style': self.property('qmx_style'), + "dx": 0, + "dy": 0, + "width": 120, + "height": 40, + "label": self.text(), + "style": self.property("qmx_style"), } ] } @@ -63,7 +63,7 @@ class DragAndDropWindow(QMainWindow): def __init__(self): QMainWindow.__init__(self) - self.setProperty('name', 'adas') + self.setProperty("name", "adas") self.setMinimumSize(QSize(640, 480)) self.setWindowTitle("Drag&Drop Styles") @@ -72,9 +72,9 @@ def __init__(self): self.button_pane = QWidget(self) self.button_pane.setEnabled(False) - red_button = create_drag_button('RED', 'fillColor=#D88', self.button_pane) - green_button = create_drag_button('GREEN', 'fillColor=#8D8', self.button_pane) - blue_button = create_drag_button('BLUE', 'fillColor=#88D', self.button_pane) + red_button = create_drag_button("RED", "fillColor=#D88", self.button_pane) + green_button = create_drag_button("GREEN", "fillColor=#8D8", self.button_pane) + blue_button = create_drag_button("BLUE", "fillColor=#88D", self.button_pane) self.graph_widget = QmxGraph(parent=central_widget) self.events_bridge = self.create_events_bridge() @@ -96,23 +96,23 @@ def create_events_bridge(self): # Based in `EventsBridge` docstring. def on_cells_added_handler(cell_ids): - print(f'added {cell_ids}') + print(f"added {cell_ids}") qmx = widget.api for cid in cell_ids: label = qmx.get_label(cid) - qmx.set_label(cid, f'{label} ({cid})') + qmx.set_label(cid, f"{label} ({cid})") def on_terminal_changed_handler(cell_id, terminal_type, new_terminal_id, old_terminal_id): print( - f'{terminal_type} of {cell_id} changed from' - f' {old_terminal_id} to {new_terminal_id}' + f"{terminal_type} of {cell_id} changed from" + f" {old_terminal_id} to {new_terminal_id}" ) def on_cells_removed_handler(cell_ids): - print(f'removed {cell_ids}') + print(f"removed {cell_ids}") def on_cells_bounds_changed_handler(changed_cell_bounds): - print(f'cells bounds changed {changed_cell_bounds}') + print(f"cells bounds changed {changed_cell_bounds}") widget = self.graph_widget events_bridge = widget.events_bridge diff --git a/examples/styles/main.py b/examples/styles/main.py index a8861f4f..c7e7a6bc 100644 --- a/examples/styles/main.py +++ b/examples/styles/main.py @@ -19,16 +19,16 @@ def __init__(self): self.setWindowTitle("Qmx Styles") styles_cfg = { - 'round_node': { - 'shape': 'ellipse', - 'fill_color': '#D88', - 'vertical_label_position': 'bottom', - 'vertical_align': 'top', + "round_node": { + "shape": "ellipse", + "fill_color": "#D88", + "vertical_label_position": "bottom", + "vertical_align": "top", }, - 'bold_edge': { - 'end_arrow': 'classic', - 'shape': 'connector', - 'stroke_width': 5.0, + "bold_edge": { + "end_arrow": "classic", + "shape": "connector", + "stroke_width": 5.0, }, } @@ -48,7 +48,7 @@ def graph_load_handler(self, is_loaded): width=100, height=50, label="BBB", - style='round_node', + style="round_node", ) # Style by explicit values. v2_id = qmx.insert_vertex( @@ -57,11 +57,11 @@ def graph_load_handler(self, is_loaded): width=50, height=100, label="CCC", - style='fillColor=#8D8', + style="fillColor=#8D8", ) - qmx.insert_edge(source_id=v0_id, target_id=v1_id, label='normal') - qmx.insert_edge(source_id=v1_id, target_id=v2_id, label='bold', style='bold_edge') + qmx.insert_edge(source_id=v0_id, target_id=v1_id, label="normal") + qmx.insert_edge(source_id=v1_id, target_id=v2_id, label="bold", style="bold_edge") if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index 10572daa..8efe2d9e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,17 @@ -[tool.black] +[build-system] +build-backend = "setuptools.build_meta" +requires = [ + "setuptools", + "setuptools-scm", +] + + +[tool.ruff] +src = ["src"] line-length = 100 -skip-string-normalization = true + +[tool.ruff.format] +docstring-code-format = true + +[tool.ruff.lint.isort] +force-single-line = true diff --git a/scripts/svg_to_stencil.py b/scripts/svg_to_stencil.py index 49aef10a..226f66b1 100644 --- a/scripts/svg_to_stencil.py +++ b/scripts/svg_to_stencil.py @@ -50,36 +50,36 @@ def __init__(self, svg_path): self.svg_path = svg_path def read(self): - ident = ' ' * 4 + ident = " " * 4 tree = xml.etree.ElementTree.parse(self.svg_path) root = tree.getroot() ns = { - 'default': "http://www.w3.org/2000/svg", - 'sodipodi': "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd", - 'inkscape': "http://www.inkscape.org/namespaces/inkscape", + "default": "http://www.w3.org/2000/svg", + "sodipodi": "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd", + "inkscape": "http://www.inkscape.org/namespaces/inkscape", } - sodipodi_docname = '{{{}}}docname'.format(ns['sodipodi']) + sodipodi_docname = "{{{}}}docname".format(ns["sodipodi"]) if sodipodi_docname in root.attrib: - name = root.attrib[sodipodi_docname].replace('.svg', '') + name = root.attrib[sodipodi_docname].replace(".svg", "") else: - name = os.path.basename(self.svg_path).replace('.svg', '') - width = root.attrib['width'].replace('px', '') - height = root.attrib['height'].replace('px', '') + name = os.path.basename(self.svg_path).replace(".svg", "") + width = root.attrib["width"].replace("px", "") + height = root.attrib["height"].replace("px", "") - default_ns = '{{{}}}'.format(ns['default']) + default_ns = "{{{}}}".format(ns["default"]) svg_elements = (c for c in root if c.tag.startswith(default_ns)) drawing_cmds = [] for svg_element in svg_elements: - tag = svg_element.tag.replace(default_ns, '') + tag = svg_element.tag.replace(default_ns, "") parser = None - if tag == 'path': + if tag == "path": parser = PathParser(ident) - elif tag == 'polygon': + elif tag == "polygon": parser = PolygonParser(ident) - elif tag == 'rect': + elif tag == "rect": parser = RectParser(ident) if parser is not None: @@ -93,19 +93,19 @@ def read(self): name=name, width=width, height=height, - drawing='\n'.join( - '{}{}'.format(ident * 2, c) for c in itertools.chain.from_iterable(drawing_cmds) + drawing="\n".join( + "{}{}".format(ident * 2, c) for c in itertools.chain.from_iterable(drawing_cmds) ), ) def _parse_size(self, value, unit): - return value.replace(unit, '') + return value.replace(unit, "") class DrawingParser: __metaclass__ = abc.ABCMeta - def __init__(self, ident=''): + def __init__(self, ident=""): self.ident = ident self.cmds = [] self.styles = {} @@ -124,16 +124,16 @@ def _add_style_commands(self, value): added = self.styles tag_map = { - 'fill': ('fillcolor', 'color'), - 'stroke': ('strokecolor', 'color'), - 'stroke-width': ('strokewidth', 'width'), - 'stroke-miterlimit': ('miterlimit', 'limit'), + "fill": ("fillcolor", "color"), + "stroke": ("strokecolor", "color"), + "stroke-width": ("strokewidth", "width"), + "stroke-miterlimit": ("miterlimit", "limit"), } - if 'style' in value.attrib: - style = value.attrib['style'] - for style_attr in style.split(';'): - style_tag, style_value = style_attr.split(':') + if "style" in value.attrib: + style = value.attrib["style"] + for style_attr in style.split(";"): + style_tag, style_value = style_attr.split(":") if style_tag in tag_map: el_name, attr = tag_map[style_tag] @@ -147,18 +147,18 @@ def _add_style_commands(self, value): added[style_tag] = self._add_if_not_none(el_name, attr, value.attrib[style_tag]) def _add_fill_stroke_command(self): - has_stroke = self.styles.get('stroke') - has_fill = self.styles.get('fill') + has_stroke = self.styles.get("stroke") + has_fill = self.styles.get("fill") if has_fill and has_stroke: - self.cmds.append('') + self.cmds.append("") elif has_fill: - self.cmds.append('') + self.cmds.append("") elif has_stroke: - self.cmds.append('') + self.cmds.append("") def _add_if_not_none(self, el_name, attr, value): added = False - if value != 'none': + if value != "none": self.cmds.append('<{} {}="{}"/>'.format(el_name, attr, value)) added = True return added @@ -174,18 +174,18 @@ def _add_drawing_commands(self, value): # * for each subsequent coordinate pair, perform an absolute `lineto` # operation to that coordinate pair # * perform a `closepath` command - points = value.attrib['points'] + points = value.attrib["points"] pos = 0 - self.cmds.append('') - m = re.match(r'(\d+(?:\.\d+)?),(\d+(?:\.\d+)?) +', points[pos:]) + self.cmds.append("") + m = re.match(r"(\d+(?:\.\d+)?),(\d+(?:\.\d+)?) +", points[pos:]) x0 = m.group(1) y0 = m.group(2) self.cmds.append('{}'.format(self.ident, x0, y0)) pos += len(m.group(0)) while True: - m = re.match(r'(\d+(?:\.\d+)?),(\d+(?:\.\d+)?) +', points[pos:]) + m = re.match(r"(\d+(?:\.\d+)?),(\d+(?:\.\d+)?) +", points[pos:]) if m is None: break @@ -194,12 +194,12 @@ def _add_drawing_commands(self, value): # Close polygon self.cmds.append('{}'.format(self.ident, x0, y0)) - self.cmds.append('') + self.cmds.append("") class PathParser(DrawingParser): def _add_drawing_commands(self, value): - drawing = value.attrib['d'] + drawing = value.attrib["d"] state = self.wait_command_state pos = 0 @@ -207,23 +207,23 @@ def _add_drawing_commands(self, value): while state is not None: state, pos = state(drawing, pos) - self.cmds.insert(cmds_offset, '') - self.cmds.append('') + self.cmds.insert(cmds_offset, "") + self.cmds.append("") def wait_command_state(self, value, pos): if pos >= len(value): return None, pos - elif value[pos] == 'M': + elif value[pos] == "M": return self.move_state, pos + 1 - elif value[pos] == 'C': + elif value[pos] == "C": return self.curve_state, pos + 1 - elif value[pos] == ' ': + elif value[pos] == " ": return self.wait_command_state, pos + 1 raise ValueError("Could not parse {}".format(value[pos])) def move_state(self, value, pos): - m = re.match(r' +(\d+(\.\d+)?),(\d+(\.\d+)?) +', value[pos:]) + m = re.match(r" +(\d+(\.\d+)?),(\d+(\.\d+)?) +", value[pos:]) if m is None: raise ValueError("Could not parse {}".format(value[pos])) @@ -233,22 +233,22 @@ def move_state(self, value, pos): def curve_state(self, svg, pos): index = 1 - cmd = '' + cmd = "" while True: - m = re.match(r' *(\d+(\.\d+)?),(\d+(\.\d+)?) *(Z)?', svg[pos:]) + m = re.match(r" *(\d+(\.\d+)?),(\d+(\.\d+)?) *(Z)?", svg[pos:]) if m is None: raise ValueError("Could not parse {}".format(svg[pos])) pos += len(m.group(0)) if index == 1: - cmd = '{} 3: index = 1 - cmd += '/>' + cmd += "/>" self.cmds.append(cmd) if m.group(5) is not None: @@ -262,20 +262,20 @@ def curve_state(self, svg, pos): class RectParser(DrawingParser): def _add_drawing_commands(self, value): svg_to_stencil_attr_map = { - 'x': 'x', - 'y': 'y', - 'width': 'w', - 'height': 'h', + "x": "x", + "y": "y", + "width": "w", + "height": "h", } - rect_stencil_tag = ' @@ -293,20 +293,20 @@ def _add_drawing_commands(self, value): -''' +""" -if __name__ == '__main__': +if __name__ == "__main__": import argparse arg_parser = argparse.ArgumentParser( - description='Converts a SVG file to a stencil file ' 'compatible with mxGraph.' + description="Converts a SVG file to a stencil file " "compatible with mxGraph." ) arg_parser.add_argument( - 'svg', - metavar='SVG_FILE', + "svg", + metavar="SVG_FILE", nargs=1, - help='A SVG file', + help="A SVG file", ) args = sys.argv[1:] args = arg_parser.parse_args(args=args) diff --git a/setup.py b/setup.py index bd511b0e..f84dc7e0 100644 --- a/setup.py +++ b/setup.py @@ -3,41 +3,41 @@ from setuptools import find_packages from setuptools import setup -with open('README.rst') as readme_file: +with open("README.rst") as readme_file: readme = readme_file.read() -with open('HISTORY.rst') as history_file: +with open("HISTORY.rst") as history_file: history = history_file.read() requirements = [] setup( - name='qmxgraph', + name="qmxgraph", use_scm_version=True, setup_requires=["setuptools_scm"], description="A Qt graph drawing widget using JavaScript's mxGraph " "library.", - long_description=readme + '\n\n' + history, + long_description=readme + "\n\n" + history, author="Rafael Bertoldi", - author_email='tochaman@gmail.com', - url='https://github.com/ESSS/qmxgraph', + author_email="tochaman@gmail.com", + url="https://github.com/ESSS/qmxgraph", packages=find_packages(where="src"), package_dir={"": "src"}, install_requires=requirements, license="MIT license", zip_safe=False, - keywords='qmxgraph', + keywords="qmxgraph", classifiers=[ - 'Development Status :: 4 - Beta', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', - 'Natural Language :: English', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Topic :: Scientific/Engineering :: Visualization', - 'Topic :: Software Development :: User Interfaces', + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Topic :: Scientific/Engineering :: Visualization", + "Topic :: Software Development :: User Interfaces", ], - test_suite='tests', + test_suite="tests", tests_require=[], ) diff --git a/src/qmxgraph/_cherrypy_server.py b/src/qmxgraph/_cherrypy_server.py index eaf45993..49febee5 100644 --- a/src/qmxgraph/_cherrypy_server.py +++ b/src/qmxgraph/_cherrypy_server.py @@ -38,7 +38,7 @@ def start(self, page, config): cherrypy_server = Process(target=_do_start_server, args=(q, page, config)) cherrypy_server.start() - if sys.platform.startswith('win'): # pragma: no cover + if sys.platform.startswith("win"): # pragma: no cover timeout = 0.1 fail_on_timeout = False else: @@ -48,9 +48,9 @@ def start(self, page, config): try: error_msg = q.get(timeout=timeout) except queue.Empty: # pragma: no cover - address = '{}:{}'.format( - config['global']['server.socket_host'], - config['global']['server.socket_port'], + address = "{}:{}".format( + config["global"]["server.socket_host"], + config["global"]["server.socket_port"], ) msg = "Server unable to start at {} after a timeout of {} seconds" assert not fail_on_timeout, msg.format(address, timeout) @@ -79,7 +79,7 @@ def stop(self): # If already dead for any reason, just let it go if e.winerror != 5: raise - if not sys.platform.startswith('win'): + if not sys.platform.startswith("win"): os.waitpid(self.server_pid, 0) self.server_pid = None @@ -106,12 +106,12 @@ def single_shot(self, page, config): def _do_start_server(queue, page, config): import cherrypy - if not sys.platform.startswith('win'): + if not sys.platform.startswith("win"): # 'main' event is more reliable than 'start' event, as 'start' is # fired just BEFORE server started, so it may not be necessarily be # ready for access yet. 'main' though is only fired during server # loop so it is already set up when reaches that point. - channel = 'main' + channel = "main" def callback(): cherrypy.engine.unsubscribe(channel, callback) diff --git a/src/qmxgraph/_web_view.py b/src/qmxgraph/_web_view.py index 936dc030..6aa779b2 100644 --- a/src/qmxgraph/_web_view.py +++ b/src/qmxgraph/_web_view.py @@ -86,11 +86,11 @@ def load_graph(self, options: GraphOptions, styles: GraphStyles, stencils): options=options, styles=styles, stencils=stencils, - mxgraph_path=':/mxgraph', - own_path=':/qmxgraph', + mxgraph_path=":/mxgraph", + own_path=":/qmxgraph", ) self._view_state = ViewState.LoadingGraph - self.setHtml(html, baseUrl=QUrl('qrc:/')) + self.setHtml(html, baseUrl=QUrl("qrc:/")) def blank(self): """ @@ -98,7 +98,7 @@ def blank(self): content. """ self._view_state = ViewState.LoadingBlank - self.setHtml('') + self.setHtml("") def closeEvent(self, event: QCloseEvent) -> None: self.stop() diff --git a/src/qmxgraph/api.py b/src/qmxgraph/api.py index a03d3bf9..0362300a 100644 --- a/src/qmxgraph/api.py +++ b/src/qmxgraph/api.py @@ -17,18 +17,18 @@ class QmxGraphApi(object): graph drawing library. """ - SOURCE_TERMINAL_CELL = 'source' - TARGET_TERMINAL_CELL = 'target' - - LAYOUT_ORGANIC = 'organic' - LAYOUT_COMPACT = 'compact' - LAYOUT_CIRCLE = 'circle' - LAYOUT_COMPACT_TREE = 'compact_tree' - LAYOUT_EDGE_LABEL = 'edge_label' - LAYOUT_PARALLEL_EDGE = 'parallel_edge' - LAYOUT_PARTITION = 'partition' - LAYOUT_RADIAL_TREE = 'radial_tree' - LAYOUT_STACK = 'stack' + SOURCE_TERMINAL_CELL = "source" + TARGET_TERMINAL_CELL = "target" + + LAYOUT_ORGANIC = "organic" + LAYOUT_COMPACT = "compact" + LAYOUT_CIRCLE = "circle" + LAYOUT_COMPACT_TREE = "compact_tree" + LAYOUT_EDGE_LABEL = "edge_label" + LAYOUT_PARALLEL_EDGE = "parallel_edge" + LAYOUT_PARTITION = "partition" + LAYOUT_RADIAL_TREE = "radial_tree" + LAYOUT_STACK = "stack" def __init__(self, graph, call_context_manager_factory): """ @@ -77,7 +77,7 @@ def insert_vertex( :return: Id of new vertex. """ return self.call_api( - 'insertVertex', x, y, width, height, label, style, tags, id, adjust_xy_coordinates + "insertVertex", x, y, width, height, label, style, tags, id, adjust_xy_coordinates ) def insert_port( @@ -104,7 +104,7 @@ def insert_port( interaction with cells in a graph. """ return self.call_api( - 'insertPort', vertex_id, port_name, x, y, width, height, label, style, tags + "insertPort", vertex_id, port_name, x, y, width, height, label, style, tags ) def insert_edge( @@ -141,7 +141,7 @@ def insert_edge( :return: Id of new edge. """ return self.call_api( - 'insertEdge', + "insertEdge", source_id, target_id, label, @@ -178,7 +178,7 @@ def insert_decoration(self, x, y, width, height, label, style=None, tags=None, i :rtype: str :return: Id of new decoration. """ - return self.call_api('insertDecoration', x, y, width, height, label, style, tags, id) + return self.call_api("insertDecoration", x, y, width, height, label, style, tags, id) def insert_decoration_on_edge( self, edge_id, position, width, height, label, style=None, tags=None, id=None @@ -209,7 +209,7 @@ def insert_decoration_on_edge( :return: Id of new decoration. """ return self.call_api( - 'insertDecorationOnEdge', edge_id, position, width, height, label, style, tags, id + "insertDecorationOnEdge", edge_id, position, width, height, label, style, tags, id ) def insert_table( @@ -247,7 +247,7 @@ def insert_table( contents = decoration_contents.asdict(contents) return self.call_api( - 'insertTable', x, y, width, contents, title, tags, style, parent_id, id + "insertTable", x, y, width, contents, title, tags, style, parent_id, id ) def update_table(self, table_id, contents, title): @@ -262,7 +262,7 @@ def update_table(self, table_id, contents, title): from . import decoration_contents contents = decoration_contents.asdict(contents) - self.call_api_async('updateTable', table_id, contents, title) + self.call_api_async("updateTable", table_id, contents, title) def update_port( self, @@ -277,38 +277,38 @@ def update_port( tags=None, ): self.call_api_async( - 'updatePort', vertex_id, port_name, x, y, width, height, label, style, tags + "updatePort", vertex_id, port_name, x, y, width, height, label, style, tags ) def get_port_names(self, vertex_id): - return self.call_api('getPortNames', vertex_id) + return self.call_api("getPortNames", vertex_id) def group(self): """ Create a group with currently selected cells in graph. Edges connected between selected vertices are automatically also included in group. """ - return self.call_api('group') + return self.call_api("group") def ungroup(self): """ Ungroup currently selected group. """ - return self.call_api('ungroup') + return self.call_api("ungroup") def toggle_outline(self): """ Outline is a small window that shows an overview of graph. It usually starts disabled and can be shown on demand. """ - self.call_api_async('toggleOutline') + self.call_api_async("toggleOutline") def toggle_grid(self): """ The grid in background of graph helps aligning cells inside graph. It usually starts enabled and can be hidden on demand. """ - self.call_api_async('toggleGrid') + self.call_api_async("toggleGrid") def toggle_snap(self): """ @@ -317,7 +317,7 @@ def toggle_snap(self): Note that if grid is hidden this feature is also disabled. """ - self.call_api_async('toggleSnap') + self.call_api_async("toggleSnap") def get_cell_id_at(self, x, y): """ @@ -328,7 +328,7 @@ def get_cell_id_at(self, x, y): :rtype: str|None :return: Id of cell if any given position, otherwise returns None. """ - return self.call_api('getCellIdAt', x, y) + return self.call_api("getCellIdAt", x, y) def get_decoration_parent_cell_id(self, cell_id): """ @@ -339,7 +339,7 @@ def get_decoration_parent_cell_id(self, cell_id): :rtype: str :return: Id of the edge containg the given decoration. """ - return self.call_api('getDecorationParentCellId', cell_id) + return self.call_api("getDecorationParentCellId", cell_id) def has_cell(self, cell_id): """ @@ -349,7 +349,7 @@ def has_cell(self, cell_id): :rtype: bool :return: True if cell exists. """ - return self.call_api('hasCell', cell_id) + return self.call_api("hasCell", cell_id) def has_port(self, cell_id, port_name): """ @@ -360,7 +360,7 @@ def has_port(self, cell_id, port_name): :rtype: bool :return: True if the port exists. """ - return self.call_api('hasPort', cell_id, port_name) + return self.call_api("hasPort", cell_id, port_name) def get_cell_type(self, cell_id): """ @@ -375,7 +375,7 @@ def get_cell_type(self, cell_id): It raises if none of these types are a match. """ - return self.call_api('getCellType', cell_id) + return self.call_api("getCellType", cell_id) def get_geometry(self, cell_id): """ @@ -391,7 +391,7 @@ def get_geometry(self, cell_id): - height; """ - return self.call_api('getGeometry', cell_id) + return self.call_api("getGeometry", cell_id) def get_terminal_points(self, cell_id): """ @@ -408,7 +408,7 @@ def get_terminal_points(self, cell_id): - the target y coordinate; """ - return self.call_api('getEdgeTerminalPoints', cell_id) + return self.call_api("getEdgeTerminalPoints", cell_id) def get_decoration_position(self, cell_id): """ @@ -419,7 +419,7 @@ def get_decoration_position(self, cell_id): :return: Returns an a normalized number between [0, 1] representing the position of the decoration along the parent edge. """ - return self.call_api('getDecorationPosition', cell_id) + return self.call_api("getDecorationPosition", cell_id) def set_decoration_position(self, cell_id, position): """ @@ -429,7 +429,7 @@ def set_decoration_position(self, cell_id, position): :param float position: A normalized number between [0, 1] representing the position of the decoration along the parent edge. """ - return self.call_api('setDecorationPosition', cell_id, position) + return self.call_api("setDecorationPosition", cell_id, position) def set_visible(self, cell_id, visible): """ @@ -438,7 +438,7 @@ def set_visible(self, cell_id, visible): :param str cell_id: Id of a cell in graph. :param bool visible: If visible or not. """ - return self.call_api('setVisible', cell_id, visible) + return self.call_api("setVisible", cell_id, visible) def is_visible(self, cell_id): """ @@ -446,7 +446,7 @@ def is_visible(self, cell_id): :param str cell_id: Id of a cell in graph. """ - return self.call_api('isVisible', cell_id) + return self.call_api("isVisible", cell_id) def set_port_visible(self, cell_id, port_name, visible): """ @@ -456,7 +456,7 @@ def set_port_visible(self, cell_id, port_name, visible): :param str port_name: Name of a port in the cell. :param bool visible: If visible or not. """ - return self.call_api('setPortVisible', cell_id, port_name, visible) + return self.call_api("setPortVisible", cell_id, port_name, visible) def is_port_visible(self, cell_id, port_name): """ @@ -465,7 +465,7 @@ def is_port_visible(self, cell_id, port_name): :param str cell_id: Id of a cell in graph. :param bool port_name: Name of a port in the cell. """ - return self.call_api('isPortVisible', cell_id, port_name) + return self.call_api("isPortVisible", cell_id, port_name) def set_connectable(self, cell_id, connectable): """ @@ -474,7 +474,7 @@ def set_connectable(self, cell_id, connectable): :param str cell_id: Id of a cell in graph. :param bool connectable: If connectable or not. """ - self.call_api('setConnectable', cell_id, connectable) + self.call_api("setConnectable", cell_id, connectable) def is_connectable(self, cell_id): """ @@ -482,31 +482,31 @@ def is_connectable(self, cell_id): :param str cell_id: Id of a cell in graph. """ - return self.call_api('isConnectable', cell_id) + return self.call_api("isConnectable", cell_id) def zoom_in(self): """ Zoom in the graph. """ - self.call_api_async('zoomIn') + self.call_api_async("zoomIn") def zoom_out(self): """ Zoom out the graph. """ - self.call_api_async('zoomOut') + self.call_api_async("zoomOut") def reset_zoom(self): """ Reset graph's zoom. """ - self.call_api_async('resetZoom') + self.call_api_async("resetZoom") def fit(self): """ Rescale the graph to fit in the container. """ - self.call_api_async('fit') + self.call_api_async("fit") def get_zoom_scale(self): """ @@ -514,7 +514,7 @@ def get_zoom_scale(self): :rtype: float """ - return self.call_api('getZoomScale') + return self.call_api("getZoomScale") def get_scale_and_translation(self): """ @@ -531,7 +531,7 @@ def get_scale_and_translation(self): supplied to :func:`QmxGraphApi.set_scale_and_translation` to set the scale and translation to a previous value. """ - return tuple(self.call_api('getScaleAndTranslation')) + return tuple(self.call_api("getScaleAndTranslation")) def set_scale_and_translation(self, scale, x, y): """ @@ -542,7 +542,7 @@ def set_scale_and_translation(self, scale, x, y): (0 = origin). :param float y: The new graph's scale along the Y axis (0 = origin}. """ - return self.call_api('setScaleAndTranslation', scale, x, y) + return self.call_api("setScaleAndTranslation", scale, x, y) def set_selected_cells(self, cell_ids): """ @@ -550,7 +550,7 @@ def set_selected_cells(self, cell_ids): :param list[str] cell_ids: """ - return self.call_api('setSelectedCells', cell_ids) + return self.call_api("setSelectedCells", cell_ids) def get_selected_cells(self): """ @@ -558,7 +558,7 @@ def get_selected_cells(self): :rtype: list[str] """ - return self.call_api('getSelectedCells') + return self.call_api("getSelectedCells") def remove_cells(self, cell_ids, ignore_missing_cells=False): """ @@ -568,7 +568,7 @@ def remove_cells(self, cell_ids, ignore_missing_cells=False): :param bool ignore_missing_cells: Ids of non existent cells are ignored instead raising an error. """ - return self.call_api('removeCells', cell_ids, ignore_missing_cells) + return self.call_api("removeCells", cell_ids, ignore_missing_cells) def remove_port(self, vertex_id, port_name): """ @@ -578,7 +578,7 @@ def remove_port(self, vertex_id, port_name): :param str vertex_id: The id of the parent vertex. :param str port_name: The port's name to remove. """ - return self.call_api('removePort', vertex_id, port_name) + return self.call_api("removePort", vertex_id, port_name) def register_double_click_handler(self, handler): """ @@ -595,7 +595,7 @@ def register_double_click_handler(self, handler): object that is going to be used as callback to event. Receives a str with double clicked cell id as only argument. """ - return self.call_api('registerDoubleClickHandler', qmxgraph.js.Variable(handler)) + return self.call_api("registerDoubleClickHandler", qmxgraph.js.Variable(handler)) def register_popup_menu_handler(self, handler): """ @@ -615,7 +615,7 @@ def register_popup_menu_handler(self, handler): screen coordinates and Y coordinate in screen coordinates as its three arguments. """ - return self.call_api('registerPopupMenuHandler', qmxgraph.js.Variable(handler)) + return self.call_api("registerPopupMenuHandler", qmxgraph.js.Variable(handler)) def register_label_changed_handler(self, handler): """ @@ -627,7 +627,7 @@ def register_label_changed_handler(self, handler): object that is going to be used as callback to event. Receives, respectively, cell id, new label and old label as arguments. """ - return self.call_api('registerLabelChangedHandler', qmxgraph.js.Variable(handler)) + return self.call_api("registerLabelChangedHandler", qmxgraph.js.Variable(handler)) def register_cells_added_handler(self, handler): """ @@ -639,7 +639,7 @@ def register_cells_added_handler(self, handler): object that is going to be used as callback to event. Receives a `QVariantList` of added cell ids as only argument. """ - return self.call_api('registerCellsAddedHandler', qmxgraph.js.Variable(handler)) + return self.call_api("registerCellsAddedHandler", qmxgraph.js.Variable(handler)) def register_cells_removed_handler(self, handler): """ @@ -651,7 +651,7 @@ def register_cells_removed_handler(self, handler): object that is going to be used as callback to event. Receives a `QVariantList` of removed cell ids as only argument. """ - return self.call_api('registerCellsRemovedHandler', qmxgraph.js.Variable(handler)) + return self.call_api("registerCellsRemovedHandler", qmxgraph.js.Variable(handler)) def register_selection_changed_handler(self, handler): """ @@ -661,7 +661,7 @@ def register_selection_changed_handler(self, handler): that is going to be used as callback to event. Receives an list of str with selected cells ids as only argument. """ - return self.call_api('registerSelectionChangedHandler', qmxgraph.js.Variable(handler)) + return self.call_api("registerSelectionChangedHandler", qmxgraph.js.Variable(handler)) def register_terminal_changed_handler(self, handler): """ @@ -673,7 +673,7 @@ def register_terminal_changed_handler(self, handler): is the source (or target), id of the net terminal, id of the old terminal. """ - return self.call_api('registerTerminalChangedHandler', qmxgraph.js.Variable(handler)) + return self.call_api("registerTerminalChangedHandler", qmxgraph.js.Variable(handler)) def register_terminal_with_port_changed_handler(self, handler): """ @@ -687,7 +687,7 @@ def register_terminal_with_port_changed_handler(self, handler): id of the old terminal. """ return self.call_api( - 'registerTerminalWithPortChangedHandler', + "registerTerminalWithPortChangedHandler", qmxgraph.js.Variable(handler), ) @@ -698,7 +698,7 @@ def register_view_update_handler(self, handler): that is going to be used as callback to event. Receives, respectively, graph dump and graph scale and translation. """ - return self.call_api('registerViewUpdateHandler', qmxgraph.js.Variable(handler)) + return self.call_api("registerViewUpdateHandler", qmxgraph.js.Variable(handler)) def register_cells_bounds_changed_handler(self, handler): """ @@ -708,7 +708,7 @@ def register_cells_bounds_changed_handler(self, handler): a map of cell id to a map describing the cell bounds. """ - return self.call_api('registerBoundsChangedHandler', qmxgraph.js.Variable(handler)) + return self.call_api("registerBoundsChangedHandler", qmxgraph.js.Variable(handler)) def resize_container(self, width, height): """ @@ -722,7 +722,7 @@ def resize_container(self, width, height): :param int width: New width. :param int height: New height. """ - self.call_api_async('resizeContainer', width, height) + self.call_api_async("resizeContainer", width, height) def get_label(self, cell_id): """ @@ -732,7 +732,7 @@ def get_label(self, cell_id): :rtype: str :return: Label of cell. """ - return self.call_api('getLabel', cell_id) + return self.call_api("getLabel", cell_id) def set_label(self, cell_id, label): """ @@ -741,7 +741,7 @@ def set_label(self, cell_id, label): :param str cell_id: Id of a cell in graph. :param str label: New label. """ - return self.call_api('setLabel', cell_id, label) + return self.call_api("setLabel", cell_id, label) def set_style(self, cell_id, style): """ @@ -750,7 +750,7 @@ def set_style(self, cell_id, style): :param str cell_id: Id of a cell in graph. :param str style: Name of a style or an inline style. """ - return self.call_api('setStyle', cell_id, style) + return self.call_api("setStyle", cell_id, style) def get_style(self, cell_id): """ @@ -760,7 +760,7 @@ def get_style(self, cell_id): :rtype: str :return: Name of a style or an inline style. """ - return self.call_api('getStyle', cell_id) + return self.call_api("getStyle", cell_id) def set_tag(self, cell_id, tag_name, tag_value): """ @@ -770,7 +770,7 @@ def set_tag(self, cell_id, tag_name, tag_value): :param str tag_name: Name of tag. :param str tag_value: Value of tag. """ - return self.call_api('setTag', cell_id, tag_name, tag_value) + return self.call_api("setTag", cell_id, tag_name, tag_value) def get_tag(self, cell_id, tag_name): """ @@ -781,7 +781,7 @@ def get_tag(self, cell_id, tag_name): :rtype: str :return: Value of tag. """ - return self.call_api('getTag', cell_id, tag_name) + return self.call_api("getTag", cell_id, tag_name) def has_tag(self, cell_id, tag_name): """ @@ -792,7 +792,7 @@ def has_tag(self, cell_id, tag_name): :rtype: bool :return: True if tag exists in cell. """ - return self.call_api('hasTag', cell_id, tag_name) + return self.call_api("hasTag", cell_id, tag_name) def get_edge_terminals(self, edge_id): """ @@ -803,7 +803,7 @@ def get_edge_terminals(self, edge_id): :return: A list with 2 items, first the source vertex id and second the target vertex id. """ - return self.call_api('getEdgeTerminals', edge_id) + return self.call_api("getEdgeTerminals", edge_id) def get_edge_terminals_with_ports(self, edge_id): """ @@ -821,7 +821,7 @@ def get_edge_terminals_with_ports(self, edge_id): - the port's name used on the target (can be `None`); """ - return self.call_api('getEdgeTerminalsWithPorts', edge_id) + return self.call_api("getEdgeTerminalsWithPorts", edge_id) def set_edge_terminal(self, edge_id, terminal_type, new_terminal_cell_id, port_name=None): """ @@ -840,11 +840,11 @@ def set_edge_terminal(self, edge_id, terminal_type, new_terminal_cell_id, port_n self.TARGET_TERMINAL_CELL, } if terminal_type not in valid_terminal_types: - err_msg = '%s is not a valid value for `terminal_type`' + err_msg = "%s is not a valid value for `terminal_type`" raise ValueError(err_msg % (terminal_type,)) return self.call_api( - 'setEdgeTerminal', edge_id, terminal_type, new_terminal_cell_id, port_name + "setEdgeTerminal", edge_id, terminal_type, new_terminal_cell_id, port_name ) def get_cell_bounds(self, cell_id): @@ -857,7 +857,7 @@ def get_cell_bounds(self, cell_id): """ from qmxgraph.cell_bounds import CellBounds - cell_bounds = self.call_api('getCellBounds', cell_id) + cell_bounds = self.call_api("getCellBounds", cell_id) return CellBounds(**cell_bounds) def set_cell_bounds(self, cell_id, cell_bounds): @@ -870,7 +870,7 @@ def set_cell_bounds(self, cell_id, cell_bounds): """ from qmxgraph.cell_bounds import asdict - return self.call_api('setCellBounds', cell_id, asdict(cell_bounds)) + return self.call_api("setCellBounds", cell_id, asdict(cell_bounds)) def dump(self): """ @@ -880,7 +880,7 @@ def dump(self): :rtype: str :return: A xml string. """ - return self.call_api('dump') + return self.call_api("dump") def restore(self, state): """ @@ -888,43 +888,43 @@ def restore(self, state): :param str state: A xml string previously obtained with `dump`. """ - return self.call_api('restore', state) + return self.call_api("restore", state) def set_interaction_enabled(self, enabled): - self.call_api('setInteractionEnabled', enabled) + self.call_api("setInteractionEnabled", enabled) def set_cells_deletable(self, enabled): - self.call_api('setCellsDeletable', enabled) + self.call_api("setCellsDeletable", enabled) def is_cells_deletable(self): - return self.call_api('isCellsDeletable') + return self.call_api("isCellsDeletable") def set_cells_disconnectable(self, enabled): - self.call_api('setCellsDisconnectable', enabled) + self.call_api("setCellsDisconnectable", enabled) def is_cells_disconnectable(self): - return self.call_api('isCellsDisconnectable') + return self.call_api("isCellsDisconnectable") def set_cells_editable(self, enabled): - self.call_api('setCellsEditable', enabled) + self.call_api("setCellsEditable", enabled) def is_cells_editable(self): - return self.call_api('isCellsEditable') + return self.call_api("isCellsEditable") def set_cells_movable(self, enabled): - self.call_api('setCellsMovable', enabled) + self.call_api("setCellsMovable", enabled) def is_cells_movable(self): - return self.call_api('isCellsMovable') + return self.call_api("isCellsMovable") def set_cells_connectable(self, enabled): - self.call_api('setCellsConnectable', enabled) + self.call_api("setCellsConnectable", enabled) def is_cells_connectable(self): - return self.call_api('isCellsConnectable') + return self.call_api("isCellsConnectable") def run_layout(self, layout_name): - return self.call_api('runLayout', layout_name) + return self.call_api("runLayout", layout_name) def call_api(self, fn: str, *args) -> Any: """ @@ -957,11 +957,11 @@ def call_api_async(self, fn: str, *args) -> None: def _call_api(self, fn: str, *args, sync): graph = self._graph() eval_func = graph.inner_web_view().eval_js if sync else graph.inner_web_view().eval_js_async - call = f'api.{qmxgraph.js.prepare_js_call(fn, *args)}' + call = f"api.{qmxgraph.js.prepare_js_call(fn, *args)}" if qmxgraph.debug.is_qmxgraph_debug_enabled(): call = textwrap.dedent( - f''' + f""" if ( (typeof graphs === "undefined") || !graphs.isRunning() @@ -980,7 +980,7 @@ def _call_api(self, fn: str, *args, sync): ); }} {call}; - ''' + """ ) # Capture all warning messages from Qt. capture_context = _capture_critical_log_messages() diff --git a/src/qmxgraph/common_testing.py b/src/qmxgraph/common_testing.py index 944551c9..85a5ecc1 100644 --- a/src/qmxgraph/common_testing.py +++ b/src/qmxgraph/common_testing.py @@ -7,7 +7,7 @@ from qmxgraph.widget import QmxGraph -RETURN_ALL_CELLS_FILTER = 'function(cell) { return true }' +RETURN_ALL_CELLS_FILTER = "function(cell) { return true }" def get_cell_count(widget: QmxGraph, filter_function: str = RETURN_ALL_CELLS_FILTER) -> int: @@ -30,7 +30,7 @@ def get_cell_ids(widget: QmxGraph, filter_function: str = RETURN_ALL_CELLS_FILTE :return: A list with the id's of the selected cells. """ cells_ids = widget.inner_web_view().eval_js( - f''' + f""" (function(){{ var all_cells = api._graphEditor.graph.model.cells; var cells = Object.keys(all_cells).map( @@ -39,6 +39,6 @@ def get_cell_ids(widget: QmxGraph, filter_function: str = RETURN_ALL_CELLS_FILTE cells = cells.filter({filter_function}); return cells.map(function(aCell){{ return aCell.getId(); }}); - }})()''' + }})()""" ) return cast(List[str], cells_ids) diff --git a/src/qmxgraph/configuration.py b/src/qmxgraph/configuration.py index 941d5764..34b38b07 100644 --- a/src/qmxgraph/configuration.py +++ b/src/qmxgraph/configuration.py @@ -22,7 +22,7 @@ def _is_image_configuration(inst, attr, value): and isinstance(value[1], int) and isinstance(value[2], int) ): - msg = '{} must be a tuple of `(str, int, int)` but got {}' + msg = "{} must be a tuple of `(str, int, int)` but got {}" raise TypeError(msg.format(attr.name, repr(value))) @@ -155,27 +155,27 @@ def __init__(self, styles=None): def validate(self): known_keys = { - 'dashed', - 'decoration_base_rotation', - 'deletable', - 'end_arrow', - 'fill_color', - 'fill_opacity', - 'foldable', - 'image', - 'label_position', - 'label_rotatable', - 'no_label', - 'resizable', - 'rotatable', - 'selectable', - 'shape', - 'start_arrow', - 'stroke_color', - 'stroke_width', - 'stroke_opacity', - 'vertical_align', - 'vertical_label_position', + "dashed", + "decoration_base_rotation", + "deletable", + "end_arrow", + "fill_color", + "fill_opacity", + "foldable", + "image", + "label_position", + "label_rotatable", + "no_label", + "resizable", + "rotatable", + "selectable", + "shape", + "start_arrow", + "stroke_color", + "stroke_width", + "stroke_opacity", + "vertical_align", + "vertical_label_position", } invalid = {} diff --git a/src/qmxgraph/decoration_contents.py b/src/qmxgraph/decoration_contents.py index c6644217..71fde182 100644 --- a/src/qmxgraph/decoration_contents.py +++ b/src/qmxgraph/decoration_contents.py @@ -36,7 +36,7 @@ def _register_decoration_class(class_): "tag" `attr.ib`. """ for attr_name in class_.__attrs_attrs__: - if attr_name.name == 'tag': + if attr_name.name == "tag": if not isinstance(attr_name.default, str): raise ValueError(f"{class_}'s `tag` default must be a `str`") if attr_name.init: @@ -64,14 +64,14 @@ def _convert_decoration_content_item(raw_data): return raw_data else: if not isinstance(raw_data, dict): - raise TypeError(f'`raw_data` must be `str` or `dict` but got {type(raw_data)}') + raise TypeError(f"`raw_data` must be `str` or `dict` but got {type(raw_data)}") raw_data = raw_data.copy() - tag = raw_data.pop('tag', None) + tag = raw_data.pop("tag", None) if tag is None: raise ValueError('`raw_data` must have a `"tag"ยด item') class_ = _tag_to_class.get(tag) if class_ is None: - raise ValueError(f"Can't locate a class for tag \"{tag}\"") + raise ValueError(f'Can\'t locate a class for tag "{tag}"') return class_(**raw_data) @@ -109,7 +109,7 @@ class Image: :ivar int height: The desired height for the image. """ - tag = attr.ib(default='img', init=False) + tag = attr.ib(default="img", init=False) src = attr.ib(validator=_is_str) width = attr.ib(validator=_is_int) height = attr.ib(validator=_is_int) @@ -131,7 +131,7 @@ class TableData: :ivar optional[str] style: A inline style for the element. """ - tag: str = attr.ib(default='td', init=False) + tag: str = attr.ib(default="td", init=False) contents: List[Union[str, Image]] = attrib_table_contents(str, Image) colspan: int = attr.ib(default=1, validator=_is_int) rowspan: int = attr.ib(default=1, validator=_is_int) @@ -154,7 +154,7 @@ class TableRow: `str` used). """ - tag: str = attr.ib(default='tr', init=False) + tag: str = attr.ib(default="tr", init=False) contents: Sequence[Union[str, TableData]] = attrib_table_contents(str, TableData) @@ -171,7 +171,7 @@ class Table: :ivar tuple[TableRow] contents: The table rows. """ - tag: str = attr.ib(default='table', init=False) + tag: str = attr.ib(default="table", init=False) contents: Sequence[TableRow] = attrib_table_contents(TableRow) def contents_after(self, caption): diff --git a/src/qmxgraph/deploy.py b/src/qmxgraph/deploy.py index cce0d370..1e489820 100644 --- a/src/qmxgraph/deploy.py +++ b/src/qmxgraph/deploy.py @@ -9,12 +9,12 @@ def get_conda_env_path(): """ import sys - conda_prefix = os.environ.get('CONDA_PREFIX', None) + conda_prefix = os.environ.get("CONDA_PREFIX", None) env_path = None if conda_prefix is not None: - if sys.platform.startswith('win'): + if sys.platform.startswith("win"): env_path = conda_prefix else: - env_path = os.path.join(conda_prefix, 'usr', 'local') + env_path = os.path.join(conda_prefix, "usr", "local") return env_path diff --git a/src/qmxgraph/js.py b/src/qmxgraph/js.py index bd114065..de719013 100644 --- a/src/qmxgraph/js.py +++ b/src/qmxgraph/js.py @@ -16,9 +16,9 @@ def prepare_js_call(fn, *args): :rtype: object :return: A JavaScript statement ready to be evaluated. """ - return '{fn}({args})'.format( + return "{fn}({args})".format( fn=fn, - args=', '.join(_js_dump(v) for v in args), + args=", ".join(_js_dump(v) for v in args), ) diff --git a/src/qmxgraph/mime.py b/src/qmxgraph/mime.py index addfa8a6..517a08ce 100644 --- a/src/qmxgraph/mime.py +++ b/src/qmxgraph/mime.py @@ -61,10 +61,10 @@ def create_qt_mime_data(data): data_stream = QDataStream(item_data, QIODevice.WriteOnly) qgraph_mime = { - 'version': qmxgraph.constants.QGRAPH_DD_MIME_VERSION, + "version": qmxgraph.constants.QGRAPH_DD_MIME_VERSION, } qgraph_mime.update(data) - data_stream.writeString(json.dumps(qgraph_mime).encode('utf8')) + data_stream.writeString(json.dumps(qgraph_mime).encode("utf8")) mime_data = QMimeData() mime_data.setData(qmxgraph.constants.QGRAPH_DD_MIME_TYPE, item_data) diff --git a/src/qmxgraph/render.py b/src/qmxgraph/render.py index 799edafe..bad7cfcb 100644 --- a/src/qmxgraph/render.py +++ b/src/qmxgraph/render.py @@ -19,7 +19,7 @@ def render_embedded_html(options, styles, stencils, mxgraph_path, own_path): """ from PyQt5.QtCore import QFile - html_file = QFile(own_path + '/graph.html') + html_file = QFile(own_path + "/graph.html") from PyQt5.QtCore import QIODevice @@ -30,12 +30,12 @@ def render_embedded_html(options, styles, stencils, mxgraph_path, own_path): from jinja2 import Template html_data = html_file.readAll().data() - template = Template(html_data.decode('utf8')) + template = Template(html_data.decode("utf8")) finally: html_file.close() def qrc_prefixed(path): - return 'qrc{}'.format(path) + return "qrc{}".format(path) mxgraph_path = qrc_prefixed(mxgraph_path) own_path = qrc_prefixed(own_path) @@ -67,7 +67,7 @@ def render_hosted_html(options, styles, stencils, mxgraph_path, own_path, templa env = Environment() env.loader = FileSystemLoader(template_path) - template = env.get_template('graph.html') + template = env.get_template("graph.html") return _render(template, options, styles, stencils, mxgraph_path, own_path, embedded=False) diff --git a/src/qmxgraph/server.py b/src/qmxgraph/server.py index 335de673..2d97d6bb 100644 --- a/src/qmxgraph/server.py +++ b/src/qmxgraph/server.py @@ -63,7 +63,7 @@ def gen_config(port, mxgraph_path, own_path, stencils_path=None, debug=False): config["global"].update( { # http://docs.cherrypy.org/en/latest/basics.html#disable-logging - 'log.screen': False, + "log.screen": False, } ) @@ -72,14 +72,14 @@ def gen_config(port, mxgraph_path, own_path, stencils_path=None, debug=False): # any reason server is unable to be started, for instance. log_path = os.path.dirname(__file__) - server_logs_dir = '{}/.server_logs'.format(log_path) + server_logs_dir = "{}/.server_logs".format(log_path) if not os.path.isdir(server_logs_dir): os.makedirs(server_logs_dir) config["global"].update( { - 'log.access_file': '{}/cherrypy_port{}_access.log'.format(server_logs_dir, port), - 'log.error_file': '{}/cherrypy_port{}_error.log'.format(server_logs_dir, port), + "log.access_file": "{}/cherrypy_port{}_access.log".format(server_logs_dir, port), + "log.error_file": "{}/cherrypy_port{}_error.log".format(server_logs_dir, port), } ) @@ -132,8 +132,8 @@ def index(self, *args, **kwargs): stencils=self.stencils, # Environment of mxGraph static files is served together with # page (see config generation in this module) - mxgraph_path='mxgraph', - own_path='own', + mxgraph_path="mxgraph", + own_path="own", template_path=self.template_path, ) return html @@ -151,7 +151,7 @@ def host(port, options=None, styles=None, stencils=tuple()): :param iterable[str] stencils: Sequence of paths in file system referring to stencil files. """ - mxgraph_path = os.environ.get('MXGRAPHPATH', None) + mxgraph_path = os.environ.get("MXGRAPHPATH", None) if mxgraph_path is None: conda_env_path = deploy.get_conda_env_path() if conda_env_path is None: @@ -160,9 +160,9 @@ def host(port, options=None, styles=None, stencils=tuple()): "server. Set MXGRAPHPATH environment variable or " "use a conda environment." ) - mxgraph_path = os.path.join(conda_env_path, 'mxgraph') - mxgraph_path = os.path.join(mxgraph_path, 'javascript', 'src') - own_path = os.path.join(os.path.dirname(__file__), 'page') + mxgraph_path = os.path.join(conda_env_path, "mxgraph") + mxgraph_path = os.path.join(mxgraph_path, "javascript", "src") + own_path = os.path.join(os.path.dirname(__file__), "page") stencils_path = None stencils_ = [] @@ -172,7 +172,7 @@ def host(port, options=None, styles=None, stencils=tuple()): "Due to simplification, expects " "all stencils in same folder" ) stencils_path = candidate - stencils_.append('stencils/{}'.format(os.path.basename(stencil))) + stencils_.append("stencils/{}".format(os.path.basename(stencil))) config = gen_config( port=port, @@ -189,7 +189,7 @@ def host(port, options=None, styles=None, stencils=tuple()): cherrypy_server = CherryPyServer() with cherrypy_server.single_shot(page=page, config=config): yield Host( - address='http://localhost:{}'.format(config['global']['server.socket_port']), + address="http://localhost:{}".format(config["global"]["server.socket_port"]), options=page.options, styles=page.styles, stencils=stencils_, @@ -233,14 +233,14 @@ def portable_path(path): """ import sys - if sys.platform.startswith('win'): - path = path.replace('\\', '/') + if sys.platform.startswith("win"): + path = path.replace("\\", "/") return path -if __name__ == '__main__': - mxgraph_path = os.path.join(deploy.get_conda_env_path(), 'mxgraph', 'javascript', 'src') - own_path = os.path.join(os.path.dirname(__file__), 'page') +if __name__ == "__main__": + mxgraph_path = os.path.join(deploy.get_conda_env_path(), "mxgraph", "javascript", "src") + own_path = os.path.join(os.path.dirname(__file__), "page") config = gen_config( port=60066, diff --git a/src/qmxgraph/widget.py b/src/qmxgraph/widget.py index 507c86e9..4589a37b 100644 --- a/src/qmxgraph/widget.py +++ b/src/qmxgraph/widget.py @@ -39,7 +39,7 @@ from qmxgraph.waiting import wait_until # Some ugliness to successfully build the doc on ReadTheDocs... -on_rtd = os.environ.get('READTHEDOCS') == 'True' +on_rtd = os.environ.get("READTHEDOCS") == "True" if not on_rtd: from qmxgraph import resource_mxgraph, resource_qmxgraph # type:ignore[attr-defined] @@ -130,10 +130,10 @@ def __init__( self._double_click_bridge = _DoubleClickBridge() self._popup_menu_bridge = _PopupMenuBridge() self._channel = QWebChannel() - self._channel.registerObject('bridge_error_handler', self._error_bridge) - self._channel.registerObject('bridge_events_handler', self._events_bridge) - self._channel.registerObject('bridge_double_click_handler', self._double_click_bridge) - self._channel.registerObject('bridge_popup_menu_handler', self._popup_menu_bridge) + self._channel.registerObject("bridge_error_handler", self._error_bridge) + self._channel.registerObject("bridge_events_handler", self._events_bridge) + self._channel.registerObject("bridge_double_click_handler", self._double_click_bridge) + self._channel.registerObject("bridge_popup_menu_handler", self._popup_menu_bridge) self._web_view.setWebChannel(self._channel) self._layout.addWidget(self._web_view, 0, 0, 1, 1) @@ -311,24 +311,24 @@ def is_enabled(self) -> bool: return self._enabled def _connect_events_bridge(self): - self.api.register_cells_added_handler('bridge_events_handler.cells_added_slot') - self.api.register_cells_removed_handler('bridge_events_handler.cells_removed_slot') - self.api.register_label_changed_handler('bridge_events_handler.label_changed_slot') - self.api.register_selection_changed_handler('bridge_events_handler.selection_changed_slot') - self.api.register_terminal_changed_handler('bridge_events_handler.terminal_changed_slot') + self.api.register_cells_added_handler("bridge_events_handler.cells_added_slot") + self.api.register_cells_removed_handler("bridge_events_handler.cells_removed_slot") + self.api.register_label_changed_handler("bridge_events_handler.label_changed_slot") + self.api.register_selection_changed_handler("bridge_events_handler.selection_changed_slot") + self.api.register_terminal_changed_handler("bridge_events_handler.terminal_changed_slot") self.api.register_terminal_with_port_changed_handler( - 'bridge_events_handler.terminal_with_port_changed_slot' + "bridge_events_handler.terminal_with_port_changed_slot" ) self.api.register_cells_bounds_changed_handler( - 'bridge_events_handler.cells_bounds_changed_slot' + "bridge_events_handler.cells_bounds_changed_slot" ) - self.api.register_view_update_handler('bridge_events_handler.view_update_slot') + self.api.register_view_update_handler("bridge_events_handler.view_update_slot") def _connect_double_click_handler(self): - self.api.register_double_click_handler('bridge_double_click_handler.double_click_slot') + self.api.register_double_click_handler("bridge_double_click_handler.double_click_slot") def _connect_popup_menu_handler(self): - self.api.register_popup_menu_handler('bridge_popup_menu_handler.popup_menu_slot') + self.api.register_popup_menu_handler("bridge_popup_menu_handler.popup_menu_slot") @property def api(self): @@ -465,10 +465,10 @@ def _on_drop(self, event): data = event.mimeData().data(constants.QGRAPH_DD_MIME_TYPE) if not data.isNull(): data_stream = QDataStream(data, QIODevice.ReadOnly) - parsed = json.loads(data_stream.readString().decode('utf8')) + parsed = json.loads(data_stream.readString().decode("utf8")) # Refer to `mime.py` for docs about format - version = parsed['version'] + version = parsed["version"] if version not in (1, 2): raise ValueError("Unsupported version of QmxGraph MIME data: {}".format(version)) @@ -476,34 +476,34 @@ def _on_drop(self, event): y = event.pos().y() if version in (1, 2): - vertices = parsed.get('vertices', []) + vertices = parsed.get("vertices", []) scale = self.api.get_zoom_scale() for v in vertices: # place vertices with an offset so their center falls # in the event point. - vertex_x = x + (v['dx'] - v['width'] * 0.5) * scale - vertex_y = y + (v['dy'] - v['height'] * 0.5) * scale + vertex_x = x + (v["dx"] - v["width"] * 0.5) * scale + vertex_y = y + (v["dy"] - v["height"] * 0.5) * scale self.api.insert_vertex( x=vertex_x, y=vertex_y, - width=v['width'], - height=v['height'], - label=v['label'], - style=v.get('style', None), - tags=v.get('tags', {}), + width=v["width"], + height=v["height"], + label=v["label"], + style=v.get("style", None), + tags=v.get("tags", {}), ) if version in (2,): - decorations = parsed.get('decorations', []) + decorations = parsed.get("decorations", []) for v in decorations: self.api.insert_decoration( x=x, y=y, - width=v['width'], - height=v['height'], - label=v['label'], - style=v.get('style', None), - tags=v.get('tags', {}), + width=v["width"], + height=v["height"], + label=v["label"], + style=v.get("style", None), + tags=v.get("tags", {}), ) event.acceptProposedAction() @@ -537,14 +537,14 @@ def __init_subclass__(cls, **kwargs): for k, v in list(cls.__dict__.items()): if isinstance(v, pyqtSignal): (signature,) = v.signatures - m = re.match(r'^([^(]+)\(([^)]*)\)$', signature) + m = re.match(r"^([^(]+)\(([^)]*)\)$", signature) assert m is not None signal_name = m.group(1) - assert signal_name.startswith('on_') - slot_name = signal_name[len('on_') :] + '_slot' + assert signal_name.startswith("on_") + slot_name = signal_name[len("on_") :] + "_slot" parameters = m.group(2) - parameters = parameters.split(',') if parameters else [] + parameters = parameters.split(",") if parameters else [] slot_method = _make_async_pyqt_slot(slot_name, signal_name, parameters) setattr(cls, slot_name, slot_method) @@ -603,7 +603,7 @@ class ErrorHandlingBridge(DelayedSignalsBridge): # url: str # line: int # column: int - on_error = pyqtSignal(str, str, int, int, name='on_error') + on_error = pyqtSignal(str, str, int, int, name="on_error") class EventsBridge(DelayedSignalsBridge): @@ -693,16 +693,16 @@ def on_cells_removed_handler(cell_ids): """ - on_cells_removed = pyqtSignal('QVariantList', name='on_cells_removed') - on_cells_added = pyqtSignal('QVariantList', name='on_cells_added') - on_label_changed = pyqtSignal(str, str, str, name='on_label_changed') - on_selection_changed = pyqtSignal('QVariantList', name='on_selection_changed') - on_terminal_changed = pyqtSignal(str, str, str, str, name='on_terminal_changed') + on_cells_removed = pyqtSignal("QVariantList", name="on_cells_removed") + on_cells_added = pyqtSignal("QVariantList", name="on_cells_added") + on_label_changed = pyqtSignal(str, str, str, name="on_label_changed") + on_selection_changed = pyqtSignal("QVariantList", name="on_selection_changed") + on_terminal_changed = pyqtSignal(str, str, str, str, name="on_terminal_changed") on_terminal_with_port_changed = pyqtSignal( - str, str, str, str, str, str, name='on_terminal_with_port_changed' + str, str, str, str, str, str, name="on_terminal_with_port_changed" ) - on_view_update = pyqtSignal(str, 'QVariantList', name='on_view_update') - on_cells_bounds_changed = pyqtSignal('QVariant', name='on_cells_bounds_changed') + on_view_update = pyqtSignal(str, "QVariantList", name="on_view_update") + on_cells_bounds_changed = pyqtSignal("QVariant", name="on_cells_bounds_changed") class _DoubleClickBridge(DelayedSignalsBridge): @@ -716,7 +716,7 @@ class _DoubleClickBridge(DelayedSignalsBridge): # Arguments: # cell_id: str - on_double_click = pyqtSignal(str, name='on_double_click') + on_double_click = pyqtSignal(str, name="on_double_click") class _PopupMenuBridge(DelayedSignalsBridge): @@ -732,7 +732,7 @@ class _PopupMenuBridge(DelayedSignalsBridge): # cell_id: str # x: int # y: int - on_popup_menu = pyqtSignal(str, int, int, name='on_popup_menu') + on_popup_menu = pyqtSignal(str, int, int, name="on_popup_menu") @contextmanager diff --git a/tasks.py b/tasks.py index 58d1e7aa..5a153421 100644 --- a/tasks.py +++ b/tasks.py @@ -31,35 +31,35 @@ def qrc( from qmxgraph import deploy WEB_EXTENSIONS = ( - '.js', - '.gif', - '.png', - '.html', - '.css', - '.txt', # used by mxGraph resources - '.xml', # used by mxGraph resources + ".js", + ".gif", + ".png", + ".html", + ".css", + ".txt", # used by mxGraph resources + ".xml", # used by mxGraph resources ) - indent = ' ' - print_message('qrc', color=Fore.BLUE, bright=True) + indent = " " + print_message("qrc", color=Fore.BLUE, bright=True) def create_web_resource(resource_name, src_dir): print_message( - '{}- resource: {}'.format(indent, resource_name), color=Fore.BLUE, bright=True + "{}- resource: {}".format(indent, resource_name), color=Fore.BLUE, bright=True ) target_dir = os.path.dirname(qmxgraph.__file__) qrc_file, py_file = generate_qrc_from_folder( - basename='resource_{}'.format(resource_name), + basename="resource_{}".format(resource_name), alias=resource_name, source_dir=src_dir, target_dir=target_dir, include=WEB_EXTENSIONS, ) - print_message('{}* generated {}'.format(indent * 2, qrc_file)) - print_message('{}* generated {}'.format(indent * 2, py_file)) + print_message("{}* generated {}".format(indent * 2, qrc_file)) + print_message("{}* generated {}".format(indent * 2, py_file)) - mxgraph = os.environ.get('MXGRAPHPATH', None) + mxgraph = os.environ.get("MXGRAPHPATH", None) if mxgraph is not None: if not os.path.isdir(mxgraph): raise IOError( @@ -76,7 +76,7 @@ def create_web_resource(resource_name, src_dir): " and no conda environment active" ) - mxgraph = '{env_dir}/mxgraph'.format(env_dir=env_dir) + mxgraph = "{env_dir}/mxgraph".format(env_dir=env_dir) if not os.path.isdir(mxgraph): raise IOError( "Unable to determine MxGraph to use:" @@ -85,20 +85,20 @@ def create_web_resource(resource_name, src_dir): ) create_web_resource( - resource_name='mxgraph', src_dir='{folder}/javascript/src'.format(folder=mxgraph) + resource_name="mxgraph", src_dir="{folder}/javascript/src".format(folder=mxgraph) ) qgraph_root = os.path.dirname(qmxgraph.__file__) create_web_resource( - resource_name='qmxgraph', - src_dir=os.path.join(qgraph_root, 'page'), + resource_name="qmxgraph", + src_dir=os.path.join(qgraph_root, "page"), ) @invoke.task( help={ - 'python-version': ( - 'Can be used to define the python version used when creating the' ' work environment' + "python-version": ( + "Can be used to define the python version used when creating the" " work environment" ), } ) @@ -111,34 +111,34 @@ def docs(ctx, python_version=None): import tempfile from pathlib import Path - conda_info_json = subprocess.check_output(['conda', 'info', '--json']) + conda_info_json = subprocess.check_output(["conda", "info", "--json"]) conda_info = json.loads(conda_info_json) current_env_name = conda_info["active_prefix_name"] - if current_env_name in (None, 'base'): + if current_env_name in (None, "base"): raise invoke.Exit("Activate the project's conda environment first") else: - docs_env_name = f'{current_env_name}-docs' + docs_env_name = f"{current_env_name}-docs" new_environ = os.environ.copy() - new_environ['TEST_QMXGRAPH'] = '0' + new_environ["TEST_QMXGRAPH"] = "0" if python_version is not None: - new_environ['PYTHON_VERSION'] = python_version + new_environ["PYTHON_VERSION"] = python_version script = [ - '', # To have a new line at the start (see windows new line). - f'conda devenv --name {docs_env_name} --file docs_environment.devenv.yml', - f'conda activate {docs_env_name}', - 'cd docs', - 'sphinx-build . _build -W', + "", # To have a new line at the start (see windows new line). + f"conda devenv --name {docs_env_name} --file docs_environment.devenv.yml", + f"conda activate {docs_env_name}", + "cd docs", + "sphinx-build . _build -W", ] - if sys.platform == 'win32': - suffix = '.bat' - new_line = '\n@echo on\ncall ' - command = ['cmd', '/C'] + if sys.platform == "win32": + suffix = ".bat" + new_line = "\n@echo on\ncall " + command = ["cmd", "/C"] else: - suffix = '.bash' - new_line = '\n' - command = ['bash', '-x'] + suffix = ".bash" + new_line = "\n" + command = ["bash", "-x"] script_file = tempfile.NamedTemporaryFile(suffix=suffix, delete=False) try: @@ -154,8 +154,8 @@ def docs(ctx, python_version=None): @invoke.task def test(ctx): - print_message('test'.format(), color=Fore.BLUE, bright=True) - cmd = 'pytest --cov=qmxgraph --timeout=30 -v --durations=10 --color=yes' + print_message("test".format(), color=Fore.BLUE, bright=True) + cmd = "pytest --cov=qmxgraph --timeout=30 -v --durations=10 --color=yes" import subprocess @@ -164,8 +164,8 @@ def test(ctx): @invoke.task def linting(ctx): - print_message('lint'.format(), color=Fore.BLUE, bright=True) - cmd = 'flake8 -v qmxgraph' + print_message("lint".format(), color=Fore.BLUE, bright=True) + cmd = "flake8 -v qmxgraph" import subprocess @@ -174,7 +174,7 @@ def linting(ctx): @invoke.task( help={ - 'svg_path': 'A SVG file', + "svg_path": "A SVG file", } ) def svgtostencil(ctx, svg_path): @@ -182,12 +182,12 @@ def svgtostencil(ctx, svg_path): Converts a SVG file to a stencil file compatible with mxGraph, output is printed in standard output. """ - qmxgraph_scripts = os.path.join(os.getcwd(), 'scripts') + qmxgraph_scripts = os.path.join(os.getcwd(), "scripts") import subprocess - svg_to_stencil_script = os.path.join(qmxgraph_scripts, 'svg_to_stencil.py') - raise invoke.Exit(subprocess.call(['python', svg_to_stencil_script, svg_path])) + svg_to_stencil_script = os.path.join(qmxgraph_scripts, "svg_to_stencil.py") + raise invoke.Exit(subprocess.call(["python", svg_to_stencil_script, svg_path])) def generate_qrc(target_filename, file_map): @@ -242,7 +242,7 @@ def generate_qrc(target_filename, file_map): # https://forum.qt.io/topic/42641/the-qt-resource-system-compile-error/4). import io - with io.open(target_filename, 'w', encoding='utf8') as f: + with io.open(target_filename, "w", encoding="utf8") as f: f.write(contents) @@ -265,9 +265,9 @@ def generate_qrc_contents(file_map, target_dir): def create_entry(alias_, path_): path_ = follow_subst(path_) rel_path = os.path.relpath(path_, target_dir) - return ' ' + QRC_ENTRY_TEMPLATE.format(alias=alias_, path=rel_path) + return " " + QRC_ENTRY_TEMPLATE.format(alias=alias_, path=rel_path) - entries = '\n'.join([create_entry(alias, path) for (alias, path) in file_map]) + entries = "\n".join([create_entry(alias, path) for (alias, path) in file_map]) return QRC_FILE_TEMPLATE.format(entries=entries) @@ -294,10 +294,10 @@ def generate_qrc_py(qrc_filename, target_filename): subprocess.check_call( [ sys.executable, - '-m', - 'PyQt5.pyrcc_main', + "-m", + "PyQt5.pyrcc_main", local_filename, - '-o', + "-o", target_filename, ], cwd=cwd, @@ -325,9 +325,7 @@ def generate_qrc_from_folder(basename, alias, source_dir, target_dir, include=No With a call like: ```python - generate_qrc_from_folder( - "resource_foo", "rsc_foo", "/home/dent/foo/", "/home/dent/foo/" - ) + generate_qrc_from_folder("resource_foo", "rsc_foo", "/home/dent/foo/", "/home/dent/foo/") ``` It would result in a .qrc like: @@ -361,10 +359,10 @@ def generate_qrc_from_folder(basename, alias, source_dir, target_dir, include=No if not os.path.isdir(target_dir): raise IOError("Invalid target directory: {}".format(target_dir)) - if sys.platform.startswith('win'): + if sys.platform.startswith("win"): def fix_alias(a): - return a.replace('\\', '/') + return a.replace("\\", "/") else: @@ -374,7 +372,7 @@ def fix_alias(a): files = [ ( fix_alias( - '{alias}/{rel_file}'.format(alias=alias, rel_file=os.path.relpath(f, source_dir)) + "{alias}/{rel_file}".format(alias=alias, rel_file=os.path.relpath(f, source_dir)) ), f, ) @@ -385,10 +383,10 @@ def fix_alias(a): "Unable to collect anything for " ".qrc file in folder {}".format(source_dir) ) - qrc_filename = os.path.join(target_dir, '{basename}{ext}'.format(basename=basename, ext='.qrc')) + qrc_filename = os.path.join(target_dir, "{basename}{ext}".format(basename=basename, ext=".qrc")) generate_qrc(qrc_filename, files) - py_filename = os.path.join(target_dir, '{basename}{ext}'.format(basename=basename, ext='.py')) + py_filename = os.path.join(target_dir, "{basename}{ext}".format(basename=basename, ext=".py")) generate_qrc_py(qrc_filename, py_filename) return qrc_filename, py_filename @@ -404,7 +402,7 @@ def collect_files_in_folder(folder, include=None): return collected -def print_message(message, color=None, bright=True, endline='\n'): +def print_message(message, color=None, bright=True, endline="\n"): """ Print a message to the standard output. @@ -421,7 +419,7 @@ def print_message(message, color=None, bright=True, endline='\n'): if color is not None: style = Style.BRIGHT if bright else Style.DIM - message = '{color}{style}{msg}{reset}'.format( + message = "{color}{style}{msg}{reset}".format( color=color, style=style, reset=Style.RESET_ALL, @@ -437,7 +435,7 @@ def print_message(message, color=None, bright=True, endline='\n'): sys.stderr.flush() -if sys.platform.startswith('win'): +if sys.platform.startswith("win"): def follow_subst(path, deep=True): """ @@ -462,7 +460,7 @@ def follow_subst(path, deep=True): path = os.path.abspath(path) while True: - drive = path[0] + ':' + drive = path[0] + ":" universal_drive = drive.lower() subst = parse_subst() if universal_drive in subst: @@ -479,7 +477,7 @@ def parse_subst(): import re import subprocess - output = subprocess.check_output('subst') + output = subprocess.check_output("subst") def parse_subst_line(line): import locale @@ -487,7 +485,7 @@ def parse_subst_line(line): if not isinstance(line, str): line = line.decode(locale.getpreferredencoding(False)) - match = re.match(r'^(\w:)\\: => (.+)$', line) + match = re.match(r"^(\w:)\\: => (.+)$", line) drive = match.group(1) replace = match.group(2) return drive.lower(), replace @@ -510,7 +508,7 @@ def follow_subst(path, deep=True): QRC_ENTRY_TEMPLATE = '{path}' -QRC_FILE_TEMPLATE = '''\ +QRC_FILE_TEMPLATE = """\ @@ -518,7 +516,7 @@ def follow_subst(path, deep=True): {entries} -''' +""" # Only task registered in this global collection will be detected by invoke. diff --git a/tests/conftest.py b/tests/conftest.py index 29a77cb4..c1ff37ad 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,7 +7,7 @@ def pytest_configure(config): # During warm up, clean up the temporary files/objects used by ports # fixture from previous runs. Note these files are shared among ALL slaves # of pytest so they can't reliably be removed by a fixture. - config.cache.set('qmxgraph/ports', []) + config.cache.set("qmxgraph/ports", []) import os @@ -54,7 +54,7 @@ def enable_qgraph_debug(): qmxgraph.debug.set_qmxgraph_debug(False) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def port(request): """ Each process hosting a graph page must use an unique port. This fixture @@ -106,7 +106,7 @@ def get(self): # On Windows it seems to sometimes to fail because of lack of # privileges, for some reason. if e.errno == errno.EEXIST or ( - sys.platform.startswith('win') and e.errno == errno.EACCES + sys.platform.startswith("win") and e.errno == errno.EACCES ): time.sleep(0.1) continue @@ -117,15 +117,15 @@ def get(self): import socket s = socket.socket() - s.bind(('', 0)) + s.bind(("", 0)) port_ = s.getsockname()[1] s.close() - ports_ = self.cache.get('qmxgraph/ports', []) + ports_ = self.cache.get("qmxgraph/ports", []) unique = port_ not in ports_ if unique: - self.cache.set('qmxgraph/ports', ports_ + [port_]) + self.cache.set("qmxgraph/ports", ports_ + [port_]) break else: attempts -= 1 @@ -138,7 +138,7 @@ def get(self): return port_ -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def host(port): """ Hosts a graph page, with a series of simple default options and styles. @@ -151,21 +151,21 @@ def host(port): styles = GraphStyles( { - 'group': { - 'shape': 'rectangle', - 'fill_color': '#ff93ba', - 'dashed': True, + "group": { + "shape": "rectangle", + "fill_color": "#ff93ba", + "dashed": True, }, - 'table': { - 'fill_color': '#ffffff', - 'stroke_opacity': 0, - 'fill_opacity': 0, + "table": { + "fill_color": "#ffffff", + "stroke_opacity": 0, + "fill_opacity": 0, }, - 'yellow': { - 'fill_color': '#ffff00', + "yellow": { + "fill_color": "#ffff00", }, - 'purple': { - 'fill_color': '#ff00ff', + "purple": { + "fill_color": "#ff00ff", }, } ) @@ -256,10 +256,10 @@ def pytest_collection_modifyitems(items): """ import os - if os.environ.get('CI', 'false') != 'true': + if os.environ.get("CI", "false") != "true": return for item in items: - if 'graph_cases' in getattr(item, 'fixturenames', []): + if "graph_cases" in getattr(item, "fixturenames", []): item.add_marker(pytest.mark.flaky(reruns=3)) @@ -306,31 +306,31 @@ def __init__(self, selenium, host): _wait_graph_page_ready(host=host, selenium=selenium) selenium.execute_script( - 'callback = function(cellIds) {' - ' if (!window.__added__) {' - ' window.__added__ = [];' - ' }' - ' window.__added__.push.apply(window.__added__, cellIds);' - '}' + "callback = function(cellIds) {" + " if (!window.__added__) {" + " window.__added__ = [];" + " }" + " window.__added__.push.apply(window.__added__, cellIds);" + "}" ) - self.eval_js_function('api.registerCellsAddedHandler', qmxgraph.js.Variable('callback')) + self.eval_js_function("api.registerCellsAddedHandler", qmxgraph.js.Variable("callback")) selenium.execute_script( - 'callback = function(cellId, newLabel, oldLabel) {' - ' if (!window.__labels__) {' - ' window.__labels__ = [];' - ' }' - ' window.__labels__.push({cellId: cellId, newLabel: newLabel, oldLabel: oldLabel});' # noqa - '}' + "callback = function(cellId, newLabel, oldLabel) {" + " if (!window.__labels__) {" + " window.__labels__ = [];" + " }" + " window.__labels__.push({cellId: cellId, newLabel: newLabel, oldLabel: oldLabel});" # noqa + "}" ) - self.eval_js_function('api.registerLabelChangedHandler', qmxgraph.js.Variable('callback')) + self.eval_js_function("api.registerLabelChangedHandler", qmxgraph.js.Variable("callback")) def get_container(self): """ :rtype: selenium.webdriver.remote.webelement.WebElement :return: DIV element containing graph drawing widget. """ - return self.selenium.find_element(By.ID, 'graphContainer') + return self.selenium.find_element(By.ID, "graphContainer") def get_container_size(self): """ @@ -339,7 +339,7 @@ def get_container_size(self): element, respectively. """ container = self.get_container() - return container.size['width'], container.size['height'] + return container.size["width"], container.size["height"] def get_vertex_position(self, vertex): """ @@ -348,7 +348,7 @@ def get_vertex_position(self, vertex): :rtype: tuple[float, float] :return: Left/X and top/Y screen coordinates of vertex, respectively. """ - return float(vertex.get_attribute('x')), float(vertex.get_attribute('y')) + return float(vertex.get_attribute("x")), float(vertex.get_attribute("y")) def get_vertex_size(self, vertex): """ @@ -357,7 +357,7 @@ def get_vertex_size(self, vertex): :rtype: tuple[int, int] :return: Width and height in pixels of vertex, respectively. """ - return int(vertex.get_attribute('width')), int(vertex.get_attribute('height')) + return int(vertex.get_attribute("width")), int(vertex.get_attribute("height")) def get_edge(self, source, target): """ @@ -378,12 +378,12 @@ def get(v, attr): from selenium.common.exceptions import StaleElementReferenceException try: - if get(source, 'x') < get(target, 'x'): - h_right = get(source, 'x') + get(source, 'width') - v_center = get(source, 'y') + get(source, 'height') // 2 + if get(source, "x") < get(target, "x"): + h_right = get(source, "x") + get(source, "width") + v_center = get(source, "y") + get(source, "height") // 2 else: - h_right = get(target, 'x') + get(target, 'width') - v_center = get(target, 'y') + get(target, 'height') // 2 + h_right = get(target, "x") + get(target, "width") + v_center = get(target, "y") + get(target, "height") // 2 except StaleElementReferenceException: # If any of vertices is not longer in page, edge is also removed return None @@ -410,14 +410,14 @@ def get_label_element(self, cell): # vertices aren't child to cell drawing node in SVG of mxGraph, they # actually share a same parent node which contains both cell drawing # and text at a same level. - common = cell.find_element(By.XPATH, '../..') - if self.selenium.execute_script('return graphEditor.graph.isHtmlLabel()'): + common = cell.find_element(By.XPATH, "../..") + if self.selenium.execute_script("return graphEditor.graph.isHtmlLabel()"): # If HTML labels are enabled, label is a bit more complicated... - g = common.find_element(By.CSS_SELECTOR, 'g[style]>g[transform]') - label = g.find_element(By.TAG_NAME, 'div') - label = label.find_element(By.TAG_NAME, 'div') + g = common.find_element(By.CSS_SELECTOR, "g[style]>g[transform]") + label = g.find_element(By.TAG_NAME, "div") + label = label.find_element(By.TAG_NAME, "div") else: - label = common.find_element(By.CSS_SELECTOR, 'g>g>text') + label = common.find_element(By.CSS_SELECTOR, "g>g>text") return label def get_edge_position(self, edge): @@ -430,7 +430,7 @@ def get_edge_position(self, edge): """ import re - edge_coords = re.search(r'M (\d+) (\d+)', edge.get_attribute('d')) + edge_coords = re.search(r"M (\d+) (\d+)", edge.get_attribute("d")) return int(edge_coords.group(1)), int(edge_coords.group(2)) def get_id(self, cell): @@ -452,7 +452,7 @@ def get_id(self, cell): tolerance = self.selenium.execute_script("return graphEditor.graph.tolerance") / 2.0 id_ = self.eval_js_function( - 'api.getCellIdAt', cell.location['x'] + tolerance, cell.location['y'] + tolerance + "api.getCellIdAt", cell.location["x"] + tolerance, cell.location["y"] + tolerance ) return id_ @@ -464,7 +464,7 @@ def get_type_at(self, x, y): :return: Cell type at position. See `QmxGraphApi.getCellTypeAt` for details about possible types. """ - return self.eval_js_function('api.getCellTypeAt', x, y) + return self.eval_js_function("api.getCellTypeAt", x, y) def get_geometry(self, cell): """ @@ -473,7 +473,7 @@ def get_geometry(self, cell): :rtype: list[int, int, int, int] :return: List composed, respectively, by x, y, width and height. """ - return self.eval_js_function('api.getGeometry', self._as_cell_id(cell)) + return self.eval_js_function("api.getGeometry", self._as_cell_id(cell)) def get_label(self, cell): """ @@ -482,7 +482,7 @@ def get_label(self, cell): :rtype: str :return: Label of cell. """ - return self.eval_js_function('api.getLabel', self._as_cell_id(cell)) + return self.eval_js_function("api.getLabel", self._as_cell_id(cell)) def set_visible(self, cell, visible): """ @@ -490,14 +490,14 @@ def set_visible(self, cell, visible): Graphical element of a cell or its id. :param bool visible: New visibility state. """ - self.eval_js_function('api.setVisible', self._as_cell_id(cell), visible) + self.eval_js_function("api.setVisible", self._as_cell_id(cell), visible) def is_visible(self, cell): """ :param selenium.webdriver.remote.webelement.WebElement|str cell: Graphical element of a cell or its id. """ - return self.eval_js_function('api.isVisible', self._as_cell_id(cell)) + return self.eval_js_function("api.isVisible", self._as_cell_id(cell)) def get_table_title(self, table): """ @@ -506,8 +506,8 @@ def get_table_title(self, table): :rtype: str :return: Table title. """ - title = table.find_element(By.CSS_SELECTOR, 'table.table-cell-title') - return title.find_element(By.TAG_NAME, 'tr').text + title = table.find_element(By.CSS_SELECTOR, "table.table-cell-title") + return title.find_element(By.TAG_NAME, "tr").text def get_table_contents(self, table): """ @@ -516,8 +516,8 @@ def get_table_contents(self, table): :rtype: str :return: Table contents. """ - contents = table.find_element(By.CSS_SELECTOR, 'table.table-cell-contents') - return [i.text for i in contents.find_elements(By.TAG_NAME, 'td')] + contents = table.find_element(By.CSS_SELECTOR, "table.table-cell-contents") + return [i.text for i in contents.find_elements(By.TAG_NAME, "td")] def select_vertex(self, vertex): """ @@ -532,8 +532,8 @@ def select_vertex(self, vertex): # because of element mismatch. This is an attempt to click in the # bottom right part of vertex that *usually* doesn't seem to have # anything over it. - x_offset = int(vertex.get_attribute('width')) // 2 - y_offset = int(vertex.get_attribute('height')) // 2 + x_offset = int(vertex.get_attribute("width")) // 2 + y_offset = int(vertex.get_attribute("height")) // 2 assert (x_offset > 0) and (y_offset > 0) actions.move_to_element_with_offset(vertex, x_offset, y_offset) actions.click() @@ -556,7 +556,7 @@ def get_added_cell_ids(self): :return: Id of every cell added to graph, captured by using `registerCellsAddedHandler` event. """ - return self.selenium.execute_script('return window.__added__') + return self.selenium.execute_script("return window.__added__") def get_label_changes(self): """ @@ -564,9 +564,9 @@ def get_label_changes(self): :return: Event object of every time a cell was renamed in graph, captured by using `onLabelChanged` event. """ - return self.selenium.execute_script('return window.__labels__') + return self.selenium.execute_script("return window.__labels__") - def insert_vertex(self, x=10, y=10, width=25, height=25, label='label', style=None, tags=None): + def insert_vertex(self, x=10, y=10, width=25, height=25, label="label", style=None, tags=None): """ Inserts a vertex with sensible, empirical defaults. @@ -576,7 +576,7 @@ def insert_vertex(self, x=10, y=10, width=25, height=25, label='label', style=No """ return self.eval_js_function("api.insertVertex", x, y, width, height, label, style, tags) - def insert_table(self, x=20, y=60, width=100, contents=None, title='Hitchhikers', tags=None): + def insert_table(self, x=20, y=60, width=100, contents=None, title="Hitchhikers", tags=None): """ Inserts a table with sensible, empirical defaults. @@ -587,17 +587,17 @@ def insert_table(self, x=20, y=60, width=100, contents=None, title='Hitchhikers' if contents is None: contents = { # graphs.utils.TableRowDescription - 'contents': [ + "contents": [ # graphs.utils.TableDataDescription - {'contents': ['arthur', 'dent']}, + {"contents": ["arthur", "dent"]}, # graphs.utils.TableDataDescription - {'contents': ['ford', 'prefect']}, + {"contents": ["ford", "prefect"]}, ] } - return self.eval_js_function('api.insertTable', x, y, width, contents, title, tags) + return self.eval_js_function("api.insertTable", x, y, width, contents, title, tags) def insert_decoration( - self, x, y, width=10, height=10, style=None, label='decoration', tags=None + self, x, y, width=10, height=10, style=None, label="decoration", tags=None ): """ Inserts a decoration with sensible, empirical defaults for most @@ -609,7 +609,7 @@ def insert_decoration( :return: New decoration id. """ return self.eval_js_function( - 'api.insertDecoration', x, y, width, height, label, style, tags + "api.insertDecoration", x, y, width, height, label, style, tags ) def insert_edge_by_drag_drop(self, source, target): @@ -627,7 +627,7 @@ def insert_edge_by_drag_drop(self, source, target): actions.drag_and_drop(source, target) actions.perform() - def insert_edge(self, source, target, label='', style=None, tags=None): + def insert_edge(self, source, target, label="", style=None, tags=None): """ Inserts an edge programatically. @@ -661,7 +661,7 @@ def remove_cells(self, *cell_ids): """ :param iterable[str] cell_ids: Id of cells to be removed. """ - return self.eval_js_function('api.removeCells', list(cell_ids)) + return self.eval_js_function("api.removeCells", list(cell_ids)) def eval_js_function(self, fn, *args): """ @@ -675,7 +675,7 @@ def eval_js_function(self, fn, *args): # Unlike Qt JS evaluation, Selenium doesn't include return by default, # it is necessary to include it in statement. return self.selenium.execute_script( - 'return {}'.format(qmxgraph.js.prepare_js_call(fn, *args)) + "return {}".format(qmxgraph.js.prepare_js_call(fn, *args)) ) def _as_cell_id(self, cell): @@ -712,11 +712,11 @@ def __init__(self, selenium, host): selenium.execute_script("api.insertVertex(10, 10, 25, 25, 'label', null)") vertex = self.get_vertex() - assert vertex.get_attribute('x') == '10' - assert vertex.get_attribute('y') == '10' - assert vertex.get_attribute('width') == '25' - assert vertex.get_attribute('height') == '25' - assert self.get_label_element(vertex).text == 'label' + assert vertex.get_attribute("x") == "10" + assert vertex.get_attribute("y") == "10" + assert vertex.get_attribute("width") == "25" + assert vertex.get_attribute("height") == "25" + assert self.get_label_element(vertex).text == "label" def get_vertex(self): color = self.selenium.execute_script( @@ -738,7 +738,7 @@ class Graph1Vertex1Port(Graph1Vertex): def __init__(self, selenium, host): Graph1Vertex.__init__(self, selenium, host) vertex_id = self.get_id(self.get_vertex()) - self.port_color = '#987654' + self.port_color = "#987654" selenium.execute_script( f"api.insertPort(" f" {vertex_id}, 'foo'," @@ -762,14 +762,14 @@ def __init__(self, selenium, host): selenium.execute_script("api.insertVertex(10, 10, 25, 25, 'yellow', 'yellow')") vertex = self.get_vertex() - assert vertex.get_attribute('x') == '10' - assert vertex.get_attribute('y') == '10' - assert vertex.get_attribute('width') == '25' - assert vertex.get_attribute('height') == '25' - assert self.get_label_element(vertex).text == 'yellow' + assert vertex.get_attribute("x") == "10" + assert vertex.get_attribute("y") == "10" + assert vertex.get_attribute("width") == "25" + assert vertex.get_attribute("height") == "25" + assert self.get_label_element(vertex).text == "yellow" def get_vertex(self): - color = self.host.styles['yellow']['fill_color'] + color = self.host.styles["yellow"]["fill_color"] # The vertex basically is composed of two parts: # * A tag drawn in SVG (which is a grandchild of two consecutive @@ -785,8 +785,8 @@ class Graph2Vertices(BaseGraphCase): def __init__(self, selenium, host): BaseGraphCase.__init__(self, selenium, host) - self.vertex1_id = self.insert_vertex(10, 10, 30, 30, 'foo', None) - self.vertex2_id = self.insert_vertex(90, 10, 30, 30, 'bar', None) + self.vertex1_id = self.insert_vertex(10, 10, 30, 30, "foo", None) + self.vertex2_id = self.insert_vertex(90, 10, 30, 30, "bar", None) class Graph2Vertices1EdgeByCode(Graph2Vertices): @@ -795,27 +795,27 @@ def __init__(self, selenium, host): self.source_id = self.vertex1_id self.target_id = self.vertex2_id - self.edge_id = self.insert_edge(self.source_id, self.target_id, 'edge') + self.edge_id = self.insert_edge(self.source_id, self.target_id, "edge") class Graph3Vertices1EdgeByCode(Graph2Vertices1EdgeByCode): def __init__(self, selenium, host): Graph2Vertices1EdgeByCode.__init__(self, selenium, host) - self.vertex3_id = self.insert_vertex(10, 90, 30, 30, 'fuz', None) + self.vertex3_id = self.insert_vertex(10, 90, 30, 30, "fuz", None) class Graph3Vertices3EdgesByCode(BaseGraphCase): def __init__(self, selenium, host): BaseGraphCase.__init__(self, selenium, host) - self.vertex1_id = self.insert_vertex(0, 0, 1, 1, label='foo') - self.vertex2_id = self.insert_vertex(100, 0, 1, 1, label='bar') - self.vertex3_id = self.insert_vertex(200, 0, 1, 1, label='fuz') + self.vertex1_id = self.insert_vertex(0, 0, 1, 1, label="foo") + self.vertex2_id = self.insert_vertex(100, 0, 1, 1, label="bar") + self.vertex3_id = self.insert_vertex(200, 0, 1, 1, label="fuz") - self.edge1_id = self.insert_edge(self.vertex1_id, self.vertex2_id, 'edge1') - self.edge2_id = self.insert_edge(self.vertex1_id, self.vertex3_id, 'edge2') - self.edge3_id = self.insert_edge(self.vertex2_id, self.vertex3_id, 'edge3') + self.edge1_id = self.insert_edge(self.vertex1_id, self.vertex2_id, "edge1") + self.edge2_id = self.insert_edge(self.vertex1_id, self.vertex3_id, "edge2") + self.edge3_id = self.insert_edge(self.vertex2_id, self.vertex3_id, "edge3") class Graph2Vertices1EdgeByDragDrop(Graph2Vertices): @@ -843,9 +843,9 @@ def __init__(self, selenium, host): y = vertices_top_border + vertices_width / 2 # edge's y coordinate. w = 10 h = 10 - style = 'purple' - label = 'decoration' - self.eval_js_function('api.insertDecoration', x, y, w, h, label, style) + style = "purple" + label = "decoration" + self.eval_js_function("api.insertDecoration", x, y, w, h, label, style) decoration = self.get_decorations()[0] assert int(decoration.get_attribute("x")) == x - (w // 2) @@ -854,8 +854,8 @@ def __init__(self, selenium, host): assert int(decoration.get_attribute("height")) == h def get_decorations(self): - style = 'purple' - selector = 'g>g>rect[fill="{}"]'.format(self.host.styles[style]['fill_color']) + style = "purple" + selector = 'g>g>rect[fill="{}"]'.format(self.host.styles[style]["fill_color"]) decoration = self.selenium.find_elements(By.CSS_SELECTOR, selector) return decoration @@ -868,27 +868,27 @@ def __init__(self, selenium, host): y = 60 w = 100 contents = { # graphs.utils.TableDescription - 'contents': [ + "contents": [ # graphs.utils.TableDataDescription - {'contents': ['arthur', 'dent']}, + {"contents": ["arthur", "dent"]}, # graphs.utils.TableDataDescription - {'contents': ['ford', 'prefect']}, + {"contents": ["ford", "prefect"]}, ] } - title = 'Hitchhikers' - self.table_id = self.eval_js_function('api.insertTable', x, y, w, contents, title) + title = "Hitchhikers" + self.table_id = self.eval_js_function("api.insertTable", x, y, w, contents, title) - assert self.get_table_title(self.get_tables()[0]) == 'Hitchhikers' + assert self.get_table_title(self.get_tables()[0]) == "Hitchhikers" assert self.get_table_contents(self.get_tables()[0]) == [ - 'arthur', - 'dent', - 'ford', - 'prefect', + "arthur", + "dent", + "ford", + "prefect", ] def get_tables(self): - titles = self.selenium.find_elements(By.CSS_SELECTOR, 'div>table.table-cell-title') - return [web_el.find_element(By.XPATH, '../..') for web_el in titles] + titles = self.selenium.find_elements(By.CSS_SELECTOR, "div>table.table-cell-title") + return [web_el.find_element(By.XPATH, "../..") for web_el in titles] class Graph1Table(BaseGraphCase): @@ -899,27 +899,27 @@ def __init__(self, selenium, host): y = 60 w = 100 contents = { # graphs.utils.TableDescription - 'contents': [ + "contents": [ # graphs.utils.TableDataDescription - {'contents': ['arthur', 'dent']}, + {"contents": ["arthur", "dent"]}, # graphs.utils.TableDataDescription - {'contents': ['ford', 'prefect']}, + {"contents": ["ford", "prefect"]}, ] } - title = 'Hitchhikers' - self.eval_js_function('api.insertTable', x, y, w, contents, title) + title = "Hitchhikers" + self.eval_js_function("api.insertTable", x, y, w, contents, title) - assert self.get_table_title(self.get_tables()[0]) == 'Hitchhikers' + assert self.get_table_title(self.get_tables()[0]) == "Hitchhikers" assert self.get_table_contents(self.get_tables()[0]) == [ - 'arthur', - 'dent', - 'ford', - 'prefect', + "arthur", + "dent", + "ford", + "prefect", ] def get_tables(self): - titles = self.selenium.find_elements(By.CSS_SELECTOR, 'div>table.table-cell-title') - return [web_el.find_element(By.XPATH, '../..') for web_el in titles] + titles = self.selenium.find_elements(By.CSS_SELECTOR, "div>table.table-cell-title") + return [web_el.find_element(By.XPATH, "../..") for web_el in titles] def _wait_graph_page_ready(host, selenium): @@ -962,12 +962,13 @@ def _wait_graph_page_ready(host, selenium): ) except timeout_exceptions as e: raise TimeoutException( - "Graph page wasn't ready in address {} after a timeout of {}" - " seconds".format(host.address, timeout) + "Graph page wasn't ready in address {} after a timeout of {}" " seconds".format( + host.address, timeout + ) ) from e for n in range(timeout): - has_api = selenium.execute_script('return !!window.api') + has_api = selenium.execute_script("return !!window.api") if has_api: break else: @@ -980,4 +981,4 @@ def _wait_graph_page_ready(host, selenium): def _get_port_lock_filename(rootdir): - return '{}/.port.lock'.format(rootdir) + return "{}/.port.lock".format(rootdir) diff --git a/tests/test_decoration_contents.py b/tests/test_decoration_contents.py index 5c2b320e..7b7f61ba 100644 --- a/tests/test_decoration_contents.py +++ b/tests/test_decoration_contents.py @@ -20,15 +20,15 @@ class Household: def test_tuple_of_validator() -> None: - Household(silverware=(Cutlery(name='fork'),)) + Household(silverware=(Cutlery(name="fork"),)) with pytest.raises(TypeError) as execinfo: - Household(silverware=Cutlery(name='fork')) + Household(silverware=Cutlery(name="fork")) msg = "'silverware' must be a tuple but got Cutlery(name='fork')" assert msg in str(execinfo.value) with pytest.raises(TypeError) as execinfo: - Household(silverware=(Cutlery(name='fork'), Door())) + Household(silverware=(Cutlery(name="fork"), Door())) assert "'silverware' must be a tuple of " in str(execinfo.value) msg = ( "but got (Cutlery(name='fork'), Door()) and item in index 1 is not" @@ -42,19 +42,19 @@ def test_contents_after() -> None: table = Table( [ - TableRow([TableData(['Cutlery'])]), - TableRow(['Spoons', '2']), - TableRow(['Knifes', '5']), - TableRow([TableData(['Bathroom'])]), - TableRow(['Soap', '2']), - TableRow(['Toothpaste', '1']), + TableRow([TableData(["Cutlery"])]), + TableRow(["Spoons", "2"]), + TableRow(["Knifes", "5"]), + TableRow([TableData(["Bathroom"])]), + TableRow(["Soap", "2"]), + TableRow(["Toothpaste", "1"]), ] ) - assert table.contents_after('Bathroom') == ( - TableRow(['Soap', '2']), - TableRow(['Toothpaste', '1']), + assert table.contents_after("Bathroom") == ( + TableRow(["Soap", "2"]), + TableRow(["Toothpaste", "1"]), ) - assert table.contents_after('Soap') == (TableRow(['Toothpaste', '1']),) + assert table.contents_after("Soap") == (TableRow(["Toothpaste", "1"]),) def test_content_converter() -> None: @@ -62,17 +62,17 @@ def test_content_converter() -> None: table = Table( [ - dict(tag='tr', contents=['1', 'Cutlery']), # type:ignore[list-item] + dict(tag="tr", contents=["1", "Cutlery"]), # type:ignore[list-item] TableRow( [ - '', - 'Spoon', + "", + "Spoon", dict( # type:ignore[list-item] - tag='td', + tag="td", contents=[ dict( - tag='img', - src='spoon.gif', + tag="img", + src="spoon.gif", height=5, width=10, ) @@ -84,7 +84,7 @@ def test_content_converter() -> None: ) assert table == Table( [ - TableRow(['1', 'Cutlery']), - TableRow(['', 'Spoon', TableData([Image(src='spoon.gif', height=5, width=10)])]), + TableRow(["1", "Cutlery"]), + TableRow(["", "Spoon", TableData([Image(src="spoon.gif", height=5, width=10)])]), ] ) diff --git a/tests/test_fixtures.py b/tests/test_fixtures.py index ddeea80d..ed2b78f8 100644 --- a/tests/test_fixtures.py +++ b/tests/test_fixtures.py @@ -1,6 +1,6 @@ import os -pytest_plugins = 'pytester' +pytest_plugins = "pytester" def test_port_fixture(testdir) -> None: @@ -10,10 +10,10 @@ def test_port_fixture(testdir) -> None: """ import shutil - shutil.copy(os.path.join(os.path.dirname(__file__), 'conftest.py'), str(testdir.tmpdir)) + shutil.copy(os.path.join(os.path.dirname(__file__), "conftest.py"), str(testdir.tmpdir)) testdir.makepyfile( - test_port=''' + test_port=""" import pytest def test_port(port) -> None: @@ -23,7 +23,7 @@ def test_port(port) -> None: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) with pytest.raises(socket.error): s.connect(('localhost', port_)) - ''' + """ ) result = testdir.runpytest() - result.stdout.fnmatch_lines(['*1 passed*']) + result.stdout.fnmatch_lines(["*1 passed*"]) diff --git a/tests/test_js_graph.py b/tests/test_js_graph.py index 961da679..9785ef56 100644 --- a/tests/test_js_graph.py +++ b/tests/test_js_graph.py @@ -22,7 +22,7 @@ def test_resize_container(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('empty') + graph = graph_cases("empty") width, height = graph.get_container_size() new_width = width + 20 @@ -38,7 +38,7 @@ def test_insert_vertex(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('1v') + graph = graph_cases("1v") assert graph.get_vertex() is not None @@ -46,7 +46,7 @@ def test_insert_vertex_with_zoom_and_scale(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('empty') + graph = graph_cases("empty") x = 10 y = 20 w = 30 @@ -54,25 +54,25 @@ def test_insert_vertex_with_zoom_and_scale(graph_cases) -> None: scale = 2 trans_x = 8 trans_y = 7 - graph.eval_js_function('api.setScaleAndTranslation', scale, trans_x, trans_y) + graph.eval_js_function("api.setScaleAndTranslation", scale, trans_x, trans_y) # `adjustXYCcoordinates` value `undefined` defaults to `true`. - graph.eval_js_function('api.insertVertex', x, y, w, h, '', '', {}, 'adjusted-1') - graph.eval_js_function('api.insertVertex', x, y, w, h, '', '', {}, 'adjusted-2', True) - adjusted1_bounds = graph.eval_js_function('api.getCellBounds', 'adjusted-1') - adjusted2_bounds = graph.eval_js_function('api.getCellBounds', 'adjusted-2') + graph.eval_js_function("api.insertVertex", x, y, w, h, "", "", {}, "adjusted-1") + graph.eval_js_function("api.insertVertex", x, y, w, h, "", "", {}, "adjusted-2", True) + adjusted1_bounds = graph.eval_js_function("api.getCellBounds", "adjusted-1") + adjusted2_bounds = graph.eval_js_function("api.getCellBounds", "adjusted-2") assert adjusted2_bounds == adjusted1_bounds - assert adjusted1_bounds['x'] == (x / scale) - trans_x == -3 - assert adjusted1_bounds['y'] == (y / scale) - trans_y == 3 + assert adjusted1_bounds["x"] == (x / scale) - trans_x == -3 + assert adjusted1_bounds["y"] == (y / scale) - trans_y == 3 # When `adjustXYCcoordinates` is `false` values are taken on face value. - graph.eval_js_function('api.insertVertex', x, y, w, h, '', '', {}, 'absolute', False) - absolute_bounds = graph.eval_js_function('api.getCellBounds', 'absolute') - assert absolute_bounds['x'] == x - assert absolute_bounds['y'] == y + graph.eval_js_function("api.insertVertex", x, y, w, h, "", "", {}, "absolute", False) + absolute_bounds = graph.eval_js_function("api.getCellBounds", "absolute") + assert absolute_bounds["x"] == x + assert absolute_bounds["y"] == y -@pytest.mark.parametrize('dumped,restored', [('1v', '2v'), ('2v', '1v')]) +@pytest.mark.parametrize("dumped,restored", [("1v", "2v"), ("2v", "1v")]) def test_dump_restore(dumped, restored, graph_cases) -> None: """ :type dumped: str @@ -81,11 +81,11 @@ def test_dump_restore(dumped, restored, graph_cases) -> None: """ graph = graph_cases(dumped) dumped_vertices_count = len(graph.get_vertices()) - dump = graph.eval_js_function('api.dump') + dump = graph.eval_js_function("api.dump") del graph graph = graph_cases(restored) assert dumped_vertices_count != len(graph.get_vertices()) - graph.eval_js_function('api.restore', dump) + graph.eval_js_function("api.restore", dump) assert dumped_vertices_count == len(graph.get_vertices()) @@ -93,7 +93,7 @@ def test_insert_vertex_with_style(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('1v_style') + graph = graph_cases("1v_style") vertex = graph.get_vertex() # Can't have same color as default vertex style @@ -102,25 +102,25 @@ def test_insert_vertex_with_style(graph_cases) -> None: ) default = default.lower() - assert vertex.get_attribute('fill') != default + assert vertex.get_attribute("fill") != default @pytest.mark.parametrize( - 'mode', + "mode", [ - 'by_code', - 'by_drag_drop', + "by_code", + "by_drag_drop", ], ) def test_insert_edge(graph_cases, mode) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - if mode == 'by_code': - case = '2v_1e' + if mode == "by_code": + case = "2v_1e" else: - assert mode == 'by_drag_drop' - case = '2v_1eDD' + assert mode == "by_drag_drop" + case = "2v_1eDD" graph = graph_cases(case) assert graph.get_edge(*graph.get_vertices()) is not None @@ -129,7 +129,7 @@ def test_get_terminal_points(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('2v_1e') + graph = graph_cases("2v_1e") edge_id = graph.get_id(graph.get_edge(*graph.get_vertices())) terminal_points = graph.eval_js_function("api.getEdgeTerminalPoints", edge_id) (source_x, source_y), (target_x, target_y) = terminal_points @@ -145,7 +145,7 @@ def test_insert_edge_error_endpoint_not_found(graph_cases, selenium_extras) -> N :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory :type selenium_extras: qmxgraph.tests.conftest.SeleniumExtras """ - graph = graph_cases('1v') + graph = graph_cases("1v") vertex = graph.get_vertices()[0] invalid_source_id = invalid_target_id = "999" @@ -171,11 +171,11 @@ def test_insert_decoration(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('2v_1e_1d') + graph = graph_cases("2v_1e_1d") assert len(graph.get_decorations()) == 1 graph.eval_js_function( - 'api.insertDecorationOnEdge', graph.edge_id, 0.75, 10, 10, 'another decoration', 'purple' + "api.insertDecorationOnEdge", graph.edge_id, 0.75, 10, 10, "another decoration", "purple" ) assert len(graph.get_decorations()) == 2 @@ -184,14 +184,14 @@ def test_decoration_position(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('2v_1e_1d') + graph = graph_cases("2v_1e_1d") cell_id = graph.get_id(graph.get_decorations()[0]) - position = graph.eval_js_function('api.getDecorationPosition', cell_id) + position = graph.eval_js_function("api.getDecorationPosition", cell_id) assert position == pytest.approx(0.4) - graph.eval_js_function('api.setDecorationPosition', cell_id, 0.8) - position = graph.eval_js_function('api.getDecorationPosition', cell_id) + graph.eval_js_function("api.setDecorationPosition", cell_id, 0.8) + position = graph.eval_js_function("api.getDecorationPosition", cell_id) assert position == pytest.approx(0.8) @@ -199,17 +199,17 @@ def test_get_decoration_parent_cell_id(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('2v_1e_1d') + graph = graph_cases("2v_1e_1d") cell_id = graph.get_id(graph.get_decorations()[0]) - parent_id = graph.eval_js_function('api.getDecorationParentCellId', cell_id) - assert parent_id == '4' + parent_id = graph.eval_js_function("api.getDecorationParentCellId", cell_id) + assert parent_id == "4" def test_delete_vertex(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('1v') + graph = graph_cases("1v") graph.select_vertex(graph.get_vertex()) actions = ActionChains(graph.selenium) @@ -224,7 +224,7 @@ def test_delete_edge(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('2v_1e') + graph = graph_cases("2v_1e") graph.select_edge(graph.get_edge(*graph.get_vertices())) actions = ActionChains(graph.selenium) @@ -239,7 +239,7 @@ def test_group(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('2v') + graph = graph_cases("2v") actions = ActionChains(graph.selenium) actions.key_down(Keys.CONTROL) @@ -256,7 +256,7 @@ def test_group(graph_cases) -> None: # Group selected vertices graph.selenium.execute_script("api.group()") - group_fill = graph.host.styles['group']['fill_color'] + group_fill = graph.host.styles["group"]["fill_color"] group_selector = 'g>g>rect[fill="{}"]'.format(group_fill) group = graph.selenium.find_elements(By.CSS_SELECTOR, group_selector) assert len(group) == 1 @@ -277,11 +277,11 @@ def test_toggle_outline(selenium, host, wait_graph_page_ready) -> None: # By default, outline starts hidden. Basically this means mxGraph's window # component used to shown outline doesn't exist yet. with pytest.raises(NoSuchElementException): - selenium.find_element(By.CSS_SELECTOR, 'div.mxWindow') + selenium.find_element(By.CSS_SELECTOR, "div.mxWindow") # Once shown, outline is displayed in a mxGraph's window component selenium.execute_script("api.toggleOutline()") - outline = selenium.find_element(By.CSS_SELECTOR, 'div.mxWindow') + outline = selenium.find_element(By.CSS_SELECTOR, "div.mxWindow") assert outline is not None # However once toggled back to hidden, it is not destroyed but simply @@ -290,7 +290,7 @@ def test_toggle_outline(selenium, host, wait_graph_page_ready) -> None: assert not outline.is_displayed() -@pytest.mark.parametrize('grid', [True, False]) +@pytest.mark.parametrize("grid", [True, False]) def test_toggle_grid(selenium, host, grid, wait_graph_page_ready) -> None: """ :type selenium: selenium.webdriver.remote.webdriver.WebDriver @@ -303,17 +303,17 @@ def test_toggle_grid(selenium, host, grid, wait_graph_page_ready) -> None: if not grid: selenium.execute_script("api.toggleGrid()") - container = selenium.find_element(By.CSS_SELECTOR, 'div.graph') - assert container.get_attribute('id') == 'graphContainer' - assert container.get_attribute('class') == ('graph' if grid else 'graph hide-bg') + container = selenium.find_element(By.CSS_SELECTOR, "div.graph") + assert container.get_attribute("id") == "graphContainer" + assert container.get_attribute("class") == ("graph" if grid else "graph hide-bg") -@pytest.mark.parametrize('snap', [True, False]) +@pytest.mark.parametrize("snap", [True, False]) def test_toggle_snap(graph_cases, snap) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('1v') + graph = graph_cases("1v") selenium = graph.selenium # If snap is enabled, it should move to closest grid block (which are @@ -339,30 +339,30 @@ def expected(v): result = math.ceil(result / 10.0) * 10 return result - assert int(vertex.get_attribute('width')) == w - assert int(vertex.get_attribute('height')) == h - assert int(vertex.get_attribute('x')) == expected(x) - assert int(vertex.get_attribute('y')) == expected(y) + assert int(vertex.get_attribute("width")) == w + assert int(vertex.get_attribute("height")) == h + assert int(vertex.get_attribute("x")) == expected(x) + assert int(vertex.get_attribute("y")) == expected(y) def test_get_cell_id_at(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('2v_1e_1d_1t') + graph = graph_cases("2v_1e_1d_1t") # mxGraph shares a global id counter for all cell types. The first # non-reserved id is 2, as lower values are used by internal control # structures. - assert graph.get_id(graph.get_vertices()[0]) == '2' - assert graph.get_id(graph.get_vertices()[1]) == '3' - assert graph.get_id(graph.get_edge(*graph.get_vertices())) == '4' - assert graph.get_id(graph.get_decorations()[0]) == '5' - assert graph.get_id(graph.get_tables()[0]) == '6' + assert graph.get_id(graph.get_vertices()[0]) == "2" + assert graph.get_id(graph.get_vertices()[1]) == "3" + assert graph.get_id(graph.get_edge(*graph.get_vertices())) == "4" + assert graph.get_id(graph.get_decorations()[0]) == "5" + assert graph.get_id(graph.get_tables()[0]) == "6" class Invalid: def __init__(self): - self.location = {'x': 999, 'y': 999} + self.location = {"x": 999, "y": 999} assert graph.get_id(Invalid()) is None @@ -371,7 +371,7 @@ def test_set_visible(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('2v_1e_1d_1t') + graph = graph_cases("2v_1e_1d_1t") # Hide then show vertex again vertices = graph.get_vertices() @@ -409,18 +409,18 @@ def test_is_and_set_port_visible(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('1v_1p') + graph = graph_cases("1v_1p") vertex_id = graph.get_id(graph.get_vertex()) - assert graph.eval_js_function('api.isPortVisible', vertex_id, 'foo') + assert graph.eval_js_function("api.isPortVisible", vertex_id, "foo") assert graph.get_port() is not None - graph.eval_js_function('api.setPortVisible', vertex_id, 'foo', False) - assert not graph.eval_js_function('api.isPortVisible', vertex_id, 'foo') + graph.eval_js_function("api.setPortVisible", vertex_id, "foo", False) + assert not graph.eval_js_function("api.isPortVisible", vertex_id, "foo") assert graph.get_port() is None - graph.eval_js_function('api.setPortVisible', vertex_id, 'foo', True) - assert graph.eval_js_function('api.isPortVisible', vertex_id, 'foo') + graph.eval_js_function("api.setPortVisible", vertex_id, "foo", True) + assert graph.eval_js_function("api.isPortVisible", vertex_id, "foo") assert graph.get_port() is not None @@ -428,9 +428,9 @@ def test_parse_port_id(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('empty') - port_data = graph.eval_js_function('mxCell.parsePortId', 'qmxgraph-port-PARENT-PORT-NAME') - assert port_data == ['PARENT', 'PORT-NAME'] + graph = graph_cases("empty") + port_data = graph.eval_js_function("mxCell.parsePortId", "qmxgraph-port-PARENT-PORT-NAME") + assert port_data == ["PARENT", "PORT-NAME"] def test_set_visible_error_not_found(graph_cases, selenium_extras) -> None: @@ -438,8 +438,8 @@ def test_set_visible_error_not_found(graph_cases, selenium_extras) -> None: :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory :type selenium_extras: qmxgraph.tests.conftest.SeleniumExtras """ - graph = graph_cases('2v_1e_1d_1t') - cell_id = '999' + graph = graph_cases("2v_1e_1d_1t") + cell_id = "999" with pytest.raises(WebDriverException) as e: graph.set_visible(cell_id, False) @@ -456,7 +456,7 @@ def test_get_geometry_plain(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('2v_1e_1d_1t') + graph = graph_cases("2v_1e_1d_1t") assert graph.get_geometry(graph.get_vertices()[0]) == [10, 10, 30, 30] assert graph.get_geometry(graph.get_edge(*graph.get_vertices())) == [40, 25, 50, 1] @@ -472,7 +472,7 @@ def test_get_geometry_error_not_found(graph_cases, selenium_extras) -> None: :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory :type selenium_extras: qmxgraph.tests.conftest.SeleniumExtras """ - graph = graph_cases('2v_1e_1d_1t') + graph = graph_cases("2v_1e_1d_1t") cell_id = "999" with pytest.raises(WebDriverException) as e: @@ -485,13 +485,13 @@ def test_insert_table(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('1t') + graph = graph_cases("1t") assert len(graph.get_tables()) == 1 @pytest.mark.parametrize( - 'action, expected_scale', - [(None, 1.0), ('zoomIn', 1.2), ('zoomOut', 0.83)], + "action, expected_scale", + [(None, 1.0), ("zoomIn", 1.2), ("zoomOut", 0.83)], ) def test_insert_child_table(graph_cases, action, expected_scale) -> None: """ @@ -501,19 +501,19 @@ def test_insert_child_table(graph_cases, action, expected_scale) -> None: :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('1t') + graph = graph_cases("1t") # Applying zoom, so it changes the scale and transformation - obtained_scale = graph.eval_js_function('api.getZoomScale') + obtained_scale = graph.eval_js_function("api.getZoomScale") assert obtained_scale == 1.0 - ini_scale, ini_x, ini_y = graph.eval_js_function('api.getScaleAndTranslation') + ini_scale, ini_x, ini_y = graph.eval_js_function("api.getScaleAndTranslation") assert (ini_scale, ini_x, ini_y) == (1, 0, 0) if action is not None: - graph.eval_js_function('api.{}'.format(action)) - obtained_scale = graph.eval_js_function('api.getZoomScale') + graph.eval_js_function("api.{}".format(action)) + obtained_scale = graph.eval_js_function("api.getZoomScale") assert obtained_scale == expected_scale - ini_scale, ini_x, ini_y = graph.eval_js_function('api.getScaleAndTranslation') + ini_scale, ini_x, ini_y = graph.eval_js_function("api.getScaleAndTranslation") assert ini_scale != 1 assert ini_x != 0 assert ini_y != 0 @@ -522,19 +522,19 @@ def test_insert_child_table(graph_cases, action, expected_scale) -> None: assert len(tables) == 1 parent_id = graph.get_id(tables[0]) child_id = graph.eval_js_function( - 'api.insertTable', 0.5, 1.5, 300, {'contents': []}, 'foobar', None, None, parent_id + "api.insertTable", 0.5, 1.5, 300, {"contents": []}, "foobar", None, None, parent_id ) tables = graph.get_tables() assert len(tables) == 2 # After resetting the zoom, bounds of the tables must respect the # constraints being tested below between parent and child bounds - graph.eval_js_function('api.resetZoom') - obtained_scale = graph.eval_js_function('api.getZoomScale') + graph.eval_js_function("api.resetZoom") + obtained_scale = graph.eval_js_function("api.getZoomScale") assert obtained_scale == 1.0 def get_bounds(cell_id): - return graph.eval_js_function('api.getGeometry', cell_id) + return graph.eval_js_function("api.getGeometry", cell_id) parent_bounds = get_bounds(parent_id) child_bounds = get_bounds(child_id) @@ -546,23 +546,23 @@ def test_table_with_image(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('1t') + graph = graph_cases("1t") tables = graph.get_tables() assert len(tables) == 1 table_id = graph.get_id(tables[0]) contents = { # graphs.utils.TableRowDescription - 'contents': [ + "contents": [ { # graphs.utils.TableDataDescription - 'contents': [ + "contents": [ { # graphs.utils.TableDataDescription - 'contents': [ - 'foo ', + "contents": [ + "foo ", { - 'tag': 'img', - 'src': 'some-image-path', - 'width': 16, - 'height': 16, + "tag": "img", + "src": "some-image-path", + "width": 16, + "height": 16, }, ] } @@ -570,34 +570,34 @@ def test_table_with_image(graph_cases) -> None: } ] } - graph.eval_js_function('api.updateTable', table_id, contents, '') + graph.eval_js_function("api.updateTable", table_id, contents, "") - image_elements = graph.selenium.find_elements(By.CSS_SELECTOR, '.table-cell-contents img') + image_elements = graph.selenium.find_elements(By.CSS_SELECTOR, ".table-cell-contents img") assert len(image_elements) == 1 image = image_elements[0] - assert image.get_attribute('src').endswith('some-image-path') + assert image.get_attribute("src").endswith("some-image-path") def test_update_table(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('1t') + graph = graph_cases("1t") table_id = graph.get_id(graph.get_tables()[0]) contents = { # graphs.utils.TableDescription - 'contents': [ + "contents": [ # graphs.utils.TableRowDescription's - {'contents': ['a', 1]}, - {'contents': ['b', 2]}, + {"contents": ["a", 1]}, + {"contents": ["b", 2]}, ] } - title = 'updated' - graph.selenium.execute_script(js.prepare_js_call('api.updateTable', table_id, contents, title)) + title = "updated" + graph.selenium.execute_script(js.prepare_js_call("api.updateTable", table_id, contents, title)) table = graph.get_tables()[0] - assert graph.get_table_title(table) == 'updated' - assert graph.get_table_contents(table) == ['a', '1', 'b', '2'] + assert graph.get_table_title(table) == "updated" + assert graph.get_table_contents(table) == ["a", "1", "b", "2"] def test_update_table_error_not_found(graph_cases, selenium_extras) -> None: @@ -605,15 +605,15 @@ def test_update_table_error_not_found(graph_cases, selenium_extras) -> None: :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory :type selenium_extras: qmxgraph.tests.conftest.SeleniumExtras """ - graph = graph_cases('1t') + graph = graph_cases("1t") table_id = "999" contents: List[Any] = [] - title = 'will not matter' + title = "will not matter" with pytest.raises(WebDriverException) as e: graph.selenium.execute_script( - js.prepare_js_call('api.updateTable', table_id, contents, title) + js.prepare_js_call("api.updateTable", table_id, contents, title) ) assert f"Unable to find cell with id {table_id}" in selenium_extras.get_exception_message(e) @@ -624,15 +624,15 @@ def test_update_table_error_not_table(graph_cases, selenium_extras) -> None: :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory :type selenium_extras: qmxgraph.tests.conftest.SeleniumExtras """ - graph = graph_cases('2v_1e_1d_1t') + graph = graph_cases("2v_1e_1d_1t") table_id = graph.get_id(graph.get_edge(*graph.get_vertices())) contents: List[Any] = [] - title = 'will not matter' + title = "will not matter" with pytest.raises(WebDriverException) as e: graph.selenium.execute_script( - js.prepare_js_call('api.updateTable', table_id, contents, title) + js.prepare_js_call("api.updateTable", table_id, contents, title) ) assert "Cell is not a table" in selenium_extras.get_exception_message(e) @@ -642,14 +642,14 @@ def test_remove_cells(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('2v_1e') + graph = graph_cases("2v_1e") vertices = graph.get_vertices() cell_ids = [ graph.get_id(vertices[0]), graph.get_id(graph.get_edge(*vertices)), ] - graph.eval_js_function('api.removeCells', cell_ids) + graph.eval_js_function("api.removeCells", cell_ids) assert len(graph.get_vertices()) == 1 assert graph.get_edge(*vertices) is None @@ -660,11 +660,11 @@ def test_remove_cells_error_not_found(graph_cases, selenium_extras) -> None: :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory :type selenium_extras: qmxgraph.tests.conftest.SeleniumExtras """ - graph = graph_cases('empty') + graph = graph_cases("empty") cell_id = 999 with pytest.raises(WebDriverException) as e: - graph.eval_js_function('api.removeCells', [cell_id]) + graph.eval_js_function("api.removeCells", [cell_id]) assert f"Unable to find cell with id {cell_id}" in selenium_extras.get_exception_message(e) @@ -673,18 +673,18 @@ def test_on_cells_removed(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('2v_1e') + graph = graph_cases("2v_1e") - graph.selenium.execute_script('callback = function(cellIds) {window.cellIds = cellIds;}') - graph.eval_js_function('api.registerCellsRemovedHandler', js.Variable('callback')) + graph.selenium.execute_script("callback = function(cellIds) {window.cellIds = cellIds;}") + graph.eval_js_function("api.registerCellsRemovedHandler", js.Variable("callback")) cell_ids = [ graph.get_id(graph.get_vertices()[0]), graph.get_id(graph.get_edge(*graph.get_vertices())), ] - graph.eval_js_function('api.removeCells', cell_ids) + graph.eval_js_function("api.removeCells", cell_ids) - assert graph.selenium.execute_script('return window.cellIds') == cell_ids + assert graph.selenium.execute_script("return window.cellIds") == cell_ids def test_custom_shapes(selenium, port, tmpdir, wait_graph_page_ready) -> None: @@ -693,7 +693,7 @@ def test_custom_shapes(selenium, port, tmpdir, wait_graph_page_ready) -> None: :type port: qmxgraph.tests.conftest.Port """ # Shape found in by https://www.draw.io/stencils/basic.xml - custom_stencil = '''\ + custom_stencil = """\ @@ -712,7 +712,7 @@ def test_custom_shapes(selenium, port, tmpdir, wait_graph_page_ready) -> None: -''' # noqa +""" # noqa stencil_file = tmpdir.mkdir("stencils").join("custom.xml") stencil_file.write(custom_stencil) @@ -720,9 +720,9 @@ def test_custom_shapes(selenium, port, tmpdir, wait_graph_page_ready) -> None: styles = GraphStyles( { - 'moon': { - 'shape': 'Moon', - 'fill_color': '#ffff00', + "moon": { + "shape": "Moon", + "fill_color": "#ffff00", }, } ) @@ -738,10 +738,10 @@ def has_custom_shape(): @pytest.mark.parametrize( - 'mode', + "mode", [ - 'by_code', - 'by_drag_drop', + "by_code", + "by_drag_drop", ], ) def test_edge_with_style(port, mode, graph_cases_factory) -> None: @@ -752,28 +752,28 @@ def test_edge_with_style(port, mode, graph_cases_factory) -> None: """ styles = GraphStyles( { - 'edge': { - 'stroke_color': '#000000', + "edge": { + "stroke_color": "#000000", }, } ) with server.host(port=port.get(), styles=styles) as host: cases = graph_cases_factory(host) - graph = cases('2v_1e' if mode == 'by_code' else '2v_1eDD') - assert graph.get_edge(*graph.get_vertices()).get_attribute('stroke') == '#000000' + graph = cases("2v_1e" if mode == "by_code" else "2v_1eDD") + assert graph.get_edge(*graph.get_vertices()).get_attribute("stroke") == "#000000" def test_get_label(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('2v_1e_1d_1t') + graph = graph_cases("2v_1e_1d_1t") - assert graph.get_label(graph.get_vertices()[0]) == 'foo' - assert graph.get_label(graph.get_vertices()[1]) == 'bar' - assert graph.get_label(graph.get_edge(*graph.get_vertices())) == 'edge' - assert graph.get_label(graph.get_decorations()[0]) == 'decoration' + assert graph.get_label(graph.get_vertices()[0]) == "foo" + assert graph.get_label(graph.get_vertices()[1]) == "bar" + assert graph.get_label(graph.get_edge(*graph.get_vertices())) == "edge" + assert graph.get_label(graph.get_decorations()[0]) == "decoration" # Tables use a complex label in HTML table_label = graph.get_label(graph.get_tables()[0]) @@ -798,7 +798,7 @@ def handle_data(self, data): parser = TableHTMLParser() parser.feed(table_label) - assert table_html_data == ['Hitchhikers', 'arthur', 'dent', 'ford', 'prefect'] + assert table_html_data == ["Hitchhikers", "arthur", "dent", "ford", "prefect"] def test_get_label_error_not_found(graph_cases, selenium_extras) -> None: @@ -806,7 +806,7 @@ def test_get_label_error_not_found(graph_cases, selenium_extras) -> None: :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory :type selenium_extras: qmxgraph.tests.conftest.SeleniumExtras """ - graph = graph_cases('2v_1e_1d_1t') + graph = graph_cases("2v_1e_1d_1t") cell_id = "999" with pytest.raises(WebDriverException) as e: @@ -819,7 +819,7 @@ def test_has_cell(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('empty') + graph = graph_cases("empty") cell_id = graph.insert_vertex(x=10, y=10) assert graph.eval_js_function("api.hasCell", cell_id) @@ -831,11 +831,11 @@ def test_get_cell_type(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('2v_1e_1d_1t') + graph = graph_cases("2v_1e_1d_1t") def get_cell_type(web_element): cell_id = graph.get_id(web_element) - return graph.eval_js_function('api.getCellType', cell_id) + return graph.eval_js_function("api.getCellType", cell_id) assert get_cell_type(graph.get_vertices()[0]) == constants.CELL_TYPE_VERTEX @@ -851,7 +851,7 @@ def test_get_cell_type_error_not_found(graph_cases, selenium_extras) -> None: :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory :type selenium_extras: qmxgraph.tests.conftest.SeleniumExtras """ - graph = graph_cases('empty') + graph = graph_cases("empty") cell_id = "999" @@ -862,7 +862,7 @@ def test_get_cell_type_error_not_found(graph_cases, selenium_extras) -> None: @pytest.mark.parametrize( - 'cell_type', + "cell_type", [ qmxgraph.constants.CELL_TYPE_VERTEX, qmxgraph.constants.CELL_TYPE_EDGE, @@ -875,33 +875,33 @@ def test_insert_with_tags(graph_cases, cell_type) -> None: :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory :type cell_type: qmxgraph.constants.CELL_TYPE_* """ - graph = graph_cases('empty') + graph = graph_cases("empty") # Listen to on cells added event to be sure tags are already configured # as soon as cell is created graph.selenium.execute_script( - 'callback = function(cellIds) {' - ' window.tags = cellIds.map(' - ' function(cellId) {' + "callback = function(cellIds) {" + " window.tags = cellIds.map(" + " function(cellId) {" ' return api.hasTag(cellId, "tagTest")? api.getTag(cellId, "tagTest") : null;' # noqa - ' }' - ' );' - '}' + " }" + " );" + "}" ) - graph.eval_js_function('api.registerCellsAddedHandler', js.Variable('callback')) - tags = {'tagTest': '1'} + graph.eval_js_function("api.registerCellsAddedHandler", js.Variable("callback")) + tags = {"tagTest": "1"} cell_id = insert_by_parametrized_type(graph, cell_type, tags=tags) assert ( graph.selenium.execute_script("return api.getTag({}, 'tagTest')".format(cell_id)) - == tags['tagTest'] + == tags["tagTest"] ) - assert graph.selenium.execute_script('return window.tags') == [tags['tagTest']] + assert graph.selenium.execute_script("return window.tags") == [tags["tagTest"]] @pytest.mark.parametrize( - 'cell_type', + "cell_type", [ qmxgraph.constants.CELL_TYPE_VERTEX, qmxgraph.constants.CELL_TYPE_EDGE, @@ -915,7 +915,7 @@ def test_insert_with_tags_error_value_not_string(graph_cases, cell_type, seleniu :type cell_type: qmxgraph.constants.CELL_TYPE_* :type selenium_extras: qmxgraph.tests.conftest.SeleniumExtras """ - graph = graph_cases('empty') + graph = graph_cases("empty") tag_name = "tagTest" tags = {tag_name: 999} @@ -927,7 +927,7 @@ def test_insert_with_tags_error_value_not_string(graph_cases, cell_type, seleniu @pytest.mark.parametrize( - 'cell_type', + "cell_type", [ qmxgraph.constants.CELL_TYPE_VERTEX, qmxgraph.constants.CELL_TYPE_EDGE, @@ -940,7 +940,7 @@ def test_set_get_tag(graph_cases, cell_type) -> None: :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory :type cell_type: qmxgraph.constants.CELL_TYPE_* """ - graph = graph_cases('empty') + graph = graph_cases("empty") cell_id = insert_by_parametrized_type(graph, cell_type) @@ -951,7 +951,7 @@ def test_set_get_tag(graph_cases, cell_type) -> None: @pytest.mark.parametrize( - 'cell_type', + "cell_type", [ qmxgraph.constants.CELL_TYPE_VERTEX, qmxgraph.constants.CELL_TYPE_EDGE, @@ -965,7 +965,7 @@ def test_set_get_tag_error_tag_not_found(graph_cases, cell_type, selenium_extras :type cell_type: qmxgraph.constants.CELL_TYPE_* :type selenium_extras: qmxgraph.tests.conftest.SeleniumExtras """ - graph = graph_cases('empty') + graph = graph_cases("empty") cell_id = insert_by_parametrized_type(graph, cell_type) tag_name = "test" @@ -981,7 +981,7 @@ def test_set_get_tag_error_tag_not_found(graph_cases, cell_type, selenium_extras @pytest.mark.parametrize( - 'cell_type', + "cell_type", [ qmxgraph.constants.CELL_TYPE_VERTEX, qmxgraph.constants.CELL_TYPE_EDGE, @@ -995,7 +995,7 @@ def test_set_get_tag_error_value_not_string(graph_cases, cell_type, selenium_ext :type cell_type: qmxgraph.constants.CELL_TYPE_* :type selenium_extras: qmxgraph.tests.conftest.SeleniumExtras """ - graph = graph_cases('empty') + graph = graph_cases("empty") cell_id = insert_by_parametrized_type(graph, cell_type) tag_name = "test" @@ -1007,7 +1007,7 @@ def test_set_get_tag_error_value_not_string(graph_cases, cell_type, selenium_ext @pytest.mark.parametrize( - 'cell_type', + "cell_type", [ qmxgraph.constants.CELL_TYPE_VERTEX, qmxgraph.constants.CELL_TYPE_EDGE, @@ -1020,7 +1020,7 @@ def test_set_get_tag_doesnt_overwrite_protected_tags(graph_cases, cell_type) -> :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory :type cell_type: qmxgraph.constants.CELL_TYPE_* """ - graph = graph_cases('empty') + graph = graph_cases("empty") cell_id = insert_by_parametrized_type(graph, cell_type) assert not graph.eval_js_function("api.hasTag", cell_id, "label") @@ -1036,7 +1036,7 @@ def test_set_get_tag_error_cell_not_found(graph_cases, selenium_extras) -> None: :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory :type selenium_extras: qmxgraph.tests.conftest.SeleniumExtras """ - graph = graph_cases('empty') + graph = graph_cases("empty") cell_id = "999" @@ -1069,7 +1069,7 @@ def test_set_get_tag_without_initial_tag_support(graph_cases) -> None: :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('2v') + graph = graph_cases("2v") graph.insert_edge_by_drag_drop(*graph.get_vertices()) edge = graph.get_edge(*graph.get_vertices()) @@ -1085,7 +1085,7 @@ def test_on_cells_added(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('2v_1e_1d_1t') + graph = graph_cases("2v_1e_1d_1t") added = [ graph.get_id(graph.get_vertices()[0]), @@ -1102,53 +1102,53 @@ def test_on_label_changed(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('1v') + graph = graph_cases("1v") vertex_id = graph.get_id(graph.get_vertex()) # Sanity check: custom tags are internally stored in same node element as # label. This is to make sure tags aren't lost when label is changed by # mistakenly overwriting whole node element instead of just label. - graph.eval_js_function('api.setTag', vertex_id, 'test', 'test') + graph.eval_js_function("api.setTag", vertex_id, "test", "test") label = graph.get_label(graph.get_vertex()) label_element = graph.get_label_element(graph.get_vertex()) actions = ActionChains(graph.selenium) actions.double_click(label_element) - actions.send_keys('foo') + actions.send_keys("foo") actions.click(graph.get_container()) # to lose focus and confirm actions.perform() - assert graph.get_label(graph.get_vertex()) == 'foo' + assert graph.get_label(graph.get_vertex()) == "foo" label_changes = graph.get_label_changes() assert label_changes == [ { - 'cellId': vertex_id, - 'newLabel': 'foo', - 'oldLabel': label, + "cellId": vertex_id, + "newLabel": "foo", + "oldLabel": label, } ] - assert graph.eval_js_function('api.getTag', vertex_id, 'test') == 'test' + assert graph.eval_js_function("api.getTag", vertex_id, "test") == "test" def test_set_label(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('1v') + graph = graph_cases("1v") vertex_id = graph.get_id(graph.get_vertex()) label = graph.get_label(graph.get_vertex()) - graph.eval_js_function('api.setLabel', vertex_id, 'foo') + graph.eval_js_function("api.setLabel", vertex_id, "foo") - assert graph.get_label(graph.get_vertex()) == 'foo' + assert graph.get_label(graph.get_vertex()) == "foo" label_changes = graph.get_label_changes() assert label_changes == [ { - 'cellId': vertex_id, - 'newLabel': 'foo', - 'oldLabel': label, + "cellId": vertex_id, + "newLabel": "foo", + "oldLabel": label, } ] @@ -1162,7 +1162,7 @@ def test_set_label_error_not_found(graph_cases, selenium_extras) -> None: cell_id = "999" with pytest.raises(WebDriverException) as e: - graph.eval_js_function('api.setLabel', cell_id, 'foo') + graph.eval_js_function("api.setLabel", cell_id, "foo") assert f"Unable to find cell with id {cell_id}" in selenium_extras.get_exception_message(e) @@ -1171,24 +1171,24 @@ def test_set_double_click_handler(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('1v') + graph = graph_cases("1v") vertex_id = graph.get_id(graph.get_vertex()) graph.selenium.execute_script( - 'callback = function(cellId) {' - ' if (!window.__dblClick__) {' - ' window.__dblClick__ = [];' - ' }' - ' window.__dblClick__.push(cellId);' - '}' + "callback = function(cellId) {" + " if (!window.__dblClick__) {" + " window.__dblClick__ = [];" + " }" + " window.__dblClick__.push(cellId);" + "}" ) - graph.eval_js_function('api.registerDoubleClickHandler', qmxgraph.js.Variable('callback')) + graph.eval_js_function("api.registerDoubleClickHandler", qmxgraph.js.Variable("callback")) actions = ActionChains(graph.selenium) actions.double_click(graph.get_vertex()) actions.perform() - assert graph.selenium.execute_script('return window.__dblClick__') == [vertex_id] + assert graph.selenium.execute_script("return window.__dblClick__") == [vertex_id] def test_add_selection_change_handler(graph_cases) -> None: @@ -1196,19 +1196,19 @@ def test_add_selection_change_handler(graph_cases) -> None: :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('2v_1e') + graph = graph_cases("2v_1e") source, target = graph.get_vertices() edge = graph.get_edge(source, target) graph.selenium.execute_script( - 'callback = function(cellIds) {' - ' if (!window.__selectionChange__) {' - ' window.__selectionChange__ = [];' - ' }' - ' window.__selectionChange__.push(cellIds);' - '}' + "callback = function(cellIds) {" + " if (!window.__selectionChange__) {" + " window.__selectionChange__ = [];" + " }" + " window.__selectionChange__.push(cellIds);" + "}" ) - graph.eval_js_function('api.registerSelectionChangedHandler', qmxgraph.js.Variable('callback')) + graph.eval_js_function("api.registerSelectionChangedHandler", qmxgraph.js.Variable("callback")) # Select all cells. actions = ActionChains(graph.selenium) @@ -1219,26 +1219,26 @@ def test_add_selection_change_handler(graph_cases) -> None: actions.key_up(Keys.CONTROL) actions.perform() - fired_selection_events = graph.selenium.execute_script('return window.__selectionChange__') + fired_selection_events = graph.selenium.execute_script("return window.__selectionChange__") assert fired_selection_events == [ - ['2'], - ['3', '2'], - ['4', '3', '2'], + ["2"], + ["3", "2"], + ["4", "3", "2"], ] - assert graph.eval_js_function('api.getSelectedCells') == ['4', '3', '2'] + assert graph.eval_js_function("api.getSelectedCells") == ["4", "3", "2"] # Programmatically select one cell. - graph.eval_js_function('api.setSelectedCells', ['3']) + graph.eval_js_function("api.setSelectedCells", ["3"]) # Clear selection. - graph.eval_js_function('api.setSelectedCells', []) + graph.eval_js_function("api.setSelectedCells", []) - fired_selection_events = graph.selenium.execute_script('return window.__selectionChange__') + fired_selection_events = graph.selenium.execute_script("return window.__selectionChange__") assert fired_selection_events == [ - ['2'], - ['3', '2'], - ['4', '3', '2'], - ['3'], + ["2"], + ["3", "2"], + ["4", "3", "2"], + ["3"], [], ] @@ -1247,33 +1247,33 @@ def test_set_popup_menu_handler(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('1v') + graph = graph_cases("1v") vertex_id = graph.get_id(graph.get_vertex()) graph.selenium.execute_script( - 'callback = function(cellId, x, y) {' - ' if (!window.__popupMenu__) {' - ' window.__popupMenu__ = [];' - ' }' - ' window.__popupMenu__.push([cellId, x, y]);' - '}' + "callback = function(cellId, x, y) {" + " if (!window.__popupMenu__) {" + " window.__popupMenu__ = [];" + " }" + " window.__popupMenu__.push([cellId, x, y]);" + "}" ) - graph.eval_js_function('api.registerPopupMenuHandler', qmxgraph.js.Variable('callback')) + graph.eval_js_function("api.registerPopupMenuHandler", qmxgraph.js.Variable("callback")) vertex_label_el = graph.get_label_element(graph.get_vertex()) actions = ActionChains(graph.selenium) actions.context_click(vertex_label_el) actions.perform() - x = vertex_label_el.location['x'] + vertex_label_el.size['width'] // 2 - y = vertex_label_el.location['y'] + vertex_label_el.size['height'] // 2 - assert graph.selenium.execute_script('return window.__popupMenu__') == [[vertex_id, x, y]] + x = vertex_label_el.location["x"] + vertex_label_el.size["width"] // 2 + y = vertex_label_el.location["y"] + vertex_label_el.size["height"] // 2 + assert graph.selenium.execute_script("return window.__popupMenu__") == [[vertex_id, x, y]] @pytest.mark.parametrize( - 'action, expected_scale', - [('zoomIn', 1.2), ('zoomOut', 0.83)], + "action, expected_scale", + [("zoomIn", 1.2), ("zoomOut", 0.83)], ) def test_zoom(graph_cases, action, expected_scale) -> None: """ @@ -1281,30 +1281,30 @@ def test_zoom(graph_cases, action, expected_scale) -> None: :type action: str :type expected_scale: float """ - graph = graph_cases('2v_1e') - obtained_scale = graph.eval_js_function('api.getZoomScale') + graph = graph_cases("2v_1e") + obtained_scale = graph.eval_js_function("api.getZoomScale") assert obtained_scale == 1.0 - graph.eval_js_function('api.{}'.format(action)) - obtained_scale = graph.eval_js_function('api.getZoomScale') + graph.eval_js_function("api.{}".format(action)) + obtained_scale = graph.eval_js_function("api.getZoomScale") assert obtained_scale == expected_scale - graph.eval_js_function('api.resetZoom') - obtained_scale = graph.eval_js_function('api.getZoomScale') + graph.eval_js_function("api.resetZoom") + obtained_scale = graph.eval_js_function("api.getZoomScale") assert obtained_scale == 1.0 @pytest.mark.xfail( 'sys.platform != "win32"', - reason='need investigate differences between linux and windows', + reason="need investigate differences between linux and windows", ) def test_set_scale_and_translation(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('1v') + graph = graph_cases("1v") - ini_scale, ini_x, ini_y = graph.eval_js_function('api.getScaleAndTranslation') + ini_scale, ini_x, ini_y = graph.eval_js_function("api.getScaleAndTranslation") assert (ini_scale, ini_x, ini_y) == (1, 0, 0) from selenium.webdriver.common.actions.mouse_button import MouseButton @@ -1323,7 +1323,7 @@ def click_and_hold_right(self, on_element=None): if on_element: self.move_to_element(on_element) self._actions.append( - lambda: self._driver.execute(Command.MOUSE_DOWN, {'button': 2}) + lambda: self._driver.execute(Command.MOUSE_DOWN, {"button": 2}) ) return self @@ -1334,14 +1334,14 @@ def release_right(self, on_element=None): self.w3c_actions.pointer_action.pointer_up(MouseButton.RIGHT) self.w3c_actions.key_action.pause() else: - self._actions.append(lambda: self._driver.execute(Command.MOUSE_UP, {'button': 2})) + self._actions.append(lambda: self._driver.execute(Command.MOUSE_UP, {"button": 2})) return self vertex = graph.get_vertex() w, h = graph.get_vertex_size(vertex) def ScaleAndTranslateGraph(): - graph.eval_js_function('api.zoomIn') + graph.eval_js_function("api.zoomIn") actions = MyActionChains(graph.selenium) actions.move_to_element_with_offset(vertex, w * 2, h * 2) @@ -1350,39 +1350,39 @@ def ScaleAndTranslateGraph(): actions.release_right() # mxgraph does some extra work on release. actions.perform() - graph.eval_js_function('api.zoomIn') + graph.eval_js_function("api.zoomIn") ScaleAndTranslateGraph() - saved_scale, saved_x, saved_y = graph.eval_js_function('api.getScaleAndTranslation') + saved_scale, saved_x, saved_y = graph.eval_js_function("api.getScaleAndTranslation") assert saved_scale == pytest.approx(1.44, abs=2) assert saved_x == pytest.approx(-36.11, abs=2) assert saved_y == pytest.approx(60.42, abs=2) ScaleAndTranslateGraph() - new_scale, new_x, new_y = graph.eval_js_function('api.getScaleAndTranslation') + new_scale, new_x, new_y = graph.eval_js_function("api.getScaleAndTranslation") assert new_scale == pytest.approx(2.08, abs=2) assert new_x == pytest.approx(-61.50, abs=2) assert new_y == pytest.approx(97.28, abs=2) - graph.eval_js_function('api.setScaleAndTranslation', saved_scale, saved_x, saved_y) - scale, x, y = graph.eval_js_function('api.getScaleAndTranslation') + graph.eval_js_function("api.setScaleAndTranslation", saved_scale, saved_x, saved_y) + scale, x, y = graph.eval_js_function("api.getScaleAndTranslation") assert (scale, x, y) == (saved_scale, saved_x, saved_y) -@pytest.mark.parametrize('action', [None, 'zoomIn', 'zoomOut']) +@pytest.mark.parametrize("action", [None, "zoomIn", "zoomOut"]) def test_fit(graph_cases, action) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory :type action: Optional[str] """ - graph = graph_cases('2v_1e') - obtained_scale = graph.eval_js_function('api.getZoomScale') + graph = graph_cases("2v_1e") + obtained_scale = graph.eval_js_function("api.getZoomScale") assert obtained_scale == 1.0 if action is not None: - graph.eval_js_function('api.{}'.format(action)) + graph.eval_js_function("api.{}".format(action)) - graph.eval_js_function('api.fit') - obtained_scale = graph.eval_js_function('api.getZoomScale') + graph.eval_js_function("api.fit") + obtained_scale = graph.eval_js_function("api.getZoomScale") assert obtained_scale == pytest.approx(3.14, abs=2) @@ -1390,29 +1390,29 @@ def test_get_edge_terminals(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('2v_1e') + graph = graph_cases("2v_1e") source, target = graph.get_vertices() edge = graph.get_edge(source, target) - source_id, target_id = graph.eval_js_function('api.getEdgeTerminals', graph.get_id(edge)) + source_id, target_id = graph.eval_js_function("api.getEdgeTerminals", graph.get_id(edge)) assert source_id == graph.get_id(source) assert target_id == graph.get_id(target) -@pytest.mark.parametrize('terminal_type', ['source', 'target']) +@pytest.mark.parametrize("terminal_type", ["source", "target"]) def test_set_edge_terminals(graph_cases, terminal_type) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory :type terminal_type: str """ - graph = graph_cases('3v_1e') - graph.eval_js_function('api.setEdgeTerminal', graph.edge_id, terminal_type, graph.vertex3_id) + graph = graph_cases("3v_1e") + graph.eval_js_function("api.setEdgeTerminal", graph.edge_id, terminal_type, graph.vertex3_id) - source_id, target_id = graph.eval_js_function('api.getEdgeTerminals', graph.edge_id) - if terminal_type == 'source': + source_id, target_id = graph.eval_js_function("api.getEdgeTerminals", graph.edge_id) + if terminal_type == "source": assert source_id == graph.vertex3_id assert target_id == graph.target_id - elif terminal_type == 'target': + elif terminal_type == "target": assert source_id == graph.source_id assert target_id == graph.vertex3_id else: @@ -1423,41 +1423,41 @@ def test_set_get_style(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('1v') + graph = graph_cases("1v") vertices = graph.get_vertices() assert len(vertices) == 1 vertex_id = graph.get_id(vertices[0]) - style = graph.eval_js_function('api.getStyle', vertex_id) + style = graph.eval_js_function("api.getStyle", vertex_id) assert style is None - graph.eval_js_function('api.setStyle', vertex_id, 'foo') - style = graph.eval_js_function('api.getStyle', vertex_id) - assert style == 'foo' + graph.eval_js_function("api.setStyle", vertex_id, "foo") + style = graph.eval_js_function("api.getStyle", vertex_id) + assert style == "foo" with pytest.raises(WebDriverException) as excinfo: - graph.eval_js_function('api.getStyle', 'nonexistent') - assert 'Unable to find cell with id nonexistent' in str(excinfo.value) + graph.eval_js_function("api.getStyle", "nonexistent") + assert "Unable to find cell with id nonexistent" in str(excinfo.value) def test_set_get_connectable(graph_cases) -> None: """ :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory """ - graph = graph_cases('1v') + graph = graph_cases("1v") vertices = graph.get_vertices() assert len(vertices) == 1 vertex_id = graph.get_id(vertices[0]) - connectable = graph.eval_js_function('api.isConnectable', vertex_id) + connectable = graph.eval_js_function("api.isConnectable", vertex_id) assert connectable - graph.eval_js_function('api.setConnectable', vertex_id, False) - connectable = graph.eval_js_function('api.isConnectable', vertex_id) + graph.eval_js_function("api.setConnectable", vertex_id, False) + connectable = graph.eval_js_function("api.isConnectable", vertex_id) assert not connectable - graph.eval_js_function('api.setConnectable', vertex_id, True) - connectable = graph.eval_js_function('api.isConnectable', vertex_id) + graph.eval_js_function("api.setConnectable", vertex_id, True) + connectable = graph.eval_js_function("api.isConnectable", vertex_id) assert connectable @@ -1466,12 +1466,12 @@ def test_get_edge_terminals_error_edge_not_found(graph_cases, selenium_extras) - :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory :type selenium_extras: qmxgraph.tests.conftest.SeleniumExtras """ - graph = graph_cases('empty') + graph = graph_cases("empty") edge_id = "999" with pytest.raises(WebDriverException) as e: - graph.eval_js_function('api.getEdgeTerminals', edge_id) + graph.eval_js_function("api.getEdgeTerminals", edge_id) assert f"Unable to find edge with id {edge_id}" in selenium_extras.get_exception_message(e) @@ -1481,11 +1481,11 @@ def test_get_edge_terminals_error_not_an_edge(graph_cases, selenium_extras) -> N :type graph_cases: qmxgraph.tests.conftest.GraphCaseFactory :type selenium_extras: qmxgraph.tests.conftest.SeleniumExtras """ - graph = graph_cases('1v') + graph = graph_cases("1v") vertex = graph.get_vertices()[0] with pytest.raises(WebDriverException) as e: - graph.eval_js_function('api.getEdgeTerminals', graph.get_id(vertex)) + graph.eval_js_function("api.getEdgeTerminals", graph.get_id(vertex)) assert ( f"Cell with id {graph.get_id(vertex)} is not an edge" @@ -1499,7 +1499,7 @@ def test_custom_font_family(graph_cases_factory, port) -> None: :type port: qmxgraph.tests.conftest.Port """ options = GraphOptions( - font_family=('Helvetica',), + font_family=("Helvetica",), ) with server.host(port=port.get(), options=options) as host: @@ -1513,41 +1513,41 @@ def test_custom_font_family(graph_cases_factory, port) -> None: def test_ports(graph_cases) -> None: - graph = graph_cases('2v') + graph = graph_cases("2v") vertex_a, vertex_b = graph.get_vertices() - port_x_name, port_y_name = 'X', 'Y' + port_x_name, port_y_name = "X", "Y" vertex_a_id = graph.get_id(vertex_a) vertex_b_id = graph.get_id(vertex_b) # Test insert port. - graph.eval_js_function('api.insertPort', vertex_a_id, port_x_name, 0, 0, 9, 9) + graph.eval_js_function("api.insertPort", vertex_a_id, port_x_name, 0, 0, 9, 9) with pytest.raises(WebDriverException) as e: - graph.eval_js_function('api.insertPort', vertex_a_id, port_x_name, 1, 1, 9, 9) - expected = 'The cell {} already have a port named {}'.format(vertex_a_id, port_x_name) + graph.eval_js_function("api.insertPort", vertex_a_id, port_x_name, 1, 1, 9, 9) + expected = "The cell {} already have a port named {}".format(vertex_a_id, port_x_name) assert expected in str(e.value) # Test remove port. - graph.eval_js_function('api.removePort', vertex_a_id, port_x_name) + graph.eval_js_function("api.removePort", vertex_a_id, port_x_name) with pytest.raises(WebDriverException) as e: - graph.eval_js_function('api.removePort', vertex_a_id, port_x_name) - expected = 'The cell {} does not have a port named {}'.format(vertex_a_id, port_x_name) + graph.eval_js_function("api.removePort", vertex_a_id, port_x_name) + expected = "The cell {} does not have a port named {}".format(vertex_a_id, port_x_name) assert expected in str(e.value) # Test insert edge. - graph.eval_js_function('api.insertPort', vertex_a_id, port_x_name, 0, 0, 9, 9) - graph.eval_js_function('api.insertPort', vertex_b_id, port_y_name, 0, 0, 9, 9) + graph.eval_js_function("api.insertPort", vertex_a_id, port_x_name, 0, 0, 9, 9) + graph.eval_js_function("api.insertPort", vertex_b_id, port_y_name, 0, 0, 9, 9) edge_id = graph.eval_js_function( - 'api.insertEdge', vertex_a_id, vertex_b_id, None, None, None, port_x_name, port_y_name + "api.insertEdge", vertex_a_id, vertex_b_id, None, None, None, port_x_name, port_y_name ) # When removing a port remove edges connected through it. - assert graph.eval_js_function('api.hasCell', edge_id) - assert [vertex_a_id, vertex_b_id] == graph.eval_js_function('api.getEdgeTerminals', edge_id) + assert graph.eval_js_function("api.hasCell", edge_id) + assert [vertex_a_id, vertex_b_id] == graph.eval_js_function("api.getEdgeTerminals", edge_id) assert [[vertex_a_id, port_x_name], [vertex_b_id, port_y_name]] == graph.eval_js_function( - 'api.getEdgeTerminalsWithPorts', edge_id + "api.getEdgeTerminalsWithPorts", edge_id ) - graph.eval_js_function('api.removePort', vertex_b_id, port_y_name) - assert not graph.eval_js_function('api.hasCell', edge_id) + graph.eval_js_function("api.removePort", vertex_b_id, port_y_name) + assert not graph.eval_js_function("api.hasCell", edge_id) def insert_by_parametrized_type(graph, cell_type, tags=None): @@ -1568,7 +1568,7 @@ def insert_by_parametrized_type(graph, cell_type, tags=None): @pytest.mark.parametrize( - 'layout_name', + "layout_name", [ QmxGraphApi.LAYOUT_ORGANIC, QmxGraphApi.LAYOUT_COMPACT, @@ -1582,39 +1582,40 @@ def insert_by_parametrized_type(graph, cell_type, tags=None): ], ) def test_run_all_layouts(layout_name, graph_cases) -> None: - graph = graph_cases('3v_1e') - graph.eval_js_function('api.runLayout', layout_name) + graph = graph_cases("3v_1e") + graph.eval_js_function("api.runLayout", layout_name) def test_run_organic_layout(graph_cases) -> None: - graph = graph_cases('3v_3e') + graph = graph_cases("3v_3e") label = lambda cell: graph.get_label(cell) nodes_positions = { label(v): { - 'before': None, - 'after': None, + "before": None, + "after": None, } for v in graph.get_vertices() } for v in graph.get_vertices(): - nodes_positions[label(v)]['before'] = graph.get_vertex_position(v) - graph.eval_js_function('api.runLayout', QmxGraphApi.LAYOUT_ORGANIC) + nodes_positions[label(v)]["before"] = graph.get_vertex_position(v) + graph.eval_js_function("api.runLayout", QmxGraphApi.LAYOUT_ORGANIC) for v in graph.get_vertices(): - nodes_positions[label(v)]['after'] = graph.get_vertex_position(v) + nodes_positions[label(v)]["after"] = graph.get_vertex_position(v) for position_data in nodes_positions.values(): # We do not have the exact expected position to check - But we do know that the positions # should at least change. - assert ( - not pytest.approx(position_data['before']) == position_data['after'] - ), "Expected position different from %s, but got %s" % ( - {position_data['before']}, - {position_data['after']}, + assert not pytest.approx(position_data["before"]) == position_data["after"], ( + "Expected position different from %s, but got %s" + % ( + {position_data["before"]}, + {position_data["after"]}, + ) ) def test_run_invalid_layout(graph_cases) -> None: - graph = graph_cases('3v_1e') + graph = graph_cases("3v_1e") with pytest.raises(WebDriverException): - graph.eval_js_function('api.runLayout', 'invalid_layout_name') + graph.eval_js_function("api.runLayout", "invalid_layout_name") diff --git a/tests/test_js_utils.py b/tests/test_js_utils.py index 8fb43960..2d498825 100644 --- a/tests/test_js_utils.py +++ b/tests/test_js_utils.py @@ -5,41 +5,41 @@ @pytest.fixture def graph(graph_cases: GraphCaseFactory) -> BaseGraphCase: - return graph_cases('empty') + return graph_cases("empty") def test_set_style_key(graph: BaseGraphCase) -> None: - assert graph.eval_js_function('graphs.utils.setStyleKey', '', 'bar', 5) == 'bar=5' - assert graph.eval_js_function('graphs.utils.setStyleKey', 'style', 'bar', 5) == 'style;bar=5' + assert graph.eval_js_function("graphs.utils.setStyleKey", "", "bar", 5) == "bar=5" + assert graph.eval_js_function("graphs.utils.setStyleKey", "style", "bar", 5) == "style;bar=5" assert ( - graph.eval_js_function('graphs.utils.setStyleKey', 'style;bar=7', 'bar', 5) == 'style;bar=5' + graph.eval_js_function("graphs.utils.setStyleKey", "style;bar=7", "bar", 5) == "style;bar=5" ) assert ( - graph.eval_js_function('graphs.utils.setStyleKey', 'foobar=3;bar=7', 'bar', 5) - == 'foobar=3;bar=5' + graph.eval_js_function("graphs.utils.setStyleKey", "foobar=3;bar=7", "bar", 5) + == "foobar=3;bar=5" ) assert ( - graph.eval_js_function('graphs.utils.setStyleKey', 'foobar=3', 'bar', 5) == 'foobar=3;bar=5' + graph.eval_js_function("graphs.utils.setStyleKey", "foobar=3", "bar", 5) == "foobar=3;bar=5" ) assert ( - graph.eval_js_function('graphs.utils.setStyleKey', 'foobar=3;fizzbar=7', 'bar', 5) - == 'foobar=3;fizzbar=7;bar=5' + graph.eval_js_function("graphs.utils.setStyleKey", "foobar=3;fizzbar=7", "bar", 5) + == "foobar=3;fizzbar=7;bar=5" ) def test_remove_style_key(graph: BaseGraphCase) -> None: - assert graph.eval_js_function('graphs.utils.removeStyleKey', '', 'bar') == '' - assert graph.eval_js_function('graphs.utils.removeStyleKey', 'style;bar=7', 'bar') == 'style' + assert graph.eval_js_function("graphs.utils.removeStyleKey", "", "bar") == "" + assert graph.eval_js_function("graphs.utils.removeStyleKey", "style;bar=7", "bar") == "style" assert ( - graph.eval_js_function('graphs.utils.removeStyleKey', 'style;bar=7', 'foo') == 'style;bar=7' + graph.eval_js_function("graphs.utils.removeStyleKey", "style;bar=7", "foo") == "style;bar=7" ) assert ( - graph.eval_js_function('graphs.utils.removeStyleKey', 'style;bar=7', 'style') - == 'style;bar=7' + graph.eval_js_function("graphs.utils.removeStyleKey", "style;bar=7", "style") + == "style;bar=7" ) assert ( - graph.eval_js_function('graphs.utils.removeStyleKey', 'foo=3;bar=7', 'style') - == 'foo=3;bar=7' + graph.eval_js_function("graphs.utils.removeStyleKey", "foo=3;bar=7", "style") + == "foo=3;bar=7" ) - assert graph.eval_js_function('graphs.utils.removeStyleKey', 'foo=3;bar=7', 'foo') == 'bar=7' - assert graph.eval_js_function('graphs.utils.removeStyleKey', 'foo=3;bar=7', 'bar') == 'foo=3' + assert graph.eval_js_function("graphs.utils.removeStyleKey", "foo=3;bar=7", "foo") == "bar=7" + assert graph.eval_js_function("graphs.utils.removeStyleKey", "foo=3;bar=7", "bar") == "foo=3" diff --git a/tests/test_qt_js_integration.py b/tests/test_qt_js_integration.py index b0e047a3..64ffb4c3 100644 --- a/tests/test_qt_js_integration.py +++ b/tests/test_qt_js_integration.py @@ -43,13 +43,13 @@ def test_error_redirection(loaded_graph) -> None: assert cb.args is not None msg, url, line, column = cb.args expected = textwrap.dedent( - '''\ + """\ Uncaught Error: test stack: Error: test - at :1:7''' + at :1:7""" ) - assert (url, line, column) == ('qrc:/', 1, 1) + assert (url, line, column) == ("qrc:/", 1, 1) def test_events_bridge_delayed_signals(graph, qtbot, mocker) -> None: @@ -108,7 +108,7 @@ def test_events_bridge_plain(graph, mocker) -> None: graph.load_and_wait() # on_cells_added with wait_signals_called(events.on_cells_added): - vertex_id = graph.api.insert_vertex(40, 40, 20, 20, 'test') + vertex_id = graph.api.insert_vertex(40, 40, 20, 20, "test") assert added_handler.call_args_list == [mocker.call([vertex_id])] # on_selection_changed assert selections_handler.call_args_list == [] @@ -117,13 +117,13 @@ def test_events_bridge_plain(graph, mocker) -> None: assert selections_handler.call_args_list == [mocker.call([vertex_id])] # on_label_changed with wait_signals_called(events.on_label_changed): - graph.api.set_label(vertex_id, 'TOTALLY NEW LABEL') - assert labels_handler.call_args_list == [mocker.call(vertex_id, 'TOTALLY NEW LABEL', 'test')] + graph.api.set_label(vertex_id, "TOTALLY NEW LABEL") + assert labels_handler.call_args_list == [mocker.call(vertex_id, "TOTALLY NEW LABEL", "test")] # on_terminal_changed, on_terminal_with_port_changed - foo_id = graph.api.insert_vertex(440, 40, 20, 20, 'foo') - bar_id = graph.api.insert_vertex(40, 140, 20, 20, 'bar') - edge_id = graph.api.insert_edge(vertex_id, foo_id, 'edge') - bar_port_name = 'a-port' + foo_id = graph.api.insert_vertex(440, 40, 20, 20, "foo") + bar_id = graph.api.insert_vertex(40, 140, 20, 20, "bar") + edge_id = graph.api.insert_edge(vertex_id, foo_id, "edge") + bar_port_name = "a-port" assert not graph.api.has_port(bar_id, bar_port_name) graph.api.insert_port(bar_id, bar_port_name, 0, 0, 5, 5) assert graph.api.has_port(bar_id, bar_port_name) @@ -152,15 +152,15 @@ def test_events_bridge_plain(graph, mocker) -> None: bar_id, bar_port_name, foo_id, - '', + "", ), mocker.call( edge_id, QmxGraphApi.SOURCE_TERMINAL_CELL, foo_id, - '', + "", vertex_id, - '', + "", ), ] # on_cells_removed @@ -187,7 +187,7 @@ def handler_that_call_api(*args): events = loaded_graph.events_bridge events.on_cells_added.connect(handler_that_call_api) with wait_signals_called(events.on_cells_added): - loaded_graph.api.insert_vertex(40, 40, 20, 20, 'test') + loaded_graph.api.insert_vertex(40, 40, 20, 20, "test") assert zoom_scale_obtained == [1] @@ -269,7 +269,7 @@ def test_container_resize(loaded_graph) -> None: def get_container_dimensions(): width = eval_js(loaded_graph, """document.getElementById('graphContainer').style.width""") height = eval_js(loaded_graph, """document.getElementById('graphContainer').style.height""") - return int(width.replace('px', '')), int(height.replace('px', '')) + return int(width.replace("px", "")), int(height.replace("px", "")) width, height = get_container_dimensions() assert width == expected_width @@ -289,8 +289,8 @@ def test_web_inspector(loaded_graph, mocker) -> None: """ from PyQt5.QtWidgets import QDialog - mocker.patch.object(QDialog, 'show') - mocker.patch.object(QDialog, 'hide') + mocker.patch.object(QDialog, "show") + mocker.patch.object(QDialog, "hide") loaded_graph.show_inspector() QDialog.show.assert_called_once_with() @@ -382,21 +382,21 @@ def test_drag_drop(loaded_graph, drag_drop_events) -> None: """ mime_data = qmxgraph.mime.create_qt_mime_data( { - 'vertices': [ + "vertices": [ { - 'dx': 0, - 'dy': 0, - 'width': 64, - 'height': 64, - 'label': 'test 1', + "dx": 0, + "dy": 0, + "width": 64, + "height": 64, + "label": "test 1", }, { - 'dx': 50, - 'dy': 50, - 'width': 32, - 'height': 32, - 'label': 'test 2', - 'tags': {'foo': '1', 'bar': 'a'}, + "dx": 50, + "dy": 50, + "width": 32, + "height": 32, + "label": "test 2", + "tags": {"foo": "1", "bar": "a"}, }, ], } @@ -417,14 +417,14 @@ def test_drag_drop(loaded_graph, drag_drop_events) -> None: cell_id = loaded_graph.api.get_cell_id_at(100, 100) assert loaded_graph.api.get_cell_type(cell_id) == qmxgraph.constants.CELL_TYPE_VERTEX assert loaded_graph.api.get_geometry(cell_id) == [100.0 - 64 / 2, 100.0 - 64 / 2, 64.0, 64.0] - assert loaded_graph.api.get_label(cell_id) == 'test 1' + assert loaded_graph.api.get_label(cell_id) == "test 1" cell_id = loaded_graph.api.get_cell_id_at(150, 150) assert loaded_graph.api.get_cell_type(cell_id) == qmxgraph.constants.CELL_TYPE_VERTEX assert loaded_graph.api.get_geometry(cell_id) == [150.0 - 32 / 2, 150.0 - 32 / 2, 32.0, 32.0] - assert loaded_graph.api.get_label(cell_id) == 'test 2' - assert loaded_graph.api.get_tag(cell_id, 'foo') == '1' - assert loaded_graph.api.get_tag(cell_id, 'bar') == 'a' + assert loaded_graph.api.get_label(cell_id) == "test 2" + assert loaded_graph.api.get_tag(cell_id, "foo") == "1" + assert loaded_graph.api.get_tag(cell_id, "bar") == "a" def test_drag_drop_invalid_mime_type(loaded_graph, drag_drop_events) -> None: @@ -438,11 +438,11 @@ def test_drag_drop_invalid_mime_type(loaded_graph, drag_drop_events) -> None: item_data = QByteArray() data_stream = QDataStream(item_data, QIODevice.WriteOnly) data_stream.writeString( - json.dumps('Hello World!').encode('utf8') + json.dumps('Hello World!').encode("utf8") ) mime_data = QMimeData() - mime_data.setData('application/xml', item_data) + mime_data.setData("application/xml", item_data) drag_enter_event = drag_drop_events.drag_enter(mime_data) loaded_graph.inner_web_view().dragEnterEvent(drag_enter_event) @@ -465,7 +465,7 @@ def test_drag_drop_invalid_version(loaded_graph, drag_drop_events) -> None: """ mime_data = qmxgraph.mime.create_qt_mime_data( { - 'version': -1, + "version": -1, } ) @@ -483,8 +483,8 @@ def test_drag_drop_invalid_version(loaded_graph, drag_drop_events) -> None: print( ( - 'This test will cause an exception in a Qt event loop:\n' - ' ValueError: Unsupported version of QmxGraph MIME data: -1' + "This test will cause an exception in a Qt event loop:\n" + " ValueError: Unsupported version of QmxGraph MIME data: -1" ), file=sys.stderr, ) @@ -498,7 +498,7 @@ def test_drag_drop_invalid_version(loaded_graph, drag_drop_events) -> None: assert str(exceptions[0][1]) == "Unsupported version of QmxGraph MIME data: -1" -@pytest.mark.parametrize('debug', (True, False)) +@pytest.mark.parametrize("debug", (True, False)) def test_invalid_api_call(loaded_graph, debug) -> None: """ :type loaded_graph: qmxgraph.widget.qmxgraph @@ -515,33 +515,33 @@ def test_invalid_api_call(loaded_graph, debug) -> None: 'Uncaught Error: [QmxGraph] unable to find function "BOOM" in javascript api' ) with pytest.raises(InvalidJavaScriptError, match=expected_message): - loaded_graph.api.call_api('BOOM') + loaded_graph.api.call_api("BOOM") else: # When debug feature is disabled, code will raise on JavaScript # side, but unless an error bridge is configured that could go # unnoticed, as call would return None and could easily be # mistaken by an OK call. - assert loaded_graph.api.call_api('BOOM') is None + assert loaded_graph.api.call_api("BOOM") is None finally: qmxgraph.debug.set_qmxgraph_debug(old_debug) -@pytest.mark.parametrize('enabled', (True, False)) +@pytest.mark.parametrize("enabled", (True, False)) def test_graph_api_calls(loaded_graph, enabled) -> None: """ Tests the available calls to the graph api. """ graph_api_functions = [ - ('is_cells_deletable', 'set_cells_deletable'), - ('is_cells_disconnectable', 'set_cells_disconnectable'), - ('is_cells_editable', 'set_cells_editable'), + ("is_cells_deletable", "set_cells_deletable"), + ("is_cells_disconnectable", "set_cells_disconnectable"), + ("is_cells_editable", "set_cells_editable"), ( - 'is_cells_movable', - 'set_cells_movable', + "is_cells_movable", + "set_cells_movable", ), ( - 'is_cells_connectable', - 'set_cells_connectable', + "is_cells_connectable", + "set_cells_connectable", ), ] @@ -559,16 +559,16 @@ def test_tags(loaded_graph) -> None: """ :type loaded_graph: qmxgraph.widget.qmxgraph """ - no_tags_id = loaded_graph.api.insert_vertex(10, 10, 20, 20, 'test') + no_tags_id = loaded_graph.api.insert_vertex(10, 10, 20, 20, "test") - assert not loaded_graph.api.has_tag(no_tags_id, 'foo') - loaded_graph.api.set_tag(no_tags_id, 'foo', '1') - assert loaded_graph.api.has_tag(no_tags_id, 'foo') - assert loaded_graph.api.get_tag(no_tags_id, 'foo') == '1' + assert not loaded_graph.api.has_tag(no_tags_id, "foo") + loaded_graph.api.set_tag(no_tags_id, "foo", "1") + assert loaded_graph.api.has_tag(no_tags_id, "foo") + assert loaded_graph.api.get_tag(no_tags_id, "foo") == "1" - with_tags_id = loaded_graph.api.insert_vertex(50, 50, 20, 20, 'test', tags={'bar': '2'}) - assert loaded_graph.api.has_tag(with_tags_id, 'bar') - assert loaded_graph.api.get_tag(with_tags_id, 'bar') == '2' + with_tags_id = loaded_graph.api.insert_vertex(50, 50, 20, 20, "test", tags={"bar": "2"}) + assert loaded_graph.api.has_tag(with_tags_id, "bar") + assert loaded_graph.api.get_tag(with_tags_id, "bar") == "2" def test_get_cell_count(loaded_graph) -> None: @@ -577,13 +577,13 @@ def test_get_cell_count(loaded_graph) -> None: """ from qmxgraph.common_testing import get_cell_count - node_a = loaded_graph.api.insert_vertex(10, 10, 50, 50, 'A') - node_b = loaded_graph.api.insert_vertex(400, 300, 50, 50, 'B') - loaded_graph.api.insert_edge(node_a, node_b, 'AB') + node_a = loaded_graph.api.insert_vertex(10, 10, 50, 50, "A") + node_b = loaded_graph.api.insert_vertex(400, 300, 50, 50, "B") + loaded_graph.api.insert_edge(node_a, node_b, "AB") - assert get_cell_count(loaded_graph, 'function(cell){ return false }') == 0 - assert get_cell_count(loaded_graph, 'function(cell){ return cell.isEdge() }') == 1 - assert get_cell_count(loaded_graph, 'function(cell){ return cell.isVertex() }') == 2 + assert get_cell_count(loaded_graph, "function(cell){ return false }") == 0 + assert get_cell_count(loaded_graph, "function(cell){ return cell.isEdge() }") == 1 + assert get_cell_count(loaded_graph, "function(cell){ return cell.isVertex() }") == 2 def test_get_cell_ids(loaded_graph) -> None: @@ -592,17 +592,17 @@ def test_get_cell_ids(loaded_graph) -> None: """ from qmxgraph.common_testing import get_cell_ids - node_a = loaded_graph.api.insert_vertex(10, 10, 50, 50, 'A') - node_b = loaded_graph.api.insert_vertex(400, 300, 50, 50, 'B') - loaded_graph.api.insert_edge(node_a, node_b, 'AB') + node_a = loaded_graph.api.insert_vertex(10, 10, 50, 50, "A") + node_b = loaded_graph.api.insert_vertex(400, 300, 50, 50, "B") + loaded_graph.api.insert_edge(node_a, node_b, "AB") - assert get_cell_ids(loaded_graph, 'function(cell){ return false }') == [] - assert get_cell_ids(loaded_graph, 'function(cell){ return cell.isEdge() }') == ['4'] - assert get_cell_ids(loaded_graph, 'function(cell){ return cell.isVertex() }') == ['2', '3'] + assert get_cell_ids(loaded_graph, "function(cell){ return false }") == [] + assert get_cell_ids(loaded_graph, "function(cell){ return cell.isEdge() }") == ["4"] + assert get_cell_ids(loaded_graph, "function(cell){ return cell.isVertex() }") == ["2", "3"] def test_cell_bounds(loaded_graph) -> None: - node_a = loaded_graph.api.insert_vertex(10, 10, 50, 50, 'A') + node_a = loaded_graph.api.insert_vertex(10, 10, 50, 50, "A") bounds = loaded_graph.api.get_cell_bounds(node_a) assert bounds == CellBounds(x=10, y=10, width=50, height=50) @@ -627,7 +627,7 @@ def test_last_index_of(loaded_graph) -> None: @pytest.mark.parametrize( - ('zoom_in', 'zoom_to_cursor'), + ("zoom_in", "zoom_to_cursor"), [ (False, False), (False, True), @@ -640,16 +640,16 @@ def test_zoom(loaded_graph: QmxGraph, zoom_in: bool, zoom_to_cursor: bool) -> No y = 234 width = height = 50 initial_geometry = (x, y, width, height) - node = loaded_graph.api.insert_vertex(*initial_geometry, 'Node') + node = loaded_graph.api.insert_vertex(*initial_geometry, "Node") assert tuple(loaded_graph.api.get_geometry(node)) == initial_geometry node_center_x = x + width / 2 node_center_y = y + height / 2 - zoom_event = {'clientX': node_center_x, 'clientY': node_center_y} + zoom_event = {"clientX": node_center_x, "clientY": node_center_y} args = ", ".join(json.dumps(arg) for arg in [zoom_event, zoom_in, zoom_to_cursor]) - eval_js(loaded_graph, f'graphs.handleMouseWheelEvent(graphEditor.graph, {args})') + eval_js(loaded_graph, f"graphs.handleMouseWheelEvent(graphEditor.graph, {args})") new_x, new_y, new_width, new_height = loaded_graph.api.get_geometry(node) assert (new_width > width) == (new_height > height) == zoom_in @@ -664,7 +664,7 @@ def eval_js(graph_widget, statement): return graph_widget.inner_web_view().eval_js(statement) -@pytest.fixture(name='graph') +@pytest.fixture(name="graph") def graph_(qtbot) -> QmxGraph: """ :type qtbot: pytestqt.plugin.QtBot @@ -677,7 +677,7 @@ def graph_(qtbot) -> QmxGraph: return graph_ -@pytest.fixture(name='loaded_graph') +@pytest.fixture(name="loaded_graph") def loaded_graph_(graph): """ :type graph: qmxgraph.widget.qmxgraph @@ -687,7 +687,7 @@ def loaded_graph_(graph): return graph -@pytest.fixture(name='drag_drop_events') +@pytest.fixture(name="drag_drop_events") def drag_drop_events_(mocker): """ :type mocker: pyest_mock.MockFixture @@ -719,8 +719,8 @@ def _create_dd_event(self, event_type, position, mime_data): position = position or (100, 100) dd_args = QPoint(*position), Qt.MoveAction, mime_data, Qt.LeftButton, Qt.NoModifier dd_event = event_type(*dd_args) - self.mocker.spy(dd_event, 'acceptProposedAction') - self.mocker.spy(dd_event, 'ignore') + self.mocker.spy(dd_event, "acceptProposedAction") + self.mocker.spy(dd_event, "ignore") return dd_event @@ -758,7 +758,7 @@ def assert_handled(self, *, js_script, called, expected_calls=()): 10, 20, 20, - 'handler fixture test', + "handler fixture test", ) js_script = js_script.format( vertex=f"graphEditor.graph.model.getCell({vertex_id})", @@ -778,7 +778,7 @@ def assert_handled(self, *, js_script, called, expected_calls=()): self.calls.clear() -@pytest.fixture(name='handler') +@pytest.fixture(name="handler") def handler_(graph, qtbot): """ :type graph: qmxgraph.widget.qmxgraph