From bb637b8ca36082255ad06906816152c43b02adaf Mon Sep 17 00:00:00 2001
From: Jon Mease <jon.mease@gmail.com>
Date: Sat, 4 Aug 2018 10:30:34 -0400
Subject: [PATCH 1/2] Fix for GH1031, large subplot grids resulted in
 validation error.

---
 .../test_tools/test_make_subplots.py          | 27 +++++++++++++++++++
 plotly/tools.py                               | 10 +++++--
 2 files changed, 35 insertions(+), 2 deletions(-)

diff --git a/plotly/tests/test_core/test_tools/test_make_subplots.py b/plotly/tests/test_core/test_tools/test_make_subplots.py
index 903ca36968b..9db24b3cd69 100644
--- a/plotly/tests/test_core/test_tools/test_make_subplots.py
+++ b/plotly/tests/test_core/test_tools/test_make_subplots.py
@@ -2128,3 +2128,30 @@ def test_subplot_titles_insets(self):
         fig = tls.make_subplots(insets=[{'cell': (1, 1), 'l': 0.7, 'b': 0.3}],
                                 subplot_titles=("", 'Inset'))
         self.assertEqual(fig, expected)
+
+    def test_large_columns_no_errors(self):
+        """
+        Test that creating subplots with a large number of columns, and
+        zero vertical spacing doesn't result in domain values that are out
+        of range.
+
+        Here is the error that was reported in GH1031
+
+        ValueError:
+            Invalid value of type 'builtins.float' received for the
+            'domain[1]' property of layout.yaxis
+                Received value: 1.0000000000000007
+
+            The 'domain[1]' property is a number and may be specified as:
+              - An int or float in the interval [0, 1]
+
+        """
+        v_space = 0.0
+
+        # 2D
+        fig = tls.make_subplots(100, 1, vertical_spacing=v_space)
+
+        # 3D
+        fig = tls.make_subplots(100, 1,
+                                vertical_spacing=v_space,
+                                specs=[[{'is_3d': True}] for _ in range(100)])
diff --git a/plotly/tools.py b/plotly/tools.py
index dffd60c9b66..cf7c8636e3f 100644
--- a/plotly/tools.py
+++ b/plotly/tools.py
@@ -1080,7 +1080,11 @@ def _get_anchors(r, c, x_cnt, y_cnt, shared_xaxes, shared_yaxes):
     # Function pasting x/y domains in layout object (2d case)
     def _add_domain(layout, x_or_y, label, domain, anchor, position):
         name = label[0] + 'axis' + label[1:]
-        axis = {'domain': domain}
+
+        # Clamp domain elements between [0, 1].
+        # This is only needed to combat numerical precision errors
+        # See GH1031
+        axis = {'domain': [max(0.0, domain[0]), min(1.0, domain[1])]}
         if anchor:
             axis['anchor'] = anchor
         if isinstance(position, float):
@@ -1090,7 +1094,9 @@ def _add_domain(layout, x_or_y, label, domain, anchor, position):
 
     # Function pasting x/y domains in layout object (3d case)
     def _add_domain_is_3d(layout, s_label, x_domain, y_domain):
-        scene = graph_objs.Scene(domain={'x': x_domain, 'y': y_domain})
+        scene = graph_objs.Scene(
+            domain={'x': [max(0.0, x_domain[0]), min(1.0, x_domain[1])],
+                    'y': [max(0.0, y_domain[0]), min(1.0, y_domain[1])]})
         layout[s_label] = scene
 
     x_cnt = y_cnt = s_cnt = 1  # subplot axis/scene counters

From 9786440ab02a3a7875af9a6fcf75cc4b797a7b74 Mon Sep 17 00:00:00 2001
From: Jon Mease <jon.mease@gmail.com>
Date: Sat, 4 Aug 2018 10:42:22 -0400
Subject: [PATCH 2/2] Removed internal usage of deprecated classes.

---
 plotly/matplotlylib/renderer.py | 6 +++---
 plotly/tools.py                 | 8 ++++----
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/plotly/matplotlylib/renderer.py b/plotly/matplotlylib/renderer.py
index 315cfc51a12..aca990da93b 100644
--- a/plotly/matplotlylib/renderer.py
+++ b/plotly/matplotlylib/renderer.py
@@ -530,7 +530,7 @@ def draw_text(self, **props):
         if not align:
             align = props['style']['halign']  # mpl default
         if 'annotations' not in self.plotly_fig['layout']:
-            self.plotly_fig['layout']['annotations'] = go.Annotations()
+            self.plotly_fig['layout']['annotations'] = []
         if props['text_type'] == 'xlabel':
             self.msg += "      Text object is an xlabel\n"
             self.draw_xlabel(**props)
@@ -576,7 +576,7 @@ def draw_text(self, **props):
                     yref = 'paper'
                 xanchor = props['style']['halign']  # no difference here!
                 yanchor = mpltools.convert_va(props['style']['valign'])
-            annotation = go.Annotation(
+            annotation = go.layout.Annotation(
                 text=(str(props['text']) if
                       isinstance(props['text'], six.string_types) else
                       props['text']),
@@ -631,7 +631,7 @@ def draw_title(self, **props):
                 'position'])
             x, y = mpltools.display_to_paper(x_px, y_px,
                                              self.plotly_fig['layout'])
-            annotation = go.Annotation(
+            annotation = go.layout.Annotation(
                 text=props['text'],
                 font=go.layout.annotation.Font(
                     color=props['style']['color'],
diff --git a/plotly/tools.py b/plotly/tools.py
index cf7c8636e3f..8e10c91f32a 100644
--- a/plotly/tools.py
+++ b/plotly/tools.py
@@ -568,9 +568,9 @@ def get_subplots(rows=1, columns=1, print_grid=False, **kwargs):
             y_start = (plot_height + vertical_spacing) * rrr
             y_end = y_start + plot_height
 
-            xaxis = graph_objs.XAxis(domain=[x_start, x_end], anchor=x_anchor)
+            xaxis = dict(domain=[x_start, x_end], anchor=x_anchor)
             fig['layout'][xaxis_name] = xaxis
-            yaxis = graph_objs.YAxis(domain=[y_start, y_end], anchor=y_anchor)
+            yaxis = dict(domain=[y_start, y_end], anchor=y_anchor)
             fig['layout'][yaxis_name] = yaxis
             plot_num += 1
 
@@ -1094,7 +1094,7 @@ def _add_domain(layout, x_or_y, label, domain, anchor, position):
 
     # Function pasting x/y domains in layout object (3d case)
     def _add_domain_is_3d(layout, s_label, x_domain, y_domain):
-        scene = graph_objs.Scene(
+        scene = dict(
             domain={'x': [max(0.0, x_domain[0]), min(1.0, x_domain[1])],
                     'y': [max(0.0, y_domain[0]), min(1.0, y_domain[1])]})
         layout[s_label] = scene
@@ -1345,7 +1345,7 @@ def _pad(s, cell_len=cell_len):
                                 'yref': 'paper',
                                 'text': subplot_titles[index],
                                 'showarrow': False,
-                                'font': graph_objs.Font(size=16),
+                                'font': dict(size=16),
                                 'xanchor': 'center',
                                 'yanchor': 'bottom'
                                 })