Skip to content

Commit 58aea76

Browse files
authored
Merge pull request #1086 from arkuznetsov/feature/get-stdin
feat (#746): Реализована возможность чтения из скрипта данных, переданных через stdin
2 parents 1a6e5b4 + 7b95747 commit 58aea76

File tree

9 files changed

+240
-7
lines changed

9 files changed

+240
-7
lines changed

src/ScriptEngine.HostedScript/Library/Binary/GenericStream.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public GenericStream(Stream underlyingStream, bool readOnly)
4747
}
4848

4949
public bool IsReadOnly => !CanWrite;
50-
50+
5151
/// <summary>
5252
///
5353
/// Признак доступности записи в поток.

src/ScriptEngine.HostedScript/Library/Binary/MemoryStreamContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public class MemoryStreamContext : AutoContext<MemoryStreamContext>, IDisposable
4141
_underlyingStream = new MemoryStream(capacity);
4242
_commonImpl = new GenericStreamImpl(_underlyingStream);
4343
}
44-
44+
4545
/// <summary>
4646
///
4747
/// Создает поток, в качестве нижележащего хранилища для которого используется заданный байтовый буфер. Ёмкость потока ограничена размером буфера. При выходе за границы буфера будет сгенерировано исключение.

src/ScriptEngine.HostedScript/Library/ConsoleContext.cs

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ This Source Code Form is subject to the terms of the
55
at http://mozilla.org/MPL/2.0/.
66
----------------------------------------------------------*/
77
using System;
8+
using System.IO;
9+
using ScriptEngine.HostedScript.Library.Binary;
810
using ScriptEngine.Machine;
911
using ScriptEngine.Machine.Contexts;
1012

@@ -168,6 +170,24 @@ public IValue InputEncoding
168170
Console.InputEncoding = TextEncodingEnum.GetEncoding(value);
169171
}
170172
}
173+
174+
/// <summary>
175+
/// Возвращает или задает кодировку консоли, используемую при чтении входных данных.
176+
/// </summary>
177+
/// <returns>КодировкаТекста</returns>
178+
[ContextProperty("КодировкаВыходногоПотока", "InputEncoding")]
179+
public IValue OutputEncoding
180+
{
181+
get
182+
{
183+
var encodingEnum = GlobalsManager.GetEnum<TextEncodingEnum>();
184+
return encodingEnum.GetValue(Console.OutputEncoding);
185+
}
186+
set
187+
{
188+
Console.OutputEncoding = TextEncodingEnum.GetEncoding(value);
189+
}
190+
}
171191

172192
/// <summary>
173193
/// Воспроизводит звуковой сигнал.
@@ -178,10 +198,77 @@ public void Beep()
178198
Console.Beep();
179199
}
180200

