code: def gen_module(schema, algo):
      """
      Ideally we should be able to avoid logic specific to algos in this file.
      Instead, customizations are externalized in ./python/gen_{algo}.py files.
      Logic that is specific to python types (e.g. H2OFrame, enums as list...) should however stay here
      as the type translation is done in this file.
      """
      classname = algo_to_classname(algo)
      extra_imports = get_customizations_for(
          algo, "extensions.__imports__"
      )
      class_doc = get_customizations_for(algo, "doc.__class__")
      class_examples = get_customizations_for(
          algo, "examples.__class__"
      )
      class_extras = get_customizations_for(
          algo, "extensions.__class__"
      )
      module_extras = get_customizations_for(
          algo, "extensions.__module__"
      )
  
      update_param_defaults = get_customizations_for(
          "defaults", "update_param"
      )
      update_param = get_customizations_for(algo, "update_param")
      deprecated_params = get_customizations_for(
          algo, "deprecated_params", {}
      )
  
      def extend_schema_params(param):
          pname = param.get("name")
          param = deepcopy(param)
          updates = None
          for update_fn in [update_param, update_param_defaults]:
              if callable(update_fn):
                  updates = update_fn(pname, param)
              if updates is not None:
                  param = updates
                  break
          # return param if isinstance(param, (list, tuple)) else [param]  # always return array to support deprecated aliases
          return param
  
      extended_params = [
          extend_schema_params(p) for p in schema["parameters"]
      ]
  
      param_names = []
      for param in extended_params:
          pname = param.get("name")
          ptype = param.get("type")
          pvalues = param.get("values")
          pdefault = param.get("default_value")
  
          assert (ptype[:4] == "enum") == bool(
              pvalues
          ), "Values are expected for enum types only"
          if pvalues:
              enum_values = [
                  normalize_enum_constant(p) for p in pvalues
              ]
              if pdefault:
                  pdefault = normalize_enum_constant(pdefault)
          else:
              enum_values = None
  
          if pname in reserved_words:
              pname += "_"
          param_names.append(pname)
          param["pname"] = pname
          param["default_value"] = pdefault
          param["ptype"] = translate_type_for_check(ptype, enum_values)
          param["dtype"] = translate_type_for_doc(ptype, enum_values)
  
      if deprecated_params:
          extended_params = [
              p
              for p in extended_params
              if p["pname"] not in deprecated_params.keys()
          ]
  
      yield "#!/usr/bin/env python"
      yield "# -*- encoding: utf-8 -*-"
      yield "#"
      yield "# This file is auto-generated by h2o-3/h2o-bindings/bin/gen_python.py"
      yield "# Copyright 2016 H2O.ai;  Apache License Version 2.0 (see LICENSE for details)"
      yield "#"
      yield ""
      if deprecated_params:
          yield "from h2o.utils.metaclass import deprecated_params, deprecated_property"
      if extra_imports:
          yield reformat_block(extra_imports)
      yield "from h2o.estimators.estimator_base import H2OEstimator"
      yield "from h2o.exceptions import H2OValueError"
      yield "from h2o.frame import H2OFrame"
      yield "from h2o.utils.typechecks import assert_is_type, Enum, numeric"
      yield ""
      yield ""
      yield "class %s(H2OEstimator):" % classname
      yield '    """'
      yield "    " + schema["algo_full_name"]
      yield ""
      if class_doc:
          yield reformat_block(class_doc, 4)
      if class_examples:
          yield ""
          yield "    :examples:"
          yield ""
          yield reformat_block(class_examples, 4)
      yield '    """'
      yield ""
      yield '    algo = "%s"' % algo
      yield "    supervised_learning = %s" % get_customizations_for(
          algo, "supervised_learning", True
      )
      options = get_customizations_for(algo, "options")
      if options:
          yield "    _options_ = %s" % reformat_block(
              pformat(options), prefix=" " * 16, prefix_first=False
          )
      yield ""
      if deprecated_params:
          yield reformat_block(
              "@deprecated_params(%s)" % deprecated_params, indent=4
          )
      init_sig = "def __init__(self,\n%s\n):" % "\n".join(
          "%s=%s,  # type: %s"
          % (
              name,
              default,
              "Optional[%s]" % type if default is None else type,
          )
          for name, default, type in [
              (
                  p.get("pname"),
                  stringify(p.get("default_value"), infinity=None),
                  p.get("dtype"),
              )
              for p in extended_params
          ]
      )
      yield reformat_block(
          init_sig, indent=4, prefix=" " * 13, prefix_first=False
      )
      yield '        """'
      for p in extended_params:
          pname, pdefault, dtype, pdoc = (
              p.get("pname"),
              stringify(p.get("default_value")),
              p.get("dtype"),
              p.get("help"),
          )
          pdesc = "%s: %s\nDefaults to ``%s``." % (
              pname,
              pdoc,
              pdefault,
          )
          pident = " " * 15
          yield "        :param %s" % bi.wrap(
              pdesc, indent=pident, indent_first=False
          )
          yield "        :type %s: %s%s" % (
              pname,
              bi.wrap(dtype, indent=pident, indent_first=False),
              ", optional" if pdefault is None else "",
          )
      yield '        """'
      yield "        super(%s, self).__init__()" % classname
      yield "        self._parms = {}"
      for p in extended_params:
          pname = p.get("pname")
          if pname == "model_id":
              yield "        self._id = self._parms['model_id'] = model_id"
          else:
              yield "        self.%s = %s" % (pname, pname)
      rest_api_version = get_customizations_for(
          algo, "rest_api_version"
      )
      if rest_api_version:
          yield '        self._parms["_rest_version"] = %s' % rest_api_version
      yield ""
      for param in extended_params:
          pname = param.get("pname")
          if pname == "model_id":
              continue  # The getter is already defined in ModelBase
          sname = pname[:-1] if pname[-1] == "_" else pname
          ptype = param.get("ptype")
          dtype = param.get("dtype")
          pdefault = param.get("default_value")
  
          if dtype.startswith("Enum"):
              vals = dtype[5:-1].split(", ")
              property_doc = "One of: " + ", ".join(
                  "``%s``" % v for v in vals
              )
          else:
              property_doc = "Type: ``%s``" % dtype
          property_doc += (
              "."
              if pdefault is None
              else ", defaults to ``%s``." % stringify(pdefault)
          )
  
          yield "    @property"
          yield "    def %s(self):" % pname
          yield '        """'
          yield bi.wrap(
              param.get("help"), indent=8 * " "
          )  # we need to wrap only for text coming from server
          yield ""
          yield bi.wrap(property_doc, indent=8 * " ")
          custom_property_doc = get_customizations_for(
              algo, "doc.{}".format(pname)
          )
          if custom_property_doc:
              yield ""
              yield reformat_block(custom_property_doc, 8)
          property_examples = get_customizations_for(
              algo, "examples.{}".format(pname)
          )
          if property_examples:
              yield ""
              yield "        :examples:"
              yield ""
              yield reformat_block(property_examples, 8)
          yield '        """'
          property_getter = get_customizations_or_defaults_for(
              algo, "overrides.{}.getter".format(pname)
          )  # check gen_stackedensemble.py for an example
          if property_getter:
              yield reformat_block(
                  property_getter.format(**locals()), 8
              )
          else:
              yield '        return self._parms.get("%s")' % sname
  
          yield ""
          yield "    @%s.setter" % pname
          yield "    def %s(self, %s):" % (pname, pname)
          property_setter = get_customizations_or_defaults_for(
              algo, "overrides.{}.setter".format(pname)
          )  # check gen_stackedensemble.py for an example
          if property_setter:
              yield reformat_block(
                  property_setter.format(**locals()), 8
              )
          elif "H2OFrame" in ptype:
              yield "        self._parms[\"%s\"] = H2OFrame._validate(%s, '%s')" % (
                  sname,
                  pname,
                  pname,
              )
          else:
              yield "        assert_is_type(%s, None, %s)" % (
                  pname,
                  ptype,
              )
              yield '        self._parms["%s"] = %s' % (sname, pname)
          yield ""
  
      for old, new in deprecated_params.items():
          new_name = new[0] if isinstance(new, tuple) else new
          yield "    %s = deprecated_property('%s', %s)" % (
              old,
              old,
              new,
          )
  
      yield ""
      if class_extras:
          yield reformat_block(code_as_str(class_extras), 4)
      if module_extras:
          yield ""
          yield reformat_block(code_as_str(module_extras))
  
imports: from copy import deepcopy
  from functools import partial
  from io import StringIO
  from inspect import getsource
  from pprint import pformat
  import sys
  import bindings
  from custom import get_customizations_for, reformat_block
label_desc: Use image node_id 7 for calling a global function with example usage: gen_module(schema, algo) and returns: param