35
35
import java .nio .file .Paths ;
36
36
import java .util .LinkedHashMap ;
37
37
import java .util .Map ;
38
+ import java .util .StringJoiner ;
38
39
39
40
import org .scijava .app .AppService ;
40
41
import org .scijava .command .CommandService ;
48
49
import org .scijava .ui .DialogPrompt ;
49
50
import org .scijava .ui .UIService ;
50
51
import org .scijava .widget .Button ;
52
+ import org .scijava .widget .TextWidget ;
51
53
52
54
/**
53
55
* Options for configuring the Python environment.
@@ -72,6 +74,14 @@ public class OptionsPython extends OptionsPlugin {
72
74
@ Parameter (label = "Python environment directory" , persist = false )
73
75
private File pythonDir ;
74
76
77
+ @ Parameter (label = "Conda dependencies" , style = TextWidget .AREA_STYLE ,
78
+ persist = false )
79
+ private String condaDependencies ;
80
+
81
+ @ Parameter (label = "Pip dependencies" , style = TextWidget .AREA_STYLE ,
82
+ persist = false )
83
+ private String pipDependencies ;
84
+
75
85
@ Parameter (label = "Build Python environment" , callback = "rebuildEnv" )
76
86
private Button rebuildEnvironment ;
77
87
@@ -83,6 +93,8 @@ public class OptionsPython extends OptionsPlugin {
83
93
private UIService uiService ;
84
94
85
95
private boolean initialPythonMode = false ;
96
+ private String initialCondaDependencies ;
97
+ private String initialPipDependencies ;
86
98
87
99
// -- OptionsPython methods --
88
100
@@ -142,10 +154,70 @@ public void load() {
142
154
143
155
// Store the initial value of pythonMode for later comparison
144
156
initialPythonMode = pythonMode ;
157
+
158
+ // Populate condaDependencies and pipDependencies from environment.yml
159
+ condaDependencies = "" ;
160
+ pipDependencies = "" ;
161
+ java .util .Set <String > pipBlacklist = new java .util .HashSet <>();
162
+ pipBlacklist .add ("appose-python" );
163
+ pipBlacklist .add ("pyimagej" );
164
+ File envFile = getEnvironmentYamlFile ();
165
+ if (envFile .exists ()) {
166
+ try {
167
+ java .util .List <String > lines = java .nio .file .Files .readAllLines (envFile
168
+ .toPath ());
169
+ boolean inDeps = false , inPip = false ;
170
+ StringJoiner condaDeps = new StringJoiner ("\n " );
171
+ StringJoiner pipDeps = new StringJoiner ("\n " );
172
+ for (String line : lines ) {
173
+ String trimmed = line .trim ();
174
+ if (trimmed .startsWith ("#" ) || trimmed .isEmpty ()) {
175
+ // Ignore empty and comment lines
176
+ continue ;
177
+ }
178
+ if (trimmed .startsWith ("dependencies:" )) {
179
+ inDeps = true ;
180
+ continue ;
181
+ }
182
+ if (inDeps && trimmed .startsWith ("- pip" )) {
183
+ inPip = true ;
184
+ continue ;
185
+ }
186
+ if (inDeps && trimmed .startsWith ("- " ) && !inPip ) {
187
+ String dep = trimmed .substring (2 ).trim ();
188
+ if (!dep .equals ("pip" )) condaDeps .add (dep );
189
+ continue ;
190
+ }
191
+ if (inPip && trimmed .startsWith ("- " )) {
192
+ String pipDep = trimmed .substring (2 ).trim ();
193
+ boolean blacklisted = false ;
194
+ for (String bad : pipBlacklist ) {
195
+ if (pipDep .contains (bad )) {
196
+ blacklisted = true ;
197
+ break ;
198
+ }
199
+ }
200
+ if (!blacklisted ) pipDeps .add (pipDep );
201
+ continue ;
202
+ }
203
+ if (inDeps && !trimmed .startsWith ("- " ) && !trimmed .isEmpty ())
204
+ inDeps = false ;
205
+ if (inPip && (!trimmed .startsWith ("- " ) || trimmed .isEmpty ())) inPip =
206
+ false ;
207
+ }
208
+ condaDependencies = condaDeps .toString ().trim ();
209
+ pipDependencies = pipDeps .toString ().trim ();
210
+ initialCondaDependencies = condaDependencies ;
211
+ initialPipDependencies = pipDependencies ;
212
+ }
213
+ catch (Exception e ) {
214
+ log .debug ("Could not read environment.yml: " + e .getMessage ());
215
+ }
216
+ }
145
217
}
146
218
147
219
public void rebuildEnv () {
148
- File environmentYaml = getEnvironmentYamlFile ();
220
+ File environmentYaml = writeEnvironmentYaml ();
149
221
commandService .run (RebuildEnvironment .class , true , "environmentYaml" ,
150
222
environmentYaml , "targetDir" , pythonDir );
151
223
}
@@ -163,7 +235,6 @@ private File getEnvironmentYamlFile() {
163
235
environmentYaml = stringToFile (appPath , pythonEnvFileProp );
164
236
}
165
237
return environmentYaml ;
166
-
167
238
}
168
239
169
240
@ Override
@@ -197,6 +268,9 @@ public void save() {
197
268
if (pythonMode && (pythonDir == null || !pythonDir .exists ())) {
198
269
rebuildEnv ();
199
270
}
271
+ else {
272
+ writeEnvironmentYaml ();
273
+ }
200
274
// Warn the user if pythonMode was just enabled and wasn't before
201
275
if (!initialPythonMode && pythonMode && uiService != null ) {
202
276
String msg =
@@ -208,6 +282,50 @@ public void save() {
208
282
}
209
283
}
210
284
285
+ private File writeEnvironmentYaml () {
286
+ File envFile = getEnvironmentYamlFile ();
287
+
288
+ // skip writing if nothing has changed
289
+ if (initialCondaDependencies .equals (condaDependencies ) &&
290
+ initialPipDependencies .equals (pipDependencies )) return envFile ;
291
+
292
+ // Update initial dependencies to detect future changes
293
+ initialCondaDependencies = condaDependencies ;
294
+ initialPipDependencies = pipDependencies ;
295
+
296
+ // Write environment.yml from condaDependencies and pipDependencies
297
+ try {
298
+ String name = "fiji" ;
299
+ String [] channels = { "conda-forge" };
300
+ String pyimagej = "pyimagej>=1.7.0" ;
301
+ String apposePython =
302
+ "git+https://github.com/apposed/appose-python.git@efe6dadb2242ca45820fcbb7aeea2096f99f9cb2" ;
303
+ StringBuilder yml = new StringBuilder ();
304
+ yml .append ("name: " ).append (name ).append ("\n channels:\n " );
305
+ for (String ch : channels )
306
+ yml .append (" - " ).append (ch ).append ("\n " );
307
+ yml .append ("dependencies:\n " );
308
+ for (String dep : condaDependencies .split ("\n " )) {
309
+ String trimmed = dep .trim ();
310
+ if (!trimmed .isEmpty ()) yml .append (" - " ).append (trimmed ).append ("\n " );
311
+ }
312
+ yml .append (" - pip\n " );
313
+ yml .append (" - pip:\n " );
314
+ for (String dep : pipDependencies .split ("\n " )) {
315
+ String trimmed = dep .trim ();
316
+ if (!trimmed .isEmpty ()) yml .append (" - " ).append (trimmed ).append (
317
+ "\n " );
318
+ }
319
+ yml .append (" - " ).append (pyimagej ).append ("\n " );
320
+ yml .append (" - " ).append (apposePython ).append ("\n " );
321
+ java .nio .file .Files .write (envFile .toPath (), yml .toString ().getBytes ());
322
+ }
323
+ catch (Exception e ) {
324
+ log .debug ("Could not write environment.yml: " + e .getMessage ());
325
+ }
326
+ return envFile ;
327
+ }
328
+
211
329
// -- Utility methods --
212
330
213
331
/**
0 commit comments