from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import sys

if "pyarrow" in sys.modules:
    raise ImportError("Ray must be imported before pyarrow because Ray "
                      "requires a specific version of pyarrow (which is "
                      "packaged along with Ray).")

# Add the directory containing pyarrow to the Python path so that we find the
# pyarrow version packaged with ray and not a pre-existing pyarrow.
pyarrow_path = os.path.join(
    os.path.abspath(os.path.dirname(__file__)), "pyarrow_files")
sys.path.insert(0, pyarrow_path)

# See https://github.com/ray-project/ray/issues/131.
helpful_message = """

If you are using Anaconda, try fixing this problem by running:

    conda install libgcc
"""

try:
    import pyarrow  # noqa: F401
except ImportError as e:
    if ((hasattr(e, "msg") and isinstance(e.msg, str)
         and ("libstdc++" in e.msg or "CXX" in e.msg))):
        # This code path should be taken with Python 3.
        e.msg += helpful_message
    elif (hasattr(e, "message") and isinstance(e.message, str)
          and ("libstdc++" in e.message or "CXX" in e.message)):
        # This code path should be taken with Python 2.
        condition = (hasattr(e, "args") and isinstance(e.args, tuple)
                     and len(e.args) == 1 and isinstance(e.args[0], str))
        if condition:
            e.args = (e.args[0] + helpful_message, )
        else:
            if not hasattr(e, "args"):
                e.args = ()
            elif not isinstance(e.args, tuple):
                e.args = (e.args, )
            e.args += (helpful_message, )
    raise

from ray.raylet import ObjectID, _config  # noqa: E402
from ray.profiling import profile  # noqa: E402
from ray.worker import (error_info, init, connect, disconnect, get, put, wait,
                        remote, get_gpu_ids, get_resource_ids, get_webui_url,
                        register_custom_serializer, shutdown,
                        is_initialized)  # noqa: E402
from ray.worker import (SCRIPT_MODE, WORKER_MODE, LOCAL_MODE,
                        PYTHON_MODE)  # noqa: E402
from ray.worker import global_state  # noqa: E402
import ray.internal  # noqa: E402
# We import ray.actor because some code is run in actor.py which initializes
# some functions in the worker.
import ray.actor  # noqa: F401
from ray.actor import method  # noqa: E402

# Ray version string.
__version__ = "0.5.3"

__all__ = [
    "error_info", "init", "connect", "disconnect", "get", "put", "wait",
    "remote", "profile", "actor", "method", "get_gpu_ids", "get_resource_ids",
    "get_webui_url", "register_custom_serializer", "shutdown",
    "is_initialized", "SCRIPT_MODE", "WORKER_MODE", "LOCAL_MODE",
    "PYTHON_MODE", "global_state", "ObjectID", "_config", "__version__",
    "internal"
]

import ctypes  # noqa: E402
# Windows only
if hasattr(ctypes, "windll"):
    # Makes sure that all child processes die when we die. Also makes sure that
    # fatal crashes result in process termination rather than an error dialog
    # (the latter is annoying since we have a lot of processes). This is done
    # by associating all child processes with a "job" object that imposes this
    # behavior.
    (lambda kernel32: (lambda job: (lambda n: kernel32.SetInformationJobObject(job, 9, "\0" * 17 + chr(0x8 | 0x4 | 0x20) + "\0" * (n - 18), n))(0x90 if ctypes.sizeof(ctypes.c_void_p) > ctypes.sizeof(ctypes.c_int) else 0x70) and kernel32.AssignProcessToJobObject(job, ctypes.c_void_p(kernel32.GetCurrentProcess())))(ctypes.c_void_p(kernel32.CreateJobObjectW(None, None))) if kernel32 is not None else None)(ctypes.windll.kernel32)  # noqa: E501

python/ray/__init__.py
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import sys

if "pyarrow" in sys.modules:
    raise ImportError("Ray must be imported before pyarrow because Ray "
                      "requires a specific version of pyarrow (which is "
                      "packaged along with Ray).")

