Skip to content

Commit

Permalink
fix: work on #105 add noStdoutResult and noStdErrResult to ShellOptio…
Browse files Browse the repository at this point in the history
…ns to avoid out of memory for very long output
  • Loading branch information
alextekartik committed Feb 21, 2024
1 parent 8135dd3 commit 057304c
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 58 deletions.
17 changes: 15 additions & 2 deletions packages/process_run/lib/src/process_run.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ export 'shell_utils_io.dart' show executableArgumentsToString;
///
/// Optional [onProcess(process)] is called to allow killing the process.
///
/// If [noStdoutResult] is true, the result will not contain the stdout.
/// If [noStderrResult] is true, the result will not contain the stderr.
///
/// Don't mess-up with the input and output for now here. only use it for kill.
Future<ProcessResult> runExecutableArguments(
String executable, List<String> arguments,
Expand All @@ -32,6 +35,8 @@ Future<ProcessResult> runExecutableArguments(
StreamSink<List<int>>? stderr,
bool? verbose,
bool? commandVerbose,
bool? noStdoutResult,
bool? noStderrResult,
void Function(Process process)? onProcess}) async {
if (verbose == true) {
commandVerbose = true;
Expand Down Expand Up @@ -137,8 +142,12 @@ Future<ProcessResult> runExecutableArguments(
return list;
}

var out = streamToResult(outCtlr.stream, stdoutEncoding);
var err = streamToResult(errCtlr.stream, stderrEncoding);
var out = (noStdoutResult ?? false)
? Future.value(null)
: streamToResult(outCtlr.stream, stdoutEncoding);
var err = (noStderrResult ?? false)
? Future.value(null)
: streamToResult(errCtlr.stream, stderrEncoding);

process.stdout.listen((List<int> d) {
if (stdout != null) {
Expand Down Expand Up @@ -303,6 +312,8 @@ Future<ProcessResult> processCmdRun(ProcessCmd cmd,
Stream<List<int>>? stdin,
StreamSink<List<int>>? stdout,
StreamSink<List<int>>? stderr,
bool? noStdoutResult,
bool? noStderrResult,
void Function(Process process)? onProcess}) async {
if (verbose == true) {
stdout ??= io.stdout;
Expand All @@ -328,6 +339,8 @@ Future<ProcessResult> processCmdRun(ProcessCmd cmd,
stdin: stdin,
stdout: stdout,
stderr: stderr,
noStdoutResult: noStdoutResult,
noStderrResult: noStderrResult,
onProcess: onProcess);
} catch (e) {
if (verbose == true) {
Expand Down
59 changes: 37 additions & 22 deletions packages/process_run/lib/src/shell.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import 'dart:convert';
import 'dart:io' as io;

import 'package:path/path.dart';
import 'package:process_run/shell.dart';
import 'package:process_run/shell.dart' as impl;
import 'package:process_run/shell.dart';
import 'package:process_run/src/bin/shell/import.dart';
import 'package:process_run/src/platform/platform.dart';
import 'package:process_run/src/process_run.dart';
Expand All @@ -23,6 +23,8 @@ export 'shell_common.dart' show shellDebug;
///
/// Returns a list of executed command line results. Verbose by default.
///
/// Prefer using [options] than the parameters. [options] overrides all other
/// parameters but [onProcess].
///
/// ```dart
/// await run('flutter build');
Expand Down Expand Up @@ -50,6 +52,7 @@ Future<List<ProcessResult>> run(
bool? commandVerbose,
// Default to true if verbose is true
bool? commentVerbose,
ShellOptions? options,
void Function(Process process)? onProcess,
}) {
return Shell(
Expand All @@ -65,7 +68,8 @@ Future<List<ProcessResult>> run(
stderr: stderr,
verbose: verbose,
commandVerbose: commandVerbose,
commentVerbose: commentVerbose)
commentVerbose: commentVerbose,
options: options)
.run(script, onProcess: onProcess);
}

Expand Down Expand Up @@ -107,6 +111,9 @@ List<ProcessResult> runSync(
bool? commandVerbose,
// Default to true if verbose is true
bool? commentVerbose,

/// Override all other options parameters
ShellOptions? options,
}) {
return Shell(
throwOnError: throwOnError,
Expand All @@ -121,7 +128,8 @@ List<ProcessResult> runSync(
stderr: stderr,
verbose: verbose,
commandVerbose: commandVerbose,
commentVerbose: commentVerbose)
commentVerbose: commentVerbose,
options: options)
.runSync(script);
}

Expand Down Expand Up @@ -181,6 +189,8 @@ abstract class Shell implements ShellCore, ShellCoreSync {
///
/// if [verbose] is not false or [commentVerbose] is true, it will display the
/// comments as well
///
/// [options] overrides all other parameters
factory Shell(
{bool throwOnError = true,
String? workingDirectory,
Expand All @@ -197,6 +207,8 @@ abstract class Shell implements ShellCore, ShellCoreSync {
bool? commandVerbose,
// Default to false
bool? commentVerbose,

/// Overrides all parameters
ShellOptions? options}) {
var shell = shellContext.newShell(
options: options ??
Expand Down Expand Up @@ -227,26 +239,27 @@ abstract class Shell implements ShellCore, ShellCoreSync {

/// Create a new shell
@Deprecated('Use clone with options')
Shell clone(
{bool? throwOnError,
String? workingDirectory,
// Don't change environment
@Deprecated('Don\'t change map') Map<String, String>? environment,
Shell clone({
bool? throwOnError,
String? workingDirectory,
// Don't change environment
@Deprecated('Don\'t change map') Map<String, String>? environment,

/// Explicetely set e new environment
/// Explicetely set e new environment
// ShellEnvironment? shellEnvironment,
@Deprecated('Don\'t change includeParentEnvironment')
// Don't change includeParentEnvironment
bool? includeParentEnvironment,
bool? runInShell,
Encoding? stdoutEncoding,
Encoding? stderrEncoding,
Stream<List<int>>? stdin,
StreamSink<List<int>>? stdout,
StreamSink<List<int>>? stderr,
bool? verbose,
bool? commandVerbose,
bool? commentVerbose}) {
@Deprecated('Don\'t change includeParentEnvironment')
// Don't change includeParentEnvironment
bool? includeParentEnvironment,
bool? runInShell,
Encoding? stdoutEncoding,
Encoding? stderrEncoding,
Stream<List<int>>? stdin,
StreamSink<List<int>>? stdout,
StreamSink<List<int>>? stderr,
bool? verbose,
bool? commandVerbose,
bool? commentVerbose,
}) {
var localShellEnvironment =
// Compat
(environment is ShellEnvironment ? environment : null);
Expand Down Expand Up @@ -601,7 +614,9 @@ abstract class Shell implements ShellCore, ShellCoreSync {
commandVerbose: _options.commandVerbose,
stderr: _options.stderr,
stdin: _options.stdin,
stdout: _options.stdout, onProcess: (process) {
stdout: _options.stdout,
noStdoutResult: _options.noStdoutResult,
noStderrResult: _options.noStderrResult, onProcess: (process) {
_currentProcess = process;
_currentProcessCmd = processCmd;
_currentProcessRunId = runId;
Expand Down
95 changes: 61 additions & 34 deletions packages/process_run/lib/src/shell_common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ abstract class ShellCoreSync {
}

/// Shell options.
///
class ShellOptions {
final bool _throwOnError;
final String? _workingDirectory;
Expand All @@ -134,6 +136,8 @@ class ShellOptions {
final bool _verbose;
final bool _commandVerbose;
final bool _commentVerbose;
final bool? _noStdoutResult;
final bool? _noStderrResult;

late final ShellEnvironment? _environment;

Expand Down Expand Up @@ -164,24 +168,34 @@ class ShellOptions {
/// default to true for non .exe files
///
/// if [verbose] is not false or [commentVerbose] is true, it will display the
/// comments as well
ShellOptions(
{bool throwOnError = true,
String? workingDirectory,
Map<String, String>? environment,
bool includeParentEnvironment = true,
bool? runInShell,
Encoding? stdoutEncoding,
Encoding? stderrEncoding,
Stream<List<int>>? stdin,
StreamSink<List<int>>? stdout,
StreamSink<List<int>>? stderr,
bool verbose = true,
// Default to true
bool? commandVerbose,
// Default to false
bool? commentVerbose})
: _throwOnError = throwOnError,
/// comments as well.
///
/// If [noStdoutResult] is true, stdout will be null in the ProcessResult result
/// of the run command (runSync will still contain it).
///
/// If [noStderrResult] is true, stderr will be null in the ProcessResult result
/// of the run command (runSync will still contain it).
ShellOptions({
bool throwOnError = true,
String? workingDirectory,
Map<String, String>? environment,
bool includeParentEnvironment = true,
bool? runInShell,
Encoding? stdoutEncoding,
Encoding? stderrEncoding,
Stream<List<int>>? stdin,
StreamSink<List<int>>? stdout,
StreamSink<List<int>>? stderr,
bool verbose = true,
// Default to true
bool? commandVerbose,
// Default to false
bool? commentVerbose,
// Default to false
bool? noStdoutResult,
// Default to false
bool? noStderrResult,
}) : _throwOnError = throwOnError,
_workingDirectory = workingDirectory,
_runInShell = runInShell,
_stdoutEncoding = stdoutEncoding,
Expand All @@ -191,7 +205,9 @@ class ShellOptions {
_stderr = stderr,
_verbose = verbose,
_commandVerbose = commandVerbose ?? verbose,
_commentVerbose = commentVerbose ?? false {
_commentVerbose = commentVerbose ?? false,
_noStderrResult = noStderrResult,
_noStdoutResult = noStdoutResult {
_environment = ShellEnvironment.full(
environment: environment,
includeParentEnvironment: includeParentEnvironment);
Expand All @@ -212,6 +228,12 @@ class ShellOptions {
/// True if it should throw if an error occurred.
bool get throwOnError => _throwOnError;

/// True if ProcessResult should not contain stdout
bool? get noStdoutResult => _noStdoutResult;

/// True if ProcessResult should not contain stderr
bool? get noStderrResult => _noStderrResult;

/// Create a new shell
ShellOptions clone(
{bool? throwOnError,
Expand All @@ -225,6 +247,8 @@ class ShellOptions {
bool? verbose,
bool? commandVerbose,
bool? commentVerbose,
bool? noStdoutResult,
bool? noStderrResult,
ShellEnvironment? shellEnvironment}) {
return ShellOptions(
verbose: verbose ?? _verbose,
Expand All @@ -238,7 +262,9 @@ class ShellOptions {
stdoutEncoding: stdoutEncoding ?? _stdoutEncoding,
throwOnError: throwOnError ?? _throwOnError,
workingDirectory: workingDirectory ?? _workingDirectory,
environment: shellEnvironment);
environment: shellEnvironment,
noStdoutResult: noStdoutResult ?? _noStdoutResult,
noStderrResult: noStderrResult ?? _noStderrResult);
}
}

Expand Down Expand Up @@ -272,20 +298,21 @@ mixin ShellMixin implements ShellCore, ShellCoreSync {
}

@Deprecated('Use clone with options')
Shell clone(
{bool? throwOnError,
String? workingDirectory,
Map<String, String>? environment,
bool? includeParentEnvironment,
bool? runInShell,
Encoding? stdoutEncoding,
Encoding? stderrEncoding,
Stream<List<int>>? stdin,
StreamSink<List<int>>? stdout,
StreamSink<List<int>>? stderr,
bool? verbose,
bool? commandVerbose,
bool? commentVerbose}) {
Shell clone({
bool? throwOnError,
String? workingDirectory,
Map<String, String>? environment,
bool? includeParentEnvironment,
bool? runInShell,
Encoding? stdoutEncoding,
Encoding? stderrEncoding,
Stream<List<int>>? stdin,
StreamSink<List<int>>? stdout,
StreamSink<List<int>>? stderr,
bool? verbose,
bool? commandVerbose,
bool? commentVerbose,
}) {
return cloneWithOptions(
ShellOptions(
throwOnError: throwOnError ?? options.throwOnError,
Expand Down
49 changes: 49 additions & 0 deletions packages/process_run/test/shell_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,56 @@ dart example/echo.dart -o éà
expect(results.length, 7);
});

// Compiled version
var echoExe = shellArgument(join('.local', 'example', 'echo.exe'));
Future<void> ensureEchoExe() async {
if (!File(echoExe).existsSync()) {
await Directory(join('.local', 'example')).create(recursive: true);
await run(
'dart compile exe ${shellArgument(join('example', 'echo.dart'))} -o $echoExe');
}
}

test('noStdoutResult/noStderrResult', () async {
await ensureEchoExe();
var options = ShellOptions(noStdoutResult: true, verbose: false);
var shell = Shell(options: options);
var script = '$echoExe -o Out -e Err';
var result = (await shell.run(script)).first;
expect(result.stdout, isNull);
expect(result.stderr, 'Err');
result = (await shell
.runExecutableArguments(echoExe, ['-o', 'Out', '-e', 'Err']));
expect(result.stdout, isNull);
expect(result.stderr, 'Err');
// Not valid for runSync
result = (shell.runSync(script)).first;
expect(result.stdout, 'Out');
expect(result.stderr, 'Err');
// Not valid for runExecutableArgumentsSync
result = (shell
.runExecutableArgumentsSync(echoExe, ['-o', 'Out', '-e', 'Err']));
expect(result.stdout, 'Out');
expect(result.stderr, 'Err');
result = (await run(script, options: options)).first;
expect(result.stdout, isNull);
expect(result.stderr, 'Err');
result = (runSync(script, options: options)).first;
// Not valid for runSync
expect(result.stdout, 'Out');
expect(result.stderr, 'Err');
options = ShellOptions(noStderrResult: true, verbose: false);
shell = Shell(options: options);
result = (await shell.run(script)).first;
expect(result.stdout, 'Out');
expect(result.stderr, isNull);
result = (await run(script, options: options)).first;
expect(result.stdout, 'Out');
expect(result.stderr, isNull);
});

test('arguments utf8', () async {
await ensureEchoExe();
var shell = Shell(verbose: debug, stdoutEncoding: utf8);
var results = await shell.run('''
dart example/echo.dart -o 你好
Expand Down

0 comments on commit 057304c

Please sign in to comment.