Repository: bokeh/bokeh

Issue:
Use only what we need from underscore
Pretty much drop underscore as a whole and use only parts we need. Use typescript for extra points.



Discussion:

Resolving patch:
diff --git a/examples/plotting/file/custom_datetime_axis.py b/examples/plotting/file/custom_datetime_axis.py
--- a/examples/plotting/file/custom_datetime_axis.py
+++ b/examples/plotting/file/custom_datetime_axis.py
@@ -20,7 +20,6 @@ class DateGapTickFormatter(TickFormatter):
     """)
 
     __implementation__ = """
-        import * as _ from "underscore"
         import {Model} from "model"
         import * as p from "core/properties"
 


Test patch:
diff --git a/bokehjs/test/client.coffee b/bokehjs/test/client.coffee
--- a/bokehjs/test/client.coffee
+++ b/bokehjs/test/client.coffee
@@ -1,4 +1,3 @@
-_ = require "underscore"
 chai = require "chai"
 chai.use(require "chai-as-promised")
 expect = chai.expect
@@ -52,7 +51,7 @@ with_server = (f) ->
   else
     pypath = basedir
   port = next_port()
-  env = _.extend({}, process.env, { PYTHONPATH: pypath })
+  env = Object.assign({}, process.env, { PYTHONPATH: pypath })
   handle = child_process.spawn("python", ["-m", "bokeh", "serve", "--port=#{port}"], {
     env: env,
     cwd: basedir
diff --git a/bokehjs/test/common/fixtures/object.coffee b/bokehjs/test/common/fixtures/object.coffee
--- a/bokehjs/test/common/fixtures/object.coffee
+++ b/bokehjs/test/common/fixtures/object.coffee
@@ -1,4 +1,3 @@
-_ = require "underscore"
 utils = require "../../utils"
 {HasProps} = utils.require "core/has_props"
 
diff --git a/bokehjs/test/common/selection_manager.coffee b/bokehjs/test/common/selection_manager.coffee
--- a/bokehjs/test/common/selection_manager.coffee
+++ b/bokehjs/test/common/selection_manager.coffee
@@ -86,7 +86,6 @@ describe "SelectionManager", ->
   selector = null
 
   it.skip "should add a selector when encountering a new renderer", ->
-    utils.require("core/util/underscore").patch()
     selector = sm._get_selector(glyph_renderer_view_normal)
     expect(Object.keys(sm.selectors)).to.have.lengthOf(1)
     expect(sm.selectors).to.have.property glyph_renderer_view_normal.model.id
diff --git a/bokehjs/test/core/fixtures/object.coffee b/bokehjs/test/core/fixtures/object.coffee
--- a/bokehjs/test/core/fixtures/object.coffee
+++ b/bokehjs/test/core/fixtures/object.coffee
@@ -1,4 +1,3 @@
-_ = require "underscore"
 utils = require "../../utils"
 {HasProps} = utils.require "core/has_props"
 
diff --git a/bokehjs/test/core/has_props.coffee b/bokehjs/test/core/has_props.coffee
--- a/bokehjs/test/core/has_props.coffee
+++ b/bokehjs/test/core/has_props.coffee
@@ -1,5 +1,4 @@
 {expect} = require "chai"
-_ = require "underscore"
 utils = require "../utils"
 fixtures = require "./fixtures/object"
 
@@ -40,31 +39,31 @@ describe "has_properties module", ->
 
     it "should have only id property", ->
       obj = new HasProps()
-      expect(_.keys(obj.properties)).to.be.deep.equal ['id']
-      expect(_.keys(obj.attributes)).to.be.deep.equal ['id']
+      expect(Object.keys(obj.properties)).to.be.deep.equal ['id']
+      expect(Object.keys(obj.attributes)).to.be.deep.equal ['id']
 
     it "should combine props from subclasses", ->
       obj = new SubclassWithProps()
-      expect(_.keys(obj.properties)).to.be.deep.equal ['id', 'foo', 'bar']
+      expect(Object.keys(obj.properties)).to.be.deep.equal ['id', 'foo', 'bar']
 
     it "should combine props from sub-subclasses", ->
       obj = new SubSubclassWithProps()
-      expect(_.keys(obj.properties)).to.be.deep.equal ['id', 'foo', 'bar', 'baz']
+      expect(Object.keys(obj.properties)).to.be.deep.equal ['id', 'foo', 'bar', 'baz']
 
     it "should combine mixins from subclasses", ->
       obj = new SubclassWithMixins()
-      props = _.keys(mixins.line(""))
-      expect(_.keys(obj.properties)).to.be.deep.equal(['id'].concat(props))
+      props = Object.keys(mixins.line(""))
+      expect(Object.keys(obj.properties)).to.be.deep.equal(['id'].concat(props))
 
     it "should combine mixins from sub-subclasses", ->
       obj = new SubSubclassWithMixins()
-      props = _.keys(_.extend(mixins.line(""), mixins.fill("foo_")))
-      expect(_.keys(obj.properties)).to.be.deep.equal(['id'].concat(props))
+      props = Object.keys(Object.assign(mixins.line(""), mixins.fill("foo_")))
+      expect(Object.keys(obj.properties)).to.be.deep.equal(['id'].concat(props))
 
     it "should combine multiple mixins from subclasses", ->
       obj = new SubclassWithMultipleMixins()
-      props = _.keys(_.extend(mixins.line(""), mixins.text("bar_")))
-      expect(_.keys(obj.properties)).to.be.deep.equal(['id'].concat(props))
+      props = Object.keys(Object.assign(mixins.line(""), mixins.text("bar_")))
+      expect(Object.keys(obj.properties)).to.be.deep.equal(['id'].concat(props))
 
   # describe "arrays of references", ->
   #   [model1, model2, model3, model4, doc] = [null, null, null, null, null]
diff --git a/bokehjs/test/core/layout/layout_canvas.coffee b/bokehjs/test/core/layout/layout_canvas.coffee
--- a/bokehjs/test/core/layout/layout_canvas.coffee
+++ b/bokehjs/test/core/layout/layout_canvas.coffee
@@ -1,4 +1,3 @@
-_ = require "underscore"
 {expect} = require "chai"
 utils = require "../../utils"
 
@@ -13,10 +12,10 @@ describe "LayoutCanvas", ->
 
   it "should get new variables on initialize", ->
     c = new LayoutCanvas()
-    expect(_.has(c, '_top')).to.be.true
-    expect(_.has(c, '_left')).to.be.true
-    expect(_.has(c, '_width')).to.be.true
-    expect(_.has(c, '_height')).to.be.true
+    expect(c).to.have.property('_top')
+    expect(c).to.have.property('_left')
+    expect(c).to.have.property('_width')
+    expect(c).to.have.property('_height')
     expect(c._top).to.be.an.instanceOf(Variable)
     expect(c._left).to.be.an.instanceOf(Variable)
     expect(c._width).to.be.an.instanceOf(Variable)
@@ -30,7 +29,7 @@ describe "LayoutCanvas", ->
     for e in ev
       variables.push(e.edit_variable._name)
       expect(e.strength._strength).to.be.equal Strength.strong._strength
-    expect(_.contains(variables, "top #{c.id}")).to.be.true
-    expect(_.contains(variables, "left #{c.id}")).to.be.true
-    expect(_.contains(variables, "width #{c.id}")).to.be.true
-    expect(_.contains(variables, "height #{c.id}")).to.be.true
+    expect("top #{c.id}"    in variables).to.be.true
+    expect("left #{c.id}"   in variables).to.be.true
+    expect("width #{c.id}"  in variables).to.be.true
+    expect("height #{c.id}" in variables).to.be.true
diff --git a/bokehjs/test/core/layout/side_panel.coffee b/bokehjs/test/core/layout/side_panel.coffee
--- a/bokehjs/test/core/layout/side_panel.coffee
+++ b/bokehjs/test/core/layout/side_panel.coffee
@@ -1,4 +1,3 @@
-_ = require "underscore"
 {expect} = require "chai"
 utils = require "../../utils"
 sinon = require 'sinon'
diff --git a/bokehjs/test/core/properties.coffee b/bokehjs/test/core/properties.coffee
--- a/bokehjs/test/core/properties.coffee
+++ b/bokehjs/test/core/properties.coffee
@@ -531,7 +531,7 @@ describe "properties module", ->
         expect(prop.transform).to.be.equal properties.Property.prototype.transform
 
   describe "Instance", ->
-    prop = new properties.Instance({obj: new SomeHasProps(a: {value: null}), attr: 'a'})
+    prop = new properties.Instance({obj: new SomeHasProps({a: null}), attr: 'a'})
 
     it "should be an instance of Property", ->
       expect(prop).to.be.instanceof properties.Property
diff --git a/bokehjs/test/core/util/math.coffee b/bokehjs/test/core/util/math.coffee
--- a/bokehjs/test/core/util/math.coffee
+++ b/bokehjs/test/core/util/math.coffee
@@ -5,17 +5,6 @@ math = utils.require("core/util/math")
 
 describe "math module", ->
 
-  array_neg = [-2, -1, 0, 1, 2]
-  array_dbl = [0, 0, 1, 1, 2, 2]
-
-  it "should return min value of array", ->
-    expect(math.array_min(array_neg)).to.eql(-2)
-    expect(math.array_min(array_dbl)).to.eql(0)
-
-  it "should return max value of array", ->
-    expect(math.array_max(array_neg)).to.eql(2)
-    expect(math.array_max(array_dbl)).to.eql(2)
-
   it "should return angle normalized between 0 and 2*Math.PI inclusive", ->
     expect(math.angle_norm(3*Math.PI)).to.be.closeTo(Math.PI, 0.000001)
     expect(math.angle_norm(-3*Math.PI)).to.be.closeTo(Math.PI, 0.000001)
diff --git a/bokehjs/test/models/formatters/func_tick_formatter.coffee b/bokehjs/test/models/formatters/func_tick_formatter.coffee
--- a/bokehjs/test/models/formatters/func_tick_formatter.coffee
+++ b/bokehjs/test/models/formatters/func_tick_formatter.coffee
@@ -34,7 +34,7 @@ describe "func_tick_formatter module", ->
 
     it "should support imports using require", ->
       formatter = new FuncTickFormatter({
-        code: "var _ = require('underscore'); return _.max([1,2,3])"
+        code: "let {max} = require('../../core/util/array'); return max([1, 2, 3])"
       })
       labels = formatter.doFormat([0, 0, 0])
       expect(labels).to.be.deep.equal([3,3,3])
diff --git a/bokehjs/test/models/layouts/box.coffee b/bokehjs/test/models/layouts/box.coffee
--- a/bokehjs/test/models/layouts/box.coffee
+++ b/bokehjs/test/models/layouts/box.coffee
@@ -49,7 +49,7 @@ describe "Box", ->
       ev = parent_box.get_edit_variables()
       expect(ev.length).to.be.equal 6
 
-      evs = _.pluck(ev, 'edit_variable')
+      evs = (item.edit_variable for item in ev)
       expect(parent_box._height in evs).is.true
       expect(parent_box._width in evs).is.true
       expect(child1._height in evs).is.true
diff --git a/bokehjs/test/models/layouts/layout_dom.coffee b/bokehjs/test/models/layouts/layout_dom.coffee
--- a/bokehjs/test/models/layouts/layout_dom.coffee
+++ b/bokehjs/test/models/layouts/layout_dom.coffee
@@ -1,4 +1,3 @@
-_ = require "underscore"
 {expect} = require "chai"
 sinon = require "sinon"
 utils = require "../../utils"
diff --git a/bokehjs/test/models/mappers/categorical_mapper.coffee b/bokehjs/test/models/mappers/categorical_mapper.coffee
--- a/bokehjs/test/models/mappers/categorical_mapper.coffee
+++ b/bokehjs/test/models/mappers/categorical_mapper.coffee
@@ -1,4 +1,3 @@
-_ = require "underscore"
 {expect} = require "chai"
 utils = require "../../utils"
 
diff --git a/bokehjs/test/models/plots/plot_canvas.coffee b/bokehjs/test/models/plots/plot_canvas.coffee
--- a/bokehjs/test/models/plots/plot_canvas.coffee
+++ b/bokehjs/test/models/plots/plot_canvas.coffee
@@ -1,4 +1,3 @@
-_ = require "underscore"
 {expect} = require "chai"
 utils = require "../../utils"
 sinon = require 'sinon'
@@ -60,12 +59,12 @@ describe "PlotCanvas", ->
   it "should have panels, frame, and canvas returned in get_layoutable_children", ->
     layoutable_children = @plot_canvas.get_layoutable_children()
     expect(layoutable_children.length).to.be.equal 6
-    expect(_.contains(layoutable_children, @plot_canvas.above_panel)).to.be.true
-    expect(_.contains(layoutable_children, @plot_canvas.below_panel)).to.be.true
-    expect(_.contains(layoutable_children, @plot_canvas.left_panel)).to.be.true
-    expect(_.contains(layoutable_children, @plot_canvas.right_panel)).to.be.true
-    expect(_.contains(layoutable_children, @plot_canvas.frame)).to.be.true
-    expect(_.contains(layoutable_children, @plot_canvas.canvas)).to.be.true
+    expect(@plot_canvas.above_panel in layoutable_children).to.be.true
+    expect(@plot_canvas.below_panel in layoutable_children).to.be.true
+    expect(@plot_canvas.left_panel  in layoutable_children).to.be.true
+    expect(@plot_canvas.right_panel in layoutable_children).to.be.true
+    expect(@plot_canvas.frame       in layoutable_children).to.be.true
+    expect(@plot_canvas.canvas      in layoutable_children).to.be.true
 
   it "should have axis panels in get_layoutable_children if axes added", ->
     plot = new Plot({x_range: new DataRange1d(), y_range: new DataRange1d(), title: null})
@@ -81,10 +80,10 @@ describe "PlotCanvas", ->
     plot_canvas = plot.plot_canvas
     layoutable_children = plot_canvas.get_layoutable_children()
     expect(layoutable_children.length).to.be.equal 10
-    expect(_.contains(layoutable_children, above_axis.panel)).to.be.true
-    expect(_.contains(layoutable_children, below_axis.panel)).to.be.true
-    expect(_.contains(layoutable_children, left_axis.panel)).to.be.true
-    expect(_.contains(layoutable_children, right_axis.panel)).to.be.true
+    expect(above_axis.panel in layoutable_children).to.be.true
+    expect(below_axis.panel in layoutable_children).to.be.true
+    expect(left_axis.panel  in layoutable_children).to.be.true
+    expect(right_axis.panel in layoutable_children).to.be.true
 
   it "should call get_edit_variables on layoutable children", ->
     plot = new Plot({x_range: new DataRange1d(), y_range: new DataRange1d(), title: null})
diff --git a/bokehjs/test/models/sources/column_data_source.coffee b/bokehjs/test/models/sources/column_data_source.coffee
--- a/bokehjs/test/models/sources/column_data_source.coffee
+++ b/bokehjs/test/models/sources/column_data_source.coffee
@@ -1,5 +1,3 @@
-_ = require "underscore"
-
 {expect} = require "chai"
 utils = require "../../utils"
 { stdoutTrap, stderrTrap } = require 'logtrap'
diff --git a/bokehjs/test/models/sources/geojson_data_source.coffee b/bokehjs/test/models/sources/geojson_data_source.coffee
--- a/bokehjs/test/models/sources/geojson_data_source.coffee
+++ b/bokehjs/test/models/sources/geojson_data_source.coffee
@@ -1,5 +1,3 @@
-_ = require "underscore"
-
 {expect} = require "chai"
 utils = require "../../utils"
 
diff --git a/bokehjs/test/models/tiles/dynamic_image_renderer.coffee b/bokehjs/test/models/tiles/dynamic_image_renderer.coffee
--- a/bokehjs/test/models/tiles/dynamic_image_renderer.coffee
+++ b/bokehjs/test/models/tiles/dynamic_image_renderer.coffee
@@ -1,4 +1,3 @@
-_ = require "underscore"
 {expect} = require "chai"
 utils = require "../../utils"
 
diff --git a/bokehjs/test/models/tools/actions/zoom_in_tool.coffee b/bokehjs/test/models/tools/actions/zoom_in_tool.coffee
--- a/bokehjs/test/models/tools/actions/zoom_in_tool.coffee
+++ b/bokehjs/test/models/tools/actions/zoom_in_tool.coffee
@@ -1,4 +1,3 @@
-_ = require "underscore"
 {expect} = require "chai"
 utils = require "../../../utils"
 sinon = require 'sinon'
diff --git a/bokehjs/test/models/tools/actions/zoom_out_tool.coffee b/bokehjs/test/models/tools/actions/zoom_out_tool.coffee
--- a/bokehjs/test/models/tools/actions/zoom_out_tool.coffee
+++ b/bokehjs/test/models/tools/actions/zoom_out_tool.coffee
@@ -1,4 +1,3 @@
-_ = require "underscore"
 {expect} = require "chai"
 utils = require "../../../utils"
 sinon = require 'sinon'
diff --git a/bokehjs/test/models/tools/gestures/resize_tool.coffee b/bokehjs/test/models/tools/gestures/resize_tool.coffee
--- a/bokehjs/test/models/tools/gestures/resize_tool.coffee
+++ b/bokehjs/test/models/tools/gestures/resize_tool.coffee
@@ -1,4 +1,3 @@
-_ = require "underscore"
 {expect} = require "chai"
 utils = require "../../../utils"
 sinon = require 'sinon'
diff --git a/bokehjs/test/models/tools/gestures/wheel_pan_tool.coffee b/bokehjs/test/models/tools/gestures/wheel_pan_tool.coffee
--- a/bokehjs/test/models/tools/gestures/wheel_pan_tool.coffee
+++ b/bokehjs/test/models/tools/gestures/wheel_pan_tool.coffee
@@ -1,4 +1,3 @@
-_ = require "underscore"
 {expect} = require "chai"
 utils = require "../../../utils"
 sinon = require 'sinon'
diff --git a/bokehjs/test/models/tools/gestures/wheel_zoom_tool.coffee b/bokehjs/test/models/tools/gestures/wheel_zoom_tool.coffee
--- a/bokehjs/test/models/tools/gestures/wheel_zoom_tool.coffee
+++ b/bokehjs/test/models/tools/gestures/wheel_zoom_tool.coffee
@@ -1,4 +1,3 @@
-_ = require "underscore"
 {expect} = require "chai"
 utils = require "../../../utils"
 sinon = require 'sinon'
diff --git a/bokehjs/test/models/tools/inspectors/crosshair_tool.coffee b/bokehjs/test/models/tools/inspectors/crosshair_tool.coffee
--- a/bokehjs/test/models/tools/inspectors/crosshair_tool.coffee
+++ b/bokehjs/test/models/tools/inspectors/crosshair_tool.coffee
@@ -1,4 +1,3 @@
-_ = require "underscore"
 {expect} = require "chai"
 utils = require "../../../utils"
 sinon = require 'sinon'
diff --git a/bokehjs/test/models/tools/toolbar.coffee b/bokehjs/test/models/tools/toolbar.coffee
--- a/bokehjs/test/models/tools/toolbar.coffee
+++ b/bokehjs/test/models/tools/toolbar.coffee
@@ -1,4 +1,3 @@
-_ = require "underscore"
 {expect} = require "chai"
 utils = require "../../utils"
 sinon = require "sinon"