Skip to content

Commit ddd1c69

Browse files
authored
Use symbolic error codes with OSError (#1840)
* Use symbolic error codes with OSError * Fix typos
1 parent 05cfb0e commit ddd1c69

File tree

5 files changed

+49
-16
lines changed

5 files changed

+49
-16
lines changed

Src/IronPython.Modules/nt.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ public static int dup2(CodeContext/*!*/ context, int fd, int fd2) {
378378
}
379379

380380
if (!fileManager.ValidateFdRange(fd2)) {
381-
throw PythonOps.OSError(9, "Bad file descriptor");
381+
throw PythonOps.OSError(PythonErrorNumber.EBADF, "Bad file descriptor");
382382
}
383383

384384
if (fileManager.TryGetStreams(fd2, out _)) {
@@ -455,7 +455,7 @@ public static object fstat(CodeContext/*!*/ context, int fd) {
455455
if (streams.IsStandardIOStream()) return new stat_result(0x1000);
456456
if (StatStream(streams.ReadStream) is not null and var res) return res;
457457
}
458-
return LightExceptions.Throw(PythonOps.OSError(9, "Bad file descriptor"));
458+
return LightExceptions.Throw(PythonOps.OSError(PythonErrorNumber.EBADF, "Bad file descriptor"));
459459

460460
static object? StatStream(Stream stream) {
461461
if (stream is FileStream fs) return lstat(fs.Name, new Dictionary<string, object>(1));
@@ -481,7 +481,7 @@ public static void fsync(CodeContext context, int fd) {
481481
try {
482482
streams.Flush();
483483
} catch (IOException) {
484-
throw PythonOps.OSError(9, "Bad file descriptor");
484+
throw PythonOps.OSError(PythonErrorNumber.EBADF, "Bad file descriptor");
485485
}
486486
}
487487

@@ -988,7 +988,7 @@ public static Bytes read(CodeContext/*!*/ context, int fd, int buffersize) {
988988
try {
989989
PythonContext pythonContext = context.LanguageContext;
990990
var streams = pythonContext.FileManager.GetStreams(fd);
991-
if (!streams.ReadStream.CanRead) throw PythonOps.OSError(9, "Bad file descriptor");
991+
if (!streams.ReadStream.CanRead) throw PythonOps.OSError(PythonErrorNumber.EBADF, "Bad file descriptor");
992992

993993
return Bytes.Make(streams.Read(buffersize));
994994
} catch (Exception e) {
@@ -1872,7 +1872,7 @@ public static int write(CodeContext/*!*/ context, int fd, [NotNone] IBufferProto
18721872
using var buffer = data.GetBuffer();
18731873
PythonContext pythonContext = context.LanguageContext;
18741874
var streams = pythonContext.FileManager.GetStreams(fd);
1875-
if (!streams.WriteStream.CanWrite) throw PythonOps.OSError(9, "Bad file descriptor");
1875+
if (!streams.WriteStream.CanWrite) throw PythonOps.OSError(PythonErrorNumber.EBADF, "Bad file descriptor");
18761876

18771877
return streams.Write(buffer);
18781878
} catch (Exception e) {

Src/IronPython/Modules/_fileio.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ public FileIO(CodeContext/*!*/ context, string name, string mode = "r", bool clo
149149
}
150150

151151
if (!_context.FileManager.TryGetStreams(fd, out _streams)) {
152-
throw PythonOps.OSError(9, "Bad file descriptor");
152+
throw PythonOps.OSError(PythonFileManager.EBADF, "Bad file descriptor");
153153
}
154154
} else {
155155
throw PythonOps.TypeError("expected integer from opener");
@@ -504,13 +504,13 @@ private static void AddFilename(CodeContext context, string name, Exception ioe)
504504
}
505505

506506
private static Stream OpenFile(CodeContext/*!*/ context, PlatformAdaptationLayer pal, string name, FileMode fileMode, FileAccess fileAccess, FileShare fileShare) {
507-
if (string.IsNullOrWhiteSpace(name)) throw PythonOps.OSError(2, "No such file or directory", filename: name);
507+
if (string.IsNullOrWhiteSpace(name)) throw PythonOps.OSError(PythonFileManager.ENOENT, "No such file or directory", filename: name);
508508
try {
509509
return pal.OpenFileStream(name, fileMode, fileAccess, fileShare, 1); // Use a 1 byte buffer size to disable buffering (if the FileStream implementation supports it).
510510
} catch (UnauthorizedAccessException) {
511-
throw PythonOps.OSError(13, "Permission denied", name);
511+
throw PythonOps.OSError(PythonFileManager.EACCES, "Permission denied", name);
512512
} catch (FileNotFoundException) {
513-
throw PythonOps.OSError(2, "No such file or directory", name);
513+
throw PythonOps.OSError(PythonFileManager.ENOENT, "No such file or directory", name);
514514
} catch (IOException e) {
515515
AddFilename(context, name, e);
516516
throw;

Src/IronPython/Modules/_io.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ public static partial class PythonIOModule {
3131
private static readonly object _blockingIOErrorKey = new object();
3232
private static readonly object _unsupportedOperationKey = new object();
3333

34-
// Values of the O_flags below has to be identical with flags defined in PythonNT
34+
35+
// Values of the O_flags below have to be identical with flags defined in PythonNT
3536

3637
#region Generated Common O_Flags
3738

@@ -57,6 +58,7 @@ public static partial class PythonIOModule {
5758

5859
#endregion
5960

61+
6062
[SpecialName]
6163
public static void PerformModuleReload(PythonContext/*!*/ context, PythonDictionary/*!*/ dict) {
6264
context.EnsureModuleException(

Src/IronPython/Runtime/PythonFileManager.cs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ public void CloseStreams(PythonFileManager? manager) {
183183
/// </summary>
184184
/// <remarks>
185185
/// PythonFileManager emulates a file descriptor table. On Windows, .NET uses Win32 API which uses file handles
186-
/// rather than file descriptors. The emulation is necesary to support Python API, which in some places uses file descriptors.
186+
/// rather than file descriptors. The emulation is necessary to support Python API, which in some places uses file descriptors.
187187
///
188188
/// The manager maintains a mapping between open files (or system file-like objects) and a "descriptor", being a small non-negative integer.
189189
/// Unlike in CPython, the descriptors are allocated lazily, meaning they are allocated only when they become exposed (requested)
@@ -198,7 +198,7 @@ public void CloseStreams(PythonFileManager? manager) {
198198
/// In such situations, only one of the FileIO may be opened with flag `closefd` (CPython rule).
199199
///
200200
/// The second lever of sharing of open files is below the file descriptor level. A file descriptor can be duplicated using dup/dup2,
201-
/// but the duplicated descriptor is still refering to the same open file in the filesystem. In such case, the manager maintains
201+
/// but the duplicated descriptor is still referring to the same open file in the filesystem. In such case, the manager maintains
202202
/// a separate StreamBox for the duplicated descriptor, but the StreamBoxes for both descriptors share the underlying Streams.
203203
/// Both such descriptors have to be closed independently by the user code (either explicitly by os.close(fd) or through close()
204204
/// on the FileIO objects), but the underlying shared streams are closed only when all such duplicated descriptors are closed.
@@ -211,21 +211,39 @@ internal class PythonFileManager {
211211
/// </summary>
212212
public const int LIMIT_OFILE = 0x100000; // hard limit on Linux
213213

214+
215+
// Values of the Errno codes below have to be identical with values defined in PythonErrorNumber
216+
217+
#region Generated Common Errno Codes
218+
219+
// *** BEGIN GENERATED CODE ***
220+
// generated by function: generate_common_errno_codes from: generate_os_codes.py
221+
222+
internal const int ENOENT = 2;
223+
internal const int EBADF = 9;
224+
internal const int EACCES = 13;
225+
internal const int EMFILE = 24;
226+
227+
// *** END GENERATED CODE ***
228+
229+
#endregion
230+
231+
214232
private readonly object _synchObject = new();
215233
private readonly Dictionary<int, StreamBox> _table = new();
216234
private const int _offset = 3; // number of lowest keys that are not automatically allocated
217235
private int _current = _offset; // lowest potentially unused key in _objects at or above _offset
218236
private readonly ConcurrentDictionary<Stream, int> _refs = new();
219237

220238
// This version of Add is used with genuine file descriptors (Unix).
221-
// Exception: support dup2 on all frameworks/platfroms.
239+
// Exception: support dup2 on all frameworks/platforms.
222240
public int Add(int id, StreamBox streams) {
223241
ContractUtils.RequiresNotNull(streams, nameof(streams));
224242
ContractUtils.Requires(streams.Id < 0, nameof(streams));
225243
ContractUtils.Requires(id >= 0, nameof(id));
226244
lock (_synchObject) {
227245
if (_table.ContainsKey(id)) {
228-
throw PythonOps.OSError(9, "Bad file descriptor", id.ToString());
246+
throw PythonOps.OSError(EBADF, "Bad file descriptor", id.ToString());
229247
}
230248
streams.Id = id;
231249
_table.Add(id, streams);
@@ -243,7 +261,7 @@ public int Add(StreamBox streams) {
243261
while (_table.ContainsKey(_current)) {
244262
_current++;
245263
if (_current >= LIMIT_OFILE)
246-
throw PythonOps.OSError(24, "Too many open files");
264+
throw PythonOps.OSError(EMFILE, "Too many open files");
247265
}
248266
streams.Id = _current;
249267
_table.Add(_current, streams);
@@ -280,7 +298,7 @@ public StreamBox GetStreams(int id) {
280298
if (TryGetStreams(id, out StreamBox? streams)) {
281299
return streams;
282300
}
283-
throw PythonOps.OSError(9, "Bad file descriptor");
301+
throw PythonOps.OSError(EBADF, "Bad file descriptor");
284302
}
285303

286304
public int GetOrAssignId(StreamBox streams) {

Src/Scripts/generate_os_codes.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,18 @@ def darwin_code_expr(codes, fmt):
102102
def linux_code_expr(codes, fmt):
103103
return fmt(codes[linux_idx])
104104

105+
common_errno_codes = ['ENOENT', 'EBADF', 'EACCES', 'EMFILE']
106+
107+
def generate_common_errno_codes(cw):
108+
for name in common_errno_codes:
109+
codes = errorvalues[name]
110+
111+
value = windows_code_expr(codes, str)
112+
if (all(c.isdigit() for c in value)):
113+
cw.write(f"internal const int {name} = {value};")
114+
else:
115+
cw.write(f"internal static int {name} => {value};")
116+
105117
def generate_errno_names(cw):
106118
def is_windows_alias(name):
107119
return "WSA" + name in errorvalues and name in errorvalues and errorvalues["WSA" + name][windows_idx] == errorvalues[name][windows_idx]
@@ -208,6 +220,7 @@ def generate_common_O_flags(cw):
208220
def main():
209221
return generate(
210222
("Errno Codes", generate_errno_codes),
223+
("Common Errno Codes", generate_common_errno_codes),
211224
("Errno Names", generate_errno_names),
212225
("O_Flags", generate_all_O_flags),
213226
("Common O_Flags", generate_common_O_flags),

0 commit comments

Comments
 (0)