# Add the directory containing pyarrow to the Python path so that we find the
# pyarrow version packaged with ray and not a pre-existing pyarrow.
pyarrow_path = os.path.join(
    os.path.abspath(os.path.dirname(__file__)), "pyarrow_files")
sys.path.insert(0, pyarrow_path)

# See https://github.com/ray-project/ray/issues/131.
helpful_message = """

If you are using Anaconda, try fixing this problem by running:

    conda install libgcc
"""

try:
    import pyarrow  # noqa: F401
except ImportError as e:
    if ((hasattr(e, "msg") and isinstance(e.msg, str)
         and ("libstdc++" in e.msg or "CXX" in e.msg))):
        # This code path should be taken with Python 3.
        e.msg += helpful_message
    elif (hasattr(e, "message") and isinstance(e.message, str)
          and ("libstdc++" in e.message or "CXX" in e.message)):
        # This code path should be taken with Python 2.
        condition = (hasattr(e, "args") and isinstance(e.args, tuple)
                     and len(e.args) == 1 and isinstance(e.args[0], str))
        if condition:
            e.args = (e.args[0] + helpful_message, )
        else:
            if not hasattr(e, "args"):
                e.args = ()
            elif not isinstance(e.args, tuple):
                e.args = (e.args, )
            e.args += (helpful_message, )
    raise

modin_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), "modin")
sys.path.insert(0, modin_path)

from ray.raylet import ObjectID, _config  # noqa: E402
from ray.profiling import profile  # noqa: E402
from ray.worker import (error_info, init, connect, disconnect, get, put, wait,
                        remote, get_gpu_ids, get_resource_ids, get_webui_url,
                        register_custom_serializer, shutdown,
                        is_initialized)  # noqa: E402
from ray.worker import (SCRIPT_MODE, WORKER_MODE, LOCAL_MODE,
                        PYTHON_MODE)  # noqa: E402
from ray.worker import global_state  # noqa: E402
import ray.internal  # noqa: E402
# We import ray.actor because some code is run in actor.py which initializes
# some functions in the worker.
import ray.actor  # noqa: F401
from ray.actor import method  # noqa: E402

# Ray version string.
__version__ = "0.5.3"

__all__ = [
    "error_info", "init", "connect", "disconnect", "get", "put", "wait",
    "remote", "profile", "actor", "method", "get_gpu_ids", "get_resource_ids",
    "get_webui_url", "register_custom_serializer", "shutdown",
    "is_initialized", "SCRIPT_MODE", "WORKER_MODE", "LOCAL_MODE",
    "PYTHON_MODE", "global_state", "ObjectID", "_config", "__version__",
    "internal"
]

import ctypes  # noqa: E402
# Windows only
if hasattr(ctypes, "windll"):
    # Makes sure that all child processes die when we die. Also makes sure that
    # fatal crashes result in process termination rather than an error dialog
    # (the latter is annoying since we have a lot of processes). This is done
    # by associating all child processes with a "job" object that imposes this
    # behavior.
    (lambda kernel32: (lambda job: (lambda n: kernel32.SetInformationJobObject(job, 9, "\0" * 17 + chr(0x8 | 0x4 | 0x20) + "\0" * (n - 18), n))(0x90 if ctypes.sizeof(ctypes.c_void_p) > ctypes.sizeof(ctypes.c_int) else 0x70) and kernel32.AssignProcessToJobObject(job, ctypes.c_void_p(kernel32.GetCurrentProcess())))(ctypes.c_void_p(kernel32.CreateJobObjectW(None, None))) if kernel32 is not None else None)(ctypes.windll.kernel32)  # noqa: E501

