Skip to content

Commit 6986cf6

Browse files
committed
Adding web support to serious_python
1 parent c2771cc commit 6986cf6

File tree

3 files changed

+96
-39
lines changed

3 files changed

+96
-39
lines changed
Lines changed: 64 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import 'dart:io';
2-
1+
import 'package:flutter/foundation.dart';
32
import 'package:path/path.dart' as path;
43
import 'package:serious_python_platform_interface/serious_python_platform_interface.dart';
54

5+
// Conditional import for IO operations
6+
import 'src/io_stub.dart' if (dart.library.io) 'src/io_impl.dart';
7+
68
export 'package:serious_python_platform_interface/src/utils.dart';
79

810
/// Provides cross-platform functionality for running Python programs.
@@ -15,56 +17,80 @@ class SeriousPython {
1517
}
1618

1719
/// Runs Python program from an asset.
18-
///
19-
/// [assetPath] is the path to an asset which is a zip archive
20-
/// with a Python program. When the app starts the archive is unpacked
21-
/// to a temporary directory and Serious Python plugin will try to run
22-
/// `main.py` in the root of the archive. Current directory is changed to
23-
/// a temporary directory.
24-
///
25-
/// If a Python app has a different entry point
26-
/// it could be specified with [appFileName] parameter.
27-
///
28-
/// Environment variables that must be available to a Python program could
29-
/// be passed in [environmentVariables].
30-
///
31-
/// By default, Serious Python expects Python dependencies installed into
32-
/// `__pypackages__` directory in the root of app directory. Additional paths
33-
/// to look for 3rd-party packages can be specified with [modulePaths] parameter.
34-
///
35-
/// Set [sync] to `true` to sychronously run Python program; otherwise the
36-
/// program starts in a new thread.
3720
static Future<String?> run(String assetPath,
3821
{String? appFileName,
39-
List<String>? modulePaths,
40-
Map<String, String>? environmentVariables,
41-
bool? sync}) async {
42-
// unpack app from asset
22+
List<String>? modulePaths,
23+
Map<String, String>? environmentVariables,
24+
bool? sync}) async {
25+
// Handle web platform differently
26+
if (kIsWeb) {
27+
return _runWeb(assetPath,
28+
appFileName: appFileName,
29+
modulePaths: modulePaths,
30+
environmentVariables: environmentVariables,
31+
sync: sync);
32+
} else {
33+
return _runDesktop(assetPath,
34+
appFileName: appFileName,
35+
modulePaths: modulePaths,
36+
environmentVariables: environmentVariables,
37+
sync: sync);
38+
}
39+
}
40+
41+
/// Web-specific implementation
42+
static Future<String?> _runWeb(String assetPath,
43+
{String? appFileName,
44+
List<String>? modulePaths,
45+
Map<String, String>? environmentVariables,
46+
bool? sync}) async {
47+
48+
String virtualPath;
49+
if (path.extension(assetPath) == ".zip") {
50+
virtualPath = assetPath.replaceAll(".zip", "");
51+
// TODO Check if path exists and except with unzip hint if not
52+
} else {
53+
virtualPath = assetPath;
54+
}
55+
56+
if (appFileName != null) {
57+
virtualPath = '$virtualPath/$appFileName';
58+
} else {
59+
virtualPath = '$virtualPath/main.py';
60+
}
61+
62+
return runProgram(virtualPath,
63+
modulePaths: modulePaths,
64+
environmentVariables: environmentVariables,
65+
sync: sync);
66+
}
67+
68+
/// Desktop-specific implementation
69+
static Future<String?> _runDesktop(String assetPath,
70+
{String? appFileName,
71+
List<String>? modulePaths,
72+
Map<String, String>? environmentVariables,
73+
bool? sync}) async {
4374
String appPath = "";
4475
if (path.extension(assetPath) == ".zip") {
4576
appPath = await extractAssetZip(assetPath);
4677
if (appFileName != null) {
4778
appPath = path.join(appPath, appFileName);
48-
} else if (await File(path.join(appPath, "main.pyc")).exists()) {
49-
appPath = path.join(appPath, "main.pyc");
50-
} else if (await File(path.join(appPath, "main.py")).exists()) {
51-
appPath = path.join(appPath, "main.py");
5279
} else {
53-
throw Exception(
54-
"App archive must contain either `main.py` or `main.pyc`; otherwise `appFileName` must be specified.");
80+
appPath = await FileSystem.findMainFile(appPath);
5581
}
5682
} else {
5783
appPath = await extractAsset(assetPath);
5884
}
5985

6086
// set current directory to app path
61-
Directory.current = path.dirname(appPath);
87+
await FileSystem.setCurrentDirectory(path.dirname(appPath));
6288

6389
// run python program
6490
return runProgram(appPath,
6591
modulePaths: modulePaths,
6692
environmentVariables: environmentVariables,
67-
script: Platform.isWindows ? "" : null,
93+
script: FileSystem.isWindows ? "" : null,
6894
sync: sync);
6995
}
7096

@@ -82,14 +108,13 @@ class SeriousPython {
82108
/// `__pypackages__` directory in the root of app directory. Additional paths
83109
/// to look for 3rd-party packages can be specified with [modulePaths] parameter.
84110
///
85-
/// Set [sync] to `true` to sychronously run Python program; otherwise the
111+
/// Set [sync] to `true` to synchronously run Python program; otherwise the
86112
/// program starts in a new thread.
87113
static Future<String?> runProgram(String appPath,
88114
{String? script,
89-
List<String>? modulePaths,
90-
Map<String, String>? environmentVariables,
91-
bool? sync}) async {
92-
// run python program
115+
List<String>? modulePaths,
116+
Map<String, String>? environmentVariables,
117+
bool? sync}) async {
93118
return SeriousPythonPlatform.instance.run(appPath,
94119
script: script,
95120
modulePaths: modulePaths,
@@ -100,4 +125,4 @@ class SeriousPython {
100125
static void terminate() {
101126
SeriousPythonPlatform.instance.terminate();
102127
}
103-
}
128+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import 'dart:io';
2+
import 'package:path/path.dart' as path;
3+
4+
class FileSystem {
5+
static Future<String> findMainFile(String appPath) async {
6+
if (await File(path.join(appPath, "main.pyc")).exists()) {
7+
return path.join(appPath, "main.pyc");
8+
} else if (await File(path.join(appPath, "main.py")).exists()) {
9+
return path.join(appPath, "main.py");
10+
}
11+
throw Exception(
12+
"App archive must contain either `main.py` or `main.pyc`; otherwise `appFileName` must be specified.");
13+
}
14+
15+
static Future<void> setCurrentDirectory(String path) async {
16+
Directory.current = path;
17+
}
18+
19+
static bool get isWindows => Platform.isWindows;
20+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Stub implementation for web
2+
class FileSystem {
3+
static Future<String> findMainFile(String appPath) async {
4+
return '$appPath/main.py';
5+
}
6+
7+
static Future<void> setCurrentDirectory(String path) async {
8+
// No-op for web
9+
}
10+
11+
static bool get isWindows => false;
12+
}

0 commit comments

Comments
 (0)