#257461: Force UTF-8 encoding in setup.py if getting properties failed
authorJulien Enselme <jenselme@netbeans.org>
Sun, 25 Sep 2016 19:46:32 +0200
changeset 183837e51032433e1
parent 18382 b36f70414d5e
child 18384 9339958c5c47
#257461: Force UTF-8 encoding in setup.py if getting properties failed

This can happen when opening Python 3 projects since encoding is UTF-8 by
default, which means setup.py may not contain the the encoding line.

To do this, when getting the properties, we:
- copy the setup.py file
- append the encoding line at the beginning
- get the properties and detele the file
python.project2/src/org/netbeans/modules/python/project2/PythonProject2.java
     1.1 --- a/python.project2/src/org/netbeans/modules/python/project2/PythonProject2.java	Sun Sep 25 16:46:29 2016 +0200
     1.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/PythonProject2.java	Sun Sep 25 19:46:32 2016 +0200
     1.3 @@ -3,6 +3,13 @@
     1.4  import java.beans.PropertyChangeListener;
     1.5  import java.beans.PropertyChangeSupport;
     1.6  import java.io.IOException;
     1.7 +import java.io.Reader;
     1.8 +import java.nio.charset.Charset;
     1.9 +import java.nio.file.Files;
    1.10 +import java.nio.file.Path;
    1.11 +import java.nio.file.Paths;
    1.12 +import java.nio.file.StandardOpenOption;
    1.13 +import java.util.List;
    1.14  import java.util.Properties;
    1.15  import java.util.Scanner;
    1.16  import java.util.concurrent.ExecutionException;
    1.17 @@ -57,6 +64,8 @@
    1.18       */
    1.19      public static final String PROP_PROJECT = "MavenProject"; //NOI18N
    1.20  
    1.21 +    public static final String TEMPORARY_SETUPPY = "tmp_ENCODING_APPENDED_AT_BEGINNING_FOR_PYTHON_2_setup.py"; //NOI18N
    1.22 +
    1.23      private static final String NS_PYTHON_1 = "http://nbpython.dev.java.net/ns/php-project/1"; // NOI18N
    1.24      private static final String EL_PYTHON = "python-data"; // NOI18N
    1.25      private static final ImageIcon PROJECT_ICON = ImageUtilities.loadImageIcon("org/netbeans/modules/python/project/resources/py_25_16.png", false);
    1.26 @@ -120,7 +129,7 @@
    1.27              state
    1.28          });
    1.29      }
    1.30 -    
    1.31 +
    1.32      public void addPropertyChangeListener(PropertyChangeListener propertyChangeListener) {
    1.33          support.addPropertyChangeListener(propertyChangeListener);
    1.34      }
    1.35 @@ -208,50 +217,80 @@
    1.36              }
    1.37          });
    1.38      }
    1.39 -    
    1.40 +
    1.41      private static Properties findProjectProperties(FileObject projectDirectory, FileChangeListener listener) throws PythonException {
    1.42          Properties props = new Properties();
    1.43 -        final PythonPlatformManager manager = PythonPlatformManager.getInstance();
    1.44 -        PythonPlatform platform = manager.getPlatform(manager.getDefaultPlatform());
    1.45          PythonExecution pye;
    1.46          try {
    1.47 -            pye = new PythonExecution();
    1.48 -            pye.setCommand(platform.getInterpreterCommand());
    1.49 -            pye.setDisplayName("Python Project Info");
    1.50              FileObject setuppy = projectDirectory.getFileObject(SETUPPY);
    1.51              if(listener != null) {
    1.52                  setuppy.addFileChangeListener(listener);
    1.53              }
    1.54 -            pye.setScript(FileUtil.toFile(setuppy).getAbsolutePath());
    1.55 -            pye.setScriptArgs("--name --version"); //NOI18N
    1.56 -            pye.setShowControls(false);
    1.57 -            pye.setShowInput(false);
    1.58 -            pye.setShowWindow(false);
    1.59 -            pye.setShowProgress(false);
    1.60 -            pye.setShowSuspended(false);
    1.61 -            //pye.setWorkingDirectory(info.getAbsolutePath().substring(0, info.getAbsolutePath().lastIndexOf(File.separator)));
    1.62 -            pye.setWorkingDirectory(FileUtil.toFile(projectDirectory).getAbsolutePath());
    1.63 -            pye.attachOutputProcessor();
    1.64 +            pye = createProjectPropertiesReader(projectDirectory, setuppy);
    1.65              Future<Integer> result = pye.run();
    1.66              Integer value = result.get();
    1.67              if (value == 0) {
    1.68 -                // Problem with encoding?
    1.69 -//                    prop.load(new ReaderInputStream(pye.getOutput()));
    1.70 -                try (Scanner sc = new Scanner(new ReaderInputStream(pye.getOutput()))) {
    1.71 -                    String newName = sc.nextLine();
    1.72 -                    props.setProperty(PROP_DISPLAY_NAME, newName);
    1.73 -                    String newVersion = sc.nextLine();
    1.74 -                    props.setProperty(PROP_VERSION, newVersion);
    1.75 -                }
    1.76 +                fillPropertiesFromSetupOutput(props, pye.getOutput());
    1.77              } else {
    1.78 -                throw new PythonException("Could not discover Python Project Info in " + pye.getWorkingDirectory());
    1.79 +                findProjectPropertiesForceUtf8InSetuppy(props, projectDirectory, setuppy);
    1.80              }
    1.81          } catch (InterruptedException | ExecutionException | IOException ex) {
    1.82              Exceptions.printStackTrace(ex);
    1.83          }
    1.84 +
    1.85          return props;
    1.86      }
    1.87 -    
    1.88 +
    1.89 +    private static PythonExecution createProjectPropertiesReader(FileObject projectDirectory, FileObject setuppy) {
    1.90 +        final PythonPlatformManager manager = PythonPlatformManager.getInstance();
    1.91 +        PythonPlatform platform = manager.getPlatform(manager.getDefaultPlatform());
    1.92 +        PythonExecution pye = new PythonExecution();
    1.93 +        pye.setCommand(platform.getInterpreterCommand());
    1.94 +        pye.setDisplayName("Python Project Info");
    1.95 +
    1.96 +        pye.setScript(FileUtil.toFile(setuppy).getAbsolutePath());
    1.97 +        pye.setScriptArgs("--name --version"); //NOI18N
    1.98 +        pye.setShowControls(false);
    1.99 +        pye.setShowInput(false);
   1.100 +        pye.setShowWindow(false);
   1.101 +        pye.setShowProgress(false);
   1.102 +        pye.setShowSuspended(false);
   1.103 +        pye.setWorkingDirectory(FileUtil.toFile(projectDirectory).getAbsolutePath());
   1.104 +        pye.attachOutputProcessor();
   1.105 +
   1.106 +        return pye;
   1.107 +    }
   1.108 +
   1.109 +    private static void fillPropertiesFromSetupOutput(Properties props, Reader output) throws IOException {
   1.110 +        try (Scanner sc = new Scanner(new ReaderInputStream(output))) {
   1.111 +            String newName = sc.nextLine();
   1.112 +            props.setProperty(PROP_DISPLAY_NAME, newName);
   1.113 +            String newVersion = sc.nextLine();
   1.114 +            props.setProperty(PROP_VERSION, newVersion);
   1.115 +        }
   1.116 +    }
   1.117 +
   1.118 +    private static void findProjectPropertiesForceUtf8InSetuppy(Properties props, FileObject projectDirectory, FileObject setuppy)
   1.119 +            throws IOException, PythonException, InterruptedException, ExecutionException {
   1.120 +        // We trying to force UTF-8 encoding in setup.py to help Jython read the properties.
   1.121 +        List<String> lines = Files.readAllLines(Paths.get(setuppy.getPath()));
   1.122 +        lines.add(0, "# -*- encoding: utf-8 -*-");
   1.123 +
   1.124 +        Path tmpSetupPath = Paths.get(projectDirectory.getFileObject(SETUPPY).getParent().getPath(), TEMPORARY_SETUPPY);
   1.125 +        Files.write(tmpSetupPath, lines, Charset.forName("UTF-8"), StandardOpenOption.CREATE, StandardOpenOption.WRITE);
   1.126 +        FileObject tmpSetup = projectDirectory.getFileObject(TEMPORARY_SETUPPY);
   1.127 +
   1.128 +        PythonExecution pye = createProjectPropertiesReader(projectDirectory, tmpSetup);
   1.129 +        Future<Integer> result = pye.run();
   1.130 +        Integer value = result.get();
   1.131 +        tmpSetup.delete();
   1.132 +        if (value == 0) {
   1.133 +            fillPropertiesFromSetupOutput(props, pye.getOutput());
   1.134 +        } else {
   1.135 +            throw new PythonException("Could not discover Python Project Info in " + pye.getWorkingDirectory());
   1.136 +        }
   1.137 +    }
   1.138 +
   1.139      public static boolean isProject(FileObject projectDirectory) {
   1.140          try {
   1.141              return projectDirectory.getFileObject(PythonProject2.SETUPPY) != null &&