def test_access_top_level(self):

        # Figure is special, we define top-level objects that always exist.

        self.assertEqual(go.Figure().data, ())
        self.assertEqual(go.Figure().layout.to_plotly_json(), {})
        self.assertEqual(go.Figure().frames, ())
    def test_nested_frames(self):
        with self.assertRaisesRegexp(ValueError, "frames"):
            go.Figure({"frames": [{"frames": []}]})

        figure = go.Figure()
        figure.frames = [{}]

        with self.assertRaisesRegexp(ValueError, "frames"):
            figure.to_plotly_json()["frames"][0]["frames"] = []
            figure.frames[0].frames = []
    def test_update_layout_dict(self):
        # Create initial figure
        fig = go.Figure()
        fig.layout.title.font.size = 10

        # Grab copy of original figure
        orig_fig = go.Figure(fig)

        fig.update_layout(dict(title=dict(font=dict(family="Courier New"))))
        orig_fig.layout.update(title_font_family="Courier New")
        self.assertEqual(fig, orig_fig)
    def test_toplevel_underscore_kwarg(self):
        fig = go.Figure(data=[{
            "type": "bar"
        }],
                        layout_title_text="Hello, Figure title!")

        self.assertEqual(fig.layout.title.text, "Hello, Figure title!")
    def test_skip_invalid_property_name(self):
        fig = go.Figure(
            data=[{
                "type": "bar",
                "bogus": 123
            }],
            layout={
                "bogus": 23,
                "title": {
                    "text": "Figure title"
                }
            },
            frames=[{
                "data": [{
                    "type": "bar",
                    "bogus": 123
                }],
                "layout": {
                    "bogus": 23,
                    "title": "Figure title"
                },
            }],
            bogus=123,
            skip_invalid=True,
        )

        fig_dict = fig.to_dict()

        # Remove trace uid property
        for trace in fig_dict["data"]:
            trace.pop("uid", None)

        self.assertEqual(fig_dict["data"], [{"type": "bar"}])
        self.assertEqual(fig_dict["layout"],
                         {"title": {
                             "text": "Figure title"
                         }})
        self.assertEqual(
            fig_dict["frames"],
            [{
                "data": [{
                    "type": "bar"
                }],
                "layout": {
                    "title": {
                        "text": "Figure title"
                    }
                },
            }],
        )
    def test_skip_invalid_property_value(self):
        fig = go.Figure(
            data=[{
                "type": "bar",
                "showlegend": "bad_value"
            }],
            layout={
                "paper_bgcolor": "bogus_color",
                "title": "Figure title"
            },
            frames=[{
                "data": [{
                    "type": "bar",
                    "showlegend": "bad_value"
                }],
                "layout": {
                    "bgcolor": "bad_color",
                    "title": "Figure title"
                },
            }],
            skip_invalid=True,
        )

        fig_dict = fig.to_dict()

        # Remove trace uid property
        for trace in fig_dict["data"]:
            trace.pop("uid", None)

        self.assertEqual(fig_dict["data"], [{"type": "bar"}])
        self.assertEqual(fig_dict["layout"],
                         {"title": {
                             "text": "Figure title"
                         }})
        self.assertEqual(
            fig_dict["frames"],
            [{
                "data": [{
                    "type": "bar"
                }],
                "layout": {
                    "title": {
                        "text": "Figure title"
                    }
                },
            }],
        )
 def test_raises_invalid_toplevel_kwarg(self):
     with self.assertRaises(TypeError):
         go.Figure(
             data=[{
                 "type": "bar"
             }],
             layout={"title": "Figure title"},
             frames=[{
                 "data": [{
                     "type": "bar"
                 }],
                 "layout": {
                     "title": "Figure title"
                 }
             }],
             bogus=123,
         )
    def test_update_overwrite_data(self):
        fig = go.Figure(
            data=[go.Bar(marker_color="blue"),
                  go.Bar(marker_color="yellow")])

        fig.update(overwrite=True,
                   data=[go.Marker(y=[1, 3, 2], line_color="yellow")])

        self.assertEqual(
            fig.to_plotly_json()["data"],
            [{
                "type": "scatter",
                "y": [1, 3, 2],
                "line": {
                    "color": "yellow"
                }
            }],
        )
    def test_update_layout_overwrite(self):
        fig = go.Figure(layout=go.Layout(annotations=[
            go.layout.Annotation(text="one"),
            go.layout.Annotation(text="two"),
        ]))

        fig.update_layout(
            overwrite=True,
            annotations=[
                go.layout.Annotation(width=10),
                go.layout.Annotation(width=20),
                go.layout.Annotation(width=30),
                go.layout.Annotation(width=40),
                go.layout.Annotation(width=50),
            ],
        )

        expected = {
            "annotations": [
                {
                    "width": 10
                },
                {
                    "width": 20
                },
                {
                    "width": 30
                },
                {
                    "width": 40
                },
                {
                    "width": 50
                },
            ]
        }

        fig.layout.pop("template")
        self.assertEqual(fig.layout.to_plotly_json(), expected)

        # Remove all annotations
        fig.update_layout(overwrite=True, annotations=None)
        self.assertEqual(fig.layout.annotations, ())