201+
/// <summary>
202+
/// Получает системный поток ввода stdin
203+
/// </summary>
204+
/// <returns>Поток</returns>
205+
[ContextMethod("ОткрытьСтандартныйПотокВвода", "OpenStandardInput")]
206+
public GenericStream OpenStandardInput()
207+
{
208+
var stream = Console.OpenStandardInput();
209+
return new GenericStream(stream, true);
210+
}
211+
212+
/// <summary>
213+
/// Получает системный поток вывода ошибок stderr
214+
/// </summary>
215+
/// <returns>Поток</returns>
216+
[ContextMethod("ОткрытьСтандартныйПотокОшибок", "OpenStandardError")]
217+
public GenericStream OpenStandardError()
218+
{
219+
var stream = Console.OpenStandardError();
220+
return new GenericStream(stream);
221+
}
222+
223+
/// <summary>
224+
/// Получает системный поток вывода stdout
225+
/// </summary>
226+
/// <returns>Поток</returns>
227+
[ContextMethod("ОткрытьСтандартныйПотокВывода", "OpenStandardOutput")]
228+
public GenericStream OpenStandardOutput()
229+
{
230+
var stream = Console.OpenStandardOutput();
231+
return new GenericStream(stream);
232+
}
233+
234+
/// <summary>
235+
/// Глобально переопределяет стандартный вывод и направляет в другой поток
236+
/// </summary>
237+
/// <param name="target">Поток назначения</param>
238+
[ContextMethod("УстановитьПотокВывода", "SetOutput")]
239+
public void SetOutput(IValue target)
240+
{
241+
if (!(target.AsObject() is IStreamWrapper stream))
242+
throw RuntimeException.InvalidArgumentType(nameof(target));
243+
244+
var writer = new StreamWriter(stream.GetUnderlyingStream(), Console.OutputEncoding)
245+
{
246+
AutoFlush = true,
247+
};
248+
Console.SetOut(writer);
249+
}
250+
251+
/// <summary>
252+
/// Глобально переопределяет стандартный поток ошибок и направляет в другой поток
253+
/// </summary>
254+
/// <param name="target">Поток назначения</param>
255+
[ContextMethod("УстановитьПотокОшибок", "SetError")]
256+
public void SetError(IValue target)
257+
{
258+
if (!(target.AsObject() is IStreamWrapper stream))
259+
throw RuntimeException.InvalidArgumentType(nameof(target));
260+
261+
var writer = new StreamWriter(stream.GetUnderlyingStream());
262+
Console.SetError(writer);
263+
}
264+
181265
[ScriptConstructor]
182266
public static ConsoleContext Constructor()
183267
{
184-
return new ConsoleContext();
268+
var provider = GlobalsManager.GetGlobalContext<ConsoleProvider>();
269+
SystemLogger.Write("WARNING: Constructor of Console is obsolete. Use global property Консоль/Console");
270+
271+
return provider.Console;
185272
}
186273
}
187274

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*----------------------------------------------------------
2+
This Source Code Form is subject to the terms of the
3+
Mozilla Public License, v.2.0. If a copy of the MPL
4+
was not distributed with this file, You can obtain one
5+
at http://mozilla.org/MPL/2.0/.
6+
----------------------------------------------------------*/
7+
8+
using System;
9+
using ScriptEngine.Machine;
10+
using ScriptEngine.Machine.Contexts;
11+
12+
namespace ScriptEngine.HostedScript.Library
13+
{
14+
[GlobalContext(Category = "Работа с консолью")]
15+
public class ConsoleProvider : GlobalContextBase<ConsoleProvider>
16+
{
17+
private readonly ConsoleContext _console = new ConsoleContext();
18+
19+
private ConsoleProvider()
20+
{
21+
}
22+
23+
public override void OnAttach(MachineInstance machine, out IVariable[] variables, out MethodInfo[] methods)
24+
{
25+
variables = new [] {Variable.CreateContextPropertyReference(this, 0, GetPropName(0))};
26+
methods = new MethodInfo[0];
27+
}
28+
29+
[ContextProperty("Консоль", "Console")]
30+
public ConsoleContext Console => _console;
31+
32+
public override bool IsPropWritable(int propNum)
33+
{
34+
// обратная совместимость. Присваивание Консоль = Новый Консоль не должно ругаться на недоступность записи
35+
return true;
36+
}
37+
38+
public override void SetPropValue(int propNum, IValue newVal)
39+
{
40+
// обратная совместимость. Присваивание Консоль = Новый Консоль не должно ничего делать
41+
if (!ReferenceEquals(newVal.GetRawValue(), _console))
42+
{
43+
throw new InvalidOperationException("Can't assign to global property Console");
44+
}
45+
}
46+
47+
public static ConsoleProvider CreateInstance()
48+
{
49+
return new ConsoleProvider();
50+
}
51+
}
52+
}

src/ScriptEngine.HostedScript/Library/SystemGlobalContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public SystemGlobalContext()
3838
RegisterProperty("ФайловыеПотоки", () => FileStreams);
3939
RegisterProperty("FileStreams", () => FileStreams);
4040

41-
RegisterProperty("Символы", () => (IValue)Chars);
41+
RegisterProperty("Символы", () => (IValue)Chars);
4242
RegisterProperty("Chars", () => (IValue)Chars);
4343
}
4444

