Skip to content

Commit cc6d296

Browse files
authored
Merge branch 'flet-dev:main' into feature/add-web-support
2 parents 1794c3b + 36ef98f commit cc6d296

File tree

19 files changed

+238
-73
lines changed

19 files changed

+238
-73
lines changed

src/serious_python/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 0.8.8
2+
3+
* Set `MinimumOSVersion` to `13.0` for generated Python frameworks.
4+
* Pyodide 0.27.2
5+
* Python 3.12.9
6+
17
## 0.8.7
28

39
* Fixed: `xcframeworks` migration script didn't work for sub-directories.

src/serious_python/bin/package_command.dart

Lines changed: 103 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import 'dart:io';
44
import 'package:archive/archive_io.dart';
55
import 'package:args/command_runner.dart';
66
import 'package:crypto/crypto.dart';
7+
import 'package:glob/glob.dart';
8+
import 'package:glob/list_local_fs.dart';
79
import 'package:http/http.dart' as http;
810
import 'package:path/path.dart' as path;
911
import 'package:shelf/shelf.dart';
@@ -13,11 +15,11 @@ import 'macos_utils.dart' as macos_utils;
1315
import 'sitecustomize.dart';
1416

1517
const mobilePyPiUrl = "https://pypi.flet.dev";
16-
const pyodideRootUrl = "https://cdn.jsdelivr.net/pyodide/v0.26.2/full";
18+
const pyodideRootUrl = "https://cdn.jsdelivr.net/pyodide/v0.27.2/full";
1719
const pyodideLockFile = "pyodide-lock.json";
1820

19-
const buildPythonVersion = "3.12.6";
20-
const buildPythonReleaseDate = "20240909";
21+
const buildPythonVersion = "3.12.9";
22+
const buildPythonReleaseDate = "20250205";
2123
const defaultSitePackagesDir = "__pypackages__";
2224
const sitePackagesEnvironmentVariable = "SERIOUS_PYTHON_SITE_PACKAGES";
2325
const flutterPackagesFlutterEnvironmentVariable =
@@ -58,11 +60,28 @@ const platforms = {
5860
}
5961
};
6062

61-
const junkFileExtensionsDesktop = [".c", ".h", ".hpp", ".typed", ".a", ".pdb"];
62-
const junkFileExtensionsMobile = [...junkFileExtensionsDesktop, ".exe", ".dll"];
63-
64-
const junkFilesDesktop = ["__pycache__"];
65-
const junkFilesMobile = [...junkFilesDesktop, "bin"];
63+
const junkFilesDesktop = [
64+
"**.c",
65+
"**.h",
66+
"**.cpp",
67+
"**.hpp",
68+
"**.typed",
69+
"**.pyi",
70+
"**.pxd",
71+
"**.pyx",
72+
"**.a",
73+
"**.pdb",
74+
"**.dist-info",
75+
"__pycache__",
76+
"**/__pycache__",
77+
];
78+
const junkFilesMobile = [
79+
...junkFilesDesktop,
80+
"**.exe",
81+
"**.dll",
82+
"bin",
83+
"**/bin",
84+
];
6685

6786
class PackageCommand extends Command {
6887
bool _verbose = false;
@@ -104,6 +123,16 @@ class PackageCommand extends Command {
104123
help:
105124
"Cleanup app and packages from unneccessary files and directories.",
106125
negatable: false);
126+
argParser.addFlag("cleanup-app",
127+
help: "Cleanup app from unneccessary files and directories.",
128+
negatable: false);
129+
argParser.addMultiOption('cleanup-app-files',
130+
help: "List of globs to delete extra app files and directories.");
131+
argParser.addFlag("cleanup-packages",
132+
help: "Cleanup packages from unneccessary files and directories.",
133+
negatable: false);
134+
argParser.addMultiOption('cleanup-packages-files',
135+
help: "List of globs to delete extra packages files and directories.");
107136
argParser.addFlag("verbose", help: "Verbose output.", negatable: false);
108137
}
109138