def test_external_server_url():
    # Build server url
    port = find_open_port()
    server_url = "http://{hostname}:{port}".format(hostname="localhost",
                                                   port=port)

    # Build external orca command
    orca_path = which("orca")
    cmd_list = [orca_path] + [
        "serve",
        "-p",
        str(port),
        "--plotly",
        pio.orca.config.plotlyjs,
        "--graph-only",
    ]

    # Run orca as subprocess to simulate external orca server
    DEVNULL = open(os.devnull, "wb")
    with orca_env():
        proc = subprocess.Popen(cmd_list, stdout=DEVNULL)

    # Start plotly managed orca server so we can ensure it gets shut down properly
    pio.orca.config.port = port
    pio.orca.ensure_server()
    assert pio.orca.status.state == "running"

    # Configure orca to use external server
    pio.orca.config.server_url = server_url

    # Make sure that the locally managed orca server has been shutdown and the local
    # config options have been cleared
    assert pio.orca.status.state == "unvalidated"
    assert pio.orca.config.port is None

    fig = go.Figure()
    img_bytes = pio.to_image(fig, format="svg")
    assert img_bytes.startswith(b"<svg class")

    # Kill server orca process
    proc.terminate()
    def test_update_overwrite_layout(self):
        fig = go.Figure(layout=go.Layout(width=1000))

        # By default, update works recursively so layout.width should remain
        fig.update(layout={"title": {"text": "Fig Title"}})
        fig.layout.pop("template")
        self.assertEqual(fig.layout.to_plotly_json(), {
            "title": {
                "text": "Fig Title"
            },
            "width": 1000
        })

        # With overwrite=True, all existing layout properties should be
        # removed
        fig.update(overwrite=True, layout={"title": {"text": "Fig2 Title"}})
        fig.layout.pop("template")
        self.assertEqual(fig.layout.to_plotly_json(),
                         {"title": {
                             "text": "Fig2 Title"
                         }})
 def test_raises_invalid_property_value(self):
     with self.assertRaises(ValueError):
         go.Figure(
             data=[{
                 "type": "bar",
                 "showlegend": "bad_value"
             }],
             layout={
                 "paper_bgcolor": "bogus_color",
                 "title": "Figure title"
             },
             frames=[{
                 "data": [{
                     "type": "bar",
                     "showlegend": "bad_value"
                 }],
                 "layout": {
                     "bgcolor": "bad_color",
                     "title": "Figure title"
                 },
             }],
         )
 def test_raises_invalid_property_name(self):
     with self.assertRaises(ValueError):
         go.Figure(
             data=[{
                 "type": "bar",
                 "bogus": 123
             }],
             layout={
                 "bogus": 23,
                 "title": "Figure title"
             },
             frames=[{
                 "data": [{
                     "type": "bar",
                     "bogus": 123
                 }],
                 "layout": {
                     "bogus": 23,
                     "title": "Figure title"
                 },
             }],
         )
 def test_pop_layout(self):
     fig = go.Figure(layout=go.Layout(width=1000))
     self.assertEqual(fig.pop("layout"), go.Layout(width=1000))
     self.assertEqual(fig.layout, go.Layout())
    def test_add_trace_underscore_kwarg(self):
        fig = go.Figure()

        fig.add_scatter(y=[2, 1, 3], marker_line_color="green")

        self.assertEqual(fig.data[0].marker.line.color, "green")
    def test_scalar_trace_as_data(self):
        fig = go.Figure(data=go.Waterfall(y=[2, 1, 3]))
        self.assertEqual(fig.data, (go.Waterfall(y=[2, 1, 3]), ))

        fig = go.Figure(data=dict(type="waterfall", y=[2, 1, 3]))
        self.assertEqual(fig.data, (go.Waterfall(y=[2, 1, 3]), ))
 def test_pop_data(self):
     fig = go.Figure(data=go.Waterfall(y=[2, 1, 3]))
     self.assertEqual(fig.pop("data"), (go.Waterfall(y=[2, 1, 3]), ))
     self.assertEqual(fig.data, ())