python/ray/__init__.py
diff --git a/.gitignore b/.gitignore
index f8130b3a2f85e..91189b6f9c41a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,9 @@
 /src/ray/object_manager/format/*_generated.h
 /src/ray/raylet/format/*_generated.h
 
+# Modin source files
+/python/ray/modin
+
 # Redis temporary files
 *dump.rdb
 
diff --git a/.travis.yml b/.travis.yml
index debf450738a79..b48089d52ecdb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -171,6 +171,9 @@ script:
   # ray temp file tests
   - python -m pytest -v test/tempfile_test.py
 
+  # modin test files
+  - python python/ray/test/test_modin.py
+
 deploy:
   - provider: s3
     access_key_id: AKIAJ2L7XDUSZVTXI5QA
diff --git a/.travis/install-dependencies.sh b/.travis/install-dependencies.sh
index 293c1b8b6b04d..0fb597d4686f8 100755
--- a/.travis/install-dependencies.sh
+++ b/.travis/install-dependencies.sh
@@ -24,7 +24,7 @@ if [[ "$PYTHON" == "2.7" ]] && [[ "$platform" == "linux" ]]; then
   wget https://repo.continuum.io/miniconda/Miniconda2-4.5.4-Linux-x86_64.sh -O miniconda.sh -nv
   bash miniconda.sh -b -p $HOME/miniconda
   export PATH="$HOME/miniconda/bin:$PATH"
-  pip install -q cython==0.27.3 cmake tensorflow gym opencv-python pyyaml pandas==0.22 requests \
+  pip install -q cython==0.27.3 cmake tensorflow gym opencv-python pyyaml pandas==0.23.4 requests \
     feather-format lxml openpyxl xlrd py-spy setproctitle faulthandler pytest-timeout
 elif [[ "$PYTHON" == "3.5" ]] && [[ "$platform" == "linux" ]]; then
   sudo apt-get update
@@ -33,7 +33,7 @@ elif [[ "$PYTHON" == "3.5" ]] && [[ "$platform" == "linux" ]]; then
   wget https://repo.continuum.io/miniconda/Miniconda3-4.5.4-Linux-x86_64.sh -O miniconda.sh -nv
   bash miniconda.sh -b -p $HOME/miniconda
   export PATH="$HOME/miniconda/bin:$PATH"
-  pip install -q cython==0.27.3 cmake tensorflow gym opencv-python pyyaml pandas==0.22 requests \
+  pip install -q cython==0.27.3 cmake tensorflow gym opencv-python pyyaml pandas==0.23.4 requests \
     feather-format lxml openpyxl xlrd py-spy setproctitle pytest-timeout
 elif [[ "$PYTHON" == "2.7" ]] && [[ "$platform" == "macosx" ]]; then
   # check that brew is installed
@@ -50,7 +50,7 @@ elif [[ "$PYTHON" == "2.7" ]] && [[ "$platform" == "macosx" ]]; then
   wget https://repo.continuum.io/miniconda/Miniconda2-4.5.4-MacOSX-x86_64.sh -O miniconda.sh -nv
   bash miniconda.sh -b -p $HOME/miniconda
   export PATH="$HOME/miniconda/bin:$PATH"
-  pip install -q cython==0.27.3 cmake tensorflow gym opencv-python pyyaml pandas==0.22 requests \
+  pip install -q cython==0.27.3 cmake tensorflow gym opencv-python pyyaml pandas==0.23.4 requests \
     feather-format lxml openpyxl xlrd py-spy setproctitle faulthandler pytest-timeout
 elif [[ "$PYTHON" == "3.5" ]] && [[ "$platform" == "macosx" ]]; then
   # check that brew is installed
@@ -67,7 +67,7 @@ elif [[ "$PYTHON" == "3.5" ]] && [[ "$platform" == "macosx" ]]; then
   wget https://repo.continuum.io/miniconda/Miniconda3-4.5.4-MacOSX-x86_64.sh -O miniconda.sh -nv
   bash miniconda.sh -b -p $HOME/miniconda
   export PATH="$HOME/miniconda/bin:$PATH"
-  pip install -q cython==0.27.3 cmake tensorflow gym opencv-python pyyaml pandas==0.22 requests \
+  pip install -q cython==0.27.3 cmake tensorflow gym opencv-python pyyaml pandas==0.23.4 requests \
     feather-format lxml openpyxl xlrd py-spy setproctitle pytest-timeout
 elif [[ "$LINT" == "1" ]]; then
   sudo apt-get update
diff --git a/.travis/test-wheels.sh b/.travis/test-wheels.sh
index 1765135ec9bef..f7870ea52d496 100755
--- a/.travis/test-wheels.sh
+++ b/.travis/test-wheels.sh
@@ -59,7 +59,7 @@ if [[ "$platform" == "linux" ]]; then
   if [[ "$NUMBER_OF_WHEELS" != "5" ]]; then
     echo "Wrong number of wheels found."
     ls -l $ROOT_DIR/../.whl/
-    exit 1
+    exit 2
   fi
 
 elif [[ "$platform" == "macosx" ]]; then
@@ -94,5 +94,5 @@ elif [[ "$platform" == "macosx" ]]; then
   done
 else
   echo "Unrecognized environment."
-  exit 1
+  exit 3
 fi
diff --git a/python/ray/__init__.py b/python/ray/__init__.py
index a507cdd2e7a2b..6d4e0ba9a6b92 100644
--- a/python/ray/__init__.py
+++ b/python/ray/__init__.py
@@ -46,6 +46,9 @@
             e.args += (helpful_message, )
     raise
 
+modin_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), "modin")
+sys.path.insert(0, modin_path)
+
 from ray.raylet import ObjectID, _config  # noqa: E402
 from ray.profiling import profile  # noqa: E402
 from ray.worker import (error_info, init, connect, disconnect, get, put, wait,
diff --git a/python/ray/test/test_modin.py b/python/ray/test/test_modin.py
new file mode 100644
index 0000000000000..83c11895ec7b8
--- /dev/null
+++ b/python/ray/test/test_modin.py
@@ -0,0 +1,12 @@
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import ray  # noqa F401
+
+
+def test_modin_import():
+    import modin.pandas as pd
+    frame_data = [1, 2, 3, 4, 5, 6, 7, 8]
+    frame = pd.DataFrame(frame_data)
+    assert frame.sum().squeeze() == sum(frame_data)
diff --git a/thirdparty/scripts/build_modin.sh b/thirdparty/scripts/build_modin.sh
new file mode 100755
index 0000000000000..96563fdb21067
--- /dev/null
+++ b/thirdparty/scripts/build_modin.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+set -x
+
+# Cause the script to exit if a single command fails.
+set -e
+
+if [[ -z  "$1" ]]; then
+  PYTHON_EXECUTABLE=`which python`
+else
+  PYTHON_EXECUTABLE=$1
+fi
+
+PYTHON_VERSION="$($PYTHON_EXECUTABLE -c 'import sys; print(sys.version_info[0])')"
+
+TP_DIR=$(cd "$(dirname "${BASH_SOURCE:-$0}")"; pwd)/../
+MODIN_VERSION=0.2.4
+MODIN_WHEELS_FNAME="modin-$MODIN_VERSION-py$PYTHON_VERSION-none-any.whl"
+MODIN_WHEELS_URL="https://github.com/modin-project/modin/releases/download/v$MODIN_VERSION/"
+
+pushd $TP_DIR/../python/ray/
+rm -rf modin
+mkdir modin
+pushd modin
+curl -kL "$MODIN_WHEELS_URL$MODIN_WHEELS_FNAME" -o "$MODIN_WHEELS_FNAME"
+unzip "$MODIN_WHEELS_FNAME"
+rm "$MODIN_WHEELS_FNAME"
+popd
+popd
diff --git a/thirdparty/scripts/setup.sh b/thirdparty/scripts/setup.sh
index 27f1ef0e3ed51..da283bd3b2bb7 100755
--- a/thirdparty/scripts/setup.sh
+++ b/thirdparty/scripts/setup.sh
@@ -67,3 +67,8 @@ bash "$TP_SCRIPT_DIR/build_ui.sh"
 # rDSN (optional)
 ##############################################
 # bash "$TP_SCRIPT_DIR/build_rdsn.sh"
+
+##############################################
+# modin
+##############################################
+bash "$TP_SCRIPT_DIR/build_modin.sh" $PYTHON_EXECUTABLE

Ship Modin with Ray
### Describe the problem
<!-- Describe the problem clearly here. -->
I think it makes sense to ship Modin with Ray. I suggest doing this similar to how pyarrow is shipped with Ray.

We don't need to rely on the dependencies of Modin, but some of the Modin source will have to be updated to make sure that the pandas version is correct.

