diff --git a/example/python_shell_example.dart b/example/python_shell_example.dart index 77601e7..7acc709 100644 --- a/example/python_shell_example.dart +++ b/example/python_shell_example.dart @@ -1,7 +1,7 @@ import "package:python_shell/python_shell.dart"; void main() async { - // 현재 스타일 + // 현재 스타일 // var shell = PythonShell(shellConfig: PythonShellConfig( // pythonRequires: [ "PySide6" ], // defaultWorkingDirectory: "example" @@ -20,17 +20,20 @@ void main() async { // } // )); - var shell = PythonShell(PythonShellConfig()); - await shell.initialize(); + var shell = PythonShell(PythonShellConfig()); + await shell.initialize(); - var instance = ShellManager.getInstance("default"); - instance.installRequires([ "PySide6" ]); - await instance.runString(""" + var instance = ShellManager.getInstance("default"); + instance.installRequires(["PySide6"]); + await instance.runString( + """ import os, PySide6 print("in python: ", os.getcwd()) print("in python: ", PySide6) -""", echo: true); +""", + echo: true, + ); - print("finished"); + print("finished"); } diff --git a/lib/src/config.dart b/lib/src/config.dart index 6c477bf..33df204 100644 --- a/lib/src/config.dart +++ b/lib/src/config.dart @@ -1,9 +1,10 @@ - import "dart:io"; -import "package:path/path.dart" as path; +import "package:path/path.dart" as path; -String appDir = path.join(Platform.environment[Platform.isWindows ? "USERPROFILE" : "HOME"]!, ".python_shell.dart"); +String appDir = path.join( + Platform.environment[Platform.isWindows ? "USERPROFILE" : "HOME"]!, + ".python_shell.dart"); String tempDir = path.join(appDir, "temp"); String instanceDir = path.join(appDir, "instances"); @@ -11,30 +12,27 @@ String defaultPythonVersion = "3.9.13"; String defaultPythonPath = "python3"; String checkPythonVersion(String rawPythonVersion) { - String realPythonVersion = "3.9.13"; + String realPythonVersion = "3.9.13"; - var versions = rawPythonVersion.split("."); - if (versions.length == 3) { - if (rawPythonVersion.endsWith(".")) { - if (versions.last != "") { - versions.removeLast(); - realPythonVersion = versions.join("."); - } - } - else { - realPythonVersion = rawPythonVersion; - } + var versions = rawPythonVersion.split("."); + if (versions.length == 3) { + if (rawPythonVersion.endsWith(".")) { + if (versions.last != "") { + versions.removeLast(); + realPythonVersion = versions.join("."); + } + } else { + realPythonVersion = rawPythonVersion; } - else if (versions.length == 2) { - if (rawPythonVersion.endsWith(".")) { - if (versions.last != "") { - realPythonVersion = "${versions.join(".")}.0"; - } - } - else { - realPythonVersion = "$rawPythonVersion.0"; - } + } else if (versions.length == 2) { + if (rawPythonVersion.endsWith(".")) { + if (versions.last != "") { + realPythonVersion = "${versions.join(".")}.0"; + } + } else { + realPythonVersion = "$rawPythonVersion.0"; } + } - return realPythonVersion; + return realPythonVersion; } diff --git a/lib/src/python_shell.dart b/lib/src/python_shell.dart index e096476..6987524 100644 --- a/lib/src/python_shell.dart +++ b/lib/src/python_shell.dart @@ -1,98 +1,142 @@ - import "dart:io"; -import "package:path/path.dart" as path; -import "package:dio/dio.dart"; import "package:archive/archive_io.dart"; +import "package:dio/dio.dart"; +import "package:path/path.dart" as path; import "config.dart" as config; import "listener.dart"; -import "shell/manager.dart"; import "shell/instance.dart"; - +import "shell/manager.dart"; /// PythonShell Class class PythonShell { - PythonShell(PythonShellConfig shellConfig); - - void clear() => ShellManager.clear(); - - Future initialize() async { - print("Initializing shell..."); - config.defaultPythonVersion = config.checkPythonVersion(config.defaultPythonVersion); - if (!Directory(config.appDir).existsSync()) Directory(config.appDir).createSync(); - if (!Directory(config.instanceDir).existsSync()) Directory(config.instanceDir).createSync(); - if (!Directory(config.tempDir).existsSync()) Directory(config.tempDir).createSync(); - - String pythonDir = ""; - if (Platform.isWindows) { - print("Check default python binary files..."); - pythonDir = path.join(config.appDir, "python"); - if (!Directory(pythonDir).existsSync()) { - String pythonBinaryFile = path.join(config.tempDir, "python.zip"); - await Dio().download("https://www.python.org/ftp/python/${config.defaultPythonVersion}/python-${config.defaultPythonVersion}-embed-amd64.zip", pythonBinaryFile); - await extractFileToDisk(pythonBinaryFile, pythonDir); - File(pythonBinaryFile).deleteSync(); - - String pythonPthFile = path.join(pythonDir, "python${config.defaultPythonVersion.replaceAll(".${config.defaultPythonVersion.split(".").last}", "").replaceAll(".", "")}._pth"); - File(pythonPthFile).writeAsStringSync(File(pythonPthFile).readAsStringSync().replaceAll("#import site", "import site")); - } - print("Python check finished."); - - config.defaultPythonPath = path.join(pythonDir, "python.exe"); - } - - if (File(config.defaultPythonPath).existsSync()) { - print("Default settings for virtualenv..."); - var result = Process.runSync(config.defaultPythonPath, [ "-m", "pip", "install", "pip", "--upgrade" ]); - if (result.stderr.toString().trim() != "") { - String pipInstallFile = path.join(config.tempDir, "get-pip.py"); - await Dio().download("https://bootstrap.pypa.io/pip/get-pip.py", pipInstallFile); - Process.runSync(config.defaultPythonPath, [ pipInstallFile ]); - File(pipInstallFile).deleteSync(); - } - - Process.runSync(config.defaultPythonPath, [ "-m", "pip", "install", "virtualenv", "--upgrade" ]); - print("Virtualenv settings finished."); - } + PythonShell(PythonShellConfig shellConfig); + + void clear() => ShellManager.clear(); + + Future initialize() async { + print("Initializing shell..."); + config.defaultPythonVersion = + config.checkPythonVersion(config.defaultPythonVersion); + if (!Directory(config.appDir).existsSync()) + Directory(config.appDir).createSync(); + if (!Directory(config.instanceDir).existsSync()) + Directory(config.instanceDir).createSync(); + if (!Directory(config.tempDir).existsSync()) + Directory(config.tempDir).createSync(); + + String pythonDir = ""; + if (Platform.isWindows) { + print("Check default python binary files..."); + pythonDir = path.join(config.appDir, "python"); + if (!Directory(pythonDir).existsSync()) { + String pythonBinaryFile = path.join(config.tempDir, "python.zip"); + await Dio().download( + "https://www.python.org/ftp/python/${config.defaultPythonVersion}/python-${config.defaultPythonVersion}-embed-amd64.zip", + pythonBinaryFile); + await extractFileToDisk(pythonBinaryFile, pythonDir); + File(pythonBinaryFile).deleteSync(); + + String pythonPthFile = path.join(pythonDir, + "python${config.defaultPythonVersion.replaceAll(".${config.defaultPythonVersion.split(".").last}", "").replaceAll(".", "")}._pth"); + File(pythonPthFile).writeAsStringSync(File(pythonPthFile) + .readAsStringSync() + .replaceAll("#import site", "import site")); + } + print("Python check finished."); + + config.defaultPythonPath = path.join(pythonDir, "python.exe"); + } - String defaultEnvDir = path.join(config.instanceDir, "default"); - if (!Directory(defaultEnvDir).existsSync()) { - print("Creating default env..."); - ShellManager.createInstance(instanceName: "default"); - print("Default env created."); + if (File(config.defaultPythonPath).existsSync()) { + print("Default settings for virtualenv..."); + var result = Process.runSync(config.defaultPythonPath, + ["-m", "pip", "install", "pip", "--upgrade"]); + if (result.exitCode != 0) { + print('Error for result, exist code ${result.exitCode}\n' + 'Stdout: ${result.stdout}\nStderr: ${result.stderr}'); + } + + if (result.stderr.toString().trim() != "") { + String pipInstallFile = path.join(config.tempDir, "get-pip.py"); + await Dio().download( + "https://bootstrap.pypa.io/pip/get-pip.py", pipInstallFile); + ProcessResult pipInstallFileProcess = + Process.runSync(config.defaultPythonPath, [pipInstallFile]); + if (pipInstallFileProcess.exitCode != 0) { + print( + 'Error for pipInstallFile, exist code ${pipInstallFileProcess.exitCode}\n' + 'Stdout: ${pipInstallFileProcess.stdout}\nStderr: ${pipInstallFileProcess.stderr}'); } - - print("Shell initialized."); + File(pipInstallFile).deleteSync(); + } + + ProcessResult virtualEnvUpgrade = Process.runSync( + config.defaultPythonPath, + ["-m", "pip", "install", "virtualenv", "--upgrade"]); + if (virtualEnvUpgrade.exitCode != 0) { + print( + 'Error for virtualEnvUpgrade, exist code ${virtualEnvUpgrade.exitCode}\n' + 'Stdout: ${virtualEnvUpgrade.stdout}\nStderr: ${virtualEnvUpgrade.stderr}'); + } + + print("Virtualenv settings finished."); } - Future runFile(String pythonFile, { ShellInstance? instance, String? workingDirectory, ShellListener? listener, bool echo = true }) async { - instance = instance ?? ShellManager.getInstance("default"); - await instance.runFile(pythonFile, workingDirectory: workingDirectory, listener: listener, echo: echo); + String defaultEnvDir = path.join(config.instanceDir, "default"); + if (!Directory(defaultEnvDir).existsSync()) { + print("Creating default env..."); + ShellManager.createInstance(instanceName: "default"); + print("Default env created."); } - Future runString(String pythonCode, { ShellInstance? instance, String? workingDirectory, ShellListener? listener, bool echo = true }) async { - instance = instance ?? ShellManager.getInstance("default"); - await instance.runString(pythonCode, workingDirectory: workingDirectory, listener: listener, echo: echo); - } + print("Shell initialized."); + } + + Future runFile( + String pythonFile, { + ShellInstance? instance, + String? workingDirectory, + ShellListener? listener, + bool echo = true, + }) async { + instance = instance ?? ShellManager.getInstance("default"); + await instance.runFile(pythonFile, + workingDirectory: workingDirectory, listener: listener, echo: echo); + } + + Future runString( + String pythonCode, { + List arguments = const [], + ShellInstance? instance, + String? workingDirectory, + ShellListener? listener, + bool echo = true, + }) async { + instance = instance ?? ShellManager.getInstance("default"); + await instance.runString(pythonCode, + arguments: arguments, + workingDirectory: workingDirectory, + listener: listener, + echo: echo); + } } /// PythonShell Configuration Class /// * [defaultPythonPath]: Default python path to use /// * [defaultPythonVersion]: Default python version to use class PythonShellConfig { - - PythonShellConfig({ - defaultPythonPath = "python3", - defaultPythonVersion = "3.9.13", - }) { - if ((Platform.isLinux || Platform.isMacOS)) { - if (["python", "python2", "python3"].contains(defaultPythonPath)) { - config.defaultPythonPath = "/usr/bin/$defaultPythonPath"; - } - else if (!File(defaultPythonPath).existsSync()) { - config.defaultPythonPath = "/usr/bin/python3"; - } - } + PythonShellConfig({ + defaultPythonPath = "python3", + defaultPythonVersion = "3.9.13", + }) { + if ((Platform.isLinux || Platform.isMacOS)) { + if (["python", "python2", "python3"].contains(defaultPythonPath)) { + config.defaultPythonPath = "/usr/bin/$defaultPythonPath"; + } else if (!File(defaultPythonPath).existsSync()) { + config.defaultPythonPath = "/usr/bin/python3"; + } } + } } diff --git a/lib/src/shell/instance.dart b/lib/src/shell/instance.dart index 9a88dab..12a017a 100644 --- a/lib/src/shell/instance.dart +++ b/lib/src/shell/instance.dart @@ -1,78 +1,98 @@ - import "dart:io"; -import "package:path/path.dart" as path; + import "package:intl/intl.dart"; +import "package:path/path.dart" as path; import "../config.dart" as config; import "../listener.dart"; - class ShellInstance { - String name; - String pythonPath; - String tempDir; - String? defaultWorkingDirectory; + String name; + String pythonPath; + String tempDir; + String? defaultWorkingDirectory; - ShellInstance({ - required this.name, - required this.pythonPath, - required this.tempDir - }); + ShellInstance({ + required this.name, + required this.pythonPath, + required this.tempDir, + }); - void installRequires(List pythonRequires, { bool echo = true }) { - if (echo) print("Installing requirements..."); - String tempPythonRequireFile = path.join(tempDir, "requirements.txt"); - File(tempPythonRequireFile).writeAsStringSync(pythonRequires.join("\n")); - Process.runSync(pythonPath, [ "-m", "pip", "install", "-r", tempPythonRequireFile ]); - File(tempPythonRequireFile).deleteSync(); - if (echo) print("Requirements installed."); + void installRequires(List pythonRequires, {bool echo = true}) { + if (echo) print("Installing requirements..."); + String tempPythonRequireFile = path.join(tempDir, "requirements.txt"); + File(tempPythonRequireFile).writeAsStringSync(pythonRequires.join("\n")); + ProcessResult pipInstallRequirements = Process.runSync( + pythonPath, + ["-m", "pip", "install", "-r", tempPythonRequireFile], + ); + if (pipInstallRequirements.exitCode != 0) { + print( + 'Error for createVirtualEnv, exist code ${pipInstallRequirements.exitCode}\n' + 'Stdout: ${pipInstallRequirements.stdout}\nStderr: ${pipInstallRequirements.stderr}'); } - Future runFile(String pythonFile, { String? workingDirectory, ShellListener? listener, bool echo = true }) async { - if (!File(pythonFile).existsSync()) { - throw FileSystemException("File does not exist or is not path!"); - } + File(tempPythonRequireFile).deleteSync(); + if (echo) print("Requirements installed."); + } - ShellListener newListener = listener ?? ShellListener(); - - Process process = await Process.start(pythonPath, [ "-u", pythonFile ], mode: ProcessStartMode.normal, workingDirectory: defaultWorkingDirectory ?? workingDirectory); - process.stdout.listen( - (event) { - String message = String.fromCharCodes(event).trim(); - print(message); - newListener.onMessage(message); - }, - onError: (e, s) { - newListener.onError(e, s); - }, - onDone: newListener.onComplete - ); - await process.exitCode; + Future runFile( + String pythonFile, { + List arguments = const [], + String? workingDirectory, + ShellListener? listener, + bool echo = true, + }) async { + if (!File(pythonFile).existsSync()) { + throw FileSystemException("File does not exist or is not path!"); } - Future runString(String pythonCode, { String? workingDirectory, ShellListener? listener, bool echo = true }) async { - ShellListener newListener = listener ?? ShellListener(); + ShellListener newListener = listener ?? ShellListener(); - String tempPythonFile = path.join(tempDir, "${DateFormat("yyyy.MM.dd.HH.mm.ss").format(DateTime.now())}.py"); - File(tempPythonFile).writeAsStringSync(pythonCode); - ShellListener listenerToSend = ShellListener( - onMessage: (message) { - newListener.onMessage(message); - }, - onError: (e, s) { - File(tempPythonFile).deleteSync(); - newListener.onError(e, s); - }, - onComplete: () { - File(tempPythonFile).deleteSync(); - newListener.onComplete(); - } - ); + Process process = await Process.start( + pythonPath, ["-u", pythonFile, ...arguments], + mode: ProcessStartMode.normal, + workingDirectory: defaultWorkingDirectory ?? workingDirectory); + process.stdout.listen((event) { + String message = String.fromCharCodes(event).trim(); + print(message); + newListener.onMessage(message); + }, onError: (e, s) { + newListener.onError(e, s); + }, onDone: newListener.onComplete); + await process.exitCode; + } - await runFile(tempPythonFile, workingDirectory: workingDirectory, listener: listenerToSend, echo: echo); - } + Future runString(String pythonCode, + {String? workingDirectory, + List arguments = const [], + ShellListener? listener, + bool echo = true}) async { + ShellListener newListener = listener ?? ShellListener(); - void remove() { - Directory(path.join(config.instanceDir, name)).deleteSync(recursive: true); - } + String tempPythonFile = path.join(tempDir, + "${DateFormat("yyyy.MM.dd.HH.mm.ss").format(DateTime.now())}.py"); + File(tempPythonFile).writeAsStringSync(pythonCode); + ShellListener listenerToSend = ShellListener(onMessage: (message) { + newListener.onMessage(message); + }, onError: (e, s) { + File(tempPythonFile).deleteSync(); + newListener.onError(e, s); + }, onComplete: () { + File(tempPythonFile).deleteSync(); + newListener.onComplete(); + }); + + await runFile( + tempPythonFile, + arguments: arguments, + workingDirectory: workingDirectory, + listener: listenerToSend, + echo: echo, + ); + } + + void remove() { + Directory(path.join(config.instanceDir, name)).deleteSync(recursive: true); + } } diff --git a/lib/src/shell/manager.dart b/lib/src/shell/manager.dart index 0079246..3456996 100644 --- a/lib/src/shell/manager.dart +++ b/lib/src/shell/manager.dart @@ -1,78 +1,91 @@ - import "dart:io"; -import "package:path/path.dart" as path; + import "package:intl/intl.dart"; +import "package:path/path.dart" as path; -import "instance.dart"; import "../config.dart" as config; - +import "instance.dart"; class ShellManager { - static ShellInstance createInstance({ String? instanceName, List? pythonRequires, bool echo = true }) { - instanceName = instanceName ?? DateFormat("yyyy.MM.dd.HH.mm.ss").format(DateTime.now()); + static ShellInstance createInstance( + {String? instanceName, List? pythonRequires, bool echo = true}) { + instanceName = instanceName ?? + DateFormat("yyyy.MM.dd.HH.mm.ss").format(DateTime.now()); - if (echo) print("Creating shell instance [$instanceName]..."); - String instanceDir = path.join(config.instanceDir, instanceName); + if (echo) print("Creating shell instance [$instanceName]..."); + String instanceDir = path.join(config.instanceDir, instanceName); - if (Directory(instanceDir).existsSync()) { - return ShellManager.getInstance(instanceName); - } - else { - Directory(instanceDir).createSync(); - String tempDir = path.join(instanceDir, "temp"); - Directory(tempDir).createSync(); + if (Directory(instanceDir).existsSync()) { + return ShellManager.getInstance(instanceName); + } else { + Directory(instanceDir).createSync(); + String tempDir = path.join(instanceDir, "temp"); + Directory(tempDir).createSync(); - if (echo) print("Creating virtualenv..."); - String envDir = path.join(instanceDir, "env"); - String envPython = Platform.isWindows ? path.join(envDir, "Scripts", "python.exe") : path.join(envDir, "bin", "python"); - var instance = ShellInstance(name: instanceName, pythonPath: envPython, tempDir: tempDir); + if (echo) print("Creating virtualenv..."); + String envDir = path.join(instanceDir, "env"); + String envPython = Platform.isWindows + ? path.join(envDir, "Scripts", "python.exe") + : path.join(envDir, "bin", "python"); + var instance = ShellInstance( + name: instanceName, pythonPath: envPython, tempDir: tempDir); - Process.runSync(config.defaultPythonPath, [ "-m", "virtualenv", envDir ]); - if (pythonRequires != null) { - instance.installRequires(pythonRequires); - } - if (echo) print("Virtualenv created."); + ProcessResult createVirtualEnv = Process.runSync( + config.defaultPythonPath, ["-m", "virtualenv", envDir]); - return instance; - } - } + if (createVirtualEnv.exitCode != 0) { + print( + 'Error for createVirtualEnv, exist code ${createVirtualEnv.exitCode}\n' + 'Stdout: ${createVirtualEnv.stdout}\nStderr: ${createVirtualEnv.stderr}'); + } + + if (pythonRequires != null) { + instance.installRequires(pythonRequires); + } + if (echo) print("Virtualenv created."); - static ShellInstance getInstance(String instanceName) { - String instanceDir = path.join(config.instanceDir, instanceName); - if (Directory(instanceDir).existsSync()) { - return ShellInstance( - name: instanceName, - pythonPath: Platform.isWindows ? path.join(instanceDir, "env", "Scripts", "python.exe") : path.join(instanceDir, "env", "bin", "python"), - tempDir: path.join(instanceDir, "temp") - ); - } - else { - return ShellManager.createInstance(instanceName: instanceName, echo: false); - } + return instance; } + } - static void removeInstance(ShellInstance instance) { - instance.remove(); + static ShellInstance getInstance(String instanceName) { + String instanceDir = path.join(config.instanceDir, instanceName); + if (Directory(instanceDir).existsSync()) { + return ShellInstance( + name: instanceName, + pythonPath: Platform.isWindows + ? path.join(instanceDir, "env", "Scripts", "python.exe") + : path.join(instanceDir, "env", "bin", "python"), + tempDir: path.join(instanceDir, "temp")); + } else { + return ShellManager.createInstance( + instanceName: instanceName, echo: false); } + } - static void clear() { - var instances = Directory(config.instanceDir).listSync().whereType().toList(); - instances.where( - (instanceDir) => path.basename(instanceDir.path) != "default" - ).forEach( - (instanceDir) { - if (instanceDir.existsSync()) { - instanceDir.deleteSync(recursive: true); - } - } - ); + static void removeInstance(ShellInstance instance) { + instance.remove(); + } - var temps = Directory(config.tempDir).listSync(); - temps.whereType().forEach((directory) { - directory.deleteSync(recursive: true); - }); - temps.whereType().forEach((file) { - file.deleteSync(); - }); - } + static void clear() { + var instances = Directory(config.instanceDir) + .listSync() + .whereType() + .toList(); + instances + .where((instanceDir) => path.basename(instanceDir.path) != "default") + .forEach((instanceDir) { + if (instanceDir.existsSync()) { + instanceDir.deleteSync(recursive: true); + } + }); + + var temps = Directory(config.tempDir).listSync(); + temps.whereType().forEach((directory) { + directory.deleteSync(recursive: true); + }); + temps.whereType().forEach((file) { + file.deleteSync(); + }); + } } diff --git a/pubspec.yaml b/pubspec.yaml index 35712c9..3ccec5d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,10 +13,10 @@ platforms: windows: dev_dependencies: - lints: ^2.0.0 - test: ^1.16.0 + lints: ^2.0.1 + test: ^1.22.2 dependencies: - archive: ^3.3.1 + archive: ^3.3.5 dio: ^4.0.6 - intl: ^0.17.0 - path: ^1.8.2 + intl: ^0.18.0 + path: ^1.8.3