Exemple #18
0
def gantt_colorscale(
    chart,
    colors,
    title,
    index_col,
    show_colorbar,
    bar_width,
    showgrid_x,
    showgrid_y,
    height,
    width,
    tasks=None,
    task_names=None,
    data=None,
    group_tasks=False,
    show_hover_fill=True,
):
    """
    Refer to FigureFactory.create_gantt() for docstring
    """
    if tasks is None:
        tasks = []
    if task_names is None:
        task_names = []
    if data is None:
        data = []
    showlegend = False

    for index in range(len(chart)):
        task = dict(
            x0=chart[index]["Start"],
            x1=chart[index]["Finish"],
            name=chart[index]["Task"],
        )
        if "Description" in chart[index]:
            task["description"] = chart[index]["Description"]
        tasks.append(task)

    # create a scatter trace for every task group
    scatter_data_dict = dict()
    # create scatter traces for the start- and endpoints
    marker_data_dict = dict()

    if show_hover_fill:
        hoverinfo = "name"
    else:
        hoverinfo = "skip"

    scatter_data_template = {
        "x": [],
        "y": [],
        "mode": "none",
        "fill": "toself",
        "showlegend": False,
        "hoverinfo": hoverinfo,
        "legendgroup": "",
    }

    marker_data_template = {
        "x": [],
        "y": [],
        "mode": "markers",
        "text": [],
        "marker": dict(color="", size=1, opacity=0),
        "name": "",
        "showlegend": False,
        "legendgroup": "",
    }

    index_vals = []
    for row in range(len(tasks)):
        if chart[row][index_col] not in index_vals:
            index_vals.append(chart[row][index_col])

    index_vals.sort()

    # compute the color for task based on indexing column
    if isinstance(chart[0][index_col], Number):
        # check that colors has at least 2 colors
        if len(colors) < 2:
            raise exceptions.PlotlyError(
                "You must use at least 2 colors in 'colors' if you "
                "are using a colorscale. However only the first two "
                "colors given will be used for the lower and upper "
                "bounds on the colormap.")

        # create the list of task names
        for index in range(len(tasks)):
            tn = tasks[index]["name"]
            # Is added to task_names if group_tasks is set to False,
            # or if the option is used (True) it only adds them if the
            # name is not already in the list
            if not group_tasks or tn not in task_names:
                task_names.append(tn)
        # Guarantees that for grouped tasks the tasks that are inserted
        # first are shown at the top
        if group_tasks:
            task_names.reverse()

        for index in range(len(tasks)):
            tn = tasks[index]["name"]
            del tasks[index]["name"]

            # If group_tasks is True, all tasks with the same name belong
            # to the same row.
            groupID = index
            if group_tasks:
                groupID = task_names.index(tn)
            tasks[index]["y0"] = groupID - bar_width
            tasks[index]["y1"] = groupID + bar_width

            # unlabel color
            colors = clrs.color_parser(colors, clrs.unlabel_rgb)
            lowcolor = colors[0]
            highcolor = colors[1]

            intermed = (chart[index][index_col]) / 100.0
            intermed_color = clrs.find_intermediate_color(
                lowcolor, highcolor, intermed)
            intermed_color = clrs.color_parser(intermed_color, clrs.label_rgb)
            tasks[index]["fillcolor"] = intermed_color
            color_id = tasks[index]["fillcolor"]

            if color_id not in scatter_data_dict:
                scatter_data_dict[color_id] = copy.deepcopy(
                    scatter_data_template)

            scatter_data_dict[color_id]["fillcolor"] = color_id
            scatter_data_dict[color_id]["name"] = str(chart[index][index_col])
            scatter_data_dict[color_id]["legendgroup"] = color_id

            # relabel colors with 'rgb'
            colors = clrs.color_parser(colors, clrs.label_rgb)

            # if there are already values append the gap
            if len(scatter_data_dict[color_id]["x"]) > 0:
                # a gap on the scatterplot separates the rectangles from each other
                scatter_data_dict[color_id]["x"].append(
                    scatter_data_dict[color_id]["x"][-1])
                scatter_data_dict[color_id]["y"].append(None)

            xs, ys = _get_corner_points(
                tasks[index]["x0"],
                tasks[index]["y0"],
                tasks[index]["x1"],
                tasks[index]["y1"],
            )

            scatter_data_dict[color_id]["x"] += xs
            scatter_data_dict[color_id]["y"] += ys

            # append dummy markers for showing start and end of interval
            if color_id not in marker_data_dict:
                marker_data_dict[color_id] = copy.deepcopy(
                    marker_data_template)
                marker_data_dict[color_id]["marker"]["color"] = color_id
                marker_data_dict[color_id]["legendgroup"] = color_id

            marker_data_dict[color_id]["x"].append(tasks[index]["x0"])
            marker_data_dict[color_id]["x"].append(tasks[index]["x1"])
            marker_data_dict[color_id]["y"].append(groupID)
            marker_data_dict[color_id]["y"].append(groupID)

            if "description" in tasks[index]:
                marker_data_dict[color_id]["text"].append(
                    tasks[index]["description"])
                marker_data_dict[color_id]["text"].append(
                    tasks[index]["description"])
                del tasks[index]["description"]
            else:
                marker_data_dict[color_id]["text"].append(None)
                marker_data_dict[color_id]["text"].append(None)

        # add colorbar to one of the traces randomly just for display
        if show_colorbar is True:
            k = list(marker_data_dict.keys())[0]
            marker_data_dict[k]["marker"].update(
                dict(
                    colorscale=[[0, colors[0]], [1, colors[1]]],
                    showscale=True,
                    cmax=100,
                    cmin=0,
                ))

    if isinstance(chart[0][index_col], str):
        index_vals = []
        for row in range(len(tasks)):
            if chart[row][index_col] not in index_vals:
                index_vals.append(chart[row][index_col])

        index_vals.sort()

        if len(colors) < len(index_vals):
            raise exceptions.PlotlyError(
                "Error. The number of colors in 'colors' must be no less "
                "than the number of unique index values in your group "
                "column.")

        # make a dictionary assignment to each index value
        index_vals_dict = {}
        # define color index
        c_index = 0
        for key in index_vals:
            if c_index > len(colors) - 1:
                c_index = 0
            index_vals_dict[key] = colors[c_index]
            c_index += 1

        # create the list of task names
        for index in range(len(tasks)):
            tn = tasks[index]["name"]
            # Is added to task_names if group_tasks is set to False,
            # or if the option is used (True) it only adds them if the
            # name is not already in the list
            if not group_tasks or tn not in task_names:
                task_names.append(tn)
        # Guarantees that for grouped tasks the tasks that are inserted
        # first are shown at the top
        if group_tasks:
            task_names.reverse()

        for index in range(len(tasks)):
            tn = tasks[index]["name"]
            del tasks[index]["name"]

            # If group_tasks is True, all tasks with the same name belong
            # to the same row.
            groupID = index
            if group_tasks:
                groupID = task_names.index(tn)
            tasks[index]["y0"] = groupID - bar_width
            tasks[index]["y1"] = groupID + bar_width

            tasks[index]["fillcolor"] = index_vals_dict[chart[index]
                                                        [index_col]]
            color_id = tasks[index]["fillcolor"]

            if color_id not in scatter_data_dict:
                scatter_data_dict[color_id] = copy.deepcopy(
                    scatter_data_template)

            scatter_data_dict[color_id]["fillcolor"] = color_id
            scatter_data_dict[color_id]["legendgroup"] = color_id
            scatter_data_dict[color_id]["name"] = str(chart[index][index_col])

            # relabel colors with 'rgb'
            colors = clrs.color_parser(colors, clrs.label_rgb)

            # if there are already values append the gap
            if len(scatter_data_dict[color_id]["x"]) > 0:
                # a gap on the scatterplot separates the rectangles from each other
                scatter_data_dict[color_id]["x"].append(
                    scatter_data_dict[color_id]["x"][-1])
                scatter_data_dict[color_id]["y"].append(None)

            xs, ys = _get_corner_points(
                tasks[index]["x0"],
                tasks[index]["y0"],
                tasks[index]["x1"],
                tasks[index]["y1"],
            )

            scatter_data_dict[color_id]["x"] += xs
            scatter_data_dict[color_id]["y"] += ys

            # append dummy markers for showing start and end of interval
            if color_id not in marker_data_dict:
                marker_data_dict[color_id] = copy.deepcopy(
                    marker_data_template)
                marker_data_dict[color_id]["marker"]["color"] = color_id
                marker_data_dict[color_id]["legendgroup"] = color_id

            marker_data_dict[color_id]["x"].append(tasks[index]["x0"])
            marker_data_dict[color_id]["x"].append(tasks[index]["x1"])
            marker_data_dict[color_id]["y"].append(groupID)
            marker_data_dict[color_id]["y"].append(groupID)

            if "description" in tasks[index]:
                marker_data_dict[color_id]["text"].append(
                    tasks[index]["description"])
                marker_data_dict[color_id]["text"].append(
                    tasks[index]["description"])
                del tasks[index]["description"]
            else:
                marker_data_dict[color_id]["text"].append(None)
                marker_data_dict[color_id]["text"].append(None)

        if show_colorbar is True:
            showlegend = True
            for k in scatter_data_dict:
                scatter_data_dict[k]["showlegend"] = showlegend
    # add colorbar to one of the traces randomly just for display
    # if show_colorbar is True:
    #     k = list(marker_data_dict.keys())[0]
    #     marker_data_dict[k]["marker"].update(
    #         dict(
    #             colorscale=[[0, colors[0]], [1, colors[1]]],
    #             showscale=True,
    #             cmax=100,
    #             cmin=0,
    #         )
    #     )

    layout = dict(
        title=title,
        showlegend=showlegend,
        height=height,
        width=width,
        shapes=[],
        hovermode="closest",
        yaxis=dict(
            showgrid=showgrid_y,
            ticktext=task_names,
            tickvals=list(range(len(task_names))),
            range=[-1, len(task_names) + 1],
            autorange=False,
            zeroline=False,
        ),
        xaxis=dict(
            showgrid=showgrid_x,
            zeroline=False,
            rangeselector=dict(buttons=list([
                dict(count=7, label="1w", step="day", stepmode="backward"),
                dict(count=1, label="1m", step="month", stepmode="backward"),
                dict(count=6, label="6m", step="month", stepmode="backward"),
                dict(count=1, label="YTD", step="year", stepmode="todate"),
                dict(count=1, label="1y", step="year", stepmode="backward"),
                dict(step="all"),
            ])),
            type="date",
        ),
    )

    data = [scatter_data_dict[k] for k in sorted(scatter_data_dict)]
    data += [marker_data_dict[k] for k in sorted(marker_data_dict)]

    # fig = dict(
    #     data=data, layout=layout
    # )
    fig = go.Figure(data=data, layout=layout)
    return fig
    def test_instantiation(self):

        native_figure = {"data": [], "layout": {}, "frames": []}

        go.Figure(native_figure)
        go.Figure()