@@ -135,6 +164,10 @@ class PackageCommand extends Command {
135164
bool compileApp = argResults?["compile-app"];
136165
bool compilePackages = argResults?["compile-packages"];
137166
bool cleanup = argResults?["cleanup"];
167+
bool cleanupApp = argResults?["cleanup-app"];
168+
List<String> cleanupAppFiles = argResults?['cleanup-app-files'];
169+
bool cleanupPackages = argResults?["cleanup-packages"];
170+
List<String> cleanupPackagesFiles = argResults?['cleanup-packages-files'];
138171
_verbose = argResults?["verbose"];
139172

140173
if (path.isRelative(sourceDirPath)) {
@@ -162,8 +195,6 @@ class PackageCommand extends Command {
162195
bool isMobile = (platform == "iOS" || platform == "Android");
163196
bool isWeb = platform == "Pyodide";
164197

165-
var junkFileExtensions =
166-
isMobile ? junkFileExtensionsMobile : junkFileExtensionsDesktop;
167198
var junkFiles = isMobile ? junkFilesMobile : junkFilesDesktop;
168199

169200
// Extra indexs
@@ -212,19 +243,19 @@ class PackageCommand extends Command {
212243
await runPython(['-m', 'compileall', '-b', tempDir.path]);
213244

214245
verbose("Deleting original .py files");
215-
await cleanupPyPackages(tempDir, [".py"], []);
246+
await cleanupDir(tempDir, ["**.py"]);
216247
}
217248

218249
// cleanup
219-
if (cleanup) {
250+
if (cleanupApp || cleanup) {
251+
var allJunkFiles = [...junkFiles, ...cleanupAppFiles];
220252
if (_verbose) {
221253
verbose(
222-
"Delete unnecessary app files with extensions: $junkFileExtensions");
223-
verbose("Delete unnecessary app files and directories: $junkFiles");
254+
"Delete unnecessary app files and directories: $allJunkFiles");
224255
} else {
225256
stdout.writeln(("Cleanup app"));
226257
}
227-
await cleanupPyPackages(tempDir, junkFileExtensions, junkFiles);
258+
await cleanupDir(tempDir, allJunkFiles);
228259
}
229260

230261
// install requirements
@@ -358,21 +389,19 @@ class PackageCommand extends Command {
358389
await runPython(['-m', 'compileall', '-b', sitePackagesDir]);
359390

360391
verbose("Deleting original .py files");
361-
await cleanupPyPackages(Directory(sitePackagesDir), [".py"], []);
392+
await cleanupDir(Directory(sitePackagesDir), ["**.py"]);
362393
}
363394

364395
// cleanup packages
365-
if (cleanup) {
396+
if (cleanupPackages || cleanup) {
397+
var allJunkFiles = [...junkFiles, ...cleanupPackagesFiles];
366398
if (_verbose) {
367399
verbose(
368-
"Delete unnecessary package files with extensions: $junkFileExtensions");
369-
verbose(
370-
"Delete unnecessary package files and directories: $junkFiles");
400+
"Delete unnecessary package files and directories: $allJunkFiles");
371401
} else {
372402
stdout.writeln(("Cleanup installed packages"));
373403
}
374-
await cleanupPyPackages(
375-
Directory(sitePackagesDir), junkFileExtensions, junkFiles);
404+
await cleanupDir(Directory(sitePackagesDir), allJunkFiles);
376405
}
377406
} finally {
378407
if (sitecustomizeDir != null && await sitecustomizeDir.exists()) {
@@ -435,28 +464,34 @@ class PackageCommand extends Command {
435464
}
436465
}
437466

438-
Future<void> cleanupPyPackages(Directory directory,
439-
List<String> fileExtensions, List<String> filesAndDirectories) async {
440-
await for (var entity in directory.list()) {
441-
if (entity is Directory) {
442-
await cleanupPyPackages(entity, fileExtensions, filesAndDirectories);
443-
} else if (entity is File &&
444-
(fileExtensions.contains(path.extension(entity.path)) ||
445-
filesAndDirectories.contains(path.basename(entity.path)))) {
446-
verbose("Deleting ${entity.path}");
447-
448-
await entity.delete();
449-
}
450-
}
467+
Future<void> cleanupDir(Directory directory, List<String> filesGlobs) async {
468+
verbose("Cleanup directory ${directory.path}: $filesGlobs");
469+
await cleanupDirRecursive(
470+
directory,
471+
filesGlobs.map((g) => Glob(g.replaceAll("\\", "/"),
472+
context: path.Context(current: directory.path))));
473+
}
451474

452-
await for (var entity in directory.list()) {
453-
if (entity is Directory &&
454-
filesAndDirectories.contains(path.basename(entity.path))) {
475+
Future<bool> cleanupDirRecursive(
476+
Directory directory, Iterable<Glob> globs) async {
477+
var emptyDir = true;
478+
for (var entity in directory.listSync()) {
479+
if (globs.any((g) => g.matches(entity.path.replaceAll("\\", "/"))) &&
480+
await entity.exists()) {
455481
verbose("Deleting ${entity.path}");
456-
457482
await entity.delete(recursive: true);
483+
} else if (entity is Directory) {
484+
if (await cleanupDirRecursive(entity, globs)) {
485+
verbose("Deleting empty directory ${entity.path}");
486+
await entity.delete(recursive: true);
487+
} else {
488+
emptyDir = false;
489+
}
490+
} else {
491+
emptyDir = false;
458492
}
459493
}
494+
return emptyDir;
460495
}
461496

462497
Future<int> runExec(String execPath, List<String> args,
@@ -507,7 +542,7 @@ class PackageCommand extends Command {
507542
if (!await File(pythonArchivePath).exists()) {
508543
// download Python distr from GitHub
509544
final url =
510-
"https://github.com/indygreg/python-build-standalone/releases/download/$buildPythonReleaseDate/$pythonArchiveFilename";
545+
"https://github.com/astral-sh/python-build-standalone/releases/download/$buildPythonReleaseDate/$pythonArchiveFilename";
511546

512547
if (_verbose) {
513548
verbose(
@@ -523,13 +558,18 @@ class PackageCommand extends Command {
523558

524559
// extract Python from archive
525560
if (_verbose) {
526-
"Extracting Python distributive from $pythonArchivePath to ${_pythonDir!.path}";
561+
verbose(
562+
"Extracting Python distributive from $pythonArchivePath to ${_pythonDir!.path}");
527563
} else {
528564
stdout.writeln("Extracting Python distributive");
529565
}
530566

531567
await Process.run(
532568
'tar', ['-xzf', pythonArchivePath, '-C', _pythonDir!.path]);
569+
570+
if (Platform.isMacOS) {
571+
duplicateSysconfigFile(_pythonDir!.path);
572+
}
533573
}
534574
}
535575

@@ -542,6 +582,28 @@ class PackageCommand extends Command {
542582
return await runExec(pythonExePath, args, environment: environment);
543583
}
544584

585+
void duplicateSysconfigFile(String pythonDir) {
586+
final sysConfigGlob = Glob("python/lib/python3.*/_sysconfigdata__*.py",
587+
context: path.Context(current: pythonDir));
588+
for (var sysConfig in sysConfigGlob.listSync(root: pythonDir)) {
589+
// copy the first found sys config and exit
590+
if (sysConfig is File) {
591+
for (final target in [
592+
'_sysconfigdata__darwin_arm64_iphoneos.py',
593+
'_sysconfigdata__darwin_arm64_iphonesimulator.py',
594+
'_sysconfigdata__darwin_x86_64_iphonesimulator.py',
595+
]) {
596+
var targetPath = path.join(sysConfig.parent.path, target);
597+
(sysConfig as File).copySync(targetPath);
598+
if (_verbose) {
599+
verbose('Copied ${sysConfig.path} -> $targetPath');
600+
}
601+
}
602+
break;
603+
}
604+
}
605+
}
606+
545607
Future<HttpServer> startSimpleServer() async {
546608
const htmlHeader = "<!DOCTYPE html><html><body>\n";
547609
const htmlFooter = "</body></html>\n";

src/serious_python/bin/sitecustomize.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ custom_system = "{platform}"
66
custom_platform = "{tag}"
77
custom_mac_ver = "{mac_ver}"
88
9+
import collections
910
import platform
1011
import sysconfig
12+
import sys
1113
1214
if custom_system:
1315
platform.system = lambda: custom_system
@@ -24,6 +26,20 @@ if custom_mac_ver:
2426
2527
platform.mac_ver = custom_mac_ver_impl
2628
29+
if custom_system == "iOS":
30+
IOSVersionInfo = collections.namedtuple(
31+
"IOSVersionInfo",
32+
["system", "release", "model", "is_simulator"]
33+
)
34+
35+
tag_parts = custom_platform.split("-")
36+
37+
def custom_ios_ver(system="", release="", model="", is_simulator=False):
38+
return IOSVersionInfo(custom_system, tag_parts[1], "iPhone", "simulator" in tag_parts[3])
39+
40+
platform.ios_ver = custom_ios_ver
41+
42+
sys.implementation._multiarch = f"{tag_parts[2]}_{tag_parts[3]}"
2743
2844
orig_platform_version = platform.version
2945
platform.version = lambda: orig_platform_version() + ";embedded"

0 commit comments

Comments
 (0)