Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 74 additions & 1 deletion src/core/IronPython.Modules/termios.cs
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,64 @@ public static void PerformModuleReload(PythonContext context, PythonDictionary d
=> tcflow(context, PythonFcntl.GetFileDescriptor(context, fd), action);


// Python 3.11: tcgetwinsize, tcsetwinsize

[LightThrowing]
public static object tcgetwinsize(CodeContext context, int fd) {
var ws = new ushort[4];
var buf = new MemoryBufferProtocolWrapper<ushort>(ws.AsMemory());

object result = PythonFcntl.ioctl(fd, TIOCGWINSZ, buf, mutate_flag: true);

if (ToTermiosError(context, result) is not null and var ex) {
return ex;
}
return PythonTuple.MakeTuple((int)ws[0], (int)ws[1]);
}

[LightThrowing]
public static object? tcgetwinsize(CodeContext context, object? fd)
=> tcgetwinsize(context, PythonFcntl.GetFileDescriptor(context, fd));


[LightThrowing]
public static object? tcsetwinsize(CodeContext context, int fd, object? winsize) {
CheckFileDescriptor(fd);

if (winsize is not IList wsList || wsList.Count != 2) {
throw PythonOps.TypeError("tcsetwinsize, arg 2: must be a two-item sequence");
}

long winsize_0 = (long)PythonOps.ToIndex(wsList[0]);
long winsize_1 = (long)PythonOps.ToIndex(wsList[1]);

var ws = new ushort[4];
var buf = new MemoryBufferProtocolWrapper<ushort>(ws.AsMemory());

object result = PythonFcntl.ioctl(fd, TIOCGWINSZ, buf, mutate_flag: true);
if (ToTermiosError(context, result) is not null and var ex) {
return ex;
}

ws[0] = unchecked((ushort)winsize_0);
ws[1] = unchecked((ushort)winsize_1);
if (ws[0] != winsize_0 || ws[1] != winsize_1) {
throw PythonOps.OverflowError("winsize value(s) out of range");
}

result = PythonFcntl.ioctl(fd, TIOCSWINSZ, buf);
if (ToTermiosError(context, result) is not null and var ex2) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer is not null and var ex2 over is object ex2? Anyway, in this case it could just be return ToTermiosError(context, result). But I'm fine with keeping the pattern for consistency...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer is not null and var ex2 over is object ex2?

It wasn't a matter of a preference. The form is object simply didn't occur to me. I've checked IL and both cases unsurprisingly generate exactly the same code. So now it is a matter of preference, and I don't have any strong feelings around the choice. The former is more clear what the intention is ("test that it is not null and capture into a variable") but the latter is more concise, which is always a good thing. As for readability of the latter, I can learn a new idiom. Since I have used the former pattern on at least two other occasions, I will merge this PR as is, and change them all together once we establish a decisive preference.

Anyway, in this case it could just be return ToTermiosError(context, result). But I'm fine with keeping the pattern for consistency...

Yes, here I prefer to keep it as is. Not only for consistency with the pattern (easing the cognitive load) but also it better communicates the intent: the function discards the result of the ioctl call and always returns null. It just happens that result is always null at this point, but this is not relevant. Similarly, I added the explicit argument mutate_flag: true to the getter calls to emphasize that the call is expected to return data in-place. It happens that true is the default argument, but I still prefer to be explicit, for the sake of clarity of intentions. Note that the setter calls don't set that argument because there are no expectations around its value.

return ex2;
}

return null;
}

[LightThrowing]
public static object? tcsetwinsize(CodeContext context, object? fd, object? winsize)
=> tcsetwinsize(context, PythonFcntl.GetFileDescriptor(context, fd), winsize);


public static object tcgetattr(CodeContext context, int fd) {
CheckFileDescriptor(fd);
if (fd > 0) throw new NotImplementedException("termios support only for stdin");
Expand Down Expand Up @@ -609,13 +667,28 @@ private static void CheckFileDescriptor(int fd) {
}
}

private static object? ToTermiosError(CodeContext context, object? error) {
if (LightExceptions.GetLightException(error) is Exception ex) {
var pex = ex.GetPythonException();
if (pex is PythonExceptions._OSError oserr) {
return LightExceptions.Throw(GetTermiosError(context, oserr.errno, oserr.strerror));
} else {
return error;
}
}
return null;
}

private static Exception GetLastTermiosError(CodeContext context) {
int errno = Marshal.GetLastWin32Error();
return PythonExceptions.CreateThrowable(termioserror(context), errno, PythonNT.strerror(errno));
return GetTermiosError(context, errno, PythonNT.strerror(errno));
}


private static Exception GetTermiosError(CodeContext context, object errno, object message)
=> PythonExceptions.CreateThrowable(termioserror(context), errno, message);


private static PythonType termioserror(CodeContext context)
=> (PythonType)context.LanguageContext.GetModuleState("termioserror");
}
5 changes: 5 additions & 0 deletions src/core/IronPython/Runtime/Operations/PythonOps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,11 @@ public static object Index(object? o) {
throw TypeErrorForUnIndexableObject(o);
}

internal static BigInteger ToIndex(object? o) {
if (TryToIndex(o, out BigInteger index)) return index;
throw TypeErrorForUnIndexableObject(o);
}

internal static bool TryToIndex(object? o, [NotNullWhen(true)] out object? index) {
var context = DefaultContext.Default;

Expand Down