Exemple #20
0
def gantt(
    chart,
    colors,
    title,
    bar_width,
    showgrid_x,
    showgrid_y,
    height,
    width,
    tasks=None,
    task_names=None,
    data=None,
    group_tasks=False,
    show_hover_fill=True,
    show_colorbar=True,
):
    """
    Refer to create_gantt() for docstring
    """
    if tasks is None:
        tasks = []
    if task_names is None:
        task_names = []
    if data is None:
        data = []

    for index in range(len(chart)):
        task = dict(
            x0=chart[index]["Start"],
            x1=chart[index]["Finish"],
            name=chart[index]["Task"],
        )
        if "Description" in chart[index]:
            task["description"] = chart[index]["Description"]
        tasks.append(task)

    # create a scatter trace for every task group
    scatter_data_dict = dict()
    marker_data_dict = dict()

    if show_hover_fill:
        hoverinfo = "name"
    else:
        hoverinfo = "skip"

    scatter_data_template = {
        "x": [],
        "y": [],
        "mode": "none",
        "fill": "toself",
        "hoverinfo": hoverinfo,
    }

    marker_data_template = {
        "x": [],
        "y": [],
        "mode": "markers",
        "text": [],
        "marker": dict(color="", size=1, opacity=0),
        "name": "",
        "showlegend": False,
    }

    # create the list of task names
    for index in range(len(tasks)):
        tn = tasks[index]["name"]
        # Is added to task_names if group_tasks is set to False,
        # or if the option is used (True) it only adds them if the
        # name is not already in the list
        if not group_tasks or tn not in task_names:
            task_names.append(tn)
    # Guarantees that for grouped tasks the tasks that are inserted first
    # are shown at the top
    if group_tasks:
        task_names.reverse()

    color_index = 0
    for index in range(len(tasks)):
        tn = tasks[index]["name"]
        del tasks[index]["name"]

        # If group_tasks is True, all tasks with the same name belong
        # to the same row.
        groupID = index
        if group_tasks:
            groupID = task_names.index(tn)
        tasks[index]["y0"] = groupID - bar_width
        tasks[index]["y1"] = groupID + bar_width

        # check if colors need to be looped
        if color_index >= len(colors):
            color_index = 0
        tasks[index]["fillcolor"] = colors[color_index]
        color_id = tasks[index]["fillcolor"]

        if color_id not in scatter_data_dict:
            scatter_data_dict[color_id] = copy.deepcopy(scatter_data_template)

        scatter_data_dict[color_id]["fillcolor"] = color_id
        scatter_data_dict[color_id]["name"] = str(tn)
        scatter_data_dict[color_id]["legendgroup"] = color_id

        # if there are already values append the gap
        if len(scatter_data_dict[color_id]["x"]) > 0:
            # a gap on the scatterplot separates the rectangles from each other
            scatter_data_dict[color_id]["x"].append(
                scatter_data_dict[color_id]["x"][-1])
            scatter_data_dict[color_id]["y"].append(None)

        xs, ys = _get_corner_points(
            tasks[index]["x0"],
            tasks[index]["y0"],
            tasks[index]["x1"],
            tasks[index]["y1"],
        )

        scatter_data_dict[color_id]["x"] += xs
        scatter_data_dict[color_id]["y"] += ys

        # append dummy markers for showing start and end of interval
        if color_id not in marker_data_dict:
            marker_data_dict[color_id] = copy.deepcopy(marker_data_template)
            marker_data_dict[color_id]["marker"]["color"] = color_id
            marker_data_dict[color_id]["legendgroup"] = color_id

        marker_data_dict[color_id]["x"].append(tasks[index]["x0"])
        marker_data_dict[color_id]["x"].append(tasks[index]["x1"])
        marker_data_dict[color_id]["y"].append(groupID)
        marker_data_dict[color_id]["y"].append(groupID)

        if "description" in tasks[index]:
            marker_data_dict[color_id]["text"].append(
                tasks[index]["description"])
            marker_data_dict[color_id]["text"].append(
                tasks[index]["description"])
            del tasks[index]["description"]
        else:
            marker_data_dict[color_id]["text"].append(None)
            marker_data_dict[color_id]["text"].append(None)

        color_index += 1

    showlegend = show_colorbar

    layout = dict(
        title=title,
        showlegend=showlegend,
        height=height,
        width=width,
        shapes=[],
        hovermode="closest",
        yaxis=dict(
            showgrid=showgrid_y,
            ticktext=task_names,
            tickvals=list(range(len(task_names))),
            range=[-1, len(task_names) + 1],
            autorange=False,
            zeroline=False,
        ),
        xaxis=dict(
            showgrid=showgrid_x,
            zeroline=False,
            rangeselector=dict(buttons=list([
                dict(count=7, label="1w", step="day", stepmode="backward"),
                dict(count=1, label="1m", step="month", stepmode="backward"),
                dict(count=6, label="6m", step="month", stepmode="backward"),
                dict(count=1, label="YTD", step="year", stepmode="todate"),
                dict(count=1, label="1y", step="year", stepmode="backward"),
                dict(step="all"),
            ])),
            type="date",
        ),
    )

    data = [scatter_data_dict[k] for k in sorted(scatter_data_dict)]
    data += [marker_data_dict[k] for k in sorted(marker_data_dict)]

    # fig = dict(
    #     data=data, layout=layout
    # )
    fig = go.Figure(data=data, layout=layout)
    return fig
 def test_pop_invalid_key(self):
     fig = go.Figure(layout=go.Layout(width=1000))
     with self.assertRaises(KeyError):
         fig.pop("bogus")