private static boolean registerModule(
      Path pythonModuleRoot, String pythonModuleName, final String pythonClassName) {

    String pythonModuleRelPath = pythonModuleName.replace('.', '/');

    Path pythonModuleFile = pythonModuleRoot.resolve(pythonModuleRelPath + ".py");
    if (!Files.exists(pythonModuleFile)) {
      LOG.severe(String.format("Missing Python module '%s'", pythonModuleFile.toUri()));
      return false;
    }

    Path pythonInfoXmlFile = pythonModuleRoot.resolve(pythonModuleRelPath + "-info.xml");
    if (!Files.exists(pythonInfoXmlFile)) {
      LOG.warning(
          String.format(
              "Missing operator metadata file '%s'. Using defaults.", pythonInfoXmlFile.toUri()));
    }

    DefaultOperatorDescriptor operatorDescriptor =
        createOperatorDescriptor(pythonInfoXmlFile, pythonModuleName);
    File pythonModuleRootFile = FileUtils.toFile(pythonModuleRoot);

    PyOperatorSpi operatorSpi =
        new PyOperatorSpi(operatorDescriptor) {

          @Override
          public Operator createOperator() throws OperatorException {
            PyOperator pyOperator = (PyOperator) super.createOperator();

            pyOperator.setParameterDefaultValues();
            pyOperator.setPythonModulePath(pythonModuleRootFile.getPath());
            pyOperator.setPythonModuleName(pythonModuleName);
            pyOperator.setPythonClassName(pythonClassName);
            return pyOperator;
          }
        };

    String operatorName =
        operatorDescriptor.getAlias() != null
            ? operatorDescriptor.getAlias()
            : operatorDescriptor.getName();
    GPF.getDefaultInstance().getOperatorSpiRegistry().addOperatorSpi(operatorName, operatorSpi);
    LOG.info(
        String.format(
            "Python operator '%s' registered (Python module: '%s', class: '%s', root: '%s')",
            operatorName, pythonModuleName, pythonClassName, pythonModuleRootFile));
    return true;
  }
 private static DefaultOperatorDescriptor createOperatorDescriptor(
     Path pythonInfoXmlFile, String pythonModuleName) {
   DefaultOperatorDescriptor operatorDescriptor;
   if (Files.exists(pythonInfoXmlFile)) {
     try {
       try (BufferedReader reader = Files.newBufferedReader(pythonInfoXmlFile)) {
         operatorDescriptor =
             DefaultOperatorDescriptor.fromXml(
                 reader,
                 pythonInfoXmlFile.toUri().toString(),
                 PyOperatorSpi.class.getClassLoader());
       }
     } catch (IOException e) {
       LOG.severe(String.format("Failed to read from '%s'", pythonInfoXmlFile));
       operatorDescriptor = null;
     }
   } else {
     operatorDescriptor = new DefaultOperatorDescriptor(pythonModuleName, PyOperator.class);
   }
   return operatorDescriptor;
 }