You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mxnet.apache.org by jx...@apache.org on 2017/10/16 03:18:08 UTC

[incubator-mxnet] branch master updated: generate op frontend code for IDE auto-complete (#8244)

This is an automated email from the ASF dual-hosted git repository.

jxie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-mxnet.git


The following commit(s) were added to refs/heads/master by this push:
     new 99099fa  generate op frontend code for IDE auto-complete (#8244)
99099fa is described below

commit 99099faa2422d1307d5a9a6c58a5d28ab0ea163f
Author: Sheng Zha <sz...@users.noreply.github.com>
AuthorDate: Sun Oct 15 20:18:06 2017 -0700

    generate op frontend code for IDE auto-complete (#8244)
    
    * decouple codegen and register
    
    * address comments on refactor
    
    * auto generate code
    
    * trigger test
---
 .gitignore                                  |   5 +
 python/mxnet/base.py                        | 134 +++++++++++++++----
 python/mxnet/ndarray/__init__.py            |   7 +-
 python/mxnet/ndarray/_internal.py           |  30 ++++-
 python/mxnet/ndarray/contrib.py             |   7 +
 python/mxnet/ndarray/linalg.py              |   7 +
 python/mxnet/ndarray/ndarray.py             |   2 +-
 python/mxnet/ndarray/op.py                  | 161 +----------------------
 python/mxnet/ndarray/{op.py => register.py} |  88 ++++++-------
 python/mxnet/ndarray/sparse.py              |  32 ++---
 python/mxnet/symbol/__init__.py             |   7 +-
 python/mxnet/symbol/_internal.py            |  33 ++++-
 python/mxnet/symbol/contrib.py              |   7 +
 python/mxnet/symbol/linalg.py               |   7 +
 python/mxnet/symbol/op.py                   | 193 +---------------------------
 python/mxnet/symbol/{op.py => register.py}  | 103 +++++++--------
 python/mxnet/symbol/sparse.py               |   7 +
 python/mxnet/symbol/symbol.py               |   3 +-
 python/setup.py                             |  20 ++-
 19 files changed, 356 insertions(+), 497 deletions(-)

diff --git a/.gitignore b/.gitignore
index 7ca76c9..2028aa7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -148,3 +148,8 @@ target
 bin/im2rec
 
 model/
+
+# generated function signature for IDE auto-complete
+python/mxnet/symbol/gen_*
+python/mxnet/ndarray/gen_*
+python/.eggs
diff --git a/python/mxnet/base.py b/python/mxnet/base.py
index fe80bd6..138bb4d 100644
--- a/python/mxnet/base.py
+++ b/python/mxnet/base.py
@@ -20,6 +20,7 @@
 """ctypes library of mxnet and helper functions."""
 from __future__ import absolute_import
 
+import os
 import sys
 import ctypes
 import atexit
@@ -392,7 +393,7 @@ def _init_op_module(root_namespace, module_name, make_op_func):
         Top level module name, `mxnet` in the current cases.
     module_name : str
         Second level module name, `ndarray` and `symbol` in the current cases.
-    make_op_func : str
+    make_op_func : function
         Function for creating op functions for `ndarray` and `symbol` modules.
     """
     plist = ctypes.POINTER(ctypes.c_char_p)()
@@ -418,29 +419,114 @@ def _init_op_module(root_namespace, module_name, make_op_func):
     for name in op_names:
         hdl = OpHandle()
         check_call(_LIB.NNGetOpHandle(c_str(name), ctypes.byref(hdl)))
-        function = make_op_func(hdl, name)
-        op_name_prefix = _get_op_name_prefix(function.__name__)
+        op_name_prefix = _get_op_name_prefix(name)
         if len(op_name_prefix) > 0:
-            # register op under mxnet.module_name.op_name_prefix[1:-1]
-            # e.g. mxnet.ndarray.sparse.dot, mxnet.symbol.linalg.gemm
-            function.__name__ = function.__name__[len(op_name_prefix):]
-            function.__module__ = "%s.%s.%s" % (root_namespace, module_name, op_name_prefix[1:-1])
+            func_name = name[len(op_name_prefix):]
             cur_module = submodule_dict[op_name_prefix]
-            setattr(cur_module, function.__name__, function)
-            cur_module.__all__.append(function.__name__)
-            # if op_name_prefix is '_contrib_', also need to register
-            # the op under mxnet.contrib.module_name for backward compatibility
-            if op_name_prefix == '_contrib_':
-                hdl = OpHandle()
-                check_call(_LIB.NNGetOpHandle(c_str(name), ctypes.byref(hdl)))
-                function = make_op_func(hdl, name)
-                function.__name__ = function.__name__[len(op_name_prefix):]
-                function.__module__ = contrib_module_name_old
-                setattr(contrib_module_old, function.__name__, function)
-                contrib_module_old.__all__.append(function.__name__)
-        elif function.__name__.startswith('_'):
-            setattr(module_internal, function.__name__, function)
-            module_internal.__all__.append(function.__name__)
+            module_name = "%s.%s.%s" % (root_namespace, module_name, op_name_prefix[1:-1])
+        elif name.startswith('_'):
+            func_name = name
+            cur_module = module_internal
         else:
-            setattr(module_op, function.__name__, function)
-            module_op.__all__.append(function.__name__)
+            func_name = name
+            cur_module = module_op
+
+        function = make_op_func(hdl, name, func_name)
+        function.__module__ = module_name
+        setattr(cur_module, function.__name__, function)
+        cur_module.__all__.append(function.__name__)
+
+        if op_name_prefix == '_contrib_':
+            hdl = OpHandle()
+            check_call(_LIB.NNGetOpHandle(c_str(name), ctypes.byref(hdl)))
+            func_name = name[len(op_name_prefix):]
+
+            function = make_op_func(hdl, name, func_name)
+            function.__module__ = contrib_module_name_old
+            setattr(contrib_module_old, function.__name__, function)
+            contrib_module_old.__all__.append(function.__name__)
+
+
+def _generate_op_module_signature(root_namespace, module_name, op_code_gen_func):
+    """
+    Generate op functions created by `op_code_gen_func` and write to the source file
+    of `root_namespace.module_name.[submodule_name]`,
+    where `submodule_name` is one of `_OP_SUBMODULE_NAME_LIST`.
+
+    Parameters
+    ----------
+    root_namespace : str
+        Top level module name, `mxnet` in the current cases.
+    module_name : str
+        Second level module name, `ndarray` and `symbol` in the current cases.
+    op_code_gen_func : function
+        Function for creating op functions for `ndarray` and `symbol` modules.
+    """
+    def get_module_file(module_name):
+        """Return the generated module file based on module name."""
+        path = os.path.dirname(__file__)
+        module_path = module_name.split('.')
+        module_path[-1] = 'gen_'+module_path[-1]
+        file_name = os.path.join(path, '..', *module_path) + '.py'
+        module_file = open(file_name, 'w')
+        dependencies = {'symbol': ['from ._internal import SymbolBase',
+                                   'from ..base import _Null'],
+                        'ndarray': ['from ._internal import NDArrayBase',
+                                    'from ..base import _Null']}
+        module_file.write('# File content is auto-generated. Do not modify.'+os.linesep)
+        module_file.write('# pylint: skip-file'+os.linesep)
+        module_file.write(os.linesep.join(dependencies[module_name.split('.')[1]]))
+        return module_file
+    def write_all_str(module_file, module_all_list):
+        """Write the proper __all__ based on available operators."""
+        module_file.write(os.linesep)
+        module_file.write(os.linesep)
+        all_str = '__all__ = [' + ', '.join(["'%s'"%s for s in module_all_list]) + ']'
+        module_file.write(all_str)
+
+    plist = ctypes.POINTER(ctypes.c_char_p)()
+    size = ctypes.c_uint()
+
+    check_call(_LIB.MXListAllOpNames(ctypes.byref(size),
+                                     ctypes.byref(plist)))
+    op_names = []
+    for i in range(size.value):
+        op_names.append(py_str(plist[i]))
+
+    module_op_file = get_module_file("%s.%s.op" % (root_namespace, module_name))
+    module_op_all = []
+    module_internal_file = get_module_file("%s.%s._internal"%(root_namespace, module_name))
+    module_internal_all = []
+    submodule_dict = {}
+    for op_name_prefix in _OP_NAME_PREFIX_LIST:
+        submodule_dict[op_name_prefix] =\
+            (get_module_file("%s.%s.%s" % (root_namespace, module_name,
+                                           op_name_prefix[1:-1])), [])
+    for name in op_names:
+        hdl = OpHandle()
+        check_call(_LIB.NNGetOpHandle(c_str(name), ctypes.byref(hdl)))
+        op_name_prefix = _get_op_name_prefix(name)
+        if len(op_name_prefix) > 0:
+            func_name = name[len(op_name_prefix):]
+            cur_module_file, cur_module_all = submodule_dict[op_name_prefix]
+        elif name.startswith('_'):
+            func_name = name
+            cur_module_file = module_internal_file
+            cur_module_all = module_internal_all
+        else:
+            func_name = name
+            cur_module_file = module_op_file
+            cur_module_all = module_op_all
+
+        code, _ = op_code_gen_func(hdl, name, func_name, True)
+        cur_module_file.write(os.linesep)
+        cur_module_file.write(code)
+        cur_module_all.append(func_name)
+
+    for (submodule_f, submodule_all) in submodule_dict.values():
+        write_all_str(submodule_f, submodule_all)
+        submodule_f.close()
+    write_all_str(module_op_file, module_op_all)
+    module_op_file.close()
+    write_all_str(module_internal_file, module_internal_all)
+    module_internal_file.close()
diff --git a/python/mxnet/ndarray/__init__.py b/python/mxnet/ndarray/__init__.py
index 1e1d8ed..586dc9e 100644
--- a/python/mxnet/ndarray/__init__.py
+++ b/python/mxnet/ndarray/__init__.py
@@ -17,8 +17,13 @@
 
 """NDArray API of MXNet."""
 
-from . import _internal, contrib, linalg, sparse, random, utils
+from . import _internal, contrib, linalg, op, random, sparse, utils
 # pylint: disable=wildcard-import, redefined-builtin
+try:
+    from .gen_op import * # pylint: disable=unused-wildcard-import
+except ImportError:
+    pass
+from . import register
 from .op import *
 from .ndarray import *
 # pylint: enable=wildcard-import
diff --git a/python/mxnet/ndarray/_internal.py b/python/mxnet/ndarray/_internal.py
index 96eff31..5f3ce97 100644
--- a/python/mxnet/ndarray/_internal.py
+++ b/python/mxnet/ndarray/_internal.py
@@ -15,5 +15,33 @@
 # specific language governing permissions and limitations
 # under the License.
 
+# pylint: disable=wildcard-import, unused-import
 """NDArray namespace used to register internal functions."""
-__all__ = []
+import os as _os
+import sys as _sys
+
+import numpy as np
+
+try:
+    if int(_os.environ.get("MXNET_ENABLE_CYTHON", True)) == 0:
+        from .._ctypes.ndarray import NDArrayBase, CachedOp
+        from .._ctypes.ndarray import _set_ndarray_class, _imperative_invoke
+    elif _sys.version_info >= (3, 0):
+        from .._cy3.ndarray import NDArrayBase, CachedOp
+        from .._cy3.ndarray import _set_ndarray_class, _imperative_invoke
+    else:
+        from .._cy2.ndarray import NDArrayBase, CachedOp
+        from .._cy2.ndarray import _set_ndarray_class, _imperative_invoke
+except ImportError:
+    if int(_os.environ.get("MXNET_ENFORCE_CYTHON", False)) != 0:
+        raise ImportError("Cython Module cannot be loaded but MXNET_ENFORCE_CYTHON=1")
+    from .._ctypes.ndarray import NDArrayBase, CachedOp
+    from .._ctypes.ndarray import _set_ndarray_class, _imperative_invoke
+
+from ..base import _Null
+try:
+    from .gen__internal import * # pylint: disable=unused-wildcard-import
+except ImportError:
+    pass
+
+__all__ = ['NDArrayBase', 'CachedOp', '_imperative_invoke', '_set_ndarray_class']
diff --git a/python/mxnet/ndarray/contrib.py b/python/mxnet/ndarray/contrib.py
index f21d144..0a3fe48 100644
--- a/python/mxnet/ndarray/contrib.py
+++ b/python/mxnet/ndarray/contrib.py
@@ -15,5 +15,12 @@
 # specific language governing permissions and limitations
 # under the License.
 
+# coding: utf-8
+# pylint: disable=wildcard-import, unused-wildcard-import
 """Contrib NDArray API of MXNet."""
+try:
+    from .gen_contrib import *
+except ImportError:
+    pass
+
 __all__ = []
diff --git a/python/mxnet/ndarray/linalg.py b/python/mxnet/ndarray/linalg.py
index 0c8e7fd..baceab2 100644
--- a/python/mxnet/ndarray/linalg.py
+++ b/python/mxnet/ndarray/linalg.py
@@ -15,5 +15,12 @@
 # specific language governing permissions and limitations
 # under the License.
 
+# coding: utf-8
+# pylint: disable=wildcard-import, unused-wildcard-import
 """Linear Algebra NDArray API of MXNet."""
+try:
+    from .gen_linalg import *
+except ImportError:
+    pass
+
 __all__ = []
diff --git a/python/mxnet/ndarray/ndarray.py b/python/mxnet/ndarray/ndarray.py
index 82e704a..2f9972b 100644
--- a/python/mxnet/ndarray/ndarray.py
+++ b/python/mxnet/ndarray/ndarray.py
@@ -39,7 +39,7 @@ from ..base import ctypes2buffer
 from ..context import Context
 from . import _internal
 from . import op
-from .op import NDArrayBase
+from ._internal import NDArrayBase
 
 __all__ = ["NDArray", "concatenate", "_DTYPE_NP_TO_MX", "_DTYPE_MX_TO_NP", "_GRAD_REQ_MAP",
            "ones", "add", "arange", "divide", "equal", "full", "greater", "greater_equal",
diff --git a/python/mxnet/ndarray/op.py b/python/mxnet/ndarray/op.py
index 27ce09b..a328677 100644
--- a/python/mxnet/ndarray/op.py
+++ b/python/mxnet/ndarray/op.py
@@ -15,160 +15,13 @@
 # specific language governing permissions and limitations
 # under the License.
 
-"""Register backend ops in mxnet.ndarray namespace"""
-__all__ = ['CachedOp']
-
-import sys as _sys
-import os as _os
-import ctypes
-import numpy as np  # pylint: disable=unused-import
-
-from ..ndarray_doc import _build_doc
-
-# Use different version of SymbolBase
-# When possible, use cython to speedup part of computation.
-# pylint: disable=unused-import
+# coding: utf-8
+# pylint: disable=wildcard-import, unused-wildcard-import, redefined-builtin
+"""Backend ops in mxnet.ndarray namespace"""
+from ._internal import CachedOp
 try:
-    if int(_os.environ.get("MXNET_ENABLE_CYTHON", True)) == 0:
-        from .._ctypes.ndarray import NDArrayBase
-        from .._ctypes.ndarray import CachedOp, _imperative_invoke
-    elif _sys.version_info >= (3, 0):
-        from .._cy3.ndarray import NDArrayBase, _imperative_invoke
-        from .._cy3.ndarray import CachedOp, _imperative_invoke
-    else:
-        from .._cy2.ndarray import NDArrayBase, _imperative_invoke
-        from .._cy2.ndarray import CachedOp, _imperative_invoke
+    from .gen_op import * # pylint: disable=unused-wildcard-import
 except ImportError:
-    if int(_os.environ.get("MXNET_ENFORCE_CYTHON", False)) != 0:
-        raise ImportError("Cython Module cannot be loaded but MXNET_ENFORCE_CYTHON=1")
-    from .._ctypes.ndarray import NDArrayBase, _imperative_invoke
-    from .._ctypes.ndarray import CachedOp, _imperative_invoke
-
-from ..base import mx_uint, check_call, _LIB, py_str, _init_op_module, _Null
-# pylint: enable=unused-import
-
-
-# pylint: disable=too-many-locals, invalid-name
-def _make_ndarray_function(handle, name):
-    """Create a NDArray function from the FunctionHandle."""
-    real_name = ctypes.c_char_p()
-    desc = ctypes.c_char_p()
-    num_args = mx_uint()
-    arg_names = ctypes.POINTER(ctypes.c_char_p)()
-    arg_types = ctypes.POINTER(ctypes.c_char_p)()
-    arg_descs = ctypes.POINTER(ctypes.c_char_p)()
-    key_var_num_args = ctypes.c_char_p()
-    ret_type = ctypes.c_char_p()
+    pass
 
-    check_call(_LIB.MXSymbolGetAtomicSymbolInfo(
-        handle, ctypes.byref(real_name), ctypes.byref(desc),
-        ctypes.byref(num_args),
-        ctypes.byref(arg_names),
-        ctypes.byref(arg_types),
-        ctypes.byref(arg_descs),
-        ctypes.byref(key_var_num_args),
-        ctypes.byref(ret_type)))
-    narg = int(num_args.value)
-    arg_names = [py_str(arg_names[i]) for i in range(narg)]
-    arg_types = [py_str(arg_types[i]) for i in range(narg)]
-    func_name = name
-    key_var_num_args = py_str(key_var_num_args.value)
-    ret_type = py_str(ret_type.value) if ret_type.value is not None else ''
-    doc_str = _build_doc(func_name,
-                         py_str(desc.value),
-                         arg_names,
-                         arg_types,
-                         [py_str(arg_descs[i]) for i in range(narg)],
-                         key_var_num_args,
-                         ret_type)
-
-    dtype_name = None
-    arr_name = None
-    ndsignature = []
-    signature = []
-    ndarg_names = []
-    kwarg_names = []
-    for i in range(narg):
-        name, atype = arg_names[i], arg_types[i]
-        if name == 'dtype':
-            dtype_name = name
-            signature.append('%s=_Null'%name)
-        elif atype.startswith('NDArray') or atype.startswith('Symbol'):
-            assert not arr_name, \
-                "Op can only have one argument with variable " \
-                "size and it must be the last argument."
-            if atype.endswith('[]'):
-                ndsignature.append('*%s'%name)
-                arr_name = name
-            else:
-                ndsignature.append('%s=None'%name)
-                ndarg_names.append(name)
-        else:
-            signature.append('%s=_Null'%name)
-            kwarg_names.append(name)
-    signature.append('out=None')
-    signature.append('name=None')
-    signature.append('**kwargs')
-    signature = ndsignature + signature
-
-    code = []
-    if arr_name:
-        code.append("""
-def %s(*%s, **kwargs):"""%(func_name, arr_name))
-        code.append("""
-    ndargs = []
-    for i in {}:
-        assert isinstance(i, NDArrayBase), \\
-            "Positional arguments must have NDArray type, " \\
-            "but got %s"%str(i)
-        ndargs.append(i)""".format(arr_name))
-        if dtype_name is not None:
-            code.append("""
-    if '%s' in kwargs:
-        kwargs['%s'] = np.dtype(kwargs['%s']).name"""%(
-            dtype_name, dtype_name, dtype_name))
-        code.append("""
-    _ = kwargs.pop('name', None)
-    out = kwargs.pop('out', None)
-    keys = list(kwargs.keys())
-    vals = list(kwargs.values())""")
-    else:
-        code.append("""
-def %s(%s):
-    ndargs = []
-    keys = list(kwargs.keys())
-    vals = list(kwargs.values())"""%(func_name, ', '.join(signature)))
-        # NDArray args
-        for name in ndarg_names: # pylint: disable=redefined-argument-from-local
-            code.append("""
-    if {name} is not None:
-        assert isinstance({name}, NDArrayBase), \\
-            "Argument {name} must have NDArray type, but got %s"%str({name})
-        ndargs.append({name})""".format(name=name))
-        # kwargs
-        for name in kwarg_names: # pylint: disable=redefined-argument-from-local
-            code.append("""
-    if %s is not _Null:
-        keys.append('%s')
-        vals.append(%s)"""%(name, name, name))
-        # dtype
-        if dtype_name is not None:
-            code.append("""
-    if %s is not _Null:
-        keys.append('%s')
-        vals.append(np.dtype(%s).name)"""%(dtype_name, dtype_name, dtype_name))
-
-    code.append("""
-    return _imperative_invoke(%d, ndargs, keys, vals, out)"""%(
-        handle.value))
-
-    local = {}
-    exec(''.join(code), None, local)  # pylint: disable=exec-used
-    ndarray_function = local[func_name]
-    ndarray_function.__name__ = func_name
-    ndarray_function.__doc__ = doc_str
-    ndarray_function.__module__ = 'mxnet.ndarray'
-    return ndarray_function
-
-
-_init_op_module('mxnet', 'ndarray', _make_ndarray_function)
+__all__ = ['CachedOp']
diff --git a/python/mxnet/ndarray/op.py b/python/mxnet/ndarray/register.py
similarity index 75%
copy from python/mxnet/ndarray/op.py
copy to python/mxnet/ndarray/register.py
index 27ce09b..48d5c01 100644
--- a/python/mxnet/ndarray/op.py
+++ b/python/mxnet/ndarray/register.py
@@ -16,41 +16,18 @@
 # under the License.
 
 """Register backend ops in mxnet.ndarray namespace"""
-__all__ = ['CachedOp']
-
-import sys as _sys
 import os as _os
 import ctypes
 import numpy as np  # pylint: disable=unused-import
 
+from ._internal import NDArrayBase, _imperative_invoke # pylint: disable=unused-import
 from ..ndarray_doc import _build_doc
 
-# Use different version of SymbolBase
-# When possible, use cython to speedup part of computation.
-# pylint: disable=unused-import
-try:
-    if int(_os.environ.get("MXNET_ENABLE_CYTHON", True)) == 0:
-        from .._ctypes.ndarray import NDArrayBase
-        from .._ctypes.ndarray import CachedOp, _imperative_invoke
-    elif _sys.version_info >= (3, 0):
-        from .._cy3.ndarray import NDArrayBase, _imperative_invoke
-        from .._cy3.ndarray import CachedOp, _imperative_invoke
-    else:
-        from .._cy2.ndarray import NDArrayBase, _imperative_invoke
-        from .._cy2.ndarray import CachedOp, _imperative_invoke
-except ImportError:
-    if int(_os.environ.get("MXNET_ENFORCE_CYTHON", False)) != 0:
-        raise ImportError("Cython Module cannot be loaded but MXNET_ENFORCE_CYTHON=1")
-    from .._ctypes.ndarray import NDArrayBase, _imperative_invoke
-    from .._ctypes.ndarray import CachedOp, _imperative_invoke
-
-from ..base import mx_uint, check_call, _LIB, py_str, _init_op_module, _Null
-# pylint: enable=unused-import
+from ..base import mx_uint, check_call, _LIB, py_str, _init_op_module, _Null # pylint: disable=unused-import
 
 
-# pylint: disable=too-many-locals, invalid-name
-def _make_ndarray_function(handle, name):
-    """Create a NDArray function from the FunctionHandle."""
+def _generate_ndarray_function_code(handle, name, func_name, signature_only=False):
+    """Generate function for ndarray op by handle and function name."""
     real_name = ctypes.c_char_p()
     desc = ctypes.c_char_p()
     num_args = mx_uint()
@@ -71,10 +48,9 @@ def _make_ndarray_function(handle, name):
     narg = int(num_args.value)
     arg_names = [py_str(arg_names[i]) for i in range(narg)]
     arg_types = [py_str(arg_types[i]) for i in range(narg)]
-    func_name = name
     key_var_num_args = py_str(key_var_num_args.value)
     ret_type = py_str(ret_type.value) if ret_type.value is not None else ''
-    doc_str = _build_doc(func_name,
+    doc_str = _build_doc(name,
                          py_str(desc.value),
                          arg_names,
                          arg_types,
@@ -115,60 +91,78 @@ def _make_ndarray_function(handle, name):
     if arr_name:
         code.append("""
 def %s(*%s, **kwargs):"""%(func_name, arr_name))
-        code.append("""
+        if not signature_only:
+            code.append("""
     ndargs = []
     for i in {}:
         assert isinstance(i, NDArrayBase), \\
             "Positional arguments must have NDArray type, " \\
             "but got %s"%str(i)
         ndargs.append(i)""".format(arr_name))
-        if dtype_name is not None:
-            code.append("""
+            if dtype_name is not None:
+                code.append("""
     if '%s' in kwargs:
         kwargs['%s'] = np.dtype(kwargs['%s']).name"""%(
             dtype_name, dtype_name, dtype_name))
-        code.append("""
+            code.append("""
     _ = kwargs.pop('name', None)
     out = kwargs.pop('out', None)
     keys = list(kwargs.keys())
     vals = list(kwargs.values())""")
     else:
         code.append("""
-def %s(%s):
+def %s(%s):"""%(func_name, ', '.join(signature)))
+        if not signature_only:
+            code.append("""
     ndargs = []
     keys = list(kwargs.keys())
-    vals = list(kwargs.values())"""%(func_name, ', '.join(signature)))
-        # NDArray args
-        for name in ndarg_names: # pylint: disable=redefined-argument-from-local
-            code.append("""
+    vals = list(kwargs.values())""")
+            # NDArray args
+            for name in ndarg_names: # pylint: disable=redefined-argument-from-local
+                code.append("""
     if {name} is not None:
         assert isinstance({name}, NDArrayBase), \\
             "Argument {name} must have NDArray type, but got %s"%str({name})
         ndargs.append({name})""".format(name=name))
-        # kwargs
-        for name in kwarg_names: # pylint: disable=redefined-argument-from-local
-            code.append("""
+            # kwargs
+            for name in kwarg_names: # pylint: disable=redefined-argument-from-local
+                code.append("""
     if %s is not _Null:
         keys.append('%s')
         vals.append(%s)"""%(name, name, name))
-        # dtype
-        if dtype_name is not None:
-            code.append("""
+            # dtype
+            if dtype_name is not None:
+                code.append("""
     if %s is not _Null:
         keys.append('%s')
         vals.append(np.dtype(%s).name)"""%(dtype_name, dtype_name, dtype_name))
 
-    code.append("""
+    if not signature_only:
+        code.append("""
     return _imperative_invoke(%d, ndargs, keys, vals, out)"""%(
         handle.value))
+    else:
+        code.append("""
+    return (0,)""")
+
+    doc_str_lines = _os.linesep+''.join(['    '+s if s.strip() else s
+                                         for s in 'r"""{doc_str}"""'.format(doc_str=doc_str)
+                                         .splitlines(True)])
+    code.insert(1, doc_str_lines)
+    return ''.join(code), doc_str
+
+
+# pylint: disable=too-many-locals, invalid-name
+def _make_ndarray_function(handle, name, func_name):
+    """Create a NDArray function from the FunctionHandle."""
+    code, doc_str = _generate_ndarray_function_code(handle, name, func_name)
 
     local = {}
-    exec(''.join(code), None, local)  # pylint: disable=exec-used
+    exec(code, None, local)  # pylint: disable=exec-used
     ndarray_function = local[func_name]
     ndarray_function.__name__ = func_name
     ndarray_function.__doc__ = doc_str
     ndarray_function.__module__ = 'mxnet.ndarray'
     return ndarray_function
 
-
 _init_op_module('mxnet', 'ndarray', _make_ndarray_function)
diff --git a/python/mxnet/ndarray/sparse.py b/python/mxnet/ndarray/sparse.py
index 5dc1092..72ce04c 100644
--- a/python/mxnet/ndarray/sparse.py
+++ b/python/mxnet/ndarray/sparse.py
@@ -16,6 +16,7 @@
 # under the License.
 
 # coding: utf-8
+# pylint: disable=wildcard-import, unused-wildcard-import, too-many-lines
 """Sparse NDArray API of MXNet."""
 
 from __future__ import absolute_import
@@ -30,13 +31,9 @@ except ImportError:
 import ctypes
 import warnings
 
-import os as _os
-import sys as _sys
-
 __all__ = ["_ndarray_cls", "csr_matrix", "row_sparse_array",
            "BaseSparseNDArray", "CSRNDArray", "RowSparseNDArray"]
 
-# import operator
 import numpy as np
 from ..base import NotSupportedForSparseNDArray
 from ..base import _LIB, numeric_types
@@ -44,29 +41,18 @@ from ..base import c_array, mx_real_t, integer_types
 from ..base import mx_uint, NDArrayHandle, check_call
 from ..context import Context
 from . import _internal
-from .ndarray import _DTYPE_NP_TO_MX, _DTYPE_MX_TO_NP
-from .ndarray import _STORAGE_TYPE_STR_TO_ID
+from . import op
+try:
+    from .gen_sparse import * # pylint: disable=redefined-builtin
+except ImportError:
+    pass
+from ._internal import _set_ndarray_class
+from .ndarray import NDArray, _storage_type, _DTYPE_NP_TO_MX, _DTYPE_MX_TO_NP
+from .ndarray import _STORAGE_TYPE_STR_TO_ID, _STORAGE_TYPE_ROW_SPARSE, _STORAGE_TYPE_CSR
 from .ndarray import _STORAGE_TYPE_UNDEFINED, _STORAGE_TYPE_DEFAULT
-from .ndarray import _STORAGE_TYPE_ROW_SPARSE, _STORAGE_TYPE_CSR
-from .ndarray import NDArray, _storage_type
 from .ndarray import zeros as _zeros_ndarray
 from .ndarray import array as _array
-from . import op
 
-# When possible, use cython to speedup part of computation.
-# pylint: disable=unused-import, too-many-lines
-try:
-    if int(_os.environ.get("MXNET_ENABLE_CYTHON", True)) == 0:
-        from .._ctypes.ndarray import _set_ndarray_class
-    elif _sys.version_info >= (3, 0):
-        from .._cy3.ndarray import _set_ndarray_class
-    else:
-        from .._cy2.ndarray import _set_ndarray_class
-except ImportError:
-    if int(_os.environ.get("MXNET_ENFORCE_CYTHON", False)) != 0:
-        raise ImportError("Cython Module cannot be loaded but MXNET_ENFORCE_CYTHON=1")
-    from .._ctypes.ndarray import _set_ndarray_class
-# pylint: enable=unused-import
 
 try:
     import scipy.sparse as spsp
diff --git a/python/mxnet/symbol/__init__.py b/python/mxnet/symbol/__init__.py
index da395b7..a07025e 100644
--- a/python/mxnet/symbol/__init__.py
+++ b/python/mxnet/symbol/__init__.py
@@ -17,8 +17,13 @@
 
 """Symbol API of MXNet."""
 
-from . import _internal, contrib, linalg, sparse, random
+from . import _internal, contrib, linalg, op, random, sparse
 # pylint: disable=wildcard-import, redefined-builtin
+try:
+    from .gen_op import * # pylint: disable=unused-wildcard-import
+except ImportError:
+    pass
+from . import register
 from .op import *
 from .symbol import *
 # pylint: enable=wildcard-import
diff --git a/python/mxnet/symbol/_internal.py b/python/mxnet/symbol/_internal.py
index 25b3b60..53fc684 100644
--- a/python/mxnet/symbol/_internal.py
+++ b/python/mxnet/symbol/_internal.py
@@ -15,5 +15,36 @@
 # specific language governing permissions and limitations
 # under the License.
 
+# pylint: disable=wildcard-import, unused-import
 """Symbol namespace used to register internal functions."""
-__all__ = []
+# Use different version of SymbolBase
+# When possible, use cython to speedup part of computation.
+import sys as _sys
+import os as _os
+
+import numpy as np
+
+try:
+    if int(_os.environ.get("MXNET_ENABLE_CYTHON", True)) == 0:
+        from .._ctypes.symbol import SymbolBase, _set_symbol_class
+        from .._ctypes.symbol import _symbol_creator
+    elif _sys.version_info >= (3, 0):
+        from .._cy3.symbol import SymbolBase, _set_symbol_class
+        from .._cy3.symbol import _symbol_creator
+    else:
+        from .._cy2.symbol import SymbolBase, _set_symbol_class
+        from .._cy2.symbol import _symbol_creator
+except ImportError:
+    if int(_os.environ.get("MXNET_ENFORCE_CYTHON", False)) != 0:
+        raise ImportError("Cython Module cannot be loaded but MXNET_ENFORCE_CYTHON=1")
+    from .._ctypes.symbol import SymbolBase, _set_symbol_class
+    from .._ctypes.symbol import _symbol_creator
+from ..attribute import AttrScope
+from ..base import _Null
+from ..name import NameManager
+try:
+    from .gen__internal import * # pylint: disable=unused-wildcard-import
+except ImportError:
+    pass
+
+__all__ = ['SymbolBase', '_set_symbol_class', '_symbol_creator']
diff --git a/python/mxnet/symbol/contrib.py b/python/mxnet/symbol/contrib.py
index ff5aa37..13feb78 100644
--- a/python/mxnet/symbol/contrib.py
+++ b/python/mxnet/symbol/contrib.py
@@ -15,5 +15,12 @@
 # specific language governing permissions and limitations
 # under the License.
 
+# coding: utf-8
+# pylint: disable=wildcard-import, unused-wildcard-import
 """Contrib Symbol API of MXNet."""
+try:
+    from .gen_contrib import *
+except ImportError:
+    pass
+
 __all__ = []
diff --git a/python/mxnet/symbol/linalg.py b/python/mxnet/symbol/linalg.py
index 58514ba..2bf543e 100644
--- a/python/mxnet/symbol/linalg.py
+++ b/python/mxnet/symbol/linalg.py
@@ -15,5 +15,12 @@
 # specific language governing permissions and limitations
 # under the License.
 
+# coding: utf-8
+# pylint: disable=wildcard-import, unused-wildcard-import
 """Linear Algebra Symbol API of MXNet."""
+try:
+    from .gen_linalg import *
+except ImportError:
+    pass
+
 __all__ = []
diff --git a/python/mxnet/symbol/op.py b/python/mxnet/symbol/op.py
index 9353a73..2d63dfc 100644
--- a/python/mxnet/symbol/op.py
+++ b/python/mxnet/symbol/op.py
@@ -15,193 +15,12 @@
 # specific language governing permissions and limitations
 # under the License.
 
-"""Register backend ops in mxnet.symbol namespace."""
-__all__ = []
-
-import sys as _sys
-import os as _os
-import ctypes
-import numpy as _numpy  # pylint: disable=unused-import
-
-from ..base import mx_uint, check_call, _LIB, py_str
-from ..symbol_doc import _build_doc
-
-# Use different version of SymbolBase
-# When possible, use cython to speedup part of computation.
-# pylint: disable=unused-import
+# coding: utf-8
+# pylint: disable=wildcard-import, unused-wildcard-import, redefined-builtin
+"""Backend ops in mxnet.symbol namespace."""
 try:
-    if int(_os.environ.get("MXNET_ENABLE_CYTHON", True)) == 0:
-        from .._ctypes.symbol import SymbolBase, _set_symbol_class
-        from .._ctypes.symbol import _symbol_creator
-    elif _sys.version_info >= (3, 0):
-        from .._cy3.symbol import SymbolBase, _set_symbol_class
-        from .._cy3.symbol import _symbol_creator
-    else:
-        from .._cy2.symbol import SymbolBase, _set_symbol_class
-        from .._cy2.symbol import _symbol_creator
+    from .gen_op import *
 except ImportError:
-    if int(_os.environ.get("MXNET_ENFORCE_CYTHON", False)) != 0:
-        raise ImportError("Cython Module cannot be loaded but MXNET_ENFORCE_CYTHON=1")
-    from .._ctypes.symbol import SymbolBase, _set_symbol_class
-    from .._ctypes.symbol import _symbol_creator
-
-from ..base import _Null, _init_op_module
-from ..name import NameManager
-from ..attribute import AttrScope
-# pylint: enable=unused-import
-
-
-def _make_atomic_symbol_function(handle, name):
-    """Create an atomic symbol function by handle and function name."""
-    real_name = ctypes.c_char_p()
-    desc = ctypes.c_char_p()
-    num_args = mx_uint()
-    arg_names = ctypes.POINTER(ctypes.c_char_p)()
-    arg_types = ctypes.POINTER(ctypes.c_char_p)()
-    arg_descs = ctypes.POINTER(ctypes.c_char_p)()
-    key_var_num_args = ctypes.c_char_p()
-    ret_type = ctypes.c_char_p()
-
-    check_call(_LIB.MXSymbolGetAtomicSymbolInfo(
-        handle, ctypes.byref(real_name), ctypes.byref(desc),
-        ctypes.byref(num_args),
-        ctypes.byref(arg_names),
-        ctypes.byref(arg_types),
-        ctypes.byref(arg_descs),
-        ctypes.byref(key_var_num_args),
-        ctypes.byref(ret_type)))
-    narg = int(num_args.value)
-    arg_names = [py_str(arg_names[i]) for i in range(narg)]
-    arg_types = [py_str(arg_types[i]) for i in range(narg)]
-    func_name = name
-    key_var_num_args = py_str(key_var_num_args.value)
-    ret_type = py_str(ret_type.value) if ret_type.value is not None else ''
-    doc_str = _build_doc(func_name,
-                         py_str(desc.value),
-                         arg_names,
-                         arg_types,
-                         [py_str(arg_descs[i]) for i in range(narg)],
-                         key_var_num_args,
-                         ret_type)
+    pass
 
-    dtype_name = None
-    arr_name = None
-    ndsignature = []
-    signature = []
-    ndarg_names = []
-    kwarg_names = []
-    for i in range(narg):
-        name, atype = arg_names[i], arg_types[i]
-        if name == 'dtype':
-            dtype_name = name
-            signature.append('%s=_Null'%name)
-        elif atype.startswith('NDArray') or atype.startswith('Symbol'):
-            assert not arr_name, \
-                "Op can only have one argument with variable " \
-                "size and it must be the last argument."
-            if atype.endswith('[]'):
-                ndsignature.append('*%s'%name)
-                arr_name = name
-            else:
-                ndsignature.append('%s=None'%name)
-                ndarg_names.append(name)
-        else:
-            signature.append('%s=_Null'%name)
-            kwarg_names.append(name)
-    #signature.append('is_train=False')
-    signature.append('name=None')
-    signature.append('attr=None')
-    signature.append('out=None')
-    signature.append('**kwargs')
-    signature = ndsignature + signature
-
-    code = []
-    if arr_name:
-        code.append("""
-def %s(*%s, **kwargs):"""%(func_name, arr_name))
-        code.append("""
-    sym_args = []
-    for i in {}:
-        assert isinstance(i, SymbolBase), \\
-            "Positional arguments must be Symbol instances, " \\
-            "but got %s"%str(i)
-        sym_args.append(i)""".format(arr_name))
-        if dtype_name is not None:
-            code.append("""
-    if '%s' in kwargs:
-        kwargs['%s'] = _numpy.dtype(kwargs['%s']).name"""%(
-            dtype_name, dtype_name, dtype_name))
-        code.append("""
-    attr = kwargs.pop('attr', None)
-    kwargs.update(AttrScope.current.get(attr))
-    name = kwargs.pop('name', None)
-    name = NameManager.current.get(name, '%s')
-    _ = kwargs.pop('out', None)
-    keys = []
-    vals = []
-    sym_kwargs = dict()
-    for k, v in kwargs.items():
-        if isinstance(v, SymbolBase):
-            sym_kwargs[k] = v
-        else:
-            keys.append(k)
-            vals.append(v)"""%(func_name.lower()))
-        if key_var_num_args:
-            code.append("""
-    if '%s' not in kwargs:
-        keys.append('%s')
-        vals.append(len(sym_args) + len(sym_kwargs))"""%(
-            key_var_num_args, key_var_num_args))
-
-        code.append("""
-    return _symbol_creator(%d, sym_args, sym_kwargs, keys, vals, name)"""%(
-        handle.value))
-    else:
-        code.append("""
-def %s(%s):
-    kwargs.update(AttrScope.current.get(attr))
-    sym_kwargs = dict()
-    keys = []
-    vals = []"""%(func_name, ', '.join(signature)))
-        code.append("""
-    for k, v in kwargs.items():
-        if isinstance(v, SymbolBase):
-            sym_kwargs[k] = v
-        else:
-            keys.append(k)
-            vals.append(v)""")
-        # NDArray args
-        for name in ndarg_names: # pylint: disable=redefined-argument-from-local
-            code.append("""
-    if {name} is not None:
-        assert isinstance({name}, SymbolBase), \\
-            "Argument {name} must be Symbol instances, but got %s"%str({name})
-        sym_kwargs['{name}'] = {name}""".format(name=name))
-        # kwargs
-        for name in kwarg_names: # pylint: disable=redefined-argument-from-local
-            code.append("""
-    if %s is not _Null:
-        keys.append('%s')
-        vals.append(%s)"""%(name, name, name))
-        # dtype
-        if dtype_name is not None:
-            code.append("""
-    if %s is not _Null:
-        keys.append('%s')
-        vals.append(_numpy.dtype(%s).name)"""%(dtype_name, dtype_name, dtype_name))
-
-        code.append("""
-    name = NameManager.current.get(name, '%s')
-    return _symbol_creator(%d, None, sym_kwargs, keys, vals, name)"""%(
-        func_name.lower(), handle.value))
-
-    local = {}
-    exec(''.join(code), None, local)  # pylint: disable=exec-used
-    symbol_function = local[func_name]
-    symbol_function.__name__ = func_name
-    symbol_function.__doc__ = doc_str
-    symbol_function.__module__ = 'mxnet.symbol'
-    return symbol_function
-
-
-_init_op_module('mxnet', 'symbol', _make_atomic_symbol_function)
+__all__ = []
diff --git a/python/mxnet/symbol/op.py b/python/mxnet/symbol/register.py
similarity index 75%
copy from python/mxnet/symbol/op.py
copy to python/mxnet/symbol/register.py
index 9353a73..cf279d1 100644
--- a/python/mxnet/symbol/op.py
+++ b/python/mxnet/symbol/register.py
@@ -15,44 +15,24 @@
 # specific language governing permissions and limitations
 # under the License.
 
+# pylint: disable=unused-import
 """Register backend ops in mxnet.symbol namespace."""
-__all__ = []
-
-import sys as _sys
 import os as _os
 import ctypes
-import numpy as _numpy  # pylint: disable=unused-import
+import numpy as np
 
+from . import _internal
+from ._internal import SymbolBase, _symbol_creator
+from ..attribute import AttrScope
 from ..base import mx_uint, check_call, _LIB, py_str
 from ..symbol_doc import _build_doc
-
-# Use different version of SymbolBase
-# When possible, use cython to speedup part of computation.
-# pylint: disable=unused-import
-try:
-    if int(_os.environ.get("MXNET_ENABLE_CYTHON", True)) == 0:
-        from .._ctypes.symbol import SymbolBase, _set_symbol_class
-        from .._ctypes.symbol import _symbol_creator
-    elif _sys.version_info >= (3, 0):
-        from .._cy3.symbol import SymbolBase, _set_symbol_class
-        from .._cy3.symbol import _symbol_creator
-    else:
-        from .._cy2.symbol import SymbolBase, _set_symbol_class
-        from .._cy2.symbol import _symbol_creator
-except ImportError:
-    if int(_os.environ.get("MXNET_ENFORCE_CYTHON", False)) != 0:
-        raise ImportError("Cython Module cannot be loaded but MXNET_ENFORCE_CYTHON=1")
-    from .._ctypes.symbol import SymbolBase, _set_symbol_class
-    from .._ctypes.symbol import _symbol_creator
-
 from ..base import _Null, _init_op_module
 from ..name import NameManager
-from ..attribute import AttrScope
 # pylint: enable=unused-import
 
 
-def _make_atomic_symbol_function(handle, name):
-    """Create an atomic symbol function by handle and function name."""
+def _generate_symbol_function_code(handle, name, func_name, signature_only=False):
+    """Generate function for symbol op by handle and function name."""
     real_name = ctypes.c_char_p()
     desc = ctypes.c_char_p()
     num_args = mx_uint()
@@ -73,10 +53,9 @@ def _make_atomic_symbol_function(handle, name):
     narg = int(num_args.value)
     arg_names = [py_str(arg_names[i]) for i in range(narg)]
     arg_types = [py_str(arg_types[i]) for i in range(narg)]
-    func_name = name
     key_var_num_args = py_str(key_var_num_args.value)
     ret_type = py_str(ret_type.value) if ret_type.value is not None else ''
-    doc_str = _build_doc(func_name,
+    doc_str = _build_doc(name,
                          py_str(desc.value),
                          arg_names,
                          arg_types,
@@ -119,19 +98,20 @@ def _make_atomic_symbol_function(handle, name):
     if arr_name:
         code.append("""
 def %s(*%s, **kwargs):"""%(func_name, arr_name))
-        code.append("""
+        if not signature_only:
+            code.append("""
     sym_args = []
     for i in {}:
         assert isinstance(i, SymbolBase), \\
             "Positional arguments must be Symbol instances, " \\
             "but got %s"%str(i)
         sym_args.append(i)""".format(arr_name))
-        if dtype_name is not None:
-            code.append("""
+            if dtype_name is not None:
+                code.append("""
     if '%s' in kwargs:
-        kwargs['%s'] = _numpy.dtype(kwargs['%s']).name"""%(
+        kwargs['%s'] = np.dtype(kwargs['%s']).name"""%(
             dtype_name, dtype_name, dtype_name))
-        code.append("""
+            code.append("""
     attr = kwargs.pop('attr', None)
     kwargs.update(AttrScope.current.get(attr))
     name = kwargs.pop('name', None)
@@ -146,62 +126,77 @@ def %s(*%s, **kwargs):"""%(func_name, arr_name))
         else:
             keys.append(k)
             vals.append(v)"""%(func_name.lower()))
-        if key_var_num_args:
-            code.append("""
+            if key_var_num_args:
+                code.append("""
     if '%s' not in kwargs:
         keys.append('%s')
         vals.append(len(sym_args) + len(sym_kwargs))"""%(
             key_var_num_args, key_var_num_args))
 
-        code.append("""
+            code.append("""
     return _symbol_creator(%d, sym_args, sym_kwargs, keys, vals, name)"""%(
         handle.value))
     else:
         code.append("""
-def %s(%s):
+def %s(%s):"""%(func_name, ', '.join(signature)))
+        if not signature_only:
+            code.append("""
     kwargs.update(AttrScope.current.get(attr))
     sym_kwargs = dict()
     keys = []
-    vals = []"""%(func_name, ', '.join(signature)))
-        code.append("""
+    vals = []
     for k, v in kwargs.items():
         if isinstance(v, SymbolBase):
             sym_kwargs[k] = v
         else:
             keys.append(k)
             vals.append(v)""")
-        # NDArray args
-        for name in ndarg_names: # pylint: disable=redefined-argument-from-local
-            code.append("""
+            # NDArray args
+            for name in ndarg_names: # pylint: disable=redefined-argument-from-local
+                code.append("""
     if {name} is not None:
         assert isinstance({name}, SymbolBase), \\
             "Argument {name} must be Symbol instances, but got %s"%str({name})
         sym_kwargs['{name}'] = {name}""".format(name=name))
-        # kwargs
-        for name in kwarg_names: # pylint: disable=redefined-argument-from-local
-            code.append("""
+            # kwargs
+            for name in kwarg_names: # pylint: disable=redefined-argument-from-local
+                code.append("""
     if %s is not _Null:
         keys.append('%s')
         vals.append(%s)"""%(name, name, name))
-        # dtype
-        if dtype_name is not None:
-            code.append("""
+            # dtype
+            if dtype_name is not None:
+                code.append("""
     if %s is not _Null:
         keys.append('%s')
-        vals.append(_numpy.dtype(%s).name)"""%(dtype_name, dtype_name, dtype_name))
+        vals.append(np.dtype(%s).name)"""%(dtype_name, dtype_name, dtype_name))
 
-        code.append("""
+            code.append("""
     name = NameManager.current.get(name, '%s')
     return _symbol_creator(%d, None, sym_kwargs, keys, vals, name)"""%(
         func_name.lower(), handle.value))
 
+    if signature_only:
+        code.append("""
+    return (0,)""")
+
+    doc_str_lines = _os.linesep+''.join(['    '+s if s.strip() else s
+                                         for s in 'r"""{doc_str}"""'.format(doc_str=doc_str)
+                                         .splitlines(True)])
+    code.insert(1, doc_str_lines)
+    return ''.join(code), doc_str
+
+
+def _make_symbol_function(handle, name, func_name):
+    """Create a symbol function by handle and function name."""
+    code, doc_str = _generate_symbol_function_code(handle, name, func_name)
+
     local = {}
-    exec(''.join(code), None, local)  # pylint: disable=exec-used
+    exec(code, None, local)  # pylint: disable=exec-used
     symbol_function = local[func_name]
     symbol_function.__name__ = func_name
     symbol_function.__doc__ = doc_str
     symbol_function.__module__ = 'mxnet.symbol'
     return symbol_function
 
-
-_init_op_module('mxnet', 'symbol', _make_atomic_symbol_function)
+_init_op_module('mxnet', 'symbol', _make_symbol_function)
diff --git a/python/mxnet/symbol/sparse.py b/python/mxnet/symbol/sparse.py
index 3bfcf4a..66f4dbe 100644
--- a/python/mxnet/symbol/sparse.py
+++ b/python/mxnet/symbol/sparse.py
@@ -15,5 +15,12 @@
 # specific language governing permissions and limitations
 # under the License.
 
+# coding: utf-8
+# pylint: disable=wildcard-import, unused-wildcard-import
 """Sparse Symbol API of MXNet."""
+try:
+    from .gen_sparse import * # pylint: disable=redefined-builtin
+except ImportError:
+    pass
+
 __all__ = []
diff --git a/python/mxnet/symbol/symbol.py b/python/mxnet/symbol/symbol.py
index 394f8b6..3c76826 100644
--- a/python/mxnet/symbol/symbol.py
+++ b/python/mxnet/symbol/symbol.py
@@ -31,6 +31,7 @@ from numbers import Number
 
 import numpy as _numpy
 
+from ..attribute import AttrScope
 from ..base import _LIB, numeric_types
 from ..base import c_array, c_str, mx_uint, py_str, string_types
 from ..base import NDArrayHandle, ExecutorHandle, SymbolHandle
@@ -42,7 +43,7 @@ from ..ndarray import _ndarray_cls
 from ..executor import Executor
 from . import _internal
 from . import op
-from .op import SymbolBase, _set_symbol_class, AttrScope, _Null  # pylint: disable=unused-import
+from ._internal import SymbolBase, _set_symbol_class
 
 __all__ = ["Symbol", "var", "Variable", "Group", "load", "load_json",
            "pow", "maximum", "minimum", "hypot", "zeros", "ones", "full", "arange"]
diff --git a/python/setup.py b/python/setup.py
index 14c8121..1e93ac0 100644
--- a/python/setup.py
+++ b/python/setup.py
@@ -22,15 +22,31 @@ import os
 import sys
 # need to use distutils.core for correct placement of cython dll
 kwargs = {}
+required_packages = ['numpy', 'requests', 'graphviz']
 if "--inplace" in sys.argv:
     from distutils.core import setup
     from distutils.extension import Extension
+    from distutils.command.install import install
 else:
     from setuptools import setup
     from setuptools.extension import Extension
-    kwargs = {'install_requires': ['numpy', 'requests', 'graphviz'], 'zip_safe': False}
+    kwargs = {'install_requires': required_packages,
+              'setup_requires': required_packages,
+              'tests_require': required_packages,
+              'zip_safe': False}
+    from setuptools.command.install import install
 from setuptools import find_packages
 
+class PostInstallCommand(install):
+    """Post-installation for installation mode."""
+    def run(self):
+        install.run(self)
+        from mxnet.base import _generate_op_module_signature
+        from mxnet.ndarray.register import _generate_ndarray_function_code
+        from mxnet.symbol.register import _generate_symbol_function_code
+        _generate_op_module_signature('mxnet', 'symbol', _generate_symbol_function_code)
+        _generate_op_module_signature('mxnet', 'ndarray', _generate_ndarray_function_code)
+
 with_cython = False
 if '--with-cython' in sys.argv:
     with_cython = True
@@ -87,7 +103,6 @@ def config_cython():
         print("WARNING: Cython is not installed, will compile without cython module")
         return []
 
-
 setup(name='mxnet',
       version=__version__,
       description=open(os.path.join(CURRENT_DIR, 'README.md')).read(),
@@ -95,4 +110,5 @@ setup(name='mxnet',
       data_files=[('mxnet', [LIB_PATH[0]])],
       url='https://github.com/dmlc/mxnet',
       ext_modules=config_cython(),
+      cmdclass={'install': PostInstallCommand},
       **kwargs)

-- 
To stop receiving notification emails like this one, please contact
['"commits@mxnet.apache.org" <co...@mxnet.apache.org>'].