src/ScriptEngine.HostedScript/Library/TextReadImpl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ private void OpenStream(IStreamWrapper streamObj, IValue encoding = null, string
5656
TextReader imReader;
5757
if (encoding == null)
5858
{
59-
imReader = Environment.FileOpener.OpenReader(streamObj.GetUnderlyingStream());
59+
imReader = Environment.FileOpener.OpenReader(streamObj.GetUnderlyingStream(), Encoding.Default);
6060
}
6161
else
6262
{

tests/console.os

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
Перем юТест;
2+
3+
Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт
4+
5+
юТест = ЮнитТестирование;
6+
7+
ВсеТесты = Новый Массив;
8+
ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоСтандартныйПотокВводаЭтоПоток");
9+
ВсеТесты.Добавить("ТестДолжен_ПроверитьПередачуДанныхВСкриптЧерезСтандартныйПотокВвода");
10+
ВсеТесты.Добавить("ТестДолжен_ПроверитьПеренаправлениеВывода");
11+
12+
Возврат ВсеТесты;
13+
14+
КонецФункции
15+
16+
Процедура ТестДолжен_ПроверитьЧтоСтандартныйПотокВводаЭтоПоток() Экспорт
17+
18+
ПотокВвода = Консоль.ОткрытьСтандартныйПотокВвода();
19+
юТест.ПроверитьРавенство(Тип("Поток"), ТипЗнч(ПотокВвода), "Стандартный поток ввода - Поток");
20+
21+
КонецПроцедуры
22+
23+
Процедура ТестДолжен_ПроверитьПередачуДанныхВСкриптЧерезСтандартныйПотокВвода() Экспорт
24+
25+
СистемнаяИнформация = Новый СистемнаяИнформация;
26+
27+
ПутьКОскрипт = ОбъединитьПути(КаталогПрограммы(), "oscript.exe");
28+
#Если Linux Тогда
29+
ПутьКОскрипт = "mono " + ПутьКОскрипт;
30+
#КонецЕсли
31+
32+
КодСкрипта = "Чтение = Новый ЧтениеТекста();
33+
|Чтение.Открыть(Консоль.ОткрытьСтандартныйПотокВвода());
34+
|Сообщить(СокрЛП(Чтение.Прочитать()));
35+
|";
36+
37+
ТекстСкрипта = Новый ТекстовыйДокумент();
38+
ТекстСкрипта.УстановитьТекст(КодСкрипта);
39+
40+
ВремФайл = ПолучитьИмяВременногоФайла("os");
41+
42+
ТекстСкрипта.Записать(ВремФайл);
43+
44+
ТестовыеДанные = "12346";
45+
46+
ИсполняемаяКоманда = СтрШаблон("echo %1 | %2 %3", ТестовыеДанные, ПутьКОскрипт, ВремФайл);
47+
48+
#Если Windows Тогда
49+
ШаблонЗапуска = "cmd /c ""%1""";
50+
#Иначе
51+
ШаблонЗапуска = "sh -c '%1'";
52+
#КонецЕсли
53+
54+
ИсполняемаяКоманда = СтрШаблон(ШаблонЗапуска, ИсполняемаяКоманда);
55+
56+
Процесс = СоздатьПроцесс(ИсполняемаяКоманда, , Истина);
57+
Процесс.Запустить();
58+
59+
Пока НЕ Процесс.Завершен Цикл
60+
Приостановить(100);
61+
КонецЦикла;
62+
63+
ВыводКоманды = СокрЛП(Процесс.ПотокВывода.Прочитать());
64+
65+
УдалитьФайлы(ВремФайл);
66+
67+
юТест.ПроверитьРавенство(ВыводКоманды, ТестовыеДанные, "Вывод команды - тестовые данные");
68+
69+
КонецПроцедуры
70+
71+
Процедура ТестДолжен_ПроверитьПеренаправлениеВывода() Экспорт
72+
73+
ВФ = ПолучитьИмяВременногоФайла();
74+
Поток = ФайловыеПотоки.ОткрытьДляЗаписи(ВФ);
75+
Консоль.УстановитьПотокВывода(Поток);
76+
Попытка
77+
Сообщить("Привет мир!");
78+
Исключение
79+
// что-то пошло не так
80+
Консоль.УстановитьПотокВывода(Консоль.ОткрытьСтандартныйПотокВывода());
81+
ВызватьИсключение;
82+
КонецПопытки;
83+
84+
Поток.Закрыть();
85+
Консоль.УстановитьПотокВывода(Консоль.ОткрытьСтандартныйПотокВывода());
86+
87+
Чтение = Новый ЧтениеТекста(ВФ, Консоль.КодировкаВыходногоПотока);
88+
Текст = Чтение.Прочитать();
89+
Чтение.Закрыть();
90+
91+
УдалитьФайлы(ВФ);
92+
93+
юТест.ПроверитьРавенство("Привет мир!", СокрЛП(Текст));
94+
95+
КонецПроцедуры

tests/process.os

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@
7676

7777
Процедура ТестДолжен_ЗаписатьВоВходнойПотокПроцесса() Экспорт
7878

79-
Консоль = Новый Консоль();
8079
КодировкаПоУмолчанию = Консоль.КодировкаВходногоПотока;
8180
Если Консоль.КодировкаВходногоПотока = КодировкаТекста.UTF8 Тогда
8281
Консоль.КодировкаВходногоПотока = КодировкаТекста.UTF8NoBOM;

tests/reflector.os

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@
232232
МассивОбъектов.Добавить(Новый РегулярноеВыражение("а"));
233233
МассивОбъектов.Добавить(Новый ЗаписьXML);
234234
МассивОбъектов.Добавить(Новый ЧтениеXML);
235-
МассивОбъектов.Добавить(Новый Консоль);
235+
МассивОбъектов.Добавить(Консоль);
236236
МассивОбъектов.Добавить(Новый Файл("ыв"));
237237
МассивОбъектов.Добавить(Новый ГенераторСлучайныхЧисел);
238238
МассивОбъектов.Добавить(Новый СистемнаяИнформация);

0 commit comments

Comments
 (0)